Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Documentation/Compatibility/BRC-Matrix.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
| BRC-84 `key-derivation/0084.md` | Linked derivation using HMAC keyed by serialized counterparty public key rather than ECDH shared secret. | **None direct** | `BSVKeys` optional derivation mode | BRC-42/43 primitives. | Inline examples. | Security/domain-separation choice; do not silently fold into Type-42.
| BRC-86 `key-derivation/0086.md` | Bidirectionally authenticated/restricted Type-42 setup and privacy controls. | **None direct** | `BSVAuth` + `BSVKeys` | BRC-42/43 and peer authentication. | Protocol walkthrough. | Higher-layer handshake, not primitive derivation parity.
| BRC-140 `key-derivation/0140.md` | Shamir sharing over secp256k1 field prime **p** (not group order n); a CSPRNG-defined interpolation polynomial; HMAC-SHA512-derived share x-coordinates; shares encoded `base58(x).base58(y).threshold.integrity`; integrity is first 8 hex chars of HASH160(compressed pubkey); Lagrange reconstruction; `n>=2`, `m>=2`, `n<=m`. | **Full**: `ec.PrivateKey.ToPolynomial`, `ToKeyShares`, `ToBackupShares`, `PrivateKeyFromKeyShares`, `PrivateKeyFromBackupShares`; `primitives/keyshares.KeyShares`, `Polynomial`. | `BSVCrypto`, `BSVKeys` | Field arithmetic mod p, HMAC-SHA512, HASH160, Base58, secure random. | BRC shows share/recovery examples; generation is randomized, so use an injected deterministic-randomness harness plus reconstruction differentials. | BRC describes counter material `[i, attempt, 32 random bytes]` without integer widths/endian; Go uses 4-byte BE `i`, 4-byte BE `attempt`, then 32 bytes. Freeze that for parity or obtain clarification.
| BRC-157 `key-derivation/0157.md` | Treat validated BIP-39 entropy as the backup subject; left-pad shorter entropy to a 32-byte BRC-140 key; retain or infer the original entropy length; derive profiles at hardened `m/0'/i'` from the BIP-39 seed with an empty passphrase by default. | **Composed primitives**: Go `compat/bip39`, `compat/bip32`, and `ec.PrivateKey` BRC-140 methods; TypeScript `Mnemonic`, `HD`, and `PrivateKey` methods. | `BSVCompat`, using `BSVKeys` BRC-140 | BRC-32/75/140, secp256k1 scalar validation. | Worked 12-word/root vector in BRC plus independently derived profile and all-word-count vectors. | Trusted, authenticated entropy-length metadata is required for exact restoration of imported 12–21-word mnemonics, and wallet restore should verify the expected identity because an oversized supported length is intrinsically ambiguous. The leading-zero heuristic is explicitly last-resort. A nonempty passphrase is a separate unrecoverable secret and must remain opt-in.
| BRC-30 `transactions/0030.md` | Extended Format: marker `0000000000EF` after version; each input additionally includes source satoshis and source locking script, permitting offline fee/signature verification. | **Full with spec-width mismatch**: `transaction.Transaction.ReadFrom` EF detection; `Transaction.EF`, `EFHex`; extended input parsing. | `BSVTransaction` | BRC-12, amount type, script codec. | Go EF tests and document examples. | BRC says source satoshis is 4 bytes, but Go reads/writes uint64 (8 bytes). Bitcoin amounts and de-facto SDK behavior favor 8; raise an erratum and gate Swift parity on 8 unless project explicitly chooses strict prose.
| BRC-58/61/71 `transactions/{0058,0061,0071}.md` | Historical Merkle path JSON and binary encodings; reverse displayed txid/hash bytes as required; SHA256d branches; CMP aggregation. | **Legacy/indirect**: current `transaction.MerklePath` primarily reflects BRC-74. | `BSVSPV` compatibility codecs only | SHA256d, CompactSize, byte order. | Document examples. | Treat as import compatibility, not the canonical in-memory/public serialization model.
| BRC-74 `transactions/0074.md` | Unified BUMP binary: blockHeight CompactSize, treeHeight byte ≤64; each level nLeaves; leaf offset CompactSize; flags 0=data, 1=duplicate/no hash, 2=txid hash; 32-byte hashes. JSON `{blockHeight,path}`. Compute root; merge only equal-height/equal-root paths; deduplicate while retaining txid flags. | **Full**: `transaction.MerklePath`, `PathElement`, `NewMerklePathFromBinary`, `NewMerklePathFromHex`, `NewMerklePathFromReader`, `Bytes`, `ComputeRoot`, `Verify`, `Combine`, `MerkleTreeParent`. | `BSVSPV` | BRC-12 byte primitives, SHA256d, transaction-id byte order. | Exact BRC hex and expected root `57aab6…83b4`; same fixture is in `transaction/merklepath_test.go`. | BRC pseudocode’s `if (!index)` would reject valid index 0; prose/format and Go support index 0. Implement semantic rule, not that typo.
Expand Down
3 changes: 2 additions & 1 deletion Documentation/Compatibility/Rulings.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ behind the general rule.
| COMP-031 | Go `NewSymmetricKey` left-pads every key shorter than 32 bytes, including an empty key, retains oversized keys until AES reports an error, and `NewSymmetricKeyFromString` terminates the process on malformed Base64. | Swift accepts 1...32 input bytes, preserves the interoperable left-zero-padding rule, rejects empty and oversized keys immediately, parses only strict standard-padded Base64 with typed errors, and redacts default stringification. Encryption delegates to Swift Crypto AES-GCM with a fresh 32-byte nonce and emits the exact Go `nonce || ciphertext || 16-byte tag` envelope without AAD. | Empty all-zero keys and process termination are unsafe construction artifacts, not required wire behavior. Unit tests cover every relevant key length, malformed Base64, randomness, empty plaintext, truncation, and tampering. Persistent pinned-Go tests compare deterministic envelopes and decrypt in both directions, including 31-byte coordinate-style keys. |
| COMP-032 | The Bitcoin Signed Message compatibility wire format defines compact headers `27...34`, while Go's address-verification helper derives a mainnet address regardless of the caller's intended network. | Swift accepts only headers `27...34`, preserves their recovery and compression bits, and verifies the recovered key against a supplied `Address` on that address's network. | The signature wire format is network-neutral; address encoding is not. Tests cover all eight valid headers, adjacent invalid values, mainnet and testnet verification, bitcoinj vectors, and bidirectional pinned-Go signing and recovery. |
| COMP-033 | Pinned Go Electrum ECIES infers embedded versus omitted sender-key layout from packet length, and Bitcore compatibility derives keys from the minimal unsigned shared X coordinate while its default encryption path uses an all-zero IV. | Swift requires callers to select the Electrum embedded or omitted layout explicitly, bounds packet and ciphertext parsing, and authenticates with constant-time HMAC comparison before decryption. The ordinary encryption APIs always generate a fresh ephemeral sender key. Deterministic caller-supplied sender keys and Bitcore IVs are exposed only through explicitly named `encryptCompatibility` seams that warn against reuse. | Explicit layouts avoid ambiguous length heuristics without changing valid packets, while the obvious encryption path is safe against accidental key reuse. Unit and pinned-Go differential tests cover both Electrum layouts, Bitcore minimal-X derivation, zero- and explicit-IV envelopes, truncation, malformed keys, bad block lengths, and authentication failures. |
| COMP-034 | BRC-140 and pinned Go perform secret sharing with variable-time big-integer interpolation over the secp256k1 field prime, and recovery can otherwise ignore shares beyond the threshold. | Swift uses the BRC-140 field prime, accepts only canonical bounded share text, caps splitting and recovery at 20 shares, and validates every extra share against the threshold-sized interpolation basis. Secret-bearing text is available only through the explicit `backupString` export; normal and debug descriptions are redacted. | Variable-time interpolation is limited to offline backup workflows and is not presented as safe for attacker-observable online use. Tests cover canonical round trips, configuration and CPU bounds, malformed or inconsistent shares, extra-share validation, integrity checks, and redaction. |
| COMP-034 | BRC-140, pinned Go, and TypeScript reconstruct from the first threshold shares and ignore later shares; coordinate selection accepts only a candidate resolved within five attempts. | Swift follows the same first-threshold rule after requiring common threshold and integrity fields across the supplied set, and checks duplicate x-coordinates among the shares actually used. It accepts only canonical bounded share text, caps splitting and recovery at 20 shares, and performs exactly five coordinate attempts. Secret-bearing text is available only through the explicit `backupString` export; normal and debug descriptions are redacted. | This replaces the former Swift-only extra-share consistency policy with exact BRC-140 recovery semantics. Callers that want to try or compare different quorums must select and order them explicitly. Tests cover canonical round trips, five-attempt exhaustion, configuration and CPU bounds, malformed shares, first-quorum duplicate rejection, ignored extras, integrity checks, and redaction. |
| COMP-035 | Swift's synthesized diagnostics and reflection can expose stored secret fields even when `description` is redacted, and the initial WIF façade returned the private-key encoding from `description`. | Every public value that stores private-key, symmetric-key, mnemonic, DRBG, extended-private-key, WIF, or key-share material supplies redacted string, debug, and custom-mirror output. Secret serialization remains available only through explicitly named exports such as `bytes`, `phrase`, `serialized`, `encoded`, and `backupString`. | Ordinary interpolation, `dump`, `Mirror`, debugger diagnostics, and logging must not become accidental key-export APIs. Tests capture each diagnostic path, require an empty custom mirror, and prove known secret fragments are absent while explicit exports retain exact wire compatibility. |
| COMP-036 | Pinned Go's BRC-77 parser accepts trailing ASN.1 bytes and its ECDSA verifier accepts mathematically valid high-S signatures. The anyone recipient is encoded by one zero discriminator and uses the protocol's scalar-one derivation key. | Swift requires one complete, canonical strict-DER signature. A canonical high-S signature remains parseable but verification returns `false`; signatures created by either SDK are low-S. Anyone mode consumes exactly one zero byte and ignores any supplied recipient private key. | Canonical packets remain bidirectionally compatible while parser ambiguity and the existing strict ECDSA policy stay visible. Unit and pinned-Go tests cover trailing data, non-minimal DER, high-S, anyone packets, and recipient-specific packets. |
| COMP-037 | Retrying a transaction POST can duplicate an accepted submission, while a provider response cannot establish which transaction the client serialized. Cancellation or transport failure after a POST starts does not prove that the provider rejected it. | WhatsOnChain chain-tracker GETs retry only bounded transient failures and honor a capped `Retry-After`. Broadcast POSTs are never retried, even under a policy with additional attempts. Swift computes the transaction ID locally, requires one canonical lowercase provider ID that matches it, rejects redirects and oversized responses, and redacts transaction material from error excerpts. | Read retries remain bounded and broadcast side effects are not duplicated automatically. Broadcast failure after dispatch is reported as failure with uncertain delivery; callers must reconcile the transaction ID before resubmitting. Tests cover retry classes and bounds, cancellation, strict responses, ID mismatch, redaction, and exactly one POST. |
Expand Down Expand Up @@ -70,6 +70,7 @@ behind the general rule.
| COMP-060 | Pinned Go certificate exchange does not enforce requested field or type completeness, does not correlate a response with one pending request, and carries an unsigned requested-certificate HTTP header. | Swift provides bounded typed post-authentication certificate requests and responses. It signs canonical request or response JSON, correlates one exact pending request, and requires separate BRC-52 certificate validation. The HTTP header remains rejected. | Canonical signed BRC-103 messages remain compatible. Swift does not accept missing disclosures, unsolicited responses, or unsigned HTTP certificate requests. |
| COMP-061 | Pinned Go overlay HTTP and policy use unbounded response reads, unsafe binary counts and indexes, default trackers, unbounded fan-out, and scheduler-dependent result order. | Swift places bounded HTTPS facilitators in `BSVNetwork` and deterministic resolver and one-shot broadcaster policy in `BSVOverlay`. All hosts and limits are explicit, discovery verifies signed tokens, and failed submission has uncertain delivery. | Preserves canonical SHIP and SLAP requests and results without hidden network policy, unsafe retry, unbounded work, or nondeterministic aggregation. |
| COMP-062 | Pinned Go provides one concrete UHRP downloader with internal resolver, tracker, and HTTP policy. | Swift keeps UHRP values in `BSVStorage` and places the injected resolver and bounded HTTPS downloader in `BSVNetwork`. It validates the exact four-field advertisement, binds it to the requested identifier, sorts hosts, makes one GET per host, and verifies the SHA-256 result. | Preserves the canonical `ls_uhrp` query and advertisement layout without hidden trackers, cleartext hosts, unbounded bodies, automatic retry, or mutable network policy. |
| COMP-063 | BRC-157 composes BIP-39, BIP-32, and BRC-140 without storage, transport, or a concrete wallet implementation. Go and TypeScript expose the constituent behavior from their SDK packages. | Swift places the composed entropy-backup and hardened-profile API in opt-in `BSVCompat`, which already owns BIP-39 and BIP-32 and depends on `BSVKeys` for BRC-140. It remains outside the modern `BSV` umbrella. | Keeps the reusable backup primitive in the SDK while preserving the explicit compatibility boundary required by BIP-39 and BIP-32. Toolbox wallets may consume it without owning or duplicating the primitive. |

