Skip to content

Commit

Permalink
feat: add new fork block and precompile contract
Browse files Browse the repository at this point in the history
  • Loading branch information
pythonberg1997 committed Oct 26, 2023
1 parent 4493ab8 commit 12214ef
Show file tree
Hide file tree
Showing 14 changed files with 4,790 additions and 1,794 deletions.
5,624 changes: 3,902 additions & 1,722 deletions consensus/parlia/abi.go

Large diffs are not rendered by default.

226 changes: 226 additions & 0 deletions consensus/parlia/fusionfork.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
package parlia

import (
"container/heap"
"context"
"math"
"math/big"
"sort"

"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"
)

// init contract
func (p *Parlia) initFusionContract(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"
// contracts
contracts := []string{
systemcontracts.StakeHubContract,
}
// get packed data
data, err := p.stakeHubABI.Pack(method)
if err != nil {
log.Error("Unable to pack tx for init fusion contract", "error", err)
return err
}
for _, c := range contracts {
msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(c), data, common.Big0)
// apply message
log.Trace("init fusion contract", "block hash", header.Hash(), "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
}

// An ValidatorHeap is a min-heap of validator's votingPower.
type ValidatorHeap []ValidatorItem

func (h ValidatorHeap) Len() int { return len(h) }

// We want topK validators with max voting power, so we need a min-heap
func (h ValidatorHeap) Less(i, j int) bool {
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{}) {
// Push and Pop use pointer receivers because they modify the slice's length,
// not just its contents.
*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) updateEligibleValidators(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
method := "getValidatorWithVotingPower"

ctx, cancel := context.WithCancel(context.Background())
defer cancel() // cancel when we are finished consuming integers

blockNr := rpc.BlockNumberOrHashWithHash(header.ParentHash, false)
toAddress := common.HexToAddress(systemcontracts.StakeHubContract)
gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2))

var allValidators []common.Address
var allVotingPowers []*big.Int
for {
data, err := p.stakeHubABI.Pack(method, big.NewInt(int64(len(allValidators))), big.NewInt(100))
if err != nil {
log.Error("Unable to pack tx for getValidatorWithVotingPower", "error", err)
return 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 err
}

var validators []common.Address
var votingPowers []*big.Int
var totalLength *big.Int
if err := p.stakeHubABI.UnpackIntoInterface(&[]interface{}{&validators, &votingPowers, &totalLength}, method, result); err != nil {
return err
}

allValidators = append(allValidators, validators...)
allVotingPowers = append(allVotingPowers, votingPowers...)
if totalLength.Cmp(big.NewInt(int64(len(allValidators)))) == 0 {
break
}
}

// 2. sort by voting power
method = "maxElectedValidators"
data, err := p.stakeHubABI.Pack(method)
if err != nil {
log.Error("Unable to pack tx for maxElectedValidators", "error", err)
return 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 err
}

var maxElectedValidators *big.Int
if err := p.stakeHubABI.UnpackIntoInterface(&maxElectedValidators, method, result); err != nil {
return err
}

var validatorHeap ValidatorHeap
if maxElectedValidators.Cmp(big.NewInt(int64(len(allValidators)))) == 1 {
for i := 0; i < len(allValidators); i++ {
if allVotingPowers[i].Cmp(big.NewInt(0)) == 1 {
validatorHeap = append(validatorHeap, ValidatorItem{
address: allValidators[i],
votingPower: allVotingPowers[i],
})
}
}
sort.SliceStable(validatorHeap, validatorHeap.Less)
} else {
i := 0
for len(validatorHeap) < int(maxElectedValidators.Int64()) && i < len(allValidators) {
if allVotingPowers[i].Cmp(big.NewInt(0)) == 1 {
validatorHeap = append(validatorHeap, ValidatorItem{
address: allValidators[i],
votingPower: allVotingPowers[i],
})
}
i++
}
hp := &validatorHeap
heap.Init(hp)
for j := i; j < len(allValidators); j++ {
if allVotingPowers[j].Cmp(validatorHeap[0].votingPower) == 1 {
heap.Pop(hp)
heap.Push(hp, ValidatorItem{
address: allValidators[j],
votingPower: allVotingPowers[j],
})
}
}
}

length := len(validatorHeap)
eligibleValidators := make([]common.Address, length)
eligibleVotingPowers := make([]uint64, length)
// reverse(from max to min)
for i, item := range validatorHeap {
eligibleValidators[length-i-1] = item.address
eligibleVotingPowers[length-i-1] = new(big.Int).Div(item.votingPower, big.NewInt(1e10)).Uint64() // to avoid overflow
}

