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/crosschainrpc.h"
14 #include "pbaas/pbaas.h"
15 #include "pbaas/notarization.h"
16 #include "pbaas/identity.h"
17 #include "rpc/pbaasrpc.h"
21 #include "transaction_builder.h"
25 CConnectedChains ConnectedChains;
29 return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 0 || strcmp(ASSETCHAINS_SYMBOL, "VRSCTEST") == 0);
32 // this adds an opret to a mutable transaction and returns the voutnum if it could be added
33 int32_t AddOpRetOutput(CMutableTransaction &mtx, const CScript &opRetScript)
35 if (opRetScript.IsOpReturn() && opRetScript.size() <= MAX_OP_RETURN_RELAY)
37 CTxOut vOut = CTxOut();
38 vOut.scriptPubKey = opRetScript;
40 mtx.vout.push_back(vOut);
41 return mtx.vout.size() - 1;
49 // returns a pointer to a base chain object, which can be cast to the
50 // object type indicated in its objType member
51 uint256 GetChainObjectHash(const CBaseChainObject &bo)
54 const CChainObject<CBlockHeader> *pNewHeader;
55 const CChainObject<CTransaction> *pNewTx;
56 const CChainObject<CMerkleBranch> *pNewProof;
57 const CChainObject<CHeaderRef> *pNewHeaderRef;
58 const CChainObject<CPriorBlocksCommitment> *pPriors;
59 const CChainObject<CReserveTransfer> *pExport;
60 const CChainObject<CCrossChainProof> *pCrossChainProof;
61 const CBaseChainObject *retPtr;
69 return pNewHeader->GetHash();
71 case CHAINOBJ_TRANSACTION:
72 return pNewTx->GetHash();
75 return pNewProof->GetHash();
77 case CHAINOBJ_HEADER_REF:
78 return pNewHeaderRef->GetHash();
80 case CHAINOBJ_PRIORBLOCKS:
81 return pPriors->GetHash();
83 case CHAINOBJ_RESERVETRANSFER:
84 return pExport->GetHash();
86 case CHAINOBJ_CROSSCHAINPROOF:
87 return pCrossChainProof->GetHash();
93 // used to export coins from one chain to another, if they are not native, they are represented on the other
95 bool ValidateCrossChainExport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
100 bool IsCrossChainExportInput(const CScript &scriptSig)
105 // used to validate import of coins from one chain to another. if they are not native and are supported,
106 // they are represented o the chain as tokens
107 bool ValidateCrossChainImport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
111 bool IsCrossChainImportInput(const CScript &scriptSig)
116 // used to validate a specific service reward based on the spending transaction
117 bool ValidateServiceReward(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
119 // for each type of service reward, we need to check and see if the spender is
120 // correctly formatted to be a valid spend of the service reward. for notarization
121 // we ensure that the notarization and its outputs are valid and that the spend
122 // applies to the correct billing period
125 bool IsServiceRewardInput(const CScript &scriptSig)
130 // used as a proxy token output for a reserve currency on its fractional reserve chain
131 bool ValidateReserveOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
135 bool IsReserveOutputInput(const CScript &scriptSig)
140 bool ValidateReserveTransfer(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
144 bool IsReserveTransferInput(const CScript &scriptSig)
149 bool ValidateReserveDeposit(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
153 bool IsReserveDepositInput(const CScript &scriptSig)
158 bool ValidateCurrencyState(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
162 bool IsCurrencyStateInput(const CScript &scriptSig)
167 // used to convert a fractional reserve currency into its reserve and back
168 bool ValidateReserveExchange(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
172 bool IsReserveExchangeInput(const CScript &scriptSig)
179 * Verifies that the input objects match the hashes and returns the transaction.
181 * If the opRetTx has the op ret, this calculates based on the actual transaction and
182 * validates the hashes. If the opRetTx does not have the opRet itself, this validates
183 * by ensuring that all objects are present on this chain, composing the opRet, and
184 * ensuring that the transaction then hashes to the correct txid.
187 bool ValidateOpretProof(CScript &opRet, COpRetProof &orProof)
189 // enumerate through the objects and validate that they are objects of the expected type that hash
190 // to the value expected. return true if so
194 int8_t ObjTypeCode(const CBlockHeader &obj)
196 return CHAINOBJ_HEADER;
199 int8_t ObjTypeCode(const CMerkleBranch &obj)
201 return CHAINOBJ_PROOF;
204 int8_t ObjTypeCode(const CTransaction &obj)
206 return CHAINOBJ_TRANSACTION;
209 int8_t ObjTypeCode(const CHeaderRef &obj)
211 return CHAINOBJ_HEADER_REF;
214 int8_t ObjTypeCode(const CPriorBlocksCommitment &obj)
216 return CHAINOBJ_PRIORBLOCKS;
219 int8_t ObjTypeCode(const CReserveTransfer &obj)
221 return CHAINOBJ_RESERVETRANSFER;
224 int8_t ObjTypeCode(const CCrossChainProof &obj)
226 return CHAINOBJ_CROSSCHAINPROOF;
229 // this adds an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction
230 CScript StoreOpRetArray(std::vector<CBaseChainObject *> &objPtrs)
233 CDataStream s = CDataStream(SER_NETWORK, PROTOCOL_VERSION);
234 s << (int32_t)OPRETTYPE_OBJECTARR;
237 for (auto pobj : objPtrs)
241 if (!DehydrateChainObject(s, pobj))
247 catch(const std::exception& e)
249 std::cerr << e.what() << '\n';
255 //std::vector<unsigned char> schars(s.begin(), s.begin() + 200);
256 //printf("stream vector chars: %s\n", HexBytes(&schars[0], schars.size()).c_str());
258 std::vector<unsigned char> vch(s.begin(), s.end());
259 return error ? CScript() : CScript() << OP_RETURN << vch;
262 void DeleteOpRetObjects(std::vector<CBaseChainObject *> &ora)
264 for (auto pobj : ora)
266 switch(pobj->objectType)
268 case CHAINOBJ_HEADER:
270 delete (CChainObject<CBlockHeader> *)pobj;
274 case CHAINOBJ_TRANSACTION:
276 delete (CChainObject<CTransaction> *)pobj;
282 delete (CChainObject<CMerkleBranch> *)pobj;
286 case CHAINOBJ_HEADER_REF:
288 delete (CChainObject<CHeaderRef> *)pobj;
292 case CHAINOBJ_PRIORBLOCKS:
294 delete (CChainObject<CPriorBlocksCommitment> *)pobj;
298 case CHAINOBJ_RESERVETRANSFER:
300 delete (CChainObject<CReserveTransfer> *)pobj;
304 case CHAINOBJ_CROSSCHAINPROOF:
306 delete (CChainObject<CCrossChainProof> *)pobj;
317 std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
319 std::vector<unsigned char> vch;
320 std::vector<CBaseChainObject *> vRet;
321 if (opRetScript.IsOpReturn() && GetOpReturnData(opRetScript, vch) && vch.size() > 0)
323 CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION);
330 if (opRetType == OPRETTYPE_OBJECTARR)
332 CBaseChainObject *pobj;
333 while (!s.empty() && (pobj = RehydrateChainObject(s)))
335 vRet.push_back(pobj);
339 printf("failed to load all objects in opret");
340 DeleteOpRetObjects(vRet);
345 catch(const std::exception& e)
347 std::cerr << e.what() << '\n';
348 DeleteOpRetObjects(vRet);
355 CNodeData::CNodeData(UniValue &obj)
357 networkAddress = uni_get_str(find_value(obj, "networkaddress"));
358 CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
359 ba.GetKeyID(paymentAddress);
362 UniValue CNodeData::ToUniValue() const
364 UniValue obj(UniValue::VOBJ);
365 obj.push_back(Pair("networkaddress", networkAddress));
366 obj.push_back(Pair("paymentaddress", CBitcoinAddress(paymentAddress).ToString()));
370 CPBaaSChainDefinition::CPBaaSChainDefinition(const UniValue &obj)
372 nVersion = PBAAS_VERSION;
373 name = std::string(uni_get_str(find_value(obj, "name")), 0, (KOMODO_ASSETCHAIN_MAXLEN - 1));
375 string invalidChars = "\\/:*?\"<>|";
376 for (int i = 0; i < name.size(); i++)
378 if (invalidChars.find(name[i]) != string::npos)
384 CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
385 ba.GetKeyID(address);
386 premine = uni_get_int64(find_value(obj, "premine"));
387 initialcontribution = uni_get_int64(find_value(obj, "initialcontribution"));
388 conversion = uni_get_int64(find_value(obj, "conversion"));
389 minpreconvert = uni_get_int64(find_value(obj, "minpreconvert"));
390 maxpreconvert = uni_get_int64(find_value(obj, "maxpreconvert"));
391 preconverted = uni_get_int64(find_value(obj, "preconverted"));
392 launchFee = uni_get_int64(find_value(obj, "launchfee"));
393 startBlock = uni_get_int(find_value(obj, "startblock"));
394 endBlock = uni_get_int(find_value(obj, "endblock"));
396 auto vEras = uni_getValues(find_value(obj, "eras"));
397 if (vEras.size() > ASSETCHAINS_MAX_ERAS)
399 vEras.resize(ASSETCHAINS_MAX_ERAS);
402 for (auto era : vEras)
404 rewards.push_back(uni_get_int64(find_value(era, "reward")));
405 rewardsDecay.push_back(uni_get_int64(find_value(era, "decay")));
406 halving.push_back(uni_get_int64(find_value(era, "halving")));
407 eraEnd.push_back(uni_get_int64(find_value(era, "eraend")));
408 eraOptions.push_back(uni_get_int64(find_value(era, "eraoptions")));
411 billingPeriod = uni_get_int(find_value(obj, "billingperiod"));
412 notarizationReward = uni_get_int64(find_value(obj, "notarizationreward"));
414 auto nodeVec = uni_getValues(find_value(obj, "nodes"));
415 for (auto node : nodeVec)
417 nodes.push_back(CNodeData(node));
421 CPBaaSChainDefinition::CPBaaSChainDefinition(const CTransaction &tx, bool validate)
423 bool definitionFound = false;
424 nVersion = PBAAS_VERSION_INVALID;
425 for (auto out : tx.vout)
428 if (IsPayToCryptoCondition(out.scriptPubKey, p))
430 if (p.evalCode == EVAL_PBAASDEFINITION)
434 nVersion = PBAAS_VERSION_INVALID;
438 FromVector(p.vData[0], *this);
439 definitionFound = true;
451 CServiceReward::CServiceReward(const CTransaction &tx, bool validate)
453 nVersion = PBAAS_VERSION_INVALID;
454 for (auto out : tx.vout)
457 if (IsPayToCryptoCondition(out.scriptPubKey, p))
459 // always take the first for now
460 if (p.evalCode == EVAL_SERVICEREWARD)
462 FromVector(p.vData[0], *this);
474 CCrossChainExport::CCrossChainExport(const CTransaction &tx)
476 for (auto out : tx.vout)
479 if (IsPayToCryptoCondition(out.scriptPubKey, p))
481 // always take the first for now
482 if (p.evalCode == EVAL_CROSSCHAIN_EXPORT)
484 FromVector(p.vData[0], *this);
491 uint160 CPBaaSChainDefinition::GetChainID(std::string name)
494 return CIdentity::GetID(name, parent);
497 uint160 CPBaaSChainDefinition::GetConditionID(int32_t condition) const
499 return CCrossChainRPCData::GetConditionID(name, condition);
502 UniValue CPBaaSChainDefinition::ToUniValue() const
504 UniValue obj(UniValue::VOBJ);
505 obj.push_back(Pair("version", (int64_t)nVersion));
506 obj.push_back(Pair("name", name));
507 obj.push_back(Pair("chainid", GetChainID().GetHex()));
508 obj.push_back(Pair("chainaddress", EncodeDestination(CTxDestination(CKeyID(GetChainID())))));
509 obj.push_back(Pair("paymentaddress", EncodeDestination(CTxDestination(address))));
510 obj.push_back(Pair("premine", (int64_t)premine));
511 obj.push_back(Pair("initialcontribution", (int64_t)initialcontribution));
512 obj.push_back(Pair("conversion", (int64_t)conversion));
513 obj.push_back(Pair("minpreconvert", (int64_t)minpreconvert));
514 obj.push_back(Pair("maxpreconvert", (int64_t)maxpreconvert));
515 obj.push_back(Pair("preconverted", (int64_t)preconverted));
516 obj.push_back(Pair("launchfee", (int64_t)launchFee));
517 obj.push_back(Pair("conversionpercent", ValueFromAmount(conversion * 100)));
518 obj.push_back(Pair("launchfeepercent", ValueFromAmount(launchFee * 100)));
519 obj.push_back(Pair("startblock", (int32_t)startBlock));
520 obj.push_back(Pair("endblock", (int32_t)endBlock));
522 UniValue eraArr(UniValue::VARR);
523 for (int i = 0; i < rewards.size(); i++)
525 UniValue era(UniValue::VOBJ);
526 era.push_back(Pair("reward", rewards.size() > i ? rewards[i] : (int64_t)0));
527 era.push_back(Pair("decay", rewardsDecay.size() > i ? rewardsDecay[i] : (int64_t)0));
528 era.push_back(Pair("halving", halving.size() > i ? (int32_t)halving[i] : (int32_t)0));
529 era.push_back(Pair("eraend", eraEnd.size() > i ? (int32_t)eraEnd[i] : (int32_t)0));
530 era.push_back(Pair("eraoptions", eraOptions.size() > i ? (int32_t)eraOptions[i] : (int32_t)0));
531 eraArr.push_back(era);
533 obj.push_back(Pair("eras", eraArr));
535 obj.push_back(Pair("billingperiod", billingPeriod));
536 obj.push_back(Pair("notarizationreward", notarizationReward));
538 UniValue nodeArr(UniValue::VARR);
539 for (auto node : nodes)
541 nodeArr.push_back(node.ToUniValue());
543 obj.push_back(Pair("nodes", nodeArr));
548 int CPBaaSChainDefinition::GetDefinedPort() const
552 for (auto node : nodes)
554 SplitHostPort(node.networkAddress, port, host);
563 #define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
564 extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
565 extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3], ASSETCHAINS_ERAOPTIONS[3];
566 extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
567 extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
568 extern std::string VERUS_CHAINNAME;
570 // adds the chain definition for this chain and nodes as well
571 // this also sets up the notarization chain, if there is one
572 bool SetThisChain(UniValue &chainDefinition)
574 ConnectedChains.ThisChain() = CPBaaSChainDefinition(chainDefinition);
576 if (!IsVerusActive())
578 CPBaaSChainDefinition ¬aryChainDef = ConnectedChains.notaryChain.chainDefinition;
579 // we set the notary chain to either Verus or VerusTest
580 notaryChainDef.nVersion = PBAAS_VERSION;
583 // setup Verus test parameters
584 notaryChainDef.name = "VRSCTEST";
585 notaryChainDef.premine = 5000000000000000;
586 notaryChainDef.rewards = std::vector<int64_t>({2400000000});
587 notaryChainDef.rewardsDecay = std::vector<int64_t>({0});
588 notaryChainDef.halving = std::vector<int32_t>({225680});
589 notaryChainDef.eraEnd = std::vector<int32_t>({0});
590 notaryChainDef.eraOptions = std::vector<int32_t>({notaryChainDef.OPTION_ID_REFERRALS});
594 // first setup Verus parameters
595 notaryChainDef.name = "VRSC";
596 notaryChainDef.premine = 0;
597 notaryChainDef.rewards = std::vector<int64_t>({0,38400000000,2400000000});
598 notaryChainDef.rewardsDecay = std::vector<int64_t>({100000000,0,0});
599 notaryChainDef.halving = std::vector<int32_t>({1,43200,1051920});
600 notaryChainDef.eraEnd = std::vector<int32_t>({10080,226080,0});
601 notaryChainDef.eraOptions = std::vector<int32_t>({notaryChainDef.OPTION_ID_REFERRALS,0,0});
605 if (ConnectedChains.ThisChain().IsValid())
607 memset(ASSETCHAINS_SYMBOL, 0, sizeof(ASSETCHAINS_SYMBOL));
608 strcpy(ASSETCHAINS_SYMBOL, ConnectedChains.ThisChain().name.c_str());
610 // set all command line parameters into mapArgs from chain definition
611 vector<string> nodeStrs;
612 for (auto node : ConnectedChains.ThisChain().nodes)
614 nodeStrs.push_back(node.networkAddress);
618 mapMultiArgs["-seednode"] = nodeStrs;
620 if (int port = ConnectedChains.ThisChain().GetDefinedPort())
622 mapArgs["-port"] = to_string(port);
625 ASSETCHAINS_SUPPLY = ConnectedChains.ThisChain().premine;
626 mapArgs["-ac_supply"] = to_string(ASSETCHAINS_SUPPLY);
627 ASSETCHAINS_ALGO = ASSETCHAINS_VERUSHASH;
628 ASSETCHAINS_LWMAPOS = 50;
630 ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF;
631 ASSETCHAINS_TIMEUNLOCKFROM = 0;
632 ASSETCHAINS_TIMEUNLOCKTO = 0;
634 auto numEras = ConnectedChains.ThisChain().rewards.size();
635 ASSETCHAINS_LASTERA = numEras - 1;
636 mapArgs["-ac_eras"] = to_string(numEras);
638 mapArgs["-ac_end"] = "";
639 mapArgs["-ac_reward"] = "";
640 mapArgs["-ac_halving"] = "";
641 mapArgs["-ac_decay"] = "";
642 mapArgs["-ac_options"] = "";
644 for (int j = 0; j < ASSETCHAINS_MAX_ERAS; j++)
646 if (j > ASSETCHAINS_LASTERA)
648 ASSETCHAINS_REWARD[j] = ASSETCHAINS_REWARD[j-1];
649 ASSETCHAINS_DECAY[j] = ASSETCHAINS_DECAY[j-1];
650 ASSETCHAINS_HALVING[j] = ASSETCHAINS_HALVING[j-1];
651 ASSETCHAINS_ENDSUBSIDY[j] = 0;
652 ASSETCHAINS_ERAOPTIONS[j] = 0;
656 ASSETCHAINS_REWARD[j] = ConnectedChains.ThisChain().rewards[j];
657 ASSETCHAINS_DECAY[j] = ConnectedChains.ThisChain().rewardsDecay[j];
658 ASSETCHAINS_HALVING[j] = ConnectedChains.ThisChain().halving[j];
659 ASSETCHAINS_ENDSUBSIDY[j] = ConnectedChains.ThisChain().eraEnd[j];
660 ASSETCHAINS_ERAOPTIONS[j] = ConnectedChains.ThisChain().eraOptions[j];
663 mapArgs["-ac_reward"] = to_string(ASSETCHAINS_REWARD[j]);
664 mapArgs["-ac_decay"] = to_string(ASSETCHAINS_DECAY[j]);
665 mapArgs["-ac_halving"] = to_string(ASSETCHAINS_HALVING[j]);
666 mapArgs["-ac_end"] = to_string(ASSETCHAINS_ENDSUBSIDY[j]);
667 mapArgs["-ac_options"] = to_string(ASSETCHAINS_ERAOPTIONS[j]);
671 mapArgs["-ac_reward"] += "," + to_string(ASSETCHAINS_REWARD[j]);
672 mapArgs["-ac_decay"] += "," + to_string(ASSETCHAINS_DECAY[j]);
673 mapArgs["-ac_halving"] += "," + to_string(ASSETCHAINS_HALVING[j]);
674 mapArgs["-ac_end"] += "," + to_string(ASSETCHAINS_ENDSUBSIDY[j]);
675 mapArgs["-ac_options"] += "," + to_string(ASSETCHAINS_ERAOPTIONS[j]);
680 PBAAS_STARTBLOCK = ConnectedChains.ThisChain().startBlock;
681 mapArgs["-startblock"] = to_string(PBAAS_STARTBLOCK);
682 PBAAS_ENDBLOCK = ConnectedChains.ThisChain().endBlock;
683 mapArgs["-endblock"] = to_string(PBAAS_ENDBLOCK);
685 PBAAS_PRECONVERSION = ConnectedChains.ThisChain().conversion;
686 PBAAS_MINPRECONVERT = ConnectedChains.ThisChain().minpreconvert;
687 PBAAS_MAXPRECONVERT = ConnectedChains.ThisChain().maxpreconvert;
688 mapArgs["-ac_conversion"] = to_string(PBAAS_PRECONVERSION);
689 mapArgs["-ac_minpreconvert"] = to_string(PBAAS_MINPRECONVERT);
690 mapArgs["-ac_maxpreconvert"] = to_string(PBAAS_MAXPRECONVERT);
699 // ensures that the chain definition is valid and that there are no other definitions of the same name
700 // that have been confirmed.
701 bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
703 // the chain definition output can be spent when the chain is at the end of its life and only then
708 // ensures that the chain definition is valid and that there are no other definitions of the same name
709 // that have been confirmed.
710 bool CheckChainDefinitionOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
712 // checked before a chain definition output script is accepted as a valid transaction
714 // basics - we need a chain definition transaction to kick off a PBaaS chain. it must have:
715 // 1) valid chain definition output with parameters in proper ranges and no duplicate name
716 // 2) notarization output with conformant values
717 // 3) finalization output
718 // 3) notarization funding
721 // get the source transaction
724 if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
726 LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
730 CPBaaSChainDefinition chainDef(thisTx, true);
731 CPBaaSNotarization notarization(thisTx, true);
732 CNotarizationFinalization finalization(thisTx, true);
734 if (!chainDef.IsValid() || !notarization.IsValid() || finalization.IsValid())
736 LogPrintf("transaction specified, %s, must have valid chain definition, notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
740 CPBaaSChainDefinition prior;
741 // this ensures that there is no other definition of the same name already on the blockchain
742 if (!GetChainDefinition(chainDef.name, prior))
744 LogPrintf("PBaaS chain with the name %s already exists\n", chainDef.name.c_str());
751 CAmount CCrossChainExport::CalculateExportFee() const
753 if (numInputs > MAX_EXPORT_INPUTS)
757 static const arith_uint256 satoshis(100000000);
759 int64_t ratio = 50000000 + ((25000000 / MAX_EXPORT_INPUTS) * (numInputs - 1));
761 return (((arith_uint256(totalFees) * arith_uint256(ratio))) / satoshis).GetLow64();
764 bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
767 LOCK(cs_mergemining);
769 //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
771 auto chainIt = mergeMinedChains.find(chainID);
772 if (chainIt != mergeMinedChains.end())
774 arith_uint256 target;
775 target.SetCompact(chainIt->second.block.nBits);
776 for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
778 // make sure we don't just match by target
779 if (removeRange.first->second->GetChainID() == chainID)
781 mergeMinedTargets.erase(removeRange.first);
785 mergeMinedChains.erase(chainID);
786 dirty = retval = true;
788 // if we get to 0, give the thread a kick to stop waiting for mining
789 //if (!mergeMinedChains.size())
791 // sem_submitthread.post();
797 // remove merge mined chains added and not updated since a specific time
798 void CConnectedChains::PruneOldChains(uint32_t pruneBefore)
800 vector<uint160> toRemove;
802 LOCK(cs_mergemining);
803 for (auto blkData : mergeMinedChains)
805 if (blkData.second.block.nTime < pruneBefore)
807 toRemove.push_back(blkData.first);
811 for (auto id : toRemove)
813 //printf("Pruning chainID: %s\n", id.GetHex().c_str());
814 RemoveMergedBlock(id);
818 // adds or updates merge mined blocks
819 // returns false if failed to add
820 bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
822 // determine if we should replace one or add to the merge mine vector
824 LOCK(cs_mergemining);
826 arith_uint256 target;
827 uint160 cID = blkData.GetChainID();
828 auto it = mergeMinedChains.find(cID);
829 if (it != mergeMinedChains.end())
831 RemoveMergedBlock(cID); // remove it if already there
833 target.SetCompact(blkData.block.nBits);
835 //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
837 mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
843 bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
846 LOCK(cs_mergemining);
847 auto chainIt = mergeMinedChains.find(chainID);
848 if (chainIt != mergeMinedChains.end())
850 rpcChainData = (CRPCChainData)chainIt->second;
857 // this returns a pointer to the data without copy and assumes the lock is held
858 CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
861 auto chainIt = mergeMinedChains.find(chainID);
862 if (chainIt != mergeMinedChains.end())
864 return &chainIt->second;
870 void CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
872 //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
874 LOCK(cs_mergemining);
876 qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
878 sem_submitthread.post();
881 // get the latest block header and submit one block at a time, returning after there are no more
882 // matching blocks to be found
883 vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
885 std::set<uint160> inHeader;
886 bool submissionFound;
887 CPBaaSMergeMinedChainData chainData;
888 vector<pair<string, UniValue>> results;
891 arith_uint256 lastHash;
892 CPBaaSBlockHeader pbh;
896 submissionFound = false;
898 LOCK(cs_mergemining);
899 // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
900 // common, merge mined headers for notarization, drop out on any submission
901 for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
903 // add the PBaaS chain ids from this header to a set for search
904 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
906 inHeader.insert(pbh.chainID);
910 // now look through all targets that are equal to or above the hash of this header
911 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
913 chainID = chainIt->second->GetChainID();
914 if (inHeader.count(chainID))
916 // first, check that the winning header matches the block that is there
917 CPBaaSPreHeader preHeader(chainIt->second->block);
918 preHeader.SetBlockData(headerIt->second);
920 // check if the block header matches the block's specific data, only then can we create a submission from this block
921 if (headerIt->second.CheckNonCanonicalData(chainID))
923 // save block as is, remove the block from merged headers, replace header, and submit
924 chainData = *chainIt->second;
926 *(CBlockHeader *)&chainData.block = headerIt->second;
928 submissionFound = true;
930 //else // not an error condition. code is here for debugging
932 // printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
935 //else // not an error condition. code is here for debugging
937 // printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
941 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
944 // once it is going to be submitted, remove block from this chain until a new one is added again
945 RemoveMergedBlock(chainID);
950 qualifiedHeaders.erase(headerIt);
956 // submit one block and loop again. this approach allows multiple threads
957 // to collectively empty the submission queue, mitigating the impact of
958 // any one stalled daemon
959 UniValue submitParams(UniValue::VARR);
960 submitParams.push_back(EncodeHexBlk(chainData.block));
961 UniValue result, error;
964 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
965 result = find_value(result, "result");
966 error = find_value(result, "error");
970 result = UniValue(e.what());
972 results.push_back(make_pair(chainData.chainDefinition.name, result));
973 if (result.isStr() || !error.isNull())
975 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());
979 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
982 } while (submissionFound);
986 // add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
987 uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
989 vector<uint160> inHeader;
990 vector<UniValue> toCombine;
991 arith_uint256 blkHash = UintToArith256(bh.GetHash());
992 arith_uint256 target(0);
994 CPBaaSBlockHeader pbh;
997 LOCK(cs_mergemining);
999 CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
1001 for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
1003 if (bh.GetPBaaSHeader(pbh, i))
1005 inHeader.push_back(pbh.chainID);
1009 // loop through the existing PBaaS chain ids in the header
1010 // remove any that are not either this Chain ID or in our local collection and then add all that are present
1011 for (uint32_t i = 0; i < inHeader.size(); i++)
1013 auto it = mergeMinedChains.find(inHeader[i]);
1014 if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
1016 bh.DeletePBaaSHeader(i);
1020 for (auto chain : mergeMinedChains)
1022 // get the native PBaaS header for each chain and put it into the
1023 // header we are given
1024 // it must have itself in as a PBaaS header
1025 uint160 cid = chain.second.GetChainID();
1026 if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
1028 if (!bh.AddUpdatePBaaSHeader(pbh))
1030 LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
1036 t.SetCompact(chain.second.block.nBits);
1045 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
1052 return target.GetCompact();
1055 bool CConnectedChains::IsVerusPBaaSAvailable()
1057 return notaryChainVersion >= "0.5.9";
1060 extern string PBAAS_HOST, PBAAS_USERPASS;
1061 extern int32_t PBAAS_PORT;
1062 bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
1064 if (chainInfoUni.isObject() && chainDefUni.isObject())
1066 UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
1069 LOCK(cs_mergemining);
1070 notaryChainVersion = uni_get_str(uniVer);
1071 notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
1072 CPBaaSChainDefinition chainDef(chainDefUni);
1073 notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
1076 return IsVerusPBaaSAvailable();
1079 bool CConnectedChains::CheckVerusPBaaSAvailable()
1081 if (IsVerusActive())
1083 notaryChainVersion = "";
1087 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
1088 // tolerate only 15 second timeout
1089 UniValue chainInfo, chainDef;
1092 UniValue params(UniValue::VARR);
1093 chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
1094 if (!chainInfo.isNull())
1096 params.push_back(VERUS_CHAINNAME);
1097 chainDef = find_value(RPCCallRoot("getchaindefinition", params), "result");
1099 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
1104 } catch (exception e)
1108 notaryChainVersion = "";
1112 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
1114 CCoinbaseCurrencyState currencyState;
1117 bool isVerusActive = IsVerusActive();
1118 if (!isVerusActive &&
1119 CConstVerusSolutionVector::activationHeight.ActiveVersion(height) >= CActivationHeight::ACTIVATE_PBAAS &&
1121 height <= chainActive.Height() &&
1122 chainActive[height] &&
1123 ReadBlockFromDisk(block, chainActive[height], Params().GetConsensus()) &&
1124 (currencyState = CCoinbaseCurrencyState(block.vtx[0])).IsValid())
1126 return currencyState;
1130 bool isReserve = (thisChain.ChainOptions() & thisChain.OPTION_RESERVE);
1131 CAmount preconvertedNative = currencyState.ReserveToNative(thisChain.preconverted, thisChain.conversion);
1132 CCurrencyState cState(thisChain.conversion, preconvertedNative, preconvertedNative, 0, isReserve ? thisChain.preconverted : 0, isReserve ? CCurrencyState::VALID + CCurrencyState::ISRESERVE : CCurrencyState::VALID);
1134 if (!height && !isVerusActive)
1136 fees = CReserveTransactionDescriptor::CalculateAdditionalConversionFee(thisChain.preconverted);
1138 return CCoinbaseCurrencyState(cState, thisChain.preconverted, 0, CReserveOutput(CReserveOutput::VALID, 0), thisChain.conversion, fees, fees);
1142 bool CConnectedChains::SetLatestMiningOutputs(const std::vector<pair<int, CScript>> &minerOutputs, CTxDestination &firstDestinationOut)
1144 LOCK(cs_mergemining);
1146 if (!minerOutputs.size() || !ExtractDestination(minerOutputs[0].second, firstDestinationOut))
1150 latestMiningOutputs = minerOutputs;
1151 latestDestination = firstDestinationOut;
1155 void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
1157 // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
1164 multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
1168 // get all available transfer outputs to aggregate into export transactions
1169 if (GetUnspentChainTransfers(transferOutputs))
1171 if (!transferOutputs.size())
1175 std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
1176 std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1179 bookEnd.SetHex("ffffffffffffffffffffffffffffffffffffffff");
1180 uint160 lastChain = bookEnd;
1182 // add a bookend entry at the end of transfer outputs to ensure that we try to export all before it
1183 transferOutputs.insert(make_pair(bookEnd, make_pair(CInputDescriptor(), CReserveTransfer())));
1185 CPBaaSChainDefinition lastChainDef;
1187 // merge all of the common chainID outputs into common export transactions if either MIN_BLOCKS blocks have passed since the last
1188 // export of that type, or there are MIN_INPUTS or more outputs to aggregate
1189 for (auto it = transferOutputs.begin(); it != transferOutputs.end(); it++)
1191 // get chain target and see if it is the same
1192 if (lastChain == bookEnd || it->first == lastChain)
1194 txInputs.push_back(it->second);
1198 // when we get here, we have a consecutive number of transfer outputs to consume in txInputs
1199 // we need an unspent export output to export
1200 if (GetUnspentChainExports(lastChain, exportOutputs) && exportOutputs.size())
1202 auto lastExport = *exportOutputs.begin();
1204 if (((nHeight - lastExport.second.first) >= CCrossChainExport::MIN_BLOCKS) || (txInputs.size() >= CCrossChainExport::MIN_INPUTS))
1206 boost::optional<CTransaction> oneExport;
1208 if (!GetChainDefinition(lastChain, lastChainDef))
1210 printf("%s: chain definition for export chainID %s not found\n\n", __func__, lastChain.GetHex().c_str());
1211 LogPrintf("%s: chain definition for export chainID %s not found\n\n", __func__, lastChain.GetHex().c_str());
1215 // make one or more transactions that spends the last export and all possible cross chain transfers
1216 while (txInputs.size())
1218 TransactionBuilder tb(Params().GetConsensus(), nHeight);
1220 int numInputs = (txInputs.size() < CCrossChainExport::MAX_EXPORT_INPUTS) ? txInputs.size() : CCrossChainExport::MAX_EXPORT_INPUTS;
1222 int inputsLeft = txInputs.size() - numInputs;
1224 if (inputsLeft > 0 && inputsLeft < CCrossChainExport::MIN_INPUTS)
1226 inputsLeft += CCrossChainExport::MIN_INPUTS - inputsLeft;
1227 numInputs -= CCrossChainExport::MIN_INPUTS - inputsLeft;
1228 assert(numInputs > 0);
1231 // each time through, we make one export transaction with the remainder or a subset of the
1232 // reserve transfer inputs. inputs can be:
1233 // 1. transfers of reserve for fractional reserve chains
1234 // 2. pre-conversions for pre-launch participation in the premine
1235 // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
1237 // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
1238 // or reserve token inputs.
1240 // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
1241 // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
1242 // as part of the import process
1244 // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain.
1245 // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the
1246 // remaining Verus reserve coin will be burned on the PBaaS chain as spending it is allowed, once notarized, on the
1249 CAmount totalTxFees = 0;
1250 CAmount totalAmount = 0;
1251 CAmount exportOutVal = 0;
1252 std::vector<CBaseChainObject *> chainObjects;
1254 // first, we must add the export output from the current export thread to this chain
1255 if (oneExport.has_value())
1257 // spend the last export transaction output
1258 CTransaction &tx = oneExport.get();
1261 for (j = 0; j < tx.vout.size(); j++)
1263 if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) && p.evalCode == EVAL_CROSSCHAIN_EXPORT)
1269 // had to be found and valid if we made the tx
1270 assert(j < tx.vout.size() && p.IsValid());
1272 tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
1273 exportOutVal = tx.vout[j].nValue;
1277 // spend the recentExportIt output
1278 tb.AddTransparentInput(lastExport.second.second.txIn.prevout, lastExport.second.second.scriptPubKey, lastExport.second.second.nValue);
1279 exportOutVal = lastExport.second.second.nValue;
1284 for (int j = 0; j < numInputs; j++)
1286 tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
1287 if (IsVerusActive() && txInputs[j].second.nValue + txInputs[j].second.nFees > txInputs[j].first.nValue)
1289 // 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
1290 // 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
1291 // we should formalize this into a chain contribution or amount adjustment.
1292 printf("%s: transaction %s claims more value than it contains\n", __func__, txInputs[j].first.txIn.prevout.hash.GetHex().c_str());
1293 LogPrintf("%s: transaction %s claims more value than it contains\n", __func__, txInputs[j].first.txIn.prevout.hash.GetHex().c_str());
1298 totalTxFees += txInputs[j].second.nFees;
1299 totalAmount += txInputs[j].second.nValue;
1300 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(txInputs[j].second), txInputs[j].second));
1303 if (fails == numInputs)
1305 // erase the inputs we've attempted to spend
1306 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1310 CCrossChainExport ccx(lastChain, numInputs, totalAmount, totalTxFees);
1311 CAmount exportFees = ccx.CalculateExportFee();
1312 CReserveTransfer feeOut(CReserveTransfer::VALID + CReserveTransfer::SEND_BACK + CReserveTransfer::FEE_OUTPUT, exportFees, 0, GetDestinationID(feeOutput));
1313 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
1315 // do a preliminary check
1316 CReserveTransactionDescriptor rtxd;
1317 std::vector<CTxOut> dummy;
1319 if (!rtxd.AddReserveTransferImportOutputs(lastChainDef, chainObjects, dummy))
1321 DeleteOpRetObjects(chainObjects);
1323 printf("%s: failed to create valid exports\n", __func__);
1324 LogPrintf("%s: failed to create valid exports\n", __func__);
1327 printf("%s: failed to export outputs:\n", __func__);
1328 for (auto oneout : dummy)
1331 ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1332 printf("%s\n", uniOut.write(true, 2).c_str());
1338 printf("%s: exported outputs:\n", __func__);
1339 for (auto oneout : dummy)
1342 ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1343 printf("%s\n", uniOut.write(true, 2).c_str());
1346 CScript opRet = StoreOpRetArray(chainObjects);
1347 DeleteOpRetObjects(chainObjects);
1350 CCcontract_info *cp;
1351 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
1353 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
1355 // send native amount of zero to a cross chain export output of the specific chain
1356 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_CROSSCHAIN_EXPORT))});
1358 CTxOut exportOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_EXPORT, exportOutVal, pk, dests, ccx);
1360 tb.AddTransparentOutput(exportOut.scriptPubKey, exportOutVal);
1362 // if we are on Verus chain, send all native funds to reserve deposit CC, which is equivalent to the reserve account
1363 // if we are on a PBaaS chain, input is burned when sent back for release
1364 if (IsVerusActive())
1366 cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
1367 pk = CPubKey(ParseHex(CC.CChexstr));
1369 // send the entire amount to a reserve transfer output of the specific chain
1370 // we receive our fee on the other chain or when it comes back
1371 dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_RESERVE_DEPOSIT))});
1373 CReserveOutput ro(CReserveOutput::VALID, totalAmount + totalTxFees);
1375 CTxOut outToReserve = MakeCC1of1Vout(EVAL_RESERVE_DEPOSIT,
1381 tb.AddTransparentOutput(outToReserve.scriptPubKey, ro.nValue);
1387 TransactionBuilderResult buildResult(tb.Build());
1389 if (!buildResult.IsError() && buildResult.IsTx())
1391 // replace the last one only if we have a valid new one
1392 CTransaction tx = buildResult.GetTxOrThrow();
1394 LOCK2(cs_main, mempool.cs);
1395 static int lastHeight = 0;
1396 // remove conflicts, so that we get in
1397 std::list<CTransaction> removed;
1398 mempool.removeConflicts(tx, removed);
1400 // add to mem pool, prioritize according to the fee we will get, and relay
1401 printf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1402 LogPrintf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1403 if (myAddtomempool(tx))
1405 uint256 hash = tx.GetHash();
1406 mempool.PrioritiseTransaction(hash, hash.GetHex(), (double)(exportFees << 1), exportFees);
1407 RelayTransaction(tx);
1412 // we can't do any more useful work for this chain if we failed here
1413 printf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1414 LogPrintf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1419 // erase the inputs we've attempted to spend
1420 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1425 lastChain = it->first;
1431 // send new imports from this chain to the specified chain, which generally will be the notary chain
1432 void CConnectedChains::SendNewImports(const uint160 &chainID,
1433 const CPBaaSNotarization ¬arization,
1434 const uint256 &lastExportTx,
1435 const CTransaction &lastCrossImport,
1436 const CTransaction &lastExport)
1438 // currently only support sending imports to
1441 void CConnectedChains::SubmissionThread()
1445 arith_uint256 lastHash;
1446 int64_t lastImportTime = 0;
1447 uint32_t lastHeight = 0;
1449 // wait for something to check on, then submit blocks that should be submitted
1452 boost::this_thread::interruption_point();
1454 if (IsVerusActive())
1456 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
1457 //printf("SubmissionThread: pruning\n");
1458 PruneOldChains(GetAdjustedTime() - 300);
1459 bool submit = false;
1461 LOCK(cs_mergemining);
1462 if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
1464 qualifiedHeaders.clear();
1466 submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
1468 //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
1472 //printf("SubmissionThread: calling submit qualified blocks\n");
1473 SubmitQualifiedBlocks();
1477 //printf("SubmissionThread: waiting on sem\n");
1478 sem_submitthread.wait();
1483 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
1484 if (CheckVerusPBaaSAvailable())
1486 // check to see if we have recently earned a block with an earned notarization that qualifies for
1487 // submitting an accepted notarization
1488 if (earnedNotarizationHeight)
1491 int32_t txIndex = -1, height;
1493 LOCK(cs_mergemining);
1494 if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
1496 blk = earnedNotarizationBlock;
1497 earnedNotarizationBlock = CBlock();
1498 txIndex = earnedNotarizationIndex;
1499 height = earnedNotarizationHeight;
1500 earnedNotarizationHeight = 0;
1506 //printf("SubmissionThread: testing notarization\n");
1507 CTransaction lastConfirmed;
1508 uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
1512 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1513 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1518 // every "n" seconds, look for imports to include in our blocks from the Verus chain
1519 if ((GetAdjustedTime() - lastImportTime) >= 30 || lastHeight < (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight()))
1521 lastImportTime = GetAdjustedTime();
1522 lastHeight = (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight());
1524 // see if our notary has a confirmed notarization for us
1525 UniValue params(UniValue::VARR);
1528 params.push_back(thisChain.name);
1532 result = find_value(RPCCallRoot("getlastimportin", params), "result");
1533 } catch (exception e)
1535 result = NullUniValue;
1538 if (!result.isNull())
1540 auto txUniStr = find_value(result, "lastimporttransaction");
1541 auto txLastConfirmedStr = find_value(result, "lastconfirmednotarization");
1542 auto txTemplateStr = find_value(result, "importtxtemplate");
1543 CAmount totalImportAvailable = uni_get_int64(find_value(result, "totalimportavailable"));
1545 CTransaction lastImportTx, lastConfirmedTx, templateTx;
1547 if (txUniStr.isStr() && txTemplateStr.isStr() &&
1548 DecodeHexTx(lastImportTx, txUniStr.get_str()) &&
1549 DecodeHexTx(lastConfirmedTx, txLastConfirmedStr.get_str()) &&
1550 DecodeHexTx(templateTx, txTemplateStr.get_str()))
1552 std::vector<CTransaction> importTxes;
1553 if (CreateLatestImports(notaryChain.chainDefinition, lastImportTx, templateTx, lastConfirmedTx, totalImportAvailable, importTxes))
1555 for (auto importTx : importTxes)
1559 params.push_back(EncodeHexTx(importTx));
1563 txResult = find_value(RPCCallRoot("signrawtransaction", params), "result");
1564 if (txResult.isObject() && !(txResult = find_value(txResult, "hex")).isNull() && txResult.isStr() && txResult.get_str().size())
1567 params.push_back(txResult);
1568 txResult = find_value(RPCCallRoot("sendrawtransaction", params), "result");
1572 txResult = NullUniValue;
1575 } catch (exception e)
1577 txResult = NullUniValue;
1580 if (txResult.isStr())
1582 testId.SetHex(txResult.get_str());
1584 if (testId.IsNull())
1596 boost::this_thread::interruption_point();
1599 catch (const boost::thread_interrupted&)
1601 LogPrintf("Verus merge mining thread terminated\n");
1605 void CConnectedChains::SubmissionThreadStub()
1607 ConnectedChains.SubmissionThread();
1610 void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
1612 // called after winning a block that contains an earned notarization
1613 // the earned notarization and its height are queued for processing by the submission thread
1614 // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
1616 LOCK(cs_mergemining);
1618 // we only care about the last
1619 earnedNotarizationHeight = height;
1620 earnedNotarizationBlock = blk;
1621 earnedNotarizationIndex = txIndex;
1624 bool IsChainDefinitionInput(const CScript &scriptSig)
1627 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_PBAASDEFINITION;