Skip to content

QuietProof v1 specification ​

Status: normative draft. An implementation claiming QuietProof v1 compatibility MUST follow this document exactly.

WARNING

This is free and open source software provided without warranty under the terms of the GNU GPL v3 license. Professional support, maintenance, integration services, and contractual warranty options are available separately. Please contact us for further information.

QuietProof v1 is an off-chain proof of control for a bounded set of native P2WPKH outputs. It neither spends nor locks Bitcoin. P2SH-wrapped P2WPKH, P2WSH, multisig, P2TR, and arbitrary scripts are unsupported.

The generic zero-knowledge primitive is PoKC (Proof of Key Control). QuietProof is the product and protocol that binds a PoKC proof to claims, verifier policy, Bitcoin chain evidence, and replay protection.

1. Conventions and constants ​

All multi-byte protocol integers are fixed-width unsigned big-endian values. u8, u16, u32, and u64 name their widths. Hex strings in this document are lowercase and omit 0x. || concatenates bytes.

secp256k1 uses prime field

p = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F

and subgroup order

n = FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141

The private scalar is an integer x with 1 <= x < n. Let P = xG, normalized to a finite affine point (Px, Py). Px_be is exactly 32 big-endian bytes. SEC = (0x02 + (Py mod 2)) || Px_be. The public witness program is

program = RIPEMD160(SHA256(SEC))

and the only accepted script is 00 14 || program (22 bytes). The circuit MUST constrain scalar range/bitness, P = xG, finiteness, curve membership, canonical coordinates, compressed-key parity and byte order, both hash functions, fixed padding, and equality with the public 20-byte program.

2. Network identifiers ​

Network IDs are protocol values, not Bitcoin magic bytes. Values assigned by the initial v1 draft are retained; testnet4 is appended.

IDQuietProof nameBitcoin Core chain
00mainnetmain
01testnet3 (testnet CLI alias)test
02signetsignet
03regtestregtest
04testnet4testnet4

Other values are unsupported. Implementations MUST compare the node-reported chain value with the statement network before accepting chain evidence.

3. Circuit input layout and identity ​

The Longfellow public vector begins with its mandatory constant-one slot, followed by 20 byte-shaped inputs for each distinct program. Programs are sorted lexicographically and unique. The circuit family contains exactly one or two ownership relations; no inactive padding relation exists.

The circuit digest is SHA-256 over the exact Longfellow circuit serialization followed by three little-endian u64 values: circuit version, proof rate, and query count. v1 fixes these to circuit version 1, rate 4, and 128 queries.

4. Canonical statement encoding ​

The statement starts with ASCII QPS1, then:

FieldEncoding and rule
statement versionu8, exactly 01
networku8, section 2
verifier/entity ID`u16(length)
purpose`u16(length)
verifier nonceexactly 32 bytes, not all zero
BIP-322 message hashexactly 32 bytes, not all zero
not-beforeu64 Unix seconds
expiryu64 Unix seconds, strictly greater than not-before
snapshot presentu8, 00 or 01
snapshotwhen present: 32-byte displayed block hash followed by u32 height; hash is not all zero
bridge binding presentu8, 00 or 01
bridge bindingsection 5, only when present
claim countu16, 1..16
claimssection 4.1

Every string MUST be shortest-form valid UTF-8. C0 control characters (U+0000 through U+001F) and U+007F are forbidden.

The complete statement MUST be at most 4096 bytes. A statement without a snapshot MUST encode neither hash nor height; its in-memory hash and height are zero. Current-tip providers MUST reject snapshot requests, and historical providers MUST authenticate the exact hash and height.

The statement digest is the BIP-340-style tagged hash

TaggedHash("QuietProof/statement/v1", canonical_statement_bytes)

where TaggedHash(tag, m) = SHA256(SHA256(tag) || SHA256(tag) || m).

4.1 Claim encoding ​

Each claim is:

FieldEncoding and rule
txid32 bytes in Bitcoin RPC/display order, not all zero
voutu32
amountu64, 1..2,100,000,000,000,000 satoshis
programexactly 20 bytes

Claims are sorted strictly by (txid, vout); duplicates are malformed. The script is not redundantly serialized: verifiers reconstruct exactly 00 14 || program and require the chain output to equal it. More than two distinct programs is malformed. The checked aggregate claim value MUST NOT exceed 2,100,000,000,000,000 satoshis.

