From 94a9e1d48e309786e2623e2d5ca42510c0765d32 Mon Sep 17 00:00:00 2001 From: Doris Benda Date: Tue, 7 Nov 2023 11:53:37 +0200 Subject: [PATCH 1/8] Add permit transfer test case --- examples/cis3-nft-sponsored-txs/Cargo.toml | 2 + examples/cis3-nft-sponsored-txs/src/lib.rs | 10 +- .../cis3-nft-sponsored-txs/tests/tests.rs | 360 +++++++++++++++++- 3 files changed, 354 insertions(+), 18 deletions(-) diff --git a/examples/cis3-nft-sponsored-txs/Cargo.toml b/examples/cis3-nft-sponsored-txs/Cargo.toml index 95d7794d..5517d326 100644 --- a/examples/cis3-nft-sponsored-txs/Cargo.toml +++ b/examples/cis3-nft-sponsored-txs/Cargo.toml @@ -16,6 +16,8 @@ concordium-cis2 = {path = "../../concordium-cis2", default-features = false} [dev-dependencies] concordium-smart-contract-testing = { path = "../../contract-testing" } +ed25519-dalek = "1.0" +rand = "0.7.0" [lib] crate-type=["cdylib", "rlib"] diff --git a/examples/cis3-nft-sponsored-txs/src/lib.rs b/examples/cis3-nft-sponsored-txs/src/lib.rs index e3b9eb1b..521a626e 100644 --- a/examples/cis3-nft-sponsored-txs/src/lib.rs +++ b/examples/cis3-nft-sponsored-txs/src/lib.rs @@ -79,13 +79,15 @@ const SUPPORTS_PERMIT_ENTRYPOINTS: [EntrypointName; 2] = pub const NONCE_EVENT_TAG: u8 = u8::MAX - 5; /// Tagged events to be serialized for the event log. -#[derive(Debug, Serial)] +#[derive(Debug, Serial, Deserial, PartialEq, Eq)] #[concordium(repr(u8))] pub enum Event { /// The event tracks the nonce used by the signer of the `PermitMessage` /// whenever the `permit` function is invoked. #[concordium(tag = 250)] Nonce(NonceEvent), + #[concordium(forward = cis2_events)] + Cis2Event(Cis2Event), } /// The NonceEvent is logged when the `permit` function is invoked. The event @@ -744,7 +746,7 @@ fn contract_view_message_hash( // or sign a message (in that case the prepend is `account` address and 8 zero // bytes). Hence, the 8 zero bytes ensure that the user does not accidentally // sign a transaction. The account nonce is of type u64 (8 bytes). - let mut msg_prepend = vec![0; 32 + 8]; + let mut msg_prepend = [0; 32 + 8]; // Prepend the `account` address of the signer. msg_prepend[0..32].copy_from_slice(param.signer.as_ref()); // Prepend 8 zero bytes. @@ -962,10 +964,10 @@ fn contract_operator_of( /// Parameter type for the CIS-2 function `balanceOf` specialized to the subset /// of TokenIDs used by this contract. -type ContractBalanceOfQueryParams = BalanceOfQueryParams; +pub type ContractBalanceOfQueryParams = BalanceOfQueryParams; /// Response type for the CIS-2 function `balanceOf` specialized to the subset /// of TokenAmounts used by this contract. -type ContractBalanceOfQueryResponse = BalanceOfQueryResponse; +pub type ContractBalanceOfQueryResponse = BalanceOfQueryResponse; /// Get the balance of given token IDs and addresses. /// diff --git a/examples/cis3-nft-sponsored-txs/tests/tests.rs b/examples/cis3-nft-sponsored-txs/tests/tests.rs index 6ade2af0..4f89c095 100644 --- a/examples/cis3-nft-sponsored-txs/tests/tests.rs +++ b/examples/cis3-nft-sponsored-txs/tests/tests.rs @@ -1,13 +1,20 @@ //! Tests for the `cis3_nft_sponsored_txs` contract. -use cis3_nft_sponsored_txs::*; -use concordium_cis2::*; -use concordium_smart_contract_testing::*; +use cis3_nft_sponsored_txs::{ + ContractTokenAmount, ContractTokenId, MintParams, NonceEvent, PermitMessage, PermitParam, *, +}; +use concordium_cis2::{TokenIdU32, *}; +use concordium_smart_contract_testing::{AccountAccessStructure, AccountKeys, *}; +use concordium_std::{ + AccountSignatures, CredentialSignatures, HashSha2256, SignatureEd25519, Timestamp, +}; +use std::collections::BTreeMap; /// The tests accounts. const ALICE: AccountAddress = AccountAddress([0; 32]); const ALICE_ADDR: Address = Address::Account(ALICE); const BOB: AccountAddress = AccountAddress([1; 32]); const BOB_ADDR: Address = Address::Account(BOB); +const ACC_ADDR_OWNER: AccountAddress = AccountAddress([2u8; 32]); /// Token IDs. const TOKEN_0: ContractTokenId = TokenIdU32(1); @@ -19,11 +26,240 @@ const ACC_INITIAL_BALANCE: Amount = Amount::from_ccd(10000); /// A signer for all the transactions. const SIGNER: Signer = Signer::with_one_key(); +// Private key: 8ECA45107A878FB879B84401084B55AD4919FC0F7D14E8915D8A5989B1AE1C01 +const PUBLIC_KEY: [u8; 32] = [ + 120, 154, 141, 6, 248, 239, 77, 224, 80, 62, 139, 136, 211, 204, 105, 208, 26, 11, 2, 208, 195, + 253, 29, 192, 126, 199, 208, 39, 69, 4, 246, 32, +]; + +const SIGNATURE_TRANSFER: SignatureEd25519 = SignatureEd25519([ + 68, 134, 96, 171, 184, 199, 1, 93, 76, 87, 144, 68, 55, 180, 93, 56, 107, 95, 127, 112, 24, 55, + 162, 131, 165, 91, 133, 104, 2, 5, 78, 224, 214, 21, 66, 0, 44, 108, 52, 4, 108, 10, 123, 75, + 21, 68, 42, 79, 106, 106, 87, 125, 122, 77, 154, 114, 208, 145, 171, 47, 108, 96, 221, 13, +]); + +const DUMMY_SIGNATURE: SignatureEd25519 = SignatureEd25519([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +]); + +/// Test permit transfer function. The signature is generated in the test case. +/// TOKEN_1 is transferred from Alice to Bob. +#[test] +fn test_inside_signature_permit_transfer() { + let (mut chain, contract_address, _update, keypairs) = + initialize_contract_with_alice_tokens(true); + + // Check balances in state. + let balance_of_alice_and_bob = get_balances(&chain, contract_address); + + assert_eq!(balance_of_alice_and_bob.0, [TokenAmountU8(1), TokenAmountU8(0)]); + + let transfer = concordium_cis2::Transfer { + from: ALICE_ADDR, + to: Receiver::from_account(BOB), + token_id: TOKEN_1, + amount: ContractTokenAmount::from(1), + data: AdditionalData::empty(), + }; + let payload = TransferParams::from(vec![transfer]); + + let mut inner_signature_map = BTreeMap::new(); + inner_signature_map.insert(0u8, concordium_std::Signature::Ed25519(DUMMY_SIGNATURE)); + + let mut signature_map = BTreeMap::new(); + signature_map.insert(0u8, CredentialSignatures { + sigs: inner_signature_map, + }); + + let mut permit_transfer_param = PermitParam { + signature: AccountSignatures { + sigs: signature_map, + }, + signer: ALICE, + message: PermitMessage { + timestamp: Timestamp::from_timestamp_millis(10000000000), + contract_address: ContractAddress { + index: 0, + subindex: 0, + }, + entry_point: OwnedEntrypointName::new_unchecked("transfer".into()), + nonce: 0, + payload: to_bytes(&payload), + }, + }; + + let invoke = chain + .contract_invoke(BOB, BOB_ADDR, Energy::from(10000), UpdateContractPayload { + amount: Amount::zero(), + address: contract_address, + receive_name: OwnedReceiveName::new_unchecked("cis3_nft.viewMessageHash".to_string()), + message: OwnedParameter::from_serial(&permit_transfer_param) + .expect("Should be a valid inut parameter"), + }) + .expect("Should be able to query balanceOf"); + + let message_hash: HashSha2256 = + from_bytes(&invoke.return_value).expect("Should return a valid result"); + + let signature = keypairs + .expect("Should have a generated private key to sign") + .sign_data(&to_bytes(&message_hash)); + + let t = signature[&CredentialIndex { + index: 0, + }][&KeyIndex(0u8)] + .sig + .clone(); + let q: [u8; 64] = t.try_into().unwrap(); + + let mut inner_signature_map = BTreeMap::new(); + inner_signature_map.insert(0u8, concordium_std::Signature::Ed25519(SignatureEd25519(q))); + + let mut signature_map = BTreeMap::new(); + signature_map.insert(0u8, CredentialSignatures { + sigs: inner_signature_map, + }); + + permit_transfer_param.signature = AccountSignatures { + sigs: signature_map, + }; + + // Transfer token with the permit function. + let update = chain + .contract_update( + Signer::with_one_key(), + BOB, + BOB_ADDR, + Energy::from(10000), + UpdateContractPayload { + amount: Amount::zero(), + address: contract_address, + receive_name: OwnedReceiveName::new_unchecked("cis3_nft.permit".to_string()), + message: OwnedParameter::from_serial(&permit_transfer_param) + .expect("Should be a valid inut parameter"), + }, + ) + .expect("Should be able to transfer token with permit"); + + // Check that the correct events occurred. + let events = update + .events() + .flat_map(|(_addr, events)| events.iter().map(|e| e.parse().expect("Deserialize event"))) + .collect::>(); + + assert_eq!(events, [ + Event::Cis2Event(Cis2Event::Transfer(TransferEvent { + token_id: TOKEN_1, + amount: ContractTokenAmount::from(1), + from: ALICE_ADDR, + to: BOB_ADDR, + })), + Event::Nonce(NonceEvent { + account: ALICE, + nonce: 0, + }) + ]); + + // Check balances in state. + let balance_of_alice_and_bob = get_balances(&chain, contract_address); + + assert_eq!(balance_of_alice_and_bob.0, [TokenAmountU8(0), TokenAmountU8(1)]); +} + +/// Test permit transfer function. The signature is generated outside this test +/// case (e.g. with https://cyphr.me/ed25519_tool/ed.html). TOKEN_1 is transferred from Alice to Bob. +#[test] +fn test_outside_signature_permit_transfer() { + let (mut chain, contract_address, _update, _keypairs) = + initialize_contract_with_alice_tokens(false); + + // Check balances in state. + let balance_of_alice_and_bob = get_balances(&chain, contract_address); + + assert_eq!(balance_of_alice_and_bob.0, [TokenAmountU8(1), TokenAmountU8(0)]); + + let transfer = concordium_cis2::Transfer { + from: ALICE_ADDR, + to: Receiver::from_account(BOB), + token_id: TOKEN_1, + amount: ContractTokenAmount::from(1), + data: AdditionalData::empty(), + }; + let payload = TransferParams::from(vec![transfer]); + + let mut inner_signature_map = BTreeMap::new(); + inner_signature_map.insert(0u8, concordium_std::Signature::Ed25519(SIGNATURE_TRANSFER)); + + let mut signature_map = BTreeMap::new(); + signature_map.insert(0u8, CredentialSignatures { + sigs: inner_signature_map, + }); + + let permit_transfer_param = PermitParam { + signature: AccountSignatures { + sigs: signature_map, + }, + signer: ALICE, + message: PermitMessage { + timestamp: Timestamp::from_timestamp_millis(10000000000), + contract_address: ContractAddress { + index: 0, + subindex: 0, + }, + entry_point: OwnedEntrypointName::new_unchecked("transfer".into()), + nonce: 0, + payload: to_bytes(&payload), + }, + }; + + // Transfer token with the permit function. + let update = chain + .contract_update( + Signer::with_one_key(), + BOB, + BOB_ADDR, + Energy::from(10000), + UpdateContractPayload { + amount: Amount::zero(), + address: contract_address, + receive_name: OwnedReceiveName::new_unchecked("cis3_nft.permit".to_string()), + message: OwnedParameter::from_serial(&permit_transfer_param) + .expect("Should be a valid inut parameter"), + }, + ) + .expect("Should be able to transfer token with permit"); + + // Check that the correct events occurred. + let events = update + .events() + .flat_map(|(_addr, events)| events.iter().map(|e| e.parse().expect("Deserialize event"))) + .collect::>(); + + assert_eq!(events, [ + Event::Cis2Event(Cis2Event::Transfer(TransferEvent { + token_id: TOKEN_1, + amount: ContractTokenAmount::from(1), + from: ALICE_ADDR, + to: BOB_ADDR, + })), + Event::Nonce(NonceEvent { + account: ALICE, + nonce: 0, + }) + ]); + + // Check balances in state. + let balance_of_alice_and_bob = get_balances(&chain, contract_address); + + assert_eq!(balance_of_alice_and_bob.0, [TokenAmountU8(0), TokenAmountU8(1)]); +} + /// Test minting succeeds and the tokens are owned by the given address and /// the appropriate events are logged. #[test] fn test_minting() { - let (chain, contract_address, update) = initialize_contract_with_alice_tokens(); + let (chain, contract_address, update, _keypairs) = initialize_contract_with_alice_tokens(false); // Invoke the view entrypoint and check that the tokens are owned by Alice. let invoke = chain @@ -71,7 +307,8 @@ fn test_minting() { /// Test regular transfer where sender is the owner. #[test] fn test_account_transfer() { - let (mut chain, contract_address, _update) = initialize_contract_with_alice_tokens(); + let (mut chain, contract_address, _update, _keypairs) = + initialize_contract_with_alice_tokens(false); // Transfer `TOKEN_0` from Alice to Bob. let transfer_params = TransferParams::from(vec![concordium_cis2::Transfer { @@ -130,7 +367,8 @@ fn test_account_transfer() { /// Then add Bob as an operator for Alice. #[test] fn test_add_operator() { - let (mut chain, contract_address, _update) = initialize_contract_with_alice_tokens(); + let (mut chain, contract_address, _update, _keypairs) = + initialize_contract_with_alice_tokens(false); // Add Bob as an operator for Alice. let params = UpdateOperatorParams(vec![UpdateOperator { @@ -186,7 +424,8 @@ fn test_add_operator() { /// himself. #[test] fn test_unauthorized_sender() { - let (mut chain, contract_address, _update) = initialize_contract_with_alice_tokens(); + let (mut chain, contract_address, _update, _keypairs) = + initialize_contract_with_alice_tokens(false); // Construct a transfer of `TOKEN_0` from Alice to Bob, which will be submitted // by Bob. @@ -216,7 +455,8 @@ fn test_unauthorized_sender() { /// Test that an operator can make a transfer. #[test] fn test_operator_can_transfer() { - let (mut chain, contract_address, _update) = initialize_contract_with_alice_tokens(); + let (mut chain, contract_address, _update, _keypairs) = + initialize_contract_with_alice_tokens(false); // Add Bob as an operator for Alice. let params = UpdateOperatorParams(vec![UpdateOperator { @@ -277,8 +517,10 @@ fn test_operator_can_transfer() { /// /// Only one token can be minted per update, so two updates are made. /// This function returns the second mint update. -fn initialize_contract_with_alice_tokens() -> (Chain, ContractAddress, ContractInvokeSuccess) { - let (mut chain, contract_address) = initialize_chain_and_contract(); +fn initialize_contract_with_alice_tokens( + generate_keys: bool, +) -> (Chain, ContractAddress, ContractInvokeSuccess, Option) { + let (mut chain, contract_address, keypairs) = initialize_chain_and_contract(generate_keys); let mint_params = MintParams { owner: ALICE_ADDR, @@ -304,7 +546,39 @@ fn initialize_contract_with_alice_tokens() -> (Chain, ContractAddress, ContractI }) .expect("Mint tokens"); - (chain, contract_address, update) + (chain, contract_address, update, keypairs) +} + +/// Get the `TOKEN_1` balances for Alice and Bob. +fn get_balances( + chain: &Chain, + contract_address: ContractAddress, +) -> ContractBalanceOfQueryResponse { + let balance_of_params = ContractBalanceOfQueryParams { + queries: vec![ + BalanceOfQuery { + token_id: TOKEN_1, + address: ALICE_ADDR, + }, + BalanceOfQuery { + token_id: TOKEN_1, + address: BOB_ADDR, + }, + ], + }; + + let invoke = chain + .contract_invoke(ALICE, ALICE_ADDR, Energy::from(10000), UpdateContractPayload { + amount: Amount::zero(), + receive_name: OwnedReceiveName::new_unchecked("cis3_nft.balanceOf".to_string()), + address: contract_address, + message: OwnedParameter::from_serial(&balance_of_params) + .expect("BalanceOf params"), + }) + .expect("Invoke balanceOf"); + let rv: ContractBalanceOfQueryResponse = + invoke.parse_return_value().expect("BalanceOf return value"); + rv } /// Setup chain and contract. @@ -312,11 +586,69 @@ fn initialize_contract_with_alice_tokens() -> (Chain, ContractAddress, ContractI /// Also creates the two accounts, Alice, and Bob. /// /// Alice is the owner of the contract. -fn initialize_chain_and_contract() -> (Chain, ContractAddress) { +/// Alice's account is created with keys. +/// Hence, Alice's account signature can be checked in the test cases. +fn initialize_chain_and_contract( + generate_keys: bool, +) -> (Chain, ContractAddress, Option) { let mut chain = Chain::new(); + let (account_access_structure, keypairs) = match generate_keys { + // If `generate_keys` is true, fresh keys are generated for Alice. + // Since Alice's private key is available, Alice can sign and generate a valid signature in + // the test cases. + true => { + let rng = &mut rand::thread_rng(); + + let keypairs = AccountKeys::singleton(rng); + ((&keypairs).into(), Some(keypairs)) + } + // If `generate_keys` is false, Alice's account is assigned a hardcoded public key. + // Since Alice's private key is NOT available, hardcoded signatures are used in the test + // cases. The signatures are generated outside the test cases (e.g. with https://cyphr.me/ed25519_tool/ed.html). + false => { + let mut inner_key_map: BTreeMap = BTreeMap::new(); + + inner_key_map.insert( + KeyIndex(0u8), + VerifyKey::Ed25519VerifyKey( + ed25519_dalek::PublicKey::from_bytes(&PUBLIC_KEY) + .expect("Should be able to create public key"), + ), + ); + + let credential_public_keys = CredentialPublicKeys { + keys: inner_key_map, + threshold: SignatureThreshold::ONE, + }; + + let mut key_map: BTreeMap = BTreeMap::new(); + key_map.insert( + CredentialIndex { + index: 0u8, + }, + credential_public_keys, + ); + + ( + AccountAccessStructure { + keys: key_map, + threshold: AccountThreshold::ONE, + }, + None, + ) + } + }; + + let balance = AccountBalance { + total: ACC_INITIAL_BALANCE, + staked: Amount::zero(), + locked: Amount::zero(), + }; + // Create some accounts accounts on the chain. - chain.create_account(Account::new(ALICE, ACC_INITIAL_BALANCE)); + chain.create_account(Account::new_with_keys(ALICE, balance, account_access_structure)); + chain.create_account(Account::new(ACC_ADDR_OWNER, ACC_INITIAL_BALANCE)); chain.create_account(Account::new(BOB, ACC_INITIAL_BALANCE)); // Load and deploy the module. @@ -333,5 +665,5 @@ fn initialize_chain_and_contract() -> (Chain, ContractAddress) { }) .expect("Initialize contract"); - (chain, init.contract_address) + (chain, init.contract_address, keypairs) } From 2e753241b5f3e85a12654c9f0107d8c2b8cfcfda Mon Sep 17 00:00:00 2001 From: Doris Benda Date: Wed, 8 Nov 2023 10:52:56 +0200 Subject: [PATCH 2/8] Simplified signing --- .../cis3-nft-sponsored-txs/tests/tests.rs | 23 ++----------------- 1 file changed, 2 insertions(+), 21 deletions(-) diff --git a/examples/cis3-nft-sponsored-txs/tests/tests.rs b/examples/cis3-nft-sponsored-txs/tests/tests.rs index 4f89c095..6f17ffec 100644 --- a/examples/cis3-nft-sponsored-txs/tests/tests.rs +++ b/examples/cis3-nft-sponsored-txs/tests/tests.rs @@ -102,28 +102,9 @@ fn test_inside_signature_permit_transfer() { let message_hash: HashSha2256 = from_bytes(&invoke.return_value).expect("Should return a valid result"); - let signature = keypairs + permit_transfer_param.signature = keypairs .expect("Should have a generated private key to sign") - .sign_data(&to_bytes(&message_hash)); - - let t = signature[&CredentialIndex { - index: 0, - }][&KeyIndex(0u8)] - .sig - .clone(); - let q: [u8; 64] = t.try_into().unwrap(); - - let mut inner_signature_map = BTreeMap::new(); - inner_signature_map.insert(0u8, concordium_std::Signature::Ed25519(SignatureEd25519(q))); - - let mut signature_map = BTreeMap::new(); - signature_map.insert(0u8, CredentialSignatures { - sigs: inner_signature_map, - }); - - permit_transfer_param.signature = AccountSignatures { - sigs: signature_map, - }; + .sign_message(&to_bytes(&message_hash)); // Transfer token with the permit function. let update = chain From 34625828da4e897904e37e2fd7ad7ab3026e2849 Mon Sep 17 00:00:00 2001 From: Doris Benda Date: Wed, 8 Nov 2023 11:27:00 +0200 Subject: [PATCH 3/8] Add update operator test cases --- .../cis3-nft-sponsored-txs/tests/tests.rs | 281 ++++++++++++++++-- 1 file changed, 249 insertions(+), 32 deletions(-) diff --git a/examples/cis3-nft-sponsored-txs/tests/tests.rs b/examples/cis3-nft-sponsored-txs/tests/tests.rs index 6f17ffec..758c8a00 100644 --- a/examples/cis3-nft-sponsored-txs/tests/tests.rs +++ b/examples/cis3-nft-sponsored-txs/tests/tests.rs @@ -38,11 +38,205 @@ const SIGNATURE_TRANSFER: SignatureEd25519 = SignatureEd25519([ 21, 68, 42, 79, 106, 106, 87, 125, 122, 77, 154, 114, 208, 145, 171, 47, 108, 96, 221, 13, ]); +const SIGNATURE_UPDATE_OPERATOR: SignatureEd25519 = SignatureEd25519([ + 199, 250, 51, 48, 15, 210, 20, 180, 70, 191, 98, 217, 109, 67, 115, 94, 195, 81, 16, 157, 59, + 26, 36, 147, 91, 196, 254, 133, 149, 27, 148, 124, 130, 206, 68, 195, 139, 189, 244, 43, 253, + 12, 58, 17, 102, 63, 203, 35, 159, 54, 94, 59, 12, 193, 48, 78, 144, 112, 245, 149, 12, 181, + 74, 10, +]); + const DUMMY_SIGNATURE: SignatureEd25519 = SignatureEd25519([ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, ]); +/// Test permit update operator function. The signature is generated in the test +/// case. ALICE adds BOB as an operator. +#[test] +fn test_inside_signature_permit_update_operator() { + let (mut chain, contract_address, _update, keypairs) = + initialize_contract_with_alice_tokens(true); + + // Check operator in state + let bob_is_operator_of_alice = operator_of(&chain, contract_address); + + assert_eq!(bob_is_operator_of_alice, OperatorOfQueryResponse(vec![false])); + + // Create input parematers for the `permit` updateOperator function. + let update_operator = UpdateOperator { + update: OperatorUpdate::Add, + operator: BOB_ADDR, + }; + let payload = UpdateOperatorParams(vec![update_operator]); + + let mut inner_signature_map = BTreeMap::new(); + inner_signature_map.insert(0u8, concordium_std::Signature::Ed25519(DUMMY_SIGNATURE)); + + let mut signature_map = BTreeMap::new(); + signature_map.insert(0u8, CredentialSignatures { + sigs: inner_signature_map, + }); + + let mut permit_update_operator_param = PermitParam { + signature: AccountSignatures { + sigs: signature_map, + }, + signer: ALICE, + message: PermitMessage { + timestamp: Timestamp::from_timestamp_millis(10000000000), + contract_address: ContractAddress { + index: 0, + subindex: 0, + }, + entry_point: OwnedEntrypointName::new_unchecked("updateOperator".into()), + nonce: 0, + payload: to_bytes(&payload), + }, + }; + + let invoke = chain + .contract_invoke(BOB, BOB_ADDR, Energy::from(10000), UpdateContractPayload { + amount: Amount::zero(), + address: contract_address, + receive_name: OwnedReceiveName::new_unchecked("cis3_nft.viewMessageHash".to_string()), + message: OwnedParameter::from_serial(&permit_update_operator_param) + .expect("Should be a valid inut parameter"), + }) + .expect("Should be able to query balanceOf"); + + let message_hash: HashSha2256 = + from_bytes(&invoke.return_value).expect("Should return a valid result"); + + permit_update_operator_param.signature = keypairs + .expect("Should have a generated private key to sign") + .sign_message(&to_bytes(&message_hash)); + + // Update operator with the permit function. + let update = chain + .contract_update( + Signer::with_one_key(), + ACC_ADDR_OWNER, + Address::Account(ACC_ADDR_OWNER), + Energy::from(10000), + UpdateContractPayload { + amount: Amount::zero(), + address: contract_address, + receive_name: OwnedReceiveName::new_unchecked("cis3_nft.permit".to_string()), + message: OwnedParameter::from_serial(&permit_update_operator_param) + .expect("Should be a valid inut parameter"), + }, + ) + .expect("Should be able to update operator with permit"); + + // Check that the correct events occurred. + let events = update + .events() + .flat_map(|(_addr, events)| events.iter().map(|e| e.parse().expect("Deserialize event"))) + .collect::>(); + + assert_eq!(events, [ + Event::Cis2Event(Cis2Event::UpdateOperator(UpdateOperatorEvent { + update: OperatorUpdate::Add, + owner: ALICE_ADDR, + operator: BOB_ADDR, + })), + Event::Nonce(NonceEvent { + account: ALICE, + nonce: 0, + }) + ]); + + // Check operator in state + let bob_is_operator_of_alice = operator_of(&chain, contract_address); + + assert_eq!(bob_is_operator_of_alice, OperatorOfQueryResponse(vec![true])); +} + +/// Test permit update operator function. The signature is generated outside +/// this test case (e.g. with https://cyphr.me/ed25519_tool/ed.html). ALICE adds BOB as an operator. +#[test] +fn test_outside_signature_permit_update_operator() { + let (mut chain, contract_address, _update, _keypairs) = + initialize_contract_with_alice_tokens(false); + + // Check operator in state + let bob_is_operator_of_alice = operator_of(&chain, contract_address); + + assert_eq!(bob_is_operator_of_alice, OperatorOfQueryResponse(vec![false])); + + // Create input parematers for the `permit` updateOperator function. + let update_operator = UpdateOperator { + update: OperatorUpdate::Add, + operator: BOB_ADDR, + }; + let payload = UpdateOperatorParams(vec![update_operator]); + + let mut inner_signature_map = BTreeMap::new(); + inner_signature_map.insert(0u8, concordium_std::Signature::Ed25519(SIGNATURE_UPDATE_OPERATOR)); + + let mut signature_map = BTreeMap::new(); + signature_map.insert(0u8, CredentialSignatures { + sigs: inner_signature_map, + }); + + let permit_update_operator_param = PermitParam { + signature: AccountSignatures { + sigs: signature_map, + }, + signer: ALICE, + message: PermitMessage { + timestamp: Timestamp::from_timestamp_millis(10000000000), + contract_address: ContractAddress { + index: 0, + subindex: 0, + }, + entry_point: OwnedEntrypointName::new_unchecked("updateOperator".into()), + nonce: 0, + payload: to_bytes(&payload), + }, + }; + + // Update operator with the permit function. + let update = chain + .contract_update( + Signer::with_one_key(), + ACC_ADDR_OWNER, + Address::Account(ACC_ADDR_OWNER), + Energy::from(10000), + UpdateContractPayload { + amount: Amount::zero(), + address: contract_address, + receive_name: OwnedReceiveName::new_unchecked("cis3_nft.permit".to_string()), + message: OwnedParameter::from_serial(&permit_update_operator_param) + .expect("Should be a valid inut parameter"), + }, + ) + .expect("Should be able to update operator with permit"); + + // Check that the correct events occurred. + let events = update + .events() + .flat_map(|(_addr, events)| events.iter().map(|e| e.parse().expect("Deserialize event"))) + .collect::>(); + + assert_eq!(events, [ + Event::Cis2Event(Cis2Event::UpdateOperator(UpdateOperatorEvent { + update: OperatorUpdate::Add, + owner: ALICE_ADDR, + operator: BOB_ADDR, + })), + Event::Nonce(NonceEvent { + account: ALICE, + nonce: 0, + }) + ]); + + // Check operator in state + let bob_is_operator_of_alice = operator_of(&chain, contract_address); + + assert_eq!(bob_is_operator_of_alice, OperatorOfQueryResponse(vec![true])); +} + /// Test permit transfer function. The signature is generated in the test case. /// TOKEN_1 is transferred from Alice to Bob. #[test] @@ -493,6 +687,61 @@ fn test_operator_can_transfer() { ]); } +/// Get the `TOKEN_1` balances for Alice and Bob. +fn get_balances( + chain: &Chain, + contract_address: ContractAddress, +) -> ContractBalanceOfQueryResponse { + let balance_of_params = ContractBalanceOfQueryParams { + queries: vec![ + BalanceOfQuery { + token_id: TOKEN_1, + address: ALICE_ADDR, + }, + BalanceOfQuery { + token_id: TOKEN_1, + address: BOB_ADDR, + }, + ], + }; + + let invoke = chain + .contract_invoke(ALICE, ALICE_ADDR, Energy::from(10000), UpdateContractPayload { + amount: Amount::zero(), + receive_name: OwnedReceiveName::new_unchecked("cis3_nft.balanceOf".to_string()), + address: contract_address, + message: OwnedParameter::from_serial(&balance_of_params) + .expect("BalanceOf params"), + }) + .expect("Invoke balanceOf"); + let rv: ContractBalanceOfQueryResponse = + invoke.parse_return_value().expect("BalanceOf return value"); + rv +} + +/// Check if Bob is an operator of Alice. +fn operator_of(chain: &Chain, contract_address: ContractAddress) -> OperatorOfQueryResponse { + let operator_of_params = OperatorOfQueryParams { + queries: vec![OperatorOfQuery { + address: BOB_ADDR, + owner: ALICE_ADDR, + }], + }; + + // Check operator in state + let invoke = chain + .contract_invoke(ALICE, ALICE_ADDR, Energy::from(10000), UpdateContractPayload { + amount: Amount::zero(), + receive_name: OwnedReceiveName::new_unchecked("cis3_nft.operatorOf".to_string()), + address: contract_address, + message: OwnedParameter::from_serial(&operator_of_params) + .expect("OperatorOf params"), + }) + .expect("Invoke operatorOf"); + let rv: OperatorOfQueryResponse = invoke.parse_return_value().expect("OperatorOf return value"); + rv +} + /// Helper function that sets up the contract with two tokens minted to /// Alice, `TOKEN_0` and `TOKEN_1`. /// @@ -530,38 +779,6 @@ fn initialize_contract_with_alice_tokens( (chain, contract_address, update, keypairs) } -/// Get the `TOKEN_1` balances for Alice and Bob. -fn get_balances( - chain: &Chain, - contract_address: ContractAddress, -) -> ContractBalanceOfQueryResponse { - let balance_of_params = ContractBalanceOfQueryParams { - queries: vec![ - BalanceOfQuery { - token_id: TOKEN_1, - address: ALICE_ADDR, - }, - BalanceOfQuery { - token_id: TOKEN_1, - address: BOB_ADDR, - }, - ], - }; - - let invoke = chain - .contract_invoke(ALICE, ALICE_ADDR, Energy::from(10000), UpdateContractPayload { - amount: Amount::zero(), - receive_name: OwnedReceiveName::new_unchecked("cis3_nft.balanceOf".to_string()), - address: contract_address, - message: OwnedParameter::from_serial(&balance_of_params) - .expect("BalanceOf params"), - }) - .expect("Invoke balanceOf"); - let rv: ContractBalanceOfQueryResponse = - invoke.parse_return_value().expect("BalanceOf return value"); - rv -} - /// Setup chain and contract. /// /// Also creates the two accounts, Alice, and Bob. From 645e0740229d70f1b88c3c4f10a37b69be2df753 Mon Sep 17 00:00:00 2001 From: Doris Benda Date: Fri, 10 Nov 2023 12:08:02 +0200 Subject: [PATCH 4/8] Add comments --- .../cis3-nft-sponsored-txs/tests/tests.rs | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/examples/cis3-nft-sponsored-txs/tests/tests.rs b/examples/cis3-nft-sponsored-txs/tests/tests.rs index 758c8a00..24cbad25 100644 --- a/examples/cis3-nft-sponsored-txs/tests/tests.rs +++ b/examples/cis3-nft-sponsored-txs/tests/tests.rs @@ -14,7 +14,7 @@ const ALICE: AccountAddress = AccountAddress([0; 32]); const ALICE_ADDR: Address = Address::Account(ALICE); const BOB: AccountAddress = AccountAddress([1; 32]); const BOB_ADDR: Address = Address::Account(BOB); -const ACC_ADDR_OWNER: AccountAddress = AccountAddress([2u8; 32]); +const CHARLIE: AccountAddress = AccountAddress([2u8; 32]); /// Token IDs. const TOKEN_0: ContractTokenId = TokenIdU32(1); @@ -62,7 +62,7 @@ fn test_inside_signature_permit_update_operator() { assert_eq!(bob_is_operator_of_alice, OperatorOfQueryResponse(vec![false])); - // Create input parematers for the `permit` updateOperator function. + // Create input parameters for the `permit` updateOperator function. let update_operator = UpdateOperator { update: OperatorUpdate::Add, operator: BOB_ADDR, @@ -94,6 +94,7 @@ fn test_inside_signature_permit_update_operator() { }, }; + // Get the message hash to be signed. let invoke = chain .contract_invoke(BOB, BOB_ADDR, Energy::from(10000), UpdateContractPayload { amount: Amount::zero(), @@ -102,7 +103,7 @@ fn test_inside_signature_permit_update_operator() { message: OwnedParameter::from_serial(&permit_update_operator_param) .expect("Should be a valid inut parameter"), }) - .expect("Should be able to query balanceOf"); + .expect("Should be able to query viewMessageHash"); let message_hash: HashSha2256 = from_bytes(&invoke.return_value).expect("Should return a valid result"); @@ -115,8 +116,8 @@ fn test_inside_signature_permit_update_operator() { let update = chain .contract_update( Signer::with_one_key(), - ACC_ADDR_OWNER, - Address::Account(ACC_ADDR_OWNER), + CHARLIE, + Address::Account(CHARLIE), Energy::from(10000), UpdateContractPayload { amount: Amount::zero(), @@ -164,7 +165,7 @@ fn test_outside_signature_permit_update_operator() { assert_eq!(bob_is_operator_of_alice, OperatorOfQueryResponse(vec![false])); - // Create input parematers for the `permit` updateOperator function. + // Create input parameters for the `permit` updateOperator function. let update_operator = UpdateOperator { update: OperatorUpdate::Add, operator: BOB_ADDR, @@ -200,8 +201,8 @@ fn test_outside_signature_permit_update_operator() { let update = chain .contract_update( Signer::with_one_key(), - ACC_ADDR_OWNER, - Address::Account(ACC_ADDR_OWNER), + CHARLIE, + Address::Account(CHARLIE), Energy::from(10000), UpdateContractPayload { amount: Amount::zero(), @@ -249,6 +250,7 @@ fn test_inside_signature_permit_transfer() { assert_eq!(balance_of_alice_and_bob.0, [TokenAmountU8(1), TokenAmountU8(0)]); + // Create input parameters for the `permit` transfer function. let transfer = concordium_cis2::Transfer { from: ALICE_ADDR, to: Receiver::from_account(BOB), @@ -283,6 +285,7 @@ fn test_inside_signature_permit_transfer() { }, }; + // Get the message hash to be signed. let invoke = chain .contract_invoke(BOB, BOB_ADDR, Energy::from(10000), UpdateContractPayload { amount: Amount::zero(), @@ -291,7 +294,7 @@ fn test_inside_signature_permit_transfer() { message: OwnedParameter::from_serial(&permit_transfer_param) .expect("Should be a valid inut parameter"), }) - .expect("Should be able to query balanceOf"); + .expect("Should be able to query viewMessageHash"); let message_hash: HashSha2256 = from_bytes(&invoke.return_value).expect("Should return a valid result"); @@ -354,6 +357,7 @@ fn test_outside_signature_permit_transfer() { assert_eq!(balance_of_alice_and_bob.0, [TokenAmountU8(1), TokenAmountU8(0)]); + // Create input parameters for the `permit` transfer function. let transfer = concordium_cis2::Transfer { from: ALICE_ADDR, to: Receiver::from_account(BOB), @@ -781,7 +785,7 @@ fn initialize_contract_with_alice_tokens( /// Setup chain and contract. /// -/// Also creates the two accounts, Alice, and Bob. +/// Also creates the three accounts, Alice, Bob, and Charlie. /// /// Alice is the owner of the contract. /// Alice's account is created with keys. @@ -846,7 +850,7 @@ fn initialize_chain_and_contract( // Create some accounts accounts on the chain. chain.create_account(Account::new_with_keys(ALICE, balance, account_access_structure)); - chain.create_account(Account::new(ACC_ADDR_OWNER, ACC_INITIAL_BALANCE)); + chain.create_account(Account::new(CHARLIE, ACC_INITIAL_BALANCE)); chain.create_account(Account::new(BOB, ACC_INITIAL_BALANCE)); // Load and deploy the module. From 960f94e796a61f54e07daaf3ae5ede777ce586e6 Mon Sep 17 00:00:00 2001 From: Doris Benda Date: Mon, 20 Nov 2023 20:24:55 +0300 Subject: [PATCH 5/8] Address comments --- contract-testing/src/lib.rs | 1 + examples/cis3-nft-sponsored-txs/Cargo.toml | 1 - .../cis3-nft-sponsored-txs/tests/tests.rs | 73 ++++++------------- 3 files changed, 23 insertions(+), 52 deletions(-) diff --git a/contract-testing/src/lib.rs b/contract-testing/src/lib.rs index 23389663..d22a28e1 100644 --- a/contract-testing/src/lib.rs +++ b/contract-testing/src/lib.rs @@ -96,6 +96,7 @@ pub use types::*; pub use concordium_base::{ base::Energy, common::types::{CredentialIndex, KeyIndex}, + PublicKey, SecretKey, Signature, contracts_common::{ from_bytes, to_bytes, AccountAddress, AccountBalance, AccountThreshold, Address, Amount, ContractAddress, ContractName, Duration, EntrypointName, ExchangeRate, ModuleReference, diff --git a/examples/cis3-nft-sponsored-txs/Cargo.toml b/examples/cis3-nft-sponsored-txs/Cargo.toml index 5517d326..68d039e9 100644 --- a/examples/cis3-nft-sponsored-txs/Cargo.toml +++ b/examples/cis3-nft-sponsored-txs/Cargo.toml @@ -16,7 +16,6 @@ concordium-cis2 = {path = "../../concordium-cis2", default-features = false} [dev-dependencies] concordium-smart-contract-testing = { path = "../../contract-testing" } -ed25519-dalek = "1.0" rand = "0.7.0" [lib] diff --git a/examples/cis3-nft-sponsored-txs/tests/tests.rs b/examples/cis3-nft-sponsored-txs/tests/tests.rs index 24cbad25..abc36b83 100644 --- a/examples/cis3-nft-sponsored-txs/tests/tests.rs +++ b/examples/cis3-nft-sponsored-txs/tests/tests.rs @@ -3,7 +3,7 @@ use cis3_nft_sponsored_txs::{ ContractTokenAmount, ContractTokenId, MintParams, NonceEvent, PermitMessage, PermitParam, *, }; use concordium_cis2::{TokenIdU32, *}; -use concordium_smart_contract_testing::{AccountAccessStructure, AccountKeys, *}; +use concordium_smart_contract_testing::{AccountAccessStructure, AccountKeys, PublicKey, *}; use concordium_std::{ AccountSignatures, CredentialSignatures, HashSha2256, SignatureEd25519, Timestamp, }; @@ -70,6 +70,12 @@ fn test_inside_signature_permit_update_operator() { let payload = UpdateOperatorParams(vec![update_operator]); let mut inner_signature_map = BTreeMap::new(); + + // The `viewMessageHash` function uses the same input parameter `PermitParam` as + // the `permit` function. The `PermitParam` type includes a `signature` and + // a `signer`. Becuase these two values (`signature` and `signer`) are not + // read in the `viewMessageHash` function, any value can be used and we choose + // to use `DUMMY_SIGNATURE` and `ALICE` in the test case below. inner_signature_map.insert(0u8, concordium_std::Signature::Ed25519(DUMMY_SIGNATURE)); let mut signature_map = BTreeMap::new(); @@ -83,11 +89,8 @@ fn test_inside_signature_permit_update_operator() { }, signer: ALICE, message: PermitMessage { - timestamp: Timestamp::from_timestamp_millis(10000000000), - contract_address: ContractAddress { - index: 0, - subindex: 0, - }, + timestamp: Timestamp::from_timestamp_millis(10_000_000_000), + contract_address: ContractAddress::new(0, 0), entry_point: OwnedEntrypointName::new_unchecked("updateOperator".into()), nonce: 0, payload: to_bytes(&payload), @@ -186,11 +189,8 @@ fn test_outside_signature_permit_update_operator() { }, signer: ALICE, message: PermitMessage { - timestamp: Timestamp::from_timestamp_millis(10000000000), - contract_address: ContractAddress { - index: 0, - subindex: 0, - }, + timestamp: Timestamp::from_timestamp_millis(10_000_000_000), + contract_address: ContractAddress::new(0, 0), entry_point: OwnedEntrypointName::new_unchecked("updateOperator".into()), nonce: 0, payload: to_bytes(&payload), @@ -261,6 +261,12 @@ fn test_inside_signature_permit_transfer() { let payload = TransferParams::from(vec![transfer]); let mut inner_signature_map = BTreeMap::new(); + + // The `viewMessageHash` function uses the same input parameter `PermitParam` as + // the `permit` function. The `PermitParam` type includes a `signature` and + // a `signer`. Becuase these two values (`signature` and `signer`) are not + // read in the `viewMessageHash` function, any value can be used and we choose + // to use `DUMMY_SIGNATURE` and `ALICE` in the test case below. inner_signature_map.insert(0u8, concordium_std::Signature::Ed25519(DUMMY_SIGNATURE)); let mut signature_map = BTreeMap::new(); @@ -274,11 +280,8 @@ fn test_inside_signature_permit_transfer() { }, signer: ALICE, message: PermitMessage { - timestamp: Timestamp::from_timestamp_millis(10000000000), - contract_address: ContractAddress { - index: 0, - subindex: 0, - }, + timestamp: Timestamp::from_timestamp_millis(10_000_000_000), + contract_address: ContractAddress::new(0, 0), entry_point: OwnedEntrypointName::new_unchecked("transfer".into()), nonce: 0, payload: to_bytes(&payload), @@ -381,11 +384,8 @@ fn test_outside_signature_permit_transfer() { }, signer: ALICE, message: PermitMessage { - timestamp: Timestamp::from_timestamp_millis(10000000000), - contract_address: ContractAddress { - index: 0, - subindex: 0, - }, + timestamp: Timestamp::from_timestamp_millis(10_000_000_000), + contract_address: ContractAddress::new(0, 0), entry_point: OwnedEntrypointName::new_unchecked("transfer".into()), nonce: 0, payload: to_bytes(&payload), @@ -809,36 +809,7 @@ fn initialize_chain_and_contract( // Since Alice's private key is NOT available, hardcoded signatures are used in the test // cases. The signatures are generated outside the test cases (e.g. with https://cyphr.me/ed25519_tool/ed.html). false => { - let mut inner_key_map: BTreeMap = BTreeMap::new(); - - inner_key_map.insert( - KeyIndex(0u8), - VerifyKey::Ed25519VerifyKey( - ed25519_dalek::PublicKey::from_bytes(&PUBLIC_KEY) - .expect("Should be able to create public key"), - ), - ); - - let credential_public_keys = CredentialPublicKeys { - keys: inner_key_map, - threshold: SignatureThreshold::ONE, - }; - - let mut key_map: BTreeMap = BTreeMap::new(); - key_map.insert( - CredentialIndex { - index: 0u8, - }, - credential_public_keys, - ); - - ( - AccountAccessStructure { - keys: key_map, - threshold: AccountThreshold::ONE, - }, - None, - ) + (AccountAccessStructure::singleton(PublicKey::from_bytes(&PUBLIC_KEY).expect("Should be able to construct public key from bytes.")), None) } }; From d1711555e4498512af9f58044c9832bf45ae08d6 Mon Sep 17 00:00:00 2001 From: Doris Benda Date: Tue, 21 Nov 2023 12:34:49 +0300 Subject: [PATCH 6/8] Fix submodule link --- concordium-rust-sdk | 2 +- contract-testing/src/lib.rs | 2 +- examples/cis3-nft-sponsored-txs/tests/tests.rs | 12 ++++++++---- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/concordium-rust-sdk b/concordium-rust-sdk index ccf3ce36..56cd048c 160000 --- a/concordium-rust-sdk +++ b/concordium-rust-sdk @@ -1 +1 @@ -Subproject commit ccf3ce36617ba15418033692fb984776b6008d30 +Subproject commit 56cd048ce8e7354be7fd151a418e3f7038e11c14 diff --git a/contract-testing/src/lib.rs b/contract-testing/src/lib.rs index d22a28e1..af135e4d 100644 --- a/contract-testing/src/lib.rs +++ b/contract-testing/src/lib.rs @@ -96,13 +96,13 @@ pub use types::*; pub use concordium_base::{ base::Energy, common::types::{CredentialIndex, KeyIndex}, - PublicKey, SecretKey, Signature, contracts_common::{ from_bytes, to_bytes, AccountAddress, AccountBalance, AccountThreshold, Address, Amount, ContractAddress, ContractName, Duration, EntrypointName, ExchangeRate, ModuleReference, OwnedContractName, OwnedEntrypointName, OwnedParameter, OwnedReceiveName, Parameter, ReceiveName, SignatureThreshold, SlotTime, Timestamp, }, + ed25519, hashes::BlockHash, id::types::{AccountKeys, CredentialPublicKeys, VerifyKey}, smart_contracts::{ContractEvent, ContractTraceElement, InstanceUpdatedEvent, WasmVersion}, diff --git a/examples/cis3-nft-sponsored-txs/tests/tests.rs b/examples/cis3-nft-sponsored-txs/tests/tests.rs index abc36b83..ac158c8a 100644 --- a/examples/cis3-nft-sponsored-txs/tests/tests.rs +++ b/examples/cis3-nft-sponsored-txs/tests/tests.rs @@ -3,7 +3,7 @@ use cis3_nft_sponsored_txs::{ ContractTokenAmount, ContractTokenId, MintParams, NonceEvent, PermitMessage, PermitParam, *, }; use concordium_cis2::{TokenIdU32, *}; -use concordium_smart_contract_testing::{AccountAccessStructure, AccountKeys, PublicKey, *}; +use concordium_smart_contract_testing::{AccountAccessStructure, AccountKeys, *}; use concordium_std::{ AccountSignatures, CredentialSignatures, HashSha2256, SignatureEd25519, Timestamp, }; @@ -808,9 +808,13 @@ fn initialize_chain_and_contract( // If `generate_keys` is false, Alice's account is assigned a hardcoded public key. // Since Alice's private key is NOT available, hardcoded signatures are used in the test // cases. The signatures are generated outside the test cases (e.g. with https://cyphr.me/ed25519_tool/ed.html). - false => { - (AccountAccessStructure::singleton(PublicKey::from_bytes(&PUBLIC_KEY).expect("Should be able to construct public key from bytes.")), None) - } + false => ( + AccountAccessStructure::singleton( + ed25519::PublicKey::from_bytes(&PUBLIC_KEY) + .expect("Should be able to construct public key from bytes."), + ), + None, + ), }; let balance = AccountBalance { From 046b7445b578b68a733beca968928a5947350d45 Mon Sep 17 00:00:00 2001 From: Doris Benda Date: Wed, 22 Nov 2023 19:11:11 +0300 Subject: [PATCH 7/8] Update examples/cis3-nft-sponsored-txs/tests/tests.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Emil Holm Gjørup --- examples/cis3-nft-sponsored-txs/tests/tests.rs | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/examples/cis3-nft-sponsored-txs/tests/tests.rs b/examples/cis3-nft-sponsored-txs/tests/tests.rs index ac158c8a..2199b9f7 100644 --- a/examples/cis3-nft-sponsored-txs/tests/tests.rs +++ b/examples/cis3-nft-sponsored-txs/tests/tests.rs @@ -69,19 +69,18 @@ fn test_inside_signature_permit_update_operator() { }; let payload = UpdateOperatorParams(vec![update_operator]); - let mut inner_signature_map = BTreeMap::new(); - // The `viewMessageHash` function uses the same input parameter `PermitParam` as // the `permit` function. The `PermitParam` type includes a `signature` and // a `signer`. Becuase these two values (`signature` and `signer`) are not // read in the `viewMessageHash` function, any value can be used and we choose // to use `DUMMY_SIGNATURE` and `ALICE` in the test case below. - inner_signature_map.insert(0u8, concordium_std::Signature::Ed25519(DUMMY_SIGNATURE)); - - let mut signature_map = BTreeMap::new(); - signature_map.insert(0u8, CredentialSignatures { - sigs: inner_signature_map, - }); + let signature_map = BTreeMap::from([ + (0u8, CredentialSignatures { + sigs: BTreeMap::from([ + (0u8, concordium_std::Signature::Ed25519(DUMMY_SIGNATURE)) + ]), + }) + ]); let mut permit_update_operator_param = PermitParam { signature: AccountSignatures { From 2cb9cf335efbb702bcf158416d2033ce39f89ce7 Mon Sep 17 00:00:00 2001 From: Doris Benda Date: Wed, 22 Nov 2023 19:17:25 +0300 Subject: [PATCH 8/8] Address comments --- .../cis3-nft-sponsored-txs/tests/tests.rs | 21 ++++++------------- 1 file changed, 6 insertions(+), 15 deletions(-) diff --git a/examples/cis3-nft-sponsored-txs/tests/tests.rs b/examples/cis3-nft-sponsored-txs/tests/tests.rs index 2199b9f7..e0730290 100644 --- a/examples/cis3-nft-sponsored-txs/tests/tests.rs +++ b/examples/cis3-nft-sponsored-txs/tests/tests.rs @@ -74,13 +74,9 @@ fn test_inside_signature_permit_update_operator() { // a `signer`. Becuase these two values (`signature` and `signer`) are not // read in the `viewMessageHash` function, any value can be used and we choose // to use `DUMMY_SIGNATURE` and `ALICE` in the test case below. - let signature_map = BTreeMap::from([ - (0u8, CredentialSignatures { - sigs: BTreeMap::from([ - (0u8, concordium_std::Signature::Ed25519(DUMMY_SIGNATURE)) - ]), - }) - ]); + let signature_map = BTreeMap::from([(0u8, CredentialSignatures { + sigs: BTreeMap::from([(0u8, concordium_std::Signature::Ed25519(DUMMY_SIGNATURE))]), + })]); let mut permit_update_operator_param = PermitParam { signature: AccountSignatures { @@ -259,19 +255,14 @@ fn test_inside_signature_permit_transfer() { }; let payload = TransferParams::from(vec![transfer]); - let mut inner_signature_map = BTreeMap::new(); - // The `viewMessageHash` function uses the same input parameter `PermitParam` as // the `permit` function. The `PermitParam` type includes a `signature` and // a `signer`. Becuase these two values (`signature` and `signer`) are not // read in the `viewMessageHash` function, any value can be used and we choose // to use `DUMMY_SIGNATURE` and `ALICE` in the test case below. - inner_signature_map.insert(0u8, concordium_std::Signature::Ed25519(DUMMY_SIGNATURE)); - - let mut signature_map = BTreeMap::new(); - signature_map.insert(0u8, CredentialSignatures { - sigs: inner_signature_map, - }); + let signature_map = BTreeMap::from([(0u8, CredentialSignatures { + sigs: BTreeMap::from([(0u8, concordium_std::Signature::Ed25519(DUMMY_SIGNATURE))]), + })]); let mut permit_transfer_param = PermitParam { signature: AccountSignatures {