以太坊nonce重复问题

环境

  • 节点A:1192.168.45.9
  • 节点B:1192.168.45.10

节点A和节点B集群(节点B连接到节点A)

说明

为了防止交易重播,ETH(ETC)节点要求每笔交易必须有一个nonce数值。每一个账户从同一个节点发起交易时,这个nonce值从0开始计数,发送一笔nonce对应加1。当前面的nonce处理完成之后才会处理后面的nonce。集群环境下,不同节点共同维护同一个用户的nonce值。


txpool中由两部分构成pendingqueued组成,一个为待打包状态,一个为队列中。如果传入的nonce就是某用户下笔交易应该传入的nonce,那么该笔交易就会放置在pending中,等待节点打包。其次,如果传入的nonce值过大,在进入txpool中检查到它之前的nonce并没有使用过,那么此笔交易不会发送到pending中,而且放置在queued中。只有当前面的nonce补齐之后,才会进入到pending中。


集群环境下,写入节点A的pending交易会广播到节点B中,因nonce问题写入节点A的queued交易,不会被广播。


集群环境下,节点B连上节点A(admin.addPeer(节点A)),节点A停掉的情况下再次启动,会很快恢复集群,但是如果是节点B停掉的情况下,却不会再次恢复集群,只能重新连接节点,才能恢复集群B。

操作结论

  • 1、发送一笔nonce为0的交易给节点A,会返回交易hash,并能在节点A的txpoolpending看到,因为广播足够快,节点B中也能看到。
{
    "id":0,
    "jsonrpc":"2.0",
    "result":"0x354185703c2460d9b3a51fa6f1473da5b83cc5fd85eb823fb112137684d89691",
    "transactionHash":"0x354185703c2460d9b3a51fa6f1473da5b83cc5fd85eb823fb112137684d89691"
}

这里写图片描述

  • 2、再次发送nonce为0的交易(交易金额和gas price等全部保持不变)给节点A,响应结果如下,表明并没有提交成功
{
    "error":{
        "code":-32000,
        "message":"known transaction: 354185703c2460d9b3a51fa6f1473da5b83cc5fd85eb823fb112137684d89691"
    },
    "id":0,
    "jsonrpc":"2.0"
}
  • 2、继续测试,再次发送nonce为0的交易给节点A,并且把gas price价格提高(必须超过10%),响应结果如下
{
    "id":0,
    "jsonrpc":"2.0",
    "result":"0x42155ca4d8fdfc3eb063234330d06bd5da16e3ab4afbc5362ad22351a3556408",
    "transactionHash":"0x42155ca4d8fdfc3eb063234330d06bd5da16e3ab4afbc5362ad22351a3556408"
}

并且txpool中可以看到该笔交易会把之前的交易替换掉,如下

这里写图片描述

也就是说,针对pending里的交易相同nonce再次提交,并且提高gas price,后面的交易可以覆盖之前的交易。

  • 4、如果碰到提交给节点A的pending交易,并没有及时同步到节点B,但此时同样的nonce的交易再次提交到节点B中,是可以提交进去的,但是提交的交易的hash和节点A的hash都是一样的(我们可以认为两次发送的交易就是同一笔交易)

如果我们重复上面的情况,依然是在节点A的交易没有及时同步到节点B中,但是我们往节点B中提交同样nonce的交易,只是去改变提交的金额,同理我们也可以提交进去,但两个节点都维护了同样noncegas price相同,交易金额不同的交易,它们的交易hash肯定也是不一样的,最终只会只有一笔会被打包,也就是哪个节点在挖矿,优先选择自己pending中的交易。
(测试方法:节点A和集群B的情况下,停掉节点A的情况下,往节点B发送一笔交易得到一个新的交易hash,
然后迅速重启节点A(保证还没来得及恢复集群的情况下),往节点A发送同样nonce的交易,但交易价格提高,同样提交进去并且也得到一个新的交易hash)

