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