Skip to content

Commit 47b28d6

Browse files
authored
Merge pull request #722 from evoskuil/master
Optimized fee and related queries.
2 parents 53ad184 + cfe75b8 commit 47b28d6

10 files changed

Lines changed: 471 additions & 132 deletions

File tree

include/bitcoin/database/impl/query/archive_read.ipp

Lines changed: 251 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@
1919
#ifndef LIBBITCOIN_DATABASE_QUERY_ARCHIVE_READ_IPP
2020
#define LIBBITCOIN_DATABASE_QUERY_ARCHIVE_READ_IPP
2121

22-
#include <algorithm>
23-
#include <ranges>
22+
#include <atomic>
23+
#include <numeric>
2424
#include <utility>
2525
#include <bitcoin/database/define.hpp>
2626
#include <bitcoin/database/tables/tables.hpp>
@@ -149,7 +149,7 @@ inline hash_digest CLASS::get_point_hash(const point_link& link) const NOEXCEPT
149149
return point.hash;
150150
}
151151

152-
// False implies not confirmed.
152+
// Position.
153153
// ----------------------------------------------------------------------------
154154

155155
TEMPLATE
@@ -175,13 +175,31 @@ bool CLASS::get_tx_position(size_t& out, const tx_link& link) const NOEXCEPT
175175
return true;
176176
}
177177

