0
点赞
收藏
分享

微信扫一扫

ETH交易以中nonce的管理流程

小a草 2022-05-05 阅读 63

ETH (武汉链)交易的随机数(nonce)

最近在使用在使用BSN开放联盟链 · 武汉链 做交易的过程用遇到了一些问题,自己记录一下。

  • 首先说下以太坊(Ethereum)和武汉链有什么关系,武汉链是BSN开放联盟链其中一条基于(Ethereum)的共用链,肯定还有人问BSN是谁,下面是我从百度百科上摘抄的一段。

        BSN是由[国家信息中心]牵头,会同中国移动、中国银联等单位联合发起并建立。BSN是基于区块链技术和共识机制的全球性基础设施网络,是面向工业、企业、政府应用的可信、可控、可扩展的联盟链,致力于改变联盟链应用的局域网架构高成本问题,以互联网理念为开发者提供公共区块链资源环境,极大降低区块链应用的开发、部署、运维、互通和监管成本,从而使区块链技术得到快速普及和发展。
    
  • 我自己为什么不直接使用ETH公链,而使用中国自己的武汉链,有两点原因:

    • 公链在国内不合法,数据无法被监管

    • ETH公链的价格太高,我这小户实在承受不起 哈哈img

      image-20220504202805148
  • 下面文档的数据都是我基于武汉链的测试结果。

什么是交易(Transactions)

  1. 先说下什么是交易

    ​ 以太坊交易是指由外部持有账户(EOA) 发起的行动,换句话说,是指由人管理而不是智能合约管理的账户。 例如,如果 Bob 发送 Alice 1 ETH,则 Bob 的帐户必须减少 1 ETH,而 Alice 的账户必须增加 1 ETH,看待交易的另一种方式是,它是唯一能触发状态改或是或促使合约在EVM 中执行的东西。以太坊是一个全域单例(global singleton) 的状态机(state machine),交易是唯一能够转动这状态机去改变状态的方法。合约无法自行运行。以太坊不会在后台运行。所有的状态改变与执行皆始于交易。

    image-20220504205557373

  2. 交易对象看起就像:

    {
        "from":"55935178000b12f5746230e17b352b8716277777",
        "to":"91f715616d0366f5670134c6e244190072798888",
        "gas":"0xC350",
        "gasLimit":"0xC950",
        "gasPrice":"0xc1b720800",
        "value":"0x1",
        "nonce":"0x2"
    }
    
  3. 交易的随机数(nonce)

    • nonce – 显示从帐户发送的交易数量的计数器。 这将确保交易只处理一次。 在合约帐户中,这个数字代表该帐户创建的合约数量

交易的随机数(nonce)

先了解下nonce在节点上是怎么管理的。

  • 所有的交易不管是本地交易AddLocals还是其他节点广播AddRemotes过来的交易终都会调用方法addTxsaddTxsLockedadd,经过一系列校验validateTx进入队列 queue【此时节点上账户address 的 nonce还未更新】

  • 源码中的调用如下:

    // This method is used to add transactions from the RPC API and performs synchronous pool
    // reorganization and event propagation.
    func (pool *TxPool) AddLocals(txs []*types.Transaction) []error {
    	return pool.addTxs(txs, !pool.config.NoLocals, true)
    }
    
    // This method is used to add transactions from the p2p network and does not wait for pool
    // reorganization and internal event propagation.
    func (pool *TxPool) AddRemotes(txs []*types.Transaction) []error {
    	return pool.addTxs(txs, false, false)
    }
    
    // addTxs attempts to queue a batch of transactions if they are valid.
    func (pool *TxPool) addTxs(txs []*types.Transaction, local, sync bool) []error {
      ...
      newErrs, dirtyAddrs := pool.addTxsLocked(news, local)
      ...
      // Reorg the pool internals if needed and return
      done := pool.requestPromoteExecutables(dirtyAddrs)
    }
    
    // addTxsLocked attempts to queue a batch of transactions if they are valid.
    // The transaction pool lock must be held.
    func (pool *TxPool) addTxsLocked(txs []*types.Transaction, local bool) ([]error, *accountSet) {
        ...
    	replaced, err := pool.add(tx, local)
    }
    
    // add validates a transaction and inserts it into the non-executable queue for later
    // pending promotion and execution. If the transaction is a replacement for an already
    // pending or queued one, it overwrites the previous transaction if its price is higher.
    func (pool *TxPool) add(tx *types.Transaction, local bool) (replaced bool, err error) {...}
    
  • 交易进入队列后此时就轮到了 scheduleReorgLoop 上场。

    1. 上面addTxs()中的pool.requestPromoteExecutables(dirtyAddrs),发送请求对给定地址进行交易升级检查至通道 reqPromoteCh

    2. 通道pool.reqPromoteCh 该通道接收到消息后 scheduleReorgLoop 开始后台运行 pool.runReorg

      // runReorg runs reset and promoteExecutables on behalf of scheduleReorgLoop.
      
    3. pool.runReorg处理过程

    4. 已成为可处理的从未来队列到待处理事务集的事件。 在此过程中,所有无效交易(低随机数、低余额)都将被删除。pool.promoteExecutables(promoteAddrs)

    5. 交易提成为pending状态,并更新addressnonce

      promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction)

      // Set the potentially new pending nonce and notify any subsystems of the new tx
      pool.pendingNonces.set(addr, tx.Nonce()+1)
      
    6. 至此交易状态提升为pending状态时addressnonce才更新(+1)

    7. 源码中的调用如下:

    // scheduleReorgLoop schedules runs of reset and promoteExecutables. Code above should not
    // call those methods directly, but request them being run using requestReset and
    // requestPromoteExecutables instead.
    func (pool *TxPool) scheduleReorgLoop() {
    	...
        // Run the background reorg and announcements
     	go pool.runReorg(nextDone, reset, dirtyAccounts, queuedEvents)
    }
    
    // runReorg runs reset and promoteExecutables on behalf of scheduleReorgLoop.
    func (pool *TxPool) runReorg(...) {
        // Check for pending transactions for every account that sent new ones
    	promoted := pool.promoteExecutables(promoteAddrs)
    }
    
    // promoteExecutables moves transactions that have become processable from the
    // future queue to the set of pending transactions. During this process, all
    // invalidated transactions (low nonce, low balance) are deleted.
    func (pool *TxPool) promoteExecutables(accounts []common.Address) []*types.Transaction {
        ...
        if pool.promoteTx(addr, hash, tx) {
    		promoted = append(promoted, tx)
    	}
    }
    
    // promoteTx adds a transaction to the pending (processable) list of transactions
    // and returns whether it was inserted or an older was better.
    //
    // Note, this method assumes the pool lock is held!
    func (pool *TxPool) promoteTx(addr common.Address, hash common.Hash, tx *types.Transaction) bool {
        // Set the potentially new pending nonce and notify any subsystems of the new tx
    	pool.pendingNonces.set(addr, tx.Nonce()+1)
    }
    

