Move 智能合约生产环境升级实践 | Move dApp 极速入门(贰拾陆)
https://github.com/NonceGeek/MoveDID
0x01 Move 合约的可升级性
Aptos链支持代码升级,这意味着已经部署的 Move 代码可以进行代码更新。如果发生了代码升级,所有被升级的代码的消费者在下次执行代码时将自动收到更新后的代码。
代码升级使代码所有者能够在一个稳定的、已知的账户地址下迭代他们的合同或框架,然后可以被链内或链外的其他应用程序引用。
文档地址:
中文 ——
https://gushi10546.gitbook.io/aptos-kai-fa-zhe-wen-dang/guides-zhi-nan/move-jiao-cheng/move-bao-geng-xin
英文 ——
https://aptos.dev/move/book/package-upgrades/
1.1 合约升级的设置
Aptos 链上的代码升级发生在 Move package 层面。在 Move.toml 文件中,开发者可以指定该 package 的升级策略。
[package]
name = "MyApp"
version = "0.0.1"
upgrade_policy = "compatible"
...M
目前存在两种不同的升级策略:
兼容(Compatible)
:这些升级必须向后兼容,具体来说:为了存储,所有旧的结构声明在新代码中必须相同。这确保了新代码可以正确解释现有的存储状态。但是,可以添加新的结构声明。对于 API,所有现有的公共函数必须具有与以前相同的签名。可以添加新功能,包括公共功能和入口功能。不可变(immutable)
:代码不可升级,并保证永远保持不变。
1.2 升级策略
我们需要关注的是,在合约升级的过程中,哪些部分是可变的
,而哪些部分是不可变的
。
使用兼容升级策略时,可以对模块包进行升级。但是,对先前已发布的现有模块的更新需要兼容并遵循以下规则:
所有现有结构的字段都无法更新。 这意味着不能添加新字段,也不能修改现有字段。结构能力也无法更改(没有添加新的能力或删除现有的能力)。 所有公共函数和入口函数都不能更改其签名(参数类型、参数类型、返回类型)。 但是,参数名称可以更改。 public(friend) 函数被视为私有函数,因此它们的签名可以任意更改。这是安全的,因为无论如何只有同一包中的模块才能调用友元函数,并且如果签名发生更改,则需要更新它们。 代码的其他部分可以修改。
更新模块时,如果您看到不兼容的错误,请务必检查上述规则并修复任何违规行为。
0x02 MoveDID 合约结构设计
.
├── Move.toml
├── README.md
├── build
│ └── did
└── sources
├── addr_aggregator.move
├── addr_aggregator.spec.move
├── addr_aptos.move
├── addr_aptos.spec.move
├── addr_bitcoin_verfificated_offline.move[new]
├── addr_eth.move
├── addr_eth.spec.move
├── addr_info.move
├── addr_info.spec.move
├── eth_sig_verifier.move
├── eth_sig_verifier.spec.move
├── init.move
├── service_aggregator.move
├── service_aggregator.spec.move
├── utils.move
└── utils.spec.move
其中各模块的功能描述如下:
init.move
:数字身份初始化,同时初始化addr_aggregator
和service_aggregator
。utils.move
:一些通用的功能,例如类型转换等。addr_info.move
:定义AddrInfo`结构体及其相关的通用方法。addr_aggregator.move
:地址聚合器,包含聚合器的初始化、地址的增删改查与批量操作等。service_aggregator.move
:服务聚合器,包含聚合器的初始化、 Web2/Web3 服务的增删改查与批量操作等。addr_aptos.move
:实现针对 Aptos 地址类型的update_addr
函数,被addr_aggregator
所调用。addr_eth.move
:实现针对 EVM 地址类型的update_addr
函数,被addr_aggregator
所调用。eth_sig_verifier.move
:实现 EVM 签名验证的函数,被addr_eth.move
所调用。
0x03 addr_bitcoin_verificated_offline 方案
RMUD Identity 去中心化数字身份协议是高可扩展的、轻量级数字身份协议。
其中一个体现是,我们可以通过升级模块的方式,优雅地增加对不同链的支持。例如,在第一个版本中,我们支持了aptos addr
和eth addr
,在之后的版本中更新addr_bitcoin
和crypto_bitcoin
这两个模块,就可以实现对bitcoin
地址格式的支持了。
然而,完整的链上版本的 bitcoin module 需要更多的代码量,在实现这个完整的支持前,我们决定更新一个过渡性的版本—— addr_bitcoin_verificated_offline
,通过在链下验证 Bitcoin 签名的方式先做简略的实现。
module my_addr::addr_bitcoin_verificated_offline {
use std::signer;
use std::string::{Self, String};
use my_addr::utils;
use aptos_framework::timestamp;
use my_addr::addr_info::{Self, AddrInfo};
friend my_addr::addr_aggregator;
// Bitcoin addr type.
const ADDR_TYPE_BTC: u64 = 2;
// Err enum.
const ERR_DEPRECATED_ALREADY: u64 = 2001;
//:!:>resource
struct DeprecatedCapability has key, store, copy, drop {
deprecated: bool
}
//<:!:resource
// only signer call this is available.
public entry fun init(acct: &signer) {
let de = DeprecatedCapability{
deprecated: false
};
move_to<DeprecatedCapability>(acct, de);
}
public entry fun enable(acct: &signer) acquires DeprecatedCapability{
let send_addr = signer::address_of(acct);
let de = borrow_global_mut<DeprecatedCapability>(send_addr);
de.deprecated = false
}
public entry fun depre(acct: &signer) acquires DeprecatedCapability{
let send_addr = signer::address_of(acct);
let de = borrow_global_mut<DeprecatedCapability>(send_addr);
de.deprecated = true
}
public(friend) fun update_addr(addr_info: &mut AddrInfo, signature: &mut String) acquires DeprecatedCapability{
// only avaiable if @my_addr's DeprecatedCapability.deprecated == false
let de_cap = borrow_global<DeprecatedCapability>(@my_addr);
// check deprecated
assert!(de_cap.deprecated == false, ERR_DEPRECATED_ALREADY);
let addr_info_msg = addr_info::get_msg(addr_info);
// Check msg etmpy.
assert!(addr_info_msg != string::utf8(b""), addr_info::err_addr_info_empty());
// Check addr type.
assert!(addr_info::get_addr_type(addr_info) == ADDR_TYPE_BTC, addr_info::err_invalid_addr_type());
let sig_bytes = utils::trim_string_to_vector_u8(signature, 2); //trim 0x
// Verify the now - created_at <= 2h.
let now = timestamp::now_seconds();
assert!(now - addr_info::get_created_at(addr_info) <= 2 * 60 * 60, addr_info::err_timestamp_exceed());
// Update signature, updated_at.
addr_info::set_sign_and_updated_at(addr_info, sig_bytes, now)
}
#[view]
public fun get_deprecated_status(owner: address) :bool acquires DeprecatedCapability {
borrow_global<DeprecatedCapability>(owner).deprecated
}
}
我们针对这一过渡方案,设计了DeprecatedCapability
这个 Resource。RMUD Identity 的管理员拥有这个 Resource,如果有了更完全的解决方案,那么管理员将DeprecatedCapability
中的deprecated
设置为true
,addr_bitcoin_verificated_offline::update_addr
这个函数即会失效。
0x04 总结
在 RMUD Identity 的整体设计中,我们完成了如下两点的实践:
对如何基于 Move 做高可扩展的协议进行了思考与实现; 对如何开发过渡性模块,也就是未来有 Deprecated 可能的模块进行了思考与实现。