Add zero-confirmation support to webhooks (only) (#72)

This commit is contained in:
Lee *!* Clagett
2023-07-01 11:18:18 -04:00
committed by Lee *!* Clagett
parent f827dca8d1
commit fdbd3669a6
12 changed files with 408 additions and 32 deletions

View File

@@ -51,6 +51,7 @@ namespace rpc
constexpr const char abort_scan_signal[] = "SCAN";
constexpr const char abort_process_signal[] = "PROCESS";
constexpr const char minimal_chain_topic[] = "json-minimal-chain_main";
constexpr const char full_txpool_topic[] = "json-full-txpool_add";
constexpr const int daemon_zmq_linger = 0;
constexpr const std::chrono::seconds chain_poll_timeout{20};
constexpr const std::chrono::minutes chain_sub_timeout{2};
@@ -225,10 +226,9 @@ namespace rpc
if (out.daemon_sub.get() == nullptr)
return net::zmq::get_error_code();
option = 1; // keep only last pub message from daemon
MONERO_ZMQ_CHECK(zmq_connect(out.daemon_sub.get(), out.ctx->sub_addr.c_str()));
MONERO_ZMQ_CHECK(zmq_setsockopt(out.daemon_sub.get(), ZMQ_CONFLATE, &option, sizeof(option)));
MONERO_CHECK(do_subscribe(out.daemon_sub.get(), minimal_chain_topic));
MONERO_CHECK(do_subscribe(out.daemon_sub.get(), full_txpool_topic));
}
out.signal_sub.reset(zmq_socket(out.ctx->comm.get(), ZMQ_SUB));
@@ -250,7 +250,7 @@ namespace rpc
return do_subscribe(signal_sub.get(), abort_scan_signal);
}
expect<minimal_chain_pub> client::wait_for_block()
expect<std::vector<std::pair<client::topic, std::string>>> client::wait_for_block()
{
MONERO_PRECOND(ctx != nullptr);
assert(daemon != nullptr);
@@ -271,14 +271,45 @@ namespace rpc
return ready.error();
}
}
expect<std::string> pub = net::zmq::receive(daemon_sub.get(), ZMQ_DONTWAIT);
if (!pub)
return pub.error();
if (!boost::string_ref{*pub}.starts_with(minimal_chain_topic))
return {lws::error::bad_daemon_response};
pub->erase(0, sizeof(minimal_chain_topic));
return minimal_chain_pub::from_json(std::move(*pub));
std::vector<std::pair<topic, std::string>> messages{};
for (; /*every message */ ;)
{
expect<std::string> pub = net::zmq::receive(daemon_sub.get(), ZMQ_DONTWAIT);
if (!pub)
{
if (pub == net::zmq::make_error_code(EAGAIN))
return {std::move(messages)};
return pub.error();
}
if (pub->size() < 5)
break; // for loop
switch (pub->at(5))
{
case 'm': // json-minimal-chain_main
if (boost::string_ref{*pub}.starts_with(minimal_chain_topic))
{
pub->erase(0, sizeof(minimal_chain_topic));
messages.emplace_back(topic::block, std::move(*pub));
}
else
MWARNING("Unexpected pub/sub message");
break;
case 'f': // json-full-txpool_add
if (boost::string_ref{*pub}.starts_with(full_txpool_topic))
{
pub->erase(0, sizeof(full_txpool_topic));
messages.emplace_back(topic::txpool, std::move(*pub));
}
else
MWARNING("Unexpected pub/sub message");
break;
default:
break;
}
} // for every message
return {lws::error::bad_daemon_response};
}
expect<void> client::send(epee::byte_slice message, std::chrono::seconds timeout) noexcept

View File

