Skip to content
Merged
5 changes: 4 additions & 1 deletion include/bitcoin/server/protocols/protocol_electrum.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ class BCS_API protocol_electrum
void blockchain_block_headers(size_t starting, size_t quantity,
size_t waypoint, bool single) NOEXCEPT;

/// Completion handlers (for long-running address queries).
/// Completion handlers (for long-running or other async queries).
/// -----------------------------------------------------------------------

void get_balance(const hash_digest& hash) NOEXCEPT;
Expand All @@ -257,6 +257,9 @@ class BCS_API protocol_electrum
void complete_get_mempool(const code& ec, const histories& histories) NOEXCEPT;
void complete_list_unspent(const code& ec, const unspents& unspents) NOEXCEPT;

void handle_estimate_fee(const code& ec, uint64_t fee) NOEXCEPT;
void complete_estimate_fee(const code& ec, uint64_t fee) NOEXCEPT;

/// Notification event handlers.
/// -----------------------------------------------------------------------

Expand Down
5 changes: 5 additions & 0 deletions src/parser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1328,6 +1328,11 @@ options_metadata parser::load_settings() THROWS
value<bool>(&configured.node.defer_confirmation),
"Defer confirmation, defaults to 'false'."
)
(
"node.fee_estimate_horizon",
value<uint16_t>(&configured.node.fee_estimate_horizon),
"Fee estimation horizon, limited to 1008, defaults to '0' (0 disables)."
)
////(
//// "node.headers_first",
//// value<bool>(&configured.node.headers_first),
Expand Down
57 changes: 52 additions & 5 deletions src/protocols/electrum/protocol_electrum_fees.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,23 @@ using namespace std::placeholders;

BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)

using mode_t = node::estimator::mode;
mode_t mode_from_string(const std::string& mode) NOEXCEPT
{
if (mode.empty()) return mode_t::basic;
if (mode == "basic") return mode_t::basic;
if (mode == "geometric") return mode_t::geometric;
if (mode == "economical") return mode_t::economical;
if (mode == "conservative") return mode_t::conservative;
return mode_t::unknown;
}

void protocol_electrum::handle_blockchain_estimate_fee(const code& ec,
rpc_interface::blockchain_estimate_fee, double number,
const std::string& mode) NOEXCEPT
{
BC_ASSERT(stranded());

if (stopped(ec))
return;

Expand All @@ -51,23 +64,57 @@ void protocol_electrum::handle_blockchain_estimate_fee(const code& ec,
return;
}

if (!mode.empty() &&
!at_least(electrum::version::v1_6))
if (!mode.empty() && !at_least(electrum::version::v1_6))
{
send_code(error::invalid_argument);
return;
}

const auto mode_ = mode_from_string(mode);
if (mode_ == mode_t::unknown)
{
send_code(error::invalid_argument);
return;
}

// TODO: integrate fee estimator.
////send_code(error::not_implemented);
estimate(target, mode_, BIND(handle_estimate_fee, _1, _2));
}

void protocol_electrum::handle_estimate_fee(const code& ec,
uint64_t fee) NOEXCEPT
{
POST(complete_estimate_fee, ec, fee);
}

void protocol_electrum::complete_estimate_fee(const code& ec,
uint64_t fee) NOEXCEPT
{
BC_ASSERT(stranded());

if (stopped())
return;

const auto disabled =
ec == node::error::estimate_false ||
ec == node::error::estimate_disabled ||
ec == node::error::estimate_premature;

if (!disabled && ec)
{
// node::error::estimates_failed, implies store fault.
send_code(error::server_error);
return;
}

// If not enough information to make an estimate, -1 is returned.
send_result(-1, 42);
send_result(disabled ? -1 : possible_narrow_sign_cast<int64_t>(fee), 42);
}

