core, eth, internal: improve getBadBlocks to return full block rlp #16902 (#1595)

This commit is contained in:
Daniel Liu 2025-09-26 19:02:13 +08:00 committed by GitHub
parent 0ece8529c4
commit f46920b407
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 54 additions and 26 deletions

View file

@ -197,7 +197,7 @@ type BlockChain struct {
resultProcess *lru.Cache[common.Hash, *ResultProcessBlock] // Cache for processed blocks
calculatingBlock *lru.Cache[common.Hash, *CalculatedBlock] // Cache for processing blocks
downloadingBlock *lru.Cache[common.Hash, struct{}] // Cache for downloading blocks (avoid duplication from fetcher)
badBlocks *lru.Cache[common.Hash, *types.Header] // Bad block cache
badBlocks *lru.Cache[common.Hash, *types.Block] // Bad block cache
// future blocks are blocks added for later processing
futureBlocks *lru.Cache[common.Hash, *types.Block]
@ -262,7 +262,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
engine: engine,
vmConfig: vmConfig,
logger: vmConfig.Tracer,
badBlocks: lru.NewCache[common.Hash, *types.Header](badBlockLimit),
badBlocks: lru.NewCache[common.Hash, *types.Block](badBlockLimit),
blocksHashCache: lru.NewCache[uint64, []common.Hash](blocksHashCacheLimit),
resultTrade: lru.NewCache[common.Hash, interface{}](tradingstate.OrderCacheLimit),
rejectedOrders: lru.NewCache[common.Hash, interface{}](tradingstate.OrderCacheLimit),
@ -2574,26 +2574,20 @@ func (bc *BlockChain) futureBlocksLoop() {
}
}
// BadBlockArgs represents the entries in the list returned when bad blocks are queried.
type BadBlockArgs struct {
Hash common.Hash `json:"hash"`
Header *types.Header `json:"header"`
}
// BadBlocks returns a list of the last 'bad blocks' that the client has seen on the network
func (bc *BlockChain) BadBlocks() ([]BadBlockArgs, error) {
headers := make([]BadBlockArgs, 0, bc.badBlocks.Len())
func (bc *BlockChain) BadBlocks() []*types.Block {
blocks := make([]*types.Block, 0, bc.badBlocks.Len())
for _, hash := range bc.badBlocks.Keys() {
if header, exist := bc.badBlocks.Peek(hash); exist {
headers = append(headers, BadBlockArgs{header.Hash(), header})
if blk, exist := bc.badBlocks.Peek(hash); exist {
blocks = append(blocks, blk)
}
}
return headers, nil
return blocks
}
// addBadBlock adds a bad block to the bad-block LRU cache
func (bc *BlockChain) addBadBlock(block *types.Block) {
bc.badBlocks.Add(block.Header().Hash(), block.Header())
bc.badBlocks.Add(block.Hash(), block)
}
// reportBlock logs a bad block error.

View file

@ -32,6 +32,7 @@ import (
"github.com/XinFinOrg/XDPoSChain/core/rawdb"
"github.com/XinFinOrg/XDPoSChain/core/state"
"github.com/XinFinOrg/XDPoSChain/core/types"
"github.com/XinFinOrg/XDPoSChain/internal/ethapi"
"github.com/XinFinOrg/XDPoSChain/log"
"github.com/XinFinOrg/XDPoSChain/miner"
"github.com/XinFinOrg/XDPoSChain/params"
@ -325,10 +326,34 @@ func (api *DebugAPI) Preimage(ctx context.Context, hash common.Hash) (hexutil.By
return nil, errors.New("unknown preimage")
}
// BadBlockArgs represents the entries in the list returned when bad blocks are queried.
type BadBlockArgs struct {
Hash common.Hash `json:"hash"`
Block map[string]interface{} `json:"block"`
RLP string `json:"rlp"`
}
// GetBadBLocks returns a list of the last 'bad blocks' that the client has seen on the network
// and returns them as a JSON list of block-hashes
func (api *DebugAPI) GetBadBlocks(ctx context.Context) ([]core.BadBlockArgs, error) {
return api.eth.BlockChain().BadBlocks()
func (api *DebugAPI) GetBadBlocks(ctx context.Context) ([]*BadBlockArgs, error) {
blocks := api.eth.BlockChain().BadBlocks()
results := make([]*BadBlockArgs, len(blocks))
var err error
for i, block := range blocks {
results[i] = &BadBlockArgs{
Hash: block.Hash(),
}
if rlpBytes, err := rlp.EncodeToBytes(block); err != nil {
results[i].RLP = err.Error() // Hacky, but hey, it works
} else {
results[i].RLP = fmt.Sprintf("0x%x", rlpBytes)
}
if results[i].Block, err = ethapi.RPCMarshalBlock(block, true, true); err != nil {
results[i].Block = map[string]interface{}{"error": err.Error()}
}
}
return results, nil
}
// AccountRangeMaxResults is the maximum number of results to be returned per call

View file

@ -382,7 +382,7 @@ func (s *BlockChainAPI) GetTransactionAndReceiptProof(ctx context.Context, hash
func (s *BlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.BlockNumber, fullTx bool) (map[string]interface{}, error) {
block, err := s.b.BlockByNumber(ctx, blockNr)
if block != nil {
response, err := s.rpcOutputBlock(block, true, fullTx, ctx)
response, err := s.rpcOutputBlock(block, true, fullTx)
if err == nil && blockNr == rpc.PendingBlockNumber {
// Pending blocks need to nil out a few fields
for _, field := range []string{"hash", "nonce", "miner"} {
@ -399,7 +399,7 @@ func (s *BlockChainAPI) GetBlockByNumber(ctx context.Context, blockNr rpc.BlockN
func (s *BlockChainAPI) GetBlockByHash(ctx context.Context, blockHash common.Hash, fullTx bool) (map[string]interface{}, error) {
block, err := s.b.GetBlock(ctx, blockHash)
if block != nil {
return s.rpcOutputBlock(block, true, fullTx, ctx)
return s.rpcOutputBlock(block, true, fullTx)
}
return nil, err
}
@ -415,7 +415,7 @@ func (s *BlockChainAPI) GetUncleByBlockNumberAndIndex(ctx context.Context, block
return nil, nil
}
block = types.NewBlockWithHeader(uncles[index])
return s.rpcOutputBlock(block, false, false, ctx)
return s.rpcOutputBlock(block, false, false)
}
return nil, err
}
@ -432,7 +432,7 @@ func (s *BlockChainAPI) GetUncleByBlockHashAndIndex(ctx context.Context, blockHa
return nil, nil
}
block = types.NewBlockWithHeader(uncles[index])
return s.rpcOutputBlock(block, false, false, ctx)
return s.rpcOutputBlock(block, false, false)
}
return nil, err
}
@ -1411,29 +1411,26 @@ func RPCMarshalHeader(head *types.Header) map[string]interface{} {
return result
}
// rpcOutputBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
// RPCMarshalBlock converts the given block to the RPC output which depends on fullTx. If inclTx is true transactions are
// returned. When fullTx is true the returned block contains full transaction details, otherwise it will only contain
// transaction hashes.
func (s *BlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool, ctx context.Context) (map[string]interface{}, error) {
func RPCMarshalBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) {
fields := RPCMarshalHeader(b.Header())
fields["size"] = hexutil.Uint64(b.Size())
fields["totalDifficulty"] = (*hexutil.Big)(s.b.GetTd(context.Background(), b.Hash()))
if inclTx {
formatTx := func(tx *types.Transaction) (interface{}, error) {
return tx.Hash(), nil
}
if fullTx {
formatTx = func(tx *types.Transaction) (interface{}, error) {
return newRPCTransactionFromBlockHash(b, tx.Hash()), nil
}
}
txs := b.Transactions()
transactions := make([]interface{}, len(txs))
var err error
for i, tx := range b.Transactions() {
for i, tx := range txs {
if transactions[i], err = formatTx(tx); err != nil {
return nil, err
}
@ -1447,9 +1444,21 @@ func (s *BlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool,
uncleHashes[i] = uncle.Hash()
}
fields["uncles"] = uncleHashes
return fields, nil
}
// rpcOutputBlock uses the generalized output filler, then adds the total difficulty field, which requires
// a `BlockChainAPI`.
func (s *BlockChainAPI) rpcOutputBlock(b *types.Block, inclTx bool, fullTx bool) (map[string]interface{}, error) {
fields, err := RPCMarshalBlock(b, inclTx, fullTx)
if err != nil {
return nil, err
}
fields["totalDifficulty"] = (*hexutil.Big)(s.b.GetTd(context.Background(), b.Hash()))
return fields, err
}
// findNearestSignedBlock finds the nearest checkpoint from input block
func (s *BlockChainAPI) findNearestSignedBlock(ctx context.Context, b *types.Block) *types.Block {
if b.Number().Int64() <= 0 {