[Solved] [@aeternity/aepp-sdk v6.0.0] Errors while calling contract methods simultaneously on testnet

Hello.

We have functionality that invokes a contract method multiple times simultaneously [in testnet].

Pseudocode:

const contractInstance = await ae.getContractInstance(contentOfContract, contractOptions);

for (let i = 0; i < n; i++) {
    contractInstance.call("contractMethod").then(...).catch(...)
}

And once in a while, it starts throwing two types of errors.

It’s either
Error: While calling getTransactionByHash (hash), GET to https://sdk-testnet.aepps.com/v2/transactions/th_2igSv4mXrsy8HGYV7XdUnrBqiLMRWKGjPH9wKRuPQ8iN8ymiRu failed with 404: Transaction not found\n at Object.sendTransaction

or

Error: While calling postTransaction (body), POST to https://sdk-testnet.aepps.com/v2/transactions failed with 400: Invalid tx"}

But sometimes all of the transactions are passing successfully.

What could possibly cause this behavior?

Additional details:

  • JS SDK version - @aeternity/aepp-sdk v6.0.0
  • We are using an old version of the contract which has been built before the Lima hardfork

Hey @nduchak.chain can you support here, please?

2 Likes

Hey @markin.io, the problem is when you are creating transaction simultaneously each time SDK make an API call to the node to get the account nonce, which will be the same for each of your contract-call transaction. So in your case, only one of your transactions have a chance to be mined after that all other transaction will be treated as invalid because of too low nonce.

The solution is to control nonce by yourself

const contractInstance = await ae.getContractInstance(contentOfContract, contractOptions);
let { nonce } = await ae.api.getAccountByPubkey(await ae.address()).catch(() => ({ nonce: 0 }))

for (let i = 0; i < n; i++) {
    nonce += 1
    contractInstance.call("contractMethod", [...callArgumetns], { nonce }).then(...).catch(...)
}
1 Like

Hey Nazar. Thank you for response.

I’ve tried to increment nonce manually as you proposed, but the problem is still appearing, unfortunately.
Sooner or later I’m starting to get lots of Error: While calling postTransaction (body), POST to https://sdk-testnet.aepps.com/v2/transactions failed with 400: Invalid tx"}.

ahh, so you need to send transaction one after another(when previous one is mined and nonce of account is updated) or prepare some queue for that as node do not accept transaction only with valid nonce

Hm. Unfortunately this does not seem to be suitable solution, because we are expecting to have a huge volume of the transactions which can happen in parallel, and maintaining a queue for that can be a problem.

I am wondering, why do we need to maintain our own queue, if the blockchain has the mempool already? Why can’t we simply burst a bunch of transactions and wait for them to finish whenever it’s possible?

Is this limitation documented anywhere? Perhaps you can advice some documentation which I can read and figure out more details? Or perhaps there’s even examples with the parallel execution of the contract methods?

Thank you!

So looks like you can,
i try this snippet and it’s works for me as expected

const cInstance = await ae.getContractInstance(identityContract)
await cInstance.deploy()
const { nonce } = await ae.getAccount(await ae.address())
await Promise.all([
      cInstance.call('main', [1], { nonce: nonce + 1, verify: false }).then(console.log).catch(console.log),
      cInstance.call('main', [2], { nonce: nonce + 2, verify: false }).then(console.log).catch(console.log),
      cInstance.call('main', [3], { nonce: nonce + 3, verify: false }).then(console.log).catch(console.log)
])

Can you please try again, if the error still present try to update SDK to the latest version

Also you need to be carful with that because if some of your transaction fail then all other transaction become invalid because of nonce

I’ve made nonce to be incremented as you proposed and upgraded the SDK. But unfortunately, it didn’t work out.

Will continue digging into that. Thank you for the suggestions.

Hey Nazar. Excuse me, do you know if there any documented examples with the parallel execution of the contract methods?

Also, do you think our problem possibly can be caused because of the gas expenditure?

Hello @markin.io, I did solve this similar as @nduchak.chain did describe in the past. You need to be careful to check if one of your transactions fails, as then you have a gap in your nonces. You could also use a secondary contract and remote calls to call that function multiple times, with just one transaction from the network.

2 Likes

JS SDK version - @aeternity/aepp-sdk v6.0.0
We are using an old version of the contract which has been built before the Lima hardfork

Maybe you should also consider migrating to Lima contract as this will save you a lot of tx fees as operations are way cheaper after the update.

Consider also SDK > v7.2.0.

