Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[r4r]Introduce Casper FFG based Fast finality onto BSC #255

Merged
merged 15 commits into from
Apr 7, 2023

Conversation

buddh0
Copy link
Contributor

@buddh0 buddh0 commented Mar 23, 2023

Summary

This PR tries to implement fast finality mechanism introduced by BEP-126.

Abstract

BEP-126 Proposal describes a fast finality mechanism to finalize a block, once the block has been finalized, it won't be
reverted forever.

It takes several steps to finalize a block:

  1. A block is proposed by a validator and propagated to other validators.
  2. Validators use their BLS private key to sign for the block as a vote message.
  3. Gather the votes from validators into a pool.
  4. Aggregate the BLS signature if its direct parent block has gotten enough votes when proposing a new block.
  5. Set the aggregated vote attestation into the extra field of the new block's header.
  6. Validators and full nodes who received the new block with the direct parent block's attestation can justify the direct parent block.
  7. If there are two continuous blocks have been justified, then the previous one is finalized.

The finality of a block can be achieved within two blocks in most cases, this is expected to reduce the chance
of chain re-organization and stabilize the block producing further.

Motivation

Finality is critical for blockchain security, once the block is finalized, it would not be reverted anymore. The fast
finality feature is very useful, users can make sure they get the accurate information from the latest finalized block,
then they can decide what to do next instantly.

Currently, on BNB Smart chain, all the full nodes and validators need to wait until enough blocks have been produced
to ensure a probabilistic finality. According to the whitepaper, with 21 validators, full nodes and validators can
wait ⅔*21+1=15 blocks to ensure a relatively secure finality, it would be quite long time for some critical applications.

Specification

The vote message is signed by validators’ private key through the BLS algorithm, however the BLS private and public keys are different from traditional BSC/Ethereum account’s private and public keys, so use a new key manager to manage BLS related keys.

In order to collect the vote information, we use another vote pool to gather the votes from validators, this vote pool is similar to txpool but independent.

Validators can vote for blocks they received under the above three rules. Once the parent block’s votes in the vote pool are more than ⅔+ validators, the validator will aggregate the votes message and write them to the proposed block’s header when it proposes new blocks.

The block receivers, either full nodes or non-proposer validators, download the block from p2p node, verify the votes from valid validators and verify the BLS aggregated signature if exist, insert the block into the blockchain, write the new attestation info to the consensus snapshot, distribute reward in finalize function if reward requirements meet.

The slash related function is implemented in a stand alone slash contract, anyone can get a vote message from the vote pool, once they find the malicious behavior, they can submit the assembled evidence to slash contract, and slash contract will do the detailed verification and slash actions. The evidence format, verification procedure and slash actions will be designed in slash component design.

BLS and BLS Key Manager

BLS is used to sign the vote for fast finality, aggregate the votes from different validators, verify the signature from different validators and verify the aggregated signatures, in order to format the vote data, in our design, we sign the hash of the vote data, not the data itself.

Since the BLS private key and public key are different from current Ethereum accounts’ key, we introduce another key manager to manage the BLS private key and public key. The BLS key manager is designed to store the BLS private key, since the private key shouldn’t be stored as plain text to avoid leaking, we introduce BLS wallet, BLS key manager and BLS accounts here. Each account is a pair of BLS private key and BLS public key, it will be stored as an encrypted keystore Json file. There will be a singleton wallet while running the Geth client, the wallet can manage several BLS accounts, actually, we just need one account in general. Users can create a new account or import existing Json keystore accounts.

The BLS wallet is bound with a singleton key manager, through the key manager, we can get all the public keys in the BLS wallet, and once the validator needs to sign a vote, call the key manager’s sign method.

Vote Pool

The votepool is used to gather votes from different validators, propagating votes to other nodes. All active validators can vote for the fast finality of blocks under the above three rules, the vote data will be {SourceNumber, SourceHash, TargetNumber, TargetHash}. Validators use their BLS private key to sign the hash of the vote data, so the vote message is {BLSPubKey, {SourceNumber, SourceHash, TargetNumber, TargetHash}, Signature}, once the receivers see the vote message, they can verify it easily.

