• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

libbitcoin / libbitcoin-network / 41739478557

03 Nov 2026 11:28PM UTC coverage: 93.912% (-0.1%) from 94.038%
41739478557

push

github

web-flow
Merge pull request #1004 from evoskuil/master

Define rpc body members w/o specialize, dispatch without cross-thread wakes.

209 of 219 new or added lines in 4 files covered. (95.43%)

134 existing lines in 39 files now uncovered.

14531 of 15473 relevant lines covered (93.91%)

2488.61 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.96
/src/settings.cpp
1
/**
2
 * Copyright (c) 2011-2026 libbitcoin developers
3
 *
4
 * This file is part of libbitcoin.
5
 *
6
 * This program is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU Affero General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU Affero General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU Affero General Public License
17
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
#include <bitcoin/network/settings.hpp>
20

21
#include <filesystem>
22
#include <bitcoin/network/async/async.hpp>
23
#include <bitcoin/network/config/config.hpp>
24
#include <bitcoin/network/messages/messages.hpp>
25

26
namespace libbitcoin {
27
namespace network {
28

29
using namespace system;
30
using namespace messages::peer;
31

32
// Default (zero) threads is capped at this many hardware threads.
33
constexpr auto maximum_default_threads = 32_size;
34

35
static bool read_text(std::string& out,
41 ✔
36
    const std::filesystem::path& file) NOEXCEPT
37
{
38
    system::ifstream stream{ file };
41 ✔
39
    if (!stream.good())
41 ✔
40
        return false;
41

42
    out.assign(std::istreambuf_iterator<char>(stream), {});
39 ✔
43
    return true;
39 ✔
44
}
41 ✔
45

46
static void set_default_port(config::authorities& authorities,
4 ✔
47
    uint16_t port) NOEXCEPT
48
{
49
    for (auto& authority: authorities)
9 ✔
50
        if (is_zero(authority.port()))
5 ✔
51
            authority = { authority.ip(), port, authority.cidr() };
4 ✔
52
}
4 ✔
53

54
// socks5
55
// ----------------------------------------------------------------------------
56

57
settings::socks5::socks5() NOEXCEPT
1,032 ✔
58
{
59
}
1,032 ✔
60

61
bool settings::socks5::proxied() const NOEXCEPT
259 ✔
62
{
63
    return is_nonzero(socks.port());
259 ✔
64
}
65

66
bool settings::socks5::authenticated() const NOEXCEPT
31 ✔
67
{
68
    return !(username.empty() && password.empty());
31 ✔
69
}
70

71
// sam
72
// ----------------------------------------------------------------------------
73

74
settings::sam::sam() NOEXCEPT
553 ✔
75
{
76
}
553 ✔
77

78
// geti2p.net/en/docs/api/samv3
79
// A destination is a fixed prefix and a length-prefixed certificate.
80
constexpr auto destination_size = 387_size;
81
constexpr auto certificate_position = 385_size;
82

83
// I2P has no ports.
84
constexpr uint16_t sam_port = 0;
85

86
// The destination (public) prefix of a private key, empty if truncated.
87
static data_chunk to_destination(const data_chunk& private_key) NOEXCEPT
17 ✔
88
{
89
    read::bytes::copy source(private_key);
17 ✔
90
    source.skip_bytes(certificate_position);
17 ✔
91
    const auto size = destination_size + source.read_2_bytes_big_endian();
17 ✔
92

93
    if (!source || private_key.size() < size)
17 ✔
94
        return {};
2 ✔
95

96
    return { private_key.begin(), std::next(private_key.begin(), size) };
30 ✔
97
}
17 ✔
98

99
// The address of a stored private key (base64), unspecified if invalid.
100
config::address settings::sam::to_self(const std::string& key) NOEXCEPT
19 ✔
101
{
102
    data_chunk private_key{};
19 ✔
103
    if (!decode_base64(private_key, key))
19 ✔
104
        return {};
2 ✔
105

106
    const auto destination = to_destination(private_key);
17 ✔
107
    if (destination.empty())
17 ✔
108
        return {};
2 ✔
109

110
    return address_item
15 ✔
111
    {
112
        unix_time(), service::node_none, i2p_t{ sha256_hash(destination) },
30 ✔
113
        sam_port
114
    };
15 ✔
115
}
19 ✔
116

117
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
118
code settings::sam::initialize() NOEXCEPT
3 ✔
119
{
120
    if (key_path.empty())
3 ✔
121
        return error::success;
2 ✔
122

123
    try
124
    {
125
        ifstream file{ key_path, ifstream::in };
1 ✔
126
        if (file.good() && !!std::getline(file, key) && !key.empty())
1 ✔
127
            self = to_self(key);
×
128
    }
1 ✔
129
    catch (const std::exception&)
×
130
    {
UNCOV
131
        key.clear();
×
132
    }
×
133

134
    return error::success;
1 ✔
135
}
136
BC_POP_WARNING()
137

138
bool settings::sam::bridged() const NOEXCEPT
124 ✔
139
{
140
    return is_nonzero(bridge.port());
124 ✔
141
}
142

143
bool settings::sam::authenticated() const NOEXCEPT
3 ✔
144
{
145
    return !(username.empty() && password.empty());
3 ✔
146
}
147

148
// tcp_server
149
// ----------------------------------------------------------------------------
150

151
settings::tcp_server::tcp_server(const std::string_view& logging_name) NOEXCEPT
1,891 ✔
152
  : name(logging_name)
1,891 ✔
153
{
154
}
1,891 ✔
155

156
bool settings::tcp_server::enabled() const NOEXCEPT
288 ✔
157
{
158
    return to_bool(connections);
288 ✔
159
}
160

161
steady_clock::duration settings::tcp_server::inactivity() const NOEXCEPT
207 ✔
162
{
163
    return minutes{ inactivity_minutes };
207 ✔
164
}
165

166
steady_clock::duration settings::tcp_server::expiration() const NOEXCEPT
204 ✔
167
{
168
    return minutes{ expiration_minutes };
204 ✔
169
}
170

171
io::endpoint settings::tcp_server::binding(size_t slot) const NOEXCEPT
176 ✔
172
{
173
    if (binds.empty())
176 ✔
174
        return {};
170 ✔
175

176
    return binds.at(slot % binds.size()).to_endpoint();
6 ✔
177
}
178

179
void settings::tcp_server::default_ports() NOEXCEPT
3 ✔
180
{
181
    set_default_port(binds, bind_port);
3 ✔
182
}
3 ✔
183

184
// secure_server
185
// ----------------------------------------------------------------------------
186

187
settings::secure_server::secure_server(
51 ✔
188
    const std::string_view& logging_name) NOEXCEPT
51 ✔
189
  : tcp_server(logging_name)
51 ✔
190
{
191
}
51 ✔
192

193
void settings::secure_server::default_ports() NOEXCEPT
1 ✔
194
{
195
    tcp_server::default_ports();
1 ✔
196
    set_default_port(safes, safe_port);
1 ✔
197
}
1 ✔
198

199
bool settings::secure_server::secure() const NOEXCEPT
11 ✔
200
{
201
    return false;
11 ✔
202
}
203

204
bool settings::secure_server::authenticate() const NOEXCEPT
6 ✔
205
{
206
    return false;
6 ✔
207
}
208

209
code settings::secure_server::initialize_context() NOEXCEPT
1 ✔
210
{
211
    return error::success;
1 ✔
212
}
213

214
settings::transport settings::secure_server::clear_context() const NOEXCEPT
6 ✔
215
{
216
    return {};
6 ✔
217
}
218

219
settings::transport settings::secure_server::secure_context() const NOEXCEPT
1 ✔
220
{
221
    return {};
1 ✔
222
}
223

224
// tls_server
225
// ----------------------------------------------------------------------------
226

227
settings::tls_server::tls_server(const std::string_view& logging_name) NOEXCEPT
37 ✔
228
  : secure_server(logging_name)
37 ✔
229
{
230
}
37 ✔
231

232
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
233
code settings::tls_server::initialize_context() NOEXCEPT
26 ✔
234
{
235
    if (context)
26 ✔
236
        return error::operation_failed;
1 ✔
237

238
    context = std::make_unique<tls::context>();
25 ✔
239
    if (safes.empty())
25 ✔
240
        return error::success;
3 ✔
241

242
    if (cert_path.empty())
22 ✔
243
        return error::tls_use_certificate;
1 ✔
244

245
    if (key_path.empty())
21 ✔
246
        return error::tls_use_private_key;
1 ✔
247

248
    std::string text{};
20 ✔
249
    if (!read_text(text, cert_path) || !context->set_chain(text))
20 ✔
250
        return error::tls_use_certificate;
3 ✔
251

252
    const auto key = read_text(text, key_path) &&
34 ✔
253
        context->set_key(text, key_pass);
17 ✔
254

255
    wipe(text.data(), text.size());
17 ✔
256
    if (!key)
17 ✔
257
        return error::tls_use_private_key;
3 ✔
258

259
    if (authenticate())
14 ✔
260
    {
261
        // Each certificate file of the directory is an anchor.
262
        context->set_verify(true, true);
4 ✔
263
        std::error_code ec{};
4 ✔
264
        std::filesystem::directory_iterator it{ cert_auth, ec };
4 ✔
265
        for (const std::filesystem::directory_iterator end{};
4 ✔
266
            !ec && it != end; it.increment(ec))
8 ✔
267
        {
268
            std::error_code ignore{};
4 ✔
269
            if (it->is_regular_file(ignore) && read_text(text, it->path()))
4 ✔
270
                context->add_anchors(text);
4 ✔
271
        }
4 ✔
272

273
        if (ec)
4 ✔
274
            return error::tls_set_add_verify;
1 ✔
275
    }
4 ✔
276

277
    return error::success;
13 ✔
278
}
20 ✔
279
BC_POP_WARNING();
280

281
bool settings::tls_server::secure() const NOEXCEPT
23 ✔
282
{
283
    return !safes.empty() && !cert_path.empty() && !key_path.empty();
23 ✔
284
}
285

286
bool settings::tls_server::authenticate() const NOEXCEPT
18 ✔
287
{
288
    return secure() && !cert_auth.empty();
18 ✔
289
}
290

291
settings::transport settings::tls_server::secure_context() const NOEXCEPT
11 ✔
292
{
293
    BC_ASSERT(context);
11 ✔
294
    return std::cref(*context);
11 ✔
295
}
296

297
// zmtp_server
298
// ----------------------------------------------------------------------------
299

300
settings::zmtp_server::zmtp_server(
8 ✔
301
    const std::string_view& logging_name) NOEXCEPT
8 ✔
302
  : secure_server(logging_name)
8 ✔
303
{
304
}
8 ✔
305

306
bool settings::zmtp_server::secure() const NOEXCEPT
10 ✔
307
{
308
    const system::data_chunk& secret = key;
10 ✔
309
    return !safes.empty() && !secret.empty();
10 ✔
310
}
311

312
bool settings::zmtp_server::authenticate() const NOEXCEPT
2 ✔
313
{
314
    return secure() && !certs.empty();
2 ✔
315
}
316

317
code settings::zmtp_server::initialize_context() NOEXCEPT
7 ✔
318
{
319
    if (context)
7 ✔
320
        return error::operation_failed;
1 ✔
321

322
    system::data_stack clients{};
6 ✔
323
    for (const system::data_chunk& cert: certs)
8 ✔
324
        clients.push_back(cert);
2 ✔
325

326
    context = std::make_unique<zmtp::context>(key, clients);
6 ✔
327

328
    if (!secure())
6 ✔
329
        return error::success;
3 ✔
330

331
    return context->curve() ? error::success : error::invalid_configuration;
5 ✔
332
}
6 ✔
333

334
settings::transport settings::zmtp_server::clear_context() const NOEXCEPT
1 ✔
335
{
336
    return std::cref(clear);
1 ✔
337
}
338

339
settings::transport settings::zmtp_server::secure_context() const NOEXCEPT
1 ✔
340
{
341
    BC_ASSERT(context);
1 ✔
342
    return std::cref(*context);
1 ✔
343
}
344

345
// http_server
346
// ----------------------------------------------------------------------------
347

348
code settings::http_server::initialize_context() NOEXCEPT
×
349
{
350
    if (const auto ec = tls_server::initialize_context())
×
351
        return ec;
×
352

353
    // secure changes default port from 80 to 443.
354
    const auto port = secure() ? http::default_tls : http::default_http;
×
355
    host_names_ = config::to_host_names(hosts, port);
×
356
    origin_names_ = config::to_host_names(origins, port);
×
357
    return error::success;
×
358
}
359

360
const system::string_list& settings::http_server::host_names() const NOEXCEPT
2 ✔
361
{
362
    return host_names_;
2 ✔
363
}
364

365
const system::string_list& settings::http_server::origin_names() const NOEXCEPT
2 ✔
366
{
367
    return origin_names_;
2 ✔
368
}
369

370
// local
371
static config::credentials::const_iterator to_credential(
16 ✔
372
    const config::credentials& credentials,
373
    const hash_digest& digest) NOEXCEPT
374
{
375
    return std::find_if(credentials.begin(), credentials.end(),
16 ✔
376
        [&digest](const config::credential& credential) NOEXCEPT
16 ✔
377
        {
378
            return credential.digest() == digest;
16 ✔
379
        });
16 ✔
380
}
381

382
bool settings::http_server::authorize() const NOEXCEPT
32 ✔
383
{
384
    return !credentials.empty();
32 ✔
385
}
386

387
bool settings::http_server::authorized(const hash_digest& digest) const NOEXCEPT
8 ✔
388
{
389
    return to_credential(credentials, digest) != credentials.end();
8 ✔
390
}
391

392
bool settings::http_server::permitted(const hash_digest& digest,
8 ✔
393
    const std::string& method) const NOEXCEPT
394
{
395
    const auto credential = to_credential(credentials, digest);
8 ✔
396
    return credential != credentials.end() && credential->permitted(method);
8 ✔
397
}
398

399
// websocket_server
400
// ----------------------------------------------------------------------------
401

402
// [outbound]
403
// ----------------------------------------------------------------------------
404

405
bool settings::peer_outbound::enabled() const NOEXCEPT
127 ✔
406
{
407
    return settings::tcp_server::enabled()
127 ✔
408
        && to_bool(host_pool_capacity)
40 ✔
409
        && to_bool(connect_batch_size);
165 ✔
410
}
411

412
size_t settings::peer_outbound::minimum_address_count() const NOEXCEPT
91 ✔
413
{
414
    // Cannot overflow size_t as long as both are uint16_t.
415
    return connect_batch_size * peer_outbound::connections;
91 ✔
416
}
417

418
steady_clock::duration settings::peer_outbound::seeding_timeout() const NOEXCEPT
83 ✔
419
{
420
    return seconds(seeding_timeout_seconds);
83 ✔
421
}
422

423
// [inbound]
424
// ----------------------------------------------------------------------------
425

426
config::address settings::peer_inbound::first_self() const NOEXCEPT
120 ✔
427
{
428
    return selfs.empty() ? config::address{} : selfs.front();
120 ✔
429
}
430

431
bool settings::peer_inbound::advertise() const NOEXCEPT
41 ✔
432
{
433
    return enabled() && (!selfs.empty() || bridged());
41 ✔
434
}
435

436
bool settings::peer_inbound::enabled() const NOEXCEPT
151 ✔
437
{
438
    return settings::tcp_server::enabled() && (!binds.empty() || bridged());
151 ✔
439
}
440

441
// [manual]
442
// ----------------------------------------------------------------------------
443

444
void settings::peer_manual::initialize() NOEXCEPT
5 ✔
445
{
446
    BC_ASSERT_MSG(friends.empty(), "friends not empty");
5 ✔
447

448
    // Dynamic conversion of peers is O(N^2), so set on initialize.
449
    // This converts endpoints to addresses so will produce the default
450
    // address for any hosts that are DNS names (i.e. not IP addresses).
451
    friends = system::projection<network::config::addresses>(peers);
5 ✔
452
}
5 ✔
453

454
bool settings::peer_manual::peered(const address_item& item) const NOEXCEPT
110 ✔
455
{
456
    // Friends should be mapped from peers by initialize().
457
    return contains(friends, item);
110 ✔
458
}
459

460
bool settings::peer_manual::enabled() const NOEXCEPT
137 ✔
461
{
462
    // connections field is not currently used, and this applies only to
463
    // initial configuration of manual connections, as they can be added.
464
    return /* settings::tcp_server::enabled() && */ !peers.empty();
