ckb_chain_spec/
lib.rs

1//! # The Chain Specification
2//!
3//! By default, when simply running CKB, CKB will connect to the official public Nervos network.
4//!
5//! In order to run a chain different to the official public one,
6//! with a config file specifying `spec = { file = "<the-path-of-spec-file>" }` under `[chain]`.
7//!
8
9// Because the limitation of toml library,
10// we must put nested config struct in the tail to make it serializable,
11// details https://docs.rs/toml/0.5.0/toml/ser/index.html
12
13use crate::consensus::{
14    Consensus, ConsensusBuilder, SATOSHI_CELL_OCCUPIED_RATIO, SATOSHI_PUBKEY_HASH,
15    TESTNET_ACTIVATION_THRESHOLD, TYPE_ID_CODE_HASH, build_genesis_dao_data,
16    build_genesis_epoch_ext,
17};
18use crate::versionbits::{ActiveMode, Deployment, DeploymentPos};
19use ckb_constant::hardfork::{mainnet, testnet};
20use ckb_crypto::secp::Privkey;
21use ckb_hash::{blake2b_256, new_blake2b};
22use ckb_jsonrpc_types::Script;
23use ckb_pow::{Pow, PowEngine};
24use ckb_resource::{
25    CODE_HASH_DAO, CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL,
26    CODE_HASH_SECP256K1_BLAKE160_SIGHASH_ALL, CODE_HASH_SECP256K1_DATA, Resource,
27};
28use ckb_types::{
29    H160, H256, U128,
30    bytes::Bytes,
31    core::{
32        BlockBuilder, BlockNumber, BlockView, Capacity, Cycle, EpochNumber,
33        EpochNumberWithFraction, Ratio, ScriptHashType, TransactionBuilder, TransactionView,
34        capacity_bytes, hardfork::HardForks,
35    },
36    h256, packed,
37    prelude::*,
38};
39use serde::{Deserialize, Serialize};
40use std::collections::HashMap;
41use std::error::Error;
42use std::fmt;
43use std::sync::Arc;
44
45pub use error::SpecError;
46pub use hardfork::HardForkConfig;
47
48pub mod consensus;
49mod error;
50mod hardfork;
51pub mod versionbits;
52
53#[cfg(test)]
54mod tests;
55
56// Just a random secp256k1 secret key for dep group input cell's lock
57const SPECIAL_CELL_PRIVKEY: H256 =
58    h256!("0xd0c5c1e2d5af8b6ced3c0800937f996c1fa38c29186cade0cd8b5a73c97aaca3");
59
60/// The output index of SECP256K1/blake160 script in the genesis no.0 transaction
61pub const OUTPUT_INDEX_SECP256K1_BLAKE160_SIGHASH_ALL: u64 = 1;
62/// The output index of DAO script in the genesis no.0 transaction
63pub const OUTPUT_INDEX_DAO: u64 = 2;
64/// The output data index of SECP256K1 in the genesis no.0 transaction
65pub const OUTPUT_INDEX_SECP256K1_DATA: u64 = 3;
66/// The output index of SECP256K1/multisig script in the genesis no.0 transaction
67pub const OUTPUT_INDEX_SECP256K1_BLAKE160_MULTISIG_ALL: u64 = 4;
68
69/// The CKB block chain specification
70#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
71#[serde(deny_unknown_fields)]
72pub struct ChainSpec {
73    /// The spec name, also used identify network
74    pub name: String,
75    /// The genesis block information
76    pub genesis: Genesis,
77    /// The block chain parameters
78    #[serde(default)]
79    pub params: Params,
80    /// The block chain pow
81    pub pow: Pow,
82    #[serde(skip)]
83    /// Hash of blake2b_256 spec content bytes, used for check consistency between database and config
84    pub hash: packed::Byte32,
85}
86
87/// The default_params mod defines the default parameters for CKB Mainnet
88pub mod default_params {
89    use crate::consensus::{
90        CELLBASE_MATURITY, DEFAULT_EPOCH_DURATION_TARGET, DEFAULT_ORPHAN_RATE_TARGET,
91        DEFAULT_PRIMARY_EPOCH_REWARD_HALVING_INTERVAL, DEFAULT_SECONDARY_EPOCH_REWARD,
92        GENESIS_EPOCH_LENGTH, INITIAL_PRIMARY_EPOCH_REWARD, MAX_BLOCK_BYTES, MAX_BLOCK_CYCLES,
93        MAX_BLOCK_PROPOSALS_LIMIT, STARTING_BLOCK_LIMITING_DAO_WITHDRAWING_LOCK,
94    };
95    use ckb_types::core::{Capacity, Cycle, EpochNumber};
96
97    /// The default initial_primary_epoch_reward
98    ///
99    /// Apply to [`initial_primary_epoch_reward`](../consensus/struct.Consensus.html#structfield.initial_primary_epoch_reward)
100    pub fn initial_primary_epoch_reward() -> Capacity {
101        INITIAL_PRIMARY_EPOCH_REWARD
102    }
103
104    /// The default secondary_epoch_reward
105    ///
106    /// Apply to [`secondary_epoch_reward`](../consensus/struct.Consensus.html#structfield.secondary_epoch_reward)
107    pub fn secondary_epoch_reward() -> Capacity {
108        DEFAULT_SECONDARY_EPOCH_REWARD
109    }
110
111    /// The default max_block_cycles
112    ///
113    /// Apply to [`max_block_cycles`](../consensus/struct.Consensus.html#structfield.max_block_cycles)
114    pub fn max_block_cycles() -> Cycle {
115        MAX_BLOCK_CYCLES
116    }
117
118    /// The default max_block_bytes
119    ///
120    /// Apply to [`max_block_bytes`](../consensus/struct.Consensus.html#structfield.max_block_bytes)
121    pub fn max_block_bytes() -> u64 {
122        MAX_BLOCK_BYTES
123    }
124
125    /// The default cellbase_maturity
126    ///
127    /// Apply to [`cellbase_maturity`](../consensus/struct.Consensus.html#structfield.cellbase_maturity)
128    pub fn cellbase_maturity() -> u64 {
129        CELLBASE_MATURITY.full_value()
130    }
131
132    /// The default primary_epoch_reward_halving_interval
133    ///
134    /// Apply to [`primary_epoch_reward_halving_interval`](../consensus/struct.Consensus.html#structfield.primary_epoch_reward_halving_interval)
135    pub fn primary_epoch_reward_halving_interval() -> EpochNumber {
136        DEFAULT_PRIMARY_EPOCH_REWARD_HALVING_INTERVAL
137    }
138
139    /// The default epoch_duration
140    ///
141    /// Apply to [`epoch_duration_target`](../consensus/struct.Consensus.html#structfield.epoch_duration_target)
142    pub fn epoch_duration_target() -> u64 {
143        DEFAULT_EPOCH_DURATION_TARGET
144    }
145
146    /// The default genesis_epoch_length
147    ///
148    /// Apply to [`genesis_epoch_length`](../consensus/struct.Consensus.html#structfield.genesis_epoch_length)
149    pub fn genesis_epoch_length() -> u64 {
150        GENESIS_EPOCH_LENGTH
151    }
152
153    /// The default max_block_proposals_limit
154    ///
155    /// Apply to [`max_block_proposals_limit`](../consensus/struct.Consensus.html#structfield.max_block_proposals_limit)
156    pub fn max_block_proposals_limit() -> u64 {
157        MAX_BLOCK_PROPOSALS_LIMIT
158    }
159
160    /// The default permanent_difficulty_in_dummy
161    ///
162    /// Apply to [`permanent_difficulty_in_dummy`](../consensus/struct.Consensus.html#structfield.permanent_difficulty_in_dummy)
163    pub fn permanent_difficulty_in_dummy() -> bool {
164        false
165    }
166
167    /// The default orphan_rate_target
168    ///
169    /// Apply to [`orphan_rate_target`](../consensus/struct.Consensus.html#structfield.orphan_rate_target)
170    pub fn orphan_rate_target() -> (u32, u32) {
171        DEFAULT_ORPHAN_RATE_TARGET
172    }
173
174    /// The default starting_block_limiting_dao_withdrawing_lock
175    ///
176    /// Apply to [`starting_block_limiting_dao_withdrawing_lock`](../consensus/struct.Consensus.html#structfield.starting_block_limiting_dao_withdrawing_lock)
177    pub fn starting_block_limiting_dao_withdrawing_lock() -> u64 {
178        STARTING_BLOCK_LIMITING_DAO_WITHDRAWING_LOCK
179    }
180}
181
182/// Parameters for CKB block chain
183#[derive(Default, Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
184#[serde(deny_unknown_fields)]
185pub struct Params {
186    /// The initial_primary_epoch_reward
187    ///
188    /// See [`initial_primary_epoch_reward`](consensus/struct.Consensus.html#structfield.initial_primary_epoch_reward)
189    #[serde(skip_serializing_if = "Option::is_none")]
190    pub initial_primary_epoch_reward: Option<Capacity>,
191    /// The secondary_epoch_reward
192    ///
193    /// See [`secondary_epoch_reward`](consensus/struct.Consensus.html#structfield.secondary_epoch_reward)
194    #[serde(skip_serializing_if = "Option::is_none")]
195    pub secondary_epoch_reward: Option<Capacity>,
196    /// The max_block_cycles
197    ///
198    /// See [`max_block_cycles`](consensus/struct.Consensus.html#structfield.max_block_cycles)
199    #[serde(skip_serializing_if = "Option::is_none")]
200    pub max_block_cycles: Option<Cycle>,
201    /// The max_block_bytes
202    ///
203    /// See [`max_block_bytes`](consensus/struct.Consensus.html#structfield.max_block_bytes)
204    #[serde(skip_serializing_if = "Option::is_none")]
205    pub max_block_bytes: Option<u64>,
206    /// The cellbase_maturity
207    ///
208    /// See [`cellbase_maturity`](consensus/struct.Consensus.html#structfield.cellbase_maturity)
209    #[serde(skip_serializing_if = "Option::is_none")]
210    pub cellbase_maturity: Option<u64>,
211    /// The primary_epoch_reward_halving_interval
212    ///
213    /// See [`primary_epoch_reward_halving_interval`](consensus/struct.Consensus.html#structfield.primary_epoch_reward_halving_interval)
214    #[serde(skip_serializing_if = "Option::is_none")]
215    pub primary_epoch_reward_halving_interval: Option<EpochNumber>,
216    /// The epoch_duration_target
217    ///
218    /// See [`epoch_duration_target`](consensus/struct.Consensus.html#structfield.epoch_duration_target)
219    #[serde(skip_serializing_if = "Option::is_none")]
220    pub epoch_duration_target: Option<u64>,
221    /// The genesis_epoch_length
222    ///
223    /// See [`genesis_epoch_length`](consensus/struct.Consensus.html#structfield.genesis_epoch_length)
224    #[serde(skip_serializing_if = "Option::is_none")]
225    pub genesis_epoch_length: Option<BlockNumber>,
226    /// The permanent_difficulty_in_dummy
227    ///
228    /// See [`permanent_difficulty_in_dummy`](consensus/struct.Consensus.html#structfield.permanent_difficulty_in_dummy)
229    #[serde(skip_serializing_if = "Option::is_none")]
230    pub permanent_difficulty_in_dummy: Option<bool>,
231    /// The max_block_proposals_limit
232    ///
233    /// See [`max_block_proposals_limit`](consensus/struct.Consensus.html#structfield.max_block_proposals_limit)
234    #[serde(skip_serializing_if = "Option::is_none")]
235    pub max_block_proposals_limit: Option<u64>,
236    /// The orphan_rate_target
237    ///
238    /// See [`orphan_rate_target`](consensus/struct.Consensus.html#structfield.orphan_rate_target)
239    #[serde(skip_serializing_if = "Option::is_none")]
240    pub orphan_rate_target: Option<(u32, u32)>,
241    /// The starting_block_limiting_dao_withdrawing_lock.
242    ///
243    /// See [`starting_block_limiting_dao_withdrawing_lock`](consensus/struct.Consensus.html#structfield.starting_block_limiting_dao_withdrawing_lock)
244    #[serde(skip_serializing_if = "Option::is_none")]
245    pub starting_block_limiting_dao_withdrawing_lock: Option<u64>,
246    /// The parameters for hard fork features.
247    ///
248    /// See [`hardfork_switch`](consensus/struct.Consensus.html#structfield.hardfork_switch)
249    #[serde(skip_serializing_if = "Option::is_none")]
250    pub hardfork: Option<HardForkConfig>,
251}
252
253impl Params {
254    /// Return the `initial_primary_epoch_reward`, otherwise if None, returns the default value
255    pub fn initial_primary_epoch_reward(&self) -> Capacity {
256        self.initial_primary_epoch_reward
257            .unwrap_or_else(default_params::initial_primary_epoch_reward)
258    }
259
260    /// Return the `secondary_epoch_reward`, otherwise if None, returns the default value
261    pub fn secondary_epoch_reward(&self) -> Capacity {
262        self.secondary_epoch_reward
263            .unwrap_or_else(default_params::secondary_epoch_reward)
264    }
265
266    /// Return the `max_block_cycles`, otherwise if None, returns the default value
267    pub fn max_block_cycles(&self) -> Cycle {
268        self.max_block_cycles
269            .unwrap_or_else(default_params::max_block_cycles)
270    }
271
272    /// Return the `max_block_bytes`, otherwise if None, returns the default value
273    pub fn max_block_bytes(&self) -> u64 {
274        self.max_block_bytes
275            .unwrap_or_else(default_params::max_block_bytes)
276    }
277
278    /// Return the `cellbase_maturity`, otherwise if None, returns the default value
279    pub fn cellbase_maturity(&self) -> u64 {
280        self.cellbase_maturity
281            .unwrap_or_else(default_params::cellbase_maturity)
282    }
283
284    /// Return the `primary_epoch_reward_halving_interval`, otherwise if None, returns the default value
285    pub fn primary_epoch_reward_halving_interval(&self) -> EpochNumber {
286        self.primary_epoch_reward_halving_interval
287            .unwrap_or_else(default_params::primary_epoch_reward_halving_interval)
288    }
289
290    /// Return the `permanent_difficulty_in_dummy`, otherwise if None, returns the default value
291    pub fn permanent_difficulty_in_dummy(&self) -> bool {
292        self.permanent_difficulty_in_dummy
293            .unwrap_or_else(default_params::permanent_difficulty_in_dummy)
294    }
295
296    /// Return the `epoch_duration_target`, otherwise if None, returns the default value
297    pub fn epoch_duration_target(&self) -> u64 {
298        self.epoch_duration_target
299            .unwrap_or_else(default_params::epoch_duration_target)
300    }
301
302    /// Return the `genesis_epoch_length`, otherwise if None, returns the default value
303    pub fn genesis_epoch_length(&self) -> BlockNumber {
304        self.genesis_epoch_length
305            .unwrap_or_else(default_params::genesis_epoch_length)
306    }
307
308    /// Return the `max_block_proposals_limit`, otherwise if None, returns the default value
309    pub fn max_block_proposals_limit(&self) -> BlockNumber {
310        self.max_block_proposals_limit
311            .unwrap_or_else(default_params::max_block_proposals_limit)
312    }
313
314    /// Return the `orphan_rate_target`, otherwise if None, returns the default value
315    pub fn orphan_rate_target(&self) -> (u32, u32) {
316        self.orphan_rate_target
317            .unwrap_or_else(default_params::orphan_rate_target)
318    }
319
320    /// Return the `starting_block_limiting_dao_withdrawing_lock`, otherwise if None, returns the default value
321    pub fn starting_block_limiting_dao_withdrawing_lock(&self) -> u64 {
322        self.starting_block_limiting_dao_withdrawing_lock
323            .unwrap_or_else(default_params::starting_block_limiting_dao_withdrawing_lock)
324    }
325}
326
327/// The genesis information
328/// Load from config file.
329#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
330#[serde(deny_unknown_fields)]
331pub struct Genesis {
332    /// The genesis block version
333    pub version: u32,
334    /// The genesis block parent_hash
335    pub parent_hash: H256,
336    /// The genesis block timestamp
337    pub timestamp: u64,
338    /// The genesis block compact_target
339    pub compact_target: u32,
340    /// The genesis block uncles_hash
341    pub uncles_hash: H256,
342    /// The genesis block hash
343    ///
344    /// If hash is provided, it will be used to check whether match with actual calculated hash
345    pub hash: Option<H256>,
346    /// The genesis block nonce
347    pub nonce: U128,
348    /// The genesis block issued_cells
349    ///
350    /// Initial token supply
351    pub issued_cells: Vec<IssuedCell>,
352    /// The genesis cell
353    ///
354    /// The genesis cell contains a message for identity
355    pub genesis_cell: GenesisCell,
356    /// The system cells
357    ///
358    /// The initial system cells, such SECP256K1/blake160, DAO.
359    pub system_cells: Vec<SystemCell>,
360    /// The system cells' lock
361    pub system_cells_lock: Script,
362    /// For block 1~11, the reward target is genesis block.
363    /// Genesis block must have the lock serialized in the cellbase witness, which is set to `bootstrap_lock`.
364    pub bootstrap_lock: Script,
365    /// The genesis dep_groups file resource
366    ///
367    /// see detail [dep-group](https://github.com/nervosnetwork/rfcs/blob/f639fa8b30b5568b895449b7ab3ef4ad40ca077a/rfcs/0022-transaction-structure/0022-transaction-structure.md#dep-group)
368    pub dep_groups: Vec<DepGroupResource>,
369    /// The burned 25% of Nervos CKBytes in genesis block
370    #[serde(default)]
371    pub satoshi_gift: SatoshiGift,
372}
373
374/// The system cell information
375#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
376#[serde(deny_unknown_fields)]
377pub struct SystemCell {
378    // NOTE: must put `create_type_id` before `file` otherwise this struct can not serialize
379    /// whether crate type script
380    pub create_type_id: bool,
381    /// Overwrite the cell capacity. Set to None to use the minimal capacity.
382    pub capacity: Option<u64>,
383    /// The file resource
384    pub file: Resource,
385}
386
387/// The genesis cell information
388#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
389#[serde(deny_unknown_fields)]
390pub struct GenesisCell {
391    /// The genesis cell message
392    pub message: String,
393    /// The genesis cell lock
394    pub lock: Script,
395}
396
397/// Initial token supply cell
398#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
399#[serde(deny_unknown_fields)]
400pub struct IssuedCell {
401    /// The cell capacity
402    pub capacity: Capacity,
403    /// The cell lock
404    pub lock: Script,
405}
406
407/// The genesis dep_group file resources
408#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
409#[serde(deny_unknown_fields)]
410pub struct DepGroupResource {
411    /// The dep_group name
412    pub name: String,
413    /// The genesis dep_group files
414    pub files: Vec<Resource>,
415}
416
417/// The burned 25% of Nervos CKBytes in genesis block
418#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
419#[serde(deny_unknown_fields)]
420pub struct SatoshiGift {
421    /// The lock pubkey hash
422    pub satoshi_pubkey_hash: H160,
423    /// The burned cell virtual occupied, design for NervosDAO
424    pub satoshi_cell_occupied_ratio: Ratio,
425}
426
427impl Default for SatoshiGift {
428    fn default() -> Self {
429        SatoshiGift {
430            satoshi_pubkey_hash: SATOSHI_PUBKEY_HASH,
431            satoshi_cell_occupied_ratio: SATOSHI_CELL_OCCUPIED_RATIO,
432        }
433    }
434}
435
436#[derive(Debug)]
437pub(crate) enum SpecLoadError {
438    FileNotFound,
439    GenesisMismatch { expect: H256, actual: H256 },
440}
441
442impl SpecLoadError {
443    fn file_not_found() -> Box<Self> {
444        Box::new(SpecLoadError::FileNotFound)
445    }
446
447    fn genesis_mismatch(expect: H256, actual: H256) -> Box<Self> {
448        Box::new(SpecLoadError::GenesisMismatch { expect, actual })
449    }
450}
451
452impl Error for SpecLoadError {}
453
454impl fmt::Display for SpecLoadError {
455    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
456        match self {
457            SpecLoadError::FileNotFound => write!(f, "ChainSpec: file not found"),
458            SpecLoadError::GenesisMismatch { expect, actual } => write!(
459                f,
460                "ChainSpec: genesis hash mismatch, expect {expect:#x}, actual {actual:#x}"
461            ),
462        }
463    }
464}
465
466impl ChainSpec {
467    /// New ChainSpec instance from load spec file resource
468    pub fn load_from(resource: &Resource) -> Result<ChainSpec, Box<dyn Error>> {
469        if !resource.exists() {
470            return Err(SpecLoadError::file_not_found());
471        }
472        let config_bytes = resource.get()?;
473
474        let mut spec: ChainSpec = toml::from_slice(&config_bytes)?;
475        if let Some(parent) = resource.parent() {
476            spec.genesis
477                .system_cells
478                .iter_mut()
479                .for_each(|system_cell| system_cell.file.absolutize(parent));
480            spec.genesis
481                .dep_groups
482                .iter_mut()
483                .for_each(|dep_group_resource| {
484                    dep_group_resource
485                        .files
486                        .iter_mut()
487                        .for_each(|resource| resource.absolutize(parent))
488                });
489        }
490        // leverage serialize for sanitizing
491        spec.hash = packed::Byte32::new(blake2b_256(toml::to_vec(&spec)?));
492
493        Ok(spec)
494    }
495
496    /// The ChainSpec specified pow engine
497    pub fn pow_engine(&self) -> Arc<dyn PowEngine> {
498        self.pow.engine()
499    }
500
501    fn verify_genesis_hash(&self, genesis: &BlockView) -> Result<(), Box<dyn Error>> {
502        if let Some(ref expect) = self.genesis.hash {
503            let actual: H256 = genesis.hash().into();
504            if &actual != expect {
505                return Err(SpecLoadError::genesis_mismatch(expect.clone(), actual));
506            }
507        }
508        Ok(())
509    }
510
511    /// Completes all parameters for hard fork features and creates a hard fork switch.
512    ///
513    /// Verify the parameters for mainnet and testnet, because all start epoch numbers
514    /// for mainnet and testnet are fixed.
515    fn build_hardfork_switch(&self) -> Result<HardForks, Box<dyn Error>> {
516        let config = self.params.hardfork.as_ref().cloned().unwrap_or_default();
517        match self.name.as_str() {
518            mainnet::CHAIN_SPEC_NAME => config.complete_mainnet(),
519            testnet::CHAIN_SPEC_NAME => config.complete_testnet(),
520            _ => config.complete_with_dev_default(),
521        }
522        .map_err(Into::into)
523    }
524
525    fn softfork_deployments(&self) -> Option<HashMap<DeploymentPos, Deployment>> {
526        match self.name.as_str() {
527            mainnet::CHAIN_SPEC_NAME => {
528                let deployments = HashMap::new();
529                Some(deployments)
530            }
531            testnet::CHAIN_SPEC_NAME => {
532                let deployments = HashMap::new();
533                Some(deployments)
534            }
535            _ => {
536                let mut deployments = HashMap::new();
537                let light_client = Deployment {
538                    bit: 1,
539                    start: 0,
540                    timeout: 0,
541                    min_activation_epoch: 0,
542                    period: 10,
543                    active_mode: ActiveMode::Always,
544                    threshold: TESTNET_ACTIVATION_THRESHOLD,
545                };
546                deployments.insert(DeploymentPos::LightClient, light_client);
547                Some(deployments)
548            }
549        }
550    }
551
552    /// Build consensus instance
553    ///
554    /// [Consensus](consensus/struct.Consensus.html)
555    pub fn build_consensus(&self) -> Result<Consensus, Box<dyn Error>> {
556        let hardfork_switch = self.build_hardfork_switch()?;
557        let genesis_epoch_ext = build_genesis_epoch_ext(
558            self.params.initial_primary_epoch_reward(),
559            self.genesis.compact_target,
560            self.params.genesis_epoch_length(),
561            self.params.epoch_duration_target(),
562            self.params.orphan_rate_target(),
563        );
564        let genesis_block = self.build_genesis()?;
565        self.verify_genesis_hash(&genesis_block)?;
566
567        let mut builder = ConsensusBuilder::new(genesis_block, genesis_epoch_ext)
568            .id(self.name.clone())
569            .cellbase_maturity(EpochNumberWithFraction::from_full_value(
570                self.params.cellbase_maturity(),
571            ))
572            .secondary_epoch_reward(self.params.secondary_epoch_reward())
573            .max_block_cycles(self.params.max_block_cycles())
574            .max_block_bytes(self.params.max_block_bytes())
575            .pow(self.pow.clone())
576            .satoshi_pubkey_hash(self.genesis.satoshi_gift.satoshi_pubkey_hash.clone())
577            .satoshi_cell_occupied_ratio(self.genesis.satoshi_gift.satoshi_cell_occupied_ratio)
578            .primary_epoch_reward_halving_interval(
579                self.params.primary_epoch_reward_halving_interval(),
580            )
581            .initial_primary_epoch_reward(self.params.initial_primary_epoch_reward())
582            .epoch_duration_target(self.params.epoch_duration_target())
583            .permanent_difficulty_in_dummy(self.params.permanent_difficulty_in_dummy())
584            .max_block_proposals_limit(self.params.max_block_proposals_limit())
585            .orphan_rate_target(self.params.orphan_rate_target())
586            .starting_block_limiting_dao_withdrawing_lock(
587                self.params.starting_block_limiting_dao_withdrawing_lock(),
588            )
589            .hardfork_switch(hardfork_switch);
590
591        if let Some(deployments) = self.softfork_deployments() {
592            builder = builder.softfork_deployments(deployments);
593        }
594
595        Ok(builder.build())
596    }
597
598    /// Build genesis block from chain spec
599    pub fn build_genesis(&self) -> Result<BlockView, Box<dyn Error>> {
600        let special_cell_capacity = {
601            let cellbase_transaction_for_special_cell_capacity =
602                self.build_cellbase_transaction(capacity_bytes!(500))?;
603            // build transaction other than cellbase should return inputs for dao statistics
604            let dep_group_transaction_for_special_cell_capacity =
605                self.build_dep_group_transaction(&cellbase_transaction_for_special_cell_capacity)?;
606            dep_group_transaction_for_special_cell_capacity
607                .data()
608                .as_reader()
609                .raw()
610                .outputs()
611                .iter()
612                .map(|output| Unpack::<Capacity>::unpack(&output.capacity()))
613                .try_fold(Capacity::zero(), Capacity::safe_add)
614        }?;
615
616        let cellbase_transaction = self.build_cellbase_transaction(special_cell_capacity)?;
617        // build transaction other than cellbase should return inputs for dao statistics
618        let dep_group_transaction = self.build_dep_group_transaction(&cellbase_transaction)?;
619
620        let genesis_epoch_length = self.params.genesis_epoch_length();
621        let genesis_primary_issuance = calculate_block_reward(
622            self.params.initial_primary_epoch_reward(),
623            genesis_epoch_length,
624        );
625        let genesis_secondary_issuance =
626            calculate_block_reward(self.params.secondary_epoch_reward(), genesis_epoch_length);
627        let dao = build_genesis_dao_data(
628            vec![&cellbase_transaction, &dep_group_transaction],
629            &self.genesis.satoshi_gift.satoshi_pubkey_hash,
630            self.genesis.satoshi_gift.satoshi_cell_occupied_ratio,
631            genesis_primary_issuance,
632            genesis_secondary_issuance,
633        );
634
635        let block = BlockBuilder::default()
636            .version(self.genesis.version)
637            .parent_hash(&self.genesis.parent_hash)
638            .timestamp(self.genesis.timestamp)
639            .compact_target(self.genesis.compact_target)
640            .extra_hash(&self.genesis.uncles_hash)
641            .epoch(EpochNumberWithFraction::new_unchecked(0, 0, 0))
642            .dao(dao)
643            .nonce(u128::from_le_bytes(self.genesis.nonce.to_le_bytes()))
644            .transaction(cellbase_transaction)
645            .transaction(dep_group_transaction)
646            .build();
647
648        self.check_block(&block)?;
649        Ok(block)
650    }
651
652    fn check_block(&self, block: &BlockView) -> Result<(), Box<dyn Error>> {
653        let mut data_hashes: HashMap<packed::Byte32, (usize, usize)> = HashMap::default();
654        let mut type_hashes: HashMap<packed::Byte32, (usize, usize)> = HashMap::default();
655        let genesis_cell_lock: packed::Script = self.genesis.genesis_cell.lock.clone().into();
656        for (tx_index, tx) in block.transactions().into_iter().enumerate() {
657            data_hashes.extend(
658                tx.outputs_data()
659                    .into_iter()
660                    .map(|data| data.raw_data())
661                    .enumerate()
662                    .filter(|(_, raw_data)| !raw_data.is_empty())
663                    .map(|(output_index, raw_data)| {
664                        (
665                            packed::CellOutput::calc_data_hash(&raw_data),
666                            (tx_index, output_index),
667                        )
668                    }),
669            );
670            type_hashes.extend(
671                tx.outputs()
672                    .into_iter()
673                    .enumerate()
674                    .filter_map(|(output_index, output)| {
675                        output
676                            .type_()
677                            .to_opt()
678                            .map(|type_script| (output_index, type_script))
679                    })
680                    .map(|(output_index, type_script)| {
681                        (type_script.calc_script_hash(), (tx_index, output_index))
682                    }),
683            );
684        }
685        let all_zero_lock_hash = packed::Byte32::default();
686        // Check lock scripts
687        for lock_script in block
688            .transactions()
689            .into_iter()
690            .flat_map(|tx| tx.outputs().into_iter().map(move |output| output.lock()))
691            .filter(|lock_script| {
692                lock_script != &genesis_cell_lock && lock_script.code_hash() != all_zero_lock_hash
693            })
694        {
695            match ScriptHashType::try_from(lock_script.hash_type()).expect("checked data") {
696                ScriptHashType::Data => {
697                    if !data_hashes.contains_key(&lock_script.code_hash()) {
698                        return Err(format!(
699                            "Invalid lock script: code_hash={}, hash_type=data",
700                            lock_script.code_hash(),
701                        )
702                        .into());
703                    }
704                }
705                ScriptHashType::Type => {
706                    if !type_hashes.contains_key(&lock_script.code_hash()) {
707                        return Err(format!(
708                            "Invalid lock script: code_hash={}, hash_type=type",
709                            lock_script.code_hash(),
710                        )
711                        .into());
712                    }
713                }
714                ScriptHashType::Data1 => {
715                    if !data_hashes.contains_key(&lock_script.code_hash()) {
716                        return Err(format!(
717                            "Invalid lock script: code_hash={}, hash_type=data1",
718                            lock_script.code_hash(),
719                        )
720                        .into());
721                    }
722                }
723                ScriptHashType::Data2 => {
724                    if !data_hashes.contains_key(&lock_script.code_hash()) {
725                        return Err(format!(
726                            "Invalid lock script: code_hash={}, hash_type=data2",
727                            lock_script.code_hash(),
728                        )
729                        .into());
730                    }
731                }
732                hash_type => {
733                    return Err(format!(
734                        "Invalid lock script: code_hash={}, hash_type={:?}",
735                        lock_script.code_hash(),
736                        hash_type
737                    )
738                    .into());
739                }
740            }
741        }
742
743        // Check system cells data hash
744        let check_cells_data_hash = |tx_index, output_index, hash: &H256| {
745            if data_hashes.get(&hash.into()) != Some(&(tx_index, output_index)) {
746                return Err(format!(
747                    "Invalid output data for tx-index: {tx_index}, output-index: {output_index}, expected data hash: {hash:x}",
748                ));
749            }
750            Ok(())
751        };
752        check_cells_data_hash(
753            0,
754            OUTPUT_INDEX_SECP256K1_BLAKE160_SIGHASH_ALL as usize,
755            &CODE_HASH_SECP256K1_BLAKE160_SIGHASH_ALL,
756        )?;
757        check_cells_data_hash(0, OUTPUT_INDEX_DAO as usize, &CODE_HASH_DAO)?;
758        check_cells_data_hash(
759            0,
760            OUTPUT_INDEX_SECP256K1_DATA as usize,
761            &CODE_HASH_SECP256K1_DATA,
762        )?;
763        check_cells_data_hash(
764            0,
765            OUTPUT_INDEX_SECP256K1_BLAKE160_MULTISIG_ALL as usize,
766            &CODE_HASH_SECP256K1_BLAKE160_MULTISIG_ALL,
767        )?;
768
769        Ok(())
770    }
771
772    fn build_cellbase_transaction(
773        &self,
774        special_cell_capacity: Capacity,
775    ) -> Result<TransactionView, Box<dyn Error>> {
776        let input = packed::CellInput::new_cellbase_input(0);
777        let mut outputs = Vec::<packed::CellOutput>::with_capacity(
778            1 + self.genesis.system_cells.len() + self.genesis.issued_cells.len(),
779        );
780        let mut outputs_data = Vec::with_capacity(outputs.capacity());
781
782        // Layout of genesis cellbase:
783        // - genesis cell, which contains a message and can never be spent.
784        // - system cells, which stores the built-in code blocks.
785        // - special issued cell, for dep group cell in next transaction
786        // - issued cells
787        let (output, data) = self.genesis.genesis_cell.build_output()?;
788        outputs.push(output);
789        outputs_data.push(data);
790
791        // The first output cell is genesis cell
792        let system_cells_output_index_start = 1;
793        let (system_cells_outputs, system_cells_data): (Vec<_>, Vec<_>) = self
794            .genesis
795            .system_cells
796            .iter()
797            .enumerate()
798            .map(|(index, system_cell)| {
799                system_cell.build_output(
800                    &input,
801                    system_cells_output_index_start + index as u64,
802                    &self.genesis.system_cells_lock,
803                )
804            })
805            .collect::<Result<Vec<_>, _>>()?
806            .into_iter()
807            .unzip();
808        outputs.extend(system_cells_outputs);
809        outputs_data.extend(system_cells_data);
810
811        let special_issued_lock = packed::Script::new_builder()
812            .args(secp_lock_arg(&Privkey::from(SPECIAL_CELL_PRIVKEY.clone())))
813            .code_hash(CODE_HASH_SECP256K1_BLAKE160_SIGHASH_ALL.clone())
814            .hash_type(ScriptHashType::Data)
815            .build();
816        let special_issued_cell = packed::CellOutput::new_builder()
817            .capacity(special_cell_capacity)
818            .lock(special_issued_lock)
819            .build();
820        outputs.push(special_issued_cell);
821        outputs_data.push(Bytes::new());
822
823        outputs.extend(
824            self.genesis
825                .issued_cells
826                .iter()
827                .map(IssuedCell::build_output),
828        );
829        outputs_data.extend(self.genesis.issued_cells.iter().map(|_| Bytes::new()));
830
831        let script: packed::Script = self.genesis.bootstrap_lock.clone().into();
832
833        let tx = TransactionBuilder::default()
834            .input(input)
835            .outputs(outputs)
836            .witness(script.into_witness())
837            .outputs_data(
838                outputs_data
839                    .iter()
840                    .map(|d| d.into())
841                    .collect::<Vec<packed::Bytes>>(),
842            )
843            .build();
844        Ok(tx)
845    }
846
847    fn build_dep_group_transaction(
848        &self,
849        cellbase_tx: &TransactionView,
850    ) -> Result<TransactionView, Box<dyn Error>> {
851        fn find_out_point_by_data_hash(
852            tx: &TransactionView,
853            data_hash: &packed::Byte32,
854        ) -> Option<packed::OutPoint> {
855            tx.outputs_data()
856                .into_iter()
857                .position(|data| {
858                    let hash = packed::CellOutput::calc_data_hash(&data.raw_data());
859                    &hash == data_hash
860                })
861                .map(|index| packed::OutPoint::new(tx.hash(), index as u32))
862        }
863
864        let (outputs, outputs_data): (Vec<_>, Vec<_>) = self
865            .genesis
866            .dep_groups
867            .iter()
868            .map(|dep_group| {
869                let out_points: Vec<_> = dep_group
870                    .files
871                    .iter()
872                    .map(|res| {
873                        let data: Bytes = res.get()?.into_owned().into();
874                        let data_hash = packed::CellOutput::calc_data_hash(&data);
875                        let out_point = find_out_point_by_data_hash(cellbase_tx, &data_hash)
876                            .ok_or_else(|| {
877                                format!("Can not find {res} in genesis cellbase transaction")
878                            })?;
879                        Ok(out_point)
880                    })
881                    .collect::<Result<_, Box<dyn Error>>>()?;
882
883                let data = Into::<packed::OutPointVec>::into(out_points).as_bytes();
884                let cell = packed::CellOutput::new_builder()
885                    .lock(self.genesis.system_cells_lock.clone())
886                    .build_exact_capacity(Capacity::bytes(data.len())?)?;
887                Ok((cell, data.into()))
888            })
889            .collect::<Result<Vec<(packed::CellOutput, packed::Bytes)>, Box<dyn Error>>>()?
890            .into_iter()
891            .unzip();
892
893        let privkey = Privkey::from(SPECIAL_CELL_PRIVKEY.clone());
894        let lock_arg = secp_lock_arg(&privkey);
895        let input_out_point = cellbase_tx
896            .outputs()
897            .into_iter()
898            .position(|output| Unpack::<Bytes>::unpack(&output.lock().args()) == lock_arg)
899            .map(|index| packed::OutPoint::new(cellbase_tx.hash(), index as u32))
900            .expect("Get special issued input failed");
901        let input = packed::CellInput::new(input_out_point, 0);
902
903        let secp_data_out_point =
904            find_out_point_by_data_hash(cellbase_tx, &CODE_HASH_SECP256K1_DATA.into())
905                .ok_or_else(|| String::from("Get secp data out point failed"))?;
906        let secp_blake160_out_point = find_out_point_by_data_hash(
907            cellbase_tx,
908            &CODE_HASH_SECP256K1_BLAKE160_SIGHASH_ALL.into(),
909        )
910        .ok_or_else(|| String::from("Get secp blake160 out point failed"))?;
911        let cell_deps = vec![
912            packed::CellDep::new_builder()
913                .out_point(secp_data_out_point)
914                .build(),
915            packed::CellDep::new_builder()
916                .out_point(secp_blake160_out_point)
917                .build(),
918        ];
919        let tx = TransactionBuilder::default()
920            .cell_deps(cell_deps.clone())
921            .input(input.clone())
922            .outputs(outputs.clone())
923            .outputs_data(outputs_data.clone())
924            .build();
925
926        let tx_hash: H256 = tx.hash().into();
927        let message = H256::from(blake2b_256(tx_hash));
928        let sig = privkey.sign_recoverable(&message).expect("sign");
929        let witness = Into::<packed::Bytes>::into(Bytes::from(sig.serialize()));
930
931        Ok(TransactionBuilder::default()
932            .cell_deps(cell_deps)
933            .input(input)
934            .outputs(outputs)
935            .outputs_data(outputs_data)
936            .witness(witness)
937            .build())
938    }
939}
940
941impl GenesisCell {
942    fn build_output(&self) -> Result<(packed::CellOutput, Bytes), Box<dyn Error>> {
943        let data: Bytes = self.message.as_bytes().to_owned().into();
944        let cell = packed::CellOutput::new_builder()
945            .lock(self.lock.clone())
946            .build_exact_capacity(Capacity::bytes(data.len())?)?;
947        Ok((cell, data))
948    }
949}
950
951impl IssuedCell {
952    fn build_output(&self) -> packed::CellOutput {
953        packed::CellOutput::new_builder()
954            .lock(self.lock.clone())
955            .capacity(self.capacity)
956            .build()
957    }
958}
959
960impl SystemCell {
961    fn build_output(
962        &self,
963        input: &packed::CellInput,
964        output_index: u64,
965        lock: &Script,
966    ) -> Result<(packed::CellOutput, Bytes), Box<dyn Error>> {
967        let data: Bytes = self.file.get()?.into_owned().into();
968        let type_script = if self.create_type_id {
969            Some(build_type_id_script(input, output_index))
970        } else {
971            None
972        };
973        let builder = packed::CellOutput::new_builder()
974            .type_(type_script)
975            .lock(lock.clone());
976
977        let data_len = Capacity::bytes(data.len())?;
978        let cell = if let Some(capacity) = self.capacity {
979            let cell = builder.capacity(capacity).build();
980            let occupied_capacity = cell.occupied_capacity(data_len)?.as_u64();
981            if occupied_capacity > capacity {
982                return Err(format!(
983                    "Insufficient capacity to create system cell at index {output_index}, \
984                     occupied / capacity = {occupied_capacity} / {capacity}"
985                )
986                .into());
987            }
988            cell
989        } else {
990            builder.build_exact_capacity(data_len)?
991        };
992
993        Ok((cell, data))
994    }
995}
996
997fn secp_lock_arg(privkey: &Privkey) -> Bytes {
998    let pubkey_data = privkey.pubkey().expect("Get pubkey failed").serialize();
999    Bytes::from((blake2b_256(pubkey_data)[0..20]).to_owned())
1000}
1001
1002/// Shortcut for build genesis type_id script from specified output_index
1003pub fn build_genesis_type_id_script(output_index: u64) -> packed::Script {
1004    build_type_id_script(&packed::CellInput::new_cellbase_input(0), output_index)
1005}
1006
1007pub(crate) fn build_type_id_script(input: &packed::CellInput, output_index: u64) -> packed::Script {
1008    let mut blake2b = new_blake2b();
1009    blake2b.update(input.as_slice());
1010    blake2b.update(&output_index.to_le_bytes());
1011    let mut ret = [0; 32];
1012    blake2b.finalize(&mut ret);
1013    let script_arg = Bytes::from(ret.to_vec());
1014    packed::Script::new_builder()
1015        .code_hash(TYPE_ID_CODE_HASH)
1016        .hash_type(ScriptHashType::Type)
1017        .args(script_arg)
1018        .build()
1019}
1020
1021/// Shortcut for calculate block_reward helper method
1022pub fn calculate_block_reward(epoch_reward: Capacity, epoch_length: BlockNumber) -> Capacity {
1023    let epoch_reward = epoch_reward.as_u64();
1024    Capacity::shannons({
1025        if epoch_reward % epoch_length != 0 {
1026            epoch_reward / epoch_length + 1
1027        } else {
1028            epoch_reward / epoch_length
1029        }
1030    })
1031}