Relay and receipts

What the relay receives, how the batch is committed, and how a signed receipt makes censorship provable.

seq 0seq 1seq 2seq 3seq 4hashhashhashroot
  • the order's leaf
  • proof: sibling hashes
  • recomputed on the way to the root
A batch of five. To prove the order at sequence 1 was committed, the proof is three hashes (0x6e8…69d, 0x12f…a04, 0x0bc…d98) that rebuild the root 0xe51f…df01. These values are computed by the SDK when this page is built.

Envelope#

The envelope is the only thing a client sends to the relay.

json
{
  "protocolVersion": 1,
  "scheme": 1,
  "epochId": 48392,
  "ciphertext": "0x…",
  "ciphertextHash": "0x…",
  "orderCommitment": "0x…"
}
  • ciphertextHash = keccak256(ciphertext).
  • orderCommitment = keccak256(orderDigest ‖ salt).
  • Maximum ciphertext size is 4096 bytes.
  • The relay rejects any envelope with extra fields, so plaintext metadata cannot ride along by accident.

There is no ticker, side, amount, price or wallet in the envelope.

Why the commitment is public. The settlement contract must be able to check that an order it is asked to settle was in the batch that was fixed before decryption. A leaf that held only the ciphertext hash would not do: the contract cannot verify a decryption, so an operator could commit a junk ciphertext, wait for everyone else's orders to decrypt, and then claim that a freshly written order was the plaintext of that leaf. The commitment binds the plaintext order to the leaf. It is a salted hash, so it reveals nothing about the order. After decryption the matcher rejects any ciphertext whose plaintext does not hash to its commitment.

Merkle leaf and tree#

text
leaf = keccak256( keccak256( abi.encode(uint64 epochId, uint64 sequence,
                                        bytes32 orderCommitment, bytes32 ciphertextHash) ) )

The double hash keeps leaves and inner nodes in different domains. Inner nodes hash the sorted pair keccak256(min ‖ max). An unpaired node is promoted unchanged. Leaves are ordered by sequence. Implementations: KasumiOrderLib.leaf and verifyProof, packages/sdk/src/merkle.ts.

Inclusion receipt#

On acceptance the relay returns an EIP-712 signed receipt.

text
domain = { name: "Kasumi Relay", version: "1", chainId }
InclusionReceipt(bytes32 orderCommitment, bytes32 ciphertextHash,
                 uint64 epochId, uint64 sequence, uint64 receivedAt, uint64 cutoff)

It states: the relay accepted this ciphertext for this epoch, at this position, before the cutoff. After the epoch closes the client fetches a Merkle proof and calls verifyInclusion(receipt, merkleProof, root, expectedRelay). If the receipt signature is valid and the leaf is not under the published root, the result has censored: true, and the receipt is the evidence.