Networking
Wire Format
AvailableData
name | type | description |
---|---|---|
availableDataRows | AvailableDataRow[] | List of rows. |
AvailableDataRow
name | type | description |
---|---|---|
shares | Share[] | Shares in a row. |
ConsensusProposal
Defined as ConsensusProposal
:
message ConsensusProposal {
SignedMsgType type = 1;
int32 round = 2;
int32 pol_round = 3;
// 32-byte hash
// Proposed block header
Header header = 4;
AvailableDataHeader da_header = 5;
// 64-byte signature
bytes proposer_signature = 6;
}
When receiving a new block proposal proposal
from the network, the following steps are performed in order. Must indicates that peers must be blacklisted (to prevent DoS attacks) and should indicates that the network blob can simply be ignored.
proposal.type
must be aSignedMsgType
.proposal.round
is processed identically to Tendermint.proposal.pol_round
is processed identically to Tendermint.proposal.header
must be well-formed.proposal.header.version.block
must beVERSION_BLOCK
.proposal.header.version.app
must be a supported app version.proposal.header.height
should be previous known height + 1.proposal.header.chain_id
must beCHAIN_ID
.proposal.header.time
is processed identically to Tendermint.proposal.header.last_header_hash
must be previous block's header hash.proposal.header.last_commit_hash
must be the previous block's commit hash.proposal.header.consensus_hash
must be the hash of consensus parameters.proposal.header.state_commitment
must be the state root after applying the previous block's transactions.proposal.header.available_data_original_shares_used
must be at mostAVAILABLE_DATA_ORIGINAL_SQUARE_MAX ** 2
.proposal.header.available_data_root
must be the root ofproposal.da_header
.proposal.header.proposer_address
must be the correct leader.proposal.da_header
must be well-formed.- The number of elements in
proposal.da_header.row_roots
andproposal.da_header.row_roots
must be equal. - The number of elements in
proposal.da_header.row_roots
must be the same as computed here. proposal.proposer_signature
must be a valid digital signature over the header hash ofproposal.header
that recovers toproposal.header.proposer_address
.- For full nodes,
proposal.da_header
must be the result of computing the roots of the shares (received separately). - For light nodes,
proposal.da_header
should be sampled from for availability.
MsgWirePayForData
Defined as MsgWirePayForData
:
message MsgWirePayForData {
TransactionFee fee = 1;
uint64 nonce = 2;
// 8-byte namespace ID
bytes message_namespace_id = 3;
uint64 message_size = 4;
bytes message = 5;
repeated MessageCommitmentAndSignature message_commitment_and_signature = 6;
}
Accepting a MsgWirePayForData
into the mempool requires different logic than other transactions in Celestia, since it leverages the paradigm of block proposers being able to malleate transaction data. Unlike SignedTransactionDataMsgPayForData (the canonical data type that is included in blocks and committed to with a data root in the block header), each MsgWirePayForData
(the over-the-wire representation of the same) has potentially multiple signatures.
Transaction senders who want to pay for a blob will create a SignedTransactionDataMsgPayForData object, stx
, filling in the stx.blobShareCommitment
field based on the blob share commitmentrules, then signing it to get a transaction tx
.
Receiving a MsgWirePayForData
object from the network follows the reverse process: verify using the blob share commitmentrules that the signature is valid.
Invalid Erasure Coding
If a malicious block producer incorrectly computes the 2D Reed-Solomon code for a block's data, a fraud proof for this can be presented. We assume that the light clients have the AvailableDataHeader and the Header for each block. Hence, given a ShareProof, they can verify if the rowRoot
or colRoot
specified by isCol
and position
commits to the corresponding Share. Similarly, given the height
of a block, they can access all elements within the AvailableDataHeader and the Header of the block.
ShareProof
name | type | description |
---|---|---|
share | Share | The share. |
proof | NamespaceMerkleTreeInclusionProof | The Merkle proof of the share in the offending row or column root. |
isCol | bool | A Boolean indicating if the proof is from a row root or column root; false if it is a row root. |
position | uint64 | The index of the share in the offending row or column. |
BadEncodingFraudProof
Defined as BadEncodingFraudProof
:
// https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#badencodingfraudproof
message BadEncodingFraudProof {
// height of the block with the offending row or column
int64 height = 1;
// the available shares in the offending row or column and their Merkle proofs
// array of ShareProofs
repeated ShareProof shareProofs = 2;
// a Boolean indicating if it is an offending row or column; false if it is a row
bool isCol = 3;
// the index of the offending row or column in the square
uint64 position = 4;
}
name | type | description |
---|---|---|
height | Height | Height of the block with the offending row or column. |
shareProofs | ShareProof[] | The available shares in the offending row or column. |
isCol | bool | A Boolean indicating if it is an offending row or column; false if it is a row. |
position | uint64 | The index of the offending row or column in the square. |
Invalid State Update
If a malicious block producer incorrectly computes the state, a fraud proof for this can be presented. We assume that the light clients have the AvailableDataHeader and the Header for each block. Hence, given a ShareProof, they can verify if the rowRoot
or colRoot
specified by isCol
and position
commits to the corresponding Share. Similarly, given the height
of a block, they can access all elements within the AvailableDataHeader and the Header of the block.
StateFraudProof
Defined as StateFraudProof
:
// https://github.com/celestiaorg/celestia-specs/blob/master/specs/networking.md#statefraudproof
message StateFraudProof {
// height of the block with the intermediate state roots
// Subtracting one from height gives the height of the block with the transactions.
int64 height = 1;
// shares containing the transactions and their Merkle proofs
// isCol within the ShareProof must be false.
// array of ShareProofs
repeated ShareProof transactionShareProofs = 2;
// shares containing the intermediate state roots and their Merkle proofs
// isCol within the ShareProof must be false.
// array of ShareProofs
repeated ShareProof isrShareProofs = 3;
// index for connecting the WrappedIntermediateStateRoot and WrappedTransaction after shares are parsed
uint64 index = 4;
// state elements that were changed by the transactions
// array of StateElements
repeated StateElement intermediateStateElements = 5;
// sparse Merkle tree inclusion proofs for the state elements
// array of SparseMerkleTreeInclusionProofs
repeated SparseMerkleTreeInclusionProof stateInclusionProofs = 6;
}
name | type | description |
---|---|---|
height | Height | Height of the block with the intermediate state roots. Subtracting one from height gives the height of the block with the transactions. |
transactionShareProofs | ShareProof[] | isCol of type bool must be false . |
isrShareProofs | ShareProof[] | isCol of type bool must be false . |
index | uint64 | Index for connecting the WrappedIntermediateStateRoot and WrappedTransaction after shares are parsed. |