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 statereplace, err := pool.add(tx, local)if err != nil {return err}// If we added a new transaction, run promotion checks and returnif !replace {from, _ := types.Sender(pool.signer, tx) // already validatedpool.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才会生效。

以太坊交易Nonce设置相关推荐

  1. 以太坊交易中的nonce和confirmation

    1. 以太坊交易中的nonce及其价值 1.1 以太坊交易中的nonce值 在以太坊中,每笔交易都有一个nonce值,该nonce值代表的是从该交易发起地址发出的交易数.(当存在使用同一地址从多个客户 ...

  2. 一文讲清楚以太坊的nonce

    续上篇:一文讲清楚以太坊的gas,gasPrice,gasLimit 我们今天来讲讲以太坊的nonce.做过钱包开发的同学都应该知道,nonce是一个非常关键的参数.对于一般用户来说,各种钱包已经帮我 ...

  3. 以太坊交易信息及event、input、logs、topics等概念机制

    文章目录 一.交易信息获取 1.1 合约事件例子定义 1.2 以太坊交易获取 二.input解析 2.1 input内容解析 2.2 input处理逻辑 三.logs解析 3.1 logs解析代码 四 ...

  4. 观点:以太坊交易费市场泡沫预警,交易费日渐成为用户难以承受之重

    在经历了一次大熊市之后,以太坊已经再次流行起来.受包括 ETH 价格反弹以及所谓"去中心化金融"(DeFi)的采用等因素的综合影响,以太坊区块链的活跃用户和日交易量都出现了强力增长 ...

  5. 区块链 以太坊 交易结构、执行、存储 解析 交易中为什么没有包含发送者地址这条数据

    一. 交易的结构 1. Transaction结构 交易结构定义在 core/types/transaction.go 中: type Transaction struct {//交易数据data t ...

  6. java如何监听以太坊交易

    2019独角兽企业重金招聘Python工程师标准>>> 你可以在web3j库的帮助下使用java轻松监听以太坊交易,但此库无法监听Erc20 Token交易. 要监听Erc20Tok ...

  7. Infura Http 客户端 以太坊 交易

    web3j Infura 模块提供了一个Infura Http 客户端(InfuraHttpService),它为Infura特定的Infura-Ethereum-Preferred-Client提供 ...

  8. 以太坊地址和公钥_以太坊交易签名解析源码解读

    上篇文章<以太坊交易签名过程源码解析[1]>从源码角度分析了一个合约调用的的签名过程,签名后的交易发送到以太坊节点后,节点需要从签名交易中还原出公钥(从公钥中单向计算出账号地址),进而将交 ...

  9. Web3j通过合约地址监听transfer事件获取以太坊交易数据

    Web3j通过合约地址监听transfer事件获取以太坊交易数据 We3j web3j是一个轻量级的Java库,用于在Ethereum网络上集成客户端(节点). 核心特性 通过Java类型的JSON- ...

最新文章

  1. 说说设计模式~组合模式(Composite)
  2. hostent结构体图解
  3. 集成电路883和883b有什么区别
  4. 阿里对大年龄清退的定义
  5. LVM逻辑卷详解及创建
  6. Orace用户创建及权限分配
  7. Tableview最后一行无法显示或者显示不全的问题IOS
  8. django中使用第三方包实现定时任务
  9. JavaScript学习笔记(四)
  10. Slideshow Maker for mac(幻灯片制作软件)
  11. Activity传递数据
  12. node 更新_更新应用时,如何实现 K8s 零中断滚动更新?
  13. 编译原理(紫龙书)第2版习题答案
  14. 《现代汉语》北大公开课
  15. python打印列表的下标和值的例子:
  16. mysql中用于删除数据的是什么意思_数据库删除语句delete有什么用?
  17. 外汇mt4 软件在哪里下载比较正规?
  18. kali2021安装百度网盘
  19. 2022-2028全球快餐肉汤食品行业调研及趋势分析报告
  20. UE4 Windows环境下游戏打包基础教程(ios, windows, android)(UFE方式以及命令行方式)

热门文章

  1. 学习笔记——Linux命令
  2. linux-自动备份MySQL数据库,并邮件发送
  3. 线性LED驱动器行业调研报告 - 市场现状分析与发展前景预测
  4. flash编程可以用c语言,C语言写的小游戏和FLASH小游戏有何区别呢-c语言编程手机软件...
  5. UCOSii源文件概要
  6. Cassandra代替Redis?
  7. ExtJs 备忘录(9)—— Ext常用属性、方法小结 [系列完]
  8. 部署jar至k8s服务器
  9. Upload-Labs-Pass-07
  10. 计算机应用什么叫逆向测试,你知道什么是逆向设计吗?