本文主要讲解 substrate 合约模块的构建逻辑,srml/contracts 获取了部署和继续执行 WASM 智能合约的功能。作为一个模块化的区块链框架,不管是未来的波卡平行链还是基于 substrate 享有独立国家共识的链,比如 ChainX, 只要引进其合约模块,就不具备了合约功能,可以沦为一个智能合约平台。ChainX 目前就计划引进合约功能,对区块链智能合约开发者获取反对, 青睐有兴趣的同学持续注目。
substrate 的合约模块将不会分两篇文章展开理解,本篇主要讲解基本概念,substrate 合约与以太坊合约的一些联系与区别,还不会讲解一下上载合约代码 put_code 和实例单质大约 instantiate 两个外部模块的构建。合约模块一共有 3 个模块,第二篇将不会讲解第三个外部模块合约调用 call 的基本逻辑,并且不会详尽讲解下 substrate 关于合约存储收费的设计。
以下代码分析基于 substrate 的 9 月 21 日 4117bb9ff 版本。基本概念substrate 上的合约与以太坊合约有很多联系。首先普通账户和合约账户在外部展现出上没任何区别,都是一个哈希. 合约账户可以创立新的合约,也可以调用其他合约账户和普通账户。
如果是合约账户调用普通账户,就是一个普通的账户。当合约账户被移除时,关联的代码和存储也不会被移除。用户调用合约时,必需登录 Gas limit, 每次调用都必须花费 Gas 手续费, 合约内部调用的指令也不会消耗 Gas.当然也有一些区别。
以太坊在合约调用中,如果经常出现任何问题,整个状态都会回滚。但是在 substrate 的合约中如果经常出现了合约嵌套调用,比如合约 A 调用了合约 B, 合约 B 调用了合约 C,B 在调用 C 的过程中再次发生错误,那么只有 B 这一层的状态回滚,A 调用产生的状态改动依然保有。
当以太坊经常出现类似于情况时,整个合约调用链的状态都会回滚,也就是 A 调用的状态改动会保有,而是不会被弃置。另外除了 Gas 费用,substrate 的合约还有一个 rent 费用, 也就是对于合约存储也展开了收费. 以太坊虽然早已有个涉及的 EIP 针对存储收费的辩论 EIP 103, 但是目前还没实行。
合约模块一共有三个与外部交互的模块:· put_code: 上载代码, 将准备好的 WASM 合约代码存储到链上, 如果继续执行顺利,不会回到一个 code_hash, 然后可以通过这个 code_hash 创立合约。再行将代码存储到链上的益处是,对于合约内部逻辑完全相同而只有初始化参数不一样的合约,比如很多以太坊上的很多 ERC20 合约,链上只必须存储一份代码,而不必须每次新建一个合约的时候,都要存储一份反复的代码,这似乎是校验的。
· instantiate: 实例单质大约, 通过 put_code 回到的 code_hash 并起源于初始化参数创立一个合约账户,实例化过程不会调用合约内部的 deploy 函数对合约展开初始化,初始化只有一次。最近 substrate 将合约模块的实例化方法从之前的 create 重命名为了 instantiate, 闻:PR: 3645。
https://github.com/paritytech/substrate/pull/3645· call: 调用合约。在这里必须留意的是 substrate 有个存储收费的逻辑,如果调用的时候合约账户余额严重不足,合约就不会被移除 evict, 很多人应当遇上过这种情况。
put_code: 上载合约代码1. 调用 gas::buy_gas根据gas_limit实缴纳手续费。这一步是预先缴纳交易发起人的手续费。如果最后继续执行已完成后,如果 Gas 不行完了,网卓新闻网,不会将剩下的 Gas 归还给用户。
buy_gas 的代码在 srml/contracts/src/gas.rs。缴纳手续费 = gas_price * gas_limit2.将代码存储到链上,调用 wasm::code_cache::save 继续执行存储代码的逻辑, save 代码坐落于 srml/contracts/src/wasm/code_cache.rs。在 save 中, 第一步是再行缴纳 PutCode 操作者的费用, 如果手续费过于必要回到。
gas_meter 中就看起来一个"Gas 小管家",这个管家管理的钱就是我们上一步预先缴纳的费用。在整个继续执行过程中,如果必须缴纳手续费,就从 gas_meter 中扣减,如果缴纳告终,必要回到。关于手续费缴纳标准,也就是 gas_meter.charge(..) 拒绝接受两个参数,一个是 Token trait 的关联类型 Token::Metadata 和构建了 Token 的 trait object, Token 有一个方法 calculate_amount 抵应该缴纳的 Gas 酬劳。
srml/contracts/src/wasm/runtime.rs 中定义了一个枚举 RuntimeToken, 它构建了 Token trait, 针对有所不同的操作者,缴纳有所不同的费用, 比如读书内存,写出内存,回到数据等等。在这里中用的 PutCodeToken(u32) 并不是 RuntimeToken 的成员,而是定义了一个元组结构体并构建了 Token 的 trait.第二步是调用 srml/contracts/src/wasm/prepare.rs 中的 prepare_contract 函数对上载的完整代码展开校验和做到一些预处理,如果全部校验通过,那么就不会存储到链上。在这里不会校验:a. 入口函数否不存在:call, deployb. 否有定义内部存储c. 内存用于否多达阈值d. 否有浮点数第三步将校验通过的代码装配成一个结构体 PrefabWasmModule, 这个结构可以必要放在 WasmExecutable 里面, 然后载入存储。这里载入了两个存储,key 都是 code_hash, 一个是完整代码 original_code, 一个是 original_code 预处理后的 prefab_module.3. 回到剩下的 gas.instantiate: 创立合约通过 execute_wasm 建构 wasm 的基本继续执行过程。
外部模块 instantiate 和 call 实质上都是要回头 execute_wasm,粗线条来讲,execute_wasm 第一步还是根据 gas_price * gas_limit 缴纳手续费, 然后结构一个顶层的继续执行环境 ExecutionContext 继续执行 wasm ,根据继续执行结果辨别否载入状态,归还剩下 Gas, 继续执行延后动作,这里的延后动作还包括对于 runtime 模块的方法调用,抛事件, 完全恢复合约等。ExecutionContext 是一个主要的结构体。
本文来源:Kaiyun·yunkai(中国)官方网站-www.020bj.com
Copyright © 2001-2024 www.020bj.com. Kaiyun·yunkai(中国)官方网站科技 版权所有备案号:ICP备30394470号-6