void protocol_electrum::handle_blockchain_relay_fee(const code& ec,
rpc_interface::blockchain_relay_fee) NOEXCEPT
{
BC_ASSERT(stranded());

if (stopped(ec))
return;

Expand Down
1 change: 1 addition & 0 deletions test/protocols/blocks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@ const block bogus_block10
0x0b,
inputs
{
// Null points in non-first tx (coinbase confusion).
input
{
point{},
Expand Down
39 changes: 31 additions & 8 deletions test/protocols/electrum/electrum_fees.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,17 +48,40 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_estimate_fee__mode_invalid_version__in
{
BOOST_REQUIRE(handshake(electrum::version::v1_4));

const auto result = get_error(R"({"id":801,"method":"blockchain.estimatefee","params":[42,"mode"]})" "\n");
const auto result = get_error(R"({"id":801,"method":"blockchain.estimatefee","params":[42,"basic"]})" "\n");
BOOST_REQUIRE_EQUAL(result, invalid_argument.value());
}

////BOOST_AUTO_TEST_CASE(electrum__blockchain_estimate_fee__valid__not_implemented)
////{
//// BOOST_REQUIRE(handshake(electrum::version::v1_6));
////
//// const auto result = get_error(R"({"id":801,"method":"blockchain.estimatefee","params":[42,"mode"]})" "\n");
//// BOOST_REQUIRE_EQUAL(result, not_implemented.value());
////}
BOOST_AUTO_TEST_CASE(electrum__blockchain_estimate_fee__nvalid_mode__invalid_argument)
{
BOOST_REQUIRE(handshake(electrum::version::v1_4));

const auto result = get_error(R"({"id":801,"method":"blockchain.estimatefee","params":[42,"bogus"]})" "\n");
BOOST_REQUIRE_EQUAL(result, invalid_argument.value());
}

BOOST_AUTO_TEST_CASE(electrum__blockchain_estimate_fee__uninitialized__negative_one)
{
BOOST_REQUIRE(handshake(electrum::version::v1_6));

const auto response = get(R"({"id":801,"method":"blockchain.estimatefee","params":[0,"basic"]})" "\n");
REQUIRE_NO_THROW_TRUE(response.at("result").is_int64());
BOOST_REQUIRE_EQUAL(response.at("result").as_int64(), -1);
}

BOOST_AUTO_TEST_CASE(electrum__blockchain_estimate_fee__zero_basic__negative_one)
{
BOOST_REQUIRE(handshake(electrum::version::v1_6));

// Trigger node chaser event to initialize fee estimator.
notify(node::chase::block, { 9_u32 });

const auto response = get(R"({"id":801,"method":"blockchain.estimatefee","params":[0,"basic"]})" "\n");
REQUIRE_NO_THROW_TRUE(response.at("result").is_int64());

// None of the first 10 blocks have fees, so no estimate is obtained.
BOOST_REQUIRE_EQUAL(response.at("result").as_int64(), -1);
}

// blockchain.relayfee

Expand Down
12 changes: 11 additions & 1 deletion test/protocols/electrum/electrum_setup_fixture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ electrum_setup_fixture::electrum_setup_fixture(const initializer& setup,
database_settings.interval_depth = 2;
node_settings.delay_inbound = false;
node_settings.minimum_fee_rate = 99.0;
node_settings.fee_estimate_horizon = 8;
network_settings.inbound.connections = 0;
network_settings.outbound.connections = 0;

Expand All @@ -74,7 +75,16 @@ electrum_setup_fixture::electrum_setup_fixture(const initializer& setup,
BOOST_REQUIRE_MESSAGE(!ec, ec.message());
BOOST_REQUIRE_MESSAGE(setup(query_), "electrum initialize");

// Run the server.
std::promise<code> started{};
server_.start([&](const code& ec) NOEXCEPT
{
started.set_value(ec);
});

// Block until server is started.
ec = started.get_future().get();
BOOST_REQUIRE_MESSAGE(!ec, ec.message());

std::promise<code> running{};
server_.run([&](const code& ec) NOEXCEPT
{
Expand Down
Loading