178+
// Sizes.
179+
// ----------------------------------------------------------------------------
180+
181+
TEMPLATE
182+
bool CLASS::get_tx_size(size_t& out, const tx_link& link,
183+
bool witness) const NOEXCEPT
184+
{
185+
size_t light{}, heavy{};
186+
if (!get_tx_sizes(light, heavy, link))
187+
return false;
188+
189+
out = witness ? heavy : light;
190+
return true;
191+
}
192+
178193
TEMPLATE
179-
size_t CLASS::get_tx_size(const tx_link& link,
194+
bool CLASS::get_block_size(size_t& out, const header_link& link,
180195
bool witness) const NOEXCEPT
181196
{
182197
size_t light{}, heavy{};
183-
return get_tx_sizes(light, heavy, link) ? (witness ? heavy : light) :
184-
max_uint64;
198+
if (!get_block_sizes(light, heavy, link))
199+
return false;
200+
201+
out = witness ? heavy : light;
202+
return true;
185203
}
186204

187205
TEMPLATE
@@ -197,18 +215,6 @@ bool CLASS::get_tx_sizes(size_t& light, size_t& heavy,
197215
return true;
198216
}
199217

200-
// Terminal implies not found, false implies fault.
201-
// ----------------------------------------------------------------------------
202-
203-
TEMPLATE
204-
size_t CLASS::get_block_size(const header_link& link,
205-
bool witness) const NOEXCEPT
206-
{
207-
size_t light{}, heavy{};
208-
return get_block_sizes(light, heavy, link) ? (witness ? heavy : light) :
209-
max_uint64;
210-
}
211-
212218
TEMPLATE
213219
bool CLASS::get_block_sizes(size_t& light, size_t& heavy,
214220
const header_link& link) const NOEXCEPT
@@ -222,6 +228,9 @@ bool CLASS::get_block_sizes(size_t& light, size_t& heavy,
222228
return true;
223229
}
224230

231+
// Heights.
232+
// ----------------------------------------------------------------------------
233+
225234
TEMPLATE
226235
height_link CLASS::get_height(const hash_digest& key) const NOEXCEPT
227236
{
@@ -265,6 +274,9 @@ bool CLASS::get_height(size_t& out, const header_link& link) const NOEXCEPT
265274
return true;
266275
}
267276

277+
// Values (value, spend, fees).
278+
// ----------------------------------------------------------------------------
279+
268280
TEMPLATE
269281
bool CLASS::get_value(uint64_t& out, const output_link& link) const NOEXCEPT
270282
{
@@ -276,31 +288,237 @@ bool CLASS::get_value(uint64_t& out, const output_link& link) const NOEXCEPT
276288
return true;
277289
}
278290

291+
// protected
279292
TEMPLATE
280-
bool CLASS::get_unassociated(association& out,
281-
const header_link& link) const NOEXCEPT
293+
bool CLASS::get_outputs_total_value(uint64_t& out,
294+
const output_links& links) const NOEXCEPT
282295
{
283-
if (is_associated(link))
296+
out = zero;
297+
for (const auto& output_fk: links)
298+
{
299+
uint64_t value{};
300+
if (!get_value(value, output_fk)) return false;
301+
out = system::ceilinged_add(out, value);
302+
}
303+
304+
return true;
305+
}
306+
307+
TEMPLATE
308+
bool CLASS::get_tx_value(uint64_t& out, const tx_link& link) const NOEXCEPT
309+
{
310+
table::transaction::get_coinbase tx{};
311+
if (!store_.tx.get(link, tx))
312+
return false;
313+
314+
// Shortcircuit coinbase prevout read.
315+
if (tx.coinbase)
316+
{
317+
out = zero;
318+
return true;
319+
}
320+
321+
// Optimizable due to sequential tx input links.
322+
const auto links = to_prevouts(link);
323+
return !links.empty() && get_outputs_total_value(out, links);
324+
}
325+
326+
TEMPLATE
327+
bool CLASS::get_tx_spend(uint64_t& out, const tx_link& link) const NOEXCEPT
328+
{
329+
const auto links = to_outputs(link);
330+
return !links.empty() && get_outputs_total_value(out, links);
331+
}
332+
333+
TEMPLATE
334+
bool CLASS::get_tx_fee(uint64_t& out, const tx_link& link) const NOEXCEPT
335+
{
336+
#if defined(SLOW_FEES)
337+
const auto tx = get_transaction(link, false);
338+
if (!tx)
284339
return false;
285340

286-
table::header::get_check_context context{};
287-
if (!store_.header.get(link, context))
341+
// Prevent coinbase populate failure.
342+
if (tx->is_coinbase())
343+
{
344+
out = zero;
345+
return true;
346+
}
347+
348+
if (!populate_without_metadata(*tx))
349+
return false;
350+
351+
out = tx->fee();
352+
return true;
353+
#elif defined(FAST_FEES)
354+
table::transaction::get_coinbase tx{};
355+
if (!store_.tx.get(link, tx))
288356
return false;
289357

290-
out =
358+
// Prevent coinbase overspend failure.
359+
if (tx.coinbase)
291360
{
292-
link,
293-
context.key,
294-
system::chain::context
361+
out = zero;
362+
return true;
363+
}
364+
365+
uint64_t value{}, spend{};
366+
if (!get_tx_value(value, link) || !get_tx_spend(spend, link) ||
367+
spend > value)
368+
return false;
369+
370+
out = value - spend;
371+
return true;
372+
#else // FASTER_FEES
373+
table::transaction::get_puts tx{};
374+
if (!store_.tx.get(link, tx))
375+
return false;
376+
377+
// Shortcircuit coinbase prevout read.
378+
if (tx.coinbase)
379+
{
380+
out = zero;
381+
return true;
382+
}
383+
384+
uint64_t value{};
385+
auto point_fk = tx.points_fk;
386+
for (size_t index{}; index < tx.ins_count; ++index)
387+
{
388+
table::point::get_composed point{};
389+
if (!store_.point.get(point_fk++, point))
390+
return false;
391+
392+
uint64_t one_value{};
393+
if (!get_value(one_value, to_output(point.key))) return false;
394+
value = system::ceilinged_add(value, one_value);
395+
}
396+
397+
table::outs::record outs{};
398+
outs.out_fks.resize(tx.outs_count);
399+
if (!store_.outs.get(tx.outs_fk, outs))
400+
return false;
401+
402+
uint64_t spend{};
403+
for (const auto& output_fk: outs.out_fks)
404+
{
405+
uint64_t one_spend{};
406+
if (!get_value(one_spend, output_fk)) return false;
407+
spend = system::ceilinged_add(spend, one_spend);
408+
}
409+
410+
if (spend > value)
411+
return false;
412+
413+
out = value - spend;
414+
return true;
415+
#endif // SLOW_FEES
416+
}
417+
418+
TEMPLATE
419+
bool CLASS::get_block_value(uint64_t& out,
420+
const header_link& link) const NOEXCEPT
421+
{
422+
table::txs::get_txs txs{};
423+
if (!store_.txs.at(to_txs(link), txs) || (txs.tx_fks.size() < one))
424+
return false;
425+
426+
std::atomic_bool fail{};
427+
const auto begin = std::next(txs.tx_fks.begin());
428+
constexpr auto parallel = poolstl::execution::par;
429+
constexpr auto relaxed = std::memory_order_relaxed;
430+
431+
out = std::transform_reduce(parallel, begin, txs.tx_fks.end(), 0_u64,
432+
[](uint64_t left, uint64_t right) NOEXCEPT
433+
{
434+
return system::ceilinged_add(left, right);
435+
},
436+
[&](const auto& tx_fk) NOEXCEPT
437+
{
438+
uint64_t value{};
439+
if (!fail.load(relaxed) && !get_tx_value(value, tx_fk))
440+
fail.store(true, relaxed);
441+
442+
return value;
443+
});
444+
445+
return !fail.load(relaxed);
446+
}
447+
448+
TEMPLATE
449+
bool CLASS::get_block_spend(uint64_t& out,
450+
const header_link& link) const NOEXCEPT
451+
{
452+
table::txs::get_txs txs{};
453+
if (!store_.txs.at(to_txs(link), txs) || (txs.tx_fks.size() < one))
454+
return false;
455+
456+
std::atomic_bool fail{};
457+
const auto begin = std::next(txs.tx_fks.begin());
458+
constexpr auto parallel = poolstl::execution::par;
459+
constexpr auto relaxed = std::memory_order_relaxed;
460+
461+
out = std::transform_reduce(parallel, begin, txs.tx_fks.end(), 0_u64,
462+
[](uint64_t left, uint64_t right) NOEXCEPT
463+
{
464+
return system::ceilinged_add(left, right);
465+
},
466+
[&](const auto& tx_fk) NOEXCEPT
295467
{
296-
context.ctx.flags,
297-
context.timestamp,
298-
context.ctx.mtp,
299-
system::possible_wide_cast<size_t>(context.ctx.height)
300-
}
301-
};
468+
uint64_t spend{};
469+
if (!fail.load(relaxed) && !get_tx_spend(spend, tx_fk))
470+
fail.store(true, relaxed);
471+
472+
return spend;
473+
});
474+
475+
return !fail.load(relaxed);
476+
}
302477

