diff --git a/jsonrpc/endpoints_zkevm.go b/jsonrpc/endpoints_zkevm.go index 6e54035686..79795ffd19 100644 --- a/jsonrpc/endpoints_zkevm.go +++ b/jsonrpc/endpoints_zkevm.go @@ -152,8 +152,15 @@ func (z *ZKEVMEndpoints) GetBatchByNumber(batchNumber types.BatchNumber, fullTx return rpcErrorResponse(types.DefaultErrorCode, fmt.Sprintf("couldn't load virtual batch from state by number %v", batchNumber), err) } + ger, err := z.state.GetExitRootByGlobalExitRoot(ctx, batch.GlobalExitRoot, dbTx) + if err != nil && !errors.Is(err, state.ErrNotFound) { + return rpcErrorResponse(types.DefaultErrorCode, fmt.Sprintf("couldn't load full GER from state by number %v", batchNumber), err) + } else if errors.Is(err, state.ErrNotFound) { + ger = &state.GlobalExitRoot{} + } + batch.Transactions = txs - rpcBatch := types.NewBatch(batch, virtualBatch, verifiedBatch, receipts, fullTx) + rpcBatch := types.NewBatch(batch, virtualBatch, verifiedBatch, receipts, fullTx, ger) return rpcBatch, nil }) diff --git a/jsonrpc/endpoints_zkevm.openrpc.json b/jsonrpc/endpoints_zkevm.openrpc.json index 93be772ceb..6f7fc80252 100644 --- a/jsonrpc/endpoints_zkevm.openrpc.json +++ b/jsonrpc/endpoints_zkevm.openrpc.json @@ -385,6 +385,12 @@ "globalExitRoot": { "$ref": "#/components/schemas/Keccak" }, + "mainnetExitRoot": { + "$ref": "#/components/schemas/Keccak" + }, + "rollupExitRoot": { + "$ref": "#/components/schemas/Keccak" + }, "accInputHash": { "$ref": "#/components/schemas/Keccak" }, diff --git a/jsonrpc/endpoints_zkevm_test.go b/jsonrpc/endpoints_zkevm_test.go index 5aa8b50e01..c42ab6ac79 100644 --- a/jsonrpc/endpoints_zkevm_test.go +++ b/jsonrpc/endpoints_zkevm_test.go @@ -661,6 +661,16 @@ func TestGetBatchByNumber(t *testing.T) { Return(verifiedBatch, nil). Once() + ger := state.GlobalExitRoot{ + MainnetExitRoot: common.HexToHash("0x4"), + RollupExitRoot: common.HexToHash("0x4"), + GlobalExitRoot: common.HexToHash("0x4"), + } + m.State. + On("GetExitRootByGlobalExitRoot", context.Background(), batch.GlobalExitRoot, m.DbTx). + Return(&ger, nil). + Once() + txs := []*ethTypes.Transaction{ signTx(ethTypes.NewTransaction(1001, common.HexToAddress("0x1000"), big.NewInt(1000), 1001, big.NewInt(1002), []byte("1003")), s.Config.ChainID), signTx(ethTypes.NewTransaction(1002, common.HexToAddress("0x1000"), big.NewInt(1000), 1001, big.NewInt(1002), []byte("1003")), s.Config.ChainID), @@ -775,6 +785,16 @@ func TestGetBatchByNumber(t *testing.T) { Return(verifiedBatch, nil). Once() + ger := state.GlobalExitRoot{ + MainnetExitRoot: common.HexToHash("0x4"), + RollupExitRoot: common.HexToHash("0x4"), + GlobalExitRoot: common.HexToHash("0x4"), + } + m.State. + On("GetExitRootByGlobalExitRoot", context.Background(), batch.GlobalExitRoot, m.DbTx). + Return(&ger, nil). + Once() + txs := []*ethTypes.Transaction{ signTx(ethTypes.NewTransaction(1001, common.HexToAddress("0x1000"), big.NewInt(1000), 1001, big.NewInt(1002), []byte("1003")), s.Config.ChainID), signTx(ethTypes.NewTransaction(1002, common.HexToAddress("0x1000"), big.NewInt(1000), 1001, big.NewInt(1002), []byte("1003")), s.Config.ChainID), @@ -874,6 +894,16 @@ func TestGetBatchByNumber(t *testing.T) { Return(verifiedBatch, nil). Once() + ger := state.GlobalExitRoot{ + MainnetExitRoot: common.HexToHash("0x4"), + RollupExitRoot: common.HexToHash("0x4"), + GlobalExitRoot: common.HexToHash("0x4"), + } + m.State. + On("GetExitRootByGlobalExitRoot", context.Background(), batch.GlobalExitRoot, m.DbTx). + Return(&ger, nil). + Once() + txs := []*ethTypes.Transaction{ signTx(ethTypes.NewTransaction(1001, common.HexToAddress("0x1000"), big.NewInt(1000), 1001, big.NewInt(1002), []byte("1003")), s.Config.ChainID), signTx(ethTypes.NewTransaction(1002, common.HexToAddress("0x1000"), big.NewInt(1000), 1001, big.NewInt(1002), []byte("1003")), s.Config.ChainID), diff --git a/jsonrpc/mocks/mock_state.go b/jsonrpc/mocks/mock_state.go index 907d0eafca..1bd6bf8339 100644 --- a/jsonrpc/mocks/mock_state.go +++ b/jsonrpc/mocks/mock_state.go @@ -204,6 +204,32 @@ func (_m *StateMock) GetCode(ctx context.Context, address common.Address, blockN return r0, r1 } +// GetExitRootByGlobalExitRoot provides a mock function with given fields: ctx, ger, dbTx +func (_m *StateMock) GetExitRootByGlobalExitRoot(ctx context.Context, ger common.Hash, dbTx pgx.Tx) (*state.GlobalExitRoot, error) { + ret := _m.Called(ctx, ger, dbTx) + + var r0 *state.GlobalExitRoot + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, common.Hash, pgx.Tx) (*state.GlobalExitRoot, error)); ok { + return rf(ctx, ger, dbTx) + } + if rf, ok := ret.Get(0).(func(context.Context, common.Hash, pgx.Tx) *state.GlobalExitRoot); ok { + r0 = rf(ctx, ger, dbTx) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*state.GlobalExitRoot) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, common.Hash, pgx.Tx) error); ok { + r1 = rf(ctx, ger, dbTx) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // GetL2BlockByHash provides a mock function with given fields: ctx, hash, dbTx func (_m *StateMock) GetL2BlockByHash(ctx context.Context, hash common.Hash, dbTx pgx.Tx) (*coretypes.Block, error) { ret := _m.Called(ctx, hash, dbTx) diff --git a/jsonrpc/types/interfaces.go b/jsonrpc/types/interfaces.go index b85b39d0a9..d6afbe89de 100644 --- a/jsonrpc/types/interfaces.go +++ b/jsonrpc/types/interfaces.go @@ -61,4 +61,5 @@ type StateInterface interface { GetTransactionsByBatchNumber(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (txs []types.Transaction, err error) GetVirtualBatch(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*state.VirtualBatch, error) GetVerifiedBatch(ctx context.Context, batchNumber uint64, dbTx pgx.Tx) (*state.VerifiedBatch, error) + GetExitRootByGlobalExitRoot(ctx context.Context, ger common.Hash, dbTx pgx.Tx) (*state.GlobalExitRoot, error) } diff --git a/jsonrpc/types/types.go b/jsonrpc/types/types.go index 5890994d10..96ea1a59a0 100644 --- a/jsonrpc/types/types.go +++ b/jsonrpc/types/types.go @@ -329,6 +329,8 @@ type Batch struct { Coinbase common.Address `json:"coinbase"` StateRoot common.Hash `json:"stateRoot"` GlobalExitRoot common.Hash `json:"globalExitRoot"` + MainnetExitRoot common.Hash `json:"mainnetExitRoot"` + RollupExitRoot common.Hash `json:"rollupExitRoot"` LocalExitRoot common.Hash `json:"localExitRoot"` AccInputHash common.Hash `json:"accInputHash"` Timestamp ArgUint64 `json:"timestamp"` @@ -338,15 +340,17 @@ type Batch struct { } // NewBatch creates a Batch instance -func NewBatch(batch *state.Batch, virtualBatch *state.VirtualBatch, verifiedBatch *state.VerifiedBatch, receipts []types.Receipt, fullTx bool) *Batch { +func NewBatch(batch *state.Batch, virtualBatch *state.VirtualBatch, verifiedBatch *state.VerifiedBatch, receipts []types.Receipt, fullTx bool, ger *state.GlobalExitRoot) *Batch { res := &Batch{ - Number: ArgUint64(batch.BatchNumber), - GlobalExitRoot: batch.GlobalExitRoot, - AccInputHash: batch.AccInputHash, - Timestamp: ArgUint64(batch.Timestamp.Unix()), - StateRoot: batch.StateRoot, - Coinbase: batch.Coinbase, - LocalExitRoot: batch.LocalExitRoot, + Number: ArgUint64(batch.BatchNumber), + GlobalExitRoot: batch.GlobalExitRoot, + MainnetExitRoot: ger.MainnetExitRoot, + RollupExitRoot: ger.RollupExitRoot, + AccInputHash: batch.AccInputHash, + Timestamp: ArgUint64(batch.Timestamp.Unix()), + StateRoot: batch.StateRoot, + Coinbase: batch.Coinbase, + LocalExitRoot: batch.LocalExitRoot, } if virtualBatch != nil {