]> Git Repo - VerusCoin.git/blame - src/pbaas/pbaas.cpp
New currency state member for preconverted output record
[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
c8c684e9 119// Validate notary evidence
120bool ValidateNotaryEvidence(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
b2a98c42 121{
13ed2980 122 return true;
b2a98c42 123}
c8c684e9 124
125bool IsNotaryEvidenceInput(const CScript &scriptSig)
78819397 126{
9ec65f90 127 return true;
78819397 128}
b2a98c42
MT
129
130// used as a proxy token output for a reserve currency on its fractional reserve chain
4ecaf167 131bool ValidateReserveOutput(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
b2a98c42 132{
78819397
MT
133 return true;
134}
135bool IsReserveOutputInput(const CScript &scriptSig)
136{
9ec65f90 137 return true;
b2a98c42
MT
138}
139
4ecaf167 140bool ValidateReserveTransfer(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
78819397
MT
141{
142 return true;
143}
144bool IsReserveTransferInput(const CScript &scriptSig)
b2a98c42 145{
9ec65f90 146 return true;
78819397 147}
b2a98c42 148
4ecaf167 149bool ValidateReserveDeposit(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
78819397
MT
150{
151 return true;
152}
153bool IsReserveDepositInput(const CScript &scriptSig)
154{
9ec65f90 155 return true;
b2a98c42
MT
156}
157
4ecaf167 158bool ValidateCurrencyState(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
b2a98c42 159{
78819397
MT
160 return true;
161}
162bool IsCurrencyStateInput(const CScript &scriptSig)
163{
9ec65f90 164 return true;
78819397 165}
b2a98c42 166
78819397 167// used to convert a fractional reserve currency into its reserve and back
4ecaf167 168bool ValidateReserveExchange(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
78819397
MT
169{
170 return true;
b2a98c42 171}
d305d656 172bool IsReserveExchangeInput(const CScript &scriptSig)
173{
9ec65f90 174 return true;
d305d656 175}
176
b2a98c42
MT
177
178/*
179 * Verifies that the input objects match the hashes and returns the transaction.
180 *
181 * If the opRetTx has the op ret, this calculates based on the actual transaction and
182 * validates the hashes. If the opRetTx does not have the opRet itself, this validates
183 * by ensuring that all objects are present on this chain, composing the opRet, and
184 * ensuring that the transaction then hashes to the correct txid.
185 *
186 */
187bool ValidateOpretProof(CScript &opRet, COpRetProof &orProof)
188{
189 // enumerate through the objects and validate that they are objects of the expected type that hash
190 // to the value expected. return true if so
9ec65f90 191 return true;
b2a98c42
MT
192}
193
56fe75cb 194int8_t ObjTypeCode(const CBlockHeaderProof &obj)
b2a98c42
MT
195{
196 return CHAINOBJ_HEADER;
197}
198
56fe75cb 199int8_t ObjTypeCode(const uint256 &obj)
b2a98c42 200{
56fe75cb 201 return CHAINOBJ_PROOF_ROOT;
b2a98c42
MT
202}
203
56fe75cb 204int8_t ObjTypeCode(const CPartialTransactionProof &obj)
b2a98c42 205{
56fe75cb 206 return CHAINOBJ_TRANSACTION_PROOF;
b2a98c42
MT
207}
208
56fe75cb 209int8_t ObjTypeCode(const CBlockHeaderAndProof &obj)
b2a98c42
MT
210{
211 return CHAINOBJ_HEADER_REF;
212}
213
1fa4454d 214int8_t ObjTypeCode(const CPriorBlocksCommitment &obj)
b2a98c42 215{
1fa4454d 216 return CHAINOBJ_PRIORBLOCKS;
b2a98c42
MT
217}
218
989b1de1
MT
219int8_t ObjTypeCode(const CReserveTransfer &obj)
220{
221 return CHAINOBJ_RESERVETRANSFER;
222}
223
34d1aa13
MT
224int8_t ObjTypeCode(const CCrossChainProof &obj)
225{
226 return CHAINOBJ_CROSSCHAINPROOF;
227}
228
56fe75cb 229int8_t ObjTypeCode(const CCompositeChainObject &obj)
230{
231 return CHAINOBJ_COMPOSITEOBJECT;
232}
233
b2a98c42 234// this adds an opret to a mutable transaction that provides the necessary evidence of a signed, cheating stake transaction
56fe75cb 235CScript StoreOpRetArray(const std::vector<CBaseChainObject *> &objPtrs)
b2a98c42
MT
236{
237 CScript vData;
238 CDataStream s = CDataStream(SER_NETWORK, PROTOCOL_VERSION);
0b806be9 239 s << (int32_t)OPRETTYPE_OBJECTARR;
b2a98c42
MT
240 bool error = false;
241
242 for (auto pobj : objPtrs)
243 {
d48d49b6
MT
244 try
245 {
246 if (!DehydrateChainObject(s, pobj))
247 {
248 error = true;
249 break;
250 }
251 }
252 catch(const std::exception& e)
b2a98c42 253 {
d48d49b6 254 std::cerr << e.what() << '\n';
b2a98c42
MT
255 error = true;
256 break;
257 }
258 }
259
05578402
MT
260 //std::vector<unsigned char> schars(s.begin(), s.begin() + 200);
261 //printf("stream vector chars: %s\n", HexBytes(&schars[0], schars.size()).c_str());
6278cf9c 262
b2a98c42 263 std::vector<unsigned char> vch(s.begin(), s.end());
d48d49b6 264 return error ? CScript() : CScript() << OP_RETURN << vch;
b2a98c42
MT
265}
266
a8c2cb11
MT
267void DeleteOpRetObjects(std::vector<CBaseChainObject *> &ora)
268{
269 for (auto pobj : ora)
270 {
271 switch(pobj->objectType)
272 {
273 case CHAINOBJ_HEADER:
274 {
56fe75cb 275 delete (CChainObject<CBlockHeaderAndProof> *)pobj;
a8c2cb11
MT
276 break;
277 }
278
56fe75cb 279 case CHAINOBJ_TRANSACTION_PROOF:
a8c2cb11 280 {
56fe75cb 281 delete (CChainObject<CPartialTransactionProof> *)pobj;
a8c2cb11
MT
282 break;
283 }
284
56fe75cb 285 case CHAINOBJ_PROOF_ROOT:
a8c2cb11 286 {
56fe75cb 287 delete (CChainObject<uint256> *)pobj;
a8c2cb11
MT
288 break;
289 }
290
291 case CHAINOBJ_HEADER_REF:
292 {
56fe75cb 293 delete (CChainObject<CBlockHeaderProof> *)pobj;
a8c2cb11
MT
294 break;
295 }
296
297 case CHAINOBJ_PRIORBLOCKS:
298 {
299 delete (CChainObject<CPriorBlocksCommitment> *)pobj;
300 break;
301 }
302
989b1de1
MT
303 case CHAINOBJ_RESERVETRANSFER:
304 {
305 delete (CChainObject<CReserveTransfer> *)pobj;
306 break;
307 }
308
34d1aa13
MT
309 case CHAINOBJ_CROSSCHAINPROOF:
310 {
311 delete (CChainObject<CCrossChainProof> *)pobj;
312 break;
313 }
314
56fe75cb 315 case CHAINOBJ_COMPOSITEOBJECT:
316 {
317 delete (CChainObject<CCompositeChainObject> *)pobj;
318 break;
319 }
320
c8c684e9 321 case CHAINOBJ_NOTARYSIGNATURE:
322 {
323 delete (CChainObject<CNotaryEvidence> *)pobj;
324 break;
325 }
326
a8c2cb11 327 default:
c8c677c9 328 {
84f390e5 329 printf("ERROR: invalid object type (%u), likely corrupt pointer %p\n", pobj->objectType, pobj);
c8c677c9 330 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());
331 printf("This is here to generate enough code for a good break point system chain name: %s\n", ConnectedChains.ThisChain().name.c_str());
332
a8c2cb11 333 delete pobj;
c8c677c9 334 }
a8c2cb11
MT
335 }
336 }
5d13ad1e 337 ora.clear();
a8c2cb11
MT
338}
339
b2a98c42
MT
340std::vector<CBaseChainObject *> RetrieveOpRetArray(const CScript &opRetScript)
341{
342 std::vector<unsigned char> vch;
343 std::vector<CBaseChainObject *> vRet;
344 if (opRetScript.IsOpReturn() && GetOpReturnData(opRetScript, vch) && vch.size() > 0)
345 {
346 CDataStream s = CDataStream(vch, SER_NETWORK, PROTOCOL_VERSION);
347
1fa4454d
MT
348 int32_t opRetType;
349
350 try
351 {
352 s >> opRetType;
353 if (opRetType == OPRETTYPE_OBJECTARR)
354 {
355 CBaseChainObject *pobj;
356 while (!s.empty() && (pobj = RehydrateChainObject(s)))
357 {
358 vRet.push_back(pobj);
359 }
aeca4678
MT
360 if (!s.empty())
361 {
2d8cedc7 362 printf("failed to load all objects in opret");
ba256d3a 363 DeleteOpRetObjects(vRet);
2d8cedc7 364 vRet.clear();
aeca4678 365 }
1fa4454d
MT
366 }
367 }
368 catch(const std::exception& e)
b2a98c42 369 {
1fa4454d 370 std::cerr << e.what() << '\n';
ba256d3a 371 DeleteOpRetObjects(vRet);
1fa4454d 372 vRet.clear();
b2a98c42
MT
373 }
374 }
375 return vRet;
376}
377
c8c684e9 378CCrossChainExport::CCrossChainExport(const CScript &script)
b2a98c42 379{
c8c684e9 380 COptCCParams p;
381 if (script.IsPayToCryptoCondition(p) &&
382 p.IsValid() &&
383 p.evalCode == EVAL_CROSSCHAIN_EXPORT)
b2a98c42 384 {
c8c684e9 385 FromVector(p.vData[0], *this);
b2a98c42 386 }
b2a98c42
MT
387}
388
56fe75cb 389CCrossChainExport::CCrossChainExport(const CTransaction &tx, int32_t *pCCXOutputNum)
13ed2980 390{
56fe75cb 391 int32_t _ccxOutputNum = 0;
392 int32_t &ccxOutputNum = pCCXOutputNum ? *pCCXOutputNum : _ccxOutputNum;
393
394 for (int i = 0; i < tx.vout.size(); i++)
13ed2980
MT
395 {
396 COptCCParams p;
56fe75cb 397 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) &&
398 p.IsValid() &&
399 p.evalCode == EVAL_CROSSCHAIN_EXPORT)
13ed2980 400 {
56fe75cb 401 FromVector(p.vData[0], *this);
402 ccxOutputNum = i;
403 break;
13ed2980
MT
404 }
405 }
13ed2980
MT
406}
407
cd853aa1 408CCurrencyDefinition::CCurrencyDefinition(const CScript &scriptPubKey)
41f170fd 409{
56fe75cb 410 nVersion = PBAAS_VERSION_INVALID;
cd853aa1 411 COptCCParams p;
412 if (scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
413 {
414 if (p.evalCode == EVAL_CURRENCY_DEFINITION)
415 {
416 FromVector(p.vData[0], *this);
417 }
418 }
419}
420
421std::vector<CCurrencyDefinition> CCurrencyDefinition::GetCurrencyDefinitions(const CTransaction &tx)
422{
423 std::vector<CCurrencyDefinition> retVal;
2f416b17 424 for (auto &out : tx.vout)
41f170fd 425 {
cd853aa1 426 CCurrencyDefinition oneCur = CCurrencyDefinition(out.scriptPubKey);
427 if (oneCur.IsValid())
41f170fd 428 {
cd853aa1 429 retVal.push_back(oneCur);
41f170fd
MT
430 }
431 }
cd853aa1 432 return retVal;
41f170fd
MT
433}
434
f8f61a6d 435#define _ASSETCHAINS_TIMELOCKOFF 0xffffffffffffffff
344a051d 436extern uint64_t ASSETCHAINS_TIMELOCKGTE, ASSETCHAINS_TIMEUNLOCKFROM, ASSETCHAINS_TIMEUNLOCKTO;
70c48c13 437extern int64_t ASSETCHAINS_SUPPLY, ASSETCHAINS_REWARD[3], ASSETCHAINS_DECAY[3], ASSETCHAINS_HALVING[3], ASSETCHAINS_ENDSUBSIDY[3], ASSETCHAINS_ERAOPTIONS[3];
344a051d 438extern int32_t PBAAS_STARTBLOCK, PBAAS_ENDBLOCK, ASSETCHAINS_LWMAPOS;
f8f61a6d 439extern uint32_t ASSETCHAINS_ALGO, ASSETCHAINS_VERUSHASH, ASSETCHAINS_LASTERA;
f2d873d0 440extern std::string VERUS_CHAINNAME;
f7917c6b 441extern uint160 VERUS_CHAINID;
f8f61a6d 442
b2a98c42
MT
443// ensures that the chain definition is valid and that there are no other definitions of the same name
444// that have been confirmed.
4ecaf167 445bool ValidateChainDefinition(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
b2a98c42
MT
446{
447 // the chain definition output can be spent when the chain is at the end of its life and only then
448 // TODO
449 return false;
450}
451
815a42a6 452CCurrencyValueMap CCrossChainExport::CalculateExportFee(const CCurrencyValueMap &fees, int numIn)
989b1de1 453{
56fe75cb 454 CCurrencyValueMap retVal;
c8c684e9 455 int maxFeeCalc = numIn;
56fe75cb 456
c8c684e9 457 if (maxFeeCalc > MAX_FEE_INPUTS)
989b1de1 458 {
c8c684e9 459 maxFeeCalc = MAX_FEE_INPUTS;
989b1de1
MT
460 }
461 static const arith_uint256 satoshis(100000000);
462
c8c684e9 463 arith_uint256 ratio(50000000 + ((25000000 / maxFeeCalc) * (numIn - 1)));
56fe75cb 464
815a42a6 465 for (auto &feePair : fees.valueMap)
56fe75cb 466 {
467 retVal.valueMap[feePair.first] = (((arith_uint256(feePair.second) * ratio)) / satoshis).GetLow64();
468 }
048edb97 469 return retVal.CanonicalMap();
56fe75cb 470}
471
e4e5ccf5 472CAmount CCrossChainExport::CalculateExportFeeRaw(CAmount fee, int numIn)
473{
4c5696b1 474 int maxFeeCalc = std::min((int)MAX_FEE_INPUTS, std::max(1, numIn));
e4e5ccf5 475 static const arith_uint256 satoshis(100000000);
476
c8c684e9 477 arith_uint256 ratio(50000000 + ((25000000 / MAX_FEE_INPUTS) * (maxFeeCalc - 1)));
e4e5ccf5 478
479 return (((arith_uint256(fee) * ratio)) / satoshis).GetLow64();
480}
481
482CAmount CCrossChainExport::ExportReward(int64_t exportFee)
483{
484 int64_t individualExportFee = ((arith_uint256(exportFee) * 10000000) / SATOSHIDEN).GetLow64();
485 if (individualExportFee < MIN_FEES_BEFORE_FEEPOOL)
486 {
487 individualExportFee = exportFee > MIN_FEES_BEFORE_FEEPOOL ? MIN_FEES_BEFORE_FEEPOOL : exportFee;
488 }
489 return individualExportFee;
490}
491
815a42a6 492CCurrencyValueMap CCrossChainExport::CalculateExportFee() const
493{
494 return CalculateExportFee(totalFees, numInputs);
495}
496
56fe75cb 497CCurrencyValueMap CCrossChainExport::CalculateImportFee() const
498{
499 CCurrencyValueMap retVal;
989b1de1 500
56fe75cb 501 for (auto &feePair : CalculateExportFee().valueMap)
502 {
503 CAmount feeAmount = feePair.second;
504 auto it = totalFees.valueMap.find(feePair.first);
505 retVal.valueMap[feePair.first] = (it != totalFees.valueMap.end() ? it->second : 0) - feeAmount;
506 }
507 return retVal;
989b1de1
MT
508}
509
c8c684e9 510bool CEthGateway::ValidateDestination(const std::string &destination) const
511{
512 // just returns true if it looks like a non-NULL ETH address
513 return (destination.substr(0,2) == "0x" &&
514 destination.length() == 42 &&
515 IsHex(destination.substr(2,40)) &&
516 !uint160(ParseHex(destination.substr(2,64))).IsNull());
517}
518
519CTransferDestination CEthGateway::ToTransferDestination(const std::string &destination) const
520{
521 // just returns true if it looks like a non-NULL ETH address
522 uint160 retVal;
523 if (destination.substr(0,2) == "0x" &&
524 destination.length() == 42 &&
525 IsHex(destination.substr(2,40)) &&
526 !(retVal = uint160(ParseHex(destination.substr(2,64)))).IsNull())
527 {
528 return CTransferDestination(CTransferDestination::FLAG_DEST_GATEWAY + CTransferDestination::DEST_RAW,
529 std::vector<unsigned char>(retVal.begin(), retVal.end()));
530 }
531 return CTransferDestination();
532}
533
534// hard coded ETH gateway currency, "veth" for Verus chain. should be updated to work with PBaaS chains
535std::set<uint160> CEthGateway::FeeCurrencies() const
536{
537 std::set<uint160> retVal;
538 retVal.insert(CCrossChainRPCData::GetID("veth@"));
539 return retVal;
540}
541
542const CCurrencyDefinition &CEthGateway::GetConverter() const
543{
544 // just returns true if it looks like a non-NULL ETH address
545 static CCurrencyDefinition ethFeeConverter;
546 if (!ethFeeConverter.IsValid())
547 {
548 GetCurrencyDefinition("vrsc-eth-dai", ethFeeConverter);
549 }
550 return ethFeeConverter;
551}
552
b2a98c42
MT
553bool CConnectedChains::RemoveMergedBlock(uint160 chainID)
554{
2fd1f0fb 555 bool retval = false;
b2a98c42 556 LOCK(cs_mergemining);
23d61f0a 557
3016973b 558 //printf("RemoveMergedBlock ID: %s\n", chainID.GetHex().c_str());
23d61f0a 559
b2a98c42
MT
560 auto chainIt = mergeMinedChains.find(chainID);
561 if (chainIt != mergeMinedChains.end())
562 {
563 arith_uint256 target;
564 target.SetCompact(chainIt->second.block.nBits);
565 for (auto removeRange = mergeMinedTargets.equal_range(target); removeRange.first != removeRange.second; removeRange.first++)
566 {
567 // make sure we don't just match by target
56fe75cb 568 if (removeRange.first->second->GetID() == chainID)
b2a98c42
MT
569 {
570 mergeMinedTargets.erase(removeRange.first);
571 break;
572 }
573 }
574 mergeMinedChains.erase(chainID);
2fd1f0fb 575 dirty = retval = true;
b2a98c42
MT
576
577 // if we get to 0, give the thread a kick to stop waiting for mining
2fd1f0fb 578 //if (!mergeMinedChains.size())
579 //{
580 // sem_submitthread.post();
581 //}
b2a98c42 582 }
2fd1f0fb 583 return retval;
b2a98c42
MT
584}
585
586// remove merge mined chains added and not updated since a specific time
9ec65f90 587void CConnectedChains::PruneOldChains(uint32_t pruneBefore)
b2a98c42
MT
588{
589 vector<uint160> toRemove;
590
591 LOCK(cs_mergemining);
592 for (auto blkData : mergeMinedChains)
593 {
594 if (blkData.second.block.nTime < pruneBefore)
595 {
596 toRemove.push_back(blkData.first);
597 }
598 }
599
600 for (auto id : toRemove)
601 {
3016973b 602 //printf("Pruning chainID: %s\n", id.GetHex().c_str());
b2a98c42
MT
603 RemoveMergedBlock(id);
604 }
605}
606
607// adds or updates merge mined blocks
608// returns false if failed to add
609bool CConnectedChains::AddMergedBlock(CPBaaSMergeMinedChainData &blkData)
610{
b2a98c42
MT
611 // determine if we should replace one or add to the merge mine vector
612 {
613 LOCK(cs_mergemining);
614
2fd1f0fb 615 arith_uint256 target;
56fe75cb 616 uint160 cID = blkData.GetID();
b2a98c42
MT
617 auto it = mergeMinedChains.find(cID);
618 if (it != mergeMinedChains.end())
619 {
1fa4454d 620 RemoveMergedBlock(cID); // remove it if already there
b2a98c42 621 }
1fa4454d 622 target.SetCompact(blkData.block.nBits);
23d61f0a 623
3016973b 624 //printf("AddMergedBlock name: %s, ID: %s\n", blkData.chainDefinition.name.c_str(), cID.GetHex().c_str());
23d61f0a 625
1fa4454d 626 mergeMinedTargets.insert(make_pair(target, &(mergeMinedChains.insert(make_pair(cID, blkData)).first->second)));
2830db29 627 dirty = true;
b2a98c42 628 }
bce52b27 629 return true;
b2a98c42
MT
630}
631
c8c684e9 632bool CInputDescriptor::operator<(const CInputDescriptor &op) const
633{
634 arith_uint256 left = UintToArith256(txIn.prevout.hash);
635 arith_uint256 right = UintToArith256(op.txIn.prevout.hash);
636 return left < right ? true : left > right ? false : txIn.prevout.n < op.txIn.prevout.n ? true : false;
637}
638
639
b2a98c42
MT
640bool CConnectedChains::GetChainInfo(uint160 chainID, CRPCChainData &rpcChainData)
641{
642 {
643 LOCK(cs_mergemining);
644 auto chainIt = mergeMinedChains.find(chainID);
645 if (chainIt != mergeMinedChains.end())
646 {
647 rpcChainData = (CRPCChainData)chainIt->second;
648 return true;
649 }
650 return false;
651 }
652}
653
654// this returns a pointer to the data without copy and assumes the lock is held
655CPBaaSMergeMinedChainData *CConnectedChains::GetChainInfo(uint160 chainID)
656{
657 {
658 auto chainIt = mergeMinedChains.find(chainID);
659 if (chainIt != mergeMinedChains.end())
660 {
661 return &chainIt->second;
662 }
663 return NULL;
664 }
665}
666
9ec65f90 667void CConnectedChains::QueueNewBlockHeader(CBlockHeader &bh)
e771a884 668{
3016973b 669 //printf("QueueNewBlockHeader %s\n", bh.GetHash().GetHex().c_str());
2830db29 670 {
671 LOCK(cs_mergemining);
cff3c5ad 672
2830db29 673 qualifiedHeaders[UintToArith256(bh.GetHash())] = bh;
674 }
e771a884 675 sem_submitthread.post();
676}
677
0ab273d2 678void CConnectedChains::CheckImports()
679{
680 sem_submitthread.post();
681}
682
f8f61a6d 683// get the latest block header and submit one block at a time, returning after there are no more
684// matching blocks to be found
685vector<pair<string, UniValue>> CConnectedChains::SubmitQualifiedBlocks()
b2a98c42
MT
686{
687 std::set<uint160> inHeader;
f8f61a6d 688 bool submissionFound;
689 CPBaaSMergeMinedChainData chainData;
b2a98c42
MT
690 vector<pair<string, UniValue>> results;
691
f8f61a6d 692 CBlockHeader bh;
693 arith_uint256 lastHash;
b2a98c42
MT
694 CPBaaSBlockHeader pbh;
695
02560af7 696 do
b2a98c42 697 {
02560af7 698 submissionFound = false;
b2a98c42 699 {
02560af7 700 LOCK(cs_mergemining);
701 // attempt to submit with the lowest hash answers first to increase the likelihood of submitting
702 // common, merge mined headers for notarization, drop out on any submission
703 for (auto headerIt = qualifiedHeaders.begin(); !submissionFound && headerIt != qualifiedHeaders.end(); headerIt = qualifiedHeaders.begin())
b2a98c42 704 {
02560af7 705 // add the PBaaS chain ids from this header to a set for search
706 for (uint32_t i = 0; headerIt->second.GetPBaaSHeader(pbh, i); i++)
f8f61a6d 707 {
02560af7 708 inHeader.insert(pbh.chainID);
709 }
b2a98c42 710
33d6f38a 711 uint160 chainID;
02560af7 712 // now look through all targets that are equal to or above the hash of this header
713 for (auto chainIt = mergeMinedTargets.lower_bound(headerIt->first); !submissionFound && chainIt != mergeMinedTargets.end(); chainIt++)
714 {
56fe75cb 715 chainID = chainIt->second->GetID();
02560af7 716 if (inHeader.count(chainID))
2830db29 717 {
02560af7 718 // first, check that the winning header matches the block that is there
719 CPBaaSPreHeader preHeader(chainIt->second->block);
720 preHeader.SetBlockData(headerIt->second);
721
722 // check if the block header matches the block's specific data, only then can we create a submission from this block
723 if (headerIt->second.CheckNonCanonicalData(chainID))
f8f61a6d 724 {
02560af7 725 // save block as is, remove the block from merged headers, replace header, and submit
726 chainData = *chainIt->second;
727
728 *(CBlockHeader *)&chainData.block = headerIt->second;
729
02560af7 730 submissionFound = true;
f8f61a6d 731 }
3016973b
MT
732 //else // not an error condition. code is here for debugging
733 //{
734 // printf("Mismatch in non-canonical data for chain %s\n", chainIt->second->chainDefinition.name.c_str());
735 //}
cff3c5ad 736 }
3016973b
MT
737 //else // not an error condition. code is here for debugging
738 //{
739 // printf("Not found in header %s\n", chainIt->second->chainDefinition.name.c_str());
740 //}
02560af7 741 }
2830db29 742
02560af7 743 // if this header matched no block, discard and move to the next, otherwise, we'll drop through
33d6f38a
MT
744 if (submissionFound)
745 {
746 // once it is going to be submitted, remove block from this chain until a new one is added again
747 RemoveMergedBlock(chainID);
748 break;
749 }
750 else
02560af7 751 {
752 qualifiedHeaders.erase(headerIt);
b2a98c42 753 }
f8f61a6d 754 }
02560af7 755 }
756 if (submissionFound)
757 {
758 // submit one block and loop again. this approach allows multiple threads
759 // to collectively empty the submission queue, mitigating the impact of
760 // any one stalled daemon
761 UniValue submitParams(UniValue::VARR);
762 submitParams.push_back(EncodeHexBlk(chainData.block));
763 UniValue result, error;
764 try
f8f61a6d 765 {
02560af7 766 result = RPCCall("submitblock", submitParams, chainData.rpcUserPass, chainData.rpcPort, chainData.rpcHost);
767 result = find_value(result, "result");
768 error = find_value(result, "error");
b2a98c42 769 }
02560af7 770 catch (exception e)
771 {
772 result = UniValue(e.what());
773 }
774 results.push_back(make_pair(chainData.chainDefinition.name, result));
775 if (result.isStr() || !error.isNull())
776 {
777 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());
778 }
779 else
780 {
781 printf("Successfully submitted block to %s chain\n", chainData.chainDefinition.name.c_str());
782 }
783 }
784 } while (submissionFound);
b2a98c42
MT
785 return results;
786}
787
f8f61a6d 788// add all merge mined chain PBaaS headers into the blockheader and return the easiest nBits target in the header
b2a98c42
MT
789uint32_t CConnectedChains::CombineBlocks(CBlockHeader &bh)
790{
791 vector<uint160> inHeader;
792 vector<UniValue> toCombine;
793 arith_uint256 blkHash = UintToArith256(bh.GetHash());
f8f61a6d 794 arith_uint256 target(0);
b2a98c42
MT
795
796 CPBaaSBlockHeader pbh;
797
b2a98c42 798 {
f8f61a6d 799 LOCK(cs_mergemining);
b2a98c42 800
f8f61a6d 801 CPBaaSSolutionDescriptor descr = CVerusSolutionVector::solutionTools.GetDescriptor(bh.nSolution);
802
803 for (uint32_t i = 0; i < descr.numPBaaSHeaders; i++)
b2a98c42 804 {
f8f61a6d 805 if (bh.GetPBaaSHeader(pbh, i))
806 {
807 inHeader.push_back(pbh.chainID);
808 }
809 }
810
811 // loop through the existing PBaaS chain ids in the header
2fd1f0fb 812 // remove any that are not either this Chain ID or in our local collection and then add all that are present
f8f61a6d 813 for (uint32_t i = 0; i < inHeader.size(); i++)
814 {
815 auto it = mergeMinedChains.find(inHeader[i]);
816 if (inHeader[i] != ASSETCHAINS_CHAINID && (it == mergeMinedChains.end()))
817 {
818 bh.DeletePBaaSHeader(i);
819 }
b2a98c42 820 }
b2a98c42 821
b2a98c42
MT
822 for (auto chain : mergeMinedChains)
823 {
824 // get the native PBaaS header for each chain and put it into the
825 // header we are given
326f5f88 826 // it must have itself in as a PBaaS header
56fe75cb 827 uint160 cid = chain.second.GetID();
3a2a1777 828 if (chain.second.block.GetPBaaSHeader(pbh, cid) != -1)
b2a98c42
MT
829 {
830 if (!bh.AddUpdatePBaaSHeader(pbh))
831 {
832 LogPrintf("Failure to add PBaaS block header for %s chain\n", chain.second.chainDefinition.name.c_str());
f8f61a6d 833 break;
834 }
835 else
836 {
837 arith_uint256 t;
838 t.SetCompact(chain.second.block.nBits);
839 if (t > target)
840 {
841 target = t;
842 }
b2a98c42
MT
843 }
844 }
326f5f88
MT
845 else
846 {
847 LogPrintf("Merge mined block for %s does not contain PBaaS information\n", chain.second.chainDefinition.name.c_str());
848 }
b2a98c42 849 }
2830db29 850 dirty = false;
b2a98c42 851 }
326f5f88 852
f8f61a6d 853 return target.GetCompact();
b2a98c42
MT
854}
855
ccac80a3 856bool CConnectedChains::IsVerusPBaaSAvailable()
857{
c8c684e9 858 return IsNotaryAvailable() && FirstNotaryChain().chainDefinition.GetID() == VERUS_CHAINID;
859}
860
861bool CConnectedChains::IsNotaryAvailable()
862{
863 return !(FirstNotaryChain().rpcHost.empty() || FirstNotaryChain().rpcPort == 0 || FirstNotaryChain().rpcUserPass.empty());
ccac80a3 864}
865
f8f61a6d 866extern string PBAAS_HOST, PBAAS_USERPASS;
867extern int32_t PBAAS_PORT;
6bb9fdbc 868bool CConnectedChains::CheckVerusPBaaSAvailable(UniValue &chainInfoUni, UniValue &chainDefUni)
9f0c14b2 869{
6bb9fdbc 870 if (chainInfoUni.isObject() && chainDefUni.isObject())
9f0c14b2 871 {
6bb9fdbc 872 UniValue uniVer = find_value(chainInfoUni, "VRSCversion");
f8f61a6d 873 if (uniVer.isStr())
874 {
2156d3d0 875 LOCK(cs_mergemining);
56fe75cb 876 CCurrencyDefinition chainDef(chainDefUni);
c8c684e9 877 if (chainDef.IsValid())
878 {
879 if (!notarySystems.count(chainDef.GetID()))
880 {
881 notarySystems[chainDef.GetID()] = CNotarySystemInfo(uni_get_int(find_value(chainInfoUni, "blocks")),
882 CRPCChainData(chainDef, PBAAS_HOST, PBAAS_PORT, PBAAS_USERPASS),
883 CCurrencyDefinition());
884 }
885 }
f8f61a6d 886 }
9f0c14b2 887 }
ccac80a3 888 return IsVerusPBaaSAvailable();
9f0c14b2
MT
889}
890
56fe75cb 891uint32_t CConnectedChains::NotaryChainHeight()
892{
893 LOCK(cs_mergemining);
c8c684e9 894 return notarySystems.size() ? notarySystems.begin()->second.height : 0;
56fe75cb 895}
896
2299bd95 897bool CConnectedChains::CheckVerusPBaaSAvailable()
9f0c14b2 898{
c8c684e9 899 if (!IsVerusActive())
9f0c14b2
MT
900 {
901 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
2156d3d0 902 // tolerate only 15 second timeout
9a891caf 903 UniValue chainInfo, chainDef;
904 try
905 {
906 UniValue params(UniValue::VARR);
2fd1f0fb 907 chainInfo = find_value(RPCCallRoot("getinfo", params), "result");
9a891caf 908 if (!chainInfo.isNull())
909 {
910 params.push_back(VERUS_CHAINNAME);
c8c677c9 911 chainDef = find_value(RPCCallRoot("getcurrency", params), "result");
9a891caf 912
913 if (!chainDef.isNull() && CheckVerusPBaaSAvailable(chainInfo, chainDef))
914 {
56fe75cb 915 // if we have not past block 1 yet, store the best known update of our current state
916 if ((!chainActive.LastTip() || !chainActive.LastTip()->GetHeight()))
917 {
918 bool success = false;
919 params.clear();
920 params.push_back(EncodeDestination(CIdentityID(thisChain.GetID())));
c8c677c9 921 chainDef = find_value(RPCCallRoot("getcurrency", params), "result");
56fe75cb 922 if (!chainDef.isNull())
923 {
924 CCurrencyDefinition currencyDef(chainDef);
925 if (currencyDef.IsValid())
926 {
927 thisChain = currencyDef;
928 if (NotaryChainHeight() >= thisChain.startBlock)
929 {
930 readyToStart = true; // this only gates mining of block one, to be sure we have the latest definition
931 }
932 success = true;
933 }
934 }
935 return success;
936 }
9a891caf 937 return true;
938 }
939 }
940 } catch (exception e)
4fa3b13d 941 {
56fe75cb 942 LogPrintf("%s: Error communicating with %s chain\n", __func__, VERUS_CHAINNAME);
4fa3b13d 943 }
9f0c14b2 944 }
4fa3b13d 945 return false;
9f0c14b2
MT
946}
947
56fe75cb 948int CConnectedChains::GetThisChainPort() const
949{
950 int port;
951 string host;
952 for (auto node : defaultPeerNodes)
953 {
954 SplitHostPort(node.networkAddress, port, host);
955 if (port)
956 {
957 return port;
958 }
959 }
960 return 0;
961}
962
19ce962e 963CCoinbaseCurrencyState CConnectedChains::AddPrelaunchConversions(CCurrencyDefinition &curDef,
964 const CCoinbaseCurrencyState &_currencyState,
965 int32_t fromHeight,
966 int32_t height,
967 int32_t curDefHeight)
968{
969 CCoinbaseCurrencyState currencyState = _currencyState;
970 bool firstUpdate = fromHeight <= curDefHeight;
971 if (firstUpdate)
972 {
973 if (curDef.IsFractional())
974 {
975 currencyState.supply = curDef.initialFractionalSupply;
976 currencyState.reserves = std::vector<int64_t>(currencyState.reserves.size(), 0);
977 currencyState.weights = curDef.weights;
978 }
979 else
980 {
981 // supply is determined by purchases * current conversion rate
982 currencyState.supply = currencyState.initialSupply;
983 }
984 }
985
986 // get chain transfers that should apply before the start block
987 // until there is a post-start block notarization, we always consider the
988 // currency state to be up to just before the start block
989 std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> unspentTransfers;
990 std::map<uint160, int32_t> currencyIndexes = currencyState.GetReserveMap();
26e2ca05 991 int32_t nativeIdx = currencyIndexes.count(curDef.systemID) ? currencyIndexes[curDef.systemID] : -1;
992
19ce962e 993 if (GetChainTransfers(unspentTransfers, curDef.GetID(), fromHeight, height < curDef.startBlock ? height : curDef.startBlock - 1))
994 {
995 currencyState.ClearForNextBlock();
996
997 for (auto &transfer : unspentTransfers)
998 {
481e7fd6 999 if (transfer.second.second.IsPreConversion())
19ce962e 1000 {
c8c684e9 1001 CAmount conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.FirstValue());
1002 CAmount valueIn = transfer.second.second.FirstValue() - conversionFee;
1003 int32_t curIdx = currencyIndexes[transfer.second.second.FirstCurrency()];
19ce962e 1004
481e7fd6 1005 currencyState.reserveIn[curIdx] += valueIn;
1006 curDef.preconverted[curIdx] += valueIn;
19ce962e 1007 if (curDef.IsFractional())
1008 {
481e7fd6 1009 currencyState.reserves[curIdx] += valueIn;
19ce962e 1010 }
1011 else
1012 {
481e7fd6 1013 currencyState.supply += CCurrencyState::ReserveToNativeRaw(valueIn, currencyState.PriceInReserve(curIdx));
19ce962e 1014 }
1015
c8c684e9 1016 if (transfer.second.second.FirstCurrency() == curDef.systemID)
19ce962e 1017 {
1018 currencyState.nativeConversionFees += conversionFee;
cd55a0f4 1019 currencyState.nativeFees += conversionFee;
19ce962e 1020 }
74df3b85 1021 currencyState.fees[curIdx] += conversionFee;
af959d1d 1022 currencyState.nativeFees += transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
9ac17ff2 1023 currencyState.fees[nativeIdx] += transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
481e7fd6 1024 currencyState.conversionFees[curIdx] += conversionFee;
19ce962e 1025 }
1026 }
1027 }
1028
481e7fd6 1029 if (curDef.IsFractional())
19ce962e 1030 {
481e7fd6 1031 // convert all non-native fees to native and update the price as a result
1032 bool isFeeConversion = false;
26e2ca05 1033 int numCurrencies = curDef.currencies.size();
1034 std::vector<int64_t> reservesToConvert(numCurrencies, 0);
1035 std::vector<int64_t> fractionalToConvert(numCurrencies, 0);
cb6830fe 1036 std::vector<int64_t> reserveAdjustments(numCurrencies, 0);
481e7fd6 1037
26e2ca05 1038 for (int i = 0; i < numCurrencies; i++)
481e7fd6 1039 {
9ac17ff2 1040 curDef.conversions[i] = currencyState.conversionPrice[i] = currencyState.PriceInReserve(i, true);
1041
481e7fd6 1042 // all currencies except the native currency of the system will be converted to the native currency
5a775c1b 1043 if (currencyState.fees[i] && curDef.currencies[i] != curDef.systemID)
481e7fd6 1044 {
9ac17ff2 1045 fractionalToConvert[nativeIdx] += currencyState.ReserveToNativeRaw(currencyState.fees[i], currencyState.conversionPrice[i]);
cb6830fe 1046 reserveAdjustments[i] += currencyState.fees[i];
26e2ca05 1047 isFeeConversion = true;
481e7fd6 1048 }
1049 }
1050
1051 // convert all non-native fee currencies to native and adjust prices
1052 if (isFeeConversion)
1053 {
cb6830fe 1054 for (int i = 0; i < numCurrencies; i++)
1055 {
1056 if (reserveAdjustments[i])
1057 {
1058 currencyState.reserveIn[i] += reserveAdjustments[i];
1059 currencyState.reserves[i] += reserveAdjustments[i];
1060 }
1061 }
1062 currencyState.supply += fractionalToConvert[nativeIdx];
1063
481e7fd6 1064 CCurrencyState converterState = static_cast<CCurrencyState>(currencyState);
cb6830fe 1065 currencyState.viaConversionPrice = converterState.ConvertAmounts(reservesToConvert, fractionalToConvert, currencyState);
1066
1067 for (int j = 0; j < numCurrencies; j++)
1068 {
1069 currencyState.reserves[j] = converterState.reserves[j];
1070 }
1071 currencyState.supply = converterState.supply - fractionalToConvert[nativeIdx];
1072
1073 // to ensure no rounding errors, use the resulting native price to convert the fee fractional to native,
1074 // subtract from supply, and subtract from native reserves
1075 CAmount nativeFeeOutput = currencyState.NativeToReserveRaw(fractionalToConvert[nativeIdx], currencyState.viaConversionPrice[nativeIdx]);
1076 currencyState.nativeConversionFees += nativeFeeOutput;
1077 currencyState.nativeFees += nativeFeeOutput;
1078 currencyState.reserves[nativeIdx] -= nativeFeeOutput;
1079 currencyState.reserveOut[nativeIdx] += nativeFeeOutput;
481e7fd6 1080 }
19ce962e 1081 }
1082
1083 // set initial supply from actual conversions if this is first update
1084 if (firstUpdate && curDef.IsFractional())
1085 {
77da5a22 1086 CAmount calculatedSupply = 0;
19ce962e 1087 for (auto &transfer : unspentTransfers)
1088 {
481e7fd6 1089 if (transfer.second.second.IsPreConversion())
19ce962e 1090 {
c8c684e9 1091 CAmount toConvert = transfer.second.second.FirstValue() - CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.FirstValue());
1092 calculatedSupply += CCurrencyState::ReserveToNativeRaw(toConvert, currencyState.conversionPrice[currencyIndexes[transfer.second.second.FirstCurrency()]]);
19ce962e 1093 }
1094 }
77da5a22 1095
1096 if (calculatedSupply > curDef.initialFractionalSupply)
1097 {
1098 // get a ratio and reduce all prices by that ratio
1099 static arith_uint256 bigSatoshi(SATOSHIDEN);
26e2ca05 1100 arith_uint256 bigMultipliedSupply(calculatedSupply * bigSatoshi);
1101 arith_uint256 newRatio(bigMultipliedSupply / curDef.initialFractionalSupply);
1102 // truncate up, not down, to prevent any overflow at all
1103 if (newRatio * curDef.initialFractionalSupply < bigMultipliedSupply)
1104 {
1105 newRatio++;
1106 }
77da5a22 1107 for (auto &rate : currencyState.conversionPrice)
1108 {
1109 arith_uint256 numerator = rate * newRatio;
1110 rate = (numerator / bigSatoshi).GetLow64();
38c7f46e 1111 if ((numerator - (bigSatoshi * rate)) > 0)
1112 {
1113 rate++;
1114 }
77da5a22 1115 }
d3c97fd3 1116 }
19ce962e 1117
92856bb2 1118 /* calculatedSupply = 0;
818494a0 1119 for (auto &transfer : unspentTransfers)
1120 {
1121 if (transfer.second.second.IsPreConversion())
1122 {
1123 CAmount toConvert = transfer.second.second.nValue - CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.nValue);
1124 calculatedSupply += CCurrencyState::ReserveToNativeRaw(toConvert, currencyState.conversionPrice[currencyIndexes[transfer.second.second.currencyID]]);
1125 }
1126 }
92856bb2 1127 printf("Calculated supply %s\n", ValueFromAmount(calculatedSupply).write().c_str()); */
818494a0 1128
19ce962e 1129 // now, remove carveout percentage from each weight & reserve
1130 // for currency state
1131 int32_t preLaunchCarveOutTotal = 0;
1132 for (auto &carveout : curDef.preLaunchCarveOuts)
1133 {
1134 preLaunchCarveOutTotal += carveout.second;
1135
1136 }
1137 static arith_uint256 bigSatoshi(SATOSHIDEN);
1138 for (auto &oneReserve : currencyState.reserves)
1139 {
1140 oneReserve = ((arith_uint256(oneReserve) * arith_uint256(SATOSHIDEN - preLaunchCarveOutTotal)) / bigSatoshi).GetLow64();
1141 }
1142 for (auto &oneWeight : currencyState.weights)
1143 {
1144 oneWeight = ((arith_uint256(oneWeight) * arith_uint256(CCurrencyDefinition::CalculateRatioOfValue((SATOSHIDEN - preLaunchCarveOutTotal), SATOSHIDEN - curDef.preLaunchDiscount))) / bigSatoshi).GetLow64();
1145 }
19ce962e 1146 }
1147
1148 return currencyState;
1149}
1150
ea574fe2 1151CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(CCurrencyDefinition &curDef, int32_t height, int32_t curDefHeight)
1fb6db72 1152{
1153 uint160 chainID = curDef.GetID();
1154 CCoinbaseCurrencyState currencyState;
ea574fe2 1155 std::vector<CAddressIndexDbEntry> notarizationIndex;
1fb6db72 1156
1157 if (chainID == ASSETCHAINS_CHAINID)
1158 {
c8c684e9 1159 currencyState = GetInitialCurrencyState(thisChain);
1fb6db72 1160 }
1161 // if this is a token on this chain, it will be simply notarized
f1a298ef 1162 else if (curDef.systemID == ASSETCHAINS_CHAINID || (curDef.launchSystemID == ASSETCHAINS_CHAINID && curDef.startBlock > height))
1fb6db72 1163 {
82c6e57e 1164 // get the last notarization in the height range for this currency, which is valid by definition for a token
1fb6db72 1165 CPBaaSNotarization notarization;
2f5978fe 1166 notarization.GetLastNotarization(chainID, EVAL_ACCEPTEDNOTARIZATION, curDefHeight, height);
1167 currencyState = notarization.currencyState;
1168 if (!currencyState.IsValid())
1fb6db72 1169 {
2f5978fe 1170 if (notarization.IsValid() && notarization.currencyStates.count(chainID))
1fb6db72 1171 {
2f5978fe 1172 currencyState = notarization.currencyStates[chainID];
1173 }
1174 else
1175 {
1176 currencyState = GetInitialCurrencyState(curDef);
f1a298ef 1177 currencyState.SetPrelaunch();
19ce962e 1178 }
1fb6db72 1179 }
f1a298ef 1180 if (currencyState.IsValid() && notarization.notarizationHeight < (curDef.startBlock - 1))
2f5978fe 1181 {
1182 // pre-launch
1183 currencyState.SetPrelaunch(true);
1184 currencyState = AddPrelaunchConversions(curDef,
1185 currencyState,
1186 notarization.IsValid() ? notarization.notarizationHeight : curDefHeight,
1187 height,
1188 curDefHeight);
1189 }
1fb6db72 1190 }
1191 else
1192 {
c8c684e9 1193 // we need to get the currency state of a currency not on this chain, so we look for the latest confirmed notarization
1194 // of that currency, and get it there
1fb6db72 1195 CChainNotarizationData cnd;
c8c684e9 1196 if (GetNotarizationData(chainID, cnd))
1fb6db72 1197 {
ea574fe2 1198 int32_t transfersFrom = curDefHeight;
1199 if (cnd.lastConfirmed != -1)
1200 {
1201 transfersFrom = cnd.vtx[cnd.lastConfirmed].second.notarizationHeight;
1202 }
1203 int32_t transfersUntil = cnd.lastConfirmed == -1 ? curDef.startBlock - 1 :
1204 (cnd.vtx[cnd.lastConfirmed].second.notarizationHeight < curDef.startBlock ?
1205 (height < curDef.startBlock ? height : curDef.startBlock - 1) :
1206 cnd.vtx[cnd.lastConfirmed].second.notarizationHeight);
1207 if (transfersUntil < curDef.startBlock)
1208 {
1209 // get chain transfers that should apply before the start block
1210 // until there is a post-start block notarization, we always consider the
1211 // currency state to be up to just before the start block
1212 std::multimap<uint160, std::pair<CInputDescriptor, CReserveTransfer>> unspentTransfers;
1213 if (GetChainTransfers(unspentTransfers, chainID, transfersFrom, transfersUntil))
1214 {
1215 // at this point, all pre-allocation, minted, and pre-converted currency are included
1216 // in the currency state before final notarization
1217 std::map<uint160, int32_t> currencyIndexes = currencyState.GetReserveMap();
1218 if (curDef.IsFractional())
1219 {
1220 currencyState.supply = curDef.initialFractionalSupply;
1221 }
1222 else
1223 {
1224 // supply is determined by purchases * current conversion rate
1225 currencyState.supply = currencyState.initialSupply;
1226 }
1227
1228 for (auto &transfer : unspentTransfers)
1229 {
481e7fd6 1230 if (transfer.second.second.IsPreConversion())
ea574fe2 1231 {
c8c684e9 1232 CAmount conversionFee = CReserveTransactionDescriptor::CalculateConversionFee(transfer.second.second.FirstValue());
ea574fe2 1233
c8c684e9 1234 currencyState.reserveIn[currencyIndexes[transfer.second.second.FirstCurrency()]] += transfer.second.second.FirstValue();
1235 curDef.preconverted[currencyIndexes[transfer.second.second.FirstCurrency()]] += transfer.second.second.FirstValue();
ea574fe2 1236 if (curDef.IsFractional())
1237 {
c8c684e9 1238 currencyState.reserves[currencyIndexes[transfer.second.second.FirstCurrency()]] += transfer.second.second.FirstValue() - conversionFee;
ea574fe2 1239 }
1240 else
1241 {
c8c684e9 1242 currencyState.supply += CCurrencyState::ReserveToNativeRaw(transfer.second.second.FirstValue() - conversionFee, currencyState.PriceInReserve(currencyIndexes[transfer.second.second.FirstCurrency()]));
ea574fe2 1243 }
1244
c8c684e9 1245 if (transfer.second.second.FirstCurrency() == curDef.systemID)
ea574fe2 1246 {
1247 currencyState.nativeConversionFees += conversionFee;
1248 currencyState.nativeFees += conversionFee + transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
1249 }
1250 else
1251 {
c8c684e9 1252 currencyState.fees[currencyIndexes[transfer.second.second.FirstCurrency()]] +=
ea574fe2 1253 conversionFee + transfer.second.second.CalculateTransferFee(transfer.second.second.destination);
c8c684e9 1254 currencyState.conversionFees[currencyIndexes[transfer.second.second.FirstCurrency()]] += conversionFee;
ea574fe2 1255 }
1256 }
1257 else if (transfer.second.second.flags & CReserveTransfer::PREALLOCATE)
1258 {
c8c684e9 1259 currencyState.emitted += transfer.second.second.FirstValue();
ea574fe2 1260 }
1261 }
1262 currencyState.supply += currencyState.emitted;
1263 if (curDef.conversions.size() != curDef.currencies.size())
1264 {
1265 curDef.conversions = std::vector<int64_t>(curDef.currencies.size());
1266 }
1267 for (int i = 0; i < curDef.conversions.size(); i++)
1268 {
1269 currencyState.conversionPrice[i] = curDef.conversions[i] = currencyState.PriceInReserve(i);
1270 }
1271 }
1272 }
1273 else
1274 {
c8c684e9 1275 std::pair<CUTXORef, CPBaaSNotarization> notPair = cnd.lastConfirmed != -1 ? cnd.vtx[cnd.lastConfirmed] : cnd.vtx[cnd.forks[cnd.bestChain][0]];
ea574fe2 1276 currencyState = notPair.second.currencyState;
1277 }
1fb6db72 1278 }
1279 }
1280 return currencyState;
1281}
1282
ea574fe2 1283CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(const uint160 &currencyID, int32_t height)
1284{
1285 int32_t curDefHeight;
1286 CCurrencyDefinition curDef;
22ce8448 1287 if (GetCurrencyDefinition(currencyID, curDef, &curDefHeight, true))
ea574fe2 1288 {
1289 return GetCurrencyState(curDef, height, curDefHeight);
1290 }
1291 else
1292 {
1293 LogPrintf("%s: currency %s:%s not found\n", __func__, currencyID.GetHex().c_str(), EncodeDestination(CIdentityID(currencyID)).c_str());
1294 printf("%s: currency %s:%s not found\n", __func__, currencyID.GetHex().c_str(), EncodeDestination(CIdentityID(currencyID)).c_str());
1295 }
1296 return CCoinbaseCurrencyState();
1297}
1298
1299CCoinbaseCurrencyState CConnectedChains::GetCurrencyState(int32_t height)
1300{
1301 return GetCurrencyState(thisChain.GetID(), height);
1302}
1303
855714b0 1304bool CConnectedChains::SetLatestMiningOutputs(const std::vector<CTxOut> &minerOutputs)
c3250dcd
MT
1305{
1306 LOCK(cs_mergemining);
c3250dcd 1307 latestMiningOutputs = minerOutputs;
bb6c3482 1308 return true;
c3250dcd
MT
1309}
1310
7beb1c0b 1311CCurrencyDefinition CConnectedChains::GetCachedCurrency(const uint160 &currencyID)
09551a1c 1312{
09551a1c 1313 CCurrencyDefinition currencyDef;
22ce8448 1314 int32_t defHeight;
09551a1c 1315 auto it = currencyDefCache.find(currencyID);
1316 if ((it != currencyDefCache.end() && !(currencyDef = it->second).IsValid()) ||
22ce8448 1317 (it == currencyDefCache.end() && !GetCurrencyDefinition(currencyID, currencyDef, &defHeight, true)))
09551a1c 1318 {
1319 printf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(currencyID)).c_str());
1320 LogPrintf("%s: definition for transfer currency ID %s not found\n\n", __func__, EncodeDestination(CIdentityID(currencyID)).c_str());
7beb1c0b 1321 return currencyDef;
09551a1c 1322 }
1323 if (it == currencyDefCache.end())
1324 {
1325 currencyDefCache[currencyID] = currencyDef;
1326 }
1327 return currencyDefCache[currencyID];
1328}
1329
1fb6db72 1330CCurrencyDefinition CConnectedChains::UpdateCachedCurrency(const uint160 &currencyID, uint32_t height)
1331{
1332 // due to the main lock being taken on the thread that waits for transaction checks,
1333 // low level functions like this must be called either from a thread that holds LOCK(cs_main),
1334 // or script validation, where it is held either by this thread or one waiting for it.
1335 // in the long run, the daemon synchonrization model should be improved
1336 CCurrencyDefinition currencyDef = GetCachedCurrency(currencyID);
1337 CCoinbaseCurrencyState curState = GetCurrencyState(currencyDef, height);
1338 currencyDefCache[currencyID] = currencyDef;
1339 return currencyDef;
1340}
1341
ec1b7e23 1342
c8c684e9 1343// returns all unspent chain exports for a specific chain/system, indexed by the actuall currency destination
1344bool CConnectedChains::GetUnspentSystemExports(const uint160 systemID, multimap<uint160, pair<int, CInputDescriptor>> &exportOutputs)
1345{
1346 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue>> unspentOutputs;
0ff96ff6 1347
c8c684e9 1348 LOCK2(cs_main, mempool.cs);
0ff96ff6 1349
c8c684e9 1350 if (!GetAddressUnspent(CCrossChainRPCData::GetConditionID(systemID, CCrossChainExport::SystemExportKey()), CScript::P2IDX, unspentOutputs))
0ff96ff6 1351 {
c8c684e9 1352 return false;
1353 }
1354 else
1355 {
1356 CCoinsViewCache view(pcoinsTip);
1357
1358 for (auto it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
0ff96ff6 1359 {
c8c684e9 1360 CCoins coins;
1361
1362 if (view.GetCoins(it->first.txhash, coins))
0ff96ff6 1363 {
c8c684e9 1364 int i = it->first.index;
1365 if (coins.IsAvailable(i))
1366 {
1367 // if this is an export output, optionally to this chain, add it to the input vector
1368 COptCCParams p;
1369 CCrossChainExport cx;
1370 if (coins.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_CROSSCHAIN_EXPORT &&
1371 p.vData.size() && (cx = CCrossChainExport(p.vData[0])).IsValid())
1372 {
1373 exportOutputs.insert(make_pair(cx.destCurrencyID,
1374 make_pair(coins.nHeight, CInputDescriptor(coins.vout[i].scriptPubKey, coins.vout[i].nValue, CTxIn(COutPoint(it->first.txhash, i))))));
1375 }
1376 }
1377 }
1378 else
1379 {
1380 printf("%s: cannot retrieve transaction %s\n", __func__, it->first.txhash.GetHex().c_str());
1381 return false;
0ff96ff6 1382 }
0ff96ff6 1383 }
c8c684e9 1384 return true;
0ff96ff6 1385 }
c8c684e9 1386}
0ff96ff6 1387
0ff96ff6 1388
c8c684e9 1389// returns all unspent chain exports for a specific chain/currency
1390bool CConnectedChains::GetUnspentCurrencyExports(const CCoinsViewCache &view,
1391 const uint160 currencyID,
1392 std::vector<pair<int, CInputDescriptor>> &exportOutputs)
1393{
1394 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue>> unspentOutputs;
1395 std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> exportUTXOs;
1396
1397 std::vector<pair<int, CInputDescriptor>> exportOuts;
1398
1399 LOCK2(cs_main, mempool.cs);
1400
1401 uint160 exportIndexKey = CCrossChainRPCData::GetConditionID(currencyID, CCrossChainExport::CurrencyExportKey());
1402
1403 if (mempool.getAddressIndex(std::vector<std::pair<uint160, int32_t>>({{exportIndexKey, CScript::P2IDX}}), exportUTXOs) &&
1404 exportUTXOs.size())
0ff96ff6 1405 {
c8c684e9 1406 // we need to remove those that are spent
1407 std::map<COutPoint, CInputDescriptor> memPoolOuts;
1408 for (auto &oneExport : exportUTXOs)
0ff96ff6 1409 {
c8c684e9 1410 if (oneExport.first.spending)
1411 {
1412 memPoolOuts.erase(COutPoint(oneExport.first.txhash, oneExport.first.index));
1413 }
1414 else
1415 {
1416 const CTransaction oneTx = mempool.mapTx.find(oneExport.first.txhash)->GetTx();
1417 memPoolOuts.insert(std::make_pair(COutPoint(oneExport.first.txhash, oneExport.first.index),
1418 CInputDescriptor(oneTx.vout[oneExport.first.index].scriptPubKey, oneExport.second.amount,
1419 CTxIn(oneExport.first.txhash, oneExport.first.index))));
1420 }
0ff96ff6 1421 }
c8c684e9 1422
1423 for (auto &oneUTXO : memPoolOuts)
0ff96ff6 1424 {
c8c684e9 1425 exportOuts.push_back(std::make_pair(0, oneUTXO.second));
0ff96ff6 1426 }
1427 }
c8c684e9 1428 if (!exportOuts.size() &&
0338564c 1429 !GetAddressUnspent(exportIndexKey, CScript::P2IDX, unspentOutputs))
0ff96ff6 1430 {
1431 return false;
1432 }
c8c684e9 1433 else
0ff96ff6 1434 {
c8c684e9 1435 for (auto it = unspentOutputs.begin(); it != unspentOutputs.end(); it++)
0ff96ff6 1436 {
c8c684e9 1437 exportOuts.push_back(std::make_pair(it->second.blockHeight, CInputDescriptor(it->second.script, it->second.satoshis,
1438 CTxIn(it->first.txhash, it->first.index))));
0ff96ff6 1439 }
0ff96ff6 1440 }
c8c684e9 1441 exportOutputs.insert(exportOutputs.end(), exportOuts.begin(), exportOuts.end());
1442 return exportOuts.size() != 0;
1443}
0ff96ff6 1444
0ff96ff6 1445
c8c684e9 1446bool CConnectedChains::GetPendingCurrencyExports(const uint160 currencyID,
1447 uint32_t fromHeight,
1448 std::vector<pair<int, CInputDescriptor>> &exportOutputs)
1449{
1450 CCurrencyDefinition chainDef;
1451 int32_t defHeight;
1452 exportOutputs.clear();
1453
1454 if (GetCurrencyDefinition(currencyID, chainDef, &defHeight))
0ff96ff6 1455 {
c8c684e9 1456 // which transaction are we in this block?
1457 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
0ff96ff6 1458
c8c684e9 1459 CChainNotarizationData cnd;
1460 if (GetNotarizationData(currencyID, cnd))
0ff96ff6 1461 {
c8c684e9 1462 uint160 exportKey = CCrossChainRPCData::GetConditionID(currencyID, CCrossChainExport::CurrencyExportKey());
1463
1464 // get all export transactions including and since this one up to the confirmed cross-notarization
1465 if (GetAddressIndex(exportKey, CScript::P2IDX, addressIndex, fromHeight))
0ff96ff6 1466 {
c8c684e9 1467 for (auto &idx : addressIndex)
1468 {
1469 uint256 blkHash;
1470 CTransaction exportTx;
1471 if (!idx.first.spending && myGetTransaction(idx.first.txhash, exportTx, blkHash))
1472 {
1473 std::vector<CBaseChainObject *> opretTransfers;
1474 CCrossChainExport ccx;
1475 if ((ccx = CCrossChainExport(exportTx.vout[idx.first.index].scriptPubKey)).IsValid())
1476 {
1477 exportOutputs.push_back(std::make_pair(idx.first.blockHeight,
1478 CInputDescriptor(exportTx.vout[idx.first.index].scriptPubKey,
1479 exportTx.vout[idx.first.index].nValue,
1480 CTxIn(idx.first.txhash, idx.first.index))));
1481 }
1482 }
1483 }
0ff96ff6 1484 }
1485 }
c8c684e9 1486 return true;
1487 }
1488 else
1489 {
1490 LogPrintf("%s: unrecognized chain name or chain ID\n", __func__);
1491 return false;
0ff96ff6 1492 }
c8c684e9 1493}
0ff96ff6 1494
0ff96ff6 1495
c8c684e9 1496CPartialTransactionProof::CPartialTransactionProof(const CTransaction tx, const std::vector<int32_t> outputNums, const CBlockIndex *pIndex, uint32_t proofAtHeight)
1497{
1498 // get map and MMR for transaction
1499 CTransactionMap txMap(tx);
1500 TransactionMMView txView(txMap.transactionMMR);
1501 uint256 txRoot = txView.GetRoot();
0ff96ff6 1502
c8c684e9 1503 std::vector<CTransactionComponentProof> txProofVec;
1504 txProofVec.push_back(CTransactionComponentProof(txView, txMap, tx, CTransactionHeader::TX_HEADER, 0));
1505 for (auto oneOutNum : outputNums)
0ff96ff6 1506 {
c8c684e9 1507 txProofVec.push_back(CTransactionComponentProof(txView, txMap, tx, CTransactionHeader::TX_OUTPUT, oneOutNum));
0ff96ff6 1508 }
1509
c8c684e9 1510 // now, both the header and stake output are dependent on the transaction MMR root being provable up
1511 // through the block MMR, and since we don't cache the new MMR proof for transactions yet, we need the block to create the proof.
1512 // when we switch to the new MMR in place of a merkle tree, we can keep that in the wallet as well
1513 CBlock block;
1514 if (!ReadBlockFromDisk(block, pIndex, Params().GetConsensus(), false))
0ff96ff6 1515 {
c8c684e9 1516 LogPrintf("%s: ERROR: could not read block number %u from disk\n", __func__, pIndex->GetHeight());
1517 version = VERSION_INVALID;
1518 return;
1519 }
0ff96ff6 1520
c8c684e9 1521 BlockMMRange blockMMR(block.GetBlockMMRTree());
1522 BlockMMView blockView(blockMMR);
0ff96ff6 1523
c8c684e9 1524 int txIndexPos;
1525 for (txIndexPos = 0; txIndexPos < blockMMR.size(); txIndexPos++)
1526 {
1527 uint256 txRootHashFromMMR = blockMMR[txIndexPos].hash;
1528 if (txRootHashFromMMR == txRoot)
0ff96ff6 1529 {
c8c684e9 1530 //printf("tx with root %s found in block\n", txRootHashFromMMR.GetHex().c_str());
1531 break;
0ff96ff6 1532 }
c8c684e9 1533 }
0ff96ff6 1534
c8c684e9 1535 if (txIndexPos == blockMMR.size())
1536 {
1537 LogPrintf("%s: ERROR: could not find transaction root in block %u\n", __func__, pIndex->GetHeight());
1538 version = VERSION_INVALID;
1539 return;
1540 }
0ff96ff6 1541
c8c684e9 1542 // prove the tx up to the MMR root, which also contains the block hash
1543 CMMRProof txRootProof;
1544 if (!blockView.GetProof(txRootProof, txIndexPos))
1545 {
1546 LogPrintf("%s: ERROR: could not create proof of source transaction in block %u\n", __func__, pIndex->GetHeight());
1547 version = VERSION_INVALID;
1548 return;
1549 }
0ff96ff6 1550
c8c684e9 1551 ChainMerkleMountainView mmv = chainActive.GetMMV();
1552 mmv.resize(proofAtHeight);
1553 chainActive.GetMerkleProof(mmv, txRootProof, pIndex->GetHeight());
1554 *this = CPartialTransactionProof(txRootProof, txProofVec);
1555}
0ff96ff6 1556
0ff96ff6 1557
c8c684e9 1558// given exports on this chain, provide the proofs of those export outputs with the MMR root at height "height"
1559// proofs are added in place
1560bool CConnectedChains::GetExportProofs(uint32_t height,
1561 std::vector<std::pair<std::pair<CInputDescriptor,CPartialTransactionProof>,std::vector<CReserveTransfer>>> &exports)
1562{
1563 // fill in proofs of the export outputs for each export at the specified height
1564 ChainMerkleMountainView mmv = chainActive.GetMMV();
1565 mmv.resize(height);
0ff96ff6 1566
c8c684e9 1567 CBlock proofBlock;
0ff96ff6 1568
c8c684e9 1569 for (auto &oneExport : exports)
1570 {
1571 uint256 blockHash;
1572 CTransaction exportTx;
1573 if (!myGetTransaction(oneExport.first.first.txIn.prevout.hash, exportTx, blockHash))
0ff96ff6 1574 {
c8c684e9 1575 LogPrintf("%s: unable to retrieve export %s\n", __func__, oneExport.first.first.txIn.prevout.hash.GetHex().c_str());
0ff96ff6 1576 return false;
1577 }
c8c684e9 1578 CCrossChainExport ccx(exportTx.vout[oneExport.first.first.txIn.prevout.n].scriptPubKey);
1579 if (!ccx.IsValid())
0ff96ff6 1580 {
c8c684e9 1581 LogPrintf("%s: invalid export on %s\n", __func__, oneExport.first.first.txIn.prevout.hash.GetHex().c_str());
0ff96ff6 1582 return false;
1583 }
c8c684e9 1584 if (blockHash.IsNull())
0ff96ff6 1585 {
c8c684e9 1586 LogPrintf("%s: cannot get proof for unconfirmed export %s\n", __func__, oneExport.first.first.txIn.prevout.hash.GetHex().c_str());
1587 return false;
0ff96ff6 1588 }
c8c684e9 1589 auto blockIt = mapBlockIndex.find(blockHash);
1590 if (blockIt == mapBlockIndex.end() || !chainActive.Contains(blockIt->second))
1591 {
1592 LogPrintf("%s: cannot validate block of export tx %s\n", __func__, oneExport.first.first.txIn.prevout.hash.GetHex().c_str());
1593 return false;
1594 }
1595 oneExport.first.second = CPartialTransactionProof(exportTx,
1596 std::vector<int32_t>({(int32_t)oneExport.first.first.txIn.prevout.n}),
1597 blockIt->second,
1598 height);
0ff96ff6 1599 }
c8c684e9 1600 return true;
ec1b7e23 1601}
1602
c8c684e9 1603bool CConnectedChains::GetReserveDeposits(const uint160 &currencyID, std::vector<CInputDescriptor> &reserveDeposits)
c3250dcd 1604{
c8c684e9 1605 std::vector<CAddressUnspentDbEntry> confirmedUTXOs;
1606 std::vector<std::pair<CMempoolAddressDeltaKey, CMempoolAddressDelta>> unconfirmedUTXOs;
c3250dcd 1607
c8c684e9 1608 LOCK(mempool.cs);
1609
1610 uint160 depositIndexKey = CReserveDeposit::ReserveDepositIndexKey(currencyID);
1611 if (!GetAddressUnspent(depositIndexKey, CScript::P2IDX, confirmedUTXOs) ||
1612 !mempool.getAddressIndex(std::vector<std::pair<uint160, int32_t>>({{depositIndexKey, CScript::P2IDX}}), unconfirmedUTXOs))
1613 {
1614 LogPrintf("%s: Cannot read address indexes\n", __func__);
1615 return false;
1616 }
1617 for (auto &oneConfirmed : confirmedUTXOs)
1618 {
1619 reserveDeposits.push_back(CInputDescriptor(oneConfirmed.second.script, oneConfirmed.second.satoshis,
1620 CTxIn(oneConfirmed.first.txhash, oneConfirmed.first.index)));
1621 }
1622
1623 // we need to remove those that are spent
1624 std::map<COutPoint, CInputDescriptor> memPoolOuts;
1625 for (auto &oneUnconfirmed : unconfirmedUTXOs)
1626 {
1627 if (oneUnconfirmed.first.spending)
1628 {
1629 memPoolOuts.erase(COutPoint(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index));
1630 }
1631 else
1632 {
1633 const CTransaction oneTx = mempool.mapTx.find(oneUnconfirmed.first.txhash)->GetTx();
1634 memPoolOuts.insert(std::make_pair(COutPoint(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index),
1635 CInputDescriptor(oneTx.vout[oneUnconfirmed.first.index].scriptPubKey, oneUnconfirmed.second.amount,
1636 CTxIn(oneUnconfirmed.first.txhash, oneUnconfirmed.first.index))));
1637 }
1638 }
1639 for (auto &oneUnconfirmed : memPoolOuts)
1640 {
1641 reserveDeposits.push_back(oneUnconfirmed.second);
1642 }
1643
1644 return true;
1645}
1646
1647// given a set of exports and the reserve deposits for this from another chain, create a set of import transactions
1648bool CConnectedChains::CreateLatestImports(const CCurrencyDefinition &sourceSystemDef, // transactions imported from system
1649 const CUTXORef &confirmedSourceNotarization, // last notarization of exporting system
1650 const std::vector<std::pair<std::pair<CInputDescriptor,CPartialTransactionProof>,std::vector<CReserveTransfer>>> &exports,
1651 std::map<uint160, std::vector<std::pair<int, CTransaction>>> &newImports)
1652{
1653 // each export is from the source system, but may be to any currency exposed on this system, so each import
1654 // made combines the potential currency sources of the source system and the importing currency
4c5696b1 1655 LOCK(cs_main);
c8c684e9 1656
1657 uint32_t nHeight = chainActive.Height();
1658 uint160 sourceSystemID = sourceSystemDef.GetID();
1659 bool useProofs = sourceSystemID != thisChain.GetID();
1660
1661 CPBaaSNotarization proofNotarization;
1662 if (useProofs)
1663 {
1664 CTransaction proofNotarizationTx;
1665 uint256 blkHash;
1666 COptCCParams p;
1667 if (confirmedSourceNotarization.hash.IsNull() ||
1668 !myGetTransaction(confirmedSourceNotarization.hash, proofNotarizationTx, blkHash) ||
1669 confirmedSourceNotarization.n >= proofNotarizationTx.vout.size() ||
1670 !proofNotarizationTx.vout[confirmedSourceNotarization.n].scriptPubKey.IsPayToCryptoCondition(p) ||
1671 !p.IsValid() ||
1672 (p.evalCode != EVAL_ACCEPTEDNOTARIZATION && p.evalCode != EVAL_EARNEDNOTARIZATION) ||
1673 !p.vData.size() ||
1674 !(proofNotarization = CPBaaSNotarization(p.vData[0])).IsValid() ||
1675 !proofNotarization.proofRoots.count(sourceSystemID))
1676 {
1677 LogPrintf("%s: invalid notarization for export proof\n", __func__);
1678 return false;
1679 }
1680 }
1681
1682 for (auto &oneIT : exports)
1683 {
1684 uint256 blkHash;
1685 CTransaction exportTx;
1686 if (useProofs)
1687 {
1688 if (!oneIT.first.second.IsValid())
1689 {
1690 LogPrintf("%s: invalid proof for export tx %s\n", __func__, oneIT.first.first.txIn.prevout.hash.GetHex().c_str());
1691 return false;
1692 }
1693 uint256 exportTxID = oneIT.first.second.GetPartialTransaction(exportTx);
1694
1695 if (oneIT.first.second.GetBlockHeight() &&
1696 proofNotarization.proofRoots[sourceSystemID].stateRoot != oneIT.first.second.CheckPartialTransaction(exportTx))
1697 {
1698 LogPrintf("%s: export tx %s fails verification\n", __func__, oneIT.first.first.txIn.prevout.hash.GetHex().c_str());
1699 return false;
1700 }
1701
1702 if (exportTx.vout.size() <= oneIT.first.first.txIn.prevout.n)
1703 {
1704 LogPrintf("%s: invalid proof for export tx output %s\n", __func__, oneIT.first.first.txIn.prevout.hash.GetHex().c_str());
1705 return false;
1706 }
1707 }
1708 else
1709 {
1710 if (!myGetTransaction(oneIT.first.first.txIn.prevout.hash, exportTx, blkHash))
1711 {
1712 LogPrintf("%s: unable to retrieve export tx %s\n", __func__, oneIT.first.first.txIn.prevout.hash.GetHex().c_str());
1713 return false;
1714 }
1715 }
1716
1717 const CCrossChainExport ccx(exportTx.vout[oneIT.first.first.txIn.prevout.n].scriptPubKey);
1718 if (!ccx.IsValid())
1719 {
1720 LogPrintf("%s: invalid export in tx %s\n", __func__, oneIT.first.first.txIn.prevout.hash.GetHex().c_str());
1721 return false;
1722 }
1723
1724 // get reserve deposits for destination currency of export. these will be available whether the source is same chain
1725 // or an external chain/gateway
1726 std::vector<CInputDescriptor> localDeposits;
1727 std::vector<CInputDescriptor> crossChainDeposits;
1728 if (!ConnectedChains.GetReserveDeposits(ccx.destCurrencyID, localDeposits))
1729 {
1730 LogPrintf("%s: cannot get reserve deposits for export in tx %s\n", __func__, oneIT.first.first.txIn.prevout.hash.GetHex().c_str());
1731 return false;
1732 }
1733
1734 // if importing from another system/chain, get reserve deposits of source system to make available to import
1735 // as well
1736 if (useProofs)
1737 {
1738 if (!ConnectedChains.GetReserveDeposits(sourceSystemID, crossChainDeposits))
1739 {
1740 LogPrintf("%s: cannot get reserve deposits for cross-system export in tx %s\n", __func__, oneIT.first.first.txIn.prevout.hash.GetHex().c_str());
1741 return false;
1742 }
1743 }
1744
1745 // now, we have all reserve deposits for both local destination and importing currency, we can use both,
1746 // but must keep track of them separately, first, get last import for the current export
1747 CTransaction lastImportTx;
1748 int32_t outputNum;
1749 CCrossChainImport lastCCI;
1750 uint256 lastImportTxID;
1751
1752 auto lastImportIt = newImports.find(ccx.destCurrencyID);
1753 if (lastImportIt != newImports.end())
1754 {
1755 lastImportTx = lastImportIt->second.back().second;
1756 outputNum = lastImportIt->second.back().first;
1757 lastCCI = CCrossChainImport(lastImportTx.vout[outputNum].scriptPubKey);
1758 lastImportTxID = lastImportTx.GetHash();
1759 }
1760 else if (nHeight && !GetLastImport(ccx.destCurrencyID, lastImportTx, outputNum))
1761 {
1762 LogPrintf("%s: cannot find last import for export %s, %d\n", __func__, oneIT.first.first.txIn.prevout.hash.GetHex().c_str(), outputNum);
1763 return false;
1764 }
1765 else if (nHeight)
1766 {
1767 lastCCI = CCrossChainImport(lastImportTx.vout[outputNum].scriptPubKey);
1768 lastImportTxID = lastImportTx.GetHash();
1769 }
1770
1771 CCurrencyDefinition destCur = ConnectedChains.GetCachedCurrency(ccx.destCurrencyID);
1772 if (!destCur.IsValid())
1773 {
1774 LogPrintf("%s: invalid destination currency for export %s, %d\n", __func__, oneIT.first.first.txIn.prevout.hash.GetHex().c_str(), outputNum);
1775 return false;
1776 }
1777
1778 // now, we have:
1779 // 1. last import output + potentially additional reserve transfer storage outputs to spend from prior import
1780 // 2. reserve transfers for next import
1781 // 3. proof notarization if export is from off chain
1782 // 4. reserve deposits for destination currency on this chain. which will matter if it holds reserves
1783 // 5. reserve deposits for source system, which will matter if we have sent currencies to it that will be used
1784 // 6. destination currency definition
1785
1786 // either:
1787 // 1) it is a gateway on our current chain, and we are creating imports for the system represented by the gateway from this system
1788 // or we are importing into this system from the gateway system
1789 // 2) we are creating an import for a fractional currency on this chain, or
1790 // 3) destination is a PBaaS currency, which is either the native currency, if we are the PBaaS chain or a token on our current chain.
1791 // We are creating imports for this system, which is the PBaaS chain, to receive exports from our notary chain, which is its parent,
1792 // or we are creating imports to receive exports from the PBaaS chain.
4c5696b1 1793 if (!((destCur.IsGateway() && destCur.systemID == ASSETCHAINS_CHAINID) &&
1794 (sourceSystemID == ASSETCHAINS_CHAINID || sourceSystemID == ccx.destCurrencyID)) &&
1795 !(sourceSystemID == destCur.systemID && destCur.systemID == ASSETCHAINS_CHAINID) &&
1796 !(destCur.IsPBaaSChain() &&
1797 (sourceSystemID == ccx.destCurrencyID ||
c8c684e9 1798 (ccx.destCurrencyID == ASSETCHAINS_CHAINID &&
1799 sourceSystemID == ConnectedChains.FirstNotaryChain().chainDefinition.GetID()))))
1800 {
1801 LogPrintf("%s: invalid currency for export/import %s, %d\n", __func__, oneIT.first.first.txIn.prevout.hash.GetHex().c_str(), outputNum);
1802 return false;
1803 }
1804
1805 // now, if we are creating an import for an external export, spend and output the import thread for that external system to make it
1806 // easy to find the last import for any external system and confirm that we are also not skipping any exports
1807 CTransaction lastSourceImportTx;
1808 int32_t sourceOutputNum;
1809 CCrossChainImport lastSourceCCI;
1810 uint256 lastSourceImportTxID;
1811
1812 // if we are importing from another system, find the last import from that system and consider this another one
1813 if (useProofs)
1814 {
1815 auto lastSourceImportIt = newImports.find(ccx.sourceSystemID);
1816 if (lastSourceImportIt != newImports.end())
1817 {
1818 lastSourceImportTx = lastSourceImportIt->second.back().second;
1819 sourceOutputNum = lastSourceImportIt->second.back().first;
1820 lastSourceCCI = CCrossChainImport(lastSourceImportTx.vout[sourceOutputNum].scriptPubKey);
1821 lastSourceImportTxID = lastSourceImportTx.GetHash();
1822 }
1823 else if (nHeight && !GetLastImport(ccx.sourceSystemID, lastSourceImportTx, sourceOutputNum))
1824 {
1825 LogPrintf("%s: cannot find last system import for export %s, %d\n", __func__, oneIT.first.first.txIn.prevout.hash.GetHex().c_str(), sourceOutputNum);
1826 return false;
1827 }
1828 else if (nHeight)
1829 {
1830 lastSourceCCI = CCrossChainImport(lastSourceImportTx.vout[sourceOutputNum].scriptPubKey);
1831 lastSourceImportTxID = lastSourceImportTx.GetHash();
1832 }
1833 }
1834
1835 CPBaaSNotarization lastNotarization;
1836 CInputDescriptor lastNotarizationOut;
1837 std::vector<CReserveTransfer> lastReserveTransfers;
1838 CCrossChainExport lastCCX;
1839 CCrossChainImport lastSysCCI;
1840 int32_t sysCCIOutNum = -1, evidenceOutNumStart = -1, evidenceOutNumEnd = -1;
1841
1842 int32_t notarizationOutNum;
1843 CValidationState state;
1844
4c5696b1 1845 uint160 destCurID = destCur.GetID();
1846
c8c684e9 1847 // if not the initial import in the thread, it should have a valid prior notarization as well
1848 // the notarization of the initial import may be superceded by pre-launch exports
f1a298ef 1849 if (nHeight && lastCCI.IsPostLaunch())
c8c684e9 1850 {
1851 if (!lastCCI.GetImportInfo(lastImportTx,
1852 outputNum,
1853 lastCCX,
1854 lastSysCCI,
1855 sysCCIOutNum,
1856 lastNotarization,
1857 notarizationOutNum,
1858 evidenceOutNumStart,
1859 evidenceOutNumEnd,
1860 lastReserveTransfers,
1861 state))
1862 {
1863 LogPrintf("%s: %u - %s\n", __func__, destCur.name.c_str(), state.GetRejectCode(), state.GetRejectReason().c_str());
1864 return false;
1865 }
1866
1867 lastNotarizationOut = CInputDescriptor(lastImportTx.vout[notarizationOutNum].scriptPubKey,
4c5696b1 1868 lastImportTx.vout[notarizationOutNum].nValue,
1869 CTxIn(lastImportTxID, notarizationOutNum));
1870
c8c684e9 1871 // verify that the current export from the source system spends the prior export from the source system
1872 if (useProofs &&
1873 !(ccx.firstInput > 0 &&
1874 exportTx.vin[ccx.firstInput - 1].prevout.hash == lastSysCCI.exportTxId &&
1875 exportTx.vin[ccx.firstInput - 1].prevout.n == lastSysCCI.exportTxOutNum))
1876 {
1877 LogPrintf("%s: out of order export %s, %d\n", __func__, oneIT.first.first.txIn.prevout.hash.GetHex().c_str(), sourceOutputNum);
1878 return false;
1879 }
1880 }
1881 else if (nHeight)
1882 {
1883 // the first import ever cannot be on a chain that is already running and is not the same chain
1884 // as the first export processed. it is either launched from the launch chain, which is running
1885 // or as a new chain, started from a different launch chain.
1886 if (useProofs)
1887 {
1888 LogPrintf("%s: invalid first import for currency %s on system %s\n", __func__, destCur.name.c_str(), EncodeDestination(CIdentityID(destCur.systemID)).c_str());
1889 return false;
1890 }
1891
1892 // first import, but not first block, so not PBaaS launch - last import has no evidence to spend
1893 CChainNotarizationData cnd;
1894 std::vector<std::pair<CTransaction, uint256>> notarizationTxes;
1895 if (!GetNotarizationData(ccx.destCurrencyID, cnd, &notarizationTxes) || !cnd.IsConfirmed())
1896 {
1897 LogPrintf("%s: cannot get notarization for currency %s on system %s\n", __func__, destCur.name.c_str(), EncodeDestination(CIdentityID(destCur.systemID)).c_str());
1898 return false;
1899 }
1900
1901 lastNotarization = cnd.vtx[cnd.lastConfirmed].second;
1902 if (!lastNotarization.IsValid())
1903 {
1904 LogPrintf("%s: invalid notarization for currency %s on system %s\n", __func__, destCur.name.c_str(), EncodeDestination(CIdentityID(destCur.systemID)).c_str());
1905 return false;
1906 }
1907 lastNotarizationOut = CInputDescriptor(notarizationTxes[cnd.lastConfirmed].first.vout[cnd.vtx[cnd.lastConfirmed].first.n].scriptPubKey,
1908 notarizationTxes[cnd.lastConfirmed].first.vout[cnd.vtx[cnd.lastConfirmed].first.n].nValue,
1909 CTxIn(cnd.vtx[cnd.lastConfirmed].first));
1910 }
1911 else // height is 0 - this is first block of PBaaS chain
1912 {
1913 if (!(proofNotarization.currencyID == ccx.destCurrencyID || proofNotarization.currencyStates.count(ccx.destCurrencyID)))
1914 {
1915 // last notarization is coming from the launch chain at height 0
1916 lastNotarization = CPBaaSNotarization(ccx.destCurrencyID,
1917 proofNotarization.currencyID ==
1918 ccx.destCurrencyID ? proofNotarization.currencyState :
1919 proofNotarization.currencyStates[ccx.destCurrencyID],
1920 0,
1921 CUTXORef(),
1922 0);
1923 lastNotarizationOut = CInputDescriptor(CScript(), 0, CTxIn());
1924 }
1925 }
1926
1927 CPBaaSNotarization newNotarization;
1928 uint256 transferHash;
1929 std::vector<CReserveTransfer> exportTransfers = oneIT.second;
1930 std::vector<CTxOut> newOutputs;
1931 CCurrencyValueMap importedCurrency, gatewayDepositsUsed, spentCurrencyOut;
f1a298ef 1932 // if we are transitioning from export to import, allow the function to set launch clear on the currency
4c5696b1 1933 if (lastNotarization.currencyState.IsLaunchClear() && !lastCCI.IsInitialLaunchImport())
1934 {
1935 lastNotarization.SetPreLaunch();
888ca186 1936 lastNotarization.currencyState.SetLaunchClear(false);
1937 lastNotarization.currencyState.SetPrelaunch(true);
4c5696b1 1938 }
c8c684e9 1939 if (!lastNotarization.NextNotarizationInfo(sourceSystemDef,
1940 destCur,
1941 ccx.sourceHeightStart,
4c5696b1 1942 std::max(ccx.sourceHeightEnd, lastNotarization.notarizationHeight),
c8c684e9 1943 exportTransfers,
1944 transferHash,
1945 newNotarization,
1946 newOutputs,
1947 importedCurrency,
1948 gatewayDepositsUsed,
1949 spentCurrencyOut))
1950 {
1951 LogPrintf("%s: invalid export for currency %s on system %s\n", __func__, destCur.name.c_str(), EncodeDestination(CIdentityID(destCur.systemID)).c_str());
1952 return false;
1953 }
4c5696b1 1954 newNotarization.prevNotarization = CUTXORef(lastNotarizationOut.txIn.prevout.hash, lastNotarizationOut.txIn.prevout.n);
c8c684e9 1955
2f5978fe 1956 //printf("%s: newNotarization:\n%s\n", __func__, newNotarization.ToUniValue().write(1,2).c_str());
1957
c8c684e9 1958 // create the import
1959 CCrossChainImport cci = CCrossChainImport(sourceSystemID,
1960 ccx.sourceHeightEnd,
4c5696b1 1961 destCurID,
c8c684e9 1962 ccx.totalAmounts,
1963 lastCCI.totalReserveOutMap,
1964 newOutputs.size(),
1965 transferHash,
1966 oneIT.first.first.txIn.prevout.hash,
1967 oneIT.first.first.txIn.prevout.n,
4c5696b1 1968 lastCCI.IsDefinitionImport() ? CCrossChainImport::FLAG_INITIALLAUNCHIMPORT : CCrossChainImport::FLAG_POSTLAUNCH);
1969 cci.SetSameChain(!useProofs);
c8c684e9 1970
1971 TransactionBuilder tb = TransactionBuilder(Params().GetConsensus(), nHeight + 1);
1972
1973 CCcontract_info CC;
1974 CCcontract_info *cp;
1975
1976 // now add the import itself
1977 cp = CCinit(&CC, EVAL_CROSSCHAIN_IMPORT);
1978 std::vector<CTxDestination> dests({CPubKey(ParseHex(CC.CChexstr))});
1979 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CCrossChainImport>(EVAL_CROSSCHAIN_IMPORT, dests, 1, &cci)), 0);
1980
1981 // get the source system import as well
1982 CCrossChainImport sysCCI;
1983 if (useProofs)
1984 {
1985 // we need a new import for the source system
1986 sysCCI = cci;
1987 sysCCI.importCurrencyID = sysCCI.sourceSystemID;
1988 sysCCI.flags |= sysCCI.FLAG_SOURCESYSTEM;
1989 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CCrossChainImport>(EVAL_CROSSCHAIN_IMPORT, dests, 1, &sysCCI)), 0);
1990 }
1991
4c5696b1 1992 // add notarization first, so it will be just after the import
c8c684e9 1993 cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
1994 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
1995 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &newNotarization)), 0);
1996
1997 // add the export evidence, null for same chain or reference specific notarization + (proof + reserve transfers) if sourced from external chain
1998 // add evidence first, then notarization, then import, add reserve deposits after that, if necessary
1999
2000 if (useProofs)
2001 {
2002 // if we need to put the partial transaction proof and follow it with reserve transfers, do it
4c5696b1 2003 CNotaryEvidence evidence = CNotaryEvidence(destCurID,
c8c684e9 2004 CUTXORef(confirmedSourceNotarization.hash, confirmedSourceNotarization.n),
2005 true,
2006 std::map<CIdentityID, CIdentitySignature>(),
2007 std::vector<CPartialTransactionProof>({oneIT.first.second}),
2008 CNotaryEvidence::TYPE_PARTIAL_TXPROOF);
2009
2010 // add notarization first, so it will be after the import
2011 cp = CCinit(&CC, EVAL_NOTARY_EVIDENCE);
2012 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2013 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CNotaryEvidence>(EVAL_NOTARY_EVIDENCE, dests, 1, &evidence)),
2014 CNotaryEvidence::DEFAULT_OUTPUT_VALUE);
2015
2016 // supplemental export evidence is posted as a supplemental export
2017 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
2018 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2019
2020 // now add all reserve transfers in supplemental outputs
2021 auto transferIT = exportTransfers.begin();
2022 while (transferIT != exportTransfers.end())
2023 {
2024 int transferCount = exportTransfers.end() - transferIT;
2025 if (transferCount > 25)
2026 {
2027 transferCount = 25;
2028 }
2029
2030 CCrossChainExport rtSupplement = ccx;
2031 rtSupplement.flags = ccx.FLAG_EVIDENCEONLY + ccx.FLAG_SUPPLEMENTAL;
2032 rtSupplement.reserveTransfers.assign(transferIT, transferIT + transferCount);
2033 CScript supScript = MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &rtSupplement));
2034 while (supScript.size() > supScript.MAX_SCRIPT_ELEMENT_SIZE)
2035 {
2036 transferCount--;
2037 rtSupplement.reserveTransfers.assign(transferIT, transferIT + transferCount);
2038 supScript = MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &rtSupplement));
2039 }
2040 tb.AddTransparentOutput(supScript, 0);
2041 transferIT += transferCount;
2042 }
2043 }
2044
2045 // add all importing and conversion outputs
2046 for (auto oneOut : newOutputs)
2047 {
2048 tb.AddTransparentOutput(oneOut.scriptPubKey, oneOut.nValue);
2049 }
2050
2051 // now, we need to spend previous import, notarization, and evidence or export finalization from export
2052
2053 // spend evidence or export finalization if necessary
2054 // if we use proofs, spend the prior, unless we have no prior proofs to spend
2055 if (lastCCI.IsValid() && !lastImportTxID.IsNull())
2056 {
2057 // spend last import
2058 tb.AddTransparentInput(COutPoint(lastImportTxID, outputNum), lastImportTx.vout[outputNum].scriptPubKey, lastImportTx.vout[outputNum].nValue);
2059
2060 // if we should add a source import input
2061 if (useProofs)
2062 {
2063 tb.AddTransparentInput(COutPoint(lastSourceImportTxID, sourceOutputNum),
2064 lastSourceImportTx.vout[sourceOutputNum].scriptPubKey, lastSourceImportTx.vout[sourceOutputNum].nValue);
2065 }
2066
2067 if (!lastNotarizationOut.txIn.prevout.hash.IsNull())
2068 {
2069 // and its notarization
2070 tb.AddTransparentInput(lastNotarizationOut.txIn.prevout, lastNotarizationOut.scriptPubKey, lastNotarizationOut.nValue);
2071 }
2072
2073 if (useProofs)
2074 {
2075 for (int i = evidenceOutNumStart; i <= evidenceOutNumEnd; i++)
2076 {
2077 tb.AddTransparentInput(COutPoint(lastImportTxID, i), lastImportTx.vout[i].scriptPubKey, lastImportTx.vout[i].nValue);
2078 }
2079 }
2080 else
2081 {
2082 // if same chain and export has a finalization, spend it on import
2083 CObjectFinalization of;
2084 COptCCParams p;
2085 if (exportTx.vout.size() > (oneIT.first.first.txIn.prevout.n + 1) &&
2086 exportTx.vout[oneIT.first.first.txIn.prevout.n + 1].scriptPubKey.IsPayToCryptoCondition(p) &&
2087 p.IsValid() &&
2088 p.evalCode == EVAL_FINALIZE_EXPORT &&
2089 p.vData.size() &&
2090 (of = CObjectFinalization(p.vData[0])).IsValid())
2091 {
2092 tb.AddTransparentInput(COutPoint(oneIT.first.first.txIn.prevout.hash, oneIT.first.first.txIn.prevout.n + 1),
2093 exportTx.vout[oneIT.first.first.txIn.prevout.n + 1].scriptPubKey,
2094 exportTx.vout[oneIT.first.first.txIn.prevout.n + 1].nValue);
2095 }
2096 }
2097 }
2098
2099 // now, get all reserve deposits and change for both gateway reserve deposits and local reserve deposits
4c5696b1 2100 CCurrencyValueMap gatewayChange;
c8c684e9 2101
2102 // add gateway deposit inputs and make a change output for those to the source system's deposits, if necessary
2103 std::vector<CInputDescriptor> depositsToUse;
2104 cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
2105 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr))});
2106
2107 if (gatewayDepositsUsed.valueMap.size())
2108 {
2109 CCurrencyValueMap totalDepositsInput;
2110
2111 // find all deposits intersecting with target currencies
2112 for (auto &oneDeposit : crossChainDeposits)
2113 {
2114 CCurrencyValueMap oneDepositVal = oneDeposit.scriptPubKey.ReserveOutValue();
2115 if (oneDeposit.nValue)
2116 {
2117 oneDepositVal.valueMap[ASSETCHAINS_CHAINID] = oneDeposit.nValue;
2118 }
2119 if (gatewayDepositsUsed.Intersects(oneDepositVal))
2120 {
2121 totalDepositsInput += oneDepositVal;
2122 depositsToUse.push_back(oneDeposit);
2123 }
2124 }
2125
4c5696b1 2126 gatewayChange = totalDepositsInput - gatewayDepositsUsed;
c8c684e9 2127
2128 // we should always be able to fulfill
2129 // gateway despoit requirements, or this is an error
2130 if (gatewayChange.HasNegative())
2131 {
2132 LogPrintf("%s: insufficient funds for gateway reserve deposits from system %s\n", __func__, EncodeDestination(CIdentityID(ccx.sourceSystemID)).c_str());
2133 return false;
2134 }
4c5696b1 2135 }
c8c684e9 2136
4c5696b1 2137 // the amount being imported is under the control of the exporting system and
2138 // will either be minted by the import from that system or spent on this chain from its reserve deposits
2139 // any remaining unmet output requirements must be met by local chain deposits as a result of conversions
2140 // conversion outputs may only be of the destination currency itself, or in the case of a fractional currency,
2141 // the currency or its reserves.
2142 //
2143 // if this is a local import, local requirements are any spent currency besides
2144 //
2145 // change will all accrue to reserve deposits
2146 //
2147
2148 // first determine total deposits required to fulfill currency in requirements
2149 // this will take fees into account, which do not intersect with other currencies
2150 CCurrencyValueMap requiredDeposits = cci.importValue;
2151
2152 // we must also come up with anything spent that is not satisfied via standard required deposits,
2153 // as determined by reserves in. any excess in required deposits beyond what is spent will go to
2154 // reserve deposits change
2155 requiredDeposits += spentCurrencyOut.SubtractToZero(requiredDeposits);
2156
2157 if (newNotarization.IsLaunchCleared())
2158 {
2159 requiredDeposits += CCurrencyValueMap(std::vector<uint160>({sourceSystemID}),
2160 std::vector<int64_t>({sourceSystemDef.GetCurrencyRegistrationFee()}));
c8c684e9 2161 }
2162
4c5696b1 2163 CCurrencyValueMap newCurrencyIn = gatewayDepositsUsed + importedCurrency;
2164 CCurrencyValueMap localDepositRequirements = requiredDeposits.SubtractToZero(newCurrencyIn);
2165 CCurrencyValueMap localDepositChange;
2166
2167 // add local reserve deposit inputs and determine change
2168 if (localDepositRequirements.valueMap.size())
c8c684e9 2169 {
2170 CCurrencyValueMap totalDepositsInput;
c8c684e9 2171
4c5696b1 2172 // find all deposits intersecting with target currencies
2173 for (auto &oneDeposit : localDeposits)
c8c684e9 2174 {
4c5696b1 2175 CCurrencyValueMap oneDepositVal = oneDeposit.scriptPubKey.ReserveOutValue();
2176 if (oneDeposit.nValue)
c8c684e9 2177 {
4c5696b1 2178 oneDepositVal.valueMap[ASSETCHAINS_CHAINID] = oneDeposit.nValue;
2179 }
2180 if (localDepositRequirements.Intersects(oneDepositVal))
2181 {
2182 totalDepositsInput += oneDepositVal;
2183 depositsToUse.push_back(oneDeposit);
c8c684e9 2184 }
2185 }
2186
4c5696b1 2187 localDepositChange = totalDepositsInput - localDepositRequirements;
c8c684e9 2188
4c5696b1 2189 // we should always be able to fulfill
2190 // local despoit requirements, or this is an error
2191 if (localDepositChange.HasNegative())
c8c684e9 2192 {
4c5696b1 2193 LogPrintf("%s: insufficient funds for local reserve deposits for currency %s\n", __func__, EncodeDestination(CIdentityID(ccx.destCurrencyID)).c_str());
2194 return false;
c8c684e9 2195 }
4c5696b1 2196 }
c8c684e9 2197
4c5696b1 2198 // add local deposit inputs
2199 for (auto oneOut : depositsToUse)
2200 {
2201 tb.AddTransparentInput(oneOut.txIn.prevout, oneOut.scriptPubKey, oneOut.nValue);
2202 }
c8c684e9 2203
4c5696b1 2204 // we will keep reserve deposit change to single currency outputs to ensure aggregation of like currencies and
2205 // prevent fragmentation edge cases
2206 for (auto &oneChangeVal : gatewayChange.valueMap)
2207 {
2208 // dust rules don't apply
2209 if (oneChangeVal.second)
c8c684e9 2210 {
f1a298ef 2211 CReserveDeposit rd = CReserveDeposit(sourceSystemID, CCurrencyValueMap());;
2212 CAmount nativeOutput = 0;
2213 if (oneChangeVal.first == ASSETCHAINS_CHAINID)
2214 {
2215 nativeOutput = oneChangeVal.second;
2216 }
2217 else
2218 {
2219 rd.reserveValues.valueMap[oneChangeVal.first] = oneChangeVal.second;
2220 }
2221 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CReserveDeposit>(EVAL_RESERVE_DEPOSIT, dests, 1, &rd)), nativeOutput);
c8c684e9 2222 }
2223 }
4c5696b1 2224
2225 // we will keep reserve deposit change to single currency outputs to ensure aggregation of like currencies and
2226 // prevent fragmentation edge cases
2227 for (auto &oneChangeVal : localDepositChange.valueMap)
c8c684e9 2228 {
4c5696b1 2229 // dust rules don't apply
2230 if (oneChangeVal.second)
c8c684e9 2231 {
f1a298ef 2232 CReserveDeposit rd = CReserveDeposit(ccx.destCurrencyID, CCurrencyValueMap());;
2233 CAmount nativeOutput = 0;
2234 if (oneChangeVal.first == ASSETCHAINS_CHAINID)
2235 {
2236 nativeOutput = oneChangeVal.second;
2237 }
2238 else
2239 {
2240 rd.reserveValues.valueMap[oneChangeVal.first] = oneChangeVal.second;
2241 }
2242 tb.AddTransparentOutput(MakeMofNCCScript(CConditionObj<CReserveDeposit>(EVAL_RESERVE_DEPOSIT, dests, 1, &rd)), nativeOutput);
c8c684e9 2243 }
2244 }
4c5696b1 2245
2246 CCurrencyValueMap reserveInMap = CCurrencyValueMap(newNotarization.currencyState.currencies,
2247 newNotarization.currencyState.reserveIn).CanonicalMap();
2248
2249 // ins and outs are correct. now calculate the fee correctly here and set the transaction builder accordingly
2250 // to prevent an automatic change output. we could just let it go and hae a setting to stop creation of a change output,
2251 // but this is a nice doublecheck requirement
2252 /* printf("%s: reserveInMap:\n%s\nspentCurrencyOut:\n%s\nrequiredDeposits:\n%s\nccx.totalAmounts:\n%s\nccx.totalFees:\n%s\n",
2253 __func__,
2254 reserveInMap.ToUniValue().write(1,2).c_str(),
2255 spentCurrencyOut.ToUniValue().write(1,2).c_str(),
2256 requiredDeposits.ToUniValue().write(1,2).c_str(),
2257 ccx.totalAmounts.ToUniValue().write(1,2).c_str(),
2258 ccx.totalFees.ToUniValue().write(1,2).c_str()); */
2259
2260 // pay the fee out to the miner
2261 tb.SetFee(requiredDeposits.valueMap[ASSETCHAINS_CHAINID] - spentCurrencyOut.valueMap[ASSETCHAINS_CHAINID]);
2262
2263 // if we've got launch currency as the fee, it means we are mining block 1
2264 // send it to our current miner or notary address
2265 if (!ccx.IsSameChain() && ccx.IsClearLaunch())
c8c684e9 2266 {
4c5696b1 2267 CTxDestination addr;
2268 extern std::string NOTARY_PUBKEY;
2269 if (mapArgs.count("-mineraddress"))
2270 {
2271 addr = DecodeDestination(mapArgs["-mineraddress"]);
2272 }
2273 else if (!VERUS_NOTARYID.IsNull())
2274 {
2275 addr = VERUS_NOTARYID;
2276 }
2277 else if (!VERUS_DEFAULTID.IsNull())
2278 {
2279 addr = VERUS_NOTARYID;
2280 }
2281 else if (!NOTARY_PUBKEY.empty())
2282 {
2283 CPubKey pkey;
2284 std::vector<unsigned char> hexKey = ParseHex(NOTARY_PUBKEY);
2285 pkey.Set(hexKey.begin(), hexKey.end());
2286 addr = pkey.GetID();
2287 }
2288 tb.SendChangeTo(addr);
c8c684e9 2289 }
2290
2291 TransactionBuilderResult result = tb.Build();
2292 if (result.IsError())
2293 {
4c5696b1 2294 UniValue jsonTx(UniValue::VOBJ);
2295 uint256 hashBlk;
2296 TxToUniv(tb.mtx, hashBlk, jsonTx);
2297 printf("%s\n", jsonTx.write(1,2).c_str());
2298 LogPrintf("%s: cannot build import transaction for currency %s: %s\n", __func__, EncodeDestination(CIdentityID(ccx.destCurrencyID)).c_str(), result.GetError().c_str());
c8c684e9 2299 return false;
2300 }
2301
4c5696b1 2302 CTransaction newImportTx;
c8c684e9 2303
2304 try
2305 {
2306 newImportTx = result.GetTxOrThrow();
2307 }
2308 catch(const std::exception& e)
2309 {
2310 LogPrintf("%s: failure to build transaction for export to %s\n", __func__, EncodeDestination(CIdentityID(ccx.destCurrencyID)).c_str());
2311 return false;
2312 }
2313
2314 // put our transaction in place of any others
2315 std::list<CTransaction> removed;
2316 mempool.removeConflicts(newImportTx, removed);
2317
2318 // add to mem pool and relay
2319 if (!myAddtomempool(newImportTx, &state))
2320 {
2321 LogPrintf("%s: %s\n", __func__, state.GetRejectReason().c_str());
2322 return false;
2323 }
2324 else
2325 {
2326 RelayTransaction(newImportTx);
2327 }
2328
2329 newImports[ccx.destCurrencyID].push_back(std::make_pair(0, newImportTx));
2330 }
2331 return true;
2332}
2333
2334
2335// get the exports to a specific system from this chain, starting from a specific height up to a specific height
2336// export proofs are all returned as null
2337bool CConnectedChains::GetSystemExports(const uint160 &systemID,
2338 std::vector<std::pair<std::pair<CInputDescriptor,CPartialTransactionProof>,std::vector<CReserveTransfer>>> &exports,
2339 uint32_t fromHeight,
2340 uint32_t toHeight)
2341{
2342 // which transaction are we in this block?
2343 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
2344
2345 // get all export transactions including and since this one up to the confirmed cross-notarization
2346 if (GetAddressIndex(CCrossChainRPCData::GetConditionID(systemID, CCrossChainExport::CurrencyExportKey()),
2347 CScript::P2IDX,
2348 addressIndex,
2349 fromHeight,
2350 toHeight))
2351 {
2352 for (auto &idx : addressIndex)
2353 {
2354 uint256 blkHash;
2355 CTransaction exportTx;
2356 if (!idx.first.spending && myGetTransaction(idx.first.txhash, exportTx, blkHash))
2357 {
2358 std::vector<CBaseChainObject *> opretTransfers;
2359 CCrossChainExport ccx;
2360 if ((ccx = CCrossChainExport(exportTx.vout[idx.first.index].scriptPubKey)).IsValid())
2361 {
2362 std::vector<CReserveTransfer> exportTransfers;
2363 CPartialTransactionProof exportProof;
2364
2365 // get the export transfers from the source
2366 if (ccx.sourceSystemID == ASSETCHAINS_CHAINID)
2367 {
2368 for (int i = ccx.firstInput; i < ccx.firstInput + ccx.numInputs; i++)
2369 {
2370 CTransaction oneTxIn;
2371 uint256 txInBlockHash;
2372 if (!myGetTransaction(exportTx.vin[i].prevout.hash, oneTxIn, txInBlockHash))
2373 {
2374 LogPrintf("%s: cannot access transaction %s\n", __func__, exportTx.vin[i].prevout.hash.GetHex().c_str());
2375 return false;
2376 }
2377 COptCCParams oneInP;
2378 if (!(oneTxIn.vout[exportTx.vin[i].prevout.n].scriptPubKey.IsPayToCryptoCondition(oneInP) &&
2379 oneInP.IsValid() &&
2380 oneInP.evalCode == EVAL_RESERVE_TRANSFER &&
2381 oneInP.vData.size()))
2382 {
2383 LogPrintf("%s: invalid reserve transfer input %s, %lu\n", __func__, exportTx.vin[i].prevout.hash.GetHex().c_str(), exportTx.vin[i].prevout.n);
2384 return false;
2385 }
2386 exportTransfers.push_back(CReserveTransfer(oneInP.vData[0]));
2387 if (!exportTransfers.back().IsValid())
2388 {
2389 LogPrintf("%s: invalid reserve transfer input 1 %s, %lu\n", __func__, exportTx.vin[i].prevout.hash.GetHex().c_str(), exportTx.vin[i].prevout.n);
2390 return false;
2391 }
2392 }
2393 }
2394 else
2395 {
2396 LogPrintf("%s: invalid export from incorrect system on this chain in tx %s\n", __func__, idx.first.txhash.GetHex().c_str());
2397 return false;
2398 }
2399
2400 exports.push_back(std::make_pair(std::make_pair(CInputDescriptor(exportTx.vout[idx.first.index].scriptPubKey,
2401 exportTx.vout[idx.first.index].nValue,
2402 CTxIn(idx.first.txhash, idx.first.index)),
2403 exportProof),
2404 exportTransfers));
2405 }
2406 }
2407 }
2408 return true;
2409 }
2410 return false;
2411}
2412
2413// get the exports to a specific system from this chain, starting from a specific height up to a specific height
2414// proofs are returned as null
2415bool CConnectedChains::GetCurrencyExports(const uint160 &currencyID,
2416 std::vector<std::pair<std::pair<CInputDescriptor,CPartialTransactionProof>,std::vector<CReserveTransfer>>> &exports,
2417 uint32_t fromHeight,
2418 uint32_t toHeight)
2419{
2420 // which transaction are we in this block?
2421 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
2422
2423 // get all export transactions including and since this one up to the confirmed cross-notarization
2424 if (GetAddressIndex(CCrossChainRPCData::GetConditionID(currencyID, CCrossChainExport::CurrencyExportKey()),
2425 CScript::P2IDX,
2426 addressIndex,
2427 fromHeight,
2428 toHeight))
2429 {
2430 for (auto &idx : addressIndex)
2431 {
2432 uint256 blkHash;
2433 CTransaction exportTx;
2434 if (!idx.first.spending && myGetTransaction(idx.first.txhash, exportTx, blkHash))
2435 {
2436 std::vector<CBaseChainObject *> opretTransfers;
2437 CCrossChainExport ccx;
2438 if ((ccx = CCrossChainExport(exportTx.vout[idx.first.index].scriptPubKey)).IsValid())
2439 {
2440 std::vector<CReserveTransfer> exportTransfers;
2441 CPartialTransactionProof exportProof;
2442
2443 // get the export transfers from the source
2444 if (ccx.sourceSystemID == ASSETCHAINS_CHAINID)
2445 {
2446 for (int i = ccx.firstInput; i < ccx.firstInput + ccx.numInputs; i++)
2447 {
2448 CTransaction oneTxIn;
2449 uint256 txInBlockHash;
2450 if (!myGetTransaction(exportTx.vin[i].prevout.hash, oneTxIn, txInBlockHash))
2451 {
2452 LogPrintf("%s: cannot access transasction %s\n", __func__, exportTx.vin[i].prevout.hash.GetHex().c_str());
2453 return false;
2454 }
2455 COptCCParams oneInP;
2456 if (!(oneTxIn.vout[exportTx.vin[i].prevout.n].scriptPubKey.IsPayToCryptoCondition(oneInP) &&
2457 oneInP.IsValid() &&
2458 oneInP.evalCode == EVAL_RESERVE_TRANSFER &&
2459 oneInP.vData.size()))
2460 {
2461 LogPrintf("%s: invalid reserve transfer input %s, %lu\n", __func__, exportTx.vin[i].prevout.hash.GetHex().c_str(), exportTx.vin[i].prevout.n);
2462 return false;
2463 }
2464 exportTransfers.push_back(CReserveTransfer(oneInP.vData[0]));
2465 if (!exportTransfers.back().IsValid())
2466 {
2467 LogPrintf("%s: invalid reserve transfer input 1 %s, %lu\n", __func__, exportTx.vin[i].prevout.hash.GetHex().c_str(), exportTx.vin[i].prevout.n);
2468 return false;
2469 }
2470 }
2471 }
2472 else
2473 {
2474 LogPrintf("%s: invalid export from incorrect system on this chain in tx %s\n", __func__, idx.first.txhash.GetHex().c_str());
2475 return false;
2476 }
2477
2478 exports.push_back(std::make_pair(std::make_pair(CInputDescriptor(exportTx.vout[idx.first.index].scriptPubKey,
2479 exportTx.vout[idx.first.index].nValue,
2480 CTxIn(idx.first.txhash, idx.first.index)),
2481 exportProof),
2482 exportTransfers));
2483 }
2484 else
2485 {
2486 LogPrintf("%s: invalid export index for txid: %s, %lu\n", __func__, idx.first.txhash.GetHex().c_str(), idx.first.index);
2487 return false;
2488 }
2489 }
2490 }
2491 return true;
2492 }
2493 return false;
2494}
2495
2496bool CConnectedChains::GetPendingSystemExports(const uint160 systemID,
2497 uint32_t fromHeight,
2498 multimap<uint160, pair<int, CInputDescriptor>> &exportOutputs)
2499{
2500 CCurrencyDefinition chainDef;
2501 int32_t defHeight;
2502 exportOutputs.clear();
2503
2504 if (GetCurrencyDefinition(systemID, chainDef, &defHeight))
2505 {
2506 // which transaction are we in this block?
2507 std::vector<std::pair<CAddressIndexKey, CAmount>> addressIndex;
2508
2509 CChainNotarizationData cnd;
2510 if (GetNotarizationData(systemID, cnd))
2511 {
2512 uint160 exportKey;
2513 if (chainDef.IsGateway())
2514 {
2515 exportKey = CCrossChainRPCData::GetConditionID(chainDef.gatewayID, CCrossChainExport::SystemExportKey());
2516 }
2517 else
2518 {
2519 exportKey = CCrossChainRPCData::GetConditionID(systemID, CCrossChainExport::SystemExportKey());
2520 }
2521
2522 // get all export transactions including and since this one up to the confirmed cross-notarization
2523 if (GetAddressIndex(exportKey, CScript::P2IDX, addressIndex, fromHeight))
2524 {
2525 for (auto &idx : addressIndex)
2526 {
2527 uint256 blkHash;
2528 CTransaction exportTx;
2529 if (!idx.first.spending && myGetTransaction(idx.first.txhash, exportTx, blkHash))
2530 {
2531 std::vector<CBaseChainObject *> opretTransfers;
2532 CCrossChainExport ccx;
2533 if ((ccx = CCrossChainExport(exportTx.vout[idx.first.index].scriptPubKey)).IsValid())
2534 {
2535 exportOutputs.insert(std::make_pair(ccx.destCurrencyID,
2536 std::make_pair(idx.first.blockHeight,
2537 CInputDescriptor(exportTx.vout[idx.first.index].scriptPubKey,
2538 exportTx.vout[idx.first.index].nValue,
2539 CTxIn(idx.first.txhash, idx.first.index)))));
2540 }
2541 }
2542 }
2543 }
2544 }
2545 return true;
2546 }
2547 else
2548 {
2549 LogPrintf("%s: unrecognized chain name or chain ID\n", __func__);
2550 return false;
2551 }
2552}
2553
2554bool CConnectedChains::CreateNextExport(const CCurrencyDefinition &_curDef,
2555 const std::vector<ChainTransferData> &_txInputs,
2556 const std::vector<CInputDescriptor> &priorExports,
2557 const CTxDestination &feeOutput,
2558 uint32_t sinceHeight,
2559 uint32_t curHeight,
2560 int32_t inputStartNum,
2561 int32_t &inputsConsumed,
2562 std::vector<CTxOut> &exportOutputs,
2563 std::vector<CReserveTransfer> &exportTransfers,
2564 const CPBaaSNotarization &lastNotarization,
f93a9379 2565 const CUTXORef &lastNotarizationUTXO,
c8c684e9 2566 CPBaaSNotarization &newNotarization,
f93a9379 2567 int &newNotarizationOutNum,
c8c684e9 2568 bool createOnlyIfRequired,
2569 const ChainTransferData *addInputTx)
2570{
2571 // Accepts all reserve transfer inputs to a particular currency destination.
2572 // Generates a new export transactions and any required notarizations.
2573 // Observes anti-front-running rules.
2574
2575 // This assumes that:
2576 // 1) _txInputs has all currencies since last export on this system with accurate block numbers
2577 // 2) _txInputs is sorted
2578 // 3) the last export transaction is added as input outside of this call
2579
2580 newNotarization = lastNotarization;
f93a9379 2581 newNotarization.prevNotarization = lastNotarizationUTXO;
c8c684e9 2582 inputsConsumed = 0;
2583
2584 uint160 destSystemID = _curDef.IsGateway() ? _curDef.gatewayID : _curDef.systemID;
2585 uint160 currencyID = _curDef.GetID();
2586 bool crossSystem = destSystemID != ASSETCHAINS_CHAINID;
2587 bool isPreLaunch = _curDef.launchSystemID == ASSETCHAINS_CHAINID && _curDef.startBlock > sinceHeight;
2588 bool isClearLaunchExport = isPreLaunch && _curDef.startBlock <= curHeight;
2589
2590 if (!isClearLaunchExport && !_txInputs.size() && !addInputTx)
2591 {
2592 // no error, just nothing to do
2593 return true;
2594 }
2595
2596 // The aggregation rules require that:
2597 // 1. Either there are MIN_INPUTS of reservetransfer or MIN_BLOCKS before an
2598 // aggregation can be made as an export transaction.
2599 // 2. We will include as many reserveTransfers as we can, block by block until the
2600 // first block that allows us to meet MIN_INPUTS on this export.
2601 // 3. One additional *conversion* input may be added in the export transaction as
2602 // a "what if", for the estimation API.
2603 //
2604 // If the addInputTx is included, this function will add it to the export transaction created.
2605
2606 // determine inputs to include in next export
2607 // early out if createOnlyIfRequired is true
2608 std::vector<ChainTransferData> txInputs;
2609 if (!isClearLaunchExport &&
2610 sinceHeight - curHeight < CCrossChainExport::MIN_BLOCKS &&
2611 _txInputs.size() < CCrossChainExport::MIN_INPUTS &&
2612 createOnlyIfRequired)
2613 {
2614 return true;
2615 }
2616
2617 uint32_t addHeight = sinceHeight;
2618 for (int inputNum = 0; inputNum < _txInputs.size(); inputNum++)
2619 {
2620 uint32_t nextHeight = std::get<0>(_txInputs[inputNum]);
2621 if (addHeight != nextHeight)
2622 {
2623 // if this is a launch export, we create one at the boundary
2624 if (isClearLaunchExport && nextHeight >= _curDef.startBlock)
2625 {
2626 addHeight = _curDef.startBlock - 1;
2627 break;
2628 }
2629 // if we have skipped to the next block, and we have enough to make an export, we cannot take any more
2630 // except the optional one to add
2631 if (inputNum >= CCrossChainExport::MIN_INPUTS)
2632 {
2633 break;
2634 }
2635 addHeight = nextHeight;
2636 }
2637 txInputs.push_back(_txInputs[inputNum]);
2638 }
2639
2640 // if we made an export before getting to the end, it doesn't clear launch
2641 // if we either early outed, due to height or landed right on the correct height, determine launch state
2642 // a clear launch export may have no inputs yet still be created with a clear launch notarization
7081a8d9 2643 if (isClearLaunchExport)
2644 {
2645 addHeight = _curDef.startBlock - 1;
2646 }
c8c684e9 2647
2648 // all we expect to add are in txInputs now
2649 inputsConsumed = txInputs.size();
2650
2651 // check to see if we need to add the optional input, not counted as "consumed"
2652 if (addInputTx)
2653 {
2654 uint32_t rtHeight = std::get<0>(*addInputTx);
2655 CReserveTransfer reserveTransfer = std::get<2>(*addInputTx);
2656 // ensure that any pre-conversions or conversions are all valid, based on mined height and
2657 if (reserveTransfer.IsPreConversion())
2658 {
2659 printf("%s: Invalid optional pre-conversion\n", __func__);
2660 LogPrintf("%s: Invalid optional pre-conversion\n", __func__);
2661 }
2662 else if (reserveTransfer.IsConversion() && rtHeight < _curDef.startBlock)
2663 {
2664 printf("%s: Invalid optional conversion added before start block\n", __func__);
2665 LogPrintf("%s: Invalid optional conversion added before start block\n", __func__);
2666 }
2667 else
2668 {
2669 txInputs.push_back(*addInputTx);
2670 }
2671 }
2672
2673 // if we are not the clear launch export and have no inputs, including the optional one, we are done
2674 if (!isClearLaunchExport && txInputs.size() == 0)
2675 {
2676 return true;
2677 }
2678
2679 // currency from reserve transfers will be stored appropriately for export as follows:
2680 // 1) Currency with this systemID can be exported to another chain, but it will be held on this
2681 // chain in a reserve deposit output. The one exception to this rule is when a gateway currency
2682 // that is using this systemID is being exported to itself as the other system. In that case,
2683 // it is sent out and assumed burned from this system, just as it is created/minted when sent
2684 // in from the gateway as the source system.
2685 //
2686 // 2) Currency being sent to the system of its origin is not tracked.
2687 //
2688 // 3) Currency from a system that is not this one being sent to a 3rd system is not allowed.
2689 //
2690 // get all the new reserve deposits. the only time we will merge old reserve deposit
2691 // outputs with current reserve deposit outputs is if there is overlap in currencies. the reserve
2692 // deposits for this new batch will be output together, and the currencies that are not present
2693 // in the new batch will be left in separate outputs, one per currency. this should enable old
2694 // currencies to get aggregated but still left behind and not carried forward when they drop out
2695 // of use.
2696 CCurrencyDefinition destSystem = ConnectedChains.GetCachedCurrency(destSystemID);
2697
2698 if (!destSystem.IsValid())
2699 {
2700 printf("%s: Invalid data for export system or corrupt chain state\n", __func__);
2701 LogPrintf("%s: Invalid data for export system or corrupt chain state\n", __func__);
2702 return false;
2703 }
2704
2705 for (int i = 0; i < txInputs.size(); i++)
2706 {
2707 exportTransfers.push_back(std::get<2>(txInputs[i]));
2708 }
2709
2710 uint256 transferHash;
2711 CCurrencyValueMap importedCurrency;
2712 CCurrencyValueMap gatewayDepositsUsed;
2713 CCurrencyValueMap spentCurrencyOut;
4c5696b1 2714 std::vector<CTxOut> checkOutputs;
c8c684e9 2715
2716 if (!lastNotarization.NextNotarizationInfo(ConnectedChains.ThisChain(),
2717 _curDef,
2718 sinceHeight,
2719 addHeight,
2720 exportTransfers,
2721 transferHash,
2722 newNotarization,
4c5696b1 2723 checkOutputs,
c8c684e9 2724 importedCurrency,
2725 gatewayDepositsUsed,
2726 spentCurrencyOut))
2727 {
2728 printf("%s: cannot create notarization\n", __func__);
2729 LogPrintf("%s: cannot create notarization\n", __func__);
2730 return false;
2731 }
2732
f1a298ef 2733 //printf("%s: num transfers %ld\n", __func__, exportTransfers.size());
2734
4c5696b1 2735 newNotarization.prevNotarization = lastNotarizationUTXO;
2736
c8c684e9 2737 CCurrencyValueMap totalExports;
2738 CCurrencyValueMap totalTransferFees; // all fees taken from reserveTransfers
2739 CCurrencyValueMap newReserveDeposits;
2740
2741 for (int i = 0; i < exportTransfers.size(); i++)
2742 {
2743 totalTransferFees += exportTransfers[i].TotalTransferFee();
2744 totalExports += exportTransfers[i].TotalCurrencyOut();
2745 }
2746
2747 // if we are exporting off of this system to a gateway or PBaaS chain, don't allow 3rd party
2748 // or unregistered exports. if same to same chain, all exports are ok.
2749 if (destSystemID != ASSETCHAINS_CHAINID)
2750 {
2751 for (auto &oneCur : totalExports.valueMap)
2752 {
2753 if (oneCur.first == ASSETCHAINS_CHAINID)
2754 {
2755 newReserveDeposits.valueMap[oneCur.first] += oneCur.second;
2756 continue;
2757 }
2758
2759 CCurrencyDefinition oneCurDef;
2760 uint160 oneCurID;
2761
2762 if (oneCur.first != oneCurID)
2763 {
2764 oneCurDef = ConnectedChains.GetCachedCurrency(oneCur.first);
2765 if (!oneCurDef.IsValid())
2766 {
2767 printf("%s: Invalid currency for export or corrupt chain state\n", __func__);
2768 LogPrintf("%s: Invalid currency for export or corrupt chain state\n", __func__);
2769 return false;
2770 }
2771 oneCurID = oneCur.first;
2772 }
2773
2774 // if we are exporting a gateway currency back to its gateway, we do not store it in reserve deposits
2775 if (oneCurDef.IsGateway() && oneCurDef.systemID == ASSETCHAINS_CHAINID && oneCurDef.gatewayID == destSystemID)
2776 {
2777 continue;
2778 }
2779 else if (oneCurDef.systemID == ASSETCHAINS_CHAINID)
2780 {
2781 // exporting from one currency on this system to another currency on this system, store reserve deposits
2782 newReserveDeposits.valueMap[oneCur.first] += oneCur.second;
2783
2784 // TODO: if we're exporting to a gateway or other chain, ensure that our export currency is registered for import
2785 //
2786 }
2787 else if (destSystemID != oneCurDef.systemID)
2788 {
2789 printf("%s: Cannot export from one external currency system or PBaaS chain to another external system\n", __func__);
2790 LogPrintf("%s: Cannot export from one external currency system or PBaaS chain to another external system\n", __func__);
2791 return false;
2792 }
2793 }
2794 }
2795 else
2796 {
2797 // we should have no export from this system to this system directly
2798 assert(currencyID != ASSETCHAINS_CHAINID);
2799
2800 // when we export from this system to a specific currency on this system,
2801 // we record the reserve deposits for the destination currency to ensure they are available for imports
2802 // which take them as inputs.
2803 newReserveDeposits = totalExports;
2804 newReserveDeposits.valueMap.erase(currencyID);
2805 }
2806
2807 // now, we have:
2808 // 1) those transactions that we will take
2809 // 2) new reserve deposits for this export
2810 // 3) all transfer and expected conversion fees, including those in the second leg
2811 // 4) total of all currencies exported, whether fee, reserve deposit, or neither
2812 // 5) hash of all reserve transfers to be exported
2813 // 6) all reserve transfers are added to our transaction inputs
2814
2815 // next actions:
2816 // 1) create the export
2817 // 2) add reserve deposit output to transaction
2818 // 3) if destination currency is pre-launch, update notarization based on pre-conversions
2819 // 4) if fractional currency is target, check fees against minimums or targets based on conversion rates in currency
2820 // 5) if post launch, call AddReserveTransferImportOutputs to go from the old currency state and notarization to the new one
2821 // 6) if actual launch closure, make launch initiating export + initial notarization
2822
2823 // currencies that are going into this export and not being recorded as reserve deposits will
2824 // have been recorded on the other side and are being unwound. they should be considered
2825 // burned on this system.
2826
2827 // inputs can be:
2828 // 1. transfers of reserve or tokens for fractional reserve chains
2829 // 2. pre-conversions for pre-launch participation in the premine
2830 // 3. reserve market conversions
2831 //
2832
2833 //printf("%s: total export amounts:\n%s\n", __func__, totalAmounts.ToUniValue().write().c_str());
2834 CCrossChainExport ccx(ASSETCHAINS_CHAINID,
2835 sinceHeight,
2836 addHeight,
2837 destSystemID,
2838 currencyID,
f1a298ef 2839 exportTransfers.size(),
c8c684e9 2840 totalExports.CanonicalMap(),
2841 totalTransferFees.CanonicalMap(),
2842 transferHash,
2843 inputStartNum,
2844 DestinationToTransferDestination(feeOutput));
2845
4c5696b1 2846 ccx.SetPreLaunch(isPreLaunch);
2847 ccx.SetClearLaunch(isClearLaunchExport);
2848
c8c684e9 2849 // if we should add a system export, do so
2850 CCrossChainExport sysCCX;
2851 if (crossSystem)
2852 {
2853 assert(priorExports.size() == 2);
2854 COptCCParams p;
2855
2856 if (!(priorExports[1].scriptPubKey.IsPayToCryptoCondition(p) &&
2857 p.IsValid() &&
2858 p.evalCode == EVAL_CROSSCHAIN_EXPORT &&
2859 p.vData.size() &&
2860 (sysCCX = CCrossChainExport(p.vData[0])).IsValid()))
2861 {
2862 printf("%s: Invalid prior system export\n", __func__);
2863 LogPrintf("%s: Invalid prior system export\n", __func__);
2864 return false;
2865 }
2866 sysCCX = CCrossChainExport(ASSETCHAINS_CHAINID,
2867 sinceHeight,
2868 addHeight,
2869 destSystemID,
2870 destSystemID,
2871 txInputs.size(),
2872 totalExports.CanonicalMap(),
2873 totalTransferFees.CanonicalMap(),
2874 transferHash,
2875 inputStartNum,
2876 DestinationToTransferDestination(feeOutput),
2877 std::vector<CReserveTransfer>(),
2878 sysCCX.flags);
2879 }
2880
4c5696b1 2881 CAmount nativeReserveDeposit = 0;
c8c684e9 2882 if (newReserveDeposits.valueMap.count(ASSETCHAINS_CHAINID))
2883 {
2884 nativeReserveDeposit += newReserveDeposits.valueMap[ASSETCHAINS_CHAINID];
2885 newReserveDeposits.valueMap.erase(ASSETCHAINS_CHAINID);
2886 }
2887
2888 CCcontract_info CC;
2889 CCcontract_info *cp;
2890
2891 if (nativeReserveDeposit || newReserveDeposits.valueMap.size())
2892 {
2893 // now send transferred currencies to a reserve deposit
2894 cp = CCinit(&CC, EVAL_RESERVE_DEPOSIT);
2895
2896 // send the entire amount to a reserve deposit output of the specific chain
2897 // we receive our fee on the other chain, when it comes back, or if a token,
2898 // when it gets imported back to the chain
2899 std::vector<CTxDestination> dests({CPubKey(ParseHex(CC.CChexstr))});
2900 // if going off-system, reserve deposits accrue to the destination system, if same system, to the currency
2901 CReserveDeposit rd = CReserveDeposit(crossSystem ? destSystemID : currencyID, newReserveDeposits);
2902 exportOutputs.push_back(CTxOut(nativeReserveDeposit, MakeMofNCCScript(CConditionObj<CReserveDeposit>(EVAL_RESERVE_DEPOSIT, dests, 1, &rd))));
2903 }
2904
2905 int exportOutNum = exportOutputs.size();
2906
2907 cp = CCinit(&CC, EVAL_CROSSCHAIN_EXPORT);
2908 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
2909 exportOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &ccx))));
2910
2911 if (crossSystem)
2912 {
2913 exportOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CCrossChainExport>(EVAL_CROSSCHAIN_EXPORT, dests, 1, &sysCCX))));
2914 }
2915
2916 // now, if we are clearing launch, determine if we should refund or launch and set notarization appropriately
2917 if (isClearLaunchExport)
2918 {
2919 if (CCurrencyValueMap(_curDef.currencies, newNotarization.currencyState.reserves) < CCurrencyValueMap(_curDef.currencies, _curDef.minPreconvert))
2920 {
2921 newNotarization.currencyState.SetRefunding();
2922 }
2923 else
2924 {
2925 newNotarization.currencyState.SetLaunchClear();
2926
2927 // set final numbers for notarization
2928
7c8e5e18 2929 // if this is a PBaaS fractional gateway launch, the issued reserves for the gateway must be considered
c8c684e9 2930 // when calculating final prices
7c8e5e18 2931 std::map<uint160, int32_t> reserveMap;
2932 if (_curDef.IsFractional() && _curDef.currencies.size() > 1 &&
2933 (reserveMap = newNotarization.currencyState.GetReserveMap()).count(_curDef.systemID) &&
2934 _curDef.systemID != ASSETCHAINS_CHAINID)
2935 {
2936 CCurrencyDefinition systemDefForGateway = ConnectedChains.GetCachedCurrency(_curDef.systemID);
2937 if (!systemDefForGateway.IsValid())
2938 {
2939 printf("%s: Invalid launch system currency for fractional gateway converter\n", __func__);
2940 LogPrintf("%s: Invalid launch system currency for fractional gateway converter\n", __func__);
2941 return false;
2942 }
2943 uint160 scratchSysID = _curDef.systemID;
2944 if (systemDefForGateway.GetID(systemDefForGateway.gatewayConverterName, scratchSysID) == currencyID)
2945 {
2946 newNotarization.currencyState.reserves[reserveMap[_curDef.systemID]] = systemDefForGateway.gatewayConverterIssuance;
2947 }
2948
2949 // to get initial prices, do so without the native currency, then add it at launch
2950 CCoinbaseCurrencyState calcState = newNotarization.currencyState;
2951
2952 int nativeReserveIndex = reserveMap[_curDef.systemID];
2953 int32_t weightToDistribute = calcState.weights[nativeReserveIndex];
2954 calcState.currencies.erase(calcState.currencies.begin() + nativeReserveIndex);
2955 calcState.weights.erase(calcState.weights.begin() + nativeReserveIndex);
2956 calcState.reserves.erase(calcState.reserves.begin() + nativeReserveIndex);
2957
2958 // adjust weights for launch pricing
2959 int32_t divisor = calcState.currencies.size();
2960 int32_t dividedWeight = weightToDistribute / divisor;
2961 int32_t excessToDivide = weightToDistribute % divisor;
2962 for (auto &oneWeight : calcState.weights)
2963 {
2964 oneWeight += excessToDivide ? dividedWeight + excessToDivide-- : dividedWeight;
2965 }
2966
2967 std::vector<int64_t> adjustedPrices = newNotarization.currencyState.PricesInReserve();
2968 adjustedPrices.insert(adjustedPrices.begin() + nativeReserveIndex, newNotarization.currencyState.PriceInReserve(nativeReserveIndex));
2969 newNotarization.currencyState.conversionPrice = adjustedPrices;
2970 }
2971 else
2972 {
2973 newNotarization.currencyState.conversionPrice = newNotarization.currencyState.PricesInReserve();
2974 }
c8c684e9 2975 }
2976 }
2977 else if (!isPreLaunch)
2978 {
2979 // now, if we are not pre-launch, update the currency via simulated import
2980 CReserveTransactionDescriptor rtxd;
2981 std::vector<CTxOut> checkOutputs;
2982 CCurrencyValueMap importedCurrency;
2983 CCurrencyValueMap gatewayDepositsUsed;
2984 if (!rtxd.AddReserveTransferImportOutputs(ConnectedChains.ThisChain(),
2985 destSystem,
2986 _curDef,
2987 lastNotarization.currencyState,
2988 exportTransfers,
2989 checkOutputs,
2990 importedCurrency,
2991 gatewayDepositsUsed,
2992 spentCurrencyOut,
2993 &newNotarization.currencyState))
2994 {
2995 printf("%s: Viable import check failure\n", __func__);
2996 LogPrintf("%s: Viable import check failure\n", __func__);
2997 return false;
2998 }
2999 }
3000
3001 // all exports to a currency on this chain include a finalization that is spent by the import of this export
3002 // external systems and gateways get one finalization for their clear to launch export
3003 if (isClearLaunchExport || (destSystemID == ASSETCHAINS_CHAINID && addHeight >= _curDef.startBlock))
3004 {
3005 cp = CCinit(&CC, EVAL_FINALIZE_EXPORT);
3006
3007 // currency ID for finalization determine indexing and query capability
3008 // since we care about looking for all exports to one system or another, the currency in the finalization
3009 // is the system destination for this export (i.e. "which system should do the next work?")
3010 CObjectFinalization finalization(CObjectFinalization::FINALIZE_EXPORT, destSystemID, uint256(), exportOutNum);
3011
c8c684e9 3012 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
3013 exportOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CObjectFinalization>(EVAL_FINALIZE_EXPORT, dests, 1, &finalization))));
3014 }
3015
3016 // if this is a pre-launch export, including clear launch, update notarization and add it to the export outputs
98735a91 3017 if (isClearLaunchExport || isPreLaunch)
c8c684e9 3018 {
3019 // if we are exporting off chain, add a notarization proof root
3020 if (destSystemID != ASSETCHAINS_CHAINID)
3021 {
3022 newNotarization.notarizationHeight = addHeight;
3023 newNotarization.proofRoots[ASSETCHAINS_CHAINID] = CProofRoot::GetProofRoot(addHeight);
3024 }
3025
3026 // TODO: check to see if we are connected to a running PBaaS daemon for this currency waiting for it to start,
3027 // and if so, share nodes in the notarization
3028
4c5696b1 3029 newNotarizationOutNum = exportOutputs.size();
f93a9379 3030
c8c684e9 3031 cp = CCinit(&CC, EVAL_ACCEPTEDNOTARIZATION);
3032 dests = std::vector<CTxDestination>({CPubKey(ParseHex(CC.CChexstr)).GetID()});
3033 exportOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CPBaaSNotarization>(EVAL_ACCEPTEDNOTARIZATION, dests, 1, &newNotarization))));
3034 }
3035
3036 return true;
3037}
3038
3039void CConnectedChains::AggregateChainTransfers(const CTxDestination &feeOutput, uint32_t nHeight)
3040{
3041 // all chains aggregate reserve transfer transactions, so aggregate and add all necessary export transactions to the mem pool
3042 {
3043 if (!nHeight)
3044 {
3045 return;
3046 }
3047
3048 std::multimap<uint160, ChainTransferData> transferOutputs;
3049
3050 LOCK(cs_main);
9ace9b55 3051
56fe75cb 3052 uint160 thisChainID = ConnectedChains.ThisChain().GetID();
3053
7c8e5e18 3054 uint32_t nHeight = chainActive.Height();
3055
3056 // check for currencies that should launch in the last 20 blocks, haven't yet, and can have their launch export mined
3057 // if we find any that have no export creation pending, add it to imports
3058 std::vector<CAddressIndexDbEntry> rawCurrenciesToLaunch;
3059 std::map<uint160, std::pair<CCurrencyDefinition, CUTXORef>> launchCurrencies;
3060 if (GetAddressIndex(CCrossChainRPCData::GetConditionID(ASSETCHAINS_CHAINID, CCurrencyDefinition::CurrencyLaunchKey()),
3061 CScript::P2IDX,
3062 rawCurrenciesToLaunch,
3063 nHeight - 20 < 0 ? 0 : nHeight - 20,
3064 nHeight) &&
3065 rawCurrenciesToLaunch.size())
3066 {
3067 // add any unlaunched currencies as an output
3068 for (auto &oneDefIdx : rawCurrenciesToLaunch)
3069 {
3070 CTransaction defTx;
3071 uint256 hashBlk;
3072 COptCCParams p;
3073 CCurrencyDefinition oneDef;
3074 if (myGetTransaction(oneDefIdx.first.txhash, defTx, hashBlk) &&
3075 defTx.vout.size() > oneDefIdx.first.index &&
3076 defTx.vout[oneDefIdx.first.index].scriptPubKey.IsPayToCryptoCondition(p) &&
3077 p.IsValid() &&
3078 p.evalCode == EVAL_CURRENCY_DEFINITION &&
3079 p.vData.size() &&
3080 (oneDef = CCurrencyDefinition(p.vData[0])).IsValid() &&
3081 oneDef.systemID == ASSETCHAINS_CHAINID)
3082 {
3083 launchCurrencies.insert(std::make_pair(oneDef.GetID(), std::make_pair(oneDef, CUTXORef())));
3084 }
3085 }
3086 }
3087
c3250dcd
MT
3088 // get all available transfer outputs to aggregate into export transactions
3089 if (GetUnspentChainTransfers(transferOutputs))
3090 {
7c8e5e18 3091 if (!(transferOutputs.size() || launchCurrencies.size()))
bb6c3482 3092 {
3093 return;
3094 }
815a42a6 3095
ec1b7e23 3096 std::vector<ChainTransferData> txInputs;
98735a91 3097 uint160 lastChain = transferOutputs.size() ? transferOutputs.begin()->first : launchCurrencies.begin()->second.first.GetID();
815a42a6 3098
efbf1423 3099 CCoins coins;
3100 CCoinsView dummy;
3101 CCoinsViewCache view(&dummy);
3102
3103 LOCK(mempool.cs);
3104
3105 CCoinsViewMemPool viewMemPool(pcoinsTip, mempool);
3106 view.SetBackend(viewMemPool);
3107
4f8f2b54 3108 auto outputIt = transferOutputs.begin();
7c8e5e18 3109 bool checkLaunchCurrencies = false;
4c5696b1 3110 for (int outputsDone = 0;
f1a298ef 3111 outputsDone <= transferOutputs.size() || launchCurrencies.size();
4c5696b1 3112 outputsDone++)
56fe75cb 3113 {
4f8f2b54 3114 if (outputIt != transferOutputs.end())
56fe75cb 3115 {
4f8f2b54 3116 auto &output = *outputIt;
3117 if (output.first == lastChain)
ea574fe2 3118 {
4f8f2b54 3119 txInputs.push_back(output.second);
4c5696b1 3120 outputIt++;
ea574fe2 3121 continue;
3122 }
4f8f2b54 3123 }
98735a91 3124 else if (checkLaunchCurrencies || !transferOutputs.size())
7c8e5e18 3125 {
3126 // we are done with all natural exports and have deleted any launch entries that had natural exports,
3127 // since they should also handle launch naturally.
3128 // if we have launch currencies that have not been launched and do not have associated
3129 // transfer outputs, force launch them
3130 std::vector<uint160> toErase;
3131 for (auto &oneLaunchCur : launchCurrencies)
3132 {
3133 CChainNotarizationData cnd;
3134 std::vector<std::pair<CTransaction, uint256>> txes;
3135
3136 if (!(GetNotarizationData(oneLaunchCur.first, cnd, &txes) &&
98735a91 3137 cnd.vtx[cnd.lastConfirmed].second.IsPreLaunch()))
7c8e5e18 3138 {
3139 toErase.push_back(oneLaunchCur.first);
3140 }
3141 }
3142 for (auto &oneToErase : toErase)
3143 {
3144 launchCurrencies.erase(oneToErase);
3145 }
3146 }
3147 else
3148 {
98735a91 3149 // this is when we have to finish one round and then continue with currency launches
7c8e5e18 3150 checkLaunchCurrencies = launchCurrencies.size() != 0;
3151 }
ea574fe2 3152
4f8f2b54 3153 CCurrencyDefinition destDef, systemDef;
56fe75cb 3154
4f8f2b54 3155 destDef = GetCachedCurrency(lastChain);
ea574fe2 3156
c8c684e9 3157 if (destDef.systemID == thisChainID)
4f8f2b54 3158 {
c8c684e9 3159 if (destDef.IsGateway())
3160 {
3161 // if the currency is a gateway on this system, any exports go through it to the gateway, not the system ID
3162 systemDef = GetCachedCurrency(destDef.gatewayID);
3163 }
3164 else
3165 {
3166 systemDef = destDef;
3167 }
4f8f2b54 3168 }
c8c684e9 3169 else
4f8f2b54 3170 {
c8c684e9 3171 systemDef = GetCachedCurrency(destDef.systemID);
56fe75cb 3172 }
c3250dcd 3173
c8c684e9 3174 if (!destDef.IsValid())
c3250dcd 3175 {
c8c684e9 3176 printf("%s: cannot find destination currency %s\n", __func__, EncodeDestination(CIdentityID(lastChain)).c_str());
3177 LogPrintf("%s: cannot find destination currency %s\n", __func__, EncodeDestination(CIdentityID(lastChain)).c_str());
4c5696b1 3178 break;
c8c684e9 3179 }
3180 if (!systemDef.IsValid())
3181 {
3182 printf("%s: cannot find destination system definition %s\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
3183 LogPrintf("%s: cannot find destination system definition %s\n", __func__, EncodeDestination(CIdentityID(destDef.systemID)).c_str());
4c5696b1 3184 break;
78a36746 3185 }
4f8f2b54 3186
0338564c 3187 bool isSameChain = destDef.systemID == thisChainID;
c3250dcd 3188
c8c684e9 3189 // when we get here, we have a consecutive number of transfer outputs to consume in txInputs
3190 // we need an unspent export output to export, or use the last one of it is an export to the same
3191 // system
3192 std::vector<std::pair<int, CInputDescriptor>> exportOutputs;
3193 std::vector<std::pair<int, CInputDescriptor>> sysExportOutputs;
3194 std::vector<CInputDescriptor> allExportOutputs;
3195 CCurrencyDefinition lastChainDef = GetCachedCurrency(lastChain);
56fe75cb 3196
c8c684e9 3197 // export outputs must come from the latest, including mempool, to ensure
3198 // enforcement of sequential exports. get unspent currency export, and if not on the current
3199 // system, the external system export as well
c3250dcd 3200
c8c684e9 3201 if ((ConnectedChains.GetUnspentCurrencyExports(view, lastChain, exportOutputs) && exportOutputs.size()) &&
3202 (isSameChain || ConnectedChains.GetUnspentCurrencyExports(view, lastChainDef.systemID, sysExportOutputs) && sysExportOutputs.size()))
3203 {
3204 assert(exportOutputs.size() == 1);
3205 std::pair<int, CInputDescriptor> lastExport = exportOutputs[0];
3206 allExportOutputs.push_back(lastExport.second);
3207 std::pair<int, CInputDescriptor> lastSysExport = std::make_pair(-1, CInputDescriptor());
3208 if (!isSameChain)
3209 {
3210 lastSysExport = sysExportOutputs[0];
3211 allExportOutputs.push_back(lastSysExport.second);
3212 }
c3250dcd 3213
c8c684e9 3214 COptCCParams p;
3215 CCrossChainExport ccx, sysCCX;
3216 if (!(lastExport.second.scriptPubKey.IsPayToCryptoCondition(p) &&
3217 p.IsValid() &&
3218 p.evalCode == EVAL_CROSSCHAIN_EXPORT &&
3219 p.vData.size() &&
3220 (ccx = CCrossChainExport(p.vData[0])).IsValid()) ||
3221 !(isSameChain ||
3222 (lastSysExport.second.scriptPubKey.IsPayToCryptoCondition(p) &&
3223 p.IsValid() &&
3224 p.evalCode != EVAL_CROSSCHAIN_EXPORT &&
3225 p.vData.size() &&
3226 (sysCCX = CCrossChainExport(p.vData[0])).IsValid())))
3227 {
3228 printf("%s: invalid export(s) for %s in index\n", __func__, EncodeDestination(CIdentityID(lastChainDef.GetID())).c_str());
3229 LogPrintf("%s: invalid export(s) for %s in index\n", __func__, EncodeDestination(CIdentityID(lastChainDef.GetID())).c_str());
4c5696b1 3230 break;
c8c684e9 3231 }
cadf8969 3232
c8c684e9 3233 // now, we have the previous export to this currency/system, which we should spend to
3234 // enable this new export. if we find no export, we're done
3235 int32_t numInputsUsed;
3236 std::vector<CTxOut> exportTxOuts;
3237 std::vector<CReserveTransfer> exportTransfers;
3238 CChainNotarizationData cnd;
3239
3240 // get notarization for the actual currency we are exporting
3241 if (!GetNotarizationData(lastChain, cnd) || cnd.lastConfirmed == -1)
3242 {
3243 printf("%s: missing or invalid notarization for %s\n", __func__, EncodeDestination(CIdentityID(lastChainDef.GetID())).c_str());
3244 LogPrintf("%s: missing or invalid notarization for %s\n", __func__, EncodeDestination(CIdentityID(lastChainDef.GetID())).c_str());
4c5696b1 3245 break;
c8c684e9 3246 }
481e7fd6 3247
c8c684e9 3248 CPBaaSNotarization lastNotarization = cnd.vtx[cnd.lastConfirmed].second;
f93a9379 3249 CUTXORef lastNotarizationUTXO = cnd.vtx[cnd.lastConfirmed].first;
c8c684e9 3250 CPBaaSNotarization newNotarization;
f93a9379 3251 int newNotarizationOutNum;
c3250dcd 3252
7c8e5e18 3253 while (txInputs.size() || launchCurrencies.count(lastChain))
c8c684e9 3254 {
7081a8d9 3255 launchCurrencies.erase(lastChain);
3256
7c8e5e18 3257 // even if we have no txInputs, currencies that need to will launch
f93a9379 3258 newNotarizationOutNum = -1;
c8c684e9 3259 if (!CConnectedChains::CreateNextExport(lastChainDef,
3260 txInputs,
3261 allExportOutputs,
3262 feeOutput,
3263 ccx.sourceHeightEnd,
3264 nHeight,
3265 isSameChain ? 1 : 2, // reserve transfers start at input 1 on same chain or after sys
3266 numInputsUsed,
3267 exportTxOuts,
3268 exportTransfers,
3269 lastNotarization,
f93a9379 3270 lastNotarizationUTXO,
3271 newNotarization,
3272 newNotarizationOutNum))
c8c684e9 3273 {
3274 printf("%s: unable to create export for %s\n", __func__, EncodeDestination(CIdentityID(lastChainDef.GetID())).c_str());
3275 LogPrintf("%s: unable to create export for %s\n", __func__, EncodeDestination(CIdentityID(lastChainDef.GetID())).c_str());
f93a9379 3276 break;
c8c684e9 3277 }
c3250dcd 3278
c8c684e9 3279 // now, if we have created any outputs, we have a transaction to make, if not, we are done
3280 if (!exportTxOuts.size())
3281 {
3282 txInputs.clear();
3283 break;
3284 }
c3250dcd 3285
c8c684e9 3286 TransactionBuilder tb(Params().GetConsensus(), nHeight);
3287 tb.SetFee(0);
c3250dcd 3288
c8c684e9 3289 // add input from last export, all consumed txInputs, and all outputs created to make
3290 // the new export tx. since we are exporting from this chain
05cef42a 3291
4c5696b1 3292 /* UniValue scriptUniOut;
3293 ScriptPubKeyToUniv(lastExport.second.scriptPubKey, scriptUniOut, false);
3294 printf("adding input %d with %ld nValue and script:\n%s\n", (int)tb.mtx.vin.size(), lastExport.second.nValue, scriptUniOut.write(1,2).c_str());
3295 */
3296
c8c684e9 3297 // first add previous export
3298 tb.AddTransparentInput(lastExport.second.txIn.prevout, lastExport.second.scriptPubKey, lastExport.second.nValue);
05cef42a 3299
c8c684e9 3300 // if going to another system, add the system export thread as well
3301 if (!isSameChain)
3302 {
4c5696b1 3303
3304 /* scriptUniOut = UniValue(UniValue::VOBJ);
3305 ScriptPubKeyToUniv(lastSysExport.second.scriptPubKey, scriptUniOut, false);
3306 printf("adding input %d with %ld nValue and script:\n%s\n", (int)tb.mtx.vin.size(), lastSysExport.second.nValue, scriptUniOut.write(1,2).c_str());
3307 */
3308
c8c684e9 3309 tb.AddTransparentInput(lastSysExport.second.txIn.prevout, lastSysExport.second.scriptPubKey, lastSysExport.second.nValue);
3310 }
09b977c6 3311
c8c684e9 3312 // now, all reserve transfers
3313 for (int i = 0; i < numInputsUsed; i++)
3314 {
3315 CInputDescriptor inputDesc = std::get<1>(txInputs[i]);
4c5696b1 3316
3317 /* scriptUniOut = UniValue(UniValue::VOBJ);
3318 ScriptPubKeyToUniv(inputDesc.scriptPubKey, scriptUniOut, false);
3319 printf("adding input %d with %ld nValue and script:\n%s\n", (int)tb.mtx.vin.size(), inputDesc.nValue, scriptUniOut.write(1,2).c_str());
3320 */
3321
c8c684e9 3322 tb.AddTransparentInput(inputDesc.txIn.prevout, inputDesc.scriptPubKey, inputDesc.nValue);
3323 }
c3250dcd 3324
c8c684e9 3325 // now, add all outputs to the transaction
3326 auto thisExport = lastExport;
3327 int outputNum = tb.mtx.vout.size();
3328 for (auto &oneOut : exportTxOuts)
3329 {
3330 COptCCParams xp;
3331 if (oneOut.scriptPubKey.IsPayToCryptoCondition(xp) && xp.IsValid() && xp.evalCode == EVAL_CROSSCHAIN_EXPORT)
3332 {
3333 thisExport.second.scriptPubKey = oneOut.scriptPubKey;
3334 thisExport.second.nValue = oneOut.nValue;
3335 thisExport.first = nHeight;
3336 thisExport.second.txIn.prevout.n = outputNum;
3337 }
4c5696b1 3338
3339 /* scriptUniOut = UniValue(UniValue::VOBJ);
3340 ScriptPubKeyToUniv(oneOut.scriptPubKey, scriptUniOut, false);
3341 printf("adding output %d with %ld nValue and script:\n%s\n", (int)tb.mtx.vout.size(), oneOut.nValue, scriptUniOut.write(1,2).c_str());
3342 */
3343
c8c684e9 3344 tb.AddTransparentOutput(oneOut.scriptPubKey, oneOut.nValue);
3345 outputNum++;
3346 }
cadf8969 3347
c8c684e9 3348 TransactionBuilderResult buildResult(tb.Build());
c3250dcd 3349
c8c684e9 3350 if (!buildResult.IsError() && buildResult.IsTx())
3351 {
3352 // replace the last one only if we have a valid new one
3353 CTransaction tx = buildResult.GetTxOrThrow();
3354
f93a9379 3355 if (newNotarizationOutNum >= 0)
3356 {
3357 lastNotarization = newNotarization;
3358 lastNotarizationUTXO.hash = tx.GetHash();
3359 lastNotarizationUTXO.n = newNotarizationOutNum;
3360 }
3361
c8c684e9 3362 /* uni = UniValue(UniValue::VOBJ);
3363 TxToUniv(tx, uint256(), uni);
3364 printf("%s: successfully built tx:\n%s\n", __func__, uni.write(1,2).c_str()); */
3365
3366 LOCK2(cs_main, mempool.cs);
3367 static int lastHeight = 0;
3368 // remove conflicts, so that we get in
3369 std::list<CTransaction> removed;
3370 mempool.removeConflicts(tx, removed);
3371
3372 // add to mem pool, prioritize according to the fee we will get, and relay
3373 //printf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
3374 //LogPrintf("Created and signed export transaction %s\n", tx.GetHash().GetHex().c_str());
3375 if (myAddtomempool(tx))
3376 {
3377 uint256 hash = tx.GetHash();
3378 thisExport.second.txIn.prevout.hash = hash;
3379 lastExport = thisExport;
4c5696b1 3380 CAmount nativeExportFees = ccx.totalFees.valueMap[ASSETCHAINS_CHAINID] ? ccx.totalFees.valueMap[ASSETCHAINS_CHAINID] : 10000;
c8c684e9 3381 mempool.PrioritiseTransaction(hash, hash.GetHex(), (double)(nativeExportFees << 1), nativeExportFees);
c3250dcd 3382 }
c8c684e9 3383 else
3384 {
3385 UniValue uni(UniValue::VOBJ);
3386 TxToUniv(tx, uint256(), uni);
3387 //printf("%s: created invalid transaction:\n%s\n", __func__, uni.write(1,2).c_str());
3388 LogPrintf("%s: created invalid transaction:\n%s\n", __func__, uni.write(1,2).c_str());
3389 break;
3390 }
3391 }
3392 else
3393 {
3394 // we can't do any more useful work for this chain if we failed here
3395 printf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
3396 LogPrintf("Failed to create export transaction: %s\n", buildResult.GetError().c_str());
3397 break;
c3250dcd 3398 }
c8c684e9 3399
3400 // erase the inputs we've attempted to spend and loop for another export tx
3401 txInputs.erase(txInputs.begin(), txInputs.begin() + numInputsUsed);
c3250dcd
MT
3402 }
3403 }
4f8f2b54 3404 txInputs.clear();
3405 if (outputIt != transferOutputs.end())
3406 {
3407 lastChain = outputIt->first;
3408 txInputs.push_back(outputIt->second);
4c5696b1 3409 outputIt++;
4f8f2b54 3410 }
c3250dcd 3411 }
0ab273d2 3412 CheckImports();
3413 }
3414 }
3415}
3416
3417void CConnectedChains::SignAndCommitImportTransactions(const CTransaction &lastImportTx, const std::vector<CTransaction> &transactions)
3418{
995a2fd0 3419 int nHeight = chainActive.LastTip()->GetHeight();
3420 uint32_t consensusBranchId = CurrentEpochBranchId(nHeight, Params().GetConsensus());
0ab273d2 3421 LOCK2(cs_main, mempool.cs);
3422
995a2fd0 3423 uint256 lastHash, lastSignedHash;
3424 CCoinsViewCache view(pcoinsTip);
3425
0ab273d2 3426 // sign and commit the transactions
995a2fd0 3427 for (auto &_tx : transactions)
0ab273d2 3428 {
995a2fd0 3429 CMutableTransaction newTx(_tx);
09551a1c 3430
995a2fd0 3431 if (!lastHash.IsNull())
3432 {
3433 //printf("last hash before signing: %s\n", lastHash.GetHex().c_str());
3434 for (auto &oneIn : newTx.vin)
3435 {
3436 //printf("checking input with hash: %s\n", oneIn.prevout.hash.GetHex().c_str());
3437 if (oneIn.prevout.hash == lastHash)
3438 {
3439 oneIn.prevout.hash = lastSignedHash;
3440 //printf("updated hash before signing: %s\n", lastSignedHash.GetHex().c_str());
3441 }
3442 }
3443 }
3444 lastHash = _tx.GetHash();
3445 CTransaction tx = newTx;
0ab273d2 3446
3447 // sign the transaction and submit
995a2fd0 3448 bool signSuccess = false;
0ab273d2 3449 for (int i = 0; i < tx.vin.size(); i++)
3450 {
3451 SignatureData sigdata;
3452 CAmount value;
3453 CScript outputScript;
3454
3455 if (tx.vin[i].prevout.hash == lastImportTx.GetHash())
3456 {
3457 value = lastImportTx.vout[tx.vin[i].prevout.n].nValue;
3458 outputScript = lastImportTx.vout[tx.vin[i].prevout.n].scriptPubKey;
3459 }
3460 else
3461 {
0ab273d2 3462 CCoins coins;
3463 if (!view.GetCoins(tx.vin[i].prevout.hash, coins))
3464 {
3465 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);
3466 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);
3467 break;
3468 }
3469 value = coins.vout[tx.vin[i].prevout.n].nValue;
3470 outputScript = coins.vout[tx.vin[i].prevout.n].scriptPubKey;
3471 }
3472
3473 signSuccess = ProduceSignature(TransactionSignatureCreator(nullptr, &tx, i, value, SIGHASH_ALL), outputScript, sigdata, consensusBranchId);
3474
3475 if (!signSuccess)
3476 {
8192d12f 3477 fprintf(stderr,"%s: failure to sign transaction\n", __func__);
3478 LogPrintf("%s: failure to sign transaction\n", __func__);
0ab273d2 3479 break;
3480 } else {
3481 UpdateTransaction(newTx, i, sigdata);
3482 }
3483 }
3484
3485 if (signSuccess)
3486 {
3487 // push to local node and sync with wallets
3488 CValidationState state;
3489 bool fMissingInputs;
3490 CTransaction signedTx(newTx);
09551a1c 3491
3492 //DEBUGGING
58b4e7b5 3493 //TxToJSON(tx, uint256(), jsonTX);
3494 //printf("signed transaction:\n%s\n", jsonTX.write(1, 2).c_str());
09551a1c 3495
0ab273d2 3496 if (!AcceptToMemoryPool(mempool, state, signedTx, false, &fMissingInputs)) {
3497 if (state.IsInvalid()) {
3ad32b99 3498 //UniValue txUni(UniValue::VOBJ);
3499 //TxToUniv(signedTx, uint256(), txUni);
3500 //fprintf(stderr,"%s: rejected by memory pool for %s\n%s\n", __func__, state.GetRejectReason().c_str(), txUni.write(1,2).c_str());
0ab273d2 3501 LogPrintf("%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
3502 } else {
3503 if (fMissingInputs) {
3504 fprintf(stderr,"%s: missing inputs\n", __func__);
3505 LogPrintf("%s: missing inputs\n", __func__);
3506 }
3507 else
3508 {
cd334056 3509 fprintf(stderr,"%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
3510 LogPrintf("%s: rejected by memory pool for %s\n", __func__, state.GetRejectReason().c_str());
0ab273d2 3511 }
3512 }
3513 break;
3514 }
995a2fd0 3515 else
3516 {
3517 UpdateCoins(signedTx, view, nHeight);
3518 lastSignedHash = signedTx.GetHash();
3519 }
3520 }
3521 else
3522 {
3523 break;
c3250dcd
MT
3524 }
3525 }
3526}
3527
05cef42a 3528// process token related, local imports and exports
3529void CConnectedChains::ProcessLocalImports()
3530{
c8c684e9 3531 // first determine all exports to the current/same system marked for action
3532 // next, get the last import of each export thread, package all pending exports,
3533 // and call CreateLatestImports
3534
3535 std::vector<std::pair<std::pair<CInputDescriptor,CPartialTransactionProof>,std::vector<CReserveTransfer>>> exportsOut;
05cef42a 3536 uint160 thisChainID = thisChain.GetID();
0ab273d2 3537
05cef42a 3538 LOCK2(cs_main, mempool.cs);
3539 uint32_t nHeight = chainActive.Height();
0ab273d2 3540
05cef42a 3541 std::vector<std::pair<CAddressUnspentKey, CAddressUnspentValue>> unspentOutputs;
c8c684e9 3542 std::set<uint160> currenciesProcessed;
3543 uint160 finalizeExportKey(CCrossChainRPCData::GetConditionID(ASSETCHAINS_CHAINID, CObjectFinalization::ObjectFinalizationExportKey()));
ea574fe2 3544
c8c684e9 3545 if (GetAddressUnspent(finalizeExportKey, CScript::P2IDX, unspentOutputs))
05cef42a 3546 {
c8c684e9 3547 // now, we have a list of all currencies with exports that have work to do
3548 // export finalizations are either on the same transaction as the export, or in the case of a clear launch export,
3549 // there may be any number of pre-launch exports still to process prior to spending it
3550 for (auto &oneFinalization : unspentOutputs)
05cef42a 3551 {
3552 COptCCParams p;
c8c684e9 3553 CObjectFinalization of;
3554 CCrossChainExport ccx;
3555 CCrossChainImport cci;
3556 CTransaction scratchTx;
3557 int32_t importOutputNum;
3558 uint256 hashBlock;
3559 std::vector<std::pair<std::pair<CInputDescriptor,CPartialTransactionProof>,std::vector<CReserveTransfer>>> exportsFound;
3560 if (oneFinalization.second.script.IsPayToCryptoCondition(p) &&
05cef42a 3561 p.IsValid() &&
3562 p.evalCode == EVAL_FINALIZE_EXPORT &&
3563 p.vData.size() &&
c8c684e9 3564 (of = CObjectFinalization(p.vData[0])).IsValid() &&
3565 myGetTransaction(of.output.hash.IsNull() ? oneFinalization.first.txhash : of.output.hash, scratchTx, hashBlock) &&
3566 scratchTx.vout.size() > of.output.n &&
3567 scratchTx.vout[of.output.n].scriptPubKey.IsPayToCryptoCondition(p) &&
3568 p.IsValid() &&
3569 p.evalCode == EVAL_CROSSCHAIN_EXPORT &&
3570 p.vData.size() &&
3571 (ccx = CCrossChainExport(p.vData[0])).IsValid() &&
3572 !currenciesProcessed.count(ccx.destCurrencyID) &&
3573 GetLastImport(ccx.destCurrencyID, scratchTx, importOutputNum) &&
3574 scratchTx.vout.size() > importOutputNum &&
3575 scratchTx.vout[importOutputNum].scriptPubKey.IsPayToCryptoCondition(p) &&
3576 p.IsValid() &&
3577 p.evalCode == EVAL_CROSSCHAIN_IMPORT &&
3578 p.vData.size() &&
3579 (cci = CCrossChainImport(p.vData[0])).IsValid() &&
3580 GetCurrencyExports(ccx.destCurrencyID, exportsFound, cci.sourceSystemHeight, nHeight))
0ab273d2 3581 {
4c5696b1 3582 uint256 cciExportTxHash = cci.exportTxId.IsNull() ? scratchTx.GetHash() : cci.exportTxId;
c8c684e9 3583 if (exportsFound.size())
0ab273d2 3584 {
c8c684e9 3585 // make sure we start from the first export not imported and skip the rest
3586 auto startingIt = exportsFound.begin();
3587 for ( ; startingIt != exportsFound.end(); startingIt++)
0ab273d2 3588 {
4c5696b1 3589 // if this is the first. then the first is the one we will always use
3590 if (cci.IsDefinitionImport())
3591 {
3592 break;
3593 }
3594 if (startingIt->first.first.txIn.prevout.hash == cciExportTxHash && startingIt->first.first.txIn.prevout.n == cci.exportTxOutNum)
0ab273d2 3595 {
c8c684e9 3596 startingIt++;
3597 break;
0ab273d2 3598 }
0ab273d2 3599 }
c8c684e9 3600 exportsOut.insert(exportsOut.end(), startingIt, exportsFound.end());
0ab273d2 3601 }
c8c684e9 3602 currenciesProcessed.insert(ccx.destCurrencyID);
0ab273d2 3603 }
3604 }
3605 }
c8c684e9 3606
3607 std::map<uint160, std::vector<std::pair<int, CTransaction>>> newImports;
3608 if (exportsOut.size())
3609 {
3610 CreateLatestImports(thisChain, CUTXORef(), exportsOut, newImports);
3611 }
c3250dcd
MT
3612}
3613
b2a98c42
MT
3614void CConnectedChains::SubmissionThread()
3615{
3616 try
3617 {
3618 arith_uint256 lastHash;
687e93d5
MT
3619 int64_t lastImportTime = 0;
3620 uint32_t lastHeight = 0;
b2a98c42 3621
a82942e4 3622 // wait for something to check on, then submit blocks that should be submitted
b2a98c42
MT
3623 while (true)
3624 {
c3250dcd
MT
3625 boost::this_thread::interruption_point();
3626
9f0c14b2 3627 if (IsVerusActive())
b2a98c42 3628 {
23d61f0a 3629 // blocks get discarded after no refresh for 5 minutes by default, probably should be more often
9014248c 3630 //printf("SubmissionThread: pruning\n");
c3250dcd 3631 PruneOldChains(GetAdjustedTime() - 300);
2fd1f0fb 3632 bool submit = false;
b2a98c42 3633 {
2fd1f0fb 3634 LOCK(cs_mergemining);
bea3e6a2
MT
3635 if (mergeMinedChains.size() == 0 && qualifiedHeaders.size() != 0)
3636 {
326f5f88 3637 qualifiedHeaders.clear();
bea3e6a2 3638 }
2fd1f0fb 3639 submit = qualifiedHeaders.size() != 0 && mergeMinedChains.size() != 0;
23d61f0a 3640
9014248c 3641 //printf("SubmissionThread: qualifiedHeaders.size(): %lu, mergeMinedChains.size(): %lu\n", qualifiedHeaders.size(), mergeMinedChains.size());
2fd1f0fb 3642 }
3643 if (submit)
3644 {
9014248c 3645 //printf("SubmissionThread: calling submit qualified blocks\n");
2fd1f0fb 3646 SubmitQualifiedBlocks();
b2a98c42 3647 }
8192d12f 3648
8192d12f 3649 if (!submit)
9f0c14b2 3650 {
2fd1f0fb 3651 sem_submitthread.wait();
9f0c14b2 3652 }
b2a98c42 3653 }
c8c684e9 3654 // if this is a PBaaS chain, poll for presence of Verus / root chain and current Verus block and version number
3655 if (IsNotaryAvailable())
b2a98c42 3656 {
c8c684e9 3657 // check to see if we have recently earned a block with an earned notarization that qualifies for
3658 // submitting an accepted notarization
3659 if (earnedNotarizationHeight)
2830db29 3660 {
c8c684e9 3661 CBlock blk;
3662 int32_t txIndex = -1, height;
572c763f 3663 {
c8c684e9 3664 LOCK(cs_mergemining);
3665 if (earnedNotarizationHeight && earnedNotarizationHeight <= chainActive.Height() && earnedNotarizationBlock.GetHash() == chainActive[earnedNotarizationHeight]->GetBlockHash())
d6bc5de8 3666 {
c8c684e9 3667 blk = earnedNotarizationBlock;
3668 earnedNotarizationBlock = CBlock();
3669 txIndex = earnedNotarizationIndex;
3670 height = earnedNotarizationHeight;
3671 earnedNotarizationHeight = 0;
d6bc5de8 3672 }
572c763f 3673 }
687e93d5 3674
c8c684e9 3675 if (txIndex != -1)
5a72525a 3676 {
c8c684e9 3677 LOCK(cs_main);
3678 //printf("SubmissionThread: testing notarization\n");
3679 CTransaction lastConfirmed;
3680 CPBaaSNotarization notarization;
3681 CNotaryEvidence evidence;
3682 CValidationState state;
3683 TransactionBuilder txBuilder(Params().GetConsensus(), chainActive.Height());
3684 // get latest earned notarization
3685
3686 bool success = notarization.CreateAcceptedNotarization(ConnectedChains.FirstNotaryChain().chainDefinition,
3687 notarization,
3688 evidence,
3689 state,
3690 txBuilder);
3691
3692 if (success)
5a72525a 3693 {
c8c684e9 3694 //printf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
3695 //LogPrintf("Submitted notarization for acceptance: %s\n", txId.GetHex().c_str());
687e93d5
MT
3696 }
3697 }
3698 }
c8c684e9 3699
3700 // every "n" seconds, look for imports to include in our blocks from the notary chain
3701 if ((GetAdjustedTime() - lastImportTime) >= 30 || lastHeight < (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight()))
3702 {
3703 lastImportTime = GetAdjustedTime();
3704 lastHeight = (chainActive.LastTip() ? 0 : chainActive.LastTip()->GetHeight());
3705
3706 // see if our notary has a confirmed notarization for us
3707 UniValue params(UniValue::VARR);
3708 UniValue result;
3709 }
b2a98c42 3710 }
c8c684e9 3711 sleep(3);
b2a98c42
MT
3712 boost::this_thread::interruption_point();
3713 }
3714 }
3715 catch (const boost::thread_interrupted&)
3716 {
3717 LogPrintf("Verus merge mining thread terminated\n");
3718 }
3719}
9f0c14b2 3720
b2a98c42
MT
3721void CConnectedChains::SubmissionThreadStub()
3722{
3723 ConnectedChains.SubmissionThread();
3724}
3725
572c763f
MT
3726void CConnectedChains::QueueEarnedNotarization(CBlock &blk, int32_t txIndex, int32_t height)
3727{
3728 // called after winning a block that contains an earned notarization
3729 // the earned notarization and its height are queued for processing by the submission thread
3730 // when a new notarization is added, older notarizations are removed, but all notarizations in the current height are
3731 // kept
3732 LOCK(cs_mergemining);
3733
94a210aa 3734 // we only care about the last
572c763f 3735 earnedNotarizationHeight = height;
94a210aa
MT
3736 earnedNotarizationBlock = blk;
3737 earnedNotarizationIndex = txIndex;
572c763f
MT
3738}
3739
68b309c0
MT
3740bool IsChainDefinitionInput(const CScript &scriptSig)
3741{
3742 uint32_t ecode;
56fe75cb 3743 return scriptSig.IsPayToCryptoCondition(&ecode) && ecode == EVAL_CURRENCY_DEFINITION;
68b309c0 3744}
13ed2980 3745
This page took 0.648494 seconds and 4 git commands to generate.