general_tests.rs 3.47 KiB
Newer Older
// This file is part of Substrate.

// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// 	http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#![cfg(test)]

use crate::{
	system::AccountInfo,
	tests::{ensure_ti_valid, Balances, ExtBuilder, System, Test, TestId, UseSystem},
	AccountData, ExtraFlags, TotalIssuance,
};
use frame_support::{
	assert_noop, assert_ok, hypothetically,
	traits::{
		fungible::{Mutate, MutateHold},
		tokens::Precision,
	},
};
use sp_runtime::DispatchError;

/// There are some accounts that have one consumer ref too few. These accounts are at risk of losing
/// their held (reserved) balance. They do not just lose it - it is also not accounted for in the
/// Total Issuance. Here we test the case that the account does not reap in such a case, but gets
/// one consumer ref for its reserved balance.
#[test]
fn regression_historic_acc_does_not_evaporate_reserve() {
	ExtBuilder::default().build_and_execute_with(|| {
		UseSystem::set(true);
		let (alice, bob) = (0, 1);
		// Alice is in a bad state with consumer == 0 && reserved > 0:
		Balances::set_balance(&alice, 100);
		TotalIssuance::<Test>::put(100);
		ensure_ti_valid();

		assert_ok!(Balances::hold(&TestId::Foo, &alice, 10));
		// This is the issue of the account:
		System::dec_consumers(&alice);

		assert_eq!(
			System::account(&alice),
			AccountInfo {
				data: AccountData {
					free: 90,
					reserved: 10,
					frozen: 0,
					flags: ExtraFlags(1u128 << 127),
				},
				nonce: 0,
				consumers: 0, // should be 1 on a good acc
				providers: 1,
				sufficients: 0,
			}
		);

		ensure_ti_valid();

		// Reaping the account is prevented by the new logic:
		assert_noop!(
			Balances::transfer_allow_death(Some(alice).into(), bob, 90),
			DispatchError::ConsumerRemaining
		);
		assert_noop!(
			Balances::transfer_all(Some(alice).into(), bob, false),
			DispatchError::ConsumerRemaining
		);

		// normal transfers still work:
		hypothetically!({
			assert_ok!(Balances::transfer_keep_alive(Some(alice).into(), bob, 40));
			// Alice got back her consumer ref:
			assert_eq!(System::consumers(&alice), 1);
			ensure_ti_valid();
		});
		hypothetically!({
			assert_ok!(Balances::transfer_all(Some(alice).into(), bob, true));
			// Alice got back her consumer ref:
			assert_eq!(System::consumers(&alice), 1);
			ensure_ti_valid();
		});

		// un-reserving all does not add a consumer ref:
		hypothetically!({
			assert_ok!(Balances::release(&TestId::Foo, &alice, 10, Precision::Exact));
			assert_eq!(System::consumers(&alice), 0);
			assert_ok!(Balances::transfer_keep_alive(Some(alice).into(), bob, 40));
			assert_eq!(System::consumers(&alice), 0);
			ensure_ti_valid();
		});
		// un-reserving some does add a consumer ref:
		hypothetically!({
			assert_ok!(Balances::release(&TestId::Foo, &alice, 5, Precision::Exact));
			assert_eq!(System::consumers(&alice), 1);
			assert_ok!(Balances::transfer_keep_alive(Some(alice).into(), bob, 40));
			assert_eq!(System::consumers(&alice), 1);
			ensure_ti_valid();
		});
	});
}