IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 使用 P2CH 拆分合约 -> 正文阅读

[游戏开发]使用 P2CH 拆分合约

我们提出了一种优化技术,可以将一个大合约拆分为多个较小的合约,从而在保持正确性的同时大幅减小其大小。我们将展示它如何在具有大循环和许多公共函数的合约中工作。

循环 (Loops)

循环在 sCrypt 中采用以下格式:

loop (maxLoopCount) {
    loopBody
}

因为循环是静态展开的,所以在编译时必须知道最大循环计数 maxLoopCount。如果设置得太小,可能会导致合约无法解锁成功,资金被永久烧毁。因此,它是为最坏的情况保守地设置的,并且当最常用的循环计数明显小于最坏的情况时,通常会导致脚本过度膨胀。

我们在上一篇文章中针对最普遍的情况展示了如何使用 Pay to Contract Hash (P2CH) 来减少合约 IncrementeLocktime 中的循环计数,同时在需要确保在最大循环计数时保持合约正常工作。

如下图所示,我们将函数 partialSha256()(其循环占用大部分合约大小)移至单独的合约 PartialSha256。循环中的每次迭代都会处理 SHA256 原像中的一个块。我们使用 P2CH 从主合约的 IncrementLocktimeSplit 调用该函数。

在这里插入图片描述

// same as contract IncrementLocktime, but splitting it
contract IncrementLocktimeSplit {
    //...

    static const int N = 3;
    // all possble hashes of the callee contract, i.e., all possible MAX_CHUNKS supported
    PubKeyHash[N] calleeContractHashes;

    public function main(int selector, bytes partialHash, bytes partialTx, bytes padding, bytes prevouts, 
        bytes calleeContractTx, bytes outputScript, int amount, SigHashPreimage txPreimage) {
        //...

        // validate partial tx without the full tx
        // P2CH: Pay to Contract Hash
        require(TxUtil.verifyContractByHash(
                    prevouts,
                    calleeContractInputIndex,
                    calleeContractTx,
                    this.calleeContractHashes[selector],    // which MAX_CHUNKS
                    txPreimage));
        // read function call arguments and return
        // remove leading OP_FALSE OP_RETURN
        bytes data = outputScript[2:];
        Reader r = new Reader(data);
        // arguments
        require(r.readBytes() == partialHash);
        require(r.readBytes() == partialTx);
        require(r.readBytes() == padding);
        // return value
        require(r.readBytes() == txid);
        // once we reach here, the following must be true
        //require(sha256(partialSha256(partialHash, partialTx, padding)) == txid);
        
        //...
    }
}
IncrementLocktimeSplit合约源码

IncrementLocktimeSplit 与合约 IncrementLocktime 相同,只是从第 15 行到第 30 行删除了 partialSha256() 并使用 P2CH 间接调用。

数组 calleeContractHashes 中的每个元素都是合约 PartialSha256 的哈希,具有不同的 MAX_CHUNKS。因此,该数组包含主合约支持的所有 MAX_CHUNKS

合约 PartialSha256 计算 partialSha256()(与原始合约 IncrementLocktime 相同的函数)并将函数调用参数和结果值存储在输出中,正如我们在合约间调用中所做的那样,它可以被合约 IncrementLocktimeSplit 访问。


contract PartialSha256 {
    // max number of chunks (512 bits) to be hashed
    static const int MAX_CHUNKS = 2;

    public function main(bytes partialHash, bytes partialPreimage, bytes padding, SigHashPreimage txPreimage) {
        //...
        bytes result = partialSha256(partialHash, partialPreimage, padding);

        // write arguments and return into the output
        bytes data = Writer.writeBytes(partialHash) + Writer.writeBytes(partialPreimage) + Writer.writeBytes(padding) + Writer.writeBytes(result);

        //...
    }
    
    // compute the sha256 from current hash (@partial_hash) and the remaining preimage (@partial_preimage)
    static function partialSha256(bytes partial_hash, bytes partial_preimage, bytes padding) : bytes {
        //...
        loop (MAX_CHUNKS) : i {
        }
        //...
    }
}
PartialSha256 合约源码

IncrementLocktime 相比,IncrementLocktimeSplit 要小得多。它可以根据要散列的数据的长度,即 MAX_CHUNKS 动态调用 partialSha256()。在大多数情况下,我们只需要 1 的 MAX_CHUNKS。在锁定时间被分成最后两个块的极少数情况下,我们使用 2 的 MAX_CHUNKS。这带来了显着的节省,因为每个额外的块都会为最终脚本增加约 60KB 的大小。

多个公共函数

contract ContractOfManyBranches {
    
    public function branchA() { }

    public function branchB() { }

    public function branchC() { }

    public function branchD() { }

    // ... more branches
}
原始合约

我们可以将具有多个公共函数的合约分解为多个较小的合约,每个合约只包含一个公共函数。我们将原始合约替换为新的主合约,该主合约可以使用上一节中的相同技术调用任何较小的合约。

contract Main {
    static const int N = 4;
    // all possble hashes of the callee contract, i.e., all possible branches
    PubKeyHash[N] calleeContractHashes; 
    
    // ...
}
拆分后的合约

在这里插入图片描述

主合约调用分支A

当每个公共函数都很大并且有很多时,这种拆分带来显著的优化。

除了减小大小之外,这种优化还允许同一合约的其他输入中的合约识别当前主合约中调用了哪个公共函数。通常这是未知的,因为无法访问 OP_PUSH_TX 中的解锁脚本。这可以通过注入较小的合约交易来访问,例如 tx1。

讨论

我们已经说明了如何通过将大合约拆分为多个较小的合约来减少大合约的规模。示例合约是一次性且无状态的。对于连续调用并累积节省的有状态合约,减少将更为显着。

优化使用许多公共函数的合约的另一种方法是使用Merklized 抽象语法树压缩智能合约

致谢

这个想法源于 Sensible Contract,它在生产中广泛使用它。

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-04-18 18:15:32  更:2022-04-18 18:19:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 15:07:29-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码