contracts: Remove wat support from test fixtures (#3588)

In order to prepare for PolkaVM support I removed the wat support from
our test fixture crate.

- Removed redundant tests (invalid module checks are already inside the
prepare module where they belong
- Converted the gas_sync tests to Rust
- Moved the start function test to the `wasm` module
This commit is contained in:
Alexander Theißen
2024-03-07 09:05:04 +08:00
committed by GitHub
parent 117a9433da
commit 4ae7398818
12 changed files with 49 additions and 283 deletions
Generated
-1
View File
@@ -9634,7 +9634,6 @@ dependencies = [
"tempfile",
"toml 0.8.8",
"twox-hash",
"wat",
]
[[package]]
@@ -11,7 +11,6 @@ description = "Fixtures for testing contracts pallet."
workspace = true
[dependencies]
wat = "1"
frame-system = { path = "../../system" }
sp-runtime = { path = "../../../primitives/runtime" }
anyhow = "1.0.0"
@@ -14,12 +14,25 @@
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//! Valid module but missing the call function
//! This fixture calls caller_is_origin `n` times.
#![no_std]
#![no_main]
extern crate common;
use common::input;
use uapi::{HostFn, HostFnImpl as api};
#[no_mangle]
#[polkavm_derive::polkavm_export]
pub extern "C" fn deploy() {}
#[no_mangle]
#[polkavm_derive::polkavm_export]
pub extern "C" fn call() {
input!(n: u32, );
for _ in 0..n {
let _ = api::caller_is_origin();
}
}
@@ -1,5 +0,0 @@
;; A valid contract which does nothing at all
(module
(func (export "deploy"))
(func (export "call"))
)
@@ -1,8 +0,0 @@
;; An invalid module
(module
(func (export "deploy"))
(func (export "call")
;; imbalanced stack
(i32.const 7)
)
)
@@ -1,10 +0,0 @@
(module
(import "env" "memory" (memory 1 1))
(start $start)
(func $start
(loop $inf (br $inf)) ;; just run out of gas
(unreachable)
)
(func (export "call"))
(func (export "deploy"))
)
@@ -1,14 +0,0 @@
;; Everything prepared for the host function call, but no call is performed.
(module
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
(import "env" "memory" (memory 1 1))
;; [0, 8) buffer to write input
;; [8, 12) size of the input buffer
(data (i32.const 8) "\04")
(func (export "call"))
(func (export "deploy"))
)
@@ -1,22 +0,0 @@
;; Stores a value of the passed size. The host function is called once.
(module
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
(import "env" "memory" (memory 1 1))
;; [0, 8) buffer to write input
;; [8, 12) size of the input buffer
(data (i32.const 8) "\04")
(func (export "call")
;; instructions to consume engine fuel
(drop
(i32.const 42)
)
(call $seal_input (i32.const 0) (i32.const 8))
)
(func (export "deploy"))
)
@@ -1,28 +0,0 @@
;; Stores a value of the passed size. The host function is called twice.
(module
(import "seal0" "seal_input" (func $seal_input (param i32 i32)))
(import "env" "memory" (memory 1 1))
;; [0, 8) buffer to write input
;; [8, 12) size of the input buffer
(data (i32.const 8) "\04")
(func (export "call")
;; instructions to consume engine fuel
(drop
(i32.const 42)
)
(call $seal_input (i32.const 0) (i32.const 8))
;; instructions to consume engine fuel
(drop
(i32.const 42)
)
(call $seal_input (i32.const 0) (i32.const 8))
)
(func (export "deploy"))
)
+5 -36
View File
@@ -16,34 +16,7 @@
// limitations under the License.
use sp_runtime::traits::Hash;
use std::{env::var, fs, path::PathBuf};
fn wat_root_dir() -> PathBuf {
match (var("CARGO_MANIFEST_DIR"), var("CARGO_PKG_NAME")) {
// When `CARGO_MANIFEST_DIR` is not set, Rust resolves relative paths from the root folder
(Err(_), _) => "substrate/frame/contracts/fixtures/data".into(),
(Ok(path), Ok(s)) if s == "pallet-contracts" => PathBuf::from(path).join("fixtures/data"),
(Ok(path), Ok(s)) if s == "pallet-contracts-mock-network" =>
PathBuf::from(path).parent().unwrap().join("fixtures/data"),
(Ok(_), pkg_name) => panic!("Failed to resolve fixture dir for tests from {pkg_name:?}."),
}
}
/// Load a given wasm module represented by a .wat file and returns a wasm binary contents along
/// with it's hash.
///
/// The fixture files are located under the `fixtures/` directory.
fn legacy_compile_module<T>(
fixture_name: &str,
) -> anyhow::Result<(Vec<u8>, <T::Hashing as Hash>::Output)>
where
T: frame_system::Config,
{
let fixture_path = wat_root_dir().join(format!("{fixture_name}.wat"));
let wasm_binary = wat::parse_file(fixture_path)?;
let code_hash = T::Hashing::hash(&wasm_binary);
Ok((wasm_binary, code_hash))
}
use std::{fs, path::PathBuf};
/// Load a given wasm module and returns a wasm binary contents along with it's hash.
/// Use the legacy compile_module as fallback, if the rust fixture does not exist yet.
@@ -53,15 +26,11 @@ pub fn compile_module<T>(
where
T: frame_system::Config,
{
let out_dir: std::path::PathBuf = env!("OUT_DIR").into();
let out_dir: PathBuf = env!("OUT_DIR").into();
let fixture_path = out_dir.join(format!("{fixture_name}.wasm"));
match fs::read(fixture_path) {
Ok(wasm_binary) => {
let code_hash = T::Hashing::hash(&wasm_binary);
Ok((wasm_binary, code_hash))
},
Err(_) => legacy_compile_module::<T>(fixture_name),
}
let binary = fs::read(fixture_path)?;
let code_hash = T::Hashing::hash(&binary);
Ok((binary, code_hash))
}
#[cfg(test)]
+11 -156
View File
@@ -852,27 +852,6 @@ fn deposit_event_max_value_limit() {
});
}
// Fail out of fuel (ref_time weight) inside the start function.
#[test]
fn run_out_of_fuel_start_fun() {
let (wasm, _code_hash) = compile_module::<Test>("run_out_of_gas_start_fn").unwrap();
ExtBuilder::default().existential_deposit(50).build().execute_with(|| {
let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
assert_err_ignore_postinfo!(
Contracts::instantiate_with_code(
RuntimeOrigin::signed(ALICE),
0,
Weight::from_parts(1_000_000_000_000, u64::MAX),
None,
wasm,
vec![],
vec![],
),
Error::<Test>::OutOfGas,
);
});
}
// Fail out of fuel (ref_time weight) in the engine.
#[test]
fn run_out_of_fuel_engine() {
@@ -956,50 +935,15 @@ fn run_out_of_fuel_host() {
#[test]
fn gas_syncs_work() {
let (wasm0, _code_hash) = compile_module::<Test>("seal_input_noop").unwrap();
let (wasm1, _code_hash) = compile_module::<Test>("seal_input_once").unwrap();
let (wasm2, _code_hash) = compile_module::<Test>("seal_input_twice").unwrap();
let (code, _code_hash) = compile_module::<Test>("caller_is_origin_n").unwrap();
ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
// Instantiate noop contract.
let addr0 = Contracts::bare_instantiate(
let addr = Contracts::bare_instantiate(
ALICE,
0,
GAS_LIMIT,
None,
Code::Upload(wasm0),
vec![],
vec![],
DebugInfo::Skip,
CollectEvents::Skip,
)
.result
.unwrap()
.account_id;
// Instantiate 1st contract.
let addr1 = Contracts::bare_instantiate(
ALICE,
0,
GAS_LIMIT,
None,
Code::Upload(wasm1),
vec![],
vec![],
DebugInfo::Skip,
CollectEvents::Skip,
)
.result
.unwrap()
.account_id;
// Instantiate 2nd contract.
let addr2 = Contracts::bare_instantiate(
ALICE,
0,
GAS_LIMIT,
None,
Code::Upload(wasm2),
Code::Upload(code),
vec![],
vec![],
DebugInfo::Skip,
@@ -1011,11 +955,11 @@ fn gas_syncs_work() {
let result = Contracts::bare_call(
ALICE,
addr0,
addr.clone(),
0,
GAS_LIMIT,
None,
1u8.to_le_bytes().to_vec(),
0u32.encode(),
DebugInfo::Skip,
CollectEvents::Skip,
Determinism::Enforced,
@@ -1025,27 +969,28 @@ fn gas_syncs_work() {
let result = Contracts::bare_call(
ALICE,
addr1,
addr.clone(),
0,
GAS_LIMIT,
None,
1u8.to_le_bytes().to_vec(),
1u32.encode(),
DebugInfo::Skip,
CollectEvents::Skip,
Determinism::Enforced,
);
assert_ok!(result.result);
let gas_consumed_once = result.gas_consumed.ref_time();
let host_consumed_once = <Test as Config>::Schedule::get().host_fn_weights.input.ref_time();
let host_consumed_once =
<Test as Config>::Schedule::get().host_fn_weights.caller_is_origin.ref_time();
let engine_consumed_once = gas_consumed_once - host_consumed_once - engine_consumed_noop;
let result = Contracts::bare_call(
ALICE,
addr2,
addr,
0,
GAS_LIMIT,
None,
1u8.to_le_bytes().to_vec(),
2u32.encode(),
DebugInfo::Skip,
CollectEvents::Skip,
Determinism::Enforced,
@@ -4453,96 +4398,6 @@ fn contract_reverted() {
});
}
#[test]
fn code_rejected_error_works() {
ExtBuilder::default().existential_deposit(200).build().execute_with(|| {
let _ = <Test as Config>::Currency::set_balance(&ALICE, 1_000_000);
let (wasm, _) = compile_module::<Test>("invalid_module").unwrap();
assert_noop!(
Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm.clone(),
None,
Determinism::Enforced
),
<Error<Test>>::CodeRejected,
);
let result = Contracts::bare_instantiate(
ALICE,
0,
GAS_LIMIT,
None,
Code::Upload(wasm),
vec![],
vec![],
DebugInfo::UnsafeDebug,
CollectEvents::Skip,
);
assert_err!(result.result, <Error<Test>>::CodeRejected);
assert_eq!(
std::str::from_utf8(&result.debug_message).unwrap(),
"Can't load the module into wasmi!"
);
let (wasm, _) = compile_module::<Test>("invalid_contract_no_call").unwrap();
assert_noop!(
Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm.clone(),
None,
Determinism::Enforced
),
<Error<Test>>::CodeRejected,
);
let result = Contracts::bare_instantiate(
ALICE,
0,
GAS_LIMIT,
None,
Code::Upload(wasm),
vec![],
vec![],
DebugInfo::UnsafeDebug,
CollectEvents::Skip,
);
assert_err!(result.result, <Error<Test>>::CodeRejected);
assert_eq!(
std::str::from_utf8(&result.debug_message).unwrap(),
"call function isn't exported"
);
let (wasm, _) = compile_module::<Test>("invalid_contract_no_memory").unwrap();
assert_noop!(
Contracts::upload_code(
RuntimeOrigin::signed(ALICE),
wasm.clone(),
None,
Determinism::Enforced
),
<Error<Test>>::CodeRejected,
);
let result = Contracts::bare_instantiate(
ALICE,
0,
GAS_LIMIT,
None,
Code::Upload(wasm),
vec![],
vec![],
DebugInfo::UnsafeDebug,
CollectEvents::Skip,
);
assert_err!(result.result, <Error<Test>>::CodeRejected);
assert_eq!(
std::str::from_utf8(&result.debug_message).unwrap(),
"No memory import found in the module"
);
});
}
#[test]
fn set_code_hash() {
let (wasm, code_hash) = compile_module::<Test>("set_code_hash").unwrap();
+18
View File
@@ -3442,4 +3442,22 @@ mod tests {
runtime.read_sandbox_memory_as(&memory, 0u32).unwrap();
assert_eq!(decoded.into_inner(), data);
}
#[test]
fn run_out_of_gas_in_start_fn() {
const CODE: &str = r#"
(module
(import "env" "memory" (memory 1 1))
(start $start)
(func $start
(loop $inf (br $inf)) ;; just run out of gas
(unreachable)
)
(func (export "call"))
(func (export "deploy"))
)
"#;
let mut mock_ext = MockExt::default();
assert_err!(execute(&CODE, vec![], &mut mock_ext), <Error<Test>>::OutOfGas);
}
}