diff --git a/src/cc/CCPrices.h b/src/cc/CCPrices.h index 9bcae60b2..933f08e19 100644 --- a/src/cc/CCPrices.h +++ b/src/cc/CCPrices.h @@ -49,6 +49,7 @@ 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(uint32_t filter, CPubKey mypk); +UniValue PricesGetOrderbook(); #endif diff --git a/src/cc/prices.cpp b/src/cc/prices.cpp index 31a63f93d..2ac89613c 100644 --- a/src/cc/prices.cpp +++ b/src/cc/prices.cpp @@ -20,16 +20,38 @@ #define IS_CHARINSTR(c, str) (std::string(str).find((char)(c)) != std::string::npos) -#define N_CCMARKER 1 +#define NVOUT_CCMARKER 1 +#define NVOUT_NORMALMARKER 3 -typedef struct BetInfo { - int64_t amount; +typedef struct OneBetData { + int64_t positionsize; int32_t firstheight; int64_t costbasis; int64_t profits; - BetInfo() { amount = 0; firstheight = 0; costbasis = 0; profits = 0; } // it is important to clear costbasis as it will be calculated as minmax from inital value 0 -} betinfo; + 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; @@ -145,7 +167,7 @@ uint8_t prices_finalopretdecode(CScript scriptPubKey,uint256 &bettxid,int64_t &p } // price opret basic validation and retrieval -static uint8_t CheckPricesOpret(const CTransaction & tx, vscript_t &opret) +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]; @@ -217,7 +239,7 @@ static bool ValidateAddFundingTx(struct CCcontract_info *cp, Eval *eval, const C return eval->Invalid("cannot decode opreturn for add funding tx"); pricespk = GetUnspendable(cp, 0); - uint8_t vintxFuncId = CheckPricesOpret(vintx, vintxOpret); + uint8_t vintxFuncId = PricesCheckOpret(vintx, vintxOpret); if (vintxFuncId != 'A' && vintxFuncId != 'B') { // if vintx is bettx return eval->Invalid("incorrect vintx funcid"); } @@ -352,7 +374,7 @@ bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx if (strcmp(ASSETCHAINS_SYMBOL, "REKT0") == 0 && chainActive.Height() < 2965) return true; // check basic opret rules: - if (CheckPricesOpret(tx, vopret) == 0) + if (PricesCheckOpret(tx, vopret) == 0) return eval->Invalid("tx has no prices opreturn"); uint8_t funcId = vopret.begin()[1]; @@ -375,7 +397,7 @@ bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx if (!myGetTransaction(vin.prevout.hash, vintx, hashBlock)) return eval->Invalid("cannot load vintx"); - if (CheckPricesOpret(vintx, vintxOpret) == 0) { + 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; } @@ -486,7 +508,7 @@ int64_t AddPricesInputs(struct CCcontract_info *cp, CMutableTransaction &mtx, ch if (GetTransaction(txid, vintx, hashBlock, false) != 0 && vout < vintx.vout.size()) { vscript_t vopret; - uint8_t funcId = CheckPricesOpret(vintx, vopret); + uint8_t funcId = PricesCheckOpret(vintx, vopret); if (funcId == 'B' && vout == 1) // skip cc marker continue; @@ -1215,7 +1237,7 @@ int32_t prices_syntheticprofits(int64_t &costbasis, int32_t firstheight, int32_t } // makes result json object -void prices_betjson(UniValue &result, std::vector bets, int16_t leverage, int32_t endheight, int64_t lastprice) +void prices_betjson(UniValue &result, std::vector bets, int16_t leverage, int32_t endheight, int64_t lastprice) { UniValue resultbets(UniValue::VARR); @@ -1224,12 +1246,12 @@ void prices_betjson(UniValue &result, std::vector bets, int16_t leverag for (auto b : bets) { UniValue entry(UniValue::VOBJ); - entry.push_back(Pair("positionsize", ValueFromAmount(b.amount))); + 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.amount; + totalbets += b.positionsize; totalprofits += b.profits; } int64_t equity = totalbets + totalprofits; @@ -1277,7 +1299,7 @@ int64_t prices_costbasis(CTransaction bettx, uint256 &txidCostbasis) } // enumerates and retrieves added bets, returns the last baton txid -int64_t prices_enumaddedbets(uint256 &batontxid, std::vector &bets, uint256 bettxid) +int64_t prices_enumaddedbets(uint256 &batontxid, std::vector &bets, uint256 bettxid) { int64_t addedBetsTotal = 0; int32_t vini; @@ -1304,10 +1326,10 @@ int64_t prices_enumaddedbets(uint256 &batontxid, std::vector &bets, uin txBaton.vout.size() > 0 && (funcId = prices_addopretdecode(txBaton.vout.back().scriptPubKey, bettxidInOpret, pk, amount)) != 0) { - BetInfo added; + OneBetData added; addedBetsTotal += amount; - added.amount = amount; + added.positionsize = amount; added.firstheight = blockIdx.GetHeight(); bets.push_back(added); std::cerr << "prices_batontxid() added amount=" << amount << std::endl; @@ -1357,9 +1379,9 @@ UniValue PricesBet(int64_t txfee, int64_t amount, int16_t leverage, std::vector< betamount = (amount * 199) / 200; 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 (N_CCMARKER) + 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 - TODO: remove it as we have cc marker now, when move to the new chain + 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 rawtx = FinalizeCCTx(0, cp, mtx, mypk, txfee, prices_betopret(mypk, nextheight - 1, amount, leverage, firstprice, vec, zeroid)); return(prices_rawtxresult(result, rawtx, 0)); } @@ -1389,7 +1411,7 @@ UniValue PricesAddFunding(int64_t txfee, uint256 bettxid, int64_t amount) //GetCCaddress(cp, myaddr, mypk); if (AddNormalinputs(mtx, mypk, amount + 2*txfee, 64) >= amount + 2*txfee) { - std::vector bets; + std::vector bets; if (prices_enumaddedbets(batontxid, bets, bettxid) >= 0) { mtx.vin.push_back(CTxIn(batontxid, 0, CScript())); @@ -1411,7 +1433,7 @@ UniValue PricesAddFunding(int64_t txfee, uint256 bettxid, int64_t amount) } // 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) { +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; @@ -1427,13 +1449,13 @@ int32_t prices_scanchain(std::vector &bets, int16_t leverage, std::vect if (height > bets[i].firstheight) { - int32_t retcode = prices_syntheticprofits(bets[i].costbasis, bets[i].firstheight, height, leverage, vec, bets[i].amount, bets[i].profits, lastprice); + 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].amount; + totalbets += bets[i].positionsize; totalprofits += bets[i].profits; } } @@ -1523,233 +1545,39 @@ UniValue PricesSetcostbasis(int64_t txfee, uint256 bettxid) return(result); } -// 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 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, firstprice, lastprice = 0, positionsize; - int32_t firstheight; - int16_t leverage; - std::vector vec; - CPubKey pk, mypk, pricespk; - std::string rawtx; - char destaddr[64]; - - cp = CCinit(&C, EVAL_PRICES); - if (txfee == 0) - txfee = PRICES_TXFEE; - mypk = pubkey2pk(Mypubkey()); - pricespk = GetUnspendable(cp, 0); - GetCCaddress(cp, destaddr, pricespk); - - if (myGetTransaction(bettxid, bettx, hashBlock) != 0 && bettx.vout.size() > 3) - { - if (prices_betopretdecode(bettx.vout.back().scriptPubKey, pk, firstheight, positionsize, leverage, firstprice, vec, tokenid) == 'B') - { - uint256 finaltxid; - int32_t vini; - int32_t finalheight, endheight; - std::vector bets; - BetInfo bet1; - - if (CCgetspenttxid(finaltxid, vini, finalheight, bettxid, N_CCMARKER) == 0) { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "position closed")); - return result; - } - - bet1.amount = positionsize; - bet1.firstheight = firstheight; - bets.push_back(bet1); - - prices_enumaddedbets(batontxid, bets, bettxid); - - if (prices_scanchain(bets, leverage, vec, lastprice, endheight) < 0) { - 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 : bets) { - totalbets += b.amount; - totalprofits += b.profits; - } - - prices_betjson(result, bets, leverage, endheight, lastprice); // fill output json - - int64_t equity = totalbets + totalprofits; - if (equity < 0) - { - myfee = totalbets / 500; // consolation fee for loss - } - if (myfee != 0) - { - int64_t CCchange = 0, inputsum; - - mtx.vin.push_back(CTxIn(bettxid, N_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, firstprice, 0, totalbets - positionsize, positionsize, leverage)); - return(prices_rawtxresult(result, rawtx, 0)); - } - else - { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "position not rekt")); - return(result); - } - } - else - { - 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 load or incorrect bettx")); - return(result); -} - -// pricescashout rpc impl: bettor can cashout hit bet if it is not rekt -UniValue PricesCashout(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; char destaddr[64]; - CTransaction bettx; - uint256 hashBlock, batontxid, tokenid; - int64_t CCchange = 0, positionsize, inputsum, firstprice, lastprice = 0; - int32_t firstheight; - 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); - GetCCaddress(cp, destaddr, pricespk); - if (myGetTransaction(bettxid, bettx, hashBlock) != 0 && bettx.vout.size() > 3) - { - if (prices_betopretdecode(bettx.vout.back().scriptPubKey, pk, firstheight, positionsize, leverage, firstprice, vec, tokenid) == 'B') - { - uint256 finaltxid; - int32_t vini; - int32_t finalheight, endheight; - std::vector bets; - BetInfo bet1; - - if (CCgetspenttxid(finaltxid, vini, finalheight, bettxid, N_CCMARKER) == 0) { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "position closed")); - return result; - } - - bet1.amount = positionsize; - bet1.firstheight = firstheight; - bets.push_back(bet1); - prices_enumaddedbets(batontxid, bets, bettxid); - - if (prices_scanchain(bets, leverage, vec, lastprice, endheight) < 0) { - 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 : bets) { - totalbets += b.amount; - totalprofits += b.profits; - } - prices_betjson(result, bets, leverage, endheight, lastprice); // fill output json - - int64_t equity = totalbets + totalprofits; - if (equity < 0) - { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "position rekt")); - return(result); - } - - mtx.vin.push_back(CTxIn(bettxid, N_CCMARKER, CScript())); // spend cc marker - if ((inputsum = AddPricesInputs(cp, mtx, destaddr, equity + txfee, 64)) > equity + txfee) - CCchange = (inputsum - equity); - mtx.vout.push_back(CTxOut(equity, 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, totalprofits, nextheight - 1, mypk, firstprice, 0, totalbets-positionsize, positionsize, leverage)); - return(prices_rawtxresult(result, rawtx, 0)); - } - else - { - 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 load or incorrect bettx")); - return(result); -} - -// 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; - //uint256 costbasistxid; if (myGetTransaction(bettxid, bettx, hashBlock) && bettx.vout.size() > 3) { if (hashBlock.IsNull()) - throw std::runtime_error("tx still in mempool"); + return -2; - if (prices_betopretdecode(bettx.vout.back().scriptPubKey, pk, firstheight, positionsize, leverage, firstprice, vec, tokenid) == 'B') + OneBetData bet1; + if (prices_betopretdecode(bettx.vout.back().scriptPubKey, betinfo.pk, bet1.firstheight, bet1.positionsize, betinfo.leverage, betinfo.firstprice, betinfo.parsed, betinfo.tokenid) == 'B') { uint256 finaltxid; int32_t vini; - int32_t finalheight, endheight; - std::vector bets; - BetInfo bet1; + int32_t finaltxheight; //, endheight; + //std::vector bets; - if (CCgetspenttxid(finaltxid, vini, finalheight, bettxid, N_CCMARKER) == 0) - result.push_back(Pair("status", "closed")); + + if (CCgetspenttxid(finaltxid, vini, finaltxheight, bettxid, NVOUT_CCMARKER) == 0) + betinfo.isOpen = false; else - result.push_back(Pair("status", "open")); + betinfo.isOpen = true; - bet1.amount = positionsize; - bet1.firstheight = firstheight; - bets.push_back(bet1); + //bet1.amount = betinfo.positionsize; + //bet1.firstheight = firstheight; + betinfo.bets.push_back(bet1); - prices_enumaddedbets(batontxid, bets, bettxid); + prices_enumaddedbets(batontxid, betinfo.bets, bettxid); - if( prices_scanchain(bets, leverage, vec, lastprice, endheight) < 0 ) { - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "error scanning chain")); - return(result); + if (prices_scanchain(betinfo.bets, betinfo.leverage, betinfo.parsed, betinfo.lastprice, betinfo.lastheight) < 0) { + return -4; } mpz_t mpzTotalbets; @@ -1760,38 +1588,37 @@ UniValue PricesInfo(uint256 bettxid, int32_t refheight) mpz_init(mpzTotalprofits); mpz_init(mpzTotalcostbasis); - int64_t totalbets = 0; int64_t totalprofits = 0; - for (auto b : bets) { + 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.amount); // b.costbasis * b.amount + 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.amount); //totalbets += b.amount; + mpz_add_ui(mpzTotalbets, mpzTotalbets, (uint64_t)b.positionsize); //totalbets += b.amount; mpz_add(mpzTotalprofits, mpzTotalprofits, mpzProfits); //totalprofits += b.profits; - totalbets += b.amount; + totalbets += b.positionsize; totalprofits += b.profits; mpz_clear(mpzProduct); mpz_clear(mpzProfits); } - int64_t equity = totalbets + totalprofits; - int64_t averageCostbasis = 0; + betinfo.equity = totalbets + totalprofits; + //int64_t averageCostbasis = 0; if (mpz_get_ui(mpzTotalbets) != 0) { //prevent zero div mpz_t mpzAverageCostbasis; @@ -1799,49 +1626,272 @@ UniValue PricesInfo(uint256 bettxid, int32_t refheight) //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(mpzAverageCostbasis, mpzTotalcostbasis, mpzTotalbets); mpz_tdiv_q_ui(mpzAverageCostbasis, mpzAverageCostbasis, SATOSHIDEN); // profits /= SATOSHIDEN de-normalization - averageCostbasis = mpz_get_ui(mpzAverageCostbasis); + betinfo.averageCostbasis = mpz_get_ui(mpzAverageCostbasis); mpz_clear(mpzAverageCostbasis); } - int64_t liqprice = 0; - if (leverage != 0) {// prevent zero div - liqprice = averageCostbasis - averageCostbasis / leverage; + betinfo.liquidationprice = 0; + if (betinfo.leverage != 0) {// prevent zero div + betinfo.liquidationprice = betinfo.averageCostbasis - betinfo.averageCostbasis / betinfo.leverage; } - if (equity >= 0) - result.push_back(Pair("rekt", 0)); + if (betinfo.equity >= 0) + betinfo.isRekt = true; else { - result.push_back(Pair("rekt", (int64_t)1)); - result.push_back(Pair("rektfee", totalbets / 500)); - result.push_back(Pair("rektheight", (int64_t)endheight)); + betinfo.isRekt = false; + betinfo.rektfee = totalbets / 500; } - std::string expr = prices_getsourceexpression(vec); - 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(averageCostbasis))); -#ifdef TESTMODE - result.push_back(Pair("costbasis_test_period", 7)); -#endif - - prices_betjson(result, bets, leverage, endheight, lastprice); - - result.push_back(Pair("LiquidationPrice", ValueFromAmount(liqprice))); - mpz_clear(mpzTotalbets); mpz_clear(mpzTotalprofits); mpz_clear(mpzTotalcostbasis); + return 0; + } + return -3; + } + return (-1); +} + +// 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; + 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); + + 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); } - result.push_back(Pair("result", "error")); - result.push_back(Pair("error", "cant find bettxid or incorrect")); + + 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); + } +} + +// pricescashout rpc impl: bettor can cashout hit bet if it is not rekt +UniValue PricesCashout(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; 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) + { + 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 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); } @@ -1852,25 +1902,20 @@ UniValue PricesList(uint32_t filter, CPubKey mypk) 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 priceslist = [&](std::vector >::const_iterator it, int32_t nvout) + auto AddBetToList = [&](uint256 txid) { int64_t amount, firstprice; int32_t height; int16_t leverage; - uint256 txid, hashBlock, tokenid; + uint256 hashBlock, tokenid; CPubKey pk, pricespk; std::vector vec; CTransaction vintx; - txid = it->first.txhash; - if (nvout != it->first.index) // our marker vout - return; - if (GetTransaction(txid, vintx, hashBlock, false) != 0) { bool bAppend = false; @@ -1884,7 +1929,7 @@ UniValue PricesList(uint32_t filter, CPubKey mypk) int32_t height; uint256 finaltxid; - int32_t spent = CCgetspenttxid(finaltxid, vini, height, txid, N_CCMARKER); + 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; @@ -1892,7 +1937,7 @@ UniValue PricesList(uint32_t filter, CPubKey mypk) if (bAppend) result.push_back(txid.GetHex()); } - std::cerr << "PricesList() " << " bettxid=" << txid.GetHex() << " mypk=" << HexStr(mypk) << " opretpk=" << HexStr(pk) << " filter=" << filter << " bAppend=" << bAppend << " index=" << it->first.index << " txindex=" << it->first.txindex << std::endl; + std::cerr << "PricesList() " << " bettxid=" << txid.GetHex() << " mypk=" << HexStr(mypk) << " opretpk=" << HexStr(pk) << " filter=" << filter << " bAppend=" << bAppend << std::endl; } }; @@ -1900,7 +1945,8 @@ UniValue PricesList(uint32_t filter, CPubKey mypk) SetCCtxids(addressIndex, cp->normaladdr, false); // old normal marker for (std::vector >::const_iterator it = addressIndex.begin(); it != addressIndex.end(); it++) { - priceslist(it, 3); + if( it->first.txindex == NVOUT_NORMALMARKER ) + AddBetToList(it->first.txhash); } /* for future when switch to cc marker only @@ -1911,4 +1957,33 @@ UniValue PricesList(uint32_t filter, CPubKey mypk) } */ 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; } \ No newline at end of file diff --git a/src/rpc/blockchain.cpp b/src/rpc/blockchain.cpp index 1f6fcadcf..cb2cfce92 100644 --- a/src/rpc/blockchain.cpp +++ b/src/rpc/blockchain.cpp @@ -1439,6 +1439,19 @@ UniValue pricesrekt(const UniValue& params, bool fHelp) 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) { diff --git a/src/rpc/server.cpp b/src/rpc/server.cpp index d18f551e6..30258a6e0 100644 --- a/src/rpc/server.cpp +++ b/src/rpc/server.cpp @@ -470,6 +470,8 @@ static const CRPCCommand vRPCCommands[] = { "prices", "pricescashout", &pricescashout, true }, { "prices", "pricesrekt", &pricesrekt, true }, { "prices", "pricesaddfunding", &pricesaddfunding, true }, + { "prices", "pricesgetorderbook", &pricesgetorderbook, true }, + // Pegs { "pegs", "pegsaddress", &pegsaddress, true }, diff --git a/src/rpc/server.h b/src/rpc/server.h index a215f9bc3..d4942eb69 100644 --- a/src/rpc/server.h +++ b/src/rpc/server.h @@ -503,6 +503,8 @@ 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); + #endif // BITCOIN_RPCSERVER_H