Browse Source

Core: move validation & data structures together (#176)

Co-authored-by: Callum Waters <cmwaters19@gmail.com>
pull/7804/head
Marko 4 years ago
committed by GitHub
parent
commit
9fce8480b0
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 257 additions and 443 deletions
  1. +257
    -443
      spec/core/data_structures.md

+ 257
- 443
spec/core/data_structures.md View File

@ -2,134 +2,173 @@
Here we describe the data structures in the Tendermint blockchain and the rules for validating them.
The Tendermint blockchains consists of a short list of basic data types:
- `Block`
- `Header`
- `Version`
- `BlockID`
- `Time`
- `Data` (for transactions)
- `Commit` and `Vote`
- `EvidenceData` and `Evidence`
The Tendermint blockchains consists of a short list of data types:
- [`Block`](#block)
- [`Header`](#header)
- [`Version`](#version)
- [`BlockID`](#blockid)
- [`PartSetHeader`](#partsetheader)
- [`Time`](#time)
- [`Data` (for transactions)](#data)
- [`Commit`](#commit)
- [`CommitSig`](#commitsig)
- [`BlockIDFlag`](#blockidflag)
- [`Vote`](#vote)
- [`CanonicalVote`](#canonicalvote)
- [`SignedMsgType`](#signedmsgtype)
- [`EvidenceData`](#evidence_data)
- [`Evidence`](#evidence)
- [`DuplicateVoteEvidence`](#duplicatevoteevidence)
- [`LightClientAttackEvidence`](#lightclientattackevidence)
- [`LightBlock](#lightblock)
- [`SignedHeader`](#signedheader)
- [`Validator`](#validator)
- [`ValidatorSet`](#validatorset)
- [`Address`](#address)
## Block
A block consists of a header, transactions, votes (the commit),
and a list of evidence of malfeasance (ie. signing conflicting votes).
| Name | Type | Description | Validation |
|------------|--------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------|
| Header | [Header](#header) | Header corresponding to the block. This field contains information used throughout consensus and other areas of the protocol. To find out what it contains, visit [header] (#header) | Must adhere to the validation rules of [header](#header) |
| Data | [Data](#data) | Data contains a list of transactions. The contents of the transaction is unknown to Tendermint. | This field can be empty or populated, but no validation is performed. Applications can perform validation on individual transactions prior to block creation using [checkTx](../abci/abci.md#checktx).
| Evidence | [EvidenceData](#evidence_data) | Evidence contains a list of infractions committed by validators. | Can be empty, but when populated the validations rules from [evidenceData](#evidence_data) apply |
| LastCommit | [Commit](#commit) | `LastCommit` includes one vote for every validator. All votes must either be for the previous block, nil or absent. If a vote is for the previous block it must have a valid signature from the corresponding validator. The sum of the voting power of the validators that voted must be greater than 2/3 of the total voting power of the complete validator set. The number of votes in a commit is limited to 10000 (see `types.MaxVotesCount`). | Must be empty for the initial height and must adhere to the validation rules of [commit](#commit). |
## Execution
Once a block is validated, it can be executed against the state.
The state follows this recursive equation:
```go
state(initialHeight) = InitialState
state(h+1) <- Execute(state(h), ABCIApp, block(h))
```
where `InitialState` includes the initial consensus parameters and validator set,
and `ABCIApp` is an ABCI application that can return results and changes to the validator
set (TODO). Execute is defined as:
```go
type Block struct {
Header Header
Txs Data
Evidence EvidenceData
LastCommit Commit
func Execute(s State, app ABCIApp, block Block) State {
// Fuction ApplyBlock executes block of transactions against the app and returns the new root hash of the app state,
// modifications to the validator set and the changes of the consensus parameters.
AppHash, ValidatorChanges, ConsensusParamChanges := app.ApplyBlock(block)
nextConsensusParams := UpdateConsensusParams(state.ConsensusParams, ConsensusParamChanges)
return State{
ChainID: state.ChainID,
InitialHeight: state.InitialHeight,
LastResults: abciResponses.DeliverTxResults,
AppHash: AppHash,
InitialHeight: state.InitialHeight,
LastValidators: state.Validators,
Validators: state.NextValidators,
NextValidators: UpdateValidators(state.NextValidators, ValidatorChanges),
ConsensusParams: nextConsensusParams,
Version: {
Consensus: {
AppVersion: nextConsensusParams.Version.AppVersion,
},
},
}
}
```
Note the `LastCommit` is the set of signatures of validators that committed the last block.
Validating a new block is first done prior to the `prevote`, `precommit` & `finalizeCommit` stages.
The steps to validate a new block are:
- Check the validity rules of the block and its fields.
- Check the versions (Block & App) are the same as in local state.
- Check the chainID's match.
- Check the height is correct.
- Check the `LastBlockID` corresponds to BlockID currently in state.
- Check the hashes in the header match those in state.
- Verify the LastCommit against state, this step is skipped for the initial height.
- This is where checking the signatures correspond to the correct block will be made.
- Make sure the proposer is part of the validator set.
- Validate bock time.
- Make sure the new blocks time is after the previous blocks time.
- Calculate the medianTime and check it against the blocks time.
- If the blocks height is the initial height then check if it matches the genesis time.
- Validate the evidence in the block. Note: Evidence can be empty
## 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:
```go
type Header struct {
// basic block info
Version Version
ChainID string
Height int64
Time Time
// prev block info
LastBlockID BlockID
// hashes of block data
LastCommitHash []byte // commit from validators from the last block
DataHash []byte // MerkleRoot of transaction hashes
// hashes from the app output from the prev block
ValidatorsHash []byte // validators for the current block
NextValidatorsHash []byte // validators for the next block
ConsensusHash []byte // consensus params for current block
AppHash []byte // state after txs from the previous block
LastResultsHash []byte // root hash of all results from the txs from the previous block
// consensus info
EvidenceHash []byte // evidence included in the block
ProposerAddress []byte // original proposer of the block
```
Further details on each of these fields is described below.
| Name | Type | Description | Validation |
|-------------------|---------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
| Version | [Version](#version) | Version defines the application and protocol verion being used. | Must adhere to the validation rules of [Version](#version) |
| ChainID | String | ChainID is the ID of the chain. This must be unique to your chain. | ChainID must be less than 50 bytes. |
| Height | int64 | Height is the height for this header. | Must be > 0, >= initialHeight, and == previous Height+1 |
| Time | [Time](#time) | The timestamp is equal to the weighted median of validators present in the last commit. Read more on time in the [BFT-time section](../consensus/bft-time.md). Note: the timestamp of a vote must be greater by at least one millisecond than that of the block being voted on. | Time must be >= previous header timestamp + consensus parameters TimeIotaMs. The timestamp of the first block must be equal to the genesis time (since there's no votes to compute the median). |
| LastBlockID | [BlockID](#blockid) | BlockID of the previous block. | Must adhere to the validation rules of [blockID](#blockid). The first block has `block.Header.LastBlockID == BlockID{}`. |
| LastCommitHash | slice of bytes (`[]byte`) | MerkleRoot of the lastCommit's signatures. The signatures represent the validators that committed to the last block. The first block has an empty slices of bytes for the hash. | Must be of length 32 |
| DataHash | slice of bytes (`[]byte`) | MerkleRoot of the hash of transactions. **Note**: The transactions are hashed before being included in the merkle tree, the leaves of the Merkle tree are the hashes, not the transactions themselves. | Must be of length 32 |
| ValidatorHash | slice of bytes (`[]byte`) | MerkleRoot of the current validator set. The validators are first sorted by voting power (descending), then by address (ascending) prior to computing the MerkleRoot. | Must be of length 32 |
| NextValidatorHash | slice of bytes (`[]byte`) | MerkleRoot of the next validator set. The validators are first sorted by voting power (descending), then by address (ascending) prior to computing the MerkleRoot. | Must be of length 32 |
| ConsensusHash | slice of bytes (`[]byte`) | Hash of the protobuf encoded consensus parameters. | Must be of length 32 |
| AppHash | slice of bytes (`[]byte`) | Arbitrary byte array returned by the application after executing and commiting the previous block. It serves as the basis for validating any merkle proofs that comes from the ABCI application and represents the state of the actual application rather than the state of the blockchain itself. The first block's `block.Header.AppHash` is given by `ResponseInitChain.app_hash`. | This hash is determined by the application, Tendermint can not perform validation on it. |
| LastResultHash | slice of bytes (`[]byte`) | `LastResultsHash` is the root hash of a Merkle tree built from `ResponseDeliverTx` responses (`Log`,`Info`, `Codespace` and `Events` fields are ignored). | Must be of length 32. The first block has `block.Header.ResultsHash == MerkleRoot(nil)`, i.e. the hash of an empty input, for RFC-6962 conformance. |
| EvidenceHash | slice of bytes (`[]byte`) | MerkleRoot of the evidence of Byzantine behaviour included in this block. | Must be of length 32 |
| ProposerAddress | slice of bytes (`[]byte`) | Address of the original proposer of the block. Validator must be in the current validatorSet. | Must be of length 20 |
## Version
```go
type Version struct {
Block uint64
App uint64
}
```
The `Version` contains the protocol version for the blockchain and the
application as two `uint64` values.
| Name | type | Description | Validation |
|-------|--------|-------------|------------------------------------------------------------------------------------------------------------------|
| Block | uint64 | This number represents the version of the block protocol and must be the same throughout an operational network | Must be equal to protocol version being used in a network (`block.Version.Block == state.Version.Consensus.Block`) |
| App | uint64 | App version is decided on by the application. Read [here](../abci/abci.md#info) | `block.Version.App == state.Version.Consensus.App` |
## BlockID
The `BlockID` contains two distinct Merkle roots of the block.
The first, used as the block's main hash, is the MerkleRoot
of all the fields in the header (ie. `MerkleRoot(header)`.
The second, used for secure gossipping of the block during consensus,
is the MerkleRoot of the complete serialized block
cut into parts (ie. `MerkleRoot(MakeParts(block))`).
The `BlockID` includes these two hashes, as well as the number of
parts (ie. `len(MakeParts(block))`)
```go
type BlockID struct {
Hash []byte
PartSetHeader PartSetHeader
}
The `BlockID` contains two distinct Merkle roots of the block. The `BlockID` includes these two hashes, as well as the number of parts (ie. `len(MakeParts(block))`)
type PartSetHeader struct {
Total uint32
Hash []byte
}
```
| Name | Type | Description | Validation |
|-------------|-----------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------|
| Hash | slice of bytes (`[]byte`) | MerkleRoot of all the fields in the header (ie. `MerkleRoot(header)`. | hash must be of length 32 |
| PartSetHeader | [PartSetHeader](#PartSetHeader) | Used for secure gossiping of the block during consensus, is the MerkleRoot of the complete serialized block cut into parts (ie. `MerkleRoot(MakeParts(block))`). | Must adhere to the validation rules of [PartSetHeader](#PartSetHeader) |
See [MerkleRoot](./encoding.md#MerkleRoot) for details.
## PartSetHeader
| Name | Type | Description | Validation |
|-------|---------------------------|-----------------------------------|----------------------|
| Total | int32 | Total amount of parts for a block | Must be > 0 |
| Hash | slice of bytes (`[]byte`) | MerkleRoot of a serialized block | Must be of length 32 |
## Time
Tendermint uses the
[Google.Protobuf.WellKnownTypes.Timestamp](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/timestamp)
Tendermint uses the [Google.Protobuf.WellKnownTypes.Timestamp](https://developers.google.com/protocol-buffers/docs/reference/csharp/class/google/protobuf/well-known-types/timestamp)
format, which uses two integers, one for Seconds and for Nanoseconds.
## Data
Data is just a wrapper for a list of transactions, where transactions are
arbitrary byte arrays:
Data is just a wrapper for a list of transactions, where transactions are arbitrary byte arrays:
```go
type Data struct {
Txs [][]byte
}
```
| Name | Type | Description | Validation |
|------|----------------------------|------------------------|-----------------------------------------------------------------------------|
| Txs | Matrix of bytes ([][]byte) | Slice of transactions. | Validation does not occur on this field, this data is unknown to Tendermint |
## Commit
Commit is a simple wrapper for a list of signatures, with one for each
validator. It also contains the relevant BlockID, height and round:
Commit is a simple wrapper for a list of signatures, with one for each validator. It also contains the relevant BlockID, height and round:
```go
type Commit struct {
Height int64
Round int32
BlockID BlockID
Signatures []CommitSig
}
```
| Name | Type | Description | Validation |
|------------|----------------------------------|----------------------------------------------------------------------|----------------------------------------------------------------------------------------------------------|
| Height | int64 | Height at which this commit was created. | Must be > 0 |
| Round | int32 | Round that the commit corresponds to. | Must be > 0 |
| BlockID | [BlockID](#blockid) | The blockID of the corresponding block. | Must adhere to the validation rules of [BlockID](#blockid). |
| Signatures | Array of [CommitSig](#commitsig) | Array of commit signatures that correspond to current validator set. | Length of signatures must be > 0 and adhere to the validation of each individual [Commitsig](#commitsig) |
## CommitSig
@ -137,63 +176,98 @@ type Commit struct {
a particular `BlockID` or was absent. It's a part of the `Commit` and can be used
to reconstruct the vote set given the validator set.
```go
type BlockIDFlag byte
| Name | Type | Description | Validation |
|------------------|-----------------------------|---------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------|
| BlockIDFlag | [BlockIDFlag](#blockidflag) | Represents the validators participation in consensus: Either voted for the block that received the majority, voted for another block, voted nil or did not vote | Must be one of the fields in the [BlockIDFlag](#blockidflag) enum |
| ValidatorAddress | [Address](#address) | Address of the validator | Must be of length 20 |
| Timestamp | [Time](#time) | This field will vary from `CommitSig` to `CommitSig`. It represents the timestamp of the validator. | [Time](#time) |
| Signature | [Signature](#signature) | Signature corresponding to the validators participation in consensus. | The length of the signature must be > 0 and < than 64 |
const (
// BlockIDFlagAbsent - no vote was received from a validator.
BlockIDFlagAbsent BlockIDFlag = 0x01
// BlockIDFlagCommit - voted for the Commit.BlockID.
BlockIDFlagCommit = 0x02
// BlockIDFlagNil - voted for nil.
BlockIDFlagNil = 0x03
)
NOTE: `ValidatorAddress` and `Timestamp` fields may be removed in the future
(see [ADR-25](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-025-commit.md)).
type CommitSig struct {
BlockIDFlag BlockIDFlag
ValidatorAddress Address
Timestamp time.Time
Signature []byte
## BlockIDFlag
BlockIDFlag represents which BlockID the [signature](#commitsig) is for.
```go
enum BlockIDFlag {
BLOCK_ID_FLAG_UNKNOWN = 0;
BLOCK_ID_FLAG_ABSENT = 1; // signatures for other blocks are also considered absent
BLOCK_ID_FLAG_COMMIT = 2;
BLOCK_ID_FLAG_NIL = 3;
}
```
NOTE: `ValidatorAddress` and `Timestamp` fields may be removed in the future
(see
[ADR-25](https://github.com/tendermint/tendermint/blob/master/docs/architecture/adr-025-commit.md)).
## Vote
A vote is a signed message from a validator for a particular block.
The vote includes information about the validator signing it.
The vote includes information about the validator signing it. When stored in the blockchain or propagated over the network, votes are encoded in Protobuf.
| Name | Type | Description | Validation |
|------------------|---------------------------------|---------------------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------|
| Type | [SignedMsgType](#signedmsgtype) | Either prevote or precommit. [SignedMsgType](#signedmsgtype) | A Vote is valid if its corresponding fields are included in the enum [signedMsgType](#signedmsgtype) |
| Height | int64 | Height for which this vote was created for | Must be > 0 |
| Round | int32 | Round that the commit corresponds to. | Must be > 0 |
| BlockID | [BlockID](#blockid) | The blockID of the corresponding block. | [BlockID](#blockid) |
| Timestamp | [Time](#Time) | Timestamp represents the time at which a validator signed. | [Time](#time) |
| ValidatorAddress | slice of bytes (`[]byte`) | Address of the validator | Length must be equal to 20 |
| ValidatorIndex | int32 | Index at a specific block height that corresponds to the Index of the validator in the set. | must be > 0 |
| Signature | slice of bytes (`[]byte`) | Signature by the validator if they participated in consensus for the associated bock. | Length of signature must be > 0 and < 64 |
## CanonicalVote
CanonicalVote is for validator signing. This type will not be present in a block. Votes are represented via `CanonicalVote` and also encoded using protobuf via `type.SignBytes` which includes the `ChainID`, and uses a different ordering of
the fields.
```proto
message CanonicalVote {
SignedMsgType type = 1;
sfixed64 height = 2;
sfixed64 round = 3;
CanonicalBlockID block_id = 4;
google.protobuf.Timestamp timestamp = 5;
string chain_id = 6;
}
```
For signing, votes are represented via [`CanonicalVote`](#canonicalvote) and also encoded using protobuf via
`type.SignBytes` which includes the `ChainID`, and uses a different ordering of
the fields.
We define a method `Verify` that returns `true` if the signature verifies against the pubkey for the `SignBytes`
using the given ChainID:
```go
type Vote struct {
Type SignedMsgType
Height int64
Round int32
BlockID BlockID
Timestamp Time
ValidatorAddress []byte
ValidatorIndex int32
Signature []byte
func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error {
if !bytes.Equal(pubKey.Address(), vote.ValidatorAddress) {
return ErrVoteInvalidValidatorAddress
}
if !pubKey.VerifyBytes(types.VoteSignBytes(chainID), vote.Signature) {
return ErrVoteInvalidSignature
}
return nil
}
```
```protobuf
## SignedMsgType
Signed message type represents a signed messages in consensus.
```proto
enum SignedMsgType {
SIGNED_MSG_TYPE_UNKNOWN = 0;
// Votes
PREVOTE_TYPE = 1;
PRECOMMIT_TYPE = 2;
// Proposals
PROPOSAL_TYPE = 32;
SIGNED_MSG_TYPE_PREVOTE = 1;
SIGNED_MSG_TYPE_PRECOMMIT = 2;
// Proposal
SIGNED_MSG_TYPE_PROPOSAL = 32;
}
```
There are two types of votes:
a _prevote_ has `vote.Type == 1` and
a _precommit_ has `vote.Type == 2`.
## Signature
Signatures in Tendermint are raw bytes representing the underlying signature.
@ -204,43 +278,26 @@ See the [signature spec](./encoding.md#key-types) for more.
EvidenceData is a simple wrapper for a list of evidence:
```go
type EvidenceData struct {
Evidence []Evidence
}
```
| Name | Type | Description | Validation |
|----------|--------------------------------|------------------------------------------|-----------------------------------------------------------------|
| Evidence | Array of [Evidence](#evidence) | List of verified [evidence](#evidence) | Validation adheres to individual types of [Evidence](#evidence) |
## Evidence
Evidence in Tendermint is used to indicate breaches in the consensus by a validator.
It is implemented as the following interface.
```go
type Evidence interface {
Height() int64 // height of the equivocation
Bytes() []byte // bytes which comprise the evidence
Hash() []byte // hash of the evidence (this is also used for equality)
ValidateBasic() error // consistency check of the data
String() string // string representation of the evidence
}
```
All evidence can be encoded and decoded to and from Protobuf with the `EvidenceToProto()`
and `EvidenceFromProto()` functions. The [Fork Accountability](../consensus/light-client/accountability.md)
document provides a good overview for the types of evidence and how they occur. For evidence to be committed onchain, it must adhere to the validation rules of each evidence and must not be expired. The expiration age, measured in both block height and time is set in `EvidenceParams`. Each evidence uses
the timestamp of the block that the evidence occured at to indicate the age of the evidence.
The [Fork Accountability](../light-client/accountability.md) document provides a good overview of evidence and how they occur. For evidence to be committed onchain, it must adhere to the validation rules of each evidence and must not be expired. The expiration age, measured in both block height and time is set in `EvidenceParams`. Each evidence uses
the timestamp of the block that the evidence occurred at to indicate the age of the evidence.
### DuplicateVoteEvidence
`DuplicateVoteEvidence` represents a validator that has voted for two different blocks
in the same round of the same height. Votes are lexicographically sorted on `BlockID`.
```go
type DuplicateVoteEvidence struct {
VoteA *Vote
VoteB *Vote
}
```
| Name | Type | Description | Validation |
|-------|---------------|-----------------------------------------------------------------|-----------------------------------------------------|
| VoteA | [Vote](#vote) | One of the votes submitted by a validator when they equivocated | VoteA must adhere to [Vote](#vote) validation rules |
| VoteB | [Vote](#vote) | The second vote submitted by a validator when they equivocated | VoteB must adhere to [Vote](#vote) validation rules |
Valid Duplicate Vote Evidence must adhere to the following rules:
@ -252,20 +309,21 @@ Valid Duplicate Vote Evidence must adhere to the following rules:
- Vote signature must be valid (using the chainID)
- Evidence must not have expired: either age in terms of height or time must be
less than the age stated in the consensus params. Time is the block time that the
votes were a part of.
- For DuplicateVoteEvidence to be included in a block it must be within the time period outlined in [evidenceParams](../abci/abci.md#evidenceparams). Evidence expiration is measured as a combination of age in terms of height and time.
### LightClientAttackEvidence
```go
type LightClientAttackEvidence struct {
ConflictingBlock *LightBlock
CommonHeight int64
}
```
LightClientAttackEvidence is a generalized evidence that captures all forms of known attacks on
a light client such that a full node can verify, propose and commit the evidence on-chain for
punishment of the malicious validators. There are three forms of attacks: Lunatic, Equivocation
and Amnesia. These attacks are exhaustive. You can find a more detailed overview of this [here](../light-client/accountability#the_misbehavior_of_faulty_validators)
| Name | Type | Description | Validation |
|------------------|---------------------------|-------------|------------|
| ConflictingBlock | [LightBlock](#LightBlock) | Read Below | Must adhere to the validation rules of [lightBlock](#lightblock) |
| CommonHeight | int64 | Read Below | must be > 0 |
Valid Light Client Attack Evidence encompasses three types of attack and must adhere to the following rules
Valid Light Client Attack Evidence must adhere to the following rules:
- If the header of the light block is invalid, thus indicating a lunatic attack, the node must check that
they can use `verifySkipping` from their header at the common height to the conflicting header
@ -276,299 +334,55 @@ Valid Light Client Attack Evidence encompasses three types of attack and must ad
- The trusted header of the node at the same height as the conflicting header must have a different hash to
the conflicting header.
- Evidence must not have expired. The height (and thus the time) is taken from the common height.
## Validation
Here we describe the validation rules for every element in a block.
Blocks which do not satisfy these rules are considered invalid.
We abuse notation by using something that looks like Go, supplemented with English.
A statement such as `x == y` is an assertion - if it fails, the item is invalid.
We refer to certain globally available objects:
`block` is the block under consideration,
`prevBlock` is the `block` at the previous height,
and `state` keeps track of the validator set, the consensus parameters
and other results from the application. At the point when `block` is the block under consideration,
the current version of the `state` corresponds to the state
after executing transactions from the `prevBlock`.
Elements of an object are accessed as expected,
ie. `block.Header`.
See the [definition of `State`](./state.md).
### Header
A Header is valid if its corresponding fields are valid.
### Version
```go
block.Version.Block == state.Version.Consensus.Block
block.Version.App == state.Version.Consensus.App
```
The block version must match consensus version from the state.
### ChainID
- For LightClientAttackEvidence to be included in a block it must be within the time period outlined in [evidenceParams](../abci/abci.md#evidenceparams). Evidence expiration is measured as a combination of age in terms of height and time.
```go
len(block.ChainID) < 50
```
## LightBlock
ChainID must be less than 50 bytes.
LightBlock is the core data structure of the [light client](../light-client/README.md). It combines two data structures needed for verification ([signedHeader](#signedheader) & [validatorSet](#validatorset)).
### Height
```go
block.Header.Height > 0
block.Header.Height >= state.InitialHeight
block.Header.Height == prevBlock.Header.Height + 1
```
| Name | Type | Description | Validation |
|--------------|-------------------------------|----------------------------------------------------------------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
| SignedHeader | [SignedHeader](#signedheader) | The header and commit, these are used for verification purposes. To find out more visit [light client docs](../light-client/README.md) | Must not be nil and adhere to the validation rules of [signedHeader](#signedheader) |
| ValidatorSet | [ValidatorSet](#validatorset) | The validatorSet is used to help with verify that the validators in that committed the infraction were truly in the validator set. | Must not be nil and adhere to the validation rules of [validatorSet](#validatorset) |
The height is an incrementing integer. The first block has `block.Header.Height == state.InitialHeight`, derived from the genesis file.
The `SignedHeader` and `ValidatorSet` are linked by the hash of the validator set(`SignedHeader.ValidatorsHash == ValidatorSet.Hash()`.
### Time
## SignedHeader
```go
block.Header.Timestamp >= prevBlock.Header.Timestamp + state.consensusParams.Block.TimeIotaMs
block.Header.Timestamp == MedianTime(block.LastCommit, state.LastValidators)
```
The SignedhHeader is the [header](#header) accompanied by the commit to prove it.
The block timestamp must be monotonic.
It must equal the weighted median of the timestamps of the valid signatures in the block.LastCommit.
| Name | Type | Description | Validation |
|--------|-------------------|-------------------|---------------------------------------------------------------------------------|
| Header | [Header](#Header) | [Header](#header) | Header cannot be nil and must adhere to the [Header](#Header) validation criteria |
| Commit | [Commit](#commit) | [Commit](#commit) | Commit cannot be nil and must adhere to the [Commit](#commit) criteria |
Note: the timestamp of a vote must be greater by at least one millisecond than that of the
block being voted on.
## ValidatorSet
The timestamp of the first block must be equal to the genesis time (since
there's no votes to compute the median).
| Name | Type | Description | Validation |
|------------|----------------------------------|----------------------------------------------------|-------------------------------------------------------------------------------------------------------------------|
| Validators | Array of [validator](#validator) | List of the active validators at a specific height | The list of validators can not be empty or nil and must adhere to the validation rules of [validator](#validator) |
| Proposer | [validator](#validator) | The block proposer for the corresponding block | The proposer cannot be nil and must adhere to the validation rules of [validator](#validator) |
```go
if block.Header.Height == state.InitialHeight {
block.Header.Timestamp == genesisTime
}
```
## Validator
See the section on [BFT time](../consensus/bft-time.md) for more details.
| Name | Type | Description | Validation |
|------------------|---------------------------|---------------------------------------------------------------------------------------------------|---------------------------------|
| Address | [Address](#address) | Validators Address | Length must be of size 20 |
| Pubkey | slice of bytes (`[]byte`) | Validators Public Key | must be a length greater than 0 |
| VotingPower | int64 | Validators voting power | cannot be < 0 |
| ProposerPriority | int64 | Validators proposer priority. This is used to gauge when a validator is up next to propose blocks | No validation, value can be negative and positive |
### LastBlockID
## Address
LastBlockID is the previous block's BlockID:
Address is a type alias of a slice of bytes. The address is calculated by hashing the public key using sha256 and truncating it to only use the first 20 bytes of the slice.
```go
prevBlockParts := MakeParts(prevBlock)
block.Header.LastBlockID == BlockID {
Hash: MerkleRoot(prevBlock.Header),
PartsHeader{
Hash: MerkleRoot(prevBlockParts),
Total: len(prevBlockParts),
},
}
```
The first block has `block.Header.LastBlockID == BlockID{}`.
### LastCommitHash
```go
block.Header.LastCommitHash == MerkleRoot(block.LastCommit.Signatures)
```
MerkleRoot of the signatures included in the block.
These are the commit signatures of the validators that committed the previous
block.
The first block has `block.Header.LastCommitHash == []byte{}`
### DataHash
```go
block.Header.DataHash == MerkleRoot(Hashes(block.Txs.Txs))
```
MerkleRoot of the hashes of transactions included in the block.
Note the transactions are hashed before being included in the Merkle tree,
so the leaves of the Merkle tree are the hashes, not the transactions
themselves. This is because transaction hashes are regularly used as identifiers for
transactions.
### ValidatorsHash
```go
block.ValidatorsHash == MerkleRoot(state.Validators)
```
MerkleRoot of the current validator set that is committing the block.
This can be used to validate the `LastCommit` included in the next block.
Note that before computing the MerkleRoot the validators are sorted
first by voting power (descending), then by address (ascending).
### NextValidatorsHash
```go
block.NextValidatorsHash == MerkleRoot(state.NextValidators)
```
MerkleRoot of the next validator set that will be the validator set that commits the next block.
This is included so that the current validator set gets a chance to sign the
next validator sets Merkle root.
Note that before computing the MerkleRoot the validators are sorted
first by voting power (descending), then by address (ascending).
### ConsensusHash
```go
block.ConsensusHash == state.ConsensusParams.Hash()
```
Hash of the protobuf-encoding of a subset of the consensus parameters.
### AppHash
```go
block.AppHash == state.AppHash
```
Arbitrary byte array returned by the application after executing and commiting the previous block. It serves as the basis for validating any merkle proofs that comes from the ABCI application and represents the state of the actual application rather than the state of the blockchain itself.
The first block's `block.Header.AppHash` is given by `ResponseInitChain.app_hash`.
### LastResultsHash
```go
block.LastResultsHash == MerkleRoot([]ResponseDeliverTx)
```
`LastResultsHash` is the root hash of a Merkle tree built from `ResponseDeliverTx` responses (`Log`,`Info`, `Codespace` and `Events` fields are ignored).
The first block has `block.Header.ResultsHash == MerkleRoot(nil)`, i.e. the hash of an empty input, for RFC-6962 conformance.
## EvidenceHash
```go
block.EvidenceHash == MerkleRoot(block.Evidence)
```
MerkleRoot of the evidence of Byzantine behaviour included in this block.
### ProposerAddress
```go
block.Header.ProposerAddress in state.Validators
```
Address of the original proposer of the block. Must be a current validator.
## Txs
Arbitrary length array of arbitrary length byte-arrays.
## LastCommit
The first height is an exception - it requires the `LastCommit` to be empty:
```go
if block.Header.Height == state.InitialHeight {
len(b.LastCommit) == 0
}
```
Otherwise, we require:
```go
len(block.LastCommit) == len(state.LastValidators)
talliedVotingPower := 0
for i, commitSig := range block.LastCommit.Signatures {
if commitSig.Absent() {
continue
}
vote.BlockID == block.LastBlockID
val := state.LastValidators[i]
vote.Verify(block.ChainID, val.PubKey) == true
talliedVotingPower += val.VotingPower
}
talliedVotingPower > (2/3)*TotalVotingPower(state.LastValidators)
```
Includes one vote for every current validator.
All votes must either be for the previous block, nil or absent.
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 2/3 of the total voting power of the complete validator set.
The number of votes in a commit is limited to 10000 (see `types.MaxVotesCount`).
### Vote
A vote is a signed message broadcast in the consensus for a particular block at a particular height and round.
When stored in the blockchain or propagated over the network, votes are encoded in Protobuf.
For signing, votes are represented via `CanonicalVote` and also encoded using Protobuf via
`VoteSignBytes` which includes the `ChainID`, and uses a different ordering of
the fields.
We define a method `Verify` that returns `true` if the signature verifies against the pubkey for the `SignBytes`
using the given ChainID:
```go
func (vote *Vote) Verify(chainID string, pubKey crypto.PubKey) error {
if !bytes.Equal(pubKey.Address(), vote.ValidatorAddress) {
return ErrVoteInvalidValidatorAddress
}
if !pubKey.VerifyBytes(vote.SignBytes(chainID), vote.Signature) {
return ErrVoteInvalidSignature
}
return nil
}
```
where `pubKey.Verify` performs the appropriate digital signature verification of the `pubKey`
against the given signature and message bytes.
## Execution
Once a block is validated, it can be executed against the state.
The state follows this recursive equation:
```go
state(initialHeight) = InitialState
state(h+1) <- Execute(state(h), ABCIApp, block(h))
```
where `InitialState` includes the initial consensus parameters and validator set,
and `ABCIApp` is an ABCI application that can return results and changes to the validator
set (TODO). Execute is defined as:
```go
func Execute(s State, app ABCIApp, block Block) State {
// Fuction ApplyBlock executes block of transactions against the app and returns the new root hash of the app state,
// modifications to the validator set and the changes of the consensus parameters.
AppHash, ValidatorChanges, ConsensusParamChanges := app.ApplyBlock(block)
const (
TruncatedSize = 20
)
nextConsensusParams := UpdateConsensusParams(state.ConsensusParams, ConsensusParamChanges)
return State{
ChainID: state.ChainID,
InitialHeight: state.InitialHeight,
LastResults: abciResponses.DeliverTxResults,
AppHash: AppHash,
InitialHeight: state.InitialHeight,
LastValidators: state.Validators,
Validators: state.NextValidators,
NextValidators: UpdateValidators(state.NextValidators, ValidatorChanges),
ConsensusParams: nextConsensusParams,
Version: {
Consensus: {
AppVersion: nextConsensusParams.Version.AppVersion,
},
},
}
func SumTruncated(bz []byte) []byte {
hash := sha256.Sum256(bz)
return hash[:TruncatedSize]
}
```

Loading…
Cancel
Save