More flexibility in spending the last import
[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;
e5961f2e 652 ASSETCHAINS_ERAOPTIONS[j] = 0;
f8f61a6d 653 }
654 else
655 {
656 ASSETCHAINS_REWARD[j] = ConnectedChains.ThisChain().rewards[j];
657 ASSETCHAINS_DECAY[j] = ConnectedChains.ThisChain().rewardsDecay[j];
658 ASSETCHAINS_HALVING[j] = ConnectedChains.ThisChain().halving[j];
659 ASSETCHAINS_ENDSUBSIDY[j] = ConnectedChains.ThisChain().eraEnd[j];
e5961f2e 660 ASSETCHAINS_ERAOPTIONS[j] = ConnectedChains.ThisChain().eraOptions[j];
f8f61a6d 661 if (j == 0)
662 {
663 mapArgs["-ac_reward"] = to_string(ASSETCHAINS_REWARD[j]);
664 mapArgs["-ac_decay"] = to_string(ASSETCHAINS_DECAY[j]);
665 mapArgs["-ac_halving"] = to_string(ASSETCHAINS_HALVING[j]);
666 mapArgs["-ac_end"] = to_string(ASSETCHAINS_ENDSUBSIDY[j]);
70c48c13 667 mapArgs["-ac_options"] = to_string(ASSETCHAINS_ERAOPTIONS[j]);
f8f61a6d 668 }
669 else
670 {
671 mapArgs["-ac_reward"] += "," + to_string(ASSETCHAINS_REWARD[j]);
672 mapArgs["-ac_decay"] += "," + to_string(ASSETCHAINS_DECAY[j]);
673 mapArgs["-ac_halving"] += "," + to_string(ASSETCHAINS_HALVING[j]);
674 mapArgs["-ac_end"] += "," + to_string(ASSETCHAINS_ENDSUBSIDY[j]);
70c48c13 675 mapArgs["-ac_options"] += "," + to_string(ASSETCHAINS_ERAOPTIONS[j]);
f8f61a6d 676 }
677 }
678 }
679
680 PBAAS_STARTBLOCK = ConnectedChains.ThisChain().startBlock;
681 mapArgs["-startblock"] = to_string(PBAAS_STARTBLOCK);
682 PBAAS_ENDBLOCK = ConnectedChains.ThisChain().endBlock;
683 mapArgs["-endblock"] = to_string(PBAAS_ENDBLOCK);
684
40951949 685 PBAAS_PRECONVERSION = ConnectedChains.ThisChain().conversion;
686 PBAAS_MINPRECONVERT = ConnectedChains.ThisChain().minpreconvert;
687 PBAAS_MAXPRECONVERT = ConnectedChains.ThisChain().maxpreconvert;
688 mapArgs["-ac_conversion"] = to_string(PBAAS_PRECONVERSION);
689 mapArgs["-ac_minpreconvert"] = to_string(PBAAS_MINPRECONVERT);
690 mapArgs["-ac_maxpreconvert"] = to_string(PBAAS_MAXPRECONVERT);
f8f61a6d 691 return true;
692 }
693 else
694 {
695 return false;
9f0c14b2
MT
696 }
697}
698
b2a98c42
MT
699// ensures that the chain definition is valid and that there are no other definitions of the same name
700// that have been confirmed.
701bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
702{
703 // the chain definition output can be spent when the chain is at the end of its life and only then
704 // TODO
705 return false;
706}
707
708// ensures that the chain definition is valid and that there are no other definitions of the same name
709// that have been confirmed.
710bool CheckChainDefinitionOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
711{
712 // checked before a chain definition output script is accepted as a valid transaction
713
714 // basics - we need a chain definition transaction to kick off a PBaaS chain. it must have:
715 // 1) valid chain definition output with parameters in proper ranges and no duplicate name
716 // 2) notarization output with conformant values
717 // 3) finalization output
718 // 3) notarization funding
719 //
720
721 // get the source transaction
722 uint256 blkHash;
723 CTransaction thisTx;
6c5d1151 724 if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
b2a98c42
MT
725 {
726 LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
727 return false;
728 }
729
730 CPBaaSChainDefinition chainDef(thisTx, true);
731 CPBaaSNotarization notarization(thisTx, true);
732 CNotarizationFinalization finalization(thisTx, true);
733
734 if (!chainDef.IsValid() || !notarization.IsValid() || finalization.IsValid())
735 {
736 LogPrintf("transaction specified, %s, must have valid chain definition, notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
737 return false;
738 }
739
740 CPBaaSChainDefinition prior;
741 // this ensures that there is no other definition of the same name already on the blockchain
742 if (!GetChainDefinition(chainDef.name, prior))
743 {
744 LogPrintf("PBaaS chain with the name %s already exists\n", chainDef.name.c_str());
745 return false;
746 }
747
748 return true;
749}
750
989b1de1
MT
751CAmount CCrossChainExport::CalculateExportFee() const
752{
753 if (numInputs > MAX_EXPORT_INPUTS)
754 {
755 return 0;
756 }
757 static const arith_uint256 satoshis(100000000);
758
759 int64_t ratio = 50000000 + ((25000000 / MAX_EXPORT_INPUTS) * numInputs);
760
761 return (((arith_uint256(totalFees) * arith_uint256(ratio))) / satoshis).GetLow64();
762}
763
b2a98c42
MT
764bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
765{
2fd1f0fb 766 bool retval = false;
b2a98c42 767 LOCK(cs_mergemining);
23d61f0a 768
3016973b 769 //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
23d61f0a 770
b2a98c42
MT
771 auto chainIt = mergeMinedChains.find(chainID);
772 if (chainIt != mergeMinedChains.end())
773 {
774 arith_uint256 target;
775 target.SetCompact(chainIt->second.block.nBits);
776 for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
777 {
778 // make sure we don't just match by target
779 if (removeRange.first->second->GetChainID() == chainID)
780 {
781 mergeMinedTargets.erase(removeRange.first);
782 break;
783 }
784 }
785 mergeMinedChains.erase(chainID);
2fd1f0fb 786 dirty = retval = true;
b2a98c42
MT
787
788 // if we get to 0, give the thread a kick to stop waiting for mining
2fd1f0fb 789 //if (!mergeMinedChains.size())
790 //{
791 // sem_submitthread.post();
792 //}
b2a98c42 793 }
2fd1f0fb 794 return retval;
b2a98c42
MT
795}
796
797// remove merge mined chains added and not updated since a specific time
798uint32_t CConnectedChains::PruneOldChains(uint32_t pruneBefore)
799{
800 vector<uint160> toRemove;
801
802 LOCK(cs_mergemining);
803 for (auto blkData : mergeMinedChains)
804 {
805 if (blkData.second.block.nTime < pruneBefore)
806 {
807 toRemove.push_back(blkData.first);
808 }
809 }
810
811 for (auto id : toRemove)
812 {
3016973b 813 //printf("Pruning chainID: %s\n", id.GetHex().c_str());
b2a98c42
MT
814 RemoveMergedBlock(id);
815 }
816}
817
818// adds or updates merge mined blocks
819// returns false if failed to add
820bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
821{
b2a98c42
MT
822 // determine if we should replace one or add to the merge mine vector
823 {
824 LOCK(cs_mergemining);
825
2fd1f0fb 826 arith_uint256 target;
b2a98c42
MT
827 uint160 cID = blkData.GetChainID();
828 auto it = mergeMinedChains.find(cID);
829 if (it != mergeMinedChains.end())
830 {
1fa4454d 831 RemoveMergedBlock(cID); // remove it if already there
b2a98c42 832 }
1fa4454d 833 target.SetCompact(blkData.block.nBits);
23d61f0a 834
3016973b 835 //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
23d61f0a 836
1fa4454d 837 mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
2830db29 838 dirty = true;
b2a98c42 839 }
bce52b27 840 return true;
b2a98c42
MT
841}
842
843bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
844{
845 {
846 LOCK(cs_mergemining);
847 auto chainIt = mergeMinedChains.find(chainID);
848 if (chainIt != mergeMinedChains.end())
849 {
850 rpcChainData = (CRPCChainData)chainIt->second;
851 return true;
852 }
853 return false;
854 }
855}
856
857// this returns a pointer to the data without copy and assumes the lock is held
858CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
859{
860 {
861 auto chainIt = mergeMinedChains.find(chainID);
862 if (chainIt != mergeMinedChains.end())
863 {
864 return &chainIt->second;
865 }
866 return NULL;
867 }
868}
869
e771a884 870bool CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
871{
3016973b 872 //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
2830db29 873 {
874 LOCK(cs_mergemining);
cff3c5ad 875
2830db29 876 qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
877 }
e771a884 878 sem_submitthread.post();
879}
880
f8f61a6d 881// get the latest block header and submit one block at a time, returning after there are no more
882// matching blocks to be found
883vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
b2a98c42
MT
884{
885 std::set<uint160> inHeader;
f8f61a6d 886 bool submissionFound;
887 CPBaaSMergeMinedChainData chainData;
b2a98c42
MT
888 vector<pair<string, UniValue>> results;
889
f8f61a6d 890 CBlockHeader bh;
891 arith_uint256 lastHash;
b2a98c42
MT
892 CPBaaSBlockHeader pbh;
893
02560af7 894 do
b2a98c42 895 {
02560af7 896 submissionFound = false;
b2a98c42 897 {
02560af7 898 LOCK(cs_mergemining);
899 // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
900 // common, merge mined headers for notarization, drop out on any submission
901 for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
b2a98c42 902 {
02560af7 903 // add the PBaaS chain ids from this header to a set for search
904 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
f8f61a6d 905 {
02560af7 906 inHeader.insert(pbh.chainID);
907 }
b2a98c42 908
33d6f38a 909 uint160 chainID;
02560af7 910 // now look through all targets that are equal to or above the hash of this header
911 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
912 {
33d6f38a 913 chainID = chainIt->second->GetChainID();
02560af7 914 if (inHeader.count(chainID))
2830db29 915 {
02560af7 916 // first, check that the winning header matches the block that is there
917 CPBaaSPreHeader preHeader(chainIt->second->block);
918 preHeader.SetBlockData(headerIt->second);
919
920 // check if the block header matches the block's specific data, only then can we create a submission from this block
921 if (headerIt->second.CheckNonCanonicalData(chainID))
f8f61a6d 922 {
02560af7 923 // save block as is, remove the block from merged headers, replace header, and submit
924 chainData = *chainIt->second;
925
926 *(CBlockHeader *)&chainData.block = headerIt->second;
927
02560af7 928 submissionFound = true;
f8f61a6d 929 }
3016973b
MT
930 //else // not an error condition. code is here for debugging
931 //{
932 // printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
933 //}
cff3c5ad 934 }
3016973b
MT
935 //else // not an error condition. code is here for debugging
936 //{
937 // printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
938 //}
02560af7 939 }
2830db29 940
02560af7 941 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
33d6f38a
MT
942 if (submissionFound)
943 {
944 // once it is going to be submitted, remove block from this chain until a new one is added again
945 RemoveMergedBlock(chainID);
946 break;
947 }
948 else
02560af7 949 {
950 qualifiedHeaders.erase(headerIt);
b2a98c42 951 }
f8f61a6d 952 }
02560af7 953 }
954 if (submissionFound)
955 {
956 // submit one block and loop again. this approach allows multiple threads
957 // to collectively empty the submission queue, mitigating the impact of
958 // any one stalled daemon
959 UniValue submitParams(UniValue::VARR);
960 submitParams.push_back(EncodeHexBlk(chainData.block));
961 UniValue result, error;
962 try
f8f61a6d 963 {
02560af7 964 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
965 result = find_value(result, "result");
966 error = find_value(result, "error");
b2a98c42 967 }
02560af7 968 catch (exception e)
969 {
970 result = UniValue(e.what());
971 }
972 results.push_back(make_pair(chainData.chainDefinition.name, result));
973 if (result.isStr() || !error.isNull())
974 {
975 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());
976 }
977 else
978 {
979 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
980 }
981 }
982 } while (submissionFound);
b2a98c42
MT
983 return results;
984}
985
f8f61a6d 986// add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
b2a98c42
MT
987uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
988{
989 vector<uint160> inHeader;
990 vector<UniValue> toCombine;
991 arith_uint256 blkHash = UintToArith256(bh.GetHash());
f8f61a6d 992 arith_uint256 target(0);
b2a98c42
MT
993
994 CPBaaSBlockHeader pbh;
995
b2a98c42 996 {
f8f61a6d 997 LOCK(cs_mergemining);
b2a98c42 998
f8f61a6d 999 CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
1000
1001 for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
b2a98c42 1002 {
f8f61a6d 1003 if (bh.GetPBaaSHeader(pbh, i))
1004 {
1005 inHeader.push_back(pbh.chainID);
1006 }
1007 }
1008
1009 // loop through the existing PBaaS chain ids in the header
2fd1f0fb 1010 // remove any that are not either this Chain ID or in our local collection and then add all that are present
f8f61a6d 1011 for (uint32_t i = 0; i < inHeader.size(); i++)
1012 {
1013 auto it = mergeMinedChains.find(inHeader[i]);
1014 if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
1015 {
1016 bh.DeletePBaaSHeader(i);
1017 }
b2a98c42 1018 }
b2a98c42 1019
b2a98c42
MT
1020 for (auto chain : mergeMinedChains)
1021 {
1022 // get the native PBaaS header for each chain and put it into the
1023 // header we are given
326f5f88 1024 // it must have itself in as a PBaaS header
b2a98c42 1025 uint160 cid = chain.second.GetChainID();
3a2a1777 1026 if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
b2a98c42
MT
1027 {
1028 if (!bh.AddUpdatePBaaSHeader(pbh))
1029 {
1030 LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
f8f61a6d 1031 break;
1032 }
1033 else
1034 {
1035 arith_uint256 t;
1036 t.SetCompact(chain.second.block.nBits);
1037 if (t > target)
1038 {
1039 target = t;
1040 }
b2a98c42
MT
1041 }
1042 }
326f5f88
MT
1043 else
1044 {
1045 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
1046 }
1047
b2a98c42 1048 }
2830db29 1049 dirty = false;
b2a98c42 1050 }
326f5f88 1051
f8f61a6d 1052 return target.GetCompact();
b2a98c42
MT
1053}
1054
ccac80a3 1055bool CConnectedChains::IsVerusPBaaSAvailable()
1056{
1057 return notaryChainVersion > "0.6";
1058}
1059
f8f61a6d 1060extern string PBAAS_HOST, PBAAS_USERPASS;
1061extern int32_t PBAAS_PORT;
6bb9fdbc 1062bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
9f0c14b2 1063{
6bb9fdbc 1064 if (chainInfoUni.isObject() && chainDefUni.isObject())
9f0c14b2 1065 {
6bb9fdbc 1066 UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
f8f61a6d 1067 if (uniVer.isStr())
1068 {
2156d3d0 1069 LOCK(cs_mergemining);
f8f61a6d 1070 notaryChainVersion = uni_get_str(uniVer);
6bb9fdbc 1071 notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
1072 CPBaaSChainDefinition chainDef(chainDefUni);
f8f61a6d 1073 notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
1074 }
9f0c14b2 1075 }
ccac80a3 1076 return IsVerusPBaaSAvailable();
9f0c14b2
MT
1077}
1078
2299bd95 1079bool CConnectedChains::CheckVerusPBaaSAvailable()
9f0c14b2
MT
1080{
1081 if (IsVerusActive())
1082 {
ccac80a3 1083 notaryChainVersion = "";
9f0c14b2
MT
1084 }
1085 else
1086 {
1087 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
2156d3d0 1088 // tolerate only 15 second timeout
9a891caf 1089 UniValue chainInfo, chainDef;
1090 try
1091 {
1092 UniValue params(UniValue::VARR);
2fd1f0fb 1093 chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
9a891caf 1094 if (!chainInfo.isNull())
1095 {
1096 params.push_back(VERUS_CHAINNAME);
2fd1f0fb 1097 chainDef = find_value(RPCCallRoot("getchaindefinition", params), "result");
9a891caf 1098
1099 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
1100 {
1101 return true;
1102 }
1103 }
1104 } catch (exception e)
4fa3b13d 1105 {
4fa3b13d 1106 }
9f0c14b2 1107 }
9a891caf 1108 notaryChainVersion = "";
4fa3b13d 1109 return false;
9f0c14b2
MT
1110}
1111
41f170fd
MT
1112CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
1113{
1114 CCoinbaseCurrencyState currencyState;
1115 CBlock block;
1116 LOCK(cs_main);
1117 if (!IsVerusActive() &&
1118 CConstVerusSolutionVector::activationHeight.ActiveVersion(height) >= CActivationHeight::SOLUTION_VERUSV3 &&
1119 height != 0 &&
1120 height <= chainActive.Height() &&
1121 chainActive[height] &&
1122 ReadBlockFromDisk(block, chainActive[height], false))
1123 {
1124 currencyState = CCoinbaseCurrencyState(block.vtx[0]);
1125 }
1126 return currencyState;
1127}
1128
c3250dcd
MT
1129bool CConnectedChains::SetLatestMiningOutputs(const std::vector<pair<int, CScript>> minerOutputs, CTxDestination &firstDestinationOut)
1130{
1131 LOCK(cs_mergemining);
1132 {
1133 txnouttype outType;
1134 std::vector<std::vector<unsigned char>> vSolutions;
1135
1136 if (!minerOutputs.size() || !Solver(minerOutputs[0].second, outType, vSolutions))
1137 {
1138 return false;
1139 }
1140
1141 if (outType == TX_PUBKEY)
1142 {
1143 CPubKey pubKey(vSolutions[0]);
1144 if (!pubKey.IsValid())
1145 {
1146 return false;
1147 }
1148 firstDestinationOut = CTxDestination(pubKey);
1149 }
1150 else if (outType == TX_PUBKEYHASH)
1151 {
1152 firstDestinationOut = CTxDestination(CKeyID(uint160(vSolutions[0])));
1153 }
1154 else
1155 {
1156 return false;
1157 }
1158 }
1159 latestMiningOutputs = minerOutputs;
1160 latestDestination = firstDestinationOut;
bb6c3482 1161 return true;
c3250dcd
MT
1162}
1163
1164void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
1165{
1166 // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
1167 {
1168 if (!nHeight)
1169 {
1170 return;
1171 }
1172
1173 multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
1174
c3250dcd
MT
1175 // get all available transfer outputs to aggregate into export transactions
1176 if (GetUnspentChainTransfers(transferOutputs))
1177 {
bb6c3482 1178 if (!transferOutputs.size())
1179 {
1180 return;
1181 }
c3250dcd
MT
1182 std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
1183 std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1184
78a36746 1185 uint160 bookEnd;
1186 bookEnd.SetHex("ffffffffffffffffffffffffffffffffffffffff");
1187 uint160 lastChain = bookEnd;
41ad1f62 1188
78a36746 1189 // add a bookend entry at the end of transfer outputs to ensure that we try to export all before it
1190 transferOutputs.insert(make_pair(bookEnd, make_pair(CInputDescriptor(), CReserveTransfer())));
c3250dcd 1191
78a36746 1192 // merge all of the common chainID outputs into common export transactions if either MIN_BLOCKS blocks have passed since the last
1193 // export of that type, or there are MIN_INPUTS or more outputs to aggregate
1194 for (auto it = transferOutputs.begin(); it != transferOutputs.end(); it++)
1195 {
1196 // get chain target and see if it is the same
1197 if (lastChain == bookEnd || it->first == lastChain)
c3250dcd 1198 {
78a36746 1199 txInputs.push_back(it->second);
1200 lastChain = it->first;
1201 }
1202 else
1203 {
1204 // when we get here, we have a consecutive number of transfer outputs to consume in txInputs
1205 // we need an unspent export output to export
1206 if (GetUnspentChainExports(lastChain, exportOutputs) && exportOutputs.size())
c3250dcd 1207 {
78a36746 1208 auto lastExport = *exportOutputs.begin();
c3250dcd 1209
78a36746 1210 if (((nHeight - lastExport.second.first) >= CCrossChainExport::MIN_BLOCKS) || (txInputs.size() >= CCrossChainExport::MIN_INPUTS))
c3250dcd
MT
1211 {
1212 boost::optional<CTransaction> oneExport;
1213
1214 // make one or more transactions that spends the last export and all possible cross chain transfers
1215 while (txInputs.size())
1216 {
1217 TransactionBuilder tb(Params().GetConsensus(), nHeight);
1218
1219 int numInputs = (txInputs.size() < CCrossChainExport::MAX_EXPORT_INPUTS) ? txInputs.size() : CCrossChainExport::MAX_EXPORT_INPUTS;
1220
1221 int inputsLeft = txInputs.size() - numInputs;
1222
1223 if (inputsLeft > 0 && inputsLeft < CCrossChainExport::MIN_INPUTS)
1224 {
1225 inputsLeft += CCrossChainExport::MIN_INPUTS - inputsLeft;
1226 numInputs -= CCrossChainExport::MIN_INPUTS - inputsLeft;
1227 assert(numInputs > 0);
1228 }
1229
1230 // each time through, we make one export transaction with the remainder or a subset of the
1231 // reserve transfer inputs. inputs can be:
1232 // 1. transfers of reserve for fractional reserve chains
1233 // 2. pre-conversions for pre-launch participation in the premine
1234 // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
1235 //
1236 // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
1237 // or reserve token inputs.
1238 //
1239 // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
1240 // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
1241 // as part of the import process
1242 //
1243 // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain.
1244 // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the
1245 // remaining Verus reserve coin considered burned.
1246 //
1247 CAmount totalTxFees = 0;
1248 CAmount totalAmount = 0;
1249 std::vector<CBaseChainObject *> chainObjects;
1250
1251 // first, we must add the export output from the current export thread to this chain
1252 if (oneExport.has_value())
1253 {
1254 // spend the last export transaction output
1255 CTransaction &tx = oneExport.get();
1256 COptCCParams p;
1257 int j;
1258 for (j = 0; j < tx.vout.size(); j++)
1259 {
1260 if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) && p.evalCode == EVAL_CROSSCHAIN_EXPORT)
1261 {
1262 break;
1263 }
1264 }
1265
1266 // had to be found and valid if we made the tx
1267 assert(j < tx.vout.size() && p.IsValid());
1268
1269 tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
1270 }
1271 else
1272 {
1273 // spend the recentExportIt output
78a36746 1274 tb.AddTransparentInput(lastExport.second.second.txIn.prevout, lastExport.second.second.scriptPubKey, lastExport.second.second.nValue);
c3250dcd
MT
1275 }
1276
1277 for (int j = 0; j < numInputs; j++)
1278 {
1279 tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
1280 totalTxFees += txInputs[j].second.nFees;
1281 totalAmount += txInputs[j].second.nValue;
1282 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(txInputs[j].second), txInputs[j].second));
1283 }
1284
1285 CCrossChainExport ccx(lastChain, numInputs, totalAmount, totalTxFees);
1286 CAmount exportFees = ccx.CalculateExportFee();
1287 CReserveTransfer feeOut = CReserveTransfer(CReserveTransfer::VALID + CReserveTransfer::SEND_BACK + CReserveTransfer::FEE_OUTPUT,
1288 exportFees, 0, GetDestinationID(feeOutput));
1289 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
1290
1291 CScript opRet = StoreOpRetArray(chainObjects);
1292 DeleteOpRetObjects(chainObjects);
1293
1294 CCcontract_info CC;
1295 CCcontract_info *cp;
1296 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
1297
1298 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
1299
78a36746 1300 // send native amount of zero to a cross chain export output of the specific chain
c3250dcd
MT
1301 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_CROSSCHAIN_EXPORT))});
1302
78a36746 1303 CTxOut exportOut = MakeCC1of1Vout(EVAL_CROSSCHAIN_EXPORT, 0, pk, dests, ccx);
c3250dcd
MT
1304
1305 tb.AddTransparentOutput(exportOut.scriptPubKey, 0);
1306
1307 // if we are on Verus chain, send all native funds, less fees to reserve deposit CC, which is equivalent to the reserve account
1308 // on a PBaaS reserve chain, input is burned
1309 if (IsVerusActive())
1310 {
1311 cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
1312 pk = CPubKey(ParseHex(CC.CChexstr));
1313
1314 // send the entire amount to a reserve transfer output of the specific chain
1315 // we receive our fee on the other chain or when it comes back
1316 dests = std::vector<CTxDestination>({CKeyID(CCrossChainRPCData::GetConditionID(lastChain, EVAL_RESERVE_DEPOSIT))});
1317
02bb1fdb 1318 CReserveOutput ro(CReserveOutput::VALID, totalAmount);
c3250dcd
MT
1319
1320 CTxOut outToReserve = MakeCC1of1Vout(EVAL_RESERVE_DEPOSIT,
78a36746 1321 ro.nValue,
1322 pk,
1323 dests,
1324 ro);
c3250dcd
MT
1325
1326 tb.AddTransparentOutput(outToReserve.scriptPubKey, ro.nValue);
1327 }
1328
1329 tb.AddOpRet(opRet);
7acea90d 1330 tb.SetFee(0);
c3250dcd
MT
1331
1332 boost::optional<CTransaction> newExport = tb.Build();
1333
1334 if (newExport.has_value())
1335 {
1336 // replace the last one only if we have a valid new one
1337 oneExport = newExport;
1338 CTransaction &tx = oneExport.get();
1339
1340 LOCK2(cs_main, mempool.cs);
1341 static int lastHeight = 0;
1342 // remove conflicts, so that we are the now
1343 std::list<CTransaction> removed;
1344 mempool.removeConflicts(tx, removed);
1345
7acea90d 1346 // add to mem pool, prioritize according to the fee we will get, and relay
c3250dcd
MT
1347 if (myAddtomempool(tx))
1348 {
7acea90d 1349 uint256 hash = tx.GetHash();
1e01b81c 1350 mempool.PrioritiseTransaction(hash, hash.GetHex(), (double)(exportFees << 1), exportFees);
c3250dcd
MT
1351 RelayTransaction(tx);
1352 }
1353 }
1354 // erase the inputs we've attempted to spend
1355 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
1356 }
1357 }
1358 }
1359 }
1360 }
1361 }
1362 }
1363}
1364
1365// send new imports from this chain to the specified chain, which generally will be the notary chain
1366void CConnectedChains::SendNewImports(const uint160 &chainID,
1367 const CPBaaSNotarization &notarization,
1368 const uint256 &lastExportTx,
1369 const CTransaction &lastCrossImport,
1370 const CTransaction &lastExport)
1371{
1372 // currently only support sending imports to
1373}
1374
b2a98c42
MT
1375void CConnectedChains::SubmissionThread()
1376{
1377 try
1378 {
1379 arith_uint256 lastHash;
687e93d5
MT
1380 int64_t lastImportTime = 0;
1381 uint32_t lastHeight = 0;
b2a98c42 1382
a82942e4 1383 // wait for something to check on, then submit blocks that should be submitted
b2a98c42
MT
1384 while (true)
1385 {
c3250dcd
MT
1386 boost::this_thread::interruption_point();
1387
9f0c14b2 1388 if (IsVerusActive())
b2a98c42 1389 {
23d61f0a 1390 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
9014248c 1391 //printf("SubmissionThread: pruning\n");
c3250dcd 1392 PruneOldChains(GetAdjustedTime() - 300);
2fd1f0fb 1393 bool submit = false;
b2a98c42 1394 {
2fd1f0fb 1395 LOCK(cs_mergemining);
bea3e6a2
MT
1396 if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
1397 {
326f5f88 1398 qualifiedHeaders.clear();
bea3e6a2 1399 }
2fd1f0fb 1400 submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
23d61f0a 1401
9014248c 1402 //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
2fd1f0fb 1403 }
1404 if (submit)
1405 {
9014248c 1406 //printf("SubmissionThread: calling submit qualified blocks\n");
2fd1f0fb 1407 SubmitQualifiedBlocks();
b2a98c42 1408 }
9f0c14b2
MT
1409 else
1410 {
9014248c 1411 //printf("SubmissionThread: waiting on sem\n");
2fd1f0fb 1412 sem_submitthread.wait();
9f0c14b2 1413 }
b2a98c42
MT
1414 }
1415 else
1416 {
9f0c14b2 1417 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
9a891caf 1418 CheckVerusPBaaSAvailable();
572c763f
MT
1419
1420 // check to see if we have recently earned a block with an earned notarization that qualifies for
1421 // submitting an accepted notarization
94a210aa 1422 if (earnedNotarizationHeight)
2830db29 1423 {
572c763f 1424 CBlock blk;
d6bc5de8 1425 int32_t txIndex = -1, height;
572c763f
MT
1426 {
1427 LOCK(cs_mergemining);
ba256d3a 1428 if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
572c763f 1429 {
94a210aa
MT
1430 blk = earnedNotarizationBlock;
1431 earnedNotarizationBlock = CBlock();
1432 txIndex = earnedNotarizationIndex;
572c763f 1433 height = earnedNotarizationHeight;
94a210aa 1434 earnedNotarizationHeight = 0;
572c763f
MT
1435 }
1436 }
1437
d6bc5de8 1438 if (txIndex != -1)
572c763f 1439 {
f39e2bdf 1440 //printf("SubmissionThread: testing notarization\n");
687e93d5 1441 CTransaction lastConfirmed;
7620ccae 1442 uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
d6bc5de8
MT
1443
1444 if (!txId.IsNull())
1445 {
1446 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1447 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
1448 }
572c763f 1449 }
2830db29 1450 }
687e93d5
MT
1451
1452 if ((GetAdjustedTime() - lastImportTime) >= 30 || lastHeight < (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight()))
1453 {
1454 lastImportTime = GetAdjustedTime();
1455 lastHeight = (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight());
1456
1457 // see if our notary has a confirmed notarization for us
1458 UniValue params(UniValue::VARR);
1459 UniValue result;
1460
1461 params.push_back(thisChain.name);
1462
1463 try
1464 {
1465 result = find_value(RPCCallRoot("getlastimportin", params), "result");
1466 } catch (exception e)
1467 {
1468 result = NullUniValue;
1469 }
1470
1471 if (!result.isNull())
1472 {
1473 auto txUniStr = find_value(result, "lastimporttransaction");
1474 auto txLastConfirmedStr = find_value(result, "lastconfirmednotarization");
1475 auto txTemplateStr = find_value(result, "importtxtemplate");
1476
1477 CTransaction lastImportTx, lastConfirmedTx, templateTx;
1478
1479 if (txUniStr.isStr() && txTemplateStr.isStr() &&
1480 DecodeHexTx(lastImportTx, txUniStr.get_str()) &&
1481 DecodeHexTx(lastConfirmedTx, txLastConfirmedStr.get_str()) &&
1482 DecodeHexTx(templateTx, txTemplateStr.get_str()))
1483 {
1484 std::vector<CTransaction> importTxes;
1485 if (CreateLatestImports(thisChain, lastImportTx, templateTx, lastConfirmedTx, importTxes))
1486 {
1487 for (auto importTx : importTxes)
1488 {
1489 UniValue txResult;
1490 params.clear();
1491 params.push_back(EncodeHexTx(importTx));
1492
1493 try
1494 {
1495 txResult = find_value(RPCCallRoot("signrawtransaction", params), "result");
1496 if (txResult.isStr() && txResult.get_str().size())
1497 {
1498 params.clear();
1499 params.push_back(txResult);
1500 txResult = find_value(RPCCallRoot("sendrawtransaction", params), "result");
1501 }
1502 else
1503 {
1504 txResult = NullUniValue;
1505 }
1506
1507 } catch (exception e)
1508 {
1509 txResult = NullUniValue;
1510 }
1511 uint256 testId;
1512 if (txResult.isStr())
1513 {
1514 testId.SetHex(txResult.get_str());
1515 }
1516 if (testId.IsNull())
1517 {
1518 break;
1519 }
1520 }
1521 }
1522 }
1523 }
1524 }
1525 sleep(3);
b2a98c42 1526 }
b2a98c42
MT
1527 boost::this_thread::interruption_point();
1528 }
1529 }
1530 catch (const boost::thread_interrupted&)
1531 {
1532 LogPrintf("Verus merge mining thread terminated\n");
1533 }
1534}
9f0c14b2 1535
b2a98c42
MT
1536void CConnectedChains::SubmissionThreadStub()
1537{
1538 ConnectedChains.SubmissionThread();
1539}
1540
572c763f
MT
1541void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
1542{
1543 // called after winning a block that contains an earned notarization
1544 // the earned notarization and its height are queued for processing by the submission thread
1545 // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
1546 // kept
1547 LOCK(cs_mergemining);
1548
94a210aa 1549 // we only care about the last
572c763f 1550 earnedNotarizationHeight = height;
94a210aa
MT
1551 earnedNotarizationBlock = blk;
1552 earnedNotarizationIndex = txIndex;
572c763f
MT
1553}
1554
68b309c0
MT
1555bool IsChainDefinitionInput(const CScript &scriptSig)
1556{
1557 uint32_t ecode;
1558 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_PBAASDEFINITION;
1559}
13ed2980 1560
This page took 0.325141 seconds and 4 git commands to generate.