Line data Source code
1 : // Copyright (c) 2021-2022 The PIVX Core developers
2 : // Distributed under the MIT software license, see the accompanying
3 : // file COPYING or https://www.opensource.org/licenses/mit-license.php.
4 :
5 : #include "test/test_pivx.h"
6 :
7 : #include "test/data/specialtx_invalid.json.h"
8 : #include "test/data/specialtx_valid.json.h"
9 :
10 : #include "consensus/validation.h"
11 : #include "core_io.h"
12 : #include "evo/providertx.h"
13 : #include "evo/specialtx_validation.h"
14 : #include "llmq/quorums_commitment.h"
15 : #include "messagesigner.h"
16 : #include "netbase.h"
17 : #include "primitives/transaction.h"
18 :
19 : #include <boost/test/unit_test.hpp>
20 :
21 : extern UniValue read_json(const std::string& jsondata);
22 :
23 : BOOST_FIXTURE_TEST_SUITE(evo_specialtx_tests, TestingSetup)
24 :
25 18 : static CKey GetRandomKey()
26 : {
27 18 : CKey key;
28 18 : key.MakeNewKey(true);
29 18 : return key;
30 : }
31 :
32 17 : static CKeyID GetRandomKeyID()
33 : {
34 34 : return GetRandomKey().GetPubKey().GetID();
35 : }
36 :
37 4 : static CBLSPublicKey GetRandomBLSKey()
38 : {
39 4 : CBLSSecretKey sk;
40 4 : sk.MakeNewKey();
41 8 : return sk.GetPublicKey();
42 : }
43 :
44 9 : static CScript GetRandomScript()
45 : {
46 18 : return GetScriptForDestination(GetRandomKeyID());
47 : }
48 :
49 3 : static ProRegPL GetRandomProRegPayload()
50 : {
51 3 : ProRegPL pl;
52 3 : pl.collateralOutpoint.hash = GetRandHash();
53 3 : pl.collateralOutpoint.n = InsecureRandBits(2);
54 9 : BOOST_CHECK(Lookup("57.12.210.11:51472", pl.addr, Params().GetDefaultPort(), false));
55 3 : pl.keyIDOwner = GetRandomKeyID();
56 3 : pl.pubKeyOperator = GetRandomBLSKey();
57 3 : pl.keyIDVoting = GetRandomKeyID();
58 3 : pl.scriptPayout = GetRandomScript();
59 3 : pl.nOperatorReward = InsecureRandRange(10000);
60 3 : pl.scriptOperatorPayout = GetRandomScript();
61 3 : pl.inputsHash = GetRandHash();
62 3 : pl.vchSig = InsecureRandBytes(63);
63 3 : return pl;
64 : }
65 :
66 1 : static ProUpServPL GetRandomProUpServPayload()
67 : {
68 1 : ProUpServPL pl;
69 1 : pl.proTxHash = GetRandHash();
70 2 : BOOST_CHECK(Lookup("127.0.0.1:51472", pl.addr, Params().GetDefaultPort(), false));
71 1 : pl.scriptOperatorPayout = GetRandomScript();
72 1 : pl.inputsHash = GetRandHash();
73 1 : pl.sig.SetByteVector(InsecureRandBytes(BLS_CURVE_SIG_SIZE));
74 1 : return pl;
75 : }
76 :
77 1 : static ProUpRegPL GetRandomProUpRegPayload()
78 : {
79 1 : ProUpRegPL pl;
80 1 : pl.proTxHash = GetRandHash();
81 1 : pl.pubKeyOperator = GetRandomBLSKey();
82 1 : pl.keyIDVoting = GetRandomKeyID();
83 1 : pl.scriptPayout = GetRandomScript();
84 1 : pl.inputsHash = GetRandHash();
85 1 : pl.vchSig = InsecureRandBytes(63);
86 1 : return pl;
87 : }
88 :
89 1 : static ProUpRevPL GetRandomProUpRevPayload()
90 : {
91 1 : ProUpRevPL pl;
92 1 : pl.proTxHash = GetRandHash();
93 1 : pl.nReason = InsecureRand16();
94 1 : pl.inputsHash = GetRandHash();
95 1 : pl.sig.SetByteVector(InsecureRandBytes(BLS_CURVE_SIG_SIZE));
96 1 : return pl;
97 : }
98 :
99 1 : llmq::CFinalCommitment GetRandomLLMQCommitment()
100 : {
101 1 : llmq::CFinalCommitment fc;
102 1 : fc.nVersion = InsecureRand16();
103 1 : fc.llmqType = InsecureRandBits(8);
104 1 : fc.quorumHash = GetRandHash();
105 1 : int vecsize = InsecureRandRange(500);
106 63 : for (int i = 0; i < vecsize; i++) {
107 62 : fc.signers.emplace_back((bool)InsecureRandBits(1));
108 124 : fc.validMembers.emplace_back((bool)InsecureRandBits(1));
109 : }
110 1 : fc.quorumPublicKey.SetByteVector(InsecureRandBytes(BLS_CURVE_PUBKEY_SIZE));
111 1 : fc.quorumVvecHash = GetRandHash();
112 1 : fc.quorumSig.SetByteVector(InsecureRandBytes(BLS_CURVE_SIG_SIZE));
113 1 : fc.membersSig.SetByteVector(InsecureRandBytes(BLS_CURVE_SIG_SIZE));
114 1 : return fc;
115 : }
116 :
117 1 : static llmq::LLMQCommPL GetRandomLLMQCommPayload()
118 : {
119 1 : llmq::LLMQCommPL pl;
120 1 : pl.nHeight = InsecureRand32();
121 1 : pl.commitment = GetRandomLLMQCommitment();
122 1 : return pl;
123 : }
124 :
125 1 : static bool EqualCommitments(const llmq::CFinalCommitment& a, const llmq::CFinalCommitment& b)
126 : {
127 1 : return a.nVersion == b.nVersion &&
128 1 : a.llmqType == b.llmqType &&
129 2 : a.quorumHash == b.quorumHash &&
130 1 : a.signers == b.signers &&
131 1 : a.quorumPublicKey == b.quorumPublicKey &&
132 1 : a.quorumVvecHash == b.quorumVvecHash &&
133 2 : a.quorumSig == b.quorumSig &&
134 1 : a.membersSig == b.membersSig;
135 : }
136 :
137 : template <typename T>
138 59 : static void TrivialCheckSpecialTx(const CMutableTransaction& mtx, const bool shouldFail, const std::string& rejectReason)
139 : {
140 105 : T pl;
141 59 : CValidationState state;
142 59 : GetTxPayload(mtx, pl);
143 118 : BOOST_CHECK(pl.IsTriviallyValid(state) == !shouldFail);
144 59 : if (shouldFail) {
145 33 : BOOST_CHECK(state.GetRejectReason() == rejectReason);
146 : }
147 59 : }
148 :
149 2 : static void SpecialTxTrivialValidator(const UniValue& tests)
150 : {
151 61 : for (size_t i = 1; i < tests.size(); i++) {
152 59 : const auto& test = tests[i];
153 :
154 59 : uint256 txHash;
155 118 : std::string txType;
156 118 : CMutableTransaction mtx;
157 118 : std::string rejectReason = "";
158 59 : try {
159 59 : txHash = uint256S(test[0].get_str());
160 :
161 59 : txType = test[1].get_str();
162 177 : CDataStream stream(ParseHex(test[2].get_str()), SER_NETWORK, PROTOCOL_VERSION);
163 59 : stream >> mtx;
164 :
165 59 : bool shouldFail = test.size() > 3;
166 59 : if (shouldFail) {
167 9 : rejectReason = test[3].get_str();
168 : }
169 118 : BOOST_CHECK(mtx.GetHash() == txHash);
170 :
171 59 : switch (mtx.nType) {
172 18 : case CTransaction::TxType::PROREG:
173 36 : BOOST_CHECK(txType == "proreg");
174 18 : TrivialCheckSpecialTx<ProRegPL>(mtx, shouldFail, rejectReason);
175 : break;
176 17 : case CTransaction::TxType::PROUPSERV:
177 34 : BOOST_CHECK(txType == "proupserv");
178 17 : TrivialCheckSpecialTx<ProUpServPL>(mtx, shouldFail, rejectReason);
179 : break;
180 11 : case CTransaction::TxType::PROUPREG:
181 22 : BOOST_CHECK(txType == "proupreg");
182 11 : TrivialCheckSpecialTx<ProUpRegPL>(mtx, shouldFail, rejectReason);
183 : break;
184 13 : case CTransaction::TxType::PROUPREV:
185 26 : BOOST_CHECK(txType == "prouprev");
186 13 : TrivialCheckSpecialTx<ProUpRevPL>(mtx, shouldFail, rejectReason);
187 : break;
188 0 : default:
189 0 : BOOST_CHECK(false);
190 : }
191 0 : } catch (...) {
192 0 : std::string strTest = test.write();
193 0 : BOOST_ERROR("Bad test, couldn't deserialize data: " << strTest);
194 0 : continue;
195 : }
196 : }
197 2 : }
198 :
199 2 : BOOST_AUTO_TEST_CASE(protx_validation_test)
200 : {
201 1 : LOCK(cs_main);
202 :
203 2 : CMutableTransaction mtx;
204 2 : CValidationState state;
205 :
206 : // v1 can only be Type=0
207 1 : mtx.nType = CTransaction::TxType::PROREG;
208 1 : mtx.nVersion = CTransaction::TxVersion::LEGACY;
209 2 : BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
210 3 : BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-type-version");
211 :
212 : // version >= Sapling, type = 0, payload != null.
213 1 : mtx.nType = CTransaction::TxType::NORMAL;
214 2 : mtx.extraPayload = std::vector<uint8_t>(10, 1);
215 1 : mtx.nVersion = CTransaction::TxVersion::SAPLING;
216 2 : BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
217 3 : BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-type-payload");
218 :
219 : // version >= Sapling, type = 0, payload == null --> pass
220 1 : mtx.extraPayload = nullopt;
221 2 : BOOST_CHECK(CheckSpecialTxNoContext(CTransaction(mtx), state));
222 :
223 : // nVersion>=2 and nType!=0 without extrapayload
224 1 : mtx.nType = CTransaction::TxType::PROREG;
225 2 : BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
226 4 : BOOST_CHECK(state.GetRejectReason().find("without extra payload"));
227 3 : BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-payload-empty");
228 :
229 : // Size limits
230 2 : mtx.extraPayload = std::vector<uint8_t>(MAX_SPECIALTX_EXTRAPAYLOAD + 1, 1);
231 2 : BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
232 3 : BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-txns-payload-oversize");
233 :
234 : // Remove one element, so now it passes the size check
235 1 : mtx.extraPayload->pop_back();
236 2 : BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
237 3 : BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-payload");
238 :
239 : // valid payload but invalid inputs hash
240 1 : mtx.extraPayload->clear();
241 2 : ProRegPL pl = GetRandomProRegPayload();
242 1 : SetTxPayload(mtx, pl);
243 2 : BOOST_CHECK(!CheckSpecialTxNoContext(CTransaction(mtx), state));
244 3 : BOOST_CHECK_EQUAL(state.GetRejectReason(), "bad-protx-inputs-hash");
245 :
246 : // all good.
247 1 : mtx.vin.emplace_back(GetRandHash(), 0);
248 1 : mtx.extraPayload->clear();
249 1 : pl.inputsHash = CalcTxInputsHash(CTransaction(mtx));
250 1 : SetTxPayload(mtx, pl);
251 2 : BOOST_CHECK(CheckSpecialTxNoContext(CTransaction(mtx), state));
252 1 : }
253 :
254 2 : BOOST_AUTO_TEST_CASE(proreg_setpayload_test)
255 : {
256 2 : const ProRegPL& pl = GetRandomProRegPayload();
257 :
258 2 : CMutableTransaction mtx;
259 1 : SetTxPayload(mtx, pl);
260 2 : ProRegPL pl2;
261 2 : BOOST_CHECK(GetTxPayload(mtx, pl2));
262 3 : BOOST_CHECK(pl.collateralOutpoint == pl2.collateralOutpoint);
263 2 : BOOST_CHECK(pl.addr == pl2.addr);
264 2 : BOOST_CHECK(pl.keyIDOwner == pl2.keyIDOwner);
265 3 : BOOST_CHECK(pl.pubKeyOperator == pl2.pubKeyOperator);
266 2 : BOOST_CHECK(pl.keyIDVoting == pl2.keyIDVoting);
267 2 : BOOST_CHECK(pl.scriptPayout == pl2.scriptPayout);
268 2 : BOOST_CHECK(pl.nOperatorReward == pl2.nOperatorReward);
269 2 : BOOST_CHECK(pl.scriptOperatorPayout == pl2.scriptOperatorPayout);
270 2 : BOOST_CHECK(pl.inputsHash == pl2.inputsHash);
271 2 : BOOST_CHECK(pl.vchSig == pl2.vchSig);
272 1 : }
273 :
274 2 : BOOST_AUTO_TEST_CASE(proupserv_setpayload_test)
275 : {
276 2 : const ProUpServPL& pl = GetRandomProUpServPayload();
277 :
278 2 : CMutableTransaction mtx;
279 1 : SetTxPayload(mtx, pl);
280 2 : ProUpServPL pl2;
281 2 : BOOST_CHECK(GetTxPayload(mtx, pl2));
282 2 : BOOST_CHECK(pl.proTxHash == pl2.proTxHash);
283 2 : BOOST_CHECK(pl.addr == pl2.addr);
284 2 : BOOST_CHECK(pl.scriptOperatorPayout == pl2.scriptOperatorPayout);
285 2 : BOOST_CHECK(pl.inputsHash == pl2.inputsHash);
286 3 : BOOST_CHECK(pl.sig == pl2.sig);
287 1 : }
288 :
289 2 : BOOST_AUTO_TEST_CASE(proupreg_setpayload_test)
290 : {
291 2 : const ProUpRegPL& pl = GetRandomProUpRegPayload();
292 :
293 2 : CMutableTransaction mtx;
294 1 : SetTxPayload(mtx, pl);
295 2 : ProUpRegPL pl2;
296 2 : BOOST_CHECK(GetTxPayload(mtx, pl2));
297 2 : BOOST_CHECK(pl.proTxHash == pl2.proTxHash);
298 3 : BOOST_CHECK(pl.pubKeyOperator == pl2.pubKeyOperator);
299 2 : BOOST_CHECK(pl.keyIDVoting == pl2.keyIDVoting);
300 2 : BOOST_CHECK(pl.scriptPayout == pl2.scriptPayout);
301 2 : BOOST_CHECK(pl.inputsHash == pl2.inputsHash);
302 2 : BOOST_CHECK(pl.vchSig == pl2.vchSig);
303 1 : }
304 :
305 2 : BOOST_AUTO_TEST_CASE(prouprev_setpayload_test)
306 : {
307 1 : const ProUpRevPL& pl = GetRandomProUpRevPayload();
308 :
309 2 : CMutableTransaction mtx;
310 1 : SetTxPayload(mtx, pl);
311 1 : ProUpRevPL pl2;
312 2 : BOOST_CHECK(GetTxPayload(mtx, pl2));
313 2 : BOOST_CHECK(pl.proTxHash == pl2.proTxHash);
314 2 : BOOST_CHECK(pl.nReason == pl2.nReason);
315 2 : BOOST_CHECK(pl.inputsHash == pl2.inputsHash);
316 3 : BOOST_CHECK(pl.sig == pl2.sig);
317 1 : }
318 :
319 2 : BOOST_AUTO_TEST_CASE(proreg_checkstringsig_test)
320 : {
321 1 : ProRegPL pl = GetRandomProRegPayload();
322 1 : pl.vchSig.clear();
323 2 : const CKey& key = GetRandomKey();
324 3 : BOOST_CHECK(CMessageSigner::SignMessage(pl.MakeSignString(), pl.vchSig, key));
325 :
326 2 : std::string strError;
327 1 : const CKeyID& keyID = key.GetPubKey().GetID();
328 3 : BOOST_CHECK(CMessageSigner::VerifyMessage(keyID, pl.vchSig, pl.MakeSignString(), strError));
329 : // Change owner address or script payout
330 1 : pl.keyIDOwner = GetRandomKeyID();
331 3 : BOOST_CHECK(!CMessageSigner::VerifyMessage(keyID, pl.vchSig, pl.MakeSignString(), strError));
332 1 : pl.scriptPayout = GetRandomScript();
333 3 : BOOST_CHECK(!CMessageSigner::VerifyMessage(keyID, pl.vchSig, pl.MakeSignString(), strError));
334 1 : }
335 :
336 2 : BOOST_AUTO_TEST_CASE(llmqcomm_setpayload_test)
337 : {
338 1 : const llmq::LLMQCommPL& pl = GetRandomLLMQCommPayload();
339 :
340 2 : CMutableTransaction mtx;
341 1 : SetTxPayload(mtx, pl);
342 2 : llmq::LLMQCommPL pl2;
343 2 : BOOST_CHECK(GetTxPayload(mtx, pl2));
344 2 : BOOST_CHECK(pl.nHeight == pl2.nHeight);
345 2 : BOOST_CHECK(EqualCommitments(pl.commitment, pl2.commitment));
346 1 : }
347 :
348 2 : BOOST_AUTO_TEST_CASE(specialtx_trivial_valid)
349 : {
350 3 : UniValue tests = read_json(std::string(json_tests::specialtx_valid, json_tests::specialtx_valid + sizeof(json_tests::specialtx_valid)));
351 1 : SpecialTxTrivialValidator(tests);
352 1 : }
353 :
354 2 : BOOST_AUTO_TEST_CASE(specialtx_trivial_invalid)
355 : {
356 3 : UniValue tests = read_json(std::string(json_tests::specialtx_invalid, json_tests::specialtx_invalid + sizeof(json_tests::specialtx_invalid)));
357 1 : SpecialTxTrivialValidator(tests);
358 1 : }
359 :
360 : BOOST_AUTO_TEST_SUITE_END()
|