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