武汉链的使用过程

上面了解了nonce的管理流程下面看下我在调用武汉链时遇到的问题。

我通过BSN网关调用武汉链做交易,由于我的交易量比较大,总是会有失败的交易,我就把经常遇到失败的的原因以及解决办法列举下。

  • nonce too low

    // ErrNonceTooLow is returned if the nonce of a transaction is lower than the
    // one present in the local chain.
    

    使用已经被旷工打包过的nonce,也就是该nonce已经被使用过了。

    通过调用RPC接口查询最新的nonce

    {
        "jsonrpc": "2.0",
        "method": "eth_getTransactionCount",
        "params": [
            "91f715616d0366f5670134c6e244190072798888",
            "pending" // 整数块编号,或字符串"latest"、"earliest"或"pending"
        ],
        "id": 1
    }
    
  • already known

    // ErrAlreadyKnown is returned if the transactions is already contained
    // within the pool.
    

    节点交易池中已经有相同nonce的交易,因为节点上交易处理慢导致nonce更新不及时,通过RPC接口获取到的nonce值是相同的。

    如果瞬时交易量大就需要考虑本地维护nonce值,本地nonce管理可以参考web3j的TransactionManager。

  • replacement transaction underpriced

    // ErrReplaceUnderpriced is returned if a transaction is attempted to be replaced
    // with a different one without the required price bump.
    

    该账户在节点上有非可执行的交易也是就有交易在对列中queued,然后使用相同的nonce和相同的gasPrice发送交易、

    1. 交易使用的低于节点限制的gasPrice,该笔交易不会进入pending,只能在queued中等待。
    2. 交易使用了过高的nonce,该笔交易不会进入pending,只能在queued中等待。

    首先需要查清楚是因为gasPrice还是nonce导致。

    1. 如果是gasPrice低于节点的最低限制(武汉链的gasPrice最低1Gwei),通过RPC接口(eth_gasPrice)获取最新的gasPrice,提交gasPricegasPrice需要高于原交易的110%)后使用该笔交易的nonce重新发送交易替换队列中的那笔交易。

    2. 如果是使用了过高的nonce,使用RPC接口(eth.getTransactionCount(address, "latest"))获取最新的【不是pending状态下的】nonce,发送交易补全至pendingnonce后该笔交易会被自动执行。

    3. 覆盖交易原交易hash就会作废。

  • intrinsic gas too low

    // ErrIntrinsicGas is returned if the transaction is specified to use less gas
    // than required to start the invocation.
    

    该笔交易,gasLimit设置 低于该笔交易的使用量

    可以通过RPC接口(eth_estimateGas)估算gasLimit,提高gasLimit后重新发送交易。

  • tx fee (*** ether) exceeds the configured cap (* ether)

    // checkTxFee is an internal function used to check whether the fee of
    // the given transaction is _reasonable_(under the cap).
    

    gasLimit 超过配置上限 (1.00 ether)

    降低gasLimit即可

  • insufficient funds for gas * price + value

    // ErrInsufficientFunds is returned if the total cost of executing a transaction
    // is higher than the balance of the user's account.
    

    该账户能量值不足了,账户的gas余额不足

    在BSN门户中购买即可

    降低gasLimit即可

如有错误欢迎指正交流😊

举报

相关推荐

0 条评论