]> Git Repo - VerusCoin.git/blame - src/pbaas/reserves.cpp
Fix PBaaS launch up to completion
[VerusCoin.git] / src / pbaas / reserves.cpp
CommitLineData
a6e612cc
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 reserve currency functions, leveraging the multi-precision boost libraries to calculate reserve currency conversions.
8 *
9 */
10
11#include "main.h"
e7e14f44 12#include "pbaas/pbaas.h"
a6e612cc 13#include "pbaas/reserves.h"
56fe75cb 14#include "pbaas/notarization.h"
715182a4 15#include "rpc/server.h"
41f170fd 16#include "key_io.h"
56fe75cb 17#include <random>
41f170fd 18
c8c684e9 19
56fe75cb 20CTokenOutput::CTokenOutput(const UniValue &obj)
21{
22 nVersion = (uint32_t)uni_get_int(find_value(obj, "version"), VERSION_CURRENT);
c8c684e9 23 UniValue values = find_value(obj, "currencyvalues");
24 if (values.isObject())
56fe75cb 25 {
c8c684e9 26 reserveValues = CCurrencyValueMap(values);
56fe75cb 27 }
28}
29
c8c684e9 30// calculate fees required in one currency to pay in another
ef75db7d 31CAmount CReserveTransfer::CalculateTransferFee(const CTransferDestination &destination, uint32_t flags)
4d62dbc5 32{
5d11fb7a 33 if ((flags & FEE_OUTPUT) || (!(flags & PRECONVERT) && (flags & CONVERT)))
34 {
35 return 0;
36 }
cfafec9e 37 return CReserveTransfer::DEFAULT_PER_STEP_FEE << 1 + ((CReserveTransfer::DEFAULT_PER_STEP_FEE << 1) * (destination.destination.size() / DESTINATION_BYTE_DIVISOR));
4d62dbc5 38}
39
cfafec9e 40CAmount CReserveTransfer::CalculateTransferFee() const
951413c7 41{
42 // determine fee for this send
c8c684e9 43 return CalculateTransferFee(destination, flags);
44}
45
46CCurrencyValueMap CReserveTransfer::TotalTransferFee() const
47{
48 CCurrencyValueMap retVal;
49 CAmount transferFee = nFees;
50 if (destination.HasGatewayLeg() && destination.fees)
951413c7 51 {
c8c684e9 52 transferFee += destination.fees;
951413c7 53 }
c8c684e9 54 retVal.valueMap[feeCurrencyID] += transferFee;
55 return retVal;
cfafec9e 56}
951413c7 57
c8c684e9 58CCurrencyValueMap CReserveTransfer::ConversionFee() const
59{
60 CCurrencyValueMap retVal;
61 // add conversion fees in source currency for conversions or pre-conversions
62 if (IsConversion() || IsPreConversion())
63 {
64 for (auto &oneCur : reserveValues.valueMap)
65 {
66 retVal.valueMap[oneCur.first] += CReserveTransactionDescriptor::CalculateConversionFee(oneCur.second);
67 }
68 if (IsReserveToReserve())
69 {
70 retVal = retVal * 2;
71 }
72 }
73 return retVal;
74}
75
76CCurrencyValueMap CReserveTransfer::CalculateFee(uint32_t flags, CAmount transferTotal) const
cfafec9e 77{
78 CCurrencyValueMap feeMap;
56fe75cb 79
c8c684e9 80 feeMap.valueMap[feeCurrencyID] = CalculateTransferFee();
cfafec9e 81
82 // add conversion fees in source currency for conversions or pre-conversions
83 if (IsConversion() || IsPreConversion())
951413c7 84 {
c8c684e9 85 for (auto &oneCur : reserveValues.valueMap)
86 {
87 feeMap.valueMap[oneCur.first] += CReserveTransactionDescriptor::CalculateConversionFee(oneCur.second);
88 }
34c3ee8e 89 if (IsReserveToReserve())
90 {
c8c684e9 91 feeMap = feeMap * 2;
34c3ee8e 92 }
951413c7 93 }
56fe75cb 94
c8c684e9 95 // consider extra-leg pricing here
96
56fe75cb 97 return feeMap;
951413c7 98}
99
56fe75cb 100CReserveExchange::CReserveExchange(const UniValue &uni) : CTokenOutput(uni)
cb265a66 101{
56fe75cb 102 if (uni_get_bool(find_value(uni, "toreserve")))
cb265a66 103 {
56fe75cb 104 flags |= TO_RESERVE;
cb265a66 105 }
56fe75cb 106 if (uni_get_bool(find_value(uni, "limitorder")))
cb265a66 107 {
56fe75cb 108 flags |= LIMIT;
109 }
110 if (uni_get_bool(find_value(uni, "fillorkill")))
111 {
112 flags |= FILL_OR_KILL;
113 }
114 if (uni_get_bool(find_value(uni, "sendoutput")))
115 {
116 flags |= SEND_OUTPUT;
117 }
118
119 try
120 {
121 nLimit = AmountFromValue(find_value(uni, "limitprice"));
122 nValidBefore = uni_get_int(find_value(uni, "validbeforeblock"));
123 }
124 catch(const std::exception& e)
125 {
126 std::cerr << e.what() << '\n';
127 nVersion = VERSION_INVALID;
cb265a66 128 }
cb265a66 129}
130
56fe75cb 131CReserveExchange::CReserveExchange(const CTransaction &tx)
a6e612cc
MT
132{
133 bool orderFound = false;
a6e612cc
MT
134 for (auto out : tx.vout)
135 {
136 COptCCParams p;
137 if (IsPayToCryptoCondition(out.scriptPubKey, p))
138 {
139 if (p.evalCode == EVAL_RESERVE_EXCHANGE)
140 {
141 if (orderFound)
142 {
56fe75cb 143 nVersion = VERSION_INVALID;
a6e612cc
MT
144 }
145 else
146 {
147 FromVector(p.vData[0], *this);
148 orderFound = true;
149 }
150 }
151 }
152 }
56fe75cb 153}
a6e612cc 154
4a53088e 155CCrossChainImport::CCrossChainImport(const CScript &script)
156{
157 COptCCParams p;
158 if (IsPayToCryptoCondition(script, p) && p.IsValid())
159 {
160 // always take the first for now
161 if (p.evalCode == EVAL_CROSSCHAIN_IMPORT && p.vData.size())
162 {
163 FromVector(p.vData[0], *this);
164 }
165 }
166}
167
0ab273d2 168CCrossChainImport::CCrossChainImport(const CTransaction &tx, int32_t *pOutNum)
cdbadc7f 169{
0ab273d2 170 for (int i = 0; i < tx.vout.size(); i++)
cdbadc7f 171 {
172 COptCCParams p;
2d8b9129 173 if (IsPayToCryptoCondition(tx.vout[i].scriptPubKey, p) && p.IsValid())
cdbadc7f 174 {
175 // always take the first for now
176 if (p.evalCode == EVAL_CROSSCHAIN_IMPORT && p.vData.size())
177 {
178 FromVector(p.vData[0], *this);
0ab273d2 179 if (pOutNum)
180 {
181 *pOutNum = i;
182 }
183 break;
cdbadc7f 184 }
185 }
186 }
187}
188
c8c684e9 189bool CCrossChainExport::GetExportInfo(const CTransaction &exportTx,
190 int numExportOut,
ce2fda95 191 int &primaryExportOutNumOut,
c8c684e9 192 int32_t &nextOutput,
193 CPBaaSNotarization &exportNotarization,
194 std::vector<CReserveTransfer> &reserveTransfers,
195 CValidationState &state) const
196{
197 // we can assume that to get here, we have decoded the first output, which is the export output
198 // specified in numExportOut, our "this" pointer
199
200 // if this is called directly to get info, though it is a supplemental output, it is currently an error
201 if (IsSupplemental())
202 {
ce2fda95 203 return state.Error(strprintf("%s: cannot get export data directly from a supplemental data output. must be in context",__func__));
c8c684e9 204 }
205
206 auto hw = CMMRNode<>::GetHashWriter();
c8c684e9 207
ce2fda95 208 // this can be called passing either a system export or a normal currency export, and it will always
209 // retrieve information from the same normal currency export in either case and return the primary output num
210 int numOutput = IsSystemThreadExport() ? numExportOut - 1 : numExportOut;
211 if (numOutput < 0)
212 {
213 return state.Error(strprintf("%s: invalid output index for export out or invalid export transaction",__func__));
214 }
215 primaryExportOutNumOut = numOutput;
216
217 // if this export is from our system
c8c684e9 218 if (sourceSystemID == ASSETCHAINS_CHAINID)
219 {
ce2fda95 220 // if we're exporting off-chain and not directly to the system currency,
221 // the system currency is added as a system export output, which ensures export serialization from this system
222 // to the other. the system export output will be after our currency export. if so skip it.
223 if (destSystemID != sourceSystemID && destCurrencyID != destSystemID)
c8c684e9 224 {
225 numOutput++;
226 }
4c5696b1 227
c8c684e9 228 // retrieve reserve transfers from export transaction inputs
4c5696b1 229 if (firstInput >= 0)
230 {
231 for (int i = firstInput; i < exportTx.vin.size(); i++)
c8c684e9 232 {
4c5696b1 233 CTransaction rtTx;
234 COptCCParams rtP;
235 CReserveTransfer rt;
236 uint256 hashBlk;
237 if (!(myGetTransaction(exportTx.vin[i].prevout.hash, rtTx, hashBlk) &&
238 exportTx.vin[i].prevout.n < rtTx.vout.size() &&
239 rtTx.vout[exportTx.vin[i].prevout.n].scriptPubKey.IsPayToCryptoCondition(rtP) &&
240 rtP.IsValid() &&
241 rtP.evalCode == EVAL_RESERVE_TRANSFER &&
242 rtP.vData.size() &&
243 (rt = CReserveTransfer(rtP.vData[0])).IsValid()))
244 {
245 return state.Error(strprintf("%s: invalid reserve transfer for export",__func__));
246 }
247 hw << rt;
248 reserveTransfers.push_back(rt);
c8c684e9 249 }
c8c684e9 250 }
251 }
252 else
253 {
ce2fda95 254 // this is coming from another chain or system.
255 // the proof of this export must already have been checked, so we are
256 // only interested in the reserve transfers for this and any supplements
c8c684e9 257 CCrossChainExport rtExport = *this;
258 while (rtExport.IsValid())
259 {
260 COptCCParams p;
261 for (auto &oneRt : rtExport.reserveTransfers)
262 {
263 hw << oneRt;
264 reserveTransfers.push_back(oneRt);
265 }
266 if (rtExport.HasSupplement())
267 {
268 numOutput++;
269 if (!(exportTx.vout.size() > numOutput &&
270 exportTx.vout[numOutput].scriptPubKey.IsPayToCryptoCondition(p) &&
271 p.IsValid() &&
272 p.evalCode == EVAL_CROSSCHAIN_EXPORT &&
273 p.vData.size() &&
274 (rtExport = CCrossChainExport(p.vData[0])).IsValid() &&
275 rtExport.IsSupplemental()))
276 {
277 return state.Error(strprintf("%s: invalid supplemental reserve transfer data for export",__func__));
278 }
279 }
280 else
281 {
282 // no more supplements, done
283 rtExport = CCrossChainExport();
284 }
285 }
286 }
287
288 // now, we should have accurate reserve transfers
4c5696b1 289 uint256 rtHash = reserveTransfers.size() ? hw.GetHash() : uint256();
c8c684e9 290 if (rtHash != hashReserveTransfers)
291 {
292 return state.Error(strprintf("%s: reserve transfers do not match reserve transfer hash in export",__func__));
293 }
294
295 exportNotarization = CPBaaSNotarization();
296
4c5696b1 297 if (IsSameChain() && !IsChainDefinition())
c8c684e9 298 {
4c5696b1 299 if (IsClearLaunch() || !IsPrelaunch())
300 {
301 numOutput++;
302 COptCCParams p;
303 // we have an export finalization to verify/skip
304 if (!(exportTx.vout.size() > numOutput &&
305 exportTx.vout[numOutput].scriptPubKey.IsPayToCryptoCondition(p) &&
306 p.IsValid() &&
307 p.evalCode == EVAL_FINALIZE_EXPORT &&
308 p.vData.size() &&
309 (CObjectFinalization(p.vData[0])).IsValid()))
310 {
311 return state.Error(strprintf("%s: invalid export finalization",__func__));
312 }
313 }
314 if ((IsPrelaunch() || IsClearLaunch()))
c8c684e9 315 {
4c5696b1 316 // in same chain before launch, we expect a notarization to follow
317 numOutput++;
318 COptCCParams p;
319 if (!(exportTx.vout.size() > numOutput &&
320 exportTx.vout[numOutput].scriptPubKey.IsPayToCryptoCondition(p) &&
321 p.IsValid() &&
322 (p.evalCode == EVAL_ACCEPTEDNOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION) &&
323 p.vData.size() &&
324 (exportNotarization = CPBaaSNotarization(p.vData[0])).IsValid()))
325 {
326 return state.Error(strprintf("%s: invalid export notarization",__func__));
327 }
c8c684e9 328 }
329 }
330 nextOutput = numOutput + 1;
331 return true;
332}
333
334bool CCrossChainExport::GetExportInfo(const CTransaction &exportTx,
335 int numExportOut,
ce2fda95 336 int &primaryExportOutNumOut,
c8c684e9 337 int32_t &nextOutput,
338 CPBaaSNotarization &exportNotarization,
339 std::vector<CReserveTransfer> &reserveTransfers) const
340{
341 CValidationState state;
ce2fda95 342 return GetExportInfo(exportTx, numExportOut, primaryExportOutNumOut, nextOutput, exportNotarization, reserveTransfers, state);
c8c684e9 343}
344
345
346bool CCrossChainImport::GetImportInfo(const CTransaction &importTx,
347 int numImportOut,
348 CCrossChainExport &ccx,
349 CCrossChainImport &sysCCI,
350 int32_t &sysCCIOut,
351 CPBaaSNotarization &importNotarization,
352 int32_t &importNotarizationOut,
353 int32_t &evidenceOutStart,
354 int32_t &evidenceOutEnd,
355 std::vector<CReserveTransfer> &reserveTransfers,
356 CValidationState &state) const
357{
358 // we can assume that to get here, we have decoded the first output, which is the import output
359 // specified in numImportOut, our "this" pointer
360
361 // following that, we should find in order:
362 //
363 // 1. Optional system import output, present only if we are importing to non-gateway, non-native currency from an external system or PBaaS chain
364 //
365 // 2. any necessary export proof for the import, present only if we are coming from an external system or PBaaS chain
366 //
367 // 3. if we are coming from an external system or PBaaS chain, following outputs will include the reserve transfers for the export proof
368 //
369 // 4. Notarization for import currency, only present if this is fractional currency or first launch of new PBaaS chain
370 //
371
372 sysCCIOut = -1;
373 evidenceOutStart = -1;
374 evidenceOutEnd = -1;
375
4c5696b1 376 LOCK(mempool.cs);
c8c684e9 377
378 importNotarizationOut = numImportOut + 1;
379
380 if (IsSameChain())
381 {
382 // reserve transfers are available via the inputs to the matching export
f5c06fc0 383 CTransaction exportTx = exportTxId.IsNull() ? importTx : CTransaction();
c8c684e9 384 uint256 hashBlk;
385 COptCCParams p;
f5c06fc0 386
387 if (!((exportTxId.IsNull() ? true : myGetTransaction(exportTxId, exportTx, hashBlk)) &&
388 IsDefinitionImport() ||
389 (exportTxOutNum >= 0 &&
c8c684e9 390 exportTx.vout.size() > exportTxOutNum &&
391 exportTx.vout[exportTxOutNum].scriptPubKey.IsPayToCryptoCondition(p) &&
392 p.IsValid() &&
393 p.evalCode == EVAL_CROSSCHAIN_EXPORT &&
394 p.vData.size() &&
f5c06fc0 395 (ccx = CCrossChainExport(p.vData[0])).IsValid())))
c8c684e9 396 {
397 return state.Error(strprintf("%s: cannot retrieve export transaction for import",__func__));
398 }
399
f5c06fc0 400 if (!IsDefinitionImport())
c8c684e9 401 {
f5c06fc0 402 int32_t nextOutput;
403 CPBaaSNotarization xNotarization;
ce2fda95 404 int primaryOutNumOut;
405 if (!ccx.GetExportInfo(exportTx, exportTxOutNum, primaryOutNumOut, nextOutput, xNotarization, reserveTransfers, state))
f5c06fc0 406 {
407 return false;
408 }
c8c684e9 409 }
410 // next output after import out is notarization
411 }
412 else
413 {
414 // next output should be the import for the system from which this export comes
415 uint256 hashBlk;
416 COptCCParams p;
417 sysCCIOut = numImportOut + 1;
418 if (!(sysCCIOut >= 0 &&
419 importTx.vout.size() > sysCCIOut &&
420 importTx.vout[sysCCIOut].scriptPubKey.IsPayToCryptoCondition(p) &&
421 p.IsValid() &&
422 p.evalCode == EVAL_CROSSCHAIN_IMPORT &&
423 p.vData.size() &&
424 (sysCCI = CCrossChainImport(p.vData[0])).IsValid()))
425 {
426 return state.Error(strprintf("%s: cannot retrieve export evidence for import",__func__));
427 }
428
429 importNotarizationOut++;
430
431 // next output should be export in evidence output followed by supplemental reserve transfers for the export
432 evidenceOutStart = importNotarizationOut + 1;
433 CNotaryEvidence evidence;
434 CPartialTransactionProof evidenceProof;
435 if (!(evidenceOutStart >= 0 &&
436 importTx.vout.size() > evidenceOutStart &&
437 importTx.vout[evidenceOutStart].scriptPubKey.IsPayToCryptoCondition(p) &&
438 p.IsValid() &&
439 p.evalCode == EVAL_NOTARY_EVIDENCE &&
440 p.vData.size() &&
441 (evidence = CNotaryEvidence(p.vData[0])).IsValid() &&
442 evidence.IsPartialTxProof() &&
443 evidence.evidence.size()))
444 {
445 return state.Error(strprintf("%s: cannot retrieve export evidence for import",__func__));
446 }
447 CTransaction exportTx;
448 p = COptCCParams();
449 if (!(evidence.evidence[0].GetPartialTransaction(exportTx) == exportTxId &&
450 exportTx.vout.size() > exportTxOutNum &&
451 exportTx.vout[exportTxOutNum].scriptPubKey.IsPayToCryptoCondition(p) &&
452 p.IsValid() &&
453 p.vData.size() &&
454 (ccx = CCrossChainExport(p.vData[0])).IsValid()))
455 {
456 return state.Error(strprintf("%s: invalid export evidence for import",__func__));
457 }
458 int32_t nextOutput;
459 CPBaaSNotarization xNotarization;
ce2fda95 460 int primaryOutNumOut;
461 if (!ccx.GetExportInfo(importTx, evidenceOutStart, primaryOutNumOut, nextOutput, xNotarization, reserveTransfers))
c8c684e9 462 {
463 return state.Error(strprintf("%s: invalid export evidence for import 1",__func__));
464 }
465
466 evidenceOutEnd = nextOutput - 1;
467 }
468 COptCCParams p;
469 if (!(importTx.vout.size() > importNotarizationOut &&
470 importTx.vout[importNotarizationOut].scriptPubKey.IsPayToCryptoCondition(p) &&
471 p.IsValid() &&
472 (p.evalCode == EVAL_ACCEPTEDNOTARIZATION || p.evalCode == EVAL_EARNEDNOTARIZATION) &&
473 p.vData.size() &&
474 (importNotarization = CPBaaSNotarization(p.vData[0])).IsValid()))
475 {
476 return state.Error(strprintf("%s: invalid import notarization for import",__func__));
477 }
478 return true;
479}
480
481bool CCrossChainImport::GetImportInfo(const CTransaction &importTx,
482 int numImportOut,
483 CCrossChainExport &ccx,
484 CCrossChainImport &sysCCI,
485 int32_t &sysCCIOut,
486 CPBaaSNotarization &importNotarization,
487 int32_t &importNotarizationOut,
488 int32_t &evidenceOutStart,
489 int32_t &evidenceOutEnd,
490 std::vector<CReserveTransfer> &reserveTransfers) const
491{
492 CValidationState state;
493 return GetImportInfo(importTx,
494 numImportOut,
495 ccx,
496 sysCCI,
497 sysCCIOut,
498 importNotarization,
499 importNotarizationOut,
500 evidenceOutStart,
501 evidenceOutEnd,
502 reserveTransfers,
503 state);
504}
505
506bool CCrossChainImport::ValidateImport(const CTransaction &tx,
507 int numImportin,
508 int numImportOut,
509 CCrossChainExport &ccx,
510 CPBaaSNotarization &importNotarization,
511 std::vector<CReserveTransfer> &reserveTransfers,
512 CValidationState &state) const
513{
514 return true;
515}
516
517bool CCrossChainImport::ValidateImport(const CTransaction &tx,
518 int numImportin,
519 int numImportOut,
520 CCrossChainExport &ccx,
521 CPBaaSNotarization &importNotarization,
522 std::vector<CReserveTransfer> &reserveTransfers) const
523{
524 CValidationState state;
525 return ValidateImport(tx, numImportin, numImportOut, ccx, importNotarization, reserveTransfers, state);
526}
527
695db088 528CCurrencyState::CCurrencyState(const UniValue &obj) : version(VERSION_CURRENT)
a6e612cc 529{
29a82b26 530 try
2d8b9129 531 {
29a82b26 532 flags = uni_get_int(find_value(obj, "flags"));
2d8b9129 533
29a82b26 534 std::string cIDStr = uni_get_str(find_value(obj, "currencyid"));
535 if (cIDStr != "")
56fe75cb 536 {
29a82b26 537 CTxDestination currencyDest = DecodeDestination(cIDStr);
538 currencyID = GetDestinationID(currencyDest);
56fe75cb 539 }
29a82b26 540
541 if (flags & FLAG_FRACTIONAL)
56fe75cb 542 {
29a82b26 543 auto CurrenciesArr = find_value(obj, "reservecurrencies");
544 size_t numCurrencies;
545 if (!CurrenciesArr.isArray() ||
546 !(numCurrencies = CurrenciesArr.size()) ||
547 numCurrencies > MAX_RESERVE_CURRENCIES)
56fe75cb 548 {
29a82b26 549 version = VERSION_INVALID;
550 LogPrintf("Failed to proplerly specify currencies in reserve currency definition\n");
551 }
552 else
553 {
554 // store currencies, weights, and reserves
555 try
56fe75cb 556 {
29a82b26 557 for (int i = 0; i < CurrenciesArr.size(); i++)
56fe75cb 558 {
29a82b26 559 uint160 currencyID = GetDestinationID(DecodeDestination(uni_get_str(find_value(CurrenciesArr[i], "currencyid"))));
560 if (currencyID.IsNull())
561 {
562 LogPrintf("Invalid currency ID\n");
563 version = VERSION_INVALID;
564 break;
565 }
566 currencies[i] = currencyID;
567 weights[i] = AmountFromValue(find_value(CurrenciesArr[i], "weight"));
568 reserves[i] = AmountFromValue(find_value(CurrenciesArr[i], "reserves"));
56fe75cb 569 }
56fe75cb 570 }
29a82b26 571 catch(const std::exception& e)
572 {
573 std::cerr << e.what() << '\n';
574 version = VERSION_INVALID;
575 LogPrintf("Invalid specification of currencies, weights, and/or reserves in initial definition of reserve currency\n");
576 }
56fe75cb 577 }
578 }
56fe75cb 579
29a82b26 580 if (version == VERSION_INVALID)
581 {
582 printf("Invalid currency specification, see debug.log for reason other than invalid flags\n");
583 LogPrintf("Invalid currency specification\n");
584 }
585 else
56fe75cb 586 {
587 initialSupply = AmountFromValue(find_value(obj, "initialsupply"));
588 emitted = AmountFromValue(find_value(obj, "emitted"));
589 supply = AmountFromValue(find_value(obj, "supply"));
590 }
29a82b26 591 }
592 catch(const std::exception& e)
593 {
594 printf("Invalid currency specification, see debug.log for reason other than invalid flags\n");
595 LogPrintf("Invalid currency specification\n");
596 version = VERSION_INVALID;
597 LogPrintf("%s: %s\n", __func__, e.what());
a6e612cc 598 }
a6e612cc
MT
599}
600
45d7e5d5 601CCoinbaseCurrencyState::CCoinbaseCurrencyState(const CTransaction &tx, int *pOutIdx)
41f170fd 602{
45d7e5d5 603 int localIdx;
604 int &i = pOutIdx ? *pOutIdx : localIdx;
605 for (i = 0; i < tx.vout.size(); i++)
41f170fd
MT
606 {
607 COptCCParams p;
45d7e5d5 608 if (IsPayToCryptoCondition(tx.vout[i].scriptPubKey, p))
41f170fd 609 {
45d7e5d5 610 if (p.evalCode == EVAL_CURRENCYSTATE && p.vData.size())
41f170fd 611 {
45d7e5d5 612 FromVector(p.vData[0], *this);
613 break;
41f170fd
MT
614 }
615 }
616 }
617}
618
56fe75cb 619std::vector<std::vector<CAmount>> ValueColumnsFromUniValue(const UniValue &uni,
620 const std::vector<std::string> &rowNames,
621 const std::vector<std::string> &columnNames)
622{
623 std::vector<std::vector<CAmount>> retVal;
624 for (int i = 0; i < rowNames.size(); i++)
625 {
626 UniValue row = find_value(uni, rowNames[i]);
627 if (row.isObject())
628 {
629 for (int j = 0; j < columnNames.size(); j++)
630 {
631 if (retVal.size() == j)
632 {
633 retVal.emplace_back();
634 }
635 CAmount columnVal = 0;
636 try
637 {
638 columnVal = AmountFromValue(find_value(row, columnNames[j]));
639 }
640 catch(const std::exception& e)
641 {
642 std::cerr << e.what() << '\n';
643 }
644 retVal[j].push_back(columnVal);
645 }
646 }
647 }
648 return retVal;
649}
650
651
c8c677c9 652CCoinbaseCurrencyState::CCoinbaseCurrencyState(const UniValue &obj) : CCurrencyState(obj)
41f170fd 653{
29a82b26 654 try
56fe75cb 655 {
29a82b26 656 std::vector<std::vector<CAmount>> columnAmounts;
657
658 auto currenciesValue = find_value(obj, "currencies");
695db088 659 std::vector<std::string> rowNames = currenciesValue.getKeys();
660 if (!currencies.size() && rowNames.size())
29a82b26 661 {
695db088 662 currencies.resize(rowNames.size());
663 weights.resize(rowNames.size());
664 reserves.resize(rowNames.size());
665 for (int i = 0; i < rowNames.size(); i++)
666 {
667 currencies[i] = GetDestinationID(DecodeDestination(rowNames[i]));
668 }
669 }
670 else if (currencies.size())
671 {
672 rowNames.resize(currencies.size());
673 for (int i = 0; i < rowNames.size(); i++)
674 {
675 rowNames[i] = EncodeDestination(CIdentityID(currencies[i]));
676 }
677 }
678 if (currencies.size() != rowNames.size())
679 {
680 LogPrintf("%s: mismatch currencies and reserve currencies\n", __func__);
681 version = VERSION_INVALID;
682 return;
29a82b26 683 }
684 std::vector<std::string> columnNames({"reservein", "nativein", "reserveout", "lastconversionprice", "viaconversionprice", "fees", "conversionfees"});
685 if (currenciesValue.isObject())
686 {
695db088 687 //printf("%s: currencies: %s\n", __func__, currenciesValue.write(1,2).c_str());
29a82b26 688 columnAmounts = ValueColumnsFromUniValue(currenciesValue, rowNames, columnNames);
695db088 689 if (columnAmounts.size() == columnNames.size())
690 {
691 reserveIn = columnAmounts[0];
692 nativeIn = columnAmounts[1];
693 reserveOut = columnAmounts[2];
694 conversionPrice = columnAmounts[4];
695 viaConversionPrice = columnAmounts[3];
696 fees = columnAmounts[5];
697 conversionFees = columnAmounts[6];
698 }
29a82b26 699 }
700 nativeFees = uni_get_int64(find_value(obj, "nativefees"));
701 nativeConversionFees = uni_get_int64(find_value(obj, "nativeconversionfees"));
56fe75cb 702 }
29a82b26 703 catch(const std::exception& e)
56fe75cb 704 {
29a82b26 705 version = VERSION_INVALID;
706 LogPrintf("%s: %s\n", __func__, e.what());
56fe75cb 707 }
41f170fd
MT
708}
709
1fb6db72 710CAmount CalculateFractionalOut(CAmount NormalizedReserveIn, CAmount Supply, CAmount NormalizedReserve, int32_t reserveRatio)
f88ddf77 711{
d3c97fd3 712 static cpp_dec_float_50 one("1");
713 static cpp_dec_float_50 bigSatoshi("100000000");
f88ddf77 714 cpp_dec_float_50 reservein(std::to_string(NormalizedReserveIn));
d3c97fd3 715 reservein = reservein / bigSatoshi;
1fb6db72 716 cpp_dec_float_50 supply(std::to_string((Supply ? Supply : 1)));
d3c97fd3 717 supply = supply / bigSatoshi;
1fb6db72 718 cpp_dec_float_50 reserve(std::to_string(NormalizedReserve ? NormalizedReserve : 1));
d3c97fd3 719 reserve = reserve / bigSatoshi;
1fb6db72 720 cpp_dec_float_50 ratio(std::to_string(reserveRatio));
d3c97fd3 721 ratio = ratio / bigSatoshi;
f88ddf77 722
4a5b9f74 723 //printf("reservein: %s\nsupply: %s\nreserve: %s\nratio: %s\n\n", reservein.str().c_str(), supply.str().c_str(), reserve.str().c_str(), ratio.str().c_str());
1fb6db72 724
f88ddf77 725 int64_t fractionalOut = 0;
726
727 // first check if anything to buy
728 if (NormalizedReserveIn)
729 {
d3c97fd3 730 cpp_dec_float_50 supplyout = bigSatoshi * (supply * (pow((reservein / reserve) + one, ratio) - one));
4a5b9f74 731 //printf("supplyout: %s\n", supplyout.str(0, std::ios_base::fmtflags::_S_fixed).c_str());
f88ddf77 732
c8c677c9 733 if (!CCurrencyState::to_int64(supplyout, fractionalOut))
f88ddf77 734 {
c8c684e9 735 return -1;
f88ddf77 736 }
737 }
738 return fractionalOut;
739}
740
1fb6db72 741CAmount CalculateReserveOut(CAmount FractionalIn, CAmount Supply, CAmount NormalizedReserve, int32_t reserveRatio)
f88ddf77 742{
d3c97fd3 743 static cpp_dec_float_50 one("1");
744 static cpp_dec_float_50 bigSatoshi("100000000");
f88ddf77 745 cpp_dec_float_50 fractionalin(std::to_string(FractionalIn));
d3c97fd3 746 fractionalin = fractionalin / bigSatoshi;
1fb6db72 747 cpp_dec_float_50 supply(std::to_string((Supply ? Supply : 1)));
d3c97fd3 748 supply = supply / bigSatoshi;
1fb6db72 749 cpp_dec_float_50 reserve(std::to_string(NormalizedReserve ? NormalizedReserve : 1));
d3c97fd3 750 reserve = reserve / bigSatoshi;
1fb6db72 751 cpp_dec_float_50 ratio(std::to_string(reserveRatio));
d3c97fd3 752 ratio = ratio / bigSatoshi;
f88ddf77 753
4a5b9f74 754 //printf("fractionalin: %s\nsupply: %s\nreserve: %s\nratio: %s\n\n", fractionalin.str().c_str(), supply.str().c_str(), reserve.str().c_str(), ratio.str().c_str());
1fb6db72 755
f88ddf77 756 int64_t reserveOut = 0;
757
758 // first check if anything to buy
759 if (FractionalIn)
760 {
d3c97fd3 761 cpp_dec_float_50 reserveout = bigSatoshi * (reserve * (one - pow(one - (fractionalin / supply), (one / ratio))));
4a5b9f74 762 //printf("reserveout: %s\n", reserveout.str(0, std::ios_base::fmtflags::_S_fixed).c_str());
d3c97fd3 763
c8c677c9 764 if (!CCurrencyState::to_int64(reserveout, reserveOut))
f88ddf77 765 {
766 assert(false);
767 }
768 }
769 return reserveOut;
770}
771
772// This can handle multiple aggregated, bidirectional conversions in one block of transactions. To determine the conversion price, it
caa143ea 773// takes both input amounts of any number of reserves and the fractional currencies targeting those reserves to merge the conversion into one
774// merged calculation with the same price across currencies for all transactions in the block. It returns the newly calculated
775// conversion prices of the fractional reserve in the reserve currency.
818494a0 776std::vector<CAmount> CCurrencyState::ConvertAmounts(const std::vector<CAmount> &_inputReserves,
777 const std::vector<CAmount> &_inputFractional,
c8c684e9 778 CCurrencyState &_newState,
26e2ca05 779 std::vector<std::vector<CAmount>> const *pCrossConversions,
780 std::vector<CAmount> *pViaPrices) const
f88ddf77 781{
26e2ca05 782 static arith_uint256 bigSatoshi(SATOSHIDEN);
783
e4e5ccf5 784 int32_t numCurrencies = currencies.size();
818494a0 785 std::vector<CAmount> inputReserves = _inputReserves;
786 std::vector<CAmount> inputFractional = _inputFractional;
f88ddf77 787
c8c684e9 788 CCurrencyState newState = *this;
810bc6a2 789 std::vector<CAmount> rates(numCurrencies);
818494a0 790 std::vector<CAmount> initialRates = PricesInReserve();
f88ddf77 791
d3c97fd3 792 bool haveConversion = false;
e4e5ccf5 793
794 if (inputReserves.size() == inputFractional.size() && inputReserves.size() == numCurrencies &&
795 (!pCrossConversions || pCrossConversions->size() == numCurrencies))
d3c97fd3 796 {
e4e5ccf5 797 int i;
798 for (i = 0; i < numCurrencies; i++)
d3c97fd3 799 {
e4e5ccf5 800 if (!pCrossConversions || (*pCrossConversions)[i].size() != numCurrencies)
801 {
802 break;
803 }
d3c97fd3 804 }
e4e5ccf5 805 if (!pCrossConversions || i == numCurrencies)
d3c97fd3 806 {
e4e5ccf5 807 for (auto oneIn : inputReserves)
d3c97fd3 808 {
e4e5ccf5 809 if (oneIn)
810 {
811 haveConversion = true;
812 break;
813 }
814 }
815 if (!haveConversion)
816 {
817 for (auto oneIn : inputFractional)
818 {
819 if (oneIn)
820 {
821 haveConversion = true;
822 break;
823 }
824 }
d3c97fd3 825 }
826 }
827 }
e4e5ccf5 828 else
829 {
830 printf("%s: invalid parameters\n", __func__);
c8c684e9 831 LogPrintf("%s: invalid parameters\n", __func__);
832 return initialRates;
e4e5ccf5 833 }
834
d3c97fd3 835 if (!haveConversion)
836 {
c8c684e9 837 // not considered an error
838 _newState = newState;
818494a0 839 return initialRates;
d3c97fd3 840 }
f88ddf77 841
c8c684e9 842 // generally an overflow will cause a fail, which will result in leaving the _newState parameter untouched, making it
843 // possible to check if it is invalid as an overflow or formula failure check
844 bool failed = false;
845
5026a487 846 for (auto oneIn : inputReserves)
847 {
848 if (oneIn < 0)
849 {
c8c684e9 850 failed = true;
5026a487 851 printf("%s: invalid reserve input amount for conversion %ld\n", __func__, oneIn);
c8c684e9 852 LogPrintf("%s: invalid reserve input amount for conversion %ld\n", __func__, oneIn);
5026a487 853 break;
854 }
855 }
856 for (auto oneIn : inputFractional)
857 {
858 if (oneIn < 0)
859 {
c8c684e9 860 failed = true;
5026a487 861 printf("%s: invalid fractional input amount for conversion %ld\n", __func__, oneIn);
c8c684e9 862 LogPrintf("%s: invalid fractional input amount for conversion %ld\n", __func__, oneIn);
5026a487 863 break;
864 }
865 }
c8c684e9 866
867 if (failed)
868 {
869 return initialRates;
870 }
5026a487 871
f88ddf77 872 // Create corresponding fractions of the supply for each currency to be used as starting calculation of that currency's value
873 // Determine the equivalent amount of input and output based on current values. Balance each such that each currency has only
874 // input or output, denominated in supply at the starting value.
875 //
876 // For each currency in either direction, sell to reserve or buy aggregate, we convert to a contribution of amount at the reserve
877 // percent value. For example, consider 4 currencies, r1...r4, which are all 25% reserves of currency fr1. For simplicity of example,
878 // assume 1000 reserve of each reserve currency, where all currencies are equal in value to each other at the outset, and a supply of
879 // 4000, where each fr1 is equal in value to 1 of each component reserve.
880 // Now, consider the following cases:
881 //
882 // 1. purchase fr1 with 100 r1
883 // This is treated as a single 25% fractional purchase with respect to amount purchased, ending price, and supply change
884 // 2. purchase fr1 with 100 r1, 100 r2, 100 r3, 100 r4
885 // This is treated as a common layer of purchase across 4 x 25% currencies, resulting in 100% fractional purchase divided 4 ways
886 // 3. purchase fr1 with 100 r1, 50 r2, 25 r3
887 // This is treated as 3 separate purchases in order:
888 // a. one of 25 units across 3 currencies (3 x 25%), making a 75% fractional purchase of 75 units divided equally across 3 currencies
889 // b. one of 25 units across 2 currencies (2 x 25%), making a 50% fractional purchase of 50 units divided equally between r1 and r2
890 // c. one purchase of 50 units in r1 at 25% fractional purchase
891 // 4. purchase fr1 with 100 r1, sell 100 fr1 to r2
892 // a. one fractional purchase of 100 units at 25%
893 // b. one fractional sell of 100 units at 25%
894 // c. do each in forward and reverse order and set conversion at mean between each
895 // 5. purchase fr1 with 100 r1, 50 r2, sell 100 fr1 to r3, 50 to r4
896 // This consists of one composite (multi-layer) buy and one composite sell
897 // a. Compose one two layer purchase of 50 r1 + 50 r2 at 50% and 50 r1 at 25%
898 // b. Compose one two layer sell of 50 r3 + 50 r4 at 50% and 50 r3 at 25%
899 // c. execute each operation of a and b in forward and reverse order and set conversion at mean between results
900 //
901
902 std::multimap<CAmount, std::pair<CAmount, uint160>> fractionalIn, fractionalOut;
903
d3c97fd3 904 // aggregate amounts of ins and outs across all currencies expressed in fractional values in both directions first buy/sell, then sell/buy
905 std::map<uint160, std::pair<CAmount, CAmount>> fractionalInMap, fractionalOutMap;
906
f88ddf77 907 arith_uint256 bigSupply(supply);
908
caa143ea 909 int32_t totalReserveWeight = 0;
f88ddf77 910 int32_t maxReserveRatio = 0;
caa143ea 911
f88ddf77 912 for (auto weight : weights)
913 {
914 maxReserveRatio = weight > maxReserveRatio ? weight : maxReserveRatio;
caa143ea 915 totalReserveWeight += weight;
c8c684e9 916 if (!weight)
917 {
918 LogPrintf("%s: invalid, zero weight currency for conversion\n", __func__);
919 return initialRates;
920 }
f88ddf77 921 }
922
923 if (!maxReserveRatio)
924 {
ee7656e8 925 LogPrintf("%s: attempting to convert amounts on non-fractional currency\n", __func__);
c8c684e9 926 return initialRates;
927 }
928
929 // it is currently an error to have > 100% reserve ratio currency
930 if (totalReserveWeight > bigSatoshi)
931 {
932 LogPrintf("%s: total currency backing weight exceeds 100%\n", __func__);
933 return initialRates;
f88ddf77 934 }
935
936 arith_uint256 bigMaxReserveRatio = arith_uint256(maxReserveRatio);
d3c97fd3 937 arith_uint256 bigTotalReserveWeight = arith_uint256(totalReserveWeight);
f88ddf77 938
939 // reduce each currency change to a net inflow or outflow of fractional currency and
d3c97fd3 940 // store both negative and positive in structures sorted by the net amount, adjusted
941 // by the difference of the ratio between the weights of each currency
e4e5ccf5 942 for (int64_t i = 0; i < numCurrencies; i++)
f88ddf77 943 {
d3c97fd3 944 arith_uint256 weight(weights[i]);
4a5b9f74 945 //printf("%s: %ld\n", __func__, ReserveToNative(inputReserves[i], i));
c8c684e9 946 CAmount asNative = ReserveToNative(inputReserves[i], i);
947 // if overflow
948 if (asNative < 0)
949 {
950 failed = true;
951 break;
952 }
953 CAmount netFractional = inputFractional[i] - asNative;
f88ddf77 954 int64_t deltaRatio;
c8c684e9 955 arith_uint256 bigDeltaRatio;
f88ddf77 956 if (netFractional > 0)
957 {
c8c684e9 958 bigDeltaRatio = ((arith_uint256(netFractional) * bigMaxReserveRatio) / weight);
959 if (bigDeltaRatio > INT64_MAX)
960 {
961 failed = true;
962 break;
963 }
964 deltaRatio = bigDeltaRatio.GetLow64();
f88ddf77 965 fractionalIn.insert(std::make_pair(deltaRatio, std::make_pair(netFractional, currencies[i])));
966 }
967 else if (netFractional < 0)
968 {
969 netFractional = -netFractional;
c8c684e9 970 bigDeltaRatio = ((arith_uint256(netFractional) * bigMaxReserveRatio) / weight);
971 if (bigDeltaRatio > INT64_MAX)
972 {
973 failed = true;
974 break;
975 }
976 deltaRatio = bigDeltaRatio.GetLow64();
f88ddf77 977 fractionalOut.insert(std::make_pair(deltaRatio, std::make_pair(netFractional, currencies[i])));
978 }
979 }
980
c8c684e9 981 if (failed)
982 {
983 LogPrintf("%s: OVERFLOW in calculating changes in currency\n", __func__);
984 return initialRates;
985 }
986
f88ddf77 987 // create "layers" of equivalent value at different fractional percentages
988 // across currencies going in or out at the same time, enabling their effect on the aggregate
989 // to be represented by a larger fractional percent impact of "normalized reserve" on the currency,
990 // which results in accurate pricing impact simulating a basket of currencies.
991 //
1fb6db72 992 // since we have all values sorted, the lowest non-zero value determines the first common layer, then next lowest, the next, etc.
f88ddf77 993 std::vector<std::pair<int32_t, std::pair<CAmount, std::vector<uint160>>>> fractionalLayersIn, fractionalLayersOut;
994 auto reserveMap = GetReserveMap();
995
996 CAmount layerAmount = 0;
997 CAmount layerStart;
998
999 for (auto inFIT = fractionalIn.upper_bound(layerAmount); inFIT != fractionalIn.end(); inFIT = fractionalIn.upper_bound(layerAmount))
1000 {
1001 // make a common layer out of all entries from here until the end
1002 int frIdx = fractionalLayersIn.size();
1003 layerStart = layerAmount;
1004 layerAmount = inFIT->first;
1005 CAmount layerHeight = layerAmount - layerStart;
1006 fractionalLayersIn.emplace_back(std::make_pair(0, std::make_pair(0, std::vector<uint160>())));
1007 for (auto it = inFIT; it != fractionalIn.end(); it++)
1008 {
1009 // reverse the calculation from layer height to amount for this currency, based on currency weight
1010 int32_t weight = weights[reserveMap[it->second.second]];
d3c97fd3 1011 CAmount curAmt = ((arith_uint256(layerHeight) * arith_uint256(weight) / bigMaxReserveRatio)).GetLow64();
f88ddf77 1012 it->second.first -= curAmt;
c8c684e9 1013
1014 if (it->second.first < 0)
1015 {
1016 LogPrintf("%s: UNDERFLOW in calculating changes in currency\n", __func__);
1017 return initialRates;
1018 }
f88ddf77 1019
1020 fractionalLayersIn[frIdx].first += weight;
1021 fractionalLayersIn[frIdx].second.first += curAmt;
1022 fractionalLayersIn[frIdx].second.second.push_back(it->second.second);
1023 }
c8c684e9 1024 }
f88ddf77 1025
1026 layerAmount = 0;
1027 for (auto outFIT = fractionalOut.upper_bound(layerAmount); outFIT != fractionalOut.end(); outFIT = fractionalOut.upper_bound(layerAmount))
1028 {
f88ddf77 1029 int frIdx = fractionalLayersOut.size();
1030 layerStart = layerAmount;
1031 layerAmount = outFIT->first;
1032 CAmount layerHeight = layerAmount - layerStart;
1033 fractionalLayersOut.emplace_back(std::make_pair(0, std::make_pair(0, std::vector<uint160>())));
1034 for (auto it = outFIT; it != fractionalOut.end(); it++)
1035 {
f88ddf77 1036 int32_t weight = weights[reserveMap[it->second.second]];
c8c684e9 1037 arith_uint256 bigCurAmt = ((arith_uint256(layerHeight) * arith_uint256(weight) / bigMaxReserveRatio));
1038 if (bigCurAmt > INT64_MAX)
1039 {
1040 LogPrintf("%s: OVERFLOW in calculating changes in currency\n", __func__);
1041 return initialRates;
1042 }
1043 CAmount curAmt = bigCurAmt.GetLow64();
f88ddf77 1044 it->second.first -= curAmt;
1045 assert(it->second.first >= 0);
1046
1047 fractionalLayersOut[frIdx].first += weight;
1048 fractionalLayersOut[frIdx].second.first += curAmt;
1049 fractionalLayersOut[frIdx].second.second.push_back(it->second.second);
1050 }
1051 }
1052
1053 int64_t supplyAfterBuy = 0, supplyAfterBuySell = 0, supplyAfterSell = 0, supplyAfterSellBuy = 0;
1054 int64_t reserveAfterBuy = 0, reserveAfterBuySell = 0, reserveAfterSell = 0, reserveAfterSellBuy = 0;
1055
1056 // first, loop through all buys layer by layer. calculate and divide the proceeds between currencies
1057 // in each participating layer, in accordance with each currency's relative percentage
1058 CAmount addSupply = 0;
1059 CAmount addNormalizedReserves = 0;
1060 for (auto &layer : fractionalLayersOut)
1061 {
1062 // each layer has a fractional percentage/weight and a total amount, determined by the total of all weights for that layer
1063 // and net amounts across all currencies in that layer. each layer also includes a list of all currencies.
1064 //
1065 // calculate a fractional buy at the total layer ratio for the amount specified
1066 // and divide the value according to the relative weight of each currency, adding to each entry of fractionalOutMap
1067 arith_uint256 bigLayerWeight = arith_uint256(layer.first);
1068 CAmount totalLayerReserves = ((bigSupply * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReserves;
1069 addNormalizedReserves += layer.second.first;
1070 CAmount newSupply = CalculateFractionalOut(layer.second.first, supply + addSupply, totalLayerReserves, layer.first);
c8c684e9 1071 if (newSupply < 0)
1072 {
1073 LogPrintf("%s: currency supply OVERFLOW\n", __func__);
1074 return initialRates;
1075 }
f88ddf77 1076 arith_uint256 bigNewSupply(newSupply);
1077 addSupply += newSupply;
1078 for (auto &id : layer.second.second)
1079 {
1080 auto idIT = fractionalOutMap.find(id);
1081 CAmount newSupplyForCurrency = ((bigNewSupply * weights[reserveMap[id]]) / bigLayerWeight).GetLow64();
1082
1083 // initialize or add to the new supply for this currency
1084 if (idIT == fractionalOutMap.end())
1085 {
1086 fractionalOutMap[id] = std::make_pair(newSupplyForCurrency, int64_t(0));
1087 }
1088 else
1089 {
1090 idIT->second.first += newSupplyForCurrency;
1091 }
1092 }
1093 }
1094
1095 supplyAfterBuy = supply + addSupply;
da4c6118 1096 assert(supplyAfterBuy >= 0);
f88ddf77 1097
d9f3d1c3 1098 reserveAfterBuy = supply + addNormalizedReserves;
f88ddf77 1099 assert(reserveAfterBuy >= 0);
1100
1101 addSupply = 0;
1102 addNormalizedReserves = 0;
1103 CAmount addNormalizedReservesBB = 0, addNormalizedReservesAB = 0;
1104
1105 // calculate sell both before and after buy through this loop
1106 for (auto &layer : fractionalLayersIn)
1107 {
1108 // first calculate sell before-buy, then after-buy
1109 arith_uint256 bigLayerWeight(layer.first);
1110
1111 // before-buy starting point
c8c684e9 1112 CAmount totalLayerReservesBB = ((bigSupply * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReservesBB;
1113 CAmount totalLayerReservesAB = ((arith_uint256(supplyAfterBuy) * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReservesAB;
f88ddf77 1114
1115 CAmount newNormalizedReserveBB = CalculateReserveOut(layer.second.first, supply + addSupply, totalLayerReservesBB + addNormalizedReservesBB, layer.first);
1116 CAmount newNormalizedReserveAB = CalculateReserveOut(layer.second.first, supplyAfterBuy + addSupply, totalLayerReservesAB + addNormalizedReservesAB, layer.first);
1117
1118 // input fractional is burned and output reserves are removed from reserves
1119 addSupply -= layer.second.first;
1120 addNormalizedReservesBB -= newNormalizedReserveBB;
1121 addNormalizedReservesAB -= newNormalizedReserveAB;
1122
1123 for (auto &id : layer.second.second)
1124 {
1125 auto idIT = fractionalInMap.find(id);
99231ee6 1126 CAmount newReservesForCurrencyBB = ((arith_uint256(newNormalizedReserveBB) * arith_uint256(weights[reserveMap[id]])) / bigLayerWeight).GetLow64();
1127 CAmount newReservesForCurrencyAB = ((arith_uint256(newNormalizedReserveAB) * arith_uint256(weights[reserveMap[id]])) / bigLayerWeight).GetLow64();
f88ddf77 1128
1129 // initialize or add to the new supply for this currency
1130 if (idIT == fractionalInMap.end())
1131 {
1132 fractionalInMap[id] = std::make_pair(newReservesForCurrencyBB, newReservesForCurrencyAB);
1133 }
1134 else
1135 {
1136 idIT->second.first += newReservesForCurrencyBB;
1137 idIT->second.second += newReservesForCurrencyAB;
1138 }
1139 }
1140 }
1141
1142 supplyAfterSell = supply + addSupply;
1143 assert(supplyAfterSell >= 0);
1144
1145 supplyAfterBuySell = supplyAfterBuy + addSupply;
1146 assert(supplyAfterBuySell >= 0);
1147
d9f3d1c3 1148 reserveAfterSell = supply + addNormalizedReservesBB;
f88ddf77 1149 assert(reserveAfterSell >= 0);
1150
5026a487 1151 reserveAfterBuySell = reserveAfterBuy + addNormalizedReservesAB;
f88ddf77 1152 assert(reserveAfterBuySell >= 0);
1153
1154 addSupply = 0;
1155 addNormalizedReserves = 0;
1156
1157 // now calculate buy after sell
1158 for (auto &layer : fractionalLayersOut)
1159 {
1160 arith_uint256 bigLayerWeight = arith_uint256(layer.first);
1161 CAmount totalLayerReserves = ((arith_uint256(supplyAfterSell) * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReserves;
1162 addNormalizedReserves += layer.second.first;
1163 CAmount newSupply = CalculateFractionalOut(layer.second.first, supplyAfterSell + addSupply, totalLayerReserves, layer.first);
1164 arith_uint256 bigNewSupply(newSupply);
1165 addSupply += newSupply;
1166 for (auto &id : layer.second.second)
1167 {
1168 auto idIT = fractionalOutMap.find(id);
1169
1170 assert(idIT != fractionalOutMap.end());
1171
1172 idIT->second.second += ((bigNewSupply * weights[reserveMap[id]]) / bigLayerWeight).GetLow64();
1173 }
1174 }
1175
1176 // now loop through all currencies, calculate conversion rates for each based on mean of all prices that we calculate for
1177 // buy before sell and sell before buy
e4e5ccf5 1178 std::vector<int64_t> fractionalSizes(numCurrencies,0);
1179 std::vector<int64_t> reserveSizes(numCurrencies,0);
1180
1181 for (int i = 0; i < numCurrencies; i++)
f88ddf77 1182 {
1183 // each coin has an amount of reserve in, an amount of fractional in, and potentially two delta amounts in one of the
1184 // fractionalInMap or fractionalOutMap maps, one for buy before sell and one for sell before buy.
1185 // add the mean of the delta amounts to the appropriate side of the equation and calculate a price for each
1186 // currency.
1187 auto fractionalOutIT = fractionalOutMap.find(currencies[i]);
1188 auto fractionalInIT = fractionalInMap.find(currencies[i]);
1189
1190 auto inputReserve = inputReserves[i];
1191 auto inputFraction = inputFractional[i];
e4e5ccf5 1192 reserveSizes[i] = inputReserve;
1193 fractionalSizes[i] = inputFraction;
f88ddf77 1194
1195 CAmount fractionDelta = 0, reserveDelta = 0;
1196
1197 if (fractionalOutIT != fractionalOutMap.end())
1198 {
1199 arith_uint256 bigFractionDelta(fractionalOutIT->second.first);
1200 fractionDelta = ((bigFractionDelta + arith_uint256(fractionalOutIT->second.second)) >> 1).GetLow64();
1201 assert(inputFraction + fractionDelta > 0);
d3c97fd3 1202
e4e5ccf5 1203 fractionalSizes[i] += fractionDelta;
1204 rates[i] = ((arith_uint256(inputReserve) * bigSatoshi) / arith_uint256(fractionalSizes[i])).GetLow64();
f88ddf77 1205
1206 // add the new reserve and supply to the currency
1207 newState.supply += fractionDelta;
1208
1209 // all reserves have been calculated using a substituted value, which was 1:1 for native initially
05939b4c 1210 newState.reserves[i] += inputFractional[i] ? NativeToReserveRaw(fractionDelta, rates[i]) : inputReserves[i];
f88ddf77 1211 }
1212 else if (fractionalInIT != fractionalInMap.end())
1213 {
da4c6118 1214 arith_uint256 bigReserveDelta(fractionalInIT->second.first);
81881107 1215 CAmount adjustedReserveDelta = NativeToReserve(((bigReserveDelta + arith_uint256(fractionalInIT->second.second)) >> 1).GetLow64(), i);
e4e5ccf5 1216 reserveSizes[i] += adjustedReserveDelta;
f88ddf77 1217 assert(inputFraction > 0);
d9f3d1c3 1218
e4e5ccf5 1219 rates[i] = ((arith_uint256(reserveSizes[i]) * bigSatoshi) / arith_uint256(inputFraction)).GetLow64();
f88ddf77 1220
1221 // subtract the fractional and reserve that has left the currency
a6bd3ba5 1222 newState.supply -= inputFraction;
81881107 1223 newState.reserves[i] -= adjustedReserveDelta;
f88ddf77 1224 }
26e2ca05 1225 }
1226
1227 // if we have cross conversions, complete a final conversion with the updated currency, including all of the
1228 // cross conversion outputs to their final currency destinations
1229 if (pCrossConversions)
1230 {
1231 bool convertRToR = false;
1232 std::vector<CAmount> reservesRToR(numCurrencies, 0); // keep track of reserve inputs to convert to the fractional currency
818494a0 1233
26e2ca05 1234 // now add all cross conversions, determine how much of the converted fractional should be converted back to each
1235 // reserve currency. after adding all together, convert all to each reserve and average the price again
1236 for (int i = 0; i < numCurrencies; i++)
1237 {
1238 // add up all conversion amounts for each fractional to each reserve-to-reserve conversion
1239 for (int j = 0; j < numCurrencies; j++)
1240 {
1241 // convert this much of currency indexed by i into currency indexed by j
1242 // figure out how much fractional the amount of currency represents and add it to the total
1243 // fractionalIn for the currency indexed by j
1244 if ((*pCrossConversions)[i][j])
1245 {
1246 convertRToR = true;
1247 reservesRToR[i] += (*pCrossConversions)[i][j];
1248 }
1249 }
1250 }
1251
1252 if (convertRToR)
818494a0 1253 {
26e2ca05 1254 std::vector<CAmount> scratchValues(numCurrencies, 0);
1255 std::vector<CAmount> fractionsToConvert(numCurrencies, 0);
1256
1257 // add fractional created to be converted to its destination
1258 for (int i = 0; i < reservesRToR.size(); i++)
1259 {
1260 if (reservesRToR[i])
1261 {
1262 for (int j = 0; j < (*pCrossConversions)[i].size(); j++)
1263 {
1264 if ((*pCrossConversions)[i][j])
1265 {
1266 fractionsToConvert[j] += ReserveToNativeRaw((*pCrossConversions)[i][j], rates[i]);
1267 }
1268 }
1269 }
1270 }
1271
1272 std::vector<CAmount> _viaPrices;
1273 std::vector<CAmount> &viaPrices(pViaPrices ? *pViaPrices : _viaPrices);
1274 CCurrencyState intermediateState = newState;
1275 viaPrices = intermediateState.ConvertAmounts(scratchValues, fractionsToConvert, newState);
818494a0 1276 }
f88ddf77 1277 }
e4e5ccf5 1278
c8c684e9 1279 if (!failed)
1280 {
1281 _newState = newState;
1282 }
1283
810bc6a2 1284 for (int i = 0; i < rates.size(); i++)
1285 {
1286 if (!rates[i])
1287 {
1288 rates[i] = PriceInReserve(i);
e4e5ccf5 1289 }
1290 }
f88ddf77 1291 return rates;
1292}
1293
c8c677c9 1294CAmount CCurrencyState::ConvertAmounts(CAmount inputReserve, CAmount inputFraction, CCurrencyState &newState, int32_t reserveIndex) const
f88ddf77 1295{
e4e5ccf5 1296 int32_t numCurrencies = currencies.size();
1297 if (reserveIndex >= numCurrencies)
56fe75cb 1298 {
1299 printf("%s: reserve index out of range\n", __func__);
1300 return 0;
1301 }
e4e5ccf5 1302 std::vector<CAmount> inputReserves(numCurrencies);
56fe75cb 1303 inputReserves[reserveIndex] = inputReserve;
e4e5ccf5 1304 std::vector<CAmount> inputFractional(numCurrencies);
56fe75cb 1305 inputFractional[reserveIndex] = inputFraction;
e4e5ccf5 1306 std::vector<CAmount> retVal = ConvertAmounts(inputReserves,
1307 inputFractional,
1308 newState);
56fe75cb 1309 return retVal[reserveIndex];
f88ddf77 1310}
1311
f21903bd 1312UniValue CReserveInOuts::ToUniValue() const
1313{
1314 UniValue retVal(UniValue::VOBJ);
1315 retVal.push_back(Pair("reservein", reserveIn));
1316 retVal.push_back(Pair("reserveout", reserveOut));
1317 retVal.push_back(Pair("reserveoutconverted", reserveOutConverted));
1318 retVal.push_back(Pair("nativeoutconverted", nativeOutConverted));
1319 retVal.push_back(Pair("reserveconversionfees", reserveConversionFees));
1320 return retVal;
1321}
1322
1323UniValue CReserveTransactionDescriptor::ToUniValue() const
1324{
1325 UniValue retVal(UniValue::VOBJ);
1326 UniValue inOuts(UniValue::VARR);
1327 for (auto &oneInOut : currencies)
1328 {
1329 UniValue oneIOUni(UniValue::VOBJ);
1330 oneIOUni.push_back(Pair("currency", EncodeDestination(CIdentityID(oneInOut.first))));
1331 oneIOUni.push_back(Pair("inouts", oneInOut.second.ToUniValue()));
1332 inOuts.push_back(oneIOUni);
1333 }
1334 retVal.push_back(Pair("inouts", inOuts));
1335 return retVal;
1336}
1337
56fe75cb 1338void CReserveTransactionDescriptor::AddReserveInput(const uint160 &currency, CAmount value)
b7c685b8 1339{
0ab273d2 1340 //printf("adding %ld:%s reserve input\n", value, EncodeDestination(CIdentityID(currency)).c_str());
481e7fd6 1341 currencies[currency].reserveIn += value;
56fe75cb 1342}
b7c685b8 1343
56fe75cb 1344void CReserveTransactionDescriptor::AddReserveOutput(const uint160 &currency, CAmount value)
1345{
0ab273d2 1346 //printf("adding %ld:%s reserve output\n", value, EncodeDestination(CIdentityID(currency)).c_str());
481e7fd6 1347 currencies[currency].reserveOut += value;
56fe75cb 1348}
b7c685b8 1349
56fe75cb 1350void CReserveTransactionDescriptor::AddReserveOutConverted(const uint160 &currency, CAmount value)
1351{
481e7fd6 1352 currencies[currency].reserveOutConverted += value;
56fe75cb 1353}
b7c685b8 1354
56fe75cb 1355void CReserveTransactionDescriptor::AddNativeOutConverted(const uint160 &currency, CAmount value)
1356{
481e7fd6 1357 currencies[currency].nativeOutConverted += value;
56fe75cb 1358}
b7c685b8 1359
56fe75cb 1360void CReserveTransactionDescriptor::AddReserveConversionFees(const uint160 &currency, CAmount value)
1361{
481e7fd6 1362 currencies[currency].reserveConversionFees += value;
b7c685b8 1363}
1364
c8c684e9 1365void CReserveTransactionDescriptor::AddReserveOutput(const CTokenOutput &ro)
41f170fd 1366{
c8c684e9 1367 flags |= IS_RESERVE;
1368 for (auto &oneCur : ro.reserveValues.valueMap)
41f170fd 1369 {
c8c684e9 1370 if (oneCur.first != ASSETCHAINS_CHAINID && oneCur.second)
41f170fd 1371 {
c8c684e9 1372 AddReserveOutput(oneCur.first, oneCur.second);
41f170fd
MT
1373 }
1374 }
c8c684e9 1375}
1376
1377void CReserveTransactionDescriptor::AddReserveTransfer(const CReserveTransfer &rt)
1378{
1379 flags |= IS_RESERVE;
1380 for (auto &oneCur : rt.reserveValues.valueMap)
41f170fd 1381 {
c8c684e9 1382 if (oneCur.first != ASSETCHAINS_CHAINID && oneCur.second)
41f170fd 1383 {
c8c684e9 1384 AddReserveOutput(oneCur.first, oneCur.second);
41f170fd 1385 }
c8c684e9 1386 }
41f170fd
MT
1387}
1388
c8c677c9 1389CAmount CReserveTransactionDescriptor::AllFeesAsNative(const CCurrencyState &currencyState) const
41f170fd 1390{
56fe75cb 1391 CAmount nativeFees = NativeFees();
1392 CCurrencyValueMap reserveFees = ReserveFees();
1393 for (int i = 0; i < currencyState.currencies.size(); i++)
1394 {
1395 auto it = reserveFees.valueMap.find(currencyState.currencies[i]);
1396 if (it != reserveFees.valueMap.end())
1397 {
1398 nativeFees += currencyState.ReserveToNative(it->second, i);
1399 }
1400 }
1401 return nativeFees;
41f170fd
MT
1402}
1403
c8c677c9 1404CAmount CReserveTransactionDescriptor::AllFeesAsNative(const CCurrencyState &currencyState, const std::vector<CAmount> &exchangeRates) const
41f170fd 1405{
56fe75cb 1406 assert(exchangeRates.size() == currencyState.currencies.size());
1407 CAmount nativeFees = NativeFees();
1408 CCurrencyValueMap reserveFees = ReserveFees();
1409 for (int i = 0; i < currencyState.currencies.size(); i++)
1410 {
1411 auto it = reserveFees.valueMap.find(currencyState.currencies[i]);
1412 if (it != reserveFees.valueMap.end())
1413 {
1414 nativeFees += currencyState.ReserveToNativeRaw(it->second, exchangeRates[i]);
1415 }
1416 }
1417 return nativeFees;
41f170fd
MT
1418}
1419
481e7fd6 1420CCurrencyValueMap CReserveTransactionDescriptor::ReserveFees(const uint160 &nativeID) const
1421{
1422 uint160 id = nativeID.IsNull() ? ASSETCHAINS_CHAINID : nativeID;
1423 CCurrencyValueMap retFees;
1424 for (auto &one : currencies)
1425 {
1426 // skip native
1427 if (one.first != id)
1428 {
1429 CAmount oneFee = one.second.reserveIn - (one.second.reserveOut - one.second.reserveOutConverted);
1430 if (oneFee)
1431 {
1432 retFees.valueMap[one.first] = oneFee;
1433 }
1434 }
1435 }
1436 return retFees;
1437}
1438
1439CAmount CReserveTransactionDescriptor::NativeFees() const
1440{
1441 return nativeIn - nativeOut;
1442}
1443
c8c677c9 1444CCurrencyValueMap CReserveTransactionDescriptor::AllFeesAsReserve(const CCurrencyState &currencyState, int defaultReserve) const
41f170fd 1445{
56fe75cb 1446 CCurrencyValueMap reserveFees = ReserveFees();
1447
1448 auto it = reserveFees.valueMap.find(currencyState.currencies[defaultReserve]);
1449 if (it != reserveFees.valueMap.end())
1450 {
1451 it->second += currencyState.NativeToReserve(NativeFees(), defaultReserve);
1452 }
1453 else
1454 {
1455 reserveFees.valueMap[currencyState.currencies[defaultReserve]] = NativeFees();
1456 }
1457 return reserveFees;
50613888 1458}
1459
c8c677c9 1460CCurrencyValueMap CReserveTransactionDescriptor::AllFeesAsReserve(const CCurrencyState &currencyState, const std::vector<CAmount> &exchangeRates, int defaultReserve) const
50613888 1461{
56fe75cb 1462 CCurrencyValueMap reserveFees = ReserveFees();
1463
1464 auto it = reserveFees.valueMap.find(currencyState.currencies[defaultReserve]);
1465 if (it != reserveFees.valueMap.end())
1466 {
1467 it->second += currencyState.NativeToReserveRaw(NativeFees(), exchangeRates[defaultReserve]);
1468 }
1469 else
1470 {
1471 reserveFees.valueMap[currencyState.currencies[defaultReserve]] = NativeFees();
1472 }
1473 return reserveFees;
41f170fd
MT
1474}
1475
e7c700b5
MT
1476/*
1477 * Checks all structural aspects of the reserve part of a transaction that may have reserve inputs and/or outputs
1478 */
283d01d5 1479CReserveTransactionDescriptor::CReserveTransactionDescriptor(const CTransaction &tx, const CCoinsViewCache &view, int32_t nHeight) :
c47c3c59 1480 flags(0),
1481 ptx(NULL),
1482 numBuys(0),
1483 numSells(0),
1484 numTransfers(0),
c47c3c59 1485 nativeIn(0),
c47c3c59 1486 nativeOut(0),
56fe75cb 1487 nativeConversionFees(0)
a6e612cc 1488{
e7c700b5
MT
1489 // market conversions can have any number of both buy and sell conversion outputs, this is used to make efficient, aggregated
1490 // reserve transfer operations with conversion
1491
1492 // limit conversion outputs may have multiple outputs with different input amounts and destinations,
1493 // but they must not be mixed in a transaction with any dissimilar set of conditions on the output,
1494 // including mixing with market orders, parity of buy or sell, limit value and validbefore values,
1495 // or the transaction is considered invalid
1496
22e5b822 1497 // no inputs are valid at height 0
1498 if (!nHeight)
1499 {
1500 flags |= IS_REJECT;
1501 return;
1502 }
1503
389d73c0 1504 int32_t solutionVersion = CConstVerusSolutionVector::activationHeight.ActiveVersion(nHeight);
56fe75cb 1505 // reserve descriptor transactions cannot run until identity activates
389d73c0 1506 if (!chainActive.LastTip() || solutionVersion < CConstVerusSolutionVector::activationHeight.ACTIVATE_IDENTITY)
e7c700b5 1507 {
41f170fd 1508 return;
e7c700b5
MT
1509 }
1510
76a2e430 1511 bool isPBaaSActivation = CConstVerusSolutionVector::activationHeight.IsActivationHeight(CActivationHeight::ACTIVATE_PBAAS, nHeight);
a20f2622 1512
a20f2622 1513 CNameReservation nameReservation;
1514 CIdentity identity;
c8c684e9 1515
1516 std::vector<CPBaaSNotarization> notarizations;
1517 CCurrencyValueMap importGeneratedCurrency;
f21903bd 1518 CCurrencyValueMap preConvertedReserves;
a20f2622 1519
c3ebe701 1520 flags |= IS_VALID;
1521
41f170fd 1522 for (int i = 0; i < tx.vout.size(); i++)
e7c700b5
MT
1523 {
1524 COptCCParams p;
b4d1aa44 1525
f80bc5fd 1526 if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
e7c700b5
MT
1527 {
1528 switch (p.evalCode)
1529 {
a20f2622 1530 case EVAL_IDENTITY_RESERVATION:
1531 {
1532 // one name reservation per transaction
1533 if (p.version < p.VERSION_V3 || !p.vData.size() || nameReservation.IsValid() || !(nameReservation = CNameReservation(p.vData[0])).IsValid())
1534 {
1535 flags &= ~IS_VALID;
1536 flags |= IS_REJECT;
1537 return;
1538 }
1539 if (identity.IsValid())
1540 {
1541 if (identity.name == nameReservation.name)
1542 {
1543 flags |= IS_IDENTITY_DEFINITION + IS_HIGH_FEE;
1544 }
1545 else
1546 {
1547 flags &= ~IS_VALID;
1548 flags |= IS_REJECT;
1549 return;
1550 }
1551 }
1552 }
1553 break;
1554
1555 case EVAL_IDENTITY_PRIMARY:
1556 {
1557 // one identity per transaction
389d73c0 1558 if (p.version < p.VERSION_V3 ||
1559 !p.vData.size() ||
1560 (solutionVersion < CActivationHeight::ACTIVATE_PBAAS && identity.IsValid()) ||
1561 !(identity = CIdentity(p.vData[0])).IsValid())
a20f2622 1562 {
1563 flags &= ~IS_VALID;
1564 flags |= IS_REJECT;
1565 return;
1566 }
1567 flags |= IS_IDENTITY;
1568 if (nameReservation.IsValid())
1569 {
1570 if (identity.name == nameReservation.name)
1571 {
1572 flags |= IS_IDENTITY_DEFINITION + IS_HIGH_FEE;
1573 }
1574 else
1575 {
1576 flags &= ~IS_VALID;
1577 flags |= IS_REJECT;
1578 return;
1579 }
1580 }
1581 }
1582 break;
1583
0ab273d2 1584 case EVAL_RESERVE_DEPOSIT:
2d8b9129 1585 {
1586 CReserveDeposit rd;
1587 if (!p.vData.size() || !(rd = CReserveDeposit(p.vData[0])).IsValid())
1588 {
1589 flags &= ~IS_VALID;
1590 flags |= IS_REJECT;
1591 return;
1592 }
1593 for (auto &oneCur : rd.reserveValues.valueMap)
1594 {
1595 if (oneCur.first != ASSETCHAINS_CHAINID)
1596 {
1597 AddReserveOutput(oneCur.first, oneCur.second);
1598 }
1599 }
1600 }
1601 break;
1602
e7c700b5 1603 case EVAL_RESERVE_OUTPUT:
b4d1aa44 1604 {
56fe75cb 1605 CTokenOutput ro;
1606 if (!p.vData.size() || !(ro = CTokenOutput(p.vData[0])).IsValid())
e7c700b5 1607 {
c3ebe701 1608 flags &= ~IS_VALID;
b4d1aa44 1609 flags |= IS_REJECT;
1610 return;
e7c700b5 1611 }
c8c684e9 1612 for (auto &oneCur : ro.reserveValues.valueMap)
0ab273d2 1613 {
c8c684e9 1614 if (oneCur.first != ASSETCHAINS_CHAINID && oneCur.second)
1615 {
1616 AddReserveOutput(oneCur.first, oneCur.second);
1617 }
0ab273d2 1618 }
b4d1aa44 1619 }
1620 break;
e7c700b5
MT
1621
1622 case EVAL_RESERVE_TRANSFER:
b4d1aa44 1623 {
1624 CReserveTransfer rt;
4370ea6f 1625 if (!p.vData.size() || !(rt = CReserveTransfer(p.vData[0])).IsValid())
e7c700b5 1626 {
c3ebe701 1627 flags &= ~IS_VALID;
b4d1aa44 1628 flags |= IS_REJECT;
1629 return;
e7c700b5 1630 }
b4d1aa44 1631 AddReserveTransfer(rt);
1632 }
1633 break;
e7c700b5
MT
1634
1635 case EVAL_RESERVE_EXCHANGE:
b4d1aa44 1636 {
c8c684e9 1637 flags &= ~IS_VALID;
1638 flags |= IS_REJECT;
1639 return;
1640 }
1641 break;
1642
1643 case EVAL_CROSSCHAIN_IMPORT:
1644 {
1645 CCrossChainImport cci, sysCCI;
1646
1647 // if this is an import, add the amount imported to the reserve input and the amount of reserve output as
1648 // the amount available to take from this transaction in reserve as an import fee
389d73c0 1649 if (!p.vData.size() || !(cci = CCrossChainImport(p.vData[0])).IsValid())
b4d1aa44 1650 {
c3ebe701 1651 flags &= ~IS_VALID;
b4d1aa44 1652 flags |= IS_REJECT;
1653 return;
1654 }
1655
4c5696b1 1656 flags |= (IS_IMPORT + IS_HIGH_FEE);
c8c684e9 1657
1658 CCurrencyDefinition importCurrencyDef, sourceSystemDef;
1659 CCrossChainExport ccx;
1660 int32_t sysCCIOut;
1661 notarizations.push_back(CPBaaSNotarization());
1662 CPBaaSNotarization &importNotarization = notarizations.back();
1663
1664 int32_t importNotarizationOut;
1665 int32_t eOutStart, eOutEnd;
1666 std::vector<CReserveTransfer> importTransfers;
1667
389d73c0 1668 // if this is the source system for a cci that we already processed, skip it
56c49bad 1669 if ((cci.flags & cci.FLAG_SOURCESYSTEM) || (cci.flags & cci.FLAG_DEFINITIONIMPORT))
389d73c0 1670 {
1671 break;
1672 }
1673
f5c06fc0 1674 if (!cci.IsDefinitionImport())
b4d1aa44 1675 {
92aab536 1676 if (!cci.GetImportInfo(tx, i, ccx, sysCCI, sysCCIOut, importNotarization, importNotarizationOut, eOutStart, eOutEnd, importTransfers))
1677 {
1678 flags &= ~IS_VALID;
1679 flags |= IS_REJECT;
1680 return;
1681 }
1682
f5c06fc0 1683 importCurrencyDef = ConnectedChains.GetCachedCurrency(cci.importCurrencyID);
1684 sourceSystemDef = ConnectedChains.GetCachedCurrency(cci.sourceSystemID);
e7c700b5 1685
4c5696b1 1686 if (!sourceSystemDef.IsValid() || !importCurrencyDef.IsValid())
f5c06fc0 1687 {
1688 flags &= ~IS_VALID;
1689 flags |= IS_REJECT;
1690 return;
1691 }
0836d773 1692
f5c06fc0 1693 // get the chain definition of the chain we are importing
1694 std::vector<CTxOut> checkOutputs;
1695 CCurrencyValueMap importedCurrency, gatewayDeposits, spentCurrencyOut;
1696
1697 CCoinbaseCurrencyState checkState = importNotarization.currencyState;
1698 CCoinbaseCurrencyState newState;
f21903bd 1699
1700 /*
1701 printf("%s: checkState:\n%s\n",
1702 __func__,
1703 checkState.ToUniValue().write(1,2).c_str());
1704 */
f5c06fc0 1705 checkState.RevertReservesAndSupply();
4c5696b1 1706 if (cci.IsInitialLaunchImport())
1707 {
1708 checkState.SetLaunchClear();
1709 }
f5c06fc0 1710
f21903bd 1711 CReserveTransactionDescriptor rtxd;
1712 if (!rtxd.AddReserveTransferImportOutputs(sourceSystemDef,
1713 ConnectedChains.thisChain,
1714 importCurrencyDef,
1715 checkState,
1716 importTransfers,
1717 checkOutputs,
1718 importedCurrency,
1719 gatewayDeposits,
1720 spentCurrencyOut,
1721 &newState))
f5c06fc0 1722 {
1723 flags &= ~IS_VALID;
1724 flags |= IS_REJECT;
1725 return;
1726 }
b4d1aa44 1727
f5c06fc0 1728 importGeneratedCurrency += importedCurrency;
4c5696b1 1729 if (newState.nativeOut)
f5c06fc0 1730 {
4c5696b1 1731 importGeneratedCurrency.valueMap[cci.importCurrencyID] = newState.nativeOut;
f5c06fc0 1732 }
f21903bd 1733
1734 /*
1735 printf("%s: importGeneratedCurrency:\n%s\nnewState:\n%s\n",
1736 __func__,
1737 importGeneratedCurrency.ToUniValue().write(1,2).c_str(),
1738 newState.ToUniValue().write(1,2).c_str());
1739 */
f5c06fc0 1740
1741 for (auto &oneOutCur : cci.totalReserveOutMap.valueMap)
1742 {
1743 AddReserveOutput(oneOutCur.first, oneOutCur.second);
1744 }
c3ebe701 1745 }
0836d773 1746 }
1747 break;
1748
41f170fd
MT
1749 // this check will need to be made complete by preventing mixing both here and where the others
1750 // are seen
e7c700b5 1751 case EVAL_CROSSCHAIN_EXPORT:
b4d1aa44 1752 {
c8c684e9 1753 CCrossChainExport ccx;
1754 if (!p.vData.size() ||
1755 !(ccx = CCrossChainExport(p.vData[0])).IsValid())
e7c700b5 1756 {
c3ebe701 1757 flags &= ~IS_VALID;
b4d1aa44 1758 flags |= IS_REJECT;
41f170fd 1759 return;
e7c700b5 1760 }
b4d1aa44 1761 flags |= IS_EXPORT;
b4d1aa44 1762 }
1763 break;
2f416b17 1764
1765 case EVAL_CURRENCY_DEFINITION:
1766 {
c8c684e9 1767 CCurrencyDefinition cDef;
1768 if (!p.vData.size() ||
1769 !(cDef = CCurrencyDefinition(p.vData[0])).IsValid())
1770 {
1771 flags &= ~IS_VALID;
1772 flags |= IS_REJECT;
1773 return;
1774 }
2f416b17 1775 }
1776 break;
c8c684e9 1777
1778 default:
1779 {
1780 CCurrencyValueMap output = tx.vout[i].scriptPubKey.ReserveOutValue();
1781 output.valueMap.erase(ASSETCHAINS_CHAINID);
1782 for (auto &oneOutCur : output.valueMap)
1783 {
1784 AddReserveOutput(oneOutCur.first, oneOutCur.second);
1785 }
1786 }
e7c700b5
MT
1787 }
1788 }
f21903bd 1789 /*
1790 if (flags & IS_IMPORT)
1791 {
1792 printf("currencies after proccessing code %d:\n", p.evalCode);
1793 for (auto &oneInOut : currencies)
1794 {
1795 printf("{\"currency\":\"%s\",\"nativeOutConverted\":\"%ld\",\"reserveConversionFees\":\"%ld\",\"reserveIn\":\"%ld\",\"reserveOut\":\"%ld\",\"reserveOutConverted\":\"%ld\"}\n",
1796 EncodeDestination(CIdentityID(oneInOut.first)).c_str(),
1797 oneInOut.second.nativeOutConverted,
1798 oneInOut.second.reserveConversionFees,
1799 oneInOut.second.reserveIn,
1800 oneInOut.second.reserveOut,
1801 oneInOut.second.reserveOutConverted);
1802 }
1803 }
1804 */
e7c700b5
MT
1805 }
1806
1807 // we have all inputs, outputs, and fees, if check inputs, we can check all for consistency
1808 // inputs may be in the memory pool or on the blockchain
56fe75cb 1809 CAmount dummyInterest;
5323e2a7 1810 nativeOut = tx.GetValueOut();
56fe75cb 1811 nativeIn = view.GetValueIn(nHeight, &dummyInterest, tx);
5323e2a7 1812
481e7fd6 1813 if (importGeneratedCurrency.valueMap.count(ASSETCHAINS_CHAINID))
1814 {
1815 nativeIn += importGeneratedCurrency.valueMap[ASSETCHAINS_CHAINID];
1816 importGeneratedCurrency.valueMap.erase(ASSETCHAINS_CHAINID);
1817 }
283d01d5 1818
9665fcad 1819 // if it is a conversion to reserve, the amount in is accurate, since it is from the native coin, if converting to
1820 // the native PBaaS coin, the amount input is a sum of all the reserve token values of all of the inputs
481e7fd6 1821 auto reservesIn = (view.GetReserveValueIn(nHeight, tx) + importGeneratedCurrency).CanonicalMap();
4c5696b1 1822
1823 /* if (flags & IS_IMPORT)
1824 {
1825 printf("%s: imported currency:\n%s\nreservesIn:\n%s\n", __func__, importGeneratedCurrency.ToUniValue().write(1,2).c_str(),
1826 reservesIn.ToUniValue().write(1,2).c_str());
1827 } */
38260c2f 1828 for (auto &oneCur : currencies)
9665fcad 1829 {
38260c2f 1830 oneCur.second.reserveIn = 0;
481e7fd6 1831 }
38260c2f 1832 if (reservesIn.valueMap.size())
481e7fd6 1833 {
38260c2f 1834 flags |= IS_RESERVE;
1835 for (auto &oneCur : reservesIn.valueMap)
9665fcad 1836 {
38260c2f 1837 currencies[oneCur.first].reserveIn = oneCur.second;
9665fcad 1838 }
1839 }
481e7fd6 1840
9665fcad 1841 if (!IsReserve() && ReserveOutputMap().valueMap.size())
1842 {
1843 flags |= IS_RESERVE;
41f170fd 1844 }
9665fcad 1845
9665fcad 1846 ptx = &tx;
41f170fd
MT
1847}
1848
61ee8dc0 1849// this is only valid when used after AddReserveTransferImportOutputs on an empty CReserveTransactionDwescriptor
481e7fd6 1850CCurrencyValueMap CReserveTransactionDescriptor::GeneratedImportCurrency(const uint160 &fromSystemID, const uint160 &importSystemID, const uint160 &importCurrencyID) const
1851{
1852 // only currencies that are controlled by the exporting chain or created in conversion by the importing currency
1853 // can be created from nothing
1854 // add newly created currency here that meets those criteria
1855 CCurrencyValueMap retVal;
1856 for (auto one : currencies)
1857 {
9e7db725 1858 bool isImportCurrency = one.first == importCurrencyID;
1859 if ((one.second.nativeOutConverted && isImportCurrency) ||
1860 (one.second.reserveIn && fromSystemID != ASSETCHAINS_CHAINID && ConnectedChains.GetCachedCurrency(one.first).systemID == fromSystemID))
481e7fd6 1861 {
9e7db725 1862 retVal.valueMap[one.first] = isImportCurrency ? one.second.nativeOutConverted : one.second.reserveIn;
481e7fd6 1863 }
1864 }
1865 return retVal;
1866}
1867
0ff96ff6 1868CReserveTransfer CReserveTransfer::GetRefundTransfer() const
20631bf1 1869{
0ff96ff6 1870 CReserveTransfer rt = *this;
1871
1872 // convert full ID destinations to normal ID outputs, since it's refund, full ID will be on this chain already
1873 if (rt.destination.type == CTransferDestination::DEST_FULLID)
20631bf1 1874 {
0ff96ff6 1875 CIdentity(rt.destination.destination);
1876 rt.destination = CTransferDestination(CTransferDestination::DEST_ID, rt.destination.destination);
1877 }
20631bf1 1878
0ff96ff6 1879 // turn it into a normal transfer, which will create an unconverted output
c8c684e9 1880 rt.flags &= ~(CReserveTransfer::DOUBLE_SEND | CReserveTransfer::PRECONVERT | CReserveTransfer::CONVERT);
20631bf1 1881
0ff96ff6 1882 if (rt.flags & (CReserveTransfer::PREALLOCATE | CReserveTransfer::MINT_CURRENCY))
1883 {
1884 rt.flags &= ~(CReserveTransfer::PREALLOCATE | CReserveTransfer::MINT_CURRENCY);
c8c684e9 1885 rt.reserveValues.valueMap.begin()->second = 0;
0ff96ff6 1886 }
c8c684e9 1887 rt.flags |= rt.REFUND;
1888 rt.destCurrencyID = rt.reserveValues.valueMap.begin()->first;
0ff96ff6 1889 return rt;
1890}
20631bf1 1891
c8c684e9 1892bool CReserveTransfer::GetTxOut(const CCurrencyValueMap &reserves, int64_t nativeAmount, CTxOut &txOut) const
1893{
1894 if (HasNextLeg())
1895 {
1896 CReserveTransfer nextLegTransfer = CReserveTransfer(CReserveTransfer::VERSION_INVALID);
1897
1898 // if we have a nested transfer, use it
1899 if (destination.type == destination.DEST_NESTEDTRANSFER)
1900 {
1901 // get the reserve transfer from the raw data and
1902 CReserveTransfer rt(destination.destination);
1903 if (rt.IsValid())
1904 {
1905 // input currency, not fees, come from the output of the
1906 // last leg. fees are converted and transfered independently.
1907 rt.reserveValues = reserves;
1908 rt.feeCurrencyID = destination.gatewayID;
1909 rt.destination.fees = destination.fees;
1910 nextLegTransfer = rt;
1911 }
1912 }
1913 else
1914 {
1915 // make an output to the gateway ID, which should be another system, since there is
1916 // no reserve transfer left for instructions to do anything else worth another leg
1917 CTransferDestination lastLegDest = CTransferDestination(destination);
1918 lastLegDest.ClearGatewayLeg();
1919 nextLegTransfer = CReserveTransfer(CReserveTransfer::VALID,
1920 reserves,
1921 FeeCurrencyID(),
1922 destination.fees,
1923 destination.gatewayID,
1924 lastLegDest);
1925 }
1926 if (nextLegTransfer.IsValid())
1927 {
1928 // emit a reserve exchange output
1929 CCcontract_info CC;
1930 CCcontract_info *cp;
1931 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
1932 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
1933
1934 // transfer it back to the source chain and to our address
1935 std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID()});
1936 txOut = CTxOut(nativeAmount, MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &nextLegTransfer)));
1937 return true;
1938 }
1939 }
1940 else
1941 {
1942 // make normal output to the destination, which must be valid
1943 if (!reserves.valueMap.size() && nativeAmount)
1944 {
1945 CTxDestination dest = TransferDestinationToDestination(destination);
f1a298ef 1946 if (dest.which() == COptCCParams::ADDRTYPE_ID ||
1947 dest.which() == COptCCParams::ADDRTYPE_PK ||
1948 dest.which() == COptCCParams::ADDRTYPE_PKH ||
1949 dest.which() == COptCCParams::ADDRTYPE_SH)
c8c684e9 1950 {
1951 txOut = CTxOut(nativeAmount, GetScriptForDestination(dest));
1952 return true;
1953 }
1954 }
1955 else
1956 {
1957 CTxDestination dest = TransferDestinationToDestination(destination);
f1a298ef 1958 if (dest.which() == COptCCParams::ADDRTYPE_ID || dest.which() == COptCCParams::ADDRTYPE_PK || dest.which() == COptCCParams::ADDRTYPE_PKH)
c8c684e9 1959 {
1960 std::vector<CTxDestination> dests = std::vector<CTxDestination>({TransferDestinationToDestination(destination)});
1961 CTokenOutput ro = CTokenOutput(reserves);
1962 txOut = CTxOut(nativeAmount, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro)));
1963 return true;
1964 }
1965 }
1966 }
1967 return false;
1968}
1969
0ff96ff6 1970CReserveTransfer RefundExport(const CBaseChainObject *objPtr)
1971{
1972 if (objPtr->objectType == CHAINOBJ_RESERVETRANSFER)
1973 {
1974 return ((CChainObject<CReserveTransfer> *)objPtr)->object.GetRefundTransfer();
20631bf1 1975 }
1976 return CReserveTransfer();
1977}
1978
56fe75cb 1979// the source currency indicates the system from which the import comes, but the imports may contain additional
1980// currencies that are supported in that system and are not limited to the native currency. Fees are assumed to
cc3d5cb5 1981// be covered by the native currency of the source or source currency, if this is a reserve conversion. That
1982// means that all explicit fees are assumed to be in the currency of the source.
c8c684e9 1983bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const CCurrencyDefinition &systemSource,
1984 const CCurrencyDefinition &systemDest,
cc3d5cb5 1985 const CCurrencyDefinition &importCurrencyDef,
1986 const CCoinbaseCurrencyState &importCurrencyState,
c8c684e9 1987 const std::vector<CReserveTransfer> &exportObjects,
cc3d5cb5 1988 std::vector<CTxOut> &vOutputs,
c8c684e9 1989 CCurrencyValueMap &importedCurrency,
1990 CCurrencyValueMap &gatewayDepositsIn,
1991 CCurrencyValueMap &spentCurrencyOut,
cc3d5cb5 1992 CCoinbaseCurrencyState *pNewCurrencyState)
15e4d481 1993{
cc3d5cb5 1994 // easy way to refer to return currency state or a dummy without conditionals
1995 CCoinbaseCurrencyState _newCurrencyState;
1996 if (!pNewCurrencyState)
1997 {
1998 pNewCurrencyState = &_newCurrencyState;
1999 }
2000 CCoinbaseCurrencyState &newCurrencyState = *pNewCurrencyState;
c8c684e9 2001
cc3d5cb5 2002 newCurrencyState = importCurrencyState;
c8c684e9 2003
2004 // prepare to update ins, outs, emissions, and last pricing
d3c97fd3 2005 newCurrencyState.ClearForNextBlock();
cc3d5cb5 2006
e4e5ccf5 2007 bool isFractional = importCurrencyDef.IsFractional();
2008
05cef42a 2009 // reserve currency amounts converted to fractional
2010 CCurrencyValueMap reserveConverted;
2011
2012 // fractional currency amount and the reserve it is converted to
8e522b06 2013 CCurrencyValueMap fractionalConverted;
2014
1e561fd3 2015 std::map<uint160,int32_t> currencyIndexMap = importCurrencyDef.GetCurrenciesMap();
e4e5ccf5 2016
c8c684e9 2017 uint160 systemSourceID = systemSource.GetID();
2018 uint160 systemDestID = systemDest.GetID(); // native on destination system
e4e5ccf5 2019 uint160 importCurrencyID = importCurrencyDef.GetID();
2020 //printf("%s\n", importCurrencyDef.ToUniValue().write(1,2).c_str());
2021
2022 // this matrix tracks n-way currency conversion
2023 // each entry contains the original amount of the row's (dim 0) currency to be converted to the currency position of its column
2024 int32_t numCurrencies = importCurrencyDef.currencies.size();
2025 std::vector<std::vector<CAmount>> crossConversions(numCurrencies, std::vector<CAmount>(numCurrencies, 0));
2026 int32_t systemDestIdx = currencyIndexMap.count(systemDestID) ? currencyIndexMap[systemDestID] : -1;
1e561fd3 2027
8e522b06 2028 // used to keep track of burned fractional currency. this currency is subtracted from the
2029 // currency supply, but not converted. In doing so, it can either raise the price of the fractional
c8c684e9 2030 // currency in all other currencies, or increase the reserve ratio of all currencies by some amount.
779442f3 2031 CAmount burnedChangePrice = 0;
e4e5ccf5 2032 CAmount burnedChangeWeight = 0;
2033 CAmount secondaryBurned = 0;
05cef42a 2034
cc3d5cb5 2035 // this is cached here, but only used for pre-conversions
2036 CCoinbaseCurrencyState initialCurrencyState;
f75cf5cb 2037 CCurrencyValueMap preConvertedOutput;
481e7fd6 2038 CCurrencyValueMap preConvertedReserves;
28ce5cdb 2039 CAmount preAllocTotal = 0;
cc3d5cb5 2040
c8c684e9 2041 // determine if we are importing from a gateway currency
2042 // if so, we can use it to mint gateway currencies via the gateway, and deal with fees and conversions on
2043 // our converter currency
2044 uint160 nativeSourceCurrencyID = systemSource.IsGateway() ? systemSource.gatewayID : systemSource.systemID;
2045 if (nativeSourceCurrencyID != systemSourceID)
2046 {
2047 printf("%s: systemSource import %s is not from either gateway, PBaaS chain, or other system level currency\n", __func__, systemSource.name.c_str());
2048 LogPrintf("%s: systemSource import %s is not from either gateway, PBaaS chain, or other system level currency\n", __func__, systemSource.name.c_str());
2049 return false;
2050 }
2051 bool isGatewayImport = nativeSourceCurrencyID != systemDestID;
2052
15e4d481 2053 nativeIn = 0;
15e4d481 2054 numTransfers = 0;
56fe75cb 2055 for (auto &oneInOut : currencies)
2056 {
2057 oneInOut.second.reserveIn = 0;
2058 oneInOut.second.reserveOut = 0;
2059 }
2060
15e4d481 2061 CCcontract_info CC;
2062 CCcontract_info *cp;
56fe75cb 2063
56fe75cb 2064 CCurrencyValueMap transferFees; // calculated fees based on all transfers/conversions, etc.
c8c684e9 2065 CCurrencyValueMap passThroughFees; // preserved, but converted if possible
56fe75cb 2066
2067 bool feeOutputStart = false; // fee outputs must come after all others, this indicates they have started
2068 int nFeeOutputs = 0; // number of fee outputs
15e4d481 2069
b4df9505 2070 bool carveOutSet = false;
d3c97fd3 2071 int32_t totalCarveOut;
d12837a1 2072 CCurrencyValueMap totalCarveOuts;
d3c97fd3 2073 CAmount totalMinted = 0;
481e7fd6 2074 CAmount exporterReward = 0;
b4df9505 2075
c8c684e9 2076 for (int i = 0; i <= exportObjects.size(); i++)
15e4d481 2077 {
c8c684e9 2078 CReserveTransfer curTransfer;
2079
2080 if (i == exportObjects.size())
15e4d481 2081 {
c8c684e9 2082 // this will be the primary fee output
2083 curTransfer = CReserveTransfer(CReserveTransfer::VALID + CReserveTransfer::FEE_OUTPUT,
4c5696b1 2084 ASSETCHAINS_CHAINID,
c8c684e9 2085 0,
4c5696b1 2086 ASSETCHAINS_CHAINID,
c8c684e9 2087 0,
4c5696b1 2088 ASSETCHAINS_CHAINID,
c8c684e9 2089 CTransferDestination());
2090 }
2091 else if (importCurrencyState.IsRefunding())
2092 {
2093 curTransfer = exportObjects[i].GetRefundTransfer();
2094 }
2095 else
2096 {
2097 curTransfer = exportObjects[i];
2098 }
4d62dbc5 2099
c8c684e9 2100 if (((importCurrencyID != curTransfer.FirstCurrency()) && (curTransfer.flags & curTransfer.IMPORT_TO_SOURCE)) ||
2101 ((importCurrencyID == curTransfer.FirstCurrency()) && !((curTransfer.flags & curTransfer.IMPORT_TO_SOURCE))))
2102 {
2103 printf("%s: Importing to source currency without flag or importing to destination with source flag\n", __func__);
2104 LogPrintf("%s: Importing to source currency without flag or importing to destination with source flag\n", __func__);
2105 return false;
2106 }
2107
2108 //printf("currency transfer #%d:\n%s\n", i, curTransfer.ToUniValue().write(1,2).c_str());
2109 CCurrencyDefinition _currencyDest;
2110 const CCurrencyDefinition &currencyDest = (importCurrencyID == curTransfer.destCurrencyID) ?
2111 importCurrencyDef :
2112 (_currencyDest = ConnectedChains.GetCachedCurrency(curTransfer.destCurrencyID));
2113
2114 if (!currencyDest.IsValid())
2115 {
2116 printf("%s: invalid currency or currency not found %s\n", __func__, EncodeDestination(CIdentityID(curTransfer.destCurrencyID)).c_str());
2117 LogPrintf("%s: invalid currency or currency not found %s\n", __func__, EncodeDestination(CIdentityID(curTransfer.destCurrencyID)).c_str());
2118 return false;
2119 }
f61c381e 2120
c8c684e9 2121 if (i == exportObjects.size() || curTransfer.IsValid())
2122 {
2123 CTxOut newOut;
2124
2125 // at the end, make our fee outputs
2126 if (i == exportObjects.size())
aca8db85 2127 {
c8c684e9 2128 // only tokens release pre-allocations here
2129 // PBaaS chain pre-allocations come out of the coinbase, not the first import
4c5696b1 2130 if (importCurrencyState.IsLaunchClear())
c8c684e9 2131 {
4c5696b1 2132 // we need to pay 1/2 of the launch cost for the launch system in launch fees
2133 // remainder was paid when the currency is defined
f21903bd 2134 transferFees.valueMap[importCurrencyDef.launchSystemID] += systemSource.GetCurrencyRegistrationFee();
4c5696b1 2135 if (importCurrencyDef.launchSystemID != systemDestID)
c8c684e9 2136 {
4c5696b1 2137 importedCurrency.valueMap[importCurrencyDef.launchSystemID] += systemSource.GetCurrencyRegistrationFee();
2138 AddReserveInput(importCurrencyDef.launchSystemID, transferFees.valueMap[importCurrencyDef.launchSystemID]);
2139 }
2140 else
2141 {
2142 nativeIn += systemSource.GetCurrencyRegistrationFee();
2143 }
2144
2145 if (importCurrencyState.IsLaunchConfirmed())
2146 {
2147 // if we have finished importing all pre-launch exports, create all pre-allocation outputs
2148 for (auto &onePreAlloc : importCurrencyDef.preAllocation)
c8c684e9 2149 {
4c5696b1 2150 // we need to make one output for each pre-allocation
2151 AddNativeOutConverted(importCurrencyID, onePreAlloc.second);
2152 if (importCurrencyID != systemDestID)
2153 {
2154 AddReserveOutConverted(importCurrencyID, onePreAlloc.second);
2155 }
28ce5cdb 2156 preAllocTotal += onePreAlloc.second;
4c5696b1 2157
2158 std::vector<CTxDestination> dests;
2159 if (onePreAlloc.first.IsNull())
2160 {
2161 // if pre-alloc/pre-mine goes to NULL, send it to fee recipient who mines the final export
2162 dests = std::vector<CTxDestination>({TransferDestinationToDestination(curTransfer.destination)});
2163 }
2164 else
2165 {
2166 dests = std::vector<CTxDestination>({CTxDestination(CIdentityID(onePreAlloc.first))});
2167 }
2168 CTokenOutput ro = CTokenOutput(importCurrencyID, onePreAlloc.second);
2169 vOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro))));
c8c684e9 2170 }
c8c684e9 2171 }
2172 }
2173
f21903bd 2174 // printf("%s: transferFees: %s\n", __func__, transferFees.ToUniValue().write(1,2).c_str());
2175
c8c684e9 2176 // convert all fees to the system currency of the import
2177 // fees that started in fractional are already converted, so not considered
2178 CAmount totalNativeFee = 0;
f21903bd 2179 if (importCurrencyState.IsLaunchConfirmed() && isFractional)
c8c684e9 2180 {
2181 // setup conversion matrix for fees that are converted to
2182 // native from another reserve
2183 for (auto &oneFee : transferFees.valueMap)
2184 {
2185 // only convert in second stage if we are going from one reserve to the system ID
2186 if (oneFee.first != importCurrencyID && oneFee.first != systemDestID)
2187 {
2188 auto curIt = currencyIndexMap.find(oneFee.first);
2189 if (curIt == currencyIndexMap.end())
2190 {
2191 printf("%s: Invalid fee currency for %s\n", __func__, curTransfer.ToUniValue().write(1,2).c_str());
2192 LogPrintf("%s: Invalid fee currency for %s\n", __func__, curTransfer.ToUniValue().write(1,2).c_str());
2193 return false;
2194 }
2195 int curIdx = curIt->second;
2196
2197 CAmount oneFeeValue = 0;
2198 reserveConverted.valueMap[oneFee.first] += oneFee.second;
2199 crossConversions[curIdx][systemDestIdx] += oneFee.second;
2200 oneFeeValue = importCurrencyState.ReserveToNativeRaw(oneFee.second,
2201 importCurrencyState.conversionPrice[currencyIndexMap[oneFee.first]]);
2202
2203 if (systemDestID == importCurrencyID)
2204 {
2205 AddNativeOutConverted(oneFee.first, oneFeeValue);
2206 totalNativeFee += oneFeeValue;
2207 }
2208 else
2209 {
2210 // if fractional currency is not native, one more conversion to native
2211 CAmount reserveFromFrac =
2212 CCurrencyState::NativeToReserveRaw(oneFeeValue, importCurrencyState.viaConversionPrice[systemDestIdx]);
2213 totalNativeFee += reserveFromFrac;
2214 nativeIn += reserveFromFrac;
2215 AddReserveOutConverted(systemDestID, reserveFromFrac);
2216 }
2217 }
2218 else if (oneFee.first == systemDestID)
2219 {
2220 totalNativeFee += oneFee.second;
2221 }
f21903bd 2222 else if (oneFee.first == importCurrencyID)
c8c684e9 2223 {
2224 // convert from fractional to system ID in the first, non-via stage, since this was
2225 // already fractional to begin with
2226 fractionalConverted.valueMap[systemDestID] += oneFee.second;
2227 AddNativeOutConverted(oneFee.first, -oneFee.second);
2228
2229 CAmount convertedFractionalFee = CCurrencyState::NativeToReserveRaw(oneFee.second, importCurrencyState.conversionPrice[systemDestIdx]);
2230 totalNativeFee += convertedFractionalFee;
2231 nativeIn += convertedFractionalFee;
2232 AddReserveOutConverted(systemDestID, convertedFractionalFee);
2233 }
2234 }
2235 }
c8c684e9 2236 else
2237 {
f21903bd 2238 // since there is no support for taking reserves as fees yet, split any available
2239 // reserves fee from the launch chain, for example, between us and the exporter
2240 std::vector<CTxDestination> dests({CIdentityID(importCurrencyID)});
2241 for (auto &oneFee : transferFees.valueMap)
2242 {
2243 if (oneFee.first != systemDestID && oneFee.second)
2244 {
2245 CAmount resExportFee = CCrossChainExport::CalculateExportFeeRaw(oneFee.second, numTransfers);
2246 CAmount exportSplit = CCrossChainExport::ExportReward(resExportFee);
2247 if (currencyIndexMap.count(oneFee.first))
2248 {
2249 newCurrencyState.fees[currencyIndexMap[oneFee.first]] += oneFee.second;
2250 }
2251 AddReserveOutput(oneFee.first, oneFee.second);
2252
2253 CTokenOutput ro = CTokenOutput(oneFee.first, oneFee.second);
2254 vOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro))));
2255 }
fd615c6c 2256 else if (oneFee.second)
2257 {
2258 totalNativeFee = oneFee.second;
2259 }
2260
f21903bd 2261 }
c8c684e9 2262 }
aca8db85 2263
c8c684e9 2264 // export fee is sent to the export pool of the sending
2265 // system, exporter reward directly to the exporter
2266 CAmount exportFee = CCrossChainExport::CalculateExportFeeRaw(totalNativeFee, numTransfers);
2267 exporterReward = CCrossChainExport::ExportReward(exportFee);
f21903bd 2268 nativeOut += totalNativeFee;
2269 newCurrencyState.nativeFees += totalNativeFee;
fd615c6c 2270 if (!curTransfer.destination.IsValid() || !exporterReward)
c8c684e9 2271 {
4c5696b1 2272 break;
c8c684e9 2273 }
c8c684e9 2274 curTransfer = CReserveTransfer(CReserveTransfer::VALID + CReserveTransfer::FEE_OUTPUT,
4c5696b1 2275 systemDestID, exporterReward, systemDestID, 0, systemDestID, curTransfer.destination);
09551a1c 2276 }
c8c684e9 2277 else
15e4d481 2278 {
951413c7 2279 numTransfers++;
15e4d481 2280
c8c684e9 2281 CAmount explicitFees = curTransfer.nFees;
2282 transferFees.valueMap[curTransfer.feeCurrencyID] += explicitFees;
2283
2284 // see if our destination is for a gateway or other blockchain and see if we are reserving some
2285 // fees for additional routing. if so, add those fees to the pass-through fees, which will get converted
2286 // to the target native currency and subtracted from this leg
2287 if (curTransfer.destination.HasGatewayLeg() && curTransfer.destination.fees)
15e4d481 2288 {
c8c684e9 2289 // we keep the destination fees in the same currency as the normal transfer fee, but
2290 // convert it as we move through systems and only use it for delivery to the system
2291 // of the destination.
2292 if (curTransfer.destination.fees)
56fe75cb 2293 {
c8c684e9 2294 explicitFees += curTransfer.destination.fees;
56fe75cb 2295 }
2296
c8c684e9 2297 // convert fees to next destination native, if necessary/possible
2298 CCurrencyDefinition curNextDest = ConnectedChains.GetCachedCurrency(curTransfer.destination.gatewayID);
2299 uint160 nextDestSysID = curNextDest.IsGateway() ? curNextDest.gatewayID : curNextDest.systemID;
2300 // if it's already in the correct currency, nothing to do, otherwise convert if we can
2301 if (curTransfer.feeCurrencyID != nextDestSysID)
15e4d481 2302 {
f21903bd 2303 if (!isFractional ||
2304 !currencyIndexMap.count(nextDestSysID) ||
2305 !currencyIndexMap.count(curTransfer.feeCurrencyID))
5026a487 2306 {
c8c684e9 2307 printf("%s: next leg fee currency %s unavailable for conversion using %s\n", __func__, curNextDest.name.c_str(), importCurrencyDef.name.c_str());
2308 LogPrintf("%s: next leg fee currency %s unavailable for conversion using %s\n", __func__, curNextDest.name.c_str(), importCurrencyDef.name.c_str());
2309 return false;
e4e5ccf5 2310 }
c8c684e9 2311 // now, convert next leg fees, which are currently in the fee currency, to the next destination system ID,
2312 // adjust curTransfer values to reflect the new state, and continue
2313 // while we won't change the fee currency ID in the curTransfer, all pass through fees are assumed to be in
2314 // the next leg's system currency by the time it is ready to produce an output
2315
2316 CAmount oneFeeValue = 0;
2317 int feeCurIdx = currencyIndexMap[curTransfer.feeCurrencyID];
2318 int nextDestIdx = currencyIndexMap[nextDestSysID];
2319
2320 // all pass-through conversions leave a reserve-to-reserve fee behind for the conversion
2321 CAmount passThroughFee = CalculateConversionFeeNoMin(curTransfer.destination.fees) << 1;
2322 curTransfer.destination.fees -= passThroughFee;
2323
2324 passThroughFees.valueMap[curTransfer.feeCurrencyID] += passThroughFee;
2325 AddReserveConversionFees(curTransfer.feeCurrencyID, passThroughFee);
2326
2327 // half the fee comes out as a mining fee, and by leaving the rest as input, it goes directly into the currency
2328 transferFees.valueMap[curTransfer.feeCurrencyID] += passThroughFee >> 1;
2329
2330 reserveConverted.valueMap[curTransfer.feeCurrencyID] += curTransfer.destination.fees;
2331 crossConversions[feeCurIdx][nextDestIdx] += curTransfer.destination.fees;
2332 oneFeeValue = importCurrencyState.ReserveToNativeRaw(curTransfer.destination.fees, importCurrencyState.conversionPrice[feeCurIdx]);
2333
2334 // one more conversion to destination native
2335 CAmount reserveFromFrac = CCurrencyState::NativeToReserveRaw(oneFeeValue, importCurrencyState.viaConversionPrice[nextDestIdx]);
2336 curTransfer.destination.fees = reserveFromFrac;
2337 AddReserveInput(nextDestSysID, reserveFromFrac);
f21903bd 2338 AddReserveOutput(nextDestSysID, reserveFromFrac);
c8c684e9 2339 AddReserveOutConverted(nextDestSysID, reserveFromFrac);
15e4d481 2340 }
c8c684e9 2341 }
2342
2343 if (curTransfer.feeCurrencyID == systemDestID)
2344 {
2345 nativeIn += explicitFees;
2346 }
2347 else
2348 {
f21903bd 2349 // if the input will go into our currency as reserves, we only record it once on export/pre-launch
c8c684e9 2350 AddReserveInput(curTransfer.feeCurrencyID, explicitFees);
2351 }
2352
2353 // if it's from a gateway, we need to be sure that the currency it is importing is valid for the current chain
2354 if (isGatewayImport)
2355 {
2356 uint160 inputID = curTransfer.FirstCurrency();
2357 CAmount inputValue = curTransfer.FirstValue();
2358
2359 // if this currency is under control of the gateway, it is minted on the way in, otherwise, it will be
2360 // on the gateway's reserve deposits, which can be spent by imports from the gateway's converter
2361 CAmount totalCurrencyInput = inputValue;
2362
2363 // source system currency is imported, dest system must come from deposits
2364 if (curTransfer.feeCurrencyID == systemSourceID)
e4e5ccf5 2365 {
c8c684e9 2366 // if it's not a reserve of this currency, we can't process this transfer's fee
2367 if (!currencyIndexMap.count(systemSourceID))
2368 {
2369 printf("%s: currency transfer fees invalid for receiving system\n", __func__);
2370 LogPrintf("%s: currency transfer fees invalid for receiving system\n", __func__);
2371 return false;
2372 }
2373 importedCurrency.valueMap[systemSourceID] += explicitFees;
e4e5ccf5 2374 }
c8c684e9 2375 else if (curTransfer.feeCurrencyID == systemDestID)
e4e5ccf5 2376 {
c8c684e9 2377 gatewayDepositsIn.valueMap[systemDestID] += explicitFees;
e4e5ccf5 2378 }
c8c684e9 2379 else if (curTransfer.feeCurrencyID == curTransfer.FirstCurrency() &&
2380 isFractional &&
2381 currencyIndexMap.count(curTransfer.feeCurrencyID) &&
2382 importCurrencyState.IsLaunchConfirmed())
20448425 2383 {
c8c684e9 2384 totalCurrencyInput += explicitFees;
20448425 2385 }
c8c684e9 2386 else
2387 {
2388 printf("%s: pass-through fees invalid\n", __func__);
2389 LogPrintf("%s: pass-through fees invalid\n", __func__);
2390 return false;
2391 }
481e7fd6 2392
c8c684e9 2393 CCurrencyDefinition inputDef = ConnectedChains.GetCachedCurrency(inputID);
2394 if (!inputDef.IsValid())
d07e4ab8 2395 {
c8c684e9 2396 printf("%s: Invalid or unregistered currency for import from %s\n", __func__, curTransfer.ToUniValue().write().c_str());
d07e4ab8 2397 return false;
2398 }
c8c684e9 2399 if (curTransfer.IsMint())
56fe75cb 2400 {
c8c684e9 2401 printf("%s: Invalid mint operation from %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2402 return false;
56fe75cb 2403 }
c8c684e9 2404
2405 if (totalCurrencyInput)
56fe75cb 2406 {
c8c684e9 2407 // all currency input must either come from being minted on the import or existing gateway deposits
2408 if (inputDef.systemID == systemSourceID || (inputDef.IsGateway() && inputDef.gatewayID == systemSourceID))
481e7fd6 2409 {
c8c684e9 2410 importedCurrency.valueMap[inputID] += totalCurrencyInput;
481e7fd6 2411 }
08d9581f 2412 else
2413 {
c8c684e9 2414 gatewayDepositsIn.valueMap[inputID] += totalCurrencyInput;
08d9581f 2415 }
e4e5ccf5 2416
c8c684e9 2417 if (inputValue)
15197dca 2418 {
c8c684e9 2419 if (inputID == systemDestID)
15197dca 2420 {
c8c684e9 2421 nativeIn += inputValue;
2422 }
2423 else
2424 {
2425 AddReserveInput(inputID, inputValue);
15197dca 2426 }
15197dca 2427 }
a045d4f7 2428 }
ef02dad1 2429 }
c8c684e9 2430 else
15e4d481 2431 {
c8c684e9 2432 // now, fees are either in the destination native currency, or this is a fractional currency, and
2433 // we convert to see if we meet fee minimums
2434 CAmount feeEquivalent = curTransfer.nFees;
2435 if (curTransfer.feeCurrencyID != systemDestID)
cc3d5cb5 2436 {
c8c684e9 2437 if (!currencyDest.IsFractional() || !currencyIndexMap.count(curTransfer.feeCurrencyID))
cc3d5cb5 2438 {
c8c684e9 2439 printf("%s: Invalid fee currency for transfer %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2440 LogPrintf("%s: Invalid fee currency for transfer %s\n", __func__, curTransfer.ToUniValue().write().c_str());
cc3d5cb5 2441 return false;
2442 }
c8c684e9 2443 feeEquivalent = importCurrencyState.ReserveToNativeRaw(feeEquivalent, importCurrencyState.conversionPrice[currencyIndexMap[curTransfer.feeCurrencyID]]);
2444 feeEquivalent = importCurrencyState.NativeToReserveRaw(curTransfer.nFees, importCurrencyState.viaConversionPrice[systemDestIdx]);
cc3d5cb5 2445 }
2446
c8c684e9 2447 if (feeEquivalent < curTransfer.CalculateTransferFee())
815a42a6 2448 {
c8c684e9 2449 printf("%s: Incorrect fee sent with export %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2450 LogPrintf("%s: Incorrect fee sent with export %s\n", __func__, curTransfer.ToUniValue().write().c_str());
815a42a6 2451 return false;
2452 }
d07e4ab8 2453
c8c684e9 2454 if (curTransfer.FirstCurrency() == systemDestID && !curTransfer.IsMint())
5026a487 2455 {
c8c684e9 2456 nativeIn += curTransfer.FirstValue();
5026a487 2457 }
c8c684e9 2458 else
481e7fd6 2459 {
c8c684e9 2460 if (curTransfer.IsMint())
481e7fd6 2461 {
c8c684e9 2462 AddReserveInput(curTransfer.destCurrencyID, curTransfer.FirstValue());
481e7fd6 2463 }
2464 else
2465 {
c8c684e9 2466 AddReserveInput(curTransfer.FirstCurrency(), curTransfer.FirstValue());
481e7fd6 2467 }
2468 }
c8c684e9 2469 }
2470 }
2471
2472 if (curTransfer.IsPreConversion())
2473 {
2474 // pre-conversions can only come from our launch system
2475 if (importCurrencyDef.launchSystemID != systemSourceID)
2476 {
2477 printf("%s: Invalid source system for preconversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2478 LogPrintf("%s: Invalid source system for preconversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2479 return false;
2480 }
3fdaaadc 2481
f1a298ef 2482 if (importCurrencyState.IsLaunchCompleteMarker())
c8c684e9 2483 {
2484 printf("%s: Invalid preconversion after launch %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2485 LogPrintf("%s: Invalid preconversion after launch %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2486 return false;
2487 }
b4df9505 2488
c8c684e9 2489 // first time through with preconvert, initialize the starting currency state
2490 // remove initialCurrencyState when not needed
2491 if (!initialCurrencyState.IsValid())
2492 {
f1a298ef 2493 initialCurrencyState = ConnectedChains.GetCurrencyState(importCurrencyID, importCurrencyDef.startBlock - 1);
c8c684e9 2494 }
5cd8b21a 2495
c8c684e9 2496 // either the destination currency must be fractional or the source currency
2497 // must be native
2498 if (!isFractional && curTransfer.FirstCurrency() != importCurrencyDef.systemID)
2499 {
2500 printf("%s: Invalid conversion %s. Source must be native or destinaton must be fractional.\n", __func__, curTransfer.ToUniValue().write().c_str());
2501 LogPrintf("%s: Invalid conversion %s. Source must be native or destinaton must be fractional\n", __func__, curTransfer.ToUniValue().write().c_str());
2502 return false;
2503 }
e4e5ccf5 2504
c8c684e9 2505 // get currency index
2506 auto curIndexIt = currencyIndexMap.find(curTransfer.FirstCurrency());
2507 if (curIndexIt == currencyIndexMap.end())
2508 {
2509 printf("%s: Invalid currency for conversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2510 LogPrintf("%s: Invalid currency for conversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2511 return false;
2512 }
2513 int curIdx = curIndexIt->second;
2514
2515 // output the converted amount, minus fees, and generate a normal output that spends the net input of the import as native
2516 // difference between all potential value out and what was taken unconverted as a fee in our fee output
2517 CAmount preConversionFee = 0;
2518 CAmount newCurrencyConverted = 0;
2519 CAmount valueOut = curTransfer.FirstValue();
2520
2521 preConversionFee = CalculateConversionFee(curTransfer.FirstValue());
2522 if (preConversionFee > curTransfer.FirstValue())
2523 {
2524 preConversionFee = curTransfer.FirstValue();
2525 }
2526
2527 valueOut -= preConversionFee;
2528
2529 AddReserveConversionFees(curTransfer.FirstCurrency(), preConversionFee);
f21903bd 2530 transferFees.valueMap[curTransfer.FirstCurrency()] += preConversionFee;
c8c684e9 2531
2532 newCurrencyConverted = initialCurrencyState.ReserveToNativeRaw(valueOut, initialCurrencyState.conversionPrice[curIdx]);
f1a298ef 2533 if (newCurrencyConverted == -1)
2534 {
2535 // if we have an overflow, this isn't going to work
2536 newCurrencyConverted = 0;
2537 }
c8c684e9 2538
2539 if (!carveOutSet)
2540 {
2541 totalCarveOut = importCurrencyDef.GetTotalCarveOut();
2542 carveOutSet = true;
2543 }
c8c684e9 2544
f1a298ef 2545 if (newCurrencyConverted)
c8c684e9 2546 {
f21903bd 2547 reserveConverted.valueMap[curTransfer.FirstCurrency()] += valueOut;
2548 preConvertedReserves.valueMap[curTransfer.FirstCurrency()] += valueOut;
2549
f1a298ef 2550 if (totalCarveOut > 0 && totalCarveOut < SATOSHIDEN)
815a42a6 2551 {
f1a298ef 2552 CAmount newReserveIn = CCurrencyState::NativeToReserveRaw(valueOut, SATOSHIDEN - totalCarveOut);
2553 totalCarveOuts.valueMap[curTransfer.FirstCurrency()] += valueOut - newReserveIn;
2554 valueOut = newReserveIn;
56fe75cb 2555 }
f1a298ef 2556
2557 if (curTransfer.FirstCurrency() != systemDestID)
4005d836 2558 {
f1a298ef 2559 // if this is a fractional currency, everything but fees and carveouts stay in reserve deposit
2560 // else all that would be reserves is sent to chain ID
2561 if (!isFractional)
2562 {
2563 AddReserveOutput(curTransfer.FirstCurrency(), valueOut);
2564 std::vector<CTxDestination> dests({CIdentityID(importCurrencyID)});
2565 CTokenOutput ro = CTokenOutput(curTransfer.FirstCurrency(), valueOut);
2566 vOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro))));
2567 }
2568 }
2569 else
2570 {
f21903bd 2571 // if it is not fractional, send proceeds to currency ID, else leave it in reserve deposit
f1a298ef 2572 if (!isFractional)
2573 {
2574 nativeOut += valueOut;
2575 vOutputs.push_back(CTxOut(valueOut, GetScriptForDestination(CIdentityID(importCurrencyID))));
2576 }
4005d836 2577 }
20631bf1 2578
c8c684e9 2579 preConvertedOutput.valueMap[curTransfer.FirstCurrency()] += newCurrencyConverted;
2580 AddNativeOutConverted(curTransfer.FirstCurrency(), newCurrencyConverted);
2581 AddNativeOutConverted(curTransfer.destCurrencyID, newCurrencyConverted);
2582 if (curTransfer.destCurrencyID == systemDestID)
4005d836 2583 {
c8c684e9 2584 nativeOut += newCurrencyConverted;
f21903bd 2585 if (!importCurrencyState.IsLaunchConfirmed())
2586 {
2587 nativeIn += newCurrencyConverted;
2588 }
c8c684e9 2589 curTransfer.GetTxOut(CCurrencyValueMap(), newCurrencyConverted, newOut);
4005d836 2590 }
c8c684e9 2591 else
4005d836 2592 {
c8c684e9 2593 AddReserveOutConverted(curTransfer.destCurrencyID, newCurrencyConverted);
2594 AddReserveOutput(curTransfer.destCurrencyID, newCurrencyConverted);
f21903bd 2595 if (!importCurrencyState.IsLaunchConfirmed())
2596 {
2597 AddReserveInput(curTransfer.destCurrencyID, newCurrencyConverted);
2598 }
f1a298ef 2599 curTransfer.GetTxOut(CCurrencyValueMap(std::vector<uint160>({curTransfer.destCurrencyID}), std::vector<int64_t>({newCurrencyConverted})),
c8c684e9 2600 0, newOut);
4005d836 2601 }
c8c684e9 2602 }
2603 }
2604 else if (curTransfer.IsConversion())
2605 {
2606 if (curTransfer.FirstCurrency() == curTransfer.destCurrencyID)
2607 {
2608 printf("%s: Conversion does not specify two currencies\n", __func__);
2609 LogPrintf("%s: Conversion does not specify two currencies\n", __func__);
2610 return false;
2611 }
da4c6118 2612
c8c684e9 2613 // either the source or destination must be a reserve currency of the other fractional currency
2614 // if destination is a fractional currency of a reserve, we will mint currency
2615 // if not, we will burn currency
2616 bool toFractional = importCurrencyID == curTransfer.destCurrencyID &&
2617 currencyDest.IsFractional() &&
2618 currencyIndexMap.count(curTransfer.FirstCurrency());
e4e5ccf5 2619
c8c684e9 2620 CCurrencyDefinition sourceCurrency = ConnectedChains.GetCachedCurrency(curTransfer.FirstCurrency());
20631bf1 2621
c8c684e9 2622 if (!sourceCurrency.IsValid())
2623 {
2624 printf("%s: Currency specified for conversion not found\n", __func__);
2625 LogPrintf("%s: Currency specified for conversion not found\n", __func__);
2626 return false;
2627 }
4005d836 2628
c8c684e9 2629 if (!(toFractional ||
2630 (importCurrencyID == curTransfer.FirstCurrency() &&
2631 sourceCurrency.IsFractional() &&
2632 currencyIndexMap.count(curTransfer.destCurrencyID))))
2633 {
2634 printf("%s: Conversion must be between a fractional currency and one of its reserves\n", __func__);
2635 LogPrintf("%s: Conversion must be between a fractional currency and one of its reserves\n", __func__);
2636 return false;
2637 }
4f8f2b54 2638
c8c684e9 2639 if (curTransfer.IsReserveToReserve() &&
2640 (!toFractional ||
2641 curTransfer.secondReserveID.IsNull() ||
2642 curTransfer.secondReserveID == curTransfer.FirstCurrency() ||
2643 !currencyIndexMap.count(curTransfer.secondReserveID)))
2644 {
2645 printf("%s: Invalid reserve to reserve transaction %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2646 LogPrintf("%s: Invalid reserve to reserve transaction %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2647 return false;
2648 }
2649
2650 const CCurrencyDefinition &fractionalCurrency = toFractional ? currencyDest : sourceCurrency;
2651 const CCurrencyDefinition &reserveCurrency = toFractional ? sourceCurrency : currencyDest;
2652 int reserveIdx = currencyIndexMap[reserveCurrency.GetID()];
cc3d5cb5 2653
c8c684e9 2654 assert(fractionalCurrency.IsValid() &&
2655 reserveCurrency.IsValid() &&
2656 fractionalCurrency.currencies[reserveIdx] == reserveCurrency.GetID());
405ab7d8 2657
c8c684e9 2658 // now, we know that we are converting from the source currency to the
2659 // destination currency and also that one of them is a reserve of the other
2660 // we convert using the provided currency state, and we update the currency
2661 // state to include newly minted or burned currencies.
2662 CAmount valueOut = curTransfer.FirstValue();
f21903bd 2663 CAmount oneConversionFee = 0;
c8c684e9 2664 CAmount newCurrencyConverted = 0;
2665
2666 if (!(curTransfer.flags & curTransfer.FEE_OUTPUT))
2667 {
f21903bd 2668 oneConversionFee = CalculateConversionFee(curTransfer.FirstValue());
c8c684e9 2669 if (curTransfer.IsReserveToReserve())
2670 {
f21903bd 2671 oneConversionFee <<= 1;
c8c684e9 2672 }
f21903bd 2673 if (oneConversionFee > curTransfer.FirstValue())
c8c684e9 2674 {
f21903bd 2675 oneConversionFee = curTransfer.FirstValue();
e4e5ccf5 2676 }
f21903bd 2677 valueOut -= oneConversionFee;
2678 AddReserveConversionFees(curTransfer.FirstCurrency(), oneConversionFee);
2679 transferFees.valueMap[curTransfer.FirstCurrency()] += curTransfer.IsReserveToReserve() ? oneConversionFee >> 1 : oneConversionFee;
c8c684e9 2680 }
2681
2682 if (toFractional)
2683 {
2684 reserveConverted.valueMap[curTransfer.FirstCurrency()] += valueOut;
2685 newCurrencyConverted = importCurrencyState.ReserveToNativeRaw(valueOut, importCurrencyState.conversionPrice[reserveIdx]);
2686 }
2687 else
2688 {
2689 fractionalConverted.valueMap[curTransfer.destCurrencyID] += valueOut;
2690 newCurrencyConverted = importCurrencyState.NativeToReserveRaw(valueOut, importCurrencyState.conversionPrice[reserveIdx]);
2691 }
2692
2693 if (newCurrencyConverted)
2694 {
2695 uint160 outputCurrencyID;
20631bf1 2696
c8c684e9 2697 if (curTransfer.IsReserveToReserve())
e4e5ccf5 2698 {
c8c684e9 2699 // we need to convert once more from fractional to a reserve currency
2700 // we burn 0.025% of the fractional that was converted, and convert the rest to
2701 // the specified reserve. since the burn depends on the first conversion, which
2702 // it is not involved in, it is tracked separately and applied after the first conversion
2703 outputCurrencyID = curTransfer.secondReserveID;
2704 int32_t outputCurrencyIdx = currencyIndexMap[outputCurrencyID];
2705 newCurrencyConverted = CCurrencyState::NativeToReserveRaw(newCurrencyConverted, importCurrencyState.viaConversionPrice[outputCurrencyIdx]);
2706 crossConversions[reserveIdx][outputCurrencyIdx] += valueOut;
e4e5ccf5 2707 }
2708 else
2709 {
c8c684e9 2710 outputCurrencyID = curTransfer.destCurrencyID;
e4e5ccf5 2711 }
2712
c8c684e9 2713 if (toFractional && !curTransfer.IsReserveToReserve())
e4e5ccf5 2714 {
c8c684e9 2715 AddNativeOutConverted(curTransfer.FirstCurrency(), newCurrencyConverted);
2716 AddNativeOutConverted(curTransfer.destCurrencyID, newCurrencyConverted);
2717 if (curTransfer.destCurrencyID == systemDestID)
cc3d5cb5 2718 {
c8c684e9 2719 nativeOut += newCurrencyConverted;
2720 nativeIn += newCurrencyConverted;
cc3d5cb5 2721 }
20631bf1 2722 else
2723 {
c8c684e9 2724 AddReserveOutConverted(curTransfer.destCurrencyID, newCurrencyConverted);
2725 AddReserveInput(curTransfer.destCurrencyID, newCurrencyConverted);
2726 AddReserveOutput(curTransfer.destCurrencyID, newCurrencyConverted);
20631bf1 2727 }
c8c684e9 2728 }
2729 else
2730 {
2731 AddReserveOutConverted(outputCurrencyID, newCurrencyConverted);
2732 if (outputCurrencyID == systemDestID)
d3c97fd3 2733 {
c8c684e9 2734 nativeOut += newCurrencyConverted;
d3c97fd3 2735 }
2736 else
2737 {
c8c684e9 2738 AddReserveOutput(outputCurrencyID, newCurrencyConverted);
d3c97fd3 2739 }
20631bf1 2740
c8c684e9 2741 // if this originated as input fractional, burn the input currency
2742 // if it was reserve to reserve, it was never added, and it's fee
2743 // value is left behind in the currency
2744 if (!curTransfer.IsReserveToReserve())
d3c97fd3 2745 {
c8c684e9 2746 AddNativeOutConverted(curTransfer.FirstCurrency(), -valueOut);
d3c97fd3 2747 }
cc3d5cb5 2748 }
5fb413e6 2749
c8c684e9 2750 if (outputCurrencyID == systemDestID)
81fc0174 2751 {
c8c684e9 2752 curTransfer.GetTxOut(CCurrencyValueMap(), newCurrencyConverted, newOut);
81fc0174 2753 }
2754 else
2755 {
69d02286 2756 curTransfer.GetTxOut(CCurrencyValueMap(std::vector<uint160>({outputCurrencyID}), std::vector<int64_t>({newCurrencyConverted})),
c8c684e9 2757 0, newOut);
81fc0174 2758 }
15e4d481 2759 }
c8c684e9 2760 }
2761 else
2762 {
2763 // if we are supposed to burn a currency, it must be the import currency, and it
2764 // is removed from the supply, which would change all calculations for price
2765 if (curTransfer.IsBurn())
15e4d481 2766 {
c8c684e9 2767 // if the source is fractional currency, it is burned
2768 if (curTransfer.FirstCurrency() != importCurrencyID || !(isFractional || importCurrencyDef.IsToken()))
15e4d481 2769 {
c8c684e9 2770 CCurrencyDefinition sourceCurrency = ConnectedChains.GetCachedCurrency(curTransfer.FirstCurrency());
2771 printf("%s: Attempting to burn %s, which is either not a token or fractional currency or not the import currency %s\n", __func__, sourceCurrency.name.c_str(), importCurrencyDef.name.c_str());
2772 LogPrintf("%s: Attempting to burn %s, which is either not a token or fractional currency or not the import currency %s\n", __func__, sourceCurrency.name.c_str(), importCurrencyDef.name.c_str());
2773 return false;
15e4d481 2774 }
c8c684e9 2775 if (curTransfer.flags & curTransfer.IsBurnChangeWeight())
15e4d481 2776 {
c8c684e9 2777 printf("%s: burning %s to change weight is not supported\n", __func__, importCurrencyDef.name.c_str());
2778 LogPrintf("%s: burning %s to change weight is not supported\n", __func__, importCurrencyDef.name.c_str());
2779 return false;
15e4d481 2780 }
c8c684e9 2781 // burn the input fractional currency
2782 AddNativeOutConverted(curTransfer.FirstCurrency(), -curTransfer.FirstValue());
2783 burnedChangePrice += curTransfer.FirstValue();
15e4d481 2784 }
c8c684e9 2785 else if (systemDestID == curTransfer.destCurrencyID)
0f5f9312 2786 {
c8c684e9 2787 nativeOut += curTransfer.FirstValue();
2788 curTransfer.GetTxOut(CCurrencyValueMap(), curTransfer.FirstValue(), newOut);
4c5696b1 2789 if (newOut.nValue == -1)
2790 {
2791 printf("%s: invalid transfer %s\n", __func__, curTransfer.ToUniValue().write(1,2).c_str());
2792 LogPrintf("%s: invalid transfer %s\n", __func__, curTransfer.ToUniValue().write().c_str());
2793 return false;
2794 }
5026a487 2795 }
2796 else
2797 {
c8c684e9 2798 // if this is a minting of currency
2799 // this is used for both pre-allocation and also centrally, algorithmically, or externally controlled currencies
2800 if (curTransfer.IsMint() && curTransfer.destCurrencyID == importCurrencyID)
2801 {
2802 // minting is emitted in new currency state
2803 totalMinted += curTransfer.FirstValue();
2804 AddNativeOutConverted(curTransfer.destCurrencyID, curTransfer.FirstValue());
2805 if (curTransfer.destCurrencyID != systemDestID)
2806 {
2807 AddReserveOutConverted(curTransfer.destCurrencyID, curTransfer.FirstValue());
2808 }
2809 }
2810 AddReserveOutput(curTransfer.destCurrencyID, curTransfer.FirstValue());
2811 curTransfer.GetTxOut(CCurrencyValueMap(std::vector<uint160>({curTransfer.destCurrencyID}), std::vector<int64_t>(curTransfer.FirstValue())),
2812 0, newOut);
0f5f9312 2813 }
15e4d481 2814 }
c8c684e9 2815 if (newOut.nValue < 0)
2816 {
2817 // if we get here, we have absorbed the entire transfer
2818 LogPrintf("%s: skip creating output for import to %s\n", __func__, currencyDest.name.c_str());
2819 }
15e4d481 2820 else
2821 {
c8c684e9 2822 vOutputs.push_back(newOut);
15e4d481 2823 }
2824 }
c8c684e9 2825 else
2826 {
2827 printf("%s: Invalid reserve transfer on export\n", __func__);
2828 LogPrintf("%s: Invalid reserve transfer on export\n", __func__);
2829 return false;
2830 }
15e4d481 2831 }
a1a4dc8b 2832
d12837a1 2833 if ((totalCarveOuts = totalCarveOuts.CanonicalMap()).valueMap.size())
2834 {
2835 // add carveout outputs
2836 for (auto &oneCur : totalCarveOuts.valueMap)
2837 {
2838 // if we are creating a reserve import for native currency, it must be spent from native inputs on the destination system
2839 if (oneCur.first == systemDestID)
2840 {
2841 nativeOut += oneCur.second;
c1761ab0 2842 vOutputs.push_back(CTxOut(oneCur.second, GetScriptForDestination(CIdentityID(importCurrencyID))));
d12837a1 2843 }
2844 else
2845 {
2846 // generate a reserve output of the amount indicated, less fees
2847 // we will send using a reserve output, fee will be paid through coinbase by converting from reserve or not, depending on currency settings
2848 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CIdentityID(importCurrencyID)});
2849 CTokenOutput ro = CTokenOutput(oneCur.first, oneCur.second);
2850 AddReserveOutput(oneCur.first, oneCur.second);
c1761ab0 2851 vOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro))));
d12837a1 2852 }
2853 }
2854 }
2855
8e522b06 2856 // remove burned currency from supply
2857 if (burnedChangePrice > 0)
2858 {
2859 if (!(burnedChangePrice <= newCurrencyState.supply))
2860 {
2861 printf("%s: Invalid burn amount %ld\n", __func__, burnedChangePrice);
2862 LogPrintf("%s: Invalid burn amount %ld\n", __func__, burnedChangePrice);
2863 return false;
2864 }
2865 newCurrencyState.supply -= burnedChangePrice;
2866 }
2867
f21903bd 2868 if (isFractional &&
2869 newCurrencyState.IsLaunchConfirmed() &&
2870 (reserveConverted.CanonicalMap().valueMap.size() || fractionalConverted.CanonicalMap().valueMap.size()))
05cef42a 2871 {
e4e5ccf5 2872 CCurrencyState dummyCurState;
2873 newCurrencyState.conversionPrice =
2874 importCurrencyState.ConvertAmounts(reserveConverted.AsCurrencyVector(importCurrencyState.currencies),
2875 fractionalConverted.AsCurrencyVector(importCurrencyState.currencies),
2876 dummyCurState,
26e2ca05 2877 &crossConversions,
2878 &newCurrencyState.viaConversionPrice);
c8c684e9 2879 if (!dummyCurState.IsValid())
2880 {
2881 printf("%s: Invalid currency conversions for import to %s : %s\n", __func__, importCurrencyDef.name.c_str(), EncodeDestination(CIdentityID(importCurrencyDef.GetID())).c_str());
2882 LogPrintf("%s: Invalid currency conversions for import to %s : %s\n", __func__, importCurrencyDef.name.c_str(), EncodeDestination(CIdentityID(importCurrencyDef.GetID())).c_str());
2883 return false;
2884 }
1a36be8f 2885 }
e4e5ccf5 2886
f21903bd 2887 CCurrencyValueMap adjustedReserveConverted = reserveConverted;
2888 CCurrencyValueMap adjustedFracOutConverted = NativeOutConvertedMap();
2889
2890 if (newCurrencyState.IsLaunchConfirmed())
2891 {
2892 adjustedReserveConverted -= preConvertedReserves;
2893 }
2894
2895 std::vector<CAmount> vResConverted = adjustedReserveConverted.AsCurrencyVector(newCurrencyState.currencies);
2896 std::vector<CAmount> vResOutConverted = ReserveOutConvertedMap(importCurrencyID).AsCurrencyVector(newCurrencyState.currencies);
1a36be8f 2897 std::vector<CAmount> vFracConverted = fractionalConverted.AsCurrencyVector(newCurrencyState.currencies);
f75cf5cb 2898 std::vector<CAmount> vFracOutConverted = (NativeOutConvertedMap() - preConvertedOutput).AsCurrencyVector(newCurrencyState.currencies);
d3c97fd3 2899
1a36be8f 2900 for (int i = 0; i < newCurrencyState.currencies.size(); i++)
2901 {
2902 newCurrencyState.reserveIn[i] = vResConverted[i];
2903 newCurrencyState.reserveOut[i] = vResOutConverted[i];
4c5696b1 2904 newCurrencyState.reserves[i] += isFractional ? vResConverted[i] - vResOutConverted[i] : 0;
1a36be8f 2905 newCurrencyState.nativeIn[i] = vFracConverted[i];
888ca186 2906 newCurrencyState.supply += (vFracOutConverted[i] - vFracConverted[i]);
1a36be8f 2907 }
d3c97fd3 2908
f21903bd 2909 // launch clear or not confirmed, we have straight prices, fees get formula based conversion, but
2910 // price is not recorded in state so that initial currency always has initial prices
2911 if (isFractional && (newCurrencyState.IsLaunchClear() || !newCurrencyState.IsLaunchConfirmed()))
2912 {
2913 newCurrencyState.viaConversionPrice = newCurrencyState.conversionPrice = newCurrencyState.PricesInReserve();
2914 }
2915
888ca186 2916 newCurrencyState.supply += preAllocTotal;
f21903bd 2917
f1a298ef 2918 newCurrencyState.preConvertedOut = 0;
2919 for (auto &oneVal : preConvertedOutput.valueMap)
2920 {
2921 newCurrencyState.preConvertedOut += oneVal.second;
2922 }
888ca186 2923
1a36be8f 2924 if (totalMinted)
2925 {
2926 newCurrencyState.UpdateWithEmission(totalMinted);
2927 }
2928
2929 // now, pull out all fractional data and sort out native vs. fractional
2930 if (currencies.count(systemDestID))
2931 {
2932 CReserveInOuts fractionalInOuts = currencies[systemDestID];
2933 newCurrencyState.nativeConversionFees = fractionalInOuts.reserveConversionFees;
05cef42a 2934 }
1a36be8f 2935 newCurrencyState.conversionFees = ReserveConversionFeesMap().AsCurrencyVector(newCurrencyState.currencies);
a7f05ef6 2936 newCurrencyState.fees = transferFees.AsCurrencyVector(newCurrencyState.currencies);
05cef42a 2937
951413c7 2938 // double check that the export fee taken as the fee output matches the export fee that should have been taken
56fe75cb 2939 CCurrencyValueMap ReserveInputs;
c8c684e9 2940 spentCurrencyOut.valueMap.clear();
a7f05ef6 2941 CAmount systemOutConverted = 0;
c8c684e9 2942
f21903bd 2943 //printf("%s currencies: %s\n", __func__, ToUniValue().write(1,2).c_str());
2944
56fe75cb 2945 for (auto &oneInOut : currencies)
2946 {
481e7fd6 2947 if (oneInOut.first == importCurrencyID)
a7f05ef6 2948 {
c8c684e9 2949 newCurrencyState.nativeOut = oneInOut.second.nativeOutConverted;
2950
481e7fd6 2951 if (oneInOut.first == systemDestID)
a7f05ef6 2952 {
2953 systemOutConverted += oneInOut.second.nativeOutConverted;
2954 }
481e7fd6 2955 }
2956 else
2957 {
2958 ReserveInputs.valueMap[importCurrencyID] += oneInOut.second.nativeOutConverted;
2959 if (oneInOut.first == systemDestID)
a7f05ef6 2960 {
2961 systemOutConverted += oneInOut.second.reserveOutConverted;
2962 }
481e7fd6 2963 if (oneInOut.second.reserveIn || oneInOut.second.reserveOutConverted)
2964 {
2965 ReserveInputs.valueMap[oneInOut.first] = oneInOut.second.reserveIn + oneInOut.second.reserveOutConverted;
2966 }
2967 if (oneInOut.second.reserveOut)
2968 {
c8c684e9 2969 spentCurrencyOut.valueMap[oneInOut.first] = oneInOut.second.reserveOut;
481e7fd6 2970 }
a7f05ef6 2971 }
481e7fd6 2972 }
2973 if (systemOutConverted)
2974 {
2975 // this does not have meaning besides a store of the system currency output that was converted
2976 currencies[importCurrencyID].reserveOutConverted = systemOutConverted;
56fe75cb 2977 }
a7f05ef6 2978 if (nativeIn || systemOutConverted)
da4c6118 2979 {
a7f05ef6 2980 ReserveInputs.valueMap[importCurrencyDef.systemID] = nativeIn + systemOutConverted;
da4c6118 2981 }
2982 if (nativeOut)
2983 {
c8c684e9 2984 spentCurrencyOut.valueMap[importCurrencyDef.systemID] = nativeOut;
da4c6118 2985 }
89fe4cca 2986
c8c684e9 2987 //printf("ReserveInputs: %s\nReserveOutputs: %s\nReserveInputs - spentCurrencyOut: %s\n", ReserveInputs.ToUniValue().write(1,2).c_str(), spentCurrencyOut.ToUniValue().write(1,2).c_str(), (ReserveInputs - spentCurrencyOut).ToUniValue().write(1,2).c_str());
2988 if ((ReserveInputs - spentCurrencyOut).HasNegative())
951413c7 2989 {
d563b0d2 2990 printf("%s: Too much fee taken by export, ReserveInputs: %s\nReserveOutputs: %s\n", __func__,
82a94c84 2991 ReserveInputs.ToUniValue().write(1,2).c_str(),
c8c684e9 2992 spentCurrencyOut.ToUniValue().write(1,2).c_str());
56fe75cb 2993 LogPrintf("%s: Too much fee taken by export\n", __func__);
951413c7 2994 return false;
2995 }
15e4d481 2996 return true;
2997}
2998
c8c677c9 2999CMutableTransaction &CReserveTransactionDescriptor::AddConversionInOuts(CMutableTransaction &conversionTx, std::vector<CInputDescriptor> &conversionInputs, const CCurrencyValueMap &_exchangeRates, const CCurrencyState *pCurrencyState) const
41f170fd
MT
3000{
3001 if (!IsReserveExchange() || IsFillOrKillFail())
3002 {
3003 return conversionTx;
3004 }
88bc6df5 3005
56fe75cb 3006 bool noExchangeRate = false;
c8c677c9 3007 CCurrencyState dummy;
3008 const CCurrencyState &currencyState = pCurrencyState ? *pCurrencyState : dummy;
56fe75cb 3009
3010 // set exchange rates as well as we can, either from explicit rates or currency state if possible
3011 CCurrencyValueMap __exchangeRates;
3012 const CCurrencyValueMap *pExchangeRates = &__exchangeRates;
3013 if (_exchangeRates.valueMap.size() != 0)
88bc6df5 3014 {
56fe75cb 3015 pExchangeRates = &_exchangeRates;
3016 }
3017 else
3018 {
4005d836 3019 if (pCurrencyState && currencyState.IsFractional())
88bc6df5 3020 {
56fe75cb 3021 __exchangeRates = CCurrencyValueMap(currencyState.currencies, currencyState.PricesInReserve());
88bc6df5
MT
3022 }
3023 else
3024 {
56fe75cb 3025 noExchangeRate = true;
88bc6df5
MT
3026 }
3027 }
56fe75cb 3028 const CCurrencyValueMap &exchangeRates = *pExchangeRates;
88bc6df5 3029
56fe75cb 3030 CAmount nativeFeesLeft = nativeConversionFees;
3031 for (auto &oneEntry : exchangeRates.valueMap)
3032 {
3033 auto it = currencies.find(oneEntry.first);
3034 if (it == currencies.end())
3035 {
3036 LogPrintf("%s: invalid conversion with no exchange rate, currency: %s\n", __func__, EncodeDestination(CIdentityID(oneEntry.first)).c_str());
3037 }
3038 else
3039 {
3040 nativeFeesLeft += currencyState.ReserveToNativeRaw(it->second.reserveConversionFees, oneEntry.second);
3041 }
3042 }
88bc6df5 3043
34d1aa13
MT
3044 uint256 txHash = ptx->GetHash();
3045
41f170fd
MT
3046 for (auto &indexRex : vRex)
3047 {
3048 COptCCParams p;
3049 ptx->vout[indexRex.first].scriptPubKey.IsPayToCryptoCondition(p);
3050
41f170fd
MT
3051 CCcontract_info CC;
3052 CCcontract_info *cp;
88bc6df5 3053
c8c684e9 3054 CAmount fee = CalculateConversionFee(indexRex.second.FirstValue());
3055 CAmount amount = indexRex.second.FirstValue() - fee;
56fe75cb 3056 CAmount nativeFee, nativeAmount;
3057
c8c684e9 3058 auto rateIt = exchangeRates.valueMap.find(indexRex.second.FirstCurrency());
56fe75cb 3059 if (rateIt == exchangeRates.valueMap.end())
88bc6df5 3060 {
56fe75cb 3061 continue;
88bc6df5 3062 }
88bc6df5 3063
56fe75cb 3064 CAmount exchangeRate = rateIt->second;
3065
3066 // if already native fees, don't convert, otherwise, do
3067 if (!(indexRex.second.flags & indexRex.second.TO_RESERVE))
3068 {
3069 nativeFee = fee;
3070 nativeAmount = ptx->vout[indexRex.first].nValue;
3071 amount = currencyState.NativeToReserveRaw(nativeAmount, exchangeRate);
3072 fee = currencyState.NativeToReserveRaw(nativeFee, exchangeRate);
3073 }
3074 else
3075 {
3076 nativeFee = currencyState.ReserveToNativeRaw(fee, exchangeRate);
3077 nativeAmount = currencyState.ReserveToNativeRaw(amount, exchangeRate);
3078 }
3079
3080 if (nativeFee > nativeFeesLeft)
3081 {
3082 nativeFee = nativeFeesLeft;
3083 }
3084 nativeFeesLeft -= nativeFee;
41f170fd 3085
0574c740 3086 // add input...
34d1aa13
MT
3087 conversionTx.vin.push_back(CTxIn(txHash, indexRex.first, CScript()));
3088
0574c740 3089 // ... and input descriptor. we leave the CTxIn empty and use the one in the corresponding input, using the input descriptor for only
3090 // script and value
3091 conversionInputs.push_back(CInputDescriptor(ptx->vout[indexRex.first].scriptPubKey, ptx->vout[indexRex.first].nValue, CTxIn()));
3092
56fe75cb 3093 // if we should emit a reserve transfer or normal reserve output, sending output only occurs when converting from
3094 // native to a reserve currency
3095 if (indexRex.second.flags & indexRex.second.SEND_OUTPUT &&
3096 indexRex.second.flags & indexRex.second.TO_RESERVE &&
3097 nativeAmount > (CReserveTransfer::DEFAULT_PER_STEP_FEE << 1) << 1)
e7c700b5 3098 {
41f170fd 3099 cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
41f170fd
MT
3100 CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
3101
56fe75cb 3102 // send the entire amount to a reserve transfer output of the controller
c8c684e9 3103 CCurrencyDefinition curDef = ConnectedChains.GetCachedCurrency(indexRex.second.FirstCurrency());
3104 if (!curDef.IsValid())
56fe75cb 3105 {
3106 std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), p.vKeys[0]});
3107
3108 // create the transfer output with the converted amount less fees
3109 CReserveTransfer rt((uint32_t)CReserveTransfer::VALID,
c8c684e9 3110 indexRex.second.FirstCurrency(),
56fe75cb 3111 amount - (CReserveTransfer::DEFAULT_PER_STEP_FEE << 1),
c8c684e9 3112 curDef.systemID,
56fe75cb 3113 CReserveTransfer::DEFAULT_PER_STEP_FEE << 1,
c8c684e9 3114 curDef.GetID(),
56fe75cb 3115 CTransferDestination(p.vKeys[0].which(), GetDestinationBytes(p.vKeys[0])));
3116
3117 // cast object to the most derived class to avoid template errors to a least derived class
4a53088e 3118 conversionTx.vout.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt))));
56fe75cb 3119 }
e7c700b5 3120 }
c3250dcd 3121 else if (indexRex.second.flags & indexRex.second.TO_RESERVE)
41f170fd 3122 {
4463b298 3123 // send the net amount to the indicated destination, which is the first entry in the destinations of the original reserve/exchange by protocol
c3250dcd 3124 std::vector<CTxDestination> dests = std::vector<CTxDestination>({p.vKeys[0]});
41f170fd 3125
c3250dcd 3126 // create the output with the unconverted amount less fees
c8c684e9 3127 CTokenOutput ro(indexRex.second.FirstCurrency(), amount);
e7c700b5 3128
3a27113e 3129 conversionTx.vout.push_back(MakeCC1ofAnyVout(EVAL_RESERVE_OUTPUT, 0, dests, ro));
c3250dcd
MT
3130 }
3131 else
3132 {
3133 // convert amount to native from reserve and send as normal output
c8c684e9 3134 amount = currencyState.ReserveToNative(amount, exchangeRates.valueMap.find(indexRex.second.FirstCurrency())->second);
c3250dcd 3135 conversionTx.vout.push_back(CTxOut(amount, GetScriptForDestination(p.vKeys[0])));
41f170fd
MT
3136 }
3137 }
3138 return conversionTx;
e7c700b5 3139}
a6e612cc 3140
481e7fd6 3141CCurrencyValueMap CReserveTransactionDescriptor::ReserveInputMap(const uint160 &nativeID) const
56fe75cb 3142{
3143 CCurrencyValueMap retVal;
481e7fd6 3144 uint160 id = nativeID.IsNull() ? ASSETCHAINS_CHAINID : nativeID;
56fe75cb 3145 for (auto &oneInOut : currencies)
3146 {
481e7fd6 3147 // skip native
3148 if (oneInOut.first != id)
56fe75cb 3149 {
481e7fd6 3150 if (oneInOut.second.reserveIn)
3151 {
3152 retVal.valueMap[oneInOut.first] = oneInOut.second.reserveIn;
3153 }
56fe75cb 3154 }
4c5696b1 3155 if (oneInOut.second.nativeOutConverted)
3156 {
3157 retVal.valueMap[oneInOut.first] = oneInOut.second.nativeOutConverted;
3158 }
56fe75cb 3159 }
3160 return retVal;
3161}
3162
481e7fd6 3163CCurrencyValueMap CReserveTransactionDescriptor::ReserveOutputMap(const uint160 &nativeID) const
56fe75cb 3164{
3165 CCurrencyValueMap retVal;
481e7fd6 3166 uint160 id = nativeID.IsNull() ? ASSETCHAINS_CHAINID : nativeID;
56fe75cb 3167 for (auto &oneInOut : currencies)
3168 {
481e7fd6 3169 // skip native
3170 if (oneInOut.first != id)
56fe75cb 3171 {
481e7fd6 3172 if (oneInOut.second.reserveOut)
3173 {
3174 retVal.valueMap[oneInOut.first] = oneInOut.second.reserveOut;
3175 }
56fe75cb 3176 }
3177 }
3178 return retVal;
3179}
3180
481e7fd6 3181CCurrencyValueMap CReserveTransactionDescriptor::ReserveOutConvertedMap(const uint160 &nativeID) const
56fe75cb 3182{
3183 CCurrencyValueMap retVal;
481e7fd6 3184 uint160 id = nativeID.IsNull() ? ASSETCHAINS_CHAINID : nativeID;
56fe75cb 3185 for (auto &oneInOut : currencies)
3186 {
481e7fd6 3187 // skip native
3188 if (oneInOut.first != id)
56fe75cb 3189 {
481e7fd6 3190 if (oneInOut.second.reserveOutConverted)
3191 {
3192 retVal.valueMap[oneInOut.first] = oneInOut.second.reserveOutConverted;
3193 }
56fe75cb 3194 }
3195 }
3196 return retVal;
3197}
3198
3199CCurrencyValueMap CReserveTransactionDescriptor::NativeOutConvertedMap() const
3200{
3201 CCurrencyValueMap retVal;
3202 for (auto &oneInOut : currencies)
3203 {
3204 if (oneInOut.second.nativeOutConverted)
3205 {
3206 retVal.valueMap[oneInOut.first] = oneInOut.second.nativeOutConverted;
3207 }
3208 }
3209 return retVal;
3210}
3211
3212CCurrencyValueMap CReserveTransactionDescriptor::ReserveConversionFeesMap() const
3213{
3214 CCurrencyValueMap retVal;
3215 for (auto &oneInOut : currencies)
3216 {
3217 if (oneInOut.second.reserveConversionFees)
3218 {
3219 retVal.valueMap[oneInOut.first] = oneInOut.second.reserveConversionFees;
3220 }
3221 }
3222 return retVal;
3223}
3224
c8c677c9 3225std::vector<CAmount> CReserveTransactionDescriptor::ReserveInputVec(const CCurrencyState &cState) const
56fe75cb 3226{
3227 std::vector<CAmount> retVal(cState.currencies.size());
3228 std::map<uint160, int> curMap = cState.GetReserveMap();
3229 for (auto &oneInOut : currencies)
3230 {
3231 retVal[curMap[oneInOut.first]] = oneInOut.second.reserveIn;
3232 }
3233 return retVal;
3234}
3235
c8c677c9 3236std::vector<CAmount> CReserveTransactionDescriptor::ReserveOutputVec(const CCurrencyState &cState) const
56fe75cb 3237{
3238 std::vector<CAmount> retVal(cState.currencies.size());
3239 std::map<uint160, int> curMap = cState.GetReserveMap();
3240 for (auto &oneInOut : currencies)
3241 {
3242 retVal[curMap[oneInOut.first]] = oneInOut.second.reserveOut;
3243 }
3244 return retVal;
3245}
3246
c8c677c9 3247std::vector<CAmount> CReserveTransactionDescriptor::ReserveOutConvertedVec(const CCurrencyState &cState) const
56fe75cb 3248{
3249 std::vector<CAmount> retVal(cState.currencies.size());
3250 std::map<uint160, int> curMap = cState.GetReserveMap();
3251 for (auto &oneInOut : currencies)
3252 {
3253 retVal[curMap[oneInOut.first]] = oneInOut.second.reserveOutConverted;
3254 }
3255 return retVal;
3256}
3257
c8c677c9 3258std::vector<CAmount> CReserveTransactionDescriptor::NativeOutConvertedVec(const CCurrencyState &cState) const
56fe75cb 3259{
3260 std::vector<CAmount> retVal(cState.currencies.size());
3261 std::map<uint160, int> curMap = cState.GetReserveMap();
3262 for (auto &oneInOut : currencies)
3263 {
3264 retVal[curMap[oneInOut.first]] = oneInOut.second.nativeOutConverted;
3265 }
3266 return retVal;
3267}
3268
c8c677c9 3269std::vector<CAmount> CReserveTransactionDescriptor::ReserveConversionFeesVec(const CCurrencyState &cState) const
56fe75cb 3270{
3271 std::vector<CAmount> retVal(cState.currencies.size());
3272 std::map<uint160, int> curMap = cState.GetReserveMap();
3273 for (auto &oneInOut : currencies)
3274 {
3275 retVal[curMap[oneInOut.first]] = oneInOut.second.reserveConversionFees;
3276 }
3277 return retVal;
3278}
3279
3280// this should be done no more than once to prepare a currency state to be updated to the next state
2f546002 3281// emission occurs for a block before any conversion or exchange and that impact on the currency state is calculated
68ddda5e 3282CCurrencyState &CCurrencyState::UpdateWithEmission(CAmount toEmit)
2f546002 3283{
56fe75cb 3284 initialSupply = supply;
3285 emitted = 0;
2f546002 3286
3287 // if supply is 0, reserve must be zero, and we cannot function as a reserve currency
cc3d5cb5 3288 if (!IsFractional() || supply <= 0 || CCurrencyValueMap(currencies, reserves) <= CCurrencyValueMap())
2f546002 3289 {
56fe75cb 3290 if (supply < 0)
2f546002 3291 {
68ddda5e 3292 emitted = supply = toEmit;
2f546002 3293 }
3294 else
3295 {
68ddda5e 3296 emitted = toEmit;
3297 supply += toEmit;
2f546002 3298 }
3299 return *this;
3300 }
3301
68ddda5e 3302 if (toEmit)
f7f56006 3303 {
56fe75cb 3304 // first determine current ratio by adding up all currency weights
3305 CAmount InitialRatio = 0;
3306 for (auto weight : weights)
3307 {
3308 InitialRatio += weight;
3309 }
3310
f7f56006 3311 // to balance rounding with truncation, we statistically add a satoshi to the initial ratio
56fe75cb 3312 static arith_uint256 bigSatoshi(SATOSHIDEN);
f7f56006 3313 arith_uint256 bigInitial(InitialRatio);
68ddda5e 3314 arith_uint256 bigEmission(toEmit);
56fe75cb 3315 arith_uint256 bigSupply(supply);
f7f56006 3316
3317 arith_uint256 bigScratch = (bigInitial * bigSupply * bigSatoshi) / (bigSupply + bigEmission);
3318 arith_uint256 bigRatio = bigScratch / bigSatoshi;
3319 // cap ratio at 1
3320 if (bigRatio >= bigSatoshi)
3321 {
56fe75cb 3322 bigScratch = arith_uint256(SATOSHIDEN) * arith_uint256(SATOSHIDEN);
f7f56006 3323 bigRatio = bigSatoshi;
3324 }
2f546002 3325
f7f56006 3326 int64_t newRatio = bigRatio.GetLow64();
56fe75cb 3327 int64_t remainder = (bigScratch - (bigRatio * SATOSHIDEN)).GetLow64();
f7f56006 3328 // form of bankers rounding, if odd, round up at half, if even, round down at half
56fe75cb 3329 if (remainder > (SATOSHIDEN >> 1) || (remainder == (SATOSHIDEN >> 1) && newRatio & 1))
f7f56006 3330 {
3331 newRatio += 1;
3332 }
2f546002 3333
56fe75cb 3334 // now, we must update all weights accordingly, based on the new, total ratio, by dividing the total among all the
3335 // weights, according to their current relative weight. because this also can be a source of rounding error, we will
3336 // distribute any modulus excess randomly among the currencies
3337 std::vector<CAmount> extraWeight(currencies.size());
3338 arith_uint256 bigRatioDelta(InitialRatio - newRatio);
3339 CAmount totalUpdates = 0;
3340
3341 for (auto &weight : weights)
3342 {
3343 CAmount weightDelta = (bigRatioDelta * arith_uint256(weight) / bigSatoshi).GetLow64();
3344 weight -= weightDelta;
3345 totalUpdates += weightDelta;
3346 }
3347
3348 CAmount updateExtra = (InitialRatio - newRatio) - totalUpdates;
3349
3350 // if we have any extra, distribute it evenly and any mod, both deterministically and pseudorandomly
3351 if (updateExtra)
3352 {
3353 CAmount forAll = updateExtra / currencies.size();
3354 CAmount forSome = updateExtra % currencies.size();
3355
3356 // get deterministic seed for linear congruential pseudorandom number for shuffle
3357 int64_t seed = supply + forAll + forSome;
3358 auto prandom = std::minstd_rand0(seed);
3359
3360 for (int i = 0; i < extraWeight.size(); i++)
3361 {
3362 extraWeight[i] = forAll;
3363 if (forSome)
3364 {
3365 extraWeight[i]++;
3366 forSome--;
3367 }
3368 }
3369 // distribute the extra as evenly as possible
3370 std::shuffle(extraWeight.begin(), extraWeight.end(), prandom);
3371 for (int i = 0; i < weights.size(); i++)
3372 {
3373 weights[i] -= extraWeight[i];
3374 }
3375 }
3376
3377 // update initial supply from what we currently have
68ddda5e 3378 emitted = toEmit;
56fe75cb 3379 supply = initialSupply + emitted;
f7f56006 3380 }
2f546002 3381 return *this;
3382}
3383
f21903bd 3384void CCoinbaseCurrencyState::RevertReservesAndSupply()
3385{
3386 // if this is the launch clear notarization, we have only the starting condition
3387 // and we don't revert anything
3388 bool launchClear = IsLaunchClear();
3389 if (!launchClear)
3390 {
3391 // add reserves out to reserves
3392 auto currencyMap = GetReserveMap();
3393
3394 // revert changes in reserves and supply to pre conversion state, add reserve outs and subtract reserve ins
3395 for (auto &oneCur : currencyMap)
3396 {
3397 reserves[oneCur.second] += (reserveOut[oneCur.second] - reserveIn[oneCur.second]);
3398 supply += nativeIn[oneCur.second];
3399 }
3400 }
3401
3402 supply -= ((nativeOut + emitted) - preConvertedOut);
3403 ClearForNextBlock();
3404}
3405
c8c677c9 3406CAmount CCurrencyState::CalculateConversionFee(CAmount inputAmount, bool convertToNative, int currencyIndex) const
e7e14f44
MT
3407{
3408 arith_uint256 bigAmount(inputAmount);
56fe75cb 3409 arith_uint256 bigSatoshi(SATOSHIDEN);
e7e14f44
MT
3410
3411 // we need to calculate a fee based either on the amount to convert or the last price
3412 // times the reserve
3413 if (convertToNative)
3414 {
3415 int64_t price;
d07e4ab8 3416 cpp_dec_float_50 priceInReserve = PriceInReserveDecFloat50(currencyIndex);
e7e14f44
MT
3417 if (!to_int64(priceInReserve, price))
3418 {
3419 assert(false);
3420 }
3421 bigAmount = price ? (bigAmount * bigSatoshi) / arith_uint256(price) : 0;
3422 }
3423
3424 CAmount fee = 0;
3425 fee = ((bigAmount * arith_uint256(CReserveExchange::SUCCESS_FEE)) / bigSatoshi).GetLow64();
3426 if (fee < CReserveExchange::MIN_SUCCESS_FEE)
3427 {
3428 fee = CReserveExchange::MIN_SUCCESS_FEE;
3429 }
41f170fd
MT
3430 return fee;
3431}
3432
e4e5ccf5 3433CAmount CReserveTransactionDescriptor::CalculateConversionFeeNoMin(CAmount inputAmount)
41f170fd
MT
3434{
3435 arith_uint256 bigAmount(inputAmount);
56fe75cb 3436 arith_uint256 bigSatoshi(SATOSHIDEN);
e4e5ccf5 3437 return ((bigAmount * arith_uint256(CReserveExchange::SUCCESS_FEE)) / bigSatoshi).GetLow64();
3438}
41f170fd 3439
e4e5ccf5 3440CAmount CReserveTransactionDescriptor::CalculateConversionFee(CAmount inputAmount)
3441{
3442 CAmount fee = CalculateConversionFeeNoMin(inputAmount);
41f170fd
MT
3443 if (fee < CReserveExchange::MIN_SUCCESS_FEE)
3444 {
3445 fee = CReserveExchange::MIN_SUCCESS_FEE;
3446 }
e7e14f44
MT
3447 return fee;
3448}
3449
715182a4 3450// this calculates a fee that will be added to an amount and result in the same percentage as above,
3451// such that a total of the inputAmount + this returned fee, if passed to CalculateConversionFee, would return
3452// the same amount
3453CAmount CReserveTransactionDescriptor::CalculateAdditionalConversionFee(CAmount inputAmount)
3454{
3455 arith_uint256 bigAmount(inputAmount);
56fe75cb 3456 arith_uint256 bigSatoshi(SATOSHIDEN);
715182a4 3457 arith_uint256 conversionFee(CReserveExchange::SUCCESS_FEE);
3458
3459 CAmount newAmount = ((bigAmount * bigSatoshi) / (bigSatoshi - conversionFee)).GetLow64();
9677f1a2 3460 if (newAmount - inputAmount < CReserveExchange::MIN_SUCCESS_FEE)
3461 {
3462 newAmount = inputAmount + CReserveExchange::MIN_SUCCESS_FEE;
3463 }
715182a4 3464 CAmount fee = CalculateConversionFee(newAmount);
90fb9349 3465 newAmount = inputAmount + fee;
2cad1340 3466 fee = CalculateConversionFee(newAmount); // again to account for minimum fee
90fb9349 3467 fee += inputAmount - (newAmount - fee); // add any additional difference
715182a4 3468 return fee;
3469}
3470
c8c684e9 3471bool CFeePool::GetCoinbaseFeePool(CFeePool &feePool, uint32_t height)
855714b0 3472{
3473 CBlock block;
3474 CTransaction coinbaseTx;
3475 feePool.SetInvalid();
3476 if (!height || chainActive.Height() < height)
3477 {
3478 height = chainActive.Height();
3479 }
3480 if (!height)
3481 {
3482 return true;
3483 }
3484 if (ReadBlockFromDisk(block, chainActive[height], Params().GetConsensus()))
3485 {
3486 coinbaseTx = block.vtx[0];
3487 }
3488 else
3489 {
3490 return false;
3491 }
3492
3493 for (auto &txOut : coinbaseTx.vout)
3494 {
3495 COptCCParams p;
3496 if (txOut.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_FEE_POOL && p.vData.size())
3497 {
3498 feePool = CFeePool(p.vData[0]);
3499 }
3500 }
3501 return true;
3502}
3503
c8c684e9 3504CFeePool::CFeePool(const CTransaction &coinbaseTx)
855714b0 3505{
c8c684e9 3506 nVersion = VERSION_INVALID;
855714b0 3507 if (coinbaseTx.IsCoinBase())
3508 {
3509 for (auto &txOut : coinbaseTx.vout)
3510 {
3511 COptCCParams p;
3512 if (txOut.scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid() && p.evalCode == EVAL_FEE_POOL && p.vData.size())
3513 {
c8c684e9 3514 ::FromVector(p.vData[0], *this);
855714b0 3515 }
3516 }
3517 }
855714b0 3518}
1e561fd3 3519
3520bool ValidateFeePool(struct CCcontract_info *cp, Eval* eval, const CTransaction &tx, uint32_t nIn, bool fulfilled)
3521{
855714b0 3522 // fee pool output is unspendable
1e561fd3 3523 return false;
3524}
3525
3526bool IsFeePoolInput(const CScript &scriptSig)
3527{
3528 return false;
3529}
3530
3531bool PrecheckFeePool(const CTransaction &tx, int32_t outNum, CValidationState &state, uint32_t height)
3532{
3533 return true;
3534}
3535
This page took 0.726251 seconds and 4 git commands to generate.