以太坊交易Nonce设置

1 什么是nonce?

以太坊中的nonce有两个意义:

1.工作量证明:为了证明工作量的无意义的值,这是采矿的本质,这个值将决定采矿的难度,

2.账户的随机数:在一个账户中的防止多重交易的用途。例如一个交易从A到B 20个币,可能从A到B发送多次。

为了防止交易的重播攻击,每笔交易必须有一个nonce随机数,针对每一个账户nonce都是从0开始,当nonce为0的交易处理完之后,才会处理nonce为1的交易,并依次加1的交易才会被处理。以下是nonce使用的几条规则:

  • 当nonce太小,交易会被直接拒绝。
  • 当nonce太大,交易会一直处于队列之中,这也就是导致我们上面描述的问题的原因;
  • 当发送一个比较大的nonce值,然后补齐开始nonce到那个值之间的nonce,那么交易依旧可以被执行。
  • 当交易处于queue中时停止geth客户端,那么交易queue中的交易会被清除掉。
2 以太坊源码中关于Nonce的解读

以太坊在geth的console端使用eth.sendTransaction({from:addr1,to:addr2,value:money})的形式来转账ether的时候,会通过RPC调用到/ethapi/api.go中的函数SendTransaction():

// SendTransaction will create a transaction from the given arguments and
// tries to sign it with the key associated with args.To. If the given passwd isn't
// able to decrypt the key it fails.
func (s *PrivateAccountAPI) SendTransaction(ctx context.Context, args SendTxArgs, passwd string) (common.Hash, error) {
	if args.Nonce == nil {
		// Hold the addresse's mutex around signing to prevent concurrent assignment of
		// the same nonce to multiple accounts.
		s.nonceLock.LockAddr(args.From)
		defer s.nonceLock.UnlockAddr(args.From)
	}
	signed, err := s.signTransaction(ctx, args, passwd)
	if err != nil {
		return common.Hash{}, err
	}
	return submitTransaction(ctx, s.b, signed)
}

sendTransaction()继续调用submitTransaction()函数来执行交易:

// submitTransaction is a helper function that submits tx to txPool and logs a message.
func submitTransaction(ctx context.Context, b Backend, tx *types.Transaction) (common.Hash, error) {
	if err := b.SendTx(ctx, tx); err != nil {
		return common.Hash{}, err
	}
	if tx.To() == nil {
		signer := types.MakeSigner(b.ChainConfig(), b.CurrentBlock().Number())
		from, err := types.Sender(signer, tx)
		if err != nil {
			return common.Hash{}, err
		}
		addr := crypto.CreateAddress(from, tx.Nonce())
		log.Info("Submitted contract creation", "fullhash", tx.Hash().Hex(), "contract", addr.Hex())
	} else {
		log.Info("Submitted transaction", "fullhash", tx.Hash().Hex(), "recipient", tx.To())
	}
	return tx.Hash(), nil
}

submitTransaction()在sendTx()函数中发送交易。然后在判断交易地址是否为空,为空的则是创建合约的交易。

追踪sendTx()函数到/core/tx_pool.go中的addTx()函数,原来是通过addTx()将交易添加到交易池。

func (pool *TxPool) addTx(tx *types.Transaction, local bool) error {
	pool.mu.Lock()
	defer pool.mu.Unlock()

	// Try to inject the transaction and update any state
	replace, err := pool.add(tx, local)
	if err != nil {
		return err
	}
	// If we added a new transaction, run promotion checks and return
	if !replace {
		from, _ := types.Sender(pool.signer, tx) // already validated
		pool.promoteExecutables([]common.Address{from})
	}
	return nil
}

addTx()首先调用add()将交易添加到交易池pool,如果成功,则调用promoteExecutables()函数去执行交易。promoteExecutables()函数关键代码是:

// Gather all executable transactions and promote them
for _, tx := range list.Ready(pool.pendingState.GetNonce(addr)) {
    hash := tx.Hash()
    log.Trace("Promoting queued transaction", "hash", hash)
    pool.promoteTx(addr, hash, tx)
    log.Info("lzj log:","hash",hash,"Nonce",pool.pendingState.GetNonce(addr))
}

这段代码收集所有可执行交易并且执行它们。pool.pendingState.GetNonce(addr)得到和地址addr相关的nonce范围N,N等于len(addr.nonces)) + addr.nstart,其实等于该地址已经发送成功的交易个数。list.Ready()函数将返回一个交易集合,这个集合中的所有交易都要求满足nonce不大于N。只有Nonce满足要求的交易才会被处理,就是通过pool.promoteTx(addr, hash, tx)执行交易。

3 Web3j等RPC方式设置Nonce的正确方式

一般的通过Web3j发起以太坊交易的方法是:

Transaction.createEtherTransaction(from,nonce,gasPrice,gasLimit,to,value);

其中的nonce得到方式是:

EthGetTransactionCount ethGetTransactionCount = ethClient.ethGetTransactionCount(
                from, DefaultBlockParameterName.LATEST).sendAsync().get();
BigInteger nonce = ethGetTransactionCount.getTransactionCount();

通过取得当前该账户发起过的交易次数,并设其为当前交易的Nonce值。

如果要在for循环里同时发起多笔交易,因为当前交易尚没有被处理成功,所以通过上述方法得到的Nonce将会与上一次交易的Nonce相同,如果这笔交易的Gas比上次交易大,则会替换上次交易,否则将会报如下错误:

transaction failed,info:replacement transaction underpriced
解决这个问题的方法是自己维护Nonce,同一个账户连续发送俩笔交易时,通过递增Nonce来防止Nonce重复。

此种方案也有限制条件。第一,由于nonce统一进行维护,那么这个地址必须是内部地址,而且发起交易必须通过统一维护的nonce作为出口,否则在其他地方发起交易,原有维护的nonce将会出现混乱。第二,一旦已经发出的交易发生异常,异常交易的nonce未被使用,那么异常交易的nonce需要重新被使用之后它后面的nonce才会生效。








  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
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
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值