]>
Commit | Line | Data |
---|---|---|
a6e612cc MT |
1 | /******************************************************************** |
2 | * (C) 2019 Michael Toutonghi | |
3 | * | |
4 | * Distributed under the MIT software license, see the accompanying | |
5 | * file COPYING or http://www.opensource.org/licenses/mit-license.php. | |
6 | * | |
7 | * This provides reserve currency functions, leveraging the multi-precision boost libraries to calculate reserve currency conversions | |
8 | * in a predictable manner that can achieve consensus. | |
9 | * | |
10 | */ | |
11 | ||
12 | #ifndef PBAAS_RESERVES_H | |
13 | #define PBAAS_RESERVES_H | |
14 | ||
15 | #include <sstream> | |
56fe75cb | 16 | #include <univalue/include/univalue.h> |
e7e14f44 | 17 | #include "pbaas/crosschainrpc.h" |
56fe75cb | 18 | #include "arith_uint256.h" |
a6e612cc | 19 | #include <boost/multiprecision/cpp_dec_float.hpp> |
989b1de1 | 20 | #include "librustzcash.h" |
e7e14f44 | 21 | #include "pubkey.h" |
56fe75cb | 22 | #include "amount.h" |
f88ddf77 | 23 | #include <map> |
a6e612cc | 24 | |
56fe75cb | 25 | #ifndef SATOSHIDEN |
26 | #define SATOSHIDEN ((uint64_t)100000000L) | |
27 | #endif | |
28 | ||
a6e612cc | 29 | using boost::multiprecision::cpp_dec_float_50; |
e7c700b5 | 30 | class CCoinsViewCache; |
0574c740 | 31 | class CInputDescriptor; |
15e4d481 | 32 | class CBaseChainObject; |
56fe75cb | 33 | class CTransaction; |
34 | class CMutableTransaction; | |
35 | class CTxOut; | |
36 | class CReserveTransactionDescriptor; | |
c8c677c9 | 37 | class CCurrencyState; |
56fe75cb | 38 | |
f88ddf77 | 39 | // reserve output is a special kind of token output that does not have to carry it's identifier, as it |
989b1de1 | 40 | // is always assumed to be the reserve currency of the current chain. |
56fe75cb | 41 | class CTokenOutput |
a6e612cc MT |
42 | { |
43 | public: | |
56fe75cb | 44 | enum |
45 | { | |
46 | VERSION_INVALID = 0, | |
47 | VERSION_CURRENT = 1, | |
48 | VERSION_FIRSTVALID = 1, | |
49 | VERSION_LASTVALID = 1, | |
50 | }; | |
a6e612cc | 51 | |
56fe75cb | 52 | static std::vector<uint160> *reserveIDs; |
53 | ||
54 | uint32_t nVersion; // information about this currency | |
f88ddf77 | 55 | uint160 currencyID; // currency ID |
56fe75cb | 56 | CAmount nValue; // amount of input reserve coins this UTXO represents before any conversion |
a6e612cc | 57 | |
56fe75cb | 58 | CTokenOutput(const std::vector<unsigned char> &asVector) |
a6e612cc MT |
59 | { |
60 | FromVector(asVector, *this); | |
61 | } | |
62 | ||
56fe75cb | 63 | CTokenOutput(const UniValue &obj); |
a6e612cc | 64 | |
56fe75cb | 65 | CTokenOutput() : nVersion(VERSION_CURRENT), nValue(0) { } |
f88ddf77 | 66 | |
56fe75cb | 67 | CTokenOutput(const uint160 &curID, CAmount value) : nVersion(VERSION_CURRENT), currencyID(curID), nValue(value) { } |
a6e612cc MT |
68 | |
69 | ADD_SERIALIZE_METHODS; | |
70 | ||
71 | template <typename Stream, typename Operation> | |
72 | inline void SerializationOp(Stream& s, Operation ser_action) { | |
56fe75cb | 73 | READWRITE(VARINT(nVersion)); |
74 | READWRITE(currencyID); | |
a6e612cc | 75 | READWRITE(VARINT(nValue)); |
a6e612cc MT |
76 | } |
77 | ||
78 | std::vector<unsigned char> AsVector() | |
79 | { | |
80 | return ::AsVector(*this); | |
81 | } | |
82 | ||
41f170fd MT |
83 | UniValue ToUniValue() const; |
84 | ||
a613ac6c | 85 | bool IsValid() const |
a6e612cc | 86 | { |
b7c685b8 | 87 | // we don't support op returns, value must be in native or reserve |
5781675d | 88 | return nVersion >= VERSION_FIRSTVALID && nVersion <= VERSION_LASTVALID; |
a6e612cc MT |
89 | } |
90 | }; | |
91 | ||
56fe75cb | 92 | class CReserveTransfer : public CTokenOutput |
a6e612cc MT |
93 | { |
94 | public: | |
56fe75cb | 95 | enum EOptions |
96 | { | |
97 | VALID = 1, | |
98 | CONVERT = 2, | |
99 | PRECONVERT = 4, | |
100 | FEE_OUTPUT = 8, // one per import, amount must match total percentage of fees for exporter, no pre-convert allowed | |
101 | SEND_BACK = 0x10, // fee is sent back immediately to destination on exporting chain | |
15197dca | 102 | MINT_CURRENCY = 0x20, // set when this output is being minted on import |
103 | PREALLOCATE = 0x40 // combined with minting for pre-allocation of currency | |
56fe75cb | 104 | }; |
105 | ||
106 | enum EConstants | |
107 | { | |
108 | DESTINATION_BYTE_DIVISOR = 128 // destination vector is divided by this and result is multiplied by normal fee and added to transfer fee | |
109 | }; | |
a6e612cc | 110 | |
56fe75cb | 111 | static const CAmount DEFAULT_PER_STEP_FEE = 10000; // default fee for each step of each transfer (initial mining, transfer, mining on new chain) |
989b1de1 | 112 | |
56fe75cb | 113 | uint32_t flags; // type of transfer and options |
114 | CAmount nFees; // cross-chain network fees only, separated out to enable market conversions, conversion fees are additional | |
2f416b17 | 115 | uint160 destCurrencyID; // system to export to, which may represent a PBaaS chain or external bridge |
56fe75cb | 116 | CTransferDestination destination; // system specific address to send funds to on the target chain |
989b1de1 MT |
117 | |
118 | CReserveTransfer(const std::vector<unsigned char> &asVector) | |
a6e612cc MT |
119 | { |
120 | FromVector(asVector, *this); | |
121 | } | |
122 | ||
56fe75cb | 123 | CReserveTransfer() : CTokenOutput(), flags(0), nFees(0) { } |
a6e612cc | 124 | |
f7917c6b | 125 | CReserveTransfer(uint32_t Flags, const uint160 &cID, CAmount value, CAmount fees, const uint160 &destCurID, const CTransferDestination &dest) : |
126 | CTokenOutput(cID, value), flags(Flags), nFees(fees), destCurrencyID(destCurID), destination(dest) { } | |
a6e612cc MT |
127 | |
128 | ADD_SERIALIZE_METHODS; | |
129 | ||
130 | template <typename Stream, typename Operation> | |
131 | inline void SerializationOp(Stream& s, Operation ser_action) { | |
56fe75cb | 132 | READWRITE(*(CTokenOutput *)this); |
133 | READWRITE(VARINT(flags)); | |
989b1de1 | 134 | READWRITE(VARINT(nFees)); |
f7917c6b | 135 | READWRITE(destCurrencyID); |
989b1de1 | 136 | READWRITE(destination); |
a6e612cc MT |
137 | } |
138 | ||
139 | std::vector<unsigned char> AsVector() | |
140 | { | |
141 | return ::AsVector(*this); | |
142 | } | |
143 | ||
cb265a66 | 144 | UniValue ToUniValue() const; |
145 | ||
2f416b17 | 146 | CCurrencyValueMap CalculateFee(uint32_t flags, CAmount transferTotal) const; |
147 | ||
148 | static CAmount CalculateTransferFee(const CTransferDestination &destination); | |
56fe75cb | 149 | |
150 | CAmount CalculateTransferFee() const; | |
951413c7 | 151 | |
a613ac6c | 152 | bool IsValid() const |
a6e612cc | 153 | { |
56fe75cb | 154 | return CTokenOutput::IsValid() && (nFees > 0 || flags & FEE_OUTPUT) && destination.destination.size(); |
a6e612cc MT |
155 | } |
156 | }; | |
157 | ||
158 | // convert from $VRSC to fractional reserve coin or vice versa. coinID determines which | |
159 | // in either direction, this is burned in the block. if burned, the block must contain less than a | |
160 | // maximum reasonable number of exchange outputs, which must be sorted, hashed, and used to validate | |
161 | // the outputs that must match exactly on any transaction spending the output. since fees are not | |
162 | // included in outputs, it is assumed that a miner will spend the output in the same block to recover | |
163 | // exchange fees | |
56fe75cb | 164 | class CReserveExchange : public CTokenOutput |
a6e612cc MT |
165 | { |
166 | public: | |
a6e612cc | 167 | // flags |
56fe75cb | 168 | enum EFlags |
169 | { | |
170 | VALID = 1, | |
171 | TO_RESERVE = 0x80000, // from fractional currency to reserve, default is reserve to fractional | |
172 | LIMIT = 0x100000, // observe the limit when converting | |
173 | FILL_OR_KILL = 0x200000, // if not filled before nValidBefore but before expiry, no execution, mined with fee, output pass through | |
174 | ALL_OR_NONE = 0x400000, // will not execute partial order | |
175 | SEND_OUTPUT = 0x800000 // send the output of this exchange to the target chain, only valid if output is reserve | |
176 | }; | |
a6e612cc MT |
177 | |
178 | // success fee is calculated by multiplying the amount by this number and dividing by satoshis (100,000,000), not less than 10x the absolute SUCCESS_FEE | |
e7e14f44 MT |
179 | // failure fee, meaning the valid before block is past but it is not expired is the difference between input and output and must follow those rules |
180 | // it is deducted in the success case from the success fee, so there is no fee beyond the success fee paid | |
56fe75cb | 181 | static const CAmount SUCCESS_FEE = 25000; |
e7e14f44 | 182 | static const CAmount MIN_SUCCESS_FEE = 50000; |
a6e612cc | 183 | static const CAmount MIN_PARTIAL = 10000000; // making partial fill minimum the number at which minimum fee meets standard percent fee, |
e7c700b5 | 184 | static const CAmount MIN_NET_CONVERSION = 10000000; // minimum conversion for input |
e7c700b5 | 185 | static const CAmount FILL_OR_KILL_FEE = 10000; |
a6e612cc | 186 | |
56fe75cb | 187 | uint32_t flags; // type of transfer and options |
188 | CAmount nLimit; // limit price to sell or buy currency | |
189 | uint32_t nValidBefore; // if not filled on or after this block, can mine tx, but is spendable to refund input | |
a6e612cc MT |
190 | |
191 | CReserveExchange(const std::vector<unsigned char> &asVector) | |
192 | { | |
193 | FromVector(asVector, *this); | |
194 | } | |
195 | ||
56fe75cb | 196 | CReserveExchange() : CTokenOutput(), nLimit(0), nValidBefore(0) { } |
a6e612cc | 197 | |
56fe75cb | 198 | CReserveExchange(uint32_t Flags, const uint160 &cID, CAmount amountIn, CAmount Limit=0, uint32_t ValidBefore=0) : |
199 | CTokenOutput(cID, amountIn), flags(Flags), nLimit(Limit), nValidBefore(ValidBefore) {} | |
a6e612cc | 200 | |
56fe75cb | 201 | CReserveExchange(const UniValue &uni); |
202 | CReserveExchange(const CTransaction &tx); | |
a6e612cc MT |
203 | |
204 | ADD_SERIALIZE_METHODS; | |
205 | ||
206 | template <typename Stream, typename Operation> | |
207 | inline void SerializationOp(Stream& s, Operation ser_action) { | |
56fe75cb | 208 | READWRITE(*(CTokenOutput *)this); |
209 | READWRITE(VARINT(flags)); | |
a6e612cc MT |
210 | READWRITE(VARINT(nLimit)); |
211 | READWRITE(nValidBefore); | |
212 | } | |
213 | ||
214 | std::vector<unsigned char> AsVector() | |
215 | { | |
216 | return ::AsVector(*this); | |
217 | } | |
218 | ||
a613ac6c | 219 | bool IsValid() const |
a6e612cc MT |
220 | { |
221 | // this needs an actual check | |
56fe75cb | 222 | return CTokenOutput::IsValid(); |
a6e612cc MT |
223 | } |
224 | ||
cb265a66 | 225 | UniValue ToUniValue() const; |
226 | ||
a6e612cc MT |
227 | bool IsExpired(int32_t height) |
228 | { | |
229 | return height >= nValidBefore; | |
230 | } | |
231 | }; | |
232 | ||
56fe75cb | 233 | // import transactions and tokens from another chain |
234 | // this represents the chain, the currencies, and the amounts of each | |
235 | // it may also import IDs from the chain on which they were defined | |
cdbadc7f | 236 | class CCrossChainImport |
237 | { | |
238 | public: | |
56fe75cb | 239 | static const uint32_t VERSION_INVALID = 0; |
240 | static const uint32_t VERSION_CURRENT = 1; | |
241 | static const uint32_t VERSION_LAST = 1; | |
242 | uint32_t nVersion; | |
243 | uint160 systemID; // the blockchain or currency system source from where these transactions come | |
244 | CCurrencyValueMap importValue; // total amount of coins imported from chain with or without conversion, including fees | |
0ab273d2 | 245 | CCurrencyValueMap totalReserveOutMap; // all non-native currencies being held in this thread and released on import |
56fe75cb | 246 | |
247 | CCrossChainImport() : nVersion(VERSION_INVALID) {} | |
0ab273d2 | 248 | CCrossChainImport(const uint160 &cID, const CCurrencyValueMap &ImportValue, const CCurrencyValueMap &InitialReserveOutput=CCurrencyValueMap()) : |
249 | nVersion(VERSION_CURRENT), systemID(cID), importValue(ImportValue), totalReserveOutMap(InitialReserveOutput) { } | |
cdbadc7f | 250 | |
251 | CCrossChainImport(const std::vector<unsigned char> &asVector) | |
252 | { | |
253 | ::FromVector(asVector, *this); | |
254 | } | |
255 | ||
0ab273d2 | 256 | CCrossChainImport(const CTransaction &tx, int32_t *pOutNum=nullptr); |
cdbadc7f | 257 | |
258 | ADD_SERIALIZE_METHODS; | |
259 | ||
260 | template <typename Stream, typename Operation> | |
261 | inline void SerializationOp(Stream& s, Operation ser_action) { | |
56fe75cb | 262 | READWRITE(nVersion); |
263 | READWRITE(systemID); | |
264 | READWRITE(importValue); | |
0ab273d2 | 265 | READWRITE(totalReserveOutMap); |
cdbadc7f | 266 | } |
267 | ||
268 | std::vector<unsigned char> AsVector() | |
269 | { | |
270 | return ::AsVector(*this); | |
271 | } | |
272 | ||
273 | bool IsValid() const | |
274 | { | |
2f416b17 | 275 | return nVersion > VERSION_INVALID && nVersion <= VERSION_LAST && !systemID.IsNull(); |
cdbadc7f | 276 | } |
277 | ||
278 | UniValue ToUniValue() const; | |
279 | }; | |
280 | ||
281 | // describes an entire output that will be realized on a target chain. target is specified as part of an aggregated transaction. | |
282 | class CCrossChainExport | |
283 | { | |
284 | public: | |
285 | static const int MIN_BLOCKS = 10; | |
286 | static const int MIN_INPUTS = 10; | |
287 | static const int MAX_EXPORT_INPUTS = 50; | |
56fe75cb | 288 | static const uint32_t VERSION_INVALID = 0; |
289 | static const uint32_t VERSION_CURRENT = 1; | |
290 | static const uint32_t VERSION_LAST = 1; | |
291 | uint32_t nVersion; | |
292 | uint160 systemID; // target blockchain or currency system ID | |
293 | int32_t numInputs; // number of inputs aggregated to calculate the fee percentage | |
56a7b665 | 294 | CCurrencyValueMap totalAmounts; // total amount of inputs of each currency, including fees |
56fe75cb | 295 | CCurrencyValueMap totalFees; // total amount of fees of each currency to split between miner on exporting chain and importing chain |
cdbadc7f | 296 | |
56fe75cb | 297 | CCrossChainExport() : nVersion(VERSION_INVALID), numInputs(0) {} |
cdbadc7f | 298 | |
299 | CCrossChainExport(const std::vector<unsigned char> &asVector) | |
300 | { | |
301 | FromVector(asVector, *this); | |
302 | } | |
303 | ||
56fe75cb | 304 | CCrossChainExport(uint160 SystemID, int32_t numin, const CCurrencyValueMap &values, const CCurrencyValueMap &fees) : |
305 | nVersion(VERSION_CURRENT), systemID(SystemID), numInputs(numin), totalAmounts(values), totalFees(fees) {} | |
cdbadc7f | 306 | |
56fe75cb | 307 | CCrossChainExport(const CTransaction &tx, int32_t *pCCXOutputNum=nullptr); |
cdbadc7f | 308 | |
309 | ADD_SERIALIZE_METHODS; | |
310 | ||
311 | template <typename Stream, typename Operation> | |
312 | inline void SerializationOp(Stream& s, Operation ser_action) { | |
56fe75cb | 313 | READWRITE(nVersion); |
314 | READWRITE(systemID); | |
cdbadc7f | 315 | READWRITE(numInputs); |
56fe75cb | 316 | READWRITE(totalAmounts); |
317 | READWRITE(totalFees); | |
cdbadc7f | 318 | } |
319 | ||
320 | std::vector<unsigned char> AsVector() | |
321 | { | |
322 | return ::AsVector(*this); | |
323 | } | |
324 | ||
325 | bool IsValid() const | |
326 | { | |
56fe75cb | 327 | return nVersion > VERSION_INVALID && |
328 | nVersion <= VERSION_LAST && | |
c8c677c9 | 329 | !systemID.IsNull(); |
cdbadc7f | 330 | } |
331 | ||
815a42a6 | 332 | static CCurrencyValueMap CalculateExportFee(const CCurrencyValueMap &fees, int numIn); |
56fe75cb | 333 | CCurrencyValueMap CalculateExportFee() const; |
cdbadc7f | 334 | |
56fe75cb | 335 | CCurrencyValueMap CalculateImportFee() const; |
cdbadc7f | 336 | |
337 | UniValue ToUniValue() const; | |
338 | }; | |
339 | ||
c8c677c9 | 340 | class CCurrencyState |
f88ddf77 | 341 | { |
342 | public: | |
56fe75cb | 343 | enum { |
344 | VALID = 1, | |
345 | ISRESERVE = 2, | |
346 | MIN_RESERVE_RATIO = 1000000, // we will not start a chain with less than 1% reserve ratio in any single currency | |
347 | MAX_RESERVE_RATIO = 100000000, // we will not start a chain with greater than 100% reserve ratio | |
348 | SHUTDOWN_RESERVE_RATIO = 500000, // if we hit this reserve ratio in any currency, initiate chain shutdown | |
349 | CONVERSION_TX_SIZE_MIN = 1024, // minimum size accounted for in a conversion transaction | |
350 | MAX_RESERVE_CURRENCIES = 10 // maximum number of reserve currencies that can underly a fractional reserve | |
351 | }; | |
f88ddf77 | 352 | |
353 | uint32_t flags; // currency flags (valid, reserve currency, etc.) | |
354 | ||
56fe75cb | 355 | std::vector<uint160> currencies; // the ID in uin160 form (maps to CIdentityID) if each currency in the reserve |
356 | std::vector<int32_t> weights; // current, individual weights for all currencies to use in calculations | |
357 | std::vector<int64_t> reserves; // total amount of reserves in each currency | |
f88ddf77 | 358 | |
56fe75cb | 359 | int64_t initialSupply; // initial premine + pre-converted coins |
360 | int64_t emitted; // emitted coins reduce the reserve ratio and are used to calculate current ratio | |
361 | CAmount supply; // current supply: total of initial, all emitted, and all purchased coins | |
f88ddf77 | 362 | |
363 | //std::vector<CAmount> Reserves; // reserve currencies amounts controlled by this fractional chain - only present for reserve currencies, currency IDs are in chain definition | |
364 | ||
c8c677c9 | 365 | CCurrencyState() : flags(0), initialSupply(0), emitted(0), supply(0) {} |
f88ddf77 | 366 | |
c8c677c9 | 367 | CCurrencyState(const std::vector<uint160> &Currencies, |
56fe75cb | 368 | const std::vector<int32_t> &Weights, |
369 | const std::vector<int64_t> &Reserves, | |
370 | CAmount InitialSupply, | |
371 | CAmount Emitted, | |
372 | CAmount Supply, | |
373 | uint32_t Flags=VALID) : | |
f88ddf77 | 374 | flags(Flags), supply(Supply), initialSupply(InitialSupply), emitted(Emitted), weights(Weights), reserves(Reserves) |
56fe75cb | 375 | {} |
f88ddf77 | 376 | |
c8c677c9 | 377 | CCurrencyState(const std::vector<unsigned char> &asVector) |
f88ddf77 | 378 | { |
379 | FromVector(asVector, *this); | |
380 | } | |
381 | ||
c8c677c9 | 382 | CCurrencyState(const UniValue &uni); |
f88ddf77 | 383 | |
384 | ADD_SERIALIZE_METHODS; | |
385 | ||
386 | template <typename Stream, typename Operation> | |
387 | inline void SerializationOp(Stream& s, Operation ser_action) { | |
388 | READWRITE(flags); | |
389 | READWRITE(currencies); | |
390 | READWRITE(weights); | |
391 | READWRITE(reserves); | |
f88ddf77 | 392 | READWRITE(VARINT(initialSupply)); |
393 | READWRITE(VARINT(emitted)); | |
394 | READWRITE(VARINT(supply)); | |
395 | } | |
396 | ||
397 | std::vector<unsigned char> AsVector() const | |
398 | { | |
399 | return ::AsVector(*this); | |
400 | } | |
401 | ||
402 | // this should be done no more than once to prepare a currency state to be moved to the next state | |
403 | // emission occurs for a block before any conversion or exchange and that impact on the currency state is calculated | |
c8c677c9 | 404 | CCurrencyState &UpdateWithEmission(CAmount emitted); |
f88ddf77 | 405 | |
56fe75cb | 406 | cpp_dec_float_50 GetReserveRatio(int32_t reserveIndex=0) const |
f88ddf77 | 407 | { |
56fe75cb | 408 | return cpp_dec_float_50(std::to_string(weights[reserveIndex])) / cpp_dec_float_50("100000000"); |
f88ddf77 | 409 | } |
410 | ||
411 | template<typename cpp_dec_float_type> | |
412 | static bool to_int64(const cpp_dec_float_type &input, int64_t &outval) | |
413 | { | |
414 | std::stringstream ss(input.str(0)); | |
415 | try | |
416 | { | |
417 | ss >> outval; | |
418 | return true; | |
419 | } | |
420 | catch(const std::exception& e) | |
421 | { | |
422 | return false; | |
423 | } | |
424 | } | |
425 | ||
426 | CAmount PriceInReserve(int32_t reserveIndex=0) const | |
427 | { | |
428 | if (reserveIndex >= reserves.size()) | |
429 | { | |
430 | return 0; | |
431 | } | |
56fe75cb | 432 | if (supply == 0 || weights[reserveIndex] == 0) |
f88ddf77 | 433 | { |
56fe75cb | 434 | return weights[reserveIndex]; |
f88ddf77 | 435 | } |
436 | arith_uint256 Supply(supply); | |
437 | ||
438 | arith_uint256 Reserve(reserves[reserveIndex]); | |
439 | ||
56fe75cb | 440 | arith_uint256 Ratio(weights[reserveIndex]); |
f88ddf77 | 441 | |
56fe75cb | 442 | arith_uint256 BigSatoshi(SATOSHIDEN); |
f88ddf77 | 443 | |
56fe75cb | 444 | return ((Reserve * arith_uint256(SATOSHIDEN) * arith_uint256(SATOSHIDEN)) / (Supply * Ratio)).GetLow64(); |
f88ddf77 | 445 | } |
446 | ||
447 | // return the current price of the fractional reserve in the reserve currency in Satoshis | |
448 | cpp_dec_float_50 GetPriceInReserve(int32_t reserveIndex=0) const | |
449 | { | |
450 | return cpp_dec_float_50(PriceInReserve(reserveIndex)); | |
451 | } | |
452 | ||
56fe75cb | 453 | std::vector<CAmount> PricesInReserve() const |
454 | { | |
455 | std::vector<CAmount> retVal(currencies.size()); | |
456 | for (int i = 0; i < currencies.size(); i++) | |
457 | { | |
458 | retVal[i] = PriceInReserve(i); | |
459 | } | |
460 | return retVal; | |
461 | } | |
462 | ||
463 | // This considers one currency at a time | |
c8c677c9 | 464 | CAmount ConvertAmounts(CAmount inputReserve, CAmount inputFractional, CCurrencyState &newState, int32_t reserveIndex=0) const; |
f88ddf77 | 465 | |
466 | // convert amounts for multi-reserve fractional reserve currencies | |
467 | // one entry in the vector for each currency in and one fractional input for each | |
468 | // currency expected as output | |
c8c677c9 | 469 | std::vector<CAmount> ConvertAmounts(const std::vector<CAmount> &inputReserve, const std::vector<CAmount> &inputFractional, CCurrencyState &newState) const; |
f88ddf77 | 470 | |
471 | CAmount CalculateConversionFee(CAmount inputAmount, bool convertToNative = false, int32_t reserveIndex=0) const; | |
472 | CAmount ReserveFeeToNative(CAmount inputAmount, CAmount outputAmount, int32_t reserveIndex=0) const; | |
473 | ||
56fe75cb | 474 | CAmount ReserveToNative(CAmount reserveAmount, int32_t reserveIndex) const |
475 | { | |
476 | static arith_uint256 bigSatoshi(SATOSHIDEN); | |
477 | arith_uint256 bigAmount(reserveAmount); | |
478 | ||
479 | int64_t price = PriceInReserve(reserveIndex); | |
480 | bigAmount = price ? (bigAmount * bigSatoshi) / arith_uint256(price) : 0; | |
481 | ||
482 | return bigAmount.GetLow64(); | |
483 | } | |
484 | ||
485 | CAmount ReserveToNative(const CCurrencyValueMap &reserveAmounts) const | |
486 | { | |
487 | CAmount nativeOut = 0; | |
488 | for (int i = 0; i < currencies.size(); i++) | |
489 | { | |
490 | auto it = reserveAmounts.valueMap.find(currencies[i]); | |
491 | if (it != reserveAmounts.valueMap.end()) | |
492 | { | |
493 | nativeOut += ReserveToNative(it->second, i); | |
494 | } | |
495 | } | |
3b45a268 | 496 | return nativeOut; |
56fe75cb | 497 | } |
498 | ||
56fe75cb | 499 | CAmount ReserveToNativeRaw(const CCurrencyValueMap &reserveAmounts, const std::vector<CAmount> &exchangeRates) const; |
f88ddf77 | 500 | static CAmount ReserveToNativeRaw(CAmount reserveAmount, CAmount exchangeRate); |
56fe75cb | 501 | static CAmount ReserveToNativeRaw(const CCurrencyValueMap &reserveAmounts, const std::vector<uint160> ¤cies, const std::vector<CAmount> &exchangeRates); |
f88ddf77 | 502 | |
503 | CAmount NativeToReserve(CAmount nativeAmount, int32_t reserveIndex=0) const | |
504 | { | |
56fe75cb | 505 | static arith_uint256 bigSatoshi(SATOSHIDEN); |
f88ddf77 | 506 | arith_uint256 bigAmount(nativeAmount); |
507 | arith_uint256 price = arith_uint256(PriceInReserve()); | |
508 | return ((bigAmount * arith_uint256(price)) / bigSatoshi).GetLow64(); | |
509 | } | |
510 | ||
56fe75cb | 511 | const CCurrencyValueMap &NativeToReserve(std::vector<CAmount> nativeAmount, int32_t reserveIndex=0) const; |
512 | static CAmount NativeToReserveRaw(CAmount nativeAmount, CAmount exchangeRate); | |
513 | CCurrencyValueMap NativeToReserveRaw(const std::vector<CAmount> &, const std::vector<CAmount> &exchangeRates) const; | |
f88ddf77 | 514 | |
515 | UniValue ToUniValue() const; | |
516 | ||
517 | bool IsValid() const | |
518 | { | |
c8c677c9 | 519 | return flags & CCurrencyState::VALID; |
f88ddf77 | 520 | } |
521 | ||
522 | bool IsReserve() const | |
523 | { | |
c8c677c9 | 524 | return flags & CCurrencyState::ISRESERVE; |
f88ddf77 | 525 | } |
526 | ||
527 | std::map<uint160, int32_t> GetReserveMap() const | |
528 | { | |
529 | std::map<uint160, int32_t> retVal; | |
530 | for (int i = 0; i < currencies.size(); i++) | |
531 | { | |
532 | retVal[currencies[i]] = i; | |
533 | } | |
534 | return retVal; | |
535 | } | |
536 | }; | |
537 | ||
c8c677c9 | 538 | class CCoinbaseCurrencyState : public CCurrencyState |
a6e612cc MT |
539 | { |
540 | public: | |
56fe75cb | 541 | CAmount nativeFees; |
542 | CAmount nativeConversionFees; | |
543 | std::vector<CAmount> reserveIn; // reserve currency converted to native | |
544 | std::vector<CAmount> nativeIn; // native currency converted to reserve | |
545 | std::vector<CAmount> reserveOut; // output can have both normal and reserve output value, if non-0, this is spent by the required output transactions | |
546 | std::vector<CAmount> conversionPrice; // calculated price in reserve for all conversions * 100000000 | |
547 | std::vector<CAmount> fees; // fee values in native (or reserve if specified) coins for reserve transaction fees for the block | |
548 | std::vector<CAmount> conversionFees; // total of only conversion fees, which will accrue to the conversion transaction | |
549 | ||
550 | CCoinbaseCurrencyState() : nativeFees(0), nativeConversionFees(0) {} | |
551 | ||
c8c677c9 | 552 | CCoinbaseCurrencyState(const CCurrencyState &CurrencyState, |
56fe75cb | 553 | CAmount NativeFees=0, CAmount NativeConversionFees=0, |
554 | const std::vector<CAmount> &ReserveIn=std::vector<CAmount>(), | |
555 | const std::vector<CAmount> &NativeIn=std::vector<CAmount>(), | |
556 | const std::vector<CAmount> &ReserveOut=std::vector<CAmount>(), | |
557 | const std::vector<CAmount> &ConversionPrice=std::vector<CAmount>(), | |
558 | const std::vector<CAmount> &Fees=std::vector<CAmount>(), | |
559 | const std::vector<CAmount> &ConversionFees=std::vector<CAmount>()) : | |
c8c677c9 | 560 | CCurrencyState(CurrencyState), nativeFees(NativeFees), nativeConversionFees(NativeConversionFees) |
56fe75cb | 561 | { |
562 | if (!reserveIn.size()) reserveIn = std::vector<CAmount>(currencies.size()); | |
563 | if (!nativeIn.size()) nativeIn = std::vector<CAmount>(currencies.size()); | |
564 | if (!reserveOut.size()) reserveOut = std::vector<CAmount>(currencies.size()); | |
565 | if (!conversionPrice.size()) conversionPrice = std::vector<CAmount>(currencies.size()); | |
566 | if (!fees.size()) fees = std::vector<CAmount>(currencies.size()); | |
567 | if (!conversionFees.size()) conversionFees = std::vector<CAmount>(currencies.size()); | |
a6e612cc | 568 | } |
e7e14f44 | 569 | |
56fe75cb | 570 | CCoinbaseCurrencyState(const UniValue &uni); |
571 | ||
572 | CCoinbaseCurrencyState(const std::vector<unsigned char> asVector) | |
e7e14f44 | 573 | { |
56fe75cb | 574 | ::FromVector(asVector, *this); |
e7e14f44 MT |
575 | } |
576 | ||
56fe75cb | 577 | CCoinbaseCurrencyState(const CTransaction &tx, int *pOutIdx=NULL); |
a6e612cc MT |
578 | |
579 | ADD_SERIALIZE_METHODS; | |
580 | ||
581 | template <typename Stream, typename Operation> | |
582 | inline void SerializationOp(Stream& s, Operation ser_action) { | |
c8c677c9 | 583 | READWRITE(*(CCurrencyState *)this); |
56fe75cb | 584 | READWRITE(nativeFees); |
585 | READWRITE(nativeConversionFees); | |
586 | READWRITE(reserveIn); | |
587 | READWRITE(nativeIn); | |
588 | READWRITE(reserveOut); | |
589 | READWRITE(conversionPrice); | |
590 | READWRITE(fees); | |
591 | READWRITE(conversionFees); | |
a6e612cc MT |
592 | } |
593 | ||
594 | std::vector<unsigned char> AsVector() const | |
595 | { | |
596 | return ::AsVector(*this); | |
597 | } | |
598 | ||
56fe75cb | 599 | UniValue ToUniValue() const; |
a6e612cc | 600 | |
56fe75cb | 601 | void ClearForNextBlock() |
602 | { | |
603 | nativeFees = 0; | |
604 | nativeConversionFees = 0; | |
605 | reserveIn = std::vector<CAmount>(currencies.size()); | |
606 | nativeIn = std::vector<CAmount>(currencies.size()); | |
607 | reserveOut = std::vector<CAmount>(currencies.size()); | |
608 | fees = std::vector<CAmount>(currencies.size()); | |
609 | conversionFees = std::vector<CAmount>(currencies.size()); | |
610 | } | |
611 | ||
612 | CCoinbaseCurrencyState MatchOrders(const std::vector<CReserveTransactionDescriptor> &orders, | |
613 | std::vector<CReserveTransactionDescriptor> &reserveFills, | |
614 | std::vector<CReserveTransactionDescriptor> &noFills, | |
615 | std::vector<const CReserveTransactionDescriptor *> &expiredFillOrKills, | |
616 | std::vector<const CReserveTransactionDescriptor *> &rejects, | |
617 | std::vector<CAmount> &exchangeRates, | |
618 | int32_t height, std::vector<CInputDescriptor> &conversionInputs, | |
619 | int64_t maxSerializedSize=LONG_MAX, int64_t *ptotalSerializeSize=NULL, CMutableTransaction *pConversionTx=NULL, | |
620 | bool feesAsReserve=false) const; | |
621 | ||
622 | template <typename NUMBERVECTOR> | |
623 | NUMBERVECTOR AddVectors(const NUMBERVECTOR &a, const NUMBERVECTOR &b) const | |
989b1de1 | 624 | { |
56fe75cb | 625 | const NUMBERVECTOR *shortVec, *longVec; |
626 | int64_t count, max; | |
627 | if (a.size() <= b.size()) | |
989b1de1 | 628 | { |
56fe75cb | 629 | count = a.size(); |
630 | max = b.size(); | |
631 | shortVec = &a; | |
632 | longVec = &b; | |
989b1de1 | 633 | } |
56fe75cb | 634 | else |
989b1de1 | 635 | { |
56fe75cb | 636 | count = b.size(); |
637 | max = a.size(); | |
638 | shortVec = &b; | |
639 | longVec = &a; | |
989b1de1 | 640 | } |
989b1de1 | 641 | |
56fe75cb | 642 | NUMBERVECTOR ret; |
643 | ret.reserve(max); | |
644 | for (int i = 0; i < count; i++) | |
eac5415b | 645 | { |
56fe75cb | 646 | ret[i] = (*longVec)[i] + (*shortVec)[i]; |
eac5415b | 647 | } |
56fe75cb | 648 | for (int i = count; i < max; i++) |
649 | { | |
650 | ret[i] = (*longVec)[i]; | |
651 | } | |
652 | return ret; | |
a6e612cc | 653 | } |
56fe75cb | 654 | }; |
a6e612cc | 655 | |
56fe75cb | 656 | class CReserveInOuts |
657 | { | |
658 | public: | |
659 | int64_t reserveIn; | |
660 | int64_t reserveOut; | |
661 | int64_t reserveOutConverted; | |
662 | int64_t nativeOutConverted; | |
663 | int64_t reserveConversionFees; | |
664 | CReserveInOuts() : reserveIn(0), reserveOut(0), reserveOutConverted(0), nativeOutConverted(0), reserveConversionFees(0) {} | |
665 | CReserveInOuts(int64_t ReserveIn, int64_t ReserveOut, int64_t ReserveOutConverted, int64_t NativeOutConverted, int64_t ReserveConversionFees) : | |
666 | reserveIn(ReserveIn), | |
667 | reserveOut(ReserveOut), | |
668 | reserveOutConverted(ReserveOutConverted), | |
669 | nativeOutConverted(NativeOutConverted), | |
670 | reserveConversionFees(ReserveConversionFees) {} | |
671 | }; | |
a6e612cc | 672 | |
56fe75cb | 673 | class CReserveTransactionDescriptor |
674 | { | |
675 | public: | |
676 | enum { | |
677 | IS_VALID=1, // known to be valid | |
678 | IS_REJECT=2, // if set, tx is known to be invalid | |
679 | IS_RESERVE=4, // if set, this transaction affects reserves and/or price if mined | |
680 | IS_RESERVEEXCHANGE=8, // is this a reserve/exchange transaction? | |
681 | IS_LIMIT=0x10, // if reserve exchange, is it a limit order? | |
682 | IS_FILLORKILL=0x20, // If set, this can expire | |
683 | IS_FILLORKILLFAIL=0x40, // If set, this is an expired fill or kill in a valid tx | |
684 | IS_IMPORT=0x80, // If set, this is an expired fill or kill in a valid tx | |
685 | IS_EXPORT=0x100, // If set, this is an expired fill or kill in a valid tx | |
686 | IS_IDENTITY=0x200, // If set, this is an identity definition or update | |
687 | IS_IDENTITY_DEFINITION=0x400, // If set, this is an identity definition | |
688 | IS_HIGH_FEE=0x800 // If set, this may have "absurdly high fees" | |
689 | }; | |
b7c685b8 | 690 | |
56fe75cb | 691 | const CTransaction *ptx; // pointer to the actual transaction if valid |
692 | uint16_t flags; // indicates transaction state | |
693 | std::map<uint160, CReserveInOuts> currencies; // currency entries in this transaction | |
694 | int16_t numBuys = 0; // each limit conversion that is valid before a certain block should account for FILL_OR_KILL_FEE | |
695 | int16_t numSells = 0; | |
696 | int16_t numTransfers = 0; // number of transfers, each of which also requires a transfer fee | |
697 | CAmount nativeIn = 0; | |
698 | CAmount nativeOut = 0; | |
699 | CAmount nativeConversionFees = 0; // non-zero only if there is a conversion | |
700 | std::vector<std::pair<int, CReserveExchange>> vRex; // index and rehydrated, validated reserve exchange outputs | |
e7e14f44 | 701 | |
56fe75cb | 702 | CReserveTransactionDescriptor() : |
703 | flags(0), | |
704 | ptx(NULL), | |
705 | numBuys(0), // each limit conversion that is valid before a certain block should account for FILL_OR_KILL_FEE | |
706 | numSells(0), | |
707 | numTransfers(0), | |
708 | nativeIn(0), | |
709 | nativeOut(0), | |
710 | nativeConversionFees(0) {} // non-zero only if there is a conversion, stored vs. calculated to get exact number with each calculated seperately | |
e7e14f44 | 711 | |
56fe75cb | 712 | CReserveTransactionDescriptor(const CTransaction &tx, const CCoinsViewCache &view, int32_t nHeight); |
e7e14f44 | 713 | |
56fe75cb | 714 | bool IsReject() const { return flags & IS_REJECT; } |
715 | bool IsValid() const { return flags & IS_VALID && !IsReject(); } | |
716 | bool IsReserve() const { return IsValid() && flags & IS_RESERVE; } | |
717 | bool IsReserveExchange() const { return flags & IS_RESERVEEXCHANGE; } | |
718 | bool IsLimit() const { return flags & IS_LIMIT; } | |
719 | bool IsFillOrKill() const { return flags & IS_FILLORKILL; } | |
720 | bool IsMarket() const { return IsReserveExchange() && !IsLimit(); } | |
721 | bool IsFillOrKillFail() const { return flags & IS_FILLORKILLFAIL; } | |
722 | bool IsIdentity() const { return flags & IS_IDENTITY; } | |
723 | bool IsIdentityDefinition() const { return flags & IS_IDENTITY_DEFINITION; } | |
724 | bool IsHighFee() const { return flags & IS_HIGH_FEE; } | |
41f170fd | 725 | |
56fe75cb | 726 | static CAmount CalculateConversionFee(CAmount inputAmount); |
727 | static CAmount CalculateAdditionalConversionFee(CAmount inputAmount); | |
a6e612cc | 728 | |
56fe75cb | 729 | CAmount NativeFees() const |
a6e612cc | 730 | { |
56fe75cb | 731 | return nativeIn - nativeOut; // native out converted does not include conversion |
a6e612cc | 732 | } |
45d7e5d5 | 733 | |
56fe75cb | 734 | CCurrencyValueMap ReserveFees() const |
45d7e5d5 | 735 | { |
56fe75cb | 736 | CCurrencyValueMap retFees; |
737 | for (auto &one : currencies) | |
738 | { | |
739 | CAmount oneFee = one.second.reserveIn - one.second.reserveOut; | |
740 | if (oneFee) | |
741 | { | |
742 | retFees.valueMap[one.first] = oneFee; | |
743 | } | |
744 | } | |
745 | return retFees; | |
45d7e5d5 | 746 | } |
a6e612cc | 747 | |
c8c677c9 | 748 | CAmount AllFeesAsNative(const CCurrencyState ¤cyState) const; |
749 | CAmount AllFeesAsNative(const CCurrencyState ¤cyState, const std::vector<CAmount> &exchangeRates) const; | |
750 | CCurrencyValueMap AllFeesAsReserve(const CCurrencyState ¤cyState, int defaultReserve=0) const; | |
751 | CCurrencyValueMap AllFeesAsReserve(const CCurrencyState ¤cyState, const std::vector<CAmount> &exchangeRates, int defaultReserve=0) const; | |
989b1de1 | 752 | |
56fe75cb | 753 | // does not check for errors |
754 | void AddReserveInput(const uint160 ¤cy, CAmount value); | |
755 | void AddReserveOutput(const uint160 ¤cy, CAmount value); | |
756 | void AddReserveOutConverted(const uint160 ¤cy, CAmount value); | |
757 | void AddNativeOutConverted(const uint160 ¤cy, CAmount value); | |
758 | void AddReserveConversionFees(const uint160 ¤cy, CAmount value); | |
989b1de1 | 759 | |
56fe75cb | 760 | CCurrencyValueMap ReserveInputMap() const; |
761 | CCurrencyValueMap ReserveOutputMap() const; | |
762 | CCurrencyValueMap ReserveOutConvertedMap() const; | |
763 | CCurrencyValueMap NativeOutConvertedMap() const; | |
764 | CCurrencyValueMap ReserveConversionFeesMap() const; | |
41f170fd | 765 | |
56fe75cb | 766 | // returns vectors in same size and order as reserve currencies |
c8c677c9 | 767 | std::vector<CAmount> ReserveInputVec(const CCurrencyState &cState) const; |
768 | std::vector<CAmount> ReserveOutputVec(const CCurrencyState &cState) const; | |
769 | std::vector<CAmount> ReserveOutConvertedVec(const CCurrencyState &cState) const; | |
770 | std::vector<CAmount> NativeOutConvertedVec(const CCurrencyState &cState) const; | |
771 | std::vector<CAmount> ReserveConversionFeesVec(const CCurrencyState &cState) const; | |
41f170fd | 772 | |
56fe75cb | 773 | void AddReserveOutput(const CTokenOutput &ro) |
41f170fd | 774 | { |
56fe75cb | 775 | flags |= IS_RESERVE; |
776 | if (!(flags & IS_IMPORT)) | |
777 | { | |
778 | AddReserveOutput(ro.currencyID, ro.nValue); | |
779 | } | |
41f170fd MT |
780 | } |
781 | ||
56fe75cb | 782 | // is boolean, since it can fail, which would render the tx invalid |
783 | void AddReserveExchange(const CReserveExchange &rex, int32_t outputIndex, int32_t nHeight); | |
989b1de1 | 784 | |
56fe75cb | 785 | void AddReserveTransfer(CReserveTransfer &rt) |
989b1de1 | 786 | { |
56fe75cb | 787 | flags |= IS_RESERVE; |
788 | if (!(flags & IS_IMPORT)) | |
789 | { | |
790 | AddReserveOutput(rt.currencyID, rt.nValue + rt.nFees); | |
791 | numTransfers++; | |
792 | } | |
989b1de1 | 793 | } |
41f170fd | 794 | |
56fe75cb | 795 | CMutableTransaction &AddConversionInOuts(CMutableTransaction &conversionTx, |
796 | std::vector<CInputDescriptor> &conversionInputs, | |
797 | const CCurrencyValueMap &exchangeRates=CCurrencyValueMap(), | |
c8c677c9 | 798 | const CCurrencyState *pCurrencyState=nullptr) const; |
41f170fd | 799 | |
56fe75cb | 800 | bool AddReserveTransferImportOutputs(const uint160 ¤cySourceID, |
09551a1c | 801 | const CCurrencyDefinition &systemDest, |
56fe75cb | 802 | const std::vector<CBaseChainObject *> &exportObjects, |
803 | std::vector<CTxOut> &vOutputs); | |
989b1de1 MT |
804 | }; |
805 | ||
a6e612cc | 806 | #endif // PBAAS_RESERVES_H |