节点A
这里写图片描述
这里写图片描述
节点B
这里写图片描述


  • 5、我们为了测试提交到queued的交易,继续发送nonce为2(跳过1)的交易到节点A中,会被提交进去,并且可以拿到交易hash,只是交易会被放入queued中,并且不会被广播到节点B
{
    "id":0,
    "jsonrpc":"2.0",
    "result":"0x21143db5226c60dec4f705d7eb9ec63bfe5abd630aebc875e2d144f02269f8be",
    "transactionHash":"0x21143db5226c60dec4f705d7eb9ec63bfe5abd630aebc875e2d144f02269f8be"
}

节点A
这里写图片描述

节点B
这里写图片描述

  • 6、再次发送nonce为2(跳过1)的交易到节点A中,提交不进去该笔交易,响应如下

{
    "error":{
        "code":-32000,
        "message":"known transaction: 21143db5226c60dec4f705d7eb9ec63bfe5abd630aebc875e2d144f02269f8be"
    },
    "id":0,
    "jsonrpc":"2.0"
}
  • 7、因为节点B的queued并不会去同步节点A之前提交的nonce为2的交易,我们再次把同样的交易提交给节点B
{
    "id":0,
    "jsonrpc":"2.0",
    "result":"0x21143db5226c60dec4f705d7eb9ec63bfe5abd630aebc875e2d144f02269f8be",
    "transactionHash":"0x21143db5226c60dec4f705d7eb9ec63bfe5abd630aebc875e2d144f02269f8be"
}

我们可以看到该笔交易可以被提交进去,但交易hash和节点A中相应的是一模一样的,也就是说我们依然认为是同一笔交易。

  • 8、依然重复操作5,我们给节点A提交一笔nonce为3的交易进入节点A的queued中,随后再次提交一笔nonce为3的交易,并且把gas price提高10%,依然可以被提交进去,但他们的hash肯定不一样,如下

节点A

{
    "id":0,
    "jsonrpc":"2.0",
    "result":"0x5d3ebd32d70ce4dfde0feeb901b7f94086da278233b3e9e2d1036b29cb371899",
    "transactionHash":"0x5d3ebd32d70ce4dfde0feeb901b7f94086da278233b3e9e2d1036b29cb371899"
}

这里写图片描述

节点B

{
    "id":0,
    "jsonrpc":"2.0",
    "result":"0x0ed9941584914d47cbea39b58fe5a89c9dd942c2ba66453904c14d867245e80c",
    "transactionHash":"0x0ed9941584914d47cbea39b58fe5a89c9dd942c2ba66453904c14d867245e80c"
}

这里写图片描述

在我们补齐nonce为1的交易后,我们观察到节点A和节点B的queued虽然都维护着nonce为3的交易(hash不同),
但最终进入pending中会是gas price价格高的交易,至此两个节点又保持一致。
节点、A节点B都如下

这里写图片描述

结论是gas price价格高的会被加入到pending

  • 9、 如果我们继续往节点A中提交nonce为5的交易进入到queued中,保持gas price不变,但提高交转账金额,
    再次提交nonce为5的交易到节点B中,两个节点中又分别维护了两笔nonce相同,但交易hash不同的交易,
    但此时因为gas price一样,在补齐nonce为4的交易后,两个节点中nonce为5的交易都会进入到各自的pending中,
    但是最终只会只有一笔会被打包,也就是哪个节点在挖矿,优先选择自己pending中的交易。

  • 10、 关于nonce的其它可能会碰到的问题

 某用户的区块链维护的nonce已经到10了,但提交一笔交易nonce为10以下(包括10)的交易

{
    "error":{
        "code":-32000,
        "message":"nonce too low"
    },
    "id":0,
    "jsonrpc":"2.0"
}

 某用户的区块链提交的交易nonce已经到10了,但依然提交一笔交易nonce为10的交易,
如果不改变之前交易的任何信息继续提交(两笔交易的hash是一样的),响应如下

{
    "error":{
        "code":-32000,
        "message":"known transaction: 98d1ca30393aa92afc5234a743f758c1aa07eed449c2eb7220039fa28e77949b"
    },
    "id":2,
    "jsonrpc":"2.0"
}

如果改变交易金额提交(交易hash不一样),响应如下

{
    "error":{
        "code":-32000,
        "message":"replacement transaction underpriced"
    },
    "id":0,
    "jsonrpc":"2.0"
}

猜你喜欢

转载自blog.csdn.net/dreamsunday/article/details/79850918