关于nonce-too-high问题的详细分析

关于ethereum 中,nonce问题总结:

  • Tx 中 nonce 过低(too low)时,Tx 立即被拒绝
  • Tx 中 nonce 过高(too high)时,Tx 被放入交易池队列 transaction pool queue
  • 如果 Tx 的 nonce 正好填补了最新的有效的 nonce 和过高的nonce之间的空隙,是的nonce的顺序完整连接时,交易池队列中的交易将被执行
  • 当 Geth 被关掉或者重新启动时,交易池中的 Txs 将消失

nonce问题,详细介绍:

  • 当 Tx 中 nonce 过低时,
1
2
3
4
5
6
> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(1, "ether"), nonce:0})
Nonce too low
at InvalidResponse (<anonymous>:-81662:-106)
at send (<anonymous>:-156322:-106)
at sendTransaction (<anonymous>:-133322:-106)
at <anonymous>:1:1
  • 当 Tx 中 nonce 过高时,
1
2
3
4
5
> txpool.status
{
pending: 0,
queued: 1
}
  • Geth 重启时,
1
2
3
4
5
> txpool.status
{
pending: 0,
queued: 0
}

解决方案

  • 在应用本地保存nonce在数据库中,并且,数据库中的nonce值更新时需要访问锁保护。不建议将nonce保存在内存中,那样的话,在应用死机或者重启时,nonce将丢失。目前思考 Redis DB 是比较好的选择。

  • 如果本地nonce出了问题,可以使用 web3.eth.getTransactionCount(ethAddress) 来恢复。但是 web3.eth.getTransactionCount(ethAddress) 是内存中的值,不是实时准确的,不建议实时使用。

另外:

  • 即使交易池(queue)中有交易存在,nonce正确的交易仍然可以继续进行。

  • 当共识停止工作了,nonce正确的交易进入到pending队列中,nonce较高的交易进入到queue队列中。当共识重新正常工作时,pending队列中的交易会被自动处理而清空。

  • 同时节点具有每隔一个小时,重新处理一次本地交易的能力。(handle local transaction journal rotation)

参考链接 ==> https://ethereum.stackexchange.com/questions/2808/what-happens-when-a-transaction-nonce-is-too-high

参考代码 ==>

1
2
3
4
5
ethereum/go-ethereum/core/tx_pool.go 

func (pool *TxPool) loop() {
......
}

本问题调查过程中,使用到的命令==>

1
2
3
4
5
6
7
8
9
10
11
12
13
eth.getTransactionCount(eth.accounts[0])

personal.unlockAccount(eth.accounts[0],"1234",99999)

eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[0],value:100,nonce:200})

txpool.status

eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[0]})

txpool.status

./odyssey update_Validator localhost:26657 UpdateValidator:OA+kUHbBDxh6q2kqF1jNQFoPqjTYsTS2m17un4Z9vI0=*/*10000