diff --git a/Cargo.toml b/Cargo.toml index b67d7fff..b00622ba 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -9,6 +9,7 @@ default-members = [ "cep18-test-contract", "tests", ] +resolver = "2" [profile.release] codegen-units = 1 diff --git a/Makefile b/Makefile index e9be4c84..4ba4430f 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PINNED_TOOLCHAIN := $(shell cat rust-toolchain) +PINNED_TOOLCHAIN := $(shell cat cep18/rust-toolchain) prepare: rustup target add wasm32-unknown-unknown @@ -18,14 +18,22 @@ setup-test: build-contract cp ./target/wasm32-unknown-unknown/release/cep18.wasm tests/wasm cp ./target/wasm32-unknown-unknown/release/cep18_test_contract.wasm tests/wasm +native-test: setup-test + cd tests && cargo test --lib should_transfer_account_to_account + test: setup-test - cd tests && cargo test + cd tests && cargo test --lib clippy: cd cep18 && cargo clippy --all-targets -- -D warnings cd cep18-test-contract && cargo clippy --all-targets -- -D warnings cd tests && cargo clippy --all-targets -- -D warnings +format: + cd cep18 && cargo fmt + cd cep18-test-contract && cargo fmt + cd tests && cargo fmt + check-lint: clippy cd cep18 && cargo fmt -- --check cd cep18-test-contract && cargo fmt -- --check diff --git a/cep18-test-contract/Cargo.toml b/cep18-test-contract/Cargo.toml index 080ccb09..265ebc97 100644 --- a/cep18-test-contract/Cargo.toml +++ b/cep18-test-contract/Cargo.toml @@ -1,8 +1,7 @@ [package] name = "cep18-test-contract" -version = "1.2.0" -authors = ["MichaƂ Papierski "] -edition = "2018" +version = "2.0.0" +edition = "2021" [[bin]] name = "cep18_test_contract" @@ -12,5 +11,5 @@ doctest = false test = false [dependencies] -casper-contract = "3.0.0" -casper-types = "3.0.0" +casper-types = { git = "https://github.com/casper-network/casper-node.git", branch = "release-2.0.0-rc3"} +casper-contract = { git = "https://github.com/casper-network/casper-node.git", branch = "release-2.0.0-rc3"} diff --git a/cep18-test-contract/rust-toolchain b/cep18-test-contract/rust-toolchain new file mode 100755 index 00000000..579ba540 --- /dev/null +++ b/cep18-test-contract/rust-toolchain @@ -0,0 +1 @@ +nightly-2024-05-28 diff --git a/rustfmt.toml b/cep18-test-contract/rustfmt.toml similarity index 80% rename from rustfmt.toml rename to cep18-test-contract/rustfmt.toml index 3d2e76e9..b16fb7d5 100644 --- a/rustfmt.toml +++ b/cep18-test-contract/rustfmt.toml @@ -1,4 +1,4 @@ wrap_comments = true comment_width = 100 imports_granularity = "Crate" -edition = "2018" +edition = "2021" diff --git a/cep18-test-contract/src/main.rs b/cep18-test-contract/src/main.rs index 77ec3546..a3c9df0a 100644 --- a/cep18-test-contract/src/main.rs +++ b/cep18-test-contract/src/main.rs @@ -15,8 +15,8 @@ use casper_contract::{ }; use casper_types::{ - bytesrepr::ToBytes, runtime_args, CLTyped, ContractHash, EntryPoint, EntryPointAccess, - EntryPointType, EntryPoints, Key, Parameter, RuntimeArgs, U256, + bytesrepr::ToBytes, runtime_args, AddressableEntityHash, ApiError, CLTyped, EntryPoint, + EntryPointAccess, EntryPointType, EntryPoints, Key, Parameter, RuntimeArgs, U256, }; const CHECK_TOTAL_SUPPLY_ENTRY_POINT_NAME: &str = "check_total_supply"; @@ -54,11 +54,10 @@ fn store_result(result: T) { #[no_mangle] extern "C" fn check_total_supply() { - let token_contract: ContractHash = ContractHash::new( + let token_contract: AddressableEntityHash = runtime::get_named_arg::(TOKEN_CONTRACT_RUNTIME_ARG_NAME) - .into_hash() - .unwrap_or_revert(), - ); + .into_entity_hash() + .unwrap_or_revert_with(ApiError::User(61000)); let total_supply: U256 = runtime::call_contract( token_contract, TOTAL_SUPPLY_ENTRY_POINT_NAME, @@ -69,11 +68,10 @@ extern "C" fn check_total_supply() { #[no_mangle] extern "C" fn check_balance_of() { - let token_contract: ContractHash = ContractHash::new( + let token_contract: AddressableEntityHash = runtime::get_named_arg::(TOKEN_CONTRACT_RUNTIME_ARG_NAME) - .into_hash() - .unwrap_or_revert(), - ); + .into_entity_hash() + .unwrap_or_revert_with(ApiError::User(61001)); let address: Key = runtime::get_named_arg(ADDRESS_RUNTIME_ARG_NAME); let balance_args = runtime_args! { @@ -87,11 +85,10 @@ extern "C" fn check_balance_of() { #[no_mangle] extern "C" fn check_allowance_of() { - let token_contract: ContractHash = ContractHash::new( + let token_contract: AddressableEntityHash = runtime::get_named_arg::(TOKEN_CONTRACT_RUNTIME_ARG_NAME) - .into_hash() - .unwrap_or_revert(), - ); + .into_entity_hash() + .unwrap_or_revert_with(ApiError::User(61002)); let owner: Key = runtime::get_named_arg(OWNER_RUNTIME_ARG_NAME); let spender: Key = runtime::get_named_arg(SPENDER_RUNTIME_ARG_NAME); @@ -107,11 +104,10 @@ extern "C" fn check_allowance_of() { #[no_mangle] extern "C" fn transfer_as_stored_contract() { - let token_contract: ContractHash = ContractHash::new( + let token_contract: AddressableEntityHash = runtime::get_named_arg::(TOKEN_CONTRACT_RUNTIME_ARG_NAME) - .into_hash() - .unwrap_or_revert(), - ); + .into_entity_hash() + .unwrap_or_revert_with(ApiError::User(61003)); let recipient: Key = runtime::get_named_arg(RECIPIENT_RUNTIME_ARG_NAME); let amount: U256 = runtime::get_named_arg(AMOUNT_RUNTIME_ARG_NAME); @@ -125,11 +121,10 @@ extern "C" fn transfer_as_stored_contract() { #[no_mangle] extern "C" fn transfer_from_as_stored_contract() { - let token_contract: ContractHash = ContractHash::new( + let token_contract: AddressableEntityHash = runtime::get_named_arg::(TOKEN_CONTRACT_RUNTIME_ARG_NAME) - .into_hash() - .unwrap_or_revert(), - ); + .into_entity_hash() + .unwrap_or_revert_with(ApiError::User(61004)); let owner: Key = runtime::get_named_arg(OWNER_RUNTIME_ARG_NAME); let recipient: Key = runtime::get_named_arg(RECIPIENT_RUNTIME_ARG_NAME); let amount: U256 = runtime::get_named_arg(AMOUNT_RUNTIME_ARG_NAME); @@ -149,11 +144,10 @@ extern "C" fn transfer_from_as_stored_contract() { #[no_mangle] extern "C" fn approve_as_stored_contract() { - let token_contract: ContractHash = ContractHash::new( + let token_contract: AddressableEntityHash = runtime::get_named_arg::(TOKEN_CONTRACT_RUNTIME_ARG_NAME) - .into_hash() - .unwrap_or_revert(), - ); + .into_entity_hash() + .unwrap_or_revert_with(ApiError::User(61005)); let spender: Key = runtime::get_named_arg(SPENDER_RUNTIME_ARG_NAME); let amount: U256 = runtime::get_named_arg(AMOUNT_RUNTIME_ARG_NAME); @@ -172,69 +166,90 @@ pub extern "C" fn call() { String::from(CHECK_TOTAL_SUPPLY_ENTRY_POINT_NAME), vec![Parameter::new( TOKEN_CONTRACT_RUNTIME_ARG_NAME, - ContractHash::cl_type(), + AddressableEntityHash::cl_type(), )], <()>::cl_type(), EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ); let check_balance_of_entrypoint = EntryPoint::new( String::from(CHECK_BALANCE_OF_ENTRY_POINT_NAME), vec![ - Parameter::new(TOKEN_CONTRACT_RUNTIME_ARG_NAME, ContractHash::cl_type()), + Parameter::new( + TOKEN_CONTRACT_RUNTIME_ARG_NAME, + AddressableEntityHash::cl_type(), + ), Parameter::new(ADDRESS_RUNTIME_ARG_NAME, Key::cl_type()), ], <()>::cl_type(), EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ); let check_allowance_of_entrypoint = EntryPoint::new( String::from(CHECK_ALLOWANCE_OF_ENTRY_POINT_NAME), vec![ - Parameter::new(TOKEN_CONTRACT_RUNTIME_ARG_NAME, ContractHash::cl_type()), + Parameter::new( + TOKEN_CONTRACT_RUNTIME_ARG_NAME, + AddressableEntityHash::cl_type(), + ), Parameter::new(OWNER_RUNTIME_ARG_NAME, Key::cl_type()), Parameter::new(SPENDER_RUNTIME_ARG_NAME, Key::cl_type()), ], <()>::cl_type(), EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ); let transfer_as_stored_contract_entrypoint = EntryPoint::new( String::from(TRANSFER_AS_STORED_CONTRACT_ENTRY_POINT_NAME), vec![ - Parameter::new(TOKEN_CONTRACT_RUNTIME_ARG_NAME, ContractHash::cl_type()), + Parameter::new( + TOKEN_CONTRACT_RUNTIME_ARG_NAME, + AddressableEntityHash::cl_type(), + ), Parameter::new(RECIPIENT_RUNTIME_ARG_NAME, Key::cl_type()), Parameter::new(AMOUNT_RUNTIME_ARG_NAME, U256::cl_type()), ], <()>::cl_type(), EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ); let approve_as_stored_contract_entrypoint = EntryPoint::new( String::from(APPROVE_AS_STORED_CONTRACT_ENTRY_POINT_NAME), vec![ - Parameter::new(TOKEN_CONTRACT_RUNTIME_ARG_NAME, ContractHash::cl_type()), + Parameter::new( + TOKEN_CONTRACT_RUNTIME_ARG_NAME, + AddressableEntityHash::cl_type(), + ), Parameter::new(SPENDER_RUNTIME_ARG_NAME, Key::cl_type()), Parameter::new(AMOUNT_RUNTIME_ARG_NAME, U256::cl_type()), ], <()>::cl_type(), EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ); let transfer_from_as_stored_contract_entrypoint = EntryPoint::new( String::from(TRANSFER_FROM_AS_STORED_CONTRACT_ENTRY_POINT_NAME), vec![ - Parameter::new(TOKEN_CONTRACT_RUNTIME_ARG_NAME, ContractHash::cl_type()), + Parameter::new( + TOKEN_CONTRACT_RUNTIME_ARG_NAME, + AddressableEntityHash::cl_type(), + ), Parameter::new(OWNER_RUNTIME_ARG_NAME, Key::cl_type()), Parameter::new(RECIPIENT_RUNTIME_ARG_NAME, Key::cl_type()), Parameter::new(AMOUNT_RUNTIME_ARG_NAME, U256::cl_type()), ], <()>::cl_type(), EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ); entry_points.add_entry_point(check_total_supply_entrypoint); @@ -249,5 +264,6 @@ pub extern "C" fn call() { None, Some(CEP18_TEST_CALL_KEY.to_string()), None, + None, ); } diff --git a/cep18/Cargo.toml b/cep18/Cargo.toml index 358d2818..6b04bf2d 100644 --- a/cep18/Cargo.toml +++ b/cep18/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "cep18" -version = "1.2.0" -edition = "2018" +version = "2.0.0" +edition = "2021" description = "A library for developing CEP-18 tokens for the Casper network." readme = "README.md" documentation = "https://docs.rs/casper-cep18" @@ -18,8 +18,8 @@ test = false [dependencies] base64 = { version = "0.20.0", default-features = false, features = ["alloc"] } -casper-contract = "3.0.0" -casper-types = "3.0.0" +casper-types = { git = "https://github.com/casper-network/casper-node.git", branch = "release-2.0.0-rc3"} +casper-contract = { git = "https://github.com/casper-network/casper-node.git", branch = "release-2.0.0-rc3"} hex = { version = "0.4.3", default-features = false } once_cell = { version = "1.16.0", default-features = false } -casper-event-standard = { version = "0.4.1", default-features = false } +casper-event-standard = { git = "https://github.com/deuszex/casper-event-standard", branch = "condor", default-features = false } \ No newline at end of file diff --git a/cep18/rust-toolchain b/cep18/rust-toolchain new file mode 100755 index 00000000..11157ba8 --- /dev/null +++ b/cep18/rust-toolchain @@ -0,0 +1 @@ +nightly-2024-05-28 \ No newline at end of file diff --git a/cep18/rustfmt.toml b/cep18/rustfmt.toml new file mode 100644 index 00000000..b16fb7d5 --- /dev/null +++ b/cep18/rustfmt.toml @@ -0,0 +1,4 @@ +wrap_comments = true +comment_width = 100 +imports_granularity = "Crate" +edition = "2021" diff --git a/cep18/src/allowances.rs b/cep18/src/allowances.rs index aef789a4..a909f2b6 100644 --- a/cep18/src/allowances.rs +++ b/cep18/src/allowances.rs @@ -7,7 +7,7 @@ use casper_contract::{ }; use casper_types::{bytesrepr::ToBytes, Key, URef, U256}; -use crate::{constants::ALLOWANCES, utils}; +use crate::{constants::ALLOWANCES, utils, Cep18Error}; #[inline] pub(crate) fn get_allowances_uref() -> URef { @@ -17,8 +17,16 @@ pub(crate) fn get_allowances_uref() -> URef { /// Creates a dictionary item key for an (owner, spender) pair. pub(crate) fn make_dictionary_item_key(owner: Key, spender: Key) -> String { let mut preimage = Vec::new(); - preimage.append(&mut owner.to_bytes().unwrap_or_revert()); - preimage.append(&mut spender.to_bytes().unwrap_or_revert()); + preimage.append( + &mut owner + .to_bytes() + .unwrap_or_revert_with(Cep18Error::FailedToConvertBytes), + ); + preimage.append( + &mut spender + .to_bytes() + .unwrap_or_revert_with(Cep18Error::FailedToConvertBytes), + ); let key_bytes = runtime::blake2b(&preimage); hex::encode(key_bytes) @@ -34,6 +42,6 @@ pub(crate) fn write_allowance_to(allowance_uref: URef, owner: Key, spender: Key, pub(crate) fn read_allowance_from(allowances_uref: URef, owner: Key, spender: Key) -> U256 { let dictionary_item_key = make_dictionary_item_key(owner, spender); storage::dictionary_get(allowances_uref, &dictionary_item_key) - .unwrap_or_revert() + .unwrap_or_revert_with(Cep18Error::FailedToGetDictionaryValue) .unwrap_or_default() } diff --git a/cep18/src/balances.rs b/cep18/src/balances.rs index d9d497e4..04e9f2e4 100644 --- a/cep18/src/balances.rs +++ b/cep18/src/balances.rs @@ -10,7 +10,9 @@ use crate::{constants::BALANCES, error::Cep18Error, utils}; /// since stringified Keys are too long to be used as dictionary keys. #[inline] fn make_dictionary_item_key(owner: Key) -> String { - let preimage = owner.to_bytes().unwrap_or_revert(); + let preimage = owner + .to_bytes() + .unwrap_or_revert_with(Cep18Error::FailedToConvertBytes); // NOTE: As for now dictionary item keys are limited to 64 characters only. Instead of using // hashing (which will effectively hash a hash) we'll use base64. Preimage is 33 bytes for // both used Key variants, and approximated base64-encoded length will be 4 * (33 / 3) ~ 44 @@ -38,7 +40,7 @@ pub(crate) fn read_balance_from(balances_uref: URef, address: Key) -> U256 { let dictionary_item_key = make_dictionary_item_key(address); storage::dictionary_get(balances_uref, &dictionary_item_key) - .unwrap_or_revert() + .unwrap_or_revert_with(Cep18Error::FailedToGetDictionaryValue) .unwrap_or_default() } diff --git a/cep18/src/constants.rs b/cep18/src/constants.rs index c99251e5..1086052e 100644 --- a/cep18/src/constants.rs +++ b/cep18/src/constants.rs @@ -12,6 +12,10 @@ pub const BALANCES: &str = "balances"; pub const ALLOWANCES: &str = "allowances"; /// Name of named-key for `total_supply` pub const TOTAL_SUPPLY: &str = "total_supply"; +pub const EVENTS: &str = "events"; +pub const REVERT: &str = "revert"; +pub const ERRORS: &str = "errors"; +pub const CONDOR: &str = "condor"; pub const HASH_KEY_NAME_PREFIX: &str = "cep18_contract_package_"; pub const ACCESS_KEY_NAME_PREFIX: &str = "cep18_contract_package_access_"; @@ -44,6 +48,10 @@ pub const BURN_ENTRY_POINT_NAME: &str = "burn"; pub const INIT_ENTRY_POINT_NAME: &str = "init"; /// Name of `change_security` entry point. pub const CHANGE_SECURITY_ENTRY_POINT_NAME: &str = "change_security"; +pub const MIGRATE_USER_BALANCE_KEYS_ENTRY_POINT_NAME: &str = "migrate_user_balance_keys"; +pub const MIGRATE_USER_ALLOWANCE_KEYS_ENTRY_POINT_NAME: &str = "migrate_user_allowance_keys"; +pub const MIGRATE_USER_SEC_KEYS_ENTRY_POINT_NAME: &str = "migrate_sec_keys"; +pub const CHANGE_EVENTS_MODE_ENTRY_POINT_NAME: &str = "change_events_mode"; pub const INCREASE_ALLOWANCE_ENTRY_POINT_NAME: &str = "increase_allowance"; pub const DECREASE_ALLOWANCE_ENTRY_POINT_NAME: &str = "decrease_allowance"; @@ -59,11 +67,11 @@ pub const AMOUNT: &str = "amount"; /// Name of `recipient` runtime argument. pub const RECIPIENT: &str = "recipient"; pub const PACKAGE_HASH: &str = "package_hash"; +pub const CONTRACT_HASH: &str = "contract_hash"; pub const EVENTS_MODE: &str = "events_mode"; pub const SECURITY_BADGES: &str = "security_badges"; pub const ADMIN_LIST: &str = "admin_list"; pub const MINTER_LIST: &str = "minter_list"; -pub const BURNER_LIST: &str = "burner_list"; pub const NONE_LIST: &str = "none_list"; -pub const MINT_AND_BURN_LIST: &str = "mint_and_burn_list"; pub const ENABLE_MINT_BURN: &str = "enable_mint_burn"; +pub const USER_KEY_MAP: &str = "user_key_map"; diff --git a/cep18/src/entry_points.rs b/cep18/src/entry_points.rs index 7c62c73f..0834a244 100644 --- a/cep18/src/entry_points.rs +++ b/cep18/src/entry_points.rs @@ -8,13 +8,28 @@ use casper_types::{ use crate::constants::{ ADDRESS, ALLOWANCE_ENTRY_POINT_NAME, AMOUNT, APPROVE_ENTRY_POINT_NAME, - BALANCE_OF_ENTRY_POINT_NAME, BURN_ENTRY_POINT_NAME, CHANGE_SECURITY_ENTRY_POINT_NAME, - DECIMALS_ENTRY_POINT_NAME, DECREASE_ALLOWANCE_ENTRY_POINT_NAME, - INCREASE_ALLOWANCE_ENTRY_POINT_NAME, INIT_ENTRY_POINT_NAME, MINT_ENTRY_POINT_NAME, - NAME_ENTRY_POINT_NAME, OWNER, RECIPIENT, SPENDER, SYMBOL_ENTRY_POINT_NAME, - TOTAL_SUPPLY_ENTRY_POINT_NAME, TRANSFER_ENTRY_POINT_NAME, TRANSFER_FROM_ENTRY_POINT_NAME, + BALANCE_OF_ENTRY_POINT_NAME, BURN_ENTRY_POINT_NAME, CHANGE_EVENTS_MODE_ENTRY_POINT_NAME, + CHANGE_SECURITY_ENTRY_POINT_NAME, CONDOR, DECIMALS_ENTRY_POINT_NAME, + DECREASE_ALLOWANCE_ENTRY_POINT_NAME, EVENTS, EVENTS_MODE, INCREASE_ALLOWANCE_ENTRY_POINT_NAME, + INIT_ENTRY_POINT_NAME, MIGRATE_USER_ALLOWANCE_KEYS_ENTRY_POINT_NAME, + MIGRATE_USER_BALANCE_KEYS_ENTRY_POINT_NAME, MIGRATE_USER_SEC_KEYS_ENTRY_POINT_NAME, + MINT_ENTRY_POINT_NAME, NAME_ENTRY_POINT_NAME, OWNER, RECIPIENT, REVERT, SPENDER, + SYMBOL_ENTRY_POINT_NAME, TOTAL_SUPPLY_ENTRY_POINT_NAME, TRANSFER_ENTRY_POINT_NAME, + TRANSFER_FROM_ENTRY_POINT_NAME, }; +/// Returns the `condor` entry point. +pub fn condor() -> EntryPoint { + EntryPoint::new( + String::from(CONDOR), + Vec::new(), + String::cl_type(), + EntryPointAccess::Public, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, + ) +} + /// Returns the `name` entry point. pub fn name() -> EntryPoint { EntryPoint::new( @@ -22,7 +37,8 @@ pub fn name() -> EntryPoint { Vec::new(), String::cl_type(), EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ) } @@ -33,7 +49,8 @@ pub fn symbol() -> EntryPoint { Vec::new(), String::cl_type(), EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ) } @@ -48,7 +65,8 @@ pub fn transfer_from() -> EntryPoint { ], CLType::Unit, EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ) } @@ -62,7 +80,8 @@ pub fn allowance() -> EntryPoint { ], U256::cl_type(), EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ) } @@ -76,7 +95,8 @@ pub fn approve() -> EntryPoint { ], CLType::Unit, EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ) } @@ -90,7 +110,8 @@ pub fn increase_allowance() -> EntryPoint { ], CLType::Unit, EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ) } @@ -104,7 +125,8 @@ pub fn decrease_allowance() -> EntryPoint { ], CLType::Unit, EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ) } @@ -118,7 +140,8 @@ pub fn transfer() -> EntryPoint { ], CLType::Unit, EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ) } @@ -129,7 +152,8 @@ pub fn balance_of() -> EntryPoint { vec![Parameter::new(ADDRESS, Key::cl_type())], U256::cl_type(), EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ) } @@ -140,7 +164,8 @@ pub fn total_supply() -> EntryPoint { Vec::new(), U256::cl_type(), EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ) } @@ -151,7 +176,8 @@ pub fn decimals() -> EntryPoint { Vec::new(), u8::cl_type(), EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ) } @@ -165,7 +191,8 @@ pub fn burn() -> EntryPoint { ], CLType::Unit, EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ) } @@ -179,7 +206,68 @@ pub fn mint() -> EntryPoint { ], CLType::Unit, EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, + ) +} + +/// Returns the `migrate_user_allowance_keys` entry point. +pub fn migrate_user_allowance_keys() -> EntryPoint { + EntryPoint::new( + String::from(MIGRATE_USER_ALLOWANCE_KEYS_ENTRY_POINT_NAME), + vec![ + Parameter::new(EVENTS, bool::cl_type()), + Parameter::new(REVERT, bool::cl_type()), + // Parameter::new(USER_KEY_MAP, BTreeMap::::cl_type()), + ], + CLType::Unit, + EntryPointAccess::Public, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, + ) +} + +/// Returns the `migrate_user_balance_keys` entry point. +pub fn migrate_user_balance_keys() -> EntryPoint { + EntryPoint::new( + String::from(MIGRATE_USER_BALANCE_KEYS_ENTRY_POINT_NAME), + vec![ + Parameter::new(EVENTS, bool::cl_type()), + Parameter::new(REVERT, bool::cl_type()), + // Parameter::new(USER_KEY_MAP, BTreeMap::::cl_type()), + ], + CLType::Unit, + EntryPointAccess::Public, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, + ) +} + +/// Returns the `migrate_user_sec_keys` entry point. +pub fn migrate_user_sec_keys() -> EntryPoint { + EntryPoint::new( + String::from(MIGRATE_USER_SEC_KEYS_ENTRY_POINT_NAME), + vec![ + Parameter::new(EVENTS, bool::cl_type()), + Parameter::new(REVERT, bool::cl_type()), + // Parameter::new(USER_KEY_MAP, BTreeMap::::cl_type()), + ], + CLType::Unit, + EntryPointAccess::Public, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, + ) +} + +/// Returns the `migrate_user_sec_keys` entry point. +pub fn change_events_mode() -> EntryPoint { + EntryPoint::new( + String::from(CHANGE_EVENTS_MODE_ENTRY_POINT_NAME), + vec![Parameter::new(EVENTS_MODE, u8::cl_type())], + CLType::Unit, + EntryPointAccess::Public, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ) } @@ -199,7 +287,8 @@ pub fn change_security() -> EntryPoint { ], CLType::Unit, EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ) } @@ -210,7 +299,8 @@ pub fn init() -> EntryPoint { Vec::new(), CLType::Unit, EntryPointAccess::Public, - EntryPointType::Contract, + EntryPointType::Called, + casper_types::EntryPointPayment::Caller, ) } @@ -219,6 +309,7 @@ pub fn generate_entry_points() -> EntryPoints { let mut entry_points = EntryPoints::new(); entry_points.add_entry_point(init()); entry_points.add_entry_point(name()); + entry_points.add_entry_point(condor()); entry_points.add_entry_point(symbol()); entry_points.add_entry_point(decimals()); entry_points.add_entry_point(total_supply()); @@ -232,5 +323,9 @@ pub fn generate_entry_points() -> EntryPoints { entry_points.add_entry_point(change_security()); entry_points.add_entry_point(burn()); entry_points.add_entry_point(mint()); + entry_points.add_entry_point(migrate_user_allowance_keys()); + entry_points.add_entry_point(migrate_user_balance_keys()); + entry_points.add_entry_point(migrate_user_sec_keys()); + entry_points.add_entry_point(change_events_mode()); entry_points } diff --git a/cep18/src/error.rs b/cep18/src/error.rs index 0a437711..f1a11d25 100644 --- a/cep18/src/error.rs +++ b/cep18/src/error.rs @@ -48,6 +48,24 @@ pub enum Cep18Error { CannotTargetSelfUser = 60017, InvalidBurnTarget = 60018, MissingPackageHashForUpgrade = 60019, + MissingContractHashForUpgrade = 60020, + InvalidKeyType = 60021, + KeyTypeMigrationMismatch = 60022, + FailedToWriteMessage = 60023, + FailedToReturnEntryPointResult = 60024, + FailedToRetrieveImmediateCaller = 60025, + FailedToCreateDictionary = 60026, + FailedToWriteToDictionary = 60027, + FailedToConvertBytes = 60028, + FailedToChangeBalance = 60029, + FailedToChangeAllowance = 60030, + FailedToChangeTotalSupply = 60031, + FailedToGetDictionaryValue = 60032, + FailedToReadFromStorage = 60033, + FailedToGetKey = 60034, + FailedToDisableContractVersion = 60035, + FailedToInsertToSecurityList = 60036, + UrefNotFound = 60037, } impl From for ApiError { diff --git a/cep18/src/events.rs b/cep18/src/events.rs index de70023f..734ffb4d 100644 --- a/cep18/src/events.rs +++ b/cep18/src/events.rs @@ -1,27 +1,36 @@ use core::convert::TryFrom; -use alloc::collections::BTreeMap; -use casper_contract::unwrap_or_revert::UnwrapOrRevert; +use alloc::{collections::BTreeMap, format, string::String, vec::Vec}; +use casper_contract::{contract_api::runtime, unwrap_or_revert::UnwrapOrRevert}; use casper_types::{Key, U256}; use crate::{ - constants::EVENTS_MODE, + constants::{EVENTS, EVENTS_MODE}, modalities::EventsMode, utils::{read_from, SecurityBadge}, + Cep18Error, }; use casper_event_standard::{emit, Event, Schemas}; pub fn record_event_dictionary(event: Event) { - let events_mode: EventsMode = - EventsMode::try_from(read_from::(EVENTS_MODE)).unwrap_or_revert(); + let events_mode: EventsMode = EventsMode::try_from(read_from::(EVENTS_MODE)) + .unwrap_or_revert_with(Cep18Error::InvalidEventsMode); match events_mode { EventsMode::NoEvents => {} EventsMode::CES => ces(event), + EventsMode::Native => runtime::emit_message(EVENTS, &format!("{event:?}").into()) + .unwrap_or_revert(), + EventsMode::NativeNCES => { + runtime::emit_message(EVENTS, &format!("{event:?}").into()) + .unwrap_or_revert(); + ces(event); + } } } +#[derive(Debug)] pub enum Event { Mint(Mint), Burn(Burn), @@ -31,6 +40,9 @@ pub enum Event { Transfer(Transfer), TransferFrom(TransferFrom), ChangeSecurity(ChangeSecurity), + BalanceMigration(BalanceMigration), + AllowanceMigration(AllowanceMigration), + ChangeEventsMode(ChangeEventsMode), } #[derive(Event, Debug, PartialEq, Eq)] @@ -89,6 +101,30 @@ pub struct ChangeSecurity { pub sec_change_map: BTreeMap, } +/// `success_list` -> Vec<(Key,Key)> where the tuple is the pair of old_key and new_key. +/// `failure_map` -> BTreeMap where the key is the provided old_key, and the String +/// value is the failure reason, while the String is the failure reason. +#[derive(Event, Debug, PartialEq, Eq)] +pub struct BalanceMigration { + pub success_map: Vec<(Key, Key)>, + pub failure_map: BTreeMap, +} + +/// `success_list` -> Vec<(Key,Key)> where one tuple is the pair of old_key and new_key for the +/// spender and another is the same for owner. +/// `failure_map` -> BTreeMap<(Key,Key), String> where the Key tuples is the pair of old_spender_key +/// and old_owner_key, while the String is the failure reason. +#[derive(Event, Debug, PartialEq, Eq)] +pub struct AllowanceMigration { + pub success_map: Vec<((Key, Key), (Key, Key))>, + pub failure_map: BTreeMap<(Key, Option), String>, +} + +#[derive(Event, Debug, PartialEq, Eq)] +pub struct ChangeEventsMode { + pub events_mode: u8, +} + fn ces(event: Event) { match event { Event::Mint(ev) => emit(ev), @@ -99,14 +135,19 @@ fn ces(event: Event) { Event::Transfer(ev) => emit(ev), Event::TransferFrom(ev) => emit(ev), Event::ChangeSecurity(ev) => emit(ev), + Event::BalanceMigration(ev) => emit(ev), + Event::AllowanceMigration(ev) => emit(ev), + Event::ChangeEventsMode(ev) => emit(ev), } } pub fn init_events() { - let events_mode: EventsMode = - EventsMode::try_from(read_from::(EVENTS_MODE)).unwrap_or_revert(); + let events_mode: EventsMode = EventsMode::try_from(read_from::(EVENTS_MODE)) + .unwrap_or_revert_with(Cep18Error::InvalidEventsMode); - if events_mode == EventsMode::CES { + if [EventsMode::CES, EventsMode::NativeNCES].contains(&events_mode) + && runtime::get_key(casper_event_standard::EVENTS_DICT).is_none() + { let schemas = Schemas::new() .with::() .with::() @@ -115,7 +156,10 @@ pub fn init_events() { .with::() .with::() .with::() - .with::(); + .with::() + .with::() + .with::() + .with::(); casper_event_standard::init(schemas); } } diff --git a/cep18/src/main.rs b/cep18/src/main.rs index 87ef4391..77d32c31 100644 --- a/cep18/src/main.rs +++ b/cep18/src/main.rs @@ -26,50 +26,77 @@ use entry_points::generate_entry_points; use casper_contract::{ contract_api::{ - runtime::{self, get_caller, get_key, get_named_arg, put_key, revert}, - storage::{self, dictionary_put}, + runtime::{ + self, call_contract, get_caller, get_key, get_named_arg, manage_message_topic, put_key, + revert, + }, + storage::{self, dictionary_put, named_dictionary_get, named_dictionary_put}, }, unwrap_or_revert::UnwrapOrRevert, }; use casper_types::{ - bytesrepr::ToBytes, contracts::NamedKeys, runtime_args, CLValue, ContractHash, - ContractPackageHash, Key, RuntimeArgs, U256, + addressable_entity::{EntityKindTag, NamedKeys}, + bytesrepr::ToBytes, + contract_messages::MessageTopicOperation, + runtime_args, AddressableEntityHash, ApiError, CLValue, EntityAddr, Key, PackageHash, U256, }; use constants::{ ACCESS_KEY_NAME_PREFIX, ADDRESS, ADMIN_LIST, ALLOWANCES, AMOUNT, BALANCES, - CONTRACT_NAME_PREFIX, CONTRACT_VERSION_PREFIX, DECIMALS, ENABLE_MINT_BURN, EVENTS_MODE, + CHANGE_EVENTS_MODE_ENTRY_POINT_NAME, CONDOR, CONTRACT_HASH, CONTRACT_NAME_PREFIX, + CONTRACT_VERSION_PREFIX, DECIMALS, ENABLE_MINT_BURN, ERRORS, EVENTS, EVENTS_MODE, HASH_KEY_NAME_PREFIX, INIT_ENTRY_POINT_NAME, MINTER_LIST, NAME, NONE_LIST, OWNER, PACKAGE_HASH, - RECIPIENT, SECURITY_BADGES, SPENDER, SYMBOL, TOTAL_SUPPLY, + RECIPIENT, REVERT, SECURITY_BADGES, SPENDER, SYMBOL, TOTAL_SUPPLY, USER_KEY_MAP, }; pub use error::Cep18Error; use events::{ - init_events, Burn, ChangeSecurity, DecreaseAllowance, Event, IncreaseAllowance, Mint, - SetAllowance, Transfer, TransferFrom, + init_events, AllowanceMigration, BalanceMigration, Burn, ChangeEventsMode, ChangeSecurity, + DecreaseAllowance, Event, IncreaseAllowance, Mint, SetAllowance, Transfer, TransferFrom, }; +use modalities::EventsMode; use utils::{ - get_immediate_caller_address, get_total_supply_uref, read_from, read_total_supply_from, - sec_check, write_total_supply_to, SecurityBadge, + get_immediate_caller_address, get_optional_named_arg_with_user_errors, get_total_supply_uref, + read_from, read_total_supply_from, sec_check, write_total_supply_to, SecurityBadge, }; +#[no_mangle] +pub extern "C" fn condor() { + runtime::ret( + CLValue::from_t(utils::read_from::(CONDOR)) + .unwrap_or_revert_with(Cep18Error::FailedToReturnEntryPointResult), + ); +} + #[no_mangle] pub extern "C" fn name() { - runtime::ret(CLValue::from_t(utils::read_from::(NAME)).unwrap_or_revert()); + runtime::ret( + CLValue::from_t(utils::read_from::(NAME)) + .unwrap_or_revert_with(Cep18Error::FailedToReturnEntryPointResult), + ); } #[no_mangle] pub extern "C" fn symbol() { - runtime::ret(CLValue::from_t(utils::read_from::(SYMBOL)).unwrap_or_revert()); + runtime::ret( + CLValue::from_t(utils::read_from::(SYMBOL)) + .unwrap_or_revert_with(Cep18Error::FailedToReturnEntryPointResult), + ); } #[no_mangle] pub extern "C" fn decimals() { - runtime::ret(CLValue::from_t(utils::read_from::(DECIMALS)).unwrap_or_revert()); + runtime::ret( + CLValue::from_t(utils::read_from::(DECIMALS)) + .unwrap_or_revert_with(Cep18Error::FailedToReturnEntryPointResult), + ); } #[no_mangle] pub extern "C" fn total_supply() { - runtime::ret(CLValue::from_t(utils::read_from::(TOTAL_SUPPLY)).unwrap_or_revert()); + runtime::ret( + CLValue::from_t(utils::read_from::(TOTAL_SUPPLY)) + .unwrap_or_revert_with(Cep18Error::FailedToReturnEntryPointResult), + ); } #[no_mangle] @@ -77,7 +104,9 @@ pub extern "C" fn balance_of() { let address: Key = runtime::get_named_arg(ADDRESS); let balances_uref = get_balances_uref(); let balance = balances::read_balance_from(balances_uref, address); - runtime::ret(CLValue::from_t(balance).unwrap_or_revert()); + runtime::ret( + CLValue::from_t(balance).unwrap_or_revert_with(Cep18Error::FailedToReturnEntryPointResult), + ); } #[no_mangle] @@ -86,12 +115,15 @@ pub extern "C" fn allowance() { let owner: Key = runtime::get_named_arg(OWNER); let allowances_uref = get_allowances_uref(); let val: U256 = read_allowance_from(allowances_uref, owner, spender); - runtime::ret(CLValue::from_t(val).unwrap_or_revert()); + runtime::ret( + CLValue::from_t(val).unwrap_or_revert_with(Cep18Error::FailedToReturnEntryPointResult), + ); } #[no_mangle] pub extern "C" fn approve() { - let owner = utils::get_immediate_caller_address().unwrap_or_revert(); + let owner = utils::get_immediate_caller_address() + .unwrap_or_revert_with(Cep18Error::FailedToRetrieveImmediateCaller); let spender: Key = runtime::get_named_arg(SPENDER); if spender == owner { revert(Cep18Error::CannotTargetSelfUser); @@ -108,7 +140,8 @@ pub extern "C" fn approve() { #[no_mangle] pub extern "C" fn decrease_allowance() { - let owner = utils::get_immediate_caller_address().unwrap_or_revert(); + let owner = utils::get_immediate_caller_address() + .unwrap_or_revert_with(Cep18Error::FailedToRetrieveImmediateCaller); let spender: Key = runtime::get_named_arg(SPENDER); if spender == owner { revert(Cep18Error::CannotTargetSelfUser); @@ -128,7 +161,8 @@ pub extern "C" fn decrease_allowance() { #[no_mangle] pub extern "C" fn increase_allowance() { - let owner = utils::get_immediate_caller_address().unwrap_or_revert(); + let owner = utils::get_immediate_caller_address() + .unwrap_or_revert_with(Cep18Error::FailedToRetrieveImmediateCaller); let spender: Key = runtime::get_named_arg(SPENDER); if spender == owner { revert(Cep18Error::CannotTargetSelfUser); @@ -148,7 +182,8 @@ pub extern "C" fn increase_allowance() { #[no_mangle] pub extern "C" fn transfer() { - let sender = utils::get_immediate_caller_address().unwrap_or_revert(); + let sender = utils::get_immediate_caller_address() + .unwrap_or_revert_with(Cep18Error::FailedToRetrieveImmediateCaller); let recipient: Key = runtime::get_named_arg(RECIPIENT); if sender == recipient { revert(Cep18Error::CannotTargetSelfUser); @@ -165,7 +200,8 @@ pub extern "C" fn transfer() { #[no_mangle] pub extern "C" fn transfer_from() { - let spender = utils::get_immediate_caller_address().unwrap_or_revert(); + let spender = utils::get_immediate_caller_address() + .unwrap_or_revert_with(Cep18Error::FailedToRetrieveImmediateCaller); let recipient: Key = runtime::get_named_arg(RECIPIENT); let owner: Key = runtime::get_named_arg(OWNER); if owner == recipient { @@ -180,8 +216,7 @@ pub extern "C" fn transfer_from() { let spender_allowance: U256 = read_allowance_from(allowances_uref, owner, spender); let new_spender_allowance = spender_allowance .checked_sub(amount) - .ok_or(Cep18Error::InsufficientAllowance) - .unwrap_or_revert(); + .unwrap_or_revert_with(Cep18Error::InsufficientAllowance); transfer_balance(owner, recipient, amount).unwrap_or_revert(); write_allowance_to(allowances_uref, owner, spender, new_spender_allowance); @@ -210,22 +245,30 @@ pub extern "C" fn mint() { let balance = read_balance_from(balances_uref, owner); balance .checked_add(amount) - .ok_or(Cep18Error::Overflow) - .unwrap_or_revert() + .unwrap_or_revert_with(Cep18Error::Overflow) }; let new_total_supply = { let total_supply: U256 = read_total_supply_from(total_supply_uref); total_supply .checked_add(amount) - .ok_or(Cep18Error::Overflow) + .ok_or(ApiError::None) + .map_err(|_: ApiError| { + runtime::emit_message( + ERRORS, + &"Cannot add to total_supply as it flow over U256::MAX value".into(), + ) + .unwrap_or_revert(); + Cep18Error::Overflow + }) .unwrap_or_revert() }; write_balance_to(balances_uref, owner, new_balance); write_total_supply_to(total_supply_uref, new_total_supply); + events::record_event_dictionary(Event::Mint(Mint { recipient: owner, amount, - })) + })); } #[no_mangle] @@ -236,7 +279,10 @@ pub extern "C" fn burn() { let owner: Key = runtime::get_named_arg(OWNER); - if owner != get_immediate_caller_address().unwrap_or_revert() { + if owner + != get_immediate_caller_address() + .unwrap_or_revert_with(Cep18Error::FailedToRetrieveImmediateCaller) + { revert(Cep18Error::InvalidBurnTarget); } @@ -247,15 +293,14 @@ pub extern "C" fn burn() { let balance = read_balance_from(balances_uref, owner); balance .checked_sub(amount) - .ok_or(Cep18Error::InsufficientBalance) - .unwrap_or_revert() + .unwrap_or_revert_with(Cep18Error::InsufficientBalance) }; let new_total_supply = { let total_supply = read_total_supply_from(total_supply_uref); total_supply .checked_sub(amount) .ok_or(Cep18Error::Overflow) - .unwrap_or_revert() + .unwrap_or_revert_with(Cep18Error::FailedToChangeTotalSupply) }; write_balance_to(balances_uref, owner, new_balance); write_total_supply_to(total_supply_uref, new_total_supply); @@ -271,31 +316,54 @@ pub extern "C" fn init() { } let package_hash = get_named_arg::(PACKAGE_HASH); put_key(PACKAGE_HASH, package_hash); - storage::new_dictionary(ALLOWANCES).unwrap_or_revert(); - let balances_uref = storage::new_dictionary(BALANCES).unwrap_or_revert(); + + let contract_hash = get_named_arg::(CONTRACT_HASH); + put_key(CONTRACT_HASH, contract_hash); + + storage::new_dictionary(ALLOWANCES).unwrap_or_revert_with(Cep18Error::FailedToCreateDictionary); + let balances_uref = storage::new_dictionary(BALANCES) + .unwrap_or_revert_with(Cep18Error::FailedToCreateDictionary); let initial_supply = runtime::get_named_arg(TOTAL_SUPPLY); let caller = get_caller(); - write_balance_to(balances_uref, caller.into(), initial_supply); + write_balance_to( + balances_uref, + Key::AddressableEntity(EntityAddr::Account(caller.value())), + initial_supply, + ); - let security_badges_dict = storage::new_dictionary(SECURITY_BADGES).unwrap_or_revert(); + let security_badges_dict = storage::new_dictionary(SECURITY_BADGES) + .unwrap_or_revert_with(Cep18Error::FailedToCreateDictionary); dictionary_put( security_badges_dict, - &base64::encode(Key::from(get_caller()).to_bytes().unwrap_or_revert()), + &base64::encode( + Key::AddressableEntity(EntityAddr::Account(caller.value())) + .to_bytes() + .unwrap_or_revert_with(Cep18Error::FailedToConvertBytes), + ), SecurityBadge::Admin, ); let admin_list: Option> = - utils::get_optional_named_arg_with_user_errors(ADMIN_LIST, Cep18Error::InvalidAdminList); + get_optional_named_arg_with_user_errors(ADMIN_LIST, Cep18Error::InvalidAdminList); let minter_list: Option> = - utils::get_optional_named_arg_with_user_errors(MINTER_LIST, Cep18Error::InvalidMinterList); + get_optional_named_arg_with_user_errors(MINTER_LIST, Cep18Error::InvalidMinterList); - init_events(); + let events_mode: EventsMode = EventsMode::try_from(get_named_arg::(EVENTS_MODE)) + .unwrap_or_revert_with(Cep18Error::InvalidEventsMode); + + if [EventsMode::CES, EventsMode::NativeNCES].contains(&events_mode) { + init_events(); + } if let Some(minter_list) = minter_list { for minter in minter_list { dictionary_put( security_badges_dict, - &base64::encode(minter.to_bytes().unwrap_or_revert()), + &base64::encode( + minter + .to_bytes() + .unwrap_or_revert_with(Cep18Error::FailedToConvertBytes), + ), SecurityBadge::Minter, ); } @@ -304,15 +372,20 @@ pub extern "C" fn init() { for admin in admin_list { dictionary_put( security_badges_dict, - &base64::encode(admin.to_bytes().unwrap_or_revert()), + &base64::encode( + admin + .to_bytes() + .unwrap_or_revert_with(Cep18Error::FailedToConvertBytes), + ), SecurityBadge::Admin, ); } } + events::record_event_dictionary(Event::Mint(Mint { - recipient: caller.into(), + recipient: Key::AddressableEntity(EntityAddr::Account(caller.value())), amount: initial_supply, - })) + })); } /// Admin EntryPoint to manipulate the security access granted to users. @@ -328,11 +401,11 @@ pub extern "C" fn change_security() { } sec_check(vec![SecurityBadge::Admin]); let admin_list: Option> = - utils::get_optional_named_arg_with_user_errors(ADMIN_LIST, Cep18Error::InvalidAdminList); + get_optional_named_arg_with_user_errors(ADMIN_LIST, Cep18Error::InvalidAdminList); let minter_list: Option> = - utils::get_optional_named_arg_with_user_errors(MINTER_LIST, Cep18Error::InvalidMinterList); + get_optional_named_arg_with_user_errors(MINTER_LIST, Cep18Error::InvalidMinterList); let none_list: Option> = - utils::get_optional_named_arg_with_user_errors(NONE_LIST, Cep18Error::InvalidNoneList); + get_optional_named_arg_with_user_errors(NONE_LIST, Cep18Error::InvalidNoneList); let mut badge_map: BTreeMap = BTreeMap::new(); if let Some(minter_list) = minter_list { @@ -351,38 +424,384 @@ pub extern "C" fn change_security() { } } - let caller = get_immediate_caller_address().unwrap_or_revert(); + let caller = get_immediate_caller_address() + .unwrap_or_revert_with(Cep18Error::FailedToRetrieveImmediateCaller); badge_map.remove(&caller); utils::change_sec_badge(&badge_map); events::record_event_dictionary(Event::ChangeSecurity(ChangeSecurity { - admin: get_immediate_caller_address().unwrap_or_revert(), + admin: caller, sec_change_map: badge_map, })); } + +/// Entrypoint to migrate user and contract keys regarding balances provided as argument from 1.x to +/// 2.x key/hash storage version. Argument is a single BTreeMap. The key is the already +/// stored key in the system (previously Key::Hash or Key::Account), while the bool value is the +/// verification for the key's state of being an account or a contract (true for account, false for +/// contract). Going forward these will be stored are Key::AddressableEntity(EntityAddr::Account) +/// and Key::AddressableEntity(EntityAddr::SmartContract) respectively. +#[no_mangle] +pub fn migrate_user_balance_keys() { + let event_on: bool = get_named_arg(EVENTS); + let revert_on: bool = get_named_arg(REVERT); + let mut success_map: Vec<(Key, Key)> = Vec::new(); + let mut failure_map: BTreeMap = BTreeMap::new(); + + let keys: BTreeMap = get_named_arg(USER_KEY_MAP); + let balances_uref = get_balances_uref(); + for (old_key, is_account_flag) in keys { + let migrated_key = match old_key { + Key::Account(account_hash) => { + if !is_account_flag { + if event_on { + failure_map.insert(old_key, String::from("FlagMismatch")); + } else if revert_on { + revert(Cep18Error::KeyTypeMigrationMismatch) + } + continue; + } + Key::AddressableEntity(EntityAddr::Account(account_hash.value())) + } + Key::Hash(contract_package) => { + if is_account_flag { + if event_on { + failure_map.insert(old_key, String::from("FlagMismatch")); + } else if revert_on { + revert(Cep18Error::KeyTypeMigrationMismatch) + } + continue; + } + Key::Package(contract_package) + } + _ => { + if event_on { + failure_map.insert(old_key, String::from("WrongKeyType")); + } else if revert_on { + revert(Cep18Error::InvalidKeyType) + } + continue; + } + }; + let old_balance = read_balance_from(balances_uref, old_key); + if old_balance > U256::zero() { + let new_key_existing_balance = read_balance_from(balances_uref, migrated_key); + write_balance_to(balances_uref, old_key, U256::zero()); + write_balance_to( + balances_uref, + migrated_key, + new_key_existing_balance + old_balance, + ) + } else if event_on { + failure_map.insert(old_key, String::from("NoOldKeyBal")); + } else if revert_on { + revert(Cep18Error::InsufficientBalance) + } + success_map.push((old_key, migrated_key)); + } + + if event_on { + events::record_event_dictionary(Event::BalanceMigration(BalanceMigration { + success_map, + failure_map, + })); + } +} + +/// Entrypoint to migrate users' and contracts' keys regarding allowances provided as argument from +/// 1.x to 2.x key/hash storage version. Argument is a single BTreeMap>. The key is +/// the already stored key in the system (previously Key::Hash or Key::Account), while the Vec +/// is a list of allowance keys, whose owners have already been migrated. Going forward these will +/// be stored are Key::AddressableEntity(EntityAddr::Account) +/// and Key::AddressableEntity(EntityAddr::SmartContract) respectively. +#[no_mangle] +pub fn migrate_user_allowance_keys() { + let event_on: bool = get_named_arg(EVENTS); + let revert_on: bool = get_named_arg(REVERT); + let mut success_map: Vec<((Key, Key), (Key, Key))> = Vec::new(); + let mut failure_map: BTreeMap<(Key, Option), String> = BTreeMap::new(); + + let keys: BTreeMap<(Key, bool), Vec<(Key, bool)>> = get_named_arg(USER_KEY_MAP); + let allowances_uref = get_allowances_uref(); + for ((spender_key, spender_is_account_flag), allowance_owner_keys) in keys { + let migrated_spender_key = match spender_key { + Key::Account(account_hash) => { + if !spender_is_account_flag { + if event_on { + failure_map + .insert((spender_key, None), String::from("SpenderFlagMismatch")); + continue; + } else if revert_on { + revert(Cep18Error::KeyTypeMigrationMismatch) + } + } + Key::AddressableEntity(EntityAddr::Account(account_hash.value())) + } + Key::Hash(contract_package) => { + if spender_is_account_flag { + if event_on { + failure_map + .insert((spender_key, None), String::from("SpenderFlagMismatch")); + continue; + } else if revert_on { + revert(Cep18Error::KeyTypeMigrationMismatch) + } + } + Key::Package(contract_package) + } + _ => { + if event_on { + failure_map.insert((spender_key, None), String::from("SpenderWrongKeyType")); + } else if revert_on { + revert(Cep18Error::InvalidKeyType) + } + continue; + } + }; + for (owner_key, owner_is_account_flag) in allowance_owner_keys { + let migrated_owner_key = match owner_key { + Key::Account(account_hash) => { + if !owner_is_account_flag { + if event_on { + failure_map.insert( + (spender_key, Some(owner_key)), + String::from("OwnerFlagMismatch"), + ); + } else if revert_on { + revert(Cep18Error::KeyTypeMigrationMismatch) + } + continue; + } + Key::AddressableEntity(EntityAddr::Account(account_hash.value())) + } + Key::Hash(contract_package) => { + if owner_is_account_flag { + if event_on { + failure_map.insert( + (spender_key, Some(owner_key)), + String::from("OwnerFlagMismatch"), + ); + continue; + } else if revert_on { + revert(Cep18Error::KeyTypeMigrationMismatch) + } + } + Key::Package(contract_package) + } + _ => { + if event_on { + failure_map.insert( + (spender_key, Some(owner_key)), + String::from("OwnerWrongKeyType"), + ); + } else if revert_on { + revert(Cep18Error::InvalidKeyType) + } + continue; + } + }; + let old_allowance = + read_allowance_from(allowances_uref, owner_key, spender_key); + if old_allowance > U256::zero() { + let new_key_existing_allowance = + read_allowance_from(allowances_uref, migrated_owner_key, migrated_spender_key); + write_allowance_to( + allowances_uref, + owner_key, + spender_key, + U256::zero(), + ); + write_allowance_to( + allowances_uref, + migrated_owner_key, + migrated_spender_key, + new_key_existing_allowance + old_allowance, + ) + } else if event_on { + failure_map.insert( + (spender_key, Some(owner_key)), + String::from("NoOldKeyAllowance"), + ); + } else if revert_on { + revert(Cep18Error::InsufficientAllowance) + } + success_map.push(( + (spender_key, migrated_spender_key), + (owner_key, migrated_owner_key), + )); + } + } + if event_on { + events::record_event_dictionary(Event::AllowanceMigration(AllowanceMigration { + success_map, + failure_map, + })); + } +} + +#[no_mangle] +pub fn migrate_sec_keys() { + let event_on: bool = get_named_arg(EVENTS); + let revert_on: bool = get_named_arg(REVERT); + let mut success_map: Vec<(Key, Key)> = Vec::new(); + let mut failure_map: BTreeMap = BTreeMap::new(); + + let keys: BTreeMap = get_named_arg(USER_KEY_MAP); + for (old_key, is_account_flag) in keys { + let migrated_key = match old_key { + Key::Account(account_hash) => { + if !is_account_flag { + if event_on { + failure_map.insert(old_key, String::from("FlagMismatch")); + } else if revert_on { + revert(Cep18Error::KeyTypeMigrationMismatch) + } + continue; + } + Key::AddressableEntity(EntityAddr::Account(account_hash.value())) + } + Key::Hash(contract_package) => { + if is_account_flag { + if event_on { + failure_map.insert(old_key, String::from("FlagMismatch")); + } else if revert_on { + revert(Cep18Error::KeyTypeMigrationMismatch) + } + continue; + } + Key::Package(contract_package) + } + _ => { + if event_on { + failure_map.insert(old_key, String::from("WrongKeyType")); + } else if revert_on { + revert(Cep18Error::InvalidKeyType) + } + continue; + } + }; + let old_user_sec_key = old_key + .to_bytes() + .unwrap_or_revert_with(Cep18Error::FailedToConvertBytes); + let old_encoded_user_sec_key = base64::encode(old_user_sec_key); + + let user_sec_key = migrated_key + .to_bytes() + .unwrap_or_revert_with(Cep18Error::FailedToConvertBytes); + let migrated_encoded_user_sec_key = base64::encode(user_sec_key); + + let sec: SecurityBadge = named_dictionary_get(SECURITY_BADGES, &old_encoded_user_sec_key) + .unwrap_or_revert_with(Cep18Error::FailedToGetDictionaryValue) + .unwrap_or(SecurityBadge::None); + if [SecurityBadge::Admin, SecurityBadge::Minter].contains(&sec) { + named_dictionary_put(SECURITY_BADGES, &migrated_encoded_user_sec_key, sec); + } else if event_on { + failure_map.insert(old_key, String::from("NoValidBadge")); + } else if revert_on { + revert(Cep18Error::InsufficientRights) + } + success_map.push((old_key, migrated_key)); + } + + if event_on { + events::record_event_dictionary(Event::BalanceMigration(BalanceMigration { + success_map, + failure_map, + })); + } +} + +#[no_mangle] +fn change_events_mode() { + sec_check(vec![SecurityBadge::Admin]); + let events_mode: EventsMode = EventsMode::try_from(get_named_arg::(EVENTS_MODE)) + .unwrap_or_revert_with(Cep18Error::InvalidEventsMode); + + let events_mode_u8 = events_mode as u8; + put_key(EVENTS_MODE, storage::new_uref(events_mode_u8).into()); + + match events_mode { + EventsMode::NoEvents => {} + EventsMode::CES => init_events(), + EventsMode::Native => { + let _ = manage_message_topic(EVENTS, MessageTopicOperation::Add); + } + EventsMode::NativeNCES => { + init_events(); + let _ = manage_message_topic(EVENTS, MessageTopicOperation::Add); + } + }; + events::record_event_dictionary(Event::ChangeEventsMode(ChangeEventsMode { + events_mode: events_mode_u8, + })); +} + pub fn upgrade(name: &str) { let entry_points = generate_entry_points(); - let contract_package_hash = runtime::get_key(&format!("{HASH_KEY_NAME_PREFIX}{name}")) - .unwrap_or_revert() - .into_hash() - .map(ContractPackageHash::new) - .unwrap_or_revert_with(Cep18Error::MissingPackageHashForUpgrade); + let old_contract_package_hash = match runtime::get_key(&format!("{HASH_KEY_NAME_PREFIX}{name}")) + .unwrap_or_revert_with(Cep18Error::FailedToGetKey) + { + Key::Hash(contract_hash) => contract_hash, + Key::AddressableEntity(EntityAddr::SmartContract(contract_hash)) => contract_hash, + Key::Package(package_hash) => package_hash, + _ => revert(Cep18Error::MissingPackageHashForUpgrade), + }; + let contract_package_hash = PackageHash::new(old_contract_package_hash); + + let previous_contract_hash = match runtime::get_key(&format!("{CONTRACT_NAME_PREFIX}{name}")) + .unwrap_or_revert_with(Cep18Error::FailedToGetKey) + { + Key::Hash(contract_hash) => contract_hash, + Key::AddressableEntity(EntityAddr::SmartContract(contract_hash)) => contract_hash, + _ => revert(Cep18Error::MissingContractHashForUpgrade), + }; + let converted_previous_contract_hash = AddressableEntityHash::new(previous_contract_hash); + + let events_mode = + get_optional_named_arg_with_user_errors::(EVENTS_MODE, Cep18Error::InvalidEventsMode); + + let mut message_topics = BTreeMap::new(); + match get_key(CONDOR) { + Some(_) => {} + None => { + message_topics.insert(EVENTS.to_string(), MessageTopicOperation::Add); + message_topics.insert(ERRORS.to_string(), MessageTopicOperation::Add); + put_key(CONDOR, storage::new_uref(CONDOR).into()); + } + } - let previous_contract_hash = runtime::get_key(&format!("{CONTRACT_NAME_PREFIX}{name}")) - .unwrap_or_revert() - .into_hash() - .map(ContractHash::new) - .unwrap_or_revert_with(Cep18Error::MissingPackageHashForUpgrade); + let (contract_hash, contract_version) = storage::add_contract_version( + contract_package_hash, + entry_points, + NamedKeys::new(), + message_topics, + ); + + storage::disable_contract_version(contract_package_hash, converted_previous_contract_hash) + .unwrap_or_revert_with(Cep18Error::FailedToDisableContractVersion); + + if let Some(events_mode_u8) = events_mode { + call_contract::<()>( + contract_hash, + CHANGE_EVENTS_MODE_ENTRY_POINT_NAME, + runtime_args! { + EVENTS_MODE => events_mode_u8 + }, + ); + } - let (contract_hash, contract_version) = - storage::add_contract_version(contract_package_hash, entry_points, NamedKeys::new()); + // migrate old ContractPackageHash as PackageHash so it's stored in a uniform format with the + // new `new_contract` implementation + runtime::put_key( + &format!("{HASH_KEY_NAME_PREFIX}{name}"), + contract_package_hash.into(), + ); - storage::disable_contract_version(contract_package_hash, previous_contract_hash) - .unwrap_or_revert(); + // ContractHash in previous versions, now AddressableEntityHash runtime::put_key( &format!("{CONTRACT_NAME_PREFIX}{name}"), - contract_hash.into(), + Key::addressable_entity_key(EntityKindTag::SmartContract, contract_hash), ); runtime::put_key( &format!("{CONTRACT_VERSION_PREFIX}{name}"), @@ -395,19 +814,17 @@ pub fn install_contract(name: &str) { let decimals: u8 = runtime::get_named_arg(DECIMALS); let total_supply: U256 = runtime::get_named_arg(TOTAL_SUPPLY); let events_mode: u8 = - utils::get_optional_named_arg_with_user_errors(EVENTS_MODE, Cep18Error::InvalidEventsMode) + get_optional_named_arg_with_user_errors(EVENTS_MODE, Cep18Error::InvalidEventsMode) .unwrap_or(0u8); let admin_list: Option> = - utils::get_optional_named_arg_with_user_errors(ADMIN_LIST, Cep18Error::InvalidAdminList); + get_optional_named_arg_with_user_errors(ADMIN_LIST, Cep18Error::InvalidAdminList); let minter_list: Option> = - utils::get_optional_named_arg_with_user_errors(MINTER_LIST, Cep18Error::InvalidMinterList); + get_optional_named_arg_with_user_errors(MINTER_LIST, Cep18Error::InvalidMinterList); - let enable_mint_burn: u8 = utils::get_optional_named_arg_with_user_errors( - ENABLE_MINT_BURN, - Cep18Error::InvalidEnableMBFlag, - ) - .unwrap_or(0); + let enable_mint_burn: u8 = + get_optional_named_arg_with_user_errors(ENABLE_MINT_BURN, Cep18Error::InvalidEnableMBFlag) + .unwrap_or(0); let mut named_keys = NamedKeys::new(); named_keys.insert(NAME.to_string(), storage::new_uref(name).into()); @@ -427,37 +844,49 @@ pub fn install_contract(name: &str) { ); let entry_points = generate_entry_points(); - let hash_key_name = format!("{HASH_KEY_NAME_PREFIX}{name}"); + let mut message_topics = BTreeMap::new(); + message_topics.insert(ERRORS.to_string(), MessageTopicOperation::Add); + if [EventsMode::Native, EventsMode::NativeNCES] + .contains(&events_mode.try_into().unwrap_or_default()) + { + message_topics.insert(EVENTS.to_string(), MessageTopicOperation::Add); + }; + let hash_key_name = format!("{HASH_KEY_NAME_PREFIX}{name}"); let (contract_hash, contract_version) = storage::new_contract( entry_points, Some(named_keys), Some(hash_key_name.clone()), Some(format!("{ACCESS_KEY_NAME_PREFIX}{name}")), + Some(message_topics), ); - let package_hash = runtime::get_key(&hash_key_name).unwrap_or_revert(); + let package_hash = + runtime::get_key(&hash_key_name).unwrap_or_revert_with(Cep18Error::FailedToGetKey); + + let contract_hash_key = + Key::addressable_entity_key(EntityKindTag::SmartContract, contract_hash); // Store contract_hash and contract_version under the keys CONTRACT_NAME and CONTRACT_VERSION - runtime::put_key( - &format!("{CONTRACT_NAME_PREFIX}{name}"), - contract_hash.into(), - ); + runtime::put_key(&format!("{CONTRACT_NAME_PREFIX}{name}"), contract_hash_key); runtime::put_key( &format!("{CONTRACT_VERSION_PREFIX}{name}"), storage::new_uref(contract_version).into(), ); // Call contract to initialize it - let mut init_args = runtime_args! {TOTAL_SUPPLY => total_supply, PACKAGE_HASH => package_hash}; + let mut init_args = runtime_args! {TOTAL_SUPPLY => total_supply, PACKAGE_HASH => package_hash, CONTRACT_HASH => contract_hash_key, EVENTS_MODE => events_mode}; if let Some(admin_list) = admin_list { - init_args.insert(ADMIN_LIST, admin_list).unwrap_or_revert(); + init_args + .insert(ADMIN_LIST, admin_list) + .unwrap_or_revert_with(Cep18Error::FailedToInsertToSecurityList); } if let Some(minter_list) = minter_list { init_args .insert(MINTER_LIST, minter_list) - .unwrap_or_revert(); + .unwrap_or_revert_with(Cep18Error::FailedToInsertToSecurityList); } + put_key(CONDOR, storage::new_uref(CONDOR).into()); runtime::call_contract::<()>(contract_hash, INIT_ENTRY_POINT_NAME, init_args); } diff --git a/cep18/src/modalities.rs b/cep18/src/modalities.rs index f4352f7c..38c8b073 100644 --- a/cep18/src/modalities.rs +++ b/cep18/src/modalities.rs @@ -3,11 +3,14 @@ use core::convert::TryFrom; use crate::Cep18Error; #[repr(u8)] -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Default, Copy, Clone)] #[allow(clippy::upper_case_acronyms)] pub enum EventsMode { + #[default] NoEvents = 0, CES = 1, + Native = 2, + NativeNCES = 3, } impl TryFrom for EventsMode { @@ -17,6 +20,8 @@ impl TryFrom for EventsMode { match value { 0 => Ok(EventsMode::NoEvents), 1 => Ok(EventsMode::CES), + 2 => Ok(EventsMode::Native), + 3 => Ok(EventsMode::NativeNCES), _ => Err(Cep18Error::InvalidEventsMode), } } diff --git a/cep18/src/utils.rs b/cep18/src/utils.rs index bb6d74a7..5159779e 100644 --- a/cep18/src/utils.rs +++ b/cep18/src/utils.rs @@ -1,7 +1,7 @@ //! Implementation details. use core::convert::TryInto; -use alloc::{collections::BTreeMap, vec, vec::Vec}; +use alloc::{collections::BTreeMap, format, vec, vec::Vec}; use casper_contract::{ contract_api::{ self, @@ -12,14 +12,15 @@ use casper_contract::{ unwrap_or_revert::UnwrapOrRevert, }; use casper_types::{ + account::AccountHash, api_error, bytesrepr::{self, FromBytes, ToBytes}, - system::CallStackElement, - ApiError, CLTyped, Key, URef, U256, + system::Caller, + ApiError, CLTyped, EntityAddr, Key, URef, U256, }; use crate::{ - constants::{SECURITY_BADGES, TOTAL_SUPPLY}, + constants::{ERRORS, SECURITY_BADGES, TOTAL_SUPPLY}, error::Cep18Error, }; @@ -27,8 +28,9 @@ use crate::{ pub(crate) fn get_uref(name: &str) -> URef { let key = runtime::get_key(name) .ok_or(ApiError::MissingKey) - .unwrap_or_revert(); - key.try_into().unwrap_or_revert() + .unwrap_or_revert_with(Cep18Error::FailedToGetKey); + key.try_into() + .unwrap_or_revert_with(Cep18Error::InvalidKeyType) } /// Reads value from a named key. @@ -37,7 +39,9 @@ where T: FromBytes + CLTyped, { let uref = get_uref(name); - let value: T = storage::read(uref).unwrap_or_revert().unwrap_or_revert(); + let value: T = storage::read(uref) + .unwrap_or_revert_with(Cep18Error::UrefNotFound) + .unwrap_or_revert_with(Cep18Error::FailedToReadFromStorage); value } @@ -45,7 +49,7 @@ where /// /// For `Session` and `StoredSession` variants it will return account hash, and for `StoredContract` /// case it will use contract package hash as the address. -fn call_stack_element_to_address(call_stack_element: CallStackElement) -> Key { +fn call_stack_element_to_address(call_stack_element: Caller) -> Key { match call_stack_element { CallStackElement::Session { account_hash } => Key::from(account_hash), CallStackElement::StoredSession { account_hash, .. } => { @@ -60,11 +64,33 @@ fn call_stack_element_to_address(call_stack_element: CallStackElement) -> Key { } } +/// Returns the call stack. +pub fn get_call_stack() -> Vec { + let (call_stack_len, result_size) = { + let mut call_stack_len: usize = 0; + let mut result_size: usize = 0; + let ret = unsafe { + ext_ffi::casper_load_call_stack( + &mut call_stack_len as *mut usize, + &mut result_size as *mut usize, + ) + }; + api_error::result_from(ret).unwrap_or_revert(); + (call_stack_len, result_size) + }; + if call_stack_len == 0 { + return Vec::new(); + } + let bytes = read_host_buffer(result_size).unwrap_or_revert(); + bytesrepr::deserialize(bytes).unwrap_or_revert() +} + /// Gets the immediate session caller of the current execution. /// -/// This function ensures that Contracts can participate and no middleman (contract) acts for users. +/// This function ensures that Contracts can participate and no middleman (contract) acts for +/// users. pub(crate) fn get_immediate_caller_address() -> Result { - let call_stack = runtime::get_call_stack(); + let call_stack = get_call_stack(); call_stack .into_iter() .rev() @@ -78,7 +104,9 @@ pub fn get_total_supply_uref() -> URef { } pub(crate) fn read_total_supply_from(uref: URef) -> U256 { - storage::read(uref).unwrap_or_revert().unwrap_or_revert() + storage::read(uref) + .unwrap_or_revert_with(Cep18Error::UrefNotFound) + .unwrap_or_revert_with(Cep18Error::FailedToReadFromStorage) } /// Writes a total supply to a specific [`URef`]. @@ -98,7 +126,10 @@ pub fn get_named_arg_size(name: &str) -> Option { match api_error::result_from(ret) { Ok(_) => Some(arg_size), Err(ApiError::MissingArgument) => None, - Err(e) => runtime::revert(e), + Err(e) => { + runtime::emit_message(ERRORS, &format!("{e:?}").into()).unwrap_or_revert(); + runtime::revert(e) + } } } @@ -135,13 +166,20 @@ pub fn get_named_arg_with_user_errors( api_error::result_from(ret).map(|_| data) }; // Assumed to be safe as `get_named_arg_size` checks the argument already - res.unwrap_or_revert_with(Cep18Error::FailedToGetArgBytes) + res.map_err(|err| { + let _ = runtime::emit_message(ERRORS, &format!("{err:?}").into()); + Cep18Error::FailedToGetArgBytes + }) + .unwrap_or_revert() } else { // Avoids allocation with 0 bytes and a call to get_named_arg Vec::new() }; - bytesrepr::deserialize(arg_bytes).map_err(|_| invalid) + bytesrepr::deserialize(arg_bytes).map_err(|err| { + let _ = runtime::emit_message(ERRORS, &format!("{err:?}").into()); + invalid + }) } #[repr(u8)] @@ -184,16 +222,50 @@ impl FromBytes for SecurityBadge { pub fn sec_check(allowed_badge_list: Vec) { let caller = get_immediate_caller_address() - .unwrap_or_revert() + .unwrap_or_revert_with(Cep18Error::FailedToRetrieveImmediateCaller); + let hash: [u8; 32] = match caller { + Key::Account(account) => account.0, + Key::Hash(hash) => hash, + Key::AddressableEntity(addressable) => match addressable { + EntityAddr::System(_) => revert(Cep18Error::InvalidKeyType), + EntityAddr::Account(account) => account, + EntityAddr::SmartContract(hash) => hash, + }, + _ => revert(Cep18Error::InvalidKeyType), + }; + let new_style_key_bytes = Key::AddressableEntity(EntityAddr::Account(hash)) .to_bytes() - .unwrap_or_revert(); - if !allowed_badge_list.contains( - &dictionary_get::(get_uref(SECURITY_BADGES), &base64::encode(caller)) - .unwrap_or_revert() - .unwrap_or_revert_with(Cep18Error::InsufficientRights), - ) { - revert(Cep18Error::InsufficientRights) - } + .unwrap_or_revert_with(Cep18Error::FailedToConvertBytes); + match dictionary_get::( + get_uref(SECURITY_BADGES), + &base64::encode(new_style_key_bytes), + ) + .unwrap_or_revert_with(Cep18Error::FailedToGetDictionaryValue) + { + Some(badge) => { + if !allowed_badge_list.contains(&badge) { + revert(Cep18Error::InsufficientRights) + } + } + None => { + let old_style_key_bytes = Key::Account(AccountHash::new(hash)) + .to_bytes() + .unwrap_or_revert_with(Cep18Error::FailedToConvertBytes); + match dictionary_get::( + get_uref(SECURITY_BADGES), + &base64::encode(old_style_key_bytes), + ) + .unwrap_or_revert_with(Cep18Error::FailedToGetDictionaryValue) + { + Some(badge) => { + if !allowed_badge_list.contains(&badge) { + revert(Cep18Error::InsufficientRights) + } + } + None => revert(Cep18Error::InsufficientRights), + }; + } + }; } pub fn change_sec_badge(badge_map: &BTreeMap) { @@ -201,7 +273,10 @@ pub fn change_sec_badge(badge_map: &BTreeMap) { for (&user, &badge) in badge_map { dictionary_put( sec_uref, - &base64::encode(user.to_bytes().unwrap_or_revert()), + &base64::encode( + user.to_bytes() + .unwrap_or_revert_with(Cep18Error::FailedToConvertBytes), + ), badge, ) } diff --git a/docs/4-tests.md b/docs/4-tests.md index cbf72421..c613bb83 100644 --- a/docs/4-tests.md +++ b/docs/4-tests.md @@ -76,8 +76,8 @@ Expand the example below to see a subset of the required constants for this proj // File https://github.com/casper-ecosystem/cep18/blob/dev/tests/src/utility/installer_request_builders.rs use casper_engine_test_support::{ - ExecuteRequestBuilder, InMemoryWasmTestBuilder, DEFAULT_ACCOUNT_ADDR, - MINIMUM_ACCOUNT_CREATION_BALANCE, PRODUCTION_RUN_GENESIS_REQUEST, + ExecuteRequestBuilder, LmdbWasmTestBuilder, DEFAULT_ACCOUNT_ADDR, + MINIMUM_ACCOUNT_CREATION_BALANCE, LOCAL_GENESIS_REQUEST, }; use casper_execution_engine::core::engine_state::ExecuteRequest; use casper_types::{ @@ -121,7 +121,7 @@ pub(crate) cep18_test_contract_package: ContractPackageHash, // Setting up the test instance of CEP-18. -pub(crate) fn setup() -> (InMemoryWasmTestBuilder, TestContext) { +pub(crate) fn setup() -> (LmdbWasmTestBuilder, TestContext) { setup_with_args(runtime_args! { ARG_NAME => TOKEN_NAME, ARG_SYMBOL => TOKEN_SYMBOL, @@ -132,9 +132,9 @@ pub(crate) fn setup() -> (InMemoryWasmTestBuilder, TestContext) { // Establishing test accounts. -pub(crate) fn setup_with_args(install_args: RuntimeArgs) -> (InMemoryWasmTestBuilder, TestContext) { - let mut builder = InMemoryWasmTestBuilder::default(); - builder.run_genesis(&PRODUCTION_RUN_GENESIS_REQUEST); +pub(crate) fn setup_with_args(install_args: RuntimeArgs) -> (LmdbWasmTestBuilder, TestContext) { + let mut builder = LmdbWasmTestBuilder::default(); + builder.run_genesis(&LOCAL_GENESIS_REQUEST); let id: Option = None; let transfer_1_args = runtime_args! { @@ -219,7 +219,7 @@ The following code snippet is an example function that tests the ability to tran // File https://github.com/casper-ecosystem/cep18/blob/dev/tests/src/utility/installer_request_builders.rs pub(crate) fn test_cep18_transfer( - builder: &mut InMemoryWasmTestBuilder, + builder: &mut LmdbWasmTestBuilder, test_context: &TestContext, sender1: Key, recipient1: Key, diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 870b86b6..8838d0ee 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -1,16 +1,23 @@ [package] name = "tests" -version = "1.2.0" -edition = "2018" -authors = ["MichaƂ Papierski "] +version = "2.0.0" +edition = "2021" [dependencies] -casper-types = "3.0.0" -casper-engine-test-support = "5.0.0" -casper-execution-engine = "5.0.0" +casper-types = { git = "https://github.com/casper-network/casper-node.git", branch = "release-2.0.0-rc3", default-features = false} +casper-engine-test-support = { git = "https://github.com/casper-network/casper-node.git", branch = "release-2.0.0-rc3", default-features = false} +casper-execution-engine = { git = "https://github.com/casper-network/casper-node.git", branch = "release-2.0.0-rc3", default-features = false} +casper-storage = { git = "https://github.com/casper-network/casper-node.git", branch = "release-2.0.0-rc3", default-features = false} once_cell = "1.16.0" +casper-fixtures = { git = "https://github.com/deuszex/casper-fixtures", branch = "2.0.0-rc3"} [lib] name = "tests" bench = false doctest = false + +[[bin]] +name = "fixture_gen" +path = "src/fixture_gen.rs" +bench = false +doctest = false diff --git a/tests/fixtures/cep18-1.5.6-minted/global_state/data.lmdb b/tests/fixtures/cep18-1.5.6-minted/global_state/data.lmdb new file mode 100644 index 00000000..22b725a7 Binary files /dev/null and b/tests/fixtures/cep18-1.5.6-minted/global_state/data.lmdb differ diff --git a/tests/fixtures/cep18-1.5.6-minted/global_state/data.lmdb-lock b/tests/fixtures/cep18-1.5.6-minted/global_state/data.lmdb-lock new file mode 100644 index 00000000..4b23cdb2 Binary files /dev/null and b/tests/fixtures/cep18-1.5.6-minted/global_state/data.lmdb-lock differ diff --git a/tests/fixtures/cep18-1.5.6-minted/state.json b/tests/fixtures/cep18-1.5.6-minted/state.json new file mode 100644 index 00000000..992219d5 --- /dev/null +++ b/tests/fixtures/cep18-1.5.6-minted/state.json @@ -0,0 +1,6 @@ +{ + "genesis_request": { + "protocol_version": "1.0.0" + }, + "post_state_hash": "5dcbe7efeadf90d16aa93251726fd7e6dbdc1a2eb81a7aff614ad94e4b0c97e3" +} \ No newline at end of file diff --git a/tests/rust-toolchain b/tests/rust-toolchain new file mode 100755 index 00000000..870bbe4e --- /dev/null +++ b/tests/rust-toolchain @@ -0,0 +1 @@ +stable \ No newline at end of file diff --git a/tests/src/allowance.rs b/tests/src/allowance.rs index b1c9d550..4982ab41 100644 --- a/tests/src/allowance.rs +++ b/tests/src/allowance.rs @@ -1,19 +1,18 @@ use casper_engine_test_support::{ExecuteRequestBuilder, DEFAULT_ACCOUNT_ADDR}; -use casper_types::{runtime_args, ApiError, Key, RuntimeArgs, U256}; +use casper_types::{runtime_args, AddressableEntityHash, ApiError, EntityAddr, Key, U256}; use crate::utility::{ constants::{ - ACCOUNT_1_ADDR, ALLOWANCE_AMOUNT_1, ALLOWANCE_AMOUNT_2, ARG_AMOUNT, ARG_OWNER, - ARG_RECIPIENT, ARG_SPENDER, DECREASE_ALLOWANCE, ERROR_INSUFFICIENT_ALLOWANCE, - INCREASE_ALLOWANCE, METHOD_APPROVE, METHOD_TRANSFER_FROM, + ALLOWANCE_AMOUNT_1, ALLOWANCE_AMOUNT_2, ARG_AMOUNT, ARG_OWNER, ARG_RECIPIENT, ARG_SPENDER, + DECREASE_ALLOWANCE, ERROR_INSUFFICIENT_ALLOWANCE, INCREASE_ALLOWANCE, METHOD_APPROVE, + METHOD_TRANSFER_FROM, }, installer_request_builders::{ - cep18_check_allowance_of, make_cep18_approve_request, setup, test_approve_for, TestContext, + cep18_check_allowance_of, get_test_account, make_cep18_approve_request, setup, + test_approve_for, TestContext, }, }; -use casper_execution_engine::core::{ - engine_state::Error as CoreError, execution::Error as ExecError, -}; +use casper_execution_engine::{engine_state::Error as CoreError, execution::ExecError}; #[test] fn should_approve_funds_contract_to_account() { @@ -26,9 +25,9 @@ fn should_approve_funds_contract_to_account() { test_approve_for( &mut builder, &test_context, - Key::Hash(cep18_test_contract_package.value()), - Key::Hash(cep18_test_contract_package.value()), - Key::Account(*DEFAULT_ACCOUNT_ADDR), + Key::Package(cep18_test_contract_package.value()), + Key::Package(cep18_test_contract_package.value()), + Key::AddressableEntity(EntityAddr::Account(DEFAULT_ACCOUNT_ADDR.value())), ); } @@ -43,9 +42,9 @@ fn should_approve_funds_contract_to_contract() { test_approve_for( &mut builder, &test_context, - Key::Hash(cep18_test_contract_package.value()), - Key::Hash(cep18_test_contract_package.value()), - Key::Hash([42; 32]), + Key::Package(cep18_test_contract_package.value()), + Key::Package(cep18_test_contract_package.value()), + Key::Package([42; 32]), ); } @@ -53,56 +52,74 @@ fn should_approve_funds_contract_to_contract() { fn should_approve_funds_account_to_account() { let (mut builder, test_context) = setup(); + let (default_account_user_key, _, _) = get_test_account("ACCOUNT_USER_0"); + let (account_user_1_key, _, _) = get_test_account("ACCOUNT_USER_1"); + test_approve_for( &mut builder, &test_context, - Key::Account(*DEFAULT_ACCOUNT_ADDR), - Key::Account(*DEFAULT_ACCOUNT_ADDR), - Key::Account(*ACCOUNT_1_ADDR), + default_account_user_key, + default_account_user_key, + account_user_1_key, ); } #[test] fn should_approve_funds_account_to_contract() { let (mut builder, test_context) = setup(); + + let (default_account_user_key, _, _) = get_test_account("ACCOUNT_USER_0"); + test_approve_for( &mut builder, &test_context, - Key::Account(*DEFAULT_ACCOUNT_ADDR), - Key::Account(*DEFAULT_ACCOUNT_ADDR), - Key::Hash([42; 32]), + default_account_user_key, + default_account_user_key, + Key::Package([42; 32]), ); } #[test] fn should_not_transfer_from_without_enough_allowance() { - let (mut builder, TestContext { cep18_token, .. }) = setup(); + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup(); + + let (default_account_user_key, default_account_user_account_hash, _) = + get_test_account("ACCOUNT_USER_0"); + let (account_user_1_key, _, _) = get_test_account("ACCOUNT_USER_1"); + + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); let allowance_amount_1 = U256::from(ALLOWANCE_AMOUNT_1); let transfer_from_amount_1 = allowance_amount_1 + U256::one(); - let sender = *DEFAULT_ACCOUNT_ADDR; - let owner = sender; - let recipient = *ACCOUNT_1_ADDR; + let sender = default_account_user_account_hash; + let sender_key = default_account_user_key; + let owner_key = sender_key; + let recipient_key = account_user_1_key; let cep18_approve_args = runtime_args! { - ARG_OWNER => Key::Account(owner), - ARG_SPENDER => Key::Account(recipient), + ARG_OWNER => owner_key, + ARG_SPENDER => recipient_key, ARG_AMOUNT => allowance_amount_1, }; let cep18_transfer_from_args = runtime_args! { - ARG_OWNER => Key::Account(owner), - ARG_RECIPIENT => Key::Account(recipient), + ARG_OWNER => owner_key, + ARG_RECIPIENT => recipient_key, ARG_AMOUNT => transfer_from_amount_1, }; - let spender_allowance_before = - cep18_check_allowance_of(&mut builder, Key::Account(owner), Key::Account(recipient)); + let spender_allowance_before = cep18_check_allowance_of(&mut builder, owner_key, recipient_key); assert_eq!(spender_allowance_before, U256::zero()); let approve_request_1 = ExecuteRequestBuilder::contract_call_by_hash( sender, - cep18_token, + addressable_cep18_contract_hash, METHOD_APPROVE, cep18_approve_args, ) @@ -110,7 +127,7 @@ fn should_not_transfer_from_without_enough_allowance() { let transfer_from_request_1 = ExecuteRequestBuilder::contract_call_by_hash( sender, - cep18_token, + addressable_cep18_contract_hash, METHOD_TRANSFER_FROM, cep18_transfer_from_args, ) @@ -119,7 +136,7 @@ fn should_not_transfer_from_without_enough_allowance() { builder.exec(approve_request_1).expect_success().commit(); let account_1_allowance_after = - cep18_check_allowance_of(&mut builder, Key::Account(owner), Key::Account(recipient)); + cep18_check_allowance_of(&mut builder, owner_key, recipient_key); assert_eq!(account_1_allowance_after, allowance_amount_1); builder.exec(transfer_from_request_1).commit(); @@ -134,21 +151,33 @@ fn should_not_transfer_from_without_enough_allowance() { #[test] fn test_decrease_allowance() { - let (mut builder, TestContext { cep18_token, .. }) = setup(); - let sender = Key::Account(*DEFAULT_ACCOUNT_ADDR); - let owner = Key::Account(*DEFAULT_ACCOUNT_ADDR); + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup(); + + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); + + let owner = *DEFAULT_ACCOUNT_ADDR; let spender = Key::Hash([42; 32]); + + let owner_key = Key::AddressableEntity(EntityAddr::Account(owner.value())); + let spender_key = Key::AddressableEntity(EntityAddr::SmartContract([42; 32])); + let allowance_amount_1 = U256::from(ALLOWANCE_AMOUNT_1); let allowance_amount_2 = U256::from(ALLOWANCE_AMOUNT_2); - let spender_allowance_before = cep18_check_allowance_of(&mut builder, owner, spender); + let spender_allowance_before = cep18_check_allowance_of(&mut builder, owner_key, spender_key); assert_eq!(spender_allowance_before, U256::zero()); let approve_request = - make_cep18_approve_request(sender, &cep18_token, spender, allowance_amount_1); + make_cep18_approve_request(owner_key, &cep18_contract_hash, spender, allowance_amount_1); let decrease_allowance_request = ExecuteRequestBuilder::contract_call_by_hash( - sender.into_account().unwrap(), - cep18_token, + owner, + addressable_cep18_contract_hash, DECREASE_ALLOWANCE, runtime_args! { ARG_SPENDER => spender, @@ -157,8 +186,8 @@ fn test_decrease_allowance() { ) .build(); let increase_allowance_request = ExecuteRequestBuilder::contract_call_by_hash( - sender.into_account().unwrap(), - cep18_token, + owner, + addressable_cep18_contract_hash, INCREASE_ALLOWANCE, runtime_args! { ARG_SPENDER => spender, @@ -169,7 +198,7 @@ fn test_decrease_allowance() { builder.exec(approve_request).expect_success().commit(); - let account_1_allowance_after = cep18_check_allowance_of(&mut builder, owner, spender); + let account_1_allowance_after = cep18_check_allowance_of(&mut builder, owner_key, spender); assert_eq!(account_1_allowance_after, allowance_amount_1); @@ -178,7 +207,8 @@ fn test_decrease_allowance() { .expect_success() .commit(); - let account_1_allowance_after_decrease = cep18_check_allowance_of(&mut builder, owner, spender); + let account_1_allowance_after_decrease = + cep18_check_allowance_of(&mut builder, owner_key, spender); assert_eq!( account_1_allowance_after_decrease, @@ -190,7 +220,8 @@ fn test_decrease_allowance() { .expect_success() .commit(); - let account_1_allowance_after_increase = cep18_check_allowance_of(&mut builder, owner, spender); + let account_1_allowance_after_increase = + cep18_check_allowance_of(&mut builder, owner_key, spender); assert_eq!( account_1_allowance_after_increase, diff --git a/tests/src/events.rs b/tests/src/events.rs index e69de29b..a4a1548c 100644 --- a/tests/src/events.rs +++ b/tests/src/events.rs @@ -0,0 +1,340 @@ +use casper_engine_test_support::{ExecuteRequestBuilder, DEFAULT_ACCOUNT_ADDR}; +use casper_types::{ + addressable_entity::EntityKindTag, bytesrepr::Bytes, contract_messages::Message, runtime_args, + AddressableEntityHash, Key, U256, +}; + +use crate::utility::{ + constants::{ + AMOUNT, ARG_DECIMALS, ARG_NAME, ARG_SYMBOL, ARG_TOTAL_SUPPLY, CEP18_TOKEN_CONTRACT_KEY, + ENABLE_MINT_BURN, EVENTS, EVENTS_MODE, METHOD_MINT, OWNER, TOKEN_DECIMALS, TOKEN_NAME, + TOKEN_OWNER_ADDRESS_1, TOKEN_OWNER_AMOUNT_1, TOKEN_SYMBOL, TOKEN_TOTAL_SUPPLY, + }, + installer_request_builders::{setup_with_args, TestContext}, + message_handlers::{entity, message_topic}, + support::get_dictionary_value_from_key, +}; + +#[test] +fn should_have_have_no_events() { + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup_with_args(runtime_args! { + ARG_NAME => TOKEN_NAME, + ARG_SYMBOL => TOKEN_SYMBOL, + ARG_DECIMALS => TOKEN_DECIMALS, + ARG_TOTAL_SUPPLY => U256::from(TOKEN_TOTAL_SUPPLY), + EVENTS_MODE => 0_u8, + ENABLE_MINT_BURN => true, + }); + + let addressable_cep18_token = AddressableEntityHash::new(cep18_contract_hash.value()); + let mint_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + addressable_cep18_token, + METHOD_MINT, + runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_1, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_1)}, + ) + .build(); + builder.exec(mint_request).expect_success().commit(); + + let entity_with_named_keys = builder.get_named_keys(casper_types::EntityAddr::SmartContract( + addressable_cep18_token.value(), + )); + assert!(entity_with_named_keys.get("__events").is_none()); + let entity = entity(&builder, &addressable_cep18_token); + assert!(entity.message_topics().len() == 1); +} + +#[test] +fn should_have_native_events() { + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup_with_args(runtime_args! { + ARG_NAME => TOKEN_NAME, + ARG_SYMBOL => TOKEN_SYMBOL, + ARG_DECIMALS => TOKEN_DECIMALS, + ARG_TOTAL_SUPPLY => U256::from(TOKEN_TOTAL_SUPPLY), + EVENTS_MODE => 2_u8, + ENABLE_MINT_BURN => true, + }); + + let addressable_cep18_token = AddressableEntityHash::new(cep18_contract_hash.value()); + let mint_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + addressable_cep18_token, + METHOD_MINT, + runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_1, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_1)}, + ) + .build(); + builder.exec(mint_request).expect_success().commit(); + + let account = builder + .get_entity_with_named_keys_by_account_hash(*DEFAULT_ACCOUNT_ADDR) + .unwrap(); + let account_named_keys = account.named_keys(); + + let cep18_token = account_named_keys + .get(CEP18_TOKEN_CONTRACT_KEY) + .and_then(|key| key.into_entity_hash()) + .expect("should have contract hash"); + + // events check + let entity = entity(&builder, &cep18_token); + + let (topic_name, message_topic_hash) = entity + .message_topics() + .iter() + .last() + .expect("should have at least one topic"); + + assert_eq!(topic_name, &EVENTS.to_string()); + + let mint_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + cep18_token, + METHOD_MINT, + runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_1, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_1)}, + ) + .build(); + + builder.exec(mint_request).expect_success().commit(); + + assert_eq!( + message_topic(&builder, &cep18_token, *message_topic_hash).message_count(), + 3 + ); + + let exec_result = builder.get_exec_result_owned(3).unwrap(); + let messages = exec_result.messages(); + let mint_message = "Mint(Mint { recipient: Key::AddressableEntity(account-2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a), amount: 1000000 })"; + let message = Message::new( + casper_types::EntityAddr::SmartContract(cep18_token.value()), + mint_message.into(), + EVENTS.to_string(), + *message_topic_hash, + 2, + 2, + ); + assert_eq!(messages, &vec![message]); +} + +#[test] +fn should_have_ces_events() { + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup_with_args(runtime_args! { + ARG_NAME => TOKEN_NAME, + ARG_SYMBOL => TOKEN_SYMBOL, + ARG_DECIMALS => TOKEN_DECIMALS, + ARG_TOTAL_SUPPLY => U256::from(TOKEN_TOTAL_SUPPLY), + EVENTS_MODE => 1_u8, + ENABLE_MINT_BURN => true, + }); + + let addressable_cep18_token = AddressableEntityHash::new(cep18_contract_hash.value()); + let mint_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + addressable_cep18_token, + METHOD_MINT, + runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_1, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_1)}, + ) + .build(); + builder.exec(mint_request).expect_success().commit(); + + let account = builder + .get_entity_with_named_keys_by_account_hash(*DEFAULT_ACCOUNT_ADDR) + .unwrap(); + let account_named_keys = account.named_keys(); + + let cep18_token = account_named_keys + .get(CEP18_TOKEN_CONTRACT_KEY) + .and_then(|key| key.into_entity_hash()) + .expect("should have contract hash"); + + let mint_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + cep18_token, + METHOD_MINT, + runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_1, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_1)}, + ) + .build(); + + builder.exec(mint_request).expect_success().commit(); + + let stored_event: Bytes = get_dictionary_value_from_key( + &builder, + &Key::addressable_entity_key(EntityKindTag::SmartContract, addressable_cep18_token), + "__events", + "1", + ); + assert_eq!( + stored_event, + Bytes::from(vec![ + 10, 0, 0, 0, 101, 118, 101, 110, 116, 95, 77, 105, 110, 116, 17, 1, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 3, 64, 66, 15 + ]) + ) +} + +#[test] +fn should_have_both_native_and_ces_events() { + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup_with_args(runtime_args! { + ARG_NAME => TOKEN_NAME, + ARG_SYMBOL => TOKEN_SYMBOL, + ARG_DECIMALS => TOKEN_DECIMALS, + ARG_TOTAL_SUPPLY => U256::from(TOKEN_TOTAL_SUPPLY), + EVENTS_MODE => 3_u8, + ENABLE_MINT_BURN => true, + }); + + let addressable_cep18_token = AddressableEntityHash::new(cep18_contract_hash.value()); + let mint_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + addressable_cep18_token, + METHOD_MINT, + runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_1, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_1)}, + ) + .build(); + builder.exec(mint_request).expect_success().commit(); + + let account = builder + .get_entity_with_named_keys_by_account_hash(*DEFAULT_ACCOUNT_ADDR) + .unwrap(); + let account_named_keys = account.named_keys(); + + let cep18_token = account_named_keys + .get(CEP18_TOKEN_CONTRACT_KEY) + .and_then(|key| key.into_entity_hash()) + .expect("should have contract hash"); + + // events check + let entity = entity(&builder, &cep18_token); + + let (topic_name, message_topic_hash) = entity + .message_topics() + .iter() + .last() + .expect("should have at least one topic"); + + assert_eq!(topic_name, &EVENTS.to_string()); + + let mint_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + cep18_token, + METHOD_MINT, + runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_1, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_1)}, + ) + .build(); + + builder.exec(mint_request).expect_success().commit(); + + assert_eq!( + message_topic(&builder, &cep18_token, *message_topic_hash).message_count(), + 3 + ); + + let exec_result = builder.get_exec_result_owned(3).unwrap(); + let messages = exec_result.messages(); + let mint_message = "Mint(Mint { recipient: Key::AddressableEntity(account-2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a2a), amount: 1000000 })"; + let message = Message::new( + casper_types::EntityAddr::SmartContract(cep18_token.value()), + mint_message.into(), + EVENTS.to_string(), + *message_topic_hash, + 2, + 2, + ); + assert_eq!(messages, &vec![message]); + + let stored_event: Bytes = get_dictionary_value_from_key( + &builder, + &Key::addressable_entity_key(EntityKindTag::SmartContract, addressable_cep18_token), + "__events", + "1", + ); + assert_eq!( + stored_event, + Bytes::from(vec![ + 10, 0, 0, 0, 101, 118, 101, 110, 116, 95, 77, 105, 110, 116, 17, 1, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, + 42, 42, 42, 42, 42, 3, 64, 66, 15 + ]) + ) +} + +#[test] +fn should_test_error_message_topic_on_mint_overflow() { + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup_with_args(runtime_args! { + ARG_NAME => TOKEN_NAME, + ARG_SYMBOL => TOKEN_SYMBOL, + ARG_DECIMALS => TOKEN_DECIMALS, + ARG_TOTAL_SUPPLY => U256::from(TOKEN_TOTAL_SUPPLY), + EVENTS_MODE => 0_u8, + ENABLE_MINT_BURN => true, + }); + + let addressable_cep18_token = AddressableEntityHash::new(cep18_contract_hash.value()); + let entity = entity(&builder, &addressable_cep18_token); + + let (topic_name, message_topic_hash) = entity + .message_topics() + .iter() + .last() + .expect("should have at least one topic"); + + assert_eq!(topic_name, "errors"); + + let mint_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + addressable_cep18_token, + METHOD_MINT, + runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_1, AMOUNT => U256::MAX}, + ) + .build(); + + builder.exec(mint_request).expect_failure().commit(); + + assert_eq!( + message_topic(&builder, &addressable_cep18_token, *message_topic_hash).message_count(), + 1 + ); + + let exec_result = builder.get_exec_result_owned(2).unwrap(); + let messages = exec_result.messages(); + let error_message = "Cannot add to total_supply as it flow over U256::MAX value"; + let message = Message::new( + casper_types::EntityAddr::SmartContract(addressable_cep18_token.value()), + error_message.into(), + "errors".to_string(), + *message_topic_hash, + 0, + 0, + ); + assert_eq!(messages, &vec![message]); +} diff --git a/tests/src/fixture_gen.rs b/tests/src/fixture_gen.rs new file mode 100644 index 00000000..cbdde986 --- /dev/null +++ b/tests/src/fixture_gen.rs @@ -0,0 +1,254 @@ +use casper_engine_test_support::utils::create_run_genesis_request; +use casper_engine_test_support::{ + ExecuteRequestBuilder, LmdbWasmTestBuilder, DEFAULT_ACCOUNT_ADDR, DEFAULT_ACCOUNT_PUBLIC_KEY, +}; +use casper_fixtures::generate_fixture; +use casper_types::addressable_entity::EntityKindTag; +use casper_types::bytesrepr::FromBytes; +use casper_types::{runtime_args, RuntimeArgs, U256}; +use casper_types::{ + AddressableEntityHash, CLTyped, EntityAddr, GenesisAccount, Motes, PackageHash, U512, +}; + +use casper_types::{account::AccountHash, Key, PublicKey, SecretKey}; +use once_cell::sync::Lazy; + +pub const CEP18_CONTRACT_WASM: &str = "cep18.wasm"; +pub const CEP18_TEST_CONTRACT_WASM: &str = "cep18_test_contract.wasm"; +pub const NAME_KEY: &str = "name"; +pub const SYMBOL_KEY: &str = "symbol"; +pub const CEP18_TOKEN_CONTRACT_KEY: &str = "cep18_contract_hash_CasperTest"; +pub const DECIMALS_KEY: &str = "decimals"; +pub const TOTAL_SUPPLY_KEY: &str = "total_supply"; +pub const BALANCES_KEY: &str = "balances"; +pub const ALLOWANCES_KEY: &str = "allowances"; +pub const OWNER: &str = "owner"; +pub const AMOUNT: &str = "amount"; + +pub const ARG_NAME: &str = "name"; +pub const ARG_SYMBOL: &str = "symbol"; +pub const ARG_DECIMALS: &str = "decimals"; +pub const ARG_TOTAL_SUPPLY: &str = "total_supply"; + +pub const _ERROR_INVALID_CONTEXT: u16 = 60000; +pub const ERROR_INSUFFICIENT_BALANCE: u16 = 60001; +pub const ERROR_INSUFFICIENT_ALLOWANCE: u16 = 60002; +pub const ERROR_OVERFLOW: u16 = 60003; + +pub const TOKEN_NAME: &str = "CasperTest"; +pub const TOKEN_SYMBOL: &str = "CSPRT"; +pub const TOKEN_DECIMALS: u8 = 100; +pub const TOKEN_TOTAL_SUPPLY: u64 = 1_000_000_000; + +pub const METHOD_TRANSFER: &str = "transfer"; +pub const ARG_AMOUNT: &str = "amount"; +pub const ARG_RECIPIENT: &str = "recipient"; + +pub const METHOD_APPROVE: &str = "approve"; +pub const ARG_OWNER: &str = "owner"; +pub const ARG_SPENDER: &str = "spender"; + +pub const METHOD_TRANSFER_FROM: &str = "transfer_from"; + +pub const CHECK_TOTAL_SUPPLY_ENTRYPOINT: &str = "check_total_supply"; +pub const CHECK_BALANCE_OF_ENTRYPOINT: &str = "check_balance_of"; +pub const CHECK_ALLOWANCE_OF_ENTRYPOINT: &str = "check_allowance_of"; +pub const ARG_TOKEN_CONTRACT: &str = "token_contract"; +pub const ARG_ADDRESS: &str = "address"; +pub const RESULT_KEY: &str = "result"; +pub const CEP18_TEST_CONTRACT_KEY: &str = "cep18_test_contract"; + +pub static ACCOUNT_1_SECRET_KEY: Lazy = + Lazy::new(|| SecretKey::secp256k1_from_bytes([221u8; 32]).unwrap()); +pub static ACCOUNT_1_PUBLIC_KEY: Lazy = + Lazy::new(|| PublicKey::from(&*ACCOUNT_1_SECRET_KEY)); +pub static ACCOUNT_1_ADDR: Lazy = Lazy::new(|| ACCOUNT_1_PUBLIC_KEY.to_account_hash()); + +pub static ACCOUNT_2_SECRET_KEY: Lazy = + Lazy::new(|| SecretKey::secp256k1_from_bytes([212u8; 32]).unwrap()); +pub static ACCOUNT_2_PUBLIC_KEY: Lazy = + Lazy::new(|| PublicKey::from(&*ACCOUNT_2_SECRET_KEY)); +pub static ACCOUNT_2_ADDR: Lazy = Lazy::new(|| ACCOUNT_2_PUBLIC_KEY.to_account_hash()); + +pub const TRANSFER_AMOUNT_1: u64 = 200_001; +pub const TRANSFER_AMOUNT_2: u64 = 19_999; +pub const ALLOWANCE_AMOUNT_1: u64 = 456_789; +pub const ALLOWANCE_AMOUNT_2: u64 = 87_654; + +pub const METHOD_TRANSFER_AS_STORED_CONTRACT: &str = "transfer_as_stored_contract"; +pub const METHOD_APPROVE_AS_STORED_CONTRACT: &str = "approve_as_stored_contract"; +pub const METHOD_FROM_AS_STORED_CONTRACT: &str = "transfer_from_as_stored_contract"; + +pub const TOKEN_OWNER_ADDRESS_1: Key = Key::Account(AccountHash::new([42; 32])); +pub const TOKEN_OWNER_AMOUNT_1: u64 = 1_000_000; +pub const TOKEN_OWNER_ADDRESS_2: Key = Key::Hash([42; 32]); +pub const TOKEN_OWNER_AMOUNT_2: u64 = 2_000_000; + +pub const METHOD_MINT: &str = "mint"; +pub const METHOD_BURN: &str = "burn"; +pub const DECREASE_ALLOWANCE: &str = "decrease_allowance"; +pub const INCREASE_ALLOWANCE: &str = "increase_allowance"; +pub const ENABLE_MINT_BURN: &str = "enable_mint_burn"; +pub const ADMIN_LIST: &str = "admin_list"; +pub const MINTER_LIST: &str = "minter_list"; +pub const NONE_LIST: &str = "none_list"; +pub const CHANGE_SECURITY: &str = "change_security"; + +pub(crate) fn get_test_result( + builder: &mut LmdbWasmTestBuilder, + cep18_test_contract_package: PackageHash, +) -> T { + let contract_package = builder + .get_package(cep18_test_contract_package) + .expect("should have contract package"); + let enabled_versions = contract_package.enabled_versions(); + + let contract_hash = enabled_versions + .contract_hashes() + .last() + .expect("should have latest version"); + let entity_addr = EntityAddr::new_smart_contract(contract_hash.value()); + builder.get_value(entity_addr, RESULT_KEY) +} + +pub(crate) fn cep18_check_balance_of( + builder: &mut LmdbWasmTestBuilder, + cep18_contract_hash: &AddressableEntityHash, + address: Key, +) -> U256 { + let account = builder + .get_entity_with_named_keys_by_account_hash(*DEFAULT_ACCOUNT_ADDR) + .expect("should have account"); + + let cep18_test_contract_package = account + .named_keys() + .get(CEP18_TEST_CONTRACT_KEY) + .and_then(|key| key.into_package_hash()) + .expect("should have test contract hash"); + + let check_balance_args = runtime_args! { + ARG_TOKEN_CONTRACT => Key::addressable_entity_key(EntityKindTag::SmartContract, *cep18_contract_hash), + ARG_ADDRESS => address, + }; + let exec_request = ExecuteRequestBuilder::versioned_contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + cep18_test_contract_package, + None, + CHECK_BALANCE_OF_ENTRYPOINT, + check_balance_args, + ) + .build(); + builder.exec(exec_request).expect_success().commit(); + + get_test_result(builder, cep18_test_contract_package) +} + +fn main() { + let genesis_request = create_run_genesis_request(vec![ + GenesisAccount::Account { + public_key: DEFAULT_ACCOUNT_PUBLIC_KEY.clone(), + balance: Motes::new(U512::from(5_000_000_000_000_u64)), + validator: None, + }, + GenesisAccount::Account { + public_key: ACCOUNT_1_PUBLIC_KEY.clone(), + balance: Motes::new(U512::from(5_000_000_000_000_u64)), + validator: None, + }, + GenesisAccount::Account { + public_key: ACCOUNT_2_PUBLIC_KEY.clone(), + balance: Motes::new(U512::from(5_000_000_000_000_u64)), + validator: None, + }, + ]); + generate_fixture("cep18-2.0.0-rc3-minted", genesis_request, |builder|{ + let mint_amount = U256::one(); + + let install_args = runtime_args! { + ARG_NAME => TOKEN_NAME, + ARG_SYMBOL => TOKEN_SYMBOL, + ARG_DECIMALS => TOKEN_DECIMALS, + ARG_TOTAL_SUPPLY => U256::from(TOKEN_TOTAL_SUPPLY), + ENABLE_MINT_BURN => true, + }; + let install_request_1 = + ExecuteRequestBuilder::standard(*DEFAULT_ACCOUNT_ADDR, CEP18_CONTRACT_WASM, install_args) + .build(); + + let install_request_2 = ExecuteRequestBuilder::standard( + *DEFAULT_ACCOUNT_ADDR, + CEP18_TEST_CONTRACT_WASM, + RuntimeArgs::default(), + ) + .build(); + + builder.exec(install_request_1).expect_success().commit(); + builder.exec(install_request_2).expect_success().commit(); + + let account = builder + .get_entity_with_named_keys_by_account_hash(*DEFAULT_ACCOUNT_ADDR) + .unwrap(); + let account_named_keys = account.named_keys(); + + let cep18_contract_hash = account_named_keys + .get(CEP18_TOKEN_CONTRACT_KEY) + .and_then(|key| key.into_entity_hash()) + .expect("should have contract hash"); + + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); + let mint_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + addressable_cep18_contract_hash, + METHOD_MINT, + runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_1, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_1)}, + ) + .build(); + builder.exec(mint_request).expect_success().commit(); + let mint_request_2 = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + addressable_cep18_contract_hash, + METHOD_MINT, + runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_2, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_2)}, + ) + .build(); + builder.exec(mint_request_2).expect_success().commit(); + assert_eq!( + cep18_check_balance_of( + builder, + &cep18_contract_hash, + Key::AddressableEntity(casper_types::EntityAddr::Account(DEFAULT_ACCOUNT_ADDR.value())) + ), + U256::from(TOKEN_TOTAL_SUPPLY), + ); + assert_eq!( + cep18_check_balance_of(builder, &cep18_contract_hash, TOKEN_OWNER_ADDRESS_1), + U256::from(TOKEN_OWNER_AMOUNT_1) + ); + assert_eq!( + cep18_check_balance_of(builder, &cep18_contract_hash, TOKEN_OWNER_ADDRESS_2), + U256::from(TOKEN_OWNER_AMOUNT_2) + ); + + let mint_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + addressable_cep18_contract_hash, + METHOD_MINT, + runtime_args! { + ARG_OWNER => TOKEN_OWNER_ADDRESS_1, + ARG_AMOUNT => mint_amount, + }, + ) + .build(); + + builder.exec(mint_request).expect_success().commit(); + + assert_eq!( + cep18_check_balance_of(builder, &cep18_contract_hash, TOKEN_OWNER_ADDRESS_1), + U256::from(TOKEN_OWNER_AMOUNT_1) + mint_amount, + ); + assert_eq!( + cep18_check_balance_of(builder, &cep18_contract_hash, TOKEN_OWNER_ADDRESS_2), + U256::from(TOKEN_OWNER_AMOUNT_2) + ); + }).unwrap(); +} diff --git a/tests/src/install.rs b/tests/src/install.rs index 40153600..bf3d74a3 100644 --- a/tests/src/install.rs +++ b/tests/src/install.rs @@ -1,10 +1,15 @@ -use casper_engine_test_support::DEFAULT_ACCOUNT_ADDR; -use casper_types::{Key, U256}; +use casper_engine_test_support::{ + utils::create_run_genesis_request, ExecuteRequestBuilder, LmdbWasmTestBuilder, + DEFAULT_ACCOUNTS, DEFAULT_ACCOUNT_ADDR, +}; +use casper_execution_engine::{engine_state::Error as CoreError, execution::ExecError}; +use casper_types::{runtime_args, ApiError, EntityAddr, Key, U256}; use crate::utility::{ constants::{ - ALLOWANCES_KEY, BALANCES_KEY, DECIMALS_KEY, NAME_KEY, SYMBOL_KEY, TOKEN_DECIMALS, - TOKEN_NAME, TOKEN_SYMBOL, TOKEN_TOTAL_SUPPLY, TOTAL_SUPPLY_KEY, + ALLOWANCES_KEY, ARG_DECIMALS, ARG_NAME, ARG_SYMBOL, ARG_TOTAL_SUPPLY, BALANCES_KEY, + CEP18_CONTRACT_WASM, DECIMALS_KEY, ENABLE_MINT_BURN, EVENTS_MODE, NAME_KEY, SYMBOL_KEY, + TOKEN_DECIMALS, TOKEN_NAME, TOKEN_SYMBOL, TOKEN_TOTAL_SUPPLY, TOTAL_SUPPLY_KEY, }, installer_request_builders::{ cep18_check_balance_of, invert_cep18_address, setup, TestContext, @@ -13,34 +18,45 @@ use crate::utility::{ #[test] fn should_have_queryable_properties() { - let (mut builder, TestContext { cep18_token, .. }) = setup(); + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup(); + + let cep18_entity_addr = EntityAddr::new_smart_contract(cep18_contract_hash.value()); - let name: String = builder.get_value(cep18_token, NAME_KEY); + let name: String = builder.get_value(cep18_entity_addr, NAME_KEY); assert_eq!(name, TOKEN_NAME); - let symbol: String = builder.get_value(cep18_token, SYMBOL_KEY); + let symbol: String = builder.get_value(cep18_entity_addr, SYMBOL_KEY); assert_eq!(symbol, TOKEN_SYMBOL); - let decimals: u8 = builder.get_value(cep18_token, DECIMALS_KEY); + let decimals: u8 = builder.get_value(cep18_entity_addr, DECIMALS_KEY); assert_eq!(decimals, TOKEN_DECIMALS); - let total_supply: U256 = builder.get_value(cep18_token, TOTAL_SUPPLY_KEY); + let total_supply: U256 = builder.get_value(cep18_entity_addr, TOTAL_SUPPLY_KEY); assert_eq!(total_supply, U256::from(TOKEN_TOTAL_SUPPLY)); - let owner_key = Key::Account(*DEFAULT_ACCOUNT_ADDR); + let owner_key = Key::AddressableEntity(EntityAddr::Account(DEFAULT_ACCOUNT_ADDR.value())); - let owner_balance = cep18_check_balance_of(&mut builder, &cep18_token, owner_key); + let owner_balance = cep18_check_balance_of(&mut builder, &cep18_contract_hash, owner_key); assert_eq!(owner_balance, total_supply); - let contract_balance = - cep18_check_balance_of(&mut builder, &cep18_token, Key::Hash(cep18_token.value())); + let contract_balance = cep18_check_balance_of( + &mut builder, + &cep18_contract_hash, + Key::Hash(cep18_contract_hash.value()), + ); assert_eq!(contract_balance, U256::zero()); // Ensures that Account and Contract ownership is respected and we're not keying ownership under // the raw bytes regardless of variant. let inverted_owner_key = invert_cep18_address(owner_key); let inverted_owner_balance = - cep18_check_balance_of(&mut builder, &cep18_token, inverted_owner_key); + cep18_check_balance_of(&mut builder, &cep18_contract_hash, inverted_owner_key); assert_eq!(inverted_owner_balance, U256::zero()); } @@ -48,11 +64,42 @@ fn should_have_queryable_properties() { fn should_not_store_balances_or_allowances_under_account_after_install() { let (builder, _contract_hash) = setup(); - let account = builder - .get_account(*DEFAULT_ACCOUNT_ADDR) - .expect("should have account"); + let named_keys = builder.get_named_keys_by_account_hash(*DEFAULT_ACCOUNT_ADDR); + + assert!(!named_keys.contains(BALANCES_KEY), "{:?}", named_keys); + assert!(!named_keys.contains(ALLOWANCES_KEY), "{:?}", named_keys); +} + +#[test] +fn should_fail_with_left_over_bytes_converted_into_60006() { + let mut builder = LmdbWasmTestBuilder::default(); + builder + .run_genesis(create_run_genesis_request(DEFAULT_ACCOUNTS.to_vec())) + .commit(); + + let install_request_1 = ExecuteRequestBuilder::standard( + *DEFAULT_ACCOUNT_ADDR, + CEP18_CONTRACT_WASM, + runtime_args! { + ARG_NAME => TOKEN_NAME, + ARG_SYMBOL => TOKEN_SYMBOL, + ARG_DECIMALS => TOKEN_DECIMALS, + ARG_TOTAL_SUPPLY => U256::from(TOKEN_TOTAL_SUPPLY), + EVENTS_MODE => Some(0_u8), + ENABLE_MINT_BURN => true, + }, + ) + .build(); + + builder.exec(install_request_1).expect_failure().commit(); - let named_keys = account.named_keys(); - assert!(!named_keys.contains_key(BALANCES_KEY), "{:?}", named_keys); - assert!(!named_keys.contains_key(ALLOWANCES_KEY), "{:?}", named_keys); + let error = builder.get_error().expect("should have error"); + assert!( + matches!( + error, + CoreError::Exec(ExecError::Revert(ApiError::User(60006))) + ), + "{:?}", + error + ); } diff --git a/tests/src/lib.rs b/tests/src/lib.rs index 17dbe6ce..cb567ac5 100644 --- a/tests/src/lib.rs +++ b/tests/src/lib.rs @@ -1,6 +1,8 @@ #[cfg(test)] mod allowance; #[cfg(test)] +mod events; +#[cfg(test)] mod install; #[cfg(test)] mod migration; @@ -9,4 +11,6 @@ mod mint_and_burn; #[cfg(test)] mod transfer; #[cfg(test)] +mod upgrade; +#[cfg(test)] mod utility; diff --git a/tests/src/migration.rs b/tests/src/migration.rs index bb60f8b0..083ae955 100644 --- a/tests/src/migration.rs +++ b/tests/src/migration.rs @@ -1,31 +1,137 @@ -use casper_engine_test_support::{ExecuteRequestBuilder, DEFAULT_ACCOUNT_ADDR}; -use casper_types::{runtime_args, Key, RuntimeArgs, U256}; +use std::collections::BTreeMap; + +use casper_engine_test_support::{ + ExecuteRequestBuilder, LmdbWasmTestBuilder, UpgradeRequestBuilder, DEFAULT_ACCOUNT_ADDR, +}; +use casper_fixtures::LmdbFixtureState; +use casper_types::{ + bytesrepr::FromBytes, runtime_args, AddressableEntityHash, CLTyped, EraId, Key, + ProtocolVersion, RuntimeArgs, U256, +}; use crate::utility::{ constants::{ - ARG_DECIMALS, ARG_NAME, ARG_SYMBOL, ARG_TOTAL_SUPPLY, CEP18_CONTRACT_WASM, TOKEN_DECIMALS, - TOKEN_NAME, TOKEN_SYMBOL, TOKEN_TOTAL_SUPPLY, + AMOUNT, ARG_DECIMALS, ARG_NAME, ARG_SYMBOL, ARG_TOTAL_SUPPLY, CEP18_CONTRACT_WASM, + CEP18_TEST_CONTRACT_WASM, CEP18_TOKEN_CONTRACT_KEY, CEP18_TOKEN_CONTRACT_VERSION_KEY, + EVENTS, EVENTS_MODE, METHOD_MINT, MIGRATE_USER_BALANCE_KEYS_ENTRY_POINT_NAME, + MIGRATE_USER_SEC_KEYS_ENTRY_POINT_NAME, OWNER, REVERT, TOKEN_DECIMALS, TOKEN_NAME, + TOKEN_OWNER_ADDRESS_1, TOKEN_OWNER_ADDRESS_1_OLD, TOKEN_OWNER_AMOUNT_1, TOKEN_SYMBOL, + TOKEN_TOTAL_SUPPLY, USER_KEY_MAP, }, - installer_request_builders::{setup, TestContext}, + installer_request_builders::cep18_check_balance_of, + message_handlers::{entity, message_summary, message_topic}, }; -#[test] -fn should_upgrade_contract_version() { - let (mut builder, TestContext { cep18_token: _, .. }) = setup(); - - let version_0: u32 = builder - .query( - None, - Key::Account(*DEFAULT_ACCOUNT_ADDR), - &["cep18_contract_version_CasperTest".to_string()], - ) +pub fn upgrade_v1_5_6_fixture_to_v2_0_0_ee( + builder: &mut LmdbWasmTestBuilder, + lmdb_fixture_state: &LmdbFixtureState, +) { + // state hash in builder and lmdb storage should be the same + assert_eq!( + builder.get_post_state_hash(), + lmdb_fixture_state.post_state_hash + ); + + // we upgrade the execution engines protocol from 1.x to 2.x + let mut upgrade_config = UpgradeRequestBuilder::new() + .with_current_protocol_version(lmdb_fixture_state.genesis_protocol_version()) + .with_new_protocol_version(ProtocolVersion::V2_0_0) + .with_migrate_legacy_accounts(true) + .with_migrate_legacy_contracts(true) + .with_activation_point(EraId::new(1)) + .build(); + + builder + .upgrade(&mut upgrade_config) + .expect_upgrade_success() + .commit(); + + // the state hash should now be different + assert_ne!( + builder.get_post_state_hash(), + lmdb_fixture_state.post_state_hash + ); +} + +pub fn query_contract_value( + builder: &LmdbWasmTestBuilder, + path: &[String], +) -> T { + builder + .query(None, Key::Account(*DEFAULT_ACCOUNT_ADDR), path) .unwrap() .as_cl_value() .unwrap() .clone() .into_t() + .unwrap() +} + +// the difference between the two is that in v1_binary the contract hash is fetched at [u8;32], while in v2_binary it is an AddressaleEntityHash +pub fn get_contract_hash_v1_binary(builder: &LmdbWasmTestBuilder) -> AddressableEntityHash { + let account = builder + .get_entity_with_named_keys_by_account_hash(*DEFAULT_ACCOUNT_ADDR) .unwrap(); + let account_named_keys = account.named_keys(); + + let cep18_token = account_named_keys + .get(CEP18_TOKEN_CONTRACT_KEY) + .and_then(|key| key.into_hash_addr()) + .map(AddressableEntityHash::new) + .expect("should have contract hash"); + + cep18_token +} + +pub fn get_contract_hash_v2_binary(builder: &LmdbWasmTestBuilder) -> AddressableEntityHash { + let account = builder + .get_entity_with_named_keys_by_account_hash(*DEFAULT_ACCOUNT_ADDR) + .unwrap(); + let account_named_keys = account.named_keys(); + + let cep18_token = account_named_keys + .get(CEP18_TOKEN_CONTRACT_KEY) + .and_then(|key| key.into_entity_hash()) + .expect("should have contract hash"); + + cep18_token +} + +#[test] +fn should_be_able_to_call_1x_contract_in_2x_execution_engine() { + // load fixture that was created in a previous EE version + let (mut builder, lmdb_fixture_state, _temp_dir) = + casper_fixtures::builder_from_global_state_fixture("cep18-1.5.6-minted"); + + // upgrade the execution engine to the new protocol version + upgrade_v1_5_6_fixture_to_v2_0_0_ee(&mut builder, &lmdb_fixture_state); + + let cep18_token = get_contract_hash_v1_binary(&builder); + + let mint_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + cep18_token, + METHOD_MINT, + runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_1_OLD, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_1)}, + ) + .build(); + + builder.exec(mint_request).expect_success().commit(); +} + +#[test] +fn should_migrate_1_5_6_to_2_0_0_rc3() { + // load fixture + let (mut builder, lmdb_fixture_state, _temp_dir) = + casper_fixtures::builder_from_global_state_fixture("cep18-1.5.6-minted"); + // upgrade engine + upgrade_v1_5_6_fixture_to_v2_0_0_ee(&mut builder, &lmdb_fixture_state); + + let version_0: u32 = + query_contract_value(&builder, &[CEP18_TOKEN_CONTRACT_VERSION_KEY.to_string()]); + + // upgrade the contract itself using a binary built for the new engine let upgrade_request = ExecuteRequestBuilder::standard( *DEFAULT_ACCOUNT_ADDR, CEP18_CONTRACT_WASM, @@ -34,24 +140,150 @@ fn should_upgrade_contract_version() { ARG_SYMBOL => TOKEN_SYMBOL, ARG_DECIMALS => TOKEN_DECIMALS, ARG_TOTAL_SUPPLY => U256::from(TOKEN_TOTAL_SUPPLY), + EVENTS_MODE => 3_u8 }, ) .build(); builder.exec(upgrade_request).expect_success().commit(); - let version_1: u32 = builder - .query( - None, - Key::Account(*DEFAULT_ACCOUNT_ADDR), - &["cep18_contract_version_CasperTest".to_string()], - ) - .unwrap() - .as_cl_value() - .unwrap() - .clone() - .into_t() - .unwrap(); + let version_1: u32 = + query_contract_value(&builder, &[CEP18_TOKEN_CONTRACT_VERSION_KEY.to_string()]); assert!(version_0 < version_1); + + let cep18_contract_hash = get_contract_hash_v2_binary(&builder); + + // use utility entrypoint to migrate the security keys in cep-18 + let mut user_map: BTreeMap = BTreeMap::new(); + user_map.insert(Key::Account(*DEFAULT_ACCOUNT_ADDR), true); + user_map.insert(TOKEN_OWNER_ADDRESS_1_OLD, true); + + let sec_key_migrate_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + cep18_contract_hash, + MIGRATE_USER_SEC_KEYS_ENTRY_POINT_NAME, + runtime_args! {EVENTS => true, REVERT => true, USER_KEY_MAP => &user_map}, + ) + .build(); + + builder + .exec(sec_key_migrate_request) + .expect_success() + .commit(); + + // mint some new tokens in cep-18 + let mint_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + cep18_contract_hash, + METHOD_MINT, + runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_1, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_1)}, + ) + .build(); + + builder.exec(mint_request).expect_success().commit(); + + let test_contract = ExecuteRequestBuilder::standard( + *DEFAULT_ACCOUNT_ADDR, + CEP18_TEST_CONTRACT_WASM, + RuntimeArgs::default(), + ) + .build(); + + builder.exec(test_contract).expect_success().commit(); + + assert_eq!( + cep18_check_balance_of(&mut builder, &cep18_contract_hash, TOKEN_OWNER_ADDRESS_1), + U256::from(TOKEN_OWNER_AMOUNT_1), + ); + + // migrate the balances from the old owner keys to the new ones in cep-18 + let balance_migrate_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + cep18_contract_hash, + MIGRATE_USER_BALANCE_KEYS_ENTRY_POINT_NAME, + runtime_args! {EVENTS => true, REVERT => true, USER_KEY_MAP => user_map}, + ) + .build(); + + builder + .exec(balance_migrate_request) + .expect_success() + .commit(); + + // even if we minted before migrating, the balance should persist + assert_eq!( + cep18_check_balance_of(&mut builder, &cep18_contract_hash, TOKEN_OWNER_ADDRESS_1), + U256::from(TOKEN_OWNER_AMOUNT_1 * 2), + ); +} + +#[test] +fn should_have_native_events() { + let (mut builder, lmdb_fixture_state, _temp_dir) = + casper_fixtures::builder_from_global_state_fixture("cep18-1.5.6-minted"); + + upgrade_v1_5_6_fixture_to_v2_0_0_ee(&mut builder, &lmdb_fixture_state); + + let upgrade_request = ExecuteRequestBuilder::standard( + *DEFAULT_ACCOUNT_ADDR, + CEP18_CONTRACT_WASM, + runtime_args! { + ARG_NAME => TOKEN_NAME, + ARG_SYMBOL => TOKEN_SYMBOL, + ARG_DECIMALS => TOKEN_DECIMALS, + ARG_TOTAL_SUPPLY => U256::from(TOKEN_TOTAL_SUPPLY), + EVENTS_MODE => 3_u8 + }, + ) + .build(); + + builder.exec(upgrade_request).expect_success().commit(); + + let cep18_token = get_contract_hash_v2_binary(&builder); + + // events check + let entity = entity(&builder, &cep18_token); + + let (topic_name, message_topic_hash) = entity + .message_topics() + .iter() + .last() + .expect("should have at least one topic"); + + let mut user_map: BTreeMap = BTreeMap::new(); + user_map.insert(Key::Account(*DEFAULT_ACCOUNT_ADDR), true); + user_map.insert(TOKEN_OWNER_ADDRESS_1_OLD, true); + + let sec_key_migrate_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + cep18_token, + MIGRATE_USER_SEC_KEYS_ENTRY_POINT_NAME, + runtime_args! {EVENTS => true, REVERT => true, USER_KEY_MAP => &user_map}, + ) + .build(); + + builder + .exec(sec_key_migrate_request) + .expect_success() + .commit(); + + assert_eq!(topic_name, &EVENTS.to_string()); + + let mint_request = ExecuteRequestBuilder::contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + cep18_token, + METHOD_MINT, + runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_1, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_1)}, + ) + .build(); + + builder.exec(mint_request).expect_success().commit(); + + assert_eq!( + message_topic(&builder, &cep18_token, *message_topic_hash).message_count(), + 3 + ); + + message_summary(&builder, &cep18_token, message_topic_hash, 0, None).unwrap(); } diff --git a/tests/src/mint_and_burn.rs b/tests/src/mint_and_burn.rs index e655e4be..5c8818fa 100644 --- a/tests/src/mint_and_burn.rs +++ b/tests/src/mint_and_burn.rs @@ -1,37 +1,43 @@ use casper_engine_test_support::{ExecuteRequestBuilder, DEFAULT_ACCOUNT_ADDR}; -use casper_types::{runtime_args, ApiError, Key, RuntimeArgs, U256}; +use casper_types::{runtime_args, AddressableEntityHash, ApiError, Key, U256}; use crate::utility::{ constants::{ - ACCOUNT_1_ADDR, ADMIN_LIST, AMOUNT, ARG_AMOUNT, ARG_DECIMALS, ARG_NAME, ARG_OWNER, - ARG_SYMBOL, ARG_TOTAL_SUPPLY, CHANGE_SECURITY, ENABLE_MINT_BURN, - ERROR_INSUFFICIENT_BALANCE, ERROR_OVERFLOW, METHOD_BURN, METHOD_MINT, MINTER_LIST, - NONE_LIST, OWNER, TOKEN_DECIMALS, TOKEN_NAME, TOKEN_OWNER_ADDRESS_1, TOKEN_OWNER_ADDRESS_2, - TOKEN_OWNER_AMOUNT_1, TOKEN_OWNER_AMOUNT_2, TOKEN_SYMBOL, TOKEN_TOTAL_SUPPLY, + ADMIN_LIST, AMOUNT, ARG_AMOUNT, ARG_DECIMALS, ARG_NAME, ARG_OWNER, ARG_SYMBOL, + ARG_TOTAL_SUPPLY, CHANGE_SECURITY, ENABLE_MINT_BURN, ERROR_INSUFFICIENT_BALANCE, + ERROR_OVERFLOW, METHOD_BURN, METHOD_MINT, MINTER_LIST, NONE_LIST, OWNER, TOKEN_DECIMALS, + TOKEN_NAME, TOKEN_OWNER_ADDRESS_1, TOKEN_OWNER_ADDRESS_2, TOKEN_OWNER_AMOUNT_1, + TOKEN_OWNER_AMOUNT_2, TOKEN_SYMBOL, TOKEN_TOTAL_SUPPLY, }, installer_request_builders::{ - cep18_check_balance_of, cep18_check_total_supply, setup_with_args, TestContext, + cep18_check_balance_of, cep18_check_total_supply, get_test_account, setup_with_args, + TestContext, }, }; -use casper_execution_engine::core::{ - engine_state::Error as CoreError, execution::Error as ExecError, -}; +use casper_execution_engine::{engine_state::Error as CoreError, execution::ExecError}; #[test] fn test_mint_and_burn_tokens() { let mint_amount = U256::one(); - let (mut builder, TestContext { cep18_token, .. }) = setup_with_args(runtime_args! { + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup_with_args(runtime_args! { ARG_NAME => TOKEN_NAME, ARG_SYMBOL => TOKEN_SYMBOL, ARG_DECIMALS => TOKEN_DECIMALS, ARG_TOTAL_SUPPLY => U256::from(TOKEN_TOTAL_SUPPLY), ENABLE_MINT_BURN => true, }); + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); let mint_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - cep18_token, + addressable_cep18_contract_hash, METHOD_MINT, runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_1, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_1)}, ) @@ -39,7 +45,7 @@ fn test_mint_and_burn_tokens() { builder.exec(mint_request).expect_success().commit(); let mint_request_2 = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - cep18_token, + addressable_cep18_contract_hash, METHOD_MINT, runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_2, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_2)}, ) @@ -48,24 +54,26 @@ fn test_mint_and_burn_tokens() { assert_eq!( cep18_check_balance_of( &mut builder, - &cep18_token, - Key::Account(*DEFAULT_ACCOUNT_ADDR) + &cep18_contract_hash, + Key::AddressableEntity(casper_types::EntityAddr::Account( + DEFAULT_ACCOUNT_ADDR.value() + )) ), U256::from(TOKEN_TOTAL_SUPPLY), ); assert_eq!( - cep18_check_balance_of(&mut builder, &cep18_token, TOKEN_OWNER_ADDRESS_1), + cep18_check_balance_of(&mut builder, &cep18_contract_hash, TOKEN_OWNER_ADDRESS_1), U256::from(TOKEN_OWNER_AMOUNT_1) ); assert_eq!( - cep18_check_balance_of(&mut builder, &cep18_token, TOKEN_OWNER_ADDRESS_2), + cep18_check_balance_of(&mut builder, &cep18_contract_hash, TOKEN_OWNER_ADDRESS_2), U256::from(TOKEN_OWNER_AMOUNT_2) ); - let total_supply_before_mint = cep18_check_total_supply(&mut builder, &cep18_token); + let total_supply_before_mint = cep18_check_total_supply(&mut builder, &cep18_contract_hash); let mint_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - cep18_token, + addressable_cep18_contract_hash, METHOD_MINT, runtime_args! { ARG_OWNER => TOKEN_OWNER_ADDRESS_1, @@ -77,15 +85,15 @@ fn test_mint_and_burn_tokens() { builder.exec(mint_request).expect_success().commit(); assert_eq!( - cep18_check_balance_of(&mut builder, &cep18_token, TOKEN_OWNER_ADDRESS_1), + cep18_check_balance_of(&mut builder, &cep18_contract_hash, TOKEN_OWNER_ADDRESS_1), U256::from(TOKEN_OWNER_AMOUNT_1) + mint_amount, ); assert_eq!( - cep18_check_balance_of(&mut builder, &cep18_token, TOKEN_OWNER_ADDRESS_2), + cep18_check_balance_of(&mut builder, &cep18_contract_hash, TOKEN_OWNER_ADDRESS_2), U256::from(TOKEN_OWNER_AMOUNT_2) ); - let total_supply_after_mint = cep18_check_total_supply(&mut builder, &cep18_token); + let total_supply_after_mint = cep18_check_total_supply(&mut builder, &cep18_contract_hash); assert_eq!( total_supply_after_mint, total_supply_before_mint + mint_amount, @@ -94,10 +102,10 @@ fn test_mint_and_burn_tokens() { let burn_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - cep18_token, + addressable_cep18_contract_hash, METHOD_BURN, runtime_args! { - ARG_OWNER => Key::Account(*DEFAULT_ACCOUNT_ADDR), + ARG_OWNER => Key::AddressableEntity(casper_types::EntityAddr::Account(DEFAULT_ACCOUNT_ADDR.value())), ARG_AMOUNT => mint_amount, }, ) @@ -108,16 +116,18 @@ fn test_mint_and_burn_tokens() { assert_eq!( cep18_check_balance_of( &mut builder, - &cep18_token, - Key::Account(*DEFAULT_ACCOUNT_ADDR) + &cep18_contract_hash, + Key::AddressableEntity(casper_types::EntityAddr::Account( + DEFAULT_ACCOUNT_ADDR.value() + )) ), U256::from(999999999), ); assert_eq!( - cep18_check_balance_of(&mut builder, &cep18_token, TOKEN_OWNER_ADDRESS_2), + cep18_check_balance_of(&mut builder, &cep18_contract_hash, TOKEN_OWNER_ADDRESS_2), U256::from(TOKEN_OWNER_AMOUNT_2) ); - let total_supply_after_burn = cep18_check_total_supply(&mut builder, &cep18_token); + let total_supply_after_burn = cep18_check_total_supply(&mut builder, &cep18_contract_hash); assert_eq!( total_supply_after_burn, total_supply_before_burn - mint_amount, @@ -130,7 +140,13 @@ fn test_mint_and_burn_tokens() { fn test_should_not_mint_above_limits() { let mint_amount = U256::MAX; - let (mut builder, TestContext { cep18_token, .. }) = setup_with_args(runtime_args! { + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup_with_args(runtime_args! { ARG_NAME => TOKEN_NAME, ARG_SYMBOL => TOKEN_SYMBOL, ARG_DECIMALS => TOKEN_DECIMALS, @@ -138,9 +154,10 @@ fn test_should_not_mint_above_limits() { "enable_mint_burn" => true, }); + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); let mint_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - cep18_token, + addressable_cep18_contract_hash, METHOD_MINT, runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_1, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_1)}, ) @@ -148,20 +165,20 @@ fn test_should_not_mint_above_limits() { builder.exec(mint_request).expect_success().commit(); let mint_request_2 = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - cep18_token, + addressable_cep18_contract_hash, METHOD_MINT, runtime_args! {OWNER => TOKEN_OWNER_ADDRESS_2, AMOUNT => U256::from(TOKEN_OWNER_AMOUNT_2)}, ) .build(); builder.exec(mint_request_2).expect_success().commit(); assert_eq!( - cep18_check_balance_of(&mut builder, &cep18_token, TOKEN_OWNER_ADDRESS_1), + cep18_check_balance_of(&mut builder, &cep18_contract_hash, TOKEN_OWNER_ADDRESS_1), U256::from(TOKEN_OWNER_AMOUNT_1) ); let mint_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - cep18_token, + addressable_cep18_contract_hash, METHOD_MINT, runtime_args! { ARG_OWNER => TOKEN_OWNER_ADDRESS_1, @@ -175,14 +192,20 @@ fn test_should_not_mint_above_limits() { let error = builder.get_error().expect("should have error"); assert!( matches!(error, CoreError::Exec(ExecError::Revert(ApiError::User(user_error))) if user_error == ERROR_OVERFLOW), - "{:?}", + "Should not mint above limits, but instead: {:?}", error ); } #[test] fn test_should_not_burn_above_balance() { - let (mut builder, TestContext { cep18_token, .. }) = setup_with_args(runtime_args! { + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup_with_args(runtime_args! { ARG_NAME => TOKEN_NAME, ARG_SYMBOL => TOKEN_SYMBOL, ARG_DECIMALS => TOKEN_DECIMALS, @@ -190,12 +213,13 @@ fn test_should_not_burn_above_balance() { "enable_mint_burn" => true, }); + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); let burn_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - cep18_token, + addressable_cep18_contract_hash, METHOD_BURN, runtime_args! { - ARG_OWNER => Key::Account(*DEFAULT_ACCOUNT_ADDR), + ARG_OWNER => Key::AddressableEntity(casper_types::EntityAddr::Account(DEFAULT_ACCOUNT_ADDR.value())), ARG_AMOUNT => U256::from(TOKEN_TOTAL_SUPPLY)+1, }, ) @@ -215,7 +239,13 @@ fn test_should_not_burn_above_balance() { fn test_should_not_mint_or_burn_with_entrypoint_disabled() { let mint_amount = U256::one(); - let (mut builder, TestContext { cep18_token, .. }) = setup_with_args(runtime_args! { + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup_with_args(runtime_args! { ARG_NAME => TOKEN_NAME, ARG_SYMBOL => TOKEN_SYMBOL, ARG_DECIMALS => TOKEN_DECIMALS, @@ -223,9 +253,10 @@ fn test_should_not_mint_or_burn_with_entrypoint_disabled() { ENABLE_MINT_BURN => false, }); + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); let mint_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - cep18_token, + addressable_cep18_contract_hash, METHOD_MINT, runtime_args! { ARG_OWNER => TOKEN_OWNER_ADDRESS_1, @@ -245,7 +276,7 @@ fn test_should_not_mint_or_burn_with_entrypoint_disabled() { let burn_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - cep18_token, + addressable_cep18_contract_hash, METHOD_BURN, runtime_args! { ARG_OWNER => TOKEN_OWNER_ADDRESS_1, @@ -266,9 +297,17 @@ fn test_should_not_mint_or_burn_with_entrypoint_disabled() { #[test] fn test_security_no_rights() { + let (account_user_1_key, account_user_1_account_hash, _) = get_test_account("ACCOUNT_USER_1"); + let mint_amount = U256::one(); - let (mut builder, TestContext { cep18_token, .. }) = setup_with_args(runtime_args! { + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup_with_args(runtime_args! { ARG_NAME => TOKEN_NAME, ARG_SYMBOL => TOKEN_SYMBOL, ARG_DECIMALS => TOKEN_DECIMALS, @@ -276,12 +315,13 @@ fn test_security_no_rights() { ENABLE_MINT_BURN => true, }); + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); let mint_request = ExecuteRequestBuilder::contract_call_by_hash( - *ACCOUNT_1_ADDR, - cep18_token, + account_user_1_account_hash, + addressable_cep18_contract_hash, METHOD_MINT, runtime_args! { - ARG_OWNER => Key::Account(*ACCOUNT_1_ADDR), + ARG_OWNER => account_user_1_key, ARG_AMOUNT => mint_amount, }, ) @@ -298,10 +338,10 @@ fn test_security_no_rights() { let passing_admin_mint_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - cep18_token, + addressable_cep18_contract_hash, METHOD_MINT, runtime_args! { - ARG_OWNER => Key::Account(*ACCOUNT_1_ADDR), + ARG_OWNER => account_user_1_key, ARG_AMOUNT => mint_amount, }, ) @@ -313,11 +353,11 @@ fn test_security_no_rights() { .commit(); let burn_request = ExecuteRequestBuilder::contract_call_by_hash( - *ACCOUNT_1_ADDR, - cep18_token, + account_user_1_account_hash, + addressable_cep18_contract_hash, METHOD_BURN, runtime_args! { - ARG_OWNER => Key::Account(*ACCOUNT_1_ADDR), + ARG_OWNER => account_user_1_key, ARG_AMOUNT => mint_amount, }, ) @@ -328,20 +368,28 @@ fn test_security_no_rights() { #[test] fn test_security_minter_rights() { + let (account_user_1_key, account_user_1_account_hash, _) = get_test_account("ACCOUNT_USER_1"); let mint_amount = U256::one(); - let (mut builder, TestContext { cep18_token, .. }) = setup_with_args(runtime_args! { + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup_with_args(runtime_args! { ARG_NAME => TOKEN_NAME, ARG_SYMBOL => TOKEN_SYMBOL, ARG_DECIMALS => TOKEN_DECIMALS, ARG_TOTAL_SUPPLY => U256::from(TOKEN_TOTAL_SUPPLY), ENABLE_MINT_BURN => true, - MINTER_LIST => vec![Key::Account(*ACCOUNT_1_ADDR)] + MINTER_LIST => vec![account_user_1_key] }); + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); let mint_request = ExecuteRequestBuilder::contract_call_by_hash( - *ACCOUNT_1_ADDR, - cep18_token, + account_user_1_account_hash, + addressable_cep18_contract_hash, METHOD_MINT, runtime_args! { ARG_OWNER => TOKEN_OWNER_ADDRESS_1, @@ -355,9 +403,17 @@ fn test_security_minter_rights() { #[test] fn test_security_burner_rights() { + let (default_account_user_key, _, _) = get_test_account("ACCOUNT_USER_0"); + let (_, account_user_1_account_hash, _) = get_test_account("ACCOUNT_USER_1"); let mint_amount = U256::one(); - let (mut builder, TestContext { cep18_token, .. }) = setup_with_args(runtime_args! { + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup_with_args(runtime_args! { ARG_NAME => TOKEN_NAME, ARG_SYMBOL => TOKEN_SYMBOL, ARG_DECIMALS => TOKEN_DECIMALS, @@ -365,9 +421,10 @@ fn test_security_burner_rights() { ENABLE_MINT_BURN => true, }); + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); let mint_request = ExecuteRequestBuilder::contract_call_by_hash( - *ACCOUNT_1_ADDR, - cep18_token, + account_user_1_account_hash, + addressable_cep18_contract_hash, METHOD_MINT, runtime_args! { ARG_OWNER => TOKEN_OWNER_ADDRESS_1, @@ -388,10 +445,10 @@ fn test_security_burner_rights() { // mint by admin let working_mint_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - cep18_token, + addressable_cep18_contract_hash, METHOD_MINT, runtime_args! { - ARG_OWNER => Key::Account(*DEFAULT_ACCOUNT_ADDR), + ARG_OWNER => default_account_user_key, ARG_AMOUNT => mint_amount, }, ) @@ -402,10 +459,10 @@ fn test_security_burner_rights() { // any user can burn let burn_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - cep18_token, + addressable_cep18_contract_hash, METHOD_BURN, runtime_args! { - ARG_OWNER => Key::Account(*DEFAULT_ACCOUNT_ADDR), + ARG_OWNER => default_account_user_key, ARG_AMOUNT => mint_amount, }, ) @@ -416,23 +473,31 @@ fn test_security_burner_rights() { #[test] fn test_change_security() { + let (account_user_1_key, account_user_1_account_hash, _) = get_test_account("ACCOUNT_USER_1"); let mint_amount = U256::one(); - let (mut builder, TestContext { cep18_token, .. }) = setup_with_args(runtime_args! { + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup_with_args(runtime_args! { ARG_NAME => TOKEN_NAME, ARG_SYMBOL => TOKEN_SYMBOL, ARG_DECIMALS => TOKEN_DECIMALS, ARG_TOTAL_SUPPLY => U256::from(TOKEN_TOTAL_SUPPLY), ENABLE_MINT_BURN => true, - ADMIN_LIST => vec![Key::Account(*ACCOUNT_1_ADDR)] + ADMIN_LIST => vec![account_user_1_key] }); + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); let change_security_request = ExecuteRequestBuilder::contract_call_by_hash( - *ACCOUNT_1_ADDR, - cep18_token, + account_user_1_account_hash, + addressable_cep18_contract_hash, CHANGE_SECURITY, runtime_args! { - NONE_LIST => vec![Key::Account(*DEFAULT_ACCOUNT_ADDR)], + NONE_LIST => vec![Key::AddressableEntity(casper_types::EntityAddr::Account(DEFAULT_ACCOUNT_ADDR.value()))], }, ) .build(); @@ -444,7 +509,7 @@ fn test_change_security() { let mint_request = ExecuteRequestBuilder::contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - cep18_token, + addressable_cep18_contract_hash, METHOD_MINT, runtime_args! { ARG_OWNER => TOKEN_OWNER_ADDRESS_1, diff --git a/tests/src/transfer.rs b/tests/src/transfer.rs index 997acc36..0c073edf 100644 --- a/tests/src/transfer.rs +++ b/tests/src/transfer.rs @@ -1,50 +1,59 @@ use casper_engine_test_support::{ExecuteRequestBuilder, DEFAULT_ACCOUNT_ADDR}; -use casper_types::{runtime_args, ApiError, Key, RuntimeArgs, U256}; +use casper_types::{ + addressable_entity::EntityKindTag, runtime_args, AddressableEntityHash, ApiError, EntityAddr, + Key, U256, +}; use crate::utility::{ constants::{ - ACCOUNT_1_ADDR, ACCOUNT_2_ADDR, ALLOWANCE_AMOUNT_1, ARG_AMOUNT, ARG_OWNER, ARG_RECIPIENT, - ARG_SPENDER, ARG_TOKEN_CONTRACT, ERROR_INSUFFICIENT_BALANCE, METHOD_APPROVE, - METHOD_FROM_AS_STORED_CONTRACT, METHOD_TRANSFER, METHOD_TRANSFER_FROM, TOKEN_TOTAL_SUPPLY, - TOTAL_SUPPLY_KEY, TRANSFER_AMOUNT_1, + ALLOWANCE_AMOUNT_1, ARG_AMOUNT, ARG_OWNER, ARG_RECIPIENT, ARG_SPENDER, ARG_TOKEN_CONTRACT, + ERROR_INSUFFICIENT_BALANCE, METHOD_APPROVE, METHOD_FROM_AS_STORED_CONTRACT, + METHOD_TRANSFER, METHOD_TRANSFER_FROM, TOKEN_TOTAL_SUPPLY, TOTAL_SUPPLY_KEY, + TRANSFER_AMOUNT_1, }, installer_request_builders::{ - cep18_check_allowance_of, cep18_check_balance_of, make_cep18_approve_request, - make_cep18_transfer_request, setup, test_cep18_transfer, TestContext, + cep18_check_allowance_of, cep18_check_balance_of, get_test_account, + make_cep18_approve_request, make_cep18_transfer_request, setup, test_cep18_transfer, + TestContext, }, }; -use casper_execution_engine::core::{ - engine_state::Error as CoreError, execution::Error as ExecError, -}; +use casper_execution_engine::{engine_state::Error as CoreError, execution::ExecError}; #[test] fn should_transfer_full_owned_amount() { - let (mut builder, TestContext { cep18_token, .. }) = setup(); + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup(); + + let (default_account_user_key, _, _) = get_test_account("ACCOUNT_USER_0"); + let (account_user_1_key, _, _) = get_test_account("ACCOUNT_USER_1"); + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); let initial_supply = U256::from(TOKEN_TOTAL_SUPPLY); let transfer_amount_1 = initial_supply; let transfer_1_sender = *DEFAULT_ACCOUNT_ADDR; let cep18_transfer_1_args = runtime_args! { - ARG_RECIPIENT => Key::Account(*ACCOUNT_1_ADDR), + ARG_RECIPIENT => account_user_1_key, ARG_AMOUNT => transfer_amount_1, }; - let owner_balance_before = cep18_check_balance_of( - &mut builder, - &cep18_token, - Key::Account(*DEFAULT_ACCOUNT_ADDR), - ); + let owner_balance_before = + cep18_check_balance_of(&mut builder, &cep18_contract_hash, default_account_user_key); assert_eq!(owner_balance_before, initial_supply); let account_1_balance_before = - cep18_check_balance_of(&mut builder, &cep18_token, Key::Account(*ACCOUNT_1_ADDR)); + cep18_check_balance_of(&mut builder, &cep18_contract_hash, account_user_1_key); assert_eq!(account_1_balance_before, U256::zero()); let token_transfer_request_1 = ExecuteRequestBuilder::contract_call_by_hash( transfer_1_sender, - cep18_token, + addressable_cep18_contract_hash, METHOD_TRANSFER, cep18_transfer_1_args, ) @@ -56,50 +65,59 @@ fn should_transfer_full_owned_amount() { .commit(); let account_1_balance_after = - cep18_check_balance_of(&mut builder, &cep18_token, Key::Account(*ACCOUNT_1_ADDR)); + cep18_check_balance_of(&mut builder, &cep18_contract_hash, account_user_1_key); assert_eq!(account_1_balance_after, transfer_amount_1); - let owner_balance_after = cep18_check_balance_of( - &mut builder, - &cep18_token, - Key::Account(*DEFAULT_ACCOUNT_ADDR), - ); + let owner_balance_after = + cep18_check_balance_of(&mut builder, &cep18_contract_hash, default_account_user_key); assert_eq!(owner_balance_after, U256::zero()); - let total_supply: U256 = builder.get_value(cep18_token, TOTAL_SUPPLY_KEY); + let total_supply: U256 = builder.get_value( + EntityAddr::new_smart_contract(cep18_contract_hash.value()), + TOTAL_SUPPLY_KEY, + ); assert_eq!(total_supply, initial_supply); } #[test] fn should_not_transfer_more_than_owned_balance() { - let (mut builder, TestContext { cep18_token, .. }) = setup(); + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup(); + + let (default_account_user_key, default_account_user_account_hash, _) = + get_test_account("ACCOUNT_USER_0"); + let (account_user_1_key, _, _) = get_test_account("ACCOUNT_USER_1"); + + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); let initial_supply = U256::from(TOKEN_TOTAL_SUPPLY); let transfer_amount = initial_supply + U256::one(); - let transfer_1_sender = *DEFAULT_ACCOUNT_ADDR; - let transfer_1_recipient = *ACCOUNT_1_ADDR; + let transfer_1_sender = default_account_user_account_hash; + let transfer_1_recipient = account_user_1_key; let cep18_transfer_1_args = runtime_args! { - ARG_RECIPIENT => Key::Account(transfer_1_recipient), + ARG_RECIPIENT => transfer_1_recipient, ARG_AMOUNT => transfer_amount, }; - let owner_balance_before = cep18_check_balance_of( - &mut builder, - &cep18_token, - Key::Account(*DEFAULT_ACCOUNT_ADDR), - ); + let owner_balance_before = + cep18_check_balance_of(&mut builder, &cep18_contract_hash, default_account_user_key); assert_eq!(owner_balance_before, initial_supply); assert!(transfer_amount > owner_balance_before); let account_1_balance_before = - cep18_check_balance_of(&mut builder, &cep18_token, Key::Account(*ACCOUNT_1_ADDR)); + cep18_check_balance_of(&mut builder, &cep18_contract_hash, account_user_1_key); assert_eq!(account_1_balance_before, U256::zero()); let token_transfer_request_1 = ExecuteRequestBuilder::contract_call_by_hash( transfer_1_sender, - cep18_token, + addressable_cep18_contract_hash, METHOD_TRANSFER, cep18_transfer_1_args, ) @@ -114,50 +132,62 @@ fn should_not_transfer_more_than_owned_balance() { error ); - let account_1_balance_after = cep18_check_balance_of( - &mut builder, - &cep18_token, - Key::Account(transfer_1_recipient), - ); + let account_1_balance_after = + cep18_check_balance_of(&mut builder, &cep18_contract_hash, account_user_1_key); assert_eq!(account_1_balance_after, account_1_balance_before); let owner_balance_after = - cep18_check_balance_of(&mut builder, &cep18_token, Key::Account(transfer_1_sender)); + cep18_check_balance_of(&mut builder, &cep18_contract_hash, default_account_user_key); assert_eq!(owner_balance_after, initial_supply); - let total_supply: U256 = builder.get_value(cep18_token, TOTAL_SUPPLY_KEY); + let total_supply: U256 = builder.get_value( + EntityAddr::new_smart_contract(cep18_contract_hash.value()), + TOTAL_SUPPLY_KEY, + ); assert_eq!(total_supply, initial_supply); } #[test] fn should_transfer_from_from_account_to_account() { - let (mut builder, TestContext { cep18_token, .. }) = setup(); + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup(); + + let (default_account_user_key, default_account_user_account_hash, _) = + get_test_account("ACCOUNT_USER_0"); + let (account_user_1_key, account_user_1_account_hash, _) = get_test_account("ACCOUNT_USER_1"); + + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); let initial_supply = U256::from(TOKEN_TOTAL_SUPPLY); let allowance_amount_1 = U256::from(ALLOWANCE_AMOUNT_1); let transfer_from_amount_1 = allowance_amount_1; - let owner = *DEFAULT_ACCOUNT_ADDR; - let spender = *ACCOUNT_1_ADDR; + let owner = default_account_user_account_hash; + let spender = account_user_1_account_hash; let cep18_approve_args = runtime_args! { - ARG_OWNER => Key::Account(owner), - ARG_SPENDER => Key::Account(spender), + ARG_OWNER => default_account_user_key, + ARG_SPENDER => account_user_1_key, ARG_AMOUNT => allowance_amount_1, }; let cep18_transfer_from_args = runtime_args! { - ARG_OWNER => Key::Account(owner), - ARG_RECIPIENT => Key::Account(spender), + ARG_OWNER => default_account_user_key, + ARG_RECIPIENT => account_user_1_key, ARG_AMOUNT => transfer_from_amount_1, }; let spender_allowance_before = - cep18_check_allowance_of(&mut builder, Key::Account(owner), Key::Account(spender)); + cep18_check_allowance_of(&mut builder, default_account_user_key, account_user_1_key); assert_eq!(spender_allowance_before, U256::zero()); let approve_request_1 = ExecuteRequestBuilder::contract_call_by_hash( owner, - cep18_token, + addressable_cep18_contract_hash, METHOD_APPROVE, cep18_approve_args, ) @@ -165,7 +195,7 @@ fn should_transfer_from_from_account_to_account() { let transfer_from_request_1 = ExecuteRequestBuilder::contract_call_by_hash( spender, - cep18_token, + addressable_cep18_contract_hash, METHOD_TRANSFER_FROM, cep18_transfer_from_args, ) @@ -174,11 +204,11 @@ fn should_transfer_from_from_account_to_account() { builder.exec(approve_request_1).expect_success().commit(); let account_1_balance_before = - cep18_check_balance_of(&mut builder, &cep18_token, Key::Account(owner)); + cep18_check_balance_of(&mut builder, &cep18_contract_hash, default_account_user_key); assert_eq!(account_1_balance_before, initial_supply); let account_1_allowance_before = - cep18_check_allowance_of(&mut builder, Key::Account(owner), Key::Account(spender)); + cep18_check_allowance_of(&mut builder, default_account_user_key, account_user_1_key); assert_eq!(account_1_allowance_before, allowance_amount_1); builder @@ -187,14 +217,14 @@ fn should_transfer_from_from_account_to_account() { .commit(); let account_1_allowance_after = - cep18_check_allowance_of(&mut builder, Key::Account(owner), Key::Account(spender)); + cep18_check_allowance_of(&mut builder, default_account_user_key, account_user_1_key); assert_eq!( account_1_allowance_after, account_1_allowance_before - transfer_from_amount_1 ); let account_1_balance_after = - cep18_check_balance_of(&mut builder, &cep18_token, Key::Account(owner)); + cep18_check_balance_of(&mut builder, &cep18_contract_hash, default_account_user_key); assert_eq!( account_1_balance_after, account_1_balance_before - transfer_from_amount_1 @@ -206,40 +236,45 @@ fn should_transfer_from_account_by_contract() { let ( mut builder, TestContext { - cep18_token, + cep18_contract_hash, cep18_test_contract_package, .. }, ) = setup(); + let (default_account_user_key, _, _) = get_test_account("ACCOUNT_USER_0"); + let (account_user_1_key, _, _) = get_test_account("ACCOUNT_USER_1"); + + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); + let initial_supply = U256::from(TOKEN_TOTAL_SUPPLY); let allowance_amount_1 = U256::from(ALLOWANCE_AMOUNT_1); let transfer_from_amount_1 = allowance_amount_1; let owner = *DEFAULT_ACCOUNT_ADDR; - let spender = Key::Hash(cep18_test_contract_package.value()); - let recipient = Key::Account(*ACCOUNT_1_ADDR); + let spender = Key::Package(cep18_test_contract_package.value()); + let recipient = account_user_1_key; let cep18_approve_args = runtime_args! { - ARG_OWNER => Key::Account(owner), + ARG_OWNER => default_account_user_key, ARG_SPENDER => spender, ARG_AMOUNT => allowance_amount_1, }; let cep18_transfer_from_args = runtime_args! { - ARG_TOKEN_CONTRACT => Key::from(cep18_token), - ARG_OWNER => Key::Account(owner), + ARG_TOKEN_CONTRACT => Key::addressable_entity_key(EntityKindTag::SmartContract, cep18_contract_hash), + ARG_OWNER => default_account_user_key, ARG_RECIPIENT => recipient, ARG_AMOUNT => transfer_from_amount_1, }; let spender_allowance_before = - cep18_check_allowance_of(&mut builder, Key::Account(owner), spender); + cep18_check_allowance_of(&mut builder, default_account_user_key, spender); assert_eq!(spender_allowance_before, U256::zero()); let approve_request_1 = ExecuteRequestBuilder::contract_call_by_hash( owner, - cep18_token, + addressable_cep18_contract_hash, METHOD_APPROVE, cep18_approve_args, ) @@ -257,11 +292,11 @@ fn should_transfer_from_account_by_contract() { builder.exec(approve_request_1).expect_success().commit(); let owner_balance_before = - cep18_check_balance_of(&mut builder, &cep18_token, Key::Account(owner)); + cep18_check_balance_of(&mut builder, &cep18_contract_hash, default_account_user_key); assert_eq!(owner_balance_before, initial_supply); let spender_allowance_before = - cep18_check_allowance_of(&mut builder, Key::Account(owner), spender); + cep18_check_allowance_of(&mut builder, default_account_user_key, spender); assert_eq!(spender_allowance_before, allowance_amount_1); builder @@ -270,14 +305,14 @@ fn should_transfer_from_account_by_contract() { .commit(); let spender_allowance_after = - cep18_check_allowance_of(&mut builder, Key::Account(owner), spender); + cep18_check_allowance_of(&mut builder, default_account_user_key, spender); assert_eq!( spender_allowance_after, spender_allowance_before - transfer_from_amount_1 ); let owner_balance_after = - cep18_check_balance_of(&mut builder, &cep18_token, Key::Account(owner)); + cep18_check_balance_of(&mut builder, &cep18_contract_hash, default_account_user_key); assert_eq!( owner_balance_after, owner_balance_before - transfer_from_amount_1 @@ -286,20 +321,29 @@ fn should_transfer_from_account_by_contract() { #[test] fn should_not_be_able_to_own_transfer() { - let (mut builder, TestContext { cep18_token, .. }) = setup(); + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup(); - let sender = Key::Account(*DEFAULT_ACCOUNT_ADDR); - let recipient = Key::Account(*DEFAULT_ACCOUNT_ADDR); + let (default_account_user_key, _, _) = get_test_account("ACCOUNT_USER_0"); + + let sender = default_account_user_key; + let recipient = default_account_user_key; let transfer_amount = U256::from(TRANSFER_AMOUNT_1); - let sender_balance_before = cep18_check_balance_of(&mut builder, &cep18_token, sender); - let recipient_balance_before = cep18_check_balance_of(&mut builder, &cep18_token, recipient); + let sender_balance_before = cep18_check_balance_of(&mut builder, &cep18_contract_hash, sender); + let recipient_balance_before = + cep18_check_balance_of(&mut builder, &cep18_contract_hash, recipient); assert_eq!(sender_balance_before, recipient_balance_before); let token_transfer_request_1 = - make_cep18_transfer_request(sender, &cep18_token, recipient, transfer_amount); + make_cep18_transfer_request(sender, &cep18_contract_hash, recipient, transfer_amount); builder.exec(token_transfer_request_1).commit(); @@ -313,18 +357,30 @@ fn should_not_be_able_to_own_transfer() { #[test] fn should_not_be_able_to_own_transfer_from() { - let (mut builder, TestContext { cep18_token, .. }) = setup(); + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup(); - let owner = Key::Account(*DEFAULT_ACCOUNT_ADDR); - let spender = Key::Account(*DEFAULT_ACCOUNT_ADDR); - let sender = Key::Account(*DEFAULT_ACCOUNT_ADDR); - let recipient = Key::Account(*DEFAULT_ACCOUNT_ADDR); + let (default_account_user_key, default_account_account_hash, _) = + get_test_account("ACCOUNT_USER_0"); + + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); + + let sender = default_account_account_hash; + let owner = default_account_user_key; + let spender = default_account_user_key; + let sender_key = default_account_user_key; + let recipient = default_account_user_key; let allowance_amount = U256::from(ALLOWANCE_AMOUNT_1); let transfer_amount = U256::from(TRANSFER_AMOUNT_1); let approve_request = - make_cep18_approve_request(sender, &cep18_token, spender, allowance_amount); + make_cep18_approve_request(sender_key, &cep18_contract_hash, spender, allowance_amount); builder.exec(approve_request).commit(); @@ -335,8 +391,10 @@ fn should_not_be_able_to_own_transfer_from() { error ); - let sender_balance_before = cep18_check_balance_of(&mut builder, &cep18_token, sender); - let recipient_balance_before = cep18_check_balance_of(&mut builder, &cep18_token, recipient); + let sender_balance_before = + cep18_check_balance_of(&mut builder, &cep18_contract_hash, sender_key); + let recipient_balance_before = + cep18_check_balance_of(&mut builder, &cep18_contract_hash, recipient); assert_eq!(sender_balance_before, recipient_balance_before); @@ -347,8 +405,8 @@ fn should_not_be_able_to_own_transfer_from() { ARG_AMOUNT => transfer_amount, }; ExecuteRequestBuilder::contract_call_by_hash( - sender.into_account().unwrap(), - cep18_token, + sender, + addressable_cep18_contract_hash, METHOD_TRANSFER_FROM, cep18_transfer_from_args, ) @@ -367,61 +425,87 @@ fn should_not_be_able_to_own_transfer_from() { #[test] fn should_verify_zero_amount_transfer_is_noop() { - let (mut builder, TestContext { cep18_token, .. }) = setup(); + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup(); - let sender = Key::Account(*DEFAULT_ACCOUNT_ADDR); - let recipient = Key::Account(*ACCOUNT_1_ADDR); + let (default_account_user_key, _, _) = get_test_account("ACCOUNT_USER_0"); + let (account_user_1_key, _, _) = get_test_account("ACCOUNT_USER_1"); + + let sender = default_account_user_key; + let recipient = account_user_1_key; let transfer_amount = U256::zero(); - let sender_balance_before = cep18_check_balance_of(&mut builder, &cep18_token, sender); - let recipient_balance_before = cep18_check_balance_of(&mut builder, &cep18_token, recipient); + let sender_balance_before = cep18_check_balance_of(&mut builder, &cep18_contract_hash, sender); + let recipient_balance_before = + cep18_check_balance_of(&mut builder, &cep18_contract_hash, recipient); let token_transfer_request_1 = - make_cep18_transfer_request(sender, &cep18_token, recipient, transfer_amount); + make_cep18_transfer_request(sender, &cep18_contract_hash, recipient, transfer_amount); builder .exec(token_transfer_request_1) .expect_success() .commit(); - let sender_balance_after = cep18_check_balance_of(&mut builder, &cep18_token, sender); + let sender_balance_after = cep18_check_balance_of(&mut builder, &cep18_contract_hash, sender); assert_eq!(sender_balance_before, sender_balance_after); - let recipient_balance_after = cep18_check_balance_of(&mut builder, &cep18_token, recipient); + let recipient_balance_after = + cep18_check_balance_of(&mut builder, &cep18_contract_hash, recipient); assert_eq!(recipient_balance_before, recipient_balance_after); } #[test] fn should_verify_zero_amount_transfer_from_is_noop() { - let (mut builder, TestContext { cep18_token, .. }) = setup(); + let ( + mut builder, + TestContext { + cep18_contract_hash, + .. + }, + ) = setup(); - let owner = Key::Account(*DEFAULT_ACCOUNT_ADDR); - let spender = Key::Account(*ACCOUNT_1_ADDR); - let recipient = Key::Account(*ACCOUNT_2_ADDR); + let (default_account_user_key, _, _) = get_test_account("ACCOUNT_USER_0"); + let (account_user_1_key, _, _) = get_test_account("ACCOUNT_USER_1"); + let (account_user_2_key, _, _) = get_test_account("ACCOUNT_USER_2"); + + let addressable_cep18_contract_hash = AddressableEntityHash::new(cep18_contract_hash.value()); + + let owner = *DEFAULT_ACCOUNT_ADDR; + let owner_key = default_account_user_key; + let spender = account_user_1_key; + let recipient = account_user_2_key; let allowance_amount = U256::from(1); let transfer_amount = U256::zero(); let approve_request = - make_cep18_approve_request(owner, &cep18_token, spender, allowance_amount); + make_cep18_approve_request(owner_key, &cep18_contract_hash, spender, allowance_amount); builder.exec(approve_request).expect_success().commit(); - let spender_allowance_before = cep18_check_allowance_of(&mut builder, owner, spender); + let spender_allowance_before = cep18_check_allowance_of(&mut builder, owner_key, spender); - let owner_balance_before = cep18_check_balance_of(&mut builder, &cep18_token, owner); - let recipient_balance_before = cep18_check_balance_of(&mut builder, &cep18_token, recipient); + let owner_balance_before = + cep18_check_balance_of(&mut builder, &cep18_contract_hash, owner_key); + let recipient_balance_before = + cep18_check_balance_of(&mut builder, &cep18_contract_hash, recipient); let transfer_from_request = { let cep18_transfer_from_args = runtime_args! { - ARG_OWNER => owner, + ARG_OWNER => owner_key, ARG_RECIPIENT => recipient, ARG_AMOUNT => transfer_amount, }; ExecuteRequestBuilder::contract_call_by_hash( - owner.into_account().unwrap(), - cep18_token, + owner, + addressable_cep18_contract_hash, METHOD_TRANSFER_FROM, cep18_transfer_from_args, ) @@ -433,13 +517,14 @@ fn should_verify_zero_amount_transfer_from_is_noop() { .expect_success() .commit(); - let owner_balance_after = cep18_check_balance_of(&mut builder, &cep18_token, owner); + let owner_balance_after = cep18_check_balance_of(&mut builder, &cep18_contract_hash, owner_key); assert_eq!(owner_balance_before, owner_balance_after); - let recipient_balance_after = cep18_check_balance_of(&mut builder, &cep18_token, recipient); + let recipient_balance_after = + cep18_check_balance_of(&mut builder, &cep18_contract_hash, recipient); assert_eq!(recipient_balance_before, recipient_balance_after); - let spender_allowance_after = cep18_check_allowance_of(&mut builder, owner, spender); + let spender_allowance_after = cep18_check_allowance_of(&mut builder, owner_key, spender); assert_eq!(spender_allowance_after, spender_allowance_before); } @@ -451,10 +536,12 @@ fn should_transfer_contract_to_contract() { .. } = test_context; - let sender1 = Key::Account(*DEFAULT_ACCOUNT_ADDR); - let recipient1 = Key::Hash(cep18_test_contract_package.value()); - let sender2 = Key::Hash(cep18_test_contract_package.value()); - let recipient2 = Key::Hash([42; 32]); + let (default_account_user_key, _, _) = get_test_account("ACCOUNT_USER_0"); + + let sender1 = default_account_user_key; + let recipient1 = Key::Package(cep18_test_contract_package.value()); + let sender2 = Key::Package(cep18_test_contract_package.value()); + let recipient2 = Key::Package([42; 32]); test_cep18_transfer( &mut builder, @@ -474,11 +561,14 @@ fn should_transfer_contract_to_account() { .. } = test_context; - let sender1 = Key::Account(*DEFAULT_ACCOUNT_ADDR); - let recipient1 = Key::Hash(cep18_test_contract_package.value()); + let (default_account_user_key, _, _) = get_test_account("ACCOUNT_USER_0"); + let (account_user_1_key, _, _) = get_test_account("ACCOUNT_USER_1"); + + let sender1 = default_account_user_key; + let recipient1 = Key::Package(cep18_test_contract_package.value()); - let sender2 = Key::Hash(cep18_test_contract_package.value()); - let recipient2 = Key::Account(*ACCOUNT_1_ADDR); + let sender2 = Key::Package(cep18_test_contract_package.value()); + let recipient2 = account_user_1_key; test_cep18_transfer( &mut builder, @@ -494,10 +584,13 @@ fn should_transfer_contract_to_account() { fn should_transfer_account_to_contract() { let (mut builder, test_context) = setup(); - let sender1 = Key::Account(*DEFAULT_ACCOUNT_ADDR); - let recipient1 = Key::Account(*ACCOUNT_1_ADDR); - let sender2 = Key::Account(*ACCOUNT_1_ADDR); - let recipient2 = Key::Hash(test_context.cep18_test_contract_package.value()); + let (default_account_user_key, _, _) = get_test_account("ACCOUNT_USER_0"); + let (account_user_1_key, _, _) = get_test_account("ACCOUNT_USER_1"); + + let sender1 = default_account_user_key; + let recipient1 = account_user_1_key; + let sender2 = account_user_1_key; + let recipient2 = Key::Package(test_context.cep18_test_contract_package.value()); test_cep18_transfer( &mut builder, @@ -512,10 +605,15 @@ fn should_transfer_account_to_contract() { #[test] fn should_transfer_account_to_account() { let (mut builder, test_context) = setup(); - let sender1 = Key::Account(*DEFAULT_ACCOUNT_ADDR); - let recipient1 = Key::Account(*ACCOUNT_1_ADDR); - let sender2 = Key::Account(*ACCOUNT_1_ADDR); - let recipient2 = Key::Account(*ACCOUNT_2_ADDR); + + let (default_account_user_key, _, _) = get_test_account("ACCOUNT_USER_0"); + let (account_user_1_key, _, _) = get_test_account("ACCOUNT_USER_1"); + let (account_user_2_key, _, _) = get_test_account("ACCOUNT_USER_2"); + + let sender1 = default_account_user_key; + let recipient1 = account_user_1_key; + let sender2 = account_user_1_key; + let recipient2 = account_user_2_key; test_cep18_transfer( &mut builder, diff --git a/tests/src/upgrade.rs b/tests/src/upgrade.rs new file mode 100644 index 00000000..681b5b90 --- /dev/null +++ b/tests/src/upgrade.rs @@ -0,0 +1,57 @@ +use casper_engine_test_support::{ExecuteRequestBuilder, DEFAULT_ACCOUNT_ADDR}; +use casper_types::{runtime_args, Key, U256}; + +use crate::utility::{ + constants::{ + ARG_DECIMALS, ARG_NAME, ARG_SYMBOL, ARG_TOTAL_SUPPLY, CEP18_CONTRACT_WASM, TOKEN_DECIMALS, + TOKEN_NAME, TOKEN_SYMBOL, TOKEN_TOTAL_SUPPLY, + }, + installer_request_builders::setup, +}; + +#[test] +fn should_upgrade_contract_version() { + let (mut builder, ..) = setup(); + + let version_0: u32 = builder + .query( + None, + Key::Account(*DEFAULT_ACCOUNT_ADDR), + &["cep18_contract_version_CasperTest".to_string()], + ) + .unwrap() + .as_cl_value() + .unwrap() + .clone() + .into_t() + .unwrap(); + + let upgrade_request = ExecuteRequestBuilder::standard( + *DEFAULT_ACCOUNT_ADDR, + CEP18_CONTRACT_WASM, + runtime_args! { + ARG_NAME => TOKEN_NAME, + ARG_SYMBOL => TOKEN_SYMBOL, + ARG_DECIMALS => TOKEN_DECIMALS, + ARG_TOTAL_SUPPLY => U256::from(TOKEN_TOTAL_SUPPLY), + }, + ) + .build(); + + builder.exec(upgrade_request).expect_success().commit(); + + let version_1: u32 = builder + .query( + None, + Key::Account(*DEFAULT_ACCOUNT_ADDR), + &["cep18_contract_version_CasperTest".to_string()], + ) + .unwrap() + .as_cl_value() + .unwrap() + .clone() + .into_t() + .unwrap(); + + assert!(version_0 < version_1); +} diff --git a/tests/src/utility/constants.rs b/tests/src/utility/constants.rs index 738e52e8..bdeb2c05 100644 --- a/tests/src/utility/constants.rs +++ b/tests/src/utility/constants.rs @@ -1,11 +1,11 @@ -use casper_types::{account::AccountHash, Key, PublicKey, SecretKey}; -use once_cell::sync::Lazy; +use casper_types::{account::AccountHash, EntityAddr, Key}; pub const CEP18_CONTRACT_WASM: &str = "cep18.wasm"; pub const CEP18_TEST_CONTRACT_WASM: &str = "cep18_test_contract.wasm"; pub const NAME_KEY: &str = "name"; pub const SYMBOL_KEY: &str = "symbol"; pub const CEP18_TOKEN_CONTRACT_KEY: &str = "cep18_contract_hash_CasperTest"; +pub const CEP18_TOKEN_CONTRACT_VERSION_KEY: &str = "cep18_contract_version_CasperTest"; pub const DECIMALS_KEY: &str = "decimals"; pub const TOTAL_SUPPLY_KEY: &str = "total_supply"; pub const BALANCES_KEY: &str = "balances"; @@ -46,18 +46,6 @@ pub const ARG_ADDRESS: &str = "address"; pub const RESULT_KEY: &str = "result"; pub const CEP18_TEST_CONTRACT_KEY: &str = "cep18_test_contract"; -pub static ACCOUNT_1_SECRET_KEY: Lazy = - Lazy::new(|| SecretKey::secp256k1_from_bytes([221u8; 32]).unwrap()); -pub static ACCOUNT_1_PUBLIC_KEY: Lazy = - Lazy::new(|| PublicKey::from(&*ACCOUNT_1_SECRET_KEY)); -pub static ACCOUNT_1_ADDR: Lazy = Lazy::new(|| ACCOUNT_1_PUBLIC_KEY.to_account_hash()); - -pub static ACCOUNT_2_SECRET_KEY: Lazy = - Lazy::new(|| SecretKey::secp256k1_from_bytes([212u8; 32]).unwrap()); -pub static ACCOUNT_2_PUBLIC_KEY: Lazy = - Lazy::new(|| PublicKey::from(&*ACCOUNT_2_SECRET_KEY)); -pub static ACCOUNT_2_ADDR: Lazy = Lazy::new(|| ACCOUNT_2_PUBLIC_KEY.to_account_hash()); - pub const TRANSFER_AMOUNT_1: u64 = 200_001; pub const TRANSFER_AMOUNT_2: u64 = 19_999; pub const ALLOWANCE_AMOUNT_1: u64 = 456_789; @@ -67,10 +55,12 @@ pub const METHOD_TRANSFER_AS_STORED_CONTRACT: &str = "transfer_as_stored_contrac pub const METHOD_APPROVE_AS_STORED_CONTRACT: &str = "approve_as_stored_contract"; pub const METHOD_FROM_AS_STORED_CONTRACT: &str = "transfer_from_as_stored_contract"; -pub const TOKEN_OWNER_ADDRESS_1: Key = Key::Account(AccountHash::new([42; 32])); +pub const TOKEN_OWNER_ADDRESS_1: Key = Key::AddressableEntity(EntityAddr::Account([42; 32])); pub const TOKEN_OWNER_AMOUNT_1: u64 = 1_000_000; -pub const TOKEN_OWNER_ADDRESS_2: Key = Key::Hash([42; 32]); +pub const TOKEN_OWNER_ADDRESS_2: Key = Key::AddressableEntity(EntityAddr::SmartContract([42; 32])); pub const TOKEN_OWNER_AMOUNT_2: u64 = 2_000_000; +pub const TOKEN_OWNER_ADDRESS_1_OLD: Key = Key::Account(AccountHash::new([42; 32])); +pub const _TOKEN_OWNER_ADDRESS_2_OLD: Key = Key::Hash([42; 32]); pub const METHOD_MINT: &str = "mint"; pub const METHOD_BURN: &str = "burn"; @@ -81,3 +71,11 @@ pub const ADMIN_LIST: &str = "admin_list"; pub const MINTER_LIST: &str = "minter_list"; pub const NONE_LIST: &str = "none_list"; pub const CHANGE_SECURITY: &str = "change_security"; + +pub const USER_KEY_MAP: &str = "user_key_map"; +pub const EVENTS: &str = "events"; +pub const REVERT: &str = "revert"; +pub const EVENTS_MODE: &str = "events_mode"; +pub const MIGRATE_USER_BALANCE_KEYS_ENTRY_POINT_NAME: &str = "migrate_user_balance_keys"; +pub const _MIGRATE_USER_ALLOWANCE_KEYS_ENTRY_POINT_NAME: &str = "migrate_user_allowance_keys"; +pub const MIGRATE_USER_SEC_KEYS_ENTRY_POINT_NAME: &str = "migrate_sec_keys"; diff --git a/tests/src/utility/installer_request_builders.rs b/tests/src/utility/installer_request_builders.rs index 5b3c3aba..52e11afc 100644 --- a/tests/src/utility/installer_request_builders.rs +++ b/tests/src/utility/installer_request_builders.rs @@ -1,11 +1,10 @@ use casper_engine_test_support::{ - ExecuteRequestBuilder, InMemoryWasmTestBuilder, DEFAULT_ACCOUNT_ADDR, - MINIMUM_ACCOUNT_CREATION_BALANCE, PRODUCTION_RUN_GENESIS_REQUEST, + utils::create_run_genesis_request, ExecuteRequest, ExecuteRequestBuilder, LmdbWasmTestBuilder, + DEFAULT_ACCOUNTS, DEFAULT_ACCOUNT_ADDR, }; -use casper_execution_engine::core::engine_state::ExecuteRequest; use casper_types::{ - account::AccountHash, bytesrepr::FromBytes, runtime_args, system::mint, CLTyped, ContractHash, - ContractPackageHash, Key, RuntimeArgs, U256, + account::AccountHash, addressable_entity::EntityKindTag, bytesrepr::FromBytes, runtime_args, + AddressableEntityHash, CLTyped, EntityAddr, Key, PackageHash, PublicKey, RuntimeArgs, U256, }; use crate::utility::constants::{ @@ -13,13 +12,12 @@ use crate::utility::constants::{ }; use super::constants::{ - ACCOUNT_1_ADDR, ACCOUNT_2_ADDR, ARG_ADDRESS, ARG_AMOUNT, ARG_DECIMALS, ARG_NAME, ARG_OWNER, - ARG_RECIPIENT, ARG_SPENDER, ARG_SYMBOL, ARG_TOKEN_CONTRACT, ARG_TOTAL_SUPPLY, - CEP18_CONTRACT_WASM, CEP18_TEST_CONTRACT_KEY, CEP18_TEST_CONTRACT_WASM, - CEP18_TOKEN_CONTRACT_KEY, CHECK_ALLOWANCE_OF_ENTRYPOINT, CHECK_BALANCE_OF_ENTRYPOINT, - CHECK_TOTAL_SUPPLY_ENTRYPOINT, METHOD_APPROVE, METHOD_APPROVE_AS_STORED_CONTRACT, - METHOD_TRANSFER, METHOD_TRANSFER_AS_STORED_CONTRACT, RESULT_KEY, TOKEN_DECIMALS, TOKEN_NAME, - TOKEN_SYMBOL, TOKEN_TOTAL_SUPPLY, + ARG_ADDRESS, ARG_AMOUNT, ARG_DECIMALS, ARG_NAME, ARG_OWNER, ARG_RECIPIENT, ARG_SPENDER, + ARG_SYMBOL, ARG_TOKEN_CONTRACT, ARG_TOTAL_SUPPLY, CEP18_CONTRACT_WASM, CEP18_TEST_CONTRACT_KEY, + CEP18_TEST_CONTRACT_WASM, CEP18_TOKEN_CONTRACT_KEY, CHECK_ALLOWANCE_OF_ENTRYPOINT, + CHECK_BALANCE_OF_ENTRYPOINT, CHECK_TOTAL_SUPPLY_ENTRYPOINT, EVENTS_MODE, METHOD_APPROVE, + METHOD_APPROVE_AS_STORED_CONTRACT, METHOD_TRANSFER, METHOD_TRANSFER_AS_STORED_CONTRACT, + RESULT_KEY, TOKEN_DECIMALS, TOKEN_NAME, TOKEN_SYMBOL, TOKEN_TOTAL_SUPPLY, }; /// Converts hash addr of Account into Hash, and Hash into Account @@ -30,45 +28,39 @@ pub(crate) fn invert_cep18_address(address: Key) -> Key { match address { Key::Account(account_hash) => Key::Hash(account_hash.value()), Key::Hash(contract_hash) => Key::Account(AccountHash::new(contract_hash)), + Key::AddressableEntity(entity_addr) => match entity_addr { + EntityAddr::System(_) => panic!("Unsupported Key variant"), + EntityAddr::Account(account) => Key::Package(account), + EntityAddr::SmartContract(_) => panic!("Unsupported Key variant"), + }, + Key::Package(contract_package_hash) => { + Key::AddressableEntity(EntityAddr::Account(contract_package_hash)) + } _ => panic!("Unsupported Key variant"), } } #[derive(Copy, Clone)] pub(crate) struct TestContext { - pub(crate) cep18_token: ContractHash, - pub(crate) cep18_test_contract_package: ContractPackageHash, + pub(crate) cep18_contract_hash: AddressableEntityHash, + pub(crate) cep18_test_contract_package: PackageHash, } -pub(crate) fn setup() -> (InMemoryWasmTestBuilder, TestContext) { +pub(crate) fn setup() -> (LmdbWasmTestBuilder, TestContext) { setup_with_args(runtime_args! { ARG_NAME => TOKEN_NAME, ARG_SYMBOL => TOKEN_SYMBOL, ARG_DECIMALS => TOKEN_DECIMALS, ARG_TOTAL_SUPPLY => U256::from(TOKEN_TOTAL_SUPPLY), + EVENTS_MODE => 2_u8 }) } -pub(crate) fn setup_with_args(install_args: RuntimeArgs) -> (InMemoryWasmTestBuilder, TestContext) { - let mut builder = InMemoryWasmTestBuilder::default(); - builder.run_genesis(&PRODUCTION_RUN_GENESIS_REQUEST); - - let id: Option = None; - let transfer_1_args = runtime_args! { - mint::ARG_TARGET => *ACCOUNT_1_ADDR, - mint::ARG_AMOUNT => MINIMUM_ACCOUNT_CREATION_BALANCE, - mint::ARG_ID => id, - }; - let transfer_2_args = runtime_args! { - mint::ARG_TARGET => *ACCOUNT_2_ADDR, - mint::ARG_AMOUNT => MINIMUM_ACCOUNT_CREATION_BALANCE, - mint::ARG_ID => id, - }; - - let transfer_request_1 = - ExecuteRequestBuilder::transfer(*DEFAULT_ACCOUNT_ADDR, transfer_1_args).build(); - let transfer_request_2 = - ExecuteRequestBuilder::transfer(*DEFAULT_ACCOUNT_ADDR, transfer_2_args).build(); +pub(crate) fn setup_with_args(install_args: RuntimeArgs) -> (LmdbWasmTestBuilder, TestContext) { + let mut builder = LmdbWasmTestBuilder::default(); + builder + .run_genesis(create_run_genesis_request(DEFAULT_ACCOUNTS.to_vec())) + .commit(); let install_request_1 = ExecuteRequestBuilder::standard(*DEFAULT_ACCOUNT_ADDR, CEP18_CONTRACT_WASM, install_args) @@ -81,54 +73,73 @@ pub(crate) fn setup_with_args(install_args: RuntimeArgs) -> (InMemoryWasmTestBui ) .build(); - builder.exec(transfer_request_1).expect_success().commit(); - builder.exec(transfer_request_2).expect_success().commit(); builder.exec(install_request_1).expect_success().commit(); builder.exec(install_request_2).expect_success().commit(); let account = builder - .get_account(*DEFAULT_ACCOUNT_ADDR) - .expect("should have account"); + .get_entity_with_named_keys_by_account_hash(*DEFAULT_ACCOUNT_ADDR) + .unwrap(); + let account_named_keys = account.named_keys(); - let cep18_token = account - .named_keys() + let cep18_contract_hash = account_named_keys .get(CEP18_TOKEN_CONTRACT_KEY) - .and_then(|key| key.into_hash()) - .map(ContractHash::new) + .and_then(|key| key.into_entity_hash()) .expect("should have contract hash"); - let cep18_test_contract_package = account - .named_keys() + let cep18_test_contract_package = account_named_keys .get(CEP18_TEST_CONTRACT_KEY) - .and_then(|key| key.into_hash()) - .map(ContractPackageHash::new) - .expect("should have contract package hash"); + .and_then(|key| key.into_package_hash()) + .expect("should have package hash"); let test_context = TestContext { - cep18_token, + cep18_contract_hash, cep18_test_contract_package, }; (builder, test_context) } +pub(crate) fn get_test_account(ending_string_index: &str) -> (Key, AccountHash, PublicKey) { + let index = ending_string_index + .chars() + .next_back() + .unwrap() + .to_digit(10) + .unwrap_or_default() as usize; + + let accounts = if let Some(account) = DEFAULT_ACCOUNTS.clone().get(index) { + let public_key = account.public_key().clone(); + let account_hash = public_key.to_account_hash(); + let entity_addr = Key::AddressableEntity(EntityAddr::Account(account_hash.value())); + Some((entity_addr, account_hash, public_key)) + } else { + None + }; + + match accounts { + Some(account) => account, + None => { + panic!("No account found for index {}", index); + } + } +} + pub(crate) fn cep18_check_total_supply( - builder: &mut InMemoryWasmTestBuilder, - cep18_contract_hash: &ContractHash, + builder: &mut LmdbWasmTestBuilder, + cep18_contract_hash: &AddressableEntityHash, ) -> U256 { let account = builder - .get_account(*DEFAULT_ACCOUNT_ADDR) + .get_entity_with_named_keys_by_account_hash(*DEFAULT_ACCOUNT_ADDR) .expect("should have account"); let cep18_test_contract_package = account .named_keys() .get(CEP18_TEST_CONTRACT_KEY) - .and_then(|key| key.into_hash()) - .map(ContractPackageHash::new) + .and_then(|key| key.into_package_hash()) .expect("should have test contract hash"); let check_total_supply_args = runtime_args! { - ARG_TOKEN_CONTRACT => Key::from(*cep18_contract_hash), + ARG_TOKEN_CONTRACT => Key::addressable_entity_key(EntityKindTag::SmartContract, *cep18_contract_hash), }; let exec_request = ExecuteRequestBuilder::versioned_contract_call_by_hash( @@ -145,39 +156,39 @@ pub(crate) fn cep18_check_total_supply( } pub(crate) fn get_test_result( - builder: &mut InMemoryWasmTestBuilder, - cep18_test_contract_package: ContractPackageHash, + builder: &mut LmdbWasmTestBuilder, + cep18_test_contract_package: PackageHash, ) -> T { let contract_package = builder - .get_contract_package(cep18_test_contract_package) + .get_package(cep18_test_contract_package) .expect("should have contract package"); let enabled_versions = contract_package.enabled_versions(); - let (_version, contract_hash) = enabled_versions - .iter() - .next_back() - .expect("should have latest version"); - builder.get_value(*contract_hash, RESULT_KEY) + let contract_hash = enabled_versions + .contract_hashes() + .last() + .expect("should have latest version"); + let contract_entity_addr = EntityAddr::new_smart_contract(contract_hash.value()); + builder.get_value(contract_entity_addr, RESULT_KEY) } pub(crate) fn cep18_check_balance_of( - builder: &mut InMemoryWasmTestBuilder, - cep18_contract_hash: &ContractHash, + builder: &mut LmdbWasmTestBuilder, + cep18_contract_hash: &AddressableEntityHash, address: Key, ) -> U256 { let account = builder - .get_account(*DEFAULT_ACCOUNT_ADDR) + .get_entity_with_named_keys_by_account_hash(*DEFAULT_ACCOUNT_ADDR) .expect("should have account"); let cep18_test_contract_package = account .named_keys() .get(CEP18_TEST_CONTRACT_KEY) - .and_then(|key| key.into_hash()) - .map(ContractPackageHash::new) - .expect("should have test contract hash"); + .and_then(|key| key.into_package_hash()) + .expect("should have test contract package hash"); let check_balance_args = runtime_args! { - ARG_TOKEN_CONTRACT => Key::from(*cep18_contract_hash), + ARG_TOKEN_CONTRACT => Key::addressable_entity_key(EntityKindTag::SmartContract, *cep18_contract_hash), ARG_ADDRESS => address, }; let exec_request = ExecuteRequestBuilder::versioned_contract_call_by_hash( @@ -194,28 +205,26 @@ pub(crate) fn cep18_check_balance_of( } pub(crate) fn cep18_check_allowance_of( - builder: &mut InMemoryWasmTestBuilder, + builder: &mut LmdbWasmTestBuilder, owner: Key, spender: Key, ) -> U256 { let account = builder - .get_account(*DEFAULT_ACCOUNT_ADDR) + .get_entity_with_named_keys_by_account_hash(*DEFAULT_ACCOUNT_ADDR) .expect("should have account"); let cep18_contract_hash = account .named_keys() .get(CEP18_TOKEN_CONTRACT_KEY) - .and_then(|key| key.into_hash()) - .map(ContractHash::new) + .and_then(|key| key.into_entity_hash()) .expect("should have test contract hash"); let cep18_test_contract_package = account .named_keys() .get(CEP18_TEST_CONTRACT_KEY) - .and_then(|key| key.into_hash()) - .map(ContractPackageHash::new) + .and_then(|key| key.into_package_hash()) .expect("should have test contract hash"); let check_balance_args = runtime_args! { - ARG_TOKEN_CONTRACT => Key::from(cep18_contract_hash), + ARG_TOKEN_CONTRACT => Key::addressable_entity_key(EntityKindTag::SmartContract, cep18_contract_hash), ARG_OWNER => owner, ARG_SPENDER => spender, }; @@ -233,40 +242,43 @@ pub(crate) fn cep18_check_allowance_of( } pub(crate) fn test_cep18_transfer( - builder: &mut InMemoryWasmTestBuilder, + builder: &mut LmdbWasmTestBuilder, test_context: &TestContext, sender1: Key, recipient1: Key, sender2: Key, recipient2: Key, ) { - let TestContext { cep18_token, .. } = test_context; + let TestContext { + cep18_contract_hash, + .. + } = test_context; let transfer_amount_1 = U256::from(TRANSFER_AMOUNT_1); let transfer_amount_2 = U256::from(TRANSFER_AMOUNT_2); - let sender_balance_before = cep18_check_balance_of(builder, cep18_token, sender1); + let sender_balance_before = cep18_check_balance_of(builder, cep18_contract_hash, sender1); assert_ne!(sender_balance_before, U256::zero()); - let account_1_balance_before = cep18_check_balance_of(builder, cep18_token, recipient1); + let account_1_balance_before = cep18_check_balance_of(builder, cep18_contract_hash, recipient1); assert_eq!(account_1_balance_before, U256::zero()); - let account_2_balance_before = cep18_check_balance_of(builder, cep18_token, recipient1); + let account_2_balance_before = cep18_check_balance_of(builder, cep18_contract_hash, recipient1); assert_eq!(account_2_balance_before, U256::zero()); let token_transfer_request_1 = - make_cep18_transfer_request(sender1, cep18_token, recipient1, transfer_amount_1); + make_cep18_transfer_request(sender1, cep18_contract_hash, recipient1, transfer_amount_1); builder .exec(token_transfer_request_1) .expect_success() .commit(); - let account_1_balance_after = cep18_check_balance_of(builder, cep18_token, recipient1); + let account_1_balance_after = cep18_check_balance_of(builder, cep18_contract_hash, recipient1); assert_eq!(account_1_balance_after, transfer_amount_1); let account_1_balance_before = account_1_balance_after; - let sender_balance_after = cep18_check_balance_of(builder, cep18_token, sender1); + let sender_balance_after = cep18_check_balance_of(builder, cep18_contract_hash, sender1); assert_eq!( sender_balance_after, sender_balance_before - transfer_amount_1 @@ -274,37 +286,37 @@ pub(crate) fn test_cep18_transfer( let sender_balance_before = sender_balance_after; let token_transfer_request_2 = - make_cep18_transfer_request(sender2, cep18_token, recipient2, transfer_amount_2); + make_cep18_transfer_request(sender2, cep18_contract_hash, recipient2, transfer_amount_2); builder .exec(token_transfer_request_2) .expect_success() .commit(); - let sender_balance_after = cep18_check_balance_of(builder, cep18_token, sender1); + let sender_balance_after = cep18_check_balance_of(builder, cep18_contract_hash, sender1); assert_eq!(sender_balance_after, sender_balance_before); - let account_1_balance_after = cep18_check_balance_of(builder, cep18_token, recipient1); + let account_1_balance_after = cep18_check_balance_of(builder, cep18_contract_hash, recipient1); assert!(account_1_balance_after < account_1_balance_before); assert_eq!( account_1_balance_after, transfer_amount_1 - transfer_amount_2 ); - let account_2_balance_after = cep18_check_balance_of(builder, cep18_token, recipient2); + let account_2_balance_after = cep18_check_balance_of(builder, cep18_contract_hash, recipient2); assert_eq!(account_2_balance_after, transfer_amount_2); } pub(crate) fn make_cep18_transfer_request( sender: Key, - cep18_token: &ContractHash, + cep18_contract_hash: &AddressableEntityHash, recipient: Key, amount: U256, ) -> ExecuteRequest { match sender { Key::Account(sender) => ExecuteRequestBuilder::contract_call_by_hash( sender, - *cep18_token, + AddressableEntityHash::new(cep18_contract_hash.value()), METHOD_TRANSFER, runtime_args! { ARG_AMOUNT => amount, @@ -314,30 +326,58 @@ pub(crate) fn make_cep18_transfer_request( .build(), Key::Hash(contract_package_hash) => ExecuteRequestBuilder::versioned_contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - ContractPackageHash::new(contract_package_hash), + PackageHash::new(contract_package_hash), None, METHOD_TRANSFER_AS_STORED_CONTRACT, runtime_args! { - ARG_TOKEN_CONTRACT => Key::from(*cep18_token), + ARG_TOKEN_CONTRACT => Key::addressable_entity_key(EntityKindTag::SmartContract, *cep18_contract_hash), ARG_AMOUNT => amount, ARG_RECIPIENT => recipient, }, ) .build(), + Key::AddressableEntity(entity_addr)=>{ + match entity_addr { + EntityAddr::System(_) => panic!("Not a use case"), + EntityAddr::Account(account_addr) => ExecuteRequestBuilder::contract_call_by_hash( + AccountHash::new(account_addr), + AddressableEntityHash::new(cep18_contract_hash.value()), + METHOD_TRANSFER, + runtime_args! { + ARG_AMOUNT => amount, + ARG_RECIPIENT => recipient, + }, + ) + .build(), + EntityAddr::SmartContract(_contract_hash) => panic!("invalid variant"), + } + } + Key::Package(package_hash) => ExecuteRequestBuilder::versioned_contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + PackageHash::new(package_hash), + None, + METHOD_TRANSFER_AS_STORED_CONTRACT, + runtime_args! { + ARG_TOKEN_CONTRACT => Key::addressable_entity_key(EntityKindTag::SmartContract, *cep18_contract_hash), + ARG_AMOUNT => amount, + ARG_RECIPIENT => recipient, + } + ) + .build(), _ => panic!("Unknown variant"), } } pub(crate) fn make_cep18_approve_request( sender: Key, - cep18_token: &ContractHash, + cep18_contract_hash: &AddressableEntityHash, spender: Key, amount: U256, ) -> ExecuteRequest { match sender { Key::Account(sender) => ExecuteRequestBuilder::contract_call_by_hash( sender, - *cep18_token, + AddressableEntityHash::new(cep18_contract_hash.value()), METHOD_APPROVE, runtime_args! { ARG_SPENDER => spender, @@ -347,11 +387,39 @@ pub(crate) fn make_cep18_approve_request( .build(), Key::Hash(contract_package_hash) => ExecuteRequestBuilder::versioned_contract_call_by_hash( *DEFAULT_ACCOUNT_ADDR, - ContractPackageHash::new(contract_package_hash), + PackageHash::new(contract_package_hash), None, METHOD_APPROVE_AS_STORED_CONTRACT, runtime_args! { - ARG_TOKEN_CONTRACT => Key::from(*cep18_token), + ARG_TOKEN_CONTRACT => Key::addressable_entity_key(EntityKindTag::SmartContract, *cep18_contract_hash), + ARG_SPENDER => spender, + ARG_AMOUNT => amount, + }, + ) + .build(), + Key::AddressableEntity(entity_addr)=>{ + match entity_addr { + EntityAddr::System(_) => panic!("Not a use case"), + EntityAddr::Account(account_addr) => ExecuteRequestBuilder::contract_call_by_hash( + AccountHash::new(account_addr), + AddressableEntityHash::new(cep18_contract_hash.value()), + METHOD_APPROVE, + runtime_args! { + ARG_SPENDER => spender, + ARG_AMOUNT => amount, + }, + ) + .build(), + EntityAddr::SmartContract(_contract_hash) => panic!("Invalid variant") + } + }, + Key::Package(contract_package_hash) => ExecuteRequestBuilder::versioned_contract_call_by_hash( + *DEFAULT_ACCOUNT_ADDR, + PackageHash::new(contract_package_hash), + None, + METHOD_APPROVE_AS_STORED_CONTRACT, + runtime_args! { + ARG_TOKEN_CONTRACT => Key::addressable_entity_key(EntityKindTag::SmartContract, *cep18_contract_hash), ARG_SPENDER => spender, ARG_AMOUNT => amount, }, @@ -362,13 +430,16 @@ pub(crate) fn make_cep18_approve_request( } pub(crate) fn test_approve_for( - builder: &mut InMemoryWasmTestBuilder, + builder: &mut LmdbWasmTestBuilder, test_context: &TestContext, sender: Key, owner: Key, spender: Key, ) { - let TestContext { cep18_token, .. } = test_context; + let TestContext { + cep18_contract_hash, + .. + } = test_context; let initial_supply = U256::from(TOKEN_TOTAL_SUPPLY); let allowance_amount_1 = U256::from(ALLOWANCE_AMOUNT_1); let allowance_amount_2 = U256::from(ALLOWANCE_AMOUNT_2); @@ -377,9 +448,9 @@ pub(crate) fn test_approve_for( assert_eq!(spender_allowance_before, U256::zero()); let approve_request_1 = - make_cep18_approve_request(sender, cep18_token, spender, allowance_amount_1); + make_cep18_approve_request(sender, cep18_contract_hash, spender, allowance_amount_1); let approve_request_2 = - make_cep18_approve_request(sender, cep18_token, spender, allowance_amount_2); + make_cep18_approve_request(sender, cep18_contract_hash, spender, allowance_amount_2); builder.exec(approve_request_1).expect_success().commit(); @@ -387,7 +458,10 @@ pub(crate) fn test_approve_for( let account_1_allowance_after = cep18_check_allowance_of(builder, owner, spender); assert_eq!(account_1_allowance_after, allowance_amount_1); - let total_supply: U256 = builder.get_value(*cep18_token, TOTAL_SUPPLY_KEY); + let total_supply: U256 = builder.get_value( + EntityAddr::new_smart_contract(cep18_contract_hash.value()), + TOTAL_SUPPLY_KEY, + ); assert_eq!(total_supply, initial_supply); } @@ -404,6 +478,9 @@ pub(crate) fn test_approve_for( let inverted_spender_allowance = cep18_check_allowance_of(builder, owner, inverted_spender_key); assert_eq!(inverted_spender_allowance, U256::zero()); - let total_supply: U256 = builder.get_value(*cep18_token, TOTAL_SUPPLY_KEY); + let total_supply: U256 = builder.get_value( + EntityAddr::new_smart_contract(cep18_contract_hash.value()), + TOTAL_SUPPLY_KEY, + ); assert_eq!(total_supply, initial_supply); } diff --git a/tests/src/utility/message_handlers.rs b/tests/src/utility/message_handlers.rs new file mode 100644 index 00000000..2e14eed4 --- /dev/null +++ b/tests/src/utility/message_handlers.rs @@ -0,0 +1,73 @@ +use casper_engine_test_support::LmdbWasmTestBuilder; +use casper_types::{ + contract_messages::{MessageChecksum, MessageTopicSummary, TopicNameHash}, + AddressableEntity, AddressableEntityHash, Digest, EntityAddr, Key, StoredValue, +}; + +pub fn entity( + builder: &LmdbWasmTestBuilder, + contract_hash: &AddressableEntityHash, +) -> AddressableEntity { + let query_result = builder + .query(None, Key::contract_entity_key(*contract_hash), &[]) + .expect("should query"); + + if let StoredValue::AddressableEntity(entity) = query_result { + entity + } else { + panic!( + "Stored value is not an addressable entity: {:?}", + query_result + ); + } +} + +pub fn message_topic( + builder: &LmdbWasmTestBuilder, + contract_hash: &AddressableEntityHash, + topic_name_hash: TopicNameHash, +) -> MessageTopicSummary { + let query_result = builder + .query( + None, + Key::message_topic( + EntityAddr::new_smart_contract(contract_hash.value()), + topic_name_hash, + ), + &[], + ) + .expect("should query"); + + match query_result { + StoredValue::MessageTopic(summary) => summary, + _ => { + panic!( + "Stored value is not a message topic summary: {:?}", + query_result + ); + } + } +} + +pub fn message_summary( + builder: &LmdbWasmTestBuilder, + contract_hash: &AddressableEntityHash, + topic_name_hash: &TopicNameHash, + message_index: u32, + state_hash: Option, +) -> Result { + let query_result = builder.query( + state_hash, + Key::message( + EntityAddr::new_smart_contract(contract_hash.value()), + *topic_name_hash, + message_index, + ), + &[], + )?; + + match query_result { + StoredValue::Message(summary) => Ok(summary), + _ => panic!("Stored value is not a message summary: {:?}", query_result), + } +} diff --git a/tests/src/utility/mod.rs b/tests/src/utility/mod.rs index d50bf297..e4cbe99f 100644 --- a/tests/src/utility/mod.rs +++ b/tests/src/utility/mod.rs @@ -1,2 +1,4 @@ pub mod constants; pub mod installer_request_builders; +pub mod message_handlers; +pub mod support; diff --git a/tests/src/utility/support.rs b/tests/src/utility/support.rs new file mode 100644 index 00000000..21c0f552 --- /dev/null +++ b/tests/src/utility/support.rs @@ -0,0 +1,45 @@ +use casper_engine_test_support::LmdbWasmTestBuilder; +use casper_types::{bytesrepr::FromBytes, CLTyped, EntityAddr, Key}; + +pub(crate) fn get_dictionary_value_from_key( + builder: &LmdbWasmTestBuilder, + contract_key: &Key, + dictionary_name: &str, + dictionary_key: &str, +) -> T { + let named_key = match contract_key.into_entity_hash() { + Some(hash) => { + let entity_with_named_keys = builder + .get_entity_with_named_keys_by_entity_hash(hash) + .expect("should be named key from entity hash"); + let named_keys = entity_with_named_keys.named_keys(); + named_keys + .get(dictionary_name) + .expect("must have key") + .to_owned() + } + None => match contract_key.into_hash_addr() { + Some(contract_key) => { + let named_keys = builder.get_named_keys(EntityAddr::SmartContract(contract_key)); + named_keys + .get(dictionary_name) + .expect("must have key") + .to_owned() + } + None => { + panic!("unsupported dictionary location") + } + }, + }; + + let seed_uref = named_key.as_uref().expect("must convert to seed uref"); + + builder + .query_dictionary_item(None, *seed_uref, dictionary_key) + .expect("should have dictionary value") + .as_cl_value() + .expect("T should be CLValue") + .to_owned() + .into_t() + .unwrap() +}