diff --git a/spec/src/consensus.rs b/spec/src/consensus.rs index e9fa6364f6..636e281241 100644 --- a/spec/src/consensus.rs +++ b/spec/src/consensus.rs @@ -10,7 +10,7 @@ use crate::{ VersionbitsConditionChecker, VersionbitsIndexer, }, OUTPUT_INDEX_DAO, OUTPUT_INDEX_SECP256K1_BLAKE160_MULTISIG_ALL, - OUTPUT_INDEX_SECP256K1_BLAKE160_SIGHASH_ALL, + OUTPUT_INDEX_SECP256K1_BLAKE160_SIGHASH_ALL, OUTPUT_INDEX_TOKEN_MANAGER, }; use ckb_constant::{ consensus::TAU, @@ -280,6 +280,7 @@ impl ConsensusBuilder { max_block_cycles: MAX_BLOCK_CYCLES, max_block_bytes: MAX_BLOCK_BYTES, dao_type_hash: Byte32::default(), + token_manager_type_hash: Byte32::default(), secp256k1_blake160_sighash_all_type_hash: None, secp256k1_blake160_multisig_all_type_hash: None, genesis_epoch_ext, @@ -312,6 +313,18 @@ impl ConsensusBuilder { .map(|type_script| type_script.calc_script_hash()) } + fn get_lock_hash(&self, output_index: u64) -> Option { + self.inner + .genesis_block + .transaction(0) + .expect("Genesis must have cellbase") + .output(output_index as usize) + .map(|output| { + ckb_logger::info!("output: {:?}", output); + output.lock().calc_script_hash() + }) + } + /// Build a new Consensus by taking ownership of the `Builder`, and returns a [`Consensus`]. pub fn build(mut self) -> Consensus { debug_assert!( @@ -346,6 +359,11 @@ impl ConsensusBuilder { ); self.inner.dao_type_hash = self.get_type_hash(OUTPUT_INDEX_DAO).unwrap_or_default(); + // Dummy implementation, currently using the hash of the lock script, + // will be changed to use the type script shortly + self.inner.token_manager_type_hash = self + .get_lock_hash(OUTPUT_INDEX_TOKEN_MANAGER) + .unwrap_or_default(); self.inner.secp256k1_blake160_sighash_all_type_hash = self.get_type_hash(OUTPUT_INDEX_SECP256K1_BLAKE160_SIGHASH_ALL); self.inner.secp256k1_blake160_multisig_all_type_hash = @@ -513,6 +531,8 @@ pub struct Consensus { /// /// [nervos-dao](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0024-ckb-genesis-script-list/0024-ckb-genesis-script-list.md#nervos-dao) pub dao_type_hash: Byte32, + /// The token manager type hash + pub token_manager_type_hash: Byte32, /// The secp256k1_blake160_sighash_all_type_hash /// /// [SECP256K1/blake160](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0024-ckb-genesis-script-list/0024-ckb-genesis-script-list.md#secp256k1blake160) @@ -628,6 +648,11 @@ impl Consensus { self.dao_type_hash.clone() } + /// The token manager type hash + pub fn token_manager_type_hash(&self) -> Byte32 { + self.token_manager_type_hash.clone() + } + /// The secp256k1_blake160_sighash_all_type_hash /// /// [SECP256K1/blake160](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0024-ckb-genesis-script-list/0024-ckb-genesis-script-list.md#secp256k1blake160) diff --git a/spec/src/lib.rs b/spec/src/lib.rs index 747d69e591..08394f2b43 100644 --- a/spec/src/lib.rs +++ b/spec/src/lib.rs @@ -65,6 +65,8 @@ pub const OUTPUT_INDEX_DAO: u64 = 2; pub const OUTPUT_INDEX_SECP256K1_DATA: u64 = 3; /// The output index of SECP256K1/multisig script in the genesis no.0 transaction pub const OUTPUT_INDEX_SECP256K1_BLAKE160_MULTISIG_ALL: u64 = 4; +/// The output index of Token Manager script in the genesis no.0 transaction +pub const OUTPUT_INDEX_TOKEN_MANAGER: u64 = 8; /// The CKB block chain specification #[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)] diff --git a/tx-pool/src/util.rs b/tx-pool/src/util.rs index c94eab5b25..4db3c35359 100644 --- a/tx-pool/src/util.rs +++ b/tx-pool/src/util.rs @@ -31,6 +31,10 @@ pub(crate) fn check_tx_fee( rtx: &ResolvedTransaction, tx_size: usize, ) -> Result { + if rtx.is_leap_tx(&snapshot.consensus().token_manager_type_hash) { + return Ok(Capacity::zero()); + } + let fee = DaoCalculator::new(snapshot.consensus(), &snapshot.borrow_as_data_loader()) .transaction_fee(rtx) .map_err(|err| { diff --git a/util/types/src/core/cell.rs b/util/types/src/core/cell.rs index e5b62499ab..499f8a387f 100644 --- a/util/types/src/core/cell.rs +++ b/util/types/src/core/cell.rs @@ -281,6 +281,25 @@ impl ResolvedTransaction { self.resolved_inputs.is_empty() } + /// Returns true if the transaction is leap transaction. + pub fn is_leap_tx(&self, token_manager_type_hash: &Byte32) -> bool { + self.resolved_inputs.iter().any(|cell_meta| { + Self::cell_uses_token_manager_lock_script( + &cell_meta.cell_output, + token_manager_type_hash, + ) + }) + } + + fn cell_uses_token_manager_lock_script( + cell_output: &CellOutput, + token_manager_type_hash: &Byte32, + ) -> bool { + // Dummy implementation, currently using the hash of the lock script, + // will be changed to use the type script shortly + &cell_output.lock().calc_script_hash() == token_manager_type_hash + } + /// TODO(doc): @quake pub fn inputs_capacity(&self) -> CapacityResult { self.resolved_inputs diff --git a/verification/src/tests/transaction_verifier.rs b/verification/src/tests/transaction_verifier.rs index 16f05fe1fe..d4e9ec5153 100644 --- a/verification/src/tests/transaction_verifier.rs +++ b/verification/src/tests/transaction_verifier.rs @@ -104,7 +104,7 @@ pub fn test_capacity_outofbound() { resolved_dep_groups: vec![], }); let dao_type_hash = build_genesis_type_id_script(OUTPUT_INDEX_DAO).calc_script_hash(); - let verifier = CapacityVerifier::new(rtx, dao_type_hash); + let verifier = CapacityVerifier::new(rtx, dao_type_hash, Byte32::default()); assert_error_eq!( verifier.verify().unwrap_err(), @@ -136,7 +136,8 @@ pub fn test_skip_dao_capacity_check() { resolved_inputs: vec![], resolved_dep_groups: vec![], }); - let verifier = CapacityVerifier::new(rtx, dao_type_script.calc_script_hash()); + let verifier = + CapacityVerifier::new(rtx, dao_type_script.calc_script_hash(), Byte32::default()); assert!(verifier.verify().is_ok()); } @@ -329,7 +330,7 @@ pub fn test_capacity_invalid() { resolved_dep_groups: vec![], }); let dao_type_hash = build_genesis_type_id_script(OUTPUT_INDEX_DAO).calc_script_hash(); - let verifier = CapacityVerifier::new(rtx, dao_type_hash); + let verifier = CapacityVerifier::new(rtx, dao_type_hash, Byte32::default()); assert_error_eq!( verifier.verify().unwrap_err(), diff --git a/verification/src/transaction_verifier.rs b/verification/src/transaction_verifier.rs index c22d74b359..be85345ab7 100644 --- a/verification/src/transaction_verifier.rs +++ b/verification/src/transaction_verifier.rs @@ -149,7 +149,11 @@ where Arc::clone(&consensus), Arc::clone(&tx_env), ), - capacity: CapacityVerifier::new(Arc::clone(&rtx), consensus.dao_type_hash()), + capacity: CapacityVerifier::new( + Arc::clone(&rtx), + consensus.dao_type_hash(), + consensus.token_manager_type_hash(), + ), fee_calculator: FeeCalculator::new(rtx, consensus, data_loader), } } @@ -255,8 +259,12 @@ impl } fn transaction_fee(&self) -> Result { - // skip tx fee calculation for cellbase - if self.transaction.is_cellbase() { + // skip tx fee calculation for cellbase and leap tx + if self.transaction.is_cellbase() + || self + .transaction + .is_leap_tx(&self.consensus.token_manager_type_hash) + { Ok(Capacity::zero()) } else { DaoCalculator::new(self.consensus.as_ref(), &self.data_loader) @@ -454,25 +462,37 @@ impl<'a> DuplicateDepsVerifier<'a> { pub struct CapacityVerifier { resolved_transaction: Arc, dao_type_hash: Byte32, + token_manager_type_hash: Byte32, } impl CapacityVerifier { /// Create a new `CapacityVerifier` - pub fn new(resolved_transaction: Arc, dao_type_hash: Byte32) -> Self { + pub fn new( + resolved_transaction: Arc, + dao_type_hash: Byte32, + token_manager_type_hash: Byte32, + ) -> Self { CapacityVerifier { resolved_transaction, dao_type_hash, + token_manager_type_hash, } } /// Verify sum of inputs capacity should be greater than or equal to sum of outputs capacity /// Verify outputs capacity should be greater than or equal to its occupied capacity pub fn verify(&self) -> Result<(), Error> { - // skip OutputsSumOverflow verification for resolved cellbase and DAO - // withdraw transactions. + // skip OutputsSumOverflow verification for resolved cellbase, leap tx + // and DAO withdraw transactions. // cellbase's outputs are verified by RewardVerifier + // leap transaction is verified via the type script of Token Manager cell // DAO withdraw transaction is verified via the type script of DAO cells - if !(self.resolved_transaction.is_cellbase() || self.valid_dao_withdraw_transaction()) { + if !(self.resolved_transaction.is_cellbase() + || self.valid_dao_withdraw_transaction() + || self + .resolved_transaction + .is_leap_tx(&self.token_manager_type_hash)) + { let inputs_sum = self.resolved_transaction.inputs_capacity()?; let outputs_sum = self.resolved_transaction.outputs_capacity()?; @@ -874,7 +894,11 @@ where data_loader.clone(), tx_env, ), - capacity: CapacityVerifier::new(Arc::clone(&rtx), consensus.dao_type_hash()), + capacity: CapacityVerifier::new( + Arc::clone(&rtx), + consensus.dao_type_hash(), + consensus.token_manager_type_hash(), + ), fee_calculator: FeeCalculator::new(rtx, consensus, data_loader), } }