-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathexport_outputs.cpp
More file actions
83 lines (73 loc) · 3.86 KB
/
Copy pathexport_outputs.cpp
File metadata and controls
83 lines (73 loc) · 3.86 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
// export_outputs: dumps every known output (m_transfers) from a synced
// wallet into a single JSON file.
#include "wallet/wallet2.h"
#include "cryptonote_basic/cryptonote_format_utils.h"
#include "string_tools.h"
#include <fstream>
#include <iostream>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
int main(int argc, char** argv) {
if (argc != 4) {
std::cerr << "usage: export_outputs <wallet_file> <password> <output.json>\n";
return 1;
}
tools::wallet2 wallet(cryptonote::STAGENET, 1, true);
wallet.load(argv[1], argv[2]);
tools::wallet2::transfer_container transfers;
wallet.get_transfers(transfers);
json out = json::array();
for (auto& td : transfers) {
crypto::public_key tx_pub_key = cryptonote::get_tx_pub_key_from_extra(td.m_tx);
// On multi-destination transactions (e.g. a mix of subaddress
// and standard addresses), each output can have its own
// "additional" tx_pub_key. We check the main key against the
// actual on-chain output key and fall back to the additional
// keys list if it doesn't match, rather than guessing.
crypto::public_key actual_out_key = boost::get<cryptonote::txout_to_tagged_key>(
td.m_tx.vout[td.m_internal_output_index].target).key;
crypto::key_derivation test_d;
crypto::public_key test_p;
crypto::generate_key_derivation(tx_pub_key, wallet.get_account().get_keys().m_view_secret_key, test_d);
crypto::derive_public_key(test_d, td.m_internal_output_index,
(td.m_subaddr_index.major == 0 && td.m_subaddr_index.minor == 0)
? wallet.get_account().get_keys().m_account_address.m_spend_public_key
: wallet.get_subaddress(td.m_subaddr_index).m_spend_public_key, test_p);
if (test_p != actual_out_key) {
std::vector<crypto::public_key> additional = cryptonote::get_additional_tx_pub_keys_from_extra(td.m_tx);
if (td.m_internal_output_index < additional.size())
tx_pub_key = additional[td.m_internal_output_index];
// Re-verify the key we just resolved (from additional, or
// still the unresolved main key) against the actual output
// before trusting it.
crypto::key_derivation retest_d;
crypto::public_key retest_p;
crypto::generate_key_derivation(tx_pub_key, wallet.get_account().get_keys().m_view_secret_key, retest_d);
crypto::derive_public_key(retest_d, td.m_internal_output_index,
(td.m_subaddr_index.major == 0 && td.m_subaddr_index.minor == 0)
? wallet.get_account().get_keys().m_account_address.m_spend_public_key
: wallet.get_subaddress(td.m_subaddr_index).m_spend_public_key, retest_p);
if (retest_p != actual_out_key) {
std::cerr << "ERROR: could not resolve tx_pub_key (tx_hash=" << epee::string_tools::pod_to_hex(td.m_txid)
<< ", output_index=" << td.m_internal_output_index << "). Skipping this output.\n";
continue;
}
}
crypto::public_key tx_pub_key_final = tx_pub_key;
json entry;
entry["global_index"] = td.m_global_output_index;
entry["subaddr_major"] = td.m_subaddr_index.major;
entry["subaddr_minor"] = td.m_subaddr_index.minor;
entry["amount"] = td.m_amount;
entry["block_height"] = td.m_block_height;
entry["real_out_tx_key"] = epee::string_tools::pod_to_hex(tx_pub_key);
entry["real_output_in_tx_index"] = td.m_internal_output_index;
entry["spent"] = td.m_spent;
entry["tx_hash"] = epee::string_tools::pod_to_hex(td.m_txid);
out.push_back(entry);
}
std::ofstream f(argv[3]);
f << out.dump(2);
std::cerr << "Written: " << argv[3] << " (" << transfers.size() << " outputs)\n";
return 0;
}