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

libbitcoin / libbitcoin-server / 41719031671

03 Nov 2026 09:08PM UTC coverage: 94.853% (+0.3%) from 94.513%
41719031671

push

github

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

Assert the bip54 forks in the native header context.

12035 of 12688 relevant lines covered (94.85%)

238.96 hits per line

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

95.16
/src/protocols/native/protocol_native.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/server/protocols/protocol_native.hpp>
20

21
#include <atomic>
22
#include <optional>
23
#include <bitcoin/server/define.hpp>
24
#include <bitcoin/server/parsers/parsers.hpp>
25

26
namespace libbitcoin {
27
namespace server {
28

29
using namespace system;
30
using namespace network;
31
using namespace std::placeholders;
32
constexpr auto relaxed = std::memory_order_relaxed;
33

34
#define CLASS protocol_native
35
#define SUBSCRIBE_NATIVE(method, ...) \
36
    subscribe<CLASS>(&CLASS::method, __VA_ARGS__)
37

38
// Start.
39
// ----------------------------------------------------------------------------
40

41
void protocol_native::start() NOEXCEPT
188 ✔
42
{
43
    BC_ASSERT(stranded());
188 ✔
44

45
    if (started())
188 ✔
46
        return;
47

48
    // Chaser subscription is asynchronous, events may be missed.
49
    subscribe_chase(BIND(handle_chase, _1, _2));
188 ✔
50

51
    // Configuration methods.
52
    SUBSCRIBE_NATIVE(handle_get_configuration, _1, _2, _3, _4);
188 ✔
53

54
    // Top methods.
55
    SUBSCRIBE_NATIVE(handle_get_top, _1, _2, _3, _4);
188 ✔
56
    SUBSCRIBE_NATIVE(handle_get_top_subscribe, _1, _2, _3, _4, _5);
188 ✔
57

58
    // Block methods.
59
    SUBSCRIBE_NATIVE(handle_get_block, _1, _2, _3, _4, _5, _6, _7);
188 ✔
60
    SUBSCRIBE_NATIVE(handle_get_block_header, _1, _2, _3, _4, _5, _6);
188 ✔
61
    SUBSCRIBE_NATIVE(handle_get_block_header_context, _1, _2, _3, _4, _5, _6);
188 ✔
62
    SUBSCRIBE_NATIVE(handle_get_block_details, _1, _2, _3, _4, _5, _6);
188 ✔
63
    SUBSCRIBE_NATIVE(handle_get_block_txs, _1, _2, _3, _4, _5, _6);
188 ✔
64
    SUBSCRIBE_NATIVE(handle_get_block_filter, _1, _2, _3, _4, _5, _6, _7);
188 ✔
65
    SUBSCRIBE_NATIVE(handle_get_block_filter_hash, _1, _2, _3, _4, _5, _6, _7);
188 ✔
66
    SUBSCRIBE_NATIVE(handle_get_block_filter_header, _1, _2, _3, _4, _5, _6, _7);
188 ✔
67
    SUBSCRIBE_NATIVE(handle_get_block_tx, _1, _2, _3, _4, _5, _6, _7, _8);
188 ✔
68
    SUBSCRIBE_NATIVE(handle_get_block_subscribe, _1, _2, _3, _4, _5);
188 ✔
69

70
    // Transaction methods.
71
    SUBSCRIBE_NATIVE(handle_get_tx, _1, _2, _3, _4, _5, _6);
188 ✔
72
    SUBSCRIBE_NATIVE(handle_get_tx_header, _1, _2, _3, _4, _5);
188 ✔
73
    SUBSCRIBE_NATIVE(handle_get_tx_details, _1, _2, _3, _4, _5);
188 ✔
74
    SUBSCRIBE_NATIVE(handle_get_tx_subscribe, _1, _2, _3, _4, _5);
188 ✔
75

76
    // Input methods.
77
    SUBSCRIBE_NATIVE(handle_get_inputs, _1, _2, _3, _4, _5, _6);
188 ✔
78
    SUBSCRIBE_NATIVE(handle_get_input, _1, _2, _3, _4, _5, _6, _7);
188 ✔
79
    SUBSCRIBE_NATIVE(handle_get_input_script, _1, _2, _3, _4, _5, _6);
188 ✔
80
    SUBSCRIBE_NATIVE(handle_get_input_witness, _1, _2, _3, _4, _5, _6);
188 ✔
81

82
    // Output methods.
83
    SUBSCRIBE_NATIVE(handle_get_outputs, _1, _2, _3, _4, _5);
188 ✔
84
    SUBSCRIBE_NATIVE(handle_get_output, _1, _2, _3, _4, _5, _6);
188 ✔
85
    SUBSCRIBE_NATIVE(handle_get_output_script, _1, _2, _3, _4, _5, _6);
188 ✔
86
    SUBSCRIBE_NATIVE(handle_get_output_spender, _1, _2, _3, _4, _5, _6);
188 ✔
87
    SUBSCRIBE_NATIVE(handle_get_output_spenders, _1, _2, _3, _4, _5, _6);
188 ✔
88
    SUBSCRIBE_NATIVE(handle_get_output_subscribe, _1, _2, _3, _4, _5, _6, _7);
188 ✔
89

90
    // Address methods.
91
    SUBSCRIBE_NATIVE(handle_get_address, _1, _2, _3, _4, _5, _6);
188 ✔
92
    SUBSCRIBE_NATIVE(handle_get_address_confirmed, _1, _2, _3, _4, _5, _6);
188 ✔
93
    SUBSCRIBE_NATIVE(handle_get_address_unconfirmed, _1, _2, _3, _4, _5, _6);
188 ✔
94
    SUBSCRIBE_NATIVE(handle_get_address_balance, _1, _2, _3, _4, _5, _6);
188 ✔
95
    SUBSCRIBE_NATIVE(handle_get_address_subscribe, _1, _2, _3, _4, _5, _6, _7);
188 ✔
96

97
    // Fee methods.
98
    SUBSCRIBE_NATIVE(handle_get_fees, _1, _2, _3, _4);
188 ✔
99
    protocol_html::start();
188 ✔
100
}
101

102
void protocol_native::stopping(const code& ec) NOEXCEPT
188 ✔
103
{
104
    BC_ASSERT(stranded());
188 ✔
105
    stopping_.store(true);
188 ✔
106
    dispatcher_.stop(ec);
188 ✔
107
    unsubscribe_chase();
188 ✔
108
    protocol_html::stopping(ec);
188 ✔
109
}
188 ✔
110

111
// Dispatch.
112
// ----------------------------------------------------------------------------
113

114
bool protocol_native::try_dispatch_object(const http::request& request) NOEXCEPT
162 ✔
115
{
116
    BC_ASSERT(stranded());
162 ✔
117

118
    BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
119
    const auto target = request.target();
162 ✔
120
    BC_POP_WARNING()
121

122
    rpc::request_t model{};
162 ✔
123
    if (const auto ec = native_target(model, target))
162 ✔
124
    {
125
        // Allow invalid interface target to be retried as a page request.
126
        return !ec;
13 ✔
127
    }
128

129
    // No media defaults injected for an http request.
130
    if (!native_query(model, request))
149 ✔
131
    {
132
        send_bad_request(request);
4 ✔
133
        return true;
134
    }
135

136
    const auto media = get_media(model);
145 ✔
137
    if (media == media_type::unknown)
145 ✔
138
    {
139
        send_not_acceptable(request);
1 ✔
140
        return true;
141
    }
142

143
    // Falls through to html page dispatch.
144
    if (media == media_type::text_html)
144 ✔
145
        return false;
146

147
    // The credential may be restricted to a subset of interface methods.
148
    if (!permitted(model.method))
143 ✔
149
    {
150
        send_forbidden(request);
1 ✔
151
        return true;
152
    }
153

154
    if (dispatcher_.notify(model))
142 ✔
155
        send_bad_request(request);
1 ✔
156

157
    return true;
158
}
162 ✔
159

160
void protocol_native::dispatch_websocket(const http::request& request) NOEXCEPT
32 ✔
161
{
162
    BC_ASSERT(stranded());
32 ✔
163

164
    // ws frames carry no headers, so ws authorization is enforced here, not
165
    // by the channel (established by basic auth on the upgrade request).
166
    if (!authorized())
32 ✔
167
    {
168
        stop(network::error::unauthorized);
×
169
        return;
3 ✔
170
    }
171

172
    // Native websocket interface supports only string requests.
173
    if (!request.body().contains<http::string_value>())
32 ✔
174
    {
175
        stop(network::error::not_acceptable);
×
176
        return;
×
177
    }
178

179
    // Target with query string is passed via websocket string body.
180
    const auto target = request.body().get<http::string_value>();
32 ✔
181

182
    rpc::request_t model{};
32 ✔
183
    if (const auto ec = native_target(model, target))
32 ✔
184
    {
185
        stop(network::error::bad_request);
1 ✔
186
        return;
1 ✔
187
    }
188

189
    // Default to json by simulating a json accept header (format overrides).
190
    if (!native_query(model, target, { media_type::application_json }))
31 ✔
191
    {
192
        stop(network::error::bad_request);
1 ✔
193
        return;
1 ✔
194
    }
195

196
    if (get_media(model) == media_type::text_html)
30 ✔
197
    {
198
        stop(network::error::not_acceptable);
1 ✔
199
        return;
1 ✔
200
    }
201

202
    // The credential may be restricted to a subset of interface methods.
203
    if (!permitted(model.method))
29 ✔
204
    {
205
        stop(network::error::unauthorized);
×
206
        return;
×
207
    }
208

209
    if (dispatcher_.notify(model))
29 ✔
210
        stop(network::error::bad_request);
1 ✔
211
}
32 ✔
212

213
// Senders.
214
// ----------------------------------------------------------------------------
215

216
void protocol_native::send_not_found(const http::request& request) NOEXCEPT
30 ✔
217
{
218
    BC_ASSERT(stranded());
30 ✔
219

220
    if (websocket())
30 ✔
221
    {
222
        send_empty(request);
2 ✔
223
        return;
2 ✔
224
    }
225

226
    protocol_html::send_not_found(request);
28 ✔
227
}
228

229
// Event handlers.
230
// ----------------------------------------------------------------------------
231

232
bool protocol_native::handle_chase(const code&,
198 ✔
233
    node::event_value value) NOEXCEPT
234
{
235
    // Do not pass ec to stopped as it is not a call status.
236
    if (stopped())
198 ✔
237
        return false;
238

239
    if (!websocket())
83 ✔
240
        return true;
241

242
    switch (node::to_chase(value))
13 ✔
243
    {
244
        case node::chase::block:
6 ✔
245
        {
246
            const auto block = node::to_payload<node::chase::block>(value);
6 ✔
247

248
            // Increments height above a fork point (start/reorg).
249
            auto media = top_subscribe_.load(relaxed);
6 ✔
250
            if (media != media_type::unknown)
6 ✔
251
            {
252
                POST(do_top, block.link, media);
3 ✔
253
            }
254

255
            // No block emission for a fork point (start/reorg).
256
            media = block_subscribe_.load(relaxed);
6 ✔
257
            if (media != media_type::unknown)
6 ✔
258
            {
259
                POST(do_block, block.link, media);
3 ✔
260
            }
261

262
            break;
263
        }
264
        case node::chase::reorganized:
1 ✔
265
        {
266
            // Resets subscriber height to the fork point.
267
            const auto media = top_subscribe_.load(relaxed);
1 ✔
268
            if (media != media_type::unknown)
1 ✔
269
            {
270
                POST(do_top,
1 ✔
271
                    node::to_payload<node::chase::reorganized>(value).link,
272
                    media);
273
            }
274

275
            break;
276
        }
277
        case node::chase::transaction:
3 ✔
278
        {
279
            const auto media = tx_subscribe_.load(relaxed);
3 ✔
280
            if (media != media_type::unknown)
3 ✔
281
            {
282
                POST(do_transaction,
3 ✔
283
                    node::to_payload<node::chase::transaction>(value).link,
284
                    media);
285
            }
286

287
            break;
288
        }
289
        default:
290
        {
291
            break;
292
        }
293
    }
294

295
    return true;
296
}
297

298
// Utilities.
299
// ----------------------------------------------------------------------------
300
// private
301

302
using namespace system::json;
303

304
BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT)
305

306
void protocol_native::inject(value& out, std::optional<uint32_t> height,
5 ✔
307
    const database::header_link& link) const NOEXCEPT
308
{
309
    out.as_object().emplace("height", height.has_value() ? height.value() :
7 ✔
310
        archive().get_height(link).value);
7 ✔
311
}
5 ✔
312

313
BC_POP_WARNING()
314

315
database::header_link protocol_native::to_header(
47 ✔
316
    const std::optional<uint32_t>& height,
317
    const std::optional<hash_cptr>& hash) NOEXCEPT
318
{
319
    const auto& query = archive();
47 ✔
320

321
    if (hash.has_value())
47 ✔
322
        return query.to_header(*(hash.value()));
6 ✔
323

324
    if (height.has_value())
41 ✔
325
        return query.to_confirmed(height.value());
41 ✔
326

327
    return {};
×
328
}
329

330
// Use if deserialization is required.
331
#if defined(UNDEFINED)
332
using inpoints = database::inpoints;
333
using outpoints = database::outpoints;
334

335
DECLARE_JSON_TAG_INVOKE(inpoints);
336
DECLARE_JSON_TAG_INVOKE(outpoints);
337

338
DEFINE_JSON_TO_TAG(inpoints)
339
{
340
    inpoints out{};
341
    for (const auto& point: value.as_array())
342
        out.insert(value_to<inpoint>(point));
343

344
    return out;
345
}
346

347
DEFINE_JSON_TO_TAG(outpoints)
348
{
349
    outpoints out{};
350
    for (const auto& point: value.as_array())
351
        out.insert(value_to<outpoint>(point));
352

353
    return out;
354
}
355
#endif
356

357
} // namespace server
358
} // 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