]> Git Repo - VerusCoin.git/blame - src/pbaas/pbaas.cpp
Update version
[VerusCoin.git] / src / pbaas / pbaas.cpp
CommitLineData
b2a98c42
MT
1/********************************************************************
2 * (C) 2019 Michael Toutonghi
3 *
4 * Distributed under the MIT software license, see the accompanying
5 * file COPYING or http://www.opensource.org/licenses/mit-license.php.
6 *
7 * This provides support for PBaaS initialization, notarization, and cross-chain token
8 * transactions and enabling liquid or non-liquid tokens across the
9 * Verus ecosystem.
10 *
11 */
12
13#include "pbaas/pbaas.h"
14#include "pbaas/notarization.h"
15#include "rpc/pbaasrpc.h"
16#include "pbaas/crosschainrpc.h"
17#include "base58.h"
85c51d62 18#include "timedata.h"
0d5cfbf5 19#include "main.h"
c3250dcd 20#include "transaction_builder.h"
b2a98c42
MT
21
22using namespace std;
23
24CConnectedChains ConnectedChains;
25
26bool IsVerusActive()
27{
28 return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 0 || strcmp(ASSETCHAINS_SYMBOL, "VRSCTEST") == 0);
29}
30
31// this adds an opret to a mutable transaction and returns the voutnum if it could be added
32int32_t AddOpRetOutput(CMutableTransaction &mtx, const CScript &opRetScript)
33{
34 if (opRetScript.IsOpReturn() && opRetScript.size() <= MAX_OP_RETURN_RELAY)
35 {
36 CTxOut vOut = CTxOut();
37 vOut.scriptPubKey = opRetScript;
38 vOut.nValue = 0;
39 mtx.vout.push_back(vOut);
40 return mtx.vout.size() - 1;
41 }
42 else
43 {
44 return -1;
45 }
46}
47
48// returns a pointer to a base chain object, which can be cast to the
49// object type indicated in its objType member
50uint256 GetChainObjectHash(const CBaseChainObject &bo)
51{
52 union {
53 const CChainObject<CBlockHeader> *pNewHeader;
54 const CChainObject<CTransaction> *pNewTx;
55 const CChainObject<CMerkleBranch> *pNewProof;
56 const CChainObject<CHeaderRef> *pNewHeaderRef;
1fa4454d 57 const CChainObject<CPriorBlocksCommitment> *pPriors;
989b1de1 58 const CChainObject<CReserveTransfer> *pExport;
34d1aa13 59 const CChainObject<CCrossChainProof> *pCrossChainProof;
b2a98c42
MT
60 const CBaseChainObject *retPtr;
61 };
62
63 retPtr = &bo;
64
65 switch(bo.objectType)
66 {
67 case CHAINOBJ_HEADER:
68 return pNewHeader->GetHash();
69
70 case CHAINOBJ_TRANSACTION:
71 return pNewTx->GetHash();
72
73 case CHAINOBJ_PROOF:
74 return pNewProof->GetHash();
75
76 case CHAINOBJ_HEADER_REF:
77 return pNewHeaderRef->GetHash();
78
1fa4454d
MT
79 case CHAINOBJ_PRIORBLOCKS:
80 return pPriors->GetHash();
989b1de1
MT
81
82 case CHAINOBJ_RESERVETRANSFER:
83 return pExport->GetHash();
34d1aa13
MT
84
85 case CHAINOBJ_CROSSCHAINPROOF:
86 return pCrossChainProof->GetHash();
87
b2a98c42
MT
88 }
89 return uint256();
90}
91
92// used to export coins from one chain to another, if they are not native, they are represented on the other
93// chain as tokens
78819397
MT
94bool ValidateCrossChainExport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
95{
96 return true;
97}
d727514f 98
78819397 99bool IsCrossChainExportInput(const CScript &scriptSig)
b2a98c42 100{
b2a98c42
MT
101}
102
103// used to validate import of coins from one chain to another. if they are not native and are supported,
104// they are represented o the chain as tokens
78819397
MT
105bool ValidateCrossChainImport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
106{
d727514f 107 return true;
78819397
MT
108}
109bool IsCrossChainImportInput(const CScript &scriptSig)
b2a98c42 110{
b2a98c42
MT
111}
112
113// used to validate a specific service reward based on the spending transaction
114bool ValidateServiceReward(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
115{
13ed2980
MT
116 // for each type of service reward, we need to check and see if the spender is
117 // correctly formatted to be a valid spend of the service reward. for notarization
118 // we ensure that the notarization and its outputs are valid and that the spend
119 // applies to the correct billing period
120 return true;
b2a98c42 121}
78819397
MT
122bool IsServiceRewardInput(const CScript &scriptSig)
123{
124}
b2a98c42
MT
125
126// used as a proxy token output for a reserve currency on its fractional reserve chain
127bool ValidateReserveOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
128{
78819397
MT
129 return true;
130}
131bool IsReserveOutputInput(const CScript &scriptSig)
132{
b2a98c42
MT
133}
134
78819397
MT
135bool ValidateReserveTransfer(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
136{
137 return true;
138}
139bool IsReserveTransferInput(const CScript &scriptSig)
b2a98c42 140{
78819397 141}
b2a98c42 142
78819397
MT
143bool ValidateReserveDeposit(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
144{
145 return true;
146}
147bool IsReserveDepositInput(const CScript &scriptSig)
148{
b2a98c42
MT
149}
150
78819397 151bool ValidateCurrencyState(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
b2a98c42 152{
78819397
MT
153 return true;
154}
155bool IsCurrencyStateInput(const CScript &scriptSig)
156{
157}
b2a98c42 158
78819397
MT
159// used to convert a fractional reserve currency into its reserve and back
160bool ValidateReserveExchange(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
161{
162 return true;
b2a98c42 163}
d305d656 164bool IsReserveExchangeInput(const CScript &scriptSig)
165{
166}
167
b2a98c42
MT
168
169/*
170 * Verifies that the input objects match the hashes and returns the transaction.
171 *
172 * If the opRetTx has the op ret, this calculates based on the actual transaction and
173 * validates the hashes. If the opRetTx does not have the opRet itself, this validates
174 * by ensuring that all objects are present on this chain, composing the opRet, and
175 * ensuring that the transaction then hashes to the correct txid.
176 *
177 */
178bool ValidateOpretProof(CScript &opRet, COpRetProof &orProof)
179{
180 // enumerate through the objects and validate that they are objects of the expected type that hash
181 // to the value expected. return true if so
b2a98c42
MT
182}
183
1fa4454d 184int8_t ObjTypeCode(const CBlockHeader &obj)
b2a98c42
MT
185{
186 return CHAINOBJ_HEADER;
187}
188
1fa4454d 189int8_t ObjTypeCode(const CMerkleBranch &obj)
b2a98c42
MT
190{
191 return CHAINOBJ_PROOF;
192}
193
1fa4454d 194int8_t ObjTypeCode(const CTransaction &obj)
b2a98c42
MT
195{
196 return CHAINOBJ_TRANSACTION;
197}
198
1fa4454d 199int8_t ObjTypeCode(const CHeaderRef &obj)
b2a98c42
MT
200{
201 return CHAINOBJ_HEADER_REF;
202}
203
1fa4454d 204int8_t ObjTypeCode(const CPriorBlocksCommitment &obj)
b2a98c42 205{
1fa4454d 206 return CHAINOBJ_PRIORBLOCKS;
b2a98c42
MT
207}
208
989b1de1
MT
209int8_t ObjTypeCode(const CReserveTransfer &obj)
210{
211 return CHAINOBJ_RESERVETRANSFER;
212}
213
34d1aa13
MT
214int8_t ObjTypeCode(const CCrossChainProof &obj)
215{
216 return CHAINOBJ_CROSSCHAINPROOF;
217}
218
b2a98c42 219// this adds an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction
f8a22472 220CScript StoreOpRetArray(std::vector<CBaseChainObject *> &objPtrs)
b2a98c42
MT
221{
222 CScript vData;
223 CDataStream s = CDataStream(SER_NETWORK, PROTOCOL_VERSION);
0b806be9 224 s << (int32_t)OPRETTYPE_OBJECTARR;
b2a98c42
MT
225 bool error = false;
226
227 for (auto pobj : objPtrs)
228 {
d48d49b6
MT
229 try
230 {
231 if (!DehydrateChainObject(s, pobj))
232 {
233 error = true;
234 break;
235 }
236 }
237 catch(const std::exception& e)
b2a98c42 238 {
d48d49b6 239 std::cerr << e.what() << '\n';
b2a98c42
MT
240 error = true;
241 break;
242 }
243 }
244
05578402
MT
245 //std::vector<unsigned char> schars(s.begin(), s.begin() + 200);
246 //printf("stream vector chars: %s\n", HexBytes(&schars[0], schars.size()).c_str());
6278cf9c 247
b2a98c42 248 std::vector<unsigned char> vch(s.begin(), s.end());
d48d49b6 249 return error ? CScript() : CScript() << OP_RETURN << vch;
b2a98c42
MT
250}
251
a8c2cb11
MT
252void DeleteOpRetObjects(std::vector<CBaseChainObject *> &ora)
253{
254 for (auto pobj : ora)
255 {
256 switch(pobj->objectType)
257 {
258 case CHAINOBJ_HEADER:
259 {
260 delete (CChainObject<CBlockHeader> *)pobj;
261 break;
262 }
263
264 case CHAINOBJ_TRANSACTION:
265 {
266 delete (CChainObject<CTransaction> *)pobj;
267 break;
268 }
269
270 case CHAINOBJ_PROOF:
271 {
272 delete (CChainObject<CMerkleBranch> *)pobj;
273 break;
274 }
275
276 case CHAINOBJ_HEADER_REF:
277 {
278 delete (CChainObject<CHeaderRef> *)pobj;
279 break;
280 }
281
282 case CHAINOBJ_PRIORBLOCKS:
283 {
284 delete (CChainObject<CPriorBlocksCommitment> *)pobj;
285 break;
286 }
287
989b1de1
MT
288 case CHAINOBJ_RESERVETRANSFER:
289 {
290 delete (CChainObject<CReserveTransfer> *)pobj;
291 break;
292 }
293
34d1aa13
MT
294 case CHAINOBJ_CROSSCHAINPROOF:
295 {
296 delete (CChainObject<CCrossChainProof> *)pobj;
297 break;
298 }
299
a8c2cb11
MT
300 default:
301 delete pobj;
302 }
303 }
5d13ad1e 304 ora.clear();
a8c2cb11
MT
305}
306
b2a98c42
MT
307std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
308{
309 std::vector<unsigned char> vch;
310 std::vector<CBaseChainObject *> vRet;
311 if (opRetScript.IsOpReturn() && GetOpReturnData(opRetScript, vch) && vch.size() > 0)
312 {
313 CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION);
314
1fa4454d
MT
315 int32_t opRetType;
316
317 try
318 {
319 s >> opRetType;
320 if (opRetType == OPRETTYPE_OBJECTARR)
321 {
322 CBaseChainObject *pobj;
323 while (!s.empty() && (pobj = RehydrateChainObject(s)))
324 {
325 vRet.push_back(pobj);
326 }
aeca4678
MT
327 if (!s.empty())
328 {
2d8cedc7 329 printf("failed to load all objects in opret");
ba256d3a 330 DeleteOpRetObjects(vRet);
2d8cedc7 331 vRet.clear();
aeca4678 332 }
1fa4454d
MT
333 }
334 }
335 catch(const std::exception& e)
b2a98c42 336 {
1fa4454d 337 std::cerr << e.what() << '\n';
ba256d3a 338 DeleteOpRetObjects(vRet);
1fa4454d 339 vRet.clear();
b2a98c42
MT
340 }
341 }
342 return vRet;
343}
344
9f0c14b2
MT
345CNodeData::CNodeData(UniValue &obj)
346{
4fa3b13d 347 networkAddress = uni_get_str(find_value(obj, "networkaddress"));
ebee7b5b
MT
348 CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
349 ba.GetKeyID(paymentAddress);
9f0c14b2
MT
350}
351
352UniValue CNodeData::ToUniValue() const
353{
354 UniValue obj(UniValue::VOBJ);
355 obj.push_back(Pair("networkaddress", networkAddress));
ebee7b5b 356 obj.push_back(Pair("paymentaddress", CBitcoinAddress(paymentAddress).ToString()));
9f0c14b2
MT
357 return obj;
358}
359
f8f61a6d 360CPBaaSChainDefinition::CPBaaSChainDefinition(const UniValue &obj)
9f0c14b2 361{
f8f61a6d 362 nVersion = PBAAS_VERSION;
f39e2bdf
MT
363 name = std::string(uni_get_str(find_value(obj, "name")), 0, (KOMODO_ASSETCHAIN_MAXLEN - 1));
364
2ebe5d52
MT
365 string invalidChars = "\\/:?\"<>|";
366 for (int i = 0; i < name.size(); i++)
367 {
368 if (invalidChars.find(name[i]) != string::npos)
369 {
370 name[i] = '_';
371 }
372 }
f39e2bdf 373
ebee7b5b
MT
374 CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
375 ba.GetKeyID(address);
4fa3b13d 376 premine = uni_get_int64(find_value(obj, "premine"));
e7e14f44 377 initialcontribution = uni_get_int64(find_value(obj, "initialcontribution"));
f8f61a6d 378 conversion = uni_get_int64(find_value(obj, "conversion"));
bb6c3482 379 minpreconvert = uni_get_int64(find_value(obj, "minpreconvert"));
989b1de1 380 maxpreconvert = uni_get_int64(find_value(obj, "maxpreconvert"));
e7e14f44 381 preconverted = uni_get_int64(find_value(obj, "preconverted"));
f8f61a6d 382 launchFee = uni_get_int64(find_value(obj, "launchfee"));
4fa3b13d
MT
383 startBlock = uni_get_int(find_value(obj, "startblock"));
384 endBlock = uni_get_int(find_value(obj, "endblock"));
9f0c14b2 385
4fa3b13d 386 auto vEras = uni_getValues(find_value(obj, "eras"));
f8f61a6d 387 if (vEras.size() > ASSETCHAINS_MAX_ERAS)
388 {
389 vEras.resize(ASSETCHAINS_MAX_ERAS);
390 }
391 eras = !vEras.size() ? 1 : vEras.size();
9f0c14b2
MT
392
393 for (auto era : vEras)
394 {
4fa3b13d
MT
395 rewards.push_back(uni_get_int64(find_value(era, "reward")));
396 rewardsDecay.push_back(uni_get_int64(find_value(era, "decay")));
397 halving.push_back(uni_get_int64(find_value(era, "halving")));
398 eraEnd.push_back(uni_get_int64(find_value(era, "eraend")));
399 eraOptions.push_back(uni_get_int64(find_value(era, "eraoptions")));
9f0c14b2
MT
400 }
401
4fa3b13d
MT
402 billingPeriod = uni_get_int(find_value(obj, "billingperiod"));
403 notarizationReward = uni_get_int64(find_value(obj, "notarizationreward"));
9f0c14b2 404
4fa3b13d 405 auto nodeVec = uni_getValues(find_value(obj, "nodes"));
9f0c14b2
MT
406 for (auto node : nodeVec)
407 {
4fa3b13d 408 nodes.push_back(CNodeData(node));
9f0c14b2
MT
409 }
410}
411
b2a98c42
MT
412CPBaaSChainDefinition::CPBaaSChainDefinition(const CTransaction &tx, bool validate)
413{
414 bool definitionFound = false;
415 nVersion = PBAAS_VERSION_INVALID;
416 for (auto out : tx.vout)
417 {
13ed2980
MT
418 COptCCParams p;
419 if (IsPayToCryptoCondition(out.scriptPubKey, p))
b2a98c42 420 {
13ed2980 421 if (p.evalCode == EVAL_PBAASDEFINITION)
b2a98c42
MT
422 {
423 if (definitionFound)
424 {
425 nVersion = PBAAS_VERSION_INVALID;
426 }
427 else
428 {
13ed2980 429 FromVector(p.vData[0], *this);
f39e2bdf
MT
430
431 // TODO - remove this after validation is finished, check now in case some larger strings got into the chain
2ebe5d52
MT
432 name = std::string(name, 0, (KOMODO_ASSETCHAIN_MAXLEN - 1));
433 string invalidChars = "\\/:?\"<>|";
434 for (int i = 0; i < name.size(); i++)
435 {
436 if (invalidChars.find(name[i]) != string::npos)
437 {
438 name[i] = '_';
439 }
440 }
f39e2bdf 441
b2a98c42 442 definitionFound = true;
b2a98c42
MT
443 }
444 }
445 }
446 }
447
448 if (validate)
449 {
450
451 }
452}
453
13ed2980
MT
454CServiceReward::CServiceReward(const CTransaction &tx, bool validate)
455{
456 nVersion = PBAAS_VERSION_INVALID;
457 for (auto out : tx.vout)
458 {
459 COptCCParams p;
460 if (IsPayToCryptoCondition(out.scriptPubKey, p))
461 {
462 // always take the first for now
463 if (p.evalCode == EVAL_SERVICEREWARD)
464 {
465 FromVector(p.vData[0], *this);
466 break;
467 }
468 }
469 }
470
471 if (validate)
472 {
473
474 }
475}
476
41f170fd
MT
477CCrossChainExport::CCrossChainExport(const CTransaction &tx)
478{
479 for (auto out : tx.vout)
480 {
481 COptCCParams p;
482 if (IsPayToCryptoCondition(out.scriptPubKey, p))
483 {
484 // always take the first for now
485 if (p.evalCode == EVAL_CROSSCHAIN_EXPORT)
486 {
487 FromVector(p.vData[0], *this);
488 break;
489 }
490 }
491 }
492}
493
b2a98c42
MT
494uint160 CPBaaSChainDefinition::GetChainID(std::string name)
495{
496 const char *chainName = name.c_str();
497 uint256 chainHash = Hash(chainName, chainName + strlen(chainName));
498 return Hash160(chainHash.begin(), chainHash.end());
499}
500
c3250dcd 501uint160 CPBaaSChainDefinition::GetConditionID(int32_t condition) const
b2a98c42 502{
57055854 503 return CCrossChainRPCData::GetConditionID(name, condition);
b2a98c42
MT
504}
505
9f0c14b2
MT
506UniValue CPBaaSChainDefinition::ToUniValue() const
507{
508 UniValue obj(UniValue::VOBJ);
509 obj.push_back(Pair("version", (int64_t)nVersion));
510 obj.push_back(Pair("name", name));
04486a9e 511 obj.push_back(Pair("chainid", GetChainID().GetHex()));
9d9d90c5 512 obj.push_back(Pair("chainaddress", EncodeDestination(CTxDestination(CKeyID(GetChainID())))));
0ca85d57 513 obj.push_back(Pair("paymentaddress", EncodeDestination(CTxDestination(address))));
9f0c14b2 514 obj.push_back(Pair("premine", (int64_t)premine));
e7e14f44 515 obj.push_back(Pair("initialcontribution", (int64_t)initialcontribution));
f8f61a6d 516 obj.push_back(Pair("conversion", (int64_t)conversion));
bb6c3482 517 obj.push_back(Pair("minpreconvert", (int64_t)minpreconvert));
989b1de1 518 obj.push_back(Pair("maxpreconvert", (int64_t)maxpreconvert));
e7e14f44 519 obj.push_back(Pair("preconverted", (int64_t)preconverted));
f8f61a6d 520 obj.push_back(Pair("launchfee", (int64_t)launchFee));
839771fb 521 obj.push_back(Pair("conversionpercent", ValueFromAmount(conversion * 100)));
522 obj.push_back(Pair("launchfeepercent", ValueFromAmount(launchFee * 100)));
4fa3b13d
MT
523 obj.push_back(Pair("startblock", (int32_t)startBlock));
524 obj.push_back(Pair("endblock", (int32_t)endBlock));
9f0c14b2
MT
525
526 UniValue eraArr(UniValue::VARR);
527 for (int i = 0; i < eras; i++)
528 {
529 UniValue era(UniValue::VOBJ);
4fa3b13d
MT
530 era.push_back(Pair("reward", rewards.size() > i ? rewards[i] : (int64_t)0));
531 era.push_back(Pair("decay", rewardsDecay.size() > i ? rewardsDecay[i] : (int64_t)0));
532 era.push_back(Pair("halving", halving.size() > i ? (int32_t)halving[i] : (int32_t)0));
533 era.push_back(Pair("eraend", eraEnd.size() > i ? (int32_t)eraEnd[i] : (int32_t)0));
534 era.push_back(Pair("eraoptions", eraOptions.size() > i ? (int32_t)eraOptions[i] : (int32_t)0));
9f0c14b2
MT
535 eraArr.push_back(era);
536 }
537 obj.push_back(Pair("eras", eraArr));
538
4fa3b13d
MT
539 obj.push_back(Pair("billingperiod", billingPeriod));
540 obj.push_back(Pair("notarizationreward", notarizationReward));
9f0c14b2
MT
541
542 UniValue nodeArr(UniValue::VARR);
543 for (auto node : nodes)
544 {
545 nodeArr.push_back(node.ToUniValue());
546 }
547 obj.push_back(Pair("nodes", nodeArr));
548
549 return obj;
550}
551
5d5737c1
MT
552int CPBaaSChainDefinition::GetDefinedPort() const
553{
554 int port;
555 string host;
8d2c835d 556 for (auto node : nodes)
5d5737c1
MT
557 {
558 SplitHostPort(node.networkAddress, port, host);
559 if (port)
560 {
561 return port;
562 }
563 }
564 return 0;
565}
566
f8f61a6d 567#define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
344a051d 568extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
70c48c13 569extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3], ASSETCHAINS_ERAOPTIONS[3];
344a051d 570extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
f8f61a6d 571extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
f2d873d0 572extern std::string VERUS_CHAINNAME;
f8f61a6d 573
ccac80a3 574// adds the chain definition for this chain and nodes as well
575// this also sets up the notarization chain, if there is one
f8f61a6d 576bool SetThisChain(UniValue &chainDefinition)
9f0c14b2 577{
ccac80a3 578 ConnectedChains.ThisChain() = CPBaaSChainDefinition(chainDefinition);
f8f61a6d 579
e95f3021 580 if (!IsVerusActive())
581 {
582 CPBaaSChainDefinition &notaryChainDef = ConnectedChains.notaryChain.chainDefinition;
583 // we set the notary chain to either Verus or VerusTest
04486a9e 584 notaryChainDef.nVersion = PBAAS_VERSION;
e95f3021 585 if (PBAAS_TESTMODE)
586 {
587 // setup Verus test parameters
588 notaryChainDef.name = "VRSCTEST";
04c3ecfc 589 notaryChainDef.premine = 5000000000000000;
04486a9e 590 notaryChainDef.eras = 1;
591 notaryChainDef.rewards = std::vector<int64_t>({2400000000});
592 notaryChainDef.rewardsDecay = std::vector<int64_t>({0});
3cb437e6 593 notaryChainDef.halving = std::vector<int32_t>({225680});
04486a9e 594 notaryChainDef.eraEnd = std::vector<int32_t>({0});
f8391429 595 notaryChainDef.eraOptions = std::vector<int32_t>({0,0,0});
e95f3021 596 }
597 else
598 {
599 // first setup Verus parameters
600 notaryChainDef.name = "VRSC";
f8391429 601 notaryChainDef.premine = 0;
e95f3021 602 notaryChainDef.eras = 3;
603 notaryChainDef.rewards = std::vector<int64_t>({0,38400000000,2400000000});
604 notaryChainDef.rewardsDecay = std::vector<int64_t>({100000000,0,0});
f8391429 605 notaryChainDef.halving = std::vector<int32_t>({1,43200,1051920});
606 notaryChainDef.eraEnd = std::vector<int32_t>({10080,226080,0});
607 notaryChainDef.eraOptions = std::vector<int32_t>({0,0,0});
e95f3021 608 }
609 }
610
f8f61a6d 611 if (ConnectedChains.ThisChain().IsValid())
9f0c14b2 612 {
94a210aa
MT
613 memset(ASSETCHAINS_SYMBOL, 0, sizeof(ASSETCHAINS_SYMBOL));
614 strcpy(ASSETCHAINS_SYMBOL, ConnectedChains.ThisChain().name.c_str());
615
f8f61a6d 616 // set all command line parameters into mapArgs from chain definition
617 vector<string> nodeStrs;
618 for (auto node : ConnectedChains.ThisChain().nodes)
619 {
620 nodeStrs.push_back(node.networkAddress);
621 }
622 if (nodeStrs.size())
623 {
624 mapMultiArgs["-seednode"] = nodeStrs;
625 }
8d2c835d 626 if (int port = ConnectedChains.ThisChain().GetDefinedPort())
5d5737c1
MT
627 {
628 mapArgs["-port"] = to_string(port);
629 }
f8f61a6d 630
631 ASSETCHAINS_SUPPLY = ConnectedChains.ThisChain().premine;
f81da2f3 632 mapArgs["-ac_supply"] = to_string(ASSETCHAINS_SUPPLY);
f8f61a6d 633 ASSETCHAINS_ALGO = ASSETCHAINS_VERUSHASH;
634 ASSETCHAINS_LWMAPOS = 50;
635
636 ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF;
637 ASSETCHAINS_TIMEUNLOCKFROM = 0;
638 ASSETCHAINS_TIMEUNLOCKTO = 0;
639
640 auto numEras = ConnectedChains.ThisChain().eras;
641 ASSETCHAINS_LASTERA = numEras - 1;
642 mapArgs["-ac_eras"] = to_string(numEras);
643
644 mapArgs["-ac_end"] = "";
645 mapArgs["-ac_reward"] = "";
646 mapArgs["-ac_halving"] = "";
647 mapArgs["-ac_decay"] = "";
d7bbd546 648 mapArgs["-ac_options"] = "";
f8f61a6d 649
650 for (int j = 0; j < ASSETCHAINS_MAX_ERAS; j++)
651 {
652 if (j > ASSETCHAINS_LASTERA)
653 {
654 ASSETCHAINS_REWARD[j] = ASSETCHAINS_REWARD[j-1];
655 ASSETCHAINS_DECAY[j] = ASSETCHAINS_DECAY[j-1];
656 ASSETCHAINS_HALVING[j] = ASSETCHAINS_HALVING[j-1];
657 ASSETCHAINS_ENDSUBSIDY[j] = 0;
e5961f2e 658 ASSETCHAINS_ERAOPTIONS[j] = 0;
f8f61a6d 659 }
660 else
661 {
662 ASSETCHAINS_REWARD[j] = ConnectedChains.ThisChain().rewards[j];
663 ASSETCHAINS_DECAY[j] = ConnectedChains.ThisChain().rewardsDecay[j];
664 ASSETCHAINS_HALVING[j] = ConnectedChains.ThisChain().halving[j];
665 ASSETCHAINS_ENDSUBSIDY[j] = ConnectedChains.ThisChain().eraEnd[j];
e5961f2e 666 ASSETCHAINS_ERAOPTIONS[j] = ConnectedChains.ThisChain().eraOptions[j];
f8f61a6d 667 if (j == 0)
668 {
669 mapArgs["-ac_reward"] = to_string(ASSETCHAINS_REWARD[j]);
670 mapArgs["-ac_decay"] = to_string(ASSETCHAINS_DECAY[j]);
671 mapArgs["-ac_halving"] = to_string(ASSETCHAINS_HALVING[j]);
672 mapArgs["-ac_end"] = to_string(ASSETCHAINS_ENDSUBSIDY[j]);
70c48c13 673 mapArgs["-ac_options"] = to_string(ASSETCHAINS_ERAOPTIONS[j]);
f8f61a6d 674 }
675 else
676 {
677 mapArgs["-ac_reward"] += "," + to_string(ASSETCHAINS_REWARD[j]);
678 mapArgs["-ac_decay"] += "," + to_string(ASSETCHAINS_DECAY[j]);
679 mapArgs["-ac_halving"] += "," + to_string(ASSETCHAINS_HALVING[j]);
680 mapArgs["-ac_end"] += "," + to_string(ASSETCHAINS_ENDSUBSIDY[j]);
70c48c13 681 mapArgs["-ac_options"] += "," + to_string(ASSETCHAINS_ERAOPTIONS[j]);
f8f61a6d 682 }
683 }
684 }
685
686 PBAAS_STARTBLOCK = ConnectedChains.ThisChain().startBlock;
687 mapArgs["-startblock"] = to_string(PBAAS_STARTBLOCK);
688 PBAAS_ENDBLOCK = ConnectedChains.ThisChain().endBlock;
689 mapArgs["-endblock"] = to_string(PBAAS_ENDBLOCK);
690
40951949 691 PBAAS_PRECONVERSION = ConnectedChains.ThisChain().conversion;
692 PBAAS_MINPRECONVERT = ConnectedChains.ThisChain().minpreconvert;
693 PBAAS_MAXPRECONVERT = ConnectedChains.ThisChain().maxpreconvert;
694 mapArgs["-ac_conversion"] = to_string(PBAAS_PRECONVERSION);
695 mapArgs["-ac_minpreconvert"] = to_string(PBAAS_MINPRECONVERT);
696 mapArgs["-ac_maxpreconvert"] = to_string(PBAAS_MAXPRECONVERT);
f8f61a6d 697 return true;
698 }
699 else
700 {
701 return false;
9f0c14b2
MT
702 }
703}
704
b2a98c42
MT
705// ensures that the chain definition is valid and that there are no other definitions of the same name
706// that have been confirmed.
707bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
708{
709 // the chain definition output can be spent when the chain is at the end of its life and only then
710 // TODO
711 return false;
712}
713
714// ensures that the chain definition is valid and that there are no other definitions of the same name
715// that have been confirmed.
716bool CheckChainDefinitionOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
717{
718 // checked before a chain definition output script is accepted as a valid transaction
719
720 // basics - we need a chain definition transaction to kick off a PBaaS chain. it must have:
721 // 1) valid chain definition output with parameters in proper ranges and no duplicate name
722 // 2) notarization output with conformant values
723 // 3) finalization output
724 // 3) notarization funding
725 //
726
727 // get the source transaction
728 uint256 blkHash;
729 CTransaction thisTx;
6c5d1151 730 if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
b2a98c42
MT
731 {
732 LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
733 return false;
734 }
735
736 CPBaaSChainDefinition chainDef(thisTx, true);
737 CPBaaSNotarization notarization(thisTx, true);
738 CNotarizationFinalization finalization(thisTx, true);
739
740 if (!chainDef.IsValid() || !notarization.IsValid() || finalization.IsValid())
741 {
742 LogPrintf("transaction specified, %s, must have valid chain definition, notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
743 return false;
744 }
745
746 CPBaaSChainDefinition prior;
747 // this ensures that there is no other definition of the same name already on the blockchain
748 if (!GetChainDefinition(chainDef.name, prior))
749 {
750 LogPrintf("PBaaS chain with the name %s already exists\n", chainDef.name.c_str());
751 return false;
752 }
753
754 return true;
755}
756
989b1de1
MT
757CAmount CCrossChainExport::CalculateExportFee() const
758{
759 if (numInputs > MAX_EXPORT_INPUTS)
760 {
761 return 0;
762 }
763 static const arith_uint256 satoshis(100000000);
764
d23f44aa 765 int64_t ratio = 50000000 + ((25000000 / MAX_EXPORT_INPUTS) * (numInputs - 1));
989b1de1
MT
766
767 return (((arith_uint256(totalFees) * arith_uint256(ratio))) / satoshis).GetLow64();
768}
769
b2a98c42
MT
770bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
771{
2fd1f0fb 772 bool retval = false;
b2a98c42 773 LOCK(cs_mergemining);
23d61f0a 774
3016973b 775 //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
23d61f0a 776
b2a98c42
MT
777 auto chainIt = mergeMinedChains.find(chainID);
778 if (chainIt != mergeMinedChains.end())
779 {
780 arith_uint256 target;
781 target.SetCompact(chainIt->second.block.nBits);
782 for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
783 {
784 // make sure we don't just match by target
785 if (removeRange.first->second->GetChainID() == chainID)
786 {
787 mergeMinedTargets.erase(removeRange.first);
788 break;
789 }
790 }
791 mergeMinedChains.erase(chainID);
2fd1f0fb 792 dirty = retval = true;
b2a98c42
MT
793
794 // if we get to 0, give the thread a kick to stop waiting for mining
2fd1f0fb 795 //if (!mergeMinedChains.size())
796 //{
797 // sem_submitthread.post();
798 //}
b2a98c42 799 }
2fd1f0fb 800 return retval;
b2a98c42
MT
801}
802
803// remove merge mined chains added and not updated since a specific time
804uint32_t CConnectedChains::PruneOldChains(uint32_t pruneBefore)
805{
806 vector<uint160> toRemove;
807
808 LOCK(cs_mergemining);
809 for (auto blkData : mergeMinedChains)
810 {
811 if (blkData.second.block.nTime < pruneBefore)
812 {
813 toRemove.push_back(blkData.first);
814 }
815 }
816
817 for (auto id : toRemove)
818 {
3016973b 819 //printf("Pruning chainID: %s\n", id.GetHex().c_str());
b2a98c42
MT
820 RemoveMergedBlock(id);
821 }
822}
823
824// adds or updates merge mined blocks
825// returns false if failed to add
826bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
827{
b2a98c42
MT
828 // determine if we should replace one or add to the merge mine vector
829 {
830 LOCK(cs_mergemining);
831
2fd1f0fb 832 arith_uint256 target;
b2a98c42
MT
833 uint160 cID = blkData.GetChainID();
834 auto it = mergeMinedChains.find(cID);
835 if (it != mergeMinedChains.end())
836 {
1fa4454d 837 RemoveMergedBlock(cID); // remove it if already there
b2a98c42 838 }
1fa4454d 839 target.SetCompact(blkData.block.nBits);
23d61f0a 840
3016973b 841 //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
23d61f0a 842
1fa4454d 843 mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
2830db29 844 dirty = true;
b2a98c42 845 }
bce52b27 846 return true;
b2a98c42
MT
847}
848
849bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
850{
851 {
852 LOCK(cs_mergemining);
853 auto chainIt = mergeMinedChains.find(chainID);
854 if (chainIt != mergeMinedChains.end())
855 {
856 rpcChainData = (CRPCChainData)chainIt->second;
857 return true;
858 }
859 return false;
860 }
861}
862
863// this returns a pointer to the data without copy and assumes the lock is held
864CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
865{
866 {
867 auto chainIt = mergeMinedChains.find(chainID);
868 if (chainIt != mergeMinedChains.end())
869 {
870 return &chainIt->second;
871 }
872 return NULL;
873 }
874}
875
e771a884 876bool CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
877{
3016973b 878 //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
2830db29 879 {
880 LOCK(cs_mergemining);
cff3c5ad 881
2830db29 882 qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
883 }
e771a884 884 sem_submitthread.post();
885}
886
f8f61a6d 887// get the latest block header and submit one block at a time, returning after there are no more
888// matching blocks to be found
889vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
b2a98c42
MT
890{
891 std::set<uint160> inHeader;
f8f61a6d 892 bool submissionFound;
893 CPBaaSMergeMinedChainData chainData;
b2a98c42
MT
894 vector<pair<string, UniValue>> results;
895
f8f61a6d 896 CBlockHeader bh;
897 arith_uint256 lastHash;
b2a98c42
MT
898 CPBaaSBlockHeader pbh;
899
02560af7 900 do
b2a98c42 901 {
02560af7 902 submissionFound = false;
b2a98c42 903 {
02560af7 904 LOCK(cs_mergemining);
905 // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
906 // common, merge mined headers for notarization, drop out on any submission
907 for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
b2a98c42 908 {
02560af7 909 // add the PBaaS chain ids from this header to a set for search
910 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
f8f61a6d 911 {
02560af7 912 inHeader.insert(pbh.chainID);
913 }
b2a98c42 914
33d6f38a 915 uint160 chainID;
02560af7 916 // now look through all targets that are equal to or above the hash of this header
917 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
918 {
33d6f38a 919 chainID = chainIt->second->GetChainID();
02560af7 920 if (inHeader.count(chainID))
2830db29 921 {
02560af7 922 // first, check that the winning header matches the block that is there
923 CPBaaSPreHeader preHeader(chainIt->second->block);
924 preHeader.SetBlockData(headerIt->second);
925
926 // check if the block header matches the block's specific data, only then can we create a submission from this block
927 if (headerIt->second.CheckNonCanonicalData(chainID))
f8f61a6d 928 {
02560af7 929 // save block as is, remove the block from merged headers, replace header, and submit
930 chainData = *chainIt->second;
931
932 *(CBlockHeader *)&chainData.block = headerIt->second;
933
02560af7 934 submissionFound = true;
f8f61a6d 935 }
3016973b
MT
936 //else // not an error condition. code is here for debugging
937 //{
938 // printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
939 //}
cff3c5ad 940 }
3016973b
MT
941 //else // not an error condition. code is here for debugging
942 //{
943 // printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
944 //}
02560af7 945 }
2830db29 946
02560af7 947 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
33d6f38a
MT
948 if (submissionFound)
949 {
950 // once it is going to be submitted, remove block from this chain until a new one is added again
951 RemoveMergedBlock(chainID);
952 break;
953 }
954 else
02560af7 955 {
956 qualifiedHeaders.erase(headerIt);
b2a98c42 957 }
f8f61a6d 958 }
02560af7 959 }
960 if (submissionFound)
961 {
962 // submit one block and loop again. this approach allows multiple threads
963 // to collectively empty the submission queue, mitigating the impact of
964 // any one stalled daemon
965 UniValue submitParams(UniValue::VARR);
966 submitParams.push_back(EncodeHexBlk(chainData.block));
967 UniValue result, error;
968 try
f8f61a6d 969 {
02560af7 970 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
971 result = find_value(result, "result");
972 error = find_value(result, "error");
b2a98c42 973 }
02560af7 974 catch (exception e)
975 {
976 result = UniValue(e.what());
977 }
978 results.push_back(make_pair(chainData.chainDefinition.name, result));
979 if (result.isStr() || !error.isNull())
980 {
981 printf("Error submitting block to %s chain: %s\n", chainData.chainDefinition.name.c_str(), result.isStr() ? result.get_str().c_str() : error.get_str().c_str());
982 }
983 else
984 {
985 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
986 }
987 }
988 } while (submissionFound);
b2a98c42
MT
989 return results;
990}
991
f8f61a6d 992// add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
b2a98c42
MT
993uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
994{
995 vector<uint160> inHeader;
996 vector<UniValue> toCombine;
997 arith_uint256 blkHash = UintToArith256(bh.GetHash());
f8f61a6d 998 arith_uint256 target(0);
b2a98c42
MT
999
1000 CPBaaSBlockHeader pbh;
1001
b2a98c42 1002 {
f8f61a6d 1003 LOCK(cs_mergemining);
b2a98c42 1004
f8f61a6d 1005 CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
1006
1007 for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
b2a98c42 1008 {
f8f61a6d 1009 if (bh.GetPBaaSHeader(pbh, i))
1010 {
1011 inHeader.push_back(pbh.chainID);
1012 }
1013 }
1014
1015 // loop through the existing PBaaS chain ids in the header
2fd1f0fb 1016 // remove any that are not either this Chain ID or in our local collection and then add all that are present
f8f61a6d 1017 for (uint32_t i = 0; i < inHeader.size(); i++)
1018 {
1019 auto it = mergeMinedChains.find(inHeader[i]);
1020 if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
1021 {
1022 bh.DeletePBaaSHeader(i);
1023 }
b2a98c42 1024 }
b2a98c42 1025
b2a98c42
MT
1026 for (auto chain : mergeMinedChains)
1027 {
1028 // get the native PBaaS header for each chain and put it into the
1029 // header we are given
326f5f88 1030 // it must have itself in as a PBaaS header
b2a98c42 1031 uint160 cid = chain.second.GetChainID();
3a2a1777 1032 if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
b2a98c42
MT
1033 {
1034 if (!bh.AddUpdatePBaaSHeader(pbh))
1035 {
1036 LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
f8f61a6d 1037 break;
1038 }
1039 else
1040 {
1041 arith_uint256 t;
1042 t.SetCompact(chain.second.block.nBits);
1043 if (t > target)
1044 {
1045 target = t;
1046 }
b2a98c42
MT
1047 }
1048 }
326f5f88
MT
1049 else
1050 {
1051 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
1052 }
1053
b2a98c42 1054 }
2830db29 1055 dirty = false;
b2a98c42 1056 }
326f5f88 1057
f8f61a6d 1058 return target.GetCompact();
b2a98c42
MT
1059}
1060
ccac80a3 1061bool CConnectedChains::IsVerusPBaaSAvailable()
1062{
736e47d2 1063 return notaryChainVersion > "0.6" || notaryChainVersion == "0.5.8-5";
ccac80a3 1064}
1065
f8f61a6d 1066extern string PBAAS_HOST, PBAAS_USERPASS;
1067extern int32_t PBAAS_PORT;
6bb9fdbc 1068bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
9f0c14b2 1069{
6bb9fdbc 1070 if (chainInfoUni.isObject() && chainDefUni.isObject())
9f0c14b2 1071 {
6bb9fdbc 1072 UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
f8f61a6d 1073 if (uniVer.isStr())
1074 {
2156d3d0 1075 LOCK(cs_mergemining);
f8f61a6d 1076 notaryChainVersion = uni_get_str(uniVer);
6bb9fdbc 1077 notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
1078 CPBaaSChainDefinition chainDef(chainDefUni);
f8f61a6d 1079 notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
1080 }
9f0c14b2 1081 }
ccac80a3 1082 return IsVerusPBaaSAvailable();
9f0c14b2
MT
1083}
1084
2299bd95 1085bool CConnectedChains::CheckVerusPBaaSAvailable()
9f0c14b2
MT
1086{
1087 if (IsVerusActive())
1088 {
ccac80a3 1089 notaryChainVersion = "";
9f0c14b2
MT
1090 }
1091 else
1092 {
1093 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
2156d3d0 1094 // tolerate only 15 second timeout
9a891caf 1095 UniValue chainInfo, chainDef;
1096 try
1097 {
1098 UniValue params(UniValue::VARR);
2fd1f0fb 1099 chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
9a891caf 1100 if (!chainInfo.isNull())
1101 {
1102 params.push_back(VERUS_CHAINNAME);
2fd1f0fb 1103 chainDef = find_value(RPCCallRoot("getchaindefinition", params), "result");
9a891caf 1104
1105 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
1106 {
1107 return true;
1108 }
1109 }
1110 } catch (exception e)
4fa3b13d 1111 {
4fa3b13d 1112 }
9f0c14b2 1113 }
9a891caf 1114 notaryChainVersion = "";
4fa3b13d 1115 return false;
9f0c14b2
MT
1116}
1117
41f170fd
MT
1118CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
1119{
1120 CCoinbaseCurrencyState currencyState;
1121 CBlock block;
1122 LOCK(cs_main);
767f8e36 1123 bool isVerusActive = IsVerusActive();
1124 if (!isVerusActive &&
41f170fd
MT
1125 CConstVerusSolutionVector::activationHeight.ActiveVersion(height) >= CActivationHeight::SOLUTION_VERUSV3 &&
1126 height != 0 &&
1127 height <= chainActive.Height() &&
1128 chainActive[height] &&
88d014d0 1129 ReadBlockFromDisk(block, chainActive[height], Params().GetConsensus()) &&
1b1a31e7 1130 (currencyState = CCoinbaseCurrencyState(block.vtx[0])).IsValid())
41f170fd 1131 {
1b1a31e7 1132 return currencyState;
41f170fd 1133 }
7d8fcc31 1134 else
1135 {
1136 bool isReserve = (thisChain.ChainOptions() & thisChain.OPTION_RESERVE);
22e5b822 1137 CAmount preconvertedNative = currencyState.ReserveToNative(thisChain.preconverted, thisChain.conversion);
1138 CCurrencyState cState(thisChain.conversion, preconvertedNative, preconvertedNative, 0, isReserve ? thisChain.preconverted : 0, isReserve ? CCurrencyState::VALID + CCurrencyState::ISRESERVE : CCurrencyState::VALID);
2ba3b416 1139 CAmount fees = 0;
767f8e36 1140 if (!height && !isVerusActive)
2ba3b416 1141 {
1142 fees = CReserveTransactionDescriptor::CalculateAdditionalConversionFee(thisChain.preconverted);
1143 }
1144 return CCoinbaseCurrencyState(cState, thisChain.preconverted, 0, CReserveOutput(CReserveOutput::VALID, 0), thisChain.conversion, fees, fees);
7d8fcc31 1145 }
41f170fd
MT
1146}
1147
c3250dcd
MT
1148bool CConnectedChains::SetLatestMiningOutputs(const std::vector<pair<int, CScript>> minerOutputs, CTxDestination &firstDestinationOut)
1149{
1150 LOCK(cs_mergemining);
1151 {
1152 txnouttype outType;
1153 std::vector<std::vector<unsigned char>> vSolutions;
1154
1155 if (!minerOutputs.size() || !Solver(minerOutputs[0].second, outType, vSolutions))
1156 {
1157 return false;
1158 }
1159
1160 if (outType == TX_PUBKEY)
1161 {
1162 CPubKey pubKey(vSolutions[0]);
1163 if (!pubKey.IsValid())
1164 {
1165 return false;
1166 }
1167 firstDestinationOut = CTxDestination(pubKey);
1168 }
1169 else if (outType == TX_PUBKEYHASH)
1170 {
1171 firstDestinationOut = CTxDestination(CKeyID(uint160(vSolutions[0])));
1172 }
1173 else
1174 {
1175 return false;
1176 }
1177 }
1178 latestMiningOutputs = minerOutputs;
1179 latestDestination = firstDestinationOut;
bb6c3482 1180 return true;
c3250dcd
MT
1181}
1182
1183void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
1184{
1185 // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
1186 {
1187 if (!nHeight)
1188 {
1189 return;
1190 }
1191
1192 multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
1193
9ace9b55 1194 LOCK(cs_main);
1195
c3250dcd
MT
1196 // get all available transfer outputs to aggregate into export transactions
1197 if (GetUnspentChainTransfers(transferOutputs))
1198 {
bb6c3482 1199 if (!transferOutputs.size())
1200 {
1201 return;
1202 }
c3250dcd
MT
1203 std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
1204 std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1205
78a36746 1206 uint160 bookEnd;
1207 bookEnd.SetHex("ffffffffffffffffffffffffffffffffffffffff");
1208 uint160 lastChain = bookEnd;
41ad1f62 1209
78a36746 1210 // add a bookend entry at the end of transfer outputs to ensure that we try to export all before it
1211 transferOutputs.insert(make_pair(bookEnd, make_pair(CInputDescriptor(), CReserveTransfer())));
c3250dcd 1212
9ace9b55 1213 CPBaaSChainDefinition lastChainDef;
1214
78a36746 1215 // merge all of the common chainID outputs into common export transactions if either MIN_BLOCKS blocks have passed since the last
1216 // export of that type, or there are MIN_INPUTS or more outputs to aggregate
1217 for (auto it = transferOutputs.begin(); it != transferOutputs.end(); it++)
1218 {
1219 // get chain target and see if it is the same
1220 if (lastChain == bookEnd || it->first == lastChain)
c3250dcd 1221 {
78a36746 1222 txInputs.push_back(it->second);
78a36746 1223 }
1224 else
1225 {
1226 // when we get here, we have a consecutive number of transfer outputs to consume in txInputs
1227 // we need an unspent export output to export
1228 if (GetUnspentChainExports(lastChain, exportOutputs) && exportOutputs.size())
c3250dcd 1229 {
78a36746 1230 auto lastExport = *exportOutputs.begin();
c3250dcd 1231
78a36746 1232 if (((nHeight - lastExport.second.first) >= CCrossChainExport::MIN_BLOCKS) || (txInputs.size() >= CCrossChainExport::MIN_INPUTS))
c3250dcd
MT
1233 {
1234 boost::optional<CTransaction> oneExport;
1235
5cd8b21a 1236 if (!GetChainDefinition(lastChain, lastChainDef))
1237 {
1238 printf("%s: chain definition for export chainID %s not found\n\n", __func__, lastChain.GetHex().c_str());
1239 LogPrintf("%s: chain definition for export chainID %s not found\n\n", __func__, lastChain.GetHex().c_str());
1240 continue;
1241 }
1242
c3250dcd
MT
1243 // make one or more transactions that spends the last export and all possible cross chain transfers
1244 while (txInputs.size())
1245 {
1246 TransactionBuilder tb(Params().GetConsensus(), nHeight);
1247
1248 int numInputs = (txInputs.size() < CCrossChainExport::MAX_EXPORT_INPUTS) ? txInputs.size() : CCrossChainExport::MAX_EXPORT_INPUTS;
1249
1250 int inputsLeft = txInputs.size() - numInputs;
1251
1252 if (inputsLeft > 0 && inputsLeft < CCrossChainExport::MIN_INPUTS)
1253 {
1254 inputsLeft += CCrossChainExport::MIN_INPUTS - inputsLeft;
1255 numInputs -= CCrossChainExport::MIN_INPUTS - inputsLeft;
1256 assert(numInputs > 0);
1257 }
1258
1259 // each time through, we make one export transaction with the remainder or a subset of the
1260 // reserve transfer inputs. inputs can be:
1261 // 1. transfers of reserve for fractional reserve chains
1262 // 2. pre-conversions for pre-launch participation in the premine
1263 // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
1264 //
1265 // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
1266 // or reserve token inputs.
1267 //
1268 // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
1269 // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
1270 // as part of the import process
1271 //
1272 // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain.
1273 // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the
5cd8b21a 1274 // remaining Verus reserve coin will be burned on the PBaaS chain as spending it is allowed, once notarized, on the
1275 // Verus chain.
c3250dcd
MT
1276 //
1277 CAmount totalTxFees = 0;
1278 CAmount totalAmount = 0;
fe9545ee 1279 CAmount exportOutVal = 0;
c3250dcd
MT
1280 std::vector<CBaseChainObject *> chainObjects;
1281
1282 // first, we must add the export output from the current export thread to this chain
1283 if (oneExport.has_value())
1284 {
1285 // spend the last export transaction output
1286 CTransaction &tx = oneExport.get();
1287 COptCCParams p;
1288 int j;
1289 for (j = 0; j < tx.vout.size(); j++)
1290 {
1291 if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) && p.evalCode == EVAL_CROSSCHAIN_EXPORT)
1292 {
1293 break;
1294 }
1295 }
1296
1297 // had to be found and valid if we made the tx
1298 assert(j < tx.vout.size() && p.IsValid());
1299
1300 tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
fe9545ee 1301 exportOutVal = tx.vout[j].nValue;
c3250dcd
MT
1302 }
1303 else
1304 {
1305 // spend the recentExportIt output
78a36746 1306 tb.AddTransparentInput(lastExport.second.second.txIn.prevout, lastExport.second.second.scriptPubKey, lastExport.second.second.nValue);
fe9545ee 1307 exportOutVal = lastExport.second.second.nValue;
c3250dcd
MT
1308 }
1309
715182a4 1310 COptCCParams p;
81fc0174 1311 int fails = 0;
c3250dcd
MT
1312 for (int j = 0; j < numInputs; j++)
1313 {
1314 tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
dd1b8052 1315 if (IsVerusActive() && txInputs[j].second.nValue + txInputs[j].second.nFees > txInputs[j].first.nValue)
81fc0174 1316 {
dd1b8052 1317 // if this transfer is invalid and claims to carry more funds than it does, we consume it since it won't properly verify as a transfer, and
5cd8b21a 1318 // it is too expensive to let it force evaluation repeatedly. this condition should not get by normal checks, but just in case, don't let it slow transfers
1319 // we should formalize this into a chain contribution or amount adjustment.
1320 printf("%s: transaction %s claims more value than it contains\n", __func__, txInputs[j].first.txIn.prevout.hash.GetHex().c_str());
1321 LogPrintf("%s: transaction %s claims more value than it contains\n", __func__, txInputs[j].first.txIn.prevout.hash.GetHex().c_str());
81fc0174 1322 fails++;
1323 }
1324 else
1325 {
dd1b8052 1326 totalTxFees += txInputs[j].second.nFees;
1327 totalAmount += txInputs[j].second.nValue;
81fc0174 1328 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(txInputs[j].second), txInputs[j].second));
1329 }
1330 }
1331 if (fails == numInputs)
1332 {
1333 // erase the inputs we've attempted to spend
1334 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1335 continue;
c3250dcd
MT
1336 }
1337
1338 CCrossChainExport ccx(lastChain, numInputs, totalAmount, totalTxFees);
1339 CAmount exportFees = ccx.CalculateExportFee();
a71f8b64 1340 CReserveTransfer feeOut(CReserveTransfer::VALID + CReserveTransfer::SEND_BACK + CReserveTransfer::FEE_OUTPUT, exportFees, 0, GetDestinationID(feeOutput));
c3250dcd
MT
1341 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
1342
410af2c6 1343 // do a preliminary check
1344 CReserveTransactionDescriptor rtxd;
1345 std::vector<CTxOut> dummy;
9ace9b55 1346
5cd8b21a 1347 if (!rtxd.AddReserveTransferImportOutputs(lastChainDef, chainObjects, dummy))
410af2c6 1348 {
8bb25a86 1349 DeleteOpRetObjects(chainObjects);
1350
410af2c6 1351 printf("%s: failed to create valid exports\n", __func__);
1352 LogPrintf("%s: failed to create valid exports\n", __func__);
9ace9b55 1353
1354 // debugging output
8bb25a86 1355 printf("%s: failed to export outputs:\n", __func__);
9ace9b55 1356 for (auto oneout : dummy)
1357 {
9ace9b55 1358 UniValue uniOut;
1359 ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1360 printf("%s\n", uniOut.write(true, 2).c_str());
1361 }
410af2c6 1362 }
8bb25a86 1363 else
410af2c6 1364 {
8bb25a86 1365 // debugging output
1366 printf("%s: exported outputs:\n", __func__);
1367 for (auto oneout : dummy)
1368 {
8bb25a86 1369 UniValue uniOut;
1370 ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1371 printf("%s\n", uniOut.write(true, 2).c_str());
1372 }
c3250dcd 1373
8bb25a86 1374 CScript opRet = StoreOpRetArray(chainObjects);
1375 DeleteOpRetObjects(chainObjects);
c3250dcd 1376
8bb25a86 1377 CCcontract_info CC;
1378 CCcontract_info *cp;
1379 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
c3250dcd 1380
8bb25a86 1381 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
c3250dcd 1382
8bb25a86 1383 // send native amount of zero to a cross chain export output of the specific chain
1384 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_CROSSCHAIN_EXPORT))});
c3250dcd 1385
8bb25a86 1386 CTxOut exportOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_EXPORT, exportOutVal, pk, dests, ccx);
c3250dcd 1387
8bb25a86 1388 tb.AddTransparentOutput(exportOut.scriptPubKey, exportOutVal);
c3250dcd 1389
5cd8b21a 1390 // if we are on Verus chain, send all native funds to reserve deposit CC, which is equivalent to the reserve account
1391 // if we are on a PBaaS chain, input is burned when sent back for release
8bb25a86 1392 if (IsVerusActive())
1393 {
1394 cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
1395 pk = CPubKey(ParseHex(CC.CChexstr));
c3250dcd 1396
8bb25a86 1397 // send the entire amount to a reserve transfer output of the specific chain
1398 // we receive our fee on the other chain or when it comes back
1399 dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_RESERVE_DEPOSIT))});
c3250dcd 1400
8bb25a86 1401 CReserveOutput ro(CReserveOutput::VALID, totalAmount + totalTxFees);
c3250dcd 1402
8bb25a86 1403 CTxOut outToReserve = MakeCC1of1Vout(EVAL_RESERVE_DEPOSIT,
1404 ro.nValue,
1405 pk,
1406 dests,
1407 ro);
c3250dcd 1408
8bb25a86 1409 tb.AddTransparentOutput(outToReserve.scriptPubKey, ro.nValue);
1410 }
c3250dcd 1411
8bb25a86 1412 tb.AddOpRet(opRet);
1413 tb.SetFee(0);
c3250dcd 1414
88d014d0 1415 TransactionBuilderResult buildResult(tb.Build());
c3250dcd 1416
88d014d0 1417 if (!buildResult.IsError() && buildResult.IsTx())
c3250dcd 1418 {
8bb25a86 1419 // replace the last one only if we have a valid new one
88d014d0 1420 CTransaction tx = buildResult.GetTxOrThrow();
8bb25a86 1421
1422 LOCK2(cs_main, mempool.cs);
1423 static int lastHeight = 0;
1424 // remove conflicts, so that we get in
1425 std::list<CTransaction> removed;
1426 mempool.removeConflicts(tx, removed);
1427
1428 // add to mem pool, prioritize according to the fee we will get, and relay
1429 printf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1430 LogPrintf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1431 if (myAddtomempool(tx))
1432 {
1433 uint256 hash = tx.GetHash();
1434 mempool.PrioritiseTransaction(hash, hash.GetHex(), (double)(exportFees << 1), exportFees);
1435 RelayTransaction(tx);
1436 }
1437 }
1438 else
1439 {
1440 // we can't do any more useful work for this chain if we failed here
88d014d0 1441 printf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1442 LogPrintf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
8bb25a86 1443 break;
c3250dcd
MT
1444 }
1445 }
8bb25a86 1446
c3250dcd
MT
1447 // erase the inputs we've attempted to spend
1448 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1449 }
1450 }
1451 }
1452 }
9ace9b55 1453 lastChain = it->first;
c3250dcd
MT
1454 }
1455 }
1456 }
1457}
1458
1459// send new imports from this chain to the specified chain, which generally will be the notary chain
1460void CConnectedChains::SendNewImports(const uint160 &chainID,
1461 const CPBaaSNotarization &notarization,
1462 const uint256 &lastExportTx,
1463 const CTransaction &lastCrossImport,
1464 const CTransaction &lastExport)
1465{
1466 // currently only support sending imports to
1467}
1468
b2a98c42
MT
1469void CConnectedChains::SubmissionThread()
1470{
1471 try
1472 {
1473 arith_uint256 lastHash;
687e93d5
MT
1474 int64_t lastImportTime = 0;
1475 uint32_t lastHeight = 0;
b2a98c42 1476
a82942e4 1477 // wait for something to check on, then submit blocks that should be submitted
b2a98c42
MT
1478 while (true)
1479 {
c3250dcd
MT
1480 boost::this_thread::interruption_point();
1481
9f0c14b2 1482 if (IsVerusActive())
b2a98c42 1483 {
23d61f0a 1484 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
9014248c 1485 //printf("SubmissionThread: pruning\n");
c3250dcd 1486 PruneOldChains(GetAdjustedTime() - 300);
2fd1f0fb 1487 bool submit = false;
b2a98c42 1488 {
2fd1f0fb 1489 LOCK(cs_mergemining);
bea3e6a2
MT
1490 if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
1491 {
326f5f88 1492 qualifiedHeaders.clear();
bea3e6a2 1493 }
2fd1f0fb 1494 submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
23d61f0a 1495
9014248c 1496 //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
2fd1f0fb 1497 }
1498 if (submit)
1499 {
9014248c 1500 //printf("SubmissionThread: calling submit qualified blocks\n");
2fd1f0fb 1501 SubmitQualifiedBlocks();
b2a98c42 1502 }
9f0c14b2
MT
1503 else
1504 {
9014248c 1505 //printf("SubmissionThread: waiting on sem\n");
2fd1f0fb 1506 sem_submitthread.wait();
9f0c14b2 1507 }
b2a98c42
MT
1508 }
1509 else
1510 {
9f0c14b2 1511 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
5a72525a 1512 if (CheckVerusPBaaSAvailable())
2830db29 1513 {
5a72525a 1514 // check to see if we have recently earned a block with an earned notarization that qualifies for
1515 // submitting an accepted notarization
1516 if (earnedNotarizationHeight)
572c763f 1517 {
5a72525a 1518 CBlock blk;
1519 int32_t txIndex = -1, height;
572c763f 1520 {
5a72525a 1521 LOCK(cs_mergemining);
1522 if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
1523 {
1524 blk = earnedNotarizationBlock;
1525 earnedNotarizationBlock = CBlock();
1526 txIndex = earnedNotarizationIndex;
1527 height = earnedNotarizationHeight;
1528 earnedNotarizationHeight = 0;
1529 }
572c763f 1530 }
572c763f 1531
5a72525a 1532 if (txIndex != -1)
d6bc5de8 1533 {
5a72525a 1534 //printf("SubmissionThread: testing notarization\n");
1535 CTransaction lastConfirmed;
1536 uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
1537
1538 if (!txId.IsNull())
1539 {
1540 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1541 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1542 }
d6bc5de8 1543 }
572c763f 1544 }
687e93d5 1545
5a72525a 1546 // every "n" seconds, look for imports to include in our blocks from the Verus chain
1547 if ((GetAdjustedTime() - lastImportTime) >= 30 || lastHeight < (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight()))
1548 {
1549 lastImportTime = GetAdjustedTime();
1550 lastHeight = (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight());
687e93d5 1551
5a72525a 1552 // see if our notary has a confirmed notarization for us
1553 UniValue params(UniValue::VARR);
1554 UniValue result;
687e93d5 1555
5a72525a 1556 params.push_back(thisChain.name);
687e93d5 1557
5a72525a 1558 try
1559 {
1560 result = find_value(RPCCallRoot("getlastimportin", params), "result");
1561 } catch (exception e)
1562 {
1563 result = NullUniValue;
1564 }
687e93d5 1565
5a72525a 1566 if (!result.isNull())
1567 {
1568 auto txUniStr = find_value(result, "lastimporttransaction");
1569 auto txLastConfirmedStr = find_value(result, "lastconfirmednotarization");
1570 auto txTemplateStr = find_value(result, "importtxtemplate");
d83c7ae8 1571 CAmount totalImportAvailable = uni_get_int64(find_value(result, "totalimportavailable"));
687e93d5 1572
5a72525a 1573 CTransaction lastImportTx, lastConfirmedTx, templateTx;
687e93d5 1574
5a72525a 1575 if (txUniStr.isStr() && txTemplateStr.isStr() &&
1576 DecodeHexTx(lastImportTx, txUniStr.get_str()) &&
1577 DecodeHexTx(lastConfirmedTx, txLastConfirmedStr.get_str()) &&
1578 DecodeHexTx(templateTx, txTemplateStr.get_str()))
687e93d5 1579 {
5a72525a 1580 std::vector<CTransaction> importTxes;
05075a67 1581 if (CreateLatestImports(notaryChain.chainDefinition, lastImportTx, templateTx, lastConfirmedTx, totalImportAvailable, importTxes))
687e93d5 1582 {
5a72525a 1583 for (auto importTx : importTxes)
687e93d5 1584 {
5a72525a 1585 UniValue txResult;
70506d95 1586 params.setArray();
5a72525a 1587 params.push_back(EncodeHexTx(importTx));
1588
1589 try
687e93d5 1590 {
5a72525a 1591 txResult = find_value(RPCCallRoot("signrawtransaction", params), "result");
fa6b4d41 1592 if (txResult.isObject() && !(txResult = find_value(txResult, "hex")).isNull() && txResult.isStr() && txResult.get_str().size())
5a72525a 1593 {
fa6b4d41 1594 params.setArray();
5a72525a 1595 params.push_back(txResult);
1596 txResult = find_value(RPCCallRoot("sendrawtransaction", params), "result");
1597 }
1598 else
1599 {
1600 txResult = NullUniValue;
1601 }
1602
1603 } catch (exception e)
687e93d5
MT
1604 {
1605 txResult = NullUniValue;
1606 }
5a72525a 1607 uint256 testId;
1608 if (txResult.isStr())
1609 {
1610 testId.SetHex(txResult.get_str());
1611 }
1612 if (testId.IsNull())
1613 {
1614 break;
1615 }
687e93d5
MT
1616 }
1617 }
1618 }
1619 }
1620 }
1621 }
1622 sleep(3);
b2a98c42 1623 }
b2a98c42
MT
1624 boost::this_thread::interruption_point();
1625 }
1626 }
1627 catch (const boost::thread_interrupted&)
1628 {
1629 LogPrintf("Verus merge mining thread terminated\n");
1630 }
1631}
9f0c14b2 1632
b2a98c42
MT
1633void CConnectedChains::SubmissionThreadStub()
1634{
1635 ConnectedChains.SubmissionThread();
1636}
1637
572c763f
MT
1638void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
1639{
1640 // called after winning a block that contains an earned notarization
1641 // the earned notarization and its height are queued for processing by the submission thread
1642 // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
1643 // kept
1644 LOCK(cs_mergemining);
1645
94a210aa 1646 // we only care about the last
572c763f 1647 earnedNotarizationHeight = height;
94a210aa
MT
1648 earnedNotarizationBlock = blk;
1649 earnedNotarizationIndex = txIndex;
572c763f
MT
1650}
1651
68b309c0
MT
1652bool IsChainDefinitionInput(const CScript &scriptSig)
1653{
1654 uint32_t ecode;
1655 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_PBAASDEFINITION;
1656}
13ed2980 1657
This page took 0.372377 seconds and 4 git commands to generate.