Skip to content

Commit 9439f62

Browse files
Sjorsfurszy
andcommitted
Add the silent payment index
Co-Authored-By: w0xlt <[email protected]> Co-Authored-By: furszy <[email protected]>
1 parent d99f2c4 commit 9439f62

File tree

7 files changed

+201
-0
lines changed

7 files changed

+201
-0
lines changed

src/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,7 @@ add_library(bitcoin_node STATIC EXCLUDE_FROM_ALL
237237
httpserver.cpp
238238
i2p.cpp
239239
index/base.cpp
240+
index/bip352.cpp
240241
index/blockfilterindex.cpp
241242
index/coinstatsindex.cpp
242243
index/txindex.cpp
@@ -325,6 +326,7 @@ target_link_libraries(bitcoin_node
325326
$<TARGET_NAME_IF_EXISTS:bitcoin_zmq>
326327
leveldb
327328
minisketch
329+
secp256k1
328330
univalue
329331
Boost::headers
330332
$<TARGET_NAME_IF_EXISTS:libevent::core>

src/index/bip352.cpp

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
// Copyright (c) 2023-present The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#include <index/bip352.h>
6+
7+
#include <common/bip352.h>
8+
#include <chainparams.h>
9+
#include <coins.h>
10+
#include <common/args.h>
11+
#include <index/disktxpos.h>
12+
#include <node/blockstorage.h>
13+
#include <pubkey.h>
14+
#include <primitives/transaction.h>
15+
16+
#include <undo.h>
17+
#include <validation.h>
18+
19+
#include <dbwrapper.h>
20+
#include <hash.h>
21+
22+
constexpr uint8_t DB_SILENT_PAYMENT_INDEX{'s'};
23+
/* Save space on mainnet by starting the index at Taproot activation.
24+
* Copying the height here assuming DEPLOYMENT_TAPROOT will be dropped:
25+
* https://github.com/bitcoin/bitcoin/pull/26201/
26+
* Only apply this storage optimization on mainnet.
27+
*/
28+
const int TAPROOT_MAINNET_ACTIVATION_HEIGHT{709632};
29+
30+
std::unique_ptr<BIP352Index> g_bip352_index;
31+
32+
/** Access to the silent payment index database (indexes/bip352/) */
33+
class BIP352Index::DB : public BaseIndex::DB
34+
{
35+
public:
36+
explicit DB(size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
37+
38+
bool WriteSilentPayments(const std::pair<uint256, std::vector<CPubKey>>& tweaks);
39+
};
40+
41+
BIP352Index::DB::DB(size_t n_cache_size, bool f_memory, bool f_wipe) :
42+
BaseIndex::DB(gArgs.GetDataDirNet() / "indexes" / "bip352", n_cache_size, f_memory, f_wipe)
43+
{}
44+
45+
bool BIP352Index::DB::WriteSilentPayments(const std::pair<uint256, std::vector<CPubKey>>& tweaks)
46+
{
47+
CDBBatch batch(*this);
48+
batch.Write(std::make_pair(DB_SILENT_PAYMENT_INDEX, tweaks.first), tweaks.second);
49+
return WriteBatch(batch);
50+
}
51+
52+
BIP352Index::BIP352Index(std::unique_ptr<interfaces::Chain> chain, size_t n_cache_size, bool f_memory, bool f_wipe)
53+
: BaseIndex(std::move(chain), "bip352 index", /*start_height=*/Params().IsTestChain() ? 0 : TAPROOT_MAINNET_ACTIVATION_HEIGHT), m_db(std::make_unique<BIP352Index::DB>(n_cache_size, f_memory, f_wipe))
54+
{}
55+
56+
BIP352Index::~BIP352Index() = default;
57+
58+
bool BIP352Index::GetSilentPaymentKeys(const std::vector<CTransactionRef>& txs, const CBlockUndo& block_undo, std::vector<CPubKey>& tweaked_pub_key_sums) const
59+
{
60+
assert(txs.size() - 1 == block_undo.vtxundo.size());
61+
62+
for (size_t i=0; i < txs.size(); i++) {
63+
auto& tx = txs.at(i);
64+
65+
if (!bip352::MaybeSilentPayment(tx)) continue;
66+
67+
// -1 as blockundo does not have coinbase tx
68+
CTxUndo undoTX{block_undo.vtxundo.at(i - 1)};
69+
std::map<COutPoint, Coin> coins;
70+
71+
for (size_t j = 0; j < tx->vin.size(); j++) {
72+
coins[tx->vin.at(j).prevout] = undoTX.vprevout.at(j);
73+
}
74+
75+
std::optional<CPubKey> tweaked_pk = bip352::GetSerializedSilentPaymentsPublicData(tx->vin, coins);
76+
if (tweaked_pk) tweaked_pub_key_sums.push_back(tweaked_pk.value());
77+
}
78+
79+
return true;
80+
}
81+
82+
interfaces::Chain::NotifyOptions BIP352Index::CustomOptions()
83+
{
84+
interfaces::Chain::NotifyOptions options;
85+
options.connect_undo_data = true;
86+
return options;
87+
}
88+
89+
bool BIP352Index::CustomAppend(const interfaces::BlockInfo& block)
90+
{
91+
// Exclude genesis block transaction because outputs are not spendable. This
92+
// is needed on non-mainnet chains because m_start_height is 0 by default.
93+
if (block.height == 0) return true;
94+
95+
// Exclude pre-taproot
96+
if (block.height < m_start_height) return true;
97+
98+
std::vector<CPubKey> tweaked_pub_key_sums;
99+
GetSilentPaymentKeys(Assert(block.data)->vtx, *Assert(block.undo_data), tweaked_pub_key_sums);
100+
101+
return m_db->WriteSilentPayments(make_pair(block.hash, tweaked_pub_key_sums));
102+
}
103+
104+
bool BIP352Index::FindSilentPayment(const uint256& block_hash, std::vector<CPubKey>& tweaked_pub_key_sums) const
105+
{
106+
return m_db->Read(std::make_pair(DB_SILENT_PAYMENT_INDEX, block_hash), tweaked_pub_key_sums);
107+
}
108+
109+
BaseIndex::DB& BIP352Index::GetDB() const { return *m_db; }

src/index/bip352.h

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright (c) 2023-present The Bitcoin Core developers
2+
// Distributed under the MIT software license, see the accompanying
3+
// file COPYING or http://www.opensource.org/licenses/mit-license.php.
4+
5+
#ifndef BITCOIN_INDEX_BIP352_H
6+
#define BITCOIN_INDEX_BIP352_H
7+
8+
#include <coins.h>
9+
#include <index/base.h>
10+
#include <pubkey.h>
11+
12+
class COutPoint;
13+
class CBlockUndo;
14+
15+
static constexpr bool DEFAULT_BIP352_INDEX{false};
16+
17+
/**
18+
* This index is used to look up the tweaked sum of eligible public keys for a
19+
* given transaction hash. See BIP352.
20+
*
21+
* Currently only silent payments v0 exists. Future versions may expand the
22+
* existing index or create a (perhaps overlapping) new one.
23+
*/
24+
class BIP352Index final : public BaseIndex
25+
{
26+
protected:
27+
class DB;
28+
29+
private:
30+
const std::unique_ptr<DB> m_db;
31+
32+
bool AllowPrune() const override { return false; }
33+
34+
/**
35+
* Derive the silent payment tweaked public key for every block transaction.
36+
*
37+
* @param[in] txs all block transactions
38+
* @param[in] block_undo block undo data
39+
* @param[out] tweaked_pub_key_sums the tweaked public keys, only for transactions that have one
40+
* @return false if something went wrong
41+
*/
42+
bool GetSilentPaymentKeys(const std::vector<CTransactionRef>& txs, const CBlockUndo& block_undo, std::vector<CPubKey>& tweaked_pub_key_sums) const;
43+
44+
protected:
45+
interfaces::Chain::NotifyOptions CustomOptions() override;
46+
47+
bool CustomAppend(const interfaces::BlockInfo& block) override;
48+
49+
BaseIndex::DB& GetDB() const override;
50+
public:
51+
52+
explicit BIP352Index(std::unique_ptr<interfaces::Chain> chain, size_t n_cache_size, bool f_memory = false, bool f_wipe = false);
53+
54+
// Destructor is declared because this class contains a unique_ptr to an incomplete type.
55+
virtual ~BIP352Index() override;
56+
57+
bool FindSilentPayment(const uint256& block_hash, std::vector<CPubKey>& tweaked_pub_key_sums) const;
58+
};
59+
60+
/// The global BIP325 index. May be null.
61+
extern std::unique_ptr<BIP352Index> g_bip352_index;
62+
63+
#endif // BITCOIN_INDEX_BIP352_H

src/init.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
#include <index/blockfilterindex.h>
2828
#include <index/coinstatsindex.h>
2929
#include <index/txindex.h>
30+
#include <index/bip352.h>
3031
#include <init/common.h>
3132
#include <interfaces/chain.h>
3233
#include <interfaces/init.h>
@@ -352,6 +353,7 @@ void Shutdown(NodeContext& node)
352353
for (auto* index : node.indexes) index->Stop();
353354
if (g_txindex) g_txindex.reset();
354355
if (g_coin_stats_index) g_coin_stats_index.reset();
356+
if (g_bip352_index) g_bip352_index.reset();
355357
DestroyAllBlockFilterIndexes();
356358
node.indexes.clear(); // all instances are nullptr now
357359

@@ -512,6 +514,9 @@ void SetupServerArgs(ArgsManager& argsman, bool can_listen_ipc)
512514
argsman.AddArg("-shutdownnotify=<cmd>", "Execute command immediately before beginning shutdown. The need for shutdown may be urgent, so be careful not to delay it long (if the command doesn't require interaction with the server, consider having it fork into the background).", ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
513515
#endif
514516
argsman.AddArg("-txindex", strprintf("Maintain a full transaction index, used by the getrawtransaction rpc call (default: %u)", DEFAULT_TXINDEX), ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
517+
argsman.AddArg("-bip352index",
518+
strprintf("Maintain an index of BIP352 v0 tweaked public keys. (default: %s)", DEFAULT_BIP352_INDEX),
519+
ArgsManager::ALLOW_ANY, OptionsCategory::OPTIONS);
515520
argsman.AddArg("-blockfilterindex=<type>",
516521
strprintf("Maintain an index of compact filters by block (default: %s, values: %s).", DEFAULT_BLOCKFILTERINDEX, ListBlockFilterTypes()) +
517522
" If <type> is not supplied or if <type> = 1, indexes for all known types are enabled.",
@@ -960,6 +965,8 @@ bool AppInitParameterInteraction(const ArgsManager& args)
960965
if (args.GetIntArg("-prune", 0)) {
961966
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX))
962967
return InitError(_("Prune mode is incompatible with -txindex."));
968+
if (args.GetBoolArg("-bip352index", DEFAULT_BIP352_INDEX))
969+
return InitError(_("Prune mode is incompatible with -bip352index."));
963970
if (args.GetBoolArg("-reindex-chainstate", false)) {
964971
return InitError(_("Prune mode is incompatible with -reindex-chainstate. Use full -reindex instead."));
965972
}
@@ -1707,6 +1714,9 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
17071714
if (args.GetBoolArg("-txindex", DEFAULT_TXINDEX)) {
17081715
LogInfo("* Using %.1f MiB for transaction index database", index_cache_sizes.tx_index * (1.0 / 1024 / 1024));
17091716
}
1717+
if (args.GetBoolArg("-bip352index", DEFAULT_BIP352_INDEX)) {
1718+
LogPrintf("* Using %.1f MiB for BIP352 index database\n", index_cache_sizes.bip352_index * (1.0 / 1024 / 1024));
1719+
}
17101720
for (BlockFilterType filter_type : g_enabled_filter_types) {
17111721
LogInfo("* Using %.1f MiB for %s block filter index database",
17121722
index_cache_sizes.filter_index * (1.0 / 1024 / 1024), BlockFilterTypeName(filter_type));
@@ -1783,6 +1793,11 @@ bool AppInitMain(NodeContext& node, interfaces::BlockAndHeaderTipInfo* tip_info)
17831793
node.indexes.emplace_back(g_coin_stats_index.get());
17841794
}
17851795

1796+
if (args.GetBoolArg("-bip352index", DEFAULT_BIP352_INDEX)) {
1797+
g_bip352_index = std::make_unique<BIP352Index>(interfaces::MakeChain(node), index_cache_sizes.bip352_index, false, do_reindex);
1798+
node.indexes.emplace_back(g_bip352_index.get());
1799+
}
1800+
17861801
// Init indexes
17871802
for (auto index : node.indexes) if (!index->Init()) return false;
17881803

src/node/caches.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include <node/caches.h>
66

77
#include <common/args.h>
8+
#include <index/bip352.h>
89
#include <index/txindex.h>
910
#include <kernel/caches.h>
1011
#include <logging.h>
@@ -21,6 +22,8 @@ static constexpr size_t MAX_TX_INDEX_CACHE{1024_MiB};
2122
static constexpr size_t MAX_FILTER_INDEX_CACHE{1024_MiB};
2223
//! Maximum dbcache size on 32-bit systems.
2324
static constexpr size_t MAX_32BIT_DBCACHE{1024_MiB};
25+
//! Max memory allocated to the BIP352 index cache in bytes.
26+
static constexpr size_t MAX_BIP352_INDEX_CACHE{1024_MiB};
2427

2528
namespace node {
2629
CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes)
@@ -36,12 +39,14 @@ CacheSizes CalculateCacheSizes(const ArgsManager& args, size_t n_indexes)
3639

3740
IndexCacheSizes index_sizes;
3841
index_sizes.tx_index = std::min(total_cache / 8, args.GetBoolArg("-txindex", DEFAULT_TXINDEX) ? MAX_TX_INDEX_CACHE : 0);
42+
index_sizes.bip352_index = std::min(total_cache / 8, args.GetBoolArg("-bip352index", DEFAULT_BIP352_INDEX) ? MAX_BIP352_INDEX_CACHE : 0);
3943
total_cache -= index_sizes.tx_index;
4044
if (n_indexes > 0) {
4145
size_t max_cache = std::min(total_cache / 8, MAX_FILTER_INDEX_CACHE);
4246
index_sizes.filter_index = max_cache / n_indexes;
4347
total_cache -= index_sizes.filter_index * n_indexes;
4448
}
49+
4550
return {index_sizes, kernel::CacheSizes{total_cache}};
4651
}
4752
} // namespace node

src/node/caches.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ static constexpr size_t DEFAULT_DB_CACHE{DEFAULT_KERNEL_CACHE};
1919

2020
namespace node {
2121
struct IndexCacheSizes {
22+
size_t bip352_index{0};
2223
size_t tx_index{0};
2324
size_t filter_index{0};
2425
};

src/rpc/node.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include <index/blockfilterindex.h>
1111
#include <index/coinstatsindex.h>
1212
#include <index/txindex.h>
13+
#include <index/bip352.h>
1314
#include <interfaces/chain.h>
1415
#include <interfaces/echo.h>
1516
#include <interfaces/init.h>
@@ -395,6 +396,11 @@ static RPCHelpMan getindexinfo()
395396
result.pushKVs(SummaryToJSON(g_coin_stats_index->GetSummary(), index_name));
396397
}
397398

399+
if (g_bip352_index) {
400+
result.pushKVs(SummaryToJSON(g_bip352_index->GetSummary(), index_name));
401+
}
402+
403+
398404
ForEachBlockFilterIndex([&result, &index_name](const BlockFilterIndex& index) {
399405
result.pushKVs(SummaryToJSON(index.GetSummary(), index_name));
400406
});

0 commit comments

Comments
 (0)