// 3. update validator set to system contract
method = "updateEligibleValidators"
data, err = p.stakeHubABI.Pack(method, eligibleValidators, eligibleVotingPowers)
if err != nil {
log.Error("Unable to pack tx for updateEligibleValidators", "error", err)
return err
}

// get system message
msg := p.getSystemMessage(header.Coinbase, common.HexToAddress(systemcontracts.StakeHubContract), data, common.Big0)
// apply message
return p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining)
}

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 {
// method
method := "updateValidatorSetV2"

// get packed data
data, err := p.validatorSetABI.Pack(method)
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, big.NewInt(0))
// apply message
return p.applyTransaction(msg, state, header, chain, txs, receipts, receivedTxs, usedGas, mining)
}
158 changes: 158 additions & 0 deletions consensus/parlia/fusionfork_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
package parlia

import (
"container/heap"
"math/big"
"sort"
"testing"

"github.com/ethereum/go-ethereum/common"
)

func TestValidatorHeap(t *testing.T) {
testCases := []struct {
description string
k int
validators []common.Address
votingPowers []uint64
expected []common.Address
}{
{
description: "normal case",
k: 2,
validators: []common.Address{
common.HexToAddress("0x1"),
common.HexToAddress("0x2"),
common.HexToAddress("0x3"),
},
votingPowers: []uint64{
300,
200,
100,
},
expected: []common.Address{
common.HexToAddress("0x2"),
common.HexToAddress("0x1"),
},
},
{
description: "same voting power",
k: 2,
validators: []common.Address{
common.HexToAddress("0x1"),
common.HexToAddress("0x2"),
common.HexToAddress("0x3"),
},
votingPowers: []uint64{
300,
100,
100,
},
expected: []common.Address{
common.HexToAddress("0x2"),
common.HexToAddress("0x1"),
},
},
{
description: "zero voting power and k > len(validators)",
k: 5,
validators: []common.Address{
common.HexToAddress("0x1"),
common.HexToAddress("0x2"),
common.HexToAddress("0x3"),
common.HexToAddress("0x4"),
},
votingPowers: []uint64{
300,
0,
0,
0,
},
expected: []common.Address{
common.HexToAddress("0x1"),
},
},
{
description: "zero voting power and k < len(validators)",
k: 2,
validators: []common.Address{
common.HexToAddress("0x1"),
common.HexToAddress("0x2"),
common.HexToAddress("0x3"),
common.HexToAddress("0x4"),
},
votingPowers: []uint64{
300,
0,
0,
0,
},
expected: []common.Address{
common.HexToAddress("0x1"),
},
},
{
description: "all zero voting power",
k: 2,
validators: []common.Address{
common.HexToAddress("0x1"),
common.HexToAddress("0x2"),
common.HexToAddress("0x3"),
common.HexToAddress("0x4"),
},
votingPowers: []uint64{
0,
0,
0,
0,
},
expected: []common.Address{},
},
}
for _, tc := range testCases {
var h ValidatorHeap
if tc.k > len(tc.validators) {
for i := 0; i < len(tc.validators); i++ {
if tc.votingPowers[i] > 0 {
h = append(h, ValidatorItem{
address: tc.validators[i],
votingPower: big.NewInt(int64(tc.votingPowers[i])),
})
}
}
sort.SliceStable(h, h.Less)
} else {
i := 0
for len(h) < tc.k && i < len(tc.validators) {
if tc.votingPowers[i] > 0 {
h = append(h, ValidatorItem{
address: tc.validators[i],
votingPower: big.NewInt(int64(tc.votingPowers[i])),
})
}
i++
}
hp := &h
heap.Init(hp)
for j := i; j < len(tc.validators); j++ {
if tc.votingPowers[i] > h[0].votingPower.Uint64() {
heap.Pop(hp)
heap.Push(hp, ValidatorItem{
address: tc.validators[j],
votingPower: big.NewInt(int64(tc.votingPowers[i])),
})
}
}
}

// check
if len(h) != len(tc.expected) {
t.Errorf("expected %d, got %d", len(tc.expected), len(h))
}
for i := 0; i < len(tc.expected); i++ {
if h[i].address != tc.expected[i] {
t.Errorf("expected %s, got %s", tc.expected[i].Hex(), h[i].address.Hex())
}
}
}
}
Loading

0 comments on commit 12214ef

Please sign in to comment.