]>
Commit | Line | Data |
---|---|---|
cbd22a50 | 1 | // Copyright (c) 2009-2010 Satoshi Nakamoto |
f914f1a7 | 2 | // Copyright (c) 2009-2014 The Bitcoin Core developers |
2d79bba3 | 3 | // Distributed under the MIT software license, see the accompanying |
bc909a7a | 4 | // file COPYING or https://www.opensource.org/licenses/mit-license.php . |
cbd22a50 | 5 | |
6 | #include "script.h" | |
7 | ||
85c579e3 CF |
8 | #include "tinyformat.h" |
9 | #include "utilstrencodings.h" | |
8a8e10f0 | 10 | #include "script/cc.h" |
0cb91a8d | 11 | #include "cc/eval.h" |
2c8d8268 | 12 | #include "cryptoconditions/include/cryptoconditions.h" |
68b309c0 | 13 | #include "standard.h" |
989b1de1 | 14 | #include "pbaas/reserves.h" |
7a912018 | 15 | #include "key_io.h" |
b826c625 | 16 | #include "univalue.h" |
757e67a5 | 17 | #include "pbaas/identity.h" |
cbd22a50 | 18 | |
19 | using namespace std; | |
0cb91a8d | 20 | |
db8eb54b | 21 | namespace { |
9feb4b9e | 22 | inline std::string ValueString(const std::vector<unsigned char>& vch) |
23 | { | |
24 | if (vch.size() <= 4) | |
25 | return strprintf("%d", CScriptNum(vch, false).getint()); | |
26 | else | |
27 | return HexStr(vch); | |
28 | } | |
db8eb54b CF |
29 | } // anon namespace |
30 | ||
cbd22a50 | 31 | const char* GetOpName(opcodetype opcode) |
32 | { | |
33 | switch (opcode) | |
34 | { | |
35 | // push value | |
36 | case OP_0 : return "0"; | |
37 | case OP_PUSHDATA1 : return "OP_PUSHDATA1"; | |
38 | case OP_PUSHDATA2 : return "OP_PUSHDATA2"; | |
39 | case OP_PUSHDATA4 : return "OP_PUSHDATA4"; | |
40 | case OP_1NEGATE : return "-1"; | |
41 | case OP_RESERVED : return "OP_RESERVED"; | |
42 | case OP_1 : return "1"; | |
43 | case OP_2 : return "2"; | |
44 | case OP_3 : return "3"; | |
45 | case OP_4 : return "4"; | |
46 | case OP_5 : return "5"; | |
47 | case OP_6 : return "6"; | |
48 | case OP_7 : return "7"; | |
49 | case OP_8 : return "8"; | |
50 | case OP_9 : return "9"; | |
51 | case OP_10 : return "10"; | |
52 | case OP_11 : return "11"; | |
53 | case OP_12 : return "12"; | |
54 | case OP_13 : return "13"; | |
55 | case OP_14 : return "14"; | |
56 | case OP_15 : return "15"; | |
57 | case OP_16 : return "16"; | |
58 | ||
59 | // control | |
60 | case OP_NOP : return "OP_NOP"; | |
61 | case OP_VER : return "OP_VER"; | |
62 | case OP_IF : return "OP_IF"; | |
63 | case OP_NOTIF : return "OP_NOTIF"; | |
64 | case OP_VERIF : return "OP_VERIF"; | |
65 | case OP_VERNOTIF : return "OP_VERNOTIF"; | |
66 | case OP_ELSE : return "OP_ELSE"; | |
67 | case OP_ENDIF : return "OP_ENDIF"; | |
68 | case OP_VERIFY : return "OP_VERIFY"; | |
69 | case OP_RETURN : return "OP_RETURN"; | |
70 | ||
71 | // stack ops | |
72 | case OP_TOALTSTACK : return "OP_TOALTSTACK"; | |
73 | case OP_FROMALTSTACK : return "OP_FROMALTSTACK"; | |
74 | case OP_2DROP : return "OP_2DROP"; | |
75 | case OP_2DUP : return "OP_2DUP"; | |
76 | case OP_3DUP : return "OP_3DUP"; | |
77 | case OP_2OVER : return "OP_2OVER"; | |
78 | case OP_2ROT : return "OP_2ROT"; | |
79 | case OP_2SWAP : return "OP_2SWAP"; | |
80 | case OP_IFDUP : return "OP_IFDUP"; | |
81 | case OP_DEPTH : return "OP_DEPTH"; | |
82 | case OP_DROP : return "OP_DROP"; | |
83 | case OP_DUP : return "OP_DUP"; | |
84 | case OP_NIP : return "OP_NIP"; | |
85 | case OP_OVER : return "OP_OVER"; | |
86 | case OP_PICK : return "OP_PICK"; | |
87 | case OP_ROLL : return "OP_ROLL"; | |
88 | case OP_ROT : return "OP_ROT"; | |
89 | case OP_SWAP : return "OP_SWAP"; | |
90 | case OP_TUCK : return "OP_TUCK"; | |
91 | ||
92 | // splice ops | |
93 | case OP_CAT : return "OP_CAT"; | |
94 | case OP_SUBSTR : return "OP_SUBSTR"; | |
95 | case OP_LEFT : return "OP_LEFT"; | |
96 | case OP_RIGHT : return "OP_RIGHT"; | |
97 | case OP_SIZE : return "OP_SIZE"; | |
98 | ||
99 | // bit logic | |
100 | case OP_INVERT : return "OP_INVERT"; | |
101 | case OP_AND : return "OP_AND"; | |
102 | case OP_OR : return "OP_OR"; | |
103 | case OP_XOR : return "OP_XOR"; | |
104 | case OP_EQUAL : return "OP_EQUAL"; | |
105 | case OP_EQUALVERIFY : return "OP_EQUALVERIFY"; | |
106 | case OP_RESERVED1 : return "OP_RESERVED1"; | |
107 | case OP_RESERVED2 : return "OP_RESERVED2"; | |
108 | ||
109 | // numeric | |
110 | case OP_1ADD : return "OP_1ADD"; | |
111 | case OP_1SUB : return "OP_1SUB"; | |
112 | case OP_2MUL : return "OP_2MUL"; | |
113 | case OP_2DIV : return "OP_2DIV"; | |
114 | case OP_NEGATE : return "OP_NEGATE"; | |
115 | case OP_ABS : return "OP_ABS"; | |
116 | case OP_NOT : return "OP_NOT"; | |
117 | case OP_0NOTEQUAL : return "OP_0NOTEQUAL"; | |
118 | case OP_ADD : return "OP_ADD"; | |
119 | case OP_SUB : return "OP_SUB"; | |
120 | case OP_MUL : return "OP_MUL"; | |
121 | case OP_DIV : return "OP_DIV"; | |
122 | case OP_MOD : return "OP_MOD"; | |
123 | case OP_LSHIFT : return "OP_LSHIFT"; | |
124 | case OP_RSHIFT : return "OP_RSHIFT"; | |
125 | case OP_BOOLAND : return "OP_BOOLAND"; | |
126 | case OP_BOOLOR : return "OP_BOOLOR"; | |
127 | case OP_NUMEQUAL : return "OP_NUMEQUAL"; | |
128 | case OP_NUMEQUALVERIFY : return "OP_NUMEQUALVERIFY"; | |
129 | case OP_NUMNOTEQUAL : return "OP_NUMNOTEQUAL"; | |
130 | case OP_LESSTHAN : return "OP_LESSTHAN"; | |
131 | case OP_GREATERTHAN : return "OP_GREATERTHAN"; | |
132 | case OP_LESSTHANOREQUAL : return "OP_LESSTHANOREQUAL"; | |
133 | case OP_GREATERTHANOREQUAL : return "OP_GREATERTHANOREQUAL"; | |
134 | case OP_MIN : return "OP_MIN"; | |
135 | case OP_MAX : return "OP_MAX"; | |
136 | case OP_WITHIN : return "OP_WITHIN"; | |
137 | ||
138 | // crypto | |
139 | case OP_RIPEMD160 : return "OP_RIPEMD160"; | |
140 | case OP_SHA1 : return "OP_SHA1"; | |
141 | case OP_SHA256 : return "OP_SHA256"; | |
142 | case OP_HASH160 : return "OP_HASH160"; | |
143 | case OP_HASH256 : return "OP_HASH256"; | |
144 | case OP_CODESEPARATOR : return "OP_CODESEPARATOR"; | |
145 | case OP_CHECKSIG : return "OP_CHECKSIG"; | |
146 | case OP_CHECKSIGVERIFY : return "OP_CHECKSIGVERIFY"; | |
147 | case OP_CHECKMULTISIG : return "OP_CHECKMULTISIG"; | |
148 | case OP_CHECKMULTISIGVERIFY : return "OP_CHECKMULTISIGVERIFY"; | |
07f83521 SS |
149 | case OP_CHECKCRYPTOCONDITION : return "OP_CHECKCRYPTOCONDITION"; |
150 | case OP_CHECKCRYPTOCONDITIONVERIFY | |
151 | : return "OP_CHECKCRYPTOCONDITIONVERIFY"; | |
cbd22a50 | 152 | |
c938fb1f | 153 | // expansion |
cbd22a50 | 154 | case OP_NOP1 : return "OP_NOP1"; |
155 | case OP_NOP2 : return "OP_NOP2"; | |
156 | case OP_NOP3 : return "OP_NOP3"; | |
157 | case OP_NOP4 : return "OP_NOP4"; | |
158 | case OP_NOP5 : return "OP_NOP5"; | |
159 | case OP_NOP6 : return "OP_NOP6"; | |
160 | case OP_NOP7 : return "OP_NOP7"; | |
161 | case OP_NOP8 : return "OP_NOP8"; | |
162 | case OP_NOP9 : return "OP_NOP9"; | |
163 | case OP_NOP10 : return "OP_NOP10"; | |
164 | ||
165 | case OP_INVALIDOPCODE : return "OP_INVALIDOPCODE"; | |
166 | ||
167 | // Note: | |
168 | // The template matching params OP_SMALLDATA/etc are defined in opcodetype enum | |
169 | // as kind of implementation hack, they are *NOT* real opcodes. If found in real | |
170 | // Script, just let the default: case deal with them. | |
171 | ||
172 | default: | |
173 | return "OP_UNKNOWN"; | |
174 | } | |
175 | } | |
176 | ||
ef54c6e1 | 177 | uint160 GetConditionID(uint160 cid, int32_t condition) |
178 | { | |
179 | CHashWriter hw(SER_GETHASH, PROTOCOL_VERSION); | |
180 | hw << condition; | |
181 | hw << cid; | |
182 | uint256 chainHash = hw.GetHash(); | |
183 | return Hash160(chainHash.begin(), chainHash.end()); | |
184 | } | |
185 | ||
56fe75cb | 186 | CTxDestination TransferDestinationToDestination(const CTransferDestination &transferDest) |
187 | { | |
188 | CTxDestination retDest; | |
189 | switch (transferDest.type) | |
190 | { | |
191 | case CTransferDestination::DEST_PKH: | |
192 | retDest = CKeyID(uint160(transferDest.destination)); | |
193 | break; | |
194 | ||
195 | case CTransferDestination::DEST_PK: | |
196 | { | |
197 | CPubKey pk; | |
198 | pk.Set(transferDest.destination.begin(), transferDest.destination.end()); | |
199 | retDest = pk; | |
200 | break; | |
201 | } | |
202 | ||
203 | case CTransferDestination::DEST_SH: | |
204 | retDest = CScriptID(uint160(transferDest.destination)); | |
205 | break; | |
206 | ||
207 | case CTransferDestination::DEST_ID: | |
208 | retDest = CIdentityID(uint160(transferDest.destination)); | |
209 | break; | |
210 | ||
211 | case CTransferDestination::DEST_FULLID: | |
212 | retDest = CIdentityID(CIdentity(transferDest.destination).GetID()); | |
213 | break; | |
214 | ||
215 | case CTransferDestination::DEST_QUANTUM: | |
216 | retDest = CQuantumID(uint160(transferDest.destination)); | |
217 | break; | |
218 | } | |
219 | return retDest; | |
220 | } | |
221 | ||
222 | CTransferDestination DestinationToTransferDestination(const CTxDestination &dest) | |
223 | { | |
224 | CTransferDestination retDest; | |
225 | switch (dest.which()) | |
226 | { | |
227 | case CTransferDestination::DEST_PKH: | |
228 | case CTransferDestination::DEST_PK: | |
229 | case CTransferDestination::DEST_SH: | |
230 | case CTransferDestination::DEST_ID: | |
231 | case CTransferDestination::DEST_QUANTUM: | |
232 | retDest = CTransferDestination(dest.which(), GetDestinationBytes(dest)); | |
233 | break; | |
234 | } | |
235 | return retDest; | |
236 | } | |
237 | ||
2f416b17 | 238 | CTransferDestination IdentityToTransferDestination(const CIdentity &identity) |
239 | { | |
240 | return CTransferDestination(CTransferDestination::DEST_FULLID, ::AsVector(identity)); | |
241 | } | |
242 | ||
243 | CIdentity TransferDestinationToIdentity(const CTransferDestination &dest) | |
244 | { | |
245 | CIdentity retIdentity; | |
246 | switch (dest.type) | |
247 | { | |
248 | case CTransferDestination::DEST_FULLID: | |
249 | { | |
250 | ::FromVector(dest.destination, retIdentity); | |
251 | break; | |
252 | } | |
253 | } | |
254 | return retIdentity; | |
255 | } | |
256 | ||
56fe75cb | 257 | std::vector<CTxDestination> TransferDestinationsToDestinations(const std::vector<CTransferDestination> &transferDests) |
258 | { | |
259 | std::vector<CTxDestination> retDests; | |
260 | for (auto &dest : transferDests) | |
261 | { | |
262 | retDests.push_back(TransferDestinationToDestination(dest)); | |
263 | } | |
264 | return retDests; | |
265 | } | |
266 | ||
267 | std::vector<CTransferDestination> DestinationsToTransferDestinations(const std::vector<CTxDestination> &dests) | |
268 | { | |
269 | std::vector<CTransferDestination> retDests; | |
270 | for (auto &dest : dests) | |
271 | { | |
272 | retDests.push_back(DestinationToTransferDestination(dest)); | |
273 | } | |
274 | return retDests; | |
275 | } | |
276 | ||
277 | CStakeInfo::CStakeInfo(std::vector<unsigned char> vch) | |
278 | { | |
279 | ::FromVector(vch, *this); | |
280 | } | |
281 | ||
282 | std::vector<unsigned char> CStakeInfo::AsVector() const | |
283 | { | |
284 | return ::AsVector(*this); | |
285 | } | |
286 | ||
a4f9bc97 | 287 | unsigned int CScript::MAX_SCRIPT_ELEMENT_SIZE = MAX_SCRIPT_ELEMENT_SIZE_V2; |
288 | ||
cbd22a50 | 289 | unsigned int CScript::GetSigOpCount(bool fAccurate) const |
290 | { | |
291 | unsigned int n = 0; | |
292 | const_iterator pc = begin(); | |
293 | opcodetype lastOpcode = OP_INVALIDOPCODE; | |
294 | while (pc < end()) | |
295 | { | |
296 | opcodetype opcode; | |
297 | if (!GetOp(pc, opcode)) | |
298 | break; | |
299 | if (opcode == OP_CHECKSIG || opcode == OP_CHECKSIGVERIFY) | |
300 | n++; | |
301 | else if (opcode == OP_CHECKMULTISIG || opcode == OP_CHECKMULTISIGVERIFY) | |
302 | { | |
303 | if (fAccurate && lastOpcode >= OP_1 && lastOpcode <= OP_16) | |
304 | n += DecodeOP_N(lastOpcode); | |
305 | else | |
306 | n += 20; | |
307 | } | |
308 | lastOpcode = opcode; | |
309 | } | |
310 | return n; | |
311 | } | |
312 | ||
313 | unsigned int CScript::GetSigOpCount(const CScript& scriptSig) const | |
314 | { | |
315 | if (!IsPayToScriptHash()) | |
316 | return GetSigOpCount(true); | |
317 | ||
318 | // This is a pay-to-script-hash scriptPubKey; | |
319 | // get the last item that the scriptSig | |
320 | // pushes onto the stack: | |
321 | const_iterator pc = scriptSig.begin(); | |
322 | vector<unsigned char> data; | |
323 | while (pc < scriptSig.end()) | |
324 | { | |
325 | opcodetype opcode; | |
326 | if (!scriptSig.GetOp(pc, opcode, data)) | |
327 | return 0; | |
328 | if (opcode > OP_16) | |
329 | return 0; | |
330 | } | |
331 | ||
332 | /// ... and return its opcount: | |
333 | CScript subscript(data.begin(), data.end()); | |
334 | return subscript.GetSigOpCount(true); | |
335 | } | |
336 | ||
8b78a819 T |
337 | bool CScript::IsPayToPublicKeyHash() const |
338 | { | |
339 | // Extra-fast test for pay-to-pubkey-hash CScripts: | |
340 | return (this->size() == 25 && | |
341 | (*this)[0] == OP_DUP && | |
342 | (*this)[1] == OP_HASH160 && | |
343 | (*this)[2] == 0x14 && | |
344 | (*this)[23] == OP_EQUALVERIFY && | |
345 | (*this)[24] == OP_CHECKSIG); | |
346 | } | |
347 | ||
fa7bf712 | 348 | bool CScript::IsPayToPublicKey() const |
349 | { | |
350 | // Extra-fast test for pay-to-pubkey CScripts: | |
351 | return (this->size() == 35 && | |
352 | (*this)[0] == 33 && | |
353 | (*this)[34] == OP_CHECKSIG); | |
354 | } | |
355 | ||
cbd22a50 | 356 | bool CScript::IsPayToScriptHash() const |
357 | { | |
358 | // Extra-fast test for pay-to-script-hash CScripts: | |
359 | return (this->size() == 23 && | |
29a8ade7 PW |
360 | (*this)[0] == OP_HASH160 && |
361 | (*this)[1] == 0x14 && | |
362 | (*this)[22] == OP_EQUAL); | |
cbd22a50 | 363 | } |
364 | ||
8a727a26 | 365 | // this returns true if either there is nothing left and pc points at the end, or |
366 | // all instructions from the pc to the end of the script are balanced pushes and pops | |
367 | // if there is data, it also returns all the values as byte vectors in a list of vectors | |
368 | bool CScript::GetBalancedData(const_iterator& pc, std::vector<std::vector<unsigned char>>& vSolutions) const | |
a99ca25a | 369 | { |
8a727a26 | 370 | int netPushes = 0; |
371 | vSolutions.clear(); | |
372 | ||
373 | while (pc < end()) | |
374 | { | |
375 | vector<unsigned char> data; | |
376 | opcodetype opcode; | |
377 | if (this->GetOp(pc, opcode, data)) | |
378 | { | |
379 | if (opcode == OP_DROP) | |
380 | { | |
381 | // this should never pop what it hasn't pushed (like a success code) | |
382 | if (--netPushes < 0) | |
383 | return false; | |
3bfa5e22 | 384 | } |
385 | else | |
386 | { | |
387 | // push or fail | |
388 | netPushes++; | |
389 | if (opcode == OP_0) | |
390 | { | |
391 | data.resize(1); | |
392 | data[0] = 0; | |
393 | vSolutions.push_back(data); | |
394 | } | |
395 | else if (opcode >= OP_1 && opcode <= OP_16) | |
396 | { | |
397 | data.resize(1); | |
398 | data[0] = (opcode - OP_1) + 1; | |
399 | vSolutions.push_back(data); | |
400 | } | |
401 | else if (opcode > 0 && opcode <= OP_PUSHDATA4 && data.size() > 0) | |
402 | { | |
403 | vSolutions.push_back(data); | |
404 | } | |
405 | else | |
406 | return false; | |
8a727a26 | 407 | } |
8a727a26 | 408 | } |
409 | else | |
410 | return false; | |
411 | } | |
412 | return netPushes == 0; | |
413 | } | |
414 | ||
3bfa5e22 | 415 | // this returns true if either there is nothing left and pc points at the end |
8a727a26 | 416 | // if there is data, it also returns all the values as byte vectors in a list of vectors |
60b798c4 | 417 | bool CScript::GetPushedData(CScript::const_iterator pc, std::vector<std::vector<unsigned char>>& vData) const |
8a727a26 | 418 | { |
419 | vector<unsigned char> data; | |
420 | opcodetype opcode; | |
3bfa5e22 | 421 | std::vector<unsigned char> vch1 = std::vector<unsigned char>(1); |
8a727a26 | 422 | |
423 | vData.clear(); | |
424 | ||
60b798c4 | 425 | while (pc < end()) |
8a727a26 | 426 | { |
60b798c4 | 427 | if (GetOp(pc, opcode, data)) |
8a727a26 | 428 | { |
60b798c4 | 429 | if (opcode == OP_0) |
8a727a26 | 430 | { |
60b798c4 MT |
431 | vch1[0] = 0; |
432 | vData.push_back(vch1); | |
433 | } | |
434 | else if (opcode >= OP_1 && opcode <= OP_16) | |
435 | { | |
436 | vch1[0] = (opcode - OP_1) + 1; | |
437 | vData.push_back(vch1); | |
8a727a26 | 438 | } |
60b798c4 MT |
439 | else if (opcode > 0 && opcode <= OP_PUSHDATA4 && data.size() > 0) |
440 | { | |
441 | vData.push_back(data); | |
442 | } | |
443 | else | |
444 | return false; | |
8a727a26 | 445 | } |
8a727a26 | 446 | } |
60b798c4 MT |
447 | return vData.size() != 0; |
448 | } | |
449 | ||
450 | // this returns true if either there is nothing left and pc points at the end | |
451 | // if there is data, it also returns all the values as byte vectors in a list of vectors | |
452 | bool CScript::GetOpretData(std::vector<std::vector<unsigned char>>& vData) const | |
453 | { | |
454 | vector<unsigned char> data; | |
455 | opcodetype opcode; | |
456 | CScript::const_iterator pc = this->begin(); | |
457 | ||
458 | if (GetOp(pc, opcode, data) && opcode == OP_RETURN) | |
459 | { | |
460 | return GetPushedData(pc, vData); | |
461 | } | |
462 | else return false; | |
8a727a26 | 463 | } |
464 | ||
06f41160 | 465 | bool CScript::IsPayToCryptoCondition(CScript *pCCSubScript, std::vector<std::vector<unsigned char>>& vParams) const |
8a727a26 | 466 | { |
467 | const_iterator pc = begin(); | |
2f537fa1 | 468 | vector<unsigned char> firstParam; |
a99ca25a | 469 | vector<unsigned char> data; |
28b94612 | 470 | opcodetype opcode; |
2f537fa1 | 471 | if (this->GetOp(pc, opcode, firstParam)) |
28b94612 SS |
472 | // Sha256 conditions are <76 bytes |
473 | if (opcode > OP_0 && opcode < OP_PUSHDATA1) | |
474 | if (this->GetOp(pc, opcode, data)) | |
475 | if (opcode == OP_CHECKCRYPTOCONDITION) | |
8a727a26 | 476 | { |
477 | const_iterator pcCCEnd = pc; | |
06f41160 | 478 | if (GetBalancedData(pc, vParams)) |
8a727a26 | 479 | { |
480 | if (pCCSubScript) | |
b7c685b8 | 481 | *pCCSubScript = CScript(begin(), pcCCEnd); |
2f537fa1 | 482 | vParams.push_back(firstParam); |
3bfa5e22 | 483 | return true; |
8a727a26 | 484 | } |
485 | } | |
486 | return false; | |
487 | } | |
488 | ||
489 | bool CScript::IsPayToCryptoCondition(CScript *pCCSubScript) const | |
490 | { | |
06f41160 | 491 | std::vector<std::vector<unsigned char>> vParams; |
492 | return IsPayToCryptoCondition(pCCSubScript, vParams); | |
8a727a26 | 493 | } |
494 | ||
495 | bool CScript::IsPayToCryptoCondition() const | |
496 | { | |
b2a98c42 MT |
497 | return IsPayToCryptoCondition((CScript *)NULL); |
498 | } | |
499 | ||
715182a4 | 500 | extern uint160 ASSETCHAINS_CHAINID, VERUS_CHAINID; |
7a912018 | 501 | |
b2a98c42 MT |
502 | bool CScript::IsInstantSpend() const |
503 | { | |
7a912018 | 504 | COptCCParams p; |
505 | bool isInstantSpend = false; | |
8496077f | 506 | if (!_IsVerusActive() && IsPayToCryptoCondition(p) && p.IsValid()) |
b2a98c42 | 507 | { |
7a912018 | 508 | // instant spends must be to expected instant spend crypto conditions and to the right address as well |
715182a4 | 509 | if ((p.evalCode == EVAL_EARNEDNOTARIZATION && GetDestinationID(p.vKeys[0]) == GetConditionID(VERUS_CHAINID, p.evalCode)) || |
510 | (p.evalCode == EVAL_CURRENCYSTATE && GetDestinationID(p.vKeys[0]) == GetConditionID(ASSETCHAINS_CHAINID, p.evalCode)) || | |
511 | (p.evalCode == EVAL_CROSSCHAIN_IMPORT && GetDestinationID(p.vKeys[0]) == GetConditionID(VERUS_CHAINID, p.evalCode)) || | |
512 | (p.evalCode == EVAL_CROSSCHAIN_EXPORT && GetDestinationID(p.vKeys[0]) == GetConditionID(VERUS_CHAINID, p.evalCode))) | |
7a912018 | 513 | { |
514 | isInstantSpend = true; | |
515 | } | |
b2a98c42 | 516 | } |
7a912018 | 517 | return isInstantSpend; |
b2a98c42 MT |
518 | } |
519 | ||
e7c700b5 MT |
520 | bool CScript::IsPayToCryptoCondition(COptCCParams &ccParams) const |
521 | { | |
522 | CScript subScript; | |
523 | std::vector<std::vector<unsigned char>> vParams; | |
524 | ||
525 | if (IsPayToCryptoCondition(&subScript, vParams)) | |
526 | { | |
527 | if (!vParams.empty()) | |
528 | { | |
529 | ccParams = COptCCParams(vParams[0]); | |
2f537fa1 | 530 | for (int i = 1; i < vParams.size(); i++) |
531 | { | |
532 | ccParams.vData.push_back(vParams[i]); | |
533 | } | |
e7c700b5 | 534 | } |
a73ab4b4 | 535 | else |
536 | { | |
537 | // make sure that we return it in a consistent and known state | |
538 | ccParams = COptCCParams(); | |
539 | } | |
e7c700b5 MT |
540 | return true; |
541 | } | |
a73ab4b4 | 542 | ccParams = COptCCParams(); |
e7c700b5 MT |
543 | return false; |
544 | } | |
545 | ||
68b309c0 | 546 | bool CScript::IsPayToCryptoCondition(CScript *ccSubScript, std::vector<std::vector<unsigned char>> &vParams, COptCCParams &optParams) const |
b2a98c42 | 547 | { |
68b309c0 MT |
548 | if (IsPayToCryptoCondition(ccSubScript, vParams)) |
549 | { | |
550 | if (vParams.size() > 0) | |
551 | { | |
552 | optParams = COptCCParams(vParams[0]); | |
553 | return optParams.IsValid(); | |
554 | } | |
555 | } | |
556 | return false; | |
557 | } | |
b2a98c42 | 558 | |
68b309c0 MT |
559 | bool CScript::IsPayToCryptoCondition(uint32_t *ecode) const |
560 | { | |
561 | CScript sub; | |
562 | std::vector<std::vector<unsigned char>> vParams; | |
563 | COptCCParams p; | |
564 | if (IsPayToCryptoCondition(&sub, vParams, p)) | |
b2a98c42 | 565 | { |
68b309c0 | 566 | *ecode = p.evalCode; |
b2a98c42 MT |
567 | return true; |
568 | } | |
569 | return false; | |
a99ca25a SS |
570 | } |
571 | ||
19f01561 | 572 | CScript &CScript::ReplaceCCParams(const COptCCParams ¶ms) |
989b1de1 MT |
573 | { |
574 | CScript subScript; | |
575 | std::vector<std::vector<unsigned char>> vParams; | |
576 | COptCCParams p; | |
19f01561 | 577 | if (this->IsPayToCryptoCondition(&subScript, vParams, p) || p.evalCode != params.evalCode) |
989b1de1 | 578 | { |
19f01561 | 579 | // add the object to the end of the script |
580 | *this = subScript; | |
581 | *this << params.AsVector() << OP_DROP; | |
989b1de1 | 582 | } |
19f01561 | 583 | return *this; |
989b1de1 MT |
584 | } |
585 | ||
15197dca | 586 | bool CScript::IsSpendableOutputType(const COptCCParams &p) const |
587 | { | |
588 | bool isSpendable = true; | |
589 | if (!p.IsValid()) | |
590 | { | |
591 | return isSpendable; | |
592 | } | |
593 | switch (p.evalCode) | |
594 | { | |
595 | case EVAL_CURRENCYSTATE: | |
596 | case EVAL_RESERVE_TRANSFER: | |
597 | case EVAL_RESERVE_EXCHANGE: | |
598 | case EVAL_CROSSCHAIN_IMPORT: | |
adb5cebb | 599 | case EVAL_IDENTITY_COMMITMENT: |
600 | case EVAL_IDENTITY_PRIMARY: | |
601 | case EVAL_IDENTITY_REVOKE: | |
602 | case EVAL_IDENTITY_RECOVER: | |
603 | case EVAL_IDENTITY_RESERVATION: | |
15197dca | 604 | { |
605 | isSpendable = false; | |
606 | break; | |
607 | } | |
608 | } | |
609 | return isSpendable; | |
610 | } | |
611 | ||
612 | bool CScript::IsSpendableOutputType() const | |
613 | { | |
614 | COptCCParams p; | |
615 | if (IsPayToCryptoCondition(p)) | |
616 | { | |
617 | return IsSpendableOutputType(p); | |
618 | } | |
619 | // default for non-CC outputs is true, this is to protect from accidentally spending specific CC output types, | |
620 | // even though they could be spent | |
621 | return true; | |
622 | } | |
623 | ||
624 | CCurrencyValueMap CScript::ReserveOutValue(COptCCParams &p, bool spendableOnly) const | |
989b1de1 | 625 | { |
56fe75cb | 626 | CCurrencyValueMap retVal; |
4b7d1485 | 627 | |
989b1de1 | 628 | // already validated above |
15197dca | 629 | if (IsPayToCryptoCondition(p) && p.IsValid() && (!spendableOnly || IsSpendableOutputType(p))) |
989b1de1 MT |
630 | { |
631 | switch (p.evalCode) | |
632 | { | |
633 | case EVAL_RESERVE_OUTPUT: | |
634 | { | |
56fe75cb | 635 | CTokenOutput ro(p.vData[0]); |
0ab273d2 | 636 | if (ro.nValue) |
637 | { | |
638 | retVal.valueMap[ro.currencyID] = ro.nValue; | |
639 | } | |
989b1de1 MT |
640 | break; |
641 | } | |
15197dca | 642 | |
643 | case EVAL_RESERVE_DEPOSIT: | |
644 | { | |
645 | CTokenOutput ro(p.vData[0]); | |
646 | if (ro.nValue) | |
647 | { | |
648 | retVal.valueMap[ro.currencyID] = ro.nValue; | |
649 | } | |
650 | break; | |
651 | } | |
652 | ||
34d1aa13 MT |
653 | case EVAL_CURRENCYSTATE: |
654 | { | |
655 | CCoinbaseCurrencyState cbcs(p.vData[0]); | |
56fe75cb | 656 | for (int i = 0; i < cbcs.currencies.size(); i++) |
657 | { | |
658 | if (cbcs.reserveOut[i]) | |
659 | { | |
660 | retVal.valueMap[cbcs.currencies[i]] = cbcs.reserveOut[i]; | |
661 | } | |
662 | } | |
34d1aa13 MT |
663 | break; |
664 | } | |
15197dca | 665 | |
989b1de1 MT |
666 | case EVAL_RESERVE_TRANSFER: |
667 | { | |
668 | CReserveTransfer rt(p.vData[0]); | |
815a42a6 | 669 | // this currency can only be present as native |
f023c2d4 | 670 | if (!(rt.flags & (rt.MINT_CURRENCY | rt.PREALLOCATE)) && rt.currencyID != ASSETCHAINS_CHAINID) |
815a42a6 | 671 | { |
672 | retVal.valueMap[rt.currencyID] = rt.nValue + rt.nFees; | |
673 | } | |
989b1de1 MT |
674 | break; |
675 | } | |
15197dca | 676 | |
989b1de1 MT |
677 | case EVAL_RESERVE_EXCHANGE: |
678 | { | |
679 | CReserveExchange re(p.vData[0]); | |
e7e14f44 MT |
680 | // reserve out amount when converting to reserve is 0, since the amount cannot be calculated in isolation as an input |
681 | // if reserve in, we can consider the output the same reserve value as the input | |
56fe75cb | 682 | if (!(re.flags & re.TO_RESERVE)) |
683 | { | |
684 | retVal.valueMap[re.currencyID] = re.nValue; | |
685 | } | |
e7e14f44 MT |
686 | break; |
687 | } | |
0ab273d2 | 688 | case EVAL_CROSSCHAIN_IMPORT: |
689 | { | |
690 | CCrossChainImport cci(p.vData[0]); | |
691 | // reserve out amount when converting to reserve is 0, since the amount cannot be calculated in isolation as an input | |
692 | // if reserve in, we can consider the output the same reserve value as the input | |
693 | retVal = cci.totalReserveOutMap; | |
694 | break; | |
695 | } | |
e7e14f44 MT |
696 | } |
697 | } | |
56fe75cb | 698 | return retVal; |
e7e14f44 MT |
699 | } |
700 | ||
56fe75cb | 701 | CCurrencyValueMap CScript::ReserveOutValue() const |
e7e14f44 MT |
702 | { |
703 | COptCCParams p; | |
cdbadc7f | 704 | return ReserveOutValue(p); |
989b1de1 MT |
705 | } |
706 | ||
56fe75cb | 707 | bool CScript::SetReserveOutValue(const CCurrencyValueMap &newValues) |
989b1de1 MT |
708 | { |
709 | COptCCParams p; | |
710 | CAmount newVal = 0; | |
711 | ||
712 | // already validated above | |
713 | if (::IsPayToCryptoCondition(*this, p) && p.IsValid()) | |
714 | { | |
715 | switch (p.evalCode) | |
716 | { | |
717 | case EVAL_RESERVE_OUTPUT: | |
718 | { | |
56fe75cb | 719 | if (newValues.valueMap.size() != 1) |
720 | { | |
721 | return false; | |
722 | } | |
723 | CTokenOutput ro(p.vData[0]); | |
724 | ro.currencyID = newValues.valueMap.begin()->first; | |
725 | ro.nValue = newValues.valueMap.begin()->second; | |
989b1de1 MT |
726 | p.vData[0] = ro.AsVector(); |
727 | break; | |
728 | } | |
729 | case EVAL_RESERVE_TRANSFER: | |
730 | { | |
56fe75cb | 731 | if (newValues.valueMap.size() != 1) |
732 | { | |
733 | return false; | |
734 | } | |
989b1de1 | 735 | CReserveTransfer rt(p.vData[0]); |
56fe75cb | 736 | rt.currencyID = newValues.valueMap.begin()->first; |
737 | rt.nValue = newValues.valueMap.begin()->second; | |
989b1de1 MT |
738 | p.vData[0] = rt.AsVector(); |
739 | break; | |
740 | } | |
741 | case EVAL_RESERVE_EXCHANGE: | |
742 | { | |
56fe75cb | 743 | if (newValues.valueMap.size() != 1) |
744 | { | |
745 | return false; | |
746 | } | |
989b1de1 | 747 | CReserveExchange re(p.vData[0]); |
56fe75cb | 748 | re.currencyID = newValues.valueMap.begin()->first; |
749 | re.nValue = newValues.valueMap.begin()->second; | |
989b1de1 MT |
750 | p.vData[0] = re.AsVector(); |
751 | break; | |
752 | } | |
cdbadc7f | 753 | case EVAL_CROSSCHAIN_IMPORT: |
754 | { | |
755 | CCrossChainImport cci(p.vData[0]); | |
56fe75cb | 756 | cci.importValue = newValues; |
cdbadc7f | 757 | p.vData[0] = cci.AsVector(); |
758 | break; | |
759 | } | |
760 | // cross chain import thread holds the original conversion amounts | |
761 | case EVAL_CURRENCYSTATE: | |
762 | { | |
763 | CCoinbaseCurrencyState cbcs(p.vData[0]); | |
56fe75cb | 764 | for (int i = 0; i < cbcs.currencies.size(); i++) |
765 | { | |
766 | auto it = newValues.valueMap.find(cbcs.currencies[i]); | |
767 | if (it != newValues.valueMap.end()) | |
768 | { | |
769 | cbcs.reserveOut[i] = it->second; | |
770 | } | |
771 | } | |
cdbadc7f | 772 | p.vData[0] = cbcs.AsVector(); |
773 | break; | |
774 | } | |
775 | ||
989b1de1 MT |
776 | default: |
777 | return false; | |
778 | } | |
779 | *this = ReplaceCCParams(p); | |
780 | return true; | |
781 | } | |
782 | return false; | |
783 | } | |
784 | ||
2c8d8268 SS |
785 | bool CScript::MayAcceptCryptoCondition() const |
786 | { | |
787 | // Get the type mask of the condition | |
788 | const_iterator pc = this->begin(); | |
789 | vector<unsigned char> data; | |
790 | opcodetype opcode; | |
791 | if (!this->GetOp(pc, opcode, data)) return false; | |
792 | if (!(opcode > OP_0 && opcode < OP_PUSHDATA1)) return false; | |
793 | CC *cond = cc_readConditionBinary(data.data(), data.size()); | |
794 | if (!cond) return false; | |
b2a98c42 | 795 | |
0574c740 | 796 | uint32_t eCode; |
797 | if (!IsPayToCryptoCondition(&eCode)) | |
798 | { | |
799 | return false; | |
800 | } | |
801 | ||
802 | bool out = IsSupportedCryptoCondition(cond, eCode); | |
b2a98c42 | 803 | |
563581af SS |
804 | cc_free(cond); |
805 | return out; | |
2c8d8268 SS |
806 | } |
807 | ||
6da11b8b | 808 | // also checks if the eval code is consistent |
809 | bool CScript::MayAcceptCryptoCondition(int evalCode) const | |
810 | { | |
811 | // Get the type mask of the condition | |
812 | const_iterator pc = this->begin(); | |
813 | vector<unsigned char> data; | |
814 | opcodetype opcode; | |
815 | if (!this->GetOp(pc, opcode, data)) return false; | |
816 | if (!(opcode > OP_0 && opcode < OP_PUSHDATA1)) return false; | |
817 | CC *cond = cc_readConditionBinary(data.data(), data.size()); | |
818 | if (!cond) return false; | |
819 | ||
0574c740 | 820 | bool out = IsSupportedCryptoCondition(cond, evalCode); |
6da11b8b | 821 | |
822 | cc_free(cond); | |
823 | return out; | |
824 | } | |
825 | ||
0cb91a8d SS |
826 | bool CScript::IsCoinImport() const |
827 | { | |
828 | const_iterator pc = this->begin(); | |
829 | vector<unsigned char> data; | |
830 | opcodetype opcode; | |
831 | if (this->GetOp(pc, opcode, data)) | |
832 | if (opcode > OP_0 && opcode <= OP_PUSHDATA4) | |
833 | return data.begin()[0] == EVAL_IMPORTCOIN; | |
834 | return false; | |
835 | } | |
836 | ||
cbd22a50 | 837 | bool CScript::IsPushOnly() const |
838 | { | |
839 | const_iterator pc = begin(); | |
840 | while (pc < end()) | |
841 | { | |
842 | opcodetype opcode; | |
843 | if (!GetOp(pc, opcode)) | |
844 | return false; | |
845 | // Note that IsPushOnly() *does* consider OP_RESERVED to be a | |
846 | // push-type opcode, however execution of OP_RESERVED fails, so | |
d752ba86 | 847 | // it's not relevant to P2SH/BIP62 as the scriptSig would fail prior to |
cbd22a50 | 848 | // the P2SH special validation code being executed. |
849 | if (opcode > OP_16) | |
850 | return false; | |
851 | } | |
852 | return true; | |
853 | } | |
854 | ||
e980a26d | 855 | // if the front of the script has check lock time verify. this is a fairly simple check. |
856 | // accepts NULL as parameter if unlockTime is not needed. | |
857 | bool CScript::IsCheckLockTimeVerify(int64_t *unlockTime) const | |
858 | { | |
859 | opcodetype op; | |
860 | std::vector<unsigned char> unlockTimeParam = std::vector<unsigned char>(); | |
861 | CScript::const_iterator it = this->begin(); | |
862 | ||
863 | if (this->GetOp2(it, op, &unlockTimeParam)) | |
864 | { | |
865 | if (unlockTimeParam.size() >= 0 && unlockTimeParam.size() < 6 && | |
9feb4b9e | 866 | (*this)[unlockTimeParam.size() + 1] == OP_CHECKLOCKTIMEVERIFY) |
e980a26d | 867 | { |
868 | int i = unlockTimeParam.size() - 1; | |
869 | for (*unlockTime = 0; i >= 0; i--) | |
870 | { | |
871 | *unlockTime <<= 8; | |
872 | *unlockTime |= *((unsigned char *)unlockTimeParam.data() + i); | |
873 | } | |
874 | return true; | |
875 | } | |
876 | } | |
877 | return false; | |
878 | } | |
879 | ||
880 | bool CScript::IsCheckLockTimeVerify() const | |
881 | { | |
882 | int64_t ult; | |
883 | return this->IsCheckLockTimeVerify(&ult); | |
884 | } | |
885 | ||
db8eb54b CF |
886 | std::string CScript::ToString() const |
887 | { | |
888 | std::string str; | |
889 | opcodetype opcode; | |
890 | std::vector<unsigned char> vch; | |
891 | const_iterator pc = begin(); | |
892 | while (pc < end()) | |
893 | { | |
894 | if (!str.empty()) | |
895 | str += " "; | |
896 | if (!GetOp(pc, opcode, vch)) | |
897 | { | |
898 | str += "[error]"; | |
899 | return str; | |
900 | } | |
901 | if (0 <= opcode && opcode <= OP_PUSHDATA4) | |
902 | str += ValueString(vch); | |
903 | else | |
904 | str += GetOpName(opcode); | |
905 | } | |
906 | return str; | |
907 | } | |
88d014d0 | 908 | |
f381d4e0 | 909 | CScript::ScriptType CScript::GetType() const |
6cf0e50b LR |
910 | { |
911 | if (this->IsPayToPublicKeyHash()) | |
88d014d0 | 912 | { |
f381d4e0 | 913 | return CScript::P2PKH; |
88d014d0 | 914 | } |
915 | else if (this->IsPayToScriptHash()) | |
916 | { | |
f381d4e0 | 917 | return CScript::P2SH; |
88d014d0 | 918 | } |
919 | else if (this->IsPayToPublicKey()) | |
920 | { | |
921 | return CScript::P2PK; | |
922 | } | |
923 | else if (this->IsPayToCryptoCondition()) | |
924 | { | |
925 | return CScript::P2CC; | |
926 | } | |
927 | ||
68e174e2 | 928 | // We don't know this script type |
f381d4e0 | 929 | return CScript::UNKNOWN; |
6cf0e50b LR |
930 | } |
931 | ||
6cf0e50b LR |
932 | uint160 CScript::AddressHash() const |
933 | { | |
88d014d0 | 934 | uint160 addressHash; |
935 | COptCCParams p; | |
936 | if (this->IsPayToScriptHash()) { | |
937 | addressHash = uint160(std::vector<unsigned char>(this->begin()+2, this->begin()+22)); | |
6cf0e50b | 938 | } |
88d014d0 | 939 | else if (this->IsPayToPublicKeyHash()) |
940 | { | |
941 | addressHash = uint160(std::vector<unsigned char>(this->begin()+3, this->begin()+23)); | |
942 | } | |
943 | else if (this->IsPayToPublicKey()) | |
944 | { | |
945 | std::vector<unsigned char> hashBytes(this->begin()+1, this->begin()+34); | |
946 | addressHash = Hash160(hashBytes); | |
947 | } | |
948 | else if (this->IsPayToCryptoCondition(p)) { | |
949 | if (p.IsValid() && (p.vKeys.size())) | |
950 | { | |
2f537fa1 | 951 | COptCCParams master; |
952 | if (p.version >= p.VERSION_V3 && p.vData.size() > 1 && (master = COptCCParams(p.vData.back())).IsValid() && master.vKeys.size()) | |
953 | { | |
954 | addressHash = GetDestinationID(master.vKeys[0]); | |
955 | } | |
956 | else | |
957 | { | |
958 | addressHash = GetDestinationID(p.vKeys[0]); | |
959 | } | |
88d014d0 | 960 | } |
961 | else | |
962 | { | |
963 | vector<unsigned char> hashBytes(this->begin(), this->end()); | |
964 | addressHash = Hash160(hashBytes); | |
965 | } | |
966 | } | |
967 | return addressHash; | |
6cf0e50b | 968 | } |
88d014d0 | 969 | |
3d682fc8 | 970 | std::vector<CTxDestination> CScript::GetDestinations() const |
2f537fa1 | 971 | { |
3d682fc8 | 972 | std::vector<CTxDestination> destinations; |
2f537fa1 | 973 | COptCCParams p; |
05cef42a | 974 | if (this->IsPayToCryptoCondition(p)) |
975 | { | |
976 | if (p.IsValid() && (p.vKeys.size())) | |
977 | { | |
978 | destinations = p.GetDestinations(); | |
979 | } | |
980 | else | |
981 | { | |
982 | vector<unsigned char> hashBytes(this->begin(), this->end()); | |
983 | destinations.push_back(CKeyID(Hash160(hashBytes))); | |
984 | } | |
985 | } | |
986 | else if (this->IsPayToScriptHash()) { | |
3d682fc8 | 987 | destinations.push_back(CScriptID(uint160(std::vector<unsigned char>(this->begin()+2, this->begin()+22)))); |
2f537fa1 | 988 | } |
989 | else if (this->IsPayToPublicKeyHash()) | |
990 | { | |
3d682fc8 | 991 | destinations.push_back(CKeyID(uint160(std::vector<unsigned char>(this->begin()+3, this->begin()+23)))); |
2f537fa1 | 992 | } |
993 | else if (this->IsPayToPublicKey()) | |
994 | { | |
995 | std::vector<unsigned char> hashBytes(this->begin()+1, this->begin()+34); | |
3d682fc8 | 996 | destinations.push_back(CPubKey(hashBytes)); |
2f537fa1 | 997 | } |
3d682fc8 | 998 | return destinations; |
2f537fa1 | 999 | } |
1000 | ||
757e67a5 | 1001 | uint160 GetNameID(const std::string &Name, const uint160 &parent) |
1002 | { | |
1003 | uint160 writeable = parent; | |
1004 | return CIdentity::GetID(Name, writeable); | |
1005 | } | |
1006 | ||
b826c625 | 1007 | CAmount AmountFromValueNoErr(const UniValue& value) |
1008 | { | |
1009 | try | |
1010 | { | |
1011 | CAmount amount; | |
1012 | if (!value.isNum() && !value.isStr()) | |
1013 | { | |
1014 | amount = 0; | |
1015 | } | |
1016 | else if (!ParseFixedPoint(value.getValStr(), 8, &amount)) | |
1017 | { | |
1018 | amount = 0; | |
1019 | } | |
1020 | else if (!MoneyRange(amount)) | |
1021 | { | |
1022 | amount = 0; | |
1023 | } | |
1024 | return amount; | |
1025 | } | |
1026 | catch(const std::exception& e) | |
1027 | { | |
1028 | return 0; | |
1029 | } | |
1030 | } | |
1031 | ||
b826c625 | 1032 | CCurrencyValueMap::CCurrencyValueMap(const std::vector<uint160> ¤cyIDs, const std::vector<CAmount> &amounts) |
1033 | { | |
1034 | int commonNum = currencyIDs.size() >= amounts.size() ? amounts.size() : currencyIDs.size(); | |
1035 | for (int i = 0; i < commonNum; i++) | |
1036 | { | |
1037 | valueMap[currencyIDs[i]] = amounts[i]; | |
1038 | } | |
1039 | } | |
1040 | ||
1041 | bool operator<(const CCurrencyValueMap& a, const CCurrencyValueMap& b) | |
1042 | { | |
1043 | // to be less than means, in this order: | |
1044 | // 1. To have fewer non-zero currencies. | |
1045 | // 2. If not fewer currencies, to be unable to be subtracted from the one being checked | |
1046 | // without creating negative values | |
1047 | if (!a.valueMap.size() && !b.valueMap.size()) | |
1048 | { | |
1049 | return false; | |
1050 | } | |
1051 | bool isaltb = false; | |
1052 | ||
1053 | for (auto &oneVal : b.valueMap) | |
1054 | { | |
1055 | if (oneVal.second) | |
1056 | { | |
1057 | auto it = a.valueMap.find(oneVal.first); | |
1058 | if (it == a.valueMap.end() || it->second < oneVal.second) | |
1059 | { | |
1060 | isaltb = true; | |
1061 | } | |
1062 | } | |
1063 | } | |
1064 | return isaltb; | |
1065 | } | |
1066 | ||
1067 | bool operator>(const CCurrencyValueMap& a, const CCurrencyValueMap& b) | |
1068 | { | |
1069 | return b < a; | |
1070 | } | |
1071 | ||
1072 | bool operator==(const CCurrencyValueMap& a, const CCurrencyValueMap& b) | |
1073 | { | |
1074 | if (a.valueMap.size() != b.valueMap.size()) | |
1075 | { | |
1076 | return false; | |
1077 | } | |
1078 | ||
1079 | bool isaeqb = true; | |
1080 | for (auto &oneVal : a.valueMap) | |
1081 | { | |
1082 | auto it = b.valueMap.find(oneVal.first); | |
1083 | if (it == b.valueMap.end() || it->second != oneVal.second) | |
1084 | { | |
1085 | isaeqb = false; | |
1086 | break; | |
1087 | } | |
1088 | } | |
1089 | return isaeqb; | |
1090 | } | |
1091 | ||
1092 | bool operator!=(const CCurrencyValueMap& a, const CCurrencyValueMap& b) | |
1093 | { | |
1094 | return !(a == b); | |
1095 | } | |
1096 | ||
1097 | bool operator<=(const CCurrencyValueMap& a, const CCurrencyValueMap& b) | |
1098 | { | |
1099 | return (a < b) || (a == b); | |
1100 | } | |
1101 | ||
1102 | bool operator>=(const CCurrencyValueMap& a, const CCurrencyValueMap& b) | |
1103 | { | |
1104 | return b <= a; | |
1105 | } | |
1106 | ||
1107 | CCurrencyValueMap operator+(const CCurrencyValueMap& a, const CCurrencyValueMap& b) | |
1108 | { | |
1109 | CCurrencyValueMap retVal = a; | |
1110 | if (a.valueMap.size() || b.valueMap.size()) | |
1111 | { | |
1112 | for (auto &oneVal : b.valueMap) | |
1113 | { | |
1114 | auto it = retVal.valueMap.find(oneVal.first); | |
1115 | if (it == retVal.valueMap.end()) | |
1116 | { | |
1117 | retVal.valueMap[oneVal.first] = oneVal.second; | |
1118 | } | |
1119 | else | |
1120 | { | |
1121 | it->second += oneVal.second; | |
1122 | } | |
1123 | } | |
1124 | } | |
1125 | return retVal; | |
1126 | } | |
1127 | ||
1128 | CCurrencyValueMap operator-(const CCurrencyValueMap& a, const CCurrencyValueMap& b) | |
1129 | { | |
1130 | CCurrencyValueMap retVal = a; | |
1131 | if (a.valueMap.size() || b.valueMap.size()) | |
1132 | { | |
1133 | for (auto &oneVal : b.valueMap) | |
1134 | { | |
1135 | auto it = retVal.valueMap.find(oneVal.first); | |
1136 | if (it == retVal.valueMap.end()) | |
1137 | { | |
1138 | retVal.valueMap[oneVal.first] = -oneVal.second; | |
1139 | } | |
1140 | else | |
1141 | { | |
1142 | it->second -= oneVal.second; | |
1143 | } | |
1144 | } | |
1145 | } | |
1146 | return retVal; | |
1147 | } | |
1148 | ||
1149 | CCurrencyValueMap operator+(const CCurrencyValueMap& a, int b) | |
1150 | { | |
1151 | CCurrencyValueMap retVal = a; | |
1152 | for (auto &oneVal : retVal.valueMap) | |
1153 | { | |
1154 | oneVal.second += b; | |
1155 | } | |
1156 | return retVal; | |
1157 | } | |
1158 | ||
1159 | CCurrencyValueMap operator-(const CCurrencyValueMap& a, int b) | |
1160 | { | |
1161 | CCurrencyValueMap retVal = a; | |
1162 | for (auto &oneVal : retVal.valueMap) | |
1163 | { | |
1164 | oneVal.second -= b; | |
1165 | } | |
1166 | return retVal; | |
1167 | } | |
1168 | ||
1169 | CCurrencyValueMap operator*(const CCurrencyValueMap& a, int b) | |
1170 | { | |
1171 | CCurrencyValueMap retVal = a; | |
1172 | for (auto &oneVal : retVal.valueMap) | |
1173 | { | |
1174 | oneVal.second *= b; | |
1175 | } | |
1176 | return retVal; | |
1177 | } | |
1178 | ||
1179 | const CCurrencyValueMap &CCurrencyValueMap::operator-=(const CCurrencyValueMap& operand) | |
1180 | { | |
1181 | return *this = *this - operand; | |
1182 | } | |
1183 | ||
1184 | const CCurrencyValueMap &CCurrencyValueMap::operator+=(const CCurrencyValueMap& operand) | |
1185 | { | |
1186 | return *this = *this + operand; | |
1187 | } | |
1188 | ||
1189 | // determine if the operand intersects this map | |
1190 | bool CCurrencyValueMap::Intersects(const CCurrencyValueMap& operand) const | |
1191 | { | |
1192 | bool retVal = false; | |
1193 | ||
1194 | if (valueMap.size() && operand.valueMap.size()) | |
1195 | { | |
1196 | for (auto &oneVal : valueMap) | |
1197 | { | |
1198 | auto it = operand.valueMap.find(oneVal.first); | |
1199 | if (it != operand.valueMap.end()) | |
1200 | { | |
1201 | if (it->second > 0 && oneVal.second > 0) | |
1202 | { | |
1203 | retVal = true; | |
1204 | break; | |
1205 | } | |
1206 | } | |
1207 | } | |
1208 | } | |
1209 | return retVal; | |
1210 | } | |
1211 | ||
1212 | CCurrencyValueMap CCurrencyValueMap::IntersectingValues(const CCurrencyValueMap& operand) const | |
1213 | { | |
1214 | CCurrencyValueMap retVal; | |
1215 | ||
1216 | if (valueMap.size() && operand.valueMap.size()) | |
1217 | { | |
1218 | for (auto &oneVal : valueMap) | |
1219 | { | |
1220 | auto it = operand.valueMap.find(oneVal.first); | |
1221 | if (it != operand.valueMap.end() && | |
1222 | it->second != 0 && | |
1223 | oneVal.second != 0) | |
1224 | { | |
1225 | retVal.valueMap[oneVal.first] = oneVal.second; | |
1226 | } | |
1227 | } | |
1228 | } | |
1229 | return retVal; | |
1230 | } | |
1231 | ||
1232 | CCurrencyValueMap CCurrencyValueMap::CanonicalMap() const | |
1233 | { | |
1234 | CCurrencyValueMap retVal; | |
1235 | for (auto valPair : valueMap) | |
1236 | { | |
1237 | if (valPair.second != 0) | |
1238 | { | |
1239 | retVal.valueMap.insert(valPair); | |
1240 | } | |
1241 | } | |
1242 | return retVal; | |
1243 | } | |
1244 | ||
1245 | CCurrencyValueMap CCurrencyValueMap::NonIntersectingValues(const CCurrencyValueMap& operand) const | |
1246 | { | |
1247 | CCurrencyValueMap retVal = operand; | |
1248 | ||
1249 | if (valueMap.size() && operand.valueMap.size()) | |
1250 | { | |
1251 | for (auto &oneVal : valueMap) | |
1252 | { | |
1253 | auto it = operand.valueMap.find(oneVal.first); | |
1254 | if (it != operand.valueMap.end()) | |
1255 | { | |
1256 | if (it->second > 0 && oneVal.second > 0) | |
1257 | { | |
1258 | retVal.valueMap.erase(it); | |
1259 | } | |
1260 | } | |
1261 | } | |
1262 | } | |
1263 | return retVal; | |
1264 | } | |
1265 | ||
1266 | bool CCurrencyValueMap::IsValid() const | |
1267 | { | |
1268 | for (auto &oneVal : valueMap) | |
1269 | { | |
1270 | if (oneVal.first.IsNull()) | |
1271 | { | |
1272 | return false; | |
1273 | } | |
1274 | } | |
1275 | return true; | |
1276 | } | |
1277 | ||
1278 | bool CCurrencyValueMap::HasNegative() const | |
1279 | { | |
1280 | for (auto &oneVal : valueMap) | |
1281 | { | |
1282 | if (oneVal.second < 0) | |
1283 | { | |
1284 | return true; | |
1285 | } | |
1286 | } | |
1287 | return false; | |
1288 | } | |
1289 | ||
1290 | // subtract, but do not subtract to negative values | |
1291 | CCurrencyValueMap CCurrencyValueMap::SubtractToZero(const CCurrencyValueMap& operand) const | |
1292 | { | |
1293 | CCurrencyValueMap retVal = *this; | |
1294 | std::vector<uint160> toRemove; | |
1295 | if (valueMap.size() && operand.valueMap.size()) | |
1296 | { | |
1297 | for (auto &oneVal : retVal.valueMap) | |
1298 | { | |
1299 | auto it = operand.valueMap.find(oneVal.first); | |
1300 | if (it != operand.valueMap.end()) | |
1301 | { | |
1302 | oneVal.second = oneVal.second - it->second; | |
1303 | if (oneVal.second <= 0) | |
1304 | { | |
1305 | toRemove.push_back(oneVal.first); | |
1306 | } | |
1307 | } | |
1308 | } | |
1309 | } | |
1310 | for (auto &toErase : toRemove) | |
1311 | { | |
1312 | retVal.valueMap.erase(toErase); | |
1313 | } | |
1314 | return retVal; | |
1315 | } | |
1316 | ||
1317 | std::vector<CAmount> CCurrencyValueMap::AsCurrencyVector(const std::vector<uint160> ¤cies) const | |
1318 | { | |
1319 | std::vector<CAmount> retVal(currencies.size()); | |
1320 | for (int i = 0; i < currencies.size(); i++) | |
1321 | { | |
1322 | auto it = valueMap.find(currencies[i]); | |
1323 | retVal[i] = it != valueMap.end() ? it->second : 0; | |
1324 | } | |
1325 | return retVal; | |
1326 | } |