Skip to content
This repository has been archived by the owner on Mar 24, 2023. It is now read-only.

Initial consensus rules #89

Merged
merged 52 commits into from
Nov 27, 2020
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
e8ab530
Organize consensus constants alphabetically.
adlerjohn Oct 27, 2020
b9cb328
Rename lastCommitRoot -> lastCommitHash.
adlerjohn Oct 27, 2020
96d7173
Add basic rules for header validity.
adlerjohn Oct 28, 2020
fc1adaa
Clarify serialization with deterministic protobuf.
adlerjohn Nov 3, 2020
09645e1
Add some info on parsing checks.
adlerjohn Nov 4, 2020
530e254
Split up logic around available data acquisition.
adlerjohn Nov 4, 2020
e1b4399
Clean up headings and notation.
adlerjohn Nov 4, 2020
5cd25ed
Add rules for available data header.
adlerjohn Nov 5, 2020
d48d262
Clarify available data header rules.
adlerjohn Nov 5, 2020
19fc54b
Add rules for last commit.
adlerjohn Nov 10, 2020
56ca555
Clean up, add initial available data setup.
adlerjohn Nov 10, 2020
f8c51f2
Fix bad links.
adlerjohn Nov 10, 2020
777cc69
Add rule for available data.
adlerjohn Nov 10, 2020
08a61d7
Merge branch 'master' into adlerjohn-initial_consensus_rules
adlerjohn Nov 10, 2020
0adcc79
Refactor available data original size field.
adlerjohn Nov 11, 2020
5a75720
Minor cleanup for data structures figure.
adlerjohn Nov 11, 2020
b566ec0
Add rule for lengths available data lists.
adlerjohn Nov 11, 2020
e2d3866
Clean up.
adlerjohn Nov 11, 2020
6ef8083
Clean up.
adlerjohn Nov 11, 2020
4d49b4a
Add minor introduction for state transitions.
adlerjohn Nov 11, 2020
de21ed0
Clarify how available data is parsed.
adlerjohn Nov 11, 2020
3e28226
Fix available data header rows and cols roots.
adlerjohn Nov 11, 2020
355553c
Add rules for basic validation of transaction data.
adlerjohn Nov 15, 2020
f3e1864
Reorganize headers.
adlerjohn Nov 15, 2020
8e01879
Add skeleton for processing transactions.
adlerjohn Nov 15, 2020
49fa3b3
Minor wording fix.
adlerjohn Nov 15, 2020
974b5fb
Fix bad link.
adlerjohn Nov 16, 2020
4f4971b
Add state access shorthand for use with state transitions.
adlerjohn Nov 17, 2020
c90a5d9
Add some partial rules for txs.
adlerjohn Nov 18, 2020
d0b9b86
Remove pay for padding tx.
adlerjohn Nov 18, 2020
343a3f7
Make padding shares deterministic.
adlerjohn Nov 18, 2020
68303b9
Extend rule to handle padding between txs and messages.
adlerjohn Nov 18, 2020
8154a60
Clarify rules around padding in data structures doc.
adlerjohn Nov 18, 2020
bf76dbc
Add consensus check for tx message namespace ID.
adlerjohn Nov 18, 2020
b33ce2b
Fix typo.
adlerjohn Nov 18, 2020
9073da8
Add rule for tx create validator.
adlerjohn Nov 18, 2020
d47e3f9
Add todo for end block.
adlerjohn Nov 18, 2020
3b0a96b
Add some rules for unbond tx.
adlerjohn Nov 18, 2020
2d63069
Rearrange.
adlerjohn Nov 18, 2020
92b311c
Add check for is validator when creating validator.
adlerjohn Nov 18, 2020
8c4c90f
Logic to handle unbonding validator with no delegations.
adlerjohn Nov 18, 2020
f9dba6e
Add delegation creation.
adlerjohn Nov 18, 2020
403aab7
Add rules for delegations. Handle balance transfers for rest of txs.
adlerjohn Nov 18, 2020
07a85db
Clean up todos.
adlerjohn Nov 18, 2020
1e2df94
Remove some todos based on issues filed.
adlerjohn Nov 18, 2020
37d31ca
Clean up wording around evidence.
adlerjohn Nov 24, 2020
0ebc93f
Fix indentation.
adlerjohn Nov 24, 2020
6d9648a
Revert changes to inter-message padding shares.
adlerjohn Nov 24, 2020
c5cf93c
Add pay for padding tx state transition rule.
adlerjohn Nov 25, 2020
1522239
Update specs/consensus.md
adlerjohn Nov 26, 2020
9a31d32
Fix max namespace id reserved.
adlerjohn Nov 26, 2020
074cd26
Fix missed graffiti bytes constant renaming.
adlerjohn Nov 27, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
104 changes: 88 additions & 16 deletions specs/consensus.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,15 @@ Consensus Rules
- [Leader Selection](#leader-selection)
- [Fork Choice](#fork-choice)
- [Block Validity](#block-validity)
- [Block Structure](#block-structure)
- [`block.header`](#blockheader)
- [`block.availableDataHeader`](#blockavailabledataheader)
- [`block.lastCommit`](#blocklastcommit)
- [`block.availableData`](#blockavailabledata)
- [State Transitions](#state-transitions)
- [`block.availableData.evidenceData`](#blockavailabledataevidencedata)
- [`block.availableData.transactionData`](#blockavailabledatatransactiondata)
- [Validators and Delegations](#validators-and-delegations)
- [Formatting](#formatting)
- [Availability](#availability)

## System Parameters

Expand All @@ -30,18 +35,18 @@ Consensus Rules

| name | type | value | unit | description |
| ------------------------------------ | -------- | ------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `VERSION` | `uint64` | `1` | | Version of the LazyLedger chain. Breaking changes (hard forks) must update this parameter. |
| `AVAILABLE_DATA_ORIGINAL_SQUARE_MAX` | `uint64` | | `share` | Maximum number of rows/columns of the original data [shares](data_structures.md#share) in [square layout](data_structures.md#arranging-available-data-into-shares). |
| `CHAIN_ID` | `uint64` | `1` | | Chain ID. Each chain assigns itself a (unique) ID. |
| `GENESIS_COIN_COUNT` | `uint64` | `10**8` | `4u` | `(= 100000000)` Number of coins at genesis. |
| `GRAFFITI_BYTES` | `uint64` | `32` | `byte` | Maximum size of transaction graffiti, in bytes. |
adlerjohn marked this conversation as resolved.
Show resolved Hide resolved
| `MAX_VALIDATORS` | `uint16` | `64` | | Maximum number of active validators. |
| `NAMESPACE_ID_BYTES` | `uint64` | `8` | `byte` | Size of namespace ID, in bytes. |
| `NAMESPACE_ID_MAX_RESERVED` | `uint64` | `255` | | Value of maximum reserved namespace ID (inclusive). 1 byte worth of IDs. |
| `SHARE_SIZE` | `uint64` | `256` | `byte` | Size of transaction and message [shares](data_structures.md#share), in bytes. |
| `SHARE_RESERVED_BYTES` | `uint64` | `1` | `byte` | Bytes reserved at the beginning of each [share](data_structures.md#share). Must be sufficient to represent `SHARE_SIZE`. |
| `AVAILABLE_DATA_ORIGINAL_SQUARE_MAX` | `uint64` | | `share` | Maximum number of rows/columns of the original data [shares](data_structures.md#share) in [square layout](data_structures.md#arranging-available-data-into-shares). |
| `GENESIS_COIN_COUNT` | `uint64` | `10**8` | `4u` | `(= 100000000)` Number of coins at genesis. |
| `UNBONDING_DURATION` | `uint32` | | `block` | Duration, in blocks, for unbonding a validator or delegation. |
| `MAX_VALIDATORS` | `uint16` | `64` | | Maximum number of active validators. |
| `SHARE_SIZE` | `uint64` | `256` | `byte` | Size of transaction and message [shares](data_structures.md#share), in bytes. |
| `STATE_SUBTREE_RESERVED_BYTES` | `uint64` | `1` | `byte` | Number of bytes reserved to identify state subtrees. |
| `GRAFFITI_BYTES` | `uint64` | `32` | `byte` | Maximum size of transaction graffiti, in bytes. |
| `UNBONDING_DURATION` | `uint32` | | `block` | Duration, in blocks, for unbonding a validator or delegation. |
| `VERSION` | `uint64` | `1` | | Version of the LazyLedger chain. Breaking changes (hard forks) must update this parameter. |

### Reserved Namespace IDs

Expand All @@ -61,7 +66,6 @@ Consensus Rules
| `ACTIVE_VALIDATORS_SUBTREE_ID` | `StateSubtreeID` | `0x02` |
| `INACTIVE_VALIDATORS_SUBTREE_ID` | `StateSubtreeID` | `0x03` |


### Rewards and Penalties

| name | type | value | unit | description |
Expand All @@ -71,12 +75,86 @@ Consensus Rules

## Leader Selection

TODO

## Fork Choice

TODO

## Block Validity

This section specifies the entirety of the validity rules for a newly-seen block, `block`.

### Block Structure

Before executing [state transitions](#state-transitions), the structure of the [block](./data_structures.md#block) must be verified.

The following block fields are acquired from the network and parsed (i.e. [deserialized](./data_structures.md#serialization)). If they cannot be parsed, the block is ignored but is not explicitly considered invalid by consensus rules. Further implications of ignoring a block are found in the [networking spec](./networking.md).
1. [block.header](./data_structures.md#header)
1. [block.availableDataHeader](./data_structures.md#availabledataheader)
1. [block.lastCommit](./data_structures.md#commit)

If the above fields are parsed successfully, the available data `block.availableData` is acquired in erasure-coded form as [a list of share rows](./networking.md#availabledata), then parsed. If it cannot be parsed, the block is ignored but not explicitly invalid, as above.
adlerjohn marked this conversation as resolved.
Show resolved Hide resolved

#### `block.header`

The [block header](./data_structures.md#header) `block.header` (`header` for short) is the first thing that is downloaded from the new block, and commits to everything inside the block in some way. For previous block `prev` (if `prev` is not known, then the block is ignored), and previous block header `prev.header`, the following checks must be `true`:
Copy link
Member

@liamsi liamsi Nov 26, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The block header block.header (header for short) is the first thing that is downloaded from the new block

If it is actively downloaded or received (e.g. via some gossip routine etc) should go in the network spec. Maybe say "processed"; this implies that you have it at hand.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The plan is actually to move that entire paragraph to the networking section, since a node won't even accept a block header unless it passes those checks.


`availableDataOriginalSquareSize` is computed as described [here](./data_structures.md#header).

1. `header.height` == `prev.header.height + 1`.
1. `header.timestamp` > `prev.header.timestamp`.
1. `header.lastBlockID` == the [block ID](./data_structures.md#blockid) of `prev`.
1. `header.lastCommitHash` == the [hash](./data_structures.md#hashing) of `lastCommit`.
1. `header.consensusRoot` == the value computed [here](./data_structures.md#consensus-parameters).
1. `header.stateCommitment` == the root of the state, computed [with the application of all state transitions in this block](#state-transitions).
1. `availableDataOriginalSquareSize` <= [`AVAILABLE_DATA_ORIGINAL_SQUARE_MAX`](#constants).
1. `header.availableDataRoot` == TODO available data root
1. `header.proposerAddress` == the [leader](#leader-selection) for `header.height`.

TODO define the genesis block

#### `block.availableDataHeader`

The [available data header](./data_structures.md#availabledataheader)) `block.availableDataHeader` (`availableDataHeader` for short) is then processed. This commits to the available data, which is only downloaded after the [consensus commit](#blocklastcommit) is processed. The following checks must be `true`:

1. Length of `availableDataHeader.availableDataCommitments` == `2 * availableDataOriginalSquareSize`.
1. The length of each element in `availableDataHeader.availableDataCommitments` must be [`32`](./consensus.md#hashing).

#### `block.lastCommit`

The last [commit](./data_structures.md#commit) `block.lastCommit` (`lastCommit` for short) is processed next. This is the Tendermint commit (i.e. polka of votes) _for the previous block_. For previous block `prev` and previous block header `prev.header`, the following checks must be `true`:

1. `lastCommit.height` == `prev.header.height`.
1. `lastCommit.round` >= `1`.
1. `lastCommit.blockID` == the [block ID](./data_structures.md#blockid) of `prev`.
1. Length of `lastCommit.signatures` <= [`MAX_VALIDATORS`](#constants).
1. Each of `lastCommit.signatures` must be a valid [CommitSig](./data_structures.md#commitsig)
1. The sum of the votes for `prev` in `lastCommit` must be at least 2/3 (rounded up) of the voting power of `prev`'s next validator set.

TODO define specifically how to validate commitsigs

#### `block.availableData`

The block's [available data](./data_structures.md#availabledata) (analogous to transactions in contemporary blockchain designs) `block.availableData` (`availableData` for short) is finally processed. The [list of share rows](./networking.md#availabledata) is parsed into the [actual data structures](./data_structures.md#availabledata); if parsing fails here, the block is invalid.

TODO define fraud proof for invalid block here

Once parsed, the following checks must be `true`:

1. The commitments of the [erasure-coded extended](./data_structures.md#2d-reed-solomon-encoding-scheme) `availableData` must match those in `header.availableDataHeader`. Implicitly, this means that both rows and columns must be ordered lexicographically by namespace ID since they are committed to in a [Namespace Merkle Tree](data_structures.md#namespace-merkle-tree).
1. Length of `availableData.intermediateStateRootData` == length of `availableData.transactionData` + length of `availableData.evidenceData`.
adlerjohn marked this conversation as resolved.
Show resolved Hide resolved

### State Transitions

#### `block.availableData.evidenceData`



#### `block.availableData.transactionData`



#### Validators and Delegations

A transaction `tx` that requests a new validator initializes a new [Validator](data_structures.md#validator) leaf in the inactive validators subtree for that account as follows:
Expand Down Expand Up @@ -149,9 +227,3 @@ then updates the target validator's voting power:
validator.delegatedCount -= 1
validator.votingPower -= delegation.votingPower
```

### Formatting

Leaves in the message [Namespace Merkle Tree](data_structures.md#namespace-merkle-tree) must be ordered lexicographically by namespace ID.

### Availability
20 changes: 12 additions & 8 deletions specs/data_structures.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ Block header, which is fully downloaded by both full clients and light clients.
| `height` | [Height](#type-aliases) | Block height. The genesis block is at height `1`. |
| `timestamp` | [Timestamp](#timestamp) | Timestamp of this block. |
| `lastBlockID` | [BlockID](#blockid) | Previous block's ID. |
| `lastCommitRoot` | [HashDigest](#hashdigest) | Previous block's Tendermint commit root. |
| `lastCommitHash` | [HashDigest](#hashdigest) | Previous block's Tendermint commit hash. |
| `consensusRoot` | [HashDigest](#hashdigest) | Merkle root of [consensus parameters](#consensus-parameters) for this block. |
| `stateCommitment` | [HashDigest](#hashdigest) | The [state root](#state) after this block's transactions are applied. |
| `availableDataOriginalSharesUsed` | `uint64` | The number of shares used in the [original data square](#arranging-available-data-into-shares) that are not [tail padding](./consensus.md#reserved-namespace-ids). |
Expand Down Expand Up @@ -145,12 +145,12 @@ Data that is [erasure-coded](#erasure-coding) for [data availability checks](htt

### Commit

| name | type | description |
| ------------ | --------------------------- | ----------- |
| `height` | [Height](#type-aliases) | |
| `round` | [Round](#type-aliases) | |
| `blockID` | [BlockID](#blockid) | |
| `signatures` | [CommitSig](#commitsig)`[]` | |
| name | type | description |
| ------------ | --------------------------- | ---------------------------------- |
| `height` | [Height](#type-aliases) | Block height. |
| `round` | [Round](#type-aliases) | Round. Incremented on view change. |
| `blockID` | [BlockID](#blockid) | Block ID of the previous block. |
| `signatures` | [CommitSig](#commitsig)`[]` | List of signatures. |

### Timestamp

Expand Down Expand Up @@ -204,7 +204,11 @@ Output of the [signing](#public-key-cryptography) process.

## Serialization

Objects that are committed to or signed over require a canonical serialization. This is done using TODO.
Objects that are committed to or signed over require a canonical serialization. This is done using a deterministic (and thus, bijective) variant of protobuf defined [here](https://github.com/cosmos/cosmos-sdk/blob/master/docs/architecture/adr-027-deterministic-protobuf-serialization.md).

Note: there are two requirements for a serialization scheme, should this need to be changed:
1. Must be bijective.
1. Serialization must include the length of dynamic structures (e.g. arrays with variable length).

## Hashing

Expand Down
4 changes: 2 additions & 2 deletions specs/figures/block_data_structures.dot
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ digraph G {

subgraph cluster_header {
label = "header";
struct1 [label = "height | timestamp | lastBlockID | <f3> lastCommitRoot | consensusRoot | stateCommitment | <f6> availableDataRoot | proposerAddress"];
struct1 [label = "height | timestamp | lastBlockID | <f3> lastCommitHash | consensusRoot | stateCommitment | availableDataOriginalSharesUsed | <f7> availableDataRoot | proposerAddress"];
}
}

struct1:f3 -> struct2;
struct1:f6 -> struct4 [label = "Merkle root of"];
struct1:f7 -> struct4 [label = "Merkle root of"];
struct4:f0 -> struct3 [label = "NMT roots to\nerasure-coded data"];

edge [style = invis];
Expand Down
Loading