Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
fe10cd7
Fix consensus validation and implement BIP transport and wallet crypto
androolloyd Oct 11, 2026
3e5aee8
Record published repair branch and draft PR
androolloyd Oct 11, 2026
8a9454a
Fix reject-message allocation panic and use agent-led review
androolloyd Oct 11, 2026
50a5ddd
Share validated persistent chain state across RPC and sync
androolloyd Oct 11, 2026
28f5c91
Wire inbound BIP324 peers and verify Core block interoperability
androolloyd Oct 11, 2026
493115f
Fix outbound block relay and durable-tip reconnects
androolloyd Oct 11, 2026
c1b864a
Implement durable reorgs and validated runtime mempool
androolloyd Oct 11, 2026
a1b1c43
Implement bounded PSBT v2 wallet tooling and Core interoperability
androolloyd Oct 11, 2026
bfb9b80
Make BIP39 checksum regressions deterministic and verify reference seeds
androolloyd Oct 11, 2026
de85c1c
Validate and expose BIP371 Taproot PSBT records
androolloyd Oct 11, 2026
0145ce9
Reject inconsistent PSBT UTXOs and script commitments
androolloyd Oct 11, 2026
2aec151
Verify Taproot PSBT proofs and output commitments
androolloyd Oct 11, 2026
36ce3b1
test: drive Core relay timers with bounded regtest clock
androolloyd Oct 11, 2026
df2f7ed
fix: validate PSBT curve keys and extended-key metadata
androolloyd Oct 11, 2026
d4c76b7
fix: validate PSBT signature encodings and sighash metadata
androolloyd Oct 11, 2026
94ee6c4
Verify finalized PSBT spending conditions against supplied prevouts
androolloyd Oct 11, 2026
f0d22a9
Verify PSBT partial signature equations and committed script keys
androolloyd Oct 11, 2026
9910aab
Enforce P2SH redeem validation in the shared runtime path
androolloyd Oct 11, 2026
3206eb8
fix(consensus): enforce witness completion and signature encoding
androolloyd Oct 11, 2026
51f3266
fix(consensus): align Taproot execution and signature commitments
androolloyd Oct 11, 2026
158f03a
fix(consensus): bound Taproot control parsing and conditional execution
androolloyd Oct 11, 2026
7fc1b7a
feat(wallet): finalize verified PSBT spends from supplied signatures
androolloyd Oct 12, 2026
f1bf56a
fix(consensus): implement legacy and BIP143 signature scriptCode rules
androolloyd Oct 12, 2026
cdaf6c8
Enforce witness key and Taproot upgrade policy against Core
androolloyd Oct 12, 2026
1e2493e
Enforce Core sigop budgets across validation mempool and mining
androolloyd Oct 12, 2026
5343c49
Verify PSBT public derivations against supplied xpub anchors
androolloyd Oct 12, 2026
1965a78
Expand public wallet descriptors with actual HD keys and scripts
androolloyd Oct 12, 2026
4cd57ac
Fix BIP32 child tweaks and Unicode Base58 wallet decoding
androolloyd Oct 12, 2026
72db541
Replace Erlay retained-set stub with real BIP330 sketches
androolloyd Oct 12, 2026
55d4c01
Fix BIP152 wire keys and integrate compact block recovery
androolloyd Oct 12, 2026
ce9f787
Support bounded Taproot threshold wallet spends
androolloyd Oct 12, 2026
2c7daa2
Support 999-key Taproot thresholds and large PSBT files
androolloyd Oct 12, 2026
154b8ce
Budget Core CPU for maximum-size wallet acceptance
androolloyd Oct 12, 2026
0f2c1f2
feat(wallet): expand private descriptors with hardened derivation
androolloyd Oct 12, 2026
199bf30
feat(wallet): add bounded WIF PSBT ECDSA signing
androolloyd Oct 12, 2026
df0c797
Implement verified Taproot key-path PSBT signing
androolloyd Oct 12, 2026
36103cf
Sign committed Taproot script paths with WIF keys
androolloyd Oct 12, 2026
b7ee14d
Implement scoped HD private PSBT signing
androolloyd Oct 12, 2026
4fb3e1c
Archive legacy checkpoints before validated runtime resync
androolloyd Oct 12, 2026
b52a46a
Implement authenticated Core HTTP RPC on the runtime backend
androolloyd Oct 12, 2026
b5cc55c
Implement undo-backed verbose transaction and block RPC
androolloyd Oct 12, 2026
c5beeb5
Wire validated Electrum chain queries and transaction admission
androolloyd Oct 12, 2026
9b7b32f
Describe the supported experimental Electrum subset
androolloyd Oct 12, 2026
412c8a0
Index validated script history and serve real Electrum wallet queries
androolloyd Oct 12, 2026
95750f0
Stream validated Electrum script statuses per connection
androolloyd Oct 12, 2026
cda59dc
Implement validated Electrum header checkpoint proofs
androolloyd Oct 13, 2026
a49afdc
Wire validated fee observations and Electrum pool providers
androolloyd Oct 13, 2026
84c4760
Fix canonical genesis and Electrum transaction positions
androolloyd Oct 13, 2026
0710a8d
Enable validated local Electrum 1.4 negotiation
androolloyd Oct 13, 2026
c23dfd4
Replace simulated stages with atomic validated block providers
androolloyd Oct 13, 2026
45fc97d
fix(exex): persist validated events and supervise durable consumers
androolloyd Oct 13, 2026
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
7 changes: 7 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.git
target
fuzz/target
fuzz/corpus
fuzz/artifacts
*.profraw
tarpaulin-report.json
86 changes: 86 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
name: Build and verify
on:
push:
pull_request:
workflow_dispatch:
permissions:
contents: read
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
jobs:
workspace:
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v4
- run: rustup toolchain install 1.88.0 --profile minimal --component rustfmt --component clippy
- run: cargo fmt --all -- --check
- run: cargo test --workspace --all-features --locked
- run: cargo clippy --workspace --all-targets --all-features --locked
- run: cargo build --release --locked --package btc-node
- run: cargo check --locked --package btc-primitives --no-default-features
- run: cargo check --locked --package btc-primitives --no-default-features --features psbt
- run: cargo check --locked --package btc-primitives --no-default-features --features descriptor
- run: cargo check --locked --package btc-consensus --no-default-features
- run: cargo check --locked --package btc-network --no-default-features
- run: cargo check --locked --package btc-network --no-default-features --features erlay
- run: cargo check --locked --package btc-network --no-default-features --features compact-blocks
- run: cargo check --locked --package btc-storage --no-default-features
- run: cargo check --locked --package btc-rpc --no-default-features
advisories:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: rustup toolchain install stable --profile minimal
- run: cargo +stable install cargo-audit --locked
- run: cargo +stable audit --deny unsound
container:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: docker build -f devnet/Dockerfile -t btc-rust:ci .
- run: docker run --rm btc-rust:ci --help
core-interop:
runs-on: ubuntu-latest
# Includes CPU-heavy Core private signing at the BIP387 999-key maximum.
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- run: rustup toolchain install 1.88.0 --profile minimal
- run: cargo build --release --locked --package btc-node --bin btc-node --example legacy_checkpoint_fixture --example exex_fixture
- name: Fetch checksum-verified Bitcoin Core v29
shell: python
run: |
import hashlib, os, pathlib, tarfile, urllib.request
directory = pathlib.Path(os.environ["RUNNER_TEMP"]) / "btc-core29"
directory.mkdir()
name = "bitcoin-29.0-x86_64-linux-gnu.tar.gz"
base = "https://bitcoincore.org/bin/bitcoin-core-29.0/"
with urllib.request.urlopen(base + "SHA256SUMS", timeout=60) as source:
manifest = source.read().decode()
expected = next(line.split()[0] for line in manifest.splitlines() if line.endswith(name))
with urllib.request.urlopen(base + name, timeout=60) as source:
data = source.read()
assert hashlib.sha256(data).hexdigest() == expected, "Core checksum mismatch"
archive = directory / name
archive.write_bytes(data)
with tarfile.open(archive) as source:
source.extractall(directory, filter="data")
- run: python3 scripts/check_core_p2p.py --bitcoind "$RUNNER_TEMP/btc-core29/bitcoin-29.0/bin/bitcoind"
- run: python3 scripts/check_core_checkpoint.py --bitcoind "$RUNNER_TEMP/btc-core29/bitcoin-29.0/bin/bitcoind"
- run: python3 scripts/check_core_http.py --bitcoind "$RUNNER_TEMP/btc-core29/bitcoin-29.0/bin/bitcoind"
- run: python3 scripts/check_core_verbose.py --bitcoind "$RUNNER_TEMP/btc-core29/bitcoin-29.0/bin/bitcoind"
- run: python3 scripts/check_core_genesis.py --bitcoind "$RUNNER_TEMP/btc-core29/bitcoin-29.0/bin/bitcoind"
- run: python3 scripts/check_core_electrum.py --bitcoind "$RUNNER_TEMP/btc-core29/bitcoin-29.0/bin/bitcoind"
- run: python3 scripts/check_core_exex.py --bitcoind "$RUNNER_TEMP/btc-core29/bitcoin-29.0/bin/bitcoind"
- run: python3 scripts/check_core_fees.py --bitcoind "$RUNNER_TEMP/btc-core29/bitcoin-29.0/bin/bitcoind"
- run: python3 scripts/check_core_psbt.py --bitcoind "$RUNNER_TEMP/btc-core29/bitcoin-29.0/bin/bitcoind"
- run: python3 scripts/check_core_signer.py --bitcoind "$RUNNER_TEMP/btc-core29/bitcoin-29.0/bin/bitcoind"
- run: python3 scripts/check_core_signer.py --taproot-only --bitcoind "$RUNNER_TEMP/btc-core29/bitcoin-29.0/bin/bitcoind"
- run: python3 scripts/check_core_signer.py --taproot-script-only --bitcoind "$RUNNER_TEMP/btc-core29/bitcoin-29.0/bin/bitcoind"
- run: python3 scripts/check_core_signer.py --hd-only --bitcoind "$RUNNER_TEMP/btc-core29/bitcoin-29.0/bin/bitcoind"
- run: python3 scripts/check_erlay.py
96 changes: 96 additions & 0 deletions .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
name: Fuzz smoke tests
on:
push:
pull_request:
workflow_dispatch:
schedule:
- cron: "23 4 * * 1"
permissions:
contents: read
jobs:
fuzz:
runs-on: ubuntu-latest
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
target: [fuzz_transaction_decode, fuzz_block_decode, fuzz_script_execute, fuzz_p2p_message, fuzz_bech32_decode, fuzz_varint_decode, fuzz_witness_verify, fuzz_block_template, fuzz_psbt_decode, fuzz_descriptor, fuzz_bip32, fuzz_erlay, fuzz_compact_blocks]
steps:
- uses: actions/checkout@v4
- run: rustup toolchain install nightly --profile minimal
- run: cargo +nightly install cargo-fuzz --locked
- name: Seed published PSBT vectors
if: matrix.target == 'fuzz_psbt_decode'
shell: python
run: |
import json, pathlib
corpus = pathlib.Path("fuzz/corpus/fuzz_psbt_decode")
corpus.mkdir(parents=True, exist_ok=True)
for name in ["bip174-utxo", "bip370", "bip371", "bip341-commitments", "bip32-keys", "psbt-signatures"]:
source = pathlib.Path(f"crates/primitives/tests/data/{name}.json")
key = "psbt_vectors" if name in ["bip341-commitments", "bip32-keys", "psbt-signatures"] else "vectors"
for i, vector in enumerate(json.loads(source.read_text())[key]):
(corpus / f"{name}-{i}").write_bytes(bytes.fromhex(vector["hex"]))
- name: Seed public derivation proofs
if: matrix.target == 'fuzz_psbt_decode'
run: python3 scripts/seed_psbt_derivations.py --corpus fuzz/corpus/fuzz_psbt_decode
- name: Seed independently signed threshold wallets
if: matrix.target == 'fuzz_psbt_decode'
shell: python
run: |
import json, pathlib
corpus = pathlib.Path("fuzz/corpus/fuzz_psbt_decode")
for i, vector in enumerate(json.loads(pathlib.Path("crates/primitives/tests/data/taproot-thresholds.json").read_text())["vectors"]):
for field in ["partial_psbt", "final_psbt"]:
(corpus / f"threshold-{i}-{field}").write_bytes(bytes.fromhex(vector[field]))
- name: Seed independent ECDSA signer cases
if: matrix.target == 'fuzz_psbt_decode'
shell: python
run: |
import json, pathlib
corpus = pathlib.Path("fuzz/corpus/fuzz_psbt_decode")
for i, vector in enumerate(json.loads(pathlib.Path("crates/primitives/tests/data/ecdsa-signer.json").read_text())["vectors"]):
(corpus / f"signer-{i}").write_bytes(bytes.fromhex(vector["psbt"]))
for i, vector in enumerate(json.loads(pathlib.Path("crates/primitives/tests/data/taproot-key-signer.json").read_text())["vectors"]):
(corpus / f"taproot-key-signer-{i}").write_bytes(bytes.fromhex(vector["psbt"]))
leaves = json.loads(pathlib.Path("crates/primitives/tests/data/taproot-script-signer.json").read_text())
for i, vector in enumerate(leaves["vectors"] + leaves["unsupported"]):
(corpus / f"taproot-script-signer-{i}").write_bytes(bytes.fromhex(vector["psbt"]))
for i, vector in enumerate(json.loads(pathlib.Path("crates/primitives/tests/data/hd-signer.json").read_text())["vectors"]):
(corpus / f"hd-signer-{i}").write_bytes(bytes.fromhex(vector["psbt"]))
- name: Seed published descriptors
if: matrix.target == 'fuzz_descriptor'
shell: python
run: |
import json, pathlib, sys
sys.path.insert(0, "scripts")
from check_core_psbt import regtest_descriptor
corpus = pathlib.Path("fuzz/corpus/fuzz_descriptor")
corpus.mkdir(parents=True, exist_ok=True)
vectors = json.loads(pathlib.Path("crates/primitives/tests/data/descriptors.json").read_text())["vectors"]
vectors += json.loads(pathlib.Path("crates/primitives/tests/data/private-descriptors.json").read_text())["vectors"]
thresholds = json.loads(pathlib.Path("crates/primitives/tests/data/taproot-thresholds.json").read_text())
vectors += thresholds["published"] + thresholds["descriptors"]
for i, vector in enumerate(vectors):
(corpus / f"published-{i}").write_text(vector["descriptor"])
translated = regtest_descriptor(vector["descriptor"])
if translated != vector["descriptor"]:
(corpus / f"regtest-{i}").write_text(translated)
- name: Seed published BIP32 keys
if: matrix.target == 'fuzz_bip32'
run: python3 scripts/seed_bip32.py --corpus fuzz/corpus/fuzz_bip32
- name: Seed independent BIP330 sketches
if: matrix.target == 'fuzz_erlay'
run: python3 scripts/seed_erlay.py --corpus fuzz/corpus/fuzz_erlay
- name: Seed independent BIP152 messages
if: matrix.target == 'fuzz_compact_blocks'
run: python3 scripts/seed_compact_blocks.py --corpus fuzz/corpus/fuzz_compact_blocks
- name: Seed framed BIP152 messages
if: matrix.target == 'fuzz_p2p_message'
run: python3 scripts/seed_compact_blocks.py --corpus fuzz/corpus/fuzz_p2p_message --p2p
- run: cargo +nightly fuzz run ${{ matrix.target }} -- -max_total_time=30 -rss_limit_mb=2048
- uses: actions/upload-artifact@v4
if: failure()
with:
name: fuzz-${{ matrix.target }}
path: fuzz/artifacts
9 changes: 8 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,15 @@
/fuzz/target
/fuzz/corpus
/fuzz/artifacts
Cargo.lock
*.profraw
tarpaulin-report.json
*.swp
*.swo
.DS_Store
.env

