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

feat: implement methods for eth_getTransactionByBlockNumberAndIndex and eth_getTransactionByBlockHashAndIndex #12618

Merged
merged 13 commits into from
Oct 25, 2024
Merged
75 changes: 75 additions & 0 deletions itests/fevm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1436,3 +1436,78 @@ func TestEthGetBlockByNumber(t *testing.T) {
require.NotNil(t, pendingBlock)
require.True(t, pendingBlock.Number >= latest)
}

func TestEthGetTransactionByBlockHashAndIndex(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()

key, ethAddr, filAddr := client.EVM().NewAccount()
// Send some funds to the f410 address
kit.SendFunds(ctx, t, client, filAddr, types.FromFil(10))

// Deploy the Blocktest contract
tx := deployContractWithEth(ctx, t, client, ethAddr, "./contracts/MultipleEvents.hex")
client.EVM().SignTransaction(tx, key.PrivateKey)
hash := client.EVM().SubmitTransaction(ctx, tx)

receipt, err := client.EVM().WaitTransaction(ctx, hash)
require.NoError(t, err)
require.NotNil(t, receipt)
require.Equal(t, ethtypes.EthUint64(1), receipt.Status)

// Use the block hash from the receipt
blockHash := receipt.BlockHash

// Get the block by its hash
block, err := client.EthGetBlockByHash(ctx, blockHash, false)
require.NoError(t, err)
require.NotNil(t, block)

ethTx, err := client.EthGetTransactionByBlockHashAndIndex(ctx, block.Hash, ethtypes.EthUint64(0))
virajbhartiya marked this conversation as resolved.
Show resolved Hide resolved
require.NoError(t, err)
require.NotNil(t, ethTx)
require.Equal(t, hash, ethTx.Hash)
}

func TestEthGetTransactionByBlockNumberAndIndex(t *testing.T) {
ctx, cancel, client := kit.SetupFEVMTest(t)
defer cancel()

key, ethAddr, filAddr := client.EVM().NewAccount()
// Send some funds to the f410 address
kit.SendFunds(ctx, t, client, filAddr, types.FromFil(10))

tx := deployContractWithEth(ctx, t, client, ethAddr, "./contracts/MultipleEvents.hex")
client.EVM().SignTransaction(tx, key.PrivateKey)
hash := client.EVM().SubmitTransaction(ctx, tx)

receipt, err := client.EVM().WaitTransaction(ctx, hash)
require.NoError(t, err)
require.NotNil(t, receipt)
require.Equal(t, ethtypes.EthUint64(1), receipt.Status)

blockHash := receipt.BlockHash

// Get the block by its hash
block, err := client.EthGetBlockByHash(ctx, blockHash, false)
require.NoError(t, err)
require.NotNil(t, block)

require.NotNil(t, receipt.BlockNumber)
require.Greater(t, receipt.BlockNumber, ethtypes.EthUint64(0))

ethTx, err := client.EthGetTransactionByBlockNumberAndIndex(ctx, receipt.BlockNumber, ethtypes.EthUint64(0))
if err != nil {
t.Logf("Error getting transaction: %v", err)
block, blockErr := client.EthGetBlockByNumber(ctx, receipt.BlockNumber.Hex(), false)
if blockErr != nil {
t.Logf("Error getting block: %v", blockErr)
} else {
t.Logf("Block exists, transaction count: %d", len(block.Transactions))
}
}

require.NoError(t, err)
require.NotNil(t, ethTx)
require.Equal(t, hash, ethTx.Hash)
}
75 changes: 71 additions & 4 deletions node/impl/full/eth.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ type EthAPI struct {

EthModuleAPI
EthEventAPI
StateAPI StateAPI
virajbhartiya marked this conversation as resolved.
Show resolved Hide resolved
}

var ErrNullRound = errors.New("requested epoch was a null round")
Expand Down Expand Up @@ -570,12 +571,78 @@ func (a *EthModule) EthGetTransactionReceiptLimited(ctx context.Context, txHash
return &receipt, nil
}

