1use 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
56const SPECIAL_CELL_PRIVKEY: H256 =
58 h256!("0xd0c5c1e2d5af8b6ced3c0800937f996c1fa38c29186cade0cd8b5a73c97aaca3");
59
60pub const OUTPUT_INDEX_SECP256K1_BLAKE160_SIGHASH_ALL: u64 = 1;
62pub const OUTPUT_INDEX_DAO: u64 = 2;
64pub const OUTPUT_INDEX_SECP256K1_DATA: u64 = 3;
66pub const OUTPUT_INDEX_SECP256K1_BLAKE160_MULTISIG_ALL: u64 = 4;
68
69#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
71#[serde(deny_unknown_fields)]
72pub struct ChainSpec {
73 pub name: String,
75 pub genesis: Genesis,
77 #[serde(default)]
79 pub params: Params,
80 pub pow: Pow,
82 #[serde(skip)]
83 pub hash: packed::Byte32,
85}
86
87pub 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 pub fn initial_primary_epoch_reward() -> Capacity {
101 INITIAL_PRIMARY_EPOCH_REWARD
102 }
103
104 pub fn secondary_epoch_reward() -> Capacity {
108 DEFAULT_SECONDARY_EPOCH_REWARD
109 }
110
111 pub fn max_block_cycles() -> Cycle {
115 MAX_BLOCK_CYCLES
116 }
117
118 pub fn max_block_bytes() -> u64 {
122 MAX_BLOCK_BYTES
123 }
124
125 pub fn cellbase_maturity() -> u64 {
129 CELLBASE_MATURITY.full_value()
130 }
131
132 pub fn primary_epoch_reward_halving_interval() -> EpochNumber {
136 DEFAULT_PRIMARY_EPOCH_REWARD_HALVING_INTERVAL
137 }
138
139 pub fn epoch_duration_target() -> u64 {
143 DEFAULT_EPOCH_DURATION_TARGET
144 }
145
146 pub fn genesis_epoch_length() -> u64 {
150 GENESIS_EPOCH_LENGTH
151 }
152
153 pub fn max_block_proposals_limit() -> u64 {
157 MAX_BLOCK_PROPOSALS_LIMIT
158 }
159
160 pub fn permanent_difficulty_in_dummy() -> bool {
164 false
165 }
166
167 pub fn orphan_rate_target() -> (u32, u32) {
171 DEFAULT_ORPHAN_RATE_TARGET
172 }
173
174 pub fn starting_block_limiting_dao_withdrawing_lock() -> u64 {
178 STARTING_BLOCK_LIMITING_DAO_WITHDRAWING_LOCK
179 }
180}
181
182#[derive(Default, Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
184#[serde(deny_unknown_fields)]
185pub struct Params {
186 #[serde(skip_serializing_if = "Option::is_none")]
190 pub initial_primary_epoch_reward: Option<Capacity>,
191 #[serde(skip_serializing_if = "Option::is_none")]
195 pub secondary_epoch_reward: Option<Capacity>,
196 #[serde(skip_serializing_if = "Option::is_none")]
200 pub max_block_cycles: Option<Cycle>,
201 #[serde(skip_serializing_if = "Option::is_none")]
205 pub max_block_bytes: Option<u64>,
206 #[serde(skip_serializing_if = "Option::is_none")]
210 pub cellbase_maturity: Option<u64>,
211 #[serde(skip_serializing_if = "Option::is_none")]
215 pub primary_epoch_reward_halving_interval: Option<EpochNumber>,
216 #[serde(skip_serializing_if = "Option::is_none")]
220 pub epoch_duration_target: Option<u64>,
221 #[serde(skip_serializing_if = "Option::is_none")]
225 pub genesis_epoch_length: Option<BlockNumber>,
226 #[serde(skip_serializing_if = "Option::is_none")]
230 pub permanent_difficulty_in_dummy: Option<bool>,
231 #[serde(skip_serializing_if = "Option::is_none")]
235 pub max_block_proposals_limit: Option<u64>,
236 #[serde(skip_serializing_if = "Option::is_none")]
240 pub orphan_rate_target: Option<(u32, u32)>,
241 #[serde(skip_serializing_if = "Option::is_none")]
245 pub starting_block_limiting_dao_withdrawing_lock: Option<u64>,
246 #[serde(skip_serializing_if = "Option::is_none")]
250 pub hardfork: Option<HardForkConfig>,
251}
252
253impl Params {
254 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 pub fn secondary_epoch_reward(&self) -> Capacity {
262 self.secondary_epoch_reward
263 .unwrap_or_else(default_params::secondary_epoch_reward)
264 }
265
266 pub fn max_block_cycles(&self) -> Cycle {
268 self.max_block_cycles
269 .unwrap_or_else(default_params::max_block_cycles)
270 }
271
272 pub fn max_block_bytes(&self) -> u64 {
274 self.max_block_bytes
275 .unwrap_or_else(default_params::max_block_bytes)
276 }
277
278 pub fn cellbase_maturity(&self) -> u64 {
280 self.cellbase_maturity
281 .unwrap_or_else(default_params::cellbase_maturity)
282 }
283
284 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 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 pub fn epoch_duration_target(&self) -> u64 {
298 self.epoch_duration_target
299 .unwrap_or_else(default_params::epoch_duration_target)
300 }
301
302 pub fn genesis_epoch_length(&self) -> BlockNumber {
304 self.genesis_epoch_length
305 .unwrap_or_else(default_params::genesis_epoch_length)
306 }
307
308 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 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 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#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
330#[serde(deny_unknown_fields)]
331pub struct Genesis {
332 pub version: u32,
334 pub parent_hash: H256,
336 pub timestamp: u64,
338 pub compact_target: u32,
340 pub uncles_hash: H256,
342 pub hash: Option<H256>,
346 pub nonce: U128,
348 pub issued_cells: Vec<IssuedCell>,
352 pub genesis_cell: GenesisCell,
356 pub system_cells: Vec<SystemCell>,
360 pub system_cells_lock: Script,
362 pub bootstrap_lock: Script,
365 pub dep_groups: Vec<DepGroupResource>,
369 #[serde(default)]
371 pub satoshi_gift: SatoshiGift,
372}
373
374#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
376#[serde(deny_unknown_fields)]
377pub struct SystemCell {
378 pub create_type_id: bool,
381 pub capacity: Option<u64>,
383 pub file: Resource,
385}
386
387#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
389#[serde(deny_unknown_fields)]
390pub struct GenesisCell {
391 pub message: String,
393 pub lock: Script,
395}
396
397#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
399#[serde(deny_unknown_fields)]
400pub struct IssuedCell {
401 pub capacity: Capacity,
403 pub lock: Script,
405}
406
407#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
409#[serde(deny_unknown_fields)]
410pub struct DepGroupResource {
411 pub name: String,
413 pub files: Vec<Resource>,
415}
416
417#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
419#[serde(deny_unknown_fields)]
420pub struct SatoshiGift {
421 pub satoshi_pubkey_hash: H160,
423 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 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 spec.hash = packed::Byte32::new(blake2b_256(toml::to_vec(&spec)?));
492
493 Ok(spec)
494 }
495
496 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 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 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 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 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 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 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 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 let (output, data) = self.genesis.genesis_cell.build_output()?;
788 outputs.push(output);
789 outputs_data.push(data);
790
791 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
1002pub 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
1021pub 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}