Skip to main content

Overview

The EigenDA disperser provides an API for dispersing and retrieving blobs to and from the EigenDA network in an untrusted fashion. (Note: as part of its essential data availability guarantee, the EigenDA network already supports direct communication with the EigenDA network for blob retrieval; permissionless dispersal of blobs to the EigenDA network is planned as a future protocol upgrade).

The source of truth for the Disperser API spec is disperser.proto, adjusted to the current release. The goal of this document is to explain this spec at a higher level.

Eigen Labs hosts one disperser endpoint for each EigenDA network. These endpoints are documented in respective network pages indexed here.

The EigenDA Disperser exposes 4 endpoints:

  1. DisperseBlob()
  2. DisperseBlobAuthenticated()
  3. GetBlobStatus()
  4. RetrieveBlob()

These endpoints enable the blob lifecycle, from enqueuing blobs for dispersal to waiting for their dispersal finalization and finally to retrieving blobs from the EigenDA network. The following flowchart describes how move blobs through this lifecycle with respect to these endpoints:

The Disperser offers an asynchrounous API for dispersing blobs, where clients should poll the GetBlobStatus() endpoint with the dispersal request ID they received from calling one of the two disperse endpoints until the disperser reports the blob as successfully dispersed and finalized.

Endpoints

Here we provide a narrative-level description of the major API endpoints. Please see the repo for detailed, field-level API documentation.

DisperseBlob()

info

This endpoint will be deprecated in future releases. All production traffic should prefer the DisperseBlobAuthenticated endpoint.

The DisperseBlob() is a simple unauthenticated endpoint which allows users to send test traffic to the EigenDA testnet and mainnet networks. Requests to the DisperseBlob() endpoint are rate limited based on IP address.

info

Currently, all users can permissionlessly utilize the DisperseBlob endpoint on testnet at free-tier throughput levels. Mainnet users can request IP-whitelisting via the EigenDA Client Registration Form, but should prefer the authenticated endpoint described in the next section.

The DisperseBlob() endpoint accepts a DisperseBlobRequest and returns a DisperseBlobReply, as described below:

DisperseBlobRequest

Field NameTypeDescription
data[]byteThe data to be dispersed. The blob dispersed must conform to the Blob Serialization Requirements which ensure that the blob's KZG commitment may be representative of the original data that was sent to the disperser.
custom_quorum_numbers[]uint32The quorums to which the blob will be sent, in addition to the required quorums which are configured on the EigenDA smart contract. If required quorums are included here, an error will be returned. The disperser will ensure that the encoded blobs for each quorum are all processed within the same batch.
account_idstringThis field can be omitted when using the DisperseBlob endpoint. When using the DisperseBlobAuthenticated endpoint, account_id is a hex-encoded string of the ECSDA public key corresponding to the key used by the client to sign the BlobAuthHeader.

DisperseBlobReply

Field NameTypeDescription
resultBlobStatusThe status of the blob associated with the request_id. This field is returned in case the blob dispersal queuing fails immediately. If the blob was successfully dispersed, this field will be set to PROCESSING (1).
request_id[]byteThe request ID generated by the disperser corresponding to the dispersal. Once a request is accepted (although not processed), a unique request ID will be generated. Two different DisperseBlobRequests (determined by the hash of the DisperseBlobRequest) will have different IDs, and the same DisperseBlobRequest sent repeatedly at different times will also have different IDs. The client should use this ID to query the processing status of the request (via the GetBlobStatus() API).

DisperseBlobAuthenticated()

DisperseBlobAuthenticated() provides a flow for authenticated dispersal to EigenDA networks. Ultimately, the purpose of authentication is to allow DA nodes to identify the source of a given blob request and map this to a payment source. Thus, the DisperseBlobAuthenticated() will ultimately serve as a convenient way for a client to provide an authorization which can be passed along to the DA nodes, without making any trust assumptions on the disperser as a service provider. The interface is expected to undergo an upgrade in order to support this use case over the next several months.

Clients authenticate a request to the disperser by providing an ECDSA signature of a BlobAuthHeader which can be passed to the DA nodes. This header should contain the KZG commitment of the blob itself, which may be inconvenient for a client to calculate given that it requres the storage of a large SRS file. The DisperseBlobAuthenticated() uses an interactive flow whereby the client can first send the blob, and then receive the KZG commitment back from the disperser, verify it, and send back the authenticating signature. The current interface implements this overall flow, but using a simple random challenge mechanism in the place of the KZG commitment, for the reason that the BlobAuthHeader will only be sent to the DA nodes once payments are released.

warning

In order to minimize security risks, we recommend that clients utilize a keypair for authentication not associated with any Ethereum funds.

info

Clients looking to send authenticated traffic to EigenDA mainnet or testnet should reach out via the EigenDA Client Registration Form so we can get in touch.

The following is a detailed description of the behavior of the DisperseBlobAuthenticated() endpoint. To quickly get started using this endpoint, you can use the golang client described in the quick start guide.

service Disperser {
rpc DisperseBlobAuthenticated(stream AuthenticatedRequest) returns (stream AuthenticatedReply);
...
}

message AuthenticatedRequest {
oneof payload {
DisperseBlobRequest disperse_request = 1;
AuthenticationData authentication_data = 2;
}
}

