use std::{collections, env, fs, io::Write, path};
use serde_derive::{Deserialize, Serialize};
use ckb_chain_spec as spec;
use ckb_resource as res;
use ckb_types::{packed, prelude::*, H256};
#[derive(Clone, Debug, Serialize, Deserialize)]
struct SystemCell {
path: String,
tx_hash: H256,
index: usize,
data_hash: H256,
type_hash: Option<H256>,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct DepGroupCell {
included_cells: Vec<String>,
tx_hash: H256,
index: usize,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
struct SpecHashes {
genesis: H256,
cellbase: H256,
system_cells: Vec<SystemCell>,
dep_groups: Vec<DepGroupCell>,
}
fn create_template_context(spec: &str) -> res::TemplateContext {
res::TemplateContext::new(
spec,
vec![
("spec_source", "bundled"),
("rpc_port", res::DEFAULT_RPC_PORT),
("p2p_port", res::DEFAULT_P2P_PORT),
("log_to_file", "false"),
("log_to_stdout", "false"),
("block_assembler", ""),
],
)
}
fn load_hashes_from_chain_spec(mut chain_spec: spec::ChainSpec) -> SpecHashes {
let spec_name = &chain_spec.name;
let hash_option = chain_spec.genesis.hash.take();
let consensus = chain_spec
.build_consensus()
.unwrap_or_else(|_| panic!("failed to build consensus for {}", spec_name));
if let Some(hash) = hash_option {
let genesis_hash: H256 = consensus.genesis_hash().unpack();
if hash != genesis_hash {
panic!(
"Genesis hash unmatched in {} chainspec config file: in file {:#x}, actual {:#x}",
spec_name, hash, genesis_hash
);
}
}
let block = consensus.genesis_block();
let cellbase = &block.transactions()[0];
let dep_group_tx = &block.transactions()[1];
let cells_hashes = chain_spec
.genesis
.system_cells
.iter()
.map(|system_cell| &system_cell.file)
.zip(
cellbase
.outputs()
.into_iter()
.zip(cellbase.outputs_data().into_iter())
.skip(1),
)
.enumerate()
.map(|(index_minus_one, (resource, (output, data)))| {
let data_hash: H256 = packed::CellOutput::calc_data_hash(&data.raw_data()).unpack();
let type_hash: Option<H256> = output
.type_()
.to_opt()
.map(|script| script.calc_script_hash().unpack());
SystemCell {
path: resource.to_string(),
tx_hash: cellbase.hash().unpack(),
index: index_minus_one + 1,
data_hash,
type_hash,
}
})
.collect();
let dep_groups = chain_spec
.genesis
.dep_groups
.iter()
.enumerate()
.map(|(index, dep_group)| DepGroupCell {
included_cells: dep_group
.files
.iter()
.map(|res| res.to_string())
.collect::<Vec<_>>(),
tx_hash: dep_group_tx.hash().unpack(),
index,
})
.collect::<Vec<_>>();
SpecHashes {
genesis: consensus.genesis_hash().unpack(),
cellbase: cellbase.hash().unpack(),
system_cells: cells_hashes,
dep_groups,
}
}
fn main() {
let out_dir = env::var("OUT_DIR").expect("environment variable OUT_DIR should be existed");
let hashes_filename = "hashes.toml";
let hashes_file = path::Path::new(&out_dir).join(hashes_filename);
let mut hashes_fd = fs::File::create(&hashes_file)
.unwrap_or_else(|_| panic!("failed to create file {:?}", hashes_file));
let mut first = true;
for (name, spec_name) in &[
("mainnet", "mainnet"),
("testnet", "testnet"),
("staging", "staging"),
("develop", "dev"),
] {
if first {
first = false;
} else {
hashes_fd
.write_all(b"\n")
.unwrap_or_else(|_| panic!("failed to write file {:?}", hashes_file));
}
let dir = path::Path::new(&out_dir).join(name);
fs::create_dir_all(&dir).unwrap_or_else(|_| panic!("failed to create directory {:?}", dir));
res::Resource::bundled_ckb_config()
.export(&create_template_context(spec_name), dir)
.unwrap_or_else(|_| panic!("failed to export ckb config for {}", name));
let bundled = res::Resource::bundled(format!("specs/{}.toml", spec_name));
let chain_spec = spec::ChainSpec::load_from(&bundled)
.unwrap_or_else(|_| panic!("failed to load ckb chain spec for {}", name));
let spec_name = chain_spec.name.clone();
let spec_hashes = load_hashes_from_chain_spec(chain_spec);
let mut spec_hashes_map = collections::BTreeMap::default();
spec_hashes_map.insert(spec_name, spec_hashes);
let spec_hashes_string = toml::to_string(&spec_hashes_map).unwrap();
hashes_fd
.write_all(spec_hashes_string.as_bytes())
.unwrap_or_else(|_| panic!("failed to write file {:?}", hashes_file));
}
}