From 89e70c74cdd3cbabe82794f975063249b00cc569 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Fri, 21 Mar 2025 18:09:27 +0400 Subject: [PATCH 1/6] RPC extended for ownership transfer and attachment to wallet --- src/currency_core/currency_basic.h | 2 + src/wallet/wallet2.cpp | 39 +++ src/wallet/wallet2.h | 2 + src/wallet/wallet_public_structs_defs.h | 56 +++++ src/wallet/wallet_rpc_server.cpp | 86 +++++-- src/wallet/wallet_rpc_server.h | 5 + tests/core_tests/wallet_rpc_tests.cpp | 303 +++++++++++++++++++++++- tests/core_tests/wallet_rpc_tests.h | 8 + 8 files changed, 482 insertions(+), 19 deletions(-) diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index 22683c3d..a844f612 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -55,6 +55,8 @@ namespace currency const static crypto::secret_key null_skey = AUTO_VAL_INIT(null_skey); const static crypto::signature null_sig = AUTO_VAL_INIT(null_sig); const static crypto::key_derivation null_derivation = AUTO_VAL_INIT(null_derivation); + const static crypto::eth_public_key null_eth_public_key = AUTO_VAL_INIT(null_eth_public_key); + const static crypto::hash gdefault_genesis = epee::string_tools::hex_to_pod("CC608F59F8080E2FBFE3C8C80EB6E6A953D47CF2D6AEBD345BADA3A1CAB99852"); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 433c5c6d..43b34ca9 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4435,6 +4435,45 @@ void wallet2::submit_externally_signed_asset_tx(const finalized_tx& ft, const cr print_tx_sent_message(tx, "from submit_externally_signed_asset_tx", true, get_tx_fee(tx)); } //---------------------------------------------------------------------------------------------------- +bool wallet2::attach_asset_descriptor(const wallet_public::COMMAND_ATTACH_ASSET_DESCRIPTOR::request& req, wallet_public::COMMAND_ATTACH_ASSET_DESCRIPTOR::response& resp) +{ + if (!req.do_attach) + { + //detaching + auto it = m_own_asset_descriptors.find(req.asset_id); + if (it == m_own_asset_descriptors.end()) + { + resp.status = API_RETURN_CODE_NOT_FOUND; + return false; + } + if (!it->second.thirdparty_custody) + { + LOG_ERROR("Detachig assets that are not 'thirdparty_custody' are not allowed"); + resp.status = API_RETURN_CODE_ACCESS_DENIED; + return false; + } + m_own_asset_descriptors.erase(it); + resp.status = API_RETURN_CODE_OK; + return true; + } + else + { + currency::COMMAND_RPC_GET_ASSET_INFO::request req_asset_info = AUTO_VAL_INIT(req_asset_info); + currency::COMMAND_RPC_GET_ASSET_INFO::response resp_asset_info = AUTO_VAL_INIT(resp_asset_info); + req_asset_info.asset_id = req.asset_id; + + bool r = m_core_proxy->call_COMMAND_RPC_GET_ASSET_INFO(req_asset_info, resp_asset_info); + if (r && resp_asset_info.status == API_RETURN_CODE_OK) + { + static_cast(m_own_asset_descriptors[req.asset_id]) = resp_asset_info.asset_descriptor; + m_own_asset_descriptors[req.asset_id].thirdparty_custody = true; + resp.status = API_RETURN_CODE_OK; + return true; + } + resp.status = API_RETURN_CODE_NOT_FOUND; + return false; + } +} void wallet2::submit_transfer(const std::string& signed_tx_blob, currency::transaction& tx) { // decrypt sources diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index a1274d6a..a2c92152 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -694,6 +694,7 @@ namespace tools size_t scan_for_collisions(std::unordered_map >& key_images); size_t fix_collisions(); size_t scan_for_transaction_entries(const crypto::hash& tx_id, const crypto::key_image& ki, std::list& details); + bool attach_asset_descriptor(const wallet_public::COMMAND_ATTACH_ASSET_DESCRIPTOR::request& req, wallet_public::COMMAND_ATTACH_ASSET_DESCRIPTOR::response& resp); bool get_contracts(escrow_contracts_container& contracts); const std::list& get_expiration_entries() const { return m_money_expirations; }; @@ -768,6 +769,7 @@ namespace tools void set_concise_mode_reorg_max_reorg_blocks(uint64_t max_blocks) { m_wallet_concise_mode_max_reorg_blocks = max_blocks; } void set_concise_mode_truncate_history(uint64_t max_entries) { m_truncate_history_max_entries = max_entries; } + construct_tx_param get_default_construct_tx_param(); //---------- m_rollback_events visitor ------------------------------------------------ diff --git a/src/wallet/wallet_public_structs_defs.h b/src/wallet/wallet_public_structs_defs.h index 4f8e8022..893200cf 100644 --- a/src/wallet/wallet_public_structs_defs.h +++ b/src/wallet/wallet_public_structs_defs.h @@ -2079,5 +2079,61 @@ namespace wallet_public }; + struct COMMAND_ATTACH_ASSET_DESCRIPTOR + { + DOC_COMMAND("Attach asset descripto to this wallet instance, if asset descripto attached then ADO operations to this asset can be performed using API of this wallet."); + + struct request + { + crypto::public_key asset_id; + bool do_attach = true; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Asset id of the ADO that need to be attached to this wallet") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END + KV_SERIALIZE(do_attach) DOC_DSCR("If true - asset descriptor attached to wallet, if false - asset detached") DOC_EXMP(true) DOC_END + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) DOC_DSCR("Status of the call") DOC_EXMP("OK") DOC_END + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_TRANSFER_ASSET_OWNERSHIP + { + DOC_COMMAND("Transfer asset ownership to new public key."); + + struct request + { + crypto::public_key asset_id; + crypto::eth_public_key owner_eth_pub_key = currency::null_eth_public_key; // note: the size is 33 bytes (if present) + crypto::public_key owner = currency::null_pkey; // regular Zano Ed25519 public key + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Own asset id, that would be transfered to someone else") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(owner) DOC_DSCR("Public key of the new owner(default Ed25519 public key, 32 bytes)") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(owner_eth_pub_key) DOC_DSCR("Public key of the new owner(ECDSA public key, 33 bytes) Used only if 'owner' field is empty") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a84d") DOC_END + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string status; + crypto::hash tx_id = currency::null_hash; + std::optional data_for_external_signing; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(status) DOC_DSCR("Status of the call") DOC_EXMP("OK") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(tx_id) DOC_DSCR("Id of transaction that carries asset transfer ownership operation") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + KV_SERIALIZE(data_for_external_signing) DOC_DSCR("[optional] Additional data for external ownership transfer tx signing(if asset is ownership is belong to third party).") DOC_EXMP_AGGR() DOC_END + END_KV_SERIALIZE_MAP() + }; + }; + } // namespace wallet_rpc } // namespace tools diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 377d214e..92f2452f 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1334,6 +1334,17 @@ namespace tools WALLET_RPC_CATCH_TRY_ENTRY(); } //------------------------------------------------------------------------------------------------------------------------------ + void finalized_tx_to_external_signing_data(const currency::finalized_tx& ft, wallet_public::data_for_external_asset_signing_tx& data) + { + // include additonal info into response, if it's an external signing asset operation + data.unsigned_tx = t_serializable_object_to_blob(ft.tx); + data.tx_secret_key = ft.one_time_key; + std::vector& outs_addr = data.outputs_addresses; + for (auto d : ft.ftp.prepared_destinations) + outs_addr.push_back(currency::get_account_address_as_str(d.addr.back())); + data.finalized_tx = t_serializable_object_to_blob(ft); + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_asset_emit(const wallet_public::COMMAND_ASSETS_EMIT::request& req, wallet_public::COMMAND_ASSETS_EMIT::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); @@ -1347,17 +1358,11 @@ namespace tools w.get_wallet()->emit_asset(req.asset_id, currency_destinations, ft); res.tx_id = ft.tx_id; - if (last_adb.owner_eth_pub_key.has_value()) + if (ft.ftp.ado_sign_thirdparty) { - // include additonal info into response, if it's an external signing asset operation + // include additional info into response, if it's an external signing asset operation wallet_public::data_for_external_asset_signing_tx data{}; - data.unsigned_tx = t_serializable_object_to_blob(ft.tx); - data.tx_secret_key = ft.one_time_key; - std::vector& outs_addr = data.outputs_addresses; - for(auto d : ft.ftp.prepared_destinations) - outs_addr.push_back(currency::get_account_address_as_str(d.addr.back())); - data.finalized_tx = t_serializable_object_to_blob(ft); - + finalized_tx_to_external_signing_data(ft, data); res.data_for_external_signing = data; } @@ -1372,17 +1377,11 @@ namespace tools w.get_wallet()->update_asset(req.asset_id, req.asset_descriptor, ft); res.tx_id = ft.tx_id; - if (req.asset_descriptor.owner_eth_pub_key.has_value()) + if (ft.ftp.ado_sign_thirdparty) { - // include additonal info into response, if it's an external signing asset operation + // include additional info into response, if it's an external signing asset operation wallet_public::data_for_external_asset_signing_tx data{}; - data.unsigned_tx = t_serializable_object_to_blob(ft.tx); - data.tx_secret_key = ft.one_time_key; - std::vector& outs_addr = data.outputs_addresses; - for(auto d : ft.ftp.prepared_destinations) - outs_addr.push_back(currency::get_account_address_as_str(d.addr.back())); - data.finalized_tx = t_serializable_object_to_blob(ft); - + finalized_tx_to_external_signing_data(ft, data); res.data_for_external_signing = data; } @@ -1445,6 +1444,57 @@ namespace tools WALLET_RPC_CATCH_TRY_ENTRY(); } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_attach_asset_descriptor(const wallet_public::COMMAND_ATTACH_ASSET_DESCRIPTOR::request& req, wallet_public::COMMAND_ATTACH_ASSET_DESCRIPTOR::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + WALLET_RPC_BEGIN_TRY_ENTRY(); + w.get_wallet()->attach_asset_descriptor(req, res); + return true; + WALLET_RPC_CATCH_TRY_ENTRY(); + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_transfer_asset_ownership(const wallet_public::COMMAND_TRANSFER_ASSET_OWNERSHIP::request& req, wallet_public::COMMAND_TRANSFER_ASSET_OWNERSHIP::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + WALLET_RPC_BEGIN_TRY_ENTRY(); + currency::asset_owner_pub_key_v new_owner_v; + if (req.owner != currency::null_pkey) + { + new_owner_v = req.owner; + } + else if(req.owner_eth_pub_key != currency::null_eth_public_key) + { + new_owner_v = req.owner_eth_pub_key; + }else + { + res.status = API_RETURN_CODE_BAD_ARG_INVALID_ADDRESS; + return true; + } + + try + { + currency::finalized_tx ft; + w.get_wallet()->transfer_asset_ownership(req.asset_id, new_owner_v, ft); + if (ft.ftp.ado_sign_thirdparty) + { + // include additional info into response, if it's an external signing asset operation + wallet_public::data_for_external_asset_signing_tx data{}; + finalized_tx_to_external_signing_data(ft, data); + res.data_for_external_signing = data; + } + res.tx_id = ft.tx_id; + res.status = API_RETURN_CODE_OK; + return true; + } + catch (std::exception& e) + { + // doing this to be able to return 'transfers_were_unlocked' to the caller even in the case of exception + res.status = e.what(); + return true; + } + + return true; + WALLET_RPC_CATCH_TRY_ENTRY(); + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_mw_get_wallets(const wallet_public::COMMAND_MW_GET_WALLETS::request& req, wallet_public::COMMAND_MW_GET_WALLETS::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index a424e103..fedc37d5 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -164,6 +164,8 @@ namespace tools MAP_JON_RPC_WE("update_asset", on_asset_update, wallet_public::COMMAND_ASSETS_UPDATE) MAP_JON_RPC_WE("burn_asset", on_asset_burn, wallet_public::COMMAND_ASSETS_BURN) MAP_JON_RPC_WE("send_ext_signed_asset_tx", on_asset_send_ext_signed_tx, wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX) + MAP_JON_RPC_WE("attach_asset_descriptor", on_attach_asset_descriptor, wallet_public::COMMAND_ATTACH_ASSET_DESCRIPTOR) + MAP_JON_RPC_WE("transfer_asset_ownership", on_transfer_asset_ownership, wallet_public::COMMAND_TRANSFER_ASSET_OWNERSHIP) //MULTIWALLET APIs MAP_JON_RPC_WE("mw_get_wallets", on_mw_get_wallets, wallet_public::COMMAND_MW_GET_WALLETS) @@ -236,6 +238,9 @@ namespace tools bool on_asset_update(const wallet_public::COMMAND_ASSETS_UPDATE::request& req, wallet_public::COMMAND_ASSETS_UPDATE::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_asset_burn(const wallet_public::COMMAND_ASSETS_BURN::request& req, wallet_public::COMMAND_ASSETS_BURN::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_asset_send_ext_signed_tx(const wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::request& req, wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_attach_asset_descriptor(const wallet_public::COMMAND_ATTACH_ASSET_DESCRIPTOR::request& req, wallet_public::COMMAND_ATTACH_ASSET_DESCRIPTOR::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_transfer_asset_ownership(const wallet_public::COMMAND_TRANSFER_ASSET_OWNERSHIP::request& req, wallet_public::COMMAND_TRANSFER_ASSET_OWNERSHIP::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_mw_get_wallets(const wallet_public::COMMAND_MW_GET_WALLETS::request& req, wallet_public::COMMAND_MW_GET_WALLETS::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_mw_select_wallet(const wallet_public::COMMAND_MW_SELECT_WALLET::request& req, wallet_public::COMMAND_MW_SELECT_WALLET::response& res, epee::json_rpc::error& er, connection_context& cntx); diff --git a/tests/core_tests/wallet_rpc_tests.cpp b/tests/core_tests/wallet_rpc_tests.cpp index 47b6d21b..7376176b 100644 --- a/tests/core_tests/wallet_rpc_tests.cpp +++ b/tests/core_tests/wallet_rpc_tests.cpp @@ -808,7 +808,7 @@ wallet_true_rpc_pos_mining::wallet_true_rpc_pos_mining() { REGISTER_CALLBACK_METHOD(wallet_true_rpc_pos_mining, c1); } - +//------------------------------------------------------------------------------ bool wallet_true_rpc_pos_mining::generate(std::vector& events) const { uint64_t ts = test_core_time::get_time(); @@ -924,3 +924,304 @@ bool wallet_true_rpc_pos_mining::c1(currency::core& c, size_t ev_index, const st return true; } + + + +wallet_rpc_thirdparty_custody::wallet_rpc_thirdparty_custody() +{ + REGISTER_CALLBACK_METHOD(wallet_rpc_thirdparty_custody, c1); +} +//------------------------------------------------------------------------------ +bool wallet_rpc_thirdparty_custody::generate(std::vector& events) const +{ + uint64_t ts = test_core_time::get_time(); + m_accounts.resize(TOTAL_ACCS_COUNT); + account_base& miner_acc = m_accounts[MINER_ACC_IDX]; miner_acc.generate(); miner_acc.set_createtime(ts); + account_base& alice_acc = m_accounts[ALICE_ACC_IDX]; alice_acc.generate(); alice_acc.set_createtime(ts); + + MAKE_GENESIS_BLOCK(events, blk_0, miner_acc, ts); + DO_CALLBACK(events, "configure_core"); // default configure_core callback will initialize core runtime config with m_hardforks + REWIND_BLOCKS_N_WITH_TIME(events, blk_0r, blk_0, miner_acc, CURRENCY_MINED_MONEY_UNLOCK_WINDOW + 3); + + DO_CALLBACK(events, "c1"); + + return true; +} +//------------------------------------------------------------------------------ + +#define TEST_TOKEN_NAME "TEST TOKEN" + +bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const std::vector& events) +{ + + bool r = false; + account_base alice_acc, bob_acc, carol_acc, custody_acc; + alice_acc.generate(); + bob_acc.generate(); + carol_acc.generate(); + custody_acc.generate(); + + std::shared_ptr miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX); + std::shared_ptr alice_wlt = init_playtime_test_wallet(events, c, alice_acc); + std::shared_ptr bob_wlt = init_playtime_test_wallet(events, c, bob_acc); + std::shared_ptr carol_wlt = init_playtime_test_wallet(events, c, carol_acc); + std::shared_ptr custody_wlt = init_playtime_test_wallet(events, c, custody_acc); + + r = mine_next_pow_blocks_in_playtime(alice_wlt->get_account().get_public_address(), c, 3); + r = mine_next_pow_blocks_in_playtime(bob_wlt->get_account().get_public_address(), c, 3); + r = mine_next_pow_blocks_in_playtime(carol_wlt->get_account().get_public_address(), c, 3); + //r = mine_next_pow_blocks_in_playtime(custody_wlt->get_account().get_public_address(), c, 3); + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + + // wallet RPC server + tools::wallet_rpc_server custody_wlt_rpc(custody_wlt); + + struct tools::wallet_public::COMMAND_ASSETS_DEPLOY::request req = AUTO_VAL_INIT(resp); + struct tools::wallet_public::COMMAND_ASSETS_DEPLOY::response resp = AUTO_VAL_INIT(resp); + + req.asset_descriptor.current_supply = 1000; + req.asset_descriptor.decimal_point = 1; + req.asset_descriptor.full_name = TEST_TOKEN_NAME; + req.asset_descriptor.hidden_supply = false; + + + r = invoke_text_json_for_rpc(custody_wlt_rpc, "get_recent_txs_and_info", req, resp); + CHECK_AND_ASSERT_MES(r, false, "failed to call"); + + + + + + + + + + r = test_payment_ids_generation(custody_wlt_rpc); + CHECK_AND_ASSERT_MES(r, false, "test_payment_ids_generation() failed "); + //====================================================================== + std::string alice_payment_id = gen_payment_id(custody_wlt_rpc); + std::string bob_payment_id = gen_payment_id(custody_wlt_rpc); + std::string carol_payment_id = gen_payment_id(custody_wlt_rpc); + + // generate payment id's for each wallet and deposit + custody_wlt->refresh(); + alice_wlt->refresh(); + bob_wlt->refresh(); + carol_wlt->refresh(); + +#define TRANSFER_AMOUNT COIN / 10 + + std::string alice_tx1 = transfer_(alice_wlt, get_integr_addr(custody_wlt_rpc, alice_payment_id), TRANSFER_AMOUNT); + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 1); + + std::string bob_tx1 = transfer_(bob_wlt, get_integr_addr(custody_wlt_rpc, bob_payment_id), TRANSFER_AMOUNT); + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 1); + + std::string bob_tx2 = transfer_(bob_wlt, get_integr_addr(custody_wlt_rpc, bob_payment_id), TRANSFER_AMOUNT); + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 1); + + std::string carol_tx1 = transfer_(carol_wlt, get_integr_addr(custody_wlt_rpc, carol_payment_id), TRANSFER_AMOUNT); + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 1); + + std::string carol_tx2 = transfer_(carol_wlt, get_integr_addr(custody_wlt_rpc, carol_payment_id), TRANSFER_AMOUNT); + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 1); + + std::string carol_tx3 = transfer_(carol_wlt, get_integr_addr(custody_wlt_rpc, carol_payment_id), TRANSFER_AMOUNT); + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 1); + + CHECK_AND_ASSERT_MES(alice_tx1.size() + && bob_tx1.size() + && bob_tx2.size() + && carol_tx1.size() + && carol_tx2.size() + && carol_tx3.size(), + false, "One of deposit transactions wan't created" + ); + + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); + + custody_wlt->refresh(); + pre_hf4_api::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::request req = AUTO_VAL_INIT(req); + req.update_provision_info = true; + req.count = 10; + + pre_hf4_api::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::response resp = AUTO_VAL_INIT(resp); + r = invoke_text_json_for_rpc(custody_wlt_rpc, "get_recent_txs_and_info", req, resp); + CHECK_AND_ASSERT_MES(r, false, "failed to call"); + +#define CHECK_RESPONSE_EQUAL(condition) CHECK_AND_ASSERT_MES((condition), false, "Failed check"); + + CHECK_RESPONSE_EQUAL(resp.pi.balance == 600000000000); + CHECK_RESPONSE_EQUAL(resp.pi.unlocked_balance == 600000000000); + CHECK_RESPONSE_EQUAL(resp.pi.transfers_count == 6); + CHECK_RESPONSE_EQUAL(resp.total_transfers == 6); + CHECK_RESPONSE_EQUAL(resp.transfers.size() == 6); + CHECK_RESPONSE_EQUAL(resp.transfers[0].comment == TRANSFER_COMMENT); + CHECK_RESPONSE_EQUAL(resp.transfers[0].amount == TRANSFER_AMOUNT); + CHECK_RESPONSE_EQUAL(resp.transfers[0].is_income == true); + CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[0].payment_id) == carol_payment_id); + CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[0].tx_hash) == carol_tx3); + + CHECK_RESPONSE_EQUAL(resp.transfers[1].comment == TRANSFER_COMMENT); + CHECK_RESPONSE_EQUAL(resp.transfers[1].amount == TRANSFER_AMOUNT); + CHECK_RESPONSE_EQUAL(resp.transfers[1].is_income == true); + CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[1].payment_id) == carol_payment_id); + CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[1].tx_hash) == carol_tx2); + + CHECK_RESPONSE_EQUAL(resp.transfers[2].comment == TRANSFER_COMMENT); + CHECK_RESPONSE_EQUAL(resp.transfers[2].amount == TRANSFER_AMOUNT); + CHECK_RESPONSE_EQUAL(resp.transfers[2].is_income == true); + CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[2].payment_id) == carol_payment_id); + CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[2].tx_hash) == carol_tx1); + + CHECK_RESPONSE_EQUAL(resp.transfers[3].comment == TRANSFER_COMMENT); + CHECK_RESPONSE_EQUAL(resp.transfers[3].amount == TRANSFER_AMOUNT); + CHECK_RESPONSE_EQUAL(resp.transfers[3].is_income == true); + CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[3].payment_id) == bob_payment_id); + CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[3].tx_hash) == bob_tx2); + + CHECK_RESPONSE_EQUAL(resp.transfers[4].comment == TRANSFER_COMMENT); + CHECK_RESPONSE_EQUAL(resp.transfers[4].amount == TRANSFER_AMOUNT); + CHECK_RESPONSE_EQUAL(resp.transfers[4].is_income == true); + CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[4].payment_id) == bob_payment_id); + CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[4].tx_hash) == bob_tx1); + + CHECK_RESPONSE_EQUAL(resp.transfers[5].comment == TRANSFER_COMMENT); + CHECK_RESPONSE_EQUAL(resp.transfers[5].amount == TRANSFER_AMOUNT); + CHECK_RESPONSE_EQUAL(resp.transfers[5].is_income == true); + CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[5].payment_id) == alice_payment_id); + CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[5].tx_hash) == alice_tx1); + + + req.count = 10; + req.offset = 2; + r = invoke_text_json_for_rpc(custody_wlt_rpc, "get_recent_txs_and_info", req, resp); + CHECK_AND_ASSERT_MES(r, false, "failed to call"); + + CHECK_RESPONSE_EQUAL(resp.pi.balance == 600000000000); + CHECK_RESPONSE_EQUAL(resp.pi.unlocked_balance == 600000000000); + CHECK_RESPONSE_EQUAL(resp.pi.transfers_count == 6); + CHECK_RESPONSE_EQUAL(resp.total_transfers == 6); + CHECK_RESPONSE_EQUAL(resp.transfers.size() == 4); + CHECK_RESPONSE_EQUAL(resp.transfers[0].comment == TRANSFER_COMMENT); + CHECK_RESPONSE_EQUAL(resp.transfers[0].amount == TRANSFER_AMOUNT); + CHECK_RESPONSE_EQUAL(resp.transfers[0].is_income == true); + CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[0].payment_id) == carol_payment_id); + CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[0].tx_hash) == carol_tx1); + + CHECK_RESPONSE_EQUAL(resp.transfers[1].comment == TRANSFER_COMMENT); + CHECK_RESPONSE_EQUAL(resp.transfers[1].amount == TRANSFER_AMOUNT); + CHECK_RESPONSE_EQUAL(resp.transfers[1].is_income == true); + CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[1].payment_id) == bob_payment_id); + CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[1].tx_hash) == bob_tx2); + + CHECK_RESPONSE_EQUAL(resp.transfers[2].comment == TRANSFER_COMMENT); + CHECK_RESPONSE_EQUAL(resp.transfers[2].amount == TRANSFER_AMOUNT); + CHECK_RESPONSE_EQUAL(resp.transfers[2].is_income == true); + CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[2].payment_id) == bob_payment_id); + CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[2].tx_hash) == bob_tx1); + + CHECK_RESPONSE_EQUAL(resp.transfers[3].comment == TRANSFER_COMMENT); + CHECK_RESPONSE_EQUAL(resp.transfers[3].amount == TRANSFER_AMOUNT); + CHECK_RESPONSE_EQUAL(resp.transfers[3].is_income == true); + CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[3].payment_id) == alice_payment_id); + CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[3].tx_hash) == alice_tx1); + + //getbalance + pre_hf4_api::COMMAND_RPC_GET_BALANCE::request gb_req = AUTO_VAL_INIT(gb_req); + pre_hf4_api::COMMAND_RPC_GET_BALANCE::response gb_resp = AUTO_VAL_INIT(gb_resp); + r = invoke_text_json_for_rpc(custody_wlt_rpc, "getbalance", gb_req, gb_resp); + CHECK_AND_ASSERT_MES(r, false, "failed to call"); + CHECK_RESPONSE_EQUAL(gb_resp.balance == 600000000000); + CHECK_RESPONSE_EQUAL(gb_resp.unlocked_balance == 600000000000); + + //get_wallet_info + pre_hf4_api::COMMAND_RPC_GET_WALLET_INFO::request gwi_req = AUTO_VAL_INIT(gwi_req); + pre_hf4_api::COMMAND_RPC_GET_WALLET_INFO::response gwi_resp = AUTO_VAL_INIT(gwi_resp); + r = invoke_text_json_for_rpc(custody_wlt_rpc, "get_wallet_info", gwi_req, gwi_resp); + CHECK_AND_ASSERT_MES(r, false, "failed to call"); + CHECK_RESPONSE_EQUAL(gwi_resp.current_height == 35); + CHECK_RESPONSE_EQUAL(gwi_resp.transfer_entries_count == 6); + CHECK_RESPONSE_EQUAL(gwi_resp.transfers_count == 6); + CHECK_RESPONSE_EQUAL(gwi_resp.address == custody_wlt->get_account().get_public_address_str()); + + + //search_for_transactions + pre_hf4_api::COMMAND_RPC_SEARCH_FOR_TRANSACTIONS::request st_req = AUTO_VAL_INIT(st_req); + st_req.filter_by_height = false; + st_req.in = true; + st_req.out = true; + st_req.pool = true; + st_req.tx_id = resp.transfers[1].tx_hash; //bob_tx2 + pre_hf4_api::COMMAND_RPC_SEARCH_FOR_TRANSACTIONS::response st_resp = AUTO_VAL_INIT(st_resp); + r = invoke_text_json_for_rpc(custody_wlt_rpc, "search_for_transactions", st_req, st_resp); + CHECK_AND_ASSERT_MES(r, false, "failed to call"); + //TODO: add more cases for search transaction in pool, in and out + + + CHECK_RESPONSE_EQUAL(st_resp.in.size() == 1); + CHECK_RESPONSE_EQUAL(st_resp.in.begin()->comment == TRANSFER_COMMENT); + CHECK_RESPONSE_EQUAL(st_resp.in.begin()->amount == TRANSFER_AMOUNT); + CHECK_RESPONSE_EQUAL(st_resp.in.begin()->is_income == true); + CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(st_resp.in.begin()->payment_id) == bob_payment_id); + CHECK_RESPONSE_EQUAL(boost::lexical_cast(st_resp.in.begin()->tx_hash) == bob_tx2); + + + //get_payments + pre_hf4_api::COMMAND_RPC_GET_PAYMENTS::request gps_req = AUTO_VAL_INIT(gps_req); + gps_req.payment_id = carol_payment_id; + pre_hf4_api::COMMAND_RPC_GET_PAYMENTS::response gps_resp = AUTO_VAL_INIT(gps_resp); + r = invoke_text_json_for_rpc(custody_wlt_rpc, "get_payments", gps_req, gps_resp); + CHECK_AND_ASSERT_MES(r, false, "failed to call"); + + { + //sort by block_height to have deterministic order + gps_resp.payments.sort([&](const pre_hf4_api::payment_details& a, const pre_hf4_api::payment_details& b) {return a.block_height < b.block_height; }); + + CHECK_RESPONSE_EQUAL(gps_resp.payments.size() == 3); + auto it = gps_resp.payments.begin(); + CHECK_RESPONSE_EQUAL(it->amount == 100000000000); + CHECK_RESPONSE_EQUAL(it->payment_id == carol_payment_id); + CHECK_RESPONSE_EQUAL(it->block_height == 23); + it++; + CHECK_RESPONSE_EQUAL(it->amount == 100000000000); + CHECK_RESPONSE_EQUAL(it->payment_id == carol_payment_id); + CHECK_RESPONSE_EQUAL(it->block_height == 24); + it++; + CHECK_RESPONSE_EQUAL(it->amount == 100000000000); + CHECK_RESPONSE_EQUAL(it->payment_id == carol_payment_id); + CHECK_RESPONSE_EQUAL(it->block_height == 25); + } + + + + //get_bulk_payments + pre_hf4_api::COMMAND_RPC_GET_BULK_PAYMENTS::request gbps_req = AUTO_VAL_INIT(gbps_req); + gbps_req.payment_ids.push_back(bob_payment_id); + gbps_req.payment_ids.push_back(alice_payment_id); + pre_hf4_api::COMMAND_RPC_GET_BULK_PAYMENTS::response gbps_resp = AUTO_VAL_INIT(gbps_resp); + r = invoke_text_json_for_rpc(custody_wlt_rpc, "get_bulk_payments", gbps_req, gbps_resp); + CHECK_AND_ASSERT_MES(r, false, "failed to call"); + + { + //sort by block_height to have deterministic order + gbps_resp.payments.sort([&](const pre_hf4_api::payment_details& a, const pre_hf4_api::payment_details& b) {return a.block_height < b.block_height; }); + + CHECK_RESPONSE_EQUAL(gbps_resp.payments.size() == 3); + auto it = gbps_resp.payments.begin(); + CHECK_RESPONSE_EQUAL(it->amount == 100000000000); + CHECK_RESPONSE_EQUAL(it->payment_id == alice_payment_id); + CHECK_RESPONSE_EQUAL(it->block_height == 20); + it++; + CHECK_RESPONSE_EQUAL(it->amount == 100000000000); + CHECK_RESPONSE_EQUAL(it->payment_id == bob_payment_id); + CHECK_RESPONSE_EQUAL(it->block_height == 21); + it++; + CHECK_RESPONSE_EQUAL(it->amount == 100000000000); + CHECK_RESPONSE_EQUAL(it->payment_id == bob_payment_id); + CHECK_RESPONSE_EQUAL(it->block_height == 22); + } + + return true; +} \ No newline at end of file diff --git a/tests/core_tests/wallet_rpc_tests.h b/tests/core_tests/wallet_rpc_tests.h index 3793ad78..ca6e3df8 100644 --- a/tests/core_tests/wallet_rpc_tests.h +++ b/tests/core_tests/wallet_rpc_tests.h @@ -55,3 +55,11 @@ struct wallet_true_rpc_pos_mining : public wallet_test bool generate(std::vector& events) const; bool c1(currency::core& c, size_t ev_index, const std::vector& events); }; + + +struct wallet_rpc_thirdparty_custody : public wallet_test +{ + wallet_rpc_exchange_suite(); + bool generate(std::vector& events) const; + bool c1(currency::core& c, size_t ev_index, const std::vector& events); +}; \ No newline at end of file From 64e2e53629abb0a1ee86285cb444dbdc67fda527 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Sat, 22 Mar 2025 14:38:57 +0400 Subject: [PATCH 2/6] test for RPC API COMMAND_ATTACH_ASSET_DESCRIPTOR (in work) --- src/wallet/wallet_rpc_server.cpp | 9 +- tests/core_tests/chaingen_main.cpp | 3 +- tests/core_tests/wallet_rpc_tests.cpp | 331 ++++++++------------------ tests/core_tests/wallet_rpc_tests.h | 2 +- 4 files changed, 110 insertions(+), 235 deletions(-) diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 92f2452f..8058ab4a 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1312,9 +1312,11 @@ namespace tools } } //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_asset_deploy(const wallet_public::COMMAND_ASSETS_DEPLOY::request& req, wallet_public::COMMAND_ASSETS_DEPLOY::response& res, epee::json_rpc::error& er, connection_context& cntx) + bool wallet_rpc_server::on_asset_deploy(const wallet_public::COMMAND_ASSETS_DEPLOY::request& req_, wallet_public::COMMAND_ASSETS_DEPLOY::response& res, epee::json_rpc::error& er, connection_context& cntx) { WALLET_RPC_BEGIN_TRY_ENTRY(); + //make local req so we can modify it (if needed) + wallet_public::COMMAND_ASSETS_DEPLOY::request req = req_; if (!currency::validate_asset_ticker_and_full_name(req.asset_descriptor)) { @@ -1323,6 +1325,11 @@ namespace tools return false; } + if (req.destinations.empty() && req.asset_descriptor.current_supply != 0) + { + req.destinations.push_back(tools::wallet_public::transfer_destination{ req.asset_descriptor.current_supply , w.get_wallet()->get_account().get_public_address_str(), currency::null_pkey }); + } + std::vector currency_destinations; rpc_destinations_to_currency_destinations(req.destinations, true, !req.do_not_split_destinations, currency_destinations); diff --git a/tests/core_tests/chaingen_main.cpp b/tests/core_tests/chaingen_main.cpp index 69002a99..ef348a15 100644 --- a/tests/core_tests/chaingen_main.cpp +++ b/tests/core_tests/chaingen_main.cpp @@ -1313,8 +1313,7 @@ int main(int argc, char* argv[]) GENERATE_AND_PLAY_HF(pos_fuse_test, "4-*"); GENERATE_AND_PLAY_HF(wallet_reorganize_and_trim_test, "4-*"); - - + GENERATE_AND_PLAY_HF(wallet_rpc_thirdparty_custody, "5-*"); GENERATE_AND_PLAY_HF(attachment_isolation_test, "4-*"); diff --git a/tests/core_tests/wallet_rpc_tests.cpp b/tests/core_tests/wallet_rpc_tests.cpp index 7376176b..4a6a73b7 100644 --- a/tests/core_tests/wallet_rpc_tests.cpp +++ b/tests/core_tests/wallet_rpc_tests.cpp @@ -950,6 +950,10 @@ bool wallet_rpc_thirdparty_custody::generate(std::vector& even //------------------------------------------------------------------------------ #define TEST_TOKEN_NAME "TEST TOKEN" +#define TEST_TOKEN_METAINFO "Metainfo" +#define TEST_TOKEN_TICKER "TT" +#define TEST_TOKEN_MAX_SUPPLY 2000 +#define TEST_TOKEN_CURRENT_SUPPLY 1000 bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const std::vector& events) { @@ -975,253 +979,118 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const // wallet RPC server - tools::wallet_rpc_server custody_wlt_rpc(custody_wlt); + tools::wallet_rpc_server miner_wlt_rpc(miner_wlt); - struct tools::wallet_public::COMMAND_ASSETS_DEPLOY::request req = AUTO_VAL_INIT(resp); + miner_wlt->refresh(); + struct tools::wallet_public::COMMAND_ASSETS_DEPLOY::request req = AUTO_VAL_INIT(req); struct tools::wallet_public::COMMAND_ASSETS_DEPLOY::response resp = AUTO_VAL_INIT(resp); - req.asset_descriptor.current_supply = 1000; req.asset_descriptor.decimal_point = 1; req.asset_descriptor.full_name = TEST_TOKEN_NAME; req.asset_descriptor.hidden_supply = false; + req.asset_descriptor.meta_info = TEST_TOKEN_METAINFO; + req.asset_descriptor.ticker = TEST_TOKEN_TICKER; + req.asset_descriptor.total_max_supply = TEST_TOKEN_MAX_SUPPLY; + req.asset_descriptor.current_supply = TEST_TOKEN_CURRENT_SUPPLY; - - r = invoke_text_json_for_rpc(custody_wlt_rpc, "get_recent_txs_and_info", req, resp); + r = invoke_text_json_for_rpc(miner_wlt_rpc, "deploy_asset", req, resp); CHECK_AND_ASSERT_MES(r, false, "failed to call"); + if (resp.new_asset_id == currency::null_pkey) + { + LOG_ERROR("Failed to deploy asset"); + return false; + } + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 3); + + // core RPC server + currency::t_currency_protocol_handler cprotocol(c, NULL); + nodetool::node_server > dummy_p2p(cprotocol); + bc_services::bc_offers_service dummy_bc(nullptr); + currency::core_rpc_server core_rpc(c, dummy_p2p, dummy_bc); + currency::COMMAND_RPC_GET_ASSET_INFO::request gai_req = AUTO_VAL_INIT(gai_req); + currency::COMMAND_RPC_GET_ASSET_INFO::response gai_resp = AUTO_VAL_INIT(gai_resp); + gai_req.asset_id = resp.new_asset_id; + r = invoke_text_json_for_rpc(core_rpc, "get_asset_info", gai_req, gai_resp); + CHECK_AND_ASSERT_MES(r, false, "failed to call"); + CHECK_AND_ASSERT_MES(gai_resp.status == API_RETURN_CODE_OK, false, "failed to call"); + CHECK_AND_ASSERT_MES(gai_resp.asset_descriptor.decimal_point == 1, false, "Wrong asset descriptor"); + CHECK_AND_ASSERT_MES(gai_resp.asset_descriptor.full_name == TEST_TOKEN_NAME, false, "Wrong asset descriptor"); + CHECK_AND_ASSERT_MES(gai_resp.asset_descriptor.hidden_supply == false, false, "Wrong asset descriptor"); + CHECK_AND_ASSERT_MES(gai_resp.asset_descriptor.meta_info == TEST_TOKEN_METAINFO, false, "Wrong asset descriptor"); + CHECK_AND_ASSERT_MES(gai_resp.asset_descriptor.ticker == TEST_TOKEN_TICKER, false, "Wrong asset descriptor"); + CHECK_AND_ASSERT_MES(gai_resp.asset_descriptor.total_max_supply == TEST_TOKEN_MAX_SUPPLY, false, "Wrong asset descriptor"); + CHECK_AND_ASSERT_MES(gai_resp.asset_descriptor.current_supply == TEST_TOKEN_CURRENT_SUPPLY, false, "Wrong asset descriptor"); - - - - - - r = test_payment_ids_generation(custody_wlt_rpc); - CHECK_AND_ASSERT_MES(r, false, "test_payment_ids_generation() failed "); - //====================================================================== - std::string alice_payment_id = gen_payment_id(custody_wlt_rpc); - std::string bob_payment_id = gen_payment_id(custody_wlt_rpc); - std::string carol_payment_id = gen_payment_id(custody_wlt_rpc); - - // generate payment id's for each wallet and deposit - custody_wlt->refresh(); alice_wlt->refresh(); + // wallet RPC server + tools::wallet_rpc_server alice_wlt_rpc(alice_wlt); + + tools::wallet_public::COMMAND_ATTACH_ASSET_DESCRIPTOR::request att_req = AUTO_VAL_INIT(att_req); + tools::wallet_public::COMMAND_ATTACH_ASSET_DESCRIPTOR::response att_resp = AUTO_VAL_INIT(att_resp); + att_req.asset_id = resp.new_asset_id; + att_req.do_attach = true; + r = invoke_text_json_for_rpc(alice_wlt_rpc, "get_asset_info", gai_req, gai_resp); + CHECK_AND_ASSERT_MES(r, false, "failed to call"); + CHECK_AND_ASSERT_MES(att_resp.status == API_RETURN_CODE_OK, false, "failed to call"); + + + //let's emit it, but transfer actually to Bob, by using Alice wallet as attache + tools::wallet_public::COMMAND_ASSETS_EMIT::request emm_req = AUTO_VAL_INIT(emm_req); + tools::wallet_public::COMMAND_ASSETS_EMIT::response emm_resp = AUTO_VAL_INIT(emm_resp); + + emm_req.asset_id = resp.new_asset_id; + emm_req.destinations.push_back(tools::wallet_public::transfer_destination{10, bob_wlt->get_account().get_public_address_str(), emm_req.asset_id }); + r = invoke_text_json_for_rpc(alice_wlt_rpc, "emit_asset", emm_req, emm_resp); + CHECK_AND_ASSERT_MES(r, false, "failed to call"); + if (!emm_resp.data_for_external_signing) + { + LOG_ERROR("Missing data_for_external_signing"); + return false; + } + + crypto::signature sig = AUTO_VAL_INIT(sig); + crypto::generate_signature(emm_resp.tx_id, miner_wlt->get_account().get_keys().spend_secret_key, sig); + // instant verification, just in case + r = crypto::check_signature(emm_resp.tx_id, miner_wlt->get_account().get_keys().account_address.spend_public_key, sig); + CHECK_AND_ASSERT_MES(r, false, "verify_eth_signature failed"); + + // + // send ETH signature alogn with all previous data to a wallet RPC call for final tx assembling and broadcasting + // + tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::request send_signed_req{}; + send_signed_req.unsigned_tx = emm_resp.data_for_external_signing->unsigned_tx; + send_signed_req.eth_sig = eth_sig; + send_signed_req.expected_tx_id = emm_resp.tx_id; + send_signed_req.finalized_tx = emm_resp.data_for_external_signing->finalized_tx; + send_signed_req.unlock_transfers_on_fail = true; + tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::response send_signed_resp{}; + + r = invoke_text_json_for_rpc(alice_wlt_rpc, "send_ext_signed_asset_tx", send_signed_req, send_signed_resp); + CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: "); + CHECK_AND_ASSERT_MES(send_signed_resp.status == API_RETURN_CODE_OK, false, "RPC send_ext_signed_asset_tx failed: "); + + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 3); + + + //check bob wallet + wallet_public::COMMAND_ASSETS_WHITELIST_ADD::request wtl_req = AUTO_VAL_INIT(wtl_req); + wallet_public::COMMAND_ASSETS_WHITELIST_ADD::request wtl_resp = AUTO_VAL_INIT(wtl_resp); + wtl_req.asset_id = resp.new_asset_id; + tools::wallet_rpc_server bob_wlt_rpc(bob_wlt); + r = invoke_text_json_for_rpc(bob_wlt_rpc, "assets_whitelist_add", send_signed_req, send_signed_resp); + CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: "); + CHECK_AND_ASSERT_MES(wtl_resp.status == API_RETURN_CODE_OK, false, "RPC status failed"); bob_wlt->refresh(); - carol_wlt->refresh(); -#define TRANSFER_AMOUNT COIN / 10 + wallet_public::COMMAND_RPC_GET_BALANCE::request balance_req = AUTO_VAL_INIT(balance_req); + wallet_public::COMMAND_RPC_GET_BALANCE::request balance_resp = AUTO_VAL_INIT(balance_resp); + r = invoke_text_json_for_rpc(bob_wlt_rpc, "getbalance", send_signed_req, send_signed_resp); + CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: "); + - std::string alice_tx1 = transfer_(alice_wlt, get_integr_addr(custody_wlt_rpc, alice_payment_id), TRANSFER_AMOUNT); - r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 1); - - std::string bob_tx1 = transfer_(bob_wlt, get_integr_addr(custody_wlt_rpc, bob_payment_id), TRANSFER_AMOUNT); - r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 1); - - std::string bob_tx2 = transfer_(bob_wlt, get_integr_addr(custody_wlt_rpc, bob_payment_id), TRANSFER_AMOUNT); - r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 1); - - std::string carol_tx1 = transfer_(carol_wlt, get_integr_addr(custody_wlt_rpc, carol_payment_id), TRANSFER_AMOUNT); - r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 1); - - std::string carol_tx2 = transfer_(carol_wlt, get_integr_addr(custody_wlt_rpc, carol_payment_id), TRANSFER_AMOUNT); - r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 1); - - std::string carol_tx3 = transfer_(carol_wlt, get_integr_addr(custody_wlt_rpc, carol_payment_id), TRANSFER_AMOUNT); - r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 1); - - CHECK_AND_ASSERT_MES(alice_tx1.size() - && bob_tx1.size() - && bob_tx2.size() - && carol_tx1.size() - && carol_tx2.size() - && carol_tx3.size(), - false, "One of deposit transactions wan't created" - ); - - r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, CURRENCY_MINED_MONEY_UNLOCK_WINDOW); - - custody_wlt->refresh(); - pre_hf4_api::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::request req = AUTO_VAL_INIT(req); - req.update_provision_info = true; - req.count = 10; - - pre_hf4_api::COMMAND_RPC_GET_RECENT_TXS_AND_INFO::response resp = AUTO_VAL_INIT(resp); - r = invoke_text_json_for_rpc(custody_wlt_rpc, "get_recent_txs_and_info", req, resp); - CHECK_AND_ASSERT_MES(r, false, "failed to call"); - -#define CHECK_RESPONSE_EQUAL(condition) CHECK_AND_ASSERT_MES((condition), false, "Failed check"); - - CHECK_RESPONSE_EQUAL(resp.pi.balance == 600000000000); - CHECK_RESPONSE_EQUAL(resp.pi.unlocked_balance == 600000000000); - CHECK_RESPONSE_EQUAL(resp.pi.transfers_count == 6); - CHECK_RESPONSE_EQUAL(resp.total_transfers == 6); - CHECK_RESPONSE_EQUAL(resp.transfers.size() == 6); - CHECK_RESPONSE_EQUAL(resp.transfers[0].comment == TRANSFER_COMMENT); - CHECK_RESPONSE_EQUAL(resp.transfers[0].amount == TRANSFER_AMOUNT); - CHECK_RESPONSE_EQUAL(resp.transfers[0].is_income == true); - CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[0].payment_id) == carol_payment_id); - CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[0].tx_hash) == carol_tx3); - - CHECK_RESPONSE_EQUAL(resp.transfers[1].comment == TRANSFER_COMMENT); - CHECK_RESPONSE_EQUAL(resp.transfers[1].amount == TRANSFER_AMOUNT); - CHECK_RESPONSE_EQUAL(resp.transfers[1].is_income == true); - CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[1].payment_id) == carol_payment_id); - CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[1].tx_hash) == carol_tx2); - - CHECK_RESPONSE_EQUAL(resp.transfers[2].comment == TRANSFER_COMMENT); - CHECK_RESPONSE_EQUAL(resp.transfers[2].amount == TRANSFER_AMOUNT); - CHECK_RESPONSE_EQUAL(resp.transfers[2].is_income == true); - CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[2].payment_id) == carol_payment_id); - CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[2].tx_hash) == carol_tx1); - - CHECK_RESPONSE_EQUAL(resp.transfers[3].comment == TRANSFER_COMMENT); - CHECK_RESPONSE_EQUAL(resp.transfers[3].amount == TRANSFER_AMOUNT); - CHECK_RESPONSE_EQUAL(resp.transfers[3].is_income == true); - CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[3].payment_id) == bob_payment_id); - CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[3].tx_hash) == bob_tx2); - - CHECK_RESPONSE_EQUAL(resp.transfers[4].comment == TRANSFER_COMMENT); - CHECK_RESPONSE_EQUAL(resp.transfers[4].amount == TRANSFER_AMOUNT); - CHECK_RESPONSE_EQUAL(resp.transfers[4].is_income == true); - CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[4].payment_id) == bob_payment_id); - CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[4].tx_hash) == bob_tx1); - - CHECK_RESPONSE_EQUAL(resp.transfers[5].comment == TRANSFER_COMMENT); - CHECK_RESPONSE_EQUAL(resp.transfers[5].amount == TRANSFER_AMOUNT); - CHECK_RESPONSE_EQUAL(resp.transfers[5].is_income == true); - CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[5].payment_id) == alice_payment_id); - CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[5].tx_hash) == alice_tx1); - - - req.count = 10; - req.offset = 2; - r = invoke_text_json_for_rpc(custody_wlt_rpc, "get_recent_txs_and_info", req, resp); - CHECK_AND_ASSERT_MES(r, false, "failed to call"); - - CHECK_RESPONSE_EQUAL(resp.pi.balance == 600000000000); - CHECK_RESPONSE_EQUAL(resp.pi.unlocked_balance == 600000000000); - CHECK_RESPONSE_EQUAL(resp.pi.transfers_count == 6); - CHECK_RESPONSE_EQUAL(resp.total_transfers == 6); - CHECK_RESPONSE_EQUAL(resp.transfers.size() == 4); - CHECK_RESPONSE_EQUAL(resp.transfers[0].comment == TRANSFER_COMMENT); - CHECK_RESPONSE_EQUAL(resp.transfers[0].amount == TRANSFER_AMOUNT); - CHECK_RESPONSE_EQUAL(resp.transfers[0].is_income == true); - CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[0].payment_id) == carol_payment_id); - CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[0].tx_hash) == carol_tx1); - - CHECK_RESPONSE_EQUAL(resp.transfers[1].comment == TRANSFER_COMMENT); - CHECK_RESPONSE_EQUAL(resp.transfers[1].amount == TRANSFER_AMOUNT); - CHECK_RESPONSE_EQUAL(resp.transfers[1].is_income == true); - CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[1].payment_id) == bob_payment_id); - CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[1].tx_hash) == bob_tx2); - - CHECK_RESPONSE_EQUAL(resp.transfers[2].comment == TRANSFER_COMMENT); - CHECK_RESPONSE_EQUAL(resp.transfers[2].amount == TRANSFER_AMOUNT); - CHECK_RESPONSE_EQUAL(resp.transfers[2].is_income == true); - CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[2].payment_id) == bob_payment_id); - CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[2].tx_hash) == bob_tx1); - - CHECK_RESPONSE_EQUAL(resp.transfers[3].comment == TRANSFER_COMMENT); - CHECK_RESPONSE_EQUAL(resp.transfers[3].amount == TRANSFER_AMOUNT); - CHECK_RESPONSE_EQUAL(resp.transfers[3].is_income == true); - CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(resp.transfers[3].payment_id) == alice_payment_id); - CHECK_RESPONSE_EQUAL(boost::lexical_cast(resp.transfers[3].tx_hash) == alice_tx1); - - //getbalance - pre_hf4_api::COMMAND_RPC_GET_BALANCE::request gb_req = AUTO_VAL_INIT(gb_req); - pre_hf4_api::COMMAND_RPC_GET_BALANCE::response gb_resp = AUTO_VAL_INIT(gb_resp); - r = invoke_text_json_for_rpc(custody_wlt_rpc, "getbalance", gb_req, gb_resp); - CHECK_AND_ASSERT_MES(r, false, "failed to call"); - CHECK_RESPONSE_EQUAL(gb_resp.balance == 600000000000); - CHECK_RESPONSE_EQUAL(gb_resp.unlocked_balance == 600000000000); - - //get_wallet_info - pre_hf4_api::COMMAND_RPC_GET_WALLET_INFO::request gwi_req = AUTO_VAL_INIT(gwi_req); - pre_hf4_api::COMMAND_RPC_GET_WALLET_INFO::response gwi_resp = AUTO_VAL_INIT(gwi_resp); - r = invoke_text_json_for_rpc(custody_wlt_rpc, "get_wallet_info", gwi_req, gwi_resp); - CHECK_AND_ASSERT_MES(r, false, "failed to call"); - CHECK_RESPONSE_EQUAL(gwi_resp.current_height == 35); - CHECK_RESPONSE_EQUAL(gwi_resp.transfer_entries_count == 6); - CHECK_RESPONSE_EQUAL(gwi_resp.transfers_count == 6); - CHECK_RESPONSE_EQUAL(gwi_resp.address == custody_wlt->get_account().get_public_address_str()); - - - //search_for_transactions - pre_hf4_api::COMMAND_RPC_SEARCH_FOR_TRANSACTIONS::request st_req = AUTO_VAL_INIT(st_req); - st_req.filter_by_height = false; - st_req.in = true; - st_req.out = true; - st_req.pool = true; - st_req.tx_id = resp.transfers[1].tx_hash; //bob_tx2 - pre_hf4_api::COMMAND_RPC_SEARCH_FOR_TRANSACTIONS::response st_resp = AUTO_VAL_INIT(st_resp); - r = invoke_text_json_for_rpc(custody_wlt_rpc, "search_for_transactions", st_req, st_resp); - CHECK_AND_ASSERT_MES(r, false, "failed to call"); - //TODO: add more cases for search transaction in pool, in and out - - - CHECK_RESPONSE_EQUAL(st_resp.in.size() == 1); - CHECK_RESPONSE_EQUAL(st_resp.in.begin()->comment == TRANSFER_COMMENT); - CHECK_RESPONSE_EQUAL(st_resp.in.begin()->amount == TRANSFER_AMOUNT); - CHECK_RESPONSE_EQUAL(st_resp.in.begin()->is_income == true); - CHECK_RESPONSE_EQUAL(epee::string_tools::buff_to_hex_nodelimer(st_resp.in.begin()->payment_id) == bob_payment_id); - CHECK_RESPONSE_EQUAL(boost::lexical_cast(st_resp.in.begin()->tx_hash) == bob_tx2); - - - //get_payments - pre_hf4_api::COMMAND_RPC_GET_PAYMENTS::request gps_req = AUTO_VAL_INIT(gps_req); - gps_req.payment_id = carol_payment_id; - pre_hf4_api::COMMAND_RPC_GET_PAYMENTS::response gps_resp = AUTO_VAL_INIT(gps_resp); - r = invoke_text_json_for_rpc(custody_wlt_rpc, "get_payments", gps_req, gps_resp); - CHECK_AND_ASSERT_MES(r, false, "failed to call"); - - { - //sort by block_height to have deterministic order - gps_resp.payments.sort([&](const pre_hf4_api::payment_details& a, const pre_hf4_api::payment_details& b) {return a.block_height < b.block_height; }); - - CHECK_RESPONSE_EQUAL(gps_resp.payments.size() == 3); - auto it = gps_resp.payments.begin(); - CHECK_RESPONSE_EQUAL(it->amount == 100000000000); - CHECK_RESPONSE_EQUAL(it->payment_id == carol_payment_id); - CHECK_RESPONSE_EQUAL(it->block_height == 23); - it++; - CHECK_RESPONSE_EQUAL(it->amount == 100000000000); - CHECK_RESPONSE_EQUAL(it->payment_id == carol_payment_id); - CHECK_RESPONSE_EQUAL(it->block_height == 24); - it++; - CHECK_RESPONSE_EQUAL(it->amount == 100000000000); - CHECK_RESPONSE_EQUAL(it->payment_id == carol_payment_id); - CHECK_RESPONSE_EQUAL(it->block_height == 25); - } - - - - //get_bulk_payments - pre_hf4_api::COMMAND_RPC_GET_BULK_PAYMENTS::request gbps_req = AUTO_VAL_INIT(gbps_req); - gbps_req.payment_ids.push_back(bob_payment_id); - gbps_req.payment_ids.push_back(alice_payment_id); - pre_hf4_api::COMMAND_RPC_GET_BULK_PAYMENTS::response gbps_resp = AUTO_VAL_INIT(gbps_resp); - r = invoke_text_json_for_rpc(custody_wlt_rpc, "get_bulk_payments", gbps_req, gbps_resp); - CHECK_AND_ASSERT_MES(r, false, "failed to call"); - - { - //sort by block_height to have deterministic order - gbps_resp.payments.sort([&](const pre_hf4_api::payment_details& a, const pre_hf4_api::payment_details& b) {return a.block_height < b.block_height; }); - - CHECK_RESPONSE_EQUAL(gbps_resp.payments.size() == 3); - auto it = gbps_resp.payments.begin(); - CHECK_RESPONSE_EQUAL(it->amount == 100000000000); - CHECK_RESPONSE_EQUAL(it->payment_id == alice_payment_id); - CHECK_RESPONSE_EQUAL(it->block_height == 20); - it++; - CHECK_RESPONSE_EQUAL(it->amount == 100000000000); - CHECK_RESPONSE_EQUAL(it->payment_id == bob_payment_id); - CHECK_RESPONSE_EQUAL(it->block_height == 21); - it++; - CHECK_RESPONSE_EQUAL(it->amount == 100000000000); - CHECK_RESPONSE_EQUAL(it->payment_id == bob_payment_id); - CHECK_RESPONSE_EQUAL(it->block_height == 22); - } return true; } \ No newline at end of file diff --git a/tests/core_tests/wallet_rpc_tests.h b/tests/core_tests/wallet_rpc_tests.h index ca6e3df8..513dafd4 100644 --- a/tests/core_tests/wallet_rpc_tests.h +++ b/tests/core_tests/wallet_rpc_tests.h @@ -59,7 +59,7 @@ struct wallet_true_rpc_pos_mining : public wallet_test struct wallet_rpc_thirdparty_custody : public wallet_test { - wallet_rpc_exchange_suite(); + wallet_rpc_thirdparty_custody(); bool generate(std::vector& events) const; bool c1(currency::core& c, size_t ev_index, const std::vector& events); }; \ No newline at end of file From be129f7a8788ed3b3e377202acd43a7ced5480bc Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Mon, 24 Mar 2025 02:36:54 +0400 Subject: [PATCH 3/6] tests are work in progress --- src/currency_core/currency_basic.h | 1 + src/currency_core/currency_format_utils.h | 24 ++++++++++++++ .../currency_protocol_handler.inl | 2 +- src/wallet/wallet2.cpp | 31 ++++++++++++++----- src/wallet/wallet2.h | 2 ++ src/wallet/wallet_public_structs_defs.h | 6 ++-- src/wallet/wallet_rpc_server.cpp | 9 +++++- tests/core_tests/wallet_rpc_tests.cpp | 30 +++++++++++------- 8 files changed, 82 insertions(+), 23 deletions(-) diff --git a/src/currency_core/currency_basic.h b/src/currency_core/currency_basic.h index a844f612..ef5a8c19 100644 --- a/src/currency_core/currency_basic.h +++ b/src/currency_core/currency_basic.h @@ -56,6 +56,7 @@ namespace currency const static crypto::signature null_sig = AUTO_VAL_INIT(null_sig); const static crypto::key_derivation null_derivation = AUTO_VAL_INIT(null_derivation); const static crypto::eth_public_key null_eth_public_key = AUTO_VAL_INIT(null_eth_public_key); + const static crypto::eth_signature null_eth_signature = AUTO_VAL_INIT(null_eth_signature); const static crypto::hash gdefault_genesis = epee::string_tools::hex_to_pod("CC608F59F8080E2FBFE3C8C80EB6E6A953D47CF2D6AEBD345BADA3A1CAB99852"); diff --git a/src/currency_core/currency_format_utils.h b/src/currency_core/currency_format_utils.h index b3cfbeed..23ccadc7 100644 --- a/src/currency_core/currency_format_utils.h +++ b/src/currency_core/currency_format_utils.h @@ -1262,4 +1262,28 @@ namespace currency return true; } + + + //@#@ + //TEMPORARY CODE, TODO: talk to @val and re-do it + //---------------------------------------------------------------------------------------------------- + inline void schnor_old_to_schnor_new(const crypto::signature& sig, crypto::generic_schnorr_sig& sig_new) + { + static_assert(sizeof(sig.c) == sizeof(sig_new.c)); + static_assert(sizeof(sig.r) == sizeof(sig_new.y)); + std::memcpy(&sig_new.c, &sig.c, sizeof(sig_new.c)); + std::memcpy(&sig_new.y, &sig.r, sizeof(sig_new.y)); + + } + //---------------------------------------------------------------------------------------------------- + inline void schnor_new_to_schnor_old(const crypto::generic_schnorr_sig& sig_new, crypto::signature& sig) + { + static_assert(sizeof(sig.c) == sizeof(sig_new.c)); + static_assert(sizeof(sig.r) == sizeof(sig_new.y)); + std::memcpy(&sig.c, &sig_new.c, sizeof(sig_new.c)); + std::memcpy(&sig.r, &sig_new.y, sizeof(sig_new.y)); + + } + + } // namespace currency diff --git a/src/currency_protocol/currency_protocol_handler.inl b/src/currency_protocol/currency_protocol_handler.inl index 86abbfd1..0ab38fc1 100644 --- a/src/currency_protocol/currency_protocol_handler.inl +++ b/src/currency_protocol/currency_protocol_handler.inl @@ -559,7 +559,7 @@ namespace currency if(req_it == context.m_priv.m_requested_objects.end()) { LOG_ERROR_CCONTEXT("sent wrong NOTIFY_RESPONSE_GET_OBJECTS: block with id=" << epst::pod_to_hex(get_blob_hash(block_entry.block)) - << " wasn't requested, dropping connection"); + << " wasn't requested, block_blob: " << epst::buff_to_hex_nodelimer(block_entry.block) << " dropping connection"); m_p2p->drop_connection(context); return 1; } diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 43b34ca9..a1e7e6e6 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -4402,14 +4402,8 @@ bool wallet2::get_utxo_distribution(std::map& distribution) return false; } //---------------------------------------------------------------------------------------------------- -void wallet2::submit_externally_signed_asset_tx(const finalized_tx& ft, const crypto::eth_signature& eth_sig, bool unlock_transfers_on_fail, currency::transaction& result_tx, bool& transfers_unlocked) +void wallet2::submit_externally_signed_asset_tx(const currency::finalized_tx& ft, const currency::transaction& tx, bool unlock_transfers_on_fail, currency::transaction& result_tx, bool& transfers_unlocked) { - transaction tx = ft.tx; - - currency::asset_operation_ownership_proof_eth aoop_eth{}; - aoop_eth.eth_sig = eth_sig; - tx.proofs.push_back(std::move(aoop_eth)); - // foolproof WLT_THROW_IF_FALSE_WALLET_CMN_ERR_EX(ft.ftp.spend_pub_key == m_account.get_keys().account_address.spend_public_key, "The given tx was created in a different wallet, keys missmatch, tx hash: " << ft.tx_id); @@ -4433,6 +4427,29 @@ void wallet2::submit_externally_signed_asset_tx(const finalized_tx& ft, const cr add_sent_tx_detailed_info(tx, ft.ftp.attachments, ft.ftp.prepared_destinations, ft.ftp.selected_transfers); print_tx_sent_message(tx, "from submit_externally_signed_asset_tx", true, get_tx_fee(tx)); + result_tx = tx; +} + +//---------------------------------------------------------------------------------------------------- +void wallet2::submit_externally_signed_asset_tx(const currency::finalized_tx& ft, const crypto::signature& sig, bool unlock_transfers_on_fail, currency::transaction& result_tx, bool& transfers_unlocked) +{ + currency::transaction tx = ft.tx; + + currency::asset_operation_ownership_proof aoop_eth{}; + currency::schnor_old_to_schnor_new(sig, aoop_eth.gss); + //aoop_eth.gss = sig; + tx.proofs.push_back(std::move(aoop_eth)); + submit_externally_signed_asset_tx(ft, tx, unlock_transfers_on_fail, result_tx, transfers_unlocked); +} +//---------------------------------------------------------------------------------------------------- +void wallet2::submit_externally_signed_asset_tx(const currency::finalized_tx& ft, const crypto::eth_signature& eth_sig, bool unlock_transfers_on_fail, currency::transaction& result_tx, bool& transfers_unlocked) +{ + transaction tx = ft.tx; + + currency::asset_operation_ownership_proof_eth aoop_eth{}; + aoop_eth.eth_sig = eth_sig; + tx.proofs.push_back(std::move(aoop_eth)); + submit_externally_signed_asset_tx(ft, tx, unlock_transfers_on_fail, result_tx, transfers_unlocked); } //---------------------------------------------------------------------------------------------------- bool wallet2::attach_asset_descriptor(const wallet_public::COMMAND_ATTACH_ASSET_DESCRIPTOR::request& req, wallet_public::COMMAND_ATTACH_ASSET_DESCRIPTOR::response& resp) diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index a2c92152..28fab783 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -616,6 +616,8 @@ namespace tools void submit_transfer(const std::string& signed_tx_blob, currency::transaction& tx); void submit_transfer_files(const std::string& signed_tx_file, currency::transaction& tx); void submit_externally_signed_asset_tx(const currency::finalized_tx& ft, const crypto::eth_signature& eth_sig, bool unlock_transfers_on_fail, currency::transaction& result_tx, bool& transfers_unlocked); + void submit_externally_signed_asset_tx(const currency::finalized_tx& ft, const crypto::signature& sig, bool unlock_transfers_on_fail, currency::transaction& result_tx, bool& transfers_unlocked); + void submit_externally_signed_asset_tx(const currency::finalized_tx& ft, const currency::transaction& tx, bool unlock_transfers_on_fail, currency::transaction& result_tx, bool& transfers_unlocked); void sweep_below(size_t fake_outs_count, const currency::account_public_address& destination_addr, uint64_t threshold_amount, const currency::payment_id_t& payment_id, uint64_t fee, size_t& outs_total, uint64_t& amount_total, size_t& outs_swept, uint64_t& amount_swept, currency::transaction* p_result_tx = nullptr, std::string* p_filename_or_unsigned_tx_blob_str = nullptr); diff --git a/src/wallet/wallet_public_structs_defs.h b/src/wallet/wallet_public_structs_defs.h index 893200cf..32cc2f34 100644 --- a/src/wallet/wallet_public_structs_defs.h +++ b/src/wallet/wallet_public_structs_defs.h @@ -2053,14 +2053,16 @@ namespace wallet_public { currency::blobdata finalized_tx; currency::blobdata unsigned_tx; - crypto::eth_signature eth_sig; //TODO: add value initialization here + crypto::eth_signature eth_sig = currency::null_eth_signature; + crypto::signature regular_sig = currency::null_sig; crypto::hash expected_tx_id = currency::null_hash; bool unlock_transfers_on_fail = false; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_BLOB_AS_BASE64_STRING(finalized_tx)DOC_DSCR("Base64-encoded finalized_tx data structure, which was received from emit_asset call.") DOC_EXMP("ewogICJ2ZXJzaW9uIjogMSwgC....iAgInZpbiI6IFsgewogICAgIC") DOC_END KV_SERIALIZE_BLOB_AS_BASE64_STRING(unsigned_tx) DOC_DSCR("Base64-encoded unsigned transaction blob, which was received from emit_asset call.") DOC_EXMP("083737bcfd826a973f74bb56a52b4fa562e6579ccaadd2697463498a66de4f1760b2cd40f11c3a00a7a80000") DOC_END - KV_SERIALIZE_POD_AS_HEX_STRING(eth_sig) DOC_DSCR("HEX-encoded ETH signature (64 bytes)") DOC_EXMP("674bb56a5b4fa562e679ccacc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6add697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(eth_sig) DOC_DSCR("HEX-encoded ETH signature (64 bytes), used only if regular_sig is empty") DOC_EXMP("674bb56a5b4fa562e679ccacc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6add697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(regular_sig) DOC_DSCR("HEX-encoded regular signature (64 bytes)") DOC_EXMP("674bb56a5b4fa562e679ccacc4e69455e63f4a581257382191de6856c2156630b3fba0db4bdd73ffcfb36b6add697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END KV_SERIALIZE_POD_AS_HEX_STRING(expected_tx_id) DOC_DSCR("The expected transaction id. Tx won't be sent if the calculated one doesn't match this one. Consider using 'verified_tx_id' returned by 'decrypt_tx_details' call.") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END KV_SERIALIZE(unlock_transfers_on_fail) DOC_DSCR("If true, all locked wallet transfers, corresponding to the transaction, will be unlocked on sending failure. False by default.") DOC_EXMP(false) DOC_END END_KV_SERIALIZE_MAP() diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 8058ab4a..3a60c61f 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1438,7 +1438,14 @@ namespace tools try { currency::transaction result_tx{}; - w.get_wallet()->submit_externally_signed_asset_tx(ft, req.eth_sig, req.unlock_transfers_on_fail, result_tx, res.transfers_were_unlocked); + if (req.regular_sig != currency::null_sig) + { + w.get_wallet()->submit_externally_signed_asset_tx(ft, req.regular_sig, req.unlock_transfers_on_fail, result_tx, res.transfers_were_unlocked); + } + else + { + w.get_wallet()->submit_externally_signed_asset_tx(ft, req.eth_sig, req.unlock_transfers_on_fail, result_tx, res.transfers_were_unlocked); + } } catch(std::exception& e) { diff --git a/tests/core_tests/wallet_rpc_tests.cpp b/tests/core_tests/wallet_rpc_tests.cpp index 4a6a73b7..50c07141 100644 --- a/tests/core_tests/wallet_rpc_tests.cpp +++ b/tests/core_tests/wallet_rpc_tests.cpp @@ -1032,7 +1032,7 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const tools::wallet_public::COMMAND_ATTACH_ASSET_DESCRIPTOR::response att_resp = AUTO_VAL_INIT(att_resp); att_req.asset_id = resp.new_asset_id; att_req.do_attach = true; - r = invoke_text_json_for_rpc(alice_wlt_rpc, "get_asset_info", gai_req, gai_resp); + r = invoke_text_json_for_rpc(alice_wlt_rpc, "attach_asset_descriptor", att_req, att_resp); CHECK_AND_ASSERT_MES(r, false, "failed to call"); CHECK_AND_ASSERT_MES(att_resp.status == API_RETURN_CODE_OK, false, "failed to call"); @@ -1052,17 +1052,23 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const } crypto::signature sig = AUTO_VAL_INIT(sig); - crypto::generate_signature(emm_resp.tx_id, miner_wlt->get_account().get_keys().spend_secret_key, sig); + crypto::generic_schnorr_sig sig_sch = AUTO_VAL_INIT(sig_sch); + r = crypto::generate_schnorr_sig(emm_resp.tx_id, miner_wlt->get_account().get_keys().spend_secret_key, sig_sch); + CHECK_AND_ASSERT_MES(r, false, "gailed to generate schnorr signature"); + + //crypto::generate_signature(emm_resp.tx_id, miner_wlt->get_account().get_keys().account_address.spend_public_key, miner_wlt->get_account().get_keys().spend_secret_key, sig); // instant verification, just in case - r = crypto::check_signature(emm_resp.tx_id, miner_wlt->get_account().get_keys().account_address.spend_public_key, sig); - CHECK_AND_ASSERT_MES(r, false, "verify_eth_signature failed"); + r = crypto::verify_schnorr_sig(emm_resp.tx_id, miner_wlt->get_account().get_keys().account_address.spend_public_key, sig_sch); + CHECK_AND_ASSERT_MES(r, false, "verify_schnorr_sig failed"); + currency::schnor_new_to_schnor_old(sig_sch, sig); + // // send ETH signature alogn with all previous data to a wallet RPC call for final tx assembling and broadcasting // - tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::request send_signed_req{}; + tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::request send_signed_req = AUTO_VAL_INIT(send_signed_req); send_signed_req.unsigned_tx = emm_resp.data_for_external_signing->unsigned_tx; - send_signed_req.eth_sig = eth_sig; + send_signed_req.regular_sig = sig; send_signed_req.expected_tx_id = emm_resp.tx_id; send_signed_req.finalized_tx = emm_resp.data_for_external_signing->finalized_tx; send_signed_req.unlock_transfers_on_fail = true; @@ -1076,18 +1082,18 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const //check bob wallet - wallet_public::COMMAND_ASSETS_WHITELIST_ADD::request wtl_req = AUTO_VAL_INIT(wtl_req); - wallet_public::COMMAND_ASSETS_WHITELIST_ADD::request wtl_resp = AUTO_VAL_INIT(wtl_resp); + tools::wallet_public::COMMAND_ASSETS_WHITELIST_ADD::request wtl_req = AUTO_VAL_INIT(wtl_req); + tools::wallet_public::COMMAND_ASSETS_WHITELIST_ADD::response wtl_resp = AUTO_VAL_INIT(wtl_resp); wtl_req.asset_id = resp.new_asset_id; tools::wallet_rpc_server bob_wlt_rpc(bob_wlt); - r = invoke_text_json_for_rpc(bob_wlt_rpc, "assets_whitelist_add", send_signed_req, send_signed_resp); + r = invoke_text_json_for_rpc(bob_wlt_rpc, "assets_whitelist_add", wtl_req, wtl_resp); CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: "); CHECK_AND_ASSERT_MES(wtl_resp.status == API_RETURN_CODE_OK, false, "RPC status failed"); bob_wlt->refresh(); - wallet_public::COMMAND_RPC_GET_BALANCE::request balance_req = AUTO_VAL_INIT(balance_req); - wallet_public::COMMAND_RPC_GET_BALANCE::request balance_resp = AUTO_VAL_INIT(balance_resp); - r = invoke_text_json_for_rpc(bob_wlt_rpc, "getbalance", send_signed_req, send_signed_resp); + tools::wallet_public::COMMAND_RPC_GET_BALANCE::request balance_req = AUTO_VAL_INIT(balance_req); + tools::wallet_public::COMMAND_RPC_GET_BALANCE::response balance_resp = AUTO_VAL_INIT(balance_resp); + r = invoke_text_json_for_rpc(bob_wlt_rpc, "getbalance", balance_req, balance_resp); CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: "); From 938050df0fab4bba3a67c568811115fd923ae5db Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Sat, 29 Mar 2025 00:14:47 +0400 Subject: [PATCH 4/6] multiple fixes for wallet rpc api on_asset_send_ext_signed_tx on_attach_asset_descriptor and on_transfer_asset_ownership --- src/wallet/wallet2.cpp | 6 +- src/wallet/wallet_public_structs_defs.h | 8 +- src/wallet/wallet_rpc_server.cpp | 8 +- tests/core_tests/wallet_rpc_tests.cpp | 158 ++++++++++++++++++++---- 4 files changed, 143 insertions(+), 37 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index a1e7e6e6..b87627be 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -5682,11 +5682,12 @@ void wallet2::update_asset(const crypto::public_key& asset_id, const currency::a ctp.tx_meaning_for_logs = "asset update"; bool send_to_network = true; - if (last_adb.owner_eth_pub_key.has_value()) + if (own_asset_entry_it->second.thirdparty_custody || last_adb.owner_eth_pub_key.has_value()) { send_to_network = false; ctp.additional_transfer_flags_to_mark = WALLET_TRANSFER_DETAIL_FLAG_ASSET_OP_RESERVATION; ctp.tx_meaning_for_logs = "asset eth update"; + ctp.ado_sign_thirdparty = true; } this->transfer(ctp, ft, send_to_network, nullptr); @@ -5724,11 +5725,12 @@ void wallet2::transfer_asset_ownership(const crypto::public_key& asset_id, const ctp.tx_meaning_for_logs = "transfer asset ownership"; bool send_to_network = true; - if (last_adb.owner_eth_pub_key.has_value()) + if (own_asset_entry_it->second.thirdparty_custody || last_adb.owner_eth_pub_key.has_value()) { send_to_network = false; ctp.additional_transfer_flags_to_mark = WALLET_TRANSFER_DETAIL_FLAG_ASSET_OP_RESERVATION; ctp.tx_meaning_for_logs = "transfer asset eth ownership"; + ctp.ado_sign_thirdparty = true; } this->transfer(ctp, ft, send_to_network, nullptr); diff --git a/src/wallet/wallet_public_structs_defs.h b/src/wallet/wallet_public_structs_defs.h index 32cc2f34..9e5e352e 100644 --- a/src/wallet/wallet_public_structs_defs.h +++ b/src/wallet/wallet_public_structs_defs.h @@ -2113,13 +2113,13 @@ namespace wallet_public struct request { crypto::public_key asset_id; - crypto::eth_public_key owner_eth_pub_key = currency::null_eth_public_key; // note: the size is 33 bytes (if present) - crypto::public_key owner = currency::null_pkey; // regular Zano Ed25519 public key + crypto::eth_public_key new_owner_eth_pub_key = currency::null_eth_public_key; // note: the size is 33 bytes (if present) + crypto::public_key new_owner = currency::null_pkey; // regular Zano Ed25519 public key BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE_POD_AS_HEX_STRING(asset_id) DOC_DSCR("Own asset id, that would be transfered to someone else") DOC_EXMP("40fa6db923728b38962718c61b4dc3af1acaa1967479c73703e260dc3609c58d") DOC_END - KV_SERIALIZE_POD_AS_HEX_STRING(owner) DOC_DSCR("Public key of the new owner(default Ed25519 public key, 32 bytes)") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END - KV_SERIALIZE_POD_AS_HEX_STRING(owner_eth_pub_key) DOC_DSCR("Public key of the new owner(ECDSA public key, 33 bytes) Used only if 'owner' field is empty") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a84d") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(new_owner) DOC_DSCR("Public key of the new owner(default Ed25519 public key, 32 bytes)") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a8") DOC_END + KV_SERIALIZE_POD_AS_HEX_STRING(new_owner_eth_pub_key) DOC_DSCR("Public key of the new owner(ECDSA public key, 33 bytes) Used only if 'owner' field is empty") DOC_EXMP("f74bb56a5b4fa562e679ccaadd697463498a66de4f1760b2cd40f11c3a00a7a84d") DOC_END END_KV_SERIALIZE_MAP() }; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 3a60c61f..424d52d8 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -1470,13 +1470,13 @@ namespace tools { WALLET_RPC_BEGIN_TRY_ENTRY(); currency::asset_owner_pub_key_v new_owner_v; - if (req.owner != currency::null_pkey) + if (req.new_owner!= currency::null_pkey) { - new_owner_v = req.owner; + new_owner_v = req.new_owner; } - else if(req.owner_eth_pub_key != currency::null_eth_public_key) + else if(req.new_owner_eth_pub_key != currency::null_eth_public_key) { - new_owner_v = req.owner_eth_pub_key; + new_owner_v = req.new_owner_eth_pub_key; }else { res.status = API_RETURN_CODE_BAD_ARG_INVALID_ADDRESS; diff --git a/tests/core_tests/wallet_rpc_tests.cpp b/tests/core_tests/wallet_rpc_tests.cpp index 50c07141..1f0ee6c8 100644 --- a/tests/core_tests/wallet_rpc_tests.cpp +++ b/tests/core_tests/wallet_rpc_tests.cpp @@ -955,6 +955,64 @@ bool wallet_rpc_thirdparty_custody::generate(std::vector& even #define TEST_TOKEN_MAX_SUPPLY 2000 #define TEST_TOKEN_CURRENT_SUPPLY 1000 + + +typedef boost::variant secret_key_v; +typedef boost::variant public_key_v; + + +template +bool sign_signature_with_keys(const t_response& rsp_with_data, tools::wallet_rpc_server& attached_wallet_rpc, const secret_key_v& signer_v, const public_key_v& verifier) +{ + bool r = false; + tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::request send_signed_req = AUTO_VAL_INIT(send_signed_req); + if (signer_v.type() == typeid(crypto::secret_key)) + { + //schnor sig + const crypto::secret_key& signer = boost::get(signer_v); + crypto::signature sig = AUTO_VAL_INIT(sig); + crypto::generic_schnorr_sig sig_sch = AUTO_VAL_INIT(sig_sch); + r = crypto::generate_schnorr_sig(rsp_with_data.tx_id, signer, sig_sch); + CHECK_AND_ASSERT_MES(r, false, "gailed to generate schnorr signature"); + + //crypto::generate_signature(emm_resp.tx_id, miner_wlt->get_account().get_keys().account_address.spend_public_key, miner_wlt->get_account().get_keys().spend_secret_key, sig); + // instant verification, just in case + r = crypto::verify_schnorr_sig(rsp_with_data.tx_id, boost::get(verifier), sig_sch); + CHECK_AND_ASSERT_MES(r, false, "verify_schnorr_sig failed"); + currency::schnor_new_to_schnor_old(sig_sch, sig); + + send_signed_req.regular_sig = sig; + } + else if (signer_v.type() == typeid(crypto::eth_secret_key)) + { + ////ecdsa sig + const crypto::eth_secret_key& signer = boost::get(signer_v); + r = crypto::generate_eth_signature(rsp_with_data.tx_id, signer, send_signed_req.eth_sig); + CHECK_AND_ASSERT_MES(r, false, "gailed to generate schnorr signature"); + + r = crypto::verify_eth_signature(rsp_with_data.tx_id, boost::get(verifier), send_signed_req.eth_sig); + CHECK_AND_ASSERT_MES(r, false, "verify_eth_signature failed"); + + } + else + { + return false; + } + + send_signed_req.unsigned_tx = rsp_with_data.data_for_external_signing->unsigned_tx; + send_signed_req.expected_tx_id = rsp_with_data.tx_id; + send_signed_req.finalized_tx = rsp_with_data.data_for_external_signing->finalized_tx; + send_signed_req.unlock_transfers_on_fail = true; + tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::response send_signed_resp{}; + + r = invoke_text_json_for_rpc(attached_wallet_rpc, "send_ext_signed_asset_tx", send_signed_req, send_signed_resp); + CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: "); + CHECK_AND_ASSERT_MES(send_signed_resp.status == API_RETURN_CODE_OK, false, "RPC send_ext_signed_asset_tx failed: "); + return true; +} + + + bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const std::vector& events) { @@ -1040,9 +1098,10 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const //let's emit it, but transfer actually to Bob, by using Alice wallet as attache tools::wallet_public::COMMAND_ASSETS_EMIT::request emm_req = AUTO_VAL_INIT(emm_req); tools::wallet_public::COMMAND_ASSETS_EMIT::response emm_resp = AUTO_VAL_INIT(emm_resp); +#define COINS_TO_TRANSFER 10 emm_req.asset_id = resp.new_asset_id; - emm_req.destinations.push_back(tools::wallet_public::transfer_destination{10, bob_wlt->get_account().get_public_address_str(), emm_req.asset_id }); + emm_req.destinations.push_back(tools::wallet_public::transfer_destination{ COINS_TO_TRANSFER, bob_wlt->get_account().get_public_address_str(), emm_req.asset_id }); r = invoke_text_json_for_rpc(alice_wlt_rpc, "emit_asset", emm_req, emm_resp); CHECK_AND_ASSERT_MES(r, false, "failed to call"); if (!emm_resp.data_for_external_signing) @@ -1051,33 +1110,9 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const return false; } - crypto::signature sig = AUTO_VAL_INIT(sig); - crypto::generic_schnorr_sig sig_sch = AUTO_VAL_INIT(sig_sch); - r = crypto::generate_schnorr_sig(emm_resp.tx_id, miner_wlt->get_account().get_keys().spend_secret_key, sig_sch); - CHECK_AND_ASSERT_MES(r, false, "gailed to generate schnorr signature"); + r = sign_signature_with_keys(emm_resp, alice_wlt_rpc, miner_wlt->get_account().get_keys().spend_secret_key, miner_wlt->get_account().get_keys().account_address.spend_public_key); + CHECK_AND_ASSERT_MES(r, false, "failed to call sign_signature_with_keys"); - //crypto::generate_signature(emm_resp.tx_id, miner_wlt->get_account().get_keys().account_address.spend_public_key, miner_wlt->get_account().get_keys().spend_secret_key, sig); - // instant verification, just in case - r = crypto::verify_schnorr_sig(emm_resp.tx_id, miner_wlt->get_account().get_keys().account_address.spend_public_key, sig_sch); - CHECK_AND_ASSERT_MES(r, false, "verify_schnorr_sig failed"); - currency::schnor_new_to_schnor_old(sig_sch, sig); - - - // - // send ETH signature alogn with all previous data to a wallet RPC call for final tx assembling and broadcasting - // - tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::request send_signed_req = AUTO_VAL_INIT(send_signed_req); - send_signed_req.unsigned_tx = emm_resp.data_for_external_signing->unsigned_tx; - send_signed_req.regular_sig = sig; - send_signed_req.expected_tx_id = emm_resp.tx_id; - send_signed_req.finalized_tx = emm_resp.data_for_external_signing->finalized_tx; - send_signed_req.unlock_transfers_on_fail = true; - tools::wallet_public::COMMAND_ASSET_SEND_EXT_SIGNED_TX::response send_signed_resp{}; - - r = invoke_text_json_for_rpc(alice_wlt_rpc, "send_ext_signed_asset_tx", send_signed_req, send_signed_resp); - CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: "); - CHECK_AND_ASSERT_MES(send_signed_resp.status == API_RETURN_CODE_OK, false, "RPC send_ext_signed_asset_tx failed: "); - r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 3); @@ -1097,6 +1132,75 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: "); + bool found_asset = false; + for (auto& bal : balance_resp.balances) + { + if (bal.asset_info.asset_id == resp.new_asset_id) + { + found_asset = true; + CHECK_AND_ASSERT_MES(bal.total == COINS_TO_TRANSFER, false, "Amount is unexpected"); + } + } + CHECK_AND_ASSERT_MES(found_asset, false, "Asset not found "); + + + + //transfer ownership of the asset to new address + //let's change the owner to ecdsa + crypto::eth_secret_key eth_sk_2{}; + crypto::eth_public_key eth_pk_2{}; + r = crypto::generate_eth_key_pair(eth_sk_2, eth_pk_2); + CHECK_AND_ASSERT_MES(r, false, "generate_eth_key_pair failed"); + + tools::wallet_public::COMMAND_TRANSFER_ASSET_OWNERSHIP::request req_own = AUTO_VAL_INIT(req_own); + tools::wallet_public::COMMAND_TRANSFER_ASSET_OWNERSHIP::response res_own = AUTO_VAL_INIT(res_own); + req_own.asset_id = resp.new_asset_id; + req_own.new_owner_eth_pub_key = eth_pk_2; + + + r = invoke_text_json_for_rpc(alice_wlt_rpc, "transfer_asset_ownership", req_own, res_own); + CHECK_AND_ASSERT_MES(r, false, "failed to call"); + if (!res_own.data_for_external_signing) + { + LOG_ERROR("Missing data_for_external_signing"); + return false; + } + + r = sign_signature_with_keys(res_own, alice_wlt_rpc, miner_wlt->get_account().get_keys().spend_secret_key, miner_wlt->get_account().get_keys().account_address.spend_public_key); + CHECK_AND_ASSERT_MES(r, false, "failed to call sign_signature_with_keys"); + + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 3); + + + //now make another tx and see of ownership got changed + emm_resp = AUTO_VAL_INIT(emm_resp); + r = invoke_text_json_for_rpc(alice_wlt_rpc, "emit_asset", emm_req, emm_resp); + CHECK_AND_ASSERT_MES(r, false, "failed to call"); + if (!emm_resp.data_for_external_signing) + { + LOG_ERROR("Missing data_for_external_signing"); + return false; + } + + r = sign_signature_with_keys(emm_resp, alice_wlt_rpc, eth_sk_2, eth_pk_2); + CHECK_AND_ASSERT_MES(r, false, "failed to call sign_signature_with_keys"); + + r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 3); + + r = invoke_text_json_for_rpc(bob_wlt_rpc, "getbalance", balance_req, balance_resp); + CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: "); + + found_asset = false; + for (auto& bal : balance_resp.balances) + { + if (bal.asset_info.asset_id == resp.new_asset_id) + { + found_asset = true; + CHECK_AND_ASSERT_MES(bal.total == COINS_TO_TRANSFER, false, "Amount is unexpected"); + } + } + CHECK_AND_ASSERT_MES(found_asset, false, "Asset not found "); + return true; } \ No newline at end of file From b48f23ab54c62c1ab8f6a81c01e7aa4a31a5362d Mon Sep 17 00:00:00 2001 From: sowle Date: Sat, 29 Mar 2025 12:55:43 +0100 Subject: [PATCH 5/6] wallet: fixed clearing owner of other type in transfer_asset_ownership() --- src/wallet/wallet2.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index b87627be..0da62144 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -5703,7 +5703,7 @@ void wallet2::update_asset(const crypto::public_key& asset_id, const currency::a void wallet2::transfer_asset_ownership(const crypto::public_key& asset_id, const currency::asset_owner_pub_key_v& new_owner_v, currency::finalized_tx& ft) { auto own_asset_entry_it = m_own_asset_descriptors.find(asset_id); - CHECK_AND_ASSERT_THROW_MES(own_asset_entry_it != m_own_asset_descriptors.end(), "Failed find asset_id " << asset_id << " in own assets list"); + CHECK_AND_ASSERT_THROW_MES(own_asset_entry_it != m_own_asset_descriptors.end(), "Couldn't find asset_id " << asset_id << " in own assets list"); currency::asset_descriptor_base last_adb{}; bool r = this->daemon_get_asset_info(asset_id, last_adb); CHECK_AND_ASSERT_THROW_MES(r, "Failed to get asset info from daemon"); @@ -5716,9 +5716,17 @@ void wallet2::transfer_asset_ownership(const crypto::public_key& asset_id, const asset_update_info.opt_asset_id = asset_id; if (new_owner_v.type() == typeid(crypto::public_key)) - asset_update_info.opt_descriptor->owner = boost::get(new_owner_v); + { + const crypto::public_key new_owner_pk = boost::get(new_owner_v); + asset_update_info.opt_descriptor->owner = new_owner_pk; + asset_update_info.opt_descriptor->owner_eth_pub_key = boost::none; + } else - asset_update_info.opt_descriptor->owner_eth_pub_key = boost::get(new_owner_v); + { + const crypto::eth_public_key new_owner_eth_pk = boost::get(new_owner_v); + asset_update_info.opt_descriptor->owner = currency::null_pkey; + asset_update_info.opt_descriptor->owner_eth_pub_key = new_owner_eth_pk; + } construct_tx_param ctp = get_default_construct_tx_param(); ctp.extra.push_back(asset_update_info); From ace2b4dbf5e1cb1f25e3f0e9d74560b0d54080e2 Mon Sep 17 00:00:00 2001 From: cryptozoidberg Date: Sat, 29 Mar 2025 23:39:36 +0400 Subject: [PATCH 6/6] tests for rpc transfer ownership done --- tests/core_tests/wallet_rpc_tests.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/core_tests/wallet_rpc_tests.cpp b/tests/core_tests/wallet_rpc_tests.cpp index 1f0ee6c8..88805e22 100644 --- a/tests/core_tests/wallet_rpc_tests.cpp +++ b/tests/core_tests/wallet_rpc_tests.cpp @@ -1186,7 +1186,8 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const CHECK_AND_ASSERT_MES(r, false, "failed to call sign_signature_with_keys"); r = mine_next_pow_blocks_in_playtime(miner_wlt->get_account().get_public_address(), c, 3); - + + bob_wlt->refresh(); r = invoke_text_json_for_rpc(bob_wlt_rpc, "getbalance", balance_req, balance_resp); CHECK_AND_ASSERT_MES(r, false, "RPC send_ext_signed_asset_tx failed: "); @@ -1196,7 +1197,7 @@ bool wallet_rpc_thirdparty_custody::c1(currency::core& c, size_t ev_index, const if (bal.asset_info.asset_id == resp.new_asset_id) { found_asset = true; - CHECK_AND_ASSERT_MES(bal.total == COINS_TO_TRANSFER, false, "Amount is unexpected"); + CHECK_AND_ASSERT_MES(bal.total == 2 * COINS_TO_TRANSFER, false, "Amount is unexpected"); } } CHECK_AND_ASSERT_MES(found_asset, false, "Asset not found ");