diff --git a/src/bloom.cpp b/src/bloom.cpp index d57b0df40c5c2..8c9e5a60a5034 100644 --- a/src/bloom.cpp +++ b/src/bloom.cpp @@ -5,6 +5,10 @@ #include "bloom.h" #include "primitives/transaction.h" +#include "evo/specialtx.h" +#include "evo/providertx.h" +#include "evo/cbtx.h" +#include "llmq/quorums_commitment.h" #include "hash.h" #include "script/script.h" #include "script/standard.h" @@ -113,6 +117,12 @@ bool CBloomFilter::contains(const uint256& hash) const return contains(data); } +bool CBloomFilter::contains(const uint160& hash) const +{ + std::vector data(hash.begin(), hash.end()); + return contains(data); +} + void CBloomFilter::clear() { vData.assign(vData.size(),0); @@ -131,6 +141,96 @@ bool CBloomFilter::IsWithinSizeConstraints() const return vData.size() <= MAX_BLOOM_FILTER_SIZE && nHashFuncs <= MAX_HASH_FUNCS; } +// Match if the filter contains any arbitrary script data element in script +bool CBloomFilter::CheckScript(const CScript &script) const +{ + CScript::const_iterator pc = script.begin(); + std::vector data; + while (pc < script.end()) { + opcodetype opcode; + if (!script.GetOp(pc, opcode, data)) + break; + if (data.size() != 0 && contains(data)) + return true; + } + return false; +} + +// If the transaction is a special transaction that has a registration +// transaction hash, test the registration transaction hash. +// If the transaction is a special transaction with any public keys or any +// public key hashes test them. +// If the transaction is a special transaction with payout addresses test +// the hash160 of those addresses. +// Filter is updated only if it has BLOOM_UPDATE_ALL flag to be able to have +// simple SPV wallets that doesn't work with DIP2 transactions (multicoin +// wallets, etc.) +bool CBloomFilter::CheckSpecialTransactionMatchesAndUpdate(const CTransaction &tx) +{ + if(tx.nVersion != 3 || tx.nType == TRANSACTION_NORMAL) { + return false; // it is not a special transaction + } + switch(tx.nType) { + case(TRANSACTION_PROVIDER_REGISTER): { + CProRegTx proTx; + if (GetTxPayload(tx, proTx)) { + if(contains(proTx.collateralOutpoint) || + contains(proTx.keyIDOwner) || + contains(proTx.keyIDVoting) || + CheckScript(proTx.scriptPayout)) { + if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL) + insert(tx.GetHash()); + return true; + } + } + return false; + } + case(TRANSACTION_PROVIDER_UPDATE_SERVICE): { + CProUpServTx proTx; + if (GetTxPayload(tx, proTx)) { + if(contains(proTx.proTxHash)) { + return true; + } + if(CheckScript(proTx.scriptOperatorPayout)) { + if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL) + insert(proTx.proTxHash); + return true; + } + } + return false; + } + case(TRANSACTION_PROVIDER_UPDATE_REGISTRAR): { + CProUpRegTx proTx; + if (GetTxPayload(tx, proTx)) { + if(contains(proTx.proTxHash)) + return true; + if(contains(proTx.keyIDVoting) || + CheckScript(proTx.scriptPayout)) { + if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL) + insert(proTx.proTxHash); + return true; + } + } + return false; + } + case(TRANSACTION_PROVIDER_UPDATE_REVOKE): { + CProUpRevTx proTx; + if (GetTxPayload(tx, proTx)) { + if(contains(proTx.proTxHash)) + return true; + } + return false; + } + case(TRANSACTION_COINBASE): + case(TRANSACTION_QUORUM_COMMITMENT): + // No aditional checks for this transaction types + return false; + } + + LogPrintf("Unknown special transaction type in Bloom filter check."); + return false; +} + bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) { bool fFound = false; @@ -144,6 +244,9 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) if (contains(hash)) fFound = true; + // Check additional matches for special transactions + fFound = fFound || CheckSpecialTransactionMatchesAndUpdate(tx); + for (unsigned int i = 0; i < tx.vout.size(); i++) { const CTxOut& txout = tx.vout[i]; @@ -151,27 +254,17 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) // If this matches, also add the specific output that was matched. // This means clients don't have to update the filter themselves when a new relevant tx // is discovered in order to find spending transactions, which avoids round-tripping and race conditions. - CScript::const_iterator pc = txout.scriptPubKey.begin(); - std::vector data; - while (pc < txout.scriptPubKey.end()) - { - opcodetype opcode; - if (!txout.scriptPubKey.GetOp(pc, opcode, data)) - break; - if (data.size() != 0 && contains(data)) + if(CheckScript(txout.scriptPubKey)) { + fFound = true; + if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL) + insert(COutPoint(hash, i)); + else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY) { - fFound = true; - if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_ALL) + txnouttype type; + std::vector > vSolutions; + if (Solver(txout.scriptPubKey, type, vSolutions) && + (type == TX_PUBKEY || type == TX_MULTISIG)) insert(COutPoint(hash, i)); - else if ((nFlags & BLOOM_UPDATE_MASK) == BLOOM_UPDATE_P2PUBKEY_ONLY) - { - txnouttype type; - std::vector > vSolutions; - if (Solver(txout.scriptPubKey, type, vSolutions) && - (type == TX_PUBKEY || type == TX_MULTISIG)) - insert(COutPoint(hash, i)); - } - break; } } } @@ -186,16 +279,8 @@ bool CBloomFilter::IsRelevantAndUpdate(const CTransaction& tx) return true; // Match if the filter contains any arbitrary script data element in any scriptSig in tx - CScript::const_iterator pc = txin.scriptSig.begin(); - std::vector data; - while (pc < txin.scriptSig.end()) - { - opcodetype opcode; - if (!txin.scriptSig.GetOp(pc, opcode, data)) - break; - if (data.size() != 0 && contains(data)) - return true; - } + if(CheckScript(txin.scriptSig)) + return true; } return false; diff --git a/src/bloom.h b/src/bloom.h index d3a017371f30a..8b6abec6bcc89 100644 --- a/src/bloom.h +++ b/src/bloom.h @@ -10,8 +10,10 @@ #include class COutPoint; +class CScript; class CTransaction; class uint256; +class uint160; //! 20,000 items with fp rate < 0.1% or 10,000 items and <0.0001% static const unsigned int MAX_BLOOM_FILTER_SIZE = 36000; // bytes @@ -57,6 +59,10 @@ class CBloomFilter CBloomFilter(unsigned int nElements, double nFPRate, unsigned int nTweak); friend class CRollingBloomFilter; + // Check matches for arbitrary script data elements + bool CheckScript(const CScript& script) const; + // Check additional matches for special transactions + bool CheckSpecialTransactionMatchesAndUpdate(const CTransaction& tx); public: /** * Creates a new bloom filter which will provide the given fp rate when filled with the given number of elements @@ -87,6 +93,7 @@ class CBloomFilter bool contains(const std::vector& vKey) const; bool contains(const COutPoint& outpoint) const; bool contains(const uint256& hash) const; + bool contains(const uint160& hash) const; void clear(); void reset(unsigned int nNewTweak); diff --git a/src/test/bloom_tests.cpp b/src/test/bloom_tests.cpp index 6eeafac09b61e..16b3d067cd055 100644 --- a/src/test/bloom_tests.cpp +++ b/src/test/bloom_tests.cpp @@ -178,6 +178,106 @@ BOOST_AUTO_TEST_CASE(bloom_match) BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(tx), "Simple Bloom filter matched COutPoint for an output we didn't care about"); } +BOOST_AUTO_TEST_CASE(dip2_bloom_match) +{ + // ProRegTx from testnet (txid: 39a1339d9bf26de701345beecc5de75a690bc9533741a3dbe90f2fd88b8ed461) + CDataStream stream(ParseHex("0300010001c02142f16969d106f4cb144e17975bf9261b8e331354879c6d006e71494b3460000000006b483045022100d6eb9e25fc3215e6a4b5180a1957319d6658103566f0274452bd50d0eddedff202200824a00f582a29c9e3694d34e5525275cabd08c488a0c55b4350c6bfa4fb2cf8012102a8d6433a8f799a13d3495f4aa5b99bd288adca5f59e0c9609f15a221220241c5feffffff0121c89a3b000000001976a914e54445646929fac8b7d6c71715913af44324978488ac00000000fd12010100000000009b9054ff7839b940277b8eb8211570b2f16850ef729ee635e24d722fbc4a46230100000000000000000000000000ffffc6c74af14e1f891cbc8a94fa7fea64ca9994870dca0f75bbd0750efda51589f86e30cc2305e7388c01ce0309c19a182cf37bced97c7da72236f660c0a395e765e6e06962ecff5a69d7de359c348a574176c210c37a25d4ffd917866fb0a300001976a914e54445646929fac8b7d6c71715913af44324978488ac26d5a99c01521d1fca6299de576bae82f901fb56e4b797a945e876ac69068f36411f9cdb72a01b273a53bd916d8c90dd584bd3a1c01dd84fec84eb046f66a4e3b30d39ea3215293a035166b5072349ebc08efd30c2c73ebd023d920f1db0aef91a4e"), SER_DISK, CLIENT_VERSION); + CTransaction proregtx(deserialize, stream); + // ProUpServTx from testnet (txid: 0c9627a054784f207db34c311b06e8bb797c4a120a236dbb02b51268dd186d39) + stream = CDataStream(ParseHex("030002000161d48e8bd82f0fe9dba3413753c90b695ae75dccee5b3401e76df29b9d33a139000000006a473044022036dfd41268e3a387b909d9d1cf61e4d5e97b6b37d5951e82c4ca4fed419b4b6a02203777d818319897a11cf4cd8b4c6495c5c3a5f96fcad6d3a0596431b1146b53c6012102a8d6433a8f799a13d3495f4aa5b99bd288adca5f59e0c9609f15a221220241c5feffffff01a1c69a3b000000001976a914e54445646929fac8b7d6c71715913af44324978488ac00000000b5010061d48e8bd82f0fe9dba3413753c90b695ae75dccee5b3401e76df29b9d33a13900000000000000000000ffffc6c74af14e1f000ccf39b295938f4021fc16b23e4eedbd64a6c9a5aaae6bc9ca085db4bc29ff911612dc4544fc470415cdd4be5659fee6aa7d84c3572c712f5904b2e5d38cb217762717d88e4e1a956a9e82ce132e2b9b144e26cb3ff1e53675ede2769d99f46796ea0b8cbf33b78c15fe9a437dc4d1131ce2af8fd2ed5b306b326f9fcffcd416"), SER_DISK, CLIENT_VERSION); + CTransaction proupservtx(deserialize, stream); + // ProUpRegTx from testnet (txid: 3037bf4e45d1a9d57891ea8baa72b38ad6323bd28945b11325bdd33ebea049b3) + stream = CDataStream(ParseHex("0300030001396d18dd6812b502bb6d230a124a7c79bbe8061b314cb37d204f7854a027960c000000006a47304402203aefa0e1a08469050edc1e5da6e24f783162bebc15cc006f717097e56e07fa0b02207c7f47d113355b095c7edd2408d0554aa6b87187bf95652c516fc7747720fb19012102a8d6433a8f799a13d3495f4aa5b99bd288adca5f59e0c9609f15a221220241c5feffffff01f2c49a3b000000001976a914e54445646929fac8b7d6c71715913af44324978488ac00000000e4010061d48e8bd82f0fe9dba3413753c90b695ae75dccee5b3401e76df29b9d33a13900000efda51589f86e30cc2305e7388c01ce0309c19a182cf37bced97c7da72236f660c0a395e765e6e06962ecff5a69d7de359c348a574176c210c37a25d4ffd917866fb0a31976a914e54445646929fac8b7d6c71715913af44324978488aca8141db635af714f32c7415b298937e171c6bb8a3c8627852ae2b69917ece86a411fa51861025bb5ff4ba4ad3be6090c4ba76b1671d70799ed6882c57bcfeaf27cbd558068e9b5fc1553abce8822fcee63e8f6fbb06ad5b753d47e794bfacadbde3f"), SER_DISK, CLIENT_VERSION); + CTransaction proupregtx(deserialize, stream); + // ProUpRevTx from testnet (txid: 70c41a5fea07a80c1297771c43eb58b8f52222b971f61f5c7c2030ec0baf7f9a) + stream = CDataStream(ParseHex("0300040001b349a0be3ed3bd2513b14589d23b32d68ab372aa8bea9178d5a9d1454ebf3730000000006a4730440220428d87daf3b0ddca9656d59592000f9930a88162c0b017ca460c69c33b76bda202200276a521d94acc57e010262aa1d7d016cebbe7a9806d26dbebb69102ce9cf7b2012102a8d6433a8f799a13d3495f4aa5b99bd288adca5f59e0c9609f15a221220241c5feffffff0183c39a3b000000001976a914e54445646929fac8b7d6c71715913af44324978488ac00000000a4010061d48e8bd82f0fe9dba3413753c90b695ae75dccee5b3401e76df29b9d33a1390000c024ffd34d15686f7a376ecdf45800ddcebf4eb966fd3123cf9450515a38881a0de852c97297bbd9cedb79280f3f1e31b092904a8870274a5a0e52de2ae1dc0bad74851dc0a1e4fe4c274535a2422d3e1510a7be2fbecaae73128e8eb2f336382fb376d2f82273a72960980972b02e3ecf895c00187e0cbe735dc44cb3d97711"), SER_DISK, CLIENT_VERSION); + CTransaction prouprevtx(deserialize, stream); + + // check collateral outpoint match in ProRegTx + CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(COutPoint(uint256S("0x23464abc2f724de235e69e72ef5068f1b2701521b88e7b2740b93978ff54909b"), 1)); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(proregtx), "Bloom filter didn't match collateral outpoint in ProRegTx"); + // check owner keyid match in ProRegTx + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(ParseHex("891cbc8a94fa7fea64ca9994870dca0f75bbd075")); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(proregtx), "Bloom filter didn't match Owner keyid in ProRegTx"); + // check voting keyid match in ProRegTx + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(ParseHex("359c348a574176c210c37a25d4ffd917866fb0a3")); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(proregtx), "Bloom filter didn't match Voting keyid in ProRegTx"); + // check scriptPayout match in ProRegTx + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(ParseHex("e54445646929fac8b7d6c71715913af443249784")); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(proregtx), "Bloom filter didn't match scriptPayout in ProRegTx"); + + // check proTxHash match in ProUpServTx + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(uint256S("0x39a1339d9bf26de701345beecc5de75a690bc9533741a3dbe90f2fd88b8ed461")); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(proupservtx), "Bloom filter didn't match proTxHash in ProUpServTx"); + // check scriptOperatorPayout match in ProUpServTx + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(ParseHex("e54445646929fac8b7d6c71715913af443249784")); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(proupservtx), "Bloom filter didn't match scriptOperatorPayout in ProUpServTx"); + + // check proTxHash match in ProUpRegTx + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(uint256S("0x39a1339d9bf26de701345beecc5de75a690bc9533741a3dbe90f2fd88b8ed461")); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(proupregtx), "Bloom filter didn't match proTxHash in ProUpRegTx"); + // check voting keyid match in ProUpRegTx + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(ParseHex("359c348a574176c210c37a25d4ffd917866fb0a3")); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(proupregtx), "Bloom filter didn't match Voting keyid in ProUpRegTx"); + // check scriptPayout match in ProUpRegTx + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(ParseHex("e54445646929fac8b7d6c71715913af443249784")); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(proupregtx), "Bloom filter didn't match scriptPayout in ProUpRegTx"); + + // check proTxHash match in ProUpRevTx + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(uint256S("0x39a1339d9bf26de701345beecc5de75a690bc9533741a3dbe90f2fd88b8ed461")); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(prouprevtx), "Bloom filter didn't match proTxHash in ProUpRevTx"); + + // check filter is not matching if it doesn't contain relative data + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + // extend real data with additional byte + filter.insert(uint256S("0x39a1339d9bf26de701345beecc5de75a690bc9533741a3dbe90f2fd88b8ed46100")); + filter.insert(ParseHex("e54445646929fac8b7d6c71715913af44324978400")); + filter.insert(ParseHex("359c348a574176c210c37a25d4ffd917866fb0a300")); + filter.insert(uint256S("0x39a1339d9bf26de701345beecc5de75a690bc9533741a3dbe90f2fd88b8ed46100")); + BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(prouprevtx), "Bloom filter match unrelated data"); + BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(proupregtx), "Bloom filter match unrelated data"); +} + +BOOST_AUTO_TEST_CASE(dip2_bloom_update) +{ + // ProRegTx from testnet (txid: 39a1339d9bf26de701345beecc5de75a690bc9533741a3dbe90f2fd88b8ed461) + CDataStream stream(ParseHex("0300010001c02142f16969d106f4cb144e17975bf9261b8e331354879c6d006e71494b3460000000006b483045022100d6eb9e25fc3215e6a4b5180a1957319d6658103566f0274452bd50d0eddedff202200824a00f582a29c9e3694d34e5525275cabd08c488a0c55b4350c6bfa4fb2cf8012102a8d6433a8f799a13d3495f4aa5b99bd288adca5f59e0c9609f15a221220241c5feffffff0121c89a3b000000001976a914e54445646929fac8b7d6c71715913af44324978488ac00000000fd12010100000000009b9054ff7839b940277b8eb8211570b2f16850ef729ee635e24d722fbc4a46230100000000000000000000000000ffffc6c74af14e1f891cbc8a94fa7fea64ca9994870dca0f75bbd0750efda51589f86e30cc2305e7388c01ce0309c19a182cf37bced97c7da72236f660c0a395e765e6e06962ecff5a69d7de359c348a574176c210c37a25d4ffd917866fb0a300001976a914e54445646929fac8b7d6c71715913af44324978488ac26d5a99c01521d1fca6299de576bae82f901fb56e4b797a945e876ac69068f36411f9cdb72a01b273a53bd916d8c90dd584bd3a1c01dd84fec84eb046f66a4e3b30d39ea3215293a035166b5072349ebc08efd30c2c73ebd023d920f1db0aef91a4e"), SER_DISK, CLIENT_VERSION); + CTransaction proregtx(deserialize, stream); + // ProUpServTx from testnet (txid: 0c9627a054784f207db34c311b06e8bb797c4a120a236dbb02b51268dd186d39) + stream = CDataStream(ParseHex("030002000161d48e8bd82f0fe9dba3413753c90b695ae75dccee5b3401e76df29b9d33a139000000006a473044022036dfd41268e3a387b909d9d1cf61e4d5e97b6b37d5951e82c4ca4fed419b4b6a02203777d818319897a11cf4cd8b4c6495c5c3a5f96fcad6d3a0596431b1146b53c6012102a8d6433a8f799a13d3495f4aa5b99bd288adca5f59e0c9609f15a221220241c5feffffff01a1c69a3b000000001976a914e54445646929fac8b7d6c71715913af44324978488ac00000000b5010061d48e8bd82f0fe9dba3413753c90b695ae75dccee5b3401e76df29b9d33a13900000000000000000000ffffc6c74af14e1f000ccf39b295938f4021fc16b23e4eedbd64a6c9a5aaae6bc9ca085db4bc29ff911612dc4544fc470415cdd4be5659fee6aa7d84c3572c712f5904b2e5d38cb217762717d88e4e1a956a9e82ce132e2b9b144e26cb3ff1e53675ede2769d99f46796ea0b8cbf33b78c15fe9a437dc4d1131ce2af8fd2ed5b306b326f9fcffcd416"), SER_DISK, CLIENT_VERSION); + CTransaction proupservtx(deserialize, stream); + // ProUpRegTx from testnet (txid: 3037bf4e45d1a9d57891ea8baa72b38ad6323bd28945b11325bdd33ebea049b3) + stream = CDataStream(ParseHex("0300030001396d18dd6812b502bb6d230a124a7c79bbe8061b314cb37d204f7854a027960c000000006a47304402203aefa0e1a08469050edc1e5da6e24f783162bebc15cc006f717097e56e07fa0b02207c7f47d113355b095c7edd2408d0554aa6b87187bf95652c516fc7747720fb19012102a8d6433a8f799a13d3495f4aa5b99bd288adca5f59e0c9609f15a221220241c5feffffff01f2c49a3b000000001976a914e54445646929fac8b7d6c71715913af44324978488ac00000000e4010061d48e8bd82f0fe9dba3413753c90b695ae75dccee5b3401e76df29b9d33a13900000efda51589f86e30cc2305e7388c01ce0309c19a182cf37bced97c7da72236f660c0a395e765e6e06962ecff5a69d7de359c348a574176c210c37a25d4ffd917866fb0a31976a914e54445646929fac8b7d6c71715913af44324978488aca8141db635af714f32c7415b298937e171c6bb8a3c8627852ae2b69917ece86a411fa51861025bb5ff4ba4ad3be6090c4ba76b1671d70799ed6882c57bcfeaf27cbd558068e9b5fc1553abce8822fcee63e8f6fbb06ad5b753d47e794bfacadbde3f"), SER_DISK, CLIENT_VERSION); + CTransaction proupregtx(deserialize, stream); + // ProUpRevTx from testnet (txid: 70c41a5fea07a80c1297771c43eb58b8f52222b971f61f5c7c2030ec0baf7f9a) + stream = CDataStream(ParseHex("0300040001b349a0be3ed3bd2513b14589d23b32d68ab372aa8bea9178d5a9d1454ebf3730000000006a4730440220428d87daf3b0ddca9656d59592000f9930a88162c0b017ca460c69c33b76bda202200276a521d94acc57e010262aa1d7d016cebbe7a9806d26dbebb69102ce9cf7b2012102a8d6433a8f799a13d3495f4aa5b99bd288adca5f59e0c9609f15a221220241c5feffffff0183c39a3b000000001976a914e54445646929fac8b7d6c71715913af44324978488ac00000000a4010061d48e8bd82f0fe9dba3413753c90b695ae75dccee5b3401e76df29b9d33a1390000c024ffd34d15686f7a376ecdf45800ddcebf4eb966fd3123cf9450515a38881a0de852c97297bbd9cedb79280f3f1e31b092904a8870274a5a0e52de2ae1dc0bad74851dc0a1e4fe4c274535a2422d3e1510a7be2fbecaae73128e8eb2f336382fb376d2f82273a72960980972b02e3ecf895c00187e0cbe735dc44cb3d97711"), SER_DISK, CLIENT_VERSION); + CTransaction prouprevtx(deserialize, stream); + + // if ProRegTx matches, all related pro txes match too + CBloomFilter filter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(COutPoint(uint256S("0x23464abc2f724de235e69e72ef5068f1b2701521b88e7b2740b93978ff54909b"), 1)); + BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(proupservtx), "Bloom filter matches without update"); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(proregtx), "Bloom filter didn't match collateral outpoint in ProRegTx"); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(proupservtx), "Bloom filter wasn't updated with proregtx hash"); + + // if ProUpRegTx matches, all related pro txes match too + filter = CBloomFilter(10, 0.000001, 0, BLOOM_UPDATE_ALL); + filter.insert(ParseHex("359c348a574176c210c37a25d4ffd917866fb0a3")); + BOOST_CHECK_MESSAGE(!filter.IsRelevantAndUpdate(proupservtx), "Bloom filter matches without update"); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(proupregtx), "Bloom filter didn't match Voting keyid in ProUpRegTx"); + BOOST_CHECK_MESSAGE(filter.IsRelevantAndUpdate(proupservtx), "Bloom filter wasn't updated with proregtx hash"); +} + BOOST_AUTO_TEST_CASE(merkle_block_1) { // Random real block (0000000000013b8ab2cd513b0261a14096412195a72a0c4827d229dcc7e0f7af)