Skip to content

Commit 6b547ab

Browse files
authored
Merge pull request #772 from evoskuil/master
Integrate node fee estimator.
2 parents a778552 + 7db88e9 commit 6b547ab

6 files changed

Lines changed: 104 additions & 15 deletions

File tree

include/bitcoin/server/protocols/protocol_electrum.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ class BCS_API protocol_electrum
239239
void blockchain_block_headers(size_t starting, size_t quantity,
240240
size_t waypoint, bool single) NOEXCEPT;
241241

242-
/// Completion handlers (for long-running address queries).
242+
/// Completion handlers (for long-running or other async queries).
243243
/// -----------------------------------------------------------------------
244244

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

260+
void handle_estimate_fee(const code& ec, uint64_t fee) NOEXCEPT;
261+
void complete_estimate_fee(const code& ec, uint64_t fee) NOEXCEPT;
262+
260263
/// Notification event handlers.
261264
/// -----------------------------------------------------------------------
262265

src/parser.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1328,6 +1328,11 @@ options_metadata parser::load_settings() THROWS
13281328
value<bool>(&configured.node.defer_confirmation),
13291329
"Defer confirmation, defaults to 'false'."
13301330
)
1331+
(
1332+
"node.fee_estimate_horizon",
1333+
value<uint16_t>(&configured.node.fee_estimate_horizon),
1334+
"Fee estimation horizon, limited to 1008, defaults to '0' (0 disables)."
1335+
)
13311336
////(
13321337
//// "node.headers_first",
13331338
//// value<bool>(&configured.node.headers_first),

src/protocols/electrum/protocol_electrum_fees.cpp

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,23 @@ using namespace std::placeholders;
3131

3232
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
3333

