From d433a696e527a01c1cbef48495652335140f0bb2 Mon Sep 17 00:00:00 2001 From: Thomas Winget Date: Tue, 17 Jun 2014 18:15:21 -0400 Subject: wallet RPC converted to use new transaction semantics wallet RPC now uses wallet2::create_transactions and wallet2::commit_tx instead of wallet2::transfer. This made it possible to add the RPC call /transfer_split, which will split transactions automatically if they are too large. The old call to /transfer will return an error stating to use /transfer_split if multiple transactions are needed to fulfill the request. --- src/wallet/wallet2.cpp | 156 +++++++++++++++++++- src/wallet/wallet2.h | 4 +- src/wallet/wallet_rpc_server.cpp | 94 ++++++++++-- src/wallet/wallet_rpc_server.h | 5 +- src/wallet/wallet_rpc_server_commands_defs.h | 211 +++++++++++++++++++++++++++ src/wallet/wallet_rpc_server_commans_defs.h | 182 ----------------------- 6 files changed, 453 insertions(+), 199 deletions(-) create mode 100644 src/wallet/wallet_rpc_server_commands_defs.h delete mode 100644 src/wallet/wallet_rpc_server_commans_defs.h (limited to 'src/wallet') diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 7dfbc7f7f..5b284c619 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -43,6 +43,9 @@ void do_prepare_file_names(const std::string& file_path, std::string& keys_file, namespace tools { +// for now, limit to 30 attempts. TODO: discuss a good number to limit to. +const size_t MAX_SPLIT_ATTEMPTS = 30; + //---------------------------------------------------------------------------------------------------- void wallet2::init(const std::string& daemon_address, uint64_t upper_transaction_size_limit) { @@ -697,6 +700,56 @@ void wallet2::transfer(const std::vector& dsts pending_tx ptx; transfer(dsts, fake_outputs_count, unlock_time, fee, extra, tx, ptx); } + +namespace { +// split_amounts(vector dsts, size_t num_splits) +// +// split amount for each dst in dsts into num_splits parts +// and make num_splits new vector instances to hold these new amounts +std::vector> split_amounts( + std::vector dsts, size_t num_splits) +{ + std::vector> retVal; + + if (num_splits <= 1) + { + retVal.push_back(dsts); + return retVal; + } + + // for each split required + for (size_t i=0; i < num_splits; i++) + { + std::vector new_dsts; + + // for each destination + for (size_t j=0; j < dsts.size(); j++) + { + cryptonote::tx_destination_entry de; + uint64_t amount; + + amount = dsts[j].amount; + amount = amount / num_splits; + + // if last split, add remainder + if (i + 1 == num_splits) + { + amount += dsts[j].amount % num_splits; + } + + de.addr = dsts[j].addr; + de.amount = amount; + + new_dsts.push_back(de); + } + + retVal.push_back(new_dsts); + } + + return retVal; +} +} // anonymous namespace + //---------------------------------------------------------------------------------------------------- // take a pending tx and actually send it to the daemon void wallet2::commit_tx(pending_tx& ptx) @@ -718,10 +771,105 @@ void wallet2::commit_tx(pending_tx& ptx) it->m_spent = true; LOG_PRINT_L0("Transaction successfully sent. <" << get_transaction_hash(ptx.tx) << ">" << ENDL - << "Commission: " << print_money(ptx.fee+ptx.dust) << " (dust: " << print_money(ptx.dust) << ")" << ENDL - << "Balance: " << print_money(balance()) << ENDL - << "Unlocked: " << print_money(unlocked_balance()) << ENDL - << "Please, wait for confirmation for your balance to be unlocked."); + << "Commission: " << print_money(ptx.fee+ptx.dust) << " (dust: " << print_money(ptx.dust) << ")" << ENDL + << "Balance: " << print_money(balance()) << ENDL + << "Unlocked: " << print_money(unlocked_balance()) << ENDL + << "Please, wait for confirmation for your balance to be unlocked."); +} + +void wallet2::commit_tx(std::vector& ptx_vector) +{ + for (auto & ptx : ptx_vector) + { + commit_tx(ptx); + } } +//---------------------------------------------------------------------------------------------------- +// separated the call(s) to wallet2::transfer into their own function +// +// this function will make multiple calls to wallet2::transfer if multiple +// transactions will be required +std::vector wallet2::create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector extra) +{ + + // failsafe split attempt counter + size_t attempt_count = 0; + + for(attempt_count = 1; ;attempt_count++) + { + auto split_values = split_amounts(dsts, attempt_count); + + // Throw if split_amounts comes back with a vector of size different than it should + if (split_values.size() != attempt_count) + { + throw std::runtime_error("Splitting transactions returned a number of potential tx not equal to what was requested"); + } + + std::vector ptx_vector; + try + { + // for each new destination vector (i.e. for each new tx) + for (auto & dst_vector : split_values) + { + cryptonote::transaction tx; + pending_tx ptx; + transfer(dst_vector, fake_outs_count, unlock_time, fee, extra, tx, ptx); + ptx_vector.push_back(ptx); + + // mark transfers to be used as "spent" + BOOST_FOREACH(transfer_container::iterator it, ptx.selected_transfers) + it->m_spent = true; + } + + // if we made it this far, we've selected our transactions. committing them will mark them spent, + // so this is a failsafe in case they don't go through + // unmark pending tx transfers as spent + for (auto & ptx : ptx_vector) + { + // mark transfers to be used as not spent + BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers) + it2->m_spent = false; + + } + + // if we made it this far, we're OK to actually send the transactions + return ptx_vector; + + } + // only catch this here, other exceptions need to pass through to the calling function + catch (const tools::error::tx_too_big& e) + { + + // unmark pending tx transfers as spent + for (auto & ptx : ptx_vector) + { + // mark transfers to be used as not spent + BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers) + it2->m_spent = false; + + } + + if (attempt_count >= MAX_SPLIT_ATTEMPTS) + { + throw; + } + } + catch (...) + { + // in case of some other exception, make sure any tx in queue are marked unspent again + + // unmark pending tx transfers as spent + for (auto & ptx : ptx_vector) + { + // mark transfers to be used as not spent + BOOST_FOREACH(transfer_container::iterator it2, ptx.selected_transfers) + it2->m_spent = false; + + } + + throw; + } + } +} } diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index bbedff961..1f5ae7062 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -138,7 +138,9 @@ namespace tools void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, T destination_split_strategy, const tx_dust_policy& dust_policy, cryptonote::transaction& tx, pending_tx& ptx); void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra); void transfer(const std::vector& dsts, size_t fake_outputs_count, uint64_t unlock_time, uint64_t fee, const std::vector& extra, cryptonote::transaction& tx, pending_tx& ptx); - void commit_tx(pending_tx& ptx); + void commit_tx(pending_tx& ptx_vector); + void commit_tx(std::vector& ptx_vector); + std::vector create_transactions(std::vector dsts, const size_t fake_outs_count, const uint64_t unlock_time, const uint64_t fee, const std::vector extra); bool check_connection(); void get_transfers(wallet2::transfer_container& incoming_transfers) const; void get_payments(const crypto::hash& payment_id, std::list& payments) const; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 95c71fc68..ec31d4625 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -10,6 +10,7 @@ using namespace epee; #include "common/command_line.h" #include "cryptonote_core/cryptonote_format_utils.h" #include "cryptonote_core/account.h" +#include "wallet_rpc_server_commands_defs.h" #include "misc_language.h" #include "string_tools.h" #include "crypto/hash.h" @@ -85,12 +86,11 @@ namespace tools } return true; } + //------------------------------------------------------------------------------------------------------------------------------ - bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx) + bool wallet_rpc_server::validate_transfer(const std::list destinations, const std::string payment_id, std::vector& dsts, std::vector extra, epee::json_rpc::error& er) { - - std::vector dsts; - for (auto it = req.destinations.begin(); it != req.destinations.end(); it++) + for (auto it = destinations.begin(); it != destinations.end(); it++) { cryptonote::tx_destination_entry de; if(!get_account_address_from_str(de.addr, it->address)) @@ -103,11 +103,11 @@ namespace tools dsts.push_back(de); } - std::vector extra; - if (!req.payment_id.empty()) { + if (!payment_id.empty()) + { /* Just to clarify */ - const std::string& payment_id_str = req.payment_id; + const std::string& payment_id_str = payment_id; crypto::hash payment_id; /* Parse payment ID */ @@ -128,13 +128,85 @@ namespace tools } } + return true; + } + + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + + std::vector dsts; + std::vector extra; + + // validate the transfer requested and populate dsts & extra + if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er)) + { + return false; + } + + try + { + std::vector ptx_vector = m_wallet.create_transactions(dsts, req.mixin, req.unlock_time, req.fee, extra); + + // reject proposed transactions if there are more than one. see on_transfer_split below. + if (ptx_vector.size() != 1) + { + er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; + er.message = "Transaction would be too large. try /transfer_split."; + return false; + } + + m_wallet.commit_tx(ptx_vector); + + // populate response with tx hash + res.tx_hash = boost::lexical_cast(cryptonote::get_transaction_hash(ptx_vector.back().tx)); + return true; + } + catch (const tools::error::daemon_busy& e) + { + er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; + er.message = e.what(); + return false; + } + catch (const std::exception& e) + { + er.code = WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR; + er.message = e.what(); + return false; + } + catch (...) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; + return false; + } + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er, connection_context& cntx) + { + + std::vector dsts; + std::vector extra; + + // validate the transfer requested and populate dsts & extra; RPC_TRANSFER::request and RPC_TRANSFER_SPLIT::request are identical types. + if (!validate_transfer(req.destinations, req.payment_id, dsts, extra, er)) + { + return false; + } try { - cryptonote::transaction tx; - wallet2::pending_tx ptx; - m_wallet.transfer(dsts, req.mixin, req.unlock_time, req.fee, extra, tx, ptx); - res.tx_hash = boost::lexical_cast(cryptonote::get_transaction_hash(tx)); + std::vector ptx_vector = m_wallet.create_transactions(dsts, req.mixin, req.unlock_time, req.fee, extra); + + m_wallet.commit_tx(ptx_vector); + + // populate response with tx hashes + for (auto & ptx : ptx_vector) + { + res.tx_hash_list.push_back(boost::lexical_cast(cryptonote::get_transaction_hash(ptx.tx))); + } + return true; } catch (const tools::error::daemon_busy& e) diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index b8373d27d..f9893566a 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -7,7 +7,7 @@ #include #include #include "net/http_server_impl_base.h" -#include "wallet_rpc_server_commans_defs.h" +#include "wallet_rpc_server_commands_defs.h" #include "wallet2.h" #include "common/command_line.h" namespace tools @@ -38,6 +38,7 @@ namespace tools MAP_JON_RPC_WE("getbalance", on_getbalance, wallet_rpc::COMMAND_RPC_GET_BALANCE) MAP_JON_RPC_WE("getaddress", on_getaddress, wallet_rpc::COMMAND_RPC_GET_ADDRESS) MAP_JON_RPC_WE("transfer", on_transfer, wallet_rpc::COMMAND_RPC_TRANSFER) + MAP_JON_RPC_WE("transfer_split", on_transfer_split, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT) MAP_JON_RPC_WE("store", on_store, wallet_rpc::COMMAND_RPC_STORE) MAP_JON_RPC_WE("get_payments", on_get_payments, wallet_rpc::COMMAND_RPC_GET_PAYMENTS) MAP_JON_RPC_WE("incoming_transfers", on_incoming_transfers, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS) @@ -47,7 +48,9 @@ namespace tools //json_rpc bool on_getbalance(const wallet_rpc::COMMAND_RPC_GET_BALANCE::request& req, wallet_rpc::COMMAND_RPC_GET_BALANCE::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_getaddress(const wallet_rpc::COMMAND_RPC_GET_ADDRESS::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool validate_transfer(const std::list destinations, const std::string payment_id, std::vector& dsts, std::vector extra, epee::json_rpc::error& er); bool on_transfer(const wallet_rpc::COMMAND_RPC_TRANSFER::request& req, wallet_rpc::COMMAND_RPC_TRANSFER::response& res, epee::json_rpc::error& er, connection_context& cntx); + bool on_transfer_split(const wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::request& req, wallet_rpc::COMMAND_RPC_TRANSFER_SPLIT::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_store(const wallet_rpc::COMMAND_RPC_STORE::request& req, wallet_rpc::COMMAND_RPC_STORE::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_get_payments(const wallet_rpc::COMMAND_RPC_GET_PAYMENTS::request& req, wallet_rpc::COMMAND_RPC_GET_PAYMENTS::response& res, epee::json_rpc::error& er, connection_context& cntx); bool on_incoming_transfers(const wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_INCOMING_TRANSFERS::response& res, epee::json_rpc::error& er, connection_context& cntx); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h new file mode 100644 index 000000000..33130da06 --- /dev/null +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -0,0 +1,211 @@ +// Copyright (c) 2012-2013 The Cryptonote developers +// Distributed under the MIT/X11 software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +#pragma once +#include "cryptonote_protocol/cryptonote_protocol_defs.h" +#include "cryptonote_core/cryptonote_basic.h" +#include "crypto/hash.h" +#include "wallet_rpc_server_error_codes.h" +namespace tools +{ +namespace wallet_rpc +{ +#define WALLET_RPC_STATUS_OK "OK" +#define WALLET_RPC_STATUS_BUSY "BUSY" + + struct COMMAND_RPC_GET_BALANCE + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + uint64_t balance; + uint64_t unlocked_balance; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(balance) + KV_SERIALIZE(unlocked_balance) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GET_ADDRESS + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string address; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(address) + END_KV_SERIALIZE_MAP() + }; + }; + + struct transfer_destination + { + uint64_t amount; + std::string address; + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE(address) + END_KV_SERIALIZE_MAP() + }; + + struct COMMAND_RPC_TRANSFER + { + struct request + { + std::list destinations; + uint64_t fee; + uint64_t mixin; + uint64_t unlock_time; + std::string payment_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(destinations) + KV_SERIALIZE(fee) + KV_SERIALIZE(mixin) + KV_SERIALIZE(unlock_time) + KV_SERIALIZE(payment_id) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string tx_hash; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_hash) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_TRANSFER_SPLIT + { + struct request + { + std::list destinations; + uint64_t fee; + uint64_t mixin; + uint64_t unlock_time; + std::string payment_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(destinations) + KV_SERIALIZE(fee) + KV_SERIALIZE(mixin) + KV_SERIALIZE(unlock_time) + KV_SERIALIZE(payment_id) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::list tx_hash_list; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_hash_list) + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_STORE + { + struct request + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + + struct payment_details + { + std::string tx_hash; + uint64_t amount; + uint64_t block_height; + uint64_t unlock_time; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(tx_hash) + KV_SERIALIZE(amount) + KV_SERIALIZE(block_height) + KV_SERIALIZE(unlock_time) + END_KV_SERIALIZE_MAP() + }; + + struct COMMAND_RPC_GET_PAYMENTS + { + struct request + { + std::string payment_id; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(payment_id) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::list payments; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(payments) + END_KV_SERIALIZE_MAP() + }; + }; + + struct transfer_details + { + uint64_t amount; + bool spent; + uint64_t global_index; + std::string tx_hash; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(amount) + KV_SERIALIZE(spent) + KV_SERIALIZE(global_index) + KV_SERIALIZE(tx_hash) + END_KV_SERIALIZE_MAP() + }; + + struct COMMAND_RPC_INCOMING_TRANSFERS + { + struct request + { + std::string transfer_type; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(transfer_type) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::list transfers; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(transfers) + END_KV_SERIALIZE_MAP() + }; + }; +} +} + diff --git a/src/wallet/wallet_rpc_server_commans_defs.h b/src/wallet/wallet_rpc_server_commans_defs.h deleted file mode 100644 index 7ffbcfc18..000000000 --- a/src/wallet/wallet_rpc_server_commans_defs.h +++ /dev/null @@ -1,182 +0,0 @@ -// Copyright (c) 2012-2013 The Cryptonote developers -// Distributed under the MIT/X11 software license, see the accompanying -// file COPYING or http://www.opensource.org/licenses/mit-license.php. - -#pragma once -#include "cryptonote_protocol/cryptonote_protocol_defs.h" -#include "cryptonote_core/cryptonote_basic.h" -#include "crypto/hash.h" -#include "wallet_rpc_server_error_codes.h" -namespace tools -{ -namespace wallet_rpc -{ -#define WALLET_RPC_STATUS_OK "OK" -#define WALLET_RPC_STATUS_BUSY "BUSY" - - struct COMMAND_RPC_GET_BALANCE - { - struct request - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - - struct response - { - uint64_t balance; - uint64_t unlocked_balance; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(balance) - KV_SERIALIZE(unlocked_balance) - END_KV_SERIALIZE_MAP() - }; - }; - - struct COMMAND_RPC_GET_ADDRESS - { - struct request - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - - struct response - { - std::string address; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(address) - END_KV_SERIALIZE_MAP() - }; - }; - - struct trnsfer_destination - { - uint64_t amount; - std::string address; - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(address) - END_KV_SERIALIZE_MAP() - }; - - struct COMMAND_RPC_TRANSFER - { - struct request - { - std::list destinations; - uint64_t fee; - uint64_t mixin; - uint64_t unlock_time; - std::string payment_id; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(destinations) - KV_SERIALIZE(fee) - KV_SERIALIZE(mixin) - KV_SERIALIZE(unlock_time) - KV_SERIALIZE(payment_id) - END_KV_SERIALIZE_MAP() - }; - - struct response - { - std::string tx_hash; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_hash) - END_KV_SERIALIZE_MAP() - }; - }; - - struct COMMAND_RPC_STORE - { - struct request - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - - struct response - { - BEGIN_KV_SERIALIZE_MAP() - END_KV_SERIALIZE_MAP() - }; - }; - - struct payment_details - { - std::string tx_hash; - uint64_t amount; - uint64_t block_height; - uint64_t unlock_time; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(tx_hash) - KV_SERIALIZE(amount) - KV_SERIALIZE(block_height) - KV_SERIALIZE(unlock_time) - END_KV_SERIALIZE_MAP() - }; - - struct COMMAND_RPC_GET_PAYMENTS - { - struct request - { - std::string payment_id; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(payment_id) - END_KV_SERIALIZE_MAP() - }; - - struct response - { - std::list payments; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(payments) - END_KV_SERIALIZE_MAP() - }; - }; - - struct transfer_details - { - uint64_t amount; - bool spent; - uint64_t global_index; - std::string tx_hash; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(amount) - KV_SERIALIZE(spent) - KV_SERIALIZE(global_index) - KV_SERIALIZE(tx_hash) - END_KV_SERIALIZE_MAP() - }; - - struct COMMAND_RPC_INCOMING_TRANSFERS - { - struct request - { - std::string transfer_type; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(transfer_type) - END_KV_SERIALIZE_MAP() - }; - - struct response - { - std::list transfers; - - BEGIN_KV_SERIALIZE_MAP() - KV_SERIALIZE(transfers) - END_KV_SERIALIZE_MAP() - }; - }; -} -} - -- cgit v1.2.3