From 950d1858788541eb41d0def2a61e48c6eadcc408 Mon Sep 17 00:00:00 2001 From: David Edey Date: Fri, 25 Oct 2024 03:54:55 +0100 Subject: [PATCH] tweak: Standardize resource assertion errors --- .../model/manifest_resource_assertion.rs | 346 +++++++++++------- radix-common/src/math/bnum_integer.rs | 33 +- radix-common/src/math/bnum_integer/test.rs | 2 + .../tests/system/assert_bucket_contents.rs | 76 ++-- .../tests/system/assert_next_call_returns.rs | 39 +- .../tests/system/assert_worktop_resources.rs | 41 ++- radix-engine-tests/tests/system/fee.rs | 20 +- .../src/blueprints/resource/worktop.rs | 86 +++-- radix-engine/src/errors.rs | 2 +- .../src/system/transaction/instructions.rs | 12 +- .../system/transaction/intent_processor.rs | 87 ++--- ...local_transaction_execution_cuttlefish.bin | Bin 35681 -> 35335 bytes radix-rust/src/rust.rs | 9 +- 13 files changed, 394 insertions(+), 359 deletions(-) diff --git a/radix-common/src/data/manifest/model/manifest_resource_assertion.rs b/radix-common/src/data/manifest/model/manifest_resource_assertion.rs index 11d07c36817..ee975ed8bd0 100644 --- a/radix-common/src/data/manifest/model/manifest_resource_assertion.rs +++ b/radix-common/src/data/manifest/model/manifest_resource_assertion.rs @@ -1,3 +1,5 @@ +use core::ops::AddAssign; + use crate::internal_prelude::*; // This file isn't part of the Manifest SBOR value model, but is included here @@ -151,42 +153,60 @@ impl ManifestResourceConstraints { pub fn validate( self, - mut fungible_resources: IndexMap, - mut non_fungible_resources: IndexMap>, - exact: bool, - ) -> Result<(), ManifestResourceConstraintsError> { + balances: AggregateResourceBalances, + prevent_unspecified_resource_balances: bool, + ) -> Result<(), ResourceConstraintsError> { + let AggregateResourceBalances { + fungible_resources, + non_fungible_resources, + } = balances; + + if prevent_unspecified_resource_balances { + for (resource_address, amount) in fungible_resources.iter() { + if !self.specified_resources.contains_key(resource_address) && amount.is_positive() + { + return Err( + ResourceConstraintsError::UnexpectedNonZeroBalanceOfUnspecifiedResource { + resource_address: *resource_address, + }, + ); + } + } + + for (resource_address, ids) in non_fungible_resources.iter() { + if !self.specified_resources.contains_key(resource_address) && !ids.is_empty() { + return Err( + ResourceConstraintsError::UnexpectedNonZeroBalanceOfUnspecifiedResource { + resource_address: *resource_address, + }, + ); + } + } + } + + let zero_balance = Decimal::ZERO; + let empty_ids: IndexSet = Default::default(); for (resource_address, constraint) in self.specified_resources { if resource_address.is_fungible() { let amount = fungible_resources - .swap_remove(&resource_address) - .unwrap_or_default(); - constraint - .validate_fungible(amount) - .map_err(ManifestResourceConstraintsError::ResourceConstraint)?; + .get(&resource_address) + .unwrap_or(&zero_balance); + constraint.validate_fungible(*amount).map_err(|error| { + ResourceConstraintsError::ResourceConstraintFailed { + resource_address, + error, + } + })?; } else { let ids = non_fungible_resources - .swap_remove(&resource_address) - .unwrap_or_default(); - constraint - .validate_non_fungible(ids) - .map_err(ManifestResourceConstraintsError::ResourceConstraint)?; - } - } - - if exact { - for (fungible_resource, amount) in fungible_resources { - if amount.is_positive() { - return Err(ManifestResourceConstraintsError::UnwantedResourcesExist( - fungible_resource, - )); - } - } - for (non_fungible_resource, ids) in non_fungible_resources { - if !ids.is_empty() { - return Err(ManifestResourceConstraintsError::UnwantedResourcesExist( - non_fungible_resource, - )); - } + .get(&resource_address) + .unwrap_or(&empty_ids); + constraint.validate_non_fungible(ids).map_err(|error| { + ResourceConstraintsError::ResourceConstraintFailed { + resource_address, + error, + } + })?; } } @@ -194,10 +214,54 @@ impl ManifestResourceConstraints { } } -#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] -pub enum ManifestResourceConstraintsError { - ResourceConstraint(ResourceConstraintError), - UnwantedResourcesExist(ResourceAddress), +pub struct AggregateResourceBalances { + fungible_resources: IndexMap, + non_fungible_resources: IndexMap>, +} + +impl AggregateResourceBalances { + pub fn new() -> Self { + Self { + fungible_resources: Default::default(), + non_fungible_resources: Default::default(), + } + } + + pub fn add_fungible(&mut self, resource_address: ResourceAddress, amount: Decimal) { + if amount.is_positive() { + self.fungible_resources + .entry(resource_address) + .or_default() + .add_assign(amount); + } + } + + pub fn add_non_fungible( + &mut self, + resource_address: ResourceAddress, + ids: IndexSet, + ) { + if !ids.is_empty() { + self.non_fungible_resources + .entry(resource_address) + .or_default() + .extend(ids); + } + } + + pub fn validate_only( + self, + constraints: ManifestResourceConstraints, + ) -> Result<(), ResourceConstraintsError> { + constraints.validate(self, true) + } + + pub fn validate_includes( + self, + constraints: ManifestResourceConstraints, + ) -> Result<(), ResourceConstraintsError> { + constraints.validate(self, false) + } } impl IntoIterator for ManifestResourceConstraints { @@ -257,7 +321,7 @@ impl ManifestResourceConstraint { pub fn validate_non_fungible( self, - ids: IndexSet, + ids: &IndexSet, ) -> Result<(), ResourceConstraintError> { let amount = Decimal::from(ids.len()); match self { @@ -270,7 +334,7 @@ impl ManifestResourceConstraint { if amount.ne(&expected_exact_amount) { return Err(ResourceConstraintError::ExpectedExactAmount { actual_amount: amount, - expected_exact_amount, + expected_amount: expected_exact_amount, }); } } @@ -283,25 +347,26 @@ impl ManifestResourceConstraint { } } ManifestResourceConstraint::ExactNonFungibles(expected_exact_ids) => { - if !expected_exact_ids.eq(&ids) { - return Err(ResourceConstraintError::ExpectedExactNonFungibles { - expected_exact_ids: Box::new(expected_exact_ids), - actual_ids: Box::new(ids), + if let Some(missing_id) = expected_exact_ids.difference(ids).next() { + return Err(ResourceConstraintError::NonFungibleMissing { + missing_id: missing_id.clone(), + }); + } + if let Some(disallowed_id) = ids.difference(&expected_exact_ids).next() { + return Err(ResourceConstraintError::NonFungibleNotAllowed { + disallowed_id: disallowed_id.clone(), }); } } ManifestResourceConstraint::AtLeastNonFungibles(expected_at_least_ids) => { - if !expected_at_least_ids.is_subset(&ids) { - return Err(ResourceConstraintError::ExpectedAtLeastNonFungibles { - actual_ids: Box::new(ids), - expected_at_least_ids: Box::new(expected_at_least_ids.clone()), + if let Some(missing_id) = expected_at_least_ids.difference(ids).next() { + return Err(ResourceConstraintError::NonFungibleMissing { + missing_id: missing_id.clone(), }); } } ManifestResourceConstraint::General(constraint) => { - constraint - .validate_non_fungible(&ids) - .map_err(ResourceConstraintError::GeneralResourceConstraintError)?; + constraint.validate_non_fungible_ids(ids)?; } } @@ -319,7 +384,7 @@ impl ManifestResourceConstraint { if amount.ne(&expected_exact_amount) { return Err(ResourceConstraintError::ExpectedExactAmount { actual_amount: amount, - expected_exact_amount, + expected_amount: expected_exact_amount, }); } } @@ -332,15 +397,17 @@ impl ManifestResourceConstraint { } } ManifestResourceConstraint::ExactNonFungibles(..) => { - return Err(ResourceConstraintError::ExpectedNonFungibleResourceButIsFungible); + return Err( + ResourceConstraintError::NonFungibleConstraintNotValidForFungibleResource, + ); } ManifestResourceConstraint::AtLeastNonFungibles(..) => { - return Err(ResourceConstraintError::ExpectedNonFungibleResourceButIsFungible); + return Err( + ResourceConstraintError::NonFungibleConstraintNotValidForFungibleResource, + ); } ManifestResourceConstraint::General(constraint) => { - constraint - .validate_fungible(amount) - .map_err(ResourceConstraintError::GeneralResourceConstraintError)?; + constraint.validate_fungible(amount)?; } } @@ -348,27 +415,43 @@ impl ManifestResourceConstraint { } } +#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] +pub enum ResourceConstraintsError { + UnexpectedNonZeroBalanceOfUnspecifiedResource { + resource_address: ResourceAddress, + }, + ResourceConstraintFailed { + resource_address: ResourceAddress, + error: ResourceConstraintError, + }, +} + #[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] pub enum ResourceConstraintError { + NonFungibleConstraintNotValidForFungibleResource, ExpectedNonZeroAmount, ExpectedExactAmount { - expected_exact_amount: Decimal, + expected_amount: Decimal, actual_amount: Decimal, }, ExpectedAtLeastAmount { expected_at_least_amount: Decimal, actual_amount: Decimal, }, - ExpectedExactNonFungibles { - expected_exact_ids: Box>, - actual_ids: Box>, + ExpectedAtMostAmount { + expected_at_most_amount: Decimal, + actual_amount: Decimal, + }, + // We purposefully don't have an `ExpectedExactNonFungibles` to avoid + // a malicious transaction creating a 2MB native error with a massive + // list of required non-fungibles. Instead, we return one of + // `RequiredNonFungibleMissing` or `NonFungibleNotAllowed`. + NonFungibleMissing { + missing_id: NonFungibleLocalId, }, - ExpectedAtLeastNonFungibles { - expected_at_least_ids: Box>, - actual_ids: Box>, + NonFungibleNotAllowed { + disallowed_id: NonFungibleLocalId, }, - GeneralResourceConstraintError(GeneralResourceConstraintError), - ExpectedNonFungibleResourceButIsFungible, } /// [`GeneralResourceConstraint`] captures constraints on the balance of a single fungible @@ -508,73 +591,32 @@ impl GeneralResourceConstraint { && self.is_valid_independent_of_resource_type() } - pub fn validate_fungible(&self, amount: Decimal) -> Result<(), GeneralResourceConstraintError> { + pub fn validate_fungible(&self, amount: Decimal) -> Result<(), ResourceConstraintError> { self.validate_amount(amount)?; // Static checker should have validated that there are no invalid non fungible checks Ok(()) } - pub fn validate_non_fungible( + pub fn validate_non_fungible_ids( &self, ids: &IndexSet, - ) -> Result<(), GeneralResourceConstraintError> { + ) -> Result<(), ResourceConstraintError> { self.validate_amount(Decimal::from(ids.len()))?; - for id in &self.required_ids { - if !ids.contains(id) { - return Err(GeneralResourceConstraintError::MissingRequiredNonFungible { - missing_id: id.clone(), - }); - } - } - match &self.allowed_ids { - AllowedIds::Allowlist(allowed) => { - for id in ids { - if !allowed.contains(id) { - return Err(GeneralResourceConstraintError::InvalidNonFungible { - invalid_id: id.clone(), - }); - } - } - } - AllowedIds::Any => {} + if let Some(missing_id) = self.required_ids.difference(ids).next() { + return Err(ResourceConstraintError::NonFungibleMissing { + missing_id: missing_id.clone(), + }); } + self.allowed_ids.validate_ids(ids)?; + Ok(()) } - fn validate_amount(&self, amount: Decimal) -> Result<(), GeneralResourceConstraintError> { - match self.lower_bound { - LowerBound::NonZero => { - if amount.is_zero() { - return Err(GeneralResourceConstraintError::ExpectedNonZeroAmount); - } - } - LowerBound::Inclusive(inclusive) => { - if amount < inclusive { - return Err( - GeneralResourceConstraintError::LowerBoundAmountNotSatisfied { - lower_bound_inclusive: inclusive, - actual: amount, - }, - ); - } - } - } - match self.upper_bound { - UpperBound::Inclusive(inclusive) => { - if amount > inclusive { - return Err( - GeneralResourceConstraintError::UpperBoundAmountNotSatisfied { - upper_bound_inclusive: inclusive, - actual: amount, - }, - ); - } - } - UpperBound::Unbounded => {} - } - + fn validate_amount(&self, amount: Decimal) -> Result<(), ResourceConstraintError> { + self.lower_bound.validate_amount(&amount)?; + self.upper_bound.validate_amount(&amount)?; Ok(()) } @@ -644,25 +686,6 @@ impl GeneralResourceConstraint { } } -#[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] -pub enum GeneralResourceConstraintError { - ExpectedNonZeroAmount, - LowerBoundAmountNotSatisfied { - lower_bound_inclusive: Decimal, - actual: Decimal, - }, - UpperBoundAmountNotSatisfied { - upper_bound_inclusive: Decimal, - actual: Decimal, - }, - MissingRequiredNonFungible { - missing_id: NonFungibleLocalId, - }, - InvalidNonFungible { - invalid_id: NonFungibleLocalId, - }, -} - /// Represents a lower bound on a non-negative decimal. /// /// [`LowerBound::NonZero`] represents a lower bound of an infinitesimal amount above 0, @@ -738,6 +761,25 @@ impl LowerBound { lower_bound.resolve() } + pub fn validate_amount(&self, amount: &Decimal) -> Result<(), ResourceConstraintError> { + match self { + LowerBound::NonZero => { + if amount.is_zero() { + return Err(ResourceConstraintError::ExpectedNonZeroAmount); + } + } + LowerBound::Inclusive(inclusive) => { + if amount < inclusive { + return Err(ResourceConstraintError::ExpectedAtLeastAmount { + expected_at_least_amount: *inclusive, + actual_amount: *amount, + }); + } + } + } + Ok(()) + } + pub fn is_valid_for_fungible_use(&self) -> bool { match self { LowerBound::NonZero => true, @@ -906,6 +948,21 @@ impl UpperBound { upper_bound.resolve() } + pub fn validate_amount(&self, amount: &Decimal) -> Result<(), ResourceConstraintError> { + match self { + UpperBound::Inclusive(inclusive) => { + if amount > inclusive { + return Err(ResourceConstraintError::ExpectedAtMostAmount { + expected_at_most_amount: *inclusive, + actual_amount: *amount, + }); + } + } + UpperBound::Unbounded => {} + } + Ok(()) + } + pub fn is_valid_for_fungible_use(&self) -> bool { match self { UpperBound::Inclusive(amount) => !amount.is_negative(), @@ -997,6 +1054,25 @@ impl AllowedIds { Self::Any } + pub fn validate_ids( + &self, + ids: &IndexSet, + ) -> Result<(), ResourceConstraintError> { + match self { + AllowedIds::Allowlist(allowed) => { + for id in ids { + if !allowed.contains(id) { + return Err(ResourceConstraintError::NonFungibleNotAllowed { + disallowed_id: id.clone(), + }); + } + } + } + AllowedIds::Any => {} + } + Ok(()) + } + pub fn allowlist_equivalent_length(&self) -> usize { match self { Self::Allowlist(allowlist) => allowlist.len(), diff --git a/radix-common/src/math/bnum_integer.rs b/radix-common/src/math/bnum_integer.rs index 2551e89f263..ad38823d26e 100644 --- a/radix-common/src/math/bnum_integer.rs +++ b/radix-common/src/math/bnum_integer.rs @@ -8,7 +8,7 @@ use num_bigint::BigInt; use num_integer::Roots; use num_traits::{FromPrimitive, One, Pow, ToPrimitive, Zero}; use paste::paste; -use sbor::rust::cmp::{Ord, Ordering, PartialEq, PartialOrd}; +use sbor::rust::cmp::{Ord, PartialEq, PartialOrd}; use sbor::rust::convert::{From, TryFrom}; use sbor::rust::fmt; use sbor::rust::ops::{Add, AddAssign, BitAnd, BitAndAssign, BitOr, BitOrAssign}; @@ -48,7 +48,7 @@ macro_rules! types { #[doc = "`" $t "` will have the same methods and traits as"] /// the built-in counterpart. #[cfg_attr(feature = "fuzzing", derive(Arbitrary, Serialize, Deserialize))] - #[derive(Clone , Copy)] + #[derive(Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] #[repr(transparent)] pub struct $t(pub $wrap); @@ -100,35 +100,6 @@ macro_rules! types { Self::ONE } } - - impl Ord for $t { - fn cmp(&self, other: &Self) -> Ordering { - self.0.cmp(&other.0) - } - } - - impl PartialOrd for $t { - fn partial_cmp(&self, other: &Self) -> Option { - Some(self.cmp(other)) - } - } - - // The following three trait implementations must be aligned. - - impl PartialEq for $t { - fn eq(&self, other: &Self) -> bool { - self.0.eq(&other.0) - } - } - - impl Eq for $t { - } - - impl sbor::rust::hash::Hash for $t { - fn hash(&self, state: &mut H) where H: sbor::rust::hash::Hasher { - self.0.hash(state) - } - } )* } }; diff --git a/radix-common/src/math/bnum_integer/test.rs b/radix-common/src/math/bnum_integer/test.rs index 34209459956..32df740d256 100644 --- a/radix-common/src/math/bnum_integer/test.rs +++ b/radix-common/src/math/bnum_integer/test.rs @@ -6,6 +6,8 @@ use paste::paste; use num_bigint::{BigInt, Sign}; use radix_common::*; +#[allow(unused_imports)] // It's needed by the `test_impl!` macro +use sbor::rust::cmp::Ordering; test_impl! {I192, I256, I320, I384, I448, I512, I768} test_impl! {U192, U256, U320, U384, U448, U512, U768} diff --git a/radix-engine-tests/tests/system/assert_bucket_contents.rs b/radix-engine-tests/tests/system/assert_bucket_contents.rs index babdc85f0ca..e23f6a10563 100644 --- a/radix-engine-tests/tests/system/assert_bucket_contents.rs +++ b/radix-engine-tests/tests/system/assert_bucket_contents.rs @@ -33,7 +33,7 @@ fn asserting_incorrect_exact_amount_should_fail() { ManifestResourceConstraint::ExactAmount(expected_exact_amount), Some(ResourceConstraintError::ExpectedExactAmount { actual_amount, - expected_exact_amount, + expected_amount: expected_exact_amount, }), ) } @@ -79,14 +79,8 @@ fn asserting_incorrect_at_least_non_fungibles_should_fail() { test_non_fungible_constraint( actual_ids.clone(), ManifestResourceConstraint::AtLeastNonFungibles(expected_at_least_ids.clone()), - Some(ResourceConstraintError::ExpectedAtLeastNonFungibles { - actual_ids: Box::new( - actual_ids - .into_iter() - .map(NonFungibleLocalId::from) - .collect(), - ), - expected_at_least_ids: Box::new(expected_at_least_ids), + Some(ResourceConstraintError::NonFungibleMissing { + missing_id: NonFungibleLocalId::from(2), }), ) } @@ -115,14 +109,8 @@ fn asserting_incorrect_exact_non_fungibles_should_fail() { test_non_fungible_constraint( actual_ids.clone(), ManifestResourceConstraint::ExactNonFungibles(expected_exact_ids.clone()), - Some(ResourceConstraintError::ExpectedExactNonFungibles { - actual_ids: Box::new( - actual_ids - .into_iter() - .map(NonFungibleLocalId::from) - .collect(), - ), - expected_exact_ids: Box::new(expected_exact_ids), + Some(ResourceConstraintError::NonFungibleMissing { + missing_id: NonFungibleLocalId::from(3), }), ) } @@ -203,19 +191,17 @@ fn asserting_incorrect_fungible_lower_bound_general_constraint_should_fail() { let lower_bound = dec!(10) + Decimal::from_attos(I192::ONE); let constraint = GeneralResourceConstraint { required_ids: Default::default(), - allowed_ids: AllowedIds::Any, lower_bound: LowerBound::Inclusive(lower_bound), upper_bound: UpperBound::Unbounded, + allowed_ids: AllowedIds::Any, }; test_fungible_constraint( amount, ManifestResourceConstraint::General(constraint), - Some(ResourceConstraintError::GeneralResourceConstraintError( - GeneralResourceConstraintError::LowerBoundAmountNotSatisfied { - actual: amount, - lower_bound_inclusive: lower_bound, - }, - )), + Some(ResourceConstraintError::ExpectedAtLeastAmount { + expected_at_least_amount: lower_bound, + actual_amount: amount, + }), ); } @@ -225,19 +211,17 @@ fn asserting_incorrect_fungible_upper_bound_general_constraint_should_fail() { let upper_bound = dec!(10) - Decimal::from_attos(I192::ONE); let constraint = GeneralResourceConstraint { required_ids: Default::default(), - allowed_ids: AllowedIds::Any, lower_bound: LowerBound::Inclusive(Decimal::zero()), upper_bound: UpperBound::Inclusive(upper_bound), + allowed_ids: AllowedIds::Any, }; test_fungible_constraint( amount, ManifestResourceConstraint::General(constraint), - Some(ResourceConstraintError::GeneralResourceConstraintError( - GeneralResourceConstraintError::UpperBoundAmountNotSatisfied { - actual: amount, - upper_bound_inclusive: upper_bound, - }, - )), + Some(ResourceConstraintError::ExpectedAtMostAmount { + expected_at_most_amount: upper_bound, + actual_amount: amount, + }), ); } @@ -246,18 +230,16 @@ fn asserting_incorrect_non_fungible_required_ids_general_constraint_should_fail( let actual_ids = vec![1, 2]; let constraint = GeneralResourceConstraint { required_ids: indexset!(NonFungibleLocalId::from(3)), - allowed_ids: AllowedIds::Any, lower_bound: LowerBound::Inclusive(Decimal::from(1)), upper_bound: UpperBound::Unbounded, + allowed_ids: AllowedIds::Any, }; test_non_fungible_constraint( actual_ids.clone(), ManifestResourceConstraint::General(constraint), - Some(ResourceConstraintError::GeneralResourceConstraintError( - GeneralResourceConstraintError::MissingRequiredNonFungible { - missing_id: NonFungibleLocalId::from(3), - }, - )), + Some(ResourceConstraintError::NonFungibleMissing { + missing_id: NonFungibleLocalId::from(3), + }), ); } @@ -266,21 +248,19 @@ fn asserting_incorrect_non_fungible_allowed_ids_general_constraint_should_fail() let actual_ids = vec![1, 3]; let constraint = GeneralResourceConstraint { required_ids: indexset!(), + lower_bound: LowerBound::NonZero, + upper_bound: UpperBound::Inclusive(Decimal::from(2)), allowed_ids: AllowedIds::Allowlist(indexset!( NonFungibleLocalId::from(3), NonFungibleLocalId::from(4) )), - lower_bound: LowerBound::NonZero, - upper_bound: UpperBound::Inclusive(Decimal::from(2)), }; test_non_fungible_constraint( actual_ids.clone(), ManifestResourceConstraint::General(constraint), - Some(ResourceConstraintError::GeneralResourceConstraintError( - GeneralResourceConstraintError::InvalidNonFungible { - invalid_id: NonFungibleLocalId::from(1), - }, - )), + Some(ResourceConstraintError::NonFungibleNotAllowed { + disallowed_id: NonFungibleLocalId::from(1), + }), ); } @@ -295,9 +275,9 @@ fn asserting_correct_empty_bucket_general_constraints_should_succeed() { for allowed_ids in &allowed_ids_list { let constraint = GeneralResourceConstraint { required_ids: Default::default(), - allowed_ids: allowed_ids.clone(), lower_bound: LowerBound::Inclusive(amount), upper_bound, + allowed_ids: allowed_ids.clone(), }; test_fungible_constraint( amount, @@ -313,16 +293,14 @@ fn asserting_incorrect_empty_bucket_lower_bound_general_constraint_should_fail() let amount = dec!(0); let constraint = GeneralResourceConstraint { required_ids: Default::default(), - allowed_ids: AllowedIds::Any, lower_bound: LowerBound::NonZero, upper_bound: UpperBound::Unbounded, + allowed_ids: AllowedIds::Any, }; test_fungible_constraint( amount, ManifestResourceConstraint::General(constraint), - Some(ResourceConstraintError::GeneralResourceConstraintError( - GeneralResourceConstraintError::ExpectedNonZeroAmount, - )), + Some(ResourceConstraintError::ExpectedNonZeroAmount), ); } diff --git a/radix-engine-tests/tests/system/assert_next_call_returns.rs b/radix-engine-tests/tests/system/assert_next_call_returns.rs index c5e0193ab28..2b2160e7911 100644 --- a/radix-engine-tests/tests/system/assert_next_call_returns.rs +++ b/radix-engine-tests/tests/system/assert_next_call_returns.rs @@ -37,9 +37,11 @@ fn when_more_is_returned_assert_next_call_returns_only_should_fail() { true, next_call_type, |_, resource2| { - Some(ManifestResourceConstraintsError::UnwantedResourcesExist( - resource2, - )) + Some( + ResourceConstraintsError::UnexpectedNonZeroBalanceOfUnspecifiedResource { + resource_address: resource2, + }, + ) }, ); } @@ -104,12 +106,13 @@ fn when_less_is_returned_assert_next_call_returns_include_should_fail() { false, next_call_type, |_, _| { - Some(ManifestResourceConstraintsError::ResourceConstraint( - ResourceConstraintError::ExpectedAtLeastAmount { + Some(ResourceConstraintsError::ResourceConstraintFailed { + resource_address: XRD, + error: ResourceConstraintError::ExpectedAtLeastAmount { expected_at_least_amount: dec!(1), actual_amount: dec!(0), }, - )) + }) }, ); } @@ -129,13 +132,12 @@ fn when_less_is_returned_assert_next_call_returns_only_should_fail() { }, true, next_call_type, - |_, _| { - Some(ManifestResourceConstraintsError::ResourceConstraint( - ResourceConstraintError::ExpectedAtLeastAmount { - expected_at_least_amount: dec!(1), - actual_amount: dec!(0), + |_, resource2| { + Some( + ResourceConstraintsError::UnexpectedNonZeroBalanceOfUnspecifiedResource { + resource_address: resource2, }, - )) + ) }, ); } @@ -161,9 +163,11 @@ fn when_empty_constraints_on_assert_next_call_returns_only_should_fail() { true, next_call_type, |resource1, _resource2| { - Some(ManifestResourceConstraintsError::UnwantedResourcesExist( - resource1, - )) + Some( + ResourceConstraintsError::UnexpectedNonZeroBalanceOfUnspecifiedResource { + resource_address: resource1, + }, + ) }, ); } @@ -275,10 +279,7 @@ fn run_return_two_resources_test( constraints: fn(ResourceAddress, ResourceAddress) -> ManifestResourceConstraints, exact: bool, next_call_type: NextCallType, - expected_result: fn( - ResourceAddress, - ResourceAddress, - ) -> Option, + expected_result: fn(ResourceAddress, ResourceAddress) -> Option, ) { // Arrange let mut ledger = LedgerSimulatorBuilder::new().build(); diff --git a/radix-engine-tests/tests/system/assert_worktop_resources.rs b/radix-engine-tests/tests/system/assert_worktop_resources.rs index e408ef12c60..22bd0b5bfea 100644 --- a/radix-engine-tests/tests/system/assert_worktop_resources.rs +++ b/radix-engine-tests/tests/system/assert_worktop_resources.rs @@ -25,9 +25,11 @@ fn when_more_is_returned_assert_worktop_resources_only_should_fail() { }, true, |_, resource2| { - Some(ManifestResourceConstraintsError::UnwantedResourcesExist( - resource2, - )) + Some( + ResourceConstraintsError::UnexpectedNonZeroBalanceOfUnspecifiedResource { + resource_address: resource2, + }, + ) }, ); } @@ -83,12 +85,13 @@ fn when_less_is_returned_assert_next_call_returns_include_should_fail() { }, false, |_, _| { - Some(ManifestResourceConstraintsError::ResourceConstraint( - ResourceConstraintError::ExpectedAtLeastAmount { + Some(ResourceConstraintsError::ResourceConstraintFailed { + resource_address: XRD, + error: ResourceConstraintError::ExpectedAtLeastAmount { expected_at_least_amount: dec!(1), actual_amount: dec!(0), }, - )) + }) }, ); } @@ -105,13 +108,12 @@ fn when_less_is_returned_assert_next_call_returns_only_should_fail() { .with(XRD, ManifestResourceConstraint::AtLeastAmount(dec!(1))) }, true, - |_, _| { - Some(ManifestResourceConstraintsError::ResourceConstraint( - ResourceConstraintError::ExpectedAtLeastAmount { - expected_at_least_amount: dec!(1), - actual_amount: dec!(0), + |_, resource2| { + Some( + ResourceConstraintsError::UnexpectedNonZeroBalanceOfUnspecifiedResource { + resource_address: resource2, }, - )) + ) }, ); } @@ -131,9 +133,11 @@ fn when_empty_constraints_on_assert_next_call_returns_only_should_fail() { |_resource1, _resource2| ManifestResourceConstraints::new(), true, |resource1, _resource2| { - Some(ManifestResourceConstraintsError::UnwantedResourcesExist( - resource1, - )) + Some( + ResourceConstraintsError::UnexpectedNonZeroBalanceOfUnspecifiedResource { + resource_address: resource1, + }, + ) }, ); } @@ -230,10 +234,7 @@ fn when_withdrawing_zero_non_fungibles_with_zero_constraints_on_assert_worktop_r fn run_worktop_two_resources_test( constraints: fn(ResourceAddress, ResourceAddress) -> ManifestResourceConstraints, exact: bool, - expected_result: fn( - ResourceAddress, - ResourceAddress, - ) -> Option, + expected_result: fn(ResourceAddress, ResourceAddress) -> Option, ) { // Arrange let mut ledger = LedgerSimulatorBuilder::new().build(); @@ -268,7 +269,7 @@ fn run_worktop_two_resources_test( if let Some(error) = expected_result(resource1, resource2) { receipt.expect_specific_failure(|e| { e.eq(&RuntimeError::ApplicationError( - ApplicationError::WorktopError(WorktopError::ResourceConstraintsError( + ApplicationError::WorktopError(WorktopError::ResourceAssertionFailed( error.clone(), )), )) diff --git a/radix-engine-tests/tests/system/fee.rs b/radix-engine-tests/tests/system/fee.rs index 2b26aac9811..03bf8a8d818 100644 --- a/radix-engine-tests/tests/system/fee.rs +++ b/radix-engine-tests/tests/system/fee.rs @@ -295,7 +295,15 @@ fn test_fee_accounting_failure() { matches!( e, RuntimeError::ApplicationError(ApplicationError::WorktopError( - WorktopError::AssertionFailed + WorktopError::ResourceAssertionFailed( + ResourceConstraintsError::ResourceConstraintFailed { + resource_address: XRD, + error: ResourceConstraintError::ExpectedAtLeastAmount { + expected_at_least_amount: Decimal::ONE, + actual_amount: Decimal::ZERO, + }, + } + ) )) ) }); @@ -456,7 +464,15 @@ fn test_contingent_fee_accounting_failure() { matches!( e, RuntimeError::ApplicationError(ApplicationError::WorktopError( - WorktopError::AssertionFailed + WorktopError::ResourceAssertionFailed( + ResourceConstraintsError::ResourceConstraintFailed { + resource_address: XRD, + error: ResourceConstraintError::ExpectedAtLeastAmount { + expected_at_least_amount: Decimal::ONE, + actual_amount: Decimal::ZERO, + }, + } + ) )) ) }); diff --git a/radix-engine/src/blueprints/resource/worktop.rs b/radix-engine/src/blueprints/resource/worktop.rs index 3aa94c9c476..ed316079145 100644 --- a/radix-engine/src/blueprints/resource/worktop.rs +++ b/radix-engine/src/blueprints/resource/worktop.rs @@ -1,6 +1,3 @@ -use crate::blueprints::transaction_processor::ResourceConstraintChecker; -use crate::errors::ApplicationError; -use crate::errors::RuntimeError; use crate::internal_prelude::*; use crate::kernel::kernel_api::KernelSubstateApi; use crate::system::system_callback::SystemLockData; @@ -8,7 +5,7 @@ use crate::system::system_substates::FieldSubstate; use radix_engine_interface::api::field_api::LockFlags; use radix_engine_interface::api::{SystemApi, ACTOR_STATE_SELF}; use radix_engine_interface::blueprints::resource::*; -use radix_native_sdk::resource::{NativeBucket, NativeNonFungibleBucket, ResourceManager}; +use radix_native_sdk::resource::*; #[derive(Debug, ScryptoSbor)] pub struct WorktopSubstate { @@ -25,9 +22,11 @@ impl WorktopSubstate { #[derive(Debug, Clone, PartialEq, Eq, ScryptoSbor)] pub enum WorktopError { + /// This is now unused, but kept in for backwards compatibility + /// of error models (at least for now) AssertionFailed, InsufficientBalance, - ResourceConstraintsError(ManifestResourceConstraintsError), + ResourceAssertionFailed(ResourceConstraintsError), } pub struct WorktopBlueprint; @@ -409,8 +408,14 @@ impl WorktopBlueprint { Decimal::zero() }; if amount.is_zero() { + let worktop_error = WorktopError::ResourceAssertionFailed( + ResourceConstraintsError::ResourceConstraintFailed { + resource_address: input.resource_address, + error: ResourceConstraintError::ExpectedNonZeroAmount, + }, + ); return Err(RuntimeError::ApplicationError( - ApplicationError::WorktopError(WorktopError::AssertionFailed), + ApplicationError::WorktopError(worktop_error), )); } api.field_close(worktop_handle)?; @@ -437,8 +442,17 @@ impl WorktopBlueprint { Decimal::zero() }; if amount < input.amount { + let worktop_error = WorktopError::ResourceAssertionFailed( + ResourceConstraintsError::ResourceConstraintFailed { + resource_address: input.resource_address, + error: ResourceConstraintError::ExpectedAtLeastAmount { + expected_at_least_amount: input.amount, + actual_amount: amount, + }, + }, + ); return Err(RuntimeError::ApplicationError( - ApplicationError::WorktopError(WorktopError::AssertionFailed), + ApplicationError::WorktopError(worktop_error), )); } api.field_close(worktop_handle)?; @@ -459,15 +473,23 @@ impl WorktopBlueprint { LockFlags::read_only(), )?; let worktop: WorktopSubstate = api.field_read_typed(worktop_handle)?; - let ids = if let Some(bucket) = worktop.resources.get(&input.resource_address) { + let bucket_ids = if let Some(bucket) = worktop.resources.get(&input.resource_address) { let bucket = Bucket(bucket.clone()); bucket.non_fungible_local_ids(api)? } else { index_set_new() }; - if !ids.is_superset(&input.ids) { + if let Some(missing_id) = input.ids.difference(&bucket_ids).next() { + let worktop_error = WorktopError::ResourceAssertionFailed( + ResourceConstraintsError::ResourceConstraintFailed { + resource_address: input.resource_address, + error: ResourceConstraintError::NonFungibleMissing { + missing_id: missing_id.clone(), + }, + }, + ); return Err(RuntimeError::ApplicationError( - ApplicationError::WorktopError(WorktopError::AssertionFailed), + ApplicationError::WorktopError(worktop_error), )); } api.field_close(worktop_handle)?; @@ -547,7 +569,15 @@ impl WorktopBlueprintCuttlefishExtension { .as_typed() .map_err(|e| RuntimeError::ApplicationError(ApplicationError::InputDecodeError(e)))?; - Self::assert_resources(input.constraints, false, api) + Self::aggregate_resources(api)? + .validate_includes(input.constraints) + .map_err(|e| { + RuntimeError::ApplicationError(ApplicationError::WorktopError( + WorktopError::ResourceAssertionFailed(e), + )) + })?; + + Ok(IndexedScryptoValue::from_typed(&())) } pub(crate) fn assert_resources_only>( @@ -558,14 +588,20 @@ impl WorktopBlueprintCuttlefishExtension { .as_typed() .map_err(|e| RuntimeError::ApplicationError(ApplicationError::InputDecodeError(e)))?; - Self::assert_resources(input.constraints, true, api) + Self::aggregate_resources(api)? + .validate_only(input.constraints) + .map_err(|e| { + RuntimeError::ApplicationError(ApplicationError::WorktopError( + WorktopError::ResourceAssertionFailed(e), + )) + })?; + + Ok(IndexedScryptoValue::from_typed(&())) } - fn assert_resources>( - constraints: ManifestResourceConstraints, - exact: bool, - api: &mut Y, - ) -> Result { + fn aggregate_resources( + api: &mut impl SystemApi, + ) -> Result { let worktop_handle = api.actor_open_field( ACTOR_STATE_SELF, WorktopField::Worktop.into(), @@ -573,28 +609,20 @@ impl WorktopBlueprintCuttlefishExtension { )?; let worktop: WorktopSubstate = api.field_read_typed(worktop_handle)?; - let mut constraint_checker = ResourceConstraintChecker::new(constraints, exact); + let mut aggregated_balances = AggregateResourceBalances::new(); for (resource, bucket) in worktop.resources { let bucket = Bucket(bucket.clone()); if resource.is_fungible() { let amount = bucket.amount(api)?; - constraint_checker.add_fungible(resource, amount); + aggregated_balances.add_fungible(resource, amount); } else { let ids = bucket.non_fungible_local_ids(api)?; - constraint_checker.add_non_fungible(resource, ids); + aggregated_balances.add_non_fungible(resource, ids); } } - constraint_checker.validate().map_err(|e| { - RuntimeError::ApplicationError(ApplicationError::WorktopError( - WorktopError::ResourceConstraintsError(e), - )) - })?; - - api.field_close(worktop_handle)?; - - Ok(IndexedScryptoValue::from_typed(&())) + Ok(aggregated_balances) } pub fn invoke_export>( diff --git a/radix-engine/src/errors.rs b/radix-engine/src/errors.rs index 7fdb1533c7c..05668d40bee 100644 --- a/radix-engine/src/errors.rs +++ b/radix-engine/src/errors.rs @@ -320,7 +320,7 @@ pub enum IntentError { VerifyParentFailed, InvalidIntentIndex(usize), NoParentToYieldTo, - AssertNextCallReturnsFailed(ManifestResourceConstraintsError), + AssertNextCallReturnsFailed(ResourceConstraintsError), AssertBucketContentsFailed(ResourceConstraintError), } diff --git a/radix-engine/src/system/transaction/instructions.rs b/radix-engine/src/system/transaction/instructions.rs index 7e33a9ff3c0..6e51d137aec 100644 --- a/radix-engine/src/system/transaction/instructions.rs +++ b/radix-engine/src/system/transaction/instructions.rs @@ -340,9 +340,10 @@ impl TxnNormalInstruction for AssertNextCallReturnsOnly { objects: &mut IntentProcessorObjects, _api: &mut Y, ) -> Result { - objects.next_call_return_constraints = Some(NextCallReturnsConstraints { + objects.next_call_return_constraints = Some(NextCallReturnsChecker { constraints: self.constraints, - exact: true, + prevent_unspecified_resource_balances: true, + aggregate_balances: AggregateResourceBalances::new(), }); Ok(InstructionOutput::None) @@ -356,9 +357,10 @@ impl TxnNormalInstruction for AssertNextCallReturnsInclude { objects: &mut IntentProcessorObjects, _api: &mut Y, ) -> Result { - objects.next_call_return_constraints = Some(NextCallReturnsConstraints { + objects.next_call_return_constraints = Some(NextCallReturnsChecker { constraints: self.constraints, - exact: false, + prevent_unspecified_resource_balances: false, + aggregate_balances: AggregateResourceBalances::new(), }); Ok(InstructionOutput::None) @@ -384,7 +386,7 @@ impl TxnNormalInstruction for AssertBucketContents { })?; } else { let ids = bucket.non_fungible_local_ids(api)?; - self.constraint.validate_non_fungible(ids).map_err(|e| { + self.constraint.validate_non_fungible(&ids).map_err(|e| { RuntimeError::SystemError(SystemError::IntentError( IntentError::AssertBucketContentsFailed(e), )) diff --git a/radix-engine/src/system/transaction/intent_processor.rs b/radix-engine/src/system/transaction/intent_processor.rs index ec2869eac82..2d29cdba7af 100644 --- a/radix-engine/src/system/transaction/intent_processor.rs +++ b/radix-engine/src/system/transaction/intent_processor.rs @@ -160,9 +160,25 @@ impl<'a, I: TxnInstruction + ManifestDecode + ManifestCategorize> IntentProcesso } } -pub struct NextCallReturnsConstraints { +pub struct NextCallReturnsChecker { pub constraints: ManifestResourceConstraints, - pub exact: bool, + pub prevent_unspecified_resource_balances: bool, + pub aggregate_balances: AggregateResourceBalances, +} + +impl NextCallReturnsChecker { + fn validate(self) -> Result<(), RuntimeError> { + let result = if self.prevent_unspecified_resource_balances { + self.aggregate_balances.validate_only(self.constraints) + } else { + self.aggregate_balances.validate_includes(self.constraints) + }; + result.map_err(|error| { + RuntimeError::SystemError(SystemError::IntentError( + IntentError::AssertNextCallReturnsFailed(error), + )) + }) + } } pub struct IntentProcessorObjects<'a> { @@ -174,7 +190,7 @@ pub struct IntentProcessorObjects<'a> { blobs_by_hash: &'a IndexMap>, max_total_size_of_blobs: usize, - pub next_call_return_constraints: Option, + pub next_call_return_constraints: Option, } impl<'a> IntentProcessorObjects<'a> { @@ -366,10 +382,7 @@ impl<'a> IntentProcessorObjects<'a> { worktop: &Worktop, api: &mut Y, ) -> Result<(), RuntimeError> { - let mut resource_constraint_checker = - self.next_call_return_constraints.take().map(|constraints| { - ResourceConstraintChecker::new(constraints.constraints, constraints.exact) - }); + let mut resource_constraint_checker = self.next_call_return_constraints.take(); // Auto move into worktop & auth_zone for node_id in value.owned_nodes() { @@ -388,7 +401,9 @@ impl<'a> IntentProcessorObjects<'a> { .expect() .try_into() .unwrap(); - checker.add_fungible(resource_address, bucket.amount(api)?); + checker + .aggregate_balances + .add_fungible(resource_address, bucket.amount(api)?); } worktop.put(bucket, api)?; } @@ -401,7 +416,7 @@ impl<'a> IntentProcessorObjects<'a> { .expect() .try_into() .unwrap(); - checker.add_non_fungible( + checker.aggregate_balances.add_non_fungible( resource_address, bucket.non_fungible_local_ids(api)?, ); @@ -426,65 +441,13 @@ impl<'a> IntentProcessorObjects<'a> { } if let Some(checker) = resource_constraint_checker { - checker.validate().map_err(|e| { - RuntimeError::SystemError(SystemError::IntentError( - IntentError::AssertNextCallReturnsFailed(e), - )) - })?; + checker.validate()?; } Ok(()) } } -pub struct ResourceConstraintChecker { - fungible_resources: IndexMap, - non_fungible_resources: IndexMap>, - constraints: ManifestResourceConstraints, - exact: bool, -} - -impl ResourceConstraintChecker { - pub fn new(constraints: ManifestResourceConstraints, exact: bool) -> Self { - Self { - fungible_resources: Default::default(), - non_fungible_resources: Default::default(), - constraints, - exact, - } - } - - pub fn add_fungible(&mut self, resource_address: ResourceAddress, amount: Decimal) { - if amount.is_positive() { - self.fungible_resources - .entry(resource_address) - .or_default() - .add_assign(amount); - } - } - - pub fn add_non_fungible( - &mut self, - resource_address: ResourceAddress, - ids: IndexSet, - ) { - if !ids.is_empty() { - self.non_fungible_resources - .entry(resource_address) - .or_default() - .extend(ids); - } - } - - pub fn validate(self) -> Result<(), ManifestResourceConstraintsError> { - self.constraints.validate( - self.fungible_resources, - self.non_fungible_resources, - self.exact, - ) - } -} - pub struct IntentProcessorObjectsWithApi<'a, 'e, Y: SystemApi> { pub(crate) worktop: &'a mut Worktop, pub(crate) objects: &'a mut IntentProcessorObjects<'e>, diff --git a/radix-engine/src/transaction/node_versioned_local_transaction_execution_cuttlefish.bin b/radix-engine/src/transaction/node_versioned_local_transaction_execution_cuttlefish.bin index 11b60809a7778f29e051dccfa3a7b2405b811b02..69813c87082c92b3b2b119a5a2dc652b1f3216de 100644 GIT binary patch delta 2361 zcmbW2TTmNS7{~W)0-;MnfDn=fNYm|%t%D#VRt86>0c@}+P}_ovSWL-6w+*{>cd@lz zB3N3fSNv+d;iX<$ZAE1=KKSA=c6jl{C!b`T))_}%9H$TZ)CbR=^Bp(U86TRLe82zs z&UZPty?k0eaZ0Wn-6*RP+2~8D%L|nao4=6wuZpD9AR&s3OwC!(d=_>lqXW-*!n|jm z^voAM^EJNgR+WN80&_%_ z*&k8T=n^KI0;zyRJ5?o_jjm9S6`yDNJ(EVKgWuAy?jM`#mspN&3`clIB$?5@jG85h z7v5i*xtve$xn_C~H2WIJT@Vem9NaEOh$5U=UYSqKh+&rLZlsV~hSH1V?wH&5uT(4X zpvol{ittcr;(zJ3Q~x3J;P_|V_M@KdCo#`rUGI~jqty1$xo z=)>1kL*i>XQS6B(NbrCNO_T$m*MHq)c!Dtfs>CChuq}2gnFJ@cDg3DLWA$5bHm3bY zOT-7rVbJ3{`i`PEzX^i)&I{r>1eL2&K$U2Bw6iBfE^5Nncry5;Cp-l=tpAG4z}osj zatv-tw1$u4V6}T-l@p*RjGE_BV^@NzFTih!EiEs3VP5vk8P9yhGZ)~_hSun-D6ly6 zBF)iQ;J#CZ75c410=qvA0qLs{|L!#!VUBm^fqn|E}}|>ML62Hwf$WbN}rwggcF;1 z=3jx{ho2j7DTqK~twkIfSeVy0#5eRTCUsZslac~t%I7EpG~Kpa)G2DGue{so`4WF0 z*TYj+NC7CzI9l zT8_3sk6mmd)j;ZI@}5Wm?dZZrR#K(`2TNNqt{l^8yT*&Tuxb=Q5@x21OQC zk_g<`og^|iX?0y#lnYnV4X~2VwENxT8m!xnZFD*PrtNf1nbU^zPem~?Yb@)GO_}gV zIyPFy=GB(e)34jMZcK_LGg3i^;)@7jv+z@-6>MPa;}Is;K(EfB*mYUeBRp z>d(j3+=0!irjX595p8WTw<3K;5r26kqWK8%s>oD4=!kQUc*qf7a>SP%@fAmW)e(<7 z;v0_mQLZDeES_n*s|h(vBea8VBp0V1q-;))BDp!WuWu*ky=fX#k9wjJg?Q%jNwp{9 zQRv8;O8G4NR~<1+Q)x_tjB!NQKN&>z#w*noKcUGz=UAb1B>P_~v3}0cQKWaorSRT& z$?9SfB$ZPalGw){BtDc|L^o$dXAy5R>?tjC(V2F`*S;;?Ez(LYnZc?EzF(Sou{I<+ z5lzsF#BK&#-9@TvgM0iT(gFScJ4q)T@vo8x;NHBw;ohtA>_rkIJOZnE=V^?U-_q2F zmP%f)A1Tb~L8K~940zXZra+*e9U=uqFi>z`dI0s}g6u;QNAwtyxF9hk56?G( zw29M`NI9Iwk-VHtq+Cu3Bp;__8qv?uM#|%qLdxefiB!O83Ms&88Y#%>DWpP9Pa_p^ zdJd_W)2!kbT*C2rSPs;YLHI7vN1{+0+((9BA=pob;iq7KzK)^nXo=T==EAMDPoQoS zKVSCxr6xyHyqc1$DPqt;I9Yf%8Gs9gzlTRLNGy2_Ni6yC^o#>U>N3@g{A0mG!^rzA5eU%m_dDv!o2RmEDql2IIslkt8mK zBvDP0TT?umLIQl?W~SdyGDuzCLM2^^c-!xwLM;9y zl34r)NMi9HB8iK#jKo&J?&I5Vx#DKu3Di=>38<~S17^Yn)y#ZK8o2ZO*95-@OO>~! zWw6}pyE2A&rTG6$;^leP7^s^20xkm$;~YMwSMW`~>Vx4_~0azfyA zy>?@FXp47i>IlOX&USfJ!Wm3$muO+N3}Tfi(Z{9n%Wi^Hw; zW%NhY&6PE>LPn2r8I38s)sl_VozSOAzcu_>`7)A4pf2HIyGo0tR(2$&^kgGl&`1Mc zQ&+<7joF4ZYUm?Z#xIUDujmkSiyG!|JY^Y^x?5~O<-Jw*O-%fUL@DOQ5~VlDB#tB0 zX;_wFj`isKQ$|uB$rx5PM7-m|hwe@+KiFzc#^T1t&W5m2XWnX{cgC{UFE)&AgUOwt h^|9oSdF!pSZVy?5?)l4bblwFQ=FO#rUGT?(?=J@hnPLC{ diff --git a/radix-rust/src/rust.rs b/radix-rust/src/rust.rs index c0eb1e98956..20dca600252 100644 --- a/radix-rust/src/rust.rs +++ b/radix-rust/src/rust.rs @@ -466,13 +466,13 @@ pub mod collections { /// This is safe for std and no-std use cases (unlike `IndexSet::new` which disappears when std is not in the toolchain - see /// [this article](https://faultlore.com/blah/defaults-affect-inference/) for deep technical reasons) pub fn new() -> IndexSet { - IndexSet::with_capacity_and_hasher(0, DefaultHashBuilder::default()) + IndexSet::with_hasher(DefaultHashBuilder::default()) } /// This is safe for std and no-std use cases (unlike `IndexSet::with_capacity` which disappears when std is not in the toolchain - see /// [this article](https://faultlore.com/blah/defaults-affect-inference/) for deep technical reasons) pub fn with_capacity(n: usize) -> IndexSet { - IndexSet::with_capacity_and_hasher(n, DefaultHashBuilder::default()) + IndexSet::with_capacity_and_hasher(n, DefaultHashBuilder::new()) } #[macro_export] @@ -535,10 +535,7 @@ pub mod collections { impl NonIterMap { /// Creates an empty map. pub fn new() -> Self { - Self(HashMap::with_capacity_and_hasher( - 0, - DefaultHashBuilder::default(), - )) + Self(HashMap::with_hasher(DefaultHashBuilder::new())) } /// Gets the given key's corresponding entry in the map for in-place manipulation.