1 /********************************************************************
2 * (C) 2019 Michael Toutonghi
4 * Distributed under the MIT software license, see the accompanying
5 * file COPYING or http://www.opensource.org/licenses/mit-license.php.
7 * This provides support for PBaaS initialization, notarization, and cross-chain token
8 * transactions and enabling liquid or non-liquid tokens across the
13 #include "pbaas/pbaas.h"
14 #include "pbaas/notarization.h"
15 #include "rpc/pbaasrpc.h"
16 #include "pbaas/crosschainrpc.h"
23 CConnectedChains ConnectedChains;
27 return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 0 || strcmp(ASSETCHAINS_SYMBOL, "VRSCTEST") == 0);
30 // this adds an opret to a mutable transaction and returns the voutnum if it could be added
31 int32_t AddOpRetOutput(CMutableTransaction &mtx, const CScript &opRetScript)
33 if (opRetScript.IsOpReturn() && opRetScript.size() <= MAX_OP_RETURN_RELAY)
35 CTxOut vOut = CTxOut();
36 vOut.scriptPubKey = opRetScript;
38 mtx.vout.push_back(vOut);
39 return mtx.vout.size() - 1;
47 // returns a pointer to a base chain object, which can be cast to the
48 // object type indicated in its objType member
49 uint256 GetChainObjectHash(const CBaseChainObject &bo)
52 const CChainObject<CBlockHeader> *pNewHeader;
53 const CChainObject<CTransaction> *pNewTx;
54 const CChainObject<CMerkleBranch> *pNewProof;
55 const CChainObject<CHeaderRef> *pNewHeaderRef;
56 const CChainObject<CPriorBlocksCommitment> *pPriors;
57 const CChainObject<CReserveTransfer> *pExport;
58 const CBaseChainObject *retPtr;
66 return pNewHeader->GetHash();
68 case CHAINOBJ_TRANSACTION:
69 return pNewTx->GetHash();
72 return pNewProof->GetHash();
74 case CHAINOBJ_HEADER_REF:
75 return pNewHeaderRef->GetHash();
77 case CHAINOBJ_PRIORBLOCKS:
78 return pPriors->GetHash();
80 case CHAINOBJ_RESERVETRANSFER:
81 return pExport->GetHash();
86 // used to export coins from one chain to another, if they are not native, they are represented on the other
88 bool ValidateChainExport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
93 // used to validate import of coins from one chain to another. if they are not native and are supported,
94 // they are represented o the chain as tokens
95 bool ValidateChainImport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
100 // used to validate a specific service reward based on the spending transaction
101 bool ValidateServiceReward(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
103 // for each type of service reward, we need to check and see if the spender is
104 // correctly formatted to be a valid spend of the service reward. for notarization
105 // we ensure that the notarization and its outputs are valid and that the spend
106 // applies to the correct billing period
110 // used as a proxy token output for a reserve currency on its fractional reserve chain
111 bool ValidateReserveOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
116 // used to convert a fractional reserve currency into its reserve and back
117 bool ValidateReserveExchange(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
122 // used for distribution of premine
123 bool ValidatePremineOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
129 * Verifies that the input objects match the hashes and returns the transaction.
131 * If the opRetTx has the op ret, this calculates based on the actual transaction and
132 * validates the hashes. If the opRetTx does not have the opRet itself, this validates
133 * by ensuring that all objects are present on this chain, composing the opRet, and
134 * ensuring that the transaction then hashes to the correct txid.
137 bool ValidateOpretProof(CScript &opRet, COpRetProof &orProof)
139 // enumerate through the objects and validate that they are objects of the expected type that hash
140 // to the value expected. return true if so
144 int8_t ObjTypeCode(const CBlockHeader &obj)
146 return CHAINOBJ_HEADER;
149 int8_t ObjTypeCode(const CMerkleBranch &obj)
151 return CHAINOBJ_PROOF;
154 int8_t ObjTypeCode(const CTransaction &obj)
156 return CHAINOBJ_TRANSACTION;
159 int8_t ObjTypeCode(const CHeaderRef &obj)
161 return CHAINOBJ_HEADER_REF;
164 int8_t ObjTypeCode(const CPriorBlocksCommitment &obj)
166 return CHAINOBJ_PRIORBLOCKS;
169 int8_t ObjTypeCode(const CReserveTransfer &obj)
171 return CHAINOBJ_RESERVETRANSFER;
174 // this adds an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction
175 CScript StoreOpRetArray(std::vector<CBaseChainObject *> &objPtrs)
178 CDataStream s = CDataStream(SER_NETWORK, PROTOCOL_VERSION);
179 s << (int32_t)OPRETTYPE_OBJECTARR;
182 for (auto pobj : objPtrs)
186 if (!DehydrateChainObject(s, pobj))
192 catch(const std::exception& e)
194 std::cerr << e.what() << '\n';
200 //std::vector<unsigned char> schars(s.begin(), s.begin() + 200);
201 //printf("stream vector chars: %s\n", HexBytes(&schars[0], schars.size()).c_str());
203 std::vector<unsigned char> vch(s.begin(), s.end());
204 return error ? CScript() : CScript() << OP_RETURN << vch;
207 void DeleteOpRetObjects(std::vector<CBaseChainObject *> &ora)
209 for (auto pobj : ora)
211 switch(pobj->objectType)
213 case CHAINOBJ_HEADER:
215 delete (CChainObject<CBlockHeader> *)pobj;
219 case CHAINOBJ_TRANSACTION:
221 delete (CChainObject<CTransaction> *)pobj;
227 delete (CChainObject<CMerkleBranch> *)pobj;
231 case CHAINOBJ_HEADER_REF:
233 delete (CChainObject<CHeaderRef> *)pobj;
237 case CHAINOBJ_PRIORBLOCKS:
239 delete (CChainObject<CPriorBlocksCommitment> *)pobj;
243 case CHAINOBJ_RESERVETRANSFER:
245 delete (CChainObject<CReserveTransfer> *)pobj;
255 std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
257 std::vector<unsigned char> vch;
258 std::vector<CBaseChainObject *> vRet;
259 if (opRetScript.IsOpReturn() && GetOpReturnData(opRetScript, vch) && vch.size() > 0)
261 CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION);
268 if (opRetType == OPRETTYPE_OBJECTARR)
270 CBaseChainObject *pobj;
271 while (!s.empty() && (pobj = RehydrateChainObject(s)))
273 vRet.push_back(pobj);
277 printf("failed to load all objects in opret");
278 DeleteOpRetObjects(vRet);
283 catch(const std::exception& e)
285 std::cerr << e.what() << '\n';
286 DeleteOpRetObjects(vRet);
293 CNodeData::CNodeData(UniValue &obj)
295 networkAddress = uni_get_str(find_value(obj, "networkaddress"));
296 CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
297 ba.GetKeyID(paymentAddress);
300 UniValue CNodeData::ToUniValue() const
302 UniValue obj(UniValue::VOBJ);
303 obj.push_back(Pair("networkaddress", networkAddress));
304 obj.push_back(Pair("paymentaddress", CBitcoinAddress(paymentAddress).ToString()));
308 CPBaaSChainDefinition::CPBaaSChainDefinition(const UniValue &obj)
310 nVersion = PBAAS_VERSION;
311 name = std::string(uni_get_str(find_value(obj, "name")), 0, (KOMODO_ASSETCHAIN_MAXLEN - 1));
313 string invalidChars = "\\/:?\"<>|";
314 for (int i = 0; i < name.size(); i++)
316 if (invalidChars.find(name[i]) != string::npos)
322 CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
323 ba.GetKeyID(address);
324 premine = uni_get_int64(find_value(obj, "premine"));
325 initialcontribution = uni_get_int64(find_value(obj, "initialcontribution"));
326 conversion = uni_get_int64(find_value(obj, "conversion"));
327 maxpreconvert = uni_get_int64(find_value(obj, "maxpreconvert"));
328 preconverted = uni_get_int64(find_value(obj, "preconverted"));
329 launchFee = uni_get_int64(find_value(obj, "launchfee"));
330 startBlock = uni_get_int(find_value(obj, "startblock"));
331 endBlock = uni_get_int(find_value(obj, "endblock"));
333 auto vEras = uni_getValues(find_value(obj, "eras"));
334 if (vEras.size() > ASSETCHAINS_MAX_ERAS)
336 vEras.resize(ASSETCHAINS_MAX_ERAS);
338 eras = !vEras.size() ? 1 : vEras.size();
340 for (auto era : vEras)
342 rewards.push_back(uni_get_int64(find_value(era, "reward")));
343 rewardsDecay.push_back(uni_get_int64(find_value(era, "decay")));
344 halving.push_back(uni_get_int64(find_value(era, "halving")));
345 eraEnd.push_back(uni_get_int64(find_value(era, "eraend")));
346 eraOptions.push_back(uni_get_int64(find_value(era, "eraoptions")));
349 billingPeriod = uni_get_int(find_value(obj, "billingperiod"));
350 notarizationReward = uni_get_int64(find_value(obj, "notarizationreward"));
352 auto nodeVec = uni_getValues(find_value(obj, "nodes"));
353 for (auto node : nodeVec)
355 nodes.push_back(CNodeData(node));
359 CPBaaSChainDefinition::CPBaaSChainDefinition(const CTransaction &tx, bool validate)
361 bool definitionFound = false;
362 nVersion = PBAAS_VERSION_INVALID;
363 for (auto out : tx.vout)
366 if (IsPayToCryptoCondition(out.scriptPubKey, p))
368 if (p.evalCode == EVAL_PBAASDEFINITION)
372 nVersion = PBAAS_VERSION_INVALID;
376 FromVector(p.vData[0], *this);
378 // TODO - remove this after validation is finished, check now in case some larger strings got into the chain
379 name = std::string(name, 0, (KOMODO_ASSETCHAIN_MAXLEN - 1));
380 string invalidChars = "\\/:?\"<>|";
381 for (int i = 0; i < name.size(); i++)
383 if (invalidChars.find(name[i]) != string::npos)
389 definitionFound = true;
401 CServiceReward::CServiceReward(const CTransaction &tx, bool validate)
403 nVersion = PBAAS_VERSION_INVALID;
404 for (auto out : tx.vout)
407 if (IsPayToCryptoCondition(out.scriptPubKey, p))
409 // always take the first for now
410 if (p.evalCode == EVAL_SERVICEREWARD)
412 FromVector(p.vData[0], *this);
424 CCrossChainImport::CCrossChainImport(const CTransaction &tx)
426 for (auto out : tx.vout)
429 if (IsPayToCryptoCondition(out.scriptPubKey, p))
431 // always take the first for now
432 if (p.evalCode == EVAL_CROSSCHAIN_IMPORT)
434 FromVector(p.vData[0], *this);
441 CCrossChainExport::CCrossChainExport(const CTransaction &tx)
443 for (auto out : tx.vout)
446 if (IsPayToCryptoCondition(out.scriptPubKey, p))
448 // always take the first for now
449 if (p.evalCode == EVAL_CROSSCHAIN_EXPORT)
451 FromVector(p.vData[0], *this);
458 uint160 CPBaaSChainDefinition::GetChainID(std::string name)
460 const char *chainName = name.c_str();
461 uint256 chainHash = Hash(chainName, chainName + strlen(chainName));
462 return Hash160(chainHash.begin(), chainHash.end());
465 uint160 CPBaaSChainDefinition::GetConditionID(int32_t condition)
467 return CCrossChainRPCData::GetConditionID(name, condition);
470 UniValue CPBaaSChainDefinition::ToUniValue() const
472 UniValue obj(UniValue::VOBJ);
473 obj.push_back(Pair("version", (int64_t)nVersion));
474 obj.push_back(Pair("name", name));
475 obj.push_back(Pair("paymentaddress", CBitcoinAddress(CTxDestination(address)).ToString()));
476 obj.push_back(Pair("premine", (int64_t)premine));
477 obj.push_back(Pair("initialcontribution", (int64_t)initialcontribution));
478 obj.push_back(Pair("conversion", (int64_t)conversion));
479 obj.push_back(Pair("maxpreconvert", (int64_t)maxpreconvert));
480 obj.push_back(Pair("preconverted", (int64_t)preconverted));
481 obj.push_back(Pair("launchfee", (int64_t)launchFee));
482 obj.push_back(Pair("conversionpercent", (double)conversion / 100000000));
483 obj.push_back(Pair("launchfeepercent", ((double)launchFee / 100000000) * 100));
484 obj.push_back(Pair("startblock", (int32_t)startBlock));
485 obj.push_back(Pair("endblock", (int32_t)endBlock));
487 UniValue eraArr(UniValue::VARR);
488 for (int i = 0; i < eras; i++)
490 UniValue era(UniValue::VOBJ);
491 era.push_back(Pair("reward", rewards.size() > i ? rewards[i] : (int64_t)0));
492 era.push_back(Pair("decay", rewardsDecay.size() > i ? rewardsDecay[i] : (int64_t)0));
493 era.push_back(Pair("halving", halving.size() > i ? (int32_t)halving[i] : (int32_t)0));
494 era.push_back(Pair("eraend", eraEnd.size() > i ? (int32_t)eraEnd[i] : (int32_t)0));
495 era.push_back(Pair("eraoptions", eraOptions.size() > i ? (int32_t)eraOptions[i] : (int32_t)0));
496 eraArr.push_back(era);
498 obj.push_back(Pair("eras", eraArr));
500 obj.push_back(Pair("billingperiod", billingPeriod));
501 obj.push_back(Pair("notarizationreward", notarizationReward));
503 UniValue nodeArr(UniValue::VARR);
504 for (auto node : nodes)
506 nodeArr.push_back(node.ToUniValue());
508 obj.push_back(Pair("nodes", nodeArr));
513 int CPBaaSChainDefinition::GetDefinedPort() const
517 for (auto node : nodes)
519 SplitHostPort(node.networkAddress, port, host);
528 #define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
529 extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
530 extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3];
531 extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
532 extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
533 extern std::string VERUS_CHAINNAME;
535 // adds the chain definition for this chain and nodes as well
536 // this also sets up the notarization chain, if there is one
537 bool SetThisChain(UniValue &chainDefinition)
539 ConnectedChains.ThisChain() = CPBaaSChainDefinition(chainDefinition);
541 if (ConnectedChains.ThisChain().IsValid())
543 memset(ASSETCHAINS_SYMBOL, 0, sizeof(ASSETCHAINS_SYMBOL));
544 strcpy(ASSETCHAINS_SYMBOL, ConnectedChains.ThisChain().name.c_str());
546 // set all command line parameters into mapArgs from chain definition
547 vector<string> nodeStrs;
548 for (auto node : ConnectedChains.ThisChain().nodes)
550 nodeStrs.push_back(node.networkAddress);
554 mapMultiArgs["-seednode"] = nodeStrs;
556 if (int port = ConnectedChains.ThisChain().GetDefinedPort())
558 mapArgs["-port"] = to_string(port);
561 ASSETCHAINS_SUPPLY = ConnectedChains.ThisChain().premine;
562 mapArgs["-ac_supply"] = to_string(ASSETCHAINS_SUPPLY);
563 ASSETCHAINS_ALGO = ASSETCHAINS_VERUSHASH;
564 ASSETCHAINS_LWMAPOS = 50;
566 ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF;
567 ASSETCHAINS_TIMEUNLOCKFROM = 0;
568 ASSETCHAINS_TIMEUNLOCKTO = 0;
570 auto numEras = ConnectedChains.ThisChain().eras;
571 ASSETCHAINS_LASTERA = numEras - 1;
572 mapArgs["-ac_eras"] = to_string(numEras);
574 mapArgs["-ac_end"] = "";
575 mapArgs["-ac_reward"] = "";
576 mapArgs["-ac_halving"] = "";
577 mapArgs["-ac_decay"] = "";
579 for (int j = 0; j < ASSETCHAINS_MAX_ERAS; j++)
581 if (j > ASSETCHAINS_LASTERA)
583 ASSETCHAINS_REWARD[j] = ASSETCHAINS_REWARD[j-1];
584 ASSETCHAINS_DECAY[j] = ASSETCHAINS_DECAY[j-1];
585 ASSETCHAINS_HALVING[j] = ASSETCHAINS_HALVING[j-1];
586 ASSETCHAINS_ENDSUBSIDY[j] = 0;
590 ASSETCHAINS_REWARD[j] = ConnectedChains.ThisChain().rewards[j];
591 ASSETCHAINS_DECAY[j] = ConnectedChains.ThisChain().rewardsDecay[j];
592 ASSETCHAINS_HALVING[j] = ConnectedChains.ThisChain().halving[j];
593 ASSETCHAINS_ENDSUBSIDY[j] = ConnectedChains.ThisChain().eraEnd[j];
596 mapArgs["-ac_reward"] = to_string(ASSETCHAINS_REWARD[j]);
597 mapArgs["-ac_decay"] = to_string(ASSETCHAINS_DECAY[j]);
598 mapArgs["-ac_halving"] = to_string(ASSETCHAINS_HALVING[j]);
599 mapArgs["-ac_end"] = to_string(ASSETCHAINS_ENDSUBSIDY[j]);
603 mapArgs["-ac_reward"] += "," + to_string(ASSETCHAINS_REWARD[j]);
604 mapArgs["-ac_decay"] += "," + to_string(ASSETCHAINS_DECAY[j]);
605 mapArgs["-ac_halving"] += "," + to_string(ASSETCHAINS_HALVING[j]);
606 mapArgs["-ac_end"] += "," + to_string(ASSETCHAINS_ENDSUBSIDY[j]);
611 PBAAS_STARTBLOCK = ConnectedChains.ThisChain().startBlock;
612 mapArgs["-startblock"] = to_string(PBAAS_STARTBLOCK);
613 PBAAS_ENDBLOCK = ConnectedChains.ThisChain().endBlock;
614 mapArgs["-endblock"] = to_string(PBAAS_ENDBLOCK);
624 // ensures that the chain definition is valid and that there are no other definitions of the same name
625 // that have been confirmed.
626 bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
628 // the chain definition output can be spent when the chain is at the end of its life and only then
633 // ensures that the chain definition is valid and that there are no other definitions of the same name
634 // that have been confirmed.
635 bool CheckChainDefinitionOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
637 // checked before a chain definition output script is accepted as a valid transaction
639 // basics - we need a chain definition transaction to kick off a PBaaS chain. it must have:
640 // 1) valid chain definition output with parameters in proper ranges and no duplicate name
641 // 2) notarization output with conformant values
642 // 3) finalization output
643 // 3) notarization funding
646 // get the source transaction
649 if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
651 LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
655 CPBaaSChainDefinition chainDef(thisTx, true);
656 CPBaaSNotarization notarization(thisTx, true);
657 CNotarizationFinalization finalization(thisTx, true);
659 if (!chainDef.IsValid() || !notarization.IsValid() || finalization.IsValid())
661 LogPrintf("transaction specified, %s, must have valid chain definition, notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
665 CPBaaSChainDefinition prior;
666 // this ensures that there is no other definition of the same name already on the blockchain
667 if (!GetChainDefinition(chainDef.name, prior))
669 LogPrintf("PBaaS chain with the name %s already exists\n", chainDef.name.c_str());
676 CAmount CCrossChainExport::CalculateExportFee() const
678 if (numInputs > MAX_EXPORT_INPUTS)
682 static const arith_uint256 satoshis(100000000);
684 int64_t ratio = 50000000 + ((25000000 / MAX_EXPORT_INPUTS) * numInputs);
686 return (((arith_uint256(totalFees) * arith_uint256(ratio))) / satoshis).GetLow64();
689 bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
692 LOCK(cs_mergemining);
694 //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
696 auto chainIt = mergeMinedChains.find(chainID);
697 if (chainIt != mergeMinedChains.end())
699 arith_uint256 target;
700 target.SetCompact(chainIt->second.block.nBits);
701 for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
703 // make sure we don't just match by target
704 if (removeRange.first->second->GetChainID() == chainID)
706 mergeMinedTargets.erase(removeRange.first);
710 mergeMinedChains.erase(chainID);
711 dirty = retval = true;
713 // if we get to 0, give the thread a kick to stop waiting for mining
714 //if (!mergeMinedChains.size())
716 // sem_submitthread.post();
722 // remove merge mined chains added and not updated since a specific time
723 uint32_t CConnectedChains::PruneOldChains(uint32_t pruneBefore)
725 vector<uint160> toRemove;
727 LOCK(cs_mergemining);
728 for (auto blkData : mergeMinedChains)
730 if (blkData.second.block.nTime < pruneBefore)
732 toRemove.push_back(blkData.first);
736 for (auto id : toRemove)
738 //printf("Pruning chainID: %s\n", id.GetHex().c_str());
739 RemoveMergedBlock(id);
743 // adds or updates merge mined blocks
744 // returns false if failed to add
745 bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
747 // determine if we should replace one or add to the merge mine vector
749 LOCK(cs_mergemining);
751 arith_uint256 target;
752 uint160 cID = blkData.GetChainID();
753 auto it = mergeMinedChains.find(cID);
754 if (it != mergeMinedChains.end())
756 RemoveMergedBlock(cID); // remove it if already there
758 target.SetCompact(blkData.block.nBits);
760 //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
762 mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
768 bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
771 LOCK(cs_mergemining);
772 auto chainIt = mergeMinedChains.find(chainID);
773 if (chainIt != mergeMinedChains.end())
775 rpcChainData = (CRPCChainData)chainIt->second;
782 // this returns a pointer to the data without copy and assumes the lock is held
783 CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
786 auto chainIt = mergeMinedChains.find(chainID);
787 if (chainIt != mergeMinedChains.end())
789 return &chainIt->second;
795 bool CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
797 //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
799 LOCK(cs_mergemining);
801 qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
803 sem_submitthread.post();
806 // get the latest block header and submit one block at a time, returning after there are no more
807 // matching blocks to be found
808 vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
810 std::set<uint160> inHeader;
811 bool submissionFound;
812 CPBaaSMergeMinedChainData chainData;
813 vector<pair<string, UniValue>> results;
816 arith_uint256 lastHash;
817 CPBaaSBlockHeader pbh;
821 submissionFound = false;
823 LOCK(cs_mergemining);
824 // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
825 // common, merge mined headers for notarization, drop out on any submission
826 for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
828 // add the PBaaS chain ids from this header to a set for search
829 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
831 inHeader.insert(pbh.chainID);
835 // now look through all targets that are equal to or above the hash of this header
836 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
838 chainID = chainIt->second->GetChainID();
839 if (inHeader.count(chainID))
841 // first, check that the winning header matches the block that is there
842 CPBaaSPreHeader preHeader(chainIt->second->block);
843 preHeader.SetBlockData(headerIt->second);
845 // check if the block header matches the block's specific data, only then can we create a submission from this block
846 if (headerIt->second.CheckNonCanonicalData(chainID))
848 // save block as is, remove the block from merged headers, replace header, and submit
849 chainData = *chainIt->second;
851 *(CBlockHeader *)&chainData.block = headerIt->second;
853 submissionFound = true;
855 //else // not an error condition. code is here for debugging
857 // printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
860 //else // not an error condition. code is here for debugging
862 // printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
866 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
869 // once it is going to be submitted, remove block from this chain until a new one is added again
870 RemoveMergedBlock(chainID);
875 qualifiedHeaders.erase(headerIt);
881 // submit one block and loop again. this approach allows multiple threads
882 // to collectively empty the submission queue, mitigating the impact of
883 // any one stalled daemon
884 UniValue submitParams(UniValue::VARR);
885 submitParams.push_back(EncodeHexBlk(chainData.block));
886 UniValue result, error;
889 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
890 result = find_value(result, "result");
891 error = find_value(result, "error");
895 result = UniValue(e.what());
897 results.push_back(make_pair(chainData.chainDefinition.name, result));
898 if (result.isStr() || !error.isNull())
900 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());
904 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
907 } while (submissionFound);
911 // add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
912 uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
914 vector<uint160> inHeader;
915 vector<UniValue> toCombine;
916 arith_uint256 blkHash = UintToArith256(bh.GetHash());
917 arith_uint256 target(0);
919 CPBaaSBlockHeader pbh;
922 LOCK(cs_mergemining);
924 CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
926 for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
928 if (bh.GetPBaaSHeader(pbh, i))
930 inHeader.push_back(pbh.chainID);
934 // loop through the existing PBaaS chain ids in the header
935 // remove any that are not either this Chain ID or in our local collection and then add all that are present
936 for (uint32_t i = 0; i < inHeader.size(); i++)
938 auto it = mergeMinedChains.find(inHeader[i]);
939 if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
941 bh.DeletePBaaSHeader(i);
945 for (auto chain : mergeMinedChains)
947 // get the native PBaaS header for each chain and put it into the
948 // header we are given
949 // it must have itself in as a PBaaS header
950 uint160 cid = chain.second.GetChainID();
951 if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
953 if (!bh.AddUpdatePBaaSHeader(pbh))
955 LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
961 t.SetCompact(chain.second.block.nBits);
970 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
977 return target.GetCompact();
980 bool CConnectedChains::IsVerusPBaaSAvailable()
982 return notaryChainVersion > "0.6";
985 extern string PBAAS_HOST, PBAAS_USERPASS;
986 extern int32_t PBAAS_PORT;
987 bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
989 if (chainInfoUni.isObject() && chainDefUni.isObject())
991 UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
994 LOCK(cs_mergemining);
995 notaryChainVersion = uni_get_str(uniVer);
996 notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
997 CPBaaSChainDefinition chainDef(chainDefUni);
998 notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
1001 return IsVerusPBaaSAvailable();
1004 bool CConnectedChains::CheckVerusPBaaSAvailable()
1006 if (IsVerusActive())
1008 notaryChainVersion = "";
1012 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
1013 // tolerate only 15 second timeout
1014 UniValue chainInfo, chainDef;
1017 UniValue params(UniValue::VARR);
1018 chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
1019 if (!chainInfo.isNull())
1021 params.push_back(VERUS_CHAINNAME);
1022 chainDef = find_value(RPCCallRoot("getchaindefinition", params), "result");
1024 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
1029 } catch (exception e)
1033 notaryChainVersion = "";
1037 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
1039 CCoinbaseCurrencyState currencyState;
1042 if (!IsVerusActive() &&
1043 CConstVerusSolutionVector::activationHeight.ActiveVersion(height) >= CActivationHeight::SOLUTION_VERUSV3 &&
1045 height <= chainActive.Height() &&
1046 chainActive[height] &&
1047 ReadBlockFromDisk(block, chainActive[height], false))
1049 currencyState = CCoinbaseCurrencyState(block.vtx[0]);
1051 return currencyState;
1054 void CConnectedChains::SubmissionThread()
1058 arith_uint256 lastHash;
1060 // wait for something to check on, then submit blocks that should be submitted
1063 if (IsVerusActive())
1065 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
1066 //printf("SubmissionThread: pruning\n");
1067 ConnectedChains.PruneOldChains(GetAdjustedTime() - 300);
1068 bool submit = false;
1070 LOCK(cs_mergemining);
1071 if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
1073 qualifiedHeaders.clear();
1075 submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
1077 //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
1081 //printf("SubmissionThread: calling submit qualified blocks\n");
1082 SubmitQualifiedBlocks();
1086 //printf("SubmissionThread: waiting on sem\n");
1087 sem_submitthread.wait();
1092 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
1093 CheckVerusPBaaSAvailable();
1095 // check to see if we have recently earned a block with an earned notarization that qualifies for
1096 // submitting an accepted notarization
1097 if (earnedNotarizationHeight)
1100 int32_t txIndex = -1, height;
1102 LOCK(cs_mergemining);
1103 if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
1105 blk = earnedNotarizationBlock;
1106 earnedNotarizationBlock = CBlock();
1107 txIndex = earnedNotarizationIndex;
1108 height = earnedNotarizationHeight;
1109 earnedNotarizationHeight = 0;
1115 //printf("SubmissionThread: testing notarization\n");
1117 uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
1121 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1122 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1128 boost::this_thread::interruption_point();
1131 catch (const boost::thread_interrupted&)
1133 LogPrintf("Verus merge mining thread terminated\n");
1137 void CConnectedChains::SubmissionThreadStub()
1139 ConnectedChains.SubmissionThread();
1142 void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
1144 // called after winning a block that contains an earned notarization
1145 // the earned notarization and its height are queued for processing by the submission thread
1146 // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
1148 LOCK(cs_mergemining);
1150 // we only care about the last
1151 earnedNotarizationHeight = height;
1152 earnedNotarizationBlock = blk;
1153 earnedNotarizationIndex = txIndex;
1156 bool IsChainDefinitionInput(const CScript &scriptSig)
1159 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_PBAASDEFINITION;
1162 bool IsServiceRewardInput(const CScript &scriptSig)
1164 // this is an output check, and is incorrect. need to change to input
1166 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_SERVICEREWARD;