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