以太坊中nonce深入解读

目录

前言

用户事务nonce

从一个集群的AB节点试验说起。

总结

区块nonce

参考


前言

以太坊中的主要有2类nonce,一类是和矿工比较密切的区块nonce,即挖矿时使用;另一类和普通使用提交提交的关系比较密切的用户事务nonce。

用户事务nonce

  1. 为了防⽌交易重播,ETH节点要求每笔交易必须有⼀个nonce数值。每⼀个账户从同⼀个节点发起交易时,这个nonce值从0开始计数,发送⼀笔nonce对应加1。当前⾯的nonce处理完成之后才会处理后⾯的nonce。集群环境下,不同节点共同维护同⼀个⽤户的nonce值。
  2. txpool中由两部分构成pending和queued组成,⼀个为待打包状态,⼀个为队列中。如果传⼊的nonce就是某⽤户下笔交易应该传⼊的nonce,那么该笔交易就会放置在pending中,等待节点打包。其次,如果传⼊的nonce值过⼤,在进⼊txpool中检查到它之前的nonce并没有使⽤过,那么此笔交易不会发送到pending中,⽽且放置在queued中。只有当前⾯的nonce补齐之后,才会进⼊到pending中。

从一个集群的AB节点试验说起。

环境
节点A:192.168.45.9
节点B:192.168.45.10
节点A和节点B集群(节点B连接到节点A)

  • 集群环境下,写⼊节点A的pending交易会⼴播到节点B中,因nonce问题写⼊节点A的queued交易,不会被⼴播。
  • 集群环境下,节点B连上节点A(admin.addPeer(节点A)),节点A停掉的情况下再次启动,会很快恢复集群,但是如果是节点B停掉的情况下,却不会再次恢复集群,只能重新连接节点,才能恢复集群B。

1、发送⼀笔nonce为0的交易给节点A,会返回交易hash,并能在节点A的txpool中pending看到,因为⼴播⾜够快,节点B中也能看到。

2、再次发送nonce为0的交易(交易⾦额和gas price等全部保持不变)给节点A,响应code=-32000,表明并没有提交成功。

3、继续测试,再次发送nonce为0的交易给节点A,并且把gas price价格提⾼(必须超过10%),
在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。

5、我们为了测试提交到queued的交易,继续发送nonce为2(跳过1)的交易到节点A中,会被提交进去,并且可以拿到交易hash,只是交易会被放⼊queued中,并且不会被⼴播到节点B。

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

7、因为节点B的queued并不会去同步节点A之前提交的nonce为2的交易,我们再次把同样的交易提交给节点B,我们可以看到该笔交易可以被提交进去,但交易hash和节点A中相应的是⼀模⼀样的,也就是说我们依然认为是同⼀笔交易。


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

9、在我们补齐nonce为1的交易后,我们观察到节点A和节点B的queued虽然都维护着nonce为3的交易(hash不同), 但最终进⼊pending中会是gas price价格⾼的交易,⾄此两个节点⼜保持⼀致。结论是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)的交易,此时会提示nonce too low,code=-32000。

某⽤户的区块链提交的交易nonce已经到10了,但依然提交⼀笔交易nonce为10的交易, 如果不改变之前交易的任何信息继续提交(两笔交易的hash是⼀样的),会提示错误,code=-32000。如果改变交易⾦额提交(交易hash不⼀样),也会提示错误,code=-32000。

总结

当nonce太小(小于之前已有交易的nonce值),交易会被直接拒绝。
当nonce太大,交易会一直处于队列之中,长久得不到执行。
当发送一个比较大的nonce值,然后补齐开始的nonce到那个nonce之间的nonce,那么交易依旧可以被执行。
当交易处于队列中时,停止geth客户端,那么交易队列中的交易会被清除。
当有一笔处于pending状态的交易,新的一笔交易与其拥有相同的nonce值,如果新交易的gas price太小,无法覆盖pending状态的交易,如果新交易的gas price高于原交易的110%,则原交易会被覆盖掉。
交易队列只保存最多64个从同一个账户发出的交易,The transaction pool queue will only hold a maximum of 64 transactions with the same From:address with nonces out of sequence. 也就是说,如果要批量转账,同一节点不要发出超过64笔交易。
当前nonce合适,但是账户余额不足时,会被以太坊拒绝;
如果发起一笔交易,但是因为gwei比较低或者网络比较忙的时候,该交易还没矿工挖出,可以通过使用相同的nonce和较高的gas费用,从而“覆盖”前一笔交易。

