Skip to content

Commit be93b9a

Browse files
authored
Merge pull request #338 from opentensor/fix/account-migration
fix: account migration
2 parents 894f4c2 + 86b0a85 commit be93b9a

File tree

3 files changed

+164
-115
lines changed

3 files changed

+164
-115
lines changed

runtime/src/lib.rs

Lines changed: 3 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,11 @@
66
#[cfg(feature = "std")]
77
include!(concat!(env!("OUT_DIR"), "/wasm_binary.rs"));
88

9+
mod migrations;
10+
911
use codec::{Decode, Encode, MaxEncodedLen};
1012

13+
use migrations::account_data_migration;
1114
use pallet_commitments::CanCommit;
1215
use pallet_grandpa::{
1316
fg_primitives, AuthorityId as GrandpaId, AuthorityList as GrandpaAuthorityList,
@@ -1147,121 +1150,6 @@ pub type SignedExtra = (
11471150
pallet_commitments::CommitmentsSignedExtension<Runtime>,
11481151
);
11491152

1150-
mod account_data_migration {
1151-
use super::*;
1152-
use frame_support::log;
1153-
use pallet_balances::ExtraFlags;
1154-
1155-
mod prev {
1156-
use super::*;
1157-
use frame_support::{pallet_prelude::ValueQuery, storage_alias, Blake2_128Concat};
1158-
1159-
#[derive(Encode, Decode, Clone, PartialEq, Eq, Default, Debug)]
1160-
pub struct AccountData<Balance> {
1161-
pub free: Balance,
1162-
pub reserved: Balance,
1163-
pub misc_frozen: Balance,
1164-
pub fee_frozen: Balance,
1165-
}
1166-
1167-
#[storage_alias]
1168-
pub type Account<T: frame_system::pallet::Config> = StorageMap<
1169-
frame_system::pallet::Pallet<T>,
1170-
Blake2_128Concat,
1171-
AccountId,
1172-
AccountData<Balance>,
1173-
ValueQuery,
1174-
>;
1175-
}
1176-
1177-
const TARGET: &'static str = "runtime::account_data_migration";
1178-
pub struct Migration;
1179-
impl OnRuntimeUpgrade for Migration {
1180-
/// Save pre-upgrade account ids to check are decodable post-upgrade.
1181-
#[cfg(feature = "try-runtime")]
1182-
fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
1183-
let account_ids = prev::Account::<Runtime>::iter_keys().collect::<Vec<_>>();
1184-
log::info!(target: TARGET, "pre-upgrade");
1185-
1186-
Ok(account_ids.encode())
1187-
}
1188-
1189-
/// Ensures post-upgrade that
1190-
/// 1. Number of accounts has not changed.
1191-
/// 2. Each account exists in storage and decodes.
1192-
/// 3. Each account has a provider val >0.
1193-
#[cfg(feature = "try-runtime")]
1194-
fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
1195-
use frame_support::ensure;
1196-
log::info!(target: TARGET, "Running post-upgrade...");
1197-
1198-
let pre_upgrade_account_ids: Vec<<Runtime as frame_system::Config>::AccountId> =
1199-
Decode::decode(&mut &state[..]).expect("pre_upgrade provides a valid state; qed");
1200-
1201-
// Ensure number of accounts has not changed.
1202-
let account_ids = prev::Account::<Runtime>::iter_keys().collect::<Vec<_>>();
1203-
ensure!(
1204-
pre_upgrade_account_ids.len() == account_ids.len(),
1205-
"number of accounts has changed"
1206-
);
1207-
1208-
for acc in account_ids {
1209-
// Ensure account exists in storage and decodes.
1210-
match frame_system::pallet::Account::<Runtime>::try_get(&acc) {
1211-
Ok(d) => {
1212-
ensure!(d.data.free > 0 || d.data.reserved > 0, "account has 0 bal");
1213-
}
1214-
_ => {
1215-
panic!("account not found")
1216-
}
1217-
};
1218-
1219-
// Ensure account provider is >0.
1220-
ensure!(
1221-
frame_system::Pallet::<Runtime>::providers(&acc) > 0,
1222-
"provider == 0"
1223-
);
1224-
}
1225-
1226-
log::info!(target: TARGET, "post-upgrade success ✅");
1227-
Ok(())
1228-
}
1229-
1230-
/// Migrates AccountData storage to the new format, bumping providers where required.
1231-
fn on_runtime_upgrade() -> Weight {
1232-
// Pull the storage in the previous format into memory
1233-
let accounts = prev::Account::<Runtime>::iter().collect::<Vec<_>>();
1234-
log::info!(target: TARGET, "Migrating {} accounts...", accounts.len());
1235-
1236-
for (acc, data) in accounts.clone().into_iter() {
1237-
// Move account to new data format
1238-
let new_data = pallet_balances::AccountData {
1239-
free: data.free,
1240-
reserved: data.reserved,
1241-
frozen: data.misc_frozen.saturating_add(data.fee_frozen),
1242-
flags: ExtraFlags::old_logic(),
1243-
};
1244-
frame_system::pallet::Account::<Runtime>::mutate(acc.clone(), |a| {
1245-
a.data = new_data;
1246-
});
1247-
1248-
// Ensure provider
1249-
if frame_system::Pallet::<Runtime>::providers(&acc) == 0 {
1250-
frame_system::Pallet::<Runtime>::inc_providers(&acc);
1251-
}
1252-
1253-
// Ensure upgraded
1254-
pallet_balances::Pallet::<Runtime, ()>::ensure_upgraded(&acc);
1255-
}
1256-
1257-
log::info!(target: TARGET, "Migrated {} accounts ✅", accounts.len());
1258-
1259-
// R/W not important for solo chain.
1260-
return <Runtime as frame_system::Config>::DbWeight::get().reads_writes(0u64, 0u64);
1261-
}
1262-
}
1263-
}
1264-
12651153
type Migrations = account_data_migration::Migration;
12661154

