Espresso API

Reference for REST APIs served by Espresso nodes and query services

Modularity

The Espresso API comprises several independent modules serving different purposes and requiring different resources. A given node may serve one, or all, or any combination of these modules, depending on its role in the system and the resources available to it. To see a list of the modules available from a particular node, navigate to the root URL of that node's API.

In brief, the available API modules are:

  • Status API: node-specific state and consensus metrics

  • Catchup API: serves recent consensus state to allow peers to catch up with the network

  • Availability API: serves data recorded by the Tiramisu DA layer, such as committed blocks

  • Node API: complements the availability API by serving eventually consistent data that is not (yet) necessarily agreed upon by all nodes

  • State API: serves consensus state derived from finalized blocks

  • Events API: streams events from HotShot

  • Submit API: allows users to submit transactions to the public mempool

Content Types

All APIs support JSON and binary formats in both request and response bodies.

  • The JSON format is a straightforward serialization of the data types used internally by consensus nodes. In the future, a formal specification will be published, and the API will conform to that specification.

  • The binary format is the bincode serialization of consensus data types, prefixed with an 8-byte version header. In the future, this will be replaced with a binary format with better cross-language support, and the data types will be defined by a published schema, rather than generated from code.

In requests and responses, the JSON format is denoted by the MIME type application/json, and the binary format by application/octet-stream. For requests with a body, the content type of the body must be set via the Content-Type header. The desired content type of the response can be controlled via the Accept header of the request. If the Accept header does not preference either format, JSON will be used by default.

Server-Hosted Documentation

All Espresso API servers provide self-hosted API documentation, which makes it easy to see exactly what APIs the server supports, and can be easier to browse than these docs. The root URL of an application, e.g. my-server.xyz, lists the supported API modules and versions. Clicking on any API module, or navigating to the root of that API, e.g. my-server.xyz/status, documents the endpoints available in that module.

Versioning and Stability

A node may serve multiple major versions of a given API at the same time. The desired version can be selected via a URL prefix. For example, my-server.xyz/v0/status/metrics and my-server.xyz/v1/status/metrics both hit the same endpoint, but in different API versions. A URL with no version segment will get a permanent redirect response to the latest supported version. In this case, /status/metrics would redirect to /v1/status/metrics.

Whenever a breaking change is made to an API, a new major version will be created, and the old version will continue to be served for some time, giving clients time to upgrade to the new version whenever it is convenient for them. Note that non-breaking changes, such as adding new endpoints, may be made in place to existing versions.

To see a list of versions of an API supported by a server, visit the root URL of that server.

Types

These types are used in requests and responses across many of the API modules.

Primitives

Integer

We use integer to represent any JSON integer, with a maximum size of at least 2^63 - 1. Of special note, byte arrays are sometimes represented as arrays of integers ([integer]). When the type [integer] is used as a byte array, each integer therein is restricted to the range [0, 255].

Hex

In the following, we use the type hex to indicate a hex-encoded binary string, with 0x prefix.

Base 64

In the following, we use the type base64 to indicate a base64-encoded binary string, using the standard base 64 alphabet with padding.

Tagged Base 64

Some types use an enhanced tagged base 64 encoding, which consists of a prefix identifying the type of the encoded object, a ~, and then a base 64 string using the URL-safe base 64 alphabet without padding. The base 64 string encodes the binary representation of a typed object, plus a checksum. Because the encoding is URL-safe, these strings can be used not only in request and response bodies, but also in URL paths. The checksum allows the server to provide useful errors if a tagged base 64 string is mistyped or corrupted. The tag makes it easy for a human to tell different types of objects apart.

For example, a transaction hash might be encoded like TX~QDuwVkmexu1fWgJbjxshXcGqXku838Pa9cTn0d-v3hZ-, while a block hash could look like BLOCK~00ISpu2jHbXD6z-BwMkwR4ijGdgUSoXLp_2jIStmqBrD.

We use the type tagged<TAG> to indicate a tagged base 64 object with the given tag, as in tagged<TX> or tagged<BLOCK>.

NamespaceTable

{
    "bytes": base64
}

ChainConfig

A chain config determines properties of consensus, such as the base fee for sequencing and the chain ID. To save space, it can be represented either as the full config object (Left variant below) or as a commitment to the chain config (Right variant). The genesis header will always contain the full config, so clients can fetch the full config from genesis and then compare its commitment against any other header.

{
    "chain_config": 
        { "Left": { "chain_id": hex, "max_block_size": integer, "base_fee": hex } }
        | { "Right": tagged<CHAIN_CONFIG> }
}
{
    "height": integer,
    "timestamp": integer,
    "l1_head": integer,
    "l1_finalized": null | {
        "number": integer,
        "timestamp": hex,
        "hash": hex
    },
    "payload_commitment": tagged<HASH>,
    "builder_commitment": tagged<BUILDER_COMMITMENT>,
    "ns_table": NamespaceTable,
    "block_merkle_tree_root": tagged<MERKLE_COMM>,
    "fee_merkle_tree_root": tagged<MERKLE_COMM>,
    "fee_info": { "account": hex, "amount": hex },
    "chain_config": ChainConfig
}

Payload

{
    "raw_payload": base64,
    "ns_table": NamespaceTable
}

VidCommon

{
    "poly_commits": tagged<FIELD>,
    "all_evals_digest": tagged<FIELD>,
    "payload_byte_len": integer,
    "num_storage_nodes": integer,
    "multiplicity": integer
}

Leaf

{
    "view_number": integer,
    "justify_qc": QC,
    "parent_commitment": string,
    "block_header": Header,
    "proposer_id": string,
}

Transaction

{
    "namespace": integer,
    "payload": base64
}

If using the Rust API, you may notice that namespace is represented by a u64. However, some internal sub-protocols represent the namespace as a u32, and thus the maximum allowable namespace ID is 4294967295 (2^32 - 1). Larger namespace IDs will be rejected on transaction submission.

MerkleProof

The low-level proof type for a Merklized data structure. The specific format of this type is not currently specified, but it can be deserialized and interpreted in Rust using the MerkleProof type.

NsIndex

The 0-based position of a namespace in a NamespaceTable. The index is a little-endian byte-encoded 4-byte integer, as in [3, 2, 1, 255] (0xff010203).

NsProof

A proof that a certain list of transactions corresponds to a certain namespace in a block.

{
    "ns_index": NsIndex,
    "ns_payload": base64, // binary encoding of the namespace data
    "ns_proof": {
        "prefix_elems": tagged<FIELD>,
        "suffix_elems": tagged<FIELD>,
        "prefix_bytes": [integer],
        "suffix_bytes": [integer]
    }
}

ns_proof is a low-level range proof in the Espresso ADVZ VID scheme. The details of this proof are out of scope of this document, but this JSON object corresponds to the LargeRangeProofType, and can be manipulated in Rust using that type.

Last updated