]> Git Repo - VerusCoin.git/blob - src/pbaas/pbaas.cpp
Don't put native currency into the reserveDepositOut of an export
[VerusCoin.git] / src / pbaas / pbaas.cpp
1 /********************************************************************
2  * (C) 2019 Michael Toutonghi
3  * 
4  * Distributed under the MIT software license, see the accompanying
5  * file COPYING or http://www.opensource.org/licenses/mit-license.php.
6  * 
7  * This provides support for PBaaS initialization, notarization, and cross-chain token
8  * transactions and enabling liquid or non-liquid tokens across the
9  * Verus ecosystem.
10  * 
11  */
12
13 #include "base58.h"
14 #include "main.h"
15 #include "rpc/pbaasrpc.h"
16 #include "timedata.h"
17 #include "transaction_builder.h"
18
19 CConnectedChains ConnectedChains;
20
21 bool IsVerusActive()
22 {
23     return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 0 || strcmp(ASSETCHAINS_SYMBOL, "VRSCTEST") == 0);
24 }
25
26 bool IsVerusMainnetActive()
27 {
28     return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 0);
29 }
30
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)
33 {
34     if (opRetScript.IsOpReturn() && opRetScript.size() <= MAX_OP_RETURN_RELAY)
35     {
36         CTxOut vOut = CTxOut();
37         vOut.scriptPubKey = opRetScript;
38         vOut.nValue = 0;
39         mtx.vout.push_back(vOut);
40         return mtx.vout.size() - 1;
41     }
42     else
43     {
44         return -1;
45     }
46 }
47
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)
51 {
52     union {
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;
62     };
63
64     retPtr = &bo;
65
66     switch(bo.objectType)
67     {
68         case CHAINOBJ_HEADER:
69             return pNewHeader->GetHash();
70
71         case CHAINOBJ_TRANSACTION_PROOF:
72             return pNewTx->GetHash();
73
74         case CHAINOBJ_HEADER_REF:
75             return pNewHeaderRef->GetHash();
76
77         case CHAINOBJ_PRIORBLOCKS:
78             return pPriors->GetHash();
79
80         case CHAINOBJ_PROOF_ROOT:
81             return pNewProofRoot->object;
82
83         case CHAINOBJ_RESERVETRANSFER:
84             return pExport->GetHash();
85
86         case CHAINOBJ_CROSSCHAINPROOF:
87             return pCrossChainProof->GetHash();
88
89         case CHAINOBJ_COMPOSITEOBJECT:
90             return pCrossChainProof->GetHash();
91
92     }
93     return uint256();
94 }
95
96 // used to export coins from one chain to another, if they are not native, they are represented on the other
97 // chain as tokens
98 bool ValidateCrossChainExport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
99 {
100     return true;
101 }
102
103 bool IsCrossChainExportInput(const CScript &scriptSig)
104 {
105     return true;
106 }
107
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)
111 {
112     return true;
113 }
114 bool IsCrossChainImportInput(const CScript &scriptSig)
115 {
116     return true;
117 }
118
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)
121 {
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
126     return true;
127 }
128 bool IsServiceRewardInput(const CScript &scriptSig)
129 {
130     return true;
131 }
132
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)
135 {
136     return true;
137 }
138 bool IsReserveOutputInput(const CScript &scriptSig)
139 {
140     return true;
141 }
142
143 bool ValidateReserveTransfer(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
144 {
145     return true;
146 }
147 bool IsReserveTransferInput(const CScript &scriptSig)
148 {
149     return true;
150 }
151
152 bool ValidateReserveDeposit(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
153 {
154     return true;
155 }
156 bool IsReserveDepositInput(const CScript &scriptSig)
157 {
158     return true;
159 }
160
161 bool ValidateCurrencyState(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
162 {
163     return true;
164 }
165 bool IsCurrencyStateInput(const CScript &scriptSig)
166 {
167     return true;
168 }
169
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)
172 {
173     return true;
174 }
175 bool IsReserveExchangeInput(const CScript &scriptSig)
176 {
177     return true;
178 }
179
180
181 /*
182  * Verifies that the input objects match the hashes and returns the transaction.
183  * 
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.
188  * 
189  */
190 bool ValidateOpretProof(CScript &opRet, COpRetProof &orProof)
191 {
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
194     return true;
195 }
196
197 int8_t ObjTypeCode(const CBlockHeaderProof &obj)
198 {
199     return CHAINOBJ_HEADER;
200 }
201
202 int8_t ObjTypeCode(const uint256 &obj)
203 {
204     return CHAINOBJ_PROOF_ROOT;
205 }
206
207 int8_t ObjTypeCode(const CPartialTransactionProof &obj)
208 {
209     return CHAINOBJ_TRANSACTION_PROOF;
210 }
211
212 int8_t ObjTypeCode(const CBlockHeaderAndProof &obj)
213 {
214     return CHAINOBJ_HEADER_REF;
215 }
216
217 int8_t ObjTypeCode(const CPriorBlocksCommitment &obj)
218 {
219     return CHAINOBJ_PRIORBLOCKS;
220 }
221
222 int8_t ObjTypeCode(const CReserveTransfer &obj)
223 {
224     return CHAINOBJ_RESERVETRANSFER;
225 }
226
227 int8_t ObjTypeCode(const CCrossChainProof &obj)
228 {
229     return CHAINOBJ_CROSSCHAINPROOF;
230 }
231
232 int8_t ObjTypeCode(const CCompositeChainObject &obj)
233 {
234     return CHAINOBJ_COMPOSITEOBJECT;
235 }
236
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)
239 {
240     CScript vData;
241     CDataStream s = CDataStream(SER_NETWORK, PROTOCOL_VERSION);
242     s << (int32_t)OPRETTYPE_OBJECTARR;
243     bool error = false;
244
245     for (auto pobj : objPtrs)
246     {
247         try
248         {
249             if (!DehydrateChainObject(s, pobj))
250             {
251                 error = true;
252                 break;
253             }
254         }
255         catch(const std::exception& e)
256         {
257             std::cerr << e.what() << '\n';
258             error = true;
259             break;
260         }
261     }
262
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());
265
266     std::vector<unsigned char> vch(s.begin(), s.end());
267     return error ? CScript() : CScript() << OP_RETURN << vch;
268 }
269
270 void DeleteOpRetObjects(std::vector<CBaseChainObject *> &ora)
271 {
272     for (auto pobj : ora)
273     {
274         switch(pobj->objectType)
275         {
276             case CHAINOBJ_HEADER:
277             {
278                 delete (CChainObject<CBlockHeaderAndProof> *)pobj;
279                 break;
280             }
281
282             case CHAINOBJ_TRANSACTION_PROOF:
283             {
284                 delete (CChainObject<CPartialTransactionProof> *)pobj;
285                 break;
286             }
287
288             case CHAINOBJ_PROOF_ROOT:
289             {
290                 delete (CChainObject<uint256> *)pobj;
291                 break;
292             }
293
294             case CHAINOBJ_HEADER_REF:
295             {
296                 delete (CChainObject<CBlockHeaderProof> *)pobj;
297                 break;
298             }
299
300             case CHAINOBJ_PRIORBLOCKS:
301             {
302                 delete (CChainObject<CPriorBlocksCommitment> *)pobj;
303                 break;
304             }
305
306             case CHAINOBJ_RESERVETRANSFER:
307             {
308                 delete (CChainObject<CReserveTransfer> *)pobj;
309                 break;
310             }
311
312             case CHAINOBJ_CROSSCHAINPROOF:
313             {
314                 delete (CChainObject<CCrossChainProof> *)pobj;
315                 break;
316             }
317
318             case CHAINOBJ_COMPOSITEOBJECT:
319             {
320                 delete (CChainObject<CCompositeChainObject> *)pobj;
321                 break;
322             }
323
324             default:
325             {
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());
329                 
330                 delete pobj;
331             }
332         }
333     }
334     ora.clear();
335 }
336
337 std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
338 {
339     std::vector<unsigned char> vch;
340     std::vector<CBaseChainObject *> vRet;
341     if (opRetScript.IsOpReturn() && GetOpReturnData(opRetScript, vch) && vch.size() > 0)
342     {
343         CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION);
344
345         int32_t opRetType;
346
347         try
348         {
349             s >> opRetType;
350             if (opRetType == OPRETTYPE_OBJECTARR)
351             {
352                 CBaseChainObject *pobj;
353                 while (!s.empty() && (pobj = RehydrateChainObject(s)))
354                 {
355                     vRet.push_back(pobj);
356                 }
357                 if (!s.empty())
358                 {
359                     printf("failed to load all objects in opret");
360                     DeleteOpRetObjects(vRet);
361                     vRet.clear();
362                 }
363             }
364         }
365         catch(const std::exception& e)
366         {
367             std::cerr << e.what() << '\n';
368             DeleteOpRetObjects(vRet);
369             vRet.clear();
370         }
371     }
372     return vRet;
373 }
374
375 CServiceReward::CServiceReward(const CTransaction &tx)
376 {
377     nVersion = PBAAS_VERSION_INVALID;
378     for (auto out : tx.vout)
379     {
380         COptCCParams p;
381         if (IsPayToCryptoCondition(out.scriptPubKey, p))
382         {
383             // always take the first for now
384             if (p.evalCode == EVAL_SERVICEREWARD)
385             {
386                 FromVector(p.vData[0], *this);
387                 break;
388             }
389         }
390     }
391 }
392
393 CCrossChainExport::CCrossChainExport(const CTransaction &tx, int32_t *pCCXOutputNum)
394 {
395     int32_t _ccxOutputNum = 0;
396     int32_t &ccxOutputNum = pCCXOutputNum ? *pCCXOutputNum : _ccxOutputNum;
397     
398     for (int i = 0; i < tx.vout.size(); i++)
399     {
400         COptCCParams p;
401         if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) &&
402             p.IsValid() &&
403             p.evalCode == EVAL_CROSSCHAIN_EXPORT)
404         {
405             FromVector(p.vData[0], *this);
406             ccxOutputNum = i;
407             break;
408         }
409     }
410 }
411
412 CCurrencyDefinition::CCurrencyDefinition(const CScript &scriptPubKey)
413 {
414     nVersion = PBAAS_VERSION_INVALID;
415     COptCCParams p;
416     if (scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
417     {
418         if (p.evalCode == EVAL_CURRENCY_DEFINITION)
419         {
420             FromVector(p.vData[0], *this);
421         }
422     }
423 }
424
425 std::vector<CCurrencyDefinition> CCurrencyDefinition::GetCurrencyDefinitions(const CTransaction &tx)
426 {
427     std::vector<CCurrencyDefinition> retVal;
428     for (auto &out : tx.vout)
429     {
430         CCurrencyDefinition oneCur = CCurrencyDefinition(out.scriptPubKey);
431         if (oneCur.IsValid())
432         {
433             retVal.push_back(oneCur);
434         }
435     }
436     return retVal;
437 }
438
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;
446
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)
450 {
451     // the chain definition output can be spent when the chain is at the end of its life and only then
452     // TODO
453     return false;
454 }
455
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)
459 {
460     // checked before a chain definition output script is accepted as a valid transaction
461
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
467     //
468
469     // get the source transaction
470     uint256 blkHash;
471     CTransaction thisTx;
472     if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
473     {
474         LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
475         return false;
476     }
477
478     std::vector<CCurrencyDefinition> chainDefs = CCurrencyDefinition::GetCurrencyDefinitions(thisTx);
479     CPBaaSNotarization notarization(thisTx);
480     CTransactionFinalization finalization(thisTx);
481     bool isVerusActive = IsVerusActive();
482
483     if (!notarization.IsValid() || !finalization.IsValid())
484     {
485         LogPrintf("transaction specified, %s, must have valid notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
486         return false;
487     }
488
489     std::set<uint160> allCurrencyIDs;
490     for (auto &curPair : ConnectedChains.ReserveCurrencies())
491     {
492         allCurrencyIDs.insert(curPair.first);
493     }
494     allCurrencyIDs.insert(ConnectedChains.ThisChain().GetID());
495     if (!isVerusActive)
496     {
497         allCurrencyIDs.insert(ConnectedChains.notaryChain.GetID());
498     }
499
500     bool isCoinbase = thisTx.IsCoinBase();
501     bool isVerified = false;
502     CIdentity activatedID(thisTx);
503
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
506     //    name and ID.
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())
514     {
515         isVerified = true;
516     }
517     else if (isCoinbase && chainDefs.size() >= 1 && !isVerusActive)
518     {
519         int32_t height1 = 1;
520         CScript expect = CScript() << height1;
521         opcodetype opcode = (opcodetype)*expect.begin();
522
523         if (opcode >= OP_1 && opcode <= OP_16)
524         {
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);
527         }
528         else
529         {
530             isVerified = thisTx.vin[0].scriptSig.size() >= expect.size() && std::equal(expect.begin(), expect.end(), thisTx.vin[0].scriptSig.begin());
531         }
532
533         for (auto &chainDef : chainDefs)
534         {
535             uint160 chainID = chainDef.GetID();
536             if (!chainDef.IsValid())
537             {
538                 LogPrintf("transaction specified, %s, must not contain invalid chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
539                 return false;
540             }
541             if (!allCurrencyIDs.count(chainID))
542             {
543                 LogPrintf("transaction specified, %s, must not contain invalid chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
544                 return false;
545             }
546             allCurrencyIDs.erase(chainID);
547         }
548         if (allCurrencyIDs.size())
549         {
550             LogPrintf("transaction specified, %s, does not contain all required chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
551             return false;
552         }
553     }
554
555     return isVerified;
556 }
557
558 CCurrencyValueMap CCrossChainExport::CalculateExportFee(const CCurrencyValueMap &fees, int numIn)
559 {
560     CCurrencyValueMap retVal;
561
562     if (numIn > MAX_EXPORT_INPUTS)
563     {
564         return retVal;
565     }
566     static const arith_uint256 satoshis(100000000);
567
568     arith_uint256 ratio(50000000 + ((25000000 / MAX_EXPORT_INPUTS) * (numIn - 1)));
569
570     for (auto &feePair : fees.valueMap)
571     {
572         retVal.valueMap[feePair.first] = (((arith_uint256(feePair.second) * ratio)) / satoshis).GetLow64();
573     }
574     return retVal.CanonicalMap();
575 }
576
577 CCurrencyValueMap CCrossChainExport::CalculateExportFee() const
578 {
579     return CalculateExportFee(totalFees, numInputs);
580 }
581
582 CCurrencyValueMap CCrossChainExport::CalculateImportFee() const
583 {
584     CCurrencyValueMap retVal;
585
586     for (auto &feePair : CalculateExportFee().valueMap)
587     {
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;
591     }
592     return retVal;
593 }
594
595 bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
596 {
597     bool retval = false;
598     LOCK(cs_mergemining);
599
600     //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
601
602     auto chainIt = mergeMinedChains.find(chainID);
603     if (chainIt != mergeMinedChains.end())
604     {
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++)
608         {
609             // make sure we don't just match by target
610             if (removeRange.first->second->GetID() == chainID)
611             {
612                 mergeMinedTargets.erase(removeRange.first);
613                 break;
614             }
615         }
616         mergeMinedChains.erase(chainID);
617         dirty = retval = true;
618
619         // if we get to 0, give the thread a kick to stop waiting for mining
620         //if (!mergeMinedChains.size())
621         //{
622         //    sem_submitthread.post();
623         //}
624     }
625     return retval;
626 }
627
628 // remove merge mined chains added and not updated since a specific time
629 void CConnectedChains::PruneOldChains(uint32_t pruneBefore)
630 {
631     vector<uint160> toRemove;
632
633     LOCK(cs_mergemining);
634     for (auto blkData : mergeMinedChains)
635     {
636         if (blkData.second.block.nTime < pruneBefore)
637         {
638             toRemove.push_back(blkData.first);
639         }
640     }
641
642     for (auto id : toRemove)
643     {
644         //printf("Pruning chainID: %s\n", id.GetHex().c_str());
645         RemoveMergedBlock(id);
646     }
647 }
648
649 // adds or updates merge mined blocks
650 // returns false if failed to add
651 bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
652 {
653     // determine if we should replace one or add to the merge mine vector
654     {
655         LOCK(cs_mergemining);
656
657         arith_uint256 target;
658         uint160 cID = blkData.GetID();
659         auto it = mergeMinedChains.find(cID);
660         if (it != mergeMinedChains.end())
661         {
662             RemoveMergedBlock(cID);             // remove it if already there
663         }
664         target.SetCompact(blkData.block.nBits);
665
666         //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
667
668         mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
669         dirty = true;
670     }
671     return true;
672 }
673
674 bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
675 {
676     {
677         LOCK(cs_mergemining);
678         auto chainIt = mergeMinedChains.find(chainID);
679         if (chainIt != mergeMinedChains.end())
680         {
681             rpcChainData = (CRPCChainData)chainIt->second;
682             return true;
683         }
684         return false;
685     }
686 }
687
688 // this returns a pointer to the data without copy and assumes the lock is held
689 CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
690 {
691     {
692         auto chainIt = mergeMinedChains.find(chainID);
693         if (chainIt != mergeMinedChains.end())
694         {
695             return &chainIt->second;
696         }
697         return NULL;
698     }
699 }
700
701 void CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
702 {
703     //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
704     {
705         LOCK(cs_mergemining);
706
707         qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
708     }
709     sem_submitthread.post();
710 }
711
712 void CConnectedChains::CheckImports()
713 {
714     sem_submitthread.post();
715 }
716
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()
720 {
721     std::set<uint160> inHeader;
722     bool submissionFound;
723     CPBaaSMergeMinedChainData chainData;
724     vector<pair<string, UniValue>>  results;
725
726     CBlockHeader bh;
727     arith_uint256 lastHash;
728     CPBaaSBlockHeader pbh;
729
730     do
731     {
732         submissionFound = false;
733         {
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())
738             {
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++)
741                 {
742                     inHeader.insert(pbh.chainID);
743                 }
744
745                 uint160 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++)
748                 {
749                     chainID = chainIt->second->GetID();
750                     if (inHeader.count(chainID))
751                     {
752                         // first, check that the winning header matches the block that is there
753                         CPBaaSPreHeader preHeader(chainIt->second->block);
754                         preHeader.SetBlockData(headerIt->second);
755
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))
758                         {
759                             // save block as is, remove the block from merged headers, replace header, and submit
760                             chainData = *chainIt->second;
761
762                             *(CBlockHeader *)&chainData.block = headerIt->second;
763
764                             submissionFound = true;
765                         }
766                         //else // not an error condition. code is here for debugging
767                         //{
768                         //    printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
769                         //}
770                     }
771                     //else // not an error condition. code is here for debugging
772                     //{
773                     //    printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
774                     //}
775                 }
776
777                 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
778                 if (submissionFound)
779                 {
780                     // once it is going to be submitted, remove block from this chain until a new one is added again
781                     RemoveMergedBlock(chainID);
782                     break;
783                 }
784                 else
785                 {
786                     qualifiedHeaders.erase(headerIt);
787                 }
788             }
789         }
790         if (submissionFound)
791         {
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;
798             try
799             {
800                 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
801                 result = find_value(result, "result");
802                 error = find_value(result, "error");
803             }
804             catch (exception e)
805             {
806                 result = UniValue(e.what());
807             }
808             results.push_back(make_pair(chainData.chainDefinition.name, result));
809             if (result.isStr() || !error.isNull())
810             {
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());
812             }
813             else
814             {
815                 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
816             }
817         }
818     } while (submissionFound);
819     return results;
820 }
821
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)
824 {
825     vector<uint160> inHeader;
826     vector<UniValue> toCombine;
827     arith_uint256 blkHash = UintToArith256(bh.GetHash());
828     arith_uint256 target(0);
829     
830     CPBaaSBlockHeader pbh;
831
832     {
833         LOCK(cs_mergemining);
834
835         CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
836
837         for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
838         {
839             if (bh.GetPBaaSHeader(pbh, i))
840             {
841                 inHeader.push_back(pbh.chainID);
842             }
843         }
844
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++)
848         {
849             auto it = mergeMinedChains.find(inHeader[i]);
850             if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
851             {
852                 bh.DeletePBaaSHeader(i);
853             }
854         }
855
856         for (auto chain : mergeMinedChains)
857         {
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)
863             {
864                 if (!bh.AddUpdatePBaaSHeader(pbh))
865                 {
866                     LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
867                     break;
868                 }
869                 else
870                 {
871                     arith_uint256 t;
872                     t.SetCompact(chain.second.block.nBits);
873                     if (t > target)
874                     {
875                         target = t;
876                     }
877                 }
878             }
879             else
880             {
881                 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
882             }
883         }
884         dirty = false;
885     }
886
887     return target.GetCompact();
888 }
889
890 bool CConnectedChains::IsVerusPBaaSAvailable()
891 {
892     return notaryChainVersion >= "0.8.0";
893 }
894
895 extern string PBAAS_HOST, PBAAS_USERPASS;
896 extern int32_t PBAAS_PORT;
897 bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
898 {
899     if (chainInfoUni.isObject() && chainDefUni.isObject())
900     {
901         UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
902         if (uniVer.isStr())
903         {
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);
909         }
910     }
911     return IsVerusPBaaSAvailable();
912 }
913
914 uint32_t CConnectedChains::NotaryChainHeight()
915 {
916     LOCK(cs_mergemining);
917     return notaryChainHeight;
918 }
919
920 bool CConnectedChains::CheckVerusPBaaSAvailable()
921 {
922     if (IsVerusActive())
923     {
924         notaryChainVersion = "";
925     }
926     else
927     {
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;
931         try
932         {
933             UniValue params(UniValue::VARR);
934             chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
935             if (!chainInfo.isNull())
936             {
937                 params.push_back(VERUS_CHAINNAME);
938                 chainDef = find_value(RPCCallRoot("getcurrency", params), "result");
939
940                 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
941                 {
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()))
944                     {
945                         bool success = false;
946                         params.clear();
947                         params.push_back(EncodeDestination(CIdentityID(thisChain.GetID())));
948                         chainDef = find_value(RPCCallRoot("getcurrency", params), "result");
949                         if (!chainDef.isNull())
950                         {
951                             CCurrencyDefinition currencyDef(chainDef);
952                             if (currencyDef.IsValid())
953                             {
954                                 thisChain = currencyDef;
955                                 if (NotaryChainHeight() >= thisChain.startBlock)
956                                 {
957                                     readyToStart = true;    // this only gates mining of block one, to be sure we have the latest definition
958                                 }
959                                 success = true;
960                             }
961                         }
962                         return success;
963                     }
964                     return true;
965                 }
966             }
967         } catch (exception e)
968         {
969             LogPrintf("%s: Error communicating with %s chain\n", __func__, VERUS_CHAINNAME);
970         }
971     }
972     notaryChainVersion = "";
973     return false;
974 }
975
976 int CConnectedChains::GetThisChainPort() const
977 {
978     int port;
979     string host;
980     for (auto node : defaultPeerNodes)
981     {
982         SplitHostPort(node.networkAddress, port, host);
983         if (port)
984         {
985             return port;
986         }
987     }
988     return 0;
989 }
990
991 CCoinbaseCurrencyState CConnectedChains::AddPrelaunchConversions(CCurrencyDefinition &curDef,
992                                                                  const CCoinbaseCurrencyState &_currencyState,
993                                                                  int32_t fromHeight,
994                                                                  int32_t height,
995                                                                  int32_t curDefHeight)
996 {
997     CCoinbaseCurrencyState currencyState = _currencyState;
998     bool firstUpdate = fromHeight <= curDefHeight;
999     if (firstUpdate)
1000     {
1001         if (curDef.IsFractional())
1002         {
1003             currencyState.supply = curDef.initialFractionalSupply;
1004             currencyState.reserves = std::vector<int64_t>(currencyState.reserves.size(), 0);
1005             currencyState.weights = curDef.weights;
1006         }
1007         else
1008         {
1009             // supply is determined by purchases * current conversion rate
1010             currencyState.supply = currencyState.initialSupply;
1011         }
1012     }
1013
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))
1020     {
1021         currencyState.ClearForNextBlock();
1022
1023         for (auto &transfer : unspentTransfers)
1024         {
1025             if (transfer.second.second.flags & CReserveTransfer::PRECONVERT)
1026             {
1027                 CAmount conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1028
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())
1032                 {
1033                     currencyState.reserves[currencyIndexes[transfer.second.second.currencyID]] += transfer.second.second.nValue - conversionFee;
1034                 }
1035                 else
1036                 {
1037                     currencyState.supply += CCurrencyState::ReserveToNativeRaw(transfer.second.second.nValue - conversionFee, currencyState.PriceInReserve(currencyIndexes[transfer.second.second.currencyID]));
1038                 }
1039
1040                 if (transfer.second.second.currencyID == curDef.systemID)
1041                 {
1042                     currencyState.nativeConversionFees += conversionFee;
1043                     currencyState.nativeFees += conversionFee + transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
1044                 }
1045
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;
1049             }
1050         }
1051     }
1052
1053     if (curDef.conversions.size() != curDef.currencies.size())
1054     {
1055         curDef.conversions = std::vector<int64_t>(curDef.currencies.size());
1056     }
1057     for (int i = 0; i < curDef.conversions.size(); i++)
1058     {
1059         currencyState.conversionPrice[i] = curDef.conversions[i] = currencyState.PriceInReserve(i, true);
1060     }
1061
1062     // set initial supply from actual conversions if this is first update
1063     if (firstUpdate && curDef.IsFractional())
1064     {
1065         CAmount lowSupply = 0, highSupply = 0;
1066         for (auto &transfer : unspentTransfers)
1067         {
1068             if (transfer.second.second.flags & CReserveTransfer::PRECONVERT)
1069             {
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));
1073             }
1074         }
1075         if (lowSupply > currencyState.supply)
1076         {
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);
1079         }
1080
1081         // now, remove carveout percentage from each weight & reserve
1082         // for currency state
1083         int32_t preLaunchCarveOutTotal = 0;
1084         for (auto &carveout : curDef.preLaunchCarveOuts)
1085         {
1086             preLaunchCarveOutTotal += carveout.second;
1087             
1088         }
1089         static arith_uint256 bigSatoshi(SATOSHIDEN);
1090         for (auto &oneReserve : currencyState.reserves)
1091         {
1092             oneReserve =  ((arith_uint256(oneReserve) * arith_uint256(SATOSHIDEN - preLaunchCarveOutTotal)) / bigSatoshi).GetLow64();
1093         }
1094         for (auto &oneWeight : currencyState.weights)
1095         {
1096             oneWeight = ((arith_uint256(oneWeight) * arith_uint256(CCurrencyDefinition::CalculateRatioOfValue((SATOSHIDEN - preLaunchCarveOutTotal), SATOSHIDEN - curDef.preLaunchDiscount))) / bigSatoshi).GetLow64();
1097         }
1098     }
1099
1100     currencyState.UpdateWithEmission(curDef.GetTotalPreallocation());
1101
1102     return currencyState;
1103 }
1104
1105 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(CCurrencyDefinition &curDef, int32_t height, int32_t curDefHeight)
1106 {
1107     uint160 chainID = curDef.GetID();
1108     CCoinbaseCurrencyState currencyState;
1109     std::vector<CAddressIndexDbEntry> notarizationIndex;
1110
1111     if (chainID == ASSETCHAINS_CHAINID)
1112     {
1113         CBlock block;
1114         if (IsVerusActive() ||
1115             CConstVerusSolutionVector::activationHeight.ActiveVersion(height) < CActivationHeight::ACTIVATE_PBAAS ||
1116             height == 0 ||
1117             height > chainActive.Height() ||
1118             !chainActive[height] ||
1119             !ReadBlockFromDisk(block, chainActive[height], Params().GetConsensus()) ||
1120             !(currencyState = CCoinbaseCurrencyState(block.vtx[0])).IsValid())
1121         {
1122             currencyState = GetInitialCurrencyState(thisChain);
1123         }
1124     }
1125     // if this is a token on this chain, it will be simply notarized
1126     else if (curDef.systemID == ASSETCHAINS_CHAINID)
1127     {
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())
1133         {
1134             if (!(notarization.IsValid() && notarization.notarizationHeight >= curDef.startBlock))
1135             {
1136                 // pre-launch
1137                 currencyState.SetPrelaunch(true);
1138                 currencyState = AddPrelaunchConversions(curDef, 
1139                                                         currencyState, 
1140                                                         notarization.IsValid() ? notarization.notarizationHeight : curDefHeight, 
1141                                                         height, 
1142                                                         curDefHeight);
1143             }
1144             else
1145             {
1146                 currencyState.SetPrelaunch(false);
1147             }
1148         }
1149     }
1150     else
1151     {
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))
1157         {
1158             int32_t transfersFrom = curDefHeight;
1159             if (cnd.lastConfirmed != -1)
1160             {
1161                 transfersFrom = cnd.vtx[cnd.lastConfirmed].second.notarizationHeight;
1162             }
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)
1168             {
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))
1174                 {
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())
1179                     {
1180                         currencyState.supply = curDef.initialFractionalSupply;
1181                     }
1182                     else
1183                     {
1184                         // supply is determined by purchases * current conversion rate
1185                         currencyState.supply = currencyState.initialSupply;
1186                     }
1187
1188                     for (auto &transfer : unspentTransfers)
1189                     {
1190                         if (transfer.second.second.flags & CReserveTransfer::PRECONVERT)
1191                         {
1192                             CAmount conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1193
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())
1197                             {
1198                                 currencyState.reserves[currencyIndexes[transfer.second.second.currencyID]] += transfer.second.second.nValue - conversionFee;
1199                             }
1200                             else
1201                             {
1202                                 currencyState.supply += CCurrencyState::ReserveToNativeRaw(transfer.second.second.nValue - conversionFee, currencyState.PriceInReserve(currencyIndexes[transfer.second.second.currencyID]));
1203                             }
1204
1205                             if (transfer.second.second.currencyID == curDef.systemID)
1206                             {
1207                                 currencyState.nativeConversionFees += conversionFee;
1208                                 currencyState.nativeFees += conversionFee + transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
1209                             }
1210                             else
1211                             {
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;
1215                             }
1216                         }
1217                         else if (transfer.second.second.flags & CReserveTransfer::PREALLOCATE)
1218                         {
1219                             currencyState.emitted += transfer.second.second.nValue;
1220                         }
1221                     }
1222                     currencyState.supply += currencyState.emitted;
1223                     if (curDef.conversions.size() != curDef.currencies.size())
1224                     {
1225                         curDef.conversions = std::vector<int64_t>(curDef.currencies.size());
1226                     }
1227                     for (int i = 0; i < curDef.conversions.size(); i++)
1228                     {
1229                         currencyState.conversionPrice[i] = curDef.conversions[i] = currencyState.PriceInReserve(i);
1230                     }
1231                 }
1232             }
1233             else
1234             {
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;
1237             }
1238         }
1239     }
1240     return currencyState;
1241 }
1242
1243 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(const uint160 &currencyID, int32_t height)
1244 {
1245     int32_t curDefHeight;
1246     CCurrencyDefinition curDef;
1247     if (GetCurrencyDefinition(currencyID, curDef, &curDefHeight, true))
1248     {
1249         return GetCurrencyState(curDef, height, curDefHeight);
1250     }
1251     else
1252     {
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());
1255     }
1256     return CCoinbaseCurrencyState();
1257 }
1258
1259 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
1260 {
1261     return GetCurrencyState(thisChain.GetID(), height);
1262 }
1263
1264 bool CConnectedChains::SetLatestMiningOutputs(const std::vector<pair<int, CScript>> &minerOutputs, CTxDestination &firstDestinationOut)
1265 {
1266     LOCK(cs_mergemining);
1267
1268     if (!minerOutputs.size() || !ExtractDestination(minerOutputs[0].second, firstDestinationOut))
1269     {
1270         return false;
1271     }
1272     latestMiningOutputs = minerOutputs;
1273     latestDestination = firstDestinationOut;
1274     return true;
1275 }
1276
1277 CCurrencyDefinition CConnectedChains::GetCachedCurrency(const uint160 &currencyID)
1278 {
1279     CCurrencyDefinition currencyDef;
1280     int32_t defHeight;
1281     auto it = currencyDefCache.find(currencyID);
1282     if ((it != currencyDefCache.end() && !(currencyDef = it->second).IsValid()) ||
1283         (it == currencyDefCache.end() && !GetCurrencyDefinition(currencyID, currencyDef, &defHeight, true)))
1284     {
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());
1287         return currencyDef;
1288     }
1289     if (it == currencyDefCache.end())
1290     {
1291         currencyDefCache[currencyID] = currencyDef;
1292     }
1293     return currencyDefCache[currencyID];
1294 }
1295
1296 CCurrencyDefinition CConnectedChains::UpdateCachedCurrency(const uint160 &currencyID, uint32_t height)
1297 {
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;
1305     return currencyDef;
1306 }
1307
1308 void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
1309 {
1310     // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
1311     {
1312         if (!nHeight)
1313         {
1314             return;
1315         }
1316
1317         std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
1318
1319         LOCK(cs_main);
1320
1321         uint160 thisChainID = ConnectedChains.ThisChain().GetID();
1322
1323         // get all available transfer outputs to aggregate into export transactions
1324         if (GetUnspentChainTransfers(transferOutputs))
1325         {
1326             if (!transferOutputs.size())
1327             {
1328                 return;
1329             }
1330
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;
1336
1337             CCoins coins;
1338             CCoinsView dummy;
1339             CCoinsViewCache view(&dummy);
1340
1341             LOCK(mempool.cs);
1342
1343             CCoinsViewMemPool viewMemPool(pcoinsTip, mempool);
1344             view.SetBackend(viewMemPool);
1345
1346             for (auto &output : transferOutputs)
1347             {
1348                 CCurrencyDefinition sourceDef, destDef, systemDef;
1349
1350                 if (output.first != bookEnd)
1351                 {
1352                     if (!output.second.second.IsValid())
1353                     {
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());
1356                         continue;
1357                     }
1358
1359                     sourceDef = GetCachedCurrency(output.second.second.currencyID);
1360                     destDef = GetCachedCurrency(output.second.second.destCurrencyID);
1361                     systemDef = GetCachedCurrency(destDef.systemID);
1362
1363                     if (!sourceDef.IsValid())
1364                     {
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());
1367                         continue;
1368                     }
1369                     if (!destDef.IsValid())
1370                     {
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());
1373                         continue;
1374                     }
1375                     if (!systemDef.IsValid())
1376                     {
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());
1379                         continue;
1380                     }
1381
1382                     // if destination is a token on the current chain, consider it its own system
1383                     if (destDef.systemID == thisChainID)
1384                     {
1385                         systemDef = destDef;
1386                     }
1387                 }
1388
1389                 // get chain target and see if it is the same
1390                 if (lastChain == bookEnd || output.first == lastChain)
1391                 {
1392                     txInputs.push_back(output.second);
1393                 }
1394                 else
1395                 {
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
1398                     // system
1399                     std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1400                     lastChainDef = UpdateCachedCurrency(lastChain, nHeight);
1401
1402                     if (GetUnspentChainExports(lastChain, exportOutputs) && exportOutputs.size())
1403                     {
1404                         std::pair<uint160, std::pair<int, CInputDescriptor>> lastExport = *exportOutputs.begin();
1405
1406                         bool oneFullSize = txInputs.size() >= CCrossChainExport::MIN_INPUTS;
1407
1408                         if (((nHeight - lastExport.second.first) >= CCrossChainExport::MIN_BLOCKS) || oneFullSize)
1409                         {
1410                             boost::optional<CTransaction> oneExport;
1411
1412                             // make one or more transactions that spends the last export and all possible cross chain transfers
1413                             while (txInputs.size())
1414                             {
1415                                 TransactionBuilder tb(Params().GetConsensus(), nHeight);
1416
1417                                 int inputsLeft = txInputs.size();
1418                                 int numInputs = inputsLeft > CCrossChainExport::MAX_EXPORT_INPUTS ? CCrossChainExport::MAX_EXPORT_INPUTS : inputsLeft;
1419
1420                                 if (numInputs > CCrossChainExport::MAX_EXPORT_INPUTS)
1421                                 {
1422                                     numInputs = CCrossChainExport::MAX_EXPORT_INPUTS;
1423                                 }
1424                                 inputsLeft = txInputs.size() - numInputs;
1425
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)
1429                                 {
1430                                     break;
1431                                 }
1432
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
1438                                 //
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.
1441                                 //
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
1445                                 // 
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
1449                                 // Verus chain.
1450                                 //
1451                                 CCurrencyValueMap totalTxFees;
1452                                 CCurrencyValueMap totalAmounts;
1453                                 CAmount exportOutVal = 0;
1454                                 std::vector<CBaseChainObject *> chainObjects;
1455
1456                                 // first, we must add the export output from the current export thread to this chain
1457                                 if (oneExport.is_initialized())
1458                                 {
1459                                     // spend the last export transaction output
1460                                     CTransaction &tx = oneExport.get();
1461                                     COptCCParams p;
1462                                     int j;
1463                                     for (j = 0; j < tx.vout.size(); j++)
1464                                     {
1465                                         if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) && 
1466                                             p.IsValid() &&
1467                                             p.evalCode == EVAL_CROSSCHAIN_EXPORT)
1468                                         {
1469                                             break;
1470                                         }
1471                                     }
1472
1473                                     // had to be found and valid if we made the tx
1474                                     assert(j < tx.vout.size() && p.IsValid());
1475
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));
1480                                 }
1481                                 else
1482                                 {
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;
1486                                     uint256 blkHash;
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))
1490                                     {
1491                                         break;
1492                                     }
1493                                     oneExport = lastExportTx;
1494                                 }
1495
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))
1502                                 {
1503                                     break;
1504                                 }
1505                                 CTransaction &lastExportTx = oneExport.get();
1506
1507                                 for (int i = 0; i < coins.vout.size(); i++)
1508                                 {
1509                                     // import on same chain spends reserve deposits as well, so only spend them if they are not
1510                                     // already spent
1511                                     auto &oneOut = lastExportTx.vout[i];
1512                                     COptCCParams p;
1513
1514                                     if (::IsPayToCryptoCondition(oneOut.scriptPubKey, p) &&
1515                                         p.IsValid() &&
1516                                         p.evalCode == EVAL_RESERVE_DEPOSIT &&
1517                                         coins.IsAvailable(i))
1518                                     {
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())
1523                                         {
1524                                             tb.AddTransparentInput(COutPoint(lastExport.second.second.txIn.prevout.hash, i), 
1525                                                                 oneOut.scriptPubKey, oneOut.nValue);
1526                                             currentReserveDeposits.valueMap[ASSETCHAINS_CHAINID] += oneOut.nValue;
1527                                         }
1528                                     }
1529                                 }
1530
1531                                 COptCCParams p;
1532                                 std::vector<int> toRemove;
1533
1534                                 for (int j = 0; j < numInputs; j++)
1535                                 {
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;
1539
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));
1545
1546                                     if (isMint)
1547                                     {
1548                                         newTransferOutput.valueMap[txInputs[j].second.currencyID] = txInputs[j].second.nFees;
1549                                     }
1550                                     else
1551                                     {
1552                                         newTransferOutput.valueMap[txInputs[j].second.currencyID] = txInputs[j].second.nValue + txInputs[j].second.nFees;
1553                                     }
1554
1555                                     //printf("input:\n%s\n", newTransferInput.ToUniValue().write().c_str());
1556                                     //printf("output:\n%s\n", newTransferOutput.ToUniValue().write().c_str());
1557
1558                                     if ((newTransferInput - newTransferOutput).HasNegative())
1559                                     {
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);
1572                                     }
1573                                     else
1574                                     {
1575                                         CAmount valueOut = isMint ? 0 : txInputs[j].second.nValue;
1576
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));
1580                                     }
1581                                 }
1582
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--)
1585                                 {
1586                                     txInputs.erase(txInputs.begin() + toRemove[j]);
1587                                     numInputs--;
1588                                 }
1589
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))
1592                                 {
1593                                     continue;
1594                                 }
1595
1596                                 //printf("%s: total export amounts:\n%s\n", __func__, totalAmounts.ToUniValue().write().c_str());
1597                                 CCrossChainExport ccx(lastChain, numInputs, totalAmounts.CanonicalMap(), totalTxFees.CanonicalMap());
1598
1599                                 // make extra outputs for fees in each currency
1600                                 for (auto &outPair : ccx.CalculateExportFee().CanonicalMap().valueMap)
1601                                 {
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));
1605                                 }
1606
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,
1614                                                     cnd))
1615                                 {
1616                                     printf("%s: failed to create valid exports\n", __func__);
1617                                     LogPrintf("%s: failed to create valid exports\n", __func__);
1618                                 }
1619                                 else
1620                                 {
1621                                     currencyState = cnd.vtx[cnd.lastConfirmed].second.currencyState;
1622                                 }
1623                                 if (!currencyState.IsValid() ||
1624                                     !rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain().GetID(), lastChainDef, currencyState, chainObjects, vOutputs))
1625                                 {
1626                                     vOutputs = std::vector<CTxOut>();
1627                                     rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain().GetID(), lastChainDef, currencyState, chainObjects, vOutputs);
1628                                     DeleteOpRetObjects(chainObjects);
1629
1630                                     printf("%s: failed to create valid exports\n", __func__);
1631                                     LogPrintf("%s: failed to create valid exports\n", __func__);
1632
1633                                     // debugging output
1634                                     printf("%s: failed to export outputs:\n", __func__);
1635                                     for (auto oneout : vOutputs)
1636                                     {
1637                                         UniValue uniOut;
1638                                         ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1639                                         printf("%s\n", uniOut.write(true, 2).c_str());
1640                                     }
1641                                 }
1642                                 else
1643                                 {
1644                                     CCcontract_info CC;
1645                                     CCcontract_info *cp;
1646
1647                                     /*
1648                                     // debugging out
1649                                     printf("%s: exported outputs:\n", __func__);
1650                                     for (auto &oneout : chainObjects)
1651                                     {
1652                                         if (oneout->objectType == CHAINOBJ_RESERVETRANSFER)
1653                                         {
1654                                             CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)(oneout))->object;
1655                                             printf("%s\n", rt.ToUniValue().write(true, 2).c_str());
1656                                         }
1657                                     }
1658                                     */
1659
1660                                     CScript opRet = StoreOpRetArray(chainObjects);
1661                                     DeleteOpRetObjects(chainObjects);
1662
1663                                     // now send transferred currencies to a reserve deposit
1664                                     cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
1665
1666                                     currentReserveDeposits += ccx.totalAmounts;
1667                                     CCurrencyValueMap reserveDepositOut;
1668                                     CAmount nativeOut = 0;
1669
1670                                     for (auto &oneCurrencyOut : currentReserveDeposits.valueMap)
1671                                     {
1672                                         CCurrencyDefinition oneDef = currencyDefCache[oneCurrencyOut.first];
1673
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)
1678                                         {
1679                                             if (oneCurrencyOut.first == ASSETCHAINS_CHAINID)
1680                                             {
1681                                                 nativeOut = oneCurrencyOut.second;
1682                                             }
1683                                             else
1684                                             {
1685                                                 reserveDepositOut.valueMap[oneCurrencyOut.first] = oneCurrencyOut.second;
1686                                             }
1687                                         }
1688                                     }
1689                                     if (reserveDepositOut.valueMap.size())
1690                                     {
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)), 
1697                                                                 nativeOut);
1698                                     }
1699
1700                                     cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
1701
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()});
1704
1705                                     tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx)),
1706                                                             exportOutVal);
1707
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 
1711                                     // acknowledged
1712                                     cp = CCinit(&CC, EVAL_FINALIZE_EXPORT);
1713                                     CTransactionFinalization finalization(CTransactionFinalization::FINALIZE_EXPORT, lastChain, 0);
1714
1715                                     //printf("%s: Finalizing export with index dest %s\n", __func__, EncodeDestination(CKeyID(CCrossChainRPCData::GetConditionID(lastChainDef.systemID, EVAL_FINALIZE_EXPORT))).c_str());
1716
1717                                     dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
1718                                     tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CTransactionFinalization>(EVAL_FINALIZE_EXPORT, dests, 1, &finalization)), 0);
1719
1720                                     tb.AddOpRet(opRet);
1721                                     tb.SetFee(0);
1722
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());
1726
1727                                     TransactionBuilderResult buildResult(tb.Build());
1728
1729                                     if (!buildResult.IsError() && buildResult.IsTx())
1730                                     {
1731                                         // replace the last one only if we have a valid new one
1732                                         CTransaction tx = buildResult.GetTxOrThrow();
1733
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());
1737
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);
1743
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))
1748                                         {
1749                                             oneExport = tx;
1750                                             uint256 hash = tx.GetHash();
1751                                             CAmount nativeExportFees = ccx.totalFees.valueMap[ASSETCHAINS_CHAINID];
1752                                             mempool.PrioritiseTransaction(hash, hash.GetHex(), (double)(nativeExportFees << 1), nativeExportFees);
1753                                         }
1754                                         else
1755                                         {
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());
1760                                             break;
1761                                         }
1762                                     }
1763                                     else
1764                                     {
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());
1768                                         break;
1769                                     }
1770                                 }
1771
1772                                 // erase the inputs we've attempted to spend
1773                                 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1774                             }
1775                         }
1776                     }
1777                     txInputs.clear();
1778                 }
1779                 lastChain = output.first;
1780             }
1781             CheckImports();
1782         }
1783     }
1784 }
1785
1786 void CConnectedChains::SignAndCommitImportTransactions(const CTransaction &lastImportTx, const std::vector<CTransaction> &transactions)
1787 {
1788     int nHeight = chainActive.LastTip()->GetHeight();
1789     uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());
1790     LOCK2(cs_main, mempool.cs);
1791
1792     uint256 lastHash, lastSignedHash;
1793     CCoinsViewCache view(pcoinsTip);
1794
1795     // sign and commit the transactions
1796     for (auto &_tx : transactions)
1797     {
1798         CMutableTransaction newTx(_tx);
1799
1800         if (!lastHash.IsNull())
1801         {
1802             //printf("last hash before signing: %s\n", lastHash.GetHex().c_str());
1803             for (auto &oneIn : newTx.vin)
1804             {
1805                 //printf("checking input with hash: %s\n", oneIn.prevout.hash.GetHex().c_str());
1806                 if (oneIn.prevout.hash == lastHash)
1807                 {
1808                     oneIn.prevout.hash = lastSignedHash;
1809                     //printf("updated hash before signing: %s\n", lastSignedHash.GetHex().c_str());
1810                 }
1811             }
1812         }
1813         lastHash = _tx.GetHash();
1814         CTransaction tx = newTx;
1815
1816         // sign the transaction and submit
1817         bool signSuccess = false;
1818         for (int i = 0; i < tx.vin.size(); i++)
1819         {
1820             SignatureData sigdata;
1821             CAmount value;
1822             CScript outputScript;
1823
1824             if (tx.vin[i].prevout.hash == lastImportTx.GetHash())
1825             {
1826                 value = lastImportTx.vout[tx.vin[i].prevout.n].nValue;
1827                 outputScript = lastImportTx.vout[tx.vin[i].prevout.n].scriptPubKey;
1828             }
1829             else
1830             {
1831                 CCoins coins;
1832                 if (!view.GetCoins(tx.vin[i].prevout.hash, coins))
1833                 {
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);
1836                     break;
1837                 }
1838                 value = coins.vout[tx.vin[i].prevout.n].nValue;
1839                 outputScript = coins.vout[tx.vin[i].prevout.n].scriptPubKey;
1840             }
1841
1842             signSuccess = ProduceSignature(TransactionSignatureCreator(nullptr, &tx, i, value, SIGHASH_ALL), outputScript, sigdata, consensusBranchId);
1843
1844             if (!signSuccess)
1845             {
1846                 fprintf(stderr,"%s: failure to sign transaction\n", __func__);
1847                 LogPrintf("%s: failure to sign transaction\n", __func__);
1848                 break;
1849             } else {
1850                 UpdateTransaction(newTx, i, sigdata);
1851             }
1852         }
1853
1854         if (signSuccess)
1855         {
1856             // push to local node and sync with wallets
1857             CValidationState state;
1858             bool fMissingInputs;
1859             CTransaction signedTx(newTx);
1860
1861             //DEBUGGING
1862             //TxToJSON(tx, uint256(), jsonTX);
1863             //printf("signed transaction:\n%s\n", jsonTX.write(1, 2).c_str());
1864
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());
1871                 } else {
1872                     if (fMissingInputs) {
1873                         fprintf(stderr,"%s: missing inputs\n", __func__);
1874                         LogPrintf("%s: missing inputs\n", __func__);
1875                     }
1876                     else
1877                     {
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());
1880                     }
1881                 }
1882                 break;
1883             }
1884             else
1885             {
1886                 UpdateCoins(signedTx, view, nHeight);
1887                 lastSignedHash = signedTx.GetHash();
1888             }
1889         }
1890         else
1891         {
1892             break;
1893         }
1894     }
1895 }
1896
1897 CCurrencyValueMap CalculatePreconversions(const CCurrencyDefinition &chainDef, int32_t definitionHeight, CCurrencyValueMap &fees)
1898 {
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())
1903     {
1904         std::multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> transferInputs;
1905         CCurrencyValueMap preconvertedAmounts;
1906
1907         if (GetChainTransfers(transferInputs, chainDef.GetID(), definitionHeight, chainDef.startBlock - 1, CReserveTransfer::PRECONVERT | CReserveTransfer::VALID))
1908         {
1909             auto curMap = chainDef.GetCurrenciesMap();
1910             for (auto &transfer : transferInputs)
1911             {
1912                 if (!(transfer.second.second.flags & CReserveTransfer::PREALLOCATE) && curMap.count(transfer.second.second.currencyID))
1913                 {
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;
1917                 }
1918             }
1919             retVal = preconvertedAmounts;
1920             if (!chainDef.IsToken() && !(chainDef.ChainOptions() & chainDef.OPTION_FEESASRESERVE))
1921             {
1922                 retVal += fees;
1923             }
1924         }
1925     }
1926     else
1927     {
1928         retVal = CCurrencyValueMap(chainDef.currencies, chainDef.preconverted);
1929     }
1930     return retVal;
1931 }
1932
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, 
1936                                              uint32_t height, 
1937                                              const CTransaction &lastImportTx, 
1938                                              uint32_t exportHeight, 
1939                                              const CTransaction &exportTx, 
1940                                              CMutableTransaction &mnewTx,
1941                                              CCoinbaseCurrencyState &newCurState)
1942 {
1943     if (!_curDef.IsValid() || !_curDef.IsToken())
1944     {
1945         LogPrintf("%s: cannot create import notarization for invalid or non-token currencies\n", __func__);
1946         return false;
1947     }
1948
1949     uint160 currencyID = _curDef.GetID();
1950
1951     CCurrencyDefinition curDef;
1952     int32_t curDefHeight;
1953     if (!GetCurrencyDefinition(currencyID, curDef, &curDefHeight))
1954     {
1955         LogPrintf("%s: cannot create import notarization - currency not found\n", __func__);
1956         return false;
1957     }
1958
1959     CPBaaSNotarization lastNotarization(lastImportTx);
1960     if (curDef.systemID == ASSETCHAINS_CHAINID && !lastNotarization.IsValid())
1961     {
1962         LogPrintf("%s: error getting notarization transaction %s\n", __func__, lastImportTx.GetHash().GetHex().c_str());
1963         return false;
1964     }
1965
1966     CCoinbaseCurrencyState initialCurrencyState = lastNotarization.currencyState;
1967
1968     bool isDefinition = false;
1969
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)
1973     {
1974         initialCurrencyState = ConnectedChains.GetCurrencyState(curDef, curDef.startBlock - 1, curDefHeight);
1975         isDefinition = true;
1976         if (height >= curDef.startBlock)
1977         {
1978             initialCurrencyState.SetPrelaunch(false);
1979         }
1980     }
1981
1982     CCrossChainExport ccx(exportTx);
1983     if (!ccx.IsValid())
1984     {
1985         LogPrintf("%s: invalid export transaction %s\n", __func__, lastImportTx.GetHash().GetHex().c_str());
1986         return false;
1987     }
1988
1989     if (!lastNotarization.IsValid())
1990     {
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)
1996         {
1997             lastNotarization = cnd.vtx[cnd.lastConfirmed].second;
1998             initialCurrencyState = lastNotarization.currencyState;
1999         }
2000         else
2001         {
2002             LogPrintf("%s: cannot get last notarization for %s\n", __func__, curDef.name.c_str());
2003             return false;
2004         }
2005     }
2006
2007     CBlockIndex *pindex;
2008     CTxDestination notarizationID = VERUS_DEFAULTID.IsNull() ? CTxDestination(CIdentityID(currencyID)) : CTxDestination(VERUS_DEFAULTID);
2009
2010     // if this is the first notarization after start, make the notarization and determine if we should
2011     // launch or refund
2012     if (isDefinition || height == curDef.startBlock -1)
2013     {
2014         bool refunding = false;
2015
2016         pindex = chainActive[curDef.startBlock];
2017
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;
2022
2023         if (curDef.minPreconvert.size() && curDef.minPreconvert.size() == curDef.currencies.size())
2024         {
2025             minPreMap = CCurrencyValueMap(curDef.currencies, curDef.minPreconvert).CanonicalMap();
2026         }
2027
2028         if (minPreMap.valueMap.size() && preConvertedMap < minPreMap)
2029         {
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);
2036             refunding = true;
2037         }
2038         else if (curDef.IsFractional() &&
2039                  exportTx.vout.size() &&
2040                  exportTx.vout.back().scriptPubKey.IsOpReturn())
2041         {
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;
2048
2049             exportObjects = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey);
2050
2051             bool isValidExport = rtxd.AddReserveTransferImportOutputs(currencyID, curDef, initialCurrencyState, exportObjects, vOutputs, &newCurState);
2052             if (isValidExport)
2053             {
2054                 // on definition, initial state is correct
2055                 newCurState = initialCurrencyState;
2056             }
2057             DeleteOpRetObjects(exportObjects);
2058             if (!isValidExport)
2059             {
2060                 LogPrintf("%s: invalid export opreturn for transaction %s\n", __func__, exportTx.GetHash().GetHex().c_str());
2061                 return false;
2062             }
2063         }
2064     }
2065     else
2066     {
2067         if (chainActive.Height() > height)
2068         {
2069             pindex = chainActive[height];
2070         }
2071         else
2072         {
2073             pindex = chainActive.LastTip();
2074         }
2075         
2076         if (!pindex)
2077         {
2078             LogPrintf("%s: invalid active chain\n", __func__);
2079             return false;
2080         }
2081
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;
2084
2085         newCurState = initialCurrencyState;
2086
2087         if (curDef.minPreconvert.size() && curDef.minPreconvert.size() == curDef.currencies.size())
2088         {
2089             minPreMap = CCurrencyValueMap(curDef.currencies, curDef.minPreconvert).CanonicalMap();
2090         }
2091
2092         // we won't change currency state in notarizations after failure to launch, if success, recalculate as needed
2093         if (!(lastNotarization.currencyState.IsRefunding()))
2094         {
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;
2102
2103             exportObjects = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey);
2104
2105             bool isValidExport = rtxd.AddReserveTransferImportOutputs(currencyID, curDef, initialCurrencyState, exportObjects, vOutputs, &newCurState);
2106             if (isValidExport && curDef.IsFractional())
2107             {
2108                 // we want the new price and the old state as a starting point to ensure no rounding error impact
2109                 // on reserves
2110                 CCoinbaseCurrencyState tempCurState = initialCurrencyState;
2111                 tempCurState.conversionPrice = newCurState.conversionPrice;
2112                 vOutputs.resize(0);
2113                 rtxd = CReserveTransactionDescriptor();
2114                 isValidExport = rtxd.AddReserveTransferImportOutputs(currencyID, curDef, tempCurState, exportObjects, vOutputs, &newCurState);
2115             }
2116             else if (!curDef.IsFractional())
2117             {
2118                 newCurState = initialCurrencyState;
2119             }
2120             DeleteOpRetObjects(exportObjects);
2121             if (!isValidExport)
2122             {
2123                 LogPrintf("%s: invalid export opreturn for transaction %s\n", __func__, exportTx.GetHash().GetHex().c_str());
2124                 return false;
2125             }
2126         }
2127     }
2128
2129     uint256 lastImportTxHash = lastImportTx.GetHash();
2130
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,
2134                                                 currencyID,
2135                                                 notarizationID,
2136                                                 pindex->GetHeight(),
2137                                                 chainActive.GetMMV().GetRoot(),
2138                                                 chainActive.GetMMRNode(pindex->GetHeight()).hash,
2139                                                 ArithToUint256(GetCompactPower(pindex->nNonce, pindex->nBits, pindex->nVersion)),
2140                                                 newCurState,
2141                                                 lastImportTxHash,
2142                                                 lastNotarization.notarizationHeight,
2143                                                 uint256(), 0, COpRetProof(), std::vector<CNodeData>());
2144
2145     // create notarization output
2146     CCcontract_info CC;
2147     CCcontract_info *cp;
2148
2149     std::vector<CTxDestination> dests;
2150
2151     // make the accepted notarization output
2152     cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
2153
2154     if (curDef.notarizationProtocol == curDef.NOTARIZATION_NOTARY_CHAINID)
2155     {
2156         dests = std::vector<CTxDestination>({CIdentityID(currencyID)});
2157     }
2158     else if (curDef.notarizationProtocol == curDef.NOTARIZATION_AUTO)
2159     {
2160         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2161     }
2162     else
2163     {
2164         return false;
2165     }
2166     mnewTx.vout.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &pbn))));
2167
2168     // make the finalization output
2169     cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
2170
2171     if (curDef.notarizationProtocol == curDef.NOTARIZATION_AUTO)
2172     {
2173         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2174     }
2175
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))
2180     {
2181         printf("ERROR: could not find expected initial notarization for currency %s\n", curDef.name.c_str());
2182         return false;
2183     }
2184
2185     mnewTx.vin.push_back(CTxIn(COutPoint(lastImportTxHash, confirmedOut)));
2186     mnewTx.vin.push_back(CTxIn(COutPoint(lastImportTxHash, finalizeOut)));
2187
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);
2190
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))));
2194
2195     return true;
2196 }
2197
2198 // process token related, local imports and exports
2199 void CConnectedChains::ProcessLocalImports()
2200 {
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();
2205
2206     LOCK2(cs_main, mempool.cs);
2207     uint32_t nHeight = chainActive.Height();
2208
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;
2213
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))
2216     {
2217         CCrossChainExport ccx, ccxDummy;
2218         CTransaction txOut, txImport;
2219         CPartialTransactionProof lastExport;
2220         CCrossChainImport cci;
2221         uint256 blkHash;
2222         for (auto &oneOut : unspentOutputs)
2223         {
2224             COptCCParams p;
2225             if (oneOut.second.blockHeight <= nHeight &&
2226                 oneOut.second.script.IsPayToCryptoCondition(p) &&
2227                 p.IsValid() &&
2228                 p.evalCode == EVAL_FINALIZE_EXPORT &&
2229                 p.vData.size() &&
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))
2237             {
2238                 auto blockIt = mapBlockIndex.find(blkHash);
2239                 if (blockIt != mapBlockIndex.end() && chainActive.Contains(blockIt->second))
2240                 {
2241                     currenciesToImport.insert(make_pair(ccx.systemID, make_pair(blockIt->second->GetHeight(), txImport)));
2242                 }
2243             }
2244         }
2245     }
2246
2247     CMutableTransaction txTemplate = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight);
2248     for (auto &oneIT : currenciesToImport)
2249     {
2250         std::vector<CTransaction> importTxes;
2251         int32_t importOutNum = 0;
2252         CCrossChainImport oneImportInput(oneIT.second.second, &importOutNum);
2253         if (oneImportInput.IsValid())
2254         {
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)
2260             {
2261                 nativeImportAvailable += oneOut.second.satoshis;
2262                 tokenImportAvailable += oneOut.second.script.ReserveOutValue();
2263                 //printf("nativeImportAvailable:%ld, tokenImportAvailable:%s\n", nativeImportAvailable, tokenImportAvailable.ToUniValue().write().c_str());
2264             }
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))
2269             {
2270                 // fund the first import transaction with all reserveDeposits
2271                 // change amounts are passed through on the import thread
2272                 //
2273                 // TODO: manage when this becomes to large by splitting reserve deposits over the
2274                 // transactions
2275                 if (importTxes.size())
2276                 {
2277                     CMutableTransaction oneImport = importTxes[0];
2278                     int32_t outNum = 0;
2279                     CCrossChainImport cci(importTxes[0], &outNum);
2280                     if (cci.IsValid())
2281                     {
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())
2286                         {
2287                             UniValue jsonTX(UniValue::VOBJ);
2288                             std::vector<uint256> prevHashes;
2289
2290                             for (int i = 0; i < importTxes.size() - 1; i++)
2291                             {
2292                                 prevHashes.push_back(importTxes[i].GetHash());
2293                             }
2294
2295                             // TODO - get reserve deposits from exports, not all at once, as in refunds
2296                             for (auto &oneOut : reserveDeposits)
2297                             {
2298                                 oneImport.vin.push_back(CTxIn(oneOut.first.txhash, oneOut.first.index));
2299                             }
2300
2301                             importTxes[0] = oneImport;
2302
2303                             for (int i = 0; i < importTxes.size() - 1; i++)
2304                             {
2305                                 oneImport = importTxes[i + 1];
2306                                 for (auto &oneIn : oneImport.vin)
2307                                 {
2308                                     if (oneIn.prevout.hash == prevHashes[i])
2309                                     {
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();
2312                                     }
2313                                 }
2314                                 importTxes[i + 1] = oneImport;
2315                             }
2316
2317                         }
2318                         SignAndCommitImportTransactions(oneIT.second.second, importTxes);
2319                     }
2320                 }
2321             }
2322         }
2323     }
2324 }
2325
2326 void CConnectedChains::SubmissionThread()
2327 {
2328     try
2329     {
2330         arith_uint256 lastHash;
2331         int64_t lastImportTime = 0;
2332         uint32_t lastHeight = 0;
2333         
2334         // wait for something to check on, then submit blocks that should be submitted
2335         while (true)
2336         {
2337             boost::this_thread::interruption_point();
2338
2339             if (IsVerusActive())
2340             {
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;
2345                 {
2346                     LOCK(cs_mergemining);
2347                     if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
2348                     {
2349                         qualifiedHeaders.clear();
2350                     }
2351                     submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
2352
2353                     //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
2354                 }
2355                 if (submit)
2356                 {
2357                     //printf("SubmissionThread: calling submit qualified blocks\n");
2358                     SubmitQualifiedBlocks();
2359                 }
2360
2361                 ProcessLocalImports();
2362
2363                 if (!submit)
2364                 {
2365                     sem_submitthread.wait();
2366                 }
2367             }
2368             else
2369             {
2370                 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
2371                 if (CheckVerusPBaaSAvailable())
2372                 {
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)
2376                     {
2377                         CBlock blk;
2378                         int32_t txIndex = -1, height;
2379                         {
2380                             LOCK(cs_mergemining);
2381                             if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
2382                             {
2383                                 blk = earnedNotarizationBlock;
2384                                 earnedNotarizationBlock = CBlock();
2385                                 txIndex = earnedNotarizationIndex;
2386                                 height = earnedNotarizationHeight;
2387                                 earnedNotarizationHeight = 0;
2388                             }
2389                         }
2390
2391                         if (txIndex != -1)
2392                         {
2393                             //printf("SubmissionThread: testing notarization\n");
2394                             CTransaction lastConfirmed;
2395                             uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
2396
2397                             if (!txId.IsNull())
2398                             {
2399                                 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
2400                                 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
2401                             }
2402                         }
2403                     }
2404
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()))
2407                     {
2408                         lastImportTime = GetAdjustedTime();
2409                         lastHeight = (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight());
2410
2411                         // see if our notary has a confirmed notarization for us
2412                         UniValue params(UniValue::VARR);
2413                         UniValue result;
2414
2415                         params.push_back(thisChain.name);
2416
2417                         try
2418                         {
2419                             result = find_value(RPCCallRoot("getlastimportin", params), "result");
2420                         } catch (exception e)
2421                         {
2422                             result = NullUniValue;
2423                         }
2424
2425                         if (!result.isNull())
2426                         {
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"));
2432
2433                             CTransaction lastImportTx, lastConfirmedTx, templateTx;
2434
2435                             if (txUniStr.isStr() && txTemplateStr.isStr() && 
2436                                 DecodeHexTx(lastImportTx, txUniStr.get_str()) && 
2437                                 DecodeHexTx(lastConfirmedTx, txLastConfirmedStr.get_str()) && 
2438                                 DecodeHexTx(templateTx, txTemplateStr.get_str()))
2439                             {
2440                                 std::vector<CTransaction> importTxes;
2441                                 if (CreateLatestImports(notaryChain.chainDefinition, lastImportTx, templateTx, lastConfirmedTx, tokenImportAvailable, nativeImportAvailable, importTxes))
2442                                 {
2443                                     for (auto importTx : importTxes)
2444                                     {
2445                                         UniValue txResult;
2446                                         params.setArray();
2447                                         params.push_back(EncodeHexTx(importTx));
2448
2449                                         try
2450                                         {
2451                                             txResult = find_value(RPCCallRoot("signrawtransaction", params), "result");
2452                                             if (txResult.isObject() && !(txResult = find_value(txResult, "hex")).isNull() && txResult.isStr() && txResult.get_str().size())
2453                                             {
2454                                                 params.setArray();
2455                                                 params.push_back(txResult);
2456                                                 txResult = find_value(RPCCallRoot("sendrawtransaction", params), "result");
2457                                             }
2458                                             else
2459                                             {
2460                                                 txResult = NullUniValue;
2461                                             }
2462                                             
2463                                         } catch (exception e)
2464                                         {
2465                                             txResult = NullUniValue;
2466                                         }
2467                                         uint256 testId;
2468                                         if (txResult.isStr())
2469                                         {
2470                                             testId.SetHex(txResult.get_str());
2471                                         }
2472                                         if (testId.IsNull())
2473                                         {
2474                                             break;
2475                                         }
2476                                     }
2477                                 }
2478                             }
2479                         }
2480                     }
2481                 }
2482                 sleep(3);
2483             }
2484             boost::this_thread::interruption_point();
2485         }
2486     }
2487     catch (const boost::thread_interrupted&)
2488     {
2489         LogPrintf("Verus merge mining thread terminated\n");
2490     }
2491 }
2492
2493 void CConnectedChains::SubmissionThreadStub()
2494 {
2495     ConnectedChains.SubmissionThread();
2496 }
2497
2498 void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
2499 {
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
2503     // kept
2504     LOCK(cs_mergemining);
2505
2506     // we only care about the last
2507     earnedNotarizationHeight = height;
2508     earnedNotarizationBlock = blk;
2509     earnedNotarizationIndex = txIndex;
2510 }
2511
2512 bool IsChainDefinitionInput(const CScript &scriptSig)
2513 {
2514     uint32_t ecode;
2515     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_CURRENCY_DEFINITION;
2516 }
2517
This page took 0.167774 seconds and 4 git commands to generate.