3 Likes

Hey Philipp! How many transactions have you tried to create at once?

It works with 5 transactions for me, but all the subsequent are failing with "While calling postTransaction (body), POST to https://sdk-testnet.aepps.com/v2/transactions failed with 400: Invalid tx" error.

can you try using a different node. Running my own node I had no issues with ~500 at once.

1 Like

Hey Philipp! Thank you for a suggestion. I have ran a node in localnet using the aeproject, but the issue persists.

The configuration looks this way:

aeternityUrl: http://localhost:3001
aeternityPrivateKey: ...
aeternityPublicKey: ak_2mwRmUeYmfuW93ti9HMSUJzCk1EYcQEfikVSzgo6k2VghsWhgU
aeternityCompilerUrl: https://latest.compiler.aepps.com
aeternityContractAddress: ct_HVb6d4kirgqzY1rShmzRTRwukcsXobjHcpLVD2EggoHmn6wt2
aeternityNetworkId: ae_devnet

And the initialization like this:

Node({ url: config.aeternityUrl })
    .then(node => {
      Ae({
        nodes: [{ name: "node", instance: node }],
        compilerUrl: config.aeternityCompilerUrl,
        accounts: [
          MemoryAccount({
            keypair: {
              secretKey: config.aeternityPrivateKey,
              publicKey: config.aeternityPublicKey
            }
          })
        ],
        address: config.aeternityPublicKey,
        networkId: config.aeternityNetworkId 
      })

After that I am getting the contract instance and calling method and incrementing the nonce accordingly.
Can you perhaps spot any misusage of the SDK?

I was thinking that maybe there’s a problem with our contract itself, so I deployed a demo version of the contract from this link https://contracts.aepps.com/#/ and tried to execute parallel transactions with it as well.

The error While calling postTransaction (body), POST to http://localhost:3001/v2/transactions failed with 400: Invalid tx still appears.

@nduchak.chain @philipp.chain one really weird thing I have noticed - if I increment the nonce, then 4 out of 10 parallel calls failing stably.
If I don’t increment nonce at all, the failure rate is quite random. Sometimes I even get all 10 parallel calls executed successfully.

Is there a way to have more verbose logs of this process?

Hey,
So basically if you are using the latest SDK version you ca catch the error of specific contract call, the error object will contain the verifyTx() function which you need to execute.
Exm:

cInstance.call('main', [1], { nonce: nonce + 1, verify: false }).catch(asyn e => console.log(await e.verifyTx())),

In result of this call you will get the possible errors and unpacked transaction with all transaction params.

Thank you. I’ve finally got more details:
The error says 'The nonce is technically valid but will not be processed immediately by the node (next valid nonce is 193)',

It sounds like the internal limitation, and we have to implement our own queue to handle the execution of parallel transactions as you suggested in the very beginning. Is that correct?

Also there are additional TX details coming out:

{ tag: '43',
     VSN: '1',
     callerId: 'ak_2mwRmUeYmfuW93ti9HMSUJzCk1EYcQEfikVSzgo6k2VghsWhgU',
     nonce: '198',
     contractId: 'ct_2XthVMBhX5axwK7beF95HTUjr2QvyHcRGNJeeiJibicU2xa55d',
     abiVersion: '3',
     fee: '452180000000000',
     ttl: '0',
     amount: '0',
     gas: '1579000',
     gasPrice: '1000000000',
     callData: 'cb_KxFoEVXIGxl2YWx1ZTGSKzKL' },

Does this mean, that transaction fee getting charged anyway? Even though the node has rejected it.

The error about nonce is just a warning.
Then you don’t charge any fee if your TX not accepted.

Also, could you try to manually set the fee for each of your transactions,
the same as you did with nonce, just another field fee: '111111111'
Try to put the bigger fee that you currently has, this will help us to understand if it’s the fee calculation problem.

Sorry for the inconvenience, what node are you using? I just remembered there is a configuration parameter for this, the publicly hosted nodes run the default config so they may not accept transaction “spam”.

There is a nonce_offset: 5 configured, which explains your issues.

At https://testnet.aeternity.art/ I run a node that has nonce_offset: 50 configured. Can you check if this resolves your issue? Please don’t rely on that node for long time, as it is my personal setup and I don’t guarantee any availability.

1 Like

Thank you very much! It’s finally resolved.

Is there any chance you know what is the value of this parameter on https://sdk-mainnet.aepps.com/.
Is this 5 as well?

1 Like