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