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