]> Git Repo - VerusCoin.git/blame - src/pbaas/pbaas.cpp
Don't donate carveouts to miners and stakers
[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
1fb6db72 13#include "base58.h"
56fe75cb 14#include "main.h"
b2a98c42 15#include "rpc/pbaasrpc.h"
85c51d62 16#include "timedata.h"
c3250dcd 17#include "transaction_builder.h"
b2a98c42 18
b2a98c42
MT
19CConnectedChains ConnectedChains;
20
21bool IsVerusActive()
22{
23 return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 0 || strcmp(ASSETCHAINS_SYMBOL, "VRSCTEST") == 0);
24}
25
56fe75cb 26bool IsVerusMainnetActive()
27{
28 return (strcmp(ASSETCHAINS_SYMBOL, "VRSC") == 0);
29}
30
b2a98c42
MT
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 {
56fe75cb 53 const CBaseChainObject *retPtr;
54 const CChainObject<CBlockHeaderAndProof> *pNewHeader;
55 const CChainObject<CPartialTransactionProof> *pNewTx;
56 const CChainObject<CBlockHeaderProof> *pNewHeaderRef;
1fa4454d 57 const CChainObject<CPriorBlocksCommitment> *pPriors;
56fe75cb 58 const CChainObject<uint256> *pNewProofRoot;
989b1de1 59 const CChainObject<CReserveTransfer> *pExport;
34d1aa13 60 const CChainObject<CCrossChainProof> *pCrossChainProof;
56fe75cb 61 const CChainObject<CCompositeChainObject> *pCompositeChainObject;
b2a98c42
MT
62 };
63
64 retPtr = &bo;
65
66 switch(bo.objectType)
67 {
68 case CHAINOBJ_HEADER:
69 return pNewHeader->GetHash();
70
56fe75cb 71 case CHAINOBJ_TRANSACTION_PROOF:
b2a98c42
MT
72 return pNewTx->GetHash();
73
b2a98c42
MT
74 case CHAINOBJ_HEADER_REF:
75 return pNewHeaderRef->GetHash();
76
1fa4454d
MT
77 case CHAINOBJ_PRIORBLOCKS:
78 return pPriors->GetHash();
989b1de1 79
56fe75cb 80 case CHAINOBJ_PROOF_ROOT:
81 return pNewProofRoot->object;
82
989b1de1
MT
83 case CHAINOBJ_RESERVETRANSFER:
84 return pExport->GetHash();
34d1aa13
MT
85
86 case CHAINOBJ_CROSSCHAINPROOF:
87 return pCrossChainProof->GetHash();
88
56fe75cb 89 case CHAINOBJ_COMPOSITEOBJECT:
90 return pCrossChainProof->GetHash();
91
b2a98c42
MT
92 }
93 return uint256();
94}
95
96// used to export coins from one chain to another, if they are not native, they are represented on the other
97// chain as tokens
4ecaf167 98bool ValidateCrossChainExport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
78819397
MT
99{
100 return true;
101}
d727514f 102
78819397 103bool IsCrossChainExportInput(const CScript &scriptSig)
b2a98c42 104{
9ec65f90 105 return true;
b2a98c42
MT
106}
107
108// used to validate import of coins from one chain to another. if they are not native and are supported,
109// they are represented o the chain as tokens
4ecaf167 110bool ValidateCrossChainImport(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
78819397 111{
d727514f 112 return true;
78819397
MT
113}
114bool IsCrossChainImportInput(const CScript &scriptSig)
b2a98c42 115{
9ec65f90 116 return true;
b2a98c42
MT
117}
118
119// used to validate a specific service reward based on the spending transaction
4ecaf167 120bool ValidateServiceReward(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
b2a98c42 121{
13ed2980
MT
122 // for each type of service reward, we need to check and see if the spender is
123 // correctly formatted to be a valid spend of the service reward. for notarization
124 // we ensure that the notarization and its outputs are valid and that the spend
125 // applies to the correct billing period
126 return true;
b2a98c42 127}
78819397
MT
128bool IsServiceRewardInput(const CScript &scriptSig)
129{
9ec65f90 130 return true;
78819397 131}
b2a98c42
MT
132
133// used as a proxy token output for a reserve currency on its fractional reserve chain
4ecaf167 134bool ValidateReserveOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
b2a98c42 135{
78819397
MT
136 return true;
137}
138bool IsReserveOutputInput(const CScript &scriptSig)
139{
9ec65f90 140 return true;
b2a98c42
MT
141}
142
4ecaf167 143bool ValidateReserveTransfer(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
78819397
MT
144{
145 return true;
146}
147bool IsReserveTransferInput(const CScript &scriptSig)
b2a98c42 148{
9ec65f90 149 return true;
78819397 150}
b2a98c42 151
4ecaf167 152bool ValidateReserveDeposit(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
78819397
MT
153{
154 return true;
155}
156bool IsReserveDepositInput(const CScript &scriptSig)
157{
9ec65f90 158 return true;
b2a98c42
MT
159}
160
4ecaf167 161bool ValidateCurrencyState(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
b2a98c42 162{
78819397
MT
163 return true;
164}
165bool IsCurrencyStateInput(const CScript &scriptSig)
166{
9ec65f90 167 return true;
78819397 168}
b2a98c42 169
78819397 170// used to convert a fractional reserve currency into its reserve and back
4ecaf167 171bool ValidateReserveExchange(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
78819397
MT
172{
173 return true;
b2a98c42 174}
d305d656 175bool IsReserveExchangeInput(const CScript &scriptSig)
176{
9ec65f90 177 return true;
d305d656 178}
179
b2a98c42
MT
180
181/*
182 * Verifies that the input objects match the hashes and returns the transaction.
183 *
184 * If the opRetTx has the op ret, this calculates based on the actual transaction and
185 * validates the hashes. If the opRetTx does not have the opRet itself, this validates
186 * by ensuring that all objects are present on this chain, composing the opRet, and
187 * ensuring that the transaction then hashes to the correct txid.
188 *
189 */
190bool ValidateOpretProof(CScript &opRet, COpRetProof &orProof)
191{
192 // enumerate through the objects and validate that they are objects of the expected type that hash
193 // to the value expected. return true if so
9ec65f90 194 return true;
b2a98c42
MT
195}
196
56fe75cb 197int8_t ObjTypeCode(const CBlockHeaderProof &obj)
b2a98c42
MT
198{
199 return CHAINOBJ_HEADER;
200}
201
56fe75cb 202int8_t ObjTypeCode(const uint256 &obj)
b2a98c42 203{
56fe75cb 204 return CHAINOBJ_PROOF_ROOT;
b2a98c42
MT
205}
206
56fe75cb 207int8_t ObjTypeCode(const CPartialTransactionProof &obj)
b2a98c42 208{
56fe75cb 209 return CHAINOBJ_TRANSACTION_PROOF;
b2a98c42
MT
210}
211
56fe75cb 212int8_t ObjTypeCode(const CBlockHeaderAndProof &obj)
b2a98c42
MT
213{
214 return CHAINOBJ_HEADER_REF;
215}
216
1fa4454d 217int8_t ObjTypeCode(const CPriorBlocksCommitment &obj)
b2a98c42 218{
1fa4454d 219 return CHAINOBJ_PRIORBLOCKS;
b2a98c42
MT
220}
221
989b1de1
MT
222int8_t ObjTypeCode(const CReserveTransfer &obj)
223{
224 return CHAINOBJ_RESERVETRANSFER;
225}
226
34d1aa13
MT
227int8_t ObjTypeCode(const CCrossChainProof &obj)
228{
229 return CHAINOBJ_CROSSCHAINPROOF;
230}
231
56fe75cb 232int8_t ObjTypeCode(const CCompositeChainObject &obj)
233{
234 return CHAINOBJ_COMPOSITEOBJECT;
235}
236
b2a98c42 237// this adds an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction
56fe75cb 238CScript StoreOpRetArray(const std::vector<CBaseChainObject *> &objPtrs)
b2a98c42
MT
239{
240 CScript vData;
241 CDataStream s = CDataStream(SER_NETWORK, PROTOCOL_VERSION);
0b806be9 242 s << (int32_t)OPRETTYPE_OBJECTARR;
b2a98c42
MT
243 bool error = false;
244
245 for (auto pobj : objPtrs)
246 {
d48d49b6
MT
247 try
248 {
249 if (!DehydrateChainObject(s, pobj))
250 {
251 error = true;
252 break;
253 }
254 }
255 catch(const std::exception& e)
b2a98c42 256 {
d48d49b6 257 std::cerr << e.what() << '\n';
b2a98c42
MT
258 error = true;
259 break;
260 }
261 }
262
05578402
MT
263 //std::vector<unsigned char> schars(s.begin(), s.begin() + 200);
264 //printf("stream vector chars: %s\n", HexBytes(&schars[0], schars.size()).c_str());
6278cf9c 265
b2a98c42 266 std::vector<unsigned char> vch(s.begin(), s.end());
d48d49b6 267 return error ? CScript() : CScript() << OP_RETURN << vch;
b2a98c42
MT
268}
269
a8c2cb11
MT
270void DeleteOpRetObjects(std::vector<CBaseChainObject *> &ora)
271{
272 for (auto pobj : ora)
273 {
274 switch(pobj->objectType)
275 {
276 case CHAINOBJ_HEADER:
277 {
56fe75cb 278 delete (CChainObject<CBlockHeaderAndProof> *)pobj;
a8c2cb11
MT
279 break;
280 }
281
56fe75cb 282 case CHAINOBJ_TRANSACTION_PROOF:
a8c2cb11 283 {
56fe75cb 284 delete (CChainObject<CPartialTransactionProof> *)pobj;
a8c2cb11
MT
285 break;
286 }
287
56fe75cb 288 case CHAINOBJ_PROOF_ROOT:
a8c2cb11 289 {
56fe75cb 290 delete (CChainObject<uint256> *)pobj;
a8c2cb11
MT
291 break;
292 }
293
294 case CHAINOBJ_HEADER_REF:
295 {
56fe75cb 296 delete (CChainObject<CBlockHeaderProof> *)pobj;
a8c2cb11
MT
297 break;
298 }
299
300 case CHAINOBJ_PRIORBLOCKS:
301 {
302 delete (CChainObject<CPriorBlocksCommitment> *)pobj;
303 break;
304 }
305
989b1de1
MT
306 case CHAINOBJ_RESERVETRANSFER:
307 {
308 delete (CChainObject<CReserveTransfer> *)pobj;
309 break;
310 }
311
34d1aa13
MT
312 case CHAINOBJ_CROSSCHAINPROOF:
313 {
314 delete (CChainObject<CCrossChainProof> *)pobj;
315 break;
316 }
317
56fe75cb 318 case CHAINOBJ_COMPOSITEOBJECT:
319 {
320 delete (CChainObject<CCompositeChainObject> *)pobj;
321 break;
322 }
323
a8c2cb11 324 default:
c8c677c9 325 {
84f390e5 326 printf("ERROR: invalid object type (%u), likely corrupt pointer %p\n", pobj->objectType, pobj);
c8c677c9 327 printf("generate code that won't be optimized away %s\n", CCurrencyValueMap(std::vector<uint160>({ASSETCHAINS_CHAINID}), std::vector<CAmount>({200000000})).ToUniValue().write(1,2).c_str());
328 printf("This is here to generate enough code for a good break point system chain name: %s\n", ConnectedChains.ThisChain().name.c_str());
329
a8c2cb11 330 delete pobj;
c8c677c9 331 }
a8c2cb11
MT
332 }
333 }
5d13ad1e 334 ora.clear();
a8c2cb11
MT
335}
336
b2a98c42
MT
337std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
338{
339 std::vector<unsigned char> vch;
340 std::vector<CBaseChainObject *> vRet;
341 if (opRetScript.IsOpReturn() && GetOpReturnData(opRetScript, vch) && vch.size() > 0)
342 {
343 CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION);
344
1fa4454d
MT
345 int32_t opRetType;
346
347 try
348 {
349 s >> opRetType;
350 if (opRetType == OPRETTYPE_OBJECTARR)
351 {
352 CBaseChainObject *pobj;
353 while (!s.empty() && (pobj = RehydrateChainObject(s)))
354 {
355 vRet.push_back(pobj);
356 }
aeca4678
MT
357 if (!s.empty())
358 {
2d8cedc7 359 printf("failed to load all objects in opret");
ba256d3a 360 DeleteOpRetObjects(vRet);
2d8cedc7 361 vRet.clear();
aeca4678 362 }
1fa4454d
MT
363 }
364 }
365 catch(const std::exception& e)
b2a98c42 366 {
1fa4454d 367 std::cerr << e.what() << '\n';
ba256d3a 368 DeleteOpRetObjects(vRet);
1fa4454d 369 vRet.clear();
b2a98c42
MT
370 }
371 }
372 return vRet;
373}
374
2d8b9129 375CServiceReward::CServiceReward(const CTransaction &tx)
b2a98c42 376{
b2a98c42
MT
377 nVersion = PBAAS_VERSION_INVALID;
378 for (auto out : tx.vout)
379 {
13ed2980
MT
380 COptCCParams p;
381 if (IsPayToCryptoCondition(out.scriptPubKey, p))
b2a98c42 382 {
56fe75cb 383 // always take the first for now
384 if (p.evalCode == EVAL_SERVICEREWARD)
b2a98c42 385 {
56fe75cb 386 FromVector(p.vData[0], *this);
387 break;
b2a98c42
MT
388 }
389 }
390 }
b2a98c42
MT
391}
392
56fe75cb 393CCrossChainExport::CCrossChainExport(const CTransaction &tx, int32_t *pCCXOutputNum)
13ed2980 394{
56fe75cb 395 int32_t _ccxOutputNum = 0;
396 int32_t &ccxOutputNum = pCCXOutputNum ? *pCCXOutputNum : _ccxOutputNum;
397
398 for (int i = 0; i < tx.vout.size(); i++)
13ed2980
MT
399 {
400 COptCCParams p;
56fe75cb 401 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) &&
402 p.IsValid() &&
403 p.evalCode == EVAL_CROSSCHAIN_EXPORT)
13ed2980 404 {
56fe75cb 405 FromVector(p.vData[0], *this);
406 ccxOutputNum = i;
407 break;
13ed2980
MT
408 }
409 }
13ed2980
MT
410}
411
cd853aa1 412CCurrencyDefinition::CCurrencyDefinition(const CScript &scriptPubKey)
41f170fd 413{
56fe75cb 414 nVersion = PBAAS_VERSION_INVALID;
cd853aa1 415 COptCCParams p;
416 if (scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
417 {
418 if (p.evalCode == EVAL_CURRENCY_DEFINITION)
419 {
420 FromVector(p.vData[0], *this);
421 }
422 }
423}
424
425std::vector<CCurrencyDefinition> CCurrencyDefinition::GetCurrencyDefinitions(const CTransaction &tx)
426{
427 std::vector<CCurrencyDefinition> retVal;
2f416b17 428 for (auto &out : tx.vout)
41f170fd 429 {
cd853aa1 430 CCurrencyDefinition oneCur = CCurrencyDefinition(out.scriptPubKey);
431 if (oneCur.IsValid())
41f170fd 432 {
cd853aa1 433 retVal.push_back(oneCur);
41f170fd
MT
434 }
435 }
cd853aa1 436 return retVal;
41f170fd
MT
437}
438
f8f61a6d 439#define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
344a051d 440extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
70c48c13 441extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3], ASSETCHAINS_ERAOPTIONS[3];
344a051d 442extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
f8f61a6d 443extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
f2d873d0 444extern std::string VERUS_CHAINNAME;
f7917c6b 445extern uint160 VERUS_CHAINID;
f8f61a6d 446
b2a98c42
MT
447// ensures that the chain definition is valid and that there are no other definitions of the same name
448// that have been confirmed.
4ecaf167 449bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
b2a98c42
MT
450{
451 // the chain definition output can be spent when the chain is at the end of its life and only then
452 // TODO
453 return false;
454}
455
456// ensures that the chain definition is valid and that there are no other definitions of the same name
457// that have been confirmed.
cd853aa1 458bool CheckChainDefinitionOutputs(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn)
b2a98c42
MT
459{
460 // checked before a chain definition output script is accepted as a valid transaction
461
462 // basics - we need a chain definition transaction to kick off a PBaaS chain. it must have:
463 // 1) valid chain definition output with parameters in proper ranges and no duplicate name
464 // 2) notarization output with conformant values
465 // 3) finalization output
466 // 3) notarization funding
467 //
468
469 // get the source transaction
470 uint256 blkHash;
471 CTransaction thisTx;
6c5d1151 472 if (!GetTransaction(tx.vin[nIn].prevout.hash, thisTx, blkHash))
b2a98c42
MT
473 {
474 LogPrintf("failed to retrieve transaction %s\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
475 return false;
476 }
477
cd853aa1 478 std::vector<CCurrencyDefinition> chainDefs = CCurrencyDefinition::GetCurrencyDefinitions(thisTx);
56fe75cb 479 CPBaaSNotarization notarization(thisTx);
09b977c6 480 CTransactionFinalization finalization(thisTx);
cd853aa1 481 bool isVerusActive = IsVerusActive();
b2a98c42 482
cd853aa1 483 if (!notarization.IsValid() || !finalization.IsValid())
b2a98c42 484 {
cd853aa1 485 LogPrintf("transaction specified, %s, must have valid notarization, and finaization outputs\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
b2a98c42
MT
486 return false;
487 }
488
cd853aa1 489 std::set<uint160> allCurrencyIDs;
490 for (auto &curPair : ConnectedChains.ReserveCurrencies())
b2a98c42 491 {
cd853aa1 492 allCurrencyIDs.insert(curPair.first);
493 }
494 allCurrencyIDs.insert(ConnectedChains.ThisChain().GetID());
495 if (!isVerusActive)
496 {
497 allCurrencyIDs.insert(ConnectedChains.notaryChain.GetID());
b2a98c42
MT
498 }
499
cd853aa1 500 bool isCoinbase = thisTx.IsCoinBase();
501 bool isVerified = false;
502 CIdentity activatedID(thisTx);
503
504 // currency definitions can be valid as follows:
505 // 1. original definition in a transaction that simultaneously sets the active currency bit on the identity of the same
506 // name and ID.
507 // 2. outputs of a coinbase transaction in block 1 that defines the parent currency, new currency, and any reserve currencies
508 // 3. currency import from the defining chain of the currency, which has not been implemented as of this comment
509 if (activatedID.IsValid() &&
510 activatedID.HasActiveCurrency() &&
511 chainDefs.size() == 1 &&
512 activatedID.parent == ASSETCHAINS_CHAINID &&
513 activatedID.GetID() == chainDefs[0].GetID())
514 {
515 isVerified = true;
516 }
517 else if (isCoinbase && chainDefs.size() >= 1 && !isVerusActive)
518 {
519 int32_t height1 = 1;
520 CScript expect = CScript() << height1;
521 opcodetype opcode = (opcodetype)*expect.begin();
522
523 if (opcode >= OP_1 && opcode <= OP_16)
524 {
525 isVerified = (thisTx.vin[0].scriptSig.size() >= 1 && CScript::DecodeOP_N(opcode) == height1) ||
526 (thisTx.vin[0].scriptSig.size() >= 2 && thisTx.vin[0].scriptSig[0] == OP_PUSHDATA1 && (int)thisTx.vin[0].scriptSig[1] == height1);
527 }
528 else
529 {
530 isVerified = thisTx.vin[0].scriptSig.size() >= expect.size() && std::equal(expect.begin(), expect.end(), thisTx.vin[0].scriptSig.begin());
531 }
532
533 for (auto &chainDef : chainDefs)
534 {
535 uint160 chainID = chainDef.GetID();
536 if (!chainDef.IsValid())
537 {
538 LogPrintf("transaction specified, %s, must not contain invalid chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
539 return false;
540 }
541 if (!allCurrencyIDs.count(chainID))
542 {
543 LogPrintf("transaction specified, %s, must not contain invalid chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
544 return false;
545 }
546 allCurrencyIDs.erase(chainID);
547 }
548 if (allCurrencyIDs.size())
549 {
550 LogPrintf("transaction specified, %s, does not contain all required chain definitions\n", tx.vin[nIn].prevout.hash.GetHex().c_str());
551 return false;
552 }
553 }
554
555 return isVerified;
b2a98c42
MT
556}
557
815a42a6 558CCurrencyValueMap CCrossChainExport::CalculateExportFee(const CCurrencyValueMap &fees, int numIn)
989b1de1 559{
56fe75cb 560 CCurrencyValueMap retVal;
561
815a42a6 562 if (numIn > MAX_EXPORT_INPUTS)
989b1de1 563 {
56fe75cb 564 return retVal;
989b1de1
MT
565 }
566 static const arith_uint256 satoshis(100000000);
567
815a42a6 568 arith_uint256 ratio(50000000 + ((25000000 / MAX_EXPORT_INPUTS) * (numIn - 1)));
56fe75cb 569
815a42a6 570 for (auto &feePair : fees.valueMap)
56fe75cb 571 {
572 retVal.valueMap[feePair.first] = (((arith_uint256(feePair.second) * ratio)) / satoshis).GetLow64();
573 }
048edb97 574 return retVal.CanonicalMap();
56fe75cb 575}
576
e4e5ccf5 577CAmount CCrossChainExport::CalculateExportFeeRaw(CAmount fee, int numIn)
578{
579 if (numIn > MAX_EXPORT_INPUTS)
580 {
581 numIn = MAX_EXPORT_INPUTS;
582 }
583 static const arith_uint256 satoshis(100000000);
584
585 arith_uint256 ratio(50000000 + ((25000000 / MAX_EXPORT_INPUTS) * (numIn - 1)));
586
587 return (((arith_uint256(fee) * ratio)) / satoshis).GetLow64();
588}
589
590CAmount CCrossChainExport::ExportReward(int64_t exportFee)
591{
592 int64_t individualExportFee = ((arith_uint256(exportFee) * 10000000) / SATOSHIDEN).GetLow64();
593 if (individualExportFee < MIN_FEES_BEFORE_FEEPOOL)
594 {
595 individualExportFee = exportFee > MIN_FEES_BEFORE_FEEPOOL ? MIN_FEES_BEFORE_FEEPOOL : exportFee;
596 }
597 return individualExportFee;
598}
599
815a42a6 600CCurrencyValueMap CCrossChainExport::CalculateExportFee() const
601{
602 return CalculateExportFee(totalFees, numInputs);
603}
604
56fe75cb 605CCurrencyValueMap CCrossChainExport::CalculateImportFee() const
606{
607 CCurrencyValueMap retVal;
989b1de1 608
56fe75cb 609 for (auto &feePair : CalculateExportFee().valueMap)
610 {
611 CAmount feeAmount = feePair.second;
612 auto it = totalFees.valueMap.find(feePair.first);
613 retVal.valueMap[feePair.first] = (it != totalFees.valueMap.end() ? it->second : 0) - feeAmount;
614 }
615 return retVal;
989b1de1
MT
616}
617
b2a98c42
MT
618bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
619{
2fd1f0fb 620 bool retval = false;
b2a98c42 621 LOCK(cs_mergemining);
23d61f0a 622
3016973b 623 //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
23d61f0a 624
b2a98c42
MT
625 auto chainIt = mergeMinedChains.find(chainID);
626 if (chainIt != mergeMinedChains.end())
627 {
628 arith_uint256 target;
629 target.SetCompact(chainIt->second.block.nBits);
630 for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
631 {
632 // make sure we don't just match by target
56fe75cb 633 if (removeRange.first->second->GetID() == chainID)
b2a98c42
MT
634 {
635 mergeMinedTargets.erase(removeRange.first);
636 break;
637 }
638 }
639 mergeMinedChains.erase(chainID);
2fd1f0fb 640 dirty = retval = true;
b2a98c42
MT
641
642 // if we get to 0, give the thread a kick to stop waiting for mining
2fd1f0fb 643 //if (!mergeMinedChains.size())
644 //{
645 // sem_submitthread.post();
646 //}
b2a98c42 647 }
2fd1f0fb 648 return retval;
b2a98c42
MT
649}
650
651// remove merge mined chains added and not updated since a specific time
9ec65f90 652void CConnectedChains::PruneOldChains(uint32_t pruneBefore)
b2a98c42
MT
653{
654 vector<uint160> toRemove;
655
656 LOCK(cs_mergemining);
657 for (auto blkData : mergeMinedChains)
658 {
659 if (blkData.second.block.nTime < pruneBefore)
660 {
661 toRemove.push_back(blkData.first);
662 }
663 }
664
665 for (auto id : toRemove)
666 {
3016973b 667 //printf("Pruning chainID: %s\n", id.GetHex().c_str());
b2a98c42
MT
668 RemoveMergedBlock(id);
669 }
670}
671
672// adds or updates merge mined blocks
673// returns false if failed to add
674bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
675{
b2a98c42
MT
676 // determine if we should replace one or add to the merge mine vector
677 {
678 LOCK(cs_mergemining);
679
2fd1f0fb 680 arith_uint256 target;
56fe75cb 681 uint160 cID = blkData.GetID();
b2a98c42
MT
682 auto it = mergeMinedChains.find(cID);
683 if (it != mergeMinedChains.end())
684 {
1fa4454d 685 RemoveMergedBlock(cID); // remove it if already there
b2a98c42 686 }
1fa4454d 687 target.SetCompact(blkData.block.nBits);
23d61f0a 688
3016973b 689 //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
23d61f0a 690
1fa4454d 691 mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
2830db29 692 dirty = true;
b2a98c42 693 }
bce52b27 694 return true;
b2a98c42
MT
695}
696
697bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
698{
699 {
700 LOCK(cs_mergemining);
701 auto chainIt = mergeMinedChains.find(chainID);
702 if (chainIt != mergeMinedChains.end())
703 {
704 rpcChainData = (CRPCChainData)chainIt->second;
705 return true;
706 }
707 return false;
708 }
709}
710
711// this returns a pointer to the data without copy and assumes the lock is held
712CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
713{
714 {
715 auto chainIt = mergeMinedChains.find(chainID);
716 if (chainIt != mergeMinedChains.end())
717 {
718 return &chainIt->second;
719 }
720 return NULL;
721 }
722}
723
9ec65f90 724void CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
e771a884 725{
3016973b 726 //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
2830db29 727 {
728 LOCK(cs_mergemining);
cff3c5ad 729
2830db29 730 qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
731 }
e771a884 732 sem_submitthread.post();
733}
734
0ab273d2 735void CConnectedChains::CheckImports()
736{
737 sem_submitthread.post();
738}
739
f8f61a6d 740// get the latest block header and submit one block at a time, returning after there are no more
741// matching blocks to be found
742vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
b2a98c42
MT
743{
744 std::set<uint160> inHeader;
f8f61a6d 745 bool submissionFound;
746 CPBaaSMergeMinedChainData chainData;
b2a98c42
MT
747 vector<pair<string, UniValue>> results;
748
f8f61a6d 749 CBlockHeader bh;
750 arith_uint256 lastHash;
b2a98c42
MT
751 CPBaaSBlockHeader pbh;
752
02560af7 753 do
b2a98c42 754 {
02560af7 755 submissionFound = false;
b2a98c42 756 {
02560af7 757 LOCK(cs_mergemining);
758 // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
759 // common, merge mined headers for notarization, drop out on any submission
760 for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
b2a98c42 761 {
02560af7 762 // add the PBaaS chain ids from this header to a set for search
763 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
f8f61a6d 764 {
02560af7 765 inHeader.insert(pbh.chainID);
766 }
b2a98c42 767
33d6f38a 768 uint160 chainID;
02560af7 769 // now look through all targets that are equal to or above the hash of this header
770 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
771 {
56fe75cb 772 chainID = chainIt->second->GetID();
02560af7 773 if (inHeader.count(chainID))
2830db29 774 {
02560af7 775 // first, check that the winning header matches the block that is there
776 CPBaaSPreHeader preHeader(chainIt->second->block);
777 preHeader.SetBlockData(headerIt->second);
778
779 // check if the block header matches the block's specific data, only then can we create a submission from this block
780 if (headerIt->second.CheckNonCanonicalData(chainID))
f8f61a6d 781 {
02560af7 782 // save block as is, remove the block from merged headers, replace header, and submit
783 chainData = *chainIt->second;
784
785 *(CBlockHeader *)&chainData.block = headerIt->second;
786
02560af7 787 submissionFound = true;
f8f61a6d 788 }
3016973b
MT
789 //else // not an error condition. code is here for debugging
790 //{
791 // printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
792 //}
cff3c5ad 793 }
3016973b
MT
794 //else // not an error condition. code is here for debugging
795 //{
796 // printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
797 //}
02560af7 798 }
2830db29 799
02560af7 800 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
33d6f38a
MT
801 if (submissionFound)
802 {
803 // once it is going to be submitted, remove block from this chain until a new one is added again
804 RemoveMergedBlock(chainID);
805 break;
806 }
807 else
02560af7 808 {
809 qualifiedHeaders.erase(headerIt);
b2a98c42 810 }
f8f61a6d 811 }
02560af7 812 }
813 if (submissionFound)
814 {
815 // submit one block and loop again. this approach allows multiple threads
816 // to collectively empty the submission queue, mitigating the impact of
817 // any one stalled daemon
818 UniValue submitParams(UniValue::VARR);
819 submitParams.push_back(EncodeHexBlk(chainData.block));
820 UniValue result, error;
821 try
f8f61a6d 822 {
02560af7 823 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
824 result = find_value(result, "result");
825 error = find_value(result, "error");
b2a98c42 826 }
02560af7 827 catch (exception e)
828 {
829 result = UniValue(e.what());
830 }
831 results.push_back(make_pair(chainData.chainDefinition.name, result));
832 if (result.isStr() || !error.isNull())
833 {
834 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());
835 }
836 else
837 {
838 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
839 }
840 }
841 } while (submissionFound);
b2a98c42
MT
842 return results;
843}
844
f8f61a6d 845// add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
b2a98c42
MT
846uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
847{
848 vector<uint160> inHeader;
849 vector<UniValue> toCombine;
850 arith_uint256 blkHash = UintToArith256(bh.GetHash());
f8f61a6d 851 arith_uint256 target(0);
b2a98c42
MT
852
853 CPBaaSBlockHeader pbh;
854
b2a98c42 855 {
f8f61a6d 856 LOCK(cs_mergemining);
b2a98c42 857
f8f61a6d 858 CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
859
860 for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
b2a98c42 861 {
f8f61a6d 862 if (bh.GetPBaaSHeader(pbh, i))
863 {
864 inHeader.push_back(pbh.chainID);
865 }
866 }
867
868 // loop through the existing PBaaS chain ids in the header
2fd1f0fb 869 // remove any that are not either this Chain ID or in our local collection and then add all that are present
f8f61a6d 870 for (uint32_t i = 0; i < inHeader.size(); i++)
871 {
872 auto it = mergeMinedChains.find(inHeader[i]);
873 if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
874 {
875 bh.DeletePBaaSHeader(i);
876 }
b2a98c42 877 }
b2a98c42 878
b2a98c42
MT
879 for (auto chain : mergeMinedChains)
880 {
881 // get the native PBaaS header for each chain and put it into the
882 // header we are given
326f5f88 883 // it must have itself in as a PBaaS header
56fe75cb 884 uint160 cid = chain.second.GetID();
3a2a1777 885 if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
b2a98c42
MT
886 {
887 if (!bh.AddUpdatePBaaSHeader(pbh))
888 {
889 LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
f8f61a6d 890 break;
891 }
892 else
893 {
894 arith_uint256 t;
895 t.SetCompact(chain.second.block.nBits);
896 if (t > target)
897 {
898 target = t;
899 }
b2a98c42
MT
900 }
901 }
326f5f88
MT
902 else
903 {
904 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
905 }
b2a98c42 906 }
2830db29 907 dirty = false;
b2a98c42 908 }
326f5f88 909
f8f61a6d 910 return target.GetCompact();
b2a98c42
MT
911}
912
ccac80a3 913bool CConnectedChains::IsVerusPBaaSAvailable()
914{
20631bf1 915 return notaryChainVersion >= "0.8.0";
ccac80a3 916}
917
f8f61a6d 918extern string PBAAS_HOST, PBAAS_USERPASS;
919extern int32_t PBAAS_PORT;
6bb9fdbc 920bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
9f0c14b2 921{
6bb9fdbc 922 if (chainInfoUni.isObject() && chainDefUni.isObject())
9f0c14b2 923 {
6bb9fdbc 924 UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
f8f61a6d 925 if (uniVer.isStr())
926 {
2156d3d0 927 LOCK(cs_mergemining);
f8f61a6d 928 notaryChainVersion = uni_get_str(uniVer);
6bb9fdbc 929 notaryChainHeight = uni_get_int(find_value(chainInfoUni, "blocks"));
56fe75cb 930 CCurrencyDefinition chainDef(chainDefUni);
f8f61a6d 931 notaryChain = CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS);
932 }
9f0c14b2 933 }
ccac80a3 934 return IsVerusPBaaSAvailable();
9f0c14b2
MT
935}
936
56fe75cb 937uint32_t CConnectedChains::NotaryChainHeight()
938{
939 LOCK(cs_mergemining);
940 return notaryChainHeight;
941}
942
2299bd95 943bool CConnectedChains::CheckVerusPBaaSAvailable()
9f0c14b2
MT
944{
945 if (IsVerusActive())
946 {
ccac80a3 947 notaryChainVersion = "";
9f0c14b2
MT
948 }
949 else
950 {
951 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
2156d3d0 952 // tolerate only 15 second timeout
9a891caf 953 UniValue chainInfo, chainDef;
954 try
955 {
956 UniValue params(UniValue::VARR);
2fd1f0fb 957 chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
9a891caf 958 if (!chainInfo.isNull())
959 {
960 params.push_back(VERUS_CHAINNAME);
c8c677c9 961 chainDef = find_value(RPCCallRoot("getcurrency", params), "result");
9a891caf 962
963 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
964 {
56fe75cb 965 // if we have not past block 1 yet, store the best known update of our current state
966 if ((!chainActive.LastTip() || !chainActive.LastTip()->GetHeight()))
967 {
968 bool success = false;
969 params.clear();
970 params.push_back(EncodeDestination(CIdentityID(thisChain.GetID())));
c8c677c9 971 chainDef = find_value(RPCCallRoot("getcurrency", params), "result");
56fe75cb 972 if (!chainDef.isNull())
973 {
974 CCurrencyDefinition currencyDef(chainDef);
975 if (currencyDef.IsValid())
976 {
977 thisChain = currencyDef;
978 if (NotaryChainHeight() >= thisChain.startBlock)
979 {
980 readyToStart = true; // this only gates mining of block one, to be sure we have the latest definition
981 }
982 success = true;
983 }
984 }
985 return success;
986 }
9a891caf 987 return true;
988 }
989 }
990 } catch (exception e)
4fa3b13d 991 {
56fe75cb 992 LogPrintf("%s: Error communicating with %s chain\n", __func__, VERUS_CHAINNAME);
4fa3b13d 993 }
9f0c14b2 994 }
9a891caf 995 notaryChainVersion = "";
4fa3b13d 996 return false;
9f0c14b2
MT
997}
998
56fe75cb 999int CConnectedChains::GetThisChainPort() const
1000{
1001 int port;
1002 string host;
1003 for (auto node : defaultPeerNodes)
1004 {
1005 SplitHostPort(node.networkAddress, port, host);
1006 if (port)
1007 {
1008 return port;
1009 }
1010 }
1011 return 0;
1012}
1013
19ce962e 1014CCoinbaseCurrencyState CConnectedChains::AddPrelaunchConversions(CCurrencyDefinition &curDef,
1015 const CCoinbaseCurrencyState &_currencyState,
1016 int32_t fromHeight,
1017 int32_t height,
1018 int32_t curDefHeight)
1019{
1020 CCoinbaseCurrencyState currencyState = _currencyState;
1021 bool firstUpdate = fromHeight <= curDefHeight;
1022 if (firstUpdate)
1023 {
1024 if (curDef.IsFractional())
1025 {
1026 currencyState.supply = curDef.initialFractionalSupply;
1027 currencyState.reserves = std::vector<int64_t>(currencyState.reserves.size(), 0);
1028 currencyState.weights = curDef.weights;
1029 }
1030 else
1031 {
1032 // supply is determined by purchases * current conversion rate
1033 currencyState.supply = currencyState.initialSupply;
1034 }
1035 }
1036
1037 // get chain transfers that should apply before the start block
1038 // until there is a post-start block notarization, we always consider the
1039 // currency state to be up to just before the start block
1040 std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> unspentTransfers;
1041 std::map<uint160, int32_t> currencyIndexes = currencyState.GetReserveMap();
481e7fd6 1042 int32_t nativeIdx = -1;
19ce962e 1043 if (GetChainTransfers(unspentTransfers, curDef.GetID(), fromHeight, height < curDef.startBlock ? height : curDef.startBlock - 1))
1044 {
1045 currencyState.ClearForNextBlock();
1046
1047 for (auto &transfer : unspentTransfers)
1048 {
481e7fd6 1049 if (transfer.second.second.IsPreConversion())
19ce962e 1050 {
1051 CAmount conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
481e7fd6 1052 CAmount valueIn = transfer.second.second.nValue - conversionFee;
1053 int32_t curIdx = currencyIndexes[transfer.second.second.currencyID];
19ce962e 1054
481e7fd6 1055 currencyState.reserveIn[curIdx] += valueIn;
1056 curDef.preconverted[curIdx] += valueIn;
19ce962e 1057 if (curDef.IsFractional())
1058 {
481e7fd6 1059 currencyState.reserves[curIdx] += valueIn;
19ce962e 1060 }
1061 else
1062 {
481e7fd6 1063 currencyState.supply += CCurrencyState::ReserveToNativeRaw(valueIn, currencyState.PriceInReserve(curIdx));
19ce962e 1064 }
1065
1066 if (transfer.second.second.currencyID == curDef.systemID)
1067 {
1068 currencyState.nativeConversionFees += conversionFee;
cd55a0f4 1069 currencyState.nativeFees += conversionFee;
481e7fd6 1070 nativeIdx = curIdx;
19ce962e 1071 }
1072
74df3b85 1073 currencyState.fees[curIdx] += conversionFee;
af959d1d 1074 currencyState.nativeFees += transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
481e7fd6 1075 currencyState.conversionFees[curIdx] += conversionFee;
19ce962e 1076 }
1077 }
1078 }
1079
481e7fd6 1080 if (curDef.IsFractional())
19ce962e 1081 {
818494a0 1082 if (curDef.name == "V")
1083 {
1084 printf("%s: prelaunch of V\n", __func__);
1085 }
1086
481e7fd6 1087 // convert all non-native fees to native and update the price as a result
1088 bool isFeeConversion = false;
1089 std::vector<int64_t> reservesToConvert;
1090 std::vector<int64_t> fractionalToConvert;
1091 std::vector<std::vector<int64_t>> crossConversions = std::vector<std::vector<int64_t>>(curDef.currencies.size(), std::vector<int64_t>(curDef.currencies.size()));
1092
1093 for (int i = 0; i < curDef.currencies.size(); i++)
1094 {
1095 currencyState.conversionPrice[i] = currencyState.PriceInReserve(i, true);
1096
1097 // all currencies except the native currency of the system will be converted to the native currency
5a775c1b 1098 if (currencyState.fees[i] && curDef.currencies[i] != curDef.systemID)
481e7fd6 1099 {
5a775c1b 1100 reservesToConvert.push_back(currencyState.fees[i] + currencyState.reserves[i]);
1101 fractionalToConvert.push_back(currencyState.ReserveToNative(currencyState.reserves[i], i));
1102 if (nativeIdx != -1)
481e7fd6 1103 {
5a775c1b 1104 crossConversions[i][nativeIdx] = currencyState.fees[i];
1105 isFeeConversion = true;
481e7fd6 1106 }
1107 }
5a775c1b 1108 else
1109 {
1110 reservesToConvert.push_back(0);
1111 fractionalToConvert.push_back(0);
1112 }
481e7fd6 1113 }
1114
1115 // convert all non-native fee currencies to native and adjust prices
1116 if (isFeeConversion)
1117 {
1118 CCurrencyState converterState = static_cast<CCurrencyState>(currencyState);
3d95d48f 1119 curDef.conversions = currencyState.conversionPrice = converterState.ConvertAmounts(reservesToConvert, fractionalToConvert, currencyState, &crossConversions);
481e7fd6 1120 }
1121 else
1122 {
1123 curDef.conversions = currencyState.PricesInReserve();
1124 }
19ce962e 1125 }
1126
1127 // set initial supply from actual conversions if this is first update
1128 if (firstUpdate && curDef.IsFractional())
1129 {
77da5a22 1130 CAmount calculatedSupply = 0;
19ce962e 1131 for (auto &transfer : unspentTransfers)
1132 {
481e7fd6 1133 if (transfer.second.second.IsPreConversion())
19ce962e 1134 {
1135 CAmount toConvert = transfer.second.second.nValue - CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
77da5a22 1136 calculatedSupply += CCurrencyState::ReserveToNativeRaw(toConvert, currencyState.conversionPrice[currencyIndexes[transfer.second.second.currencyID]]);
19ce962e 1137 }
1138 }
77da5a22 1139
1140 if (calculatedSupply > curDef.initialFractionalSupply)
1141 {
1142 // get a ratio and reduce all prices by that ratio
1143 static arith_uint256 bigSatoshi(SATOSHIDEN);
818494a0 1144 arith_uint256 newRatio((calculatedSupply * bigSatoshi) / curDef.initialFractionalSupply);
77da5a22 1145 for (auto &rate : currencyState.conversionPrice)
1146 {
1147 arith_uint256 numerator = rate * newRatio;
1148 rate = (numerator / bigSatoshi).GetLow64();
1149 // truncate up, not down, to prevent any overflow at all
818494a0 1150 if ((numerator - (bigSatoshi * rate)) > 0)
77da5a22 1151 {
1152 rate++;
1153 }
1154 }
d3c97fd3 1155 }
19ce962e 1156
818494a0 1157 calculatedSupply = 0;
1158 for (auto &transfer : unspentTransfers)
1159 {
1160 if (transfer.second.second.IsPreConversion())
1161 {
1162 CAmount toConvert = transfer.second.second.nValue - CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1163 calculatedSupply += CCurrencyState::ReserveToNativeRaw(toConvert, currencyState.conversionPrice[currencyIndexes[transfer.second.second.currencyID]]);
1164 }
1165 }
1166 printf("Calculated supply %s\n", ValueFromAmount(calculatedSupply).write().c_str());
1167
19ce962e 1168 // now, remove carveout percentage from each weight & reserve
1169 // for currency state
1170 int32_t preLaunchCarveOutTotal = 0;
1171 for (auto &carveout : curDef.preLaunchCarveOuts)
1172 {
1173 preLaunchCarveOutTotal += carveout.second;
1174
1175 }
1176 static arith_uint256 bigSatoshi(SATOSHIDEN);
1177 for (auto &oneReserve : currencyState.reserves)
1178 {
1179 oneReserve = ((arith_uint256(oneReserve) * arith_uint256(SATOSHIDEN - preLaunchCarveOutTotal)) / bigSatoshi).GetLow64();
1180 }
1181 for (auto &oneWeight : currencyState.weights)
1182 {
1183 oneWeight = ((arith_uint256(oneWeight) * arith_uint256(CCurrencyDefinition::CalculateRatioOfValue((SATOSHIDEN - preLaunchCarveOutTotal), SATOSHIDEN - curDef.preLaunchDiscount))) / bigSatoshi).GetLow64();
1184 }
19ce962e 1185 }
1186
d9012609 1187 currencyState.UpdateWithEmission(curDef.GetTotalPreallocation());
1188
19ce962e 1189 return currencyState;
1190}
1191
ea574fe2 1192CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(CCurrencyDefinition &curDef, int32_t height, int32_t curDefHeight)
1fb6db72 1193{
1194 uint160 chainID = curDef.GetID();
1195 CCoinbaseCurrencyState currencyState;
ea574fe2 1196 std::vector<CAddressIndexDbEntry> notarizationIndex;
1fb6db72 1197
1198 if (chainID == ASSETCHAINS_CHAINID)
1199 {
ea574fe2 1200 CBlock block;
1201 if (IsVerusActive() ||
1202 CConstVerusSolutionVector::activationHeight.ActiveVersion(height) < CActivationHeight::ACTIVATE_PBAAS ||
1203 height == 0 ||
1204 height > chainActive.Height() ||
1205 !chainActive[height] ||
1206 !ReadBlockFromDisk(block, chainActive[height], Params().GetConsensus()) ||
1207 !(currencyState = CCoinbaseCurrencyState(block.vtx[0])).IsValid())
1208 {
1209 currencyState = GetInitialCurrencyState(thisChain);
1210 }
1fb6db72 1211 }
1212 // if this is a token on this chain, it will be simply notarized
1213 else if (curDef.systemID == ASSETCHAINS_CHAINID)
1214 {
1215 // get the last unspent notarization for this currency, which is valid by definition for a token
1216 CPBaaSNotarization notarization;
68ddda5e 1217 if ((notarization.GetLastNotarization(chainID, EVAL_ACCEPTEDNOTARIZATION, curDefHeight, height) &&
1218 (currencyState = notarization.currencyState).IsValid()) ||
1219 (currencyState = GetInitialCurrencyState(curDef)).IsValid())
1fb6db72 1220 {
68ddda5e 1221 if (!(notarization.IsValid() && notarization.notarizationHeight >= curDef.startBlock))
1fb6db72 1222 {
68ddda5e 1223 // pre-launch
1224 currencyState.SetPrelaunch(true);
19ce962e 1225 currencyState = AddPrelaunchConversions(curDef,
1226 currencyState,
1227 notarization.IsValid() ? notarization.notarizationHeight : curDefHeight,
1228 height,
1229 curDefHeight);
1230 }
1231 else
1232 {
1233 currencyState.SetPrelaunch(false);
1fb6db72 1234 }
1235 }
1236 }
1237 else
1238 {
1239 CChainNotarizationData cnd;
1240 uint32_t ecode = IsVerusActive() ?
1241 EVAL_ACCEPTEDNOTARIZATION :
1242 (chainID == notaryChain.GetID() ? EVAL_EARNEDNOTARIZATION : EVAL_ACCEPTEDNOTARIZATION);
1243 if (GetNotarizationData(chainID, ecode, cnd))
1244 {
ea574fe2 1245 int32_t transfersFrom = curDefHeight;
1246 if (cnd.lastConfirmed != -1)
1247 {
1248 transfersFrom = cnd.vtx[cnd.lastConfirmed].second.notarizationHeight;
1249 }
1250 int32_t transfersUntil = cnd.lastConfirmed == -1 ? curDef.startBlock - 1 :
1251 (cnd.vtx[cnd.lastConfirmed].second.notarizationHeight < curDef.startBlock ?
1252 (height < curDef.startBlock ? height : curDef.startBlock - 1) :
1253 cnd.vtx[cnd.lastConfirmed].second.notarizationHeight);
1254 if (transfersUntil < curDef.startBlock)
1255 {
1256 // get chain transfers that should apply before the start block
1257 // until there is a post-start block notarization, we always consider the
1258 // currency state to be up to just before the start block
1259 std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> unspentTransfers;
1260 if (GetChainTransfers(unspentTransfers, chainID, transfersFrom, transfersUntil))
1261 {
1262 // at this point, all pre-allocation, minted, and pre-converted currency are included
1263 // in the currency state before final notarization
1264 std::map<uint160, int32_t> currencyIndexes = currencyState.GetReserveMap();
1265 if (curDef.IsFractional())
1266 {
1267 currencyState.supply = curDef.initialFractionalSupply;
1268 }
1269 else
1270 {
1271 // supply is determined by purchases * current conversion rate
1272 currencyState.supply = currencyState.initialSupply;
1273 }
1274
1275 for (auto &transfer : unspentTransfers)
1276 {
481e7fd6 1277 if (transfer.second.second.IsPreConversion())
ea574fe2 1278 {
1279 CAmount conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1280
1281 currencyState.reserveIn[currencyIndexes[transfer.second.second.currencyID]] += transfer.second.second.nValue;
1282 curDef.preconverted[currencyIndexes[transfer.second.second.currencyID]] += transfer.second.second.nValue;
1283 if (curDef.IsFractional())
1284 {
1285 currencyState.reserves[currencyIndexes[transfer.second.second.currencyID]] += transfer.second.second.nValue - conversionFee;
1286 }
1287 else
1288 {
1289 currencyState.supply += CCurrencyState::ReserveToNativeRaw(transfer.second.second.nValue - conversionFee, currencyState.PriceInReserve(currencyIndexes[transfer.second.second.currencyID]));
1290 }
1291
1292 if (transfer.second.second.currencyID == curDef.systemID)
1293 {
1294 currencyState.nativeConversionFees += conversionFee;
1295 currencyState.nativeFees += conversionFee + transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
1296 }
1297 else
1298 {
1299 currencyState.fees[currencyIndexes[transfer.second.second.currencyID]] +=
1300 conversionFee + transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
1301 currencyState.conversionFees[currencyIndexes[transfer.second.second.currencyID]] += conversionFee;
1302 }
1303 }
1304 else if (transfer.second.second.flags & CReserveTransfer::PREALLOCATE)
1305 {
1306 currencyState.emitted += transfer.second.second.nValue;
1307 }
1308 }
1309 currencyState.supply += currencyState.emitted;
1310 if (curDef.conversions.size() != curDef.currencies.size())
1311 {
1312 curDef.conversions = std::vector<int64_t>(curDef.currencies.size());
1313 }
1314 for (int i = 0; i < curDef.conversions.size(); i++)
1315 {
1316 currencyState.conversionPrice[i] = curDef.conversions[i] = currencyState.PriceInReserve(i);
1317 }
1318 }
1319 }
1320 else
1321 {
1322 std::pair<uint256, CPBaaSNotarization> notPair = cnd.lastConfirmed != -1 ? cnd.vtx[cnd.lastConfirmed] : cnd.vtx[cnd.forks[cnd.bestChain][0]];
1323 currencyState = notPair.second.currencyState;
1324 }
1fb6db72 1325 }
1326 }
1327 return currencyState;
1328}
1329
ea574fe2 1330CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(const uint160 &currencyID, int32_t height)
1331{
1332 int32_t curDefHeight;
1333 CCurrencyDefinition curDef;
22ce8448 1334 if (GetCurrencyDefinition(currencyID, curDef, &curDefHeight, true))
ea574fe2 1335 {
1336 return GetCurrencyState(curDef, height, curDefHeight);
1337 }
1338 else
1339 {
1340 LogPrintf("%s: currency %s:%s not found\n", __func__, currencyID.GetHex().c_str(), EncodeDestination(CIdentityID(currencyID)).c_str());
1341 printf("%s: currency %s:%s not found\n", __func__, currencyID.GetHex().c_str(), EncodeDestination(CIdentityID(currencyID)).c_str());
1342 }
1343 return CCoinbaseCurrencyState();
1344}
1345
1346CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
1347{
1348 return GetCurrencyState(thisChain.GetID(), height);
1349}
1350
855714b0 1351bool CConnectedChains::SetLatestMiningOutputs(const std::vector<CTxOut> &minerOutputs)
c3250dcd
MT
1352{
1353 LOCK(cs_mergemining);
c3250dcd 1354 latestMiningOutputs = minerOutputs;
bb6c3482 1355 return true;
c3250dcd
MT
1356}
1357
7beb1c0b 1358CCurrencyDefinition CConnectedChains::GetCachedCurrency(const uint160 &currencyID)
09551a1c 1359{
09551a1c 1360 CCurrencyDefinition currencyDef;
22ce8448 1361 int32_t defHeight;
09551a1c 1362 auto it = currencyDefCache.find(currencyID);
1363 if ((it != currencyDefCache.end() && !(currencyDef = it->second).IsValid()) ||
22ce8448 1364 (it == currencyDefCache.end() && !GetCurrencyDefinition(currencyID, currencyDef, &defHeight, true)))
09551a1c 1365 {
1366 printf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(currencyID)).c_str());
1367 LogPrintf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(currencyID)).c_str());
7beb1c0b 1368 return currencyDef;
09551a1c 1369 }
1370 if (it == currencyDefCache.end())
1371 {
1372 currencyDefCache[currencyID] = currencyDef;
1373 }
1374 return currencyDefCache[currencyID];
1375}
1376
1fb6db72 1377CCurrencyDefinition CConnectedChains::UpdateCachedCurrency(const uint160 &currencyID, uint32_t height)
1378{
1379 // due to the main lock being taken on the thread that waits for transaction checks,
1380 // low level functions like this must be called either from a thread that holds LOCK(cs_main),
1381 // or script validation, where it is held either by this thread or one waiting for it.
1382 // in the long run, the daemon synchonrization model should be improved
1383 CCurrencyDefinition currencyDef = GetCachedCurrency(currencyID);
1384 CCoinbaseCurrencyState curState = GetCurrencyState(currencyDef, height);
1385 currencyDefCache[currencyID] = currencyDef;
1386 return currencyDef;
1387}
1388
c3250dcd
MT
1389void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
1390{
1391 // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
1392 {
1393 if (!nHeight)
1394 {
1395 return;
1396 }
1397
56fe75cb 1398 std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> transferOutputs;
c3250dcd 1399
9ace9b55 1400 LOCK(cs_main);
1401
56fe75cb 1402 uint160 thisChainID = ConnectedChains.ThisChain().GetID();
1403
c3250dcd
MT
1404 // get all available transfer outputs to aggregate into export transactions
1405 if (GetUnspentChainTransfers(transferOutputs))
1406 {
bb6c3482 1407 if (!transferOutputs.size())
1408 {
1409 return;
1410 }
815a42a6 1411
1412 std::vector<pair<CInputDescriptor, CReserveTransfer>> txInputs;
4f8f2b54 1413 uint160 lastChain = transferOutputs.begin()->first;
815a42a6 1414
efbf1423 1415 CCoins coins;
1416 CCoinsView dummy;
1417 CCoinsViewCache view(&dummy);
1418
1419 LOCK(mempool.cs);
1420
1421 CCoinsViewMemPool viewMemPool(pcoinsTip, mempool);
1422 view.SetBackend(viewMemPool);
1423
4f8f2b54 1424 auto outputIt = transferOutputs.begin();
1425 for (int outputsDone = 0; outputsDone <= transferOutputs.size(); outputIt++, outputsDone++)
56fe75cb 1426 {
4f8f2b54 1427 if (outputIt != transferOutputs.end())
56fe75cb 1428 {
4f8f2b54 1429 auto &output = *outputIt;
1430 if (output.first == lastChain)
ea574fe2 1431 {
4f8f2b54 1432 txInputs.push_back(output.second);
ea574fe2 1433 continue;
1434 }
4f8f2b54 1435 }
ea574fe2 1436
4f8f2b54 1437 CCurrencyDefinition destDef, systemDef;
56fe75cb 1438
4f8f2b54 1439 destDef = GetCachedCurrency(lastChain);
1440 systemDef = GetCachedCurrency(destDef.systemID);
ea574fe2 1441
4f8f2b54 1442 if (!destDef.IsValid())
1443 {
1444 printf("%s: cannot find destination currency %s\n", __func__, EncodeDestination(CIdentityID(lastChain)).c_str());
1445 LogPrintf("%s: cannot find destination currency %s\n", __func__, EncodeDestination(CIdentityID(lastChain)).c_str());
1446 continue;
1447 }
1448 if (!systemDef.IsValid())
1449 {
1450 printf("%s: cannot find destination system definition %s\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
1451 LogPrintf("%s: cannot find destination system definition %s\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
1452 continue;
56fe75cb 1453 }
c3250dcd 1454
4f8f2b54 1455 // if destination is a token on the current chain, consider it its own system
1456 if (destDef.systemID == thisChainID)
c3250dcd 1457 {
4f8f2b54 1458 systemDef = destDef;
78a36746 1459 }
4f8f2b54 1460
1461 // when we get here, we have a consecutive number of transfer outputs to consume in txInputs
1462 // we need an unspent export output to export, or use the last one of it is an export to the same
1463 // system
1464 std::multimap<uint160, pair<int, CInputDescriptor>> exportOutputs;
1465 CCurrencyDefinition lastChainDef = UpdateCachedCurrency(lastChain, nHeight);
1466
1467 if (GetUnspentChainExports(lastChain, exportOutputs) && exportOutputs.size())
78a36746 1468 {
4f8f2b54 1469 std::pair<uint160, std::pair<int, CInputDescriptor>> lastExport = *exportOutputs.begin();
815a42a6 1470
4f8f2b54 1471 bool oneFullSize = txInputs.size() >= CCrossChainExport::MIN_INPUTS;
8e522b06 1472
4f8f2b54 1473 if (((nHeight - lastExport.second.first) >= CCrossChainExport::MIN_BLOCKS) || oneFullSize)
1474 {
1475 boost::optional<CTransaction> oneExport;
c3250dcd 1476
4f8f2b54 1477 // make one or more transactions that spends the last export and all possible cross chain transfers
1478 while (txInputs.size())
c3250dcd 1479 {
4f8f2b54 1480 TransactionBuilder tb(Params().GetConsensus(), nHeight);
c3250dcd 1481
4f8f2b54 1482 int inputsLeft = txInputs.size();
1483 int numInputs = inputsLeft > CCrossChainExport::MAX_EXPORT_INPUTS ? CCrossChainExport::MAX_EXPORT_INPUTS : inputsLeft;
1484
1485 if (numInputs > CCrossChainExport::MAX_EXPORT_INPUTS)
c3250dcd 1486 {
4f8f2b54 1487 numInputs = CCrossChainExport::MAX_EXPORT_INPUTS;
1488 }
1489 inputsLeft = txInputs.size() - numInputs;
c3250dcd 1490
4f8f2b54 1491 // if we have already made one and don't have enough to make another
1492 // without going under the input minimum, wait until next time for the others
1493 if (numInputs > 0 && numInputs < CCrossChainExport::MIN_INPUTS && oneFullSize)
1494 {
1495 break;
1496 }
c3250dcd 1497
4f8f2b54 1498 // each time through, we make one export transaction with the remainder or a subset of the
1499 // reserve transfer inputs. inputs can be:
1500 // 1. transfers of reserve for fractional reserve chains
1501 // 2. pre-conversions for pre-launch participation in the premine
1502 // 3. reserve market conversions to send between Verus and a fractional reserve chain and always output the native coin
1503 //
1504 // If we are on the Verus chain, all inputs will include native coins. On a PBaaS chain, inputs can either be native
1505 // or reserve token inputs.
1506 //
1507 // On the Verus chain, total native amount, minus the fee, must be sent to the reserve address of the specific chain
1508 // as reserve deposit with native coin equivalent. Pre-conversions and conversions will be realized on the PBaaS chain
1509 // as part of the import process
1510 //
1511 // If we are on the PBaaS chain, conversions must happen before coins are sent this way back to the reserve chain.
1512 // Verus reserve outputs can be directly aggregated and transferred, with fees paid through conversion and the
1513 // remaining Verus reserve coin will be burned on the PBaaS chain as spending it is allowed, once notarized, on the
1514 // Verus chain.
1515 //
1516 CCurrencyValueMap totalTxFees;
1517 CCurrencyValueMap totalAmounts;
1518 CAmount exportOutVal = 0;
1519 std::vector<CBaseChainObject *> chainObjects;
1520
1521 // first, we must add the export output from the current export thread to this chain
1522 if (oneExport.is_initialized())
1523 {
1524 // spend the last export transaction output
1525 CTransaction &tx = oneExport.get();
1526 COptCCParams p;
1527 int j;
1528 for (j = 0; j < tx.vout.size(); j++)
56fe75cb 1529 {
4f8f2b54 1530 if (::IsPayToCryptoCondition(tx.vout[j].scriptPubKey, p) &&
1531 p.IsValid() &&
1532 p.evalCode == EVAL_CROSSCHAIN_EXPORT)
1533 {
1534 break;
1535 }
56fe75cb 1536 }
815a42a6 1537
4f8f2b54 1538 // had to be found and valid if we made the tx
1539 assert(j < tx.vout.size() && p.IsValid());
1540
1541 tb.AddTransparentInput(COutPoint(tx.GetHash(), j), tx.vout[j].scriptPubKey, tx.vout[j].nValue);
1542 exportOutVal = tx.vout[j].nValue;
1543 lastExport.second.first = nHeight;
1544 lastExport.second.second = CInputDescriptor(tx.vout[j].scriptPubKey, tx.vout[j].nValue, CTxIn(tx.GetHash(), j));
1545 }
1546 else
1547 {
1548 // spend the recentExportIt output
1549 tb.AddTransparentInput(lastExport.second.second.txIn.prevout, lastExport.second.second.scriptPubKey, lastExport.second.second.nValue);
1550 exportOutVal = lastExport.second.second.nValue;
1551 uint256 blkHash;
1552 CTransaction lastExportTx;
1553 // we must find the export, or it doesn't exist
1554 if (!myGetTransaction(lastExport.second.second.txIn.prevout.hash, lastExportTx, blkHash))
c3250dcd 1555 {
815a42a6 1556 break;
c3250dcd 1557 }
4f8f2b54 1558 oneExport = lastExportTx;
1559 }
c3250dcd 1560
4f8f2b54 1561 // combine any reserve deposits from the last export
1562 // into the reserve deposit outputs for this export and spend them
1563 // TODO: allow reserve deposits to hold multiple reserve token types
1564 // to make this more efficient for multi-reserves
1565 CCurrencyValueMap currentReserveDeposits;
1566 if (!view.GetCoins(lastExport.second.second.txIn.prevout.hash, coins))
1567 {
1568 break;
1569 }
1570 CTransaction &lastExportTx = oneExport.get();
c3250dcd 1571
4f8f2b54 1572 for (int i = 0; i < coins.vout.size(); i++)
1573 {
1574 // import on same chain spends reserve deposits as well, so only spend them if they are not
1575 // already spent
1576 auto &oneOut = lastExportTx.vout[i];
1577 COptCCParams p;
c3250dcd 1578
4f8f2b54 1579 if (::IsPayToCryptoCondition(oneOut.scriptPubKey, p) &&
1580 p.IsValid() &&
1581 p.evalCode == EVAL_RESERVE_DEPOSIT &&
1582 coins.IsAvailable(i))
c3250dcd 1583 {
4f8f2b54 1584 // only reserve deposits for the export currency/system will be present on an export transaction
1585 CCurrencyValueMap reserveOut = oneOut.scriptPubKey.ReserveOutValue().CanonicalMap();
1586 currentReserveDeposits += reserveOut;
1587 if (oneOut.nValue || reserveOut.valueMap.size())
8e522b06 1588 {
4f8f2b54 1589 tb.AddTransparentInput(COutPoint(lastExport.second.second.txIn.prevout.hash, i),
1590 oneOut.scriptPubKey, oneOut.nValue);
1591 currentReserveDeposits.valueMap[ASSETCHAINS_CHAINID] += oneOut.nValue;
8e522b06 1592 }
8e522b06 1593 }
4f8f2b54 1594 }
8e522b06 1595
4f8f2b54 1596 COptCCParams p;
1597 std::vector<int> toRemove;
efbf1423 1598
4f8f2b54 1599 for (int j = 0; j < numInputs; j++)
1600 {
1601 tb.AddTransparentInput(txInputs[j].first.txIn.prevout, txInputs[j].first.scriptPubKey, txInputs[j].first.nValue, txInputs[j].first.txIn.nSequence);
1602 CCurrencyValueMap newTransferInput = txInputs[j].first.scriptPubKey.ReserveOutValue();
1603 newTransferInput.valueMap[ASSETCHAINS_CHAINID] = txInputs[j].first.nValue;
2d8b9129 1604
4f8f2b54 1605 totalTxFees += txInputs[j].second.CalculateFee(txInputs[j].second.flags, txInputs[j].second.nValue, lastChainDef.systemID);
1606 totalAmounts += newTransferInput;
1607 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(txInputs[j].second), txInputs[j].second));
1608 }
c3250dcd 1609
4f8f2b54 1610 // remove in reverse order so one removal does not affect the position of the next
1611 for (int j = toRemove.size() - 1; j >= 0; j--)
1612 {
1613 txInputs.erase(txInputs.begin() + toRemove[j]);
1614 numInputs--;
1615 }
815a42a6 1616
4f8f2b54 1617 // this logic may cause us to create a tx that will get rejected, but we will never wait too long
1618 if (!numInputs || (oneFullSize && (nHeight - lastExport.second.first) < CCrossChainExport::MIN_BLOCKS && numInputs < CCrossChainExport::MIN_INPUTS))
1619 {
1620 continue;
1621 }
56fe75cb 1622
4f8f2b54 1623 //printf("%s: total export amounts:\n%s\n", __func__, totalAmounts.ToUniValue().write().c_str());
1624 CCrossChainExport ccx(lastChain, numInputs, totalAmounts.CanonicalMap(), totalTxFees.CanonicalMap());
1625
1626 // make a fee output
1627 CReserveTransfer feeOut(CReserveTransfer::VALID + CReserveTransfer::FEE_OUTPUT,
1628 lastChainDef.systemID, 0, 0, lastChainDef.systemID, DestinationToTransferDestination(feeOutput));
1629 chainObjects.push_back(new CChainObject<CReserveTransfer>(ObjTypeCode(feeOut), feeOut));
1630
1631 // do a preliminary check
1632 CReserveTransactionDescriptor rtxd;
1633 std::vector<CTxOut> vOutputs;
1634 CChainNotarizationData cnd;
1635 CCoinbaseCurrencyState currencyState;
1636 if (!GetNotarizationData(lastChain,
1637 !lastChainDef.IsToken() && lastChainDef.systemID == lastChain ? EVAL_EARNEDNOTARIZATION : EVAL_ACCEPTEDNOTARIZATION,
1638 cnd))
1639 {
1640 printf("%s: failed to create valid exports\n", __func__);
1641 LogPrintf("%s: failed to create valid exports\n", __func__);
1642 }
1643 else
1644 {
1645 currencyState = cnd.vtx[cnd.lastConfirmed].second.currencyState;
1646 }
1647 if (!currencyState.IsValid() ||
1648 !rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain().GetID(), lastChainDef, currencyState, chainObjects, vOutputs))
1649 {
1650 vOutputs = std::vector<CTxOut>();
1651 rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain().GetID(), lastChainDef, currencyState, chainObjects, vOutputs);
1652 DeleteOpRetObjects(chainObjects);
815a42a6 1653
4f8f2b54 1654 printf("%s: failed to create valid exports\n", __func__);
1655 LogPrintf("%s: failed to create valid exports\n", __func__);
815a42a6 1656
4f8f2b54 1657 // debugging output
1658 printf("%s: failed to export outputs:\n", __func__);
1659 for (auto oneout : vOutputs)
81fc0174 1660 {
4f8f2b54 1661 UniValue uniOut;
1662 ScriptPubKeyToJSON(oneout.scriptPubKey, uniOut, false);
1663 printf("%s\n", uniOut.write(true, 2).c_str());
c3250dcd 1664 }
4f8f2b54 1665 }
1666 else
1667 {
1668 CCcontract_info CC;
1669 CCcontract_info *cp;
c3250dcd 1670
4f8f2b54 1671 /*
1672 // debugging out
1673 printf("%s: exported outputs:\n", __func__);
1674 for (auto &oneout : chainObjects)
1adcca2c 1675 {
4f8f2b54 1676 if (oneout->objectType == CHAINOBJ_RESERVETRANSFER)
9ace9b55 1677 {
4f8f2b54 1678 CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)(oneout))->object;
1679 printf("%s\n", rt.ToUniValue().write(true, 2).c_str());
9ace9b55 1680 }
410af2c6 1681 }
4f8f2b54 1682 */
56fe75cb 1683
4f8f2b54 1684 CScript opRet = StoreOpRetArray(chainObjects);
1685 DeleteOpRetObjects(chainObjects);
c3250dcd 1686
4f8f2b54 1687 // now send transferred currencies to a reserve deposit
1688 cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
c3250dcd 1689
4f8f2b54 1690 currentReserveDeposits += ccx.totalAmounts;
1691 CCurrencyValueMap reserveDepositOut;
1692 CAmount nativeOut = 0;
cadf8969 1693
4f8f2b54 1694 for (auto &oneCurrencyOut : currentReserveDeposits.valueMap)
1695 {
1696 CCurrencyDefinition oneDef = currencyDefCache[oneCurrencyOut.first];
8e522b06 1697
4f8f2b54 1698 // if the destination is this chain, or
1699 // the destination is another blockchain that does not control source currency, store in reserve
1700 if ((oneDef.systemID == ASSETCHAINS_CHAINID || oneDef.systemID != lastChainDef.systemID) &&
1701 oneCurrencyOut.second)
56fe75cb 1702 {
4f8f2b54 1703 if (oneCurrencyOut.first == ASSETCHAINS_CHAINID)
56fe75cb 1704 {
4f8f2b54 1705 nativeOut = oneCurrencyOut.second;
1706 }
1707 else
1708 {
1709 reserveDepositOut.valueMap[oneCurrencyOut.first] = oneCurrencyOut.second;
56fe75cb 1710 }
1711 }
4f8f2b54 1712 }
481e7fd6 1713
4f8f2b54 1714 if (reserveDepositOut.valueMap.size() || nativeOut)
1715 {
1716 // send the entire amount to a reserve deposit output of the specific chain
1717 // we receive our fee on the other chain, when it comes back, or if a token,
1718 // when it gets imported back to the chain
1719 std::vector<CTxDestination> dests({CPubKey(ParseHex(CC.CChexstr))});
1720 CReserveDeposit rd = CReserveDeposit(lastChain, reserveDepositOut);
1721 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CReserveDeposit>(EVAL_RESERVE_DEPOSIT, dests, 1, &rd)),
1722 nativeOut);
1723 }
c3250dcd 1724
4f8f2b54 1725 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
c3250dcd 1726
4f8f2b54 1727 // send native amount of zero to a cross chain export output of the specific chain
1728 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
c3250dcd 1729
4f8f2b54 1730 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx)),
1731 exportOutVal);
c3250dcd 1732
4f8f2b54 1733 // when exports are confirmed as having been imported, they are finalized
1734 // until then, a finalization UTXO enables an index search to only find transactions
1735 // that have work to complete on this chain, or have not had their cross-chain import
1736 // acknowledged
1737 cp = CCinit(&CC, EVAL_FINALIZE_EXPORT);
1738 CTransactionFinalization finalization(CTransactionFinalization::FINALIZE_EXPORT, lastChain, 0);
05cef42a 1739
4f8f2b54 1740 //printf("%s: Finalizing export with index dest %s\n", __func__, EncodeDestination(CKeyID(CCrossChainRPCData::GetConditionID(lastChainDef.systemID, EVAL_FINALIZE_EXPORT))).c_str());
05cef42a 1741
4f8f2b54 1742 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
1743 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CTransactionFinalization>(EVAL_FINALIZE_EXPORT, dests, 1, &finalization)), 0);
09b977c6 1744
4f8f2b54 1745 tb.AddOpRet(opRet);
1746 tb.SetFee(0);
c3250dcd 1747
4f8f2b54 1748 /* UniValue uni(UniValue::VOBJ);
1749 TxToUniv(tb.mtx, uint256(), uni);
1750 printf("%s: about to send reserve deposits with tx:\n%s\n", __func__, uni.write(1,2).c_str()); */
cadf8969 1751
4f8f2b54 1752 TransactionBuilderResult buildResult(tb.Build());
c3250dcd 1753
4f8f2b54 1754 if (!buildResult.IsError() && buildResult.IsTx())
1755 {
1756 // replace the last one only if we have a valid new one
1757 CTransaction tx = buildResult.GetTxOrThrow();
1758
1759 /* uni = UniValue(UniValue::VOBJ);
1760 TxToUniv(tx, uint256(), uni);
1761 printf("%s: successfully built tx:\n%s\n", __func__, uni.write(1,2).c_str()); */
1762
1763 LOCK2(cs_main, mempool.cs);
1764 static int lastHeight = 0;
1765 // remove conflicts, so that we get in
1766 std::list<CTransaction> removed;
1767 mempool.removeConflicts(tx, removed);
1768
1769 // add to mem pool, prioritize according to the fee we will get, and relay
1770 //printf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1771 //LogPrintf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
1772 if (myAddtomempool(tx))
c3250dcd 1773 {
4f8f2b54 1774 oneExport = tx;
1775 uint256 hash = tx.GetHash();
1776 CAmount nativeExportFees = ccx.totalFees.valueMap[ASSETCHAINS_CHAINID];
1777 mempool.PrioritiseTransaction(hash, hash.GetHex(), (double)(nativeExportFees << 1), nativeExportFees);
8bb25a86 1778 }
1779 else
1780 {
4f8f2b54 1781 UniValue uni(UniValue::VOBJ);
1782 TxToUniv(tx, uint256(), uni);
1783 //printf("%s: created invalid transaction:\n%s\n", __func__, uni.write(1,2).c_str());
1784 LogPrintf("%s: created invalid transaction:\n%s\n", __func__, uni.write(1,2).c_str());
8bb25a86 1785 break;
c3250dcd
MT
1786 }
1787 }
4f8f2b54 1788 else
1789 {
1790 // we can't do any more useful work for this chain if we failed here
1791 printf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1792 LogPrintf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
1793 break;
1794 }
c3250dcd 1795 }
4f8f2b54 1796
1797 // erase the inputs we've attempted to spend
1798 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputs);
c3250dcd
MT
1799 }
1800 }
1801 }
4f8f2b54 1802 txInputs.clear();
1803 if (outputIt != transferOutputs.end())
1804 {
1805 lastChain = outputIt->first;
1806 txInputs.push_back(outputIt->second);
1807 }
c3250dcd 1808 }
0ab273d2 1809 CheckImports();
1810 }
1811 }
1812}
1813
1814void CConnectedChains::SignAndCommitImportTransactions(const CTransaction &lastImportTx, const std::vector<CTransaction> &transactions)
1815{
995a2fd0 1816 int nHeight = chainActive.LastTip()->GetHeight();
1817 uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());
0ab273d2 1818 LOCK2(cs_main, mempool.cs);
1819
995a2fd0 1820 uint256 lastHash, lastSignedHash;
1821 CCoinsViewCache view(pcoinsTip);
1822
0ab273d2 1823 // sign and commit the transactions
995a2fd0 1824 for (auto &_tx : transactions)
0ab273d2 1825 {
995a2fd0 1826 CMutableTransaction newTx(_tx);
09551a1c 1827
995a2fd0 1828 if (!lastHash.IsNull())
1829 {
1830 //printf("last hash before signing: %s\n", lastHash.GetHex().c_str());
1831 for (auto &oneIn : newTx.vin)
1832 {
1833 //printf("checking input with hash: %s\n", oneIn.prevout.hash.GetHex().c_str());
1834 if (oneIn.prevout.hash == lastHash)
1835 {
1836 oneIn.prevout.hash = lastSignedHash;
1837 //printf("updated hash before signing: %s\n", lastSignedHash.GetHex().c_str());
1838 }
1839 }
1840 }
1841 lastHash = _tx.GetHash();
1842 CTransaction tx = newTx;
0ab273d2 1843
1844 // sign the transaction and submit
995a2fd0 1845 bool signSuccess = false;
0ab273d2 1846 for (int i = 0; i < tx.vin.size(); i++)
1847 {
1848 SignatureData sigdata;
1849 CAmount value;
1850 CScript outputScript;
1851
1852 if (tx.vin[i].prevout.hash == lastImportTx.GetHash())
1853 {
1854 value = lastImportTx.vout[tx.vin[i].prevout.n].nValue;
1855 outputScript = lastImportTx.vout[tx.vin[i].prevout.n].scriptPubKey;
1856 }
1857 else
1858 {
0ab273d2 1859 CCoins coins;
1860 if (!view.GetCoins(tx.vin[i].prevout.hash, coins))
1861 {
1862 fprintf(stderr,"%s: cannot get input coins from tx: %s, output: %d\n", __func__, tx.vin[i].prevout.hash.GetHex().c_str(), tx.vin[i].prevout.n);
1863 LogPrintf("%s: cannot get input coins from tx: %s, output: %d\n", __func__, tx.vin[i].prevout.hash.GetHex().c_str(), tx.vin[i].prevout.n);
1864 break;
1865 }
1866 value = coins.vout[tx.vin[i].prevout.n].nValue;
1867 outputScript = coins.vout[tx.vin[i].prevout.n].scriptPubKey;
1868 }
1869
1870 signSuccess = ProduceSignature(TransactionSignatureCreator(nullptr, &tx, i, value, SIGHASH_ALL), outputScript, sigdata, consensusBranchId);
1871
1872 if (!signSuccess)
1873 {
8192d12f 1874 fprintf(stderr,"%s: failure to sign transaction\n", __func__);
1875 LogPrintf("%s: failure to sign transaction\n", __func__);
0ab273d2 1876 break;
1877 } else {
1878 UpdateTransaction(newTx, i, sigdata);
1879 }
1880 }
1881
1882 if (signSuccess)
1883 {
1884 // push to local node and sync with wallets
1885 CValidationState state;
1886 bool fMissingInputs;
1887 CTransaction signedTx(newTx);
09551a1c 1888
1889 //DEBUGGING
58b4e7b5 1890 //TxToJSON(tx, uint256(), jsonTX);
1891 //printf("signed transaction:\n%s\n", jsonTX.write(1, 2).c_str());
09551a1c 1892
0ab273d2 1893 if (!AcceptToMemoryPool(mempool, state, signedTx, false, &fMissingInputs)) {
1894 if (state.IsInvalid()) {
3ad32b99 1895 //UniValue txUni(UniValue::VOBJ);
1896 //TxToUniv(signedTx, uint256(), txUni);
1897 //fprintf(stderr,"%s: rejected by memory pool for %s\n%s\n", __func__, state.GetRejectReason().c_str(), txUni.write(1,2).c_str());
0ab273d2 1898 LogPrintf("%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
1899 } else {
1900 if (fMissingInputs) {
1901 fprintf(stderr,"%s: missing inputs\n", __func__);
1902 LogPrintf("%s: missing inputs\n", __func__);
1903 }
1904 else
1905 {
cd334056 1906 fprintf(stderr,"%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
1907 LogPrintf("%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
0ab273d2 1908 }
1909 }
1910 break;
1911 }
995a2fd0 1912 else
1913 {
1914 UpdateCoins(signedTx, view, nHeight);
1915 lastSignedHash = signedTx.GetHash();
1916 }
1917 }
1918 else
1919 {
1920 break;
c3250dcd
MT
1921 }
1922 }
1923}
1924
595242ca 1925CCurrencyValueMap CalculatePreconversions(const CCurrencyDefinition &chainDef, int32_t definitionHeight, CCurrencyValueMap &fees)
8192d12f 1926{
1927 // if we are getting information on the current chain, we assume that preconverted amounts have been
1928 // pre-calculated. otherwise, we will calculate them.
1929 CCurrencyValueMap retVal;
1930 if (chainDef.GetID() != ConnectedChains.ThisChain().GetID())
1931 {
1932 std::multimap<uint160, pair<CInputDescriptor, CReserveTransfer>> transferInputs;
1933 CCurrencyValueMap preconvertedAmounts;
8192d12f 1934
375c5ff8 1935 if (GetChainTransfers(transferInputs, chainDef.GetID(), definitionHeight, chainDef.startBlock - 1, CReserveTransfer::PRECONVERT | CReserveTransfer::VALID))
8192d12f 1936 {
f023c2d4 1937 auto curMap = chainDef.GetCurrenciesMap();
595242ca 1938 for (auto &transfer : transferInputs)
8192d12f 1939 {
595242ca 1940 if (!(transfer.second.second.flags & CReserveTransfer::PREALLOCATE) && curMap.count(transfer.second.second.currencyID))
1941 {
1942 CAmount conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1943 preconvertedAmounts.valueMap[transfer.second.second.currencyID] += (transfer.second.second.nValue - conversionFee);
1944 fees.valueMap[transfer.second.second.currencyID] += transfer.second.second.nFees + conversionFee;
1945 }
1946 }
1947 retVal = preconvertedAmounts;
1948 if (!chainDef.IsToken() && !(chainDef.ChainOptions() & chainDef.OPTION_FEESASRESERVE))
1949 {
1950 retVal += fees;
8192d12f 1951 }
1952 }
1953 }
1954 else
1955 {
1956 retVal = CCurrencyValueMap(chainDef.currencies, chainDef.preconverted);
1957 }
8192d12f 1958 return retVal;
1959}
1960
05cef42a 1961// This creates a new token notarization input and output and attaches them to a new import transaction,
1962// given the next export transaction about to be imported and its height
1963bool CConnectedChains::NewImportNotarization(const CCurrencyDefinition &_curDef,
1964 uint32_t height,
1965 const CTransaction &lastImportTx,
1966 uint32_t exportHeight,
1967 const CTransaction &exportTx,
1968 CMutableTransaction &mnewTx,
c88b19bd 1969 CCoinbaseCurrencyState &oldCurState,
05cef42a 1970 CCoinbaseCurrencyState &newCurState)
c3250dcd 1971{
05cef42a 1972 if (!_curDef.IsValid() || !_curDef.IsToken())
1973 {
1974 LogPrintf("%s: cannot create import notarization for invalid or non-token currencies\n", __func__);
1975 return false;
1976 }
0ab273d2 1977
05cef42a 1978 uint160 currencyID = _curDef.GetID();
1979
d3c97fd3 1980 CCurrencyDefinition curDef;
1981 int32_t curDefHeight;
1982 if (!GetCurrencyDefinition(currencyID, curDef, &curDefHeight))
1983 {
1984 LogPrintf("%s: cannot create import notarization - currency not found\n", __func__);
1985 return false;
1986 }
0ab273d2 1987
05cef42a 1988 CPBaaSNotarization lastNotarization(lastImportTx);
d3c97fd3 1989 if (curDef.systemID == ASSETCHAINS_CHAINID && !lastNotarization.IsValid())
0ab273d2 1990 {
05cef42a 1991 LogPrintf("%s: error getting notarization transaction %s\n", __func__, lastImportTx.GetHash().GetHex().c_str());
1992 return false;
1993 }
0ab273d2 1994
d3c97fd3 1995 CCoinbaseCurrencyState initialCurrencyState = lastNotarization.currencyState;
1996
81d264d6 1997 bool isDefinition = false;
1998
d3c97fd3 1999 // if our last notarization is prior to the start block, then we need to get our
2000 // initial currency state from a new start
2001 if (lastNotarization.notarizationHeight < curDef.startBlock)
2002 {
2003 initialCurrencyState = ConnectedChains.GetCurrencyState(curDef, curDef.startBlock - 1, curDefHeight);
81d264d6 2004 isDefinition = true;
2005 if (height >= curDef.startBlock)
2006 {
2007 initialCurrencyState.SetPrelaunch(false);
2008 }
d3c97fd3 2009 }
2010
05cef42a 2011 CCrossChainExport ccx(exportTx);
2012 if (!ccx.IsValid())
2013 {
2014 LogPrintf("%s: invalid export transaction %s\n", __func__, lastImportTx.GetHash().GetHex().c_str());
2015 return false;
2016 }
245743e7 2017
d3c97fd3 2018 if (!lastNotarization.IsValid())
375c5ff8 2019 {
d3c97fd3 2020 CChainNotarizationData cnd;
2021 // TODO: right now, there is no notarization made until after the launch
2022 // we should roll up periodic notarizations and pay miners to do it, making
2023 // long pre-launch periods possible
2024 if (GetNotarizationData(curDef.GetID(), EVAL_ACCEPTEDNOTARIZATION, cnd) && cnd.vtx.size() && cnd.lastConfirmed >= 0)
375c5ff8 2025 {
d3c97fd3 2026 lastNotarization = cnd.vtx[cnd.lastConfirmed].second;
2027 initialCurrencyState = lastNotarization.currencyState;
2028 }
2029 else
2030 {
2031 LogPrintf("%s: cannot get last notarization for %s\n", __func__, curDef.name.c_str());
375c5ff8 2032 return false;
2033 }
375c5ff8 2034 }
375c5ff8 2035
c88b19bd 2036 oldCurState = initialCurrencyState;
2037
05cef42a 2038 CBlockIndex *pindex;
2039 CTxDestination notarizationID = VERUS_DEFAULTID.IsNull() ? CTxDestination(CIdentityID(currencyID)) : CTxDestination(VERUS_DEFAULTID);
c0cdaabf 2040
05cef42a 2041 // if this is the first notarization after start, make the notarization and determine if we should
2042 // launch or refund
81d264d6 2043 if (isDefinition || height == curDef.startBlock -1)
05cef42a 2044 {
2045 bool refunding = false;
183a182b 2046
05cef42a 2047 pindex = chainActive[curDef.startBlock];
183a182b 2048
05cef42a 2049 // check if the chain is qualified for a refund
68ddda5e 2050 CCurrencyValueMap minPreMap, fees;
2051 CCurrencyValueMap preConvertedMap = CCurrencyValueMap(curDef.currencies, curDef.preconverted).CanonicalMap();
d3c97fd3 2052 newCurState = initialCurrencyState;
c0cdaabf 2053
05cef42a 2054 if (curDef.minPreconvert.size() && curDef.minPreconvert.size() == curDef.currencies.size())
2055 {
2056 minPreMap = CCurrencyValueMap(curDef.currencies, curDef.minPreconvert).CanonicalMap();
2057 }
8192d12f 2058
05cef42a 2059 if (minPreMap.valueMap.size() && preConvertedMap < minPreMap)
2060 {
2061 // we force the supply to zero
68ddda5e 2062 // in any case where there was less than minimum participation,
05cef42a 2063 // the result of the supply cannot be zero, enabling us to easily determine that this
2064 // represents a failed launch
2065 newCurState.supply = 0;
375c5ff8 2066 newCurState.SetRefunding(true);
05cef42a 2067 refunding = true;
2068 }
2069 else if (curDef.IsFractional() &&
2070 exportTx.vout.size() &&
2071 exportTx.vout.back().scriptPubKey.IsOpReturn())
2072 {
2073 // we are not refunding, and it is possible that we also have
2074 // normal conversions in addition to pre-conversions. add any conversions that may
2075 // be present into the new currency state
2076 CReserveTransactionDescriptor rtxd;
2077 std::vector<CBaseChainObject *> exportObjects;
2078 std::vector<CTxOut> vOutputs;
2079
2080 exportObjects = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey);
2081
d3c97fd3 2082 bool isValidExport = rtxd.AddReserveTransferImportOutputs(currencyID, curDef, initialCurrencyState, exportObjects, vOutputs, &newCurState);
1a36be8f 2083 if (isValidExport)
f8c4eeac 2084 {
1a36be8f 2085 // on definition, initial state is correct
2086 newCurState = initialCurrencyState;
f8c4eeac 2087 }
05cef42a 2088 DeleteOpRetObjects(exportObjects);
2089 if (!isValidExport)
2090 {
2091 LogPrintf("%s: invalid export opreturn for transaction %s\n", __func__, exportTx.GetHash().GetHex().c_str());
2092 return false;
2093 }
2094 }
2095 }
2096 else
2097 {
a7f05ef6 2098 if (chainActive.Height() > height)
2099 {
2100 pindex = chainActive[height];
2101 }
2102 else
2103 {
2104 pindex = chainActive.LastTip();
2105 }
2106
05cef42a 2107 if (!pindex)
2108 {
2109 LogPrintf("%s: invalid active chain\n", __func__);
2110 return false;
2111 }
8192d12f 2112
05cef42a 2113 // this is not the first notarization, so the last notarization will let us know if this is a refund or not
2114 CCurrencyValueMap minPreMap;
8192d12f 2115
d3c97fd3 2116 newCurState = initialCurrencyState;
8192d12f 2117
05cef42a 2118 if (curDef.minPreconvert.size() && curDef.minPreconvert.size() == curDef.currencies.size())
2119 {
2120 minPreMap = CCurrencyValueMap(curDef.currencies, curDef.minPreconvert).CanonicalMap();
2121 }
8192d12f 2122
375c5ff8 2123 // we won't change currency state in notarizations after failure to launch, if success, recalculate as needed
2124 if (!(lastNotarization.currencyState.IsRefunding()))
05cef42a 2125 {
2126 // calculate new currency state from this import
2127 // we are not refunding, and it is possible that we also have
2128 // normal conversions in addition to pre-conversions. add any conversions that may
2129 // be present into the new currency state
2130 CReserveTransactionDescriptor rtxd;
2131 std::vector<CBaseChainObject *> exportObjects;
2132 std::vector<CTxOut> vOutputs;
2133
2134 exportObjects = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey);
2135
d3c97fd3 2136 bool isValidExport = rtxd.AddReserveTransferImportOutputs(currencyID, curDef, initialCurrencyState, exportObjects, vOutputs, &newCurState);
1a36be8f 2137 if (isValidExport && curDef.IsFractional())
2138 {
2139 // we want the new price and the old state as a starting point to ensure no rounding error impact
2140 // on reserves
2141 CCoinbaseCurrencyState tempCurState = initialCurrencyState;
2142 tempCurState.conversionPrice = newCurState.conversionPrice;
2143 vOutputs.resize(0);
6b1372bb 2144 rtxd = CReserveTransactionDescriptor();
1a36be8f 2145 isValidExport = rtxd.AddReserveTransferImportOutputs(currencyID, curDef, tempCurState, exportObjects, vOutputs, &newCurState);
2146 }
fa42aa8e 2147 else if (!curDef.IsFractional())
2148 {
2149 newCurState = initialCurrencyState;
2150 }
05cef42a 2151 DeleteOpRetObjects(exportObjects);
2152 if (!isValidExport)
2153 {
2154 LogPrintf("%s: invalid export opreturn for transaction %s\n", __func__, exportTx.GetHash().GetHex().c_str());
2155 return false;
2156 }
2157 }
2158 }
8192d12f 2159
05cef42a 2160 uint256 lastImportTxHash = lastImportTx.GetHash();
2161
2162 // now, add the initial notarization to the import tx
2163 // we will begin refund or import after notarization is accepted and returned by GetNotarizationData
2164 CPBaaSNotarization pbn = CPBaaSNotarization(curDef.notarizationProtocol,
2165 currencyID,
2166 notarizationID,
19dac530 2167 height,
05cef42a 2168 chainActive.GetMMV().GetRoot(),
2169 chainActive.GetMMRNode(pindex->GetHeight()).hash,
2170 ArithToUint256(GetCompactPower(pindex->nNonce, pindex->nBits, pindex->nVersion)),
2171 newCurState,
2172 lastImportTxHash,
2173 lastNotarization.notarizationHeight,
2174 uint256(), 0, COpRetProof(), std::vector<CNodeData>());
2175
2176 // create notarization output
2177 CCcontract_info CC;
2178 CCcontract_info *cp;
2179
2180 std::vector<CTxDestination> dests;
05cef42a 2181
2182 // make the accepted notarization output
2183 cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
2184
2185 if (curDef.notarizationProtocol == curDef.NOTARIZATION_NOTARY_CHAINID)
2186 {
2187 dests = std::vector<CTxDestination>({CIdentityID(currencyID)});
2188 }
2189 else if (curDef.notarizationProtocol == curDef.NOTARIZATION_AUTO)
2190 {
2191 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2192 }
2193 else
2194 {
2195 return false;
2196 }
4a53088e 2197 mnewTx.vout.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &pbn))));
8192d12f 2198
05cef42a 2199 // make the finalization output
2200 cp = CCinit(&CC, EVAL_FINALIZE_NOTARIZATION);
8192d12f 2201
05cef42a 2202 if (curDef.notarizationProtocol == curDef.NOTARIZATION_AUTO)
2203 {
2204 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2205 }
8192d12f 2206
05cef42a 2207 // finish transaction by adding the prior input and finalization, sign, then put it in the mempool
2208 // all output for notarizing will be paid as mining fees, so there's no need to relay
2209 uint32_t confirmedOut, finalizeOut;
2210 if (!GetNotarizationAndFinalization(EVAL_ACCEPTEDNOTARIZATION, lastImportTx, pbn, &confirmedOut, &finalizeOut))
2211 {
2212 printf("ERROR: could not find expected initial notarization for currency %s\n", curDef.name.c_str());
2213 return false;
2214 }
8192d12f 2215
05cef42a 2216 mnewTx.vin.push_back(CTxIn(COutPoint(lastImportTxHash, confirmedOut)));
2217 mnewTx.vin.push_back(CTxIn(COutPoint(lastImportTxHash, finalizeOut)));
8192d12f 2218
05cef42a 2219 // we need to store the input that we confirmed if we spent finalization outputs
2d8b9129 2220 CTransactionFinalization nf(CTransactionFinalization::FINALIZE_NOTARIZATION, pbn.currencyID, mnewTx.vin.size() - 1);
8192d12f 2221
05cef42a 2222 // update crypto condition with final notarization output data
375c5ff8 2223 mnewTx.vout.push_back(CTxOut(0,
2d8b9129 2224 MakeMofNCCScript(CConditionObj<CTransactionFinalization>(EVAL_FINALIZE_NOTARIZATION, dests, 1, &nf))));
0ab273d2 2225
05cef42a 2226 return true;
2227}
0ab273d2 2228
05cef42a 2229// process token related, local imports and exports
2230void CConnectedChains::ProcessLocalImports()
2231{
2232 // first determine all export threads on the current chain that are valid to import
2233 std::multimap<uint160, std::pair<int, CInputDescriptor>> exportOutputs;
2234 std::multimap<uint160, CTransaction> importThreads;
2235 uint160 thisChainID = thisChain.GetID();
0ab273d2 2236
05cef42a 2237 LOCK2(cs_main, mempool.cs);
2238 uint32_t nHeight = chainActive.Height();
0ab273d2 2239
05cef42a 2240 // get all pending, local exports and put them into a map
2241 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue>> unspentOutputs;
2242 std::map<uint160, std::pair<uint32_t, CTransaction>> currenciesToImport; // height of earliest tx
2243 CCurrencyDefinition oneCurrency;
ea574fe2 2244
b4df9505 2245 //printf("%s: Searching for %s\n", __func__, EncodeDestination(CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_FINALIZE_EXPORT))).c_str());
e8add12d 2246 if (GetAddressUnspent(ConnectedChains.ThisChain().GetConditionID(EVAL_FINALIZE_EXPORT), CScript::P2IDX, unspentOutputs))
05cef42a 2247 {
2248 CCrossChainExport ccx, ccxDummy;
2249 CTransaction txOut, txImport;
2250 CPartialTransactionProof lastExport;
2251 CCrossChainImport cci;
2252 uint256 blkHash;
2253 for (auto &oneOut : unspentOutputs)
2254 {
2255 COptCCParams p;
e53b0990 2256 if (oneOut.second.blockHeight <= nHeight &&
2257 oneOut.second.script.IsPayToCryptoCondition(p) &&
05cef42a 2258 p.IsValid() &&
2259 p.evalCode == EVAL_FINALIZE_EXPORT &&
2260 p.vData.size() &&
2261 CTransactionFinalization(p.vData[0]).IsValid() &&
2262 myGetTransaction(oneOut.first.txhash, txOut, blkHash) &&
2263 (ccx = CCrossChainExport(txOut)).IsValid() &&
c88b19bd 2264 !currenciesToImport.count(ccx.systemID) &&
05cef42a 2265 (oneCurrency = GetCachedCurrency(ccx.systemID)).IsValid() &&
375c5ff8 2266 oneCurrency.startBlock <= nHeight &&
05cef42a 2267 GetLastImport(ccx.systemID, txImport, lastExport, cci, ccxDummy))
0ab273d2 2268 {
05cef42a 2269 auto blockIt = mapBlockIndex.find(blkHash);
2270 if (blockIt != mapBlockIndex.end() && chainActive.Contains(blockIt->second))
0ab273d2 2271 {
05cef42a 2272 currenciesToImport.insert(make_pair(ccx.systemID, make_pair(blockIt->second->GetHeight(), txImport)));
0ab273d2 2273 }
2274 }
2275 }
2276 }
2277
2278 CMutableTransaction txTemplate = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nHeight);
05cef42a 2279 for (auto &oneIT : currenciesToImport)
0ab273d2 2280 {
2281 std::vector<CTransaction> importTxes;
c8c677c9 2282 int32_t importOutNum = 0;
c88b19bd 2283
05cef42a 2284 CCrossChainImport oneImportInput(oneIT.second.second, &importOutNum);
0ab273d2 2285 if (oneImportInput.IsValid())
2286 {
2287 std::vector<CAddressUnspentDbEntry> reserveDeposits;
481e7fd6 2288 GetAddressUnspent(ConnectedChains.GetCachedCurrency(oneIT.first).GetConditionID(EVAL_RESERVE_DEPOSIT), CScript::P2IDX, reserveDeposits);
0ab273d2 2289 CCurrencyValueMap tokenImportAvailable;
2290 CAmount nativeImportAvailable = 0;
2291 for (auto &oneOut : reserveDeposits)
2292 {
2293 nativeImportAvailable += oneOut.second.satoshis;
2294 tokenImportAvailable += oneOut.second.script.ReserveOutValue();
20448425 2295 //printf("nativeImportAvailable:%ld, tokenImportAvailable:%s\n", nativeImportAvailable, tokenImportAvailable.ToUniValue().write().c_str());
0ab273d2 2296 }
05cef42a 2297 nativeImportAvailable += oneIT.second.second.vout[importOutNum].nValue;
2298 tokenImportAvailable += oneIT.second.second.vout[importOutNum].ReserveOutValue();
20448425 2299 //printf("nativeImportAvailable:%ld, tokenImportAvailable:%s\n", nativeImportAvailable, tokenImportAvailable.ToUniValue().write().c_str());
05cef42a 2300 if (CreateLatestImports(currencyDefCache[oneIT.first], oneIT.second.second, txTemplate, CTransaction(), tokenImportAvailable, nativeImportAvailable, importTxes))
0ab273d2 2301 {
2302 // fund the first import transaction with all reserveDeposits
2303 // change amounts are passed through on the import thread
2304 //
2305 // TODO: manage when this becomes to large by splitting reserve deposits over the
2306 // transactions
2307 if (importTxes.size())
2308 {
995a2fd0 2309 CMutableTransaction oneImport = importTxes[0];
c8c677c9 2310 int32_t outNum = 0;
0ab273d2 2311 CCrossChainImport cci(importTxes[0], &outNum);
2312 if (cci.IsValid())
2313 {
56a7b665 2314 // add the reserve deposit inputs to the first transaction
2315 // the outputs should have been automatically propagated through
995a2fd0 2316 // fixup inputs if necessary
2317 if (reserveDeposits.size())
0ab273d2 2318 {
995a2fd0 2319 UniValue jsonTX(UniValue::VOBJ);
2320 std::vector<uint256> prevHashes;
2321
2322 for (int i = 0; i < importTxes.size() - 1; i++)
2323 {
2324 prevHashes.push_back(importTxes[i].GetHash());
2325 }
2326
2327 // TODO - get reserve deposits from exports, not all at once, as in refunds
2328 for (auto &oneOut : reserveDeposits)
2329 {
2330 oneImport.vin.push_back(CTxIn(oneOut.first.txhash, oneOut.first.index));
2331 }
2332
2333 importTxes[0] = oneImport;
2334
2335 for (int i = 0; i < importTxes.size() - 1; i++)
2336 {
2337 oneImport = importTxes[i + 1];
2338 for (auto &oneIn : oneImport.vin)
2339 {
2340 if (oneIn.prevout.hash == prevHashes[i])
2341 {
2342 //printf("updating hash before signing to new value\nold: %s\nnew: %s\n", oneIn.prevout.hash.GetHex().c_str(), importTxes[i].GetHash().GetHex().c_str());
2343 oneIn.prevout.hash = importTxes[i].GetHash();
2344 }
2345 }
2346 importTxes[i + 1] = oneImport;
2347 }
2348
0ab273d2 2349 }
05cef42a 2350 SignAndCommitImportTransactions(oneIT.second.second, importTxes);
0ab273d2 2351 }
2352 }
2353 }
2354 }
2355 }
c3250dcd
MT
2356}
2357
b2a98c42
MT
2358void CConnectedChains::SubmissionThread()
2359{
2360 try
2361 {
2362 arith_uint256 lastHash;
687e93d5
MT
2363 int64_t lastImportTime = 0;
2364 uint32_t lastHeight = 0;
b2a98c42 2365
a82942e4 2366 // wait for something to check on, then submit blocks that should be submitted
b2a98c42
MT
2367 while (true)
2368 {
c3250dcd
MT
2369 boost::this_thread::interruption_point();
2370
9f0c14b2 2371 if (IsVerusActive())
b2a98c42 2372 {
23d61f0a 2373 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
9014248c 2374 //printf("SubmissionThread: pruning\n");
c3250dcd 2375 PruneOldChains(GetAdjustedTime() - 300);
2fd1f0fb 2376 bool submit = false;
b2a98c42 2377 {
2fd1f0fb 2378 LOCK(cs_mergemining);
bea3e6a2
MT
2379 if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
2380 {
326f5f88 2381 qualifiedHeaders.clear();
bea3e6a2 2382 }
2fd1f0fb 2383 submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
23d61f0a 2384
9014248c 2385 //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
2fd1f0fb 2386 }
2387 if (submit)
2388 {
9014248c 2389 //printf("SubmissionThread: calling submit qualified blocks\n");
2fd1f0fb 2390 SubmitQualifiedBlocks();
b2a98c42 2391 }
8192d12f 2392
2393 ProcessLocalImports();
2394
2395 if (!submit)
9f0c14b2 2396 {
2fd1f0fb 2397 sem_submitthread.wait();
9f0c14b2 2398 }
b2a98c42
MT
2399 }
2400 else
2401 {
9f0c14b2 2402 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
5a72525a 2403 if (CheckVerusPBaaSAvailable())
2830db29 2404 {
5a72525a 2405 // check to see if we have recently earned a block with an earned notarization that qualifies for
2406 // submitting an accepted notarization
2407 if (earnedNotarizationHeight)
572c763f 2408 {
5a72525a 2409 CBlock blk;
2410 int32_t txIndex = -1, height;
572c763f 2411 {
5a72525a 2412 LOCK(cs_mergemining);
2413 if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
2414 {
2415 blk = earnedNotarizationBlock;
2416 earnedNotarizationBlock = CBlock();
2417 txIndex = earnedNotarizationIndex;
2418 height = earnedNotarizationHeight;
2419 earnedNotarizationHeight = 0;
2420 }
572c763f 2421 }
572c763f 2422
5a72525a 2423 if (txIndex != -1)
d6bc5de8 2424 {
5a72525a 2425 //printf("SubmissionThread: testing notarization\n");
2426 CTransaction lastConfirmed;
2427 uint256 txId = CreateAcceptedNotarization(blk, txIndex, height);
2428
2429 if (!txId.IsNull())
2430 {
2431 printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
2432 LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
2433 }
d6bc5de8 2434 }
572c763f 2435 }
687e93d5 2436
5a72525a 2437 // every "n" seconds, look for imports to include in our blocks from the Verus chain
2438 if ((GetAdjustedTime() - lastImportTime) >= 30 || lastHeight < (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight()))
2439 {
2440 lastImportTime = GetAdjustedTime();
2441 lastHeight = (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight());
687e93d5 2442
5a72525a 2443 // see if our notary has a confirmed notarization for us
2444 UniValue params(UniValue::VARR);
2445 UniValue result;
687e93d5 2446
5a72525a 2447 params.push_back(thisChain.name);
687e93d5 2448
5a72525a 2449 try
2450 {
2451 result = find_value(RPCCallRoot("getlastimportin", params), "result");
2452 } catch (exception e)
2453 {
2454 result = NullUniValue;
2455 }
687e93d5 2456
5a72525a 2457 if (!result.isNull())
2458 {
2459 auto txUniStr = find_value(result, "lastimporttransaction");
2460 auto txLastConfirmedStr = find_value(result, "lastconfirmednotarization");
2461 auto txTemplateStr = find_value(result, "importtxtemplate");
56fe75cb 2462 CAmount nativeImportAvailable = uni_get_int64(find_value(result, "nativeimportavailable"));
2463 CCurrencyValueMap tokenImportAvailable(find_value(params[0], "tokenimportavailable"));
687e93d5 2464
5a72525a 2465 CTransaction lastImportTx, lastConfirmedTx, templateTx;
687e93d5 2466
5a72525a 2467 if (txUniStr.isStr() && txTemplateStr.isStr() &&
2468 DecodeHexTx(lastImportTx, txUniStr.get_str()) &&
2469 DecodeHexTx(lastConfirmedTx, txLastConfirmedStr.get_str()) &&
2470 DecodeHexTx(templateTx, txTemplateStr.get_str()))
687e93d5 2471 {
5a72525a 2472 std::vector<CTransaction> importTxes;
56fe75cb 2473 if (CreateLatestImports(notaryChain.chainDefinition, lastImportTx, templateTx, lastConfirmedTx, tokenImportAvailable, nativeImportAvailable, importTxes))
687e93d5 2474 {
5a72525a 2475 for (auto importTx : importTxes)
687e93d5 2476 {
5a72525a 2477 UniValue txResult;
70506d95 2478 params.setArray();
5a72525a 2479 params.push_back(EncodeHexTx(importTx));
2480
2481 try
687e93d5 2482 {
5a72525a 2483 txResult = find_value(RPCCallRoot("signrawtransaction", params), "result");
fa6b4d41 2484 if (txResult.isObject() && !(txResult = find_value(txResult, "hex")).isNull() && txResult.isStr() && txResult.get_str().size())
5a72525a 2485 {
fa6b4d41 2486 params.setArray();
5a72525a 2487 params.push_back(txResult);
2488 txResult = find_value(RPCCallRoot("sendrawtransaction", params), "result");
2489 }
2490 else
2491 {
2492 txResult = NullUniValue;
2493 }
2494
2495 } catch (exception e)
687e93d5
MT
2496 {
2497 txResult = NullUniValue;
2498 }
5a72525a 2499 uint256 testId;
2500 if (txResult.isStr())
2501 {
2502 testId.SetHex(txResult.get_str());
2503 }
2504 if (testId.IsNull())
2505 {
2506 break;
2507 }
687e93d5
MT
2508 }
2509 }
2510 }
2511 }
2512 }
2513 }
2514 sleep(3);
b2a98c42 2515 }
b2a98c42
MT
2516 boost::this_thread::interruption_point();
2517 }
2518 }
2519 catch (const boost::thread_interrupted&)
2520 {
2521 LogPrintf("Verus merge mining thread terminated\n");
2522 }
2523}
9f0c14b2 2524
b2a98c42
MT
2525void CConnectedChains::SubmissionThreadStub()
2526{
2527 ConnectedChains.SubmissionThread();
2528}
2529
572c763f
MT
2530void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
2531{
2532 // called after winning a block that contains an earned notarization
2533 // the earned notarization and its height are queued for processing by the submission thread
2534 // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
2535 // kept
2536 LOCK(cs_mergemining);
2537
94a210aa 2538 // we only care about the last
572c763f 2539 earnedNotarizationHeight = height;
94a210aa
MT
2540 earnedNotarizationBlock = blk;
2541 earnedNotarizationIndex = txIndex;
572c763f
MT
2542}
2543
68b309c0
MT
2544bool IsChainDefinitionInput(const CScript &scriptSig)
2545{
2546 uint32_t ecode;
56fe75cb 2547 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_CURRENCY_DEFINITION;
68b309c0 2548}
13ed2980 2549
This page took 0.587384 seconds and 4 git commands to generate.