From 7b832d7124d72473ca58066589970a9c764e45d0 Mon Sep 17 00:00:00 2001 From: jl777 Date: Tue, 26 Mar 2019 23:00:55 -1100 Subject: [PATCH] Games dir --- src/cc/{ => games}/prices.c | 6 +- src/cc/{tetris.cpp => games/prices.cpp} | 0 src/cc/{ => games}/prices.h | 0 src/cc/{ => games}/tetris.c | 0 src/cc/games/tetris.cpp | 73 ++++ src/cc/{ => games}/tetris.h | 0 src/cc/makeprices | 3 + src/cc/prices.cpp | 462 +++++++++++++++++++++--- 8 files changed, 500 insertions(+), 44 deletions(-) rename src/cc/{ => games}/prices.c (99%) rename src/cc/{tetris.cpp => games/prices.cpp} (100%) rename src/cc/{ => games}/prices.h (100%) rename src/cc/{ => games}/tetris.c (100%) create mode 100644 src/cc/games/tetris.cpp rename src/cc/{ => games}/tetris.h (100%) diff --git a/src/cc/prices.c b/src/cc/games/prices.c similarity index 99% rename from src/cc/prices.c rename to src/cc/games/prices.c index 651b1018c..02366fbc4 100644 --- a/src/cc/prices.c +++ b/src/cc/games/prices.c @@ -1,5 +1,5 @@ -#include "tetris.h" +#include "prices.h" /* In order to port a game into gamesCC, the RNG needs to be seeded with the gametxid seed, also events needs to be broadcast using issue_games_events. Also the game engine needs to be daemonized, preferably by putting all globals into a single data structure. @@ -13,7 +13,7 @@ int random_tetromino(struct games_state *rs) return(rs->seed % NUM_TETROMINOS); } -int32_t tetrisdata(struct games_player *P,void *ptr) +int32_t pricesdata(struct games_player *P,void *ptr) { tetris_game *tg = (tetris_game *)ptr; P->gold = tg->points; @@ -832,7 +832,7 @@ int32_t issue_games_events(struct games_state *rs,char *gametxidstr,uint32_t eve } else return(0); } -int tetris(int argc, char **argv) +int prices(int argc, char **argv) { struct games_state *rs = &globalR; int32_t c,skipcount=0; uint32_t eventid = 0; tetris_game *tg = 0; diff --git a/src/cc/tetris.cpp b/src/cc/games/prices.cpp similarity index 100% rename from src/cc/tetris.cpp rename to src/cc/games/prices.cpp diff --git a/src/cc/prices.h b/src/cc/games/prices.h similarity index 100% rename from src/cc/prices.h rename to src/cc/games/prices.h diff --git a/src/cc/tetris.c b/src/cc/games/tetris.c similarity index 100% rename from src/cc/tetris.c rename to src/cc/games/tetris.c diff --git a/src/cc/games/tetris.cpp b/src/cc/games/tetris.cpp new file mode 100644 index 000000000..8a52dcb37 --- /dev/null +++ b/src/cc/games/tetris.cpp @@ -0,0 +1,73 @@ + +/****************************************************************************** + * 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. * + * * + ******************************************************************************/ + +// game specific code for daemon +void games_packitemstr(char *packitemstr,struct games_packitem *item) +{ + strcpy(packitemstr,""); +} + +int64_t games_cashout(struct games_player *P) +{ + int32_t dungeonlevel = P->dungeonlevel; int64_t mult=1000,cashout = 0; + cashout = (uint64_t)P->gold * mult * dungeonlevel * dungeonlevel; + return(cashout); +} + +void tetrisplayerjson(UniValue &obj,struct games_player *P) +{ + obj.push_back(Pair("packsize",(int64_t)P->packsize)); + obj.push_back(Pair("hitpoints",(int64_t)P->hitpoints)); + obj.push_back(Pair("strength",(int64_t)(P->strength&0xffff))); + obj.push_back(Pair("maxstrength",(int64_t)(P->strength>>16))); + obj.push_back(Pair("level",(int64_t)P->level)); + obj.push_back(Pair("experience",(int64_t)P->experience)); + obj.push_back(Pair("dungeonlevel",(int64_t)P->dungeonlevel)); +} + +int32_t disp_gamesplayer(char *str,struct games_player *P) +{ + str[0] = 0; + //if ( P->gold <= 0 )//|| P->hitpoints <= 0 || (P->strength&0xffff) <= 0 || P->level <= 0 || P->experience <= 0 || P->dungeonlevel <= 0 ) + // return(-1); + sprintf(str," <- playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",P->gold,P->hitpoints,P->strength&0xffff,P->strength>>16,P->level,P->experience,P->dungeonlevel); + return(0); +} + +int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) +{ + uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; + if ( (len= payload.size()) > 36 ) + { + len -= 36; + for (i=0; i<32; i++) + ((uint8_t *)&gametxid)[i] = payload[len+i]; + eventid = (uint32_t)payload[len+32]; + eventid |= (uint32_t)payload[len+33] << 8; + eventid |= (uint32_t)payload[len+34] << 16; + eventid |= (uint32_t)payload[len+35] << 24; + //for (i=0; i oracletxid start with 'L', leverage, funding, direction + funds are locked into global CC address + it can be closed at anytime by the trader for cash settlement + the house account can close it if rekt + + Implementation Notes: + In order to eliminate the need for worrying about sybil attacks, each prices plan would be able to specific pubkey(s?) for whitelisted publishers. It would be possible to have a non-whitelisted plan that would use 50% correlation between publishers. + + delta neutral balancing of riskexposure: fabs(long exposure - short exposure) + bet +B at leverage L + absval(sum(+BLi) - sum(-Bli)) + + validate: update riskexposure and it needs to be <= funds + + PricesProfits: limit withdraw to funds in excess of riskexposure + PricesFinish: payout (if winning) and update riskexposure + need long/short exposure assets + + funding -> 1of2 CC global CC address and dealer address, exposure tokens to global 1of2 assets CC address + pricebet -> user funds and exposure token to 1of2 address. + pricewin -> winnings from dealer funds, exposure token back to global address + priceloss -> exposuretoken back to global address + + exposure address, funds address + + + +*/ + +// start of consensus code + +int64_t PricesOraclePrice(int64_t &rektprice,uint64_t mode,uint256 oracletxid,std::vectorpubkeys,int32_t dir,int64_t amount,int32_t leverage) { - strcpy(packitemstr,""); + int64_t price; + // howto ensure price when block it confirms it not known + // get price from oracle + current chaintip + // normalize leveraged amount + if ( dir > 0 ) + rektprice = price * leverage / (leverage-1); + else rektprice = price * (leverage-1) / leverage; + return(price); } -int64_t games_cashout(struct games_player *P) +CScript EncodePricesFundingOpRet(uint8_t funcid,CPubKey planpk,uint256 oracletxid,uint256 longtoken,uint256 shorttoken,int32_t millimargin,uint64_t mode,int32_t maxleverage,std::vector pubkeys,uint256 bettoken) { - int32_t dungeonlevel = P->dungeonlevel; int64_t mult=1000,cashout = 0; - cashout = (uint64_t)P->gold * mult * dungeonlevel * dungeonlevel; - return(cashout); + CScript opret; + fprintf(stderr,"implement EncodePricesFundingOpRet\n"); + return(opret); } -void tetrisplayerjson(UniValue &obj,struct games_player *P) +uint8_t DecodePricesFundingOpRet(CScript scriptPubKey,CPubKey &planpk,uint256 &oracletxid,uint256 &longtoken,uint256 &shorttoken,int32_t &millimargin,uint64_t &mode,int32_t &maxleverage,std::vector &pubkeys,uint256 &bettoken) { - obj.push_back(Pair("packsize",(int64_t)P->packsize)); - obj.push_back(Pair("hitpoints",(int64_t)P->hitpoints)); - obj.push_back(Pair("strength",(int64_t)(P->strength&0xffff))); - obj.push_back(Pair("maxstrength",(int64_t)(P->strength>>16))); - obj.push_back(Pair("level",(int64_t)P->level)); - obj.push_back(Pair("experience",(int64_t)P->experience)); - obj.push_back(Pair("dungeonlevel",(int64_t)P->dungeonlevel)); -} - -int32_t disp_gamesplayer(char *str,struct games_player *P) -{ - str[0] = 0; - //if ( P->gold <= 0 )//|| P->hitpoints <= 0 || (P->strength&0xffff) <= 0 || P->level <= 0 || P->experience <= 0 || P->dungeonlevel <= 0 ) - // return(-1); - sprintf(str," <- playerdata: gold.%d hp.%d strength.%d/%d level.%d exp.%d dl.%d",P->gold,P->hitpoints,P->strength&0xffff,P->strength>>16,P->level,P->experience,P->dungeonlevel); + fprintf(stderr,"implement DecodePricesFundingOpRet\n"); return(0); } -int32_t games_payloadrecv(CPubKey pk,uint32_t timestamp,std::vector payload) +bool PricesValidate(struct CCcontract_info *cp,Eval* eval,const CTransaction &tx, uint32_t nIn) { - uint256 gametxid; int32_t i,len; char str[67]; uint32_t eventid = 0; - if ( (len= payload.size()) > 36 ) + int32_t numvins,numvouts,preventCCvins,preventCCvouts,i,numblocks; bool retval; uint256 txid; uint8_t hash[32]; char str[65],destaddr[64]; + + return true; // TODO remove, for test dual-evals + + return eval->Invalid("no validation yet"); + std::vector > txids; + numvins = tx.vin.size(); + numvouts = tx.vout.size(); + preventCCvins = preventCCvouts = -1; + if ( numvouts < 1 ) + return eval->Invalid("no vouts"); + else { - len -= 36; - for (i=0; i<32; i++) - ((uint8_t *)&gametxid)[i] = payload[len+i]; - eventid = (uint32_t)payload[len+32]; - eventid |= (uint32_t)payload[len+33] << 8; - eventid |= (uint32_t)payload[len+34] << 16; - eventid |= (uint32_t)payload[len+35] << 24; - //for (i=0; iInvalid("illegal normal vini"); + } + } + //fprintf(stderr,"check amounts\n"); + //if ( PricesExactAmounts(cp,eval,tx,1,10000) == false ) + { + fprintf(stderr,"Pricesget invalid amount\n"); + return false; + } + //else + { + txid = tx.GetHash(); + memcpy(hash,&txid,sizeof(hash)); + retval = PreventCC(eval,tx,preventCCvins,numvins,preventCCvouts,numvouts); + if ( retval != 0 ) + fprintf(stderr,"Pricesget validated\n"); + else fprintf(stderr,"Pricesget invalid\n"); + return(retval); + } + } } +// end of consensus code -bool games_validate(struct CCcontract_info *cp,int32_t height,Eval *eval,const CTransaction tx) +// helper functions for rpc calls in rpcwallet.cpp + +int64_t AddTokensInputs(struct CCcontract_info *cp,CMutableTransaction &mtx,char *destaddr,uint256 tolenid,int64_t total,int32_t maxinputs) { - return(true); + // add threshold check + 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); + for (std::vector >::const_iterator it=unspentOutputs.begin(); it!=unspentOutputs.end(); it++) + { + txid = it->first.txhash; + vout = (int32_t)it->first.index; + // need to prevent dup + if ( GetTransaction(txid,vintx,hashBlock,false) != 0 && vout < vintx.vout.size() ) + { + // need to verify assetid + if ( (nValue= vintx.vout[vout].nValue) >= 10000 && myIsutxo_spentinmempool(ignoretxid,ignorevin,txid,vout) == 0 ) + { + 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) ) + break; + } + } + } + return(totalinputs); } +UniValue PricesList() +{ + UniValue result(UniValue::VARR); std::vector > addressIndex; struct CCcontract_info *cp,C; uint64_t mode; int32_t margin,maxleverage; std::vectorpubkeys; uint256 txid,hashBlock,oracletxid,longtoken,shorttoken,bettoken; CPubKey planpk,pricespk; char str[65]; CTransaction vintx; + cp = CCinit(&C,EVAL_PRICES); + pricespk = GetUnspendable(cp,0); + SetCCtxids(addressIndex,cp->normaladdr); + 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 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) + { + result.push_back(uint256_str(str,txid)); + } + } + } + return(result); +} + +// longtoken satoshis limits long exposure +// shorttoken satoshis limits short exposure +// both must be in the 1of2 CC address with its total supply +// bettoken +std::string PricesCreateFunding(uint64_t txfee,uint256 bettoken,uint256 oracletxid,uint64_t margin,uint64_t mode,uint256 longtoken,uint256 shorttoken,int32_t maxleverage,int64_t funding,std::vector pubkeys) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + CTransaction oracletx; int64_t fullsupply,inputs,CCchange=0; uint256 hashBlock; char str[65],coinaddr[64],houseaddr[64]; CPubKey mypk,pricespk; int32_t i,N,numvouts; struct CCcontract_info *cp,C; + if ( funding < 100*COIN || maxleverage <= 0 || maxleverage > 10000 ) + { + CCerror = "invalid parameter error"; + fprintf(stderr,"%s\n", CCerror.c_str() ); + return(""); + } + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + if ( (N= (int32_t)pubkeys.size()) || N > 15 ) + { + fprintf(stderr,"too many pubkeys N.%d\n",N); + return(""); + } + for (i=0; i 0 ) + { + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(mypk)) << OP_CHECKSIG)); + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(pricespk)) << OP_CHECKSIG)); + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesFundingOpRet('F',mypk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken))); + } + else + { + CCerror = "cant find enough inputs"; + fprintf(stderr,"%s\n", CCerror.c_str() ); + } + return(""); +} + +UniValue PricesInfo(uint256 fundingtxid) +{ + UniValue result(UniValue::VOBJ),a(UniValue::VARR); CPubKey pricespk,planpk; uint256 hashBlock,oracletxid,longtoken,shorttoken,bettoken; CTransaction vintx; int64_t balance,supply,exposure; uint64_t funding,mode; int32_t i,margin,maxleverage; char numstr[65],houseaddr[64],exposureaddr[64],str[65]; std::vectorpubkeys; struct CCcontract_info *cp,C; + cp = CCinit(&C,EVAL_PRICES); + pricespk = GetUnspendable(cp,0); + if ( GetTransaction(fundingtxid,vintx,hashBlock,false) == 0 ) + { + fprintf(stderr,"cant find fundingtxid\n"); + ERR_RESULT("cant find fundingtxid"); + return(result); + } + if ( vintx.vout.size() > 0 && DecodePricesFundingOpRet(vintx.vout[vintx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' ) + { + result.push_back(Pair("result","success")); + result.push_back(Pair("fundingtxid",uint256_str(str,fundingtxid))); + result.push_back(Pair("bettoken",uint256_str(str,bettoken))); + result.push_back(Pair("oracletxid",uint256_str(str,oracletxid))); + sprintf(numstr,"%.3f",(double)margin/1000); + result.push_back(Pair("profitmargin",numstr)); + result.push_back(Pair("maxleverage",maxleverage)); + result.push_back(Pair("mode",(int64_t)mode)); + for (i=0; ipubkeys; + if ( amount < 10000 ) + { + CCerror = "amount must be positive"; + fprintf(stderr,"%s\n", CCerror.c_str() ); + return(""); + } + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + GetCCaddress(cp,myaddr,mypk); + if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) + { + fprintf(stderr,"cant find fundingtxid\n"); + return(""); + } + if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) + { + GetCCaddress1of2(cp,houseaddr,pricespk,planpk); + if ( AddNormalinputs(mtx,mypk,2*txfee,3) > 0 ) + { + if ( (inputs= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,60)) >= amount ) + { + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); + mtx.vout.push_back(CTxOut(txfee,CScript() << ParseHex(HexStr(planpk)) << OP_CHECKSIG)); + if ( inputs > amount+txfee ) + CCchange = (inputs - amount); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange,mypk)); + // add addr2 + + std::vector voutTokenPubkeysEmpty; //TODO: add token vout pubkeys + return(FinalizeCCTx(0,cp,mtx,mypk,txfee, + EncodeTokenOpRet(bettoken, voutTokenPubkeysEmpty, + std::make_pair(OPRETID_ASSETSDATA, EncodeAssetOpRet('t',/*bettoken,*/zeroid, 0, Mypubkey()))))); + } + else + { + CCerror = "cant find enough bet inputs"; + fprintf(stderr,"%s\n", CCerror.c_str() ); + } + } + else + { + CCerror = "cant find enough inputs"; + fprintf(stderr,"%s\n", CCerror.c_str() ); + } + } + return(""); +} + +std::string PricesBet(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,int64_t amount,int32_t leverage) +{ + CMutableTransaction mtx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), komodo_nextheight()); + struct CCcontract_info *cp,C; CPubKey pricespk,planpk,mypk; uint256 hashBlock,oracletxid,longtoken,shorttoken,tokenid,bettoken; CTransaction tx; int64_t balance,supply,exposure,inputs,inputs2,longexposure,netexposure,shortexposure,CCchange = 0,CCchange2 = 0; uint64_t funding,mode; int32_t dir,margin,maxleverage; char houseaddr[64],myaddr[64],exposureaddr[64]; std::vectorpubkeys; + if ( amount < 0 ) + { + amount = -amount; + dir = -1; + } else dir = 1; + cp = CCinit(&C,EVAL_PRICES); + if ( txfee == 0 ) + txfee = 10000; + mypk = pubkey2pk(Mypubkey()); + pricespk = GetUnspendable(cp,0); + GetCCaddress(cp,myaddr,mypk); + if ( GetTransaction(fundingtxid,tx,hashBlock,false) == 0 ) + { + fprintf(stderr,"cant find fundingtxid\n"); + return(""); + } + if ( tx.vout.size() > 0 && DecodePricesFundingOpRet(tx.vout[tx.vout.size()-1].scriptPubKey,planpk,oracletxid,longtoken,shorttoken,margin,mode,maxleverage,pubkeys,bettoken) == 'F' && bettoken == refbettoken ) + { + if ( leverage > maxleverage || leverage < 1 ) + { + fprintf(stderr,"illegal leverage\n"); + return(""); + } + GetCCaddress1of2(cp,houseaddr,pricespk,planpk); + GetCCaddress1of2(cp,exposureaddr,pricespk,pricespk); + if ( dir < 0 ) + tokenid = shorttoken; + else tokenid = longtoken; + exposure = leverage * amount; + longexposure = CCtoken_balance(exposureaddr,longtoken); + shortexposure = CCtoken_balance(exposureaddr,shorttoken); + netexposure = (longexposure - shortexposure + exposure*dir); + if ( netexposure < 0 ) + netexposure = -netexposure; + balance = CCtoken_balance(myaddr,bettoken) / COIN; + if ( balance < netexposure*9/10 ) // 10% extra room for dynamically closed bets in wrong direction + { + fprintf(stderr,"balance %lld < 90%% netexposure %lld, refuse bet\n",(long long)balance,(long long)netexposure); + return(""); + } + if ( AddNormalinputs(mtx,mypk,txfee,3) > 0 ) + { + if ( (inputs= AddTokensInputs(cp,mtx,houseaddr,tokenid,exposure,30)) >= exposure ) + { + if ( (inputs2= AddTokensInputs(cp,mtx,myaddr,bettoken,amount,30)) >= amount ) + { + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,amount,pricespk,planpk)); + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,exposure,pricespk,pricespk)); + if ( inputs > exposure+txfee ) + CCchange = (inputs - exposure); + if ( inputs2 > amount+txfee ) + CCchange2 = (inputs2 - amount); + mtx.vout.push_back(MakeCC1of2vout(cp->evalcode,CCchange,pricespk,planpk)); + mtx.vout.push_back(MakeCC1vout(cp->evalcode,CCchange2,mypk)); + // add addr2 and addr3 + //return(FinalizeCCTx(0,cp,mtx,mypk,txfee,EncodePricesExtra('T',tokenid,bettoken,zeroid,dir*leverage))); + CScript opret; + return(FinalizeCCTx(0,cp,mtx,mypk,txfee,opret)); + } + else + { + fprintf(stderr,"cant find enough bettoken inputs\n"); + return(""); + } + } + else + { + fprintf(stderr,"cant find enough exposure inputs\n"); + return(""); + } + } + else + { + CCerror = "cant find enough inputsB"; + fprintf(stderr,"%s\n", CCerror.c_str() ); + } + } + return(""); +} + +UniValue PricesStatus(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) +{ + UniValue result(UniValue::VOBJ); + // get height of bettxid + // get price and rekt + // get current height and price + // what about if rekt in the past? + return(result); +} + +std::string PricesFinish(uint64_t txfee,uint256 refbettoken,uint256 fundingtxid,uint256 bettxid) +{ + return(""); +} + +