diff --git a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/governance.rs b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/governance.rs index ea438f80552e..3b1779e40b60 100644 --- a/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/governance.rs +++ b/cumulus/parachains/integration-tests/emulated/tests/people/people-westend/src/tests/governance.rs @@ -396,7 +396,7 @@ fn relay_commands_add_remove_username_authority() { ); }); - // Now, remove the username authority with another priviledged XCM call. + // Now, remove the username authority with another privileged XCM call. Westend::execute_with(|| { type Runtime = ::Runtime; type RuntimeCall = ::RuntimeCall; diff --git a/prdoc/pr_6890.prdoc b/prdoc/pr_6890.prdoc new file mode 100644 index 000000000000..286d89222723 --- /dev/null +++ b/prdoc/pr_6890.prdoc @@ -0,0 +1,13 @@ +# Schema: Polkadot SDK PRDoc Schema (prdoc) v1.0.0 +# See doc at https://raw.githubusercontent.com/paritytech/polkadot-sdk/master/prdoc/schema_user.json + +title: Bound resources for nested EVM contract calls + +doc: + - audience: [ Runtime Dev, Runtime User ] + description: | + Implementation of EIP-150 for `pallet-revive`-based EVM contracts. + +crates: + - name: pallet-revive + bump: minor diff --git a/substrate/frame/revive/fixtures/contracts/call.rs b/substrate/frame/revive/fixtures/contracts/call.rs index ee51548879d9..d01fc31ac872 100644 --- a/substrate/frame/revive/fixtures/contracts/call.rs +++ b/substrate/frame/revive/fixtures/contracts/call.rs @@ -20,7 +20,7 @@ #![no_main] use common::input; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -38,10 +38,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::empty(), callee_addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &[0u8; 32], // Value transferred to the contract. + u64::MAX, // How much ref_time to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size to devote for the execution. u64::MAX = use all. + &U256_MAX, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. callee_input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/call_diverging_out_len.rs b/substrate/frame/revive/fixtures/contracts/call_diverging_out_len.rs index 129adde2cec9..069ec0a483e1 100644 --- a/substrate/frame/revive/fixtures/contracts/call_diverging_out_len.rs +++ b/substrate/frame/revive/fixtures/contracts/call_diverging_out_len.rs @@ -28,7 +28,7 @@ extern crate common; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; const BUF_SIZE: usize = 8; static DATA: [u8; BUF_SIZE] = [1, 2, 3, 4, 5, 6, 7, 8]; @@ -42,9 +42,9 @@ fn assert_call(callee_address: &[u8; 20], expected_output: [u8; api::call( uapi::CallFlags::ALLOW_REENTRY, callee_address, - 0u64, - 0u64, - None, + u64::MAX, + u64::MAX, + &U256_MAX, &[0u8; 32], &[], Some(output_buf_capped), @@ -67,9 +67,9 @@ fn assert_instantiate(expected_output: [u8; BUF_SIZE]) { api::instantiate( &code_hash, - 0u64, - 0u64, - None, + u64::MAX, + u64::MAX, + &U256_MAX, &[0; 32], &[0; 32], None, diff --git a/substrate/frame/revive/fixtures/contracts/call_return_code.rs b/substrate/frame/revive/fixtures/contracts/call_return_code.rs index 2d13b9f70956..00ac4e51f5fe 100644 --- a/substrate/frame/revive/fixtures/contracts/call_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/call_return_code.rs @@ -22,7 +22,7 @@ #![no_main] use common::input; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -42,10 +42,10 @@ pub extern "C" fn call() { let err_code = match api::call( uapi::CallFlags::empty(), callee_addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - value, // Value transferred to the contract. + u64::MAX, // How much ref_time to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size to devote for the execution. u64::MAX = use all. + &U256_MAX, // No deposit limit. + value, // Value transferred to the contract. input, None, ) { diff --git a/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs index 8c8aee962849..5fc3242cf3ba 100644 --- a/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/call_runtime_and_call.rs @@ -19,7 +19,7 @@ #![no_main] use common::input; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -42,10 +42,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::empty(), callee_addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &[0u8; 32], // Value transferred to the contract. + u64::MAX, // How much ref_time to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size to devote for the execution. u64::MAX = use all. + &U256_MAX, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. callee_input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs index 330393e706e9..d90b71aa73a5 100644 --- a/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs +++ b/substrate/frame/revive/fixtures/contracts/call_with_flags_and_value.rs @@ -20,7 +20,7 @@ #![no_main] use common::{input, u256_bytes}; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -40,9 +40,9 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::from_bits(flags).unwrap(), callee_addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. + u64::MAX, // How much ref_time to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size to devote for the execution. u64::MAX = use all. + &U256_MAX, // No deposit limit. &u256_bytes(value), // Value transferred to the contract. forwarded_input, None, diff --git a/substrate/frame/revive/fixtures/contracts/call_with_limit.rs b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs index 6ab892a6b7ae..4ff0352dfe51 100644 --- a/substrate/frame/revive/fixtures/contracts/call_with_limit.rs +++ b/substrate/frame/revive/fixtures/contracts/call_with_limit.rs @@ -21,7 +21,7 @@ #![no_main] use common::input; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -43,8 +43,8 @@ pub extern "C" fn call() { callee_addr, ref_time, proof_size, - None, // No deposit limit. - &[0u8; 32], // value transferred to the contract. + &U256_MAX, // No deposit limit. + &[0u8; 32], // value transferred to the contract. forwarded_input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/caller_contract.rs b/substrate/frame/revive/fixtures/contracts/caller_contract.rs index edad43fae251..99a821ebc0e8 100644 --- a/substrate/frame/revive/fixtures/contracts/caller_contract.rs +++ b/substrate/frame/revive/fixtures/contracts/caller_contract.rs @@ -19,7 +19,7 @@ #![no_main] use common::{input, u256_bytes}; -use uapi::{HostFn, HostFnImpl as api, ReturnErrorCode}; +use uapi::{HostFn, HostFnImpl as api, ReturnErrorCode, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -42,9 +42,9 @@ pub extern "C" fn call() { // Fail to deploy the contract since it returns a non-zero exit status. let res = api::instantiate( code_hash, - 0u64, // How much ref_time weight to devote for the execution. 0 = all. - 0u64, // How much proof_size weight to devote for the execution. 0 = all. - None, // No deposit limit. + u64::MAX, // How much ref_time weight to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size weight to devote for the execution. u64::MAX = use all. + &U256_MAX, // No deposit limit. &value, &reverted_input, None, @@ -56,9 +56,9 @@ pub extern "C" fn call() { // Fail to deploy the contract due to insufficient ref_time weight. let res = api::instantiate( code_hash, - 1u64, // too little ref_time weight - 0u64, // How much proof_size weight to devote for the execution. 0 = all. - None, // No deposit limit. + 1u64, // too little ref_time weight + u64::MAX, // How much proof_size weight to devote for the execution. u64::MAX = use all. + &U256_MAX, // No deposit limit. &value, &input, None, @@ -70,9 +70,9 @@ pub extern "C" fn call() { // Fail to deploy the contract due to insufficient proof_size weight. let res = api::instantiate( code_hash, - 0u64, // How much ref_time weight to devote for the execution. 0 = all. - 1u64, // Too little proof_size weight - None, // No deposit limit. + u64::MAX, // How much ref_time weight to devote for the execution. u64::MAX = use all. + 1u64, // Too little proof_size weight + &U256_MAX, // No deposit limit. &value, &input, None, @@ -86,9 +86,9 @@ pub extern "C" fn call() { api::instantiate( code_hash, - 0u64, // How much ref_time weight to devote for the execution. 0 = all. - 0u64, // How much proof_size weight to devote for the execution. 0 = all. - None, // No deposit limit. + u64::MAX, // How much ref_time weight to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size weight to devote for the execution. u64::MAX = use all. + &U256_MAX, // No deposit limit. &value, &input, Some(&mut callee), @@ -101,9 +101,9 @@ pub extern "C" fn call() { let res = api::call( uapi::CallFlags::empty(), &callee, - 0u64, // How much ref_time weight to devote for the execution. 0 = all. - 0u64, // How much proof_size weight to devote for the execution. 0 = all. - None, // No deposit limit. + u64::MAX, // How much ref_time weight to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size weight to devote for the execution. u64::MAX = use all. + &U256_MAX, // No deposit limit. &value, &reverted_input, None, @@ -114,9 +114,9 @@ pub extern "C" fn call() { let res = api::call( uapi::CallFlags::empty(), &callee, - 1u64, // Too little ref_time weight. - 0u64, // How much proof_size weight to devote for the execution. 0 = all. - None, // No deposit limit. + 1u64, // Too little ref_time weight. + u64::MAX, // How much proof_size weight to devote for the execution. u64::MAX = use all. + &U256_MAX, // No deposit limit. &value, &input, None, @@ -127,9 +127,9 @@ pub extern "C" fn call() { let res = api::call( uapi::CallFlags::empty(), &callee, - 0u64, // How much ref_time weight to devote for the execution. 0 = all. - 1u64, // too little proof_size weight - None, // No deposit limit. + u64::MAX, // How much ref_time weight to devote for the execution. u64::MAX = use all. + 1u64, // too little proof_size weight + &U256_MAX, // No deposit limit. &value, &input, None, @@ -141,9 +141,9 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::empty(), &callee, - 0u64, // How much ref_time weight to devote for the execution. 0 = all. - 0u64, // How much proof_size weight to devote for the execution. 0 = all. - None, // No deposit limit. + u64::MAX, // How much ref_time weight to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size weight to devote for the execution. u64::MAX = use all. + &U256_MAX, // No deposit limit. &value, &input, Some(&mut &mut output[..]), diff --git a/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs b/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs index 22d6c5b548d8..8c3e897df813 100644 --- a/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs +++ b/substrate/frame/revive/fixtures/contracts/chain_extension_temp_storage.rs @@ -21,7 +21,7 @@ #![no_main] use common::input; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -54,10 +54,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::ALLOW_REENTRY, &addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &[0u8; 32], // Value transferred to the contract. + u64::MAX, // How much ref_time to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size to devote for the execution. u64::MAX = use all. + &U256_MAX, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/create1_with_value.rs b/substrate/frame/revive/fixtures/contracts/create1_with_value.rs index c6adab828860..22de069db5e6 100644 --- a/substrate/frame/revive/fixtures/contracts/create1_with_value.rs +++ b/substrate/frame/revive/fixtures/contracts/create1_with_value.rs @@ -19,7 +19,7 @@ #![no_main] use common::input; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -34,6 +34,6 @@ pub extern "C" fn call() { api::value_transferred(&mut value); // Deploy the contract with no salt (equivalent to create1). - let ret = api::instantiate(code_hash, 0u64, 0u64, None, &value, &[], None, None, None); + let ret = api::instantiate(code_hash, u64::MAX, u64::MAX, &U256_MAX, &value, &[], None, None, None); assert!(ret.is_ok()); } diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs index a12c36af856a..5bb11e27903e 100644 --- a/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_call.rs @@ -43,10 +43,10 @@ pub extern "C" fn call() { let ret = api::call( uapi::CallFlags::empty(), callee, - 0u64, // How much ref_time weight to devote for the execution. 0 = all. - 0u64, // How much proof_size weight to devote for the execution. 0 = all. - Some(deposit_limit), - &[0u8; 32], // Value transferred to the contract. + u64::MAX, // How much ref_time weight to devote for the execution. u64::MAX = use all resources. + u64::MAX, // How much proof_size weight to devote for the execution. u64::MAX = use all resources. + deposit_limit, + &[0u8; 32], // Value transferred to the contract. input, None, ); diff --git a/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs index ecc0fc79e6fd..f627bc8ba6c4 100644 --- a/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs +++ b/substrate/frame/revive/fixtures/contracts/create_storage_and_instantiate.rs @@ -41,9 +41,9 @@ pub extern "C" fn call() { let ret = api::instantiate( code_hash, - 0u64, // How much ref_time weight to devote for the execution. 0 = all. - 0u64, // How much proof_size weight to devote for the execution. 0 = all. - Some(deposit_limit), + u64::MAX, // How much ref_time weight to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size weight to devote for the execution. u64::MAX = use all. + deposit_limit, &value, input, Some(&mut address), diff --git a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs index cf12fed27563..390b6e0735e3 100644 --- a/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs +++ b/substrate/frame/revive/fixtures/contracts/create_transient_storage_and_call.rs @@ -20,7 +20,7 @@ #![no_main] use common::input; -use uapi::{HostFn, HostFnImpl as api, StorageFlags}; +use uapi::{HostFn, HostFnImpl as api, StorageFlags, U256_MAX}; static BUFFER: [u8; 448] = [0u8; 448]; @@ -49,10 +49,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::empty(), callee, - 0u64, // How much ref_time weight to devote for the execution. 0 = all. - 0u64, // How much proof_size weight to devote for the execution. 0 = all. - None, - &[0u8; 32], // Value transferred to the contract. + u64::MAX, // How much ref_time weight to devote for the execution. u64::MAX = all. + u64::MAX, // How much proof_size weight to devote for the execution. u64::MAX = all. + &U256_MAX, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call.rs b/substrate/frame/revive/fixtures/contracts/delegate_call.rs index 3cf74acf1321..686c459b9da9 100644 --- a/substrate/frame/revive/fixtures/contracts/delegate_call.rs +++ b/substrate/frame/revive/fixtures/contracts/delegate_call.rs @@ -19,7 +19,7 @@ #![no_main] use common::input; -use uapi::{HostFn, HostFnImpl as api, StorageFlags}; +use uapi::{HostFn, HostFnImpl as api, StorageFlags, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -46,7 +46,15 @@ pub extern "C" fn call() { assert!(value[0] == 2u8); let input = [0u8; 0]; - api::delegate_call(uapi::CallFlags::empty(), address, ref_time, proof_size, None, &input, None).unwrap(); + api::delegate_call( + uapi::CallFlags::empty(), + address, + ref_time, + proof_size, + &U256_MAX, + &input, + None + ).unwrap(); api::get_storage(StorageFlags::empty(), &key, value).unwrap(); assert!(value[0] == 1u8); diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call_deposit_limit.rs b/substrate/frame/revive/fixtures/contracts/delegate_call_deposit_limit.rs index 0f157f5a18ac..0c503aa93c56 100644 --- a/substrate/frame/revive/fixtures/contracts/delegate_call_deposit_limit.rs +++ b/substrate/frame/revive/fixtures/contracts/delegate_call_deposit_limit.rs @@ -34,7 +34,15 @@ pub extern "C" fn call() { ); let input = [0u8; 0]; - let ret = api::delegate_call(uapi::CallFlags::empty(), address, 0, 0, Some(&u256_bytes(deposit_limit)), &input, None); + let ret = api::delegate_call( + uapi::CallFlags::empty(), + address, + u64::MAX, + u64::MAX, + &u256_bytes(deposit_limit), + &input, + None + ); if let Err(code) = ret { api::return_value(uapi::ReturnFlags::REVERT, &(code as u32).to_le_bytes()); diff --git a/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs b/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs index a8501dad4692..116d648bb60d 100644 --- a/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs +++ b/substrate/frame/revive/fixtures/contracts/delegate_call_simple.rs @@ -19,7 +19,7 @@ #![no_main] use common::input; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -32,5 +32,13 @@ pub extern "C" fn call() { // Delegate call into passed address. let input = [0u8; 0]; - api::delegate_call(uapi::CallFlags::empty(), address, 0, 0, None, &input, None).unwrap(); + api::delegate_call( + uapi::CallFlags::empty(), + address, + u64::MAX, + u64::MAX, + &U256_MAX, + &input, + None + ).unwrap(); } diff --git a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs index 8342f4acf952..e921b1b9f336 100644 --- a/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs +++ b/substrate/frame/revive/fixtures/contracts/destroy_and_transfer.rs @@ -19,7 +19,7 @@ #![no_main] use common::{input, u256_bytes}; -use uapi::{HostFn, HostFnImpl as api, StorageFlags}; +use uapi::{HostFn, HostFnImpl as api, StorageFlags, U256_MAX}; const ADDRESS_KEY: [u8; 32] = [0u8; 32]; const VALUE: [u8; 32] = u256_bytes(65536); @@ -35,9 +35,9 @@ pub extern "C" fn deploy() { api::instantiate( code_hash, - 0u64, // How much ref_time weight to devote for the execution. 0 = all. - 0u64, // How much proof_size weight to devote for the execution. 0 = all. - None, // No deposit limit. + u64::MAX, // How much ref_time weight to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size weight to devote for the execution. u64::MAX = use all. + &U256_MAX, // No deposit limit. &VALUE, &input, Some(&mut address), @@ -62,9 +62,9 @@ pub extern "C" fn call() { let res = api::call( uapi::CallFlags::empty(), &callee_addr, - 0u64, // How much ref_time weight to devote for the execution. 0 = all. - 0u64, // How much proof_size weight to devote for the execution. 0 = all. - None, // No deposit limit. + u64::MAX, // How much ref_time weight to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size weight to devote for the execution. u64::MAX = use all. + &U256_MAX, // No deposit limit. &VALUE, &[0u8; 1], None, @@ -75,9 +75,9 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::empty(), &callee_addr, - 0u64, // How much ref_time weight to devote for the execution. 0 = all. - 0u64, // How much proof_size weight to devote for the execution. 0 = all. - None, // No deposit limit. + u64::MAX, // How much ref_time weight to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size weight to devote for the execution. u64::MAX = use all. + &U256_MAX, // No deposit limit. &VALUE, &[0u8; 0], None, diff --git a/substrate/frame/revive/fixtures/contracts/drain.rs b/substrate/frame/revive/fixtures/contracts/drain.rs index 6e3e708a6b3d..23bd6e0a4704 100644 --- a/substrate/frame/revive/fixtures/contracts/drain.rs +++ b/substrate/frame/revive/fixtures/contracts/drain.rs @@ -19,7 +19,7 @@ #![no_main] use common::{u256_bytes, u64_output}; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -41,7 +41,7 @@ pub extern "C" fn call() { &[0u8; 20], 0, 0, - None, + &U256_MAX, &u256_bytes(balance), &[], None, diff --git a/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs index 9764859c619b..6b43f9fed9cd 100644 --- a/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/instantiate_return_code.rs @@ -19,7 +19,7 @@ #![no_main] use common::{input, u256_bytes}; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -33,10 +33,9 @@ pub extern "C" fn call() { let err_code = match api::instantiate( code_hash, - 0u64, // How much ref_time weight to devote for the execution. 0 = all. - 0u64, /* How much proof_size weight to devote for the execution. 0 = - * all. */ - None, // No deposit limit. + u64::MAX, // How much ref_time weight to devote for the execution. u64::MAX = use all. + u64::MAX, // How much proof_size weight to devote for the execution. u64::MAX = use all. + &U256_MAX, // No deposit limit. &u256_bytes(10_000u64), // Value to transfer. input, None, diff --git a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs index 3d7702c6537a..9d0bbe5c8b9a 100644 --- a/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs +++ b/substrate/frame/revive/fixtures/contracts/locking_delegate_dependency.rs @@ -21,7 +21,7 @@ #![no_main] use common::input; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; const ALICE_FALLBACK: [u8; 20] = [1u8; 20]; @@ -52,7 +52,7 @@ fn load_input(delegate_call: bool) { } if delegate_call { - api::delegate_call(uapi::CallFlags::empty(), address, 0, 0, None, &[], None).unwrap(); + api::delegate_call(uapi::CallFlags::empty(), address, u64::MAX, u64::MAX, &U256_MAX, &[], None).unwrap(); } } diff --git a/substrate/frame/revive/fixtures/contracts/origin.rs b/substrate/frame/revive/fixtures/contracts/origin.rs index 8e9afd8e8052..b12a45b114f8 100644 --- a/substrate/frame/revive/fixtures/contracts/origin.rs +++ b/substrate/frame/revive/fixtures/contracts/origin.rs @@ -23,7 +23,7 @@ #![no_main] extern crate common; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -49,9 +49,9 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::ALLOW_REENTRY, &addr, - 0u64, - 0u64, - None, + u64::MAX, + u64::MAX, + &U256_MAX, &[0; 32], &[], Some(&mut &mut buf[..]), diff --git a/substrate/frame/revive/fixtures/contracts/read_only_call.rs b/substrate/frame/revive/fixtures/contracts/read_only_call.rs index ea74d56867f5..96786cf9e370 100644 --- a/substrate/frame/revive/fixtures/contracts/read_only_call.rs +++ b/substrate/frame/revive/fixtures/contracts/read_only_call.rs @@ -20,7 +20,7 @@ #![no_main] use common::input; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -39,10 +39,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::READ_ONLY, callee_addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &[0u8; 32], // Value transferred to the contract. + u64::MAX, // How much ref_time to devote for the execution. u64::MAX = all. + u64::MAX, // How much proof_size to devote for the execution. u64::MAX = all. + &U256_MAX, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. callee_input, None, ) diff --git a/substrate/frame/revive/fixtures/contracts/recurse.rs b/substrate/frame/revive/fixtures/contracts/recurse.rs index 2e70d67d8c73..64b4358e5ffb 100644 --- a/substrate/frame/revive/fixtures/contracts/recurse.rs +++ b/substrate/frame/revive/fixtures/contracts/recurse.rs @@ -21,7 +21,7 @@ #![no_main] use common::input; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -43,10 +43,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::ALLOW_REENTRY, &addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much deposit_limit to devote for the execution. 0 = all. - None, // No deposit limit. - &[0u8; 32], // Value transferred to the contract. + u64::MAX, // How much ref_time to devote for the execution. u64::MAX = use all resources. + u64::MAX, // How much proof_size to devote for the execution. u64::MAX = use all resources. + &U256_MAX, // No deposit limit. + &[0u8; 32], // Value transferred to the contract. &(calls_left - 1).to_le_bytes(), None, ) diff --git a/substrate/frame/revive/fixtures/contracts/return_data_api.rs b/substrate/frame/revive/fixtures/contracts/return_data_api.rs index 1d483373cffd..0ce89bc71ca6 100644 --- a/substrate/frame/revive/fixtures/contracts/return_data_api.rs +++ b/substrate/frame/revive/fixtures/contracts/return_data_api.rs @@ -27,7 +27,7 @@ #![no_main] use common::{input, u256_bytes}; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; const INPUT_BUF_SIZE: usize = 128; static INPUT_DATA: [u8; INPUT_BUF_SIZE] = [0xFF; INPUT_BUF_SIZE]; @@ -80,8 +80,16 @@ fn assert_return_data_size_of(expected: u64) { /// Assert the return data to be reset after a balance transfer. fn assert_balance_transfer_does_reset() { - api::call(uapi::CallFlags::empty(), &[0u8; 20], 0, 0, None, &u256_bytes(128), &[], None) - .unwrap(); + api::call( + uapi::CallFlags::empty(), + &[0u8; 20], + u64::MAX, + u64::MAX, + &U256_MAX, + &u256_bytes(128), + &[], + None + ).unwrap(); assert_return_data_size_of(0); } @@ -111,9 +119,9 @@ pub extern "C" fn call() { let mut instantiate = |exit_flag| { api::instantiate( code_hash, - 0u64, - 0u64, - None, + u64::MAX, + u64::MAX, + &U256_MAX, &[0; 32], &construct_input(exit_flag), Some(&mut address_buf), @@ -125,9 +133,9 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::empty(), address_buf, - 0u64, - 0u64, - None, + u64::MAX, + u64::MAX, + &U256_MAX, &[0; 32], &construct_input(exit_flag), None, diff --git a/substrate/frame/revive/fixtures/contracts/self_destruct.rs b/substrate/frame/revive/fixtures/contracts/self_destruct.rs index 2f37706634bd..ea3eedf9ee0c 100644 --- a/substrate/frame/revive/fixtures/contracts/self_destruct.rs +++ b/substrate/frame/revive/fixtures/contracts/self_destruct.rs @@ -19,7 +19,7 @@ #![no_main] use common::input; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; const DJANGO_FALLBACK: [u8; 20] = [4u8; 20]; @@ -42,10 +42,10 @@ pub extern "C" fn call() { api::call( uapi::CallFlags::ALLOW_REENTRY, &addr, - 0u64, // How much ref_time to devote for the execution. 0 = all. - 0u64, // How much proof_size to devote for the execution. 0 = all. - None, // No deposit limit. - &[0u8; 32], // Value to transfer. + u64::MAX, // How much ref_time to devote for the execution. u64 = all. + u64::MAX, // How much proof_size to devote for the execution. u64 = all. + &U256_MAX, // No deposit limit. + &[0u8; 32], // Value to transfer. &[0u8; 0], None, ) diff --git a/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs index 09d45d0a8411..cbfe34f96964 100644 --- a/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs +++ b/substrate/frame/revive/fixtures/contracts/transfer_return_code.rs @@ -19,7 +19,7 @@ #![no_main] use common::u256_bytes; -use uapi::{HostFn, HostFnImpl as api}; +use uapi::{HostFn, HostFnImpl as api, U256_MAX}; #[no_mangle] #[polkavm_derive::polkavm_export] @@ -33,7 +33,7 @@ pub extern "C" fn call() { &[0u8; 20], 0, 0, - None, + &U256_MAX, &u256_bytes(100u64), &[], None, diff --git a/substrate/frame/revive/fixtures/src/lib.rs b/substrate/frame/revive/fixtures/src/lib.rs index 38171edf1152..7685253d1ea2 100644 --- a/substrate/frame/revive/fixtures/src/lib.rs +++ b/substrate/frame/revive/fixtures/src/lib.rs @@ -22,7 +22,7 @@ extern crate alloc; // generated file that tells us where to find the fixtures include!(concat!(env!("OUT_DIR"), "/fixture_location.rs")); -/// Load a given wasm module and returns a wasm binary contents along with it's hash. +/// Load a given wasm module and returns a wasm binary contents along with its hash. #[cfg(feature = "std")] pub fn compile_module(fixture_name: &str) -> anyhow::Result<(Vec, sp_core::H256)> { let out_dir: std::path::PathBuf = FIXTURE_DIR.into(); diff --git a/substrate/frame/revive/src/benchmarking/mod.rs b/substrate/frame/revive/src/benchmarking/mod.rs index e67c39ec0899..2cf856f5f0f2 100644 --- a/substrate/frame/revive/src/benchmarking/mod.rs +++ b/substrate/frame/revive/src/benchmarking/mod.rs @@ -1648,8 +1648,8 @@ mod benchmarks { memory.as_mut_slice(), CallFlags::CLONE_INPUT.bits(), // flags 0, // callee_ptr - 0, // ref_time_limit - 0, // proof_size_limit + u64::MAX, // ref_time_limit + u64::MAX, // proof_size_limit callee_len, // deposit_ptr callee_len + deposit_len, // value_ptr 0, // input_data_ptr @@ -1688,8 +1688,8 @@ mod benchmarks { memory.as_mut_slice(), 0, // flags 0, // address_ptr - 0, // ref_time_limit - 0, // proof_size_limit + u64::MAX, // ref_time_limit + u64::MAX, // proof_size_limit address_len, // deposit_ptr 0, // input_data_ptr 0, // input_data_len @@ -1715,7 +1715,7 @@ mod benchmarks { let value_bytes = Into::::into(value).encode(); let value_len = value_bytes.len() as u32; - let deposit: BalanceOf = 0u32.into(); + let deposit: BalanceOf = u32::MAX.into(); let deposit_bytes = Into::::into(deposit).encode(); let deposit_len = deposit_bytes.len() as u32; @@ -1750,8 +1750,8 @@ mod benchmarks { result = runtime.bench_instantiate( memory.as_mut_slice(), 0, // code_hash_ptr - 0, // ref_time_limit - 0, // proof_size_limit + u64::MAX, // ref_time_limit + u64::MAX, // proof_size_limit offset(hash_len), // deposit_ptr offset(deposit_len), // value_ptr offset(value_len), // input_data_ptr diff --git a/substrate/frame/revive/src/exec.rs b/substrate/frame/revive/src/exec.rs index a6a259149768..359d9608e940 100644 --- a/substrate/frame/revive/src/exec.rs +++ b/substrate/frame/revive/src/exec.rs @@ -53,7 +53,7 @@ use sp_core::{ }; use sp_io::{crypto::secp256k1_ecdsa_recover_compressed, hashing::blake2_256}; use sp_runtime::{ - traits::{BadOrigin, Convert, Dispatchable, Saturating, Zero}, + traits::{BadOrigin, Bounded, Convert, Dispatchable, Saturating, Zero}, DispatchError, SaturatedConversion, }; @@ -885,9 +885,9 @@ where args, value, gas_meter, - Weight::zero(), + Weight::max_value(), storage_meter, - BalanceOf::::zero(), + BalanceOf::::max_value(), false, true, )? @@ -982,14 +982,25 @@ where }, }; + let nested_gas; + let nested_storage; + + if origin_is_caller { + nested_gas = gas_meter.nested_take_all(); + nested_storage = storage_meter.nested_take_all(); + } else { + nested_gas = gas_meter.nested(gas_limit); + nested_storage = storage_meter.nested(deposit_limit); + } + let frame = Frame { delegate, value_transferred, contract_info: CachedContract::Cached(contract_info), account_id, entry_point, - nested_gas: gas_meter.nested(gas_limit), - nested_storage: storage_meter.nested(deposit_limit), + nested_gas, + nested_storage, allows_reentry: true, read_only, last_frame_output: Default::default(), @@ -1117,25 +1128,15 @@ where return Ok(output); } - // Storage limit is normally enforced as late as possible (when the last frame returns) - // so that the ordering of storage accesses does not matter. - // (However, if a special limit was set for a sub-call, it should be enforced right - // after the sub-call returned. See below for this case of enforcement). - if self.frames.is_empty() { - let frame = &mut self.first_frame; - frame.contract_info.load(&frame.account_id); - let contract = frame.contract_info.as_contract(); - frame.nested_storage.enforce_limit(contract)?; - } - let frame = self.top_frame_mut(); - // If a special limit was set for the sub-call, we enforce it here. - // The sub-call will be rolled back in case the limit is exhausted. + // The storage deposit is only charged at the end of every call stack. + // To make sure that no sub call uses more than it is allowed to, + // the limit is manually enforced here. let contract = frame.contract_info.as_contract(); frame .nested_storage - .enforce_subcall_limit(contract) + .enforce_limit(contract) .map_err(|e| ExecError { error: e, origin: ErrorOrigin::Callee })?; let account_id = T::AddressMapper::to_address(&frame.account_id); @@ -3098,8 +3099,8 @@ mod tests { let (address, output) = ctx .ext .instantiate( - Weight::zero(), - U256::zero(), + Weight::MAX, + U256::from(u64::MAX), dummy_ch, ::Currency::minimum_balance().into(), vec![], @@ -3802,8 +3803,8 @@ mod tests { let succ_fail_code = MockLoader::insert(Constructor, move |ctx, _| { ctx.ext .instantiate( - Weight::zero(), - U256::zero(), + Weight::MAX, + U256::from(u64::MAX), fail_code, ctx.ext.minimum_balance() * 100, vec![], @@ -3819,8 +3820,8 @@ mod tests { let addr = ctx .ext .instantiate( - Weight::zero(), - U256::zero(), + Weight::MAX, + U256::from(u64::MAX), success_code, ctx.ext.minimum_balance() * 100, vec![], @@ -4597,7 +4598,7 @@ mod tests { // Successful instantiation should set the output let address = ctx .ext - .instantiate(Weight::zero(), U256::zero(), ok_ch, value, vec![], None) + .instantiate(Weight::MAX, U256::from(u64::MAX), ok_ch, value, vec![], None) .unwrap(); assert_eq!( ctx.ext.last_frame_output(), @@ -4607,8 +4608,8 @@ mod tests { // Balance transfers should reset the output ctx.ext .call( - Weight::zero(), - U256::zero(), + Weight::MAX, + U256::from(u64::MAX), &address, U256::from(1), vec![], @@ -4827,7 +4828,7 @@ mod tests { // Constructors can not access the immutable data ctx.ext - .instantiate(Weight::zero(), U256::zero(), dummy_ch, value, vec![], None) + .instantiate(Weight::MAX, U256::from(u64::MAX), dummy_ch, value, vec![], None) .unwrap(); exec_success() @@ -4944,7 +4945,7 @@ mod tests { move |ctx, _| { let value = ::Currency::minimum_balance().into(); ctx.ext - .instantiate(Weight::zero(), U256::zero(), dummy_ch, value, vec![], None) + .instantiate(Weight::MAX, U256::from(u64::MAX), dummy_ch, value, vec![], None) .unwrap(); exec_success() @@ -4989,7 +4990,7 @@ mod tests { move |ctx, _| { let value = ::Currency::minimum_balance().into(); ctx.ext - .instantiate(Weight::zero(), U256::zero(), dummy_ch, value, vec![], None) + .instantiate(Weight::MAX, U256::from(u64::MAX), dummy_ch, value, vec![], None) .unwrap(); exec_success() diff --git a/substrate/frame/revive/src/gas.rs b/substrate/frame/revive/src/gas.rs index 9aad84e69201..d0fc604b7bc6 100644 --- a/substrate/frame/revive/src/gas.rs +++ b/substrate/frame/revive/src/gas.rs @@ -22,7 +22,7 @@ use frame_support::{ weights::Weight, DefaultNoBound, }; -use sp_runtime::{traits::Zero, DispatchError}; +use sp_runtime::DispatchError; #[cfg(test)] use std::{any::Any, fmt::Debug}; @@ -168,25 +168,22 @@ impl GasMeter { } } - /// Create a new gas meter by removing gas from the current meter. + /// Create a new gas meter by removing *all* the gas from the current meter. /// - /// # Note + /// This should only be used by the primordial frame in a sequence of calls - every subsequent + /// frame should use [`nested`](Self::nested). + pub fn nested_take_all(&mut self) -> Self { + let gas_left = self.gas_left; + self.gas_left -= gas_left; + GasMeter::new(gas_left) + } + + /// Create a new gas meter for a nested call by removing gas from the current meter. /// - /// Passing `0` as amount is interpreted as "all remaining gas". + /// Per [EIP-150](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-150.md), this is + /// capped at 63/64ths of the caller's gas limit. pub fn nested(&mut self, amount: Weight) -> Self { - let amount = Weight::from_parts( - if amount.ref_time().is_zero() { - self.gas_left().ref_time() - } else { - amount.ref_time() - }, - if amount.proof_size().is_zero() { - self.gas_left().proof_size() - } else { - amount.proof_size() - }, - ) - .min(self.gas_left); + let amount = amount.min(self.gas_left - self.gas_left / 64); self.gas_left -= amount; GasMeter::new(amount) } @@ -392,6 +389,52 @@ mod tests { assert!(gas_meter.charge(SimpleToken(1)).is_err()); } + /// Previously, passing a `Weight` of 0 to `nested` would consume all of the meter's current + /// gas. + /// + /// Now, a `Weight` of 0 means no gas for the nested call. + #[test] + fn nested_zero_gas_requested() { + let test_weight = 50000.into(); + let mut gas_meter = GasMeter::::new(test_weight); + let gas_for_nested_call = gas_meter.nested(0.into()); + + assert_eq!(gas_meter.gas_left(), 50000.into()); + assert_eq!(gas_for_nested_call.gas_left(), 0.into()) + } + + #[test] + fn nested_some_gas_requested() { + let test_weight = 50000.into(); + let mut gas_meter = GasMeter::::new(test_weight); + let gas_for_nested_call = gas_meter.nested(10000.into()); + + assert_eq!(gas_meter.gas_left(), 40000.into()); + assert_eq!(gas_for_nested_call.gas_left(), 10000.into()) + } + + #[test] + fn nested_all_gas_requested() { + let test_weight = Weight::from_parts(50000, 50000); + let mut gas_meter = GasMeter::::new(test_weight); + let gas_for_nested_call = gas_meter.nested(test_weight); + + // With EIP-150, it is not possible for subcalls to consume all available gas. + // They are limited to 63/64ths; 50000 / 64 ≈ 781 + assert_eq!(gas_meter.gas_left(), Weight::from_parts(781, 781)); + assert_eq!(gas_for_nested_call.gas_left(), 49219.into()) + } + + #[test] + fn nested_excess_gas_requested() { + let test_weight = Weight::from_parts(50000, 50000); + let mut gas_meter = GasMeter::::new(test_weight); + let gas_for_nested_call = gas_meter.nested(test_weight + 10000.into()); + + assert_eq!(gas_meter.gas_left(), Weight::from_parts(781, 781)); + assert_eq!(gas_for_nested_call.gas_left(), 49219.into()) + } + // Make sure that the gas meter does not charge in case of overcharge #[test] fn overcharge_does_not_charge() { diff --git a/substrate/frame/revive/src/primitives.rs b/substrate/frame/revive/src/primitives.rs index a7127f812b4b..452d2c8a3067 100644 --- a/substrate/frame/revive/src/primitives.rs +++ b/substrate/frame/revive/src/primitives.rs @@ -72,7 +72,7 @@ pub struct ContractResult { /// /// # Note /// - /// This can only different from [`Self::gas_consumed`] when weight pre charging + /// This can only be different from [`Self::gas_consumed`] when weight pre charging /// is used. Currently, only `seal_call_runtime` makes use of pre charging. /// Additionally, any `seal_call` or `seal_instantiate` makes use of pre-charging /// when a non-zero `gas_limit` argument is supplied. diff --git a/substrate/frame/revive/src/storage/meter.rs b/substrate/frame/revive/src/storage/meter.rs index 6eddf048be98..aa198cfced94 100644 --- a/substrate/frame/revive/src/storage/meter.rs +++ b/substrate/frame/revive/src/storage/meter.rs @@ -100,13 +100,8 @@ pub trait State: private::Sealed {} pub struct Root; /// State parameter that constitutes a meter that is in its nested state. -/// Its value indicates whether the nested meter has its own limit. -#[derive(DefaultNoBound, RuntimeDebugNoBound)] -pub enum Nested { - #[default] - DerivedLimit, - OwnLimit, -} +#[derive(Default, Debug)] +pub struct Nested; impl State for Root {} impl State for Nested {} @@ -125,10 +120,8 @@ pub struct RawMeter { /// We only have one charge per contract hence the size of this vector is /// limited by the maximum call depth. charges: Vec>, - /// We store the nested state to determine if it has a special limit for sub-call. - nested: S, /// Type parameter only used in impls. - _phantom: PhantomData, + _phantom: PhantomData<(E, S)>, } /// This type is used to describe a storage change when charging from the meter. @@ -281,21 +274,22 @@ where S: State + Default + Debug, { /// Create a new child that has its `limit`. - /// Passing `0` as the limit is interpreted as to take whatever is remaining from its parent. /// /// This is called whenever a new subcall is initiated in order to track the storage /// usage for this sub call separately. This is necessary because we want to exchange balance /// with the current contract we are interacting with. + /// + /// Gas for nested calls are capped to 63/64ths of the caller's originally available limit, per + /// EIP-150. pub fn nested(&self, limit: BalanceOf) -> RawMeter { debug_assert!(matches!(self.contract_state(), ContractState::Alive)); + + let available = self.available() - self.available() / (BalanceOf::::from(64u32)); + // If a special limit is specified higher than it is available, // we want to enforce the lesser limit to the nested meter, to fail in the sub-call. - let limit = self.available().min(limit); - if limit.is_zero() { - RawMeter { limit: self.available(), ..Default::default() } - } else { - RawMeter { limit, nested: Nested::OwnLimit, ..Default::default() } - } + let limit = available.min(limit); + RawMeter { limit, ..Default::default() } } /// Absorb a child that was spawned to handle a sub call. @@ -347,6 +341,14 @@ where _ => ContractState::Alive, } } + + /// Create a new storage meter using the calling meter's available limit. + /// + /// This should only be used by the primordial frame in a sequence of calls - every subsequent + /// frame should use [`nested`](Self::nested) + pub fn nested_take_all(&self) -> RawMeter { + RawMeter { limit: self.available(), ..Default::default() } + } } /// Functions that only apply to the root state. @@ -477,13 +479,6 @@ impl> RawMeter { /// [`Self::charge`] does not enforce the storage limit since we want to do this check as late /// as possible to allow later refunds to offset earlier charges. - /// - /// # Note - /// - /// We normally need to call this **once** for every call stack and not for every cross contract - /// call. However, if a dedicated limit is specified for a sub-call, this needs to be called - /// once the sub-call has returned. For this, the [`Self::enforce_subcall_limit`] wrapper is - /// used. pub fn enforce_limit( &mut self, info: Option<&mut ContractInfo>, @@ -502,18 +497,6 @@ impl> RawMeter { } Ok(()) } - - /// This is a wrapper around [`Self::enforce_limit`] to use on the exit from a sub-call to - /// enforce its special limit if needed. - pub fn enforce_subcall_limit( - &mut self, - info: Option<&mut ContractInfo>, - ) -> Result<(), DispatchError> { - match self.nested { - Nested::OwnLimit => self.enforce_limit(info), - Nested::DerivedLimit => Ok(()), - } - } } impl Ext for ReservingExt { @@ -724,6 +707,49 @@ mod tests { ) } + /// Previously, passing a limit of 0 meant unlimited storage for a nested call. + /// + /// Now, a limit of 0 means the subcall will not be able to use any storage. + #[test] + fn nested_zero_limit_requested() { + clear_ext(); + + let meter = TestMeter::new(&Origin::from_account_id(ALICE), 1_000, 0).unwrap(); + assert_eq!(meter.available(), 1_000); + let nested0 = meter.nested(BalanceOf::::zero()); + assert_eq!(nested0.available(), 0); + } + + #[test] + fn nested_some_limit_requested() { + clear_ext(); + + let meter = TestMeter::new(&Origin::from_account_id(ALICE), 1_000, 0).unwrap(); + assert_eq!(meter.available(), 1_000); + let nested0 = meter.nested(500); + assert_eq!(nested0.available(), 500); + } + + #[test] + fn nested_all_limit_requested() { + clear_ext(); + + let meter = TestMeter::new(&Origin::from_account_id(ALICE), 1_000, 0).unwrap(); + assert_eq!(meter.available(), 1_000); + let nested0 = meter.nested(1_000); + assert_eq!(nested0.available(), 985); + } + + #[test] + fn nested_over_limit_requested() { + clear_ext(); + + let meter = TestMeter::new(&Origin::from_account_id(ALICE), 1_000, 0).unwrap(); + assert_eq!(meter.available(), 1_000); + let nested0 = meter.nested(2_000); + assert_eq!(nested0.available(), 985); + } + #[test] fn empty_charge_works() { clear_ext(); @@ -879,7 +905,7 @@ mod tests { let mut meter = TestMeter::new(&test_case.origin, 1_000, 0).unwrap(); assert_eq!(meter.available(), 1_000); - let mut nested0 = meter.nested(BalanceOf::::zero()); + let mut nested0 = meter.nested(BalanceOf::::max_value()); nested0.charge(&Diff { bytes_added: 5, bytes_removed: 1, @@ -895,7 +921,7 @@ mod tests { items_deposit: 20, immutable_data_len: 0, }); - let mut nested1 = nested0.nested(BalanceOf::::zero()); + let mut nested1 = nested0.nested(BalanceOf::::max_value()); nested1.charge(&Diff { items_removed: 5, ..Default::default() }); nested1.charge(&Diff { bytes_added: 20, ..Default::default() }); nested1.terminate(&nested1_info, CHARLIE); diff --git a/substrate/frame/revive/src/tests.rs b/substrate/frame/revive/src/tests.rs index 664578bf7672..f16aa67ea708 100644 --- a/substrate/frame/revive/src/tests.rs +++ b/substrate/frame/revive/src/tests.rs @@ -1149,7 +1149,7 @@ fn delegate_call() { assert_ok!(builder::call(caller_addr) .value(1337) - .data((callee_addr, 0u64, 0u64).encode()) + .data((callee_addr, u64::MAX, u64::MAX).encode()) .build()); }); } @@ -2261,7 +2261,7 @@ fn gas_estimation_for_subcalls() { // Run the test for all of those weight limits for the subcall let weights = [ - Weight::zero(), + Weight::MAX, GAS_LIMIT, GAS_LIMIT * 2, GAS_LIMIT / 5, @@ -3453,13 +3453,13 @@ fn deposit_limit_in_nested_calls() { // We do not remove any storage but add a storage item of 12 bytes in the caller // contract. This would cost 12 + 2 = 14 Balance. - // The nested call doesn't get a special limit, which is set by passing 0 to it. + // The nested call doesn't get a special limit, which is set by passing `u64::MAX` to it. // This should fail as the specified parent's limit is less than the cost: 13 < // 14. assert_err_ignore_postinfo!( builder::call(addr_caller) .storage_deposit_limit(13) - .data((100u32, &addr_callee, U256::from(0u64)).encode()) + .data((100u32, &addr_callee, U256::from(u64::MAX)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3467,13 +3467,13 @@ fn deposit_limit_in_nested_calls() { // Now we specify the parent's limit high enough to cover the caller's storage // additions. However, we use a single byte more in the callee, hence the storage // deposit should be 15 Balance. - // The nested call doesn't get a special limit, which is set by passing 0 to it. + // The nested call doesn't get a special limit, which is set by passing `u64::MAX` to it. // This should fail as the specified parent's limit is less than the cost: 14 // < 15. assert_err_ignore_postinfo!( builder::call(addr_caller) .storage_deposit_limit(14) - .data((101u32, &addr_callee, U256::from(0u64)).encode()) + .data((101u32, &addr_callee, U256::from(u64::MAX)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3495,7 +3495,7 @@ fn deposit_limit_in_nested_calls() { assert_err_ignore_postinfo!( builder::call(addr_caller) .storage_deposit_limit(0) - .data((87u32, &addr_callee, U256::from(0u64)).encode()) + .data((87u32, &addr_callee, U256::from(u64::MAX)).encode()) .build(), >::StorageDepositLimitExhausted, ); @@ -3551,28 +3551,24 @@ fn deposit_limit_in_nested_instantiate() { // // Provided the limit is set to be 1 Balance less, // this call should fail on the return from the caller contract. - assert_err_ignore_postinfo!( - builder::call(addr_caller) - .origin(RuntimeOrigin::signed(BOB)) - .storage_deposit_limit(callee_info_len + 2 + ED + 1) - .data((0u32, &code_hash_callee, U256::from(0u64)).encode()) - .build(), - >::StorageDepositLimitExhausted, - ); + let ret = builder::bare_call(addr_caller) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(DepositLimit::Balance(callee_info_len + 2 + ED + 1)) + .data((0u32, &code_hash_callee, U256::from(u64::MAX)).encode()) + .build_and_unwrap_result(); + assert_return_code!(ret, RuntimeReturnCode::OutOfResources); // The charges made on instantiation should be rolled back. assert_eq!(::Currency::free_balance(&BOB), 1_000_000); // Now we give enough limit for the instantiation itself, but require for 1 more storage // byte in the constructor. Hence +1 Balance to the limit is needed. This should fail on // the return from constructor. - assert_err_ignore_postinfo!( - builder::call(addr_caller) - .origin(RuntimeOrigin::signed(BOB)) - .storage_deposit_limit(callee_info_len + 2 + ED + 2) - .data((1u32, &code_hash_callee, U256::from(0u64)).encode()) - .build(), - >::StorageDepositLimitExhausted, - ); + let ret = builder::bare_call(addr_caller) + .origin(RuntimeOrigin::signed(BOB)) + .storage_deposit_limit(DepositLimit::Balance(callee_info_len + 2 + ED + 2)) + .data((1u32, &code_hash_callee, U256::from(0u64)).encode()) + .build_and_unwrap_result(); + assert_return_code!(ret, RuntimeReturnCode::OutOfResources); // The charges made on the instantiation should be rolled back. assert_eq!(::Currency::free_balance(&BOB), 1_000_000); diff --git a/substrate/frame/revive/src/wasm/runtime.rs b/substrate/frame/revive/src/wasm/runtime.rs index 52f79f2eb55a..8529c7d9e73b 100644 --- a/substrate/frame/revive/src/wasm/runtime.rs +++ b/substrate/frame/revive/src/wasm/runtime.rs @@ -1004,8 +1004,7 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { self.charge_gas(call_type.cost())?; let callee = memory.read_h160(callee_ptr)?; - let deposit_limit = - if deposit_ptr == SENTINEL { U256::zero() } else { memory.read_u256(deposit_ptr)? }; + let deposit_limit = memory.read_u256(deposit_ptr)?; let input_data = if flags.contains(CallFlags::CLONE_INPUT) { let input = self.input_data.as_ref().ok_or(Error::::InputForwarded)?; @@ -1091,8 +1090,7 @@ impl<'a, E: Ext, M: ?Sized + Memory> Runtime<'a, E, M> { salt_ptr: u32, ) -> Result { self.charge_gas(RuntimeCosts::Instantiate { input_data_len })?; - let deposit_limit: U256 = - if deposit_ptr == SENTINEL { U256::zero() } else { memory.read_u256(deposit_ptr)? }; + let deposit_limit: U256 = memory.read_u256(deposit_ptr)?; let value = memory.read_u256(value_ptr)?; let code_hash = memory.read_h256(code_hash_ptr)?; let input_data = memory.read(input_data_ptr, input_data_len)?; diff --git a/substrate/frame/revive/uapi/src/host.rs b/substrate/frame/revive/uapi/src/host.rs index eced4843b552..6156b6eba76a 100644 --- a/substrate/frame/revive/uapi/src/host.rs +++ b/substrate/frame/revive/uapi/src/host.rs @@ -113,7 +113,7 @@ pub trait HostFn: private::Sealed { callee: &[u8; 20], ref_time_limit: u64, proof_size_limit: u64, - deposit: Option<&[u8; 32]>, + deposit: &[u8; 32], value: &[u8; 32], input_data: &[u8], output: Option<&mut &mut [u8]>, @@ -202,7 +202,7 @@ pub trait HostFn: private::Sealed { address: &[u8; 20], ref_time_limit: u64, proof_size_limit: u64, - deposit_limit: Option<&[u8; 32]>, + deposit_limit: &[u8; 32], input_data: &[u8], output: Option<&mut &mut [u8]>, ) -> Result; @@ -318,7 +318,7 @@ pub trait HostFn: private::Sealed { code_hash: &[u8; 32], ref_time_limit: u64, proof_size_limit: u64, - deposit: Option<&[u8; 32]>, + deposit: &[u8; 32], value: &[u8; 32], input: &[u8], address: Option<&mut [u8; 20]>, diff --git a/substrate/frame/revive/uapi/src/host/riscv64.rs b/substrate/frame/revive/uapi/src/host/riscv64.rs index 6fdda86892d5..bb608cfe49d0 100644 --- a/substrate/frame/revive/uapi/src/host/riscv64.rs +++ b/substrate/frame/revive/uapi/src/host/riscv64.rs @@ -168,7 +168,7 @@ impl HostFn for HostFnImpl { code_hash: &[u8; 32], ref_time_limit: u64, proof_size_limit: u64, - deposit_limit: Option<&[u8; 32]>, + deposit_limit: &[u8; 32], value: &[u8; 32], input: &[u8], mut address: Option<&mut [u8; 20]>, @@ -180,7 +180,7 @@ impl HostFn for HostFnImpl { None => crate::SENTINEL as _, }; let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); - let deposit_limit_ptr = ptr_or_sentinel(&deposit_limit); + let deposit_limit_ptr = deposit_limit.as_ptr(); let salt_ptr = ptr_or_sentinel(&salt); #[repr(C)] #[allow(dead_code)] @@ -225,13 +225,13 @@ impl HostFn for HostFnImpl { callee: &[u8; 20], ref_time_limit: u64, proof_size_limit: u64, - deposit_limit: Option<&[u8; 32]>, + deposit_limit: &[u8; 32], value: &[u8; 32], input: &[u8], mut output: Option<&mut &mut [u8]>, ) -> Result { let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); - let deposit_limit_ptr = ptr_or_sentinel(&deposit_limit); + let deposit_limit_ptr = deposit_limit.as_ptr(); #[repr(C)] #[allow(dead_code)] struct Args { @@ -273,12 +273,12 @@ impl HostFn for HostFnImpl { address: &[u8; 20], ref_time_limit: u64, proof_size_limit: u64, - deposit_limit: Option<&[u8; 32]>, + deposit_limit: &[u8; 32], input: &[u8], mut output: Option<&mut &mut [u8]>, ) -> Result { let (output_ptr, mut output_len) = ptr_len_or_sentinel(&mut output); - let deposit_limit_ptr = ptr_or_sentinel(&deposit_limit); + let deposit_limit_ptr = deposit_limit.as_ptr(); #[repr(C)] #[allow(dead_code)] struct Args { diff --git a/substrate/frame/revive/uapi/src/lib.rs b/substrate/frame/revive/uapi/src/lib.rs index ef1798b4bf61..0715532c61e7 100644 --- a/substrate/frame/revive/uapi/src/lib.rs +++ b/substrate/frame/revive/uapi/src/lib.rs @@ -116,6 +116,27 @@ pub struct ReturnCode(u32); /// make sense for a memory location or length. const SENTINEL: u32 = u32::MAX; +/// Converts a `u64` to a 32-byte array, encoded little-endian. +/// +/// The `u64` value will be placed in the first 8 bytes, +/// and the remaining bytes are set to zero. +const fn u64_to_u256_bytes(value: u64) -> [u8; 32] { + let mut bytes = [0u8; 32]; + let value_bytes = value.to_le_bytes(); + let mut i = 0; + while i < 8 { + bytes[i] = value_bytes[i]; + i += 1; + } + bytes +} + +/// A constant representing `u64::MAX` as a 32-byte array, in little-endian encoding. +/// +/// Deposit limits are `U256`, but balances are represented as `u64`. +/// To represent no deposit limits on an operation, this should be used. +pub const U256_MAX: [u8; 32] = u64_to_u256_bytes(u64::MAX); + impl From for Option { fn from(code: ReturnCode) -> Self { (code.0 < SENTINEL).then_some(code.0)