diff --git a/.gitignore b/.gitignore index 9f62575e5a..4387e1c9dc 100644 --- a/.gitignore +++ b/.gitignore @@ -120,3 +120,5 @@ nbproject __pycache__/ *.pyc *.log +.gitignore +/dist/ diff --git a/src/wallet/api/pending_transaction.cpp b/src/wallet/api/pending_transaction.cpp index 9783da5bcd..a13959806c 100644 --- a/src/wallet/api/pending_transaction.cpp +++ b/src/wallet/api/pending_transaction.cpp @@ -162,6 +162,30 @@ bool PendingTransactionImpl::commit(const std::string &filename, bool overwrite) return m_status == Status_Ok; } +std::string PendingTransactionImpl::commitToString() +{ + + std::string tx; + LOG_PRINT_L3("m_pending_tx size: " << m_pending_tx.size()); + + try { + tx = m_wallet.m_wallet->dump_tx_to_str(m_pending_tx); + m_status = Status_Ok; + return tx; + } catch (const tools::error::wallet_internal_error&) { + m_errorString = tr("Wallet internal eror."); + m_status = Status_Error; + } catch (const std::exception &e) { + m_errorString = string(tr("Unknown exception: ")) + e.what(); + m_status = Status_Error; + } catch (...) { + m_errorString = tr("Unhandled exception"); + LOG_ERROR(m_errorString); + m_status = Status_Error; + } + return ""; // m_status != Status_Ok +} + uint64_t PendingTransactionImpl::amount() const { uint64_t result = 0; diff --git a/src/wallet/api/pending_transaction.h b/src/wallet/api/pending_transaction.h index 9d8d754c0c..e1c3cc678e 100644 --- a/src/wallet/api/pending_transaction.h +++ b/src/wallet/api/pending_transaction.h @@ -45,6 +45,7 @@ class PendingTransactionImpl : public PendingTransaction ~PendingTransactionImpl(); int status() const override; std::string errorString() const override; + std::string commitToString() override; bool commit(const std::string &filename = "", bool overwrite = false) override; uint64_t amount() const override; uint64_t dust() const override; diff --git a/src/wallet/api/unsigned_transaction.cpp b/src/wallet/api/unsigned_transaction.cpp index c549539e5b..378e1886f2 100644 --- a/src/wallet/api/unsigned_transaction.cpp +++ b/src/wallet/api/unsigned_transaction.cpp @@ -96,6 +96,28 @@ bool UnsignedTransactionImpl::sign(const std::string &signedFileName) return true; } +std::string UnsignedTransactionImpl::signAsString() +{ + if(m_wallet.watchOnly()) + { + m_errorString = tr("This is a watch only wallet"); + m_status = Status_Error; + return ""; + } + tools::wallet2::signed_tx_set signed_txes; + std::vector ptx; + try + { + return m_wallet.m_wallet->sign_tx_dump_to_str(m_unsigned_tx_set, ptx, signed_txes); + } + catch (const std::exception &e) + { + m_errorString = string(tr("Failed to sign transaction")) + e.what(); + m_status = Status_Error; + return ""; + } +} + //---------------------------------------------------------------------------------------------------- bool UnsignedTransactionImpl::checkLoadedTx(const std::function get_num_txes, const std::function &get_tx, const std::string &extra_message) { diff --git a/src/wallet/api/unsigned_transaction.h b/src/wallet/api/unsigned_transaction.h index b07d43fb11..6a4d87b530 100644 --- a/src/wallet/api/unsigned_transaction.h +++ b/src/wallet/api/unsigned_transaction.h @@ -53,6 +53,7 @@ class UnsignedTransactionImpl : public UnsignedTransaction uint64_t txCount() const override; // sign txs and save to file bool sign(const std::string &signedFileName) override; + std::string signAsString() override; std::string confirmationMessage() const override {return m_confirmationMessage;} uint64_t minMixinCount() const override; diff --git a/src/wallet/api/wallet.cpp b/src/wallet/api/wallet.cpp index 6c50002dd1..d501b40e31 100644 --- a/src/wallet/api/wallet.cpp +++ b/src/wallet/api/wallet.cpp @@ -1134,6 +1134,27 @@ UnsignedTransaction *WalletImpl::loadUnsignedTx(const std::string &unsigned_file return transaction; } +UnsignedTransaction *WalletImpl::loadUnsignedTxFromString(const std::string &data) { + clearStatus(); + std::unique_ptr transaction(new UnsignedTransactionImpl(*this)); + if (checkBackgroundSync("cannot load tx") || !m_wallet->parse_unsigned_tx_from_str(data, transaction->m_unsigned_tx_set)){ + setStatusError(tr("Failed to load unsigned transactions")); + transaction->m_status = UnsignedTransaction::Status::Status_Error; + transaction->m_errorString = errorString(); + + return transaction.release(); + } + + // Check tx data and construct confirmation message + std::string extra_message; + if (!std::get<2>(transaction->m_unsigned_tx_set.transfers).empty()) + extra_message = (boost::format("%u outputs to import. ") % (unsigned)std::get<2>(transaction->m_unsigned_tx_set.transfers).size()).str(); + transaction->checkLoadedTx([&transaction](){return transaction->m_unsigned_tx_set.txes.size();}, [&transaction](size_t n)->const tools::wallet2::tx_construction_data&{return transaction->m_unsigned_tx_set.txes[n];}, extra_message); + setStatus(transaction->status(), transaction->errorString()); + + return transaction.release(); +} + bool WalletImpl::submitTransaction(const string &fileName) { clearStatus(); if (checkBackgroundSync("cannot submit tx")) @@ -1154,6 +1175,48 @@ bool WalletImpl::submitTransaction(const string &fileName) { return true; } +bool WalletImpl::submitTransactionFromString(const string &data) { + clearStatus(); + if (checkBackgroundSync("cannot submit tx")) + return false; + std::unique_ptr transaction(new PendingTransactionImpl(*this)); + + bool r = m_wallet->parse_tx_from_str(data, transaction->m_pending_tx, NULL); + if (!r) { + setStatusError(Status_Ok, tr("Failed to load transaction from string")); + return false; + } + + if(!transaction->commit()) { + setStatusError(transaction->m_errorString); + return false; + } + + return true; +} + +std::string WalletImpl::exportKeyImagesAsString(bool all) +{ + if (m_wallet->watch_only()) + { + setStatusError(tr("Wallet is view only")); + return ""; + } + if (checkBackgroundSync("cannot export key images")) + return ""; + + try + { + return m_wallet->export_key_images_string(all); + } + catch (const std::exception &e) + { + LOG_ERROR("Error exporting key images: " << e.what()); + setStatusError(e.what()); + return ""; + } +} + bool WalletImpl::exportKeyImages(const string &filename, bool all) { if (m_wallet->watch_only()) @@ -1181,6 +1244,32 @@ bool WalletImpl::exportKeyImages(const string &filename, bool all) return true; } +bool WalletImpl::importKeyImagesFromString(const std::string &data) +{ + if (checkBackgroundSync("cannot import key images")) + return false; + if (!trustedDaemon()) + { + setStatusError(tr("Key images can only be imported with a trusted daemon")); + return false; + } + try + { + uint64_t spent = 0, unspent = 0; + uint64_t height = m_wallet->import_key_images_string(data, spent, unspent); + LOG_PRINT_L2("Signed key images imported to height " << height << ", " + << print_money(spent) << " spent, " << print_money(unspent) << " unspent"); + } + catch (const std::exception &e) + { + LOG_ERROR("Error exporting key images: " << e.what()); + setStatusError(string(tr("Failed to import key images: ")) + e.what()); + return false; + } + + return true; +} + bool WalletImpl::importKeyImages(const string &filename) { if (checkBackgroundSync("cannot import key images")) @@ -1206,6 +1295,29 @@ bool WalletImpl::importKeyImages(const string &filename) return true; } +std::string WalletImpl::exportOutputsAsString(bool all) +{ + if (checkBackgroundSync("cannot export outputs")) + return ""; + if (m_wallet->key_on_device()) + { + setStatusError(string(tr("Not supported on HW wallets."))); + return ""; + } + + try + { + return m_wallet->export_outputs_to_str(all); + } + catch (const std::exception &e) + { + LOG_ERROR("Error exporting outputs: " << e.what()); + setStatusError(string(tr("Error exporting outputs: ")) + e.what()); + return ""; + } + return ""; +} + bool WalletImpl::exportOutputs(const string &filename, bool all) { if (checkBackgroundSync("cannot export outputs")) @@ -1238,6 +1350,32 @@ bool WalletImpl::exportOutputs(const string &filename, bool all) return true; } +bool WalletImpl::importOutputsFromString(const std::string &data) +{ + if (checkBackgroundSync("cannot import outputs")) + return false; + if (m_wallet->key_on_device()) + { + setStatusError(string(tr("Not supported on HW wallets."))); + return false; + } + + + try + { + size_t n_outputs = m_wallet->import_outputs_from_str(data); + LOG_PRINT_L2(std::to_string(n_outputs) << " outputs imported"); + } + catch (const std::exception &e) + { + LOG_ERROR("Failed to import outputs: " << e.what()); + setStatusError(string(tr("Failed to import outputs: ")) + e.what()); + return false; + } + + return true; +} + bool WalletImpl::importOutputs(const string &filename) { if (checkBackgroundSync("cannot import outputs")) diff --git a/src/wallet/api/wallet.h b/src/wallet/api/wallet.h index d48d7f130e..89de247b1f 100644 --- a/src/wallet/api/wallet.h +++ b/src/wallet/api/wallet.h @@ -166,10 +166,16 @@ class WalletImpl : public Wallet std::set subaddr_indices = {}) override; virtual PendingTransaction * createSweepUnmixableTransaction() override; bool submitTransaction(const std::string &fileName) override; + bool submitTransactionFromString(const std::string &data) override; virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) override; + virtual UnsignedTransaction * loadUnsignedTxFromString(const std::string &data) override; + std::string exportKeyImagesAsString(bool all = false) override; bool exportKeyImages(const std::string &filename, bool all = false) override; + bool importKeyImagesFromString(const std::string &data) override; bool importKeyImages(const std::string &filename) override; + std::string exportOutputsAsString(bool all = false) override; bool exportOutputs(const std::string &filename, bool all = false) override; + bool importOutputsFromString(const std::string &data) override; bool importOutputs(const std::string &filename) override; bool scanTransactions(const std::vector &txids) override; diff --git a/src/wallet/api/wallet2_api.h b/src/wallet/api/wallet2_api.h index c374d1574b..378bd9fefd 100644 --- a/src/wallet/api/wallet2_api.h +++ b/src/wallet/api/wallet2_api.h @@ -89,6 +89,8 @@ struct PendingTransaction virtual ~PendingTransaction() = 0; virtual int status() const = 0; virtual std::string errorString() const = 0; + // return string of transaction gives the same content which would be saved to file with commit(filename) + virtual std::string commitToString() = 0; // commit transaction or save to file if filename is provided. virtual bool commit(const std::string &filename = "", bool overwrite = false) = 0; virtual uint64_t amount() const = 0; @@ -161,6 +163,11 @@ struct UnsignedTransaction * return - true on success */ virtual bool sign(const std::string &signedFileName) = 0; + /*! + * @brief signAsString - Sign txs and return as string + * return - signed tx as string on success + */ + virtual std::string signAsString() = 0; }; /** @@ -895,12 +902,24 @@ struct Wallet */ virtual UnsignedTransaction * loadUnsignedTx(const std::string &unsigned_filename) = 0; + /*! + * \brief loadUnsignedTxFromString - creates transaction from unsigned tx string + * \return - UnsignedTransaction object. caller is responsible to check UnsignedTransaction::status() + * after object returned + */ + virtual UnsignedTransaction * loadUnsignedTxFromString(const std::string &data) = 0; + /*! * \brief submitTransaction - submits transaction in signed tx file * \return - true on success */ virtual bool submitTransaction(const std::string &fileName) = 0; - + + /*! + * \brief submitTransactionFromString - submits transaction provided as string + * \return - true on success + */ + virtual bool submitTransactionFromString(const std::string &data) = 0; /*! * \brief disposeTransaction - destroys transaction object @@ -916,6 +935,13 @@ struct Wallet virtual uint64_t estimateTransactionFee(const std::vector> &destinations, PendingTransaction::Priority priority) const = 0; + /*! + * \brief exportKeyImages - exports key images as string + * \param all - export all key images or only those that have not yet been exported + * \return - key images as std::string + */ + virtual std::string exportKeyImagesAsString(bool all = false) = 0; + /*! * \brief exportKeyImages - exports key images to file * \param filename @@ -923,6 +949,13 @@ struct Wallet * \return - true on success */ virtual bool exportKeyImages(const std::string &filename, bool all = false) = 0; + + /*! + * \brief importKeyImagesFromString - imports key images from string for UR use. + * \param data + * \return - true on success + */ + virtual bool importKeyImagesFromString(const std::string &data) = 0; /*! * \brief importKeyImages - imports key images from file @@ -932,12 +965,25 @@ struct Wallet virtual bool importKeyImages(const std::string &filename) = 0; /*! - * \brief importOutputs - exports outputs to file + * \brief exportOutputsAsString - exports outputs to a string for UR + * \return - true on success + */ + virtual std::string exportOutputsAsString(bool all = false) = 0; + + /*! + * \brief exportOutputs - exports outputs to file * \param filename * \return - true on success */ virtual bool exportOutputs(const std::string &filename, bool all = false) = 0; + /*! + * \brief importOutputsFromString - imports outputs from string for UR + * \param filename + * \return - true on success + */ + virtual bool importOutputsFromString(const std::string &data) = 0; + /*! * \brief importOutputs - imports outputs from file * \param filename diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 3874ce8504..f8330baa74 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -13262,9 +13262,8 @@ crypto::public_key wallet2::get_tx_pub_key_from_received_outs(const tools::walle return tx_pub_key; } -bool wallet2::export_key_images(const std::string &filename, bool all) const +std::string wallet2::export_key_images_string(bool all) const { - PERF_TIMER(export_key_images); std::pair>> ski = export_key_images(all); std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; @@ -13287,8 +13286,13 @@ bool wallet2::export_key_images(const std::string &filename, bool all) const // encrypt data, keep magic plaintext PERF_TIMER(export_key_images_encrypt); - std::string ciphertext = encrypt_with_view_secret_key(data); - return save_to_file(filename, magic + ciphertext); + return magic + encrypt_with_view_secret_key(data); +} + +bool wallet2::export_key_images(const std::string &filename, bool all) const +{ + PERF_TIMER(export_key_images); + return save_to_file(filename, export_key_images_string(all)); } //---------------------------------------------------------------------------------------------------- @@ -13353,10 +13357,16 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent THROW_WALLET_EXCEPTION_IF(!r, error::wallet_internal_error, std::string(tr("failed to read file ")) + filename); + return import_key_images_string(data, spent, unspent); +} + +uint64_t wallet2::import_key_images_string(const std::string &keyImages, uint64_t &spent, uint64_t &unspent) +{ + std::string data = keyImages; const size_t magiclen = strlen(KEY_IMAGE_EXPORT_FILE_MAGIC); if (data.size() < magiclen || memcmp(data.data(), KEY_IMAGE_EXPORT_FILE_MAGIC, magiclen)) { - THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic in ") + filename); + THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Bad key image export file magic")); } try @@ -13366,24 +13376,24 @@ uint64_t wallet2::import_key_images(const std::string &filename, uint64_t &spent } catch (const std::exception &e) { - THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt ") + filename + ": " + e.what()); + THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string("Failed to decrypt: ") + e.what()); } const size_t headerlen = 4 + 2 * sizeof(crypto::public_key); - THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size from file ") + filename); + THROW_WALLET_EXCEPTION_IF(data.size() < headerlen, error::wallet_internal_error, std::string("Bad data size")); const uint32_t offset = (uint8_t)data[0] | (((uint8_t)data[1]) << 8) | (((uint8_t)data[2]) << 16) | (((uint8_t)data[3]) << 24); const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[4]; const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[4 + sizeof(crypto::public_key)]; const cryptonote::account_public_address &keys = get_account().get_keys().m_account_address; if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key) { - THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images from ") + filename + " are for a different account"); + THROW_WALLET_EXCEPTION(error::wallet_internal_error, std::string( "Key images are from a different account" )); } THROW_WALLET_EXCEPTION_IF(offset > m_transfers.size(), error::wallet_internal_error, "Offset larger than known outputs"); const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature); THROW_WALLET_EXCEPTION_IF((data.size() - headerlen) % record_size, - error::wallet_internal_error, std::string("Bad data size from file ") + filename); + error::wallet_internal_error, std::string("Bad data size")); size_t nki = (data.size() - headerlen) / record_size; std::vector> ski; diff --git a/src/wallet/wallet2.h b/src/wallet/wallet2.h index 7228f19077..1da82e734f 100644 --- a/src/wallet/wallet2.h +++ b/src/wallet/wallet2.h @@ -1630,10 +1630,12 @@ namespace tools void import_payments_out(const std::list> &confirmed_payments); std::tuple> export_blockchain() const; void import_blockchain(const std::tuple> &bc); + std::string export_key_images_string(bool all = false) const; bool export_key_images(const std::string &filename, bool all = false) const; std::pair>> export_key_images(bool all = false) const; uint64_t import_key_images(const std::vector> &signed_key_images, size_t offset, uint64_t &spent, uint64_t &unspent, bool check_spent = true); uint64_t import_key_images(const std::string &filename, uint64_t &spent, uint64_t &unspent); + uint64_t import_key_images_string(const std::string &data, uint64_t &spent, uint64_t &unspent); bool import_key_images(std::vector key_images, size_t offset=0, boost::optional> selected_transfers=boost::none); bool import_key_images(signed_tx_set & signed_tx, size_t offset=0, bool only_selected_transfers=false); crypto::public_key get_tx_pub_key_from_received_outs(const tools::wallet2::transfer_details &td) const; diff --git a/src/wallet/wallet_rpc_server.cpp b/src/wallet/wallet_rpc_server.cpp index 478bf37d8f..a6ef141f7e 100644 --- a/src/wallet/wallet_rpc_server.cpp +++ b/src/wallet/wallet_rpc_server.cpp @@ -56,6 +56,8 @@ using namespace epee; #include "rpc/core_rpc_server_commands_defs.h" #include "daemonizer/daemonizer.h" +#define KEY_IMAGE_EXPORT_FILE_MAGIC "Monero key image export\003" + #undef MONERO_DEFAULT_LOG_CATEGORY #define MONERO_DEFAULT_LOG_CATEGORY "wallet.rpc" @@ -3104,6 +3106,152 @@ namespace tools return true; } + //------------------------------------------------------------------------------------------------------------------------------ + bool wallet_rpc_server::on_export_encrypted_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_ENCRYPTED_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_ENCRYPTED_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx) +{ + if (!m_wallet) return not_open(er); + try + { + std::pair>> ski = m_wallet->export_key_images(req.all); + res.offset = ski.first; + + std::string data; + const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address; + + data.reserve(4 + sizeof(crypto::public_key) * 2 + ski.second.size() * (sizeof(crypto::key_image) + sizeof(crypto::signature))); + + data.resize(4); + data[0] = res.offset & 0xff; + data[1] = (res.offset >> 8) & 0xff; + data[2] = (res.offset >> 16) & 0xff; + data[3] = (res.offset >> 24) & 0xff; + + data += std::string((const char *)&keys.m_spend_public_key, sizeof(crypto::public_key)); + data += std::string((const char *)&keys.m_view_public_key, sizeof(crypto::public_key)); + + for (const auto &item : ski.second) + { + data += std::string((const char *)&item.first, sizeof(crypto::key_image)); + data += std::string((const char *)&item.second, sizeof(crypto::signature)); + } + + std::string ciphertext = m_wallet->encrypt_with_view_secret_key(data); + + std::string magic(KEY_IMAGE_EXPORT_FILE_MAGIC, strlen(KEY_IMAGE_EXPORT_FILE_MAGIC)); + res.encrypted_key_images_blob = epee::string_tools::buff_to_hex_nodelimer(magic + ciphertext); + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + + return true; +} +//------------------------------------------------------------------------------------------------------------------------------ +bool wallet_rpc_server::on_import_encrypted_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_ENCRYPTED_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_ENCRYPTED_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx) +{ + if (!m_wallet) return not_open(er); + if (m_restricted) + { + er.code = WALLET_RPC_ERROR_CODE_DENIED; + er.message = "Command unavailable in restricted mode."; + return false; + } + if (!m_wallet->is_trusted_daemon()) + { + er.code = WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR; + er.message = "This command requires a trusted daemon."; + return false; + } + try + { + std::string data; + if (!epee::string_tools::parse_hexstr_to_binbuff(req.encrypted_key_images_blob, data)) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE; + er.message = "Failed to parse encrypted key images blob"; + return false; + } + + const size_t magiclen = strlen(KEY_IMAGE_EXPORT_FILE_MAGIC); + if (data.size() < magiclen || memcmp(data.data(), KEY_IMAGE_EXPORT_FILE_MAGIC, magiclen)) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE; + er.message = "Bad key image export file magic in blob"; + return false; + } + + try + { + data = m_wallet->decrypt_with_view_secret_key(std::string(data, magiclen)); + } + catch (const std::exception& e) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE; + er.message = "Failed to decrypt blob"; + return false; + } + + const size_t headerlen = 4 + 2 * sizeof(crypto::public_key); + if (data.size() < headerlen) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE; + er.message = "Bad data size from file"; + return false; + } + + uint32_t offset = (uint8_t)data[0] | (((uint8_t)data[1]) << 8) | (((uint8_t)data[2]) << 16) | (((uint8_t)data[3]) << 24); + if (offset > m_wallet->get_num_transfer_details()) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE; + er.message = "Offset larger than known outputs"; + return false; + } + + const crypto::public_key &public_spend_key = *(const crypto::public_key*)&data[4]; + const crypto::public_key &public_view_key = *(const crypto::public_key*)&data[4 + sizeof(crypto::public_key)]; + const cryptonote::account_public_address &keys = m_wallet->get_account().get_keys().m_account_address; + if (public_spend_key != keys.m_spend_public_key || public_view_key != keys.m_view_public_key) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE; + er.message = "Key images from different account"; + return false; + } + + const size_t record_size = sizeof(crypto::key_image) + sizeof(crypto::signature); + if ((data.size() - headerlen) % record_size) + { + er.code = WALLET_RPC_ERROR_CODE_WRONG_KEY_IMAGE; + er.message = "Bad data size from file"; + return false; + } + size_t nki = (data.size() - headerlen) / record_size; + + std::vector> ski; + ski.reserve(nki); + for (size_t n = 0; n < nki; ++n) + { + crypto::key_image key_image = *reinterpret_cast(&data[headerlen + n * record_size]); + crypto::signature signature = *reinterpret_cast(&data[headerlen + n * record_size + sizeof(crypto::key_image)]); + + ski.push_back(std::make_pair(key_image, signature)); + } + + uint64_t spent = 0, unspent = 0; + uint64_t height = m_wallet->import_key_images(ski, offset, spent, unspent); + res.height = height; + res.spent = spent; + res.unspent = unspent; + } + catch (const std::exception& e) + { + handle_rpc_exception(std::current_exception(), er, WALLET_RPC_ERROR_CODE_UNKNOWN_ERROR); + return false; + } + + return true; +} //------------------------------------------------------------------------------------------------------------------------------ bool wallet_rpc_server::on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er, const connection_context *ctx) { diff --git a/src/wallet/wallet_rpc_server.h b/src/wallet/wallet_rpc_server.h index f118f581a6..af8a5f234d 100644 --- a/src/wallet/wallet_rpc_server.h +++ b/src/wallet/wallet_rpc_server.h @@ -124,7 +124,9 @@ namespace tools MAP_JON_RPC_WE("export_outputs", on_export_outputs, wallet_rpc::COMMAND_RPC_EXPORT_OUTPUTS) MAP_JON_RPC_WE("import_outputs", on_import_outputs, wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS) MAP_JON_RPC_WE("export_key_images", on_export_key_images, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES) - MAP_JON_RPC_WE("import_key_images", on_import_key_images, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES) + MAP_JON_RPC_WE("import_key_images", on_import_key_images, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES) + MAP_JON_RPC_WE("export_encrypted_key_images", on_export_encrypted_key_images, wallet_rpc::COMMAND_RPC_EXPORT_ENCRYPTED_KEY_IMAGES) + MAP_JON_RPC_WE("import_encrypted_key_images", on_import_encrypted_key_images, wallet_rpc::COMMAND_RPC_IMPORT_ENCRYPTED_KEY_IMAGES) MAP_JON_RPC_WE("make_uri", on_make_uri, wallet_rpc::COMMAND_RPC_MAKE_URI) MAP_JON_RPC_WE("parse_uri", on_parse_uri, wallet_rpc::COMMAND_RPC_PARSE_URI) MAP_JON_RPC_WE("get_address_book", on_get_address_book, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY) @@ -220,6 +222,8 @@ namespace tools bool on_import_outputs(const wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::request& req, wallet_rpc::COMMAND_RPC_IMPORT_OUTPUTS::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_export_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_import_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_export_encrypted_key_images(const wallet_rpc::COMMAND_RPC_EXPORT_ENCRYPTED_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_EXPORT_ENCRYPTED_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); + bool on_import_encrypted_key_images(const wallet_rpc::COMMAND_RPC_IMPORT_ENCRYPTED_KEY_IMAGES::request& req, wallet_rpc::COMMAND_RPC_IMPORT_ENCRYPTED_KEY_IMAGES::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_make_uri(const wallet_rpc::COMMAND_RPC_MAKE_URI::request& req, wallet_rpc::COMMAND_RPC_MAKE_URI::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_parse_uri(const wallet_rpc::COMMAND_RPC_PARSE_URI::request& req, wallet_rpc::COMMAND_RPC_PARSE_URI::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); bool on_get_address_book(const wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::request& req, wallet_rpc::COMMAND_RPC_GET_ADDRESS_BOOK_ENTRY::response& res, epee::json_rpc::error& er, const connection_context *ctx = NULL); diff --git a/src/wallet/wallet_rpc_server_commands_defs.h b/src/wallet/wallet_rpc_server_commands_defs.h index ab7898299e..9ae3881ed3 100644 --- a/src/wallet/wallet_rpc_server_commands_defs.h +++ b/src/wallet/wallet_rpc_server_commands_defs.h @@ -1025,7 +1025,7 @@ namespace wallet_rpc }; typedef epee::misc_utils::struct_init response; }; - + struct transfer_details { uint64_t amount; @@ -1825,6 +1825,60 @@ namespace wallet_rpc typedef epee::misc_utils::struct_init response; }; + struct COMMAND_RPC_EXPORT_ENCRYPTED_KEY_IMAGES + { + struct request_t + { + bool all; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(all, false) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init request; + + struct response_t + { + uint32_t offset; + std::string encrypted_key_images_blob; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(offset) + KV_SERIALIZE(encrypted_key_images_blob) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init response; + }; + + struct COMMAND_RPC_IMPORT_ENCRYPTED_KEY_IMAGES + { + struct request_t + { + uint32_t offset; + std::string encrypted_key_images_blob; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE_OPT(offset, (uint32_t)0) + KV_SERIALIZE(encrypted_key_images_blob) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init request; + + struct response_t + { + uint64_t height; + uint64_t spent; + uint64_t unspent; + + BEGIN_KV_SERIALIZE_MAP() + KV_SERIALIZE(height) + KV_SERIALIZE(spent) + KV_SERIALIZE(unspent) + END_KV_SERIALIZE_MAP() + }; + typedef epee::misc_utils::struct_init response; + }; + struct uri_spec { std::string address; @@ -2310,7 +2364,7 @@ namespace wallet_rpc }; typedef epee::misc_utils::struct_init response; }; - + struct COMMAND_RPC_IS_MULTISIG { struct request_t