• 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

96.48
/src/net.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/net.hpp>
20

21
#include <bitcoin/network/async/async.hpp>
22
#include <bitcoin/network/channels/channels.hpp>
23
#include <bitcoin/network/config/config.hpp>
24
#include <bitcoin/network/define.hpp>
25
#include <bitcoin/network/log/log.hpp>
26
#include <bitcoin/network/messages/messages.hpp>
27
#include <bitcoin/network/net/net.hpp>
28
#include <bitcoin/network/protocols/protocols.hpp>
29
#include <bitcoin/network/sessions/sessions.hpp>
30
#include <bitcoin/network/settings.hpp>
31

32
namespace libbitcoin {
33
namespace network {
34

35
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
36

37
using namespace system;
38
using namespace std::placeholders;
39
constexpr auto encryption = messages::peer::service::node_encrypted_transport;
40

41
// Seeding is not repeated within this many seeding timeouts of completion.
42
constexpr auto seed_backoff = 10;
43

44
net::net(const settings& settings, const logger& log,
286 ✔
45
    uint64_t required_services, uint64_t provided_services) NOEXCEPT
286 ✔
46
  : reporter(log),
47
    settings_(settings),
286 ✔
48
    privacy_(to_bool(bit_and(provided_services, to_value(encryption)))),
286 ✔
49
    threadpool_(settings.threads_()),
286 ✔
50
    strand_(threadpool_.service().get_executor()),
286 ✔
51
    hosts_(settings, log, required_services)
572 ✔
52
{
53
    ////LOG_LOG("Aplication log compiled..: ", news_defined);
54
    ////LOG_LOG("News log compiled........: ", news_defined);
55
    ////LOG_LOG("Session log compiled.....: ", session_defined);
56
    ////LOG_LOG("Protocol log compiled....: ", protocol_defined);
57
    ////LOG_LOG("ProXy log compiled.......: ", proxy_defined);
58
    ////LOG_LOG("Remote log compiled......: ", remote_defined);
59
    ////LOG_LOG("Fault log compiled.......: ", fault_defined);
60
    ////LOG_LOG("Quitting log compiled....: ", quitting_defined);
61
    ////LOG_LOG("Object log compiled......: ", objects_defined);
62
    ////LOG_LOG("Verbose log compiled.....: ", verbose_defined);
63
}
286 ✔
64

65
net::~net() NOEXCEPT
286 ✔
66
{
67
    // Weak references in threadpool closures safe as net joins threads here.
68
    net::close();
286 ✔
69
}
286 ✔
70

71
// I/O factories.
72
// ----------------------------------------------------------------------------
73

74
// server
75
acceptor::ptr net::create_service(socket::parameters&& params) NOEXCEPT
7 ✔
76
{
77
    return emplace_shared<acceptor>(log, strand(), service(),
78
        service_suspended_, std::move(params));
7 ✔
79
}
80

81
// inbound
82
acceptor::ptr net::create_acceptor(const socket::context& context) NOEXCEPT
84 ✔
83
{
84
    const auto& settings = network_settings();
84 ✔
85

86
    // bip324 (v2) inbound acceptance, v1 peers detected and passed through.
87
    const auto accept = privacy_ &&
84 ✔
88
        std::holds_alternative<std::monostate>(context) ?
84 ✔
89
            settings.peer_context() : context;
84 ✔
90

91
    socket::parameters params
84 ✔
92
    {
93
        .connect_timeout = settings.connect_timeout(settings.inbound),
84 ✔
94
        .maximum_request = settings.inbound.maximum_request,
84 ✔
95
        .minimum_buffer = settings.inbound.minimum_buffer,
84 ✔
96
        .maximum_buffer = settings.inbound.maximum_buffer,
84 ✔
97
        .context = accept
98
    };
84 ✔
99

100
    return emplace_shared<acceptor>(log, strand(), service(),
101
        accept_suspended_, std::move(params));
84 ✔
102
}
103

104
// inbound (sam)
105
acceptor_sam::ptr net::create_acceptor_sam() NOEXCEPT
2 ✔
106
{
107
    const auto& settings = network_settings();
2 ✔
108

109
    // bip324 (v2) inbound acceptance, v1 peers detected and passed through.
110
    socket::parameters params
2 ✔
111
    {
112
        .connect_timeout = settings.connect_timeout(settings.inbound),
2 ✔
113
        .maximum_request = settings.inbound.maximum_request,
2 ✔
114
        .minimum_buffer = settings.inbound.minimum_buffer,
2 ✔
115
        .maximum_buffer = settings.inbound.maximum_buffer,
2 ✔
116
        .context = privacy_ ? settings.peer_context() : socket::context{}
1 ✔
117
    };
3 ✔
118

119
    return emplace_shared<acceptor_sam>(log, strand(), service(),
120
        accept_suspended_, std::move(params), settings.inbound);
2 ✔
121
}
122

123
connector::ptr net::to_connector(const settings::socks5& socks,
89 ✔
124
    const settings::tcp_server& options,
125
    const steady_clock::duration& connect_timeout, size_t slot) NOEXCEPT
126
{
127
    socket::parameters params
89 ✔
128
    {
129
        .connect_timeout = connect_timeout,
130
        .maximum_request = options.maximum_request,
89 ✔
131
        .minimum_buffer = options.minimum_buffer,
89 ✔
132
        .maximum_buffer = options.maximum_buffer,
89 ✔
133
        .bind = options.binding(slot),
89 ✔
134
        .slot = slot
135
    };
89 ✔
136

137
    if (privacy_)
89 ✔
138
        params.context = network_settings().peer_context();
1 ✔
139

140
    if (socks.proxied())
89 ✔
141
        return emplace_shared<connector_socks>(log, strand(), service(),
6 ✔
142
            connect_suspended_, std::move(params), socks);
6 ✔
143

144
    // Above can handle both proxy and non-proxy, but this is more efficient.
145
    return emplace_shared<connector>(log, strand(), service(),
146
        connect_suspended_, std::move(params));
86 ✔
147
}
148

149
// outbound (general)
150
connector::ptr net::create_connector(const settings::socks5& socks,
157 ✔
151
    const settings::tcp_server& options, size_t slot) NOEXCEPT
152
{
153
    return to_connector(socks, options,
157 ✔
154
        network_settings().connect_timeout(options), slot);
157 ✔
155
}
156

157
// outbound (seed)
158
connector::ptr net::create_seed_connector(size_t slot) NOEXCEPT
52 ✔
159
{
160
    const auto& settings = network_settings();
52 ✔
161

162
    return to_connector(settings.outbound, settings.outbound,
52 ✔
163
        settings.outbound.seeding_timeout(), slot);
52 ✔
164
}
165

166
// outbound (manual)
167
connector::ptr net::create_manual_connector(size_t slot) NOEXCEPT
35 ✔
168
{
169
    const auto& settings = network_settings();
35 ✔
170

171
    return create_connector(settings.manual, settings.manual, slot);
35 ✔
172
}
173

174
// outbound (batch)
175
connectors_ptr net::create_connectors(size_t count, size_t slot) NOEXCEPT
59 ✔
176
{
177
    const auto& settings = network_settings();
59 ✔
178
    const auto connects = to_shared<connectors>();
59 ✔
179
    connects->reserve(count);
59 ✔
180

181
    for (size_t connect{}; connect < count; ++connect)
181 ✔
182
        connects->push_back(create_connector(settings.outbound,
122 ✔
183
            settings.outbound, slot));
184

185
    return connects;
59 ✔
UNCOV
186
}
×
187

188
// Start sequence.
189
// ----------------------------------------------------------------------------
190

191
void net::start(result_handler&& handler) NOEXCEPT
147 ✔
192
{
193
    // Threadpool is started on construct, stopped only by work starvation.
194
    io::post(strand_,
147 ✔
195
        std::bind(&net::do_start, this, std::move(handler)));
294 ✔
196
}
147 ✔
197

198
void net::do_start(const result_handler& handler) NOEXCEPT
147 ✔
199
{
200
    BC_ASSERT(stranded());
147 ✔
201
    raise_descriptor_limit();
147 ✔
202
    manual_ = attach_manual_session();
147 ✔
203
    manual_->start(std::bind(&net::handle_start, this, _1, handler));
147 ✔
204
}
147 ✔
205

206
void net::handle_start(const code& ec, const result_handler& handler) NOEXCEPT
147 ✔
207
{
208
    BC_ASSERT(stranded());
147 ✔
209

210
    if (ec)
147 ✔
211
    {
212
        handler(ec);
4 ✔
213
        return;
4 ✔
214
    }
215

216
    // Deserialize hosts from file.
217
    if (const auto error_code = start_hosts())
143 ✔
218
    {
219
        LOGF("Hosts file failed to deserialize, " << error_code.message());
2 ✔
220
        handler(error_code);
2 ✔
221
        return;
2 ✔
222
    }
223

224
    seed_ = attach_seed_session();
141 ✔
225
    seed_->start(move_copy(handler));
141 ✔
226
}
227

228
// Run sequence (seeding may be ongoing after its handler is invoked).
229
// ----------------------------------------------------------------------------
230

231
void net::run(result_handler&& handler) NOEXCEPT
113 ✔
232
{
233
    // Public methods can complete on caller thread.
234
    if (closed())
113 ✔
235
    {
236
        handler(error::service_stopped);
5 ✔
237
        return;
5 ✔
238
    }
239

240
    io::post(strand_,
108 ✔
241
        std::bind(&net::do_run, this, std::move(handler)));
216 ✔
242
}
243

244
void net::do_run(const result_handler& handler) NOEXCEPT
109 ✔
245
{
246
    BC_ASSERT(stranded());
109 ✔
247

248
    if (closed())
109 ✔
249
    {
250
        handler(error::service_stopped);
1 ✔
251
        return;
1 ✔
252
    }
253

254
    // Start manual connections.
255
    for (const auto& peer: network_settings().manual.peers)
113 ✔
256
        do_connect(peer);
5 ✔
257

258
    // Start inbound connections.
259
    attach_inbound_session()->start(
324 ✔
260
        std::bind(&net::handle_run, this, _1, handler));
216 ✔
261
}
262

263
void net::handle_run(const code& ec, const result_handler& handler) NOEXCEPT
108 ✔
264
{
265
    BC_ASSERT(stranded());
108 ✔
266

267
    if (ec)
108 ✔
268
    {
269
        handler(ec);
2 ✔
270
        return;
2 ✔
271
    }
272

273
    attach_outbound_session()->start(move_copy(handler));
106 ✔
274
}
275

276
// Shutdown sequence.
277
// ----------------------------------------------------------------------------
278

279
// Results in std::abort if called from a thread within the threadpool.
280
void net::close() NOEXCEPT
428 ✔
281
{
282
    closed_.store(true);
428 ✔
283
    io::post(strand_,
428 ✔
284
        std::bind(&net::do_close, this));
428 ✔
285

286
    // Blocks on join of all threadpool threads.
287
    if (!threadpool_.join())
428 ✔
288
    {
289
        BC_ASSERT_MSG(false, "failed to join threadpool");
×
290
        std::abort();
291
    }
292

293
    // Serialize hosts to file.
294
    if (const auto error_code = stop_hosts())
428 ✔
295
    {
296
        LOGF("Hosts file failed to serialize, " << error_code.message());
×
297
    }
298
}
428 ✔
299

300
void net::do_close() NOEXCEPT
286 ✔
301
{
302
    BC_ASSERT(stranded());
286 ✔
303

304
    // Release session references (also held by stop subscriber).
305
    if (manual_) manual_.reset();
286 ✔
306
    if (seed_) seed_.reset();
286 ✔
307

308
    // Notify and delete all stop subscribers (all sessions).
309
    stop_subscriber_.stop(error::service_stopped);
286 ✔
310
    service_subscriber_.stop(error::service_stopped);
286 ✔
311

312
    // Notify and delete subscribers to channel notifications.
313
    connect_subscriber_.stop_default(error::service_stopped);
286 ✔
314

315
    // Notify and delete subscribers to message broadcast notifications.
316
    broadcaster_.stop(error::service_stopped);
286 ✔
317

318
    // Stop threadpool keep-alive, all work must self-terminate to affect join.
319
    threadpool_.stop();
286 ✔
320
}
286 ✔
321

322
bool net::closed() const NOEXCEPT
370 ✔
323
{
324
    return closed_.load();
370 ✔
325
}
326

327
// Suspensions.
328
// ----------------------------------------------------------------------------
329
// Service connections are managed independently from P2P.
330

331
void net::suspend_services() NOEXCEPT
1 ✔
332
{
333
    service_suspended_.store(true);
1 ✔
334
}
1 ✔
335

336
void net::resume_services() NOEXCEPT
×
337
{
UNCOV
338
    service_suspended_.store(false);
×
339
}
×
340

341
void net::suspend(const code&) NOEXCEPT
2 ✔
342
{
343
    suspend_acceptors();
2 ✔
344
    suspend_connectors();
2 ✔
345
}
2 ✔
346

347
bool net::resume() NOEXCEPT
1 ✔
348
{
349
    resume_acceptors();
1 ✔
350
    resume_connectors();
1 ✔
351
    return true;
1 ✔
352
}
353

354
void net::stop_services(const code& ec) NOEXCEPT
1 ✔
355
{
356
    io::post(strand_,
1 ✔
357
        std::bind(&net::do_stop_services, this, ec));
1 ✔
358
}
1 ✔
359

360
void net::do_stop_services(const code& ec) NOEXCEPT
1 ✔
361
{
362
    BC_ASSERT(stranded());
1 ✔
363
    suspend_services();
1 ✔
364
    service_subscriber_.stop(ec);
1 ✔
365
}
1 ✔
366

367
bool net::suspended() const NOEXCEPT
3 ✔
368
{
369
    return connect_suspended_.load() || accept_suspended_.load();
3 ✔
370
}
371

372
void net::suspend_acceptors() NOEXCEPT
2 ✔
373
{
374
    accept_suspended_.store(true);
2 ✔
375
}
2 ✔
376

377
void net::resume_acceptors() NOEXCEPT
1 ✔
378
{
379
    accept_suspended_.store(false);
1 ✔
380
}
1 ✔
381

382
void net::suspend_connectors() NOEXCEPT
2 ✔
383
{
384
    connect_suspended_.store(true);
2 ✔
385
}
2 ✔
386

387
void net::resume_connectors() NOEXCEPT
1 ✔
388
{
389
    connect_suspended_.store(false);
1 ✔
390
}
1 ✔
391

392
// Properties.
393
// ----------------------------------------------------------------------------
394

395
const settings& net::network_settings() const NOEXCEPT
5,588 ✔
396
{
397
    return settings_;
5,588 ✔
398
}
399

400
io::context& net::service() NOEXCEPT
322 ✔
401
{
402
    return threadpool_.service();
322 ✔
403
}
404

405
io::strand& net::strand() NOEXCEPT
1,184 ✔
406
{
407
    return strand_;
1,184 ✔
408
}
409

410
bool net::stranded() const NOEXCEPT
9,989 ✔
411
{
412
    return strand_.running_in_this_thread();
9,989 ✔
413
}
414

415

416
// Subscriptions.
417
// ----------------------------------------------------------------------------
418
// Channel and network strands share same pool, and as long as a job is
419
// running in the pool, it will continue to accept work. Therefore handlers
420
// will not be orphaned during a stop as long as they remain in the pool.
421
// But when entering from outside the pool (such as subscribe) handler must be
422
// invoked when stopped as the handler will go uninvoked if the pool empties.
423

424
// public
425
void net::subscribe_connect(channel_notifier&& handler,
3 ✔
426
    channel_completer&& complete) NOEXCEPT
427
{
428
    // Public methods can complete on caller thread.
429
    if (closed())
3 ✔
430
    {
431
        complete(error::service_stopped, {});
1 ✔
432
        handler(error::service_stopped, {});
1 ✔
433
        return;
1 ✔
434
    }
435

436
    io::post(strand_,
2 ✔
437
        std::bind(&net::do_subscribe_connect,
2 ✔
438
            this, std::move(handler), std::move(complete)));
4 ✔
439
}
440

441
void net::do_subscribe_connect(const channel_notifier& handler,
2 ✔
442
    const channel_completer& complete) NOEXCEPT
443
{
444
    BC_ASSERT(stranded());
2 ✔
445

446
    const auto key = create_key();
2 ✔
447
    complete(connect_subscriber_.subscribe(move_copy(handler), key), key);
2 ✔
448
}
2 ✔
449

450
// protected
451
void net::notify_connect(const channel::ptr& channel) NOEXCEPT
64 ✔
452
{
453
    io::post(strand_,
64 ✔
454
        std::bind(&net::do_notify_connect, this, channel));
128 ✔
455
}
64 ✔
456

457
void net::do_notify_connect(const channel::ptr& channel) NOEXCEPT
64 ✔
458
{
459
    BC_ASSERT(stranded());
64 ✔
460
    connect_subscriber_.notify(error::success, channel);
64 ✔
461
}
64 ✔
462

463
void net::unsubscribe_connect(object_key key) NOEXCEPT
1 ✔
464
{
465
    io::post(strand_,
1 ✔
466
        std::bind(&net::do_unsubscribe_connect, this, key));
1 ✔
467
}
1 ✔
468

469
void net::do_unsubscribe_connect(object_key key) NOEXCEPT
1 ✔
470
{
471
    BC_ASSERT(stranded());
1 ✔
472
    connect_subscriber_.notify_one(key, error::desubscribed, nullptr);
1 ✔
473
}
1 ✔
474

475
// protected
476
void net::subscribe_close(stop_handler&& handler) NOEXCEPT
1 ✔
477
{
478
    BC_ASSERT(stranded());
1 ✔
479
    subscribe_close(std::move(handler), create_key());
1 ✔
480
}
1 ✔
481

482
// private
483
code net::subscribe_close(stop_handler&& handler, object_key key) NOEXCEPT
514 ✔
484
{
485
    BC_ASSERT(stranded());
514 ✔
486
    return stop_subscriber_.subscribe(std::move(handler), key);
514 ✔
487
}
488

489
code net::subscribe_service(stop_handler&& handler, object_key key) NOEXCEPT
3 ✔
490
{
491
    BC_ASSERT(stranded());
3 ✔
492
    return service_subscriber_.subscribe(std::move(handler), key);
3 ✔
493
}
494

495
// public
496
void net::subscribe_close(stop_handler&& handler,
2 ✔
497
    stop_completer&& complete) NOEXCEPT
498
{
499
    // Public methods can complete on caller thread.
500
    if (closed())
2 ✔
501
    {
502
        complete(error::service_stopped, {});
1 ✔
503
        handler(error::service_stopped);
1 ✔
504
        return;
1 ✔
505
    }
506

507
    io::post(strand_,
1 ✔
508
        std::bind(&net::do_subscribe_close,
1 ✔
509
            this, std::move(handler), std::move(complete)));
2 ✔
510
}
511

512
void net::do_subscribe_close(const stop_handler& handler,
1 ✔
513
    const stop_completer& complete) NOEXCEPT
514
{
515
    BC_ASSERT(stranded());
1 ✔
516
    
517
    const auto key = create_key();
1 ✔
518
    complete(subscribe_close(move_copy(handler), key), key);
1 ✔
519
}
1 ✔
520

521
void net::unsubscribe_close(object_key key) NOEXCEPT
226 ✔
522
{
523
    io::post(strand_,
226 ✔
524
        std::bind(&net::do_unsubscribe_close, this, key));
226 ✔
525
}
226 ✔
526

527
void net::do_unsubscribe_close(object_key key) NOEXCEPT
226 ✔
528
{
529
    BC_ASSERT(stranded());
226 ✔
530
    stop_subscriber_.notify_one(key, error::desubscribed);
226 ✔
531
}
226 ✔
532

533
// Broadcasts.
534
// ----------------------------------------------------------------------------
535

536
void net::demand_wire_tx(bool demand) NOEXCEPT
4 ✔
537
{
538
    if (demand)
4 ✔
539
        ++wire_tx_demand_;
2 ✔
540
    else
541
        --wire_tx_demand_;
2 ✔
542
}
4 ✔
543

544
void net::demand_wire_block(bool demand) NOEXCEPT
4 ✔
545
{
546
    if (demand)
4 ✔
547
        ++wire_block_demand_;
2 ✔
548
    else
549
        --wire_block_demand_;
2 ✔
550
}
4 ✔
551

552
bool net::wire_tx_demanded() const NOEXCEPT
5 ✔
553
{
554
    return is_nonzero(wire_tx_demand_.load());
5 ✔
555
}
556

557
bool net::wire_block_demanded() const NOEXCEPT
5 ✔
558
{
559
    return is_nonzero(wire_block_demand_.load());
5 ✔
560
}
561

562
// At one object/session/ns, this overflows in ~585 years (and handled).
563
net::object_key net::create_key() NOEXCEPT
1,020 ✔
564
{
565
    BC_ASSERT(stranded());
1,020 ✔
566

567
    if (is_zero(++keys_))
1,020 ✔
568
    {
569
        BC_ASSERT_MSG(false, "overflow");
×
570
        LOGF("Session object overflow.");
571
    }
572

573
    return keys_;
1,020 ✔
574
}
575

576
// P2P Manual connections.
577
// ----------------------------------------------------------------------------
578

579
void net::connect(const config::endpoint& endpoint) NOEXCEPT
18 ✔
580
{
581
    io::post(strand_,
18 ✔
582
        std::bind(&net::do_connect, this, endpoint));
36 ✔
583
}
18 ✔
584

585
void net::do_connect(const config::endpoint& endpoint) NOEXCEPT
23 ✔
586
{
587
    BC_ASSERT(stranded());
23 ✔
588
    if (manual_) manual_->connect(endpoint);
23 ✔
589
}
23 ✔
590

591
void net::connect(const config::endpoint& endpoint,
10 ✔
592
    channel_notifier&& handler) NOEXCEPT
593
{
594
    // Public methods can complete on caller thread.
595
    if (closed())
10 ✔
596
    {
597
        handler(error::service_stopped, {});
1 ✔
598
        return;
1 ✔
599
    }
600

601
    io::post(strand_,
9 ✔
602
        std::bind(&net::do_connect_handled, this, endpoint,
9 ✔
603
            std::move(handler)));
9 ✔
604
}
605

606
void net::do_connect_handled(const config::endpoint& endpoint,
9 ✔
607
    const channel_notifier& handler) NOEXCEPT
608
{
609
    BC_ASSERT(stranded());
9 ✔
610

611
    if (manual_)
9 ✔
612
        manual_->connect(endpoint, move_copy(handler));
8 ✔
613
    else
614
        handler(error::service_stopped, nullptr);
1 ✔
615
}
9 ✔
616

617
// P2P Administration.
618
// ----------------------------------------------------------------------------
619

620
void net::dump_addresses(address_handler&& handler) NOEXCEPT
1 ✔
621
{
622
    fetch(std::move(handler));
1 ✔
623
}
1 ✔
624

625
// P2P Properties.
626
// ----------------------------------------------------------------------------
627

628
size_t net::address_count() const NOEXCEPT
105 ✔
629
{
630
    return hosts_.count();
105 ✔
631
}
632

633
config::address_counts net::address_counts() const NOEXCEPT
1 ✔
634
{
635
    return hosts_.counts();
1 ✔
636
}
637

638
size_t net::reserved_count() const NOEXCEPT
3 ✔
639
{
640
    return hosts_.reserved();
3 ✔
641
}
642

643
void net::fetch_totals(totals_handler&& handler) NOEXCEPT
10 ✔
644
{
645
    if (closed())
10 ✔
646
    {
647
        handler(error::service_stopped, {});
1 ✔
648
        return;
1 ✔
649
    }
650

651
    io::post(strand_,
9 ✔
652
        std::bind(&net::do_fetch_totals, this, std::move(handler)));
18 ✔
653
}
1 ✔
654

655
void net::do_fetch_totals(const totals_handler& handler) NOEXCEPT
9 ✔
656
{
657
    BC_ASSERT(stranded());
9 ✔
658

659
    handler(error::success,
9 ✔
660
    {
661
        peer_sent_.load(),
662
        peer_received_.load(),
663
        actives_,
9 ✔
664
    });
665
}
27 ✔
666

667
size_t net::channel_count() const NOEXCEPT
6 ✔
668
{
669
    return total_channel_count_;
6 ✔
670
}
671

672
size_t net::inbound_channel_count() const NOEXCEPT
152 ✔
673
{
674
    return inbound_channel_count_;
152 ✔
675
}
676

677
// P2P hosts collection.
678
// ----------------------------------------------------------------------------
679
// Protected, called from session (network strand) and channel (network pool).
680

681
// private
682
code net::start_hosts() NOEXCEPT
130 ✔
683
{
684
    return hosts_.start();
130 ✔
685
}
686

687
// private
688
code net::stop_hosts() NOEXCEPT
428 ✔
689
{
690
    return hosts_.stop();
428 ✔
691
}
692

693
void net::take(hosts::family family, address_item_handler&& handler) NOEXCEPT
3 ✔
694
{
695
    io::post(strand_,
3 ✔
696
        std::bind(&net::do_take, this, family, std::move(handler)));
6 ✔
697
}
3 ✔
698

699
void net::do_take(hosts::family family,
3 ✔
700
    const address_item_handler& handler) NOEXCEPT
701
{
702
    BC_ASSERT(stranded());
3 ✔
703
    hosts_.take(family, move_copy(handler));
3 ✔
704
}
3 ✔
705

706
void net::restore(const address_item_cptr& address,
17 ✔
707
    result_handler&& handler) NOEXCEPT
708
{
709
    io::post(strand_,
17 ✔
710
        std::bind(&net::do_restore, this, address, std::move(handler)));
34 ✔
711
}
17 ✔
712

713
void net::do_restore(const address_item_cptr& address,
17 ✔
714
    const result_handler& handler) NOEXCEPT
715
{
716
    BC_ASSERT(stranded());
17 ✔
717
    hosts_.restore(address, move_copy(handler));
17 ✔
718
}
17 ✔
719

720
void net::fetch(address_handler&& handler) NOEXCEPT
10 ✔
721
{
722
    io::post(strand_,
10 ✔
723
        std::bind(&net::do_fetch, this, std::move(handler)));
20 ✔
724
}
10 ✔
725

726
void net::do_fetch(const address_handler& handler) NOEXCEPT
10 ✔
727
{
728
    BC_ASSERT(stranded());
10 ✔
729

730
    // Accelerate stop, since hosts keeps running until all threads closed.
731
    if (closed())
10 ✔
732
    {
733
        handler(error::service_stopped, {});
1 ✔
734
        return;
1 ✔
735
    }
736

737
    hosts_.fetch(move_copy(handler));
9 ✔
738
}
739

740
void net::save(const address_cptr& message, count_handler&& handler) NOEXCEPT
26 ✔
741
{
742
    io::post(strand_,
26 ✔
743
        std::bind(&net::do_save, this, message, std::move(handler)));
52 ✔
744
}
26 ✔
745

746
void net::do_save(const address_cptr& message,
26 ✔
747
    const count_handler& handler) NOEXCEPT
748
{
749
    BC_ASSERT(stranded());
26 ✔
750

751
    // Accelerate stop, since hosts keeps running until all threads closed.
752
    if (closed())
26 ✔
753
    {
754
        handler(error::service_stopped, {});
1 ✔
755
        return;
1 ✔
756
    }
757

758
    hosts_.save(message, move_copy(handler));
25 ✔
759
}
760

761
void net::seed() NOEXCEPT
16 ✔
762
{
763
    io::post(strand_,
16 ✔
764
        std::bind(&net::do_seed, this));
16 ✔
765
}
16 ✔
766

767
void net::do_seed() NOEXCEPT
16 ✔
768
{
769
    BC_ASSERT(stranded());
16 ✔
770

771
    // address_not_found may only mean that no pooled address suits the slot.
772
    const auto& outbound = network_settings().outbound;
16 ✔
773
    if (closed() || outbound.seeds.empty() ||
28 ✔
774
        address_count() >= outbound.minimum_address_count())
12 ✔
775
        return;
9 ✔
776

777
    BC_ASSERT(seed_);
11 ✔
778
    const auto completed = seed_->completed();
11 ✔
779
    const auto backoff = outbound.seeding_timeout() * seed_backoff;
11 ✔
780

781
    if (seed_->seeding() || (completed != steady_clock::time_point{} &&
11 ✔
782
        (steady_clock::now() - completed) < backoff))
3 ✔
783
        return;
4 ✔
784

785
    seed_ = attach_seed_session();
7 ✔
786
    seed_->start([](const code&) NOEXCEPT {});
7 ✔
787
}
788

789
// P2P self address.
790
// ----------------------------------------------------------------------------
791

792
// P2P loopback detection.
793
// ----------------------------------------------------------------------------
794

795
bool net::store_nonce(const channel_peer& channel) NOEXCEPT
128 ✔
796
{
797
    BC_ASSERT(stranded());
128 ✔
798

799
    if (network_settings().inbound.enable_loopback || channel.inbound())
128 ✔
800
        return true;
85 ✔
801

802
    if (!nonces_.insert(channel.nonce()).second)
43 ✔
803
    {
804
        LOGF("Failed to store nonce for [" << channel.endpoint() << "].");
1 ✔
805
        return false;
1 ✔
806
    }
807

808
    return true;
809
}
810

811
bool net::unstore_nonce(const channel_peer& channel) NOEXCEPT
127 ✔
812
{
813
    BC_ASSERT(stranded());
127 ✔
814

815
    if (network_settings().inbound.enable_loopback || channel.inbound())
127 ✔
816
        return true;
85 ✔
817

818
    if (!to_bool(nonces_.erase(channel.nonce())))
42 ✔
819
    {
820
        LOGF("Failed to unstore nonce for [" << channel.endpoint() << "].");
1 ✔
821
        return false;
1 ✔
822
    }
823

824
    return true;
825
}
826

827
bool net::is_loopback(const channel_peer& channel) const NOEXCEPT
85 ✔
828
{
829
    BC_ASSERT(stranded());
85 ✔
830

831
    if (network_settings().inbound.enable_loopback || !channel.inbound())
85 ✔
832
        return false;
39 ✔
833

834
    return to_bool(nonces_.count(channel.peer_version()->nonce));
46 ✔
835
}
836

837
// P2P channel counting with address deconfliction.
838
// ----------------------------------------------------------------------------
839

840
code net::count_channel(const channel_peer& channel) NOEXCEPT
87 ✔
841
{
842
    BC_ASSERT(stranded());
87 ✔
843

844
    if (closed())
87 ✔
845
        return error::service_stopped;
2 ✔
846

847
    if (is_loopback(channel))
85 ✔
848
    {
849
        LOGS("Loopback detected from [" << channel.endpoint() << "].");
1 ✔
850
        return error::accept_failed;
1 ✔
851
    }
852

853
    if (channel.inbound() && is_zero(add1(inbound_channel_count_.load())))
129 ✔
854
    {
855
        LOGF("Overflow: inbound channel count.");
×
856
        return error::channel_overflow;
×
857
    }
858

859
    if (!channel.quiet() && is_zero(add1(total_channel_count_.load())))
152 ✔
860
    {
861
        LOGF("Overflow: total channel count.");
×
862
        return error::channel_overflow;
×
863
    }
864

865
    if (!hosts_.reserve(channel.endpoint()))
84 ✔
866
    {
867
        LOGS("Duplicate connection to [" << channel.endpoint() << "].");
1 ✔
868
        return error::address_in_use;
1 ✔
869
    }
870

871
    if (channel.inbound())
83 ✔
872
        ++inbound_channel_count_;
45 ✔
873

874
    if (!channel.quiet())
83 ✔
875
        ++total_channel_count_;
67 ✔
876

877
    actives_.push_back(channel.identifier());
83 ✔
878
    return error::success;
83 ✔
879
}
880

881
void net::uncount_channel(const channel_peer& channel) NOEXCEPT
83 ✔
882
{
883
    BC_ASSERT(stranded());
83 ✔
884

885
    hosts_.unreserve(channel.endpoint());
83 ✔
886
    peer_sent_ = ceilinged_add(peer_sent_.load(), channel.sent());
166 ✔
887
    peer_received_ = ceilinged_add(peer_received_.load(), channel.received());
166 ✔
888
    std::erase(actives_, channel.identifier());
83 ✔
889

890
    if (channel.inbound() && is_zero(inbound_channel_count_.load()))
128 ✔
891
    {
892
        LOGF("Underflow: inbound channel count.");
×
893
        return;
×
894
    }
895

896
    if (!channel.quiet() && is_zero(total_channel_count_.load()))
150 ✔
897
    {
898
        LOGF("Underflow: total channel count.");
×
899
        return;
×
900
    }
901

902
    if (channel.inbound())
83 ✔
903
        --inbound_channel_count_;
45 ✔
904

905
    if (!channel.quiet())
83 ✔
906
        --total_channel_count_;
67 ✔
907
}
908

909
// Specializations (protected).
910
// ----------------------------------------------------------------------------
911

912
session_seed::ptr net::attach_seed_session() NOEXCEPT
108 ✔
913
{
914
    BC_ASSERT(stranded());
108 ✔
915
    return attach<session_seed>(*this);
108 ✔
916
}
917

918
session_manual::ptr net::attach_manual_session() NOEXCEPT
130 ✔
919
{
920
    BC_ASSERT(stranded());
130 ✔
921
    return attach<session_manual>(*this);
130 ✔
922
}
923

924
session_inbound::ptr net::attach_inbound_session() NOEXCEPT
83 ✔
925
{
926
    BC_ASSERT(stranded());
83 ✔
927
    return attach<session_inbound>(*this);
83 ✔
928
}
929

930
session_outbound::ptr net::attach_outbound_session() NOEXCEPT
91 ✔
931
{
932
    BC_ASSERT(stranded());
91 ✔
933
    return attach<session_outbound>(*this);
91 ✔
934
}
935

936
BC_POP_WARNING()
937

938
} // namespace network
939
} // 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