5. Optional bridge binding ​

A bridge binding is permitted only for purpose bridge-authorization and is:

destination_network:u8
destination_commitment:32 bytes, not all zero
asset_id:u16(length) || UTF-8, 1..256 bytes
lock_id:32 bytes, not all zero

bridge-authorization without this binding is malformed. The binding does not prove a lock; successful verification additionally requires an independent, atomic lock/custody and one-time-mint policy callback.

6. Envelope and transcript ​

The proof envelope starts with ASCII QPE1, then:

envelope_version:u8 = 01
statement_size:u32
statement_bytes
circuit_digest:32 bytes
circuit_version:u32 = 1
rate:u32 = 4
queries:u32 = 128
proof_size:u32
proof_bytes

statement_size is 1..4096. proof_size is 1..128 MiB and MUST equal the remaining envelope length. The circuit digest MUST NOT be all zero. The entire envelope is bounded before any nested allocation. Unknown envelope/statement/network/circuit versions or proof parameters are unsupported; malformed lengths or fields are malformed_statement.

The Fiat-Shamir seed is

TaggedHash(
  "PoKC/transcript/v1",
  statement_digest || circuit_digest ||
  circuit_version:u32 || rate:u32 || queries:u32)

The proof therefore binds every statement byte, the exact circuit, and proof parameters.

7. Verification and result selection ​

The verifier returns one of invalid_proof, malformed_statement, unsupported, state_inconclusive, spent_at_snapshot, stale_snapshot, bridge_rejected, valid_historical, or valid_current.

  1. Enforce the outer size cap and canonical decoding.
  2. Reject unsupported versions, networks, or proof parameters during decoding.
  3. Enforce verifier identity, exact purpose, validity window, maximum lifetime, and replay precheck before expensive proof verification.
  4. Reject an unsupported circuit identity, then verify the proof and transcript binding before querying chain state.
  5. For every outpoint, verify the selected Bitcoin network, exact amount, exact 00 14 || program script, and unspent state using one stable chain tip shared by every claim or an authenticated historical snapshot.
  6. Apply confirmation/finality policy and sum amounts with checked arithmetic.
  7. Apply bridge authorization when present.
  8. Atomically consume the nonce only after every preceding check succeeds.

A current gettxout null result cannot distinguish a nonexistent output from a spent output and is therefore state_inconclusive without additional evidence. Historical providers may return spent_at_snapshot only when they can authenticate existence and spending state at the named snapshot.

8. Required test corpus ​

Implementations MUST test every network mapping, unsupported IDs/versions, empty/all-zero nonce, zero message hash, invalid UTF-8, invalid purpose, duplicate/unsorted/zero/excess claims, more than two programs, lifetime boundaries, snapshot consistency, bridge consistency, truncated/trailing and oversized encodings, wrong proof parameters, exact BTC-to-satoshi boundaries, wrong-node networks, within-lookup and cross-claim node tip races, replay races, and circuit mutations.

tests/quietproof/testdata/pokc_vectors.json is generated independently by scripts/generate_pokc_vectors.py. It contains secp256k1/HASH160 vectors and a complete canonical statement encoding/digest vector. Regenerate it with:

python3 scripts/generate_pokc_vectors.py | diff -u tests/quietproof/testdata/pokc_vectors.json -

An independent Sage reference model in spec/pokc_reference.sage reconstructs the same shared vectors using Sage finite-field/elliptic-curve arithmetic and local, non-OpenSSL SHA-256 and RIPEMD-160 implementations. It additionally checks group-order and negation identities, invalid scalar boundaries, compressed-SEC rejection, and an example transcript seed. Run its pinned reproducibility and differential gate with:

sh
cmake -S . -B build -DQUIETPROOF_ENABLE_POKC_SAGE_TEST=ON
ctest --test-dir build -R pokc-sage-reference-test --output-on-failure

pokc-sage-test loads the checked Sage fixture and evaluates every valid vector through the C++ native helpers and the complete composed circuit; it also consumes the invalid scalar and statement/transcript vectors.

Software licensed under the GNU General Public License v3 (GPLv3+).