]> Git Repo - VerusCoin.git/blob - src/pbaas/reserves.cpp
testnet breaking changes - Implement index address, hardening, makefile fixes
[VerusCoin.git] / src / pbaas / reserves.cpp
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"
12 #include "pbaas/pbaas.h"
13 #include "pbaas/reserves.h"
14 #include "pbaas/notarization.h"
15 #include "rpc/server.h"
16 #include "key_io.h"
17 #include <random>
18
19 CTokenOutput::CTokenOutput(const UniValue &obj)
20 {
21     nVersion = (uint32_t)uni_get_int(find_value(obj, "version"), VERSION_CURRENT);
22     currencyID = GetDestinationID(DecodeDestination(uni_get_str(find_value(obj, "currencyid"))));
23
24     try
25     {
26         nValue = AmountFromValue(find_value(obj, "value"));
27     }
28     catch(const std::exception& e)
29     {
30         std::cerr << e.what() << '\n';
31         nVersion = VERSION_INVALID;
32     }
33 }
34
35 CAmount CReserveTransfer::CalculateTransferFee(const CTransferDestination &destination)
36 {
37     return CReserveTransfer::DEFAULT_PER_STEP_FEE << 1 +
38                 ((CReserveTransfer::DEFAULT_PER_STEP_FEE << 1) * (destination.destination.size() / DESTINATION_BYTE_DIVISOR));
39 }
40
41 CCurrencyValueMap CReserveTransfer::CalculateFee(uint32_t flags, CAmount transferTotal) const
42 {
43     CCurrencyValueMap feeMap;
44
45     // determine fee for this send
46     if (flags & FEE_OUTPUT)
47     {
48         return feeMap;
49     }
50
51     feeMap.valueMap[currencyID] = CalculateTransferFee(destination);
52
53     // add conversion fees in source currency for preconvert
54     if (flags & (CReserveTransfer::PRECONVERT | CReserveTransfer::CONVERT))
55     {
56         feeMap.valueMap[currencyID] += CReserveTransactionDescriptor::CalculateConversionFee(transferTotal);
57     }
58
59     return feeMap;
60 }
61
62 CAmount CReserveTransfer::CalculateTransferFee() const
63 {
64     // determine fee for this send
65     if (flags & FEE_OUTPUT)
66     {
67         return 0;
68     }
69     return CalculateTransferFee(destination);
70 }
71
72 CReserveExchange::CReserveExchange(const UniValue &uni) : CTokenOutput(uni)
73 {
74     nVersion = (uint32_t)uni_get_int(find_value(uni, "version"), VERSION_CURRENT);
75     currencyID = GetDestinationID(DecodeDestination(uni_get_str(find_value(uni, "currencyid"))));
76     nValue = AmountFromValue(find_value(uni, "value"));
77
78     if (uni_get_bool(find_value(uni, "toreserve")))
79     {
80         flags |= TO_RESERVE;
81     }
82     if (uni_get_bool(find_value(uni, "limitorder")))
83     {
84         flags |= LIMIT;
85     }
86     if (uni_get_bool(find_value(uni, "fillorkill")))
87     {
88         flags |= FILL_OR_KILL;
89     }
90     if (uni_get_bool(find_value(uni, "sendoutput")))
91     {
92         flags |= SEND_OUTPUT;
93     }
94
95     try
96     {
97         nLimit = AmountFromValue(find_value(uni, "limitprice"));
98         nValidBefore = uni_get_int(find_value(uni, "validbeforeblock"));
99     }
100     catch(const std::exception& e)
101     {
102         std::cerr << e.what() << '\n';
103         nVersion = VERSION_INVALID;
104     }
105 }
106
107 CReserveExchange::CReserveExchange(const CTransaction &tx)
108 {
109     bool orderFound = false;
110     for (auto out : tx.vout)
111     {
112         COptCCParams p;
113         if (IsPayToCryptoCondition(out.scriptPubKey, p))
114         {
115             if (p.evalCode == EVAL_RESERVE_EXCHANGE)
116             {
117                 if (orderFound)
118                 {
119                     nVersion = VERSION_INVALID;
120                 }
121                 else
122                 {
123                     FromVector(p.vData[0], *this);
124                     orderFound = true;
125                 }
126             }
127         }
128     }
129 }
130
131 CCrossChainImport::CCrossChainImport(const CTransaction &tx, int32_t *pOutNum)
132 {
133     for (int i = 0; i < tx.vout.size(); i++)
134     {
135         COptCCParams p;
136         if (IsPayToCryptoCondition(tx.vout[i].scriptPubKey, p) && p.IsValid())
137         {
138             // always take the first for now
139             if (p.evalCode == EVAL_CROSSCHAIN_IMPORT && p.vData.size())
140             {
141                 FromVector(p.vData[0], *this);
142                 if (pOutNum)
143                 {
144                     *pOutNum = i;
145                 }
146                 break;
147             }
148         }
149     }
150 }
151
152 CCurrencyState::CCurrencyState(const UniValue &obj)
153 {
154     flags = uni_get_int(find_value(obj, "flags"));
155
156     std::string cIDStr = uni_get_str(find_value(obj, "currencyid"));
157     if (cIDStr != "")
158     {
159         CTxDestination currencyDest = DecodeDestination(cIDStr);
160         currencyID = GetDestinationID(currencyDest);
161     }
162
163     if (flags & FLAG_FRACTIONAL)
164     {
165         auto CurrenciesArr = find_value(obj, "reservecurrencies");
166         size_t numCurrencies;
167         if (!CurrenciesArr.isArray() ||
168             !(numCurrencies = CurrenciesArr.size()) ||
169             numCurrencies > MAX_RESERVE_CURRENCIES)
170         {
171             flags &= ~FLAG_VALID;
172             LogPrintf("Failed to proplerly specify currencies in reserve currency definition\n");
173         }
174         else
175         {
176             // store currencies, weights, and reserves
177             try
178             {
179                 for (int i = 0; i < CurrenciesArr.size(); i++)
180                 {
181                     uint160 currencyID = GetDestinationID(DecodeDestination(uni_get_str(find_value(CurrenciesArr[i], "currencyid"))));
182                     if (currencyID.IsNull())
183                     {
184                         LogPrintf("Invalid currency ID\n");
185                         flags &= ~FLAG_VALID;
186                         break;
187                     }
188                     currencies[i] = currencyID;
189                     weights[i] = AmountFromValue(find_value(CurrenciesArr[i], "weight"));
190                     reserves[i] = AmountFromValue(find_value(CurrenciesArr[i], "reserves"));
191                 }
192             }
193             catch(const std::exception& e)
194             {
195                 std::cerr << e.what() << '\n';
196                 flags &= ~FLAG_VALID;
197                 LogPrintf("Invalid specification of currencies, weights, and/or reserves in initial definition of reserve currency\n");
198             }
199         }
200     }
201
202     if (!(flags & FLAG_VALID))
203     {
204         printf("Invalid currency specification, see debug.log for reason other than invalid flags\n");
205         LogPrintf("Invalid currency specification\n");
206     }
207     else
208     {
209         try
210         {
211             initialSupply = AmountFromValue(find_value(obj, "initialsupply"));
212             emitted = AmountFromValue(find_value(obj, "emitted"));
213             supply = AmountFromValue(find_value(obj, "supply"));
214         }
215         catch(const std::exception& e)
216         {
217             std::cerr << e.what() << '\n';
218             flags &= ~FLAG_VALID;
219         }
220     }
221 }
222
223 CCoinbaseCurrencyState::CCoinbaseCurrencyState(const CTransaction &tx, int *pOutIdx)
224 {
225     int localIdx;
226     int &i = pOutIdx ? *pOutIdx : localIdx;
227     for (i = 0; i < tx.vout.size(); i++)
228     {
229         COptCCParams p;
230         if (IsPayToCryptoCondition(tx.vout[i].scriptPubKey, p))
231         {
232             if (p.evalCode == EVAL_CURRENCYSTATE && p.vData.size())
233             {
234                 FromVector(p.vData[0], *this);
235                 break;
236             }
237         }
238     }
239 }
240
241 std::vector<std::vector<CAmount>> ValueColumnsFromUniValue(const UniValue &uni,
242                                                            const std::vector<std::string> &rowNames,
243                                                            const std::vector<std::string> &columnNames)
244 {
245     std::vector<std::vector<CAmount>> retVal;
246     for (int i = 0; i < rowNames.size(); i++)
247     {
248         UniValue row = find_value(uni, rowNames[i]);
249         if (row.isObject())
250         {
251             for (int j = 0; j < columnNames.size(); j++)
252             {
253                 if (retVal.size() == j)
254                 {
255                     retVal.emplace_back();
256                 }
257                 CAmount columnVal = 0;
258                 try
259                 {
260                     columnVal = AmountFromValue(find_value(row, columnNames[j]));
261                 }
262                 catch(const std::exception& e)
263                 {
264                     std::cerr << e.what() << '\n';
265                 }
266                 retVal[j].push_back(columnVal);
267             }
268         }
269     }
270     return retVal;
271 }
272
273
274 CCoinbaseCurrencyState::CCoinbaseCurrencyState(const UniValue &obj) : CCurrencyState(obj)
275 {
276     std::vector<std::vector<CAmount>> columnAmounts;
277
278     auto currenciesValue = find_value(obj, "currencies");
279     std::vector<std::string> rowNames;
280     for (int i = 0; i < currencies.size(); i++)
281     {
282         rowNames.push_back(EncodeDestination(CIdentityID(currencies[i])));
283     }
284     std::vector<std::string> columnNames({"reservein", "nativein", "reserveout", "lastconversionprice", "fees", "conversionfees"});
285     if (currenciesValue.isObject())
286     {
287         columnAmounts = ValueColumnsFromUniValue(currenciesValue, rowNames, columnNames);
288         reserveIn = columnAmounts[0];
289         nativeIn = columnAmounts[1];
290         reserveOut = columnAmounts[2];
291         conversionPrice = columnAmounts[3];
292         fees = columnAmounts[4];
293         conversionFees = columnAmounts[5];
294     }
295     nativeFees = uni_get_int64(find_value(obj, "nativefees"));
296     nativeConversionFees = uni_get_int64(find_value(obj, "nativeconversionfees"));
297 }
298
299 CAmount CalculateFractionalOut(CAmount NormalizedReserveIn, CAmount Supply, CAmount NormalizedReserve, int32_t reserveRatio)
300 {
301     static cpp_dec_float_50 one("1");
302     static cpp_dec_float_50 bigSatoshi("100000000");
303     cpp_dec_float_50 reservein(std::to_string(NormalizedReserveIn));
304     reservein = reservein / bigSatoshi;
305     cpp_dec_float_50 supply(std::to_string((Supply ? Supply : 1)));
306     supply = supply / bigSatoshi;
307     cpp_dec_float_50 reserve(std::to_string(NormalizedReserve ? NormalizedReserve : 1));
308     reserve = reserve / bigSatoshi;
309     cpp_dec_float_50 ratio(std::to_string(reserveRatio));
310     ratio = ratio / bigSatoshi;
311
312     //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());
313
314     int64_t fractionalOut = 0;
315
316     // first check if anything to buy
317     if (NormalizedReserveIn)
318     {
319         cpp_dec_float_50 supplyout = bigSatoshi * (supply * (pow((reservein / reserve) + one, ratio) - one));
320         //printf("supplyout: %s\n", supplyout.str(0, std::ios_base::fmtflags::_S_fixed).c_str());
321
322         if (!CCurrencyState::to_int64(supplyout, fractionalOut))
323         {
324             assert(false);
325         }
326     }
327     return fractionalOut;
328 }
329
330 CAmount CalculateReserveOut(CAmount FractionalIn, CAmount Supply, CAmount NormalizedReserve, int32_t reserveRatio)
331 {
332     static cpp_dec_float_50 one("1");
333     static cpp_dec_float_50 bigSatoshi("100000000");
334     cpp_dec_float_50 fractionalin(std::to_string(FractionalIn));
335     fractionalin = fractionalin / bigSatoshi;
336     cpp_dec_float_50 supply(std::to_string((Supply ? Supply : 1)));
337     supply = supply / bigSatoshi;
338     cpp_dec_float_50 reserve(std::to_string(NormalizedReserve ? NormalizedReserve : 1));
339     reserve = reserve / bigSatoshi;
340     cpp_dec_float_50 ratio(std::to_string(reserveRatio));
341     ratio = ratio / bigSatoshi;
342
343     //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());
344
345     int64_t reserveOut = 0;
346
347     // first check if anything to buy
348     if (FractionalIn)
349     {
350         cpp_dec_float_50 reserveout = bigSatoshi * (reserve * (one - pow(one - (fractionalin / supply), (one / ratio))));
351         //printf("reserveout: %s\n", reserveout.str(0, std::ios_base::fmtflags::_S_fixed).c_str());
352
353         if (!CCurrencyState::to_int64(reserveout, reserveOut))
354         {
355             assert(false);
356         }
357     }
358     return reserveOut;
359 }
360
361 // This can handle multiple aggregated, bidirectional conversions in one block of transactions. To determine the conversion price, it 
362 // takes both input amounts of any number of reserves and the fractional currencies targeting those reserves to merge the conversion into one 
363 // merged calculation with the same price across currencies for all transactions in the block. It returns the newly calculated 
364 // conversion prices of the fractional reserve in the reserve currency.
365 std::vector<CAmount> CCurrencyState::ConvertAmounts(const std::vector<CAmount> &inputReserves, const std::vector<CAmount> &inputFractional, CCurrencyState &newState) const
366 {
367     assert(inputReserves.size() == inputFractional.size() && inputReserves.size() == currencies.size());
368
369     newState = *this;
370     std::vector<CAmount> rates;
371
372     bool haveConversion = false;
373     for (auto oneIn : inputReserves)
374     {
375         if (oneIn)
376         {
377             haveConversion = true;
378             break;
379         }
380     }
381     if (!haveConversion)
382     {
383         for (auto oneIn : inputFractional)
384         {
385             if (oneIn)
386             {
387                 haveConversion = true;
388                 break;
389             }
390         }
391     }
392     if (!haveConversion)
393     {
394         return PricesInReserve();
395     }
396
397     // DEBUG ONLY
398     for (auto oneIn : inputReserves)
399     {
400         if (oneIn < 0)
401         {
402             printf("%s: invalid reserve input amount for conversion %ld\n", __func__, oneIn);
403             break;
404         }
405     }
406     for (auto oneIn : inputFractional)
407     {
408         if (oneIn < 0)
409         {
410             printf("%s: invalid fractional input amount for conversion %ld\n", __func__, oneIn);
411             haveConversion = true;
412             break;
413         }
414     }
415     // END DEBUG ONLY
416
417     // Create corresponding fractions of the supply for each currency to be used as starting calculation of that currency's value
418     // Determine the equivalent amount of input and output based on current values. Balance each such that each currency has only
419     // input or output, denominated in supply at the starting value.
420     //
421     // For each currency in either direction, sell to reserve or buy aggregate, we convert to a contribution of amount at the reserve
422     // percent value. For example, consider 4 currencies, r1...r4, which are all 25% reserves of currency fr1. For simplicity of example,
423     // assume 1000 reserve of each reserve currency, where all currencies are equal in value to each other at the outset, and a supply of
424     // 4000, where each fr1 is equal in value to 1 of each component reserve. 
425     // Now, consider the following cases:
426     //
427     // 1. purchase fr1 with 100 r1
428     //      This is treated as a single 25% fractional purchase with respect to amount purchased, ending price, and supply change
429     // 2. purchase fr1 with 100 r1, 100 r2, 100 r3, 100 r4
430     //      This is treated as a common layer of purchase across 4 x 25% currencies, resulting in 100% fractional purchase divided 4 ways
431     // 3. purchase fr1 with 100 r1, 50 r2, 25 r3
432     //      This is treated as 3 separate purchases in order:
433     //          a. one of 25 units across 3 currencies (3 x 25%), making a 75% fractional purchase of 75 units divided equally across 3 currencies
434     //          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
435     //          c. one purchase of 50 units in r1 at 25% fractional purchase
436     // 4. purchase fr1 with 100 r1, sell 100 fr1 to r2
437     //          a. one fractional purchase of 100 units at 25%
438     //          b. one fractional sell of 100 units at 25%
439     //          c. do each in forward and reverse order and set conversion at mean between each
440     // 5. purchase fr1 with 100 r1, 50 r2, sell 100 fr1 to r3, 50 to r4
441     //          This consists of one composite (multi-layer) buy and one composite sell
442     //          a. Compose one two layer purchase of 50 r1 + 50 r2 at 50% and 50 r1 at 25%
443     //          b. Compose one two layer sell of 50 r3 + 50 r4 at 50% and 50 r3 at 25%
444     //          c. execute each operation of a and b in forward and reverse order and set conversion at mean between results
445     //
446
447     std::multimap<CAmount, std::pair<CAmount, uint160>> fractionalIn, fractionalOut;
448
449     // aggregate amounts of ins and outs across all currencies expressed in fractional values in both directions first buy/sell, then sell/buy
450     std::map<uint160, std::pair<CAmount, CAmount>> fractionalInMap, fractionalOutMap;
451
452     arith_uint256 bigSatoshi(SATOSHIDEN);
453     arith_uint256 bigSupply(supply);
454
455     int32_t totalReserveWeight = 0;
456     int32_t maxReserveRatio = 0;
457
458     for (auto weight : weights)
459     {
460         maxReserveRatio = weight > maxReserveRatio ? weight : maxReserveRatio;
461         totalReserveWeight += weight;
462     }
463
464     if (!maxReserveRatio)
465     {
466         LogPrintf("%s: attempting to convert amounts on non-reserve currency\n", __func__);
467         return rates;
468     }
469
470
471
472     arith_uint256 bigMaxReserveRatio = arith_uint256(maxReserveRatio);
473     arith_uint256 bigTotalReserveWeight = arith_uint256(totalReserveWeight);
474
475     // reduce each currency change to a net inflow or outflow of fractional currency and
476     // store both negative and positive in structures sorted by the net amount, adjusted
477     // by the difference of the ratio between the weights of each currency
478     for (int64_t i = 0; i < currencies.size(); i++)
479     {
480         arith_uint256 weight(weights[i]);
481         //printf("%s: %ld\n", __func__, ReserveToNative(inputReserves[i], i));
482         CAmount netFractional = inputFractional[i] - ReserveToNative(inputReserves[i], i);
483         int64_t deltaRatio;
484         if (netFractional > 0)
485         {
486             deltaRatio = ((arith_uint256(netFractional) * bigMaxReserveRatio) / weight).GetLow64();
487             fractionalIn.insert(std::make_pair(deltaRatio, std::make_pair(netFractional, currencies[i])));
488         }
489         else if (netFractional < 0)
490         {
491             netFractional = -netFractional;
492             deltaRatio = ((arith_uint256(netFractional) * bigMaxReserveRatio) / weight).GetLow64();
493             fractionalOut.insert(std::make_pair(deltaRatio, std::make_pair(netFractional, currencies[i])));
494         }
495     }
496
497     // create "layers" of equivalent value at different fractional percentages
498     // across currencies going in or out at the same time, enabling their effect on the aggregate
499     // to be represented by a larger fractional percent impact of "normalized reserve" on the currency, 
500     // which results in accurate pricing impact simulating a basket of currencies.
501     //
502     // since we have all values sorted, the lowest non-zero value determines the first common layer, then next lowest, the next, etc.
503     std::vector<std::pair<int32_t, std::pair<CAmount, std::vector<uint160>>>> fractionalLayersIn, fractionalLayersOut;
504     auto reserveMap = GetReserveMap();
505
506     CAmount layerAmount = 0;
507     CAmount layerStart;
508
509     for (auto inFIT = fractionalIn.upper_bound(layerAmount); inFIT != fractionalIn.end(); inFIT = fractionalIn.upper_bound(layerAmount))
510     {
511         // make a common layer out of all entries from here until the end
512         int frIdx = fractionalLayersIn.size();
513         layerStart = layerAmount;
514         layerAmount = inFIT->first;
515         CAmount layerHeight = layerAmount - layerStart;
516         fractionalLayersIn.emplace_back(std::make_pair(0, std::make_pair(0, std::vector<uint160>())));
517         for (auto it = inFIT; it != fractionalIn.end(); it++)
518         {
519             // reverse the calculation from layer height to amount for this currency, based on currency weight
520             int32_t weight = weights[reserveMap[it->second.second]];
521             CAmount curAmt = ((arith_uint256(layerHeight) * arith_uint256(weight) / bigMaxReserveRatio)).GetLow64();
522             it->second.first -= curAmt;
523             assert(it->second.first >= 0);
524
525             fractionalLayersIn[frIdx].first += weight;
526             fractionalLayersIn[frIdx].second.first += curAmt;
527             fractionalLayersIn[frIdx].second.second.push_back(it->second.second);
528         }
529     }    
530
531     layerAmount = 0;
532     for (auto outFIT = fractionalOut.upper_bound(layerAmount); outFIT != fractionalOut.end(); outFIT = fractionalOut.upper_bound(layerAmount))
533     {
534         int frIdx = fractionalLayersOut.size();
535         layerStart = layerAmount;
536         layerAmount = outFIT->first;
537         CAmount layerHeight = layerAmount - layerStart;
538         fractionalLayersOut.emplace_back(std::make_pair(0, std::make_pair(0, std::vector<uint160>())));
539         for (auto it = outFIT; it != fractionalOut.end(); it++)
540         {
541             int32_t weight = weights[reserveMap[it->second.second]];
542             CAmount curAmt = ((arith_uint256(layerHeight) * arith_uint256(weight) / bigMaxReserveRatio)).GetLow64();
543             it->second.first -= curAmt;
544             assert(it->second.first >= 0);
545
546             fractionalLayersOut[frIdx].first += weight;
547             fractionalLayersOut[frIdx].second.first += curAmt;
548             fractionalLayersOut[frIdx].second.second.push_back(it->second.second);
549         }
550     }    
551
552     int64_t supplyAfterBuy = 0, supplyAfterBuySell = 0, supplyAfterSell = 0, supplyAfterSellBuy = 0;
553     int64_t reserveAfterBuy = 0, reserveAfterBuySell = 0, reserveAfterSell = 0, reserveAfterSellBuy = 0;
554
555     // first, loop through all buys layer by layer. calculate and divide the proceeds between currencies
556     // in each participating layer, in accordance with each currency's relative percentage
557     CAmount addSupply = 0;
558     CAmount addNormalizedReserves = 0;
559     for (auto &layer : fractionalLayersOut)
560     {
561         // each layer has a fractional percentage/weight and a total amount, determined by the total of all weights for that layer
562         // and net amounts across all currencies in that layer. each layer also includes a list of all currencies.
563         //
564         // calculate a fractional buy at the total layer ratio for the amount specified
565         // and divide the value according to the relative weight of each currency, adding to each entry of fractionalOutMap
566         arith_uint256 bigLayerWeight = arith_uint256(layer.first);
567         CAmount totalLayerReserves = ((bigSupply * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReserves;
568         addNormalizedReserves += layer.second.first;
569         CAmount newSupply = CalculateFractionalOut(layer.second.first, supply + addSupply, totalLayerReserves, layer.first);
570         arith_uint256 bigNewSupply(newSupply);
571         addSupply += newSupply;
572         for (auto &id : layer.second.second)
573         {
574             auto idIT = fractionalOutMap.find(id);
575             CAmount newSupplyForCurrency = ((bigNewSupply * weights[reserveMap[id]]) / bigLayerWeight).GetLow64();
576
577             // initialize or add to the new supply for this currency
578             if (idIT == fractionalOutMap.end())
579             {
580                 fractionalOutMap[id] = std::make_pair(newSupplyForCurrency, int64_t(0));
581             }
582             else
583             {
584                 idIT->second.first += newSupplyForCurrency;
585             }
586         }
587     }
588
589     supplyAfterBuy = supply + addSupply;
590     assert(supplyAfterBuy >= 0);
591
592     reserveAfterBuy = supply + addNormalizedReserves;
593     assert(reserveAfterBuy >= 0);
594
595     addSupply = 0;
596     addNormalizedReserves = 0;
597     CAmount addNormalizedReservesBB = 0, addNormalizedReservesAB = 0;
598
599     // calculate sell both before and after buy through this loop
600     for (auto &layer : fractionalLayersIn)
601     {
602         // first calculate sell before-buy, then after-buy
603         arith_uint256 bigLayerWeight(layer.first);
604
605         // before-buy starting point
606         CAmount totalLayerReservesBB = ((bigSupply * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReserves;
607         CAmount totalLayerReservesAB = ((arith_uint256(supplyAfterBuy) * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReserves;
608
609         CAmount newNormalizedReserveBB = CalculateReserveOut(layer.second.first, supply + addSupply, totalLayerReservesBB + addNormalizedReservesBB, layer.first);
610         CAmount newNormalizedReserveAB = CalculateReserveOut(layer.second.first, supplyAfterBuy + addSupply, totalLayerReservesAB + addNormalizedReservesAB, layer.first);
611
612         // input fractional is burned and output reserves are removed from reserves
613         addSupply -= layer.second.first;
614         addNormalizedReservesBB -= newNormalizedReserveBB;
615         addNormalizedReservesAB -= newNormalizedReserveAB;
616
617         for (auto &id : layer.second.second)
618         {
619             auto idIT = fractionalInMap.find(id);
620             CAmount newReservesForCurrencyBB = ((arith_uint256(newNormalizedReserveBB) * arith_uint256(weights[reserveMap[id]])) / bigLayerWeight).GetLow64();
621             CAmount newReservesForCurrencyAB = ((arith_uint256(newNormalizedReserveAB) * arith_uint256(weights[reserveMap[id]])) / bigLayerWeight).GetLow64();
622
623             // initialize or add to the new supply for this currency
624             if (idIT == fractionalInMap.end())
625             {
626                 fractionalInMap[id] = std::make_pair(newReservesForCurrencyBB, newReservesForCurrencyAB);
627             }
628             else
629             {
630                 idIT->second.first += newReservesForCurrencyBB;
631                 idIT->second.second += newReservesForCurrencyAB;
632             }
633         }
634     }
635
636     supplyAfterSell = supply + addSupply;
637     assert(supplyAfterSell >= 0);
638
639     supplyAfterBuySell = supplyAfterBuy + addSupply;
640     assert(supplyAfterBuySell >= 0);
641
642     reserveAfterSell = supply + addNormalizedReservesBB;
643     assert(reserveAfterSell >= 0);
644
645     reserveAfterBuySell = reserveAfterBuy + addNormalizedReservesAB;
646     assert(reserveAfterBuySell >= 0);
647
648     addSupply = 0;
649     addNormalizedReserves = 0;
650
651     // now calculate buy after sell
652     for (auto &layer : fractionalLayersOut)
653     {
654         arith_uint256 bigLayerWeight = arith_uint256(layer.first);
655         CAmount totalLayerReserves = ((arith_uint256(supplyAfterSell) * bigLayerWeight) / bigSatoshi).GetLow64() + addNormalizedReserves;
656         addNormalizedReserves += layer.second.first;
657         CAmount newSupply = CalculateFractionalOut(layer.second.first, supplyAfterSell + addSupply, totalLayerReserves, layer.first);
658         arith_uint256 bigNewSupply(newSupply);
659         addSupply += newSupply;
660         for (auto &id : layer.second.second)
661         {
662             auto idIT = fractionalOutMap.find(id);
663
664             assert(idIT != fractionalOutMap.end());
665
666             idIT->second.second += ((bigNewSupply * weights[reserveMap[id]]) / bigLayerWeight).GetLow64();
667         }
668     }
669
670     // now loop through all currencies, calculate conversion rates for each based on mean of all prices that we calculate for
671     // buy before sell and sell before buy
672     rates.resize(currencies.size());
673     for (int i = 0; i < currencies.size(); i++)
674     {
675         // each coin has an amount of reserve in, an amount of fractional in, and potentially two delta amounts in one of the
676         // fractionalInMap or fractionalOutMap maps, one for buy before sell and one for sell before buy.
677         // add the mean of the delta amounts to the appropriate side of the equation and calculate a price for each
678         // currency.
679         auto fractionalOutIT = fractionalOutMap.find(currencies[i]);
680         auto fractionalInIT = fractionalInMap.find(currencies[i]);
681
682         auto inputReserve = inputReserves[i];
683         auto inputFraction = inputFractional[i];
684
685         CAmount fractionDelta = 0, reserveDelta = 0;
686
687         if (fractionalOutIT != fractionalOutMap.end())
688         {
689             arith_uint256 bigFractionDelta(fractionalOutIT->second.first);
690             fractionDelta = ((bigFractionDelta + arith_uint256(fractionalOutIT->second.second)) >> 1).GetLow64();
691             assert(inputFraction + fractionDelta > 0);
692
693             rates[i] = ((arith_uint256(inputReserve) * bigSatoshi) / arith_uint256(inputFraction + fractionDelta)).GetLow64();
694
695             // add the new reserve and supply to the currency
696             newState.supply += fractionDelta;
697
698             // all reserves have been calculated using a substituted value, which was 1:1 for native initially
699             newState.reserves[i] += inputFractional[i] ? NativeToReserveRaw(fractionDelta, rates[i]) : inputReserves[i];
700         }
701         else if (fractionalInIT != fractionalInMap.end())
702         {
703             arith_uint256 bigReserveDelta(fractionalInIT->second.first);
704             CAmount adjustedReserveDelta = NativeToReserve(((bigReserveDelta + arith_uint256(fractionalInIT->second.second)) >> 1).GetLow64(), i);
705
706             assert(inputFraction > 0);
707
708             rates[i] = ((arith_uint256(inputReserve + adjustedReserveDelta) * bigSatoshi) / arith_uint256(inputFraction)).GetLow64();
709
710             // subtract the fractional and reserve that has left the currency
711             newState.supply -= inputFraction;
712             newState.reserves[i] -= adjustedReserveDelta;
713         }
714         else
715         {
716             rates[i] = PriceInReserve(i);
717         }
718     }
719     return rates;
720 }
721
722 CAmount CCurrencyState::ConvertAmounts(CAmount inputReserve, CAmount inputFraction, CCurrencyState &newState, int32_t reserveIndex) const
723 {
724     if (reserveIndex >= newState.currencies.size())
725     {
726         printf("%s: reserve index out of range\n", __func__);
727         return 0;
728     }
729     std::vector<CAmount> inputReserves(newState.currencies.size());
730     inputReserves[reserveIndex] = inputReserve;
731     std::vector<CAmount> inputFractional(newState.currencies.size());
732     inputFractional[reserveIndex] = inputFraction;
733     std::vector<CAmount> retVal = ConvertAmounts(inputReserves, inputFractional, newState);
734     return retVal[reserveIndex];
735 }
736
737 /*
738 // This can handle multiple aggregated, bidirectional conversions in one block of transactions. To determine the conversion price, it 
739 // takes both input amounts of the reserve and the fractional currency to merge the conversion into one calculation
740 // with the same price for all transactions in the block. It returns the newly calculated conversion price of the fractional 
741 // reserve in the reserve currency.
742 CAmount CCurrencyState::ConvertAmounts(CAmount inputReserve, CAmount inputFractional, CCurrencyState &newState) const
743 {
744     newState = *this;
745     CAmount conversionPrice = PriceInReserve();
746
747     int64_t totalFractionalOut = 0;     // how much fractional goes to buyers
748     int64_t totalReserveOut = 0;        // how much reserve goes to sellers
749
750     // if both conversions are zero, nothing to do but return current price
751     if ((!inputReserve && !inputFractional) || !(flags & FLAG_FRACTIONAL))
752     {
753         return conversionPrice;
754     }
755
756     // first, cancel out all parity at the current price, leaving one or the other amount to be converted, but not both
757     // that results in consistently minimum slippage in either direction
758     CAmount reserveOffset = NativeToReserve(inputFractional, conversionPrice);
759     CAmount convertFractional = 0, convertReserve = 0;
760     if (inputReserve >= reserveOffset)
761     {
762         convertReserve = inputReserve - reserveOffset;
763         totalFractionalOut += inputFractional;
764     }
765     else
766     {
767         CAmount fractionalOffset = ReserveToNative(inputReserve, conversionPrice);
768         convertFractional = inputFractional - fractionalOffset;
769         if (convertFractional < 0)
770         {
771             convertFractional = 0;
772         }
773         totalReserveOut += inputReserve;
774     }
775
776     if (!convertReserve && !convertFractional)
777     {
778         return conversionPrice;
779     }
780
781     cpp_dec_float_50 reservein(std::to_string(convertReserve));
782     cpp_dec_float_50 fractionalin(std::to_string(convertFractional));
783     cpp_dec_float_50 supply(std::to_string((Supply)));
784     cpp_dec_float_50 reserve(std::to_string(Reserve));
785     cpp_dec_float_50 ratio = GetReserveRatio();
786     cpp_dec_float_50 one("1");
787     cpp_dec_float_50 dec_satoshi(std::to_string(CReserveExchange::SATOSHIDEN));
788
789     // first check if anything to buy
790     if (convertReserve)
791     {
792         cpp_dec_float_50 supplyout;
793         supplyout = (supply * (pow((reservein / reserve) + one, ratio) - one));
794
795         int64_t supplyOut;
796         if (!to_int64(supplyout, supplyOut))
797         {
798             assert(false);
799         }
800         totalFractionalOut += supplyOut;
801
802         cpp_dec_float_50 dec_price = cpp_dec_float_50(std::to_string(inputReserve)) / cpp_dec_float_50(std::to_string(totalFractionalOut));
803         cpp_dec_float_50 reserveFromFractional = cpp_dec_float_50(std::to_string(inputFractional)) * dec_price;
804         dec_price = dec_price * dec_satoshi;
805
806         if (!to_int64(reserveFromFractional, totalReserveOut))
807         {
808             assert(false);
809         }
810
811         if (!to_int64(dec_price, conversionPrice))
812         {
813             assert(false);
814         }
815     }
816     else
817     { 
818         cpp_dec_float_50 reserveout;
819         int64_t reserveOut;
820         reserveout = reserve * (one - pow(one - (fractionalin / supply), (one / ratio)));
821         if (!to_int64(reserveout, reserveOut))
822         {
823             assert(false);
824         }
825
826         totalReserveOut += reserveOut;
827
828         cpp_dec_float_50 dec_price = cpp_dec_float_50(std::to_string(totalReserveOut)) / cpp_dec_float_50(std::to_string(inputFractional));
829         cpp_dec_float_50 fractionalFromReserve = cpp_dec_float_50(std::to_string(inputReserve)) / dec_price;
830         dec_price = dec_price * dec_satoshi;
831
832         if (!to_int64(fractionalFromReserve, totalFractionalOut))
833         {
834             assert(false);
835         }
836
837         if (!to_int64(dec_price, conversionPrice))
838         {
839             assert(false);
840         }
841     }
842
843     newState.Supply += totalFractionalOut - inputFractional;
844     newState.Reserve += inputReserve - totalReserveOut;
845
846     return conversionPrice;
847 }
848 */
849
850 void CReserveTransactionDescriptor::AddReserveInput(const uint160 &currency, CAmount value)
851 {
852     //printf("adding %ld:%s reserve input\n", value, EncodeDestination(CIdentityID(currency)).c_str());
853
854     auto it = currencies.find(currency);
855     if (it != currencies.end())
856     {
857         it->second.reserveIn += value;
858     }
859     else
860     {
861         currencies[currency] = CReserveInOuts(value, 0, 0, 0, 0);
862     }
863 }
864
865 void CReserveTransactionDescriptor::AddReserveOutput(const uint160 &currency, CAmount value)
866 {
867     //printf("adding %ld:%s reserve output\n", value, EncodeDestination(CIdentityID(currency)).c_str());
868
869     auto it = currencies.find(currency);
870     if (it != currencies.end())
871     {
872         it->second.reserveOut += value;
873     }
874     else
875     {
876         currencies[currency] = CReserveInOuts(0, value, 0, 0, 0);
877     }
878 }
879
880 void CReserveTransactionDescriptor::AddReserveOutConverted(const uint160 &currency, CAmount value)
881 {
882     auto it = currencies.find(currency);
883     if (it != currencies.end())
884     {
885         it->second.reserveOutConverted += value;
886     }
887     else
888     {
889         currencies[currency] = CReserveInOuts(0, 0, value, 0, 0);
890     }
891 }
892
893 void CReserveTransactionDescriptor::AddNativeOutConverted(const uint160 &currency, CAmount value)
894 {
895     auto it = currencies.find(currency);
896     if (it != currencies.end())
897     {
898         it->second.nativeOutConverted += value;
899     }
900     else
901     {
902         currencies[currency] = CReserveInOuts(0, 0, 0, value, 0);
903     }
904 }
905
906 void CReserveTransactionDescriptor::AddReserveConversionFees(const uint160 &currency, CAmount value)
907 {
908     auto it = currencies.find(currency);
909     if (it != currencies.end())
910     {
911         it->second.reserveConversionFees += value;
912     }
913     else
914     {
915         currencies[currency] = CReserveInOuts(0, 0, 0, 0, value);
916     }
917 }
918
919 void CReserveTransactionDescriptor::AddReserveExchange(const CReserveExchange &rex, int32_t outputIndex, int32_t nHeight)
920 {
921     CAmount fee = 0;
922
923     bool wasMarket = IsMarket();
924
925     flags |= IS_RESERVE + IS_RESERVEEXCHANGE;
926
927     if (IsLimit() || rex.flags & CReserveExchange::LIMIT)
928     {
929         if (wasMarket || (vRex.size() && (vRex.back().second.flags != rex.flags || (vRex.back().second.nValidBefore != rex.nValidBefore))))
930         {
931             flags |= IS_REJECT;
932             return;
933         }
934
935         flags |= IS_LIMIT;
936
937         if (rex.nValidBefore > nHeight)
938         {
939             flags |= IS_FILLORKILL;
940             // fill or kill fee must be pre-subtracted, but is added back on success, making conversion the only
941             // requred fee on success
942             fee = CalculateConversionFee(rex.nValue + rex.FILL_OR_KILL_FEE);
943             if (rex.flags & CReserveExchange::TO_RESERVE)
944             {
945                 numSells += 1;
946                 nativeConversionFees += fee;
947                 AddNativeOutConverted(rex.currencyID, (rex.nValue + rex.FILL_OR_KILL_FEE) - fee);
948                 nativeOut += rex.nValue + rex.FILL_OR_KILL_FEE;             // output is always less fees
949             }
950             else
951             {
952                 numBuys += 1;
953                 auto it = currencies.find(rex.currencyID);
954                 if (it != currencies.end())
955                 {
956                     it->second.reserveOut += rex.nValue + rex.FILL_OR_KILL_FEE;
957                     it->second.reserveOutConverted += rex.nValue + rex.FILL_OR_KILL_FEE - fee;
958                     it->second.reserveConversionFees += fee;
959                 }
960                 else
961                 {
962                     currencies[rex.currencyID] = CReserveInOuts(0, rex.nValue + rex.FILL_OR_KILL_FEE, rex.nValue + rex.FILL_OR_KILL_FEE - fee, 0, fee);
963                 }
964             }
965         }
966         else
967         {
968             flags &= !IS_RESERVEEXCHANGE;                       // no longer a reserve exchange transaction, falls back to normal reserve tx
969             flags |= IS_FILLORKILLFAIL;
970
971             if (rex.flags & CReserveExchange::TO_RESERVE)
972             {
973                 numSells += 1;
974                 nativeOut += rex.nValue;
975             }
976             else
977             {
978                 numBuys += 1;
979                 AddReserveOutput(rex.currencyID, rex.nValue);
980             }
981         }
982     }
983     else
984     {
985         fee = CalculateConversionFee(rex.nValue);
986         if (rex.flags & CReserveExchange::TO_RESERVE)
987         {
988             numSells += 1;
989             nativeConversionFees += fee;
990             AddNativeOutConverted(rex.currencyID, rex.nValue - fee);
991             nativeOut += rex.nValue;                    // conversion fee is considered part of the total native out, since it will be attributed to conversion tx
992         }
993         else
994         {
995             numBuys += 1;
996             if (!(flags & IS_IMPORT))
997             {
998                 auto it = currencies.find(rex.currencyID);
999                 if (it != currencies.end())
1000                 {
1001                     it->second.reserveOut += rex.nValue;
1002                     it->second.reserveOutConverted += rex.nValue - fee;
1003                     it->second.reserveConversionFees += fee;
1004                 }
1005                 else
1006                 {
1007                     currencies[rex.currencyID] = CReserveInOuts(0, rex.nValue, rex.nValue - fee, 0, fee);
1008                 }
1009             }
1010         }
1011     }                        
1012     vRex.push_back(std::make_pair(outputIndex, rex));
1013 }
1014
1015 CAmount CReserveTransactionDescriptor::AllFeesAsNative(const CCurrencyState &currencyState) const
1016 {
1017     CAmount nativeFees = NativeFees();
1018     CCurrencyValueMap reserveFees = ReserveFees();
1019     for (int i = 0; i < currencyState.currencies.size(); i++)
1020     {
1021         auto it = reserveFees.valueMap.find(currencyState.currencies[i]);
1022         if (it != reserveFees.valueMap.end())
1023         {
1024             nativeFees += currencyState.ReserveToNative(it->second, i);
1025         }
1026     }
1027     return nativeFees;
1028 }
1029
1030 CAmount CReserveTransactionDescriptor::AllFeesAsNative(const CCurrencyState &currencyState, const std::vector<CAmount> &exchangeRates) const
1031 {
1032     assert(exchangeRates.size() == currencyState.currencies.size());
1033     CAmount nativeFees = NativeFees();
1034     CCurrencyValueMap reserveFees = ReserveFees();
1035     for (int i = 0; i < currencyState.currencies.size(); i++)
1036     {
1037         auto it = reserveFees.valueMap.find(currencyState.currencies[i]);
1038         if (it != reserveFees.valueMap.end())
1039         {
1040             nativeFees += currencyState.ReserveToNativeRaw(it->second, exchangeRates[i]);
1041         }
1042     }
1043     return nativeFees;
1044 }
1045
1046 CCurrencyValueMap CReserveTransactionDescriptor::AllFeesAsReserve(const CCurrencyState &currencyState, int defaultReserve) const
1047 {
1048     CCurrencyValueMap reserveFees = ReserveFees();
1049
1050     auto it = reserveFees.valueMap.find(currencyState.currencies[defaultReserve]);
1051     if (it != reserveFees.valueMap.end())
1052     {
1053         it->second += currencyState.NativeToReserve(NativeFees(), defaultReserve);
1054     }
1055     else
1056     {
1057         reserveFees.valueMap[currencyState.currencies[defaultReserve]] = NativeFees();
1058     }
1059     return reserveFees;
1060 }
1061
1062 CCurrencyValueMap CReserveTransactionDescriptor::AllFeesAsReserve(const CCurrencyState &currencyState, const std::vector<CAmount> &exchangeRates, int defaultReserve) const
1063 {
1064     CCurrencyValueMap reserveFees = ReserveFees();
1065
1066     auto it = reserveFees.valueMap.find(currencyState.currencies[defaultReserve]);
1067     if (it != reserveFees.valueMap.end())
1068     {
1069         it->second += currencyState.NativeToReserveRaw(NativeFees(), exchangeRates[defaultReserve]);
1070     }
1071     else
1072     {
1073         reserveFees.valueMap[currencyState.currencies[defaultReserve]] = NativeFees();
1074     }
1075     return reserveFees;
1076 }
1077
1078 /*
1079  * Checks all structural aspects of the reserve part of a transaction that may have reserve inputs and/or outputs
1080  */
1081 CReserveTransactionDescriptor::CReserveTransactionDescriptor(const CTransaction &tx, const CCoinsViewCache &view, int32_t nHeight) :
1082         flags(0),
1083         ptx(NULL),
1084         numBuys(0),
1085         numSells(0),
1086         numTransfers(0),
1087         nativeIn(0),
1088         nativeOut(0),
1089         nativeConversionFees(0)
1090 {
1091     // market conversions can have any number of both buy and sell conversion outputs, this is used to make efficient, aggregated
1092     // reserve transfer operations with conversion
1093
1094     // limit conversion outputs may have multiple outputs with different input amounts and destinations, 
1095     // but they must not be mixed in a transaction with any dissimilar set of conditions on the output, 
1096     // including mixing with market orders, parity of buy or sell, limit value and validbefore values, 
1097     // or the transaction is considered invalid
1098
1099     // no inputs are valid at height 0
1100     if (!nHeight)
1101     {
1102         flags |= IS_REJECT;
1103         return;
1104     }
1105
1106     // reserve descriptor transactions cannot run until identity activates
1107     if (!chainActive.LastTip() ||
1108         CConstVerusSolutionVector::activationHeight.ActiveVersion(nHeight) < CConstVerusSolutionVector::activationHeight.ACTIVATE_IDENTITY)
1109     {
1110         return;
1111     }
1112
1113     bool isVerusActive = IsVerusActive();
1114     bool isPBaaSActivation = CConstVerusSolutionVector::activationHeight.IsActivationHeight(CActivationHeight::ACTIVATE_PBAAS, nHeight);
1115
1116     CCrossChainImport cci;
1117     CCrossChainExport ccx;
1118
1119     CNameReservation nameReservation;
1120     CIdentity identity;
1121     CCurrencyDefinition newCurrencyDef;
1122
1123     flags |= IS_VALID;
1124
1125     for (int i = 0; i < tx.vout.size(); i++)
1126     {
1127         COptCCParams p;
1128
1129         if (tx.vout[i].scriptPubKey.IsPayToCryptoCondition(p) && p.IsValid())
1130         {
1131             switch (p.evalCode)
1132             {
1133                 case EVAL_IDENTITY_RESERVATION:
1134                 {
1135                     // one name reservation per transaction
1136                     if (p.version < p.VERSION_V3 || !p.vData.size() || nameReservation.IsValid() || !(nameReservation = CNameReservation(p.vData[0])).IsValid())
1137                     {
1138                         flags &= ~IS_VALID;
1139                         flags |= IS_REJECT;
1140                         return;
1141                     }
1142                     if (identity.IsValid())
1143                     {
1144                         if (identity.name == nameReservation.name)
1145                         {
1146                             flags |= IS_IDENTITY_DEFINITION + IS_HIGH_FEE;
1147                         }
1148                         else
1149                         {
1150                             flags &= ~IS_VALID;
1151                             flags |= IS_REJECT;
1152                             return;
1153                         }
1154                     }
1155                 }
1156                 break;
1157
1158                 case EVAL_IDENTITY_PRIMARY:
1159                 {
1160                     // one identity per transaction
1161                     if (p.version < p.VERSION_V3 || !p.vData.size() || identity.IsValid() || !(identity = CIdentity(p.vData[0])).IsValid())
1162                     {
1163                         flags &= ~IS_VALID;
1164                         flags |= IS_REJECT;
1165                         return;
1166                     }
1167                     flags |= IS_IDENTITY;
1168                     if (nameReservation.IsValid())
1169                     {
1170                         if (identity.name == nameReservation.name)
1171                         {
1172                             flags |= IS_IDENTITY_DEFINITION + IS_HIGH_FEE;
1173                         }
1174                         else
1175                         {
1176                             flags &= ~IS_VALID;
1177                             flags |= IS_REJECT;
1178                             return;
1179                         }
1180                     }
1181                 }
1182                 break;
1183
1184                 case EVAL_RESERVE_DEPOSIT:
1185                 {
1186                     CReserveDeposit rd;
1187                     if (!p.vData.size() || !(rd = CReserveDeposit(p.vData[0])).IsValid())
1188                     {
1189                         flags &= ~IS_VALID;
1190                         flags |= IS_REJECT;
1191                         return;
1192                     }
1193                     for (auto &oneCur : rd.reserveValues.valueMap)
1194                     {
1195                         if (oneCur.first != ASSETCHAINS_CHAINID)
1196                         {
1197                             AddReserveOutput(oneCur.first, oneCur.second);
1198                         }
1199                     }
1200                 }
1201                 break;
1202
1203                 case EVAL_RESERVE_OUTPUT:
1204                 {
1205                     CTokenOutput ro;
1206                     if (!p.vData.size() || !(ro = CTokenOutput(p.vData[0])).IsValid())
1207                     {
1208                         flags &= ~IS_VALID;
1209                         flags |= IS_REJECT;
1210                         return;
1211                     }
1212                     if (ro.nValue)
1213                     {
1214                         AddReserveOutput(ro);
1215                     }
1216                 }
1217                 break;
1218
1219                 case EVAL_RESERVE_TRANSFER:
1220                 {
1221                     CReserveTransfer rt;
1222                     if (!p.vData.size() || !(rt = CReserveTransfer(p.vData[0])).IsValid())
1223                     {
1224                         flags &= ~IS_VALID;
1225                         flags |= IS_REJECT;
1226                         return;
1227                     }
1228                     // on a PBaaS reserve chain, a reserve transfer is always denominated in reserve, as exchange must happen before it is
1229                     // created. explicit fees in transfer object are for export and import, not initial mining
1230                     AddReserveTransfer(rt);
1231                 }
1232                 break;
1233
1234                 case EVAL_RESERVE_EXCHANGE:
1235                 {
1236                     CReserveExchange rex;
1237                     if (isVerusActive || !p.vData.size() || !(rex = CReserveExchange(p.vData[0])).IsValid())
1238                     {
1239                         flags &= ~IS_VALID;
1240                         flags |= IS_REJECT;
1241                         return;
1242                     }
1243
1244                     // if we send the output to the reserve chain, it must be a TO_RESERVE transaction
1245                     if ((rex.flags & rex.SEND_OUTPUT) && !(rex.flags & rex.TO_RESERVE))
1246                     {
1247                         flags &= ~IS_VALID;
1248                         flags |= IS_REJECT;
1249                         return;
1250                     }
1251                     AddReserveExchange(rex, i, nHeight);
1252
1253                     if (IsReject())
1254                     {
1255                         flags &= ~IS_VALID;
1256                         return;
1257                     }
1258                 }
1259                 break;
1260
1261                 case EVAL_CROSSCHAIN_IMPORT:
1262                 {
1263                     // if this is an import, add the amount imported to the reserve input and the amount of reserve output as
1264                     // the amount available to take from this transaction in reserve as an import fee
1265                     if (flags & IS_IMPORT || !p.vData.size() || !(cci = CCrossChainImport(p.vData[0])).IsValid())
1266                     {
1267                         flags &= ~IS_VALID;
1268                         flags |= IS_REJECT;
1269                         return;
1270                     }
1271
1272                     flags |= IS_IMPORT;
1273
1274                     std::vector<CBaseChainObject *> chainObjs;
1275                     // either a fully valid import with an export or the first import either in block 1 or the chain definition
1276                     // on the Verus chain
1277                     std::vector<CCurrencyDefinition> txCurrencies = CCurrencyDefinition::GetCurrencyDefinitions(tx);
1278                     if ((nHeight == 1 && tx.IsCoinBase() && !IsVerusActive()) ||
1279                         (txCurrencies.size() == 1 &&
1280                         ((txCurrencies[0].parent == ASSETCHAINS_CHAINID) || 
1281                          (txCurrencies[0].GetID() == ASSETCHAINS_CHAINID && isPBaaSActivation)) &&
1282                         isVerusActive) ||
1283                         (tx.vout.back().scriptPubKey.IsOpReturn() &&
1284                         (chainObjs = RetrieveOpRetArray(tx.vout.back().scriptPubKey)).size() >= 1 &&
1285                         chainObjs[0]->objectType == CHAINOBJ_TRANSACTION_PROOF))
1286                     {
1287                         if (chainObjs.size())
1288                         {
1289                             CPartialTransactionProof &exportTxProof = ((CChainObject<CPartialTransactionProof> *)chainObjs[0])->object;
1290                             CTransaction exportTx;
1291                             std::vector<CBaseChainObject *> exportTransfers;
1292
1293                             if (exportTxProof.txProof.proofSequence.size() == 3 &&
1294                                 !exportTxProof.GetPartialTransaction(exportTx).IsNull() &&
1295                                 (ccx = CCrossChainExport(exportTx)).IsValid() && 
1296                                 exportTx.vout.back().scriptPubKey.IsOpReturn() &&
1297                                 (exportTransfers = RetrieveOpRetArray(exportTx.vout.back().scriptPubKey)).size() &&
1298                                 exportTransfers[0]->objectType == CHAINOBJ_RESERVETRANSFER)
1299                             {
1300                                 // an import transaction is built from the export list
1301                                 // we can rebuild it and confirm that it matches exactly
1302                                 // we can also subtract all pre-converted reserve from input
1303                                 // and verify that the output matches the proper conversion
1304
1305                                 // get the chain definition of the chain we are importing
1306                                 std::vector<CTxOut> checkOutputs;
1307
1308                                 CPBaaSNotarization importNotarization;
1309                                 CCurrencyDefinition importCurrencyDef = ConnectedChains.GetCachedCurrency(cci.systemID);
1310                                 if (importCurrencyDef.IsToken() && !(importNotarization = CPBaaSNotarization(tx)).IsValid())
1311                                 {
1312                                     flags |= IS_REJECT;
1313                                 }
1314                                 else
1315                                 {
1316                                     CCoinbaseCurrencyState currencyState = importCurrencyDef.IsToken() ?
1317                                                                            importNotarization.currencyState :
1318                                                                            GetInitialCurrencyState(importCurrencyDef);
1319                                     importCurrencyDef.conversions = currencyState.conversionPrice;
1320
1321                                     if (!currencyState.IsValid() ||
1322                                         !AddReserveTransferImportOutputs(cci.systemID, importCurrencyDef, currencyState, exportTransfers, checkOutputs))
1323                                     {
1324                                         flags |= IS_REJECT;
1325                                     }
1326
1327                                     for (auto &oneOutCur : cci.totalReserveOutMap.valueMap)
1328                                     {
1329                                         AddReserveOutput(oneOutCur.first, oneOutCur.second);
1330                                     }
1331                                 }
1332                                 // TODO:PBAAS - hardening - validate all the outputs we got back as the same as what is in the transaction
1333                             }
1334                             else
1335                             {
1336                                 flags |= IS_REJECT;
1337                             }
1338                         }
1339                     }
1340                     else
1341                     {
1342                         flags |= IS_REJECT;
1343                     }
1344                     DeleteOpRetObjects(chainObjs);
1345                     if (IsReject())
1346                     {
1347                         flags &= ~IS_VALID;
1348                         return;
1349                     }
1350                 }
1351                 break;
1352
1353                 // this check will need to be made complete by preventing mixing both here and where the others
1354                 // are seen
1355                 case EVAL_CROSSCHAIN_EXPORT:
1356                 {
1357                     // cross chain export is incompatible with reserve exchange outputs
1358                     std::vector<CCurrencyDefinition> txCurrencies = CCurrencyDefinition::GetCurrencyDefinitions(tx);
1359                     if (IsReserveExchange() ||
1360                         (!(nHeight == 1 && tx.IsCoinBase() && !isVerusActive) &&
1361                          !(txCurrencies.size() &&
1362                           ((txCurrencies[0].parent == ASSETCHAINS_CHAINID) || 
1363                           (txCurrencies[0].GetID() == ASSETCHAINS_CHAINID && isPBaaSActivation))) &&
1364                         (flags & IS_IMPORT)))
1365                     {
1366                         flags &= ~IS_VALID;
1367                         flags |= IS_REJECT;
1368                         return;
1369                     }
1370                     flags |= IS_EXPORT;
1371                 }
1372                 break;
1373
1374                 case EVAL_CURRENCY_DEFINITION:
1375                 {
1376
1377                 }
1378                 break;
1379             }
1380         }
1381     }
1382
1383     // we have all inputs, outputs, and fees, if check inputs, we can check all for consistency
1384     // inputs may be in the memory pool or on the blockchain
1385     CAmount dummyInterest;
1386     nativeOut = tx.GetValueOut();
1387     nativeIn = view.GetValueIn(nHeight, &dummyInterest, tx);
1388
1389     // if we don't have a reserve transaction, we're done
1390     // don't try to replace basic transaction validation
1391     if (IsReserve())
1392     {
1393         CAmount nValueIn = 0;
1394
1395         // if it is a conversion to reserve, the amount in is accurate, since it is from the native coin, if converting to
1396         // the native PBaaS coin, the amount input is a sum of all the reserve token values of all of the inputs
1397         auto reservesIn = view.GetReserveValueIn(nHeight, tx);
1398         for (auto &oneIn : reservesIn.valueMap)
1399         {
1400             auto it = currencies.find(oneIn.first);
1401             if (it == currencies.end())
1402             {
1403                 currencies[oneIn.first] = CReserveInOuts(oneIn.second, 0, 0, 0, 0);
1404             }
1405             else
1406             {
1407                 it->second.reserveIn += oneIn.second;
1408             }
1409         }
1410
1411         // TODO:PBAAS hardening total minimum required fees as we build the descriptor and
1412         // reject if not correct
1413         ptx = &tx;
1414     }
1415 }
1416
1417 CReserveTransfer RefundExport(const CBaseChainObject *objPtr)
1418 {
1419     if (objPtr->objectType == CHAINOBJ_RESERVETRANSFER)
1420     {
1421         CReserveTransfer &rt = ((CChainObject<CReserveTransfer> *)objPtr)->object;
1422
1423         // convert full ID destinations to normal ID outputs, since it's refund, full ID will be on this chain already
1424         if (rt.destination.type == CTransferDestination::DEST_FULLID)
1425         {
1426             CIdentity(rt.destination.destination);
1427             rt.destination = CTransferDestination(CTransferDestination::DEST_ID, rt.destination.destination);
1428         }
1429
1430         // turn it into a normal transfer, which will create an unconverted output
1431         rt.flags &= ~(CReserveTransfer::SEND_BACK | CReserveTransfer::PRECONVERT | CReserveTransfer::CONVERT);
1432
1433         if (rt.flags & (CReserveTransfer::PREALLOCATE | CReserveTransfer::MINT_CURRENCY))
1434         {
1435             rt.flags &= ~(CReserveTransfer::PREALLOCATE | CReserveTransfer::MINT_CURRENCY);
1436             rt.nValue = 0;
1437         }
1438         rt.destCurrencyID = rt.currencyID;
1439         return rt;
1440     }
1441     return CReserveTransfer();
1442 }
1443
1444 // the source currency indicates the system from which the import comes, but the imports may contain additional
1445 // currencies that are supported in that system and are not limited to the native currency. Fees are assumed to
1446 // be covered by the native currency of the source or source currency, if this is a reserve conversion. That 
1447 // means that all explicit fees are assumed to be in the currency of the source.
1448 bool CReserveTransactionDescriptor::AddReserveTransferImportOutputs(const uint160 &nativeSourceCurrencyID, 
1449                                                                     const CCurrencyDefinition &importCurrencyDef, 
1450                                                                     const CCoinbaseCurrencyState &importCurrencyState,
1451                                                                     const std::vector<CBaseChainObject *> &exportObjects, 
1452                                                                     std::vector<CTxOut> &vOutputs,
1453                                                                     CCoinbaseCurrencyState *pNewCurrencyState)
1454 {
1455     // easy way to refer to return currency state or a dummy without conditionals
1456     CCoinbaseCurrencyState _newCurrencyState;
1457     if (!pNewCurrencyState)
1458     {
1459         pNewCurrencyState = &_newCurrencyState;
1460     }
1461     CCoinbaseCurrencyState &newCurrencyState = *pNewCurrencyState;
1462     newCurrencyState = importCurrencyState;
1463     newCurrencyState.ClearForNextBlock();
1464
1465     // reserve currency amounts converted to fractional
1466     CCurrencyValueMap reserveConverted;
1467
1468     // fractional currency amount and the reserve it is converted to
1469     CCurrencyValueMap fractionalConverted;
1470
1471     // used to keep track of burned fractional currency. this currency is subtracted from the
1472     // currency supply, but not converted. In doing so, it can either raise the price of the fractional
1473     // currency in all other currencies, or increase the reserve ratio of all currencies by some slight amount.
1474     CAmount burnedChangePrice = 0;
1475
1476     // this is cached here, but only used for pre-conversions
1477     CCoinbaseCurrencyState initialCurrencyState;
1478     CCurrencyValueMap preConvertedOutput;
1479
1480     // we do not change native in or conversion fees, but we define all of these members
1481     nativeIn = 0;
1482     numTransfers = 0;
1483     for (auto &oneInOut : currencies)
1484     {
1485         oneInOut.second.reserveIn = 0;
1486         oneInOut.second.reserveOut = 0;
1487     }
1488
1489     bool isVerusActive = IsVerusActive();
1490
1491     CCcontract_info CC;
1492     CCcontract_info *cp;
1493
1494     uint160 systemDestID = importCurrencyDef.systemID;     // native on destination system
1495     uint160 importCurrencyID = importCurrencyDef.GetID();
1496     std::map<uint160, int32_t> currencyIndexMap = importCurrencyDef.GetCurrenciesMap();
1497     //printf("%s\n", importCurrencyDef.ToUniValue().write(1,2).c_str());
1498
1499     std::map<uint160, CAmount> preAllocMap;             // if this contains pre-allocations, only make the necessary map once
1500     CCurrencyValueMap transferFees;                     // calculated fees based on all transfers/conversions, etc.
1501     CCurrencyValueMap feeOutputs;                       // actual fee output amounts
1502
1503     bool feeOutputStart = false;                        // fee outputs must come after all others, this indicates they have started
1504     int nFeeOutputs = 0;                                // number of fee outputs
1505
1506     bool carveOutSet = false;
1507     int32_t totalCarveOut;
1508     CCurrencyValueMap totalCarveOuts;
1509     CAmount totalMinted = 0;
1510
1511     for (int i = 0; i < exportObjects.size(); i++)
1512     {
1513         if (exportObjects[i]->objectType == CHAINOBJ_RESERVETRANSFER)
1514         {
1515             CReserveTransfer _curTransfer;
1516             CReserveTransfer *pCurTransfer;
1517
1518             if (importCurrencyState.IsRefunding())
1519             {
1520                 _curTransfer = RefundExport(exportObjects[i]);
1521                 pCurTransfer = &_curTransfer;
1522             }
1523             else
1524             {
1525                 pCurTransfer = &(((CChainObject<CReserveTransfer> *)exportObjects[i])->object);
1526             }
1527             CReserveTransfer &curTransfer = *pCurTransfer;
1528
1529             //printf("currency transfer #%d:\n%s\n", i, curTransfer.ToUniValue().write(1,2).c_str());
1530             CCurrencyDefinition currencyDest = ConnectedChains.GetCachedCurrency(curTransfer.destCurrencyID);
1531
1532             if (!currencyDest.IsValid())
1533             {
1534                 printf("%s: invalid currency or currency not found %s\n", __func__, EncodeDestination(CIdentityID(curTransfer.destCurrencyID)).c_str());
1535                 LogPrintf("%s: invalid currency or currency not found %s\n", __func__, EncodeDestination(CIdentityID(curTransfer.destCurrencyID)).c_str());
1536                 return false;
1537             }
1538
1539             if (curTransfer.IsValid())
1540             {
1541                 CTxOut newOut;
1542                 numTransfers++;
1543
1544                 if (curTransfer.flags & curTransfer.FEE_OUTPUT)
1545                 {
1546                     feeOutputStart = true;
1547                     nFeeOutputs++;
1548                     numTransfers--;
1549
1550                     // if first fee output, calculate what they all should be
1551                     if (nFeeOutputs == 1)
1552                     {
1553                         feeOutputs = CCrossChainExport::CalculateExportFee(transferFees, numTransfers);
1554                     }
1555
1556                     //printf("%s: transferFees, numTransfers: %s, %d\n", __func__, transferFees.ToUniValue().write().c_str(), numTransfers);
1557                     //printf("%s: feeOutputs: %s\n", __func__, feeOutputs.ToUniValue().write().c_str());
1558
1559                     if (curTransfer.nValue > feeOutputs.valueMap[curTransfer.currencyID])
1560                     {
1561                         // TODO: we are going to allow rewriting of the fee out value for now
1562                         // to accomodate prior versions that calculated too much fee. we may or may not want to keep this
1563                         // behavior, in favor of the more restricted, refund-only rewriting that is
1564                         // commented out below
1565                         curTransfer.nValue = feeOutputs.valueMap[curTransfer.currencyID];
1566                         if (curTransfer.nValue == 0)
1567                         {
1568                             // this currency will not have a fee output at all
1569                             continue;
1570                         }
1571                         /*
1572                         // if this is a refund, we will adjust the output, otherwise, reject the transaction
1573                         if (importCurrencyState.IsRefunding())
1574                         {
1575                             curTransfer.nValue = feeOutputs.valueMap[curTransfer.currencyID];
1576                             if (curTransfer.nValue == 0)
1577                             {
1578                                 // this currency will not have a fee output at all
1579                                 continue;
1580                             }
1581                         }
1582                         else
1583                         {
1584                             // invalid
1585                             printf("%s: Too much fee taken by transfer in export %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1586                             LogPrintf("%s: Too much fee taken %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1587                             return false;
1588                         }
1589                         */
1590                     }
1591                     // erase so any repeats will be caught
1592                     feeOutputs.valueMap.erase(curTransfer.currencyID);
1593                 }
1594                 else if (feeOutputStart)
1595                 {
1596                     printf("%s: Invalid non-fee output after fee output %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1597                     LogPrintf("%s: Invalid non-fee output after fee output %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1598                     return false;
1599                 }
1600                 else
1601                 {
1602                     if (curTransfer.nFees < curTransfer.CalculateTransferFee(curTransfer.destination))
1603                     {
1604                         printf("%s: Incorrect fee sent with export %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1605                         LogPrintf("%s: Incorrect fee sent with export %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1606                         return false;
1607                     }
1608
1609                     if (curTransfer.currencyID == systemDestID && !(curTransfer.flags & (curTransfer.MINT_CURRENCY | curTransfer.PREALLOCATE)))
1610                     {
1611                         nativeIn += (curTransfer.nValue + curTransfer.nFees);
1612                     }
1613                     else
1614                     {
1615                         // when minting new currency or burning a fractional for conversion to a reserve,
1616                         // we only add fees
1617                         if ((curTransfer.flags & (curTransfer.MINT_CURRENCY | curTransfer.PREALLOCATE)))
1618                         {
1619                             nativeIn += curTransfer.nFees;
1620                         }
1621                         else
1622                         {
1623                             AddReserveInput(curTransfer.currencyID, curTransfer.nValue + curTransfer.nFees);
1624                         }
1625                     }
1626                     if (!(curTransfer.flags & (curTransfer.PRECONVERT | curTransfer.CONVERT)))
1627                     {
1628                         transferFees.valueMap[curTransfer.currencyID] += curTransfer.nFees;
1629                     }
1630
1631                     if (curTransfer.flags & curTransfer.PREALLOCATE)
1632                     {
1633                         // look up preallocation in the currency definition based on ID, add the correct amount to input
1634                         // and to the transfer for output
1635                         if (currencyDest.preAllocation.size() && !preAllocMap.size())
1636                         {
1637                             for (auto &onePreAlloc : currencyDest.preAllocation)
1638                             {
1639                                 preAllocMap.insert(onePreAlloc);
1640                             }
1641                             // TODO: this is where we should add percentage based pre-allocation calculations
1642                         }
1643
1644                         auto it = preAllocMap.find(GetDestinationID(TransferDestinationToDestination(curTransfer.destination)));
1645                         if (it == preAllocMap.end() || it->second != curTransfer.nValue)
1646                         {
1647                             printf("%s: Invalid preallocation transfer\n", __func__);
1648                             LogPrintf("%s: Invalid preallocation transfer\n", __func__);
1649                             return false;
1650                         }
1651                     }
1652                 }
1653
1654                 if (curTransfer.flags & curTransfer.PRECONVERT)
1655                 {
1656                     // first time through with preconvert, initialize the starting currency state
1657                     if (!initialCurrencyState.IsValid())
1658                     {
1659                         initialCurrencyState = ConnectedChains.GetCurrencyState(importCurrencyID, currencyDest.startBlock - 1);
1660                         if (!initialCurrencyState.IsValid())
1661                         {
1662                             printf("%s: Invalid currency for preconversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1663                             LogPrintf("%s: Invalid currency for preconversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1664                             return false;
1665                         }
1666                     }
1667
1668                     // get currency index
1669                     auto curIndexIt = currencyIndexMap.find(curTransfer.currencyID);
1670                     if (curIndexIt == currencyIndexMap.end())
1671                     {
1672                         printf("%s: Invalid currency for conversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1673                         LogPrintf("%s: Invalid currency for conversion %s\n", __func__, curTransfer.ToUniValue().write().c_str());
1674                         return false;
1675                     }
1676                     int curIdx = curIndexIt->second;
1677
1678                     // output the converted amount, minus fees, and generate a normal output that spends the net input of the import as native
1679                     // difference between all potential value out and what was taken unconverted as a fee in our fee output
1680                     CAmount preConversionFee = 0;
1681                     CAmount newConvertedFees = 0;
1682                     CAmount newCurrencyConverted = 0;
1683                     CAmount feesConverted = 0;
1684
1685                     preConversionFee = CalculateConversionFee(curTransfer.nValue);
1686                     if (preConversionFee > curTransfer.nValue)
1687                     {
1688                         preConversionFee = curTransfer.nValue;
1689                     }
1690                     CAmount valueOut = curTransfer.nValue - preConversionFee;
1691
1692                     if (!(curTransfer.flags & curTransfer.FEE_OUTPUT))
1693                     {
1694                         newCurrencyConverted = initialCurrencyState.ReserveToNativeRaw(valueOut, initialCurrencyState.conversionPrice[curIdx]);
1695                         CAmount totalReserveFee = preConversionFee + curTransfer.nFees;
1696
1697                         // see if fees should be paid in the new currency or not
1698                         if ((currencyDest.ChainOptions() & currencyDest.OPTION_FEESASRESERVE) || currencyDest.IsToken())
1699                         {
1700                             AddReserveConversionFees(curTransfer.currencyID, preConversionFee);
1701                             CAmount reserveIn = valueOut;
1702                             if (!carveOutSet)
1703                             {
1704                                 totalCarveOut = importCurrencyDef.GetTotalCarveOut();
1705                             }
1706                             if (totalCarveOut > 0 && totalCarveOut < SATOSHIDEN)
1707                             {
1708                                 CAmount newReserveIn = CCurrencyState::NativeToReserveRaw(reserveIn, SATOSHIDEN - totalCarveOut);
1709                                 totalCarveOuts.valueMap[curTransfer.currencyID] += reserveIn - newReserveIn;
1710                                 reserveIn = newReserveIn;
1711                             }
1712
1713                             if (curTransfer.currencyID != systemDestID)
1714                             {
1715                                 // if this is a fractional currency, everything but fees and carveouts stay in reserve deposit
1716                                 // else all that would be reserves is sent to chain ID
1717                                 if (!importCurrencyDef.IsFractional())
1718                                 {
1719                                     AddReserveOutput(curTransfer.currencyID, reserveIn);
1720                                     std::vector<CTxDestination> dests({CIdentityID(importCurrencyID)});
1721                                     CTokenOutput ro = CTokenOutput(curTransfer.currencyID, reserveIn);
1722                                     vOutputs.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro))));
1723                                 }
1724                             }
1725                             else
1726                             {
1727                                 newConvertedFees = totalReserveFee;
1728                                 totalReserveFee = 0;
1729                                 // leave it in reserve deposit
1730                                 if (!importCurrencyDef.IsFractional())
1731                                 {
1732                                     nativeOut += reserveIn;
1733                                     vOutputs.push_back(CTxOut(reserveIn, GetScriptForDestination(CIdentityID(importCurrencyID))));
1734                                 }
1735                             }
1736                         }
1737                         else
1738                         {
1739                             if (curTransfer.destCurrencyID == systemDestID)
1740                             {
1741                                 newConvertedFees = initialCurrencyState.ReserveToNativeRaw(totalReserveFee, initialCurrencyState.conversionPrice[curIdx]);
1742                                 AddReserveConversionFees(systemDestID, newConvertedFees);
1743                                 feesConverted = newConvertedFees;
1744                                 totalReserveFee = 0;
1745                             }
1746                             else if (curTransfer.currencyID == systemDestID)
1747                             {
1748                                 AddReserveConversionFees(curTransfer.currencyID, preConversionFee);
1749                                 newConvertedFees = totalReserveFee;
1750                                 totalReserveFee = 0;
1751                             }
1752                             else
1753                             {
1754                                 AddReserveConversionFees(curTransfer.currencyID, preConversionFee);
1755                             }
1756                         }
1757
1758                         // add reserve fees, if any as input funds to be taken by fee outputs
1759                         if (totalReserveFee)
1760                         {
1761                             transferFees.valueMap[curTransfer.currencyID] += totalReserveFee;
1762                         }
1763                         if (newConvertedFees)
1764                         {
1765                             transferFees.valueMap[systemDestID] += newConvertedFees;
1766                         }
1767                     }
1768                     else
1769                     {
1770                         // input comes from fees
1771                         newCurrencyConverted = initialCurrencyState.ReserveToNativeRaw(curTransfer.nValue, initialCurrencyState.conversionPrice[curIdx]);
1772                     }
1773
1774                     if (newCurrencyConverted | feesConverted)
1775                     {
1776                         preConvertedOutput.valueMap[curTransfer.currencyID] += newCurrencyConverted + feesConverted;
1777                         AddNativeOutConverted(curTransfer.currencyID, newCurrencyConverted + feesConverted);
1778                         if (curTransfer.destCurrencyID == systemDestID)
1779                         {
1780                             nativeOut += newCurrencyConverted;
1781                             newOut = CTxOut(newCurrencyConverted, GetScriptForDestination(TransferDestinationToDestination(curTransfer.destination)));
1782                         }
1783                         else
1784                         {
1785                             AddReserveOutConverted(curTransfer.destCurrencyID, newCurrencyConverted + feesConverted);
1786                             AddReserveOutput(curTransfer.destCurrencyID, newCurrencyConverted);
1787                             std::vector<CTxDestination> dests = std::vector<CTxDestination>({TransferDestinationToDestination(curTransfer.destination)});
1788                             CTokenOutput ro = CTokenOutput(curTransfer.destCurrencyID, newCurrencyConverted);
1789                             newOut = CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro)));
1790                         }
1791                     }
1792                 }
1793                 else if (curTransfer.flags & curTransfer.CONVERT)
1794                 {
1795                     if (curTransfer.currencyID == curTransfer.destCurrencyID)
1796                     {
1797                         printf("%s: Conversion does not specify two currencies\n", __func__);
1798                         LogPrintf("%s: Conversion does not specify two currencies\n", __func__);
1799                         return false;
1800                     }
1801
1802                     // either the source or destination must be a reserve currency of the other fractional currency
1803                     // if destination is a fractional currency of a reserve, we will mint currency
1804                     // if not, we will burn currency
1805                     bool toFractional = importCurrencyID == curTransfer.destCurrencyID &&
1806                                         currencyDest.IsFractional() && 
1807                                         currencyIndexMap.count(curTransfer.currencyID);
1808
1809                     CCurrencyDefinition sourceCurrency = ConnectedChains.GetCachedCurrency(curTransfer.currencyID);
1810
1811                     if (!sourceCurrency.IsValid())
1812                     {
1813                         printf("%s: Currency specified for conversion not found\n", __func__);
1814                         LogPrintf("%s: Currency specified for conversion not found\n", __func__);
1815                         return false;
1816                     }
1817
1818                     if (!(toFractional || 
1819                         (importCurrencyID == curTransfer.currencyID &&
1820                          sourceCurrency.IsFractional() &&
1821                          currencyIndexMap.count(curTransfer.destCurrencyID))))
1822                     {
1823                         printf("%s: Conversion must be between a fractional currency and one of its reserves\n", __func__);
1824                         LogPrintf("%s: Conversion must be between a fractional currency and one of its reserves\n", __func__);
1825                         return false;
1826                     }
1827
1828                     CCurrencyDefinition &fractionalCurrency = toFractional ? currencyDest : sourceCurrency;
1829                     CCurrencyDefinition &reserveCurrency = toFractional ? sourceCurrency : currencyDest;
1830                     int reserveIdx = currencyIndexMap[reserveCurrency.GetID()];
1831
1832                     assert(fractionalCurrency.IsValid() && 
1833                            reserveCurrency.IsValid() && 
1834                            fractionalCurrency.currencies[reserveIdx] == reserveCurrency.GetID());
1835
1836                     // now, we know that we are converting from the source currency to the
1837                     // destination currency and also that one of them is a reserve of the other
1838                     // we convert using the provided currency state, and we update the currency 
1839                     // state to include newly minted or burned currencies.
1840                     CAmount valueOut;
1841                     CAmount preConversionFee = 0;
1842                     CAmount newConvertedFees = 0;
1843                     CAmount newCurrencyConverted = 0;
1844                     CAmount feesConverted = 0;
1845                     if (!(curTransfer.flags & curTransfer.FEE_OUTPUT))
1846                     {
1847                         preConversionFee = CalculateConversionFee(curTransfer.nValue);
1848                         if (preConversionFee > curTransfer.nValue)
1849                         {
1850                             preConversionFee = curTransfer.nValue;
1851                         }
1852                         valueOut = curTransfer.nValue - preConversionFee;
1853
1854                         if (toFractional)
1855                         {
1856                             reserveConverted.valueMap[curTransfer.currencyID] += valueOut;
1857                             newCurrencyConverted = importCurrencyState.ReserveToNativeRaw(valueOut, importCurrencyState.conversionPrice[reserveIdx]);
1858                         }
1859                         else
1860                         {
1861                             fractionalConverted.valueMap[curTransfer.destCurrencyID] += valueOut;
1862                             newCurrencyConverted = importCurrencyState.NativeToReserveRaw(valueOut, importCurrencyState.conversionPrice[reserveIdx]);
1863                         }
1864
1865                         CAmount totalSourceFee = preConversionFee + curTransfer.nFees;
1866
1867                         // see if fees should be converted or not
1868                         if ((currencyDest.ChainOptions() & currencyDest.OPTION_FEESASRESERVE) || importCurrencyDef.IsToken())
1869                         {
1870                             AddReserveConversionFees(curTransfer.currencyID, preConversionFee);
1871                             if (curTransfer.currencyID == systemDestID)
1872                             {
1873                                 nativeConversionFees += preConversionFee;
1874                             }
1875                         }
1876                         else
1877                         {
1878                             // convert to fractional reserve if toFractional, and native currency of chain if not and fractional is not native
1879                             if (toFractional)
1880                             {
1881                                 reserveConverted.valueMap[curTransfer.currencyID] += totalSourceFee;
1882                                 newConvertedFees = importCurrencyState.ReserveToNativeRaw(totalSourceFee, importCurrencyState.conversionPrice[reserveIdx]);
1883                                 AddReserveConversionFees(curTransfer.destCurrencyID, newConvertedFees);
1884                                 feesConverted = newConvertedFees;
1885                                 totalSourceFee = 0;
1886                             }
1887                             else
1888                             {
1889                                 fractionalConverted.valueMap[curTransfer.currencyID] += totalSourceFee;
1890                                 AddReserveConversionFees(curTransfer.currencyID, preConversionFee);
1891                             }
1892                         }
1893
1894                         // add reserve fees, if any as input funds to validate fee outputs
1895                         if (totalSourceFee)
1896                         {
1897                             transferFees.valueMap[curTransfer.currencyID] += totalSourceFee;
1898                         }
1899                         if (newConvertedFees)
1900                         {
1901                             transferFees.valueMap[curTransfer.destCurrencyID] += newConvertedFees;
1902                         }
1903                     }
1904                     else
1905                     {
1906                         valueOut = curTransfer.nValue;
1907
1908                         if (toFractional)
1909                         {
1910                             reserveConverted.valueMap[curTransfer.currencyID] += valueOut;
1911                             newCurrencyConverted = importCurrencyState.ReserveToNativeRaw(valueOut, importCurrencyState.conversionPrice[reserveIdx]);
1912                         }
1913                         else
1914                         {
1915                             fractionalConverted.valueMap[curTransfer.destCurrencyID] += valueOut;
1916                             newCurrencyConverted = importCurrencyState.NativeToReserveRaw(valueOut, importCurrencyState.conversionPrice[reserveIdx]);
1917                         }
1918                     }
1919
1920                     if (newCurrencyConverted | feesConverted)
1921                     {
1922                         if (toFractional)
1923                         {
1924                             AddNativeOutConverted(curTransfer.currencyID, newCurrencyConverted + feesConverted);
1925                             if (curTransfer.destCurrencyID == systemDestID)
1926                             {
1927                                 nativeOut += newCurrencyConverted;
1928                             }
1929                             else
1930                             {
1931                                 AddReserveOutConverted(curTransfer.destCurrencyID, newCurrencyConverted + feesConverted);
1932                                 AddReserveOutput(curTransfer.destCurrencyID, newCurrencyConverted);
1933                             }
1934                         }
1935                         else
1936                         {
1937                             AddReserveOutConverted(curTransfer.destCurrencyID, newCurrencyConverted + feesConverted);
1938                             if (curTransfer.destCurrencyID == systemDestID)
1939                             {
1940                                 nativeOut += newCurrencyConverted;
1941                             }
1942                             else
1943                             {
1944                                 AddReserveOutput(curTransfer.destCurrencyID, newCurrencyConverted);
1945                             }
1946
1947                             // burn the input fractional currency
1948                             AddNativeOutConverted(curTransfer.currencyID, -valueOut);
1949                         }
1950
1951                         if ((newCurrencyConverted | feesConverted) && curTransfer.destCurrencyID == systemDestID)
1952                         {
1953                             newOut = CTxOut(newCurrencyConverted, GetScriptForDestination(TransferDestinationToDestination(curTransfer.destination)));
1954                         }
1955                         else if (newCurrencyConverted | feesConverted)
1956                         {
1957                             std::vector<CTxDestination> dests = std::vector<CTxDestination>({TransferDestinationToDestination(curTransfer.destination)});
1958                             CTokenOutput ro = CTokenOutput(curTransfer.destCurrencyID, newCurrencyConverted);
1959                             newOut = CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro)));
1960                         }
1961                     }
1962
1963                     /*
1964                     // emit a reserve exchange output
1965                     cp = CCinit(&CC, EVAL_RESERVE_EXCHANGE);
1966                     CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
1967
1968                     std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), TransferDestinationToDestination(curTransfer.destination)});
1969                     CAmount conversionFees = CalculateConversionFee(curTransfer.nValue);
1970                     AddReserveConversionFees(curTransfer.currencyID, conversionFees);
1971                     AddReserveOutConverted(curTransfer.currencyID, curTransfer.nValue - conversionFees);
1972                     AddReserveOutput(curTransfer.currencyID, curTransfer.nValue);
1973
1974                     CReserveExchange rex = CReserveExchange(CReserveExchange::VALID, curTransfer.currencyID, curTransfer.nValue);
1975                     newOut = CTxOut(0, MakeMofNCCScript(CConditionObj<CReserveExchange>(EVAL_RESERVE_EXCHANGE, dests, 1, &rex)));
1976                     */
1977                 }
1978                 else if ((curTransfer.flags & curTransfer.SEND_BACK) && 
1979                          curTransfer.nValue >= (curTransfer.DEFAULT_PER_STEP_FEE << 2) && 
1980                          curTransfer.currencyID == nativeSourceCurrencyID)
1981                 {
1982                     // emit a reserve exchange output
1983                     cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
1984                     CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
1985
1986                     // transfer it back to the source chain and to our address
1987                     std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), TransferDestinationToDestination(curTransfer.destination)});
1988                     
1989                     // naturally compress a full ID to an ID destination, since it is going back where it came from
1990                     CTxDestination sendBackAddr = TransferDestinationToDestination(curTransfer.destination);
1991
1992                     CAmount fees = curTransfer.CalculateFee(curTransfer.flags, curTransfer.nValue).valueMap.begin()->second;
1993
1994                     CReserveTransfer rt = CReserveTransfer(CReserveExchange::VALID, 
1995                                                            curTransfer.currencyID, 
1996                                                            curTransfer.nValue - fees, 
1997                                                            fees, 
1998                                                            curTransfer.currencyID,
1999                                                            DestinationToTransferDestination(sendBackAddr));
2000
2001                     std::vector<CTxDestination> indexDests({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)),
2002                                                             CKeyID(currencyDest.systemID == ConnectedChains.ThisChain().GetID() ? 
2003                                                                    currencyDest.GetID() : 
2004                                                                    currencyDest.systemID)});
2005                     CScript sendBackScript = MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), 
2006                                                               &indexDests);
2007
2008                     // if this is sending back to the same chain as its native currency, make it a native output
2009                     if (systemDestID == nativeSourceCurrencyID)
2010                     {
2011                         nativeOut += curTransfer.nValue;
2012                         newOut = CTxOut(curTransfer.nValue, sendBackScript);                    
2013                     }
2014                     else
2015                     {
2016                         AddReserveOutput(curTransfer.currencyID, curTransfer.nValue);
2017                         newOut = CTxOut(0, sendBackScript);                    
2018                     }
2019                 }
2020                 else
2021                 {
2022                     // if we are supposed to burn a currency, it must be the import currency, and it
2023                     // is removed from the supply, which would change all calculations for price
2024                     if (curTransfer.flags & (curTransfer.BURN_CHANGE_PRICE | curTransfer.BURN_CHANGE_WEIGHT))
2025                     {
2026                         // if the source is fractional currency, it is burned
2027                         if (curTransfer.currencyID != importCurrencyID || !(importCurrencyDef.IsFractional() || importCurrencyDef.IsToken()))
2028                         {
2029                             CCurrencyDefinition sourceCurrency = ConnectedChains.GetCachedCurrency(curTransfer.currencyID);
2030                             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());
2031                             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());
2032                             return false;
2033                         }
2034                         if (curTransfer.flags & curTransfer.BURN_CHANGE_WEIGHT)
2035                         {
2036                             printf("%s: burning %s to change weight is not supported\n", __func__, importCurrencyDef.name.c_str());
2037                             LogPrintf("%s: burning %s to change weight is not supported\n", __func__, importCurrencyDef.name.c_str());
2038                             return false;
2039                         }
2040                         // burn the input fractional currency
2041                         AddNativeOutConverted(curTransfer.currencyID, -curTransfer.nValue);
2042                         burnedChangePrice += curTransfer.nValue;
2043                     }
2044                     else if (systemDestID == curTransfer.destCurrencyID)
2045                     {
2046                         nativeOut += curTransfer.nValue;
2047                         newOut = CTxOut(curTransfer.nValue, GetScriptForDestination(TransferDestinationToDestination(curTransfer.destination)));
2048                     }
2049                     else
2050                     {
2051                         // generate a reserve output of the amount indicated, less fees
2052                         // we will send using a reserve output, fee will be paid through coinbase by converting from reserve or not, depending on currency settings
2053                         std::vector<CTxDestination> dests = std::vector<CTxDestination>({TransferDestinationToDestination(curTransfer.destination)});
2054                         CTokenOutput ro = CTokenOutput(curTransfer.destCurrencyID, curTransfer.nValue);
2055
2056                         // if this is a minting of currency
2057                         // this is used for both pre-allocation and also centrally, algorithmically, or externally controlled currencies
2058                         if ((curTransfer.flags & (curTransfer.MINT_CURRENCY | curTransfer.PREALLOCATE)) && curTransfer.destCurrencyID == importCurrencyID)
2059                         {
2060                             // pre-allocation is accounted for outside of this
2061                             // minting is emitted in new currency state
2062                             if (curTransfer.flags & curTransfer.MINT_CURRENCY)
2063                             {
2064                                 totalMinted += ro.nValue;
2065                             }
2066                             AddNativeOutConverted(curTransfer.destCurrencyID, ro.nValue);
2067                             if (curTransfer.destCurrencyID != systemDestID)
2068                             {
2069                                 AddReserveOutConverted(curTransfer.destCurrencyID, ro.nValue);
2070                             }
2071                         }
2072                         AddReserveOutput(curTransfer.destCurrencyID, ro.nValue);
2073                         newOut = CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro)));
2074                     }
2075                 }
2076                 if (newOut.nValue < 0)
2077                 {
2078                     // if we get here, we have absorbed the entire transfer
2079                     LogPrintf("%s: skip creating output for import to %s\n", __func__, currencyDest.name.c_str());
2080                 }
2081                 else
2082                 {
2083                     vOutputs.push_back(newOut);
2084                 }
2085             }
2086             else
2087             {
2088                 printf("%s: Invalid reserve transfer on export\n", __func__);
2089                 LogPrintf("%s: Invalid reserve transfer on export\n", __func__);
2090                 return false;
2091             }
2092         }
2093     }
2094
2095     if ((totalCarveOuts = totalCarveOuts.CanonicalMap()).valueMap.size())
2096     {
2097         // add carveout outputs
2098         for (auto &oneCur : totalCarveOuts.valueMap)
2099         {
2100             // if we are creating a reserve import for native currency, it must be spent from native inputs on the destination system
2101             if (oneCur.first == systemDestID)
2102             {
2103                 nativeOut += oneCur.second;
2104                 CTxOut(oneCur.second, GetScriptForDestination(CIdentityID(importCurrencyID)));
2105             }
2106             else
2107             {
2108                 // generate a reserve output of the amount indicated, less fees
2109                 // we will send using a reserve output, fee will be paid through coinbase by converting from reserve or not, depending on currency settings
2110                 std::vector<CTxDestination> dests = std::vector<CTxDestination>({CIdentityID(importCurrencyID)});
2111                 CTokenOutput ro = CTokenOutput(oneCur.first, oneCur.second);
2112                 AddReserveOutput(oneCur.first, oneCur.second);
2113                 CTxOut(0, MakeMofNCCScript(CConditionObj<CTokenOutput>(EVAL_RESERVE_OUTPUT, dests, 1, &ro)));
2114             }
2115         }
2116     }
2117
2118     // remove burned currency from supply
2119     if (burnedChangePrice > 0)
2120     {
2121         if (!(burnedChangePrice <= newCurrencyState.supply))
2122         {
2123             printf("%s: Invalid burn amount %ld\n", __func__, burnedChangePrice);
2124             LogPrintf("%s: Invalid burn amount %ld\n", __func__, burnedChangePrice);
2125             return false;
2126         }
2127         newCurrencyState.supply -= burnedChangePrice;
2128     }
2129
2130     if (reserveConverted.CanonicalMap().valueMap.size() || fractionalConverted.CanonicalMap().valueMap.size())
2131     {
2132         if (importCurrencyDef.IsFractional())
2133         {
2134             CCurrencyState dummyCurState;
2135             newCurrencyState.conversionPrice = 
2136                 importCurrencyState.ConvertAmounts(reserveConverted.AsCurrencyVector(importCurrencyState.currencies),
2137                                                     fractionalConverted.AsCurrencyVector(importCurrencyState.currencies),
2138                                                     dummyCurState);
2139         }
2140     }
2141     std::vector<CAmount> vResConverted = reserveConverted.AsCurrencyVector(newCurrencyState.currencies);
2142     std::vector<CAmount> vResOutConverted = ReserveOutConvertedMap().AsCurrencyVector(newCurrencyState.currencies);
2143     std::vector<CAmount> vFracConverted = fractionalConverted.AsCurrencyVector(newCurrencyState.currencies);
2144     std::vector<CAmount> vFracOutConverted = (NativeOutConvertedMap() - preConvertedOutput).AsCurrencyVector(newCurrencyState.currencies);
2145
2146     for (int i = 0; i < newCurrencyState.currencies.size(); i++)
2147     {
2148         newCurrencyState.reserveIn[i] = vResConverted[i];
2149         newCurrencyState.reserveOut[i] = vResOutConverted[i];
2150         newCurrencyState.reserves[i] += vResConverted[i] - vResOutConverted[i];
2151         newCurrencyState.nativeIn[i] = vFracConverted[i];
2152         newCurrencyState.supply += vFracOutConverted[i];
2153     }
2154
2155     if (totalMinted)
2156     {
2157         newCurrencyState.UpdateWithEmission(totalMinted);
2158     }
2159
2160     // now, pull out all fractional data and sort out native vs. fractional
2161     if (currencies.count(systemDestID))
2162     {
2163         CReserveInOuts fractionalInOuts = currencies[systemDestID];
2164         newCurrencyState.nativeConversionFees = fractionalInOuts.reserveConversionFees;
2165     }
2166     newCurrencyState.conversionFees = ReserveConversionFeesMap().AsCurrencyVector(newCurrencyState.currencies);
2167     newCurrencyState.fees = transferFees.AsCurrencyVector(newCurrencyState.currencies);
2168
2169     // double check that the export fee taken as the fee output matches the export fee that should have been taken
2170     CCurrencyValueMap ReserveInputs;
2171     CCurrencyValueMap ReserveOutputs;
2172     CAmount systemOutConverted = 0;
2173     for (auto &oneInOut : currencies)
2174     {
2175         ReserveInputs.valueMap[importCurrencyID] += oneInOut.second.nativeOutConverted;
2176         if (oneInOut.first == systemDestID)
2177         {
2178             if (systemDestID == importCurrencyID)
2179             {
2180                 systemOutConverted += oneInOut.second.nativeOutConverted;
2181             }
2182             else
2183             {
2184                 systemOutConverted += oneInOut.second.reserveOutConverted;
2185             }
2186         }
2187         if (oneInOut.second.reserveIn || oneInOut.second.reserveOutConverted)
2188         {
2189             ReserveInputs.valueMap[oneInOut.first] = oneInOut.second.reserveIn + oneInOut.second.reserveOutConverted;
2190         }
2191         if (oneInOut.second.reserveOut)
2192         {
2193             ReserveOutputs.valueMap[oneInOut.first] = oneInOut.second.reserveOut;
2194         }
2195     }
2196     if (nativeIn || systemOutConverted)
2197     {
2198         ReserveInputs.valueMap[importCurrencyDef.systemID] = nativeIn + systemOutConverted;
2199     }
2200     if (nativeOut)
2201     {
2202         ReserveOutputs.valueMap[importCurrencyDef.systemID] = nativeOut;
2203     }
2204     CCrossChainExport ccx(systemDestID, numTransfers, ReserveInputs, transferFees);
2205     //printf("ReserveInputs: %s\nReserveOutputs: %s\nReserveInputs - ReserveOutputs: %s\n", ReserveInputs.ToUniValue().write(1,2).c_str(), ReserveOutputs.ToUniValue().write(1,2).c_str(), (ReserveInputs - ReserveOutputs).ToUniValue().write(1,2).c_str());
2206     if (ReserveInputs - ReserveOutputs < ccx.CalculateImportFee())
2207     {
2208         printf("%s: Too much fee taken by export, ReserveInputs: %s\nReserveOutputs: %s\nccx.CalculateImportFee(): %s\nccx: %s\n", __func__,
2209                 ReserveInputs.ToUniValue().write(1,2).c_str(), 
2210                 ReserveOutputs.ToUniValue().write(1,2).c_str(), 
2211                 ccx.CalculateImportFee().ToUniValue().write(1,2).c_str(), 
2212                 ccx.ToUniValue().write(1,2).c_str());
2213         LogPrintf("%s: Too much fee taken by export\n", __func__);
2214         return false;
2215     }
2216     return true;
2217 }
2218
2219 CMutableTransaction &CReserveTransactionDescriptor::AddConversionInOuts(CMutableTransaction &conversionTx, std::vector<CInputDescriptor> &conversionInputs, const CCurrencyValueMap &_exchangeRates, const CCurrencyState *pCurrencyState) const
2220 {
2221     if (!IsReserveExchange() || IsFillOrKillFail())
2222     {
2223         return conversionTx;
2224     }
2225
2226     bool noExchangeRate = false;
2227     CCurrencyState dummy;
2228     const CCurrencyState &currencyState = pCurrencyState ? *pCurrencyState : dummy;
2229
2230     // set exchange rates as well as we can, either from explicit rates or currency state if possible
2231     CCurrencyValueMap __exchangeRates;
2232     const CCurrencyValueMap *pExchangeRates = &__exchangeRates;
2233     if (_exchangeRates.valueMap.size() != 0)
2234     {
2235         pExchangeRates = &_exchangeRates;
2236     }
2237     else
2238     {
2239         if (pCurrencyState && currencyState.IsFractional())
2240         {
2241             __exchangeRates = CCurrencyValueMap(currencyState.currencies, currencyState.PricesInReserve());
2242         }
2243         else
2244         {
2245             noExchangeRate = true;
2246         }
2247     }
2248     const CCurrencyValueMap &exchangeRates = *pExchangeRates;
2249
2250     CAmount nativeFeesLeft = nativeConversionFees;
2251     for (auto &oneEntry : exchangeRates.valueMap)
2252     {
2253         auto it = currencies.find(oneEntry.first);
2254         if (it == currencies.end())
2255         {
2256             LogPrintf("%s: invalid conversion with no exchange rate, currency: %s\n", __func__, EncodeDestination(CIdentityID(oneEntry.first)).c_str());
2257         }
2258         else
2259         {
2260             nativeFeesLeft += currencyState.ReserveToNativeRaw(it->second.reserveConversionFees, oneEntry.second);
2261         }
2262     }
2263
2264     uint256 txHash = ptx->GetHash();
2265
2266     for (auto &indexRex : vRex)
2267     {
2268         COptCCParams p;
2269         ptx->vout[indexRex.first].scriptPubKey.IsPayToCryptoCondition(p);
2270
2271         CCcontract_info CC;
2272         CCcontract_info *cp;
2273
2274         CAmount fee = CalculateConversionFee(indexRex.second.nValue);
2275         CAmount amount = indexRex.second.nValue - fee;
2276         CAmount nativeFee, nativeAmount;
2277
2278         auto rateIt = exchangeRates.valueMap.find(indexRex.second.currencyID);
2279         if (rateIt == exchangeRates.valueMap.end())
2280         {
2281             continue;
2282         }
2283
2284         CAmount exchangeRate = rateIt->second;
2285
2286         // if already native fees, don't convert, otherwise, do
2287         if (!(indexRex.second.flags & indexRex.second.TO_RESERVE))
2288         {
2289             nativeFee = fee;
2290             nativeAmount = ptx->vout[indexRex.first].nValue;
2291             amount = currencyState.NativeToReserveRaw(nativeAmount, exchangeRate);
2292             fee = currencyState.NativeToReserveRaw(nativeFee, exchangeRate);
2293         }
2294         else
2295         {
2296             nativeFee = currencyState.ReserveToNativeRaw(fee, exchangeRate);
2297             nativeAmount = currencyState.ReserveToNativeRaw(amount, exchangeRate);
2298         }
2299
2300         if (nativeFee > nativeFeesLeft)
2301         {
2302             nativeFee = nativeFeesLeft;
2303         }
2304         nativeFeesLeft -= nativeFee;
2305
2306         // add input...
2307         conversionTx.vin.push_back(CTxIn(txHash, indexRex.first, CScript()));
2308
2309         // ... and input descriptor. we leave the CTxIn empty and use the one in the corresponding input, using the input descriptor for only
2310         // script and value
2311         conversionInputs.push_back(CInputDescriptor(ptx->vout[indexRex.first].scriptPubKey, ptx->vout[indexRex.first].nValue, CTxIn()));
2312
2313         // if we should emit a reserve transfer or normal reserve output, sending output only occurs when converting from
2314         // native to a reserve currency
2315         if (indexRex.second.flags & indexRex.second.SEND_OUTPUT && 
2316             indexRex.second.flags & indexRex.second.TO_RESERVE &&
2317             nativeAmount > (CReserveTransfer::DEFAULT_PER_STEP_FEE << 1) << 1)
2318         {
2319             cp = CCinit(&CC, EVAL_RESERVE_TRANSFER);
2320             CPubKey pk = CPubKey(ParseHex(CC.CChexstr));
2321
2322             // send the entire amount to a reserve transfer output of the controller
2323             const auto &reserveCurrencyIt = ConnectedChains.ReserveCurrencies().find(indexRex.second.currencyID);
2324             if (reserveCurrencyIt != ConnectedChains.ReserveCurrencies().end())
2325             {
2326                 std::vector<CTxDestination> dests = std::vector<CTxDestination>({pk.GetID(), p.vKeys[0]});
2327
2328                 // create the transfer output with the converted amount less fees
2329                 CReserveTransfer rt((uint32_t)CReserveTransfer::VALID,
2330                                     indexRex.second.currencyID,
2331                                     amount - (CReserveTransfer::DEFAULT_PER_STEP_FEE << 1),
2332                                     CReserveTransfer::DEFAULT_PER_STEP_FEE << 1,
2333                                     reserveCurrencyIt->second.systemID,
2334                                     CTransferDestination(p.vKeys[0].which(), GetDestinationBytes(p.vKeys[0])));
2335
2336                 // cast object to the most derived class to avoid template errors to a least derived class
2337                 CTxDestination rtIndexDest(CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)));
2338                 std::vector<CTxDestination> indexDests({CKeyID(ConnectedChains.ThisChain().GetConditionID(EVAL_RESERVE_TRANSFER)),
2339                                                         CKeyID(reserveCurrencyIt->second.systemID == ConnectedChains.ThisChain().GetID() ? 
2340                                                                reserveCurrencyIt->second.GetID() : 
2341                                                                reserveCurrencyIt->second.systemID)});
2342                 conversionTx.vout.push_back(CTxOut(0, MakeMofNCCScript(CConditionObj<CReserveTransfer>(EVAL_RESERVE_TRANSFER, dests, 1, &rt), 
2343                                                                        &rtIndexDest)));
2344             }
2345         }
2346         else if (indexRex.second.flags & indexRex.second.TO_RESERVE)
2347         {
2348             // send the net amount to the indicated destination, which is the first entry in the destinations of the original reserve/exchange by protocol
2349             std::vector<CTxDestination> dests = std::vector<CTxDestination>({p.vKeys[0]});
2350
2351             // create the output with the unconverted amount less fees
2352             CTokenOutput ro(indexRex.second.currencyID, amount);
2353
2354             conversionTx.vout.push_back(MakeCC1ofAnyVout(EVAL_RESERVE_OUTPUT, 0, dests, ro));
2355         }
2356         else
2357         {
2358             // convert amount to native from reserve and send as normal output
2359             amount = currencyState.ReserveToNative(amount, exchangeRates.valueMap.find(indexRex.second.currencyID)->second);
2360             conversionTx.vout.push_back(CTxOut(amount, GetScriptForDestination(p.vKeys[0])));
2361         }
2362     }
2363     return conversionTx;
2364 }
2365
2366 CCurrencyValueMap CReserveTransactionDescriptor::ReserveInputMap() const
2367 {
2368     CCurrencyValueMap retVal;
2369     for (auto &oneInOut : currencies)
2370     {
2371         if (oneInOut.second.reserveIn)
2372         {
2373             retVal.valueMap[oneInOut.first] = oneInOut.second.reserveIn;
2374         }
2375     }
2376     return retVal;
2377 }
2378
2379 CCurrencyValueMap CReserveTransactionDescriptor::ReserveOutputMap() const
2380 {
2381     CCurrencyValueMap retVal;
2382     for (auto &oneInOut : currencies)
2383     {
2384         if (oneInOut.second.reserveOut)
2385         {
2386             retVal.valueMap[oneInOut.first] = oneInOut.second.reserveOut;
2387         }
2388     }
2389     return retVal;
2390 }
2391
2392 CCurrencyValueMap CReserveTransactionDescriptor::ReserveOutConvertedMap() const
2393 {
2394     CCurrencyValueMap retVal;
2395     for (auto &oneInOut : currencies)
2396     {
2397         if (oneInOut.second.reserveOutConverted)
2398         {
2399             retVal.valueMap[oneInOut.first] = oneInOut.second.reserveOutConverted;
2400         }
2401     }
2402     return retVal;
2403 }
2404
2405 CCurrencyValueMap CReserveTransactionDescriptor::NativeOutConvertedMap() const
2406 {
2407     CCurrencyValueMap retVal;
2408     for (auto &oneInOut : currencies)
2409     {
2410         if (oneInOut.second.nativeOutConverted)
2411         {
2412             retVal.valueMap[oneInOut.first] = oneInOut.second.nativeOutConverted;
2413         }
2414     }
2415     return retVal;
2416 }
2417
2418 CCurrencyValueMap CReserveTransactionDescriptor::ReserveConversionFeesMap() const
2419 {
2420     CCurrencyValueMap retVal;
2421     for (auto &oneInOut : currencies)
2422     {
2423         if (oneInOut.second.reserveConversionFees)
2424         {
2425             retVal.valueMap[oneInOut.first] = oneInOut.second.reserveConversionFees;
2426         }
2427     }
2428     return retVal;
2429 }
2430
2431 std::vector<CAmount> CReserveTransactionDescriptor::ReserveInputVec(const CCurrencyState &cState) const
2432 {
2433     std::vector<CAmount> retVal(cState.currencies.size());
2434     std::map<uint160, int> curMap = cState.GetReserveMap();
2435     for (auto &oneInOut : currencies)
2436     {
2437         retVal[curMap[oneInOut.first]] = oneInOut.second.reserveIn;
2438     }
2439     return retVal;
2440 }
2441
2442 std::vector<CAmount> CReserveTransactionDescriptor::ReserveOutputVec(const CCurrencyState &cState) const
2443 {
2444     std::vector<CAmount> retVal(cState.currencies.size());
2445     std::map<uint160, int> curMap = cState.GetReserveMap();
2446     for (auto &oneInOut : currencies)
2447     {
2448         retVal[curMap[oneInOut.first]] = oneInOut.second.reserveOut;
2449     }
2450     return retVal;
2451 }
2452
2453 std::vector<CAmount> CReserveTransactionDescriptor::ReserveOutConvertedVec(const CCurrencyState &cState) const
2454 {
2455     std::vector<CAmount> retVal(cState.currencies.size());
2456     std::map<uint160, int> curMap = cState.GetReserveMap();
2457     for (auto &oneInOut : currencies)
2458     {
2459         retVal[curMap[oneInOut.first]] = oneInOut.second.reserveOutConverted;
2460     }
2461     return retVal;
2462 }
2463
2464 std::vector<CAmount> CReserveTransactionDescriptor::NativeOutConvertedVec(const CCurrencyState &cState) const
2465 {
2466     std::vector<CAmount> retVal(cState.currencies.size());
2467     std::map<uint160, int> curMap = cState.GetReserveMap();
2468     for (auto &oneInOut : currencies)
2469     {
2470         retVal[curMap[oneInOut.first]] = oneInOut.second.nativeOutConverted;
2471     }
2472     return retVal;
2473 }
2474
2475 std::vector<CAmount> CReserveTransactionDescriptor::ReserveConversionFeesVec(const CCurrencyState &cState) const
2476 {
2477     std::vector<CAmount> retVal(cState.currencies.size());
2478     std::map<uint160, int> curMap = cState.GetReserveMap();
2479     for (auto &oneInOut : currencies)
2480     {
2481         retVal[curMap[oneInOut.first]] = oneInOut.second.reserveConversionFees;
2482     }
2483     return retVal;
2484 }
2485
2486 // this should be done no more than once to prepare a currency state to be updated to the next state
2487 // emission occurs for a block before any conversion or exchange and that impact on the currency state is calculated
2488 CCurrencyState &CCurrencyState::UpdateWithEmission(CAmount toEmit)
2489 {
2490     initialSupply = supply;
2491     emitted = 0;
2492
2493     // if supply is 0, reserve must be zero, and we cannot function as a reserve currency
2494     if (!IsFractional() || supply <= 0 || CCurrencyValueMap(currencies, reserves) <= CCurrencyValueMap())
2495     {
2496         if (supply < 0)
2497         {
2498             emitted = supply = toEmit;
2499         }
2500         else
2501         {
2502             emitted = toEmit;
2503             supply += toEmit;
2504         }
2505         return *this;
2506     }
2507
2508     if (toEmit)
2509     {
2510         // first determine current ratio by adding up all currency weights
2511         CAmount InitialRatio = 0;
2512         for (auto weight : weights)
2513         {
2514             InitialRatio += weight;
2515         }
2516
2517         // to balance rounding with truncation, we statistically add a satoshi to the initial ratio
2518         static arith_uint256 bigSatoshi(SATOSHIDEN);
2519         arith_uint256 bigInitial(InitialRatio);
2520         arith_uint256 bigEmission(toEmit);
2521         arith_uint256 bigSupply(supply);
2522
2523         arith_uint256 bigScratch = (bigInitial * bigSupply * bigSatoshi) / (bigSupply + bigEmission);
2524         arith_uint256 bigRatio = bigScratch / bigSatoshi;
2525         // cap ratio at 1
2526         if (bigRatio >= bigSatoshi)
2527         {
2528             bigScratch = arith_uint256(SATOSHIDEN) * arith_uint256(SATOSHIDEN);
2529             bigRatio = bigSatoshi;
2530         }
2531
2532         int64_t newRatio = bigRatio.GetLow64();
2533         int64_t remainder = (bigScratch - (bigRatio * SATOSHIDEN)).GetLow64();
2534         // form of bankers rounding, if odd, round up at half, if even, round down at half
2535         if (remainder > (SATOSHIDEN >> 1) || (remainder == (SATOSHIDEN >> 1) && newRatio & 1))
2536         {
2537             newRatio += 1;
2538         }
2539
2540         // now, we must update all weights accordingly, based on the new, total ratio, by dividing the total among all the
2541         // weights, according to their current relative weight. because this also can be a source of rounding error, we will
2542         // distribute any modulus excess randomly among the currencies
2543         std::vector<CAmount> extraWeight(currencies.size());
2544         arith_uint256 bigRatioDelta(InitialRatio - newRatio);
2545         CAmount totalUpdates = 0;
2546
2547         for (auto &weight : weights)
2548         {
2549             CAmount weightDelta = (bigRatioDelta * arith_uint256(weight) / bigSatoshi).GetLow64();
2550             weight -= weightDelta;
2551             totalUpdates += weightDelta;
2552         }
2553
2554         CAmount updateExtra = (InitialRatio - newRatio) - totalUpdates;
2555
2556         // if we have any extra, distribute it evenly and any mod, both deterministically and pseudorandomly
2557         if (updateExtra)
2558         {
2559             CAmount forAll = updateExtra / currencies.size();
2560             CAmount forSome = updateExtra % currencies.size();
2561
2562             // get deterministic seed for linear congruential pseudorandom number for shuffle
2563             int64_t seed = supply + forAll + forSome;
2564             auto prandom = std::minstd_rand0(seed);
2565
2566             for (int i = 0; i < extraWeight.size(); i++)
2567             {
2568                 extraWeight[i] = forAll;
2569                 if (forSome)
2570                 {
2571                     extraWeight[i]++;
2572                     forSome--;
2573                 }
2574             }
2575             // distribute the extra as evenly as possible
2576             std::shuffle(extraWeight.begin(), extraWeight.end(), prandom);
2577             for (int i = 0; i < weights.size(); i++)
2578             {
2579                 weights[i] -= extraWeight[i];
2580             }
2581         }
2582
2583         // update initial supply from what we currently have
2584         emitted = toEmit;
2585         supply = initialSupply + emitted;
2586     }
2587     return *this; 
2588 }
2589
2590 // From a vector of transaction pointers, match all that are valid orders and can be matched,
2591 // put them into a vector of reserve transaction descriptors, and return a new fractional reserve state,
2592 // if pConversionTx is present, this will add one output to it for each transaction included
2593 // and consider its size wen comparing to maxSerializeSize
2594 CCoinbaseCurrencyState CCoinbaseCurrencyState::MatchOrders(const std::vector<CReserveTransactionDescriptor> &orders, 
2595                                                            std::vector<CReserveTransactionDescriptor> &reserveFills, 
2596                                                            std::vector<CReserveTransactionDescriptor> &noFills, 
2597                                                            std::vector<const CReserveTransactionDescriptor *> &expiredFillOrKills, 
2598                                                            std::vector<const CReserveTransactionDescriptor *> &rejects, 
2599                                                            std::vector<CAmount> &prices,
2600                                                            int32_t height, std::vector<CInputDescriptor> &conversionInputs,
2601                                                            int64_t maxSerializeSize, int64_t *pInOutTotalSerializeSize, CMutableTransaction *pConversionTx,
2602                                                            bool feesAsReserve) const
2603 {
2604     // synthetic order book of limitBuys and limitSells sorted by limit, order of preference beyond limit sorting is random
2605     std::map<uint160, std::multimap<CAmount, CReserveTransactionDescriptor>> limitBuys;
2606     std::map<uint160, std::multimap<CAmount, CReserveTransactionDescriptor>> limitSells;        // limit orders are prioritized by limit
2607     std::multimap<CAmount, CReserveTransactionDescriptor> marketOrders;      // prioritized by fee rate
2608     CAmount totalNativeConversionFees = 0;
2609     CCurrencyValueMap totalReserveConversionFees;
2610     std::vector<CAmount> exchangeRates(PricesInReserve());
2611     CCurrencyValueMap exchangeRateMap = CCurrencyValueMap(currencies, exchangeRates);
2612
2613     CMutableTransaction mConversionTx = pConversionTx ? *pConversionTx : CMutableTransaction();
2614     std::vector<CInputDescriptor> tmpConversionInputs(conversionInputs);
2615
2616     int64_t totalSerializedSize = pInOutTotalSerializeSize ? *pInOutTotalSerializeSize + CCurrencyState::CONVERSION_TX_SIZE_MIN : CCurrencyState::CONVERSION_TX_SIZE_MIN;
2617     int64_t conversionSizeOverhead = 0;
2618
2619     if (!(flags & FLAG_FRACTIONAL))
2620     {
2621         return CCoinbaseCurrencyState();
2622     }
2623
2624     uint32_t tipTime = (chainActive.Height() >= height) ? chainActive[height]->nTime : chainActive.LastTip()->nTime;
2625     CCoinsViewCache view(pcoinsTip);
2626
2627     // organize all valid reserve exchange transactions into multimaps, limitBuys, limitSells, and market orders
2628     // orders that should be treated as normal/failed fill or kill will go into refunds and invalid transactions into rejects
2629     for (int i = 0; i < orders.size(); i++)
2630     {
2631         if (orders[i].IsValid() && orders[i].IsReserveExchange())
2632         {
2633             // if this is a market order, put it in, if limit, put it in an order book, sorted by limit
2634             if (orders[i].IsMarket())
2635             {
2636                 CAmount fee = orders[i].AllFeesAsNative(*this) + ReserveToNative(orders[i].ReserveConversionFeesMap()) + orders[i].nativeConversionFees;
2637                 CFeeRate feeRate = CFeeRate(fee, GetSerializeSize(CDataStream(SER_NETWORK, PROTOCOL_VERSION), *orders[i].ptx));
2638                 marketOrders.insert(std::make_pair(feeRate.GetFeePerK(), orders[i]));
2639             }
2640             else
2641             {
2642                 assert(orders[i].IsLimit());
2643                 // limit order, so put it in buy or sell
2644                 if (orders[i].numBuys)
2645                 {
2646                     limitBuys[orders[i].vRex[0].second.currencyID].insert(std::make_pair(orders[i].vRex[0].second.nLimit, orders[i]));
2647                 }
2648                 else
2649                 {
2650                     assert(orders[i].numSells);
2651                     limitSells[orders[i].vRex[0].second.currencyID].insert(std::make_pair(orders[i].vRex[0].second.nLimit, orders[i]));
2652                 }
2653             }
2654         }
2655         else if (orders[i].IsValid())
2656         {
2657             expiredFillOrKills.push_back(&(orders[i]));
2658         }
2659         else
2660         {
2661             rejects.push_back(&(orders[i]));
2662         }
2663     }
2664
2665     // now we have all market orders in marketOrders multimap, sorted by feePerK, rejects that are expired or invalid in rejects
2666     // first, prune as many market orders as possible up to the maximum storage space available for market orders, which we calculate
2667     // as a functoin of the total space available and precentage of total orders that are market orders
2668     int64_t numLimitOrders = limitBuys.size() + limitSells.size();
2669     int64_t numMarketOrders = marketOrders.size();
2670
2671     // if nothing to do, we are done, don't update anything
2672     if (!(reserveFills.size() + numLimitOrders + numMarketOrders))
2673     {
2674         return *this;
2675     }
2676
2677     // 1. start from the current state and calculate what the price would be with market orders that fit, leaving room for limit orders
2678     // 2. add limit orders, first buys, as many as we can from the highest value and number downward, one at a time, until we run out or
2679     //    cannot add any more due to not meeting the price. then we do the same for sells if there are any available, and alternate until
2680     //    we either run out or cannot add from either side. within a specific limit, orders are sorted by largest first, which means
2681     //    there is no point in retracing if an element fails to be added
2682     // 3. calculate a final order price
2683     // 4. create and return a new, updated CCoinbaseCurrencyState
2684     CCoinbaseCurrencyState newState(*(CCurrencyState *)this);
2685
2686     conversionSizeOverhead = GetSerializeSize(mConversionTx, SER_NETWORK, PROTOCOL_VERSION);
2687
2688     int64_t curSpace = maxSerializeSize - (totalSerializedSize + conversionSizeOverhead);
2689
2690     if (curSpace < 0)
2691     {
2692         printf("%s: no space available in block to include reserve orders\n", __func__);
2693         LogPrintf("%s: no space available in block to include reserve orders\n", __func__);
2694         return *this;
2695     }
2696
2697     // the ones that are passed in reserveFills may be any valid reserve related transaction
2698     // we need to first process those with the assumption that they are included
2699     for (auto it = reserveFills.begin(); it != reserveFills.end(); )
2700     {
2701         auto &txDesc = *it;
2702
2703         // add up the starting point for conversions
2704         if (txDesc.IsReserveExchange())
2705         {
2706             CMutableTransaction mtx;
2707             mtx = mConversionTx;
2708             txDesc.AddConversionInOuts(mtx, tmpConversionInputs, exchangeRateMap);
2709             conversionSizeOverhead = GetSerializeSize(mtx, SER_NETWORK, PROTOCOL_VERSION);
2710             int64_t tmpSpace = maxSerializeSize - (totalSerializedSize + conversionSizeOverhead);
2711
2712             if (tmpSpace < 0)
2713             {
2714                 // can't fit, so it's a noFill
2715                 noFills.push_back(txDesc);
2716                 it = reserveFills.erase(it);
2717             }
2718             else
2719             {
2720                 curSpace = tmpSpace;
2721
2722                 totalNativeConversionFees += txDesc.nativeConversionFees;
2723                 totalReserveConversionFees += txDesc.ReserveConversionFeesMap();
2724
2725                 if (feesAsReserve)
2726                 {
2727                     newState.reserveIn = AddVectors(txDesc.ReserveOutConvertedVec(*this), newState.reserveIn);
2728                     newState.nativeIn = AddVectors(txDesc.NativeOutConvertedVec(*this), newState.nativeIn);
2729                     newState.nativeIn[0] += txDesc.NativeFees() + txDesc.nativeConversionFees;
2730                 }
2731                 else
2732                 {
2733                     newState.reserveIn = AddVectors(
2734                                             AddVectors(txDesc.ReserveOutConvertedVec(*this), newState.reserveIn),
2735                                             AddVectors(txDesc.ReserveFees().AsCurrencyVector(currencies), txDesc.ReserveConversionFeesVec(*this))
2736                                          );
2737                     newState.nativeIn = AddVectors(txDesc.NativeOutConvertedVec(*this), newState.nativeIn);
2738                 }
2739
2740                 mConversionTx = mtx;
2741                 it++;
2742             }
2743         }
2744         else
2745         {
2746             if (feesAsReserve && newState.nativeIn.size())
2747             {
2748                 newState.nativeIn[0] += txDesc.NativeFees();
2749             }
2750             else
2751             {
2752                 newState.reserveIn = AddVectors(newState.reserveIn, txDesc.ReserveFees().AsCurrencyVector(currencies));
2753             }
2754             it++;
2755         }
2756     }
2757
2758     int64_t marketOrdersSizeLimit = curSpace;
2759     int64_t limitOrdersSizeLimit = curSpace;
2760     if (numLimitOrders + numMarketOrders)
2761     {
2762         marketOrdersSizeLimit = ((arith_uint256(numMarketOrders) * arith_uint256(curSpace)) / arith_uint256(numLimitOrders + numMarketOrders)).GetLow64();
2763         limitOrdersSizeLimit = curSpace - marketOrdersSizeLimit;
2764     }
2765     
2766     if (limitOrdersSizeLimit < 1024 && maxSerializeSize > 2048)
2767     {
2768         marketOrdersSizeLimit = maxSerializeSize - 1024;
2769         limitOrdersSizeLimit = 1024;
2770     }
2771
2772     for (auto marketOrder : marketOrders)
2773     {
2774         // add as many as we can fit, if we are able to, consider the output transaction overhead as well
2775         int64_t thisSerializeSize = GetSerializeSize(*marketOrder.second.ptx, SER_NETWORK, PROTOCOL_VERSION);
2776         CMutableTransaction mtx;
2777         mtx = mConversionTx;
2778         marketOrder.second.AddConversionInOuts(mtx, tmpConversionInputs, exchangeRateMap);
2779         conversionSizeOverhead = GetSerializeSize(mtx, SER_NETWORK, PROTOCOL_VERSION);
2780         if ((totalSerializedSize + thisSerializeSize + conversionSizeOverhead) <= marketOrdersSizeLimit)
2781         {
2782             totalNativeConversionFees += marketOrder.second.nativeConversionFees;
2783             totalReserveConversionFees += marketOrder.second.ReserveConversionFeesMap();
2784             mConversionTx = mtx;
2785
2786             if (feesAsReserve)
2787             {
2788                 newState.reserveIn = AddVectors(marketOrder.second.ReserveOutConvertedVec(*this), newState.reserveIn);
2789                 newState.nativeIn = AddVectors(marketOrder.second.NativeOutConvertedVec(*this), newState.nativeIn);
2790                 newState.nativeIn[0] += marketOrder.second.NativeFees() + marketOrder.second.nativeConversionFees;
2791             }
2792             else
2793             {
2794                 newState.reserveIn = AddVectors(
2795                                         AddVectors(marketOrder.second.ReserveOutConvertedVec(*this), newState.reserveIn),
2796                                         AddVectors(marketOrder.second.ReserveFees().AsCurrencyVector(currencies), marketOrder.second.ReserveConversionFeesVec(*this))
2797                                         );
2798                 newState.nativeIn = AddVectors(marketOrder.second.NativeOutConvertedVec(*this), newState.nativeIn);
2799             }
2800
2801             reserveFills.push_back(marketOrder.second);
2802             totalSerializedSize += thisSerializeSize;
2803         }
2804         else
2805         {
2806             // can't fit, no fill
2807             noFills.push_back(marketOrder.second);
2808         }
2809     }
2810
2811     // iteratively add limit orders first buy, then sell, until we no longer have anything to add
2812     // this must iterate because each time we add a buy, it may put another sell's limit within reach and
2813     // vice versa
2814     std::map<uint160, std::pair<std::multimap<CAmount, CReserveTransactionDescriptor>::iterator, CAmount>> buyPartial; // valid in loop for partial fill
2815     std::map<uint160, std::pair<std::multimap<CAmount, CReserveTransactionDescriptor>::iterator, CAmount>> sellPartial;
2816
2817     limitOrdersSizeLimit = maxSerializeSize - totalSerializedSize;
2818     int64_t buyLimitSizeLimit = numLimitOrders ? totalSerializedSize + ((arith_uint256(limitBuys.size()) * arith_uint256(limitOrdersSizeLimit)) / arith_uint256(numLimitOrders)).GetLow64() : limitOrdersSizeLimit;
2819
2820     CCurrencyState latestState = newState;
2821
2822     // TODO - finish limit orders, which are significantly more complex after moving to
2823     // multi-reserves. until then, skip limit orders.
2824     /*
2825     for (bool tryagain = true; tryagain; )
2826     {
2827         tryagain = false;
2828
2829         exchangeRates = ConvertAmounts(newState.reserveIn, newState.nativeIn, latestState);
2830
2831         // starting with that exchange rate, add buys one at a time until we run out of buys to add or reach the limit,
2832         // possibly in a partial fill, then see if we can add any sells. we go back and forth until we have stopped being able to add
2833         // new orders. it would be possible to recognize a state where we are able to simultaneously fill a buy and a sell that are
2834         // the the same or very minimally overlapping limits
2835         
2836         for (auto limitBuysIt = limitBuys.rbegin(); limitBuysIt != limitBuys.rend() && exchangeRate > limitBuysIt->second.vRex.front().second.nLimit; limitBuysIt = limitBuys.rend())
2837         {
2838             // any time there are entries above the lower bound, we only actually look at the end, since we mutate the
2839             // search space each iteration and remove anything we've already added, making the end always the most in-the-money
2840             CReserveTransactionDescriptor &currentBuy = limitBuysIt->second;
2841
2842             // it must first fit, space-wise
2843             int64_t thisSerializeSize = GetSerializeSize(*(currentBuy.ptx), SER_NETWORK, PROTOCOL_VERSION);
2844
2845             CMutableTransaction mtx;
2846             if (pConversionTx)
2847             {
2848                 mtx = *pConversionTx;
2849                 currentBuy.AddConversionInOuts(mtx, tmpConversionInputs);
2850                 conversionSizeOverhead = GetSerializeSize(mtx, SER_NETWORK, PROTOCOL_VERSION);
2851             }
2852             if ((totalSerializedSize + thisSerializeSize + conversionSizeOverhead) <= buyLimitSizeLimit)
2853             {
2854                 CAmount newReserveIn, newNativeIn;
2855                 if (feesAsReserve)
2856                 {
2857                     newReserveIn = currentBuy.reserveOutConverted;
2858                     newNativeIn = currentBuy.nativeOutConverted + currentBuy.NativeFees() + currentBuy.nativeConversionFees;
2859                 }
2860                 else
2861                 {
2862                     newReserveIn = currentBuy.reserveOutConverted + currentBuy.ReserveFees() + currentBuy.reserveConversionFees;
2863                     newNativeIn = currentBuy.nativeOutConverted;
2864                 }
2865
2866                 // calculate fresh with all conversions together to see if we still meet the limit
2867                 CAmount newExchange = ConvertAmounts(newState.ReserveIn + newReserveIn, newState.NativeIn + newNativeIn, latestState);
2868
2869                 if (newExchange <= currentBuy.vRex[0].second.nLimit)
2870                 {
2871                     totalNativeConversionFees += currentBuy.nativeConversionFees;
2872                     totalReserveConversionFees += currentBuy.reserveConversionFees;
2873
2874                     // update conversion transaction if we have one
2875                     if (pConversionTx)
2876                     {
2877                         *pConversionTx = mtx;
2878                     }
2879
2880                     // add to the current buys, we will never do something to disqualify this, since all orders left are either
2881                     // the same limit or lower
2882                     reserveFills.push_back(currentBuy);
2883                     totalSerializedSize += thisSerializeSize;
2884
2885                     newState.ReserveIn += newReserveIn;
2886                     newState.NativeIn += newNativeIn;
2887
2888                     exchangeRate = newExchange;
2889                     limitBuys.erase(--limitBuys.end());
2890                 }
2891                 else
2892                 {
2893                     // TODO:PBAAS support partial fills
2894                     break;
2895                 }
2896             }
2897         }
2898
2899         // now, iterate from lowest/most qualified sell limit first to highest/least qualified and attempt to put all in
2900         // if we can only fill an order partially, then do so
2901         for (auto limitSellsIt = limitSells.begin(); limitSellsIt != limitSells.end() && exchangeRate > limitSellsIt->second.vRex.front().second.nLimit; limitSellsIt = limitSells.begin())
2902         {
2903             CReserveTransactionDescriptor &currentSell = limitSellsIt->second;
2904
2905             int64_t thisSerializeSize = GetSerializeSize(*currentSell.ptx, SER_NETWORK, PROTOCOL_VERSION);
2906
2907             CMutableTransaction mtx;
2908             if (pConversionTx)
2909             {
2910                 mtx = *pConversionTx;
2911                 currentSell.AddConversionInOuts(mtx, tmpConversionInputs);
2912                 conversionSizeOverhead = GetSerializeSize(mtx, SER_NETWORK, PROTOCOL_VERSION);
2913             }
2914
2915             if ((totalSerializedSize + thisSerializeSize + conversionSizeOverhead) <= maxSerializeSize)
2916             {
2917                 CAmount newReserveIn, newNativeIn;
2918                 if (feesAsReserve)
2919                 {
2920                     newReserveIn = currentSell.reserveOutConverted;
2921                     newNativeIn = currentSell.nativeOutConverted + currentSell.NativeFees() + currentSell.nativeConversionFees;
2922                 }
2923                 else
2924                 {
2925                     newReserveIn = currentSell.reserveOutConverted + currentSell.ReserveFees() + currentSell.reserveConversionFees;
2926                     newNativeIn = currentSell.nativeOutConverted;
2927                 }
2928
2929                 // calculate fresh with all conversions together to see if we still meet the limit
2930                 CAmount newExchange = ConvertAmounts(newState.ReserveIn + newReserveIn, newState.NativeIn + newNativeIn, latestState);
2931                 if (newExchange >= currentSell.vRex.front().second.nLimit)
2932                 {
2933                     totalNativeConversionFees += currentSell.nativeConversionFees;
2934                     totalReserveConversionFees += currentSell.reserveConversionFees;
2935
2936                     // update conversion transaction if we have one
2937                     if (pConversionTx)
2938                     {
2939                         *pConversionTx = mtx;
2940                     }
2941
2942                     // add to the current sells, we will never do something to disqualify this, since all orders left are either
2943                     // the same limit or higher
2944                     reserveFills.push_back(currentSell);
2945                     totalSerializedSize += thisSerializeSize;
2946                     exchangeRate = newExchange;
2947
2948                     newState.ReserveIn += newReserveIn;
2949                     newState.NativeIn += newNativeIn;
2950
2951                     limitSells.erase(limitSellsIt);
2952                     tryagain = true;
2953                 }
2954                 else
2955                 {
2956                     // we must be done with buys whether we created a partial or not
2957                     break;
2958                 }
2959             }
2960         }
2961         buyLimitSizeLimit = maxSerializeSize - totalSerializedSize;
2962     }
2963     */
2964
2965     // we can calculate total fees now, but to avoid a rounding error when converting native to reserve or vice versa on fees, 
2966     // we must loop through once more to calculate all fees for the currency state
2967     auto curMap = GetReserveMap();
2968     CCurrencyValueMap totalReserveFees;
2969     CAmount totalNativeFees = 0;
2970     CCurrencyValueMap reserveOutVal;
2971     CMutableTransaction mtx = pConversionTx ? *pConversionTx : CMutableTransaction();
2972     for (auto fill : reserveFills)
2973     {
2974         fill.AddConversionInOuts(mtx, conversionInputs, exchangeRateMap);
2975         reserveOutVal += NativeToReserveRaw(fill.NativeOutConvertedVec(latestState), exchangeRates);
2976         if (feesAsReserve)
2977         {
2978             totalReserveFees += fill.AllFeesAsReserve(newState, exchangeRates);
2979         }
2980         else
2981         {
2982             totalNativeFees += fill.AllFeesAsNative(newState, exchangeRates);
2983         }
2984     }
2985     if (pConversionTx)
2986     {
2987         *pConversionTx = mtx;
2988     }
2989     if (feesAsReserve)
2990     {
2991         totalReserveConversionFees.valueMap[currencies[0]] += NativeToReserve(totalNativeConversionFees, exchangeRates[0]);
2992         reserveOutVal += totalReserveConversionFees;
2993         totalReserveFees += totalReserveConversionFees;
2994     }
2995     else
2996     {
2997         totalNativeConversionFees = totalNativeConversionFees + ReserveToNativeRaw(totalReserveConversionFees, exchangeRates);
2998         totalNativeFees += totalNativeConversionFees;
2999     }
3000
3001     newState = CCoinbaseCurrencyState(latestState, 
3002                                       totalNativeFees, totalNativeConversionFees,
3003                                       newState.reserveIn,
3004                                       newState.nativeIn,
3005                                       newState.reserveOut, 
3006                                       exchangeRates, 
3007                                       totalReserveFees.AsCurrencyVector(currencies), 
3008                                       totalReserveConversionFees.AsCurrencyVector(currencies));
3009
3010     prices = exchangeRates;
3011
3012     for (auto &curBuys : limitBuys)
3013     {
3014         for (auto &entry : curBuys.second)
3015         {
3016             noFills.push_back(entry.second);
3017         }
3018     }
3019
3020     for (auto &curSells : limitSells)
3021     {
3022         for (auto &entry : curSells.second)
3023         {
3024             noFills.push_back(entry.second);
3025         }
3026     }
3027
3028     // if no matches, no state updates
3029     if (!reserveFills.size())
3030     {
3031         return *this;
3032     }
3033     else
3034     {
3035         if (pInOutTotalSerializeSize)
3036         {
3037             *pInOutTotalSerializeSize = totalSerializedSize;
3038         }
3039         printf("%s: %s\n", __func__, newState.ToUniValue().write(1, 2).c_str());
3040         return newState;
3041     }
3042 }
3043
3044 CAmount CCurrencyState::CalculateConversionFee(CAmount inputAmount, bool convertToNative, int currencyIndex) const
3045 {
3046     arith_uint256 bigAmount(inputAmount);
3047     arith_uint256 bigSatoshi(SATOSHIDEN);
3048
3049     // we need to calculate a fee based either on the amount to convert or the last price
3050     // times the reserve
3051     if (convertToNative)
3052     {
3053         int64_t price;
3054         cpp_dec_float_50 priceInReserve = PriceInReserveDecFloat50(currencyIndex);
3055         if (!to_int64(priceInReserve, price))
3056         {
3057             assert(false);
3058         }
3059         bigAmount = price ? (bigAmount * bigSatoshi) / arith_uint256(price) : 0;
3060     }
3061
3062     CAmount fee = 0;
3063     fee = ((bigAmount * arith_uint256(CReserveExchange::SUCCESS_FEE)) / bigSatoshi).GetLow64();
3064     if (fee < CReserveExchange::MIN_SUCCESS_FEE)
3065     {
3066         fee = CReserveExchange::MIN_SUCCESS_FEE;
3067     }
3068     return fee;
3069 }
3070
3071 CAmount CReserveTransactionDescriptor::CalculateConversionFee(CAmount inputAmount)
3072 {
3073     arith_uint256 bigAmount(inputAmount);
3074     arith_uint256 bigSatoshi(SATOSHIDEN);
3075
3076     CAmount fee = 0;
3077     fee = ((bigAmount * arith_uint256(CReserveExchange::SUCCESS_FEE)) / bigSatoshi).GetLow64();
3078     if (fee < CReserveExchange::MIN_SUCCESS_FEE)
3079     {
3080         fee = CReserveExchange::MIN_SUCCESS_FEE;
3081     }
3082     return fee;
3083 }
3084
3085 // this calculates a fee that will be added to an amount and result in the same percentage as above,
3086 // such that a total of the inputAmount + this returned fee, if passed to CalculateConversionFee, would return
3087 // the same amount
3088 CAmount CReserveTransactionDescriptor::CalculateAdditionalConversionFee(CAmount inputAmount)
3089 {
3090     arith_uint256 bigAmount(inputAmount);
3091     arith_uint256 bigSatoshi(SATOSHIDEN);
3092     arith_uint256 conversionFee(CReserveExchange::SUCCESS_FEE);
3093
3094     CAmount newAmount = ((bigAmount * bigSatoshi) / (bigSatoshi - conversionFee)).GetLow64();
3095     CAmount fee = CalculateConversionFee(newAmount);
3096     newAmount = inputAmount + fee;
3097     fee = CalculateConversionFee(newAmount);            // again to account for minimum fee
3098     fee += inputAmount - (newAmount - fee);             // add any additional difference
3099     return fee;
3100 }
3101
This page took 0.202346 seconds and 4 git commands to generate.