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