Skip to content
Open app

Payment predicate

AI-generated

The guest verifies one recipient output and derives its transaction ID and output key.

Recipients are 95 ASCII bytes for a Monero mainnet standard address or subaddress, with valid Base58 encoding, checksum, prefix, and canonical nonidentity prime-order spend/view keys.

Payments require transaction version 2, unlock_time = 0, RingCT type 6 (BulletproofPlus), tagged-key outputs, and 16 ring members per input. Parsing consumes each component exactly and rejects ambiguous/duplicate extra-key encodings and invalid transaction public keys.

txid = Keccak256(
Keccak256(payment_prefix) || Keccak256(rct_base) || prunable_hash
)

Hash raw 32-byte values, without byte reversal; Keccak-256 is not SHA3-256. The output index must exist. key_selector = 0 selects the primary key; 1 selects that output’s additional key. The selected key and shared point must be nonidentity prime-order points.

Let R be the selected transaction key, A the recipient view key, B its spend key, D the shared point, and G the Edwards base point. Scalars c and r must be canonical.

message = ASCII("ETH-XMR-SWAP/1:") || lowercase_hex(swap_id)
m = Keccak256(raw_txid || message)
proof_base = G for standard addresses; B for subaddresses
X = cR + r·proof_base
Y = cD + rA

The swap hex is 64 lowercase digits without 0x. The challenge equals hash_to_scalar of the ordered concatenation:

m || compress(D) || compress(X) || compress(Y)
|| Keccak256("TXPROOF_V2") || compress(R) || compress(A) || spend_field

spend_field is 32 zero bytes for standard addresses or compress(B) for subaddresses. The witness carries the decoded binary tuple, not the Base58 envelope.

Compute derivation = compress(8D) and append the canonical varint output index; hashing to a scalar gives s. Require:

  • Output key B + sG, transparent amount zero, and the correct first-byte view_tag domain hash.
  • Eight amount bytes unmasked with the amount domain hash of s, decoded as little-endian uint64 piconero, at least the winning quote.
  • A matching RingCT commitment using the commitment_mask domain hash and commitment base H.

One output must cover the entire quote; smaller outputs are not summed. Change outputs are permitted. See security for the work-based model and checks outside this predicate.