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
15 #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)
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);
393 CCrossChainExport::CCrossChainExport(const CTransaction &tx, int32_t *pCCXOutputNum)
395 int32_t _ccxOutputNum = 0;
396 int32_t &ccxOutputNum = pCCXOutputNum ? *pCCXOutputNum : _ccxOutputNum;
398 for (int i = 0; i < tx.vout.size(); i++)
401 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) &&
403 p.evalCode == EVAL_CROSSCHAIN_EXPORT)
405 FromVector(p.vData[0], *this);
412 CCurrencyDefinition::CCurrencyDefinition(const CScript &scriptPubKey)
414 nVersion = PBAAS_VERSION_INVALID;
416 if (scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
418 if (p.evalCode == EVAL_CURRENCY_DEFINITION)
420 FromVector(p.vData[0], *this);
425 std::vector<CCurrencyDefinition> CCurrencyDefinition::GetCurrencyDefinitions(const CTransaction &tx)
427 std::vector<CCurrencyDefinition> retVal;
428 for (auto &out : tx.vout)
430 CCurrencyDefinition oneCur = CCurrencyDefinition(out.scriptPubKey);
431 if (oneCur.IsValid())
433 retVal.push_back(oneCur);
439 #define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
440 extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
441 extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3], ASSETCHAINS_ERAOPTIONS[3];
442 extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
443 extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
444 extern std::string VERUS_CHAINNAME;
445 extern uint160 VERUS_CHAINID;
447 // ensures that the chain definition is valid and that there are no other definitions of the same name
448 // that have been confirmed.
449 bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
451 // the chain definition output can be spent when the chain is at the end of its life and only then
456 // ensures that the chain definition is valid and that there are no other definitions of the same name
457 // that have been confirmed.
458 bool CheckChainDefinitionOutputs(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
460 // checked before a chain definition output script is accepted as a valid transaction
462 // basics - we need a chain definition transaction to kick off a PBaaS chain. it must have:
463 // 1) valid chain definition output with parameters in proper ranges and no duplicate name
464 // 2) notarization output with conformant values
465 // 3) finalization output
466 // 3) notarization funding
469 // get the source transaction
472 if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
474 LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
478 std::vector<CCurrencyDefinition> chainDefs = CCurrencyDefinition::GetCurrencyDefinitions(thisTx);
479 CPBaaSNotarization notarization(thisTx);
480 CTransactionFinalization finalization(thisTx);
481 bool isVerusActive = IsVerusActive();
483 if (!notarization.IsValid() || !finalization.IsValid())
485 LogPrintf("transaction specified, %s, must have valid notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
489 std::set<uint160> allCurrencyIDs;
490 for (auto &curPair : ConnectedChains.ReserveCurrencies())
492 allCurrencyIDs.insert(curPair.first);
494 allCurrencyIDs.insert(ConnectedChains.ThisChain().GetID());
497 allCurrencyIDs.insert(ConnectedChains.notaryChain.GetID());
500 bool isCoinbase = thisTx.IsCoinBase();
501 bool isVerified = false;
502 CIdentity activatedID(thisTx);
504 // currency definitions can be valid as follows:
505 // 1. original definition in a transaction that simultaneously sets the active currency bit on the identity of the same
507 // 2. outputs of a coinbase transaction in block 1 that defines the parent currency, new currency, and any reserve currencies
508 // 3. currency import from the defining chain of the currency, which has not been implemented as of this comment
509 if (activatedID.IsValid() &&
510 activatedID.HasActiveCurrency() &&
511 chainDefs.size() == 1 &&
512 activatedID.parent == ASSETCHAINS_CHAINID &&
513 activatedID.GetID() == chainDefs[0].GetID())
517 else if (isCoinbase && chainDefs.size() >= 1 && !isVerusActive)
520 CScript expect = CScript() << height1;
521 opcodetype opcode = (opcodetype)*expect.begin();
523 if (opcode >= OP_1 && opcode <= OP_16)
525 isVerified = (thisTx.vin[0].scriptSig.size() >= 1 && CScript::DecodeOP_N(opcode) == height1) ||
526 (thisTx.vin[0].scriptSig.size() >= 2 && thisTx.vin[0].scriptSig[0] == OP_PUSHDATA1 && (int)thisTx.vin[0].scriptSig[1] == height1);
530 isVerified = thisTx.vin[0].scriptSig.size() >= expect.size() && std::equal(expect.begin(), expect.end(), thisTx.vin[0].scriptSig.begin());
533 for (auto &chainDef : chainDefs)
535 uint160 chainID = chainDef.GetID();
536 if (!chainDef.IsValid())
538 LogPrintf("transaction specified, %s, must not contain invalid chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
541 if (!allCurrencyIDs.count(chainID))
543 LogPrintf("transaction specified, %s, must not contain invalid chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
546 allCurrencyIDs.erase(chainID);
548 if (allCurrencyIDs.size())
550 LogPrintf("transaction specified, %s, does not contain all required chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
558 CCurrencyValueMap CCrossChainExport::CalculateExportFee(const CCurrencyValueMap &fees, int numIn)
560 CCurrencyValueMap retVal;
562 if (numIn > MAX_EXPORT_INPUTS)
566 static const arith_uint256 satoshis(100000000);
568 arith_uint256 ratio(50000000 + ((25000000 / MAX_EXPORT_INPUTS) * (numIn - 1)));
570 for (auto &feePair : fees.valueMap)
572 retVal.valueMap[feePair.first] = (((arith_uint256(feePair.second) * ratio)) / satoshis).GetLow64();
574 return retVal.CanonicalMap();
577 CCurrencyValueMap CCrossChainExport::CalculateExportFee() const
579 return CalculateExportFee(totalFees, numInputs);
582 CCurrencyValueMap CCrossChainExport::CalculateImportFee() const
584 CCurrencyValueMap retVal;
586 for (auto &feePair : CalculateExportFee().valueMap)
588 CAmount feeAmount = feePair.second;
589 auto it = totalFees.valueMap.find(feePair.first);
590 retVal.valueMap[feePair.first] = (it != totalFees.valueMap.end() ? it->second : 0) - feeAmount;
595 bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
598 LOCK(cs_mergemining);
600 //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
602 auto chainIt = mergeMinedChains.find(chainID);
603 if (chainIt != mergeMinedChains.end())
605 arith_uint256 target;
606 target.SetCompact(chainIt->second.block.nBits);
607 for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
609 // make sure we don't just match by target
610 if (removeRange.first->second->GetID() == chainID)
612 mergeMinedTargets.erase(removeRange.first);
616 mergeMinedChains.erase(chainID);
617 dirty = retval = true;
619 // if we get to 0, give the thread a kick to stop waiting for mining
620 //if (!mergeMinedChains.size())
622 // sem_submitthread.post();
628 // remove merge mined chains added and not updated since a specific time
629 void CConnectedChains::PruneOldChains(uint32_t pruneBefore)
631 vector<uint160> toRemove;
633 LOCK(cs_mergemining);
634 for (auto blkData : mergeMinedChains)
636 if (blkData.second.block.nTime < pruneBefore)
638 toRemove.push_back(blkData.first);
642 for (auto id : toRemove)
644 //printf("Pruning chainID: %s\n", id.GetHex().c_str());
645 RemoveMergedBlock(id);
649 // adds or updates merge mined blocks
650 // returns false if failed to add
651 bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
653 // determine if we should replace one or add to the merge mine vector
655 LOCK(cs_mergemining);
657 arith_uint256 target;
658 uint160 cID = blkData.GetID();
659 auto it = mergeMinedChains.find(cID);
660 if (it != mergeMinedChains.end())
662 RemoveMergedBlock(cID); // remove it if already there
664 target.SetCompact(blkData.block.nBits);
666 //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
668 mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
674 bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
677 LOCK(cs_mergemining);
678 auto chainIt = mergeMinedChains.find(chainID);
679 if (chainIt != mergeMinedChains.end())
681 rpcChainData = (CRPCChainData)chainIt->second;
688 // this returns a pointer to the data without copy and assumes the lock is held
689 CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
692 auto chainIt = mergeMinedChains.find(chainID);
693 if (chainIt != mergeMinedChains.end())
695 return &chainIt->second;
701 void CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
703 //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
705 LOCK(cs_mergemining);
707 qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
709 sem_submitthread.post();
712 void CConnectedChains::CheckImports()
714 sem_submitthread.post();
717 // get the latest block header and submit one block at a time, returning after there are no more
718 // matching blocks to be found
719 vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
721 std::set<uint160> inHeader;
722 bool submissionFound;
723 CPBaaSMergeMinedChainData chainData;
724 vector<pair<string, UniValue>> results;
727 arith_uint256 lastHash;
728 CPBaaSBlockHeader pbh;
732 submissionFound = false;
734 LOCK(cs_mergemining);
735 // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
736 // common, merge mined headers for notarization, drop out on any submission
737 for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
739 // add the PBaaS chain ids from this header to a set for search
740 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
742 inHeader.insert(pbh.chainID);
746 // now look through all targets that are equal to or above the hash of this header
747 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
749 chainID = chainIt->second->GetID();
750 if (inHeader.count(chainID))
752 // first, check that the winning header matches the block that is there
753 CPBaaSPreHeader preHeader(chainIt->second->block);
754 preHeader.SetBlockData(headerIt->second);
756 // check if the block header matches the block's specific data, only then can we create a submission from this block
757 if (headerIt->second.CheckNonCanonicalData(chainID))
759 // save block as is, remove the block from merged headers, replace header, and submit
760 chainData = *chainIt->second;
762 *(CBlockHeader *)&chainData.block = headerIt->second;
764 submissionFound = true;
766 //else // not an error condition. code is here for debugging
768 // printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
771 //else // not an error condition. code is here for debugging
773 // printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
777 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
780 // once it is going to be submitted, remove block from this chain until a new one is added again
781 RemoveMergedBlock(chainID);
786 qualifiedHeaders.erase(headerIt);
792 // submit one block and loop again. this approach allows multiple threads
793 // to collectively empty the submission queue, mitigating the impact of
794 // any one stalled daemon
795 UniValue submitParams(UniValue::VARR);
796 submitParams.push_back(EncodeHexBlk(chainData.block));
797 UniValue result, error;
800 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
801 result = find_value(result, "result");
802 error = find_value(result, "error");
806 result = UniValue(e.what());
808 results.push_back(make_pair(chainData.chainDefinition.name, result));
809 if (result.isStr() || !error.isNull())
811 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());
815 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
818 } while (submissionFound);
822 // add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
823 uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
825 vector<uint160> inHeader;
826 vector<UniValue> toCombine;
827 arith_uint256 blkHash = UintToArith256(bh.GetHash());
828 arith_uint256 target(0);
830 CPBaaSBlockHeader pbh;
833 LOCK(cs_mergemining);
835 CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
837 for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
839 if (bh.GetPBaaSHeader(pbh, i))
841 inHeader.push_back(pbh.chainID);
845 // loop through the existing PBaaS chain ids in the header
846 // remove any that are not either this Chain ID or in our local collection and then add all that are present
847 for (uint32_t i = 0; i < inHeader.size(); i++)
849 auto it = mergeMinedChains.find(inHeader[i]);
850 if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
852 bh.DeletePBaaSHeader(i);
856 for (auto chain : mergeMinedChains)
858 // get the native PBaaS header for each chain and put it into the
859 // header we are given
860 // it must have itself in as a PBaaS header
861 uint160 cid = chain.second.GetID();
862 if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
864 if (!bh.AddUpdatePBaaSHeader(pbh))
866 LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
872 t.SetCompact(chain.second.block.nBits);
881 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
887 return target.GetCompact();
890 bool CConnectedChains::IsVerusPBaaSAvailable()
892 return notaryChainVersion >= "0.8.0";
895 extern string PBAAS_HOST, PBAAS_USERPASS;
896 extern int32_t PBAAS_PORT;
897 bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
899 if (chainInfoUni.isObject() && chainDefUni.isObject())
901 UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
904 LOCK(cs_mergemining);
905 notaryChainVersion = uni_get_str(uniVer);
906 notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
907 CCurrencyDefinition chainDef(chainDefUni);
908 notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
911 return IsVerusPBaaSAvailable();
914 uint32_t CConnectedChains::NotaryChainHeight()
916 LOCK(cs_mergemining);
917 return notaryChainHeight;
920 bool CConnectedChains::CheckVerusPBaaSAvailable()
924 notaryChainVersion = "";
928 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
929 // tolerate only 15 second timeout
930 UniValue chainInfo, chainDef;
933 UniValue params(UniValue::VARR);
934 chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
935 if (!chainInfo.isNull())
937 params.push_back(VERUS_CHAINNAME);
938 chainDef = find_value(RPCCallRoot("getcurrency", params), "result");
940 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
942 // if we have not past block 1 yet, store the best known update of our current state
943 if ((!chainActive.LastTip() || !chainActive.LastTip()->GetHeight()))
945 bool success = false;
947 params.push_back(EncodeDestination(CIdentityID(thisChain.GetID())));
948 chainDef = find_value(RPCCallRoot("getcurrency", params), "result");
949 if (!chainDef.isNull())
951 CCurrencyDefinition currencyDef(chainDef);
952 if (currencyDef.IsValid())
954 thisChain = currencyDef;
955 if (NotaryChainHeight() >= thisChain.startBlock)
957 readyToStart = true; // this only gates mining of block one, to be sure we have the latest definition
967 } catch (exception e)
969 LogPrintf("%s: Error communicating with %s chain\n", __func__, VERUS_CHAINNAME);
972 notaryChainVersion = "";
976 int CConnectedChains::GetThisChainPort() const
980 for (auto node : defaultPeerNodes)
982 SplitHostPort(node.networkAddress, port, host);
991 CCoinbaseCurrencyState CConnectedChains::AddPrelaunchConversions(CCurrencyDefinition &curDef,
992 const CCoinbaseCurrencyState &_currencyState,
995 int32_t curDefHeight)
997 CCoinbaseCurrencyState currencyState = _currencyState;
998 bool firstUpdate = fromHeight <= curDefHeight;
1001 if (curDef.IsFractional())
1003 currencyState.supply = curDef.initialFractionalSupply;
1004 currencyState.reserves = std::vector<int64_t>(currencyState.reserves.size(), 0);
1005 currencyState.weights = curDef.weights;
1009 // supply is determined by purchases * current conversion rate
1010 currencyState.supply = currencyState.initialSupply;
1014 // get chain transfers that should apply before the start block
1015 // until there is a post-start block notarization, we always consider the
1016 // currency state to be up to just before the start block
1017 std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> unspentTransfers;
1018 std::map<uint160, int32_t> currencyIndexes = currencyState.GetReserveMap();
1019 if (GetChainTransfers(unspentTransfers, curDef.GetID(), fromHeight, height < curDef.startBlock ? height : curDef.startBlock - 1))
1021 currencyState.ClearForNextBlock();
1023 for (auto &transfer : unspentTransfers)
1025 if (transfer.second.second.flags & CReserveTransfer::PRECONVERT)
1027 CAmount conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1029 currencyState.reserveIn[currencyIndexes[transfer.second.second.currencyID]] += (transfer.second.second.nValue - conversionFee);
1030 curDef.preconverted[currencyIndexes[transfer.second.second.currencyID]] += (transfer.second.second.nValue - conversionFee);
1031 if (curDef.IsFractional())
1033 currencyState.reserves[currencyIndexes[transfer.second.second.currencyID]] += transfer.second.second.nValue - conversionFee;
1037 currencyState.supply += CCurrencyState::ReserveToNativeRaw(transfer.second.second.nValue - conversionFee, currencyState.PriceInReserve(currencyIndexes[transfer.second.second.currencyID]));
1040 if (transfer.second.second.currencyID == curDef.systemID)
1042 currencyState.nativeConversionFees += conversionFee;
1043 currencyState.nativeFees += conversionFee + transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
1046 currencyState.fees[currencyIndexes[transfer.second.second.currencyID]] +=
1047 conversionFee + transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
1048 currencyState.conversionFees[currencyIndexes[transfer.second.second.currencyID]] += conversionFee;
1053 if (curDef.conversions.size() != curDef.currencies.size())
1055 curDef.conversions = std::vector<int64_t>(curDef.currencies.size());
1057 for (int i = 0; i < curDef.conversions.size(); i++)
1059 currencyState.conversionPrice[i] = curDef.conversions[i] = currencyState.PriceInReserve(i, true);
1062 // set initial supply from actual conversions if this is first update
1063 if (firstUpdate && curDef.IsFractional())
1065 CAmount lowSupply = 0, highSupply = 0;
1066 for (auto &transfer : unspentTransfers)
1068 if (transfer.second.second.flags & CReserveTransfer::PRECONVERT)
1070 CAmount toConvert = transfer.second.second.nValue - CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1071 lowSupply += CCurrencyState::ReserveToNativeRaw(toConvert, currencyState.conversionPrice[currencyIndexes[transfer.second.second.currencyID]]);
1072 //highSupply += CCurrencyState::ReserveToNativeRaw(toConvert, currencyState.PriceInReserve(currencyIndexes[transfer.second.second.currencyID], true));
1075 if (lowSupply > currencyState.supply)
1077 LogPrintf("%s: incorrect reserve currency supply low: %lu, high: %lu, current supply: %lu\n", __func__, lowSupply, highSupply, currencyState.supply);
1078 printf("%s: incorrect reserve currency supply low: %lu, high: %lu, current supply: %lu\n", __func__, lowSupply, highSupply, currencyState.supply);
1081 // now, remove carveout percentage from each weight & reserve
1082 // for currency state
1083 int32_t preLaunchCarveOutTotal = 0;
1084 for (auto &carveout : curDef.preLaunchCarveOuts)
1086 preLaunchCarveOutTotal += carveout.second;
1089 static arith_uint256 bigSatoshi(SATOSHIDEN);
1090 for (auto &oneReserve : currencyState.reserves)
1092 oneReserve = ((arith_uint256(oneReserve) * arith_uint256(SATOSHIDEN - preLaunchCarveOutTotal)) / bigSatoshi).GetLow64();
1094 for (auto &oneWeight : currencyState.weights)
1096 oneWeight = ((arith_uint256(oneWeight) * arith_uint256(CCurrencyDefinition::CalculateRatioOfValue((SATOSHIDEN - preLaunchCarveOutTotal), SATOSHIDEN - curDef.preLaunchDiscount))) / bigSatoshi).GetLow64();
1100 currencyState.UpdateWithEmission(curDef.GetTotalPreallocation());
1102 return currencyState;
1105 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(CCurrencyDefinition &curDef, int32_t height, int32_t curDefHeight)
1107 uint160 chainID = curDef.GetID();
1108 CCoinbaseCurrencyState currencyState;
1109 std::vector<CAddressIndexDbEntry> notarizationIndex;
1111 if (chainID == ASSETCHAINS_CHAINID)
1114 if (IsVerusActive() ||
1115 CConstVerusSolutionVector::activationHeight.ActiveVersion(height) < CActivationHeight::ACTIVATE_PBAAS ||
1117 height > chainActive.Height() ||
1118 !chainActive[height] ||
1119 !ReadBlockFromDisk(block, chainActive[height], Params().GetConsensus()) ||
1120 !(currencyState = CCoinbaseCurrencyState(block.vtx[0])).IsValid())
1122 currencyState = GetInitialCurrencyState(thisChain);
1125 // if this is a token on this chain, it will be simply notarized
1126 else if (curDef.systemID == ASSETCHAINS_CHAINID)
1128 // get the last unspent notarization for this currency, which is valid by definition for a token
1129 CPBaaSNotarization notarization;
1130 if ((notarization.GetLastNotarization(chainID, EVAL_ACCEPTEDNOTARIZATION, curDefHeight, height) &&
1131 (currencyState = notarization.currencyState).IsValid()) ||
1132 (currencyState = GetInitialCurrencyState(curDef)).IsValid())
1134 if (!(notarization.IsValid() && notarization.notarizationHeight >= curDef.startBlock))
1137 currencyState.SetPrelaunch(true);
1138 currencyState = AddPrelaunchConversions(curDef,
1140 notarization.IsValid() ? notarization.notarizationHeight : curDefHeight,
1146 currencyState.SetPrelaunch(false);
1152 CChainNotarizationData cnd;
1153 uint32_t ecode = IsVerusActive() ?
1154 EVAL_ACCEPTEDNOTARIZATION :
1155 (chainID == notaryChain.GetID() ? EVAL_EARNEDNOTARIZATION : EVAL_ACCEPTEDNOTARIZATION);
1156 if (GetNotarizationData(chainID, ecode, cnd))
1158 int32_t transfersFrom = curDefHeight;
1159 if (cnd.lastConfirmed != -1)
1161 transfersFrom = cnd.vtx[cnd.lastConfirmed].second.notarizationHeight;
1163 int32_t transfersUntil = cnd.lastConfirmed == -1 ? curDef.startBlock - 1 :
1164 (cnd.vtx[cnd.lastConfirmed].second.notarizationHeight < curDef.startBlock ?
1165 (height < curDef.startBlock ? height : curDef.startBlock - 1) :
1166 cnd.vtx[cnd.lastConfirmed].second.notarizationHeight);
1167 if (transfersUntil < curDef.startBlock)
1169 // get chain transfers that should apply before the start block
1170 // until there is a post-start block notarization, we always consider the
1171 // currency state to be up to just before the start block
1172 std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> unspentTransfers;
1173 if (GetChainTransfers(unspentTransfers, chainID, transfersFrom, transfersUntil))
1175 // at this point, all pre-allocation, minted, and pre-converted currency are included
1176 // in the currency state before final notarization
1177 std::map<uint160, int32_t> currencyIndexes = currencyState.GetReserveMap();
1178 if (curDef.IsFractional())
1180 currencyState.supply = curDef.initialFractionalSupply;
1184 // supply is determined by purchases * current conversion rate
1185 currencyState.supply = currencyState.initialSupply;
1188 for (auto &transfer : unspentTransfers)
1190 if (transfer.second.second.flags & CReserveTransfer::PRECONVERT)
1192 CAmount conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1194 currencyState.reserveIn[currencyIndexes[transfer.second.second.currencyID]] += transfer.second.second.nValue;
1195 curDef.preconverted[currencyIndexes[transfer.second.second.currencyID]] += transfer.second.second.nValue;
1196 if (curDef.IsFractional())
1198 currencyState.reserves[currencyIndexes[transfer.second.second.currencyID]] += transfer.second.second.nValue - conversionFee;
1202 currencyState.supply += CCurrencyState::ReserveToNativeRaw(transfer.second.second.nValue - conversionFee, currencyState.PriceInReserve(currencyIndexes[transfer.second.second.currencyID]));
1205 if (transfer.second.second.currencyID == curDef.systemID)
1207 currencyState.nativeConversionFees += conversionFee;
1208 currencyState.nativeFees += conversionFee + transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
1212 currencyState.fees[currencyIndexes[transfer.second.second.currencyID]] +=
1213 conversionFee + transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
1214 currencyState.conversionFees[currencyIndexes[transfer.second.second.currencyID]] += conversionFee;
1217 else if (transfer.second.second.flags & CReserveTransfer::PREALLOCATE)
1219 currencyState.emitted += transfer.second.second.nValue;
1222 currencyState.supply += currencyState.emitted;
1223 if (curDef.conversions.size() != curDef.currencies.size())
1225 curDef.conversions = std::vector<int64_t>(curDef.currencies.size());
1227 for (int i = 0; i < curDef.conversions.size(); i++)
1229 currencyState.conversionPrice[i] = curDef.conversions[i] = currencyState.PriceInReserve(i);
1235 std::pair<uint256, CPBaaSNotarization> notPair = cnd.lastConfirmed != -1 ? cnd.vtx[cnd.lastConfirmed] : cnd.vtx[cnd.forks[cnd.bestChain][0]];
1236 currencyState = notPair.second.currencyState;
1240 return currencyState;
1243 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(const uint160 ¤cyID, int32_t height)
1245 int32_t curDefHeight;
1246 CCurrencyDefinition curDef;
1247 if (GetCurrencyDefinition(currencyID, curDef, &curDefHeight, true))
1249 return GetCurrencyState(curDef, height, curDefHeight);
1253 LogPrintf("%s: currency %s:%s not found\n", __func__, currencyID.GetHex().c_str(), EncodeDestination(CIdentityID(currencyID)).c_str());
1254 printf("%s: currency %s:%s not found\n", __func__, currencyID.GetHex().c_str(), EncodeDestination(CIdentityID(currencyID)).c_str());
1256 return CCoinbaseCurrencyState();
1259 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
1261 return GetCurrencyState(thisChain.GetID(), height);
1264 bool CConnectedChains::SetLatestMiningOutputs(const std::vector<pair<int, CScript>> &minerOutputs, CTxDestination &firstDestinationOut)
1266 LOCK(cs_mergemining);
1268 if (!minerOutputs.size() || !ExtractDestination(minerOutputs[0].second, firstDestinationOut))
1272 latestMiningOutputs = minerOutputs;
1273 latestDestination = firstDestinationOut;
1277 CCurrencyDefinition CConnectedChains::GetCachedCurrency(const uint160 ¤cyID)
1279 CCurrencyDefinition currencyDef;
1281 auto it = currencyDefCache.find(currencyID);
1282 if ((it != currencyDefCache.end() && !(currencyDef = it->second).IsValid()) ||
1283 (it == currencyDefCache.end() && !GetCurrencyDefinition(currencyID, currencyDef, &defHeight, true)))
1285 printf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(currencyID)).c_str());
1286 LogPrintf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(currencyID)).c_str());
1289 if (it == currencyDefCache.end())
1291 currencyDefCache[currencyID] = currencyDef;
1293 return currencyDefCache[currencyID];
1296 CCurrencyDefinition CConnectedChains::UpdateCachedCurrency(const uint160 ¤cyID, uint32_t height)
1298 // due to the main lock being taken on the thread that waits for transaction checks,
1299 // low level functions like this must be called either from a thread that holds LOCK(cs_main),
1300 // or script validation, where it is held either by this thread or one waiting for it.
1301 // in the long run, the daemon synchonrization model should be improved
1302 CCurrencyDefinition currencyDef = GetCachedCurrency(currencyID);
1303 CCoinbaseCurrencyState curState = GetCurrencyState(currencyDef, height);
1304 currencyDefCache[currencyID] = currencyDef;
1308 void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
1310 // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
1317 std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
1321 uint160 thisChainID = ConnectedChains.ThisChain().GetID();
1323 // get all available transfer outputs to aggregate into export transactions
1324 if (GetUnspentChainTransfers(transferOutputs))
1326 if (!transferOutputs.size())
1331 std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
1332 uint160 bookEnd({uint160(ParseHex("ffffffffffffffffffffffffffffffffffffffff"))});
1333 uint160 lastChain = bookEnd;
1334 transferOutputs.insert(std::make_pair(bookEnd, std::make_pair(CInputDescriptor(), CReserveTransfer())));
1335 CCurrencyDefinition lastChainDef;
1339 CCoinsViewCache view(&dummy);
1343 CCoinsViewMemPool viewMemPool(pcoinsTip, mempool);
1344 view.SetBackend(viewMemPool);
1346 for (auto &output : transferOutputs)
1348 CCurrencyDefinition sourceDef, destDef, systemDef;
1350 if (output.first != bookEnd)
1352 if (!output.second.second.IsValid())
1354 printf("%s: invalid reserve transfer in index for currency %s\n", __func__, EncodeDestination(CIdentityID(output.second.second.currencyID)).c_str());
1355 LogPrintf("%s: invalid reserve transfer in index for currency %s\n", __func__, EncodeDestination(CIdentityID(output.second.second.currencyID)).c_str());
1359 sourceDef = GetCachedCurrency(output.second.second.currencyID);
1360 destDef = GetCachedCurrency(output.second.second.destCurrencyID);
1361 systemDef = GetCachedCurrency(destDef.systemID);
1363 if (!sourceDef.IsValid())
1365 printf("%s: cannot find source currency %s\n", __func__, EncodeDestination(CIdentityID(output.second.second.currencyID)).c_str());
1366 LogPrintf("%s: cannot find source currency %s\n", __func__, EncodeDestination(CIdentityID(output.second.second.currencyID)).c_str());
1369 if (!destDef.IsValid())
1371 printf("%s: cannot find destination currency %s\n", __func__, EncodeDestination(CIdentityID(output.second.second.destCurrencyID)).c_str());
1372 LogPrintf("%s: cannot find destination currency %s\n", __func__, EncodeDestination(CIdentityID(output.second.second.destCurrencyID)).c_str());
1375 if (!systemDef.IsValid())
1377 printf("%s: cannot find destination system definition %s\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
1378 LogPrintf("%s: cannot find destination system definition %s\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
1382 // if destination is a token on the current chain, consider it its own system
1383 if (destDef.systemID == thisChainID)
1385 systemDef = destDef;
1389 // get chain target and see if it is the same
1390 if (lastChain == bookEnd || output.first == lastChain)
1392 txInputs.push_back(output.second);
1396 // when we get here, we have a consecutive number of transfer outputs to consume in txInputs
1397 // we need an unspent export output to export, or use the last one of it is an export to the same
1399 std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1400 lastChainDef = UpdateCachedCurrency(lastChain, nHeight);
1402 if (GetUnspentChainExports(lastChain, exportOutputs) && exportOutputs.size())
1404 std::pair<uint160, std::pair<int, CInputDescriptor>> lastExport = *exportOutputs.begin();
1406 bool oneFullSize = txInputs.size() >= CCrossChainExport::MIN_INPUTS;
1408 if (((nHeight - lastExport.second.first) >= CCrossChainExport::MIN_BLOCKS) || oneFullSize)
1410 boost::optional<CTransaction> oneExport;
1412 // make one or more transactions that spends the last export and all possible cross chain transfers
1413 while (txInputs.size())
1415 TransactionBuilder tb(Params().GetConsensus(), nHeight);
1417 int inputsLeft = txInputs.size();
1418 int numInputs = inputsLeft > CCrossChainExport::MAX_EXPORT_INPUTS ? CCrossChainExport::MAX_EXPORT_INPUTS : inputsLeft;
1420 if (numInputs > CCrossChainExport::MAX_EXPORT_INPUTS)
1422 numInputs = CCrossChainExport::MAX_EXPORT_INPUTS;
1424 inputsLeft = txInputs.size() - numInputs;
1426 // if we have already made one and don't have enough to make another
1427 // without going under the input minimum, wait until next time for the others
1428 if (numInputs > 0 && numInputs < CCrossChainExport::MIN_INPUTS && oneFullSize)
1433 // each time through, we make one export transaction with the remainder or a subset of the
1434 // reserve transfer inputs. inputs can be:
1435 // 1. transfers of reserve for fractional reserve chains
1436 // 2. pre-conversions for pre-launch participation in the premine
1437 // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
1439 // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
1440 // or reserve token inputs.
1442 // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
1443 // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
1444 // as part of the import process
1446 // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain.
1447 // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the
1448 // remaining Verus reserve coin will be burned on the PBaaS chain as spending it is allowed, once notarized, on the
1451 CCurrencyValueMap totalTxFees;
1452 CCurrencyValueMap totalAmounts;
1453 CAmount exportOutVal = 0;
1454 std::vector<CBaseChainObject *> chainObjects;
1456 // first, we must add the export output from the current export thread to this chain
1457 if (oneExport.is_initialized())
1459 // spend the last export transaction output
1460 CTransaction &tx = oneExport.get();
1463 for (j = 0; j < tx.vout.size(); j++)
1465 if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) &&
1467 p.evalCode == EVAL_CROSSCHAIN_EXPORT)
1473 // had to be found and valid if we made the tx
1474 assert(j < tx.vout.size() && p.IsValid());
1476 tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
1477 exportOutVal = tx.vout[j].nValue;
1478 lastExport.second.first = nHeight;
1479 lastExport.second.second = CInputDescriptor(tx.vout[j].scriptPubKey, tx.vout[j].nValue, CTxIn(tx.GetHash(), j));
1483 // spend the recentExportIt output
1484 tb.AddTransparentInput(lastExport.second.second.txIn.prevout, lastExport.second.second.scriptPubKey, lastExport.second.second.nValue);
1485 exportOutVal = lastExport.second.second.nValue;
1487 CTransaction lastExportTx;
1488 // we must find the export, or it doesn't exist
1489 if (!myGetTransaction(lastExport.second.second.txIn.prevout.hash, lastExportTx, blkHash))
1493 oneExport = lastExportTx;
1496 // combine any reserve deposits from the last export
1497 // into the reserve deposit outputs for this export and spend them
1498 // TODO: allow reserve deposits to hold multiple reserve token types
1499 // to make this more efficient for multi-reserves
1500 CCurrencyValueMap currentReserveDeposits;
1501 if (!view.GetCoins(lastExport.second.second.txIn.prevout.hash, coins))
1505 CTransaction &lastExportTx = oneExport.get();
1507 for (int i = 0; i < coins.vout.size(); i++)
1509 // import on same chain spends reserve deposits as well, so only spend them if they are not
1511 auto &oneOut = lastExportTx.vout[i];
1514 if (::IsPayToCryptoCondition(oneOut.scriptPubKey, p) &&
1516 p.evalCode == EVAL_RESERVE_DEPOSIT &&
1517 coins.IsAvailable(i))
1519 // only reserve deposits for the export currency/system will be present on an export transaction
1520 CCurrencyValueMap reserveOut = oneOut.scriptPubKey.ReserveOutValue().CanonicalMap();
1521 currentReserveDeposits += reserveOut;
1522 if (oneOut.nValue || reserveOut.valueMap.size())
1524 tb.AddTransparentInput(COutPoint(lastExport.second.second.txIn.prevout.hash, i),
1525 oneOut.scriptPubKey, oneOut.nValue);
1526 currentReserveDeposits.valueMap[ASSETCHAINS_CHAINID] += oneOut.nValue;
1532 std::vector<int> toRemove;
1534 for (int j = 0; j < numInputs; j++)
1536 tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
1537 CCurrencyValueMap newTransferInput = txInputs[j].first.scriptPubKey.ReserveOutValue();
1538 newTransferInput.valueMap[ASSETCHAINS_CHAINID] = txInputs[j].first.nValue;
1540 // TODO: make fee currency calculation more flexible on conversion
1541 // rules should be pay fee in native currency of destination system
1542 // if source is same currency
1543 CCurrencyValueMap newTransferOutput;
1544 bool isMint = (txInputs[j].second.flags & (CReserveTransfer::PREALLOCATE | CReserveTransfer::MINT_CURRENCY));
1548 newTransferOutput.valueMap[txInputs[j].second.currencyID] = txInputs[j].second.nFees;
1552 newTransferOutput.valueMap[txInputs[j].second.currencyID] = txInputs[j].second.nValue + txInputs[j].second.nFees;
1555 //printf("input:\n%s\n", newTransferInput.ToUniValue().write().c_str());
1556 //printf("output:\n%s\n", newTransferOutput.ToUniValue().write().c_str());
1558 if ((newTransferInput - newTransferOutput).HasNegative())
1560 // 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
1561 // 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
1562 // we should formalize this into a chain contribution or amount adjustment.
1563 printf("%s: transaction %s claims incorrect value:\n%s\nactual:\n%s\n", __func__,
1564 txInputs[j].first.txIn.prevout.hash.GetHex().c_str(),
1565 newTransferInput.ToUniValue().write().c_str(),
1566 newTransferOutput.ToUniValue().write().c_str());
1567 LogPrintf("%s: transaction %s claims incorrect value:\n%s\nactual:\n%s\n", __func__,
1568 txInputs[j].first.txIn.prevout.hash.GetHex().c_str(),
1569 newTransferInput.ToUniValue().write().c_str(),
1570 newTransferOutput.ToUniValue().write().c_str());
1571 toRemove.push_back(j);
1575 CAmount valueOut = isMint ? 0 : txInputs[j].second.nValue;
1577 totalTxFees += txInputs[j].second.CalculateFee(txInputs[j].second.flags, valueOut);
1578 totalAmounts += newTransferInput;
1579 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(txInputs[j].second), txInputs[j].second));
1583 // remove in reverse order so one removal does not affect the position of the next
1584 for (int j = toRemove.size() - 1; j >= 0; j--)
1586 txInputs.erase(txInputs.begin() + toRemove[j]);
1590 // this logic may cause us to create a tx that will get rejected, but we will never wait too long
1591 if (!numInputs || (oneFullSize && (nHeight - lastExport.second.first) < CCrossChainExport::MIN_BLOCKS && numInputs < CCrossChainExport::MIN_INPUTS))
1596 //printf("%s: total export amounts:\n%s\n", __func__, totalAmounts.ToUniValue().write().c_str());
1597 CCrossChainExport ccx(lastChain, numInputs, totalAmounts.CanonicalMap(), totalTxFees.CanonicalMap());
1599 // make extra outputs for fees in each currency
1600 for (auto &outPair : ccx.CalculateExportFee().CanonicalMap().valueMap)
1602 CReserveTransfer feeOut(CReserveTransfer::VALID + CReserveTransfer::FEE_OUTPUT,
1603 outPair.first, outPair.second, 0, outPair.first, DestinationToTransferDestination(feeOutput));
1604 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
1607 // do a preliminary check
1608 CReserveTransactionDescriptor rtxd;
1609 std::vector<CTxOut> vOutputs;
1610 CChainNotarizationData cnd;
1611 CCoinbaseCurrencyState currencyState;
1612 if (!GetNotarizationData(lastChain,
1613 !lastChainDef.IsToken() && lastChainDef.systemID == lastChain ? EVAL_EARNEDNOTARIZATION : EVAL_ACCEPTEDNOTARIZATION,
1616 printf("%s: failed to create valid exports\n", __func__);
1617 LogPrintf("%s: failed to create valid exports\n", __func__);
1621 currencyState = cnd.vtx[cnd.lastConfirmed].second.currencyState;
1623 if (!currencyState.IsValid() ||
1624 !rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain().GetID(), lastChainDef, currencyState, chainObjects, vOutputs))
1626 vOutputs = std::vector<CTxOut>();
1627 rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain().GetID(), lastChainDef, currencyState, chainObjects, vOutputs);
1628 DeleteOpRetObjects(chainObjects);
1630 printf("%s: failed to create valid exports\n", __func__);
1631 LogPrintf("%s: failed to create valid exports\n", __func__);
1634 printf("%s: failed to export outputs:\n", __func__);
1635 for (auto oneout : vOutputs)
1638 ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1639 printf("%s\n", uniOut.write(true, 2).c_str());
1645 CCcontract_info *cp;
1649 printf("%s: exported outputs:\n", __func__);
1650 for (auto &oneout : chainObjects)
1652 if (oneout->objectType == CHAINOBJ_RESERVETRANSFER)
1654 CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)(oneout))->object;
1655 printf("%s\n", rt.ToUniValue().write(true, 2).c_str());
1660 CScript opRet = StoreOpRetArray(chainObjects);
1661 DeleteOpRetObjects(chainObjects);
1663 // now send transferred currencies to a reserve deposit
1664 cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
1666 currentReserveDeposits += ccx.totalAmounts;
1667 CCurrencyValueMap reserveDepositOut;
1668 CAmount nativeOut = 0;
1670 for (auto &oneCurrencyOut : currentReserveDeposits.valueMap)
1672 CCurrencyDefinition oneDef = currencyDefCache[oneCurrencyOut.first];
1674 // if the destination is this chain, or
1675 // the destination is another blockchain that does not control source currency, store in reserve
1676 if ((oneDef.systemID == ASSETCHAINS_CHAINID || oneDef.systemID != lastChainDef.systemID) &&
1677 oneCurrencyOut.second)
1679 if (oneCurrencyOut.first == ASSETCHAINS_CHAINID)
1681 nativeOut = oneCurrencyOut.second;
1685 reserveDepositOut.valueMap[oneCurrencyOut.first] = oneCurrencyOut.second;
1689 if (reserveDepositOut.valueMap.size())
1691 // send the entire amount to a reserve deposit output of the specific chain
1692 // we receive our fee on the other chain, when it comes back, or if a token,
1693 // when it gets imported back to the chain
1694 std::vector<CTxDestination> dests({CPubKey(ParseHex(CC.CChexstr))});
1695 CReserveDeposit rd = CReserveDeposit(lastChain, reserveDepositOut);
1696 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CReserveDeposit>(EVAL_RESERVE_DEPOSIT, dests, 1, &rd)),
1700 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
1702 // send native amount of zero to a cross chain export output of the specific chain
1703 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
1705 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx)),
1708 // when exports are confirmed as having been imported, they are finalized
1709 // until then, a finalization UTXO enables an index search to only find transactions
1710 // that have work to complete on this chain, or have not had their cross-chain import
1712 cp = CCinit(&CC, EVAL_FINALIZE_EXPORT);
1713 CTransactionFinalization finalization(CTransactionFinalization::FINALIZE_EXPORT, lastChain, 0);
1715 //printf("%s: Finalizing export with index dest %s\n", __func__, EncodeDestination(CKeyID(CCrossChainRPCData::GetConditionID(lastChainDef.systemID, EVAL_FINALIZE_EXPORT))).c_str());
1717 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
1718 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CTransactionFinalization>(EVAL_FINALIZE_EXPORT, dests, 1, &finalization)), 0);
1723 UniValue uni(UniValue::VOBJ);
1724 TxToUniv(tb.mtx, uint256(), uni);
1725 printf("%s: about to send reserve deposits with tx:\n%s\n", __func__, uni.write(1,2).c_str());
1727 TransactionBuilderResult buildResult(tb.Build());
1729 if (!buildResult.IsError() && buildResult.IsTx())
1731 // replace the last one only if we have a valid new one
1732 CTransaction tx = buildResult.GetTxOrThrow();
1734 uni = UniValue(UniValue::VOBJ);
1735 TxToUniv(tx, uint256(), uni);
1736 printf("%s: successfully built tx:\n%s\n", __func__, uni.write(1,2).c_str());
1738 LOCK2(cs_main, mempool.cs);
1739 static int lastHeight = 0;
1740 // remove conflicts, so that we get in
1741 std::list<CTransaction> removed;
1742 mempool.removeConflicts(tx, removed);
1744 // add to mem pool, prioritize according to the fee we will get, and relay
1745 //printf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1746 //LogPrintf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1747 if (myAddtomempool(tx))
1750 uint256 hash = tx.GetHash();
1751 CAmount nativeExportFees = ccx.totalFees.valueMap[ASSETCHAINS_CHAINID];
1752 mempool.PrioritiseTransaction(hash, hash.GetHex(), (double)(nativeExportFees << 1), nativeExportFees);
1756 UniValue uni(UniValue::VOBJ);
1757 TxToUniv(tx, uint256(), uni);
1758 //printf("%s: created invalid transaction:\n%s\n", __func__, uni.write(1,2).c_str());
1759 LogPrintf("%s: created invalid transaction:\n%s\n", __func__, uni.write(1,2).c_str());
1765 // we can't do any more useful work for this chain if we failed here
1766 printf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1767 LogPrintf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1772 // erase the inputs we've attempted to spend
1773 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1779 lastChain = output.first;
1786 void CConnectedChains::SignAndCommitImportTransactions(const CTransaction &lastImportTx, const std::vector<CTransaction> &transactions)
1788 int nHeight = chainActive.LastTip()->GetHeight();
1789 uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());
1790 LOCK2(cs_main, mempool.cs);
1792 uint256 lastHash, lastSignedHash;
1793 CCoinsViewCache view(pcoinsTip);
1795 // sign and commit the transactions
1796 for (auto &_tx : transactions)
1798 CMutableTransaction newTx(_tx);
1800 if (!lastHash.IsNull())
1802 //printf("last hash before signing: %s\n", lastHash.GetHex().c_str());
1803 for (auto &oneIn : newTx.vin)
1805 //printf("checking input with hash: %s\n", oneIn.prevout.hash.GetHex().c_str());
1806 if (oneIn.prevout.hash == lastHash)
1808 oneIn.prevout.hash = lastSignedHash;
1809 //printf("updated hash before signing: %s\n", lastSignedHash.GetHex().c_str());
1813 lastHash = _tx.GetHash();
1814 CTransaction tx = newTx;
1816 // sign the transaction and submit
1817 bool signSuccess = false;
1818 for (int i = 0; i < tx.vin.size(); i++)
1820 SignatureData sigdata;
1822 CScript outputScript;
1824 if (tx.vin[i].prevout.hash == lastImportTx.GetHash())
1826 value = lastImportTx.vout[tx.vin[i].prevout.n].nValue;
1827 outputScript = lastImportTx.vout[tx.vin[i].prevout.n].scriptPubKey;
1832 if (!view.GetCoins(tx.vin[i].prevout.hash, coins))
1834 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);
1835 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);
1838 value = coins.vout[tx.vin[i].prevout.n].nValue;
1839 outputScript = coins.vout[tx.vin[i].prevout.n].scriptPubKey;
1842 signSuccess = ProduceSignature(TransactionSignatureCreator(nullptr, &tx, i, value, SIGHASH_ALL), outputScript, sigdata, consensusBranchId);
1846 fprintf(stderr,"%s: failure to sign transaction\n", __func__);
1847 LogPrintf("%s: failure to sign transaction\n", __func__);
1850 UpdateTransaction(newTx, i, sigdata);
1856 // push to local node and sync with wallets
1857 CValidationState state;
1858 bool fMissingInputs;
1859 CTransaction signedTx(newTx);
1862 //TxToJSON(tx, uint256(), jsonTX);
1863 //printf("signed transaction:\n%s\n", jsonTX.write(1, 2).c_str());
1865 if (!AcceptToMemoryPool(mempool, state, signedTx, false, &fMissingInputs)) {
1866 if (state.IsInvalid()) {
1867 //UniValue txUni(UniValue::VOBJ);
1868 //TxToUniv(signedTx, uint256(), txUni);
1869 //fprintf(stderr,"%s: rejected by memory pool for %s\n%s\n", __func__, state.GetRejectReason().c_str(), txUni.write(1,2).c_str());
1870 LogPrintf("%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
1872 if (fMissingInputs) {
1873 fprintf(stderr,"%s: missing inputs\n", __func__);
1874 LogPrintf("%s: missing inputs\n", __func__);
1878 fprintf(stderr,"%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
1879 LogPrintf("%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
1886 UpdateCoins(signedTx, view, nHeight);
1887 lastSignedHash = signedTx.GetHash();
1897 CCurrencyValueMap CalculatePreconversions(const CCurrencyDefinition &chainDef, int32_t definitionHeight, CCurrencyValueMap &fees)
1899 // if we are getting information on the current chain, we assume that preconverted amounts have been
1900 // pre-calculated. otherwise, we will calculate them.
1901 CCurrencyValueMap retVal;
1902 if (chainDef.GetID() != ConnectedChains.ThisChain().GetID())
1904 std::multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> transferInputs;
1905 CCurrencyValueMap preconvertedAmounts;
1907 if (GetChainTransfers(transferInputs, chainDef.GetID(), definitionHeight, chainDef.startBlock - 1, CReserveTransfer::PRECONVERT | CReserveTransfer::VALID))
1909 auto curMap = chainDef.GetCurrenciesMap();
1910 for (auto &transfer : transferInputs)
1912 if (!(transfer.second.second.flags & CReserveTransfer::PREALLOCATE) && curMap.count(transfer.second.second.currencyID))
1914 CAmount conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1915 preconvertedAmounts.valueMap[transfer.second.second.currencyID] += (transfer.second.second.nValue - conversionFee);
1916 fees.valueMap[transfer.second.second.currencyID] += transfer.second.second.nFees + conversionFee;
1919 retVal = preconvertedAmounts;
1920 if (!chainDef.IsToken() && !(chainDef.ChainOptions() & chainDef.OPTION_FEESASRESERVE))
1928 retVal = CCurrencyValueMap(chainDef.currencies, chainDef.preconverted);
1933 // This creates a new token notarization input and output and attaches them to a new import transaction,
1934 // given the next export transaction about to be imported and its height
1935 bool CConnectedChains::NewImportNotarization(const CCurrencyDefinition &_curDef,
1937 const CTransaction &lastImportTx,
1938 uint32_t exportHeight,
1939 const CTransaction &exportTx,
1940 CMutableTransaction &mnewTx,
1941 CCoinbaseCurrencyState &newCurState)
1943 if (!_curDef.IsValid() || !_curDef.IsToken())
1945 LogPrintf("%s: cannot create import notarization for invalid or non-token currencies\n", __func__);
1949 uint160 currencyID = _curDef.GetID();
1951 CCurrencyDefinition curDef;
1952 int32_t curDefHeight;
1953 if (!GetCurrencyDefinition(currencyID, curDef, &curDefHeight))
1955 LogPrintf("%s: cannot create import notarization - currency not found\n", __func__);
1959 CPBaaSNotarization lastNotarization(lastImportTx);
1960 if (curDef.systemID == ASSETCHAINS_CHAINID && !lastNotarization.IsValid())
1962 LogPrintf("%s: error getting notarization transaction %s\n", __func__, lastImportTx.GetHash().GetHex().c_str());
1966 CCoinbaseCurrencyState initialCurrencyState = lastNotarization.currencyState;
1968 bool isDefinition = false;
1970 // if our last notarization is prior to the start block, then we need to get our
1971 // initial currency state from a new start
1972 if (lastNotarization.notarizationHeight < curDef.startBlock)
1974 initialCurrencyState = ConnectedChains.GetCurrencyState(curDef, curDef.startBlock - 1, curDefHeight);
1975 isDefinition = true;
1976 if (height >= curDef.startBlock)
1978 initialCurrencyState.SetPrelaunch(false);
1982 CCrossChainExport ccx(exportTx);
1985 LogPrintf("%s: invalid export transaction %s\n", __func__, lastImportTx.GetHash().GetHex().c_str());
1989 if (!lastNotarization.IsValid())
1991 CChainNotarizationData cnd;
1992 // TODO: right now, there is no notarization made until after the launch
1993 // we should roll up periodic notarizations and pay miners to do it, making
1994 // long pre-launch periods possible
1995 if (GetNotarizationData(curDef.GetID(), EVAL_ACCEPTEDNOTARIZATION, cnd) && cnd.vtx.size() && cnd.lastConfirmed >= 0)
1997 lastNotarization = cnd.vtx[cnd.lastConfirmed].second;
1998 initialCurrencyState = lastNotarization.currencyState;
2002 LogPrintf("%s: cannot get last notarization for %s\n", __func__, curDef.name.c_str());
2007 CBlockIndex *pindex;
2008 CTxDestination notarizationID = VERUS_DEFAULTID.IsNull() ? CTxDestination(CIdentityID(currencyID)) : CTxDestination(VERUS_DEFAULTID);
2010 // if this is the first notarization after start, make the notarization and determine if we should
2012 if (isDefinition || height == curDef.startBlock -1)
2014 bool refunding = false;
2016 pindex = chainActive[curDef.startBlock];
2018 // check if the chain is qualified for a refund
2019 CCurrencyValueMap minPreMap, fees;
2020 CCurrencyValueMap preConvertedMap = CCurrencyValueMap(curDef.currencies, curDef.preconverted).CanonicalMap();
2021 newCurState = initialCurrencyState;
2023 if (curDef.minPreconvert.size() && curDef.minPreconvert.size() == curDef.currencies.size())
2025 minPreMap = CCurrencyValueMap(curDef.currencies, curDef.minPreconvert).CanonicalMap();
2028 if (minPreMap.valueMap.size() && preConvertedMap < minPreMap)
2030 // we force the supply to zero
2031 // in any case where there was less than minimum participation,
2032 // the result of the supply cannot be zero, enabling us to easily determine that this
2033 // represents a failed launch
2034 newCurState.supply = 0;
2035 newCurState.SetRefunding(true);
2038 else if (curDef.IsFractional() &&
2039 exportTx.vout.size() &&
2040 exportTx.vout.back().scriptPubKey.IsOpReturn())
2042 // we are not refunding, and it is possible that we also have
2043 // normal conversions in addition to pre-conversions. add any conversions that may
2044 // be present into the new currency state
2045 CReserveTransactionDescriptor rtxd;
2046 std::vector<CBaseChainObject *> exportObjects;
2047 std::vector<CTxOut> vOutputs;
2049 exportObjects = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey);
2051 bool isValidExport = rtxd.AddReserveTransferImportOutputs(currencyID, curDef, initialCurrencyState, exportObjects, vOutputs, &newCurState);
2054 // on definition, initial state is correct
2055 newCurState = initialCurrencyState;
2057 DeleteOpRetObjects(exportObjects);
2060 LogPrintf("%s: invalid export opreturn for transaction %s\n", __func__, exportTx.GetHash().GetHex().c_str());
2067 if (chainActive.Height() > height)
2069 pindex = chainActive[height];
2073 pindex = chainActive.LastTip();
2078 LogPrintf("%s: invalid active chain\n", __func__);
2082 // this is not the first notarization, so the last notarization will let us know if this is a refund or not
2083 CCurrencyValueMap minPreMap;
2085 newCurState = initialCurrencyState;
2087 if (curDef.minPreconvert.size() && curDef.minPreconvert.size() == curDef.currencies.size())
2089 minPreMap = CCurrencyValueMap(curDef.currencies, curDef.minPreconvert).CanonicalMap();
2092 // we won't change currency state in notarizations after failure to launch, if success, recalculate as needed
2093 if (!(lastNotarization.currencyState.IsRefunding()))
2095 // calculate new currency state from this import
2096 // we are not refunding, and it is possible that we also have
2097 // normal conversions in addition to pre-conversions. add any conversions that may
2098 // be present into the new currency state
2099 CReserveTransactionDescriptor rtxd;
2100 std::vector<CBaseChainObject *> exportObjects;
2101 std::vector<CTxOut> vOutputs;
2103 exportObjects = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey);
2105 bool isValidExport = rtxd.AddReserveTransferImportOutputs(currencyID, curDef, initialCurrencyState, exportObjects, vOutputs, &newCurState);
2106 if (isValidExport && curDef.IsFractional())
2108 // we want the new price and the old state as a starting point to ensure no rounding error impact
2110 CCoinbaseCurrencyState tempCurState = initialCurrencyState;
2111 tempCurState.conversionPrice = newCurState.conversionPrice;
2113 rtxd = CReserveTransactionDescriptor();
2114 isValidExport = rtxd.AddReserveTransferImportOutputs(currencyID, curDef, tempCurState, exportObjects, vOutputs, &newCurState);
2116 else if (!curDef.IsFractional())
2118 newCurState = initialCurrencyState;
2120 DeleteOpRetObjects(exportObjects);
2123 LogPrintf("%s: invalid export opreturn for transaction %s\n", __func__, exportTx.GetHash().GetHex().c_str());
2129 uint256 lastImportTxHash = lastImportTx.GetHash();
2131 // now, add the initial notarization to the import tx
2132 // we will begin refund or import after notarization is accepted and returned by GetNotarizationData
2133 CPBaaSNotarization pbn = CPBaaSNotarization(curDef.notarizationProtocol,
2136 pindex->GetHeight(),
2137 chainActive.GetMMV().GetRoot(),
2138 chainActive.GetMMRNode(pindex->GetHeight()).hash,
2139 ArithToUint256(GetCompactPower(pindex->nNonce, pindex->nBits, pindex->nVersion)),
2142 lastNotarization.notarizationHeight,
2143 uint256(), 0, COpRetProof(), std::vector<CNodeData>());
2145 // create notarization output
2147 CCcontract_info *cp;
2149 std::vector<CTxDestination> dests;
2151 // make the accepted notarization output
2152 cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
2154 if (curDef.notarizationProtocol == curDef.NOTARIZATION_NOTARY_CHAINID)
2156 dests = std::vector<CTxDestination>({CIdentityID(currencyID)});
2158 else if (curDef.notarizationProtocol == curDef.NOTARIZATION_AUTO)
2160 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2166 mnewTx.vout.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &pbn))));
2168 // make the finalization output
2169 cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
2171 if (curDef.notarizationProtocol == curDef.NOTARIZATION_AUTO)
2173 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2176 // finish transaction by adding the prior input and finalization, sign, then put it in the mempool
2177 // all output for notarizing will be paid as mining fees, so there's no need to relay
2178 uint32_t confirmedOut, finalizeOut;
2179 if (!GetNotarizationAndFinalization(EVAL_ACCEPTEDNOTARIZATION, lastImportTx, pbn, &confirmedOut, &finalizeOut))
2181 printf("ERROR: could not find expected initial notarization for currency %s\n", curDef.name.c_str());
2185 mnewTx.vin.push_back(CTxIn(COutPoint(lastImportTxHash, confirmedOut)));
2186 mnewTx.vin.push_back(CTxIn(COutPoint(lastImportTxHash, finalizeOut)));
2188 // we need to store the input that we confirmed if we spent finalization outputs
2189 CTransactionFinalization nf(CTransactionFinalization::FINALIZE_NOTARIZATION, pbn.currencyID, mnewTx.vin.size() - 1);
2191 // update crypto condition with final notarization output data
2192 mnewTx.vout.push_back(CTxOut(0,
2193 MakeMofNCCScript(CConditionObj<CTransactionFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &nf))));
2198 // process token related, local imports and exports
2199 void CConnectedChains::ProcessLocalImports()
2201 // first determine all export threads on the current chain that are valid to import
2202 std::multimap<uint160, std::pair<int, CInputDescriptor>> exportOutputs;
2203 std::multimap<uint160, CTransaction> importThreads;
2204 uint160 thisChainID = thisChain.GetID();
2206 LOCK2(cs_main, mempool.cs);
2207 uint32_t nHeight = chainActive.Height();
2209 // get all pending, local exports and put them into a map
2210 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue>> unspentOutputs;
2211 std::map<uint160, std::pair<uint32_t, CTransaction>> currenciesToImport; // height of earliest tx
2212 CCurrencyDefinition oneCurrency;
2214 //printf("%s: Searching for %s\n", __func__, EncodeDestination(CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_FINALIZE_EXPORT))).c_str());
2215 if (GetAddressUnspent(ConnectedChains.ThisChain().GetConditionID(EVAL_FINALIZE_EXPORT), CScript::P2IDX, unspentOutputs))
2217 CCrossChainExport ccx, ccxDummy;
2218 CTransaction txOut, txImport;
2219 CPartialTransactionProof lastExport;
2220 CCrossChainImport cci;
2222 for (auto &oneOut : unspentOutputs)
2225 if (oneOut.second.blockHeight <= nHeight &&
2226 oneOut.second.script.IsPayToCryptoCondition(p) &&
2228 p.evalCode == EVAL_FINALIZE_EXPORT &&
2230 CTransactionFinalization(p.vData[0]).IsValid() &&
2231 myGetTransaction(oneOut.first.txhash, txOut, blkHash) &&
2232 (ccx = CCrossChainExport(txOut)).IsValid() &&
2233 (oneCurrency = GetCachedCurrency(ccx.systemID)).IsValid() &&
2234 oneCurrency.startBlock <= nHeight &&
2235 !currenciesToImport.count(ccx.systemID) &&
2236 GetLastImport(ccx.systemID, txImport, lastExport, cci, ccxDummy))
2238 auto blockIt = mapBlockIndex.find(blkHash);
2239 if (blockIt != mapBlockIndex.end() && chainActive.Contains(blockIt->second))
2241 currenciesToImport.insert(make_pair(ccx.systemID, make_pair(blockIt->second->GetHeight(), txImport)));
2247 CMutableTransaction txTemplate = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight);
2248 for (auto &oneIT : currenciesToImport)
2250 std::vector<CTransaction> importTxes;
2251 int32_t importOutNum = 0;
2252 CCrossChainImport oneImportInput(oneIT.second.second, &importOutNum);
2253 if (oneImportInput.IsValid())
2255 std::vector<CAddressUnspentDbEntry> reserveDeposits;
2256 GetAddressUnspent(currencyDefCache[oneIT.first].GetConditionID(EVAL_RESERVE_DEPOSIT), CScript::P2IDX, reserveDeposits);
2257 CCurrencyValueMap tokenImportAvailable;
2258 CAmount nativeImportAvailable = 0;
2259 for (auto &oneOut : reserveDeposits)
2261 nativeImportAvailable += oneOut.second.satoshis;
2262 tokenImportAvailable += oneOut.second.script.ReserveOutValue();
2263 //printf("nativeImportAvailable:%ld, tokenImportAvailable:%s\n", nativeImportAvailable, tokenImportAvailable.ToUniValue().write().c_str());
2265 nativeImportAvailable += oneIT.second.second.vout[importOutNum].nValue;
2266 tokenImportAvailable += oneIT.second.second.vout[importOutNum].ReserveOutValue();
2267 //printf("nativeImportAvailable:%ld, tokenImportAvailable:%s\n", nativeImportAvailable, tokenImportAvailable.ToUniValue().write().c_str());
2268 if (CreateLatestImports(currencyDefCache[oneIT.first], oneIT.second.second, txTemplate, CTransaction(), tokenImportAvailable, nativeImportAvailable, importTxes))
2270 // fund the first import transaction with all reserveDeposits
2271 // change amounts are passed through on the import thread
2273 // TODO: manage when this becomes to large by splitting reserve deposits over the
2275 if (importTxes.size())
2277 CMutableTransaction oneImport = importTxes[0];
2279 CCrossChainImport cci(importTxes[0], &outNum);
2282 // add the reserve deposit inputs to the first transaction
2283 // the outputs should have been automatically propagated through
2284 // fixup inputs if necessary
2285 if (reserveDeposits.size())
2287 UniValue jsonTX(UniValue::VOBJ);
2288 std::vector<uint256> prevHashes;
2290 for (int i = 0; i < importTxes.size() - 1; i++)
2292 prevHashes.push_back(importTxes[i].GetHash());
2295 // TODO - get reserve deposits from exports, not all at once, as in refunds
2296 for (auto &oneOut : reserveDeposits)
2298 oneImport.vin.push_back(CTxIn(oneOut.first.txhash, oneOut.first.index));
2301 importTxes[0] = oneImport;
2303 for (int i = 0; i < importTxes.size() - 1; i++)
2305 oneImport = importTxes[i + 1];
2306 for (auto &oneIn : oneImport.vin)
2308 if (oneIn.prevout.hash == prevHashes[i])
2310 //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());
2311 oneIn.prevout.hash = importTxes[i].GetHash();
2314 importTxes[i + 1] = oneImport;
2318 SignAndCommitImportTransactions(oneIT.second.second, importTxes);
2326 void CConnectedChains::SubmissionThread()
2330 arith_uint256 lastHash;
2331 int64_t lastImportTime = 0;
2332 uint32_t lastHeight = 0;
2334 // wait for something to check on, then submit blocks that should be submitted
2337 boost::this_thread::interruption_point();
2339 if (IsVerusActive())
2341 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
2342 //printf("SubmissionThread: pruning\n");
2343 PruneOldChains(GetAdjustedTime() - 300);
2344 bool submit = false;
2346 LOCK(cs_mergemining);
2347 if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
2349 qualifiedHeaders.clear();
2351 submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
2353 //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
2357 //printf("SubmissionThread: calling submit qualified blocks\n");
2358 SubmitQualifiedBlocks();
2361 ProcessLocalImports();
2365 sem_submitthread.wait();
2370 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
2371 if (CheckVerusPBaaSAvailable())
2373 // check to see if we have recently earned a block with an earned notarization that qualifies for
2374 // submitting an accepted notarization
2375 if (earnedNotarizationHeight)
2378 int32_t txIndex = -1, height;
2380 LOCK(cs_mergemining);
2381 if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
2383 blk = earnedNotarizationBlock;
2384 earnedNotarizationBlock = CBlock();
2385 txIndex = earnedNotarizationIndex;
2386 height = earnedNotarizationHeight;
2387 earnedNotarizationHeight = 0;
2393 //printf("SubmissionThread: testing notarization\n");
2394 CTransaction lastConfirmed;
2395 uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
2399 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
2400 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
2405 // every "n" seconds, look for imports to include in our blocks from the Verus chain
2406 if ((GetAdjustedTime() - lastImportTime) >= 30 || lastHeight < (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight()))
2408 lastImportTime = GetAdjustedTime();
2409 lastHeight = (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight());
2411 // see if our notary has a confirmed notarization for us
2412 UniValue params(UniValue::VARR);
2415 params.push_back(thisChain.name);
2419 result = find_value(RPCCallRoot("getlastimportin", params), "result");
2420 } catch (exception e)
2422 result = NullUniValue;
2425 if (!result.isNull())
2427 auto txUniStr = find_value(result, "lastimporttransaction");
2428 auto txLastConfirmedStr = find_value(result, "lastconfirmednotarization");
2429 auto txTemplateStr = find_value(result, "importtxtemplate");
2430 CAmount nativeImportAvailable = uni_get_int64(find_value(result, "nativeimportavailable"));
2431 CCurrencyValueMap tokenImportAvailable(find_value(params[0], "tokenimportavailable"));
2433 CTransaction lastImportTx, lastConfirmedTx, templateTx;
2435 if (txUniStr.isStr() && txTemplateStr.isStr() &&
2436 DecodeHexTx(lastImportTx, txUniStr.get_str()) &&
2437 DecodeHexTx(lastConfirmedTx, txLastConfirmedStr.get_str()) &&
2438 DecodeHexTx(templateTx, txTemplateStr.get_str()))
2440 std::vector<CTransaction> importTxes;
2441 if (CreateLatestImports(notaryChain.chainDefinition, lastImportTx, templateTx, lastConfirmedTx, tokenImportAvailable, nativeImportAvailable, importTxes))
2443 for (auto importTx : importTxes)
2447 params.push_back(EncodeHexTx(importTx));
2451 txResult = find_value(RPCCallRoot("signrawtransaction", params), "result");
2452 if (txResult.isObject() && !(txResult = find_value(txResult, "hex")).isNull() && txResult.isStr() && txResult.get_str().size())
2455 params.push_back(txResult);
2456 txResult = find_value(RPCCallRoot("sendrawtransaction", params), "result");
2460 txResult = NullUniValue;
2463 } catch (exception e)
2465 txResult = NullUniValue;
2468 if (txResult.isStr())
2470 testId.SetHex(txResult.get_str());
2472 if (testId.IsNull())
2484 boost::this_thread::interruption_point();
2487 catch (const boost::thread_interrupted&)
2489 LogPrintf("Verus merge mining thread terminated\n");
2493 void CConnectedChains::SubmissionThreadStub()
2495 ConnectedChains.SubmissionThread();
2498 void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
2500 // called after winning a block that contains an earned notarization
2501 // the earned notarization and its height are queued for processing by the submission thread
2502 // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
2504 LOCK(cs_mergemining);
2506 // we only care about the last
2507 earnedNotarizationHeight = height;
2508 earnedNotarizationBlock = blk;
2509 earnedNotarizationIndex = txIndex;
2512 bool IsChainDefinitionInput(const CScript &scriptSig)
2515 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_CURRENCY_DEFINITION;