diff --git a/contract/Cargo.toml b/contract/Cargo.toml index ca763fc6..3ce3b460 100644 --- a/contract/Cargo.toml +++ b/contract/Cargo.toml @@ -4,15 +4,13 @@ version = "1.5.1" edition = "2018" [dependencies] -casper-contract = { version = "3.0.0", default-features = false, features = [ - "test-support", -], optional = true } -casper-types = "3.0.0" +casper-contract = { version = "3.0.0", default-features = false, optional = true } +casper-types = { version = "3.0.0", default-features = false } serde = { version = "1.0.80", default-features = false } serde_json = { version = "1.0.59", default-features = false } serde-json-wasm = { version = "0.5.1", default-features = false } base16 = { version = "0.2.1", default-features = false } -casper-event-standard = { version = "0.4.0", default-features = false } +casper-event-standard = { version = "0.4.1", default-features = false } hex = { version = "0.4.3", default-features = false } [[bin]] diff --git a/contract/src/main.rs b/contract/src/main.rs index 8d9ab635..6a667e5d 100644 --- a/contract/src/main.rs +++ b/contract/src/main.rs @@ -2001,27 +2001,50 @@ pub extern "C" fn migrate() { runtime::put_key(RLO_MFLAG, storage::new_uref(false).into()); - let events_mode: EventsMode = utils::get_optional_named_arg_with_user_errors::( - ARG_EVENTS_MODE, - NFTCoreError::InvalidEventsMode, - ) - .unwrap_or(EventsMode::NoEvents as u8) - .try_into() - .unwrap_or_revert(); - - match events_mode { - EventsMode::NoEvents => {} - EventsMode::CES => { - // Initialize events structures. - utils::init_events(); - // Emit Migration event. - casper_event_standard::emit(Migration::new()); + let optional_events_mode: Option = runtime::get_named_arg::>(ARG_EVENTS_MODE); + let current_events_mode: EventsMode = runtime::get_key(EVENTS_MODE) + .and_then(|_| { + utils::get_stored_value_with_user_errors::( + EVENTS_MODE, + NFTCoreError::MissingEventsMode, + NFTCoreError::InvalidEventsMode, + ) + .try_into() + .ok() + }) + .unwrap_or(EventsMode::NoEvents); + + if let Some(optional_events_mode) = optional_events_mode { + let requested_events_mode: EventsMode = optional_events_mode + .try_into() + .unwrap_or_revert_with(NFTCoreError::InvalidEventsMode); + match (current_events_mode, requested_events_mode) { + (EventsMode::CES, EventsMode::CES) => casper_event_standard::emit(Migration::new()), + (_, EventsMode::CES) => { + // Initialize events structures. + utils::init_events(); + casper_event_standard::emit(Migration::new()); + } + (_, EventsMode::CEP47) => record_cep47_event_dictionary(CEP47Event::Migrate), + (_, _) => {} + } + runtime::put_key(EVENTS_MODE, storage::new_uref(optional_events_mode).into()); + } else { + match current_events_mode { + EventsMode::CEP47 => record_cep47_event_dictionary(CEP47Event::Migrate), + EventsMode::CES => casper_event_standard::emit(Migration::new()), + _ => { + // Store "no events" mode in case it was never stored like version < 1.2 + if !runtime::has_key(EVENTS_MODE) { + runtime::put_key( + EVENTS_MODE, + storage::new_uref(EventsMode::NoEvents as u8).into(), + ) + } + } } - EventsMode::CEP47 => record_cep47_event_dictionary(CEP47Event::Migrate), } - runtime::put_key(EVENTS_MODE, storage::new_uref(events_mode as u8).into()); - let acl_package_mode: bool = utils::get_optional_named_arg_with_user_errors::( ARG_ACL_PACKAGE_MODE, NFTCoreError::InvalidACLPackageMode, @@ -2823,8 +2846,7 @@ fn migrate_contract(access_key_name: String, package_key_name: String) { let events_mode = utils::get_optional_named_arg_with_user_errors::( ARG_EVENTS_MODE, NFTCoreError::InvalidEventsMode, - ) - .unwrap_or(0u8); + ); let acl_package_mode: bool = utils::get_optional_named_arg_with_user_errors( ARG_ACL_PACKAGE_MODE, diff --git a/contract/src/modalities.rs b/contract/src/modalities.rs index 88e89846..a24019f9 100644 --- a/contract/src/modalities.rs +++ b/contract/src/modalities.rs @@ -423,7 +423,7 @@ impl TryFrom for NamedKeyConventionMode { } #[repr(u8)] -#[derive(PartialEq, Eq)] +#[derive(PartialEq, Eq, Clone, Copy)] #[allow(clippy::upper_case_acronyms)] pub enum EventsMode { NoEvents = 0, diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 4836385f..4a09e7af 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -3,12 +3,10 @@ name = "tests" version = "1.5.1" edition = "2018" [dependencies] -casper-engine-test-support = { version = "5.0.0", default-features = false, features = [ - "test-support", -] } +casper-engine-test-support = { version = "5.0.0", default-features = false } contract = { path = "../contract", default-features = false } casper-execution-engine = { version = "5.0.0", default-features = false } -casper-types = "3.0.0" +casper-types = { version = "3.0.0", default-features = false } serde = { version = "1.0.80", default-features = false } serde_json = { version = "1.0.59", default-features = false } once_cell = "1" diff --git a/tests/src/upgrade.rs b/tests/src/upgrade.rs index c6b064a9..6b78a5e4 100644 --- a/tests/src/upgrade.rs +++ b/tests/src/upgrade.rs @@ -720,7 +720,6 @@ fn should_safely_upgrade_from_old_version_to_new_version_with_reporting_mode( .with_metadata_mutability(MetadataMutability::Mutable) .with_identifier_mode(NFTIdentifierMode::Ordinal) .with_nft_metadata_kind(NFTMetadataKind::Raw) - .with_events_mode(EventsMode::CES) .build(); builder.exec(install_request).expect_success().commit(); @@ -766,7 +765,8 @@ fn should_safely_upgrade_from_old_version_to_new_version_with_reporting_mode( ARG_NAMED_KEY_CONVENTION => NamedKeyConventionMode::V1_0Custom as u8, ARG_ACCESS_KEY_NAME_1_0_0 => format!("{PREFIX_ACCESS_KEY_NAME}_{NFT_TEST_COLLECTION}"), ARG_HASH_KEY_NAME_1_0_0 => format!("{PREFIX_HASH_KEY_NAME}_{NFT_TEST_COLLECTION}"), - ARG_TOTAL_TOKEN_SUPPLY => 10u64 + ARG_TOTAL_TOKEN_SUPPLY => 10u64, + ARG_EVENTS_MODE => EventsMode::CES as u8 // Optin for CES on upgrade }, ) .build(); @@ -796,21 +796,13 @@ fn should_safely_upgrade_from_old_version_to_new_version_with_reporting_mode( expected_total_token_supply_post_upgrade ); - // Expect No Migration event after 3 Mint events. - let seed_uref = *builder - .query(None, nft_contract_key, &[]) - .expect("must have nft contract") - .as_contract() - .expect("must convert contract") - .named_keys() - .get(casper_event_standard::EVENTS_DICT) - .expect("must have key") - .as_uref() - .expect("must convert to seed uref"); - - builder - .query_dictionary_item(None, seed_uref, "3") - .expect_err("should not have dictionary value for a third migration event"); + // Expect Migration event after 3 mint events + // Below version 1.5.1 migration event is not recorded as per bug https://github.com/casper-ecosystem/cep-78-enhanced-nft/issues/261 + let expected_event = Migration::new(); + let expected_event_index = 3; + let actual_event: Migration = + support::get_event(&builder, &nft_contract_key, expected_event_index).unwrap(); + assert_eq!(actual_event, expected_event, "Expected Migration event."); } #[test] @@ -1002,63 +994,6 @@ fn should_safely_upgrade_with_operator_burn_mode() { assert_eq!(actual_event, expected_event, "Expected Migration event."); } -#[test] -fn should_safely_upgrade_from_1_2_0_to_1_3_0() { - //* starting total_token_supply 100u64 - let expected_total_token_supply_post_upgrade = 10; - should_safely_upgrade_from_old_version_to_new_version_with_reporting_mode( - CONTRACT_1_2_0_WASM, - CONTRACT_1_3_0_WASM, - OwnerReverseLookupMode::NoLookUp, - expected_total_token_supply_post_upgrade, - ); - let expected_total_token_supply_post_upgrade = 100; - should_safely_upgrade_from_old_version_to_new_version_with_reporting_mode( - CONTRACT_1_2_0_WASM, - CONTRACT_1_3_0_WASM, - OwnerReverseLookupMode::Complete, - expected_total_token_supply_post_upgrade, - ); -} - -#[test] -fn should_safely_upgrade_from_1_3_0_to_1_4_0() { - //* starting total_token_supply 100u64 - let expected_total_token_supply_post_upgrade = 10; - should_safely_upgrade_from_old_version_to_new_version_with_reporting_mode( - CONTRACT_1_3_0_WASM, - CONTRACT_1_4_0_WASM, - OwnerReverseLookupMode::NoLookUp, - expected_total_token_supply_post_upgrade, - ); - let expected_total_token_supply_post_upgrade = 100; - should_safely_upgrade_from_old_version_to_new_version_with_reporting_mode( - CONTRACT_1_3_0_WASM, - CONTRACT_1_4_0_WASM, - OwnerReverseLookupMode::Complete, - expected_total_token_supply_post_upgrade, - ); -} - -#[test] -fn should_safely_upgrade_from_1_4_0_to_1_5_0() { - //* starting total_token_supply 100u64 - let expected_total_token_supply_post_upgrade = 10; - should_safely_upgrade_from_old_version_to_new_version_with_reporting_mode( - CONTRACT_1_4_0_WASM, - CONTRACT_1_5_0_WASM, - OwnerReverseLookupMode::NoLookUp, - expected_total_token_supply_post_upgrade, - ); - let expected_total_token_supply_post_upgrade = 100; - should_safely_upgrade_from_old_version_to_new_version_with_reporting_mode( - CONTRACT_1_4_0_WASM, - CONTRACT_1_5_0_WASM, - OwnerReverseLookupMode::Complete, - expected_total_token_supply_post_upgrade, - ); -} - #[test] fn should_safely_upgrade_from_1_4_0_to_current_version() { //* starting total_token_supply 100u64 @@ -1175,6 +1110,7 @@ fn should_safely_upgrade_from_1_0_0_to_1_2_0_to_current_version() { ARG_ACCESS_KEY_NAME_1_0_0 => format!("{PREFIX_ACCESS_KEY_NAME}_{NFT_TEST_COLLECTION}"), ARG_HASH_KEY_NAME_1_0_0 => format!("{PREFIX_HASH_KEY_NAME}_{NFT_TEST_COLLECTION}"), ARG_TOTAL_TOKEN_SUPPLY => 10u64, + ARG_EVENTS_MODE => EventsMode::CES as u8 }, ) .build(); @@ -1286,6 +1222,7 @@ fn should_safely_upgrade_from_1_0_0_to_1_3_0_to_current_version() { ARG_ACCESS_KEY_NAME_1_0_0 => format!("{PREFIX_ACCESS_KEY_NAME}_{NFT_TEST_COLLECTION}"), ARG_HASH_KEY_NAME_1_0_0 => format!("{PREFIX_HASH_KEY_NAME}_{NFT_TEST_COLLECTION}"), ARG_TOTAL_TOKEN_SUPPLY => 10u64, + ARG_EVENTS_MODE => EventsMode::CES as u8 }, ) .build(); @@ -1397,6 +1334,7 @@ fn should_safely_upgrade_from_1_0_0_to_1_4_0_to_current_version() { ARG_ACCESS_KEY_NAME_1_0_0 => format!("{PREFIX_ACCESS_KEY_NAME}_{NFT_TEST_COLLECTION}"), ARG_HASH_KEY_NAME_1_0_0 => format!("{PREFIX_HASH_KEY_NAME}_{NFT_TEST_COLLECTION}"), ARG_TOTAL_TOKEN_SUPPLY => 10u64, + ARG_EVENTS_MODE => EventsMode::CES as u8 }, ) .build(); @@ -1508,6 +1446,7 @@ fn should_safely_upgrade_from_1_0_0_to_1_5_0_to_current_version() { ARG_ACCESS_KEY_NAME_1_0_0 => format!("{PREFIX_ACCESS_KEY_NAME}_{NFT_TEST_COLLECTION}"), ARG_HASH_KEY_NAME_1_0_0 => format!("{PREFIX_HASH_KEY_NAME}_{NFT_TEST_COLLECTION}"), ARG_TOTAL_TOKEN_SUPPLY => 10u64, + ARG_EVENTS_MODE => EventsMode::CES as u8 }, ) .build(); @@ -1541,6 +1480,190 @@ fn should_safely_upgrade_from_1_0_0_to_1_5_0_to_current_version() { assert_eq!(actual_event, expected_event, "Expected Migration event."); } +#[test] +fn should_safely_upgrade_from_1_5_0_to_current_version_without_supplying_events_mode_to_keep_current_mode( +) { + let mut builder = InMemoryWasmTestBuilder::default(); + builder + .run_genesis(&PRODUCTION_RUN_GENESIS_REQUEST) + .commit(); + + let install_request = InstallerRequestBuilder::new(*DEFAULT_ACCOUNT_ADDR, CONTRACT_1_5_0_WASM) + .with_collection_name(NFT_TEST_COLLECTION.to_string()) + .with_collection_symbol(NFT_TEST_SYMBOL.to_string()) + .with_total_token_supply(100u64) + .with_ownership_mode(OwnershipMode::Transferable) + .with_metadata_mutability(MetadataMutability::Mutable) + .with_identifier_mode(NFTIdentifierMode::Ordinal) + .with_nft_metadata_kind(NFTMetadataKind::Raw) + .build(); + + builder.exec(install_request).expect_success().commit(); + + let nft_contract_hash_1_5_0: ContractHash = support::get_nft_contract_hash(&builder); + let nft_contract_key_1_5_0: Key = nft_contract_hash_1_5_0.into(); + + let total_token_supply_post_install = support::get_stored_value_from_global_state::( + &builder, + nft_contract_key_1_5_0, + vec![ARG_TOTAL_TOKEN_SUPPLY.to_string()], + ) + .expect("must get u64 value"); + + assert_eq!(total_token_supply_post_install, 100u64); + + let number_of_tokens_pre_migration = 3usize; + + // Build of prestate before migration. + for _i in 0..number_of_tokens_pre_migration { + let mint_request = ExecuteRequestBuilder::standard( + *DEFAULT_ACCOUNT_ADDR, + MINT_SESSION_WASM, + runtime_args! { + ARG_NFT_CONTRACT_HASH => nft_contract_key_1_5_0, + ARG_TOKEN_OWNER => Key::Account(*DEFAULT_ACCOUNT_ADDR), + ARG_TOKEN_META_DATA => "", + ARG_COLLECTION_NAME => NFT_TEST_COLLECTION.to_string() + }, + ) + .build(); + + builder.exec(mint_request).expect_success().commit(); + } + + let upgrade_request = ExecuteRequestBuilder::standard( + *DEFAULT_ACCOUNT_ADDR, + NFT_CONTRACT_WASM, + runtime_args! { + ARG_NFT_CONTRACT_HASH => nft_contract_key_1_5_0, + ARG_COLLECTION_NAME => NFT_TEST_COLLECTION.to_string(), + ARG_NAMED_KEY_CONVENTION => NamedKeyConventionMode::V1_0Custom as u8, + ARG_ACCESS_KEY_NAME_1_0_0 => format!("{PREFIX_ACCESS_KEY_NAME}_{NFT_TEST_COLLECTION}"), + ARG_HASH_KEY_NAME_1_0_0 => format!("{PREFIX_HASH_KEY_NAME}_{NFT_TEST_COLLECTION}"), + ARG_TOTAL_TOKEN_SUPPLY => 10u64, + }, + ) + .build(); + + builder.exec(upgrade_request).expect_success().commit(); + + let nft_contract_hash = support::get_nft_contract_hash(&builder); + let nft_contract_key: Key = nft_contract_hash.into(); + + let number_of_tokens_at_upgrade = support::get_stored_value_from_global_state::( + &builder, + nft_contract_key, + vec![NUMBER_OF_MINTED_TOKENS.to_string()], + ) + .expect("must get u64 value"); + + assert_eq!(number_of_tokens_at_upgrade, 3); + + // Expect Migration event. + let expected_event = Migration::new(); + let expected_event_index = 3; + let actual_event: Migration = + support::get_event(&builder, &nft_contract_key, expected_event_index).unwrap(); + assert_eq!(actual_event, expected_event, "Expected Migration event."); +} + +#[test] +fn should_safely_upgrade_from_1_5_0_and_disable_events_mode() { + let mut builder = InMemoryWasmTestBuilder::default(); + builder + .run_genesis(&PRODUCTION_RUN_GENESIS_REQUEST) + .commit(); + + let install_request = InstallerRequestBuilder::new(*DEFAULT_ACCOUNT_ADDR, CONTRACT_1_5_0_WASM) + .with_collection_name(NFT_TEST_COLLECTION.to_string()) + .with_collection_symbol(NFT_TEST_SYMBOL.to_string()) + .with_total_token_supply(100u64) + .with_ownership_mode(OwnershipMode::Transferable) + .with_metadata_mutability(MetadataMutability::Mutable) + .with_identifier_mode(NFTIdentifierMode::Ordinal) + .with_nft_metadata_kind(NFTMetadataKind::Raw) + .with_events_mode(EventsMode::CES) + .build(); + + builder.exec(install_request).expect_success().commit(); + + let nft_contract_hash_1_5_0: ContractHash = support::get_nft_contract_hash(&builder); + let nft_contract_key_1_5_0: Key = nft_contract_hash_1_5_0.into(); + + let total_token_supply_post_install = support::get_stored_value_from_global_state::( + &builder, + nft_contract_key_1_5_0, + vec![ARG_TOTAL_TOKEN_SUPPLY.to_string()], + ) + .expect("must get u64 value"); + + assert_eq!(total_token_supply_post_install, 100u64); + + let number_of_tokens_pre_migration = 3usize; + + // Build of prestate before migration. + for _i in 0..number_of_tokens_pre_migration { + let mint_request = ExecuteRequestBuilder::standard( + *DEFAULT_ACCOUNT_ADDR, + MINT_SESSION_WASM, + runtime_args! { + ARG_NFT_CONTRACT_HASH => nft_contract_key_1_5_0, + ARG_TOKEN_OWNER => Key::Account(*DEFAULT_ACCOUNT_ADDR), + ARG_TOKEN_META_DATA => "", + ARG_COLLECTION_NAME => NFT_TEST_COLLECTION.to_string() + }, + ) + .build(); + + builder.exec(mint_request).expect_success().commit(); + } + + let upgrade_request = ExecuteRequestBuilder::standard( + *DEFAULT_ACCOUNT_ADDR, + NFT_CONTRACT_WASM, + runtime_args! { + ARG_NFT_CONTRACT_HASH => nft_contract_key_1_5_0, + ARG_COLLECTION_NAME => NFT_TEST_COLLECTION.to_string(), + ARG_NAMED_KEY_CONVENTION => NamedKeyConventionMode::V1_0Custom as u8, + ARG_ACCESS_KEY_NAME_1_0_0 => format!("{PREFIX_ACCESS_KEY_NAME}_{NFT_TEST_COLLECTION}"), + ARG_HASH_KEY_NAME_1_0_0 => format!("{PREFIX_HASH_KEY_NAME}_{NFT_TEST_COLLECTION}"), + ARG_TOTAL_TOKEN_SUPPLY => 10u64, + ARG_EVENTS_MODE => EventsMode::NoEvents as u8, + }, + ) + .build(); + + builder.exec(upgrade_request).expect_success().commit(); + + let nft_contract_hash = support::get_nft_contract_hash(&builder); + let nft_contract_key: Key = nft_contract_hash.into(); + + let number_of_tokens_at_upgrade = support::get_stored_value_from_global_state::( + &builder, + nft_contract_key, + vec![NUMBER_OF_MINTED_TOKENS.to_string()], + ) + .expect("must get u64 value"); + + assert_eq!(number_of_tokens_at_upgrade, 3); + + // Expect No Migration event after 3 Mint events. + let seed_uref = *builder + .query(None, nft_contract_key, &[]) + .expect("must have nft contract") + .as_contract() + .expect("must convert contract") + .named_keys() + .get(casper_event_standard::EVENTS_DICT) + .expect("must have key") + .as_uref() + .expect("must convert to seed uref"); + + builder + .query_dictionary_item(None, seed_uref, "3") + .expect_err("should not have dictionary value for a third migration event"); +} + #[test] fn should_safely_upgrade_from_1_0_0_to_current_version() { let mut builder = InMemoryWasmTestBuilder::default();