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