diff --git a/contracts/cluster/src/entry.rs b/contracts/cluster/src/entry.rs index 25f7232..64ae330 100644 --- a/contracts/cluster/src/entry.rs +++ b/contracts/cluster/src/entry.rs @@ -25,40 +25,6 @@ use spore_utils::{ use crate::hash::SPORE_EXTENSION_LUA; -#[allow(unused)] -fn process_input( - index: usize, - input_source: Source, - group_cell_in_outputs: &mut Vec, - output_source: Source, -) -> Result<(), Error> { - let group_id = load_cell_type(index, input_source)? - .unwrap_or_default() - .args(); - - for i in 0..group_cell_in_outputs.len() { - let output_index = group_cell_in_outputs.get(i).unwrap(); - let output_group_id = load_cell_type(*output_index, output_source)? - .unwrap_or_default() - .args(); - - if group_id.as_slice()[..] == output_group_id.as_slice()[..] { - let group_data = load_cluster_data(index, input_source)?; - let output_group_data = load_cluster_data(i, output_source)?; - - if group_data.name().as_slice()[..] != output_group_data.name().as_slice()[..] { - return Err(Error::ModifyClusterPermanentField); - } - - group_cell_in_outputs.remove(i); - return Ok(()); - } - } - - // can not destroy a group cell now - Err(Error::InvalidClusterOperation) -} - fn load_cluster_data(index: usize, source: Source) -> Result { let raw_data = load_cell_data(index, source)?; let cluster_data = ClusterData::from_compatible_slice(raw_data.as_slice()) @@ -108,7 +74,8 @@ fn process_transfer() -> Result<(), Error> { } // check co-build action @lyk - let action::SporeActionUnion::TransferCluster(transfer) = extract_spore_action()?.to_enum() else { + let action::SporeActionUnion::TransferCluster(transfer) = extract_spore_action()?.to_enum() + else { return Err(Error::SporeActionMismatch); }; if transfer.cluster_id().as_slice() != &load_self_id()? { diff --git a/contracts/cluster_agent/src/entry.rs b/contracts/cluster_agent/src/entry.rs index a767500..8ba1a3f 100644 --- a/contracts/cluster_agent/src/entry.rs +++ b/contracts/cluster_agent/src/entry.rs @@ -17,9 +17,10 @@ use spore_types::generated::action; use spore_utils::{ calc_capacity_sum, find_position_by_type, find_position_by_type_hash, load_self_id, }; -use spore_utils::{check_spore_address, extract_spore_action}; - -const CLUSTER_PROXY_ID_LEN: usize = 32; +use spore_utils::{ + check_spore_address, extract_spore_action, CLUSTER_PROXY_ID_LEN, + CLUSTER_PROXY_ID_WITH_PAYMENT_LEN, +}; fn is_valid_cluster_proxy_cell(script_hash: &[u8; 32]) -> bool { crate::hash::CLUSTER_PROXY_CODE_HASHES.contains(script_hash) @@ -72,11 +73,12 @@ fn process_creation(_index: usize) -> Result<(), Error> { .unwrap_or_default() .args() .raw_data(); - if proxy_type_args.len() > CLUSTER_PROXY_ID_LEN { - let minimal_payment_args = proxy_type_args.get(CLUSTER_PROXY_ID_LEN).unwrap_or(&0); - debug!("Minimal payment is: {}", minimal_payment_args); + if proxy_type_args.len() == CLUSTER_PROXY_ID_WITH_PAYMENT_LEN { + let range = CLUSTER_PROXY_ID_LEN..CLUSTER_PROXY_ID_WITH_PAYMENT_LEN; + let minimal_payment = + u64::from_le_bytes(proxy_type_args[range].try_into().unwrap_or_default()); + debug!("Minimal payment is: {}", minimal_payment); - let minimal_payment = 10u128.pow(*minimal_payment_args as u32); let proxy_lock_hash = load_cell_lock_hash(proxy_index, CellDep)?; let input_capacity = calc_capacity_sum(&proxy_lock_hash, Input); let output_capacity = calc_capacity_sum(&proxy_lock_hash, Output); @@ -89,7 +91,9 @@ fn process_creation(_index: usize) -> Result<(), Error> { } } } else { - return Err(Error::PaymentMethodNotSupport); + if proxy_type_args.len() != CLUSTER_PROXY_ID_LEN { + return Err(Error::PaymentMethodNotSupport); + } } } @@ -98,7 +102,7 @@ fn process_creation(_index: usize) -> Result<(), Error> { return Err(Error::SporeActionMismatch); }; if &cluster_id != mint.cluster_id().as_slice() - || &proxy_type.args().raw_data()[..32] != mint.proxy_id().as_slice() + || &proxy_type.args().raw_data()[..CLUSTER_PROXY_ID_LEN] != mint.proxy_id().as_slice() { return Err(Error::SporeActionFieldMismatch); } @@ -116,7 +120,8 @@ fn process_transfer() -> Result<(), Error> { } // co-build check @lyk - let action::SporeActionUnion::TransferAgent(transfer) = extract_spore_action()?.to_enum() else { + let action::SporeActionUnion::TransferAgent(transfer) = extract_spore_action()?.to_enum() + else { return Err(Error::SporeActionMismatch); }; if &load_self_id()? != transfer.cluster_id().as_slice() { diff --git a/contracts/cluster_proxy/src/entry.rs b/contracts/cluster_proxy/src/entry.rs index 73c3952..64bc39b 100644 --- a/contracts/cluster_proxy/src/entry.rs +++ b/contracts/cluster_proxy/src/entry.rs @@ -5,13 +5,16 @@ use core::result::Result; // Import from `core` instead of from `std` since we are in no-std mode use ckb_std::ckb_constants::Source::{CellDep, GroupInput, GroupOutput, Input, Output}; use ckb_std::ckb_types::packed::Script; -use ckb_std::high_level::{load_cell_data, load_cell_lock_hash, load_cell_type, QueryIter}; +use ckb_std::high_level::{ + load_cell_data, load_cell_lock_hash, load_cell_type, load_script, QueryIter, +}; use spore_errors::error::Error; use spore_types::generated::action; use spore_utils::{ check_spore_address, extract_spore_action, find_position_by_lock_hash, find_position_by_type, - find_position_by_type_args, load_self_id, verify_type_id, + find_position_by_type_args, load_self_id, verify_type_id, CLUSTER_PROXY_ID_LEN, + CLUSTER_PROXY_ID_WITH_PAYMENT_LEN, }; fn is_valid_cluster_cell(script_hash: &[u8; 32]) -> bool { @@ -25,6 +28,12 @@ fn process_creation(index: usize) -> Result<(), Error> { find_position_by_type_args(&cluster_id, CellDep, Some(is_valid_cluster_cell)) .ok_or(Error::ClusterCellNotInDep)?; + // verify script args format + let args = load_script()?.args().raw_data(); + if args.len() != CLUSTER_PROXY_ID_LEN && args.len() != CLUSTER_PROXY_ID_WITH_PAYMENT_LEN { + return Err(Error::InvalidProxyArgs); + } + // verify Proxy ID let Some(proxy_id) = verify_type_id(index) else { return Err(Error::InvalidProxyID); @@ -66,7 +75,8 @@ fn process_transfer() -> Result<(), Error> { } // co-build check @lyk - let action::SporeActionUnion::TransferProxy(transfer) = extract_spore_action()?.to_enum() else { + let action::SporeActionUnion::TransferProxy(transfer) = extract_spore_action()?.to_enum() + else { return Err(Error::SporeActionMismatch); }; if input_data.as_slice() != transfer.cluster_id().as_slice() diff --git a/contracts/spore/src/entry.rs b/contracts/spore/src/entry.rs index d308a39..697789f 100644 --- a/contracts/spore/src/entry.rs +++ b/contracts/spore/src/entry.rs @@ -21,7 +21,7 @@ use spore_types::generated::spore::SporeData; use spore_utils::{ calc_capacity_sum, check_spore_address, compatible_load_cluster_data, extract_spore_action, find_position_by_lock_hash, find_position_by_type, find_position_by_type_args, load_self_id, - verify_type_id, MIME, + verify_type_id, MIME, MUTANT_ID_LEN, MUTANT_ID_WITH_PAYMENT_LEN, }; use crate::hash::{CLUSTER_AGENT_CODE_HASHES, CLUSTER_CODE_HASHES}; @@ -241,7 +241,7 @@ fn process_transfer() -> Result<(), Error> { } fn verify_extension(mime: &MIME, op: Operation, argv: Vec) -> Result<(), Error> { - let mut payment_map: BTreeMap<[u8; 32], u8> = BTreeMap::new(); + let mut payment_map: BTreeMap<[u8; 32], u64> = BTreeMap::new(); let mut extension_hash = [0u8; 32]; for mutant_id in mime.mutants.iter() { let mutant_index = @@ -306,28 +306,28 @@ fn verify_extension(mime: &MIME, op: Operation, argv: Vec) -> Result<(), Err fn check_payment( mutant_index: usize, - payment_map: &mut BTreeMap<[u8; 32], u8>, + payment_map: &mut BTreeMap<[u8; 32], u64>, ) -> Result<(), Error> { let mutant_type = load_cell_type(mutant_index, CellDep)?.unwrap_or_default(); let args = mutant_type.args().raw_data(); - // CAUTION: only check 33 size pattern, leave room for user customization - if args.len() > 32 { + // CAUTION: only check bytes in [32, 40) pattern, leave room for user customization + if args.len() > MUTANT_ID_LEN { + if args.len() < MUTANT_ID_WITH_PAYMENT_LEN { + return Err(Error::InvalidExtensionPaymentFormat); + } // we need a payment let self_lock_hash = load_cell_lock_hash(0, GroupOutput)?; let mutant_lock_hash = load_cell_lock_hash(mutant_index, CellDep)?; let input_capacity = calc_capacity_sum(&self_lock_hash, Input); let output_capacity = calc_capacity_sum(&mutant_lock_hash, Output); - let payment_power = { - let previous_power = payment_map.entry(mutant_lock_hash).or_default(); - let current_power = args.get(32).cloned().unwrap_or(0); - let power = *previous_power + current_power; - *previous_power = power; - power + let minimal_payment = { + let range = MUTANT_ID_LEN..MUTANT_ID_WITH_PAYMENT_LEN; + let threshold = u64::from_le_bytes(args[range].try_into().unwrap_or_default()); + let payment_threshold = payment_map.entry(mutant_lock_hash).or_default(); + *payment_threshold += threshold; + *payment_threshold }; - let minimal_payment = 10u128.pow(payment_power as u32); - - debug!("check mutant payment: {minimal_payment}"); if input_capacity + minimal_payment > output_capacity { return Err(Error::ExtensionPaymentNotEnough); } diff --git a/lib/errors/src/error.rs b/lib/errors/src/error.rs index fad3da0..35bd050 100644 --- a/lib/errors/src/error.rs +++ b/lib/errors/src/error.rs @@ -36,6 +36,7 @@ pub enum Error { InvalidProxyOperation = 30, ImmutableProxyFieldModification, InvalidProxyID, + InvalidProxyArgs, // cluster_agent errors InvalidAgentOperation = 40, @@ -69,6 +70,7 @@ pub enum Error { ExtensionCellNotInDep, ExtensionPaymentNotEnough, ClusterRequiresMutantApplied, + InvalidExtensionPaymentFormat, // mime errors Illformed = 80, diff --git a/lib/utils/src/lib.rs b/lib/utils/src/lib.rs index 4775e99..eb77dec 100644 --- a/lib/utils/src/lib.rs +++ b/lib/utils/src/lib.rs @@ -25,6 +25,12 @@ pub mod co_build_types { mod mime; +pub const MUTANT_ID_LEN: usize = 32; +pub const MUTANT_ID_WITH_PAYMENT_LEN: usize = MUTANT_ID_LEN + 8; + +pub const CLUSTER_PROXY_ID_LEN: usize = 32; +pub const CLUSTER_PROXY_ID_WITH_PAYMENT_LEN: usize = CLUSTER_PROXY_ID_LEN + 8; + pub fn load_self_id() -> Result, Error> { Ok(load_script()?.args().raw_data()[..32].to_vec()) } @@ -147,10 +153,10 @@ pub fn find_position_by_lock_hash(lock_hash: &[u8; 32], source: Source) -> Optio QueryIter::new(load_cell_lock_hash, source).position(|hash| hash[..] == lock_hash[..]) } -pub fn calc_capacity_sum(lock_hash: &[u8; 32], source: Source) -> u128 { +pub fn calc_capacity_sum(lock_hash: &[u8; 32], source: Source) -> u64 { QueryIter::new(load_cell, source) .filter(|cell| cell.lock().calc_script_hash().raw_data().as_ref() == lock_hash) - .map(|cell| cell.capacity().unpack() as u128) + .map(|cell| cell.capacity().unpack()) .sum() } diff --git a/tests/src/tests/cluster.rs b/tests/src/tests/cluster.rs index 072274f..7ed47f5 100644 --- a/tests/src/tests/cluster.rs +++ b/tests/src/tests/cluster.rs @@ -200,7 +200,7 @@ fn test_cluster_agent_mint() { build_spore_type_script(&mut context, &agent_out_point, cluster_id.to_vec().into()); let agent_out_cell = build_normal_output_cell_with_type(&mut context, agent_type.clone()) .as_builder() - .capacity((UNIFORM_CAPACITY + 10).pack()) + .capacity((2 * CAPACITY_UNIT).pack()) .build(); let tx = TransactionBuilder::default() diff --git a/tests/src/utils/mod.rs b/tests/src/utils/mod.rs index 906054c..fe01cc1 100644 --- a/tests/src/utils/mod.rs +++ b/tests/src/utils/mod.rs @@ -19,6 +19,7 @@ pub mod co_build; mod internal; pub const UNIFORM_CAPACITY: u64 = 1000u64; +pub const CAPACITY_UNIT: u64 = 100_000_000; pub fn build_serialized_cluster_data(name: &str, description: &str) -> ClusterData { ClusterData::new_builder() @@ -64,9 +65,10 @@ pub fn build_spore_type_script_with_payment( context: &mut Context, out_point: &OutPoint, args: &[u8; 32], - payment: u8, + payment: u64, ) -> Option