From f90fa0359ebafccccbe2f947a205e35364fbd179 Mon Sep 17 00:00:00 2001 From: igor-casper Date: Thu, 5 Dec 2024 16:35:34 +0100 Subject: [PATCH 1/8] fix max spending enforcement on non-main purses --- .../src/runtime/auction_internal.rs | 6 + execution_engine/src/runtime/mod.rs | 5 + .../src/test/system_contracts/auction/mod.rs | 152 ++++++++++++++++++ .../contracts/test/staking/src/lib.rs | 22 +++ storage/src/system/auction.rs | 3 +- storage/src/system/auction/auction_native.rs | 4 + storage/src/system/auction/providers.rs | 3 + 7 files changed, 194 insertions(+), 1 deletion(-) diff --git a/execution_engine/src/runtime/auction_internal.rs b/execution_engine/src/runtime/auction_internal.rs index f655f750a2..b5dc40ab0a 100644 --- a/execution_engine/src/runtime/auction_internal.rs +++ b/execution_engine/src/runtime/auction_internal.rs @@ -533,6 +533,12 @@ where .borrow_mut() .set_main_purse(purse); } + + // Set the remaining spending limit. + fn set_remaining_spending_limit(&mut self, amount: U512) { + Runtime::context_mut(self) + .set_remaining_spending_limit(amount); + } } impl<'a, R> Auction for Runtime<'a, R> where diff --git a/execution_engine/src/runtime/mod.rs b/execution_engine/src/runtime/mod.rs index 010baf0c8a..4c2b956e12 100644 --- a/execution_engine/src/runtime/mod.rs +++ b/execution_engine/src/runtime/mod.rs @@ -188,6 +188,11 @@ where &self.context } + /// Returns the mutable context. + pub(crate) fn context_mut(&mut self) -> &mut RuntimeContext<'a, R> { + &mut self.context + } + fn gas(&mut self, amount: Gas) -> Result<(), ExecError> { self.context.charge_gas(amount) } diff --git a/execution_engine_testing/tests/src/test/system_contracts/auction/mod.rs b/execution_engine_testing/tests/src/test/system_contracts/auction/mod.rs index 104c536d1d..8964bc2b70 100644 --- a/execution_engine_testing/tests/src/test/system_contracts/auction/mod.rs +++ b/execution_engine_testing/tests/src/test/system_contracts/auction/mod.rs @@ -331,3 +331,155 @@ fn should_support_contract_staking() { "unbonded amount should be available to contract staking purse" ); } + +#[ignore] +#[test] +fn should_not_enforce_max_spending_when_main_purse_not_in_use() { + const ARG_ACTION: &str = "action"; + let timestamp_millis = DEFAULT_GENESIS_TIMESTAMP_MILLIS; + let purse_name = "staking_purse".to_string(); + let contract_name = "staking".to_string(); + let entry_point_name = "run".to_string(); + let stake_all = "STAKE_ALL".to_string(); + let account = *DEFAULT_ACCOUNT_ADDR; + let seed_amount = U512::from(10_000_000_000_000_000_u64); + let validator_pk = &*DEFAULT_PROPOSER_PUBLIC_KEY; + let other_validator_pk = { + let secret_key = SecretKey::ed25519_from_bytes([199; SecretKey::ED25519_LENGTH]).unwrap(); + PublicKey::from(&secret_key) + }; + + let mut builder = LmdbWasmTestBuilder::default(); + let mut genesis_request = LOCAL_GENESIS_REQUEST.clone(); + genesis_request.set_enable_entity(false); + + genesis_request.push_genesis_validator( + validator_pk, + GenesisValidator::new( + Motes::new(10_000_000_000_000_000_u64), + DelegationRate::zero(), + ), + ); + genesis_request.push_genesis_account(GenesisAccount::Account { + public_key: other_validator_pk.clone(), + validator: Some(GenesisValidator::new( + Motes::new(1_000_000_000_000_000_u64), + DelegationRate::zero(), + )), + balance: Motes::new(10_000_000_000_000_000_u64), + }); + builder.run_genesis(genesis_request); + + let auction_delay = builder.get_unbonding_delay(); + + for _ in 0..=auction_delay { + // crank era + builder.run_auction(timestamp_millis, vec![]); + } + + let account_main_purse = builder + .get_entity_with_named_keys_by_account_hash(account) + .expect("should have account") + .main_purse(); + let starting_account_balance = builder.get_purse_balance(account_main_purse); + + builder + .exec( + ExecuteRequestBuilder::standard( + account, + STORED_STAKING_CONTRACT_NAME, + runtime_args! { + ARG_AMOUNT => seed_amount + }, + ) + .build(), + ) + .commit() + .expect_success(); + + let default_account = builder.get_account(account).expect("should have account"); + let named_keys = default_account.named_keys(); + + let contract_key = named_keys + .get(&contract_name) + .expect("contract_name key should exist"); + + let stored_contract = builder + .query(None, *contract_key, &[]) + .expect("should have stored value at contract key"); + + let contract = stored_contract + .as_contract() + .expect("stored value should be contract"); + + let contract_named_keys = contract.named_keys(); + + let contract_purse = contract_named_keys + .get(&purse_name) + .expect("purse_name key should exist") + .into_uref() + .expect("should be a uref"); + + let post_install_account_balance = builder.get_purse_balance(account_main_purse); + assert_eq!( + post_install_account_balance, + starting_account_balance.saturating_sub(seed_amount), + "post install should be reduced due to seeding contract purse" + ); + + let pre_delegation_balance = builder.get_purse_balance(contract_purse); + assert_eq!(pre_delegation_balance, seed_amount); + + // stake from contract + builder + .exec( + ExecuteRequestBuilder::contract_call_by_name( + account, + &contract_name, + &entry_point_name, + runtime_args! { + ARG_ACTION => stake_all, + ARG_VALIDATOR => validator_pk.clone(), + }, + ) + .build(), + ) + .commit() + .expect_success(); + + let post_delegation_balance = builder.get_purse_balance(contract_purse); + assert_eq!( + post_delegation_balance, + U512::zero(), + "contract purse balance should be reduced by staked amount" + ); + + let delegation_key = Key::BidAddr(BidAddr::DelegatedPurse { + validator: validator_pk.to_account_hash(), + delegator: contract_purse.addr(), + }); + + let stored_value = builder + .query(None, delegation_key, &[]) + .expect("should have delegation bid"); + + assert!( + matches!(stored_value, StoredValue::BidKind(BidKind::Delegator(_))), + "expected delegator bid" + ); + + if let StoredValue::BidKind(BidKind::Delegator(delegator)) = stored_value { + assert_eq!( + delegator.staked_amount(), + pre_delegation_balance, + "staked amount should match delegation amount" + ); + } + + for _ in 0..=auction_delay { + // crank era + builder.run_auction(timestamp_millis, vec![]); + } + + builder.query(None, delegation_key, &[]).expect("should have delegation bid"); +} \ No newline at end of file diff --git a/smart_contracts/contracts/test/staking/src/lib.rs b/smart_contracts/contracts/test/staking/src/lib.rs index fafde7b7d5..5b86e07344 100644 --- a/smart_contracts/contracts/test/staking/src/lib.rs +++ b/smart_contracts/contracts/test/staking/src/lib.rs @@ -75,6 +75,8 @@ pub fn run() { unstake(); } else if action == *"STAKE".to_string() { stake(); + } else if action == *"STAKE_ALL".to_string() { + stake_all(); } else if action == *"RESTAKE".to_string() { restake(); } else { @@ -114,6 +116,26 @@ fn stake() { runtime::call_contract::(contract_hash, auction::METHOD_DELEGATE, args); } +fn stake_all() { + let staking_purse = get_uref_with_user_errors( + STAKING_PURSE, + StakingError::MissingStakingPurse, + StakingError::InvalidStakingPurse, + ); + let validator: PublicKey = match runtime::try_get_named_arg(ARG_VALIDATOR) { + Some(validator_public_key) => validator_public_key, + None => revert(ApiError::User(StakingError::MissingValidator as u16)), + }; + let amount: U512 = system::get_purse_balance(staking_purse).unwrap_or_revert(); + let contract_hash = system::get_auction(); + let args = runtime_args! { + auction::ARG_DELEGATOR_PURSE => staking_purse, + auction::ARG_VALIDATOR => validator, + auction::ARG_AMOUNT => amount, + }; + runtime::call_contract::(contract_hash, auction::METHOD_DELEGATE, args); +} + fn get_unstaking_args(is_restake: bool) -> casper_types::RuntimeArgs { let staking_purse = get_uref_with_user_errors( STAKING_PURSE, diff --git a/storage/src/system/auction.rs b/storage/src/system/auction.rs index 43adcd6632..0db87c916c 100644 --- a/storage/src/system/auction.rs +++ b/storage/src/system/auction.rs @@ -23,7 +23,7 @@ use casper_types::{ UnbondEra, UnbondKind, ValidatorBid, ValidatorCredit, ValidatorWeights, DELEGATION_RATE_DENOMINATOR, }, - AccessRights, ApiError, EraId, Key, PublicKey, URef, U512, + AccessRights, ApiError, EraId, Key, PublicKey, URef, U512, UREF_ADDR_LENGTH, }; use self::providers::{AccountProvider, MintProvider, RuntimeProvider, StorageProvider}; @@ -284,6 +284,7 @@ pub trait Auction: return Err(Error::InvalidContext.into()); } self.set_main_purse(uref); + self.set_remaining_spending_limit(U512::MAX); uref } }; diff --git a/storage/src/system/auction/auction_native.rs b/storage/src/system/auction/auction_native.rs index d4338c8371..94d2231cd2 100644 --- a/storage/src/system/auction/auction_native.rs +++ b/storage/src/system/auction/auction_native.rs @@ -499,6 +499,10 @@ where fn set_main_purse(&mut self, purse: URef) { self.runtime_footprint_mut().set_main_purse(purse); } + + fn set_remaining_spending_limit(&mut self, limit: U512) { + self.set_remaining_spending_limit(limit); + } } impl Auction for RuntimeNative where S: StateReader diff --git a/storage/src/system/auction/providers.rs b/storage/src/system/auction/providers.rs index 7bffbc207c..0ca8fe4f64 100644 --- a/storage/src/system/auction/providers.rs +++ b/storage/src/system/auction/providers.rs @@ -122,4 +122,7 @@ pub trait AccountProvider { /// Set main purse. fn set_main_purse(&mut self, purse: URef); + + /// Set the remaining spending limit. + fn set_remaining_spending_limit(&mut self, limit: U512); } From e9235730c69a5f40cc3b66d566258549187291a0 Mon Sep 17 00:00:00 2001 From: igor-casper Date: Thu, 5 Dec 2024 18:08:56 +0100 Subject: [PATCH 2/8] update obsolete perms check in mint transfer --- .../src/runtime/auction_internal.rs | 15 +- execution_engine/src/runtime/mod.rs | 5 - .../src/test/system_contracts/auction/mod.rs | 6 +- node/src/components/network/tasks.rs | 199 +++++++++--------- storage/src/system/auction.rs | 4 +- storage/src/system/auction/auction_native.rs | 4 - storage/src/system/auction/providers.rs | 3 - storage/src/system/transfer.rs | 35 ++- 8 files changed, 122 insertions(+), 149 deletions(-) diff --git a/execution_engine/src/runtime/auction_internal.rs b/execution_engine/src/runtime/auction_internal.rs index b5dc40ab0a..b8268ec653 100644 --- a/execution_engine/src/runtime/auction_internal.rs +++ b/execution_engine/src/runtime/auction_internal.rs @@ -394,14 +394,7 @@ where amount: U512, id: Option, ) -> Result, Error> { - if !(self - .context - .runtime_footprint() - .borrow() - .main_purse() - .expect("didnt have purse") - .addr() - == source.addr() + if !(self.context.validate_uref(&source).is_ok() || self.context.get_initiator() == PublicKey::System.to_account_hash()) { return Err(Error::InvalidCaller); @@ -533,12 +526,6 @@ where .borrow_mut() .set_main_purse(purse); } - - // Set the remaining spending limit. - fn set_remaining_spending_limit(&mut self, amount: U512) { - Runtime::context_mut(self) - .set_remaining_spending_limit(amount); - } } impl<'a, R> Auction for Runtime<'a, R> where diff --git a/execution_engine/src/runtime/mod.rs b/execution_engine/src/runtime/mod.rs index 4c2b956e12..010baf0c8a 100644 --- a/execution_engine/src/runtime/mod.rs +++ b/execution_engine/src/runtime/mod.rs @@ -188,11 +188,6 @@ where &self.context } - /// Returns the mutable context. - pub(crate) fn context_mut(&mut self) -> &mut RuntimeContext<'a, R> { - &mut self.context - } - fn gas(&mut self, amount: Gas) -> Result<(), ExecError> { self.context.charge_gas(amount) } diff --git a/execution_engine_testing/tests/src/test/system_contracts/auction/mod.rs b/execution_engine_testing/tests/src/test/system_contracts/auction/mod.rs index 8964bc2b70..36f0b17219 100644 --- a/execution_engine_testing/tests/src/test/system_contracts/auction/mod.rs +++ b/execution_engine_testing/tests/src/test/system_contracts/auction/mod.rs @@ -481,5 +481,7 @@ fn should_not_enforce_max_spending_when_main_purse_not_in_use() { builder.run_auction(timestamp_millis, vec![]); } - builder.query(None, delegation_key, &[]).expect("should have delegation bid"); -} \ No newline at end of file + builder + .query(None, delegation_key, &[]) + .expect("should have delegation bid"); +} diff --git a/node/src/components/network/tasks.rs b/node/src/components/network/tasks.rs index f2a637a384..2a31d7f8cb 100644 --- a/node/src/components/network/tasks.rs +++ b/node/src/components/network/tasks.rs @@ -673,111 +673,110 @@ where let demands_in_flight = Arc::new(Semaphore::new(context.max_in_flight_demands)); let event_queue = context.event_queue.expect("component not initialized"); - let read_messages = async move { - while let Some(msg_result) = stream.next().await { - match msg_result { - Ok(msg) => { - trace!(%msg, "message received"); - - let effect_builder = EffectBuilder::new(event_queue); - - match msg.try_into_demand(effect_builder, peer_id) { - Ok((event, wait_for_response)) => { - // Note: For now, demands bypass the limiter, as we expect the - // backpressure to handle this instead. - - // Acquire a permit. If we are handling too many demands at this - // time, this will block, halting the processing of new message, - // thus letting the peer they have reached their maximum allowance. - let in_flight = demands_in_flight - .clone() - .acquire_owned() - .await - // Note: Since the semaphore is reference counted, it must - // explicitly be closed for acquisition to fail, which we - // never do. If this happens, there is a bug in the code; - // we exit with an error and close the connection. - .map_err(|_| { - io::Error::new( - io::ErrorKind::Other, - "demand limiter semaphore closed unexpectedly", - ) - })?; - - Metrics::record_trie_request_start(&context.net_metrics); - - let net_metrics = context.net_metrics.clone(); - // Spawn a future that will eventually send the returned message. It - // will essentially buffer the response. - tokio::spawn(async move { - if let Some(payload) = wait_for_response.await { - // Send message and await its return. `send_message` should - // only return when the message has been buffered, if the - // peer is not accepting data, we will block here until the - // send buffer has sufficient room. - effect_builder.send_message(peer_id, payload).await; - - // Note: We could short-circuit the event queue here and - // directly insert into the outgoing message queue, - // which may be potential performance improvement. - } - - // Missing else: The handler of the demand did not deem it - // worthy a response. Just drop it. - - // After we have either successfully buffered the message for - // sending, failed to do so or did not have a message to send - // out, we consider the request handled and free up the permit. - Metrics::record_trie_request_end(&net_metrics); - drop(in_flight); - }); - - // Schedule the created event. - event_queue - .schedule::(event, QueueKind::NetworkDemand) - .await; - } - Err(msg) => { - // We've received a non-demand message. Ensure we have the proper amount - // of resources, then push it to the reactor. - limiter - .request_allowance( - msg.payload_incoming_resource_estimate( + let read_messages = + async move { + while let Some(msg_result) = stream.next().await { + match msg_result { + Ok(msg) => { + trace!(%msg, "message received"); + + let effect_builder = EffectBuilder::new(event_queue); + + match msg.try_into_demand(effect_builder, peer_id) { + Ok((event, wait_for_response)) => { + // Note: For now, demands bypass the limiter, as we expect the + // backpressure to handle this instead. + + // Acquire a permit. If we are handling too many demands at this + // time, this will block, halting the processing of new message, + // thus letting the peer they have reached their maximum allowance. + let in_flight = demands_in_flight + .clone() + .acquire_owned() + .await + // Note: Since the semaphore is reference counted, it must + // explicitly be closed for acquisition to fail, which we + // never do. If this happens, there is a bug in the code; + // we exit with an error and close the connection. + .map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + "demand limiter semaphore closed unexpectedly", + ) + })?; + + Metrics::record_trie_request_start(&context.net_metrics); + + let net_metrics = context.net_metrics.clone(); + // Spawn a future that will eventually send the returned message. It + // will essentially buffer the response. + tokio::spawn(async move { + if let Some(payload) = wait_for_response.await { + // Send message and await its return. `send_message` should + // only return when the message has been buffered, if the + // peer is not accepting data, we will block here until the + // send buffer has sufficient room. + effect_builder.send_message(peer_id, payload).await; + + // Note: We could short-circuit the event queue here and + // directly insert into the outgoing message queue, + // which may be potential performance improvement. + } + + // Missing else: The handler of the demand did not deem it + // worthy a response. Just drop it. + + // After we have either successfully buffered the message for + // sending, failed to do so or did not have a message to send + // out, we consider the request handled and free up the permit. + Metrics::record_trie_request_end(&net_metrics); + drop(in_flight); + }); + + // Schedule the created event. + event_queue + .schedule::(event, QueueKind::NetworkDemand) + .await; + } + Err(msg) => { + // We've received a non-demand message. Ensure we have the proper amount + // of resources, then push it to the reactor. + limiter + .request_allowance(msg.payload_incoming_resource_estimate( &context.payload_weights, - ), - ) - .await; - - let queue_kind = if msg.is_low_priority() { - QueueKind::NetworkLowPriority - } else { - QueueKind::NetworkIncoming - }; - - event_queue - .schedule( - Event::IncomingMessage { - peer_id: Box::new(peer_id), - msg, - span: span.clone(), - }, - queue_kind, - ) - .await; + )) + .await; + + let queue_kind = if msg.is_low_priority() { + QueueKind::NetworkLowPriority + } else { + QueueKind::NetworkIncoming + }; + + event_queue + .schedule( + Event::IncomingMessage { + peer_id: Box::new(peer_id), + msg, + span: span.clone(), + }, + queue_kind, + ) + .await; + } } } - } - Err(err) => { - warn!( - err = display_error(&err), - "receiving message failed, closing connection" - ); - return Err(err); + Err(err) => { + warn!( + err = display_error(&err), + "receiving message failed, closing connection" + ); + return Err(err); + } } } - } - Ok(()) - }; + Ok(()) + }; let shutdown_messages = async move { while close_incoming_receiver.changed().await.is_ok() {} }; diff --git a/storage/src/system/auction.rs b/storage/src/system/auction.rs index 0db87c916c..7ff638b4de 100644 --- a/storage/src/system/auction.rs +++ b/storage/src/system/auction.rs @@ -23,7 +23,7 @@ use casper_types::{ UnbondEra, UnbondKind, ValidatorBid, ValidatorCredit, ValidatorWeights, DELEGATION_RATE_DENOMINATOR, }, - AccessRights, ApiError, EraId, Key, PublicKey, URef, U512, UREF_ADDR_LENGTH, + AccessRights, ApiError, EraId, Key, PublicKey, URef, U512, }; use self::providers::{AccountProvider, MintProvider, RuntimeProvider, StorageProvider}; @@ -283,8 +283,6 @@ pub trait Auction: if !self.is_valid_uref(uref) { return Err(Error::InvalidContext.into()); } - self.set_main_purse(uref); - self.set_remaining_spending_limit(U512::MAX); uref } }; diff --git a/storage/src/system/auction/auction_native.rs b/storage/src/system/auction/auction_native.rs index 94d2231cd2..d4338c8371 100644 --- a/storage/src/system/auction/auction_native.rs +++ b/storage/src/system/auction/auction_native.rs @@ -499,10 +499,6 @@ where fn set_main_purse(&mut self, purse: URef) { self.runtime_footprint_mut().set_main_purse(purse); } - - fn set_remaining_spending_limit(&mut self, limit: U512) { - self.set_remaining_spending_limit(limit); - } } impl Auction for RuntimeNative where S: StateReader diff --git a/storage/src/system/auction/providers.rs b/storage/src/system/auction/providers.rs index 0ca8fe4f64..7bffbc207c 100644 --- a/storage/src/system/auction/providers.rs +++ b/storage/src/system/auction/providers.rs @@ -122,7 +122,4 @@ pub trait AccountProvider { /// Set main purse. fn set_main_purse(&mut self, purse: URef); - - /// Set the remaining spending limit. - fn set_remaining_spending_limit(&mut self, limit: U512); } diff --git a/storage/src/system/transfer.rs b/storage/src/system/transfer.rs index c0b9f7f484..afe5910ae9 100644 --- a/storage/src/system/transfer.rs +++ b/storage/src/system/transfer.rs @@ -425,24 +425,23 @@ impl TransferRuntimeArgsBuilder { where R: StateReader, { - let (to, target) = match self - .resolve_transfer_target_mode(protocol_version, Rc::clone(&tracking_copy))? - { - TransferTargetMode::ExistingAccount { - main_purse: purse_uref, - target_account_hash: target_account, - } => (Some(target_account), purse_uref), - TransferTargetMode::PurseExists { - target_account_hash, - purse_uref, - } => (target_account_hash, purse_uref), - TransferTargetMode::CreateAccount(_) => { - // Method "build()" is called after `resolve_transfer_target_mode` is first called - // and handled by creating a new account. Calling `resolve_transfer_target_mode` - // for the second time should never return `CreateAccount` variant. - return Err(TransferError::InvalidOperation); - } - }; + let (to, target) = + match self.resolve_transfer_target_mode(protocol_version, Rc::clone(&tracking_copy))? { + TransferTargetMode::ExistingAccount { + main_purse: purse_uref, + target_account_hash: target_account, + } => (Some(target_account), purse_uref), + TransferTargetMode::PurseExists { + target_account_hash, + purse_uref, + } => (target_account_hash, purse_uref), + TransferTargetMode::CreateAccount(_) => { + // Method "build()" is called after `resolve_transfer_target_mode` is first called + // and handled by creating a new account. Calling `resolve_transfer_target_mode` + // for the second time should never return `CreateAccount` variant. + return Err(TransferError::InvalidOperation); + } + }; let source = self.resolve_source_uref(from, Rc::clone(&tracking_copy))?; From 883d8772c9cf078f366dd867f79524b05163eef5 Mon Sep 17 00:00:00 2001 From: igor-casper Date: Thu, 5 Dec 2024 18:12:32 +0100 Subject: [PATCH 3/8] apply formatting --- node/src/components/network/tasks.rs | 199 ++++++++++++++------------- storage/src/system/transfer.rs | 35 ++--- 2 files changed, 118 insertions(+), 116 deletions(-) diff --git a/node/src/components/network/tasks.rs b/node/src/components/network/tasks.rs index 2a31d7f8cb..f2a637a384 100644 --- a/node/src/components/network/tasks.rs +++ b/node/src/components/network/tasks.rs @@ -673,110 +673,111 @@ where let demands_in_flight = Arc::new(Semaphore::new(context.max_in_flight_demands)); let event_queue = context.event_queue.expect("component not initialized"); - let read_messages = - async move { - while let Some(msg_result) = stream.next().await { - match msg_result { - Ok(msg) => { - trace!(%msg, "message received"); - - let effect_builder = EffectBuilder::new(event_queue); - - match msg.try_into_demand(effect_builder, peer_id) { - Ok((event, wait_for_response)) => { - // Note: For now, demands bypass the limiter, as we expect the - // backpressure to handle this instead. - - // Acquire a permit. If we are handling too many demands at this - // time, this will block, halting the processing of new message, - // thus letting the peer they have reached their maximum allowance. - let in_flight = demands_in_flight - .clone() - .acquire_owned() - .await - // Note: Since the semaphore is reference counted, it must - // explicitly be closed for acquisition to fail, which we - // never do. If this happens, there is a bug in the code; - // we exit with an error and close the connection. - .map_err(|_| { - io::Error::new( - io::ErrorKind::Other, - "demand limiter semaphore closed unexpectedly", - ) - })?; - - Metrics::record_trie_request_start(&context.net_metrics); - - let net_metrics = context.net_metrics.clone(); - // Spawn a future that will eventually send the returned message. It - // will essentially buffer the response. - tokio::spawn(async move { - if let Some(payload) = wait_for_response.await { - // Send message and await its return. `send_message` should - // only return when the message has been buffered, if the - // peer is not accepting data, we will block here until the - // send buffer has sufficient room. - effect_builder.send_message(peer_id, payload).await; - - // Note: We could short-circuit the event queue here and - // directly insert into the outgoing message queue, - // which may be potential performance improvement. - } - - // Missing else: The handler of the demand did not deem it - // worthy a response. Just drop it. - - // After we have either successfully buffered the message for - // sending, failed to do so or did not have a message to send - // out, we consider the request handled and free up the permit. - Metrics::record_trie_request_end(&net_metrics); - drop(in_flight); - }); - - // Schedule the created event. - event_queue - .schedule::(event, QueueKind::NetworkDemand) - .await; - } - Err(msg) => { - // We've received a non-demand message. Ensure we have the proper amount - // of resources, then push it to the reactor. - limiter - .request_allowance(msg.payload_incoming_resource_estimate( - &context.payload_weights, - )) - .await; - - let queue_kind = if msg.is_low_priority() { - QueueKind::NetworkLowPriority - } else { - QueueKind::NetworkIncoming - }; - - event_queue - .schedule( - Event::IncomingMessage { - peer_id: Box::new(peer_id), - msg, - span: span.clone(), - }, - queue_kind, + let read_messages = async move { + while let Some(msg_result) = stream.next().await { + match msg_result { + Ok(msg) => { + trace!(%msg, "message received"); + + let effect_builder = EffectBuilder::new(event_queue); + + match msg.try_into_demand(effect_builder, peer_id) { + Ok((event, wait_for_response)) => { + // Note: For now, demands bypass the limiter, as we expect the + // backpressure to handle this instead. + + // Acquire a permit. If we are handling too many demands at this + // time, this will block, halting the processing of new message, + // thus letting the peer they have reached their maximum allowance. + let in_flight = demands_in_flight + .clone() + .acquire_owned() + .await + // Note: Since the semaphore is reference counted, it must + // explicitly be closed for acquisition to fail, which we + // never do. If this happens, there is a bug in the code; + // we exit with an error and close the connection. + .map_err(|_| { + io::Error::new( + io::ErrorKind::Other, + "demand limiter semaphore closed unexpectedly", ) - .await; - } + })?; + + Metrics::record_trie_request_start(&context.net_metrics); + + let net_metrics = context.net_metrics.clone(); + // Spawn a future that will eventually send the returned message. It + // will essentially buffer the response. + tokio::spawn(async move { + if let Some(payload) = wait_for_response.await { + // Send message and await its return. `send_message` should + // only return when the message has been buffered, if the + // peer is not accepting data, we will block here until the + // send buffer has sufficient room. + effect_builder.send_message(peer_id, payload).await; + + // Note: We could short-circuit the event queue here and + // directly insert into the outgoing message queue, + // which may be potential performance improvement. + } + + // Missing else: The handler of the demand did not deem it + // worthy a response. Just drop it. + + // After we have either successfully buffered the message for + // sending, failed to do so or did not have a message to send + // out, we consider the request handled and free up the permit. + Metrics::record_trie_request_end(&net_metrics); + drop(in_flight); + }); + + // Schedule the created event. + event_queue + .schedule::(event, QueueKind::NetworkDemand) + .await; + } + Err(msg) => { + // We've received a non-demand message. Ensure we have the proper amount + // of resources, then push it to the reactor. + limiter + .request_allowance( + msg.payload_incoming_resource_estimate( + &context.payload_weights, + ), + ) + .await; + + let queue_kind = if msg.is_low_priority() { + QueueKind::NetworkLowPriority + } else { + QueueKind::NetworkIncoming + }; + + event_queue + .schedule( + Event::IncomingMessage { + peer_id: Box::new(peer_id), + msg, + span: span.clone(), + }, + queue_kind, + ) + .await; } - } - Err(err) => { - warn!( - err = display_error(&err), - "receiving message failed, closing connection" - ); - return Err(err); } } + Err(err) => { + warn!( + err = display_error(&err), + "receiving message failed, closing connection" + ); + return Err(err); + } } - Ok(()) - }; + } + Ok(()) + }; let shutdown_messages = async move { while close_incoming_receiver.changed().await.is_ok() {} }; diff --git a/storage/src/system/transfer.rs b/storage/src/system/transfer.rs index afe5910ae9..c0b9f7f484 100644 --- a/storage/src/system/transfer.rs +++ b/storage/src/system/transfer.rs @@ -425,23 +425,24 @@ impl TransferRuntimeArgsBuilder { where R: StateReader, { - let (to, target) = - match self.resolve_transfer_target_mode(protocol_version, Rc::clone(&tracking_copy))? { - TransferTargetMode::ExistingAccount { - main_purse: purse_uref, - target_account_hash: target_account, - } => (Some(target_account), purse_uref), - TransferTargetMode::PurseExists { - target_account_hash, - purse_uref, - } => (target_account_hash, purse_uref), - TransferTargetMode::CreateAccount(_) => { - // Method "build()" is called after `resolve_transfer_target_mode` is first called - // and handled by creating a new account. Calling `resolve_transfer_target_mode` - // for the second time should never return `CreateAccount` variant. - return Err(TransferError::InvalidOperation); - } - }; + let (to, target) = match self + .resolve_transfer_target_mode(protocol_version, Rc::clone(&tracking_copy))? + { + TransferTargetMode::ExistingAccount { + main_purse: purse_uref, + target_account_hash: target_account, + } => (Some(target_account), purse_uref), + TransferTargetMode::PurseExists { + target_account_hash, + purse_uref, + } => (target_account_hash, purse_uref), + TransferTargetMode::CreateAccount(_) => { + // Method "build()" is called after `resolve_transfer_target_mode` is first called + // and handled by creating a new account. Calling `resolve_transfer_target_mode` + // for the second time should never return `CreateAccount` variant. + return Err(TransferError::InvalidOperation); + } + }; let source = self.resolve_source_uref(from, Rc::clone(&tracking_copy))?; From 0348298b6c4352ff43f69f8884a772c95ddcc268 Mon Sep 17 00:00:00 2001 From: igor-casper Date: Thu, 5 Dec 2024 18:47:48 +0100 Subject: [PATCH 4/8] conjoin new perms logic with the old one --- execution_engine/src/runtime/auction_internal.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/execution_engine/src/runtime/auction_internal.rs b/execution_engine/src/runtime/auction_internal.rs index b8268ec653..12081d29bf 100644 --- a/execution_engine/src/runtime/auction_internal.rs +++ b/execution_engine/src/runtime/auction_internal.rs @@ -394,9 +394,17 @@ where amount: U512, id: Option, ) -> Result, Error> { - if !(self.context.validate_uref(&source).is_ok() - || self.context.get_initiator() == PublicKey::System.to_account_hash()) - { + let is_main_purse_transfer = self + .context + .runtime_footprint() + .borrow() + .main_purse() + .expect("didnt have purse") + .addr() + == source.addr(); + let has_perms = is_main_purse_transfer + || (source.is_writeable() && self.context.validate_uref(&source).is_ok()); + if !(has_perms || self.context.get_initiator() == PublicKey::System.to_account_hash()) { return Err(Error::InvalidCaller); } From fd4007d1c12e7f2cde51ec4e7a6546a2973d5fb4 Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Fri, 6 Dec 2024 12:43:05 -0600 Subject: [PATCH 5/8] Update wasm lane settings --- resources/local/chainspec.toml.in | 8 ++++---- resources/production/chainspec.toml | 10 +++++----- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/resources/local/chainspec.toml.in b/resources/local/chainspec.toml.in index 5b199d6313..ed007d8d33 100644 --- a/resources/local/chainspec.toml.in +++ b/resources/local/chainspec.toml.in @@ -199,14 +199,14 @@ vm_casper_v2 = false # Note: For the given mainnet implementation we specially reserve the label 2 for install and upgrades and # the lane must be present and defined. # Different casper networks may not impose such a restriction. -# [1] -> Max transaction size in bytes for a given transaction in a certain lane +# [1] -> Max serialized length of the entire transaction in bytes for a given transaction in a certain lane # [2] -> Max args length size in bytes for a given transaction in a certain lane # [3] -> Transaction gas limit size in bytes for a given transaction in a certain lane # [4] -> The maximum number of transactions the lane can contain -native_mint_lane = [0, 1024, 1024, 65_000_000_000, 650] -native_auction_lane = [1, 2048, 2048, 2_500_000_000, 145] +native_mint_lane = [0, 2048, 1024, 2_500_000_000, 650] +native_auction_lane = [1, 3096, 2048, 2_500_000_000, 145] install_upgrade_lane = [2, 1_048_576, 2048, 100_000_000_000, 1] -wasm_lanes = [[3, 344_064, 1024, 100_000_000_000, 3], [4, 172_032, 1024, 50_000_000_000, 7], [5, 12_288, 512, 1_500_000_000, 15]] +wasm_lanes = [[3, 344_064, 1024, 100_000_000_000, 3], [4, 172_032, 1024, 50_000_000_000, 7], [5, 12_288, 512, 2_500_000_000, 25]] [transactions.deploy] # The maximum number of Motes allowed to be spent during payment. 0 means unlimited. diff --git a/resources/production/chainspec.toml b/resources/production/chainspec.toml index 43a55ac1c6..91aa8483e9 100644 --- a/resources/production/chainspec.toml +++ b/resources/production/chainspec.toml @@ -141,7 +141,7 @@ validator_credit_cap = [1, 5] # 'classic': senders of transaction self-specify how much they pay. # 'fixed': costs are fixed, per the cost table # 'prepaid': prepaid transaction (currently not supported) -pricing_handling = { type = 'fixed' } +pricing_handling = { type = 'classic' } # Does the network allow pre-payment for future # execution? Currently not supported. # @@ -207,14 +207,14 @@ vm_casper_v2 = false # Note: For the given mainnet implementation we specially reserve the label 2 for install and upgrades and # the lane must be present and defined. # Different casper networks may not impose such a restriction. -# [1] -> Max transaction size in bytes for a given transaction in a certain lane +# [1] -> Max serialized length of the entire transaction in bytes for a given transaction in a certain lane # [2] -> Max args length size in bytes for a given transaction in a certain lane # [3] -> Transaction gas limit size in bytes for a given transaction in a certain lane # [4] -> The maximum number of transactions the lane can contain -native_mint_lane = [0, 1024, 1024, 65_000_000_000, 650] -native_auction_lane = [1, 2048, 2048, 2_500_000_000, 145] +native_mint_lane = [0, 2048, 1024, 2_500_000_000, 650] +native_auction_lane = [1, 3096, 2048, 2_500_000_000, 145] install_upgrade_lane = [2, 1_048_576, 2048, 100_000_000_000, 1] -wasm_lanes = [[3, 344_064, 1024, 100_000_000_000, 3], [4, 172_032, 1024, 50_000_000_000, 7], [5, 12_288, 512, 1_500_000_000, 15]] +wasm_lanes = [[3, 344_064, 1024, 100_000_000_000, 3], [4, 172_032, 1024, 50_000_000_000, 7], [5, 12_288, 512, 2_500_000_000, 25]] [transactions.deploy] # The maximum number of Motes allowed to be spent during payment. 0 means unlimited. From c1b3f4c4f4c222f787f70cfcc123bc8660fedfdf Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Fri, 6 Dec 2024 12:44:59 -0600 Subject: [PATCH 6/8] Update price handling in test chainspec --- resources/local/chainspec.toml.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/resources/local/chainspec.toml.in b/resources/local/chainspec.toml.in index ed007d8d33..ebc014559b 100644 --- a/resources/local/chainspec.toml.in +++ b/resources/local/chainspec.toml.in @@ -134,7 +134,7 @@ validator_credit_cap = [1, 5] # 'classic': senders of transaction self-specify how much they pay. # 'fixed': costs are fixed, per the cost table # 'prepaid': prepaid transaction (currently not supported) -pricing_handling = { type = 'fixed' } +pricing_handling = { type = 'classic' } # Does the network allow pre-payment for future # execution? Currently not supported. # From 9b97f5e19064c20be14e5740f1264d28e98aafc9 Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Fri, 6 Dec 2024 14:16:12 -0600 Subject: [PATCH 7/8] Fix generation of test transactions --- .../components/transaction_acceptor/tests.rs | 21 ++++++++++++------- .../transaction/transaction_v1_builder.rs | 21 +++++++++++-------- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/node/src/components/transaction_acceptor/tests.rs b/node/src/components/transaction_acceptor/tests.rs index 9c92db6f52..dc795bedd0 100644 --- a/node/src/components/transaction_acceptor/tests.rs +++ b/node/src/components/transaction_acceptor/tests.rs @@ -36,9 +36,9 @@ use casper_types::{ global_state::TrieMerkleProof, testing::TestRng, Block, BlockV2, CLValue, Chainspec, ChainspecRawBytes, Contract, Deploy, EraId, HashAddr, - InvalidDeploy, InvalidTransaction, InvalidTransactionV1, Package, PricingMode, ProtocolVersion, - PublicKey, SecretKey, StoredValue, TestBlockBuilder, TimeDiff, Timestamp, Transaction, - TransactionConfig, TransactionRuntime, TransactionV1, URef, U512, + InvalidDeploy, InvalidTransaction, InvalidTransactionV1, Package, PricingHandling, PricingMode, + ProtocolVersion, PublicKey, SecretKey, StoredValue, TestBlockBuilder, TimeDiff, Timestamp, + Transaction, TransactionConfig, TransactionRuntime, TransactionV1, URef, U512, }; use super::*; @@ -595,10 +595,9 @@ impl TestScenario { } TestScenario::InvalidPricingModeForTransactionV1 => { let classic_mode_transaction = TransactionV1Builder::new_random(rng) - .with_pricing_mode(PricingMode::PaymentLimited { - payment_amount: 10000u64, - gas_price_tolerance: 1u8, - standard_payment: true, + .with_pricing_mode(PricingMode::Fixed { + gas_price_tolerance: 5, + additional_computation_factor: 0, }) .with_chain_name("casper-example") .build() @@ -1152,6 +1151,14 @@ async fn run_transaction_acceptor_without_timeout( let admin = SecretKey::random(rng); let (mut chainspec, chainspec_raw_bytes) = <(Chainspec, ChainspecRawBytes)>::from_resources("local"); + let mut chainspec = if let TestScenario::TooLowGasPriceToleranceForTransactionV1 = test_scenario + { + chainspec.with_pricing_handling(PricingHandling::Fixed); + chainspec + } else { + chainspec + }; + chainspec.core_config.administrators = iter::once(PublicKey::from(&admin)).collect(); let chainspec = Arc::new(chainspec); diff --git a/node/src/types/transaction/transaction_v1_builder.rs b/node/src/types/transaction/transaction_v1_builder.rs index 8be1ef73e6..df8ef856d0 100644 --- a/node/src/types/transaction/transaction_v1_builder.rs +++ b/node/src/types/transaction/transaction_v1_builder.rs @@ -107,9 +107,10 @@ impl<'a> TransactionV1Builder<'a> { /// The default time-to-live for transactions, i.e. 30 minutes. pub const DEFAULT_TTL: TimeDiff = TimeDiff::from_millis(30 * 60 * 1_000); /// The default pricing mode for v1 transactions, ie FIXED cost. - pub const DEFAULT_PRICING_MODE: PricingMode = PricingMode::Fixed { - gas_price_tolerance: 5, - additional_computation_factor: 0, + pub const DEFAULT_PRICING_MODE: PricingMode = PricingMode::PaymentLimited { + payment_amount: 2_500_000_000, + gas_price_tolerance: 3, + standard_payment: true, }; /// The default scheduling for transactions, i.e. `Standard`. pub const DEFAULT_SCHEDULING: TransactionScheduling = TransactionScheduling::Standard; @@ -384,9 +385,10 @@ impl<'a> TransactionV1Builder<'a> { target: fields.target, entry_point: fields.entry_point, scheduling: fields.scheduling, - pricing_mode: PricingMode::Fixed { - gas_price_tolerance: 5, - additional_computation_factor: 0, + pricing_mode: PricingMode::PaymentLimited { + payment_amount: 2_500_000_000, + gas_price_tolerance: 3, + standard_payment: true, }, initiator_addr: Some(InitiatorAddr::PublicKey(PublicKey::from(&secret_key))), secret_key: Some(secret_key), @@ -423,9 +425,10 @@ impl<'a> TransactionV1Builder<'a> { target, entry_point, scheduling, - pricing_mode: PricingMode::Fixed { - gas_price_tolerance: 5, - additional_computation_factor: 0, + pricing_mode: PricingMode::PaymentLimited { + payment_amount: 2_500_000_000, + gas_price_tolerance: 3, + standard_payment: true, }, initiator_addr: Some(InitiatorAddr::PublicKey(PublicKey::from(&secret_key))), secret_key: Some(secret_key), From 120476097634398b50b338b445d206d4bc9bfa4b Mon Sep 17 00:00:00 2001 From: Karan Dhareshwar Date: Fri, 6 Dec 2024 17:21:54 -0600 Subject: [PATCH 8/8] Fix transaction reactor tests --- .../main_reactor/tests/transactions.rs | 67 +++++++++---------- .../transaction/transaction_v1_builder.rs | 2 +- types/src/transaction/transaction_v1.rs | 10 +++ 3 files changed, 44 insertions(+), 35 deletions(-) diff --git a/node/src/reactor/main_reactor/tests/transactions.rs b/node/src/reactor/main_reactor/tests/transactions.rs index b3f28589c2..b60d7ac3db 100644 --- a/node/src/reactor/main_reactor/tests/transactions.rs +++ b/node/src/reactor/main_reactor/tests/transactions.rs @@ -32,7 +32,7 @@ static CHARLIE_SECRET_KEY: Lazy> = Lazy::new(|| { static CHARLIE_PUBLIC_KEY: Lazy = Lazy::new(|| PublicKey::from(&*CHARLIE_SECRET_KEY.clone())); -const MIN_GAS_PRICE: u8 = 5; +const MIN_GAS_PRICE: u8 = 1; const CHAIN_NAME: &str = "single-transaction-test-net"; async fn transfer_to_account>( @@ -1023,8 +1023,6 @@ impl SingleTransactionTestCase { ConfigsOverride::default() .with_minimum_era_height(5) // make the era longer so that the transaction doesn't land in the switch block. .with_balance_hold_interval(TimeDiff::from_seconds(5)) - .with_min_gas_price(MIN_GAS_PRICE) - .with_max_gas_price(MIN_GAS_PRICE) .with_chain_name("single-transaction-test-net".to_string()) } @@ -3045,25 +3043,27 @@ async fn insufficient_funds_transfer_from_account() { let transfer_amount = U512::max_value(); - let mut txn = Transaction::from( + let txn_v1 = TransactionV1Builder::new_transfer(transfer_amount, None, ALICE_PUBLIC_KEY.clone(), None) .unwrap() .with_chain_name(CHAIN_NAME) .with_initiator_addr(PublicKey::from(&**BOB_SECRET_KEY)) .build() - .unwrap(), - ); + .unwrap(); + let price = txn_v1 + .payment_amount() + .expect("must have payment amount as txns are using classic"); + let mut txn = Transaction::from(txn_v1); txn.sign(&BOB_SECRET_KEY); let (_txn_hash, _block_height, exec_result) = test.send_transaction(txn).await; let ExecutionResult::V2(result) = exec_result else { panic!("Expected ExecutionResult::V2 but got {:?}", exec_result); }; - let transfer_cost: U512 = - U512::from(test.chainspec().system_costs_config.mint_costs().transfer) * MIN_GAS_PRICE; + let expected_cost: U512 = U512::from(price) * MIN_GAS_PRICE; assert_eq!(result.error_message.as_deref(), Some("Insufficient funds")); - assert_eq!(result.cost, transfer_cost); + assert_eq!(result.cost, expected_cost); } #[tokio::test] @@ -3089,22 +3089,22 @@ async fn insufficient_funds_add_bid() { let (_, bob_initial_balance, _) = test.get_balances(None); let bid_amount = bob_initial_balance.total; - let mut txn = Transaction::from( + let txn = TransactionV1Builder::new_add_bid(BOB_PUBLIC_KEY.clone(), 0, bid_amount, None, None, None) .unwrap() .with_chain_name(CHAIN_NAME) .with_initiator_addr(PublicKey::from(&**BOB_SECRET_KEY)) .build() - .unwrap(), - ); + .unwrap(); + let price = txn.payment_amount().expect("must get payment amount"); + let mut txn = Transaction::from(txn); txn.sign(&BOB_SECRET_KEY); let (_txn_hash, _block_height, exec_result) = test.send_transaction(txn).await; let ExecutionResult::V2(result) = exec_result else { panic!("Expected ExecutionResult::V2 but got {:?}", exec_result); }; - let bid_cost: U512 = - U512::from(test.chainspec().system_costs_config.auction_costs().add_bid) * MIN_GAS_PRICE; + let bid_cost: U512 = U512::from(price) * MIN_GAS_PRICE; assert_eq!( result.error_message.as_deref(), @@ -3175,27 +3175,26 @@ async fn insufficient_funds_transfer_from_purse() { // now we try to transfer from the purse we just created let transfer_amount = U512::max_value(); - let mut txn = Transaction::from( - TransactionV1Builder::new_transfer( - transfer_amount, - Some(uref), - ALICE_PUBLIC_KEY.clone(), - None, - ) - .unwrap() - .with_chain_name(CHAIN_NAME) - .with_initiator_addr(PublicKey::from(&**BOB_SECRET_KEY)) - .build() - .unwrap(), - ); + let txn = TransactionV1Builder::new_transfer( + transfer_amount, + Some(uref), + ALICE_PUBLIC_KEY.clone(), + None, + ) + .unwrap() + .with_chain_name(CHAIN_NAME) + .with_initiator_addr(PublicKey::from(&**BOB_SECRET_KEY)) + .build() + .unwrap(); + let price = txn.payment_amount().expect("must get payment amount"); + let mut txn = Transaction::from(txn); txn.sign(&BOB_SECRET_KEY); let (_txn_hash, _block_height, exec_result) = test.send_transaction(txn).await; let ExecutionResult::V2(result) = exec_result else { panic!("Expected ExecutionResult::V2 but got {:?}", exec_result); }; - let transfer_cost: U512 = - U512::from(test.chainspec().system_costs_config.mint_costs().transfer) * MIN_GAS_PRICE; + let transfer_cost: U512 = U512::from(price) * MIN_GAS_PRICE; assert_eq!(result.error_message.as_deref(), Some("Insufficient funds")); assert_eq!(result.cost, transfer_cost); @@ -3223,22 +3222,22 @@ async fn insufficient_funds_when_caller_lacks_minimum_balance() { let (_, bob_initial_balance, _) = test.get_balances(None); let transfer_amount = bob_initial_balance.total - U512::one(); - let mut txn = Transaction::from( + let txn = TransactionV1Builder::new_transfer(transfer_amount, None, ALICE_PUBLIC_KEY.clone(), None) .unwrap() .with_chain_name(CHAIN_NAME) .with_initiator_addr(PublicKey::from(&**BOB_SECRET_KEY)) .build() - .unwrap(), - ); + .unwrap(); + let price = txn.payment_amount().expect("must get payment amount"); + let mut txn = Transaction::from(txn); txn.sign(&BOB_SECRET_KEY); let (_txn_hash, _block_height, exec_result) = test.send_transaction(txn).await; let ExecutionResult::V2(result) = exec_result else { panic!("Expected ExecutionResult::V2 but got {:?}", exec_result); }; - let transfer_cost: U512 = - U512::from(test.chainspec().system_costs_config.mint_costs().transfer) * MIN_GAS_PRICE; + let transfer_cost: U512 = U512::from(price) * MIN_GAS_PRICE; assert_eq!(result.error_message.as_deref(), Some("Insufficient funds")); assert_eq!(result.cost, transfer_cost); diff --git a/node/src/types/transaction/transaction_v1_builder.rs b/node/src/types/transaction/transaction_v1_builder.rs index df8ef856d0..e793319d3f 100644 --- a/node/src/types/transaction/transaction_v1_builder.rs +++ b/node/src/types/transaction/transaction_v1_builder.rs @@ -108,7 +108,7 @@ impl<'a> TransactionV1Builder<'a> { pub const DEFAULT_TTL: TimeDiff = TimeDiff::from_millis(30 * 60 * 1_000); /// The default pricing mode for v1 transactions, ie FIXED cost. pub const DEFAULT_PRICING_MODE: PricingMode = PricingMode::PaymentLimited { - payment_amount: 2_500_000_000, + payment_amount: 10_000_000_000, gas_price_tolerance: 3, standard_payment: true, }; diff --git a/types/src/transaction/transaction_v1.rs b/types/src/transaction/transaction_v1.rs index 47c28fa4d7..9508fb9eb0 100644 --- a/types/src/transaction/transaction_v1.rs +++ b/types/src/transaction/transaction_v1.rs @@ -281,6 +281,16 @@ impl TransactionV1 { self.approvals.extend(approvals); } + /// Returns the payment amount if the txn is using classic mode. + #[cfg(any(all(feature = "std", feature = "testing"), test))] + pub fn payment_amount(&self) -> Option { + if let PricingMode::PaymentLimited { payment_amount, .. } = self.pricing_mode() { + Some(*payment_amount) + } else { + None + } + } + /// Returns a random, valid but possibly expired transaction. #[cfg(any(all(feature = "std", feature = "testing"), test))] pub fn random(rng: &mut TestRng) -> Self {