diff --git a/builds/gnu/Makefile.am b/builds/gnu/Makefile.am
index 2bb03c06b..3a3819cae 100644
--- a/builds/gnu/Makefile.am
+++ b/builds/gnu/Makefile.am
@@ -324,6 +324,7 @@ include_bitcoin_database_typesdir = \
include_bitcoin_database_types_HEADERS = \
${srcdir}/../../include/bitcoin/database/types/association.hpp \
${srcdir}/../../include/bitcoin/database/types/associations.hpp \
+ ${srcdir}/../../include/bitcoin/database/types/block_amounts.hpp \
${srcdir}/../../include/bitcoin/database/types/block_state.hpp \
${srcdir}/../../include/bitcoin/database/types/difference_set.hpp \
${srcdir}/../../include/bitcoin/database/types/envelope.hpp \
diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
index 4c22f9137..570e9b435 100644
--- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj
@@ -211,6 +211,7 @@
+
diff --git a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
index 543fa3a6f..c8b684323 100644
--- a/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
+++ b/builds/msvc/vs2022/libbitcoin-database/libbitcoin-database.vcxproj.filters
@@ -353,6 +353,9 @@
include\bitcoin\database\types
+
+ include\bitcoin\database\types
+
include\bitcoin\database\types
diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj
index 5b5917e91..c2f545055 100644
--- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj
+++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj
@@ -211,6 +211,7 @@
+
diff --git a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters
index 543fa3a6f..c8b684323 100644
--- a/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters
+++ b/builds/msvc/vs2026/libbitcoin-database/libbitcoin-database.vcxproj.filters
@@ -353,6 +353,9 @@
include\bitcoin\database\types
+
+ include\bitcoin\database\types
+
include\bitcoin\database\types
diff --git a/include/bitcoin/database.hpp b/include/bitcoin/database.hpp
index a21a08de0..94a9bfef0 100644
--- a/include/bitcoin/database.hpp
+++ b/include/bitcoin/database.hpp
@@ -84,6 +84,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/include/bitcoin/database/impl/query/unspent.ipp b/include/bitcoin/database/impl/query/unspent.ipp
index 89465a23e..d482ba0de 100644
--- a/include/bitcoin/database/impl/query/unspent.ipp
+++ b/include/bitcoin/database/impl/query/unspent.ipp
@@ -70,11 +70,58 @@ bool CLASS::is_bip30_exception(bool& out,
}
TEMPLATE
-code CLASS::scan_unspent(difference_set<>& set, const stopper& cancel,
- const header_links& branch, bool turbo) const NOEXCEPT
+code CLASS::get_block_amounts(block_amounts& out,
+ const header_link& link) const NOEXCEPT
+{
+ out = {};
+ auto bip30_exception = false;
+ if (!is_bip30_exception(bip30_exception, link))
+ return error::integrity;
+
+ auto coinbase = true;
+ table::output::get_spendable value{};
+ for (const auto& tx: to_transactions(link))
+ {
+ for (const auto& put: to_outputs(tx))
+ {
+ if (!store_.output.get(put, value))
+ return error::integrity;
+
+ if (value.unspendable)
+ out.unspendable += value.value;
+
+ if (coinbase)
+ out.coinbase += value.value;
+ else
+ out.outputs += value.value;
+ }
+
+ coinbase = false;
+ }
+
+ // The duplicated coinbase overwrites (destroys) that of the original.
+ if (bip30_exception)
+ out.bip30 = out.coinbase;
+
+ for (const auto& put: to_block_prevouts(link))
+ {
+ if (!store_.output.get(put, value))
+ return error::integrity;
+
+ out.prevouts += value.value;
+ }
+
+ return error::success;
+}
+
+TEMPLATE
+code CLASS::scan_unspent(difference_set<>& set, unspent_totals& out,
+ const stopper& cancel, const header_links& branch, bool turbo) const NOEXCEPT
{
using namespace system;
std::atomic_bool fail{};
+ std::atomic unspendable{};
+ std::atomic overwritten{};
const auto parallel = poolstl::execution::par_if(turbo);
// Reverse order in best effort attempt to minimize set size.
@@ -92,12 +139,27 @@ code CLASS::scan_unspent(difference_set<>& set, const stopper& cancel,
return;
}
- // The bip30 exception blocks are coinbase only.
- if (bip30_exception)
- return;
-
auto coinbase = true;
table::output::get_spendable value{};
+
+ // The bip30 exception blocks are coinbase only, their duplicated
+ // outputs overwrite (destroy) those of the original blocks.
+ if (bip30_exception)
+ {
+ for (const auto& tx: to_transactions(link))
+ for (const auto& out_: to_outputs(tx))
+ {
+ if (!store_.output.get(out_, value))
+ {
+ fail = true;
+ return;
+ }
+
+ overwritten += value.value;
+ }
+
+ return;
+ }
for (const auto& tx: to_transactions(link))
{
if (cancel || fail)
@@ -112,7 +174,9 @@ code CLASS::scan_unspent(difference_set<>& set, const stopper& cancel,
return;
}
- if (!value.unspendable)
+ if (value.unspendable)
+ unspendable += value.value;
+ else
set.toggle(tx, index);
++index;
@@ -165,6 +229,8 @@ code CLASS::scan_unspent(difference_set<>& set, const stopper& cancel,
if (cancel)
return error::query_canceled;
+ out.unspendable = unspendable;
+ out.bip30 = overwritten;
return error::success;
}
@@ -174,7 +240,6 @@ code CLASS::count_unspent(unspent_totals& out, const stopper& cancel,
{
using namespace system;
- out = {};
output_links puts{};
auto previous = max_uint64;
table::output::get_spendable value{};
@@ -280,20 +345,23 @@ TEMPLATE
code CLASS::get_unspent_totals(const stopper& cancel, unspent_totals& out,
const header_links& branch, bool turbo) const NOEXCEPT
{
+ out = {};
difference_set<> set{};
- const auto ec = scan_unspent(set, cancel, branch, turbo);
+ const auto ec = scan_unspent(set, out, cancel, branch, turbo);
return ec ? ec : count_unspent(out, cancel, set.drain());
}
TEMPLATE
template
-code CLASS::get_unspent_coins(const stopper& cancel, const Visitor& visit,
- const header_links& branch, bool ordered, bool turbo) const NOEXCEPT
+code CLASS::get_unspent_coins(const stopper& cancel, unspent_totals& out,
+ const Visitor& visit, const header_links& branch, bool ordered,
+ bool turbo) const NOEXCEPT
{
using namespace system;
+ out = {};
difference_set<> set{};
- if (const auto ec = scan_unspent(set, cancel, branch, turbo))
+ if (const auto ec = scan_unspent(set, out, cancel, branch, turbo))
return ec;
auto survivors = set.drain();
diff --git a/include/bitcoin/database/query.hpp b/include/bitcoin/database/query.hpp
index 4d4d78d82..137b25599 100644
--- a/include/bitcoin/database/query.hpp
+++ b/include/bitcoin/database/query.hpp
@@ -775,6 +775,10 @@ class query
/// Unspent.
/// -----------------------------------------------------------------------
+ /// Coin amounts moved by the block.
+ code get_block_amounts(block_amounts& out,
+ const header_link& link) const NOEXCEPT;
+
/// Unspent output totals over the branch (administrative scan).
code get_unspent_totals(const stopper& cancel, unspent_totals& out,
const header_links& branch, bool turbo=false) const NOEXCEPT;
@@ -783,8 +787,8 @@ class query
/// canonical (txid, index) order as required for utxo set serialization,
/// otherwise unordered (ordering requires a full materialized sort).
template
- code get_unspent_coins(const stopper& cancel, const Visitor& visit,
- const header_links& branch, bool ordered,
+ code get_unspent_coins(const stopper& cancel, unspent_totals& out,
+ const Visitor& visit, const header_links& branch, bool ordered,
bool turbo=false) const NOEXCEPT;
protected:
@@ -989,8 +993,9 @@ class query
bool is_bip30_exception(bool& out,
const header_link& link) const NOEXCEPT;
- code scan_unspent(difference_set<>& set, const stopper& cancel,
- const header_links& branch, bool turbo) const NOEXCEPT;
+ code scan_unspent(difference_set<>& set, unspent_totals& out,
+ const stopper& cancel, const header_links& branch,
+ bool turbo) const NOEXCEPT;
code count_unspent(unspent_totals& out, const stopper& cancel,
const difference_set<>::entries& survivors) const NOEXCEPT;
template
diff --git a/include/bitcoin/database/types/block_amounts.hpp b/include/bitcoin/database/types/block_amounts.hpp
new file mode 100644
index 000000000..c4863a218
--- /dev/null
+++ b/include/bitcoin/database/types/block_amounts.hpp
@@ -0,0 +1,49 @@
+/**
+ * Copyright (c) 2011-2026 libbitcoin developers
+ *
+ * This file is part of libbitcoin.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see .
+ */
+#ifndef LIBBITCOIN_DATABASE_TYPES_BLOCK_AMOUNTS_HPP
+#define LIBBITCOIN_DATABASE_TYPES_BLOCK_AMOUNTS_HPP
+
+#include
+
+namespace libbitcoin {
+namespace database {
+
+/// Coin amounts moved by a single block.
+struct BCD_API block_amounts
+{
+ /// Total value of the prevouts spent by the block.
+ uint64_t prevouts{};
+
+ /// Total value of the coinbase outputs.
+ uint64_t coinbase{};
+
+ /// Total value of the non-coinbase outputs.
+ uint64_t outputs{};
+
+ /// Total value of the provably unspendable outputs created.
+ uint64_t unspendable{};
+
+ /// Total value of the coinbase outputs overwritten by bip30 duplication.
+ uint64_t bip30{};
+};
+
+} // namespace database
+} // namespace libbitcoin
+
+#endif
diff --git a/include/bitcoin/database/types/types.hpp b/include/bitcoin/database/types/types.hpp
index 815321a90..1cd664bad 100644
--- a/include/bitcoin/database/types/types.hpp
+++ b/include/bitcoin/database/types/types.hpp
@@ -33,6 +33,7 @@
#include
#include
#include
+#include
#include
#include
#include
diff --git a/include/bitcoin/database/types/unspent_totals.hpp b/include/bitcoin/database/types/unspent_totals.hpp
index a5e23e7b6..e2b550bec 100644
--- a/include/bitcoin/database/types/unspent_totals.hpp
+++ b/include/bitcoin/database/types/unspent_totals.hpp
@@ -41,6 +41,12 @@ struct BCD_API unspent_totals
/// Total value of unspent outputs.
uint64_t value{};
+
+ /// Total value of provably unspendable outputs (excluded from the set).
+ uint64_t unspendable{};
+
+ /// Total value of coinbase outputs overwritten by bip30 duplication.
+ uint64_t bip30{};
};
} // namespace database