diff --git a/blockchain/blockchain.go b/blockchain/blockchain.go index 0771a6e6d9..1c9c74fc12 100644 --- a/blockchain/blockchain.go +++ b/blockchain/blockchain.go @@ -33,7 +33,15 @@ type Reader interface { TransactionByHash(hash *felt.Felt) (transaction core.Transaction, err error) TransactionByBlockNumberAndIndex(blockNumber, index uint64) (transaction core.Transaction, err error) + BlockNumberAndIndexByTxHash( + hash *felt.TransactionHash, + ) (blockNumber uint64, index uint64, err error) + Receipt(hash *felt.Felt) (receipt *core.TransactionReceipt, blockHash *felt.Felt, blockNumber uint64, err error) + ReceiptByBlockNumberAndIndex( + blockNumber, index uint64, + ) (receipt core.TransactionReceipt, blockHash *felt.Felt, err error) + StateUpdateByNumber(number uint64) (update *core.StateUpdate, err error) StateUpdateByHash(hash *felt.Felt) (update *core.StateUpdate, err error) L1HandlerTxnHash(msgHash *common.Hash) (l1HandlerTxnHash felt.Felt, err error) @@ -199,6 +207,15 @@ func (b *Blockchain) TransactionByHash(hash *felt.Felt) (core.Transaction, error return core.GetTxByHash(b.database, (*felt.TransactionHash)(hash)) } +// BlockNumberAndIndexByTxHash gets transaction block number and index by Tx hash +func (b *Blockchain) BlockNumberAndIndexByTxHash( + hash *felt.TransactionHash, +) (blockNumber uint64, txIndex uint64, returnedErr error) { + b.listener.OnRead("BlockNumberAndIndexByTxHash") + data, err := core.TransactionBlockNumbersAndIndicesByHashBucket.Get(b.database, hash) + return data.Number, data.Index, err +} + // Receipt gets the transaction receipt for a given transaction hash. // TODO: Return TransactionReceipt instead of *TransactionReceipt. func (b *Blockchain) Receipt(hash *felt.Felt) (*core.TransactionReceipt, *felt.Felt, uint64, error) { @@ -222,6 +239,24 @@ func (b *Blockchain) Receipt(hash *felt.Felt) (*core.TransactionReceipt, *felt.F return receipt, header.Hash, header.Number, nil } +func (b *Blockchain) ReceiptByBlockNumberAndIndex( + blockNumber, index uint64, +) (core.TransactionReceipt, *felt.Felt, error) { + b.listener.OnRead("ReceiptByBlockNumberAndIndex") + + receipt, err := core.GetReceiptByBlockNumIndex(b.database, blockNumber, index) + if err != nil { + return receipt, nil, err + } + + header, err := core.GetBlockHeaderByNumber(b.database, blockNumber) + if err != nil { + return receipt, nil, err + } + + return receipt, header.Hash, nil +} + func (b *Blockchain) SubscribeL1Head() L1HeadSubscription { return L1HeadSubscription{b.l1HeadFeed.Subscribe()} } diff --git a/core/accessors.go b/core/accessors.go index cdd63e0b74..9f4aed34ba 100644 --- a/core/accessors.go +++ b/core/accessors.go @@ -263,6 +263,18 @@ func GetReceiptByHash( return &receipt, nil } +func GetReceiptByBlockNumIndex( + r db.KeyValueReader, + num, + index uint64, +) (TransactionReceipt, error) { + numIdxKey := db.BlockNumIndexKey{ + Number: num, + Index: index, + } + return ReceiptsByBlockNumberAndIndexBucket.Get(r, numIdxKey) +} + func DeleteTxsAndReceipts(batch db.IndexedBatch, blockNum, numTxs uint64) error { // remove txs and receipts for i := range numTxs { diff --git a/mocks/mock_blockchain.go b/mocks/mock_blockchain.go index 193726354f..d85491aa84 100644 --- a/mocks/mock_blockchain.go +++ b/mocks/mock_blockchain.go @@ -256,6 +256,31 @@ func (mr *MockReaderMockRecorder) Receipt(hash any) *gomock.Call { return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Receipt", reflect.TypeOf((*MockReader)(nil).Receipt), hash) } +func (m *MockReader) ReceiptByBlockNumberAndIndex( + blockNumber, index uint64, +) (core.TransactionReceipt, *felt.Felt, error) { + m.ctrl.T.Helper() + + ret := m.ctrl.Call(m, "ReceiptByBlockNumberAndIndex", blockNumber, index) + ret0, _ := ret[0].(core.TransactionReceipt) + ret1, _ := ret[1].(*felt.Felt) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +func (mr *MockReaderMockRecorder) ReceiptByBlockNumberAndIndex( + blockNumber any, index any, +) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType( + mr.mock, + "ReceiptByBlockNumberAndIndex", + reflect.TypeOf((*MockReader)(nil).ReceiptByBlockNumberAndIndex), + blockNumber, + index, + ) +} + // StateAtBlockHash mocks base method. func (m *MockReader) StateAtBlockHash(blockHash *felt.Felt) (core.StateReader, blockchain.StateCloser, error) { m.ctrl.T.Helper() @@ -361,3 +386,22 @@ func (mr *MockReaderMockRecorder) TransactionByHash(hash any) *gomock.Call { mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TransactionByHash", reflect.TypeOf((*MockReader)(nil).TransactionByHash), hash) } + +func (m *MockReader) BlockNumberAndIndexByTxHash(hash *felt.TransactionHash) (uint64, uint64, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "BlockNumberAndIndexByTxHash", hash) + ret0, _ := ret[0].(uint64) + ret1, _ := ret[1].(uint64) + ret2, _ := ret[2].(error) + return ret0, ret1, ret2 +} + +func (mr *MockReaderMockRecorder) BlockNumberAndIndexByTxHash(hash any) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType( + mr.mock, + "BlockNumberAndIndexByTxHash", + reflect.TypeOf((*MockReader)(nil).BlockNumberAndIndexByTxHash), + hash, + ) +} diff --git a/rpc/v10/subscriptions_test.go b/rpc/v10/subscriptions_test.go index 61b2c5f697..911e9c2f3e 100644 --- a/rpc/v10/subscriptions_test.go +++ b/rpc/v10/subscriptions_test.go @@ -1021,7 +1021,9 @@ func TestSubscribeTxnStatus(t *testing.T) { cache := rpccore.NewTransactionCache(cacheEntryTimeOut, cacheSize) handler := New(mockChain, mockSyncer, nil, log).WithSubmittedTransactionsCache(cache) - mockChain.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound).AnyTimes() + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound).AnyTimes() mockSyncer.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound).AnyTimes() mockChain.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound).AnyTimes() mockSyncer.EXPECT().PendingBlock().Return(nil).AnyTimes() @@ -1049,7 +1051,9 @@ func TestSubscribeTxnStatus(t *testing.T) { txHash, err := felt.NewFromString[felt.Felt]("0x1011") require.NoError(t, err) - mockChain.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) id, conn := createTestTxStatusWebsocket(t, handler, txHash) assertNextTxnStatus( @@ -1066,7 +1070,9 @@ func TestSubscribeTxnStatus(t *testing.T) { txHash, err := felt.NewFromString[felt.Felt]("0x1010") require.NoError(t, err) - mockChain.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) id, conn := createTestTxStatusWebsocket(t, handler, txHash) assertNextTxnStatus( t, @@ -1127,7 +1133,9 @@ func TestSubscribeTxnStatus(t *testing.T) { ) require.Nil(t, addErr) txHash := addRes.TransactionHash - mockChain.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncer.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound).Times(2) mockChain.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound).Times(2) @@ -1143,7 +1151,9 @@ func TestSubscribeTxnStatus(t *testing.T) { "", ) // Candidate Status - mockChain.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) preConfirmed := &core.PreConfirmed{ Block: &core.Block{ Header: &core.Header{ @@ -1218,8 +1228,15 @@ func TestSubscribeTxnStatus(t *testing.T) { nil, ).Times(1) // Accepted on l2 Status - mockChain.EXPECT().TransactionByHash(txHash).Return(block.Transactions[0], nil) - mockChain.EXPECT().Receipt(txHash).Return(block.Receipts[0], block.Hash, block.Number, nil) + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(block.Number, uint64(0), nil) + mockChain.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(block.Transactions[0], nil) + mockChain.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(*block.Receipts[0], block.Hash, nil) mockChain.EXPECT().L1Head().Return(core.L1Head{}, db.ErrKeyNotFound) handler.newHeads.Send(block) @@ -1238,8 +1255,15 @@ func TestSubscribeTxnStatus(t *testing.T) { nil, ).Times(1) l1Head := core.L1Head{BlockNumber: block.Number} - mockChain.EXPECT().TransactionByHash(txHash).Return(block.Transactions[0], nil) - mockChain.EXPECT().Receipt(txHash).Return(block.Receipts[0], block.Hash, block.Number, nil) + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(block.Number, uint64(0), nil) + mockChain.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(block.Transactions[0], nil) + mockChain.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(*block.Receipts[0], block.Hash, nil) mockChain.EXPECT().L1Head().Return(l1Head, nil) handler.l1Heads.Send(&l1Head) assertNextTxnStatus( diff --git a/rpc/v10/transaction_test.go b/rpc/v10/transaction_test.go index 33fd3bb219..540836f8b2 100644 --- a/rpc/v10/transaction_test.go +++ b/rpc/v10/transaction_test.go @@ -209,13 +209,15 @@ func TestTransactionReceiptByHash(t *testing.T) { _, _, _, err := pendingData.ReceiptByHash(transaction.Hash()) if err != nil { // receipt belong to canonical block mock expectations - mockReader.EXPECT().TransactionByHash(expected.Hash).Return(transaction, nil) - mockReader.EXPECT().Receipt(expected.Hash).Return( - loadedBlock.Receipts[transactionIndex], - expected.BlockHash, - *expected.BlockNumber, - nil, - ) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(expected.Hash), + ).Return(*expected.BlockNumber, uint64(transactionIndex), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + *expected.BlockNumber, uint64(transactionIndex), + ).Return(transaction, nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + *expected.BlockNumber, uint64(transactionIndex), + ).Return(*loadedBlock.Receipts[transactionIndex], expected.BlockHash, nil) mockReader.EXPECT().L1Head().Return(test.l1Head, nil) } @@ -235,7 +237,9 @@ func TestTransactionReceiptByHash_NotFound(t *testing.T) { handler := rpcv10.New(mockReader, mockSyncReader, nil, nil) txHash := felt.NewFromBytes[felt.Felt]([]byte("random hash")) - mockReader.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound) mockReader.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound) @@ -274,13 +278,15 @@ func TestTransactionStatus(t *testing.T) { mockReader *mocks.MockReader, mockSyncReader *mocks.MockSyncReader, ) { - mockReader.EXPECT().TransactionByHash(tx.Hash()).Return(tx, nil) - mockReader.EXPECT().Receipt(tx.Hash()).Return( - block.Receipts[0], - block.Hash, - block.Number, - nil, - ) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(tx.Hash()), + ).Return(block.Number, uint64(0), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(tx, nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(*block.Receipts[0], block.Hash, nil) mockSyncReader.EXPECT().PendingData().Return(&preConfirmedPlaceHolder, nil) } @@ -288,7 +294,9 @@ func TestTransactionStatus(t *testing.T) { mockReader *mocks.MockReader, mockSyncReader *mocks.MockSyncReader, ) { - mockReader.EXPECT().TransactionByHash(gomock.Any()).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + gomock.Any(), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return(&preConfirmedPlaceHolder, nil).Times(2) } @@ -351,9 +359,9 @@ func TestTransactionStatus(t *testing.T) { Execution: rpcv9.UnknownExecution, }, setupMocks: func(mockReader *mocks.MockReader, mockSyncReader *mocks.MockSyncReader) { - mockReader.EXPECT().TransactionByHash( - targetTxnHash, - ).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(targetTxnHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return( &core.PreConfirmed{ Block: &core.Block{ @@ -545,7 +553,9 @@ func TestSubmittedTransactionsCache(t *testing.T) { res, err := rpcv9Handler.AddTransaction(ctx, broadcastedTxn) require.Nil(t, err) - mockReader.EXPECT().TransactionByHash(res.TransactionHash).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(res.TransactionHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return(&preConfirmedPlaceHolder, nil).Times(2) status, err := handler.TransactionStatus(ctx, res.TransactionHash) @@ -577,7 +587,9 @@ func TestSubmittedTransactionsCache(t *testing.T) { res, err := rpcv9Handler.AddTransaction(ctx, broadcastedTxn) require.Nil(t, err) - mockReader.EXPECT().TransactionByHash(res.TransactionHash).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(res.TransactionHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return(&preConfirmedPlaceHolder, nil).Times(2) // Expire cache entry diff --git a/rpc/v10/transactions.go b/rpc/v10/transactions.go index 99526dbecc..e474fd7309 100644 --- a/rpc/v10/transactions.go +++ b/rpc/v10/transactions.go @@ -81,7 +81,7 @@ func (h *Handler) TransactionReceiptByHash( return adaptedReceipt, nil } - txn, err := h.bcReader.TransactionByHash(hash) + blockNumber, idx, err := h.bcReader.BlockNumberAndIndexByTxHash((*felt.TransactionHash)(hash)) if err != nil { if !errors.Is(err, db.ErrKeyNotFound) { return nil, rpccore.ErrInternal.CloneWithData(err) @@ -89,7 +89,15 @@ func (h *Handler) TransactionReceiptByHash( return nil, rpccore.ErrTxnHashNotFound } - receipt, blockHash, blockNumber, err := h.bcReader.Receipt(hash) + txn, err := h.bcReader.TransactionByBlockNumberAndIndex(blockNumber, idx) + if err != nil { + if !errors.Is(err, db.ErrKeyNotFound) { + return nil, rpccore.ErrInternal.CloneWithData(err) + } + return nil, rpccore.ErrTxnHashNotFound + } + + receipt, blockHash, err := h.bcReader.ReceiptByBlockNumberAndIndex(blockNumber, idx) if err != nil { return nil, rpccore.ErrTxnHashNotFound } @@ -105,7 +113,7 @@ func (h *Handler) TransactionReceiptByHash( } return rpcv9.AdaptReceiptWithBlockInfo( - receipt, + &receipt, txn, status, blockHash, diff --git a/rpc/v8/l1_test.go b/rpc/v8/l1_test.go index 1607907678..53df2d4212 100644 --- a/rpc/v8/l1_test.go +++ b/rpc/v8/l1_test.go @@ -91,8 +91,15 @@ func TestGetMessageStatus(t *testing.T) { nil, ) // Expects for h.TransactionStatus() - mockReader.EXPECT().TransactionByHash(msg.L1HandlerHash).Return(l1handlerTxns[i], nil) - mockReader.EXPECT().Receipt(msg.L1HandlerHash).Return(block.Receipts[0], block.Hash, block.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(msg.L1HandlerHash), + ).Return(block.Number, uint64(0), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(l1handlerTxns[i], nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(*block.Receipts[0], block.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{BlockNumber: uint64(test.l1HeadBlockNum)}, nil) } msgStatuses, rpcErr := handler.GetMessageStatus(t.Context(), &test.l1TxnHash) diff --git a/rpc/v8/subscriptions_test.go b/rpc/v8/subscriptions_test.go index 5ba5d205a9..77268a267d 100644 --- a/rpc/v8/subscriptions_test.go +++ b/rpc/v8/subscriptions_test.go @@ -278,7 +278,9 @@ func TestSubscribeTxnStatus(t *testing.T) { mockSyncer := mocks.NewMockSyncReader(mockCtrl) handler := New(mockChain, mockSyncer, nil, log) - mockChain.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound).AnyTimes() + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound).AnyTimes() mockSyncer.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound).AnyTimes() mockChain.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound).AnyTimes() id, _ := createTestTxStatusWebsocket(t, handler, txHash) @@ -305,7 +307,9 @@ func TestSubscribeTxnStatus(t *testing.T) { txHash, err := new(felt.Felt).SetString("0x1011") require.NoError(t, err) - mockChain.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) id, conn := createTestTxStatusWebsocket(t, handler, txHash) assertNextTxnStatus(t, conn, id, txHash, TxnStatusAcceptedOnL2, TxnFailure, "some error") }) @@ -314,7 +318,9 @@ func TestSubscribeTxnStatus(t *testing.T) { txHash, err := new(felt.Felt).SetString("0x1111") require.NoError(t, err) - mockChain.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) id, conn := createTestTxStatusWebsocket(t, handler, txHash) assertNextTxnStatus(t, conn, id, txHash, TxnStatusRejected, 0, "some error") }) @@ -323,7 +329,9 @@ func TestSubscribeTxnStatus(t *testing.T) { txHash, err := new(felt.Felt).SetString("0x1010") require.NoError(t, err) - mockChain.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) id, conn := createTestTxStatusWebsocket(t, handler, txHash) assertNextTxnStatus(t, conn, id, txHash, TxnStatusAcceptedOnL1, TxnSuccess, "") }) @@ -346,14 +354,23 @@ func TestSubscribeTxnStatus(t *testing.T) { txHash, err := new(felt.Felt).SetString("0x1001") require.NoError(t, err) - mockChain.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + gomock.Any(), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncer.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound) mockChain.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound) id, conn := createTestTxStatusWebsocket(t, handler, txHash) assertNextTxnStatus(t, conn, id, txHash, TxnStatusReceived, TxnSuccess, "") - mockChain.EXPECT().TransactionByHash(txHash).Return(block.Transactions[0], nil) - mockChain.EXPECT().Receipt(txHash).Return(block.Receipts[0], block.Hash, block.Number, nil) + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(block.Number, uint64(0), nil) + mockChain.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(block.Transactions[0], nil) + mockChain.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(*block.Receipts[0], block.Hash, nil) mockChain.EXPECT().L1Head().Return(core.L1Head{}, db.ErrKeyNotFound) for i := range 3 { handler.pendingData.Send(&core.Pending{Block: &core.Block{Header: &core.Header{}}}) @@ -363,8 +380,15 @@ func TestSubscribeTxnStatus(t *testing.T) { assertNextTxnStatus(t, conn, id, txHash, TxnStatusAcceptedOnL2, TxnSuccess, "") l1Head := core.L1Head{BlockNumber: block.Number} - mockChain.EXPECT().TransactionByHash(txHash).Return(block.Transactions[0], nil) - mockChain.EXPECT().Receipt(txHash).Return(block.Receipts[0], block.Hash, block.Number, nil) + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(block.Number, uint64(0), nil) + mockChain.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(block.Transactions[0], nil) + mockChain.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(*block.Receipts[0], block.Hash, nil) mockChain.EXPECT().L1Head().Return(l1Head, nil) handler.l1Heads.Send(&l1Head) assertNextTxnStatus(t, conn, id, txHash, TxnStatusAcceptedOnL1, TxnSuccess, "") diff --git a/rpc/v8/transaction.go b/rpc/v8/transaction.go index e3a742cd95..701334abfb 100644 --- a/rpc/v8/transaction.go +++ b/rpc/v8/transaction.go @@ -545,7 +545,8 @@ func (h *Handler) TransactionReceiptByHash(hash felt.Felt) (*TransactionReceipt, pendingBIndex int ) - txn, err := h.bcReader.TransactionByHash(&hash) + txh := felt.TransactionHash(hash) + blockNumber, idx, err := h.bcReader.BlockNumberAndIndexByTxHash(&txh) if err != nil { if !errors.Is(err, db.ErrKeyNotFound) { return nil, rpccore.ErrInternal.CloneWithData(err) @@ -556,6 +557,7 @@ func (h *Handler) TransactionReceiptByHash(hash felt.Felt) (*TransactionReceipt, return nil, rpccore.ErrTxnHashNotFound } + var txn core.Transaction for i, t := range pendingB.Transactions { if hash.Equal(t.Hash()) { pendingBIndex = i @@ -567,37 +569,36 @@ func (h *Handler) TransactionReceiptByHash(hash felt.Felt) (*TransactionReceipt, if txn == nil { return nil, rpccore.ErrTxnHashNotFound } - } - var ( - receipt *core.TransactionReceipt - blockHash *felt.Felt - blockNumber uint64 - ) + receipt := pendingB.Receipts[pendingBIndex] + return AdaptReceipt(receipt, txn, TxnAcceptedOnL2, nil, 0), nil + } - if pendingB != nil { - receipt = pendingB.Receipts[pendingBIndex] - } else { - receipt, blockHash, blockNumber, err = h.bcReader.Receipt(&hash) - if err != nil { - return nil, rpccore.ErrTxnHashNotFound + txn, err := h.bcReader.TransactionByBlockNumberAndIndex(blockNumber, idx) + if err != nil { + if !errors.Is(err, db.ErrKeyNotFound) { + return nil, rpccore.ErrInternal.CloneWithData(err) } + return nil, rpccore.ErrTxnHashNotFound } - status := TxnAcceptedOnL2 + receipt, blockHash, err := h.bcReader.ReceiptByBlockNumberAndIndex(blockNumber, idx) + if err != nil { + return nil, rpccore.ErrTxnHashNotFound + } + status := TxnAcceptedOnL2 if blockHash != nil { l1H, jsonErr := h.l1Head() if jsonErr != nil { return nil, jsonErr } - if isL1Verified(blockNumber, l1H) { status = TxnAcceptedOnL1 } } - return AdaptReceipt(receipt, txn, status, blockHash, blockNumber), nil + return AdaptReceipt(&receipt, txn, status, blockHash, blockNumber), nil } // AddTransaction relays a transaction to the gateway, or to the sequencer if enabled diff --git a/rpc/v8/transaction_test.go b/rpc/v8/transaction_test.go index 911fd82cb5..eaccfb8513 100644 --- a/rpc/v8/transaction_test.go +++ b/rpc/v8/transaction_test.go @@ -618,7 +618,9 @@ func TestTransactionReceiptByHash(t *testing.T) { t.Run("transaction not found", func(t *testing.T) { txHash := new(felt.Felt).SetBytes([]byte("random hash")) - mockReader.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + gomock.Any(), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound) mockReader.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound) @@ -705,8 +707,15 @@ func TestTransactionReceiptByHash(t *testing.T) { for name, test := range tests { t.Run(name, func(t *testing.T) { txHash := block0.Transactions[test.index].Hash() - mockReader.EXPECT().TransactionByHash(txHash).Return(block0.Transactions[test.index], nil) - mockReader.EXPECT().Receipt(txHash).Return(block0.Receipts[test.index], block0.Hash, block0.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + gomock.Any(), + ).Return(block0.Number, uint64(test.index), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block0.Number, uint64(test.index), + ).Return(block0.Transactions[test.index], nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block0.Number, uint64(test.index), + ).Return(*block0.Receipts[test.index], block0.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{}, db.ErrKeyNotFound) checkTxReceipt(t, txHash, test.expected) @@ -740,7 +749,9 @@ func TestTransactionReceiptByHash(t *testing.T) { }` txHash := block0.Transactions[i].Hash() - mockReader.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + gomock.Any(), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) pending := core.NewPending(block0, nil, nil) mockSyncReader.EXPECT().PendingData().Return( &pending, @@ -778,8 +789,15 @@ func TestTransactionReceiptByHash(t *testing.T) { }` txHash := block0.Transactions[i].Hash() - mockReader.EXPECT().TransactionByHash(txHash).Return(block0.Transactions[i], nil) - mockReader.EXPECT().Receipt(txHash).Return(block0.Receipts[i], block0.Hash, block0.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(block0.Number, uint64(i), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block0.Number, uint64(i), + ).Return(block0.Transactions[i], nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block0.Number, uint64(i), + ).Return(*block0.Receipts[i], block0.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{ BlockNumber: block0.Number, BlockHash: block0.Hash, @@ -816,9 +834,15 @@ func TestTransactionReceiptByHash(t *testing.T) { revertedTxnIdx := 1 revertedTxnHash := blockWithRevertedTxn.Transactions[revertedTxnIdx].Hash() - mockReader.EXPECT().TransactionByHash(revertedTxnHash).Return(blockWithRevertedTxn.Transactions[revertedTxnIdx], nil) - mockReader.EXPECT().Receipt(revertedTxnHash).Return(blockWithRevertedTxn.Receipts[revertedTxnIdx], - blockWithRevertedTxn.Hash, blockWithRevertedTxn.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(revertedTxnHash), + ).Return(blockWithRevertedTxn.Number, uint64(revertedTxnIdx), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + blockWithRevertedTxn.Number, uint64(revertedTxnIdx), + ).Return(blockWithRevertedTxn.Transactions[revertedTxnIdx], nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + blockWithRevertedTxn.Number, uint64(revertedTxnIdx), + ).Return(*blockWithRevertedTxn.Receipts[revertedTxnIdx], blockWithRevertedTxn.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{}, db.ErrKeyNotFound) checkTxReceipt(t, revertedTxnHash, expected) @@ -880,9 +904,15 @@ func TestTransactionReceiptByHash(t *testing.T) { index := 0 txnHash := block.Transactions[index].Hash() - mockReader.EXPECT().TransactionByHash(txnHash).Return(block.Transactions[index], nil) - mockReader.EXPECT().Receipt(txnHash).Return(block.Receipts[index], - block.Hash, block.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txnHash), + ).Return(block.Number, uint64(index), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(index), + ).Return(block.Transactions[index], nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(index), + ).Return(*block.Receipts[index], block.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{}, db.ErrKeyNotFound) checkTxReceipt(t, txnHash, expected) @@ -930,9 +960,15 @@ func TestTransactionReceiptByHash(t *testing.T) { index := 0 txnHash := block.Transactions[index].Hash() - mockReader.EXPECT().TransactionByHash(txnHash).Return(block.Transactions[index], nil) - mockReader.EXPECT().Receipt(txnHash).Return(block.Receipts[index], - block.Hash, block.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txnHash), + ).Return(block.Number, uint64(index), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(index), + ).Return(block.Transactions[index], nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(index), + ).Return(*block.Receipts[index], block.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{}, db.ErrKeyNotFound) checkTxReceipt(t, txnHash, expected) @@ -1363,8 +1399,15 @@ func TestTransactionStatus(t *testing.T) { t.Run("not verified", func(t *testing.T) { mockReader := mocks.NewMockReader(mockCtrl) - mockReader.EXPECT().TransactionByHash(tx.Hash()).Return(tx, nil) - mockReader.EXPECT().Receipt(tx.Hash()).Return(block.Receipts[0], block.Hash, block.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(tx.Hash()), + ).Return(block.Number, uint64(0), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(tx, nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(*block.Receipts[0], block.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{}, nil) handler := rpc.New(mockReader, nil, nil, nil) @@ -1379,8 +1422,15 @@ func TestTransactionStatus(t *testing.T) { }) t.Run("verified", func(t *testing.T) { //nolint:dupl mockReader := mocks.NewMockReader(mockCtrl) - mockReader.EXPECT().TransactionByHash(tx.Hash()).Return(tx, nil) - mockReader.EXPECT().Receipt(tx.Hash()).Return(block.Receipts[0], block.Hash, block.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(tx.Hash()), + ).Return(block.Number, uint64(0), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(tx, nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(*block.Receipts[0], block.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{ BlockNumber: block.Number + 1, }, nil) @@ -1397,8 +1447,15 @@ func TestTransactionStatus(t *testing.T) { }) t.Run("verified v0.7.0", func(t *testing.T) { //nolint:dupl mockReader := mocks.NewMockReader(mockCtrl) - mockReader.EXPECT().TransactionByHash(tx.Hash()).Return(tx, nil) - mockReader.EXPECT().Receipt(tx.Hash()).Return(block.Receipts[0], block.Hash, block.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(tx.Hash()), + ).Return(block.Number, uint64(0), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(tx, nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(*block.Receipts[0], block.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{ BlockNumber: block.Number + 1, }, nil) @@ -1433,7 +1490,10 @@ func TestTransactionStatus(t *testing.T) { t.Run(description, func(t *testing.T) { mockReader := mocks.NewMockReader(mockCtrl) mockSyncReader := mocks.NewMockSyncReader(mockCtrl) - mockReader.EXPECT().TransactionByHash(notFoundTest.hash).Return(nil, db.ErrKeyNotFound).Times(2) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(notFoundTest.hash), + ).Return( + uint64(0), uint64(0), db.ErrKeyNotFound).Times(2) mockSyncReader.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound).Times(2) mockReader.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound).Times(2) handler := rpc.New(mockReader, mockSyncReader, nil, log) @@ -1452,7 +1512,9 @@ func TestTransactionStatus(t *testing.T) { t.Run("transaction not found in db and feeder ", func(t *testing.T) { mockReader := mocks.NewMockReader(mockCtrl) mockSyncReader := mocks.NewMockSyncReader(mockCtrl) - mockReader.EXPECT().TransactionByHash(test.notFoundTxHash).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(test.notFoundTxHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound) mockReader.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound) handler := rpc.New(mockReader, mockSyncReader, nil, log).WithFeeder(client) @@ -1472,7 +1534,9 @@ func TestTransactionStatus(t *testing.T) { require.NoError(t, err) handler := rpc.New(mockReader, mockSyncReader, nil, log).WithFeeder(client) - mockReader.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound) mockReader.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound) @@ -1941,7 +2005,9 @@ func TestSubmittedTransactionsCache(t *testing.T) { res, err := handler.AddTransaction(ctx, broadcastedTxn) require.Nil(t, err) - mockReader.EXPECT().TransactionByHash(res.TransactionHash).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(res.TransactionHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound) mockReader.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound) status, err := handler.TransactionStatus(ctx, *res.TransactionHash) @@ -1966,7 +2032,9 @@ func TestSubmittedTransactionsCache(t *testing.T) { res, err := handler.AddTransaction(ctx, broadcastedTxn) require.Nil(t, err) - mockReader.EXPECT().TransactionByHash(res.TransactionHash).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(res.TransactionHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound) mockReader.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound) // Expire cache entry diff --git a/rpc/v9/l1_test.go b/rpc/v9/l1_test.go index de03eec8bc..a1a53c62aa 100644 --- a/rpc/v9/l1_test.go +++ b/rpc/v9/l1_test.go @@ -40,8 +40,8 @@ func TestGetMessageStatus(t *testing.T) { msgs []rpc.MsgStatus msgHashes []common.Hash l1TxnReceipt types.Receipt - blockNum uint - l1HeadBlockNum uint + blockNum uint64 + l1HeadBlockNum uint64 }{ "mainnet 0.13.2.1": { network: utils.Mainnet, @@ -77,7 +77,7 @@ func TestGetMessageStatus(t *testing.T) { t.Run(name, func(t *testing.T) { client := feeder.NewTestClient(t, &test.network) gw := adaptfeeder.New(client) - block, err := gw.BlockByNumber(t.Context(), uint64(test.blockNum)) + block, err := gw.BlockByNumber(t.Context(), test.blockNum) require.NoError(t, err) preConfirmed := &core.PreConfirmed{ @@ -103,9 +103,16 @@ func TestGetMessageStatus(t *testing.T) { nil, ) // Expects for h.TransactionStatus() - mockReader.EXPECT().TransactionByHash(msg.L1HandlerHash).Return(l1handlerTxns[i], nil) - mockReader.EXPECT().Receipt(msg.L1HandlerHash).Return(block.Receipts[0], block.Hash, block.Number, nil) - mockReader.EXPECT().L1Head().Return(core.L1Head{BlockNumber: uint64(test.l1HeadBlockNum)}, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(msg.L1HandlerHash), + ).Return(block.Number, uint64(i), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(i), + ).Return(l1handlerTxns[i], nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(i), + ).Return(*block.Receipts[i], block.Hash, nil) + mockReader.EXPECT().L1Head().Return(core.L1Head{BlockNumber: test.l1HeadBlockNum}, nil) } msgStatuses, rpcErr := handler.GetMessageStatus(t.Context(), &test.l1TxnHash) require.Nil(t, rpcErr) diff --git a/rpc/v9/subscriptions_test.go b/rpc/v9/subscriptions_test.go index 4eb010d595..2d3695c028 100644 --- a/rpc/v9/subscriptions_test.go +++ b/rpc/v9/subscriptions_test.go @@ -960,7 +960,9 @@ func TestSubscribeTxnStatus(t *testing.T) { cache := rpccore.NewTransactionCache(cacheEntryTimeOut, cacheSize) handler := New(mockChain, mockSyncer, nil, log).WithSubmittedTransactionsCache(cache) - mockChain.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound).AnyTimes() + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound).AnyTimes() mockSyncer.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound).AnyTimes() mockChain.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound).AnyTimes() mockSyncer.EXPECT().PendingBlock().Return(nil).AnyTimes() @@ -988,8 +990,9 @@ func TestSubscribeTxnStatus(t *testing.T) { txHash, err := felt.NewFromString[felt.Felt]("0x1011") require.NoError(t, err) - mockChain.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) - + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) id, conn := createTestTxStatusWebsocket(t, handler, txHash) assertNextTxnStatus(t, conn, id, txHash, TxnStatusAcceptedOnL2, TxnFailure, "some error") }) @@ -997,7 +1000,9 @@ func TestSubscribeTxnStatus(t *testing.T) { txHash, err := felt.NewFromString[felt.Felt]("0x1010") require.NoError(t, err) - mockChain.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) id, conn := createTestTxStatusWebsocket(t, handler, txHash) assertNextTxnStatus(t, conn, id, txHash, TxnStatusAcceptedOnL1, TxnSuccess, "") }) @@ -1043,7 +1048,9 @@ func TestSubscribeTxnStatus(t *testing.T) { ) require.Nil(t, addErr) txHash := addRes.TransactionHash - mockChain.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockChain.EXPECT().BlockNumberAndIndexByTxHash((*felt.TransactionHash)(txHash)).Return( + uint64(0), uint64(0), db.ErrKeyNotFound, + ) mockSyncer.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound).Times(2) mockChain.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound).Times(2) @@ -1051,7 +1058,9 @@ func TestSubscribeTxnStatus(t *testing.T) { assertNextTxnStatus(t, conn, id, txHash, TxnStatusReceived, UnknownExecution, "") // Candidate Status - mockChain.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockChain.EXPECT().BlockNumberAndIndexByTxHash((*felt.TransactionHash)(txHash)).Return( + uint64(0), uint64(0), db.ErrKeyNotFound, + ) preConfirmed := &core.PreConfirmed{ Block: &core.Block{ Header: &core.Header{ @@ -1110,8 +1119,15 @@ func TestSubscribeTxnStatus(t *testing.T) { nil, ).Times(1) // Accepted on l2 Status - mockChain.EXPECT().TransactionByHash(txHash).Return(block.Transactions[0], nil) - mockChain.EXPECT().Receipt(txHash).Return(block.Receipts[0], block.Hash, block.Number, nil) + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(block.Number, uint64(0), nil) + mockChain.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(block.Transactions[0], nil) + mockChain.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(*block.Receipts[0], block.Hash, nil) mockChain.EXPECT().L1Head().Return(core.L1Head{}, db.ErrKeyNotFound) handler.newHeads.Send(block) @@ -1122,8 +1138,14 @@ func TestSubscribeTxnStatus(t *testing.T) { nil, ).Times(1) l1Head := core.L1Head{BlockNumber: block.Number} - mockChain.EXPECT().TransactionByHash(txHash).Return(block.Transactions[0], nil) - mockChain.EXPECT().Receipt(txHash).Return(block.Receipts[0], block.Hash, block.Number, nil) + mockChain.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(block.Number, uint64(0), nil) + mockChain.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(0)).Return(block.Transactions[0], nil) + mockChain.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(*block.Receipts[0], block.Hash, nil) mockChain.EXPECT().L1Head().Return(l1Head, nil) handler.l1Heads.Send(&l1Head) assertNextTxnStatus(t, conn, id, txHash, TxnStatusAcceptedOnL1, TxnSuccess, "") diff --git a/rpc/v9/transaction.go b/rpc/v9/transaction.go index 5693a0be20..94789bc71d 100644 --- a/rpc/v9/transaction.go +++ b/rpc/v9/transaction.go @@ -611,7 +611,7 @@ func (h *Handler) TransactionReceiptByHash(hash *felt.Felt) (*TransactionReceipt return adaptedReceipt, nil } - txn, err := h.bcReader.TransactionByHash(hash) + blockNumber, idx, err := h.bcReader.BlockNumberAndIndexByTxHash((*felt.TransactionHash)(hash)) if err != nil { if !errors.Is(err, db.ErrKeyNotFound) { return nil, rpccore.ErrInternal.CloneWithData(err) @@ -619,7 +619,15 @@ func (h *Handler) TransactionReceiptByHash(hash *felt.Felt) (*TransactionReceipt return nil, rpccore.ErrTxnHashNotFound } - receipt, blockHash, blockNumber, err := h.bcReader.Receipt(hash) + txn, err := h.bcReader.TransactionByBlockNumberAndIndex(blockNumber, idx) + if err != nil { + if !errors.Is(err, db.ErrKeyNotFound) { + return nil, rpccore.ErrInternal.CloneWithData(err) + } + return nil, rpccore.ErrTxnHashNotFound + } + + receipt, blockHash, err := h.bcReader.ReceiptByBlockNumberAndIndex(blockNumber, idx) if err != nil { return nil, rpccore.ErrTxnHashNotFound } @@ -635,7 +643,7 @@ func (h *Handler) TransactionReceiptByHash(hash *felt.Felt) (*TransactionReceipt } return AdaptReceiptWithBlockInfo( - receipt, + &receipt, txn, status, blockHash, diff --git a/rpc/v9/transaction_test.go b/rpc/v9/transaction_test.go index d805fe1a18..7bb79afc1d 100644 --- a/rpc/v9/transaction_test.go +++ b/rpc/v9/transaction_test.go @@ -683,7 +683,9 @@ func TestTransactionReceiptByHash(t *testing.T) { t.Run("transaction not found", func(t *testing.T) { txHash := felt.NewFromBytes[felt.Felt]([]byte("random hash")) - mockReader.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound) mockReader.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound) @@ -777,8 +779,15 @@ func TestTransactionReceiptByHash(t *testing.T) { }, }, }, nil) - mockReader.EXPECT().TransactionByHash(txHash).Return(block0.Transactions[test.index], nil) - mockReader.EXPECT().Receipt(txHash).Return(block0.Receipts[test.index], block0.Hash, block0.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(block0.Number, uint64(test.index), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block0.Number, uint64(test.index), + ).Return(block0.Transactions[test.index], nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block0.Number, uint64(test.index), + ).Return(*block0.Receipts[test.index], block0.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{}, db.ErrKeyNotFound) checkTxReceipt(t, txHash, test.expected) @@ -961,8 +970,15 @@ func TestTransactionReceiptByHash(t *testing.T) { }, nil, ) - mockReader.EXPECT().TransactionByHash(txHash).Return(block0.Transactions[i], nil) - mockReader.EXPECT().Receipt(txHash).Return(block0.Receipts[i], block0.Hash, block0.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(block0.Number, uint64(i), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block0.Number, uint64(i), + ).Return(block0.Transactions[i], nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block0.Number, uint64(i), + ).Return(*block0.Receipts[i], block0.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{ BlockNumber: block0.Number, BlockHash: block0.Hash, @@ -1009,9 +1025,15 @@ func TestTransactionReceiptByHash(t *testing.T) { }, nil, ).Times(1) - mockReader.EXPECT().TransactionByHash(revertedTxnHash).Return(blockWithRevertedTxn.Transactions[revertedTxnIdx], nil) - mockReader.EXPECT().Receipt(revertedTxnHash).Return(blockWithRevertedTxn.Receipts[revertedTxnIdx], - blockWithRevertedTxn.Hash, blockWithRevertedTxn.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(revertedTxnHash), + ).Return(blockWithRevertedTxn.Number, uint64(revertedTxnIdx), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + blockWithRevertedTxn.Number, uint64(revertedTxnIdx), + ).Return(blockWithRevertedTxn.Transactions[revertedTxnIdx], nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + blockWithRevertedTxn.Number, uint64(revertedTxnIdx), + ).Return(*blockWithRevertedTxn.Receipts[revertedTxnIdx], blockWithRevertedTxn.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{}, db.ErrKeyNotFound) checkTxReceipt(t, revertedTxnHash, expected) @@ -1082,9 +1104,15 @@ func TestTransactionReceiptByHash(t *testing.T) { }, nil, ).Times(1) - mockReader.EXPECT().TransactionByHash(txnHash).Return(block.Transactions[index], nil) - mockReader.EXPECT().Receipt(txnHash).Return(block.Receipts[index], - block.Hash, block.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txnHash), + ).Return(block.Number, uint64(index), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(index), + ).Return(block.Transactions[index], nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(index), + ).Return(*block.Receipts[index], block.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{}, db.ErrKeyNotFound) checkTxReceipt(t, txnHash, expected) @@ -1142,9 +1170,15 @@ func TestTransactionReceiptByHash(t *testing.T) { }, nil, ).Times(1) - mockReader.EXPECT().TransactionByHash(txnHash).Return(block.Transactions[index], nil) - mockReader.EXPECT().Receipt(txnHash).Return(block.Receipts[index], - block.Hash, block.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txnHash), + ).Return(block.Number, uint64(index), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(index), + ).Return(block.Transactions[index], nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(index), + ).Return(*block.Receipts[index], block.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{}, db.ErrKeyNotFound) checkTxReceipt(t, txnHash, expected) @@ -1615,8 +1649,15 @@ func TestTransactionStatus(t *testing.T) { }, }, }, nil) - mockReader.EXPECT().TransactionByHash(tx.Hash()).Return(tx, nil) - mockReader.EXPECT().Receipt(tx.Hash()).Return(block.Receipts[0], block.Hash, block.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(tx.Hash()), + ).Return(block.Number, uint64(0), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(tx, nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(*block.Receipts[0], block.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{}, nil) handler := rpc.New(mockReader, mockSyncReader, nil, nil) @@ -1639,8 +1680,15 @@ func TestTransactionStatus(t *testing.T) { }, }, }, nil) - mockReader.EXPECT().TransactionByHash(tx.Hash()).Return(tx, nil) - mockReader.EXPECT().Receipt(tx.Hash()).Return(block.Receipts[0], block.Hash, block.Number, nil) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(tx.Hash()), + ).Return(block.Number, uint64(0), nil) + mockReader.EXPECT().TransactionByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(tx, nil) + mockReader.EXPECT().ReceiptByBlockNumberAndIndex( + block.Number, uint64(0), + ).Return(*block.Receipts[0], block.Hash, nil) mockReader.EXPECT().L1Head().Return(core.L1Head{ BlockNumber: block.Number + 1, }, nil) @@ -1675,7 +1723,9 @@ func TestTransactionStatus(t *testing.T) { t.Run(description, func(t *testing.T) { mockReader := mocks.NewMockReader(mockCtrl) mockSyncReader := mocks.NewMockSyncReader(mockCtrl) - mockReader.EXPECT().TransactionByHash(notFoundTest.hash).Return(nil, db.ErrKeyNotFound).Times(2) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(notFoundTest.hash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound).Times(2) mockSyncReader.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound).Times(4) mockReader.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound).Times(4) handler := rpc.New(mockReader, mockSyncReader, nil, log) @@ -1694,7 +1744,9 @@ func TestTransactionStatus(t *testing.T) { t.Run("transaction not found in db and feeder ", func(t *testing.T) { mockReader := mocks.NewMockReader(mockCtrl) mockSyncReader := mocks.NewMockSyncReader(mockCtrl) - mockReader.EXPECT().TransactionByHash(test.notFoundTxHash).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(test.notFoundTxHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound).Times(2) mockReader.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound).Times(2) handler := rpc.New(mockReader, mockSyncReader, nil, log).WithFeeder(client) @@ -1720,7 +1772,9 @@ func TestTransactionStatus(t *testing.T) { t.Run("found in candidates", func(t *testing.T) { require.Greater(t, len(preConfirmed.CandidateTxs), 0) for _, candidateTx := range preConfirmed.CandidateTxs { - mockReader.EXPECT().TransactionByHash(candidateTx.Hash()).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(candidateTx.Hash()), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return(&preConfirmed, nil).Times(2) status, err := handler.TransactionStatus(ctx, candidateTx.Hash()) @@ -1767,7 +1821,9 @@ func TestTransactionStatus(t *testing.T) { require.NoError(t, err) handler := rpc.New(mockReader, mockSyncReader, nil, log).WithFeeder(client) - mockReader.EXPECT().TransactionByHash(txHash).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(txHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound).Times(2) mockReader.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound).Times(2) @@ -2235,7 +2291,9 @@ func TestSubmittedTransactionsCache(t *testing.T) { res, err := handler.AddTransaction(ctx, broadcastedTxn) require.Nil(t, err) - mockReader.EXPECT().TransactionByHash(res.TransactionHash).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(res.TransactionHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound).Times(2) mockReader.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound).Times(2) @@ -2262,7 +2320,9 @@ func TestSubmittedTransactionsCache(t *testing.T) { res, err := handler.AddTransaction(ctx, broadcastedTxn) require.Nil(t, err) - mockReader.EXPECT().TransactionByHash(res.TransactionHash).Return(nil, db.ErrKeyNotFound) + mockReader.EXPECT().BlockNumberAndIndexByTxHash( + (*felt.TransactionHash)(res.TransactionHash), + ).Return(uint64(0), uint64(0), db.ErrKeyNotFound) mockSyncReader.EXPECT().PendingData().Return(nil, core.ErrPendingDataNotFound).Times(2) mockReader.EXPECT().HeadsHeader().Return(nil, db.ErrKeyNotFound).Times(2)