forked from cerc-io/plugeth
core, eth, internal, rpc: implement final block (#24282)
* eth: core: implement finalized block * eth/catalyst: fix final block * eth/catalyst: update finalized head gauge * internal/jsre/deps: updated web3.js to allow for finalized block * eth/catalyst: make sure only one thread can call fcu * eth/catalyst: nitpicks * eth/catalyst: use plain mutex * eth: nitpicks
This commit is contained in:
parent
57192bd0dc
commit
e6fa102eb0
@ -50,6 +50,7 @@ var (
|
|||||||
headBlockGauge = metrics.NewRegisteredGauge("chain/head/block", nil)
|
headBlockGauge = metrics.NewRegisteredGauge("chain/head/block", nil)
|
||||||
headHeaderGauge = metrics.NewRegisteredGauge("chain/head/header", nil)
|
headHeaderGauge = metrics.NewRegisteredGauge("chain/head/header", nil)
|
||||||
headFastBlockGauge = metrics.NewRegisteredGauge("chain/head/receipt", nil)
|
headFastBlockGauge = metrics.NewRegisteredGauge("chain/head/receipt", nil)
|
||||||
|
headFinalizedBlockGauge = metrics.NewRegisteredGauge("chain/head/finalized", nil)
|
||||||
|
|
||||||
accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil)
|
accountReadTimer = metrics.NewRegisteredTimer("chain/account/reads", nil)
|
||||||
accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil)
|
accountHashTimer = metrics.NewRegisteredTimer("chain/account/hashes", nil)
|
||||||
@ -189,6 +190,7 @@ type BlockChain struct {
|
|||||||
|
|
||||||
currentBlock atomic.Value // Current head of the block chain
|
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!)
|
currentFastBlock atomic.Value // Current head of the fast-sync chain (may be above the block chain!)
|
||||||
|
currentFinalizedBlock atomic.Value // Current finalized head
|
||||||
|
|
||||||
stateCache state.Database // State database to reuse between imports (contains state cache)
|
stateCache state.Database // State database to reuse between imports (contains state cache)
|
||||||
bodyCache *lru.Cache // Cache for the most recent block bodies
|
bodyCache *lru.Cache // Cache for the most recent block bodies
|
||||||
@ -264,6 +266,7 @@ func NewBlockChain(db ethdb.Database, cacheConfig *CacheConfig, chainConfig *par
|
|||||||
var nilBlock *types.Block
|
var nilBlock *types.Block
|
||||||
bc.currentBlock.Store(nilBlock)
|
bc.currentBlock.Store(nilBlock)
|
||||||
bc.currentFastBlock.Store(nilBlock)
|
bc.currentFastBlock.Store(nilBlock)
|
||||||
|
bc.currentFinalizedBlock.Store(nilBlock)
|
||||||
|
|
||||||
// Initialize the chain with ancient data if it isn't empty.
|
// Initialize the chain with ancient data if it isn't empty.
|
||||||
var txIndexBlock uint64
|
var txIndexBlock uint64
|
||||||
@ -460,8 +463,17 @@ func (bc *BlockChain) loadLastState() error {
|
|||||||
headFastBlockGauge.Update(int64(block.NumberU64()))
|
headFastBlockGauge.Update(int64(block.NumberU64()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restore the last known finalized block
|
||||||
|
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()))
|
||||||
|
}
|
||||||
|
}
|
||||||
// Issue a status log for the user
|
// Issue a status log for the user
|
||||||
currentFastBlock := bc.CurrentFastBlock()
|
currentFastBlock := bc.CurrentFastBlock()
|
||||||
|
currentFinalizedBlock := bc.CurrentFinalizedBlock()
|
||||||
|
|
||||||
headerTd := bc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64())
|
headerTd := bc.GetTd(currentHeader.Hash(), currentHeader.Number.Uint64())
|
||||||
blockTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
|
blockTd := bc.GetTd(currentBlock.Hash(), currentBlock.NumberU64())
|
||||||
@ -470,6 +482,11 @@ func (bc *BlockChain) loadLastState() error {
|
|||||||
log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd, "age", common.PrettyAge(time.Unix(int64(currentHeader.Time), 0)))
|
log.Info("Loaded most recent local header", "number", currentHeader.Number, "hash", currentHeader.Hash(), "td", headerTd, "age", common.PrettyAge(time.Unix(int64(currentHeader.Time), 0)))
|
||||||
log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd, "age", common.PrettyAge(time.Unix(int64(currentBlock.Time()), 0)))
|
log.Info("Loaded most recent local full block", "number", currentBlock.Number(), "hash", currentBlock.Hash(), "td", blockTd, "age", common.PrettyAge(time.Unix(int64(currentBlock.Time()), 0)))
|
||||||
log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), "hash", currentFastBlock.Hash(), "td", fastTd, "age", common.PrettyAge(time.Unix(int64(currentFastBlock.Time()), 0)))
|
log.Info("Loaded most recent local fast block", "number", currentFastBlock.Number(), "hash", currentFastBlock.Hash(), "td", fastTd, "age", common.PrettyAge(time.Unix(int64(currentFastBlock.Time()), 0)))
|
||||||
|
|
||||||
|
if currentFinalizedBlock != nil {
|
||||||
|
finalTd := bc.GetTd(currentFinalizedBlock.Hash(), currentFinalizedBlock.NumberU64())
|
||||||
|
log.Info("Loaded most recent local finalized block", "number", currentFinalizedBlock.Number(), "hash", currentFinalizedBlock.Hash(), "td", finalTd, "age", common.PrettyAge(time.Unix(int64(currentFinalizedBlock.Time()), 0)))
|
||||||
|
}
|
||||||
if pivot := rawdb.ReadLastPivotNumber(bc.db); pivot != nil {
|
if pivot := rawdb.ReadLastPivotNumber(bc.db); pivot != nil {
|
||||||
log.Info("Loaded last fast-sync pivot marker", "number", *pivot)
|
log.Info("Loaded last fast-sync pivot marker", "number", *pivot)
|
||||||
}
|
}
|
||||||
@ -484,6 +501,13 @@ func (bc *BlockChain) SetHead(head uint64) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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()))
|
||||||
|
}
|
||||||
|
|
||||||
// setHeadBeyondRoot rewinds the local chain to a new head with the extra condition
|
// setHeadBeyondRoot rewinds the local chain to a new head with the extra condition
|
||||||
// that the rewind must pass the specified state root. This method is meant to be
|
// that the rewind must pass the specified state root. This method is meant to be
|
||||||
// used when rewinding with snapshots enabled to ensure that we go back further than
|
// used when rewinding with snapshots enabled to ensure that we go back further than
|
||||||
|
@ -49,6 +49,12 @@ func (bc *BlockChain) CurrentFastBlock() *types.Block {
|
|||||||
return bc.currentFastBlock.Load().(*types.Block)
|
return bc.currentFastBlock.Load().(*types.Block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CurrentFinalizedBlock retrieves the current finalized block of the canonical
|
||||||
|
// chain. The block is retrieved from the blockchain's internal cache.
|
||||||
|
func (bc *BlockChain) CurrentFinalizedBlock() *types.Block {
|
||||||
|
return bc.currentFinalizedBlock.Load().(*types.Block)
|
||||||
|
}
|
||||||
|
|
||||||
// HasHeader checks if a block header is present in the database or not, caching
|
// HasHeader checks if a block header is present in the database or not, caching
|
||||||
// it if present.
|
// it if present.
|
||||||
func (bc *BlockChain) HasHeader(hash common.Hash, number uint64) bool {
|
func (bc *BlockChain) HasHeader(hash common.Hash, number uint64) bool {
|
||||||
|
@ -216,6 +216,22 @@ func WriteHeadFastBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ReadFinalizedBlockHash retrieves the hash of the finalized block.
|
||||||
|
func ReadFinalizedBlockHash(db ethdb.KeyValueReader) common.Hash {
|
||||||
|
data, _ := db.Get(headFinalizedBlockKey)
|
||||||
|
if len(data) == 0 {
|
||||||
|
return common.Hash{}
|
||||||
|
}
|
||||||
|
return common.BytesToHash(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// WriteFinalizedBlockHash stores the hash of the finalized block.
|
||||||
|
func WriteFinalizedBlockHash(db ethdb.KeyValueWriter, hash common.Hash) {
|
||||||
|
if err := db.Put(headFinalizedBlockKey, hash.Bytes()); err != nil {
|
||||||
|
log.Crit("Failed to store last finalized block's hash", "err", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// ReadLastPivotNumber retrieves the number of the last pivot block. If the node
|
// ReadLastPivotNumber retrieves the number of the last pivot block. If the node
|
||||||
// full synced, the last pivot will always be nil.
|
// full synced, the last pivot will always be nil.
|
||||||
func ReadLastPivotNumber(db ethdb.KeyValueReader) *uint64 {
|
func ReadLastPivotNumber(db ethdb.KeyValueReader) *uint64 {
|
||||||
|
@ -418,8 +418,8 @@ func InspectDatabase(db ethdb.Database, keyPrefix, keyStart []byte) error {
|
|||||||
default:
|
default:
|
||||||
var accounted bool
|
var accounted bool
|
||||||
for _, meta := range [][]byte{
|
for _, meta := range [][]byte{
|
||||||
databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, lastPivotKey,
|
databaseVersionKey, headHeaderKey, headBlockKey, headFastBlockKey, headFinalizedBlockKey,
|
||||||
fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey,
|
lastPivotKey, fastTrieProgressKey, snapshotDisabledKey, SnapshotRootKey, snapshotJournalKey,
|
||||||
snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey,
|
snapshotGeneratorKey, snapshotRecoveryKey, txIndexTailKey, fastTxLookupLimitKey,
|
||||||
uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey,
|
uncleanShutdownKey, badBlockKey, transitionStatusKey, skeletonSyncStatusKey,
|
||||||
} {
|
} {
|
||||||
|
@ -39,6 +39,9 @@ var (
|
|||||||
// headFastBlockKey tracks the latest known incomplete block's hash during fast sync.
|
// headFastBlockKey tracks the latest known incomplete block's hash during fast sync.
|
||||||
headFastBlockKey = []byte("LastFast")
|
headFastBlockKey = []byte("LastFast")
|
||||||
|
|
||||||
|
// headFinalizedBlockKey tracks the latest known finalized block hash.
|
||||||
|
headFinalizedBlockKey = []byte("LastFinalized")
|
||||||
|
|
||||||
// lastPivotKey tracks the last pivot block used by fast sync (to reenable on sethead).
|
// lastPivotKey tracks the last pivot block used by fast sync (to reenable on sethead).
|
||||||
lastPivotKey = []byte("LastPivot")
|
lastPivotKey = []byte("LastPivot")
|
||||||
|
|
||||||
|
@ -285,6 +285,8 @@ func (api *PublicDebugAPI) DumpBlock(blockNr rpc.BlockNumber) (state.Dump, error
|
|||||||
var block *types.Block
|
var block *types.Block
|
||||||
if blockNr == rpc.LatestBlockNumber {
|
if blockNr == rpc.LatestBlockNumber {
|
||||||
block = api.eth.blockchain.CurrentBlock()
|
block = api.eth.blockchain.CurrentBlock()
|
||||||
|
} else if blockNr == rpc.FinalizedBlockNumber {
|
||||||
|
block = api.eth.blockchain.CurrentFinalizedBlock()
|
||||||
} else {
|
} else {
|
||||||
block = api.eth.blockchain.GetBlockByNumber(uint64(blockNr))
|
block = api.eth.blockchain.GetBlockByNumber(uint64(blockNr))
|
||||||
}
|
}
|
||||||
@ -373,6 +375,8 @@ func (api *PublicDebugAPI) AccountRange(blockNrOrHash rpc.BlockNumberOrHash, sta
|
|||||||
var block *types.Block
|
var block *types.Block
|
||||||
if number == rpc.LatestBlockNumber {
|
if number == rpc.LatestBlockNumber {
|
||||||
block = api.eth.blockchain.CurrentBlock()
|
block = api.eth.blockchain.CurrentBlock()
|
||||||
|
} else if number == rpc.FinalizedBlockNumber {
|
||||||
|
block = api.eth.blockchain.CurrentFinalizedBlock()
|
||||||
} else {
|
} else {
|
||||||
block = api.eth.blockchain.GetBlockByNumber(uint64(number))
|
block = api.eth.blockchain.GetBlockByNumber(uint64(number))
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,9 @@ func (b *EthAPIBackend) HeaderByNumber(ctx context.Context, number rpc.BlockNumb
|
|||||||
if number == rpc.LatestBlockNumber {
|
if number == rpc.LatestBlockNumber {
|
||||||
return b.eth.blockchain.CurrentBlock().Header(), nil
|
return b.eth.blockchain.CurrentBlock().Header(), nil
|
||||||
}
|
}
|
||||||
|
if number == rpc.FinalizedBlockNumber {
|
||||||
|
return b.eth.blockchain.CurrentFinalizedBlock().Header(), nil
|
||||||
|
}
|
||||||
return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil
|
return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,6 +110,9 @@ func (b *EthAPIBackend) BlockByNumber(ctx context.Context, number rpc.BlockNumbe
|
|||||||
if number == rpc.LatestBlockNumber {
|
if number == rpc.LatestBlockNumber {
|
||||||
return b.eth.blockchain.CurrentBlock(), nil
|
return b.eth.blockchain.CurrentBlock(), nil
|
||||||
}
|
}
|
||||||
|
if number == rpc.FinalizedBlockNumber {
|
||||||
|
return b.eth.blockchain.CurrentFinalizedBlock(), nil
|
||||||
|
}
|
||||||
return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil
|
return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,7 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/ethereum/go-ethereum/common"
|
"github.com/ethereum/go-ethereum/common"
|
||||||
@ -60,6 +61,8 @@ type ConsensusAPI struct {
|
|||||||
eth *eth.Ethereum
|
eth *eth.Ethereum
|
||||||
remoteBlocks *headerQueue // Cache of remote payloads received
|
remoteBlocks *headerQueue // Cache of remote payloads received
|
||||||
localBlocks *payloadQueue // Cache of local payloads generated
|
localBlocks *payloadQueue // Cache of local payloads generated
|
||||||
|
// Lock for the forkChoiceUpdated method
|
||||||
|
forkChoiceLock sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewConsensusAPI creates a new consensus api for the given backend.
|
// NewConsensusAPI creates a new consensus api for the given backend.
|
||||||
@ -86,11 +89,15 @@ func NewConsensusAPI(eth *eth.Ethereum) *ConsensusAPI {
|
|||||||
// If there are payloadAttributes:
|
// If there are payloadAttributes:
|
||||||
// we try to assemble a block with the payloadAttributes and return its payloadID
|
// we try to assemble a block with the payloadAttributes and return its payloadID
|
||||||
func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributesV1) (beacon.ForkChoiceResponse, error) {
|
func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, payloadAttributes *beacon.PayloadAttributesV1) (beacon.ForkChoiceResponse, error) {
|
||||||
|
api.forkChoiceLock.Lock()
|
||||||
|
defer api.forkChoiceLock.Unlock()
|
||||||
|
|
||||||
log.Trace("Engine API request received", "method", "ForkchoiceUpdated", "head", update.HeadBlockHash, "finalized", update.FinalizedBlockHash, "safe", update.SafeBlockHash)
|
log.Trace("Engine API request received", "method", "ForkchoiceUpdated", "head", update.HeadBlockHash, "finalized", update.FinalizedBlockHash, "safe", update.SafeBlockHash)
|
||||||
if update.HeadBlockHash == (common.Hash{}) {
|
if update.HeadBlockHash == (common.Hash{}) {
|
||||||
log.Warn("Forkchoice requested update to zero hash")
|
log.Warn("Forkchoice requested update to zero hash")
|
||||||
return beacon.STATUS_INVALID, nil // TODO(karalabe): Why does someone send us this?
|
return beacon.STATUS_INVALID, nil // TODO(karalabe): Why does someone send us this?
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check whether we have the block yet in our database or not. If not, we'll
|
// Check whether we have the block yet in our database or not. If not, we'll
|
||||||
// need to either trigger a sync, or to reject this forkchoice update for a
|
// need to either trigger a sync, or to reject this forkchoice update for a
|
||||||
// reason.
|
// reason.
|
||||||
@ -154,7 +161,7 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
|
|||||||
if merger := api.eth.Merger(); !merger.PoSFinalized() {
|
if merger := api.eth.Merger(); !merger.PoSFinalized() {
|
||||||
merger.FinalizePoS()
|
merger.FinalizePoS()
|
||||||
}
|
}
|
||||||
// TODO (MariusVanDerWijden): If the finalized block is not in our canonical tree, somethings wrong
|
// If the finalized block is not in our canonical tree, somethings wrong
|
||||||
finalBlock := api.eth.BlockChain().GetBlockByHash(update.FinalizedBlockHash)
|
finalBlock := api.eth.BlockChain().GetBlockByHash(update.FinalizedBlockHash)
|
||||||
if finalBlock == nil {
|
if finalBlock == nil {
|
||||||
log.Warn("Final block not available in database", "hash", update.FinalizedBlockHash)
|
log.Warn("Final block not available in database", "hash", update.FinalizedBlockHash)
|
||||||
@ -163,8 +170,10 @@ func (api *ConsensusAPI) ForkchoiceUpdatedV1(update beacon.ForkchoiceStateV1, pa
|
|||||||
log.Warn("Final block not in canonical chain", "number", block.NumberU64(), "hash", update.HeadBlockHash)
|
log.Warn("Final block not in canonical chain", "number", block.NumberU64(), "hash", update.HeadBlockHash)
|
||||||
return beacon.STATUS_INVALID, errors.New("final block not canonical")
|
return beacon.STATUS_INVALID, errors.New("final block not canonical")
|
||||||
}
|
}
|
||||||
|
// Set the finalized block
|
||||||
|
api.eth.BlockChain().SetFinalized(finalBlock)
|
||||||
}
|
}
|
||||||
// TODO (MariusVanDerWijden): Check if the safe block hash is in our canonical tree, if not somethings wrong
|
// Check if the safe block hash is in our canonical tree, if not somethings wrong
|
||||||
if update.SafeBlockHash != (common.Hash{}) {
|
if update.SafeBlockHash != (common.Hash{}) {
|
||||||
safeBlock := api.eth.BlockChain().GetBlockByHash(update.SafeBlockHash)
|
safeBlock := api.eth.BlockChain().GetBlockByHash(update.SafeBlockHash)
|
||||||
if safeBlock == nil {
|
if safeBlock == nil {
|
||||||
|
@ -476,7 +476,10 @@ func TestFullAPI(t *testing.T) {
|
|||||||
t.Fatalf("Failed to insert block: %v", err)
|
t.Fatalf("Failed to insert block: %v", err)
|
||||||
}
|
}
|
||||||
if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number {
|
if ethservice.BlockChain().CurrentBlock().NumberU64() != payload.Number {
|
||||||
t.Fatalf("Chain head should be updated")
|
t.Fatal("Chain head should be updated")
|
||||||
|
}
|
||||||
|
if ethservice.BlockChain().CurrentFinalizedBlock().NumberU64() != payload.Number-1 {
|
||||||
|
t.Fatal("Finalized block should be updated")
|
||||||
}
|
}
|
||||||
parent = ethservice.BlockChain().CurrentBlock()
|
parent = ethservice.BlockChain().CurrentBlock()
|
||||||
}
|
}
|
||||||
|
@ -3696,7 +3696,7 @@ var outputBigNumberFormatter = function (number) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var isPredefinedBlockNumber = function (blockNumber) {
|
var isPredefinedBlockNumber = function (blockNumber) {
|
||||||
return blockNumber === 'latest' || blockNumber === 'pending' || blockNumber === 'earliest';
|
return blockNumber === 'latest' || blockNumber === 'pending' || blockNumber === 'earliest' || blockNumber === 'finalized';
|
||||||
};
|
};
|
||||||
|
|
||||||
var inputDefaultBlockNumberFormatter = function (blockNumber) {
|
var inputDefaultBlockNumberFormatter = function (blockNumber) {
|
||||||
|
10
rpc/types.go
10
rpc/types.go
@ -61,6 +61,7 @@ type jsonWriter interface {
|
|||||||
type BlockNumber int64
|
type BlockNumber int64
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
FinalizedBlockNumber = BlockNumber(-3)
|
||||||
PendingBlockNumber = BlockNumber(-2)
|
PendingBlockNumber = BlockNumber(-2)
|
||||||
LatestBlockNumber = BlockNumber(-1)
|
LatestBlockNumber = BlockNumber(-1)
|
||||||
EarliestBlockNumber = BlockNumber(0)
|
EarliestBlockNumber = BlockNumber(0)
|
||||||
@ -88,6 +89,9 @@ func (bn *BlockNumber) UnmarshalJSON(data []byte) error {
|
|||||||
case "pending":
|
case "pending":
|
||||||
*bn = PendingBlockNumber
|
*bn = PendingBlockNumber
|
||||||
return nil
|
return nil
|
||||||
|
case "finalized":
|
||||||
|
*bn = FinalizedBlockNumber
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
blckNum, err := hexutil.DecodeUint64(input)
|
blckNum, err := hexutil.DecodeUint64(input)
|
||||||
@ -112,6 +116,8 @@ func (bn BlockNumber) MarshalText() ([]byte, error) {
|
|||||||
return []byte("latest"), nil
|
return []byte("latest"), nil
|
||||||
case PendingBlockNumber:
|
case PendingBlockNumber:
|
||||||
return []byte("pending"), nil
|
return []byte("pending"), nil
|
||||||
|
case FinalizedBlockNumber:
|
||||||
|
return []byte("finalized"), nil
|
||||||
default:
|
default:
|
||||||
return hexutil.Uint64(bn).MarshalText()
|
return hexutil.Uint64(bn).MarshalText()
|
||||||
}
|
}
|
||||||
@ -158,6 +164,10 @@ func (bnh *BlockNumberOrHash) UnmarshalJSON(data []byte) error {
|
|||||||
bn := PendingBlockNumber
|
bn := PendingBlockNumber
|
||||||
bnh.BlockNumber = &bn
|
bnh.BlockNumber = &bn
|
||||||
return nil
|
return nil
|
||||||
|
case "finalized":
|
||||||
|
bn := FinalizedBlockNumber
|
||||||
|
bnh.BlockNumber = &bn
|
||||||
|
return nil
|
||||||
default:
|
default:
|
||||||
if len(input) == 66 {
|
if len(input) == 66 {
|
||||||
hash := common.Hash{}
|
hash := common.Hash{}
|
||||||
|
@ -1 +1 @@
|
|||||||
Subproject commit 092a8834dc445e683103689d6f0e75a5d380a190
|
Subproject commit a380655e5ffab1a5ea0f4d860224bdb19013f06a
|
Loading…
Reference in New Issue
Block a user