
Diem 框架中的 RegisteredCurrencies 模块货币注册机制的源码级解析【免费下载链接】diemDiem’s mission is to build a trusted and innovative financial network that empowers people and businesses around the world.项目地址: https://gitcode.com/gh_mirrors/di/diem导读RegisteredCurrencies是 Diem 区块链Move 语言实现的链上框架中负责**注册货币代码currency code**的核心模块。它以DiemConfig链上配置的形式维护一张全局的货币代码清单并作为Diem::register_currency等货币发行流程的底层登记入口。读完本文你将掌握该模块的数据结构设计与权限模型、两个公开函数initialize/add_currency_code的完整实现与中止条件、它与DiemConfig、Diem、Roles、DiemTimestamp的协作关系以及 Move 形式化规范spec对它的约束验证方式。模块定位货币代码的全局登记簿在 Diem 的链上框架中一个货币要想流通首先必须被“注册”。RegisteredCurrencies模块的职责非常聚焦——正如其在 RegisteredCurrencies.move 源码注释中所写Module for registering currencies in Diem. Basically, this means adding a string (vector ) for the currency name to vector of names in DiemConfig.即把每个货币的名称以vectoru8字符串表示追加到DiemConfig中维护的一个名称向量里。它不负责货币的铸造、销毁或汇率只负责维护“哪些货币代码已被登记”这一事实。该模块的依赖关系非常清晰见 模块源码DiemFramework::DiemConfig把货币代码清单作为链上配置发布与读取DiemFramework::DiemTimestamp校验初始化是否发生在 genesis 阶段DiemFramework::Roles校验调用者是否具有 DiemRoot 角色Std::Errors构造标准错误码Std::Vector对货币代码向量做包含检查与追加操作。这一依赖结构也决定了它的两个基本事实该模块只能由 Diem 根账户在 genesis 时初始化后续任何新增货币代码的操作都必须由 Diem 根账户签名发起。数据结构RegisteredCurrencies结构体模块中仅定义一个结构体见 模块源码struct RegisteredCurrencies has copy, drop, store { currency_codes: vectorvectoru8, }currency_codes: vectorvectoru8一个二维字节向量。外层向量按添加顺序保存所有已注册货币内层vectoru8是货币代码的 UTF-8 字符串表示例如bXUS、bXDX。结构体同时具备copy、drop、store三种能力ability这是DiemConfig泛型配置类型的前提DiemConfigConfig要求Config: copy drop store见 DiemConfig.move因此该结构体可以被整体复制、丢弃并存储为链上资源。从语义上讲这个结构体本身并不直接持有key能力它总是作为DiemConfigRegisteredCurrencies的 payload 挂在 Diem 根账户地址下这一点由模块级不变量见下文保证。错误常量重复注册的防护const ECURRENCY_CODE_ALREADY_TAKEN: u64 0;该常量表示“尝试添加一个已经被占用的货币代码”。配合Errors::invalid_argument(ECURRENCY_CODE_ALREADY_TAKEN)使用最终产生的 Move 错误码为INVALID_ARGUMENT (0x1)类别下编号 0 的错误。注意invalid_argument返回的原始 abort code 是1Errors标准库中INVALID_ARGUMENT 1的类别前缀而ECURRENCY_CODE_ALREADY_TAKEN 0是模块内序号二者经Errors::invalid_argument组合后实际 abort 码由测试用例可以印证见下文测试部分。初始化initialize实现解析public fun initialize(dr_account: signer) { DiemTimestamp::assert_genesis(); Roles::assert_diem_root(dr_account); DiemConfig::publish_new_config( dr_account, RegisteredCurrencies { currency_codes: Vector::empty() } ); }初始化流程由三道防线构成时间防线DiemTimestamp::assert_genesis()—— 断言当前处于 genesis 阶段时间戳尚未进入运行状态确保该模块只能在链创世时被初始化一次权限防线Roles::assert_diem_root(dr_account)—— 断言签名者具备 DiemRoot 角色发布动作DiemConfig::publish_new_configRegisteredCurrencies(dr_account, ...)—— 以 Diem 根账户为发布者将初始状态{ currency_codes: Vector::empty() }空清单发布为一条DiemConfig配置。也就是说初始状态下系统不预置任何货币代码XUS、XDX等正式货币是在 genesis 之后通过货币注册流程逐个登记的。调用方Diem::initializeRegisteredCurrencies::initialize并不是一个游离的入口它被Diem模块的初始化流程显式调用。在 Diem.move 中public fun initialize(dr_account: signer) { DiemTimestamp::assert_genesis(); // Operational constraint CoreAddresses::assert_diem_root(dr_account); RegisteredCurrencies::initialize(dr_account); }可见Diem::initialize先校验调用地址确为DiemRoot随即委托给RegisteredCurrencies::initialize完成货币清单的初始化。因此可以推断注册货币的全局清单是 Diem 框架启动genesis过程中的一个固定环节与CurrencyInfo、铸币/销毁能力MintCapability/BurnCapability体系的初始化同步完成。规范约束initialize的 Move 规范spec通过两个 schema 定义了中止与后置条件见 RegisteredCurrencies.moveInitializeAbortsIf依次引入DiemTimestamp::AbortsIfNotGenesis、Roles::AbortsIfNotDiemRoot、DiemConfig::PublishNewConfigAbortsIfRegisteredCurrencies即非 genesis 阶段、非 Diem 根、或配置已发布时都必须中止InitializeEnsures引入DiemConfig::PublishNewConfigEnsures并断言len(get_currency_codes()) 0即初始化后清单长度必须为 0。新增货币代码add_currency_code实现解析public fun add_currency_code( dr_account: signer, currency_code: vectoru8, ) { let config DiemConfig::getRegisteredCurrencies(); assert( !Vector::contains(config.currency_codes, currency_code), Errors::invalid_argument(ECURRENCY_CODE_ALREADY_TAKEN) ); Vector::push_back(mut config.currency_codes, currency_code); DiemConfig::set(dr_account, config); }执行路径分四步DiemConfig::getRegisteredCurrencies()读出当前清单按值拷贝得益于结构体的copy能力用Vector::contains检查新代码是否已存在若已存在则抛Errors::invalid_argument(ECURRENCY_CODE_ALREADY_TAKEN)中止Vector::push_back将新代码追加到向量末尾保持注册顺序DiemConfig::set(dr_account, config)以 Diem 根签名写回配置。这里存在一个值得注意的细节add_currency_code内部并未显式调用Roles::assert_diem_root其权限约束来自DiemConfig::set的实现——DiemConfig把RegisteredCurrencies声明为friend见 DiemConfig.move并且set只允许具有ModifyConfigCapabilityRegisteredCurrencies的账户修改配置而该能力仅授予 Diem 根账户见 DiemConfig.move。因此非 Diem 根调用会在DiemConfig::set处被拒绝这一点也由AddCurrencyCodeAbortsIf规范中的DiemConfig::SetAbortsIf覆盖见 RegisteredCurrencies.move。实际调用方Diem::register_currencyadd_currency_code的真正业务入口是货币注册函数Diem::register_currencyCoinType见 Diem.move。该函数完成校验 Diem 根角色与CurrencyInfo地址约束校验0 scaling_factor MAX_SCALING_FACTOR在CurrencyInfo下发布CurrencyInfoCoinType含汇率、缩放因子、小数位、事件句柄等最后调用RegisteredCurrencies::add_currency_code(dr_account, currency_code)登记代码返回MintCapabilityCoinType与BurnCapabilityCoinType。由此可以推断出完整的“注册一个货币”的语义链条类型注册CurrencyInfo资源 代码登记RegisteredCurrencies清单是原子绑定在一起的代码被占用会导致整个注册中止对应规范RegisterCurrencyAbortsIf中的RegisteredCurrencies::AddCurrencyCodeAbortsIf见 Diem.move。另外add_currency_code的规范还保证了追加语义Vector::eq_push_back(get_currency_codes(), old(get_currency_codes()), currency_code)即新清单等于旧清单末尾追加新代码不改变已有元素的顺序见 RegisteredCurrencies.move。模块规范全局不变量与辅助函数初始化不变量模块级规范定义了最重要的全局不变量见 RegisteredCurrencies.moveinvariant [suspendable] DiemTimestamp::is_operating() DiemConfig::spec_is_publishedRegisteredCurrencies();含义只要链进入运行状态is_operatingRegisteredCurrencies配置必须已被发布。它把“genesis 必须初始化该配置”从约定上升为可被 Move Prover 验证的形式化约束防止任何 genesis 流程遗漏这一步。辅助函数规范中定义了仅供规范使用的辅助函数get_currency_codes()见 RegisteredCurrencies.movefun get_currency_codes(): vectorvectoru8 { DiemConfig::getRegisteredCurrencies().currency_codes }它封装了“读取当前货币代码清单”的规范访问路径被InitializeEnsures、AddCurrencyCodeAbortsIf、AddCurrencyCodeEnsures等多个 schema 复用。单元测试验证仓库在 RegisteredCurrencyTests.move 中提供了针对该模块的三组单元测试直接验证了本文所述的权限与去重语义#[test(dr DiemRoot, tc TreasuryCompliance, alice 0x2)] #[expected_failure(abort_code 1)] fun cannot_call_initialize_as_non_diem_root(dr: signer, tc: signer, alice: signer) { Genesis::setup(dr, tc); RegisteredCurrencies::initialize(alice); } #[test(dr DiemRoot, tc TreasuryCompliance)] #[expected_failure(abort_code 1)] fun cannot_call_initialize_outside_genesis(dr: signer, tc: signer) { Genesis::setup(dr, tc); RegisteredCurrencies::initialize(dr); } #[test(dr DiemRoot, tc TreasuryCompliance)] #[expected_failure(abort_code 7)] fun cannot_add_currency_whose_currency_code_has_already_been_taken(dr: signer, tc: signer) { Genesis::setup(dr, tc); RegisteredCurrencies::add_currency_code(dr, bXDX); }三个用例分别验证非 Diem 根调用initialize必须失败abort_code 1来自Roles::assert_diem_root的INVALID_ARGUMENT类别genesis 之后再次调用initialize必须失败abort_code 1来自DiemTimestamp::assert_genesis且此时配置已发布publish_new_config同样会中止重复添加已被占用的货币代码bXDX必须失败abort_code 7即Errors::invalid_argument(ECURRENCY_CODE_ALREADY_TAKEN)组合出的错误码反向证明了XDX在 genesis 流程中已被登记、且去重断言真实生效。这些测试同时也印证Genesis::setup完成的链创世流程会预先登记XDX等货币与 Diem.move 中“注册货币并写入 RegisteredCurrencies 清单”的设计闭环一致。配套参考与进一步阅读模块源码language/diem-framework/modules/RegisteredCurrencies.move本文对应的自动生成文档language/diem-framework/modules/doc/RegisteredCurrencies.md配置宿主模块publish_new_config/get/set的实现language/diem-framework/modules/DiemConfig.move货币注册主流程register_currency/register_SCS_currencylanguage/diem-framework/modules/Diem.move角色校验DiemRootlanguage/diem-framework/modules/Roles.move单元测试language/diem-framework/tests/RegisteredCurrencyTests.move链上模块总览language/diem-framework/modules/doc/overview.md如需在本地复现上述测试可进入language/diem-framework目录通过项目自带的move-unit-test工具链运行RegisteredCurrencyTests测试文件已按#[test]标注直接纳入框架的单元测试构建即可。【免费下载链接】diemDiem’s mission is to build a trusted and innovative financial network that empowers people and businesses around the world.项目地址: https://gitcode.com/gh_mirrors/di/diem创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考