-
Notifications
You must be signed in to change notification settings - Fork 1.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: add new fork time and precompile contracts
- Loading branch information
1 parent
cd0356b
commit 5365b22
Showing
19 changed files
with
4,616 additions
and
1,804 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,232 @@ | ||
package parlia | ||
|
||
import ( | ||
"container/heap" | ||
"context" | ||
"fmt" | ||
"math" | ||
"math/big" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/common/hexutil" | ||
"github.com/ethereum/go-ethereum/core" | ||
"github.com/ethereum/go-ethereum/core/state" | ||
"github.com/ethereum/go-ethereum/core/systemcontracts" | ||
"github.com/ethereum/go-ethereum/core/types" | ||
"github.com/ethereum/go-ethereum/internal/ethapi" | ||
"github.com/ethereum/go-ethereum/log" | ||
"github.com/ethereum/go-ethereum/rpc" | ||
) | ||
|
||
// initializeFusionContract initialize new contracts of fusion fork | ||
func (p *Parlia) initializeFusionContract(state *state.StateDB, header *types.Header, chain core.ChainContext, | ||
txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool, | ||
) error { | ||
// method | ||
method := "initialize" | ||
|
||
// initialize contracts | ||
contracts := []string{ | ||
systemcontracts.StakeHubContract, | ||
systemcontracts.GovernorContract, | ||
systemcontracts.GovTokenContract, | ||
systemcontracts.TimelockContract, | ||
} | ||
// get packed data | ||
data, err := p.stakeHubABI.Pack(method) | ||
if err != nil { | ||
log.Error("Unable to pack tx for initialize fusion contracts", "error", err) | ||
return err | ||
} | ||
for _, c := range contracts { | ||
msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(c), data, common.Big0) | ||
// apply message | ||
log.Info("initialize fusion contract", "block number", header.Number.Uint64(), "contract", c) | ||
err = p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining) | ||
if err != nil { | ||
return err | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
type ValidatorItem struct { | ||
address common.Address | ||
votingPower *big.Int | ||
voteAddress []byte | ||
} | ||
|
||
type BSCValidatorSetValidator struct { | ||
ConsensusAddress common.Address | ||
FeeAddress common.Address | ||
BBCFeeAddress common.Address | ||
VotingPower uint64 | ||
Jailed bool | ||
Incoming *big.Int | ||
} | ||
|
||
// An ValidatorHeap is a max-heap of validator's votingPower. | ||
type ValidatorHeap []ValidatorItem | ||
|
||
func (h *ValidatorHeap) Len() int { return len(*h) } | ||
|
||
func (h *ValidatorHeap) Less(i, j int) bool { | ||
// We want topK validators with max voting power, so we need a max-heap | ||
if (*h)[i].votingPower.Cmp((*h)[j].votingPower) == 0 { | ||
return (*h)[i].address.Hex() < (*h)[j].address.Hex() | ||
} else { | ||
return (*h)[i].votingPower.Cmp((*h)[j].votingPower) == 1 | ||
} | ||
} | ||
|
||
func (h *ValidatorHeap) Swap(i, j int) { (*h)[i], (*h)[j] = (*h)[j], (*h)[i] } | ||
|
||
func (h *ValidatorHeap) Push(x interface{}) { | ||
*h = append(*h, x.(ValidatorItem)) | ||
} | ||
|
||
func (h *ValidatorHeap) Pop() interface{} { | ||
old := *h | ||
n := len(old) | ||
x := old[n-1] | ||
*h = old[0 : n-1] | ||
return x | ||
} | ||
|
||
func (p *Parlia) updateValidatorSetV2(state *state.StateDB, header *types.Header, chain core.ChainContext, | ||
txs *[]*types.Transaction, receipts *[]*types.Receipt, receivedTxs *[]*types.Transaction, usedGas *uint64, mining bool, | ||
) error { | ||
// 1. get all validators and its voting power | ||
blockNr := rpc.BlockNumberOrHashWithHash(header.ParentHash, false) | ||
validatorItems, err := p.getValidatorElectionInfo(blockNr) | ||
if err != nil { | ||
return err | ||
} | ||
maxElectedValidators, err := p.getMaxElectedValidators(blockNr) | ||
if err != nil { | ||
return err | ||
} | ||
|
||
// 2. sort by voting power | ||
eValidators, eVotingPowers, eVoteAddrs := getTopValidatorsByVotingPower(validatorItems, maxElectedValidators) | ||
|
||
// 3. update validator set to system contract | ||
method := "updateValidatorSetV2" | ||
data, err := p.validatorSetABI.Pack(method, eValidators, eVotingPowers, eVoteAddrs) | ||
if err != nil { | ||
log.Error("Unable to pack tx for updateValidatorSetV2", "error", err) | ||
return err | ||
} | ||
|
||
// get system message | ||
msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(systemcontracts.ValidatorContract), data, common.Big0) | ||
// apply message | ||
return p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining) | ||
} | ||
|
||
func (p *Parlia) getValidatorElectionInfo(blockNr rpc.BlockNumberOrHash) ([]ValidatorItem, error) { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() // cancel when we are finished consuming integers | ||
|
||
method := "getValidatorElectionInfo" | ||
toAddress := common.HexToAddress(systemcontracts.StakeHubContract) | ||
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2)) | ||
|
||
data, err := p.stakeHubABI.Pack(method, big.NewInt(0), big.NewInt(0)) | ||
if err != nil { | ||
log.Error("Unable to pack tx for getValidatorElectionInfo", "error", err) | ||
return nil, err | ||
} | ||
msgData := (hexutil.Bytes)(data) | ||
|
||
result, err := p.ethAPI.Call(ctx, ethapi.TransactionArgs{ | ||
Gas: &gas, | ||
To: &toAddress, | ||
Data: &msgData, | ||
}, blockNr, nil, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
var validators []common.Address | ||
var votingPowers []*big.Int | ||
var voteAddrs [][]byte | ||
var totalLength *big.Int | ||
if err := p.stakeHubABI.UnpackIntoInterface(&[]interface{}{&validators, &votingPowers, &voteAddrs, &totalLength}, method, result); err != nil { | ||
return nil, err | ||
} | ||
if totalLength.Int64() != int64(len(validators)) || totalLength.Int64() != int64(len(votingPowers)) || totalLength.Int64() != int64(len(voteAddrs)) { | ||
return nil, fmt.Errorf("validator length not match") | ||
} | ||
|
||
validatorItems := make([]ValidatorItem, len(validators)) | ||
for i := 0; i < len(validators); i++ { | ||
validatorItems[i] = ValidatorItem{ | ||
address: validators[i], | ||
votingPower: votingPowers[i], | ||
voteAddress: voteAddrs[i], | ||
} | ||
} | ||
|
||
return validatorItems, nil | ||
} | ||
|
||
func (p *Parlia) getMaxElectedValidators(blockNr rpc.BlockNumberOrHash) (maxElectedValidators *big.Int, err error) { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() // cancel when we are finished consuming integers | ||
|
||
method := "maxElectedValidators" | ||
toAddress := common.HexToAddress(systemcontracts.StakeHubContract) | ||
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2)) | ||
|
||
data, err := p.stakeHubABI.Pack(method) | ||
if err != nil { | ||
log.Error("Unable to pack tx for maxElectedValidators", "error", err) | ||
return nil, err | ||
} | ||
msgData := (hexutil.Bytes)(data) | ||
|
||
result, err := p.ethAPI.Call(ctx, ethapi.TransactionArgs{ | ||
Gas: &gas, | ||
To: &toAddress, | ||
Data: &msgData, | ||
}, blockNr, nil, nil) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
if err := p.stakeHubABI.UnpackIntoInterface(&maxElectedValidators, method, result); err != nil { | ||
return nil, err | ||
} | ||
|
||
return maxElectedValidators, nil | ||
} | ||
|
||
func getTopValidatorsByVotingPower(validatorItems []ValidatorItem, maxElectedValidators *big.Int) ([]common.Address, []uint64, [][]byte) { | ||
var validatorHeap ValidatorHeap | ||
for i := 0; i < len(validatorItems); i++ { | ||
// only keep validators with voting power > 0 | ||
if validatorItems[i].votingPower.Cmp(big.NewInt(0)) == 1 { | ||
validatorHeap = append(validatorHeap, validatorItems[i]) | ||
} | ||
} | ||
hp := &validatorHeap | ||
heap.Init(hp) | ||
|
||
length := int(maxElectedValidators.Int64()) | ||
if length > len(validatorHeap) { | ||
length = len(validatorHeap) | ||
} | ||
eValidators := make([]common.Address, length) | ||
eVotingPowers := make([]uint64, length) | ||
eVoteAddrs := make([][]byte, length) | ||
for i := 0; i < length; i++ { | ||
item := heap.Pop(hp).(ValidatorItem) | ||
eValidators[i] = item.address | ||
// as the decimal in BNB Beacon Chain is 1e8 and in BNB Smart Chain is 1e18, we need to divide it by 1e10 | ||
eVotingPowers[i] = new(big.Int).Div(item.votingPower, big.NewInt(1e10)).Uint64() | ||
eVoteAddrs[i] = item.voteAddress | ||
} | ||
|
||
return eValidators, eVotingPowers, eVoteAddrs | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
package parlia | ||
|
||
import ( | ||
"math/big" | ||
"testing" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
) | ||
|
||
func TestValidatorHeap(t *testing.T) { | ||
testCases := []struct { | ||
description string | ||
k int64 | ||
validators []ValidatorItem | ||
expected []common.Address | ||
}{ | ||
{ | ||
description: "normal case", | ||
k: 2, | ||
validators: []ValidatorItem{ | ||
{ | ||
address: common.HexToAddress("0x1"), | ||
votingPower: new(big.Int).Mul(big.NewInt(300), big.NewInt(1e10)), | ||
voteAddress: []byte("0x1"), | ||
}, | ||
{ | ||
address: common.HexToAddress("0x2"), | ||
votingPower: new(big.Int).Mul(big.NewInt(200), big.NewInt(1e10)), | ||
voteAddress: []byte("0x2"), | ||
}, | ||
{ | ||
address: common.HexToAddress("0x3"), | ||
votingPower: new(big.Int).Mul(big.NewInt(100), big.NewInt(1e10)), | ||
voteAddress: []byte("0x3"), | ||
}, | ||
}, | ||
expected: []common.Address{ | ||
common.HexToAddress("0x1"), | ||
common.HexToAddress("0x2"), | ||
}, | ||
}, | ||
{ | ||
description: "same voting power", | ||
k: 2, | ||
validators: []ValidatorItem{ | ||
{ | ||
address: common.HexToAddress("0x1"), | ||
votingPower: new(big.Int).Mul(big.NewInt(300), big.NewInt(1e10)), | ||
voteAddress: []byte("0x1"), | ||
}, | ||
{ | ||
address: common.HexToAddress("0x2"), | ||
votingPower: new(big.Int).Mul(big.NewInt(100), big.NewInt(1e10)), | ||
voteAddress: []byte("0x2"), | ||
}, | ||
{ | ||
address: common.HexToAddress("0x3"), | ||
votingPower: new(big.Int).Mul(big.NewInt(100), big.NewInt(1e10)), | ||
voteAddress: []byte("0x3"), | ||
}, | ||
}, | ||
expected: []common.Address{ | ||
common.HexToAddress("0x1"), | ||
common.HexToAddress("0x2"), | ||
}, | ||
}, | ||
{ | ||
description: "zero voting power and k > len(validators)", | ||
k: 5, | ||
validators: []ValidatorItem{ | ||
{ | ||
address: common.HexToAddress("0x1"), | ||
votingPower: new(big.Int).Mul(big.NewInt(300), big.NewInt(1e10)), | ||
voteAddress: []byte("0x1"), | ||
}, | ||
{ | ||
address: common.HexToAddress("0x2"), | ||
votingPower: big.NewInt(0), | ||
voteAddress: []byte("0x2"), | ||
}, | ||
{ | ||
address: common.HexToAddress("0x3"), | ||
votingPower: big.NewInt(0), | ||
voteAddress: []byte("0x3"), | ||
}, | ||
{ | ||
address: common.HexToAddress("0x4"), | ||
votingPower: big.NewInt(0), | ||
voteAddress: []byte("0x4"), | ||
}, | ||
}, | ||
expected: []common.Address{ | ||
common.HexToAddress("0x1"), | ||
}, | ||
}, | ||
{ | ||
description: "zero voting power and k < len(validators)", | ||
k: 2, | ||
validators: []ValidatorItem{ | ||
{ | ||
address: common.HexToAddress("0x1"), | ||
votingPower: new(big.Int).Mul(big.NewInt(300), big.NewInt(1e10)), | ||
voteAddress: []byte("0x1"), | ||
}, | ||
{ | ||
address: common.HexToAddress("0x2"), | ||
votingPower: big.NewInt(0), | ||
voteAddress: []byte("0x2"), | ||
}, | ||
{ | ||
address: common.HexToAddress("0x3"), | ||
votingPower: big.NewInt(0), | ||
voteAddress: []byte("0x3"), | ||
}, | ||
{ | ||
address: common.HexToAddress("0x4"), | ||
votingPower: big.NewInt(0), | ||
voteAddress: []byte("0x4"), | ||
}, | ||
}, | ||
expected: []common.Address{ | ||
common.HexToAddress("0x1"), | ||
}, | ||
}, | ||
{ | ||
description: "all zero voting power", | ||
k: 2, | ||
validators: []ValidatorItem{ | ||
{ | ||
address: common.HexToAddress("0x1"), | ||
votingPower: big.NewInt(0), | ||
voteAddress: []byte("0x1"), | ||
}, | ||
{ | ||
address: common.HexToAddress("0x2"), | ||
votingPower: big.NewInt(0), | ||
voteAddress: []byte("0x2"), | ||
}, | ||
{ | ||
address: common.HexToAddress("0x3"), | ||
votingPower: big.NewInt(0), | ||
voteAddress: []byte("0x3"), | ||
}, | ||
{ | ||
address: common.HexToAddress("0x4"), | ||
votingPower: big.NewInt(0), | ||
voteAddress: []byte("0x4"), | ||
}, | ||
}, | ||
expected: []common.Address{}, | ||
}, | ||
} | ||
for _, tc := range testCases { | ||
eligibleValidators, _, _ := getTopValidatorsByVotingPower(tc.validators, big.NewInt(tc.k)) | ||
|
||
// check | ||
if len(eligibleValidators) != len(tc.expected) { | ||
t.Errorf("expected %d, got %d", len(tc.expected), len(eligibleValidators)) | ||
} | ||
for i := 0; i < len(tc.expected); i++ { | ||
if eligibleValidators[i] != tc.expected[i] { | ||
t.Errorf("expected %s, got %s", tc.expected[i].Hex(), eligibleValidators[i].Hex()) | ||
} | ||
} | ||
} | ||
} |
Oops, something went wrong.