|
|
@ -0,0 +1,303 @@ |
|
|
|
# Tendermint Blockchain |
|
|
|
|
|
|
|
Here we describe the data structures in the Tendermint blockchain and the rules for validating them. |
|
|
|
|
|
|
|
# Data Structures |
|
|
|
|
|
|
|
The Tendermint blockchains consists of a short list of basic data types: |
|
|
|
blocks, headers, votes, block ids, and signatures. |
|
|
|
|
|
|
|
## Block |
|
|
|
|
|
|
|
A block consists of a header, a list of transactions, and a list of votes (the commit). |
|
|
|
|
|
|
|
``` |
|
|
|
type Block struct { |
|
|
|
Header Header |
|
|
|
Txs [][]byte |
|
|
|
LastCommit []Vote |
|
|
|
Evidence []Evidence |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
## Header |
|
|
|
|
|
|
|
A block header contains metadata about the block and about the consensus, as well as commitments to |
|
|
|
the data in the current block, the previous block, and the results returned by the application: |
|
|
|
|
|
|
|
``` |
|
|
|
type Header struct { |
|
|
|
// block metadata |
|
|
|
Version string // Version string |
|
|
|
ChainID string // ID of the chain |
|
|
|
Height int64 // current block height |
|
|
|
Time int64 // UNIX time, in millisconds |
|
|
|
|
|
|
|
// current block |
|
|
|
NumTxs int64 // Number of txs in this block |
|
|
|
TxHash []byte // SimpleMerkle of the block.Txs |
|
|
|
LastCommitHash []byte // SimpleMerkle of the block.LastCommit |
|
|
|
|
|
|
|
// previous block |
|
|
|
TotalTxs int64 // prevBlock.TotalTxs + block.NumTxs |
|
|
|
LastBlockID BlockID // BlockID of prevBlock |
|
|
|
|
|
|
|
// application |
|
|
|
ResultsHash []byte // SimpleMerkle of []abci.Result from prevBlock |
|
|
|
AppHash []byte // Arbitrary state digest |
|
|
|
ValidatorsHash []byte // SimpleMerkle of the ValidatorSet |
|
|
|
ConsensusParamsHash []byte // SimpleMerkle of the ConsensusParams |
|
|
|
|
|
|
|
// consensus |
|
|
|
Proposer []byte // Address of the block proposer |
|
|
|
EvidenceHash []byte // SimpleMerkle of []Evidence |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
## BlockID |
|
|
|
|
|
|
|
The `BlockID` contains two distinct Merkle roots of the block. |
|
|
|
The first, used as the block's main hash, is the Merkle root |
|
|
|
of all the fields in the header. The second, used for secure gossipping of |
|
|
|
the block during consensus, is the Merkle root of the complete serialized block |
|
|
|
cut into pieces. The `BlockID` includes these two hashes, as well as the number of |
|
|
|
pieces. |
|
|
|
|
|
|
|
``` |
|
|
|
type BlockID struct { |
|
|
|
HeaderHash []byte |
|
|
|
NumParts int32 |
|
|
|
PartsHash []byte |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
## Vote |
|
|
|
|
|
|
|
A vote is a signed message from a validator for a particular block. |
|
|
|
The vote includes information about the validator signing it. |
|
|
|
There are two types of votes, but they have identical structure: |
|
|
|
|
|
|
|
``` |
|
|
|
type Vote struct { |
|
|
|
Timestamp int64 |
|
|
|
Address []byte |
|
|
|
Index int |
|
|
|
Height int64 |
|
|
|
Round int |
|
|
|
Type int8 |
|
|
|
BlockID BlockID |
|
|
|
Signature Signature |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
## Signature |
|
|
|
|
|
|
|
Tendermint allows for multiple signature schemes to be used by prepending a single type-byte |
|
|
|
to the signature bytes. Different signatures may also come in different lengths, but this length |
|
|
|
is not made explicit. Currently, Tendermint supports Ed25519 and Secp256k1 |
|
|
|
|
|
|
|
``` |
|
|
|
// Implements Signature |
|
|
|
type Ed25519Signature struct { |
|
|
|
Type int8 = 0x1 |
|
|
|
Signature [64]byte |
|
|
|
} |
|
|
|
|
|
|
|
// Implements Signature |
|
|
|
type Secp256k1Signature struct { |
|
|
|
Type int8 = 0x2 |
|
|
|
Signature []byte |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
# Validation |
|
|
|
|
|
|
|
Here we go through every element of a block and describe the rules for validation. |
|
|
|
Blocks which do not satisfy these rules are considered invalid. |
|
|
|
|
|
|
|
In the following, `block` refers to the block under consideration, |
|
|
|
`prevBlock` refers to the `block` at the previous height, |
|
|
|
`state` refers to an object that keeps track of the validator set |
|
|
|
and the consensus parameters as they are updated by the application, |
|
|
|
and `app` refers to the responses returned by the application during the |
|
|
|
execution of the `prevBlock`. |
|
|
|
|
|
|
|
We abuse notation by using something that looks like Go, supplemented with English. |
|
|
|
|
|
|
|
## Header |
|
|
|
|
|
|
|
A Header is valid if its corresponding fields are valid. |
|
|
|
|
|
|
|
### Version |
|
|
|
|
|
|
|
Arbitrary string. |
|
|
|
|
|
|
|
### ChainID |
|
|
|
|
|
|
|
Arbitrary constant string. |
|
|
|
|
|
|
|
### Height |
|
|
|
|
|
|
|
``` |
|
|
|
block.Header.Height > 0 |
|
|
|
block.Header.Height == prevBlock.Header.Height + 1 |
|
|
|
``` |
|
|
|
|
|
|
|
### Time |
|
|
|
|
|
|
|
The median of the timestamps of the valid votes in the block.LastCommit. |
|
|
|
Corresponds to the number of milliseconds since January 1, 1970. |
|
|
|
Increments with every new block. |
|
|
|
|
|
|
|
### NumTxs |
|
|
|
|
|
|
|
``` |
|
|
|
block.Header.NumTxs == len(block.Txs) |
|
|
|
``` |
|
|
|
|
|
|
|
Number of transactions included in the block. |
|
|
|
|
|
|
|
### TxHash |
|
|
|
|
|
|
|
``` |
|
|
|
block.Header.TxHash == SimpleMerkleRoot(block.Txs) |
|
|
|
``` |
|
|
|
|
|
|
|
Simple Merkle root of the transactions in the block. |
|
|
|
|
|
|
|
### LastCommitHash |
|
|
|
|
|
|
|
``` |
|
|
|
block.Header.LastCommitHash == SimpleMerkleRoot(block.LastCommit) |
|
|
|
``` |
|
|
|
|
|
|
|
Simple Merkle root of the votes included in the block. |
|
|
|
These are the votes that committed the previous block. |
|
|
|
|
|
|
|
### TotalTxs |
|
|
|
|
|
|
|
``` |
|
|
|
block.Header.TotalTxs == prevBlock.Header.TotalTxs + block.header.NumTxs |
|
|
|
``` |
|
|
|
|
|
|
|
The cumulative sum of all transactions included in this blockchain. |
|
|
|
|
|
|
|
### LastBlockID |
|
|
|
|
|
|
|
``` |
|
|
|
parts := MakeBlockParts(block, state.ConsensusParams.BlockGossip.BlockPartSize) |
|
|
|
block.HeaderLastBlockID == BlockID{ |
|
|
|
SimpleMerkleRoot(block.Header), |
|
|
|
len(parts), |
|
|
|
SimpleMerkleRoot(parts), |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
Previous block's BlockID. Note it depends on the ConsensusParams, |
|
|
|
which are held in the `state` and may be updated by the application. |
|
|
|
|
|
|
|
### ResultsHash |
|
|
|
|
|
|
|
``` |
|
|
|
block.ResultsHash == SimpleMerkleRoot(app.Results) |
|
|
|
``` |
|
|
|
|
|
|
|
Simple Merkle root of the results of the transactions in the previous block. |
|
|
|
|
|
|
|
### AppHash |
|
|
|
|
|
|
|
``` |
|
|
|
block.AppHash == app.AppHash |
|
|
|
``` |
|
|
|
|
|
|
|
Arbitrary byte array returned by the application after executing and commiting the previous block. |
|
|
|
|
|
|
|
### ValidatorsHash |
|
|
|
|
|
|
|
``` |
|
|
|
block.ValidatorsHash == SimpleMerkleRoot(state.Validators) |
|
|
|
``` |
|
|
|
|
|
|
|
Simple Merkle root of the current validator set that is committing the block. |
|
|
|
This can be used to validate the `LastCommit` included in the next block. |
|
|
|
May be updated by the applicatoin. |
|
|
|
|
|
|
|
### ConsensusParamsHash |
|
|
|
|
|
|
|
``` |
|
|
|
block.ValidatorsHash == SimpleMerkleRoot(state.ConsensusParams) |
|
|
|
``` |
|
|
|
|
|
|
|
Simple Merkle root of the consensus parameters. |
|
|
|
May be updated by the application. |
|
|
|
|
|
|
|
### Proposer |
|
|
|
|
|
|
|
Original proposer of the block. |
|
|
|
|
|
|
|
TODO |
|
|
|
|
|
|
|
### EvidenceHash |
|
|
|
|
|
|
|
``` |
|
|
|
block.EvidenceHash == SimpleMerkleRoot(block.Evidence) |
|
|
|
``` |
|
|
|
|
|
|
|
Simple Merkle root of the evidence of Byzantine behaviour included in this block. |
|
|
|
|
|
|
|
## Txs |
|
|
|
|
|
|
|
Arbitrary length array of arbitrary length byte-arrays. |
|
|
|
|
|
|
|
## LastCommit |
|
|
|
|
|
|
|
``` |
|
|
|
if block.Header.Height == 1 { |
|
|
|
len(b.LastCommit) == 0 |
|
|
|
} |
|
|
|
``` |
|
|
|
|
|
|
|
The first height is an exception - it requires the LastCommit to be empty. |
|
|
|
Otherwise, we require: |
|
|
|
|
|
|
|
``` |
|
|
|
len(block.LastCommit) == len(state.LastValidators) |
|
|
|
talliedVotingPower := 0 |
|
|
|
for i, vote := range block.LastCommit{ |
|
|
|
if vote == nil{ |
|
|
|
continue |
|
|
|
} |
|
|
|
vote.Type == 2 |
|
|
|
vote.Height == block.LastCommit.Height() |
|
|
|
vote.Round == block.LastCommit.Round() |
|
|
|
vote.BlockID == block.LastBlockID |
|
|
|
|
|
|
|
pubkey, votingPower := state.LastValidators.Get(i) |
|
|
|
VerifyVoteSignature(block.ChainID, vote, pubkey) |
|
|
|
|
|
|
|
talliedVotingPower += votingPower |
|
|
|
} |
|
|
|
|
|
|
|
talliedVotingPower > state.LastValidators.TotalVotingPower() |
|
|
|
``` |
|
|
|
|
|
|
|
Includes one (possibly nil) vote for every current validator. |
|
|
|
Non-nil votes must be Precommits. |
|
|
|
All votes must be for the same height and round. |
|
|
|
All votes must be for the previous block. |
|
|
|
All votes must have a valid signature from the corresponding validator. |
|
|
|
The sum total of the voting power of the validators that voted |
|
|
|
must be greater than the total voting power of the complete validator set. |
|
|
|
|
|
|
|
## Evidence |
|
|
|
|
|
|
|
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
|
|
``` |
|
|
|
|
|
|
|
Every piece of evidence contains two conflicting votes from a single validator that |
|
|
|
was active at the height indicated in the votes. |
|
|
|
The votes must not be too old. |
|
|
|
|