contracts: Allow indeterministic instructions off-chain (#12469)

* Allow indetermistic instructions off-chain

* Apply suggestions from code review

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>

* fmt

Co-authored-by: Sasha Gryaznov <hi@agryaznov.com>
This commit is contained in:
Alexander Theißen
2022-10-24 19:48:04 +02:00
committed by GitHub
parent d0dcf008ec
commit 3ae4be8662
15 changed files with 926 additions and 169 deletions
+489 -56
View File
@@ -24,7 +24,7 @@ use crate::{
exec::{FixSizedKey, Frame},
storage::Storage,
tests::test_utils::{get_contract, get_contract_checked},
wasm::{PrefabWasmModule, ReturnCode as RuntimeReturnCode},
wasm::{Determinism, PrefabWasmModule, ReturnCode as RuntimeReturnCode},
weights::WeightInfo,
BalanceOf, Code, CodeStorage, Config, ContractInfoOf, DefaultAddressGenerator, DeletionQueue,
Error, Pallet, Schedule,
@@ -340,6 +340,7 @@ parameter_types! {
// We want stack height to be always enabled for tests so that this
// instrumentation path is always tested implicitly.
schedule.limits.stack_height = Some(512);
schedule.instruction_weights.fallback = 1;
schedule
};
pub static DepositPerByte: BalanceOf<Test> = 1;
@@ -522,7 +523,12 @@ fn instantiate_and_call_and_deposit_event() {
// We determine the storage deposit limit after uploading because it depends on ALICEs free
// balance which is changed by uploading a module.
assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, None));
assert_ok!(Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm,
None,
Determinism::Deterministic
));
// Drop previous events
initialize_block(2);
@@ -690,7 +696,13 @@ fn instantiate_unique_trie_id() {
ExtBuilder::default().existential_deposit(500).build().execute_with(|| {
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, None).unwrap();
Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm,
None,
Determinism::Deterministic,
)
.unwrap();
let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
// Instantiate the contract and store its trie id for later comparison.
@@ -940,6 +952,7 @@ fn delegate_call() {
RuntimeOrigin::signed(ALICE),
callee_wasm,
Some(codec::Compact(100_000)),
Determinism::Deterministic,
));
assert_ok!(Contracts::call(
@@ -1376,10 +1389,18 @@ fn crypto_hashes() {
// We offset data in the contract tables by 1.
let mut params = vec![(n + 1) as u8];
params.extend_from_slice(input);
let result =
<Pallet<Test>>::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, params, false)
.result
.unwrap();
let result = <Pallet<Test>>::bare_call(
ALICE,
addr.clone(),
0,
GAS_LIMIT,
None,
params,
false,
Determinism::Deterministic,
)
.result
.unwrap();
assert!(!result.did_revert());
let expected = hash_fn(input.as_ref());
assert_eq!(&result.data[..*expected_size], &*expected);
@@ -1407,9 +1428,18 @@ fn transfer_return_code() {
// Contract has only the minimal balance so any transfer will fail.
Balances::make_free_balance_be(&addr, min_balance);
let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, vec![], false)
.result
.unwrap();
let result = Contracts::bare_call(
ALICE,
addr.clone(),
0,
GAS_LIMIT,
None,
vec![],
false,
Determinism::Deterministic,
)
.result
.unwrap();
assert_return_code!(result, RuntimeReturnCode::TransferFailed);
// Contract has enough total balance in order to not go below the min balance
@@ -1417,9 +1447,18 @@ fn transfer_return_code() {
// the transfer still fails.
Balances::make_free_balance_be(&addr, min_balance + 100);
Balances::reserve(&addr, min_balance + 100).unwrap();
let result = Contracts::bare_call(ALICE, addr, 0, GAS_LIMIT, None, vec![], false)
.result
.unwrap();
let result = Contracts::bare_call(
ALICE,
addr,
0,
GAS_LIMIT,
None,
vec![],
false,
Determinism::Deterministic,
)
.result
.unwrap();
assert_return_code!(result, RuntimeReturnCode::TransferFailed);
});
}
@@ -1454,6 +1493,7 @@ fn call_return_code() {
None,
AsRef::<[u8]>::as_ref(&DJANGO).to_vec(),
false,
Determinism::Deterministic,
)
.result
.unwrap();
@@ -1484,6 +1524,7 @@ fn call_return_code() {
.cloned()
.collect(),
false,
Determinism::Deterministic,
)
.result
.unwrap();
@@ -1506,6 +1547,7 @@ fn call_return_code() {
.cloned()
.collect(),
false,
Determinism::Deterministic,
)
.result
.unwrap();
@@ -1525,6 +1567,7 @@ fn call_return_code() {
.cloned()
.collect(),
false,
Determinism::Deterministic,
)
.result
.unwrap();
@@ -1543,6 +1586,7 @@ fn call_return_code() {
.cloned()
.collect(),
false,
Determinism::Deterministic,
)
.result
.unwrap();
@@ -1591,6 +1635,7 @@ fn instantiate_return_code() {
None,
callee_hash.clone(),
false,
Determinism::Deterministic,
)
.result
.unwrap();
@@ -1609,6 +1654,7 @@ fn instantiate_return_code() {
None,
callee_hash.clone(),
false,
Determinism::Deterministic,
)
.result
.unwrap();
@@ -1616,10 +1662,18 @@ fn instantiate_return_code() {
// Contract has enough balance but the passed code hash is invalid
Balances::make_free_balance_be(&addr, min_balance + 10_000);
let result =
Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, vec![0; 33], false)
.result
.unwrap();
let result = Contracts::bare_call(
ALICE,
addr.clone(),
0,
GAS_LIMIT,
None,
vec![0; 33],
false,
Determinism::Deterministic,
)
.result
.unwrap();
assert_return_code!(result, RuntimeReturnCode::CodeNotFound);
// Contract has enough balance but callee reverts because "1" is passed.
@@ -1631,6 +1685,7 @@ fn instantiate_return_code() {
None,
callee_hash.iter().chain(&1u32.to_le_bytes()).cloned().collect(),
false,
Determinism::Deterministic,
)
.result
.unwrap();
@@ -1645,6 +1700,7 @@ fn instantiate_return_code() {
None,
callee_hash.iter().chain(&2u32.to_le_bytes()).cloned().collect(),
false,
Determinism::Deterministic,
)
.result
.unwrap();
@@ -1717,8 +1773,16 @@ fn chain_extension_works() {
// 0 = read input buffer and pass it through as output
let input: Vec<u8> = ExtensionInput { extension_id: 0, func_id: 0, extra: &[99] }.into();
let result =
Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, input.clone(), false);
let result = Contracts::bare_call(
ALICE,
addr.clone(),
0,
GAS_LIMIT,
None,
input.clone(),
false,
Determinism::Deterministic,
);
assert_eq!(TestExtension::last_seen_buffer(), input);
assert_eq!(result.result.unwrap().data, input);
@@ -1731,6 +1795,7 @@ fn chain_extension_works() {
None,
ExtensionInput { extension_id: 0, func_id: 1, extra: &[] }.into(),
false,
Determinism::Deterministic,
)
.result
.unwrap();
@@ -1746,6 +1811,7 @@ fn chain_extension_works() {
None,
ExtensionInput { extension_id: 0, func_id: 2, extra: &[0] }.into(),
false,
Determinism::Deterministic,
);
assert_ok!(result.result);
let gas_consumed = result.gas_consumed;
@@ -1757,6 +1823,7 @@ fn chain_extension_works() {
None,
ExtensionInput { extension_id: 0, func_id: 2, extra: &[42] }.into(),
false,
Determinism::Deterministic,
);
assert_ok!(result.result);
assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 42);
@@ -1768,6 +1835,7 @@ fn chain_extension_works() {
None,
ExtensionInput { extension_id: 0, func_id: 2, extra: &[95] }.into(),
false,
Determinism::Deterministic,
);
assert_ok!(result.result);
assert_eq!(result.gas_consumed.ref_time(), gas_consumed.ref_time() + 95);
@@ -1781,6 +1849,7 @@ fn chain_extension_works() {
None,
ExtensionInput { extension_id: 0, func_id: 3, extra: &[] }.into(),
false,
Determinism::Deterministic,
)
.result
.unwrap();
@@ -1798,6 +1867,7 @@ fn chain_extension_works() {
None,
ExtensionInput { extension_id: 1, func_id: 0, extra: &[] }.into(),
false,
Determinism::Deterministic,
)
.result
.unwrap();
@@ -1847,8 +1917,17 @@ fn chain_extension_temp_storage_works() {
);
assert_ok!(
Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, input.clone(), false)
.result
Contracts::bare_call(
ALICE,
addr.clone(),
0,
GAS_LIMIT,
None,
input.clone(),
false,
Determinism::Deterministic
)
.result
);
})
}
@@ -2387,12 +2466,28 @@ fn reinstrument_does_charge() {
// Call the contract two times without reinstrument
let result0 =
Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, zero.clone(), false);
let result0 = Contracts::bare_call(
ALICE,
addr.clone(),
0,
GAS_LIMIT,
None,
zero.clone(),
false,
Determinism::Deterministic,
);
assert!(!result0.result.unwrap().did_revert());
let result1 =
Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, zero.clone(), false);
let result1 = Contracts::bare_call(
ALICE,
addr.clone(),
0,
GAS_LIMIT,
None,
zero.clone(),
false,
Determinism::Deterministic,
);
assert!(!result1.result.unwrap().did_revert());
// They should match because both where called with the same schedule.
@@ -2405,8 +2500,16 @@ fn reinstrument_does_charge() {
});
// This call should trigger reinstrumentation
let result2 =
Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, zero.clone(), false);
let result2 = Contracts::bare_call(
ALICE,
addr.clone(),
0,
GAS_LIMIT,
None,
zero.clone(),
false,
Determinism::Deterministic,
);
assert!(!result2.result.unwrap().did_revert());
assert!(result2.gas_consumed.ref_time() > result1.gas_consumed.ref_time());
assert_eq!(
@@ -2433,7 +2536,16 @@ fn debug_message_works() {
vec![],
),);
let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
let result = Contracts::bare_call(ALICE, addr, 0, GAS_LIMIT, None, vec![], true);
let result = Contracts::bare_call(
ALICE,
addr,
0,
GAS_LIMIT,
None,
vec![],
true,
Determinism::Deterministic,
);
assert_matches!(result.result, Ok(_));
assert_eq!(std::str::from_utf8(&result.debug_message).unwrap(), "Hello World!");
@@ -2457,7 +2569,16 @@ fn debug_message_logging_disabled() {
),);
let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
// disable logging by passing `false`
let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, vec![], false);
let result = Contracts::bare_call(
ALICE,
addr.clone(),
0,
GAS_LIMIT,
None,
vec![],
false,
Determinism::Deterministic,
);
assert_matches!(result.result, Ok(_));
// the dispatchables always run without debugging
assert_ok!(Contracts::call(RuntimeOrigin::signed(ALICE), addr, 0, GAS_LIMIT, None, vec![]));
@@ -2481,7 +2602,16 @@ fn debug_message_invalid_utf8() {
vec![],
),);
let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
let result = Contracts::bare_call(ALICE, addr, 0, GAS_LIMIT, None, vec![], true);
let result = Contracts::bare_call(
ALICE,
addr,
0,
GAS_LIMIT,
None,
vec![],
true,
Determinism::Deterministic,
);
assert_err!(result.result, <Error<Test>>::DebugMessageInvalidUTF8);
});
}
@@ -2532,6 +2662,7 @@ fn gas_estimation_nested_call_fixed_limit() {
None,
input.clone(),
false,
Determinism::Deterministic,
);
assert_ok!(&result.result);
@@ -2548,6 +2679,7 @@ fn gas_estimation_nested_call_fixed_limit() {
Some(result.storage_deposit.charge_or_zero()),
input,
false,
Determinism::Deterministic,
)
.result
);
@@ -2604,6 +2736,7 @@ fn gas_estimation_call_runtime() {
None,
call.encode(),
false,
Determinism::Deterministic,
);
// contract encodes the result of the dispatch runtime
let outcome = u32::decode(&mut result.result.unwrap().data.as_ref()).unwrap();
@@ -2620,6 +2753,7 @@ fn gas_estimation_call_runtime() {
None,
call.encode(),
false,
Determinism::Deterministic,
)
.result
);
@@ -2668,10 +2802,18 @@ fn ecdsa_recover() {
params.extend_from_slice(&signature);
params.extend_from_slice(&message_hash);
assert!(params.len() == 65 + 32);
let result =
<Pallet<Test>>::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, params, false)
.result
.unwrap();
let result = <Pallet<Test>>::bare_call(
ALICE,
addr.clone(),
0,
GAS_LIMIT,
None,
params,
false,
Determinism::Deterministic,
)
.result
.unwrap();
assert!(!result.did_revert());
assert_eq!(result.data, EXPECTED_COMPRESSED_PUBLIC_KEY);
})
@@ -2691,7 +2833,8 @@ fn upload_code_works() {
assert_ok!(Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm,
Some(codec::Compact(1_000))
Some(codec::Compact(1_000)),
Determinism::Deterministic,
));
assert!(<CodeStorage<Test>>::contains_key(code_hash));
@@ -2702,7 +2845,7 @@ fn upload_code_works() {
phase: Phase::Initialization,
event: RuntimeEvent::Balances(pallet_balances::Event::Reserved {
who: ALICE,
amount: 240,
amount: 241,
}),
topics: vec![],
},
@@ -2727,7 +2870,12 @@ fn upload_code_limit_too_low() {
initialize_block(2);
assert_noop!(
Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, Some(codec::Compact(100))),
Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm,
Some(codec::Compact(100)),
Determinism::Deterministic
),
<Error<Test>>::StorageDepositLimitExhausted,
);
@@ -2746,7 +2894,12 @@ fn upload_code_not_enough_balance() {
initialize_block(2);
assert_noop!(
Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm, Some(codec::Compact(1_000))),
Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm,
Some(codec::Compact(1_000)),
Determinism::Deterministic
),
<Error<Test>>::StorageDepositNotEnoughFunds,
);
@@ -2767,7 +2920,8 @@ fn remove_code_works() {
assert_ok!(Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm,
Some(codec::Compact(1_000))
Some(codec::Compact(1_000)),
Determinism::Deterministic,
));
assert!(<CodeStorage<Test>>::contains_key(code_hash));
@@ -2781,7 +2935,7 @@ fn remove_code_works() {
phase: Phase::Initialization,
event: RuntimeEvent::Balances(pallet_balances::Event::Reserved {
who: ALICE,
amount: 240,
amount: 241,
}),
topics: vec![],
},
@@ -2794,7 +2948,7 @@ fn remove_code_works() {
phase: Phase::Initialization,
event: RuntimeEvent::Balances(pallet_balances::Event::Unreserved {
who: ALICE,
amount: 240,
amount: 241,
}),
topics: vec![],
},
@@ -2821,7 +2975,8 @@ fn remove_code_wrong_origin() {
assert_ok!(Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm,
Some(codec::Compact(1_000))
Some(codec::Compact(1_000)),
Determinism::Deterministic,
));
assert_noop!(
@@ -2836,7 +2991,7 @@ fn remove_code_wrong_origin() {
phase: Phase::Initialization,
event: RuntimeEvent::Balances(pallet_balances::Event::Reserved {
who: ALICE,
amount: 240,
amount: 241,
}),
topics: vec![],
},
@@ -2969,7 +3124,7 @@ fn instantiate_with_zero_balance_works() {
phase: Phase::Initialization,
event: RuntimeEvent::Balances(pallet_balances::Event::Reserved {
who: ALICE,
amount: 240,
amount: 241,
}),
topics: vec![],
},
@@ -3071,7 +3226,7 @@ fn instantiate_with_below_existential_deposit_works() {
phase: Phase::Initialization,
event: RuntimeEvent::Balances(pallet_balances::Event::Reserved {
who: ALICE,
amount: 240,
amount: 241,
}),
topics: vec![],
},
@@ -3261,7 +3416,12 @@ fn set_code_extrinsic() {
));
let addr = Contracts::contract_address(&ALICE, &code_hash, &[]);
assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), new_wasm, None,));
assert_ok!(Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
new_wasm,
None,
Determinism::Deterministic
));
// Drop previous events
initialize_block(2);
@@ -3457,7 +3617,12 @@ fn contract_reverted() {
let input = (flags.bits(), buffer).encode();
// We just upload the code for later use
assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm.clone(), None));
assert_ok!(Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm.clone(),
None,
Determinism::Deterministic
));
// Calling extrinsic: revert leads to an error
assert_err_ignore_postinfo!(
@@ -3536,9 +3701,18 @@ fn contract_reverted() {
);
// Calling directly: revert leads to success but the flags indicate the error
let result = Contracts::bare_call(ALICE, addr.clone(), 0, GAS_LIMIT, None, input, false)
.result
.unwrap();
let result = Contracts::bare_call(
ALICE,
addr.clone(),
0,
GAS_LIMIT,
None,
input,
false,
Determinism::Deterministic,
)
.result
.unwrap();
assert_eq!(result.flags, flags);
assert_eq!(result.data, buffer);
});
@@ -3551,7 +3725,12 @@ fn code_rejected_error_works() {
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
assert_noop!(
Contracts::upload_code(RuntimeOrigin::signed(ALICE), wasm.clone(), None),
Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm.clone(),
None,
Determinism::Deterministic
),
<Error<Test>>::CodeRejected,
);
@@ -3594,7 +3773,12 @@ fn set_code_hash() {
vec![],
));
// upload new code
assert_ok!(Contracts::upload_code(RuntimeOrigin::signed(ALICE), new_wasm.clone(), None));
assert_ok!(Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
new_wasm.clone(),
None,
Determinism::Deterministic
));
System::reset_events();
@@ -3607,16 +3791,25 @@ fn set_code_hash() {
None,
new_code_hash.as_ref().to_vec(),
true,
Determinism::Deterministic,
)
.result
.unwrap();
assert_return_code!(result, 1);
// Second calls new contract code that returns 2
let result =
Contracts::bare_call(ALICE, contract_addr.clone(), 0, GAS_LIMIT, None, vec![], true)
.result
.unwrap();
let result = Contracts::bare_call(
ALICE,
contract_addr.clone(),
0,
GAS_LIMIT,
None,
vec![],
true,
Determinism::Deterministic,
)
.result
.unwrap();
assert_return_code!(result, 2);
// Checking for the last event only
@@ -3950,3 +4143,243 @@ fn deposit_limit_honors_min_leftover() {
assert_eq!(Balances::free_balance(&BOB), 1_000);
});
}
#[test]
fn cannot_instantiate_indeterministic_code() {
let (wasm, code_hash) = compile_module::<Test>("float_instruction").unwrap();
let (caller_wasm, _) = compile_module::<Test>("instantiate_return_code").unwrap();
ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
// Try to instantiate directly from code
assert_err_ignore_postinfo!(
Contracts::instantiate_with_code(
RuntimeOrigin::signed(ALICE),
0,
GAS_LIMIT,
None,
wasm.clone(),
vec![],
vec![],
),
<Error<Test>>::CodeRejected,
);
assert_err!(
Contracts::bare_instantiate(
ALICE,
0,
GAS_LIMIT,
None,
Code::Upload(wasm.clone()),
vec![],
vec![],
false,
)
.result,
<Error<Test>>::CodeRejected,
);
// Try to upload a non deterministic code as deterministic
assert_err!(
Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm.clone(),
None,
Determinism::Deterministic
),
<Error<Test>>::CodeRejected,
);
// Try to instantiate from already stored indeterministic code hash
assert_ok!(Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm,
None,
Determinism::AllowIndeterminism,
));
assert_err_ignore_postinfo!(
Contracts::instantiate(
RuntimeOrigin::signed(ALICE),
0,
GAS_LIMIT,
None,
code_hash,
vec![],
vec![],
),
<Error<Test>>::Indeterministic,
);
assert_err!(
Contracts::bare_instantiate(
ALICE,
0,
GAS_LIMIT,
None,
Code::Existing(code_hash),
vec![],
vec![],
false,
)
.result,
<Error<Test>>::Indeterministic,
);
// Deploy contract which instantiates another contract
let addr = Contracts::bare_instantiate(
ALICE,
0,
GAS_LIMIT,
None,
Code::Upload(caller_wasm),
vec![],
vec![],
false,
)
.result
.unwrap()
.account_id;
// Try to instantiate `code_hash` from another contract in deterministic mode
assert_err!(
<Pallet<Test>>::bare_call(
ALICE,
addr.clone(),
0,
GAS_LIMIT,
None,
code_hash.encode(),
false,
Determinism::Deterministic,
)
.result,
<Error<Test>>::Indeterministic,
);
// Instantiations are not allowed even in non determinism mode
assert_err!(
<Pallet<Test>>::bare_call(
ALICE,
addr.clone(),
0,
GAS_LIMIT,
None,
code_hash.encode(),
false,
Determinism::AllowIndeterminism,
)
.result,
<Error<Test>>::Indeterministic,
);
});
}
#[test]
fn cannot_set_code_indeterministic_code() {
let (wasm, code_hash) = compile_module::<Test>("float_instruction").unwrap();
let (caller_wasm, _) = compile_module::<Test>("set_code_hash").unwrap();
ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
// Put the non deterministic contract on-chain
assert_ok!(Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm,
None,
Determinism::AllowIndeterminism,
));
// Create the contract that will call `seal_set_code_hash`
let caller_addr = Contracts::bare_instantiate(
ALICE,
0,
GAS_LIMIT,
None,
Code::Upload(caller_wasm),
vec![],
vec![],
false,
)
.result
.unwrap()
.account_id;
// We do not allow to set the code hash to a non determinstic wasm
assert_err!(
<Pallet<Test>>::bare_call(
ALICE,
caller_addr.clone(),
0,
GAS_LIMIT,
None,
code_hash.encode(),
false,
Determinism::AllowIndeterminism,
)
.result,
<Error<Test>>::Indeterministic,
);
});
}
#[test]
fn delegate_call_indeterministic_code() {
let (wasm, code_hash) = compile_module::<Test>("float_instruction").unwrap();
let (caller_wasm, _) = compile_module::<Test>("delegate_call_simple").unwrap();
ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
let _ = Balances::deposit_creating(&ALICE, 1_000_000);
// Put the non deterministic contract on-chain
assert_ok!(Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm,
None,
Determinism::AllowIndeterminism,
));
// Create the contract that will call `seal_delegate_call`
let caller_addr = Contracts::bare_instantiate(
ALICE,
0,
GAS_LIMIT,
None,
Code::Upload(caller_wasm),
vec![],
vec![],
false,
)
.result
.unwrap()
.account_id;
// The delegate call will fail in deterministic mode
assert_err!(
<Pallet<Test>>::bare_call(
ALICE,
caller_addr.clone(),
0,
GAS_LIMIT,
None,
code_hash.encode(),
false,
Determinism::Deterministic,
)
.result,
<Error<Test>>::Indeterministic,
);
// The delegate call will work on non deterministic mode
assert_ok!(
<Pallet<Test>>::bare_call(
ALICE,
caller_addr.clone(),
0,
GAS_LIMIT,
None,
code_hash.encode(),
false,
Determinism::AllowIndeterminism,
)
.result
);
});
}