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