wallet2: correctly handle in/out payments for view-only wallet
This commit is contained in:
parent
a6403846ef
commit
b5cbdce8a6
|
@ -510,11 +510,12 @@ namespace cryptonote
|
||||||
e.in_pool = pool_tx_hashes.find(tx_hash) != pool_tx_hashes.end();
|
e.in_pool = pool_tx_hashes.find(tx_hash) != pool_tx_hashes.end();
|
||||||
if (e.in_pool)
|
if (e.in_pool)
|
||||||
{
|
{
|
||||||
e.block_height = std::numeric_limits<uint64_t>::max();
|
e.block_height = e.block_timestamp = std::numeric_limits<uint64_t>::max();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
e.block_height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash);
|
e.block_height = m_core.get_blockchain_storage().get_db().get_tx_block_height(tx_hash);
|
||||||
|
e.block_timestamp = m_core.get_blockchain_storage().get_db().get_block_timestamp(e.block_height);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill up old style responses too, in case an old wallet asks
|
// fill up old style responses too, in case an old wallet asks
|
||||||
|
|
|
@ -49,7 +49,7 @@ namespace cryptonote
|
||||||
// advance which version they will stop working with
|
// advance which version they will stop working with
|
||||||
// Don't go over 32767 for any of these
|
// Don't go over 32767 for any of these
|
||||||
#define CORE_RPC_VERSION_MAJOR 1
|
#define CORE_RPC_VERSION_MAJOR 1
|
||||||
#define CORE_RPC_VERSION_MINOR 13
|
#define CORE_RPC_VERSION_MINOR 14
|
||||||
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
|
#define MAKE_CORE_RPC_VERSION(major,minor) (((major)<<16)|(minor))
|
||||||
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
|
#define CORE_RPC_VERSION MAKE_CORE_RPC_VERSION(CORE_RPC_VERSION_MAJOR, CORE_RPC_VERSION_MINOR)
|
||||||
|
|
||||||
|
@ -215,6 +215,7 @@ namespace cryptonote
|
||||||
std::string as_json;
|
std::string as_json;
|
||||||
bool in_pool;
|
bool in_pool;
|
||||||
uint64_t block_height;
|
uint64_t block_height;
|
||||||
|
uint64_t block_timestamp;
|
||||||
std::vector<uint64_t> output_indices;
|
std::vector<uint64_t> output_indices;
|
||||||
|
|
||||||
BEGIN_KV_SERIALIZE_MAP()
|
BEGIN_KV_SERIALIZE_MAP()
|
||||||
|
@ -223,6 +224,7 @@ namespace cryptonote
|
||||||
KV_SERIALIZE(as_json)
|
KV_SERIALIZE(as_json)
|
||||||
KV_SERIALIZE(in_pool)
|
KV_SERIALIZE(in_pool)
|
||||||
KV_SERIALIZE(block_height)
|
KV_SERIALIZE(block_height)
|
||||||
|
KV_SERIALIZE(block_timestamp)
|
||||||
KV_SERIALIZE(output_indices)
|
KV_SERIALIZE(output_indices)
|
||||||
END_KV_SERIALIZE_MAP()
|
END_KV_SERIALIZE_MAP()
|
||||||
};
|
};
|
||||||
|
|
|
@ -5381,6 +5381,9 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
|
||||||
}
|
}
|
||||||
spent = 0;
|
spent = 0;
|
||||||
unspent = 0;
|
unspent = 0;
|
||||||
|
std::unordered_set<crypto::hash> spent_txids; // For each spent key image, search for a tx in m_transfers that uses it as input.
|
||||||
|
std::vector<size_t> swept_transfers; // If such a spending tx wasn't found in m_transfers, this means the spending tx
|
||||||
|
// was created by sweep_all, so we can't know the spent height and other detailed info.
|
||||||
for(size_t i = 0; i < m_transfers.size(); ++i)
|
for(size_t i = 0; i < m_transfers.size(); ++i)
|
||||||
{
|
{
|
||||||
transfer_details &td = m_transfers[i];
|
transfer_details &td = m_transfers[i];
|
||||||
|
@ -5391,8 +5394,146 @@ uint64_t wallet2::import_key_images(const std::vector<std::pair<crypto::key_imag
|
||||||
unspent += amount;
|
unspent += amount;
|
||||||
LOG_PRINT_L2("Transfer " << i << ": " << print_money(amount) << " (" << td.m_global_output_index << "): "
|
LOG_PRINT_L2("Transfer " << i << ": " << print_money(amount) << " (" << td.m_global_output_index << "): "
|
||||||
<< (td.m_spent ? "spent" : "unspent") << " (key image " << req.key_images[i] << ")");
|
<< (td.m_spent ? "spent" : "unspent") << " (key image " << req.key_images[i] << ")");
|
||||||
|
|
||||||
|
if (i < daemon_resp.spent_status.size() && daemon_resp.spent_status[i] == COMMAND_RPC_IS_KEY_IMAGE_SPENT::SPENT_IN_BLOCKCHAIN)
|
||||||
|
{
|
||||||
|
bool is_spent_tx_found = false;
|
||||||
|
for (auto it = m_transfers.rbegin(); &(*it) != &td; ++it)
|
||||||
|
{
|
||||||
|
bool is_spent_tx = false;
|
||||||
|
for(const cryptonote::txin_v& in : it->m_tx.vin)
|
||||||
|
{
|
||||||
|
if(in.type() == typeid(cryptonote::txin_to_key) && td.m_key_image == boost::get<cryptonote::txin_to_key>(in).k_image)
|
||||||
|
{
|
||||||
|
is_spent_tx = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (is_spent_tx)
|
||||||
|
{
|
||||||
|
is_spent_tx_found = true;
|
||||||
|
spent_txids.insert(it->m_txid);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!is_spent_tx_found)
|
||||||
|
swept_transfers.push_back(i);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent");
|
MDEBUG("Total: " << print_money(spent) << " spent, " << print_money(unspent) << " unspent");
|
||||||
|
|
||||||
|
if (check_spent)
|
||||||
|
{
|
||||||
|
// query outgoing txes
|
||||||
|
COMMAND_RPC_GET_TRANSACTIONS::request gettxs_req;
|
||||||
|
COMMAND_RPC_GET_TRANSACTIONS::response gettxs_res;
|
||||||
|
gettxs_req.decode_as_json = false;
|
||||||
|
for (const crypto::hash& spent_txid : spent_txids)
|
||||||
|
gettxs_req.txs_hashes.push_back(epee::string_tools::pod_to_hex(spent_txid));
|
||||||
|
m_daemon_rpc_mutex.lock();
|
||||||
|
bool r = epee::net_utils::invoke_http_json("/gettransactions", gettxs_req, gettxs_res, m_http_client, rpc_timeout);
|
||||||
|
m_daemon_rpc_mutex.unlock();
|
||||||
|
THROW_WALLET_EXCEPTION_IF(!r, error::no_connection_to_daemon, "gettransactions");
|
||||||
|
THROW_WALLET_EXCEPTION_IF(gettxs_res.status == CORE_RPC_STATUS_BUSY, error::daemon_busy, "gettransactions");
|
||||||
|
THROW_WALLET_EXCEPTION_IF(gettxs_res.txs.size() != spent_txids.size(), error::wallet_internal_error,
|
||||||
|
"daemon returned wrong response for gettransactions, wrong count = " + std::to_string(gettxs_res.txs.size()) + ", expected " + std::to_string(spent_txids.size()));
|
||||||
|
|
||||||
|
// process each outgoing tx
|
||||||
|
auto spent_txid = spent_txids.begin();
|
||||||
|
for (const COMMAND_RPC_GET_TRANSACTIONS::entry& e : gettxs_res.txs)
|
||||||
|
{
|
||||||
|
THROW_WALLET_EXCEPTION_IF(e.in_pool, error::wallet_internal_error, "spent tx isn't supposed to be in txpool");
|
||||||
|
|
||||||
|
// parse tx
|
||||||
|
cryptonote::blobdata bd;
|
||||||
|
THROW_WALLET_EXCEPTION_IF(!epee::string_tools::parse_hexstr_to_binbuff(e.as_hex, bd), error::wallet_internal_error, "parse_hexstr_to_binbuff failed");
|
||||||
|
cryptonote::transaction spent_tx;
|
||||||
|
crypto::hash spnet_txid_parsed, spent_txid_prefix;
|
||||||
|
THROW_WALLET_EXCEPTION_IF(!cryptonote::parse_and_validate_tx_from_blob(bd, spent_tx, spnet_txid_parsed, spent_txid_prefix), error::wallet_internal_error, "parse_and_validate_tx_from_blob failed");
|
||||||
|
THROW_WALLET_EXCEPTION_IF(*spent_txid != spnet_txid_parsed, error::wallet_internal_error, "parsed txid mismatch");
|
||||||
|
|
||||||
|
// get received (change) amount
|
||||||
|
uint64_t tx_money_got_in_outs = 0;
|
||||||
|
const cryptonote::account_keys& keys = m_account.get_keys();
|
||||||
|
const crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(spent_tx);
|
||||||
|
crypto::key_derivation derivation;
|
||||||
|
generate_key_derivation(tx_pub_key, keys.m_view_secret_key, derivation);
|
||||||
|
size_t output_index = 0;
|
||||||
|
for (const cryptonote::tx_out& out : spent_tx.vout)
|
||||||
|
{
|
||||||
|
uint64_t money_transfered = 0;
|
||||||
|
bool error = false, received = false;
|
||||||
|
check_acc_out_precomp(keys.m_account_address.m_spend_public_key, out, derivation, output_index, received, money_transfered, error);
|
||||||
|
THROW_WALLET_EXCEPTION_IF(error, error::wallet_internal_error, "check_acc_out_precomp failed");
|
||||||
|
if (received)
|
||||||
|
{
|
||||||
|
if (money_transfered == 0)
|
||||||
|
{
|
||||||
|
rct::key mask;
|
||||||
|
money_transfered = tools::decodeRct(spent_tx.rct_signatures, tx_pub_key, keys.m_view_secret_key, output_index, mask);
|
||||||
|
}
|
||||||
|
tx_money_got_in_outs += money_transfered;
|
||||||
|
}
|
||||||
|
++output_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get spent amount
|
||||||
|
uint64_t tx_money_spent_in_ins = 0;
|
||||||
|
for (const cryptonote::txin_v& in : spent_tx.vin)
|
||||||
|
{
|
||||||
|
if (in.type() != typeid(cryptonote::txin_to_key))
|
||||||
|
continue;
|
||||||
|
auto it = m_key_images.find(boost::get<cryptonote::txin_to_key>(in).k_image);
|
||||||
|
if (it != m_key_images.end())
|
||||||
|
{
|
||||||
|
const transfer_details& td = m_transfers[it->second];
|
||||||
|
uint64_t amount = boost::get<cryptonote::txin_to_key>(in).amount;
|
||||||
|
if (amount > 0)
|
||||||
|
{
|
||||||
|
THROW_WALLET_EXCEPTION_IF(amount != td.amount(), error::wallet_internal_error,
|
||||||
|
std::string("Inconsistent amount in tx input: got ") + print_money(amount) +
|
||||||
|
std::string(", expected ") + print_money(td.amount()));
|
||||||
|
}
|
||||||
|
amount = td.amount();
|
||||||
|
tx_money_spent_in_ins += amount;
|
||||||
|
|
||||||
|
LOG_PRINT_L0("Spent money: " << print_money(amount) << ", with tx: " << *spent_txid);
|
||||||
|
set_spent(it->second, e.block_height);
|
||||||
|
if (m_callback)
|
||||||
|
m_callback->on_money_spent(e.block_height, *spent_txid, spent_tx, amount, spent_tx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// create outgoing payment
|
||||||
|
process_outgoing(*spent_txid, spent_tx, e.block_height, e.block_timestamp, tx_money_spent_in_ins, tx_money_got_in_outs);
|
||||||
|
|
||||||
|
// erase corresponding incoming payment
|
||||||
|
for (auto j = m_payments.begin(); j != m_payments.end(); ++j)
|
||||||
|
{
|
||||||
|
if (j->second.m_tx_hash == *spent_txid)
|
||||||
|
{
|
||||||
|
m_payments.erase(j);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
++spent_txid;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (size_t n : swept_transfers)
|
||||||
|
{
|
||||||
|
const transfer_details& td = m_transfers[n];
|
||||||
|
confirmed_transfer_details pd;
|
||||||
|
pd.m_change = (uint64_t)-1; // cahnge is unknown
|
||||||
|
pd.m_amount_in = pd.m_amount_out = td.amount(); // fee is unknown
|
||||||
|
std::string err;
|
||||||
|
pd.m_block_height = get_daemon_blockchain_height(err); // spent block height is unknown, so hypothetically set to the highest
|
||||||
|
crypto::hash spent_txid = crypto::rand<crypto::hash>(); // spent txid is unknown, so hypothetically set to random
|
||||||
|
m_confirmed_txs.insert(std::make_pair(spent_txid, pd));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return m_transfers[signed_key_images.size() - 1].m_block_height;
|
return m_transfers[signed_key_images.size() - 1].m_block_height;
|
||||||
}
|
}
|
||||||
wallet2::payment_container wallet2::export_payments() const
|
wallet2::payment_container wallet2::export_payments() const
|
||||||
|
|
Loading…
Reference in New Issue