478+
TEMPLATE
479+
bool CLASS::get_block_fee(uint64_t& out, const header_link& link) const NOEXCEPT
480+
{
481+
#if defined(SLOW_FEES)
482+
const auto block = get_block(link, false);
483+
if (!block || !populate_without_metadata(*block))
484+
return false;
485+
486+
out = block->fees();
303487
return true;
488+
#elif defined(FAST_FEES)
489+
uint64_t value{}, spend{};
490+
if (!get_block_value(value, link) || !get_block_spend(spend, link) ||
491+
spend > value)
492+
return false;
493+
494+
out = value - spend;
495+
return true;
496+
#else // FASTER_FEES
497+
table::txs::get_txs txs{};
498+
if (!store_.txs.at(to_txs(link), txs) || (txs.tx_fks.size() < one))
499+
return false;
500+
501+
std::atomic_bool fail{};
502+
const auto begin = std::next(txs.tx_fks.begin());
503+
constexpr auto parallel = poolstl::execution::par;
504+
constexpr auto relaxed = std::memory_order_relaxed;
505+
506+
out = std::transform_reduce(parallel, begin, txs.tx_fks.end(), 0_u64,
507+
[](uint64_t left, uint64_t right) NOEXCEPT
508+
{
509+
return system::ceilinged_add(left, right);
510+
},
511+
[&](const auto& tx_fk) NOEXCEPT
512+
{
513+
uint64_t fee{};
514+
if (!fail.load(relaxed) && !get_tx_fee(fee, tx_fk))
515+
fail.store(true, relaxed);
516+
517+
return fee;
518+
});
519+
520+
return !fail.load(relaxed);
521+
#endif // SLOW_FEES
304522
}
305523

306524
} // namespace database

0 commit comments

Comments
 (0)