## Scope decisions

Expand Down
4 changes: 3 additions & 1 deletion Documentation/Planning/Roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,12 @@ canonical parsing and bounded offline interpolation.
Accepted checkpoint: `BSVKeys` contains bounded WIF and P2PKH addresses for
mainnet and testnet. The opt-in `BSVCompat` product contains BIP-32 hierarchical
deterministic keys, English BIP-39 mnemonics with the fixed standard PBKDF2
profile, Bitcoin Signed Message, and Electrum- and Bitcore-compatible ECIES.
profile, BRC-157 entropy-rooted mnemonic and share backups, Bitcoin Signed
Message, and Electrum- and Bitcore-compatible ECIES.

- WIF and P2PKH addresses. (Accepted)
- BIP-32 and BIP-39. (Accepted)
- BRC-157 entropy-rooted backup and profile derivation. (Accepted)
- Bitcoin Signed Message. (Accepted)
- Electrum- and Bitcore-compatible ECIES. (Accepted)

Expand Down
33 changes: 31 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,7 @@ print(script.isPayToPublicKeyHash)
- Supports BRC-42 child keys and BRC-94 shared-secret proofs.
- Supports WIF and P2PKH addresses.
- Supports BRC-140 key shares for offline backups.
- Supports BRC-157 entropy-rooted mnemonic and share backups through opt-in `BSVCompat`.

