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;
326 printf("ERROR: invalid object type (%u), likely corrupt pointer %p\n", pobj->objectType, pobj);
327 printf("generate code that won't be optimized away %s\n", CCurrencyValueMap(std::vector<uint160>({ASSETCHAINS_CHAINID}), std::vector<CAmount>({200000000})).ToUniValue().write(1,2).c_str());
328 printf("This is here to generate enough code for a good break point system chain name: %s\n", ConnectedChains.ThisChain().name.c_str());
337 std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
339 std::vector<unsigned char> vch;
340 std::vector<CBaseChainObject *> vRet;
341 if (opRetScript.IsOpReturn() && GetOpReturnData(opRetScript, vch) && vch.size() > 0)
343 CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION);
350 if (opRetType == OPRETTYPE_OBJECTARR)
352 CBaseChainObject *pobj;
353 while (!s.empty() && (pobj = RehydrateChainObject(s)))
355 vRet.push_back(pobj);
359 printf("failed to load all objects in opret");
360 DeleteOpRetObjects(vRet);
365 catch(const std::exception& e)
367 std::cerr << e.what() << '\n';
368 DeleteOpRetObjects(vRet);
375 CServiceReward::CServiceReward(const CTransaction &tx, bool validate)
377 nVersion = PBAAS_VERSION_INVALID;
378 for (auto out : tx.vout)
381 if (IsPayToCryptoCondition(out.scriptPubKey, p))
383 // always take the first for now
384 if (p.evalCode == EVAL_SERVICEREWARD)
386 FromVector(p.vData[0], *this);
398 CCrossChainExport::CCrossChainExport(const CTransaction &tx, int32_t *pCCXOutputNum)
400 int32_t _ccxOutputNum = 0;
401 int32_t &ccxOutputNum = pCCXOutputNum ? *pCCXOutputNum : _ccxOutputNum;
403 for (int i = 0; i < tx.vout.size(); i++)
406 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) &&
408 p.evalCode == EVAL_CROSSCHAIN_EXPORT)
410 FromVector(p.vData[0], *this);
417 CCurrencyDefinition::CCurrencyDefinition(const CScript &scriptPubKey)
419 nVersion = PBAAS_VERSION_INVALID;
421 if (scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
423 if (p.evalCode == EVAL_CURRENCY_DEFINITION)
425 FromVector(p.vData[0], *this);
430 std::vector<CCurrencyDefinition> CCurrencyDefinition::GetCurrencyDefinitions(const CTransaction &tx)
432 std::vector<CCurrencyDefinition> retVal;
433 for (auto &out : tx.vout)
435 CCurrencyDefinition oneCur = CCurrencyDefinition(out.scriptPubKey);
436 if (oneCur.IsValid())
438 retVal.push_back(oneCur);
444 #define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
445 extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
446 extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3], ASSETCHAINS_ERAOPTIONS[3];
447 extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
448 extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
449 extern std::string VERUS_CHAINNAME;
450 extern uint160 VERUS_CHAINID;
452 // ensures that the chain definition is valid and that there are no other definitions of the same name
453 // that have been confirmed.
454 bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
456 // the chain definition output can be spent when the chain is at the end of its life and only then
461 // ensures that the chain definition is valid and that there are no other definitions of the same name
462 // that have been confirmed.
463 bool CheckChainDefinitionOutputs(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
465 // checked before a chain definition output script is accepted as a valid transaction
467 // basics - we need a chain definition transaction to kick off a PBaaS chain. it must have:
468 // 1) valid chain definition output with parameters in proper ranges and no duplicate name
469 // 2) notarization output with conformant values
470 // 3) finalization output
471 // 3) notarization funding
474 // get the source transaction
477 if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
479 LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
483 std::vector<CCurrencyDefinition> chainDefs = CCurrencyDefinition::GetCurrencyDefinitions(thisTx);
484 CPBaaSNotarization notarization(thisTx);
485 CNotarizationFinalization finalization(thisTx);
486 bool isVerusActive = IsVerusActive();
488 if (!notarization.IsValid() || !finalization.IsValid())
490 LogPrintf("transaction specified, %s, must have valid notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
494 std::set<uint160> allCurrencyIDs;
495 for (auto &curPair : ConnectedChains.ReserveCurrencies())
497 allCurrencyIDs.insert(curPair.first);
499 allCurrencyIDs.insert(ConnectedChains.ThisChain().GetID());
502 allCurrencyIDs.insert(ConnectedChains.notaryChain.GetID());
505 bool isCoinbase = thisTx.IsCoinBase();
506 bool isVerified = false;
507 CIdentity activatedID(thisTx);
509 // currency definitions can be valid as follows:
510 // 1. original definition in a transaction that simultaneously sets the active currency bit on the identity of the same
512 // 2. outputs of a coinbase transaction in block 1 that defines the parent currency, new currency, and any reserve currencies
513 // 3. currency import from the defining chain of the currency, which has not been implemented as of this comment
514 if (activatedID.IsValid() &&
515 activatedID.HasActiveCurrency() &&
516 chainDefs.size() == 1 &&
517 activatedID.parent == ASSETCHAINS_CHAINID &&
518 activatedID.GetID() == chainDefs[0].GetID())
522 else if (isCoinbase && chainDefs.size() >= 1 && !isVerusActive)
525 CScript expect = CScript() << height1;
526 opcodetype opcode = (opcodetype)*expect.begin();
528 if (opcode >= OP_1 && opcode <= OP_16)
530 isVerified = (thisTx.vin[0].scriptSig.size() >= 1 && CScript::DecodeOP_N(opcode) == height1) ||
531 (thisTx.vin[0].scriptSig.size() >= 2 && thisTx.vin[0].scriptSig[0] == OP_PUSHDATA1 && (int)thisTx.vin[0].scriptSig[1] == height1);
535 isVerified = thisTx.vin[0].scriptSig.size() >= expect.size() && std::equal(expect.begin(), expect.end(), thisTx.vin[0].scriptSig.begin());
538 for (auto &chainDef : chainDefs)
540 uint160 chainID = chainDef.GetID();
541 if (!chainDef.IsValid())
543 LogPrintf("transaction specified, %s, must not contain invalid chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
546 if (!allCurrencyIDs.count(chainID))
548 LogPrintf("transaction specified, %s, must not contain invalid chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
551 allCurrencyIDs.erase(chainID);
553 if (allCurrencyIDs.size())
555 LogPrintf("transaction specified, %s, does not contain all required chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
563 CCurrencyValueMap CCrossChainExport::CalculateExportFee(const CCurrencyValueMap &fees, int numIn)
565 CCurrencyValueMap retVal;
567 if (numIn > MAX_EXPORT_INPUTS)
571 static const arith_uint256 satoshis(100000000);
573 arith_uint256 ratio(50000000 + ((25000000 / MAX_EXPORT_INPUTS) * (numIn - 1)));
575 for (auto &feePair : fees.valueMap)
577 retVal.valueMap[feePair.first] = (((arith_uint256(feePair.second) * ratio)) / satoshis).GetLow64();
582 CCurrencyValueMap CCrossChainExport::CalculateExportFee() const
584 return CalculateExportFee(totalFees, numInputs);
587 CCurrencyValueMap CCrossChainExport::CalculateImportFee() const
589 CCurrencyValueMap retVal;
591 for (auto &feePair : CalculateExportFee().valueMap)
593 CAmount feeAmount = feePair.second;
594 auto it = totalFees.valueMap.find(feePair.first);
595 retVal.valueMap[feePair.first] = (it != totalFees.valueMap.end() ? it->second : 0) - feeAmount;
600 bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
603 LOCK(cs_mergemining);
605 //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
607 auto chainIt = mergeMinedChains.find(chainID);
608 if (chainIt != mergeMinedChains.end())
610 arith_uint256 target;
611 target.SetCompact(chainIt->second.block.nBits);
612 for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
614 // make sure we don't just match by target
615 if (removeRange.first->second->GetID() == chainID)
617 mergeMinedTargets.erase(removeRange.first);
621 mergeMinedChains.erase(chainID);
622 dirty = retval = true;
624 // if we get to 0, give the thread a kick to stop waiting for mining
625 //if (!mergeMinedChains.size())
627 // sem_submitthread.post();
633 // remove merge mined chains added and not updated since a specific time
634 void CConnectedChains::PruneOldChains(uint32_t pruneBefore)
636 vector<uint160> toRemove;
638 LOCK(cs_mergemining);
639 for (auto blkData : mergeMinedChains)
641 if (blkData.second.block.nTime < pruneBefore)
643 toRemove.push_back(blkData.first);
647 for (auto id : toRemove)
649 //printf("Pruning chainID: %s\n", id.GetHex().c_str());
650 RemoveMergedBlock(id);
654 // adds or updates merge mined blocks
655 // returns false if failed to add
656 bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
658 // determine if we should replace one or add to the merge mine vector
660 LOCK(cs_mergemining);
662 arith_uint256 target;
663 uint160 cID = blkData.GetID();
664 auto it = mergeMinedChains.find(cID);
665 if (it != mergeMinedChains.end())
667 RemoveMergedBlock(cID); // remove it if already there
669 target.SetCompact(blkData.block.nBits);
671 //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
673 mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
679 bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
682 LOCK(cs_mergemining);
683 auto chainIt = mergeMinedChains.find(chainID);
684 if (chainIt != mergeMinedChains.end())
686 rpcChainData = (CRPCChainData)chainIt->second;
693 // this returns a pointer to the data without copy and assumes the lock is held
694 CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
697 auto chainIt = mergeMinedChains.find(chainID);
698 if (chainIt != mergeMinedChains.end())
700 return &chainIt->second;
706 void CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
708 //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
710 LOCK(cs_mergemining);
712 qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
714 sem_submitthread.post();
717 void CConnectedChains::CheckImports()
719 sem_submitthread.post();
722 // get the latest block header and submit one block at a time, returning after there are no more
723 // matching blocks to be found
724 vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
726 std::set<uint160> inHeader;
727 bool submissionFound;
728 CPBaaSMergeMinedChainData chainData;
729 vector<pair<string, UniValue>> results;
732 arith_uint256 lastHash;
733 CPBaaSBlockHeader pbh;
737 submissionFound = false;
739 LOCK(cs_mergemining);
740 // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
741 // common, merge mined headers for notarization, drop out on any submission
742 for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
744 // add the PBaaS chain ids from this header to a set for search
745 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
747 inHeader.insert(pbh.chainID);
751 // now look through all targets that are equal to or above the hash of this header
752 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
754 chainID = chainIt->second->GetID();
755 if (inHeader.count(chainID))
757 // first, check that the winning header matches the block that is there
758 CPBaaSPreHeader preHeader(chainIt->second->block);
759 preHeader.SetBlockData(headerIt->second);
761 // check if the block header matches the block's specific data, only then can we create a submission from this block
762 if (headerIt->second.CheckNonCanonicalData(chainID))
764 // save block as is, remove the block from merged headers, replace header, and submit
765 chainData = *chainIt->second;
767 *(CBlockHeader *)&chainData.block = headerIt->second;
769 submissionFound = true;
771 //else // not an error condition. code is here for debugging
773 // printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
776 //else // not an error condition. code is here for debugging
778 // printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
782 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
785 // once it is going to be submitted, remove block from this chain until a new one is added again
786 RemoveMergedBlock(chainID);
791 qualifiedHeaders.erase(headerIt);
797 // submit one block and loop again. this approach allows multiple threads
798 // to collectively empty the submission queue, mitigating the impact of
799 // any one stalled daemon
800 UniValue submitParams(UniValue::VARR);
801 submitParams.push_back(EncodeHexBlk(chainData.block));
802 UniValue result, error;
805 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
806 result = find_value(result, "result");
807 error = find_value(result, "error");
811 result = UniValue(e.what());
813 results.push_back(make_pair(chainData.chainDefinition.name, result));
814 if (result.isStr() || !error.isNull())
816 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());
820 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
823 } while (submissionFound);
827 // add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
828 uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
830 vector<uint160> inHeader;
831 vector<UniValue> toCombine;
832 arith_uint256 blkHash = UintToArith256(bh.GetHash());
833 arith_uint256 target(0);
835 CPBaaSBlockHeader pbh;
838 LOCK(cs_mergemining);
840 CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
842 for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
844 if (bh.GetPBaaSHeader(pbh, i))
846 inHeader.push_back(pbh.chainID);
850 // loop through the existing PBaaS chain ids in the header
851 // remove any that are not either this Chain ID or in our local collection and then add all that are present
852 for (uint32_t i = 0; i < inHeader.size(); i++)
854 auto it = mergeMinedChains.find(inHeader[i]);
855 if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
857 bh.DeletePBaaSHeader(i);
861 for (auto chain : mergeMinedChains)
863 // get the native PBaaS header for each chain and put it into the
864 // header we are given
865 // it must have itself in as a PBaaS header
866 uint160 cid = chain.second.GetID();
867 if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
869 if (!bh.AddUpdatePBaaSHeader(pbh))
871 LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
877 t.SetCompact(chain.second.block.nBits);
886 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
892 return target.GetCompact();
895 bool CConnectedChains::IsVerusPBaaSAvailable()
897 return notaryChainVersion >= "0.6.4";
900 extern string PBAAS_HOST, PBAAS_USERPASS;
901 extern int32_t PBAAS_PORT;
902 bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
904 if (chainInfoUni.isObject() && chainDefUni.isObject())
906 UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
909 LOCK(cs_mergemining);
910 notaryChainVersion = uni_get_str(uniVer);
911 notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
912 CCurrencyDefinition chainDef(chainDefUni);
913 notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
916 return IsVerusPBaaSAvailable();
919 uint32_t CConnectedChains::NotaryChainHeight()
921 LOCK(cs_mergemining);
922 return notaryChainHeight;
925 bool CConnectedChains::CheckVerusPBaaSAvailable()
929 notaryChainVersion = "";
933 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
934 // tolerate only 15 second timeout
935 UniValue chainInfo, chainDef;
938 UniValue params(UniValue::VARR);
939 chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
940 if (!chainInfo.isNull())
942 params.push_back(VERUS_CHAINNAME);
943 chainDef = find_value(RPCCallRoot("getcurrency", params), "result");
945 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
947 // if we have not past block 1 yet, store the best known update of our current state
948 if ((!chainActive.LastTip() || !chainActive.LastTip()->GetHeight()))
950 bool success = false;
952 params.push_back(EncodeDestination(CIdentityID(thisChain.GetID())));
953 chainDef = find_value(RPCCallRoot("getcurrency", params), "result");
954 if (!chainDef.isNull())
956 CCurrencyDefinition currencyDef(chainDef);
957 if (currencyDef.IsValid())
959 thisChain = currencyDef;
960 if (NotaryChainHeight() >= thisChain.startBlock)
962 readyToStart = true; // this only gates mining of block one, to be sure we have the latest definition
972 } catch (exception e)
974 LogPrintf("%s: Error communicating with %s chain\n", __func__, VERUS_CHAINNAME);
977 notaryChainVersion = "";
981 int CConnectedChains::GetThisChainPort() const
985 for (auto node : defaultPeerNodes)
987 SplitHostPort(node.networkAddress, port, host);
996 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
998 CCoinbaseCurrencyState currencyState;
1001 bool isVerusActive = IsVerusActive();
1002 if (!isVerusActive &&
1003 CConstVerusSolutionVector::activationHeight.ActiveVersion(height) >= CActivationHeight::ACTIVATE_PBAAS &&
1005 height <= chainActive.Height() &&
1006 chainActive[height] &&
1007 ReadBlockFromDisk(block, chainActive[height], Params().GetConsensus()) &&
1008 (currencyState = CCoinbaseCurrencyState(block.vtx[0])).IsValid())
1010 return currencyState;
1014 return GetInitialCurrencyState(thisChain);
1018 bool CConnectedChains::SetLatestMiningOutputs(const std::vector<pair<int, CScript>> &minerOutputs, CTxDestination &firstDestinationOut)
1020 LOCK(cs_mergemining);
1022 if (!minerOutputs.size() || !ExtractDestination(minerOutputs[0].second, firstDestinationOut))
1026 latestMiningOutputs = minerOutputs;
1027 latestDestination = firstDestinationOut;
1031 CCurrencyDefinition CConnectedChains::GetCachedCurrency(const uint160 ¤cyID)
1034 CCurrencyDefinition currencyDef;
1035 auto it = currencyDefCache.find(currencyID);
1036 if ((it != currencyDefCache.end() && !(currencyDef = it->second).IsValid()) ||
1037 (it == currencyDefCache.end() && !GetCurrencyDefinition(currencyID, currencyDef)))
1039 printf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(currencyID)).c_str());
1040 LogPrintf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(currencyID)).c_str());
1043 if (it == currencyDefCache.end())
1045 currencyDefCache[currencyID] = currencyDef;
1047 return currencyDefCache[currencyID];
1050 void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
1052 // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
1059 std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
1063 uint160 thisChainID = ConnectedChains.ThisChain().GetID();
1065 // get all available transfer outputs to aggregate into export transactions
1066 if (GetUnspentChainTransfers(transferOutputs))
1068 if (!transferOutputs.size())
1073 std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
1074 uint160 bookEnd({uint160(ParseHex("ffffffffffffffffffffffffffffffffffffffff"))});
1075 uint160 lastChain = bookEnd;
1076 transferOutputs.insert(std::make_pair(bookEnd, std::make_pair(CInputDescriptor(), CReserveTransfer())));
1077 CCurrencyDefinition lastChainDef;
1079 for (auto &output : transferOutputs)
1081 CCurrencyDefinition sourceDef, destDef, systemDef;
1083 if (output.first != bookEnd)
1085 auto it = currencyDefCache.find(output.second.second.currencyID);
1086 if ((it != currencyDefCache.end() && !(sourceDef = it->second).IsValid()) ||
1087 (it == currencyDefCache.end() && !GetCurrencyDefinition(output.second.second.currencyID, sourceDef)))
1089 printf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.currencyID)).c_str());
1090 LogPrintf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.currencyID)).c_str());
1093 if (it == currencyDefCache.end())
1095 currencyDefCache[output.second.second.currencyID] = sourceDef;
1098 it = currencyDefCache.find(output.second.second.destCurrencyID);
1099 if ((it != currencyDefCache.end() && !(destDef = it->second).IsValid()) ||
1100 (it == currencyDefCache.end() && !GetCurrencyDefinition(output.second.second.destCurrencyID, destDef)))
1102 printf("%s: definition for destination currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.destCurrencyID)).c_str());
1103 LogPrintf("%s: definition for destination currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.destCurrencyID)).c_str());
1106 if (it == currencyDefCache.end())
1108 currencyDefCache[output.second.second.destCurrencyID] = destDef;
1111 it = currencyDefCache.find(destDef.systemID);
1112 if ((it != currencyDefCache.end() && !(systemDef = it->second).IsValid()) ||
1113 (it == currencyDefCache.end() && !GetCurrencyDefinition(destDef.systemID, systemDef)))
1115 printf("%s: definition for export system ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
1116 LogPrintf("%s: definition for export system ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
1119 if (it == currencyDefCache.end())
1121 currencyDefCache[destDef.systemID] = systemDef;
1124 // if destination is a token on the current chain, consider it its own system
1125 if (destDef.systemID == thisChainID)
1127 systemDef = destDef;
1131 // get chain target and see if it is the same
1132 if (lastChain == bookEnd || output.first == lastChain)
1134 txInputs.push_back(output.second);
1138 // when we get here, we have a consecutive number of transfer outputs to consume in txInputs
1139 // we need an unspent export output to export, or use the last one of it is an export to the same
1141 std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1142 lastChainDef = currencyDefCache[lastChain];
1144 if (GetUnspentChainExports(lastChain, exportOutputs) && exportOutputs.size())
1146 auto lastExport = *exportOutputs.begin();
1147 bool oneFullSize = txInputs.size() >= CCrossChainExport::MIN_INPUTS;
1149 if (((nHeight - lastExport.second.first) >= CCrossChainExport::MIN_BLOCKS) || oneFullSize)
1151 boost::optional<CTransaction> oneExport;
1153 // make one or more transactions that spends the last export and all possible cross chain transfers
1154 while (txInputs.size())
1156 TransactionBuilder tb(Params().GetConsensus(), nHeight);
1158 int inputsLeft = txInputs.size();
1159 int numInputs = inputsLeft > CCrossChainExport::MAX_EXPORT_INPUTS ? CCrossChainExport::MAX_EXPORT_INPUTS : inputsLeft;
1161 if (numInputs > CCrossChainExport::MAX_EXPORT_INPUTS)
1163 numInputs = CCrossChainExport::MAX_EXPORT_INPUTS;
1165 inputsLeft = txInputs.size() - numInputs;
1167 // if we have already made one and don't have enough to make another
1168 // without going under the input minimum, wait until next time for the others
1169 if (numInputs > 0 && numInputs < CCrossChainExport::MIN_INPUTS && oneFullSize)
1174 // each time through, we make one export transaction with the remainder or a subset of the
1175 // reserve transfer inputs. inputs can be:
1176 // 1. transfers of reserve for fractional reserve chains
1177 // 2. pre-conversions for pre-launch participation in the premine
1178 // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
1180 // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
1181 // or reserve token inputs.
1183 // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
1184 // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
1185 // as part of the import process
1187 // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain.
1188 // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the
1189 // remaining Verus reserve coin will be burned on the PBaaS chain as spending it is allowed, once notarized, on the
1192 CCurrencyValueMap totalTxFees;
1193 CCurrencyValueMap totalAmounts;
1194 CAmount exportOutVal = 0;
1195 std::vector<CBaseChainObject *> chainObjects;
1197 // first, we must add the export output from the current export thread to this chain
1198 if (oneExport.is_initialized())
1200 // spend the last export transaction output
1201 CTransaction &tx = oneExport.get();
1204 for (j = 0; j < tx.vout.size(); j++)
1206 if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) && p.evalCode == EVAL_CROSSCHAIN_EXPORT)
1212 // had to be found and valid if we made the tx
1213 assert(j < tx.vout.size() && p.IsValid());
1215 tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
1216 exportOutVal = tx.vout[j].nValue;
1220 // spend the recentExportIt output
1221 tb.AddTransparentInput(lastExport.second.second.txIn.prevout, lastExport.second.second.scriptPubKey, lastExport.second.second.nValue);
1222 exportOutVal = lastExport.second.second.nValue;
1226 std::vector<int> toRemove;
1228 for (int j = 0; j < numInputs; j++)
1230 tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
1231 CCurrencyValueMap newTransferInput = txInputs[j].first.scriptPubKey.ReserveOutValue();
1232 newTransferInput.valueMap[ASSETCHAINS_CHAINID] = txInputs[j].first.nValue;
1234 // TODO: make fee currency calculation more flexible on conversion
1235 // rules should be pay fee in native currency of destination system
1236 // if source is same currency
1237 CCurrencyValueMap newTransferOutput;
1238 if (txInputs[j].second.flags | txInputs[j].second.MINT_CURRENCY)
1240 newTransferOutput.valueMap[txInputs[j].second.currencyID] = txInputs[j].second.nFees;
1244 newTransferOutput.valueMap[txInputs[j].second.currencyID] = txInputs[j].second.nValue + txInputs[j].second.nFees;
1247 //printf("input:\n%s\n", newTransferInput.ToUniValue().write().c_str());
1248 //printf("output:\n%s\n", newTransferOutput.ToUniValue().write().c_str());
1250 if ((newTransferInput - newTransferOutput).HasNegative())
1252 // 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
1253 // 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
1254 // we should formalize this into a chain contribution or amount adjustment.
1255 printf("%s: transaction %s claims incorrect value:\n%s\nactual:\n%s\n", __func__,
1256 txInputs[j].first.txIn.prevout.hash.GetHex().c_str(),
1257 newTransferInput.ToUniValue().write().c_str(),
1258 newTransferOutput.ToUniValue().write().c_str());
1259 LogPrintf("%s: transaction %s claims incorrect value:\n%s\nactual:\n%s\n", __func__,
1260 txInputs[j].first.txIn.prevout.hash.GetHex().c_str(),
1261 newTransferInput.ToUniValue().write().c_str(),
1262 newTransferOutput.ToUniValue().write().c_str());
1263 toRemove.push_back(j);
1267 totalTxFees += txInputs[j].second.CalculateFee(txInputs[j].second.flags, txInputs[j].second.nValue);
1268 totalAmounts += newTransferInput;
1269 CReserveTransfer rt(txInputs[j].second);
1270 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(rt), rt));
1274 // remove in reverse order so one removal does not affect the position of the next
1275 for (int j = toRemove.size() - 1; j >= 0; j--)
1277 txInputs.erase(txInputs.begin() + toRemove[j]);
1281 // this logic may cause us to create a tx that will get rejected, but we will never wait too long
1282 if (!numInputs || (oneFullSize && (nHeight - lastExport.second.first) < CCrossChainExport::MIN_BLOCKS && numInputs < CCrossChainExport::MIN_INPUTS))
1287 //printf("%s: total export amounts:\n%s\n", __func__, totalAmounts.ToUniValue().write().c_str());
1289 CCrossChainExport ccx(lastChain, numInputs, totalAmounts, totalTxFees);
1291 // make extra outputs for fees in each currency
1292 for (auto &outPair : ccx.CalculateExportFee().CanonicalMap().valueMap)
1294 CReserveTransfer feeOut(CReserveTransfer::VALID + CReserveTransfer::FEE_OUTPUT,
1295 outPair.first, outPair.second, 0, outPair.first, DestinationToTransferDestination(feeOutput));
1296 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
1299 // do a preliminary check
1300 CReserveTransactionDescriptor rtxd;
1301 std::vector<CTxOut> vOutputs;
1303 if (!rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain().GetID(), lastChainDef, chainObjects, vOutputs))
1305 DeleteOpRetObjects(chainObjects);
1307 printf("%s: failed to create valid exports\n", __func__);
1308 LogPrintf("%s: failed to create valid exports\n", __func__);
1311 printf("%s: failed to export outputs:\n", __func__);
1312 for (auto oneout : vOutputs)
1315 ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1316 printf("%s\n", uniOut.write(true, 2).c_str());
1322 CCcontract_info *cp;
1325 // printf("%s: exported outputs:\n", __func__);
1327 for (auto &oneout : chainObjects)
1329 if (oneout->objectType == CHAINOBJ_RESERVETRANSFER)
1331 CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)(oneout))->object;
1332 printf("%s\n", rt.ToUniValue().write(true, 2).c_str());
1337 CScript opRet = StoreOpRetArray(chainObjects);
1338 DeleteOpRetObjects(chainObjects);
1340 // now send transferred currencies to a reserve deposit
1341 cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
1343 for (auto &oneCurrencyOut : ccx.totalAmounts.valueMap)
1345 CCurrencyDefinition oneDef = currencyDefCache[oneCurrencyOut.first];
1347 // if the destination is the not the source currency, and
1348 // the destination is not another blockchain that controls the source currency, store in reserve
1349 if (!(oneCurrencyOut.first == lastChain ||
1350 (lastChainDef.systemID != ASSETCHAINS_CHAINID && oneDef.systemID == lastChainDef.systemID)))
1352 CAmount nativeOut = oneDef.GetID() == ASSETCHAINS_CHAINID ? oneCurrencyOut.second : 0;
1354 // send the entire amount to a reserve deposit output of the specific chain
1355 // we receive our fee on the other chain, when it comes back, or if a token,
1356 // when it gets imported back to the chain
1357 std::vector<CTxDestination> indexDests({CKeyID(lastChainDef.GetConditionID(EVAL_RESERVE_DEPOSIT))});
1358 std::vector<CTxDestination> dests({CPubKey(ParseHex(CC.CChexstr))});
1360 CTokenOutput ro = CTokenOutput(oneCurrencyOut.first, nativeOut ? 0 : oneCurrencyOut.second);
1361 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_DEPOSIT, dests, 1, &ro), &indexDests),
1366 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
1368 // send native amount of zero to a cross chain export output of the specific chain
1369 std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(lastChainDef.GetConditionID(EVAL_CROSSCHAIN_EXPORT))});
1370 if (lastChain != lastChainDef.systemID)
1372 indexDests.push_back(CKeyID(CCrossChainRPCData::GetConditionID(lastChainDef.systemID, EVAL_CROSSCHAIN_EXPORT)));
1374 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
1376 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx), &indexDests),
1383 UniValue uni(UniValue::VOBJ);
1384 TxToUniv(tb.mtx, uint256(), uni);
1385 printf("%s: about to send reserve deposits with tx:\n%s\n", __func__, uni.write(1,2).c_str());
1388 TransactionBuilderResult buildResult(tb.Build());
1390 if (!buildResult.IsError() && buildResult.IsTx())
1392 // replace the last one only if we have a valid new one
1393 CTransaction tx = buildResult.GetTxOrThrow();
1395 LOCK2(cs_main, mempool.cs);
1396 static int lastHeight = 0;
1397 // remove conflicts, so that we get in
1398 std::list<CTransaction> removed;
1399 mempool.removeConflicts(tx, removed);
1401 // add to mem pool, prioritize according to the fee we will get, and relay
1402 //printf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1403 //LogPrintf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1404 if (myAddtomempool(tx))
1406 uint256 hash = tx.GetHash();
1407 CAmount nativeExportFees = ccx.totalFees.valueMap[ASSETCHAINS_CHAINID];
1408 mempool.PrioritiseTransaction(hash, hash.GetHex(), (double)(nativeExportFees << 1), nativeExportFees);
1412 UniValue uni(UniValue::VOBJ);
1413 TxToUniv(tx, uint256(), uni);
1414 printf("%s: created invalid transaction:\n%s\n", __func__, uni.write(1,2).c_str());
1419 // we can't do any more useful work for this chain if we failed here
1420 printf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1421 LogPrintf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1426 // erase the inputs we've attempted to spend
1427 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1432 lastChain = output.first;
1439 void CConnectedChains::SignAndCommitImportTransactions(const CTransaction &lastImportTx, const std::vector<CTransaction> &transactions)
1441 int nHeight = chainActive.LastTip()->GetHeight();
1442 uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());
1443 LOCK2(cs_main, mempool.cs);
1445 uint256 lastHash, lastSignedHash;
1446 CCoinsViewCache view(pcoinsTip);
1448 // sign and commit the transactions
1449 for (auto &_tx : transactions)
1451 CMutableTransaction newTx(_tx);
1453 if (!lastHash.IsNull())
1455 //printf("last hash before signing: %s\n", lastHash.GetHex().c_str());
1456 for (auto &oneIn : newTx.vin)
1458 //printf("checking input with hash: %s\n", oneIn.prevout.hash.GetHex().c_str());
1459 if (oneIn.prevout.hash == lastHash)
1461 oneIn.prevout.hash = lastSignedHash;
1462 //printf("updated hash before signing: %s\n", lastSignedHash.GetHex().c_str());
1466 lastHash = _tx.GetHash();
1467 CTransaction tx = newTx;
1469 // sign the transaction and submit
1470 bool signSuccess = false;
1471 for (int i = 0; i < tx.vin.size(); i++)
1473 SignatureData sigdata;
1475 CScript outputScript;
1477 if (tx.vin[i].prevout.hash == lastImportTx.GetHash())
1479 value = lastImportTx.vout[tx.vin[i].prevout.n].nValue;
1480 outputScript = lastImportTx.vout[tx.vin[i].prevout.n].scriptPubKey;
1485 if (!view.GetCoins(tx.vin[i].prevout.hash, coins))
1487 fprintf(stderr,"%s: cannot get input coins from tx: %s, output: %d\n", __func__, tx.vin[i].prevout.hash.GetHex().c_str(), tx.vin[i].prevout.n);
1488 LogPrintf("%s: cannot get input coins from tx: %s, output: %d\n", __func__, tx.vin[i].prevout.hash.GetHex().c_str(), tx.vin[i].prevout.n);
1491 value = coins.vout[tx.vin[i].prevout.n].nValue;
1492 outputScript = coins.vout[tx.vin[i].prevout.n].scriptPubKey;
1495 signSuccess = ProduceSignature(TransactionSignatureCreator(nullptr, &tx, i, value, SIGHASH_ALL), outputScript, sigdata, consensusBranchId);
1499 fprintf(stderr,"%s: failure to sign transaction\n", __func__);
1500 LogPrintf("%s: failure to sign transaction\n", __func__);
1503 UpdateTransaction(newTx, i, sigdata);
1509 // push to local node and sync with wallets
1510 CValidationState state;
1511 bool fMissingInputs;
1512 CTransaction signedTx(newTx);
1515 //TxToJSON(tx, uint256(), jsonTX);
1516 //printf("signed transaction:\n%s\n", jsonTX.write(1, 2).c_str());
1518 if (!AcceptToMemoryPool(mempool, state, signedTx, false, &fMissingInputs)) {
1519 if (state.IsInvalid()) {
1520 fprintf(stderr,"%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
1521 LogPrintf("%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
1523 if (fMissingInputs) {
1524 fprintf(stderr,"%s: missing inputs\n", __func__);
1525 LogPrintf("%s: missing inputs\n", __func__);
1529 fprintf(stderr,"%s: rejected by memory pool for\n", __func__);
1530 LogPrintf("%s: rejected by memory pool for\n", __func__);
1537 UpdateCoins(signedTx, view, nHeight);
1538 lastSignedHash = signedTx.GetHash();
1548 bool RefundFailedLaunch(uint160 currencyID, CTransaction &lastImportTx, std::vector<CTransaction> &newRefunds, std::string &errorReason);
1550 CCurrencyValueMap CalculatePreconversions(const CCurrencyDefinition &chainDef, int32_t definitionHeight, CCurrencyValueMap &fees)
1552 // if we are getting information on the current chain, we assume that preconverted amounts have been
1553 // pre-calculated. otherwise, we will calculate them.
1554 CCurrencyValueMap retVal;
1555 if (chainDef.GetID() != ConnectedChains.ThisChain().GetID())
1557 std::multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> transferInputs;
1558 CCurrencyValueMap preconvertedAmounts;
1560 if (GetChainTransfers(transferInputs, chainDef.GetID(), definitionHeight, chainDef.startBlock, CReserveTransfer::PRECONVERT | CReserveTransfer::VALID))
1562 auto curMap = chainDef.GetCurrenciesMap();
1563 for (auto &transfer : transferInputs)
1565 if (!(transfer.second.second.flags & CReserveTransfer::PREALLOCATE) && curMap.count(transfer.second.second.currencyID))
1567 CAmount conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1568 preconvertedAmounts.valueMap[transfer.second.second.currencyID] += (transfer.second.second.nValue - conversionFee);
1569 fees.valueMap[transfer.second.second.currencyID] += transfer.second.second.nFees + conversionFee;
1572 retVal = preconvertedAmounts;
1573 if (!chainDef.IsToken() && !(chainDef.ChainOptions() & chainDef.OPTION_FEESASRESERVE))
1581 retVal = CCurrencyValueMap(chainDef.currencies, chainDef.preconverted);
1586 // process token related, local imports and exports
1587 void CConnectedChains::ProcessLocalImports()
1589 // first determine all export threads on the current chain that are valid to import
1590 std::multimap<uint160, std::pair<int, CInputDescriptor>> exportOutputs;
1591 std::multimap<uint160, CTransaction> importThreads;
1592 uint160 thisChainID = thisChain.GetID();
1594 LOCK2(cs_main, mempool.cs);
1595 uint32_t nHeight = chainActive.Height();
1597 if (GetUnspentChainExports(thisChainID, exportOutputs) && exportOutputs.size())
1599 for (auto &exportThread : exportOutputs)
1601 CCurrencyDefinition exportDef;
1602 int32_t defHeight = 0;
1603 if (!GetCurrencyDefinition(exportThread.first, exportDef, &defHeight))
1605 printf("%s: definition for export currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(exportThread.first)).c_str());
1606 LogPrintf("%s: definition for export currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(exportThread.first)).c_str());
1609 currencyDefCache[exportThread.first] = exportDef;
1611 //printf("processing exports for currency: %s\n", exportDef.name.c_str());
1613 // if it is not launched, check and initiate refund if we control this currency
1614 bool refunding = false;
1616 // if chain hasn't started, and it is controlled by us, or if it has no exports, skip
1617 if ((exportDef.startBlock > nHeight && exportDef.systemID == thisChainID) ||
1618 defHeight == exportThread.second.first)
1622 // we have started and this currency is controlled by our chain, check or create its launch notarization
1623 else if (exportThread.first != thisChainID && exportDef.systemID == thisChainID)
1625 CChainNotarizationData cnd;
1626 std::vector<std::pair<CTransaction, uint256>> notarizations;
1627 if (!GetNotarizationData(exportThread.first, EVAL_ACCEPTEDNOTARIZATION, cnd, ¬arizations))
1629 LogPrintf("%s: error getting notarization data for token %s\n", __func__, exportDef.name.c_str());
1633 CCurrencyValueMap minPreMap;
1634 if (exportDef.minPreconvert.size())
1636 minPreMap = CCurrencyValueMap(exportDef.currencies, exportDef.minPreconvert).CanonicalMap();
1639 if (cnd.vtx.back().second.prevHeight == 0)
1641 // check if the chain is qualified for a refund
1642 CCurrencyValueMap minPreMap, preConvertedMap, fees;
1643 preConvertedMap = CalculatePreconversions(exportDef, defHeight, fees).CanonicalMap();
1644 exportDef.preconverted = preConvertedMap.AsCurrencyVector(exportDef.currencies);
1645 CCoinbaseCurrencyState initialCur = GetInitialCurrencyState(exportDef);
1647 if (exportDef.minPreconvert.size() && exportDef.minPreconvert.size() == exportDef.currencies.size())
1649 minPreMap = CCurrencyValueMap(exportDef.currencies, exportDef.minPreconvert).CanonicalMap();
1652 if (minPreMap.valueMap.size() && preConvertedMap < minPreMap)
1654 // we force the supply to zero
1655 // in any case where there was a minimum participation,
1656 // the result of the supply cannot be zero, enabling us to easily determine that this
1657 // represents a failed launch
1658 initialCur.supply = 0;
1661 // now, create the initial notarization and continue
1662 // we will begin refund or import after notarization is accepted and returned by GetNotarizationData
1663 CBlockIndex *pindex = chainActive[exportDef.startBlock];
1664 ChainMerkleMountainView mmv(chainActive.GetMMR(), exportDef.startBlock);
1665 CPBaaSNotarization pbn = CPBaaSNotarization(exportDef.notarizationProtocol,
1667 CTxDestination(CIdentityID(exportThread.first)),
1668 exportDef.startBlock,
1669 chainActive.GetMMV().GetRoot(),
1670 chainActive.GetMMRNode(exportDef.startBlock).hash,
1671 ArithToUint256(GetCompactPower(pindex->nNonce, pindex->nBits, pindex->nVersion)),
1673 notarizations[0].first.GetHash(),
1675 uint256(), 0, COpRetProof(), std::vector<CNodeData>());
1678 // setup to create the accepted notarization transaction
1679 CMutableTransaction mnewTx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight);
1681 // create notarization output
1683 CCcontract_info *cp;
1685 std::vector<CTxDestination> dests;
1686 std::vector<CTxDestination> indexDests;
1688 // make the accepted notarization output
1689 cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
1691 if (exportDef.notarizationProtocol == exportDef.NOTARIZATION_NOTARY_CHAINID)
1693 dests = std::vector<CTxDestination>({CIdentityID(exportThread.first)});
1695 else if (exportDef.notarizationProtocol == exportDef.NOTARIZATION_AUTO)
1697 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1703 indexDests = std::vector<CTxDestination>({CKeyID(exportDef.GetConditionID(EVAL_ACCEPTEDNOTARIZATION))});
1704 mnewTx.vout.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &pbn), &indexDests)));
1706 // make the finalization output
1707 cp = CCinit(&CC, EVAL_FINALIZENOTARIZATION);
1709 if (exportDef.notarizationProtocol == exportDef.NOTARIZATION_AUTO)
1711 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1718 // finish transaction by adding the prior input and finalization, sign, then put it in the mempool
1719 // all output for notarizing will be paid as mining fees, so there's no need to relay
1720 uint32_t confirmedOut, finalizeOut;
1721 if (!GetNotarizationAndFinalization(EVAL_ACCEPTEDNOTARIZATION, notarizations[0].first, pbn, &confirmedOut, &finalizeOut))
1723 printf("ERROR: could not find expected initial notarization for currency %s\n", exportDef.name.c_str());
1727 mnewTx.vin.push_back(CTxIn(COutPoint(notarizations[0].first.GetHash(), confirmedOut)));
1728 mnewTx.vin.push_back(CTxIn(COutPoint(notarizations[0].first.GetHash(), finalizeOut)));
1730 // we need to store the input that we confirmed if we spent finalization outputs
1731 CNotarizationFinalization nf(0);
1733 indexDests = std::vector<CTxDestination>({CKeyID(exportDef.GetConditionID(EVAL_FINALIZENOTARIZATION))});
1735 // update crypto condition with final notarization output data
1736 mnewTx.vout.push_back(CTxOut(PBAAS_MINNOTARIZATIONOUTPUT,
1737 MakeMofNCCScript(CConditionObj<CNotarizationFinalization>(EVAL_FINALIZENOTARIZATION, dests, 1, &nf), &indexDests)));
1739 CTransaction ntx(mnewTx);
1741 uint32_t consensusBranchId = CurrentEpochBranchId(chainActive.LastTip()->GetHeight(), Params().GetConsensus());
1743 // sign the transaction and submit
1744 for (int i = 0; i < ntx.vin.size(); i++)
1747 SignatureData sigdata;
1749 const CScript *pScriptPubKey;
1751 pScriptPubKey = &(notarizations[0].first.vout[ntx.vin[i].prevout.n].scriptPubKey);
1752 value = notarizations[0].first.vout[ntx.vin[i].prevout.n].nValue;
1754 extern CWallet *pwalletMain;
1755 signSuccess = ProduceSignature(TransactionSignatureCreator(pwalletMain, &ntx, i, value, SIGHASH_ALL), *pScriptPubKey, sigdata, consensusBranchId);
1759 printf("%s: failure to sign notarization\n", __func__);
1762 UpdateTransaction(mnewTx, i, sigdata);
1766 // add to mempool and submit transaction
1769 CValidationState state;
1770 if (!myAddtomempool(ntx, &state))
1772 if (state.GetRejectCode() != REJECT_DUPLICATE)
1774 //UniValue uniTx(UniValue::VOBJ);
1775 //TxToUniv(ntx, uint256(), uniTx);
1776 //printf("ERROR: could not create launch notarization for currency %s: %s\ntx: %s\n", exportDef.name.c_str(), state.GetRejectReason().c_str(), uniTx.write(1,2).c_str());
1777 //printf("ERROR: could not create launch notarization for currency %s: %s\n", exportDef.name.c_str(), state.GetRejectReason().c_str());
1778 LogPrintf("ERROR: could not create launch notarization for currency %s: %s\n", exportDef.name.c_str(), state.GetRejectReason().c_str());
1781 // we will import after the launch notarization is mined/staked
1784 else if (cnd.vtx.size() > 1)
1786 if (minPreMap.valueMap.size() && !cnd.vtx[cnd.forks[cnd.bestChain].back()].second.currencyState.supply)
1793 LogPrintf("%s: error getting notarization data for token %s\n", __func__, exportDef.name.c_str());
1798 // get the first import for each of the remaining export threads
1799 CTransaction lastImportTx;
1801 // we need to find the last unspent import transaction
1802 std::vector<CAddressUnspentDbEntry> unspentOutputs;
1808 std::vector<CTransaction> newRefunds;
1809 std::string failReason;
1810 if (!RefundFailedLaunch(exportThread.first, lastImportTx, newRefunds, failReason))
1812 LogPrintf("%s: ERROR refunding token %s: %s\n", __func__, exportDef.name.c_str(), failReason.c_str());
1814 ConnectedChains.SignAndCommitImportTransactions(lastImportTx, newRefunds);
1817 else if (GetAddressUnspent(CKeyID(CCrossChainRPCData::GetConditionID(exportThread.first, EVAL_CROSSCHAIN_IMPORT)), 1, unspentOutputs))
1819 for (auto txidx : unspentOutputs)
1823 CCrossChainImport oneCCI;
1824 if (myGetTransaction(txidx.first.txhash, lastImportTx, blkHash) &&
1825 (oneCCI = CCrossChainImport(lastImportTx)).IsValid() &&
1826 oneCCI.systemID == exportDef.GetID())
1828 importThreads.insert(std::make_pair(exportThread.first, lastImportTx));
1835 CMutableTransaction txTemplate = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight);
1836 for (auto &oneIT : importThreads)
1838 std::vector<CTransaction> importTxes;
1839 int32_t importOutNum = 0;
1840 CCrossChainImport oneImportInput(oneIT.second, &importOutNum);
1841 if (oneImportInput.IsValid())
1843 std::vector<CAddressUnspentDbEntry> reserveDeposits;
1844 GetAddressUnspent(currencyDefCache[oneIT.first].GetConditionID(EVAL_RESERVE_DEPOSIT), CScript::P2CC, reserveDeposits);
1845 CCurrencyValueMap tokenImportAvailable;
1846 CAmount nativeImportAvailable = 0;
1847 for (auto &oneOut : reserveDeposits)
1849 nativeImportAvailable += oneOut.second.satoshis;
1850 tokenImportAvailable += oneOut.second.script.ReserveOutValue();
1851 //printf("nativeImportAvailable:%ld, tokenImportAvailable:%s\n", nativeImportAvailable, tokenImportAvailable.ToUniValue().write().c_str());
1853 nativeImportAvailable += oneIT.second.vout[importOutNum].nValue;
1854 tokenImportAvailable += oneIT.second.vout[importOutNum].ReserveOutValue();
1855 //printf("nativeImportAvailable:%ld, tokenImportAvailable:%s\n", nativeImportAvailable, tokenImportAvailable.ToUniValue().write().c_str());
1856 if (CreateLatestImports(currencyDefCache[oneIT.first], oneIT.second, txTemplate, CTransaction(), tokenImportAvailable, nativeImportAvailable, importTxes))
1858 // fund the first import transaction with all reserveDeposits
1859 // change amounts are passed through on the import thread
1861 // TODO: manage when this becomes to large by splitting reserve deposits over the
1863 if (importTxes.size())
1865 CMutableTransaction oneImport = importTxes[0];
1867 CCrossChainImport cci(importTxes[0], &outNum);
1870 // add the reserve deposit inputs to the first transaction
1871 // the outputs should have been automatically propagated through
1872 // fixup inputs if necessary
1873 if (reserveDeposits.size())
1875 UniValue jsonTX(UniValue::VOBJ);
1876 std::vector<uint256> prevHashes;
1878 for (int i = 0; i < importTxes.size() - 1; i++)
1880 prevHashes.push_back(importTxes[i].GetHash());
1883 // TODO - get reserve deposits from exports, not all at once, as in refunds
1884 for (auto &oneOut : reserveDeposits)
1886 oneImport.vin.push_back(CTxIn(oneOut.first.txhash, oneOut.first.index));
1889 importTxes[0] = oneImport;
1891 for (int i = 0; i < importTxes.size() - 1; i++)
1893 oneImport = importTxes[i + 1];
1894 for (auto &oneIn : oneImport.vin)
1896 if (oneIn.prevout.hash == prevHashes[i])
1898 //printf("updating hash before signing to new value\nold: %s\nnew: %s\n", oneIn.prevout.hash.GetHex().c_str(), importTxes[i].GetHash().GetHex().c_str());
1899 oneIn.prevout.hash = importTxes[i].GetHash();
1902 importTxes[i + 1] = oneImport;
1906 SignAndCommitImportTransactions(oneIT.second, importTxes);
1914 void CConnectedChains::SubmissionThread()
1918 arith_uint256 lastHash;
1919 int64_t lastImportTime = 0;
1920 uint32_t lastHeight = 0;
1922 // wait for something to check on, then submit blocks that should be submitted
1925 boost::this_thread::interruption_point();
1927 if (IsVerusActive())
1929 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
1930 //printf("SubmissionThread: pruning\n");
1931 PruneOldChains(GetAdjustedTime() - 300);
1932 bool submit = false;
1934 LOCK(cs_mergemining);
1935 if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
1937 qualifiedHeaders.clear();
1939 submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
1941 //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
1945 //printf("SubmissionThread: calling submit qualified blocks\n");
1946 SubmitQualifiedBlocks();
1949 ProcessLocalImports();
1953 sem_submitthread.wait();
1958 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
1959 if (CheckVerusPBaaSAvailable())
1961 // check to see if we have recently earned a block with an earned notarization that qualifies for
1962 // submitting an accepted notarization
1963 if (earnedNotarizationHeight)
1966 int32_t txIndex = -1, height;
1968 LOCK(cs_mergemining);
1969 if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
1971 blk = earnedNotarizationBlock;
1972 earnedNotarizationBlock = CBlock();
1973 txIndex = earnedNotarizationIndex;
1974 height = earnedNotarizationHeight;
1975 earnedNotarizationHeight = 0;
1981 //printf("SubmissionThread: testing notarization\n");
1982 CTransaction lastConfirmed;
1983 uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
1987 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1988 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1993 // every "n" seconds, look for imports to include in our blocks from the Verus chain
1994 if ((GetAdjustedTime() - lastImportTime) >= 30 || lastHeight < (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight()))
1996 lastImportTime = GetAdjustedTime();
1997 lastHeight = (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight());
1999 // see if our notary has a confirmed notarization for us
2000 UniValue params(UniValue::VARR);
2003 params.push_back(thisChain.name);
2007 result = find_value(RPCCallRoot("getlastimportin", params), "result");
2008 } catch (exception e)
2010 result = NullUniValue;
2013 if (!result.isNull())
2015 auto txUniStr = find_value(result, "lastimporttransaction");
2016 auto txLastConfirmedStr = find_value(result, "lastconfirmednotarization");
2017 auto txTemplateStr = find_value(result, "importtxtemplate");
2018 CAmount nativeImportAvailable = uni_get_int64(find_value(result, "nativeimportavailable"));
2019 CCurrencyValueMap tokenImportAvailable(find_value(params[0], "tokenimportavailable"));
2021 CTransaction lastImportTx, lastConfirmedTx, templateTx;
2023 if (txUniStr.isStr() && txTemplateStr.isStr() &&
2024 DecodeHexTx(lastImportTx, txUniStr.get_str()) &&
2025 DecodeHexTx(lastConfirmedTx, txLastConfirmedStr.get_str()) &&
2026 DecodeHexTx(templateTx, txTemplateStr.get_str()))
2028 std::vector<CTransaction> importTxes;
2029 if (CreateLatestImports(notaryChain.chainDefinition, lastImportTx, templateTx, lastConfirmedTx, tokenImportAvailable, nativeImportAvailable, importTxes))
2031 for (auto importTx : importTxes)
2035 params.push_back(EncodeHexTx(importTx));
2039 txResult = find_value(RPCCallRoot("signrawtransaction", params), "result");
2040 if (txResult.isObject() && !(txResult = find_value(txResult, "hex")).isNull() && txResult.isStr() && txResult.get_str().size())
2043 params.push_back(txResult);
2044 txResult = find_value(RPCCallRoot("sendrawtransaction", params), "result");
2048 txResult = NullUniValue;
2051 } catch (exception e)
2053 txResult = NullUniValue;
2056 if (txResult.isStr())
2058 testId.SetHex(txResult.get_str());
2060 if (testId.IsNull())
2072 boost::this_thread::interruption_point();
2075 catch (const boost::thread_interrupted&)
2077 LogPrintf("Verus merge mining thread terminated\n");
2081 void CConnectedChains::SubmissionThreadStub()
2083 ConnectedChains.SubmissionThread();
2086 void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
2088 // called after winning a block that contains an earned notarization
2089 // the earned notarization and its height are queued for processing by the submission thread
2090 // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
2092 LOCK(cs_mergemining);
2094 // we only care about the last
2095 earnedNotarizationHeight = height;
2096 earnedNotarizationBlock = blk;
2097 earnedNotarizationIndex = txIndex;
2100 bool IsChainDefinitionInput(const CScript &scriptSig)
2103 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_CURRENCY_DEFINITION;