区块nonce

区块上的nonce是一个无意义的随机数,用于工作量证明,与挖矿的难度有关。
矿工要想成功挖出一个区块,必须不停的穷举随机数nonce,直到通过哈希算法得到的区块hash值小于或等于目标值target,目标值越低,发现随机数需要的时间越多,难度值越高。

具体的数据结构:

// Block represents an entire block in the Ethereum blockchain.
type Block struct {
	header       *Header
	uncles       []*Header
	transactions Transactions

	// caches
	hash atomic.Value
	size atomic.Value

	// Td is used by package core to store the total difficulty
	// of the chain up to and including the block.
	td *big.Int

	// These fields are used by package eth to track
	// inter-peer block relay.
	ReceivedAt   time.Time
	ReceivedFrom interface{}
}

// Header represents a block header in the Ethereum blockchain.
type Header struct {
	ParentHash  common.Hash    `json:"parentHash"       gencodec:"required"`
	UncleHash   common.Hash    `json:"sha3Uncles"       gencodec:"required"`
	Coinbase    common.Address `json:"miner"            gencodec:"required"`
	Root        common.Hash    `json:"stateRoot"        gencodec:"required"`
	TxHash      common.Hash    `json:"transactionsRoot" gencodec:"required"`
	ReceiptHash common.Hash    `json:"receiptsRoot"     gencodec:"required"`
	Bloom       Bloom          `json:"logsBloom"        gencodec:"required"`
	Difficulty  *big.Int       `json:"difficulty"       gencodec:"required"`
	Number      *big.Int       `json:"number"           gencodec:"required"`
	GasLimit    uint64         `json:"gasLimit"         gencodec:"required"`
	GasUsed     uint64         `json:"gasUsed"          gencodec:"required"`
	Time        *big.Int       `json:"timestamp"        gencodec:"required"`
	Extra       []byte         `json:"extraData"        gencodec:"required"`
	MixDigest   common.Hash    `json:"mixHash"          gencodec:"required"`
	Nonce       BlockNonce     `json:"nonce"            gencodec:"required"`
}

参考

https://wenku.baidu.com/view/c5440a5702f69e3143323968011ca300a6c3f63b.html

以太坊中的两个nonce值_Elonjelinek的博客-CSDN博客

Python以太坊交易是指使用Python编程语言进行以太坊区块链上的交易操作。以太坊是一种基于区块链技术的智能合约平台,它允许开发者构建和部署去中心化应用程序(DApps)。 在Python中,可以使用以太坊的官方库web3.py来进行以太坊交易的编程操作。web3.py提供了一系列的API,可以与以太坊节点进行通信,并执行各种操作,包括创建账户、发送交易、查询余额等。 以下是一个简单的Python代码示例,展示了如何使用web3.py库发送以太坊交易: ```python from web3 import Web3 # 连接到以太坊节点 w3 = Web3(Web3.HTTPProvider('https://mainnet.infura.io/v3/your_infura_project_id')) # 设置发送方账户私钥和接收方地址 private_key = 'your_private_key' receiver_address = '0x1234567890abcdef1234567890abcdef12345678' # 构建交易参数 transaction = { 'to': receiver_address, 'value': w3.toWei(1, 'ether'), 'gas': 21000, 'gasPrice': w3.toWei('50', 'gwei'), 'nonce': w3.eth.getTransactionCount(w3.eth.accounts), } # 签名交易 signed_transaction = w3.eth.account.signTransaction(transaction, private_key) # 发送交易 transaction_hash = w3.eth.sendRawTransaction(signed_transaction.rawTransaction) # 等待交易确认 transaction_receipt = w3.eth.waitForTransactionReceipt(transaction_hash) # 打印交易结果 print('交易成功,交易哈希:', transaction_receipt.transactionHash.hex()) ``` 上述代码中,首先使用`Web3`类连接到以太坊节点。然后设置发送方账户的私钥和接收方地址。接下来,构建交易参数,包括接收方地址、转账金额、燃气限制、燃气价格和交易序号。然后使用发送方账户的私钥对交易进行签名,并发送签名后的交易到以太坊网络。最后,等待交易被确认,并打印交易结果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FeelTouch Labs

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值