137 ✔
465
}
466

467
// [network]
468
// ----------------------------------------------------------------------------
469

470
static_assert(heading::maximum_payload(true) == 4'000'000);
471
static_assert(heading::maximum_payload(false) == 1'800'003);
472

473
static uint32_t identifier_from_context(chain::selection context) NOEXCEPT
496 ✔
474
{
475
    switch (context)
496 ✔
476
    {
477
        case chain::selection::mainnet:  return 3652501241;
478
        case chain::selection::testnet3: return 118034699;
1 ✔
479
        case chain::selection::testnet4: return 675223068;
1 ✔
480
        case chain::selection::regtest:  return 3669344250;
1 ✔
481
        case chain::selection::signet:   return 1087308554;
1 ✔
482
        default: return 0;
458 ✔
483
    }
484
}
485

486
settings::settings(chain::selection context) NOEXCEPT
496 ✔
487
  : network(context),
496 ✔
488
    outbound{ context },
496 ✔
489
    inbound{ context },
496 ✔
490
    manual{ context },
496 ✔
491
    identifier(identifier_from_context(context))
992 ✔
492
{
493
}
496 ✔
494

495
// Derived values are read and computed here, and are const thereafter.
496
code settings::initialize() NOEXCEPT
1 ✔
497
{
498
    peer.identifier = identifier;
1 ✔
499
    manual.initialize();
1 ✔
500
    return inbound.initialize();
1 ✔
501
}
502

