Class: X402::BSV::ProofGateway
- Defined in:
- lib/x402/bsv/proof_gateway.rb
Overview
Merkleworks x402 compatible gateway.
Challenge: X402-Challenge (merkleworks JSON with nonce UTXO + request binding) Proof: X402-Proof (echoed challenge hash + rawtx + txid)
Supports two modes:
- Profile A: challenge includes nonce UTXO metadata only
- Profile B: nonce_provider returns a pre-signed partial_tx template
Constant Summary collapse
- ACCEPTABLE_MEMPOOL_STATUSES =
ARC status values that confirm ARC knows about the tx and hasn't rejected it. ARC progresses a broadcast tx through roughly:
RECEIVED → STORED → ANNOUNCED_TO_NETWORK → REQUESTED_BY_NETWORK → SENT_TO_NETWORK → ACCEPTED_BY_NETWORK → SEEN_ON_NETWORK → MINED
Any of these mean "ARC has the tx and is processing it"; the client has demonstrably broadcast. The bad states we must reject are UNKNOWN (never seen), REJECTED, DOUBLE_SPEND_ATTEMPTED — anything in this whitelist is acceptable.
Bitcoin's single-spend guarantee at the network layer is the actual replay gate; this check just confirms the client fulfilled their broadcast obligation. Matches the merkleworks reference implementation's
visiblecheck. %w[ RECEIVED STORED ANNOUNCED_TO_NETWORK REQUESTED_BY_NETWORK SENT_TO_NETWORK ACCEPTED_BY_NETWORK SEEN_ON_NETWORK MINED ].freeze
- MEMPOOL_RETRY_DELAYS_SECONDS =
Mempool polling retry schedule. ARC typically returns an intermediate status (RECEIVED, STORED, SENT_TO_NETWORK) for a brief window after broadcast before progressing to SEEN_ON_NETWORK. A short retry loop absorbs that race without adding stateful polling infrastructure.
[0.25, 0.5, 1.0].freeze
Constants inherited from Gateway
Instance Attribute Summary
Attributes inherited from Gateway
Instance Method Summary collapse
-
#challenge_headers(rack_request, route) ⇒ Hash
Build a 402 challenge with merkleworks +X402-Challenge+ header.
-
#initialize(nonce_provider:, arc_client:, payee_locking_script_hex: nil, wallet: nil, challenge_secret: nil, challenge_store: nil) ⇒ ProofGateway
constructor
A new instance of ProofGateway.
-
#proof_header_names ⇒ Array<String>
Proof header names this gateway responds to.
-
#settle!(_header_name, proof_payload, rack_request, route) ⇒ SettlementResult
Verify a merkleworks x402 proof against the challenge and check mempool.
Methods inherited from Gateway
#build_template, #request_binding_hash
Constructor Details
#initialize(nonce_provider:, arc_client:, payee_locking_script_hex: nil, wallet: nil, challenge_secret: nil, challenge_store: nil) ⇒ ProofGateway
Returns a new instance of ProofGateway.
64 65 66 67 68 69 70 71 72 |
# File 'lib/x402/bsv/proof_gateway.rb', line 64 def initialize(nonce_provider:, arc_client:, payee_locking_script_hex: nil, wallet: nil, challenge_secret: nil, challenge_store: nil) super(payee_locking_script_hex: payee_locking_script_hex, wallet: wallet, challenge_secret: challenge_secret) @nonce_provider = nonce_provider @arc_client = arc_client @challenge_store = challenge_store || ChallengeStore::Memory.new end |
Instance Method Details
#challenge_headers(rack_request, route) ⇒ Hash
Build a 402 challenge with merkleworks +X402-Challenge+ header.
80 81 82 83 84 85 86 87 88 89 90 91 92 |
# File 'lib/x402/bsv/proof_gateway.rb', line 80 def challenge_headers(rack_request, route) if route.amount_sats.respond_to?(:call) raise ConfigurationError, "proof_gateway does not support callable amount_sats (fiat pricing) — use a static value" end challenge = build_merkleworks_challenge(rack_request, route) begin @challenge_store.store!(challenge.sha256_hex, challenge) rescue ChallengeStore::StoreFullError raise VerificationError.new("server at capacity — try again later", status: 503) end { "X402-Challenge" => challenge.to_header } end |
#proof_header_names ⇒ Array<String>
Returns proof header names this gateway responds to.
95 96 97 |
# File 'lib/x402/bsv/proof_gateway.rb', line 95 def proof_header_names ["X402-Proof"] end |
#settle!(_header_name, proof_payload, rack_request, route) ⇒ SettlementResult
Verify a merkleworks x402 proof against the challenge and check mempool.
107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/x402/bsv/proof_gateway.rb', line 107 def settle!(_header_name, proof_payload, rack_request, route) required_sats = route.resolve_amount_sats proof = Proof.from_header(proof_payload) challenge = lookup_challenge!(proof) run_protocol_checks!(challenge, proof, rack_request) decode_and_verify_transaction!(proof, challenge, required_sats) check_mempool!(proof.txid) consume_challenge!(proof) SettlementResult.new(txid: proof.txid, network: X402.configuration.network) end |