1
0
Fork 0
forked from lthn/blockchain

RPC extended for ownership transfer and attachment to wallet

This commit is contained in:
cryptozoidberg 2025-03-21 18:09:27 +04:00
parent 334a90bbe0
commit 89e70c74cd
No known key found for this signature in database
GPG key ID: 2E10CC61CAC8F36D
8 changed files with 482 additions and 19 deletions

View file

@ -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<crypto::hash>("CC608F59F8080E2FBFE3C8C80EB6E6A953D47CF2D6AEBD345BADA3A1CAB99852");

View file

@ -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<currency::asset_descriptor_base&>(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

View file

@ -694,6 +694,7 @@ namespace tools
size_t scan_for_collisions(std::unordered_map<crypto::key_image, std::list<size_t> >& key_images);
size_t fix_collisions();
size_t scan_for_transaction_entries(const crypto::hash& tx_id, const crypto::key_image& ki, std::list<transfer_details>& 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<expiration_entry_info>& 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 ------------------------------------------------

View file

@ -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_asset_signing_tx> 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

View file

@ -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<std::string>& 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<std::string>& 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<std::string>& 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();

View file

@ -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);

View file

@ -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<test_event_entry>& 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<test_event_entry>& 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<test_event_entry>& 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<tools::wallet2> miner_wlt = init_playtime_test_wallet(events, c, MINER_ACC_IDX);
std::shared_ptr<tools::wallet2> alice_wlt = init_playtime_test_wallet(events, c, alice_acc);
std::shared_ptr<tools::wallet2> bob_wlt = init_playtime_test_wallet(events, c, bob_acc);
std::shared_ptr<tools::wallet2> carol_wlt = init_playtime_test_wallet(events, c, carol_acc);
std::shared_ptr<tools::wallet2> 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<std::string>(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<std::string>(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<std::string>(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<std::string>(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<std::string>(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<std::string>(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<std::string>(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<std::string>(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<std::string>(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<std::string>(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<std::string>(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;
}

View file

@ -55,3 +55,11 @@ struct wallet_true_rpc_pos_mining : public wallet_test
bool generate(std::vector<test_event_entry>& events) const;
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};
struct wallet_rpc_thirdparty_custody : public wallet_test
{
wallet_rpc_exchange_suite();
bool generate(std::vector<test_event_entry>& events) const;
bool c1(currency::core& c, size_t ev_index, const std::vector<test_event_entry>& events);
};