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