From f24e68b015b5d674861ae2a5de5267f8c816ef54 Mon Sep 17 00:00:00 2001 From: Daniel Liu <139250065@qq.com> Date: Sat, 15 Nov 2025 19:14:08 +0800 Subject: [PATCH] consensus/XDPoS, eth: fix potential rpc.BlockNumber overflow, close XFN-69 (#1735) --- consensus/XDPoS/api.go | 98 +++++++++++++++++---- consensus/tests/engine_v2_tests/api_test.go | 4 +- eth/api_backend.go | 5 ++ 3 files changed, 89 insertions(+), 18 deletions(-) diff --git a/consensus/XDPoS/api.go b/consensus/XDPoS/api.go index b68214d564..178ea7f785 100644 --- a/consensus/XDPoS/api.go +++ b/consensus/XDPoS/api.go @@ -139,6 +139,8 @@ func (api *API) GetSnapshot(number *rpc.BlockNumber) (*utils.PublicApiSnapshot, var header *types.Header if number == nil || *number == rpc.LatestBlockNumber { header = api.chain.CurrentHeader() + } else if number.Int64() < 0 { + return nil, fmt.Errorf("invalid block number %d", number.Int64()) } else { header = api.chain.GetHeaderByNumber(uint64(number.Int64())) } @@ -164,6 +166,8 @@ func (api *API) GetSigners(number *rpc.BlockNumber) ([]common.Address, error) { var header *types.Header if number == nil || *number == rpc.LatestBlockNumber { header = api.chain.CurrentHeader() + } else if number.Int64() < 0 { + return nil, fmt.Errorf("invalid block number %d", number.Int64()) } else { header = api.chain.GetHeaderByNumber(uint64(number.Int64())) } @@ -192,13 +196,22 @@ func (api *API) GetMasternodesByNumber(number *rpc.BlockNumber) MasternodesStatu if info := api.XDPoS.EngineV2.GetLatestCommittedBlockInfo(); info != nil { header = api.chain.GetHeaderByHash(info.Hash) } + } else if number.Int64() < 0 { + return MasternodesStatus{ + Error: fmt.Errorf("invalid block number %d", number.Int64()), + } } else { header = api.chain.GetHeaderByNumber(uint64(number.Int64())) } if header == nil { + if number == nil { + return MasternodesStatus{ + Error: errors.New("can not get header by nil number"), + } + } return MasternodesStatus{ - Error: fmt.Errorf("can not get header by number: %v", number), + Error: fmt.Errorf("can not get header by number %d", number.Int64()), } } @@ -311,7 +324,12 @@ func (api *API) GetV2BlockByHeader(header *types.Header, uncle bool) *V2BlockInf } func (api *API) GetV2BlockByNumber(number *rpc.BlockNumber) *V2BlockInfo { - header := api.getHeaderFromApiBlockNum(number) + header, err := api.getHeaderFromApiBlockNum(number) + if err != nil { + return &V2BlockInfo{ + Error: err.Error(), + } + } if header == nil { if number == nil { return &V2BlockInfo{ @@ -368,10 +386,20 @@ func (api *API) NetworkInformation() NetworkInformation { An API exclusively for V2 consensus, designed to assist in troubleshooting miners by identifying who mined during their allocated term. */ func (api *API) GetMissedRoundsInEpochByBlockNum(number *rpc.BlockNumber) (*utils.PublicApiMissedRoundsMetadata, error) { - return api.XDPoS.CalculateMissingRounds(api.chain, api.getHeaderFromApiBlockNum(number)) + header, err := api.getHeaderFromApiBlockNum(number) + if err != nil { + return nil, err + } + if header == nil { + if number == nil { + return nil, errors.New("can not get header by nil number") + } + return nil, fmt.Errorf("can not get header by number %d", number.Int64()) + } + return api.XDPoS.CalculateMissingRounds(api.chain, header) } -func (api *API) getHeaderFromApiBlockNum(number *rpc.BlockNumber) *types.Header { +func (api *API) getHeaderFromApiBlockNum(number *rpc.BlockNumber) (*types.Header, error) { var header *types.Header if number == nil || *number == rpc.LatestBlockNumber { header = api.chain.CurrentHeader() @@ -379,10 +407,12 @@ func (api *API) getHeaderFromApiBlockNum(number *rpc.BlockNumber) *types.Header if info := api.XDPoS.EngineV2.GetLatestCommittedBlockInfo(); info != nil { header = api.chain.GetHeaderByHash(info.Hash) } + } else if number.Int64() < 0 { + return nil, fmt.Errorf("invalid block number %d", number.Int64()) } else { header = api.chain.GetHeaderByNumber(uint64(number.Int64())) } - return header + return header, nil } func calculateSigners(message map[string]SignerTypes, pool map[string]map[common.Hash]utils.PoolObj, masternodes []common.Address) { @@ -464,16 +494,34 @@ func (api *API) GetRewardByAccount(account common.Address, begin rpc.BlockNumber } func (api *API) getRewardFileNamesInRange(begin, end *rpc.BlockNumber) ([]rewardFileName, error) { - beginHeader := api.getHeaderFromApiBlockNum(begin) - if beginHeader == nil { - return nil, errors.New("illegal begin block number") + beginHeader, err := api.getHeaderFromApiBlockNum(begin) + if err != nil { + if begin == nil { + return nil, fmt.Errorf("can not get begin header from nil number, err: %w", err) + } + return nil, fmt.Errorf("can not get begin header from number %d, err: %w", begin.Int64(), err) + } + if beginHeader == nil { + if begin == nil { + return nil, errors.New("begin block number is nil") + } + return nil, fmt.Errorf("illegal begin block number %d", begin.Int64()) + } + endHeader, err := api.getHeaderFromApiBlockNum(end) + if err != nil { + if end == nil { + return nil, fmt.Errorf("can not get end header from nil number, err: %w", err) + } + return nil, fmt.Errorf("can not get end header from number %d, err: %w", end.Int64(), err) } - endHeader := api.getHeaderFromApiBlockNum(end) if endHeader == nil { - return nil, errors.New("illegal end block number") + if end == nil { + return nil, errors.New("end block number is nil") + } + return nil, fmt.Errorf("illegal end block number %d", end.Int64()) } if beginHeader.Number.Cmp(endHeader.Number) > 0 { - return nil, errors.New("illegal begin and end block number, begin > end") + return nil, fmt.Errorf("illegal block numbers: begin(%d) > end(%d)", beginHeader.Number.Int64(), endHeader.Number.Int64()) } diff := new(big.Int).Sub(endHeader.Number, beginHeader.Number).Int64() if diff < 0 { @@ -621,18 +669,36 @@ func (rewardObj *AccountEpochReward) getRewardAndStatus(account string, data map return } } - } func (api *API) GetEpochNumbersBetween(begin, end *rpc.BlockNumber) ([]uint64, error) { - beginHeader := api.getHeaderFromApiBlockNum(begin) + beginHeader, err := api.getHeaderFromApiBlockNum(begin) + if err != nil { + if begin == nil { + return nil, fmt.Errorf("can not get begin header from nil number, err: %w", err) + } + return nil, fmt.Errorf("can not get begin header from number %d, err: %w", begin.Int64(), err) + } if beginHeader == nil { - return nil, errors.New("illegal begin block number") + if begin == nil { + return nil, errors.New("begin block is nil") + } + return nil, fmt.Errorf("illegal begin block number %d", begin.Int64()) + } + endHeader, err := api.getHeaderFromApiBlockNum(end) + if err != nil { + if end == nil { + return nil, fmt.Errorf("can not get end header from nil number, err: %w", err) + } + return nil, fmt.Errorf("can not get end header from number %d, err: %w", end.Int64(), err) } - endHeader := api.getHeaderFromApiBlockNum(end) if endHeader == nil { - return nil, errors.New("illegal end block number") + if end == nil { + return nil, errors.New("end block number is nil") + } + return nil, fmt.Errorf("illegal end block number %d", end.Int64()) } + diff := new(big.Int).Sub(endHeader.Number, beginHeader.Number).Int64() if diff < 0 { return nil, errors.New("illegal begin and end block number, begin > end") diff --git a/consensus/tests/engine_v2_tests/api_test.go b/consensus/tests/engine_v2_tests/api_test.go index 01d69ef97d..b70627d7f8 100644 --- a/consensus/tests/engine_v2_tests/api_test.go +++ b/consensus/tests/engine_v2_tests/api_test.go @@ -158,7 +158,7 @@ func TestGetEpochNumbersBetween(t *testing.T) { numbers, err = engine.APIs(bc.BlockChain())[0].Service.(*XDPoS.API).GetEpochNumbersBetween(&begin, &end) assert.Nil(t, numbers) - assert.EqualError(t, err, "illegal end block number") + assert.EqualError(t, err, "illegal end block number 1803") // 1803 not exist begin = rpc.BlockNumber(1803) @@ -166,7 +166,7 @@ func TestGetEpochNumbersBetween(t *testing.T) { numbers, err = engine.APIs(bc.BlockChain())[0].Service.(*XDPoS.API).GetEpochNumbersBetween(&begin, &end) assert.Nil(t, numbers) - assert.EqualError(t, err, "illegal begin block number") + assert.EqualError(t, err, "illegal begin block number 1803") } func TestGetBlockByEpochNumber(t *testing.T) { blockchain, _, currentBlock, signer, signFn := PrepareXDCTestBlockChainWithPenaltyForV2Engine(t, 1802, params.TestXDPoSMockChainConfig) diff --git a/eth/api_backend.go b/eth/api_backend.go index 8cbd6e705d..9d379d8194 100644 --- a/eth/api_backend.go +++ b/eth/api_backend.go @@ -20,6 +20,7 @@ import ( "context" "encoding/json" "errors" + "fmt" "math/big" "os" "path/filepath" @@ -97,6 +98,8 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb } else { return nil, errors.New("PoS V1 does not support confirmed block lookup") } + } else if number.Int64() < 0 { + return nil, fmt.Errorf("invalid block number %d", number.Int64()) } header := b.eth.blockchain.GetHeaderByNumber(uint64(number)) if header == nil { @@ -147,6 +150,8 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe } else { return nil, errors.New("PoS V1 does not support confirmed block lookup") } + } else if number.Int64() < 0 { + return nil, fmt.Errorf("invalid block number %d", number.Int64()) } return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil }