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