Skip to content

Commit c6dcd01

Browse files
core: eth: rpc: implement safe rpc block (#25165)
* core: eth: rpc: implement safe rpc block * core: fix setHead, panics
1 parent f543e6b commit c6dcd01

File tree

7 files changed

+73
-5
lines changed

7 files changed

+73
-5
lines changed

core/blockchain.go

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ var (
5151
headHeaderGauge = metrics.NewRegisteredGauge("chain/head/header", nil)
5252
headFastBlockGauge = metrics.NewRegisteredGauge("chain/head/receipt", nil)
5353
headFinalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil)
54+
headSafeBlockGauge = metrics.NewRegisteredGauge("chain/head/safe", nil)
5455

5556
accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil)
5657
accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil)
@@ -191,6 +192,7 @@ type BlockChain struct {
191192
currentBlock atomic.Value // Current head of the block chain
192193
currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)
193194
currentFinalizedBlock atomic.Value // Current finalized head
195+
currentSafeBlock atomic.Value // Current safe head
194196

195197
stateCache state.Database // State database to reuse between imports (contains state cache)
196198
bodyCache *lru.Cache // Cache for the most recent block bodies
@@ -267,6 +269,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
267269
bc.currentBlock.Store(nilBlock)
268270
bc.currentFastBlock.Store(nilBlock)
269271
bc.currentFinalizedBlock.Store(nilBlock)
272+
bc.currentSafeBlock.Store(nilBlock)
270273

271274
// Initialize the chain with ancient data if it isn't empty.
272275
var txIndexBlock uint64
@@ -464,11 +467,15 @@ func (bc *BlockChain) loadLastState() error {
464467
}
465468
}
466469

467-
// Restore the last known finalized block
470+
// Restore the last known finalized block and safe block
471+
// Note: the safe block is not stored on disk and it is set to the last
472+
// known finalized block on startup
468473
if head := rawdb.ReadFinalizedBlockHash(bc.db); head != (common.Hash{}) {
469474
if block := bc.GetBlockByHash(head); block != nil {
470475
bc.currentFinalizedBlock.Store(block)
471476
headFinalizedBlockGauge.Update(int64(block.NumberU64()))
477+
bc.currentSafeBlock.Store(block)
478+
headSafeBlockGauge.Update(int64(block.NumberU64()))
472479
}
473480
}
474481
// Issue a status log for the user
@@ -504,8 +511,23 @@ func (bc *BlockChain) SetHead(head uint64) error {
504511
// SetFinalized sets the finalized block.
505512
func (bc *BlockChain) SetFinalized(block *types.Block) {
506513
bc.currentFinalizedBlock.Store(block)
507-
rawdb.WriteFinalizedBlockHash(bc.db, block.Hash())
508-
headFinalizedBlockGauge.Update(int64(block.NumberU64()))
514+
if block != nil {
515+
rawdb.WriteFinalizedBlockHash(bc.db, block.Hash())
516+
headFinalizedBlockGauge.Update(int64(block.NumberU64()))
517+
} else {
518+
rawdb.WriteFinalizedBlockHash(bc.db, common.Hash{})
519+
headFinalizedBlockGauge.Update(0)
520+
}
521+
}
522+
523+
// SetSafe sets the safe block.
524+
func (bc *BlockChain) SetSafe(block *types.Block) {
525+
bc.currentSafeBlock.Store(block)
526+
if block != nil {
527+
headSafeBlockGauge.Update(int64(block.NumberU64()))
528+
} else {
529+
headSafeBlockGauge.Update(0)
530+
}
509531
}
510532

511533
// 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
663685
bc.txLookupCache.Purge()
664686
bc.futureBlocks.Purge()
665687

688+
// Clear safe block, finalized block if needed
689+
if safe := bc.CurrentSafeBlock(); safe != nil && head < safe.NumberU64() {
690+
log.Warn("SetHead invalidated safe block")
691+
bc.SetSafe(nil)
692+
}
693+
if finalized := bc.CurrentFinalizedBlock(); finalized != nil && head < finalized.NumberU64() {
694+
log.Error("SetHead invalidated finalized block")
695+
bc.SetFinalized(nil)
696+
}
697+
666698
return rootNumber, bc.loadLastState()
667699
}
668700

core/blockchain_reader.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,12 @@ func (bc *BlockChain) CurrentFinalizedBlock() *types.Block {
5555
return bc.currentFinalizedBlock.Load().(*types.Block)
5656
}
5757

