Skip to main content
For the complete documentation index, see llms.txt

Ledger API

@midnight/ledger v8.0.3


Ledger TypeScript API

This document outlines the flow of transaction assembly and usage with the ledger TS API.

Proof stages​

Most transaction components will be in one of three stages: X, UnprovenX, or ProofErasedX. The UnprovenX stage is always the first one. It is possible to transition to the X stage by proving an UnprovenTransaction through the proof server. For testing, and where proofs aren't necessary, the ProofErasedX stage is used, which can be reached via eraseProof[s] from the other two stages.

Transaction structure​

A Transaction runs in two phases: a guaranteed segment, handling fee payments and fast-to-verify operations, and a series of fallible segments. Each segment may fail atomically, separately from the guaranteed segment. It therefore contains:

It also contains additional cryptographic glue that will be omitted in this document.

Zswap​

A ZswapOffer consists of:

  • A set of ZswapInputs, burning coins.
  • A set of ZswapOutputs, creating coins.
  • A set of ZswapTransients, indicating a coin that is created and burnt in the same transaction.
  • A mapping from RawTokenTypes to offer balance, positive when there are more inputs than outputs and vice versa.

ZswapInputs can be created either from a QualifiedShieldedCoinInfo and a contract address, if the coin is contract-owned, or from a QualifiedShieldedCoinInfo and a ZswapLocalState, if it is user-owned. Similarly, ZswapOutputs can be created from a ShieldedCoinInfo and a contract address for contract-owned coins, or from a ShieldedCoinInfo and a user's public key(s), if it is user-owned. A ZswapTransient is created similarly to a ZswapInput, but directly converts an existing ZswapOutput.

A QualifiedShieldedCoinInfo is a ShieldedCoinInfo with an index into the Merkle tree of coin commitments that can be used to find the relevant coin to spend, while a ShieldedCoinInfo consists of a coin's RawTokenType, value, and a nonce.

Calls​

A ContractDeploy consists of an initial contract state, and a nonce.

A ContractCall consists of a contract's address, the entry point used on this contract, a guaranteed and a fallible public oracle transcript, a communication commitment, and a proof. ContractCalls are constructed via ContractCallPrototypes, which consist of the following raw pieces of data:

  • The contract address
  • The contract's entry point
  • The contract operation expected (that is, the verifier key and transcript shape expected to be at this contract address and entry point)
  • The guaranteed transcript (as produced by the generated JS code)
  • The fallible transcript (as produced by the generated JS code)
  • The outputs of the private oracle calls (As a FAB AlignedValues)
  • The input(s) to the call, concatenated together (As a FAB AlignedValue)
  • The output(s) to the call, concatenated together (As a FAB AlignedValue)
  • The communications commitment randomness (As a hex-encoded field element string)
  • A unique identifier for the ZK circuit used (used by the proof server to index for the prover key)

NOTE: currently the JS code only generates a single transcript. We probably just want a canonical way to split this into guaranteed/fallible?

A Intent object is assembed, and ContractCallPrototypes / ContractDeploys are added to this directly. This can then be inserted into an Transaction.

State Structure​

The LedgerState is the primary entry point for Midnight's ledger state, and it consists of a ZswapChainState, as well as a mapping from ContractAddresses to ContractStates. States are immutable, and applying transactions always produce new outputs states.