starcoin 配置加载,检查,升级与测试

genesis 配置

genesis 文件是第一个区块,一般被称为初创区块,高度为0。里面主要数据是整条链的区块配置(genesis config),比如 gas 费配置,stdlib version(framework 版本号),move 版本号等。不同网络有不同的配置策略:

网络

genesis config 配置策略

网络

genesis config 配置策略

test

一般情况下使用最新的 genesis config

dev

halley

定时重置

proxima

重大升级重置

barnard

固化,不会被修改,stdlib version 为 1,但后续区块会有升级交易写入

main

固化,不会被修改,stdlib version 为 4,但后续区块会有升级交易写入

启动流程中的配置生成与加载

starcoin 启动的时候会生成或加载 genesis config 配置。不同的网络有不同的加载策略,其启动时,加载配置流程如下:

其主要大致的流程为:首先从 cmd 模块(starcoin 的 main 函数在此)开始启动 starcoin,然后开始初始化一个网络节点(node 模块),在 NodeService::init_system 中进行一系列的初始化,如初始化 log,storage, state,当然也就包括加载配置 genesis config。

genesis config 的初始化是在 genesis 模块中完成的,其依赖 config 模块,starcoin 中的各个网络的 genesis config 配置均写死在 genesis_config.rs 中,其变量名分别为: G_DEV_CONFIG,G_TEST_CONFIG,G_HALLEY_CONFIG,G_PROXIMA_CONFIG,G_BARNARD_CONFIG 和 G_MAIN_CONFIG,以下统称 genesis 内置配置。

如上图所示,starcoin 首先会尝试加载本地 genesis(在 genesis::load_and_check_genesis 中实现),若加载成功,则会判断所加载的 genesis 文件是否与 genesis 内置配置相同,相同则配置加载成功。

若本地文件不存在,则不同网络有不同的策略:

  1. dev 和 test 网络会根据 genesis 内置配置生成新的 genesis 文件(在 gensis::build_genesis_block 中实现)。并且会 build 一个新的 transaction 执行交易(交易主要是 load framework 字节码,调用framework 的 genesisi initialize 函数),相当于重新开启一个新的链。

  2. 其它网络(halley,proxima,barnard 和 main)则直接根据 genesis 的内置配置生成 genesis 文件(在 genesis::load 中实现)。

gas fee 配置细节

genesis config 中有一个字段为 vm_config,类型为 VMConfig(其中包含的字段为gas_schedule,类型为 CostTable,而 CostTable 包含三个字段:instruction_table,native_table 和 gas_constants,存放了不同的 gas fee数值,注意从 stdlib version 12 版本开始,配置策略有所不同,下面会详述。),不同的阶段,关于 gas fee 的加载,有不同的逻辑:

genesis 初始化阶段

其主要实现是在 build_stdlib_package 函数中,这个函数有两个地方会调用进来,一个是 starcoin 启动 dev 和 test 网络的时候;一个是我们使用 starcoin-genesis 工具生成新的 genesis 文件的时候,此时会生成新的 genesis 文件,同时将配置写入 core address 中。

 

交易执行阶段

其主要实现是在,主要实现在 StarcoinVM::load_configs,流程如下:

 

总结:starcoin 配置读取

gas 配置

从配置初始化的流程可以看出,区块配置主要有两个地方可以读取:

  1. core address 资源;即 0x1 地址资源 ,这个应该是比较标准的读取方法。在 genesis 初始化或者版本升级的时候会调用 framework 的 Genesis.move 的 initialize 函数进行设置(可能不同 stdlib version 版本调用不同的 initialize 函数)。

  2. 直接从 move 的字节 mv 文件读取;例如读取 gas 配置的时候,可以从 VMConfig.rs 或者 GasSchedule.rs 文件相关函数读取。
    经过多个版本的迭代, gas 配置已经经历多个版本,在不同的版本中,有不同的 gas 配置代码,详见:

阶段

版本

代码

阶段

版本

代码

genesisi 初始化阶段

根据网络 加载genesis文件,若genesis 文件不存在,根据genesis_config.rs 的内置配置生成新的 genesis。

交易阶段

V11 版本和之前

代码在StarcoinVM.load_configs_impl 调用 framework 的 VMConfig.move

V12 及之后

代码在StarcoinVM.load_configs_impl 调用 framework 的 GasSchedule.move

版本配置

配置

读取

配置

读取

move version

读取 core address 版本配置,MoveLanguageVersion::fetch_config

stdlib version

读取 core address 版本配置,其代码逻辑是在 Version::fetch_config (读取版本号)

