Skip to content

Commit c8cd63a

Browse files
committed
Limit recursion when checking for balance transfers or contract creation
1 parent 8302355 commit c8cd63a

File tree

2 files changed

+38
-12
lines changed

2 files changed

+38
-12
lines changed

Diff for: crates/subspace-runtime/src/signed_extensions.rs

+19-6
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ use sp_runtime::transaction_validity::{
77
};
88
use sp_std::prelude::*;
99

10+
const MAX_BALANCE_RECURSION_DEPTH: u16 = 5;
11+
1012
/// Disable balance transfers, if configured in the runtime.
1113
#[derive(Debug, Encode, Decode, Clone, Eq, PartialEq, Default, TypeInfo)]
1214
pub struct DisablePallets;
@@ -30,7 +32,9 @@ impl SignedExtension for DisablePallets {
3032
_len: usize,
3133
) -> TransactionValidity {
3234
// Disable normal balance transfers.
33-
if !RuntimeConfigs::enable_balance_transfers() && contains_balance_transfer(call) {
35+
if !RuntimeConfigs::enable_balance_transfers()
36+
&& contains_balance_transfer(call, MAX_BALANCE_RECURSION_DEPTH)
37+
{
3438
InvalidTransaction::Call.into()
3539
} else {
3640
Ok(ValidTransaction::default())
@@ -61,7 +65,14 @@ impl SignedExtension for DisablePallets {
6165
}
6266
}
6367

64-
fn contains_balance_transfer(call: &RuntimeCall) -> bool {
68+
fn contains_balance_transfer(call: &RuntimeCall, mut recursion_depth_left: u16) -> bool {
69+
if recursion_depth_left == 0 {
70+
// If the recursion depth is reached, we assume the call contains a balance transfer.
71+
return true;
72+
}
73+
74+
recursion_depth_left -= 1;
75+
6576
match call {
6677
RuntimeCall::Balances(
6778
pallet_balances::Call::transfer_allow_death { .. }
@@ -71,12 +82,14 @@ fn contains_balance_transfer(call: &RuntimeCall) -> bool {
7182
RuntimeCall::Utility(utility_call) => match utility_call {
7283
pallet_utility::Call::batch { calls }
7384
| pallet_utility::Call::batch_all { calls }
74-
| pallet_utility::Call::force_batch { calls } => {
75-
calls.iter().any(contains_balance_transfer)
76-
}
85+
| pallet_utility::Call::force_batch { calls } => calls
86+
.iter()
87+
.any(|call| contains_balance_transfer(call, recursion_depth_left)),
7788
pallet_utility::Call::as_derivative { call, .. }
7889
| pallet_utility::Call::dispatch_as { call, .. }
79-
| pallet_utility::Call::with_weight { call, .. } => contains_balance_transfer(call),
90+
| pallet_utility::Call::with_weight { call, .. } => {
91+
contains_balance_transfer(call, recursion_depth_left)
92+
}
8093
pallet_utility::Call::__Ignore(..) => false,
8194
},
8295
_ => false,

Diff for: domains/runtime/evm/src/lib.rs

+19-6
Original file line numberDiff line numberDiff line change
@@ -148,12 +148,15 @@ pub type Executive = domain_pallet_executive::Executive<
148148
AllPalletsWithSystem,
149149
>;
150150

151+
const MAX_CONTRACT_RECURSION_DEPTH: u16 = 5;
152+
151153
/// Rejects contracts that can't be created under the current allow list.
152154
/// Returns false if the call is a contract call, and the account is *not* allowed to call it.
153155
/// Otherwise, returns true.
154156
pub fn is_create_contract_allowed(call: &RuntimeCall, signer: &AccountId) -> bool {
155-
if is_create_contract(call)
156-
&& !pallet_evm_nonce_tracker::Pallet::<Runtime>::is_allowed_to_create_contracts(signer)
157+
// Only enter recursive code if this account can't create contracts
158+
if !pallet_evm_nonce_tracker::Pallet::<Runtime>::is_allowed_to_create_contracts(signer)
159+
&& is_create_contract(call, MAX_CONTRACT_RECURSION_DEPTH)
157160
{
158161
return false;
159162
}
@@ -163,7 +166,14 @@ pub fn is_create_contract_allowed(call: &RuntimeCall, signer: &AccountId) -> boo
163166
}
164167

165168
/// Returns true if the call is a contract creation call.
166-
pub fn is_create_contract(call: &RuntimeCall) -> bool {
169+
pub fn is_create_contract(call: &RuntimeCall, mut recursion_depth_left: u16) -> bool {
170+
if recursion_depth_left == 0 {
171+
// If the recursion depth is reached, we assume the call contains a contract.
172+
return true;
173+
}
174+
175+
recursion_depth_left -= 1;
176+
167177
match call {
168178
RuntimeCall::EVM(pallet_evm::Call::create { .. })
169179
| RuntimeCall::EVM(pallet_evm::Call::create2 { .. }) => true,
@@ -179,14 +189,17 @@ pub fn is_create_contract(call: &RuntimeCall) -> bool {
179189
transaction: EthereumTransaction::EIP1559(transaction),
180190
..
181191
}) => transaction.action == TransactionAction::Create,
182-
// TODO: does this need a recursion limit?
183192
RuntimeCall::Utility(utility_call) => match utility_call {
184193
pallet_utility::Call::batch { calls }
185194
| pallet_utility::Call::batch_all { calls }
186-
| pallet_utility::Call::force_batch { calls } => calls.iter().any(is_create_contract),
195+
| pallet_utility::Call::force_batch { calls } => calls
196+
.iter()
197+
.any(|call| is_create_contract(call, recursion_depth_left)),
187198
pallet_utility::Call::as_derivative { call, .. }
188199
| pallet_utility::Call::dispatch_as { call, .. }
189-
| pallet_utility::Call::with_weight { call, .. } => is_create_contract(call),
200+
| pallet_utility::Call::with_weight { call, .. } => {
201+
is_create_contract(call, recursion_depth_left)
202+
}
190203
pallet_utility::Call::__Ignore(..) => false,
191204
},
192205
_ => false,

0 commit comments

Comments
 (0)