mirror of
https://github.com/fluencelabs/tendermint
synced 2025-05-15 08:01:19 +00:00
177 lines
9.2 KiB
Markdown
177 lines
9.2 KiB
Markdown
# Block Structure
|
|
|
|
The tendermint consensus engine records all agreements by a supermajority of
|
|
nodes into a blockchain, which is replicated among all nodes. This blockchain
|
|
is accessible via various rpc endpoints, mainly `/block?height=` to get the full
|
|
block, as well as `/blockchain?minHeight=_&maxHeight=_` to get a list of headers.
|
|
But what exactly is stored in these blocks?
|
|
|
|
### Block
|
|
|
|
A [Block](/docs/specs/tendermint-types#Block) contains:
|
|
|
|
* a [Header](#header) contains merkle hashes for various chain states
|
|
* the [Data]((/docs/specs/tendermint-types#Data) is all transactions which are to be processed
|
|
* the [LastCommit](#commit) > 2/3 signatures for the last block
|
|
|
|
The signatures returned along with block `H` are those validating block `H-1`.
|
|
This can be a little confusing, but we must also consider that the
|
|
[Header](/docs/specs/tendermint-types#Header) also contains the `LastCommitHash`.
|
|
It would be impossible for a Header to include the commits that sign it, as it
|
|
would cause an infinite loop here. But when we get block `H`, we find
|
|
`Header.LastCommitHash`, which must match the hash of `LastCommit`.
|
|
|
|
### Header
|
|
|
|
The [Header](/docs/specs/tendermint-types#Header) contains lots of information (follow
|
|
link for up-to-date info). Notably, it maintains the `Height`, the `LastBlockID`
|
|
(to make it a chain), and hashes of the data, the app state, and the validator set.
|
|
This is important as the only item that is signed by the validators is the `Header`,
|
|
and all other data must be validated against one of the merkle hashes in the `Header`.
|
|
|
|
The `DataHash` can provide a nice check on the [Data](/docs/specs/tendermint-types#Data)
|
|
returned in this same block. If you are subscribed to new blocks, via tendermint RPC, in order to display or process the new transactions
|
|
you should at least validate that the `DataHash` is valid.
|
|
If it is important to verify autheniticity, you must wait for the `LastCommit` from the next block to make sure the block header (including `DataHash`) was properly signed.
|
|
|
|
The `ValidatorHash` contains a hash of the current
|
|
[Validators](/docs/specs/tendermint-types#Validator). Tracking all changes in the
|
|
validator set is complex, but a client can quickly compare this hash
|
|
with the [hash of the currently known validators](/docs/specs/tendermint-types#ValidatorSet.Hash)
|
|
to see if there have been changes.
|
|
|
|
The `AppHash` serves as the basis for validating any merkle proofs that come
|
|
from the [ABCI application](https://github.com/tendermint/abci). It represents
|
|
the state of the actual application, rather that the state of the blockchain
|
|
itself. This means it's necessary in order to perform any business logic,
|
|
such as verifying and account balance.
|
|
|
|
**Note** After the transactions are committed to a block, they still need to
|
|
be processed in a separate step, which happens between the blocks. If you
|
|
find a given transaction in the block at height `H`, the effects of running
|
|
that transaction will be first visible in the `AppHash` from the block
|
|
header at height `H+1`.
|
|
|
|
Like the `LastCommit` issue, this is a requirement of the
|
|
immutability of the block chain, as the application only applies transactions
|
|
*after* they are commited to the chain.
|
|
|
|
### Commit
|
|
|
|
The [Commit](/docs/specs/tendermint-types#Commit) contains a set of
|
|
[Votes](/docs/specs/tendermint-types#Vote) that were made by the validator set to
|
|
reach consensus on this block. This is the key to the security in any PoS
|
|
system, and actually no data that cannot be traced back to a block header
|
|
with a valid set of Votes can be trusted. Thus, getting the Commit data
|
|
and verifying the votes is extremely important.
|
|
|
|
As mentioned above, in order to find the `precommit votes` for block header `H`,
|
|
we need to query block `H+1`. Then we need to check the votes, make sure they
|
|
really are for that block, and properly formatted. Much of this code is implemented
|
|
in Go in the [light-client](https://github.com/tendermint/light-client) package,
|
|
especially [Node.SignedHeader](https://github.com/tendermint/light-client/blob/develop/rpc/node.go#L117).
|
|
If you look at the code, you will notice that we need to provide the `chainID`
|
|
of the blockchain in order to properly calculate the votes. This is to protect
|
|
anyone from swapping votes between chains to fake (or frame) a validator.
|
|
Also note that this `chainID` is in the `genesis.json` from _Tendermint_,
|
|
not the `genesis.json` from the basecoin app ([that is a different chainID...](https://github.com/tendermint/basecoin/issues/32)).
|
|
|
|
Once we have those votes,
|
|
and we calculated the proper [sign bytes](/docs/specs/tendermint-types#Vote.WriteSignBytes)
|
|
using the chainID and a [nice helper function](/docs/specs/tendermint-types#SignBytes),
|
|
we can verify them. The light client is responsible for maintaining a set of
|
|
validators that we trust. Each vote only stores the validators `Address`, as well
|
|
as the `Signature`. Assuming we have a local copy of the trusted validator set,
|
|
we can look up the `Public Key` of the validator given its `Address`, then
|
|
verify that the `Signature` matches the `SignBytes` and `Public Key`.
|
|
Then we sum up the total voting power of all validators, whose votes fulfilled
|
|
all these stringent requirements. If the total number of voting power for a single block is greater
|
|
than 2/3 of all voting power, then we can finally trust the
|
|
block header, the AppHash, and the proof we got from the ABCI application.
|
|
|
|
To make this a bit more concrete, you can take a look at a
|
|
[StaticCertifier](https://github.com/tendermint/light-client/blob/develop/rpc/certifier.go#L23)
|
|
to see how this logic works, given a static set of validators. And you can see
|
|
an example of how one can perform the entire chain of validation in the
|
|
proxy server [proof call](https://github.com/tendermint/light-client/blob/develop/proxy/viewer.go#L61)
|
|
or the [test code for auditing](https://github.com/tendermint/light-client/blob/develop/rpc/tests/node_test.go#L102).
|
|
|
|
#### Vote Sign Bytes
|
|
The `sign-bytes` of a vote is produced by taking a [`stable-json`](https://github.com/substack/json-stable-stringify)-like deterministic JSON [`wire`](/docs/specs/wire-protocol) encoding of the vote (excluding the `Signature` field), and wrapping it with `{"chain_id":"my_chain","vote":...}`.
|
|
|
|
For example, a precommit vote might have the following `sign-bytes`:
|
|
|
|
```json
|
|
{"chain_id":"my_chain","vote":{"block_hash":"611801F57B4CE378DF1A3FFF1216656E89209A99","block_parts_header":{"hash":"B46697379DBE0774CC2C3B656083F07CA7E0F9CE","total":123},"height":1234,"round":1,"type":2}}
|
|
```
|
|
|
|
### Block Hash
|
|
|
|
The [block hash](/docs/specs/tendermint-types#Block.Hash) is the [Simple Tree hash](Merkle-Trees#simple-tree-with-dictionaries) of the fields of the block `Header` encoded as a list of `KVPair`s.
|
|
|
|
### Transaction
|
|
|
|
A transaction is any sequence of bytes. It is up to your [ABCI](https://github.com/tendermint/abci) application to accept or reject transactions.
|
|
|
|
### BlockID
|
|
|
|
Many of these data structures refer to the [BlockID](/docs/specs/tendermint-types#BlockID),
|
|
which is the `BlockHash` (hash of the block header, also referred to by the next block)
|
|
along with the `PartSetHeader`. The `PartSetHeader` is explained below and is used internally
|
|
to orchestrate the p2p propogation. For clients, it is basically opaque bytes,
|
|
but they must match for all votes.
|
|
|
|
### PartSetHeader
|
|
|
|
The [PartSetHeader](/docs/specs/tendermint-types#PartSetHeader) contains the total number of pieces in a [PartSet](/docs/specs/tendermint-types#PartSet), and the Merkle root hash of those pieces.
|
|
|
|
### 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 securely propagate a large chunk of data (like a block) over a gossip network.
|
|
|
|
PartSet was inspired by the LibSwift project.
|
|
|
|
Usage:
|
|
|
|
```Go
|
|
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
|
|
```
|