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