diff --git a/src/Makefile.am b/src/Makefile.am index 7e1647a6c..e6c1fbb14 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -524,6 +524,11 @@ if GLIBC_BACK_COMPAT libbitcoin_util_a_SOURCES += compat/glibc_compat.cpp endif +if ENABLE_TESTS +libbitcoin_server_a_SOURCES += rpc/testtransactions.cpp +endif + + # cli: zcash-cli libbitcoin_cli_a_CPPFLAGS = $(AM_CPPFLAGS) $(BITCOIN_INCLUDES) libbitcoin_cli_a_CXXFLAGS = $(AM_CXXFLAGS) $(PIE_FLAGS) diff --git a/src/assetchains.old b/src/assetchains.old index a0cbd3b9c..fd472eff9 100755 --- a/src/assetchains.old +++ b/src/assetchains.old @@ -51,3 +51,4 @@ echo $pubkey ./komodod -pubkey=$pubkey -ac_name=MORTY -ac_supply=90000000000 -ac_reward=100000000 -ac_cc=3 -addnode=138.201.136.145 & ./komodod -pubkey=$pubkey -ac_name=VOTE2019 -ac_supply=123651638 -ac_public=1 -addnode=95.213.238.98 & ./komodod -pubkey=$pubkey -ac_name=KOIN -ac_supply=125000000 -addnode=3.0.32.10 & +~/hush3/src/komodod -pubkey=$pubkey -ac_name=HUSH3 -ac_sapling=1 -ac_reward=0,1125000000,562500000 -ac_halving=129,340000,840000 -ac_end=128,340000,5422111 -ac_eras=3 -ac_blocktime=150 -ac_cc=2 -ac_ccenable=228,234,235,236,241 -ac_founders=1 -ac_supply=6178674 -ac_perc=11111111 -clientname=GoldenSandtrout -addnode=188.165.212.101 -ac_cclib=hush3 -ac_script=76a9145eb10cf64f2bab1b457f1f25e658526155928fac88ac & diff --git a/src/cc/CCPrices.h b/src/cc/CCPrices.h index 3dbe6a87c..238131d59 100644 --- a/src/cc/CCPrices.h +++ b/src/cc/CCPrices.h @@ -17,34 +17,41 @@ #ifndef CC_PRICES_H #define CC_PRICES_H +#include "komodo_defs.h" #include "CCinclude.h" int32_t komodo_priceget(int64_t *buf64,int32_t ind,int32_t height,int32_t numblocks); +extern void GetKomodoEarlytxidScriptPub(); +extern CScript KOMODO_EARLYTXID_SCRIPTPUB; -#define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) +// #define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) // defined in komodo_defs.h #define PRICES_TXFEE 10000 #define PRICES_MAXLEVERAGE 777 #define PRICES_SMOOTHWIDTH 1 #define KOMODO_MAXPRICES 2048 // must be power of 2 and less than 8192 -#define KOMODO_PRICEMASK (~(KOMODO_MAXPRICES - 1)) -#define PRICES_WEIGHT (KOMODO_MAXPRICES * 1) -#define PRICES_MULT (KOMODO_MAXPRICES * 2) -#define PRICES_DIV (KOMODO_MAXPRICES * 3) -#define PRICES_INV (KOMODO_MAXPRICES * 4) -#define PRICES_MDD (KOMODO_MAXPRICES * 5) -#define PRICES_MMD (KOMODO_MAXPRICES * 6) -#define PRICES_MMM (KOMODO_MAXPRICES * 7) -#define PRICES_DDD (KOMODO_MAXPRICES * 8) +#define KOMODO_PRICEMASK (~(KOMODO_MAXPRICES - 1)) // actually 1111 1000 0000 0000 +#define PRICES_WEIGHT (KOMODO_MAXPRICES * 1) // 0000 1000 0000 0000 +#define PRICES_MULT (KOMODO_MAXPRICES * 2) // 0001 0000 0000 0000 +#define PRICES_DIV (KOMODO_MAXPRICES * 3) // 0001 1000 0000 0000 +#define PRICES_INV (KOMODO_MAXPRICES * 4) // 0010 0000 0000 0000 +#define PRICES_MDD (KOMODO_MAXPRICES * 5) // 0010 1000 0000 0000 +#define PRICES_MMD (KOMODO_MAXPRICES * 6) // 0011 0000 0000 0000 +#define PRICES_MMM (KOMODO_MAXPRICES * 7) // 0011 1000 0000 0000 +#define PRICES_DDD (KOMODO_MAXPRICES * 8) // 0100 0000 0000 0000 + +#define PRICES_NORMFACTOR (int64_t)(SATOSHIDEN) +#define PRICES_POINTFACTOR (int64_t)10000 bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn); // CCcustom -UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic); -UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount); -UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid); -UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight); -UniValue PricesCashout(uint64_t txfee,uint256 bettxid); +UniValue PricesBet(int64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic); +UniValue PricesAddFunding(int64_t txfee,uint256 bettxid,int64_t amount); +UniValue PricesSetcostbasis(int64_t txfee,uint256 bettxid); +UniValue PricesRekt(int64_t txfee,uint256 bettxid,int32_t rektheight); +UniValue PricesCashout(int64_t txfee,uint256 bettxid); UniValue PricesInfo(uint256 bettxid,int32_t refheight); -UniValue PricesList(); +UniValue PricesList(uint32_t filter, CPubKey mypk); +UniValue PricesGetOrderbook(); #endif diff --git a/src/cc/CCinclude.h b/src/cc/CCinclude.h index 4ca73b6d7..75d804df5 100644 --- a/src/cc/CCinclude.h +++ b/src/cc/CCinclude.h @@ -298,6 +298,7 @@ UniValue ValueFromAmount(const CAmount& amount); int64_t TotalPubkeyNormalInputs(const CTransaction &tx, const CPubKey &pubkey); int64_t TotalPubkeyCCInputs(const CTransaction &tx, const CPubKey &pubkey); +inline std::string STR_TOLOWER(const std::string &str) { std::string out; for (std::string::const_iterator i = str.begin(); i != str.end(); i++) out += std::tolower(*i); return out; } // bitcoin LogPrintStr with category "-debug" cmdarg support for C++ ostringstream: #define CCLOG_INFO 0 diff --git a/src/cc/oracles.cpp b/src/cc/oracles.cpp index ef457fe82..a5d61d404 100644 --- a/src/cc/oracles.cpp +++ b/src/cc/oracles.cpp @@ -935,13 +935,13 @@ UniValue OracleFormat(uint8_t *data,int32_t datalen,char *format,int32_t formatl if ( j >= datalen ) break; } - return(obj); + return(str); } UniValue OracleDataSamples(uint256 reforacletxid,uint256 batontxid,int32_t num) { - UniValue result(UniValue::VOBJ),a(UniValue::VARR); CTransaction tx,oracletx; uint256 hashBlock,btxid,oracletxid; - CPubKey pk; std::string name,description,format; int32_t numvouts,n=0; std::vector data; char *formatstr = 0; + UniValue result(UniValue::VOBJ),b(UniValue::VARR); CTransaction tx,oracletx; uint256 hashBlock,btxid,oracletxid; + CPubKey pk; std::string name,description,format; int32_t numvouts,n=0; std::vector data; char str[67], *formatstr = 0; result.push_back(Pair("result","success")); if ( GetTransaction(reforacletxid,oracletx,hashBlock,false) != 0 && (numvouts=oracletx.vout.size()) > 0 ) @@ -954,15 +954,18 @@ UniValue OracleDataSamples(uint256 reforacletxid,uint256 batontxid,int32_t num) { if ( (formatstr= (char *)format.c_str()) == 0 ) formatstr = (char *)""; + UniValue a(UniValue::VARR); a.push_back(OracleFormat((uint8_t *)data.data(),(int32_t)data.size(),formatstr,(int32_t)format.size())); + a.push_back(uint256_str(str,batontxid)); + b.push_back(a); batontxid = btxid; - if ( ++n >= num ) + if ( ++n >= num && num != 0) break; } else break; } } } - result.push_back(Pair("samples",a)); + result.push_back(Pair("samples",b)); return(result); } diff --git a/src/cc/payments.cpp b/src/cc/payments.cpp index f78474e12..6311e393d 100644 --- a/src/cc/payments.cpp +++ b/src/cc/payments.cpp @@ -1192,6 +1192,7 @@ UniValue PaymentsAirdrop(struct CCcontract_info *cp,char *jsonstr) lockedblocks = juint(jitem(params,0),0); minrelease = juint(jitem(params,1),0); minimum = juint(jitem(params,2),0); + if ( minimum < 10000 ) minimum = 10000; top = juint(jitem(params,3),0); bottom = juint(jitem(params,4),0); fixedAmount = juint(jitem(params,5),0); // fixed amount is a flag, set to 7 does game mode, 0 normal snapshot, anything else fixed allocations. diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 407cd81fa..27529bca4 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -11,137 +11,113 @@ * * * Removal or modification of this copyright notice is prohibited. * * * - ******************************************************************************/ + ***************************************************************************** +To create payments plan start a chain with the following ac_params: + -ac_snapshot=1440 (or for test chain something smaller, if you like.) + - this enables the payments airdrop cc to work. + -ac_earlytxidcontract=237 (Eval code for prices cc.) + - this allows to know what contract this chain is paying with the scriptpubkey in the earlytxid op_return. + +./komodod -ac_name=TESTPRC -ac_supply=100000000 -ac_reward=1000000000 -ac_nk=96,5 -ac_blocktime=20 -ac_cc=2 -ac_snapshot=50 -ac_sapling=1 -ac_earlytxidcontract=237 -testnode=1 -gen -genproclimit=1 + +Then in very early block < 10 or so, do paymentsairdrop eg. + `./komodo-cli -ac_name=TESTPRC paymentsairdrop '[10,10,0,3999,0,0]' +Once this tx is confirmed, do `paymentsfund` and decode the raw hex. You can edit the source to not send the tx if requried. +Get the full `hex` of the vout[0] that pays to CryptoCondition. then place it on chain with the following command: with the hex you got in place of the hex below. + './komodo-cli -ac_name=TESTPRC opreturn_burn 1 2ea22c8020292ba5c8fd9cc89b12b35bf8f5d00196990ecbb06102b84d9748d11d883ef01e81031210008203000401cc' +copy the hex, and sendrawtransaction, copy the txid returned. +this places the scriptpubkey that pays the plan into an op_return before block 100, allowing us to retreive it, and nobody to change it. +Restart the daemon with -earlytxid= eg: + +./komodod -ac_name=TESTPRC -ac_supply=100000000 -ac_reward=1000000000 -ac_nk=96,5 -ac_blocktime=20 -ac_cc=2 -ac_snapshot=50 -ac_sapling=1 -ac_earlytxidcontract=237 -earlytxid=cf89d17fb11037f65c160d0749dddd74dc44d9893b0bb67fe1f96c1f59786496 -testnode=1 -gen -genproclimit=1 + +mine the chain past block 100, preventing anyone else, creating another payments plan on chain before block 100. + +We call the following in Validation and RPC where the address is needed. +if ( ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_EARLYTXID_SCRIPTPUB.size() == 0 ) + GetKomodoEarlytxidScriptPub(); + +This will fetch the op_return, calculate the scriptPubKey and save it to the global. +On daemon restart as soon as validation for BETTX happens the global will be filled, after this the transaction never needs to be looked up again. +GetKomodoEarlytxidScriptPub is on line #2080 of komodo_bitcoind.h + */ #include "CCassets.h" #include "CCPrices.h" +#include + +#define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos) + +#define NVOUT_CCMARKER 1 +#define NVOUT_NORMALMARKER 3 + +typedef struct OneBetData { + int64_t positionsize; + int32_t firstheight; + int64_t costbasis; + int64_t profits; + + OneBetData() { positionsize = 0; firstheight = 0; costbasis = 0; profits = 0; } // it is important to clear costbasis as it will be calculated as minmax from inital value 0 +} onebetdata; + +typedef struct BetInfo { + int64_t averageCostbasis, firstprice, lastprice, liquidationprice, equity; + int64_t rektfee; + int32_t lastheight; + int16_t leverage; + bool isOpen, isRekt; + uint256 tokenid; + + std::vector parsed; + std::vector bets; + CPubKey pk; + + BetInfo() { + averageCostbasis = firstprice = lastprice = liquidationprice = equity = 0; + lastheight = 0; + leverage = 0; + rektfee = 0; + isOpen = isRekt = false; + } +} BetInfo; + /* CBOPRET creates trustless oracles, which can be used for making a synthetic cash settlement system based on real world prices; 0.5% fee based on betamount, NOT leveraged betamount!! 0.1% collected by price basis determinant 0.2% collected by rekt tx + + PricesBet -> +/-leverage, amount, synthetic -> opreturn includes current price + funds are locked into 1of2 global CC address + for first day, long basis is MAX(correlated,smoothed), short is MIN() + reference price is the smoothed of the previous block + if synthetic value + amount goes negative, then anybody can rekt it to collect a rektfee, proof of rekt must be included to show cost basis, rekt price + original creator can liquidate at anytime and collect (synthetic value + amount) from globalfund + 0.5% of bet -> globalfund + + PricesStatus -> bettxid maxsamples returns initial params, cost basis, amount left, rekt:true/false, rektheight, initial synthetic price, current synthetic price, net gain + + PricesRekt -> bettxid height -> 0.1% to miner, rest to global CC + + PricesClose -> bettxid returns (synthetic value + amount) + + PricesList -> all bettxid -> list [bettxid, netgain] + + +*/ - At the simplest, prices CC allows to make leveraged cash settled bets on long and short BTCUSD: - BTCUSD, 1 with positive leverage parameter up to 777 - BTCUSD, 1 with negative leverage parameter up to -777 - - These specific limits of 0.5%, 0.1%, 0.2%, 777 should be able to be changed based on #define - - Another configuration is to send the 0.4% (or 0.2%) fees to a fee collection address (this is not currently implemented, but would be needed for systems that dont use -ac_perc to collect a global override) - - The definition of the synthetic instrument that is being tracked is actually defined with a forth type of syntax and is quite flexible to allow unlimited combinations and weights, but that is an independent aspect and will be covered later. - - Let us discuss the simplest synthetic case of a long or short of BTCUSD (or any other direct pricepoint from the trustless oracle). If you look at the charts, you will see the blue line that is direct from the miners (and therefore cant be directly used). The orange line is the 51% correlated price that is deterministically random selected from the prior 24 hours. And finally the green line which is simply the average value from the priot 24 hours. - - We will assume that the orange and green prices are able to be deterministically calculated from the raw coinbase data (blue). The prices rpc is currently working reasonably well and appears to return deterministic prices, however for use in the prices CC, it is often needed to find just a single data point. To that effect there is the temporary function prices_syntheticprice, which uses a bruteforce and totally inefficient way to calculate the correlated and smoothed prices. This inefficient process will need to be changed to a permanent storage of correlated and smoothed prices for each trustless oracle that is updated each block. This will then allow to directly lookup each value for any of the trustless prices. Such speed will indeed be needed to scale up usage of a REKT chain. - - Since the mined prices can be manipulated by any miner, combined with the +/-1% tolerance, it would be possible for an external miner to obtain a large hashrate and skew the price +1%, then to -1% to create a 2% price difference, which under high leverage might be able to generate a large profit. - - To avoid this precisely controllable biasing, the 51% correlation of past day is used (orange), which makes the price jump around a lot. The green line that sums the prior day is needed to remove this jitter back to the average value. However, this creates a 1.5 day delay to the price movement of the smoothed price compared to the current price. What this means is that if we want to use the green line to settle prices automatically, a trivial way to make money is to bet in the direct that the mined prices are relative to the smoothed prices. Given 1.5 day head start, it wont be any mystery which direction the smoothed line will move in the next 24 hours. - - So this makes the smoothed price unusable for settling the bets with. However, we cant use the correlated price either, as it would also allow to make bets when the correlated price picked a significantly lower/higher price than the raw price. The solution is to have a floating basis for the costbasis of the bet. In order to allow finding the right costbasis, for long bets the maximum price between the correlated and smoothed price is needed over the first day after the bet. For short bets, the minimum price is needed. - - What this means is that the actual starting price for a bet wont be known for sure when the bet is made, but if the price is relatively stable, there wont be much of a change. If there is a lot of recent volatility, then the starting price will have a high variability. In order to calculate the costbasis, it currently requires many calculations to find the MAX (or MIN) for the first day. Even when this is made to be a lookup, it still requires to issue a transaction to change the state of a bet from a "starting" state to a "in effect" state and this state change is indicated by whether the bettx vout that contains the costbasis utxo is spent or not. - - Once a bet goes into effect, then block by block, it is checked by the decentralized set of rekt scanners if it is rekt or not, in this case for double the reward for calculating the cost basis. This fully decentralized mechanism ensures that some node will decide to collect the free money and ensures that the bankroll is growing. To miss a rekt bet and not close it out when it can be is to allow it to survive for another block, which can change it profitability from negative to positive. - - Which comes to how profits are calculated. Once the costbasis is locked, then the profit calculation can be made based on the ratio between the costbasis and the current price, multiplied by the leverage. So, if a long position gains 10% at a 10x leverage, then approximately the entire bet amount will be made, ie. a double. Similarily with a short position, a 10% drop in price at 10x leverage will double the bet amount. Since it takes a full day to establish the costbasis, it is not possible to allow changing the costbasis for an existing bet, however it is possible to add funds so that it is less likely to be rekt. The sum of the initial bet and all added funds must be greater than the loss at every block to prevent a position from being rekt. To make it simple to calculate the amount of funds added, another bettx vout is used as a baton. Techniques similar to rogue CC to find where the bettx vout was spent and looking at each such transaction to find the total funds added can be used. - - The once that makes the bet is able to add funds or cashout at any block, as long as it isnt rekt. If it is rekt, the bettor is able to collect the rekt fee, so at least that is some consolation, but it is advised to increase the total funding to avoid being rekt. On a cashout all the funds that were bet adjusted by the profits can be collected. - - Hopefully the above description makes it clear what the following rpc calls should be doing: - - UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic) - funds are locked into 1of2 global CC address - for first day, long basis is MAX(correlated,smoothed), short is MIN() - reference price is the smoothed of the previous block - if synthetic value + amount goes negative, then anybody can rekt it to collect a rektfee, proof of rekt must be included to show cost basis, rekt price - original creator can liquidate at anytime and collect (synthetic value + amount) from globalfund - 0.5% of bet -> globalfund +int32_t prices_syntheticprofits(int64_t &costbasis, int32_t firstheight, int32_t height, int16_t leverage, std::vector vec, int64_t positionsize, int64_t &profits, int64_t &outprice); - UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) - add funding to an existing bet, doesnt change the profit calcs but does make the bet less likely to be rekt - - UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) - in the first day from the bet, the costbasis can (and usually does) change based on the MAX(correlated,smoothed) for long and MIN() for shorts - to calculate this requires a bit of work, so whatever node is first to get the proper calculation confirmed, gets a 0.1% costbasis fee - - UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight) - similarily, any node can submit a rekt tx and cash in on 0.2% fee if their claim is confirmed - - UniValue PricesCashout(uint64_t txfee,uint256 bettxid) - only the actually creator of bet is able to cashout and only if it isnt rekt at that moment - - UniValue PricesInfo(uint256 bettxid,int32_t refheight) - all relevant info about a bet - - UniValue PricesList() - a list of all pending and completed bets in different lists - - - Appendix Synthetic position definition language: - - Let us start from the familiar BTCUSD nomenclature. this is similar (identical) to forex EURUSD and the equivalent. Notice the price needs two things as it is actually a ratio, ie. BTCUSD is how many USD does 1 BTC get converted to. It can be thought of as the value of BTC/USD, in other words a ratio. - - The value of BTC alone, or USD alone, it is actually quite an abstract issue and it can only be approximated. Specific ways of how to do this can be discussed later, for now it is only important to understand that all prices are actually ratios. - - And these ratios work as normal algebra does. So a/b * b/c == a/c! You can try this out with BTCUSD and USDJPY -> BTC/USD * USD/JPY -> BTC/JPY or the BTCJPY price - - division is the reciprocal, so BTCUSD reciprocated is USDBTC - - Without getting into all the possible combinations of things, let us first understand what the language allows. It uses a stack based language, where individual tokens update the state. The final state needs to have an empty stack and it will have a calculated price. - - The most important is pushing a specific price onto the stack. All the correlated and smoothed prices are normalized so it has each integer unit equal to 0.00000001, amounts above 100 million are above one, amounts less are below one. 100 million is the unit constant. - - In the prices CC synthetic definition language, the symbol that is returned pushes the adjusted price to the stack. The adjustment is selecting between the correlated and smoothed if within a day from the bet creation, or just the smoothed if after a day. You can have a maximum depth of 3, any more than that and it should return an error. - - This means there are operations that are possible on 1, 2 and 3 symbols. For 1 symbol, it is easy, the only direct operation is the inverse, which is represented by "!". All items in the language are separated by "," - - "BTCUSD, !" - - The above is the inverse or USD/BTC, which is another way to short BTCUSD. It is also possible to short the entire synthetic with a negative leverage. The exact results of a -1 leverage and inverse, might not be exact due to the math involved with calculating the profit, but it should generally be similar. - - For two symbols, there is a bit more we can do, namely multiply and divide, combined with inverting, however to simplify the language any inverting is required to be done by the ordering of the symbols and usage of multiply or divide. multiply is "*" and divide is "/" the top of the stack (last to be pushed) is the divisor, the one right before the divisor is the numerator. - - "BTCUSD, USDJPY, *" <- That will create a BTCJPY synthetic - - "BTCEUR, BTCUSD, /" <- That will create a USDEUR synthetic - - If you experiment around with this, you will see that given two symbols and the proper order and * or /, you can always create the synthetic that you want, assuming what you want is the cancelling out of the term in common with the two symbols and the resulting ratio is between the two unique terms. - */ +// helpers: -// Now we get to the three symbol operations, which there are 4 of *//, **/, *** and /// - -/* - these four operators work on the top of the stack in left to right order as the syntax of the definition lists it, though it is even possible to have the value from an earlier computation on the top of the stack. Ultimately all three will be multiplied together, so a * in a spot means it us used without changing. A / means its inverse will be used. - - "KMDBTC, BTCUSD, USDJPY, ***" <- this would create a KMDJPY synthetic. The various location of the / to make an inverse is to orient the raw symbol into the right orientation as the pricefeed is only getting one orientation of the ratio. - - So now we have covered all ways to map 1, 2 and 3 values on the top of the stack. A value can be on the top of the stack directly from a symbol, or as the result of some 1, 2 or 3 symbol operation. With a maximum stack depth of 3, it might require some experimentation to get a very complex synthetic to compile. Alternately, a stack deeper than 3 might be the acceptable solution if there are a family of synthetics that need it. - - At this point, it is time to describe the weights. It turns out that all above examples are incomplete and the synthetic descriptions are all insufficient and should generate an error. The reason is that the actual synthetic price is the value of the accumulator, which adds up all the weighted prices. In order to assign a weight to a price value on the stack, you simply use a number that is less than 2048. - - What such a weight number does, is consume the top of the stack, which also must be at depth of 1 and adds it to the accumulator. This allows combining multiple different synthetics into a meta synthetic, like for an aggregated index that is weighted by marketcap, or whatever other type of ratio trade that is desired. - - "BTCUSD, 1000, ETHBTC, BTCUSD, *, 300" -> that creates a dual index of BTCUSD and ETHUSD with a 30% ETH weight - - all weight operations consumes the one and only stack element and adds its weight to the accumulator, using this a very large number of terms can be all added together. Using inverses, allows to get the short direction into the equation mixed with longs, but all terms must be positive. If you want to create a spread between two synthetics, you need to create two different synthetics and go long with one and short with another. - - "BTCUSD, 1" with leverage -2 and "KMDBTC, BTCUSD, *, 1" with leverage 1 this will setup a +KMDUSD -2 BTCUSD spread when the two are combined, and would be at breakeven when KMDUSD gains 2x more percentage wise than BTC does. anytime KMD gains more, will be positive, if the gains are less, would be negative. - - Believe it or not, the string to binary compiler for synthetic description and interpretation of it is less than 200 lines of code. - - todo: complete all the above, bet tokens, cross chain prices within cluster These specific limits of 0.5%, 0.1%, 0.2%, 777 should be able to be changed based on #define - - Another configuration is to send the 0.4% (or 0.2%) fees to a fee collection scriptPubKey (this is not currently implemented, but would be needed for systems that dont use -ac_perc to collect a global override) this requires adding new vouts - - Modification: in the event there is one price in the accumulator and one price on the stack at the end, then it is a (A - B) spread - - Monetizations should be sent to: RGsWqwFviaNJSbmgWi6338NL2tKkY7ZqKL - - */ +// returns true if there are only digits and no alphas or slashes in 's' +inline bool is_weight_str(std::string s) { + return + std::count_if(s.begin(), s.end(), [](unsigned char c) { return std::isdigit(c); } ) > 0 && + std::count_if(s.begin(), s.end(), [](unsigned char c) { return std::isalpha(c) || c == '/'; } ) == 0; +} // start of consensus code @@ -156,8 +132,9 @@ CScript prices_betopret(CPubKey mypk,int32_t height,int64_t amount,int16_t lever uint8_t prices_betopretdecode(CScript scriptPubKey,CPubKey &pk,int32_t &height,int64_t &amount,int16_t &leverage,int64_t &firstprice,std::vector &vec,uint256 &tokenid) { std::vector vopret; uint8_t e,f; + GetOpReturnData(scriptPubKey,vopret); - if ( vopret.size() > 2 && E_UNMARSHAL(vopret,ss >> e; ss >> f; ss >> pk; ss >> height; ss >> amount; ss >> leverage; ss >> firstprice; ss >> vec; ss >> tokenid) != 0 && e == EVAL_PRICES && f == 'B' ) + if (vopret.size() > 2 && E_UNMARSHAL(vopret, ss >> e; ss >> f; ss >> pk; ss >> height; ss >> amount; ss >> leverage; ss >> firstprice; ss >> vec; ss >> tokenid) != 0 && e == EVAL_PRICES && f == 'B') { return(f); } @@ -218,35 +195,364 @@ uint8_t prices_finalopretdecode(CScript scriptPubKey,uint256 &bettxid,int64_t &p return(0); } +// price opret basic validation and retrieval +static uint8_t PricesCheckOpret(const CTransaction & tx, vscript_t &opret) +{ + if (tx.vout.size() > 0 && GetOpReturnData(tx.vout.back().scriptPubKey, opret) && opret.size() > 2 && opret.begin()[0] == EVAL_PRICES && IS_CHARINSTR(opret.begin()[1], "BACF")) + return opret.begin()[1]; + else + return (uint8_t)0; +} + +// validate bet tx helper +static bool ValidateBetTx(struct CCcontract_info *cp, Eval *eval, const CTransaction & bettx) +{ + uint256 tokenid; + int64_t positionsize, firstprice; + int32_t firstheight; + int16_t leverage; + CPubKey pk, pricespk; + std::vector vec; + if ( ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_EARLYTXID_SCRIPTPUB.size() == 0 ) + GetKomodoEarlytxidScriptPub(); + if (bettx.vout.size() < 5 || bettx.vout.size() > 6) + return eval->Invalid("incorrect vout number for bet tx"); + + vscript_t opret; + if( prices_betopretdecode(bettx.vout.back().scriptPubKey, pk, firstheight, positionsize, leverage, firstprice, vec, tokenid) != 'B') + return eval->Invalid("cannot decode opreturn for bet tx"); + + pricespk = GetUnspendable(cp, 0); + + if (MakeCC1vout(cp->evalcode, bettx.vout[0].nValue, pk) != bettx.vout[0]) + return eval->Invalid("cannot validate vout0 in bet tx with pk from opreturn"); + if (MakeCC1vout(cp->evalcode, bettx.vout[1].nValue, pricespk) != bettx.vout[1]) + return eval->Invalid("cannot validate vout1 in bet tx with global pk"); + if (MakeCC1vout(cp->evalcode, bettx.vout[2].nValue, pricespk) != bettx.vout[2] ) + return eval->Invalid("cannot validate vout2 in bet tx with pk from opreturn"); + // This should be all you need to verify it, maybe also check amount? + if ( bettx.vout[4].scriptPubKey != KOMODO_EARLYTXID_SCRIPTPUB ) + return eval->Invalid("the fee was paid to wrong address."); + + int64_t betamount = bettx.vout[2].nValue; + if (betamount != (positionsize * 199) / 200) { + return eval->Invalid("invalid position size in the opreturn"); + } + + // validate if normal inputs are really signed by originator pubkey (someone not cheating with originator pubkey) + CAmount ccOutputs = 0; + for (auto vout : bettx.vout) + if (vout.scriptPubKey.IsPayToCryptoCondition()) + ccOutputs += vout.nValue; + CAmount normalInputs = TotalPubkeyNormalInputs(bettx, pk); + if (normalInputs < ccOutputs) { + return eval->Invalid("bettx normal inputs not signed with pubkey in opret"); + } + + if (leverage > PRICES_MAXLEVERAGE || leverage < -PRICES_MAXLEVERAGE) { + return eval->Invalid("invalid leverage"); + } + + return true; +} + +// validate add funding tx helper +static bool ValidateAddFundingTx(struct CCcontract_info *cp, Eval *eval, const CTransaction & addfundingtx, const CTransaction & vintx) +{ + uint256 bettxid; + int64_t amount; + CPubKey pk, pricespk; + vscript_t vintxOpret; + + if (addfundingtx.vout.size() < 3 || addfundingtx.vout.size() > 4) + return eval->Invalid("incorrect vout number for add funding tx"); + + vscript_t opret; + if (prices_addopretdecode(addfundingtx.vout.back().scriptPubKey, bettxid, pk, amount) != 'A') + return eval->Invalid("cannot decode opreturn for add funding tx"); + + pricespk = GetUnspendable(cp, 0); + uint8_t vintxFuncId = PricesCheckOpret(vintx, vintxOpret); + if (vintxFuncId != 'A' && vintxFuncId != 'B') { // if vintx is bettx + return eval->Invalid("incorrect vintx funcid"); + } + + if (vintxFuncId == 'B' && vintx.GetHash() != bettxid) {// if vintx is bettx + return eval->Invalid("incorrect bet txid in opreturn"); + } + + if (MakeCC1vout(cp->evalcode, addfundingtx.vout[0].nValue, pk) != addfundingtx.vout[0]) + return eval->Invalid("cannot validate vout0 in add funding tx with pk from opreturn"); + if (MakeCC1vout(cp->evalcode, addfundingtx.vout[1].nValue, pricespk) != addfundingtx.vout[1]) + return eval->Invalid("cannot validate vout1 in add funding tx with global pk"); + + return true; +} + +// validate costbasis tx helper +static bool ValidateCostbasisTx(struct CCcontract_info *cp, Eval *eval, const CTransaction & costbasistx, const CTransaction & bettx) +{ + uint256 bettxid; + int64_t costbasisInOpret; + CPubKey pk, pricespk; + int32_t height; + + return true; //deprecated + + // check basic structure: + if (costbasistx.vout.size() < 3 || costbasistx.vout.size() > 4) + return eval->Invalid("incorrect vout count for costbasis tx"); + + vscript_t opret; + if (prices_costbasisopretdecode(costbasistx.vout.back().scriptPubKey, bettxid, pk, height, costbasisInOpret) != 'C') + return eval->Invalid("cannot decode opreturn for costbasis tx"); + + pricespk = GetUnspendable(cp, 0); + if (CTxOut(costbasistx.vout[0].nValue, CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG) != costbasistx.vout[0]) //might go to any pk who calculated costbasis + return eval->Invalid("cannot validate vout0 in costbasis tx with pk from opreturn"); + if (MakeCC1vout(cp->evalcode, costbasistx.vout[1].nValue, pricespk) != costbasistx.vout[1]) + return eval->Invalid("cannot validate vout1 in costbasis tx with global pk"); + + if (bettx.GetHash() != bettxid) + return eval->Invalid("incorrect bettx id"); + + if (bettx.vout.size() < 1) // for safety and for check encapsulation + return eval->Invalid("incorrect bettx no vouts"); + + // check costbasis rules: + if (costbasistx.vout[0].nValue > bettx.vout[1].nValue / 10) { + return eval->Invalid("costbasis myfee too big"); + } + + uint256 tokenid; + int64_t positionsize, firstprice; + int32_t firstheight; + int16_t leverage; + CPubKey betpk; + std::vector vec; + if (prices_betopretdecode(bettx.vout.back().scriptPubKey, betpk, firstheight, positionsize, leverage, firstprice, vec, tokenid) != 'B') + return eval->Invalid("cannot decode opreturn for bet tx"); + + if (firstheight + PRICES_DAYWINDOW + PRICES_SMOOTHWIDTH > chainActive.Height()) { + return eval->Invalid("cannot calculate costbasis yet"); + } + + int64_t costbasis = 0, profits, lastprice; + int32_t retcode = prices_syntheticprofits(costbasis, firstheight, firstheight + PRICES_DAYWINDOW, leverage, vec, positionsize, profits, lastprice); + if (retcode < 0) + return eval->Invalid("cannot calculate costbasis yet"); + std::cerr << "ValidateCostbasisTx() costbasis=" << costbasis << " costbasisInOpret=" << costbasisInOpret << std::endl; + if (costbasis != costbasisInOpret) { + //std::cerr << "ValidateBetTx() " << "incorrect costbasis value" << std::endl; + return eval->Invalid("incorrect costbasis value"); + } + + return true; +} + +// validate final tx helper +static bool ValidateFinalTx(struct CCcontract_info *cp, Eval *eval, const CTransaction & finaltx, const CTransaction & bettx) +{ + uint256 bettxid; + int64_t amount; + CPubKey pk, pricespk; + int64_t profits; + int32_t height; + int64_t firstprice, costbasis, addedbets, positionsize; + int16_t leverage; + + if (finaltx.vout.size() < 3 || finaltx.vout.size() > 4) { + //std::cerr << "ValidateFinalTx()" << " incorrect vout number for final tx =" << finaltx.vout.size() << std::endl; + return eval->Invalid("incorrect vout number for final tx"); + } + + vscript_t opret; + if (prices_finalopretdecode(finaltx.vout.back().scriptPubKey, bettxid, profits, height, pk, firstprice, costbasis, addedbets, positionsize, leverage) != 'F') + return eval->Invalid("cannot decode opreturn for final tx"); + + if (bettx.GetHash() != bettxid) + return eval->Invalid("incorrect bettx id"); + + pricespk = GetUnspendable(cp, 0); + + if (CTxOut(finaltx.vout[0].nValue, CScript() << ParseHex(HexStr(pk)) << OP_CHECKSIG) != finaltx.vout[0]) + return eval->Invalid("cannot validate vout0 in final tx with pk from opreturn"); + + if( finaltx.vout.size() == 3 && MakeCC1vout(cp->evalcode, finaltx.vout[1].nValue, pricespk) != finaltx.vout[1] ) + return eval->Invalid("cannot validate vout1 in final tx with global pk"); + + return true; +} + +// validate prices tx function +// performed checks: +// basic tx structure (vout num) +// basic tx opret structure +// reference to the bet tx vout +// referenced bet txid in tx opret +// referenced bet tx structure +// non-final tx has only 1 cc vin +// cc vouts to self with mypubkey from opret +// cc vouts to global pk with global pk +// for bet tx that normal inputs digned with my pubkey from the opret >= cc outputs - disable betting for other pubkeys (Do we need this rule?) +// TODO: +// opret params (firstprice,positionsize...) +// costbasis calculation +// cashout balance (PricesExactAmounts) +// use the special address for 50% fees bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { + vscript_t vopret; + + if (strcmp(ASSETCHAINS_SYMBOL, "REKT0") == 0 && chainActive.Height() < 2965) + return true; + // check basic opret rules: + if (PricesCheckOpret(tx, vopret) == 0) + return eval->Invalid("tx has no prices opreturn"); + + uint8_t funcId = vopret.begin()[1]; + + CTransaction firstVinTx; + vscript_t firstVinTxOpret; + bool foundFirst = false; + int32_t ccVinCount = 0; + uint32_t prevoutN = 0; + + // check basic rules: + + // find first cc vin and load vintx (might be either bet or add funding tx): + for (auto vin : tx.vin) { + if (cp->ismyvin(vin.scriptSig)) { + CTransaction vintx; + uint256 hashBlock; + vscript_t vintxOpret; + + if (!myGetTransaction(vin.prevout.hash, vintx, hashBlock)) + return eval->Invalid("cannot load vintx"); + + if (PricesCheckOpret(vintx, vintxOpret) == 0) { + //return eval->Invalid("cannot find prices opret in vintx"); + std::cerr << "PricesValidate() " << "cannot find prices opret in vintx" << std::endl; + } + + if (funcId != 'F' && vintxOpret.begin()[1] == 'B' && prevoutN == 1) { + //return eval->Invalid("cannot spend bet marker"); + std::cerr << "PricesValidate() " << " non-final tx cannot spend cc marker vout=" << prevoutN << std::endl; + } + + if (!foundFirst) { + prevoutN = vin.prevout.n; + firstVinTx = vintx; + firstVinTxOpret = vintxOpret; + foundFirst = true; + } + ccVinCount++; + } + } + if (!foundFirst) + return eval->Invalid("prices cc vin not found"); + + if (funcId != 'F' && ccVinCount > 1) {// for all prices tx except final tx only one cc vin is allowed + //return eval->Invalid("only one prices cc vin allowed for this tx"); + std::cerr << "PricesValidate() " << "only one prices cc vin allowed for this tx" << std::endl; + } + + switch (funcId) { + case 'B': // bet + return eval->Invalid("unexpected validate for bet funcid"); + + case 'A': // add funding + // check tx structure: + if (!ValidateAddFundingTx(cp, eval, tx, firstVinTx)) { + //return false; // invalid state is already set in the func + std::cerr << "PricesValidate() " << "ValidateAddFundingTx = false " << eval->state.GetRejectReason() << std::endl; + } + + if (firstVinTxOpret.begin()[1] == 'B') { + if (!ValidateBetTx(cp, eval, firstVinTx)) {// check tx structure + // return false; + std::cerr << "PricesValidate() " << "funcId=A ValidatebetTx = false " << eval->state.GetRejectReason() << std::endl; + } + } + else if (firstVinTxOpret.begin()[1] == 'A') { + // no need to validate the previous addfunding tx (it was validated when added) + } + + if (prevoutN != 0) { // check spending rules + // return eval->Invalid("incorrect vintx vout to spend"); + std::cerr << "PricesValidate() " << "add fund tx incorrect vout to spend=" << prevoutN << std::endl; + } + break; + + case 'C': // set costbasis + if (!ValidateCostbasisTx(cp, eval, tx, firstVinTx)) { + //return false; + std::cerr << "PricesValidate() " << "ValidateCostbasisTx=false " << eval->state.GetRejectReason() << std::endl; + } + if (!ValidateBetTx(cp, eval, firstVinTx)) { + //return false; + std::cerr << "PricesValidate() " << "funcId=C ValidateBetTx=false " << eval->state.GetRejectReason() << std::endl; + } + if (prevoutN != 1) { // check spending rules + // return eval->Invalid("incorrect vout to spend"); + std::cerr << "PricesValidate() " << "costbasis tx incorrect vout to spend=" << prevoutN << std::endl; + } + //return eval->Invalid("test: costbasis is good"); + break; + + case 'F': // final tx + if (!ValidateFinalTx(cp, eval, tx, firstVinTx)) { + ///return false; + std::cerr << "PricesValidate() " << "ValidateFinalTx=false " << eval->state.GetRejectReason() << std::endl; + } + if (!ValidateBetTx(cp, eval, firstVinTx)) { + // return false; + std::cerr << "PricesValidate() " << "ValidateBetTx=false " << eval->state.GetRejectReason() << std::endl; + } + if (prevoutN != 1) { // check spending rules + // return eval->Invalid("incorrect vout to spend"); + std::cerr << "PricesValidate() "<< "final tx incorrect vout to spend=" << prevoutN << std::endl; + } + break; + + default: + return eval->Invalid("invalid funcid"); + } + + eval->state = CValidationState(); return true; } // end of consensus code // helper functions for rpc calls in rpcwallet.cpp -int64_t AddPricesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char *destaddr,int64_t total,int32_t maxinputs,uint256 vintxid,int32_t vinvout) +int64_t AddPricesInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, char *destaddr, int64_t total, int32_t maxinputs) { - int64_t nValue,price,totalinputs = 0; uint256 txid,hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout,n = 0; + int64_t nValue, price, totalinputs = 0; uint256 txid, hashBlock; std::vector origpubkey; CTransaction vintx; int32_t vout, n = 0; std::vector > unspentOutputs; - SetCCunspents(unspentOutputs,destaddr,true); - for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + + SetCCunspents(unspentOutputs, destaddr); + for (std::vector >::const_iterator it = unspentOutputs.begin(); it != unspentOutputs.end(); it++) { txid = it->first.txhash; vout = (int32_t)it->first.index; - if ( vout == vinvout && txid == vintxid ) - continue; - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 && vout < vintx.vout.size() ) + //if (vout == exclvout && txid == excltxid) // exclude vout which is added directly to vins outside this function + // continue; + if (GetTransaction(txid, vintx, hashBlock, false) != 0 && vout < vintx.vout.size()) { - if ( (nValue= vintx.vout[vout].nValue) >= total/maxinputs && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + vscript_t vopret; + uint8_t funcId = PricesCheckOpret(vintx, vopret); + if (funcId == 'B' && vout == 1) // skip cc marker + continue; + + if ((nValue = vintx.vout[vout].nValue) >= total / maxinputs && myIsutxo_spentinmempool(ignoretxid, ignorevin, txid, vout) == 0) { - if ( total != 0 && maxinputs != 0 ) - mtx.vin.push_back(CTxIn(txid,vout,CScript())); + if (total != 0 && maxinputs != 0) + mtx.vin.push_back(CTxIn(txid, vout, CScript())); nValue = it->second.satoshis; totalinputs += nValue; n++; - if ( (total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs) ) + if ((total > 0 && totalinputs >= total) || (maxinputs > 0 && n >= maxinputs)) break; } } @@ -254,474 +560,1482 @@ int64_t AddPricesInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char return(totalinputs); } -UniValue prices_rawtxresult(UniValue &result,std::string rawtx,int32_t broadcastflag) +UniValue prices_rawtxresult(UniValue &result, std::string rawtx, int32_t broadcastflag) { CTransaction tx; - if ( rawtx.size() > 0 ) + if (rawtx.size() > 0) { - result.push_back(Pair("hex",rawtx)); - if ( DecodeHexTx(tx,rawtx) != 0 ) + result.push_back(Pair("hex", rawtx)); + if (DecodeHexTx(tx, rawtx) != 0) { - if ( broadcastflag != 0 && myAddtomempool(tx) != 0 ) + if (broadcastflag != 0 && myAddtomempool(tx) != 0) RelayTransaction(tx); - result.push_back(Pair("txid",tx.GetHash().ToString())); - result.push_back(Pair("result","success")); - } else result.push_back(Pair("error","decode hex")); - } else result.push_back(Pair("error","couldnt finalize CCtx")); + result.push_back(Pair("txid", tx.GetHash().ToString())); + result.push_back(Pair("result", "success")); + } + else + result.push_back(Pair("error", "decode hex")); + } + else + result.push_back(Pair("error", "couldnt finalize CCtx")); return(result); } -int32_t prices_syntheticvec(std::vector &vec,std::vector synthetic) +static std::string prices_getsourceexpression(const std::vector &vec) { + + std::string expr; + + for (int32_t i = 0; i < vec.size(); i++) + { + char name[65]; + std::string operand; + uint16_t opcode = vec[i]; + int32_t value = (opcode & (KOMODO_MAXPRICES - 1)); // index or weight + + switch (opcode & KOMODO_PRICEMASK) + { + case 0: // indices + komodo_pricename(name, value); + operand = std::string(name); + break; + + case PRICES_WEIGHT: // multiply by weight and consume top of stack by updating price + operand = std::to_string(value); + break; + + case PRICES_MULT: // "*" + operand = std::string("*"); + break; + + case PRICES_DIV: // "/" + operand = std::string("/"); + break; + + case PRICES_INV: // "!" + operand = std::string("!"); + break; + + case PRICES_MDD: // "*//" + operand = std::string("*//"); + break; + + case PRICES_MMD: // "**/" + operand = std::string("**/"); + break; + + case PRICES_MMM: // "***" + operand = std::string("***"); + break; + + case PRICES_DDD: // "///" + operand = std::string("///"); + break; + + default: + return "invalid opcode"; + break; + } + + if (expr.size() > 0) + expr += std::string(", "); + expr += operand; + } + return expr; +} + +// helper functions to get synthetic expression reduced: + +// return s true and needed operand count if string is opcode +static bool prices_isopcode(const std::string &s, int &need) { - int32_t i,need,ind,depth = 0; std::string opstr; uint16_t opcode,weight; - if ( synthetic.size() == 0 ) + if (s == "!") { + need = 1; + return true; + } + else if (s == "*" || s == "/") { + need = 2; + return true; + } + else if (s == "***" || s == "///" || s == "*//" || s == "**/") { + need = 3; + return true; + } + else + return false; +} + +// split pair onto two quotes divided by "_" +static void prices_splitpair(const std::string &pair, std::string &upperquote, std::string &bottomquote) +{ + size_t pos = pair.find('_'); // like BTC_USD + if (pos != std::string::npos) { + upperquote = pair.substr(0, pos); + bottomquote = pair.substr(pos + 1); + } + else { + upperquote = pair; + bottomquote = ""; + } + //std::cerr << "prices_splitpair: upperquote=" << upperquote << " bottomquote=" << bottomquote << std::endl; +} + +// invert pair like BTS_USD -> USD_BTC +static std::string prices_invertpair(const std::string &pair) +{ + std::string upperquote, bottomquote; + prices_splitpair(pair, upperquote, bottomquote); + return bottomquote + std::string("_") + upperquote; +} + +// invert pairs in operation accordingly to "/" operator, convert operator to * or *** +static void prices_invertoperation(const std::vector &vexpr, int p, std::vector &voperation) +{ + int need; + + voperation.clear(); + if (prices_isopcode(vexpr[p], need)) { + if (need > 1) { + if (need == 2) { + voperation.push_back(vexpr[p - 2]); + if (vexpr[p] == "/") + voperation.push_back(prices_invertpair(vexpr[p - 1])); + else + voperation.push_back(vexpr[p - 1]); + voperation.push_back("*"); + } + + if (need == 3) { + int i; + std::string::const_iterator c; + for (c = vexpr[p].begin(), i = -3; c != vexpr[p].end(); c++, i++) { + if (*c == '/') + voperation.push_back(prices_invertpair(vexpr[p + i])); + else + voperation.push_back(vexpr[p + i]); + } + voperation.push_back("***"); + } + } + else if (vexpr[p] == "!") { + voperation.push_back(prices_invertpair(vexpr[p - 1])); + // do not add operator + } + } + + //std::cerr << "prices_invert inverted="; + //for (auto v : voperation) std::cerr << v << " "; + //std::cerr << std::endl; +} + +// reduce pairs in the operation, change or remove opcode if reduced +static int prices_reduceoperands(std::vector &voperation) +{ + int opcount = voperation.size() - 1; + int need = opcount; + //std::cerr << "prices_reduceoperands begin need=" << need << std::endl; + + while (true) { + int i; + //std::cerr << "prices_reduceoperands opcount=" << opcount << std::endl; + for (i = 0; i < opcount; i++) { + std::string upperquote, bottomquote; + bool breaktostart = false; + + //std::cerr << "prices_reduceoperands voperation[i]=" << voperation[i] << " i=" << i << std::endl; + prices_splitpair(voperation[i], upperquote, bottomquote); + if (upperquote == bottomquote) { + std::cerr << "prices_reduceoperands erasing i=" << i << std::endl; + voperation.erase(voperation.begin() + i); + opcount--; + //std::cerr << "prices_reduceoperands erased, size=" << voperation.size() << std::endl; + + if (voperation.size() > 0 && voperation.back() == "*") + voperation.pop_back(); + breaktostart = true; + break; + } + + + int j; + for (j = i + 1; j < opcount; j++) { + + //std::cerr << "prices_reduceoperands voperation[j]=" << voperation[j] << " j=" << j << std::endl; + + std::string upperquotej, bottomquotej; + prices_splitpair(voperation[j], upperquotej, bottomquotej); + if (upperquote == bottomquotej || bottomquote == upperquotej) { + if (upperquote == bottomquotej) + voperation[i] = upperquotej + "_" + bottomquote; + else + voperation[i] = upperquote + "_" + bottomquotej; + //std::cerr << "prices_reduceoperands erasing j=" << j << std::endl; + voperation.erase(voperation.begin() + j); + opcount--; + //std::cerr << "prices_reduceoperands erased, size=" << voperation.size() << std::endl; + + need--; + if (voperation.back() == "***") { + voperation.pop_back(); + voperation.push_back("*"); // convert *** to * + } + else if (voperation.back() == "*") { + voperation.pop_back(); // convert * to nothing + } + breaktostart = true; + break; + } + } + if (breaktostart) + break; + } + if (i >= opcount) // all seen + break; + } + + //std::cerr << "prices_reduceoperands end need=" << need << std::endl; + return need; +} + +// substitute reduced operation in vectored expr +static void prices_substitutereduced(std::vector &vexpr, int p, std::vector voperation) +{ + int need; + if (prices_isopcode(vexpr[p], need)) { + vexpr.erase(vexpr.begin() + p - need, vexpr.begin() + p + 1); + vexpr.insert(vexpr.begin() + p - need, voperation.begin(), voperation.end()); + } +} + +// try to reduce synthetic expression by substituting "BTC_USD, BTC_EUR, 30, /" with "EUR_USD, 30" etc +static std::string prices_getreducedexpr(const std::string &expr) +{ + std::string reduced; + + std::vector vexpr; + SplitStr(expr, vexpr); + + for (size_t i = 0; i < vexpr.size(); i++) { + int need; + + if (prices_isopcode(vexpr[i], need)) { + std::vector voperation; + prices_invertoperation(vexpr, i, voperation); + if (voperation.size() > 0) { + int reducedneed = prices_reduceoperands(voperation); + if (reducedneed < need) { + prices_substitutereduced(vexpr, i, voperation); + } + } + } + } + + for (size_t i = 0; i < vexpr.size(); i++) { + if (reduced.size() > 0) + reduced += std::string(", "); + reduced += vexpr[i]; + } + + //std::cerr << "reduced=" << reduced << std::endl; + return reduced; +} + +// parse synthetic expression into vector of codes +int32_t prices_syntheticvec(std::vector &vec, std::vector synthetic) +{ + int32_t i, need, ind, depth = 0; std::string opstr; uint16_t opcode, weight; + if (synthetic.size() == 0) { + std::cerr << "prices_syntheticvec() expression is empty" << std::endl; return(-1); - for (i=0; i= 0 ) + else if (!is_weight_str(opstr) && (ind = komodo_priceind(opstr.c_str())) >= 0) opcode = ind, need = 0; - else if ( (weight= atoi(opstr.c_str())) > 0 && weight < KOMODO_MAXPRICES ) + else if ((weight = atoi(opstr.c_str())) > 0 && weight < KOMODO_MAXPRICES) { opcode = PRICES_WEIGHT | weight; need = 1; - } else return(-2); - if ( depth < need ) + } + else { + std::cerr << "prices_syntheticvec() incorrect opcode=" << opstr << std::endl; + return(-2); + } + if (depth < need) { + std::cerr << "prices_syntheticvec() incorrect not enough operands for opcode=" << opstr << std::endl; return(-3); + } depth -= need; - if ( (opcode & KOMODO_PRICEMASK) != PRICES_WEIGHT ) // weight - depth++; - if ( depth > 3 ) + std::cerr << "prices_syntheticvec() opcode=" << opcode << " opstr=" << opstr << " need=" << need << " depth=" << depth << std::endl; + if ((opcode & KOMODO_PRICEMASK) != PRICES_WEIGHT) { // skip weight + depth++; // increase operands count + std::cerr << "depth++=" << depth << std::endl; + } + if (depth > 3) { + std::cerr << "prices_syntheticvec() too many operands, last=" << opstr << std::endl; return(-4); + } vec.push_back(opcode); } - if ( depth != 0 ) + if (depth != 0) { - fprintf(stderr,"depth.%d not empty\n",depth); + fprintf(stderr, "prices_syntheticvec() depth.%d not empty\n", depth); return(-5); } return(0); } -int64_t prices_syntheticprice(std::vector vec,int32_t height,int32_t minmax,int16_t leverage) +// calculates price for synthetic expression +int64_t prices_syntheticprice(std::vector vec, int32_t height, int32_t minmax, int16_t leverage) { - int32_t i,ind,errcode,depth,retval = -1; uint16_t opcode; int64_t pricedata[PRICES_MAXDATAPOINTS],pricestack[4],price,den,a,b,c; - price = den = depth = errcode = 0; - for (i=0; i= 2) { + b = pricestack[--depth]; + a = pricestack[--depth]; + // pricestack[depth++] = (a * b) / SATOSHIDEN; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_mul(mpzResult, mpzA, mpzB); + mpz_tdiv_q_ui(mpzResult, mpzResult, SATOSHIDEN); + pricestack[depth++] = mpz_get_si(mpzResult); + + } + else + errcode = -3; + break; + + case PRICES_DIV: // "/" + if (depth >= 2) { + b = pricestack[--depth]; + a = pricestack[--depth]; + // pricestack[depth++] = (a * SATOSHIDEN) / b; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_mul_ui(mpzResult, mpzA, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzB); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -4; + break; + + case PRICES_INV: // "!" + if (depth >= 1) { + a = pricestack[--depth]; + // pricestack[depth++] = (SATOSHIDEN * SATOSHIDEN) / a; + mpz_set_si(mpzA, a); + mpz_set_ui(mpzResult, SATOSHIDEN); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzA); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -5; + break; + + case PRICES_MDD: // "*//" + if (depth >= 3) { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + // pricestack[depth++] = (((a * SATOSHIDEN) / b) * SATOSHIDEN) / c; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); + mpz_mul_ui(mpzResult, mpzA, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzB); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzC); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -6; + break; + + case PRICES_MMD: // "**/" + if (depth >= 3) { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + // pricestack[depth++] = (a * b) / c; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); + mpz_mul(mpzResult, mpzA, mpzB); + mpz_tdiv_q(mpzResult, mpzResult, mpzC); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -7; + break; + + case PRICES_MMM: // "***" + if (depth >= 3) { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + // pricestack[depth++] = (((a * b) / SATOSHIDEN ) * c) / SATOSHIDEN; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); + mpz_mul(mpzResult, mpzA, mpzB); + mpz_tdiv_q_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_mul(mpzResult, mpzResult, mpzC); + mpz_tdiv_q_ui(mpzResult, mpzResult, SATOSHIDEN); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -8; + break; + + case PRICES_DDD: // "///" + if (depth >= 3) { + c = pricestack[--depth]; + b = pricestack[--depth]; + a = pricestack[--depth]; + //pricestack[depth++] = (((((SATOSHIDEN * SATOSHIDEN) / a) * SATOSHIDEN) / b) * SATOSHIDEN) / c; + mpz_set_si(mpzA, a); + mpz_set_si(mpzB, b); + mpz_set_si(mpzC, c); + mpz_set_ui(mpzResult, SATOSHIDEN); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzA); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzB); + mpz_mul_ui(mpzResult, mpzResult, SATOSHIDEN); + mpz_tdiv_q(mpzResult, mpzResult, mpzC); + pricestack[depth++] = mpz_get_si(mpzResult); + } + else + errcode = -9; + break; + + default: + errcode = -10; + break; + } + + std::cerr << "prices_syntheticprice test mpzResult=" << mpz_get_si(mpzResult) << std::endl; + // check overflow: + if (mpz_cmp_si(mpzResult, std::numeric_limits::max()) > 0) { + errcode = -13; + break; + } + + if (errcode != 0) + break; + + if( depth > 0 ) + std::cerr << "prices_syntheticprice top pricestack[depth-1=" << depth-1 << "]=" << pricestack[depth-1] << std::endl; + else + std::cerr << "prices_syntheticprice pricestack empty" << std::endl; + } - if ( den == 0 ) + free(pricedata); + mpz_clear(mpzResult); + mpz_clear(mpzA); + mpz_clear(mpzB); + mpz_clear(mpzC); + + if( mpz_get_si(mpzDen) != 0 ) + mpz_tdiv_q(mpzTotalPrice, mpzTotalPrice, mpzDen); // price / den + + int64_t den = mpz_get_si(mpzDen); + int64_t priceIndex = mpz_get_si(mpzTotalPrice); + + mpz_clear(mpzDen); + mpz_clear(mpzTotalPrice); + mpz_clear(mpzPriceValue); + + if (errcode != 0) + std::cerr << "prices_syntheticprice errcode in switch=" << errcode << std::endl; + + if (errcode == -13) { + std::cerr << "prices_syntheticprice overflow in price" << std::endl; + return errcode; + } + + if (den == 0) { + std::cerr << "prices_syntheticprice den==0 return err=-11" << std::endl; return(-11); - else if ( depth != 0 ) + } + else if (depth != 0) { + std::cerr << "prices_syntheticprice depth!=0 err=-12" << std::endl; return(-12); - else if ( errcode != 0 ) + } + else if (errcode != 0) { + std::cerr << "prices_syntheticprice err=" << errcode << std::endl; return(errcode); - return(price / den); -} - -int64_t prices_syntheticprofits(int64_t &costbasis,int32_t firstheight,int32_t height,int16_t leverage,std::vector vec,int64_t positionsize,int64_t addedbets) -{ - int64_t price,profits = 0; int32_t minmax; - minmax = (height > firstheight+PRICES_DAYWINDOW); - if ( (price= prices_syntheticprice(vec,height,minmax,leverage)) < 0 ) - { - fprintf(stderr,"unexpected zero synthetic price at height.%d\n",height); - return(0); } - if ( minmax != 0 ) - { - if ( leverage > 0 && price > costbasis ) - costbasis = price; - else if ( leverage < 0 && (costbasis == 0 || price < costbasis) ) - costbasis = price; - } - profits = ((price * SATOSHIDEN) / costbasis) - SATOSHIDEN; - profits *= leverage * positionsize; - return(positionsize + addedbets + profits); + std::cerr << "prices_syntheticprice priceIndex=" << priceIndex << " den=" << den << std::endl; + + return priceIndex; } -void prices_betjson(UniValue &result,int64_t profits,int64_t costbasis,int64_t positionsize,int64_t addedbets,int16_t leverage,int32_t firstheight,int64_t firstprice) +// calculates costbasis and profit/loss for the bet +int32_t prices_syntheticprofits(int64_t &costbasis, int32_t firstheight, int32_t height, int16_t leverage, std::vector vec, int64_t positionsize, int64_t &profits, int64_t &outprice) { - result.push_back(Pair("profits",ValueFromAmount(profits))); - result.push_back(Pair("costbasis",ValueFromAmount(costbasis))); - result.push_back(Pair("positionsize",ValueFromAmount(positionsize))); - result.push_back(Pair("addedbets",ValueFromAmount(addedbets))); - result.push_back(Pair("leverage",(int64_t)leverage)); - result.push_back(Pair("firstheight",(int64_t)firstheight)); - result.push_back(Pair("firstprice",ValueFromAmount(firstprice))); + int64_t price; +#ifndef TESTMODE + const int32_t COSTBASIS_PERIOD = PRICES_DAYWINDOW; +#else + const int32_t COSTBASIS_PERIOD = 7; +#endif + + + if (height < firstheight) { + fprintf(stderr, "requested height is lower than bet firstheight.%d\n", height); + return -1; + } + + int32_t minmax = (height < firstheight + COSTBASIS_PERIOD); // if we are within 24h then use min or max value + + if ((price = prices_syntheticprice(vec, height, minmax, leverage)) < 0) + { + fprintf(stderr, "error getting synthetic price at height.%d\n", height); + return -1; + } + + // clear lowest positions: + //price /= PRICES_POINTFACTOR; + //price *= PRICES_POINTFACTOR; + outprice = price; + + if (minmax) { // if we are within day window, set temp costbasis to max (or min) price value + if (leverage > 0 && price > costbasis) { + costbasis = price; // set temp costbasis + std::cerr << "prices_syntheticprofits() minmax costbasis=" << costbasis << std::endl; + } + else if (leverage < 0 && (costbasis == 0 || price < costbasis)) { + costbasis = price; + std::cerr << "prices_syntheticprofits() minmax costbasis=" << costbasis << std::endl; + } + //else { //-> use the previous value + // std::cerr << "prices_syntheticprofits() unchanged costbasis=" << costbasis << " price=" << price << " leverage=" << leverage << std::endl; + //} + } + else { + if (height == firstheight + COSTBASIS_PERIOD) { + // if costbasis not set, just set it + //costbasis = price; + + // use calculated minmax costbasis + std::cerr << "prices_syntheticprofits() use permanent costbasis=" << costbasis << " at height=" << height << std::endl; + } + } + + // normalize to 10,000,000 to prevent underflow + //profits = costbasis > 0 ? (((price / PRICES_POINTFACTOR * PRICES_NORMFACTOR) / costbasis) - PRICES_NORMFACTOR / PRICES_POINTFACTOR) * PRICES_POINTFACTOR : 0; + //double dprofits = costbasis > 0 ? ((double)price / (double)costbasis - 1) : 0; + + //std::cerr << "prices_syntheticprofits() test value1 (price/PRICES_POINTFACTOR * PRICES_NORMFACTOR)=" << (price / PRICES_POINTFACTOR * PRICES_NORMFACTOR) << std::endl; + //std::cerr << "prices_syntheticprofits() test value2 (price/PRICES_POINTFACTOR * PRICES_NORMFACTOR)/costbasis=" << (costbasis != 0 ? (price / PRICES_POINTFACTOR * PRICES_NORMFACTOR)/costbasis : 0) << std::endl; + + //std::cerr << "prices_syntheticprofits() fractional profits=" << profits << std::endl; + //std::cerr << "prices_syntheticprofits() profits double=" << (double)price / (double)costbasis -1.0 << std::endl; + //double dprofits = (double)price / (double)costbasis - 1.0; + + //profits *= ((int64_t)leverage * (int64_t)positionsize); + //profits /= (int64_t)PRICES_NORMFACTOR; // de-normalize + + //dprofits *= ((double)leverage * (double)positionsize); + + //dprofits *= leverage * positionsize; + // profits = dprofits; + //std::cerr << "prices_syntheticprofits() dprofits=" << dprofits << std::endl; + + if (costbasis > 0) { + mpz_t mpzProfits; + mpz_t mpzCostbasis; + mpz_t mpzPrice; + mpz_t mpzLeverage; + + mpz_init(mpzProfits); + mpz_init(mpzCostbasis); + mpz_init(mpzPrice); + mpz_init(mpzLeverage); + + mpz_set_si(mpzCostbasis, costbasis); + mpz_set_si(mpzPrice, price); + mpz_mul_ui(mpzPrice, mpzPrice, SATOSHIDEN); // (price*SATOSHIDEN) + + mpz_tdiv_q(mpzProfits, mpzPrice, mpzCostbasis); // profits = (price*SATOSHIDEN)/costbasis // normalization + mpz_sub_ui(mpzProfits, mpzProfits, SATOSHIDEN); // profits -= SATOSHIDEN + + mpz_set_si(mpzLeverage, leverage); + mpz_mul(mpzProfits, mpzProfits, mpzLeverage); // profits *= leverage + mpz_mul_ui(mpzProfits, mpzProfits, positionsize); // profits *= positionsize + mpz_tdiv_q_ui(mpzProfits, mpzProfits, SATOSHIDEN); // profits /= SATOSHIDEN // de-normalization + + profits = mpz_get_si(mpzProfits); + + mpz_clear(mpzLeverage); + mpz_clear(mpzProfits); + mpz_clear(mpzCostbasis); + mpz_clear(mpzPrice); + + } + else + profits = 0; + + std::cerr << "prices_syntheticprofits() profits=" << profits << std::endl; + return 0; // (positionsize + addedbets + profits); } -int64_t prices_costbasis(CTransaction bettx) +// makes result json object +void prices_betjson(UniValue &result, std::vector bets, int16_t leverage, int32_t endheight, int64_t lastprice) +{ + + UniValue resultbets(UniValue::VARR); + int64_t totalbets = 0; + int64_t totalprofits = 0; + + for (auto b : bets) { + UniValue entry(UniValue::VOBJ); + entry.push_back(Pair("positionsize", ValueFromAmount(b.positionsize))); + entry.push_back(Pair("profits", ValueFromAmount(b.profits))); + entry.push_back(Pair("costbasis", ValueFromAmount(b.costbasis))); + entry.push_back(Pair("firstheight", b.firstheight)); + resultbets.push_back(entry); + totalbets += b.positionsize; + totalprofits += b.profits; + } + int64_t equity = totalbets + totalprofits; + + result.push_back(Pair("bets", resultbets)); + result.push_back(Pair("leverage", (int64_t)leverage)); + result.push_back(Pair("TotalPositionSize", ValueFromAmount(totalbets))); + result.push_back(Pair("TotalProfits", ValueFromAmount(totalprofits))); + result.push_back(Pair("equity", ValueFromAmount(equity))); + result.push_back(Pair("LastPrice", ValueFromAmount(lastprice))); + result.push_back(Pair("LastHeight", endheight)); +} + +// retrieves costbasis from a tx spending bettx vout1 (deprecated) +int64_t prices_costbasis(CTransaction bettx, uint256 &txidCostbasis) { int64_t costbasis = 0; // if vout1 is spent, follow and extract costbasis from opreturn //uint8_t prices_costbasisopretdecode(CScript scriptPubKey,uint256 &bettxid,CPubKey &pk,int32_t &height,int64_t &costbasis) + //uint256 txidCostbasis; + int32_t vini; + int32_t height; + txidCostbasis = zeroid; +/* + if (CCgetspenttxid(txidCostbasis, vini, height, bettx.GetHash(), 1) < 0) { + std::cerr << "prices_costbasis() no costbasis txid found" << std::endl; + return 0; + } - return(costbasis); + CTransaction txCostbasis; + uint256 hashBlock; + uint256 bettxid; + CPubKey pk; + bool isLoaded = false; + uint8_t funcId = 0; + + if ((isLoaded = myGetTransaction(txidCostbasis, txCostbasis, hashBlock)) && + txCostbasis.vout.size() > 0 && + (funcId = prices_costbasisopretdecode(txCostbasis.vout.back().scriptPubKey, bettxid, pk, height, costbasis)) != 0) { + return costbasis; + } + + std::cerr << "prices_costbasis() cannot load costbasis tx or decode opret" << " isLoaded=" << isLoaded << " funcId=" << (int)funcId << std::endl; */ + return 0; } -int64_t prices_batontxid(uint256 &batontxid,CTransaction bettx,uint256 bettxid) +// enumerates and retrieves added bets, returns the last baton txid +int64_t prices_enumaddedbets(uint256 &batontxid, std::vector &bets, uint256 bettxid) { - int64_t addedbets = 0; + int64_t addedBetsTotal = 0; + int32_t vini; + int32_t height; + int32_t retcode; + + batontxid = bettxid; // initially set to the source bet tx + uint256 sourcetxid = bettxid; + // iterate through batons, adding up vout1 -> addedbets - return(addedbets); + while ((retcode = CCgetspenttxid(batontxid, vini, height, sourcetxid, 0)) == 0) { + + CTransaction txBaton; + CBlockIndex blockIdx; + uint256 bettxidInOpret; + CPubKey pk; + bool isLoaded = false; + uint8_t funcId = 0; + int64_t amount; + EvalRef eval; + + if ((isLoaded = eval->GetTxConfirmed(batontxid, txBaton, blockIdx)) && + blockIdx.IsValid() && + txBaton.vout.size() > 0 && + (funcId = prices_addopretdecode(txBaton.vout.back().scriptPubKey, bettxidInOpret, pk, amount)) != 0) + { + OneBetData added; + + addedBetsTotal += amount; + added.positionsize = amount; + added.firstheight = blockIdx.GetHeight(); + bets.push_back(added); + std::cerr << "prices_batontxid() added amount=" << amount << std::endl; + } + else { + std::cerr << "prices_batontxid() cannot load or decode add bet tx, isLoaded=" << isLoaded << " funcId=" << (int)funcId << std::endl; + return -1; + } + sourcetxid = batontxid; + } + + return(addedBetsTotal); } -UniValue PricesBet(uint64_t txfee,int64_t amount,int16_t leverage,std::vector synthetic) +// pricesbet rpc impl: make betting tx +UniValue PricesBet(int64_t txfee, int64_t amount, int16_t leverage, std::vector synthetic) { - int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CPubKey pricespk,mypk; int64_t betamount,firstprice; std::vector vec; char myaddr[64]; std::string rawtx; - if ( leverage > PRICES_MAXLEVERAGE || leverage < -PRICES_MAXLEVERAGE ) + fprintf(stderr, "assetchains_contract.%i vs eval_prices.%i\n",ASSETCHAINS_EARLYTXIDCONTRACT, EVAL_PRICES); + if ( ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_EARLYTXID_SCRIPTPUB.size() == 0 ) { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","leverage too big")); + // Lock here, as in validation we cannot call lock in the function itself. + // may not be needed as the validation call to update the global, is called in a LOCK already, and it can only update there and here. + LOCK(cs_main); + GetKomodoEarlytxidScriptPub(); + } + /* + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; + CPubKey pricespk, mypk; + int64_t betamount, firstprice = 0; + std::vector vec; + //char myaddr[64]; + std::string rawtx; + + if (leverage > PRICES_MAXLEVERAGE || leverage < -PRICES_MAXLEVERAGE) + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "leverage too big")); return(result); } - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) + cp = CCinit(&C, EVAL_PRICES); + if (txfee == 0) txfee = PRICES_TXFEE; mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,myaddr,mypk); - if ( prices_syntheticvec(vec,synthetic) < 0 || (firstprice= prices_syntheticprice(vec,nextheight-1,1,leverage)) < 0 || vec.size() == 0 || vec.size() > 4096 ) + pricespk = GetUnspendable(cp, 0); + //GetCCaddress(cp, myaddr, mypk); + if (prices_syntheticvec(vec, synthetic) < 0 || (firstprice = prices_syntheticprice(vec, nextheight - 1, 1, leverage)) < 0 || vec.size() == 0 || vec.size() > 4096) { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","invalid synthetic")); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "invalid synthetic")); return(result); } - if ( AddNormalinputs(mtx,mypk,amount+4*txfee,64) >= amount+4*txfee ) + if (AddNormalinputs(mtx, mypk, amount + 4 * txfee, 64) >= amount + 4 * txfee) { betamount = (amount * 199) / 200; - mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); // baton for total funding - mtx.vout.push_back(MakeCC1vout(cp->evalcode,(amount-betamount)+2*txfee,pricespk)); - mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,betamount,pricespk,mypk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_betopret(mypk,nextheight-1,amount,leverage,firstprice,vec,zeroid)); - return(prices_rawtxresult(result,rawtx,0)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode, txfee, mypk)); // vout0 baton for total funding + // mtx.vout.push_back(MakeCC1vout(cp->evalcode, (amount - betamount) + 2 * txfee, pricespk)); // vout1, when spent, costbasis is set + mtx.vout.push_back(MakeCC1vout(cp->evalcode, txfee, pricespk)); // vout1 cc marker (NVOUT_CCMARKER) + mtx.vout.push_back(MakeCC1vout(cp->evalcode, betamount, pricespk)); // vout2 betamount + mtx.vout.push_back(CTxOut(txfee, CScript() << ParseHex(HexStr(pricespk)) << OP_CHECKSIG)); // vout3 normal marker NVOUT_NORMALMARKER - TODO: remove it as we have cc marker now, when move to the new chain + if ( ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_EARLYTXID_SCRIPTPUB.size() == 0 ) + { + // Lock here, as in validation we cannot call lock in the function itself. + // may not be needed as the validation call to update the global, is called in a LOCK already, and it can only update there and here. + LOCK(cs_main); + GetKomodoEarlytxidScriptPub(); + } + mtx.vout.push_back(CTxOut(amount-betamount, KOMODO_EARLYTXID_SCRIPTPUB)); + + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_betopret(mypk, nextheight - 1, amount, leverage, firstprice, vec, zeroid)); + return(prices_rawtxresult(result, rawtx, 0)); } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","not enough funds")); - return(result); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "not enough funds")); + return(result); + */ } -UniValue PricesAddFunding(uint64_t txfee,uint256 bettxid,int64_t amount) +// pricesaddfunding rpc impl: add yet another bet +UniValue PricesAddFunding(int64_t txfee, uint256 bettxid, int64_t amount) { int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CTransaction bettx; CPubKey pricespk,mypk; int64_t addedbets=0,betamount,firstprice; std::vector vec; uint256 batontxid; std::string rawtx; char myaddr[64]; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; CTransaction bettx; + CPubKey pricespk, mypk; + //int64_t addedbets = 0, betamount, firstprice; + std::vector vec; + uint256 batontxid; + std::string rawtx; + //char myaddr[64]; + + cp = CCinit(&C, EVAL_PRICES); + if (txfee == 0) txfee = PRICES_TXFEE; mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,myaddr,mypk); - if ( AddNormalinputs(mtx,mypk,amount+txfee,64) >= amount+txfee ) + pricespk = GetUnspendable(cp, 0); + //GetCCaddress(cp, myaddr, mypk); + if (AddNormalinputs(mtx, mypk, amount + 2*txfee, 64) >= amount + 2*txfee) { - if ( prices_batontxid(batontxid,bettx,bettxid) >= 0 ) + std::vector bets; + if (prices_enumaddedbets(batontxid, bets, bettxid) >= 0) { - mtx.vin.push_back(CTxIn(batontxid,0,CScript())); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,txfee,mypk)); // baton for total funding - mtx.vout.push_back(MakeCC1vout(cp->evalcode,amount,pricespk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_addopret(bettxid,mypk,amount)); - return(prices_rawtxresult(result,rawtx,0)); + mtx.vin.push_back(CTxIn(batontxid, 0, CScript())); + mtx.vout.push_back(MakeCC1vout(cp->evalcode, txfee, mypk)); // vout0 baton for total funding + mtx.vout.push_back(MakeCC1vout(cp->evalcode, amount, pricespk)); // vout1 added amount + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_addopret(bettxid, mypk, amount)); + return(prices_rawtxresult(result, rawtx, 0)); } else { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","couldnt find batonttxid")); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "couldnt find batonttxid")); return(result); } } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","not enough funds")); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "not enough funds")); return(result); } -UniValue PricesSetcostbasis(uint64_t txfee,uint256 bettxid) -{ - int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t myfee,positionsize=0,addedbets,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = PRICES_TXFEE; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) +// scan chain from the initial bet's first position upto the chain tip and calculate bet's costbasises and profits, breaks if rekt detected +int32_t prices_scanchain(std::vector &bets, int16_t leverage, std::vector vec, int64_t &lastprice, int32_t &endheight) { + + if (bets.size() == 0) + return -1; + + bool stop = false; + for (int32_t height = bets[0].firstheight; ; height++) // the last datum for 24h is the costbasis value { - if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) - { - addedbets = prices_batontxid(batontxid,bettx,bettxid); - mtx.vin.push_back(CTxIn(bettxid,1,CScript())); - for (i=0; i bets[i].firstheight) { + + int32_t retcode = prices_syntheticprofits(bets[i].costbasis, bets[i].firstheight, height, leverage, vec, bets[i].positionsize, bets[i].profits, lastprice); + if (retcode < 0) { + std::cerr << "prices_scanchain() prices_syntheticprofits returned -1, breaking" << std::endl; + stop = true; break; } + totalbets += bets[i].positionsize; + totalprofits += bets[i].profits; } - if ( i == PRICES_DAYWINDOW ) - result.push_back(Pair("rekt",0)); - prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - myfee = bettx.vout[1].nValue / 10; - result.push_back(Pair("myfee",myfee)); - mtx.vout.push_back(CTxOut(myfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,bettx.vout[1].nValue-myfee-txfee,pricespk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_costbasisopret(bettxid,mypk,firstheight+PRICES_DAYWINDOW-1,costbasis)); - return(prices_rawtxresult(result,rawtx,0)); + } + + if (stop) + break; + + endheight = height; + int64_t equity = totalbets + totalprofits; + if (equity < 0) + { // we are in loss + break; } } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant find bettxid")); + + return 0; +} + +// pricescostbasis rpc impl: set cost basis (open price) for the bet (deprecated) +UniValue PricesSetcostbasis(int64_t txfee, uint256 bettxid) +{ + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); + UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; CTransaction bettx; uint256 hashBlock, batontxid, tokenid; + int64_t myfee, positionsize = 0, addedbets, firstprice = 0, lastprice, profits = 0, costbasis = 0, equity; + int32_t i, firstheight = 0, height, numvouts; int16_t leverage = 0; + std::vector vec; + CPubKey pk, mypk, pricespk; std::string rawtx; +/* + cp = CCinit(&C, EVAL_PRICES); + if (txfee == 0) + txfee = PRICES_TXFEE; + + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp, 0); + if (myGetTransaction(bettxid, bettx, hashBlock) != 0 && (numvouts = bettx.vout.size()) > 3) + { + if (prices_betopretdecode(bettx.vout[numvouts - 1].scriptPubKey, pk, firstheight, positionsize, leverage, firstprice, vec, tokenid) == 'B') + { + if (nextheight <= firstheight + PRICES_DAYWINDOW + 1) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cannot calculate costbasis yet")); + return(result); + } + + addedbets = prices_enumaddedbets(batontxid, bettx, bettxid); + mtx.vin.push_back(CTxIn(bettxid, 1, CScript())); // spend vin1 with betamount + //for (i = 0; i < PRICES_DAYWINDOW + 1; i++) // the last datum for 24h is the actual costbasis value + //{ + int32_t retcode = prices_syntheticprofits(costbasis, firstheight, firstheight + PRICES_DAYWINDOW, leverage, vec, positionsize, addedbets, profits, lastprice); + if (retcode < 0) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cannot calculate costbasis error getting price")); + return(result); + } + equity = positionsize + addedbets + profits; + //if (equity < 0) + //{ // we are in loss + // result.push_back(Pair("rekt", (int64_t)1)); + // result.push_back(Pair("rektheight", (int64_t)firstheight + i)); + // break; + //} + //} + //if (i == PRICES_DAYWINDOW + 1) + // result.push_back(Pair("rekt", 0)); + + prices_betjson(result, profits, costbasis, positionsize, addedbets, leverage, firstheight, firstprice, lastprice, equity); + + if (AddNormalinputs(mtx, mypk, txfee, 4) >= txfee) + { + myfee = bettx.vout[1].nValue / 10; // fee for setting costbasis + result.push_back(Pair("myfee", myfee)); + + mtx.vout.push_back(CTxOut(myfee, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode, bettx.vout[1].nValue - myfee, pricespk)); + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_costbasisopret(bettxid, mypk, firstheight + PRICES_DAYWINDOW , costbasis)); // -1 + return(prices_rawtxresult(result, rawtx, 0)); + } + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "not enough funds")); + return(result); + } + } */ + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "deprecated")); return(result); } -UniValue PricesRekt(uint64_t txfee,uint256 bettxid,int32_t rektheight) + +int32_t prices_getbetinfo(uint256 bettxid, BetInfo &betinfo) { - int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; CTransaction bettx; uint256 hashBlock,tokenid,batontxid; int64_t myfee=0,positionsize,addedbets,firstprice,profits,ignore,costbasis=0; int32_t firstheight,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) - txfee = PRICES_TXFEE; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) + CTransaction bettx; + uint256 hashBlock, batontxid, tokenid; + + if (myGetTransaction(bettxid, bettx, hashBlock) && bettx.vout.size() > 3) { - if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + if (hashBlock.IsNull()) + return -2; + + OneBetData bet1; + if (prices_betopretdecode(bettx.vout.back().scriptPubKey, betinfo.pk, bet1.firstheight, bet1.positionsize, betinfo.leverage, betinfo.firstprice, betinfo.parsed, betinfo.tokenid) == 'B') { - costbasis = prices_costbasis(bettx); - addedbets = prices_batontxid(batontxid,bettx,bettxid); - if ( (profits= prices_syntheticprofits(ignore,firstheight,rektheight,leverage,vec,positionsize,addedbets)) < 0 ) - { - myfee = (positionsize + addedbets) / 500; + uint256 finaltxid; + int32_t vini; + int32_t finaltxheight; //, endheight; + //std::vector bets; + + + if (CCgetspenttxid(finaltxid, vini, finaltxheight, bettxid, NVOUT_CCMARKER) == 0) + betinfo.isOpen = false; + else + betinfo.isOpen = true; + + //bet1.amount = betinfo.positionsize; + //bet1.firstheight = firstheight; + betinfo.bets.push_back(bet1); + + prices_enumaddedbets(batontxid, betinfo.bets, bettxid); + + if (prices_scanchain(betinfo.bets, betinfo.leverage, betinfo.parsed, betinfo.lastprice, betinfo.lastheight) < 0) { + return -4; } - prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - if ( myfee != 0 ) - { - mtx.vin.push_back(CTxIn(bettxid,2,CScript())); - mtx.vout.push_back(CTxOut(myfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - mtx.vout.push_back(MakeCC1vout(cp->evalcode,bettx.vout[2].nValue-myfee-txfee,pricespk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_finalopret(bettxid,profits,rektheight,mypk,firstprice,costbasis,addedbets,positionsize,leverage)); - return(prices_rawtxresult(result,rawtx,0)); + + mpz_t mpzTotalbets; + mpz_t mpzTotalprofits; + mpz_t mpzTotalcostbasis; + + mpz_init(mpzTotalbets); + mpz_init(mpzTotalprofits); + mpz_init(mpzTotalcostbasis); + + int64_t totalbets = 0; + int64_t totalprofits = 0; + + for (auto b : betinfo.bets) { + mpz_t mpzProduct; + mpz_t mpzProfits; + + mpz_init(mpzProduct); + mpz_init(mpzProfits); + + //totalprofits += b.profits; + //dcostbasis += b.amount * (double)b.costbasis; + // costbasis += b.amount * (b.costbasis / PRICES_POINTFACTOR); // prevent int64 overflow (but we have underflow for 1/BTC) + // std::cerr << "PricesInfo() acc dcostbasis=" << dcostbasis << " b.amount=" << b.amount << " b.costbasis/PRICES_POINTFACTOR=" << (b.costbasis / PRICES_POINTFACTOR) << std::endl; + //std::cerr << "PricesInfo() acc dcostbasis=" << dcostbasis << " b.amount=" << b.amount << " b.costbasis/PRICES_POINTFACTOR=" << (b.costbasis / PRICES_POINTFACTOR) << std::endl; + mpz_set_ui(mpzProduct, b.costbasis); + mpz_mul_ui(mpzProduct, mpzProduct, (uint64_t)b.positionsize); // b.costbasis * b.amount + mpz_add(mpzTotalcostbasis, mpzTotalcostbasis, mpzProduct); //averageCostbasis += b.costbasis * b.amount; + + mpz_add_ui(mpzTotalbets, mpzTotalbets, (uint64_t)b.positionsize); //totalbets += b.amount; + mpz_add(mpzTotalprofits, mpzTotalprofits, mpzProfits); //totalprofits += b.profits; + + totalbets += b.positionsize; + totalprofits += b.profits; + + mpz_clear(mpzProduct); + mpz_clear(mpzProfits); } + + betinfo.equity = totalbets + totalprofits; + //int64_t averageCostbasis = 0; + + if (mpz_get_ui(mpzTotalbets) != 0) { //prevent zero div + mpz_t mpzAverageCostbasis; + mpz_init(mpzAverageCostbasis); + + //averageCostbasis = totalcostbasis / totalbets; + mpz_mul_ui(mpzTotalcostbasis, mpzTotalcostbasis, SATOSHIDEN); // profits *= SATOSHIDEN normalization to prevent loss of significance while division + mpz_tdiv_q(mpzAverageCostbasis, mpzTotalcostbasis, mpzTotalbets); + + mpz_tdiv_q_ui(mpzAverageCostbasis, mpzAverageCostbasis, SATOSHIDEN); // profits /= SATOSHIDEN de-normalization + + betinfo.averageCostbasis = mpz_get_ui(mpzAverageCostbasis); + mpz_clear(mpzAverageCostbasis); + } + + betinfo.liquidationprice = 0; + if (betinfo.leverage != 0) {// prevent zero div + betinfo.liquidationprice = betinfo.averageCostbasis - betinfo.averageCostbasis / betinfo.leverage; + } + + if (betinfo.equity >= 0) + betinfo.isRekt = false; else { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","position not rekt")); - return(result); + betinfo.isRekt = true; + betinfo.rektfee = totalbets / 500; } + + mpz_clear(mpzTotalbets); + mpz_clear(mpzTotalprofits); + mpz_clear(mpzTotalcostbasis); + return 0; } - else - { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant decode opret")); - return(result); - } + return -3; } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant find bettxid")); - return(result); + return (-1); } -UniValue PricesCashout(uint64_t txfee,uint256 bettxid) +// pricesrekt rpc: anyone can rekt a bet at some block where losses reached limit, collecting fee +UniValue PricesRekt(int64_t txfee, uint256 bettxid, int32_t rektheight) { int32_t nextheight = komodo_nextheight(); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(),nextheight); UniValue result(UniValue::VOBJ); - struct CCcontract_info *cp,C; char destaddr[64]; CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t CCchange=0,positionsize,inputsum,ignore,addedbets,firstprice,profits,costbasis=0; int32_t i,firstheight,height,numvouts; int16_t leverage; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; - cp = CCinit(&C,EVAL_PRICES); - if ( txfee == 0 ) + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; + CTransaction bettx; +/* uint256 hashBlock, tokenid, batontxid; + int64_t firstprice, lastprice = 0, positionsize; + int32_t firstheight; + int16_t leverage; + std::vector vec; */ + int64_t myfee = 0; + CPubKey pk, mypk, pricespk; + std::string rawtx; + char destaddr[64]; + + cp = CCinit(&C, EVAL_PRICES); + if (txfee == 0) // TODO: what did we want tot do with txfee in prices? txfee = PRICES_TXFEE; mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp,0); - GetCCaddress(cp,destaddr,pricespk); - if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) - { - if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) - { - costbasis = prices_costbasis(bettx); - addedbets = prices_batontxid(batontxid,bettx,bettxid); - if ( (profits= prices_syntheticprofits(ignore,firstheight,nextheight-1,leverage,vec,positionsize,addedbets)) < 0 ) - { - prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - result.push_back(Pair("result","error")); - result.push_back(Pair("error","position rekt")); - return(result); - } - prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - mtx.vin.push_back(CTxIn(bettxid,2,CScript())); - if ( (inputsum= AddPricesInputs(cp,mtx,destaddr,profits+txfee,64,bettxid,2)) > profits+txfee ) - CCchange = (inputsum - profits); - mtx.vout.push_back(CTxOut(bettx.vout[2].nValue + profits,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); - if ( CCchange >= txfee ) - mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,pricespk)); - rawtx = FinalizeCCTx(0,cp,mtx,mypk,txfee,prices_finalopret(bettxid,profits,nextheight-1,mypk,firstprice,costbasis,addedbets,positionsize,leverage)); - return(prices_rawtxresult(result,rawtx,0)); + pricespk = GetUnspendable(cp, 0); + GetCCaddress(cp, destaddr, pricespk); + + BetInfo betinfo; + int32_t retcode = prices_getbetinfo(bettxid, betinfo); + if (retcode < 0) { + if (retcode == -1) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant find bettxid or incorrect")); } - else + else if (retcode == -2) { + throw std::runtime_error("tx still in mempool"); + } + else if (retcode == -3) { - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant decode opret")); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant decode opret")); return(result); } + else if (retcode == -4) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "error scanning chain")); + } + return(result); + } + + int64_t totalbets = 0; + int64_t totalprofits = 0; + + for (auto b : betinfo.bets) { + totalbets += b.positionsize; + totalprofits += b.profits; + } + + + if (!betinfo.isOpen) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "position closed")); + return result; + } + + prices_betjson(result, betinfo.bets, betinfo.leverage, betinfo.lastheight, betinfo.lastprice); // fill output + if (betinfo.isRekt) + { + myfee = betinfo.rektfee; // consolation fee for loss + } + if (myfee != 0) + { + int64_t CCchange = 0, inputsum; + + mtx.vin.push_back(CTxIn(bettxid, NVOUT_CCMARKER, CScript())); // spend cc marker + if ((inputsum = AddPricesInputs(cp, mtx, destaddr, myfee + txfee, 64)) > myfee + txfee) // TODO: why do we take txfee from global addr and not from user's addr? + CCchange = (inputsum - myfee); + mtx.vout.push_back(CTxOut(myfee, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + if (CCchange >= txfee) + mtx.vout.push_back(MakeCC1vout(cp->evalcode, CCchange, pricespk)); + + /// mtx.vout.push_back(MakeCC1vout(cp->evalcode, bettx.vout[2].nValue - myfee - txfee, pricespk)); // change + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_finalopret(bettxid, totalprofits, rektheight, mypk, betinfo.firstprice, 0, totalbets /*- positionsize*/, 0/*positionsize*/, betinfo.leverage)); + return(prices_rawtxresult(result, rawtx, 0)); + } + else + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "position not rekt")); + return(result); } - return(result); } -UniValue PricesInfo(uint256 bettxid,int32_t refheight) +// pricescashout rpc impl: bettor can cashout hit bet if it is not rekt +UniValue PricesCashout(int64_t txfee, uint256 bettxid) { - UniValue result(UniValue::VOBJ); CTransaction bettx; uint256 hashBlock,batontxid,tokenid; int64_t myfee,ignore,positionsize=0,addedbets=0,firstprice=0,profits=0,costbasis=0; int32_t i,firstheight=0,height,numvouts; int16_t leverage=0; std::vector vec; CPubKey pk,mypk,pricespk; std::string rawtx; - if ( myGetTransaction(bettxid,bettx,hashBlock) != 0 && (numvouts= bettx.vout.size()) > 3 ) - { - if ( prices_betopretdecode(bettx.vout[numvouts-1].scriptPubKey,pk,firstheight,positionsize,leverage,firstprice,vec,tokenid) == 'B' ) + int32_t nextheight = komodo_nextheight(); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextheight); + UniValue result(UniValue::VOBJ); + struct CCcontract_info *cp, C; char destaddr[64]; +/* CTransaction bettx; + uint256 hashBlock, batontxid, tokenid; + int64_t positionsize, firstprice, lastprice = 0; + int32_t firstheight; + int16_t leverage; + std::vector vec;*/ + int64_t CCchange = 0, inputsum; + CPubKey pk, mypk, pricespk; + std::string rawtx; + + cp = CCinit(&C, EVAL_PRICES); + if (txfee == 0) + txfee = PRICES_TXFEE; + + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp, 0); + GetCCaddress(cp, destaddr, pricespk); + + BetInfo betinfo; + int32_t retcode = prices_getbetinfo(bettxid, betinfo); + if (retcode < 0) { + if (retcode == -1) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant find bettxid or incorrect")); + } + else if (retcode == -2) { + throw std::runtime_error("tx still in mempool"); + } + else if (retcode == -3) { - costbasis = prices_costbasis(bettx); - addedbets = prices_batontxid(batontxid,bettx,bettxid); - if ( (profits= prices_syntheticprofits(ignore,firstheight,refheight,leverage,vec,positionsize,addedbets)) < 0 ) - { - result.push_back(Pair("rekt",1)); - result.push_back(Pair("rektfee",(positionsize + addedbets) / 500)); - } else result.push_back(Pair("rekt",0)); - result.push_back(Pair("batontxid",batontxid.GetHex())); - prices_betjson(result,profits,costbasis,positionsize,addedbets,leverage,firstheight,firstprice); - result.push_back(Pair("height",(int64_t)refheight)); + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant decode opret")); return(result); } - } - result.push_back(Pair("result","error")); - result.push_back(Pair("error","cant find bettxid")); - return(result); -} - -UniValue PricesList() -{ - UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; int64_t amount,firstprice; int32_t height; int16_t leverage; uint256 txid,hashBlock,tokenid; CPubKey pk,pricespk; std::vector vec; CTransaction vintx; char str[65]; - cp = CCinit(&C,EVAL_PRICES); - pricespk = GetUnspendable(cp,0); - SetCCtxids(addressIndex,cp->normaladdr,false); - for (std::vector >::const_iterator it=addressIndex.begin(); it!=addressIndex.end(); it++) - { - txid = it->first.txhash; - if ( GetTransaction(txid,vintx,hashBlock,false) != 0 ) - { - if ( vintx.vout.size() > 0 && prices_betopretdecode(vintx.vout[vintx.vout.size()-1].scriptPubKey,pk,height,amount,leverage,firstprice,vec,tokenid) == 'B' ) - { - result.push_back(uint256_str(str,txid)); - } + else if (retcode == -4) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "error scanning chain")); } + return(result); } + + int64_t totalbets = 0; + int64_t totalprofits = 0; + + for (auto b : betinfo.bets) { + totalbets += b.positionsize; + totalprofits += b.profits; + } + + + if (!betinfo.isOpen) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "position closed")); + return result; + } + + prices_betjson(result, betinfo.bets, betinfo.leverage, betinfo.lastheight, betinfo.lastprice); // fill output json + + if (betinfo.isRekt) + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "position rekt")); + return(result); + } + + mtx.vin.push_back(CTxIn(bettxid, NVOUT_CCMARKER, CScript())); // spend cc marker + if ((inputsum = AddPricesInputs(cp, mtx, destaddr, betinfo.equity + txfee, 64)) > betinfo.equity + txfee) + CCchange = (inputsum - betinfo.equity); + mtx.vout.push_back(CTxOut(betinfo.equity, CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + if (CCchange >= txfee) + mtx.vout.push_back(MakeCC1vout(cp->evalcode, CCchange, pricespk)); + // TODO: what should the opret param be: + rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_finalopret(bettxid, totalprofits, nextheight - 1, mypk, betinfo.firstprice, 0, totalbets/*- betinfo.positionsize*/, 0/*betinfo.positionsize*/, betinfo.leverage)); + return(prices_rawtxresult(result, rawtx, 0)); + +} + + + + +// pricesinfo rpc impl +UniValue PricesInfo(uint256 bettxid, int32_t refheight) +{ + UniValue result(UniValue::VOBJ); +/* CTransaction bettx; + uint256 hashBlock, batontxid, tokenid; + int64_t positionsize = 0, firstprice = 0, lastprice = 0; + int32_t firstheight = 0, endheight; + int16_t leverage = 0; + std::vector vec; + CPubKey pk, mypk, pricespk; + std::string rawtx; */ + + BetInfo betinfo; + int32_t retcode = prices_getbetinfo(bettxid, betinfo); + if (retcode < 0) { + if( retcode == -1 ) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant find bettxid or incorrect")); + } + else if (retcode == -2) { + throw std::runtime_error("tx still in mempool"); + } + else if (retcode == -3) + { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "cant decode opret")); + return(result); + } + else if (retcode == -4) { + result.push_back(Pair("result", "error")); + result.push_back(Pair("error", "error scanning chain")); + } + return(result); + } + + if (!betinfo.isRekt) + result.push_back(Pair("rekt", 0)); + else + { + result.push_back(Pair("rekt", (int64_t)1)); + result.push_back(Pair("rektfee", betinfo.rektfee)); + result.push_back(Pair("rektheight", betinfo.lastheight)); + } + + std::string expr = prices_getsourceexpression(betinfo.parsed); + result.push_back(Pair("expression", expr)); + result.push_back(Pair("reduced", prices_getreducedexpr(expr))); +// result.push_back(Pair("batontxid", batontxid.GetHex())); + result.push_back(Pair("costbasis", ValueFromAmount(betinfo.averageCostbasis))); +#ifdef TESTMODE + result.push_back(Pair("costbasis_test_period", 7)); +#endif + + prices_betjson(result, betinfo.bets, betinfo.leverage, betinfo.lastheight, betinfo.lastprice); + + result.push_back(Pair("LiquidationPrice", ValueFromAmount(betinfo.liquidationprice))); + + return(result); +} + +// priceslist rpc impl +UniValue PricesList(uint32_t filter, CPubKey mypk) +{ + UniValue result(UniValue::VARR); + std::vector > addressIndex, addressIndexCC; + struct CCcontract_info *cp, C; + + cp = CCinit(&C, EVAL_PRICES); + //pricespk = GetUnspendable(cp, 0); + + // filters and outputs prices bet txid + auto AddBetToList = [&](uint256 txid) + { + int64_t amount, firstprice; + int32_t height; + int16_t leverage; + uint256 hashBlock, tokenid; + CPubKey pk, pricespk; + std::vector vec; + CTransaction vintx; + + if (GetTransaction(txid, vintx, hashBlock, false) != 0) + { + bool bAppend = false; + if (vintx.vout.size() > 0 && prices_betopretdecode(vintx.vout.back().scriptPubKey, pk, height, amount, leverage, firstprice, vec, tokenid) == 'B' && + (mypk == CPubKey() || mypk == pk)) // if only mypubkey to list + { + if (filter == 0) + bAppend = true; + else { + int32_t vini; + int32_t height; + uint256 finaltxid; + + int32_t spent = CCgetspenttxid(finaltxid, vini, height, txid, NVOUT_CCMARKER); + if (filter == 1 && spent < 0 || // open positions + filter == 2 && spent == 0) // closed positions + bAppend = true; + } + if (bAppend) + result.push_back(txid.GetHex()); + } + std::cerr << "PricesList() " << " bettxid=" << txid.GetHex() << " mypk=" << HexStr(mypk) << " opretpk=" << HexStr(pk) << " filter=" << filter << " bAppend=" << bAppend << std::endl; + } + }; + + + SetCCtxids(addressIndex, cp->normaladdr, false); // old normal marker + for (std::vector >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) + { + if( it->first.txindex == NVOUT_NORMALMARKER ) + AddBetToList(it->first.txhash); + } + + /* for future when switch to cc marker only + SetCCtxids(addressIndexCC, cp->unspendableCCaddr, true); // cc marker + for (std::vector >::const_iterator it = addressIndexCC.begin(); it != addressIndexCC.end(); it++) + { + priceslist(it, 1); + } + */ return(result); } +void prices_addbookentry(uint256 txid) +{ + BetInfo betinfo; + //if( prices_getbetinfo(txid, betinfo) == 0 ) +} + +// walk through uxtos on the global address +// calculate the balance: +// + rekt positions +// = opposite positions +// - unbalanced positions +UniValue PricesGetOrderbook() +{ + UniValue result(UniValue::VARR); + std::vector > addressIndex, addressIndexCC; + struct CCcontract_info *cp, C; + + cp = CCinit(&C, EVAL_PRICES); + + SetCCtxids(addressIndex, cp->normaladdr, false); // old normal marker + for (std::vector >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) + { + if (it->first.txindex == NVOUT_NORMALMARKER) + prices_addbookentry(it->first.txhash); + } + return result; +} diff --git a/src/komodo_bitcoind.h b/src/komodo_bitcoind.h index 292d5f8df..e616fb179 100644 --- a/src/komodo_bitcoind.h +++ b/src/komodo_bitcoind.h @@ -29,6 +29,7 @@ int32_t komodo_voutupdate(bool fJustCheck,int32_t *isratificationp,int32_t notar unsigned int lwmaGetNextPOSRequired(const CBlockIndex* pindexLast, const Consensus::Params& params); bool EnsureWalletIsAvailable(bool avoidException); extern bool fRequestShutdown; +extern CScript KOMODO_EARLYTXID_SCRIPTPUB; int32_t MarmaraSignature(uint8_t *utxosig,CMutableTransaction &txNew); uint8_t DecodeMaramaraCoinbaseOpRet(const CScript scriptPubKey,CPubKey &pk,int32_t &height,int32_t &unlockht); @@ -2062,7 +2063,6 @@ bool komodo_appendACscriptpub() { ASSETCHAINS_SCRIPTPUB.pop_back(); ASSETCHAINS_SCRIPTPUB.pop_back(); // remove last 2 chars. // get OP_RETURN from txid and append the HexStr of it to scriptpub - // encoded opreturn incorrectly on TESTHC chain, once we no longer need this it can be changed to a straight +1 to drop OP_RETURN opcode. ASSETCHAINS_SCRIPTPUB.append(HexStr(tx.vout[i].scriptPubKey.begin()+3, tx.vout[i].scriptPubKey.end())); //fprintf(stderr, "ac_script.%s\n",ASSETCHAINS_SCRIPTPUB.c_str()); didinit = true; @@ -2076,6 +2076,44 @@ bool komodo_appendACscriptpub() return false; } +void GetKomodoEarlytxidScriptPub() +{ + if ( KOMODO_EARLYTXID == zeroid ) + { + fprintf(stderr, "Restart deamon with -earlytxid.\n"); + StartShutdown(); + return; + } + if ( ASSETCHAINS_EARLYTXIDCONTRACT == EVAL_PRICES && KOMODO_SNAPSHOT_INTERVAL == 0 ) + { + fprintf(stderr, "Prices->paymentsCC contract must have -ac_snapshot enabled to pay out.\n"); + StartShutdown(); + return; + } + if ( chainActive.Height() < 100 ) + { + fprintf(stderr, "Cannot fetch -earlytxid before block 100.\n"); + StartShutdown(); + return; + } + CTransaction tx; uint256 blockhash; int32_t i; + // get transaction and check that it occured before height 100. + if ( myGetTransaction(KOMODO_EARLYTXID,tx,blockhash) && mapBlockIndex[blockhash]->GetHeight() < 100 ) + { + for (i = 0; i < tx.vout.size(); i++) + if ( tx.vout[i].scriptPubKey[0] == OP_RETURN ) + break; + if ( i < tx.vout.size() ) + { + KOMODO_EARLYTXID_SCRIPTPUB = CScript(tx.vout[i].scriptPubKey.begin()+3, tx.vout[i].scriptPubKey.end()); + fprintf(stderr, "KOMODO_EARLYTXID_SCRIPTPUB.%s\n", HexStr(KOMODO_EARLYTXID_SCRIPTPUB.begin(),KOMODO_EARLYTXID_SCRIPTPUB.end()).c_str()); + return; + } + } + fprintf(stderr, "INVALID -earlytxid, restart daemon with correct txid.\n"); + StartShutdown(); +} + int64_t komodo_checkcommission(CBlock *pblock,int32_t height) { int64_t checktoshis=0; uint8_t *script,scripthex[8192]; int32_t scriptlen,matched = 0; static bool didinit = false; diff --git a/src/komodo_defs.h b/src/komodo_defs.h index a3bde708b..5efbe58fb 100644 --- a/src/komodo_defs.h +++ b/src/komodo_defs.h @@ -38,7 +38,12 @@ #define KOMODO_MAXNVALUE (((uint64_t)1 << 63) - 1) #define KOMODO_BIT63SET(x) ((x) & ((uint64_t)1 << 63)) #define KOMODO_VALUETOOBIG(x) ((x) > (uint64_t)10000000001*COIN) + +//#ifndef TESTMODE #define PRICES_DAYWINDOW ((3600*24/ASSETCHAINS_BLOCKTIME) + 1) +//#else +//#define PRICES_DAYWINDOW (7) +//#endif extern uint8_t ASSETCHAINS_TXPOW,ASSETCHAINS_PUBLIC; int32_t MAX_BLOCK_SIZE(int32_t height); @@ -79,6 +84,7 @@ extern uint8_t ASSETCHAINS_PRIVATE; extern int32_t USE_EXTERNAL_PUBKEY; extern char NOTARYADDRS[64][64]; extern int32_t KOMODO_TESTNODE, KOMODO_SNAPSHOT_INTERVAL; +extern int32_t ASSETCHAINS_EARLYTXIDCONTRACT; int tx_height( const uint256 &hash ); extern std::vector vWhiteListAddress; void komodo_netevent(std::vector payload); @@ -101,7 +107,7 @@ int32_t komodo_dpowconfs(int32_t height,int32_t numconfs); int8_t komodo_segid(int32_t nocache,int32_t height); int32_t komodo_heightpricebits(uint64_t *seedp,uint32_t *heightbits,int32_t nHeight); char *komodo_pricename(char *name,int32_t ind); -int32_t komodo_priceind(char *symbol); +int32_t komodo_priceind(const char *symbol); int32_t komodo_pricesinit(); int64_t komodo_priceave(int64_t *tmpbuf,int64_t *correlated,int32_t cskip); int64_t komodo_pricecorrelated(uint64_t seed,int32_t ind,uint32_t *rawprices,int32_t rawskip,uint32_t *nonzprices,int32_t smoothwidth); diff --git a/src/komodo_gateway.h b/src/komodo_gateway.h index 0ff1c0b66..1fafcc856 100644 --- a/src/komodo_gateway.h +++ b/src/komodo_gateway.h @@ -2400,8 +2400,8 @@ char *komodo_pricename(char *name,int32_t ind) } return(0); } - -int32_t komodo_priceind(char *symbol) +// finds index for its symbol name +int32_t komodo_priceind(const char *symbol) { char name[65]; int32_t i,n = (int32_t)(komodo_cbopretsize(ASSETCHAINS_CBOPRET) / sizeof(uint32_t)); for (i=1; i 1 || ASSETCHAINS_SELFIMPORT.size() > 0 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_TIMELOCKGTE != _ASSETCHAINS_TIMELOCKOFF|| ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH || ASSETCHAINS_LWMAPOS != 0 || ASSETCHAINS_LASTERA > 0 || ASSETCHAINS_BEAMPORT != 0 || ASSETCHAINS_CODAPORT != 0 || ASSETCHAINS_MARMARA != 0 || nonz > 0 || ASSETCHAINS_CCLIB.size() > 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 || ASSETCHAINS_NOTARY_PAY[0] != 0 || ASSETCHAINS_BLOCKTIME != 60 || ASSETCHAINS_CBOPRET != 0 || Mineropret.size() != 0 || (ASSETCHAINS_NK[0] != 0 && ASSETCHAINS_NK[1] != 0) || KOMODO_SNAPSHOT_INTERVAL != 0 ) + if ( ASSETCHAINS_ENDSUBSIDY[0] != 0 || ASSETCHAINS_REWARD[0] != 0 || ASSETCHAINS_HALVING[0] != 0 || ASSETCHAINS_DECAY[0] != 0 || ASSETCHAINS_COMMISSION != 0 || ASSETCHAINS_PUBLIC != 0 || ASSETCHAINS_PRIVATE != 0 || ASSETCHAINS_TXPOW != 0 || ASSETCHAINS_FOUNDERS != 0 || ASSETCHAINS_SCRIPTPUB.size() > 1 || ASSETCHAINS_SELFIMPORT.size() > 0 || ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 || ASSETCHAINS_TIMELOCKGTE != _ASSETCHAINS_TIMELOCKOFF|| ASSETCHAINS_ALGO != ASSETCHAINS_EQUIHASH || ASSETCHAINS_LWMAPOS != 0 || ASSETCHAINS_LASTERA > 0 || ASSETCHAINS_BEAMPORT != 0 || ASSETCHAINS_CODAPORT != 0 || ASSETCHAINS_MARMARA != 0 || nonz > 0 || ASSETCHAINS_CCLIB.size() > 0 || ASSETCHAINS_FOUNDERS_REWARD != 0 || ASSETCHAINS_NOTARY_PAY[0] != 0 || ASSETCHAINS_BLOCKTIME != 60 || ASSETCHAINS_CBOPRET != 0 || Mineropret.size() != 0 || (ASSETCHAINS_NK[0] != 0 && ASSETCHAINS_NK[1] != 0) || KOMODO_SNAPSHOT_INTERVAL != 0 || ASSETCHAINS_EARLYTXIDCONTRACT != 0 ) { fprintf(stderr,"perc %.4f%% ac_pub=[%02x%02x%02x...] acsize.%d\n",dstr(ASSETCHAINS_COMMISSION)*100,ASSETCHAINS_OVERRIDE_PUBKEY33[0],ASSETCHAINS_OVERRIDE_PUBKEY33[1],ASSETCHAINS_OVERRIDE_PUBKEY33[2],(int32_t)ASSETCHAINS_SCRIPTPUB.size()); extraptr = extrabuf; @@ -2152,7 +2154,10 @@ void komodo_args(char *argv0) if ( KOMODO_SNAPSHOT_INTERVAL != 0 ) { extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(KOMODO_SNAPSHOT_INTERVAL),(void *)&KOMODO_SNAPSHOT_INTERVAL); - fprintf(stderr, "snapshot interval.%i\n",KOMODO_SNAPSHOT_INTERVAL); + } + if ( ASSETCHAINS_EARLYTXIDCONTRACT != 0 ) + { + extralen += iguana_rwnum(1,&extraptr[extralen],sizeof(ASSETCHAINS_EARLYTXIDCONTRACT),(void *)&ASSETCHAINS_EARLYTXIDCONTRACT); } } diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 035d44662..cb2cfce92 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -43,6 +43,9 @@ #include +#include "cc/CCinclude.h" +#include "cc/CCPrices.h" + using namespace std; extern void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry); @@ -1155,17 +1158,17 @@ UniValue paxprice(const UniValue& params, bool fHelp) } return ret; } - +// fills pricedata with raw price, correlated and smoothed values for numblock /*int32_t prices_extract(int64_t *pricedata,int32_t firstheight,int32_t numblocks,int32_t ind) { int32_t height,i,n,width,numpricefeeds = -1; uint64_t seed,ignore,rngval; uint32_t rawprices[1440*6],*ptr; int64_t *tmpbuf; - width = numblocks+PRICES_DAYWINDOW*2+PRICES_SMOOTHWIDTH; + width = numblocks+PRICES_DAYWINDOW*2+PRICES_SMOOTHWIDTH; // need 2*PRICES_DAYWINDOW previous raw price points to calc PRICES_DAYWINDOW correlated points to calc, in turn, smoothed point komodo_heightpricebits(&seed,rawprices,firstheight + numblocks - 1); if ( firstheight < width ) return(-1); for (i=0; i vexpr; + SplitStr(sexpr, vexpr); + + // debug print parsed strings: + std::cerr << "parsed synthetic: "; + for (auto s : vexpr) + std::cerr << s << " "; + std::cerr << std::endl; + + return PricesBet(txfee, amount, leverage, vexpr); +} + +// pricesaddfunding rpc implementation +UniValue pricesaddfunding(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error("pricesaddfunding bettxid amount\n" + "where amount is in coins\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + CAmount txfee = 10000; + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + CAmount amount = atof(params[1].get_str().c_str()) * COIN; + if (amount <= 0) + throw runtime_error("invalid amount\n"); + + return PricesAddFunding(txfee, bettxid, amount); +} + +// rpc pricessetcostbasis implementation +UniValue pricessetcostbasis(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error("pricessetcostbasis bettxid\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + int64_t txfee = 10000; + + return PricesSetcostbasis(txfee, bettxid); +} + +// pricescashout rpc implementation +UniValue pricescashout(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 1) + throw runtime_error("pricescashout bettxid\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + int64_t txfee = 10000; + + return PricesCashout(txfee, bettxid); +} + +// pricesrekt rpc implementation +UniValue pricesrekt(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 2) + throw runtime_error("pricesrekt bettxid height\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + uint256 bettxid = Parseuint256(params[0].get_str().c_str()); + if (bettxid.IsNull()) + throw runtime_error("invalid bettxid\n"); + + int32_t height = atoi(params[0].get_str().c_str()); + + int64_t txfee = 10000; + + return PricesRekt(txfee, bettxid, height); +} + +// pricesrekt rpc implementation +UniValue pricesgetorderbook(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0) + throw runtime_error("pricesgetorderbook\n"); + LOCK(cs_main); + UniValue ret(UniValue::VOBJ); + + if (ASSETCHAINS_CBOPRET == 0) + throw JSONRPCError(RPC_INVALID_PARAMETER, "only -ac_cbopret chains have prices"); + + return PricesGetOrderbook(); +} + UniValue gettxout(const UniValue& params, bool fHelp) { if (fHelp || params.size() < 2 || params.size() > 3) @@ -1944,4 +2082,4 @@ void RegisterBlockchainRPCCommands(CRPCTable &tableRPC) { for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); -} +} \ No newline at end of file diff --git a/src/rpc/register.h b/src/rpc/register.h index 072517d88..245f76e22 100644 --- a/src/rpc/register.h +++ b/src/rpc/register.h @@ -35,6 +35,10 @@ void RegisterMiningRPCCommands(CRPCTable &tableRPC); /** Register raw transaction RPC commands */ void RegisterRawTransactionRPCCommands(CRPCTable &tableRPC); +/** Register test transaction RPC commands */ +void RegisterTesttransactionsRPCCommands(CRPCTable &tableRPC); + + static inline void RegisterAllCoreRPCCommands(CRPCTable &tableRPC) { RegisterBlockchainRPCCommands(tableRPC); @@ -42,6 +46,9 @@ static inline void RegisterAllCoreRPCCommands(CRPCTable &tableRPC) RegisterMiscRPCCommands(tableRPC); RegisterMiningRPCCommands(tableRPC); RegisterRawTransactionRPCCommands(tableRPC); +#ifdef TESTMODE + RegisterTesttransactionsRPCCommands(tableRPC); +#endif } #endif diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index b1359ef15..a41c7f413 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -463,7 +463,15 @@ static const CRPCCommand vRPCCommands[] = { "prices", "prices", &prices, true }, { "prices", "pricesaddress", &pricesaddress, true }, { "prices", "priceslist", &priceslist, true }, + { "prices", "mypriceslist", &mypriceslist, true }, { "prices", "pricesinfo", &pricesinfo, true }, + { "prices", "pricesbet", &pricesbet, true }, + { "prices", "pricessetcostbasis", &pricessetcostbasis, true }, + { "prices", "pricescashout", &pricescashout, true }, + { "prices", "pricesrekt", &pricesrekt, true }, + { "prices", "pricesaddfunding", &pricesaddfunding, true }, + { "prices", "pricesgetorderbook", &pricesgetorderbook, true }, + // Pegs { "pegs", "pegsaddress", &pegsaddress, true }, @@ -569,10 +577,6 @@ static const CRPCCommand vRPCCommands[] = { "util", "reconsiderblock", &reconsiderblock, true }, /* Not shown in help */ { "hidden", "setmocktime", &setmocktime, true }, - { "hidden", "test_ac", &test_ac, true }, - { "hidden", "test_heirmarker", &test_heirmarker, true }, - { "hidden", "test_proof", &test_proof, true }, - { "hidden", "test_burntx", &test_burntx, true }, #ifdef ENABLE_WALLET diff --git a/src/rpc/server.h b/src/rpc/server.h index 5ec7640f2..d0882d6cb 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -273,6 +273,7 @@ extern UniValue oraclesdata(const UniValue& params, bool fHelp); extern UniValue oraclessamples(const UniValue& params, bool fHelp); extern UniValue pricesaddress(const UniValue& params, bool fHelp); extern UniValue priceslist(const UniValue& params, bool fHelp); +extern UniValue mypriceslist(const UniValue& params, bool fHelp); extern UniValue pricesinfo(const UniValue& params, bool fHelp); extern UniValue pegsaddress(const UniValue& params, bool fHelp); extern UniValue marmaraaddress(const UniValue& params, bool fHelp); @@ -502,11 +503,13 @@ extern UniValue paxdeposit(const UniValue& params, bool fHelp); extern UniValue paxwithdraw(const UniValue& params, bool fHelp); extern UniValue prices(const UniValue& params, bool fHelp); +extern UniValue pricesbet(const UniValue& params, bool fHelp); +extern UniValue pricessetcostbasis(const UniValue& params, bool fHelp); +extern UniValue pricescashout(const UniValue& params, bool fHelp); +extern UniValue pricesrekt(const UniValue& params, bool fHelp); +extern UniValue pricesaddfunding(const UniValue& params, bool fHelp); +extern UniValue pricesgetorderbook(const UniValue& params, bool fHelp); + -// test rpc: -extern UniValue test_ac(const UniValue& params, bool fHelp); -extern UniValue test_heirmarker(const UniValue& params, bool fHelp); -extern UniValue test_burntx(const UniValue& params, bool fHelp); -extern UniValue test_proof(const UniValue& params, bool fHelp); #endif // BITCOIN_RPCSERVER_H diff --git a/src/rpc/testtransactions.cpp b/src/rpc/testtransactions.cpp new file mode 100644 index 000000000..ddf7c8895 --- /dev/null +++ b/src/rpc/testtransactions.cpp @@ -0,0 +1,268 @@ +// Copyright (c) 2010 Satoshi Nakamoto +// Copyright (c) 2009-2014 The Bitcoin Core developers +// Distributed under the MIT software license, see the accompanying +// file COPYING or http://www.opensource.org/licenses/mit-license.php. + +/****************************************************************************** + * Copyright © 2014-2019 The SuperNET Developers. * + * * + * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * + * the top-level directory of this distribution for the individual copyright * + * holder information and the developer policies on copyright and licensing. * + * * + * Unless otherwise agreed in a custom licensing agreement, no part of the * + * SuperNET software, including this file may be copied, modified, propagated * + * or distributed except according to the terms contained in the LICENSE file * + * * + * Removal or modification of this copyright notice is prohibited. * + * * + ******************************************************************************/ + +#include + +#include "amount.h" +#include "chain.h" +#include "chainparams.h" +#include "checkpoints.h" +#include "crosschain.h" +#include "base58.h" +#include "consensus/validation.h" +#include "cc/eval.h" +#include "main.h" +#include "primitives/transaction.h" +#include "rpc/server.h" +#include "streams.h" +#include "sync.h" +#include "util.h" +#include "script/script.h" +#include "script/script_error.h" +#include "script/sign.h" +#include "script/standard.h" + +#include + +#include + +#include + + +#include "cc/CCinclude.h" +#include "cc/CCPrices.h" + +using namespace std; + +int32_t ensure_CCrequirements(uint8_t evalcode); + +UniValue test_ac(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 4)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements(EVAL_HEIR) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + std::vector pubkey1; + std::vector pubkey2; + + pubkey1 = ParseHex(params[0].get_str().c_str()); + pubkey2 = ParseHex(params[1].get_str().c_str()); + + CPubKey pk1 = pubkey2pk(pubkey1); + CPubKey pk2 = pubkey2pk(pubkey2); + + if (!pk1.IsValid() || !pk2.IsValid()) + throw runtime_error("invalid pubkey\n"); + + int64_t txfee = 10000; + int64_t amount = atoll(params[2].get_str().c_str()) * COIN; + uint256 fundingtxid = Parseuint256((char *)params[3].get_str().c_str()); + + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, txfee + amount, 60); + + if (normalInputs < txfee + amount) + throw runtime_error("not enough normals\n"); + + mtx.vout.push_back(MakeCC1of2vout(EVAL_HEIR, amount, pk1, pk2)); + + CScript opret; + fundingtxid = revuint256(fundingtxid); + + opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'A' << fundingtxid << (uint8_t)0); + + cp = CCinit(&C, EVAL_HEIR); + return(FinalizeCCTx(0, cp, mtx, myPubkey, txfee, opret)); +} + +UniValue test_heirmarker(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 1)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements(EVAL_HEIR) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + uint256 fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); + + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); + if (normalInputs < 10000) + throw runtime_error("not enough normals\n"); + + mtx.vin.push_back(CTxIn(fundingtxid, 1)); + mtx.vout.push_back(MakeCC1vout(EVAL_HEIR, 10000, myPubkey)); + + CScript opret; + fundingtxid = revuint256(fundingtxid); + + opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'C' << fundingtxid << (uint8_t)0); + + cp = CCinit(&C, EVAL_HEIR); + return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret)); +} + +UniValue test_burntx(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 1)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements(EVAL_TOKENS) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + uint256 tokenid = Parseuint256((char *)params[0].get_str().c_str()); + + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); + if (normalInputs < 10000) + throw runtime_error("not enough normals\n"); + + CPubKey burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY)); + + mtx.vin.push_back(CTxIn(tokenid, 0)); + mtx.vin.push_back(CTxIn(tokenid, 1)); + mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS, 1, burnpk)); + + std::vector voutPubkeys; + voutPubkeys.push_back(burnpk); + + cp = CCinit(&C, EVAL_TOKENS); + + std::vector vopret; + GetNonfungibleData(tokenid, vopret); + if (vopret.size() > 0) + cp->additionalTokensEvalcode2 = vopret.begin()[0]; + + uint8_t tokenpriv[33]; + char unspendableTokenAddr[64]; + CPubKey unspPk = GetUnspendable(cp, tokenpriv); + GetCCaddress(cp, unspendableTokenAddr, unspPk); + CCaddr2set(cp, EVAL_TOKENS, unspPk, tokenpriv, unspendableTokenAddr); + return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, EncodeTokenOpRet(tokenid, voutPubkeys, std::make_pair(0, vscript_t())))); +} + +UniValue test_proof(const UniValue& params, bool fHelp) +{ + UniValue result(UniValue::VOBJ); + std::vectorproof; + + if (fHelp || (params.size() != 2)) + throw runtime_error("incorrect params\n"); + + + proof = ParseHex(params[0].get_str()); + uint256 cointxid = Parseuint256((char *)params[1].get_str().c_str()); + + std::vector txids; + + CMerkleBlock merkleBlock; + if (!E_UNMARSHAL(proof, ss >> merkleBlock)) { + result.push_back(Pair("error", "could not unmarshal proof")); + return result; + } + uint256 merkleRoot = merkleBlock.txn.ExtractMatches(txids); + + result.push_back(Pair("source_root", merkleRoot.GetHex())); + + for (int i = 0; i < txids.size(); i++) + std::cerr << "merkle block txid=" << txids[0].GetHex() << std::endl; + + + std::vector vMatches(txids.size()); + for (auto v : vMatches) v = true; + CPartialMerkleTree verifTree(txids, vMatches); + + result.push_back(Pair("verif_root", verifTree.ExtractMatches(txids).GetHex())); + + if (std::find(txids.begin(), txids.end(), cointxid) == txids.end()) { + fprintf(stderr, "invalid proof for this cointxid\n"); + } + + std::vector vMerkleTree; + bool f; + ::BuildMerkleTree(&f, txids, vMerkleTree); + + std::vector vMerkleBranch = ::GetMerkleBranch(0, txids.size(), vMerkleTree); + + uint256 ourResult = SafeCheckMerkleBranch(zeroid, vMerkleBranch, 0); + result.push_back(Pair("SafeCheckMerkleBranch", ourResult.GetHex())); + + return result; +} + +extern CScript prices_costbasisopret(uint256 bettxid, CPubKey mypk, int32_t height, int64_t costbasis); +UniValue test_pricesmarker(const UniValue& params, bool fHelp) +{ + // make fake token tx: + struct CCcontract_info *cp, C; + + if (fHelp || (params.size() != 1)) + throw runtime_error("incorrect params\n"); + if (ensure_CCrequirements(EVAL_PRICES) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + uint256 bettxid = Parseuint256((char *)params[0].get_str().c_str()); + + cp = CCinit(&C, EVAL_PRICES); + CPubKey myPubkey = pubkey2pk(Mypubkey()); + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + + int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); + if (normalInputs < 10000) + throw runtime_error("not enough normals\n"); + + mtx.vin.push_back(CTxIn(bettxid, 1)); + mtx.vout.push_back(CTxOut(1000, CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG)); + + return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, prices_costbasisopret(bettxid, myPubkey, 100, 100))); +} + + +static const CRPCCommand commands[] = +{ // category name actor (function) okSafeMode + // --------------------- ------------------------ ----------------------- ---------- + + /* Not shown in help */ + { "hidden", "test_ac", &test_ac, true }, + { "hidden", "test_heirmarker", &test_heirmarker, true }, + { "hidden", "test_proof", &test_proof, true }, + { "hidden", "test_burntx", &test_burntx, true }, + { "hidden", "test_pricesmarker", &test_pricesmarker, true } +}; + +void RegisterTesttransactionsRPCCommands(CRPCTable &tableRPC) +{ + for (unsigned int vcidx = 0; vcidx < ARRAYLEN(commands); vcidx++) + tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); +} \ No newline at end of file diff --git a/src/util.cpp b/src/util.cpp index 980d82ac9..1aa079085 100644 --- a/src/util.cpp +++ b/src/util.cpp @@ -394,27 +394,23 @@ void ParseParameters(int argc, const char* const argv[]) } } +// split string using by space or comma as a delimiter char void SplitStr(const std::string& strVal, std::vector &outVals) { stringstream ss(strVal); - std::string str; - while ( ss.peek() == ' ' ) - ss.ignore(); - - while ( ss >> str ) - { - if ( str.size() == 0 ) - continue; - if ( str[str.size()-1] == ',' ) - str.resize(str.size()-1); - outVals.push_back(str); - while ( ss.peek() == ' ' ) - ss.ignore(); - if ( ss.peek() == ',' ) - ss.ignore(); - while ( ss.peek() == ' ' ) + while (!ss.eof()) { + int c; + std::string str; + + while (std::isspace(ss.peek())) ss.ignore(); + + while ((c = ss.get()) != EOF && !std::isspace(c) && c != ',') + str += c; + + if (!str.empty()) + outVals.push_back(str); } } diff --git a/src/util.h b/src/util.h index dc421092a..617faaf36 100644 --- a/src/util.h +++ b/src/util.h @@ -287,6 +287,9 @@ template void TraceThread(const char* name, Callable func) } } +// split string using by space or comma as a delimiter char +void SplitStr(const std::string& strVal, std::vector &outVals); + #define KOMODO_ASSETCHAIN_MAXLEN 65 diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index aff740e31..5ce7ae773 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -59,6 +59,7 @@ #include #include "komodo_defs.h" +#include using namespace std; @@ -6945,25 +6946,61 @@ UniValue faucetget(const UniValue& params, bool fHelp) return(result); } +uint32_t pricesGetParam(UniValue param) { + uint32_t filter = 0; + if (STR_TOLOWER(param.get_str()) == "all") + filter = 0; + else if (STR_TOLOWER(param.get_str()) == "open") + filter = 1; + else if (STR_TOLOWER(param.get_str()) == "closed") + filter = 2; + else + throw runtime_error("incorrect parameter\n"); + return filter; +} + UniValue priceslist(const UniValue& params, bool fHelp) { - if ( fHelp || params.size() > 0 ) - throw runtime_error("priceslist\n"); + if ( fHelp || params.size() != 0 && params.size() != 1) + throw runtime_error("priceslist [all|open|closed]\n"); if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - return(PricesList()); + uint32_t filter = 0; + if (params.size() == 1) + filter = pricesGetParam(params[0]); + + CPubKey emptypk; + + return(PricesList(filter, emptypk)); +} + +UniValue mypriceslist(const UniValue& params, bool fHelp) +{ + if (fHelp || params.size() != 0 && params.size() != 1) + throw runtime_error("mypriceslist [all|open|closed]\n"); + if (ensure_CCrequirements(EVAL_PRICES) < 0) + throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); + + uint32_t filter = 0; + if (params.size() == 1) + filter = pricesGetParam(params[0]); + CPubKey mypk = pubkey2pk(Mypubkey()); + + return(PricesList(filter, mypk)); } UniValue pricesinfo(const UniValue& params, bool fHelp) { uint256 bettxid; int32_t height; - if ( fHelp || params.size() != 2 ) - throw runtime_error("pricesinfo fundingtxid\n"); + if ( fHelp || params.size() != 1 && params.size() != 2) + throw runtime_error("pricesinfo bettxid [height]\n"); if ( ensure_CCrequirements(EVAL_PRICES) < 0 ) throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); bettxid = Parseuint256((char *)params[0].get_str().c_str()); - height = atoi(params[1].get_str().c_str()); - return(PricesInfo(bettxid,height)); + height = 0; + if (params.size() == 2) + height = atoi(params[1].get_str().c_str()); + return(PricesInfo(bettxid, height)); } UniValue dicefund(const UniValue& params, bool fHelp) @@ -7961,86 +7998,9 @@ void RegisterWalletRPCCommands(CRPCTable &tableRPC) tableRPC.appendCommand(commands[vcidx].name, &commands[vcidx]); } -UniValue test_ac(const UniValue& params, bool fHelp) -{ - // make fake token tx: - struct CCcontract_info *cp, C; - - if (fHelp || (params.size() != 4)) - throw runtime_error("incorrect params\n"); - if (ensure_CCrequirements(EVAL_HEIR) < 0) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - - std::vector pubkey1; - std::vector pubkey2; - - pubkey1 = ParseHex(params[0].get_str().c_str()); - pubkey2 = ParseHex(params[1].get_str().c_str()); - - CPubKey pk1 = pubkey2pk(pubkey1); - CPubKey pk2 = pubkey2pk(pubkey2); - - if(!pk1.IsValid() || !pk2.IsValid()) - throw runtime_error("invalid pubkey\n"); - - int64_t txfee = 10000; - int64_t amount = atoll(params[2].get_str().c_str()) * COIN; - uint256 fundingtxid = Parseuint256((char *)params[3].get_str().c_str()); - - CPubKey myPubkey = pubkey2pk(Mypubkey()); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - - int64_t normalInputs = AddNormalinputs(mtx, myPubkey, txfee + amount, 60); - - if( normalInputs < txfee + amount) - throw runtime_error("not enough normals\n"); - - mtx.vout.push_back(MakeCC1of2vout(EVAL_HEIR, amount, pk1, pk2)); - - CScript opret; - fundingtxid = revuint256(fundingtxid); - - opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'A' << fundingtxid << (uint8_t)0); - - cp = CCinit(&C, EVAL_HEIR); - return(FinalizeCCTx(0, cp, mtx, myPubkey, txfee, opret)); -} - -extern bool komodo_appendACscriptpub(); - -UniValue test_heirmarker(const UniValue& params, bool fHelp) -{ - //make fake token tx: - struct CCcontract_info *cp, C; - - if (fHelp || (params.size() != 1)) - throw runtime_error("incorrect params\n"); - if (ensure_CCrequirements(EVAL_HEIR) < 0) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - - uint256 fundingtxid = Parseuint256((char *)params[0].get_str().c_str()); - - CPubKey myPubkey = pubkey2pk(Mypubkey()); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - - int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); - if (normalInputs < 10000) - throw runtime_error("not enough normals\n"); - - mtx.vin.push_back(CTxIn(fundingtxid, 1)); - mtx.vout.push_back(MakeCC1vout(EVAL_HEIR, 10000, myPubkey)); - - CScript opret; - fundingtxid = revuint256(fundingtxid); - - opret << OP_RETURN << E_MARSHAL(ss << (uint8_t)EVAL_HEIR << (uint8_t)'C' << fundingtxid << (uint8_t)0); - - cp = CCinit(&C, EVAL_HEIR); - return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, opret)); -} - UniValue opreturn_burn(const UniValue& params, bool fHelp) { + std::vector vHexStr; CScript opret; int32_t txfee = 10000; if (fHelp || (params.size() != 2)) throw runtime_error("amount to burn, hexstring to send\n"); struct CCcontract_info *cp, C; UniValue ret(UniValue::VOBJ); @@ -8050,122 +8010,22 @@ UniValue opreturn_burn(const UniValue& params, bool fHelp) CAmount nAmount = AmountFromValue(params[0]); if (nAmount <= 10000) - throw JSONRPCError(RPC_TYPE_ERROR, "must send at least 10000 sat"); - std::string strHex = params[1].get_str(); + throw JSONRPCError(RPC_TYPE_ERROR, "must burn at least 10000 sat"); + vHexStr = ParseHex(params[1].get_str()); + if ( vHexStr.size() == 0 ) + throw JSONRPCError(RPC_TYPE_ERROR, "hexstring is not valid."); + CPubKey myPubkey = pubkey2pk(Mypubkey()); CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); int64_t normalInputs = AddNormalinputs(mtx, myPubkey, nAmount, 60); if (normalInputs < nAmount) throw runtime_error("not enough normals\n"); - - CScript opret; uint8_t scripthex[8192]; - - decode_hex(scripthex,strHex.size()/2,(char *)strHex.c_str()); - std::string test; - test.append((char*)scripthex); - std::vector opretdata(test.begin(), test.end()); - opret << OP_RETURN << E_MARSHAL(ss << opretdata); - /*CScript opret; uint8_t *ptr; - opret << OP_RETURN << 0; - int32_t len = strlen(strHex.c_str()); - len >>=1; - opret.resize(len+2); - ptr = (uint8_t *)&opret[1]; - decode_hex(ptr,len,(char *)strHex.c_str()); */ - mtx.vout.push_back(CTxOut(nAmount,opret)); - ret.push_back(Pair("hex",FinalizeCCTx(0, cp, mtx, myPubkey, 10000, CScript()))); + + opret << OP_RETURN << E_MARSHAL(ss << vHexStr); + + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(myPubkey)) << OP_CHECKSIG)); + mtx.vout.push_back(CTxOut(nAmount,opret)); + ret.push_back(Pair("hex",FinalizeCCTx(0, cp, mtx, myPubkey, txfee, CScript()))); return(ret); } - -UniValue test_burntx(const UniValue& params, bool fHelp) -{ - // make fake token tx: - struct CCcontract_info *cp, C; - - if (fHelp || (params.size() != 1)) - throw runtime_error("incorrect params\n"); - if (ensure_CCrequirements(EVAL_TOKENS) < 0) - throw runtime_error("to use CC contracts, you need to launch daemon with valid -pubkey= for an address in your wallet\n"); - - uint256 tokenid = Parseuint256((char *)params[0].get_str().c_str()); - - CPubKey myPubkey = pubkey2pk(Mypubkey()); - CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); - - int64_t normalInputs = AddNormalinputs(mtx, myPubkey, 10000, 60); - if (normalInputs < 10000) - throw runtime_error("not enough normals\n"); - - CPubKey burnpk = pubkey2pk(ParseHex(CC_BURNPUBKEY)); - - mtx.vin.push_back(CTxIn(tokenid, 0)); - mtx.vin.push_back(CTxIn(tokenid, 1)); - mtx.vout.push_back(MakeTokensCC1vout(EVAL_TOKENS, 1, burnpk)); - - std::vector voutPubkeys; - voutPubkeys.push_back(burnpk); - - cp = CCinit(&C, EVAL_TOKENS); - - std::vector vopret; - GetNonfungibleData(tokenid, vopret); - if (vopret.size() > 0) - cp->additionalTokensEvalcode2 = vopret.begin()[0]; - - uint8_t tokenpriv[33]; - char unspendableTokenAddr[64]; - CPubKey unspPk = GetUnspendable(cp, tokenpriv); - GetCCaddress(cp, unspendableTokenAddr, unspPk); - CCaddr2set(cp, EVAL_TOKENS, unspPk, tokenpriv, unspendableTokenAddr); - return(FinalizeCCTx(0, cp, mtx, myPubkey, 10000, EncodeTokenOpRet(tokenid, voutPubkeys, std::make_pair(0, vscript_t())))); -} - -UniValue test_proof(const UniValue& params, bool fHelp) -{ - UniValue result(UniValue::VOBJ); - std::vectorproof; - - if (fHelp || (params.size() != 2)) - throw runtime_error("incorrect params\n"); - - - proof = ParseHex(params[0].get_str()); - uint256 cointxid = Parseuint256((char *)params[1].get_str().c_str()); - - std::vector txids; - - CMerkleBlock merkleBlock; - if (!E_UNMARSHAL(proof, ss >> merkleBlock)) { - result.push_back(Pair("error", "could not unmarshal proof")); - return result; - } - uint256 merkleRoot = merkleBlock.txn.ExtractMatches(txids); - - result.push_back(Pair("source_root", merkleRoot.GetHex())); - - for (int i = 0; i < txids.size(); i++) - std::cerr << "merkle block txid=" << txids[0].GetHex() << std::endl; - - - std::vector vMatches(txids.size()); - for (auto v : vMatches) v = true; - CPartialMerkleTree verifTree(txids, vMatches); - - result.push_back(Pair("verif_root", verifTree.ExtractMatches(txids).GetHex())); - - if (std::find(txids.begin(), txids.end(), cointxid) == txids.end()) { - fprintf(stderr, "invalid proof for this cointxid\n"); - } - - std::vector vMerkleTree; - bool f; - ::BuildMerkleTree(&f, txids, vMerkleTree); - - std::vector vMerkleBranch = ::GetMerkleBranch(0, txids.size(), vMerkleTree); - - uint256 ourResult = SafeCheckMerkleBranch(zeroid, vMerkleBranch, 0); - result.push_back(Pair("SafeCheckMerkleBranch", ourResult.GetHex())); - - return result; -}