feat(rpc): add scroll_* rollup APIs (#548)

* feat(rpc): add scroll_* rollup APIs

* address comments

* address comments

* address comments

* support finalized tag in eth_feeHistory

* Update internal/web3ext/web3ext.go

Co-authored-by: Péter Garamvölgyi <peter@scroll.io>

* tweak

---------

Co-authored-by: Péter Garamvölgyi <peter@scroll.io>
This commit is contained in:
colin 2023-11-09 16:55:13 +08:00 committed by GitHub
parent a8a9dc83cb
commit 1bf26286db
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 89 additions and 1 deletions

View file

@ -715,6 +715,60 @@ func (api *ScrollAPI) GetNumSkippedTransactions(ctx context.Context) (uint64, er
return rawdb.ReadNumSkippedTransactions(api.eth.ChainDb()), nil return rawdb.ReadNumSkippedTransactions(api.eth.ChainDb()), nil
} }
// SyncStatus includes L2 block sync height, L1 rollup sync height,
// L1 message sync height, and L2 finalized block height.
type SyncStatus struct {
L2BlockSyncHeight uint64 `json:"l2BlockSyncHeight,omitempty"`
L1RollupSyncHeight uint64 `json:"l1RollupSyncHeight,omitempty"`
L1MessageSyncHeight uint64 `json:"l1MessageSyncHeight,omitempty"`
L2FinalizedBlockHeight uint64 `json:"l2FinalizedBlockHeight,omitempty"`
}
// SyncStatus returns the overall rollup status including L2 block sync height, L1 rollup sync height,
// L1 message sync height, and L2 finalized block height.
func (api *ScrollAPI) SyncStatus(_ context.Context) *SyncStatus {
status := &SyncStatus{}
l2BlockHeader := api.eth.blockchain.CurrentHeader()
if l2BlockHeader != nil {
status.L2BlockSyncHeight = l2BlockHeader.Number.Uint64()
}
l1RollupSyncHeightPtr := rawdb.ReadRollupEventSyncedL1BlockNumber(api.eth.ChainDb())
if l1RollupSyncHeightPtr != nil {
status.L1RollupSyncHeight = *l1RollupSyncHeightPtr
}
l1MessageSyncHeightPtr := rawdb.ReadSyncedL1BlockNumber(api.eth.ChainDb())
if l1MessageSyncHeightPtr != nil {
status.L1MessageSyncHeight = *l1MessageSyncHeightPtr
}
l2FinalizedBlockHeightPtr := rawdb.ReadFinalizedL2BlockNumber(api.eth.ChainDb())
if l2FinalizedBlockHeightPtr != nil {
status.L2FinalizedBlockHeight = *l2FinalizedBlockHeightPtr
}
return status
}
// EstimateL1DataFee returns an estimate of the L1 data fee required to
// process the given transaction against the current pending block.
func (api *ScrollAPI) EstimateL1DataFee(ctx context.Context, args ethapi.TransactionArgs, blockNrOrHash *rpc.BlockNumberOrHash) (*hexutil.Uint64, error) {
bNrOrHash := rpc.BlockNumberOrHashWithNumber(rpc.PendingBlockNumber)
if blockNrOrHash != nil {
bNrOrHash = *blockNrOrHash
}
l1DataFee, err := ethapi.EstimateL1MsgFee(ctx, api.eth.APIBackend, args, bNrOrHash, nil, 0, api.eth.APIBackend.RPCGasCap(), api.eth.APIBackend.ChainConfig())
if err != nil {
return nil, fmt.Errorf("failed to estimate L1 data fee: %w", err)
}
result := hexutil.Uint64(l1DataFee.Uint64())
return &result, nil
}
// RPCTransaction is the standard RPC transaction return type with some additional skip-related fields. // RPCTransaction is the standard RPC transaction return type with some additional skip-related fields.
type RPCTransaction struct { type RPCTransaction struct {
ethapi.RPCTransaction ethapi.RPCTransaction

View file

@ -76,6 +76,14 @@ 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 {
finalizedBlockHeightPtr := rawdb.ReadFinalizedL2BlockNumber(b.eth.ChainDb())
if finalizedBlockHeightPtr == nil {
return nil, errors.New("L2 finalized block height not found in database")
}
number = rpc.BlockNumber(*finalizedBlockHeightPtr)
return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil
}
return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil return b.eth.blockchain.GetHeaderByNumber(uint64(number)), nil
} }
@ -110,6 +118,14 @@ 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 {
finalizedBlockHeightPtr := rawdb.ReadFinalizedL2BlockNumber(b.eth.ChainDb())
if finalizedBlockHeightPtr == nil {
return nil, errors.New("L2 finalized block height not found in database")
}
number = rpc.BlockNumber(*finalizedBlockHeightPtr)
return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil
}
return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil return b.eth.blockchain.GetBlockByNumber(uint64(number)), nil
} }

View file

@ -170,6 +170,13 @@ func (oracle *Oracle) resolveBlockRange(ctx context.Context, lastBlock rpc.Block
} else if pendingBlock == nil && lastBlock > headBlock { } else if pendingBlock == nil && lastBlock > headBlock {
return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, lastBlock, headBlock) return nil, nil, 0, 0, fmt.Errorf("%w: requested %d, head %d", errRequestBeyondHead, lastBlock, headBlock)
} }
if lastBlock == rpc.FinalizedBlockNumber {
if latestFinalizedHeader, err := oracle.backend.HeaderByNumber(ctx, rpc.FinalizedBlockNumber); err == nil {
lastBlock = rpc.BlockNumber(latestFinalizedHeader.Number.Uint64())
} else {
return nil, nil, 0, 0, err
}
}
// ensure not trying to retrieve before genesis // ensure not trying to retrieve before genesis
if rpc.BlockNumber(blocks) > lastBlock+1 { if rpc.BlockNumber(blocks) > lastBlock+1 {
blocks = int(lastBlock + 1) blocks = int(lastBlock + 1)

View file

@ -897,6 +897,13 @@ web3._extend({
call: 'scroll_getSkippedTransactionHashes', call: 'scroll_getSkippedTransactionHashes',
params: 2 params: 2
}), }),
new web3._extend.Method({
name: 'estimateL1DataFee',
call: 'scroll_estimateL1DataFee',
params: 2,
inputFormatter: [web3._extend.formatters.inputCallFormatter, web3._extend.formatters.inputBlockNumberFormatter],
outputFormatter: web3._extend.utils.toDecimal
}),
], ],
properties: properties:
[ [
@ -912,6 +919,10 @@ web3._extend({
name: 'numSkippedTransactions', name: 'numSkippedTransactions',
getter: 'scroll_getNumSkippedTransactions' getter: 'scroll_getNumSkippedTransactions'
}), }),
new web3._extend.Property({
name: 'syncStatus',
getter: 'scroll_syncStatus',
}),
] ]
}); });
` `

View file

@ -24,7 +24,7 @@ import (
const ( const (
VersionMajor = 5 // Major version component of the current release VersionMajor = 5 // Major version component of the current release
VersionMinor = 1 // Minor version component of the current release VersionMinor = 1 // Minor version component of the current release
VersionPatch = 0 // Patch version component of the current release VersionPatch = 1 // Patch version component of the current release
VersionMeta = "mainnet" // Version metadata to append to the version string VersionMeta = "mainnet" // Version metadata to append to the version string
) )