mirror of https://github.com/oxen-io/oxen-core.git
119 lines
3.7 KiB
C++
119 lines
3.7 KiB
C++
#include "transaction_scanner.hpp"
|
|
|
|
#include "block_tx.hpp"
|
|
|
|
#include <sqlitedb/database.hpp>
|
|
#include <common/string_util.h>
|
|
|
|
#include <vector>
|
|
|
|
namespace wallet
|
|
{
|
|
namespace log = oxen::log;
|
|
static auto logcat = log::Cat("wallet.wallet3");
|
|
|
|
std::vector<Output>
|
|
TransactionScanner::scan_received(
|
|
const BlockTX& tx, int64_t height, int64_t timestamp)
|
|
{
|
|
const auto tx_public_keys = tx.tx.get_public_keys();
|
|
|
|
std::vector<Output> received_outputs;
|
|
|
|
if (tx_public_keys.empty())
|
|
{
|
|
log::warning(logcat, "TransactionScanner found no tx public keys in transaction with hash <{}>.", tx.hash);
|
|
return {};
|
|
}
|
|
if (tx.tx.vout.size() != tx.global_indices.size())
|
|
{
|
|
throw std::invalid_argument(
|
|
"Invalid wallet::BlockTX, created outputs count != global indices count.");
|
|
}
|
|
|
|
auto derivations = wallet_keys->generate_key_derivations(tx_public_keys);
|
|
|
|
// Output belongs to public key derived as follows:
|
|
// let `Hs` := hash_to_scalar
|
|
// let `B` := recipient public spend key
|
|
// let `R` := transaction public key
|
|
// `out_key = Hs(R || output_index)*G + B`
|
|
//
|
|
// Output belongs to us if we have a public key B such that
|
|
// `out_key - Hs(R || output_index) * G == B`
|
|
for (size_t output_index = 0; output_index < tx.tx.vout.size(); output_index++)
|
|
{
|
|
const auto& output = tx.tx.vout[output_index];
|
|
|
|
if (auto* output_target = std::get_if<cryptonote::txout_to_key>(&output.target))
|
|
{
|
|
size_t derivation_index = 0;
|
|
std::optional<cryptonote::subaddress_index> sub_index{std::nullopt};
|
|
for (derivation_index = 0; derivation_index < derivations.size(); derivation_index++)
|
|
{
|
|
sub_index = wallet_keys->output_and_derivation_ours(
|
|
derivations[derivation_index], output_target->key, output_index);
|
|
if (sub_index)
|
|
break;
|
|
}
|
|
|
|
if (not sub_index)
|
|
continue; // not ours, move on to the next output
|
|
|
|
// TODO: device "conceal derivation" as needed
|
|
|
|
auto key_image = wallet_keys->key_image(
|
|
derivations[derivation_index], output_target->key, output_index, *sub_index);
|
|
|
|
Output o;
|
|
|
|
// TODO: ringct mask returned by reference. ugh.
|
|
std::tie(o.amount, o.rct_mask) = wallet_keys->output_amount_and_mask(
|
|
tx.tx.rct_signatures, derivations[derivation_index], output_index);
|
|
|
|
o.key_image = key_image;
|
|
o.subaddress_index = *sub_index;
|
|
o.output_index = output_index;
|
|
o.global_index = tx.global_indices[output_index];
|
|
o.tx_hash = tx.hash;
|
|
o.tx_public_key = tx_public_keys[0];
|
|
o.block_height = height;
|
|
o.block_time = timestamp;
|
|
o.unlock_time = tx.tx.get_unlock_time(output_index);
|
|
o.key = output_target->key;
|
|
o.derivation = derivations[derivation_index];
|
|
|
|
received_outputs.push_back(std::move(o));
|
|
}
|
|
else
|
|
{
|
|
throw std::invalid_argument("Invalid output target variant, only txout_to_key is valid.");
|
|
}
|
|
}
|
|
|
|
return received_outputs;
|
|
}
|
|
|
|
std::vector<crypto::key_image>
|
|
TransactionScanner::scan_spent(const cryptonote::transaction& tx)
|
|
{
|
|
std::vector<crypto::key_image> spends;
|
|
|
|
for (size_t input_index = 0; input_index < tx.vin.size(); input_index++)
|
|
{
|
|
const auto& input_variant = tx.vin[input_index];
|
|
if (auto* input = std::get_if<cryptonote::txin_to_key>(&input_variant))
|
|
{
|
|
auto our_spend = db->prepared_get<int>(
|
|
"SELECT COUNT(*) FROM key_images WHERE key_image = ?",
|
|
tools::type_to_hex(input->k_image));
|
|
|
|
if (our_spend > 0)
|
|
spends.push_back(input->k_image);
|
|
}
|
|
}
|
|
return spends;
|
|
}
|
|
|
|
} // namespace wallet
|