blockfilter_tests.cpp raw
1 // Copyright (c) 2018-2022 The Limenka 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 <test/data/blockfilters.json.h>
6 #include <test/util/setup_common.h>
7
8 #include <addresstype.h>
9 #include <blockfilter.h>
10 #include <core_io.h>
11 #include <primitives/block.h>
12 #include <serialize.h>
13 #include <streams.h>
14 #include <undo.h>
15 #include <univalue.h>
16 #include <util/strencodings.h>
17
18 #include <boost/test/unit_test.hpp>
19
20 BOOST_AUTO_TEST_SUITE(blockfilter_tests)
21
22 BOOST_AUTO_TEST_CASE(gcsfilter_test)
23 {
24 GCSFilter::ElementSet included_elements, excluded_elements;
25 for (int i = 0; i < 100; ++i) {
26 GCSFilter::Element element1(32);
27 element1[0] = i;
28 included_elements.insert(std::move(element1));
29
30 GCSFilter::Element element2(32);
31 element2[1] = i;
32 excluded_elements.insert(std::move(element2));
33 }
34
35 GCSFilter filter({0, 0, 10, 1 << 10}, included_elements);
36 for (const auto& element : included_elements) {
37 BOOST_CHECK(filter.Match(element));
38
39 auto insertion = excluded_elements.insert(element);
40 BOOST_CHECK(filter.MatchAny(excluded_elements));
41 excluded_elements.erase(insertion.first);
42 }
43 }
44
45 BOOST_AUTO_TEST_CASE(gcsfilter_default_constructor)
46 {
47 GCSFilter filter;
48 BOOST_CHECK_EQUAL(filter.GetN(), 0U);
49 BOOST_CHECK_EQUAL(filter.GetEncoded().size(), 1U);
50
51 const GCSFilter::Params& params = filter.GetParams();
52 BOOST_CHECK_EQUAL(params.m_siphash_k0, 0U);
53 BOOST_CHECK_EQUAL(params.m_siphash_k1, 0U);
54 BOOST_CHECK_EQUAL(params.m_P, 0);
55 BOOST_CHECK_EQUAL(params.m_M, 1U);
56 }
57
58 BOOST_AUTO_TEST_CASE(blockfilter_basic_test)
59 {
60 CScript included_scripts[5], excluded_scripts[4];
61
62 // First two are outputs on a single transaction.
63 included_scripts[0] << std::vector<unsigned char>(0, 65) << OP_CHECKSIG;
64 included_scripts[1] << OP_DUP << OP_HASH160 << std::vector<unsigned char>(1, 20) << OP_EQUALVERIFY << OP_CHECKSIG;
65
66 // Third is an output on in a second transaction.
67 included_scripts[2] << OP_1 << std::vector<unsigned char>(2, 33) << OP_1 << OP_CHECKMULTISIG;
68
69 // Last two are spent by a single transaction.
70 included_scripts[3] << OP_0 << std::vector<unsigned char>(3, 32);
71 included_scripts[4] << OP_4 << OP_ADD << OP_8 << OP_EQUAL;
72
73 // OP_RETURN output is an output on the second transaction.
74 excluded_scripts[0] << OP_RETURN << std::vector<unsigned char>(4, 40);
75
76 // This script is not related to the block at all.
77 excluded_scripts[1] << std::vector<unsigned char>(5, 33) << OP_CHECKSIG;
78
79 // OP_RETURN is non-standard since it's not followed by a data push, but is still excluded from
80 // filter.
81 excluded_scripts[2] << OP_RETURN << OP_4 << OP_ADD << OP_8 << OP_EQUAL;
82
83 CMutableTransaction tx_1;
84 tx_1.vout.emplace_back(100, included_scripts[0]);
85 tx_1.vout.emplace_back(200, included_scripts[1]);
86 tx_1.vout.emplace_back(0, excluded_scripts[0]);
87
88 CMutableTransaction tx_2;
89 tx_2.vout.emplace_back(300, included_scripts[2]);
90 tx_2.vout.emplace_back(0, excluded_scripts[2]);
91 tx_2.vout.emplace_back(400, excluded_scripts[3]); // Script is empty
92
93 CBlock block;
94 block.vtx.push_back(MakeTransactionRef(tx_1));
95 block.vtx.push_back(MakeTransactionRef(tx_2));
96
97 CBlockUndo block_undo;
98 block_undo.vtxundo.emplace_back();
99 block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(500, included_scripts[3]), 1000, true);
100 block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(600, included_scripts[4]), 10000, false);
101 block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(700, excluded_scripts[3]), 100000, false);
102
103 BlockFilter block_filter(BlockFilterType::BASIC, block, block_undo);
104 const GCSFilter& filter = block_filter.GetFilter();
105
106 for (const CScript& script : included_scripts) {
107 BOOST_CHECK(filter.Match(GCSFilter::Element(script.begin(), script.end())));
108 }
109 for (const CScript& script : excluded_scripts) {
110 BOOST_CHECK(!filter.Match(GCSFilter::Element(script.begin(), script.end())));
111 }
112
113 // Test serialization/unserialization.
114 BlockFilter block_filter2;
115
116 DataStream stream{};
117 stream << block_filter;
118 stream >> block_filter2;
119
120 BOOST_CHECK_EQUAL(block_filter.GetFilterType(), block_filter2.GetFilterType());
121 BOOST_CHECK_EQUAL(block_filter.GetBlockHash(), block_filter2.GetBlockHash());
122 BOOST_CHECK(block_filter.GetEncodedFilter() == block_filter2.GetEncodedFilter());
123
124 BlockFilter default_ctor_block_filter_1;
125 BlockFilter default_ctor_block_filter_2;
126 BOOST_CHECK_EQUAL(default_ctor_block_filter_1.GetFilterType(), default_ctor_block_filter_2.GetFilterType());
127 BOOST_CHECK_EQUAL(default_ctor_block_filter_1.GetBlockHash(), default_ctor_block_filter_2.GetBlockHash());
128 BOOST_CHECK(default_ctor_block_filter_1.GetEncodedFilter() == default_ctor_block_filter_2.GetEncodedFilter());
129 }
130
131 BOOST_AUTO_TEST_CASE(blockfilter_v0_test)
132 {
133 CScript included_scripts[4], excluded_scripts[8];
134
135 included_scripts[0] = GetScriptForDestination(WitnessV0KeyHash()); // p2wpkh
136 included_scripts[1] = GetScriptForDestination(WitnessV0KeyHash()); // p2wpkh
137 included_scripts[2] = GetScriptForDestination(WitnessV0ScriptHash()); // p2wsh
138 included_scripts[3] = GetScriptForDestination(WitnessV0ScriptHash()); // p2wsh
139
140 excluded_scripts[0] << std::vector<unsigned char>(0, 65) << OP_CHECKSIG; // p2pk
141 excluded_scripts[1] << OP_0 << OP_HASH160 << std::vector<unsigned char>(1, 20) << OP_EQUALVERIFY << OP_CHECKSIG; // p2pkh
142 excluded_scripts[2] << OP_1 << std::vector<unsigned char>(2, 33) << OP_1 << OP_CHECKMULTISIG; // multisig
143 excluded_scripts[3] << OP_0 << std::vector<unsigned char>(3, 32); // push data
144 excluded_scripts[4] << OP_4 << OP_ADD << OP_8 << OP_EQUAL; // random script
145 excluded_scripts[5] << OP_RETURN << std::vector<unsigned char>(4, 40); // opreturn
146 excluded_scripts[6] << OP_RETURN << OP_4 << OP_ADD << OP_8 << OP_EQUAL; // none standard opreturn
147
148 CMutableTransaction tx_1;
149 tx_1.vout.emplace_back(100, included_scripts[0]);
150 tx_1.vout.emplace_back(100, included_scripts[2]);
151 tx_1.vout.emplace_back(200, excluded_scripts[0]);
152 tx_1.vout.emplace_back(300, excluded_scripts[1]);
153 tx_1.vout.emplace_back(400, excluded_scripts[2]);
154
155 CMutableTransaction tx_2;
156 tx_2.vout.emplace_back(100, included_scripts[3]);
157 tx_2.vout.emplace_back(100, excluded_scripts[3]);
158 tx_2.vout.emplace_back(0, excluded_scripts[4]);
159 tx_2.vout.emplace_back(400, excluded_scripts[7]); // Script is empty
160
161 CBlock block;
162 block.vtx.push_back(MakeTransactionRef(tx_1));
163 block.vtx.push_back(MakeTransactionRef(tx_2));
164
165 CBlockUndo block_undo;
166 block_undo.vtxundo.emplace_back();
167 block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(500, included_scripts[1]), 1000, true);
168 block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(600, excluded_scripts[5]), 10000, false);
169 block_undo.vtxundo.back().vprevout.emplace_back(CTxOut(700, excluded_scripts[6]), 100000, false);
170
171 BlockFilter block_filter(BlockFilterType::V0, block, block_undo);
172 const GCSFilter& filter = block_filter.GetFilter();
173
174 for (const CScript& script : included_scripts) {
175 BOOST_CHECK(filter.Match(GCSFilter::Element(script.begin(), script.end())));
176 }
177 for (const CScript& script : excluded_scripts) {
178 BOOST_CHECK(!filter.Match(GCSFilter::Element(script.begin(), script.end())));
179 }
180
181 // Test serialization/unserialization.
182 BlockFilter block_filter2;
183
184 DataStream stream;
185 stream << block_filter;
186 stream >> block_filter2;
187
188 BOOST_CHECK_EQUAL(block_filter.GetFilterType(), block_filter2.GetFilterType());
189 BOOST_CHECK_EQUAL(block_filter.GetBlockHash(), block_filter2.GetBlockHash());
190 BOOST_CHECK(block_filter.GetEncodedFilter() == block_filter2.GetEncodedFilter());
191
192 BlockFilter default_ctor_block_filter_1;
193 BlockFilter default_ctor_block_filter_2;
194 BOOST_CHECK_EQUAL(default_ctor_block_filter_1.GetFilterType(), default_ctor_block_filter_2.GetFilterType());
195 BOOST_CHECK_EQUAL(default_ctor_block_filter_1.GetBlockHash(), default_ctor_block_filter_2.GetBlockHash());
196 BOOST_CHECK(default_ctor_block_filter_1.GetEncodedFilter() == default_ctor_block_filter_2.GetEncodedFilter());
197 }
198
199 BOOST_AUTO_TEST_CASE(blockfilters_json_test)
200 {
201 UniValue json;
202 if (!json.read(json_tests::blockfilters) || !json.isArray()) {
203 BOOST_ERROR("Parse error.");
204 return;
205 }
206
207 const UniValue& tests = json.get_array();
208 for (unsigned int i = 0; i < tests.size(); i++) {
209 const UniValue& test = tests[i];
210 std::string strTest = test.write();
211
212 if (test.size() == 1) {
213 continue;
214 } else if (test.size() < 7) {
215 BOOST_ERROR("Bad test: " << strTest);
216 continue;
217 }
218
219 unsigned int pos = 0;
220 /*int block_height =*/ test[pos++].getInt<int>();
221 BOOST_CHECK(uint256::FromHex(test[pos++].get_str()));
222
223 CBlock block;
224 BOOST_REQUIRE(DecodeHexBlk(block, test[pos++].get_str()));
225
226 CBlockUndo block_undo;
227 block_undo.vtxundo.emplace_back();
228 CTxUndo& tx_undo = block_undo.vtxundo.back();
229 const UniValue& prev_scripts = test[pos++].get_array();
230 for (unsigned int ii = 0; ii < prev_scripts.size(); ii++) {
231 std::vector<unsigned char> raw_script = ParseHex(prev_scripts[ii].get_str());
232 CTxOut txout(0, CScript(raw_script.begin(), raw_script.end()));
233 tx_undo.vprevout.emplace_back(txout, 0, false);
234 }
235
236 uint256 prev_filter_header_basic{*Assert(uint256::FromHex(test[pos++].get_str()))};
237 std::vector<unsigned char> filter_basic = ParseHex(test[pos++].get_str());
238 uint256 filter_header_basic{*Assert(uint256::FromHex(test[pos++].get_str()))};
239
240 BlockFilter computed_filter_basic(BlockFilterType::BASIC, block, block_undo);
241 BOOST_CHECK(computed_filter_basic.GetFilter().GetEncoded() == filter_basic);
242
243 uint256 computed_header_basic = computed_filter_basic.ComputeHeader(prev_filter_header_basic);
244 BOOST_CHECK(computed_header_basic == filter_header_basic);
245 }
246 }
247
248 BOOST_AUTO_TEST_CASE(blockfilter_type_names)
249 {
250 BOOST_CHECK_EQUAL(BlockFilterTypeName(BlockFilterType::BASIC), "basic");
251 BOOST_CHECK_EQUAL(BlockFilterTypeName(BlockFilterType::V0), "v0");
252 BOOST_CHECK_EQUAL(BlockFilterTypeName(static_cast<BlockFilterType>(255)), "");
253
254 BlockFilterType filter_type;
255 BOOST_CHECK(BlockFilterTypeByName("basic", filter_type));
256 BOOST_CHECK_EQUAL(filter_type, BlockFilterType::BASIC);
257
258 BlockFilterType filter_type_v0;
259 BOOST_CHECK(BlockFilterTypeByName("v0", filter_type_v0));
260 BOOST_CHECK_EQUAL(filter_type_v0, BlockFilterType::V0);
261
262 BOOST_CHECK(!BlockFilterTypeByName("unknown", filter_type));
263 }
264
265 BOOST_AUTO_TEST_SUITE_END()
266