// Copyright (C) 2019-2020 Boyu Yang // // Licensed under the Apache License, Version 2.0 or the MIT license // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. 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, } #[derive(Clone, Debug, Serialize, Deserialize)] struct DepGroupCell { included_cells: Vec, tx_hash: H256, index: usize, } #[derive(Clone, Debug, Serialize, Deserialize)] struct SpecHashes { genesis: H256, cellbase: H256, system_cells: Vec, dep_groups: Vec, } 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 = 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::>(), tx_hash: dep_group_tx.hash().unpack(), index, }) .collect::>(); 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)); } }