12671155
// Unchecked extrinsic type as expected by this runtime.
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
use crate::*;
2+
use frame_support::log;
3+
use pallet_balances::ExtraFlags;
4+
5+
mod prev {
6+
use super::*;
7+
use frame_support::{pallet_prelude::ValueQuery, storage_alias, Blake2_128Concat};
8+
9+
#[derive(
10+
Clone, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen,
11+
)]
12+
pub struct AccountDataStruct<Balance> {
13+
pub free: Balance,
14+
pub reserved: Balance,
15+
pub misc_frozen: Balance,
16+
pub fee_frozen: Balance,
17+
}
18+
19+
#[derive(
20+
Clone, Eq, PartialEq, Default, RuntimeDebug, Encode, Decode, TypeInfo, MaxEncodedLen,
21+
)]
22+
pub struct AccountStruct<Balance> {
23+
pub nonce: u32,
24+
pub consumers: u32,
25+
pub providers: u32,
26+
pub sufficients: u32,
27+
pub data: AccountDataStruct<Balance>,
28+
}
29+
30+
#[storage_alias]
31+
pub type Account<T: frame_system::pallet::Config> = StorageMap<
32+
frame_system::pallet::Pallet<T>,
33+
Blake2_128Concat,
34+
AccountId,
35+
AccountStruct<Balance>,
36+
ValueQuery,
37+
>;
38+
}
39+
40+
const TARGET: &'static str = "runtime::account_data_migration";
41+
pub struct Migration;
42+
impl OnRuntimeUpgrade for Migration {
43+
/// Save pre-upgrade account ids to check are decodable post-upgrade.
44+
#[cfg(feature = "try-runtime")]
45+
fn pre_upgrade() -> Result<Vec<u8>, sp_runtime::TryRuntimeError> {
46+
use sp_std::collections::btree_map::BTreeMap;
47+
log::info!(target: TARGET, "pre-upgrade");
48+
49+
// Save the expected post-migration account state.
50+
let mut expected_account: BTreeMap<
51+
AccountId,
52+
frame_system::AccountInfo<u32, pallet_balances::AccountData<Balance>>,
53+
> = BTreeMap::new();
54+
55+
for (acc_id, acc) in prev::Account::<Runtime>::iter() {
56+
let expected_data = pallet_balances::AccountData {
57+
free: acc.data.free,
58+
reserved: acc.data.reserved,
59+
frozen: acc.data.misc_frozen.saturating_add(acc.data.fee_frozen),
60+
flags: ExtraFlags::default(),
61+
};
62+
63+
// `ensure_upgraded` bumps the consumers if there is a non zero reserved balance and no frozen balance.
64+
// https://github.com/paritytech/polkadot-sdk/blob/305d311d5c732fcc4629f3295768f1ed44ef434c/substrate/frame/balances/src/lib.rs#L785
65+
let expected_consumers = if acc.data.reserved > 0 && expected_data.frozen == 0 {
66+
acc.consumers + 1
67+
} else {
68+
acc.consumers
69+
};
70+
let expected_acc = frame_system::AccountInfo {
71+
nonce: acc.nonce,
72+
consumers: expected_consumers,
73+
providers: acc.providers,
74+
sufficients: acc.sufficients,
75+
data: expected_data,
76+
};
77+
expected_account.insert(acc_id, expected_acc);
78+
}
79+
80+
Ok(expected_account.encode())
81+
}
82+
83+
/// Migrates Account storage to the new format, and calls `ensure_upgraded` for them.
84+
fn on_runtime_upgrade() -> Weight {
85+
// Pull the storage in the previous format into memory
86+
let accounts = prev::Account::<Runtime>::iter().collect::<Vec<_>>();
87+
log::info!(target: TARGET, "Migrating {} accounts...", accounts.len());
88+
89+
for (acc_id, acc_info) in accounts.clone().into_iter() {
90+
let prev_data = acc_info.clone().data;
91+
92+
// Move account to new data format
93+
let new_data = pallet_balances::AccountData {
94+
free: prev_data.free,
95+
reserved: prev_data.reserved,
96+
frozen: prev_data.misc_frozen.saturating_add(prev_data.fee_frozen),
97+
flags: ExtraFlags::old_logic(),
98+
};
99+
let new_account = frame_system::AccountInfo {
100+
nonce: acc_info.nonce,
101+
consumers: acc_info.consumers,
102+
providers: acc_info.providers,
103+
sufficients: acc_info.sufficients,
104+
data: new_data,
105+
};
106+
frame_system::pallet::Account::<Runtime>::insert(acc_id.clone(), new_account);
107+
108+
// Ensure upgraded
109+
pallet_balances::Pallet::<Runtime, ()>::ensure_upgraded(&acc_id);
110+
}
111+
112+
log::info!(target: TARGET, "Migrated {} accounts ✅", accounts.len());
113+
114+
// R/W not important for solo chain.
115+
<Runtime as frame_system::Config>::DbWeight::get().reads_writes(0u64, 0u64)
116+
}
117+
118+
/// Ensures post-upgrade that every Account entry matches what is expected.
119+
#[cfg(feature = "try-runtime")]
120+
fn post_upgrade(state: Vec<u8>) -> Result<(), sp_runtime::TryRuntimeError> {
121+
use frame_support::ensure;
122+
use sp_std::collections::btree_map::BTreeMap;
123+
124+
log::info!(target: TARGET, "Running post-upgrade...");
125+
126+
let expected_accounts: BTreeMap<
127+
AccountId,
128+
frame_system::AccountInfo<u32, pallet_balances::AccountData<Balance>>,
129+
> = Decode::decode(&mut &state[..]).expect("decoding state failed");
130+
131+
// Ensure the actual post-migration state matches the expected
132+
for (acc_id, acc) in frame_system::pallet::Account::<Runtime>::iter() {
133+
let expected = expected_accounts.get(&acc_id).expect("account not found");
134+
135+
// New system logic nukes the account if no providers or sufficients.
136+
if acc.providers > 0 || acc.sufficients > 0 {
137+
ensure!(acc.nonce == expected.nonce, "nonce mismatch");
138+
ensure!(acc.consumers == expected.consumers, "consumers mismatch");
139+
ensure!(acc.providers == expected.providers, "providers mismatch");
140+
ensure!(
141+
acc.sufficients == expected.sufficients,
142+
"sufficients mismatch"
143+
);
144+
ensure!(acc.data.free == expected.data.free, "data.free mismatch");
145+
ensure!(
146+
acc.data.reserved == expected.data.reserved,
147+
"data.reserved mismatch"
148+
);
149+
ensure!(
150+
acc.data.frozen == expected.data.frozen,
151+
"data.frozen mismatch"
152+
);
153+
ensure!(acc.data.flags == expected.data.flags, "data.flags mismatch");
154+
}
155+
}
156+
157+
log::info!(target: TARGET, "post-upgrade success ✅");
158+
Ok(())
159+
}
160+
}

runtime/src/migrations/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub mod account_data_migration;

0 commit comments

Comments
 (0)