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
14 #include "rpc/pbaasrpc.h"
17 #include "transaction_builder.h"
19 CConnectedChains ConnectedChains;
23 return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 0 || strcmp(ASSETCHAINS_SYMBOL, "VRSCTEST") == 0);
26 bool IsVerusMainnetActive()
28 return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 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 CBaseChainObject *retPtr;
54 const CChainObject<CBlockHeaderAndProof> *pNewHeader;
55 const CChainObject<CPartialTransactionProof> *pNewTx;
56 const CChainObject<CBlockHeaderProof> *pNewHeaderRef;
57 const CChainObject<CPriorBlocksCommitment> *pPriors;
58 const CChainObject<uint256> *pNewProofRoot;
59 const CChainObject<CReserveTransfer> *pExport;
60 const CChainObject<CCrossChainProof> *pCrossChainProof;
61 const CChainObject<CCompositeChainObject> *pCompositeChainObject;
69 return pNewHeader->GetHash();
71 case CHAINOBJ_TRANSACTION_PROOF:
72 return pNewTx->GetHash();
74 case CHAINOBJ_HEADER_REF:
75 return pNewHeaderRef->GetHash();
77 case CHAINOBJ_PRIORBLOCKS:
78 return pPriors->GetHash();
80 case CHAINOBJ_PROOF_ROOT:
81 return pNewProofRoot->object;
83 case CHAINOBJ_RESERVETRANSFER:
84 return pExport->GetHash();
86 case CHAINOBJ_CROSSCHAINPROOF:
87 return pCrossChainProof->GetHash();
89 case CHAINOBJ_COMPOSITEOBJECT:
90 return pCrossChainProof->GetHash();
96 // used to export coins from one chain to another, if they are not native, they are represented on the other
98 bool ValidateCrossChainExport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
103 bool IsCrossChainExportInput(const CScript &scriptSig)
108 // used to validate import of coins from one chain to another. if they are not native and are supported,
109 // they are represented o the chain as tokens
110 bool ValidateCrossChainImport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
114 bool IsCrossChainImportInput(const CScript &scriptSig)
119 // used to validate a specific service reward based on the spending transaction
120 bool ValidateServiceReward(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
122 // for each type of service reward, we need to check and see if the spender is
123 // correctly formatted to be a valid spend of the service reward. for notarization
124 // we ensure that the notarization and its outputs are valid and that the spend
125 // applies to the correct billing period
128 bool IsServiceRewardInput(const CScript &scriptSig)
133 // used as a proxy token output for a reserve currency on its fractional reserve chain
134 bool ValidateReserveOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
138 bool IsReserveOutputInput(const CScript &scriptSig)
143 bool ValidateReserveTransfer(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
147 bool IsReserveTransferInput(const CScript &scriptSig)
152 bool ValidateReserveDeposit(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
156 bool IsReserveDepositInput(const CScript &scriptSig)
161 bool ValidateCurrencyState(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
165 bool IsCurrencyStateInput(const CScript &scriptSig)
170 // used to convert a fractional reserve currency into its reserve and back
171 bool ValidateReserveExchange(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
175 bool IsReserveExchangeInput(const CScript &scriptSig)
182 * Verifies that the input objects match the hashes and returns the transaction.
184 * If the opRetTx has the op ret, this calculates based on the actual transaction and
185 * validates the hashes. If the opRetTx does not have the opRet itself, this validates
186 * by ensuring that all objects are present on this chain, composing the opRet, and
187 * ensuring that the transaction then hashes to the correct txid.
190 bool ValidateOpretProof(CScript &opRet, COpRetProof &orProof)
192 // enumerate through the objects and validate that they are objects of the expected type that hash
193 // to the value expected. return true if so
197 int8_t ObjTypeCode(const CBlockHeaderProof &obj)
199 return CHAINOBJ_HEADER;
202 int8_t ObjTypeCode(const uint256 &obj)
204 return CHAINOBJ_PROOF_ROOT;
207 int8_t ObjTypeCode(const CPartialTransactionProof &obj)
209 return CHAINOBJ_TRANSACTION_PROOF;
212 int8_t ObjTypeCode(const CBlockHeaderAndProof &obj)
214 return CHAINOBJ_HEADER_REF;
217 int8_t ObjTypeCode(const CPriorBlocksCommitment &obj)
219 return CHAINOBJ_PRIORBLOCKS;
222 int8_t ObjTypeCode(const CReserveTransfer &obj)
224 return CHAINOBJ_RESERVETRANSFER;
227 int8_t ObjTypeCode(const CCrossChainProof &obj)
229 return CHAINOBJ_CROSSCHAINPROOF;
232 int8_t ObjTypeCode(const CCompositeChainObject &obj)
234 return CHAINOBJ_COMPOSITEOBJECT;
237 // this adds an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction
238 CScript StoreOpRetArray(const std::vector<CBaseChainObject *> &objPtrs)
241 CDataStream s = CDataStream(SER_NETWORK, PROTOCOL_VERSION);
242 s << (int32_t)OPRETTYPE_OBJECTARR;
245 for (auto pobj : objPtrs)
249 if (!DehydrateChainObject(s, pobj))
255 catch(const std::exception& e)
257 std::cerr << e.what() << '\n';
263 //std::vector<unsigned char> schars(s.begin(), s.begin() + 200);
264 //printf("stream vector chars: %s\n", HexBytes(&schars[0], schars.size()).c_str());
266 std::vector<unsigned char> vch(s.begin(), s.end());
267 return error ? CScript() : CScript() << OP_RETURN << vch;
270 void DeleteOpRetObjects(std::vector<CBaseChainObject *> &ora)
272 for (auto pobj : ora)
274 switch(pobj->objectType)
276 case CHAINOBJ_HEADER:
278 delete (CChainObject<CBlockHeaderAndProof> *)pobj;
282 case CHAINOBJ_TRANSACTION_PROOF:
284 delete (CChainObject<CPartialTransactionProof> *)pobj;
288 case CHAINOBJ_PROOF_ROOT:
290 delete (CChainObject<uint256> *)pobj;
294 case CHAINOBJ_HEADER_REF:
296 delete (CChainObject<CBlockHeaderProof> *)pobj;
300 case CHAINOBJ_PRIORBLOCKS:
302 delete (CChainObject<CPriorBlocksCommitment> *)pobj;
306 case CHAINOBJ_RESERVETRANSFER:
308 delete (CChainObject<CReserveTransfer> *)pobj;
312 case CHAINOBJ_CROSSCHAINPROOF:
314 delete (CChainObject<CCrossChainProof> *)pobj;
318 case CHAINOBJ_COMPOSITEOBJECT:
320 delete (CChainObject<CCompositeChainObject> *)pobj;
331 std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
333 std::vector<unsigned char> vch;
334 std::vector<CBaseChainObject *> vRet;
335 if (opRetScript.IsOpReturn() && GetOpReturnData(opRetScript, vch) && vch.size() > 0)
337 CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION);
344 if (opRetType == OPRETTYPE_OBJECTARR)
346 CBaseChainObject *pobj;
347 while (!s.empty() && (pobj = RehydrateChainObject(s)))
349 vRet.push_back(pobj);
353 printf("failed to load all objects in opret");
354 DeleteOpRetObjects(vRet);
359 catch(const std::exception& e)
361 std::cerr << e.what() << '\n';
362 DeleteOpRetObjects(vRet);
369 CServiceReward::CServiceReward(const CTransaction &tx, bool validate)
371 nVersion = PBAAS_VERSION_INVALID;
372 for (auto out : tx.vout)
375 if (IsPayToCryptoCondition(out.scriptPubKey, p))
377 // always take the first for now
378 if (p.evalCode == EVAL_SERVICEREWARD)
380 FromVector(p.vData[0], *this);
392 CCrossChainExport::CCrossChainExport(const CTransaction &tx, int32_t *pCCXOutputNum)
394 int32_t _ccxOutputNum = 0;
395 int32_t &ccxOutputNum = pCCXOutputNum ? *pCCXOutputNum : _ccxOutputNum;
397 for (int i = 0; i < tx.vout.size(); i++)
400 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) &&
402 p.evalCode == EVAL_CROSSCHAIN_EXPORT)
404 FromVector(p.vData[0], *this);
411 CCurrencyDefinition::CCurrencyDefinition(const CScript &scriptPubKey)
413 nVersion = PBAAS_VERSION_INVALID;
415 if (scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
417 if (p.evalCode == EVAL_CURRENCY_DEFINITION)
419 FromVector(p.vData[0], *this);
424 std::vector<CCurrencyDefinition> CCurrencyDefinition::GetCurrencyDefinitions(const CTransaction &tx)
426 std::vector<CCurrencyDefinition> retVal;
427 for (auto &out : tx.vout)
429 CCurrencyDefinition oneCur = CCurrencyDefinition(out.scriptPubKey);
430 if (oneCur.IsValid())
432 retVal.push_back(oneCur);
438 uint160 CCurrencyDefinition::GetID(const std::string &Name, uint160 &Parent)
440 return CIdentity::GetID(Name, Parent);
443 uint160 CCurrencyDefinition::GetConditionID(int32_t condition) const
445 return CCrossChainRPCData::GetConditionID(name, condition);
448 #define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
449 extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
450 extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3], ASSETCHAINS_ERAOPTIONS[3];
451 extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
452 extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
453 extern std::string VERUS_CHAINNAME;
454 extern uint160 VERUS_CHAINID;
456 // ensures that the chain definition is valid and that there are no other definitions of the same name
457 // that have been confirmed.
458 bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
460 // the chain definition output can be spent when the chain is at the end of its life and only then
465 // ensures that the chain definition is valid and that there are no other definitions of the same name
466 // that have been confirmed.
467 bool CheckChainDefinitionOutputs(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
469 // checked before a chain definition output script is accepted as a valid transaction
471 // basics - we need a chain definition transaction to kick off a PBaaS chain. it must have:
472 // 1) valid chain definition output with parameters in proper ranges and no duplicate name
473 // 2) notarization output with conformant values
474 // 3) finalization output
475 // 3) notarization funding
478 // get the source transaction
481 if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
483 LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
487 std::vector<CCurrencyDefinition> chainDefs = CCurrencyDefinition::GetCurrencyDefinitions(thisTx);
488 CPBaaSNotarization notarization(thisTx);
489 CNotarizationFinalization finalization(thisTx);
490 bool isVerusActive = IsVerusActive();
492 if (!notarization.IsValid() || !finalization.IsValid())
494 LogPrintf("transaction specified, %s, must have valid notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
498 std::set<uint160> allCurrencyIDs;
499 for (auto &curPair : ConnectedChains.ReserveCurrencies())
501 allCurrencyIDs.insert(curPair.first);
503 allCurrencyIDs.insert(ConnectedChains.ThisChain().GetID());
506 allCurrencyIDs.insert(ConnectedChains.notaryChain.GetID());
509 bool isCoinbase = thisTx.IsCoinBase();
510 bool isVerified = false;
511 CIdentity activatedID(thisTx);
513 // currency definitions can be valid as follows:
514 // 1. original definition in a transaction that simultaneously sets the active currency bit on the identity of the same
516 // 2. outputs of a coinbase transaction in block 1 that defines the parent currency, new currency, and any reserve currencies
517 // 3. currency import from the defining chain of the currency, which has not been implemented as of this comment
518 if (activatedID.IsValid() &&
519 activatedID.HasActiveCurrency() &&
520 chainDefs.size() == 1 &&
521 activatedID.parent == ASSETCHAINS_CHAINID &&
522 activatedID.GetID() == chainDefs[0].GetID())
526 else if (isCoinbase && chainDefs.size() >= 1 && !isVerusActive)
529 CScript expect = CScript() << height1;
530 opcodetype opcode = (opcodetype)*expect.begin();
532 if (opcode >= OP_1 && opcode <= OP_16)
534 isVerified = (thisTx.vin[0].scriptSig.size() >= 1 && CScript::DecodeOP_N(opcode) == height1) ||
535 (thisTx.vin[0].scriptSig.size() >= 2 && thisTx.vin[0].scriptSig[0] == OP_PUSHDATA1 && (int)thisTx.vin[0].scriptSig[1] == height1);
539 isVerified = thisTx.vin[0].scriptSig.size() >= expect.size() && std::equal(expect.begin(), expect.end(), thisTx.vin[0].scriptSig.begin());
542 for (auto &chainDef : chainDefs)
544 uint160 chainID = chainDef.GetID();
545 if (!chainDef.IsValid())
547 LogPrintf("transaction specified, %s, must not contain invalid chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
550 if (!allCurrencyIDs.count(chainID))
552 LogPrintf("transaction specified, %s, must not contain invalid chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
555 allCurrencyIDs.erase(chainID);
557 if (allCurrencyIDs.size())
559 LogPrintf("transaction specified, %s, does not contain all required chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
567 CCurrencyValueMap CCrossChainExport::CalculateExportFee() const
569 CCurrencyValueMap retVal;
571 if (numInputs > MAX_EXPORT_INPUTS)
575 static const arith_uint256 satoshis(100000000);
577 arith_uint256 ratio(50000000 + ((25000000 / MAX_EXPORT_INPUTS) * (numInputs - 1)));
579 for (auto &feePair : totalFees.valueMap)
581 retVal.valueMap[feePair.first] = (((arith_uint256(feePair.second) * ratio)) / satoshis).GetLow64();
586 CCurrencyValueMap CCrossChainExport::CalculateImportFee() const
588 CCurrencyValueMap retVal;
590 for (auto &feePair : CalculateExportFee().valueMap)
592 CAmount feeAmount = feePair.second;
593 auto it = totalFees.valueMap.find(feePair.first);
594 retVal.valueMap[feePair.first] = (it != totalFees.valueMap.end() ? it->second : 0) - feeAmount;
599 bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
602 LOCK(cs_mergemining);
604 //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
606 auto chainIt = mergeMinedChains.find(chainID);
607 if (chainIt != mergeMinedChains.end())
609 arith_uint256 target;
610 target.SetCompact(chainIt->second.block.nBits);
611 for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
613 // make sure we don't just match by target
614 if (removeRange.first->second->GetID() == chainID)
616 mergeMinedTargets.erase(removeRange.first);
620 mergeMinedChains.erase(chainID);
621 dirty = retval = true;
623 // if we get to 0, give the thread a kick to stop waiting for mining
624 //if (!mergeMinedChains.size())
626 // sem_submitthread.post();
632 // remove merge mined chains added and not updated since a specific time
633 void CConnectedChains::PruneOldChains(uint32_t pruneBefore)
635 vector<uint160> toRemove;
637 LOCK(cs_mergemining);
638 for (auto blkData : mergeMinedChains)
640 if (blkData.second.block.nTime < pruneBefore)
642 toRemove.push_back(blkData.first);
646 for (auto id : toRemove)
648 //printf("Pruning chainID: %s\n", id.GetHex().c_str());
649 RemoveMergedBlock(id);
653 // adds or updates merge mined blocks
654 // returns false if failed to add
655 bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
657 // determine if we should replace one or add to the merge mine vector
659 LOCK(cs_mergemining);
661 arith_uint256 target;
662 uint160 cID = blkData.GetID();
663 auto it = mergeMinedChains.find(cID);
664 if (it != mergeMinedChains.end())
666 RemoveMergedBlock(cID); // remove it if already there
668 target.SetCompact(blkData.block.nBits);
670 //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
672 mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
678 bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
681 LOCK(cs_mergemining);
682 auto chainIt = mergeMinedChains.find(chainID);
683 if (chainIt != mergeMinedChains.end())
685 rpcChainData = (CRPCChainData)chainIt->second;
692 // this returns a pointer to the data without copy and assumes the lock is held
693 CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
696 auto chainIt = mergeMinedChains.find(chainID);
697 if (chainIt != mergeMinedChains.end())
699 return &chainIt->second;
705 void CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
707 //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
709 LOCK(cs_mergemining);
711 qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
713 sem_submitthread.post();
716 // get the latest block header and submit one block at a time, returning after there are no more
717 // matching blocks to be found
718 vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
720 std::set<uint160> inHeader;
721 bool submissionFound;
722 CPBaaSMergeMinedChainData chainData;
723 vector<pair<string, UniValue>> results;
726 arith_uint256 lastHash;
727 CPBaaSBlockHeader pbh;
731 submissionFound = false;
733 LOCK(cs_mergemining);
734 // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
735 // common, merge mined headers for notarization, drop out on any submission
736 for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
738 // add the PBaaS chain ids from this header to a set for search
739 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
741 inHeader.insert(pbh.chainID);
745 // now look through all targets that are equal to or above the hash of this header
746 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
748 chainID = chainIt->second->GetID();
749 if (inHeader.count(chainID))
751 // first, check that the winning header matches the block that is there
752 CPBaaSPreHeader preHeader(chainIt->second->block);
753 preHeader.SetBlockData(headerIt->second);
755 // check if the block header matches the block's specific data, only then can we create a submission from this block
756 if (headerIt->second.CheckNonCanonicalData(chainID))
758 // save block as is, remove the block from merged headers, replace header, and submit
759 chainData = *chainIt->second;
761 *(CBlockHeader *)&chainData.block = headerIt->second;
763 submissionFound = true;
765 //else // not an error condition. code is here for debugging
767 // printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
770 //else // not an error condition. code is here for debugging
772 // printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
776 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
779 // once it is going to be submitted, remove block from this chain until a new one is added again
780 RemoveMergedBlock(chainID);
785 qualifiedHeaders.erase(headerIt);
791 // submit one block and loop again. this approach allows multiple threads
792 // to collectively empty the submission queue, mitigating the impact of
793 // any one stalled daemon
794 UniValue submitParams(UniValue::VARR);
795 submitParams.push_back(EncodeHexBlk(chainData.block));
796 UniValue result, error;
799 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
800 result = find_value(result, "result");
801 error = find_value(result, "error");
805 result = UniValue(e.what());
807 results.push_back(make_pair(chainData.chainDefinition.name, result));
808 if (result.isStr() || !error.isNull())
810 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());
814 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
817 } while (submissionFound);
821 // add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
822 uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
824 vector<uint160> inHeader;
825 vector<UniValue> toCombine;
826 arith_uint256 blkHash = UintToArith256(bh.GetHash());
827 arith_uint256 target(0);
829 CPBaaSBlockHeader pbh;
832 LOCK(cs_mergemining);
834 CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
836 for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
838 if (bh.GetPBaaSHeader(pbh, i))
840 inHeader.push_back(pbh.chainID);
844 // loop through the existing PBaaS chain ids in the header
845 // remove any that are not either this Chain ID or in our local collection and then add all that are present
846 for (uint32_t i = 0; i < inHeader.size(); i++)
848 auto it = mergeMinedChains.find(inHeader[i]);
849 if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
851 bh.DeletePBaaSHeader(i);
855 for (auto chain : mergeMinedChains)
857 // get the native PBaaS header for each chain and put it into the
858 // header we are given
859 // it must have itself in as a PBaaS header
860 uint160 cid = chain.second.GetID();
861 if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
863 if (!bh.AddUpdatePBaaSHeader(pbh))
865 LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
871 t.SetCompact(chain.second.block.nBits);
880 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
887 return target.GetCompact();
890 bool CConnectedChains::IsVerusPBaaSAvailable()
892 return notaryChainVersion >= "0.6.4";
895 extern string PBAAS_HOST, PBAAS_USERPASS;
896 extern int32_t PBAAS_PORT;
897 bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
899 if (chainInfoUni.isObject() && chainDefUni.isObject())
901 UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
904 LOCK(cs_mergemining);
905 notaryChainVersion = uni_get_str(uniVer);
906 notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
907 CCurrencyDefinition chainDef(chainDefUni);
908 notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
911 return IsVerusPBaaSAvailable();
914 uint32_t CConnectedChains::NotaryChainHeight()
916 LOCK(cs_mergemining);
917 return notaryChainHeight;
920 bool CConnectedChains::CheckVerusPBaaSAvailable()
924 notaryChainVersion = "";
928 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
929 // tolerate only 15 second timeout
930 UniValue chainInfo, chainDef;
933 UniValue params(UniValue::VARR);
934 chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
935 if (!chainInfo.isNull())
937 params.push_back(VERUS_CHAINNAME);
938 chainDef = find_value(RPCCallRoot("getcurrencydefinition", params), "result");
940 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
942 // if we have not past block 1 yet, store the best known update of our current state
943 if ((!chainActive.LastTip() || !chainActive.LastTip()->GetHeight()))
945 bool success = false;
947 params.push_back(EncodeDestination(CIdentityID(thisChain.GetID())));
948 chainDef = find_value(RPCCallRoot("getcurrencydefinition", params), "result");
949 if (!chainDef.isNull())
951 CCurrencyDefinition currencyDef(chainDef);
952 if (currencyDef.IsValid())
954 thisChain = currencyDef;
955 if (NotaryChainHeight() >= thisChain.startBlock)
957 readyToStart = true; // this only gates mining of block one, to be sure we have the latest definition
967 } catch (exception e)
969 LogPrintf("%s: Error communicating with %s chain\n", __func__, VERUS_CHAINNAME);
972 notaryChainVersion = "";
976 int CConnectedChains::GetThisChainPort() const
980 for (auto node : defaultPeerNodes)
982 SplitHostPort(node.networkAddress, port, host);
991 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
993 CCoinbaseCurrencyState currencyState;
996 bool isVerusActive = IsVerusActive();
997 if (!isVerusActive &&
998 CConstVerusSolutionVector::activationHeight.ActiveVersion(height) >= CActivationHeight::ACTIVATE_PBAAS &&
1000 height <= chainActive.Height() &&
1001 chainActive[height] &&
1002 ReadBlockFromDisk(block, chainActive[height], Params().GetConsensus()) &&
1003 (currencyState = CCoinbaseCurrencyState(block.vtx[0])).IsValid())
1005 return currencyState;
1009 return GetInitialCurrencyState(thisChain);
1013 bool CConnectedChains::SetLatestMiningOutputs(const std::vector<pair<int, CScript>> &minerOutputs, CTxDestination &firstDestinationOut)
1015 LOCK(cs_mergemining);
1017 if (!minerOutputs.size() || !ExtractDestination(minerOutputs[0].second, firstDestinationOut))
1021 latestMiningOutputs = minerOutputs;
1022 latestDestination = firstDestinationOut;
1026 void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
1028 // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
1035 std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
1036 std::multimap<std::pair<uint160, uint160>, std::pair<CInputDescriptor, CReserveTransfer>> sortedTransferOutputs;
1037 std::set<uint160> failedLaunches;
1038 std::map<uint160, CCurrencyDefinition> currencyDefCache; // keep cache as we look up definitions
1042 uint160 thisChainID = ConnectedChains.ThisChain().GetID();
1044 // get all available transfer outputs to aggregate into export transactions
1045 if (GetUnspentChainTransfers(transferOutputs))
1047 if (!transferOutputs.size())
1051 for (auto &output : transferOutputs)
1053 CCurrencyDefinition sourceDef, destDef, systemDef;
1054 auto it = currencyDefCache.find(output.second.second.currencyID);
1055 if ((it != currencyDefCache.end() && !(sourceDef = it->second).IsValid()) ||
1056 (it == currencyDefCache.end() && !GetCurrencyDefinition(output.second.second.currencyID, sourceDef)))
1058 printf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.currencyID)).c_str());
1059 LogPrintf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.currencyID)).c_str());
1062 if (it == currencyDefCache.end())
1064 currencyDefCache[output.second.second.currencyID] = sourceDef;
1067 it = currencyDefCache.find(output.second.second.destCurrencyID);
1068 if ((it != currencyDefCache.end() && !(destDef = it->second).IsValid()) ||
1069 (it == currencyDefCache.end() && !GetCurrencyDefinition(output.second.second.destCurrencyID, destDef)))
1071 printf("%s: definition for destination currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.destCurrencyID)).c_str());
1072 LogPrintf("%s: definition for destination currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.destCurrencyID)).c_str());
1075 if (it == currencyDefCache.end())
1077 currencyDefCache[output.second.second.currencyID] = destDef;
1080 // if currency is controlled by this chain and it hasn't started yet, skip it
1081 if (destDef.systemID == ASSETCHAINS_CHAINID && destDef.startBlock > nHeight)
1086 if (destDef.systemID == ASSETCHAINS_CHAINID)
1088 // see if the chain has failed to launch, and we haven't recorded that yet, do so
1089 CCurrencyValueMap minPreMap, preConvertedMap;
1090 uint160 destID = destDef.GetID();
1091 if (!failedLaunches.count(destID) &&
1092 destDef.minPreconvert.size() &&
1093 (minPreMap = CCurrencyValueMap(destDef.currencies, destDef.minPreconvert)) > preConvertedMap &&
1094 (preConvertedMap = CCurrencyValueMap(destDef.currencies, GetInitialCurrencyState(destDef).reserveIn)) < minPreMap)
1096 failedLaunches.insert(destID);
1098 // if this currency is controlled by the ID that created it, consider the currency def its own system
1099 // even for these types of currencies, pre-convert is considered an automatic chain function controlled by this
1100 // chain. If any centralized participatory control is desired, it will currenty require that qualified
1101 // pre-conversions be handled off chain and posted by the currency's controller. All chain defined preconversions
1102 // are available to all participants.
1103 if (destDef.proofProtocol == CCurrencyDefinition::PROOF_CHAINID && !(output.second.second.flags & CReserveTransfer::PRECONVERT))
1105 // controller of the currency is responsible for fielding export transactions
1106 systemDef = destDef;
1110 systemDef = ConnectedChains.ThisChain();
1112 currencyDefCache[ASSETCHAINS_CHAINID] = systemDef;
1116 it = currencyDefCache.find(destDef.systemID);
1117 if ((it != currencyDefCache.end() && !(systemDef = it->second).IsValid()) ||
1118 (it == currencyDefCache.end() && !GetCurrencyDefinition(destDef.systemID, systemDef)))
1120 printf("%s: definition for export system ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
1121 LogPrintf("%s: definition for export system ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
1124 if (it == currencyDefCache.end())
1126 currencyDefCache[destDef.systemID] = systemDef;
1130 // if tokens on this chain, group like currencies together, otherwise don't
1131 if (systemDef.GetID() == thisChainID)
1133 sortedTransferOutputs.insert({{output.first, output.second.second.currencyID}, output.second});
1137 sortedTransferOutputs.insert({{output.first, uint160()}, output.second});
1141 std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
1142 std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1144 // this pair will sort to the end
1145 std::pair<uint160, uint160> bookEnd({uint160(ParseHex("ffffffffffffffffffffffffffffffffffffffff")), uint160(ParseHex("ffffffffffffffffffffffffffffffffffffffff"))});
1146 std::pair<uint160, uint160> lastChain = bookEnd;
1148 // add a bookend entry at the end of transfer outputs to ensure that we try to export all before it
1149 sortedTransferOutputs.insert(make_pair(bookEnd, make_pair(CInputDescriptor(), CReserveTransfer())));
1151 CCurrencyDefinition lastChainDef;
1153 // merge all of the common chainID outputs into common export transactions if either MIN_BLOCKS blocks have passed since the last
1154 // export of that type, or there are MIN_INPUTS or more outputs to aggregate
1155 for (auto it = sortedTransferOutputs.begin(); it != sortedTransferOutputs.end(); it++)
1157 // get chain target and see if it is the same
1158 if (lastChain.first == bookEnd.first || it->first.first == lastChain.first)
1160 txInputs.push_back(it->second);
1164 // when we get here, we have a consecutive number of transfer outputs to consume in txInputs
1165 // we need an unspent export output to export, or use the last one of it is an export to the same
1167 if (GetUnspentChainExports(lastChain.first, exportOutputs) && exportOutputs.size())
1169 auto lastExport = *exportOutputs.begin();
1171 if (((nHeight - lastExport.second.first) >= CCrossChainExport::MIN_BLOCKS) || (txInputs.size() >= CCrossChainExport::MIN_INPUTS))
1173 boost::optional<CTransaction> oneExport;
1175 // make one or more transactions that spends the last export and all possible cross chain transfers
1176 while (txInputs.size())
1178 TransactionBuilder tb(Params().GetConsensus(), nHeight);
1181 int inputsLeft = txInputs.size();
1183 if (it->first.second.IsNull())
1185 numInputs = txInputs.size();
1189 for (int i = 0; i < txInputs.size(); i++)
1191 if (txInputs[i].second.currencyID == it->first.second)
1202 if (numInputs > CCrossChainExport::MAX_EXPORT_INPUTS)
1204 numInputs = CCrossChainExport::MAX_EXPORT_INPUTS;
1206 inputsLeft = txInputs.size() - numInputs;
1207 if (inputsLeft > 0 && inputsLeft < CCrossChainExport::MIN_INPUTS)
1209 inputsLeft += CCrossChainExport::MIN_INPUTS - inputsLeft;
1210 numInputs -= CCrossChainExport::MIN_INPUTS - inputsLeft;
1211 assert(numInputs > 0);
1214 // each time through, we make one export transaction with the remainder or a subset of the
1215 // reserve transfer inputs. inputs can be:
1216 // 1. transfers of reserve for fractional reserve chains
1217 // 2. pre-conversions for pre-launch participation in the premine
1218 // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
1220 // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
1221 // or reserve token inputs.
1223 // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
1224 // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
1225 // as part of the import process
1227 // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain.
1228 // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the
1229 // remaining Verus reserve coin will be burned on the PBaaS chain as spending it is allowed, once notarized, on the
1232 CCurrencyValueMap totalTxFees;
1233 CCurrencyValueMap totalAmounts;
1234 CAmount exportOutVal = 0;
1235 std::vector<CBaseChainObject *> chainObjects;
1237 // first, we must add the export output from the current export thread to this chain
1238 if (oneExport.is_initialized())
1240 // spend the last export transaction output
1241 CTransaction &tx = oneExport.get();
1244 for (j = 0; j < tx.vout.size(); j++)
1246 if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) && p.evalCode == EVAL_CROSSCHAIN_EXPORT)
1252 // had to be found and valid if we made the tx
1253 assert(j < tx.vout.size() && p.IsValid());
1255 tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
1256 exportOutVal = tx.vout[j].nValue;
1260 // spend the recentExportIt output
1261 tb.AddTransparentInput(lastExport.second.second.txIn.prevout, lastExport.second.second.scriptPubKey, lastExport.second.second.nValue);
1262 exportOutVal = lastExport.second.second.nValue;
1266 std::vector<int> toRemove;
1267 for (int j = 0; j < numInputs; j++)
1269 tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
1270 CCurrencyValueMap newTransferInput = txInputs[j].first.scriptPubKey.ReserveOutValue();
1271 newTransferInput.valueMap[ASSETCHAINS_CHAINID] = txInputs[j].first.nValue;
1273 // TODO: make fee currency calculation more flexible on conversion
1274 // rules should be pay fee in native currency of source system
1275 uint160 feeCurrencyID = currencyDefCache[txInputs[j].second.currencyID].systemID;
1277 CCurrencyValueMap newTransferOutput;
1278 newTransferOutput.valueMap[txInputs[j].second.currencyID] = txInputs[j].second.nValue;
1279 newTransferOutput.valueMap[feeCurrencyID] += txInputs[j].second.nFees;
1281 if ((newTransferInput - newTransferOutput).HasNegative())
1283 // 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
1284 // 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
1285 // we should formalize this into a chain contribution or amount adjustment.
1286 printf("%s: transaction %s claims incorrect value\n", __func__, txInputs[j].first.txIn.prevout.hash.GetHex().c_str());
1287 LogPrintf("%s: transaction %s claims incorrect value\n", __func__, txInputs[j].first.txIn.prevout.hash.GetHex().c_str());
1288 toRemove.push_back(j);
1292 totalAmounts += newTransferOutput;
1293 totalTxFees.valueMap[feeCurrencyID] += txInputs[j].second.nFees;
1294 CReserveTransfer rt(txInputs[j].second);
1295 if (failedLaunches.count(rt.currencyID))
1297 // turn it into a normal transfer, which will create an unconverted output
1298 rt.flags &= ~(CReserveTransfer::SEND_BACK | CReserveTransfer::PRECONVERT | CReserveTransfer::CONVERT);
1300 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(rt), rt));
1303 // remove in reverse order so one removal does not affect the position of the next
1304 for (int j = toRemove.size() - 1; j >= 0; j--)
1306 txInputs.erase(txInputs.begin() + toRemove[j]);
1314 CCrossChainExport ccx(lastChain.first, numInputs, totalAmounts, totalTxFees);
1316 // make extra outputs for fees in each currency
1317 for (auto &outPair : ccx.CalculateExportFee().valueMap)
1319 CReserveTransfer feeOut(CReserveTransfer::VALID + CReserveTransfer::FEE_OUTPUT,
1320 outPair.first, outPair.second, 0, lastChain.first, DestinationToTransferDestination(feeOutput));
1321 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
1324 // do a preliminary check
1325 CReserveTransactionDescriptor rtxd;
1326 std::vector<CTxOut> vOutputs;
1328 if (!rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain().GetID(), lastChainDef, chainObjects, vOutputs))
1330 DeleteOpRetObjects(chainObjects);
1332 printf("%s: failed to create valid exports\n", __func__);
1333 LogPrintf("%s: failed to create valid exports\n", __func__);
1336 printf("%s: failed to export outputs:\n", __func__);
1337 for (auto oneout : vOutputs)
1340 ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1341 printf("%s\n", uniOut.write(true, 2).c_str());
1347 CCcontract_info *cp;
1350 printf("%s: exported outputs:\n", __func__);
1351 for (auto &oneout : chainObjects)
1353 if (oneout->objectType == CHAINOBJ_RESERVETRANSFER)
1355 CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)(oneout))->object;
1356 printf("%s\n", rt.ToUniValue().write(true, 2).c_str());
1360 CScript opRet = StoreOpRetArray(chainObjects);
1361 DeleteOpRetObjects(chainObjects);
1363 cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
1364 for (auto &oneCurrencyOut : ccx.totalAmounts.valueMap)
1366 CCurrencyDefinition oneDef = currencyDefCache[oneCurrencyOut.first];
1367 // if we control this currency, it must go to reserve deposit of the system it is
1368 // being exported to
1369 if (oneDef.systemID == ASSETCHAINS_CHAINID)
1371 CAmount nativeOut = oneDef.GetID() == ASSETCHAINS_CHAINID ? oneCurrencyOut.second : 0;
1373 // send the entire amount to a reserve transfer output of the specific chain
1374 // we receive our fee on the other chain or when it comes back
1375 std::vector<CTxDestination> indexDests({CKeyID(CCrossChainRPCData::GetConditionID(oneDef.GetID(), EVAL_RESERVE_DEPOSIT))});
1376 std::vector<CTxDestination> dests({CPubKey(ParseHex(CC.CChexstr))});
1378 CTokenOutput ro(oneCurrencyOut.first, oneCurrencyOut.second);
1380 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_DEPOSIT, dests, 1, &ro), &indexDests),
1385 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
1387 // send native amount of zero to a cross chain export output of the specific chain
1388 std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain.first, EVAL_CROSSCHAIN_EXPORT))});
1389 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
1391 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx), &indexDests),
1397 TransactionBuilderResult buildResult(tb.Build());
1399 if (!buildResult.IsError() && buildResult.IsTx())
1401 // replace the last one only if we have a valid new one
1402 CTransaction tx = buildResult.GetTxOrThrow();
1404 LOCK2(cs_main, mempool.cs);
1405 static int lastHeight = 0;
1406 // remove conflicts, so that we get in
1407 std::list<CTransaction> removed;
1408 mempool.removeConflicts(tx, removed);
1410 // add to mem pool, prioritize according to the fee we will get, and relay
1411 printf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1412 LogPrintf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1413 if (myAddtomempool(tx))
1415 uint256 hash = tx.GetHash();
1416 CAmount nativeExportFees = ccx.totalFees.valueMap[ASSETCHAINS_CHAINID];
1417 mempool.PrioritiseTransaction(hash, hash.GetHex(), (double)(nativeExportFees << 1), nativeExportFees);
1418 RelayTransaction(tx);
1423 // we can't do any more useful work for this chain if we failed here
1424 printf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1425 LogPrintf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1430 // erase the inputs we've attempted to spend
1431 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1436 lastChain = it->first;
1442 // send new imports from this chain to the specified chain, which generally will be the notary chain
1443 void CConnectedChains::SendNewImports(const uint160 &chainID,
1444 const CPBaaSNotarization ¬arization,
1445 const uint256 &lastExportTx,
1446 const CTransaction &lastCrossImport,
1447 const CTransaction &lastExport)
1449 // currently only support sending imports to
1452 void CConnectedChains::SubmissionThread()
1456 arith_uint256 lastHash;
1457 int64_t lastImportTime = 0;
1458 uint32_t lastHeight = 0;
1460 // wait for something to check on, then submit blocks that should be submitted
1463 boost::this_thread::interruption_point();
1465 if (IsVerusActive())
1467 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
1468 //printf("SubmissionThread: pruning\n");
1469 PruneOldChains(GetAdjustedTime() - 300);
1470 bool submit = false;
1472 LOCK(cs_mergemining);
1473 if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
1475 qualifiedHeaders.clear();
1477 submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
1479 //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
1483 //printf("SubmissionThread: calling submit qualified blocks\n");
1484 SubmitQualifiedBlocks();
1488 //printf("SubmissionThread: waiting on sem\n");
1489 sem_submitthread.wait();
1494 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
1495 if (CheckVerusPBaaSAvailable())
1497 // check to see if we have recently earned a block with an earned notarization that qualifies for
1498 // submitting an accepted notarization
1499 if (earnedNotarizationHeight)
1502 int32_t txIndex = -1, height;
1504 LOCK(cs_mergemining);
1505 if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
1507 blk = earnedNotarizationBlock;
1508 earnedNotarizationBlock = CBlock();
1509 txIndex = earnedNotarizationIndex;
1510 height = earnedNotarizationHeight;
1511 earnedNotarizationHeight = 0;
1517 //printf("SubmissionThread: testing notarization\n");
1518 CTransaction lastConfirmed;
1519 uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
1523 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1524 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1529 // every "n" seconds, look for imports to include in our blocks from the Verus chain
1530 if ((GetAdjustedTime() - lastImportTime) >= 30 || lastHeight < (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight()))
1532 lastImportTime = GetAdjustedTime();
1533 lastHeight = (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight());
1535 // see if our notary has a confirmed notarization for us
1536 UniValue params(UniValue::VARR);
1539 params.push_back(thisChain.name);
1543 result = find_value(RPCCallRoot("getlastimportin", params), "result");
1544 } catch (exception e)
1546 result = NullUniValue;
1549 if (!result.isNull())
1551 auto txUniStr = find_value(result, "lastimporttransaction");
1552 auto txLastConfirmedStr = find_value(result, "lastconfirmednotarization");
1553 auto txTemplateStr = find_value(result, "importtxtemplate");
1554 CAmount nativeImportAvailable = uni_get_int64(find_value(result, "nativeimportavailable"));
1555 CCurrencyValueMap tokenImportAvailable(find_value(params[0], "tokenimportavailable"));
1557 CTransaction lastImportTx, lastConfirmedTx, templateTx;
1559 if (txUniStr.isStr() && txTemplateStr.isStr() &&
1560 DecodeHexTx(lastImportTx, txUniStr.get_str()) &&
1561 DecodeHexTx(lastConfirmedTx, txLastConfirmedStr.get_str()) &&
1562 DecodeHexTx(templateTx, txTemplateStr.get_str()))
1564 std::vector<CTransaction> importTxes;
1565 if (CreateLatestImports(notaryChain.chainDefinition, lastImportTx, templateTx, lastConfirmedTx, tokenImportAvailable, nativeImportAvailable, importTxes))
1567 for (auto importTx : importTxes)
1571 params.push_back(EncodeHexTx(importTx));
1575 txResult = find_value(RPCCallRoot("signrawtransaction", params), "result");
1576 if (txResult.isObject() && !(txResult = find_value(txResult, "hex")).isNull() && txResult.isStr() && txResult.get_str().size())
1579 params.push_back(txResult);
1580 txResult = find_value(RPCCallRoot("sendrawtransaction", params), "result");
1584 txResult = NullUniValue;
1587 } catch (exception e)
1589 txResult = NullUniValue;
1592 if (txResult.isStr())
1594 testId.SetHex(txResult.get_str());
1596 if (testId.IsNull())
1608 boost::this_thread::interruption_point();
1611 catch (const boost::thread_interrupted&)
1613 LogPrintf("Verus merge mining thread terminated\n");
1617 void CConnectedChains::SubmissionThreadStub()
1619 ConnectedChains.SubmissionThread();
1622 void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
1624 // called after winning a block that contains an earned notarization
1625 // the earned notarization and its height are queued for processing by the submission thread
1626 // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
1628 LOCK(cs_mergemining);
1630 // we only care about the last
1631 earnedNotarizationHeight = height;
1632 earnedNotarizationBlock = blk;
1633 earnedNotarizationIndex = txIndex;
1636 bool IsChainDefinitionInput(const CScript &scriptSig)
1639 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_CURRENCY_DEFINITION;