# Python interoperability tools
__pycache__/
# Per-start local RPC authentication material.
.cookie
.cookie.tmp-*
33 changes: 33 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# btc-rust development

Read `docs/BUILD_PLAN.md` and `docs/REVIEW.md` before selecting repair work.
The user authorized autonomous repository review, bug fixes, and replacement
of placeholder implementations. Prioritize BIP transport and wallet crypto,
then the remaining runtime backends. Resolve routine choices without asking.

- Preserve user changes and existing test artifacts; inspect Git status first.
- Trace runtime callers and use primary Bitcoin specifications/Core sources.
- Use independent vectors or meaningful regressions for consensus/crypto changes.
- A passing self-roundtrip does not establish protocol interoperability.
- Never replace missing verification with success, fabricated data, or a hash
that only resembles a curve key. Report unsupported behavior explicitly.
- Proposed consensus rules remain explicit experiments; do not activate them
on ordinary networks merely because a BIP is marked Complete.
- Batch each coherent set of changes before running tests, as the user requested
to save time. Use `./scripts/check.sh` as the final local gate, with focused
follow-up checks only for failures or unresolved concerns. Keep `Cargo.lock`
synchronized with dependency changes.
- Update the build plan with actual acceptance results and remaining work.
- The user authorized keeping the repository updated without pull requests.
Review changes yourself and commit verified milestones to
`codex/review-bip-runtime-fixes`. Do not create PRs or require user review.
Direct updates to `main` and closing the existing draft are pending explicit
authorization after automatic approval review rejected those actions.
Preserve unrelated user changes. Use normal pushes; do not force-push.
Main integration, releases, deployment and external messages need separate
authorization.
- When SSH authentication is unavailable, use authenticated HTTPS with the
GitHub CLI credential helper; never print or embed raw access tokens.

The workspace uses Rust 1.88 and has twelve library/binary crates. The fuzz
workspace is separate and uses nightly for sanitizer runs.
8 changes: 4 additions & 4 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ A modular Rust Bitcoin full node inspired by reth's architecture.
## Build & Test

```bash
cargo build # build all crates
cargo test # run all tests (~280+ tests)
cargo build --workspace --locked # build all crates
cargo test --workspace --all-features --locked # run all workspace tests
cargo test -p <crate> # test individual crate
cargo run -- --help # CLI help
cargo run -p btc-node -- --help # CLI help
```

## Architecture

8-crate workspace following reth's patterns:
12-crate workspace following reth's patterns:

- **btc-primitives** — Hash, encode, script, tx, block, address, amount, bech32. All our own code, no rust-bitcoin dependency.
- **btc-consensus** — Script engine with real CHECKSIG/secp256k1, sighash (500/500 Core vectors), UTXO processing, header chain management, taproot, segwit witness verification, chain reorgs.
Expand Down
Loading
Loading