Skip to content

Repository files navigation

bip360p_enforcer — a BIP360+ soft-fork enforcer (P2MR + covenant opcodes)

A CUSF ("Core Untouched Soft Fork") enforcer: a sidecar daemon that watches an unmodified Bitcoin Core node and enforces the BIP360+ ruleset out-of-band — hence the +: it does more than plain BIP360. P2MR and the covenant opcodes look anyone-can-spend to a stock node, so the enforcer adds the missing consensus rules: it validates every relevant spend and calls invalidateblock on any block containing an invalid one, and keeps invalid spends out of the block templates it serves to miners. No patched Bitcoin Core is required.

The enforced ruleset:

  • BIP 360 Pay-to-Merkle-Root (P2MR, SegWit v2) outputs and their post-quantum signature spends (four schemes, below).
  • BIP360+ opcodes given fail-closed meaning inside standard Taproot v1 script-path leaves (they are OP_SUCCESSx to stock Core): OP_CAT (BIP347), OP_CHECKTEMPLATEVERIFY (BIP119), and OP_VAULT / OP_VAULT_RECOVER (BIP345) — the last with full Schnorr trigger authorization and BIP112/65 timelocks, so private-key ownership is the sole withdrawal authority.

Four spend schemes are validated inside P2MR tapscript leaves:

Scheme Signature Notes
secp256k1 Schnorr (BIP340) 64 B baseline, not post-quantum
ML-DSA-44 (CRYSTALS-Dilithium, FIPS 204) 2420 B lattice
SLH-DSA-SHA2-128s (SPHINCS+, FIPS 205) 7856 B hash-based
hybrid EC + SLH (overload) 64 + 7856 B Schnorr and SLH-DSA in one leaf

Validation lives in lib/validator/pqc/, and the wallet's spend construction in lib/validator/pqc/signer.rs. See docs/CUSF-BIP360.md for the activation height, the signature-length "overload" model, and the module layout.

Requirements

  1. Bitcoin Core (v29+), with the ZMQ pubsequence notifier and the REST interface (-rest) enabled; -txindex is required when running with a wallet.

  2. Rustc & Cargo, version 1.88.0 or higher. Installing via Rustup is recommended.

Supported Bitcoin Core versions

The enforcer supports running against the 3 latest major versions of Bitcoin Core. getnetworkinfo is queried at startup and refuses to run against an unsupported Bitcoin Core version. The supported set lives in lib/version.rs; see --help for the override flags.

Getting started

Building/running:

# Compiles the project
$ cargo build

# See available options
$ cargo run -- --help

# Starts the Connect RPC server at localhost:50001
# Adjust these parameters to match your local Bitcoin
# Core instance
$ cargo run -- \
  --node-rpc-addr=localhost:38332 \
  --node-rpc-user=user \
  --node-rpc-pass=password \
  --node-zmq-addr-sequence=tcp://0.0.0.0:29000

# You should now be able to fetch data from the server!
$ curl -H 'application/json' \
        http://localhost:50051/cusf.validator.v1.ValidatorService/GetChainInfo
{
  "network": "NETWORK_SIGNET"
}

Interacting with the enforcer

The CUSF enforcer exposes multiple Connect (gRPC) services. These can be interacted with using either plain curl or a Connect/gRPC client of your choice, for example buf curl or grpcurl.

Some examples of interacting with the enforcer using curl, assuming you expose the server at the default address localhost:50051:

# Define an alias for ease of use
$ alias enforcer_curl='curl -X POST -H "Content-Type: application/json"'

# List all the available RPCs
$ buf_curl --list-methods http://localhost:50051
cusf.mainchain.v1.ValidatorService/GetBlockHeaderInfo
cusf.mainchain.v1.ValidatorService/GetChainInfo
cusf.mainchain.v1.ValidatorService/GetChainTip
cusf.mainchain.v1.ValidatorService/GetSidechains
cusf.mainchain.v1.WalletService/CreateNewAddress
cusf.mainchain.v1.WalletService/CreateSidechainProposal
... list continues

