From 72cbafbd16ce577cdbdc308234b9edc2733a44b3 Mon Sep 17 00:00:00 2001 From: Lucca Martins Date: Fri, 24 Jan 2025 15:45:46 -0300 Subject: [PATCH] apply validator id from validatorSet contract --- consensus/bor/bor.go | 2 +- consensus/bor/heimdall/span/spanner.go | 144 ++++++++++++++++++++++++- consensus/bor/snapshot.go | 6 +- consensus/bor/valset/validator_set.go | 18 ++++ 4 files changed, 166 insertions(+), 4 deletions(-) diff --git a/consensus/bor/bor.go b/consensus/bor/bor.go index 829a5f67c4..0102a4a52d 100644 --- a/consensus/bor/bor.go +++ b/consensus/bor/bor.go @@ -620,7 +620,7 @@ func (c *Bor) snapshot(chain consensus.ChainHeaderReader, number uint64, hash co headers[i], headers[len(headers)-1-i] = headers[len(headers)-1-i], headers[i] } - snap, err := snap.apply(headers) + snap, err := snap.apply(headers, c) if err != nil { return nil, err } diff --git a/consensus/bor/heimdall/span/spanner.go b/consensus/bor/heimdall/span/spanner.go index d7da3549f4..5ed619d71c 100644 --- a/consensus/bor/heimdall/span/spanner.go +++ b/consensus/bor/heimdall/span/spanner.go @@ -29,6 +29,13 @@ type ChainSpanner struct { validatorContractAddress common.Address } +// validator response on ValidatorSet contract +type contractValidator struct { + Id *big.Int + Power *big.Int + Signer common.Address +} + func NewChainSpanner(ethAPI api.Caller, validatorSet abi.ABI, chainConfig *params.ChainConfig, validatorContractAddress common.Address) *ChainSpanner { return &ChainSpanner{ ethAPI: ethAPI, @@ -93,6 +100,141 @@ func (c *ChainSpanner) GetCurrentValidatorsByBlockNrOrHash(ctx context.Context, ctx, cancel := context.WithCancel(ctx) defer cancel() + toAddress := c.validatorContractAddress + gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2)) + + valz, err := c.tryGetBorValidatorsWithId(ctx, blockNrOrHash, blockNumber, toAddress, gas) + if err != nil { + return nil, err + } + + return valz, nil +} + +// Try to get bor validators with Id from ValidatorSet contract by querying each element on mapping(uint256 => Validator[]) public producers +// If fails then returns GetBorValidators without id +func (c *ChainSpanner) tryGetBorValidatorsWithId(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, blockNumber uint64, toAddress common.Address, gas hexutil.Uint64) ([]*valset.Validator, error) { + firstEndBlock, err := c.getFirstEndBlock(ctx, blockNrOrHash, toAddress, gas) + if err != nil { + return nil, err + } + var spanNumber *big.Int + if big.NewInt(int64(blockNumber)).Cmp(firstEndBlock) <= 0 { + spanNumber = big.NewInt(0) + } else { + spanNumber, err = c.getSpanByBlock(ctx, blockNrOrHash, blockNumber, toAddress, gas) + if err != nil { + return nil, err + } + + } + + borValidatorsWithoutId, err := c.getBorValidatorsWithoutId(ctx, blockNrOrHash, blockNumber, toAddress, gas) + if err != nil { + return nil, err + } + + producersCount := len(borValidatorsWithoutId) + + valz := make([]*valset.Validator, producersCount) + + for i := 0; i < producersCount; i++ { + p, err := c.getProducersBySpanAndIndexMethod(ctx, blockNrOrHash, blockNumber, toAddress, gas, spanNumber, i) + // if fails, return validators without id + if err != nil { + return borValidatorsWithoutId, nil + } + + valz[i] = &valset.Validator{ + ID: p.Id.Uint64(), + Address: p.Signer, + VotingPower: p.Power.Int64(), + } + } + + return valz, nil +} + +func (c *ChainSpanner) getSpanByBlock(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, blockNumber uint64, toAddress common.Address, gas hexutil.Uint64) (*big.Int, error) { + const getSpanByBlockMethod = "getSpanByBlock" + spanData, err := c.validatorSet.Pack(getSpanByBlockMethod, big.NewInt(0).SetUint64(blockNumber)) + if err != nil { + log.Error("Unable to pack tx for getSpanByBlock", "error", err) + return nil, err + } + + spanMsgData := (hexutil.Bytes)(spanData) + + spanResult, err := c.ethAPI.Call(ctx, ethapi.TransactionArgs{ + Gas: &gas, + To: &toAddress, + Data: &spanMsgData, + }, &blockNrOrHash, nil, nil) + if err != nil { + return nil, err + } + + var spanNumber *big.Int + if err := c.validatorSet.UnpackIntoInterface(&spanNumber, getSpanByBlockMethod, spanResult); err != nil { + return nil, err + } + return spanNumber, nil +} + +func (c *ChainSpanner) getProducersBySpanAndIndexMethod(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, blockNumber uint64, toAddress common.Address, gas hexutil.Uint64, spanNumber *big.Int, index int) (*contractValidator, error) { + const getProducersBySpanAndIndexMethod = "producers" + producerData, err := c.validatorSet.Pack(getProducersBySpanAndIndexMethod, spanNumber, big.NewInt(int64(index))) + if err != nil { + log.Error("Unable to pack tx for producers", "error", err) + return nil, err + } + + producerMsgData := (hexutil.Bytes)(producerData) + + result, err := c.ethAPI.Call(ctx, ethapi.TransactionArgs{ + Gas: &gas, + To: &toAddress, + Data: &producerMsgData, + }, &blockNrOrHash, nil, nil) + if err != nil { + return nil, err + } + + var producer contractValidator + if err := c.validatorSet.UnpackIntoInterface(&producer, getProducersBySpanAndIndexMethod, result); err != nil { + return nil, err + } + return &producer, nil +} + +func (c *ChainSpanner) getFirstEndBlock(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, toAddress common.Address, gas hexutil.Uint64) (*big.Int, error) { + const getFirstEndBlockMethod = "FIRST_END_BLOCK" + firstEndBlockData, err := c.validatorSet.Pack(getFirstEndBlockMethod) + if err != nil { + log.Error("Unable to pack tx for getFirstEndBlock", "error", err) + return nil, err + } + + firstEndBlockMsgData := (hexutil.Bytes)(firstEndBlockData) + + firstEndBlockResult, err := c.ethAPI.Call(ctx, ethapi.TransactionArgs{ + Gas: &gas, + To: &toAddress, + Data: &firstEndBlockMsgData, + }, &blockNrOrHash, nil, nil) + if err != nil { + return nil, err + } + + var firstEndBlockNumber *big.Int + if err := c.validatorSet.UnpackIntoInterface(&firstEndBlockNumber, getFirstEndBlockMethod, firstEndBlockResult); err != nil { + return nil, err + } + return firstEndBlockNumber, nil +} + +func (c *ChainSpanner) getBorValidatorsWithoutId(ctx context.Context, blockNrOrHash rpc.BlockNumberOrHash, blockNumber uint64, toAddress common.Address, gas hexutil.Uint64) ([]*valset.Validator, error) { + // method const method = "getBorValidators" @@ -104,8 +246,6 @@ func (c *ChainSpanner) GetCurrentValidatorsByBlockNrOrHash(ctx context.Context, // call msgData := (hexutil.Bytes)(data) - toAddress := c.validatorContractAddress - gas := (hexutil.Uint64)(uint64(math.MaxUint64 / 2)) result, err := c.ethAPI.Call(ctx, ethapi.TransactionArgs{ Gas: &gas, diff --git a/consensus/bor/snapshot.go b/consensus/bor/snapshot.go index 87e3fb163c..72c7bc1ab1 100644 --- a/consensus/bor/snapshot.go +++ b/consensus/bor/snapshot.go @@ -1,6 +1,7 @@ package bor import ( + "context" "encoding/json" "github.com/ethereum/go-ethereum/consensus/bor/valset" @@ -100,7 +101,7 @@ func (s *Snapshot) copy() *Snapshot { return cpy } -func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) { +func (s *Snapshot) apply(headers []*types.Header, c *Bor) (*Snapshot, error) { // Allow passing in no headers for cleaner code if len(headers) == 0 { return s, nil @@ -157,6 +158,9 @@ func (s *Snapshot) apply(headers []*types.Header) (*Snapshot, error) { newVals, _ := valset.ParseValidators(validatorBytes) v := getUpdatedValidatorSet(snap.ValidatorSet.Copy(), newVals) v.IncrementProposerPriority(1) + + valsWithId, _ := c.spanner.GetCurrentValidatorsByHash(context.Background(), header.Hash(), header.Number.Uint64()) + v.IncludeIds(valsWithId) snap.ValidatorSet = v } } diff --git a/consensus/bor/valset/validator_set.go b/consensus/bor/valset/validator_set.go index b301470e56..7107679117 100644 --- a/consensus/bor/valset/validator_set.go +++ b/consensus/bor/valset/validator_set.go @@ -110,6 +110,24 @@ func (vals *ValidatorSet) IncrementProposerPriority(times int) { vals.Proposer = proposer } +// IncludeIds include the proper Id of each validator by getting the id from +// validator queried on ValidatorSet contract +func (vals *ValidatorSet) IncludeIds(valsWithId []*Validator) { + if vals.IsNilOrEmpty() { + panic("empty validator set") + } + + addressToId := make(map[common.Address]uint64) + + for _, val := range valsWithId { + addressToId[val.Address] = val.ID + } + + for _, val := range vals.Validators { + val.ID = addressToId[val.Address] + } +} + func (vals *ValidatorSet) RescalePriorities(diffMax int64) { if vals.IsNilOrEmpty() { panic("empty validator set")