Skip to content

Commit 93d9c8c

Browse files
zdave-paritybkchr
andauthored
Make CheckNonce refuse transactions signed by accounts with no providers (#1578)
See #1453. Co-authored-by: Bastian Köcher <[email protected]>
1 parent 98286ad commit 93d9c8c

File tree

3 files changed

+62
-21
lines changed

3 files changed

+62
-21
lines changed

substrate/bin/node/executor/tests/submit_transaction.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ fn submitted_transaction_should_be_valid() {
239239
let author = extrinsic.signature.clone().unwrap().0;
240240
let address = Indices::lookup(author).unwrap();
241241
let data = pallet_balances::AccountData { free: 5_000_000_000_000, ..Default::default() };
242-
let account = frame_system::AccountInfo { data, ..Default::default() };
242+
let account = frame_system::AccountInfo { providers: 1, data, ..Default::default() };
243243
<frame_system::Account<Runtime>>::insert(&address, account);
244244

245245
// check validity

substrate/client/service/test/src/client/mod.rs

Lines changed: 8 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ use sp_runtime::{
3939
};
4040
use sp_state_machine::{backend::Backend as _, InMemoryBackend, OverlayedChanges, StateMachine};
4141
use sp_storage::{ChildInfo, StorageKey};
42-
use sp_trie::{LayoutV0, TrieConfiguration};
4342
use std::{collections::HashSet, sync::Arc};
4443
use substrate_test_runtime::TestAPI;
4544
use substrate_test_runtime_client::{
@@ -62,22 +61,17 @@ fn construct_block(
6261
backend: &InMemoryBackend<BlakeTwo256>,
6362
number: BlockNumber,
6463
parent_hash: Hash,
65-
state_root: Hash,
6664
txs: Vec<Transfer>,
67-
) -> (Vec<u8>, Hash) {
65+
) -> Vec<u8> {
6866
let transactions = txs.into_iter().map(|tx| tx.into_unchecked_extrinsic()).collect::<Vec<_>>();
6967

70-
let iter = transactions.iter().map(Encode::encode);
71-
let extrinsics_root = LayoutV0::<BlakeTwo256>::ordered_trie_root(iter).into();
72-
7368
let mut header = Header {
7469
parent_hash,
7570
number,
76-
state_root,
77-
extrinsics_root,
71+
state_root: Default::default(),
72+
extrinsics_root: Default::default(),
7873
digest: Digest { logs: vec![] },
7974
};
80-
let hash = header.hash();
8175
let mut overlay = OverlayedChanges::default();
8276
let backend_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(backend);
8377
let runtime_code = backend_runtime_code.runtime_code().expect("Code is part of the backend");
@@ -124,19 +118,16 @@ fn construct_block(
124118
.unwrap();
125119
header = Header::decode(&mut &ret_data[..]).unwrap();
126120

127-
(vec![].and(&Block { header, extrinsics: transactions }), hash)
121+
vec![].and(&Block { header, extrinsics: transactions })
128122
}
129123

130-
fn block1(genesis_hash: Hash, backend: &InMemoryBackend<BlakeTwo256>) -> (Vec<u8>, Hash) {
124+
fn block1(genesis_hash: Hash, backend: &InMemoryBackend<BlakeTwo256>) -> Vec<u8> {
131125
construct_block(
132126
backend,
133127
1,
134128
genesis_hash,
135-
array_bytes::hex_n_into_unchecked(
136-
"25e5b37074063ab75c889326246640729b40d0c86932edc527bc80db0e04fe5c",
137-
),
138129
vec![Transfer {
139-
from: AccountKeyring::Alice.into(),
130+
from: AccountKeyring::One.into(),
140131
to: AccountKeyring::Two.into(),
141132
amount: 69 * DOLLARS,
142133
nonce: 0,
@@ -175,7 +166,7 @@ fn construct_genesis_should_work_with_native() {
175166
let genesis_hash = insert_genesis_block(&mut storage);
176167

177168
let backend = InMemoryBackend::from((storage, StateVersion::default()));
178-
let (b1data, _b1hash) = block1(genesis_hash, &backend);
169+
let b1data = block1(genesis_hash, &backend);
179170
let backend_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&backend);
180171
let runtime_code = backend_runtime_code.runtime_code().expect("Code is part of the backend");
181172

@@ -206,7 +197,7 @@ fn construct_genesis_should_work_with_wasm() {
206197
let genesis_hash = insert_genesis_block(&mut storage);
207198

208199
let backend = InMemoryBackend::from((storage, StateVersion::default()));
209-
let (b1data, _b1hash) = block1(genesis_hash, &backend);
200+
let b1data = block1(genesis_hash, &backend);
210201
let backend_runtime_code = sp_state_machine::backend::BackendRuntimeCode::new(&backend);
211202
let runtime_code = backend_runtime_code.runtime_code().expect("Code is part of the backend");
212203

substrate/frame/system/src/extensions/check_nonce.rs

Lines changed: 53 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ use codec::{Decode, Encode};
2020
use frame_support::dispatch::DispatchInfo;
2121
use scale_info::TypeInfo;
2222
use sp_runtime::{
23-
traits::{DispatchInfoOf, Dispatchable, One, SignedExtension},
23+
traits::{DispatchInfoOf, Dispatchable, One, SignedExtension, Zero},
2424
transaction_validity::{
2525
InvalidTransaction, TransactionLongevity, TransactionValidity, TransactionValidityError,
2626
ValidTransaction,
@@ -80,6 +80,10 @@ where
8080
_len: usize,
8181
) -> Result<(), TransactionValidityError> {
8282
let mut account = crate::Account::<T>::get(who);
83+
if account.providers.is_zero() && account.sufficients.is_zero() {
84+
// Nonce storage not paid for
85+
return Err(InvalidTransaction::Payment.into())
86+
}
8387
if self.0 != account.nonce {
8488
return Err(if self.0 < account.nonce {
8589
InvalidTransaction::Stale
@@ -100,8 +104,11 @@ where
100104
_info: &DispatchInfoOf<Self::Call>,
101105
_len: usize,
102106
) -> TransactionValidity {
103-
// check index
104107
let account = crate::Account::<T>::get(who);
108+
if account.providers.is_zero() && account.sufficients.is_zero() {
109+
// Nonce storage not paid for
110+
return InvalidTransaction::Payment.into()
111+
}
105112
if self.0 < account.nonce {
106113
return InvalidTransaction::Stale.into()
107114
}
@@ -137,7 +144,7 @@ mod tests {
137144
crate::AccountInfo {
138145
nonce: 1,
139146
consumers: 0,
140-
providers: 0,
147+
providers: 1,
141148
sufficients: 0,
142149
data: 0,
143150
},
@@ -164,4 +171,47 @@ mod tests {
164171
);
165172
})
166173
}
174+
175+
#[test]
176+
fn signed_ext_check_nonce_requires_provider() {
177+
new_test_ext().execute_with(|| {
178+
crate::Account::<Test>::insert(
179+
2,
180+
crate::AccountInfo {
181+
nonce: 1,
182+
consumers: 0,
183+
providers: 1,
184+
sufficients: 0,
185+
data: 0,
186+
},
187+
);
188+
crate::Account::<Test>::insert(
189+
3,
190+
crate::AccountInfo {
191+
nonce: 1,
192+
consumers: 0,
193+
providers: 0,
194+
sufficients: 1,
195+
data: 0,
196+
},
197+
);
198+
let info = DispatchInfo::default();
199+
let len = 0_usize;
200+
// Both providers and sufficients zero
201+
assert_noop!(
202+
CheckNonce::<Test>(1).validate(&1, CALL, &info, len),
203+
InvalidTransaction::Payment
204+
);
205+
assert_noop!(
206+
CheckNonce::<Test>(1).pre_dispatch(&1, CALL, &info, len),
207+
InvalidTransaction::Payment
208+
);
209+
// Non-zero providers
210+
assert_ok!(CheckNonce::<Test>(1).validate(&2, CALL, &info, len));
211+
assert_ok!(CheckNonce::<Test>(1).pre_dispatch(&2, CALL, &info, len));
212+
// Non-zero sufficients
213+
assert_ok!(CheckNonce::<Test>(1).validate(&3, CALL, &info, len));
214+
assert_ok!(CheckNonce::<Test>(1).pre_dispatch(&3, CALL, &info, len));
215+
})
216+
}
167217
}

0 commit comments

Comments
 (0)