diff --git a/core/rawdb/accessors_l1_message.go b/core/rawdb/accessors_l1_message.go index 61226ceb3b..5d0eebb110 100644 --- a/core/rawdb/accessors_l1_message.go +++ b/core/rawdb/accessors_l1_message.go @@ -3,23 +3,15 @@ package rawdb import ( "bytes" "encoding/binary" - "errors" "math/big" - leveldb "github.com/syndtr/goleveldb/leveldb/errors" - "github.com/scroll-tech/go-ethereum/common" "github.com/scroll-tech/go-ethereum/core/types" "github.com/scroll-tech/go-ethereum/ethdb" - "github.com/scroll-tech/go-ethereum/ethdb/memorydb" "github.com/scroll-tech/go-ethereum/log" "github.com/scroll-tech/go-ethereum/rlp" ) -func isNotFoundErr(err error) bool { - return errors.Is(err, leveldb.ErrNotFound) || errors.Is(err, memorydb.ErrMemorydbNotFound) -} - // WriteSyncedL1BlockNumber writes the highest synced L1 block number to the database. func WriteSyncedL1BlockNumber(db ethdb.KeyValueWriter, L1BlockNumber uint64) { value := big.NewInt(0).SetUint64(L1BlockNumber).Bytes() diff --git a/core/rawdb/accessors_row_consumption.go b/core/rawdb/accessors_row_consumption.go new file mode 100644 index 0000000000..5d08e210a9 --- /dev/null +++ b/core/rawdb/accessors_row_consumption.go @@ -0,0 +1,47 @@ +package rawdb + +import ( + "bytes" + + "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/core/types" + "github.com/scroll-tech/go-ethereum/ethdb" + "github.com/scroll-tech/go-ethereum/log" + "github.com/scroll-tech/go-ethereum/rlp" +) + +// WriteBlockRowConsumption writes a RowConsumption of the block to the database. +func WriteBlockRowConsumption(db ethdb.KeyValueWriter, l2BlockHash common.Hash, rc types.RowConsumption) { + bytes, err := rlp.EncodeToBytes(&rc) + if err != nil { + log.Crit("Failed to RLP encode RowConsumption ", "err", err) + } + if err := db.Put(rowConsumptionKey(l2BlockHash), bytes); err != nil { + log.Crit("Failed to store RowConsumption ", "err", err) + } +} + +// ReadBlockRowConsumption retrieves the RowConsumption corresponding to the block hash. +func ReadBlockRowConsumption(db ethdb.Reader, l2BlockHash common.Hash) *types.RowConsumption { + data := ReadBlockRowConsumptionRLP(db, l2BlockHash) + if len(data) == 0 { + return nil + } + rc := new(types.RowConsumption) + if err := rlp.Decode(bytes.NewReader(data), rc); err != nil { + log.Crit("Invalid RowConsumption message RLP", "l2BlockHash", l2BlockHash.String(), "data", data, "err", err) + } + return rc +} + +// ReadBlockRowConsumption retrieves the RowConsumption in its raw RLP database encoding. +func ReadBlockRowConsumptionRLP(db ethdb.Reader, l2BlockHash common.Hash) rlp.RawValue { + data, err := db.Get(rowConsumptionKey(l2BlockHash)) + if err != nil && isNotFoundErr(err) { + return nil + } + if err != nil { + log.Crit("Failed to load RowConsumption", "l2BlockHash", l2BlockHash.String(), "err", err) + } + return data +} diff --git a/core/rawdb/accessors_row_consumption_test.go b/core/rawdb/accessors_row_consumption_test.go new file mode 100644 index 0000000000..4195b3d2b4 --- /dev/null +++ b/core/rawdb/accessors_row_consumption_test.go @@ -0,0 +1,22 @@ +package rawdb + +import ( + "math/big" + "testing" + + "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/core/types" +) + +func TestReadBlockRowConsumption(t *testing.T) { + l2BlockHash := common.BigToHash(big.NewInt(10)) + rc := types.RowConsumption{ + Rows: 11, + } + db := NewMemoryDatabase() + WriteBlockRowConsumption(db, l2BlockHash, rc) + got := ReadBlockRowConsumption(db, l2BlockHash) + if got == nil || got.Rows != rc.Rows { + t.Fatal("RowConsumption mismatch", "expected", rc, "got", got) + } +} diff --git a/core/rawdb/schema.go b/core/rawdb/schema.go index de264b6656..4b7ac8ab70 100644 --- a/core/rawdb/schema.go +++ b/core/rawdb/schema.go @@ -20,8 +20,12 @@ package rawdb import ( "bytes" "encoding/binary" + "errors" + + leveldb "github.com/syndtr/goleveldb/leveldb/errors" "github.com/scroll-tech/go-ethereum/common" + "github.com/scroll-tech/go-ethereum/ethdb/memorydb" "github.com/scroll-tech/go-ethereum/metrics" ) @@ -103,6 +107,9 @@ var ( syncedL1BlockNumberKey = []byte("LastSyncedL1BlockNumber") l1MessagePrefix = []byte("l1") // l1MessagePrefix + queueIndex (uint64 big endian) -> L1MessageTx firstQueueIndexNotInL2BlockPrefix = []byte("q") // firstQueueIndexNotInL2BlockPrefix + L2 block hash -> enqueue index + + // Row consumption + rowConsumptionPrefix = []byte("rc") // rowConsumptionPrefix + hash -> row consumption by block ) const ( @@ -252,3 +259,12 @@ func L1MessageKey(queueIndex uint64) []byte { func FirstQueueIndexNotInL2BlockKey(l2BlockHash common.Hash) []byte { return append(firstQueueIndexNotInL2BlockPrefix, l2BlockHash.Bytes()...) } + +// rowConsumptionKey = rowConsumptionPrefix + hash +func rowConsumptionKey(hash common.Hash) []byte { + return append(rowConsumptionPrefix, hash.Bytes()...) +} + +func isNotFoundErr(err error) bool { + return errors.Is(err, leveldb.ErrNotFound) || errors.Is(err, memorydb.ErrMemorydbNotFound) +} diff --git a/core/types/row_consumption.go b/core/types/row_consumption.go new file mode 100644 index 0000000000..dbd2cd0852 --- /dev/null +++ b/core/types/row_consumption.go @@ -0,0 +1,28 @@ +package types + +import ( + "fmt" + "io" + + "github.com/scroll-tech/go-ethereum/rlp" +) + +type RowConsumption struct { + Rows uint64 +} + +func (rc *RowConsumption) EncodeRLP(w io.Writer) error { + return rlp.Encode(w, rc.Rows) +} + +func (rc *RowConsumption) DecodeRLP(s *rlp.Stream) error { + _, size, err := s.Kind() + if err != nil { + return err + } + if size <= 8 { + return s.Decode(&rc.Rows) + } else { + return fmt.Errorf("invalid input size %d for origin", size) + } +} diff --git a/eth/api.go b/eth/api.go index 87fd6bc0d8..e855cf019c 100644 --- a/eth/api.go +++ b/eth/api.go @@ -671,3 +671,30 @@ func (api *ScrollAPI) GetLatestRelayedQueueIndex(ctx context.Context) (queueInde lastIncluded := *queueIndex - 1 return &lastIncluded, nil } + +// rpcMarshalBlock uses the generalized output filler, then adds the total difficulty field, which requires +// a `ScrollAPI`. +func (api *ScrollAPI) rpcMarshalBlock(ctx context.Context, b *types.Block, fullTx bool) (map[string]interface{}, error) { + fields, err := ethapi.RPCMarshalBlock(b, true, fullTx, api.eth.APIBackend.ChainConfig()) + if err != nil { + return nil, err + } + fields["totalDifficulty"] = (*hexutil.Big)(api.eth.APIBackend.GetTd(ctx, b.Hash())) + rc := rawdb.ReadBlockRowConsumption(api.eth.ChainDb(), b.Hash()) + if rc != nil { + fields["rowConsumption"] = hexutil.Uint64(rc.Rows) + } else { + fields["rowConsumption"] = nil + } + return fields, err +} + +// GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full +// detail, otherwise only the transaction hash is returned. +func (api *ScrollAPI) GetBlockByHash(ctx context.Context, hash common.Hash, fullTx bool) (map[string]interface{}, error) { + block, err := api.eth.APIBackend.BlockByHash(ctx, hash) + if block != nil { + return api.rpcMarshalBlock(ctx, block, fullTx) + } + return nil, err +} diff --git a/ethclient/ethclient.go b/ethclient/ethclient.go index 9378b5f571..bded33ef72 100644 --- a/ethclient/ethclient.go +++ b/ethclient/ethclient.go @@ -337,6 +337,14 @@ func (ec *Client) GetBlockTraceByNumber(ctx context.Context, number *big.Int) (* return blockTrace, ec.c.CallContext(ctx, &blockTrace, "scroll_getBlockTraceByNumberOrHash", toBlockNumArg(number)) } +// GetBlockByHash returns the requested block. When fullTx is true all transactions in the block are returned in full +// detail, otherwise only the transaction hash is returned. +func (ec *Client) GetBlockByHash(ctx context.Context, blockHash common.Hash, inclTx bool) (map[string]interface{}, error) { + block := make(map[string]interface{}) + err := ec.c.CallContext(ctx, &block, "scroll_getBlockByHash", blockHash, inclTx) + return block, err +} + // SubscribeNewBlockTrace subscribes to block execution trace when a new block is created. func (ec *Client) SubscribeNewBlockTrace(ctx context.Context, ch chan<- *types.BlockTrace) (ethereum.Subscription, error) { return ec.c.EthSubscribe(ctx, ch, "newBlockTrace") diff --git a/internal/web3ext/web3ext.go b/internal/web3ext/web3ext.go index e424d09c79..917204409b 100644 --- a/internal/web3ext/web3ext.go +++ b/internal/web3ext/web3ext.go @@ -874,7 +874,13 @@ web3._extend({ name: 'getFirstQueueIndexNotInL2Block', call: 'scroll_getFirstQueueIndexNotInL2Block', params: 1 - }) + }), + new web3._extend.Method({ + name: 'getBlockByHash', + call: 'scroll_getBlockByHash', + params: 2, + inputFormatter: [null, function (val) { return !!val; }] + }), ], properties: [ diff --git a/params/version.go b/params/version.go index 63705f7a38..249133d886 100644 --- a/params/version.go +++ b/params/version.go @@ -24,7 +24,7 @@ import ( const ( VersionMajor = 4 // Major version component of the current release VersionMinor = 2 // Minor version component of the current release - VersionPatch = 9 // Patch version component of the current release + VersionPatch = 10 // Patch version component of the current release VersionMeta = "sepolia" // Version metadata to append to the version string )