2021-07-19 20:42:38 +02:00
|
|
|
#include "default_daemon_comms.hpp"
|
|
|
|
|
|
|
|
#include "wallet.hpp"
|
|
|
|
#include "wallet2½.hpp"
|
|
|
|
#include "block.hpp"
|
|
|
|
#include "block_tx.hpp"
|
|
|
|
|
2022-03-08 00:14:39 +01:00
|
|
|
#include <cryptonote_basic/cryptonote_format_utils.h>
|
2021-07-19 20:42:38 +02:00
|
|
|
#include <common/string_util.h>
|
|
|
|
|
|
|
|
#include <iostream>
|
|
|
|
|
|
|
|
namespace wallet
|
|
|
|
{
|
2022-12-02 05:17:59 +01:00
|
|
|
static auto logcat = oxen::log::Cat("wallet");
|
|
|
|
|
2021-07-19 20:42:38 +02:00
|
|
|
void
|
2021-12-06 21:57:16 +01:00
|
|
|
DefaultDaemonComms::on_get_blocks_response(std::vector<std::string> response)
|
2021-07-19 20:42:38 +02:00
|
|
|
{
|
|
|
|
if (not response.size())
|
|
|
|
{
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::warning(logcat, "on_get_blocks_response(): empty get_blocks response\n");
|
2021-07-19 20:42:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
const auto& status = response[0];
|
|
|
|
if (status != "OK" and status != "END")
|
|
|
|
{
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::warning(logcat, "get_blocks response: {}\n", response[0]);
|
2021-07-19 20:42:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// "OK" response with no blocks may mean we requested blocks past the end of the chain
|
|
|
|
// TODO: decide/confirm this behavior on the daemon side of things
|
|
|
|
if (response.size() == 1)
|
|
|
|
{
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::warning(logcat, "get_blocks response.size() == 1\n");
|
2021-07-19 20:42:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<Block> blocks;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
auto itr = response.cbegin();
|
|
|
|
itr++;
|
|
|
|
while( itr != response.cend())
|
|
|
|
{
|
|
|
|
const auto& block_str = *itr;
|
2022-09-28 18:13:37 +02:00
|
|
|
auto block_dict = oxenc::bt_dict_consumer{block_str};
|
2021-07-19 20:42:38 +02:00
|
|
|
|
|
|
|
Block& b = blocks.emplace_back();
|
|
|
|
|
|
|
|
if (block_dict.key() != "hash")
|
|
|
|
return;
|
|
|
|
b.hash = tools::make_from_guts<crypto::hash>(block_dict.consume_string_view());
|
|
|
|
|
|
|
|
if (block_dict.key() != "height")
|
|
|
|
return;
|
|
|
|
b.height = block_dict.consume_integer<int64_t>();
|
|
|
|
|
|
|
|
if (block_dict.key() != "timestamp")
|
|
|
|
return;
|
|
|
|
b.timestamp = block_dict.consume_integer<int64_t>();
|
|
|
|
|
|
|
|
if (block_dict.key() != "transactions")
|
|
|
|
return;
|
|
|
|
auto txs_list = block_dict.consume_list_consumer();
|
|
|
|
|
|
|
|
while (not txs_list.is_finished())
|
|
|
|
{
|
|
|
|
if (not txs_list.is_dict())
|
|
|
|
return;
|
|
|
|
|
|
|
|
BlockTX tx;
|
|
|
|
|
|
|
|
auto tx_dict = txs_list.consume_dict_consumer();
|
|
|
|
|
|
|
|
if (tx_dict.key() != "global_indices")
|
|
|
|
return;
|
|
|
|
tx.global_indices = tx_dict.consume_list<std::vector<int64_t> >();
|
|
|
|
|
|
|
|
if (tx_dict.key() != "hash")
|
|
|
|
return;
|
|
|
|
tx.hash = tools::make_from_guts<crypto::hash>(tx_dict.consume_string_view());
|
|
|
|
|
|
|
|
if (tx_dict.key() != "tx")
|
|
|
|
return;
|
|
|
|
|
2021-12-02 01:58:12 +01:00
|
|
|
tx.tx = wallet25::tx_from_blob(tx_dict.consume_string_view());
|
2021-07-19 20:42:38 +02:00
|
|
|
|
|
|
|
if (not tx_dict.is_finished())
|
|
|
|
return;
|
|
|
|
|
|
|
|
b.transactions.push_back(tx);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not block_dict.is_finished())
|
|
|
|
return;
|
|
|
|
|
|
|
|
itr++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const std::exception& e)
|
|
|
|
{
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::warning(logcat, "exception thrown: {}\n", e.what());
|
2021-07-19 20:42:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (blocks.size() == 0)
|
|
|
|
{
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::warning(logcat, "received no blocks, but server said response OK\n");
|
2021-07-19 20:42:38 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t start_height = blocks.front().height;
|
|
|
|
int64_t end_height = blocks.back().height;
|
|
|
|
|
2022-04-05 04:14:08 +02:00
|
|
|
omq->job([blocks=std::move(blocks),this](){
|
|
|
|
for_each_wallet([&](std::shared_ptr<Wallet> wallet){
|
2023-01-16 21:52:30 +01:00
|
|
|
wallet->add_blocks(blocks);
|
2022-04-05 04:14:08 +02:00
|
|
|
});
|
|
|
|
}, sync_thread);
|
|
|
|
|
2021-07-19 20:42:38 +02:00
|
|
|
if (status == "END")
|
|
|
|
{
|
2022-04-05 04:14:08 +02:00
|
|
|
omq->job([this, old = this->sync_from_height, start_height, end_height](){
|
|
|
|
// if a new wallet hasn't been added requesting to sync from lower,
|
|
|
|
// we should be done syncing all wallets
|
|
|
|
if (old <= this->sync_from_height)
|
|
|
|
syncing = false;
|
|
|
|
got_blocks(start_height, end_height);
|
|
|
|
}, sync_thread);
|
2021-07-19 20:42:38 +02:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2021-12-06 21:57:16 +01:00
|
|
|
omq->job([this,start_height,end_height](){got_blocks(start_height, end_height);}, sync_thread);
|
2021-07-19 20:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-12-06 21:57:16 +01:00
|
|
|
DefaultDaemonComms::request_top_block_info()
|
2021-07-19 20:42:38 +02:00
|
|
|
{
|
2023-01-16 21:52:30 +01:00
|
|
|
oxen::log::trace(logcat, "request top block called");
|
2021-07-19 20:42:38 +02:00
|
|
|
auto timeout_job = [self=weak_from_this()](){
|
|
|
|
if (auto comms = self.lock())
|
2021-12-06 21:57:16 +01:00
|
|
|
comms->request_top_block_info();
|
2021-07-19 20:42:38 +02:00
|
|
|
};
|
|
|
|
|
2021-12-06 21:57:16 +01:00
|
|
|
omq->cancel_timer(status_timer);
|
2021-07-19 20:42:38 +02:00
|
|
|
if (top_block_height == 0)
|
|
|
|
{
|
2021-12-06 21:57:16 +01:00
|
|
|
omq->add_timer(status_timer, timeout_job, 3s);
|
2021-07-19 20:42:38 +02:00
|
|
|
}
|
|
|
|
else
|
2021-12-06 21:57:16 +01:00
|
|
|
omq->add_timer(status_timer, timeout_job, 15s);
|
2021-07-19 20:42:38 +02:00
|
|
|
|
2023-01-16 21:52:30 +01:00
|
|
|
oxen::log::trace(logcat, "requesting rpc.get_height");
|
2021-12-06 21:57:16 +01:00
|
|
|
omq->request(conn, "rpc.get_height",
|
2021-07-19 20:42:38 +02:00
|
|
|
[this](bool ok, std::vector<std::string> response)
|
|
|
|
{
|
2023-01-16 21:52:30 +01:00
|
|
|
oxen::log::trace(logcat, "rpc get_height response");
|
2021-07-19 20:42:38 +02:00
|
|
|
if (not ok or response.size() != 2 or response[0] != "200")
|
|
|
|
return;
|
|
|
|
|
2022-09-28 18:13:37 +02:00
|
|
|
oxenc::bt_dict_consumer dc{response[1]};
|
2021-07-19 20:42:38 +02:00
|
|
|
|
|
|
|
int64_t new_height = 0;
|
|
|
|
crypto::hash new_hash;
|
|
|
|
|
|
|
|
if (not dc.skip_until("hash"))
|
2022-12-02 05:17:59 +01:00
|
|
|
{
|
|
|
|
oxen::log::warning(logcat, "bad response from rpc.get_height, key 'hash' missing");
|
2021-07-19 20:42:38 +02:00
|
|
|
throw std::runtime_error("bad response from rpc.get_height, key 'hash' missing");
|
2022-12-02 05:17:59 +01:00
|
|
|
}
|
2021-07-19 20:42:38 +02:00
|
|
|
new_hash = tools::make_from_guts<crypto::hash>(dc.consume_string_view());
|
|
|
|
|
|
|
|
if (not dc.skip_until("height"))
|
2022-12-02 05:17:59 +01:00
|
|
|
{
|
|
|
|
oxen::log::warning(logcat, "bad response from rpc.get_height, key 'height' missing");
|
2021-07-19 20:42:38 +02:00
|
|
|
throw std::runtime_error("bad response from rpc.get_height, key 'height' missing");
|
2022-12-02 05:17:59 +01:00
|
|
|
}
|
2021-07-19 20:42:38 +02:00
|
|
|
new_height = dc.consume_integer<int64_t>();
|
|
|
|
|
2022-08-25 23:48:08 +02:00
|
|
|
bool got_new = (new_height > (top_block_height + 1));
|
2021-07-19 20:42:38 +02:00
|
|
|
top_block_hash = new_hash;
|
|
|
|
|
|
|
|
// RPC response is chain length, not top height
|
|
|
|
top_block_height = new_height - 1;
|
2022-04-05 04:14:08 +02:00
|
|
|
omq->job([this](){
|
|
|
|
for_each_wallet([this](auto wallet){
|
|
|
|
wallet->update_top_block_info(top_block_height, top_block_hash);
|
|
|
|
});
|
|
|
|
}, sync_thread);
|
2022-08-25 23:48:08 +02:00
|
|
|
|
|
|
|
if (got_new)
|
|
|
|
{
|
|
|
|
omq->job([this](){
|
|
|
|
if (not syncing) start_syncing();
|
|
|
|
}, sync_thread);
|
|
|
|
}
|
2021-07-19 20:42:38 +02:00
|
|
|
}, "de");
|
2021-12-01 22:51:26 +01:00
|
|
|
|
2023-01-16 21:52:30 +01:00
|
|
|
oxen::log::trace(logcat, "requesting rpc.get_fee_estimate");
|
2021-12-01 22:51:26 +01:00
|
|
|
omq->request(conn, "rpc.get_fee_estimate",
|
|
|
|
[this](bool ok, std::vector<std::string> response)
|
|
|
|
{
|
2023-01-16 21:52:30 +01:00
|
|
|
oxen::log::trace(logcat, "rpc get_fee estimate response");
|
2021-12-01 22:51:26 +01:00
|
|
|
if (not ok or response.size() != 2 or response[0] != "200")
|
|
|
|
return;
|
|
|
|
|
2022-09-28 18:13:37 +02:00
|
|
|
oxenc::bt_dict_consumer dc{response[1]};
|
2021-12-01 22:51:26 +01:00
|
|
|
|
|
|
|
int64_t new_fee_per_byte = 0;
|
|
|
|
int64_t new_fee_per_output = 0;
|
|
|
|
|
|
|
|
if (not dc.skip_until("fee_per_byte"))
|
2022-12-02 05:17:59 +01:00
|
|
|
{
|
|
|
|
oxen::log::warning(logcat, "bad response from rpc.get_fee_estimate, key 'fee_per_byte' missing");
|
2021-12-01 22:51:26 +01:00
|
|
|
throw std::runtime_error("bad response from rpc.get_fee_estimate, key 'fee_per_byte' missing");
|
2022-12-02 05:17:59 +01:00
|
|
|
}
|
2021-12-01 22:51:26 +01:00
|
|
|
new_fee_per_byte = dc.consume_integer<int64_t>();
|
|
|
|
|
|
|
|
if (not dc.skip_until("fee_per_output"))
|
2022-12-02 05:17:59 +01:00
|
|
|
{
|
|
|
|
oxen::log::warning(logcat, "bad response from rpc.get_fee_estimate, key 'fee_per_output' missing");
|
2021-12-01 22:51:26 +01:00
|
|
|
throw std::runtime_error("bad response from rpc.get_fee_estimate, key 'fee_per_output' missing");
|
2022-12-02 05:17:59 +01:00
|
|
|
}
|
2021-12-01 22:51:26 +01:00
|
|
|
new_fee_per_output = dc.consume_integer<int64_t>();
|
|
|
|
|
|
|
|
fee_per_byte = new_fee_per_byte;
|
|
|
|
fee_per_output = new_fee_per_output;
|
|
|
|
|
|
|
|
}, "de");
|
2021-07-19 20:42:38 +02:00
|
|
|
}
|
|
|
|
|
2022-09-08 01:34:52 +02:00
|
|
|
DefaultDaemonComms::DefaultDaemonComms(std::shared_ptr<oxenmq::OxenMQ> omq, DaemonCommsConfig cfg)
|
2021-12-06 21:57:16 +01:00
|
|
|
: omq(omq),
|
2022-03-29 07:51:11 +02:00
|
|
|
config(cfg),
|
2021-12-06 21:57:16 +01:00
|
|
|
sync_thread(omq->add_tagged_thread("sync"))
|
2021-07-19 20:42:38 +02:00
|
|
|
{
|
2021-12-06 21:57:16 +01:00
|
|
|
omq->MAX_MSG_SIZE = max_response_size;
|
2021-07-19 20:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-12-06 21:57:16 +01:00
|
|
|
DefaultDaemonComms::set_remote(std::string_view address)
|
2021-07-19 20:42:38 +02:00
|
|
|
{
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::info(logcat, "Set remote called with address: {}", address);
|
2021-07-19 20:42:38 +02:00
|
|
|
try
|
|
|
|
{
|
|
|
|
remote = oxenmq::address{address};
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
//TODO: handle this properly
|
|
|
|
throw;
|
|
|
|
}
|
|
|
|
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::info(logcat, "Daemon Comms trying to connect to OMQ");
|
|
|
|
conn = omq->connect_remote(remote,
|
|
|
|
// Callback for success case of connect remote
|
|
|
|
[](auto){
|
|
|
|
oxen::log::info(logcat, "Daemon Comms successfully connected to OMQ");
|
|
|
|
},
|
|
|
|
// Callback for failure case of connect remote
|
|
|
|
[](auto, auto reason){
|
|
|
|
oxen::log::error(logcat, "Daemon Comms was not successful in connecting to OMQ. Reason: {}", reason);
|
|
|
|
});
|
2021-07-19 20:42:38 +02:00
|
|
|
|
2021-12-06 21:57:16 +01:00
|
|
|
request_top_block_info();
|
2021-07-19 20:42:38 +02:00
|
|
|
}
|
|
|
|
|
2022-03-29 07:51:11 +02:00
|
|
|
void
|
|
|
|
DefaultDaemonComms::propogate_config()
|
|
|
|
{
|
|
|
|
//This should refresh everywhere when a member in the config changes
|
|
|
|
set_remote(config.address);
|
|
|
|
}
|
|
|
|
|
2021-07-19 20:42:38 +02:00
|
|
|
void
|
2021-12-06 21:57:16 +01:00
|
|
|
DefaultDaemonComms::get_blocks()
|
2021-07-19 20:42:38 +02:00
|
|
|
{
|
|
|
|
auto req_cb = [this](bool ok, std::vector<std::string> response)
|
|
|
|
{
|
|
|
|
if (not ok or response.size() == 0)
|
|
|
|
{
|
|
|
|
//TODO: error logging/handling
|
|
|
|
|
|
|
|
// Retry after a delay to not spam/spin
|
|
|
|
auto timer = std::make_shared<oxenmq::TimerID>();
|
|
|
|
auto& timer_ref = *timer;
|
2021-12-06 21:57:16 +01:00
|
|
|
omq->add_timer(timer_ref, [this,timer=std::move(timer)]{
|
|
|
|
omq->cancel_timer(*timer);
|
|
|
|
get_blocks();
|
2021-07-19 20:42:38 +02:00
|
|
|
},
|
|
|
|
500ms,
|
|
|
|
true,
|
|
|
|
sync_thread);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-12-06 21:57:16 +01:00
|
|
|
on_get_blocks_response(response);
|
2021-07-19 20:42:38 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
std::map<std::string, int64_t> req_params_dict{
|
|
|
|
{"max_count", max_sync_blocks},
|
|
|
|
{"size_limit", max_response_size},
|
|
|
|
{"start_height", sync_from_height}};
|
|
|
|
|
2022-09-28 18:13:37 +02:00
|
|
|
omq->request(conn, "rpc.get_blocks", req_cb, oxenc::bt_serialize(req_params_dict));
|
2021-07-19 20:42:38 +02:00
|
|
|
}
|
|
|
|
|
2021-12-06 23:49:08 +01:00
|
|
|
std::future<std::vector<Decoy>>
|
2022-04-05 04:14:08 +02:00
|
|
|
DefaultDaemonComms::fetch_decoys(const std::vector<int64_t>& indexes, bool with_txid)
|
2021-12-06 23:49:08 +01:00
|
|
|
{
|
|
|
|
auto p = std::make_shared<std::promise<std::vector<Decoy> > >();
|
|
|
|
auto fut = p->get_future();
|
2022-04-05 04:14:08 +02:00
|
|
|
auto req_cb = [p=std::move(p), with_txid, indexes=indexes](bool ok, std::vector<std::string> response)
|
2021-12-06 23:49:08 +01:00
|
|
|
{
|
|
|
|
if (not ok or response.size() == 0)
|
|
|
|
{
|
|
|
|
//TODO: error logging/handling
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-04-05 04:14:08 +02:00
|
|
|
// if not OK
|
|
|
|
if (response[0] != "200")
|
2021-12-06 23:49:08 +01:00
|
|
|
{
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::warning(logcat, "get_outputs response not ok: {}\n", response[0]);
|
2022-05-24 01:34:08 +02:00
|
|
|
if (response.size() == 2)
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::warning(logcat, " -- error: \"{}\"\n", response[1]);
|
2021-12-06 23:49:08 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// "OK" response with no outputs
|
|
|
|
// TODO: decide/confirm this behavior on the daemon side of things
|
|
|
|
if (response.size() == 1)
|
|
|
|
{
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::warning(logcat, "get_blocks response.size() == 1\n");
|
2021-12-06 23:49:08 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<Decoy> outputs;
|
2022-04-05 04:14:08 +02:00
|
|
|
size_t i=0;
|
2021-12-06 23:49:08 +01:00
|
|
|
try
|
|
|
|
{
|
2022-09-28 18:13:37 +02:00
|
|
|
auto outer_dict = oxenc::bt_dict_consumer(response[1]);
|
2022-04-05 04:14:08 +02:00
|
|
|
|
|
|
|
if (outer_dict.key() != "outs")
|
|
|
|
return;
|
|
|
|
|
|
|
|
auto outputs_list = outer_dict.consume_list_consumer();
|
|
|
|
|
|
|
|
while (not outputs_list.is_finished())
|
2021-12-06 23:49:08 +01:00
|
|
|
{
|
2022-04-05 04:14:08 +02:00
|
|
|
auto output_dict = outputs_list.consume_dict_consumer();
|
2021-12-06 23:49:08 +01:00
|
|
|
|
|
|
|
Decoy& o = outputs.emplace_back();
|
|
|
|
|
2022-04-05 04:14:08 +02:00
|
|
|
o.global_index = indexes[i++];
|
|
|
|
|
2021-12-06 23:49:08 +01:00
|
|
|
if (output_dict.key() != "height")
|
|
|
|
return;
|
|
|
|
o.height = output_dict.consume_integer<int64_t>();
|
|
|
|
|
|
|
|
if (output_dict.key() != "key")
|
|
|
|
return;
|
2022-02-16 23:27:27 +01:00
|
|
|
o.key = tools::make_from_guts<crypto::public_key>(output_dict.consume_string_view());
|
2021-12-06 23:49:08 +01:00
|
|
|
|
|
|
|
if (output_dict.key() != "mask")
|
|
|
|
return;
|
2022-02-16 23:27:27 +01:00
|
|
|
o.mask = tools::make_from_guts<rct::key>(output_dict.consume_string_view());
|
2021-12-06 23:49:08 +01:00
|
|
|
|
2022-04-05 04:14:08 +02:00
|
|
|
if (with_txid)
|
|
|
|
{
|
|
|
|
if (output_dict.key() != "txid")
|
|
|
|
return;
|
|
|
|
o.txid = output_dict.consume_string_view();
|
|
|
|
}
|
2021-12-06 23:49:08 +01:00
|
|
|
|
|
|
|
if (output_dict.key() != "unlocked")
|
|
|
|
return;
|
|
|
|
o.unlocked = output_dict.consume_integer<bool>();
|
|
|
|
|
|
|
|
if (not output_dict.is_finished())
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch (const std::exception& e)
|
|
|
|
{
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::warning(logcat, "exception thrown: {}\n", e.what());
|
2021-12-06 23:49:08 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (outputs.size() == 0)
|
|
|
|
{
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::warning(logcat, "received no outputs, but server said response OK\n");
|
2021-12-06 23:49:08 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-04-05 04:14:08 +02:00
|
|
|
p->set_value(std::move(outputs));
|
2021-12-06 23:49:08 +01:00
|
|
|
}; // req_cb
|
|
|
|
|
2022-09-28 18:13:37 +02:00
|
|
|
oxenc::bt_dict req_params_dict;
|
|
|
|
oxenc::bt_list decoy_list_bt;
|
2021-12-06 23:49:08 +01:00
|
|
|
for (auto index : indexes)
|
|
|
|
{
|
2022-04-05 04:14:08 +02:00
|
|
|
decoy_list_bt.push_back(index);
|
2021-12-06 23:49:08 +01:00
|
|
|
}
|
2022-04-05 04:14:08 +02:00
|
|
|
req_params_dict["get_txid"] = with_txid;
|
2021-12-06 23:49:08 +01:00
|
|
|
req_params_dict["outputs"] = std::move(decoy_list_bt);
|
2022-09-28 18:13:37 +02:00
|
|
|
omq->request(conn, "rpc.get_outs", req_cb, oxenc::bt_serialize(req_params_dict));
|
2021-12-06 23:49:08 +01:00
|
|
|
|
|
|
|
return fut;
|
|
|
|
}
|
|
|
|
|
2022-03-08 00:14:39 +01:00
|
|
|
std::future<std::string>
|
|
|
|
DefaultDaemonComms::submit_transaction(const cryptonote::transaction& tx, bool blink)
|
|
|
|
{
|
|
|
|
auto p = std::make_shared<std::promise<std::string> >();
|
|
|
|
auto fut = p->get_future();
|
|
|
|
auto req_cb = [p=std::move(p)](bool ok, std::vector<std::string> response)
|
|
|
|
{
|
|
|
|
// TODO: handle various error cases.
|
|
|
|
if (not ok or response.size() != 2 or response[0] != "200")
|
|
|
|
{
|
|
|
|
p->set_value("Unknown Error");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2022-09-28 18:13:37 +02:00
|
|
|
oxenc::bt_dict_consumer dc{response[1]};
|
2022-08-31 01:58:51 +02:00
|
|
|
if (dc.skip_until("reason"))
|
2022-03-08 00:14:39 +01:00
|
|
|
{
|
2022-08-31 01:58:51 +02:00
|
|
|
auto reason = dc.consume_string();
|
|
|
|
p->set_value(std::string("Submit Transaction rejected, reason: ") + reason);
|
2022-03-08 00:14:39 +01:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (not dc.skip_until("status"))
|
|
|
|
{
|
|
|
|
p->set_value("Invalid response from daemon");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto status = dc.consume_string();
|
|
|
|
|
|
|
|
if (status == "OK")
|
|
|
|
p->set_value("OK");
|
|
|
|
else
|
2022-08-31 01:58:51 +02:00
|
|
|
p->set_value(std::string("Something getting wrong.") + status);
|
2022-03-08 00:14:39 +01:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-04-05 04:14:08 +02:00
|
|
|
std::string tx_str;
|
2022-05-24 01:34:08 +02:00
|
|
|
if (not cryptonote::tx_to_blob(tx, tx_str))
|
|
|
|
throw std::runtime_error{"wallet daemon comms, failed to serialize transaction"};
|
|
|
|
|
2022-09-28 18:13:37 +02:00
|
|
|
oxenc::bt_dict req_params_dict;
|
2022-03-08 00:14:39 +01:00
|
|
|
|
|
|
|
req_params_dict["blink"] = blink;
|
2022-04-05 04:14:08 +02:00
|
|
|
req_params_dict["tx"] = tx_str;
|
2022-03-08 00:14:39 +01:00
|
|
|
|
2022-09-28 18:13:37 +02:00
|
|
|
omq->request(conn, "rpc.submit_transaction", req_cb, oxenc::bt_serialize(req_params_dict));
|
2022-03-08 00:14:39 +01:00
|
|
|
|
|
|
|
return fut;
|
|
|
|
}
|
|
|
|
|
2023-02-27 22:56:41 +01:00
|
|
|
std::future<std::string>
|
|
|
|
DefaultDaemonComms::ons_names_to_owners(const std::string& name_hash, const uint16_t type)
|
|
|
|
{
|
|
|
|
auto p = std::make_shared<std::promise<std::string> >();
|
|
|
|
auto fut = p->get_future();
|
|
|
|
auto req_cb = [p=std::move(p)](bool ok, std::vector<std::string> response)
|
|
|
|
{
|
|
|
|
|
|
|
|
oxenc::bt_dict_consumer dc{response[1]};
|
|
|
|
|
|
|
|
if (not dc.skip_until("result"))
|
|
|
|
{
|
|
|
|
auto reason = dc.consume_string();
|
|
|
|
p->set_value(std::string("ONS names to owners rejected, reason: ") + reason);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
auto result_list = dc.consume_list_consumer();
|
|
|
|
const auto result = result_list.consume_dict_data();
|
|
|
|
|
|
|
|
p->set_value(std::string(result));
|
|
|
|
return;
|
|
|
|
};
|
|
|
|
|
|
|
|
oxenc::bt_dict req_params_dict;
|
|
|
|
|
|
|
|
oxenc::bt_list name_hash_list;
|
|
|
|
name_hash_list.push_back(name_hash);
|
|
|
|
oxenc::bt_list type_list;
|
|
|
|
type_list.push_back(type);
|
|
|
|
|
|
|
|
req_params_dict["name_hash"] = name_hash_list;
|
|
|
|
req_params_dict["type"] = type_list;
|
|
|
|
|
|
|
|
omq->request(conn, "rpc.ons_names_to_owners", req_cb, oxenc::bt_serialize(req_params_dict));
|
|
|
|
|
|
|
|
return fut;
|
|
|
|
}
|
|
|
|
|
2021-07-19 20:42:38 +02:00
|
|
|
void
|
2022-03-29 04:24:21 +02:00
|
|
|
DefaultDaemonComms::register_wallet(wallet::Wallet& wallet, int64_t height, bool check_sync_height, bool new_wallet)
|
2021-07-19 20:42:38 +02:00
|
|
|
{
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::trace(logcat, "Daemon Comms register_wallet called");
|
2022-03-29 04:24:21 +02:00
|
|
|
omq->job([this,w=wallet.shared_from_this(),height,check_sync_height,new_wallet](){
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::trace(logcat, "register_wallet lambda called");
|
2022-03-29 04:24:21 +02:00
|
|
|
if (wallets.count(w))
|
|
|
|
wallets[w] = height;
|
|
|
|
else if (new_wallet)
|
|
|
|
wallets.emplace(w, height);
|
|
|
|
|
2021-07-19 20:42:38 +02:00
|
|
|
if (check_sync_height)
|
2022-03-29 04:24:21 +02:00
|
|
|
{
|
|
|
|
if (wallets.size() == 1) // if it's the only wallet
|
|
|
|
sync_from_height = height;
|
|
|
|
else
|
|
|
|
sync_from_height = std::min(sync_from_height, height);
|
|
|
|
}
|
2021-12-06 21:57:16 +01:00
|
|
|
start_syncing();
|
2021-07-19 20:42:38 +02:00
|
|
|
}, sync_thread);
|
|
|
|
}
|
|
|
|
|
2021-12-01 22:51:26 +01:00
|
|
|
std::pair<int64_t, int64_t>
|
|
|
|
DefaultDaemonComms::get_fee_parameters()
|
|
|
|
{
|
|
|
|
return std::make_pair(fee_per_byte,fee_per_output);
|
|
|
|
}
|
|
|
|
|
2021-07-19 20:42:38 +02:00
|
|
|
void
|
2021-12-06 21:57:16 +01:00
|
|
|
DefaultDaemonComms::deregister_wallet(wallet::Wallet& wallet, std::promise<void>& p)
|
2021-07-19 20:42:38 +02:00
|
|
|
{
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::trace(logcat, "Daemon Comms deregister_wallet called");
|
2022-03-29 04:24:21 +02:00
|
|
|
auto dereg_finish = [this,&p]() mutable {
|
|
|
|
p.set_value();
|
|
|
|
};
|
|
|
|
|
|
|
|
omq->job([this, w=wallet.shared_from_this(), &p, dereg_finish]() mutable {
|
2021-07-19 20:42:38 +02:00
|
|
|
wallets.erase(w);
|
|
|
|
w.reset();
|
2022-03-29 04:24:21 +02:00
|
|
|
|
|
|
|
// this fulfills the promise after any functions waiting on this thread
|
|
|
|
// have completed, so all references to wallet from here should be gone.
|
|
|
|
omq->job(dereg_finish, sync_thread);
|
2021-07-19 20:42:38 +02:00
|
|
|
auto itr = std::min_element(wallets.begin(), wallets.end(),
|
|
|
|
[](const auto& l, const auto& r){ return l.second < r.second; });
|
2022-03-29 04:24:21 +02:00
|
|
|
if (itr != wallets.end())
|
|
|
|
sync_from_height = itr->second;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
sync_from_height = 0;
|
|
|
|
syncing = false;
|
|
|
|
}
|
|
|
|
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::debug(logcat, "deregister_wallet() setting sync_from_height to {}", sync_from_height);
|
2021-07-19 20:42:38 +02:00
|
|
|
if (sync_from_height != 0 and sync_from_height == top_block_height)
|
|
|
|
syncing = false;
|
|
|
|
}, sync_thread);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-12-06 21:57:16 +01:00
|
|
|
DefaultDaemonComms::for_each_wallet(std::function<void(std::shared_ptr<Wallet>)> func)
|
2021-07-19 20:42:38 +02:00
|
|
|
{
|
|
|
|
for (auto [wallet,h] : wallets)
|
|
|
|
{
|
|
|
|
func(wallet);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-12-06 21:57:16 +01:00
|
|
|
DefaultDaemonComms::got_blocks(int64_t start_height, int64_t end_height)
|
2021-07-19 20:42:38 +02:00
|
|
|
{
|
2022-04-05 04:14:08 +02:00
|
|
|
if (start_height == sync_from_height)
|
|
|
|
sync_from_height = end_height + 1;
|
|
|
|
|
2021-07-19 20:42:38 +02:00
|
|
|
// if we get caught up, or all wallets are removed, no need to request more blocks
|
|
|
|
if (not syncing)
|
|
|
|
return;
|
|
|
|
|
2021-12-06 21:57:16 +01:00
|
|
|
get_blocks();
|
2021-07-19 20:42:38 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2021-12-06 21:57:16 +01:00
|
|
|
DefaultDaemonComms::start_syncing()
|
2021-07-19 20:42:38 +02:00
|
|
|
{
|
2022-08-25 23:48:08 +02:00
|
|
|
if ((not syncing and sync_from_height <= top_block_height) or (top_block_height == 0))
|
2021-07-19 20:42:38 +02:00
|
|
|
{
|
|
|
|
syncing = true;
|
2022-12-02 05:17:59 +01:00
|
|
|
oxen::log::debug(logcat, "Start Syncing");
|
2021-12-06 21:57:16 +01:00
|
|
|
get_blocks();
|
2021-07-19 20:42:38 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace wallet
|