升级原理

新版本需要兼容老区块数据

区块链的一大特性是不可修改性,越老的区块越稳固,因此,区块链代码在升级的时候需要兼容新老区块,随着升级次数增多,区块版本也就越多,因此,每一次升级都要保证新的代码总是能兼容老的区块。

例如,starcoin 在 import 全量区块的时候,需要验证区块中的所有交易,这就要保证不同版本产出的交易执行不同版本的交易代码,否则会校验不通过造成硬分叉。

 

如上图,区块版本从 V1 增长到 V11(随 framework 的stdlib version),因此,历史上新的 starcoin 都需要能处理之前的区块版本,如starocin 1.13.4 可以处理从 V1 到 V11 的区块。

现在 starcoin 需要升级到 1.13.5,其中一起打包的 framework 升级到 V12,那么新版本的 starcoin 也需要能处理 V1 到 V 11 版本的区块。这是第一步,如上图的红色方框。这一步主要是二进制文件的更新。虽然 framework V12 版本被打包发布了,但链上的区块还是 V11,要想出线 V12 版本的区块,需要进行 DAO 流程,然后将升级交易写入区块中,后续的区块才会变成 V12。

因此第二步,我们发起 DAO 流程,通过后会将升级交易打包到新的区块上,即执行升级合约,内容是 core address 的 stdlib_version 版本号升级为 12,此后新版本的 starcoin(1.13.5) 就会读到新的版本号数据,由于 starcoin 已经打包好对应的 framework V12 版本包,可以处理 V12 版本的区块了。通过 DAO 流程写入 core address 详见

相关单元测试

以上说明升级过程中,如果出现老代码被修改导致不兼容的情况则会出现硬分叉的风险,例如,如果我们发布 1.13.5 的时候无法处理 V11 的区块交易,那么 V11 的区块将不会被新版本的 starcoin 承认,从而会在 V10 最后一个区块之后出现硬分叉。

因此需要有足够的测试保证测试覆盖。

genesis 配置一致性测试

是指 genesis 文件(同目录下还有一个叫 genesis_config.json,前者是二进制文件,后者是其 反序列化后保存为 json 格式的文件,理论上这两个文件是相同的)和 genesis_config.rs 配置一致性测试。

网络

测试方法

网络

测试方法

dev 和 test

因为都是使用 lastest 版本,因此若报 genesis 配置不一致的错误,则删去老的 genesis 文件即可。单元测试也会绕过这两个网络类型。

halley 和 proxima

一般和 dev ,test 一样,使用 lastest 版本,但重大升级时,会进行降级模拟升级流程。因此测试方法视情况而定。

单元测试会用 config/example/proxima/genesis_config.json 来进行测试,保证其和 genesis_config.rs 的 G_PROXIMA_CONFIG 一致。(单元测试在 test_generate_and_load() 和 test_custom_chain_genesis())

barnard 和 main

需要保证新代码和老的 genesis 文件一致,这个保证是在代码 BaseConfig::load_genesis_config_by_opt 中( 代码 ensure! 那一行保证)。代码会检查 genesis 文件与内置配置是否一致。这个检查在 starcoin 启动和 单元测试中都有做(单元测试在 test_generate_and_load() 和 test_custom_chain_genesis())

 

gas 配置一致性测试

如前述,gas 配置在两个地方有写入,一个是升级的时候(如从 V1 一直升到 V12),每一次版本升级我们将新的配置写入到 core address中 (每次 load 新module 的时候会调用其 init_script 函数,即调用 Genesis.move 的initialize_vX 函数,不同版本调用不同的函数,写入的数据来来自 genesis_config.rs),一个是 VMConfig,.move ( V11 版本及之前),一个是 GasSchedule.move (V12 版本及之后)。因此需要保证这两个地方的数据是一致的。单元测试 test_generate_and_load() 和 test_custom_chain_genesis() 中会调用 test_gas_schedule_in_genesis(),其中会读出 core address 的 gas 配置和 move 代码中的 gas 配置,然后它们是否一致。

 

修改 gas 配置注意事项

由上讨论知,如果我们修改 gas 配置,那么需要保证上图红框的校验都能通过验证,即保证:

  1. genesis 文件(genesis_config.json);

  2. genesis_config.rs 中的配置;

  3. framework 的 VMConfig.move( V11 版本及之前)和 GasSchedule.move (V12 版本及之后)。

以上三个地方的:

  1. 版本号;

  2. 字段key;

  3. 字段值。

以上三个维度信息一致。