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