diff --git a/op-program/client/l2/db.go b/op-program/client/l2/db.go index 295d266443bba..018b79ed9356e 100644 --- a/op-program/client/l2/db.go +++ b/op-program/client/l2/db.go @@ -40,12 +40,12 @@ func (o *OracleKeyValueStore) Get(key []byte) ([]byte, error) { if len(key) == codePrefixedKeyLength && bytes.HasPrefix(key, rawdb.CodePrefix) { key = key[len(rawdb.CodePrefix):] - return o.oracle.CodeByHash(*(*[common.HashLength]byte)(key)) + return o.oracle.CodeByHash(*(*[common.HashLength]byte)(key)), nil } if len(key) != common.HashLength { return nil, ErrInvalidKeyLength } - return o.oracle.NodeByHash(*(*[common.HashLength]byte)(key)) + return o.oracle.NodeByHash(*(*[common.HashLength]byte)(key)), nil } func (o *OracleKeyValueStore) NewBatch() ethdb.Batch { diff --git a/op-program/client/l2/db_test.go b/op-program/client/l2/db_test.go index 7d345628439f2..79a89d61a33cd 100644 --- a/op-program/client/l2/db_test.go +++ b/op-program/client/l2/db_test.go @@ -1,7 +1,6 @@ package l2 import ( - "fmt" "math/big" "testing" @@ -27,16 +26,8 @@ var ( var _ ethdb.KeyValueStore = (*OracleKeyValueStore)(nil) func TestGet(t *testing.T) { - t.Run("UnknownKey", func(t *testing.T) { - oracle := newStubStateOracle() - db := NewOracleBackedDB(oracle) - val, err := db.Get(common.Hash{}.Bytes()) - require.Error(t, err) - require.Nil(t, val) - }) - t.Run("IncorrectLengthKey", func(t *testing.T) { - oracle := newStubStateOracle() + oracle := newStubStateOracle(t) db := NewOracleBackedDB(oracle) val, err := db.Get([]byte{1, 2, 3}) require.ErrorIs(t, err, ErrInvalidKeyLength) @@ -44,7 +35,7 @@ func TestGet(t *testing.T) { }) t.Run("KeyWithCodePrefix", func(t *testing.T) { - oracle := newStubStateOracle() + oracle := newStubStateOracle(t) db := NewOracleBackedDB(oracle) key := common.HexToHash("0x12345678") prefixedKey := append(rawdb.CodePrefix, key.Bytes()...) @@ -58,7 +49,7 @@ func TestGet(t *testing.T) { }) t.Run("NormalKeyThatHappensToStartWithCodePrefix", func(t *testing.T) { - oracle := newStubStateOracle() + oracle := newStubStateOracle(t) db := NewOracleBackedDB(oracle) key := make([]byte, common.HashLength) copy(rawdb.CodePrefix, key) @@ -74,7 +65,7 @@ func TestGet(t *testing.T) { t.Run("KnownKey", func(t *testing.T) { key := common.HexToHash("0xAA4488") expected := []byte{2, 6, 3, 8} - oracle := newStubStateOracle() + oracle := newStubStateOracle(t) oracle.data[key] = expected db := NewOracleBackedDB(oracle) val, err := db.Get(key.Bytes()) @@ -85,7 +76,7 @@ func TestGet(t *testing.T) { func TestPut(t *testing.T) { t.Run("NewKey", func(t *testing.T) { - oracle := newStubStateOracle() + oracle := newStubStateOracle(t) db := NewOracleBackedDB(oracle) key := common.HexToHash("0xAA4488") value := []byte{2, 6, 3, 8} @@ -97,7 +88,7 @@ func TestPut(t *testing.T) { require.Equal(t, value, actual) }) t.Run("ReplaceKey", func(t *testing.T) { - oracle := newStubStateOracle() + oracle := newStubStateOracle(t) db := NewOracleBackedDB(oracle) key := common.HexToHash("0xAA4488") value1 := []byte{2, 6, 3, 8} @@ -119,6 +110,7 @@ func TestSupportsStateDBOperations(t *testing.T) { genesisBlock := l2Genesis.MustCommit(realDb) loader := &kvStateOracle{ + t: t, source: realDb, } assertStateDataAvailable(t, NewOracleBackedDB(loader), l2Genesis, genesisBlock) @@ -126,7 +118,7 @@ func TestSupportsStateDBOperations(t *testing.T) { func TestUpdateState(t *testing.T) { l2Genesis := createGenesis() - oracle := newStubStateOracle() + oracle := newStubStateOracle(t) db := rawdb.NewDatabase(NewOracleBackedDB(oracle)) genesisBlock := l2Genesis.MustCommit(db) @@ -203,43 +195,50 @@ func assertStateDataAvailable(t *testing.T, db ethdb.KeyValueStore, l2Genesis *c require.Equal(t, common.Hash{}, statedb.GetCodeHash(unknownAccount), "unset account code hash") } -func newStubStateOracle() *stubStateOracle { +func newStubStateOracle(t *testing.T) *stubStateOracle { return &stubStateOracle{ + t: t, data: make(map[common.Hash][]byte), code: make(map[common.Hash][]byte), } } type stubStateOracle struct { + t *testing.T data map[common.Hash][]byte code map[common.Hash][]byte } -func (o *stubStateOracle) NodeByHash(nodeHash common.Hash) ([]byte, error) { +func (o *stubStateOracle) NodeByHash(nodeHash common.Hash) []byte { data, ok := o.data[nodeHash] if !ok { - return nil, fmt.Errorf("no value for node %v", nodeHash) + o.t.Fatalf("no value for node %v", nodeHash) } - return data, nil + return data } -func (o *stubStateOracle) CodeByHash(hash common.Hash) ([]byte, error) { +func (o *stubStateOracle) CodeByHash(hash common.Hash) []byte { data, ok := o.code[hash] if !ok { - return nil, fmt.Errorf("no value for code %v", hash) + o.t.Fatalf("no value for code %v", hash) } - return data, nil + return data } // kvStateOracle loads data from a source ethdb.KeyValueStore type kvStateOracle struct { + t *testing.T source ethdb.KeyValueStore } -func (o *kvStateOracle) NodeByHash(nodeHash common.Hash) ([]byte, error) { - return o.source.Get(nodeHash.Bytes()) +func (o *kvStateOracle) NodeByHash(nodeHash common.Hash) []byte { + val, err := o.source.Get(nodeHash.Bytes()) + if err != nil { + o.t.Fatalf("error retrieving node %v: %v", nodeHash, err) + } + return val } -func (o *kvStateOracle) CodeByHash(hash common.Hash) ([]byte, error) { - return rawdb.ReadCode(o.source, hash), nil +func (o *kvStateOracle) CodeByHash(hash common.Hash) []byte { + return rawdb.ReadCode(o.source, hash) } diff --git a/op-program/client/l2/engine_backend.go b/op-program/client/l2/engine_backend.go index 7fd4c741a468e..4fd151ceff015 100644 --- a/op-program/client/l2/engine_backend.go +++ b/op-program/client/l2/engine_backend.go @@ -35,10 +35,7 @@ type OracleBackedL2Chain struct { var _ engineapi.EngineBackend = (*OracleBackedL2Chain)(nil) func NewOracleBackedL2Chain(logger log.Logger, oracle Oracle, chainCfg *params.ChainConfig, l2Head common.Hash) (*OracleBackedL2Chain, error) { - head, err := oracle.BlockByHash(l2Head) - if err != nil { - return nil, fmt.Errorf("loading l2 head: %w", err) - } + head := oracle.BlockByHash(l2Head) logger.Info("Loaded L2 head", "hash", head.Hash(), "number", head.Number()) return &OracleBackedL2Chain{ log: logger, @@ -99,10 +96,7 @@ func (o *OracleBackedL2Chain) GetBlockByHash(hash common.Hash) *types.Block { return block } // Retrieve from the oracle - block, err := o.oracle.BlockByHash(hash) - if err != nil { - handleError(err) - } + block = o.oracle.BlockByHash(hash) if block == nil { return nil } @@ -195,7 +189,3 @@ func (o *OracleBackedL2Chain) SetFinalized(header *types.Header) { func (o *OracleBackedL2Chain) SetSafe(header *types.Header) { o.safe = header } - -func handleError(err error) { - panic(err) -} diff --git a/op-program/client/l2/engine_backend_test.go b/op-program/client/l2/engine_backend_test.go index 966b17353b1df..1d28caedc9874 100644 --- a/op-program/client/l2/engine_backend_test.go +++ b/op-program/client/l2/engine_backend_test.go @@ -113,8 +113,9 @@ func TestUpdateStateDatabaseWhenImportingBlock(t *testing.T) { require.NotEqual(t, blocks[1].Root(), newBlock.Root(), "block should have modified world state") - _, err = chain.StateAt(newBlock.Root()) - require.Error(t, err, "state from non-imported block should not be available") + require.Panics(t, func() { + _, _ = chain.StateAt(newBlock.Root()) + }, "state from non-imported block should not be available") err = chain.InsertBlockWithoutSetHead(newBlock) require.NoError(t, err) @@ -223,8 +224,8 @@ func newStubBlockOracle(chain []*types.Block, db ethdb.Database) *stubBlockOracl } } -func (o stubBlockOracle) BlockByHash(blockHash common.Hash) (*types.Block, error) { - return o.blocks[blockHash], nil +func (o stubBlockOracle) BlockByHash(blockHash common.Hash) *types.Block { + return o.blocks[blockHash] } func TestEngineAPITests(t *testing.T) { diff --git a/op-program/client/l2/oracle.go b/op-program/client/l2/oracle.go index a8a05e0690219..02839fccf45e9 100644 --- a/op-program/client/l2/oracle.go +++ b/op-program/client/l2/oracle.go @@ -11,13 +11,11 @@ type StateOracle interface { // NodeByHash retrieves the merkle-patricia trie node pre-image for a given hash. // Trie nodes may be from the world state trie or any account storage trie. // Contract code is not stored as part of the trie and must be retrieved via CodeByHash - // Returns an error if the pre-image is unavailable. - NodeByHash(nodeHash common.Hash) ([]byte, error) + NodeByHash(nodeHash common.Hash) []byte // CodeByHash retrieves the contract code pre-image for a given hash. // codeHash should be retrieved from the world state account for a contract. - // Returns an error if the pre-image is unavailable. - CodeByHash(codeHash common.Hash) ([]byte, error) + CodeByHash(codeHash common.Hash) []byte } // Oracle defines the high-level API used to retrieve L2 data. @@ -26,6 +24,5 @@ type Oracle interface { StateOracle // BlockByHash retrieves the block with the given hash. - // Returns an error if the block is not available. - BlockByHash(blockHash common.Hash) (*types.Block, error) + BlockByHash(blockHash common.Hash) *types.Block } diff --git a/op-program/host/l2/fetcher.go b/op-program/host/l2/fetcher.go index b9c0167fb14ba..5fe08f9a83efb 100644 --- a/op-program/host/l2/fetcher.go +++ b/op-program/host/l2/fetcher.go @@ -42,19 +42,26 @@ func NewFetchingL2Oracle(ctx context.Context, logger log.Logger, l2Url string) ( }, nil } -func (o *FetchingL2Oracle) NodeByHash(hash common.Hash) ([]byte, error) { +func (o *FetchingL2Oracle) NodeByHash(hash common.Hash) []byte { // MPT nodes are stored as the hash of the node (with no prefix) - return o.dbGet(hash.Bytes()) + node, err := o.dbGet(hash.Bytes()) + if err != nil { + panic(err) + } + return node } -func (o *FetchingL2Oracle) CodeByHash(hash common.Hash) ([]byte, error) { +func (o *FetchingL2Oracle) CodeByHash(hash common.Hash) []byte { // First try retrieving with the new code prefix code, err := o.dbGet(append(rawdb.CodePrefix, hash.Bytes()...)) if err != nil { // Fallback to the legacy un-prefixed version - return o.dbGet(hash.Bytes()) + code, err = o.dbGet(hash.Bytes()) + if err != nil { + panic(err) + } } - return code, nil + return code } func (o *FetchingL2Oracle) dbGet(key []byte) ([]byte, error) { @@ -66,10 +73,10 @@ func (o *FetchingL2Oracle) dbGet(key []byte) ([]byte, error) { return node, nil } -func (o *FetchingL2Oracle) BlockByHash(blockHash common.Hash) (*types.Block, error) { +func (o *FetchingL2Oracle) BlockByHash(blockHash common.Hash) *types.Block { block, err := o.blockSource.BlockByHash(o.ctx, blockHash) if err != nil { - return nil, fmt.Errorf("fetch block %s: %w", blockHash.Hex(), err) + panic(fmt.Errorf("fetch block %s: %w", blockHash.Hex(), err)) } - return block, nil + return block } diff --git a/op-program/host/l2/fetcher_test.go b/op-program/host/l2/fetcher_test.go index 63b4cb929d106..42e516209bec2 100644 --- a/op-program/host/l2/fetcher_test.go +++ b/op-program/host/l2/fetcher_test.go @@ -63,9 +63,9 @@ func TestNodeByHash(t *testing.T) { } fetcher := newFetcher(nil, stub) - node, err := fetcher.NodeByHash(hash) - require.ErrorIs(t, err, stub.nextErr) - require.Nil(t, node) + require.Panics(t, func() { + fetcher.NodeByHash(hash) + }) }) t.Run("Success", func(t *testing.T) { @@ -75,8 +75,7 @@ func TestNodeByHash(t *testing.T) { } fetcher := newFetcher(nil, stub) - node, err := fetcher.NodeByHash(hash) - require.NoError(t, err) + node := fetcher.NodeByHash(hash) require.EqualValues(t, expected, node) }) @@ -86,7 +85,7 @@ func TestNodeByHash(t *testing.T) { } fetcher := newFetcher(nil, stub) - _, _ = fetcher.NodeByHash(hash) + fetcher.NodeByHash(hash) require.Len(t, stub.requests, 1, "should make single request") req := stub.requests[0] require.Equal(t, "debug_dbGet", req.method) @@ -104,9 +103,7 @@ func TestCodeByHash(t *testing.T) { } fetcher := newFetcher(nil, stub) - node, err := fetcher.CodeByHash(hash) - require.ErrorIs(t, err, stub.nextErr) - require.Nil(t, node) + require.Panics(t, func() { fetcher.CodeByHash(hash) }) }) t.Run("Success", func(t *testing.T) { @@ -116,8 +113,7 @@ func TestCodeByHash(t *testing.T) { } fetcher := newFetcher(nil, stub) - node, err := fetcher.CodeByHash(hash) - require.NoError(t, err) + node := fetcher.CodeByHash(hash) require.EqualValues(t, expected, node) }) @@ -127,7 +123,7 @@ func TestCodeByHash(t *testing.T) { } fetcher := newFetcher(nil, stub) - _, _ = fetcher.CodeByHash(hash) + fetcher.CodeByHash(hash) require.Len(t, stub.requests, 1, "should make single request") req := stub.requests[0] require.Equal(t, "debug_dbGet", req.method) @@ -141,7 +137,8 @@ func TestCodeByHash(t *testing.T) { } fetcher := newFetcher(nil, stub) - _, _ = fetcher.CodeByHash(hash) + // Panics because the code can't be found with or without the prefix + require.Panics(t, func() { fetcher.CodeByHash(hash) }) require.Len(t, stub.requests, 2, "should request with and without prefix") req := stub.requests[0] require.Equal(t, "debug_dbGet", req.method) @@ -183,8 +180,7 @@ func TestBlockByHash(t *testing.T) { stub := &stubBlockSource{nextResult: block} fetcher := newFetcher(stub, nil) - res, err := fetcher.BlockByHash(hash) - require.NoError(t, err) + res := fetcher.BlockByHash(hash) require.Same(t, block, res) }) @@ -192,16 +188,16 @@ func TestBlockByHash(t *testing.T) { stub := &stubBlockSource{nextErr: errors.New("boom")} fetcher := newFetcher(stub, nil) - res, err := fetcher.BlockByHash(hash) - require.ErrorIs(t, err, stub.nextErr) - require.Nil(t, res) + require.Panics(t, func() { + fetcher.BlockByHash(hash) + }) }) t.Run("RequestArgs", func(t *testing.T) { stub := &stubBlockSource{} fetcher := newFetcher(stub, nil) - _, _ = fetcher.BlockByHash(hash) + fetcher.BlockByHash(hash) require.Len(t, stub.requests, 1, "should make single request") req := stub.requests[0]