diff --git a/substrate/node/runtime/src/lib.rs b/substrate/node/runtime/src/lib.rs index 6ef50ef61f..1f52620cd4 100644 --- a/substrate/node/runtime/src/lib.rs +++ b/substrate/node/runtime/src/lib.rs @@ -59,7 +59,7 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { impl_name: create_runtime_str!("substrate-node"), authoring_version: 10, spec_version: 38, - impl_version: 38, + impl_version: 39, apis: RUNTIME_API_VERSIONS, }; diff --git a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm index 22b953faab..446e9eaad9 100644 Binary files a/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm and b/substrate/node/runtime/wasm/target/wasm32-unknown-unknown/release/node_runtime.compact.wasm differ diff --git a/substrate/srml/balances/src/lib.rs b/substrate/srml/balances/src/lib.rs index 8eee7ad130..775d83e33b 100644 --- a/substrate/srml/balances/src/lib.rs +++ b/substrate/srml/balances/src/lib.rs @@ -283,8 +283,9 @@ impl, I: Instance> Module { /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that /// the caller will do this. fn set_free_balance(who: &T::AccountId, balance: T::Balance) -> UpdateBalanceOutcome { - // Commented out for no - but consider it instructive. + // Commented out for now - but consider it instructive. // assert!(!Self::total_balance(who).is_zero()); + // assert!(Self::free_balance(who) > Self::existential_deposit()); if balance < Self::existential_deposit() { >::insert(who, balance); Self::on_free_too_low(who); @@ -657,6 +658,19 @@ where UpdateBalanceOutcome ) { let original = Self::free_balance(who); + if balance < Self::existential_deposit() && original.is_zero() { + // If we're attempting to set an existing account to less than ED, then + // bypass the entire operation. It's a no-op if you follow it through, but + // since this is an instance where we might account for a negative imbalance + // (in the dust cleaner of set_free_balance) before we account for its actual + // equal and opposite cause (returned as an Imbalance), then in the + // instance that there's no other accounts on the system at all, we might + // underflow the issuance and our arithmetic will be off. + return ( + SignedImbalance::Positive(Self::PositiveImbalance::zero()), + UpdateBalanceOutcome::AccountKilled, + ) + } let imbalance = if original <= balance { SignedImbalance::Positive(PositiveImbalance(balance - original)) } else { diff --git a/substrate/srml/balances/src/tests.rs b/substrate/srml/balances/src/tests.rs index 802f184aca..123a04524e 100644 --- a/substrate/srml/balances/src/tests.rs +++ b/substrate/srml/balances/src/tests.rs @@ -440,6 +440,36 @@ fn transferring_too_high_value_should_not_panic() { }); } +#[test] +fn account_create_on_free_too_low_with_other() { + with_externalities( + &mut ExtBuilder::default().existential_deposit(100).build(), + || { + let _ = Balances::deposit_creating(&1, 100); + assert_eq!(>::get(), 100); + + // No-op. + let _ = Balances::deposit_creating(&2, 50); + assert_eq!(Balances::free_balance(&2), 0); + assert_eq!(>::get(), 100); + } + ) +} + + +#[test] +fn account_create_on_free_too_low() { + with_externalities( + &mut ExtBuilder::default().existential_deposit(100).build(), + || { + // No-op. + let _ = Balances::deposit_creating(&2, 50); + assert_eq!(Balances::free_balance(&2), 0); + assert_eq!(>::get(), 0); + } + ) +} + #[test] fn account_removal_on_free_too_low() { with_externalities(