美文网首页
以太坊C-源码解析(十)以太坊交易中的nonce

以太坊C-源码解析(十)以太坊交易中的nonce

作者: sky2016 | 来源:发表于2019-07-15 14:06 被阅读0次

以太坊交易中存在一个特殊的值nonce,此nonce并非计算block难度的nonce,此nonce仅仅表示发送账号发送交易的次数,从0开始,每发送一次交易+1,那么第一次发送nonce为0,第二次为1,以此类推。
nonce的存在可以用来防止重放攻击,也就是同一个交易只能被发送一次,下次发送同一个交易时,因为nonce值和最新的nonce不同,会被区块链拒绝。
我们来从代码层面看看这个nonce的生成和检测。

我们可以从一张图来看这个nonce的来龙去脉。

nonce
可以看到这张图上存在一个关键性的三角关系。
  • 三角形上面顶点是区块链blockchain
  • 左边顶点是postseal()中的noncepostseal()是从区块链获取到的最新的区块,那么左边顶点表示当前区块链中最新区块该发送账号的nonce
  • 右边顶点是交易队列中的nonce
    当我们提交一个交易时,交易的nonce取值是左右两个顶点的nonce值中取最大值。然后再与blockchain中最新块的nonce进行比较,如果不同,则区块链拒绝此交易。那么问题来了,postseal()中不就是最新块的nonce吗?为什么还需要再次比较?这是因为postseal()并不是一直与区块链同步的,只有满足某些条件才会同步。另外当交易成功提交后,该交易在正式被区块链确认前,是被存放在交易队列中的,此时右顶点的nonce值为该交易的nonce+1。

我们来一步一步拆开来看:
第一步,假设该发送者从来没有发送过交易,那么他的nonce值应该为0,上面的图会变成:

第一步
此时blockchainpostseal()中均没有该账号的信息,nonce值为初始值0。 交易队列中没有该发送账号的交易,因此nonce值也是0,那么该发送者第一次提交交易时,nonce值会被设置为max(0, 0),也就是0。然后再比较0与0是相等的,那么此交易被正确发送。

第二步,交易被发送到交易队列,这张图变成:

第二步
此时上顶点和左顶点的nonce值不变,右顶点因为交易队列中已有一个该发送者发送的交易,那么nonce+1,变成1。
此时如果该发送者想再发一个交易,那么新交易的nonce会被设置为max(0, 1),也就是1。然后再与上顶点nonce比较,得出不相等的结论,此交易被拒绝,提交失败!

第三步,第一个交易被区块链确认,这张图变成:

第三步
交易被确认后,上顶点blockchainnonce变成1,左顶点因为同步,nonce也变成1,而右顶点交易队列会删除掉已确认的交易,所以没有该发送者的交易了,nonce就为0。此时如果该发送者再发一个交易,新交易的nonce会被设置为max(1, 0),也就是1。然后再与上顶点nonce比较,结果相等,该交易被成功发出!

交易被发出后被发到交易队列,流程就同第二步了。

下面我们来看看具体涉及到的代码实现:

TransactionSkeleton Client::populateTransactionWithDefaults(TransactionSkeleton const& _t) const
{
    TransactionSkeleton ret(_t);

    // Default gas value meets the intrinsic gas requirements of both
    // send value and create contract transactions and is the same default
    // value used by geth and testrpc.
    const u256 defaultTransactionGas = 90000;
    if (ret.nonce == Invalid256)
        ret.nonce = max<u256>(postSeal().transactionsFrom(ret.from), m_tq.maxNonce(ret.from));
    if (ret.gasPrice == Invalid256)
        ret.gasPrice = gasBidPrice();
    if (ret.gas == Invalid256)
        ret.gas = defaultTransactionGas;

    return ret;
}

这段代码是提交交易时设置交易的一些参数,其中

ret.nonce = max<u256>(postSeal().transactionsFrom(ret.from), m_tq.maxNonce(ret.from));

就是设置交易的nonce,是左顶点和右顶点的nonce值的最大值。

再来看交易队列中的nonce

u256 TransactionQueue::maxNonce(Address const& _a) const
{
    ReadGuard l(m_lock);
    return maxNonce_WITH_LOCK(_a);
}

