-
Notifications
You must be signed in to change notification settings - Fork 6k
Description
Change log:
- Aug 8, 2017:
- Add gossip network
- Jul 24, 2017:
- Add block locking mechanism.
- Performance/bug fixes.
- Jun 26, 2017:
- Add
extraDatatools. - Update notes and discussions on zero gas price transaction
- Add
- Jun 22, 2017:
- Initial proposal of Istanbul BFT consensus protocol.
Pull request
Istanbul byzantine fault tolerant consensus protocol
Note, this work is deeply inspired by Clique POA. We've tried to design as similar a mechanism as possible in the protocol layer, such as with validator voting. We've also followed its EIP style of putting the background and rationale behind the proposed consensus protocol to help developers easily find technical references. This work is also inspired by Hyperledger's SBFT, Tendermint, HydraChain, and NCCU BFT.
Terminology
- Validator: Block validation participant.
- Proposer: A block validation participant that is chosen to propose block in a consensus round.
- Round: Consensus round. A round starts with the proposer creating a block proposal and ends with a block commitment or round change.
- Proposal: New block generation proposal which is undergoing consensus processing.
- Sequence: Sequence number of a proposal. A sequence number should be greater than all previous sequence numbers. Currently each proposed block height is its associated sequence number.
- Backlog: The storage to keep future consensus messages.
- Round state: Consensus messages of a specific sequence and round, including pre-prepare message, prepare message, and commit message.
- Consensus proof: The commitment signatures of a block that can prove the block has gone through the consensus process.
- Snapshot: The validator voting state from last epoch.
Consensus
Istanbul BFT is inspired by Castro-Liskov 99 paper. However, the original PBFT needed quite a bit of tweaking to make it work with blockchain. First off, there is no specific "client" which sends out requests and waits for the results. Instead, all of the validators can be seen as clients. Furthermore, to keep the blockchain progressing, a proposer will be continuously selected in each round to create block proposal for consensus. Also, for each consensus result, we expect to generate a verifiable new block rather than a bunch of read/write operations to the file system.
Istanbul BFT inherits from the original PBFT by using 3-phase consensus, PRE-PREPARE, PREPARE, and COMMIT. The system can tolerate at most of F faulty nodes in a N validator nodes network, where N = 3F + 1. Before each round, the validators will pick one of them as the proposer, by default, in a round robin fashion. The proposer will then propose a new block proposal and broadcast it along with the PRE-PREPARE message. Upon receiving the PRE-PREPARE message from the proposer, validators enter the state of PRE-PREPARED and then broadcast PREPARE message. This step is to make sure all validators are working on the same sequence and the same round. While receiving 2F + 1 of PREPARE messages, the validator enters the state of PREPARED and then broadcasts COMMIT message. This step is to inform its peers that it accepts the proposed block and is going to insert the block to the chain. Lastly, validators wait for 2F + 1 of COMMIT messages to enter COMMITTED state and then insert the block to the chain.
Blocks in Istanbul BFT protocol are final, which means that there are no forks and any valid block must be somewhere in the main chain. To prevent a faulty node from generating a totally different chain from the main chain, each validator appends 2F + 1 received COMMIT signatures to extraData field in the header before inserting it into the chain. Thus blocks are self-verifiable and light client can be supported as well. However, the dynamic extraData would cause an issue on block hash calculation. Since the same block from different validators can have different set of COMMIT signatures, the same block can have different block hashes as well. To solve this, we calculate the block hash by excluding the COMMIT signatures part. Therefore, we can still keep the block/block hash consistency as well as put the consensus proof in the block header.
Consensus states
Istanbul BFT is a state machine replication algorithm. Each validator maintains a state machine replica in order reach block consensus.
States:
NEW ROUND: Proposer to send new block proposal. Validators wait forPRE-PREPAREmessage.PRE-PREPARED: A validator has receivedPRE-PREPAREmessage and broadcastsPREPAREmessage. Then it waits for2F + 1ofPREPAREorCOMMITmessages.PREPARED: A validator has received2F + 1ofPREPAREmessages and broadcastsCOMMITmessages. Then it waits for2F + 1ofCOMMITmessages.COMMITTED: A validator has received2F + 1ofCOMMITmessages and is able to insert the proposed block into the blockchain.FINAL COMMITTED: A new block is successfully inserted into the blockchain and the validator is ready for the next round.ROUND CHANGE: A validator is waiting for2F + 1ofROUND CHANGEmessages on the same proposed round number.
State transitions:
NEW ROUND->PRE-PREPARED:- Proposer collects transactions from txpool.
- Proposer generates a block proposal and broadcasts it to validators. It then enters the
PRE-PREPAREDstate. - Each validator enters
PRE-PREPAREDupon receiving thePRE-PREPAREmessage with the following conditions:- Block proposal is from the valid proposer.
- Block header is valid.
- Block proposal's sequence and round match the validator's state.
- Validator broadcasts
PREPAREmessage to other validators.
PRE-PREPARED->PREPARED:- Validator receives
2F + 1of validPREPAREmessages to enterPREPAREDstate. Valid messages conform to the following conditions:- Matched sequence and round.
- Matched block hash.
- Messages are from known validators.
- Validator broadcasts
COMMITmessage upon enteringPREPAREDstate.
- Validator receives
PREPARED->COMMITTED:- Validator receives
2F + 1of validCOMMITmessages to enterCOMMITTEDstate. Valid messages conform to the following conditions:- Matched sequence and round.
- Matched block hash.
- Messages are from known validators.
- Validator receives
COMMITTED->FINAL COMMITTED:- Validator appends
2F + 1commitment signatures toextraDataand tries to insert the block into the blockchain. - Validator enters
FINAL COMMITTEDstate when insertion succeeds.
- Validator appends
FINAL COMMITTED->NEW ROUND:- Validators pick a new proposer and starts a new round timer.
Round change flow
- There are three conditions that would trigger
ROUND CHANGE:- Round change timer expires.
- Invalid
PREPREPAREmessage. - Block insertion fails.
- When a validator notices that one of the above conditions applies, it broadcasts a
ROUND CHANGEmessage along with the proposed round number and waits forROUND CHANGEmessages from other validators. The proposed round number is selected based on following condition:- If the validator has received
ROUND CHANGEmessages from its peers, it picks the largest round number which hasF + 1ofROUND CHANGEmessages. - Otherwise, it picks
1 + current round numberas the proposed round number.
- If the validator has received
- Whenever a validator receives
F + 1ofROUND CHANGEmessages on the same proposed round number, it compares the received one with its own. If the received is larger, the validator broadcastsROUND CHANGEmessage again with the received number. - Upon receiving
2F + 1ofROUND CHANGEmessages on the same proposed round number, the validator exits the round change loop, calculates the new proposer, and then entersNEW ROUNDstate. - Another condition that a validator jumps out of round change loop is when it receives verified block(s) through peer synchronization.
Proposer selection
Currently we support two policies: round robin and sticky proposer.
- Round robin: in a round robin setting, proposer will change in every block and round change.
- Sticky proposer: in a sticky proposer setting, propose will change only when a round change happens.
Validator list voting
We use a similar validator voting mechanism as Clique and copy most of the content from Clique EIP. Every epoch transaction resets the validator voting, meaning if an authorization or de-authorization vote is still in progress, that voting process will be terminated.
For all transactions blocks:
- Proposer can cast one vote to propose a change to the validators list.
- Only the latest proposal per target beneficiary is kept from a single validator.
- Votes are tallied live as the chain progresses (concurrent proposals allowed).
- Proposals reaching majority consensus
VALIDATOR_LIMITcome into effect immediately. - Invalid proposals are not to be penalized for client implementation simplicity.
- A proposal coming into effect entails discarding all pending votes for that proposal (both for and against) and starts with a clean slate.
Future message and backlog
In an asynchronous network environment, one may receive future messages which cannot be processed in the current state. For example, a validator can receive COMMIT messages on NEW ROUND. We call this kind of message a "future message." When a validator receives a future message, it will put the message into its backlog and try to process later whenever possible.
Optimization
To speed up the consensus process, a validator that received 2F + 1 of COMMIT messages prior to receiving 2F + 1 of PREPARE message will jump to the COMMITTED state so that it is not necessary to wait for further PREPARE messages.
Constants
We define the following constants:
EPOCH_LENGTH: Number of blocks after which to checkpoint and reset the pending votes.- Suggested
30000for the testnet to remain analogous to the main netethashepoch.
- Suggested
REQUEST_TIMEOUT: Timeout for each consensus round before firing a round change in millisecond.BLOCK_PERIOD: Minimum timestamp difference in seconds between two consecutive blocks.PROPOSER_POLICY: Proposer selection policy, defaults to round robin.ISTANBUL_DIGEST: Fixed magic number0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365ofmixDigestin block header for Istanbul block identification.DEFAULT_DIFFICULTY: Default block difficulty, which is set to0x0000000000000001.EXTRA_VANITY: Fixed number of extra-data prefix bytes reserved for proposer vanity.- Suggested
32 bytesto retain the current extra-data allowance and/or use.
- Suggested
NONCE_AUTH: Magic nonce number0xffffffffffffffffto vote on adding a validator.NONCE_DROP: Magic nonce number0x0000000000000000to vote on removing a validator.UNCLE_HASH: AlwaysKeccak256(RLP([]))as uncles are meaningless outside of PoW.PREPREPARE_MSG_CODE: Fixed number0. Message code forPREPREPAREmessage.COMMIT_MSG_CODE: Fixed number1. Message code forCOMMITmessage.ROUND_CHANGE_MSG_CODE: Fixed number2. Message code forROUND CHANGEmessage.
We also define the following per-block constants:
BLOCK_NUMBER: Block height in the chain, where the height of the genesis block is 0.N: Number of authorized validators.F: Number of allowed faulty validators.VALIDATOR_INDEX: Index of the block validator in the sorted list of current authorized validators.VALIDATOR_LIMIT: Number of validators to pass an authorization or de-authorization proposal.- Must be
floor(N / 2) + 1to enforce majority consensus on a chain.
- Must be
Block header
We didn't invent a new block header for Istanbul BFT. Instead, we follow Clique in repurposing the ethash header fields as follows:
-
beneficiary: Address to propose modifying the list of validator with.- Should be filled with zeroes normally, modified only while voting.
- Arbitrary values are permitted nonetheless (even meaningless ones such as voting out non validators) to avoid extra complexity in voting mechanics implementation.
-
nonce: Proposer proposal regarding the account defined by the beneficiary field.- Should be
NONCE_DROPto propose deauthorizing beneficiary as a existing validator. - Should be
NONCE_AUTHto propose authorizing beneficiary as a new validator. - Must be filled with zeroes,
NONCE_DROPorNONCE_AUTH
- Should be
-
mixHash: Fixed magic number0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365for Istanbul block identification. -
ommersHash: Must beUNCLE_HASHas uncles are meaningless outside of PoW. -
timestamp: Must be at least the parent timestamp +BLOCK_PERIOD -
difficulty: Must be filled with0x0000000000000001. -
extraData: Combined field for signer vanity and RLP encoded Istanbul extra data, where Istanbul extra data contains validator list, proposer seal, and commit seals. Istanbul extra data is defined as follows:type IstanbulExtra struct { Validators []common.Address //Validator addresses Seal []byte //Proposer seal 65 bytes CommittedSeal [][]byte //Committed seal, 65 * len(Validators) bytes }
Thus the
extraDatawould be in the form ofEXTRA_VANITY | ISTANBUL_EXTRAwhere|represents a fixed index to separate vanity and Istanbul extra data (not an actual character for separator).- First
EXTRA_VANITYbytes (fixed) may contain arbitrary proposer vanity data. ISTANBUL_EXTRAbytes are the RLP encoded Istanbul extra data calculated fromRLP(IstanbulExtra), whereRLP()is RLP encoding function, andIstanbulExtrais the Istanbul extra data.Validators: The list of validators, which must be sorted in ascending order.Seal: The proposer's signature sealing of the header.CommittedSeal: The list of commitment signature seals as consensus proof.
- First
Block hash, proposer seal, and committed seals
The Istanbul block hash calculation is different from the ethash block hash calculation due to the following reasons:
- The proposer needs to put proposer seal in
extraDatato prove the block is signed by the chosen proposer. - The validators need to put
2F + 1of committed seals as consensus proof inextraDatato prove the block has gone through consensus.
The calculation is still similar to the ethash block hash calculation, with the exception that we need to deal with extraData. We calculate the fields as follows:
Proposer seal calculation
By the time of proposer seal calculation, the committed seals are still unknown, so we calculate the seal with those unknowns empty. The calculation is as follows:
Proposer seal:SignECDSA(Keccak256(RLP(Header)), PrivateKey)PrivateKey: Proposer's private key.Header: Same asethashheader only with a differentextraData.extraData:vanity | RLP(IstanbulExtra), where in theIstanbulExtra,CommittedSealandSealare empty arrays.
Block hash calculation
While calculating block hash, we need to exclude committed seals since that data is dynamic between different validators. Therefore, we make CommittedSeal an empty array while calculating the hash. The calculation is:
Header: Same as ethash header only with a differentextraData.extraData:vanity | RLP(IstanbulExtra), where in theIstanbulExtra,CommittedSealis an empty array.
Consensus proof
Before inserting a block into the blockchain, each validator needs to collect 2F + 1 of committed seals from other validators to compose a consensus proof. Once it receives enough committed seals, it will fill the CommittedSeal in IstanbulExtra, recalculate the extraData, and then insert the block into the blockchain. Note that since committed seals can differ by different sources, we exclude that part while calculating the block hash as in the previous section.
Committed seal calculation:
Committed seal is calculated by each of the validator signing the hash along with COMMIT_MSG_CODE message code of its private key. The calculation is as follows:
Committed seal:SignECDSA(Keccak256(CONCAT(Hash, COMMIT_MSG_CODE)), PrivateKey).CONCAT(Hash, COMMIT_MSG_CODE): Concatenate block hash andCOMMIT_MSG_CODEbytes.PrivateKey: Signing validator's private key.
Block locking mechanism
Locking mechanism is introduced to resolve safety issues. In general, when a proposer is locked at certain height H with a block B, it can only propose B for height H. On the other hand, when a validator is locked, it can only vote on B for height H.
Lock
A lock Lock(B, H) contains a block and its height, which means its belonging validator is currently locked at certain block B and height H. In the following, we also use + to denote more than and - to denote less than. For example +2/3 validators denotes more than two-thirds of validators, while -1/3 validators denotes less than one-third of validators.
Lock and unlock
- Lock: A validator is locked when it receives
2F + 1PREPAREmessages on a blockBat heightH. - Unlock: A validator is unlocked at height
Hand blockBwhen it fails to insert blockBto blockchain.
Protocol (+2/3 validators are locked with Lock(B,H))
-
PRE-PREPARE:- Proposer:
- Case 1, proposer is locked: Broadcasts
PRE-PREPAREonB, and entersPREPAREDstate. - Case 2, proposer is not locked: Broadcasts
PRE-PREPAREon blockB'.
- Case 1, proposer is locked: Broadcasts
- Validator:
- Case 1, received
PRE-PREPAREon existing block: Ignore.- Note: It will eventually lead to a round change, and the proposer will get the old block through synchronization.
- Case 2, validator is locked:
- Case 2.1, received
PRE-PREPAREonB: BroadcastsPREPAREonB. - Case 2.2, received
PRE-PREPAREonB': BroadcastsROUND CHANGE.
- Case 2.1, received
- Case 3, validator is not locked:
- Case 3.1, received
PRE-PREPAREonB: BroadcastsPREPAREonB. - Case 3.2, received
PRE-PREPAREonB': BroadcastsPREPAREonB'.- Note: This consensus round will eventually get into round change since
+2/3are locked atBand which would lead to round change.
- Note: This consensus round will eventually get into round change since
- Case 3.1, received
- Case 1, received
- Proposer:
-
PREPARE:- Case 1, validator is locked:
- Case 1.1, received
PREPAREonB: BroadcastsCOMMITonB, and entersPREPAREDstate.- Note: This shouldn't happen though, it should have skipped this step and entered
PREPAREDinPRE-PREPAREstage.
- Note: This shouldn't happen though, it should have skipped this step and entered
- Case 1.2, received
PREPAREonB': Ignore.- Note: There shouldn't be
+1/3PREPAREonB'since+2/3are locked atB. Thus the consensus round onB'will cause round change. Validator cannot broadcastROUND CHANGEdirectly here since thisPREPAREmessage can possibly from a faulty node.
- Note: There shouldn't be
- Case 1.1, received
- Case 2, validator is not locked:
- Case 2.1, received
PREPAREonB: Waits for2F + 1PREPAREmessages onB.- Note: Most likely it will receive
2F + 1COMMITmessages prior to receiving2F + 1PREPAREmessages since there are+2/3validators being locked atB. In this case, it will jump toCOMMITTEDstate directly.
- Note: Most likely it will receive
- Case 2.2, received
PREPAREonB': Waits for2F + 1PREPAREmessage onB'.- Note: This consensus will eventually get into round change since
+2/3validators are locked onBand which would lead to round change.
- Note: This consensus will eventually get into round change since
- Case 2.1, received
- Case 1, validator is locked:
-
COMMIT:- Validator must be locked:
- Case 1, received
COMMITonB: Waits for2F + 1COMMITmessages. - Case 2, received
COMMITonB': Shouldn't happen.
- Case 1, received
- Validator must be locked:
Locking cases
-
Round change:
- Case 1,
+2/3are locked:- If proposer is locked, it'd propose
B. - Else it'd propose
B', but which will lead to another round change. - Conclusion: eventually
Bwill be committed by honest validators.
- If proposer is locked, it'd propose
- Case 2,
+1/3 ~ 2/3are locked:- If proposer is locked, it'd propose
B. - Else it'd propose
B'. However, since+1/3are locked atB, no validators can ever receive2F + 1PREPAREonB', meaning no validators can be locked atB'. Also those+1/3locked validators will not response toB'and eventually lead to round change. - Conclusion: eventually
Bwill be committed by honest validators.
- If proposer is locked, it'd propose
- Case 3,
-1/3are locked:- If propose is locked, it'd propose
B. - Else it'd propose
B'. If+2/3reach consensus onB', those locked-1/3will getB'through synchronization and move to next height. Otherwise, there will be another round change. - Conclusion: it can be
Bor other blockB'be finally committed.
- If propose is locked, it'd propose
- Case 1,
-
Round change caused by insertion failure:
- It will fall in one of the above round change cases.
- If the block is actually bad (cannot be inserted to blockchain), eventually
+2/3validators will unlock blockBatHand try to propose a new blockB'. - If the block is good (can be inserted to blockchain), then it would still be one of the above round change cases.
- If the block is actually bad (cannot be inserted to blockchain), eventually
- It will fall in one of the above round change cases.
-
-1/3validators insert the block successfully, but others successfully trigger round change, meaning+1/3are still locked atLock(B,H)- Case 1, proposer has inserted
B: Proposer will proposeB'atH', but+1/3are locked atB, soB'won't pass the consensus, which will eventually lead to round change. Other validators will either perform consensus onBor getBthrough synchronization. - Case 2, proposer hasn't inserted
B:- Case 2.1, proposer is locked: Proposer proposes
B. - Case 2.2, proposer is not locked: Proposer will propose
B'atH. The rest is the same as above case 1.
- Case 2.1, proposer is locked: Proposer proposes
- Case 1, proposer has inserted
-
+1/3validators insert the block successfully,-2/3are trying to trigger round change atH.- Case 1, proposer has inserted
B: Proposer will proposeB'atH', but won't pass the consensus until+1/3getBthrough synchronization. - Case 2, proposer has not inserted
B:- Case 2.1, proposer is locked: Proposer proposes
B. - Case 2.2, proposer is not locked: Proposer proposes
B'atH. The rest is the same as above case 1.
- Case 2.1, proposer is locked: Proposer proposes
- Case 1, proposer has inserted
-
+2/3validators insert the block successfully,-1/3are trying to trigger round change atH.- Case 1, proposer has inserted
B: proposer will proposeB'atH', which may lead to a successful consensus. Then those-1/3need to getBthrough synchronization. - Case 2, proposer has not inserted
B:- Case 2.1, proposer is locked: Proposer proposes
B. - Case 2.2, proposer is not locked: Proposer proposes
B'atH. Since+2/3haveBatHalready, this round would cause round change.
- Case 2.1, proposer is locked: Proposer proposes
- Case 1, proposer has inserted
Gossip network
Traditionally, validators need to be strongly connected in order to reach stable consensus results, which means all validators need to be connected directly to each other; however, in practical network environment, stable and constant p2p connections are hard to achieve. To resolve this issue, Istanbul BFT implements gossip network to overcome this constrain. In a gossip network environment, all validators only need to be weakly connected, which means any two validators are seen connected when either they are directly connected or they are connected with one or more validators in between. Consensus messages will be relayed between validators.
How to run
Running Istanbul BFT validators and nodes is similar to running the official node in a private chain. First of all, you need to initialize the data folder as:
geth --datadir "/eth" init "/eth/genesis.json"
Then,
for validators:
geth --datadir "/eth" --mine --minerthreads 1 --syncmode "full"
for regular nodes:
geth --datadir "/eth"
Note on syncmode:
--syncmode "full" is required for the first set of validators to initialize a new network. Since we are using fetcher to insert blocks, if we don't set it to full mode, the fetcher cannot insert the first block. Please refer the following code in eth/handler.go.
inserter := func(blocks types.Blocks) (int, error) {
// If fast sync is running, deny importing weird blocks
if atomic.LoadUint32(&manager.fastSync) == 1 {
log.Warn("Discarded bad propagated block", "number", blocks[0].Number(), "hash", blocks[0].Hash())
return 0, nil
}
atomic.StoreUint32(&manager.acceptTxs, 1) // Mark initial sync done on any fetcher import
return manager.blockchain.InsertChain(blocks)
}The sync mode affects only if there are some existing blocks, so there is no impact for initializing a new network.
For the later joined validators, we don't need to use full mode as they can get blocks by downloader. After the first sync from peers, they will automatically switch to full mode.
Command line options
$geth help
ISTANBUL OPTIONS:
--istanbul.requesttimeout value Timeout for each Istanbul round in milliseconds (default: 10000)
--istanbul.blockperiod value Default minimum difference between two consecutive block's timestamps in seconds (default: 1)
Nodekey and validator
To be a validator, a node needs to meet the following conditions:
- Its account (the address derived from its nodekey) needs to be listed in
extraData's validators section. - Use its nodekey as its private key to sign consensus messages.
genesis.json
To run the Istanbul BFT chain, the config field is required, and the pbft subfield must present. Example as the following:
{
"config": {
"chainId": 2016,
"istanbul": {
"epoch": 30000,
"policy" 0,
}
},
"timestamp": "0x0",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000f89af85494475cc98b5521ab2a1335683e7567c8048bfe79ed9407d8299de61faed3686ba4c4e6c3b9083d7e2371944fe035ce99af680d89e2c4d73aca01dbfc1bd2fd94dc421209441a754f79c4a4ecd2b49c935aad0312b8410000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000c0",
"gasLimit": "0x47e7c4",
"mixhash": "0x63746963616c2062797a616e74696e65206661756c7420746f6c6572616e6365",
"coinbase": "0x3333333333333333333333333333333333333333",
"nonce": "0x0",
"difficulity": "0x0",
"alloc": {}
}extraData tools
We've create a set of extraData coding tools in istanbul-tools repository to help developers to manually generate genesis.json.
Encoding:
Before encoding you need to define a toml file with vanity and validators fields to define proposer vanity and validator set. Please refer to example.toml for the example. The output would be a hex string which can be put into extraData field directly.
Command:
istanbul encode --config ./config.toml
Decoding:
Use --extradata option to give the extraData hex string. The output would show the following if presents: vanity, validator set, seal, and committed seal.
Command:
istanbul decode --extradata <EXTRA_DATA_HEX_STRING>
Ottoman testnet
We have setup a testnet for public testing. There are initially 4 validators and no designated faulty nodes. In the future, we want to extend it to 22 validators and setup few faulty nodes amongst them.
Run testnet node
geth --ottoman
Faulty node
We have implemented a simple faulty node that can make a validator run faulty behaviors during consensus. There are five behaviors included in this implementation:
NotBroadcast: The validator doesn't broadcast any message.SendWrongMsg: The validator sends out messages with wrong message codes.ModifySig: The validator modifies the message signatures.AlwaysPropose: The validator always sends out proposals.AlwaysRoundChange: The validator always sendsROUND CHANGEwhile receiving messages.BadBlock: The validator proposes a block with bad body
Run following command to enable faulty node:
geth --istanbul.faultymode <MODE>
Where the <MODE> can be the following number:
0: Disable faulty behaviors.1: Randomly run any faulty behaviors.2:NotBroadcast.3:SendWrongMsg.4:ModifySig.5:AlwaysPropose.6:AlwaysRoundChange.7:BadBlock.
Background
The idea of implementing a byzantine fault tolerance (BFT) consensus came from the challenges we faced while building blockchain solutions for banks. We chose ethereum as the baseline protocol mostly because of its smart contract capability. However, the built-in consensus, proof of work or ethash, is not the ideal choice when settlement finality and minimum latency is required.
Banking systems tend to form a private chain or consortium chain to run their applications. PBFT is ideal for these settings. These environments require a higher degree of manageability and higher throughput. In terms of scalability, validator scalability is not required. Many of the decentralization benefits of PoW in public chains become drawbacks in a private/consortium chain. On the other hand, designated validators in a PBFT environment maps well to private/consortium chains.
Remaining Tasks
- Testnet: Currently the Ottoman testnet only has 4 validators. We'd like to extend it to 22 validator nodes and setup some faulty nodes amongst them (fewer than 7 faulty nodes).
- Weighted round robin: This will require a redesign of the
extraDatafield, but should be fairly straightforward. - Remove or make block period configurable: In certain setups, it may make sense to generate as many blocks as possible. Currently, the default value is 1 second. To remove this limitation, we will also need to adjust the original
worker.gocode. - Benchmarking and stress testing:
- Validator scalability.
- Node scalability.
- Transaction per second.
- Smarter way to detect faulty proposer: A proposer can always generate empty blocks or small blocks without being acting faulty; however, this would impact the throughput of the network. We need to design better round change criteria to take into consideration those kind of performance related faulty behaviors.
- Formal proof of safety and liveness.
Notes and discussions
Does it still make sense to use gas?
Yes. We still need gas to prevent infinite loops and any kind of EVM exhaustion.
Does it make sense to charge gas in a consortium chain?
The network would be vulnerable if every account has unlimited gas or unlimited transaction sending power. However, to enable so, one can run all validators with gas price flag --gasprice 0 to accept gas price at zero.
Put consensus proof in the next block?
Currently our block header can be varied in extraData depending on its source validator because of the need to put consensus proof in the block header (by each validator). One way to resolve this is to put the proof in the next block. Therefore, in the proposing stage, the proposer can select 2F + 1 of commitment signatures of the previous block and put them in the current proposed block header. However, it would require each block to have one confirmation to reach finality (not instant finality).
Proof of lock
Inspired by Tendermint. We are still considering whether to add it to this EIP. Further efficiency benefits can be realized by reusing a current proposed block in a round change situation.
Contribution
The work was initiated and open sourced by the Amis team. We're looking for developers around the world to contribute. Please feel free to contact us:
Forked repository (and original implementation branch)
https://github.com/getamis/go-ethereum/tree/feature/pbft
Clarifications and feedback
TBD
