Skip to content

Commit 6406e56

Browse files
committed
Keeping Kens new changes, all tests passing and reverting deletion of CLAUDE.md. Updated CHANGELOG.md
1 parent dfc6d57 commit 6406e56

File tree

5 files changed

+433
-385
lines changed

5 files changed

+433
-385
lines changed

CLAUDE.md

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
# CLAUDE.md
2+
3+
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
4+
5+
## Project Overview
6+
7+
The BSV SDK is a comprehensive Python library for developing scalable applications on the BSV Blockchain. It provides a peer-to-peer approach adhering to SPV (Simplified Payment Verification) with focus on privacy and scalability.
8+
9+
**Repository**: https://github.com/bitcoin-sv/py-sdk
10+
**Package name**: bsv-sdk
11+
**Current version**: 1.0.8
12+
**Python requirement**: >=3.9
13+
14+
## Development Commands
15+
16+
### Installation
17+
```bash
18+
pip install -r requirements.txt
19+
```
20+
21+
### Testing
22+
```bash
23+
# Run full test suite with coverage
24+
pytest --cov=bsv --cov-report=html
25+
26+
# Run specific test file
27+
pytest tests/test_transaction.py
28+
29+
# Run tests with asyncio
30+
pytest tests/bsv/auth/test_auth_peer_basic.py
31+
```
32+
33+
### Building the Package
34+
```bash
35+
# Build distribution packages (requires python3 -m build)
36+
make build
37+
38+
# Or directly:
39+
python3 -m build
40+
```
41+
42+
### Publishing (Maintainers Only)
43+
```bash
44+
make upload_test # Upload to TestPyPI
45+
make upload # Upload to PyPI
46+
```
47+
48+
## Code Architecture
49+
50+
### Module Organization
51+
52+
The `bsv` package is organized into functional submodules:
53+
54+
- **Core Transaction Components** (`bsv/transaction.py`, `bsv/transaction_input.py`, `bsv/transaction_output.py`)
55+
- `Transaction`: Main transaction class with serialization, signing, fee calculation, and broadcasting
56+
- Supports BEEF (Bitcoin Encapsulated Format) and EF (Extended Format) serialization
57+
- SPV validation through merkle paths
58+
59+
- **Script System** (`bsv/script/`)
60+
- `ScriptTemplate`: Abstract base for locking/unlocking scripts
61+
- Built-in templates: `P2PKH`, `P2PK`, `OpReturn`, `BareMultisig`, `RPuzzle`
62+
- `Script`: Low-level script operations
63+
- `Spend`: Script validation engine
64+
65+
- **Keys & Cryptography** (`bsv/keys.py`, `bsv/curve.py`, `bsv/hash.py`)
66+
- `PrivateKey`, `PublicKey`: ECDSA key management
67+
- Support for compressed/uncompressed keys
68+
- WIF format support
69+
70+
- **HD Wallets** (`bsv/hd/`)
71+
- Full BIP32/39/44 implementation
72+
- Hierarchical deterministic key derivation
73+
- Mnemonic phrase support (multiple languages via `hd/wordlist/`)
74+
75+
- **Authentication** (`bsv/auth/`)
76+
- `Peer`: Central authentication protocol implementation
77+
- `Certificate`: Certificate handling and verification
78+
- `SessionManager`: Session lifecycle management
79+
- `Transport`: Communication layer abstraction
80+
- PKI-based authentication between peers
81+
82+
- **Wallet** (`bsv/wallet/`)
83+
- `WalletInterface`: Abstract wallet interface
84+
- `WalletImpl`: Full wallet implementation
85+
- `KeyDeriver`: Protocol-based key derivation
86+
- `CachedKeyDeriver`: Optimized key derivation with caching
87+
88+
- **Broadcasting** (`bsv/broadcasters/`)
89+
- `Broadcaster`: Interface for transaction broadcasting
90+
- `arc.py`: ARC broadcaster implementation
91+
- `whatsonchain.py`: WhatsOnChain broadcaster
92+
- `default_broadcaster.py`: Default broadcaster selection
93+
94+
- **Chain Tracking** (`bsv/chaintrackers/`)
95+
- `ChainTracker`: Interface for chain state verification
96+
- `whatsonchain.py`: WhatsOnChain chain tracker
97+
- `default.py`: Default chain tracker
98+
99+
- **Storage** (`bsv/storage/`)
100+
- `Uploader`, `Downloader`: File upload/download utilities
101+
- Integration with blockchain storage
102+
103+
- **Keystore** (`bsv/keystore/`)
104+
- Key persistence and retention management
105+
- Local key-value store implementation
106+
107+
- **BEEF Support** (`bsv/beef/`)
108+
- `build_beef_v2_from_raw_hexes`: BEEF format construction
109+
- Transaction validation with merkle proofs
110+
111+
- **Utilities** (`bsv/utils.py`)
112+
- `Reader`, `Writer`: Binary serialization helpers
113+
- Varint encoding/decoding
114+
- Address utilities
115+
116+
### Important Design Patterns
117+
118+
**Lazy Imports**: The `bsv/__init__.py` is intentionally minimal to avoid circular imports. Import specific modules where needed:
119+
```python
120+
from bsv.keys import PrivateKey
121+
from bsv.transaction import Transaction
122+
```
123+
124+
**Async Operations**: Transaction broadcasting and verification are async:
125+
```python
126+
await tx.broadcast()
127+
await tx.verify(chaintracker)
128+
```
129+
130+
**Template Pattern**: Script types use templates that provide `lock()` and `unlock()` methods:
131+
```python
132+
script_template = P2PKH()
133+
locking_script = script_template.lock(address)
134+
unlocking_template = script_template.unlock(private_key)
135+
```
136+
137+
**Source Transactions**: Inputs require source transactions for fee calculation and verification. The SDK tracks UTXOs through linked source transactions rather than external UTXO databases.
138+
139+
**SIGHASH Handling**: Each transaction input has a `sighash` field (defaults to `SIGHASH.ALL | SIGHASH.FORKID`) used during signing.
140+
141+
## Testing Structure
142+
143+
Tests are organized in two locations:
144+
1. **Root-level tests** (`tests/`): Classic test structure with direct imports
145+
2. **Nested tests** (`tests/bsv/`): Mirror the `bsv/` package structure
146+
147+
Test organization by feature:
148+
- `tests/bsv/primitives/`: Core cryptographic primitives
149+
- `tests/bsv/transaction/`: Transaction building and validation
150+
- `tests/bsv/auth/`: Full authentication protocol test suite
151+
- `tests/bsv/wallet/`: Wallet implementation tests
152+
- `tests/bsv/storage/`: Storage system tests
153+
- `tests/bsv/broadcasters/`: Broadcaster integration tests
154+
155+
**Running single test**: Use standard pytest patterns:
156+
```bash
157+
pytest tests/bsv/auth/test_auth_peer_basic.py::test_function_name
158+
pytest -k "test_pattern"
159+
```
160+
161+
## Code Style
162+
163+
- **PEP 8 compliance**: Follow Python standard style guide
164+
- **Type hints**: Use where appropriate (not comprehensive in current codebase)
165+
- **Docstrings**: Document functions, classes, and modules
166+
- **Comments**: Annotate complex logic
167+
168+
## Development Practices
169+
170+
- **Test-Driven Development**: Write tests before or alongside implementation where smart, quick, and reasonable. This helps ensure correctness and prevents regressions.
171+
- Run `pytest --cov=bsv --cov-report=html` to verify test coverage before committing
172+
- All PRs should maintain or improve current test coverage
173+
174+
## BRC-106 Compliance (Script ASM Format)
175+
176+
The SDK implements Assembly (ASM) representation of Bitcoin Script via `Script.from_asm()` and `Script.to_asm()` methods.
177+
178+
**BRC-106 Standard**: https://github.com/bitcoin-sv/BRCs/blob/master/scripts/0106.md
179+
180+
Key requirements from BRC-106:
181+
- Use full English names for op-codes (e.g., "OP_FALSE" not "OP_0")
182+
- Output should always use the most human-readable format
183+
- Multiple input names should parse to the same hex value
184+
- Ensure deterministic translation across different SDKs (Py-SDK, TS-SDK, Go-SDK)
185+
186+
**Current Implementation** (bsv/script/script.py:140-191):
187+
- `from_asm()`: Accepts both "OP_FALSE" and "OP_0", converts to b'\x00'
188+
- `to_asm()`: Currently outputs "OP_0" for b'\x00' (see OPCODE_VALUE_NAME_DICT override at constants.py:343)
189+
190+
**Note**: The current `to_asm()` output may need adjustment to fully comply with BRC-106's human-readability requirement (should output "OP_FALSE" instead of "OP_0").
191+
192+
### Working with ASM
193+
```python
194+
# Parse ASM string to Script
195+
script = Script.from_asm("OP_DUP OP_HASH160 abcd1234 OP_EQUALVERIFY OP_CHECKSIG")
196+
197+
# Convert Script to ASM representation
198+
asm_string = script.to_asm()
199+
200+
# Access script chunks
201+
for chunk in script.chunks:
202+
print(chunk) # Prints opcode name or hex data
203+
```
204+
205+
## Important Notes
206+
207+
- The SDK uses `coincurve` for ECDSA operations (not pure Python)
208+
- Encryption uses `pycryptodomex` (not standard `pycryptodome`)
209+
- Network operations require `aiohttp` for async HTTP
210+
- Tests require `pytest-asyncio` for async test support
211+
- Coverage configuration excludes tests and setup.py (see `.coveragerc`)
212+
- Git branches: `master` is main branch, `develop-port` is development branch
213+
214+
## Common Patterns
215+
216+
### Creating and Broadcasting a Transaction
217+
```python
218+
priv_key = PrivateKey(wif_string)
219+
source_tx = Transaction.from_hex(hex_string)
220+
221+
tx_input = TransactionInput(
222+
source_transaction=source_tx,
223+
source_txid=source_tx.txid(),
224+
source_output_index=0,
225+
unlocking_script_template=P2PKH().unlock(priv_key)
226+
)
227+
228+
tx_output = TransactionOutput(
229+
locking_script=P2PKH().lock(priv_key.address()),
230+
change=True
231+
)
232+
233+
tx = Transaction([tx_input], [tx_output])
234+
tx.fee() # Calculate and distribute fees
235+
tx.sign() # Sign all inputs
236+
await tx.broadcast() # Broadcast to network
237+
```
238+
239+
### Working with BEEF Format
240+
```python
241+
# Parse BEEF
242+
tx = Transaction.from_beef(beef_hex)
243+
244+
# Create BEEF
245+
beef_bytes = tx.to_beef()
246+
```
247+
248+
### Script Templates
249+
```python
250+
# P2PKH
251+
p2pkh = P2PKH()
252+
lock_script = p2pkh.lock(address_string)
253+
unlock_template = p2pkh.unlock(private_key)
254+
255+
# OP_RETURN
256+
op_return = OpReturn()
257+
data_script = op_return.lock(['Hello', b'World'])
258+
259+
# Multisig
260+
multisig = BareMultisig()
261+
lock_script = multisig.lock([pubkey1, pubkey2, pubkey3], threshold=2)
262+
unlock_template = multisig.unlock([privkey1, privkey2])
263+
```

