Skip to content
This repository was archived by the owner on Oct 3, 2025. It is now read-only.

Commit 5afd5b8

Browse files
authored
contracts: Add test to verify unique trie ids (paritytech#10914)
* Add test to verify unique trie ids * Rename trie_seed to nonce * Rename AccountCounter -> Nonce * fmt
1 parent 1321c6f commit 5afd5b8

6 files changed

Lines changed: 162 additions & 56 deletions

File tree

frame/contracts/src/exec.rs

Lines changed: 34 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
use crate::{
1919
gas::GasMeter,
2020
storage::{self, Storage, WriteOutcome},
21-
AccountCounter, BalanceOf, CodeHash, Config, ContractInfo, ContractInfoOf, Error, Event,
21+
BalanceOf, CodeHash, Config, ContractInfo, ContractInfoOf, Error, Event, Nonce,
2222
Pallet as Contracts, Schedule,
2323
};
2424
use frame_support::{
@@ -315,9 +315,10 @@ pub struct Stack<'a, T: Config, E> {
315315
timestamp: MomentOf<T>,
316316
/// The block number at the time of call stack instantiation.
317317
block_number: T::BlockNumber,
318-
/// The account counter is cached here when accessed. It is written back when the call stack
319-
/// finishes executing.
320-
account_counter: Option<u64>,
318+
/// The nonce is cached here when accessed. It is written back when the call stack
319+
/// finishes executing. Please refer to [`Nonce`] to a description of
320+
/// the nonce itself.
321+
nonce: Option<u64>,
321322
/// The actual call stack. One entry per nested contract called/instantiated.
322323
/// This does **not** include the [`Self::first_frame`].
323324
frames: SmallVec<T::CallStack>,
@@ -385,8 +386,8 @@ enum FrameArgs<'a, T: Config, E> {
385386
Instantiate {
386387
/// The contract or signed origin which instantiates the new contract.
387388
sender: T::AccountId,
388-
/// The seed that should be used to derive a new trie id for the contract.
389-
trie_seed: u64,
389+
/// The nonce that should be used to derive a new trie id for the contract.
390+
nonce: u64,
390391
/// The executable whose `deploy` function is run.
391392
executable: E,
392393
/// A salt used in the contract address deriviation of the new contract.
@@ -571,7 +572,7 @@ where
571572
let (mut stack, executable) = Self::new(
572573
FrameArgs::Instantiate {
573574
sender: origin.clone(),
574-
trie_seed: Self::initial_trie_seed(),
575+
nonce: Self::initial_nonce(),
575576
executable,
576577
salt,
577578
},
@@ -596,7 +597,7 @@ where
596597
value: BalanceOf<T>,
597598
debug_message: Option<&'a mut Vec<u8>>,
598599
) -> Result<(Self, E), ExecError> {
599-
let (first_frame, executable, account_counter) =
600+
let (first_frame, executable, nonce) =
600601
Self::new_frame(args, value, gas_meter, storage_meter, 0, &schedule)?;
601602
let stack = Self {
602603
origin,
@@ -605,7 +606,7 @@ where
605606
storage_meter,
606607
timestamp: T::Time::now(),
607608
block_number: <frame_system::Pallet<T>>::block_number(),
608-
account_counter,
609+
nonce,
609610
first_frame,
610611
frames: Default::default(),
611612
debug_message,
@@ -627,7 +628,7 @@ where
627628
gas_limit: Weight,
628629
schedule: &Schedule<T>,
629630
) -> Result<(Frame<T>, E, Option<u64>), ExecError> {
630-
let (account_id, contract_info, executable, delegate_caller, entry_point, account_counter) =
631+
let (account_id, contract_info, executable, delegate_caller, entry_point, nonce) =
631632
match frame_args {
632633
FrameArgs::Call { dest, cached_info, delegated_call } => {
633634
let contract = if let Some(contract) = cached_info {
@@ -645,10 +646,10 @@ where
645646

646647
(dest, contract, executable, delegate_caller, ExportedFunction::Call, None)
647648
},
648-
FrameArgs::Instantiate { sender, trie_seed, executable, salt } => {
649+
FrameArgs::Instantiate { sender, nonce, executable, salt } => {
649650
let account_id =
650651
<Contracts<T>>::contract_address(&sender, executable.code_hash(), &salt);
651-
let trie_id = Storage::<T>::generate_trie_id(&account_id, trie_seed);
652+
let trie_id = Storage::<T>::generate_trie_id(&account_id, nonce);
652653
let contract = Storage::<T>::new_contract(
653654
&account_id,
654655
trie_id,
@@ -660,7 +661,7 @@ where
660661
executable,
661662
None,
662663
ExportedFunction::Constructor,
663-
Some(trie_seed),
664+
Some(nonce),
664665
)
665666
},
666667
};
@@ -676,7 +677,7 @@ where
676677
allows_reentry: true,
677678
};
678679

679-
Ok((frame, executable, account_counter))
680+
Ok((frame, executable, nonce))
680681
}
681682

682683
/// Create a subsequent nested frame.
@@ -782,9 +783,9 @@ where
782783
/// This is called after running the current frame. It commits cached values to storage
783784
/// and invalidates all stale references to it that might exist further down the call stack.
784785
fn pop_frame(&mut self, persist: bool) {
785-
// Revert the account counter in case of a failed instantiation.
786+
// Revert changes to the nonce in case of a failed instantiation.
786787
if !persist && self.top_frame().entry_point == ExportedFunction::Constructor {
787-
self.account_counter.as_mut().map(|c| *c = c.wrapping_sub(1));
788+
self.nonce.as_mut().map(|c| *c = c.wrapping_sub(1));
788789
}
789790

790791
// Pop the current frame from the stack and return it in case it needs to interact
@@ -861,8 +862,8 @@ where
861862
if let Some(contract) = contract {
862863
<ContractInfoOf<T>>::insert(&self.first_frame.account_id, contract);
863864
}
864-
if let Some(counter) = self.account_counter {
865-
<AccountCounter<T>>::set(counter);
865+
if let Some(nonce) = self.nonce {
866+
<Nonce<T>>::set(nonce);
866867
}
867868
}
868869
}
@@ -920,20 +921,20 @@ where
920921
!self.frames().any(|f| &f.account_id == id && !f.allows_reentry)
921922
}
922923

923-
/// Increments the cached account id and returns the value to be used for the trie_id.
924-
fn next_trie_seed(&mut self) -> u64 {
925-
let next = if let Some(current) = self.account_counter {
924+
/// Increments and returns the next nonce. Pulls it from storage if it isn't in cache.
925+
fn next_nonce(&mut self) -> u64 {
926+
let next = if let Some(current) = self.nonce {
926927
current.wrapping_add(1)
927928
} else {
928-
Self::initial_trie_seed()
929+
Self::initial_nonce()
929930
};
930-
self.account_counter = Some(next);
931+
self.nonce = Some(next);
931932
next
932933
}
933934

934-
/// The account seed to be used to instantiate the account counter cache.
935-
fn initial_trie_seed() -> u64 {
936-
<AccountCounter<T>>::get().wrapping_add(1)
935+
/// Pull the current nonce from storage.
936+
fn initial_nonce() -> u64 {
937+
<Nonce<T>>::get().wrapping_add(1)
937938
}
938939
}
939940

@@ -1020,11 +1021,11 @@ where
10201021
salt: &[u8],
10211022
) -> Result<(AccountIdOf<T>, ExecReturnValue), ExecError> {
10221023
let executable = E::from_storage(code_hash, &self.schedule, self.gas_meter())?;
1023-
let trie_seed = self.next_trie_seed();
1024+
let nonce = self.next_nonce();
10241025
let executable = self.push_frame(
10251026
FrameArgs::Instantiate {
10261027
sender: self.top_frame().account_id.clone(),
1027-
trie_seed,
1028+
nonce,
10281029
executable,
10291030
salt,
10301031
},
@@ -2445,7 +2446,7 @@ mod tests {
24452446
}
24462447

24472448
#[test]
2448-
fn account_counter() {
2449+
fn nonce() {
24492450
let fail_code = MockLoader::insert(Constructor, |_, _| exec_trapped());
24502451
let success_code = MockLoader::insert(Constructor, |_, _| exec_success());
24512452
let succ_fail_code = MockLoader::insert(Constructor, move |ctx, _| {
@@ -2495,7 +2496,7 @@ mod tests {
24952496
None,
24962497
)
24972498
.ok();
2498-
assert_eq!(<AccountCounter<Test>>::get(), 0);
2499+
assert_eq!(<Nonce<Test>>::get(), 0);
24992500

25002501
assert_ok!(MockStack::run_instantiate(
25012502
ALICE,
@@ -2508,7 +2509,7 @@ mod tests {
25082509
&[],
25092510
None,
25102511
));
2511-
assert_eq!(<AccountCounter<Test>>::get(), 1);
2512+
assert_eq!(<Nonce<Test>>::get(), 1);
25122513

25132514
assert_ok!(MockStack::run_instantiate(
25142515
ALICE,
@@ -2521,7 +2522,7 @@ mod tests {
25212522
&[],
25222523
None,
25232524
));
2524-
assert_eq!(<AccountCounter<Test>>::get(), 2);
2525+
assert_eq!(<Nonce<Test>>::get(), 2);
25252526

25262527
assert_ok!(MockStack::run_instantiate(
25272528
ALICE,
@@ -2534,7 +2535,7 @@ mod tests {
25342535
&[],
25352536
None,
25362537
));
2537-
assert_eq!(<AccountCounter<Test>>::get(), 4);
2538+
assert_eq!(<Nonce<Test>>::get(), 4);
25382539
});
25392540
}
25402541

frame/contracts/src/lib.rs

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ type BalanceOf<T> =
134134
<<T as Config>::Currency as Currency<<T as frame_system::Config>::AccountId>>::Balance;
135135

136136
/// The current storage version.
137-
const STORAGE_VERSION: StorageVersion = StorageVersion::new(6);
137+
const STORAGE_VERSION: StorageVersion = StorageVersion::new(7);
138138

139139
/// Used as a sentinel value when reading and writing contract memory.
140140
///
@@ -665,9 +665,30 @@ pub mod pallet {
665665
#[pallet::storage]
666666
pub(crate) type OwnerInfoOf<T: Config> = StorageMap<_, Identity, CodeHash<T>, OwnerInfo<T>>;
667667

668-
/// The subtrie counter.
668+
/// This is a **monotonic** counter incremented on contract instantiation.
669+
///
670+
/// This is used in order to generate unique trie ids for contracts.
671+
/// The trie id of a new contract is calculated from hash(account_id, nonce).
672+
/// The nonce is required because otherwise the following sequence would lead to
673+
/// a possible collision of storage:
674+
///
675+
/// 1. Create a new contract.
676+
/// 2. Terminate the contract.
677+
/// 3. Immediately recreate the contract with the same account_id.
678+
///
679+
/// This is bad because the contents of a trie are deleted lazily and there might be
680+
/// storage of the old instantiation still in it when the new contract is created. Please
681+
/// note that we can't replace the counter by the block number because the sequence above
682+
/// can happen in the same block. We also can't keep the account counter in memory only
683+
/// because storage is the only way to communicate across different extrinsics in the
684+
/// same block.
685+
///
686+
/// # Note
687+
///
688+
/// Do not use it to determine the number of contracts. It won't be decremented if
689+
/// a contract is destroyed.
669690
#[pallet::storage]
670-
pub(crate) type AccountCounter<T: Config> = StorageValue<_, u64, ValueQuery>;
691+
pub(crate) type Nonce<T: Config> = StorageValue<_, u64, ValueQuery>;
671692

672693
/// The code associated with a given account.
673694
///

frame/contracts/src/migration.rs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use crate::{BalanceOf, CodeHash, Config, Pallet, TrieId, Weight};
1919
use codec::{Decode, Encode};
2020
use frame_support::{
2121
codec, generate_storage_alias,
22+
pallet_prelude::*,
2223
storage::migration,
2324
traits::{Get, PalletInfoAccess},
2425
Identity, Twox64Concat,
@@ -47,6 +48,11 @@ pub fn migrate<T: Config>() -> Weight {
4748
StorageVersion::new(6).put::<Pallet<T>>();
4849
}
4950

51+
if version < 7 {
52+
weight = weight.saturating_add(v7::migrate::<T>());
53+
StorageVersion::new(7).put::<Pallet<T>>();
54+
}
55+
5056
weight
5157
}
5258

@@ -249,3 +255,21 @@ mod v6 {
249255
weight
250256
}
251257
}
258+
259+
/// Rename `AccountCounter` to `Nonce`.
260+
mod v7 {
261+
use super::*;
262+
263+
pub fn migrate<T: Config>() -> Weight {
264+
generate_storage_alias!(
265+
Contracts,
266+
AccountCounter => Value<u64, ValueQuery>
267+
);
268+
generate_storage_alias!(
269+
Contracts,
270+
Nonce => Value<u64, ValueQuery>
271+
);
272+
Nonce::set(AccountCounter::take());
273+
T::DbWeight::get().reads_writes(1, 2)
274+
}
275+
}

frame/contracts/src/storage.rs

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -290,10 +290,9 @@ where
290290
weight_limit.saturating_sub(weight_per_key.saturating_mul(remaining_key_budget as Weight))
291291
}
292292

293-
/// This generator uses inner counter for account id and applies the hash over `AccountId +
294-
/// accountid_counter`.
295-
pub fn generate_trie_id(account_id: &AccountIdOf<T>, seed: u64) -> TrieId {
296-
let buf: Vec<_> = account_id.as_ref().iter().chain(&seed.to_le_bytes()).cloned().collect();
293+
/// Generates a unique trie id by returning `hash(account_id ++ nonce)`.
294+
pub fn generate_trie_id(account_id: &AccountIdOf<T>, nonce: u64) -> TrieId {
295+
let buf: Vec<_> = account_id.as_ref().iter().chain(&nonce.to_le_bytes()).cloned().collect();
297296
T::Hashing::hash(&buf).as_ref().into()
298297
}
299298

0 commit comments

Comments
 (0)