From 187d7d7540291dc99bec96db9a51af8fbac9ab34 Mon Sep 17 00:00:00 2001 From: sczembor Date: Wed, 13 Mar 2024 15:29:09 +0100 Subject: [PATCH 1/4] contract v3 draft --- Makefile | 7 + contract-v3/.cargo/config.toml | 2 + contract-v3/Cargo.toml | 15 +++ contract-v3/src/main.rs | 226 +++++++++++++++++++++++++++++++++ 4 files changed, 250 insertions(+) create mode 100644 contract-v3/.cargo/config.toml create mode 100644 contract-v3/Cargo.toml create mode 100644 contract-v3/src/main.rs diff --git a/Makefile b/Makefile index f286d8e..91cd1ff 100644 --- a/Makefile +++ b/Makefile @@ -4,10 +4,12 @@ prepare: build-contract: cd contract-v1 && cargo build --release --target wasm32-unknown-unknown cd contract-v2 && cargo build --release --target wasm32-unknown-unknown + cd contract-v3 && cargo build --release --target wasm32-unknown-unknown cd counter-call && cargo build --release --target wasm32-unknown-unknown wasm-strip contract-v1/target/wasm32-unknown-unknown/release/contract-v1.wasm 2>/dev/null | true wasm-strip contract-v2/target/wasm32-unknown-unknown/release/contract-v2.wasm 2>/dev/null | true + wasm-strip contract-v3/target/wasm32-unknown-unknown/release/contract-v2.wasm 2>/dev/null | true wasm-strip counter-call/target/wasm32-unknown-unknown/release/counter-call.wasm 2>/dev/null | true test-only: @@ -17,29 +19,34 @@ test: build-contract mkdir -p tests/wasm cp contract-v1/target/wasm32-unknown-unknown/release/counter-v1.wasm tests/wasm cp contract-v2/target/wasm32-unknown-unknown/release/counter-v2.wasm tests/wasm + cp contract-v3/target/wasm32-unknown-unknown/release/counter-v3.wasm tests/wasm cp counter-call/target/wasm32-unknown-unknown/release/counter-call.wasm tests/wasm cd tests && cargo test clippy: cd contract-v1 && cargo clippy --all-targets -- -D warnings cd contract-v2 && cargo clippy --all-targets -- -D warnings + cd contract-v3 && cargo clippy --all-targets -- -D warnings cd tests && cargo clippy --all-targets -- -D warnings check-lint: clippy cd contract-v1 && cargo fmt -- --check cd contract-v2 && cargo fmt -- --check + cd contract-v3 && cargo fmt -- --check cd counter-call && cargo fmt -- --check cd tests && cargo fmt -- --check lint: clippy cd contract-v1 && cargo fmt cd contract-v2 && cargo fmt + cd contract-v3 && cargo fmt cd counter-call && cargo fmt cd tests && cargo fmt clean: cd contract-v1 && cargo clean cd contract-v2 && cargo clean + cd contract-v3 && cargo clean cd counter-call && cargo clean cd tests && cargo clean rm -rf tests/wasm diff --git a/contract-v3/.cargo/config.toml b/contract-v3/.cargo/config.toml new file mode 100644 index 0000000..f4e8c00 --- /dev/null +++ b/contract-v3/.cargo/config.toml @@ -0,0 +1,2 @@ +[build] +target = "wasm32-unknown-unknown" diff --git a/contract-v3/Cargo.toml b/contract-v3/Cargo.toml new file mode 100644 index 0000000..f688db7 --- /dev/null +++ b/contract-v3/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "counter-v3" +version = "1.0.0" +edition = "2021" + +[dependencies] +casper-contract = "3.0.0" +casper-types = "3.0.0" + +[[bin]] +name = "counter-v3" +path = "src/main.rs" +bench = false +doctest = false +test = false diff --git a/contract-v3/src/main.rs b/contract-v3/src/main.rs new file mode 100644 index 0000000..a36cbc1 --- /dev/null +++ b/contract-v3/src/main.rs @@ -0,0 +1,226 @@ +#![no_std] +#![no_main] + +#[cfg(not(target_arch = "wasm32"))] +compile_error!("target arch should be wasm32: compile with '--target wasm32-unknown-unknown'"); + +// This code imports necessary aspects of external crates that we will use in our contract code. +extern crate alloc; + +// Importing Rust types. +use alloc::{ + string::{String, ToString}, + vec::Vec, +}; +// Importing aspects of the Casper platform. +use casper_contract::{ + contract_api::{runtime, storage}, + unwrap_or_revert::UnwrapOrRevert, +}; +// Importing specific Casper types. +use casper_types::{ + api_error::ApiError, + contracts::{EntryPoint, EntryPointAccess, EntryPointType, EntryPoints, NamedKeys}, + CLType, CLValue, URef, +}; + +/// Creating constants for the various contract entry points. +const ENTRY_POINT_COUNTER_INC: &str = "counter_increment"; +const ENTRY_POINT_COUNTER_GET: &str = "counter_get"; +const ENTRY_POINT_COUNTER_LAST_UPDATED_AT: &str = "counter_last_updated_at"; +const ENTRY_POINT_COUNTER_DECREMENT: &str = "counter_decrement"; + +/// Constants for the keys pointing to values stored in the contract's named keys. +const CONTRACT_VERSION_KEY: &str = "version"; +const CONTRACT_KEY: &str = "counter"; +const COUNT_KEY: &str = "count"; +const LAST_UPDATED_KEY: &str = "last_updated"; + +/// Constants for the keys pointing to values stored in the account's named keys. +const CONTRACT_PACKAGE_NAME: &str = "counter_package_name"; +const CONTRACT_ACCESS_UREF: &str = "counter_access_uref"; + +/// Entry point that increments the count value by 1. +#[no_mangle] +pub extern "C" fn counter_increment() { + let uref: URef = runtime::get_key(COUNT_KEY) + .unwrap_or_revert_with(ApiError::MissingKey) + .into_uref() + .unwrap_or_revert_with(ApiError::UnexpectedKeyVariant); + storage::add(uref, 1); // Increment the count by 1. +} + +/// Entry point that returns the count value. +#[no_mangle] +pub extern "C" fn counter_get() { + let uref: URef = runtime::get_key(COUNT_KEY) + .unwrap_or_revert_with(ApiError::MissingKey) + .into_uref() + .unwrap_or_revert_with(ApiError::UnexpectedKeyVariant); + let result: i32 = storage::read(uref) + .unwrap_or_revert_with(ApiError::Read) + .unwrap_or_revert_with(ApiError::ValueNotFound); + let typed_result = CLValue::from_t(result).unwrap_or_revert(); + runtime::ret(typed_result); // Return the count value. +} + +/// Entry point that returns the unix time when the counter was updated last time. +#[no_mangle] +pub extern "C" fn counter_last_updated_at() { + let uref: URef = runtime::get_key(LAST_UPDATED_KEY) + .unwrap_or_revert_with(ApiError::MissingKey) + .into_uref() + .unwrap_or_revert_with(ApiError::UnexpectedKeyVariant); + let result: i32 = storage::read(uref) + .unwrap_or_revert_with(ApiError::Read) + .unwrap_or_revert_with(ApiError::ValueNotFound); + let typed_result = CLValue::from_t(result).unwrap_or_revert(); + runtime::ret(typed_result); // Return the count value. +} + +/// Entry point that decrements the count value by 1. +#[no_mangle] +pub extern "C" fn counter_decrement() { + let uref: URef = runtime::get_key(COUNT_KEY) + .unwrap_or_revert_with(ApiError::MissingKey) + .into_uref() + .unwrap_or_revert_with(ApiError::UnexpectedKeyVariant); + storage::add(uref, -1); // Decrement the count. +} + +/// Helper function that installs the counter contract on chain. +fn install_counter() { + // Initialize the count to 0, locally. + let count_start = storage::new_uref(0_i32); + let last_updated = storage::new_uref(0_u64); + + // In the named keys of the contract, add keys for `count` and `last_updated`. + let mut counter_named_keys = NamedKeys::new(); + counter_named_keys.insert(String::from(COUNT_KEY), count_start.into()); + counter_named_keys.insert(String::from(LAST_UPDATED_KEY), last_updated.into()); + + // Create the entry points for this contract. + let mut counter_entry_points = EntryPoints::new(); + + counter_entry_points.add_entry_point(EntryPoint::new( + ENTRY_POINT_COUNTER_GET, + Vec::new(), + CLType::I32, + EntryPointAccess::Public, + EntryPointType::Contract, + )); + + counter_entry_points.add_entry_point(EntryPoint::new( + ENTRY_POINT_COUNTER_INC, + Vec::new(), + CLType::Unit, + EntryPointAccess::Public, + EntryPointType::Contract, + )); + + // Create a new contract package that can be upgraded. + let (stored_contract_hash, contract_version) = storage::new_contract( + counter_entry_points, + Some(counter_named_keys), + Some("counter_package_name".to_string()), + Some("counter_access_uref".to_string()), + ); + + /* To create a locked contract instead, use new_locked_contract and throw away the contract version returned + let (stored_contract_hash, _) = + storage::new_locked_contract(counter_entry_points, Some(counter_named_keys), None, None); */ + + // Store the contract version in the context's named keys. + let version_uref = storage::new_uref(contract_version); + runtime::put_key(CONTRACT_VERSION_KEY, version_uref.into()); + + // Create a named key for the contract hash. + runtime::put_key(CONTRACT_KEY, stored_contract_hash.into()); +} + +/// Helper function that upgrades the contract package to a new version. +fn upgrade_counter() { + // In this version, we will not add any named keys. + // The named keys from the previous version should still be available. + // Create a new entry point list that includes counter_decrement. + // We need to specify all entry points, including the ones from the previous version. + let mut counter_entry_points = EntryPoints::new(); + + counter_entry_points.add_entry_point(EntryPoint::new( + ENTRY_POINT_COUNTER_GET, + Vec::new(), + CLType::I32, + EntryPointAccess::Public, + EntryPointType::Contract, + )); + + counter_entry_points.add_entry_point(EntryPoint::new( + ENTRY_POINT_COUNTER_INC, + Vec::new(), + CLType::Unit, + EntryPointAccess::Public, + EntryPointType::Contract, + )); + + // Create an entry point to decrement the counter by 1. + counter_entry_points.add_entry_point(EntryPoint::new( + ENTRY_POINT_COUNTER_DECREMENT, + Vec::new(), + CLType::Unit, + EntryPointAccess::Public, + EntryPointType::Contract, + )); + + // Create an entry point for getting the last_updated at timestamp + counter_entry_points.add_entry_point(EntryPoint::new( + ENTRY_POINT_COUNTER_LAST_UPDATED_AT, + Vec::new(), + CLType::U64, + EntryPointAccess::Public, + EntryPointType::Contract, + )); + + // Get the counter package hash so we can upgrade the package. + let counter_package_hash = runtime::get_key(CONTRACT_PACKAGE_NAME) + .unwrap_or_revert() + .into_hash() + .unwrap() + .into(); + + // Get the existing named keys and add a new one to it + let mut named_keys = NamedKeys::default(); + named_keys.insert() + + // Add a new contract version to the package with the new list of entry points. + let (stored_contract_hash, contract_version) = storage::add_contract_version( + counter_package_hash, + counter_entry_points, + NamedKeys::default(), + ); + + // Here we are updating the version named key with a new value. + // The version named key should already be part of the account. + let version_uref = storage::new_uref(contract_version); + runtime::put_key(CONTRACT_VERSION_KEY, version_uref.into()); + + // Add the latest contract hash into the named key. + // The key should already exist and we will have access to it in this version. + runtime::put_key(CONTRACT_KEY, stored_contract_hash.into()); +} + +/// Entry point that executes automatically when a caller installs the contract. +#[no_mangle] +pub extern "C" fn call() { + match runtime::get_key(CONTRACT_ACCESS_UREF) { + None => { + // The given key doesn't exist, so install the contract. + install_counter(); + // Next, upgrade the contract. + upgrade_counter(); + } + Some(_contract_key) => { + // The stored contract and key exist, so upgrade the contract. + upgrade_counter(); + } + } +} From dadf7b0a04e741cfceeaa84c026a2ed13ecb4503 Mon Sep 17 00:00:00 2001 From: sczembor Date: Tue, 19 Mar 2024 17:56:13 +0100 Subject: [PATCH 2/4] refactor unit tests --- contract-v3/src/main.rs | 36 ++-- tests/src/integration_tests.rs | 365 ++++++++++++++++++++++++++++++++- 2 files changed, 388 insertions(+), 13 deletions(-) diff --git a/contract-v3/src/main.rs b/contract-v3/src/main.rs index a36cbc1..bbf78af 100644 --- a/contract-v3/src/main.rs +++ b/contract-v3/src/main.rs @@ -25,7 +25,7 @@ use casper_types::{ }; /// Creating constants for the various contract entry points. -const ENTRY_POINT_COUNTER_INC: &str = "counter_increment"; +const ENTRY_POINT_COUNTER_INC: &str = "counter_inc"; const ENTRY_POINT_COUNTER_GET: &str = "counter_get"; const ENTRY_POINT_COUNTER_LAST_UPDATED_AT: &str = "counter_last_updated_at"; const ENTRY_POINT_COUNTER_DECREMENT: &str = "counter_decrement"; @@ -42,12 +42,19 @@ const CONTRACT_ACCESS_UREF: &str = "counter_access_uref"; /// Entry point that increments the count value by 1. #[no_mangle] -pub extern "C" fn counter_increment() { - let uref: URef = runtime::get_key(COUNT_KEY) +pub extern "C" fn counter_inc() { + let count_uref: URef = runtime::get_key(COUNT_KEY) .unwrap_or_revert_with(ApiError::MissingKey) .into_uref() .unwrap_or_revert_with(ApiError::UnexpectedKeyVariant); - storage::add(uref, 1); // Increment the count by 1. + storage::add(count_uref, 1); // Increment the count by 1. + + let last_updated_uref = runtime::get_key(LAST_UPDATED_KEY) + .unwrap_or_revert_with(ApiError::MissingKey) + .into_uref() + .unwrap_or_revert_with(ApiError::UnexpectedKeyVariant); + let timestamp: u64 = runtime::get_blocktime().into(); + storage::write(last_updated_uref, timestamp); } /// Entry point that returns the count value. @@ -81,11 +88,18 @@ pub extern "C" fn counter_last_updated_at() { /// Entry point that decrements the count value by 1. #[no_mangle] pub extern "C" fn counter_decrement() { - let uref: URef = runtime::get_key(COUNT_KEY) + let count_uref: URef = runtime::get_key(COUNT_KEY) .unwrap_or_revert_with(ApiError::MissingKey) .into_uref() .unwrap_or_revert_with(ApiError::UnexpectedKeyVariant); - storage::add(uref, -1); // Decrement the count. + storage::add(count_uref, -1); // Decrement the count. + + let last_updated_uref = runtime::get_key(LAST_UPDATED_KEY) + .unwrap_or_revert_with(ApiError::MissingKey) + .into_uref() + .unwrap_or_revert_with(ApiError::UnexpectedKeyVariant); + let timestamp: u64 = runtime::get_blocktime().into(); + storage::write(last_updated_uref, timestamp); } /// Helper function that installs the counter contract on chain. @@ -189,14 +203,12 @@ fn upgrade_counter() { // Get the existing named keys and add a new one to it let mut named_keys = NamedKeys::default(); - named_keys.insert() + let last_updated = storage::new_uref(0_u64); + named_keys.insert(String::from(LAST_UPDATED_KEY), last_updated.into()); // Add a new contract version to the package with the new list of entry points. - let (stored_contract_hash, contract_version) = storage::add_contract_version( - counter_package_hash, - counter_entry_points, - NamedKeys::default(), - ); + let (stored_contract_hash, contract_version) = + storage::add_contract_version(counter_package_hash, counter_entry_points, named_keys); // Here we are updating the version named key with a new value. // The version named key should already be part of the account. diff --git a/tests/src/integration_tests.rs b/tests/src/integration_tests.rs index 3580e7c..ef9e1e2 100644 --- a/tests/src/integration_tests.rs +++ b/tests/src/integration_tests.rs @@ -6,18 +6,109 @@ mod tests { PRODUCTION_RUN_GENESIS_REQUEST, }; // Custom Casper types that will be used within this test. - use casper_types::{runtime_args, ContractHash, RuntimeArgs}; + use casper_types::{runtime_args, Contract, ContractHash, RuntimeArgs}; const COUNTER_V1_WASM: &str = "counter-v1.wasm"; // The first version of the contract const COUNTER_V2_WASM: &str = "counter-v2.wasm"; // The second version of the contract + const COUNTER_V3_WASM: &str = "counter-v3.wasm"; // The third version of the contract const COUNTER_CALL_WASM: &str = "counter-call.wasm"; // Session code that calls the contract const CONTRACT_KEY: &str = "counter"; // Named key referencing this contract const COUNT_KEY: &str = "count"; // Named key referencing the value to increment/decrement + const LAST_UPDATED_KEY: &str = "last_updated"; const CONTRACT_VERSION_KEY: &str = "version"; // Key maintaining the version of a contract package const ENTRY_POINT_COUNTER_DECREMENT: &str = "counter_decrement"; // Entry point to decrement the count value const ENTRY_POINT_COUNTER_INC: &str = "counter_inc"; // Entry point to increment the count value + const ENTRY_POINT_COUNTER_LAST_UPDATED_AT: &str = "counter_last_updated_at"; + + /* + Helper functions + */ + + /// Setup function to deploy a contract version + fn deploy_contract(builder: &mut InMemoryWasmTestBuilder, wasm_code: &str) -> ContractHash { + let request = + ExecuteRequestBuilder::standard(*DEFAULT_ACCOUNT_ADDR, wasm_code, runtime_args! {}) + .build(); + builder.exec(request).expect_success().commit(); + get_contract_hash_from_account(builder, CONTRACT_KEY) + } + + /// Helper function to get contract hash from account + fn get_contract_hash_from_account( + builder: &mut InMemoryWasmTestBuilder, + key: &str, + ) -> ContractHash { + builder + .get_expected_account(*DEFAULT_ACCOUNT_ADDR) + .named_keys() + .get(key) + .expect("must have contract hash key") + .into_hash() + .map(ContractHash::new) + .expect("must get contract hash") + } + + // Helper function to get the `count` value from contract storage + fn get_count(builder: &mut InMemoryWasmTestBuilder, contract_hash: ContractHash) -> i32 { + let count_key = *builder + .get_contract(contract_hash) + .expect("this contract should exist") + .named_keys() + .get(COUNT_KEY) + .expect("count uref should exist in the contract named keys"); + + builder + .query(None, count_key, &[]) + .expect("should be stored value.") + .as_cl_value() + .expect("should be cl value.") + .clone() + .into_t::() + .expect("should be i32.") + } + + // Helper function to get the `last_updated_at` value from contract storage + fn get_last_updated_at( + builder: &mut InMemoryWasmTestBuilder, + contract_hash: ContractHash, + ) -> u64 { + let count_key = *builder + .get_contract(contract_hash) + .expect("this contract should exist") + .named_keys() + .get(LAST_UPDATED_KEY) + .expect("count uref should exist in the contract named keys"); + + builder + .query(None, count_key, &[]) + .expect("should be stored value.") + .as_cl_value() + .expect("should be cl value.") + .clone() + .into_t::() + .expect("should be u64.") + } + + // Helper function to get the contract version from account storage + fn get_contract_version(builder: &mut InMemoryWasmTestBuilder) -> u32 { + let version_key = *builder + .get_account(*DEFAULT_ACCOUNT_ADDR) + .expect("should have account") + .named_keys() + .get(CONTRACT_VERSION_KEY) + .expect("version uref should exist"); + + builder + .query(None, version_key, &[]) + .expect("should be stored value.") + .as_cl_value() + .expect("should be cl value.") + .clone() + .into_t::() + .expect("should be u32.") + } /// Install version 1 of the counter contract and check its available entry points. /// Only the increment entry point should be available. @@ -518,6 +609,278 @@ mod tests { assert_eq!(decremented_count, 0); } + + #[test] + fn install_version2_and_upgrade_to_version3() { + let mut builder = InMemoryWasmTestBuilder::default(); + builder + .run_genesis(&PRODUCTION_RUN_GENESIS_REQUEST) + .commit(); + + // Install the contract v2. + let contract_v2_installation_request = ExecuteRequestBuilder::standard( + *DEFAULT_ACCOUNT_ADDR, + COUNTER_V2_WASM, + runtime_args! {}, + ) + .build(); + + builder + .exec(contract_v2_installation_request) + .expect_success() + .commit(); + + // Check the contract hash. + let contract_v2_hash = builder + .get_expected_account(*DEFAULT_ACCOUNT_ADDR) + .named_keys() + .get(CONTRACT_KEY) + .expect("must have contract hash key as part of contract creation") + .into_hash() + .map(ContractHash::new) + .expect("must get contract hash"); + + // Verify the first contract version is 2. We'll check this when we upgrade later. + let account = builder + .get_account(*DEFAULT_ACCOUNT_ADDR) + .expect("should have account"); + + let version_key = *account + .named_keys() + .get(CONTRACT_VERSION_KEY) + .expect("version uref should exist"); + + let version = builder + .query(None, version_key, &[]) + .expect("should be stored value.") + .as_cl_value() + .expect("should be cl value.") + .clone() + .into_t::() + .expect("should be u32."); + + assert_eq!(version, 2); + + // Verify the initial value of count is 0. + let mut contract = builder + .get_contract(contract_v2_hash) + .expect("this contract should exist"); + + let count_key = *contract + .named_keys() + .get(COUNT_KEY) + .expect("count uref should exist in the contract named keys"); + + let count = builder + .query(None, count_key, &[]) + .expect("should be stored value.") + .as_cl_value() + .expect("should be cl value.") + .clone() + .into_t::() + .expect("should be i32."); + + assert_eq!(count, 0); + + // Use session code to increment the counter. + let session_code_request = ExecuteRequestBuilder::standard( + *DEFAULT_ACCOUNT_ADDR, + COUNTER_CALL_WASM, + runtime_args! { + CONTRACT_KEY => contract_v2_hash + }, + ) + .build(); + + builder.exec(session_code_request).expect_success().commit(); + + // Verify the value of count is now 1. + let incremented_count = builder + .query(None, count_key, &[]) + .expect("should be stored value.") + .as_cl_value() + .expect("should be cl value.") + .clone() + .into_t::() + .expect("should be i32."); + + assert_eq!(incremented_count, 1); + + // Call the counter_last_updated_at entry point, which should not be in version 2 before the upgrade. + let contract_decrement_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + contract_v2_hash, + ENTRY_POINT_COUNTER_LAST_UPDATED_AT, + runtime_args! {}, + ) + .build(); + + // Try executing the counter_last_updated_at entry point and expect an error. + builder + .exec(contract_decrement_request) + .expect_failure() + .commit(); + + //////////////////////////////////////////////////////////////// + // Upgrade the contract. + //////////////////////////////////////////////////////////////// + let contract_v3_installation_request = ExecuteRequestBuilder::standard( + *DEFAULT_ACCOUNT_ADDR, + COUNTER_V3_WASM, + runtime_args! {}, + ) + .build(); + + builder + .exec(contract_v3_installation_request) + .expect_success() + .commit(); + + let contract_v3_hash = builder + .get_expected_account(*DEFAULT_ACCOUNT_ADDR) + .named_keys() + .get(CONTRACT_KEY) + .expect("must have contract hash key as part of contract creation") + .into_hash() + .map(ContractHash::new) + .expect("must get contract hash"); + + contract = builder + .get_contract(contract_v3_hash) + .expect("this contract should exist"); + + let last_updated_at_key = *contract + .named_keys() + .get(LAST_UPDATED_KEY) + .expect("last_updated_at uref should exist in the contract named keys"); + + // Assert that we have a new contract hash for the upgraded version. + assert_ne!(contract_v2_hash, contract_v3_hash); + + // Verify the contract version is now 3. + let account = builder + .get_account(*DEFAULT_ACCOUNT_ADDR) + .expect("should have account"); + + let version_key = *account + .named_keys() + .get(CONTRACT_VERSION_KEY) + .expect("version uref should exist"); + + let version = builder + .query(None, version_key, &[]) + .expect("should be stored value.") + .as_cl_value() + .expect("should be cl value.") + .clone() + .into_t::() + .expect("should be u32."); + + assert_eq!(version, 3); + + // Call the counter_last_udpated_at entry point to get the value stored under "last_updated_at". + let last_updated_at = builder + .query(None, last_updated_at_key, &[]) + .expect("should be stored value.") + .as_cl_value() + .expect("should be cl value.") + .clone() + .into_t::() + .expect("should be u64."); + + assert_eq!(last_updated_at, 0); + + // Call the increment entry point to increment the value stored under "count". + let contract_increment_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + contract_v3_hash, + ENTRY_POINT_COUNTER_INC, + runtime_args! {}, + ) + .with_block_time(10) + .build(); + + builder + .exec(contract_increment_request) + .expect_success() + .commit(); + + // Call the counter_last_udpated_at entry point to get the value stored under "last_updated_at". + let last_updated_at_new = builder + .query(None, last_updated_at_key, &[]) + .expect("should be stored value.") + .as_cl_value() + .expect("should be cl value.") + .clone() + .into_t::() + .expect("should be u64."); + + assert_eq!(last_updated_at_new, 10); + assert_ne!(last_updated_at, last_updated_at_new); + } + + #[test] + fn install_and_test_counter_v3() { + // Test summary: + // - Deploys the counter v3 contract. + // - Verifies initial state and version. + // - Tests increment and decrement entry points. + // - Asserts correct state updates, including last_updated_at. + + let mut builder = InMemoryWasmTestBuilder::default(); + builder + .run_genesis(&PRODUCTION_RUN_GENESIS_REQUEST) + .commit(); + + // Deploy the contract + let contract_v3_hash = deploy_contract(&mut builder, COUNTER_V3_WASM); + + // Verify the contract version + let contract_version = get_contract_version(&mut builder); + assert_eq!(contract_version, 2); + + // Verify the initial value of count + let initial_count = get_count(&mut builder, contract_v3_hash); + assert_eq!(initial_count, 0); + + // Call the increment entry point + let session_code_request = ExecuteRequestBuilder::standard( + *DEFAULT_ACCOUNT_ADDR, + COUNTER_CALL_WASM, + runtime_args! { CONTRACT_KEY => contract_v3_hash }, + ) + .with_block_time(1) + .build(); + + builder.exec(session_code_request).expect_success().commit(); + + // Verify count is now 1 and last_updated_at is 1 + let count = get_count(&mut builder, contract_v3_hash); + assert_eq!(count, 1); + let last_updated_at = get_last_updated_at(&mut builder, contract_v3_hash); + assert_eq!(last_updated_at, 1); + + // Call the decrement entry point + let contract_call_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + contract_v3_hash, + ENTRY_POINT_COUNTER_DECREMENT, + runtime_args! {}, + ) + .with_block_time(2) + .build(); + + builder + .exec(contract_call_request) + .expect_success() + .commit(); + + // Verify count is back to 0 and last_updated_at is 2 + let count = get_count(&mut builder, contract_v3_hash); + assert_eq!(count, 0); + let last_updated_at = get_last_updated_at(&mut builder, contract_v3_hash); + assert_eq!(last_updated_at, 2); + } } fn main() { From 746e75896a92609cdc87e223c7928c71e5a1db8d Mon Sep 17 00:00:00 2001 From: sczembor Date: Wed, 20 Mar 2024 14:25:23 +0100 Subject: [PATCH 3/4] refactor unit tests --- contract-v3/src/main.rs | 27 +- tests/src/integration_tests.rs | 695 ++++++++------------------------- 2 files changed, 170 insertions(+), 552 deletions(-) diff --git a/contract-v3/src/main.rs b/contract-v3/src/main.rs index bbf78af..3999092 100644 --- a/contract-v3/src/main.rs +++ b/contract-v3/src/main.rs @@ -4,27 +4,23 @@ #[cfg(not(target_arch = "wasm32"))] compile_error!("target arch should be wasm32: compile with '--target wasm32-unknown-unknown'"); -// This code imports necessary aspects of external crates that we will use in our contract code. extern crate alloc; -// Importing Rust types. use alloc::{ string::{String, ToString}, vec::Vec, }; -// Importing aspects of the Casper platform. use casper_contract::{ contract_api::{runtime, storage}, unwrap_or_revert::UnwrapOrRevert, }; -// Importing specific Casper types. use casper_types::{ api_error::ApiError, contracts::{EntryPoint, EntryPointAccess, EntryPointType, EntryPoints, NamedKeys}, CLType, CLValue, URef, }; -/// Creating constants for the various contract entry points. +/// Constands for contract entry points const ENTRY_POINT_COUNTER_INC: &str = "counter_inc"; const ENTRY_POINT_COUNTER_GET: &str = "counter_get"; const ENTRY_POINT_COUNTER_LAST_UPDATED_AT: &str = "counter_last_updated_at"; @@ -47,7 +43,7 @@ pub extern "C" fn counter_inc() { .unwrap_or_revert_with(ApiError::MissingKey) .into_uref() .unwrap_or_revert_with(ApiError::UnexpectedKeyVariant); - storage::add(count_uref, 1); // Increment the count by 1. + storage::add(count_uref, 1); let last_updated_uref = runtime::get_key(LAST_UPDATED_KEY) .unwrap_or_revert_with(ApiError::MissingKey) @@ -68,10 +64,10 @@ pub extern "C" fn counter_get() { .unwrap_or_revert_with(ApiError::Read) .unwrap_or_revert_with(ApiError::ValueNotFound); let typed_result = CLValue::from_t(result).unwrap_or_revert(); - runtime::ret(typed_result); // Return the count value. + runtime::ret(typed_result); } -/// Entry point that returns the unix time when the counter was updated last time. +/// Entry point that returns the last updated timestamp of the counter. #[no_mangle] pub extern "C" fn counter_last_updated_at() { let uref: URef = runtime::get_key(LAST_UPDATED_KEY) @@ -132,6 +128,15 @@ fn install_counter() { EntryPointType::Contract, )); + // Create an entry point to decrement the counter by 1. + counter_entry_points.add_entry_point(EntryPoint::new( + ENTRY_POINT_COUNTER_DECREMENT, + Vec::new(), + CLType::Unit, + EntryPointAccess::Public, + EntryPointType::Contract, + )); + // Create a new contract package that can be upgraded. let (stored_contract_hash, contract_version) = storage::new_contract( counter_entry_points, @@ -154,12 +159,7 @@ fn install_counter() { /// Helper function that upgrades the contract package to a new version. fn upgrade_counter() { - // In this version, we will not add any named keys. - // The named keys from the previous version should still be available. - // Create a new entry point list that includes counter_decrement. - // We need to specify all entry points, including the ones from the previous version. let mut counter_entry_points = EntryPoints::new(); - counter_entry_points.add_entry_point(EntryPoint::new( ENTRY_POINT_COUNTER_GET, Vec::new(), @@ -185,7 +185,6 @@ fn upgrade_counter() { EntryPointType::Contract, )); - // Create an entry point for getting the last_updated at timestamp counter_entry_points.add_entry_point(EntryPoint::new( ENTRY_POINT_COUNTER_LAST_UPDATED_AT, Vec::new(), diff --git a/tests/src/integration_tests.rs b/tests/src/integration_tests.rs index ef9e1e2..9110969 100644 --- a/tests/src/integration_tests.rs +++ b/tests/src/integration_tests.rs @@ -1,32 +1,31 @@ #[cfg(test)] mod tests { - // Outlining aspects of the Casper test support crate to include. use casper_engine_test_support::{ ExecuteRequestBuilder, InMemoryWasmTestBuilder, DEFAULT_ACCOUNT_ADDR, PRODUCTION_RUN_GENESIS_REQUEST, }; - // Custom Casper types that will be used within this test. - use casper_types::{runtime_args, Contract, ContractHash, RuntimeArgs}; + use casper_types::{runtime_args, ContractHash, RuntimeArgs}; - const COUNTER_V1_WASM: &str = "counter-v1.wasm"; // The first version of the contract - const COUNTER_V2_WASM: &str = "counter-v2.wasm"; // The second version of the contract - const COUNTER_V3_WASM: &str = "counter-v3.wasm"; // The third version of the contract - const COUNTER_CALL_WASM: &str = "counter-call.wasm"; // Session code that calls the contract + // Contract Wasm File Paths (Constants) + const COUNTER_V1_WASM: &str = "counter-v1.wasm"; + const COUNTER_V2_WASM: &str = "counter-v2.wasm"; + const COUNTER_V3_WASM: &str = "counter-v3.wasm"; + const COUNTER_CALL_WASM: &str = "counter-call.wasm"; - const CONTRACT_KEY: &str = "counter"; // Named key referencing this contract - const COUNT_KEY: &str = "count"; // Named key referencing the value to increment/decrement + // Contract Storage Keys (Constants) + const CONTRACT_KEY: &str = "counter"; + const COUNT_KEY: &str = "count"; const LAST_UPDATED_KEY: &str = "last_updated"; - const CONTRACT_VERSION_KEY: &str = "version"; // Key maintaining the version of a contract package + const CONTRACT_VERSION_KEY: &str = "version"; - const ENTRY_POINT_COUNTER_DECREMENT: &str = "counter_decrement"; // Entry point to decrement the count value - const ENTRY_POINT_COUNTER_INC: &str = "counter_inc"; // Entry point to increment the count value + // Contract Entry Points (Constants) + const ENTRY_POINT_COUNTER_DECREMENT: &str = "counter_decrement"; + const ENTRY_POINT_COUNTER_INC: &str = "counter_inc"; const ENTRY_POINT_COUNTER_LAST_UPDATED_AT: &str = "counter_last_updated_at"; - /* - Helper functions - */ + // Helper Functions - /// Setup function to deploy a contract version + /// Deploys a contract version to the InMemoryWasmTestBuilder fn deploy_contract(builder: &mut InMemoryWasmTestBuilder, wasm_code: &str) -> ContractHash { let request = ExecuteRequestBuilder::standard(*DEFAULT_ACCOUNT_ADDR, wasm_code, runtime_args! {}) @@ -35,7 +34,7 @@ mod tests { get_contract_hash_from_account(builder, CONTRACT_KEY) } - /// Helper function to get contract hash from account + /// Retrieves the contract hash from the default account's storage by a given key fn get_contract_hash_from_account( builder: &mut InMemoryWasmTestBuilder, key: &str, @@ -50,7 +49,7 @@ mod tests { .expect("must get contract hash") } - // Helper function to get the `count` value from contract storage + /// Retrieves the value stored under the `count` key in the given contract fn get_count(builder: &mut InMemoryWasmTestBuilder, contract_hash: ContractHash) -> i32 { let count_key = *builder .get_contract(contract_hash) @@ -69,7 +68,7 @@ mod tests { .expect("should be i32.") } - // Helper function to get the `last_updated_at` value from contract storage + /// Retrieves the value stored under the `last_updated_at` key in the given contract (if present) fn get_last_updated_at( builder: &mut InMemoryWasmTestBuilder, contract_hash: ContractHash, @@ -91,7 +90,7 @@ mod tests { .expect("should be u64.") } - // Helper function to get the contract version from account storage + /// Retrieves the contract version stored in the default account's storage fn get_contract_version(builder: &mut InMemoryWasmTestBuilder) -> u32 { let version_key = *builder .get_account(*DEFAULT_ACCOUNT_ADDR) @@ -110,91 +109,32 @@ mod tests { .expect("should be u32.") } - /// Install version 1 of the counter contract and check its available entry points. - /// Only the increment entry point should be available. - /// The decrement call should fail, because that entry point should not be in this version. - /// Test summary: - /// - Install the counter-v1.wasm contract. - /// - Check the contract hash. - /// - Check the contract version is 1. - /// - Verify the initial value of count is 0. - /// - Test the counter_inc entry point and increment the counter. - /// - Verify that the count value is now 1. - /// - Call the decrement entry point, which should fail. - /// - Ensure the count value was not decremented and is still 1. #[test] - fn install_version1_and_check_entry_points() { + fn install_and_test_version1() { + // Test summary: + // - Installs and verifies counter contract v1. + // - Verifies initial contract version and count value (0). + // - Tests counter_inc entry point in v1 and updates count. + // - Attempts (and expects failure) to call counter_decrement_at (not present in v1). + // - Ensures count value remains unchanged after failed decrement attempt. + let mut builder = InMemoryWasmTestBuilder::default(); builder .run_genesis(&PRODUCTION_RUN_GENESIS_REQUEST) .commit(); - // Install the contract. - let contract_v1_installation_request = ExecuteRequestBuilder::standard( - *DEFAULT_ACCOUNT_ADDR, - COUNTER_V1_WASM, - runtime_args! {}, - ) - .build(); - - builder - .exec(contract_v1_installation_request) - .expect_success() - .commit(); - - // Check the contract hash. - let contract_v1_hash = builder - .get_expected_account(*DEFAULT_ACCOUNT_ADDR) - .named_keys() - .get(CONTRACT_KEY) - .expect("must have contract hash key as part of contract creation") - .into_hash() - .map(ContractHash::new) - .expect("must get contract hash"); - - // Verify the first contract version is 1. - let account = builder - .get_account(*DEFAULT_ACCOUNT_ADDR) - .expect("should have account"); - - let version_key = *account - .named_keys() - .get(CONTRACT_VERSION_KEY) - .expect("version uref should exist"); - - let version = builder - .query(None, version_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be u32."); + // Deploy the counter contract (v1) + let contract_v1_hash = deploy_contract(&mut builder, COUNTER_V1_WASM); + // Verify the initial contract version is 1 + let version = get_contract_version(&mut builder); assert_eq!(version, 1); - // Verify the initial value of count is 0. - let contract = builder - .get_contract(contract_v1_hash) - .expect("this contract should exist"); - - let count_key = *contract - .named_keys() - .get(COUNT_KEY) - .expect("count uref should exist in the contract named keys"); - - let count = builder - .query(None, count_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be i32."); - + // Verify the initial value of count is 0 + let count = get_count(&mut builder, contract_v1_hash); assert_eq!(count, 0); - // Use session code to increment the counter. + // Use session code to increment the counter let session_code_request = ExecuteRequestBuilder::standard( *DEFAULT_ACCOUNT_ADDR, COUNTER_CALL_WASM, @@ -206,19 +146,11 @@ mod tests { builder.exec(session_code_request).expect_success().commit(); - // Verify the value of count is now 1. - let incremented_count = builder - .query(None, count_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be i32."); - - assert_eq!(incremented_count, 1); + // Verify the value of count is now 1 + let count = get_count(&mut builder, contract_v1_hash); + assert_eq!(count, 1); - // Call the decrement entry point, which should not be in this version. + // Call the counter_decrement entry point (not in v1) let contract_decrement_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, contract_v1_hash, @@ -227,115 +159,45 @@ mod tests { ) .build(); - // Try executing the decrement entry point and expect an error. + // Expect an error since counter_decrement_at doesn't exist in v1 builder .exec(contract_decrement_request) .expect_failure() .commit(); - // Ensure the count value was not decremented. - let current_count = builder - .query(None, count_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be i32."); - - assert_eq!(current_count, 1); + // Ensure the count value remains 1 after failed decrement attempt + let count = get_count(&mut builder, contract_v1_hash); + assert_eq!(count, 1); } - /// Install version 1 of the counter contract and check its functionality. - /// Then, upgrade the contract by installing a second Wasm for version 2. - /// Check the functionality of the second version. - /// Test summary: - /// - Install the counter-v1.wasm contract. - /// - Check the contract hash. - /// - Check the contract version is 1. - /// - Verify the initial value of count is 0. - /// - Test the counter_inc entry point and increment the counter. - /// - Verify that the count value is now 1. - /// - Call the decrement entry point, which should fail. - /// - Ensure the count value was not decremented and is still 1. - /// - UPGRADE the contract by installing the counter-v2.wasm. - /// - Assert that we have a new contract hash for the upgraded version. - /// - Verify the new contract version is 2. - /// - Increment the counter to check that counter_inc is still working after the upgrade. Count is now 2. - /// - Call the decrement entry point and verify that the count is now 1. #[test] - fn install_version1_and_upgrade_to_version2() { + fn install_and_test_version1_and_upgrade_to_version2() { + // Test summary: + // - Installs and verifies counter contract v1. + // - Tests counter_inc entry point in v1. + // - Attempts (and expects failure) to call counter_decrement_at (not present in v1). + // - Upgrades the contract to v2. + // - Verifies contract version is now 2. + // - Tests counter_inc entry point in v2. + // - Tests counter_decrement_at entry point in v2 (successful after upgrade). + let mut builder = InMemoryWasmTestBuilder::default(); builder .run_genesis(&PRODUCTION_RUN_GENESIS_REQUEST) .commit(); - // Install the first version of the contract. - let contract_v1_installation_request = ExecuteRequestBuilder::standard( - *DEFAULT_ACCOUNT_ADDR, - COUNTER_V1_WASM, - runtime_args! {}, - ) - .build(); - - builder - .exec(contract_v1_installation_request) - .expect_success() - .commit(); - - // Check the contract hash. - let contract_v1_hash = builder - .get_expected_account(*DEFAULT_ACCOUNT_ADDR) - .named_keys() - .get(CONTRACT_KEY) - .expect("must have contract hash key as part of contract creation") - .into_hash() - .map(ContractHash::new) - .expect("must get contract hash"); - - // Verify the first contract version is 1. We'll check this when we upgrade later. - let account = builder - .get_account(*DEFAULT_ACCOUNT_ADDR) - .expect("should have account"); - - let version_key = *account - .named_keys() - .get(CONTRACT_VERSION_KEY) - .expect("version uref should exist"); - - let version = builder - .query(None, version_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be u32."); + // Deploy the counter contract (v1) + let contract_v1_hash = deploy_contract(&mut builder, COUNTER_V1_WASM); + // Verify the initial contract version is 1 + let version = get_contract_version(&mut builder); assert_eq!(version, 1); - // Verify the initial value of count is 0. - let contract = builder - .get_contract(contract_v1_hash) - .expect("this contract should exist"); - - let count_key = *contract - .named_keys() - .get(COUNT_KEY) - .expect("count uref should exist in the contract named keys"); - - let count = builder - .query(None, count_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be i32."); - + // Verify the initial value of count is 0 + let count = get_count(&mut builder, contract_v1_hash); assert_eq!(count, 0); - // Use session code to increment the counter. + // Use session code to increment the counter let session_code_request = ExecuteRequestBuilder::standard( *DEFAULT_ACCOUNT_ADDR, COUNTER_CALL_WASM, @@ -347,19 +209,11 @@ mod tests { builder.exec(session_code_request).expect_success().commit(); - // Verify the value of count is now 1. - let incremented_count = builder - .query(None, count_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be i32."); - - assert_eq!(incremented_count, 1); + // Verify the value of count is now 1 + let count = get_count(&mut builder, contract_v1_hash); + assert_eq!(count, 1); - // Call the decrement entry point, which should not be in version 1 before the upgrade. + // Call the counter_decrement entry point (not in v1) let contract_decrement_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, contract_v1_hash, @@ -368,73 +222,25 @@ mod tests { ) .build(); - // Try executing the decrement entry point and expect an error. + // Expect an error since counter_decrement_at doesn't exist in v1 builder .exec(contract_decrement_request) .expect_failure() .commit(); - // Ensure the count value was not decremented. - let current_count = builder - .query(None, count_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be i32."); - - assert_eq!(current_count, 1); - - //////////////////////////////////////////////////////////////// - // Upgrade the contract. - //////////////////////////////////////////////////////////////// - let contract_v2_installation_request = ExecuteRequestBuilder::standard( - *DEFAULT_ACCOUNT_ADDR, - COUNTER_V2_WASM, - runtime_args! {}, - ) - .build(); - - builder - .exec(contract_v2_installation_request) - .expect_success() - .commit(); - - let contract_v2_hash = builder - .get_expected_account(*DEFAULT_ACCOUNT_ADDR) - .named_keys() - .get(CONTRACT_KEY) - .expect("must have contract hash key as part of contract creation") - .into_hash() - .map(ContractHash::new) - .expect("must get contract hash"); + // Ensure the count value remains 1 after failed decrement attempt + let count = get_count(&mut builder, contract_v1_hash); + assert_eq!(count, 1); - // Assert that we have a new contract hash for the upgraded version. + // Deploy the v2 contract (should upgrade from v1) + let contract_v2_hash = deploy_contract(&mut builder, COUNTER_V2_WASM); assert_ne!(contract_v1_hash, contract_v2_hash); - // Verify the contract version is now 2. - let account = builder - .get_account(*DEFAULT_ACCOUNT_ADDR) - .expect("should have account"); - - let version_key = *account - .named_keys() - .get(CONTRACT_VERSION_KEY) - .expect("version uref should exist"); - - let version = builder - .query(None, version_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be u32."); - + // Verify the contract version is now 2 (after upgrade) + let version = get_contract_version(&mut builder); assert_eq!(version, 2); - // Call the increment entry point to increment the value stored under "count". + // Use session code to increment the counter let contract_increment_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, contract_v2_hash, @@ -448,8 +254,12 @@ mod tests { .expect_success() .commit(); - // Call the decrement entry point to decrement the value stored under "count". - let contract_call_request = ExecuteRequestBuilder::contract_call_by_hash( + // Verify the count is now 2 + let count = get_count(&mut builder, contract_v2_hash); + assert_eq!(count, 2); + + // Test counter_decrement_at entry point in v2 (should work now) + let contract_decrement_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, contract_v2_hash, ENTRY_POINT_COUNTER_DECREMENT, @@ -458,107 +268,42 @@ mod tests { .build(); builder - .exec(contract_call_request) + .exec(contract_decrement_request) .expect_success() .commit(); - // Expect the counter to be 1 now. - // This tells us the contract was successfully upgraded and the decrement entry point can be called. - let decremented_count = builder - .query(None, count_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be i32."); - - assert_eq!(decremented_count, 1); + // Expect the counter to be 1 now + // This tells us the decrement functionality is available + let count = get_count(&mut builder, contract_v1_hash); + assert_eq!(count, 1); } - /// Install version 2 of the counter contract without having version 1 already on chain. - /// Test summary: - /// - Install the counter-v2.wasm contract. - /// - Check the contract hash exists. - /// - Check the contract version is 2, because the code installs and upgrades under the hood. - /// - Start checking the entry points. Verify the initial value of count is 0. - /// - Test the counter_inc entry point and increment the counter. - /// - Verify that the count value is now 1. - /// - Call the decrement entry point, which should succeed. - /// - Verify that the count is 0. #[test] - fn install_version2_directly_without_version1() { + fn install_and_test_version2() { + // Test summary: + // - Directly installs and verifies counter contract v2 (skipping v1). + // - Verifies contract version is 2. + // - Verifies initial count value is 0. + // - Tests counter_inc entry point and updates count. + // - Tests counter_decrement_at entry point and updates count. + let mut builder = InMemoryWasmTestBuilder::default(); builder .run_genesis(&PRODUCTION_RUN_GENESIS_REQUEST) .commit(); - // Install the contract. - let contract_v2_installation_request = ExecuteRequestBuilder::standard( - *DEFAULT_ACCOUNT_ADDR, - COUNTER_V2_WASM, - runtime_args! {}, - ) - .build(); - - builder - .exec(contract_v2_installation_request) - .expect_success() - .commit(); - - // Check the contract hash exists. - let contract_v2_hash = builder - .get_expected_account(*DEFAULT_ACCOUNT_ADDR) - .named_keys() - .get(CONTRACT_KEY) - .expect("must have contract hash key as part of contract creation") - .into_hash() - .map(ContractHash::new) - .expect("must get contract hash"); - - // Verify the contract version is now 2. - let account = builder - .get_account(*DEFAULT_ACCOUNT_ADDR) - .expect("should have account"); - - let version_key = *account - .named_keys() - .get(CONTRACT_VERSION_KEY) - .expect("version uref should exist"); - - let version = builder - .query(None, version_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be u32."); + // Deploy the counter contract (v2) - no v1 installation + let contract_v2_hash = deploy_contract(&mut builder, COUNTER_V2_WASM); + // Verify the contract version is now 2 (directly installed) + let version = get_contract_version(&mut builder); assert_eq!(version, 2); - // Verify the initial value of count is 0. - let contract = builder - .get_contract(contract_v2_hash) - .expect("this contract should exist"); - - let count_key = *contract - .named_keys() - .get(COUNT_KEY) - .expect("count uref should exist in the contract named keys"); - - let count = builder - .query(None, count_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be i32."); - + // Verify the initial value of count is 0 + let count = get_count(&mut builder, contract_v2_hash); assert_eq!(count, 0); - // Check that the increment entry point is working. + // Use session code to increment the counter let session_code_request = ExecuteRequestBuilder::standard( *DEFAULT_ACCOUNT_ADDR, COUNTER_CALL_WASM, @@ -570,20 +315,12 @@ mod tests { builder.exec(session_code_request).expect_success().commit(); - // Verify the value of count is now 1. - let incremented_count = builder - .query(None, count_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be i32."); - - assert_eq!(incremented_count, 1); + // Verify the value of count is now 1 + let count = get_count(&mut builder, contract_v2_hash); + assert_eq!(count, 1); - // Test the decrement entry point to decrement the value stored under "count". - let contract_call_request = ExecuteRequestBuilder::contract_call_by_hash( + // Test decrement functionality (assumed to be present in v2) + let contract_decrement_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, contract_v2_hash, ENTRY_POINT_COUNTER_DECREMENT, @@ -592,97 +329,44 @@ mod tests { .build(); builder - .exec(contract_call_request) + .exec(contract_decrement_request) .expect_success() .commit(); - // Expect the counter to be 0 now. - // This tells us the contract was successfully upgraded and the decrement entry point can be called. - let decremented_count = builder - .query(None, count_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be i32."); - - assert_eq!(decremented_count, 0); + // Expect the counter to be 0 now + let count = get_count(&mut builder, contract_v2_hash); + assert_eq!(count, 0); } #[test] - fn install_version2_and_upgrade_to_version3() { + fn install_and_test_version2_and_upgrade_to_version3() { + // Test summary: + // - Installs and verifies counter contract v2. + // - Tests counter_inc entry point in v2. + // - Attempts (and expects failure) to call counter_last_updated_at (not present in v2). + // - Upgrades the contract to v3. + // - Verifies contract version is now 3. + // - Checks that count value remains 1 after upgrade. + // - Verifies last_updated_at functionality in v3 (initially 0). + // - Tests counter_inc entry point in v3 and updates last_updated_at. + let mut builder = InMemoryWasmTestBuilder::default(); builder .run_genesis(&PRODUCTION_RUN_GENESIS_REQUEST) .commit(); - // Install the contract v2. - let contract_v2_installation_request = ExecuteRequestBuilder::standard( - *DEFAULT_ACCOUNT_ADDR, - COUNTER_V2_WASM, - runtime_args! {}, - ) - .build(); - - builder - .exec(contract_v2_installation_request) - .expect_success() - .commit(); - - // Check the contract hash. - let contract_v2_hash = builder - .get_expected_account(*DEFAULT_ACCOUNT_ADDR) - .named_keys() - .get(CONTRACT_KEY) - .expect("must have contract hash key as part of contract creation") - .into_hash() - .map(ContractHash::new) - .expect("must get contract hash"); - - // Verify the first contract version is 2. We'll check this when we upgrade later. - let account = builder - .get_account(*DEFAULT_ACCOUNT_ADDR) - .expect("should have account"); - - let version_key = *account - .named_keys() - .get(CONTRACT_VERSION_KEY) - .expect("version uref should exist"); - - let version = builder - .query(None, version_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be u32."); + // Deploy the counter contract (v2) + let contract_v2_hash = deploy_contract(&mut builder, COUNTER_V2_WASM); + // Verify the initial contract version is 2 + let version = get_contract_version(&mut builder); assert_eq!(version, 2); - // Verify the initial value of count is 0. - let mut contract = builder - .get_contract(contract_v2_hash) - .expect("this contract should exist"); - - let count_key = *contract - .named_keys() - .get(COUNT_KEY) - .expect("count uref should exist in the contract named keys"); - - let count = builder - .query(None, count_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be i32."); - + // Verify the initial value of count is 0 + let count = get_count(&mut builder, contract_v2_hash); assert_eq!(count, 0); - // Use session code to increment the counter. + // Use session code to increment the counter let session_code_request = ExecuteRequestBuilder::standard( *DEFAULT_ACCOUNT_ADDR, COUNTER_CALL_WASM, @@ -694,103 +378,43 @@ mod tests { builder.exec(session_code_request).expect_success().commit(); - // Verify the value of count is now 1. - let incremented_count = builder - .query(None, count_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be i32."); - - assert_eq!(incremented_count, 1); - - // Call the counter_last_updated_at entry point, which should not be in version 2 before the upgrade. - let contract_decrement_request = ExecuteRequestBuilder::contract_call_by_hash( - *DEFAULT_ACCOUNT_ADDR, - contract_v2_hash, - ENTRY_POINT_COUNTER_LAST_UPDATED_AT, - runtime_args! {}, - ) - .build(); + // Verify the value of count is now 1 + let count = get_count(&mut builder, contract_v2_hash); + assert_eq!(count, 1); - // Try executing the counter_last_updated_at entry point and expect an error. + // Call the counter_last_updated_at entry point (not in v2) + let contract_counter_last_updated_at_request = + ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + contract_v2_hash, + ENTRY_POINT_COUNTER_LAST_UPDATED_AT, + runtime_args! {}, + ) + .build(); + + // Expect an error since counter_last_updated_at doesn't exist in v2 builder - .exec(contract_decrement_request) + .exec(contract_counter_last_updated_at_request) .expect_failure() .commit(); - //////////////////////////////////////////////////////////////// - // Upgrade the contract. - //////////////////////////////////////////////////////////////// - let contract_v3_installation_request = ExecuteRequestBuilder::standard( - *DEFAULT_ACCOUNT_ADDR, - COUNTER_V3_WASM, - runtime_args! {}, - ) - .build(); - - builder - .exec(contract_v3_installation_request) - .expect_success() - .commit(); - - let contract_v3_hash = builder - .get_expected_account(*DEFAULT_ACCOUNT_ADDR) - .named_keys() - .get(CONTRACT_KEY) - .expect("must have contract hash key as part of contract creation") - .into_hash() - .map(ContractHash::new) - .expect("must get contract hash"); - - contract = builder - .get_contract(contract_v3_hash) - .expect("this contract should exist"); - - let last_updated_at_key = *contract - .named_keys() - .get(LAST_UPDATED_KEY) - .expect("last_updated_at uref should exist in the contract named keys"); - - // Assert that we have a new contract hash for the upgraded version. + // Deploy the v3 contract (should upgrade from v2) + let contract_v3_hash = deploy_contract(&mut builder, COUNTER_V3_WASM); assert_ne!(contract_v2_hash, contract_v3_hash); - // Verify the contract version is now 3. - let account = builder - .get_account(*DEFAULT_ACCOUNT_ADDR) - .expect("should have account"); - - let version_key = *account - .named_keys() - .get(CONTRACT_VERSION_KEY) - .expect("version uref should exist"); - - let version = builder - .query(None, version_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be u32."); - + // Verify the contract version is now 3 (after upgrade) + let version = get_contract_version(&mut builder); assert_eq!(version, 3); - // Call the counter_last_udpated_at entry point to get the value stored under "last_updated_at". - let last_updated_at = builder - .query(None, last_updated_at_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be u64."); + // Verify the count value remains 1 after upgrade + let count = get_count(&mut builder, contract_v3_hash); + assert_eq!(count, 1); + // Verify last_updated_at is 0 initially in v3 + let last_updated_at = get_last_updated_at(&mut builder, contract_v3_hash); assert_eq!(last_updated_at, 0); - // Call the increment entry point to increment the value stored under "count". + // Call the increment entry point to increment the value stored under "count" let contract_increment_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, contract_v3_hash, @@ -805,22 +429,17 @@ mod tests { .expect_success() .commit(); - // Call the counter_last_udpated_at entry point to get the value stored under "last_updated_at". - let last_updated_at_new = builder - .query(None, last_updated_at_key, &[]) - .expect("should be stored value.") - .as_cl_value() - .expect("should be cl value.") - .clone() - .into_t::() - .expect("should be u64."); + // Verify the count is now 2 + let count = get_count(&mut builder, contract_v3_hash); + assert_eq!(count, 2); - assert_eq!(last_updated_at_new, 10); - assert_ne!(last_updated_at, last_updated_at_new); + // Verify the last updated_at is now 10 + let last_updated_at = get_last_updated_at(&mut builder, contract_v3_hash); + assert_eq!(last_updated_at, 10); } #[test] - fn install_and_test_counter_v3() { + fn install_and_test_version3() { // Test summary: // - Deploys the counter v3 contract. // - Verifies initial state and version. From ad17da68208f2f0133ad0ae268c45db837ebeb20 Mon Sep 17 00:00:00 2001 From: sczembor Date: Wed, 20 Mar 2024 16:19:47 +0100 Subject: [PATCH 4/4] improve comments --- contract-v3/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contract-v3/src/main.rs b/contract-v3/src/main.rs index 3999092..2af5a62 100644 --- a/contract-v3/src/main.rs +++ b/contract-v3/src/main.rs @@ -200,8 +200,8 @@ fn upgrade_counter() { .unwrap() .into(); - // Get the existing named keys and add a new one to it - let mut named_keys = NamedKeys::default(); + // Add a new named_key + let mut named_keys = NamedKeys::new(); let last_updated = storage::new_uref(0_u64); named_keys.insert(String::from(LAST_UPDATED_KEY), last_updated.into());