bsv/fee_models/live_policy.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from typing import Optional, Tuple
99

1010
from ..constants import HTTP_REQUEST_TIMEOUT, TRANSACTION_FEE_RATE
11-
from ..http_client import default_http_client
11+
from ..http_client import default_sync_http_client
1212
from .satoshis_per_kilobyte import SatoshisPerKilobyte
1313

1414

@@ -88,19 +88,19 @@ def get_instance(
8888
)
8989
return cls._instance
9090

91-
async def compute_fee(self, tx) -> int: # type: ignore[override]
91+
def compute_fee(self, tx) -> int: # type: ignore[override]
9292
"""Compute a fee for ``tx`` using the latest ARC rate."""
93-
rate = await self._current_rate_sat_per_kb()
93+
rate = self.current_rate_sat_per_kb()
9494
self.value = rate
9595
return super().compute_fee(tx)
9696

97-
async def _current_rate_sat_per_kb(self) -> int:
97+
def current_rate_sat_per_kb(self) -> int:
9898
"""Return the cached sat/kB rate or fetch a new value from ARC."""
9999
cache = self._get_cache(allow_stale=True)
100100
if cache and self._cache_valid(cache):
101101
return cache.value
102102

103-
rate, error = await self._fetch_sat_per_kb()
103+
rate, error = self._fetch_sat_per_kb()
104104
if rate is not None:
105105
self._set_cache(rate)
106106
return rate
@@ -142,15 +142,15 @@ def _set_cache(self, value: int) -> None:
142142
with self._cache_lock:
143143
self._cache = _CachedRate(value=value, fetched_at_ms=time.time() * 1000)
144144

145-
async def _fetch_sat_per_kb(self) -> Tuple[Optional[int], Optional[Exception]]:
145+
def _fetch_sat_per_kb(self) -> Tuple[Optional[int], Optional[Exception]]:
146146
"""Fetch the latest fee policy from ARC and coerce it to sat/kB."""
147147
try:
148148
headers = {"Accept": "application/json"}
149149
if self.api_key:
150150
headers["Authorization"] = self.api_key
151151

152-
http_client = default_http_client()
153-
response = await http_client.get(
152+
http_client = default_sync_http_client()
153+
response = http_client.get(
154154
self.arc_policy_url,
155155
headers=headers,
156156
timeout=self.request_timeout,
@@ -195,4 +195,4 @@ def _extract_rate(payload: dict) -> Optional[int]:
195195
if isinstance(value, (int, float)) and value > 0:
196196
return max(1, int(round(value)))
197197

198-
return None
198+
return None

0 commit comments

Comments
 (0)