core: eth: rpc: implement safe rpc block (#25165)
* core: eth: rpc: implement safe rpc block * core: fix setHead, panics
This commit is contained in:
parent
f543e6b065
commit
c6dcd018d2
@ -51,6 +51,7 @@ var (
|
||||
headHeaderGauge = metrics.NewRegisteredGauge("chain/head/header", nil)
|
||||
headFastBlockGauge = metrics.NewRegisteredGauge("chain/head/receipt", nil)
|
||||
headFinalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil)
|
||||
headSafeBlockGauge = metrics.NewRegisteredGauge("chain/head/safe", nil)
|
||||
|
||||
accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil)
|
||||
accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil)
|
||||
@ -191,6 +192,7 @@ type BlockChain struct {
|
||||
currentBlock atomic.Value // Current head of the block chain
|
||||
currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)
|
||||
currentFinalizedBlock atomic.Value // Current finalized head
|
||||
currentSafeBlock atomic.Value // Current safe head
|
||||
|
||||
stateCache state.Database // State database to reuse between imports (contains state cache)
|
||||
bodyCache *lru.Cache // Cache for the most recent block bodies
|
||||
@ -267,6 +269,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
||||
bc.currentBlock.Store(nilBlock)
|
||||
bc.currentFastBlock.Store(nilBlock)
|
||||
bc.currentFinalizedBlock.Store(nilBlock)
|
||||
bc.currentSafeBlock.Store(nilBlock)
|
||||
|
||||
// Initialize the chain with ancient data if it isn't empty.
|
||||
var txIndexBlock uint64
|
||||
@ -464,11 +467,15 @@ func (bc *BlockChain) loadLastState() error {
|
||||
}
|
||||
}
|
||||
|
||||
// Restore the last known finalized block
|
||||
// Restore the last known finalized block and safe block
|
||||
// Note: the safe block is not stored on disk and it is set to the last
|
||||
// known finalized block on startup
|
||||
if head := rawdb.ReadFinalizedBlockHash(bc.db); head != (common.Hash{}) {
|
||||
if block := bc.GetBlockByHash(head); block != nil {
|
||||
bc.currentFinalizedBlock.Store(block)
|
||||
headFinalizedBlockGauge.Update(int64(block.NumberU64()))
|
||||
bc.currentSafeBlock.Store(block)
|
||||
headSafeBlockGauge.Update(int64(block.NumberU64()))
|
||||
}
|
||||
}
|
||||
// Issue a status log for the user
|
||||
@ -504,8 +511,23 @@ func (bc *BlockChain) SetHead(head uint64) error {
|
||||
// SetFinalized sets the finalized block.
|
||||
func (bc *BlockChain) SetFinalized(block *types.Block) {
|
||||
bc.currentFinalizedBlock.Store(block)
|
||||
if block != nil {
|
||||
rawdb.WriteFinalizedBlockHash(bc.db, block.Hash())
|
||||
headFinalizedBlockGauge.Update(int64(block.NumberU64()))
|
||||
} else {
|
||||
rawdb.WriteFinalizedBlockHash(bc.db, common.Hash{})
|
||||
headFinalizedBlockGauge.Update(0)
|
||||
}
|
||||
}
|
||||
|
||||
// SetSafe sets the safe block.
|
||||
func (bc *BlockChain) SetSafe(block *types.Block) {
|
||||
bc.currentSafeBlock.Store(block)
|
||||
if block != nil {
|
||||
headSafeBlockGauge.Update(int64(block.NumberU64()))
|
||||
} else {
|
||||
headSafeBlockGauge.Update(0)
|
||||
}
|
||||
}
|
||||
|
||||
// setHeadBeyondRoot rewinds the local chain to a new head with the extra condition
|
||||
@ -663,6 +685,16 @@ func (bc *BlockChain) setHeadBeyondRoot(head uint64, root common.Hash, repair bo
|
||||
bc.txLookupCache.Purge()
|
||||
bc.futureBlocks.Purge()
|
||||
|
||||
// Clear safe block, finalized block if needed
|
||||
if safe := bc.CurrentSafeBlock(); safe != nil && head < safe.NumberU64() {
|
||||
log.Warn("SetHead invalidated safe block")
|
||||
bc.SetSafe(nil)
|
||||
}
|
||||
if finalized := bc.CurrentFinalizedBlock(); finalized != nil && head < finalized.NumberU64() {
|
||||
log.Error("SetHead invalidated finalized block")
|
||||
bc.SetFinalized(nil)
|
||||
}
|
||||
|
||||
return rootNumber, bc.loadLastState()
|
||||
}
|
||||
|
||||
|
@ -55,6 +55,12 @@ func (bc *BlockChain) CurrentFinalizedBlock() *types.Block {
|
||||
return bc.currentFinalizedBlock.Load().(*types.Block)
|
||||
}
|
||||
|
||||
// CurrentSafeBlock retrieves the current safe block of the canonical
|
||||
// chain. The block is retrieved from the blockchain's internal cache.
|
||||
func (bc *BlockChain) CurrentSafeBlock() *types.Block {
|
||||
return bc.currentSafeBlock.Load().(*types.Block)
|
||||
}
|
||||
|
||||
// HasHeader checks if a block header is present in the database or not, caching
|
||||
// it if present.
|
||||
func (bc *BlockChain) HasHeader(hash common.Hash, number uint64) bool {
|
||||
|
@ -272,6 +272,8 @@ func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
|
||||
block = api.eth.blockchain.CurrentBlock()
|
||||
} else if blockNr == rpc.FinalizedBlockNumber {
|
||||
block = api.eth.blockchain.CurrentFinalizedBlock()
|
||||
} else if blockNr == rpc.SafeBlockNumber {
|
||||
block = api.eth.blockchain.CurrentSafeBlock()
|
||||
} else {
|
||||
block = api.eth.blockchain.GetBlockByNumber(uint64(blockNr))
|
||||
}
|
||||
@ -350,6 +352,8 @@ func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hex
|
||||
block = api.eth.blockchain.CurrentBlock()
|
||||
} else if number == rpc.FinalizedBlockNumber {
|
||||
block = api.eth.blockchain.CurrentFinalizedBlock()
|
||||
} else if number == rpc.SafeBlockNumber {
|
||||
block = api.eth.blockchain.CurrentSafeBlock()
|
||||
} else {
|
||||
block = api.eth.blockchain.GetBlockByNumber(uint64(number))
|
||||
}
|
||||
|
@ -74,7 +74,18 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb
|
||||
return b.eth.blockchain.CurrentBlock().Header(), nil
|
||||
}
|
||||
if number == rpc.FinalizedBlockNumber {
|
||||
return b.eth.blockchain.CurrentFinalizedBlock().Header(), nil
|
||||
block := b.eth.blockchain.CurrentFinalizedBlock()
|
||||
if block != nil {
|
||||
return block.Header(), nil
|
||||
}
|
||||
return nil, errors.New("finalized block not found")
|
||||
}
|
||||
if number == rpc.SafeBlockNumber {
|
||||
block := b.eth.blockchain.CurrentSafeBlock()
|
||||
if block != nil {
|
||||
return block.Header(), nil
|
||||
}
|
||||
return nil, errors.New("safe block not found")
|
||||
}
|
||||
return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil
|
||||
}
|
||||
@ -113,6 +124,9 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe
|
||||
if number == rpc.FinalizedBlockNumber {
|
||||
return b.eth.blockchain.CurrentFinalizedBlock(), nil
|
||||
}
|
||||
if number == rpc.SafeBlockNumber {
|
||||
return b.eth.blockchain.CurrentSafeBlock(), nil
|
||||
}
|
||||
return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil
|
||||
}
|
||||
|
||||
|
@ -235,6 +235,8 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
|
||||
log.Warn("Safe block not in canonical chain")
|
||||
return beacon.STATUS_INVALID, beacon.InvalidForkChoiceState.With(errors.New("safe block not in canonical chain"))
|
||||
}
|
||||
// Set the safe block
|
||||
api.eth.BlockChain().SetSafe(safeBlock)
|
||||
}
|
||||
// If payload generation was requested, create a new block to be potentially
|
||||
// sealed by the beacon client. The payload will be requested later, and we
|
||||
|
@ -3696,7 +3696,7 @@ var outputBigNumberFormatter = function (number) {
|
||||
};
|
||||
|
||||
var isPredefinedBlockNumber = function (blockNumber) {
|
||||
return blockNumber === 'latest' || blockNumber === 'pending' || blockNumber === 'earliest' || blockNumber === 'finalized';
|
||||
return blockNumber === 'latest' || blockNumber === 'pending' || blockNumber === 'earliest' || blockNumber === 'finalized' || blockNumber === 'safe';
|
||||
};
|
||||
|
||||
var inputDefaultBlockNumberFormatter = function (blockNumber) {
|
||||
|
10
rpc/types.go
10
rpc/types.go
@ -61,6 +61,7 @@ type jsonWriter interface {
|
||||
type BlockNumber int64
|
||||
|
||||
const (
|
||||
SafeBlockNumber = BlockNumber(-4)
|
||||
FinalizedBlockNumber = BlockNumber(-3)
|
||||
PendingBlockNumber = BlockNumber(-2)
|
||||
LatestBlockNumber = BlockNumber(-1)
|
||||
@ -92,6 +93,9 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
|
||||
case "finalized":
|
||||
*bn = FinalizedBlockNumber
|
||||
return nil
|
||||
case "safe":
|
||||
*bn = SafeBlockNumber
|
||||
return nil
|
||||
}
|
||||
|
||||
blckNum, err := hexutil.DecodeUint64(input)
|
||||
@ -118,6 +122,8 @@ func (bn BlockNumber) MarshalText() ([]byte, error) {
|
||||
return []byte("pending"), nil
|
||||
case FinalizedBlockNumber:
|
||||
return []byte("finalized"), nil
|
||||
case SafeBlockNumber:
|
||||
return []byte("safe"), nil
|
||||
default:
|
||||
return hexutil.Uint64(bn).MarshalText()
|
||||
}
|
||||
@ -168,6 +174,10 @@ func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error {
|
||||
bn := FinalizedBlockNumber
|
||||
bnh.BlockNumber = &bn
|
||||
return nil
|
||||
case "safe":
|
||||
bn := SafeBlockNumber
|
||||
bnh.BlockNumber = &bn
|
||||
return nil
|
||||
default:
|
||||
if len(input) == 66 {
|
||||
hash := common.Hash{}
|
||||
|
Loading…
Reference in New Issue
Block a user