u256 TransactionQueue::maxNonce_WITH_LOCK(Address const& _a) const
{
    u256 ret = 0;
    auto cs = m_currentByAddressAndNonce.find(_a);
    if (cs != m_currentByAddressAndNonce.end() && !cs->second.empty())
        ret = cs->second.rbegin()->first + 1;
    auto fs = m_future.find(_a);
    if (fs != m_future.end() && !fs->second.empty())
        ret = std::max(ret, fs->second.rbegin()->first + 1);
    return ret;
}

注意那个+1,这个表示当前队列中该发送者最大的nonce再+1。

postseal()从区块链同步的代码在:

void Client::restartMining()
{
    bool preChanged = false;
    Block newPreMine(chainParams().accountStartNonce);
    DEV_READ_GUARDED(x_preSeal)
        newPreMine = m_preSeal;

    // TODO: use m_postSeal to avoid re-evaluating our own blocks.
    preChanged = newPreMine.sync(bc());

    if (preChanged || m_postSeal.author() != m_preSeal.author())
    {
        DEV_WRITE_GUARDED(x_preSeal)
            m_preSeal = newPreMine;
        DEV_WRITE_GUARDED(x_working)
            m_working = newPreMine;
        /// ...
        DEV_READ_GUARDED(x_working) DEV_WRITE_GUARDED(x_postSeal)
            m_postSeal = m_working;

        /// ...
    }

    /// ...
}

省略了一些次要代码,从这里可以看出,先是从区块链同步最新信息到newPreMine,然后拷贝到m_postSeal,也就是postseal()
那么Client::restartMining()是从哪里调用的呢?
有两个地方,但是一般都是从Client::resyncStateFromChain()这里调用的:

void Client::resyncStateFromChain()
{
    /// ...

    restartMining();
}

Client::resyncStateFromChain()函数又是从哪里调用呢?
也有两个地方,一处是Client::onChainChanged()表示区块链同步新块的时候,另一处是Client::syncTransactionQueue()表示同步交易队列中交易的时候。
由此可见,以太坊通过这些机制保证nonce与发送账号的发送次数严格对应,如果发送交易不遵守这条规则,则发送可能会失败,错误码为InvalidNonce

更多更新文章,欢迎访问我的博客 https://xingkong2014.github.io/

相关文章

  • 以太坊C-源码解析(十)以太坊交易中的nonce

    以太坊交易中存在一个特殊的值nonce,此nonce并非计算block难度的nonce,此nonce仅仅表示发送账...

  • ETH交易源码

    006 以太坊源码解析 - 交易源码分析 以太坊交易基本流程: 完整流程分为以下几个步骤: 发起交易:指定目标地址...

  • 以太坊交易签名解析源码解读

    上篇文章《以太坊交易签名过程源码解析》从源码角度分析了一个合约调用的的签名过程,签名后的交易发送到以太坊节点后,节...

  • Abstract account

    以太坊交易通过 nonce 和 signauter 验证,只能通过外部账户(EOA) 发起交易。 zkSync 2...

  • 精通以太坊设计之:Gas驱动

    本文精通以太坊系列第二篇,前一篇《精通以太坊之nonce值,真的能避免51%攻击吗?》 Gas,是用来驱动以太坊平...

  • 以太坊中的nonce

    以太坊中的nonce 为了防止交易重播,ETH(ETC)节点要求每笔交易必须有一个nonce数值。每一个账户从同一...

  • 如何购买以太坊

    以太坊ETH是一种区块链技术,而以太币是以太坊的原生代币。随着以太坊价格的暴涨以及数字货币被人们所承认,以太坊交易...

  • 以太坊中的nonce是什么

    为了防止交易重复进行,以太坊要求每笔交易必须有一个nonce数值。nonce值从0开始递增,每发送一笔交易,non...

  • 以太坊交易中的Nonce详解

    nonce在区块链中是一个非常重要的概念,从比特币到以太坊都有nonce的身影。 在比特币中,nonce主要用于调...

  • 以太坊入门(一)账户和nonce的关系

    什么是nonce? 在以太坊的交易数据中,大家都可以看到一个数字,nonce,从0开始,一直向上递增,这个代表什么...

网友评论

      本文标题:以太坊C-源码解析(十)以太坊交易中的nonce

      本文链接:https://www.haomeiwen.com/subject/falnkctx.html