从 sCrypt 智能合约中访问区块链数据(4)

一个区块中的交易总数是一个重要的信息。我们展示了如何在没有受信任的第三方的情况下获得它,这在以前被认为是不可能的。

范围

让我们将 Merkle 路径的长度表示为 n。叶子的数量,即一个区块中的交易数量,介于 2^(n-1) + 12^n 之间。这是因为高度为 n 的满二叉树¹ 恰好有 2^n 个叶子。Merkle 路径的长度与 Merkle 树的高度相同。

在这里插入图片描述

高度 2 的满二叉树

确切数字

查找最后一笔交易

上一篇文章中,我们访问了区块中的第一个 coinbase 交易。如果我们还可以访问最后一笔交易,我们就可以推断出交易总数。我们可以通过验证其 Merkle 路径上的所有节点都在右分支上来识别第一个/最左边的交易。类似地,通过要求所有 Merkle 路径节点都在左分支上,找到最后/最右边的交易看上去也很简单合理,但这可能并不总是正确的。

如果我们有一个满的 Merkle 树,那么最后一笔交易的 Merkle 路径上的所有节点确实都在左分支上,如下所示。
在这里插入图片描述

最后一笔交易的彩色默克尔路径

但是,当树不满时,情况并非如此。

例如,下面的树有 5 个叶子,最后一个交易的 Merkle 路径由所有颜色为橙色的节点组成。其中两个在右边的树枝上,只有最上面的一个在左边。

在这里插入图片描述

为了克服这个问题,我们注意到当 Merkle 树的任何单层中有奇数个节点时,最后一个节点被复制。这意味着,如果最后一笔交易的 Merkle 路径上的任何节点在右分支上,则必须从当前的左分支复制它,如下图所示。

在这里插入图片描述

下面的代码直接实现了算法。

// is txid the last transaction in a block
static function lastTxInBlock(Sha256 txid, BlockHeader bh, MerkleProof merkleproof) : bool {
    bool last = true;
    Sha256 root = txid;

    loop (MerklePath.DEPTH) : i {
        Node node = merkleproof[i];

        if(node.left != MerklePath.INVALID_NODE) { // s is valid
            // if node on the merkle path is on the right, it must be a duplicate
            // if node on the merkle path is on the left, it must NOT be a duplicate
            if (node.left != MerklePath.LEFT_NODE && node.hash != root || node.left == MerklePath.LEFT_NODE && node.hash == root) {
                last = false;
            }

            root = node.left == MerklePath.LEFT_NODE ? hash256(node.hash + root) : hash256(root + node.hash);
        }
    }

    return last && root == bh.merkleRoot;
}
Blockchain 源代码

从默克尔路径到交易索引

为了导出交易的索引,我们遵循从根到代表它的叶子的 Merkle 路径。当路径上的一个节点在左分支上时,我们向右走(即二进制1);否则,我们向左走(二进制 0)。容易看出,我们可以通过这种方式获得其索引的二进制表示。在下面的示例中,我们将此规则应用于最后一笔交易,然后一直向右走,得到二进制的 111,这正是它的十进制索引 7

在这里插入图片描述

一棵所有叶子都以二进制索引的树

此代码如下所示:

// calculate a tx's index in a block from its merkle path
// goes from top to bottom, the path basically encodes the index in binary form
// left/L means 1, and right/R 0: e.g., (L, R, L) denotes 101 in binary, and 5 in decimal
static function txIndex(MerkleProof merkleproof) : int {
    int sum = 0;

    // traverse the path from top to bottom
    loop (MerklePath.DEPTH) : i {
        Node node = merkleproof[MerklePath.DEPTH - i - 1];

        if(node.left != MerklePath.INVALID_NODE ) {
            sum *= 2;
            if (node.left == MerklePath.LEFT_NODE) {
                sum++;
            }
        }
    }
    return sum;
}
Blockchain 源代码

获取交易总数

最后,我们将上面的两个函数应用于 blockTxCount() 函数中,以获取某个区块内包含的准确交易数量。

// get number of transactions in a block
static function blockTxCount(BlockHeader bh, Sha256 lastTxid, MerklePath merklePath) : int {
    // ensure this tx is indeed the last one
    require(lastTxInBlock(lastTxid, bh, merklePath));

    return txIndex(merklePath) + 1;
}

总结

一旦我们可以访问一个区块中的交易数量,我们就可以使用它来构建以前被认为不可能的智能合约。

我们仅在下面列出几个示例:

  • 放置一个仅在一个区块包含超过 100 万笔交易时才支付的赏金合约,以赞助压力测试。
  • 结合之前获得的时间信息,无论是区块头中的时间戳还是区块高度,都可以忠实地计算每秒交易量(TPS)并在合约中使用。例如,只有在 TPS 达到 100,000 时才解锁的合约。

我们期待看到您能提出什么样的令人兴奋的新合约。

致谢

这篇文章的灵感来自 shilch 的工作

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

sCrypt Web3应用开发

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

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

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

打赏作者

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

抵扣说明:

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

余额充值