@@ -75,6 +75,12 @@ namespace rpc
expect<void> get_response(cryptonote::rpc::Message& response, std::chrono::seconds timeout, source_location loc);
public:
enum class topic : std::uint8_t
{
block = 0, txpool
};
//! A client with no connection (all send/receive functions fail).
explicit client() noexcept
: ctx(), daemon(), daemon_sub(), signal_sub()
@@ -110,7 +116,7 @@ namespace rpc
expect<void> watch_scan_signals() noexcept;
//! Wait for new block announce or internal timeout.
expect<minimal_chain_pub> wait_for_block();
expect<std::vector<std::pair<topic, std::string>>> wait_for_block();
//! \return A JSON message for RPC request `M`.
template<typename M>

View File

@@ -27,6 +27,8 @@
#include "daemon_pub.h"
#include "cryptonote_basic/cryptonote_basic.h" // monero/src
#include "rpc/daemon_zmq.h"
#include "wire/crypto.h"
#include "wire/error.h"
#include "wire/field.h"
@@ -83,5 +85,19 @@ namespace rpc
return err;
return {std::move(out)};
}
static void read_bytes(wire::json_reader& source, full_txpool_pub& self)
{
wire_read::array(source, self.txes);
}
expect<full_txpool_pub> full_txpool_pub::from_json(std::string&& source)
{
full_txpool_pub out{};
std::error_code err = wire::json::from_bytes(std::move(source), out);
if (err)
return err;
return {std::move(out)};
}
}
}

View File

@@ -34,6 +34,7 @@
#include "crypto/hash.h" // monero/src
#include "wire/json/fwd.h"
namespace cryptonote { class transaction; }
namespace lws
{
namespace rpc
@@ -46,5 +47,12 @@ namespace rpc
static expect<minimal_chain_pub> from_json(std::string&&);
};
struct full_txpool_pub
{
std::vector<cryptonote::transaction> txes;
static expect<full_txpool_pub> from_json(std::string&&);
};
}
}

View File

@@ -42,6 +42,7 @@ namespace
constexpr const std::size_t default_inputs = 2;
constexpr const std::size_t default_outputs = 4;
constexpr const std::size_t default_txextra_size = 2048;
constexpr const std::size_t default_txpool_size = 32;
}
namespace rct
@@ -141,7 +142,7 @@ namespace cryptonote
);
}
static void read_bytes(wire::json_reader& source, transaction& self)
void read_bytes(wire::json_reader& source, transaction& self)
{
self.vin.reserve(default_inputs);
self.vout.reserve(default_outputs);
@@ -177,6 +178,11 @@ namespace cryptonote
self.transactions.reserve(default_transaction_count);
wire::object(source, WIRE_FIELD(block), WIRE_FIELD(transactions));
}
static void read_bytes(wire::json_reader& source, tx_in_pool& self)
{
wire::object(source, WIRE_FIELD(tx), WIRE_FIELD(tx_hash));
}
} // rpc
} // cryptonote
@@ -187,3 +193,8 @@ void lws::rpc::read_bytes(wire::json_reader& source, get_blocks_fast_response& s
wire::object(source, WIRE_FIELD(blocks), WIRE_FIELD(output_indices), WIRE_FIELD(start_height), WIRE_FIELD(current_height));
}
void lws::rpc::read_bytes(wire::json_reader& source, get_transaction_pool_response& self)
{
self.transactions.reserve(default_txpool_size);
wire::object(source, WIRE_FIELD(transactions));
}

View File

@@ -40,9 +40,13 @@ namespace crypto
namespace cryptonote
{
class transaction;
void read_bytes(wire::json_reader& source, transaction& self);
namespace rpc
{
struct block_with_transactions;
struct tx_in_pool;
}
}
@@ -71,5 +75,21 @@ namespace rpc
using response = get_blocks_fast_response;
};
void read_bytes(wire::json_reader&, get_blocks_fast_response&);
struct get_transaction_pool_request
{
get_transaction_pool_request() = delete;
};
struct get_transaction_pool_response
{
get_transaction_pool_response() = delete;
std::vector<cryptonote::rpc::tx_in_pool> transactions;
};
struct get_transaction_pool
{
using request = get_transaction_pool_request;
using response = get_transaction_pool_response;
};
void read_bytes(wire::json_reader&, get_transaction_pool_response&);
} // rpc
} // lws