From 3593abe815ddcebf3e477d84027138c1afeac5ca Mon Sep 17 00:00:00 2001 From: wgr523 Date: Mon, 2 Sep 2024 15:11:12 +0800 Subject: [PATCH] feat: GetEpochNumbersBetween API (#606) * feat: GetEpochNumbersBetween API * style: refine GetEpochNumbersBetween API --- consensus/XDPoS/XDPoS.go | 10 ++++ consensus/XDPoS/api.go | 24 ++++++++ .../XDPoS/engines/engine_v2/epochSwitch.go | 31 ++++++++++ consensus/tests/engine_v2_tests/api_test.go | 59 +++++++++++++++++++ internal/web3ext/web3ext.go | 6 ++ 5 files changed, 130 insertions(+) diff --git a/consensus/XDPoS/XDPoS.go b/consensus/XDPoS/XDPoS.go index 951fe99a7d..eb976ca603 100644 --- a/consensus/XDPoS/XDPoS.go +++ b/consensus/XDPoS/XDPoS.go @@ -550,3 +550,13 @@ func (x *XDPoS) CacheSigningTxs(hash common.Hash, txs []*types.Transaction) []*t func (x *XDPoS) GetCachedSigningTxs(hash common.Hash) (interface{}, bool) { return x.signingTxsCache.Get(hash) } + +func (x *XDPoS) GetEpochSwitchInfoBetween(chain consensus.ChainReader, begin, end *types.Header) ([]*types.EpochSwitchInfo, error) { + beginBlockVersion := x.config.BlockConsensusVersion(begin.Number, begin.Extra, ExtraFieldCheck) + endBlockVersion := x.config.BlockConsensusVersion(end.Number, end.Extra, ExtraFieldCheck) + if beginBlockVersion == params.ConsensusEngineVersion2 && endBlockVersion == params.ConsensusEngineVersion2 { + return x.EngineV2.GetEpochSwitchInfoBetween(chain, begin, end) + } + // Default "v1" + return nil, errors.New("not supported in the v1 consensus") +} diff --git a/consensus/XDPoS/api.go b/consensus/XDPoS/api.go index 7209939662..fd06703128 100644 --- a/consensus/XDPoS/api.go +++ b/consensus/XDPoS/api.go @@ -17,6 +17,7 @@ package XDPoS import ( "encoding/base64" + "errors" "math/big" "github.com/XinFinOrg/XDPoSChain/common" @@ -320,3 +321,26 @@ func calculateSigners(message map[string]SignerTypes, pool map[string]map[common } } } + +func (api *API) GetEpochNumbersBetween(begin, end *rpc.BlockNumber) ([]uint64, error) { + beginHeader := api.getHeaderFromApiBlockNum(begin) + if beginHeader == nil { + return nil, errors.New("illegal begin block number") + } + endHeader := api.getHeaderFromApiBlockNum(end) + if endHeader == nil { + return nil, errors.New("illegal end block number") + } + if beginHeader.Number.Cmp(endHeader.Number) > 0 { + return nil, errors.New("illegal begin and end block number, begin > end") + } + epochSwitchInfos, err := api.XDPoS.GetEpochSwitchInfoBetween(api.chain, beginHeader, endHeader) + if err != nil { + return nil, err + } + epochSwitchNumbers := make([]uint64, len(epochSwitchInfos)) + for i, info := range epochSwitchInfos { + epochSwitchNumbers[i] = info.EpochSwitchBlockInfo.Number.Uint64() + } + return epochSwitchNumbers, nil +} diff --git a/consensus/XDPoS/engines/engine_v2/epochSwitch.go b/consensus/XDPoS/engines/engine_v2/epochSwitch.go index 981c46ff9a..abb35bde09 100644 --- a/consensus/XDPoS/engines/engine_v2/epochSwitch.go +++ b/consensus/XDPoS/engines/engine_v2/epochSwitch.go @@ -157,3 +157,34 @@ func (x *XDPoS_v2) IsEpochSwitch(header *types.Header) (bool, uint64, error) { log.Debug("[IsEpochSwitch]", "is", parentRound < epochStartRound, "parentRound", parentRound, "round", round, "number", header.Number.Uint64(), "epochNum", epochNum, "hash", header.Hash()) return parentRound < epochStartRound, epochNum, nil } + +// GetEpochSwitchInfoBetween get epoch switch between begin and end headers +// Search backwardly from end number to begin number +func (x *XDPoS_v2) GetEpochSwitchInfoBetween(chain consensus.ChainReader, begin, end *types.Header) ([]*types.EpochSwitchInfo, error) { + infos := make([]*types.EpochSwitchInfo, 0) + // after the first iteration, it becomes nil since epoch switch info does not have header info + iteratorHeader := end + // after the first iteration, it becomes the parent hash of the epoch switch block + iteratorHash := end.Hash() + iteratorNum := end.Number + // when iterator is strictly > begin number, do the search + for iteratorNum.Cmp(begin.Number) > 0 { + epochSwitchInfo, err := x.getEpochSwitchInfo(chain, iteratorHeader, iteratorHash) + if err != nil { + log.Error("[GetEpochSwitchInfoBetween] Adaptor v2 getEpochSwitchInfo has error, potentially bug", "err", err) + return nil, err + } + iteratorHeader = nil + iteratorHash = epochSwitchInfo.EpochSwitchParentBlockInfo.Hash + iteratorNum = epochSwitchInfo.EpochSwitchBlockInfo.Number + if iteratorNum.Cmp(begin.Number) >= 0 { + infos = append(infos, epochSwitchInfo) + } + + } + // reverse the array + for i := 0; i < len(infos)/2; i++ { + infos[i], infos[len(infos)-1-i] = infos[len(infos)-1-i], infos[i] + } + return infos, nil +} diff --git a/consensus/tests/engine_v2_tests/api_test.go b/consensus/tests/engine_v2_tests/api_test.go index fb99f6f879..185f9a40cc 100644 --- a/consensus/tests/engine_v2_tests/api_test.go +++ b/consensus/tests/engine_v2_tests/api_test.go @@ -2,6 +2,7 @@ package engine_v2_tests import ( "math/big" + "reflect" "testing" "github.com/XinFinOrg/XDPoSChain/consensus/XDPoS" @@ -109,3 +110,61 @@ func TestGetMissedRoundsInEpochByBlockNum(t *testing.T) { assert.NotEqual(t, data.MissedRounds[0].Miner, data.MissedRounds[1].Miner) } + +func TestGetEpochNumbersBetween(t *testing.T) { + _, bc, _, _, _ := PrepareXDCTestBlockChainWith128Candidates(t, 1802, params.TestXDPoSMockChainConfig) + + engine := bc.GetBlockChain().Engine().(*XDPoS.XDPoS) + + begin := rpc.BlockNumber(1800) + end := rpc.BlockNumber(1802) + numbers, err := engine.APIs(bc.GetBlockChain())[0].Service.(*XDPoS.API).GetEpochNumbersBetween(&begin, &end) + + assert.True(t, reflect.DeepEqual([]uint64{1800}, numbers)) + assert.Nil(t, err) + + begin = rpc.BlockNumber(1799) + end = rpc.BlockNumber(1802) + numbers, err = engine.APIs(bc.GetBlockChain())[0].Service.(*XDPoS.API).GetEpochNumbersBetween(&begin, &end) + + assert.True(t, reflect.DeepEqual([]uint64{1800}, numbers)) + assert.Nil(t, err) + + begin = rpc.BlockNumber(1799) + end = rpc.BlockNumber(1802) + numbers, err = engine.APIs(bc.GetBlockChain())[0].Service.(*XDPoS.API).GetEpochNumbersBetween(&begin, &end) + + assert.True(t, reflect.DeepEqual([]uint64{1800}, numbers)) + assert.Nil(t, err) + + begin = rpc.BlockNumber(901) + end = rpc.BlockNumber(1802) + numbers, err = engine.APIs(bc.GetBlockChain())[0].Service.(*XDPoS.API).GetEpochNumbersBetween(&begin, &end) + + assert.True(t, reflect.DeepEqual([]uint64{901, 1800}, numbers)) + assert.Nil(t, err) + + // 900 is V1, not V2, so error + begin = rpc.BlockNumber(900) + end = rpc.BlockNumber(1802) + numbers, err = engine.APIs(bc.GetBlockChain())[0].Service.(*XDPoS.API).GetEpochNumbersBetween(&begin, &end) + + assert.Nil(t, numbers) + assert.EqualError(t, err, "not supported in the v1 consensus") + + // 1803 not exist + begin = rpc.BlockNumber(901) + end = rpc.BlockNumber(1803) + numbers, err = engine.APIs(bc.GetBlockChain())[0].Service.(*XDPoS.API).GetEpochNumbersBetween(&begin, &end) + + assert.Nil(t, numbers) + assert.EqualError(t, err, "illegal end block number") + + // 1803 not exist + begin = rpc.BlockNumber(1803) + end = rpc.BlockNumber(1803) + numbers, err = engine.APIs(bc.GetBlockChain())[0].Service.(*XDPoS.API).GetEpochNumbersBetween(&begin, &end) + + assert.Nil(t, numbers) + assert.EqualError(t, err, "illegal begin block number") +} diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index dbb3f41df4..688f8df33a 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -162,6 +162,12 @@ web3._extend({ params: 1, inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter] }), + new web3._extend.Method({ + name: 'getEpochNumbersBetween', + call: 'XDPoS_getEpochNumbersBetween', + params: 2, + inputFormatter: [web3._extend.formatters.inputBlockNumberFormatter, web3._extend.formatters.inputBlockNumberFormatter] + }), ], properties: [ new web3._extend.Property({