# Fetching data with a RPC that takes no input data
$ enforcer_curl http://localhost:50051/cusf.mainchain.v1.ValidatorService/GetChainInfo
{
  "network": "NETWORK_SIGNET"
}

# Fetching data with a RPC that takes input data
$ request='{"block_hash": {"hex": "000002a78fc54150bb2d4cdb0fb19bcf744f2877faf90a172972fca5daf5fe92"}}'
$ enforcer_curl -d "$request" http://localhost:50051/cusf.mainchain.v1.ValidatorService/GetBlockHeaderInfo
{
  "headerInfo": {
    "blockHash": {
      "hex": "000002a78fc54150bb2d4cdb0fb19bcf744f2877faf90a172972fca5daf5fe92"
    },
    "prevBlockHash": {
      "hex": "000002501d569e62a56ea175896d4348dd9cfef1d700e5b06250486df07c9225"
    },
    "height": 34998,
    "work": {
      "hex": "14d4490000000000000000000000000000000000000000000000000000000000"
    }
  }
}

Regtest

By default, the enforcer runs against our custom signet. If you instead want to run against a local regtest, you need to also run a local regtest Electrum server. There are multiple implementations of Electrum servers, an easy-to-use one is mempool/electrs.

For complete instructions on how to do this, consult the official docs.

A quickstart (that might not work, in case you're missing some dependencies):

$ git clone https://github.com/mempool/electrs

$ cd electrs

$ cargo run --bin electrs --release -- \
    --network regtest \
    --cookie=user:password \
    --jsonrpc-import

Logging

The application uses the tracing crate for logging. Logging is configured through setting the --log-level argument. Some examples:

# Prints ALL debug logs
$ cargo run ... --log-level DEBUG

Logs can also be configured via env vars, which take precedence over CLI args.

# Prints logs at the "info" level and above, plus our logs the "debug" level and above
$ RUST_LOG=info,bip360p_enforcer_lib=debug cargo run ...

Working with the proto files

The proto definitions live in the upstream LayerTwo-Labs/cusf_sidechain_proto repo. We pin a specific commit in buf.gen.yaml and check the generated Rust code into lib/proto/generated/. Generation is performed by the remote buf.build/anthropics/buffa (message types) and buf.build/anthropics/connect-rust (Connect RPC service stubs) plugins.

To regenerate (after bumping the ref: in buf.gen.yaml):

$ just generate

Code formatting

Rust code is formatted with rustfmt. You need to ensure you have a nightly version of Rust installed on your system. To format the project files from the command line:

$ just fmt          # preferred: nightly rustfmt + prettier (npx/bunx) + buf if present
$ just fmt-check    # nightly rustfmt --check (no stable “unstable option” spam)

rustfmt.toml uses nightly-only options (group_imports, imports_granularity). Always use cargo +nightly fmt (as just fmt / just fmt-check do), not stable cargo fmt, if you want a clean check.

Markdown/YAML: just fmt runs Prettier via bunx or npx when available. buf format is optional (skipped with a note if buf is not on PATH).

Linting Rust code

Rust code is linted with Clippy.

$ just clippy                                        # format check + workspace clippy
# or directly:
$ cargo clippy --workspace --all-targets -- -D warnings

Unit tests

$ just test-unit                                     # all workspace unit tests
$ just test-pqc                                      # P2MR / PQC validation tests only

Integration tests

Integration tests can be run using

$ just setup-core            # download stock Bitcoin Core, write integrationtests.env
$ just it-all                # run the full trial suite against stock bitcoind
$ just it <trial_name>       # run one trial

Requires integrationtests.env (see just setup-core / just setup / just setup-p2mr).

Profiling

# Generate a flamegraph for Rust code. This does NOT
# measure syscalls/IO wait
# https://github.com/flamegraph-rs/flamegraph
$ cargo install flamegraph
$ cargo flamegraph --  --data-dir ./datadir \
          --node-rpc-addr=localhost:38332 \
          --node-rpc-user=user \
          --node-rpc-pass=password \
          --enable-mempool --exit-after-sync 100000

# macOS only
$ just trace-macos

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages