diff --git a/substrate/Cargo.lock b/substrate/Cargo.lock index 52a9c261ce..7a44b0ed77 100644 --- a/substrate/Cargo.lock +++ b/substrate/Cargo.lock @@ -4218,6 +4218,7 @@ version = "2.0.0" dependencies = [ "frame-support", "frame-system", + "pallet-balances", "parity-scale-codec", "serde", "sp-core", diff --git a/substrate/bin/node-template/node/src/chain_spec.rs b/substrate/bin/node-template/node/src/chain_spec.rs index aa50d06b23..9bdfea3b78 100644 --- a/substrate/bin/node-template/node/src/chain_spec.rs +++ b/substrate/bin/node-template/node/src/chain_spec.rs @@ -1,7 +1,7 @@ use sp_core::{Pair, Public, sr25519}; use node_template_runtime::{ AccountId, AuraConfig, BalancesConfig, GenesisConfig, GrandpaConfig, - SudoConfig, IndicesConfig, SystemConfig, WASM_BINARY, Signature + IndicesConfig, SudoConfig, SystemConfig, WASM_BINARY, Signature }; use sp_consensus_aura::sr25519::{AuthorityId as AuraId}; use grandpa_primitives::{AuthorityId as GrandpaId}; @@ -128,7 +128,7 @@ fn testnet_genesis(initial_authorities: Vec<(AuraId, GrandpaId)>, changes_trie_config: Default::default(), }), indices: Some(IndicesConfig { - ids: endowed_accounts.clone(), + indices: vec![], }), balances: Some(BalancesConfig { balances: endowed_accounts.iter().cloned().map(|k|(k, 1 << 60)).collect(), diff --git a/substrate/bin/node-template/pallets/template/src/mock.rs b/substrate/bin/node-template/pallets/template/src/mock.rs index 2cbfc89d5b..b3c1098db6 100644 --- a/substrate/bin/node-template/pallets/template/src/mock.rs +++ b/substrate/bin/node-template/pallets/template/src/mock.rs @@ -39,6 +39,9 @@ impl system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl Trait for Test { type Event = (); diff --git a/substrate/bin/node-template/runtime/src/lib.rs b/substrate/bin/node-template/runtime/src/lib.rs index b8a12e3112..04394917f1 100644 --- a/substrate/bin/node-template/runtime/src/lib.rs +++ b/substrate/bin/node-template/runtime/src/lib.rs @@ -161,6 +161,12 @@ impl system::Trait for Runtime { /// /// This type is being generated by `construct_runtime!`. type ModuleToIndex = ModuleToIndex; + /// What to do if a new account is created. + type OnNewAccount = (); + /// What to do if an account is fully reaped from the system. + type OnReapAccount = Balances; + /// The data to be stored in an account. + type AccountData = balances::AccountData; } impl aura::Trait for Runtime { @@ -171,16 +177,21 @@ impl grandpa::Trait for Runtime { type Event = Event; } +parameter_types! { + /// How much an index costs. + pub const IndexDeposit: u128 = 100; +} + impl indices::Trait for Runtime { /// The type for recording indexing into the account enumeration. If this ever overflows, there /// will be problems! type AccountIndex = AccountIndex; - /// Use the standard means of resolving an index hint from an id. - type ResolveHint = indices::SimpleResolveHint; - /// Determine whether an account is dead. - type IsDeadAccount = Balances; /// The ubiquitous event type. type Event = Event; + /// The currency type. + type Currency = Balances; + /// How much an index costs. + type Deposit = IndexDeposit; } parameter_types! { @@ -196,22 +207,16 @@ impl timestamp::Trait for Runtime { parameter_types! { pub const ExistentialDeposit: u128 = 500; - pub const CreationFee: u128 = 0; } impl balances::Trait for Runtime { /// The type for recording an account's balance. type Balance = Balance; - /// What to do if an account is fully reaped from the system. - type OnReapAccount = System; - /// What to do if a new account is created. - type OnNewAccount = Indices; /// The ubiquitous event type. type Event = Event; type DustRemoval = (); - type TransferPayment = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { @@ -244,12 +249,12 @@ construct_runtime!( NodeBlock = opaque::Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Module, Call, Storage, Config, Event}, + System: system::{Module, Call, Config, Storage, Event}, Timestamp: timestamp::{Module, Call, Storage, Inherent}, Aura: aura::{Module, Config, Inherent(Timestamp)}, Grandpa: grandpa::{Module, Call, Storage, Config, Event}, - Indices: indices, - Balances: balances, + Indices: indices::{Module, Call, Storage, Event, Config}, + Balances: balances::{Module, Call, Storage, Config, Event}, TransactionPayment: transaction_payment::{Module, Storage}, Sudo: sudo, // Used for the module template in `./template.rs` diff --git a/substrate/bin/node/cli/src/chain_spec.rs b/substrate/bin/node/cli/src/chain_spec.rs index c68283ad65..5cdfb5cab8 100644 --- a/substrate/bin/node/cli/src/chain_spec.rs +++ b/substrate/bin/node/cli/src/chain_spec.rs @@ -21,8 +21,8 @@ use sp_core::{Pair, Public, crypto::UncheckedInto, sr25519}; use serde::{Serialize, Deserialize}; use node_runtime::{ AuthorityDiscoveryConfig, BabeConfig, BalancesConfig, ContractsConfig, CouncilConfig, DemocracyConfig, - GrandpaConfig, ImOnlineConfig, IndicesConfig, SessionConfig, SessionKeys, StakerStatus, StakingConfig, - SocietyConfig, SudoConfig, SystemConfig, TechnicalCommitteeConfig, WASM_BINARY, + GrandpaConfig, ImOnlineConfig, SessionConfig, SessionKeys, StakerStatus, StakingConfig, + IndicesConfig, SocietyConfig, SudoConfig, SystemConfig, TechnicalCommitteeConfig, WASM_BINARY, }; use node_runtime::Block; use node_runtime::constants::currency::*; @@ -239,9 +239,7 @@ pub fn testnet_genesis( .collect(), }), pallet_indices: Some(IndicesConfig { - ids: endowed_accounts.iter().cloned() - .chain(initial_authorities.iter().map(|x| x.0.clone())) - .collect::>(), + indices: vec![], }), pallet_session: Some(SessionConfig { keys: initial_authorities.iter().map(|x| { diff --git a/substrate/bin/node/executor/tests/basic.rs b/substrate/bin/node/executor/tests/basic.rs index b306852f45..100bdf3fe6 100644 --- a/substrate/bin/node/executor/tests/basic.rs +++ b/substrate/bin/node/executor/tests/basic.rs @@ -35,7 +35,7 @@ use frame_system::{self, EventRecord, Phase}; use node_runtime::{ Header, Block, UncheckedExtrinsic, CheckedExtrinsic, Call, Runtime, Balances, - System, TransactionPayment, Event, TransactionBaseFee, TransactionByteFee, CreationFee, + System, TransactionPayment, Event, TransactionBaseFee, TransactionByteFee, constants::currency::*, }; use node_primitives::{Balance, Hash}; @@ -163,15 +163,12 @@ fn block_with_size(time: u64, nonce: u32, size: usize) -> (Vec, Hash) { fn panic_execution_with_foreign_code_gives_error() { let mut t = TestExternalities::::new_with_code(BLOATY_CODE, Storage { top: map![ - >::hashed_key_for(alice()) => { + >::hashed_key_for(alice()) => { (69u128, 0u128, 0u128, 0u128).encode() }, >::hashed_key().to_vec() => { 69_u128.encode() }, - >::hashed_key().to_vec() => { - 0_u128.encode() - }, >::hashed_key_for(0) => { vec![0u8; 32] } @@ -202,15 +199,12 @@ fn panic_execution_with_foreign_code_gives_error() { fn bad_extrinsic_with_native_equivalent_code_gives_error() { let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { top: map![ - >::hashed_key_for(alice()) => { - (69u128, 0u128, 0u128, 0u128).encode() + >::hashed_key_for(alice()) => { + (0u32, 69u128, 0u128, 0u128, 0u128).encode() }, >::hashed_key().to_vec() => { 69_u128.encode() }, - >::hashed_key().to_vec() => { - 0_u128.encode() - }, >::hashed_key_for(0) => { vec![0u8; 32] } @@ -241,13 +235,12 @@ fn bad_extrinsic_with_native_equivalent_code_gives_error() { fn successful_execution_with_native_equivalent_code_gives_ok() { let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { top: map![ - >::hashed_key_for(alice()) => { - (111 * DOLLARS, 0u128, 0u128, 0u128).encode() + >::hashed_key_for(alice()) => { + (0u32, 111 * DOLLARS, 0u128, 0u128, 0u128).encode() }, >::hashed_key().to_vec() => { (111 * DOLLARS).encode() }, - >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] ], children: map![], @@ -274,7 +267,7 @@ fn successful_execution_with_native_equivalent_code_gives_ok() { assert!(r.is_ok()); t.execute_with(|| { - let fees = transfer_fee(&xt(), fm) + CreationFee::get(); + let fees = transfer_fee(&xt(), fm); assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); @@ -284,13 +277,12 @@ fn successful_execution_with_native_equivalent_code_gives_ok() { fn successful_execution_with_foreign_code_gives_ok() { let mut t = TestExternalities::::new_with_code(BLOATY_CODE, Storage { top: map![ - >::hashed_key_for(alice()) => { - (111 * DOLLARS, 0u128, 0u128, 0u128).encode() + >::hashed_key_for(alice()) => { + (0u32, 111 * DOLLARS, 0u128, 0u128, 0u128).encode() }, >::hashed_key().to_vec() => { (111 * DOLLARS).encode() }, - >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] ], children: map![], @@ -317,7 +309,7 @@ fn successful_execution_with_foreign_code_gives_ok() { assert!(r.is_ok()); t.execute_with(|| { - let fees = transfer_fee(&xt(), fm) + CreationFee::get(); + let fees = transfer_fee(&xt(), fm); assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); @@ -348,7 +340,7 @@ fn full_native_block_import_works() { let events = vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: Event::system(frame_system::Event::ExtrinsicSuccess( + event: Event::frame_system(frame_system::RawEvent::ExtrinsicSuccess( DispatchInfo { weight: 10000, class: DispatchClass::Operational, pays_fee: true } )), topics: vec![], @@ -364,13 +356,12 @@ fn full_native_block_import_works() { alice().into(), bob().into(), 69 * DOLLARS, - 0, )), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), - event: Event::system(frame_system::Event::ExtrinsicSuccess( + event: Event::frame_system(frame_system::RawEvent::ExtrinsicSuccess( DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } )), topics: vec![], @@ -401,7 +392,7 @@ fn full_native_block_import_works() { let events = vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: Event::system(frame_system::Event::ExtrinsicSuccess( + event: Event::frame_system(frame_system::RawEvent::ExtrinsicSuccess( DispatchInfo { weight: 10000, class: DispatchClass::Operational, pays_fee: true } )), topics: vec![], @@ -418,14 +409,13 @@ fn full_native_block_import_works() { bob().into(), alice().into(), 5 * DOLLARS, - 0, ) ), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(1), - event: Event::system(frame_system::Event::ExtrinsicSuccess( + event: Event::frame_system(frame_system::RawEvent::ExtrinsicSuccess( DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } )), topics: vec![], @@ -442,14 +432,13 @@ fn full_native_block_import_works() { alice().into(), bob().into(), 15 * DOLLARS, - 0, ) ), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(2), - event: Event::system(frame_system::Event::ExtrinsicSuccess( + event: Event::frame_system(frame_system::RawEvent::ExtrinsicSuccess( DispatchInfo { weight: 1000000, class: DispatchClass::Normal, pays_fee: true } )), topics: vec![], @@ -712,13 +701,9 @@ fn native_big_block_import_fails_on_fallback() { fn panic_execution_gives_error() { let mut t = TestExternalities::::new_with_code(BLOATY_CODE, Storage { top: map![ - >::hashed_key_for(alice()) => { - (0_u128, 0_u128, 0_u128, 0_u128).encode() - }, >::hashed_key().to_vec() => { 0_u128.encode() }, - >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] ], children: map![], @@ -747,13 +732,12 @@ fn panic_execution_gives_error() { fn successful_execution_gives_ok() { let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { top: map![ - >::hashed_key_for(alice()) => { - (111 * DOLLARS, 0u128, 0u128, 0u128).encode() + >::hashed_key_for(alice()) => { + (0u32, 111 * DOLLARS, 0u128, 0u128, 0u128).encode() }, >::hashed_key().to_vec() => { (111 * DOLLARS).encode() }, - >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] ], children: map![], @@ -777,11 +761,11 @@ fn successful_execution_gives_ok() { ).0.unwrap().into_encoded(); ApplyExtrinsicResult::decode(&mut &r[..]) .unwrap() - .expect("Extrinsic could be applied") - .expect("Extrinsic did not fail"); + .expect("Extrinsic could not be applied") + .expect("Extrinsic failed"); t.execute_with(|| { - let fees = transfer_fee(&xt(), fm) + CreationFee::get(); + let fees = transfer_fee(&xt(), fm); assert_eq!(Balances::total_balance(&alice()), 42 * DOLLARS - fees); assert_eq!(Balances::total_balance(&bob()), 69 * DOLLARS); }); diff --git a/substrate/bin/node/executor/tests/fees.rs b/substrate/bin/node/executor/tests/fees.rs index 155cefa4cc..ba303a6feb 100644 --- a/substrate/bin/node/executor/tests/fees.rs +++ b/substrate/bin/node/executor/tests/fees.rs @@ -134,16 +134,15 @@ fn transaction_fee_is_correct_ultimate() { // (this baed on assigning 0.1 CENT to the cheapest tx with `weight = 100`) let mut t = TestExternalities::::new_with_code(COMPACT_CODE, Storage { top: map![ - >::hashed_key_for(alice()) => { - (100 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS).encode() + >::hashed_key_for(alice()) => { + (0u32, 100 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS).encode() }, - >::hashed_key_for(bob()) => { - (10 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS).encode() + >::hashed_key_for(bob()) => { + (0u32, 10 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS).encode() }, >::hashed_key().to_vec() => { - (110 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS, 0 * DOLLARS).encode() + (110 * DOLLARS).encode() }, - >::hashed_key().to_vec() => vec![0u8; 16], >::hashed_key_for(0) => vec![0u8; 32] ], children: map![], diff --git a/substrate/bin/node/executor/tests/submit_transaction.rs b/substrate/bin/node/executor/tests/submit_transaction.rs index 40c0c6b80f..b870cf4037 100644 --- a/substrate/bin/node/executor/tests/submit_transaction.rs +++ b/substrate/bin/node/executor/tests/submit_transaction.rs @@ -168,7 +168,7 @@ fn submitted_transaction_should_be_valid() { let author = extrinsic.signature.clone().unwrap().0; let address = Indices::lookup(author).unwrap(); let account = pallet_balances::AccountData { free: 5_000_000_000_000, ..Default::default() }; - >::insert(&address, account); + >::insert(&address, (0u32, account)); // check validity let res = Executive::validate_transaction(extrinsic); diff --git a/substrate/bin/node/runtime/src/lib.rs b/substrate/bin/node/runtime/src/lib.rs index 8d8e784f98..cdcf670c64 100644 --- a/substrate/bin/node/runtime/src/lib.rs +++ b/substrate/bin/node/runtime/src/lib.rs @@ -82,8 +82,8 @@ pub const VERSION: RuntimeVersion = RuntimeVersion { // and set impl_version to 0. If only runtime // implementation changes and behavior does not, then leave spec_version as // is and increment impl_version. - spec_version: 217, - impl_version: 1, + spec_version: 218, + impl_version: 0, apis: RUNTIME_API_VERSIONS, }; @@ -130,6 +130,9 @@ impl frame_system::Trait for Runtime { type AvailableBlockRatio = AvailableBlockRatio; type Version = Version; type ModuleToIndex = ModuleToIndex; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = (Balances, Staking, Contracts, Session, Recovery); } parameter_types! { @@ -160,27 +163,27 @@ impl pallet_babe::Trait for Runtime { type EpochChangeTrigger = pallet_babe::ExternalTrigger; } +parameter_types! { + pub const IndexDeposit: Balance = 1 * DOLLARS; +} + impl pallet_indices::Trait for Runtime { type AccountIndex = AccountIndex; - type IsDeadAccount = Balances; - type ResolveHint = pallet_indices::SimpleResolveHint; type Event = Event; + type Currency = Balances; + type Deposit = IndexDeposit; } parameter_types! { pub const ExistentialDeposit: Balance = 1 * DOLLARS; - pub const CreationFee: Balance = 1 * CENTS; } impl pallet_balances::Trait for Runtime { type Balance = Balance; - type OnReapAccount = ((((System, Staking), Contracts), Session), Recovery); - type OnNewAccount = Indices; - type Event = Event; type DustRemoval = (); - type TransferPayment = (); + type Event = Event; type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = frame_system::Module; } parameter_types! { @@ -235,13 +238,13 @@ parameter_types! { } impl pallet_session::Trait for Runtime { - type SessionManager = Staking; - type SessionHandler = ::KeyTypeIdProviders; - type ShouldEndSession = Babe; type Event = Event; - type Keys = SessionKeys; type ValidatorId = ::AccountId; type ValidatorIdOf = pallet_staking::StashOf; + type ShouldEndSession = Babe; + type SessionManager = Staking; + type SessionHandler = ::KeyTypeIdProviders; + type Keys = SessionKeys; type DisabledValidatorsThreshold = DisabledValidatorsThreshold; } @@ -303,7 +306,6 @@ impl pallet_democracy::Trait for Runtime { type EnactmentPeriod = EnactmentPeriod; type LaunchPeriod = LaunchPeriod; type VotingPeriod = VotingPeriod; - type EmergencyVotingPeriod = EmergencyVotingPeriod; type MinimumDeposit = MinimumDeposit; /// A straight majority of the council can decide what their next motion is. type ExternalOrigin = pallet_collective::EnsureProportionAtLeast<_1, _2, AccountId, CouncilCollective>; @@ -315,6 +317,7 @@ impl pallet_democracy::Trait for Runtime { /// Two thirds of the technical committee can have an ExternalMajority/ExternalDefault vote /// be tabled immediately and with a shorter voting/enactment period. type FastTrackOrigin = pallet_collective::EnsureProportionAtLeast<_2, _3, AccountId, TechnicalCollective>; + type EmergencyVotingPeriod = EmergencyVotingPeriod; // To cancel a proposal which has been passed, 2/3 of the council must agree to it. type CancellationOrigin = pallet_collective::EnsureProportionAtLeast<_2, _3, AccountId, CouncilCollective>; // Any single technical committee member may veto a coming council proposal, however they can @@ -343,16 +346,16 @@ parameter_types! { impl pallet_elections_phragmen::Trait for Runtime { type Event = Event; type Currency = Balances; + type ChangeMembers = Council; type CurrencyToVote = CurrencyToVoteHandler; type CandidacyBond = CandidacyBond; type VotingBond = VotingBond; - type TermDuration = TermDuration; - type DesiredMembers = DesiredMembers; - type DesiredRunnersUp = DesiredRunnersUp; type LoserCandidate = (); type BadReport = (); type KickedMember = (); - type ChangeMembers = Council; + type DesiredMembers = DesiredMembers; + type DesiredRunnersUp = DesiredRunnersUp; + type TermDuration = TermDuration; } type TechnicalCollective = pallet_collective::Instance2; @@ -387,22 +390,20 @@ impl pallet_treasury::Trait for Runtime { type Currency = Balances; type ApproveOrigin = pallet_collective::EnsureMembers<_4, AccountId, CouncilCollective>; type RejectOrigin = pallet_collective::EnsureMembers<_2, AccountId, CouncilCollective>; + type Tippers = Elections; + type TipCountdown = TipCountdown; + type TipFindersFee = TipFindersFee; + type TipReportDepositBase = TipReportDepositBase; + type TipReportDepositPerByte = TipReportDepositPerByte; type Event = Event; type ProposalRejection = (); type ProposalBond = ProposalBond; type ProposalBondMinimum = ProposalBondMinimum; type SpendPeriod = SpendPeriod; type Burn = Burn; - type Tippers = Elections; - type TipCountdown = TipCountdown; - type TipFindersFee = TipFindersFee; - type TipReportDepositBase = TipReportDepositBase; - type TipReportDepositPerByte = TipReportDepositPerByte; } parameter_types! { - pub const ContractTransferFee: Balance = 1 * CENTS; - pub const ContractCreationFee: Balance = 1 * CENTS; pub const ContractTransactionBaseFee: Balance = 1 * CENTS; pub const ContractTransactionByteFee: Balance = 10 * MILLICENTS; pub const ContractFee: Balance = 1 * CENTS; @@ -429,7 +430,6 @@ impl pallet_contracts::Trait for Runtime { type RentByteFee = RentByteFee; type RentDepositOffset = RentDepositOffset; type SurchargeReward = SurchargeReward; - type CreationFee = ContractCreationFee; type TransactionBaseFee = ContractTransactionBaseFee; type TransactionByteFee = ContractTransactionByteFee; type ContractFee = ContractFee; @@ -454,11 +454,11 @@ parameter_types! { impl pallet_im_online::Trait for Runtime { type AuthorityId = ImOnlineId; - type Call = Call; type Event = Event; + type Call = Call; type SubmitTransaction = SubmitTransaction; - type ReportUnresponsiveness = Offences; type SessionDuration = SessionDuration; + type ReportUnresponsiveness = Offences; } impl pallet_offences::Trait for Runtime { @@ -495,14 +495,14 @@ parameter_types! { impl pallet_identity::Trait for Runtime { type Event = Event; type Currency = Balances; - type Slashed = Treasury; type BasicDeposit = BasicDeposit; type FieldDeposit = FieldDeposit; type SubAccountDeposit = SubAccountDeposit; type MaxSubAccounts = MaxSubAccounts; type MaxAdditionalFields = MaxAdditionalFields; - type RegistrarOrigin = pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; + type Slashed = Treasury; type ForceOrigin = pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; + type RegistrarOrigin = pallet_collective::EnsureProportionMoreThan<_1, _2, AccountId, CouncilCollective>; } impl frame_system::offchain::CreateTransaction for Runtime { @@ -600,13 +600,13 @@ construct_runtime!( NodeBlock = node_primitives::Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: frame_system::{Module, Call, Storage, Config, Event}, + System: frame_system::{Module, Call, Config, Storage, Event}, Utility: pallet_utility::{Module, Call, Storage, Event}, Babe: pallet_babe::{Module, Call, Storage, Config, Inherent(Timestamp)}, Timestamp: pallet_timestamp::{Module, Call, Storage, Inherent}, Authorship: pallet_authorship::{Module, Call, Storage, Inherent}, - Indices: pallet_indices, - Balances: pallet_balances, + Indices: pallet_indices::{Module, Call, Storage, Config, Event}, + Balances: pallet_balances::{Module, Call, Storage, Config, Event}, TransactionPayment: pallet_transaction_payment::{Module, Storage}, Staking: pallet_staking, Session: pallet_session::{Module, Call, Storage, Event, Config}, diff --git a/substrate/bin/node/testing/src/genesis.rs b/substrate/bin/node/testing/src/genesis.rs index 58e646e3d7..9c163ea615 100644 --- a/substrate/bin/node/testing/src/genesis.rs +++ b/substrate/bin/node/testing/src/genesis.rs @@ -62,7 +62,7 @@ pub fn config_endowed( code: code.map(|x| x.to_vec()).unwrap_or_else(|| WASM_BINARY.to_vec()), }), pallet_indices: Some(IndicesConfig { - ids: vec![alice(), bob(), charlie(), dave(), eve(), ferdie()], + indices: vec![], }), pallet_balances: Some(BalancesConfig { balances: endowed, diff --git a/substrate/frame/assets/src/lib.rs b/substrate/frame/assets/src/lib.rs index e5c370cd21..8cdc9b9cc0 100644 --- a/substrate/frame/assets/src/lib.rs +++ b/substrate/frame/assets/src/lib.rs @@ -293,6 +293,9 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl Trait for Test { type Event = (); diff --git a/substrate/frame/aura/src/mock.rs b/substrate/frame/aura/src/mock.rs index 81366cf084..5b3ccd7745 100644 --- a/substrate/frame/aura/src/mock.rs +++ b/substrate/frame/aura/src/mock.rs @@ -61,6 +61,9 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl pallet_timestamp::Trait for Test { diff --git a/substrate/frame/authority-discovery/src/lib.rs b/substrate/frame/authority-discovery/src/lib.rs index 22ea3d3bba..77906e1bfe 100644 --- a/substrate/frame/authority-discovery/src/lib.rs +++ b/substrate/frame/authority-discovery/src/lib.rs @@ -157,6 +157,9 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl_outer_origin! { diff --git a/substrate/frame/authorship/src/lib.rs b/substrate/frame/authorship/src/lib.rs index 216f5c729a..f2dbd4674f 100644 --- a/substrate/frame/authorship/src/lib.rs +++ b/substrate/frame/authorship/src/lib.rs @@ -431,6 +431,9 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } parameter_types! { diff --git a/substrate/frame/babe/src/mock.rs b/substrate/frame/babe/src/mock.rs index f5ade4ab33..efb5570c1d 100644 --- a/substrate/frame/babe/src/mock.rs +++ b/substrate/frame/babe/src/mock.rs @@ -66,6 +66,9 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type MaximumBlockLength = MaximumBlockLength; type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl_opaque_keys! { diff --git a/substrate/frame/balances/src/benchmarking.rs b/substrate/frame/balances/src/benchmarking.rs index f48220c1ba..605561d597 100644 --- a/substrate/frame/balances/src/benchmarking.rs +++ b/substrate/frame/balances/src/benchmarking.rs @@ -50,7 +50,7 @@ impl BenchmarkingSetup, RawOrigin> for { // Constants let ed = T::ExistentialDeposit::get(); - + // Select an account let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1; let user = account::("user", u); @@ -58,8 +58,7 @@ impl BenchmarkingSetup, RawOrigin> for // Give some multiple of the existential deposit + creation fee + transfer fee let e = components.iter().find(|&c| c.0 == BenchmarkParameter::E).unwrap().1; - let mut balance = ed.saturating_mul(e.into()); - balance += T::CreationFee::get(); + let balance = ed.saturating_mul(e.into()); let _ = as Currency<_>>::make_free_balance_be(&user, balance); // Transfer `e - 1` existential deposits + 1 unit, which guarantees to create one account, and reap this user. @@ -90,7 +89,7 @@ impl BenchmarkingSetup, RawOrigin> for { // Constants let ed = T::ExistentialDeposit::get(); - + // Select a sender let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1; let user = account::("user", u); @@ -135,7 +134,7 @@ impl BenchmarkingSetup, RawOrigin> for { // Constants let ed = T::ExistentialDeposit::get(); - + // Select a sender let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1; let user = account::("user", u); @@ -176,7 +175,7 @@ impl BenchmarkingSetup, RawOrigin> for { // Constants let ed = T::ExistentialDeposit::get(); - + // Select a sender let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1; let user = account::("user", u); @@ -208,7 +207,7 @@ impl BenchmarkingSetup, RawOrigin> for { // Constants let ed = T::ExistentialDeposit::get(); - + // Select a sender let u = components.iter().find(|&c| c.0 == BenchmarkParameter::U).unwrap().1; let user = account::("user", u); @@ -275,7 +274,7 @@ impl Benchmarking for Module { sp_io::benchmarking::commit_db(); sp_io::benchmarking::wipe_db(); - let components = , RawOrigin>>::components(&selected_benchmark); + let components = , RawOrigin>>::components(&selected_benchmark); // results go here let mut results: Vec = Vec::new(); // Select the component we will be benchmarking. Each component will be benchmarked. diff --git a/substrate/frame/balances/src/lib.rs b/substrate/frame/balances/src/lib.rs index 10151ab461..fc2294dd51 100644 --- a/substrate/frame/balances/src/lib.rs +++ b/substrate/frame/balances/src/lib.rs @@ -149,21 +149,24 @@ #![cfg_attr(not(feature = "std"), no_std)] #[cfg(test)] -mod mock; +mod tests_local; #[cfg(test)] +mod tests_composite; +#[cfg(test)] +#[macro_use] mod tests; -mod migration; mod benchmarking; use sp_std::prelude::*; -use sp_std::{cmp, result, mem, fmt::Debug, ops::BitOr}; +use sp_std::{cmp, result, mem, fmt::Debug, ops::BitOr, convert::Infallible}; use codec::{Codec, Encode, Decode}; use frame_support::{ StorageValue, Parameter, decl_event, decl_storage, decl_module, decl_error, ensure, - weights::SimpleDispatchInfo, traits::{ - UpdateBalanceOutcome, Currency, OnReapAccount, OnUnbalanced, TryDrop, + weights::SimpleDispatchInfo, traits::{ + Currency, OnReapAccount, OnUnbalanced, TryDrop, StoredMap, WithdrawReason, WithdrawReasons, LockIdentifier, LockableCurrency, ExistenceRequirement, - Imbalance, SignedImbalance, ReservableCurrency, Get, ExistenceRequirement::KeepAlive + Imbalance, SignedImbalance, ReservableCurrency, Get, ExistenceRequirement::KeepAlive, + ExistenceRequirement::AllowDeath, IsDeadAccount, BalanceStatus as Status } }; use sp_runtime::{ @@ -173,8 +176,10 @@ use sp_runtime::{ MaybeSerializeDeserialize, Saturating, Bounded, }, }; -use frame_system::{self as system, IsDeadAccount, OnNewAccount, ensure_signed, ensure_root}; -use migration::{get_storage_value, put_storage_value, StorageIterator}; +use frame_system::{self as system, ensure_signed, ensure_root}; +use frame_support::storage::migration::{ + get_storage_value, take_storage_value, put_storage_value, StorageIterator +}; pub use self::imbalances::{PositiveImbalance, NegativeImbalance}; @@ -183,21 +188,11 @@ pub trait Subtrait: frame_system::Trait { type Balance: Parameter + Member + AtLeast32Bit + Codec + Default + Copy + MaybeSerializeDeserialize + Debug; - /// A function that is invoked when the free-balance and the reserved-balance has fallen below - /// the existential deposit and both have been reduced to zero. - /// - /// All resources should be cleaned up all resources associated with the given account. - type OnReapAccount: OnReapAccount; - - /// Handler for when a new account is created. - type OnNewAccount: OnNewAccount; - /// The minimum amount required to keep an account open. type ExistentialDeposit: Get; - /// The fee required to create an account. If you're doing significant stuff with `OnNewAccount` - /// then you'll probably want to make this non-zero. - type CreationFee: Get; + /// The means of storing the balances of an account. + type AccountStore: StoredMap>; } pub trait Trait: frame_system::Trait { @@ -205,19 +200,6 @@ pub trait Trait: frame_system::Trait { type Balance: Parameter + Member + AtLeast32Bit + Codec + Default + Copy + MaybeSerializeDeserialize + Debug; - /// A function that is invoked when the free-balance and the reserved-balance has fallen below - /// the existential deposit and both have been reduced to zero. - /// - /// All resources should be cleaned up all resources associated with the given account. - type OnReapAccount: OnReapAccount; - - /// Handler for when a new account is created. - type OnNewAccount: OnNewAccount; - - /// Handler for the unbalanced reduction when taking fees associated with balance - /// transfer (which may also include account creation). - type TransferPayment: OnUnbalanced>; - /// Handler for the unbalanced reduction when removing a dust account. type DustRemoval: OnUnbalanced>; @@ -227,16 +209,14 @@ pub trait Trait: frame_system::Trait { /// The minimum amount required to keep an account open. type ExistentialDeposit: Get; - /// The fee required to create an account. - type CreationFee: Get; + /// The means of storing the balances of an account. + type AccountStore: StoredMap>; } impl, I: Instance> Subtrait for T { type Balance = T::Balance; - type OnReapAccount = T::OnReapAccount; - type OnNewAccount = T::OnNewAccount; type ExistentialDeposit = T::ExistentialDeposit; - type CreationFee = T::CreationFee; + type AccountStore = T::AccountStore; } decl_event!( @@ -244,12 +224,13 @@ decl_event!( ::AccountId, >::Balance { - /// A new account was created. - NewAccount(AccountId, Balance), - /// An account was reaped. - ReapedAccount(AccountId, Balance), - /// Transfer succeeded (from, to, value, fees). - Transfer(AccountId, AccountId, Balance, Balance), + /// An account was created with some free balance. + Endowed(AccountId, Balance), + /// An account was removed whose balance was non-zero but below ExistentialDeposit, + /// resulting in an outright loss. + DustLost(AccountId, Balance), + /// Transfer succeeded (from, to, value). + Transfer(AccountId, AccountId, Balance), /// A balance was set by root (who, free, reserved). BalanceSet(AccountId, Balance, Balance), /// Some amount was deposited (e.g. for transaction fees). @@ -376,11 +357,9 @@ decl_storage! { /// /// NOTE: THIS MAY NEVER BE IN EXISTENCE AND YET HAVE A `total().is_zero()`. If the total /// is ever zero, then the entry *MUST* be removed. - pub Account get(fn account) - build(|config: &GenesisConfig| config.balances.iter() - .map(|&(ref who, free)| (who.clone(), AccountData { free, .. Default::default() })) - .collect::>() - ): map hasher(blake2_256) T::AccountId => AccountData; + /// + /// NOTE: This is only used in the case that this module is used to store balances. + pub Account: map hasher(blake2_256) T::AccountId => AccountData; /// Any liquidity locks on some account balances. /// NOTE: Should only be accessed when setting, changing and freeing a lock. @@ -405,6 +384,9 @@ decl_storage! { "the balance of any account should always be more than existential deposit.", ) } + for &(ref who, free) in config.balances.iter() { + T::AccountStore::insert(who, AccountData { free, .. Default::default() }); + } }); } } @@ -416,9 +398,6 @@ decl_module! { /// The minimum amount required to keep an account open. const ExistentialDeposit: T::Balance = T::ExistentialDeposit::get(); - /// The fee required to create an account. - const CreationFee: T::Balance = T::CreationFee::get(); - fn deposit_event() = default; /// Transfer some liquid free balance to another account. @@ -484,24 +463,25 @@ decl_module! { let new_free = if wipeout { Zero::zero() } else { new_free }; let new_reserved = if wipeout { Zero::zero() } else { new_reserved }; - let old_account = Account::::get(&who); + let (free, reserved) = Self::mutate_account(&who, |account| { + if new_free > account.free { + mem::drop(PositiveImbalance::::new(new_free - account.free)); + } else if new_free < account.free { + mem::drop(NegativeImbalance::::new(account.free - new_free)); + } - if new_free > old_account.free { - mem::drop(PositiveImbalance::::new(new_free - old_account.free)); - } else if new_free < old_account.free { - mem::drop(NegativeImbalance::::new(old_account.free - new_free)); - } + if new_reserved > account.reserved { + mem::drop(PositiveImbalance::::new(new_reserved - account.reserved)); + } else if new_reserved < account.reserved { + mem::drop(NegativeImbalance::::new(account.reserved - new_reserved)); + } - if new_reserved > old_account.reserved { - mem::drop(PositiveImbalance::::new(new_reserved - old_account.reserved)); - } else if new_reserved < old_account.reserved { - mem::drop(NegativeImbalance::::new(old_account.reserved - new_reserved)); - } + account.free = new_free; + account.reserved = new_reserved; - let account = AccountData { free: new_free, reserved: new_reserved, ..old_account }; - Self::set_account(&who, &account, &old_account); - - Self::deposit_event(RawEvent::BalanceSet(who, account.free, account.reserved)); + (account.free, account.reserved) + }); + Self::deposit_event(RawEvent::BalanceSet(who, free, reserved)); } /// Exactly as `transfer`, except the origin must be root and the source account may be @@ -626,87 +606,110 @@ impl, I: Instance> Module { put_storage_value(b"Balances", b"Locks", &hash, locks); put_storage_value(b"Balances", b"Account", &hash, account); } + + for (hash, balances) in StorageIterator::>::new(b"Balances", b"Account").drain() { + let nonce = take_storage_value::(b"System", b"AccountNonce", &hash).unwrap_or_default(); + put_storage_value(b"System", b"Account", &hash, (nonce, balances)); + } } /// Get the free balance of an account. pub fn free_balance(who: impl sp_std::borrow::Borrow) -> T::Balance { - Account::::get(who.borrow()).free + Self::account(who.borrow()).free } /// Get the balance of an account that can be used for transfers, reservations, or any other /// non-locking, non-transaction-fee activity. Will be at most `free_balance`. pub fn usable_balance(who: impl sp_std::borrow::Borrow) -> T::Balance { - Account::::get(who.borrow()).usable(Reasons::Misc) + Self::account(who.borrow()).usable(Reasons::Misc) } /// Get the balance of an account that can be used for paying transaction fees (not tipping, /// or any other kind of fees, though). Will be at most `free_balance`. pub fn usable_balance_for_fees(who: impl sp_std::borrow::Borrow) -> T::Balance { - Account::::get(who.borrow()).usable(Reasons::Fee) + Self::account(who.borrow()).usable(Reasons::Fee) } /// Get the reserved balance of an account. pub fn reserved_balance(who: impl sp_std::borrow::Borrow) -> T::Balance { - Account::::get(who.borrow()).reserved + Self::account(who.borrow()).reserved } - /// Set both the free and reserved balance of an account to some new value. Will enforce + /// Get both the free and reserved balances of an account. + fn account(who: &T::AccountId) -> AccountData { + T::AccountStore::get(&who) + } + + /// Places the `free` and `reserved` parts of `new` into `account`. Also does any steps needed + /// after mutating an account. This includes DustRemoval unbalancing, in the case than the `new` + /// account's total balance is non-zero but below ED. + /// + /// Returns the final free balance, iff the account was previously of total balance zero, known + /// as its "endowment". + fn post_mutation( + who: &T::AccountId, + new: AccountData, + ) -> Option> { + let total = new.total(); + if total < T::ExistentialDeposit::get() { + if !total.is_zero() { + T::DustRemoval::on_unbalanced(NegativeImbalance::new(total)); + Self::deposit_event(RawEvent::DustLost(who.clone(), total)); + } + None + } else { + Some(new) + } + } + + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce /// `ExistentialDeposit` law, annulling the account as needed. /// - /// Will return `AccountKilled` if either reserved or free are too low. - /// - /// NOTE: This assumes that `account` is the same as `Self::account(who)` except for altered - /// values of `free` and `balance`. - /// /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used /// when it is known that the account already exists. /// /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that /// the caller will do this. - fn set_account( + fn mutate_account( who: &T::AccountId, - account: &AccountData, - old: &AccountData, - ) -> UpdateBalanceOutcome { - let total = account.free + account.reserved; - if total < T::ExistentialDeposit::get() { - T::DustRemoval::on_unbalanced(NegativeImbalance::new(total)); - if !old.total().is_zero() { - Self::reap_account(who, total); - UpdateBalanceOutcome::AccountKilled - } else { - UpdateBalanceOutcome::StillDead - } - } else { - if old.total().is_zero() { - Self::about_to_create_account(who, account.free); - } - Account::::insert(who, account); - UpdateBalanceOutcome::Updated - } + f: impl FnOnce(&mut AccountData) -> R + ) -> R { + Self::try_mutate_account(who, |a| -> Result { Ok(f(a)) }) + .expect("Error is infallible; qed") } - /// Register a new account (with existential balance). + /// Mutate an account to some new value, or delete it entirely with `None`. Will enforce + /// `ExistentialDeposit` law, annulling the account as needed. This will do nothing if the + /// result of `f` is an `Err`. /// - /// This just calls appropriate hooks. It doesn't (necessarily) make any state changes. - fn about_to_create_account(who: &T::AccountId, balance: T::Balance) { - T::OnNewAccount::on_new_account(&who); - Self::deposit_event(RawEvent::NewAccount(who.clone(), balance.clone())); - } - - /// Unregister an account. + /// NOTE: Doesn't do any preparatory work for creating a new account, so should only be used + /// when it is known that the account already exists. /// - /// This just removes the nonce and leaves an event. - fn reap_account(who: &T::AccountId, dust: T::Balance) { - Locks::::remove(who); - Account::::remove(who); - T::OnReapAccount::on_reap_account(who); - Self::deposit_event(RawEvent::ReapedAccount(who.clone(), dust)); + /// NOTE: LOW-LEVEL: This will not attempt to maintain total issuance. It is expected that + /// the caller will do this. + fn try_mutate_account( + who: &T::AccountId, + f: impl FnOnce(&mut AccountData) -> Result + ) -> Result { + T::AccountStore::try_mutate_exists(who, |maybe_account| { + let mut account = maybe_account.take().unwrap_or_default(); + let was_zero = account.total().is_zero(); + f(&mut account).map(move |result| { + let maybe_endowed = if was_zero { Some(account.free) } else { None }; + *maybe_account = Self::post_mutation(who, account); + (maybe_endowed, result) + }) + }).map(|(maybe_endowed, result)| { + if let Some(endowed) = maybe_endowed { + Self::deposit_event(RawEvent::Endowed(who.clone(), endowed)); + } + result + }) } /// Update the account entry for `who`, given the locks. fn update_locks(who: &T::AccountId, locks: &[BalanceLock]) { - Account::::mutate(who, |b| { + Self::mutate_account(who, |b| { b.misc_frozen = Zero::zero(); b.fee_frozen = Zero::zero(); for l in locks.iter() { @@ -722,6 +725,13 @@ impl, I: Instance> Module { } } +impl, I: Instance> OnReapAccount for Module { + fn on_reap_account(who: &T::AccountId) { + Locks::::remove(who); + Account::::remove(who); + } +} + // wrapping these imbalances in a private module is necessary to ensure absolute privacy // of the inner member. mod imbalances { @@ -885,9 +895,8 @@ mod imbalances { // its type declaration). // This works as long as `increase_total_issuance_by` doesn't use the Imbalance // types (basically for charging fees). -// This should eventually be refactored so that the three type items that do -// depend on the Imbalance type (TransferPayment, DustRemoval) -// are placed in their own SRML module. +// This should eventually be refactored so that the type item that +// depends on the Imbalance type (DustRemoval) is placed in its own SRML module. struct ElevatedTrait, I: Instance>(T, I); impl, I: Instance> Clone for ElevatedTrait { fn clone(&self) -> Self { unimplemented!() } @@ -913,16 +922,16 @@ impl, I: Instance> frame_system::Trait for ElevatedTrait { type AvailableBlockRatio = T::AvailableBlockRatio; type Version = T::Version; type ModuleToIndex = T::ModuleToIndex; + type OnNewAccount = T::OnNewAccount; + type OnReapAccount = T::OnReapAccount; + type AccountData = T::AccountData; } impl, I: Instance> Trait for ElevatedTrait { type Balance = T::Balance; - type OnReapAccount = T::OnReapAccount; - type OnNewAccount = T::OnNewAccount; type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = T::ExistentialDeposit; - type CreationFee = T::CreationFee; + type AccountStore = T::AccountStore; } impl, I: Instance> Currency for Module where @@ -950,10 +959,6 @@ impl, I: Instance> Currency for Module where T::ExistentialDeposit::get() } - fn free_balance(who: &T::AccountId) -> Self::Balance { - Account::::get(who).free - } - // Burn funds from the total issuance, returning a positive imbalance for the amount burned. // Is a no-op if amount to be burned is zero. fn burn(mut amount: Self::Balance) -> Self::PositiveImbalance { @@ -981,6 +986,10 @@ impl, I: Instance> Currency for Module where NegativeImbalance::new(amount) } + fn free_balance(who: &T::AccountId) -> Self::Balance { + Self::account(who).free + } + // Ensure that an account can withdraw from their free balance given any existing withdrawal // restrictions like locks and vesting balance. // Is a no-op if amount to be withdrawn is zero. @@ -996,7 +1005,7 @@ impl, I: Instance> Currency for Module where new_balance: T::Balance, ) -> DispatchResult { if amount.is_zero() { return Ok(()) } - let min_balance = Account::::get(who).frozen(reasons.into()); + let min_balance = Self::account(who).frozen(reasons.into()); ensure!(new_balance >= min_balance, Error::::LiquidityRestrictions); Ok(()) } @@ -1011,80 +1020,38 @@ impl, I: Instance> Currency for Module where ) -> DispatchResult { if value.is_zero() || transactor == dest { return Ok(()) } - let old_from_account = Self::account(transactor); - let mut from_account = old_from_account.clone(); - let old_to_account = Self::account(dest); - let mut to_account = old_to_account.clone(); + Self::try_mutate_account(dest, |to_account| -> DispatchResult { + Self::try_mutate_account(transactor, |from_account| -> DispatchResult { + from_account.free = from_account.free.checked_sub(&value) + .ok_or(Error::::InsufficientBalance)?; - let would_create = to_account.total().is_zero(); - let fee = if would_create { T::CreationFee::get() } else { Zero::zero() }; - let liability = value.checked_add(&fee).ok_or(Error::::Overflow)?; + // NOTE: total stake being stored in the same type means that this could never overflow + // but better to be safe than sorry. + to_account.free = to_account.free.checked_add(&value).ok_or(Error::::Overflow)?; - from_account.free = from_account.free.checked_sub(&liability) - .ok_or(Error::::InsufficientBalance)?; + let ed = T::ExistentialDeposit::get(); + ensure!(to_account.total() >= ed, Error::::ExistentialDeposit); - // NOTE: total stake being stored in the same type means that this could never overflow - // but better to be safe than sorry. - to_account.free = to_account.free.checked_add(&value).ok_or(Error::::Overflow)?; + Self::ensure_can_withdraw( + transactor, + value, + WithdrawReason::Transfer.into(), + from_account.free, + )?; - let ed = T::ExistentialDeposit::get(); - ensure!(to_account.free >= ed, Error::::ExistentialDeposit); + let allow_death = existence_requirement == ExistenceRequirement::AllowDeath; + ensure!(allow_death || from_account.free >= ed, Error::::KeepAlive); - Self::ensure_can_withdraw( - transactor, - value, - WithdrawReason::Transfer.into(), - from_account.free, - )?; - - let allow_death = existence_requirement == ExistenceRequirement::AllowDeath; - ensure!(allow_death || from_account.free >= ed, Error::::KeepAlive); - - Self::set_account(transactor, &from_account, &old_from_account); - - // Take action on the set_account call. - // This will emit events that _resulted_ from the transfer. - Self::set_account(dest, &to_account, &old_to_account); + Ok(()) + }) + })?; // Emit transfer event. - Self::deposit_event(RawEvent::Transfer(transactor.clone(), dest.clone(), value, fee)); - - T::TransferPayment::on_unbalanced(NegativeImbalance::new(fee)); + Self::deposit_event(RawEvent::Transfer(transactor.clone(), dest.clone(), value)); Ok(()) } - // Withdraw some free balance from an account, respecting existence requirements. - // Is a no-op if value to be withdrawn is zero. - fn withdraw( - who: &T::AccountId, - value: Self::Balance, - reasons: WithdrawReasons, - liveness: ExistenceRequirement, - ) -> result::Result { - if value.is_zero() { return Ok(NegativeImbalance::zero()); } - - let old_account = Self::account(who); - let mut account = old_account.clone(); - if let Some(new_free_account) = account.free.checked_sub(&value) { - // if we need to keep the account alive... - if liveness == ExistenceRequirement::KeepAlive - // ...and it would be dead afterwards... - && new_free_account < T::ExistentialDeposit::get() - // ...yet is was alive before - && account.free >= T::ExistentialDeposit::get() - { - Err(Error::::KeepAlive)? - } - Self::ensure_can_withdraw(who, value, reasons, new_free_account)?; - account.free = new_free_account; - Self::set_account(who, &account, &old_account); - Ok(NegativeImbalance::new(value)) - } else { - Err(Error::::InsufficientBalance)? - } - } - /// Slash a target account `who`, returning the negative imbalance created and any left over /// amount that could not be slashed. /// @@ -1100,22 +1067,19 @@ impl, I: Instance> Currency for Module where ) -> (Self::NegativeImbalance, Self::Balance) { if value.is_zero() { return (NegativeImbalance::zero(), Zero::zero()) } - let old_account = Self::account(who); - let mut account = old_account.clone(); + Self::mutate_account(who, |account| { + let free_slash = cmp::min(account.free, value); + account.free -= free_slash; - let free_slash = cmp::min(account.free, value); - account.free -= free_slash; - - let remaining_slash = value - free_slash; - let result = if !remaining_slash.is_zero() { - let reserved_slash = cmp::min(account.reserved, remaining_slash); - account.reserved -= reserved_slash; - (NegativeImbalance::new(free_slash + reserved_slash), remaining_slash - reserved_slash) - } else { - (NegativeImbalance::new(value), Zero::zero()) - }; - Self::set_account(who, &account, &old_account); - result + let remaining_slash = value - free_slash; + if !remaining_slash.is_zero() { + let reserved_slash = cmp::min(account.reserved, remaining_slash); + account.reserved -= reserved_slash; + (NegativeImbalance::new(free_slash + reserved_slash), remaining_slash - reserved_slash) + } else { + (NegativeImbalance::new(value), Zero::zero()) + } + }) } /// Deposit some `value` into the free balance of an existing target account `who`. @@ -1124,16 +1088,14 @@ impl, I: Instance> Currency for Module where fn deposit_into_existing( who: &T::AccountId, value: Self::Balance - ) -> result::Result { + ) -> Result { if value.is_zero() { return Ok(PositiveImbalance::zero()) } - let old_account = Self::account(who); - let mut account = old_account.clone(); - ensure!(!account.total().is_zero(), Error::::DeadAccount); - account.free = account.free.checked_add(&value).ok_or(Error::::Overflow)?; - - Self::set_account(who, &account, &old_account); - Ok(PositiveImbalance::new(value)) + Self::try_mutate_account(who, |account| -> Result { + ensure!(!account.total().is_zero(), Error::::DeadAccount); + account.free = account.free.checked_add(&value).ok_or(Error::::Overflow)?; + Ok(PositiveImbalance::new(value)) + }) } /// Deposit some `value` into the free balance of `who`, possibly creating a new account. @@ -1148,34 +1110,58 @@ impl, I: Instance> Currency for Module where ) -> Self::PositiveImbalance { if value.is_zero() { return Self::PositiveImbalance::zero() } - let old_account = Self::account(who); - let mut account = old_account.clone(); - let ed = T::ExistentialDeposit::get(); + Self::try_mutate_account(who, |account| -> Result { + // bail if not yet created and this operation wouldn't be enough to create it. + let ed = T::ExistentialDeposit::get(); + ensure!(value >= ed || !account.total().is_zero(), Self::PositiveImbalance::zero()); - // bail if not yet created and this operation wouldn't be enough to create it. - if value < ed && account.total().is_zero() { return Self::PositiveImbalance::zero() } + // defensive only: overflow should never happen, however in case it does, then this + // operation is a no-op. + account.free = account.free.checked_add(&value).ok_or(Self::PositiveImbalance::zero())?; - // defensive only: overflow should never happen, however in case it does, then this - // operation is a no-op. - account.free = match account.free.checked_add(&value) { - Some(f) => f, - None => return Self::PositiveImbalance::zero(), - }; + Ok(PositiveImbalance::new(value)) + }).unwrap_or_else(|x| x) + } - Self::set_account(who, &account, &old_account); + /// Withdraw some free balance from an account, respecting existence requirements. + /// + /// Is a no-op if value to be withdrawn is zero. + fn withdraw( + who: &T::AccountId, + value: Self::Balance, + reasons: WithdrawReasons, + liveness: ExistenceRequirement, + ) -> result::Result { + if value.is_zero() { return Ok(NegativeImbalance::zero()); } - PositiveImbalance::new(value) + Self::try_mutate_account(who, |account| + -> Result + { + let new_free_account = account.free.checked_sub(&value) + .ok_or(Error::::InsufficientBalance)?; + + // bail if we need to keep the account alive and this would kill it. + let ed = T::ExistentialDeposit::get(); + let would_be_dead = new_free_account + account.reserved < ed; + let would_kill = would_be_dead && account.free + account.reserved >= ed; + ensure!(liveness == AllowDeath || !would_kill, Error::::KeepAlive); + + Self::ensure_can_withdraw(who, value, reasons, new_free_account)?; + + account.free = new_free_account; + + Ok(NegativeImbalance::new(value)) + }) } /// Force the new free balance of a target account `who` to some new value `balance`. - fn make_free_balance_be(who: &T::AccountId, value: Self::Balance) -> ( - SignedImbalance, - UpdateBalanceOutcome - ) { - let old_account = Self::account(who); - let mut account = old_account.clone(); - - if value < T::ExistentialDeposit::get() && account.free.is_zero() { + fn make_free_balance_be(who: &T::AccountId, value: Self::Balance) + -> SignedImbalance + { + Self::try_mutate_account(who, |account| + -> Result, ()> + { + let ed = T::ExistentialDeposit::get(); // 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 @@ -1183,24 +1169,16 @@ impl, I: Instance> Currency for Module where // 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 account.free <= value { - SignedImbalance::Positive(PositiveImbalance::new(value - account.free)) - } else { - SignedImbalance::Negative(NegativeImbalance::new(account.free - value)) - }; - account.free = value; + ensure!(value + account.reserved >= ed || !account.total().is_zero(), ()); - // If the balance is too low, then the account is reaped. - // Free balance can never be less than ED. If that happens, it gets reduced to zero - // and the account information relevant to this subsystem is deleted (i.e. the - // account is reaped). - let outcome = Self::set_account(who, &account, &old_account); - (imbalance, outcome) + let imbalance = if account.free <= value { + SignedImbalance::Positive(PositiveImbalance::new(value - account.free)) + } else { + SignedImbalance::Negative(NegativeImbalance::new(account.free - value)) + }; + account.free = value; + Ok(imbalance) + }).unwrap_or(SignedImbalance::Positive(Self::PositiveImbalance::zero())) } } @@ -1226,18 +1204,14 @@ impl, I: Instance> ReservableCurrency for Module /// Move `value` from the free balance from `who` to their reserved balance. /// /// Is a no-op if value to be reserved is zero. - fn reserve(who: &T::AccountId, value: Self::Balance) -> result::Result<(), DispatchError> { + fn reserve(who: &T::AccountId, value: Self::Balance) -> DispatchResult { if value.is_zero() { return Ok(()) } - let old_account = Self::account(who); - let mut account = old_account.clone(); - - account.free = account.free.checked_sub(&value).ok_or(Error::::InsufficientBalance)?; - account.reserved = account.reserved.checked_add(&value).ok_or(Error::::Overflow)?; - Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve.into(), account.free)?; - - Self::set_account(who, &account, &old_account); - Ok(()) + Self::try_mutate_account(who, |account| -> DispatchResult { + account.free = account.free.checked_sub(&value).ok_or(Error::::InsufficientBalance)?; + account.reserved = account.reserved.checked_add(&value).ok_or(Error::::Overflow)?; + Self::ensure_can_withdraw(who, value, WithdrawReason::Reserve.into(), account.free) + }) } /// Unreserve some funds, returning any amount that was unable to be unreserved. @@ -1246,18 +1220,14 @@ impl, I: Instance> ReservableCurrency for Module fn unreserve(who: &T::AccountId, value: Self::Balance) -> Self::Balance { if value.is_zero() { return Zero::zero() } - let old_account = Self::account(who); - let mut account = old_account.clone(); - - let actual = cmp::min(account.reserved, value); - account.reserved -= actual; - // defensive only: this can never fail since total issuance which is at least free+reserved - // fits into the same datatype. - account.free = account.free.saturating_add(actual); - - Self::set_account(who, &account, &old_account); - - value - actual + Self::mutate_account(who, |account| { + let actual = cmp::min(account.reserved, value); + account.reserved -= actual; + // defensive only: this can never fail since total issuance which is at least free+reserved + // fits into the same datatype. + account.free = account.free.saturating_add(actual); + value - actual + }) } /// Slash from reserved balance, returning the negative imbalance created, @@ -1270,47 +1240,46 @@ impl, I: Instance> ReservableCurrency for Module ) -> (Self::NegativeImbalance, Self::Balance) { if value.is_zero() { return (NegativeImbalance::zero(), Zero::zero()) } - let old_account = Self::account(who); - let mut account = old_account.clone(); - - // underflow should never happen, but it if does, there's nothing to be done here. - let actual = cmp::min(account.reserved, value); - account.reserved -= actual; - - Self::set_account(who, &account, &old_account); - - (NegativeImbalance::new(actual), value - actual) + Self::mutate_account(who, |account| { + // underflow should never happen, but it if does, there's nothing to be done here. + let actual = cmp::min(account.reserved, value); + account.reserved -= actual; + (NegativeImbalance::new(actual), value - actual) + }) } - /// Move the reserved balance of one account into the free balance of another. + /// Move the reserved balance of one account into the balance of another, according to `status`. /// - /// Is a no-op if the value to be moved is zero. + /// Is a no-op if: + /// - the value to be moved is zero; or + /// - the `slashed` id equal to `beneficiary` and the `status` is `Reserved`. fn repatriate_reserved( slashed: &T::AccountId, beneficiary: &T::AccountId, value: Self::Balance, - ) -> result::Result { - if value.is_zero() { return Ok (Zero::zero()) } + status: Status, + ) -> Result { + if value.is_zero() { return Ok(Zero::zero()) } if slashed == beneficiary { - return Ok(Self::unreserve(slashed, value)); + return match status { + Status::Free => Ok(Self::unreserve(slashed, value)), + Status::Reserved => Ok(value.saturating_sub(Self::reserved_balance(slashed))), + }; } - let old_to_account = Self::account(beneficiary); - let mut to_account = old_to_account.clone(); - ensure!(!to_account.total().is_zero(), Error::::DeadAccount); - - let old_from_account = Self::account(slashed); - let mut from_account = old_from_account.clone(); - let actual = cmp::min(from_account.reserved, value); - - to_account.free = to_account.free.checked_add(&actual).ok_or(Error::::Overflow)?; - from_account.reserved -= actual; - - Self::set_account(slashed, &from_account, &old_from_account); - Self::set_account(beneficiary, &to_account, &old_to_account); - - Ok(value - actual) + Self::try_mutate_account(beneficiary, |to_account| -> Result { + ensure!(!to_account.total().is_zero(), Error::::DeadAccount); + Self::try_mutate_account(slashed, |from_account| -> Result { + let actual = cmp::min(from_account.reserved, value); + match status { + Status::Free => to_account.free = to_account.free.checked_add(&actual).ok_or(Error::::Overflow)?, + Status::Reserved => to_account.reserved = to_account.reserved.checked_add(&actual).ok_or(Error::::Overflow)?, + } + from_account.reserved -= actual; + Ok(value - actual) + }) + }) } } @@ -1380,12 +1349,11 @@ where } } -impl, I: Instance> IsDeadAccount for Module -where +impl, I: Instance> IsDeadAccount for Module where T::Balance: MaybeSerializeDeserialize + Debug { fn is_dead_account(who: &T::AccountId) -> bool { // this should always be exactly equivalent to `Self::account(who).total().is_zero()` - !Account::::contains_key(who) + !T::AccountStore::is_explicit(who) } } diff --git a/substrate/frame/balances/src/tests.rs b/substrate/frame/balances/src/tests.rs index 816768901b..3d0c9e9207 100644 --- a/substrate/frame/balances/src/tests.rs +++ b/substrate/frame/balances/src/tests.rs @@ -14,644 +14,638 @@ // You should have received a copy of the GNU General Public License // along with Substrate. If not, see . -//! Tests for the module. +//! Macro for creating the tests for the module. -use super::*; -use mock::{Balances, ExtBuilder, Test, System, info_from_weight, CALL}; -use sp_runtime::{Fixed64, traits::{SignedExtension, BadOrigin}}; -use frame_support::{ - assert_noop, assert_ok, assert_err, - traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons, - Currency, ReservableCurrency, ExistenceRequirement::AllowDeath} -}; -use pallet_transaction_payment::ChargeTransactionPayment; -use frame_system::RawOrigin; +#[macro_export] +macro_rules! decl_tests { + ($test:ty, $ext_builder:ty, $existential_deposit:expr) => { -const ID_1: LockIdentifier = *b"1 "; -const ID_2: LockIdentifier = *b"2 "; + use crate::*; + use sp_runtime::{Fixed64, traits::{SignedExtension, BadOrigin}}; + use frame_support::{ + assert_noop, assert_ok, assert_err, + traits::{LockableCurrency, LockIdentifier, WithdrawReason, WithdrawReasons, + Currency, ReservableCurrency, ExistenceRequirement::AllowDeath} + }; + use pallet_transaction_payment::ChargeTransactionPayment; + use frame_system::RawOrigin; -#[test] -fn basic_locking_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - assert_eq!(Balances::free_balance(1), 10); - Balances::set_lock(ID_1, &1, 9, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 5, AllowDeath), - Error::::LiquidityRestrictions - ); - }); -} - -#[test] -fn partial_locking_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); -} - -#[test] -fn lock_removal_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, u64::max_value(), WithdrawReasons::all()); - Balances::remove_lock(ID_1, &1); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); -} - -#[test] -fn lock_replacement_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, u64::max_value(), WithdrawReasons::all()); - Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); -} - -#[test] -fn double_locking_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); - Balances::set_lock(ID_2, &1, 5, WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); -} - -#[test] -fn combination_locking_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, u64::max_value(), WithdrawReasons::none()); - Balances::set_lock(ID_2, &1, 0, WithdrawReasons::all()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - }); -} - -#[test] -fn lock_value_extension_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::::LiquidityRestrictions - ); - Balances::extend_lock(ID_1, &1, 2, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::::LiquidityRestrictions - ); - Balances::extend_lock(ID_1, &1, 8, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 3, AllowDeath), - Error::::LiquidityRestrictions - ); - }); -} - -#[test] -fn lock_reasons_should_work() { - ExtBuilder::default() - .existential_deposit(1) - .monied(true) - .build() - .execute_with(|| { - pallet_transaction_payment::NextFeeMultiplier::put(Fixed64::from_natural(1)); - Balances::set_lock(ID_1, &1, 10, WithdrawReason::Reserve.into()); - assert_noop!( - >::transfer(&1, &2, 1, AllowDeath), - Error::::LiquidityRestrictions - ); - assert_noop!( - >::reserve(&1, 1), - Error::::LiquidityRestrictions - ); - assert!( as SignedExtension>::pre_dispatch( - ChargeTransactionPayment::from(1), - &1, - CALL, - info_from_weight(1), - 1, - ).is_err()); - assert!( as SignedExtension>::pre_dispatch( - ChargeTransactionPayment::from(0), - &1, - CALL, - info_from_weight(1), - 1, - ).is_ok()); - - Balances::set_lock(ID_1, &1, 10, WithdrawReason::TransactionPayment.into()); - assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); - assert_ok!(>::reserve(&1, 1)); - assert!( as SignedExtension>::pre_dispatch( - ChargeTransactionPayment::from(1), - &1, - CALL, - info_from_weight(1), - 1, - ).is_err()); - assert!( as SignedExtension>::pre_dispatch( - ChargeTransactionPayment::from(0), - &1, - CALL, - info_from_weight(1), - 1, - ).is_err()); - }); -} - -#[test] -fn lock_block_number_extension_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 10, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::::LiquidityRestrictions - ); - Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::::LiquidityRestrictions - ); - System::set_block_number(2); - Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::all()); - assert_noop!( - >::transfer(&1, &2, 3, AllowDeath), - Error::::LiquidityRestrictions - ); - }); -} - -#[test] -fn lock_reasons_extension_should_work() { - ExtBuilder::default().existential_deposit(1).monied(true).build().execute_with(|| { - Balances::set_lock(ID_1, &1, 10, WithdrawReason::Transfer.into()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::::LiquidityRestrictions - ); - Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::none()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::::LiquidityRestrictions - ); - Balances::extend_lock(ID_1, &1, 10, WithdrawReason::Reserve.into()); - assert_noop!( - >::transfer(&1, &2, 6, AllowDeath), - Error::::LiquidityRestrictions - ); - }); -} - -#[test] -fn default_indexing_on_new_accounts_should_not_work2() { - ExtBuilder::default() - .existential_deposit(10) - .creation_fee(50) - .monied(true) - .build() - .execute_with(|| { - assert_eq!(Balances::is_dead_account(&5), true); // account 5 should not exist - // ext_deposit is 10, value is 9, not satisfies for ext_deposit - assert_noop!( - Balances::transfer(Some(1).into(), 5, 9), - Error::::ExistentialDeposit, - ); - assert_eq!(Balances::is_dead_account(&5), true); // account 5 should not exist - assert_eq!(Balances::free_balance(1), 100); - }); -} - -#[test] -fn reserved_balance_should_prevent_reclaim_count() { - ExtBuilder::default() - .existential_deposit(256 * 1) - .monied(true) - .build() - .execute_with(|| { - System::inc_account_nonce(&2); - assert_eq!(Balances::is_dead_account(&2), false); - assert_eq!(Balances::is_dead_account(&5), true); - assert_eq!(Balances::total_balance(&2), 256 * 20); - - assert_ok!(Balances::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved - assert_eq!(Balances::free_balance(2), 255); // "free" account deleted." - assert_eq!(Balances::total_balance(&2), 256 * 20); // reserve still exists. - assert_eq!(Balances::is_dead_account(&2), false); - assert_eq!(System::account_nonce(&2), 1); - - // account 4 tries to take index 1 for account 5. - assert_ok!(Balances::transfer(Some(4).into(), 5, 256 * 1 + 0x69)); - assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69); - assert_eq!(Balances::is_dead_account(&5), false); - - assert!(Balances::slash(&2, 256 * 19 + 2).1.is_zero()); // account 2 gets slashed - // "reserve" account reduced to 255 (below ED) so account deleted - assert_eq!(Balances::total_balance(&2), 0); - assert_eq!(System::account_nonce(&2), 0); // nonce zero - assert_eq!(Balances::is_dead_account(&2), true); - - // account 4 tries to take index 1 again for account 6. - assert_ok!(Balances::transfer(Some(4).into(), 6, 256 * 1 + 0x69)); - assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69); - assert_eq!(Balances::is_dead_account(&6), false); - }); -} - - -#[test] -fn reward_should_work() { - ExtBuilder::default().monied(true).build().execute_with(|| { - assert_eq!(Balances::total_balance(&1), 10); - assert_ok!(Balances::deposit_into_existing(&1, 10).map(drop)); - assert_eq!(Balances::total_balance(&1), 20); - assert_eq!(>::get(), 120); - }); -} - -#[test] -fn dust_account_removal_should_work() { - ExtBuilder::default() - .existential_deposit(100) - .monied(true) - .build() - .execute_with(|| { - System::inc_account_nonce(&2); - assert_eq!(System::account_nonce(&2), 1); - assert_eq!(Balances::total_balance(&2), 2000); - - assert_ok!(Balances::transfer(Some(2).into(), 5, 1901)); // index 1 (account 2) becomes zombie - assert_eq!(Balances::total_balance(&2), 0); - assert_eq!(Balances::total_balance(&5), 1901); - assert_eq!(System::account_nonce(&2), 0); - }); -} - -#[test] -fn dust_account_removal_should_work2() { - ExtBuilder::default() - .existential_deposit(100) - .creation_fee(50) - .monied(true) - .build() - .execute_with(|| { - System::inc_account_nonce(&2); - assert_eq!(System::account_nonce(&2), 1); - assert_eq!(Balances::total_balance(&2), 2000); - // index 1 (account 2) becomes zombie for 256*10 + 50(fee) < 256 * 10 (ext_deposit) - assert_ok!(Balances::transfer(Some(2).into(), 5, 1851)); - assert_eq!(Balances::total_balance(&2), 0); - assert_eq!(Balances::total_balance(&5), 1851); - assert_eq!(System::account_nonce(&2), 0); - }); -} - -#[test] -fn balance_works() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 42); - assert_eq!(Balances::free_balance(1), 42); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(Balances::total_balance(&1), 42); - assert_eq!(Balances::free_balance(2), 0); - assert_eq!(Balances::reserved_balance(2), 0); - assert_eq!(Balances::total_balance(&2), 0); - }); -} - -#[test] -fn balance_transfer_works() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::transfer(Some(1).into(), 2, 69)); - assert_eq!(Balances::total_balance(&1), 42); - assert_eq!(Balances::total_balance(&2), 69); - }); -} - -#[test] -fn force_transfer_works() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_noop!( - Balances::force_transfer(Some(2).into(), 1, 2, 69), - BadOrigin, - ); - assert_ok!(Balances::force_transfer(RawOrigin::Root.into(), 1, 2, 69)); - assert_eq!(Balances::total_balance(&1), 42); - assert_eq!(Balances::total_balance(&2), 69); - }); -} - -#[test] -fn reserving_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - - assert_eq!(Balances::total_balance(&1), 111); - assert_eq!(Balances::free_balance(1), 111); - assert_eq!(Balances::reserved_balance(1), 0); - - assert_ok!(Balances::reserve(&1, 69)); - - assert_eq!(Balances::total_balance(&1), 111); - assert_eq!(Balances::free_balance(1), 42); - assert_eq!(Balances::reserved_balance(1), 69); - }); -} - -#[test] -fn balance_transfer_when_reserved_should_not_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 69)); - assert_noop!( - Balances::transfer(Some(1).into(), 2, 69), - Error::::InsufficientBalance, - ); - }); -} - -#[test] -fn deducting_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 69)); - assert_eq!(Balances::free_balance(1), 42); - }); -} - -#[test] -fn refunding_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 42); - let account = Balances::account(&1); - Balances::set_account(&1, &AccountData { reserved: 69, ..account }, &account); - Balances::unreserve(&1, 69); - assert_eq!(Balances::free_balance(1), 111); - assert_eq!(Balances::reserved_balance(1), 0); - }); -} - -#[test] -fn slashing_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 69)); - assert!(Balances::slash(&1, 69).1.is_zero()); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 42); - assert_eq!(>::get(), 42); - }); -} - -#[test] -fn slashing_incomplete_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 42); - assert_ok!(Balances::reserve(&1, 21)); - assert_eq!(Balances::slash(&1, 69).1, 27); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(>::get(), 0); - }); -} - -#[test] -fn unreserving_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 111)); - Balances::unreserve(&1, 42); - assert_eq!(Balances::reserved_balance(1), 69); - assert_eq!(Balances::free_balance(1), 42); - }); -} - -#[test] -fn slashing_reserved_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 111)); - assert_eq!(Balances::slash_reserved(&1, 42).1, 0); - assert_eq!(Balances::reserved_balance(1), 69); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(>::get(), 69); - }); -} - -#[test] -fn slashing_incomplete_reserved_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 42)); - assert_eq!(Balances::slash_reserved(&1, 69).1, 27); - assert_eq!(Balances::free_balance(1), 69); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(>::get(), 69); - }); -} - -#[test] -fn transferring_reserved_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 110); - let _ = Balances::deposit_creating(&2, 1); - assert_ok!(Balances::reserve(&1, 110)); - assert_ok!(Balances::repatriate_reserved(&1, &2, 41), 0); - assert_eq!(Balances::reserved_balance(1), 69); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(2), 0); - assert_eq!(Balances::free_balance(2), 42); - }); -} - -#[test] -fn transferring_reserved_balance_to_nonexistent_should_fail() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 111); - assert_ok!(Balances::reserve(&1, 111)); - assert_noop!(Balances::repatriate_reserved(&1, &2, 42), Error::::DeadAccount); - }); -} - -#[test] -fn transferring_incomplete_reserved_balance_should_work() { - ExtBuilder::default().build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 110); - let _ = Balances::deposit_creating(&2, 1); - assert_ok!(Balances::reserve(&1, 41)); - assert_ok!(Balances::repatriate_reserved(&1, &2, 69), 28); - assert_eq!(Balances::reserved_balance(1), 0); - assert_eq!(Balances::free_balance(1), 69); - assert_eq!(Balances::reserved_balance(2), 0); - assert_eq!(Balances::free_balance(2), 42); - }); -} - -#[test] -fn transferring_too_high_value_should_not_panic() { - ExtBuilder::default().build().execute_with(|| { - Account::::insert(1, AccountData { free: u64::max_value(), .. Default::default() }); - Account::::insert(2, AccountData { free: 1, .. Default::default() }); - - assert_err!( - Balances::transfer(Some(1).into(), 2, u64::max_value()), - Error::::Overflow, - ); - - assert_eq!(Balances::free_balance(1), u64::max_value()); - assert_eq!(Balances::free_balance(2), 1); - }); -} - -#[test] -fn account_create_on_free_too_low_with_other() { - ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - 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() { - ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - // 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() { - ExtBuilder::default().existential_deposit(100).build().execute_with(|| { - assert_eq!(>::get(), 0); - - // Setup two accounts with free balance above the existential threshold. - let _ = Balances::deposit_creating(&1, 110); - let _ = Balances::deposit_creating(&2, 110); - - assert_eq!(Balances::free_balance(1), 110); - assert_eq!(Balances::free_balance(2), 110); - assert_eq!(>::get(), 220); - - // Transfer funds from account 1 of such amount that after this transfer - // the balance of account 1 will be below the existential threshold. - // This should lead to the removal of all balance of this account. - assert_ok!(Balances::transfer(Some(1).into(), 2, 20)); - - // Verify free balance removal of account 1. - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::free_balance(2), 130); - - // Verify that TotalIssuance tracks balance removal when free balance is too low. - assert_eq!(>::get(), 130); - }); -} - -#[test] -fn transfer_overflow_isnt_exploitable() { - ExtBuilder::default().creation_fee(50).build().execute_with(|| { - // Craft a value that will overflow if summed with `creation_fee`. - let evil_value = u64::max_value() - 49; - - assert_err!( - Balances::transfer(Some(1).into(), 5, evil_value), - Error::::Overflow, - ); - }); -} - -#[test] -fn burn_must_work() { - ExtBuilder::default().monied(true).build().execute_with(|| { - let init_total_issuance = Balances::total_issuance(); - let imbalance = Balances::burn(10); - assert_eq!(Balances::total_issuance(), init_total_issuance - 10); - drop(imbalance); - assert_eq!(Balances::total_issuance(), init_total_issuance); - }); -} - -#[test] -fn transfer_keep_alive_works() { - ExtBuilder::default().existential_deposit(1).build().execute_with(|| { - let _ = Balances::deposit_creating(&1, 100); - assert_err!( - Balances::transfer_keep_alive(Some(1).into(), 2, 100), - Error::::KeepAlive - ); - assert_eq!(Balances::is_dead_account(&1), false); - assert_eq!(Balances::total_balance(&1), 100); - assert_eq!(Balances::total_balance(&2), 0); - }); -} - -#[test] -#[should_panic="the balance of any account should always be more than existential deposit."] -fn cannot_set_genesis_value_below_ed() { - mock::EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = 11); - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - let _ = GenesisConfig:: { - balances: vec![(1, 10)], - }.assimilate_storage(&mut t).unwrap(); -} - -#[test] -fn dust_moves_between_free_and_reserved() { - ExtBuilder::default() - .existential_deposit(100) - .build() - .execute_with(|| { - // Set balance to free and reserved at the existential deposit - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 100, 0)); - // Check balance - assert_eq!(Balances::free_balance(1), 100); - assert_eq!(Balances::reserved_balance(1), 0); - - // Reserve some free balance - assert_ok!(Balances::reserve(&1, 50)); - // Check balance, the account should be ok. - assert_eq!(Balances::free_balance(1), 50); - assert_eq!(Balances::reserved_balance(1), 50); - - // Reserve the rest of the free balance - assert_ok!(Balances::reserve(&1, 50)); - // Check balance, the account should be ok. - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 100); - - // Unreserve everything - Balances::unreserve(&1, 100); - // Check balance, all 100 should move to free_balance - assert_eq!(Balances::free_balance(1), 100); - assert_eq!(Balances::reserved_balance(1), 0); - }); -} - -#[test] -fn account_deleted_when_just_dust() { - ExtBuilder::default() - .existential_deposit(100) - .build() - .execute_with(|| { - // Set balance to free and reserved at the existential deposit - assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 50, 50)); - // Check balance - assert_eq!(Balances::free_balance(1), 50); - assert_eq!(Balances::reserved_balance(1), 50); - - // Reserve some free balance - let _ = Balances::slash(&1, 1); - // The account should be dead. - assert!(Balances::is_dead_account(&1)); - assert_eq!(Balances::free_balance(1), 0); - assert_eq!(Balances::reserved_balance(1), 0); - }); + const ID_1: LockIdentifier = *b"1 "; + const ID_2: LockIdentifier = *b"2 "; + + pub type System = frame_system::Module<$test>; + pub type Balances = Module<$test>; + + pub const CALL: &<$test as frame_system::Trait>::Call = &(); + + /// create a transaction info struct from weight. Handy to avoid building the whole struct. + pub fn info_from_weight(w: Weight) -> DispatchInfo { + DispatchInfo { weight: w, pays_fee: true, ..Default::default() } + } + + #[test] + fn basic_locking_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + assert_eq!(Balances::free_balance(1), 10); + Balances::set_lock(ID_1, &1, 9, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 5, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + }); + } + + #[test] + fn partial_locking_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); + } + + #[test] + fn lock_removal_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, u64::max_value(), WithdrawReasons::all()); + Balances::remove_lock(ID_1, &1); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); + } + + #[test] + fn lock_replacement_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, u64::max_value(), WithdrawReasons::all()); + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); + } + + #[test] + fn double_locking_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + Balances::set_lock(ID_2, &1, 5, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); + } + + #[test] + fn combination_locking_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, u64::max_value(), WithdrawReasons::none()); + Balances::set_lock(ID_2, &1, 0, WithdrawReasons::all()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + }); + } + + #[test] + fn lock_value_extension_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, 5, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + Balances::extend_lock(ID_1, &1, 2, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + Balances::extend_lock(ID_1, &1, 8, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 3, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + }); + } + + #[test] + fn lock_reasons_should_work() { + <$ext_builder>::default() + .existential_deposit(1) + .monied(true) + .build() + .execute_with(|| { + pallet_transaction_payment::NextFeeMultiplier::put(Fixed64::from_natural(1)); + Balances::set_lock(ID_1, &1, 10, WithdrawReason::Reserve.into()); + assert_noop!( + >::transfer(&1, &2, 1, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + assert_noop!( + >::reserve(&1, 1), + Error::<$test, _>::LiquidityRestrictions + ); + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(1), + &1, + CALL, + info_from_weight(1), + 1, + ).is_err()); + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(0), + &1, + CALL, + info_from_weight(1), + 1, + ).is_ok()); + + Balances::set_lock(ID_1, &1, 10, WithdrawReason::TransactionPayment.into()); + assert_ok!(>::transfer(&1, &2, 1, AllowDeath)); + assert_ok!(>::reserve(&1, 1)); + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(1), + &1, + CALL, + info_from_weight(1), + 1, + ).is_err()); + assert!( as SignedExtension>::pre_dispatch( + ChargeTransactionPayment::from(0), + &1, + CALL, + info_from_weight(1), + 1, + ).is_err()); + }); + } + + #[test] + fn lock_block_number_extension_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, 10, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + System::set_block_number(2); + Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::all()); + assert_noop!( + >::transfer(&1, &2, 3, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + }); + } + + #[test] + fn lock_reasons_extension_should_work() { + <$ext_builder>::default().existential_deposit(1).monied(true).build().execute_with(|| { + Balances::set_lock(ID_1, &1, 10, WithdrawReason::Transfer.into()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + Balances::extend_lock(ID_1, &1, 10, WithdrawReasons::none()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + Balances::extend_lock(ID_1, &1, 10, WithdrawReason::Reserve.into()); + assert_noop!( + >::transfer(&1, &2, 6, AllowDeath), + Error::<$test, _>::LiquidityRestrictions + ); + }); + } + + #[test] + fn default_indexing_on_new_accounts_should_not_work2() { + <$ext_builder>::default() + .existential_deposit(10) + .monied(true) + .build() + .execute_with(|| { + assert_eq!(Balances::is_dead_account(&5), true); + // account 5 should not exist + // ext_deposit is 10, value is 9, not satisfies for ext_deposit + assert_noop!( + Balances::transfer(Some(1).into(), 5, 9), + Error::<$test, _>::ExistentialDeposit, + ); + assert_eq!(Balances::is_dead_account(&5), true); // account 5 should not exist + assert_eq!(Balances::free_balance(1), 100); + }); + } + + #[test] + fn reserved_balance_should_prevent_reclaim_count() { + <$ext_builder>::default() + .existential_deposit(256 * 1) + .monied(true) + .build() + .execute_with(|| { + System::inc_account_nonce(&2); + assert_eq!(Balances::is_dead_account(&2), false); + assert_eq!(Balances::is_dead_account(&5), true); + assert_eq!(Balances::total_balance(&2), 256 * 20); + + assert_ok!(Balances::reserve(&2, 256 * 19 + 1)); // account 2 becomes mostly reserved + assert_eq!(Balances::free_balance(2), 255); // "free" account deleted." + assert_eq!(Balances::total_balance(&2), 256 * 20); // reserve still exists. + assert_eq!(Balances::is_dead_account(&2), false); + assert_eq!(System::account_nonce(&2), 1); + + // account 4 tries to take index 1 for account 5. + assert_ok!(Balances::transfer(Some(4).into(), 5, 256 * 1 + 0x69)); + assert_eq!(Balances::total_balance(&5), 256 * 1 + 0x69); + assert_eq!(Balances::is_dead_account(&5), false); + + assert!(Balances::slash(&2, 256 * 19 + 2).1.is_zero()); // account 2 gets slashed + // "reserve" account reduced to 255 (below ED) so account deleted + assert_eq!(Balances::total_balance(&2), 0); + assert_eq!(System::account_nonce(&2), 0); // nonce zero + assert_eq!(Balances::is_dead_account(&2), true); + + // account 4 tries to take index 1 again for account 6. + assert_ok!(Balances::transfer(Some(4).into(), 6, 256 * 1 + 0x69)); + assert_eq!(Balances::total_balance(&6), 256 * 1 + 0x69); + assert_eq!(Balances::is_dead_account(&6), false); + }); + } + + #[test] + fn reward_should_work() { + <$ext_builder>::default().monied(true).build().execute_with(|| { + assert_eq!(Balances::total_balance(&1), 10); + assert_ok!(Balances::deposit_into_existing(&1, 10).map(drop)); + assert_eq!(Balances::total_balance(&1), 20); + assert_eq!(>::get(), 120); + }); + } + + #[test] + fn dust_account_removal_should_work() { + <$ext_builder>::default() + .existential_deposit(100) + .monied(true) + .build() + .execute_with(|| { + System::inc_account_nonce(&2); + assert_eq!(System::account_nonce(&2), 1); + assert_eq!(Balances::total_balance(&2), 2000); + // index 1 (account 2) becomes zombie + assert_ok!(Balances::transfer(Some(2).into(), 5, 1901)); + assert_eq!(Balances::total_balance(&2), 0); + assert_eq!(Balances::total_balance(&5), 1901); + assert_eq!(System::account_nonce(&2), 0); + }); + } + + #[test] + fn balance_works() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 42); + assert_eq!(Balances::free_balance(1), 42); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::free_balance(2), 0); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::total_balance(&2), 0); + }); + } + + #[test] + fn balance_transfer_works() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::transfer(Some(1).into(), 2, 69)); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::total_balance(&2), 69); + }); + } + + #[test] + fn force_transfer_works() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_noop!( + Balances::force_transfer(Some(2).into(), 1, 2, 69), + BadOrigin, + ); + assert_ok!(Balances::force_transfer(RawOrigin::Root.into(), 1, 2, 69)); + assert_eq!(Balances::total_balance(&1), 42); + assert_eq!(Balances::total_balance(&2), 69); + }); + } + + #[test] + fn reserving_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(1), 111); + assert_eq!(Balances::reserved_balance(1), 0); + + assert_ok!(Balances::reserve(&1, 69)); + + assert_eq!(Balances::total_balance(&1), 111); + assert_eq!(Balances::free_balance(1), 42); + assert_eq!(Balances::reserved_balance(1), 69); + }); + } + + #[test] + fn balance_transfer_when_reserved_should_not_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert_noop!( + Balances::transfer(Some(1).into(), 2, 69), + Error::<$test, _>::InsufficientBalance, + ); + }); + } + + #[test] + fn deducting_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert_eq!(Balances::free_balance(1), 42); + }); + } + + #[test] + fn refunding_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 42); + Balances::mutate_account(&1, |a| a.reserved = 69); + Balances::unreserve(&1, 69); + assert_eq!(Balances::free_balance(1), 111); + assert_eq!(Balances::reserved_balance(1), 0); + }); + } + + #[test] + fn slashing_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 69)); + assert!(Balances::slash(&1, 69).1.is_zero()); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 42); + assert_eq!(>::get(), 42); + }); + } + + #[test] + fn slashing_incomplete_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 42); + assert_ok!(Balances::reserve(&1, 21)); + assert_eq!(Balances::slash(&1, 69).1, 27); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(>::get(), 0); + }); + } + + #[test] + fn unreserving_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + Balances::unreserve(&1, 42); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 42); + }); + } + + #[test] + fn slashing_reserved_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + assert_eq!(Balances::slash_reserved(&1, 42).1, 0); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(>::get(), 69); + }); + } + + #[test] + fn slashing_incomplete_reserved_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 42)); + assert_eq!(Balances::slash_reserved(&1, 69).1, 27); + assert_eq!(Balances::free_balance(1), 69); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(>::get(), 69); + }); + } + + #[test] + fn repatriating_reserved_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 1); + assert_ok!(Balances::reserve(&1, 110)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 41, Status::Free), 0); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::free_balance(2), 42); + }); + } + + #[test] + fn transferring_reserved_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 1); + assert_ok!(Balances::reserve(&1, 110)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 41, Status::Reserved), 0); + assert_eq!(Balances::reserved_balance(1), 69); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(2), 41); + assert_eq!(Balances::free_balance(2), 1); + }); + } + + #[test] + fn transferring_reserved_balance_to_nonexistent_should_fail() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 111); + assert_ok!(Balances::reserve(&1, 111)); + assert_noop!(Balances::repatriate_reserved(&1, &2, 42, Status::Free), Error::<$test, _>::DeadAccount); + }); + } + + #[test] + fn transferring_incomplete_reserved_balance_should_work() { + <$ext_builder>::default().build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 1); + assert_ok!(Balances::reserve(&1, 41)); + assert_ok!(Balances::repatriate_reserved(&1, &2, 69, Status::Free), 28); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::free_balance(1), 69); + assert_eq!(Balances::reserved_balance(2), 0); + assert_eq!(Balances::free_balance(2), 42); + }); + } + + #[test] + fn transferring_too_high_value_should_not_panic() { + <$ext_builder>::default().build().execute_with(|| { + Balances::make_free_balance_be(&1, u64::max_value()); + Balances::make_free_balance_be(&2, 1); + + assert_err!( + Balances::transfer(Some(1).into(), 2, u64::max_value()), + Error::<$test, _>::Overflow, + ); + + assert_eq!(Balances::free_balance(1), u64::max_value()); + assert_eq!(Balances::free_balance(2), 1); + }); + } + + #[test] + fn account_create_on_free_too_low_with_other() { + <$ext_builder>::default().existential_deposit(100).build().execute_with(|| { + 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() { + <$ext_builder>::default().existential_deposit(100).build().execute_with(|| { + // 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() { + <$ext_builder>::default().existential_deposit(100).build().execute_with(|| { + assert_eq!(>::get(), 0); + + // Setup two accounts with free balance above the existential threshold. + let _ = Balances::deposit_creating(&1, 110); + let _ = Balances::deposit_creating(&2, 110); + + assert_eq!(Balances::free_balance(1), 110); + assert_eq!(Balances::free_balance(2), 110); + assert_eq!(>::get(), 220); + + // Transfer funds from account 1 of such amount that after this transfer + // the balance of account 1 will be below the existential threshold. + // This should lead to the removal of all balance of this account. + assert_ok!(Balances::transfer(Some(1).into(), 2, 20)); + + // Verify free balance removal of account 1. + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::free_balance(2), 130); + + // Verify that TotalIssuance tracks balance removal when free balance is too low. + assert_eq!(>::get(), 130); + }); + } + + #[test] + fn burn_must_work() { + <$ext_builder>::default().monied(true).build().execute_with(|| { + let init_total_issuance = Balances::total_issuance(); + let imbalance = Balances::burn(10); + assert_eq!(Balances::total_issuance(), init_total_issuance - 10); + drop(imbalance); + assert_eq!(Balances::total_issuance(), init_total_issuance); + }); + } + + #[test] + fn transfer_keep_alive_works() { + <$ext_builder>::default().existential_deposit(1).build().execute_with(|| { + let _ = Balances::deposit_creating(&1, 100); + assert_noop!( + Balances::transfer_keep_alive(Some(1).into(), 2, 100), + Error::<$test, _>::KeepAlive + ); + assert_eq!(Balances::is_dead_account(&1), false); + assert_eq!(Balances::total_balance(&1), 100); + assert_eq!(Balances::total_balance(&2), 0); + }); + } + + #[test] + #[should_panic = "the balance of any account should always be more than existential deposit."] + fn cannot_set_genesis_value_below_ed() { + ($existential_deposit).with(|v| *v.borrow_mut() = 11); + let mut t = frame_system::GenesisConfig::default().build_storage::<$test>().unwrap(); + let _ = GenesisConfig::<$test> { + balances: vec![(1, 10)], + }.assimilate_storage(&mut t).unwrap(); + } + + #[test] + fn dust_moves_between_free_and_reserved() { + <$ext_builder>::default() + .existential_deposit(100) + .build() + .execute_with(|| { + // Set balance to free and reserved at the existential deposit + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 100, 0)); + // Check balance + assert_eq!(Balances::free_balance(1), 100); + assert_eq!(Balances::reserved_balance(1), 0); + + // Reserve some free balance + assert_ok!(Balances::reserve(&1, 50)); + // Check balance, the account should be ok. + assert_eq!(Balances::free_balance(1), 50); + assert_eq!(Balances::reserved_balance(1), 50); + + // Reserve the rest of the free balance + assert_ok!(Balances::reserve(&1, 50)); + // Check balance, the account should be ok. + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 100); + + // Unreserve everything + Balances::unreserve(&1, 100); + // Check balance, all 100 should move to free_balance + assert_eq!(Balances::free_balance(1), 100); + assert_eq!(Balances::reserved_balance(1), 0); + }); + } + + #[test] + fn account_deleted_when_just_dust() { + <$ext_builder>::default() + .existential_deposit(100) + .build() + .execute_with(|| { + // Set balance to free and reserved at the existential deposit + assert_ok!(Balances::set_balance(RawOrigin::Root.into(), 1, 50, 50)); + // Check balance + assert_eq!(Balances::free_balance(1), 50); + assert_eq!(Balances::reserved_balance(1), 50); + + // Reserve some free balance + let _ = Balances::slash(&1, 1); + // The account should be dead. + assert!(Balances::is_dead_account(&1)); + assert_eq!(Balances::free_balance(1), 0); + assert_eq!(Balances::reserved_balance(1), 0); + }); + } + } } diff --git a/substrate/frame/balances/src/mock.rs b/substrate/frame/balances/src/tests_composite.rs similarity index 79% rename from substrate/frame/balances/src/mock.rs rename to substrate/frame/balances/src/tests_composite.rs index 8a651a0ff7..c566c9a9d0 100644 --- a/substrate/frame/balances/src/mock.rs +++ b/substrate/frame/balances/src/tests_composite.rs @@ -23,7 +23,7 @@ use frame_support::{impl_outer_origin, parameter_types}; use frame_support::traits::Get; use frame_support::weights::{Weight, DispatchInfo}; use std::cell::RefCell; -use crate::{GenesisConfig, Module, Trait}; +use crate::{GenesisConfig, Module, Trait, decl_tests}; use frame_system as system; impl_outer_origin!{ @@ -31,8 +31,7 @@ impl_outer_origin!{ } thread_local! { - pub(crate) static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); - static CREATION_FEE: RefCell = RefCell::new(0); + static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); } pub struct ExistentialDeposit; @@ -40,11 +39,6 @@ impl Get for ExistentialDeposit { fn get() -> u64 { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow()) } } -pub struct CreationFee; -impl Get for CreationFee { - fn get() -> u64 { CREATION_FEE.with(|v| *v.borrow()) } -} - // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, PartialEq, Eq, Debug)] pub struct Test; @@ -71,6 +65,9 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = super::AccountData; + type OnNewAccount = (); + type OnReapAccount = Module; } parameter_types! { pub const TransactionBaseFee: u64 = 0; @@ -86,25 +83,20 @@ impl pallet_transaction_payment::Trait for Test { } impl Trait for Test { type Balance = u64; - type OnReapAccount = System; - type OnNewAccount = (); - type Event = (); type DustRemoval = (); - type TransferPayment = (); + type Event = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = system::Module; } pub struct ExtBuilder { existential_deposit: u64, - creation_fee: u64, monied: bool, } impl Default for ExtBuilder { fn default() -> Self { Self { existential_deposit: 1, - creation_fee: 0, monied: false, } } @@ -114,17 +106,12 @@ impl ExtBuilder { self.existential_deposit = existential_deposit; self } - pub fn creation_fee(mut self, creation_fee: u64) -> Self { - self.creation_fee = creation_fee; - self - } pub fn monied(mut self, monied: bool) -> Self { self.monied = monied; self } pub fn set_associated_consts(&self) { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); - CREATION_FEE.with(|v| *v.borrow_mut() = self.creation_fee); } pub fn build(self) -> sp_io::TestExternalities { self.set_associated_consts(); @@ -146,12 +133,4 @@ impl ExtBuilder { } } -pub type System = frame_system::Module; -pub type Balances = Module; - -pub const CALL: &::Call = &(); - -/// create a transaction info struct from weight. Handy to avoid building the whole struct. -pub fn info_from_weight(w: Weight) -> DispatchInfo { - DispatchInfo { weight: w, pays_fee: true, ..Default::default() } -} +decl_tests!{ Test, ExtBuilder, EXISTENTIAL_DEPOSIT } diff --git a/substrate/frame/balances/src/tests_local.rs b/substrate/frame/balances/src/tests_local.rs new file mode 100644 index 0000000000..a63046e901 --- /dev/null +++ b/substrate/frame/balances/src/tests_local.rs @@ -0,0 +1,144 @@ +// Copyright 2018-2020 Parity Technologies (UK) Ltd. +// This file is part of Substrate. + +// Substrate is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// Substrate is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with Substrate. If not, see . + +//! Test utilities + +use sp_runtime::{Perbill, traits::{ConvertInto, IdentityLookup}, testing::Header}; +use sp_core::H256; +use sp_io; +use frame_support::{impl_outer_origin, parameter_types}; +use frame_support::traits::{Get, StorageMapShim}; +use frame_support::weights::{Weight, DispatchInfo}; +use std::cell::RefCell; +use crate::{GenesisConfig, Module, Trait, decl_tests}; + +use frame_system as system; +impl_outer_origin!{ + pub enum Origin for Test {} +} + +thread_local! { + static EXISTENTIAL_DEPOSIT: RefCell = RefCell::new(0); +} + +pub struct ExistentialDeposit; +impl Get for ExistentialDeposit { + fn get() -> u64 { EXISTENTIAL_DEPOSIT.with(|v| *v.borrow()) } +} + +// Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct Test; +parameter_types! { + pub const BlockHashCount: u64 = 250; + pub const MaximumBlockWeight: Weight = 1024; + pub const MaximumBlockLength: u32 = 2 * 1024; + pub const AvailableBlockRatio: Perbill = Perbill::one(); +} +impl frame_system::Trait for Test { + type Origin = Origin; + type Index = u64; + type BlockNumber = u64; + type Call = (); + type Hash = H256; + type Hashing = ::sp_runtime::traits::BlakeTwo256; + type AccountId = u64; + type Lookup = IdentityLookup; + type Header = Header; + type Event = (); + type BlockHashCount = BlockHashCount; + type MaximumBlockWeight = MaximumBlockWeight; + type MaximumBlockLength = MaximumBlockLength; + type AvailableBlockRatio = AvailableBlockRatio; + type Version = (); + type ModuleToIndex = (); + type AccountData = super::AccountData; + type OnNewAccount = (); + type OnReapAccount = Module; +} +parameter_types! { + pub const TransactionBaseFee: u64 = 0; + pub const TransactionByteFee: u64 = 1; +} +impl pallet_transaction_payment::Trait for Test { + type Currency = Module; + type OnTransactionPayment = (); + type TransactionBaseFee = TransactionBaseFee; + type TransactionByteFee = TransactionByteFee; + type WeightToFee = ConvertInto; + type FeeMultiplierUpdate = (); +} +impl Trait for Test { + type Balance = u64; + type DustRemoval = (); + type Event = (); + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = StorageMapShim< + super::Account, + system::CallOnCreatedAccount, + system::CallKillAccount, + u64, super::AccountData + >; +} + +pub struct ExtBuilder { + existential_deposit: u64, + monied: bool, +} +impl Default for ExtBuilder { + fn default() -> Self { + Self { + existential_deposit: 1, + monied: false, + } + } +} +impl ExtBuilder { + pub fn existential_deposit(mut self, existential_deposit: u64) -> Self { + self.existential_deposit = existential_deposit; + self + } + pub fn monied(mut self, monied: bool) -> Self { + self.monied = monied; + if self.existential_deposit == 0 { + self.existential_deposit = 1; + } + self + } + pub fn set_associated_consts(&self) { + EXISTENTIAL_DEPOSIT.with(|v| *v.borrow_mut() = self.existential_deposit); + } + pub fn build(self) -> sp_io::TestExternalities { + self.set_associated_consts(); + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + GenesisConfig:: { + balances: if self.monied { + vec![ + (1, 10 * self.existential_deposit), + (2, 20 * self.existential_deposit), + (3, 30 * self.existential_deposit), + (4, 40 * self.existential_deposit), + (12, 10 * self.existential_deposit) + ] + } else { + vec![] + }, + }.assimilate_storage(&mut t).unwrap(); + t.into() + } +} + +decl_tests!{ Test, ExtBuilder, EXISTENTIAL_DEPOSIT } diff --git a/substrate/frame/collective/src/lib.rs b/substrate/frame/collective/src/lib.rs index ffc62d976a..e9e6c75b83 100644 --- a/substrate/frame/collective/src/lib.rs +++ b/substrate/frame/collective/src/lib.rs @@ -433,6 +433,9 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl Trait for Test { type Origin = Origin; @@ -454,7 +457,7 @@ mod tests { NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Module, Call, Event}, + System: system::{Module, Call, Event}, Collective: collective::::{Module, Call, Event, Origin, Config}, DefaultCollective: collective::{Module, Call, Event, Origin, Config}, } diff --git a/substrate/frame/contracts/src/account_db.rs b/substrate/frame/contracts/src/account_db.rs index efef02222d..5204f1003a 100644 --- a/substrate/frame/contracts/src/account_db.rs +++ b/substrate/frame/contracts/src/account_db.rs @@ -26,7 +26,7 @@ use sp_std::collections::btree_map::{BTreeMap, Entry}; use sp_std::prelude::*; use sp_io::hashing::blake2_256; use sp_runtime::traits::{Bounded, Zero}; -use frame_support::traits::{Currency, Get, Imbalance, SignedImbalance, UpdateBalanceOutcome}; +use frame_support::traits::{Currency, Get, Imbalance, SignedImbalance}; use frame_support::{storage::child, StorageMap}; use frame_system; @@ -146,9 +146,11 @@ impl AccountDb for DirectAccountDb { let mut total_imbalance = SignedImbalance::zero(); for (address, changed) in s.into_iter() { if let Some(balance) = changed.balance() { - let (imbalance, outcome) = T::Currency::make_free_balance_be(&address, balance); + let existed = !T::Currency::total_balance(&address).is_zero(); + let imbalance = T::Currency::make_free_balance_be(&address, balance); + let exists = !T::Currency::total_balance(&address).is_zero(); total_imbalance = total_imbalance.merge(imbalance); - if let UpdateBalanceOutcome::AccountKilled = outcome { + if existed && !exists { // Account killed. This will ultimately lead to calling `OnReapAccount` callback // which will make removal of CodeHashOf and AccountStorage for this account. // In order to avoid writing over the deleted properties we `continue` here. diff --git a/substrate/frame/contracts/src/exec.rs b/substrate/frame/contracts/src/exec.rs index e84fded920..ec9fad93b4 100644 --- a/substrate/frame/contracts/src/exec.rs +++ b/substrate/frame/contracts/src/exec.rs @@ -554,7 +554,6 @@ where #[derive(Copy, Clone)] pub enum TransferFeeKind { ContractInstantiate, - AccountCreate, Transfer, } @@ -572,7 +571,6 @@ impl Token for TransferFeeToken> { fn calculate_amount(&self, metadata: &Config) -> Gas { let balance_fee = match self.kind { TransferFeeKind::ContractInstantiate => metadata.contract_account_instantiate_fee, - TransferFeeKind::AccountCreate => metadata.account_create_fee, TransferFeeKind::Transfer => return metadata.schedule.transfer_cost, }; approx_gas_for_balance(self.gas_price, balance_fee) @@ -612,28 +610,14 @@ fn transfer<'a, T: Trait, V: Vm, L: Loader>( use self::TransferCause::*; use self::TransferFeeKind::*; - let to_balance = ctx.overlay.get_balance(dest); - - // `would_create` indicates whether the account will be created if this transfer gets executed. - // This flag is orthogonal to `cause. - // For example, we can instantiate a contract at the address which already has some funds. In this - // `would_create` will be `false`. Another example would be when this function is called from `call`, - // and account with the address `dest` doesn't exist yet `would_create` will be `true`. - let would_create = to_balance.is_zero(); - let token = { let kind: TransferFeeKind = match cause { // If this function is called from `Instantiate` routine, then we always // charge contract account creation fee. Instantiate => ContractInstantiate, - // Otherwise the fee depends on whether we create a new account or transfer - // to an existing one. - Call => if would_create { - TransferFeeKind::AccountCreate - } else { - TransferFeeKind::Transfer - }, + // Otherwise the fee is to transfer to an account. + Call => TransferFeeKind::Transfer, }; TransferFeeToken { kind, @@ -651,7 +635,8 @@ fn transfer<'a, T: Trait, V: Vm, L: Loader>( Some(b) => b, None => Err("balance too low to send value")?, }; - if would_create && value < ctx.config.existential_deposit { + let to_balance = ctx.overlay.get_balance(dest); + if to_balance.is_zero() && value < ctx.config.existential_deposit { Err("value too low to create account")? } T::Currency::ensure_can_withdraw( @@ -1105,7 +1090,7 @@ mod tests { toks, ExecFeeToken::Call, TransferFeeToken { - kind: TransferFeeKind::AccountCreate, + kind: TransferFeeKind::Transfer, gas_price: 1u64 }, ); diff --git a/substrate/frame/contracts/src/lib.rs b/substrate/frame/contracts/src/lib.rs index bd1e91f1a9..19e070bd03 100644 --- a/substrate/frame/contracts/src/lib.rs +++ b/substrate/frame/contracts/src/lib.rs @@ -401,9 +401,6 @@ pub trait Trait: frame_system::Trait { /// to removal of a contract. type SurchargeReward: Get>; - /// The fee required to create an account. - type CreationFee: Get>; - /// The fee to be paid for making a transaction; the base. type TransactionBaseFee: Get>; @@ -517,9 +514,6 @@ decl_module! { /// to removal of a contract. const SurchargeReward: BalanceOf = T::SurchargeReward::get(); - /// The fee required to create an account. - const CreationFee: BalanceOf = T::CreationFee::get(); - /// The fee to be paid for making a transaction; the base. const TransactionBaseFee: BalanceOf = T::TransactionBaseFee::get(); @@ -966,7 +960,6 @@ pub struct Config { pub max_depth: u32, pub max_value_size: u32, pub contract_account_instantiate_fee: BalanceOf, - pub account_create_fee: BalanceOf, } impl Config { @@ -978,7 +971,6 @@ impl Config { max_depth: T::MaxDepth::get(), max_value_size: T::MaxValueSize::get(), contract_account_instantiate_fee: T::ContractFee::get(), - account_create_fee: T::CreationFee::get(), } } } diff --git a/substrate/frame/contracts/src/tests.rs b/substrate/frame/contracts/src/tests.rs index 9ee44a767a..8cb854ac97 100644 --- a/substrate/frame/contracts/src/tests.rs +++ b/substrate/frame/contracts/src/tests.rs @@ -41,7 +41,7 @@ use std::{cell::RefCell, sync::atomic::{AtomicUsize, Ordering}}; use sp_core::storage::well_known_keys; use frame_system::{self as system, EventRecord, Phase}; -mod contract { +mod contracts { // Re-export contents of the root. This basically // needs to give a name for the current crate. // This hack is required for `impl_outer_event!`. @@ -53,7 +53,9 @@ use pallet_balances as balances; impl_outer_event! { pub enum MetaEvent for Test { - balances, contract, + system, + balances, + contracts, } } impl_outer_origin! { @@ -62,7 +64,7 @@ impl_outer_origin! { impl_outer_dispatch! { pub enum Call for Test where origin: Origin { balances::Balances, - contract::Contract, + contracts::Contracts, } } @@ -83,11 +85,6 @@ impl Get for TransferFee { fn get() -> u64 { TRANSFER_FEE.with(|v| *v.borrow()) } } -pub struct CreationFee; -impl Get for CreationFee { - fn get() -> u64 { INSTANTIATION_FEE.with(|v| *v.borrow()) } -} - pub struct BlockGasLimit; impl Get for BlockGasLimit { fn get() -> u64 { BLOCK_GAS_LIMIT.with(|v| *v.borrow()) } @@ -118,16 +115,16 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = (Balances, Contracts); } impl pallet_balances::Trait for Test { type Balance = u64; - type OnReapAccount = (System, Contract); - type OnNewAccount = (); type Event = MetaEvent; type DustRemoval = (); - type TransferPayment = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { pub const MinimumPeriod: u64 = 1; @@ -169,7 +166,6 @@ impl Trait for Test { type RentByteFee = RentByteFee; type RentDepositOffset = RentDepositOffset; type SurchargeReward = SurchargeReward; - type CreationFee = CreationFee; type TransactionBaseFee = TransactionBaseFee; type TransactionByteFee = TransactionByteFee; type ContractFee = ContractFee; @@ -182,7 +178,7 @@ impl Trait for Test { type Balances = pallet_balances::Module; type Timestamp = pallet_timestamp::Module; -type Contract = Module; +type Contracts = Module; type System = frame_system::Module; type Randomness = pallet_randomness_collective_flip::Module; @@ -304,7 +300,7 @@ fn refunds_unused_gas() { ExtBuilder::default().gas_price(2).build().execute_with(|| { Balances::deposit_creating(&ALICE, 100_000_000); - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, Vec::new())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, Vec::new())); // 2 * 135 - gas price multiplied by the call base fee. assert_eq!(Balances::free_balance(ALICE), 100_000_000 - (2 * 135)); @@ -413,10 +409,10 @@ fn instantiate_and_call_and_deposit_event() { ExtBuilder::default().existential_deposit(100).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); // Check at the end to get hash on error easily - let creation = Contract::instantiate( + let creation = Contracts::instantiate( Origin::signed(ALICE), 100, 100_000, @@ -427,34 +423,44 @@ fn instantiate_and_call_and_deposit_event() { assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(BOB)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( - pallet_balances::RawEvent::NewAccount(BOB, 100) + pallet_balances::RawEvent::Endowed(BOB, 100) ), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)), + event: MetaEvent::contracts(RawEvent::Transfer(ALICE, BOB, 100)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::ContractExecution(BOB, vec![1, 2, 3, 4])), + event: MetaEvent::contracts(RawEvent::ContractExecution(BOB, vec![1, 2, 3, 4])), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)), + event: MetaEvent::contracts(RawEvent::Instantiated(ALICE, BOB)), topics: vec![], } ]); @@ -493,24 +499,29 @@ fn dispatch_call() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); // Let's keep this assert even though it's redundant. If you ever need to update the // wasm source this test will fail and will show you the actual hash. assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())), topics: vec![], }, ]); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, 100_000, @@ -518,7 +529,7 @@ fn dispatch_call() { vec![], )); - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), BOB, // newly created account 0, @@ -529,44 +540,59 @@ fn dispatch_call() { assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(BOB)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( - pallet_balances::RawEvent::NewAccount(BOB, 100) + pallet_balances::RawEvent::Endowed(BOB, 100) ), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)), + event: MetaEvent::contracts(RawEvent::Transfer(ALICE, BOB, 100)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)), + event: MetaEvent::contracts(RawEvent::Instantiated(ALICE, BOB)), topics: vec![], }, // Dispatching the call. + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(CHARLIE)), + topics: vec![], + }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( - pallet_balances::RawEvent::NewAccount(CHARLIE, 50) + pallet_balances::RawEvent::Endowed(CHARLIE, 50) ), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( - pallet_balances::RawEvent::Transfer(BOB, CHARLIE, 50, 0) + pallet_balances::RawEvent::Transfer(BOB, CHARLIE, 50) ), topics: vec![], }, @@ -574,7 +600,7 @@ fn dispatch_call() { // Event emited as a result of dispatch. EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Dispatched(BOB, true)), + event: MetaEvent::contracts(RawEvent::Dispatched(BOB, true)), topics: vec![], } ]); @@ -611,24 +637,29 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); // Let's keep this assert even though it's redundant. If you ever need to update the // wasm source this test will fail and will show you the actual hash. assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())), topics: vec![], }, ]); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, 100_000, @@ -639,7 +670,7 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { // Call the newly instantiated contract. The contract is expected to dispatch a call // and then trap. assert_err!( - Contract::call( + Contracts::call( Origin::signed(ALICE), BOB, // newly created account 0, @@ -651,29 +682,39 @@ fn dispatch_call_not_dispatched_after_top_level_transaction_failure() { assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(BOB)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), event: MetaEvent::balances( - pallet_balances::RawEvent::NewAccount(BOB, 100) + pallet_balances::RawEvent::Endowed(BOB, 100) ), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Transfer(ALICE, BOB, 100)), + event: MetaEvent::contracts(RawEvent::Transfer(ALICE, BOB, 100)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Instantiated(ALICE, BOB)), + event: MetaEvent::contracts(RawEvent::Instantiated(ALICE, BOB)), topics: vec![], }, // ABSENCE of events which would be caused by dispatched Balances::transfer call @@ -810,19 +851,24 @@ fn test_set_rent_code_and_hash() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); // If you ever need to update the wasm source this test will fail // and will show you the actual hash. assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(code_hash.into())), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contracts(RawEvent::CodeStored(code_hash.into())), topics: vec![], }, ]); @@ -837,8 +883,8 @@ fn storage_size() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 30_000, 100_000, code_hash.into(), @@ -847,11 +893,11 @@ fn storage_size() { let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); assert_eq!(bob_contract.storage_size, ::StorageSizeOffset::get() + 4); - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::set_storage_4_byte())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::set_storage_4_byte())); let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); assert_eq!(bob_contract.storage_size, ::StorageSizeOffset::get() + 4 + 4); - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::remove_storage_4_byte())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::remove_storage_4_byte())); let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); assert_eq!(bob_contract.storage_size, ::StorageSizeOffset::get() + 4); }); @@ -874,8 +920,8 @@ fn deduct_blocks() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 30_000, 100_000, code_hash.into(), @@ -890,7 +936,7 @@ fn deduct_blocks() { initialize_block(5); // Trigger rent through call - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); // Check result let rent = (8 + 4 - 3) // storage size = size_offset + deploy_set_storage - deposit_offset @@ -905,7 +951,7 @@ fn deduct_blocks() { initialize_block(12); // Trigger rent through call - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); // Check result let rent_2 = (8 + 4 - 2) // storage size = size_offset + deploy_set_storage - deposit_offset @@ -917,7 +963,7 @@ fn deduct_blocks() { assert_eq!(Balances::free_balance(BOB), 30_000 - rent - rent_2); // Second call on same block should have no effect on rent - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap(); assert_eq!(bob_contract.rent_allowance, 1_000 - rent - rent_2); @@ -930,34 +976,34 @@ fn deduct_blocks() { fn call_contract_removals() { removals(|| { // Call on already-removed account might fail, and this is fine. - Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()); + Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()); true }); } #[test] fn inherent_claim_surcharge_contract_removals() { - removals(|| Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok()); + removals(|| Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok()); } #[test] fn signed_claim_surcharge_contract_removals() { - removals(|| Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok()); + removals(|| Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok()); } #[test] fn claim_surcharge_malus() { // Test surcharge malus for inherent - claim_surcharge(4, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true); - claim_surcharge(3, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true); - claim_surcharge(2, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true); - claim_surcharge(1, || Contract::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), false); + claim_surcharge(4, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true); + claim_surcharge(3, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true); + claim_surcharge(2, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), true); + claim_surcharge(1, || Contracts::claim_surcharge(Origin::NONE, BOB, Some(ALICE)).is_ok(), false); // Test surcharge malus for signed - claim_surcharge(4, || Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), true); - claim_surcharge(3, || Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false); - claim_surcharge(2, || Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false); - claim_surcharge(1, || Contract::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false); + claim_surcharge(4, || Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), true); + claim_surcharge(3, || Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false); + claim_surcharge(2, || Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false); + claim_surcharge(1, || Contracts::claim_surcharge(Origin::signed(ALICE), BOB, None).is_ok(), false); } /// Claim surcharge with the given trigger_call at the given blocks. @@ -968,8 +1014,8 @@ fn claim_surcharge(blocks: u64, trigger_call: impl Fn() -> bool, removes: bool) ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, 100_000, code_hash.into(), @@ -1001,8 +1047,8 @@ fn removals(trigger_call: impl Fn() -> bool) { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, 100_000, code_hash.into(), @@ -1037,8 +1083,8 @@ fn removals(trigger_call: impl Fn() -> bool) { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 1_000, 100_000, code_hash.into(), @@ -1072,8 +1118,8 @@ fn removals(trigger_call: impl Fn() -> bool) { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 50+Balances::minimum_balance(), 100_000, code_hash.into(), @@ -1086,7 +1132,7 @@ fn removals(trigger_call: impl Fn() -> bool) { assert_eq!(Balances::free_balance(BOB), 50 + Balances::minimum_balance()); // Transfer funds - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::transfer())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::transfer())); assert_eq!(ContractInfoOf::::get(BOB).unwrap().get_alive().unwrap().rent_allowance, 1_000); assert_eq!(Balances::free_balance(BOB), Balances::minimum_balance()); @@ -1116,8 +1162,8 @@ fn call_removed_contract() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm.clone())); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, 100_000, code_hash.into(), @@ -1125,28 +1171,28 @@ fn call_removed_contract() { )); // Calling contract should succeed. - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); // Advance blocks initialize_block(10); // Calling contract should remove contract and fail. assert_err!( - Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), + Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), "contract has been evicted" ); // Calling a contract that is about to evict shall emit an event. assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Evicted(BOB, true)), + event: MetaEvent::contracts(RawEvent::Evicted(BOB, true)), topics: vec![], }, ]); // Subsequent contract calls should also fail. assert_err!( - Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), + Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), "contract has been evicted" ); }) @@ -1209,8 +1255,8 @@ fn default_rent_allowance_on_instantiate() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 30_000, 100_000, @@ -1226,7 +1272,7 @@ fn default_rent_allowance_on_instantiate() { initialize_block(5); // Trigger rent through call - assert_ok!(Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); + assert_ok!(Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null())); // Check contract is still alive let bob_contract = ContractInfoOf::::get(BOB).unwrap().get_alive(); @@ -1322,32 +1368,37 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, restoration_wasm)); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, set_rent_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, restoration_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, set_rent_wasm)); // If you ever need to update the wasm source this test will fail // and will show you the actual hash. assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(1, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(1)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(restoration_code_hash.into())), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(1, 1_000_000)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::CodeStored(set_rent_code_hash.into())), + event: MetaEvent::contracts(RawEvent::CodeStored(restoration_code_hash.into())), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contracts(RawEvent::CodeStored(set_rent_code_hash.into())), topics: vec![], }, ]); // Create an account with address `BOB` with code `CODE_SET_RENT`. // The input parameter sets the rent allowance to 0. - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 30_000, 100_000, @@ -1361,7 +1412,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: assert_eq!(bob_contract.rent_allowance, 0); if test_different_storage { - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), BOB, 0, 100_000, call::set_storage_4_byte()) @@ -1377,14 +1428,14 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: // Call `BOB`, which makes it pay rent. Since the rent allowance is set to 0 // we expect that it will get removed leaving tombstone. assert_err!( - Contract::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), + Contracts::call(Origin::signed(ALICE), BOB, 0, 100_000, call::null()), "contract has been evicted" ); assert!(ContractInfoOf::::get(BOB).unwrap().get_tombstone().is_some()); assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract( + event: MetaEvent::contracts( RawEvent::Evicted(BOB.clone(), true) ), topics: vec![], @@ -1396,7 +1447,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: /// Note that we can't use `ALICE` for creating `DJANGO` so we create yet another /// account `CHARLIE` and create `DJANGO` with it. Balances::deposit_creating(&CHARLIE, 1_000_000); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(CHARLIE), 30_000, 100_000, @@ -1415,7 +1466,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: // Perform a call to `DJANGO`. This should either perform restoration successfully or // fail depending on the test parameters. - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), DJANGO, 0, @@ -1437,7 +1488,7 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract( + event: MetaEvent::contracts( RawEvent::Restored(DJANGO, BOB, bob_code_hash, 50, false) ), topics: vec![], @@ -1448,32 +1499,42 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Evicted(BOB, true)), + event: MetaEvent::contracts(RawEvent::Evicted(BOB, true)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(CHARLIE, 1_000_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(CHARLIE)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(pallet_balances::RawEvent::NewAccount(DJANGO, 30_000)), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(CHARLIE, 1_000_000)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Transfer(CHARLIE, DJANGO, 30_000)), + event: MetaEvent::system(frame_system::RawEvent::NewAccount(DJANGO)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Instantiated(CHARLIE, DJANGO)), + event: MetaEvent::balances(pallet_balances::RawEvent::Endowed(DJANGO, 30_000)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract(RawEvent::Restored( + event: MetaEvent::contracts(RawEvent::Transfer(CHARLIE, DJANGO, 30_000)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contracts(RawEvent::Instantiated(CHARLIE, DJANGO)), + topics: vec![], + }, + EventRecord { + phase: Phase::ApplyExtrinsic(0), + event: MetaEvent::contracts(RawEvent::Restored( DJANGO, BOB, bob_code_hash, @@ -1500,12 +1561,12 @@ fn restoration(test_different_storage: bool, test_restore_to_with_dirty_storage: assert_eq!(System::events(), vec![ EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::balances(balances::RawEvent::ReapedAccount(DJANGO, 0)), + event: MetaEvent::system(system::RawEvent::ReapedAccount(DJANGO)), topics: vec![], }, EventRecord { phase: Phase::ApplyExtrinsic(0), - event: MetaEvent::contract( + event: MetaEvent::contracts( RawEvent::Restored(DJANGO, BOB, bob_contract.code_hash, 50, true) ), topics: vec![], @@ -1586,8 +1647,8 @@ fn storage_max_value_limit() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 30_000, 100_000, @@ -1600,7 +1661,7 @@ fn storage_max_value_limit() { assert_eq!(bob_contract.rent_allowance, >::max_value()); // Call contract with allowed storage value. - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), BOB, 0, @@ -1610,7 +1671,7 @@ fn storage_max_value_limit() { // Call contract with too large a storage value. assert_err!( - Contract::call( + Contracts::call( Origin::signed(ALICE), BOB, 0, @@ -1950,10 +2011,10 @@ fn deploy_and_call_other_contract() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, callee_wasm)); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, caller_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, callee_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, caller_wasm)); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100_000, 100_000, @@ -1963,7 +2024,7 @@ fn deploy_and_call_other_contract() { // Call BOB contract, which attempts to instantiate and call the callee contract and // makes various assertions on the results from those calls. - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), BOB, 0, @@ -2077,10 +2138,10 @@ fn self_destruct_by_draining_balance() { let (wasm, code_hash) = compile_module::(CODE_SELF_DESTRUCT).unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); // Instantiate the BOB contract. - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100_000, 100_000, @@ -2095,7 +2156,7 @@ fn self_destruct_by_draining_balance() { ); // Call BOB with no input data, forcing it to self-destruct. - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), BOB, 0, @@ -2113,10 +2174,10 @@ fn cannot_self_destruct_while_live() { let (wasm, code_hash) = compile_module::(CODE_SELF_DESTRUCT).unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); // Instantiate the BOB contract. - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100_000, 100_000, @@ -2133,7 +2194,7 @@ fn cannot_self_destruct_while_live() { // Call BOB with input data, forcing it make a recursive call to itself to // self-destruct, resulting in a trap. assert_err!( - Contract::call( + Contracts::call( Origin::signed(ALICE), BOB, 0, @@ -2313,12 +2374,12 @@ fn destroy_contract_and_transfer_funds() { ExtBuilder::default().existential_deposit(50).build().execute_with(|| { // Create Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, callee_wasm)); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, caller_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, callee_wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, caller_wasm)); // This deploys the BOB contract, which in turn deploys the CHARLIE contract during // construction. - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 200_000, 100_000, @@ -2333,7 +2394,7 @@ fn destroy_contract_and_transfer_funds() { ); // Call BOB, which calls CHARLIE, forcing CHARLIE to self-destruct. - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), BOB, 0, @@ -2408,12 +2469,12 @@ fn cannot_self_destruct_in_constructor() { let (wasm, code_hash) = compile_module::(CODE_SELF_DESTRUCTING_CONSTRUCTOR).unwrap(); ExtBuilder::default().existential_deposit(50).build().execute_with(|| { Balances::deposit_creating(&ALICE, 1_000_000); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); // Fail to instantiate the BOB contract since its final balance is below existential // deposit. assert_err!( - Contract::instantiate( + Contracts::instantiate( Origin::signed(ALICE), 100_000, 100_000, @@ -2529,15 +2590,15 @@ fn get_runtime_storage() { 0x14144020u32.to_le_bytes().to_vec().as_ref() ); - assert_ok!(Contract::put_code(Origin::signed(ALICE), 100_000, wasm)); - assert_ok!(Contract::instantiate( + assert_ok!(Contracts::put_code(Origin::signed(ALICE), 100_000, wasm)); + assert_ok!(Contracts::instantiate( Origin::signed(ALICE), 100, 100_000, code_hash.into(), vec![], )); - assert_ok!(Contract::call( + assert_ok!(Contracts::call( Origin::signed(ALICE), BOB, 0, diff --git a/substrate/frame/democracy/src/lib.rs b/substrate/frame/democracy/src/lib.rs index d2033fa8b3..33ddf69427 100644 --- a/substrate/frame/democracy/src/lib.rs +++ b/substrate/frame/democracy/src/lib.rs @@ -30,7 +30,7 @@ use frame_support::{ weights::SimpleDispatchInfo, traits::{ Currency, ReservableCurrency, LockableCurrency, WithdrawReason, LockIdentifier, Get, - OnReapAccount, OnUnbalanced + OnReapAccount, OnUnbalanced, BalanceStatus } }; use frame_system::{self as system, ensure_signed, ensure_root}; @@ -802,7 +802,7 @@ decl_module! { let queue = >::get(); ensure!(!queue.iter().any(|item| &item.1 == &proposal_hash), Error::::Imminent); - let _ = T::Currency::repatriate_reserved(&old, &who, deposit); + let _ = T::Currency::repatriate_reserved(&old, &who, deposit, BalanceStatus::Free); >::remove(&proposal_hash); Self::deposit_event(RawEvent::PreimageReaped(proposal_hash, old, deposit, who)); } @@ -1227,20 +1227,19 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { pub const ExistentialDeposit: u64 = 1; - pub const CreationFee: u64 = 0; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnReapAccount = System; - type OnNewAccount = (); type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { pub const LaunchPeriod: u64 = 2; diff --git a/substrate/frame/elections-phragmen/src/lib.rs b/substrate/frame/elections-phragmen/src/lib.rs index f5ffd87537..f250e77121 100644 --- a/substrate/frame/elections-phragmen/src/lib.rs +++ b/substrate/frame/elections-phragmen/src/lib.rs @@ -91,7 +91,7 @@ use frame_support::{ decl_storage, decl_event, ensure, decl_module, decl_error, weights::SimpleDispatchInfo, traits::{ Currency, Get, LockableCurrency, LockIdentifier, ReservableCurrency, WithdrawReasons, - ChangeMembers, OnUnbalanced, WithdrawReason, Contains + ChangeMembers, OnUnbalanced, WithdrawReason, Contains, BalanceStatus } }; use sp_phragmen::ExtendedBalance; @@ -314,7 +314,7 @@ decl_module! { let valid = Self::is_defunct_voter(&target); if valid { // reporter will get the voting bond of the target - T::Currency::repatriate_reserved(&target, &reporter, T::VotingBond::get())?; + T::Currency::repatriate_reserved(&target, &reporter, T::VotingBond::get(), BalanceStatus::Free)?; // remove the target. They are defunct. Self::do_remove_voter(&target, false); } else { @@ -814,23 +814,22 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { pub const ExistentialDeposit: u64 = 1; - pub const CreationFee: u64 = 0; - } +} impl pallet_balances::Trait for Test { type Balance = u64; - type OnNewAccount = (); - type OnReapAccount = System; type Event = Event; - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; - } + type AccountStore = frame_system::Module; +} parameter_types! { pub const CandidacyBond: u64 = 3; @@ -937,7 +936,7 @@ mod tests { NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Module, Call, Event}, + System: system::{Module, Call, Event}, Balances: pallet_balances::{Module, Call, Event, Config}, Elections: elections::{Module, Call, Event}, } @@ -1427,7 +1426,7 @@ mod tests { assert_ok!(Elections::report_defunct_voter(Origin::signed(5), 3)); assert_eq!( - System::events()[1].event, + System::events()[7].event, Event::elections(RawEvent::VoterReported(3, 5, true)) ); @@ -1456,7 +1455,7 @@ mod tests { assert_ok!(Elections::report_defunct_voter(Origin::signed(5), 4)); assert_eq!( - System::events()[1].event, + System::events()[7].event, Event::elections(RawEvent::VoterReported(4, 5, false)) ); @@ -1867,7 +1866,7 @@ mod tests { assert_eq!(balances(&5), (45, 2)); assert_eq!( - System::events()[0].event, + System::events()[6].event, Event::elections(RawEvent::NewTerm(vec![(4, 40), (5, 50)])), ); }) diff --git a/substrate/frame/elections/src/lib.rs b/substrate/frame/elections/src/lib.rs index eb87057f95..a3fdd74426 100644 --- a/substrate/frame/elections/src/lib.rs +++ b/substrate/frame/elections/src/lib.rs @@ -32,7 +32,7 @@ use frame_support::{ decl_storage, decl_event, ensure, decl_module, decl_error, weights::SimpleDispatchInfo, traits::{ - Currency, ExistenceRequirement, Get, LockableCurrency, LockIdentifier, + Currency, ExistenceRequirement, Get, LockableCurrency, LockIdentifier, BalanceStatus, OnUnbalanced, ReservableCurrency, WithdrawReason, WithdrawReasons, ChangeMembers } }; @@ -501,7 +501,7 @@ decl_module! { if valid { // This only fails if `reporter` doesn't exist, which it clearly must do since its // the origin. Still, it's no more harmful to propagate any error at this point. - T::Currency::repatriate_reserved(&who, &reporter, T::VotingBond::get())?; + T::Currency::repatriate_reserved(&who, &reporter, T::VotingBond::get(), BalanceStatus::Free)?; Self::deposit_event(RawEvent::VoterReaped(who, reporter)); } else { let imbalance = T::Currency::slash_reserved(&reporter, T::VotingBond::get()).0; diff --git a/substrate/frame/elections/src/mock.rs b/substrate/frame/elections/src/mock.rs index 7cef8e3bdb..c5af6ba045 100644 --- a/substrate/frame/elections/src/mock.rs +++ b/substrate/frame/elections/src/mock.rs @@ -39,9 +39,9 @@ parameter_types! { } impl frame_system::Trait for Test { type Origin = Origin; + type Call = (); type Index = u64; type BlockNumber = u64; - type Call = (); type Hash = H256; type Hashing = BlakeTwo256; type AccountId = u64; @@ -54,21 +54,20 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { pub const ExistentialDeposit: u64 = 1; - pub const CreationFee: u64 = 0; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnNewAccount = (); - type OnReapAccount = System; - type Event = Event; - type TransferPayment = (); type DustRemoval = (); + type Event = Event; type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { @@ -151,7 +150,7 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Module, Call, Event}, + System: system::{Module, Call, Event}, Balances: pallet_balances::{Module, Call, Event, Config}, Elections: elections::{Module, Call, Event, Config}, } @@ -266,8 +265,7 @@ pub(crate) fn create_candidate(i: u64, index: u32) { } pub(crate) fn balances(who: &u64) -> (u64, u64) { - let a = Balances::account(who); - (a.free, a.reserved) + (Balances::free_balance(who), Balances::reserved_balance(who)) } pub(crate) fn locks(who: &u64) -> Vec { diff --git a/substrate/frame/example/src/lib.rs b/substrate/frame/example/src/lib.rs index 14f88fc5f9..fb8a162d41 100644 --- a/substrate/frame/example/src/lib.rs +++ b/substrate/frame/example/src/lib.rs @@ -688,20 +688,19 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = pallet_balances::Module; } parameter_types! { pub const ExistentialDeposit: u64 = 1; - pub const CreationFee: u64 = 0; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnReapAccount = System; - type OnNewAccount = (); - type Event = (); - type TransferPayment = (); type DustRemoval = (); + type Event = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = System; } impl Trait for Test { type Event = (); diff --git a/substrate/frame/executive/src/lib.rs b/substrate/frame/executive/src/lib.rs index 72411c904b..2d1b306531 100644 --- a/substrate/frame/executive/src/lib.rs +++ b/substrate/frame/executive/src/lib.rs @@ -416,6 +416,7 @@ mod tests { impl_outer_event!{ pub enum MetaEvent for Runtime { + system, balances, } } @@ -451,20 +452,19 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { pub const ExistentialDeposit: u64 = 1; - pub const CreationFee: u64 = 0; } impl pallet_balances::Trait for Runtime { type Balance = u64; - type OnReapAccount = System; - type OnNewAccount = (); type Event = MetaEvent; type DustRemoval = (); - type TransferPayment = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { @@ -559,7 +559,7 @@ mod tests { header: Header { parent_hash: [69u8; 32].into(), number: 1, - state_root: hex!("a0b84fec49718caf59350dab6ec2993f12db399a7cccdb80f3cf79618ed93bd8").into(), + state_root: hex!("96797237079b6d6ffab7a47f90ee257a439a0e8268bdab3fe2f1e52572b101de").into(), extrinsics_root: hex!("03170a2e7597b7b7e3d84c05391d139a62b157e78786d8c082f29dcf4c111314").into(), digest: Digest { logs: vec![], }, }, diff --git a/substrate/frame/finality-tracker/src/lib.rs b/substrate/frame/finality-tracker/src/lib.rs index c9c9fbb0b5..f7bb7b8566 100644 --- a/substrate/frame/finality-tracker/src/lib.rs +++ b/substrate/frame/finality-tracker/src/lib.rs @@ -261,6 +261,9 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } parameter_types! { pub const WindowSize: u64 = 11; diff --git a/substrate/frame/generic-asset/src/lib.rs b/substrate/frame/generic-asset/src/lib.rs index 28986463e5..59535de447 100644 --- a/substrate/frame/generic-asset/src/lib.rs +++ b/substrate/frame/generic-asset/src/lib.rs @@ -166,7 +166,7 @@ use frame_support::{ decl_event, decl_module, decl_storage, ensure, decl_error, traits::{ Currency, ExistenceRequirement, Imbalance, LockIdentifier, LockableCurrency, ReservableCurrency, - SignedImbalance, UpdateBalanceOutcome, WithdrawReason, WithdrawReasons, TryDrop, + SignedImbalance, WithdrawReason, WithdrawReasons, TryDrop, BalanceStatus, }, Parameter, StorageMap, }; @@ -714,8 +714,8 @@ impl Module { } } - /// Move up to `amount` from reserved balance of account `who` to free balance of account - /// `beneficiary`. + /// Move up to `amount` from reserved balance of account `who` to balance of account + /// `beneficiary`, either free or reserved depending on `status`. /// /// As much funds up to `amount` will be moved as possible. If this is less than `amount`, then /// the `remaining` would be returned, else `Zero::zero()`. @@ -726,13 +726,23 @@ impl Module { who: &T::AccountId, beneficiary: &T::AccountId, amount: T::Balance, + status: BalanceStatus, ) -> T::Balance { let b = Self::reserved_balance(asset_id, who); let slash = sp_std::cmp::min(b, amount); - let original_free_balance = Self::free_balance(asset_id, beneficiary); - let new_free_balance = original_free_balance + slash; - Self::set_free_balance(asset_id, beneficiary, new_free_balance); + match status { + BalanceStatus::Free => { + let original_free_balance = Self::free_balance(asset_id, beneficiary); + let new_free_balance = original_free_balance + slash; + Self::set_free_balance(asset_id, beneficiary, new_free_balance); + } + BalanceStatus::Reserved => { + let original_reserved_balance = Self::reserved_balance(asset_id, beneficiary); + let new_reserved_balance = original_reserved_balance + slash; + Self::set_reserved_balance(asset_id, beneficiary, new_reserved_balance); + } + } let new_reserve_balance = b - slash; Self::set_reserved_balance(asset_id, who, new_reserve_balance); @@ -1080,8 +1090,8 @@ mod imbalances { // its type declaration). // This works as long as `increase_total_issuance_by` doesn't use the Imbalance // types (basically for charging fees). -// This should eventually be refactored so that the three type items that do -// depend on the Imbalance type (TransactionPayment, TransferPayment, DustRemoval) +// This should eventually be refactored so that the two type items that do +// depend on the Imbalance type (TransactionPayment, DustRemoval) // are placed in their own SRML module. struct ElevatedTrait(T); impl Clone for ElevatedTrait { @@ -1106,12 +1116,15 @@ impl frame_system::Trait for ElevatedTrait { type Lookup = T::Lookup; type Header = T::Header; type Event = (); + type BlockHashCount = T::BlockHashCount; type MaximumBlockWeight = T::MaximumBlockWeight; type MaximumBlockLength = T::MaximumBlockLength; type AvailableBlockRatio = T::AvailableBlockRatio; - type BlockHashCount = T::BlockHashCount; type Version = T::Version; type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl Trait for ElevatedTrait { type Balance = T::Balance; @@ -1189,7 +1202,7 @@ where } fn deposit_creating(who: &T::AccountId, value: Self::Balance) -> Self::PositiveImbalance { - let (imbalance, _) = Self::make_free_balance_be(who, Self::free_balance(who) + value); + let imbalance = Self::make_free_balance_be(who, Self::free_balance(who) + value); if let SignedImbalance::Positive(p) = imbalance { p } else { @@ -1201,10 +1214,7 @@ where fn make_free_balance_be( who: &T::AccountId, balance: Self::Balance, - ) -> ( - SignedImbalance, - UpdateBalanceOutcome, - ) { + ) -> SignedImbalance { let original = >::free_balance(&U::asset_id(), who); let imbalance = if original <= balance { SignedImbalance::Positive(PositiveImbalance::new(balance - original)) @@ -1212,7 +1222,7 @@ where SignedImbalance::Negative(NegativeImbalance::new(original - balance)) }; >::set_free_balance(&U::asset_id(), who, balance); - (imbalance, UpdateBalanceOutcome::Updated) + imbalance } fn can_slash(who: &T::AccountId, value: Self::Balance) -> bool { @@ -1288,8 +1298,9 @@ where slashed: &T::AccountId, beneficiary: &T::AccountId, value: Self::Balance, + status: BalanceStatus, ) -> result::Result { - Ok(>::repatriate_reserved(&U::asset_id(), slashed, beneficiary, value)) + Ok(>::repatriate_reserved(&U::asset_id(), slashed, beneficiary, value, status)) } } diff --git a/substrate/frame/generic-asset/src/mock.rs b/substrate/frame/generic-asset/src/mock.rs index 6fdf2d2d88..141f7d0030 100644 --- a/substrate/frame/generic-asset/src/mock.rs +++ b/substrate/frame/generic-asset/src/mock.rs @@ -31,7 +31,7 @@ use frame_support::{parameter_types, impl_outer_event, impl_outer_origin, weight use super::*; impl_outer_origin! { - pub enum Origin for Test where system = frame_system {} + pub enum Origin for Test where system = frame_system {} } // For testing the module, we construct most of a mock runtime. This means @@ -62,6 +62,9 @@ impl frame_system::Trait for Test { type BlockHashCount = BlockHashCount; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl Trait for Test { @@ -77,6 +80,7 @@ mod generic_asset { use frame_system as system; impl_outer_event! { pub enum TestEvent for Test { + system, generic_asset, } } diff --git a/substrate/frame/generic-asset/src/tests.rs b/substrate/frame/generic-asset/src/tests.rs index 7c12c391bf..ad8fc2bf1b 100644 --- a/substrate/frame/generic-asset/src/tests.rs +++ b/substrate/frame/generic-asset/src/tests.rs @@ -556,7 +556,7 @@ fn slash_reserved_should_return_none() { fn repatriate_reserved_return_amount_substracted_by_slash_amount() { ExtBuilder::default().build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 130), 30); + assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 130, BalanceStatus::Free), 30); }); } @@ -571,7 +571,7 @@ fn repatriate_reserved_return_amount_substracted_by_slash_amount() { fn repatriate_reserved_return_none() { ExtBuilder::default().build().execute_with(|| { GenericAsset::set_reserved_balance(&1, &0, 100); - assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 90), 0); + assert_eq!(GenericAsset::repatriate_reserved(&1, &0, &1, 90, BalanceStatus::Free), 0); }); } diff --git a/substrate/frame/grandpa/src/mock.rs b/substrate/frame/grandpa/src/mock.rs index 0015dd1f50..77a19c7626 100644 --- a/substrate/frame/grandpa/src/mock.rs +++ b/substrate/frame/grandpa/src/mock.rs @@ -65,6 +65,9 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } mod grandpa { @@ -73,6 +76,7 @@ mod grandpa { impl_outer_event!{ pub enum TestEvent for Test { + system, grandpa, } } diff --git a/substrate/frame/identity/src/benchmarking.rs b/substrate/frame/identity/src/benchmarking.rs index 86ec6b30c9..11e98101ec 100644 --- a/substrate/frame/identity/src/benchmarking.rs +++ b/substrate/frame/identity/src/benchmarking.rs @@ -148,7 +148,7 @@ impl BenchmarkingSetup, RawOrigin> for Judgement::Reasonable )?; } - + // Create identity info with x additional fields let x = components.iter().find(|&c| c.0 == BenchmarkParameter::X).unwrap().1; // 32 byte data that we reuse below @@ -171,7 +171,7 @@ impl BenchmarkingSetup, RawOrigin> for fn instance(&self, components: &[(BenchmarkParameter, u32)]) -> Result<(crate::Call, RawOrigin), &'static str> - { + { // Generic data to be used. let data = Data::Raw(vec![0; 32]); @@ -212,7 +212,7 @@ impl BenchmarkingSetup, RawOrigin> for fn instance(&self, components: &[(BenchmarkParameter, u32)]) -> Result<(crate::Call, RawOrigin), &'static str> - { + { // The target user let caller = account::("caller", 0); let caller_origin: ::Origin = RawOrigin::Signed(caller.clone()).into(); @@ -262,7 +262,7 @@ impl BenchmarkingSetup, RawOrigin> for fn instance(&self, components: &[(BenchmarkParameter, u32)]) -> Result<(crate::Call, RawOrigin), &'static str> - { + { // The target user let caller = account::("caller", 0); let caller_origin: ::Origin = RawOrigin::Signed(caller.clone()).into(); @@ -296,7 +296,7 @@ impl BenchmarkingSetup, RawOrigin> for fn instance(&self, components: &[(BenchmarkParameter, u32)]) -> Result<(crate::Call, RawOrigin), &'static str> - { + { // The target user let caller = account::("caller", 0); let caller_origin: ::Origin = RawOrigin::Signed(caller.clone()).into(); @@ -330,7 +330,7 @@ impl BenchmarkingSetup, RawOrigin> for fn instance(&self, components: &[(BenchmarkParameter, u32)]) -> Result<(crate::Call, RawOrigin), &'static str> - { + { // The target user let caller = account::("caller", 0); let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -359,7 +359,7 @@ impl BenchmarkingSetup, RawOrigin> for fn instance(&self, components: &[(BenchmarkParameter, u32)]) -> Result<(crate::Call, RawOrigin), &'static str> - { + { // The target user let caller = account::("caller", 0); let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -388,7 +388,7 @@ impl BenchmarkingSetup, RawOrigin> for fn instance(&self, components: &[(BenchmarkParameter, u32)]) -> Result<(crate::Call, RawOrigin), &'static str> - { + { // The target user let caller = account::("caller", 0); let _ = T::Currency::make_free_balance_be(&caller, BalanceOf::::max_value()); @@ -410,7 +410,7 @@ impl BenchmarkingSetup, RawOrigin> for } } -// Benchmark `provide_judgement` extrinsic. +// Benchmark `provide_judgement` extrinsic.g struct ProvideJudgement; impl BenchmarkingSetup, RawOrigin> for ProvideJudgement { @@ -425,7 +425,7 @@ impl BenchmarkingSetup, RawOrigin> for fn instance(&self, components: &[(BenchmarkParameter, u32)]) -> Result<(crate::Call, RawOrigin), &'static str> - { + { // Add r registrars let r = components.iter().find(|&c| c.0 == BenchmarkParameter::R).unwrap().1; benchmarking::add_registrars::(r)?; @@ -477,7 +477,7 @@ impl BenchmarkingSetup, RawOrigin> for fn instance(&self, components: &[(BenchmarkParameter, u32)]) -> Result<(crate::Call, RawOrigin), &'static str> - { + { // The target user let caller = account::("caller", 0); let caller_origin: ::Origin = RawOrigin::Signed(caller.clone()).into(); @@ -550,8 +550,8 @@ impl Benchmarking for Module { sp_io::benchmarking::commit_db(); sp_io::benchmarking::wipe_db(); - // first one is set_identity. - let components = , RawOrigin>>::components(&selected_benchmark); + // first one is set_identity. + let components = , RawOrigin>>::components(&selected_benchmark); // results go here let mut results: Vec = Vec::new(); // Select the component we will be benchmarking. Each component will be benchmarked. diff --git a/substrate/frame/identity/src/lib.rs b/substrate/frame/identity/src/lib.rs index c69239350c..5470e3d4b0 100644 --- a/substrate/frame/identity/src/lib.rs +++ b/substrate/frame/identity/src/lib.rs @@ -73,7 +73,7 @@ use sp_runtime::{DispatchResult, RuntimeDebug}; use sp_runtime::traits::{StaticLookup, EnsureOrigin, Zero, AppendZerosInput}; use frame_support::{ decl_module, decl_event, decl_storage, ensure, decl_error, - traits::{Currency, ReservableCurrency, OnUnbalanced, Get}, + traits::{Currency, ReservableCurrency, OnUnbalanced, Get, BalanceStatus}, weights::SimpleDispatchInfo, }; use frame_system::{self as system, ensure_signed, ensure_root}; @@ -823,7 +823,7 @@ decl_module! { match id.judgements.binary_search_by_key(®_index, |x| x.0) { Ok(position) => { if let Judgement::FeePaid(fee) = id.judgements[position].1 { - let _ = T::Currency::repatriate_reserved(&target, &sender, fee); + let _ = T::Currency::repatriate_reserved(&target, &sender, fee, BalanceStatus::Free); } id.judgements[position] = item } @@ -924,20 +924,19 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { pub const ExistentialDeposit: u64 = 1; - pub const CreationFee: u64 = 0; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnReapAccount = System; - type OnNewAccount = (); type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { pub const BasicDeposit: u64 = 10; diff --git a/substrate/frame/im-online/src/mock.rs b/substrate/frame/im-online/src/mock.rs index 5c428c3858..7ee4c89ab4 100644 --- a/substrate/frame/im-online/src/mock.rs +++ b/substrate/frame/im-online/src/mock.rs @@ -115,6 +115,9 @@ impl frame_system::Trait for Runtime { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } parameter_types! { diff --git a/substrate/frame/indices/Cargo.toml b/substrate/frame/indices/Cargo.toml index 839f668819..eb5298dcfa 100644 --- a/substrate/frame/indices/Cargo.toml +++ b/substrate/frame/indices/Cargo.toml @@ -16,6 +16,9 @@ sp-core = { version = "2.0.0", default-features = false, path = "../../primitive frame-support = { version = "2.0.0", default-features = false, path = "../support" } frame-system = { version = "2.0.0", default-features = false, path = "../system" } +[dev-dependencies] +pallet-balances = { version = "2.0.0", path = "../balances" } + [features] default = ["std"] std = [ diff --git a/substrate/frame/indices/src/lib.rs b/substrate/frame/indices/src/lib.rs index 945095288a..ad1a7f300f 100644 --- a/substrate/frame/indices/src/lib.rs +++ b/substrate/frame/indices/src/lib.rs @@ -19,41 +19,24 @@ #![cfg_attr(not(feature = "std"), no_std)] -use sp_std::{prelude::*, marker::PhantomData, convert::TryInto}; -use codec::{Encode, Codec}; -use frame_support::{Parameter, decl_module, decl_event, decl_storage}; -use sp_runtime::traits::{One, AtLeast32Bit, StaticLookup, Member, LookupError}; -use frame_system::{IsDeadAccount, OnNewAccount}; - +use sp_std::prelude::*; +use codec::Codec; +use sp_runtime::traits::{ + StaticLookup, Member, LookupError, Zero, One, BlakeTwo256, Hash, Saturating, AtLeast32Bit +}; +use frame_support::{Parameter, decl_module, decl_error, decl_event, decl_storage, ensure}; +use frame_support::dispatch::DispatchResult; +use frame_support::traits::{Currency, ReservableCurrency, Get, BalanceStatus::Reserved}; +use frame_support::storage::migration::take_storage_value; +use frame_system::{ensure_signed, ensure_root}; use self::address::Address as RawAddress; mod mock; - pub mod address; mod tests; -/// Number of account IDs stored per enum set. -const ENUM_SET_SIZE: u32 = 64; - pub type Address = RawAddress<::AccountId, ::AccountIndex>; - -/// Turn an Id into an Index, or None for the purpose of getting -/// a hint at a possibly desired index. -pub trait ResolveHint { - /// Turn an Id into an Index, or None for the purpose of getting - /// a hint at a possibly desired index. - fn resolve_hint(who: &AccountId) -> Option; -} - -/// Simple encode-based resolve hint implementation. -pub struct SimpleResolveHint(PhantomData<(AccountId, AccountIndex)>); -impl> - ResolveHint for SimpleResolveHint -{ - fn resolve_hint(who: &AccountId) -> Option { - Some(AccountIndex::from(who.using_encoded(|e| e[0] as u32 + e[1] as u32 * 256))) - } -} +type BalanceOf = <::Currency as Currency<::AccountId>>::Balance; /// The module's config trait. pub trait Trait: frame_system::Trait { @@ -61,19 +44,28 @@ pub trait Trait: frame_system::Trait { /// can hold. type AccountIndex: Parameter + Member + Codec + Default + AtLeast32Bit + Copy; - /// Whether an account is dead or not. - type IsDeadAccount: IsDeadAccount; + /// The currency trait. + type Currency: ReservableCurrency; - /// How to turn an id into an index. - type ResolveHint: ResolveHint; + /// The deposit needed for reserving an index. + type Deposit: Get>; /// The overarching event type. type Event: From> + Into<::Event>; } -decl_module! { - pub struct Module for enum Call where origin: T::Origin, system = frame_system { - fn deposit_event() = default; +decl_storage! { + trait Store for Module as Indices { + /// The lookup from index to account. + pub Accounts build(|config: &GenesisConfig| + config.indices.iter() + .cloned() + .map(|(a, b)| (a, (b, Zero::zero()))) + .collect::>() + ): map hasher(blake2_128_concat) T::AccountIndex => Option<(T::AccountId, BalanceOf)>; + } + add_extra_genesis { + config(indices): Vec<(T::AccountIndex, T::AccountId)>; } } @@ -82,36 +74,146 @@ decl_event!( ::AccountId, ::AccountIndex { - /// A new account index was assigned. - /// - /// This event is not triggered when an existing index is reassigned - /// to another `AccountId`. - NewAccountIndex(AccountId, AccountIndex), + /// A account index was assigned. + IndexAssigned(AccountId, AccountIndex), + /// A account index has been freed up (unassigned). + IndexFreed(AccountIndex), } ); -decl_storage! { - trait Store for Module as Indices { - /// The next free enumeration set. - pub NextEnumSet get(fn next_enum_set) build(|config: &GenesisConfig| { - (config.ids.len() as u32 / ENUM_SET_SIZE).into() - }): T::AccountIndex; - - /// The enumeration sets. - pub EnumSet get(fn enum_set) build(|config: &GenesisConfig| { - (0..((config.ids.len() as u32) + ENUM_SET_SIZE - 1) / ENUM_SET_SIZE) - .map(|i| ( - i.into(), - config.ids[ - (i * ENUM_SET_SIZE) as usize.. - config.ids.len().min(((i + 1) * ENUM_SET_SIZE) as usize) - ].to_owned(), - )) - .collect::>() - }): map hasher(blake2_256) T::AccountIndex => Vec; +decl_error! { + pub enum Error for Module { + /// The index was not already assigned. + NotAssigned, + /// The index is assigned to another account. + NotOwner, + /// The index was not available. + InUse, + /// The source and destination accounts are identical. + NotTransfer, } - add_extra_genesis { - config(ids): Vec; +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin, system = frame_system { + fn deposit_event() = default; + + fn on_initialize() { + Self::migrations(); + } + + /// Assign an previously unassigned index. + /// + /// Payment: `Deposit` is reserved from the sender account. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// - `index`: the index to be claimed. This must not be in use. + /// + /// Emits `IndexAssigned` if successful. + /// + /// # + /// - `O(1)`. + /// - One storage mutation (codec `O(1)`). + /// - One reserve operation. + /// - One event. + /// # + fn claim(origin, index: T::AccountIndex) { + let who = ensure_signed(origin)?; + + Accounts::::try_mutate(index, |maybe_value| { + ensure!(maybe_value.is_none(), Error::::InUse); + *maybe_value = Some((who.clone(), T::Deposit::get())); + T::Currency::reserve(&who, T::Deposit::get()) + })?; + Self::deposit_event(RawEvent::IndexAssigned(who, index)); + } + + /// Assign an index already owned by the sender to another account. The balance reservation + /// is effectively transfered to the new account. + /// + /// The dispatch origin for this call must be _Signed_. + /// + /// - `index`: the index to be re-assigned. This must be owned by the sender. + /// - `new`: the new owner of the index. This function is a no-op if it is equal to sender. + /// + /// Emits `IndexAssigned` if successful. + /// + /// # + /// - `O(1)`. + /// - One storage mutation (codec `O(1)`). + /// - One transfer operation. + /// - One event. + /// # + fn transfer(origin, new: T::AccountId, index: T::AccountIndex) { + let who = ensure_signed(origin)?; + ensure!(who != new, Error::::NotTransfer); + + Accounts::::try_mutate(index, |maybe_value| -> DispatchResult { + let (account, amount) = maybe_value.take().ok_or(Error::::NotAssigned)?; + ensure!(&account == &who, Error::::NotOwner); + let lost = T::Currency::repatriate_reserved(&who, &new, amount, Reserved)?; + *maybe_value = Some((new.clone(), amount.saturating_sub(lost))); + Ok(()) + })?; + Self::deposit_event(RawEvent::IndexAssigned(new, index)); + } + + /// Free up an index owned by the sender. + /// + /// Payment: Any previous deposit placed for the index is unreserved in the sender account. + /// + /// The dispatch origin for this call must be _Signed_ and the sender must own the index. + /// + /// - `index`: the index to be freed. This must be owned by the sender. + /// + /// Emits `IndexFreed` if successful. + /// + /// # + /// - `O(1)`. + /// - One storage mutation (codec `O(1)`). + /// - One reserve operation. + /// - One event. + /// # + fn free(origin, index: T::AccountIndex) { + let who = ensure_signed(origin)?; + + Accounts::::try_mutate(index, |maybe_value| -> DispatchResult { + let (account, amount) = maybe_value.take().ok_or(Error::::NotAssigned)?; + ensure!(&account == &who, Error::::NotOwner); + T::Currency::unreserve(&who, amount); + Ok(()) + })?; + Self::deposit_event(RawEvent::IndexFreed(index)); + } + + /// Force an index to an account. This doesn't require a deposit. If the index is already + /// held, then any deposit is reimbursed to its current owner. + /// + /// The dispatch origin for this call must be _Root_. + /// + /// - `index`: the index to be (re-)assigned. + /// - `new`: the new owner of the index. This function is a no-op if it is equal to sender. + /// + /// Emits `IndexAssigned` if successful. + /// + /// # + /// - `O(1)`. + /// - One storage mutation (codec `O(1)`). + /// - Up to one reserve operation. + /// - One event. + /// # + fn force_transfer(origin, new: T::AccountId, index: T::AccountIndex) { + ensure_root(origin)?; + + Accounts::::mutate(index, |maybe_value| { + if let Some((account, amount)) = maybe_value.take() { + T::Currency::unreserve(&account, amount); + } + *maybe_value = Some((new.clone(), Zero::zero())); + }); + Self::deposit_event(RawEvent::IndexAssigned(new, index)); + } } } @@ -120,22 +222,7 @@ impl Module { /// Lookup an T::AccountIndex to get an Id, if there's one there. pub fn lookup_index(index: T::AccountIndex) -> Option { - let enum_set_size = Self::enum_set_size(); - let set = Self::enum_set(index / enum_set_size); - let i: usize = (index % enum_set_size).try_into().ok()?; - set.get(i).cloned() - } - - /// `true` if the account `index` is ready for reclaim. - pub fn can_reclaim(try_index: T::AccountIndex) -> bool { - let enum_set_size = Self::enum_set_size(); - let try_set = Self::enum_set(try_index / enum_set_size); - let maybe_usize: Result = (try_index % enum_set_size).try_into(); - if let Ok(i) = maybe_usize { - i < try_set.len() && T::IsDeadAccount::is_dead_account(&try_set[i]) - } else { - false - } + Accounts::::get(index).map(|x| x.0) } /// Lookup an address to get an Id, if there's one there. @@ -148,76 +235,28 @@ impl Module { } } - // PUBLIC MUTABLES (DANGEROUS) + /// Do any migrations. + fn migrations() { + if let Some(set_count) = take_storage_value::(b"Indices", b"NextEnumSet", b"") { + // migrations need doing. + let set_size: T::AccountIndex = 64.into(); - fn enum_set_size() -> T::AccountIndex { - ENUM_SET_SIZE.into() - } -} - -impl OnNewAccount for Module { - // Implementation of the config type managing the creation of new accounts. - // See Balances module for a concrete example. - // - // # - // - Independent of the arguments. - // - Given the correct value of `Self::next_enum_set`, it always has a limited - // number of reads and writes and no complex computation. - // - // As for storage, calling this function with _non-dead-indices_ will linearly grow the length of - // of `Self::enum_set`. Appropriate economic incentives should exist to make callers of this - // function provide a `who` argument that reclaims a dead account. - // - // At the time of this writing, only the Balances module calls this function upon creation - // of new accounts. - // # - fn on_new_account(who: &T::AccountId) { - let enum_set_size = Self::enum_set_size(); - let next_set_index = Self::next_enum_set(); - - if let Some(try_index) = T::ResolveHint::resolve_hint(who) { - // then check to see if this account id identifies a dead account index. - let set_index = try_index / enum_set_size; - let mut try_set = Self::enum_set(set_index); - if let Ok(item_index) = (try_index % enum_set_size).try_into() { - if item_index < try_set.len() { - if T::IsDeadAccount::is_dead_account(&try_set[item_index]) { - // yup - this index refers to a dead account. can be reused. - try_set[item_index] = who.clone(); - >::insert(set_index, try_set); - - return + let mut set_index: T::AccountIndex = Zero::zero(); + while set_index < set_count { + let maybe_accounts = take_storage_value::>(b"Indices", b"EnumSet", BlakeTwo256::hash_of(&set_index).as_ref()); + if let Some(accounts) = maybe_accounts { + for (item_index, target) in accounts.into_iter().enumerate() { + if target != T::AccountId::default() && !T::Currency::total_balance(&target).is_zero() { + let index = set_index * set_size + T::AccountIndex::from(item_index as u32); + Accounts::::insert(index, (target, BalanceOf::::zero())); + } } + } else { + break; } + set_index += One::one(); } } - - // insert normally as a back up - let mut set_index = next_set_index; - // defensive only: this loop should never iterate since we keep NextEnumSet up to date - // later. - let mut set = loop { - let set = Self::enum_set(set_index); - if set.len() < ENUM_SET_SIZE as usize { - break set; - } - set_index += One::one(); - }; - - let index = set_index * enum_set_size + T::AccountIndex::from(set.len() as u32); - - // update set. - set.push(who.clone()); - - // keep NextEnumSet up to date - if set.len() == ENUM_SET_SIZE as usize { - >::put(set_index + One::one()); - } - - // write set. - >::insert(set_index, set); - - Self::deposit_event(RawEvent::NewAccountIndex(who.clone(), index)); } } diff --git a/substrate/frame/indices/src/mock.rs b/substrate/frame/indices/src/mock.rs index b8c9b0a0ad..fe01b680bc 100644 --- a/substrate/frame/indices/src/mock.rs +++ b/substrate/frame/indices/src/mock.rs @@ -18,51 +18,29 @@ #![cfg(test)] -use std::{cell::RefCell, collections::HashSet}; use sp_runtime::testing::Header; use sp_runtime::Perbill; use sp_core::H256; -use frame_support::{impl_outer_origin, parameter_types, weights::Weight}; -use crate::{GenesisConfig, Module, Trait, IsDeadAccount, OnNewAccount, ResolveHint}; +use frame_support::{impl_outer_origin, impl_outer_event, parameter_types, weights::Weight}; +use crate::{self as indices, Module, Trait}; +use frame_system as system; +use pallet_balances as balances; impl_outer_origin!{ - pub enum Origin for Runtime where system = frame_system {} + pub enum Origin for Test where system = frame_system {} } - -thread_local! { - static ALIVE: RefCell> = Default::default(); -} - -pub fn make_account(who: u64) { - ALIVE.with(|a| a.borrow_mut().insert(who)); - Indices::on_new_account(&who); -} - -pub fn kill_account(who: u64) { - ALIVE.with(|a| a.borrow_mut().remove(&who)); -} - -pub struct TestIsDeadAccount {} -impl IsDeadAccount for TestIsDeadAccount { - fn is_dead_account(who: &u64) -> bool { - !ALIVE.with(|a| a.borrow_mut().contains(who)) - } -} - -pub struct TestResolveHint; -impl ResolveHint for TestResolveHint { - fn resolve_hint(who: &u64) -> Option { - if *who < 256 { - None - } else { - Some(*who - 256) - } +impl_outer_event!{ + pub enum MetaEvent for Test { + system, + balances, + indices, } } // Workaround for https://github.com/rust-lang/rust/issues/26925 . Remove when sorted. #[derive(Clone, PartialEq, Eq, Debug)] -pub struct Runtime; +pub struct Test; + parameter_types! { pub const BlockHashCount: u64 = 250; pub const MaximumBlockWeight: Weight = 1024; @@ -70,46 +48,59 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); } -impl frame_system::Trait for Runtime { +impl frame_system::Trait for Test { type Origin = Origin; + type Call = (); type Index = u64; type BlockNumber = u64; - type Call = (); type Hash = H256; type Hashing = ::sp_runtime::traits::BlakeTwo256; type AccountId = u64; type Lookup = Indices; type Header = Header; - type Event = (); + type Event = MetaEvent; type BlockHashCount = BlockHashCount; type MaximumBlockWeight = MaximumBlockWeight; type MaximumBlockLength = MaximumBlockLength; type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } -impl Trait for Runtime { +parameter_types! { + pub const ExistentialDeposit: u64 = 1; +} + +impl pallet_balances::Trait for Test { + type Balance = u64; + type DustRemoval = (); + type Event = MetaEvent; + type ExistentialDeposit = ExistentialDeposit; + type AccountStore = System; +} + +parameter_types! { + pub const Deposit: u64 = 1; +} + +impl Trait for Test { type AccountIndex = u64; - type IsDeadAccount = TestIsDeadAccount; - type ResolveHint = TestResolveHint; - type Event = (); + type Currency = Balances; + type Deposit = Deposit; + type Event = MetaEvent; } pub fn new_test_ext() -> sp_io::TestExternalities { - { - ALIVE.with(|a| { - let mut h = a.borrow_mut(); - h.clear(); - for i in 1..5 { h.insert(i); } - }); - } - - let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); - GenesisConfig:: { - ids: vec![1, 2, 3, 4] + let mut t = frame_system::GenesisConfig::default().build_storage::().unwrap(); + pallet_balances::GenesisConfig::{ + balances: vec![(1, 10), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)], }.assimilate_storage(&mut t).unwrap(); t.into() } -pub type Indices = Module; +pub type System = frame_system::Module; +pub type Balances = pallet_balances::Module; +pub type Indices = Module; diff --git a/substrate/frame/indices/src/tests.rs b/substrate/frame/indices/src/tests.rs index 95afcef734..9e434cfbe2 100644 --- a/substrate/frame/indices/src/tests.rs +++ b/substrate/frame/indices/src/tests.rs @@ -19,49 +19,85 @@ #![cfg(test)] use super::*; -use crate::mock::{Indices, new_test_ext, make_account, kill_account, TestIsDeadAccount}; +use super::mock::*; +use frame_support::{assert_ok, assert_noop}; +use pallet_balances::Error as BalancesError; + +#[test] +fn claiming_should_work() { + new_test_ext().execute_with(|| { + assert_noop!(Indices::claim(Some(0).into(), 0), BalancesError::::InsufficientBalance); + assert_ok!(Indices::claim(Some(1).into(), 0)); + assert_noop!(Indices::claim(Some(2).into(), 0), Error::::InUse); + assert_eq!(Balances::reserved_balance(1), 1); + }); +} + +#[test] +fn freeing_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Indices::claim(Some(1).into(), 0)); + assert_ok!(Indices::claim(Some(2).into(), 1)); + assert_noop!(Indices::free(Some(0).into(), 0), Error::::NotOwner); + assert_noop!(Indices::free(Some(1).into(), 1), Error::::NotOwner); + assert_noop!(Indices::free(Some(1).into(), 2), Error::::NotAssigned); + assert_ok!(Indices::free(Some(1).into(), 0)); + assert_eq!(Balances::reserved_balance(1), 0); + assert_noop!(Indices::free(Some(1).into(), 0), Error::::NotAssigned); + }); +} #[test] fn indexing_lookup_should_work() { new_test_ext().execute_with(|| { + assert_ok!(Indices::claim(Some(1).into(), 0)); + assert_ok!(Indices::claim(Some(2).into(), 1)); assert_eq!(Indices::lookup_index(0), Some(1)); assert_eq!(Indices::lookup_index(1), Some(2)); - assert_eq!(Indices::lookup_index(2), Some(3)); - assert_eq!(Indices::lookup_index(3), Some(4)); - assert_eq!(Indices::lookup_index(4), None); + assert_eq!(Indices::lookup_index(2), None); }); } #[test] -fn default_indexing_on_new_accounts_should_work() { +fn reclaim_index_on_accounts_should_work() { new_test_ext().execute_with(|| { - assert_eq!(Indices::lookup_index(4), None); - make_account(5); - assert_eq!(Indices::lookup_index(4), Some(5)); + assert_ok!(Indices::claim(Some(1).into(), 0)); + assert_ok!(Indices::free(Some(1).into(), 0)); + assert_ok!(Indices::claim(Some(2).into(), 0)); + assert_eq!(Indices::lookup_index(0), Some(2)); + assert_eq!(Balances::reserved_balance(2), 1); }); } #[test] -fn reclaim_indexing_on_new_accounts_should_work() { +fn transfer_index_on_accounts_should_work() { new_test_ext().execute_with(|| { - assert_eq!(Indices::lookup_index(1), Some(2)); - assert_eq!(Indices::lookup_index(4), None); - - kill_account(2); // index 1 no longer locked to id 2 - - make_account(1 + 256); // id 257 takes index 1. - assert_eq!(Indices::lookup_index(1), Some(257)); + assert_ok!(Indices::claim(Some(1).into(), 0)); + assert_noop!(Indices::transfer(Some(1).into(), 2, 1), Error::::NotAssigned); + assert_noop!(Indices::transfer(Some(2).into(), 3, 0), Error::::NotOwner); + assert_ok!(Indices::transfer(Some(1).into(), 3, 0)); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::reserved_balance(3), 1); + assert_eq!(Indices::lookup_index(0), Some(3)); }); } #[test] -fn alive_account_should_prevent_reclaim() { +fn force_transfer_index_on_preowned_should_work() { new_test_ext().execute_with(|| { - assert!(!TestIsDeadAccount::is_dead_account(&2)); - assert_eq!(Indices::lookup_index(1), Some(2)); - assert_eq!(Indices::lookup_index(4), None); - - make_account(1 + 256); // id 257 takes index 1. - assert_eq!(Indices::lookup_index(4), Some(257)); + assert_ok!(Indices::claim(Some(1).into(), 0)); + assert_ok!(Indices::force_transfer(Origin::ROOT, 3, 0)); + assert_eq!(Balances::reserved_balance(1), 0); + assert_eq!(Balances::reserved_balance(3), 0); + assert_eq!(Indices::lookup_index(0), Some(3)); + }); +} + +#[test] +fn force_transfer_index_on_free_should_work() { + new_test_ext().execute_with(|| { + assert_ok!(Indices::force_transfer(Origin::ROOT, 3, 0)); + assert_eq!(Balances::reserved_balance(3), 0); + assert_eq!(Indices::lookup_index(0), Some(3)); }); } diff --git a/substrate/frame/membership/src/lib.rs b/substrate/frame/membership/src/lib.rs index 3880b6cbb8..62d1315ee2 100644 --- a/substrate/frame/membership/src/lib.rs +++ b/substrate/frame/membership/src/lib.rs @@ -269,6 +269,9 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } ord_parameter_types! { pub const One: u64 = 1; diff --git a/substrate/frame/nicks/src/lib.rs b/substrate/frame/nicks/src/lib.rs index 814673a6ff..6a86f08093 100644 --- a/substrate/frame/nicks/src/lib.rs +++ b/substrate/frame/nicks/src/lib.rs @@ -285,20 +285,19 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { pub const ExistentialDeposit: u64 = 1; - pub const CreationFee: u64 = 0; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnReapAccount = System; - type OnNewAccount = (); type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { pub const ReservationFee: u64 = 2; diff --git a/substrate/frame/offences/src/mock.rs b/substrate/frame/offences/src/mock.rs index f344206f5c..f2e19b63f5 100644 --- a/substrate/frame/offences/src/mock.rs +++ b/substrate/frame/offences/src/mock.rs @@ -89,6 +89,9 @@ impl frame_system::Trait for Runtime { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl Trait for Runtime { @@ -103,6 +106,7 @@ mod offences { impl_outer_event! { pub enum TestEvent for Runtime { + system, offences, } } diff --git a/substrate/frame/randomness-collective-flip/src/lib.rs b/substrate/frame/randomness-collective-flip/src/lib.rs index 336b038c18..64d8f40099 100644 --- a/substrate/frame/randomness-collective-flip/src/lib.rs +++ b/substrate/frame/randomness-collective-flip/src/lib.rs @@ -191,6 +191,9 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } type System = frame_system::Module; diff --git a/substrate/frame/recovery/src/lib.rs b/substrate/frame/recovery/src/lib.rs index 668608c892..d293e1da9a 100644 --- a/substrate/frame/recovery/src/lib.rs +++ b/substrate/frame/recovery/src/lib.rs @@ -164,7 +164,7 @@ use frame_support::{ GetDispatchInfo, PaysFee, DispatchClass, ClassifyDispatch, Weight, WeighData, SimpleDispatchInfo, }, - traits::{Currency, ReservableCurrency, Get, OnReapAccount}, + traits::{Currency, ReservableCurrency, Get, OnReapAccount, BalanceStatus}, }; use frame_system::{self as system, ensure_signed, ensure_root}; @@ -587,7 +587,7 @@ decl_module! { let active_recovery = >::take(&who, &rescuer).ok_or(Error::::NotStarted)?; // Move the reserved funds from the rescuer to the rescued account. // Acts like a slashing mechanism for those who try to maliciously recover accounts. - let _ = T::Currency::repatriate_reserved(&rescuer, &who, active_recovery.deposit); + let _ = T::Currency::repatriate_reserved(&rescuer, &who, active_recovery.deposit, BalanceStatus::Free); Self::deposit_event(RawEvent::RecoveryClosed(who, rescuer)); } diff --git a/substrate/frame/recovery/src/mock.rs b/substrate/frame/recovery/src/mock.rs index fa074e1faf..e6bc84f76b 100644 --- a/substrate/frame/recovery/src/mock.rs +++ b/substrate/frame/recovery/src/mock.rs @@ -36,6 +36,7 @@ impl_outer_origin! { impl_outer_event! { pub enum TestEvent for Test { + system, pallet_balances, recovery, } @@ -62,10 +63,10 @@ parameter_types! { impl frame_system::Trait for Test { type Origin = Origin; + type Call = Call; type Index = u64; type BlockNumber = u64; type Hash = H256; - type Call = Call; type Hashing = BlakeTwo256; type AccountId = u64; type Lookup = IdentityLookup; @@ -77,22 +78,21 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = (Balances, Recovery); } parameter_types! { pub const ExistentialDeposit: u64 = 1; - pub const CreationFee: u64 = 0; } impl pallet_balances::Trait for Test { type Balance = u128; - type OnReapAccount = (System, Recovery); - type OnNewAccount = (); - type Event = TestEvent; - type TransferPayment = (); type DustRemoval = (); + type Event = TestEvent; type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { diff --git a/substrate/frame/scored-pool/src/mock.rs b/substrate/frame/scored-pool/src/mock.rs index 58acc9c8d7..38a01a69af 100644 --- a/substrate/frame/scored-pool/src/mock.rs +++ b/substrate/frame/scored-pool/src/mock.rs @@ -47,7 +47,6 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); pub const ExistentialDeposit: u64 = 1; - pub const CreationFee: u64 = 0; } ord_parameter_types! { pub const KickOrigin: u64 = 2; @@ -71,17 +70,17 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnReapAccount = System; - type OnNewAccount = (); type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = System; } thread_local! { diff --git a/substrate/frame/session/src/mock.rs b/substrate/frame/session/src/mock.rs index 9aae181771..0c92267069 100644 --- a/substrate/frame/session/src/mock.rs +++ b/substrate/frame/session/src/mock.rs @@ -176,6 +176,9 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = Session; } impl pallet_timestamp::Trait for Test { diff --git a/substrate/frame/society/src/lib.rs b/substrate/frame/society/src/lib.rs index 0188ef7cb2..e803e54d4f 100644 --- a/substrate/frame/society/src/lib.rs +++ b/substrate/frame/society/src/lib.rs @@ -262,7 +262,7 @@ use sp_runtime::{Percent, ModuleId, RuntimeDebug, use frame_support::{decl_error, decl_module, decl_storage, decl_event, ensure, dispatch::DispatchResult}; use frame_support::weights::SimpleDispatchInfo; use frame_support::traits::{ - Currency, ReservableCurrency, Randomness, Get, ChangeMembers, + Currency, ReservableCurrency, Randomness, Get, ChangeMembers, BalanceStatus, ExistenceRequirement::AllowDeath, }; use frame_system::{self as system, ensure_signed, ensure_root}; @@ -984,7 +984,7 @@ decl_module! { match kind { BidKind::Deposit(deposit) => { // Slash deposit and move it to the society account - let _ = T::Currency::repatriate_reserved(&who, &Self::account_id(), deposit); + let _ = T::Currency::repatriate_reserved(&who, &Self::account_id(), deposit, BalanceStatus::Free); } BidKind::Vouch(voucher, _) => { // Ban the voucher from vouching again diff --git a/substrate/frame/society/src/mock.rs b/substrate/frame/society/src/mock.rs index e3393080ca..081d68ada4 100644 --- a/substrate/frame/society/src/mock.rs +++ b/substrate/frame/society/src/mock.rs @@ -53,7 +53,6 @@ parameter_types! { pub const AvailableBlockRatio: Perbill = Perbill::one(); pub const ExistentialDeposit: u64 = 1; - pub const CreationFee: u64 = 0; } ord_parameter_types! { @@ -78,17 +77,17 @@ impl frame_system::Trait for Test { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type OnNewAccount = (); + type OnReapAccount = Balances; + type AccountData = pallet_balances::AccountData; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnNewAccount = (); type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; - type OnReapAccount = System; + type AccountStore = System; } impl Trait for Test { diff --git a/substrate/frame/staking/src/lib.rs b/substrate/frame/staking/src/lib.rs index b4745c1fd3..447f655048 100644 --- a/substrate/frame/staking/src/lib.rs +++ b/substrate/frame/staking/src/lib.rs @@ -250,7 +250,6 @@ mod mock; #[cfg(test)] mod tests; -mod migration; mod slashing; pub mod inflation; @@ -761,9 +760,6 @@ decl_storage! { /// The earliest era for which we have a pending, unapplied slash. EarliestUnappliedSlash: Option; - - /// The version of storage for upgrade. - StorageVersion: u32; } add_extra_genesis { config(stakers): @@ -795,8 +791,6 @@ decl_storage! { }, _ => Ok(()) }; } - - StorageVersion::put(migration::CURRENT_VERSION); }); } } @@ -1298,9 +1292,10 @@ impl Module { } /// Ensures storage is upgraded to most recent necessary state. - fn ensure_storage_upgraded() { - migration::perform_migrations::(); - } + /// + /// Right now it's a no-op as all networks that are supported by Substrate Frame Core are + /// running with the latest staking storage scheme. + fn ensure_storage_upgraded() {} /// Actually make a payment to a staker. This uses the currency's reward function /// to pay the right payee for the given staker account. diff --git a/substrate/frame/staking/src/migration.rs b/substrate/frame/staking/src/migration.rs deleted file mode 100644 index 6cb472375a..0000000000 --- a/substrate/frame/staking/src/migration.rs +++ /dev/null @@ -1,140 +0,0 @@ -// Copyright 2019-2020 Parity Technologies (UK) Ltd. -// This file is part of Substrate. - -// Substrate is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. - -// Substrate is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. - -// You should have received a copy of the GNU General Public License -// along with Substrate. If not, see . - -//! Storage migrations for srml-staking. - -/// Indicator of a version of a storage layout. -pub type VersionNumber = u32; - -// the current expected version of the storage -pub const CURRENT_VERSION: VersionNumber = 2; - -/// The inner logic of migrations. -#[cfg(any(test, feature = "migrate"))] -pub mod inner { - use crate::{Store, Module, Trait}; - use frame_support::{StorageLinkedMap, StoragePrefixedMap, StorageValue}; - use codec::{Encode, Decode}; - use sp_std::vec::Vec; - use super::{CURRENT_VERSION, VersionNumber}; - - // the minimum supported version of the migration logic. - const MIN_SUPPORTED_VERSION: VersionNumber = 0; - - // migrate storage from v0 to v1. - // - // this upgrades the `Nominators` linked_map value type from `Vec` to - // `Option>` - pub fn to_v1(version: &mut VersionNumber) { - if *version != 0 { return } - *version += 1; - - let now = >::current_era(); - let res = as Store>::Nominators::translate::, _, _>( - |key| key, - |targets| crate::Nominations { - targets, - submitted_in: now, - suppressed: false, - }, - ); - - if let Err(e) = res { - frame_support::print("Encountered error in migration of Staking::Nominators map."); - if e.is_none() { - frame_support::print("Staking::Nominators map reinitialized"); - } - } - - frame_support::print("Finished migrating Staking storage to v1."); - } - - // migrate storage from v1 to v2: adds another field to the `SlashingSpans` - // struct. - pub fn to_v2(version: &mut VersionNumber) { - use crate::{EraIndex, slashing::SpanIndex}; - #[derive(Decode)] - struct V1SlashingSpans { - span_index: SpanIndex, - last_start: EraIndex, - prior: Vec, - } - - #[derive(Encode)] - struct V2SlashingSpans { - span_index: SpanIndex, - last_start: EraIndex, - last_nonzero_slash: EraIndex, - prior: Vec, - } - - if *version != 1 { return } - *version += 1; - - let prefix = as Store>::SlashingSpans::final_prefix(); - let mut current_key = prefix.to_vec(); - loop { - let maybe_next_key = sp_io::storage::next_key(¤t_key[..]) - .filter(|v| v.starts_with(&prefix[..])); - - match maybe_next_key { - Some(next_key) => { - let maybe_spans = sp_io::storage::get(&next_key[..]) - .and_then(|v| V1SlashingSpans::decode(&mut &v[..]).ok()); - if let Some(spans) = maybe_spans { - let new_val = V2SlashingSpans { - span_index: spans.span_index, - last_start: spans.last_start, - last_nonzero_slash: spans.last_start, - prior: spans.prior, - }.encode(); - - sp_io::storage::set(&next_key[..], &new_val[..]); - } - current_key = next_key; - } - None => break, - } - } - } - - pub(super) fn perform_migrations() { - as Store>::StorageVersion::mutate(|version| { - if *version < MIN_SUPPORTED_VERSION { - frame_support::print("Cannot migrate staking storage because version is less than\ - minimum."); - frame_support::print(*version); - return - } - - if *version == CURRENT_VERSION { return } - - to_v1::(version); - to_v2::(version); - }); - } -} - -#[cfg(not(any(test, feature = "migrate")))] -mod inner { - pub(super) fn perform_migrations() { } -} - -/// Perform all necessary storage migrations to get storage into the expected stsate for current -/// logic. No-op if fully upgraded. -pub(crate) fn perform_migrations() { - inner::perform_migrations::(); -} diff --git a/substrate/frame/staking/src/mock.rs b/substrate/frame/staking/src/mock.rs index 7da30300f0..c5f3ef2508 100644 --- a/substrate/frame/staking/src/mock.rs +++ b/substrate/frame/staking/src/mock.rs @@ -138,19 +138,16 @@ impl frame_system::Trait for Test { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); -} -parameter_types! { - pub const CreationFee: Balance = 0; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = (Balances, Staking, Session); } impl pallet_balances::Trait for Test { type Balance = Balance; - type OnReapAccount = (System, Staking); - type OnNewAccount = (); type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { pub const Period: BlockNumber = 1; diff --git a/substrate/frame/staking/src/tests.rs b/substrate/frame/staking/src/tests.rs index e59f211b87..73f1afd634 100644 --- a/substrate/frame/staking/src/tests.rs +++ b/substrate/frame/staking/src/tests.rs @@ -18,7 +18,6 @@ use super::*; use mock::*; -use codec::Encode; use sp_runtime::{assert_eq_error_rate, traits::{OnInitialize, BadOrigin}}; use sp_staking::offence::OffenceDetails; use frame_support::{ @@ -2671,13 +2670,6 @@ fn remove_multi_deferred() { }) } -#[test] -fn version_initialized() { - ExtBuilder::default().build().execute_with(|| { - assert_eq!(::StorageVersion::get(), crate::migration::CURRENT_VERSION); - }); -} - #[test] fn slash_kicks_validators_not_nominators() { ExtBuilder::default().build().execute_with(|| { @@ -2717,56 +2709,6 @@ fn slash_kicks_validators_not_nominators() { }); } -#[test] -fn migration_v2() { - ExtBuilder::default().build().execute_with(|| { - use crate::{EraIndex, slashing::SpanIndex}; - - #[derive(Encode)] - struct V1SlashingSpans { - span_index: SpanIndex, - last_start: EraIndex, - prior: Vec, - } - - // inject old-style values directly into storage. - let set = |stash, spans: V1SlashingSpans| { - let key = ::SlashingSpans::hashed_key_for(stash); - sp_io::storage::set(&key, &spans.encode()); - }; - - let spans_11 = V1SlashingSpans { - span_index: 10, - last_start: 1, - prior: vec![0], - }; - - let spans_21 = V1SlashingSpans { - span_index: 1, - last_start: 5, - prior: vec![], - }; - - set(11, spans_11); - set(21, spans_21); - - ::StorageVersion::put(1); - - // perform migration. - crate::migration::inner::to_v2::(&mut 1); - - assert_eq!( - ::SlashingSpans::get(&11).unwrap().last_nonzero_slash(), - 1, - ); - - assert_eq!( - ::SlashingSpans::get(&21).unwrap().last_nonzero_slash(), - 5, - ); - }); -} - #[test] fn zero_slash_keeps_nominators() { ExtBuilder::default().build().execute_with(|| { diff --git a/substrate/frame/support/procedural/src/construct_runtime/mod.rs b/substrate/frame/support/procedural/src/construct_runtime/mod.rs index b4eaf09f6a..6c14f0ecfd 100644 --- a/substrate/frame/support/procedural/src/construct_runtime/mod.rs +++ b/substrate/frame/support/procedural/src/construct_runtime/mod.rs @@ -20,7 +20,7 @@ use frame_support_procedural_tools::syn_ext as ext; use frame_support_procedural_tools::{generate_crate_access, generate_hidden_includes}; use parse::{ModuleDeclaration, RuntimeDefinition, WhereSection}; use proc_macro::TokenStream; -use proc_macro2::{Span, TokenStream as TokenStream2}; +use proc_macro2::TokenStream as TokenStream2; use quote::quote; use syn::{Ident, Result, TypePath}; @@ -58,7 +58,7 @@ fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result},`", )) } }; @@ -68,19 +68,17 @@ fn construct_runtime_parsed(definition: RuntimeDefinition) -> Result( ) } -#[derive(Debug, PartialEq, Eq, Clone, Copy)] -enum DeclOuterKind { - Event, - Origin, -} - -fn decl_outer_event_or_origin<'a>( +fn decl_outer_origin<'a>( runtime_name: &'a Ident, module_declarations: impl Iterator, system_name: &'a Ident, scrate: &'a TokenStream2, - kind: DeclOuterKind, ) -> syn::Result { let mut modules_tokens = TokenStream2::new(); - let kind_str = format!("{:?}", kind); for module_declaration in module_declarations { - match module_declaration.find_part(&kind_str) { + match module_declaration.find_part("Origin") { Some(module_entry) => { let module = &module_declaration.module; let instance = module_declaration.instance.as_ref(); let generics = &module_entry.generics; if instance.is_some() && generics.params.len() == 0 { let msg = format!( - "Instantiable module with no generic `{}` cannot \ - be constructed: module `{}` must have generic `{}`", - kind_str, module_declaration.name, kind_str + "Instantiable module with no generic `Origin` cannot \ + be constructed: module `{}` must have generic `Origin`", + module_declaration.name ); return Err(syn::Error::new(module_declaration.name.span(), msg)); } @@ -299,14 +289,46 @@ fn decl_outer_event_or_origin<'a>( None => {} } } - let macro_call = match kind { - DeclOuterKind::Event => quote!(#scrate::impl_outer_event!), - DeclOuterKind::Origin => quote!(#scrate::impl_outer_origin!), - }; - let enum_name = Ident::new(kind_str.as_str(), Span::call_site()); + Ok(quote!( - #macro_call { - pub enum #enum_name for #runtime_name where system = #system_name { + #scrate::impl_outer_origin! { + pub enum Origin for #runtime_name where system = #system_name { + #modules_tokens + } + } + )) +} + +fn decl_outer_event<'a>( + runtime_name: &'a Ident, + module_declarations: impl Iterator, + scrate: &'a TokenStream2, +) -> syn::Result { + let mut modules_tokens = TokenStream2::new(); + for module_declaration in module_declarations { + match module_declaration.find_part("Event") { + Some(module_entry) => { + let module = &module_declaration.module; + let instance = module_declaration.instance.as_ref(); + let generics = &module_entry.generics; + if instance.is_some() && generics.params.len() == 0 { + let msg = format!( + "Instantiable module with no generic `Event` cannot \ + be constructed: module `{}` must have generic `Event`", + module_declaration.name, + ); + return Err(syn::Error::new(module_declaration.name.span(), msg)); + } + let tokens = quote!(#module #instance #generics ,); + modules_tokens.extend(tokens); + } + None => {} + } + } + + Ok(quote!( + #scrate::impl_outer_event! { + pub enum Event for #runtime_name { #modules_tokens } } diff --git a/substrate/frame/support/src/event.rs b/substrate/frame/support/src/event.rs index 3ff6d4ab45..d30e6ddeea 100644 --- a/substrate/frame/support/src/event.rs +++ b/substrate/frame/support/src/event.rs @@ -337,30 +337,14 @@ macro_rules! impl_outer_event { ( $(#[$attr:meta])* pub enum $name:ident for $runtime:ident { - $( $rest_event_without_system:tt )* + $( $rest_events:tt )* } ) => { $crate::impl_outer_event!( $( #[$attr] )*; $name; $runtime; - system; - Modules { $( $rest_event_without_system )* }; - ; - ); - }; - ( - $(#[$attr:meta])* - pub enum $name:ident for $runtime:ident where system = $system:ident { - $( $rest_event_with_system:tt )* - } - ) => { - $crate::impl_outer_event!( - $( #[$attr] )*; - $name; - $runtime; - $system; - Modules { $( $rest_event_with_system )* }; + Modules { $( $rest_events )* }; ; ); }; @@ -369,7 +353,6 @@ macro_rules! impl_outer_event { $(#[$attr:meta])*; $name:ident; $runtime:ident; - $system:ident; Modules { $module:ident $instance:ident, $( $rest_event_generic_instance:tt )* @@ -380,7 +363,6 @@ macro_rules! impl_outer_event { $( #[$attr] )*; $name; $runtime; - $system; Modules { $( $rest_event_generic_instance )* }; $( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event<$runtime>{ $instance },; ); @@ -390,7 +372,6 @@ macro_rules! impl_outer_event { $(#[$attr:meta])*; $name:ident; $runtime:ident; - $system:ident; Modules { $module:ident $instance:ident, $( $rest_event_instance:tt )* @@ -401,7 +382,6 @@ macro_rules! impl_outer_event { $( #[$attr] )*; $name; $runtime; - $system; Modules { $( $rest_event_instance )* }; $( $module_name::Event $( <$generic_param> )* $( { $generic_instance } )?, )* $module::Event { $instance },; ); @@ -411,7 +391,6 @@ macro_rules! impl_outer_event { $(#[$attr:meta])*; $name:ident; $runtime:ident; - $system:ident; Modules { $module:ident, $( $rest_event_generic:tt )* @@ -422,7 +401,6 @@ macro_rules! impl_outer_event { $( #[$attr] )*; $name; $runtime; - $system; Modules { $( $rest_event_generic )* }; $( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event<$runtime>,; ); @@ -432,7 +410,6 @@ macro_rules! impl_outer_event { $(#[$attr:meta])*; $name:ident; $runtime:ident; - $system:ident; Modules { $module:ident, $( $rest_event_no_generic_no_instance:tt )* @@ -443,7 +420,6 @@ macro_rules! impl_outer_event { $( #[$attr] )*; $name; $runtime; - $system; Modules { $( $rest_event_no_generic_no_instance )* }; $( $module_name::Event $( <$generic_param> )? $( { $generic_instance } )?, )* $module::Event,; ); @@ -454,7 +430,6 @@ macro_rules! impl_outer_event { $(#[$attr:meta])*; $name:ident; $runtime:ident; - $system:ident; Modules {}; $( $module_name:ident::Event $( <$generic_param:ident> )? $( { $generic_instance:ident } )?, )*; ) => { @@ -468,18 +443,12 @@ macro_rules! impl_outer_event { $(#[$attr])* #[allow(non_camel_case_types)] pub enum $name { - system($system::Event), $( [< $module_name $(_ $generic_instance )? >]( $module_name::Event < $( $generic_param )? $(, $module_name::$generic_instance )? > ), )* } - impl From<$system::Event> for $name { - fn from(x: $system::Event) -> Self { - $name::system(x) - } - } $( impl From<$module_name::Event < $( $generic_param, )? $( $module_name::$generic_instance )? >> for $name { fn from(x: $module_name::Event < $( $generic_param, )? $( $module_name::$generic_instance )? >) -> Self { @@ -505,7 +474,6 @@ macro_rules! impl_outer_event { $crate::__impl_outer_event_json_metadata!( $runtime; $name; - $system; $( $module_name::Event < $( $generic_param )? $(, $module_name::$generic_instance )? > @@ -521,7 +489,6 @@ macro_rules! __impl_outer_event_json_metadata { ( $runtime:ident; $event_name:ident; - $system:ident; $( $module_name:ident::Event < $( $generic_params:path ),* > $( $instance:ident )?, )*; ) => { impl $runtime { @@ -530,22 +497,20 @@ macro_rules! __impl_outer_event_json_metadata { $crate::event::OuterEventMetadata { name: $crate::event::DecodeDifferent::Encode(stringify!($event_name)), events: $crate::event::DecodeDifferent::Encode(&[ - ("system", $crate::event::FnEncode($system::Event::metadata)) $( - , ( + ( stringify!($module_name), $crate::event::FnEncode( $module_name::Event ::< $( $generic_params ),* > ::metadata ) ) - )* + ),* ]) } } $crate::__impl_outer_event_json_metadata! { @DECL_MODULE_EVENT_FNS - $system <> ; $( $module_name < $( $generic_params ),* > $( $instance )? ; )* } } @@ -717,6 +682,7 @@ mod tests { impl_outer_event! { pub enum TestEvent for TestRuntime { + system, event_module, event_module2, event_module3, @@ -727,7 +693,8 @@ mod tests { pub struct TestRuntime2; impl_outer_event! { - pub enum TestEventSystemRenamed for TestRuntime2 where system = system_renamed { + pub enum TestEventSystemRenamed for TestRuntime2 { + system_renamed, event_module, event_module2, event_module3, diff --git a/substrate/frame/support/src/metadata.rs b/substrate/frame/support/src/metadata.rs index f3c9945f88..46662e5354 100644 --- a/substrate/frame/support/src/metadata.rs +++ b/substrate/frame/support/src/metadata.rs @@ -404,6 +404,7 @@ mod tests { impl_outer_event! { pub enum TestEvent for TestRuntime { + system, event_module, event_module2, } diff --git a/substrate/frame/support/src/storage/generator/map.rs b/substrate/frame/support/src/storage/generator/map.rs index c1d9f1b149..497b3fd477 100644 --- a/substrate/frame/support/src/storage/generator/map.rs +++ b/substrate/frame/support/src/storage/generator/map.rs @@ -104,7 +104,7 @@ impl> storage::StorageMap } fn insert, ValArg: EncodeLike>(key: KeyArg, val: ValArg) { - unhashed::put(Self::storage_map_final_key(key).as_ref(), &val.borrow()) + unhashed::put(Self::storage_map_final_key(key).as_ref(), &val) } fn remove>(key: KeyArg) { @@ -117,12 +117,58 @@ impl> storage::StorageMap let ret = f(&mut val); match G::from_query_to_optional_value(val) { - Some(ref val) => unhashed::put(final_key.as_ref(), &val.borrow()), + Some(ref val) => unhashed::put(final_key.as_ref(), &val), None => unhashed::kill(final_key.as_ref()), } ret } + fn mutate_exists, R, F: FnOnce(&mut Option) -> R>(key: KeyArg, f: F) -> R { + let final_key = Self::storage_map_final_key(key); + let mut val = unhashed::get(final_key.as_ref()); + + let ret = f(&mut val); + match val { + Some(ref val) => unhashed::put(final_key.as_ref(), &val), + None => unhashed::kill(final_key.as_ref()), + } + ret + } + + fn try_mutate, R, E, F: FnOnce(&mut Self::Query) -> Result>( + key: KeyArg, + f: F + ) -> Result { + let final_key = Self::storage_map_final_key(key); + let mut val = G::from_optional_value_to_query(unhashed::get(final_key.as_ref())); + + let ret = f(&mut val); + if ret.is_ok() { + match G::from_query_to_optional_value(val) { + Some(ref val) => unhashed::put(final_key.as_ref(), &val.borrow()), + None => unhashed::kill(final_key.as_ref()), + } + } + ret + } + + fn try_mutate_exists, R, E, F: FnOnce(&mut Option) -> Result>( + key: KeyArg, + f: F + ) -> Result { + let final_key = Self::storage_map_final_key(key); + let mut val = unhashed::get(final_key.as_ref()); + + let ret = f(&mut val); + if ret.is_ok() { + match val { + Some(ref val) => unhashed::put(final_key.as_ref(), &val.borrow()), + None => unhashed::kill(final_key.as_ref()), + } + } + ret + } + fn take>(key: KeyArg) -> Self::Query { let key = Self::storage_map_final_key(key); let value = unhashed::take(key.as_ref()); diff --git a/substrate/frame/support/src/storage/generator/value.rs b/substrate/frame/support/src/storage/generator/value.rs index 44641b85f7..4083576e29 100644 --- a/substrate/frame/support/src/storage/generator/value.rs +++ b/substrate/frame/support/src/storage/generator/value.rs @@ -66,6 +66,10 @@ impl> storage::StorageValue for G { G::from_optional_value_to_query(value) } + fn try_get() -> Result { + unhashed::get(&Self::storage_value_final_key()).ok_or(()) + } + fn translate) -> Option>(f: F) -> Result, ()> { let key = Self::storage_value_final_key(); diff --git a/substrate/frame/balances/src/migration.rs b/substrate/frame/support/src/storage/migration.rs similarity index 75% rename from substrate/frame/balances/src/migration.rs rename to substrate/frame/support/src/storage/migration.rs index 4748cf3913..e8d58b46d4 100644 --- a/substrate/frame/balances/src/migration.rs +++ b/substrate/frame/support/src/storage/migration.rs @@ -18,8 +18,9 @@ use sp_std::prelude::*; use codec::{Encode, Decode}; -use frame_support::{StorageHasher, Twox128}; +use crate::{StorageHasher, Twox128}; +/// Utility to iterate through raw items in storage. pub struct StorageIterator { prefix: [u8; 32], previous_key: Vec, @@ -28,12 +29,14 @@ pub struct StorageIterator { } impl StorageIterator { + /// Construct iterator to iterate over map items in `module` for the map called `item`. pub fn new(module: &[u8], item: &[u8]) -> Self { let mut prefix = [0u8; 32]; prefix[0..16].copy_from_slice(&Twox128::hash(module)); prefix[16..32].copy_from_slice(&Twox128::hash(item)); Self { prefix, previous_key: prefix[..].to_vec(), drain: false, _phantom: Default::default() } } + /// Mutate this iterator into a draining iterator; items iterated are removed from storage. pub fn drain(mut self) -> Self { self.drain = true; self @@ -67,6 +70,7 @@ impl Iterator for StorageIterator { } } +/// Get a particular value in storage by the `module`, the map's `item` name and the key `hash`. pub fn get_storage_value(module: &[u8], item: &[u8], hash: &[u8]) -> Option { let mut key = vec![0u8; 32 + hash.len()]; key[0..16].copy_from_slice(&Twox128::hash(module)); @@ -75,6 +79,16 @@ pub fn get_storage_value(module: &[u8], item: &[u8], hash: &[ frame_support::storage::unhashed::get::(&key) } +/// Get a particular value in storage by the `module`, the map's `item` name and the key `hash`. +pub fn take_storage_value(module: &[u8], item: &[u8], hash: &[u8]) -> Option { + let mut key = vec![0u8; 32 + hash.len()]; + key[0..16].copy_from_slice(&Twox128::hash(module)); + key[16..32].copy_from_slice(&Twox128::hash(item)); + key[32..].copy_from_slice(hash); + frame_support::storage::unhashed::take::(&key) +} + +/// Put a particular value into storage by the `module`, the map's `item` name and the key `hash`. pub fn put_storage_value(module: &[u8], item: &[u8], hash: &[u8], value: T) { let mut key = vec![0u8; 32 + hash.len()]; key[0..16].copy_from_slice(&Twox128::hash(module)); diff --git a/substrate/frame/support/src/storage/mod.rs b/substrate/frame/support/src/storage/mod.rs index 3eb1a6f116..4bca5ea402 100644 --- a/substrate/frame/support/src/storage/mod.rs +++ b/substrate/frame/support/src/storage/mod.rs @@ -25,6 +25,7 @@ pub mod hashed; pub mod child; #[doc(hidden)] pub mod generator; +pub mod migration; /// A trait for working with macro-generated storage values under the substrate storage API. /// @@ -43,6 +44,10 @@ pub trait StorageValue { /// Load the value from the provided storage instance. fn get() -> Self::Query; + /// Try to get the underlying value from the provided storage instance; `Ok` if it exists, + /// `Err` if not. + fn try_get() -> Result; + /// Translate a value from some previous type (`O`) to the current type. /// /// `f: F` is the translation function. @@ -143,14 +148,28 @@ pub trait StorageMap { /// Mutate the value under a key. fn mutate, R, F: FnOnce(&mut Self::Query) -> R>(key: KeyArg, f: F) -> R; + /// Mutate the item, only if an `Ok` value is returned. + fn try_mutate, R, E, F: FnOnce(&mut Self::Query) -> Result>( + key: KeyArg, + f: F, + ) -> Result; + + /// Mutate the value under a key. Deletes the item if mutated to a `None`. + fn mutate_exists, R, F: FnOnce(&mut Option) -> R>(key: KeyArg, f: F) -> R; + + /// Mutate the item, only if an `Ok` value is returned. Deletes the item if mutated to a `None`. + fn try_mutate_exists, R, E, F: FnOnce(&mut Option) -> Result>( + key: KeyArg, + f: F, + ) -> Result; + /// Take the value under a key. fn take>(key: KeyArg) -> Self::Query; /// Append the given items to the value in the storage. /// /// `V` is required to implement `codec::EncodeAppend`. - fn append(key: KeyArg, items: Items) -> Result<(), &'static str> - where + fn append(key: KeyArg, items: Items) -> Result<(), &'static str> where KeyArg: EncodeLike, Item: Encode, EncodeLikeItem: EncodeLike, @@ -162,8 +181,7 @@ pub trait StorageMap { /// old (presumably corrupt) value is replaced with the given `items`. /// /// `V` is required to implement `codec::EncodeAppend`. - fn append_or_insert(key: KeyArg, items: Items) - where + fn append_or_insert(key: KeyArg, items: Items) where KeyArg: EncodeLike, Item: Encode, EncodeLikeItem: EncodeLike, diff --git a/substrate/frame/support/src/traits.rs b/substrate/frame/support/src/traits.rs index 0722012910..e12defcc54 100644 --- a/substrate/frame/support/src/traits.rs +++ b/substrate/frame/support/src/traits.rs @@ -28,6 +28,115 @@ use sp_runtime::{ }; use crate::dispatch::Parameter; +use crate::storage::StorageMap; + +/// An abstraction of a value stored within storage, but possibly as part of a larger composite +/// item. +pub trait StoredMap { + /// Get the item, or its default if it doesn't yet exist; we make no distinction between the + /// two. + fn get(k: &K) -> T; + /// Get whether the item takes up any storage. If this is `false`, then `get` will certainly + /// return the `T::default()`. If `true`, then there is no implication for `get` (i.e. it + /// may return any value, including the default). + /// + /// NOTE: This may still be `true`, even after `remove` is called. This is the case where + /// a single storage entry is shared between multiple `StoredMap` items single, without + /// additional logic to enforce it, deletion of any one them doesn't automatically imply + /// deletion of them all. + fn is_explicit(k: &K) -> bool; + /// Mutate the item. + fn mutate(k: &K, f: impl FnOnce(&mut T) -> R) -> R; + /// Mutate the item, removing or resetting to default value if it has been mutated to `None`. + fn mutate_exists(k: &K, f: impl FnOnce(&mut Option) -> R) -> R; + /// Maybe mutate the item only if an `Ok` value is returned from `f`. Do nothing if an `Err` is + /// returned. It is removed or reset to default value if it has been mutated to `None` + fn try_mutate_exists(k: &K, f: impl FnOnce(&mut Option) -> Result) -> Result; + /// Set the item to something new. + fn insert(k: &K, t: T) { Self::mutate(k, |i| *i = t); } + /// Remove the item or otherwise replace it with its default value; we don't care which. + fn remove(k: &K); +} + +/// A simple, generic one-parameter event notifier/handler. +pub trait Happened { + /// The thing happened. + fn happened(t: &T); +} + +/// A shim for placing around a storage item in order to use it as a `StoredValue`. Ideally this +/// wouldn't be needed as `StorageValue`s should blanket implement `StoredValue`s, however this +/// would break the ability to have custom impls of `StoredValue`. The other workaround is to +/// implement it directly in the macro. +/// +/// This form has the advantage that two additional types are provides, `Created` and `Removed`, +/// which are both generic events that can be tied to handlers to do something in the case of being +/// about to create an account where one didn't previously exist (at all; not just where it used to +/// be the default value), or where the account is being removed or reset back to the default value +/// where previously it did exist (though may have been in a default state). This works well with +/// system module's `CallOnCreatedAccount` and `CallKillAccount`. +pub struct StorageMapShim< + S, + Created, + Removed, + K, + T +>(sp_std::marker::PhantomData<(S, Created, Removed, K, T)>); +impl< + S: StorageMap, + Created: Happened, + Removed: Happened, + K: FullCodec, + T: FullCodec +> StoredMap for StorageMapShim { + fn get(k: &K) -> T { S::get(k) } + fn is_explicit(k: &K) -> bool { S::contains_key(k) } + fn insert(k: &K, t: T) { + S::insert(k, t); + if !S::contains_key(&k) { + Created::happened(k); + } + } + fn remove(k: &K) { + if S::contains_key(&k) { + Removed::happened(&k); + } + S::remove(k); + } + fn mutate(k: &K, f: impl FnOnce(&mut T) -> R) -> R { + let r = S::mutate(k, f); + if !S::contains_key(&k) { + Created::happened(k); + } + r + } + fn mutate_exists(k: &K, f: impl FnOnce(&mut Option) -> R) -> R { + let (existed, exists, r) = S::mutate_exists(k, |maybe_value| { + let existed = maybe_value.is_some(); + let r = f(maybe_value); + (existed, maybe_value.is_some(), r) + }); + if !existed && exists { + Created::happened(k); + } else if existed && !exists { + Removed::happened(k); + } + r + } + fn try_mutate_exists(k: &K, f: impl FnOnce(&mut Option) -> Result) -> Result { + S::try_mutate_exists(k, |maybe_value| { + let existed = maybe_value.is_some(); + f(maybe_value).map(|v| (existed, maybe_value.is_some(), v)) + }).map(|(existed, exists, v)| { + if !existed && exists { + Created::happened(k); + } else if existed && !exists { + Removed::happened(k); + } + v + }) + } +} /// Anything that can have a `::len()` method. pub trait Len { @@ -65,6 +174,25 @@ pub trait Contains { fn count() -> usize { Self::sorted_members().len() } } +/// Determiner to say whether a given account is unused. +pub trait IsDeadAccount { + /// Is the given account dead? + fn is_dead_account(who: &AccountId) -> bool; +} + +impl IsDeadAccount for () { + fn is_dead_account(_who: &AccountId) -> bool { + true + } +} + +/// Handler for when a new account has been created. +#[impl_trait_for_tuples::impl_for_tuples(30)] +pub trait OnNewAccount { + /// A new account `who` has been registered. + fn on_new_account(who: &AccountId); +} + /// The account with the given id was reaped. #[impl_trait_for_tuples::impl_for_tuples(30)] pub trait OnReapAccount { @@ -72,20 +200,6 @@ pub trait OnReapAccount { fn on_reap_account(who: &AccountId); } -/// Outcome of a balance update. -pub enum UpdateBalanceOutcome { - /// Account balance was simply updated. - Updated, - /// The update led to killing the account. - AccountKilled, - /// Free balance became zero as a result of this update. - FreeBalanceZero, - /// Reserved balance became zero as a result of this update. - ReservedBalanceZero, - /// The account started and ended non-existent. - StillDead, -} - /// A trait for finding the author of a block header based on the `PreRuntime` digests contained /// within it. pub trait FindAuthor { @@ -494,10 +608,15 @@ pub trait Currency { fn make_free_balance_be( who: &AccountId, balance: Self::Balance, - ) -> ( - SignedImbalance, - UpdateBalanceOutcome, - ); + ) -> SignedImbalance; +} + +/// Status of funds. +pub enum BalanceStatus { + /// Funds are free, as corresponding to `free` item in Balances. + Free, + /// Funds are reserved, as corresponding to `reserved` item in Balances. + Reserved, } /// A currency where funds can be reserved from the user. @@ -528,7 +647,6 @@ pub trait ReservableCurrency: Currency { /// collapsed to zero if it ever becomes less than `ExistentialDeposit`. fn reserved_balance(who: &AccountId) -> Self::Balance; - /// Moves `value` from balance to reserved balance. /// /// If the free balance is lower than `value`, then no funds will be moved and an `Err` will @@ -547,16 +665,18 @@ pub trait ReservableCurrency: Currency { /// invoke `on_reserved_too_low` and could reap the account. fn unreserve(who: &AccountId, value: Self::Balance) -> Self::Balance; - /// Moves up to `value` from reserved balance of account `slashed` to free balance of account + /// Moves up to `value` from reserved balance of account `slashed` to balance of account /// `beneficiary`. `beneficiary` must exist for this to succeed. If it does not, `Err` will be - /// returned. + /// returned. Funds will be placed in either the `free` balance or the `reserved` balance, + /// depending on the `status`. /// /// As much funds up to `value` will be deducted as possible. If this is less than `value`, /// then `Ok(non_zero)` will be returned. fn repatriate_reserved( slashed: &AccountId, beneficiary: &AccountId, - value: Self::Balance + value: Self::Balance, + status: BalanceStatus, ) -> result::Result; } diff --git a/substrate/frame/support/test/tests/construct_runtime_ui/missing_system_module.stderr b/substrate/frame/support/test/tests/construct_runtime_ui/missing_system_module.stderr index 5964a8aa76..442af9c01f 100644 --- a/substrate/frame/support/test/tests/construct_runtime_ui/missing_system_module.stderr +++ b/substrate/frame/support/test/tests/construct_runtime_ui/missing_system_module.stderr @@ -1,4 +1,4 @@ -error: `System` module declaration is missing. Please add this line: `System: system::{Module, Call, Storage, Config, Event},` +error: `System` module declaration is missing. Please add this line: `System: system::{Module, Call, Storage, Config, Event},` --> $DIR/missing_system_module.rs:8:2 | 8 | { diff --git a/substrate/frame/support/test/tests/decl_error.rs b/substrate/frame/support/test/tests/decl_error.rs index b90c870c31..39d7dbdb96 100644 --- a/substrate/frame/support/test/tests/decl_error.rs +++ b/substrate/frame/support/test/tests/decl_error.rs @@ -99,7 +99,7 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Module, Call, Event}, + System: system::{Module, Call, Event}, Module1_1: module1::::{Module, Call, Storage}, Module2: module2::{Module, Call, Storage}, Module1_2: module1::::{Module, Call, Storage}, diff --git a/substrate/frame/support/test/tests/instance.rs b/substrate/frame/support/test/tests/instance.rs index 4c509ae18f..48854baad9 100644 --- a/substrate/frame/support/test/tests/instance.rs +++ b/substrate/frame/support/test/tests/instance.rs @@ -247,7 +247,7 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Module, Call, Event}, + System: system::{Module, Call, Event}, Module1_1: module1::::{ Module, Call, Storage, Event, Config, Origin, Inherent }, diff --git a/substrate/frame/support/test/tests/issue2219.rs b/substrate/frame/support/test/tests/issue2219.rs index c0a253f22b..4537766981 100644 --- a/substrate/frame/support/test/tests/issue2219.rs +++ b/substrate/frame/support/test/tests/issue2219.rs @@ -173,7 +173,7 @@ frame_support::construct_runtime!( NodeBlock = Block, UncheckedExtrinsic = UncheckedExtrinsic { - System: system::{Module, Call, Event}, + System: system::{Module, Call, Event}, Module: module::{Module, Call, Storage, Config}, } ); diff --git a/substrate/frame/support/test/tests/system.rs b/substrate/frame/support/test/tests/system.rs index c5b591642b..c7f60117bc 100644 --- a/substrate/frame/support/test/tests/system.rs +++ b/substrate/frame/support/test/tests/system.rs @@ -23,7 +23,7 @@ pub trait Trait: 'static + Eq + Clone { type BlockNumber: Decode + Encode + EncodeLike + Clone + Default; type Hash; type AccountId: Encode + EncodeLike + Decode; - type Event: From; + type Event: From>; type ModuleToIndex: frame_support::traits::ModuleToIndex; } @@ -36,9 +36,10 @@ impl Module { } frame_support::decl_event!( - pub enum Event { + pub enum Event where BlockNumber = ::BlockNumber { ExtrinsicSuccess, ExtrinsicFailed, + Ignore(BlockNumber), } ); diff --git a/substrate/frame/system/benches/bench.rs b/substrate/frame/system/benches/bench.rs index 49872ef7db..1e24cb2c05 100644 --- a/substrate/frame/system/benches/bench.rs +++ b/substrate/frame/system/benches/bench.rs @@ -46,6 +46,7 @@ impl_outer_origin!{ impl_outer_event! { pub enum Event for Runtime { + system, module, } } @@ -75,6 +76,9 @@ impl system::Trait for Runtime { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl module::Trait for Runtime { diff --git a/substrate/frame/system/src/lib.rs b/substrate/frame/system/src/lib.rs index 58a6dbc56d..c351020a41 100644 --- a/substrate/frame/system/src/lib.rs +++ b/substrate/frame/system/src/lib.rs @@ -113,35 +113,19 @@ use sp_runtime::{ use sp_core::{ChangesTrieConfiguration, storage::well_known_keys}; use frame_support::{ decl_module, decl_event, decl_storage, decl_error, storage, Parameter, - traits::{Contains, Get, ModuleToIndex, OnReapAccount}, + traits::{ + Contains, Get, ModuleToIndex, OnNewAccount, OnReapAccount, IsDeadAccount, Happened, + StoredMap + }, weights::{Weight, DispatchInfo, DispatchClass, SimpleDispatchInfo}, }; -use codec::{Encode, Decode}; +use codec::{Encode, Decode, FullCodec, EncodeLike}; #[cfg(any(feature = "std", test))] use sp_io::TestExternalities; pub mod offchain; -/// Handler for when a new account has been created. -#[impl_trait_for_tuples::impl_for_tuples(30)] -pub trait OnNewAccount { - /// A new account `who` has been registered. - fn on_new_account(who: &AccountId); -} - -/// Determiner to say whether a given account is unused. -pub trait IsDeadAccount { - /// Is the given account dead? - fn is_dead_account(who: &AccountId) -> bool; -} - -impl IsDeadAccount for () { - fn is_dead_account(_who: &AccountId) -> bool { - true - } -} - /// Compute the trie root of a list of extrinsics. pub fn extrinsics_root(extrinsics: &[E]) -> H::Output { extrinsics_data_root::(extrinsics.iter().map(codec::Encode::encode).collect()) @@ -200,7 +184,7 @@ pub trait Trait: 'static + Eq + Clone { >; /// The aggregated event type of the runtime. - type Event: Parameter + Member + From + Debug; + type Event: Parameter + Member + From> + Debug; /// Maximum number of block number to block hash mappings to keep (oldest pruned first). type BlockHashCount: Get; @@ -224,6 +208,18 @@ pub trait Trait: 'static + Eq + Clone { /// Expects the `ModuleToIndex` type that is being generated by `construct_runtime!` in the /// runtime. For tests it is okay to use `()` as type (returns `0` for each input). type ModuleToIndex: ModuleToIndex; + + /// Data to be associated with an account (other than nonce/transaction counter, which this + /// module does regardless). + type AccountData: Member + FullCodec + Clone + Default; + + /// Handler for when a new account has just been created. + type OnNewAccount: OnNewAccount; + + /// A function that is invoked when an account has been determined to be dead. + /// + /// All resources should be cleaned up associated with the given account. + type OnReapAccount: OnReapAccount; } pub type DigestOf = generic::Digest<::Hash>; @@ -232,111 +228,6 @@ pub type DigestItemOf = generic::DigestItem<::Hash>; pub type Key = Vec; pub type KeyValue = (Vec, Vec); -decl_module! { - pub struct Module for enum Call where origin: T::Origin { - type Error = Error; - - /// A big dispatch that will disallow any other transaction to be included. - // TODO: this must be preferable available for testing really (not possible at the moment). - #[weight = SimpleDispatchInfo::MaxOperational] - fn fill_block(origin) { - ensure_root(origin)?; - } - - /// Make some on-chain remark. - #[weight = SimpleDispatchInfo::FixedNormal(10_000)] - fn remark(origin, _remark: Vec) { - ensure_signed(origin)?; - } - - /// Set the number of pages in the WebAssembly environment's heap. - #[weight = SimpleDispatchInfo::FixedOperational(10_000)] - fn set_heap_pages(origin, pages: u64) { - ensure_root(origin)?; - storage::unhashed::put_raw(well_known_keys::HEAP_PAGES, &pages.encode()); - } - - /// Set the new runtime code. - #[weight = SimpleDispatchInfo::FixedOperational(200_000)] - pub fn set_code(origin, code: Vec) { - ensure_root(origin)?; - - let current_version = T::Version::get(); - let new_version = sp_io::misc::runtime_version(&code) - .and_then(|v| RuntimeVersion::decode(&mut &v[..]).ok()) - .ok_or_else(|| Error::::FailedToExtractRuntimeVersion)?; - - if new_version.spec_name != current_version.spec_name { - Err(Error::::InvalidSpecName)? - } - - if new_version.spec_version < current_version.spec_version { - Err(Error::::SpecVersionNotAllowedToDecrease)? - } else if new_version.spec_version == current_version.spec_version { - if new_version.impl_version < current_version.impl_version { - Err(Error::::ImplVersionNotAllowedToDecrease)? - } else if new_version.impl_version == current_version.impl_version { - Err(Error::::SpecOrImplVersionNeedToIncrease)? - } - } - - storage::unhashed::put_raw(well_known_keys::CODE, &code); - Self::deposit_event(Event::CodeUpdated); - } - - /// Set the new runtime code without doing any checks of the given `code`. - #[weight = SimpleDispatchInfo::FixedOperational(200_000)] - pub fn set_code_without_checks(origin, code: Vec) { - ensure_root(origin)?; - storage::unhashed::put_raw(well_known_keys::CODE, &code); - Self::deposit_event(Event::CodeUpdated); - } - - /// Set the new changes trie configuration. - #[weight = SimpleDispatchInfo::FixedOperational(20_000)] - pub fn set_changes_trie_config(origin, changes_trie_config: Option) { - ensure_root(origin)?; - match changes_trie_config.clone() { - Some(changes_trie_config) => storage::unhashed::put_raw( - well_known_keys::CHANGES_TRIE_CONFIG, - &changes_trie_config.encode(), - ), - None => storage::unhashed::kill(well_known_keys::CHANGES_TRIE_CONFIG), - } - - let log = generic::DigestItem::ChangesTrieSignal( - generic::ChangesTrieSignal::NewConfiguration(changes_trie_config), - ); - Self::deposit_log(log.into()); - } - - /// Set some items of storage. - #[weight = SimpleDispatchInfo::FixedOperational(10_000)] - fn set_storage(origin, items: Vec) { - ensure_root(origin)?; - for i in &items { - storage::unhashed::put_raw(&i.0, &i.1); - } - } - - /// Kill some items from storage. - #[weight = SimpleDispatchInfo::FixedOperational(10_000)] - fn kill_storage(origin, keys: Vec) { - ensure_root(origin)?; - for key in &keys { - storage::unhashed::kill(&key); - } - } - - /// Kill all storage items with a key that starts with the given prefix. - #[weight = SimpleDispatchInfo::FixedOperational(10_000)] - fn kill_prefix(origin, prefix: Key) { - ensure_root(origin)?; - storage::unhashed::kill_prefix(&prefix); - } - } -} - /// A phase of a block's execution. #[derive(Encode, Decode, RuntimeDebug)] #[cfg_attr(feature = "std", derive(Serialize, PartialEq, Eq, Clone))] @@ -359,40 +250,6 @@ pub struct EventRecord { pub topics: Vec, } -decl_event!( - /// Event for the System module. - pub enum Event { - /// An extrinsic completed successfully. - ExtrinsicSuccess(DispatchInfo), - /// An extrinsic failed. - ExtrinsicFailed(DispatchError, DispatchInfo), - /// `:code` was updated. - CodeUpdated, - } -); - -decl_error! { - /// Error for the System module - pub enum Error for Module { - /// The name of specification does not match between the current runtime - /// and the new runtime. - InvalidSpecName, - /// The specification version is not allowed to decrease between the current runtime - /// and the new runtime. - SpecVersionNotAllowedToDecrease, - /// The implementation version is not allowed to decrease between the current runtime - /// and the new runtime. - ImplVersionNotAllowedToDecrease, - /// The specification or the implementation version need to increase between the - /// current runtime and the new runtime. - SpecOrImplVersionNeedToIncrease, - /// Failed to extract the runtime version from the new runtime. - /// - /// Either calling `Core_version` or decoding `RuntimeVersion` failed. - FailedToExtractRuntimeVersion, - } -} - /// Origin for the System module. #[derive(PartialEq, Eq, Clone, RuntimeDebug)] pub enum RawOrigin { @@ -435,29 +292,45 @@ type EventIndex = u32; decl_storage! { trait Store for Module as System { - /// Extrinsics nonce for accounts. - pub AccountNonce get(fn account_nonce): map hasher(blake2_256) T::AccountId => T::Index; + /// The full account information for a particular account ID. + // TODO: should be hasher(twox64_concat) - will need staged migration + // TODO: should not including T::Index (the nonce) + // https://github.com/paritytech/substrate/issues/4917 + pub Account get(fn account): map hasher(blake2_256) T::AccountId => (T::Index, T::AccountData); + /// Total extrinsics count for the current block. ExtrinsicCount: Option; + /// Total weight for all extrinsics put together, for the current block. AllExtrinsicsWeight: Option; + /// Total length (in bytes) for all extrinsics put together, for the current block. AllExtrinsicsLen: Option; + /// Map of block numbers to block hashes. + // TODO: should be hasher(twox64_concat) - will need one-off migration + // https://github.com/paritytech/substrate/issues/4917 pub BlockHash get(fn block_hash) build(|_| vec![(T::BlockNumber::zero(), hash69())]): map hasher(blake2_256) T::BlockNumber => T::Hash; + /// Extrinsics data for the current block (maps an extrinsic's index to its data). - ExtrinsicData get(fn extrinsic_data): map hasher(blake2_256) u32 => Vec; + ExtrinsicData get(fn extrinsic_data): map hasher(twox_64_concat) u32 => Vec; + /// The current block number being processed. Set by `execute_block`. Number get(fn block_number) build(|_| 1.into()): T::BlockNumber; + /// Hash of the previous block. ParentHash get(fn parent_hash) build(|_| hash69()): T::Hash; + /// Extrinsics root of the current block, also part of the block header. ExtrinsicsRoot get(fn extrinsics_root): T::Hash; + /// Digest of the current block, also part of the block header. Digest get(fn digest): DigestOf; + /// Events deposited for the current block. Events get(fn events): Vec>; + /// The number of events in the `Events` list. EventCount get(fn event_count): EventIndex; @@ -499,6 +372,150 @@ decl_storage! { } } +decl_event!( + /// Event for the System module. + pub enum Event where AccountId = ::AccountId { + /// An extrinsic completed successfully. + ExtrinsicSuccess(DispatchInfo), + /// An extrinsic failed. + ExtrinsicFailed(DispatchError, DispatchInfo), + /// `:code` was updated. + CodeUpdated, + /// A new account was created. + NewAccount(AccountId), + /// An account was reaped. + ReapedAccount(AccountId), + } +); + +decl_error! { + /// Error for the System module + pub enum Error for Module { + /// The name of specification does not match between the current runtime + /// and the new runtime. + InvalidSpecName, + /// The specification version is not allowed to decrease between the current runtime + /// and the new runtime. + SpecVersionNotAllowedToDecrease, + /// The implementation version is not allowed to decrease between the current runtime + /// and the new runtime. + ImplVersionNotAllowedToDecrease, + /// The specification or the implementation version need to increase between the + /// current runtime and the new runtime. + SpecOrImplVersionNeedToIncrease, + /// Failed to extract the runtime version from the new runtime. + /// + /// Either calling `Core_version` or decoding `RuntimeVersion` failed. + FailedToExtractRuntimeVersion, + } +} + +decl_module! { + pub struct Module for enum Call where origin: T::Origin { + type Error = Error; + + /// A big dispatch that will disallow any other transaction to be included. + // TODO: This should only be available for testing, rather than in general usage, but + // that's not possible at present (since it's within the decl_module macro). + #[weight = SimpleDispatchInfo::MaxOperational] + fn fill_block(origin) { + ensure_root(origin)?; + } + + /// Make some on-chain remark. + #[weight = SimpleDispatchInfo::FixedNormal(10_000)] + fn remark(origin, _remark: Vec) { + ensure_signed(origin)?; + } + + /// Set the number of pages in the WebAssembly environment's heap. + #[weight = SimpleDispatchInfo::FixedOperational(10_000)] + fn set_heap_pages(origin, pages: u64) { + ensure_root(origin)?; + storage::unhashed::put_raw(well_known_keys::HEAP_PAGES, &pages.encode()); + } + + /// Set the new runtime code. + #[weight = SimpleDispatchInfo::FixedOperational(200_000)] + pub fn set_code(origin, code: Vec) { + ensure_root(origin)?; + + let current_version = T::Version::get(); + let new_version = sp_io::misc::runtime_version(&code) + .and_then(|v| RuntimeVersion::decode(&mut &v[..]).ok()) + .ok_or_else(|| Error::::FailedToExtractRuntimeVersion)?; + + if new_version.spec_name != current_version.spec_name { + Err(Error::::InvalidSpecName)? + } + + if new_version.spec_version < current_version.spec_version { + Err(Error::::SpecVersionNotAllowedToDecrease)? + } else if new_version.spec_version == current_version.spec_version { + if new_version.impl_version < current_version.impl_version { + Err(Error::::ImplVersionNotAllowedToDecrease)? + } else if new_version.impl_version == current_version.impl_version { + Err(Error::::SpecOrImplVersionNeedToIncrease)? + } + } + + storage::unhashed::put_raw(well_known_keys::CODE, &code); + Self::deposit_event(RawEvent::CodeUpdated); + } + + /// Set the new runtime code without doing any checks of the given `code`. + #[weight = SimpleDispatchInfo::FixedOperational(200_000)] + pub fn set_code_without_checks(origin, code: Vec) { + ensure_root(origin)?; + storage::unhashed::put_raw(well_known_keys::CODE, &code); + Self::deposit_event(RawEvent::CodeUpdated); + } + + /// Set the new changes trie configuration. + #[weight = SimpleDispatchInfo::FixedOperational(20_000)] + pub fn set_changes_trie_config(origin, changes_trie_config: Option) { + ensure_root(origin)?; + match changes_trie_config.clone() { + Some(changes_trie_config) => storage::unhashed::put_raw( + well_known_keys::CHANGES_TRIE_CONFIG, + &changes_trie_config.encode(), + ), + None => storage::unhashed::kill(well_known_keys::CHANGES_TRIE_CONFIG), + } + + let log = generic::DigestItem::ChangesTrieSignal( + generic::ChangesTrieSignal::NewConfiguration(changes_trie_config), + ); + Self::deposit_log(log.into()); + } + + /// Set some items of storage. + #[weight = SimpleDispatchInfo::FixedOperational(10_000)] + fn set_storage(origin, items: Vec) { + ensure_root(origin)?; + for i in &items { + storage::unhashed::put_raw(&i.0, &i.1); + } + } + + /// Kill some items from storage. + #[weight = SimpleDispatchInfo::FixedOperational(10_000)] + fn kill_storage(origin, keys: Vec) { + ensure_root(origin)?; + for key in &keys { + storage::unhashed::kill(&key); + } + } + + /// Kill all storage items with a key that starts with the given prefix. + #[weight = SimpleDispatchInfo::FixedOperational(10_000)] + fn kill_prefix(origin, prefix: Key) { + ensure_root(origin)?; + storage::unhashed::kill_prefix(&prefix); + } + } +} + pub struct EnsureRoot(sp_std::marker::PhantomData); impl< O: Into, O>> + From>, @@ -622,7 +639,7 @@ impl Module { Self::deposit_event_indexed(&[], event.into()); } - /// Deposits an event into this block's event record adding this event + /// Deposits an event into this block's event record adding this event /// to the corresponding topic indexes. /// /// This will update storage entries that correspond to the specified topics. @@ -832,9 +849,14 @@ impl Module { /// Return the chain's current runtime version. pub fn runtime_version() -> RuntimeVersion { T::Version::get() } + /// Retrieve the account transaction counter from storage. + pub fn account_nonce(who: impl EncodeLike) -> T::Index { + Account::::get(who).0 + } + /// Increment a particular account's nonce by 1. - pub fn inc_account_nonce(who: &T::AccountId) { - >::insert(who, Self::account_nonce(who) + T::Index::one()); + pub fn inc_account_nonce(who: impl EncodeLike) { + Account::::mutate(who, |a| a.0 += T::Index::one()); } /// Note what the extrinsic data of the current extrinsic index is. If this @@ -851,10 +873,10 @@ impl Module { pub fn note_applied_extrinsic(r: &DispatchOutcome, _encoded_len: u32, info: DispatchInfo) { Self::deposit_event( match r { - Ok(()) => Event::ExtrinsicSuccess(info), + Ok(()) => RawEvent::ExtrinsicSuccess(info), Err(err) => { sp_runtime::print(err); - Event::ExtrinsicFailed(err.clone(), info) + RawEvent::ExtrinsicFailed(err.clone(), info) }, } ); @@ -879,12 +901,118 @@ impl Module { let xts_root = extrinsics_data_root::(extrinsics); >::put(xts_root); } + + /// An account is being created. + pub fn on_created_account(who: T::AccountId) { + T::OnNewAccount::on_new_account(&who); + Self::deposit_event(RawEvent::NewAccount(who)); + } + + /// Kill the account and reap any related information. + pub fn kill_account(who: T::AccountId) { + if Account::::contains_key(&who) { + Account::::remove(&who); + Self::on_killed_account(who); + } + } + + /// Do anything that needs to be done after an account has been killed. + fn on_killed_account(who: T::AccountId) { + T::OnReapAccount::on_reap_account(&who); + Self::deposit_event(RawEvent::ReapedAccount(who)); + } } -impl OnReapAccount for Module { - /// Remove the nonce for the account. Account is considered fully removed from the system. - fn on_reap_account(who: &T::AccountId) { - >::remove(who); +/// Event handler which calls on_created_account when it happens. +pub struct CallOnCreatedAccount(PhantomData); +impl Happened for CallOnCreatedAccount { + fn happened(who: &T::AccountId) { + Module::::on_created_account(who.clone()); + } +} + +/// Event handler which calls kill_account when it happens. +pub struct CallKillAccount(PhantomData); +impl Happened for CallKillAccount { + fn happened(who: &T::AccountId) { + Module::::kill_account(who.clone()); + } +} + +// Implement StoredMap for a simple single-item, kill-account-on-remove system. This works fine for +// storing a single item which is required to not be empty/default for the account to exist. +// Anything more complex will need more sophisticated logic. +impl StoredMap for Module { + fn get(k: &T::AccountId) -> T::AccountData { + Account::::get(k).1 + } + fn is_explicit(k: &T::AccountId) -> bool { + Account::::contains_key(k) + } + fn insert(k: &T::AccountId, t: T::AccountData) { + let existed = Account::::contains_key(k); + Account::::insert(k, (T::Index::default(), t)); + if !existed { + Self::on_created_account(k.clone()); + } + } + fn remove(k: &T::AccountId) { + if Account::::contains_key(&k) { + Self::kill_account(k.clone()); + } + } + fn mutate(k: &T::AccountId, f: impl FnOnce(&mut T::AccountData) -> R) -> R { + let existed = Account::::contains_key(k); + let r = Account::::mutate(k, |a| f(&mut a.1)); + if !existed { + Self::on_created_account(k.clone()); + } + r + } + fn mutate_exists(k: &T::AccountId, f: impl FnOnce(&mut Option) -> R) -> R { + let (existed, exists, r) = Account::::mutate_exists(k, |maybe_value| { + let existed = maybe_value.is_some(); + let (maybe_nonce, mut maybe_extra) = split_inner(maybe_value.take(), |v| v); + let r = f(&mut maybe_extra); + *maybe_value = maybe_extra.map(|extra| (maybe_nonce.unwrap_or_default(), extra)); + (existed, maybe_value.is_some(), r) + }); + if !existed && exists { + Self::on_created_account(k.clone()); + } else if existed && !exists { + Self::on_killed_account(k.clone()); + } + r + } + fn try_mutate_exists(k: &T::AccountId, f: impl FnOnce(&mut Option) -> Result) -> Result { + Account::::try_mutate_exists(k, |maybe_value| { + let existed = maybe_value.is_some(); + let (maybe_nonce, mut maybe_extra) = split_inner(maybe_value.take(), |v| v); + f(&mut maybe_extra).map(|v| { + *maybe_value = maybe_extra.map(|extra| (maybe_nonce.unwrap_or_default(), extra)); + (existed, maybe_value.is_some(), v) + }) + }).map(|(existed, exists, v)| { + if !existed && exists { + Self::on_created_account(k.clone()); + } else if existed && !exists { + Self::on_killed_account(k.clone()); + } + v + }) + } +} + +/// Split an `option` into two constituent options, as defined by a `splitter` function. +pub fn split_inner(option: Option, splitter: impl FnOnce(T) -> (R, S)) + -> (Option, Option) +{ + match option { + Some(inner) => { + let (r, s) = splitter(inner); + (Some(r), Some(s)) + } + None => (None, None), } } @@ -1052,7 +1180,7 @@ impl SignedExtension for CheckNonce { _info: Self::DispatchInfo, _len: usize, ) -> Result<(), TransactionValidityError> { - let expected = >::get(who); + let (expected, extra) = Account::::get(who); if self.0 != expected { return Err( if self.0 < expected { @@ -1062,8 +1190,7 @@ impl SignedExtension for CheckNonce { }.into() ) } - - >::insert(who, expected + T::Index::one()); + Account::::insert(who, (expected + T::Index::one(), extra)); Ok(()) } @@ -1075,7 +1202,7 @@ impl SignedExtension for CheckNonce { _len: usize, ) -> TransactionValidity { // check index - let expected = >::get(who); + let (expected, _extra) = Account::::get(who); if self.0 < expected { return InvalidTransaction::Stale.into() } @@ -1097,6 +1224,12 @@ impl SignedExtension for CheckNonce { } } +impl IsDeadAccount for Module { + fn is_dead_account(who: &T::AccountId) -> bool { + !Account::::contains_key(who) + } +} + /// Check for transaction mortality. #[derive(Encode, Decode, Clone, Eq, PartialEq)] pub struct CheckEra((Era, sp_std::marker::PhantomData)); @@ -1288,14 +1421,18 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = Version; type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } - impl From for u16 { - fn from(e: Event) -> u16 { + impl From> for u16 { + fn from(e: Event) -> u16 { match e { - Event::ExtrinsicSuccess(..) => 100, - Event::ExtrinsicFailed(..) => 101, - Event::CodeUpdated => 102, + Event::::ExtrinsicSuccess(..) => 100, + Event::::ExtrinsicFailed(..) => 101, + Event::::CodeUpdated => 102, + _ => 103, } } } @@ -1475,7 +1612,7 @@ mod tests { #[test] fn signed_ext_check_nonce_works() { new_test_ext().execute_with(|| { - >::insert(1, 1); + Account::::insert(1, (1, ())); let info = DispatchInfo::default(); let len = 0_usize; // stale diff --git a/substrate/frame/system/src/offchain.rs b/substrate/frame/system/src/offchain.rs index f5fda34585..b26f4be4b4 100644 --- a/substrate/frame/system/src/offchain.rs +++ b/substrate/frame/system/src/offchain.rs @@ -20,8 +20,8 @@ use codec::Encode; use sp_std::convert::TryInto; use sp_std::prelude::Vec; use sp_runtime::app_crypto::{RuntimeAppPublic, AppPublic, AppSignature}; -use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount}; -use frame_support::debug; +use sp_runtime::traits::{Extrinsic as ExtrinsicT, IdentifyAccount, One}; +use frame_support::{debug, storage::StorageMap}; /// Creates runtime-specific signed transaction. /// @@ -128,19 +128,19 @@ pub trait SignAndSubmitTransaction { fn sign_and_submit(call: impl Into, public: PublicOf) -> Result<(), ()> { let call = call.into(); let id = public.clone().into_account(); - let expected = >::account_nonce(&id); + let (expected_nonce, extra) = super::Account::::get(&id); debug::native::debug!( target: "offchain", "Creating signed transaction from account: {:?} (nonce: {:?})", id, - expected, + expected_nonce, ); let (call, signature_data) = Self::CreateTransaction - ::create_transaction::(call, public, id.clone(), expected) + ::create_transaction::(call, public, id.clone(), expected_nonce) .ok_or(())?; // increment the nonce. This is fine, since the code should always // be running in off-chain context, so we NEVER persists data. - >::inc_account_nonce(&id); + super::Account::::insert(&id, (expected_nonce + One::one(), extra)); let xt = Self::Extrinsic::new(call, Some(signature_data)).ok_or(())?; sp_io::offchain::submit_transaction(xt.encode()) diff --git a/substrate/frame/timestamp/src/lib.rs b/substrate/frame/timestamp/src/lib.rs index 6a4cdf92fa..95e1fc3a5e 100644 --- a/substrate/frame/timestamp/src/lib.rs +++ b/substrate/frame/timestamp/src/lib.rs @@ -276,6 +276,9 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } parameter_types! { pub const MinimumPeriod: u64 = 5; diff --git a/substrate/frame/transaction-payment/src/lib.rs b/substrate/frame/transaction-payment/src/lib.rs index 2b9637651d..0d9cfc0b77 100644 --- a/substrate/frame/transaction-payment/src/lib.rs +++ b/substrate/frame/transaction-payment/src/lib.rs @@ -302,25 +302,23 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { - pub const CreationFee: u64 = 0; pub const ExistentialDeposit: u64 = 1; } impl pallet_balances::Trait for Runtime { type Balance = u64; - type OnReapAccount = System; - type OnNewAccount = (); type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = System; } - - thread_local! { +thread_local! { static TRANSACTION_BASE_FEE: RefCell = RefCell::new(0); static TRANSACTION_BYTE_FEE: RefCell = RefCell::new(1); static WEIGHT_TO_FEE: RefCell = RefCell::new(1); diff --git a/substrate/frame/treasury/src/lib.rs b/substrate/frame/treasury/src/lib.rs index 44ce575f17..36f92c401e 100644 --- a/substrate/frame/treasury/src/lib.rs +++ b/substrate/frame/treasury/src/lib.rs @@ -758,20 +758,19 @@ mod tests { type MaximumBlockLength = MaximumBlockLength; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { pub const ExistentialDeposit: u64 = 1; - pub const CreationFee: u64 = 0; - } +} impl pallet_balances::Trait for Test { type Balance = u64; - type OnNewAccount = (); - type OnReapAccount = System; type Event = (); - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = System; } pub struct TenToFourteen; impl Contains for TenToFourteen { diff --git a/substrate/frame/utility/src/lib.rs b/substrate/frame/utility/src/lib.rs index 72f35745a4..414651659c 100644 --- a/substrate/frame/utility/src/lib.rs +++ b/substrate/frame/utility/src/lib.rs @@ -661,6 +661,7 @@ mod tests { impl_outer_event! { pub enum TestEvent for Test { + system, pallet_balances, utility, } @@ -700,20 +701,19 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } parameter_types! { pub const ExistentialDeposit: u64 = 1; - pub const CreationFee: u64 = 0; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnReapAccount = System; - type OnNewAccount = (); type Event = TestEvent; - type TransferPayment = (); type DustRemoval = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = System; } parameter_types! { pub const MultisigDepositBase: u64 = 1; diff --git a/substrate/frame/vesting/src/lib.rs b/substrate/frame/vesting/src/lib.rs index e39e2ae3a5..3777475f3f 100644 --- a/substrate/frame/vesting/src/lib.rs +++ b/substrate/frame/vesting/src/lib.rs @@ -50,7 +50,7 @@ use sp_std::prelude::*; use sp_std::fmt::Debug; use codec::{Encode, Decode}; use sp_runtime::{DispatchResult, RuntimeDebug, traits::{ - StaticLookup, Zero, AtLeast32Bit, MaybeSerializeDeserialize, Saturating, Convert + StaticLookup, Zero, AtLeast32Bit, MaybeSerializeDeserialize, Convert }}; use frame_support::{decl_module, decl_event, decl_storage, decl_error}; use frame_support::traits::{ @@ -115,6 +115,7 @@ decl_storage! { add_extra_genesis { config(vesting): Vec<(T::AccountId, T::BlockNumber, T::BlockNumber, BalanceOf)>; build(|config: &GenesisConfig| { + use sp_runtime::traits::Saturating; // Generate initial vesting configuration // * who - Account which we are generating vesting configuration for // * begin - Block when the account will start to vest @@ -336,19 +337,16 @@ mod tests { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); - } - parameter_types! { - pub const CreationFee: u64 = 0; + type AccountData = pallet_balances::AccountData; + type OnNewAccount = (); + type OnReapAccount = Balances; } impl pallet_balances::Trait for Test { type Balance = u64; - type OnReapAccount = System; - type OnNewAccount = (); - type Event = (); - type TransferPayment = (); type DustRemoval = (); + type Event = (); type ExistentialDeposit = ExistentialDeposit; - type CreationFee = CreationFee; + type AccountStore = System; } impl Trait for Test { type Event = (); diff --git a/substrate/test-utils/runtime/src/lib.rs b/substrate/test-utils/runtime/src/lib.rs index dac232b25c..c8dff15ef2 100644 --- a/substrate/test-utils/runtime/src/lib.rs +++ b/substrate/test-utils/runtime/src/lib.rs @@ -340,8 +340,8 @@ impl_outer_origin!{ #[derive(Clone, Encode, Decode, Eq, PartialEq, RuntimeDebug)] pub struct Event; -impl From for Event { - fn from(_evt: frame_system::Event) -> Self { +impl From> for Event { + fn from(_evt: frame_system::Event) -> Self { unimplemented!("Not required in tests!") } } @@ -371,6 +371,9 @@ impl frame_system::Trait for Runtime { type AvailableBlockRatio = AvailableBlockRatio; type Version = (); type ModuleToIndex = (); + type AccountData = (); + type OnNewAccount = (); + type OnReapAccount = (); } impl pallet_timestamp::Trait for Runtime {