func (a *EthAPI) EthGetTransactionByBlockHashAndIndex(context.Context, ethtypes.EthHash, ethtypes.EthUint64) (ethtypes.EthTx, error) {
return ethtypes.EthTx{}, ErrUnsupported
func (a *EthAPI) EthGetTransactionByBlockHashAndIndex(ctx context.Context, blkHash ethtypes.EthHash, index ethtypes.EthUint64) (ethtypes.EthTx, error) {
ts, err := a.Chain.GetTipSetByCid(ctx, blkHash.ToCid())
virajbhartiya marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return ethtypes.EthTx{}, xerrors.Errorf("failed to find tipset for block hash %s: %w", blkHash, err)
}

return a.getTransactionByTipsetAndIndex(ctx, ts, index)
}

func (a *EthAPI) EthGetTransactionByBlockNumberAndIndex(context.Context, ethtypes.EthUint64, ethtypes.EthUint64) (ethtypes.EthTx, error) {
return ethtypes.EthTx{}, ErrUnsupported
func (a *EthAPI) EthGetTransactionByBlockNumberAndIndex(ctx context.Context, blkNum ethtypes.EthUint64, index ethtypes.EthUint64) (ethtypes.EthTx, error) {
virajbhartiya marked this conversation as resolved.
Show resolved Hide resolved
ts, err := getTipsetByBlockNumber(ctx, a.Chain, strconv.FormatUint(uint64(blkNum), 10), false)
virajbhartiya marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return ethtypes.EthTx{}, xerrors.Errorf("failed to get tipset: %w", err)
}
return a.getTransactionByTipsetAndIndex(ctx, ts, index)
}
func (a *EthAPI) getTransactionByTipsetAndIndex(ctx context.Context, ts *types.TipSet, index ethtypes.EthUint64) (ethtypes.EthTx, error) {
virajbhartiya marked this conversation as resolved.
Show resolved Hide resolved
msgs, err := a.Chain.MessagesForTipset(ctx, ts)
if err != nil {
log.Errorf("Failed to get messages for tipset: %v", err)
return ethtypes.EthTx{}, xerrors.Errorf("failed to get messages for tipset: %w", err)
}

if uint64(index) >= uint64(len(msgs)) {
log.Errorf("Transaction index %d out of bounds (total messages: %d)", index, len(msgs))
return ethtypes.EthTx{}, xerrors.Errorf("transaction index out of bounds")
virajbhartiya marked this conversation as resolved.
Show resolved Hide resolved
}

msg := msgs[index]

log.Debugf("Searching for message with CID %s in tipset %s", msg.Cid(), ts.Key())

msgLookup, err := a.StateAPI.StateSearchMsg(ctx, ts.Key(), msg.Cid(), 10, false)
virajbhartiya marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
log.Errorf("Failed to search for message: %v", err)
return ethtypes.EthTx{}, xerrors.Errorf("failed to search for message: %w", err)
}

if msgLookup == nil {
log.Debugf("Message not found in chain, attempting retry")
for i := 0; i < 3; i++ {
time.Sleep(time.Second * 2)
msgLookup, err = a.StateAPI.StateSearchMsg(ctx, ts.Key(), msg.Cid(), 10, false)
if err != nil {
log.Errorf("Retry %d failed to search for message: %v", i+1, err)
continue
}
if msgLookup != nil {
break
}
}
}

if msgLookup == nil {
log.Debugf("Message not found in chain after retries, returning pending transaction")
pendingTx, err := newEthTxFromSignedMessage(&types.SignedMessage{Message: *msg.VMMessage()}, nil)
if err != nil {
log.Errorf("Failed to create pending Ethereum transaction: %v", err)
return ethtypes.EthTx{}, xerrors.Errorf("failed to create pending Ethereum transaction: %w", err)
}
return pendingTx, nil
}

log.Debugf("Message found in chain: %+v", msgLookup)

tx, err := newEthTxFromMessageLookup(ctx, msgLookup, -1, a.Chain, a.StateAPI)
virajbhartiya marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
log.Errorf("Failed to convert message to Ethereum transaction: %v", err)
return ethtypes.EthTx{}, xerrors.Errorf("failed to convert message to Ethereum transaction: %w", err)
}
log.Debugf("Retrieved transaction: %+v", tx)
return tx, nil
}

func (a *EthModule) EthGetBlockReceipts(ctx context.Context, blockParam ethtypes.EthBlockNumberOrHash) ([]*api.EthTxReceipt, error) {
Expand Down
Loading