### Wallets, messages, and network services

Expand Down Expand Up @@ -223,7 +224,7 @@ print(script.isPayToPublicKeyHash)
| `BSVCrypto` | Supplies hashes, symmetric cryptography, key derivation functions, and random data. |
| `BSVKeys` | Supplies secp256k1 keys and signatures, ECDH, key tweaks, WIF, addresses, BRC-42, BRC-94, and BRC-140. |
| `BSVMessage` | Supplies bounded BRC-77 signed messages and BRC-78 encrypted messages. |
| `BSVCompat` | Supplies opt-in BSM, ECIES, BIP-32, and BIP-39 compatibility APIs. |
| `BSVCompat` | Supplies opt-in BSM, ECIES, BIP-32, BIP-39, and BRC-157 backup APIs. |
| `BSVScript` | Supplies Script data, BIP-276, opcodes, ASM, numbers, and templates. |
| `BSVKVStore` | Supplies bounded, transport-neutral, Go-compatible one-field key-value tokens. |
| `BSVStorage` | Supplies bounded UHRP identifiers, content values, and a transport-neutral content-provider boundary. |
Expand Down Expand Up @@ -257,7 +258,7 @@ Use the modern protocol API for new applications when a replacement exists:
| Bitcoin Signed Message | BRC-77 `SignedMessage` from `BSVMessage` |
| Electrum and Bitcore ECIES | BRC-78 `EncryptedMessage` from `BSVMessage` |
| BIP-32 protocol keys | BRC-42 derivation from `BSVKeys` |
| BIP-39 mnemonic backup or import | No replacement; use `BSVCompat` when required |
| BIP-39 mnemonic or BRC-157 backup | No replacement; use `BSVCompat` when required |