message AuthenticatedReply {
oneof payload {
BlobAuthHeader blob_auth_header = 1;
DisperseBlobReply disperse_reply = 2;
}
}

  1. The client opens a connection to DisperseBlobAuthenticated() endpoint, sending a DisperseBlobRequest message with the Ethereum address they wish to authenticate with as account_id:
message DisperseBlobRequest {
bytes data = 1;
repeated uint32 custom_quorum_numbers = 2;

// The account ID of the client. This should be a hex-encoded string of the ECSDA public key
// corresponding to the key used by the client to sign the BlobAuthHeader.
string account_id = 3;
}
  1. The server validates this request, sending back a challenge string in the form of a BlobAuthHeader:
message BlobAuthHeader {
uint32 challenge_parameter = 1;
}
  1. The client ECDSA signs the challenge parameter bytes with the private key associated with the Ethereum address they sent in step 1, returning this to the server in an AuthenticationData message:
message AuthenticationData {
bytes authentication_data = 1;
}
  1. The server validates the returned challenge. If the signature of the challenge verifies against the public key of the Ethereum address that was specified in step 1, then the request is granted, and the blob is dispersed. The server returns a DisperseBlobReply conforming to the following schema:
message DisperseBlobReply {
BlobStatus result = 1;
bytes request_id = 2;
}

GetBlobStatus()

This endpoint returns the dispersal status and metadata associated with a given blob request ID, and is meant to be polled until the blob is reported as finalized and a DA certificate is returned.

BlobStatusRequest

Field NameTypeDescription
request_id[]byteThe ID of the blob that is being queried for its status.

BlobStatusReply

Field NameTypeDescription
statusBlobStatusThe dispersal status of the blob
infoBlobInfoThe blob info needed for clients to confirm the blob against the EigenDA contracts

Since the BlobInfo type has many nested sub-structs, it's easier to describe its schema by annotating an example:

{
"status": "CONFIRMED", // means that the blob's batch metadata has been registered in the EigenDA manager contract, but the block in which it was registered has not yet finalized.
"info": {
"blobHeader": {
"commitment": { // KZG commitment associated with the data that was dispersed
"x": "EBXIwkZ7nXChaRx2Nz+SZyU/rX3WvZnLGeKpCW32OWs=", // BN254 X point
"y": "LoTp8Bqz7pyhptnRBT5o01GAbPGXB52Ll+X+Pw+ibeg=" // BN254 Y point
},
"dataLength": 1,
"blobQuorumParams": [
{
"adversaryThresholdPercentage": 33,
"confirmationThresholdPercentage": 55,
"chunkLength": 1
},
{
"quorumNumber": 1,
"adversaryThresholdPercentage": 33,
"confirmationThresholdPercentage": 55,
"chunkLength": 1
}
]
},
"blobVerificationProof": {
"batchId": 15219, // batchId and batchHeaderHash are the minimum fields necessary for later retrieving a blob.
"blobIndex": 687,
"batchMetadata": {
"batchHeader": {
"batchRoot": "+yFLC9HFHJxkBixjGdFGv0psPC6R0DNynhowYgUvjtE=",
"quorumNumbers": "AAE=",
"quorumSignedPercentages": "VU4=",
"referenceBlockNumber": 1564355
},
"signatoryRecordHash": "HG1kkSIGjTOX2kFexdGnuAj7zDJaat0XQQavHjjXdPs=",
"fee": "AA==",
"confirmationBlockNumber": 1564476, // ethereum block number when the blob's dispersal metadata was registered
"batchHeaderHash": "d1KhHvr0lhNCYiizYS5+v/2QWvSTsm7MeACChYDRli0=" // batchHeaderHash and batchId are the minimum fields necessary for later retrieving a blob.
},
"inclusionProof": "3DDZAQV1jdb4Eb3pLAAVqAq69EMrmGMfwfcW9jQwShN8O4oqv7041DVjM09LARNO4VX1WUoVrSdXQ5ZXpaKKL7iREgnhNrHydYXfmJuGiS7dtxQubTDQ2O5bYTckzt/LZakvNf5hz87vEQdvHcYh2wpBugaX6/kgY/8OGiHLwocIXXwC5upaU92WSxFkHmd31xq7nAwDM5N8s7R9ktWBTbBGVFTtmTcctapohz551bskMoV79w28ie4Tc6NcdS5S9z1hR6tW9IGoHqeifynPjdvRaq51T/jnJWSC6gixbO6DOcw2qIU0+jhZsu6/ucHIwzxBQtvmp+7dLBthC7dZYllIOsc2nyTmUfp2mKXjP5vPEhbX+FLIMwagi3lGOI9zUdG/RYIpKxEIVoO5ffStDMotX4ZCgGZyQiTYR0maags/yc/ID27M8YVyu54nAAAyG89TpmqvVofJ1ove863ufA==", // this field proves that the blob was included within the batch specified by the batchHeaderHash.
"quorumIndexes": "AAE="
}
}
}

RetrieveBlob()

The RetrieveBlob() endpoint enables clients to retrieve an individual blob using a (batch_header_hash, blob_index) pair, originally derived from inside a BlobInfo object returned by GetBlobStatus(). Retrieving blobs via the Retrieve endpoint on the disperser is more efficient than directly retrieving from the DA Nodes (see detail about this approach in retriever.proto. The blob should have been initially dispersed via this Disperser service for this API to work.