exmaple.cpp and README updated

pull/3/head
moneroexamples 5 years ago
parent fa4707c084
commit c83ca9c071

@ -1,6 +1,6 @@
cmake_minimum_required(VERSION 3.8)
set(PROJECT_NAME xmregcore)
set(PROJECT_NAME xmregcore_example)
project(${PROJECT_NAME})
@ -18,7 +18,7 @@ include(MyUtils)
find_package(Monero)
add_executable(${PROJECT_NAME}
main.cpp)
example.cpp)
add_subdirectory(src/)

@ -1,27 +1,134 @@
# Core moneroexamples repositry
# Core repository of moneroexamples
This repository includes code that is oftenly used among moneroexamples projects.
It includes:
This repository includes code that is oftenly used among moneroexamples projects. It includes:
- classess for decoding outputs/inputs, payment ids
- general utility tools (e.g., get default monero blockchain path)
- unified representation of monero addresses/accounts
- identification of outputs for subaddresses based on primary address
- classess for decoding outputs/inputs, payment ids,
- general utility tools (e.g., get default monero blockchain path),
- unified representation of monero addresses/accounts,
- identification of outputs for subaddresses based on primary address,
- estimation of possible spendings based on address and viewkey.
# Example usage
#### Monero download and compilation
More examples along with its full code are located in [example.cpp](https://github.com/moneroexamples/xmregcore/blob/master/example.cpp).
### Identify outputs in a tx based on address and viewkey with subaddresses
```C++
// use Monero forum donation address and viewkwey.
// will search for outputs in a give tx to
// to the primary address and its subaddresses.
auto primary_account = xmreg::make_primaryaccount(
"45ttEikQEZWN1m7VxaVN9rjQkpSdmpGZ82GwUps66neQ1PqbQMno4wMY8F5jiDt2GoHzCtMwa7PDPJUJYb1GYrMP4CwAwNp",
"c9347bc1e101eab46d3a6532c5b6066e925f499b47d285d5720e6a6f4cc4350c");
// if we want to analyze subaddresses, we need to generate
// an initial set of 10'000 possible subaddresses
primary_account->populate_subaddress_indices();
auto tx = get_tx("54cef43983ca9eeded46c6cc1091bf6f689d91faf78e306a5ac6169e981105d8");
// so now we can create an instance of a universal identifier
// which is going to identify outputs in a given tx using
// address and viewkey data. the search will include subaddresses.
auto identifier = make_identifier(*tx,
make_unique<xmreg::Output>(primary_account.get()));
identifier.identify();
// get the results of the identification
auto outputs_found = identifier.get<xmreg::Output>()->get();
cout << "Found following outputs in tx " << tx << ":\n"
<< outputs_found << '\n';
```
Identified output for `0.081774999238` xmr is for a subaddress of index `0/10`
which in this case is for the
["xiphon part time coding (3 months)"](https://ccs.getmonero.org/proposals/xiphon-part-time.html)
proposal.
### Possible spending based on address and viewkey
```C++
// use offical Monero project donation address and viewkwey.
// will search for outputs and inputs in a give tx addressed
// to the primary address only. this search will not account
// for any outputs sent to subaddresses.
auto primary_account = xmreg::make_primaryaccount(
"44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A",
"f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501");
auto tx = get_tx("aa739a3ce8d3171a422ed3a3f5016384cdb17a5d3eb5905021f1103574d47eaf");
// we can join individual identifiers as shown below, since to estimate
// spendings we need to identify possible inputs with their amount values,
// as well as outputs corresponding to the change returned to Monero
// donation address
auto identifier = make_identifier(*tx,
make_unique<xmreg::Output>(primary_account.get()),
make_unique<xmreg::GuessInput>(primary_account.get(), &mcore));
identifier.identify();
// get identified outputs and inputs in the tx
auto outputs_found = identifier.get<xmreg::Output>()->get();
auto inputs_found = identifier.get<xmreg::GuessInput>()->get();
// possible spending is just basic math
auto possible_total_spent = xmreg::calc_total_xmr(inputs_found)
- xmreg::calc_total_xmr(outputs_found)
- get_tx_fee(*tx);
cout << "Possible spending from Monero project donation is: "
<< print_money(possible_total_spent) << " xmr\n";
```
Result is `118.778154000000` xmr which possibly were withdrawn from
Monero donation address.
### Identify and decrypt integrated payment id
```C++
// use Monero forum donation address and viewkwey
auto account = xmreg::make_account(
"45ttEikQEZWN1m7VxaVN9rjQkpSdmpGZ82GwUps66neQ1PqbQMno4wMY8F5jiDt2GoHzCtMwa7PDPJUJYb1GYrMP4CwAwNp",
"c9347bc1e101eab46d3a6532c5b6066e925f499b47d285d5720e6a6f4cc4350c");
auto tx = get_tx("401bf77c9a49dd28df5f9fb15846f9de05fce5f0e11da16d980c4c9ac9156354");
auto identifier = make_identifier(*tx,
make_unique<xmreg::IntegratedPaymentID>(account.get()));
identifier.identify();
auto payment_id = identifier.get<xmreg::IntegratedPaymentID>()->get();
cout << "Found following itegrated payment id in tx " << payment_id << '\n';
```
The result is decrypted short payment id of `1fcbb836a748f4dc`. The tx is also
a possible withdrawn from Monero forum donation wallet for `350` xmr
(see examples.cpp for the code).
# Compilation
The project depends on monero libraries and it has same dependecies as the monero
(except C++14 requirement). Thus monero needs to be setup first.
### Monero download and compilation
Follow instructions in the following link:
https://github.com/moneroexamples/monero-compilation/blob/master/README.md
### Compilation of the xmregcore
#### Compilation of the xmregcore
C++14 is required to run this code.
C++14 is required to compile the project. This means that
GCC 7.1 or higher can be used. For example, Ubuntu 18.04 ships with
GCC 7.3 which is sufficient.
```bash
# go to home folder if still in ~/monero
cd ~
@ -33,7 +140,7 @@ mkdir build && cd build
cmake ..
# altearnatively can use cmake -DMONERO_DIR=/path/to/monero_folder ..
# alternatively can use cmake -DMONERO_DIR=/path/to/monero_folder ..
# if monero is not in ~/monero
make
@ -50,4 +157,4 @@ finished and may not work as intended.
# How can you help?
Constructive criticism, code and website edits are always good. They can be made through github.
Constructive criticism and code are always welcomed. They can be made through github.

@ -0,0 +1,317 @@
#include "src/MicroCore.h"
#include "src/UniversalIdentifier.hpp"
#include "version.h"
#include <iostream>
#include <memory>
using namespace std;
using namespace cryptonote;
using xmreg::operator<<;
/**
* A helper functor to get transaction based
* on its hash in string
*/
struct TxGetter
{
xmreg::MicroCore const* mcore {nullptr};
boost::optional<transaction>
operator()(string tx_hash_str) const
{
assert(mcore);
crypto::hash tx_hash;
if (!epee::string_tools::hex_to_pod(tx_hash_str, tx_hash))
{
cerr << "Cant decode tx hash: " << tx_hash_str << '\n';
return {};
}
boost::optional<transaction> tx = transaction {};
if (!mcore->get_tx(tx_hash, *tx))
{
cerr << "Cant get tx: " << tx_hash_str << '\n';
return {};
}
return tx;
}
};
inline std::ostream&
operator<<(std::ostream& os, boost::optional<transaction> const& tx)
{
if (tx)
{
os << get_transaction_hash(*tx);
}
else
{
os << "N/A";
}
return os;
}
template <typename T>
inline std::ostream&
operator<<(std::ostream& os, boost::optional<T> const& pid)
{
if (pid)
{
os << epee::string_tools::pod_to_hex<T>(*pid);
}
else
{
os << "N/A";
}
return os;
}
int
main(int ac, const char* av[])
{
// setup monero logger for minimum output
mlog_configure(mlog_get_default_log_path(""), true);
mlog_set_log("1");
cout << "Program is starting\n";
network_type nettype = network_type::MAINNET;
string blockchain_path = xmreg::get_default_lmdb_folder(nettype);
cout << "Mainnet blockchain path: " << blockchain_path << '\n'
<< "Monero Version: " << MONERO_VERSION_FULL << '\n';
cout << "Initializaing MicroCore\n\n";
xmreg::MicroCore mcore {blockchain_path, nettype};
// transaction getter helper
TxGetter get_tx {&mcore};
cout << "\n***Identify outputs in a tx based on address and viewkey (no subaddreses)***\n\n";
{
// use Monero donation address and viewkwey
// will search of output in a give tx addressed
// to the primary address only.
auto primary_account = xmreg::make_primaryaccount(
"44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A",
"f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501");
cout << "Monero donation account: " << *primary_account << '\n';
auto tx = get_tx("e8ceef12683b3180d83dd1c24f8f871d52d206b80d8a6db6c5504eb0596b0312");
if (!tx)
return EXIT_FAILURE;
auto identifier = make_identifier(*tx,
make_unique<xmreg::Output>(primary_account.get()));
identifier.identify();
auto outputs_found = identifier.get<xmreg::Output>()->get();
if (!outputs_found.empty())
{
cout << "Found following outputs in tx " << tx << ":\n"
<< outputs_found << '\n';
}
}
cout << "\n***Identify outputs in a tx based on address and viewkey (with subaddresses)***\n\n";
{
// use Monero forum donation address and viewkwey
// will search of inputs in a give tx addressed
// to the primary address and its subaddress.
auto primary_account = xmreg::make_primaryaccount(
"45ttEikQEZWN1m7VxaVN9rjQkpSdmpGZ82GwUps66neQ1PqbQMno4wMY8F5jiDt2GoHzCtMwa7PDPJUJYb1GYrMP4CwAwNp",
"c9347bc1e101eab46d3a6532c5b6066e925f499b47d285d5720e6a6f4cc4350c");
// if we want to analyze subaddress, we need to generate
// an initial list of 10'000 possible subaddress
primary_account->populate_subaddress_indices();
cout << "Monero forum donation account: " << *primary_account << '\n';
auto tx = get_tx("54cef43983ca9eeded46c6cc1091bf6f689d91faf78e306a5ac6169e981105d8");
if (!tx)
return EXIT_FAILURE;
auto identifier = make_identifier(*tx,
make_unique<xmreg::Output>(primary_account.get()));
identifier.identify();
auto outputs_found = identifier.get<xmreg::Output>()->get();
if (!outputs_found.empty())
{
cout << "Found following outputs in tx " << tx << ":\n"
<< outputs_found << '\n';
// identified output is for subaddress of index 0/10 which
// in this case is for the "xiphon part time coding (3 months)"
// proposal https://ccs.getmonero.org/proposals/xiphon-part-time.html
}
}
cout << "\n***Possible spending based on address and viewkey (no subaddress)***\n\n";
{
// use Monero donation address and viewkwey
// will search of inputs in a give tx addressed
// to the primary address only.
auto primary_account = xmreg::make_primaryaccount(
"44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A",
"f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501");
// not using subaddresess here as people donate directly to the primary
// address
auto tx = get_tx("aa739a3ce8d3171a422ed3a3f5016384cdb17a5d3eb5905021f1103574d47eaf");
if (!tx)
return EXIT_FAILURE;
// we can join individual identifiers as below, sice to estimate
// spendings we need to identify possible inputs with their values,
// as well as outputs corresponding to the change returned to Monero
// donation address
auto identifier = make_identifier(*tx,
make_unique<xmreg::Output>(primary_account.get()),
make_unique<xmreg::GuessInput>(primary_account.get(), &mcore));
identifier.identify();
auto outputs_found = identifier.get<xmreg::Output>()->get();
auto inputs_found = identifier.get<xmreg::GuessInput>()->get();
// basic sanity check. If the spending was correctly guesseid,
// at least the number of identify inputs should match the
// number of inputs in the tx
if (tx->vin.size() == inputs_found.size())
{
// possible spending is just basic math
auto possible_total_spent = xmreg::calc_total_xmr(inputs_found)
- xmreg::calc_total_xmr(outputs_found)
- get_tx_fee(*tx);
cout << "Possible spending from Monero project donation is: "
<< print_money(possible_total_spent) << " xmr\n";
}
}
cout << "\n***Possible spending based on address and viewkey (with subaddress)***\n\n";
{
// use Monero forum donation address and viewkwey
// will search of inputs in a give tx addressed
// to the primary address only.
auto primary_account = xmreg::make_primaryaccount(
"45ttEikQEZWN1m7VxaVN9rjQkpSdmpGZ82GwUps66neQ1PqbQMno4wMY8F5jiDt2GoHzCtMwa7PDPJUJYb1GYrMP4CwAwNp",
"c9347bc1e101eab46d3a6532c5b6066e925f499b47d285d5720e6a6f4cc4350c");
// include outputs from subaddress as monero forum is primarly based
// on donation to subaddresses
primary_account->populate_subaddress_indices();
cout << "Monero formum donation account: " << *primary_account << '\n';
auto tx = get_tx("401bf77c9a49dd28df5f9fb15846f9de05fce5f0e11da16d980c4c9ac9156354");
if (!tx)
return EXIT_FAILURE;
// we can join individual identifiers as below, sice to estimate
// spendings we need to identify possible inputs with their values,
// as well as outputs corresponding to the change returned to Monero
// donation address
auto identifier = make_identifier(*tx,
make_unique<xmreg::Output>(primary_account.get()),
make_unique<xmreg::GuessInput>(primary_account.get(), &mcore));
identifier.identify();
auto outputs_found = identifier.get<xmreg::Output>()->get();
auto inputs_found = identifier.get<xmreg::GuessInput>()->get();
// basic sanity check. If the spending was correctly guesses
// at least the number of identify inputs should match the
// number of inputs in the tx
if (tx->vin.size() == inputs_found.size())
{
// possible spending is just basic math
auto possible_total_spent = xmreg::calc_total_xmr(inputs_found)
- xmreg::calc_total_xmr(outputs_found)
- get_tx_fee(*tx);
cout << "Possible spending from Monero fourm donation is: "
<< print_money(possible_total_spent) << " xmr\n";
}
}
cout << "\n***Identify legacy payment id***\n\n";
{
auto tx = get_tx("ce0d32093ca9cc5b7bcae3f4d3508c04846e8bceecc0819fd2c3191b64caad05");
if (!tx)
return EXIT_FAILURE;
auto identifier = make_identifier(*tx,
make_unique<xmreg::LegacyPaymentID>());
identifier.identify();
auto payment_id = identifier.get<xmreg::LegacyPaymentID>()->get();
if (payment_id)
{
cout << "Found following legacy payment id in tx " << payment_id << '\n';
}
}
cout << "\n***Identify and decrypt short payment id***\n\n";
{
// use Monero forum donation address and viewkwey
auto account = xmreg::make_account(
"45ttEikQEZWN1m7VxaVN9rjQkpSdmpGZ82GwUps66neQ1PqbQMno4wMY8F5jiDt2GoHzCtMwa7PDPJUJYb1GYrMP4CwAwNp",
"c9347bc1e101eab46d3a6532c5b6066e925f499b47d285d5720e6a6f4cc4350c");
auto tx = get_tx("401bf77c9a49dd28df5f9fb15846f9de05fce5f0e11da16d980c4c9ac9156354");
if (!tx)
return EXIT_FAILURE;
auto identifier = make_identifier(*tx,
make_unique<xmreg::IntegratedPaymentID>(account.get()));
identifier.identify();
auto payment_id = identifier.get<xmreg::IntegratedPaymentID>()->get();
if (payment_id)
{
cout << "Found following itegrated payment id in tx " << payment_id << '\n';
}
}
return EXIT_SUCCESS;
}

@ -1,35 +0,0 @@
#include "src/MicroCore.h"
#include "src/tools.h"
#include "version.h"
#include <iostream>
#include <memory>
using namespace std;
using namespace cryptonote;
int
main(int ac, const char* av[])
{
mlog_configure(mlog_get_default_log_path(""), true);
mlog_set_log("1");
cout << "Program is starting\n";
network_type nettype = network_type::STAGENET;
string blockchain_path = xmreg::get_default_lmdb_folder(nettype);
cout << "Blockchain path: " << blockchain_path << '\n';
cout << "Monero Version: " << MONERO_VERSION_FULL << endl;
cout << "Initializaing MicroCore\n";
xmreg::MicroCore mcore {blockchain_path, nettype};
return EXIT_SUCCESS;
}

@ -393,11 +393,18 @@ operator<<(std::ostream& os, Account const& _acc)
subaddr_str = ss.str();
}
string spendkey_str {"N/A"};
if (_acc.sk())
{
spendkey_str = _acc.sk2str();
}
return os << "nt:" << static_cast<size_t>(_acc.nettype)
<< "," << subaddr_str
<< ",a:" << _acc.ai2str()
<< ",v:" << _acc.vk2str()
<< ",s:" << _acc.sk2str();
<< ",s:" << spendkey_str;
}
}

@ -258,11 +258,19 @@ class PaymentID : public BaseIdentifier
public:
using payments_t = tuple<crypto::hash, crypto::hash8>;
PaymentID()
: BaseIdentifier(nullptr, nullptr)
{}
PaymentID(address_parse_info const* _address,
secret_key const* _viewkey)
: BaseIdentifier(_address, _viewkey)
{}
PaymentID(Account* _acc)
: BaseIdentifier(_acc)
{}
void identify(transaction const& tx,
public_key const& tx_pub_key,
@ -274,18 +282,34 @@ public:
payment_id_tuple = get_payment_id(tx);
payment_id = std::get<HashT>(payment_id_tuple);
//cout << "payment_id_found: "
//<< pod_to_hex(*payment_id) << endl;
// if no payment_id found, return
if (payment_id == null_hash || get_viewkey() == nullptr)
if (*payment_id == null_hash)
{
payment_id = boost::none;
return;
}
// if no viewkey and we have integrated payment id
if (get_viewkey() == nullptr
&& sizeof(*payment_id) == sizeof(crypto::hash8))
{
payment_id = boost::none;
return;
}
// decrypt integrated payment id. if its legacy payment id
// nothing will happen.
if (!decrypt(payment_id, tx_pub_key))
if (!decrypt(*payment_id, tx_pub_key))
{
throw std::runtime_error("Cant decrypt pay_id: "
+ pod_to_hex(payment_id));
}
}
}
payments_t
@ -323,7 +347,7 @@ public:
{return std::get<HashT>(payment_id_tuple);}
private:
HashT payment_id {};
boost::optional<HashT> payment_id;
HashT null_hash {};
payments_t payment_id_tuple;
};

@ -122,13 +122,12 @@ JsonTx::init()
hex_to_pod(jtx["payment_id"], payment_id);
}
if (jtx.count("payment_id8"))
{
hex_to_pod(jtx["payment_id8"], payment_id8);
hex_to_pod(jtx["payment_id8e"], payment_id8e);
cout << "jtx[\"payment_id8e\"] "<< jtx["tx_hash"]
<< ", " << jtx["payment_id8e"] << endl;
//cout << "jtx[\"payment_id8e\"] "<< jtx["tx_hash"]
//<< ", " << jtx["payment_id8e"] << endl;
}

@ -227,15 +227,23 @@ TEST_P(ModularIdentifierTest, LegacyPaymentID)
auto jtx = construct_jsontx(tx_hash_str);
auto identifier = make_identifier(jtx->tx,
make_unique<LegacyPaymentID>(nullptr, nullptr));
make_unique<LegacyPaymentID>());
identifier.identify();
EXPECT_TRUE(identifier.get<0>()->get()
== jtx->payment_id);
auto pid = identifier.get<0>()->get();
//cout << pod_to_hex(jtx->payment_id) << '\n';
EXPECT_TRUE(identifier.get<0>()->raw()
== jtx->payment_id);
if (jtx->payment_id == crypto::null_hash)
{
EXPECT_FALSE(pid);
}
else
{
EXPECT_TRUE(pid);
EXPECT_TRUE(*pid == jtx->payment_id);
}
}
@ -254,18 +262,17 @@ TEST_P(ModularIdentifierTest, IntegratedPaymentID)
identifier.identify();
//cout << "decrypted: " << pod_to_hex(identifier.get<0>()->get())
// << ", " << pod_to_hex(jtx->payment_id8e) << endl;
auto pid = identifier.get<0>()->get();
EXPECT_TRUE(identifier.get<0>()->get()
== jtx->payment_id8e);
EXPECT_TRUE(identifier.get<0>()->raw()
== jtx->payment_id8);
//cout << "row: " << pod_to_hex(identifier.get<0>()->raw())
// << ", " << pod_to_hex(jtx->payment_id8) << endl;
if (jtx->payment_id8 == crypto::null_hash8)
{
EXPECT_FALSE(pid);
}
else
{
EXPECT_TRUE(pid);
EXPECT_TRUE(*pid == jtx->payment_id8e);
}
}
TEST_P(ModularIdentifierTest, InputWithKnownOutputs)

Loading…
Cancel
Save