503
settings::transport settings::peer_context() const NOEXCEPT
3 ✔
504
{
505
    return std::cref(peer);
3 ✔
506
}
507

508
size_t settings::threads_() const NOEXCEPT
287 ✔
509
{
510
    // Zero implies the lesser of hardware threads and the default cap.
511
    return to_bool(threads) ? threads :
287 ✔
512
        std::min(cores(), maximum_default_threads);
286 ✔
513
}
514

515
// Randomized from 50% to maximum milliseconds (specified in seconds).
516
steady_clock::duration settings::retry_timeout() const NOEXCEPT
156 ✔
517
{
518
    const auto from = retry_timeout_seconds * 500_u64;
156 ✔
519
    const auto to = retry_timeout_seconds * 1'000_u64;
156 ✔
520
    return milliseconds{ system::maybe_random::next(from, to) };
156 ✔
521
}
522

523
steady_clock::duration settings::channel_handshake() const NOEXCEPT
218 ✔
524
{
525
    return seconds(handshake_timeout_seconds);
218 ✔
526
}
527

528
steady_clock::duration settings::channel_heartbeat() const NOEXCEPT
159 ✔
529
{
530
    return minutes(channel_heartbeat_minutes);
159 ✔
531
}
532

533
steady_clock::duration settings::maximum_skew() const NOEXCEPT
1 ✔
534
{
535
    return minutes(maximum_skew_minutes);
1 ✔
536
}
537

