loomnetwork / loomchain

Loom DAppChain Engine
Other
166 stars 32 forks source link

Review nonce handling strategy #1360

Closed enlight closed 5 years ago

enlight commented 5 years ago

Failed evm txs can be included in blocks, but they cannot alter chain state in any way, only successful txs can change chain state. The per-account tx nonce is stored in chain state so it doesn't get incremented when an EVM tx fails. We should investigate the possibility of incrementing nonces for failed txs to better emulate Ethereum,

pathornteng commented 5 years ago

Saving nonce in chain state makes us unable to update nonce when transactions revert. One possible solution is to move all account nonces to receipts_db so it does not change app hash when we update nonce. In web3, it is using get_TransactionCount to determine nonce of an account when sending a new tx.

Update: checking the go-etheruem code, it seems that it saves nonce in account object in chain state. So I think failed txs change chain state. https://github.com/ethereum/wiki/wiki/Patricia-Tree

enlight commented 5 years ago

OK, then we should probably increment the nonce if a tx fails in DeliverTx, but we need to be super careful that only the nonce is updated, and all other state changes are discarded.

enlight commented 5 years ago

We can modify NonceHandler so it can be given a KVStore (reference to Application.Store) when the nonce middleware are constructed. That way the NonceHandler can increment the nonce unconditionally in DeliverTx directly via the KVStore.

pathornteng commented 5 years ago

Expected NonceMiddleware Behavior

  1. If a tx has been included in BlockStore, account nonce should increment. This means EVM reverted tx will increment account nonce.
  2. An account can submit multiple txs in one blocks
  3. A tx with speculative nonce can get Invallid nonce if the previous tx does not pass CheckTx

Example Block 1

  1. Successful Tx -> nonce = 1
  2. EVM reverted Tx -> nonce = 2
  3. Successful Tx -> nonce = 3

Block 2

  1. Successful Tx -> nonce = 4
  2. Error Tx (Tx does not pass CheckTx) -> nonce = 4
  3. Successful Tx -> nonce = 5

As you can see, in block 2, if a client sends tx 6 with speculative nonce 6, the server will return Invalid Nonce because the tx 5 does not pass CheckTx and account nonce does not get incremented.

enlight commented 5 years ago

Should be resolved by https://github.com/loomnetwork/loomchain/pull/1410