]> Git Repo - VerusCoin.git/blob - src/pbaas/pbaas.cpp
Complete block solution and transaction fixups for reserve conversions - not debugged
[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
21 using namespace std;
22
23 CConnectedChains ConnectedChains;
24
25 bool IsVerusActive()
26 {
27     return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 0 || strcmp(ASSETCHAINS_SYMBOL, "VRSCTEST") == 0);
28 }
29
30 // this adds an opret to a mutable transaction and returns the voutnum if it could be added
31 int32_t AddOpRetOutput(CMutableTransaction &mtx, const CScript &opRetScript)
32 {
33     if (opRetScript.IsOpReturn() && opRetScript.size() <= MAX_OP_RETURN_RELAY)
34     {
35         CTxOut vOut = CTxOut();
36         vOut.scriptPubKey = opRetScript;
37         vOut.nValue = 0;
38         mtx.vout.push_back(vOut);
39         return mtx.vout.size() - 1;
40     }
41     else
42     {
43         return -1;
44     }
45 }
46
47 // returns a pointer to a base chain object, which can be cast to the
48 // object type indicated in its objType member
49 uint256 GetChainObjectHash(const CBaseChainObject &bo)
50 {
51     union {
52         const CChainObject<CBlockHeader> *pNewHeader;
53         const CChainObject<CTransaction> *pNewTx;
54         const CChainObject<CMerkleBranch> *pNewProof;
55         const CChainObject<CHeaderRef> *pNewHeaderRef;
56         const CChainObject<CPriorBlocksCommitment> *pPriors;
57         const CChainObject<CReserveTransfer> *pExport;
58         const CBaseChainObject *retPtr;
59     };
60
61     retPtr = &bo;
62
63     switch(bo.objectType)
64     {
65         case CHAINOBJ_HEADER:
66             return pNewHeader->GetHash();
67
68         case CHAINOBJ_TRANSACTION:
69             return pNewTx->GetHash();
70
71         case CHAINOBJ_PROOF:
72             return pNewProof->GetHash();
73
74         case CHAINOBJ_HEADER_REF:
75             return pNewHeaderRef->GetHash();
76
77         case CHAINOBJ_PRIORBLOCKS:
78             return pPriors->GetHash();
79
80         case CHAINOBJ_RESERVETRANSFER:
81             return pExport->GetHash();
82     }
83     return uint256();
84 }
85
86 // used to export coins from one chain to another, if they are not native, they are represented on the other
87 // chain as tokens
88 bool ValidateChainExport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
89 {
90
91 }
92
93 // used to validate import of coins from one chain to another. if they are not native and are supported,
94 // they are represented o the chain as tokens
95 bool ValidateChainImport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
96 {
97
98 }
99
100 // used to validate a specific service reward based on the spending transaction
101 bool ValidateServiceReward(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
102 {
103     // for each type of service reward, we need to check and see if the spender is
104     // correctly formatted to be a valid spend of the service reward. for notarization
105     // we ensure that the notarization and its outputs are valid and that the spend
106     // applies to the correct billing period
107     return true;
108 }
109
110 // used as a proxy token output for a reserve currency on its fractional reserve chain
111 bool ValidateReserveOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
112 {
113
114 }
115
116 // used to convert a fractional reserve currency into its reserve and back 
117 bool ValidateReserveExchange(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
118 {
119
120 }
121
122 // used for distribution of premine
123 bool ValidatePremineOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
124 {
125
126 }
127
128 /*
129  * Verifies that the input objects match the hashes and returns the transaction.
130  * 
131  * If the opRetTx has the op ret, this calculates based on the actual transaction and
132  * validates the hashes. If the opRetTx does not have the opRet itself, this validates
133  * by ensuring that all objects are present on this chain, composing the opRet, and
134  * ensuring that the transaction then hashes to the correct txid.
135  * 
136  */
137 bool ValidateOpretProof(CScript &opRet, COpRetProof &orProof)
138 {
139     // enumerate through the objects and validate that they are objects of the expected type that hash
140     // to the value expected. return true if so
141         
142 }
143
144 int8_t ObjTypeCode(const CBlockHeader &obj)
145 {
146     return CHAINOBJ_HEADER;
147 }
148
149 int8_t ObjTypeCode(const CMerkleBranch &obj)
150 {
151     return CHAINOBJ_PROOF;
152 }
153
154 int8_t ObjTypeCode(const CTransaction &obj)
155 {
156     return CHAINOBJ_TRANSACTION;
157 }
158
159 int8_t ObjTypeCode(const CHeaderRef &obj)
160 {
161     return CHAINOBJ_HEADER_REF;
162 }
163
164 int8_t ObjTypeCode(const CPriorBlocksCommitment &obj)
165 {
166     return CHAINOBJ_PRIORBLOCKS;
167 }
168
169 int8_t ObjTypeCode(const CReserveTransfer &obj)
170 {
171     return CHAINOBJ_RESERVETRANSFER;
172 }
173
174 // this adds an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction
175 CScript StoreOpRetArray(std::vector<CBaseChainObject *> &objPtrs)
176 {
177     CScript vData;
178     CDataStream s = CDataStream(SER_NETWORK, PROTOCOL_VERSION);
179     s << (int32_t)OPRETTYPE_OBJECTARR;
180     bool error = false;
181
182     for (auto pobj : objPtrs)
183     {
184         try
185         {
186             if (!DehydrateChainObject(s, pobj))
187             {
188                 error = true;
189                 break;
190             }
191         }
192         catch(const std::exception& e)
193         {
194             std::cerr << e.what() << '\n';
195             error = true;
196             break;
197         }
198     }
199
200     //std::vector<unsigned char> schars(s.begin(), s.begin() + 200);
201     //printf("stream vector chars: %s\n", HexBytes(&schars[0], schars.size()).c_str());
202
203     std::vector<unsigned char> vch(s.begin(), s.end());
204     return error ? CScript() : CScript() << OP_RETURN << vch;
205 }
206
207 void DeleteOpRetObjects(std::vector<CBaseChainObject *> &ora)
208 {
209     for (auto pobj : ora)
210     {
211         switch(pobj->objectType)
212         {
213             case CHAINOBJ_HEADER:
214             {
215                 delete (CChainObject<CBlockHeader> *)pobj;
216                 break;
217             }
218
219             case CHAINOBJ_TRANSACTION:
220             {
221                 delete (CChainObject<CTransaction> *)pobj;
222                 break;
223             }
224
225             case CHAINOBJ_PROOF:
226             {
227                 delete (CChainObject<CMerkleBranch> *)pobj;
228                 break;
229             }
230
231             case CHAINOBJ_HEADER_REF:
232             {
233                 delete (CChainObject<CHeaderRef> *)pobj;
234                 break;
235             }
236
237             case CHAINOBJ_PRIORBLOCKS:
238             {
239                 delete (CChainObject<CPriorBlocksCommitment> *)pobj;
240                 break;
241             }
242
243             case CHAINOBJ_RESERVETRANSFER:
244             {
245                 delete (CChainObject<CReserveTransfer> *)pobj;
246                 break;
247             }
248
249             default:
250                 delete pobj;
251         }
252     }
253 }
254
255 std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
256 {
257     std::vector<unsigned char> vch;
258     std::vector<CBaseChainObject *> vRet;
259     if (opRetScript.IsOpReturn() && GetOpReturnData(opRetScript, vch) && vch.size() > 0)
260     {
261         CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION);
262
263         int32_t opRetType;
264
265         try
266         {
267             s >> opRetType;
268             if (opRetType == OPRETTYPE_OBJECTARR)
269             {
270                 CBaseChainObject *pobj;
271                 while (!s.empty() && (pobj = RehydrateChainObject(s)))
272                 {
273                     vRet.push_back(pobj);
274                 }
275                 if (!s.empty())
276                 {
277                     printf("failed to load all objects in opret");
278                     DeleteOpRetObjects(vRet);
279                     vRet.clear();
280                 }
281             }
282         }
283         catch(const std::exception& e)
284         {
285             std::cerr << e.what() << '\n';
286             DeleteOpRetObjects(vRet);
287             vRet.clear();
288         }
289     }
290     return vRet;
291 }
292
293 CNodeData::CNodeData(UniValue &obj)
294 {
295     networkAddress = uni_get_str(find_value(obj, "networkaddress"));
296     CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
297     ba.GetKeyID(paymentAddress);
298 }
299
300 UniValue CNodeData::ToUniValue() const
301 {
302     UniValue obj(UniValue::VOBJ);
303     obj.push_back(Pair("networkaddress", networkAddress));
304     obj.push_back(Pair("paymentaddress", CBitcoinAddress(paymentAddress).ToString()));
305     return obj;
306 }
307
308 CPBaaSChainDefinition::CPBaaSChainDefinition(const UniValue &obj)
309 {
310     nVersion = PBAAS_VERSION;
311     name = std::string(uni_get_str(find_value(obj, "name")), 0, (KOMODO_ASSETCHAIN_MAXLEN - 1));
312
313     string invalidChars = "\\/:?\"<>|";
314     for (int i = 0; i < name.size(); i++)
315     {
316         if (invalidChars.find(name[i]) != string::npos)
317         {
318             name[i] = '_';
319         }
320     }
321
322     CBitcoinAddress ba(uni_get_str(find_value(obj, "paymentaddress")));
323     ba.GetKeyID(address);
324     premine = uni_get_int64(find_value(obj, "premine"));
325     initialcontribution = uni_get_int64(find_value(obj, "initialcontribution"));
326     conversion = uni_get_int64(find_value(obj, "conversion"));
327     maxpreconvert = uni_get_int64(find_value(obj, "maxpreconvert"));
328     preconverted = uni_get_int64(find_value(obj, "preconverted"));
329     launchFee = uni_get_int64(find_value(obj, "launchfee"));
330     startBlock = uni_get_int(find_value(obj, "startblock"));
331     endBlock = uni_get_int(find_value(obj, "endblock"));
332
333     auto vEras = uni_getValues(find_value(obj, "eras"));
334     if (vEras.size() > ASSETCHAINS_MAX_ERAS)
335     {
336         vEras.resize(ASSETCHAINS_MAX_ERAS);
337     }
338     eras = !vEras.size() ? 1 : vEras.size();
339
340     for (auto era : vEras)
341     {
342         rewards.push_back(uni_get_int64(find_value(era, "reward")));
343         rewardsDecay.push_back(uni_get_int64(find_value(era, "decay")));
344         halving.push_back(uni_get_int64(find_value(era, "halving")));
345         eraEnd.push_back(uni_get_int64(find_value(era, "eraend")));
346         eraOptions.push_back(uni_get_int64(find_value(era, "eraoptions")));
347     }
348
349     billingPeriod = uni_get_int(find_value(obj, "billingperiod"));
350     notarizationReward = uni_get_int64(find_value(obj, "notarizationreward"));
351
352     auto nodeVec = uni_getValues(find_value(obj, "nodes"));
353     for (auto node : nodeVec)
354     {
355         nodes.push_back(CNodeData(node));
356     }
357 }
358
359 CPBaaSChainDefinition::CPBaaSChainDefinition(const CTransaction &tx, bool validate)
360 {
361     bool definitionFound = false;
362     nVersion = PBAAS_VERSION_INVALID;
363     for (auto out : tx.vout)
364     {
365         COptCCParams p;
366         if (IsPayToCryptoCondition(out.scriptPubKey, p))
367         {
368             if (p.evalCode == EVAL_PBAASDEFINITION)
369             {
370                 if (definitionFound)
371                 {
372                     nVersion = PBAAS_VERSION_INVALID;
373                 }
374                 else
375                 {
376                     FromVector(p.vData[0], *this);
377
378                     // TODO - remove this after validation is finished, check now in case some larger strings got into the chain
379                     name = std::string(name, 0, (KOMODO_ASSETCHAIN_MAXLEN - 1));
380                     string invalidChars = "\\/:?\"<>|";
381                     for (int i = 0; i < name.size(); i++)
382                     {
383                         if (invalidChars.find(name[i]) != string::npos)
384                         {
385                             name[i] = '_';
386                         }
387                     }
388
389                     definitionFound = true;
390                 }
391             }
392         }
393     }
394
395     if (validate)
396     {
397         
398     }
399 }
400
401 CServiceReward::CServiceReward(const CTransaction &tx, bool validate)
402 {
403     nVersion = PBAAS_VERSION_INVALID;
404     for (auto out : tx.vout)
405     {
406         COptCCParams p;
407         if (IsPayToCryptoCondition(out.scriptPubKey, p))
408         {
409             // always take the first for now
410             if (p.evalCode == EVAL_SERVICEREWARD)
411             {
412                 FromVector(p.vData[0], *this);
413                 break;
414             }
415         }
416     }
417
418     if (validate)
419     {
420         
421     }
422 }
423
424 CCrossChainImport::CCrossChainImport(const CTransaction &tx)
425 {
426     for (auto out : tx.vout)
427     {
428         COptCCParams p;
429         if (IsPayToCryptoCondition(out.scriptPubKey, p))
430         {
431             // always take the first for now
432             if (p.evalCode == EVAL_CROSSCHAIN_IMPORT)
433             {
434                 FromVector(p.vData[0], *this);
435                 break;
436             }
437         }
438     }
439 }
440
441 CCrossChainExport::CCrossChainExport(const CTransaction &tx)
442 {
443     for (auto out : tx.vout)
444     {
445         COptCCParams p;
446         if (IsPayToCryptoCondition(out.scriptPubKey, p))
447         {
448             // always take the first for now
449             if (p.evalCode == EVAL_CROSSCHAIN_EXPORT)
450             {
451                 FromVector(p.vData[0], *this);
452                 break;
453             }
454         }
455     }
456 }
457
458 uint160 CPBaaSChainDefinition::GetChainID(std::string name)
459 {
460     const char *chainName = name.c_str();
461     uint256 chainHash = Hash(chainName, chainName + strlen(chainName));
462     return Hash160(chainHash.begin(), chainHash.end());
463 }
464
465 uint160 CPBaaSChainDefinition::GetConditionID(int32_t condition)
466 {
467     return CCrossChainRPCData::GetConditionID(name, condition);
468 }
469
470 UniValue CPBaaSChainDefinition::ToUniValue() const
471 {
472     UniValue obj(UniValue::VOBJ);
473     obj.push_back(Pair("version", (int64_t)nVersion));
474     obj.push_back(Pair("name", name));
475     obj.push_back(Pair("paymentaddress", CBitcoinAddress(CTxDestination(address)).ToString()));
476     obj.push_back(Pair("premine", (int64_t)premine));
477     obj.push_back(Pair("initialcontribution", (int64_t)initialcontribution));
478     obj.push_back(Pair("conversion", (int64_t)conversion));
479     obj.push_back(Pair("maxpreconvert", (int64_t)maxpreconvert));
480     obj.push_back(Pair("preconverted", (int64_t)preconverted));
481     obj.push_back(Pair("launchfee", (int64_t)launchFee));
482     obj.push_back(Pair("conversionpercent", (double)conversion / 100000000));
483     obj.push_back(Pair("launchfeepercent", ((double)launchFee / 100000000) * 100));
484     obj.push_back(Pair("startblock", (int32_t)startBlock));
485     obj.push_back(Pair("endblock", (int32_t)endBlock));
486
487     UniValue eraArr(UniValue::VARR);
488     for (int i = 0; i < eras; i++)
489     {
490         UniValue era(UniValue::VOBJ);
491         era.push_back(Pair("reward", rewards.size() > i ? rewards[i] : (int64_t)0));
492         era.push_back(Pair("decay", rewardsDecay.size() > i ? rewardsDecay[i] : (int64_t)0));
493         era.push_back(Pair("halving", halving.size() > i ? (int32_t)halving[i] : (int32_t)0));
494         era.push_back(Pair("eraend", eraEnd.size() > i ? (int32_t)eraEnd[i] : (int32_t)0));
495         era.push_back(Pair("eraoptions", eraOptions.size() > i ? (int32_t)eraOptions[i] : (int32_t)0));
496         eraArr.push_back(era);
497     }
498     obj.push_back(Pair("eras", eraArr));
499
500     obj.push_back(Pair("billingperiod", billingPeriod));
501     obj.push_back(Pair("notarizationreward", notarizationReward));
502
503     UniValue nodeArr(UniValue::VARR);
504     for (auto node : nodes)
505     {
506         nodeArr.push_back(node.ToUniValue());
507     }
508     obj.push_back(Pair("nodes", nodeArr));
509
510     return obj;
511 }
512
513 int CPBaaSChainDefinition::GetDefinedPort() const
514 {
515     int port;
516     string host;
517     for (auto node : nodes)
518     {
519         SplitHostPort(node.networkAddress, port, host);
520         if (port)
521         {
522             return port;
523         }
524     }
525     return 0;
526 }
527
528 #define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
529 extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
530 extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3];
531 extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
532 extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
533 extern std::string VERUS_CHAINNAME;
534
535 // adds the chain definition for this chain and nodes as well
536 // this also sets up the notarization chain, if there is one
537 bool SetThisChain(UniValue &chainDefinition)
538 {
539     ConnectedChains.ThisChain() = CPBaaSChainDefinition(chainDefinition);
540
541     if (ConnectedChains.ThisChain().IsValid())
542     {
543         memset(ASSETCHAINS_SYMBOL, 0, sizeof(ASSETCHAINS_SYMBOL));
544         strcpy(ASSETCHAINS_SYMBOL, ConnectedChains.ThisChain().name.c_str());
545
546         // set all command line parameters into mapArgs from chain definition
547         vector<string> nodeStrs;
548         for (auto node : ConnectedChains.ThisChain().nodes)
549         {
550             nodeStrs.push_back(node.networkAddress);
551         }
552         if (nodeStrs.size())
553         {
554             mapMultiArgs["-seednode"] = nodeStrs;
555         }
556         if (int port = ConnectedChains.ThisChain().GetDefinedPort())
557         {
558             mapArgs["-port"] = to_string(port);
559         }
560
561         ASSETCHAINS_SUPPLY = ConnectedChains.ThisChain().premine;
562         mapArgs["-ac_supply"] = to_string(ASSETCHAINS_SUPPLY);
563         ASSETCHAINS_ALGO = ASSETCHAINS_VERUSHASH;
564         ASSETCHAINS_LWMAPOS = 50;
565
566         ASSETCHAINS_TIMELOCKGTE = _ASSETCHAINS_TIMELOCKOFF;
567         ASSETCHAINS_TIMEUNLOCKFROM = 0;
568         ASSETCHAINS_TIMEUNLOCKTO = 0;
569
570         auto numEras = ConnectedChains.ThisChain().eras;
571         ASSETCHAINS_LASTERA = numEras - 1;
572         mapArgs["-ac_eras"] = to_string(numEras);
573
574         mapArgs["-ac_end"] = "";
575         mapArgs["-ac_reward"] = "";
576         mapArgs["-ac_halving"] = "";
577         mapArgs["-ac_decay"] = "";
578
579         for (int j = 0; j < ASSETCHAINS_MAX_ERAS; j++)
580         {
581             if (j > ASSETCHAINS_LASTERA)
582             {
583                 ASSETCHAINS_REWARD[j] = ASSETCHAINS_REWARD[j-1];
584                 ASSETCHAINS_DECAY[j] = ASSETCHAINS_DECAY[j-1];
585                 ASSETCHAINS_HALVING[j] = ASSETCHAINS_HALVING[j-1];
586                 ASSETCHAINS_ENDSUBSIDY[j] = 0;
587             }
588             else
589             {
590                 ASSETCHAINS_REWARD[j] = ConnectedChains.ThisChain().rewards[j];
591                 ASSETCHAINS_DECAY[j] = ConnectedChains.ThisChain().rewardsDecay[j];
592                 ASSETCHAINS_HALVING[j] = ConnectedChains.ThisChain().halving[j];
593                 ASSETCHAINS_ENDSUBSIDY[j] = ConnectedChains.ThisChain().eraEnd[j];
594                 if (j == 0)
595                 {
596                     mapArgs["-ac_reward"] = to_string(ASSETCHAINS_REWARD[j]);
597                     mapArgs["-ac_decay"] = to_string(ASSETCHAINS_DECAY[j]);
598                     mapArgs["-ac_halving"] = to_string(ASSETCHAINS_HALVING[j]);
599                     mapArgs["-ac_end"] = to_string(ASSETCHAINS_ENDSUBSIDY[j]);
600                 }
601                 else
602                 {
603                     mapArgs["-ac_reward"] += "," + to_string(ASSETCHAINS_REWARD[j]);
604                     mapArgs["-ac_decay"] += "," + to_string(ASSETCHAINS_DECAY[j]);
605                     mapArgs["-ac_halving"] += "," + to_string(ASSETCHAINS_HALVING[j]);
606                     mapArgs["-ac_end"] += "," + to_string(ASSETCHAINS_ENDSUBSIDY[j]);
607                 }
608             }
609         }
610
611         PBAAS_STARTBLOCK = ConnectedChains.ThisChain().startBlock;
612         mapArgs["-startblock"] = to_string(PBAAS_STARTBLOCK);
613         PBAAS_ENDBLOCK = ConnectedChains.ThisChain().endBlock;
614         mapArgs["-endblock"] = to_string(PBAAS_ENDBLOCK);
615
616         return true;
617     }
618     else
619     {
620         return false;
621     }
622 }
623
624 // ensures that the chain definition is valid and that there are no other definitions of the same name
625 // that have been confirmed.
626 bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
627 {
628     // the chain definition output can be spent when the chain is at the end of its life and only then
629     // TODO
630     return false;
631 }
632
633 // ensures that the chain definition is valid and that there are no other definitions of the same name
634 // that have been confirmed.
635 bool CheckChainDefinitionOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
636 {
637     // checked before a chain definition output script is accepted as a valid transaction
638
639     // basics - we need a chain definition transaction to kick off a PBaaS chain. it must have:
640     // 1) valid chain definition output with parameters in proper ranges and no duplicate name
641     // 2) notarization output with conformant values
642     // 3) finalization output
643     // 3) notarization funding
644     //
645
646     // get the source transaction
647     uint256 blkHash;
648     CTransaction thisTx;
649     if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
650     {
651         LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
652         return false;
653     }
654
655     CPBaaSChainDefinition chainDef(thisTx, true);
656     CPBaaSNotarization notarization(thisTx, true);
657     CNotarizationFinalization finalization(thisTx, true);
658
659     if (!chainDef.IsValid() || !notarization.IsValid() || finalization.IsValid())
660     {
661         LogPrintf("transaction specified, %s, must have valid chain definition, notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
662         return false;
663     }
664
665     CPBaaSChainDefinition prior;
666     // this ensures that there is no other definition of the same name already on the blockchain
667     if (!GetChainDefinition(chainDef.name, prior))
668     {
669         LogPrintf("PBaaS chain with the name %s already exists\n", chainDef.name.c_str());
670         return false;
671     }
672
673     return true;
674 }
675
676 CAmount CCrossChainExport::CalculateExportFee() const
677 {
678     if (numInputs > MAX_EXPORT_INPUTS)
679     {
680         return 0;
681     }
682     static const arith_uint256 satoshis(100000000);
683
684     int64_t ratio = 50000000 + ((25000000 / MAX_EXPORT_INPUTS) * numInputs);
685
686     return (((arith_uint256(totalFees) * arith_uint256(ratio))) / satoshis).GetLow64();
687 }
688
689 bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
690 {
691     bool retval = false;
692     LOCK(cs_mergemining);
693
694     //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
695
696     auto chainIt = mergeMinedChains.find(chainID);
697     if (chainIt != mergeMinedChains.end())
698     {
699         arith_uint256 target;
700         target.SetCompact(chainIt->second.block.nBits);
701         for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
702         {
703             // make sure we don't just match by target
704             if (removeRange.first->second->GetChainID() == chainID)
705             {
706                 mergeMinedTargets.erase(removeRange.first);
707                 break;
708             }
709         }
710         mergeMinedChains.erase(chainID);
711         dirty = retval = true;
712
713         // if we get to 0, give the thread a kick to stop waiting for mining
714         //if (!mergeMinedChains.size())
715         //{
716         //    sem_submitthread.post();
717         //}
718     }
719     return retval;
720 }
721
722 // remove merge mined chains added and not updated since a specific time
723 uint32_t CConnectedChains::PruneOldChains(uint32_t pruneBefore)
724 {
725     vector<uint160> toRemove;
726
727     LOCK(cs_mergemining);
728     for (auto blkData : mergeMinedChains)
729     {
730         if (blkData.second.block.nTime < pruneBefore)
731         {
732             toRemove.push_back(blkData.first);
733         }
734     }
735
736     for (auto id : toRemove)
737     {
738         //printf("Pruning chainID: %s\n", id.GetHex().c_str());
739         RemoveMergedBlock(id);
740     }
741 }
742
743 // adds or updates merge mined blocks
744 // returns false if failed to add
745 bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
746 {
747     // determine if we should replace one or add to the merge mine vector
748     {
749         LOCK(cs_mergemining);
750
751         arith_uint256 target;
752         uint160 cID = blkData.GetChainID();
753         auto it = mergeMinedChains.find(cID);
754         if (it != mergeMinedChains.end())
755         {
756             RemoveMergedBlock(cID);             // remove it if already there
757         }
758         target.SetCompact(blkData.block.nBits);
759
760         //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
761
762         mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
763         dirty = true;
764     }
765     return true;
766 }
767
768 bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
769 {
770     {
771         LOCK(cs_mergemining);
772         auto chainIt = mergeMinedChains.find(chainID);
773         if (chainIt != mergeMinedChains.end())
774         {
775             rpcChainData = (CRPCChainData)chainIt->second;
776             return true;
777         }
778         return false;
779     }
780 }
781
782 // this returns a pointer to the data without copy and assumes the lock is held
783 CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
784 {
785     {
786         auto chainIt = mergeMinedChains.find(chainID);
787         if (chainIt != mergeMinedChains.end())
788         {
789             return &chainIt->second;
790         }
791         return NULL;
792     }
793 }
794
795 bool CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
796 {
797     //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
798     {
799         LOCK(cs_mergemining);
800
801         qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
802     }
803     sem_submitthread.post();
804 }
805
806 // get the latest block header and submit one block at a time, returning after there are no more
807 // matching blocks to be found
808 vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
809 {
810     std::set<uint160> inHeader;
811     bool submissionFound;
812     CPBaaSMergeMinedChainData chainData;
813     vector<pair<string, UniValue>>  results;
814
815     CBlockHeader bh;
816     arith_uint256 lastHash;
817     CPBaaSBlockHeader pbh;
818
819     do
820     {
821         submissionFound = false;
822         {
823             LOCK(cs_mergemining);
824             // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
825             // common, merge mined headers for notarization, drop out on any submission
826             for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
827             {
828                 // add the PBaaS chain ids from this header to a set for search
829                 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
830                 {
831                     inHeader.insert(pbh.chainID);
832                 }
833
834                 uint160 chainID;
835                 // now look through all targets that are equal to or above the hash of this header
836                 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
837                 {
838                     chainID = chainIt->second->GetChainID();
839                     if (inHeader.count(chainID))
840                     {
841                         // first, check that the winning header matches the block that is there
842                         CPBaaSPreHeader preHeader(chainIt->second->block);
843                         preHeader.SetBlockData(headerIt->second);
844
845                         // check if the block header matches the block's specific data, only then can we create a submission from this block
846                         if (headerIt->second.CheckNonCanonicalData(chainID))
847                         {
848                             // save block as is, remove the block from merged headers, replace header, and submit
849                             chainData = *chainIt->second;
850
851                             *(CBlockHeader *)&chainData.block = headerIt->second;
852
853                             submissionFound = true;
854                         }
855                         //else // not an error condition. code is here for debugging
856                         //{
857                         //    printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
858                         //}
859                     }
860                     //else // not an error condition. code is here for debugging
861                     //{
862                     //    printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
863                     //}
864                 }
865
866                 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
867                 if (submissionFound)
868                 {
869                     // once it is going to be submitted, remove block from this chain until a new one is added again
870                     RemoveMergedBlock(chainID);
871                     break;
872                 }
873                 else
874                 {
875                     qualifiedHeaders.erase(headerIt);
876                 }
877             }
878         }
879         if (submissionFound)
880         {
881             // submit one block and loop again. this approach allows multiple threads
882             // to collectively empty the submission queue, mitigating the impact of
883             // any one stalled daemon
884             UniValue submitParams(UniValue::VARR);
885             submitParams.push_back(EncodeHexBlk(chainData.block));
886             UniValue result, error;
887             try
888             {
889                 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
890                 result = find_value(result, "result");
891                 error = find_value(result, "error");
892             }
893             catch (exception e)
894             {
895                 result = UniValue(e.what());
896             }
897             results.push_back(make_pair(chainData.chainDefinition.name, result));
898             if (result.isStr() || !error.isNull())
899             {
900                 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());
901             }
902             else
903             {
904                 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
905             }
906         }
907     } while (submissionFound);
908     return results;
909 }
910
911 // add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
912 uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
913 {
914     vector<uint160> inHeader;
915     vector<UniValue> toCombine;
916     arith_uint256 blkHash = UintToArith256(bh.GetHash());
917     arith_uint256 target(0);
918     
919     CPBaaSBlockHeader pbh;
920
921     {
922         LOCK(cs_mergemining);
923
924         CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
925
926         for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
927         {
928             if (bh.GetPBaaSHeader(pbh, i))
929             {
930                 inHeader.push_back(pbh.chainID);
931             }
932         }
933
934         // loop through the existing PBaaS chain ids in the header
935         // remove any that are not either this Chain ID or in our local collection and then add all that are present
936         for (uint32_t i = 0; i < inHeader.size(); i++)
937         {
938             auto it = mergeMinedChains.find(inHeader[i]);
939             if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
940             {
941                 bh.DeletePBaaSHeader(i);
942             }
943         }
944
945         for (auto chain : mergeMinedChains)
946         {
947             // get the native PBaaS header for each chain and put it into the
948             // header we are given
949             // it must have itself in as a PBaaS header
950             uint160 cid = chain.second.GetChainID();
951             if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
952             {
953                 if (!bh.AddUpdatePBaaSHeader(pbh))
954                 {
955                     LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
956                     break;
957                 }
958                 else
959                 {
960                     arith_uint256 t;
961                     t.SetCompact(chain.second.block.nBits);
962                     if (t > target)
963                     {
964                         target = t;
965                     }
966                 }
967             }
968             else
969             {
970                 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
971             }
972             
973         }
974         dirty = false;
975     }
976
977     return target.GetCompact();
978 }
979
980 bool CConnectedChains::IsVerusPBaaSAvailable()
981 {
982     return notaryChainVersion > "0.6";
983 }
984
985 extern string PBAAS_HOST, PBAAS_USERPASS;
986 extern int32_t PBAAS_PORT;
987 bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
988 {
989     if (chainInfoUni.isObject() && chainDefUni.isObject())
990     {
991         UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
992         if (uniVer.isStr())
993         {
994             LOCK(cs_mergemining);
995             notaryChainVersion = uni_get_str(uniVer);
996             notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
997             CPBaaSChainDefinition chainDef(chainDefUni);
998             notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
999         }
1000     }
1001     return IsVerusPBaaSAvailable();
1002 }
1003
1004 bool CConnectedChains::CheckVerusPBaaSAvailable()
1005 {
1006     if (IsVerusActive())
1007     {
1008         notaryChainVersion = "";
1009     }
1010     else
1011     {
1012         // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
1013         // tolerate only 15 second timeout
1014         UniValue chainInfo, chainDef;
1015         try
1016         {
1017             UniValue params(UniValue::VARR);
1018             chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
1019             if (!chainInfo.isNull())
1020             {
1021                 params.push_back(VERUS_CHAINNAME);
1022                 chainDef = find_value(RPCCallRoot("getchaindefinition", params), "result");
1023
1024                 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
1025                 {
1026                     return true;
1027                 }
1028             }
1029         } catch (exception e)
1030         {
1031         }
1032     }
1033     notaryChainVersion = "";
1034     return false;
1035 }
1036
1037 CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
1038 {
1039     CCoinbaseCurrencyState currencyState;
1040     CBlock block;
1041     LOCK(cs_main);
1042     if (!IsVerusActive() && 
1043         CConstVerusSolutionVector::activationHeight.ActiveVersion(height) >= CActivationHeight::SOLUTION_VERUSV3 &&
1044         height != 0 && 
1045         height <= chainActive.Height() && 
1046         chainActive[height] && 
1047         ReadBlockFromDisk(block, chainActive[height], false))
1048     {
1049         currencyState = CCoinbaseCurrencyState(block.vtx[0]);
1050     }
1051     return currencyState;
1052 }
1053
1054 void CConnectedChains::SubmissionThread()
1055 {
1056     try
1057     {
1058         arith_uint256 lastHash;
1059         
1060         // wait for something to check on, then submit blocks that should be submitted
1061         while (true)
1062         {
1063             if (IsVerusActive())
1064             {
1065                 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
1066                 //printf("SubmissionThread: pruning\n");
1067                 ConnectedChains.PruneOldChains(GetAdjustedTime() - 300);
1068                 bool submit = false;
1069                 {
1070                     LOCK(cs_mergemining);
1071                     if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
1072                     {
1073                         qualifiedHeaders.clear();
1074                     }
1075                     submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
1076
1077                     //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
1078                 }
1079                 if (submit)
1080                 {
1081                     //printf("SubmissionThread: calling submit qualified blocks\n");
1082                     SubmitQualifiedBlocks();
1083                 }
1084                 else
1085                 {
1086                     //printf("SubmissionThread: waiting on sem\n");
1087                     sem_submitthread.wait();
1088                 }
1089             }
1090             else
1091             {
1092                 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
1093                 CheckVerusPBaaSAvailable();
1094
1095                 // check to see if we have recently earned a block with an earned notarization that qualifies for
1096                 // submitting an accepted notarization
1097                 if (earnedNotarizationHeight)
1098                 {
1099                     CBlock blk;
1100                     int32_t txIndex = -1, height;
1101                     {
1102                         LOCK(cs_mergemining);
1103                         if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
1104                         {
1105                             blk = earnedNotarizationBlock;
1106                             earnedNotarizationBlock = CBlock();
1107                             txIndex = earnedNotarizationIndex;
1108                             height = earnedNotarizationHeight;
1109                             earnedNotarizationHeight = 0;
1110                         }
1111                     }
1112
1113                     if (txIndex != -1)
1114                     {
1115                         //printf("SubmissionThread: testing notarization\n");
1116
1117                         uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
1118
1119                         if (!txId.IsNull())
1120                         {
1121                             printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1122                             LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1123                         }
1124                     }
1125                 }
1126                 sleep(1);
1127             }
1128             boost::this_thread::interruption_point();
1129         }
1130     }
1131     catch (const boost::thread_interrupted&)
1132     {
1133         LogPrintf("Verus merge mining thread terminated\n");
1134     }
1135 }
1136
1137 void CConnectedChains::SubmissionThreadStub()
1138 {
1139     ConnectedChains.SubmissionThread();
1140 }
1141
1142 void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
1143 {
1144     // called after winning a block that contains an earned notarization
1145     // the earned notarization and its height are queued for processing by the submission thread
1146     // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
1147     // kept
1148     LOCK(cs_mergemining);
1149
1150     // we only care about the last
1151     earnedNotarizationHeight = height;
1152     earnedNotarizationBlock = blk;
1153     earnedNotarizationIndex = txIndex;
1154 }
1155
1156 bool IsChainDefinitionInput(const CScript &scriptSig)
1157 {
1158     uint32_t ecode;
1159     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_PBAASDEFINITION;
1160 }
1161
1162 bool IsServiceRewardInput(const CScript &scriptSig)
1163 {
1164     // this is an output check, and is incorrect. need to change to input
1165     uint32_t ecode;
1166     return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_SERVICEREWARD;
1167 }
This page took 0.087836 seconds and 4 git commands to generate.