538
std::filesystem::path settings::file() const NOEXCEPT
72 ✔
539
{
540
    BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
541
    return path / "hosts.cache";
72 ✔
542
    BC_POP_WARNING()
543
}
544

545
bool settings::gossip_v2() const NOEXCEPT
33 ✔
546
{
547
    // Only tor and i2p addresses are unrepresentable in the v1 protocol.
548
    return gossip_tor || gossip_i2p;
33 ✔
549
}
550

551
// Randomized from 50% to maximum milliseconds (specified in seconds).
552
steady_clock::duration settings::connect_timeout(
255 ✔
553
    const tcp_server& options) const NOEXCEPT
554
{
555
    const auto seconds_ = to_bool(options.connect_timeout_seconds) ?
255 ✔
556
        options.connect_timeout_seconds : connect_timeout_seconds;
557

558
    const auto from = seconds_ * 500_u64;
255 ✔
559
    const auto to = seconds_ * 1'000_u64;
255 ✔
560
    return milliseconds{ system::maybe_random::next(from, to) };
255 ✔
561
}
562

563
uint32_t settings::rate_limited(const tcp_server& options) const NOEXCEPT
208 ✔
564
{
565
    return to_bool(rate_limit) && to_bool(options.rate_limit) ?
208 ✔
566
        std::min(rate_limit, options.rate_limit) :
2 ✔
567
        std::max(rate_limit, options.rate_limit);
206 ✔
568
}
569

