环境
- 节点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
中由两部分构成pending
和queued
组成,一个为待打包状态,一个为队列中。如果传入的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的txpool
中pending
看到,因为广播足够快,节点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的交易,只是去改变提交的金额,同理我们也可以提交进去,但两个节点都维护了同样
nonce
,gas 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"
}