Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: Reuse v6 starknet_getTransactionByHash handler for v7 #2486

Merged
merged 9 commits into from
Mar 6, 2025
2 changes: 1 addition & 1 deletion rpc/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,7 @@ func (h *Handler) MethodsV0_7() ([]jsonrpc.Method, string) { //nolint: funlen
{
Name: "starknet_getTransactionByHash",
Params: []jsonrpc.Parameter{{Name: "transaction_hash"}},
Handler: h.rpcv7Handler.TransactionByHash,
Handler: h.rpcv6Handler.TransactionByHash,
},
{
Name: "starknet_getTransactionReceipt",
Expand Down
27 changes: 24 additions & 3 deletions rpc/v6/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/NethermindEth/juno/clients/gateway"
"github.com/NethermindEth/juno/core"
"github.com/NethermindEth/juno/core/felt"
"github.com/NethermindEth/juno/db"
"github.com/NethermindEth/juno/jsonrpc"
rpccore "github.com/NethermindEth/juno/rpc/rpccore"
"github.com/NethermindEth/juno/starknet"
Expand Down Expand Up @@ -434,8 +435,28 @@ func adaptRPCTxToFeederTx(rpcTx *Transaction) *starknet.Transaction {
func (h *Handler) TransactionByHash(hash felt.Felt) (*Transaction, *jsonrpc.Error) {
txn, err := h.bcReader.TransactionByHash(&hash)
if err != nil {
return nil, rpccore.ErrTxnHashNotFound
if !errors.Is(err, db.ErrKeyNotFound) {
return nil, rpccore.ErrInternal.CloneWithData(err)
}

// check now if tx is in pending block
pendingB := h.syncReader.PendingBlock()
if pendingB == nil {
return nil, rpccore.ErrTxnHashNotFound
}

for _, t := range pendingB.Transactions {
if hash.Equal(t.Hash()) {
txn = t
break
}
}

if txn == nil {
return nil, rpccore.ErrTxnHashNotFound
}
}

return AdaptTransaction(txn), nil
}

Expand Down Expand Up @@ -672,7 +693,7 @@ func AdaptTransaction(t core.Transaction) *Transaction {
case *core.DeclareTransaction:
txn = adaptDeclareTransaction(v)
case *core.DeployAccountTransaction:
txn = adaptDeployAccountTrandaction(v)
txn = adaptDeployAccountTransaction(v)
case *core.L1HandlerTransaction:
nonce := v.Nonce
if nonce == nil {
Expand Down Expand Up @@ -816,7 +837,7 @@ func adaptDeclareTransaction(t *core.DeclareTransaction) *Transaction {
return tx
}

func adaptDeployAccountTrandaction(t *core.DeployAccountTransaction) *Transaction {
func adaptDeployAccountTransaction(t *core.DeployAccountTransaction) *Transaction {
tx := &Transaction{
Hash: t.Hash(),
MaxFee: t.MaxFee,
Expand Down
114 changes: 104 additions & 10 deletions rpc/v6/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,19 +24,113 @@ import (
)

func TestTransactionByHashNotFound(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
mockReader := mocks.NewMockReader(mockCtrl)
t.Run("internal error", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
mockReader := mocks.NewMockReader(mockCtrl)
mockSyncReader := mocks.NewMockSyncReader(mockCtrl)

n := utils.HeapPtr(utils.Mainnet)
txHash := new(felt.Felt).SetBytes([]byte("random hash"))
mockReader.EXPECT().TransactionByHash(txHash).Return(nil, errors.New("tx not found"))
randomTxHash := new(felt.Felt).SetBytes([]byte("random hash"))
mockReader.EXPECT().TransactionByHash(randomTxHash).Return(nil, errors.New("some internal error"))

handler := rpc.New(mockReader, nil, nil, "", n, nil)
n := &utils.Mainnet
handler := rpc.New(mockReader, mockSyncReader, nil, "", n, nil)
tx, rpcErr := handler.TransactionByHash(*randomTxHash)

assert.Nil(t, tx)
assert.Equal(t, rpcErr, rpccore.ErrInternal.CloneWithData(errors.New("some internal error")))
})

t.Run("tx not found and no pending block", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
mockReader := mocks.NewMockReader(mockCtrl)
mockSyncReader := mocks.NewMockSyncReader(mockCtrl)

randomTxHash := new(felt.Felt).SetBytes([]byte("random hash"))
mockReader.EXPECT().TransactionByHash(randomTxHash).Return(nil, db.ErrKeyNotFound)
mockSyncReader.EXPECT().PendingBlock().Return(nil)

n := &utils.Mainnet
handler := rpc.New(mockReader, mockSyncReader, nil, "", n, nil)
tx, rpcErr := handler.TransactionByHash(*randomTxHash)

assert.Nil(t, tx)
assert.Equal(t, rpccore.ErrTxnHashNotFound, rpcErr)
})

t.Run("tx found in pending block", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
mockReader := mocks.NewMockReader(mockCtrl)
mockSyncReader := mocks.NewMockSyncReader(mockCtrl)

n := &utils.Mainnet
client := feeder.NewTestClient(t, n)
mainnetGw := adaptfeeder.New(client)

block, err := mainnetGw.BlockByNumber(context.Background(), 19199)
require.NoError(t, err)

tx, rpcErr := handler.TransactionByHash(*txHash)
assert.Nil(t, tx)
assert.Equal(t, rpccore.ErrTxnHashNotFound, rpcErr)
txAtIdx1InBlock := utils.HexToFelt(t, "0x5f3d9e538af40474c894820d2c0d0e8f92ee8fef92e2254f0b06e306f88dcc8")
mockReader.EXPECT().TransactionByHash(txAtIdx1InBlock).Return(nil, db.ErrKeyNotFound)
mockSyncReader.EXPECT().PendingBlock().Return(block)

handler := rpc.New(mockReader, mockSyncReader, nil, "", n, nil)
tx, rpcErr := handler.TransactionByHash(*txAtIdx1InBlock)

expectedTx := rpc.Transaction{
Type: rpc.TxnInvoke,
Hash: txAtIdx1InBlock,
MaxFee: utils.HexToFelt(t, "0x65d5eabc5218"),
Version: utils.HexToFelt(t, "0x0"),
Signature: &[]*felt.Felt{
utils.HexToFelt(t, "0x2ccb8d2b482d67d8358482832705549d1e5278dc4d04878d9f8256a47423d6a"),
utils.HexToFelt(t, "0x6958e023ab0ffa07a84bd4e79032a5f2312ca4a2937585e49534877d13ea918"),
},
Nonce: nil,
CallData: &[]*felt.Felt{
utils.HexToFelt(t, "0x1"),
utils.HexToFelt(t, "0x4a4479e16bf55ebbe7ccb36f438060d994fac69c75e2edfaf00ae56d45d5796"),
utils.HexToFelt(t, "0x1474f761b9a93b1c727b60fb4cc7aa6c6c1c866ad7f1cd88ec9545ff065ddad"),
utils.HexToFelt(t, "0x0"),
utils.HexToFelt(t, "0x1"),
utils.HexToFelt(t, "0x1"),
utils.HexToFelt(t, "0x6b648b36b074a91eee55730f5f5e075ec19c0a8f9ffb0903cefeee93b6ff328"),
utils.HexToFelt(t, "0x7d1"),
},
ContractAddress: utils.HexToFelt(t, "0x4a4479e16bf55ebbe7ccb36f438060d994fac69c75e2edfaf00ae56d45d5796"),
SenderAddress: nil,
EntryPointSelector: utils.HexToFelt(t, "0x15d40a3d6ca2ac30f4031e42be28da9b056fef9bb7357ac5e85627ee876e5ad"),
}

assert.Nil(t, rpcErr)
assert.Equal(t, &expectedTx, tx)
})

t.Run("tx not found anywhere", func(t *testing.T) {
mockCtrl := gomock.NewController(t)
t.Cleanup(mockCtrl.Finish)
mockReader := mocks.NewMockReader(mockCtrl)
mockSyncReader := mocks.NewMockSyncReader(mockCtrl)

n := &utils.Mainnet
client := feeder.NewTestClient(t, n)
mainnetGw := adaptfeeder.New(client)

block, err := mainnetGw.BlockByNumber(context.Background(), 19199)
require.NoError(t, err)

randomTxHash := new(felt.Felt).SetBytes([]byte("random hash"))
mockReader.EXPECT().TransactionByHash(randomTxHash).Return(nil, db.ErrKeyNotFound)
mockSyncReader.EXPECT().PendingBlock().Return(block)

handler := rpc.New(mockReader, mockSyncReader, nil, "", n, nil)
tx, rpcErr := handler.TransactionByHash(*randomTxHash)

assert.Nil(t, tx)
assert.Equal(t, rpccore.ErrTxnHashNotFound, rpcErr)
})
}

func TestTransactionByHash(t *testing.T) {
Expand Down
11 changes: 8 additions & 3 deletions rpc/v7/block_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,11 @@ func TestBlockWithTxs(t *testing.T) {
n := &utils.Mainnet
handler := rpcv7.New(mockReader, mockSyncReader, nil, "", n, nil)

// Use v6 handler as v7 `starknet_getTransactionByHash` uses v6 handler
mockSyncReaderV6 := mocks.NewMockSyncReader(mockCtrl)
mockReaderV6 := mocks.NewMockReader(mockCtrl)
rpcv6Handler := rpcv6.New(mockReaderV6, mockSyncReaderV6, nil, "", n, nil)

client := feeder.NewTestClient(t, n)
gw := adaptfeeder.New(client)

Expand All @@ -267,10 +272,10 @@ func TestBlockWithTxs(t *testing.T) {
assert.Equal(t, len(blockWithTxHashes.TxnHashes), len(blockWithTxs.Transactions))

for i, txnHash := range blockWithTxHashes.TxnHashes {
txn, err := handler.TransactionByHash(*txnHash)
txn, err := rpcv6Handler.TransactionByHash(*txnHash)
require.Nil(t, err)

assert.Equal(t, txn, blockWithTxs.Transactions[i])
assert.Equal(t, adaptV6TxToV7(t, txn), blockWithTxs.Transactions[i])
}
}

Expand All @@ -279,7 +284,7 @@ func TestBlockWithTxs(t *testing.T) {
latestBlockTxMap[*tx.Hash()] = tx
}

mockReader.EXPECT().TransactionByHash(gomock.Any()).DoAndReturn(func(hash *felt.Felt) (core.Transaction, error) {
mockReaderV6.EXPECT().TransactionByHash(gomock.Any()).DoAndReturn(func(hash *felt.Felt) (core.Transaction, error) {
if tx, found := latestBlockTxMap[*hash]; found {
return tx, nil
}
Expand Down
30 changes: 0 additions & 30 deletions rpc/v7/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -427,36 +427,6 @@ func adaptRPCTxToFeederTx(rpcTx *Transaction) *starknet.Transaction {
Transaction Handlers
*****************************************************/

// TransactionByHash returns the details of a transaction identified by the given hash.
//
// It follows the specification defined here:
// https://github.com/starkware-libs/starknet-specs/blob/master/api/starknet_api_openrpc.json#L158
func (h *Handler) TransactionByHash(hash felt.Felt) (*Transaction, *jsonrpc.Error) {
txn, err := h.bcReader.TransactionByHash(&hash)
if err != nil {
if !errors.Is(err, db.ErrKeyNotFound) {
return nil, rpccore.ErrInternal.CloneWithData(err)
}

pendingB := h.syncReader.PendingBlock()
if pendingB == nil {
return nil, rpccore.ErrTxnHashNotFound
}

for _, t := range pendingB.Transactions {
if hash.Equal(t.Hash()) {
txn = t
break
}
}

if txn == nil {
return nil, rpccore.ErrTxnHashNotFound
}
}
return AdaptTransaction(txn), nil
}

// TransactionByBlockIDAndIndex returns the details of a transaction identified by the given
// BlockID and index.
//
Expand Down
Loading
Loading