Skip to content

Commit 8ae2280

Browse files
feat: add special transaction support to compact block filters (BIP-157/158)
Implements extraction of special transaction fields for compact block filters, achieving feature parity with bloom filters. SPV clients can now detect special transactions using either filtering mechanism. Changes: - Add ExtractSpecialTxFilterElements() to extract fields from special txs - Integrate special tx extraction into BasicFilterElements() - Support all special transaction types: * ProRegTx: collateral, owner/voting keys, payout script * ProUpServTx: ProTx hash, operator payout * ProUpRegTx: ProTx hash, voting key, payout script * ProUpRevTx: ProTx hash * AssetLockTx: credit output scripts - Add comprehensive unit tests for all transaction types - Add functional tests with actual AssetLockTx transactions - Use delegation pattern to avoid circular dependencies The implementation mirrors CheckSpecialTransactionMatchesAndUpdate() to ensure consistent behavior between bloom and compact filters. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent 3c6ab9f commit 8ae2280

File tree

6 files changed

+690
-1
lines changed

6 files changed

+690
-1
lines changed

src/Makefile.am

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ BITCOIN_CORE_H = \
200200
evo/simplifiedmns.h \
201201
evo/smldiff.h \
202202
evo/specialtx.h \
203+
evo/specialtx_filter.h \
203204
evo/specialtxman.h \
204205
dsnotificationinterface.h \
205206
governance/governance.h \
@@ -479,6 +480,7 @@ libbitcoin_node_a_SOURCES = \
479480
evo/simplifiedmns.cpp \
480481
evo/smldiff.cpp \
481482
evo/specialtx.cpp \
483+
evo/specialtx_filter.cpp \
482484
evo/specialtxman.cpp \
483485
flatfile.cpp \
484486
governance/classes.cpp \

src/blockfilter.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
#include <blockfilter.h>
99
#include <crypto/siphash.h>
10+
#include <evo/specialtx_filter.h>
1011
#include <hash.h>
1112
#include <primitives/transaction.h>
1213
#include <script/script.h>
@@ -195,6 +196,11 @@ static GCSFilter::ElementSet BasicFilterElements(const CBlock& block,
195196
if (script.empty() || script[0] == OP_RETURN) continue;
196197
elements.emplace(script.begin(), script.end());
197198
}
199+
200+
// Extract special transaction elements using delegation pattern
201+
ExtractSpecialTxFilterElements(*tx, [&elements](const std::vector<unsigned char>& data) {
202+
elements.emplace(data.begin(), data.end());
203+
});
198204
}
199205

