web3.js icon indicating copy to clipboard operation
web3.js copied to clipboard

If sending a transaction stuck at the node, the code waiting for the transaction stuck forever

Open Muhammad-Altabba opened this issue 2 years ago • 0 comments

Is there an existing issue for this?

  • [X] I have searched the existing issues

Current Behavior

If a transaction was not mined, like in case the nonce was high, the code waiting for the transaction stuck forever. this means that both transactionReceiptPollingInterval and transactionPollingTimeout does not work for this case.

Expected Behavior

If the node does not return a response to web3.js, the library should still try polling for the transaction. Additionally, if transactionPollingTimeout passed the code should terminate.

Steps to Reproduce

const eth = new Web3Eth(clientUrl);

const from = accounts[0];
const to = accounts[1];
const value = `0x1`;

const sentTx: Web3PromiEvent<
    TransactionReceipt,
    SendTransactionEvents<typeof DEFAULT_RETURN_FORMAT>
> = eth.sendTransaction({
    to,
    value: '0x99999',
    from,
    nonce: Number.MAX_SAFE_INTEGER,
});

await sentTx; // this call stuck and do not terminate after transactionPollingTimeout pass

Web3.js Version

4.x

Environment

  • Operating System: Linux Mint
  • Node.js Version: v16.16.0
  • Yarn Version: v1.22.19

Anything Else?

No response

Muhammad-Altabba avatar Jul 28 '22 16:07 Muhammad-Altabba

It turned out that there are a couple of issues combined together that made the transaction to stuck when raising the nonce. And it is different between Ganache and Geth, where one of them never returns the response when sending a transaction. And the other Ethereum Node showed another behavior that is not also handled, where it returns a response when sending a transaction. But is stuck after that when trying to get the transaction receipt. It is important to handle these 2 behaviors of Ethereum Nodes. Because it could happen if the Node is jammed for example.

However, here are the found 3 issues described in more detail:

  1. The Node does not respond back when we ask to send a transaction. Where the code just stuck forever waiting for the Node response. (this behavior was observed with Ganache v7.4.0 and could happen with other Node implementations and more importantly if the Node was jammed for example).

And for handling this case, a new timeout configuration is used with the name transactionSendTimeout and the new error TransactionSendTimeoutError with code 431 is raised if that happens.

  1. And the other Ethereum Node showed another behavior, where it returns a response when sending a transaction. But is stuck after that when trying to get the transaction receipt. In this case, both transactionReceiptPollingInterval and transactionPollingTimeout does not work because the current code handles the case when the Node responds. But, if the request to the Node stuck, the code stuck waiting for it, forever. (this behavior was observed with Geth v1.10.22-unstable and could happen with other Node implementations and more importantly if the Node was jammed for example).

  2. TransactionPollingTimeoutError is un-catchable because it is thrown inside a Promise. This issue is different from the previously mentioned. As even if the Node responded when asked for a transaction receipt, if the response did not contain the receipt, and this kept happening for a duration of TransactionPollingTimeout, the raised exception was not catchable.

The first mentioned 2 issues are under progress at https://github.com/ChainSafe/web3.js/pull/5311 The third issue is identified at https://github.com/ChainSafe/web3.js/issues/5334

Muhammad-Altabba avatar Aug 11 '22 16:08 Muhammad-Altabba

For point 1. and 2. we can add additional logic for transactionPollingTimeout to reject promise after timeout based on either:

  • A. reject when node didn't respond back transaction hash and transactionPollingTimeout passed
  • B. node responded transaction hash but transaction receipt was not available during transactionPollingTimeout

Instead of rejecting promise for B only or even current logic missing that.

Good work for finding these detailed scenarios.

jdevcs avatar Aug 12 '22 08:08 jdevcs

Thanks @jdevcs :bouquet: As discussed on chat later, even that, unfortunately, that would mean additional config that the package user needs to care about, we will add transactionSendTimeout. Because it addresses a unique case and also enables raising a related error that could provide more help in identifying and tracing issues.

And so the first 2 issues will be implemented as suggested in https://github.com/ChainSafe/web3.js/issues/5293#issuecomment-1212192200 and the MR for that is: https://github.com/ChainSafe/web3.js/pull/5311

Muhammad-Altabba avatar Aug 12 '22 20:08 Muhammad-Altabba