diff options
author | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2016-07-15 12:11:55 +0100 |
---|---|---|
committer | moneromooo-monero <moneromooo-monero@users.noreply.github.com> | 2016-07-24 09:23:30 +0100 |
commit | ebf97d76f0020eb027175818ebbdafd6d578aa77 (patch) | |
tree | 5efd79bff6431a845b88f007bf4db783118ce272 /src/wallet/wallet2.cpp | |
parent | Merge pull request #923 (diff) | |
download | monero-ebf97d76f0020eb027175818ebbdafd6d578aa77.tar.xz |
wallet: new {ex,im}port_key_images commands and RPC calls
They are used to export a signed set of key images from a wallet
with a private spend key, so an auditor with the matching view key
may see which of those are spent, and which are not.
Diffstat (limited to 'src/wallet/wallet2.cpp')
-rw-r--r-- | src/wallet/wallet2.cpp | 116 |
1 files changed, 116 insertions, 0 deletions
diff --git a/src/wallet/wallet2.cpp b/src/wallet/wallet2.cpp index 201f0a1f4..43dedaf82 100644 --- a/src/wallet/wallet2.cpp +++ b/src/wallet/wallet2.cpp @@ -3286,7 +3286,123 @@ bool wallet2::verify(const std::string &data, const cryptonote::account_public_a memcpy(&s, decoded.data(), sizeof(s)); return crypto::check_signature(hash, address.m_spend_public_key, s); } +//---------------------------------------------------------------------------------------------------- +std::vector<std::pair<crypto::key_image, crypto::signature>> wallet2::export_key_images() const +{ + std::vector<std::pair<crypto::key_image, crypto::signature>> ski; + + ski.reserve(m_transfers.size()); + for (size_t n = 0; n < m_transfers.size(); ++n) + { + const transfer_details &td = m_transfers[n]; + + crypto::hash hash; + crypto::cn_fast_hash(&td.m_key_image, sizeof(td.m_key_image), hash); + + // get ephemeral public key + const cryptonote::tx_out &out = td.m_tx.vout[td.m_internal_output_index]; + THROW_WALLET_EXCEPTION_IF(out.target.type() != typeid(txout_to_key), error::wallet_internal_error, + "Output is not txout_to_key"); + const cryptonote::txout_to_key &o = boost::get<const cryptonote::txout_to_key>(out.target); + const crypto::public_key pkey = o.key; + + // get tx pub key + std::vector<tx_extra_field> tx_extra_fields; + if(!parse_tx_extra(td.m_tx.extra, tx_extra_fields)) + { + // Extra may only be partially parsed, it's OK if tx_extra_fields contains public key + } + tx_extra_pub_key pub_key_field; + THROW_WALLET_EXCEPTION_IF(!find_tx_extra_field_by_type(tx_extra_fields, pub_key_field), error::wallet_internal_error, + "Public key wasn't found in the transaction extra"); + crypto::public_key tx_pub_key = pub_key_field.pub_key; + + // generate ephemeral secret key + crypto::key_image ki; + cryptonote::keypair in_ephemeral; + cryptonote::generate_key_image_helper(m_account.get_keys(), tx_pub_key, td.m_internal_output_index, in_ephemeral, ki); + THROW_WALLET_EXCEPTION_IF(ki != td.m_key_image, + error::wallet_internal_error, "key_image generated not matched with cached key image"); + THROW_WALLET_EXCEPTION_IF(in_ephemeral.pub != pkey, + error::wallet_internal_error, "key_image generated ephemeral public key not matched with output_key"); + // sign the key image with the output secret key + crypto::signature signature; + std::vector<const crypto::public_key*> key_ptrs; + key_ptrs.push_back(&pkey); + + crypto::generate_ring_signature((const crypto::hash&)td.m_key_image, td.m_key_image, key_ptrs, in_ephemeral.sec, 0, &signature); + + ski.push_back(std::make_pair(td.m_key_image, signature)); + } + return ski; +} +//---------------------------------------------------------------------------------------------------- +uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_image, crypto::signature>> &signed_key_images, uint64_t &spent, uint64_t &unspent) +{ + COMMAND_RPC_IS_KEY_IMAGE_SPENT::request req = AUTO_VAL_INIT(req); + COMMAND_RPC_IS_KEY_IMAGE_SPENT::response daemon_resp = AUTO_VAL_INIT(daemon_resp); + + THROW_WALLET_EXCEPTION_IF(signed_key_images.size() > m_transfers.size(), error::wallet_internal_error, + "The blockchain is out of date compared to the signed key images"); + + if (signed_key_images.empty()) + { + spent = 0; + unspent = 0; + return 0; + } + + for (size_t n = 0; n < signed_key_images.size(); ++n) + { + const transfer_details &td = m_transfers[n]; + const crypto::key_image &key_image = signed_key_images[n].first; + const crypto::signature &signature = signed_key_images[n].second; + + // get ephemeral public key + const cryptonote::tx_out &out = td.m_tx.vout[td.m_internal_output_index]; + THROW_WALLET_EXCEPTION_IF(out.target.type() != typeid(txout_to_key), error::wallet_internal_error, + "Non txout_to_key output found"); + const cryptonote::txout_to_key &o = boost::get<cryptonote::txout_to_key>(out.target); + const crypto::public_key pkey = o.key; + + std::vector<const crypto::public_key*> pkeys; + pkeys.push_back(&pkey); + THROW_WALLET_EXCEPTION_IF(!crypto::check_ring_signature((const crypto::hash&)key_image, key_image, pkeys, &signature), + error::wallet_internal_error, "Signature check failed: key image " + epee::string_tools::pod_to_hex(key_image) + + ", signature " + epee::string_tools::pod_to_hex(signature) + ", pubkey " + epee::string_tools::pod_to_hex(*pkeys[0])); + + req.key_images.push_back(epee::string_tools::pod_to_hex(key_image)); + } + + m_daemon_rpc_mutex.lock(); + bool r = epee::net_utils::invoke_http_json_remote_command2(m_daemon_address + "/is_key_image_spent", req, daemon_resp, m_http_client, 200000); + m_daemon_rpc_mutex.unlock(); + THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "is_key_image_spent"); + THROW_WALLET_EXCEPTION_IF(daemon_resp.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "is_key_image_spent"); + THROW_WALLET_EXCEPTION_IF(daemon_resp.status != CORE_RPC_STATUS_OK, error::is_key_image_spent_error, daemon_resp.status); + THROW_WALLET_EXCEPTION_IF(daemon_resp.spent_status.size() != signed_key_images.size(), error::wallet_internal_error, + "daemon returned wrong response for is_key_image_spent, wrong amounts count = " + + std::to_string(daemon_resp.spent_status.size()) + ", expected " + std::to_string(signed_key_images.size())); + + spent = 0; + unspent = 0; + for (size_t n = 0; n < daemon_resp.spent_status.size(); ++n) + { + transfer_details &td = m_transfers[n]; + uint64_t amount = td.m_tx.vout[td.m_internal_output_index].amount; + td.m_spent = daemon_resp.spent_status[n] != COMMAND_RPC_IS_KEY_IMAGE_SPENT::UNSPENT; + if (td.m_spent) + spent += amount; + else + unspent += amount; + LOG_PRINT_L2("Transfer " << n << ": " << print_money(amount) << " (" << td.m_global_output_index << "): " + << (td.m_spent ? "spent" : "unspent") << " (key image " << req.key_images[n] << ")"); + } + LOG_PRINT_L1("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent"); + + return m_transfers[signed_key_images.size() - 1].m_block_height; +} //---------------------------------------------------------------------------------------------------- void wallet2::generate_genesis(cryptonote::block& b) { if (m_testnet) |