From 6f0a02ac428455722cc5367fe75c8796723f227d Mon Sep 17 00:00:00 2001 From: Manav Darji Date: Tue, 27 Jun 2023 20:45:45 +0530 Subject: [PATCH] [bor] Implement bor_getSnapshotProposerSequence RPC method (#7770) Implements `bor_getSnapshotProposerSequence` RPC method which returns an ordered set of validators (selected to mine) for a given block number. ``` > curl http://localhost:8545 -X POST --data '{"jsonrpc":"2.0","method":"bor_getSnapshotProposerSequence","params":["0x235A310"],"id":1}' -H "Content-Type: application/json" { "jsonrpc": "2.0", "id": 1, "result": { "Signers": [ { "Signer": "0xcfef2a3dc244ef7d0fb93c45e762d671445c4569", "Difficulty": 5 }, { "Signer": "0x3a22c8bc68e98b0faf40f349dd2b2890fae01484", "Difficulty": 4 }, { "Signer": "0xbe188d6641e8b680743a4815dfa0f6208038960f", "Difficulty": 3 }, { "Signer": "0xc26880a0af2ea0c7e8130e6ec47af756465452e8", "Difficulty": 2 }, { "Signer": "0xc275dc8be39f50d12f66b6a63629c39da5bae5bd", "Difficulty": 1 } ], "Diff": 5, "Author": "0xcfef2a3dc244ef7d0fb93c45e762d671445c4569" } } ``` --- cmd/rpcdaemon/README.md | 1 + cmd/rpcdaemon/commands/bor_api.go | 1 + cmd/rpcdaemon/commands/bor_helper.go | 14 ++++ cmd/rpcdaemon/commands/bor_snapshot.go | 92 ++++++++++++++++++++++++++ 4 files changed, 108 insertions(+) diff --git a/cmd/rpcdaemon/README.md b/cmd/rpcdaemon/README.md index 491e8735a28..5bbf457bfea 100644 --- a/cmd/rpcdaemon/README.md +++ b/cmd/rpcdaemon/README.md @@ -329,6 +329,7 @@ The following table shows the current implementation status of Erigon's RPC daem | bor_getSignersAtHash | Yes | Bor only | | bor_getCurrentProposer | Yes | Bor only | | bor_getCurrentValidators | Yes | Bor only | +| bor_getSnapshotProposerSequence | Yes | Bor only | | bor_getRootHash | Yes | Bor only | ### GraphQL diff --git a/cmd/rpcdaemon/commands/bor_api.go b/cmd/rpcdaemon/commands/bor_api.go index 4cf6f295ab2..5db34d8370a 100644 --- a/cmd/rpcdaemon/commands/bor_api.go +++ b/cmd/rpcdaemon/commands/bor_api.go @@ -18,6 +18,7 @@ type BorAPI interface { GetSignersAtHash(hash common.Hash) ([]common.Address, error) GetCurrentProposer() (common.Address, error) GetCurrentValidators() ([]*valset.Validator, error) + GetSnapshotProposerSequence(blockNrOrHash *rpc.BlockNumberOrHash) (BlockSigners, error) GetRootHash(start uint64, end uint64) (string, error) } diff --git a/cmd/rpcdaemon/commands/bor_helper.go b/cmd/rpcdaemon/commands/bor_helper.go index dccdc363621..2d50122708b 100644 --- a/cmd/rpcdaemon/commands/bor_helper.go +++ b/cmd/rpcdaemon/commands/bor_helper.go @@ -5,6 +5,7 @@ import ( "context" "errors" "fmt" + "sort" "github.com/ledgerwatch/erigon-lib/chain" "github.com/ledgerwatch/erigon-lib/common" @@ -160,3 +161,16 @@ func author(api *BorImpl, tx kv.Tx, header *types.Header) (common.Address, error config, _ := api.chainConfig(tx) return ecrecover(header, config.Bor) } + +func rankMapDifficulties(values map[common.Address]uint64) []difficultiesKV { + ss := make([]difficultiesKV, 0, len(values)) + for k, v := range values { + ss = append(ss, difficultiesKV{k, v}) + } + + sort.Slice(ss, func(i, j int) bool { + return ss[i].Difficulty > ss[j].Difficulty + }) + + return ss +} diff --git a/cmd/rpcdaemon/commands/bor_snapshot.go b/cmd/rpcdaemon/commands/bor_snapshot.go index 1bde2086118..27287735562 100644 --- a/cmd/rpcdaemon/commands/bor_snapshot.go +++ b/cmd/rpcdaemon/commands/bor_snapshot.go @@ -193,6 +193,98 @@ func (api *BorImpl) GetCurrentValidators() ([]*valset.Validator, error) { return snap.ValidatorSet.Validators, nil } +type BlockSigners struct { + Signers []difficultiesKV + Diff int + Author common.Address +} + +type difficultiesKV struct { + Signer common.Address + Difficulty uint64 +} + +func (api *BorImpl) GetSnapshotProposerSequence(blockNrOrHash *rpc.BlockNumberOrHash) (BlockSigners, error) { + // init chain db + ctx := context.Background() + tx, err := api.db.BeginRo(ctx) + if err != nil { + return BlockSigners{}, err + } + defer tx.Rollback() + + // Retrieve the requested block number (or current if none requested) + var header *types.Header + if blockNrOrHash == nil { + header = rawdb.ReadCurrentHeader(tx) + } else { + if blockNr, ok := blockNrOrHash.Number(); ok { + if blockNr == rpc.LatestBlockNumber { + header = rawdb.ReadCurrentHeader(tx) + } else { + header, err = getHeaderByNumber(ctx, blockNr, api, tx) + } + } else { + if blockHash, ok := blockNrOrHash.Hash(); ok { + header, err = getHeaderByHash(ctx, api, tx, blockHash) + } + } + } + + // Ensure we have an actually valid block + if header == nil || err != nil { + return BlockSigners{}, errUnknownBlock + } + + // init consensus db + borTx, err := api.borDb.BeginRo(ctx) + if err != nil { + return BlockSigners{}, err + } + defer borTx.Rollback() + + parent, err := getHeaderByNumber(ctx, rpc.BlockNumber(int64(header.Number.Uint64()-1)), api, tx) + if parent == nil || err != nil { + return BlockSigners{}, errUnknownBlock + } + snap, err := snapshot(ctx, api, tx, borTx, parent) + + var difficulties = make(map[common.Address]uint64) + + if err != nil { + return BlockSigners{}, err + } + + proposer := snap.ValidatorSet.GetProposer().Address + proposerIndex, _ := snap.ValidatorSet.GetByAddress(proposer) + + signers := snap.signers() + for i := 0; i < len(signers); i++ { + tempIndex := i + if tempIndex < proposerIndex { + tempIndex = tempIndex + len(signers) + } + + difficulties[signers[i]] = uint64(len(signers) - (tempIndex - proposerIndex)) + } + + rankedDifficulties := rankMapDifficulties(difficulties) + + author, err := author(api, tx, header) + if err != nil { + return BlockSigners{}, err + } + + diff := int(difficulties[author]) + blockSigners := BlockSigners{ + Signers: rankedDifficulties, + Diff: diff, + Author: author, + } + + return blockSigners, nil +} + // GetRootHash returns the merkle root of the start to end block headers func (api *BorImpl) GetRootHash(start, end uint64) (string, error) { length := end - start + 1