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
155155TEMPLATE
@@ -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+
178193TEMPLATE
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
187205TEMPLATE
@@ -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-
212218TEMPLATE
213219bool 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+
225234TEMPLATE
226235height_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+
268280TEMPLATE
269281bool 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
279292TEMPLATE
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