]> Git Repo - VerusCoin.git/blob - src/pbaas/pbaas.cpp
Validation of multi-currency coinbases with multiple currency definitions
[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 "main.h"
14 #include "rpc/pbaasrpc.h"
15 #include "base58.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                 delete pobj;
326         }
327     }
328     ora.clear();
329 }
330
331 std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
332 {
333     std::vector<unsigned char> vch;
334     std::vector<CBaseChainObject *> vRet;
335     if (opRetScript.IsOpReturn() && GetOpReturnData(opRetScript, vch) && vch.size() > 0)
336     {
337         CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION);
338
339         int32_t opRetType;
340
341         try
342         {
343             s >> opRetType;
344             if (opRetType == OPRETTYPE_OBJECTARR)
345             {
346                 CBaseChainObject *pobj;
347                 while (!s.empty() && (pobj = RehydrateChainObject(s)))
348                 {
349                     vRet.push_back(pobj);
350                 }
351                 if (!s.empty())
352                 {
353                     printf("failed to load all objects in opret");
354                     DeleteOpRetObjects(vRet);
355                     vRet.clear();
356                 }
357             }
358         }
359         catch(const std::exception& e)
360         {
361             std::cerr << e.what() << '\n';
362             DeleteOpRetObjects(vRet);
363             vRet.clear();
364         }
365     }
366     return vRet;
367 }
368
369 CServiceReward::CServiceReward(const CTransaction &tx, bool validate)
370 {
371     nVersion = PBAAS_VERSION_INVALID;
372     for (auto out : tx.vout)
373     {
374         COptCCParams p;
375         if (IsPayToCryptoCondition(out.scriptPubKey, p))
376         {
377             // always take the first for now
378             if (p.evalCode == EVAL_SERVICEREWARD)
379             {
380                 FromVector(p.vData[0], *this);
381                 break;
382             }
383         }
384     }
385
386     if (validate)
387     {
388         
389     }
390 }
391
392 CCrossChainExport::CCrossChainExport(const CTransaction &tx, int32_t *pCCXOutputNum)
393 {
394     int32_t _ccxOutputNum = 0;
395     int32_t &ccxOutputNum = pCCXOutputNum ? *pCCXOutputNum : _ccxOutputNum;
396     
397     for (int i = 0; i < tx.vout.size(); i++)
398     {
399         COptCCParams p;
400         if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) &&
401             p.IsValid() &&
402             p.evalCode == EVAL_CROSSCHAIN_EXPORT)
403         {
404             FromVector(p.vData[0], *this);
405             ccxOutputNum = i;
406             break;
407         }
408     }
409 }
410
411 CCurrencyDefinition::CCurrencyDefinition(const CScript &scriptPubKey)
412 {
413     nVersion = PBAAS_VERSION_INVALID;
414     COptCCParams p;
415     if (scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
416     {
417         if (p.evalCode == EVAL_CURRENCY_DEFINITION)
418         {
419             FromVector(p.vData[0], *this);
420         }
421     }
422 }
423
424 std::vector<CCurrencyDefinition> CCurrencyDefinition::GetCurrencyDefinitions(const CTransaction &tx)
425 {
426     std::vector<CCurrencyDefinition> retVal;
427     for (auto &out : tx.vout)
428     {
429         CCurrencyDefinition oneCur = CCurrencyDefinition(out.scriptPubKey);
430         if (oneCur.IsValid())
431         {
432             retVal.push_back(oneCur);
433         }
434     }
435     return retVal;
436 }
437
438 uint160 CCurrencyDefinition::GetID(const std::string &Name, uint160 &Parent)
439 {
440     return CIdentity::GetID(Name, Parent);
441 }
442
443 uint160 CCurrencyDefinition::GetConditionID(int32_t condition) const
444 {
445     return CCrossChainRPCData::GetConditionID(name, condition);
446 }
447
448 #define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
449 extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
450 extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3], ASSETCHAINS_ERAOPTIONS[3];
451 extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
452 extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
453 extern std::string VERUS_CHAINNAME;
454 extern uint160 VERUS_CHAINID;
455
456 // ensures that the chain definition is valid and that there are no other definitions of the same name
457 // that have been confirmed.
458 bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
459 {
460     // the chain definition output can be spent when the chain is at the end of its life and only then
461     // TODO
462     return false;
463 }
464
465 // ensures that the chain definition is valid and that there are no other definitions of the same name
466 // that have been confirmed.
467 bool CheckChainDefinitionOutputs(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
468 {
469     // checked before a chain definition output script is accepted as a valid transaction
470
471     // basics - we need a chain definition transaction to kick off a PBaaS chain. it must have:
472     // 1) valid chain definition output with parameters in proper ranges and no duplicate name
473     // 2) notarization output with conformant values
474     // 3) finalization output
475     // 3) notarization funding
476     //
477
478     // get the source transaction
479     uint256 blkHash;
480     CTransaction thisTx;
481     if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
482     {
483         LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
484         return false;
485     }
486
487     std::vector<CCurrencyDefinition> chainDefs = CCurrencyDefinition::GetCurrencyDefinitions(thisTx);
488     CPBaaSNotarization notarization(thisTx);
489     CNotarizationFinalization finalization(thisTx);
490     bool isVerusActive = IsVerusActive();
491
492     if (!notarization.IsValid() || !finalization.IsValid())
493     {
494         LogPrintf("transaction specified, %s, must have valid notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
495         return false;
496     }
497
498     std::set<uint160> allCurrencyIDs;
499     for (auto &curPair : ConnectedChains.ReserveCurrencies())
500     {
501         allCurrencyIDs.insert(curPair.first);
502     }
503     allCurrencyIDs.insert(ConnectedChains.ThisChain().GetID());
504     if (!isVerusActive)
505     {
506         allCurrencyIDs.insert(ConnectedChains.notaryChain.GetID());
507     }
508
509     bool isCoinbase = thisTx.IsCoinBase();
510     bool isVerified = false;
511     CIdentity activatedID(thisTx);
512
513     // currency definitions can be valid as follows:
514     // 1. original definition in a transaction that simultaneously sets the active currency bit on the identity of the same
515     //    name and ID.
516     // 2. outputs of a coinbase transaction in block 1 that defines the parent currency, new currency, and any reserve currencies
517     // 3. currency import from the defining chain of the currency, which has not been implemented as of this comment
518     if (activatedID.IsValid() && 
519         activatedID.HasActiveCurrency() && 
520         chainDefs.size() == 1 &&
521         activatedID.parent == ASSETCHAINS_CHAINID &&
522         activatedID.GetID() == chainDefs[0].GetID())
523     {
524         isVerified = true;
525     }
526     else if (isCoinbase && chainDefs.size() >= 1 && !isVerusActive)
527     {
528         int32_t height1 = 1;
529         CScript expect = CScript() << height1;
530         opcodetype opcode = (opcodetype)*expect.begin();
531
532         if (opcode >= OP_1 && opcode <= OP_16)
533         {
534             isVerified = (thisTx.vin[0].scriptSig.size() >= 1 && CScript::DecodeOP_N(opcode) == height1) || 
535                             (thisTx.vin[0].scriptSig.size() >= 2 && thisTx.vin[0].scriptSig[0] == OP_PUSHDATA1 && (int)thisTx.vin[0].scriptSig[1] == height1);
536         }
537         else
538         {
539             isVerified = thisTx.vin[0].scriptSig.size() >= expect.size() && std::equal(expect.begin(), expect.end(), thisTx.vin[0].scriptSig.begin());
540         }
541
542         for (auto &chainDef : chainDefs)
543         {
544             uint160 chainID = chainDef.GetID();
545             if (!chainDef.IsValid())
546             {
547                 LogPrintf("transaction specified, %s, must not contain invalid chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
548                 return false;
549             }
550             if (!allCurrencyIDs.count(chainID))
551             {
552                 LogPrintf("transaction specified, %s, must not contain invalid chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
553                 return false;
554             }
555             allCurrencyIDs.erase(chainID);
556         }
557         if (allCurrencyIDs.size())
558         {
559             LogPrintf("transaction specified, %s, does not contain all required chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
560             return false;
561         }
562     }
563
564     return isVerified;
565 }
566
567 CCurrencyValueMap CCrossChainExport::CalculateExportFee() const
568 {
569     CCurrencyValueMap retVal;
570
571     if (numInputs > MAX_EXPORT_INPUTS)
572     {
573         return retVal;
574     }
575     static const arith_uint256 satoshis(100000000);
576
577     arith_uint256 ratio(50000000 + ((25000000 / MAX_EXPORT_INPUTS) * (numInputs - 1)));
578
579     for (auto &feePair : totalFees.valueMap)
580     {
581         retVal.valueMap[feePair.first] = (((arith_uint256(feePair.second) * ratio)) / satoshis).GetLow64();
582     }
583     return retVal;
584 }
585
586 CCurrencyValueMap CCrossChainExport::CalculateImportFee() const
587 {
588     CCurrencyValueMap retVal;
589
590     for (auto &feePair : CalculateExportFee().valueMap)
591     {
592         CAmount feeAmount = feePair.second;
593         auto it = totalFees.valueMap.find(feePair.first);
594         retVal.valueMap[feePair.first] = (it != totalFees.valueMap.end() ? it->second : 0) - feeAmount;
595     }
596     return retVal;
597 }
598
599 bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
600 {
601     bool retval = false;
602     LOCK(cs_mergemining);
603
604     //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
605
606     auto chainIt = mergeMinedChains.find(chainID);
607     if (chainIt != mergeMinedChains.end())
608     {
609         arith_uint256 target;
610         target.SetCompact(chainIt->second.block.nBits);
611         for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
612         {
613             // make sure we don't just match by target
614             if (removeRange.first->second->GetID() == chainID)
615             {
616                 mergeMinedTargets.erase(removeRange.first);
617                 break;
618             }
619         }
620         mergeMinedChains.erase(chainID);
621         dirty = retval = true;
622
623         // if we get to 0, give the thread a kick to stop waiting for mining
624         //if (!mergeMinedChains.size())
625         //{
626         //    sem_submitthread.post();
627         //}
628     }
629     return retval;
630 }
631
632 // remove merge mined chains added and not updated since a specific time
633 void CConnectedChains::PruneOldChains(uint32_t pruneBefore)
634 {
635     vector<uint160> toRemove;
636
637     LOCK(cs_mergemining);
638     for (auto blkData : mergeMinedChains)
639     {
640         if (blkData.second.block.nTime < pruneBefore)
641         {
642             toRemove.push_back(blkData.first);
643         }
644     }
645
646     for (auto id : toRemove)
647     {
648         //printf("Pruning chainID: %s\n", id.GetHex().c_str());
649         RemoveMergedBlock(id);
650     }
651 }
652
653 // adds or updates merge mined blocks
654 // returns false if failed to add
655 bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
656 {
657     // determine if we should replace one or add to the merge mine vector
658     {
659         LOCK(cs_mergemining);
660
661         arith_uint256 target;
662         uint160 cID = blkData.GetID();
663         auto it = mergeMinedChains.find(cID);
664         if (it != mergeMinedChains.end())
665         {
666             RemoveMergedBlock(cID);             // remove it if already there
667         }
668         target.SetCompact(blkData.block.nBits);
669
670         //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
671
672         mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
673         dirty = true;
674     }
675     return true;
676 }
677
678 bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
679 {
680     {
681         LOCK(cs_mergemining);
682         auto chainIt = mergeMinedChains.find(chainID);
683         if (chainIt != mergeMinedChains.end())
684         {
685             rpcChainData = (CRPCChainData)chainIt->second;
686             return true;
687         }
688         return false;
689     }
690 }
691
692 // this returns a pointer to the data without copy and assumes the lock is held
693 CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
694 {
695     {
696         auto chainIt = mergeMinedChains.find(chainID);
697         if (chainIt != mergeMinedChains.end())
698         {
699             return &chainIt->second;
700         }
701         return NULL;
702     }
703 }
704
705 void CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
706 {
707     //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
708     {
709         LOCK(cs_mergemining);
710
711         qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
712     }
713     sem_submitthread.post();
714 }
715
716 // get the latest block header and submit one block at a time, returning after there are no more
717 // matching blocks to be found
718 vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
719 {
720     std::set<uint160> inHeader;
721     bool submissionFound;
722     CPBaaSMergeMinedChainData chainData;
723     vector<pair<string, UniValue>>  results;
724
725     CBlockHeader bh;
726     arith_uint256 lastHash;
727     CPBaaSBlockHeader pbh;
728
729     do
730     {
731         submissionFound = false;
732         {
733             LOCK(cs_mergemining);
734             // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
735             // common, merge mined headers for notarization, drop out on any submission
736             for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
737             {
738                 // add the PBaaS chain ids from this header to a set for search
739                 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
740                 {
741                     inHeader.insert(pbh.chainID);
742                 }
743
744                 uint160 chainID;
745                 // now look through all targets that are equal to or above the hash of this header
746                 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
747                 {
748                     chainID = chainIt->second->GetID();
749                     if (inHeader.count(chainID))
750                     {
751                         // first, check that the winning header matches the block that is there
752                         CPBaaSPreHeader preHeader(chainIt->second->block);
753                         preHeader.SetBlockData(headerIt->second);
754
755                         // check if the block header matches the block's specific data, only then can we create a submission from this block
756                         if (headerIt->second.CheckNonCanonicalData(chainID))
757                         {
758                             // save block as is, remove the block from merged headers, replace header, and submit
759                             chainData = *chainIt->second;
760
761                             *(CBlockHeader *)&chainData.block = headerIt->second;
762
763                             submissionFound = true;
764                         }
765                         //else // not an error condition. code is here for debugging
766                         //{
767                         //    printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
768                         //}
769                     }
770                     //else // not an error condition. code is here for debugging
771                     //{
772                     //    printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
773                     //}
774                 }
775
776                 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
777                 if (submissionFound)
778                 {
779                     // once it is going to be submitted, remove block from this chain until a new one is added again
780                     RemoveMergedBlock(chainID);
781                     break;
782                 }
783                 else
784                 {
785                     qualifiedHeaders.erase(headerIt);
786                 }
787             }
788         }
789         if (submissionFound)
790         {
791             // submit one block and loop again. this approach allows multiple threads
792             // to collectively empty the submission queue, mitigating the impact of
793             // any one stalled daemon
794             UniValue submitParams(UniValue::VARR);
795             submitParams.push_back(EncodeHexBlk(chainData.block));
796             UniValue result, error;
797             try
798             {
799                 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
800                 result = find_value(result, "result");
801                 error = find_value(result, "error");
802             }
803             catch (exception e)
804             {
805                 result = UniValue(e.what());
806             }
807             results.push_back(make_pair(chainData.chainDefinition.name, result));
808             if (result.isStr() || !error.isNull())
809             {
810                 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());
811             }
812             else
813             {
814                 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
815             }
816         }
817     } while (submissionFound);
818     return results;
819 }
820
821 // add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
822 uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
823 {
824     vector<uint160> inHeader;
825     vector<UniValue> toCombine;
826     arith_uint256 blkHash = UintToArith256(bh.GetHash());
827     arith_uint256 target(0);
828     
829     CPBaaSBlockHeader pbh;
830
831     {
832         LOCK(cs_mergemining);
833
834         CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
835
836         for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
837         {
838             if (bh.GetPBaaSHeader(pbh, i))
839             {
840                 inHeader.push_back(pbh.chainID);
841             }
842         }
843
844         // loop through the existing PBaaS chain ids in the header
845         // remove any that are not either this Chain ID or in our local collection and then add all that are present
846         for (uint32_t i = 0; i < inHeader.size(); i++)
847         {
848             auto it = mergeMinedChains.find(inHeader[i]);
849             if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
850             {
851                 bh.DeletePBaaSHeader(i);
852             }
853         }
854
855         for (auto chain : mergeMinedChains)
856         {
857             // get the native PBaaS header for each chain and put it into the
858             // header we are given
859             // it must have itself in as a PBaaS header
860             uint160 cid = chain.second.GetID();
861             if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
862             {
863                 if (!bh.AddUpdatePBaaSHeader(pbh))
864                 {
865                     LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
866                     break;
867                 }
868                 else
869                 {
870                     arith_uint256 t;
871                     t.SetCompact(chain.second.block.nBits);
872                     if (t > target)
873                     {
874                         target = t;
875                     }
876                 }
877             }
878             else
879             {
880                 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
881             }
882             
883         }
884         dirty = false;
885     }
886
887     return target.GetCompact();
888 }
889
890 bool CConnectedChains::IsVerusPBaaSAvailable()
891 {
892     return notaryChainVersion >= "0.6.4";
893 }
894
895 extern string PBAAS_HOST, PBAAS_USERPASS;
896 extern int32_t PBAAS_PORT;
897 bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
898 {
899     if (chainInfoUni.isObject() && chainDefUni.isObject())
900     {
901         UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
902         if (uniVer.isStr())
903         {
904             LOCK(cs_mergemining);
905             notaryChainVersion = uni_get_str(uniVer);
906             notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
907             CCurrencyDefinition chainDef(chainDefUni);
908             notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
909         }
910     }
911     return IsVerusPBaaSAvailable();
912 }
913
914 uint32_t CConnectedChains::NotaryChainHeight()
915 {
916     LOCK(cs_mergemining);
917     return notaryChainHeight;
918 }
919
920 bool CConnectedChains::CheckVerusPBaaSAvailable()
921 {
922     if (IsVerusActive())
923     {
924         notaryChainVersion = "";
925     }
926     else
927     {
928         // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
929         // tolerate only 15 second timeout
930         UniValue chainInfo, chainDef;
931         try
932         {
933             UniValue params(UniValue::VARR);
934             chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
935             if (!chainInfo.isNull())
936             {
937                 params.push_back(VERUS_CHAINNAME);
938                 chainDef = find_value(RPCCallRoot("getcurrencydefinition", params), "result");
939
940                 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
941                 {
942                     // if we have not past block 1 yet, store the best known update of our current state
943                     if ((!chainActive.LastTip() || !chainActive.LastTip()->GetHeight()))
944                     {
945                         bool success = false;
946                         params.clear();
947                         params.push_back(EncodeDestination(CIdentityID(thisChain.GetID())));
948                         chainDef = find_value(RPCCallRoot("getcurrencydefinition", params), "result");
949                         if (!chainDef.isNull())
950                         {
951                             CCurrencyDefinition currencyDef(chainDef);
952                             if (currencyDef.IsValid())
953                             {
954                                 thisChain = currencyDef;
955                                 if (NotaryChainHeight() >= thisChain.startBlock)
956                                 {
957                                     readyToStart = true;    // this only gates mining of block one, to be sure we have the latest definition
958                                 }
959                                 success = true;
960                             }
961                         }
962                         return success;
963                     }
964                     return true;
965                 }
966             }
967         } catch (exception e)
968         {
969             LogPrintf("%s: Error communicating with %s chain\n", __func__, VERUS_CHAINNAME);
970         }
971     }
972     notaryChainVersion = "";
973     return false;
974 }
975
976 int CConnectedChains::GetThisChainPort() const
977 {
978     int port;
979     string host;
980     for (auto node : defaultPeerNodes)
981     {
982         SplitHostPort(node.networkAddress, port, host);
983         if (port)
984         {
985             return port;
986         }
987     }
988     return 0;
989 }
990
991 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
992 {
993     CCoinbaseCurrencyState currencyState;
994     CBlock block;
995     LOCK(cs_main);
996     bool isVerusActive = IsVerusActive();
997     if (!isVerusActive && 
998         CConstVerusSolutionVector::activationHeight.ActiveVersion(height) >= CActivationHeight::ACTIVATE_PBAAS &&
999         height != 0 && 
1000         height <= chainActive.Height() && 
1001         chainActive[height] && 
1002         ReadBlockFromDisk(block, chainActive[height], Params().GetConsensus()) &&
1003         (currencyState = CCoinbaseCurrencyState(block.vtx[0])).IsValid())
1004     {
1005         return currencyState;
1006     }
1007     else
1008     {
1009         return GetInitialCurrencyState(thisChain);
1010     }
1011 }
1012
1013 bool CConnectedChains::SetLatestMiningOutputs(const std::vector<pair<int, CScript>> &minerOutputs, CTxDestination &firstDestinationOut)
1014 {
1015     LOCK(cs_mergemining);
1016
1017     if (!minerOutputs.size() || !ExtractDestination(minerOutputs[0].second, firstDestinationOut))
1018     {
1019         return false;
1020     }
1021     latestMiningOutputs = minerOutputs;
1022     latestDestination = firstDestinationOut;
1023     return true;
1024 }
1025
1026 void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
1027 {
1028     // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
1029     {
1030         if (!nHeight)
1031         {
1032             return;
1033         }
1034
1035         std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
1036         std::multimap<std::pair<uint160, uint160>, std::pair<CInputDescriptor, CReserveTransfer>> sortedTransferOutputs;
1037         std::set<uint160> failedLaunches;
1038         std::map<uint160, CCurrencyDefinition> currencyDefCache;                            // keep cache as we look up definitions
1039
1040         LOCK(cs_main);
1041
1042         uint160 thisChainID = ConnectedChains.ThisChain().GetID();
1043
1044         // get all available transfer outputs to aggregate into export transactions
1045         if (GetUnspentChainTransfers(transferOutputs))
1046         {
1047             if (!transferOutputs.size())
1048             {
1049                 return;
1050             }
1051             for (auto &output : transferOutputs)
1052             {
1053                 CCurrencyDefinition sourceDef, destDef, systemDef;
1054                 auto it = currencyDefCache.find(output.second.second.currencyID);
1055                 if ((it != currencyDefCache.end() && !(sourceDef = it->second).IsValid()) ||
1056                     (it == currencyDefCache.end() && !GetCurrencyDefinition(output.second.second.currencyID, sourceDef)))
1057                 {
1058                     printf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.currencyID)).c_str());
1059                     LogPrintf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.currencyID)).c_str());
1060                     continue;
1061                 }
1062                 if (it == currencyDefCache.end())
1063                 {
1064                     currencyDefCache[output.second.second.currencyID] = sourceDef;
1065                 }
1066
1067                 it = currencyDefCache.find(output.second.second.destCurrencyID);
1068                 if ((it != currencyDefCache.end() && !(destDef = it->second).IsValid()) ||
1069                     (it == currencyDefCache.end() && !GetCurrencyDefinition(output.second.second.destCurrencyID, destDef)))
1070                 {
1071                     printf("%s: definition for destination currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.destCurrencyID)).c_str());
1072                     LogPrintf("%s: definition for destination currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(output.second.second.destCurrencyID)).c_str());
1073                     continue;
1074                 }
1075                 if (it == currencyDefCache.end())
1076                 {
1077                     currencyDefCache[output.second.second.currencyID] = destDef;
1078                 }
1079
1080                 // if currency is controlled by this chain and it hasn't started yet, skip it
1081                 if (destDef.systemID == ASSETCHAINS_CHAINID && destDef.startBlock > nHeight)
1082                 {
1083                     continue;
1084                 }
1085
1086                 if (destDef.systemID == ASSETCHAINS_CHAINID)
1087                 {
1088                     // see if the chain has failed to launch, and we haven't recorded that yet, do so
1089                     CCurrencyValueMap minPreMap, preConvertedMap;
1090                     uint160 destID = destDef.GetID();
1091                     if (!failedLaunches.count(destID) &&
1092                         destDef.minPreconvert.size() &&
1093                         (minPreMap = CCurrencyValueMap(destDef.currencies, destDef.minPreconvert)) > preConvertedMap &&
1094                         (preConvertedMap = CCurrencyValueMap(destDef.currencies, GetInitialCurrencyState(destDef).reserveIn)) < minPreMap)
1095                     {
1096                         failedLaunches.insert(destID);
1097                     }
1098                     // if this currency is controlled by the ID that created it, consider the currency def its own system
1099                     // even for these types of currencies, pre-convert is considered an automatic chain function controlled by this
1100                     // chain. If any centralized participatory control is desired, it will currenty require that qualified
1101                     // pre-conversions be handled off chain and posted by the currency's controller. All chain defined preconversions
1102                     // are available to all participants.
1103                     if (destDef.proofProtocol == CCurrencyDefinition::PROOF_CHAINID && !(output.second.second.flags & CReserveTransfer::PRECONVERT))
1104                     {
1105                         // controller of the currency is responsible for fielding export transactions
1106                         systemDef = destDef;
1107                     }
1108                     else
1109                     {
1110                         systemDef = ConnectedChains.ThisChain();
1111                     }
1112                     currencyDefCache[ASSETCHAINS_CHAINID] = systemDef;
1113                 }
1114                 else
1115                 {
1116                     it = currencyDefCache.find(destDef.systemID);
1117                     if ((it != currencyDefCache.end() && !(systemDef = it->second).IsValid()) ||
1118                         (it == currencyDefCache.end() && !GetCurrencyDefinition(destDef.systemID, systemDef)))
1119                     {
1120                         printf("%s: definition for export system ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
1121                         LogPrintf("%s: definition for export system ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
1122                         continue;
1123                     }
1124                     if (it == currencyDefCache.end())
1125                     {
1126                         currencyDefCache[destDef.systemID] = systemDef;
1127                     }
1128                 }
1129
1130                 // if tokens on this chain, group like currencies together, otherwise don't
1131                 if (systemDef.GetID() == thisChainID)
1132                 {
1133                     sortedTransferOutputs.insert({{output.first, output.second.second.currencyID}, output.second});
1134                 }
1135                 else
1136                 {
1137                     sortedTransferOutputs.insert({{output.first, uint160()}, output.second});
1138                 }
1139             }
1140
1141             std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
1142             std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1143
1144             // this pair will sort to the end
1145             std::pair<uint160, uint160> bookEnd({uint160(ParseHex("ffffffffffffffffffffffffffffffffffffffff")), uint160(ParseHex("ffffffffffffffffffffffffffffffffffffffff"))});
1146             std::pair<uint160, uint160> lastChain = bookEnd;
1147
1148             // add a bookend entry at the end of transfer outputs to ensure that we try to export all before it
1149             sortedTransferOutputs.insert(make_pair(bookEnd, make_pair(CInputDescriptor(), CReserveTransfer())));
1150
1151             CCurrencyDefinition lastChainDef;
1152
1153             // merge all of the common chainID outputs into common export transactions if either MIN_BLOCKS blocks have passed since the last
1154             // export of that type, or there are MIN_INPUTS or more outputs to aggregate
1155             for (auto it = sortedTransferOutputs.begin(); it != sortedTransferOutputs.end(); it++)
1156             {
1157                 // get chain target and see if it is the same
1158                 if (lastChain.first == bookEnd.first || it->first.first == lastChain.first)
1159                 {
1160                     txInputs.push_back(it->second);
1161                 }
1162                 else
1163                 {
1164                     // when we get here, we have a consecutive number of transfer outputs to consume in txInputs
1165                     // we need an unspent export output to export, or use the last one of it is an export to the same
1166                     // system
1167                     if (GetUnspentChainExports(lastChain.first, exportOutputs) && exportOutputs.size())
1168                     {
1169                         auto lastExport = *exportOutputs.begin();
1170
1171                         if (((nHeight - lastExport.second.first) >= CCrossChainExport::MIN_BLOCKS) || (txInputs.size() >= CCrossChainExport::MIN_INPUTS))
1172                         {
1173                             boost::optional<CTransaction> oneExport;
1174
1175                             // make one or more transactions that spends the last export and all possible cross chain transfers
1176                             while (txInputs.size())
1177                             {
1178                                 TransactionBuilder tb(Params().GetConsensus(), nHeight);
1179
1180                                 int numInputs = 0;
1181                                 int inputsLeft = txInputs.size();
1182
1183                                 if (it->first.second.IsNull())
1184                                 {
1185                                     numInputs = txInputs.size();
1186                                 }
1187                                 else
1188                                 {
1189                                     for (int i = 0; i < txInputs.size(); i++)
1190                                     {
1191                                         if (txInputs[i].second.currencyID == it->first.second)
1192                                         {
1193                                             numInputs++;
1194                                         }
1195                                         else
1196                                         {
1197                                             break;
1198                                         }
1199                                     }
1200
1201                                 }
1202                                 if (numInputs > CCrossChainExport::MAX_EXPORT_INPUTS)
1203                                 {
1204                                     numInputs = CCrossChainExport::MAX_EXPORT_INPUTS;
1205                                 }
1206                                 inputsLeft = txInputs.size() - numInputs;
1207                                 if (inputsLeft > 0 && inputsLeft < CCrossChainExport::MIN_INPUTS)
1208                                 {
1209                                     inputsLeft += CCrossChainExport::MIN_INPUTS - inputsLeft;
1210                                     numInputs -= CCrossChainExport::MIN_INPUTS - inputsLeft;
1211                                     assert(numInputs > 0);
1212                                 }
1213
1214                                 // each time through, we make one export transaction with the remainder or a subset of the
1215                                 // reserve transfer inputs. inputs can be:
1216                                 // 1. transfers of reserve for fractional reserve chains
1217                                 // 2. pre-conversions for pre-launch participation in the premine
1218                                 // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
1219                                 //
1220                                 // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
1221                                 // or reserve token inputs.
1222                                 //
1223                                 // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
1224                                 // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
1225                                 // as part of the import process
1226                                 // 
1227                                 // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain. 
1228                                 // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the 
1229                                 // remaining Verus reserve coin will be burned on the PBaaS chain as spending it is allowed, once notarized, on the
1230                                 // Verus chain.
1231                                 //
1232                                 CCurrencyValueMap totalTxFees;
1233                                 CCurrencyValueMap totalAmounts;
1234                                 CAmount exportOutVal = 0;
1235                                 std::vector<CBaseChainObject *> chainObjects;
1236
1237                                 // first, we must add the export output from the current export thread to this chain
1238                                 if (oneExport.is_initialized())
1239                                 {
1240                                     // spend the last export transaction output
1241                                     CTransaction &tx = oneExport.get();
1242                                     COptCCParams p;
1243                                     int j;
1244                                     for (j = 0; j < tx.vout.size(); j++)
1245                                     {
1246                                         if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) && p.evalCode == EVAL_CROSSCHAIN_EXPORT)
1247                                         {
1248                                             break;
1249                                         }
1250                                     }
1251
1252                                     // had to be found and valid if we made the tx
1253                                     assert(j < tx.vout.size() && p.IsValid());
1254
1255                                     tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
1256                                     exportOutVal = tx.vout[j].nValue;
1257                                 }
1258                                 else
1259                                 {
1260                                     // spend the recentExportIt output
1261                                     tb.AddTransparentInput(lastExport.second.second.txIn.prevout, lastExport.second.second.scriptPubKey, lastExport.second.second.nValue);
1262                                     exportOutVal = lastExport.second.second.nValue;
1263                                 }
1264
1265                                 COptCCParams p;
1266                                 std::vector<int> toRemove;
1267                                 for (int j = 0; j < numInputs; j++)
1268                                 {
1269                                     tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
1270                                     CCurrencyValueMap newTransferInput = txInputs[j].first.scriptPubKey.ReserveOutValue();
1271                                     newTransferInput.valueMap[ASSETCHAINS_CHAINID] = txInputs[j].first.nValue;
1272
1273                                     // TODO: make fee currency calculation more flexible on conversion
1274                                     // rules should be pay fee in native currency of source system
1275                                     uint160 feeCurrencyID = currencyDefCache[txInputs[j].second.currencyID].systemID;
1276
1277                                     CCurrencyValueMap newTransferOutput;
1278                                     newTransferOutput.valueMap[txInputs[j].second.currencyID] = txInputs[j].second.nValue;
1279                                     newTransferOutput.valueMap[feeCurrencyID] += txInputs[j].second.nFees;
1280
1281                                     if ((newTransferInput - newTransferOutput).HasNegative())
1282                                     {
1283                                         // 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
1284                                         // 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
1285                                         // we should formalize this into a chain contribution or amount adjustment.
1286                                         printf("%s: transaction %s claims incorrect value\n", __func__, txInputs[j].first.txIn.prevout.hash.GetHex().c_str());
1287                                         LogPrintf("%s: transaction %s claims incorrect value\n", __func__, txInputs[j].first.txIn.prevout.hash.GetHex().c_str());
1288                                         toRemove.push_back(j);
1289                                     }
1290                                     else
1291                                     {
1292                                         totalAmounts += newTransferOutput;
1293                                         totalTxFees.valueMap[feeCurrencyID] += txInputs[j].second.nFees;
1294                                         CReserveTransfer rt(txInputs[j].second);
1295                                         if (failedLaunches.count(rt.currencyID))
1296                                         {
1297                                             // turn it into a normal transfer, which will create an unconverted output
1298                                             rt.flags &= ~(CReserveTransfer::SEND_BACK | CReserveTransfer::PRECONVERT | CReserveTransfer::CONVERT);
1299                                         }
1300                                         chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(rt), rt));
1301                                     }
1302                                 }
1303                                 // remove in reverse order so one removal does not affect the position of the next
1304                                 for (int j = toRemove.size() - 1; j >= 0; j--)
1305                                 {
1306                                     txInputs.erase(txInputs.begin() + toRemove[j]);
1307                                     numInputs--;
1308                                 }
1309                                 if (!numInputs)
1310                                 {
1311                                     continue;
1312                                 }
1313
1314                                 CCrossChainExport ccx(lastChain.first, numInputs, totalAmounts, totalTxFees);
1315
1316                                 // make extra outputs for fees in each currency
1317                                 for (auto &outPair : ccx.CalculateExportFee().valueMap)
1318                                 {
1319                                     CReserveTransfer feeOut(CReserveTransfer::VALID + CReserveTransfer::FEE_OUTPUT, 
1320                                                             outPair.first, outPair.second, 0, lastChain.first, DestinationToTransferDestination(feeOutput));
1321                                     chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
1322                                 }
1323
1324                                 // do a preliminary check
1325                                 CReserveTransactionDescriptor rtxd;
1326                                 std::vector<CTxOut> vOutputs;
1327
1328                                 if (!rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain().GetID(), lastChainDef, chainObjects, vOutputs))
1329                                 {
1330                                     DeleteOpRetObjects(chainObjects);
1331
1332                                     printf("%s: failed to create valid exports\n", __func__);
1333                                     LogPrintf("%s: failed to create valid exports\n", __func__);
1334
1335                                     // debugging output
1336                                     printf("%s: failed to export outputs:\n", __func__);
1337                                     for (auto oneout : vOutputs)
1338                                     {
1339                                         UniValue uniOut;
1340                                         ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1341                                         printf("%s\n", uniOut.write(true, 2).c_str());
1342                                     }
1343                                 }
1344                                 else
1345                                 {
1346                                     CCcontract_info CC;
1347                                     CCcontract_info *cp;
1348
1349                                     // debugging out
1350                                     printf("%s: exported outputs:\n", __func__);
1351                                     for (auto &oneout : chainObjects)
1352                                     {
1353                                         if (oneout->objectType == CHAINOBJ_RESERVETRANSFER)
1354                                         {
1355                                             CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)(oneout))->object;
1356                                             printf("%s\n", rt.ToUniValue().write(true, 2).c_str());
1357                                         }
1358                                     }
1359
1360                                     CScript opRet = StoreOpRetArray(chainObjects);
1361                                     DeleteOpRetObjects(chainObjects);
1362
1363                                     cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
1364                                     for (auto &oneCurrencyOut : ccx.totalAmounts.valueMap)
1365                                     {
1366                                         CCurrencyDefinition oneDef = currencyDefCache[oneCurrencyOut.first];
1367                                         // if we control this currency, it must go to reserve deposit of the system it is
1368                                         // being exported to
1369                                         if (oneDef.systemID == ASSETCHAINS_CHAINID)
1370                                         {
1371                                             CAmount nativeOut = oneDef.GetID() == ASSETCHAINS_CHAINID ? oneCurrencyOut.second : 0;
1372
1373                                             // send the entire amount to a reserve transfer output of the specific chain
1374                                             // we receive our fee on the other chain or when it comes back
1375                                             std::vector<CTxDestination> indexDests({CKeyID(CCrossChainRPCData::GetConditionID(oneDef.GetID(), EVAL_RESERVE_DEPOSIT))});
1376                                             std::vector<CTxDestination> dests({CPubKey(ParseHex(CC.CChexstr))});
1377
1378                                             CTokenOutput ro(oneCurrencyOut.first, oneCurrencyOut.second);
1379
1380                                             tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_DEPOSIT, dests, 1, &ro), &indexDests), 
1381                                                                     nativeOut);
1382                                         }
1383                                     }
1384
1385                                     cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
1386
1387                                     // send native amount of zero to a cross chain export output of the specific chain
1388                                     std::vector<CTxDestination> indexDests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain.first, EVAL_CROSSCHAIN_EXPORT))});
1389                                     std::vector<CTxDestination> dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
1390
1391                                     tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx), &indexDests),
1392                                                             exportOutVal);
1393
1394                                     tb.AddOpRet(opRet);
1395                                     tb.SetFee(0);
1396
1397                                     TransactionBuilderResult buildResult(tb.Build());
1398
1399                                     if (!buildResult.IsError() && buildResult.IsTx())
1400                                     {
1401                                         // replace the last one only if we have a valid new one
1402                                         CTransaction tx = buildResult.GetTxOrThrow();
1403
1404                                         LOCK2(cs_main, mempool.cs);
1405                                         static int lastHeight = 0;
1406                                         // remove conflicts, so that we get in
1407                                         std::list<CTransaction> removed;
1408                                         mempool.removeConflicts(tx, removed);
1409
1410                                         // add to mem pool, prioritize according to the fee we will get, and relay
1411                                         printf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1412                                         LogPrintf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1413                                         if (myAddtomempool(tx))
1414                                         {
1415                                             uint256 hash = tx.GetHash();
1416                                             CAmount nativeExportFees = ccx.totalFees.valueMap[ASSETCHAINS_CHAINID];
1417                                             mempool.PrioritiseTransaction(hash, hash.GetHex(), (double)(nativeExportFees << 1), nativeExportFees);
1418                                             RelayTransaction(tx);
1419                                         }
1420                                     }
1421                                     else
1422                                     {
1423                                         // we can't do any more useful work for this chain if we failed here
1424                                         printf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1425                                         LogPrintf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1426                                         break;
1427                                     }
1428                                 }
1429
1430                                 // erase the inputs we've attempted to spend
1431                                 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1432                             }
1433                         }
1434                     }
1435                 }
1436                 lastChain = it->first;
1437             }
1438         }
1439     }
1440 }
1441
1442 // send new imports from this chain to the specified chain, which generally will be the notary chain
1443 void CConnectedChains::SendNewImports(const uint160 &chainID, 
1444                                       const CPBaaSNotarization &notarization, 
1445                                       const uint256 &lastExportTx, 
1446                                       const CTransaction &lastCrossImport, 
1447                                       const CTransaction &lastExport)
1448 {
1449     // currently only support sending imports to  
1450 }
1451
1452 void CConnectedChains::SubmissionThread()
1453 {
1454     try
1455     {
1456         arith_uint256 lastHash;
1457         int64_t lastImportTime = 0;
1458         uint32_t lastHeight = 0;
1459         
1460         // wait for something to check on, then submit blocks that should be submitted
1461         while (true)
1462         {
1463             boost::this_thread::interruption_point();
1464
1465             if (IsVerusActive())
1466             {
1467                 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
1468                 //printf("SubmissionThread: pruning\n");
1469                 PruneOldChains(GetAdjustedTime() - 300);
1470                 bool submit = false;
1471                 {
1472                     LOCK(cs_mergemining);
1473                     if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
1474                     {
1475                         qualifiedHeaders.clear();
1476                     }
1477                     submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
1478
1479                     //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
1480                 }
1481                 if (submit)
1482                 {
1483                     //printf("SubmissionThread: calling submit qualified blocks\n");
1484                     SubmitQualifiedBlocks();
1485                 }
1486                 else
1487                 {
1488                     //printf("SubmissionThread: waiting on sem\n");
1489                     sem_submitthread.wait();
1490                 }
1491             }
1492             else
1493             {
1494                 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
1495                 if (CheckVerusPBaaSAvailable())
1496                 {
1497                     // check to see if we have recently earned a block with an earned notarization that qualifies for
1498                     // submitting an accepted notarization
1499                     if (earnedNotarizationHeight)
1500                     {
1501                         CBlock blk;
1502                         int32_t txIndex = -1, height;
1503                         {
1504                             LOCK(cs_mergemining);
1505                             if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
1506                             {
1507                                 blk = earnedNotarizationBlock;
1508                                 earnedNotarizationBlock = CBlock();
1509                                 txIndex = earnedNotarizationIndex;
1510                                 height = earnedNotarizationHeight;
1511                                 earnedNotarizationHeight = 0;
1512                             }
1513                         }
1514
1515                         if (txIndex != -1)
1516                         {
1517                             //printf("SubmissionThread: testing notarization\n");
1518                             CTransaction lastConfirmed;
1519                             uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
1520
1521                             if (!txId.IsNull())
1522                             {
1523                                 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1524                                 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1525                             }
1526                         }
1527                     }
1528
1529                     // every "n" seconds, look for imports to include in our blocks from the Verus chain
1530                     if ((GetAdjustedTime() - lastImportTime) >= 30 || lastHeight < (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight()))
1531                     {
1532                         lastImportTime = GetAdjustedTime();
1533                         lastHeight = (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight());
1534
1535                         // see if our notary has a confirmed notarization for us
1536                         UniValue params(UniValue::VARR);
1537                         UniValue result;
1538
1539                         params.push_back(thisChain.name);
1540
1541                         try
1542                         {
1543                             result = find_value(RPCCallRoot("getlastimportin", params), "result");
1544                         } catch (exception e)
1545                         {
1546                             result = NullUniValue;
1547                         }
1548
1549                         if (!result.isNull())
1550                         {
1551                             auto txUniStr = find_value(result, "lastimporttransaction");
1552                             auto txLastConfirmedStr = find_value(result, "lastconfirmednotarization");
1553                             auto txTemplateStr = find_value(result, "importtxtemplate");
1554                             CAmount nativeImportAvailable = uni_get_int64(find_value(result, "nativeimportavailable"));
1555                             CCurrencyValueMap tokenImportAvailable(find_value(params[0], "tokenimportavailable"));
1556
1557                             CTransaction lastImportTx, lastConfirmedTx, templateTx;
1558
1559                             if (txUniStr.isStr() && txTemplateStr.isStr() && 
1560                                 DecodeHexTx(lastImportTx, txUniStr.get_str()) && 
1561                                 DecodeHexTx(lastConfirmedTx, txLastConfirmedStr.get_str()) && 
1562                                 DecodeHexTx(templateTx, txTemplateStr.get_str()))
1563                             {
1564                                 std::vector<CTransaction> importTxes;
1565                                 if (CreateLatestImports(notaryChain.chainDefinition, lastImportTx, templateTx, lastConfirmedTx, tokenImportAvailable, nativeImportAvailable, importTxes))
1566                                 {
1567                                     for (auto importTx : importTxes)
1568                                     {
1569                                         UniValue txResult;
1570                                         params.setArray();
1571                                         params.push_back(EncodeHexTx(importTx));
1572
1573                                         try
1574                                         {
1575                                             txResult = find_value(RPCCallRoot("signrawtransaction", params), "result");
1576                                             if (txResult.isObject() && !(txResult = find_value(txResult, "hex")).isNull() && txResult.isStr() && txResult.get_str().size())
1577                                             {
1578                                                 params.setArray();
1579                                                 params.push_back(txResult);
1580                                                 txResult = find_value(RPCCallRoot("sendrawtransaction", params), "result");
1581                                             }
1582                                             else
1583                                             {
1584                                                 txResult = NullUniValue;
1585                                             }
1586                                             
1587                                         } catch (exception e)
1588                                         {
1589                                             txResult = NullUniValue;
1590                                         }
1591                                         uint256 testId;
1592                                         if (txResult.isStr())
1593                                         {
1594                                             testId.SetHex(txResult.get_str());
1595                                         }
1596                                         if (testId.IsNull())
1597                                         {
1598                                             break;
1599                                         }
1600                                     }
1601                                 }
1602                             }
1603                         }
1604                     }
1605                 }
1606                 sleep(3);
1607             }
1608             boost::this_thread::interruption_point();
1609         }
1610     }
1611     catch (const boost::thread_interrupted&)
1612     {
1613         LogPrintf("Verus merge mining thread terminated\n");
1614     }
1615 }
1616
1617 void CConnectedChains::SubmissionThreadStub()
1618 {
1619     ConnectedChains.SubmissionThread();
1620 }
1621
1622 void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
1623 {
1624     // called after winning a block that contains an earned notarization
1625     // the earned notarization and its height are queued for processing by the submission thread
1626     // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
1627     // kept
1628     LOCK(cs_mergemining);
1629
1630     // we only care about the last
1631     earnedNotarizationHeight = height;
1632     earnedNotarizationBlock = blk;
1633     earnedNotarizationIndex = txIndex;
1634 }
1635
1636 bool IsChainDefinitionInput(const CScript &scriptSig)
1637 {
1638     uint32_t ecode;
1639     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_CURRENCY_DEFINITION;
1640 }
1641
This page took 0.121616 seconds and 4 git commands to generate.