LIP: 0034
Title: Define new block schema and processing for genesis block
Author: Jan Hackfeld <[email protected]>
Nazar Hussain <[email protected]>
Discussions-To: https://research.lisk.com/t/define-new-block-schema-and-processing-for-genesis-block/221
Status: Replaced
Type: Standards Track
Created: 2020-04-08
Updated: 2024-01-04
Requires: 0003, 0014, 0017, 0018, 0022, 0029, 0030, 0032
Superseded-By: 0060
This LIP defines a new block asset schema that allows to specify an initial state of accounts and an initial delegate list valid for a certain bootstrapping period. The LIP also specifies how such a block is processed. This can make it easier to create a genesis block, the first block of a new blockchain, with a desired initial state using the Lisk SDK. The block schema is general enough to also define a more complex state computed as a snapshot of an existing blockchain.
This LIP is licensed under the Creative Commons Zero 1.0 Universal.
The Lisk mainnet genesis block creates the initial state of the Lisk blockchain by including a set of transactions that result in the desired state after being processed. The transaction processing logic in the genesis block, however, works differently than in other blocks. For instance, the desired token distribution is achieved by balance transfers transactions originating from a special genesis account (public key d121d3abf5425fdc0f161d9ddb32f89b7750b4bdb0bff7d18b191d4b4bafa6d4
and Lisk address 6566229458323231555L
in the old format), which is allowed to have a negative balance. Additionally, an initial set of forgers is defined by including the respective delegate registration transactions and vote transactions. In contrast to transactions in other blocks, however, the transaction fee for all transactions in the genesis block is 0. This means that the transaction processing logic requires redundancy or special exceptions to be able to process the genesis block. Moreover, introducing the genesis account with special account rules creates extra complexity.
For these reasons, this LIP defines a new block asset schema that allows to specify an initial state directly, which is simpler and more compact. Using this new block schema, it should be easier for developers to define the desired initial state of a new blockchain created with the Lisk SDK. For defining the initial state of accounts we use the account schema and serialization introduced in LIP 0030.
In this section, we introduce a new block asset schema and define how a block using this schema is validated and processed.
The block follows the block
and blockHeader
schema defined in LIP 0029 with the following custom blockAsset
schema defined below:
blockAsset = {
"type": "object",
"properties": {
"accounts": {
"type": "array",
"items": {
...AccountSchema,
},
"fieldNumber": 1
},
"initDelegates": {
"type": "array",
"items": {
"dataType": "bytes"
},
"fieldNumber": 2
},
"initRounds": {
"dataType": "uint32",
"fieldNumber": 3
},
},
"required": [
"accounts",
"initDelegates",
"initRounds",
],
}
Here ...AccountSchema
is a placeholder for the account schema defined in LIP 0030.
For a block b
we impose the following conditions on its validity in addition to the schema above:
- Block
b
contains no transactions, which means the payload is empty. - The value
b.header.version
is0
, which is the block version used for this block. - The value
b.header.timestamp
is a Unix time in seconds and can be any value in theuint32
range. - The value
b.header.height
can be any value in theuint32
range. - The value
b.header.previousBlockID
can be any 32-byte value. - The value of
b.header.transactionRoot
is computed and verified as specified in LIP 0032, as for any other block. - The value of
b.header.generatorPublicKey
must be empty bytes. - The value of
b.header.reward
must be0
. - The value of
b.header.asset.accounts
must be an array of serialized accounts according to LIP 0030 ordered lexicographically by theaddress
values of the accounts.- For every account with
account.keys.numberOfSignatures>0
, the value of thekeys
property is validated according to Steps 1-4 of the Multisignature Account Registration validation in LIP 0017. - The sum of the
balance
values of all accounts is at most2^63-1
(this is the maximum int64 value).
- For every account with
- The value of the property
b.header.asset.initDelegates
must be an array of distinct 20-byte values which correspond to addresses of accounts provided inb.header.asset.accounts
. These accounts are required to be delegates, i.e., the value ofaccount.asset.delegate.username
is non-empty. The length of the list is at least 1 and at most the round length of the blockchain, denoted byROUND_LENGTH
. - The value of
b.header.asset.initRounds
must be at least3
. - The value of
b.header.signature
must be empty bytes.
Note that we impose no limit on the size of the overall block b
.
A block b
obeying the schema and validity conditions above implies the following state changes:
- The state of accounts is set as defined in
b.header.asset.accounts
. Only accounts defined inb.header.asset.accounts
exist. - The direct child block of
b
is the first block of a round. It further must have height equal tob.header.height + 1
,previousBlockID
equal to the block ID ofb
and a timestamp of at leastb.header.timestamp+BLOCK_SLOT_LENGTH
, whereBLOCK_SLOT_LENGTH
is the length of a block slot in seconds. This way, the genesis block timestamp defines the block slots of the blockchain as[b.header.timestamp+BLOCK_SLOT_LENGTH, b.header.timestamp + 2*BLOCK_SLOT_LENGTH)
,[b.header.timestamp + 2*BLOCK_SLOT_LENGTH, b.header.timestamp + 3*BLOCK_SLOT_LENGTH)
, and so on.
The generation of the forger list and consensus on blocks works differently during the bootstrap period of b.header.asset.initRounds
rounds, compared to the rounds afterwards.
The forger list of a round, which assigns delegates to block slots, is computed as follows:
- For the first
b.header.asset.initRounds
rounds, the forger list is given byIn particular, during the bootstrap period the forger list is not affected by the fact that a delegate is punished due to a proof-of-misbehavior or banned due to missing blocks.[ initDelegates[k % initDelegates.length] for k in [0,..., ROUND_LENGTH-1]].
- From round
b.header.asset.initRounds+1
onwards, the forger list is generated as specified in LIP 0003 and LIP 0022.
All delegate-related properties, i.e., pomHeights
, consecutiveMissedBlocks
, lastForgedHeight
, isBanned
, are updated the same way during the bootstrap period as after the bootstrap period, following the protocol specified in LIP 0023 and LIP 0024.
For the Lisk-BFT consensus protocol, specified in LIP 0014, the values chainMaxHeightPrevoted
and chainMaxHeightFinalized
are both initialized with b.header.height
. Additionally, the number of prevotes and precommits for blocks is computed as follows:
- The consensus weight of a delegates in
b.header.asset.initDelegates
is 0 during the firstb.header.asset.initRounds
rounds, which implies that any prevotes or precommits for blocks in these rounds are ignored. IflastHeightBootstrap
is the height of the last block in roundb.header.asset.initRounds
, then this means thatprevoteCounts[h]==0
andprecommitCounts[h]==0
holds for anyh
withb.header.height<h<=lastHeightBootstrap
. See also Section "Computing Prevotes and Precommits" in LIP 0014. Note that this change of consensus weight up to roundb.header.asset.initRounds
has no impact on the delegate weight in the DPoS voting system. - From round
b.header.asset.initRounds+1
onwards, the consensus works as specified in LIP 0014. If a delegate contained inb.header.asset.initDelegates
is active in roundb.header.asset.initRounds+1
, then its prevotes or precommits for blocks at height at mostlastHeightBootstrap
are still ignored. This can be achieved by settingdelegateMinHeightActive
to at leastlastHeightBootstrap+1
for any delegate, see Section "Computing Prevotes and Precommits" in LIP 0014.
Proof-of-Work blockchains such as Bitcoin do not require an initial distribution of tokens to start the blockchain, as new tokens can simply be created and distributed with new blocks. However, for a (Delegated) Proof-of-Stake blockchain, the right to create blocks necessarily depends on the distribution of stake. Therefore, an initial distribution of tokens is typically defined in the genesis block of the blockchain.
One approach to define an initial state is to start from a very simple starting state (no accounts/UTXOs or only one account/UTXO controlling all tokens) and include a set of transactions in the genesis block that result in the desired initial state. One advantage is that this makes it easier to guarantee that the initial state is consistent because it can be obtained from a sequence of valid transactions. This is the approach used for the Lisk mainnet genesis block.
Many Proof-of-Stake blockchains, however, define the desired initial state directly (Cardano, Cosmos, Substrate). This can be simpler and more compact than creating and signing a set of transactions that result in the desired state, in particular, if that state is rather complex. For instance, the desired initial token distribution can be achieved by specifying only the address, balance and initial nonce for all accounts according to LIP 0030, which is more compact than a set of signed balance transfer transactions to every account.
Further note that the block defined in the specifications enforces a unique account ordering and does not contain any signatures (which could only be created by the party holding the respective private key). This means that multiple parties that agree on the same initial state can independently create equal genesis blocks.
Overall, we therefore propose to define the state of accounts and the set of initial forgers directly as part of the genesis block, rather than using a set of transactions that result in the same state.
In order to keep the block asset schema as simple as possible, we choose to only include the ability to define an initial blockchain state, i.e., the state of accounts and an initial set of delegates used to generate the forger list valid for a certain number of rounds. Other configurable protocol parameters of a blockchain build with the Lisk SDK (for instance, the block time, the length of a round or the network identifier) are not included in the block asset schema. Of course these parameters as well as custom transactions or any chain-specific logic must still be shared among all nodes that run the same blockchain.
Using a sufficiently long bootstrapping time to allow for the majority of stake to vote is important for the security of the blockchain. That is why the length of the bootstrapping period can be defined using the property b.header.asset.initRounds
. If there is only a short bootstrapping period in which only a small fraction of stake votes, then the delegates in the first rounds are only supported by a small fraction of stake. This means that a malicious entity with small stake could control more than 2/3 of the forging delegates in one of the first rounds. This can be an issue as these delegates could attempt to perform a Long Range Attack at any later stage.
The selection of standby delegates and shuffling of the forger list for a round r
depends on seedReveal
values of blocks in rounds r-1
, r-2
and r-3
, see LIP 0022. Moreover, the selection of active delegates and random selection of standby delegates for round r
uses the delegate weights at the end or round r-2
. In order to avoid some edge cases, we therefore require that b.header.asset.initRounds
is set to at least 3
.
During the bootstrap period, the prevotes and precommits by the delegates in b.header.asset.initDelegates
are ignored and therefore no blocks are finalized. This is to avoid the possibility that a huge change of delegates at the end of the bootstrap period together with adverse network conditions could lead to two conflicting blocks being considered final. As discussed in LIP 0014, the safety guarantee of the Lisk-BFT consensus algorithm only holds if not too many honest delegates change at the same time. As the bootstrap period is rather short and users are mainly expected to vote during this period, we believe it is acceptable that blocks can only be finalized after the bootstrap period.
In this section, we shortly want to elaborate how the block asset defined above can be used to create the genesis block of a new blockchain.
- The value
b.header.timestamp
should be set to the Unix time in seconds that is supposed to be the starting time of the blockchain. - The height of the block,
b.header.height
, can simply be0
. - The value of
b.header.previousBlockID
should be the SHA-256 hash of the empty string as the block is the first block of the blockchain. - The value of
b.header.asset.accounts
can be used to specify the desired initial token distribution. - The value of
b.header.asset.initDelegates
should be an array of addresses of accounts that are registered as delegates. - The value of
b.header.asset.initRounds
is the number of rounds for which the initial forger list is used. It should be long enough for accounts to register delegates and the majority of stake to vote for delegates.
This LIP defines a new block schema and processing, but does not imply a hardfork of the Lisk mainnet.