Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
8ae2280
feat: add special transaction support to compact block filters (BIP-1…
PastaPastaPasta Aug 19, 2025
773fe9b
lint: add specialtx_filter circular dependencies to allowed list
PastaPastaPasta Aug 20, 2025
0ea85f7
fix: remove unused variable in blockfilter_tests
PastaPastaPasta Aug 20, 2025
8d08a54
Apply suggestions from code review
PastaPastaPasta Aug 20, 2025
3fb9183
test: improve p2p_blockfilters special transaction tests
PastaPastaPasta Aug 20, 2025
7a33c14
tests: compact filters: fix F841, verify tx inclusion, and cleanups
PastaPastaPasta Aug 20, 2025
1c9809f
filters: add cross-reference comments to keep bloom and compact filte…
PastaPastaPasta Aug 20, 2025
9266b9a
refactor: break circular dependency over evo/assetlock and llmq/signing
knst Aug 20, 2025
a1ecf5d
tests: p2p_blockfilters: skip when wallet module is not available
PastaPastaPasta Aug 20, 2025
a5bab55
test: remove wallet dependency from p2p_blockfilters.py and refactor …
knst Aug 20, 2025
c734b64
tests: p2p_blockfilters: use assert_greater_than for clearer failures
PastaPastaPasta Aug 20, 2025
bf352e7
evo: use Span<const unsigned char> in specialtx filter extraction cal…
PastaPastaPasta Aug 20, 2025
492c109
feat: add versioning to blockfilter index to detect incompatible format
PastaPastaPasta Aug 20, 2025
d3f1b9e
test: remove wallet dependency check from blockfilter tests
PastaPastaPasta Aug 20, 2025
f28bf6d
feat: start blockfilter sync from scratch on db version upgrades
UdjinM6 Aug 20, 2025
4750246
doc: shrink release notes
UdjinM6 Aug 20, 2025
09490b7
chore: drop useless feature_blockfilter_version.py
UdjinM6 Aug 20, 2025
b2522a2
chore: make clang format happy
UdjinM6 Aug 20, 2025
4b90441
docs: indicate that blockfilter will be re-created
PastaPastaPasta Aug 21, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,7 @@ BITCOIN_CORE_H = \
evo/simplifiedmns.h \
evo/smldiff.h \
evo/specialtx.h \
evo/specialtx_filter.h \
evo/specialtxman.h \
dsnotificationinterface.h \
governance/governance.h \
Expand Down Expand Up @@ -479,6 +480,7 @@ libbitcoin_node_a_SOURCES = \
evo/simplifiedmns.cpp \
evo/smldiff.cpp \
evo/specialtx.cpp \
evo/specialtx_filter.cpp \
evo/specialtxman.cpp \
flatfile.cpp \
governance/classes.cpp \
Expand Down
6 changes: 6 additions & 0 deletions src/blockfilter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include <blockfilter.h>
#include <crypto/siphash.h>
#include <evo/specialtx_filter.h>
#include <hash.h>
#include <primitives/transaction.h>
#include <script/script.h>
Expand Down Expand Up @@ -195,6 +196,11 @@ static GCSFilter::ElementSet BasicFilterElements(const CBlock& block,
if (script.empty() || script[0] == OP_RETURN) continue;
elements.emplace(script.begin(), script.end());
}

// Extract special transaction elements using delegation pattern
ExtractSpecialTxFilterElements(*tx, [&elements](const std::vector<unsigned char>& data) {
elements.emplace(data.begin(), data.end());
});
}

