]> Git Repo - VerusCoin.git/blob - src/pbaas/pbaas.cpp
Merge pull request #60 from VerusCoin/dev
[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 "pbaas/crosschainrpc.h"
14 #include "pbaas/pbaas.h"
15 #include "pbaas/notarization.h"
16 #include "pbaas/identity.h"
17 #include "rpc/pbaasrpc.h"
18 #include "base58.h"
19 #include "timedata.h"
20 #include "main.h"
21 #include "transaction_builder.h"
22
23 using namespace std;
24
25 CConnectedChains ConnectedChains;
26
27 bool IsVerusActive()
28 {
29     return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 0 || strcmp(ASSETCHAINS_SYMBOL, "VRSCTEST") == 0);
30 }
31
32 // this adds an opret to a mutable transaction and returns the voutnum if it could be added
33 int32_t AddOpRetOutput(CMutableTransaction &mtx, const CScript &opRetScript)
34 {
35     if (opRetScript.IsOpReturn() && opRetScript.size() <= MAX_OP_RETURN_RELAY)
36     {
37         CTxOut vOut = CTxOut();
38         vOut.scriptPubKey = opRetScript;
39         vOut.nValue = 0;
40         mtx.vout.push_back(vOut);
41         return mtx.vout.size() - 1;
42     }
43     else
44     {
45         return -1;
46     }
47 }
48
49 // returns a pointer to a base chain object, which can be cast to the
50 // object type indicated in its objType member
51 uint256 GetChainObjectHash(const CBaseChainObject &bo)
52 {
53     union {
54         const CChainObject<CBlockHeader> *pNewHeader;
55         const CChainObject<CTransaction> *pNewTx;
56         const CChainObject<CMerkleBranch> *pNewProof;
57         const CChainObject<CHeaderRef> *pNewHeaderRef;
58         const CChainObject<CPriorBlocksCommitment> *pPriors;
59         const CChainObject<CReserveTransfer> *pExport;
60         const CChainObject<CCrossChainProof> *pCrossChainProof;
61         const CBaseChainObject *retPtr;
62     };
63
64     retPtr = &bo;
65
66     switch(bo.objectType)
67     {
68         case CHAINOBJ_HEADER:
69             return pNewHeader->GetHash();
70
71         case CHAINOBJ_TRANSACTION:
72             return pNewTx->GetHash();
73
74         case CHAINOBJ_PROOF:
75             return pNewProof->GetHash();
76
77         case CHAINOBJ_HEADER_REF:
78             return pNewHeaderRef->GetHash();
79
80         case CHAINOBJ_PRIORBLOCKS:
81             return pPriors->GetHash();
82
83         case CHAINOBJ_RESERVETRANSFER:
84             return pExport->GetHash();
85
86         case CHAINOBJ_CROSSCHAINPROOF:
87             return pCrossChainProof->GetHash();
88
89     }
90     return uint256();
91 }
92
93 // used to export coins from one chain to another, if they are not native, they are represented on the other
94 // chain as tokens
95 bool ValidateCrossChainExport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
96 {
97     return true;
98 }
99
100 bool IsCrossChainExportInput(const CScript &scriptSig)
101 {
102     return true;
103 }
104
105 // used to validate import of coins from one chain to another. if they are not native and are supported,
106 // they are represented o the chain as tokens
107 bool ValidateCrossChainImport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
108 {
109     return true;
110 }
111 bool IsCrossChainImportInput(const CScript &scriptSig)
112 {
113     return true;
114 }
115
116 // used to validate a specific service reward based on the spending transaction
117 bool ValidateServiceReward(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
118 {
119     // for each type of service reward, we need to check and see if the spender is
120     // correctly formatted to be a valid spend of the service reward. for notarization
121     // we ensure that the notarization and its outputs are valid and that the spend
122     // applies to the correct billing period
123     return true;
124 }
125 bool IsServiceRewardInput(const CScript &scriptSig)
126 {
127     return true;
128 }
129
130 // used as a proxy token output for a reserve currency on its fractional reserve chain
131 bool ValidateReserveOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
132 {
133     return true;
134 }
135 bool IsReserveOutputInput(const CScript &scriptSig)
136 {
137     return true;
138 }
139
140 bool ValidateReserveTransfer(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
141 {
142     return true;
143 }
144 bool IsReserveTransferInput(const CScript &scriptSig)
145 {
146     return true;
147 }
148
149 bool ValidateReserveDeposit(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
150 {
151     return true;
152 }
153 bool IsReserveDepositInput(const CScript &scriptSig)
154 {
155     return true;
156 }
157
158 bool ValidateCurrencyState(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
159 {
160     return true;
161 }
162 bool IsCurrencyStateInput(const CScript &scriptSig)
163 {
164     return true;
165 }
166
167 // used to convert a fractional reserve currency into its reserve and back 
168 bool ValidateReserveExchange(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
169 {
170     return true;
171 }
172 bool IsReserveExchangeInput(const CScript &scriptSig)
173 {
174     return true;
175 }
176
177
178 /*
179  * Verifies that the input objects match the hashes and returns the transaction.
180  * 
181  * If the opRetTx has the op ret, this calculates based on the actual transaction and
182  * validates the hashes. If the opRetTx does not have the opRet itself, this validates
183  * by ensuring that all objects are present on this chain, composing the opRet, and
184  * ensuring that the transaction then hashes to the correct txid.
185  * 
186  */
187 bool ValidateOpretProof(CScript &opRet, COpRetProof &orProof)
188 {
189     // enumerate through the objects and validate that they are objects of the expected type that hash
190     // to the value expected. return true if so
191     return true;
192 }
193
194 int8_t ObjTypeCode(const CBlockHeader &obj)
195 {
196     return CHAINOBJ_HEADER;
197 }
198
199 int8_t ObjTypeCode(const CMerkleBranch &obj)
200 {
201     return CHAINOBJ_PROOF;
202 }
203
204 int8_t ObjTypeCode(const CTransaction &obj)
205 {
206     return CHAINOBJ_TRANSACTION;
207 }
208
209 int8_t ObjTypeCode(const CHeaderRef &obj)
210 {
211     return CHAINOBJ_HEADER_REF;
212 }
213
214 int8_t ObjTypeCode(const CPriorBlocksCommitment &obj)
215 {
216     return CHAINOBJ_PRIORBLOCKS;
217 }
218
219 int8_t ObjTypeCode(const CReserveTransfer &obj)
220 {
221     return CHAINOBJ_RESERVETRANSFER;
222 }
223
224 int8_t ObjTypeCode(const CCrossChainProof &obj)
225 {
226     return CHAINOBJ_CROSSCHAINPROOF;
227 }
228
229 // this adds an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction
230 CScript StoreOpRetArray(std::vector<CBaseChainObject *> &objPtrs)
231 {
232     CScript vData;
233     CDataStream s = CDataStream(SER_NETWORK, PROTOCOL_VERSION);
234     s << (int32_t)OPRETTYPE_OBJECTARR;
235     bool error = false;
236
237     for (auto pobj : objPtrs)
238     {
239         try
240         {
241             if (!DehydrateChainObject(s, pobj))
242             {
243                 error = true;
244                 break;
245             }
246         }
247         catch(const std::exception& e)
248         {
249             std::cerr << e.what() << '\n';
250             error = true;
251             break;
252         }
253     }
254
255     //std::vector<unsigned char> schars(s.begin(), s.begin() + 200);
256     //printf("stream vector chars: %s\n", HexBytes(&schars[0], schars.size()).c_str());
257
258     std::vector<unsigned char> vch(s.begin(), s.end());
259     return error ? CScript() : CScript() << OP_RETURN << vch;
260 }
261
262 void DeleteOpRetObjects(std::vector<CBaseChainObject *> &ora)
263 {
264     for (auto pobj : ora)
265     {
266         switch(pobj->objectType)
267         {
268             case CHAINOBJ_HEADER:
269             {
270                 delete (CChainObject<CBlockHeader> *)pobj;
271                 break;
272             }
273
274             case CHAINOBJ_TRANSACTION:
275             {
276                 delete (CChainObject<CTransaction> *)pobj;
277                 break;
278             }
279
280             case CHAINOBJ_PROOF:
281             {
282                 delete (CChainObject<CMerkleBranch> *)pobj;
283                 break;
284             }
285
286             case CHAINOBJ_HEADER_REF:
287             {
288                 delete (CChainObject<CHeaderRef> *)pobj;
289                 break;
290             }
291
292             case CHAINOBJ_PRIORBLOCKS:
293             {
294                 delete (CChainObject<CPriorBlocksCommitment> *)pobj;
295                 break;
296             }
297
298             case CHAINOBJ_RESERVETRANSFER:
299             {
300                 delete (CChainObject<CReserveTransfer> *)pobj;
301                 break;
302             }
303
304             case CHAINOBJ_CROSSCHAINPROOF:
305             {
306                 delete (CChainObject<CCrossChainProof> *)pobj;
307                 break;
308             }
309
310             default:
311                 delete pobj;
312         }
313     }
314     ora.clear();
315 }
316
317 std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
318 {
319     std::vector<unsigned char> vch;
320     std::vector<CBaseChainObject *> vRet;
321     if (opRetScript.IsOpReturn() && GetOpReturnData(opRetScript, vch) && vch.size() > 0)
322     {
323         CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION);
324
325         int32_t opRetType;
326
327         try
328         {
329             s >> opRetType;
330             if (opRetType == OPRETTYPE_OBJECTARR)
331             {
332                 CBaseChainObject *pobj;
333                 while (!s.empty() && (pobj = RehydrateChainObject(s)))
334                 {
335                     vRet.push_back(pobj);
336                 }
337                 if (!s.empty())
338                 {
339                     printf("failed to load all objects in opret");
340                     DeleteOpRetObjects(vRet);
341                     vRet.clear();
342                 }
343             }
344         }
345         catch(const std::exception& e)
346         {
347             std::cerr << e.what() << '\n';
348             DeleteOpRetObjects(vRet);
349             vRet.clear();
350         }
351     }
352     return vRet;
353 }
354
355 CNodeData::CNodeData(UniValue &obj)
356 {
357     networkAddress = uni_get_str(find_value(obj, "networkaddress"));
358     CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
359     ba.GetKeyID(paymentAddress);
360 }
361
362 UniValue CNodeData::ToUniValue() const
363 {
364     UniValue obj(UniValue::VOBJ);
365     obj.push_back(Pair("networkaddress", networkAddress));
366     obj.push_back(Pair("paymentaddress", CBitcoinAddress(paymentAddress).ToString()));
367     return obj;
368 }
369
370 CPBaaSChainDefinition::CPBaaSChainDefinition(const UniValue &obj)
371 {
372     nVersion = PBAAS_VERSION;
373     name = std::string(uni_get_str(find_value(obj, "name")), 0, (KOMODO_ASSETCHAIN_MAXLEN - 1));
374
375     string invalidChars = "\\/:*?\"<>|";
376     for (int i = 0; i < name.size(); i++)
377     {
378         if (invalidChars.find(name[i]) != string::npos)
379         {
380             name[i] = '_';
381         }
382     }
383
384     CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
385     ba.GetKeyID(address);
386     premine = uni_get_int64(find_value(obj, "premine"));
387     initialcontribution = uni_get_int64(find_value(obj, "initialcontribution"));
388     conversion = uni_get_int64(find_value(obj, "conversion"));
389     minpreconvert = uni_get_int64(find_value(obj, "minpreconvert"));
390     maxpreconvert = uni_get_int64(find_value(obj, "maxpreconvert"));
391     preconverted = uni_get_int64(find_value(obj, "preconverted"));
392     launchFee = uni_get_int64(find_value(obj, "launchfee"));
393     startBlock = uni_get_int(find_value(obj, "startblock"));
394     endBlock = uni_get_int(find_value(obj, "endblock"));
395
396     auto vEras = uni_getValues(find_value(obj, "eras"));
397     if (vEras.size() > ASSETCHAINS_MAX_ERAS)
398     {
399         vEras.resize(ASSETCHAINS_MAX_ERAS);
400     }
401
402     for (auto era : vEras)
403     {
404         rewards.push_back(uni_get_int64(find_value(era, "reward")));
405         rewardsDecay.push_back(uni_get_int64(find_value(era, "decay")));
406         halving.push_back(uni_get_int64(find_value(era, "halving")));
407         eraEnd.push_back(uni_get_int64(find_value(era, "eraend")));
408         eraOptions.push_back(uni_get_int64(find_value(era, "eraoptions")));
409     }
410
411     billingPeriod = uni_get_int(find_value(obj, "billingperiod"));
412     notarizationReward = uni_get_int64(find_value(obj, "notarizationreward"));
413
414     auto nodeVec = uni_getValues(find_value(obj, "nodes"));
415     for (auto node : nodeVec)
416     {
417         nodes.push_back(CNodeData(node));
418     }
419 }
420
421 CPBaaSChainDefinition::CPBaaSChainDefinition(const CTransaction &tx, bool validate)
422 {
423     bool definitionFound = false;
424     nVersion = PBAAS_VERSION_INVALID;
425     for (auto out : tx.vout)
426     {
427         COptCCParams p;
428         if (IsPayToCryptoCondition(out.scriptPubKey, p))
429         {
430             if (p.evalCode == EVAL_PBAASDEFINITION)
431             {
432                 if (definitionFound)
433                 {
434                     nVersion = PBAAS_VERSION_INVALID;
435                 }
436                 else
437                 {
438                     FromVector(p.vData[0], *this);
439                     definitionFound = true;
440                 }
441             }
442         }
443     }
444
445     if (validate)
446     {
447         
448     }
449 }
450
451 CServiceReward::CServiceReward(const CTransaction &tx, bool validate)
452 {
453     nVersion = PBAAS_VERSION_INVALID;
454     for (auto out : tx.vout)
455     {
456         COptCCParams p;
457         if (IsPayToCryptoCondition(out.scriptPubKey, p))
458         {
459             // always take the first for now
460             if (p.evalCode == EVAL_SERVICEREWARD)
461             {
462                 FromVector(p.vData[0], *this);
463                 break;
464             }
465         }
466     }
467
468     if (validate)
469     {
470         
471     }
472 }
473
474 CCrossChainExport::CCrossChainExport(const CTransaction &tx)
475 {
476     for (auto out : tx.vout)
477     {
478         COptCCParams p;
479         if (IsPayToCryptoCondition(out.scriptPubKey, p))
480         {
481             // always take the first for now
482             if (p.evalCode == EVAL_CROSSCHAIN_EXPORT)
483             {
484                 FromVector(p.vData[0], *this);
485                 break;
486             }
487         }
488     }
489 }
490
491 uint160 CPBaaSChainDefinition::GetChainID(std::string name)
492 {
493     uint160 parent;
494     return CIdentity::GetID(name, parent);
495 }
496
497 uint160 CPBaaSChainDefinition::GetConditionID(int32_t condition) const
498 {
499     return CCrossChainRPCData::GetConditionID(name, condition);
500 }
501
502 UniValue CPBaaSChainDefinition::ToUniValue() const
503 {
504     UniValue obj(UniValue::VOBJ);
505     obj.push_back(Pair("version", (int64_t)nVersion));
506     obj.push_back(Pair("name", name));
507     obj.push_back(Pair("chainid", GetChainID().GetHex()));
508     obj.push_back(Pair("chainaddress", EncodeDestination(CTxDestination(CKeyID(GetChainID())))));
509     obj.push_back(Pair("paymentaddress", EncodeDestination(CTxDestination(address))));
510     obj.push_back(Pair("premine", (int64_t)premine));
511     obj.push_back(Pair("initialcontribution", (int64_t)initialcontribution));
512     obj.push_back(Pair("conversion", (int64_t)conversion));
513     obj.push_back(Pair("minpreconvert", (int64_t)minpreconvert));
514     obj.push_back(Pair("maxpreconvert", (int64_t)maxpreconvert));
515     obj.push_back(Pair("preconverted", (int64_t)preconverted));
516     obj.push_back(Pair("launchfee", (int64_t)launchFee));
517     obj.push_back(Pair("conversionpercent", ValueFromAmount(conversion * 100)));
518     obj.push_back(Pair("launchfeepercent", ValueFromAmount(launchFee * 100)));
519     obj.push_back(Pair("startblock", (int32_t)startBlock));
520     obj.push_back(Pair("endblock", (int32_t)endBlock));
521
522     UniValue eraArr(UniValue::VARR);
523     for (int i = 0; i < rewards.size(); i++)
524     {
525         UniValue era(UniValue::VOBJ);
526         era.push_back(Pair("reward", rewards.size() > i ? rewards[i] : (int64_t)0));
527         era.push_back(Pair("decay", rewardsDecay.size() > i ? rewardsDecay[i] : (int64_t)0));
528         era.push_back(Pair("halving", halving.size() > i ? (int32_t)halving[i] : (int32_t)0));
529         era.push_back(Pair("eraend", eraEnd.size() > i ? (int32_t)eraEnd[i] : (int32_t)0));
530         era.push_back(Pair("eraoptions", eraOptions.size() > i ? (int32_t)eraOptions[i] : (int32_t)0));
531         eraArr.push_back(era);
532     }
533     obj.push_back(Pair("eras", eraArr));
534
535     obj.push_back(Pair("billingperiod", billingPeriod));
536     obj.push_back(Pair("notarizationreward", notarizationReward));
537
538     UniValue nodeArr(UniValue::VARR);
539     for (auto node : nodes)
540     {
541         nodeArr.push_back(node.ToUniValue());
542     }
543     obj.push_back(Pair("nodes", nodeArr));
544
545     return obj;
546 }
547
548 int CPBaaSChainDefinition::GetDefinedPort() const
549 {
550     int port;
551     string host;
552     for (auto node : nodes)
553     {
554         SplitHostPort(node.networkAddress, port, host);
555         if (port)
556         {
557             return port;
558         }
559     }
560     return 0;
561 }
562
563 #define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
564 extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
565 extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3], ASSETCHAINS_ERAOPTIONS[3];
566 extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
567 extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
568 extern std::string VERUS_CHAINNAME;
569
570 // adds the chain definition for this chain and nodes as well
571 // this also sets up the notarization chain, if there is one
572 bool SetThisChain(UniValue &chainDefinition)
573 {
574     ConnectedChains.ThisChain() = CPBaaSChainDefinition(chainDefinition);
575
576     if (!IsVerusActive())
577     {
578         CPBaaSChainDefinition &notaryChainDef = ConnectedChains.notaryChain.chainDefinition;
579         // we set the notary chain to either Verus or VerusTest
580         notaryChainDef.nVersion = PBAAS_VERSION;
581         if (PBAAS_TESTMODE)
582         {
583             // setup Verus test parameters
584             notaryChainDef.name = "VRSCTEST";
585             notaryChainDef.premine = 5000000000000000;
586             notaryChainDef.rewards = std::vector<int64_t>({2400000000});
587             notaryChainDef.rewardsDecay = std::vector<int64_t>({0});
588             notaryChainDef.halving = std::vector<int32_t>({225680});
589             notaryChainDef.eraEnd = std::vector<int32_t>({0});
590             notaryChainDef.eraOptions = std::vector<int32_t>({notaryChainDef.OPTION_ID_REFERRALS});
591         }
592         else
593         {
594             // first setup Verus parameters
595             notaryChainDef.name = "VRSC";
596             notaryChainDef.premine = 0;
597             notaryChainDef.rewards = std::vector<int64_t>({0,38400000000,2400000000});
598             notaryChainDef.rewardsDecay = std::vector<int64_t>({100000000,0,0});
599             notaryChainDef.halving = std::vector<int32_t>({1,43200,1051920});
600             notaryChainDef.eraEnd = std::vector<int32_t>({10080,226080,0});
601             notaryChainDef.eraOptions = std::vector<int32_t>({notaryChainDef.OPTION_ID_REFERRALS,0,0});
602         }
603     }
604
605     if (ConnectedChains.ThisChain().IsValid())
606     {
607         memset(ASSETCHAINS_SYMBOL, 0, sizeof(ASSETCHAINS_SYMBOL));
608         strcpy(ASSETCHAINS_SYMBOL, ConnectedChains.ThisChain().name.c_str());
609
610         // set all command line parameters into mapArgs from chain definition
611         vector<string> nodeStrs;
612         for (auto node : ConnectedChains.ThisChain().nodes)
613         {
614             nodeStrs.push_back(node.networkAddress);
615         }
616         if (nodeStrs.size())
617         {
618             mapMultiArgs["-seednode"] = nodeStrs;
619         }
620         if (int port = ConnectedChains.ThisChain().GetDefinedPort())
621         {
622             mapArgs["-port"] = to_string(port);
623         }
624
625         ASSETCHAINS_SUPPLY = ConnectedChains.ThisChain().premine;
626         mapArgs["-ac_supply"] = to_string(ASSETCHAINS_SUPPLY);
627         ASSETCHAINS_ALGO = ASSETCHAINS_VERUSHASH;
628         ASSETCHAINS_LWMAPOS = 50;
629
630         ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF;
631         ASSETCHAINS_TIMEUNLOCKFROM = 0;
632         ASSETCHAINS_TIMEUNLOCKTO = 0;
633
634         auto numEras = ConnectedChains.ThisChain().rewards.size();
635         ASSETCHAINS_LASTERA = numEras - 1;
636         mapArgs["-ac_eras"] = to_string(numEras);
637
638         mapArgs["-ac_end"] = "";
639         mapArgs["-ac_reward"] = "";
640         mapArgs["-ac_halving"] = "";
641         mapArgs["-ac_decay"] = "";
642         mapArgs["-ac_options"] = "";
643
644         for (int j = 0; j < ASSETCHAINS_MAX_ERAS; j++)
645         {
646             if (j > ASSETCHAINS_LASTERA)
647             {
648                 ASSETCHAINS_REWARD[j] = ASSETCHAINS_REWARD[j-1];
649                 ASSETCHAINS_DECAY[j] = ASSETCHAINS_DECAY[j-1];
650                 ASSETCHAINS_HALVING[j] = ASSETCHAINS_HALVING[j-1];
651                 ASSETCHAINS_ENDSUBSIDY[j] = 0;
652                 ASSETCHAINS_ERAOPTIONS[j] = 0;
653             }
654             else
655             {
656                 ASSETCHAINS_REWARD[j] = ConnectedChains.ThisChain().rewards[j];
657                 ASSETCHAINS_DECAY[j] = ConnectedChains.ThisChain().rewardsDecay[j];
658                 ASSETCHAINS_HALVING[j] = ConnectedChains.ThisChain().halving[j];
659                 ASSETCHAINS_ENDSUBSIDY[j] = ConnectedChains.ThisChain().eraEnd[j];
660                 ASSETCHAINS_ERAOPTIONS[j] = ConnectedChains.ThisChain().eraOptions[j];
661                 if (j == 0)
662                 {
663                     mapArgs["-ac_reward"] = to_string(ASSETCHAINS_REWARD[j]);
664                     mapArgs["-ac_decay"] = to_string(ASSETCHAINS_DECAY[j]);
665                     mapArgs["-ac_halving"] = to_string(ASSETCHAINS_HALVING[j]);
666                     mapArgs["-ac_end"] = to_string(ASSETCHAINS_ENDSUBSIDY[j]);
667                     mapArgs["-ac_options"] = to_string(ASSETCHAINS_ERAOPTIONS[j]);
668                 }
669                 else
670                 {
671                     mapArgs["-ac_reward"] += "," + to_string(ASSETCHAINS_REWARD[j]);
672                     mapArgs["-ac_decay"] += "," + to_string(ASSETCHAINS_DECAY[j]);
673                     mapArgs["-ac_halving"] += "," + to_string(ASSETCHAINS_HALVING[j]);
674                     mapArgs["-ac_end"] += "," + to_string(ASSETCHAINS_ENDSUBSIDY[j]);
675                     mapArgs["-ac_options"] += "," + to_string(ASSETCHAINS_ERAOPTIONS[j]);
676                 }
677             }
678         }
679
680         PBAAS_STARTBLOCK = ConnectedChains.ThisChain().startBlock;
681         mapArgs["-startblock"] = to_string(PBAAS_STARTBLOCK);
682         PBAAS_ENDBLOCK = ConnectedChains.ThisChain().endBlock;
683         mapArgs["-endblock"] = to_string(PBAAS_ENDBLOCK);
684
685         PBAAS_PRECONVERSION = ConnectedChains.ThisChain().conversion;
686         PBAAS_MINPRECONVERT = ConnectedChains.ThisChain().minpreconvert;
687         PBAAS_MAXPRECONVERT = ConnectedChains.ThisChain().maxpreconvert;
688         mapArgs["-ac_conversion"] = to_string(PBAAS_PRECONVERSION);
689         mapArgs["-ac_minpreconvert"] = to_string(PBAAS_MINPRECONVERT);
690         mapArgs["-ac_maxpreconvert"] = to_string(PBAAS_MAXPRECONVERT);
691         return true;
692     }
693     else
694     {
695         return false;
696     }
697 }
698
699 // ensures that the chain definition is valid and that there are no other definitions of the same name
700 // that have been confirmed.
701 bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
702 {
703     // the chain definition output can be spent when the chain is at the end of its life and only then
704     // TODO
705     return false;
706 }
707
708 // ensures that the chain definition is valid and that there are no other definitions of the same name
709 // that have been confirmed.
710 bool CheckChainDefinitionOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
711 {
712     // checked before a chain definition output script is accepted as a valid transaction
713
714     // basics - we need a chain definition transaction to kick off a PBaaS chain. it must have:
715     // 1) valid chain definition output with parameters in proper ranges and no duplicate name
716     // 2) notarization output with conformant values
717     // 3) finalization output
718     // 3) notarization funding
719     //
720
721     // get the source transaction
722     uint256 blkHash;
723     CTransaction thisTx;
724     if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
725     {
726         LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
727         return false;
728     }
729
730     CPBaaSChainDefinition chainDef(thisTx, true);
731     CPBaaSNotarization notarization(thisTx, true);
732     CNotarizationFinalization finalization(thisTx, true);
733
734     if (!chainDef.IsValid() || !notarization.IsValid() || finalization.IsValid())
735     {
736         LogPrintf("transaction specified, %s, must have valid chain definition, notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
737         return false;
738     }
739
740     CPBaaSChainDefinition prior;
741     // this ensures that there is no other definition of the same name already on the blockchain
742     if (!GetChainDefinition(chainDef.name, prior))
743     {
744         LogPrintf("PBaaS chain with the name %s already exists\n", chainDef.name.c_str());
745         return false;
746     }
747
748     return true;
749 }
750
751 CAmount CCrossChainExport::CalculateExportFee() const
752 {
753     if (numInputs > MAX_EXPORT_INPUTS)
754     {
755         return 0;
756     }
757     static const arith_uint256 satoshis(100000000);
758
759     int64_t ratio = 50000000 + ((25000000 / MAX_EXPORT_INPUTS) * (numInputs - 1));
760
761     return (((arith_uint256(totalFees) * arith_uint256(ratio))) / satoshis).GetLow64();
762 }
763
764 bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
765 {
766     bool retval = false;
767     LOCK(cs_mergemining);
768
769     //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
770
771     auto chainIt = mergeMinedChains.find(chainID);
772     if (chainIt != mergeMinedChains.end())
773     {
774         arith_uint256 target;
775         target.SetCompact(chainIt->second.block.nBits);
776         for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
777         {
778             // make sure we don't just match by target
779             if (removeRange.first->second->GetChainID() == chainID)
780             {
781                 mergeMinedTargets.erase(removeRange.first);
782                 break;
783             }
784         }
785         mergeMinedChains.erase(chainID);
786         dirty = retval = true;
787
788         // if we get to 0, give the thread a kick to stop waiting for mining
789         //if (!mergeMinedChains.size())
790         //{
791         //    sem_submitthread.post();
792         //}
793     }
794     return retval;
795 }
796
797 // remove merge mined chains added and not updated since a specific time
798 void CConnectedChains::PruneOldChains(uint32_t pruneBefore)
799 {
800     vector<uint160> toRemove;
801
802     LOCK(cs_mergemining);
803     for (auto blkData : mergeMinedChains)
804     {
805         if (blkData.second.block.nTime < pruneBefore)
806         {
807             toRemove.push_back(blkData.first);
808         }
809     }
810
811     for (auto id : toRemove)
812     {
813         //printf("Pruning chainID: %s\n", id.GetHex().c_str());
814         RemoveMergedBlock(id);
815     }
816 }
817
818 // adds or updates merge mined blocks
819 // returns false if failed to add
820 bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
821 {
822     // determine if we should replace one or add to the merge mine vector
823     {
824         LOCK(cs_mergemining);
825
826         arith_uint256 target;
827         uint160 cID = blkData.GetChainID();
828         auto it = mergeMinedChains.find(cID);
829         if (it != mergeMinedChains.end())
830         {
831             RemoveMergedBlock(cID);             // remove it if already there
832         }
833         target.SetCompact(blkData.block.nBits);
834
835         //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
836
837         mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
838         dirty = true;
839     }
840     return true;
841 }
842
843 bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
844 {
845     {
846         LOCK(cs_mergemining);
847         auto chainIt = mergeMinedChains.find(chainID);
848         if (chainIt != mergeMinedChains.end())
849         {
850             rpcChainData = (CRPCChainData)chainIt->second;
851             return true;
852         }
853         return false;
854     }
855 }
856
857 // this returns a pointer to the data without copy and assumes the lock is held
858 CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
859 {
860     {
861         auto chainIt = mergeMinedChains.find(chainID);
862         if (chainIt != mergeMinedChains.end())
863         {
864             return &chainIt->second;
865         }
866         return NULL;
867     }
868 }
869
870 void CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
871 {
872     //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
873     {
874         LOCK(cs_mergemining);
875
876         qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
877     }
878     sem_submitthread.post();
879 }
880
881 // get the latest block header and submit one block at a time, returning after there are no more
882 // matching blocks to be found
883 vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
884 {
885     std::set<uint160> inHeader;
886     bool submissionFound;
887     CPBaaSMergeMinedChainData chainData;
888     vector<pair<string, UniValue>>  results;
889
890     CBlockHeader bh;
891     arith_uint256 lastHash;
892     CPBaaSBlockHeader pbh;
893
894     do
895     {
896         submissionFound = false;
897         {
898             LOCK(cs_mergemining);
899             // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
900             // common, merge mined headers for notarization, drop out on any submission
901             for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
902             {
903                 // add the PBaaS chain ids from this header to a set for search
904                 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
905                 {
906                     inHeader.insert(pbh.chainID);
907                 }
908
909                 uint160 chainID;
910                 // now look through all targets that are equal to or above the hash of this header
911                 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
912                 {
913                     chainID = chainIt->second->GetChainID();
914                     if (inHeader.count(chainID))
915                     {
916                         // first, check that the winning header matches the block that is there
917                         CPBaaSPreHeader preHeader(chainIt->second->block);
918                         preHeader.SetBlockData(headerIt->second);
919
920                         // check if the block header matches the block's specific data, only then can we create a submission from this block
921                         if (headerIt->second.CheckNonCanonicalData(chainID))
922                         {
923                             // save block as is, remove the block from merged headers, replace header, and submit
924                             chainData = *chainIt->second;
925
926                             *(CBlockHeader *)&chainData.block = headerIt->second;
927
928                             submissionFound = true;
929                         }
930                         //else // not an error condition. code is here for debugging
931                         //{
932                         //    printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
933                         //}
934                     }
935                     //else // not an error condition. code is here for debugging
936                     //{
937                     //    printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
938                     //}
939                 }
940
941                 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
942                 if (submissionFound)
943                 {
944                     // once it is going to be submitted, remove block from this chain until a new one is added again
945                     RemoveMergedBlock(chainID);
946                     break;
947                 }
948                 else
949                 {
950                     qualifiedHeaders.erase(headerIt);
951                 }
952             }
953         }
954         if (submissionFound)
955         {
956             // submit one block and loop again. this approach allows multiple threads
957             // to collectively empty the submission queue, mitigating the impact of
958             // any one stalled daemon
959             UniValue submitParams(UniValue::VARR);
960             submitParams.push_back(EncodeHexBlk(chainData.block));
961             UniValue result, error;
962             try
963             {
964                 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
965                 result = find_value(result, "result");
966                 error = find_value(result, "error");
967             }
968             catch (exception e)
969             {
970                 result = UniValue(e.what());
971             }
972             results.push_back(make_pair(chainData.chainDefinition.name, result));
973             if (result.isStr() || !error.isNull())
974             {
975                 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());
976             }
977             else
978             {
979                 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
980             }
981         }
982     } while (submissionFound);
983     return results;
984 }
985
986 // add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
987 uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
988 {
989     vector<uint160> inHeader;
990     vector<UniValue> toCombine;
991     arith_uint256 blkHash = UintToArith256(bh.GetHash());
992     arith_uint256 target(0);
993     
994     CPBaaSBlockHeader pbh;
995
996     {
997         LOCK(cs_mergemining);
998
999         CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
1000
1001         for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
1002         {
1003             if (bh.GetPBaaSHeader(pbh, i))
1004             {
1005                 inHeader.push_back(pbh.chainID);
1006             }
1007         }
1008
1009         // loop through the existing PBaaS chain ids in the header
1010         // remove any that are not either this Chain ID or in our local collection and then add all that are present
1011         for (uint32_t i = 0; i < inHeader.size(); i++)
1012         {
1013             auto it = mergeMinedChains.find(inHeader[i]);
1014             if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
1015             {
1016                 bh.DeletePBaaSHeader(i);
1017             }
1018         }
1019
1020         for (auto chain : mergeMinedChains)
1021         {
1022             // get the native PBaaS header for each chain and put it into the
1023             // header we are given
1024             // it must have itself in as a PBaaS header
1025             uint160 cid = chain.second.GetChainID();
1026             if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
1027             {
1028                 if (!bh.AddUpdatePBaaSHeader(pbh))
1029                 {
1030                     LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
1031                     break;
1032                 }
1033                 else
1034                 {
1035                     arith_uint256 t;
1036                     t.SetCompact(chain.second.block.nBits);
1037                     if (t > target)
1038                     {
1039                         target = t;
1040                     }
1041                 }
1042             }
1043             else
1044             {
1045                 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
1046             }
1047             
1048         }
1049         dirty = false;
1050     }
1051
1052     return target.GetCompact();
1053 }
1054
1055 bool CConnectedChains::IsVerusPBaaSAvailable()
1056 {
1057     return notaryChainVersion >= "0.5.9";
1058 }
1059
1060 extern string PBAAS_HOST, PBAAS_USERPASS;
1061 extern int32_t PBAAS_PORT;
1062 bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
1063 {
1064     if (chainInfoUni.isObject() && chainDefUni.isObject())
1065     {
1066         UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
1067         if (uniVer.isStr())
1068         {
1069             LOCK(cs_mergemining);
1070             notaryChainVersion = uni_get_str(uniVer);
1071             notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
1072             CPBaaSChainDefinition chainDef(chainDefUni);
1073             notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
1074         }
1075     }
1076     return IsVerusPBaaSAvailable();
1077 }
1078
1079 bool CConnectedChains::CheckVerusPBaaSAvailable()
1080 {
1081     if (IsVerusActive())
1082     {
1083         notaryChainVersion = "";
1084     }
1085     else
1086     {
1087         // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
1088         // tolerate only 15 second timeout
1089         UniValue chainInfo, chainDef;
1090         try
1091         {
1092             UniValue params(UniValue::VARR);
1093             chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
1094             if (!chainInfo.isNull())
1095             {
1096                 params.push_back(VERUS_CHAINNAME);
1097                 chainDef = find_value(RPCCallRoot("getchaindefinition", params), "result");
1098
1099                 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
1100                 {
1101                     return true;
1102                 }
1103             }
1104         } catch (exception e)
1105         {
1106         }
1107     }
1108     notaryChainVersion = "";
1109     return false;
1110 }
1111
1112 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
1113 {
1114     CCoinbaseCurrencyState currencyState;
1115     CBlock block;
1116     LOCK(cs_main);
1117     bool isVerusActive = IsVerusActive();
1118     if (!isVerusActive && 
1119         CConstVerusSolutionVector::activationHeight.ActiveVersion(height) >= CActivationHeight::ACTIVATE_PBAAS &&
1120         height != 0 && 
1121         height <= chainActive.Height() && 
1122         chainActive[height] && 
1123         ReadBlockFromDisk(block, chainActive[height], Params().GetConsensus()) &&
1124         (currencyState = CCoinbaseCurrencyState(block.vtx[0])).IsValid())
1125     {
1126         return currencyState;
1127     }
1128     else
1129     {
1130         bool isReserve = (thisChain.ChainOptions() & thisChain.OPTION_RESERVE);
1131         CAmount preconvertedNative = currencyState.ReserveToNative(thisChain.preconverted, thisChain.conversion);
1132         CCurrencyState cState(thisChain.conversion, preconvertedNative, preconvertedNative, 0, isReserve ? thisChain.preconverted : 0, isReserve ? CCurrencyState::VALID + CCurrencyState::ISRESERVE : CCurrencyState::VALID);
1133         CAmount fees = 0;
1134         if (!height && !isVerusActive)
1135         {
1136             fees = CReserveTransactionDescriptor::CalculateAdditionalConversionFee(thisChain.preconverted);
1137         }
1138         return CCoinbaseCurrencyState(cState, thisChain.preconverted, 0, CReserveOutput(CReserveOutput::VALID, 0), thisChain.conversion, fees, fees);
1139     }
1140 }
1141
1142 bool CConnectedChains::SetLatestMiningOutputs(const std::vector<pair<int, CScript>> &minerOutputs, CTxDestination &firstDestinationOut)
1143 {
1144     LOCK(cs_mergemining);
1145
1146     if (!minerOutputs.size() || !ExtractDestination(minerOutputs[0].second, firstDestinationOut))
1147     {
1148         return false;
1149     }
1150     latestMiningOutputs = minerOutputs;
1151     latestDestination = firstDestinationOut;
1152     return true;
1153 }
1154
1155 void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
1156 {
1157     // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
1158     {
1159         if (!nHeight)
1160         {
1161             return;
1162         }
1163
1164         multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
1165
1166         LOCK(cs_main);
1167
1168         // get all available transfer outputs to aggregate into export transactions
1169         if (GetUnspentChainTransfers(transferOutputs))
1170         {
1171             if (!transferOutputs.size())
1172             {
1173                 return;
1174             }
1175             std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
1176             std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1177
1178             uint160 bookEnd;
1179             bookEnd.SetHex("ffffffffffffffffffffffffffffffffffffffff");
1180             uint160 lastChain = bookEnd;
1181
1182             // add a bookend entry at the end of transfer outputs to ensure that we try to export all before it
1183             transferOutputs.insert(make_pair(bookEnd, make_pair(CInputDescriptor(), CReserveTransfer())));
1184
1185             CPBaaSChainDefinition lastChainDef;
1186
1187             // merge all of the common chainID outputs into common export transactions if either MIN_BLOCKS blocks have passed since the last
1188             // export of that type, or there are MIN_INPUTS or more outputs to aggregate
1189             for (auto it = transferOutputs.begin(); it != transferOutputs.end(); it++)
1190             {
1191                 // get chain target and see if it is the same
1192                 if (lastChain == bookEnd || it->first == lastChain)
1193                 {
1194                     txInputs.push_back(it->second);
1195                 }
1196                 else
1197                 {
1198                     // when we get here, we have a consecutive number of transfer outputs to consume in txInputs
1199                     // we need an unspent export output to export
1200                     if (GetUnspentChainExports(lastChain, exportOutputs) && exportOutputs.size())
1201                     {
1202                         auto lastExport = *exportOutputs.begin();
1203
1204                         if (((nHeight - lastExport.second.first) >= CCrossChainExport::MIN_BLOCKS) || (txInputs.size() >= CCrossChainExport::MIN_INPUTS))
1205                         {
1206                             boost::optional<CTransaction> oneExport;
1207
1208                             if (!GetChainDefinition(lastChain, lastChainDef))
1209                             {
1210                                 printf("%s: chain definition for export chainID %s not found\n\n", __func__, lastChain.GetHex().c_str());
1211                                 LogPrintf("%s: chain definition for export chainID %s not found\n\n", __func__, lastChain.GetHex().c_str());
1212                                 continue;
1213                             }
1214
1215                             // make one or more transactions that spends the last export and all possible cross chain transfers
1216                             while (txInputs.size())
1217                             {
1218                                 TransactionBuilder tb(Params().GetConsensus(), nHeight);
1219
1220                                 int numInputs = (txInputs.size() < CCrossChainExport::MAX_EXPORT_INPUTS) ? txInputs.size() : CCrossChainExport::MAX_EXPORT_INPUTS;
1221
1222                                 int inputsLeft = txInputs.size() - numInputs;
1223
1224                                 if (inputsLeft > 0 && inputsLeft < CCrossChainExport::MIN_INPUTS)
1225                                 {
1226                                     inputsLeft += CCrossChainExport::MIN_INPUTS - inputsLeft;
1227                                     numInputs -= CCrossChainExport::MIN_INPUTS - inputsLeft;
1228                                     assert(numInputs > 0);
1229                                 }
1230
1231                                 // each time through, we make one export transaction with the remainder or a subset of the
1232                                 // reserve transfer inputs. inputs can be:
1233                                 // 1. transfers of reserve for fractional reserve chains
1234                                 // 2. pre-conversions for pre-launch participation in the premine
1235                                 // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
1236                                 //
1237                                 // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
1238                                 // or reserve token inputs.
1239                                 //
1240                                 // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
1241                                 // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
1242                                 // as part of the import process
1243                                 // 
1244                                 // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain. 
1245                                 // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the 
1246                                 // remaining Verus reserve coin will be burned on the PBaaS chain as spending it is allowed, once notarized, on the
1247                                 // Verus chain.
1248                                 //
1249                                 CAmount totalTxFees = 0;
1250                                 CAmount totalAmount = 0;
1251                                 CAmount exportOutVal = 0;
1252                                 std::vector<CBaseChainObject *> chainObjects;
1253
1254                                 // first, we must add the export output from the current export thread to this chain
1255                                 if (oneExport.has_value())
1256                                 {
1257                                     // spend the last export transaction output
1258                                     CTransaction &tx = oneExport.get();
1259                                     COptCCParams p;
1260                                     int j;
1261                                     for (j = 0; j < tx.vout.size(); j++)
1262                                     {
1263                                         if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) && p.evalCode == EVAL_CROSSCHAIN_EXPORT)
1264                                         {
1265                                             break;
1266                                         }
1267                                     }
1268
1269                                     // had to be found and valid if we made the tx
1270                                     assert(j < tx.vout.size() && p.IsValid());
1271
1272                                     tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
1273                                     exportOutVal = tx.vout[j].nValue;
1274                                 }
1275                                 else
1276                                 {
1277                                     // spend the recentExportIt output
1278                                     tb.AddTransparentInput(lastExport.second.second.txIn.prevout, lastExport.second.second.scriptPubKey, lastExport.second.second.nValue);
1279                                     exportOutVal = lastExport.second.second.nValue;
1280                                 }
1281
1282                                 COptCCParams p;
1283                                 int fails = 0;
1284                                 for (int j = 0; j < numInputs; j++)
1285                                 {
1286                                     tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
1287                                     if (IsVerusActive() && txInputs[j].second.nValue + txInputs[j].second.nFees > txInputs[j].first.nValue)
1288                                     {
1289                                         // 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
1290                                         // 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
1291                                         // we should formalize this into a chain contribution or amount adjustment.
1292                                         printf("%s: transaction %s claims more value than it contains\n", __func__, txInputs[j].first.txIn.prevout.hash.GetHex().c_str());
1293                                         LogPrintf("%s: transaction %s claims more value than it contains\n", __func__, txInputs[j].first.txIn.prevout.hash.GetHex().c_str());
1294                                         fails++;
1295                                     }
1296                                     else
1297                                     {
1298                                         totalTxFees += txInputs[j].second.nFees;
1299                                         totalAmount += txInputs[j].second.nValue;
1300                                         chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(txInputs[j].second), txInputs[j].second));
1301                                     }
1302                                 }
1303                                 if (fails == numInputs)
1304                                 {
1305                                     // erase the inputs we've attempted to spend
1306                                     txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1307                                     continue;
1308                                 }
1309
1310                                 CCrossChainExport ccx(lastChain, numInputs, totalAmount, totalTxFees);
1311                                 CAmount exportFees = ccx.CalculateExportFee();
1312                                 CReserveTransfer feeOut(CReserveTransfer::VALID + CReserveTransfer::SEND_BACK + CReserveTransfer::FEE_OUTPUT, exportFees, 0, GetDestinationID(feeOutput));
1313                                 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
1314
1315                                 // do a preliminary check
1316                                 CReserveTransactionDescriptor rtxd;
1317                                 std::vector<CTxOut> dummy;
1318
1319                                 if (!rtxd.AddReserveTransferImportOutputs(lastChainDef, chainObjects, dummy))
1320                                 {
1321                                     DeleteOpRetObjects(chainObjects);
1322
1323                                     printf("%s: failed to create valid exports\n", __func__);
1324                                     LogPrintf("%s: failed to create valid exports\n", __func__);
1325
1326                                     // debugging output
1327                                     printf("%s: failed to export outputs:\n", __func__);
1328                                     for (auto oneout : dummy)
1329                                     {
1330                                         UniValue uniOut;
1331                                         ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1332                                         printf("%s\n", uniOut.write(true, 2).c_str());
1333                                     }
1334                                 }
1335                                 else
1336                                 {
1337                                     // debugging output
1338                                     printf("%s: exported outputs:\n", __func__);
1339                                     for (auto oneout : dummy)
1340                                     {
1341                                         UniValue uniOut;
1342                                         ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1343                                         printf("%s\n", uniOut.write(true, 2).c_str());
1344                                     }
1345
1346                                     CScript opRet = StoreOpRetArray(chainObjects);
1347                                     DeleteOpRetObjects(chainObjects);
1348
1349                                     CCcontract_info CC;
1350                                     CCcontract_info *cp;
1351                                     cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
1352
1353                                     CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
1354
1355                                     // send native amount of zero to a cross chain export output of the specific chain
1356                                     std::vector<CTxDestination> dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_CROSSCHAIN_EXPORT))});
1357
1358                                     CTxOut exportOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_EXPORT, exportOutVal, pk, dests, ccx);
1359
1360                                     tb.AddTransparentOutput(exportOut.scriptPubKey, exportOutVal);
1361
1362                                     // if we are on Verus chain, send all native funds to reserve deposit CC, which is equivalent to the reserve account
1363                                     // if we are on a PBaaS chain, input is burned when sent back for release
1364                                     if (IsVerusActive())
1365                                     {
1366                                         cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
1367                                         pk = CPubKey(ParseHex(CC.CChexstr));
1368
1369                                         // send the entire amount to a reserve transfer output of the specific chain
1370                                         // we receive our fee on the other chain or when it comes back
1371                                         dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_RESERVE_DEPOSIT))});
1372
1373                                         CReserveOutput ro(CReserveOutput::VALID, totalAmount + totalTxFees);
1374
1375                                         CTxOut outToReserve = MakeCC1of1Vout(EVAL_RESERVE_DEPOSIT, 
1376                                                                             ro.nValue,
1377                                                                             pk,
1378                                                                             dests,
1379                                                                             ro);
1380
1381                                         tb.AddTransparentOutput(outToReserve.scriptPubKey, ro.nValue);
1382                                     }
1383
1384                                     tb.AddOpRet(opRet);
1385                                     tb.SetFee(0);
1386
1387                                     TransactionBuilderResult buildResult(tb.Build());
1388
1389                                     if (!buildResult.IsError() && buildResult.IsTx())
1390                                     {
1391                                         // replace the last one only if we have a valid new one
1392                                         CTransaction tx = buildResult.GetTxOrThrow();
1393
1394                                         LOCK2(cs_main, mempool.cs);
1395                                         static int lastHeight = 0;
1396                                         // remove conflicts, so that we get in
1397                                         std::list<CTransaction> removed;
1398                                         mempool.removeConflicts(tx, removed);
1399
1400                                         // add to mem pool, prioritize according to the fee we will get, and relay
1401                                         printf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1402                                         LogPrintf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1403                                         if (myAddtomempool(tx))
1404                                         {
1405                                             uint256 hash = tx.GetHash();
1406                                             mempool.PrioritiseTransaction(hash, hash.GetHex(), (double)(exportFees << 1), exportFees);
1407                                             RelayTransaction(tx);
1408                                         }
1409                                     }
1410                                     else
1411                                     {
1412                                         // we can't do any more useful work for this chain if we failed here
1413                                         printf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1414                                         LogPrintf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1415                                         break;
1416                                     }
1417                                 }
1418
1419                                 // erase the inputs we've attempted to spend
1420                                 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1421                             }
1422                         }
1423                     }
1424                 }
1425                 lastChain = it->first;
1426             }
1427         }
1428     }
1429 }
1430
1431 // send new imports from this chain to the specified chain, which generally will be the notary chain
1432 void CConnectedChains::SendNewImports(const uint160 &chainID, 
1433                                       const CPBaaSNotarization &notarization, 
1434                                       const uint256 &lastExportTx, 
1435                                       const CTransaction &lastCrossImport, 
1436                                       const CTransaction &lastExport)
1437 {
1438     // currently only support sending imports to  
1439 }
1440
1441 void CConnectedChains::SubmissionThread()
1442 {
1443     try
1444     {
1445         arith_uint256 lastHash;
1446         int64_t lastImportTime = 0;
1447         uint32_t lastHeight = 0;
1448         
1449         // wait for something to check on, then submit blocks that should be submitted
1450         while (true)
1451         {
1452             boost::this_thread::interruption_point();
1453
1454             if (IsVerusActive())
1455             {
1456                 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
1457                 //printf("SubmissionThread: pruning\n");
1458                 PruneOldChains(GetAdjustedTime() - 300);
1459                 bool submit = false;
1460                 {
1461                     LOCK(cs_mergemining);
1462                     if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
1463                     {
1464                         qualifiedHeaders.clear();
1465                     }
1466                     submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
1467
1468                     //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
1469                 }
1470                 if (submit)
1471                 {
1472                     //printf("SubmissionThread: calling submit qualified blocks\n");
1473                     SubmitQualifiedBlocks();
1474                 }
1475                 else
1476                 {
1477                     //printf("SubmissionThread: waiting on sem\n");
1478                     sem_submitthread.wait();
1479                 }
1480             }
1481             else
1482             {
1483                 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
1484                 if (CheckVerusPBaaSAvailable())
1485                 {
1486                     // check to see if we have recently earned a block with an earned notarization that qualifies for
1487                     // submitting an accepted notarization
1488                     if (earnedNotarizationHeight)
1489                     {
1490                         CBlock blk;
1491                         int32_t txIndex = -1, height;
1492                         {
1493                             LOCK(cs_mergemining);
1494                             if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
1495                             {
1496                                 blk = earnedNotarizationBlock;
1497                                 earnedNotarizationBlock = CBlock();
1498                                 txIndex = earnedNotarizationIndex;
1499                                 height = earnedNotarizationHeight;
1500                                 earnedNotarizationHeight = 0;
1501                             }
1502                         }
1503
1504                         if (txIndex != -1)
1505                         {
1506                             //printf("SubmissionThread: testing notarization\n");
1507                             CTransaction lastConfirmed;
1508                             uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
1509
1510                             if (!txId.IsNull())
1511                             {
1512                                 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1513                                 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1514                             }
1515                         }
1516                     }
1517
1518                     // every "n" seconds, look for imports to include in our blocks from the Verus chain
1519                     if ((GetAdjustedTime() - lastImportTime) >= 30 || lastHeight < (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight()))
1520                     {
1521                         lastImportTime = GetAdjustedTime();
1522                         lastHeight = (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight());
1523
1524                         // see if our notary has a confirmed notarization for us
1525                         UniValue params(UniValue::VARR);
1526                         UniValue result;
1527
1528                         params.push_back(thisChain.name);
1529
1530                         try
1531                         {
1532                             result = find_value(RPCCallRoot("getlastimportin", params), "result");
1533                         } catch (exception e)
1534                         {
1535                             result = NullUniValue;
1536                         }
1537
1538                         if (!result.isNull())
1539                         {
1540                             auto txUniStr = find_value(result, "lastimporttransaction");
1541                             auto txLastConfirmedStr = find_value(result, "lastconfirmednotarization");
1542                             auto txTemplateStr = find_value(result, "importtxtemplate");
1543                             CAmount totalImportAvailable = uni_get_int64(find_value(result, "totalimportavailable"));
1544
1545                             CTransaction lastImportTx, lastConfirmedTx, templateTx;
1546
1547                             if (txUniStr.isStr() && txTemplateStr.isStr() && 
1548                                 DecodeHexTx(lastImportTx, txUniStr.get_str()) && 
1549                                 DecodeHexTx(lastConfirmedTx, txLastConfirmedStr.get_str()) && 
1550                                 DecodeHexTx(templateTx, txTemplateStr.get_str()))
1551                             {
1552                                 std::vector<CTransaction> importTxes;
1553                                 if (CreateLatestImports(notaryChain.chainDefinition, lastImportTx, templateTx, lastConfirmedTx, totalImportAvailable, importTxes))
1554                                 {
1555                                     for (auto importTx : importTxes)
1556                                     {
1557                                         UniValue txResult;
1558                                         params.setArray();
1559                                         params.push_back(EncodeHexTx(importTx));
1560
1561                                         try
1562                                         {
1563                                             txResult = find_value(RPCCallRoot("signrawtransaction", params), "result");
1564                                             if (txResult.isObject() && !(txResult = find_value(txResult, "hex")).isNull() && txResult.isStr() && txResult.get_str().size())
1565                                             {
1566                                                 params.setArray();
1567                                                 params.push_back(txResult);
1568                                                 txResult = find_value(RPCCallRoot("sendrawtransaction", params), "result");
1569                                             }
1570                                             else
1571                                             {
1572                                                 txResult = NullUniValue;
1573                                             }
1574                                             
1575                                         } catch (exception e)
1576                                         {
1577                                             txResult = NullUniValue;
1578                                         }
1579                                         uint256 testId;
1580                                         if (txResult.isStr())
1581                                         {
1582                                             testId.SetHex(txResult.get_str());
1583                                         }
1584                                         if (testId.IsNull())
1585                                         {
1586                                             break;
1587                                         }
1588                                     }
1589                                 }
1590                             }
1591                         }
1592                     }
1593                 }
1594                 sleep(3);
1595             }
1596             boost::this_thread::interruption_point();
1597         }
1598     }
1599     catch (const boost::thread_interrupted&)
1600     {
1601         LogPrintf("Verus merge mining thread terminated\n");
1602     }
1603 }
1604
1605 void CConnectedChains::SubmissionThreadStub()
1606 {
1607     ConnectedChains.SubmissionThread();
1608 }
1609
1610 void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
1611 {
1612     // called after winning a block that contains an earned notarization
1613     // the earned notarization and its height are queued for processing by the submission thread
1614     // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
1615     // kept
1616     LOCK(cs_mergemining);
1617
1618     // we only care about the last
1619     earnedNotarizationHeight = height;
1620     earnedNotarizationBlock = blk;
1621     earnedNotarizationIndex = txIndex;
1622 }
1623
1624 bool IsChainDefinitionInput(const CScript &scriptSig)
1625 {
1626     uint32_t ecode;
1627     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_PBAASDEFINITION;
1628 }
1629
This page took 0.120153 seconds and 4 git commands to generate.