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 详见 Starcoin-Framework 升级

相关单元测试

以上说明升级过程中,如果出现老代码被修改导致不兼容的情况则会出现硬分叉的风险,例如,如果我们发布 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. 字段值。

以上三个维度信息一致。