200206
for (const CTxUndo& tx_undo : block_undo.vtxundo) {

src/evo/specialtx_filter.cpp

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright (c) 2024 The Dash 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 <evo/specialtx_filter.h>
6+
7+
#include <evo/assetlocktx.h>
8+
#include <evo/providertx.h>
9+
#include <evo/specialtx.h>
10+
#include <primitives/transaction.h>
11+
#include <script/script.h>
12+
#include <span.h>
13+
#include <streams.h>
14+
15+
/**
16+
* Rationale for Special Transaction Field Extraction:
17+
*
18+
* This implementation extracts specific fields from Dash special transactions
19+
* to maintain parity with the bloom filter implementation (CBloomFilter::CheckSpecialTransactionMatchesAndUpdate).
20+
*
21+
* The fields extracted are those that SPV clients might need to detect:
22+
* - Owner/Voting keys: To track masternode ownership and voting rights
23+
* - Payout scripts: To detect payments to specific addresses
24+
* - ProTx hashes: To track masternode lifecycle and updates
25+
* - Collateral outpoints: To track masternode collateral
26+
* - Credit outputs: To track platform-related transactions
27+
*
28+
* Each transaction type has different fields based on its purpose:
29+
* - ProRegTx: All identity and payout fields (initial registration)
30+
* - ProUpServTx: ProTx hash and operator payout (service updates)
31+
* - ProUpRegTx: ProTx hash, voting key, and payout (ownership updates)
32+
* - ProUpRevTx: ProTx hash only (revocation tracking)
33+
* - AssetLockTx: Credit output scripts (platform credits)
34+
*/
35+
// Helper function to add a script to the filter if it's not empty
36+
static void AddScriptElement(const CScript& script,
37+
const std::function<void(const std::vector<unsigned char>&)>& addElement)
38+
{
39+
if (!script.empty()) {
40+
addElement(std::vector<unsigned char>(script.begin(), script.end()));
41+
}
42+
}
43+
44+
// Helper function to add a hash/key to the filter
45+
template<typename T>
46+
static void AddHashElement(const T& hash,
47+
const std::function<void(const std::vector<unsigned char>&)>& addElement)
48+
{
49+
addElement(std::vector<unsigned char>(hash.begin(), hash.end()));
50+
}
51+
52+
void ExtractSpecialTxFilterElements(const CTransaction& tx,
53+
const std::function<void(const std::vector<unsigned char>&)>& addElement)
54+
{
55+
if (!tx.HasExtraPayloadField()) {
56+
return; // not a special transaction
57+
}
58+
59+
switch(tx.nType) {
60+
case TRANSACTION_PROVIDER_REGISTER: {
61+
if (const auto opt_proTx = GetTxPayload<CProRegTx>(tx)) {
62+
// Add collateral outpoint
63+
CDataStream stream(SER_NETWORK, PROTOCOL_VERSION);
64+
stream << opt_proTx->collateralOutpoint;
65+
auto span = MakeUCharSpan(stream);
66+
addElement(std::vector<unsigned char>(span.begin(), span.end()));
67+
68+
// Add owner key ID
69+
AddHashElement(opt_proTx->keyIDOwner, addElement);
70+
71+
// Add voting key ID
72+
AddHashElement(opt_proTx->keyIDVoting, addElement);
73+
74+
// Add payout script
75+
AddScriptElement(opt_proTx->scriptPayout, addElement);
76+
}
77+
break;
78+
}
79+
case TRANSACTION_PROVIDER_UPDATE_SERVICE: {
80+
if (const auto opt_proTx = GetTxPayload<CProUpServTx>(tx)) {
81+
// Add ProTx hash
82+
AddHashElement(opt_proTx->proTxHash, addElement);
83+
84+
// Add operator payout script
85+
AddScriptElement(opt_proTx->scriptOperatorPayout, addElement);
86+
}
87+
break;
88+
}
89+
case TRANSACTION_PROVIDER_UPDATE_REGISTRAR: {
90+
if (const auto opt_proTx = GetTxPayload<CProUpRegTx>(tx)) {
91+
// Add ProTx hash
92+
AddHashElement(opt_proTx->proTxHash, addElement);
93+
94+
// Add voting key ID
95+
AddHashElement(opt_proTx->keyIDVoting, addElement);
96+
97+
// Add payout script
98+
AddScriptElement(opt_proTx->scriptPayout, addElement);
99+
}
100+
break;
101+
}
102+
case TRANSACTION_PROVIDER_UPDATE_REVOKE: {
103+
if (const auto opt_proTx = GetTxPayload<CProUpRevTx>(tx)) {
104+
// Add ProTx hash
105+
AddHashElement(opt_proTx->proTxHash, addElement);
106+
}
107+
break;
108+
}
109+
case TRANSACTION_ASSET_LOCK: {
110+
// Asset Lock transactions have special outputs (creditOutputs) that should be included
111+
if (const auto opt_assetlockTx = GetTxPayload<CAssetLockPayload>(tx)) {
112+
const auto& extraOuts = opt_assetlockTx->getCreditOutputs();
113+
for (const CTxOut& txout : extraOuts) {
114+
const CScript& script = txout.scriptPubKey;
115+
// Exclude OP_RETURN outputs as they are not spendable
116+
if (!script.empty() && script[0] != OP_RETURN) {
117+
AddScriptElement(script, addElement);
118+
}
119+
}
120+
}
121+
break;
122+
}
123+
case TRANSACTION_ASSET_UNLOCK:
124+
case TRANSACTION_COINBASE:
125+
case TRANSACTION_QUORUM_COMMITMENT:
126+
case TRANSACTION_MNHF_SIGNAL:
127+
// No additional special fields needed for these transaction types
128+
// Their standard outputs are already included in the base filter
129+
break;
130+
default:
131+
// Unknown special transaction type - ignore
132+
break;
133+
}
134+
}

src/evo/specialtx_filter.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Copyright (c) 2024 The Dash 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_EVO_SPECIALTX_FILTER_H
6+
#define BITCOIN_EVO_SPECIALTX_FILTER_H
7+
8+
#include <functional>
9+
#include <vector>
10+
11+
class CTransaction;
12+
13+
/**
14+
* Extract filterable elements from special transactions for use in compact block filters.
15+
* This function extracts the same fields that are included in bloom filters to ensure
16+
* SPV clients can detect special transactions using either filtering mechanism.
17+
*
18+
* @param tx The transaction to extract elements from
19+
* @param addElement Callback function to add extracted elements to the filter
20+
*/
21+
void ExtractSpecialTxFilterElements(const CTransaction& tx,
22+
const std::function<void(const std::vector<unsigned char>&)>& addElement);
23+
24+
#endif // BITCOIN_EVO_SPECIALTX_FILTER_H

0 commit comments

Comments
 (0)