diff --git a/Cargo.lock b/Cargo.lock index 79128647ea7a9..630874b9339ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -708,6 +708,16 @@ dependencies = [ "proptest", ] +[[package]] +name = "bytecode-verifier-transactional-tests" +version = "0.1.0" +dependencies = [ + "datatest-stable", + "diem-workspace-hack", + "move-command-line-common", + "move-transactional-test-runner", +] + [[package]] name = "byteorder" version = "1.4.3" diff --git a/Cargo.toml b/Cargo.toml index 657f59fb4c58f..b20ceadfd45c4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -61,6 +61,7 @@ members = [ "language/bytecode-verifier", "language/bytecode-verifier/bytecode-verifier-tests", "language/bytecode-verifier/invalid-mutations", + "language/bytecode-verifier/transactional-tests", "language/compiler", "language/compiler/bytecode-source-map", "language/compiler/ir-to-bytecode", diff --git a/language/bytecode-verifier/transactional-tests/Cargo.toml b/language/bytecode-verifier/transactional-tests/Cargo.toml new file mode 100644 index 0000000000000..7b1444bc0948d --- /dev/null +++ b/language/bytecode-verifier/transactional-tests/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "bytecode-verifier-transactional-tests" +version = "0.1.0" +authors = ["Diem Association "] +publish = false +edition = "2018" +license = "Apache-2.0" + +[dependencies] +diem-workspace-hack = { path = "../../../common/workspace-hack" } + +[dev-dependencies] +datatest-stable = "0.1.1" +move-command-line-common = { path = "../../move-command-line-common" } +move-transactional-test-runner = { path = "../../testing-infra/transactional-test-runner" } + +[[test]] +name = "tests" +harness = false diff --git a/language/bytecode-verifier/transactional-tests/src/lib.rs b/language/bytecode-verifier/transactional-tests/src/lib.rs new file mode 100644 index 0000000000000..5496168458c2b --- /dev/null +++ b/language/bytecode-verifier/transactional-tests/src/lib.rs @@ -0,0 +1,6 @@ +// Copyright (c) The Diem Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +#![forbid(unsafe_code)] + +// Empty src/lib.rs to get rusty-tags working. diff --git a/language/bytecode-verifier/transactional-tests/tests/reference_safety/eq_bad.exp b/language/bytecode-verifier/transactional-tests/tests/reference_safety/eq_bad.exp new file mode 100644 index 0000000000000..a7543c6c6060c --- /dev/null +++ b/language/bytecode-verifier/transactional-tests/tests/reference_safety/eq_bad.exp @@ -0,0 +1,37 @@ +processed 4 tasks + +task 0 'publish'. lines 1-13: +Error: Unable to publish module '00000000000000000000000000000001::Tester'. Got VMError: { + major_status: READREF_EXISTS_MUTABLE_BORROW_ERROR, + sub_status: None, + location: 0x1::Tester, + indices: [(FunctionDefinition, 0)], + offsets: [(FunctionDefinitionIndex(0), 6)], +} + +task 1 'publish'. lines 15-27: +Error: Unable to publish module '00000000000000000000000000000001::Tester2'. Got VMError: { + major_status: READREF_EXISTS_MUTABLE_BORROW_ERROR, + sub_status: None, + location: 0x1::Tester2, + indices: [(FunctionDefinition, 0)], + offsets: [(FunctionDefinitionIndex(0), 6)], +} + +task 2 'publish'. lines 29-41: +Error: Unable to publish module '00000000000000000000000000000001::Tester3'. Got VMError: { + major_status: READREF_EXISTS_MUTABLE_BORROW_ERROR, + sub_status: None, + location: 0x1::Tester3, + indices: [(FunctionDefinition, 0)], + offsets: [(FunctionDefinitionIndex(0), 6)], +} + +task 3 'publish'. lines 43-55: +Error: Unable to publish module '00000000000000000000000000000001::Tester4'. Got VMError: { + major_status: READREF_EXISTS_MUTABLE_BORROW_ERROR, + sub_status: None, + location: 0x1::Tester4, + indices: [(FunctionDefinition, 0)], + offsets: [(FunctionDefinitionIndex(0), 6)], +} diff --git a/language/ir-testsuite/tests/move/borrow_tests/eq_bad.mvir b/language/bytecode-verifier/transactional-tests/tests/reference_safety/eq_bad.mvir similarity index 63% rename from language/ir-testsuite/tests/move/borrow_tests/eq_bad.mvir rename to language/bytecode-verifier/transactional-tests/tests/reference_safety/eq_bad.mvir index 839b4d48d4f01..14cdf3ae385b4 100644 --- a/language/ir-testsuite/tests/move/borrow_tests/eq_bad.mvir +++ b/language/bytecode-verifier/transactional-tests/tests/reference_safety/eq_bad.mvir @@ -1,3 +1,4 @@ +//# publish --address 0x1 module Tester { eqtest1() { let x: u64; @@ -5,13 +6,13 @@ module Tester { x = 0; r = &mut x; + // invalid read of mutable ref, does not currently own its data _ = copy(r) == copy(r); return; } } -// check: READREF_EXISTS_MUTABLE_BORROW_ERROR -//! new-transaction +//# publish --address 0x1 module Tester2 { eqtest2() { let x: u64; @@ -19,13 +20,13 @@ module Tester2 { x = 0; r = &mut x; + // invalid read of mutable ref, does not currently own its data _ = copy(r) == move(r); return; } } -// check: READREF_EXISTS_MUTABLE_BORROW_ERROR -//! new-transaction +//# publish --address 0x1 module Tester3 { neqtest1() { let x: u64; @@ -33,13 +34,13 @@ module Tester3 { x = 0; r = &mut x; + // invalid read of mutable ref, does not currently own its data _ = copy(r) != copy(r); return; } } -// check: READREF_EXISTS_MUTABLE_BORROW_ERROR -//! new-transaction +//# publish --address 0x1 module Tester4 { neqtest2() { let x: u64; @@ -47,8 +48,8 @@ module Tester4 { x = 0; r = &mut x; + // invalid read of mutable ref, does not currently own its data _ = copy(r) != move(r); return; } } -// check: READREF_EXISTS_MUTABLE_BORROW_ERROR diff --git a/language/bytecode-verifier/transactional-tests/tests/reference_safety/eq_ok.exp b/language/bytecode-verifier/transactional-tests/tests/reference_safety/eq_ok.exp new file mode 100644 index 0000000000000..6cd67db3f6472 --- /dev/null +++ b/language/bytecode-verifier/transactional-tests/tests/reference_safety/eq_ok.exp @@ -0,0 +1 @@ +processed 1 task diff --git a/language/ir-testsuite/tests/move/borrow_tests/eq_ok.mvir b/language/bytecode-verifier/transactional-tests/tests/reference_safety/eq_ok.mvir similarity index 89% rename from language/ir-testsuite/tests/move/borrow_tests/eq_ok.mvir rename to language/bytecode-verifier/transactional-tests/tests/reference_safety/eq_ok.mvir index 79f45d4e92680..ce1784b18d5a4 100644 --- a/language/ir-testsuite/tests/move/borrow_tests/eq_ok.mvir +++ b/language/bytecode-verifier/transactional-tests/tests/reference_safety/eq_ok.mvir @@ -1,3 +1,6 @@ +//# publish --address 0x1 + +// testing valid usages of == and != over references module Tester { eqtest1() { let x: u64; diff --git a/language/bytecode-verifier/transactional-tests/tests/tests.rs b/language/bytecode-verifier/transactional-tests/tests/tests.rs new file mode 100644 index 0000000000000..33a6573229fc8 --- /dev/null +++ b/language/bytecode-verifier/transactional-tests/tests/tests.rs @@ -0,0 +1,7 @@ +// Copyright (c) The Diem Core Contributors +// SPDX-License-Identifier: Apache-2.0 + +pub const TEST_DIR: &str = "tests"; +use move_transactional_test_runner::vm_test_harness::run_test; + +datatest_stable::harness!(run_test, TEST_DIR, r".*\.(mvir|move)$"); diff --git a/language/testing-infra/transactional-test-runner/src/vm_test_harness.rs b/language/testing-infra/transactional-test-runner/src/vm_test_harness.rs index c3c9c9e1faa17..07a29ad53e3b6 100644 --- a/language/testing-infra/transactional-test-runner/src/vm_test_harness.rs +++ b/language/testing-infra/transactional-test-runner/src/vm_test_harness.rs @@ -9,7 +9,12 @@ use crate::{ tasks::{InitCommand, KnownCommandFormat, SyntaxChoice, TaskInput}, }; use anyhow::*; -use move_binary_format::{errors::VMResult, file_format::CompiledScript, CompiledModule}; +use move_binary_format::{ + access::ModuleAccess, + errors::{Location, VMError, VMResult}, + file_format::CompiledScript, + CompiledModule, +}; use move_core_types::{ account_address::AccountAddress, identifier::IdentStr, @@ -17,7 +22,8 @@ use move_core_types::{ transaction_argument::{convert_txn_args, TransactionArgument}, }; use move_lang::{ - compiled_unit::CompiledUnit, shared::verify_and_create_named_address_mapping, + compiled_unit::CompiledUnit, + shared::{verify_and_create_named_address_mapping, AddressBytes}, FullyCompiledProgram, }; use move_stdlib::move_stdlib_named_addresses; @@ -95,6 +101,16 @@ impl<'a> MoveTestAdapter<'a> for SimpleVMTestAdapter<'a> { Ok(()) }) .unwrap(); + let mut addr_to_name_mapping = BTreeMap::new(); + for (name, addr) in move_stdlib_named_addresses() { + let prev = addr_to_name_mapping.insert(addr, name); + assert!(prev.is_none()); + } + for module in &*MOVE_STDLIB_COMPILED { + let bytes = AddressBytes::new(module.address().to_u8()); + let named_addr = addr_to_name_mapping.get(&bytes).unwrap().clone(); + adapter.compiled_state.add(Some(named_addr), module.clone()); + } adapter } @@ -114,9 +130,9 @@ impl<'a> MoveTestAdapter<'a> for SimpleVMTestAdapter<'a> { }) .map_err(|e| { anyhow!( - "Unable to publish module '{}'. Got VMError: {:?}", + "Unable to publish module '{}'. Got VMError: {}", module.self_id(), - e + format_vm_error(&e) ) }) } @@ -136,7 +152,12 @@ impl<'a> MoveTestAdapter<'a> for SimpleVMTestAdapter<'a> { self.perform_session_action(gas_budget, |session, gas_status| { session.execute_script(script_bytes, type_args, args, signers, gas_status) }) - .map_err(|e| anyhow!("Script execution failed with VMError: {:?}", e)) + .map_err(|e| { + anyhow!( + "Script execution failed with VMError: {}", + format_vm_error(&e) + ) + }) } fn call_function( @@ -153,7 +174,12 @@ impl<'a> MoveTestAdapter<'a> for SimpleVMTestAdapter<'a> { self.perform_session_action(gas_budget, |session, gas_status| { session.execute_script_function(module, function, type_args, args, signers, gas_status) }) - .map_err(|e| anyhow!("Function execution failed with VMError: {:?}", e)) + .map_err(|e| { + anyhow!( + "Function execution failed with VMError: {}", + format_vm_error(&e) + ) + }) } fn view_data( @@ -184,6 +210,29 @@ impl<'a> MoveTestAdapter<'a> for SimpleVMTestAdapter<'a> { } } +pub fn format_vm_error(e: &VMError) -> String { + let location_string = match e.location() { + Location::Undefined => "undefined".to_owned(), + Location::Script => "script".to_owned(), + Location::Module(id) => format!("0x{}::{}", id.address().short_str_lossless(), id.name()), + }; + format!( + "{{ + major_status: {major_status:?}, + sub_status: {sub_status:?}, + location: {location_string}, + indices: {indices:?}, + offsets: {offsets:?}, +}}", + major_status = e.major_status(), + sub_status = e.sub_status(), + location_string = location_string, + // TODO maybe include source map info? + indices = e.indices(), + offsets = e.offsets(), + ) +} + impl<'a> SimpleVMTestAdapter<'a> { fn perform_session_action( &mut self, diff --git a/language/testing-infra/transactional-test-runner/tests/vm_test_harness/example.exp b/language/testing-infra/transactional-test-runner/tests/vm_test_harness/example.exp index 3882c74458bea..2cf322bf7adb9 100644 --- a/language/testing-infra/transactional-test-runner/tests/vm_test_harness/example.exp +++ b/language/testing-infra/transactional-test-runner/tests/vm_test_harness/example.exp @@ -4,7 +4,13 @@ task 3 'view'. lines 13-15: [No Resource Exists] task 5 'run'. lines 37-37: -Error: Function execution failed with VMError: VMError { major_status: ABORTED, sub_status: Some(0), message: Some("0x00000000000000000000000000000042::N::ex at offset 1"), location: Module(ModuleId { address: 00000000000000000000000000000042, name: Identifier("N") }), indices: [], offsets: [(FunctionDefinitionIndex(0), 1)] } +Error: Function execution failed with VMError: { + major_status: ABORTED, + sub_status: Some(0), + location: 0x42::N, + indices: [], + offsets: [(FunctionDefinitionIndex(0), 1)], +} task 7 'view'. lines 47-49: key 0x42::N::R { diff --git a/x.toml b/x.toml index cc89d6d65bc5a..a5aee9dd53490 100644 --- a/x.toml +++ b/x.toml @@ -179,6 +179,7 @@ members = [ "bytecode-interpreter-testsuite", "bytecode-verifier-tests", + "bytecode-verifier-transactional-tests", "cli", "cluster-test", "compatibility-test",