Skip to content

Commit

Permalink
core: eth: rpc: implement safe rpc block (ethereum#25165)
Browse files Browse the repository at this point in the history
* core: eth: rpc: implement safe rpc block

* core: fix setHead, panics
  • Loading branch information
MariusVanDerWijden authored and blakehhuynh committed Oct 7, 2022
1 parent e270f4b commit 5026c55
Show file tree
Hide file tree
Showing 7 changed files with 73 additions and 5 deletions.
38 changes: 35 additions & 3 deletions core/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
rawdb.WriteFinalizedBlockHash(bc.db, block.Hash())
headFinalizedBlockGauge.Update(int64(block.NumberU64()))
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
Expand Down Expand Up @@ -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()
}

Expand Down
6 changes: 6 additions & 0 deletions core/blockchain_reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
4 changes: 4 additions & 0 deletions eth/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
}
Expand Down Expand Up @@ -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))
}
Expand Down
16 changes: 15 additions & 1 deletion eth/api_backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}

Expand Down
2 changes: 2 additions & 0 deletions eth/catalyst/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion internal/jsre/deps/web3.js
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
10 changes: 10 additions & 0 deletions rpc/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ type jsonWriter interface {
type BlockNumber int64

const (
SafeBlockNumber = BlockNumber(-4)
FinalizedBlockNumber = BlockNumber(-3)
PendingBlockNumber = BlockNumber(-2)
LatestBlockNumber = BlockNumber(-1)
Expand Down Expand Up @@ -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)
Expand All @@ -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()
}
Expand Down Expand Up @@ -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{}
Expand Down

0 comments on commit 5026c55

Please sign in to comment.