Skip to main content

Interactive transactions

An Epic transfer is an exchange, not a broadcast. Two wallets pass a document called a slate back and forth, each adding its own half of the cryptography, and only then does anything reach the chain.

This follows from the commitment scheme described in the MimbleWimble model. The receiver's output commits to a blinding factor only the receiver knows, and the transaction's excess signature is an aggregate both parties must contribute to. The sender cannot finish alone.

The sequence

The sender builds a partial slate and reserves its inputs locally. The slate travels to the receiver over whichever transport is in use. The receiver adds an output and a partial signature and returns it the same way. The sender completes the aggregate signature and broadcasts the finished transaction to a node.

One transfer, two rounds. Nothing reaches the chain until step 05.
  1. The sender builds a partial slate. init_send_tx selects inputs and writes the first round. tx_lock_outputs then reserves those inputs locally, before the receiver has done anything.
  2. The slate travels to the receiver. A file you move, an HTTP request, or a message queued by a relay. The transport is the part that fails in practice.
  3. The receiver completes their half. receive_tx adds an output, its range proof and a partial signature, so the slate now carries two participants.
  4. The slate comes back the same way. Over epicbox this arrives asynchronously, which is why the sender also needs a listener running.
  5. The sender finalises and posts. finalize_tx completes the aggregate signature and post_tx broadcasts it. Only now does a node see a transaction at all.

Steps 01 and 02 happen inside a single send on every transport except file. The reservation in step 01 exists from the moment you start a send, not when it completes. See what reserves outputs.

The invoice flow reverses the roles. The payee calls issue_invoice_tx to request an amount, the payer calls process_invoice_tx to fund it, and the payee calls finalize_invoice_tx. The cryptography is the same; only the initiator changes.

How transfers fail

Failures are almost always in delivery rather than on the chain:

  • The receiver was not listening. No listener means no second round and no transaction.
  • The relay did not deliver. With epicbox, a slate waits in a queue for the receiver to reconnect. That queue has limits; see delivery guarantees.
  • The sender went away between rounds. The receiver has completed their part and returned a slate that no wallet is waiting for. Over epicbox the return leg needs the sender's own subscription; see the comparison.
  • The two wallets disagree on the slate version. See slate versions.

Retrying the same send does not fix any of them, because the sender's inputs are already committed to the first attempt. Cancel, then start again. See what releases them.

What a slate contains

The slate is JSON. id is the UUID both parties use for the transfer, and it is your handle on an in-flight send, so persist it. num_participants is 2 for an ordinary transfer, and participant_data holds one entry per party with that party's public nonces and partial signature. Comparing the length of participant_data against num_participants tells you which round the slate is in. Fewer entries means it is still outbound and awaiting the receiver, equal means it has come back and can be finalised.

amount and fee are in freemen, and the sender sets the fee. tx is the transaction being built, holding the inputs, outputs and kernels. height is the chain height the sender observed, and lock_height is the kernel lock height. ttl_cutoff_height is an optional expiry height, and it does not release outputs when it passes; see what releases them. payment_proof is an optional proof request; see payment proofs. version_info carries the slate format version and the version the sender wants in return.

Field types and defaults are in the Wallet Owner API reference.

Slate versions

Two formats are in use, V2 and V3. V3 carries two fields that V2 has no place for, ttl_cutoff_height and payment_proof, and converting a V3 slate to V2 discards both. Which transports convert, and which therefore carry a payment proof, is in the comparison.

target_slate_version requests a format. Pass 2 or 3.

Next