starcoin 配置加载,检查,升级与测试
- 1 genesis 配置
- 2 启动流程中的配置生成与加载
- 2.1 gas fee 配置细节
- 2.1.1 genesis 初始化阶段
- 2.1.2 交易执行阶段
- 2.1 gas fee 配置细节
- 3 总结:starcoin 配置读取
- 4 升级原理
- 4.1 新版本需要兼容老区块数据
- 5 相关单元测试
- 5.1 genesis 配置一致性测试
- 5.2 gas 配置一致性测试
- 5.3 修改 gas 配置注意事项
genesis 配置
genesis 文件是第一个区块,一般被称为初创区块,高度为0。里面主要数据是整条链的区块配置(genesis config),比如 gas 费配置,stdlib version(framework 版本号),move 版本号等。不同网络有不同的配置策略:
网络 | 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 内置配置相同,相同则配置加载成功。
若本地文件不存在,则不同网络有不同的策略:
dev 和 test 网络会根据 genesis 内置配置生成新的 genesis 文件(在 gensis::build_genesis_block 中实现)。并且会 build 一个新的 transaction 执行交易(交易主要是 load framework 字节码,调用framework 的 genesisi initialize 函数),相当于重新开启一个新的链。
其它网络(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 配置
从配置初始化的流程可以看出,区块配置主要有两个地方可以读取:
core address 资源;即 0x1 地址资源 ,这个应该是比较标准的读取方法。在 genesis 初始化或者版本升级的时候会调用 framework 的 Genesis.move 的 initialize 函数进行设置(可能不同 stdlib version 版本调用不同的 initialize 函数)。
直接从 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 配置,那么需要保证上图红框的校验都能通过验证,即保证:
genesis 文件(genesis_config.json);
genesis_config.rs 中的配置;
framework 的 VMConfig.move( V11 版本及之前)和 GasSchedule.move (V12 版本及之后)。
以上三个地方的:
版本号;
字段key;
字段值。
以上三个维度信息一致。