58+
// CurrentSafeBlock retrieves the current safe block of the canonical
59+
// chain. The block is retrieved from the blockchain's internal cache.
60+
func (bc *BlockChain) CurrentSafeBlock() *types.Block {
61+
return bc.currentSafeBlock.Load().(*types.Block)
62+
}
63+
5864
// HasHeader checks if a block header is present in the database or not, caching
5965
// it if present.
6066
func (bc *BlockChain) HasHeader(hash common.Hash, number uint64) bool {

eth/api.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,8 @@ func (api *DebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error) {
272272
block = api.eth.blockchain.CurrentBlock()
273273
} else if blockNr == rpc.FinalizedBlockNumber {
274274
block = api.eth.blockchain.CurrentFinalizedBlock()
275+
} else if blockNr == rpc.SafeBlockNumber {
276+
block = api.eth.blockchain.CurrentSafeBlock()
275277
} else {
276278
block = api.eth.blockchain.GetBlockByNumber(uint64(blockNr))
277279
}
@@ -350,6 +352,8 @@ func (api *DebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, start hex
350352
block = api.eth.blockchain.CurrentBlock()
351353
} else if number == rpc.FinalizedBlockNumber {
352354
block = api.eth.blockchain.CurrentFinalizedBlock()
355+
} else if number == rpc.SafeBlockNumber {
356+
block = api.eth.blockchain.CurrentSafeBlock()
353357
} else {
354358
block = api.eth.blockchain.GetBlockByNumber(uint64(number))
355359
}

eth/api_backend.go

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,18 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb
7474
return b.eth.blockchain.CurrentBlock().Header(), nil
7575
}
7676
if number == rpc.FinalizedBlockNumber {
77-
return b.eth.blockchain.CurrentFinalizedBlock().Header(), nil
77+
block := b.eth.blockchain.CurrentFinalizedBlock()
78+
if block != nil {
79+
return block.Header(), nil
80+
}
81+
return nil, errors.New("finalized block not found")
82+
}
83+
if number == rpc.SafeBlockNumber {
84+
block := b.eth.blockchain.CurrentSafeBlock()
85+
if block != nil {
86+
return block.Header(), nil
87+
}
88+
return nil, errors.New("safe block not found")
7889
}
7990
return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil
8091
}
@@ -113,6 +124,9 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe
113124
if number == rpc.FinalizedBlockNumber {
114125
return b.eth.blockchain.CurrentFinalizedBlock(), nil
115126
}
127+
if number == rpc.SafeBlockNumber {
128+
return b.eth.blockchain.CurrentSafeBlock(), nil
129+
}
116130
return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil
117131
}
118132

eth/catalyst/api.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,8 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
235235
log.Warn("Safe block not in canonical chain")
236236
return beacon.STATUS_INVALID, beacon.InvalidForkChoiceState.With(errors.New("safe block not in canonical chain"))
237237
}
238+
// Set the safe block
239+
api.eth.BlockChain().SetSafe(safeBlock)
238240
}
239241
// If payload generation was requested, create a new block to be potentially
240242
// sealed by the beacon client. The payload will be requested later, and we

internal/jsre/deps/web3.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3696,7 +3696,7 @@ var outputBigNumberFormatter = function (number) {
36963696
};
36973697

36983698
var isPredefinedBlockNumber = function (blockNumber) {
3699-
return blockNumber === 'latest' || blockNumber === 'pending' || blockNumber === 'earliest' || blockNumber === 'finalized';
3699+
return blockNumber === 'latest' || blockNumber === 'pending' || blockNumber === 'earliest' || blockNumber === 'finalized' || blockNumber === 'safe';
37003700
};
37013701

37023702
var inputDefaultBlockNumberFormatter = function (blockNumber) {

rpc/types.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ type jsonWriter interface {
6161
type BlockNumber int64
6262

6363
const (
64+
SafeBlockNumber = BlockNumber(-4)
6465
FinalizedBlockNumber = BlockNumber(-3)
6566
PendingBlockNumber = BlockNumber(-2)
6667
LatestBlockNumber = BlockNumber(-1)
@@ -92,6 +93,9 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
9293
case "finalized":
9394
*bn = FinalizedBlockNumber
9495
return nil
96+
case "safe":
97+
*bn = SafeBlockNumber
98+
return nil
9599
}
96100

97101
blckNum, err := hexutil.DecodeUint64(input)
@@ -118,6 +122,8 @@ func (bn BlockNumber) MarshalText() ([]byte, error) {
118122
return []byte("pending"), nil
119123
case FinalizedBlockNumber:
120124
return []byte("finalized"), nil
125+
case SafeBlockNumber:
126+
return []byte("safe"), nil
121127
default:
122128
return hexutil.Uint64(bn).MarshalText()
123129
}
@@ -168,6 +174,10 @@ func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error {
168174
bn := FinalizedBlockNumber
169175
bnh.BlockNumber = &bn
170176
return nil
177+
case "safe":
178+
bn := SafeBlockNumber
179+
bnh.BlockNumber = &bn
180+
return nil
171181
default:
172182
if len(input) == 66 {
173183
hash := common.Hash{}

0 commit comments

Comments
 (0)