[contracts] Forbid calling back to contracts after switching to runtime (#13443)

* save: compiles and tests pass

* save: added global

* done + test

* cleanup

* changelog update

* cleanup

* address feedback, step 1

* address feedback, step 2

* address feedback, step 3

* returned updated gas_estimation_call_runtime test

* clippy fix

* address feedback, step 4

* address feedback, step 5

* move data from context to inputs

* docs fix

* Apply suggestions from code review

Co-authored-by: Alexander Theißen <alex.theissen@me.com>

* address feedback, step 6

---------

Co-authored-by: Alexander Theißen <alex.theissen@me.com>
This commit is contained in:
Sasha Gryaznov
2023-03-06 10:40:03 +02:00
committed by GitHub
parent e171e2ada4
commit f85d6dc6dd
5 changed files with 287 additions and 150 deletions
+67 -5
View File
@@ -2805,12 +2805,9 @@ fn gas_estimation_call_runtime() {
// Call something trivial with a huge gas limit so that we can observe the effects
// of pre-charging. This should create a difference between consumed and required.
let call = RuntimeCall::Contracts(crate::Call::call {
let call = RuntimeCall::Balances(pallet_balances::Call::transfer {
dest: addr_callee,
value: 0,
gas_limit: GAS_LIMIT / 3,
storage_deposit_limit: None,
data: vec![],
value: min_balance * 10,
});
let result = Contracts::bare_call(
ALICE,
@@ -2844,6 +2841,71 @@ fn gas_estimation_call_runtime() {
});
}
#[test]
fn gas_call_runtime_reentrancy_guarded() {
let (caller_code, _caller_hash) = compile_module::<Test>("call_runtime").unwrap();
let (callee_code, _callee_hash) = compile_module::<Test>("dummy").unwrap();
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
let min_balance = <Test as Config>::Currency::minimum_balance();
let _ = Balances::deposit_creating(&ALICE, 1000 * min_balance);
let _ = Balances::deposit_creating(&CHARLIE, 1000 * min_balance);
let addr_caller = Contracts::bare_instantiate(
ALICE,
min_balance * 100,
GAS_LIMIT,
None,
Code::Upload(caller_code),
vec![],
vec![0],
false,
)
.result
.unwrap()
.account_id;
let addr_callee = Contracts::bare_instantiate(
ALICE,
min_balance * 100,
GAS_LIMIT,
None,
Code::Upload(callee_code),
vec![],
vec![1],
false,
)
.result
.unwrap()
.account_id;
// Call pallet_contracts call() dispatchable
let call = RuntimeCall::Contracts(crate::Call::call {
dest: addr_callee,
value: 0,
gas_limit: GAS_LIMIT / 3,
storage_deposit_limit: None,
data: vec![],
});
// Call runtime to re-enter back to contracts engine by
// calling dummy contract
let result = Contracts::bare_call(
ALICE,
addr_caller.clone(),
0,
GAS_LIMIT,
None,
call.encode(),
false,
Determinism::Deterministic,
)
.result
.unwrap();
// Call to runtime should fail because of the re-entrancy guard
assert_return_code!(result, RuntimeReturnCode::CallRuntimeFailed);
});
}
#[test]
fn ecdsa_recover() {
let (wasm, _code_hash) = compile_module::<Test>("ecdsa_recover").unwrap();