1 /********************************************************************
2 * (C) 2019 Michael Toutonghi
4 * Distributed under the MIT software license, see the accompanying
5 * file COPYING or http://www.opensource.org/licenses/mit-license.php.
7 * This provides support for PBaaS initialization, notarization, and cross-chain token
8 * transactions and enabling liquid or non-liquid tokens across the
13 #include "pbaas/pbaas.h"
14 #include "pbaas/notarization.h"
15 #include "rpc/pbaasrpc.h"
16 #include "pbaas/crosschainrpc.h"
20 #include "transaction_builder.h"
24 CConnectedChains ConnectedChains;
28 return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 0 || strcmp(ASSETCHAINS_SYMBOL, "VRSCTEST") == 0);
31 // this adds an opret to a mutable transaction and returns the voutnum if it could be added
32 int32_t AddOpRetOutput(CMutableTransaction &mtx, const CScript &opRetScript)
34 if (opRetScript.IsOpReturn() && opRetScript.size() <= MAX_OP_RETURN_RELAY)
36 CTxOut vOut = CTxOut();
37 vOut.scriptPubKey = opRetScript;
39 mtx.vout.push_back(vOut);
40 return mtx.vout.size() - 1;
48 // returns a pointer to a base chain object, which can be cast to the
49 // object type indicated in its objType member
50 uint256 GetChainObjectHash(const CBaseChainObject &bo)
53 const CChainObject<CBlockHeader> *pNewHeader;
54 const CChainObject<CTransaction> *pNewTx;
55 const CChainObject<CMerkleBranch> *pNewProof;
56 const CChainObject<CHeaderRef> *pNewHeaderRef;
57 const CChainObject<CPriorBlocksCommitment> *pPriors;
58 const CChainObject<CReserveTransfer> *pExport;
59 const CChainObject<CCrossChainProof> *pCrossChainProof;
60 const CBaseChainObject *retPtr;
68 return pNewHeader->GetHash();
70 case CHAINOBJ_TRANSACTION:
71 return pNewTx->GetHash();
74 return pNewProof->GetHash();
76 case CHAINOBJ_HEADER_REF:
77 return pNewHeaderRef->GetHash();
79 case CHAINOBJ_PRIORBLOCKS:
80 return pPriors->GetHash();
82 case CHAINOBJ_RESERVETRANSFER:
83 return pExport->GetHash();
85 case CHAINOBJ_CROSSCHAINPROOF:
86 return pCrossChainProof->GetHash();
92 // used to export coins from one chain to another, if they are not native, they are represented on the other
94 bool ValidateCrossChainExport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
99 bool IsCrossChainExportInput(const CScript &scriptSig)
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
105 bool ValidateCrossChainImport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
109 bool IsCrossChainImportInput(const CScript &scriptSig)
113 // used to validate a specific service reward based on the spending transaction
114 bool ValidateServiceReward(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
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
122 bool IsServiceRewardInput(const CScript &scriptSig)
126 // used as a proxy token output for a reserve currency on its fractional reserve chain
127 bool ValidateReserveOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
131 bool IsReserveOutputInput(const CScript &scriptSig)
135 bool ValidateReserveTransfer(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
139 bool IsReserveTransferInput(const CScript &scriptSig)
143 bool ValidateReserveDeposit(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
147 bool IsReserveDepositInput(const CScript &scriptSig)
151 bool ValidateCurrencyState(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
155 bool IsCurrencyStateInput(const CScript &scriptSig)
159 // used to convert a fractional reserve currency into its reserve and back
160 bool ValidateReserveExchange(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
164 bool IsReserveExchangeInput(const CScript &scriptSig)
170 * Verifies that the input objects match the hashes and returns the transaction.
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.
178 bool ValidateOpretProof(CScript &opRet, COpRetProof &orProof)
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
184 int8_t ObjTypeCode(const CBlockHeader &obj)
186 return CHAINOBJ_HEADER;
189 int8_t ObjTypeCode(const CMerkleBranch &obj)
191 return CHAINOBJ_PROOF;
194 int8_t ObjTypeCode(const CTransaction &obj)
196 return CHAINOBJ_TRANSACTION;
199 int8_t ObjTypeCode(const CHeaderRef &obj)
201 return CHAINOBJ_HEADER_REF;
204 int8_t ObjTypeCode(const CPriorBlocksCommitment &obj)
206 return CHAINOBJ_PRIORBLOCKS;
209 int8_t ObjTypeCode(const CReserveTransfer &obj)
211 return CHAINOBJ_RESERVETRANSFER;
214 int8_t ObjTypeCode(const CCrossChainProof &obj)
216 return CHAINOBJ_CROSSCHAINPROOF;
219 // this adds an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction
220 CScript StoreOpRetArray(std::vector<CBaseChainObject *> &objPtrs)
223 CDataStream s = CDataStream(SER_NETWORK, PROTOCOL_VERSION);
224 s << (int32_t)OPRETTYPE_OBJECTARR;
227 for (auto pobj : objPtrs)
231 if (!DehydrateChainObject(s, pobj))
237 catch(const std::exception& e)
239 std::cerr << e.what() << '\n';
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());
248 std::vector<unsigned char> vch(s.begin(), s.end());
249 return error ? CScript() : CScript() << OP_RETURN << vch;
252 void DeleteOpRetObjects(std::vector<CBaseChainObject *> &ora)
254 for (auto pobj : ora)
256 switch(pobj->objectType)
258 case CHAINOBJ_HEADER:
260 delete (CChainObject<CBlockHeader> *)pobj;
264 case CHAINOBJ_TRANSACTION:
266 delete (CChainObject<CTransaction> *)pobj;
272 delete (CChainObject<CMerkleBranch> *)pobj;
276 case CHAINOBJ_HEADER_REF:
278 delete (CChainObject<CHeaderRef> *)pobj;
282 case CHAINOBJ_PRIORBLOCKS:
284 delete (CChainObject<CPriorBlocksCommitment> *)pobj;
288 case CHAINOBJ_RESERVETRANSFER:
290 delete (CChainObject<CReserveTransfer> *)pobj;
294 case CHAINOBJ_CROSSCHAINPROOF:
296 delete (CChainObject<CCrossChainProof> *)pobj;
307 std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
309 std::vector<unsigned char> vch;
310 std::vector<CBaseChainObject *> vRet;
311 if (opRetScript.IsOpReturn() && GetOpReturnData(opRetScript, vch) && vch.size() > 0)
313 CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION);
320 if (opRetType == OPRETTYPE_OBJECTARR)
322 CBaseChainObject *pobj;
323 while (!s.empty() && (pobj = RehydrateChainObject(s)))
325 vRet.push_back(pobj);
329 printf("failed to load all objects in opret");
330 DeleteOpRetObjects(vRet);
335 catch(const std::exception& e)
337 std::cerr << e.what() << '\n';
338 DeleteOpRetObjects(vRet);
345 CNodeData::CNodeData(UniValue &obj)
347 networkAddress = uni_get_str(find_value(obj, "networkaddress"));
348 CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
349 ba.GetKeyID(paymentAddress);
352 UniValue CNodeData::ToUniValue() const
354 UniValue obj(UniValue::VOBJ);
355 obj.push_back(Pair("networkaddress", networkAddress));
356 obj.push_back(Pair("paymentaddress", CBitcoinAddress(paymentAddress).ToString()));
360 CPBaaSChainDefinition::CPBaaSChainDefinition(const UniValue &obj)
362 nVersion = PBAAS_VERSION;
363 name = std::string(uni_get_str(find_value(obj, "name")), 0, (KOMODO_ASSETCHAIN_MAXLEN - 1));
365 string invalidChars = "\\/:?\"<>|";
366 for (int i = 0; i < name.size(); i++)
368 if (invalidChars.find(name[i]) != string::npos)
374 CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
375 ba.GetKeyID(address);
376 premine = uni_get_int64(find_value(obj, "premine"));
377 initialcontribution = uni_get_int64(find_value(obj, "initialcontribution"));
378 conversion = uni_get_int64(find_value(obj, "conversion"));
379 minpreconvert = uni_get_int64(find_value(obj, "minpreconvert"));
380 maxpreconvert = uni_get_int64(find_value(obj, "maxpreconvert"));
381 preconverted = uni_get_int64(find_value(obj, "preconverted"));
382 launchFee = uni_get_int64(find_value(obj, "launchfee"));
383 startBlock = uni_get_int(find_value(obj, "startblock"));
384 endBlock = uni_get_int(find_value(obj, "endblock"));
386 auto vEras = uni_getValues(find_value(obj, "eras"));
387 if (vEras.size() > ASSETCHAINS_MAX_ERAS)
389 vEras.resize(ASSETCHAINS_MAX_ERAS);
391 eras = !vEras.size() ? 1 : vEras.size();
393 for (auto era : vEras)
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")));
402 billingPeriod = uni_get_int(find_value(obj, "billingperiod"));
403 notarizationReward = uni_get_int64(find_value(obj, "notarizationreward"));
405 auto nodeVec = uni_getValues(find_value(obj, "nodes"));
406 for (auto node : nodeVec)
408 nodes.push_back(CNodeData(node));
412 CPBaaSChainDefinition::CPBaaSChainDefinition(const CTransaction &tx, bool validate)
414 bool definitionFound = false;
415 nVersion = PBAAS_VERSION_INVALID;
416 for (auto out : tx.vout)
419 if (IsPayToCryptoCondition(out.scriptPubKey, p))
421 if (p.evalCode == EVAL_PBAASDEFINITION)
425 nVersion = PBAAS_VERSION_INVALID;
429 FromVector(p.vData[0], *this);
431 // TODO - remove this after validation is finished, check now in case some larger strings got into the chain
432 name = std::string(name, 0, (KOMODO_ASSETCHAIN_MAXLEN - 1));
433 string invalidChars = "\\/:?\"<>|";
434 for (int i = 0; i < name.size(); i++)
436 if (invalidChars.find(name[i]) != string::npos)
442 definitionFound = true;
454 CServiceReward::CServiceReward(const CTransaction &tx, bool validate)
456 nVersion = PBAAS_VERSION_INVALID;
457 for (auto out : tx.vout)
460 if (IsPayToCryptoCondition(out.scriptPubKey, p))
462 // always take the first for now
463 if (p.evalCode == EVAL_SERVICEREWARD)
465 FromVector(p.vData[0], *this);
477 CCrossChainExport::CCrossChainExport(const CTransaction &tx)
479 for (auto out : tx.vout)
482 if (IsPayToCryptoCondition(out.scriptPubKey, p))
484 // always take the first for now
485 if (p.evalCode == EVAL_CROSSCHAIN_EXPORT)
487 FromVector(p.vData[0], *this);
494 uint160 CPBaaSChainDefinition::GetChainID(std::string name)
496 const char *chainName = name.c_str();
497 uint256 chainHash = Hash(chainName, chainName + strlen(chainName));
498 return Hash160(chainHash.begin(), chainHash.end());
501 uint160 CPBaaSChainDefinition::GetConditionID(int32_t condition) const
503 return CCrossChainRPCData::GetConditionID(name, condition);
506 UniValue CPBaaSChainDefinition::ToUniValue() const
508 UniValue obj(UniValue::VOBJ);
509 obj.push_back(Pair("version", (int64_t)nVersion));
510 obj.push_back(Pair("name", name));
511 obj.push_back(Pair("chainid", GetChainID().GetHex()));
512 obj.push_back(Pair("chainaddress", EncodeDestination(CTxDestination(CKeyID(GetChainID())))));
513 obj.push_back(Pair("paymentaddress", EncodeDestination(CTxDestination(address))));
514 obj.push_back(Pair("premine", (int64_t)premine));
515 obj.push_back(Pair("initialcontribution", (int64_t)initialcontribution));
516 obj.push_back(Pair("conversion", (int64_t)conversion));
517 obj.push_back(Pair("minpreconvert", (int64_t)minpreconvert));
518 obj.push_back(Pair("maxpreconvert", (int64_t)maxpreconvert));
519 obj.push_back(Pair("preconverted", (int64_t)preconverted));
520 obj.push_back(Pair("launchfee", (int64_t)launchFee));
521 obj.push_back(Pair("conversionpercent", ValueFromAmount(conversion * 100)));
522 obj.push_back(Pair("launchfeepercent", ValueFromAmount(launchFee * 100)));
523 obj.push_back(Pair("startblock", (int32_t)startBlock));
524 obj.push_back(Pair("endblock", (int32_t)endBlock));
526 UniValue eraArr(UniValue::VARR);
527 for (int i = 0; i < eras; i++)
529 UniValue era(UniValue::VOBJ);
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));
535 eraArr.push_back(era);
537 obj.push_back(Pair("eras", eraArr));
539 obj.push_back(Pair("billingperiod", billingPeriod));
540 obj.push_back(Pair("notarizationreward", notarizationReward));
542 UniValue nodeArr(UniValue::VARR);
543 for (auto node : nodes)
545 nodeArr.push_back(node.ToUniValue());
547 obj.push_back(Pair("nodes", nodeArr));
552 int CPBaaSChainDefinition::GetDefinedPort() const
556 for (auto node : nodes)
558 SplitHostPort(node.networkAddress, port, host);
567 #define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
568 extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
569 extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3], ASSETCHAINS_ERAOPTIONS[3];
570 extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
571 extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
572 extern std::string VERUS_CHAINNAME;
574 // adds the chain definition for this chain and nodes as well
575 // this also sets up the notarization chain, if there is one
576 bool SetThisChain(UniValue &chainDefinition)
578 ConnectedChains.ThisChain() = CPBaaSChainDefinition(chainDefinition);
580 if (!IsVerusActive())
582 CPBaaSChainDefinition ¬aryChainDef = ConnectedChains.notaryChain.chainDefinition;
583 // we set the notary chain to either Verus or VerusTest
584 notaryChainDef.nVersion = PBAAS_VERSION;
587 // setup Verus test parameters
588 notaryChainDef.name = "VRSCTEST";
589 notaryChainDef.premine = 5000000000000000;
590 notaryChainDef.eras = 1;
591 notaryChainDef.rewards = std::vector<int64_t>({2400000000});
592 notaryChainDef.rewardsDecay = std::vector<int64_t>({0});
593 notaryChainDef.halving = std::vector<int32_t>({226080});
594 notaryChainDef.eraEnd = std::vector<int32_t>({0});
595 notaryChainDef.eraOptions = std::vector<int32_t>({0,0,0});
599 // first setup Verus parameters
600 notaryChainDef.name = "VRSC";
601 notaryChainDef.premine = 0;
602 notaryChainDef.eras = 3;
603 notaryChainDef.rewards = std::vector<int64_t>({0,38400000000,2400000000});
604 notaryChainDef.rewardsDecay = std::vector<int64_t>({100000000,0,0});
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});
611 if (ConnectedChains.ThisChain().IsValid())
613 memset(ASSETCHAINS_SYMBOL, 0, sizeof(ASSETCHAINS_SYMBOL));
614 strcpy(ASSETCHAINS_SYMBOL, ConnectedChains.ThisChain().name.c_str());
616 // set all command line parameters into mapArgs from chain definition
617 vector<string> nodeStrs;
618 for (auto node : ConnectedChains.ThisChain().nodes)
620 nodeStrs.push_back(node.networkAddress);
624 mapMultiArgs["-seednode"] = nodeStrs;
626 if (int port = ConnectedChains.ThisChain().GetDefinedPort())
628 mapArgs["-port"] = to_string(port);
631 ASSETCHAINS_SUPPLY = ConnectedChains.ThisChain().premine;
632 mapArgs["-ac_supply"] = to_string(ASSETCHAINS_SUPPLY);
633 ASSETCHAINS_ALGO = ASSETCHAINS_VERUSHASH;
634 ASSETCHAINS_LWMAPOS = 50;
636 ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF;
637 ASSETCHAINS_TIMEUNLOCKFROM = 0;
638 ASSETCHAINS_TIMEUNLOCKTO = 0;
640 auto numEras = ConnectedChains.ThisChain().eras;
641 ASSETCHAINS_LASTERA = numEras - 1;
642 mapArgs["-ac_eras"] = to_string(numEras);
644 mapArgs["-ac_end"] = "";
645 mapArgs["-ac_reward"] = "";
646 mapArgs["-ac_halving"] = "";
647 mapArgs["-ac_decay"] = "";
648 mapArgs["-ac_options"] = "";
650 for (int j = 0; j < ASSETCHAINS_MAX_ERAS; j++)
652 if (j > ASSETCHAINS_LASTERA)
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;
658 ASSETCHAINS_ERAOPTIONS[j] = 0;
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];
666 ASSETCHAINS_ERAOPTIONS[j] = ConnectedChains.ThisChain().eraOptions[j];
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]);
673 mapArgs["-ac_options"] = to_string(ASSETCHAINS_ERAOPTIONS[j]);
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]);
681 mapArgs["-ac_options"] += "," + to_string(ASSETCHAINS_ERAOPTIONS[j]);
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);
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);
705 // ensures that the chain definition is valid and that there are no other definitions of the same name
706 // that have been confirmed.
707 bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
709 // the chain definition output can be spent when the chain is at the end of its life and only then
714 // ensures that the chain definition is valid and that there are no other definitions of the same name
715 // that have been confirmed.
716 bool CheckChainDefinitionOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
718 // checked before a chain definition output script is accepted as a valid transaction
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
727 // get the source transaction
730 if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
732 LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
736 CPBaaSChainDefinition chainDef(thisTx, true);
737 CPBaaSNotarization notarization(thisTx, true);
738 CNotarizationFinalization finalization(thisTx, true);
740 if (!chainDef.IsValid() || !notarization.IsValid() || finalization.IsValid())
742 LogPrintf("transaction specified, %s, must have valid chain definition, notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
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))
750 LogPrintf("PBaaS chain with the name %s already exists\n", chainDef.name.c_str());
757 CAmount CCrossChainExport::CalculateExportFee() const
759 if (numInputs > MAX_EXPORT_INPUTS)
763 static const arith_uint256 satoshis(100000000);
765 int64_t ratio = 50000000 + ((25000000 / MAX_EXPORT_INPUTS) * (numInputs - 1));
767 return (((arith_uint256(totalFees) * arith_uint256(ratio))) / satoshis).GetLow64();
770 bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
773 LOCK(cs_mergemining);
775 //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
777 auto chainIt = mergeMinedChains.find(chainID);
778 if (chainIt != mergeMinedChains.end())
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++)
784 // make sure we don't just match by target
785 if (removeRange.first->second->GetChainID() == chainID)
787 mergeMinedTargets.erase(removeRange.first);
791 mergeMinedChains.erase(chainID);
792 dirty = retval = true;
794 // if we get to 0, give the thread a kick to stop waiting for mining
795 //if (!mergeMinedChains.size())
797 // sem_submitthread.post();
803 // remove merge mined chains added and not updated since a specific time
804 uint32_t CConnectedChains::PruneOldChains(uint32_t pruneBefore)
806 vector<uint160> toRemove;
808 LOCK(cs_mergemining);
809 for (auto blkData : mergeMinedChains)
811 if (blkData.second.block.nTime < pruneBefore)
813 toRemove.push_back(blkData.first);
817 for (auto id : toRemove)
819 //printf("Pruning chainID: %s\n", id.GetHex().c_str());
820 RemoveMergedBlock(id);
824 // adds or updates merge mined blocks
825 // returns false if failed to add
826 bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
828 // determine if we should replace one or add to the merge mine vector
830 LOCK(cs_mergemining);
832 arith_uint256 target;
833 uint160 cID = blkData.GetChainID();
834 auto it = mergeMinedChains.find(cID);
835 if (it != mergeMinedChains.end())
837 RemoveMergedBlock(cID); // remove it if already there
839 target.SetCompact(blkData.block.nBits);
841 //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
843 mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
849 bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
852 LOCK(cs_mergemining);
853 auto chainIt = mergeMinedChains.find(chainID);
854 if (chainIt != mergeMinedChains.end())
856 rpcChainData = (CRPCChainData)chainIt->second;
863 // this returns a pointer to the data without copy and assumes the lock is held
864 CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
867 auto chainIt = mergeMinedChains.find(chainID);
868 if (chainIt != mergeMinedChains.end())
870 return &chainIt->second;
876 bool CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
878 //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
880 LOCK(cs_mergemining);
882 qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
884 sem_submitthread.post();
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
889 vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
891 std::set<uint160> inHeader;
892 bool submissionFound;
893 CPBaaSMergeMinedChainData chainData;
894 vector<pair<string, UniValue>> results;
897 arith_uint256 lastHash;
898 CPBaaSBlockHeader pbh;
902 submissionFound = false;
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())
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++)
912 inHeader.insert(pbh.chainID);
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++)
919 chainID = chainIt->second->GetChainID();
920 if (inHeader.count(chainID))
922 // first, check that the winning header matches the block that is there
923 CPBaaSPreHeader preHeader(chainIt->second->block);
924 preHeader.SetBlockData(headerIt->second);
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))
929 // save block as is, remove the block from merged headers, replace header, and submit
930 chainData = *chainIt->second;
932 *(CBlockHeader *)&chainData.block = headerIt->second;
934 submissionFound = true;
936 //else // not an error condition. code is here for debugging
938 // printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
941 //else // not an error condition. code is here for debugging
943 // printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
947 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
950 // once it is going to be submitted, remove block from this chain until a new one is added again
951 RemoveMergedBlock(chainID);
956 qualifiedHeaders.erase(headerIt);
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;
970 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
971 result = find_value(result, "result");
972 error = find_value(result, "error");
976 result = UniValue(e.what());
978 results.push_back(make_pair(chainData.chainDefinition.name, result));
979 if (result.isStr() || !error.isNull())
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());
985 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
988 } while (submissionFound);
992 // add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
993 uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
995 vector<uint160> inHeader;
996 vector<UniValue> toCombine;
997 arith_uint256 blkHash = UintToArith256(bh.GetHash());
998 arith_uint256 target(0);
1000 CPBaaSBlockHeader pbh;
1003 LOCK(cs_mergemining);
1005 CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
1007 for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
1009 if (bh.GetPBaaSHeader(pbh, i))
1011 inHeader.push_back(pbh.chainID);
1015 // loop through the existing PBaaS chain ids in the header
1016 // remove any that are not either this Chain ID or in our local collection and then add all that are present
1017 for (uint32_t i = 0; i < inHeader.size(); i++)
1019 auto it = mergeMinedChains.find(inHeader[i]);
1020 if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
1022 bh.DeletePBaaSHeader(i);
1026 for (auto chain : mergeMinedChains)
1028 // get the native PBaaS header for each chain and put it into the
1029 // header we are given
1030 // it must have itself in as a PBaaS header
1031 uint160 cid = chain.second.GetChainID();
1032 if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
1034 if (!bh.AddUpdatePBaaSHeader(pbh))
1036 LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
1042 t.SetCompact(chain.second.block.nBits);
1051 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
1058 return target.GetCompact();
1061 bool CConnectedChains::IsVerusPBaaSAvailable()
1063 return notaryChainVersion > "0.6";
1066 extern string PBAAS_HOST, PBAAS_USERPASS;
1067 extern int32_t PBAAS_PORT;
1068 bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
1070 if (chainInfoUni.isObject() && chainDefUni.isObject())
1072 UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
1075 LOCK(cs_mergemining);
1076 notaryChainVersion = uni_get_str(uniVer);
1077 notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
1078 CPBaaSChainDefinition chainDef(chainDefUni);
1079 notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
1082 return IsVerusPBaaSAvailable();
1085 bool CConnectedChains::CheckVerusPBaaSAvailable()
1087 if (IsVerusActive())
1089 notaryChainVersion = "";
1093 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
1094 // tolerate only 15 second timeout
1095 UniValue chainInfo, chainDef;
1098 UniValue params(UniValue::VARR);
1099 chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
1100 if (!chainInfo.isNull())
1102 params.push_back(VERUS_CHAINNAME);
1103 chainDef = find_value(RPCCallRoot("getchaindefinition", params), "result");
1105 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
1110 } catch (exception e)
1114 notaryChainVersion = "";
1118 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
1120 CCoinbaseCurrencyState currencyState;
1123 bool isVerusActive = IsVerusActive();
1124 if (!isVerusActive &&
1125 CConstVerusSolutionVector::activationHeight.ActiveVersion(height) >= CActivationHeight::SOLUTION_VERUSV3 &&
1127 height <= chainActive.Height() &&
1128 chainActive[height] &&
1129 ReadBlockFromDisk(block, chainActive[height], false) &&
1130 (currencyState = CCoinbaseCurrencyState(block.vtx[0])).IsValid())
1132 return currencyState;
1136 bool isReserve = (thisChain.ChainOptions() & thisChain.OPTION_RESERVE);
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);
1140 if (!height && !isVerusActive)
1142 fees = CReserveTransactionDescriptor::CalculateAdditionalConversionFee(thisChain.preconverted);
1144 return CCoinbaseCurrencyState(cState, thisChain.preconverted, 0, CReserveOutput(CReserveOutput::VALID, 0), thisChain.conversion, fees, fees);
1148 bool CConnectedChains::SetLatestMiningOutputs(const std::vector<pair<int, CScript>> minerOutputs, CTxDestination &firstDestinationOut)
1150 LOCK(cs_mergemining);
1153 std::vector<std::vector<unsigned char>> vSolutions;
1155 if (!minerOutputs.size() || !Solver(minerOutputs[0].second, outType, vSolutions))
1160 if (outType == TX_PUBKEY)
1162 CPubKey pubKey(vSolutions[0]);
1163 if (!pubKey.IsValid())
1167 firstDestinationOut = CTxDestination(pubKey);
1169 else if (outType == TX_PUBKEYHASH)
1171 firstDestinationOut = CTxDestination(CKeyID(uint160(vSolutions[0])));
1178 latestMiningOutputs = minerOutputs;
1179 latestDestination = firstDestinationOut;
1183 void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
1185 // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
1192 multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
1196 // get all available transfer outputs to aggregate into export transactions
1197 if (GetUnspentChainTransfers(transferOutputs))
1199 if (!transferOutputs.size())
1203 std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
1204 std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1207 bookEnd.SetHex("ffffffffffffffffffffffffffffffffffffffff");
1208 uint160 lastChain = bookEnd;
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())));
1213 CPBaaSChainDefinition lastChainDef;
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++)
1219 // get chain target and see if it is the same
1220 if (lastChain == bookEnd || it->first == lastChain)
1222 txInputs.push_back(it->second);
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())
1230 auto lastExport = *exportOutputs.begin();
1232 if (((nHeight - lastExport.second.first) >= CCrossChainExport::MIN_BLOCKS) || (txInputs.size() >= CCrossChainExport::MIN_INPUTS))
1234 boost::optional<CTransaction> oneExport;
1236 // make one or more transactions that spends the last export and all possible cross chain transfers
1237 while (txInputs.size())
1239 TransactionBuilder tb(Params().GetConsensus(), nHeight);
1241 int numInputs = (txInputs.size() < CCrossChainExport::MAX_EXPORT_INPUTS) ? txInputs.size() : CCrossChainExport::MAX_EXPORT_INPUTS;
1243 int inputsLeft = txInputs.size() - numInputs;
1245 if (inputsLeft > 0 && inputsLeft < CCrossChainExport::MIN_INPUTS)
1247 inputsLeft += CCrossChainExport::MIN_INPUTS - inputsLeft;
1248 numInputs -= CCrossChainExport::MIN_INPUTS - inputsLeft;
1249 assert(numInputs > 0);
1252 // each time through, we make one export transaction with the remainder or a subset of the
1253 // reserve transfer inputs. inputs can be:
1254 // 1. transfers of reserve for fractional reserve chains
1255 // 2. pre-conversions for pre-launch participation in the premine
1256 // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
1258 // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
1259 // or reserve token inputs.
1261 // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
1262 // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
1263 // as part of the import process
1265 // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain.
1266 // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the
1267 // remaining Verus reserve coin considered burned.
1269 CAmount totalTxFees = 0;
1270 CAmount totalAmount = 0;
1271 CAmount exportOutVal = 0;
1272 std::vector<CBaseChainObject *> chainObjects;
1274 // first, we must add the export output from the current export thread to this chain
1275 if (oneExport.has_value())
1277 // spend the last export transaction output
1278 CTransaction &tx = oneExport.get();
1281 for (j = 0; j < tx.vout.size(); j++)
1283 if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) && p.evalCode == EVAL_CROSSCHAIN_EXPORT)
1289 // had to be found and valid if we made the tx
1290 assert(j < tx.vout.size() && p.IsValid());
1292 tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
1293 exportOutVal = tx.vout[j].nValue;
1297 // spend the recentExportIt output
1298 tb.AddTransparentInput(lastExport.second.second.txIn.prevout, lastExport.second.second.scriptPubKey, lastExport.second.second.nValue);
1299 exportOutVal = lastExport.second.second.nValue;
1304 for (int j = 0; j < numInputs; j++)
1306 tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
1307 if (IsVerusActive() && txInputs[j].second.nValue + txInputs[j].second.nFees > txInputs[j].first.nValue)
1309 // 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
1310 // it is too expensive to let it force evaluation repeatedly
1311 // we should formalize this into a chain contribution. right now, and reserve coins are basically added to chain surplus
1316 totalTxFees += txInputs[j].second.nFees;
1317 totalAmount += txInputs[j].second.nValue;
1318 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(txInputs[j].second), txInputs[j].second));
1321 if (fails == numInputs)
1323 // erase the inputs we've attempted to spend
1324 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1328 CCrossChainExport ccx(lastChain, numInputs, totalAmount, totalTxFees);
1329 CAmount exportFees = ccx.CalculateExportFee();
1330 CReserveTransfer feeOut(CReserveTransfer::VALID + CReserveTransfer::SEND_BACK + CReserveTransfer::FEE_OUTPUT, exportFees, 0, GetDestinationID(feeOutput));
1331 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
1333 // do a preliminary check
1334 CReserveTransactionDescriptor rtxd;
1335 std::vector<CTxOut> dummy;
1337 bool badChain = false;
1338 if ((badChain = !GetChainDefinition(lastChain, lastChainDef)) || !rtxd.AddReserveTransferImportOutputs(lastChainDef, chainObjects, dummy))
1340 DeleteOpRetObjects(chainObjects);
1342 // we can't do any more useful work for this chain if we failed here
1343 printf("%s: failed to create valid exports\n", __func__);
1344 LogPrintf("%s: failed to create valid exports\n", __func__);
1347 printf("%s: failed to export outputs:\n", __func__);
1348 for (auto oneout : dummy)
1351 ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1352 printf("%s\n", uniOut.write(true, 2).c_str());
1363 printf("%s: exported outputs:\n", __func__);
1364 for (auto oneout : dummy)
1367 ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1368 printf("%s\n", uniOut.write(true, 2).c_str());
1371 CScript opRet = StoreOpRetArray(chainObjects);
1372 DeleteOpRetObjects(chainObjects);
1375 CCcontract_info *cp;
1376 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
1378 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
1380 // send native amount of zero to a cross chain export output of the specific chain
1381 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_CROSSCHAIN_EXPORT))});
1383 CTxOut exportOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_EXPORT, exportOutVal, pk, dests, ccx);
1385 tb.AddTransparentOutput(exportOut.scriptPubKey, exportOutVal);
1387 // if we are on Verus chain, send all native funds, less fees to reserve deposit CC, which is equivalent to the reserve account
1388 // on a PBaaS reserve chain, input is burned
1389 if (IsVerusActive())
1391 cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
1392 pk = CPubKey(ParseHex(CC.CChexstr));
1394 // send the entire amount to a reserve transfer output of the specific chain
1395 // we receive our fee on the other chain or when it comes back
1396 dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_RESERVE_DEPOSIT))});
1398 CReserveOutput ro(CReserveOutput::VALID, totalAmount + totalTxFees);
1400 CTxOut outToReserve = MakeCC1of1Vout(EVAL_RESERVE_DEPOSIT,
1406 tb.AddTransparentOutput(outToReserve.scriptPubKey, ro.nValue);
1412 oneExport = tb.Build();
1414 if (oneExport.has_value())
1416 // replace the last one only if we have a valid new one
1417 CTransaction &tx = oneExport.get();
1419 LOCK2(cs_main, mempool.cs);
1420 static int lastHeight = 0;
1421 // remove conflicts, so that we get in
1422 std::list<CTransaction> removed;
1423 mempool.removeConflicts(tx, removed);
1425 // add to mem pool, prioritize according to the fee we will get, and relay
1426 printf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1427 LogPrintf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1428 if (myAddtomempool(tx))
1430 uint256 hash = tx.GetHash();
1431 mempool.PrioritiseTransaction(hash, hash.GetHex(), (double)(exportFees << 1), exportFees);
1432 RelayTransaction(tx);
1437 // we can't do any more useful work for this chain if we failed here
1438 printf("Failed to create export transaction\n");
1439 LogPrintf("Failed to create export transaction\n");
1444 // erase the inputs we've attempted to spend
1445 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1450 lastChain = it->first;
1456 // send new imports from this chain to the specified chain, which generally will be the notary chain
1457 void CConnectedChains::SendNewImports(const uint160 &chainID,
1458 const CPBaaSNotarization ¬arization,
1459 const uint256 &lastExportTx,
1460 const CTransaction &lastCrossImport,
1461 const CTransaction &lastExport)
1463 // currently only support sending imports to
1466 void CConnectedChains::SubmissionThread()
1470 arith_uint256 lastHash;
1471 int64_t lastImportTime = 0;
1472 uint32_t lastHeight = 0;
1474 // wait for something to check on, then submit blocks that should be submitted
1477 boost::this_thread::interruption_point();
1479 if (IsVerusActive())
1481 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
1482 //printf("SubmissionThread: pruning\n");
1483 PruneOldChains(GetAdjustedTime() - 300);
1484 bool submit = false;
1486 LOCK(cs_mergemining);
1487 if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
1489 qualifiedHeaders.clear();
1491 submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
1493 //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
1497 //printf("SubmissionThread: calling submit qualified blocks\n");
1498 SubmitQualifiedBlocks();
1502 //printf("SubmissionThread: waiting on sem\n");
1503 sem_submitthread.wait();
1508 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
1509 if (CheckVerusPBaaSAvailable())
1511 // check to see if we have recently earned a block with an earned notarization that qualifies for
1512 // submitting an accepted notarization
1513 if (earnedNotarizationHeight)
1516 int32_t txIndex = -1, height;
1518 LOCK(cs_mergemining);
1519 if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
1521 blk = earnedNotarizationBlock;
1522 earnedNotarizationBlock = CBlock();
1523 txIndex = earnedNotarizationIndex;
1524 height = earnedNotarizationHeight;
1525 earnedNotarizationHeight = 0;
1531 //printf("SubmissionThread: testing notarization\n");
1532 CTransaction lastConfirmed;
1533 uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
1537 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1538 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1543 // every "n" seconds, look for imports to include in our blocks from the Verus chain
1544 if ((GetAdjustedTime() - lastImportTime) >= 30 || lastHeight < (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight()))
1546 lastImportTime = GetAdjustedTime();
1547 lastHeight = (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight());
1549 // see if our notary has a confirmed notarization for us
1550 UniValue params(UniValue::VARR);
1553 params.push_back(thisChain.name);
1557 result = find_value(RPCCallRoot("getlastimportin", params), "result");
1558 } catch (exception e)
1560 result = NullUniValue;
1563 if (!result.isNull())
1565 auto txUniStr = find_value(result, "lastimporttransaction");
1566 auto txLastConfirmedStr = find_value(result, "lastconfirmednotarization");
1567 auto txTemplateStr = find_value(result, "importtxtemplate");
1568 CAmount totalImportAvailable = uni_get_int64(find_value(result, "totalimportavailable"));
1570 CTransaction lastImportTx, lastConfirmedTx, templateTx;
1572 if (txUniStr.isStr() && txTemplateStr.isStr() &&
1573 DecodeHexTx(lastImportTx, txUniStr.get_str()) &&
1574 DecodeHexTx(lastConfirmedTx, txLastConfirmedStr.get_str()) &&
1575 DecodeHexTx(templateTx, txTemplateStr.get_str()))
1577 std::vector<CTransaction> importTxes;
1578 if (CreateLatestImports(notaryChain.chainDefinition, lastImportTx, templateTx, lastConfirmedTx, totalImportAvailable, importTxes))
1580 for (auto importTx : importTxes)
1584 params.push_back(EncodeHexTx(importTx));
1588 txResult = find_value(RPCCallRoot("signrawtransaction", params), "result");
1589 if (txResult.isObject() && !(txResult = find_value(txResult, "hex")).isNull() && txResult.isStr() && txResult.get_str().size())
1592 params.push_back(txResult);
1593 txResult = find_value(RPCCallRoot("sendrawtransaction", params), "result");
1597 txResult = NullUniValue;
1600 } catch (exception e)
1602 txResult = NullUniValue;
1605 if (txResult.isStr())
1607 testId.SetHex(txResult.get_str());
1609 if (testId.IsNull())
1621 boost::this_thread::interruption_point();
1624 catch (const boost::thread_interrupted&)
1626 LogPrintf("Verus merge mining thread terminated\n");
1630 void CConnectedChains::SubmissionThreadStub()
1632 ConnectedChains.SubmissionThread();
1635 void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
1637 // called after winning a block that contains an earned notarization
1638 // the earned notarization and its height are queued for processing by the submission thread
1639 // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
1641 LOCK(cs_mergemining);
1643 // we only care about the last
1644 earnedNotarizationHeight = height;
1645 earnedNotarizationBlock = blk;
1646 earnedNotarizationIndex = txIndex;
1649 bool IsChainDefinitionInput(const CScript &scriptSig)
1652 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_PBAASDEFINITION;