34+
using mode_t = node::estimator::mode;
35+
mode_t mode_from_string(const std::string& mode) NOEXCEPT
36+
{
37+
if (mode.empty()) return mode_t::basic;
38+
if (mode == "basic") return mode_t::basic;
39+
if (mode == "geometric") return mode_t::geometric;
40+
if (mode == "economical") return mode_t::economical;
41+
if (mode == "conservative") return mode_t::conservative;
42+
return mode_t::unknown;
43+
}
44+
3445
void protocol_electrum::handle_blockchain_estimate_fee(const code& ec,
3546
rpc_interface::blockchain_estimate_fee, double number,
3647
const std::string& mode) NOEXCEPT
3748
{
49+
BC_ASSERT(stranded());
50+
3851
if (stopped(ec))
3952
return;
4053

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

54-
if (!mode.empty() &&
55-
!at_least(electrum::version::v1_6))
67+
if (!mode.empty() && !at_least(electrum::version::v1_6))
68+
{
69+
send_code(error::invalid_argument);
70+
return;
71+
}
72+
73+
const auto mode_ = mode_from_string(mode);
74+
if (mode_ == mode_t::unknown)
5675
{
5776
send_code(error::invalid_argument);
5877
return;
5978
}
6079

61-
// TODO: integrate fee estimator.
62-
////send_code(error::not_implemented);
80+
estimate(target, mode_, BIND(handle_estimate_fee, _1, _2));
81+
}
82+
83+
void protocol_electrum::handle_estimate_fee(const code& ec,
84+
uint64_t fee) NOEXCEPT
85+
{
86+
POST(complete_estimate_fee, ec, fee);
87+
}
88+
89+
void protocol_electrum::complete_estimate_fee(const code& ec,
90+
uint64_t fee) NOEXCEPT
91+
{
92+
BC_ASSERT(stranded());
93+
94+
if (stopped())
95+
return;
96+
97+
const auto disabled =
98+
ec == node::error::estimate_false ||
99+
ec == node::error::estimate_disabled ||
100+
ec == node::error::estimate_premature;
101+
102+
if (!disabled && ec)
103+
{
104+
// node::error::estimates_failed, implies store fault.
105+
send_code(error::server_error);
106+
return;
107+
}
63108

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

68113
void protocol_electrum::handle_blockchain_relay_fee(const code& ec,
69114
rpc_interface::blockchain_relay_fee) NOEXCEPT
70115
{
116+
BC_ASSERT(stranded());
117+
71118
if (stopped(ec))
72119
return;
73120

test/protocols/blocks.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,7 @@ const block bogus_block10
233233
0x0b,
234234
inputs
235235
{
236+
// Null points in non-first tx (coinbase confusion).
236237
input
237238
{
238239
point{},

test/protocols/electrum/electrum_fees.cpp

Lines changed: 31 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,17 +48,40 @@ BOOST_AUTO_TEST_CASE(electrum__blockchain_estimate_fee__mode_invalid_version__in
4848
{
4949
BOOST_REQUIRE(handshake(electrum::version::v1_4));
5050

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

55-
////BOOST_AUTO_TEST_CASE(electrum__blockchain_estimate_fee__valid__not_implemented)
56-
////{
57-
//// BOOST_REQUIRE(handshake(electrum::version::v1_6));
58-
////
59-
//// const auto result = get_error(R"({"id":801,"method":"blockchain.estimatefee","params":[42,"mode"]})" "\n");
60-
//// BOOST_REQUIRE_EQUAL(result, not_implemented.value());
61-
////}
55+
BOOST_AUTO_TEST_CASE(electrum__blockchain_estimate_fee__nvalid_mode__invalid_argument)
56+
{
57+
BOOST_REQUIRE(handshake(electrum::version::v1_4));
58+
59+
const auto result = get_error(R"({"id":801,"method":"blockchain.estimatefee","params":[42,"bogus"]})" "\n");
60+
BOOST_REQUIRE_EQUAL(result, invalid_argument.value());
61+
}
62+
63+
BOOST_AUTO_TEST_CASE(electrum__blockchain_estimate_fee__uninitialized__negative_one)
64+
{
65+
BOOST_REQUIRE(handshake(electrum::version::v1_6));
66+
67+
const auto response = get(R"({"id":801,"method":"blockchain.estimatefee","params":[0,"basic"]})" "\n");
68+
REQUIRE_NO_THROW_TRUE(response.at("result").is_int64());
69+
BOOST_REQUIRE_EQUAL(response.at("result").as_int64(), -1);
70+
}
71+
72+
BOOST_AUTO_TEST_CASE(electrum__blockchain_estimate_fee__zero_basic__negative_one)
73+
{
74+
BOOST_REQUIRE(handshake(electrum::version::v1_6));
75+
76+
// Trigger node chaser event to initialize fee estimator.
77+
notify(node::chase::block, { 9_u32 });
78+
79+
const auto response = get(R"({"id":801,"method":"blockchain.estimatefee","params":[0,"basic"]})" "\n");
80+
REQUIRE_NO_THROW_TRUE(response.at("result").is_int64());
81+
82+
// None of the first 10 blocks have fees, so no estimate is obtained.
83+
BOOST_REQUIRE_EQUAL(response.at("result").as_int64(), -1);
84+
}
6285

6386
// blockchain.relayfee
6487

test/protocols/electrum/electrum_setup_fixture.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ electrum_setup_fixture::electrum_setup_fixture(const initializer& setup,
6666
database_settings.interval_depth = 2;
6767
node_settings.delay_inbound = false;
6868
node_settings.minimum_fee_rate = 99.0;
69+
node_settings.fee_estimate_horizon = 8;
6970
network_settings.inbound.connections = 0;
7071
network_settings.outbound.connections = 0;
7172

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

77-
// Run the server.
78+
std::promise<code> started{};
79+
server_.start([&](const code& ec) NOEXCEPT
80+
{
81+
started.set_value(ec);
82+
});
83+
84+
// Block until server is started.
85+
ec = started.get_future().get();
86+
BOOST_REQUIRE_MESSAGE(!ec, ec.message());
87+
7888
std::promise<code> running{};
7989
server_.run([&](const code& ec) NOEXCEPT
8090
{

0 commit comments

Comments
 (0)