对话Lingfeng合伙人:从Binance Labs到基金合伙人
11月 8 日,7upDAO 海归公会邀请了Nicole Zhang,Lingfeng Innovation Fund创始合伙人,Binance Labs前执行董事进行了对话分享。 据悉11月1日,Web3风险投资基金Lingfeng Innovation F…

最近,在 11 月 4 日,我升级了 Papyrs 的 1,000 多个容器智能合约的代码 —— 一个开源、隐私优先、去中心化的博客平台,其中一半的源代码已从 Motoko 完全重写为 Rust。
互联网计算机 —— 托管这些容器的通用区块链 —— 使用 WebAssembly(Wasm)虚拟机来运行其智能合约,这些可以用各种编程语言编写,包括上面提到的两种语言。
虽然将代码库从一个切换到另一个在纸面上和实践中都非常有效,但我必须首先解决一些挑战才能执行这样的迁移,其中,需要特别关注两个挑战:
在 Papyrs 上,没有一个智能合约可以保存所有用户的数据 —— 他们每个人都有自己的领域,登录后,社区的每个成员都会获得两个容器:一个用于数据(“db”),另一个用于为网络上的博客提供服务(“CDN 存储”)。
当我希望迁移后一种类型的容器时,我必须找到一种方法,从我现有的用 Motoko 编写的“管理器”容器中即时创建基于 Rust 的容器。
我很快发现可以通过实际更改几行代码来解决这样的功能,事实上,在 Motoko 和同一个项目中导入一个 Rust 容器,借助一个简单的工具 import 就可以了。

然而,这不完全是我的用例 —— 也就是说,我不是要调用一个给定的特定容器,而是要即时创建新的智能合约。
在经历了各种路径之后,我最终选择了包含实施通用解决方案的路径,我实现了一个创建新容器并从 Wasm 字节安装代码的函数。这样,容器的代码是用 Rust 还是任何其他语言开发的都没有关系。

-
该函数首先指示在下一次创建新容器的调用中将传输多少 cycles;
-
-
在我的特定用例中,我希望将用户和 “manager”(希望有一天我将 Papyrs 推迟到 SNS)设置为新创建的容器的控制器,这就是函数更新这些设置的原因,请注意,我不确定是否可以通过在前面的步骤二中创建容器时提供控制器来省去此步骤,据我所知,它一次都没有成功,所以我总是这样做;
-
它对初始化 Wasm 代码所需的参数进行编码,如果没有初始化参数,这可以省去;
-
它最终安装了 Wasm 代码 —— 即我编写和编译的 Rust 代码。
此时,您可能会问,“Wasm 代码从何而来?”我会及时解释的。
起初,我的想法是在编译 Motoko 代码时在构建时加载字节,不幸的是,由于内存限制,这没有成功。
在论坛上讨论了这个问题后,Austin Fatheree 分享了在部署 “manager”,容器后将 Wasm 代码上传到内存中的想法,这是我选择并实施的解决方案。

我向创建其他容器的容器添加了两个函数:一个用于重置状态,另一个用于将 Wasm 代码块附加到本地状态。
⚠️ 这些函数需要一些在上面的代码片段中没有实现的正确断言,您很可能不希望攻击者能够用一些恶意的 Wasm 代码覆盖和替换状态。
最后,我创建了一个 NodeJS 脚本,它读取 Wasm 代码并调用上面的端点来加载我在 “manager” 的内存中开发的 Wasm 代码。

虽然上一章可能与 Papyrs 的行为方式更相关,但如果不支持,能够将 Motoko 容器迁移到 Rust 同时在主网上保留其状态肯定会成为阻碍。当然,剧透警报,它是 —— 而且迁移非常顺利。
互联网计算机的一个关键特性是它能够使用 WebAssembly 内存和全局变量来保存容器智能合约状态,这就是为什么无论使用何种编程语言都可以升级容器的原因。
话虽这么说,但它可能不太容易,特别是对于像我这样的 Rust 新手。
鉴于下面的容器,我合理地假设必须在新的容器中解码稳定的内存,在用 Rust 编写的那个容器中,但是,我不知道如何。

幸运的是,令人惊叹的 IC 社区再次闪耀,感谢 Frederik Rothenberger 的帮助,特别是 Alexander Vtyurin 代码片段,我遇到的最大问题可以在开发者论坛上得到解决!
在 Rust 中,升级前已填充的稳定内存可以在稳定 API 的帮助下在升级后挂钩中读取,并使用 candid decode_args 解码。

虽然这是一个重要的里程碑,但挑战尚未结束,记忆仍然需要“重组”。
对于这个特定任务,我没有找到任何灵丹妙药,这些类型必须手动转换 —— 也就是说,我检查了我的 Motoko 代码并从头开始重写了所有类型(我知道,我有奇怪的爱好)。

上面的代码片段是前面 Motoko 代码的对应类型,下面是这种转换的最重要的要点:
-
变量名必须匹配,如果一个稳定的变量在 Motoko 中被命名为 test,它必须在 Rust 中被命名为 test;
-
从稳定内存解码的所有根类型都成为可选的,无论它们是否在之前的代码中被声明为强制性的;
-
不将这些类型声明为可选类型会导致解码错误,该错误将在以 dfx 开头的本地 IC 中抛出;
-
在任何级别的转换中,任何与原始类型不匹配的类型都将导致静默错误(⚠️),并且将有效解码为无 —— 例如,在上面的例子中,如果我将 modified 声明为 u128 而不是正确的 candid::int —— 这与 Motoko Time 相关 —— 所有的 entires 都会被解码为无,没有错误,没有堆栈跟踪,只有无和泪水的海洋。
一旦内存读取和重组,升级就差不多完成了,没有其他大的阻挡者。我可以坚持状态,所有数据仍然存在。
为了验证这种迁移的假设,我将这两章都开发成两个不同的样本库,他们的代码不像最终实现那样干净,但如果它们有用的话,它们是:
-
github.com/peterpeterparker/motoko_rust_interop
-
github.com/peterpeterparker/motoko_to_rust_migration
我的迁移已经结束,我在主网上执行的相关代码已经存档,但你仍然可以在 Papyrs 的后端和提供者的历史中找到它:
我在互联网计算机上将 500 多个容器从 Motoko 迁移到 Rust,说真的,我不想撒谎,因为我对此不信任自己,我几乎仍然不敢相信它在主网上成功了,没有任何问题。
这可能是因为我在本地从头开始至少执行了 50 次整个迁移过程,在主网上也执行了几次。
因此,如果您打算也执行这样的迁移,如果我可以分享最后一条建议:测试、测试和执行之前再测试一些!
如需更多冒险,请在 Twitter 上关注我(@daviddalbusco)。

– 往 期 推 荐 –


长按关注 IC 微信公众号
随时答疑解惑
*添加小助手微信 comiocn 进交流社群
SeeWorld | 大航海时代之墨尔本篇
澳洲对 crypto 行业比较友好,政策也相对宽松,吸引数字游民以各种身份来澳洲居住。从数字游民的角度,我介绍一下大家最关心的事情。 #1选择哪个城市?墨尔本 vs 悉尼 1 > Web3 氛围 Web3 的工作机会两个城市五五开吧,差别不大。大家也…
Click to rate this post!
[Total: 0 Average: 0]