diff --git a/README.md b/README.md index f7cb8a330..985d18afa 100644 --- a/README.md +++ b/README.md @@ -100,3 +100,11 @@ postCall('{"agent":"iguana","method":"test"}'} iguana_JSON("{\"agent\":\"iguana",\"method\":\"test\"}"); -> direct C function call +iguana can be invoked with a command line argument. if it is a name of a file, it will load it and check to see if it is valid JSON and if it is, it will use it. Otherwise the command line argument needs to be valid JSON to be used and it will process the JSON to initialize account passphrases, exchange apikeys, etc. A few special keys: + +"wallet" -> passphrase used for the persistent privkey +"2fafile" -> secondary part (optional) for the persistent privkey +"numhelpers" -> number of helper threads (need at least 1) +"exchanges" -> { "name":"", ... } + "apikey", "apisecret", "userid", "tradepassword" these are as expected + "pollgap" -> gap between each access to exchange for getting prices diff --git a/iguana/SuperNET_keys.c b/iguana/SuperNET_keys.c index 8d50e3bb7..a50dc7333 100644 --- a/iguana/SuperNET_keys.c +++ b/iguana/SuperNET_keys.c @@ -43,30 +43,40 @@ bits256 SuperNET_wallet2priv(char *wallet2fname,bits256 wallethash) char *SuperNET_parsemainargs(struct supernet_info *myinfo,bits256 *wallethashp,bits256 *wallet2privp,char *argjsonstr) { - cJSON *json; uint8_t secretbuf[512]; char *wallet2fname,*coinargs=0,*secret; - bits256 wallethash,wallet2priv; int32_t len; + cJSON *exchanges,*json = 0; char *wallet2fname,*coinargs=0,*secret,*filestr; + long allocsize; bits256 wallethash,wallet2priv; int32_t n,len; uint8_t secretbuf[8192]; wallethash = wallet2priv = GENESIS_PRIVKEY; - if ( argjsonstr != 0 && (json= cJSON_Parse(argjsonstr)) != 0 ) + if ( argjsonstr != 0 ) { - printf("ARGSTR.(%s)\n",argjsonstr); - if ( jobj(json,"numhelpers") != 0 ) - IGUANA_NUMHELPERS = juint(json,"numhelpers"); - if ( (secret= jstr(json,"wallet")) != 0 ) + if ( (filestr= OS_filestr(&allocsize,argjsonstr)) != 0 ) { - len = (int32_t)strlen(secret); - if ( is_hexstr(secret,0) != 0 && len == 128 ) + json = cJSON_Parse(filestr); + free(filestr); + } + if ( json != 0 || (json= cJSON_Parse(argjsonstr)) != 0 ) + { + printf("ARGSTR.(%s)\n",argjsonstr); + if ( jobj(json,"numhelpers") != 0 ) + IGUANA_NUMHELPERS = juint(json,"numhelpers"); + if ( (secret= jstr(json,"wallet")) != 0 ) { - len >>= 1; - decode_hex(secretbuf,len,secret); - } else vcalc_sha256(0,secretbuf,(void *)secret,len), len = sizeof(bits256); - memcpy(wallethash.bytes,secretbuf,sizeof(wallethash)); - //printf("wallethash.(%s)\n",bits256_str(str,wallethash)); - if ( (wallet2fname= jstr(json,"2fafile")) != 0 ) - wallet2priv = SuperNET_wallet2priv(wallet2fname,wallethash); + len = (int32_t)strlen(secret); + if ( is_hexstr(secret,0) != 0 && len == 128 ) + { + len >>= 1; + decode_hex(secretbuf,len,secret); + } else vcalc_sha256(0,secretbuf,(void *)secret,len), len = sizeof(bits256); + memcpy(wallethash.bytes,secretbuf,sizeof(wallethash)); + //printf("wallethash.(%s)\n",bits256_str(str,wallethash)); + if ( (wallet2fname= jstr(json,"2fafile")) != 0 ) + wallet2priv = SuperNET_wallet2priv(wallet2fname,wallethash); + } + if ( (exchanges= jarray(&n,json,"exchanges")) != 0 ) + exchanges777_init(myinfo,exchanges,0); + if ( jobj(json,"coins") != 0 ) + coinargs = argjsonstr; + free_json(json); } - if ( jobj(json,"coins") != 0 ) - coinargs = argjsonstr; - free_json(json); } *wallethashp = wallethash, *wallet2privp = wallet2priv; return(coinargs); diff --git a/iguana/exchanges777.h b/iguana/exchanges777.h index e6ac2e2df..837369263 100755 --- a/iguana/exchanges777.h +++ b/iguana/exchanges777.h @@ -25,7 +25,7 @@ #define EXCHANGES777_DEFAULT_TIMEOUT 30 struct exchange_info; -struct exchange_quote { double price,volume; uint64_t orderid,offerNXT; uint32_t timestamp; }; +struct exchange_quote { double price,volume; uint64_t orderid,offerNXT; uint32_t timestamp,val; }; struct exchange_funcs { @@ -53,6 +53,24 @@ struct exchange_info CURL *cHandle; queue_t requestQ,pricesQ,pendingQ[2],tradebotsQ; }; +struct instantdex_msghdr +{ + struct acct777_sig sig __attribute__((packed)); + char cmd[8]; + uint8_t serialized[]; +} __attribute__((packed)); + +struct exchange_request +{ + struct queueitem DL; + cJSON *argjson; char **retstrp; + double price,volume,hbla,lastbid,lastask,commission; + uint64_t orderid; uint32_t timedout,expiration,dead,timestamp; + int32_t dir,depth,func,numbids,numasks; + char base[32],rel[32],destaddr[64],invert,allflag,dotrade; + struct exchange_quote bidasks[]; +}; + void *curl_post(void **cHandlep,char *url,char *userpass,char *postfields,char *hdr0,char *hdr1,char *hdr2,char *hdr3); char *InstantDEX_hexmsg(struct supernet_info *myinfo,void *data,int32_t len,char *remoteaddr); char *instantdex_sendcmd(struct supernet_info *myinfo,cJSON *argjson,char *cmdstr,char *ipaddr,int32_t hops); @@ -61,6 +79,8 @@ struct exchange_info *exchanges777_info(char *exchangestr,int32_t sleepflag,cJSO char *exchanges777_unmonitor(struct exchange_info *exchange,char *base,char *rel); void tradebot_timeslice(struct exchange_info *exchange,void *bot); char *exchanges777_Qtrade(struct exchange_info *exchange,char *base,char *rel,int32_t maxseconds,int32_t dotrade,int32_t dir,double price,double volume,cJSON *argjson); +struct exchange_request *exchanges777_baserelfind(struct exchange_info *exchange,char *base,char *rel,int32_t func); void prices777_processprice(struct exchange_info *exchange,char *base,char *rel,struct exchange_quote *bidasks,int32_t maxdepth); + #endif diff --git a/iguana/iguana777.h b/iguana/iguana777.h index 01200b210..f474e4200 100755 --- a/iguana/iguana777.h +++ b/iguana/iguana777.h @@ -721,7 +721,7 @@ cJSON *iguana_voutjson(struct iguana_info *coin,struct iguana_msgvout *vout,int3 cJSON *iguana_vinjson(struct iguana_info *coin,struct iguana_msgvin *vin); char *iguana_rawtxbytes(struct iguana_info *coin,cJSON *json,uint8_t *data,int32_t datalen); int32_t iguana_send_VPNversion(struct iguana_info *coin,struct iguana_peer *addr,uint64_t myservices); -void exchanges777_init(int32_t sleepflag); +void exchanges777_init(struct supernet_info *myinfo,cJSON *exchanges,int32_t sleepflag); extern queue_t bundlesQ; diff --git a/iguana/iguana_exchanges.c b/iguana/iguana_exchanges.c index da7be6ed6..00fec1dac 100755 --- a/iguana/iguana_exchanges.c +++ b/iguana/iguana_exchanges.c @@ -19,17 +19,6 @@ #define EXCHANGE777_ISPENDING 2 #define EXCHANGE777_REQUEUE 3 -struct exchange_request -{ - struct queueitem DL; - cJSON *argjson; char **retstrp; - double price,volume,hbla,lastbid,lastask,commission; - uint64_t orderid; uint32_t timedout,expiration; - int32_t dir,depth,func,numbids,numasks; - char base[32],rel[32],destaddr[64],invert,allflag,dotrade; - struct exchange_quote bidasks[]; -}; - char *Exchange_names[] = { "bitfinex", "btc38", "bitstamp", "btce", "poloniex", "bittrex", "huobi", "coinbase", "okcoin", "lakebtc", "quadriga", "truefx", "ecb", "instaforex", "fxcm", "yahoo" }; struct exchange_info *Exchanges[sizeof(Exchange_names)/sizeof(*Exchange_names)]; @@ -439,7 +428,7 @@ char *exchanges777_process(struct exchange_info *exchange,int32_t *retvalp,struc *retvalp = EXCHANGE777_DONE; switch ( req->func ) { - case 'Q': + case 'Q': case 'M': memset(req->bidasks,0,req->depth * sizeof(*req->bidasks) * 2); (*exchange->issue.price)(exchange,req->base,req->rel,req->bidasks,req->depth,req->commission,req->argjson); retstr = exchanges777_orderbook_jsonstr(exchange,req->base,req->rel,req->bidasks,req->depth,req->invert,req->allflag); @@ -490,42 +479,51 @@ char *exchanges777_process(struct exchange_info *exchange,int32_t *retvalp,struc void exchanges777_loop(void *ptr) { struct exchange_info *exchange = ptr; - int32_t flag,retval; struct exchange_request *req; char *retstr; void *bot; + int32_t flag,retval,i; struct exchange_request *req; char *retstr; void *bot; while ( 1 ) { flag = retval = 0; retstr = 0; if ( (req= queue_dequeue(&exchange->requestQ,0)) != 0 ) { - //printf("dequeued %s.%c\n",exchange->name,req->func); - retstr = exchanges777_process(exchange,&retval,req); - if ( retval == EXCHANGE777_DONE ) + if ( req->dead == 0 ) { - if ( retstr != 0 ) + //printf("dequeued %s.%c\n",exchange->name,req->func); + retstr = exchanges777_process(exchange,&retval,req); + if ( retval == EXCHANGE777_DONE ) { - if ( req->retstrp != 0 && req->timedout == 0 ) - *req->retstrp = retstr; - else free(retstr); - if ( req->timedout != 0 ) - printf("timedout.%u req finally finished at %u\n",req->timedout,(uint32_t)time(NULL)); + if ( retstr != 0 ) + { + if ( req->retstrp != 0 && req->timedout == 0 ) + *req->retstrp = retstr; + else free(retstr); + if ( req->timedout != 0 ) + printf("timedout.%u req finally finished at %u\n",req->timedout,(uint32_t)time(NULL)); + } + free(req); + flag++; } - free(req); - flag++; - } - else - { - if ( retstr != 0 ) - free(retstr); - if ( retval == EXCHANGE777_ISPENDING ) - queue_enqueue("Xpending",&exchange->pendingQ[0],&req->DL,0), flag++; - else if ( retval == EXCHANGE777_REQUEUE ) - queue_enqueue("requeue",&exchange->requestQ,&req->DL,0); else { - printf("exchanges777_process: illegal retval.%d\n",retval); - free(req); + if ( retstr != 0 ) + free(retstr); + if ( retval == EXCHANGE777_ISPENDING ) + queue_enqueue("Xpending",&exchange->pendingQ[0],&req->DL,0), flag++; + else if ( retval == EXCHANGE777_REQUEUE ) + queue_enqueue("requeue",&exchange->requestQ,&req->DL,0); + else + { + printf("exchanges777_process: illegal retval.%d\n",retval); + free(req); + } } } + else + { + if ( req->retstrp != 0 ) + *req->retstrp = clonestr("{\"result\":\"request killed\"}"); + free(req); + } } if ( (bot= queue_dequeue(&exchange->tradebotsQ,0)) != 0 ) tradebot_timeslice(exchange,bot); @@ -533,14 +531,29 @@ void exchanges777_loop(void *ptr) { if ( (req= queue_dequeue(&exchange->pricesQ,0)) != 0 ) { - if ( req->base[0] != 0 ) + if ( req->dead == 0 ) + { + if ( req->base[0] != 0 ) + { + //printf("check %s pricesQ (%s %s)\n",exchange->name,req->base,req->rel); + req->timestamp = exchange->lastpoll = (uint32_t)time(NULL); + req->hbla = (*exchange->issue.price)(exchange,req->base,req->rel,req->bidasks,req->depth,req->commission,req->argjson); + for (i=req->numbids=0; idepth; i++) + if ( req->bidasks[i << 1].price > SMALLVAL ) + req->numbids++; + for (i=req->numasks=0; idepth; i++) + if ( req->bidasks[(i << 1) + 1].price > SMALLVAL ) + req->numasks++; + prices777_processprice(exchange,req->base,req->rel,req->bidasks,req->depth); + } + queue_enqueue("pricesQ",&exchange->pricesQ,&req->DL,0); + } + else { - //printf("check %s pricesQ (%s %s)\n",exchange->name,req->base,req->rel); - exchange->lastpoll = (uint32_t)time(NULL); - req->hbla = (*exchange->issue.price)(exchange,req->base,req->rel,req->bidasks,req->depth,req->commission,req->argjson); - prices777_processprice(exchange,req->base,req->rel,req->bidasks,req->depth); + if ( req->retstrp != 0 ) + *req->retstrp = clonestr("{\"result\":\"request killed\"}"); + free(req); } - queue_enqueue("pricesQ",&exchange->pricesQ,&req->DL,0); } } if ( flag == 0 ) @@ -548,19 +561,28 @@ void exchanges777_loop(void *ptr) } } -char *exchanges777_unmonitor(struct exchange_info *exchange,char *base,char *rel) +struct exchange_request *exchanges777_baserelfind(struct exchange_info *exchange,char *base,char *rel,int32_t func) { - struct exchange_request PAD,*req; char *retstr = 0; + struct exchange_request PAD,*req,*retreq=0; memset(&PAD,0,sizeof(PAD)); queue_enqueue("pricesQ",&exchange->pricesQ,&PAD.DL,0); while ( (req= queue_dequeue(&exchange->pricesQ,0)) != 0 && req != &PAD ) { - if ( strcmp(base,req->base) == 0 || strcmp(rel,req->rel) == 0 ) - { - printf("unmonitor.%s (%s %s)\n",exchange->name,base,rel); - free(req); - retstr = clonestr("{\"result\":\"unmonitored\"}"); - } else queue_enqueue("pricesQ",&exchange->pricesQ,&req->DL,0); + if ( ((strcmp(base,req->base) == 0 && strcmp(rel,req->rel) == 0) || (strcmp(rel,req->base) == 0 && strcmp(base,req->rel) == 0)) && (func < 0 || req->func == func) ) + retreq = req; + else queue_enqueue("pricesQ",&exchange->pricesQ,&req->DL,0); + } + return(retreq); +} + +char *exchanges777_unmonitor(struct exchange_info *exchange,char *base,char *rel) +{ + struct exchange_request *req; char *retstr = 0; + if ( (req= exchanges777_baserelfind(exchange,base,rel,'M')) != 0 ) + { + printf("unmonitor.%s (%s %s)\n",exchange->name,base,rel); + req->dead = (uint32_t)time(NULL); + retstr = clonestr("{\"result\":\"mark priceQ entry as dead\"}"); } if ( retstr == 0 ) retstr = clonestr("{\"error\":\"cant find base/rel pair to unmonitor\"}"); @@ -619,7 +641,7 @@ char *exchanges777_Qprices(struct exchange_info *exchange,char *base,char *rel,i return(exchanges777_submit(exchange,&retstr,req,'Q',maxseconds)); else { - req->func = 'Q'; + req->func = 'M'; queue_enqueue("pricesQ",&exchange->pricesQ,&req->DL,0); return(clonestr("{\"result\":\"start monitoring\"}")); } @@ -665,6 +687,8 @@ struct exchange_info *exchange_create(char *exchangestr,cJSON *argjson) huobi_funcs, lakebtc_funcs, quadriga_funcs, okcoin_funcs, coinbase_funcs, bitstamp_funcs }; char *key,*secret,*userid,*tradepassword; struct exchange_info *exchange; int32_t i,exchangeid; + if ( exchangestr == 0 || exchangestr[0] == 0 ) + return(0); if ( (exchangeid= exchanges777_id(exchangestr)) < 0 ) { printf("exchange_create: cant find.(%s)\n",exchangestr); @@ -725,9 +749,19 @@ struct exchange_info *exchanges777_info(char *exchangestr,int32_t sleepflag,cJSO return(exchange); } -void exchanges777_init(int32_t sleepflag) +void exchanges777_init(struct supernet_info *myinfo,cJSON *exchanges,int32_t sleepflag) { - int32_t i; cJSON *argjson; bits256 instantdexhash; + int32_t i,n; cJSON *argjson,*item; bits256 instantdexhash; struct exchange_info *exchange; + if ( exchanges != 0 ) + { + n = cJSON_GetArraySize(exchanges); + for (i=0; itradingexchanges[myinfo->numexchanges++] = exchange; + } + } if ( 0 ) { argjson = cJSON_CreateObject(); diff --git a/iguana/iguana_instantdex.c b/iguana/iguana_instantdex.c index bb7e99242..5eac14ca0 100755 --- a/iguana/iguana_instantdex.c +++ b/iguana/iguana_instantdex.c @@ -15,13 +15,6 @@ #include "exchanges777.h" -struct instantdex_msghdr -{ - struct acct777_sig sig __attribute__((packed)); - char cmd[8]; - uint8_t serialized[]; -} __attribute__((packed)); - int32_t instantdex_rwdata(int32_t rwflag,uint64_t cmdbits,uint8_t *data,int32_t datalen) { // need to inplace serialize/deserialize here @@ -85,11 +78,22 @@ char *instantdex_sendcmd(struct supernet_info *myinfo,cJSON *argjson,char *cmdst } } +void instantdex_updatesources(int32_t dir,struct exchange_quote *quotes,int32_t numquotes) +{ + int32_t i; struct exchange_quote *quote; + for (i=0; itradingexchanges[i]) != 0 ) { - + if ( (req= exchanges777_baserelfind(exchange,base,rel,'M')) == 0 ) + { + if ( (str= exchanges777_Qprices(exchange,base,rel,30,1,depth,argjson,1,exchange->commission)) != 0 ) + free(str); + req = exchanges777_baserelfind(exchange,base,rel,'M'); + } + if ( req == 0 ) + { + if ( (*exchange->issue.supports)(exchange,base,rel,argjson) != 0 ) + printf("unexpected null req.(%s %s) %s\n",base,rel,exchange->name); + } else active[num++] = req; } } + for (i=0; inumbids > 0 ) + instantdex_updatesources(1,active[i]->bidasks,active[i]->numbids); + else if ( volume > 0. && active[i]->numasks > 0 ) + instantdex_updatesources(-1,&active[i]->bidasks[1],active[i]->numasks); + } return(clonestr("{\"result\":\"reqprice response sent\"}")); } return(clonestr("{\"error\":\"request missing parameter\"}")); diff --git a/iguana/iguana_tradebots.c b/iguana/iguana_tradebots.c index 3dc41ffda..8e3c5be38 100755 --- a/iguana/iguana_tradebots.c +++ b/iguana/iguana_tradebots.c @@ -86,7 +86,7 @@ cJSON *tradebot_json(struct supernet_info *myinfo,struct exchange_info *exchange return(json); } -struct tradebot_info *tradebot_find(struct supernet_info *myinfo,struct exchange_info *exchange,char *botname,cJSON *array) +struct tradebot_info *tradebot_find(struct supernet_info *myinfo,struct exchange_info *exchange,char *botname,cJSON *array,char *base,char *rel) { struct tradebot_info PAD,*bot,*retbot = 0; memset(&PAD,0,sizeof(PAD)); @@ -97,6 +97,8 @@ struct tradebot_info *tradebot_find(struct supernet_info *myinfo,struct exchange retbot = bot; if ( array != 0 ) jaddi(array,tradebot_json(myinfo,exchange,bot)); + if ( base != 0 && rel != 0 && strcmp(base,bot->base) == 0 && strcmp(rel,bot->rel) == 0 ) + retbot = bot; queue_enqueue("tradebotsQ",&exchange->tradebotsQ,&bot->DL,0); } return(retbot); @@ -158,30 +160,6 @@ void tradebot_timeslice(struct exchange_info *exchange,void *_bot) } else free(bot); } -#include "../includes/iguana_apidefs.h" - -THREE_STRINGS_AND_DOUBLE(tradebot,monitor,exchange,base,rel,commission) -{ - int32_t allfields = 1,depth = 50; struct exchange_info *ptr; - if ( remoteaddr == 0 ) - { - if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 ) - return(exchanges777_Qprices(ptr,base,rel,30,allfields,depth,json,1,commission * .01)); - else return(clonestr("{\"error\":\"couldnt find/create exchange info\"}")); - } else return(clonestr("{\"error\":\"tradebots only local usage!\"}")); -} - -THREE_STRINGS(tradebot,unmonitor,exchange,base,rel) -{ - struct exchange_info *ptr; - if ( remoteaddr == 0 ) - { - if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 ) - return(exchanges777_unmonitor(ptr,base,rel)); - else return(clonestr("{\"error\":\"couldnt find/create exchange info\"}")); - } else return(clonestr("{\"error\":\"tradebots only local usage!\"}")); -} - char *tradebot_launch(struct supernet_info *myinfo,char *exchangestr,char *base,char *rel,int32_t dir,double price,double volume,int32_t duration,char *remoteaddr,cJSON *json) { struct exchange_info *exchange; char retbuf[1024]; struct tradebot_info *bot; @@ -206,7 +184,7 @@ char *tradebot_control(struct supernet_info *myinfo,char *exchangestr,char *boti { if ( (exchange= exchanges777_info(exchangestr,1,json,remoteaddr)) != 0 ) { - if ( (bot= tradebot_find(myinfo,exchange,botid,0)) != 0 ) + if ( (bot= tradebot_find(myinfo,exchange,botid,0,0,0)) != 0 ) { if ( control > 1 ) bot->pause = (uint32_t)time(NULL); @@ -220,6 +198,30 @@ char *tradebot_control(struct supernet_info *myinfo,char *exchangestr,char *boti } else return(clonestr("{\"error\":\"tradebots only local usage!\"}")); } +#include "../includes/iguana_apidefs.h" + +THREE_STRINGS_AND_DOUBLE(tradebot,monitor,exchange,base,rel,commission) +{ + int32_t allfields = 1,depth = 50; struct exchange_info *ptr; + if ( remoteaddr == 0 ) + { + if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 ) + return(exchanges777_Qprices(ptr,base,rel,30,allfields,depth,json,1,commission * .01)); + else return(clonestr("{\"error\":\"couldnt find/create exchange info\"}")); + } else return(clonestr("{\"error\":\"tradebots only local usage!\"}")); +} + +THREE_STRINGS(tradebot,unmonitor,exchange,base,rel) +{ + struct exchange_info *ptr; + if ( remoteaddr == 0 ) + { + if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 ) + return(exchanges777_unmonitor(ptr,base,rel)); + else return(clonestr("{\"error\":\"couldnt find/create exchange info\"}")); + } else return(clonestr("{\"error\":\"tradebots only local usage!\"}")); +} + THREE_STRINGS_AND_THREE_DOUBLES(tradebot,accumulate,exchange,base,rel,price,volume,duration) { return(tradebot_launch(myinfo,exchange,base,rel,1,price,volume,duration,remoteaddr,json)); @@ -237,7 +239,7 @@ TWO_STRINGS(tradebot,status,exchange,botid) { if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 ) { - if ( (bot= tradebot_find(myinfo,ptr,botid,0)) != 0 ) + if ( (bot= tradebot_find(myinfo,ptr,botid,0,0,0)) != 0 ) return(jprint(tradebot_json(myinfo,ptr,bot),1)); } } @@ -251,7 +253,7 @@ STRING_ARG(tradebot,activebots,exchange) { if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 ) { - tradebot_find(myinfo,ptr,0,array); + tradebot_find(myinfo,ptr,0,array,0,0); retjson = cJSON_CreateObject(); jaddstr(retjson,"result","success"); jadd(retjson,"tradebots",array);