## Examples

Expand All @@ -278,6 +279,34 @@ let account = try master.derived(path: "m/44'/236'/0'")
print(account.neutered.serialized)
```

### Entropy-rooted backups

Create interchangeable BRC-157 mnemonic and BRC-140 share backups, then derive
the BRC-100 root at `m/0'/0'`:

```swift
import BSVCompat

let backup = try BRC157Entropy.generate()
let words = backup.mnemonic.phrase
let shares = try backup.backupShares(threshold: 2, shareCount: 3)
let rootKey = try backup.rootKey()

let recovered = try BRC157Entropy.recover(
from: [shares[0], shares[2]],
entropyByteCount: backup.entropyByteCount
)
let recoveredRootKey = try recovered.rootKey()
precondition(recoveredRootKey == rootKey)
```

Persist trusted, authenticated `entropyByteCount` metadata with shares imported
from a 12-, 15-, 18-, or 21-word mnemonic so recovery can reproduce the exact
original words. A too-large supported length is intrinsically ambiguous and can
derive a different valid wallet, so wallet restore should also verify the
expected identity key. A nonempty BIP-39 passphrase is a separate secret and is
not recoverable from the mnemonic or shares.

### BIP-276

Encode a Script payload as bounded BIP-276 text:
Expand Down
Loading
Loading