As we know propagating messages is the duty of P2P protocols, in order to propagate the vote message, we will define new message types based on current ETH67 protocol.

In order to finalize blocks, the miner should assemble the vote messages for the direct parent block once its valid votes have more than ⅔+ validators, and write them to the block header as an attestation. The assembled signature should be {ValidatorsIndexBitmap, {SourceNumber, SourceHash, TargetNumber, TargetHash}, AggregatedSignature}, the bitmap indicates the validators that voted for the block, aggregate their vote signatures into aggregated signature, the aggregated signature is the same length with each common signature.

Once the validators or full nodes receive a block, they should verify the block header, for this fast finality feature, they should verify the assembled attestation information.
As the consensus engine can get validators’ information from the snapshot, the BLS public key should be recorded as a part of validator, through the ValidatorsIndexBitmap we can easily get their BLS public keys, then we can use the BLS FastAggregateVerify function to verify the aggregated signature.

Reward

Distributing reward should be a part of the consensus engine, in order to make the consensus lighter, we would distribute the reward by batch, once an epoch. As the vote attestation has been recorded in the headers, so we can iterate the headers from the first block to the last block of the previous epoch, find out the attested blocks and which validators have voted for them.

Through the iteration, we can get two arrays {validators[], accumulatedWeights[]}, then call the distributeFinalityReward method of ValidatorSet contract and pass these two parameters.

The total reward for validators of this epoch would be a part balance of the SystemReward contract, the partial would be set to 10% initially and can be governed. Once we get the total reward, then distribute the total reward to each validator by the accumulatedWeights.

Slash

Anyone can get validators’ vote message from vote pool, if they find some validators’ vote message violate one of the rules described in BEP-126, they can assemble the evidence and submit to the slash contract, slash contract will verify the submitted evidence, once the evidence has been verified, the malicious validator will be slashed and the submitter will be rewarded.

pythonberg1997 and others added 11 commits March 23, 2023 18:37
* [WIP]Fast Finality: reward distribution and slash parts

* Update codes

* Fix review comments and update test

* Fix code errors related contract SystemReward and BSCValidatorSet

* Fix code errors related invalid opcode and add new api

* Optimize gasused of updateValidator and update test

* Optimize gasused of updateValidator and update test

* Minor error fixed

* Fix review comments and update scripts

* Fix init issue

* Fix external view issue and update test

* Fix review comments
@buddh0 buddh0 changed the base branch from master to develop March 23, 2023 12:19
@pythonberg1997 pythonberg1997 changed the title Fast finality rebase [r4r]Introduce Casper FFG based Fast finality onto BSC Mar 29, 2023

