]> Git Repo - VerusCoin.git/blob - src/pbaas/pbaas.cpp
Cleanup fees
[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 CAmount CCrossChainExport::CalculateExportFeeRaw(CAmount fee, int numIn)
578 {
579     if (numIn > MAX_EXPORT_INPUTS)
580     {
581         numIn = MAX_EXPORT_INPUTS;
582     }
583     static const arith_uint256 satoshis(100000000);
584
585     arith_uint256 ratio(50000000 + ((25000000 / MAX_EXPORT_INPUTS) * (numIn - 1)));
586
587     return (((arith_uint256(fee) * ratio)) / satoshis).GetLow64();
588 }
589
590 CAmount CCrossChainExport::ExportReward(int64_t exportFee)
591 {
592     int64_t individualExportFee = ((arith_uint256(exportFee) * 10000000) / SATOSHIDEN).GetLow64();
593     if (individualExportFee < MIN_FEES_BEFORE_FEEPOOL)
594     {
595         individualExportFee = exportFee > MIN_FEES_BEFORE_FEEPOOL ? MIN_FEES_BEFORE_FEEPOOL : exportFee;
596     }
597     return individualExportFee;
598 }
599
600 CCurrencyValueMap CCrossChainExport::CalculateExportFee() const
601 {
602     return CalculateExportFee(totalFees, numInputs);
603 }
604
605 CCurrencyValueMap CCrossChainExport::CalculateImportFee() const
606 {
607     CCurrencyValueMap retVal;
608
609     for (auto &feePair : CalculateExportFee().valueMap)
610     {
611         CAmount feeAmount = feePair.second;
612         auto it = totalFees.valueMap.find(feePair.first);
613         retVal.valueMap[feePair.first] = (it != totalFees.valueMap.end() ? it->second : 0) - feeAmount;
614     }
615     return retVal;
616 }
617
618 bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
619 {
620     bool retval = false;
621     LOCK(cs_mergemining);
622
623     //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
624
625     auto chainIt = mergeMinedChains.find(chainID);
626     if (chainIt != mergeMinedChains.end())
627     {
628         arith_uint256 target;
629         target.SetCompact(chainIt->second.block.nBits);
630         for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
631         {
632             // make sure we don't just match by target
633             if (removeRange.first->second->GetID() == chainID)
634             {
635                 mergeMinedTargets.erase(removeRange.first);
636                 break;
637             }
638         }
639         mergeMinedChains.erase(chainID);
640         dirty = retval = true;
641
642         // if we get to 0, give the thread a kick to stop waiting for mining
643         //if (!mergeMinedChains.size())
644         //{
645         //    sem_submitthread.post();
646         //}
647     }
648     return retval;
649 }
650
651 // remove merge mined chains added and not updated since a specific time
652 void CConnectedChains::PruneOldChains(uint32_t pruneBefore)
653 {
654     vector<uint160> toRemove;
655
656     LOCK(cs_mergemining);
657     for (auto blkData : mergeMinedChains)
658     {
659         if (blkData.second.block.nTime < pruneBefore)
660         {
661             toRemove.push_back(blkData.first);
662         }
663     }
664
665     for (auto id : toRemove)
666     {
667         //printf("Pruning chainID: %s\n", id.GetHex().c_str());
668         RemoveMergedBlock(id);
669     }
670 }
671
672 // adds or updates merge mined blocks
673 // returns false if failed to add
674 bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
675 {
676     // determine if we should replace one or add to the merge mine vector
677     {
678         LOCK(cs_mergemining);
679
680         arith_uint256 target;
681         uint160 cID = blkData.GetID();
682         auto it = mergeMinedChains.find(cID);
683         if (it != mergeMinedChains.end())
684         {
685             RemoveMergedBlock(cID);             // remove it if already there
686         }
687         target.SetCompact(blkData.block.nBits);
688
689         //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
690
691         mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
692         dirty = true;
693     }
694     return true;
695 }
696
697 bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
698 {
699     {
700         LOCK(cs_mergemining);
701         auto chainIt = mergeMinedChains.find(chainID);
702         if (chainIt != mergeMinedChains.end())
703         {
704             rpcChainData = (CRPCChainData)chainIt->second;
705             return true;
706         }
707         return false;
708     }
709 }
710
711 // this returns a pointer to the data without copy and assumes the lock is held
712 CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
713 {
714     {
715         auto chainIt = mergeMinedChains.find(chainID);
716         if (chainIt != mergeMinedChains.end())
717         {
718             return &chainIt->second;
719         }
720         return NULL;
721     }
722 }
723
724 void CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
725 {
726     //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
727     {
728         LOCK(cs_mergemining);
729
730         qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
731     }
732     sem_submitthread.post();
733 }
734
735 void CConnectedChains::CheckImports()
736 {
737     sem_submitthread.post();
738 }
739
740 // get the latest block header and submit one block at a time, returning after there are no more
741 // matching blocks to be found
742 vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
743 {
744     std::set<uint160> inHeader;
745     bool submissionFound;
746     CPBaaSMergeMinedChainData chainData;
747     vector<pair<string, UniValue>>  results;
748
749     CBlockHeader bh;
750     arith_uint256 lastHash;
751     CPBaaSBlockHeader pbh;
752
753     do
754     {
755         submissionFound = false;
756         {
757             LOCK(cs_mergemining);
758             // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
759             // common, merge mined headers for notarization, drop out on any submission
760             for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
761             {
762                 // add the PBaaS chain ids from this header to a set for search
763                 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
764                 {
765                     inHeader.insert(pbh.chainID);
766                 }
767
768                 uint160 chainID;
769                 // now look through all targets that are equal to or above the hash of this header
770                 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
771                 {
772                     chainID = chainIt->second->GetID();
773                     if (inHeader.count(chainID))
774                     {
775                         // first, check that the winning header matches the block that is there
776                         CPBaaSPreHeader preHeader(chainIt->second->block);
777                         preHeader.SetBlockData(headerIt->second);
778
779                         // check if the block header matches the block's specific data, only then can we create a submission from this block
780                         if (headerIt->second.CheckNonCanonicalData(chainID))
781                         {
782                             // save block as is, remove the block from merged headers, replace header, and submit
783                             chainData = *chainIt->second;
784
785                             *(CBlockHeader *)&chainData.block = headerIt->second;
786
787                             submissionFound = true;
788                         }
789                         //else // not an error condition. code is here for debugging
790                         //{
791                         //    printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
792                         //}
793                     }
794                     //else // not an error condition. code is here for debugging
795                     //{
796                     //    printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
797                     //}
798                 }
799
800                 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
801                 if (submissionFound)
802                 {
803                     // once it is going to be submitted, remove block from this chain until a new one is added again
804                     RemoveMergedBlock(chainID);
805                     break;
806                 }
807                 else
808                 {
809                     qualifiedHeaders.erase(headerIt);
810                 }
811             }
812         }
813         if (submissionFound)
814         {
815             // submit one block and loop again. this approach allows multiple threads
816             // to collectively empty the submission queue, mitigating the impact of
817             // any one stalled daemon
818             UniValue submitParams(UniValue::VARR);
819             submitParams.push_back(EncodeHexBlk(chainData.block));
820             UniValue result, error;
821             try
822             {
823                 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
824                 result = find_value(result, "result");
825                 error = find_value(result, "error");
826             }
827             catch (exception e)
828             {
829                 result = UniValue(e.what());
830             }
831             results.push_back(make_pair(chainData.chainDefinition.name, result));
832             if (result.isStr() || !error.isNull())
833             {
834                 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());
835             }
836             else
837             {
838                 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
839             }
840         }
841     } while (submissionFound);
842     return results;
843 }
844
845 // add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
846 uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
847 {
848     vector<uint160> inHeader;
849     vector<UniValue> toCombine;
850     arith_uint256 blkHash = UintToArith256(bh.GetHash());
851     arith_uint256 target(0);
852     
853     CPBaaSBlockHeader pbh;
854
855     {
856         LOCK(cs_mergemining);
857
858         CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
859
860         for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
861         {
862             if (bh.GetPBaaSHeader(pbh, i))
863             {
864                 inHeader.push_back(pbh.chainID);
865             }
866         }
867
868         // loop through the existing PBaaS chain ids in the header
869         // remove any that are not either this Chain ID or in our local collection and then add all that are present
870         for (uint32_t i = 0; i < inHeader.size(); i++)
871         {
872             auto it = mergeMinedChains.find(inHeader[i]);
873             if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
874             {
875                 bh.DeletePBaaSHeader(i);
876             }
877         }
878
879         for (auto chain : mergeMinedChains)
880         {
881             // get the native PBaaS header for each chain and put it into the
882             // header we are given
883             // it must have itself in as a PBaaS header
884             uint160 cid = chain.second.GetID();
885             if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
886             {
887                 if (!bh.AddUpdatePBaaSHeader(pbh))
888                 {
889                     LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
890                     break;
891                 }
892                 else
893                 {
894                     arith_uint256 t;
895                     t.SetCompact(chain.second.block.nBits);
896                     if (t > target)
897                     {
898                         target = t;
899                     }
900                 }
901             }
902             else
903             {
904                 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
905             }
906         }
907         dirty = false;
908     }
909
910     return target.GetCompact();
911 }
912
913 bool CConnectedChains::IsVerusPBaaSAvailable()
914 {
915     return notaryChainVersion >= "0.8.0";
916 }
917
918 extern string PBAAS_HOST, PBAAS_USERPASS;
919 extern int32_t PBAAS_PORT;
920 bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
921 {
922     if (chainInfoUni.isObject() && chainDefUni.isObject())
923     {
924         UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
925         if (uniVer.isStr())
926         {
927             LOCK(cs_mergemining);
928             notaryChainVersion = uni_get_str(uniVer);
929             notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
930             CCurrencyDefinition chainDef(chainDefUni);
931             notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
932         }
933     }
934     return IsVerusPBaaSAvailable();
935 }
936
937 uint32_t CConnectedChains::NotaryChainHeight()
938 {
939     LOCK(cs_mergemining);
940     return notaryChainHeight;
941 }
942
943 bool CConnectedChains::CheckVerusPBaaSAvailable()
944 {
945     if (IsVerusActive())
946     {
947         notaryChainVersion = "";
948     }
949     else
950     {
951         // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
952         // tolerate only 15 second timeout
953         UniValue chainInfo, chainDef;
954         try
955         {
956             UniValue params(UniValue::VARR);
957             chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
958             if (!chainInfo.isNull())
959             {
960                 params.push_back(VERUS_CHAINNAME);
961                 chainDef = find_value(RPCCallRoot("getcurrency", params), "result");
962
963                 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
964                 {
965                     // if we have not past block 1 yet, store the best known update of our current state
966                     if ((!chainActive.LastTip() || !chainActive.LastTip()->GetHeight()))
967                     {
968                         bool success = false;
969                         params.clear();
970                         params.push_back(EncodeDestination(CIdentityID(thisChain.GetID())));
971                         chainDef = find_value(RPCCallRoot("getcurrency", params), "result");
972                         if (!chainDef.isNull())
973                         {
974                             CCurrencyDefinition currencyDef(chainDef);
975                             if (currencyDef.IsValid())
976                             {
977                                 thisChain = currencyDef;
978                                 if (NotaryChainHeight() >= thisChain.startBlock)
979                                 {
980                                     readyToStart = true;    // this only gates mining of block one, to be sure we have the latest definition
981                                 }
982                                 success = true;
983                             }
984                         }
985                         return success;
986                     }
987                     return true;
988                 }
989             }
990         } catch (exception e)
991         {
992             LogPrintf("%s: Error communicating with %s chain\n", __func__, VERUS_CHAINNAME);
993         }
994     }
995     notaryChainVersion = "";
996     return false;
997 }
998
999 int CConnectedChains::GetThisChainPort() const
1000 {
1001     int port;
1002     string host;
1003     for (auto node : defaultPeerNodes)
1004     {
1005         SplitHostPort(node.networkAddress, port, host);
1006         if (port)
1007         {
1008             return port;
1009         }
1010     }
1011     return 0;
1012 }
1013
1014 CCoinbaseCurrencyState CConnectedChains::AddPrelaunchConversions(CCurrencyDefinition &curDef,
1015                                                                  const CCoinbaseCurrencyState &_currencyState,
1016                                                                  int32_t fromHeight,
1017                                                                  int32_t height,
1018                                                                  int32_t curDefHeight)
1019 {
1020     if (curDef.name == "PA-PD-PC-Tri")
1021     {
1022         printf("%s: currency PA-PD-PC-Tri starting\n", __func__);
1023     }
1024
1025     CCoinbaseCurrencyState currencyState = _currencyState;
1026     bool firstUpdate = fromHeight <= curDefHeight;
1027     if (firstUpdate)
1028     {
1029         if (curDef.IsFractional())
1030         {
1031             currencyState.supply = curDef.initialFractionalSupply;
1032             currencyState.reserves = std::vector<int64_t>(currencyState.reserves.size(), 0);
1033             currencyState.weights = curDef.weights;
1034         }
1035         else
1036         {
1037             // supply is determined by purchases * current conversion rate
1038             currencyState.supply = currencyState.initialSupply;
1039         }
1040     }
1041
1042     // get chain transfers that should apply before the start block
1043     // until there is a post-start block notarization, we always consider the
1044     // currency state to be up to just before the start block
1045     std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> unspentTransfers;
1046     std::map<uint160, int32_t> currencyIndexes = currencyState.GetReserveMap();
1047     int32_t nativeIdx = currencyIndexes.count(curDef.systemID) ? currencyIndexes[curDef.systemID] : -1;
1048
1049     if (GetChainTransfers(unspentTransfers, curDef.GetID(), fromHeight, height < curDef.startBlock ? height : curDef.startBlock - 1))
1050     {
1051         currencyState.ClearForNextBlock();
1052
1053         for (auto &transfer : unspentTransfers)
1054         {
1055             if (transfer.second.second.IsPreConversion())
1056             {
1057                 CAmount conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1058                 CAmount valueIn = transfer.second.second.nValue - conversionFee;
1059                 int32_t curIdx = currencyIndexes[transfer.second.second.currencyID];
1060
1061                 currencyState.reserveIn[curIdx] += valueIn;
1062                 curDef.preconverted[curIdx] += valueIn;
1063                 if (curDef.IsFractional())
1064                 {
1065                     currencyState.reserves[curIdx] += valueIn;
1066                 }
1067                 else
1068                 {
1069                     currencyState.supply += CCurrencyState::ReserveToNativeRaw(valueIn, currencyState.PriceInReserve(curIdx));
1070                 }
1071
1072                 if (transfer.second.second.currencyID == curDef.systemID)
1073                 {
1074                     currencyState.nativeConversionFees += conversionFee;
1075                     currencyState.nativeFees += conversionFee;
1076                 }
1077                 currencyState.fees[curIdx] += conversionFee;
1078                 currencyState.nativeFees +=  transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
1079                 currencyState.fees[nativeIdx] +=  transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
1080                 currencyState.conversionFees[curIdx] += conversionFee;
1081             }
1082         }
1083     }
1084
1085     if (curDef.IsFractional())
1086     {
1087         // convert all non-native fees to native and update the price as a result
1088         bool isFeeConversion = false;
1089         int numCurrencies = curDef.currencies.size();
1090         std::vector<int64_t> reservesToConvert(numCurrencies, 0);
1091         std::vector<int64_t> fractionalToConvert(numCurrencies, 0);
1092         CAmount newSupply =  currencyState.supply;
1093
1094         for (int i = 0; i < numCurrencies; i++)
1095         {
1096             curDef.conversions[i] = currencyState.conversionPrice[i] = currencyState.PriceInReserve(i, true);
1097
1098             // all currencies except the native currency of the system will be converted to the native currency
1099             if (currencyState.fees[i] && curDef.currencies[i] != curDef.systemID)
1100             {
1101                 fractionalToConvert[nativeIdx] += currencyState.ReserveToNativeRaw(currencyState.fees[i], currencyState.conversionPrice[i]);
1102                 newSupply += fractionalToConvert[i];
1103                 isFeeConversion = true;
1104             }
1105         }
1106         currencyState.supply = newSupply;
1107
1108         // convert all non-native fee currencies to native and adjust prices
1109         if (isFeeConversion)
1110         {
1111             CCurrencyState converterState = static_cast<CCurrencyState>(currencyState);
1112             currencyState.viaConversionPrice = 
1113                 converterState.ConvertAmounts(reservesToConvert, fractionalToConvert, currencyState);
1114         }
1115     }
1116
1117     // set initial supply from actual conversions if this is first update
1118     if (firstUpdate && curDef.IsFractional())
1119     {
1120         CAmount calculatedSupply = 0;
1121         for (auto &transfer : unspentTransfers)
1122         {
1123             if (transfer.second.second.IsPreConversion())
1124             {
1125                 CAmount toConvert = transfer.second.second.nValue - CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1126                 calculatedSupply += CCurrencyState::ReserveToNativeRaw(toConvert, currencyState.conversionPrice[currencyIndexes[transfer.second.second.currencyID]]);
1127             }
1128         }
1129
1130         if (calculatedSupply > curDef.initialFractionalSupply)
1131         {            
1132             // get a ratio and reduce all prices by that ratio
1133             static arith_uint256 bigSatoshi(SATOSHIDEN);
1134             arith_uint256 bigMultipliedSupply(calculatedSupply * bigSatoshi);
1135             arith_uint256 newRatio(bigMultipliedSupply / curDef.initialFractionalSupply);
1136             // truncate up, not down, to prevent any overflow at all
1137             if (newRatio * curDef.initialFractionalSupply < bigMultipliedSupply)
1138             {
1139                 newRatio++;
1140             }
1141             for (auto &rate : currencyState.conversionPrice)
1142             {
1143                 arith_uint256 numerator = rate * newRatio;
1144                 rate = (numerator / bigSatoshi).GetLow64();
1145                 if ((numerator - (bigSatoshi * rate)) > 0)
1146                 {
1147                     rate++;
1148                 }
1149             }
1150         }
1151
1152         calculatedSupply = 0;
1153         for (auto &transfer : unspentTransfers)
1154         {
1155             if (transfer.second.second.IsPreConversion())
1156             {
1157                 CAmount toConvert = transfer.second.second.nValue - CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1158                 calculatedSupply += CCurrencyState::ReserveToNativeRaw(toConvert, currencyState.conversionPrice[currencyIndexes[transfer.second.second.currencyID]]);
1159             }
1160         }
1161         printf("Calculated supply %s\n", ValueFromAmount(calculatedSupply).write().c_str());
1162
1163         // now, remove carveout percentage from each weight & reserve
1164         // for currency state
1165         int32_t preLaunchCarveOutTotal = 0;
1166         for (auto &carveout : curDef.preLaunchCarveOuts)
1167         {
1168             preLaunchCarveOutTotal += carveout.second;
1169             
1170         }
1171         static arith_uint256 bigSatoshi(SATOSHIDEN);
1172         for (auto &oneReserve : currencyState.reserves)
1173         {
1174             oneReserve =  ((arith_uint256(oneReserve) * arith_uint256(SATOSHIDEN - preLaunchCarveOutTotal)) / bigSatoshi).GetLow64();
1175         }
1176         for (auto &oneWeight : currencyState.weights)
1177         {
1178             oneWeight = ((arith_uint256(oneWeight) * arith_uint256(CCurrencyDefinition::CalculateRatioOfValue((SATOSHIDEN - preLaunchCarveOutTotal), SATOSHIDEN - curDef.preLaunchDiscount))) / bigSatoshi).GetLow64();
1179         }
1180     }
1181
1182     currencyState.UpdateWithEmission(curDef.GetTotalPreallocation());
1183
1184     return currencyState;
1185 }
1186
1187 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(CCurrencyDefinition &curDef, int32_t height, int32_t curDefHeight)
1188 {
1189     uint160 chainID = curDef.GetID();
1190     CCoinbaseCurrencyState currencyState;
1191     std::vector<CAddressIndexDbEntry> notarizationIndex;
1192
1193     if (chainID == ASSETCHAINS_CHAINID)
1194     {
1195         CBlock block;
1196         if (IsVerusActive() ||
1197             CConstVerusSolutionVector::activationHeight.ActiveVersion(height) < CActivationHeight::ACTIVATE_PBAAS ||
1198             height == 0 ||
1199             height > chainActive.Height() ||
1200             !chainActive[height] ||
1201             !ReadBlockFromDisk(block, chainActive[height], Params().GetConsensus()) ||
1202             !(currencyState = CCoinbaseCurrencyState(block.vtx[0])).IsValid())
1203         {
1204             currencyState = GetInitialCurrencyState(thisChain);
1205         }
1206     }
1207     // if this is a token on this chain, it will be simply notarized
1208     else if (curDef.systemID == ASSETCHAINS_CHAINID)
1209     {
1210         // get the last unspent notarization for this currency, which is valid by definition for a token
1211         CPBaaSNotarization notarization;
1212         if ((notarization.GetLastNotarization(chainID, EVAL_ACCEPTEDNOTARIZATION, curDefHeight, height) &&
1213              (currencyState = notarization.currencyState).IsValid()) ||
1214             (currencyState = GetInitialCurrencyState(curDef)).IsValid())
1215         {
1216             if (!(notarization.IsValid() && notarization.notarizationHeight >= curDef.startBlock))
1217             {
1218                 // pre-launch
1219                 currencyState.SetPrelaunch(true);
1220                 currencyState = AddPrelaunchConversions(curDef, 
1221                                                         currencyState, 
1222                                                         notarization.IsValid() ? notarization.notarizationHeight : curDefHeight, 
1223                                                         height, 
1224                                                         curDefHeight);
1225             }
1226             else
1227             {
1228                 currencyState.SetPrelaunch(false);
1229             }
1230         }
1231     }
1232     else
1233     {
1234         CChainNotarizationData cnd;
1235         uint32_t ecode = IsVerusActive() ? 
1236                          EVAL_ACCEPTEDNOTARIZATION : 
1237                          (chainID == notaryChain.GetID() ? EVAL_EARNEDNOTARIZATION : EVAL_ACCEPTEDNOTARIZATION);
1238         if (GetNotarizationData(chainID, ecode, cnd))
1239         {
1240             int32_t transfersFrom = curDefHeight;
1241             if (cnd.lastConfirmed != -1)
1242             {
1243                 transfersFrom = cnd.vtx[cnd.lastConfirmed].second.notarizationHeight;
1244             }
1245             int32_t transfersUntil = cnd.lastConfirmed == -1 ? curDef.startBlock - 1 :
1246                                        (cnd.vtx[cnd.lastConfirmed].second.notarizationHeight < curDef.startBlock ?
1247                                         (height < curDef.startBlock ? height : curDef.startBlock - 1) :
1248                                         cnd.vtx[cnd.lastConfirmed].second.notarizationHeight);
1249             if (transfersUntil < curDef.startBlock)
1250             {
1251                 // get chain transfers that should apply before the start block
1252                 // until there is a post-start block notarization, we always consider the
1253                 // currency state to be up to just before the start block
1254                 std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> unspentTransfers;
1255                 if (GetChainTransfers(unspentTransfers, chainID, transfersFrom, transfersUntil))
1256                 {
1257                     // at this point, all pre-allocation, minted, and pre-converted currency are included
1258                     // in the currency state before final notarization
1259                     std::map<uint160, int32_t> currencyIndexes = currencyState.GetReserveMap();
1260                     if (curDef.IsFractional())
1261                     {
1262                         currencyState.supply = curDef.initialFractionalSupply;
1263                     }
1264                     else
1265                     {
1266                         // supply is determined by purchases * current conversion rate
1267                         currencyState.supply = currencyState.initialSupply;
1268                     }
1269
1270                     for (auto &transfer : unspentTransfers)
1271                     {
1272                         if (transfer.second.second.IsPreConversion())
1273                         {
1274                             CAmount conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1275
1276                             currencyState.reserveIn[currencyIndexes[transfer.second.second.currencyID]] += transfer.second.second.nValue;
1277                             curDef.preconverted[currencyIndexes[transfer.second.second.currencyID]] += transfer.second.second.nValue;
1278                             if (curDef.IsFractional())
1279                             {
1280                                 currencyState.reserves[currencyIndexes[transfer.second.second.currencyID]] += transfer.second.second.nValue - conversionFee;
1281                             }
1282                             else
1283                             {
1284                                 currencyState.supply += CCurrencyState::ReserveToNativeRaw(transfer.second.second.nValue - conversionFee, currencyState.PriceInReserve(currencyIndexes[transfer.second.second.currencyID]));
1285                             }
1286
1287                             if (transfer.second.second.currencyID == curDef.systemID)
1288                             {
1289                                 currencyState.nativeConversionFees += conversionFee;
1290                                 currencyState.nativeFees += conversionFee + transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
1291                             }
1292                             else
1293                             {
1294                                 currencyState.fees[currencyIndexes[transfer.second.second.currencyID]] += 
1295                                             conversionFee + transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
1296                                 currencyState.conversionFees[currencyIndexes[transfer.second.second.currencyID]] += conversionFee;
1297                             }
1298                         }
1299                         else if (transfer.second.second.flags & CReserveTransfer::PREALLOCATE)
1300                         {
1301                             currencyState.emitted += transfer.second.second.nValue;
1302                         }
1303                     }
1304                     currencyState.supply += currencyState.emitted;
1305                     if (curDef.conversions.size() != curDef.currencies.size())
1306                     {
1307                         curDef.conversions = std::vector<int64_t>(curDef.currencies.size());
1308                     }
1309                     for (int i = 0; i < curDef.conversions.size(); i++)
1310                     {
1311                         currencyState.conversionPrice[i] = curDef.conversions[i] = currencyState.PriceInReserve(i);
1312                     }
1313                 }
1314             }
1315             else
1316             {
1317                 std::pair<uint256, CPBaaSNotarization> notPair = cnd.lastConfirmed != -1 ? cnd.vtx[cnd.lastConfirmed] : cnd.vtx[cnd.forks[cnd.bestChain][0]];
1318                 currencyState = notPair.second.currencyState;
1319             }
1320         }
1321     }
1322     return currencyState;
1323 }
1324
1325 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(const uint160 &currencyID, int32_t height)
1326 {
1327     int32_t curDefHeight;
1328     CCurrencyDefinition curDef;
1329     if (GetCurrencyDefinition(currencyID, curDef, &curDefHeight, true))
1330     {
1331         return GetCurrencyState(curDef, height, curDefHeight);
1332     }
1333     else
1334     {
1335         LogPrintf("%s: currency %s:%s not found\n", __func__, currencyID.GetHex().c_str(), EncodeDestination(CIdentityID(currencyID)).c_str());
1336         printf("%s: currency %s:%s not found\n", __func__, currencyID.GetHex().c_str(), EncodeDestination(CIdentityID(currencyID)).c_str());
1337     }
1338     return CCoinbaseCurrencyState();
1339 }
1340
1341 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
1342 {
1343     return GetCurrencyState(thisChain.GetID(), height);
1344 }
1345
1346 bool CConnectedChains::SetLatestMiningOutputs(const std::vector<CTxOut> &minerOutputs)
1347 {
1348     LOCK(cs_mergemining);
1349     latestMiningOutputs = minerOutputs;
1350     return true;
1351 }
1352
1353 CCurrencyDefinition CConnectedChains::GetCachedCurrency(const uint160 &currencyID)
1354 {
1355     CCurrencyDefinition currencyDef;
1356     int32_t defHeight;
1357     auto it = currencyDefCache.find(currencyID);
1358     if ((it != currencyDefCache.end() && !(currencyDef = it->second).IsValid()) ||
1359         (it == currencyDefCache.end() && !GetCurrencyDefinition(currencyID, currencyDef, &defHeight, true)))
1360     {
1361         printf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(currencyID)).c_str());
1362         LogPrintf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(currencyID)).c_str());
1363         return currencyDef;
1364     }
1365     if (it == currencyDefCache.end())
1366     {
1367         currencyDefCache[currencyID] = currencyDef;
1368     }
1369     return currencyDefCache[currencyID];
1370 }
1371
1372 CCurrencyDefinition CConnectedChains::UpdateCachedCurrency(const uint160 &currencyID, uint32_t height)
1373 {
1374     // due to the main lock being taken on the thread that waits for transaction checks,
1375     // low level functions like this must be called either from a thread that holds LOCK(cs_main),
1376     // or script validation, where it is held either by this thread or one waiting for it.
1377     // in the long run, the daemon synchonrization model should be improved
1378     CCurrencyDefinition currencyDef = GetCachedCurrency(currencyID);
1379     CCoinbaseCurrencyState curState = GetCurrencyState(currencyDef, height);
1380     currencyDefCache[currencyID] = currencyDef;
1381     return currencyDef;
1382 }
1383
1384 void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
1385 {
1386     // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
1387     {
1388         if (!nHeight)
1389         {
1390             return;
1391         }
1392
1393         std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
1394
1395         LOCK(cs_main);
1396
1397         uint160 thisChainID = ConnectedChains.ThisChain().GetID();
1398
1399         // get all available transfer outputs to aggregate into export transactions
1400         if (GetUnspentChainTransfers(transferOutputs))
1401         {
1402             if (!transferOutputs.size())
1403             {
1404                 return;
1405             }
1406
1407             std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
1408             uint160 lastChain = transferOutputs.begin()->first;
1409
1410             CCoins coins;
1411             CCoinsView dummy;
1412             CCoinsViewCache view(&dummy);
1413
1414             LOCK(mempool.cs);
1415
1416             CCoinsViewMemPool viewMemPool(pcoinsTip, mempool);
1417             view.SetBackend(viewMemPool);
1418
1419             auto outputIt = transferOutputs.begin();
1420             for (int outputsDone = 0; outputsDone <= transferOutputs.size(); outputIt++, outputsDone++)
1421             {
1422                 if (outputIt != transferOutputs.end())
1423                 {
1424                     auto &output = *outputIt;
1425                     if (output.first == lastChain)
1426                     {
1427                         txInputs.push_back(output.second);
1428                         continue;
1429                     }
1430                 }
1431
1432                 CCurrencyDefinition destDef, systemDef;
1433
1434                 destDef = GetCachedCurrency(lastChain);
1435                 systemDef = GetCachedCurrency(destDef.systemID);
1436
1437                 if (!destDef.IsValid())
1438                 {
1439                     printf("%s: cannot find destination currency %s\n", __func__, EncodeDestination(CIdentityID(lastChain)).c_str());
1440                     LogPrintf("%s: cannot find destination currency %s\n", __func__, EncodeDestination(CIdentityID(lastChain)).c_str());
1441                     continue;
1442                 }
1443                 if (!systemDef.IsValid())
1444                 {
1445                     printf("%s: cannot find destination system definition %s\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
1446                     LogPrintf("%s: cannot find destination system definition %s\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
1447                     continue;
1448                 }
1449
1450                 // if destination is a token on the current chain, consider it its own system
1451                 if (destDef.systemID == thisChainID)
1452                 {
1453                     systemDef = destDef;
1454                 }
1455
1456                 // when we get here, we have a consecutive number of transfer outputs to consume in txInputs
1457                 // we need an unspent export output to export, or use the last one of it is an export to the same
1458                 // system
1459                 std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1460                 CCurrencyDefinition lastChainDef = UpdateCachedCurrency(lastChain, nHeight);
1461
1462                 if (GetUnspentChainExports(lastChain, exportOutputs) && exportOutputs.size())
1463                 {
1464                     std::pair<uint160, std::pair<int, CInputDescriptor>> lastExport = *exportOutputs.begin();
1465
1466                     bool oneFullSize = txInputs.size() >= CCrossChainExport::MIN_INPUTS;
1467
1468                     if (((nHeight - lastExport.second.first) >= CCrossChainExport::MIN_BLOCKS) || oneFullSize)
1469                     {
1470                         boost::optional<CTransaction> oneExport;
1471
1472                         // make one or more transactions that spends the last export and all possible cross chain transfers
1473                         while (txInputs.size())
1474                         {
1475                             TransactionBuilder tb(Params().GetConsensus(), nHeight);
1476
1477                             int inputsLeft = txInputs.size();
1478                             int numInputs = inputsLeft > CCrossChainExport::MAX_EXPORT_INPUTS ? CCrossChainExport::MAX_EXPORT_INPUTS : inputsLeft;
1479
1480                             if (numInputs > CCrossChainExport::MAX_EXPORT_INPUTS)
1481                             {
1482                                 numInputs = CCrossChainExport::MAX_EXPORT_INPUTS;
1483                             }
1484                             inputsLeft = txInputs.size() - numInputs;
1485
1486                             // if we have already made one and don't have enough to make another
1487                             // without going under the input minimum, wait until next time for the others
1488                             if (numInputs > 0 && numInputs < CCrossChainExport::MIN_INPUTS && oneFullSize)
1489                             {
1490                                 break;
1491                             }
1492
1493                             // each time through, we make one export transaction with the remainder or a subset of the
1494                             // reserve transfer inputs. inputs can be:
1495                             // 1. transfers of reserve for fractional reserve chains
1496                             // 2. pre-conversions for pre-launch participation in the premine
1497                             // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
1498                             //
1499                             // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
1500                             // or reserve token inputs.
1501                             //
1502                             // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
1503                             // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
1504                             // as part of the import process
1505                             // 
1506                             // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain. 
1507                             // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the 
1508                             // remaining Verus reserve coin will be burned on the PBaaS chain as spending it is allowed, once notarized, on the
1509                             // Verus chain.
1510                             //
1511                             CCurrencyValueMap totalTxFees;
1512                             CCurrencyValueMap totalAmounts;
1513                             CAmount exportOutVal = 0;
1514                             std::vector<CBaseChainObject *> chainObjects;
1515
1516                             // first, we must add the export output from the current export thread to this chain
1517                             if (oneExport.is_initialized())
1518                             {
1519                                 // spend the last export transaction output
1520                                 CTransaction &tx = oneExport.get();
1521                                 COptCCParams p;
1522                                 int j;
1523                                 for (j = 0; j < tx.vout.size(); j++)
1524                                 {
1525                                     if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) && 
1526                                         p.IsValid() &&
1527                                         p.evalCode == EVAL_CROSSCHAIN_EXPORT)
1528                                     {
1529                                         break;
1530                                     }
1531                                 }
1532
1533                                 // had to be found and valid if we made the tx
1534                                 assert(j < tx.vout.size() && p.IsValid());
1535
1536                                 tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
1537                                 exportOutVal = tx.vout[j].nValue;
1538                                 lastExport.second.first = nHeight;
1539                                 lastExport.second.second = CInputDescriptor(tx.vout[j].scriptPubKey, tx.vout[j].nValue, CTxIn(tx.GetHash(), j));
1540                             }
1541                             else
1542                             {
1543                                 // spend the recentExportIt output
1544                                 tb.AddTransparentInput(lastExport.second.second.txIn.prevout, lastExport.second.second.scriptPubKey, lastExport.second.second.nValue);
1545                                 exportOutVal = lastExport.second.second.nValue;
1546                                 uint256 blkHash;
1547                                 CTransaction lastExportTx;
1548                                 // we must find the export, or it doesn't exist
1549                                 if (!myGetTransaction(lastExport.second.second.txIn.prevout.hash, lastExportTx, blkHash))
1550                                 {
1551                                     break;
1552                                 }
1553                                 oneExport = lastExportTx;
1554                             }
1555
1556                             // combine any reserve deposits from the last export
1557                             // into the reserve deposit outputs for this export and spend them
1558                             // TODO: allow reserve deposits to hold multiple reserve token types
1559                             // to make this more efficient for multi-reserves
1560                             CCurrencyValueMap currentReserveDeposits;
1561                             if (!view.GetCoins(lastExport.second.second.txIn.prevout.hash, coins))
1562                             {
1563                                 break;
1564                             }
1565                             CTransaction &lastExportTx = oneExport.get();
1566
1567                             for (int i = 0; i < coins.vout.size(); i++)
1568                             {
1569                                 // import on same chain spends reserve deposits as well, so only spend them if they are not
1570                                 // already spent
1571                                 auto &oneOut = lastExportTx.vout[i];
1572                                 COptCCParams p;
1573
1574                                 if (::IsPayToCryptoCondition(oneOut.scriptPubKey, p) &&
1575                                     p.IsValid() &&
1576                                     p.evalCode == EVAL_RESERVE_DEPOSIT &&
1577                                     coins.IsAvailable(i))
1578                                 {
1579                                     // only reserve deposits for the export currency/system will be present on an export transaction
1580                                     CCurrencyValueMap reserveOut = oneOut.scriptPubKey.ReserveOutValue().CanonicalMap();
1581                                     currentReserveDeposits += reserveOut;
1582                                     if (oneOut.nValue || reserveOut.valueMap.size())
1583                                     {
1584                                         tb.AddTransparentInput(COutPoint(lastExport.second.second.txIn.prevout.hash, i), 
1585                                                             oneOut.scriptPubKey, oneOut.nValue);
1586                                         currentReserveDeposits.valueMap[ASSETCHAINS_CHAINID] += oneOut.nValue;
1587                                     }
1588                                 }
1589                             }
1590
1591                             COptCCParams p;
1592                             std::vector<int> toRemove;
1593
1594                             for (int j = 0; j < numInputs; j++)
1595                             {
1596                                 tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
1597                                 CCurrencyValueMap newTransferInput = txInputs[j].first.scriptPubKey.ReserveOutValue();
1598                                 newTransferInput.valueMap[ASSETCHAINS_CHAINID] = txInputs[j].first.nValue;
1599
1600                                 totalTxFees += txInputs[j].second.CalculateFee(txInputs[j].second.flags, txInputs[j].second.nValue, lastChainDef.systemID);
1601                                 totalAmounts += newTransferInput;
1602                                 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(txInputs[j].second), txInputs[j].second));
1603                             }
1604
1605                             // remove in reverse order so one removal does not affect the position of the next
1606                             for (int j = toRemove.size() - 1; j >= 0; j--)
1607                             {
1608                                 txInputs.erase(txInputs.begin() + toRemove[j]);
1609                                 numInputs--;
1610                             }
1611
1612                             // this logic may cause us to create a tx that will get rejected, but we will never wait too long
1613                             if (!numInputs || (oneFullSize && (nHeight - lastExport.second.first) < CCrossChainExport::MIN_BLOCKS && numInputs < CCrossChainExport::MIN_INPUTS))
1614                             {
1615                                 continue;
1616                             }
1617
1618                             //printf("%s: total export amounts:\n%s\n", __func__, totalAmounts.ToUniValue().write().c_str());
1619                             CCrossChainExport ccx(lastChain, numInputs, totalAmounts.CanonicalMap(), totalTxFees.CanonicalMap());
1620
1621                             // make a fee output
1622                             CReserveTransfer feeOut(CReserveTransfer::VALID + CReserveTransfer::FEE_OUTPUT, 
1623                                                     lastChainDef.systemID, 0, 0, lastChainDef.systemID, DestinationToTransferDestination(feeOutput));
1624                             chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
1625
1626                             // do a preliminary check
1627                             CReserveTransactionDescriptor rtxd;
1628                             std::vector<CTxOut> vOutputs;
1629                             CChainNotarizationData cnd;
1630                             CCoinbaseCurrencyState currencyState;
1631                             if (!GetNotarizationData(lastChain,
1632                                                 !lastChainDef.IsToken() && lastChainDef.systemID == lastChain ? EVAL_EARNEDNOTARIZATION : EVAL_ACCEPTEDNOTARIZATION,
1633                                                 cnd))
1634                             {
1635                                 printf("%s: failed to create valid exports\n", __func__);
1636                                 LogPrintf("%s: failed to create valid exports\n", __func__);
1637                             }
1638                             else
1639                             {
1640                                 currencyState = cnd.vtx[cnd.lastConfirmed].second.currencyState;
1641                             }
1642                             if (!currencyState.IsValid() ||
1643                                 !rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain().GetID(), lastChainDef, currencyState, chainObjects, vOutputs))
1644                             {
1645                                 vOutputs = std::vector<CTxOut>();
1646                                 rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain().GetID(), lastChainDef, currencyState, chainObjects, vOutputs);
1647                                 DeleteOpRetObjects(chainObjects);
1648
1649                                 printf("%s: failed to create valid exports\n", __func__);
1650                                 LogPrintf("%s: failed to create valid exports\n", __func__);
1651
1652                                 // debugging output
1653                                 printf("%s: failed to export outputs:\n", __func__);
1654                                 for (auto oneout : vOutputs)
1655                                 {
1656                                     UniValue uniOut;
1657                                     ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1658                                     printf("%s\n", uniOut.write(true, 2).c_str());
1659                                 }
1660                             }
1661                             else
1662                             {
1663                                 CCcontract_info CC;
1664                                 CCcontract_info *cp;
1665
1666                                 /*
1667                                 // debugging out
1668                                 printf("%s: exported outputs:\n", __func__);
1669                                 for (auto &oneout : chainObjects)
1670                                 {
1671                                     if (oneout->objectType == CHAINOBJ_RESERVETRANSFER)
1672                                     {
1673                                         CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)(oneout))->object;
1674                                         printf("%s\n", rt.ToUniValue().write(true, 2).c_str());
1675                                     }
1676                                 }
1677                                 */
1678
1679                                 CScript opRet = StoreOpRetArray(chainObjects);
1680                                 DeleteOpRetObjects(chainObjects);
1681
1682                                 // now send transferred currencies to a reserve deposit
1683                                 cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
1684
1685                                 currentReserveDeposits += ccx.totalAmounts;
1686                                 CCurrencyValueMap reserveDepositOut;
1687                                 CAmount nativeOut = 0;
1688
1689                                 for (auto &oneCurrencyOut : currentReserveDeposits.valueMap)
1690                                 {
1691                                     CCurrencyDefinition oneDef = currencyDefCache[oneCurrencyOut.first];
1692
1693                                     // if the destination is this chain, or
1694                                     // the destination is another blockchain that does not control source currency, store in reserve
1695                                     if ((oneDef.systemID == ASSETCHAINS_CHAINID || oneDef.systemID != lastChainDef.systemID) &&
1696                                         oneCurrencyOut.second)
1697                                     {
1698                                         if (oneCurrencyOut.first == ASSETCHAINS_CHAINID)
1699                                         {
1700                                             nativeOut = oneCurrencyOut.second;
1701                                         }
1702                                         else
1703                                         {
1704                                             reserveDepositOut.valueMap[oneCurrencyOut.first] = oneCurrencyOut.second;
1705                                         }
1706                                     }
1707                                 }
1708
1709                                 if (reserveDepositOut.valueMap.size() || nativeOut)
1710                                 {
1711                                     // send the entire amount to a reserve deposit output of the specific chain
1712                                     // we receive our fee on the other chain, when it comes back, or if a token,
1713                                     // when it gets imported back to the chain
1714                                     std::vector<CTxDestination> dests({CPubKey(ParseHex(CC.CChexstr))});
1715                                     CReserveDeposit rd = CReserveDeposit(lastChain, reserveDepositOut);
1716                                     tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CReserveDeposit>(EVAL_RESERVE_DEPOSIT, dests, 1, &rd)), 
1717                                                             nativeOut);
1718                                 }
1719
1720                                 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
1721
1722                                 // send native amount of zero to a cross chain export output of the specific chain
1723                                 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
1724
1725                                 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx)),
1726                                                         exportOutVal);
1727
1728                                 // when exports are confirmed as having been imported, they are finalized
1729                                 // until then, a finalization UTXO enables an index search to only find transactions
1730                                 // that have work to complete on this chain, or have not had their cross-chain import 
1731                                 // acknowledged
1732                                 cp = CCinit(&CC, EVAL_FINALIZE_EXPORT);
1733                                 CTransactionFinalization finalization(CTransactionFinalization::FINALIZE_EXPORT, lastChain, 0);
1734
1735                                 //printf("%s: Finalizing export with index dest %s\n", __func__, EncodeDestination(CKeyID(CCrossChainRPCData::GetConditionID(lastChainDef.systemID, EVAL_FINALIZE_EXPORT))).c_str());
1736
1737                                 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
1738                                 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CTransactionFinalization>(EVAL_FINALIZE_EXPORT, dests, 1, &finalization)), 0);
1739
1740                                 tb.AddOpRet(opRet);
1741                                 tb.SetFee(0);
1742
1743                                 /* UniValue uni(UniValue::VOBJ);
1744                                 TxToUniv(tb.mtx, uint256(), uni);
1745                                 printf("%s: about to send reserve deposits with tx:\n%s\n", __func__, uni.write(1,2).c_str()); */
1746
1747                                 TransactionBuilderResult buildResult(tb.Build());
1748
1749                                 if (!buildResult.IsError() && buildResult.IsTx())
1750                                 {
1751                                     // replace the last one only if we have a valid new one
1752                                     CTransaction tx = buildResult.GetTxOrThrow();
1753
1754                                     /* uni = UniValue(UniValue::VOBJ);
1755                                     TxToUniv(tx, uint256(), uni);
1756                                     printf("%s: successfully built tx:\n%s\n", __func__, uni.write(1,2).c_str()); */
1757
1758                                     LOCK2(cs_main, mempool.cs);
1759                                     static int lastHeight = 0;
1760                                     // remove conflicts, so that we get in
1761                                     std::list<CTransaction> removed;
1762                                     mempool.removeConflicts(tx, removed);
1763
1764                                     // add to mem pool, prioritize according to the fee we will get, and relay
1765                                     //printf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1766                                     //LogPrintf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1767                                     if (myAddtomempool(tx))
1768                                     {
1769                                         oneExport = tx;
1770                                         uint256 hash = tx.GetHash();
1771                                         CAmount nativeExportFees = ccx.totalFees.valueMap[ASSETCHAINS_CHAINID];
1772                                         mempool.PrioritiseTransaction(hash, hash.GetHex(), (double)(nativeExportFees << 1), nativeExportFees);
1773                                     }
1774                                     else
1775                                     {
1776                                         UniValue uni(UniValue::VOBJ);
1777                                         TxToUniv(tx, uint256(), uni);
1778                                         //printf("%s: created invalid transaction:\n%s\n", __func__, uni.write(1,2).c_str());
1779                                         LogPrintf("%s: created invalid transaction:\n%s\n", __func__, uni.write(1,2).c_str());
1780                                         break;
1781                                     }
1782                                 }
1783                                 else
1784                                 {
1785                                     // we can't do any more useful work for this chain if we failed here
1786                                     printf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1787                                     LogPrintf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1788                                     break;
1789                                 }
1790                             }
1791
1792                             // erase the inputs we've attempted to spend
1793                             txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1794                         }
1795                     }
1796                 }
1797                 txInputs.clear();
1798                 if (outputIt != transferOutputs.end())
1799                 {
1800                     lastChain = outputIt->first;
1801                     txInputs.push_back(outputIt->second);
1802                 }
1803             }
1804             CheckImports();
1805         }
1806     }
1807 }
1808
1809 void CConnectedChains::SignAndCommitImportTransactions(const CTransaction &lastImportTx, const std::vector<CTransaction> &transactions)
1810 {
1811     int nHeight = chainActive.LastTip()->GetHeight();
1812     uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());
1813     LOCK2(cs_main, mempool.cs);
1814
1815     uint256 lastHash, lastSignedHash;
1816     CCoinsViewCache view(pcoinsTip);
1817
1818     // sign and commit the transactions
1819     for (auto &_tx : transactions)
1820     {
1821         CMutableTransaction newTx(_tx);
1822
1823         if (!lastHash.IsNull())
1824         {
1825             //printf("last hash before signing: %s\n", lastHash.GetHex().c_str());
1826             for (auto &oneIn : newTx.vin)
1827             {
1828                 //printf("checking input with hash: %s\n", oneIn.prevout.hash.GetHex().c_str());
1829                 if (oneIn.prevout.hash == lastHash)
1830                 {
1831                     oneIn.prevout.hash = lastSignedHash;
1832                     //printf("updated hash before signing: %s\n", lastSignedHash.GetHex().c_str());
1833                 }
1834             }
1835         }
1836         lastHash = _tx.GetHash();
1837         CTransaction tx = newTx;
1838
1839         // sign the transaction and submit
1840         bool signSuccess = false;
1841         for (int i = 0; i < tx.vin.size(); i++)
1842         {
1843             SignatureData sigdata;
1844             CAmount value;
1845             CScript outputScript;
1846
1847             if (tx.vin[i].prevout.hash == lastImportTx.GetHash())
1848             {
1849                 value = lastImportTx.vout[tx.vin[i].prevout.n].nValue;
1850                 outputScript = lastImportTx.vout[tx.vin[i].prevout.n].scriptPubKey;
1851             }
1852             else
1853             {
1854                 CCoins coins;
1855                 if (!view.GetCoins(tx.vin[i].prevout.hash, coins))
1856                 {
1857                     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);
1858                     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);
1859                     break;
1860                 }
1861                 value = coins.vout[tx.vin[i].prevout.n].nValue;
1862                 outputScript = coins.vout[tx.vin[i].prevout.n].scriptPubKey;
1863             }
1864
1865             signSuccess = ProduceSignature(TransactionSignatureCreator(nullptr, &tx, i, value, SIGHASH_ALL), outputScript, sigdata, consensusBranchId);
1866
1867             if (!signSuccess)
1868             {
1869                 fprintf(stderr,"%s: failure to sign transaction\n", __func__);
1870                 LogPrintf("%s: failure to sign transaction\n", __func__);
1871                 break;
1872             } else {
1873                 UpdateTransaction(newTx, i, sigdata);
1874             }
1875         }
1876
1877         if (signSuccess)
1878         {
1879             // push to local node and sync with wallets
1880             CValidationState state;
1881             bool fMissingInputs;
1882             CTransaction signedTx(newTx);
1883
1884             //DEBUGGING
1885             //TxToJSON(tx, uint256(), jsonTX);
1886             //printf("signed transaction:\n%s\n", jsonTX.write(1, 2).c_str());
1887
1888             if (!AcceptToMemoryPool(mempool, state, signedTx, false, &fMissingInputs)) {
1889                 if (state.IsInvalid()) {
1890                     //UniValue txUni(UniValue::VOBJ);
1891                     //TxToUniv(signedTx, uint256(), txUni);
1892                     //fprintf(stderr,"%s: rejected by memory pool for %s\n%s\n", __func__, state.GetRejectReason().c_str(), txUni.write(1,2).c_str());
1893                     LogPrintf("%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
1894                 } else {
1895                     if (fMissingInputs) {
1896                         fprintf(stderr,"%s: missing inputs\n", __func__);
1897                         LogPrintf("%s: missing inputs\n", __func__);
1898                     }
1899                     else
1900                     {
1901                         fprintf(stderr,"%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
1902                         LogPrintf("%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
1903                     }
1904                 }
1905                 break;
1906             }
1907             else
1908             {
1909                 UpdateCoins(signedTx, view, nHeight);
1910                 lastSignedHash = signedTx.GetHash();
1911             }
1912         }
1913         else
1914         {
1915             break;
1916         }
1917     }
1918 }
1919
1920 CCurrencyValueMap CalculatePreconversions(const CCurrencyDefinition &chainDef, int32_t definitionHeight, CCurrencyValueMap &fees)
1921 {
1922     // if we are getting information on the current chain, we assume that preconverted amounts have been
1923     // pre-calculated. otherwise, we will calculate them.
1924     CCurrencyValueMap retVal;
1925     if (chainDef.GetID() != ConnectedChains.ThisChain().GetID())
1926     {
1927         std::multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> transferInputs;
1928         CCurrencyValueMap preconvertedAmounts;
1929
1930         if (GetChainTransfers(transferInputs, chainDef.GetID(), definitionHeight, chainDef.startBlock - 1, CReserveTransfer::PRECONVERT | CReserveTransfer::VALID))
1931         {
1932             auto curMap = chainDef.GetCurrenciesMap();
1933             for (auto &transfer : transferInputs)
1934             {
1935                 if (!(transfer.second.second.flags & CReserveTransfer::PREALLOCATE) && curMap.count(transfer.second.second.currencyID))
1936                 {
1937                     CAmount conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1938                     preconvertedAmounts.valueMap[transfer.second.second.currencyID] += (transfer.second.second.nValue - conversionFee);
1939                     fees.valueMap[transfer.second.second.currencyID] += transfer.second.second.nFees + conversionFee;
1940                 }
1941             }
1942             retVal = preconvertedAmounts;
1943             if (!chainDef.IsToken() && !(chainDef.ChainOptions() & chainDef.OPTION_FEESASRESERVE))
1944             {
1945                 retVal += fees;
1946             }
1947         }
1948     }
1949     else
1950     {
1951         retVal = CCurrencyValueMap(chainDef.currencies, chainDef.preconverted);
1952     }
1953     return retVal;
1954 }
1955
1956 // This creates a new token notarization input and output and attaches them to a new import transaction,
1957 // given the next export transaction about to be imported and its height
1958 bool CConnectedChains::NewImportNotarization(const CCurrencyDefinition &_curDef, 
1959                                              uint32_t height, 
1960                                              const CTransaction &lastImportTx, 
1961                                              uint32_t exportHeight, 
1962                                              const CTransaction &exportTx, 
1963                                              CMutableTransaction &mnewTx,
1964                                              CCoinbaseCurrencyState &oldCurState,
1965                                              CCoinbaseCurrencyState &newCurState)
1966 {
1967     if (!_curDef.IsValid() || !_curDef.IsToken())
1968     {
1969         LogPrintf("%s: cannot create import notarization for invalid or non-token currencies\n", __func__);
1970         return false;
1971     }
1972
1973     uint160 currencyID = _curDef.GetID();
1974
1975     CCurrencyDefinition curDef;
1976     int32_t curDefHeight;
1977     if (!GetCurrencyDefinition(currencyID, curDef, &curDefHeight))
1978     {
1979         LogPrintf("%s: cannot create import notarization - currency not found\n", __func__);
1980         return false;
1981     }
1982
1983     CPBaaSNotarization lastNotarization(lastImportTx);
1984     if (curDef.systemID == ASSETCHAINS_CHAINID && !lastNotarization.IsValid())
1985     {
1986         LogPrintf("%s: error getting notarization transaction %s\n", __func__, lastImportTx.GetHash().GetHex().c_str());
1987         return false;
1988     }
1989
1990     CCoinbaseCurrencyState initialCurrencyState = lastNotarization.currencyState;
1991
1992     bool isDefinition = false;
1993
1994     // if our last notarization is prior to the start block, then we need to get our
1995     // initial currency state from a new start
1996     if (lastNotarization.notarizationHeight < curDef.startBlock)
1997     {
1998         initialCurrencyState = ConnectedChains.GetCurrencyState(curDef, curDef.startBlock - 1, curDefHeight);
1999         isDefinition = true;
2000         if (height >= curDef.startBlock)
2001         {
2002             initialCurrencyState.SetPrelaunch(false);
2003         }
2004     }
2005
2006     CCrossChainExport ccx(exportTx);
2007     if (!ccx.IsValid())
2008     {
2009         LogPrintf("%s: invalid export transaction %s\n", __func__, lastImportTx.GetHash().GetHex().c_str());
2010         return false;
2011     }
2012
2013     if (!lastNotarization.IsValid())
2014     {
2015         CChainNotarizationData cnd;
2016         // TODO: right now, there is no notarization made until after the launch
2017         // we should roll up periodic notarizations and pay miners to do it, making
2018         // long pre-launch periods possible
2019         if (GetNotarizationData(curDef.GetID(), EVAL_ACCEPTEDNOTARIZATION, cnd) && cnd.vtx.size() && cnd.lastConfirmed >= 0)
2020         {
2021             lastNotarization = cnd.vtx[cnd.lastConfirmed].second;
2022             initialCurrencyState = lastNotarization.currencyState;
2023         }
2024         else
2025         {
2026             LogPrintf("%s: cannot get last notarization for %s\n", __func__, curDef.name.c_str());
2027             return false;
2028         }
2029     }
2030
2031     oldCurState = initialCurrencyState;
2032
2033     CBlockIndex *pindex;
2034     CTxDestination notarizationID = VERUS_DEFAULTID.IsNull() ? CTxDestination(CIdentityID(currencyID)) : CTxDestination(VERUS_DEFAULTID);
2035
2036     // if this is the first notarization after start, make the notarization and determine if we should
2037     // launch or refund
2038     if (isDefinition || height == curDef.startBlock -1)
2039     {
2040         bool refunding = false;
2041
2042         pindex = chainActive[curDef.startBlock];
2043
2044         // check if the chain is qualified for a refund
2045         CCurrencyValueMap minPreMap, fees;
2046         CCurrencyValueMap preConvertedMap = CCurrencyValueMap(curDef.currencies, curDef.preconverted).CanonicalMap();
2047         newCurState = initialCurrencyState;
2048
2049         if (curDef.minPreconvert.size() && curDef.minPreconvert.size() == curDef.currencies.size())
2050         {
2051             minPreMap = CCurrencyValueMap(curDef.currencies, curDef.minPreconvert).CanonicalMap();
2052         }
2053
2054         if (minPreMap.valueMap.size() && preConvertedMap < minPreMap)
2055         {
2056             // we force the supply to zero
2057             // in any case where there was less than minimum participation,
2058             // the result of the supply cannot be zero, enabling us to easily determine that this
2059             // represents a failed launch
2060             newCurState.supply = 0;
2061             newCurState.SetRefunding(true);
2062             refunding = true;
2063         }
2064         else if (curDef.IsFractional() &&
2065                  exportTx.vout.size() &&
2066                  exportTx.vout.back().scriptPubKey.IsOpReturn())
2067         {
2068             // we are not refunding, and it is possible that we also have
2069             // normal conversions in addition to pre-conversions. add any conversions that may 
2070             // be present into the new currency state
2071             CReserveTransactionDescriptor rtxd;
2072             std::vector<CBaseChainObject *> exportObjects;
2073             std::vector<CTxOut> vOutputs;
2074
2075             exportObjects = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey);
2076
2077             bool isValidExport = rtxd.AddReserveTransferImportOutputs(currencyID, curDef, initialCurrencyState, exportObjects, vOutputs, &newCurState);
2078             if (isValidExport)
2079             {
2080                 // on definition, initial state is correct
2081                 newCurState = initialCurrencyState;
2082             }
2083             DeleteOpRetObjects(exportObjects);
2084             if (!isValidExport)
2085             {
2086                 LogPrintf("%s: invalid export opreturn for transaction %s\n", __func__, exportTx.GetHash().GetHex().c_str());
2087                 return false;
2088             }
2089         }
2090     }
2091     else
2092     {
2093         if (chainActive.Height() > height)
2094         {
2095             pindex = chainActive[height];
2096         }
2097         else
2098         {
2099             pindex = chainActive.LastTip();
2100         }
2101         
2102         if (!pindex)
2103         {
2104             LogPrintf("%s: invalid active chain\n", __func__);
2105             return false;
2106         }
2107
2108         // this is not the first notarization, so the last notarization will let us know if this is a refund or not
2109         CCurrencyValueMap minPreMap;
2110
2111         newCurState = initialCurrencyState;
2112
2113         if (curDef.minPreconvert.size() && curDef.minPreconvert.size() == curDef.currencies.size())
2114         {
2115             minPreMap = CCurrencyValueMap(curDef.currencies, curDef.minPreconvert).CanonicalMap();
2116         }
2117
2118         // we won't change currency state in notarizations after failure to launch, if success, recalculate as needed
2119         if (!(lastNotarization.currencyState.IsRefunding()))
2120         {
2121             // calculate new currency state from this import
2122             // we are not refunding, and it is possible that we also have
2123             // normal conversions in addition to pre-conversions. add any conversions that may 
2124             // be present into the new currency state
2125             CReserveTransactionDescriptor rtxd;
2126             std::vector<CBaseChainObject *> exportObjects;
2127             std::vector<CTxOut> vOutputs;
2128
2129             exportObjects = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey);
2130
2131             bool isValidExport = rtxd.AddReserveTransferImportOutputs(currencyID, curDef, initialCurrencyState, exportObjects, vOutputs, &newCurState);
2132             if (isValidExport && curDef.IsFractional())
2133             {
2134                 // we want the new price and the old state as a starting point to ensure no rounding error impact
2135                 // on reserves
2136                 CCoinbaseCurrencyState tempCurState = initialCurrencyState;
2137                 tempCurState.conversionPrice = newCurState.conversionPrice;
2138                 vOutputs.resize(0);
2139                 rtxd = CReserveTransactionDescriptor();
2140                 isValidExport = rtxd.AddReserveTransferImportOutputs(currencyID, curDef, tempCurState, exportObjects, vOutputs, &newCurState);
2141             }
2142             else if (!curDef.IsFractional())
2143             {
2144                 newCurState = initialCurrencyState;
2145             }
2146             DeleteOpRetObjects(exportObjects);
2147             if (!isValidExport)
2148             {
2149                 LogPrintf("%s: invalid export opreturn for transaction %s\n", __func__, exportTx.GetHash().GetHex().c_str());
2150                 return false;
2151             }
2152         }
2153     }
2154
2155     uint256 lastImportTxHash = lastImportTx.GetHash();
2156
2157     // now, add the initial notarization to the import tx
2158     // we will begin refund or import after notarization is accepted and returned by GetNotarizationData
2159     CPBaaSNotarization pbn = CPBaaSNotarization(curDef.notarizationProtocol,
2160                                                 currencyID,
2161                                                 notarizationID,
2162                                                 height,
2163                                                 chainActive.GetMMV().GetRoot(),
2164                                                 chainActive.GetMMRNode(pindex->GetHeight()).hash,
2165                                                 ArithToUint256(GetCompactPower(pindex->nNonce, pindex->nBits, pindex->nVersion)),
2166                                                 newCurState,
2167                                                 lastImportTxHash,
2168                                                 lastNotarization.notarizationHeight,
2169                                                 uint256(), 0, COpRetProof(), std::vector<CNodeData>());
2170
2171     // create notarization output
2172     CCcontract_info CC;
2173     CCcontract_info *cp;
2174
2175     std::vector<CTxDestination> dests;
2176
2177     // make the accepted notarization output
2178     cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
2179
2180     if (curDef.notarizationProtocol == curDef.NOTARIZATION_NOTARY_CHAINID)
2181     {
2182         dests = std::vector<CTxDestination>({CIdentityID(currencyID)});
2183     }
2184     else if (curDef.notarizationProtocol == curDef.NOTARIZATION_AUTO)
2185     {
2186         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2187     }
2188     else
2189     {
2190         return false;
2191     }
2192     mnewTx.vout.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &pbn))));
2193
2194     // make the finalization output
2195     cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
2196
2197     if (curDef.notarizationProtocol == curDef.NOTARIZATION_AUTO)
2198     {
2199         dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2200     }
2201
2202     // finish transaction by adding the prior input and finalization, sign, then put it in the mempool
2203     // all output for notarizing will be paid as mining fees, so there's no need to relay
2204     uint32_t confirmedOut, finalizeOut;
2205     if (!GetNotarizationAndFinalization(EVAL_ACCEPTEDNOTARIZATION, lastImportTx, pbn, &confirmedOut, &finalizeOut))
2206     {
2207         printf("ERROR: could not find expected initial notarization for currency %s\n", curDef.name.c_str());
2208         return false;
2209     }
2210
2211     mnewTx.vin.push_back(CTxIn(COutPoint(lastImportTxHash, confirmedOut)));
2212     mnewTx.vin.push_back(CTxIn(COutPoint(lastImportTxHash, finalizeOut)));
2213
2214     // we need to store the input that we confirmed if we spent finalization outputs
2215     CTransactionFinalization nf(CTransactionFinalization::FINALIZE_NOTARIZATION, pbn.currencyID, mnewTx.vin.size() - 1);
2216
2217     // update crypto condition with final notarization output data
2218     mnewTx.vout.push_back(CTxOut(0, 
2219             MakeMofNCCScript(CConditionObj<CTransactionFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &nf))));
2220
2221     return true;
2222 }
2223
2224 // process token related, local imports and exports
2225 void CConnectedChains::ProcessLocalImports()
2226 {
2227     // first determine all export threads on the current chain that are valid to import
2228     std::multimap<uint160, std::pair<int, CInputDescriptor>> exportOutputs;
2229     std::multimap<uint160, CTransaction> importThreads;
2230     uint160 thisChainID = thisChain.GetID();
2231
2232     LOCK2(cs_main, mempool.cs);
2233     uint32_t nHeight = chainActive.Height();
2234
2235     // get all pending, local exports and put them into a map
2236     std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue>> unspentOutputs;
2237     std::map<uint160, std::pair<uint32_t, CTransaction>> currenciesToImport;    // height of earliest tx
2238     CCurrencyDefinition oneCurrency;
2239
2240     //printf("%s: Searching for %s\n", __func__, EncodeDestination(CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_FINALIZE_EXPORT))).c_str());
2241     if (GetAddressUnspent(ConnectedChains.ThisChain().GetConditionID(EVAL_FINALIZE_EXPORT), CScript::P2IDX, unspentOutputs))
2242     {
2243         CCrossChainExport ccx, ccxDummy;
2244         CTransaction txOut, txImport;
2245         CPartialTransactionProof lastExport;
2246         CCrossChainImport cci;
2247         uint256 blkHash;
2248         for (auto &oneOut : unspentOutputs)
2249         {
2250             COptCCParams p;
2251             if (oneOut.second.blockHeight <= nHeight &&
2252                 oneOut.second.script.IsPayToCryptoCondition(p) &&
2253                 p.IsValid() &&
2254                 p.evalCode == EVAL_FINALIZE_EXPORT &&
2255                 p.vData.size() &&
2256                 CTransactionFinalization(p.vData[0]).IsValid() &&
2257                 myGetTransaction(oneOut.first.txhash, txOut, blkHash) &&
2258                 (ccx = CCrossChainExport(txOut)).IsValid() &&
2259                 !currenciesToImport.count(ccx.systemID) &&
2260                 (oneCurrency = GetCachedCurrency(ccx.systemID)).IsValid() &&
2261                 oneCurrency.startBlock <= nHeight &&
2262                 GetLastImport(ccx.systemID, txImport, lastExport, cci, ccxDummy))
2263             {
2264                 auto blockIt = mapBlockIndex.find(blkHash);
2265                 if (blockIt != mapBlockIndex.end() && chainActive.Contains(blockIt->second))
2266                 {
2267                     currenciesToImport.insert(make_pair(ccx.systemID, make_pair(blockIt->second->GetHeight(), txImport)));
2268                 }
2269             }
2270         }
2271     }
2272
2273     CMutableTransaction txTemplate = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight);
2274     for (auto &oneIT : currenciesToImport)
2275     {
2276         std::vector<CTransaction> importTxes;
2277         int32_t importOutNum = 0;
2278
2279         CCrossChainImport oneImportInput(oneIT.second.second, &importOutNum);
2280         if (oneImportInput.IsValid())
2281         {
2282             std::vector<CAddressUnspentDbEntry> reserveDeposits;
2283             GetAddressUnspent(ConnectedChains.GetCachedCurrency(oneIT.first).GetConditionID(EVAL_RESERVE_DEPOSIT), CScript::P2IDX, reserveDeposits);
2284             CCurrencyValueMap tokenImportAvailable;
2285             CAmount nativeImportAvailable = 0;
2286             for (auto &oneOut : reserveDeposits)
2287             {
2288                 nativeImportAvailable += oneOut.second.satoshis;
2289                 tokenImportAvailable += oneOut.second.script.ReserveOutValue();
2290                 //printf("nativeImportAvailable:%ld, tokenImportAvailable:%s\n", nativeImportAvailable, tokenImportAvailable.ToUniValue().write().c_str());
2291             }
2292             nativeImportAvailable += oneIT.second.second.vout[importOutNum].nValue;
2293             tokenImportAvailable += oneIT.second.second.vout[importOutNum].ReserveOutValue();
2294             //printf("nativeImportAvailable:%ld, tokenImportAvailable:%s\n", nativeImportAvailable, tokenImportAvailable.ToUniValue().write().c_str());
2295             if (CreateLatestImports(currencyDefCache[oneIT.first], oneIT.second.second, txTemplate, CTransaction(), tokenImportAvailable, nativeImportAvailable, importTxes))
2296             {
2297                 // fund the first import transaction with all reserveDeposits
2298                 // change amounts are passed through on the import thread
2299                 //
2300                 // TODO: manage when this becomes to large by splitting reserve deposits over the
2301                 // transactions
2302                 if (importTxes.size())
2303                 {
2304                     CMutableTransaction oneImport = importTxes[0];
2305                     int32_t outNum = 0;
2306                     CCrossChainImport cci(importTxes[0], &outNum);
2307                     if (cci.IsValid())
2308                     {
2309                         // add the reserve deposit inputs to the first transaction
2310                         // the  outputs should have been automatically propagated through
2311                         // fixup inputs if necessary
2312                         if (reserveDeposits.size())
2313                         {
2314                             UniValue jsonTX(UniValue::VOBJ);
2315                             std::vector<uint256> prevHashes;
2316
2317                             for (int i = 0; i < importTxes.size() - 1; i++)
2318                             {
2319                                 prevHashes.push_back(importTxes[i].GetHash());
2320                             }
2321
2322                             // TODO - get reserve deposits from exports, not all at once, as in refunds
2323                             for (auto &oneOut : reserveDeposits)
2324                             {
2325                                 oneImport.vin.push_back(CTxIn(oneOut.first.txhash, oneOut.first.index));
2326                             }
2327
2328                             importTxes[0] = oneImport;
2329
2330                             for (int i = 0; i < importTxes.size() - 1; i++)
2331                             {
2332                                 oneImport = importTxes[i + 1];
2333                                 for (auto &oneIn : oneImport.vin)
2334                                 {
2335                                     if (oneIn.prevout.hash == prevHashes[i])
2336                                     {
2337                                         //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());
2338                                         oneIn.prevout.hash = importTxes[i].GetHash();
2339                                     }
2340                                 }
2341                                 importTxes[i + 1] = oneImport;
2342                             }
2343
2344                         }
2345                         SignAndCommitImportTransactions(oneIT.second.second, importTxes);
2346                     }
2347                 }
2348             }
2349         }
2350     }
2351 }
2352
2353 void CConnectedChains::SubmissionThread()
2354 {
2355     try
2356     {
2357         arith_uint256 lastHash;
2358         int64_t lastImportTime = 0;
2359         uint32_t lastHeight = 0;
2360         
2361         // wait for something to check on, then submit blocks that should be submitted
2362         while (true)
2363         {
2364             boost::this_thread::interruption_point();
2365
2366             if (IsVerusActive())
2367             {
2368                 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
2369                 //printf("SubmissionThread: pruning\n");
2370                 PruneOldChains(GetAdjustedTime() - 300);
2371                 bool submit = false;
2372                 {
2373                     LOCK(cs_mergemining);
2374                     if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
2375                     {
2376                         qualifiedHeaders.clear();
2377                     }
2378                     submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
2379
2380                     //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
2381                 }
2382                 if (submit)
2383                 {
2384                     //printf("SubmissionThread: calling submit qualified blocks\n");
2385                     SubmitQualifiedBlocks();
2386                 }
2387
2388                 ProcessLocalImports();
2389
2390                 if (!submit)
2391                 {
2392                     sem_submitthread.wait();
2393                 }
2394             }
2395             else
2396             {
2397                 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
2398                 if (CheckVerusPBaaSAvailable())
2399                 {
2400                     // check to see if we have recently earned a block with an earned notarization that qualifies for
2401                     // submitting an accepted notarization
2402                     if (earnedNotarizationHeight)
2403                     {
2404                         CBlock blk;
2405                         int32_t txIndex = -1, height;
2406                         {
2407                             LOCK(cs_mergemining);
2408                             if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
2409                             {
2410                                 blk = earnedNotarizationBlock;
2411                                 earnedNotarizationBlock = CBlock();
2412                                 txIndex = earnedNotarizationIndex;
2413                                 height = earnedNotarizationHeight;
2414                                 earnedNotarizationHeight = 0;
2415                             }
2416                         }
2417
2418                         if (txIndex != -1)
2419                         {
2420                             //printf("SubmissionThread: testing notarization\n");
2421                             CTransaction lastConfirmed;
2422                             uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
2423
2424                             if (!txId.IsNull())
2425                             {
2426                                 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
2427                                 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
2428                             }
2429                         }
2430                     }
2431
2432                     // every "n" seconds, look for imports to include in our blocks from the Verus chain
2433                     if ((GetAdjustedTime() - lastImportTime) >= 30 || lastHeight < (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight()))
2434                     {
2435                         lastImportTime = GetAdjustedTime();
2436                         lastHeight = (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight());
2437
2438                         // see if our notary has a confirmed notarization for us
2439                         UniValue params(UniValue::VARR);
2440                         UniValue result;
2441
2442                         params.push_back(thisChain.name);
2443
2444                         try
2445                         {
2446                             result = find_value(RPCCallRoot("getlastimportin", params), "result");
2447                         } catch (exception e)
2448                         {
2449                             result = NullUniValue;
2450                         }
2451
2452                         if (!result.isNull())
2453                         {
2454                             auto txUniStr = find_value(result, "lastimporttransaction");
2455                             auto txLastConfirmedStr = find_value(result, "lastconfirmednotarization");
2456                             auto txTemplateStr = find_value(result, "importtxtemplate");
2457                             CAmount nativeImportAvailable = uni_get_int64(find_value(result, "nativeimportavailable"));
2458                             CCurrencyValueMap tokenImportAvailable(find_value(params[0], "tokenimportavailable"));
2459
2460                             CTransaction lastImportTx, lastConfirmedTx, templateTx;
2461
2462                             if (txUniStr.isStr() && txTemplateStr.isStr() && 
2463                                 DecodeHexTx(lastImportTx, txUniStr.get_str()) && 
2464                                 DecodeHexTx(lastConfirmedTx, txLastConfirmedStr.get_str()) && 
2465                                 DecodeHexTx(templateTx, txTemplateStr.get_str()))
2466                             {
2467                                 std::vector<CTransaction> importTxes;
2468                                 if (CreateLatestImports(notaryChain.chainDefinition, lastImportTx, templateTx, lastConfirmedTx, tokenImportAvailable, nativeImportAvailable, importTxes))
2469                                 {
2470                                     for (auto importTx : importTxes)
2471                                     {
2472                                         UniValue txResult;
2473                                         params.setArray();
2474                                         params.push_back(EncodeHexTx(importTx));
2475
2476                                         try
2477                                         {
2478                                             txResult = find_value(RPCCallRoot("signrawtransaction", params), "result");
2479                                             if (txResult.isObject() && !(txResult = find_value(txResult, "hex")).isNull() && txResult.isStr() && txResult.get_str().size())
2480                                             {
2481                                                 params.setArray();
2482                                                 params.push_back(txResult);
2483                                                 txResult = find_value(RPCCallRoot("sendrawtransaction", params), "result");
2484                                             }
2485                                             else
2486                                             {
2487                                                 txResult = NullUniValue;
2488                                             }
2489                                             
2490                                         } catch (exception e)
2491                                         {
2492                                             txResult = NullUniValue;
2493                                         }
2494                                         uint256 testId;
2495                                         if (txResult.isStr())
2496                                         {
2497                                             testId.SetHex(txResult.get_str());
2498                                         }
2499                                         if (testId.IsNull())
2500                                         {
2501                                             break;
2502                                         }
2503                                     }
2504                                 }
2505                             }
2506                         }
2507                     }
2508                 }
2509                 sleep(3);
2510             }
2511             boost::this_thread::interruption_point();
2512         }
2513     }
2514     catch (const boost::thread_interrupted&)
2515     {
2516         LogPrintf("Verus merge mining thread terminated\n");
2517     }
2518 }
2519
2520 void CConnectedChains::SubmissionThreadStub()
2521 {
2522     ConnectedChains.SubmissionThread();
2523 }
2524
2525 void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
2526 {
2527     // called after winning a block that contains an earned notarization
2528     // the earned notarization and its height are queued for processing by the submission thread
2529     // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
2530     // kept
2531     LOCK(cs_mergemining);
2532
2533     // we only care about the last
2534     earnedNotarizationHeight = height;
2535     earnedNotarizationBlock = blk;
2536     earnedNotarizationIndex = txIndex;
2537 }
2538
2539 bool IsChainDefinitionInput(const CScript &scriptSig)
2540 {
2541     uint32_t ecode;
2542     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_CURRENCY_DEFINITION;
2543 }
2544
This page took 0.172714 seconds and 4 git commands to generate.