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