4.8 KiB
NOTE: this wiki is mostly deprecated and left for archival purposes. Please see the documentation website which is built from the docs directory. Additional information about the specification can also be found in that directory.
Block
A Block
is composed of:
Header
: basic information about the chain and last stateData
: transactions and other dataLastCommit
: +2/3 precommit signatures for the previous block
Header
The block Header
is composed of:
ChainId (string)
: name of the blockchain, e.g. "tendermint"Height (int)
: sequential block number starting with 1Time (time)
: local time of the proposer who proposed this blockLastBlockHash ([]byte)
:block hash
of the previous block at heightHeight-1
LastBlockParts (PartSetHeader)
:partset header
of the previous blockStateHash ([]byte)
:state hash
of the state after processing this block
Data
The block Data
is composed of:
Txs ([]Tx)
: a list oftransactions
Commit
The block Commit
is composed of:
Precommits ([]Vote)
: a list ofprecommit votes
Vote
A Vote
is composed of:
Height (int)
: The block height being decided onRound (int)
: The consensus round number, starting with 0Type (byte)
: The type of vote, either aprevote
or aprecommit
BlockHash ([]byte)
: Theblock hash
of a valid block, ornil
BlockParts (PartSetHeader)
: The correspondingpartset header
, orx0000
if theblock hash
isnil
Signature (Signature)
: The signature of thisVote
'ssign-bytes
Vote Sign Bytes
The sign-bytes
of a transaction is produced by taking a stable-json
-like deterministic JSON wire
encoding of the vote (excluding the Signature
field), and wrapping it with {"chain_id":"tendermint","vote":...}
.
For example, a precommit vote might have the following sign-bytes
:
{"chain_id":"tendermint","vote":{"block_hash":"611801F57B4CE378DF1A3FFF1216656E89209A99","block_parts_header":{"hash":"B46697379DBE0774CC2C3B656083F07CA7E0F9CE","total":123},"height":1234,"round":1,"type":2}}
Block Hash
The block hash is the Simple Tree hash of the fields of the block Header
encoded as a list of KVPair
s.
State Hash
The state hash is the Simple Tree hash of the state's fields (e.g. BondedValidators
, UnbondingValidators
, Accounts
, ValidatorInfos
, and NameRegistry
) encoded as a list of KVPair
s. This state hash is recursively included in the block Header
and thus the block hash indirectly.
Transaction
A transaction is any sequence of bytes. It is up to your TMSP application to accept or reject transactions.
PartSet
PartSet is used to split a byteslice of data into parts (pieces) for transmission. By splitting data into smaller parts and computing a Merkle root hash on the list, you can verify that a part is legitimately part of the complete data, and the part can be forwarded to other peers before all the parts are known. In short, it's a fast way to propagate a large file over a gossip network.
PartSet was inspired by the LibSwift project.
Usage:
data := RandBytes(2 << 20) // Something large
partSet := NewPartSetFromData(data)
partSet.Total() // Total number of 4KB parts
partSet.Count() // Equal to the Total, since we already have all the parts
partSet.Hash() // The Merkle root hash
partSet.BitArray() // A BitArray of partSet.Total() 1's
header := partSet.Header() // Send this to the peer
header.Total // Total number of parts
header.Hash // The merkle root hash
// Now we'll reconstruct the data from the parts
partSet2 := NewPartSetFromHeader(header)
partSet2.Total() // Same total as partSet.Total()
partSet2.Count() // Zero, since this PartSet doesn't have any parts yet.
partSet2.Hash() // Same hash as in partSet.Hash()
partSet2.BitArray() // A BitArray of partSet.Total() 0's
// In a gossip network the parts would arrive in arbitrary order, perhaps
// in response to explicit requests for parts, or optimistically in response
// to the receiving peer's partSet.BitArray().
for !partSet2.IsComplete() {
part := receivePartFromGossipNetwork()
added, err := partSet2.AddPart(part)
if err != nil {
// A wrong part,
// the merkle trail does not hash to partSet2.Hash()
} else if !added {
// A duplicate part already received
}
}
data2, _ := ioutil.ReadAll(partSet2.GetReader())
bytes.Equal(data, data2) // true