570
bool settings::connectable(const address_item& item) const NOEXCEPT
22 ✔
571
{
572
    // A proxy connects by name, otherwise only an ip address is routable.
573
    return is_v4(item.address) || is_v6(item.address) ||
18 ✔
574
        is_cjdns(item.address) || (outbound.proxied() && is_named(item.address));
32 ✔
575
}
576

577
bool settings::gossiped(const address_item& item) const NOEXCEPT
121 ✔
578
{
579
    switch (item.address.index())
121 ✔
580
    {
581
        case ipv4_t::id:
90 ✔
582
            return gossip_ipv4;
90 ✔
583
        case ipv6_t::id:
22 ✔
584
        case cjdns_t::id:
585
            return gossip_ipv6;
22 ✔
586
        case torv3_t::id:
4 ✔
587
            return gossip_tor;
4 ✔
588
        case i2p_t::id:
2 ✔
589
            return gossip_i2p;
2 ✔
590
        default:
591
            return false;
592
    }
593
}
594

595
bool settings::unsupported(const address_item& item) const NOEXCEPT
99 ✔
596
{
597
    return to_bool(bit_and(item.services, invalid_services));
99 ✔
598
}
599

600
bool settings::blacklisted(const address_item& item) const NOEXCEPT
174 ✔
601
{
602
    return contains(blacklists, item);
174 ✔
603
}
604

605
bool settings::whitelisted(const address_item& item) const NOEXCEPT
175 ✔
606
{
607
    return whitelists.empty() || contains(whitelists, item);
175 ✔
608
}
609

610
bool settings::excluded(const address_item& item) const NOEXCEPT
69 ✔
611
{
612
    return !is_specified(item)
69 ✔
613
        || !gossiped(item)
67 ✔
614
        || unsupported(item)
67 ✔
615
        || manual.peered(item)
64 ✔
616
        || blacklisted(item)
64 ✔
617
        || !whitelisted(item);
133 ✔
618
}
619

620
} // namespace network
621
} // namespace libbitcoin
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc