0%

eth | nonce 含义

这个是转账的基本参数。


参考资料


以太坊系列(ETH&ETC)在发送交易有三个对应的RPC接口,分别是

  • ethsendTransaction
  • ethsendRawTransaction
  • personal_sendTransaction

这三个接口发送(或构造发送内容时)都需要一个参数nonce

官方文档对此参数的解释是:整数类型,允许使用相同随机数覆盖自己发送的处于pending状态的交易。


规则


为了防止交易重播,ETH(ETC)节点要求每笔交易必须有一个nonce数值。每一个账户从同一个节点发起交易时,这个nonce值从0开始计数,发送一笔nonce对应加1。当前面的nonce处理完成之后才会处理后面的nonce。注意这里的前提条件是相同的地址在相同的节点发送交易。

以下是nonce使用的几条规则:

  • nonce太小(小于之前已经有交易使用的nonce值),交易会被直接拒绝。
  • nonce太大,交易会一直处于队列之中。
  • 当发送一个比较大的nonce值,然后补齐开始nonce到那个值之间的nonce,那么交易依旧可以被执行。
  • 当交易处于queue中时停止geth客户端,那么交易queue中的交易会被清除掉。

获取nonce值


在实际应该用中我们如何保障 nonce 值的可靠性呢?这里有两个思路。

第一个思路就是由业务系统维护nonce值的递增。如果交易发送就出现问题,那么该地址下一笔交易继续使用这个nonce进行发送交易。

第二个思路就是使用现有的api查询当前地址已经发送交易的nonce值,然后对其加1,再发送交易。对应的API接口为:eth_getTransactionCount,此方法由两个参数,第一个参数为需要查询nonce的地址,第二个参数为block的状态:latestearliestpending。一般情况使用pending就可以查询获得最新已使用的nonce

我写的代码,一般来说直接用

eth_getTransactionCount

返回的值作为 nonce 就好了,但是,有的时候交易在 pending ,导致这个交易没有发送成功,然后,继续调用的话,会导致 nonce 的数值一样,出现失败。我的解决方案是这样的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
def send_hoo():
fromAddress = Web3.toChecksumAddress(address)
addresseres = init_address()
for _add in addresseres:
toAddress = Web3.toChecksumAddress(_add)
nonce = w3.eth.getTransactionCount(fromAddress)
balance = w3.eth.get_balance(toAddress)
if w3.fromWei(balance, "ether") >= user_hoo:
continue
gasPrice = w3.eth.gasPrice
value = Web3.toWei(user_hoo, 'ether')
gas = w3.eth.estimateGas({'from': fromAddress, 'to': toAddress, 'value': value})
transaction = {'from': fromAddress,
'to': toAddress,
'nonce': nonce,
'gasPrice': gasPrice,
'gas': gas,
'value': value,
'data': ''}

signed_tx = w3.eth.account.signTransaction(transaction, private_key)
txn_hash = w3.eth.sendRawTransaction(signed_tx.rawTransaction)
print(Web3.toHex(txn_hash))
time.sleep(sleep_time)
while w3.eth.getTransactionCount(fromAddress) == nonce:
time.sleep(2)

nonce使用陷阱


热点账户:所谓的热点账户就是频繁被使用的账户,在以太坊中比如交易所的统一出币账户,在短时间内频繁发起交易的账户,均可被称作热点账户。

replacement transaction underpriced异常

如果系统中的热点账户或普通账户发起交易时出现 error: replacement transaction underpriced 异常,那么就需要考虑 nonce 使用是否正确。

引起此异常原因主要是当一个账户发起一笔交易,假设使用nonce1,交易已经发送至节点中,但由于手续费不高或网络拥堵或nonce值过高,此交易处于queued中迟迟未被打包。

同时此地址再发起一笔交易,如果通过eth_getTransactionCount获取的nonce值与上一个nonce值相同,用同样的nonce值再发出交易时,如果手续费高于原来的交易,那么第一笔交易将会被覆盖,如果手续费低于原来的交易就会发生上面的异常。

通常发生此异常意味着:

  • nonce 已经用过
  • 你的Ethereum客户端中已经有一币处于pending状态的交易。
  • 新的一笔交易拥有pending状态交易相同的nonce值。
  • 新的交易的gas price太小,无法覆盖pending状态的交易。

通常情况下,覆盖掉一笔处于 pending 状态的交易 gas price 需要高于原交易的110%


案例分析


这次以币安智能链作为研究对象。

地址: 0xE8e069C47A45050DBA9D566CEfFA7bc7D453F0c5

此时最新的 nonce 是 20。

web3.js

测试函数 getTransactionCount ,下面分别是 2 种状态下 nonce 的取值。

  • latest
    • 0x15 「10进制下 nonce 等于 21」
  • pending
    • 0x15

web3.py

测试函数 getTransactionCount

  • latest
    • 21
  • pending
    • 21

然后我故意把 gas 调低一点,让其没办法成交。

ps: 调低一点失败了,因为网络不拥堵,那等一会在说吧。

请我喝杯咖啡吧~