for (const CTxUndo& tx_undo : block_undo.vtxundo) {
Expand Down
134 changes: 134 additions & 0 deletions src/evo/specialtx_filter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Copyright (c) 2024 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#include <evo/specialtx_filter.h>

#include <evo/assetlocktx.h>
#include <evo/providertx.h>
#include <evo/specialtx.h>
#include <primitives/transaction.h>
#include <script/script.h>
#include <span.h>
#include <streams.h>

/**
* Rationale for Special Transaction Field Extraction:
*
* This implementation extracts specific fields from Dash special transactions
* to maintain parity with the bloom filter implementation (CBloomFilter::CheckSpecialTransactionMatchesAndUpdate).
*
* The fields extracted are those that SPV clients might need to detect:
* - Owner/Voting keys: To track masternode ownership and voting rights
* - Payout scripts: To detect payments to specific addresses
* - ProTx hashes: To track masternode lifecycle and updates
* - Collateral outpoints: To track masternode collateral
* - Credit outputs: To track platform-related transactions
*
* Each transaction type has different fields based on its purpose:
* - ProRegTx: All identity and payout fields (initial registration)
* - ProUpServTx: ProTx hash and operator payout (service updates)
* - ProUpRegTx: ProTx hash, voting key, and payout (ownership updates)
* - ProUpRevTx: ProTx hash only (revocation tracking)
* - AssetLockTx: Credit output scripts (platform credits)
*/
// Helper function to add a script to the filter if it's not empty
static void AddScriptElement(const CScript& script,
const std::function<void(const std::vector<unsigned char>&)>& addElement)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it correct, that here is std::vector due to bitcoin's implementation of GCSFilter? Because it seems as there should be Span to avoid memory allocation.

{
if (!script.empty()) {
addElement(std::vector<unsigned char>(script.begin(), script.end()));
}
}

// Helper function to add a hash/key to the filter
template<typename T>
static void AddHashElement(const T& hash,
const std::function<void(const std::vector<unsigned char>&)>& addElement)
{
addElement(std::vector<unsigned char>(hash.begin(), hash.end()));
}

void ExtractSpecialTxFilterElements(const CTransaction& tx,
const std::function<void(const std::vector<unsigned char>&)>& addElement)
{
if (!tx.HasExtraPayloadField()) {
return; // not a special transaction
}

switch(tx.nType) {
case TRANSACTION_PROVIDER_REGISTER: {
if (const auto opt_proTx = GetTxPayload<CProRegTx>(tx)) {
// Add collateral outpoint
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
stream << opt_proTx->collateralOutpoint;
auto span = MakeUCharSpan(stream);
addElement(std::vector<unsigned char>(span.begin(), span.end()));

// Add owner key ID
AddHashElement(opt_proTx->keyIDOwner, addElement);

// Add voting key ID
AddHashElement(opt_proTx->keyIDVoting, addElement);

// Add payout script
AddScriptElement(opt_proTx->scriptPayout, addElement);
}
break;
}
case TRANSACTION_PROVIDER_UPDATE_SERVICE: {
if (const auto opt_proTx = GetTxPayload<CProUpServTx>(tx)) {
// Add ProTx hash
AddHashElement(opt_proTx->proTxHash, addElement);

// Add operator payout script
AddScriptElement(opt_proTx->scriptOperatorPayout, addElement);
}
break;
}
case TRANSACTION_PROVIDER_UPDATE_REGISTRAR: {
if (const auto opt_proTx = GetTxPayload<CProUpRegTx>(tx)) {
// Add ProTx hash
AddHashElement(opt_proTx->proTxHash, addElement);

// Add voting key ID
AddHashElement(opt_proTx->keyIDVoting, addElement);

// Add payout script
AddScriptElement(opt_proTx->scriptPayout, addElement);
}
break;
}
case TRANSACTION_PROVIDER_UPDATE_REVOKE: {
if (const auto opt_proTx = GetTxPayload<CProUpRevTx>(tx)) {
// Add ProTx hash
AddHashElement(opt_proTx->proTxHash, addElement);
}
break;
}
case TRANSACTION_ASSET_LOCK: {
// Asset Lock transactions have special outputs (creditOutputs) that should be included
if (const auto opt_assetlockTx = GetTxPayload<CAssetLockPayload>(tx)) {
const auto& extraOuts = opt_assetlockTx->getCreditOutputs();
for (const CTxOut& txout : extraOuts) {
const CScript& script = txout.scriptPubKey;
// Exclude OP_RETURN outputs as they are not spendable
if (!script.empty() && script[0] != OP_RETURN) {
AddScriptElement(script, addElement);
}
}
}
break;
}
case TRANSACTION_ASSET_UNLOCK:
case TRANSACTION_COINBASE:
case TRANSACTION_QUORUM_COMMITMENT:
case TRANSACTION_MNHF_SIGNAL:
// No additional special fields needed for these transaction types
// Their standard outputs are already included in the base filter
break;
default:
// Unknown special transaction type - ignore
break;
}
}
24 changes: 24 additions & 0 deletions src/evo/specialtx_filter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Copyright (c) 2024 The Dash Core developers
// Distributed under the MIT software license, see the accompanying
// file COPYING or http://www.opensource.org/licenses/mit-license.php.

#ifndef BITCOIN_EVO_SPECIALTX_FILTER_H
#define BITCOIN_EVO_SPECIALTX_FILTER_H

#include <functional>
#include <vector>

class CTransaction;

/**
* Extract filterable elements from special transactions for use in compact block filters.
* This function extracts the same fields that are included in bloom filters to ensure
* SPV clients can detect special transactions using either filtering mechanism.
*
* @param tx The transaction to extract elements from
* @param addElement Callback function to add extracted elements to the filter
*/
void ExtractSpecialTxFilterElements(const CTransaction& tx,
const std::function<void(const std::vector<unsigned char>&)>& addElement);

#endif // BITCOIN_EVO_SPECIALTX_FILTER_H
Loading
Loading