diff options
Diffstat (limited to 'src/wallet')
-rw-r--r-- | src/wallet/api/transaction_history.cpp | 6 | ||||
-rw-r--r-- | src/wallet/api/wallet.cpp | 25 | ||||
-rw-r--r-- | src/wallet/api/wallet.h | 12 | ||||
-rw-r--r-- | src/wallet/wallet2.cpp | 142 | ||||
-rw-r--r-- | src/wallet/wallet2.h | 88 | ||||
-rw-r--r-- | src/wallet/wallet2_api.h | 12 | ||||
-rw-r--r-- | src/wallet/wallet_errors.h | 35 | ||||
-rwxr-xr-x[-rw-r--r--] | src/wallet/wallet_rpc_server.cpp | 232 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server.h | 7 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server_commands_defs.h | 44 | ||||
-rw-r--r-- | src/wallet/wallet_rpc_server_error_codes.h | 8 |
11 files changed, 435 insertions, 176 deletions
diff --git a/src/wallet/api/transaction_history.cpp b/src/wallet/api/transaction_history.cpp index 59eca3dd7..8a8243047 100644 --- a/src/wallet/api/transaction_history.cpp +++ b/src/wallet/api/transaction_history.cpp @@ -217,10 +217,10 @@ void TransactionHistoryImpl::refresh() // unconfirmed payments (tx pool) - std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> upayments; + std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> upayments; m_wallet->m_wallet->get_unconfirmed_payments(upayments); - for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) { - const tools::wallet2::payment_details &pd = i->second; + for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = upayments.begin(); i != upayments.end(); ++i) { + const tools::wallet2::payment_details &pd = i->second.m_pd; std::string payment_id = string_tools::pod_to_hex(i->first); if (payment_id.substr(16).find_first_not_of('0') == std::string::npos) payment_id = payment_id.substr(0,16); diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index db7e60cd7..8e747d16b 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1044,8 +1044,7 @@ void WalletImpl::setSubaddressLabel(uint32_t accountIndex, uint32_t addressIndex // - confirmed_transfer_details) PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const string &payment_id, optional<uint64_t> amount, uint32_t mixin_count, - uint32_t subaddr_account, std::set<uint32_t> subaddr_indices, - PendingTransaction::Priority priority) + PendingTransaction::Priority priority, uint32_t subaddr_account, std::set<uint32_t> subaddr_indices) { clearStatus(); @@ -1147,7 +1146,7 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const m_errorString = (boost::format(tr("failed to get random outputs to mix: %s")) % e.what()).str(); m_status = Status_Error; - } catch (const tools::error::not_enough_money& e) { + } catch (const tools::error::not_enough_unlocked_money& e) { m_status = Status_Error; std::ostringstream writer; @@ -1156,6 +1155,15 @@ PendingTransaction *WalletImpl::createTransaction(const string &dst_addr, const print_money(e.tx_amount()); m_errorString = writer.str(); + } catch (const tools::error::not_enough_money& e) { + m_status = Status_Error; + std::ostringstream writer; + + writer << boost::format(tr("not enough money to transfer, overall balance only %s, sent amount %s")) % + print_money(e.available()) % + print_money(e.tx_amount()); + m_errorString = writer.str(); + } catch (const tools::error::tx_not_possible& e) { m_status = Status_Error; std::ostringstream writer; @@ -1241,7 +1249,7 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction() m_errorString = tr("failed to get random outputs to mix"); m_status = Status_Error; - } catch (const tools::error::not_enough_money& e) { + } catch (const tools::error::not_enough_unlocked_money& e) { m_status = Status_Error; std::ostringstream writer; @@ -1250,6 +1258,15 @@ PendingTransaction *WalletImpl::createSweepUnmixableTransaction() print_money(e.tx_amount()); m_errorString = writer.str(); + } catch (const tools::error::not_enough_money& e) { + m_status = Status_Error; + std::ostringstream writer; + + writer << boost::format(tr("not enough money to transfer, overall balance only %s, sent amount %s")) % + print_money(e.available()) % + print_money(e.tx_amount()); + m_errorString = writer.str(); + } catch (const tools::error::tx_not_possible& e) { m_status = Status_Error; std::ostringstream writer; diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index ecb218ea0..051eda3ba 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -73,7 +73,7 @@ public: int status() const; std::string errorString() const; bool setPassword(const std::string &password); - std::string address(uint32_t accountIndex, uint32_t addressIndex) const; + std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const; std::string integratedAddress(const std::string &payment_id) const; std::string secretViewKey() const; std::string publicViewKey() const; @@ -88,8 +88,8 @@ public: ConnectionStatus connected() const; void setTrustedDaemon(bool arg); bool trustedDaemon() const; - uint64_t balance(uint32_t accountIndex) const; - uint64_t unlockedBalance(uint32_t accountIndex) const; + uint64_t balance(uint32_t accountIndex = 0) const; + uint64_t unlockedBalance(uint32_t accountIndex = 0) const; uint64_t blockChainHeight() const; uint64_t approximateBlockChainHeight() const; uint64_t daemonBlockChainHeight() const; @@ -117,9 +117,9 @@ public: PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, optional<uint64_t> amount, uint32_t mixin_count, - uint32_t subaddr_account, - std::set<uint32_t> subaddr_indices, - PendingTransaction::Priority priority = PendingTransaction::Priority_Low); + PendingTransaction::Priority priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, + std::set<uint32_t> subaddr_indices = {}); virtual PendingTransaction * createSweepUnmixableTransaction(); bool submitTransaction(const std::string &fileName); virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename); diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 2b887fbae..9acda9004 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -305,9 +305,9 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, GET_FIELD_FROM_JSON_RETURN_ON_ERROR(json, address, std::string, String, false, std::string()); // compatibility checks - if (!field_seed_found && !field_viewkey_found) + if (!field_seed_found && !field_viewkey_found && !field_spendkey_found) { - tools::fail_msg_writer() << tools::wallet2::tr("At least one of Electrum-style word list and private view key must be specified"); + tools::fail_msg_writer() << tools::wallet2::tr("At least one of Electrum-style word list and private view key and private spend key must be specified"); return false; } if (field_seed_found && (field_viewkey_found || field_spendkey_found)) @@ -368,6 +368,10 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, { wallet->generate(field_filename, field_password, recovery_key, recover, false); } + else if (field_viewkey.empty() && !field_spendkey.empty()) + { + wallet->generate(field_filename, field_password, spendkey, recover, false); + } else { cryptonote::account_public_address address; @@ -390,6 +394,11 @@ std::unique_ptr<tools::wallet2> generate_from_json(const std::string& json_file, } address.m_spend_public_key = info.address.m_spend_public_key; } + else + { + tools::fail_msg_writer() << tools::wallet2::tr("Address must be specified in order to create watch-only wallet"); + return false; + } wallet->generate(field_filename, field_password, address, viewkey); } else @@ -444,6 +453,21 @@ std::string strjoin(const std::vector<size_t> &V, const char *sep) return ss.str(); } +static void emplace_or_replace(std::unordered_multimap<crypto::hash, tools::wallet2::pool_payment_details> &container, + const crypto::hash &key, const tools::wallet2::pool_payment_details &pd) +{ + auto range = container.equal_range(key); + for (auto i = range.first; i != range.second; ++i) + { + if (i->second.m_pd.m_tx_hash == pd.m_pd.m_tx_hash) + { + i->second = pd; + return; + } + } + container.emplace(key, pd); +} + } //namespace namespace tools @@ -692,20 +716,20 @@ void wallet2::expand_subaddresses(const cryptonote::subaddress_index& index) //---------------------------------------------------------------------------------------------------- std::string wallet2::get_subaddress_label(const cryptonote::subaddress_index& index) const { - if (index.major >= m_subaddress_labels.size()) - throw std::runtime_error("index.major is out of bound"); - if (index.minor >= m_subaddress_labels[index.major].size()) - throw std::runtime_error("index.minor is out of bound"); + if (index.major >= m_subaddress_labels.size() || index.minor >= m_subaddress_labels[index.major].size()) + { + MERROR("Subaddress label doesn't exist"); + return ""; + } return m_subaddress_labels[index.major][index.minor]; } //---------------------------------------------------------------------------------------------------- void wallet2::set_subaddress_label(const cryptonote::subaddress_index& index, const std::string &label) { - if (index.major >= m_subaddress_labels.size()) - throw std::runtime_error("index.major is out of bound"); - if (index.minor >= m_subaddress_labels[index.major].size()) - throw std::runtime_error("index.minor is out of bound"); - m_subaddress_labels[index.major][index.minor] = label; + if (index.major >= m_subaddress_labels.size() || index.minor >= m_subaddress_labels[index.major].size()) + MERROR("Subaddress index is out of bounds. Failed to set subaddress label."); + else + m_subaddress_labels[index.major][index.minor] = label; } //---------------------------------------------------------------------------------------------------- /*! @@ -788,12 +812,12 @@ void wallet2::scan_output(const cryptonote::account_keys &keys, const cryptonote { tx_scan_info.money_transfered = tools::decodeRct(tx.rct_signatures, tx_scan_info.received->derivation, i, tx_scan_info.mask); } - tx_money_got_in_outs[tx_scan_info.received->index] = tx_scan_info.money_transfered; + tx_money_got_in_outs[tx_scan_info.received->index] += tx_scan_info.money_transfered; tx_scan_info.amount = tx_scan_info.money_transfered; ++num_vouts_received; } //---------------------------------------------------------------------------------------------------- -void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool) +void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen) { // In this function, tx (probably) only contains the base information // (that is, the prunable stuff may or may not be included) @@ -1163,7 +1187,7 @@ void wallet2::process_new_transaction(const crypto::hash &txid, const cryptonote payment.m_timestamp = ts; payment.m_subaddr_index = i.first; if (pool) { - m_unconfirmed_payments.emplace(payment_id, payment); + emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, double_spend_seen}); if (0 != m_callback) m_callback->on_unconfirmed_money_received(height, txid, tx, payment.m_amount, payment.m_subaddr_index); } @@ -1241,7 +1265,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry if(b.timestamp + 60*60*24 > m_account.get_createtime() && height >= m_refresh_from_block_height) { TIME_MEASURE_START(miner_tx_handle_time); - process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false); + process_new_transaction(get_transaction_hash(b.miner_tx), b.miner_tx, o_indices.indices[txidx++].indices, height, b.timestamp, true, false, false); TIME_MEASURE_FINISH(miner_tx_handle_time); TIME_MEASURE_START(txs_handle_time); @@ -1252,7 +1276,7 @@ void wallet2::process_new_blockchain_entry(const cryptonote::block& b, const cry cryptonote::transaction tx; bool r = parse_and_validate_tx_base_from_blob(txblob, tx); THROW_WALLET_EXCEPTION_IF(!r, error::tx_parse_error, txblob); - process_new_transaction(b.tx_hashes[idx], tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false); + process_new_transaction(b.tx_hashes[idx], tx, o_indices.indices[txidx++].indices, height, b.timestamp, false, false, false); ++idx; } TIME_MEASURE_FINISH(txs_handle_time); @@ -1520,10 +1544,10 @@ void wallet2::pull_next_blocks(uint64_t start_height, uint64_t &blocks_start_hei void wallet2::remove_obsolete_pool_txs(const std::vector<crypto::hash> &tx_hashes) { // remove pool txes to us that aren't in the pool anymore - std::unordered_map<crypto::hash, wallet2::payment_details>::iterator uit = m_unconfirmed_payments.begin(); + std::unordered_multimap<crypto::hash, wallet2::pool_payment_details>::iterator uit = m_unconfirmed_payments.begin(); while (uit != m_unconfirmed_payments.end()) { - const crypto::hash &txid = uit->second.m_tx_hash; + const crypto::hash &txid = uit->second.m_pd.m_tx_hash; bool found = false; for (const auto &it2: tx_hashes) { @@ -1626,23 +1650,27 @@ void wallet2::update_pool_state(bool refreshed) MDEBUG("update_pool_state done second loop"); // gather txids of new pool txes to us - std::vector<crypto::hash> txids; + std::vector<std::pair<crypto::hash, bool>> txids; for (const auto &txid: res.tx_hashes) { - if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end()) - { - LOG_PRINT_L2("Already seen " << txid << ", skipped"); - continue; - } bool txid_found_in_up = false; for (const auto &up: m_unconfirmed_payments) { - if (up.second.m_tx_hash == txid) + if (up.second.m_pd.m_tx_hash == txid) { txid_found_in_up = true; break; } } + if (m_scanned_pool_txs[0].find(txid) != m_scanned_pool_txs[0].end() || m_scanned_pool_txs[1].find(txid) != m_scanned_pool_txs[1].end()) + { + // if it's for us, we want to keep track of whether we saw a double spend, so don't bail out + if (!txid_found_in_up) + { + LOG_PRINT_L2("Already seen " << txid << ", and not for us, skipped"); + continue; + } + } if (!txid_found_in_up) { LOG_PRINT_L1("Found new pool tx: " << txid); @@ -1670,7 +1698,7 @@ void wallet2::update_pool_state(bool refreshed) if (!found) { // not one of those we sent ourselves - txids.push_back(txid); + txids.push_back({txid, false}); } else { @@ -1680,6 +1708,7 @@ void wallet2::update_pool_state(bool refreshed) else { LOG_PRINT_L1("Already saw that one, it's for us"); + txids.push_back({txid, true}); } } @@ -1688,8 +1717,8 @@ void wallet2::update_pool_state(bool refreshed) { cryptonote::COMMAND_RPC_GET_TRANSACTIONS::request req; cryptonote::COMMAND_RPC_GET_TRANSACTIONS::response res; - for (const auto &txid: txids) - req.txs_hashes.push_back(epee::string_tools::pod_to_hex(txid)); + for (const auto &p: txids) + req.txs_hashes.push_back(epee::string_tools::pod_to_hex(p.first)); MDEBUG("asking for " << txids.size() << " transactions"); req.decode_as_json = false; m_daemon_rpc_mutex.lock(); @@ -1711,10 +1740,11 @@ void wallet2::update_pool_state(bool refreshed) { if (cryptonote::parse_and_validate_tx_from_blob(bd, tx, tx_hash, tx_prefix_hash)) { - const std::vector<crypto::hash>::const_iterator i = std::find(txids.begin(), txids.end(), tx_hash); + const std::vector<std::pair<crypto::hash, bool>>::const_iterator i = std::find_if(txids.begin(), txids.end(), + [tx_hash](const std::pair<crypto::hash, bool> &e) { return e.first == tx_hash; }); if (i != txids.end()) { - process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true); + process_new_transaction(tx_hash, tx, std::vector<uint64_t>(), 0, time(NULL), false, true, tx_entry.double_spend_seen); m_scanned_pool_txs[0].insert(tx_hash); if (m_scanned_pool_txs[0].size() > 5000) { @@ -3073,11 +3103,11 @@ void wallet2::get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wall } } //---------------------------------------------------------------------------------------------------- -void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const +void wallet2::get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::pool_payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account, const std::set<uint32_t>& subaddr_indices) const { for (auto i = m_unconfirmed_payments.begin(); i != m_unconfirmed_payments.end(); ++i) { - if ((!subaddr_account || *subaddr_account == i->second.m_subaddr_index.major) && - (subaddr_indices.empty() || subaddr_indices.count(i->second.m_subaddr_index.minor) == 1)) + if ((!subaddr_account || *subaddr_account == i->second.m_pd.m_subaddr_index.major) && + (subaddr_indices.empty() || subaddr_indices.count(i->second.m_pd.m_subaddr_index.minor) == 1)) unconfirmed_payments.push_back(*i); } } @@ -4441,7 +4471,7 @@ void wallet2::transfer_selected(const std::vector<cryptonote::tx_destination_ent } LOG_PRINT_L2("wanted " << print_money(needed_money) << ", found " << print_money(found_money) << ", fee " << print_money(fee)); - THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee); + THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee); uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major; for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i) @@ -4598,7 +4628,7 @@ void wallet2::transfer_selected_rct(std::vector<cryptonote::tx_destination_entry } LOG_PRINT_L2("wanted " << print_money(needed_money) << ", found " << print_money(found_money) << ", fee " << print_money(fee)); - THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee); + THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee); uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major; for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i) @@ -4751,7 +4781,7 @@ static size_t estimate_rct_tx_size(int n_inputs, int mixin, int n_outputs) size += (2*64*32+32+64*32) * n_outputs; // MGs - size += n_inputs * (32 * (mixin+1) + 32); + size += n_inputs * (64 * (mixin+1) + 32); // mixRing - not serialized, can be reconstructed /* size += 2 * 32 * (mixin+1) * n_inputs; */ @@ -5129,7 +5159,7 @@ void wallet2::light_wallet_get_address_txs() payments_txs.push_back(p.second.m_tx_hash); std::vector<crypto::hash> unconfirmed_payments_txs; for(const auto &up: m_unconfirmed_payments) - unconfirmed_payments_txs.push_back(up.second.m_tx_hash); + unconfirmed_payments_txs.push_back(up.second.m_pd.m_tx_hash); // for balance calculation uint64_t wallet_total_sent = 0; @@ -5195,7 +5225,11 @@ void wallet2::light_wallet_get_address_txs() if (t.mempool) { if (std::find(unconfirmed_payments_txs.begin(), unconfirmed_payments_txs.end(), tx_hash) == unconfirmed_payments_txs.end()) { pool_txs.push_back(tx_hash); - m_unconfirmed_payments.emplace(tx_hash, payment); + // assume false as we don't get that info from the light wallet server + crypto::hash payment_id; + THROW_WALLET_EXCEPTION_IF(!epee::string_tools::hex_to_pod(t.payment_id, payment_id), + error::wallet_internal_error, "Failed to parse payment id"); + emplace_or_replace(m_unconfirmed_payments, payment_id, pool_payment_details{payment, false}); if (0 != m_callback) { m_callback->on_lw_unconfirmed_money_received(t.height, payment.m_tx_hash, payment.m_amount); } @@ -5491,6 +5525,15 @@ std::vector<wallet2::pending_tx> wallet2::create_transactions_2(std::vector<cryp } } + // early out if we know we can't make it anyway + // we could also check for being within FEE_PER_KB, but if the fee calculation + // ever changes, this might be missed, so let this go through + // first check overall balance is enough, then unlocked one, so we throw distinct exceptions + THROW_WALLET_EXCEPTION_IF(needed_money > balance(subaddr_account), error::not_enough_money, + unlocked_balance(subaddr_account), needed_money, 0); + THROW_WALLET_EXCEPTION_IF(needed_money > unlocked_balance(subaddr_account), error::not_enough_unlocked_money, + unlocked_balance(subaddr_account), needed_money, 0); + // shuffle & sort output indices { std::random_device rd; @@ -6264,6 +6307,29 @@ std::string wallet2::get_tx_note(const crypto::hash &txid) const return i->second; } +void wallet2::set_attribute(const std::string &key, const std::string &value) +{ + m_attributes[key] = value; +} + +std::string wallet2::get_attribute(const std::string &key) const +{ + std::unordered_map<std::string, std::string>::const_iterator i = m_attributes.find(key); + if (i == m_attributes.end()) + return std::string(); + return i->second; +} + +void wallet2::set_description(const std::string &description) +{ + set_attribute(ATTRIBUTE_DESCRIPTION, description); +} + +std::string wallet2::get_description() const +{ + return get_attribute(ATTRIBUTE_DESCRIPTION); +} + std::string wallet2::sign(const std::string &data) const { crypto::hash hash; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 26680c3da..ba2fc567d 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -244,6 +244,12 @@ namespace tools bool m_incoming; }; + struct pool_payment_details + { + payment_details m_pd; + bool m_double_spend_seen; + }; + struct unconfirmed_transfer_details { cryptonote::transaction_prefix m_tx; @@ -467,8 +473,8 @@ namespace tools size_t get_num_subaddresses(uint32_t index_major) const { return index_major < m_subaddress_labels.size() ? m_subaddress_labels[index_major].size() : 0; } void add_subaddress(uint32_t index_major, const std::string& label); // throws when index is out of bound void expand_subaddresses(const cryptonote::subaddress_index& index); - std::string get_subaddress_label(const cryptonote::subaddress_index& index) const; // throws when index is out of bound - void set_subaddress_label(const cryptonote::subaddress_index &index, const std::string &label); // throws when index is out of bound + std::string get_subaddress_label(const cryptonote::subaddress_index& index) const; + void set_subaddress_label(const cryptonote::subaddress_index &index, const std::string &label); /*! * \brief Tells if the wallet file is deprecated. */ @@ -530,7 +536,7 @@ namespace tools void get_payments_out(std::list<std::pair<crypto::hash,wallet2::confirmed_transfer_details>>& confirmed_payments, uint64_t min_height, uint64_t max_height = (uint64_t)-1, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const; void get_unconfirmed_payments_out(std::list<std::pair<crypto::hash,wallet2::unconfirmed_transfer_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const; - void get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const; + void get_unconfirmed_payments(std::list<std::pair<crypto::hash,wallet2::pool_payment_details>>& unconfirmed_payments, const boost::optional<uint32_t>& subaddr_account = boost::none, const std::set<uint32_t>& subaddr_indices = {}) const; uint64_t get_blockchain_current_height() const { return m_local_bc_height; } void rescan_spent(); @@ -585,7 +591,7 @@ namespace tools std::unordered_map<crypto::hash, payment_details> m; a & m; for (std::unordered_map<crypto::hash, payment_details>::const_iterator i = m.begin(); i != m.end(); ++i) - m_unconfirmed_payments.insert(*i); + m_unconfirmed_payments.insert(std::make_pair(i->first, pool_payment_details{i->second, false})); } if(ver < 14) return; @@ -607,7 +613,15 @@ namespace tools a & m_address_book; if(ver < 17) return; - a & m_unconfirmed_payments; + if (ver < 21) + { + // we're loading an old version, where m_unconfirmed_payments payload was payment_details + std::unordered_map<crypto::hash, payment_details> m; + a & m; + for (const auto &i: m) + m_unconfirmed_payments.insert(std::make_pair(i.first, pool_payment_details{i.second, false})); + return; + } if(ver < 18) return; a & m_scanned_pool_txs[0]; @@ -618,6 +632,12 @@ namespace tools a & m_subaddresses_inv; a & m_subaddress_labels; a & m_additional_tx_keys; + if(ver < 21) + return; + a & m_attributes; + if(ver < 22) + return; + a & m_unconfirmed_payments; } /*! @@ -703,6 +723,9 @@ namespace tools void set_tx_note(const crypto::hash &txid, const std::string ¬e); std::string get_tx_note(const crypto::hash &txid) const; + void set_description(const std::string &description); + std::string get_description() const; + std::string sign(const std::string &data) const; bool verify(const std::string &data, const cryptonote::account_public_address &address, const std::string &signature) const; @@ -757,6 +780,25 @@ namespace tools // check if key image is ours bool light_wallet_key_image_is_ours(const crypto::key_image& key_image, const crypto::public_key& tx_public_key, uint64_t out_index); + /* + * "attributes" are a mechanism to store an arbitrary number of string values + * on the level of the wallet as a whole, identified by keys. Their introduction, + * technically the unordered map m_attributes stored as part of a wallet file, + * led to a new wallet file version, but now new singular pieces of info may be added + * without the need for a new version. + * + * The first and so far only value stored as such an attribute is the description. + * It's stored under the standard key ATTRIBUTE_DESCRIPTION (see method set_description). + * + * The mechanism is open to all clients and allows them to use it for storing basically any + * single string values in a wallet. To avoid the problem that different clients possibly + * overwrite or misunderstand each other's attributes, a two-part key scheme is + * proposed: <client name>.<value name> + */ + const char* const ATTRIBUTE_DESCRIPTION = "wallet2.description"; + void set_attribute(const std::string &key, const std::string &value); + std::string get_attribute(const std::string &key) const; + private: /*! * \brief Stores wallet information to wallet file. @@ -772,7 +814,7 @@ namespace tools * \param password Password of wallet file */ bool load_keys(const std::string& keys_file_name, const std::string& password); - void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool); + void process_new_transaction(const crypto::hash &txid, const cryptonote::transaction& tx, const std::vector<uint64_t> &o_indices, uint64_t height, uint64_t ts, bool miner_tx, bool pool, bool double_spend_seen); void process_new_blockchain_entry(const cryptonote::block& b, const cryptonote::block_complete_entry& bche, const crypto::hash& bl_id, uint64_t height, const cryptonote::COMMAND_RPC_GET_BLOCKS_FAST::block_output_indices &o_indices); void detach_blockchain(uint64_t height); void get_short_chain_history(std::list<crypto::hash>& ids) const; @@ -821,7 +863,7 @@ namespace tools std::atomic<uint64_t> m_local_bc_height; //temporary workaround std::unordered_map<crypto::hash, unconfirmed_transfer_details> m_unconfirmed_txs; std::unordered_map<crypto::hash, confirmed_transfer_details> m_confirmed_txs; - std::unordered_multimap<crypto::hash, payment_details> m_unconfirmed_payments; + std::unordered_multimap<crypto::hash, pool_payment_details> m_unconfirmed_payments; std::unordered_map<crypto::hash, crypto::secret_key> m_tx_keys; cryptonote::checkpoints m_checkpoints; std::unordered_map<crypto::hash, std::vector<crypto::secret_key>> m_additional_tx_keys; @@ -835,6 +877,7 @@ namespace tools std::unordered_map<cryptonote::subaddress_index, crypto::public_key> m_subaddresses_inv; std::vector<std::vector<std::string>> m_subaddress_labels; std::unordered_map<crypto::hash, std::string> m_tx_notes; + std::unordered_map<std::string, std::string> m_attributes; std::vector<tools::wallet2::address_book_row> m_address_book; uint64_t m_upper_transaction_size_limit; //TODO: auto-calc this value or request from daemon, now use some fixed value @@ -882,9 +925,10 @@ namespace tools std::unordered_map<crypto::public_key, std::map<uint64_t, crypto::key_image> > m_key_image_cache; }; } -BOOST_CLASS_VERSION(tools::wallet2, 20) +BOOST_CLASS_VERSION(tools::wallet2, 22) BOOST_CLASS_VERSION(tools::wallet2::transfer_details, 8) BOOST_CLASS_VERSION(tools::wallet2::payment_details, 2) +BOOST_CLASS_VERSION(tools::wallet2::pool_payment_details, 1) BOOST_CLASS_VERSION(tools::wallet2::unconfirmed_transfer_details, 7) BOOST_CLASS_VERSION(tools::wallet2::confirmed_transfer_details, 5) BOOST_CLASS_VERSION(tools::wallet2::address_book_row, 17) @@ -1042,7 +1086,10 @@ namespace boost x.m_amount_out += x.m_change; } if (ver < 7) + { + x.m_subaddr_account = 0; return; + } a & x.m_subaddr_account; a & x.m_subaddr_indices; } @@ -1083,7 +1130,10 @@ namespace boost } a & x.m_unlock_time; if (ver < 5) + { + x.m_subaddr_account = 0; return; + } a & x.m_subaddr_account; a & x.m_subaddr_indices; } @@ -1099,10 +1149,20 @@ namespace boost return; a & x.m_timestamp; if (ver < 2) + { + x.m_subaddr_index = {}; return; + } a & x.m_subaddr_index; } - + + template <class Archive> + inline void serialize(Archive& a, tools::wallet2::pool_payment_details& x, const boost::serialization::version_type ver) + { + a & x.m_pd; + a & x.m_double_spend_seen; + } + template <class Archive> inline void serialize(Archive& a, tools::wallet2::address_book_row& x, const boost::serialization::version_type ver) { @@ -1110,7 +1170,10 @@ namespace boost a & x.m_payment_id; a & x.m_description; if (ver < 17) + { + x.m_is_subaddress = false; return; + } a & x.m_is_subaddress; } @@ -1140,7 +1203,10 @@ namespace boost a & x.use_rct; a & x.dests; if (ver < 1) + { + x.subaddr_account = 0; return; + } a & x.subaddr_account; a & x.subaddr_indices; } @@ -1249,10 +1315,10 @@ namespace tools } // randomly select inputs for transaction - // throw if requested send amount is greater than amount available to send + // throw if requested send amount is greater than (unlocked) amount available to send std::list<size_t> selected_transfers; uint64_t found_money = select_transfers(needed_money, unused_transfers_indices, selected_transfers, trusted_daemon); - THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_money, found_money, needed_money - fee, fee); + THROW_WALLET_EXCEPTION_IF(found_money < needed_money, error::not_enough_unlocked_money, found_money, needed_money - fee, fee); uint32_t subaddr_account = m_transfers[*selected_transfers.begin()].m_subaddr_index.major; for (auto i = ++selected_transfers.begin(); i != selected_transfers.end(); ++i) diff --git a/src/wallet/wallet2_api.h b/src/wallet/wallet2_api.h index 4d734ab94..a8c150ca7 100644 --- a/src/wallet/wallet2_api.h +++ b/src/wallet/wallet2_api.h @@ -360,7 +360,7 @@ struct Wallet //! in case error status, returns error string virtual std::string errorString() const = 0; virtual bool setPassword(const std::string &password) = 0; - virtual std::string address(uint32_t accountIndex, uint32_t addressIndex) const = 0; + virtual std::string address(uint32_t accountIndex = 0, uint32_t addressIndex = 0) const = 0; std::string mainAddress() const { return address(0, 0); } virtual std::string path() const = 0; virtual bool testnet() const = 0; @@ -476,14 +476,14 @@ struct Wallet virtual ConnectionStatus connected() const = 0; virtual void setTrustedDaemon(bool arg) = 0; virtual bool trustedDaemon() const = 0; - virtual uint64_t balance(uint32_t accountIndex) const = 0; + virtual uint64_t balance(uint32_t accountIndex = 0) const = 0; uint64_t balanceAll() const { uint64_t result = 0; for (uint32_t i = 0; i < numSubaddressAccounts(); ++i) result += balance(i); return result; } - virtual uint64_t unlockedBalance(uint32_t accountIndex) const = 0; + virtual uint64_t unlockedBalance(uint32_t accountIndex = 0) const = 0; uint64_t unlockedBalanceAll() const { uint64_t result = 0; for (uint32_t i = 0; i < numSubaddressAccounts(); ++i) @@ -623,9 +623,9 @@ struct Wallet virtual PendingTransaction * createTransaction(const std::string &dst_addr, const std::string &payment_id, optional<uint64_t> amount, uint32_t mixin_count, - uint32_t subaddr_account, - std::set<uint32_t> subaddr_indices, - PendingTransaction::Priority = PendingTransaction::Priority_Low) = 0; + PendingTransaction::Priority = PendingTransaction::Priority_Low, + uint32_t subaddr_account = 0, + std::set<uint32_t> subaddr_indices = {}) = 0; /*! * \brief createSweepUnmixableTransaction creates transaction with unmixable outputs. diff --git a/src/wallet/wallet_errors.h b/src/wallet/wallet_errors.h index d1f4a796d..9d66f125e 100644 --- a/src/wallet/wallet_errors.h +++ b/src/wallet/wallet_errors.h @@ -68,6 +68,7 @@ namespace tools // get_tx_pool_error // transfer_error * // get_random_outs_general_error + // not_enough_unlocked_money // not_enough_money // tx_not_possible // not_enough_outs_to_mix @@ -356,11 +357,37 @@ namespace tools //---------------------------------------------------------------------------------------------------- typedef failed_rpc_request<transfer_error, get_random_outs_error_message_index> get_random_outs_error; //---------------------------------------------------------------------------------------------------- + struct not_enough_unlocked_money : public transfer_error + { + explicit not_enough_unlocked_money(std::string&& loc, uint64_t available, uint64_t tx_amount, uint64_t fee) + : transfer_error(std::move(loc), "not enough unlocked money") + , m_available(available) + , m_tx_amount(tx_amount) + { + } + + uint64_t available() const { return m_available; } + uint64_t tx_amount() const { return m_tx_amount; } + + std::string to_string() const + { + std::ostringstream ss; + ss << transfer_error::to_string() << + ", available = " << cryptonote::print_money(m_available) << + ", tx_amount = " << cryptonote::print_money(m_tx_amount); + return ss.str(); + } + + private: + uint64_t m_available; + uint64_t m_tx_amount; + }; + //---------------------------------------------------------------------------------------------------- struct not_enough_money : public transfer_error { - explicit not_enough_money(std::string&& loc, uint64_t availbable, uint64_t tx_amount, uint64_t fee) + explicit not_enough_money(std::string&& loc, uint64_t available, uint64_t tx_amount, uint64_t fee) : transfer_error(std::move(loc), "not enough money") - , m_available(availbable) + , m_available(available) , m_tx_amount(tx_amount) { } @@ -384,9 +411,9 @@ namespace tools //---------------------------------------------------------------------------------------------------- struct tx_not_possible : public transfer_error { - explicit tx_not_possible(std::string&& loc, uint64_t availbable, uint64_t tx_amount, uint64_t fee) + explicit tx_not_possible(std::string&& loc, uint64_t available, uint64_t tx_amount, uint64_t fee) : transfer_error(std::move(loc), "tx not possible") - , m_available(availbable) + , m_available(available) , m_tx_amount(tx_amount) , m_fee(fee) { diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index a048a53ae..5dbf30419 100644..100755 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -216,7 +216,7 @@ namespace tools m_net_server.set_threads_prefix("RPC"); return epee::http_server_impl_base<wallet_rpc_server, connection_context>::init( - std::move(bind_port), std::move(rpc_config->bind_ip), std::move(http_login) + std::move(bind_port), std::move(rpc_config->bind_ip), std::move(rpc_config->access_control_origins), std::move(http_login) ); } //------------------------------------------------------------------------------------------------------------------------------ @@ -299,8 +299,9 @@ namespace tools entry.subaddr_index = { pd.m_subaddr_account, 0 }; } //------------------------------------------------------------------------------------------------------------------------------ - void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd) + void wallet_rpc_server::fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::pool_payment_details &ppd) { + const tools::wallet2::payment_details &pd = ppd.m_pd; entry.txid = string_tools::pod_to_hex(pd.m_tx_hash); entry.payment_id = string_tools::pod_to_hex(payment_id); if (entry.payment_id.substr(16).find_first_not_of('0') == std::string::npos) @@ -311,6 +312,7 @@ namespace tools entry.unlock_time = pd.m_unlock_time; entry.fee = 0; // TODO entry.note = m_wallet->get_tx_note(pd.m_tx_hash); + entry.double_spend_seen = ppd.m_double_spend_seen; entry.type = "pool"; entry.subaddr_index = pd.m_subaddr_index; } @@ -341,8 +343,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -369,8 +370,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -406,8 +406,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -436,8 +435,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -454,8 +452,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -476,8 +473,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -492,8 +488,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -618,7 +613,7 @@ namespace tools // 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.code = WALLET_RPC_ERROR_CODE_TX_TOO_LARGE; er.message = "Transaction would be too large. try /transfer_split."; return false; } @@ -642,22 +637,9 @@ namespace tools } 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"; + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR); return false; } return true; @@ -725,22 +707,9 @@ namespace tools 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"; + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR); return false; } return true; @@ -782,22 +751,9 @@ namespace tools 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"; + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR); return false; } return true; @@ -852,22 +808,9 @@ namespace tools 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"; + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_GENERIC_TRANSFER_ERROR); return false; } return true; @@ -897,10 +840,9 @@ namespace tools res.payment_id = epee::string_tools::pod_to_hex(payment_id); return true; } - catch (const std::exception &e) + catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -929,10 +871,9 @@ namespace tools res.payment_id = epee::string_tools::pod_to_hex(info.payment_id); return true; } - catch (const std::exception &e) + catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -954,8 +895,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -1185,8 +1125,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -1258,8 +1197,7 @@ namespace tools } catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -1337,6 +1275,35 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_set_attribute(const wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + + m_wallet->set_attribute(req.key, req.value); + + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_get_attribute(const wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::response& res, epee::json_rpc::error& er) + { + if (!m_wallet) return not_open(er); + if (m_wallet->restricted()) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + + res.value = m_wallet->get_attribute(req.key); + return true; + } + //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er) { if (!m_wallet) return not_open(er); @@ -1392,9 +1359,9 @@ namespace tools { m_wallet->update_pool_state(); - std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> payments; + std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> payments; m_wallet->get_unconfirmed_payments(payments, req.account_index, req.subaddr_indices); - for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { + for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = payments.begin(); i != payments.end(); ++i) { res.pool.push_back(wallet_rpc::transfer_entry()); fill_transfer_entry(res.pool.back(), i->first, i->second); } @@ -1465,10 +1432,10 @@ namespace tools m_wallet->update_pool_state(); - std::list<std::pair<crypto::hash, tools::wallet2::payment_details>> pool_payments; + std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>> pool_payments; m_wallet->get_unconfirmed_payments(pool_payments); - for (std::list<std::pair<crypto::hash, tools::wallet2::payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) { - if (i->second.m_tx_hash == txid) + for (std::list<std::pair<crypto::hash, tools::wallet2::pool_payment_details>>::const_iterator i = pool_payments.begin(); i != pool_payments.end(); ++i) { + if (i->second.m_pd.m_tx_hash == txid) { fill_transfer_entry(res.transfer, i->first, i->second); return true; @@ -1494,10 +1461,9 @@ namespace tools } } - catch (...) + catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "Failed"; + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } @@ -1550,10 +1516,9 @@ namespace tools res.height = height; } - catch (...) + catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = "Failed"; + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } @@ -1730,10 +1695,9 @@ namespace tools m_wallet->rescan_spent(); return true; } - catch (const std::exception &e) + catch (const std::exception& e) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; - er.message = e.what(); + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); return false; } return true; @@ -1798,7 +1762,7 @@ namespace tools { if (m_wallet_dir.empty()) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.code = WALLET_RPC_ERROR_CODE_NO_WALLET_DIR; er.message = "No wallet dir configured"; return false; } @@ -1866,6 +1830,11 @@ namespace tools } catch (const std::exception& e) { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + if (!wal) + { er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; er.message = "Failed to generate wallet"; return false; @@ -1880,7 +1849,7 @@ namespace tools { if (m_wallet_dir.empty()) { - er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.code = WALLET_RPC_ERROR_CODE_NO_WALLET_DIR; er.message = "No wallet dir configured"; return false; } @@ -1914,13 +1883,13 @@ namespace tools command_line::add_arg(desc, arg_password); po::store(po::parse_command_line(argc, argv, desc), vm2); } - std::unique_ptr<tools::wallet2> wal; + std::unique_ptr<tools::wallet2> wal = nullptr; try { wal = tools::wallet2::make_from_file(vm2, wallet_file).first; } catch (const std::exception& e) { - wal = nullptr; + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); } if (!wal) { @@ -1934,6 +1903,63 @@ namespace tools return true; } //------------------------------------------------------------------------------------------------------------------------------ + void wallet_rpc_server::handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code) { + try + { + std::rethrow_exception(e); + } + catch (const tools::error::daemon_busy& e) + { + er.code = WALLET_RPC_ERROR_CODE_DAEMON_IS_BUSY; + er.message = e.what(); + } + catch (const tools::error::zero_destination& e) + { + er.code = WALLET_RPC_ERROR_CODE_ZERO_DESTINATION; + er.message = e.what(); + } + catch (const tools::error::not_enough_money& e) + { + er.code = WALLET_RPC_ERROR_CODE_NOT_ENOUGH_MONEY; + er.message = e.what(); + } + catch (const tools::error::tx_not_possible& e) + { + er.code = WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE; + er.message = (boost::format(tr("Transaction not possible. Available only %s, transaction amount %s = %s + %s (fee)")) % + cryptonote::print_money(e.available()) % + cryptonote::print_money(e.tx_amount() + e.fee()) % + cryptonote::print_money(e.tx_amount()) % + cryptonote::print_money(e.fee())).str(); + er.message = e.what(); + } + catch (const tools::error::not_enough_outs_to_mix& e) + { + er.code = WALLET_RPC_ERROR_CODE_NOT_ENOUGH_OUTS_TO_MIX; + er.message = e.what(); + } + catch (const error::file_exists& e) + { + er.code = WALLET_RPC_ERROR_CODE_WALLET_ALREADY_EXISTS; + er.message = "Cannot create wallet. Already exists."; + } + catch (const error::invalid_password& e) + { + er.code = WALLET_RPC_ERROR_CODE_INVALID_PASSWORD; + er.message = "Invalid password."; + } + catch (const std::exception& e) + { + er.code = default_error_code; + er.message = e.what(); + } + catch (...) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR"; + } + } + //------------------------------------------------------------------------------------------------------------------------------ } int main(int argc, char** argv) { diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index 7f4c412e4..a2677ef1b 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -90,6 +90,8 @@ namespace tools MAP_JON_RPC_WE("rescan_blockchain", on_rescan_blockchain, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN) MAP_JON_RPC_WE("set_tx_notes", on_set_tx_notes, wallet_rpc::COMMAND_RPC_SET_TX_NOTES) MAP_JON_RPC_WE("get_tx_notes", on_get_tx_notes, wallet_rpc::COMMAND_RPC_GET_TX_NOTES) + MAP_JON_RPC_WE("set_attribute", on_set_attribute, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE) + MAP_JON_RPC_WE("get_attribute", on_get_attribute, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE) MAP_JON_RPC_WE("get_transfers", on_get_transfers, wallet_rpc::COMMAND_RPC_GET_TRANSFERS) MAP_JON_RPC_WE("get_transfer_by_txid", on_get_transfer_by_txid, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID) MAP_JON_RPC_WE("sign", on_sign, wallet_rpc::COMMAND_RPC_SIGN) @@ -134,6 +136,8 @@ namespace tools bool on_rescan_blockchain(const wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::request& req, wallet_rpc::COMMAND_RPC_RESCAN_BLOCKCHAIN::response& res, epee::json_rpc::error& er); bool on_set_tx_notes(const wallet_rpc::COMMAND_RPC_SET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_SET_TX_NOTES::response& res, epee::json_rpc::error& er); bool on_get_tx_notes(const wallet_rpc::COMMAND_RPC_GET_TX_NOTES::request& req, wallet_rpc::COMMAND_RPC_GET_TX_NOTES::response& res, epee::json_rpc::error& er); + bool on_set_attribute(const wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_SET_ATTRIBUTE::response& res, epee::json_rpc::error& er); + bool on_get_attribute(const wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::request& req, wallet_rpc::COMMAND_RPC_GET_ATTRIBUTE::response& res, epee::json_rpc::error& er); bool on_get_transfers(const wallet_rpc::COMMAND_RPC_GET_TRANSFERS::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFERS::response& res, epee::json_rpc::error& er); bool on_get_transfer_by_txid(const wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::request& req, wallet_rpc::COMMAND_RPC_GET_TRANSFER_BY_TXID::response& res, epee::json_rpc::error& er); bool on_sign(const wallet_rpc::COMMAND_RPC_SIGN::request& req, wallet_rpc::COMMAND_RPC_SIGN::response& res, epee::json_rpc::error& er); @@ -159,9 +163,10 @@ namespace tools void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd); void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::confirmed_transfer_details &pd); void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &txid, const tools::wallet2::unconfirmed_transfer_details &pd); - void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::payment_details &pd); + void fill_transfer_entry(tools::wallet_rpc::transfer_entry &entry, const crypto::hash &payment_id, const tools::wallet2::pool_payment_details &pd); bool not_open(epee::json_rpc::error& er); uint64_t adjust_mixin(uint64_t mixin); + void handle_rpc_exception(const std::exception_ptr& e, epee::json_rpc::error& er, int default_error_code); wallet2 *m_wallet; std::string m_wallet_dir; diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index f652fa7ff..06f2456c3 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -739,6 +739,48 @@ namespace wallet_rpc }; }; + struct COMMAND_RPC_SET_ATTRIBUTE + { + struct request + { + std::string key; + std::string value; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(key) + KV_SERIALIZE(value) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + BEGIN_KV_SERIALIZE_MAP() + END_KV_SERIALIZE_MAP() + }; + }; + + struct COMMAND_RPC_GET_ATTRIBUTE + { + struct request + { + + std::string key; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(key) + END_KV_SERIALIZE_MAP() + }; + + struct response + { + std::string value; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(value) + END_KV_SERIALIZE_MAP() + }; + }; + struct transfer_entry { std::string txid; @@ -752,6 +794,7 @@ namespace wallet_rpc std::string type; uint64_t unlock_time; cryptonote::subaddress_index subaddr_index; + bool double_spend_seen; BEGIN_KV_SERIALIZE_MAP() KV_SERIALIZE(txid); @@ -765,6 +808,7 @@ namespace wallet_rpc KV_SERIALIZE(type); KV_SERIALIZE(unlock_time) KV_SERIALIZE(subaddr_index); + KV_SERIALIZE(double_spend_seen) END_KV_SERIALIZE_MAP() }; diff --git a/src/wallet/wallet_rpc_server_error_codes.h b/src/wallet/wallet_rpc_server_error_codes.h index cc9fd3856..e74e9110b 100644 --- a/src/wallet/wallet_rpc_server_error_codes.h +++ b/src/wallet/wallet_rpc_server_error_codes.h @@ -46,3 +46,11 @@ #define WALLET_RPC_ERROR_CODE_NOT_OPEN -13 #define WALLET_RPC_ERROR_CODE_ACCOUNT_INDEX_OUTOFBOUND -14 #define WALLET_RPC_ERROR_CODE_ADDRESS_INDEX_OUTOFBOUND -15 +#define WALLET_RPC_ERROR_CODE_TX_NOT_POSSIBLE -16 +#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_MONEY -17 +#define WALLET_RPC_ERROR_CODE_TX_TOO_LARGE -18 +#define WALLET_RPC_ERROR_CODE_NOT_ENOUGH_OUTS_TO_MIX -19 +#define WALLET_RPC_ERROR_CODE_ZERO_DESTINATION -20 +#define WALLET_RPC_ERROR_CODE_WALLET_ALREADY_EXISTS -21 +#define WALLET_RPC_ERROR_CODE_INVALID_PASSWORD -22 +#define WALLET_RPC_ERROR_CODE_NO_WALLET_DIR -23 |