diff --git a/Cargo.lock b/Cargo.lock index 4d060cc506..3e5bc424d4 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1061,6 +1061,7 @@ dependencies = [ "merkle-cbt", "molecule", "numext-fixed-uint", + "once_cell", "proptest", ] @@ -2816,6 +2817,12 @@ dependencies = [ "syn 0.15.29", ] +[[package]] +name = "once_cell" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c601810575c99596d4afc46f78a678c80105117c379eb3650cf99b8a21ce5b" + [[package]] name = "opaque-debug" version = "0.2.2" diff --git a/ckb-bin/src/subcommand/run.rs b/ckb-bin/src/subcommand/run.rs index 2cd00f3083..8c1369e37d 100644 --- a/ckb-bin/src/subcommand/run.rs +++ b/ckb-bin/src/subcommand/run.rs @@ -12,8 +12,9 @@ use ckb_network_alert::alert_relayer::AlertRelayer; use ckb_resource::Resource; use ckb_rpc::{RpcServer, ServiceBuilder}; use ckb_shared::shared::{Shared, SharedBuilder}; +use ckb_store::ChainStore; use ckb_sync::{NetTimeProtocol, NetworkProtocol, Relayer, SyncShared, Synchronizer}; -use ckb_types::prelude::*; +use ckb_types::{core::cell::setup_system_cell_cache, prelude::*}; use ckb_util::{Condvar, Mutex}; use ckb_verification::{GenesisVerifier, Verifier}; use std::sync::Arc; @@ -42,6 +43,11 @@ pub fn run(args: RunArgs, version: Version) -> Result<(), ExitCode> { // Verify genesis every time starting node verify_genesis(&shared)?; + setup_system_cell_cache( + shared.consensus().genesis_block(), + &shared.store().cell_provider(), + ); + ckb_memory_tracker::track_current_process(args.config.memory_tracker.interval); let chain_service = ChainService::new(shared.clone(), table); diff --git a/util/types/Cargo.toml b/util/types/Cargo.toml index 212884f942..d03be8935f 100644 --- a/util/types/Cargo.toml +++ b/util/types/Cargo.toml @@ -18,6 +18,7 @@ bit-vec = "0.5.1" failure = "0.1.5" ckb-error = { path = "../../error" } ckb-rational = { path = "../rational" } +once_cell = "1.3.1" [dev-dependencies] proptest = "0.9" diff --git a/util/types/src/core/cell.rs b/util/types/src/core/cell.rs index e78202c48c..f0813cc48d 100644 --- a/util/types/src/core/cell.rs +++ b/util/types/src/core/cell.rs @@ -2,16 +2,25 @@ use crate::{ bytes::Bytes, core::error::OutPointError, core::{BlockView, Capacity, DepType, TransactionInfo, TransactionView}, - packed::{Byte32, CellOutput, OutPoint, OutPointVec}, + packed::{Byte32, CellDep, CellOutput, OutPoint, OutPointVec}, prelude::*, }; use ckb_error::Error; use ckb_occupied_capacity::Result as CapacityResult; +use once_cell::sync::OnceCell; use std::collections::{HashMap, HashSet}; use std::convert::TryInto; use std::fmt; use std::hash::BuildHasher; +#[derive(Debug)] +pub enum ResolvedDep { + Cell(Box), + Group(Box<(CellMeta, Vec)>), +} + +pub static SYSTEM_CELL: OnceCell> = OnceCell::new(); + #[derive(Clone, Eq, PartialEq, Default)] pub struct CellMeta { pub cell_output: CellOutput, @@ -162,6 +171,33 @@ pub struct ResolvedTransaction { pub resolved_dep_groups: Vec, } +impl ResolvedTransaction { + // cellbase will be resolved with empty input cells, we can use low cost check here: + pub fn is_cellbase(&self) -> bool { + self.resolved_inputs.is_empty() + } + + pub fn inputs_capacity(&self) -> CapacityResult { + self.resolved_inputs + .iter() + .map(CellMeta::capacity) + .try_fold(Capacity::zero(), Capacity::safe_add) + } + + pub fn outputs_capacity(&self) -> CapacityResult { + self.transaction.outputs_capacity() + } + + pub fn related_dep_out_points(&self) -> Vec { + self.resolved_cell_deps + .iter() + .map(|d| &d.out_point) + .chain(self.resolved_dep_groups.iter().map(|d| &d.out_point)) + .cloned() + .collect() + } +} + pub trait CellProvider { fn cell(&self, out_point: &OutPoint, with_data: bool) -> CellStatus; } @@ -424,18 +460,12 @@ pub fn resolve_transaction( } } - for cell_dep in transaction.cell_deps_iter() { - if cell_dep.dep_type() == DepType::DepGroup.into() { - if let Some((dep_group, cell_deps)) = - resolve_dep_group(&cell_dep.out_point(), &mut resolve_cell)? - { - resolved_dep_groups.push(dep_group); - resolved_cell_deps.extend(cell_deps); - } - } else if let Some(cell_meta) = resolve_cell(&cell_dep.out_point(), true)? { - resolved_cell_deps.push(*cell_meta); - } - } + resolve_transaction_deps_with_system_cell_cache( + &transaction, + &mut resolve_cell, + &mut resolved_cell_deps, + &mut resolved_dep_groups, + )?; for block_hash in transaction.header_deps_iter() { header_checker.check_valid(&block_hash)?; @@ -454,31 +484,148 @@ pub fn resolve_transaction( } } -impl ResolvedTransaction { - // cellbase will be resolved with empty input cells, we can use low cost check here: - pub fn is_cellbase(&self) -> bool { - self.resolved_inputs.is_empty() +fn resolve_transaction_deps_with_system_cell_cache< + F: FnMut(&OutPoint, bool) -> Result>, Error>, +>( + transaction: &TransactionView, + cell_resolver: &mut F, + resolved_cell_deps: &mut Vec, + resolved_dep_groups: &mut Vec, +) -> Result<(), Error> { + if let Some(system_cell) = SYSTEM_CELL.get() { + for cell_dep in transaction.cell_deps_iter() { + if let Some(resolved_dep) = system_cell.get(&cell_dep) { + match resolved_dep { + ResolvedDep::Cell(cell_meta) => resolved_cell_deps.push(*cell_meta.clone()), + ResolvedDep::Group(group) => { + let (dep_group, cell_deps) = group.as_ref(); + resolved_dep_groups.push(dep_group.clone()); + resolved_cell_deps.extend(cell_deps.clone()); + } + } + } else { + resolve_transaction_dep( + &cell_dep, + cell_resolver, + resolved_cell_deps, + resolved_dep_groups, + )?; + } + } + } else { + for cell_dep in transaction.cell_deps_iter() { + resolve_transaction_dep( + &cell_dep, + cell_resolver, + resolved_cell_deps, + resolved_dep_groups, + )?; + } } + Ok(()) +} - pub fn inputs_capacity(&self) -> CapacityResult { - self.resolved_inputs - .iter() - .map(CellMeta::capacity) - .try_fold(Capacity::zero(), Capacity::safe_add) +fn resolve_transaction_dep Result>, Error>>( + cell_dep: &CellDep, + cell_resolver: &mut F, + resolved_cell_deps: &mut Vec, + resolved_dep_groups: &mut Vec, +) -> Result<(), Error> { + if cell_dep.dep_type() == DepType::DepGroup.into() { + if let Some((dep_group, cell_deps)) = + resolve_dep_group(&cell_dep.out_point(), cell_resolver)? + { + resolved_dep_groups.push(dep_group); + resolved_cell_deps.extend(cell_deps); + } + } else if let Some(cell_meta) = cell_resolver(&cell_dep.out_point(), true)? { + resolved_cell_deps.push(*cell_meta); } + Ok(()) +} - pub fn outputs_capacity(&self) -> CapacityResult { - self.transaction.outputs_capacity() +fn build_cell_meta_from_out_point( + cell_provider: &CP, + out_point: &OutPoint, + with_data: bool, +) -> Result>, Error> { + let cell_status = cell_provider.cell(out_point, with_data); + match cell_status { + CellStatus::Dead => Err(OutPointError::Dead(out_point.clone()).into()), + CellStatus::Unknown => Ok(None), + CellStatus::Live(cell_meta) => Ok(Some(cell_meta)), } +} - pub fn related_dep_out_points(&self) -> Vec { - self.resolved_cell_deps - .iter() - .map(|d| &d.out_point) - .chain(self.resolved_dep_groups.iter().map(|d| &d.out_point)) - .cloned() - .collect() - } +pub fn setup_system_cell_cache(genesis: &BlockView, cell_provider: &CP) { + let system_cell_transaction = &genesis.transactions()[0]; + let secp_cell_transaction = &genesis.transactions()[1]; + let secp_code_dep = CellDep::new_builder() + .out_point(OutPoint::new(system_cell_transaction.hash(), 1)) + .dep_type(DepType::Code.into()) + .build(); + + let dao_dep = CellDep::new_builder() + .out_point(OutPoint::new(system_cell_transaction.hash(), 2)) + .dep_type(DepType::Code.into()) + .build(); + + let secp_data_dep = CellDep::new_builder() + .out_point(OutPoint::new(system_cell_transaction.hash(), 3)) + .dep_type(DepType::Code.into()) + .build(); + + let secp_group_dep = CellDep::new_builder() + .out_point(OutPoint::new(secp_cell_transaction.hash(), 0)) + .dep_type(DepType::DepGroup.into()) + .build(); + + let multi_sign_secp_group = CellDep::new_builder() + .out_point(OutPoint::new(secp_cell_transaction.hash(), 1)) + .dep_type(DepType::DepGroup.into()) + .build(); + + let mut cell_deps = HashMap::new(); + let secp_code_dep_cell = + build_cell_meta_from_out_point(cell_provider, &secp_code_dep.out_point(), true) + .expect("resolve secp_code_dep_cell") + .expect("resolve secp_code_dep_cell"); + cell_deps.insert(secp_code_dep, ResolvedDep::Cell(secp_code_dep_cell)); + + let dao_dep_cell = build_cell_meta_from_out_point(cell_provider, &dao_dep.out_point(), true) + .expect("resolve dao_dep_cell") + .expect("resolve dao_dep_cell"); + cell_deps.insert(dao_dep, ResolvedDep::Cell(dao_dep_cell)); + + let secp_data_dep_cell = + build_cell_meta_from_out_point(cell_provider, &secp_data_dep.out_point(), true) + .expect("resolve secp_data_dep_cell") + .expect("resolve secp_data_dep_cell"); + cell_deps.insert(secp_data_dep, ResolvedDep::Cell(secp_data_dep_cell)); + + let resolve_cell = + |out_point: &OutPoint, with_data: bool| -> Result>, Error> { + build_cell_meta_from_out_point(cell_provider, out_point, with_data) + }; + + let secp_group_dep_cell = resolve_dep_group(&secp_group_dep.out_point(), resolve_cell) + .expect("resolve secp_group_dep_cell") + .expect("resolve secp_group_dep_cell"); + cell_deps.insert( + secp_group_dep, + ResolvedDep::Group(Box::new(secp_group_dep_cell)), + ); + + let multi_sign_secp_group_cell = + resolve_dep_group(&multi_sign_secp_group.out_point(), resolve_cell) + .expect("resolve multi_sign_secp_group") + .expect("resolve multi_sign_secp_group"); + cell_deps.insert( + multi_sign_secp_group, + ResolvedDep::Group(Box::new(multi_sign_secp_group_cell)), + ); + + SYSTEM_CELL.set(cell_deps).expect("SYSTEM_CELL init once"); } #[cfg(test)]