function updateParam(string calldata key, bytes calldata value) onlyGov external override {
if (Memory.compareStrings(key, "addOperator")) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

@pythonberg1997 is it possible that when BC hard fork happen, it can generate a cross-chain package automatically? (so we do not have to gov again)

Copy link
Collaborator

Choose a reason for hiding this comment

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

Sure

@@ -54,7 +55,7 @@ contract BSCValidatorSet is IBSCValidatorSet, System, IParamSubscriber, IApplica

uint256 public constant BURN_RATIO_SCALE = 10000;
address public constant BURN_ADDRESS = 0x000000000000000000000000000000000000dEaD;
uint256 public constant INIT_BURN_RATIO = 0;
uint256 public constant INIT_BURN_RATIO = 1000;
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the purpose to change this?

Copy link
Collaborator

Choose a reason for hiding this comment

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

1000 is the mainnet's setup. It's for unit tests.

@@ -74,7 +75,13 @@ contract BSCValidatorSet is IBSCValidatorSet, System, IParamSubscriber, IApplica
uint256 public maxNumOfCandidates;
uint256 public maxNumOfWorkingCandidates;

struct Validator{
// BEP-126 Fast Finality
uint256 public constant INIT_FINALITY_REWARD_RATIO = 10;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let us lower the ratio?

Copy link
Contributor

@NathanBSC NathanBSC Apr 4, 2023

Choose a reason for hiding this comment

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

epoch n system reward balance blance(n)
epoch n+1 system reward balance blance(n+1)
if blance(n+1)>maxSystemBalance
reward = 1% * blance(n+1)
else if blance(n+1)>blance(n)
reward = 50% * (blance(n+1)-blance(n))
else if blance(n+1)<blance(n)
reward =0

// reserve for future use
uint256[20] slots;
uint256[19] slots;
Copy link
Contributor

Choose a reason for hiding this comment

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

What is the reason for this change?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Because we add a new field, so the placeholder should be reduced by one

if (balanceOfSystemReward > MAX_SYSTEM_REWARD_BALANCE) {
totalValue = balanceOfSystemReward/100;
} else if (balanceOfSystemReward > previousBalanceOfSystemReward) {
totalValue = (balanceOfSystemReward - previousBalanceOfSystemReward)*finalityRewardRatio/100;
Copy link
Collaborator

@unclezoro unclezoro Apr 6, 2023

Choose a reason for hiding this comment

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

Please use safe math?

Copy link
Collaborator

Choose a reason for hiding this comment

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

It is possible that totalValue is zero, if it is zero, no need to continue.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Done

} else if (Memory.compareStrings(key, "finalityRewardRatio")) {
require(value.length == 32, "length of finalityRewardRatio mismatch");
uint256 newFinalityRewardRatio = BytesToTypes.bytesToUint256(32, value);
require(newFinalityRewardRatio >= 1 && newFinalityRewardRatio < 100, "the finalityRewardRatio is out of range");
Copy link
Contributor

Choose a reason for hiding this comment

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

100 is a legal value?

@pythonberg1997 pythonberg1997 merged commit a74f320 into bnb-chain:develop Apr 7, 2023
unclezoro pushed a commit that referenced this pull request Apr 20, 2023
* [WIP]Fast Finality: reward distribution and slash parts (#8)

* Update codes

* Fix review comments and update test

* Fix code errors related contract SystemReward and BSCValidatorSet

* Fix code errors related invalid opcode and add new api

* Optimize gasused of updateValidator and update test

* Minor error fixed

* Fix review comments and update scripts

* Fix init issue

* Fix external view issue and update test

* Fix review comments

* Update for new fast finality rules and related issues

* Revert code format changes

* Resolve conflict

* Update contracts' abi

* Update finality reward related events

* Fix some audit comments

* resolve conflict and adapt to new precompile bls verify contract

* fix: recover deleted verifyBLSSignature function call

* fix: revert modification for local unit test

* fix: errors with template

* fix rebase errors

* update contract abi

* update finality reward distribution logic

* update reward calculation using SafeMath lib

---------

Co-authored-by: Roshan <[email protected]>
unclezoro pushed a commit that referenced this pull request Apr 20, 2023
* [WIP]Fast Finality: reward distribution and slash parts (#8)

* Update codes

* Fix review comments and update test

* Fix code errors related contract SystemReward and BSCValidatorSet

* Fix code errors related invalid opcode and add new api

* Optimize gasused of updateValidator and update test

* Minor error fixed

* Fix review comments and update scripts

* Fix init issue

* Fix external view issue and update test

* Fix review comments

* Update for new fast finality rules and related issues

* Revert code format changes

* Resolve conflict

* Update contracts' abi

* Update finality reward related events

* Fix some audit comments

* resolve conflict and adapt to new precompile bls verify contract

* fix: recover deleted verifyBLSSignature function call

* fix: revert modification for local unit test

* fix: errors with template

* fix rebase errors

* update contract abi

* update finality reward distribution logic

* update reward calculation using SafeMath lib

---------

Co-authored-by: Roshan <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants