以太坊交易中存在一个特殊的值nonce,此nonce并非计算block难度的nonce,此nonce仅仅表示发送账号发送交易的次数,从0开始,每发送一次交易+1,那么第一次发送nonce为0,第二次为1,以此类推。nonce的存在可以用来防止重放攻击,也就是同一个交易只能被发送一次,下次发送同一个交易时,因为nonce值和最新的nonce不同,会被区块链拒绝。
我们来从代码层面看看这个nonce的生成和检测。
我们可以从一张图来看这个nonce的来龙去脉。
可以看到这张图上存在一个关键性的三角关系。
- 三角形上面顶点是区块链blockchain。
- 左边顶点是postseal()中的nonce,postseal()是从区块链获取到的最新的区块,那么左边顶点表示当前区块链中最新区块该发送账号的nonce。
- 右边顶点是交易队列中的nonce
 当我们提交一个交易时,交易的nonce取值是左右两个顶点的nonce值中取最大值。然后再与blockchain中最新块的nonce进行比较,如果不同,则区块链拒绝此交易。那么问题来了,postseal()中不就是最新块的nonce吗?为什么还需要再次比较?这是因为postseal()并不是一直与区块链同步的,只有满足某些条件才会同步。另外当交易成功提交后,该交易在正式被区块链确认前,是被存放在交易队列中的,此时右顶点的nonce值为该交易的nonce+1。
我们来一步一步拆开来看:
第一步,假设该发送者从来没有发送过交易,那么他的nonce值应该为0,上面的图会变成:
此时blockchain和postseal()中均没有该账号的信息,nonce值为初始值0。 交易队列中没有该发送账号的交易,因此nonce值也是0,那么该发送者第一次提交交易时,nonce值会被设置为max(0, 0),也就是0。然后再比较0与0是相等的,那么此交易被正确发送。
第二步,交易被发送到交易队列,这张图变成:
此时上顶点和左顶点的nonce值不变,右顶点因为交易队列中已有一个该发送者发送的交易,那么nonce+1,变成1。
此时如果该发送者想再发一个交易,那么新交易的nonce会被设置为max(0, 1),也就是1。然后再与上顶点nonce比较,得出不相等的结论,此交易被拒绝,提交失败!
第三步,第一个交易被区块链确认,这张图变成:
交易被确认后,上顶点blockchain的nonce变成1,左顶点因为同步,nonce也变成1,而右顶点交易队列会删除掉已确认的交易,所以没有该发送者的交易了,nonce就为0。此时如果该发送者再发一个交易,新交易的nonce会被设置为max(1, 0),也就是1。然后再与上顶点nonce比较,结果相等,该交易被成功发出!
交易被发出后被发到交易队列,流程就同第二步了。
下面我们来看看具体涉及到的代码实现:
| 1 | TransactionSkeleton Client::populateTransactionWithDefaults(TransactionSkeleton const& _t) const | 
这段代码是提交交易时设置交易的一些参数,其中
| 1 | ret.nonce = max<u256>(postSeal().transactionsFrom(ret.from), m_tq.maxNonce(ret.from)); | 
就是设置交易的nonce,是左顶点和右顶点的nonce值的最大值。
再来看交易队列中的nonce:
| 1 | u256 TransactionQueue::maxNonce(Address const& _a) const | 
注意那个+1,这个表示当前队列中该发送者最大的nonce再+1。
而postseal()从区块链同步的代码在:
| 1 | void Client::restartMining() | 
省略了一些次要代码,从这里可以看出,先是从区块链同步最新信息到newPreMine,然后拷贝到m_postSeal,也就是postseal()。
那么Client::restartMining()是从哪里调用的呢?
有两个地方,但是一般都是从Client::resyncStateFromChain()这里调用的:
| 1 | void Client::resyncStateFromChain() | 
Client::resyncStateFromChain()函数又是从哪里调用呢?
也有两个地方,一处是Client::onChainChanged()表示区块链同步新块的时候,另一处是Client::syncTransactionQueue()表示同步交易队列中交易的时候。
由此可见,以太坊通过这些机制保证nonce与发送账号的发送次数严格对应,如果发送交易不遵守这条规则,则发送可能会失败,错误码为InvalidNonce

