/****************************************************************************** * Copyright © 2014-2018 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. * * * ******************************************************************************/ // // LP_statemachine.c // marketmaker // char DEX_baseaddr[64],DEX_reladdr[64]; struct mmpending_order { double price,volume; int32_t dir; uint32_t pending,completed,canceled,cancelstarted,reported; cJSON *errorjson; char exchange[16],base[65],rel[65],orderid[64]; } *Pending_orders; int32_t Num_Pending; #define IGUANA_URL "http://127.0.0.1:7778" /*char CURRENCIES[][8] = { "USD", "EUR", "JPY", "GBP", "AUD", "CAD", "CHF", "NZD", // major currencies "CNY", "RUB", "MXN", "BRL", "INR", "HKD", "TRY", "ZAR", "PLN", "NOK", "SEK", "DKK", "CZK", "HUF", "ILS", "KRW", "MYR", "PHP", "RON", "SGD", "THB", "BGN", "IDR", "HRK", // end of currencies };*/ double PAXPRICES[sizeof(CURRENCIES)/sizeof(*CURRENCIES)]; uint32_t PAXACTIVE; char *DEX_swapstatus() { char url[512],postdata[1024]; sprintf(url,"%s/?",IGUANA_URL); sprintf(postdata,"{\"agent\":\"InstantDEX\",\"method\":\"getswaplist\"}"); return(bitcoind_RPC(0,"InstantDEX",url,0,"getswaplist",postdata,0)); } char *DEX_amlp(char *blocktrail) { char url[512],postdata[1024]; sprintf(url,"%s/?",IGUANA_URL); sprintf(postdata,"{\"agent\":\"tradebot\",\"method\":\"amlp\",\"blocktrail\":\"%s\"}",blocktrail); return(bitcoind_RPC(0,"tradebot",url,0,"amlp",postdata,0)); } char *DEX_openorders(char *exchange) { char url[512],postdata[1024]; sprintf(url,"%s/?",IGUANA_URL); sprintf(postdata,"{\"agent\":\"InstantDEX\",\"method\":\"openorders\",\"exchange\":\"%s\"}",exchange); return(bitcoind_RPC(0,"InstantDEX",url,0,"openorders",postdata,0)); } char *DEX_tradehistory(char *exchange) { char url[512],postdata[1024]; sprintf(url,"%s/?",IGUANA_URL); sprintf(postdata,"{\"agent\":\"InstantDEX\",\"method\":\"tradehistory\",\"exchange\":\"%s\"}",exchange); return(bitcoind_RPC(0,"InstantDEX",url,0,"tradehistory",postdata,0)); } char *DEX_orderstatus(char *exchange,char *orderid) { char url[512],postdata[1024]; sprintf(url,"%s/?",IGUANA_URL); sprintf(postdata,"{\"agent\":\"InstantDEX\",\"method\":\"orderstatus\",\"exchange\":\"%s\",\"orderid\":\"%s\"}",exchange,orderid); return(bitcoind_RPC(0,"InstantDEX",url,0,"orderstatus",postdata,0)); } char *DEX_cancelorder(char *exchange,char *orderid) { char url[512],postdata[1024]; sprintf(url,"%s/?",IGUANA_URL); sprintf(postdata,"{\"agent\":\"InstantDEX\",\"method\":\"cancelorder\",\"exchange\":\"%s\",\"orderid\":\"%s\"}",exchange,orderid); return(bitcoind_RPC(0,"InstantDEX",url,0,"cancelorder",postdata,0)); } char *DEX_balance(char *exchange,char *base,char *coinaddr) { char url[512],postdata[1024]; sprintf(url,"%s/?",IGUANA_URL); if ( strcmp(exchange,"DEX") == 0 ) { sprintf(postdata,"{\"agent\":\"dex\",\"method\":\"getbalance\",\"address\":\"%s\",\"symbol\":\"%s\"}",coinaddr,base); return(bitcoind_RPC(0,"dex",url,0,"getbalance",postdata,0)); } else { sprintf(postdata,"{\"agent\":\"InstantDEX\",\"method\":\"balance\",\"exchange\":\"%s\",\"base\":\"%s\"}",exchange,base); return(bitcoind_RPC(0,"InstantDEX",url,0,"balance",postdata,0)); } } char *DEX_apikeypair(char *exchange,char *apikey,char *apisecret) { char url[512],postdata[1024]; sprintf(url,"%s/?",IGUANA_URL); sprintf(postdata,"{\"agent\":\"InstantDEX\",\"method\":\"apikeypair\",\"exchange\":\"%s\",\"apikey\":\"%s\",\"apisecret\":\"%s\"}",exchange,apikey,apisecret); return(bitcoind_RPC(0,"InstantDEX",url,0,"apikeypair",postdata,0)); } char *DEX_setuserid(char *exchange,char *userid,char *tradepassword) { char url[512],postdata[1024]; sprintf(url,"%s/?",IGUANA_URL); sprintf(postdata,"{\"agent\":\"InstantDEX\",\"method\":\"setuserid\",\"exchange\":\"%s\",\"userid\":\"%s\",\"tradepassword\":\"%s\"}",exchange,userid,tradepassword); return(bitcoind_RPC(0,"InstantDEX",url,0,"setuserid",postdata,0)); } char *DEX_trade(char *exchange,char *base,char *rel,int32_t dir,double price,double volume) { char url[512],postdata[1024]; sprintf(url,"%s/?",IGUANA_URL); sprintf(postdata,"{\"agent\":\"InstantDEX\",\"method\":\"%s\",\"exchange\":\"%s\",\"base\":\"%s\",\"rel\":\"%s\",\"price\":%.8f,\"volume\":%.8f,\"dotrade\":1}",dir>0?"buy":"sell",exchange,base,rel,price,volume); //printf("DEX_trade.(%s)\n",postdata); return(bitcoind_RPC(0,"InstantDEX",url,0,dir>0?"buy":"sell",postdata,0)); } char *DEX_withdraw(char *exchange,char *base,char *destaddr,double amount) { char url[512],postdata[1024]; sprintf(url,"%s/?",IGUANA_URL); sprintf(postdata,"{\"agent\":\"InstantDEX\",\"method\":\"withdraw\",\"exchange\":\"%s\",\"destaddr\":\"%s\",\"amount\":%.8f}",exchange,destaddr,amount); return(bitcoind_RPC(0,"InstantDEX",url,0,"withdraw",postdata,0)); } char *iguana_walletpassphrase(char *passphrase,int32_t timeout) { char url[512],postdata[1024]; sprintf(url,"%s/coin=KMD&agent=bitcoinrpc&method=walletpassphrase?",IGUANA_URL); sprintf(postdata,"[\"%s\", %d]",passphrase,timeout); return(bitcoind_RPC(0,"",url,0,"walletpassphrase",postdata,0)); } /*char *iguana_listunspent(char *coin,char *coinaddr) { char url[512],postdata[1024]; sprintf(url,"%s/coin=%s&agent=bitcoinrpc&method=listunspent?",IGUANA_URL,coin); sprintf(postdata,"[\"%s\"]",coinaddr); return(bitcoind_RPC(0,"",url,0,"listunspent",postdata)); }*/ /*char *issue_LP_intro(char *destip,uint16_t destport,char *ipaddr,uint16_t port,int32_t numpeers) { char url[512]; sprintf(url,"http://%s:%u/api/stats/intro?ipaddr=%s&port=%u&numpeers=%d",destip,destport,ipaddr,port,numpeers); printf("(%s)\n",url); return(issue_curl(url)); }*/ // // http://127.0.0.1:7779/api/stats/getpeers char *DEX_listunspent(char *coin,char *coinaddr) { char url[512],postdata[1024]; sprintf(url,"%s/?",IGUANA_URL); sprintf(postdata,"{\"agent\":\"dex\",\"method\":\"listunspent\",\"address\":\"%s\",\"symbol\":\"%s\",\"timeout\":60000}",coinaddr,coin); return(bitcoind_RPC(0,"dex",url,0,"listunspent",postdata,0)); } bits256 iguana_wif2privkey(char *wifstr) { char url[512],postdata[1024],*retstr,*privstr; bits256 privkey; cJSON *retjson; memset(privkey.bytes,0,sizeof(privkey)); sprintf(url,"%s/?",IGUANA_URL); sprintf(postdata,"{\"agent\":\"SuperNET\",\"method\":\"wif2priv\",\"wif\":\"%s\"}",wifstr); if ( (retstr= bitcoind_RPC(0,"SuperNET",url,0,"wif2priv",postdata,0)) != 0 ) { if ( (retjson= cJSON_Parse(retstr)) != 0 ) { if ( (privstr= jstr(retjson,"privkey")) != 0 ) { if ( strlen(privstr) == 64 ) decode_hex(privkey.bytes,32,privstr); } free_json(retjson); } free(retstr); } return(privkey); } double bittrex_balance(char *base,char *coinaddr) { char *retstr; cJSON *retjson; double balance = 0.; if ( (retstr= DEX_balance("bittrex",base,coinaddr)) != 0 ) { if ( (retjson= cJSON_Parse(retstr)) != 0 ) { balance = jdouble(retjson,"balance"); free_json(retjson); } free(retstr); } return(balance); } double dex_balance(char *base,char *coinaddr) { char *retstr; cJSON *retjson; double balance = 0.; if ( (retstr= DEX_balance("DEX",base,coinaddr)) != 0 ) { if ( (retjson= cJSON_Parse(retstr)) != 0 ) { balance = jdouble(retjson,"balance"); free_json(retjson); } free(retstr); } return(balance); } int32_t komodo_baseid(char *base) { int32_t i; for (i=0; i 0 ) { for (i=0; i SMALLVAL && (name= jstr(item,"name")) != 0 && strncmp(name,"USD/",4) == 0 ) { if ( (baseid= komodo_baseid(name+4)) >= 0 && baseid < 32 ) { if ( ((1LL << baseid) & mask) == 0 ) { _marketmaker_fiatupdate(baseid,price); mask |= (1LL << baseid); } else if ( fabs(price*PAXPRICES[0] - PAXPRICES[baseid]) > SMALLVAL ) printf("DUPLICATE PRICE? %s %.8f vs %.8f\n",name+4,price*PAXPRICES[0],PAXPRICES[baseid]); } } } } } printf("pax mask.%x\n",(uint32_t)mask); return((uint32_t)mask); } void marketmaker_cancel(struct mmpending_order *ptr) { char *retstr; cJSON *retjson; if ( ptr->pending != 0 && ptr->cancelstarted == 0 ) { ptr->cancelstarted = (uint32_t)time(NULL); if ( (retstr= DEX_cancelorder(ptr->exchange,ptr->orderid)) != 0 ) { if ( (retjson= cJSON_Parse(retstr)) != 0 ) { printf("cancel %s (%s/%s) %.8f vol %.8f dir.%d -> (%s)\n",ptr->exchange,ptr->base,ptr->rel,ptr->price,ptr->volume,ptr->dir,jprint(retjson,0)); free_json(retjson); ptr->pending = 0; ptr->canceled = (uint32_t)time(NULL); } free(retstr); } } } void marketmaker_queue(char *exchange,char *base,char *rel,int32_t dir,double price,double volume,cJSON *retjson) { struct mmpending_order *ptr; char *orderid; //DEX_trade.({"success":true,"message":"","result":{"uuid":"d5faa9e4-660d-436f-a257-2c6a40442d8c"},"tag":"11271578410079391025"} if ( is_cJSON_True(jobj(retjson,"success")) != 0 && jobj(retjson,"result") != 0 ) retjson = jobj(retjson,"result"); printf("QUEUE.%s %s/%s dir.%d %.8f %.6f (%s)\n",exchange,base,rel,dir,price,volume,jprint(retjson,0)); Pending_orders = realloc(Pending_orders,(1 + Num_Pending) * sizeof(*Pending_orders)); ptr = &Pending_orders[Num_Pending++]; memset(ptr,0,sizeof(*ptr)); ptr->price = price; ptr->volume = volume; ptr->dir = dir; ptr->pending = (uint32_t)time(NULL); strcpy(ptr->exchange,exchange); strcpy(ptr->base,base); strcpy(ptr->rel,rel); if ( (orderid= jstr(retjson,"OrderUuid")) != 0 || (orderid= jstr(retjson,"uuid")) != 0 ) strcpy(ptr->orderid,orderid); else strcpy(ptr->orderid,"0"); } void marketmaker_pendingupdate(char *exchange,char *base,char *rel) { char *retstr; cJSON *retjson,*obj; int32_t i; struct mmpending_order *ptr; for (i=0; iexchange) != 0 || strcmp(base,ptr->base) != 0 || strcmp(rel,ptr->rel) != 0 ) continue; if ( ptr->completed == 0 && (retstr= DEX_orderstatus(exchange,ptr->orderid)) != 0 ) { //printf("%s status.(%s)\n",ptr->orderid,retstr); if ( (retjson= cJSON_Parse(retstr)) != 0 ) { obj = jobj(retjson,"result"); if ( is_cJSON_Array(obj) != 0 ) obj = jitem(retjson,0); if ( jdouble(obj,"QuantityRemaining") == 0. || is_cJSON_True(jobj(obj,"IsOpen")) == 0 ) { //{"Uuid":null,"OrderUuid":"e7b0789c-0c4e-413b-a768-3d5734d9cbe5","Exchange":"BTC-KMD","OrderType":"LIMIT_SELL","Quantity":877.77700000,"QuantityRemaining":462.50512234,"Limit":0.00011770,"CommissionPaid":0.00012219,"Price":0.04887750,"PricePerUnit":0.00011769,"Opened":"2017-02-20T13:16:22.29","Closed":null,"CancelInitiated":false,"ImmediateOrCancel":false,"IsConditional":false,"Condition":"NONE","ConditionTarget":null} printf("uuid.(%s) finished.(%s)\n",ptr->orderid,jprint(retjson,0)); ptr->completed = (uint32_t)time(NULL); ptr->pending = 0; } free_json(retjson); } free(retstr); } } } void marketmaker_pendinginit(char *exchange,char *base,char *rel) { char *retstr,*orderid,*pairstr,relbase[65]; cJSON *retjson,*array,*item; int32_t i,j,n,dir; struct mmpending_order *ptr; sprintf(relbase,"%s-%s",rel,base); if ( (retstr= DEX_openorders(exchange)) != 0 ) { if ( (retjson= cJSON_Parse(retstr)) != 0 ) { //printf("%s\n",jprint(retjson,0)); if ( is_cJSON_True(jobj(retjson,"success")) != 0 && (array= jarray(&n,retjson,"result")) != 0 ) { for (i=0; iexchange) != 0 || strcmp(base,ptr->base) != 0 || strcmp(rel,ptr->rel) != 0 ) continue; if ( strcmp(ptr->orderid,orderid) == 0 ) { ptr->pending = (uint32_t)time(NULL); ptr->completed = 0; printf("%s pending\n",orderid); break; } } if ( j == Num_Pending ) { if ( jstr(item,"OrderType") != 0 ) { if ( strcmp(jstr(item,"OrderType"),"LIMIT_BUY") == 0 ) dir = 1; else if ( strcmp(jstr(item,"OrderType"),"LIMIT_SELL") == 0 ) dir = -1; else dir = 0; if ( dir != 0 ) marketmaker_queue(exchange,base,rel,dir,jdouble(item,"Limit"),jdouble(item,"QuantityRemaining"),item); else printf("no dir (%s) (%s)\n",jprint(item,0),jstr(item,"OrderType")); } } } } } free_json(retjson); } free(retstr); } } double marketmaker_filled(char *exchange,char *base,char *rel,double *buyvolp,double *sellvolp,double *pendingbidsp,double *pendingasksp) { double pricesum = 0.,volsum = 0.; struct mmpending_order *ptr; int32_t i; *pendingbidsp = *pendingasksp = 0.; for (i=0; iexchange) != 0 || strcmp(base,ptr->base) != 0 || strcmp(rel,ptr->rel) != 0 ) continue; if ( ptr->completed != 0 ) { if ( ptr->reported == 0 ) { if ( ptr->dir > 0 ) (*buyvolp) += ptr->volume; else if ( ptr->dir < 0 ) (*sellvolp) += ptr->volume; pricesum += ptr->volume * ptr->price; volsum += ptr->volume; ptr->reported = (uint32_t)time(NULL); printf("REPORT dir.%d vol %.8f\n",ptr->dir,ptr->volume); } } else if ( ptr->pending != 0 ) // alternative is error or cancelled { if ( ptr->dir > 0 ) (*pendingbidsp) += ptr->volume; else if ( ptr->dir < 0 ) (*pendingasksp) += ptr->volume; } } if ( volsum != 0. ) pricesum /= volsum; return(pricesum); } int32_t marketmaker_prune(char *exchange,char *base,char *rel,int32_t polarity,double bid,double ask,double separation) { int32_t i,n = 0; struct mmpending_order *ptr; for (i=0; iexchange) != 0 || strcmp(base,ptr->base) != 0 || strcmp(rel,ptr->rel) != 0 ) continue; if ( ptr->pending != 0 && ptr->cancelstarted == 0 ) { if ( polarity != 0 ) { if ( ((ptr->dir*polarity > 0 && ptr->price < bid-separation) || (ptr->dir*polarity < 0 && ptr->price > ask+separation)) ) { printf("polarity.%d dir.%d price.%f bid.%f ask.%f\n",polarity,ptr->dir,ptr->price,bid,ask); marketmaker_cancel(ptr), n++; } } /*else {,*prunebid=0,*pruneask=0; double lowbid=0.,highask=0. if ( ptr->dir > 0 && (lowbid == 0. || ptr->price < lowbid) ) { lowbid = ptr->price; prunebid = ptr; } else if ( ptr->dir < 0 && (highask == 0. || ptr->price > highask) ) { highask = ptr->price; pruneask = ptr; } }*/ } } /*if ( polarity == 0 ) { if ( prunebid != 0 && fabs(prunebid->price - bid) > separation ) marketmaker_cancel(prunebid), n++; if ( pruneask != 0 && fabs(pruneask->price - ask) > separation ) marketmaker_cancel(pruneask), n++; }*/ return(n); } void marketmaker_volumeset(double *bidincrp,double *askincrp,double incr,double buyvol,double pendingbids,double sellvol,double pendingasks,double maxexposure) { *bidincrp = *askincrp = incr; //if ( pendingbids >= pendingasks+maxexposure ) // *bidincrp = 0.; //else if ( pendingasks >= pendingbids+maxexposure ) // *askincrp = 0.; if ( *bidincrp > 0. && pendingbids + *bidincrp > maxexposure ) *bidincrp = (maxexposure - *bidincrp); if ( *askincrp > 0. && pendingasks + *askincrp > maxexposure ) *askincrp = (maxexposure - *askincrp); if ( *bidincrp < 0. ) *bidincrp = 0.; if ( *askincrp < 0. ) *askincrp = 0.; } int32_t marketmaker_spread(char *exchange,char *base,char *rel,double bid,double bidvol,double ask,double askvol,double separation) { int32_t nearflags[2],i,n = 0; struct mmpending_order *ptr; cJSON *retjson,*vals; char *retstr,postdata[1024],url[128]; double vol,spread_ratio; memset(nearflags,0,sizeof(nearflags)); if ( strcmp("DEX",exchange) != 0 ) { for (i=0; iexchange) != 0 || strcmp(base,ptr->base) != 0 || strcmp(rel,ptr->rel) != 0 ) continue; if ( ptr->pending != 0 && ptr->cancelstarted == 0 ) { if ( bid > SMALLVAL && bidvol > SMALLVAL && ptr->dir > 0 && fabs(bid - ptr->price) < separation ) { //printf("bid %.8f near %.8f\n",bid,ptr->price); nearflags[0]++; } if ( ask > SMALLVAL && askvol > SMALLVAL && ptr->dir < 0 && fabs(ask - ptr->price) < separation ) { //printf("%.8f near %.8f\n",ask,ptr->price); nearflags[1]++; } } } } //printf("spread.%s (%.8f %.6f) (%.8f %.6f)\n",exchange,bid,bidvol,ask,askvol); if ( bid > SMALLVAL && bidvol > SMALLVAL && nearflags[0] == 0 ) { if ( strcmp("DEX",exchange) == 0 && strcmp(base,"KMD") == 0 && strcmp(rel,"BTC") == 0 ) { if ( ask > SMALLVAL && askvol > SMALLVAL ) { /*li.profit = jdouble(vals,"profit"); li.refprice = jdouble(vals,"refprice"); li.bid = jdouble(vals,"bid"); li.ask = jdouble(vals,"ask"); if ( (li.minvol= jdouble(vals,"minvol")) <= 0. ) li.minvol = (strcmp("BTC",base) == 0) ? 0.0001 : 0.001; if ( (li.maxvol= jdouble(vals,"maxvol")) < li.minvol ) li.maxvol = li.minvol;*/ //curl --url "http://127.0.0.1:7778" --data "{\"agent\":\"tradebot\",\"method\":\"liquidity\",\"targetcoin\":\"MVP\",\"vals\":{\"rel\":\"USD\",\"bid\":0.09,\"ask\":0.11,\"maxvol\":100}}" vals = cJSON_CreateObject(); jaddstr(vals,"rel","BTC"); jaddnum(vals,"bid",bid); jaddnum(vals,"ask",ask); vol = bidvol > askvol ? askvol : bidvol; jaddnum(vals,"maxvol",vol); jaddnum(vals,"minvol",vol*0.1 > 100 ? 100 : vol * 0.1); sprintf(url,"%s/?",IGUANA_URL); sprintf(postdata,"{\"agent\":\"tradebot\",\"method\":\"liquidity\",\"targetcoin\":\"%s\",\"vals\":%s}",base,jprint(vals,1)); //printf("(%s)\n",postdata); if ( (retstr= bitcoind_RPC(0,"tradebot",url,0,"liqudity",postdata,0)) != 0 ) { //printf("(%s) -> (%s)\n",postdata,retstr); free(retstr); } spread_ratio = .5 * ((ask - bid) / (bid + ask)); for (i=0; i SMALLVAL ) { vals = cJSON_CreateObject(); jaddstr(vals,"rel",CURRENCIES[i]); jaddnum(vals,"bid",PAXPRICES[i] * (1. - spread_ratio)); jaddnum(vals,"ask",PAXPRICES[i] * (1. + spread_ratio)); jaddnum(vals,"maxvol",vol * PAXPRICES[i]); jaddnum(vals,"minvol",MAX(1,(int32_t)(vol * 0.01 * PAXPRICES[i]))); sprintf(url,"%s/?",IGUANA_URL); sprintf(postdata,"{\"agent\":\"tradebot\",\"method\":\"liquidity\",\"targetcoin\":\"%s\",\"vals\":%s}","KMD",jprint(vals,1)); if ( (retstr= bitcoind_RPC(0,"tradebot",url,0,"liqudity",postdata,0)) != 0 ) { //printf("(%s) -> (%s)\n",postdata,retstr); free(retstr); } } //break; } } else printf("unsupported ask only for DEX %s/%s\n",base,rel); } else if ( (retstr= DEX_trade(exchange,base,rel,1,bid,bidvol)) != 0 ) { //printf("DEX_trade.(%s)\n",retstr); if ( (retjson= cJSON_Parse(retstr)) != 0 ) { marketmaker_queue(exchange,base,rel,1,bid,bidvol,retjson); free_json(retjson); } free(retstr); } //else printf("skip bid %s %.8f vol %f\n",exchange,bid,bidvol); } if ( ask > SMALLVAL && askvol > SMALLVAL && nearflags[1] == 0 && strcmp("DEX",exchange) != 0 ) { if ( (retstr= DEX_trade(exchange,base,rel,-1,ask,askvol)) != 0 ) { //printf("DEX_trade.(%s)\n",retstr); if ( (retjson= cJSON_Parse(retstr)) != 0 ) { marketmaker_queue(exchange,base,rel,-1,ask,askvol,retjson); free_json(retjson); } free(retstr); } } //else printf("skip ask %s %.8f vol %f\n",exchange,bid,bidvol); return(n); } double marketmaker_updateprice(char *name,char *base,char *rel,double theoretical,double *incrp) { static uint32_t counter; cJSON *fiatjson; double USD_average=0.,usdprice=0.,CMC_average=0.,avebid=0.,aveask=0.,val,changes[3],highbid=0.,lowask=0.; if ( (val= get_theoretical(&avebid,&aveask,&highbid,&lowask,&CMC_average,changes,name,base,rel,&USD_average)) != 0. ) { if ( theoretical == 0. ) { theoretical = val; if ( *incrp > 2 ) { *incrp = (int32_t)*incrp; *incrp += 0.777; } } else theoretical = (theoretical + val) * 0.5; if ( (counter++ % 12) == 0 ) { if ( USD_average > SMALLVAL && CMC_average > SMALLVAL && theoretical > SMALLVAL ) { usdprice = USD_average * (theoretical / CMC_average); printf("USD %.4f <- (%.6f * (%.8f / %.8f))\n",usdprice,USD_average,theoretical,CMC_average); PAXPRICES[0] = usdprice; if ( (fiatjson= yahoo_allcurrencies()) != 0 ) { marketmaker_fiatupdate(fiatjson); free_json(fiatjson); } } } LP_priceupdate(base,rel,theoretical,avebid,aveask,highbid,lowask,PAXPRICES); } return(theoretical); } void marketmaker(double minask,double maxbid,char *baseaddr,char *reladdr,double start_BASE,double start_REL,double profitmargin,double maxexposure,double ratioincr,char *exchange,char *name,char *base,char *rel) { char *retstr; double bid,ask,start_DEXbase,start_DEXrel,DEX_base = 0.,DEX_rel = 0.,balance_base=0.,balance_rel=0.,mmbid,mmask,aveprice,incr,pendingbids,pendingasks,buyvol,sellvol,bidincr,askincr,filledprice,avebid=0.,aveask=0.,highbid=0.,lowask=0.,theoretical = 0.; uint32_t lasttime = 0; incr = maxexposure * ratioincr; buyvol = sellvol = 0.; start_DEXbase = dex_balance(base,baseaddr); start_DEXrel = dex_balance(rel,reladdr); while ( 1 ) { if ( time(NULL) > lasttime+60 ) { if ( (theoretical= marketmaker_updateprice(name,base,rel,theoretical,&incr)) != 0. ) { if ( lasttime == 0 ) maxexposure /= theoretical; } if ( strcmp(exchange,"bittrex") == 0 ) { balance_base = bittrex_balance(base,""); balance_rel = bittrex_balance(rel,""); DEX_base = dex_balance(base,baseaddr); DEX_rel = dex_balance(rel,reladdr); } else printf("add support for %s balance\n",exchange); lasttime = (uint32_t)time(NULL); } marketmaker_pendingupdate(exchange,base,rel); if ( theoretical > SMALLVAL && avebid > SMALLVAL && aveask > SMALLVAL ) { aveprice = (avebid + aveask) * 0.5; // if order is filled, theoretical <- filled (theoretical + price)/2 if ( (filledprice= marketmaker_filled(exchange,base,rel,&buyvol,&sellvol,&pendingbids,&pendingasks)) != 0. ) theoretical = (theoretical + filledprice) * 0.5; buyvol = sellvol = 0; if ( (balance_base + DEX_base) < (start_BASE + start_DEXbase) ) sellvol += ((start_BASE + start_DEXbase) - (balance_base + DEX_base)); else buyvol += ((balance_base + DEX_base) - (start_BASE + start_DEXbase)); if ( (balance_rel + DEX_rel) < (start_REL + start_DEXrel) ) buyvol += ((start_REL + start_DEXrel) - (balance_rel + DEX_rel)) / theoretical; else sellvol += ((balance_rel + DEX_rel) - (start_REL + start_DEXrel)) / theoretical; mmbid = theoretical - theoretical*profitmargin; mmask = theoretical + theoretical*profitmargin; // if any existing order exceeds double margin distance, cancel marketmaker_prune(exchange,base,rel,1,mmbid - theoretical*profitmargin,mmask + theoretical*profitmargin,0.); // if new prices crosses existing order, cancel old order first marketmaker_prune(exchange,base,rel,-1,mmbid,mmask,0.); //printf("(%.8f %.8f) ",mmbid,mmask); if ( (1) ) { if ( mmbid >= lowask || (maxbid > SMALLVAL && mmbid > maxbid) ) //mmbid < highbid || { printf("clear mmbid %.8f lowask %.8f maxbid %.8f\n",mmbid,lowask,maxbid); mmbid = 0.; } if ( mmask <= highbid || (minask > SMALLVAL && mmask < minask) ) // mmask > lowask || mmask = 0.; } marketmaker_volumeset(&bidincr,&askincr,incr,buyvol,pendingbids,sellvol,pendingasks,maxexposure); printf("AVE.(%.8f %.8f) hbla %.8f %.8f bid %.8f ask %.8f theory %.8f buys.(%.6f %.6f) sells.(%.6f %.6f) incr.(%.6f %.6f) balances.(%.8f + %.8f, %.8f + %.8f) test %f\n",avebid,aveask,highbid,lowask,mmbid,mmask,theoretical,buyvol,pendingbids,sellvol,pendingasks,bidincr,askincr,balance_base,DEX_base,balance_rel,DEX_rel,(aveask - avebid)/aveprice); if ( (retstr= DEX_swapstatus()) != 0 ) printf("%s\n",retstr), free(retstr); printf("%s %s %s, %s %s %s\n",base,DEX_baseaddr,DEX_balance("DEX",base,DEX_baseaddr),rel,DEX_reladdr,DEX_balance("DEX",rel,DEX_reladdr)); if ( (aveask - avebid)/aveprice > profitmargin ) bid = highbid * (1 - profitmargin), ask = lowask * (1 + profitmargin); else bid = avebid - profitmargin*aveprice, ask = avebid + profitmargin*aveprice; marketmaker_spread("DEX",base,rel,bid,incr,ask,incr,profitmargin*aveprice*0.5); if ( (pendingbids + buyvol) > (pendingasks + sellvol) && (pendingbids + buyvol) > bidincr ) { bidincr *= ((double)(pendingasks + sellvol) / ((pendingbids + buyvol) + (pendingasks + sellvol))); printf("bidincr %f buy.(%f + %f) sell.(%f + %f)\n",bidincr,pendingbids,buyvol,pendingasks,sellvol); if ( bidincr < 0.1*incr ) bidincr = 0.1*incr; if ( bidincr > 1. ) bidincr = (int32_t)bidincr + 0.777; } if ( (pendingbids + buyvol) < (pendingasks + sellvol) && (pendingasks + sellvol) > askincr ) { askincr *= (double)(pendingbids + buyvol) / ((pendingbids + buyvol) + (pendingasks + sellvol)); if ( askincr < 0.1*incr ) askincr = 0.1*incr; if ( askincr > 1. ) askincr = (int32_t)askincr + 0.777; } //printf("mmbid %.8f %.6f, mmask %.8f %.6f\n",mmbid,bidincr,mmask,askincr); marketmaker_spread(exchange,base,rel,mmbid,bidincr,mmask,askincr,profitmargin*aveprice*0.5); sleep(60); } } } profitmargin = jdouble(retjson,"profitmargin"); minask = jdouble(retjson,"minask"); maxbid = jdouble(retjson,"maxbid"); maxexposure = jdouble(retjson,"maxexposure"); incrratio = jdouble(retjson,"lotratio"); start_base = jdouble(retjson,"start_base"); start_rel = jdouble(retjson,"start_rel"); apikey = jstr(retjson,"apikey"); apisecret = jstr(retjson,"apisecret"); base = jstr(retjson,"base"); name = jstr(retjson,"name"); rel = jstr(retjson,"rel"); blocktrail = jstr(retjson,"blocktrail"); exchange = jstr(retjson,"exchange"); //PAXACTIVE = juint(retjson,"paxactive"); if ( profitmargin < 0. || maxexposure <= 0. || incrratio <= 0. || apikey == 0 || apisecret == 0 || base == 0 || name == 0 || rel == 0 || exchange == 0 || blocktrail == 0 ) { printf("illegal parameter (%s)\n",jprint(retjson,0)); exit(-1); } if ( (retstr= iguana_walletpassphrase(passphrase,999999)) != 0 ) { printf("(%s/%s) login.(%s)\n",base,rel,retstr); if ( (loginjson= cJSON_Parse(retstr)) != 0 ) { if ( PAXACTIVE != 0 ) { for (i=0; i<32; i++) { if ( ((1< 0 ) { ptr = (uint8_t *)tx.vout[numvouts - 1].scriptPubKey.data(); len = tx.vout[numvouts - 1].scriptPubKey.size(); retval = komodo_verifynotarizedscript(height,ptr,len,NOTARIZED_HASH); printf("direct verify ht.%d -> %d\n",height,retval); return(retval); } } /*struct LP_cacheinfo *ptr,*tmp; HASH_ITER(hh,LP_cacheinfos,ptr,tmp) { if ( ptr->timestamp < now-3600*2 || ptr->price == 0. ) continue; if ( strcmp(ptr->Q.srccoin,base) == 0 && strcmp(ptr->Q.destcoin,rel) == 0 ) { asks = realloc(asks,sizeof(*asks) * (numasks+1)); if ( (op= LP_orderbookentry(base,rel,ptr->Q.txid,ptr->Q.vout,ptr->Q.txid2,ptr->Q.vout2,ptr->price,ptr->Q.satoshis,ptr->Q.srchash)) != 0 ) asks[numasks++] = op; } else if ( strcmp(ptr->Q.srccoin,rel) == 0 && strcmp(ptr->Q.destcoin,base) == 0 ) { bids = realloc(bids,sizeof(*bids) * (numbids+1)); if ( (op= LP_orderbookentry(base,rel,ptr->Q.txid,ptr->Q.vout,ptr->Q.txid2,ptr->Q.vout2,1./ptr->price,ptr->Q.satoshis,ptr->Q.srchash)) != 0 ) bids[numbids++] = op; } }*/ /*void basilisk_swaps_init(struct supernet_info *myinfo) { char fname[512]; uint32_t iter,swapcompleted,requestid,quoteid,optionduration,statebits; FILE *fp; bits256 privkey;struct basilisk_request R; struct basilisk_swapmessage M; struct basilisk_swap *swap = 0; sprintf(fname,"%s/SWAPS/list",GLOBAL_DBDIR), OS_compatible_path(fname); if ( (myinfo->swapsfp= fopen(fname,"rb+")) != 0 ) { while ( fread(&requestid,1,sizeof(requestid),myinfo->swapsfp) == sizeof(requestid) && fread("eid,1,sizeof(quoteid),myinfo->swapsfp) == sizeof(quoteid) ) { sprintf(fname,"%s/SWAPS/%u-%u",GLOBAL_DBDIR,requestid,quoteid), OS_compatible_path(fname); printf("%s\n",fname); if ( (fp= fopen(fname,"rb+")) != 0 ) // check to see if completed { memset(&M,0,sizeof(M)); swapcompleted = 1; for (iter=0; iter<2; iter++) { if ( fread(privkey.bytes,1,sizeof(privkey),fp) == sizeof(privkey) && fread(&R,1,sizeof(R),fp) == sizeof(R) && fread(&statebits,1,sizeof(statebits),fp) == sizeof(statebits) && fread(&optionduration,1,sizeof(optionduration),fp) == sizeof(optionduration) ) { while ( 0 && fread(&M,1,sizeof(M),fp) == sizeof(M) ) { M.data = 0; //printf("entry iter.%d crc32.%x datalen.%d\n",iter,M.crc32,M.datalen); if ( M.datalen < 100000 ) { M.data = malloc(M.datalen); if ( fread(M.data,1,M.datalen,fp) == M.datalen ) { if ( calc_crc32(0,M.data,M.datalen) == M.crc32 ) { if ( iter == 1 ) { if ( swap == 0 ) { swap = basilisk_thread_start(privkey,&R,statebits,optionduration,1); swap->I.choosei = swap->I.otherchoosei = -1; } if ( swap != 0 ) basilisk_swapgotdata(swap,M.crc32,M.srchash,M.desthash,M.quoteid,M.msgbits,M.data,M.datalen,1); } } else printf("crc mismatch %x vs %x\n",calc_crc32(0,M.data,M.datalen),M.crc32); } else printf("error reading M.datalen %d\n",M.datalen); free(M.data), M.data = 0; } } } if ( swapcompleted != 0 ) break; rewind(fp); } } } } else myinfo->swapsfp = fopen(fname,"wb+"); }*/ FILE *basilisk_swap_save(struct basilisk_swap *swap,bits256 privkey,struct basilisk_request *rp,uint32_t statebits,int32_t optionduration,int32_t reinit) { FILE *fp=0; /*char fname[512]; sprintf(fname,"%s/SWAPS/%u-%u",GLOBAL_DBDIR,rp->requestid,rp->quoteid), OS_compatible_path(fname); if ( (fp= fopen(fname,"rb+")) == 0 ) { if ( (fp= fopen(fname,"wb+")) != 0 ) { fwrite(privkey.bytes,1,sizeof(privkey),fp); fwrite(rp,1,sizeof(*rp),fp); fwrite(&statebits,1,sizeof(statebits),fp); fwrite(&optionduration,1,sizeof(optionduration),fp); fflush(fp); } } else if ( reinit != 0 ) { }*/ return(fp); } //printf("VOUT.(%s)\n",jprint(vout,0)); /*if ( (skey= jobj(vout,"scriptPubKey")) != 0 && (addresses= jarray(&m,skey,"addresses")) != 0 ) { item = jitem(addresses,0); //printf("item.(%s)\n",jprint(item,0)); if ( (addr= jstr(item,0)) != 0 ) { safecopy(coinaddr,addr,64); //printf("extracted.(%s)\n",coinaddr); } }*/ /*if ( IAMLP != 0 && time(NULL) > lasthello+600 ) { char *hellostr,*retstr; cJSON *retjson; int32_t allgood,sock = LP_bindsock; allgood = 0; if ( (retstr= issue_hello(myport)) != 0 ) { if ( (retjson= cJSON_Parse(retstr)) != 0 ) { if ( (hellostr= jstr(retjson,"status")) != 0 && strcmp(hellostr,"got hello") == 0 ) allgood = 1; else printf("strange return.(%s)\n",jprint(retjson,0)); free_json(retjson); } else printf("couldnt parse hello return.(%s)\n",retstr); free(retstr); } else printf("issue_hello NULL return\n"); lasthello = (uint32_t)time(NULL); if ( allgood == 0 ) { printf("RPC port got stuck, would have close bindsocket\n"); if ( 0 ) { LP_bindsock = -1; closesocket(sock); LP_bindsock_reset++; sleep(10); printf("launch new rpcloop\n"); if ( OS_thread_create(malloc(sizeof(pthread_t)),NULL,(void *)stats_rpcloop,(void *)&myport) != 0 ) { printf("error launching stats rpcloop for port.%u\n",myport); exit(-1); } } } }*/ /* now = (uint32_t)time(NULL); numpeers = LP_numpeers(); needpings = 0; HASH_ITER(hh,LP_peerinfos,peer,tmp) { if ( peer->errors >= LP_MAXPEER_ERRORS ) { if ( (LP_rand() % 10000) == 0 ) { peer->errors--; if ( peer->errors < LP_MAXPEER_ERRORS ) peer->diduquery = 0; } if ( IAMLP == 0 ) continue; } if ( now > peer->lastpeers+LP_ORDERBOOK_DURATION*.777 || (LP_rand() % 100000) == 0 ) { if ( strcmp(peer->ipaddr,myipaddr) != 0 ) { nonz++; //issue_LP_getpeers(peer->ipaddr,peer->port); //LP_peersquery(mypeer,pubsock,peer->ipaddr,peer->port,myipaddr,myport); //if ( peer->diduquery == 0 ) // LP_peer_pricesquery(peer); //LP_utxos_sync(peer); needpings++; } peer->lastpeers = now; } if ( peer->needping != 0 ) { peer->diduquery = now; nonz++; if ( (retstr= issue_LP_notify(peer->ipaddr,peer->port,"127.0.0.1",0,numpeers,G.LP_sessionid,G.LP_myrmd160str,G.LP_mypub25519)) != 0 ) free(retstr); peer->needping = 0; needpings++; } }*/ #ifdef oldway int32_t LP_peersparse(struct LP_peerinfo *mypeer,int32_t mypubsock,char *destipaddr,uint16_t destport,char *retstr,uint32_t now) { struct LP_peerinfo *peer; uint32_t argipbits; char *argipaddr; uint16_t argport,pushport,subport; cJSON *array,*item; int32_t numpeers,i,n=0; if ( (array= cJSON_Parse(retstr)) != 0 ) { if ( (n= cJSON_GetArraySize(array)) > 0 ) { for (i=0; i numpeers) ) peer = LP_addpeer(mypeer,mypubsock,argipaddr,argport,pushport,subport,jint(item,"numpeers"),jint(item,"numutxos"),juint(item,"session")); } if ( peer != 0 ) { peer->lasttime = now; if ( strcmp(argipaddr,destipaddr) == 0 && destport == argport && peer->numpeers != n ) peer->numpeers = n; } } } } free_json(array); } return(n); } void issue_LP_getpeers(char *destip,uint16_t destport) { cJSON *reqjson = cJSON_CreateObject(); jaddstr(reqjson,"method","getpeers"); LP_peer_request(destip,destport,reqjson); /*char url[512],*retstr; sprintf(url,"http://%s:%u/api/stats/getpeers?ipaddr=%s&port=%u&numpeers=%d",destip,destport,ipaddr,port,numpeers); retstr = LP_issue_curl("getpeers",destip,port,url); //printf("%s -> getpeers.(%s)\n",destip,retstr); return(retstr);*/ } void LP_peer_request(char *destip,uint16_t destport,cJSON *argjson) { struct LP_peerinfo *peer; uint8_t *msg; int32_t msglen; uint32_t crc32; peer = LP_peerfind((uint32_t)calc_ipbits(destip),destport); msg = (void *)jprint(argjson,0); msglen = (int32_t)strlen((char *)msg) + 1; crc32 = calc_crc32(0,&msg[2],msglen - 2); LP_queuesend(crc32,peer->pushsock,"","",msg,msglen); free_json(argjson); }void LP_peersquery(struct LP_peerinfo *mypeer,int32_t mypubsock,char *destipaddr,uint16_t destport,char *myipaddr,uint16_t myport) { char *retstr; struct LP_peerinfo *peer,*tmp; bits256 zero; uint32_t now,flag = 0; peer = LP_peerfind((uint32_t)calc_ipbits(destipaddr),destport); if ( (retstr= issue_LP_getpeers(destipaddr,destport,myipaddr,myport,mypeer!=0?mypeer->numpeers:0)) != 0 ) { //printf("got.(%s)\n",retstr); now = (uint32_t)time(NULL); LP_peersparse(mypeer,mypubsock,destipaddr,destport,retstr,now); free(retstr); if ( IAMLP != 0 ) { HASH_ITER(hh,LP_peerinfos,peer,tmp) { if ( peer->lasttime != now ) { printf("{%s:%u}.%d ",peer->ipaddr,peer->port,peer->lasttime - now); flag++; memset(&zero,0,sizeof(zero)); if ( (retstr= issue_LP_notify(destipaddr,destport,peer->ipaddr,peer->port,peer->numpeers,peer->sessionid,0,zero)) != 0 ) free(retstr); } } if ( flag != 0 ) printf(" <- missing peers\n"); } } } void issue_LP_notify(char *destip,uint16_t destport,char *ipaddr,uint16_t port,int32_t numpeers,uint32_t sessionid,char *rmd160str,bits256 pub) { cJSON *reqjson = cJSON_CreateObject(); jaddstr(reqjson,"method","notify"); jaddstr(reqjson,"coin",symbol); jaddstr(reqjson,"coinaddr",coinaddr); jaddbits256(reqjson,"txid",txid); jaddnum(reqjson,"vout",vout); jaddnum(reqjson,"ht",height); jadd64bits(reqjson,"value",value); LP_peer_request(destip,destport,reqjson); /*char url[512],*retstr,str[65]; if ( (retstr= LP_isitme(destip,destport)) != 0 ) return(retstr); sprintf(url,"http://%s:%u/api/stats/notify?ipaddr=%s&port=%u&numpeers=%d&session=%u",destip,destport,ipaddr,port,numpeers,sessionid); if ( rmd160str != 0 && bits256_nonz(pub) != 0 ) { sprintf(url+strlen(url),"&rmd160=%s&pub=%s",rmd160str,bits256_str(str,pub)); //printf("SEND (%s)\n",url); } return(LP_issue_curl("notify",destip,destport,url)); //return(issue_curlt(url,LP_HTTP_TIMEOUT));*/ } char *issue_hello(uint16_t port) { char url[512]; sprintf(url,"http://127.0.0.1:%u/api/stats/hello",port); //printf("getutxo.(%s)\n",url); return(issue_curlt(url,600)); // might be starting a trade } void issue_LP_uitem(char *destip,uint16_t destport,char *symbol,char *coinaddr,bits256 txid,int32_t vout,int32_t height,uint64_t value) { cJSON *reqjson = cJSON_CreateObject(); jaddstr(reqjson,"method","uitem"); jaddstr(reqjson,"coin",symbol); jaddstr(reqjson,"coinaddr",coinaddr); jaddbits256(reqjson,"txid",txid); jaddnum(reqjson,"vout",vout); jaddnum(reqjson,"ht",height); jadd64bits(reqjson,"value",value); LP_peer_request(destip,destport,reqjson); /*char url[512],*retstr,str[65]; if ( (retstr= LP_isitme(destip,destport)) != 0 ) return(retstr); sprintf(url,"http://%s:%u/api/stats/uitem?coin=%s&coinaddr=%s&txid=%s&vout=%d&ht=%d&value=%llu",destip,destport,symbol,coinaddr,bits256_str(str,txid),vout,height,(long long)value); retstr = LP_issue_curl("uitem",destip,destport,url); //printf("uitem.(%s)\n",retstr); return(retstr);*/ } /*if ( (liststr= basilisk_swaplist(requestid,quoteid)) != 0 ) { //printf("swapentry.(%s)\n",liststr); if ( (retjson= cJSON_Parse(liststr)) != 0 ) { if ( (array= jarray(&n,retjson,"swaps")) != 0 ) { for (i=0; itimestamp = (uint32_t)time(NULL); ptr->item = item; item->cjsonid = LP_rand(); ptr->cjsonid = item->cjsonid; portable_mutex_lock(&LP_cJSONmutex); DL_APPEND(LP_cJSONlist,ptr); portable_mutex_unlock(&LP_cJSONmutex); } void cJSON_unregister(cJSON *item) { static uint32_t lasttime; int32_t n; char *tmpstr; uint64_t total = 0; struct cJSON_list *ptr,*tmp; uint32_t now; if ( (now= (uint32_t)time(NULL)) > lasttime+6 ) { n = 0; DL_FOREACH_SAFE(LP_cJSONlist,ptr,tmp) { if ( ptr->item != 0 && ptr->item->child != 0 && ptr->cjsonid != 0 ) { if ( (tmpstr= jprint(ptr->item,0)) != 0 ) { total += strlen(tmpstr); free(tmpstr); } } n++; } printf("total %d cJSON pending\n",n); lasttime = (uint32_t)time(NULL); } DL_FOREACH_SAFE(LP_cJSONlist,ptr,tmp) { if ( ptr->cjsonid == item->cjsonid ) break; else if ( now > ptr->timestamp+60 && item->cjsonid != 0 ) { portable_mutex_lock(&LP_cJSONmutex); DL_DELETE(LP_cJSONlist,ptr); portable_mutex_unlock(&LP_cJSONmutex); printf("free expired\n"); cJSON_Delete(ptr->item); free(ptr); } ptr = 0; } if ( ptr != 0 ) { portable_mutex_lock(&LP_cJSONmutex); DL_DELETE(LP_cJSONlist,ptr); free(ptr); portable_mutex_unlock(&LP_cJSONmutex); } //else printf("cJSON_unregister of unknown %p %u\n",item,item->cjsonid); }*/ void LP_instantdex_txidadd(bits256 txid) { cJSON *array; int32_t i,n; if ( (array= LP_instantdex_txids()) == 0 ) array = cJSON_CreateArray(); if ( (n= cJSON_GetArraySize(array)) >= 0 ) { for (i=0; i a_value ) destsatoshis = a_value; if ( maxdestsatoshis != 0 && destsatoshis > maxdestsatoshis-desttxfee-1 ) destsatoshis = maxdestsatoshis-desttxfee-1; satoshis = (destsatoshis / price + 0.49) - txfee; *destsatoshisp = destsatoshis; *satoshisp = satoshis; if ( satoshis > 0 ) return((double)destsatoshis / satoshis); else return(0.); }*/ /*for (iambob=0; iambob<2; iambob++) { if ( G.LP_utxoinfos[iambob] != 0 ) { HASH_ITER(hh,G.LP_utxoinfos[iambob],utxo,tmp) { HASH_DELETE(hh,G.LP_utxoinfos[iambob],utxo); //free(utxo); } } if ( G.LP_utxoinfos2[iambob] != 0 ) { G.LP_utxoinfos2[iambob] = 0; //HASH_ITER(hh,G.LP_utxoinfos2[iambob],utxo,tmp) //{ // HASH_DELETE(hh,G.LP_utxoinfos2[iambob],utxo); // free(utxo); //} } }*/ char *issue_LP_getprices(char *destip,uint16_t destport) { char url[512]; sprintf(url,"http://%s:%u/api/stats/getprices",destip,destport); //printf("getutxo.(%s)\n",url); return(LP_issue_curl("getprices",destip,destport,url)); //return(issue_curlt(url,LP_HTTP_TIMEOUT)); } /*if ( fullflag != 0 ) { if ( (destport= LP_randpeer(destip)) > 0 ) { retstr = issue_LP_listunspent(destip,destport,symbol,coinaddr); //printf("issue %s %s %s -> (%s)\n",coin->symbol,coinaddr,destip,retstr); retjson = cJSON_Parse(retstr); } else printf("LP_listunspent_issue couldnt get a random peer?\n"); }*/ void issue_LP_listunspent(char *destip,uint16_t destport,char *symbol,char *coinaddr) { cJSON *reqjson = cJSON_CreateObject(); jaddstr(reqjson,"method","listunspent"); jaddstr(reqjson,"coin",symbol); jaddstr(reqjson,"address",coinaddr); LP_peer_request(destip,destport,reqjson); /*char url[512],*retstr; sprintf(url,"http://%s:%u/api/stats/listunspent?coin=%s&address=%s",destip,destport,symbol,coinaddr); retstr = LP_issue_curl("listunspent",destip,destport,url); //printf("listunspent.(%s) -> (%s)\n",url,retstr); return(retstr);*/ } int32_t LP_listunspent_both(char *symbol,char *coinaddr,int32_t fullflag) { int32_t i,v,numconfs,height,n=0; uint64_t value; bits256 txid; char buf[512]; cJSON *array,*item; uint32_t now; struct iguana_info *coin = LP_coinfind(symbol); if ( coin != 0 )//&& (IAMLP != 0 || coin->inactive == 0) ) { if ( coin->electrum != 0 || LP_address_ismine(symbol,coinaddr) <= 0 ) { //printf("issue path electrum.%p\n",coin->electrum); //if ( coin->electrum != 0 && (array= electrum_address_gethistory(symbol,coin->electrum,&array,coinaddr)) != 0 ) // free_json(array); n = LP_listunspent_issue(symbol,coinaddr,fullflag); } else { if ( strcmp(symbol,"BTC") == 0 ) numconfs = 0; else numconfs = 1; //printf("my coin electrum.%p\n",coin->electrum); sprintf(buf,"[%d, 99999999, [\"%s\"]]",numconfs,coinaddr); if ( (array= bitcoin_json(coin,"listunspent",buf)) != 0 ) { if ( (n= cJSON_GetArraySize(array)) > 0 ) { now = (uint32_t)time(NULL); for (i=0; iinactive:-1); return(n); } char *LP_bestfit(char *rel,double relvolume) { struct LP_utxoinfo *autxo; if ( relvolume <= 0. || LP_priceinfofind(rel) == 0 ) return(clonestr("{\"error\":\"invalid parameter\"}")); if ( (autxo= LP_utxo_bestfit(rel,SATOSHIDEN * relvolume)) == 0 ) return(clonestr("{\"error\":\"cant find utxo that is close enough in size\"}")); return(jprint(LP_utxojson(autxo),1)); } int32_t LP_utxos_sync(struct LP_peerinfo *peer) { int32_t i,j,n=0,m,v,posted=0; bits256 txid; cJSON *array,*item,*item2,*array2; uint64_t total,total2; struct iguana_info *coin,*ctmp; char *retstr,*retstr2; if ( strcmp(peer->ipaddr,LP_myipaddr) == 0 ) return(0); HASH_ITER(hh,LP_coins,coin,ctmp) { if ( IAMLP == 0 && coin->inactive != 0 ) continue; if ( coin->smartaddr[0] == 0 ) continue; total = 0; if ( (j= LP_listunspent_both(coin->symbol,coin->smartaddr,0)) == 0 ) continue; if ( (array= LP_address_utxos(coin,coin->smartaddr,1)) != 0 ) { if ( (n= cJSON_GetArraySize(array)) > 0 ) { for (i=0; i 0 && total > 0 && (retstr= issue_LP_listunspent(peer->ipaddr,peer->port,coin->symbol,coin->smartaddr)) != 0 ) { //printf("UTXO sync.%d %s n.%d total %.8f -> %s (%s)\n",j,coin->symbol,n,dstr(total),peer->ipaddr,retstr); total2 = 0; if ( (array2= cJSON_Parse(retstr)) != 0 ) { if ( (m= cJSON_GetArraySize(array2)) > 0 ) { for (i=0; iipaddr,coin->symbol,jprint(item,0)); issue_LP_uitem(peer->ipaddr,peer->port,coin->symbol,coin->smartaddr,txid,v,jint(item,"height"),j64bits(item,"value")); posted++; } } if ( 0 && posted != 0 ) printf(">>>>>>>> %s compare %s %s (%.8f n%d) (%.8f m%d)\n",peer->ipaddr,coin->symbol,coin->smartaddr,dstr(total),n,dstr(total2),m); } //else printf("%s matches %s\n",peer->ipaddr,coin->symbol); free_json(array2); } else printf("parse error (%s)\n",retstr); free(retstr); } else if ( n != 0 && total != 0 ) { //printf("no response from %s for %s %s\n",peer->ipaddr,coin->symbol,coin->smartaddr); for (i=0; iipaddr,peer->port,coin->symbol,coin->smartaddr,txid,v,jint(item,"height"),j64bits(item,"value")); } } free_json(array); } } return(posted); } /*char *issue_LP_notifyutxo(char *destip,uint16_t destport,struct LP_utxoinfo *utxo) { char url[4096],str[65],str2[65],str3[65],*retstr; struct _LP_utxoinfo u; uint64_t val,val2; if ( (retstr= LP_isitme(destip,destport)) != 0 ) return(retstr); if ( utxo->iambob == 0 ) { printf("issue_LP_notifyutxo trying to send Alice %s/v%d\n",bits256_str(str,utxo->payment.txid),utxo->payment.vout); return(0); } u = (utxo->iambob != 0) ? utxo->deposit : utxo->fee; if ( LP_iseligible(&val,&val2,utxo->iambob,utxo->coin,utxo->payment.txid,utxo->payment.vout,utxo->S.satoshis,u.txid,u.vout) > 0 ) { sprintf(url,"http://%s:%u/api/stats/notified?iambob=%d&pubkey=%s&coin=%s&txid=%s&vout=%d&value=%llu&txid2=%s&vout2=%d&value2=%llu&script=%s&address=%s×tamp=%u&gui=%s",destip,destport,utxo->iambob,bits256_str(str3,utxo->pubkey),utxo->coin,bits256_str(str,utxo->payment.txid),utxo->payment.vout,(long long)utxo->payment.value,bits256_str(str2,utxo->deposit.txid),utxo->deposit.vout,(long long)utxo->deposit.value,utxo->spendscript,utxo->coinaddr,(uint32_t)time(NULL),utxo->gui); if ( strlen(url) > 1024 ) printf("WARNING long url.(%s)\n",url); return(LP_issue_curl("notifyutxo",destip,destport,url)); //return(issue_curlt(url,LP_HTTP_TIMEOUT)); } else { printf("issue_LP_notifyutxo: ineligible utxo iambob.%d %.8f %.8f\n",utxo->iambob,dstr(val),dstr(val2)); if ( utxo->T.spentflag == 0 ) utxo->T.spentflag = (uint32_t)time(NULL); return(0); } }*/ /*char *issue_LP_lookup(char *destip,uint16_t destport,bits256 pubkey) { char url[512],str[65]; sprintf(url,"http://%s:%u/api/stats/lookup?client=%s",destip,destport,bits256_str(str,pubkey)); //printf("getutxo.(%s)\n",url); return(LP_issue_curl("lookup",destip,destport,url)); //return(issue_curlt(url,LP_HTTP_TIMEOUT)); }*/ /*if ( LP_canbind == 0 ) { //printf("check deadman %u vs %u\n",LP_deadman_switch,(uint32_t)time(NULL)); if ( LP_deadman_switch < time(NULL)-PSOCK_KEEPALIVE ) { printf("DEAD man's switch %u activated at %u lag.%d, register forwarding again\n",LP_deadman_switch,(uint32_t)time(NULL),(uint32_t)(time(NULL) - LP_deadman_switch)); if ( pullsock >= 0 ) nn_close(pullsock); pullsock = LP_initpublicaddr(ctx,&mypullport,pushaddr,myipaddr,mypullport,0); LP_deadman_switch = (uint32_t)time(NULL); strcpy(LP_publicaddr,pushaddr); LP_publicport = mypullport; LP_forwarding_register(LP_mypubkey,pushaddr,mypullport,MAX_PSOCK_PORT); } }*/ /*if ( lastforward < now-3600 ) { if ( (retstr= LP_registerall(0)) != 0 ) free(retstr); //LP_forwarding_register(LP_mypubkey,pushaddr,pushport,10); lastforward = now; }*/ //if ( IAMLP != 0 && (counter % 600) == 42 ) // LP_hellos(); /*if ( 0 && LP_canbind == 0 && (counter % (PSOCK_KEEPALIVE*MAINLOOP_PERSEC/2)) == 13 ) { char keepalive[128]; sprintf(keepalive,"{\"method\":\"keepalive\"}"); //printf("send keepalive to %s pullsock.%d\n",pushaddr,pullsock); if ( /LP_send(pullsock,keepalive,(int32_t)strlen(keepalive)+1,0) < 0 ) { //LP_deadman_switch = 0; } }*/ /*int32_t nn_tests(void *ctx,int32_t pullsock,char *pushaddr,int32_t nnother) { int32_t sock,n,m,timeout,retval = -1; char msg[512],*retstr; printf("nn_tests.(%s)\n",pushaddr); if ( (sock= nn_socket(AF_SP,nnother)) >= 0 ) { if ( nn_connect(sock,pushaddr) < 0 ) printf("connect error %s\n",nn_strerror(nn_errno())); else { sleep(3); timeout = 1; nn_setsockopt(sock,NN_SOL_SOCKET,NN_SNDTIMEO,&timeout,sizeof(timeout)); sprintf(msg,"{\"method\":\"nn_tests\",\"ipaddr\":\"%s\"}",pushaddr); n = /LP_send(sock,msg,(int32_t)strlen(msg)+1,0); sleep(3); LP_pullsock_check(ctx,&retstr,"127.0.0.1",-1,pullsock,0.); sprintf(msg,"{\"method\":\"nn_tests2\",\"ipaddr\":\"%s\"}",pushaddr); m = /LP_send(pullsock,msg,(int32_t)strlen(msg)+1,0); printf(">>>>>>>>>>>>>>>>>>>>>> sent %d+%d bytes -> pullsock.%d retstr.(%s)\n",n,m,pullsock,retstr!=0?retstr:""); if ( retstr != 0 ) { free(retstr); retval = 0; } } nn_close(sock); } return(retval); }*/ int32_t basilisk_swap_load(uint32_t requestid,uint32_t quoteid,bits256 *privkeyp,struct basilisk_request *rp,uint32_t *statebitsp,int32_t *optiondurationp) { FILE *fp=0; char fname[512]; int32_t retval = -1; sprintf(fname,"%s/SWAPS/%u-%u",GLOBAL_DBDIR,requestid,quoteid), OS_compatible_path(fname); if ( (fp= fopen(fname,"rb+")) != 0 ) { if ( fread(privkeyp,1,sizeof(*privkeyp),fp) == sizeof(*privkeyp) && fread(rp,1,sizeof(*rp),fp) == sizeof(*rp) && fread(statebitsp,1,sizeof(*statebitsp),fp) == sizeof(*statebitsp) && fread(optiondurationp,1,sizeof(*optiondurationp),fp) == sizeof(*optiondurationp) ) retval = 0; fclose(fp); } return(retval); } void basilisk_swap_saveupdate(struct basilisk_swap *swap) { FILE *fp; char fname[512]; sprintf(fname,"%s/SWAPS/%u-%u.swap",GLOBAL_DBDIR,swap->I.req.requestid,swap->I.req.quoteid), OS_compatible_path(fname); if ( (fp= fopen(fname,"wb")) != 0 ) { fwrite(&swap->I,1,sizeof(swap->I),fp); /*fwrite(&swap->bobdeposit,1,sizeof(swap->bobdeposit),fp); fwrite(&swap->bobpayment,1,sizeof(swap->bobpayment),fp); fwrite(&swap->alicepayment,1,sizeof(swap->alicepayment),fp); fwrite(&swap->myfee,1,sizeof(swap->myfee),fp); fwrite(&swap->otherfee,1,sizeof(swap->otherfee),fp); fwrite(&swap->aliceclaim,1,sizeof(swap->aliceclaim),fp); fwrite(&swap->alicespend,1,sizeof(swap->alicespend),fp); fwrite(&swap->bobreclaim,1,sizeof(swap->bobreclaim),fp); fwrite(&swap->bobspend,1,sizeof(swap->bobspend),fp); fwrite(&swap->bobrefund,1,sizeof(swap->bobrefund),fp); fwrite(&swap->alicereclaim,1,sizeof(swap->alicereclaim),fp);*/ fwrite(swap->privkeys,1,sizeof(swap->privkeys),fp); fwrite(swap->otherdeck,1,sizeof(swap->otherdeck),fp); fwrite(swap->deck,1,sizeof(swap->deck),fp); fclose(fp); } } void basilisk_swap_sendabort(struct basilisk_swap *swap) { uint32_t msgbits = 0; uint8_t buf[sizeof(msgbits) + sizeof(swap->I.req.quoteid) + sizeof(bits256)*2]; int32_t sentbytes,offset=0; memset(buf,0,sizeof(buf)); offset += iguana_rwnum(1,&buf[offset],sizeof(swap->I.req.quoteid),&swap->I.req.quoteid); offset += iguana_rwnum(1,&buf[offset],sizeof(msgbits),&msgbits); if ( (sentbytes= nn_send(swap->pushsock,buf,offset,0)) != offset ) { if ( sentbytes < 0 ) { if ( swap->pushsock >= 0 ) // nn_close(swap->pushsock), swap->pushsock = -1; if ( swap->subsock >= 0 ) // nn_close(swap->subsock), swap->subsock = -1; swap->connected = 0; } } else printf("basilisk_swap_sendabort\n"); } void basilisk_psockinit(struct basilisk_swap *swap,int32_t amlp); void basilisk_swapgotdata(struct basilisk_swap *swap,uint32_t crc32,bits256 srchash,bits256 desthash,uint32_t quoteid,uint32_t msgbits,uint8_t *data,int32_t datalen,int32_t reinit) { int32_t i; struct basilisk_swapmessage *mp; for (i=0; inummessages; i++) if ( crc32 == swap->messages[i].crc32 && msgbits == swap->messages[i].msgbits && bits256_cmp(srchash,swap->messages[i].srchash) == 0 && bits256_cmp(desthash,swap->messages[i].desthash) == 0 ) return; //printf(" new message.[%d] datalen.%d Q.%x msg.%x [%llx]\n",swap->nummessages,datalen,quoteid,msgbits,*(long long *)data); swap->messages = realloc(swap->messages,sizeof(*swap->messages) * (swap->nummessages + 1)); mp = &swap->messages[swap->nummessages++]; mp->crc32 = crc32; mp->srchash = srchash; mp->desthash = desthash; mp->msgbits = msgbits; mp->quoteid = quoteid; mp->data = malloc(datalen); mp->datalen = datalen; memcpy(mp->data,data,datalen); if ( reinit == 0 && swap->fp != 0 ) { fwrite(mp,1,sizeof(*mp),swap->fp); fwrite(data,1,datalen,swap->fp); fflush(swap->fp); } } int32_t basilisk_swapget(struct basilisk_swap *swap,uint32_t msgbits,uint8_t *data,int32_t maxlen,int32_t (*basilisk_verify_func)(void *ptr,uint8_t *data,int32_t datalen)) { uint8_t *ptr; bits256 srchash,desthash; uint32_t crc32,_msgbits,quoteid; int32_t i,size,offset,retval = -1; struct basilisk_swapmessage *mp = 0; while ( (size= nn_recv(swap->subsock,&ptr,NN_MSG,NN_DONTWAIT)) >= 0 ) { swap->lasttime = (uint32_t)time(NULL); memset(srchash.bytes,0,sizeof(srchash)); memset(desthash.bytes,0,sizeof(desthash)); //printf("gotmsg.[%d] crc.%x\n",size,crc32); offset = 0; for (i=0; i<32; i++) srchash.bytes[i] = ptr[offset++]; for (i=0; i<32; i++) desthash.bytes[i] = ptr[offset++]; offset += iguana_rwnum(0,&ptr[offset],sizeof(uint32_t),"eid); offset += iguana_rwnum(0,&ptr[offset],sizeof(uint32_t),&_msgbits); if ( size > offset ) { crc32 = calc_crc32(0,&ptr[offset],size-offset); if ( size > offset ) { //printf("size.%d offset.%d datalen.%d\n",size,offset,size-offset); basilisk_swapgotdata(swap,crc32,srchash,desthash,quoteid,_msgbits,&ptr[offset],size-offset,0); } } else if ( bits256_nonz(srchash) == 0 && bits256_nonz(desthash) == 0 ) { if ( swap->aborted == 0 ) { swap->aborted = (uint32_t)time(NULL); printf("got abort signal from other side\n"); } } else printf("basilisk_swapget: got strange packet\n"); if ( ptr != 0 ) nn_freemsg(ptr), ptr = 0; } //char str[65],str2[65]; for (i=0; inummessages; i++) { //printf("%d: %s vs %s\n",i,bits256_str(str,swap->messages[i].srchash),bits256_str(str2,swap->messages[i].desthash)); if ( bits256_cmp(swap->messages[i].desthash,swap->I.myhash) == 0 ) { if ( swap->messages[i].msgbits == msgbits ) { if ( swap->I.iambob == 0 && swap->lasttime != 0 && time(NULL) > swap->lasttime+360 ) { printf("nothing received for a while from Bob, try new sockets\n"); if ( swap->pushsock >= 0 ) // nn_close(swap->pushsock), swap->pushsock = -1; if ( swap->subsock >= 0 ) // nn_close(swap->subsock), swap->subsock = -1; swap->connected = 0; basilisk_psockinit(swap,swap->I.iambob != 0); } mp = &swap->messages[i]; if ( msgbits != 0x80000000 ) break; } } } if ( mp != 0 ) retval = (*basilisk_verify_func)(swap,mp->data,mp->datalen); //printf("mine/other %s vs %s\n",bits256_str(str,swap->I.myhash),bits256_str(str2,swap->I.otherhash)); return(retval); } int32_t basilisk_messagekeyread(uint8_t *key,uint32_t *channelp,uint32_t *msgidp,bits256 *srchashp,bits256 *desthashp) { int32_t keylen = 0; keylen += iguana_rwnum(0,&key[keylen],sizeof(uint32_t),channelp); keylen += iguana_rwnum(0,&key[keylen],sizeof(uint32_t),msgidp); keylen += iguana_rwbignum(0,&key[keylen],sizeof(*srchashp),srchashp->bytes); keylen += iguana_rwbignum(0,&key[keylen],sizeof(*desthashp),desthashp->bytes); return(keylen); } int32_t basilisk_messagekey(uint8_t *key,uint32_t channel,uint32_t msgid,bits256 srchash,bits256 desthash) { int32_t keylen = 0; keylen += iguana_rwnum(1,&key[keylen],sizeof(uint32_t),&channel); keylen += iguana_rwnum(1,&key[keylen],sizeof(uint32_t),&msgid); keylen += iguana_rwbignum(1,&key[keylen],sizeof(srchash),srchash.bytes); keylen += iguana_rwbignum(1,&key[keylen],sizeof(desthash),desthash.bytes); return(keylen); } void LP_channelsend(bits256 srchash,bits256 desthash,uint32_t channel,uint32_t msgid,uint8_t *data,int32_t datalen) { int32_t keylen; uint8_t key[BASILISK_KEYSIZE]; //char *retstr; keylen = basilisk_messagekey(key,channel,msgid,srchash,desthash); //if ( (retstr= _dex_reqsend(myinfo,"DEX",key,keylen,data,datalen)) != 0 ) // free(retstr); } #ifdef adfafds void iguana_ensure_privkey(struct iguana_info *coin,bits256 privkey) { uint8_t pubkey33[33]; struct iguana_waccount *wacct; struct iguana_waddress *waddr,addr; char coinaddr[128]; bitcoin_pubkey33(swap->ctx,pubkey33,privkey); bitcoin_address(coinaddr,coin->pubtype,pubkey33,33); //printf("privkey for (%s)\n",coinaddr); if ( myinfo->expiration != 0 && ((waddr= iguana_waddresssearch(&wacct,coinaddr)) == 0 || bits256_nonz(waddr->privkey) == 0) ) { if ( waddr == 0 ) { memset(&addr,0,sizeof(addr)); iguana_waddresscalc(coin->pubtype,coin->wiftype,&addr,privkey); if ( (wacct= iguana_waccountfind("default")) != 0 ) waddr = iguana_waddressadd(coin,wacct,&addr,0); } if ( waddr != 0 ) { waddr->privkey = privkey; if ( bitcoin_priv2wif(waddr->wifstr,waddr->privkey,coin->wiftype) > 0 ) { if ( (0) && waddr->wiftype != coin->wiftype ) printf("ensurepriv warning: mismatched wiftype %02x != %02x\n",waddr->wiftype,coin->wiftype); if ( (0) && waddr->addrtype != coin->pubtype ) printf("ensurepriv warning: mismatched addrtype %02x != %02x\n",waddr->addrtype,coin->pubtype); } } } } int32_t basilisk_rawtx_return(struct basilisk_rawtx *rawtx,cJSON *item,int32_t lockinputs,struct vin_info *V) { char *signedtx,*txbytes; cJSON *vins,*privkeyarray; int32_t i,n,retval = -1; if ( (txbytes= jstr(item,"rawtx")) != 0 && (vins= jobj(item,"vins")) != 0 ) { privkeyarray = cJSON_CreateArray(); jaddistr(privkeyarray,wifstr); if ( (signedtx= LP_signrawtx(rawtx->coin->symbol,&rawtx->I.signedtxid,&rawtx->I.completed,vins,txbytes,privkeyarray,V)) != 0 ) { if ( lockinputs != 0 ) { //printf("lockinputs\n"); LP_unspentslock(rawtx->coin->symbol,vins); if ( (n= cJSON_GetArraySize(vins)) != 0 ) { bits256 txid; int32_t vout; for (i=0; iI.datalen = (int32_t)strlen(signedtx) >> 1; //rawtx->txbytes = calloc(1,rawtx->I.datalen); decode_hex(rawtx->txbytes,rawtx->I.datalen,signedtx); //printf("%s SIGNEDTX.(%s)\n",rawtx->name,signedtx); free(signedtx); retval = 0; } else printf("error signrawtx\n"); //do a very short timeout so it finishes via local poll free_json(privkeyarray); } return(retval); } #endif cJSON *LP_createvins(struct basilisk_rawtx *dest,struct vin_info *V,struct basilisk_rawtx *rawtx,uint8_t *userdata,int32_t userdatalen,uint32_t sequenceid) { cJSON *vins,*item,*sobj; char hexstr[8192]; vins = cJSON_CreateArray(); item = cJSON_CreateObject(); if ( userdata != 0 && userdatalen > 0 ) { memcpy(V[0].userdata,userdata,userdatalen); V[0].userdatalen = userdatalen; init_hexbytes_noT(hexstr,userdata,userdatalen); jaddstr(item,"userdata",hexstr); #ifdef DISABLE_CHECKSIG needsig = 0; #endif } //printf("rawtx B\n"); if ( bits256_nonz(rawtx->I.actualtxid) != 0 ) jaddbits256(item,"txid",rawtx->I.actualtxid); else jaddbits256(item,"txid",rawtx->I.signedtxid); jaddnum(item,"vout",0); //sobj = cJSON_CreateObject(); init_hexbytes_noT(hexstr,rawtx->spendscript,rawtx->I.spendlen); //jaddstr(sobj,"hex",hexstr); //jadd(item,"scriptPubKey",sobj); jaddstr(item,"scriptPubKey",hexstr); jaddnum(item,"suppress",dest->I.suppress_pubkeys); jaddnum(item,"sequence",sequenceid); if ( (dest->I.redeemlen= rawtx->I.redeemlen) != 0 ) { init_hexbytes_noT(hexstr,rawtx->redeemscript,rawtx->I.redeemlen); memcpy(dest->redeemscript,rawtx->redeemscript,rawtx->I.redeemlen); jaddstr(item,"redeemScript",hexstr); } jaddi(vins,item); return(vins); } int32_t _basilisk_rawtx_gen(char *str,uint32_t swapstarted,uint8_t *pubkey33,int32_t iambob,int32_t lockinputs,struct basilisk_rawtx *rawtx,uint32_t locktime,uint8_t *script,int32_t scriptlen,int64_t txfee,int32_t minconf,int32_t delay,bits256 privkey) { char scriptstr[1024],wifstr[256],coinaddr[64],*signedtx,*rawtxbytes; uint32_t basilisktag; int32_t retval = -1; cJSON *vins,*privkeys,*addresses,*valsobj; struct vin_info *V; init_hexbytes_noT(scriptstr,script,scriptlen); basilisktag = (uint32_t)LP_rand(); valsobj = cJSON_CreateObject(); jaddstr(valsobj,"coin",rawtx->coin->symbol); jaddstr(valsobj,"spendscript",scriptstr); jaddstr(valsobj,"changeaddr",rawtx->coin->smartaddr); jadd64bits(valsobj,"satoshis",rawtx->I.amount); if ( strcmp(rawtx->coin->symbol,"BTC") == 0 && txfee > 0 && txfee < 50000 ) txfee = 50000; jadd64bits(valsobj,"txfee",txfee); jaddnum(valsobj,"minconf",minconf); if ( locktime == 0 ) locktime = (uint32_t)time(NULL) - 777; jaddnum(valsobj,"locktime",locktime); jaddnum(valsobj,"timeout",30000); jaddnum(valsobj,"timestamp",swapstarted+delay); addresses = cJSON_CreateArray(); bitcoin_address(coinaddr,rawtx->coin->pubtype,pubkey33,33); jaddistr(addresses,coinaddr); jadd(valsobj,"addresses",addresses); rawtx->I.locktime = locktime; printf("%s locktime.%u\n",rawtx->name,locktime); V = calloc(256,sizeof(*V)); privkeys = cJSON_CreateArray(); bitcoin_priv2wif(wifstr,privkey,rawtx->coin->wiftype); jaddistr(privkeys,wifstr); vins = LP_createvins(rawtx,V,rawtx,0,0,0xffffffff); rawtx->vins = jduplicate(vins); jdelete(valsobj,"vin"); jadd(valsobj,"vin",vins); if ( (rawtxbytes= bitcoin_json2hex(rawtx->coin->isPoS,&rawtx->I.txid,valsobj,V)) != 0 ) { //printf("rawtx.(%s) vins.%p\n",rawtxbytes,vins); if ( (signedtx= LP_signrawtx(rawtx->coin->symbol,&rawtx->I.signedtxid,&rawtx->I.completed,vins,rawtxbytes,privkeys,V)) != 0 ) { rawtx->I.datalen = (int32_t)strlen(signedtx) >> 1; if ( rawtx->I.datalen <= sizeof(rawtx->txbytes) ) decode_hex(rawtx->txbytes,rawtx->I.datalen,signedtx); else printf("DEX tx is too big %d vs %d\n",rawtx->I.datalen,(int32_t)sizeof(rawtx->txbytes)); if ( signedtx != rawtxbytes ) free(signedtx); if ( rawtx->I.completed != 0 ) retval = 0; else printf("couldnt complete sign transaction %s\n",rawtx->name); } else printf("error signing\n"); free(rawtxbytes); } else printf("error making rawtx\n"); free_json(privkeys); free_json(valsobj); free(V); return(retval); } int32_t _basilisk_rawtx_sign(char *symbol,uint8_t pubtype,uint8_t p2shtype,uint8_t isPoS,uint8_t wiftype,struct basilisk_swap *swap,uint32_t timestamp,uint32_t locktime,uint32_t sequenceid,struct basilisk_rawtx *dest,struct basilisk_rawtx *rawtx,bits256 privkey,bits256 *privkey2,uint8_t *userdata,int32_t userdatalen,int32_t ignore_cltverr) { char *rawtxbytes=0,*signedtx=0,wifstr[128]; cJSON *txobj,*vins,*privkeys; int32_t needsig=1,retval = -1; struct vin_info *V; V = calloc(256,sizeof(*V)); V[0].signers[0].privkey = privkey; bitcoin_pubkey33(swap->ctx,V[0].signers[0].pubkey,privkey); privkeys = cJSON_CreateArray(); bitcoin_priv2wif(wifstr,privkey,wiftype); jaddistr(privkeys,wifstr); if ( privkey2 != 0 ) { V[0].signers[1].privkey = *privkey2; bitcoin_pubkey33(swap->ctx,V[0].signers[1].pubkey,*privkey2); bitcoin_priv2wif(wifstr,*privkey2,wiftype); jaddistr(privkeys,wifstr); V[0].N = V[0].M = 2; //char str[65]; printf("add second privkey.(%s) %s\n",jprint(privkeys,0),bits256_str(str,*privkey2)); } else V[0].N = V[0].M = 1; V[0].suppress_pubkeys = dest->I.suppress_pubkeys; V[0].ignore_cltverr = ignore_cltverr; if ( dest->I.redeemlen != 0 ) memcpy(V[0].p2shscript,dest->redeemscript,dest->I.redeemlen), V[0].p2shlen = dest->I.redeemlen; txobj = bitcoin_txcreate(symbol,isPoS,locktime,userdata == 0 ? 1 : 1,timestamp);//rawtx->coin->locktime_txversion); vins = LP_createvins(dest,V,rawtx,userdata,userdatalen,sequenceid); jdelete(txobj,"vin"); jadd(txobj,"vin",vins); //printf("basilisk_rawtx_sign locktime.%u/%u for %s spendscript.%s -> %s, suppress.%d\n",rawtx->I.locktime,dest->I.locktime,rawtx->name,hexstr,dest->name,dest->I.suppress_pubkeys); txobj = bitcoin_txoutput(txobj,dest->spendscript,dest->I.spendlen,dest->I.amount); if ( (rawtxbytes= bitcoin_json2hex(isPoS,&dest->I.txid,txobj,V)) != 0 ) { //printf("rawtx.(%s) vins.%p\n",rawtxbytes,vins); if ( needsig == 0 ) signedtx = rawtxbytes; if ( signedtx != 0 || (signedtx= LP_signrawtx(symbol,&dest->I.signedtxid,&dest->I.completed,vins,rawtxbytes,privkeys,V)) != 0 ) { dest->I.datalen = (int32_t)strlen(signedtx) >> 1; if ( dest->I.datalen <= sizeof(dest->txbytes) ) decode_hex(dest->txbytes,dest->I.datalen,signedtx); else printf("DEX tx is too big %d vs %d\n",dest->I.datalen,(int32_t)sizeof(dest->txbytes)); if ( signedtx != rawtxbytes ) free(signedtx); if ( dest->I.completed != 0 ) retval = 0; else printf("couldnt complete sign transaction %s\n",rawtx->name); } else printf("error signing\n"); free(rawtxbytes); } else printf("error making rawtx\n"); free_json(privkeys); free_json(txobj); free(V); return(retval); } int32_t basilisk_process_swapverify(void *ptr,int32_t (*internal_func)(void *ptr,uint8_t *data,int32_t datalen),uint32_t channel,uint32_t msgid,uint8_t *data,int32_t datalen,uint32_t expiration,uint32_t duration) { struct basilisk_swap *swap = ptr; if ( internal_func != 0 ) return((*internal_func)(swap,data,datalen)); else return(0); } int32_t basilisk_priviextract(struct iguana_info *coin,char *name,bits256 *destp,uint8_t secret160[20],bits256 srctxid,int32_t srcvout) { /*bits256 txid; char str[65]; int32_t i,vini,scriptlen; uint8_t rmd160[20],scriptsig[IGUANA_MAXSCRIPTSIZE]; memset(privkey.bytes,0,sizeof(privkey)); // use dex_listtransactions! if ( (vini= iguana_vinifind(coin,&txid,srctxid,srcvout)) >= 0 ) { if ( (scriptlen= iguana_scriptsigextract(coin,scriptsig,sizeof(scriptsig),txid,vini)) > 32 ) { for (i=0; i<32; i++) privkey.bytes[i] = scriptsig[scriptlen - 33 + i]; revcalc_rmd160_sha256(rmd160,privkey);//.bytes,sizeof(privkey)); if ( memcmp(secret160,rmd160,sizeof(rmd160)) == sizeof(rmd160) ) { *destp = privkey; printf("basilisk_priviextract found privi %s (%s)\n",name,bits256_str(str,privkey)); return(0); } } }*/ return(-1); } int32_t basilisk_verify_privi(void *ptr,uint8_t *data,int32_t datalen); int32_t basilisk_privBn_extract(struct basilisk_swap *swap,uint8_t *data,int32_t maxlen) { if ( basilisk_priviextract(&swap->bobcoin,"privBn",&swap->I.privBn,swap->I.secretBn,swap->bobrefund.I.actualtxid,0) == 0 ) { printf("extracted privBn from blockchain\n"); } else if ( basilisk_swapget(swap,0x40000000,data,maxlen,basilisk_verify_privi) == 0 ) { } if ( bits256_nonz(swap->I.privBn) != 0 && swap->alicereclaim.I.datalen == 0 ) { char str[65]; printf("got privBn.%s\n",bits256_str(str,swap->I.privBn)); return(basilisk_alicepayment_spend(swap,&swap->alicereclaim)); } return(-1); } int32_t basilisk_privAm_extract(struct basilisk_swap *swap) { if ( basilisk_priviextract(&swap->bobcoin,"privAm",&swap->I.privAm,swap->I.secretAm,swap->bobpayment.I.actualtxid,0) == 0 ) { printf("extracted privAm from blockchain\n"); } if ( bits256_nonz(swap->I.privAm) != 0 && swap->bobspend.I.datalen == 0 ) { char str[65]; printf("got privAm.%s\n",bits256_str(str,swap->I.privAm)); return(basilisk_alicepayment_spend(swap,&swap->bobspend)); } return(-1); } int32_t basilisk_verify_otherstatebits(void *ptr,uint8_t *data,int32_t datalen) { int32_t retval; struct basilisk_swap *swap = ptr; if ( datalen == sizeof(swap->I.otherstatebits) ) { retval = iguana_rwnum(0,data,sizeof(swap->I.otherstatebits),&swap->I.otherstatebits); return(retval); } else return(-1); } int32_t basilisk_verify_statebits(void *ptr,uint8_t *data,int32_t datalen) { int32_t retval = -1; uint32_t statebits; struct basilisk_swap *swap = ptr; if ( datalen == sizeof(swap->I.statebits) ) { retval = iguana_rwnum(0,data,sizeof(swap->I.statebits),&statebits); if ( statebits != swap->I.statebits ) { printf("statebits.%x != %x\n",statebits,swap->I.statebits); return(-1); } } return(retval); } void basilisk_sendstate(struct basilisk_swap *swap,uint8_t *data,int32_t maxlen) { int32_t datalen=0; datalen = iguana_rwnum(1,data,sizeof(swap->I.statebits),&swap->I.statebits); LP_swapsend(swap,0x80000000,data,datalen,0,0); } int32_t basilisk_swapiteration(struct basilisk_swap *swap,uint8_t *data,int32_t maxlen) { int32_t j,datalen,retval = 0; uint32_t savestatebits=0,saveotherbits=0; if ( swap->I.iambob != 0 ) swap->I.statebits |= 0x80; while ( swap->aborted == 0 && ((swap->I.otherstatebits & 0x80) == 0 || (swap->I.statebits & 0x80) == 0) && retval == 0 && time(NULL) < swap->I.expiration ) { if ( swap->connected == 0 ) basilisk_psockinit(swap,swap->I.iambob != 0); printf("D r%u/q%u swapstate.%x otherstate.%x remaining %d\n",swap->I.req.requestid,swap->I.req.quoteid,swap->I.statebits,swap->I.otherstatebits,(int32_t)(swap->I.expiration-time(NULL))); if ( swap->I.iambob != 0 && (swap->I.statebits & 0x80) == 0 ) // wait for fee { if ( basilisk_swapget(swap,0x80,data,maxlen,basilisk_verify_otherfee) == 0 ) { // verify and submit otherfee swap->I.statebits |= 0x80; basilisk_sendstate(swap,data,maxlen); } } else if ( swap->I.iambob == 0 ) swap->I.statebits |= 0x80; basilisk_sendstate(swap,data,maxlen); basilisk_swapget(swap,0x80000000,data,maxlen,basilisk_verify_otherstatebits); if ( (swap->I.otherstatebits & 0x80) != 0 && (swap->I.statebits & 0x80) != 0 ) break; if ( swap->I.statebits == savestatebits && swap->I.otherstatebits == saveotherbits ) sleep(DEX_SLEEP + (swap->I.iambob == 0)*1); savestatebits = swap->I.statebits; saveotherbits = swap->I.otherstatebits; basilisk_swapget(swap,0x80000000,data,maxlen,basilisk_verify_otherstatebits); basilisk_sendstate(swap,data,maxlen); if ( (swap->I.otherstatebits & 0x80) == 0 ) LP_swapdata_rawtxsend(swap,0x80,data,maxlen,&swap->myfee,0x40,0); } basilisk_swap_saveupdate(swap); while ( swap->aborted == 0 && retval == 0 && time(NULL) < swap->I.expiration ) // both sides have setup required data and paid txfee { basilisk_swap_saveupdate(swap); if ( swap->connected == 0 ) basilisk_psockinit(swap,swap->I.iambob != 0); //if ( (LP_rand() % 30) == 0 ) printf("E r%u/q%u swapstate.%x otherstate.%x remaining %d\n",swap->I.req.requestid,swap->I.req.quoteid,swap->I.statebits,swap->I.otherstatebits,(int32_t)(swap->I.expiration-time(NULL))); if ( swap->I.iambob != 0 ) { //printf("BOB\n"); if ( (swap->I.statebits & 0x100) == 0 ) { printf("send bobdeposit\n"); swap->I.statebits |= LP_swapdata_rawtxsend(swap,0x200,data,maxlen,&swap->bobdeposit,0x100,0); } // [BLOCKING: altfound] make sure altpayment is confirmed and send payment else if ( (swap->I.statebits & 0x1000) == 0 ) { printf("check alicepayment\n"); if ( basilisk_swapget(swap,0x1000,data,maxlen,basilisk_verify_alicepaid) == 0 ) { swap->I.statebits |= 0x1000; printf("got alicepayment aliceconfirms.%d\n",swap->I.aliceconfirms); } } else if ( (swap->I.statebits & 0x2000) == 0 ) { if ( (swap->I.aliceconfirms == 0 && swap->aliceunconf != 0) || LP_numconfirms(swap,&swap->alicepayment,1) >= swap->I.aliceconfirms ) { swap->I.statebits |= 0x2000; printf("alicepayment confirmed\n"); } } else if ( (swap->I.statebits & 0x4000) == 0 ) { basilisk_bobscripts_set(swap,0,1); printf("send bobpayment\n"); swap->I.statebits |= LP_swapdata_rawtxsend(swap,0x8000,data,maxlen,&swap->bobpayment,0x4000,0); } // [BLOCKING: privM] Bob waits for privAm either from Alice or alice blockchain else if ( (swap->I.statebits & 0xc0000) != 0xc0000 ) { if ( basilisk_swapget(swap,0x40000,data,maxlen,basilisk_verify_privi) == 0 || basilisk_privAm_extract(swap) == 0 ) // divulges privAm { //printf("got privi spend alicepayment, dont divulge privBn until bobspend propagated\n"); basilisk_alicepayment_spend(swap,&swap->bobspend); if ( LP_swapdata_rawtxsend(swap,0,data,maxlen,&swap->bobspend,0x40000,1) == 0 ) printf("Bob error spending alice payment\n"); else { tradebot_swap_balancingtrade(swap,1); printf("Bob spends alicepayment aliceconfirms.%d\n",swap->I.aliceconfirms); swap->I.statebits |= 0x40000; if ( LP_numconfirms(swap,&swap->bobspend,1) >= swap->I.aliceconfirms ) { printf("bobspend confirmed\n"); swap->I.statebits |= 0x80000; printf("Bob confirming spend of Alice's payment\n"); sleep(DEX_SLEEP); } retval = 1; } } } if ( swap->bobpayment.I.locktime != 0 && time(NULL) > swap->bobpayment.I.locktime ) { // submit reclaim of payment printf("bob reclaims bobpayment\n"); swap->I.statebits |= (0x40000 | 0x80000); if ( LP_swapdata_rawtxsend(swap,0,data,maxlen,&swap->bobreclaim,0,0) == 0 ) printf("Bob error reclaiming own payment after alice timed out\n"); else { printf("Bob reclaimed own payment\n"); while ( 0 && (swap->I.statebits & 0x100000) == 0 ) // why wait for own tx? { if ( LP_numconfirms(swap,&swap->bobreclaim,1) >= 1 ) { printf("bobreclaim confirmed\n"); swap->I.statebits |= 0x100000; printf("Bob confirms reclain of payment\n"); break; } } retval = 1; } } } else { //printf("ALICE\n"); // [BLOCKING: depfound] Alice waits for deposit to confirm and sends altpayment if ( (swap->I.statebits & 0x200) == 0 ) { printf("checkfor deposit\n"); if ( basilisk_swapget(swap,0x200,data,maxlen,basilisk_verify_bobdeposit) == 0 ) { // verify deposit and submit, set confirmed height printf("got bobdeposit\n"); swap->I.statebits |= 0x200; } else printf("no valid deposit\n"); } else if ( (swap->I.statebits & 0x400) == 0 ) { if ( basilisk_istrustedbob(swap) != 0 || (swap->I.bobconfirms == 0 && swap->depositunconf != 0) || LP_numconfirms(swap,&swap->bobdeposit,1) >= swap->I.bobconfirms ) { printf("bobdeposit confirmed\n"); swap->I.statebits |= 0x400; } } else if ( (swap->I.statebits & 0x800) == 0 ) { printf("send alicepayment\n"); swap->I.statebits |= LP_swapdata_rawtxsend(swap,0x1000,data,maxlen,&swap->alicepayment,0x800,0); } // [BLOCKING: payfound] make sure payment is confrmed and send in spend or see bob's reclaim and claim else if ( (swap->I.statebits & 0x8000) == 0 ) { if ( basilisk_swapget(swap,0x8000,data,maxlen,basilisk_verify_bobpaid) == 0 ) { printf("got bobpayment\n"); tradebot_swap_balancingtrade(swap,0); // verify payment and submit, set confirmed height swap->I.statebits |= 0x8000; } } else if ( (swap->I.statebits & 0x10000) == 0 ) { if ( basilisk_istrustedbob(swap) != 0 || (swap->I.bobconfirms == 0 && swap->paymentunconf != 0) || LP_numconfirms(swap,&swap->bobpayment,1) >= swap->I.bobconfirms ) { printf("bobpayment confirmed\n"); swap->I.statebits |= 0x10000; } } else if ( (swap->I.statebits & 0x20000) == 0 ) { printf("alicespend bobpayment\n"); if ( LP_swapdata_rawtxsend(swap,0,data,maxlen,&swap->alicespend,0x20000,0) != 0 )//&& (swap->aliceunconf != 0 || basilisk_numconfirms(swap,&swap->alicespend) > 0) ) { swap->I.statebits |= 0x20000; } } else if ( (swap->I.statebits & 0x40000) == 0 ) { int32_t numconfs; if ( (numconfs= LP_numconfirms(swap,&swap->alicespend,1)) >= swap->I.bobconfirms ) { for (j=datalen=0; j<32; j++) data[datalen++] = swap->I.privAm.bytes[j]; printf("send privAm %x\n",swap->I.statebits); swap->I.statebits |= LP_swapsend(swap,0x40000,data,datalen,0x20000,swap->I.crcs_mypriv); printf("Alice confirms spend of Bob's payment\n"); retval = 1; } else printf("alicespend numconfs.%d < %d\n",numconfs,swap->I.bobconfirms); } if ( swap->bobdeposit.I.locktime != 0 && time(NULL) > swap->bobdeposit.I.locktime ) { printf("Alice claims deposit\n"); if ( LP_swapdata_rawtxsend(swap,0,data,maxlen,&swap->aliceclaim,0,0) == 0 ) printf("Alice couldnt claim deposit\n"); else { printf("Alice claimed deposit\n"); retval = 1; } } else if ( swap->aborted != 0 || basilisk_privBn_extract(swap,data,maxlen) == 0 ) { printf("Alice reclaims her payment\n"); swap->I.statebits |= 0x40000000; if ( LP_swapdata_rawtxsend(swap,0,data,maxlen,&swap->alicereclaim,0x40000000,0) == 0 ) printf("Alice error sending alicereclaim\n"); else { printf("Alice reclaimed her payment\n"); retval = 1; } } } if ( (LP_rand() % 30) == 0 ) printf("finished swapstate.%x other.%x\n",swap->I.statebits,swap->I.otherstatebits); if ( swap->I.statebits == savestatebits && swap->I.otherstatebits == saveotherbits ) sleep(DEX_SLEEP + (swap->I.iambob == 0)*1); savestatebits = swap->I.statebits; saveotherbits = swap->I.otherstatebits; basilisk_sendstate(swap,data,maxlen); basilisk_swapget(swap,0x80000000,data,maxlen,basilisk_verify_otherstatebits); } return(retval); } int32_t swapcompleted(struct basilisk_swap *swap) { if ( swap->I.iambob != 0 ) return(swap->I.bobspent); else return(swap->I.alicespent); } cJSON *swapjson(struct basilisk_swap *swap) { cJSON *retjson = cJSON_CreateObject(); return(retjson); } int32_t basilisk_rwDEXquote(int32_t rwflag,uint8_t *serialized,struct basilisk_request *rp) { int32_t len = 0; len += iguana_rwnum(rwflag,&serialized[len],sizeof(rp->requestid),&rp->requestid); len += iguana_rwnum(rwflag,&serialized[len],sizeof(rp->timestamp),&rp->timestamp); // must be 2nd len += iguana_rwnum(rwflag,&serialized[len],sizeof(rp->quoteid),&rp->quoteid); len += iguana_rwnum(rwflag,&serialized[len],sizeof(rp->quotetime),&rp->quotetime); len += iguana_rwnum(rwflag,&serialized[len],sizeof(rp->optionhours),&rp->optionhours); len += iguana_rwnum(rwflag,&serialized[len],sizeof(rp->srcamount),&rp->srcamount); len += iguana_rwnum(rwflag,&serialized[len],sizeof(rp->unused),&rp->unused); len += iguana_rwbignum(rwflag,&serialized[len],sizeof(rp->srchash),rp->srchash.bytes); len += iguana_rwbignum(rwflag,&serialized[len],sizeof(rp->desthash),rp->desthash.bytes); len += iguana_rwnum(rwflag,&serialized[len],sizeof(rp->destamount),&rp->destamount); if ( rwflag != 0 ) { memcpy(&serialized[len],rp->src,sizeof(rp->src)), len += sizeof(rp->src); memcpy(&serialized[len],rp->dest,sizeof(rp->dest)), len += sizeof(rp->dest); } else { memcpy(rp->src,&serialized[len],sizeof(rp->src)), len += sizeof(rp->src); memcpy(rp->dest,&serialized[len],sizeof(rp->dest)), len += sizeof(rp->dest); } //len += iguana_rwnum(rwflag,&serialized[len],sizeof(rp->DEXselector),&rp->DEXselector); //len += iguana_rwnum(rwflag,&serialized[len],sizeof(rp->extraspace),&rp->extraspace); if ( rp->quoteid != 0 && basilisk_quoteid(rp) != rp->quoteid ) printf(" basilisk_rwDEXquote.%d: quoteid.%u mismatch calc %u rp.%p\n",rwflag,rp->quoteid,basilisk_quoteid(rp),rp); if ( basilisk_requestid(rp) != rp->requestid ) printf(" basilisk_rwDEXquote.%d: requestid.%u mismatch calc %u rp.%p\n",rwflag,rp->requestid,basilisk_requestid(rp),rp); return(len); } struct basilisk_request *basilisk_parsejson(struct basilisk_request *rp,cJSON *reqjson) { uint32_t requestid,quoteid; memset(rp,0,sizeof(*rp)); rp->srchash = jbits256(reqjson,"srchash"); rp->desthash = jbits256(reqjson,"desthash"); rp->srcamount = j64bits(reqjson,"srcamount"); //rp->minamount = j64bits(reqjson,"minamount"); //rp->destamount = j64bits(reqjson,"destamount"); rp->destamount = j64bits(reqjson,"destsatoshis"); //printf("parse DESTSATOSHIS.%llu (%s)\n",(long long)rp->destamount,jprint(reqjson,0)); requestid = juint(reqjson,"requestid"); quoteid = juint(reqjson,"quoteid"); //if ( jstr(reqjson,"relay") != 0 ) // rp->relaybits = (uint32_t)calc_ipbits(jstr(reqjson,"relay")); rp->timestamp = juint(reqjson,"timestamp"); rp->quotetime = juint(reqjson,"quotetime"); safecopy(rp->src,jstr(reqjson,"src"),sizeof(rp->src)); safecopy(rp->dest,jstr(reqjson,"dest"),sizeof(rp->dest)); if ( quoteid != 0 ) { rp->quoteid = basilisk_quoteid(rp); if ( quoteid != rp->quoteid ) printf("basilisk_parsejson quoteid.%u != %u error\n",quoteid,rp->quoteid); } rp->requestid = basilisk_requestid(rp); if ( requestid != rp->requestid ) { int32_t i; for (i=0; irequestid); } return(rp); } cJSON *basilisk_requestjson(struct basilisk_request *rp) { cJSON *item = cJSON_CreateObject(); /*if ( rp->relaybits != 0 ) { expand_ipbits(ipaddr,rp->relaybits); jaddstr(item,"relay",ipaddr); }*/ jaddbits256(item,"srchash",rp->srchash); if ( bits256_nonz(rp->desthash) != 0 ) jaddbits256(item,"desthash",rp->desthash); jaddstr(item,"src",rp->src); if ( rp->srcamount != 0 ) jadd64bits(item,"srcamount",rp->srcamount); //if ( rp->minamount != 0 ) // jadd64bits(item,"minamount",rp->minamount); jaddstr(item,"dest",rp->dest); if ( rp->destamount != 0 ) { //jadd64bits(item,"destamount",rp->destamount); jadd64bits(item,"destsatoshis",rp->destamount); //printf("DESTSATOSHIS.%llu\n",(long long)rp->destamount); } jaddnum(item,"quotetime",rp->quotetime); jaddnum(item,"timestamp",rp->timestamp); jaddnum(item,"requestid",rp->requestid); jaddnum(item,"quoteid",rp->quoteid); //jaddnum(item,"DEXselector",rp->DEXselector); jaddnum(item,"optionhours",rp->optionhours); //jaddnum(item,"profit",(double)rp->profitmargin / 1000000.); if ( rp->quoteid != 0 && basilisk_quoteid(rp) != rp->quoteid ) printf("quoteid mismatch %u vs %u\n",basilisk_quoteid(rp),rp->quoteid); if ( basilisk_requestid(rp) != rp->requestid ) printf("requestid mismatch %u vs calc %u\n",rp->requestid,basilisk_requestid(rp)); { int32_t i; struct basilisk_request R; if ( basilisk_parsejson(&R,item) != 0 ) { if ( memcmp(&R,rp,sizeof(*rp)-sizeof(uint32_t)) != 0 ) { for (i=0; iI.req.requestid); jaddnum(item,"quoteid",swap->I.req.quoteid); jaddnum(item,"state",swap->I.statebits); jaddnum(item,"otherstate",swap->I.otherstatebits); jadd(item,"request",basilisk_requestjson(&swap->I.req)); return(item); } #ifdef later cJSON *basilisk_privkeyarray(struct iguana_info *coin,cJSON *vins) { cJSON *privkeyarray,*item,*sobj; struct iguana_waddress *waddr; struct iguana_waccount *wacct; char coinaddr[64],account[128],wifstr[64],str[65],typestr[64],*hexstr; uint8_t script[1024]; int32_t i,n,len,vout; bits256 txid,privkey; double bidasks[2]; privkeyarray = cJSON_CreateArray(); if ( (n= cJSON_GetArraySize(vins)) > 0 ) { for (i=0; i= 0 ) { iguana_txidcategory(coin,account,coinaddr,txid,vout); if ( coinaddr[0] == 0 && (sobj= jobj(item,"scriptPubKey")) != 0 && (hexstr= jstr(sobj,"hex")) != 0 && is_hexstr(hexstr,0) > 0 ) { len = (int32_t)strlen(hexstr) >> 1; if ( len < (sizeof(script) << 1) ) { decode_hex(script,len,hexstr); if ( len == 25 && script[0] == 0x76 && script[1] == 0xa9 && script[2] == 0x14 ) bitcoin_address(coinaddr,coin->chain->pubtype,script+3,20); } } if ( coinaddr[0] != 0 ) { if ( (waddr= iguana_waddresssearch(&wacct,coinaddr)) != 0 ) { bitcoin_priv2wif(wifstr,waddr->privkey,coin->chain->wiftype); jaddistr(privkeyarray,waddr->wifstr); } else if ( smartaddress(typestr,bidasks,&privkey,coin->symbol,coinaddr) >= 0 ) { bitcoin_priv2wif(wifstr,privkey,coin->chain->wiftype); jaddistr(privkeyarray,wifstr); } else printf("cant find (%s) in wallet\n",coinaddr); } else printf("cant coinaddr from (%s).v%d\n",bits256_str(str,txid),vout); } else printf("invalid txid/vout %d of %d\n",i,n); } } return(privkeyarray); } #endif #ifdef old void basilisk_swaploop(void *_utxo) { uint8_t *data; uint32_t expiration,savestatebits=0,saveotherbits=0; uint32_t channel; int32_t iters,retval=0,j,datalen,maxlen; struct basilisk_swap *swap; struct LP_utxoinfo *utxo = _utxo; swap = utxo->swap; //fprintf(stderr,"start swap iambob.%d\n",swap->I.iambob); maxlen = 1024*1024 + sizeof(*swap); data = malloc(maxlen); expiration = (uint32_t)time(NULL) + 300; //myinfo->DEXactive = expiration; channel = 'D' + ((uint32_t)'E' << 8) + ((uint32_t)'X' << 16); while ( swap->aborted == 0 && (swap->I.statebits & (0x08|0x02)) != (0x08|0x02) && time(NULL) < expiration ) { LP_channelsend(swap->I.req.srchash,swap->I.req.desthash,channel,0x4000000,(void *)&swap->I.req.requestid,sizeof(swap->I.req.requestid)); //,60); if ( swap->connected == 0 ) basilisk_psockinit(swap,swap->I.iambob != 0); if ( swap->connected > 0 ) { printf("A r%u/q%u swapstate.%x\n",swap->I.req.requestid,swap->I.req.quoteid,swap->I.statebits); basilisk_sendstate(swap,data,maxlen); basilisk_sendpubkeys(swap,data,maxlen); // send pubkeys if ( basilisk_checkdeck(swap,data,maxlen) == 0) // check for other deck 0x02 basilisk_sendchoosei(swap,data,maxlen); basilisk_waitchoosei(swap,data,maxlen); // wait for choosei 0x08 if ( (swap->I.statebits & (0x08|0x02)) == (0x08|0x02) ) break; } if ( swap->I.statebits == savestatebits && swap->I.otherstatebits == saveotherbits ) sleep(DEX_SLEEP + (swap->I.iambob == 0)*1); savestatebits = swap->I.statebits; saveotherbits = swap->I.otherstatebits; } if ( swap->connected == 0 ) { printf("couldnt establish connection\n"); retval = -1; } while ( swap->aborted == 0 && retval == 0 && (swap->I.statebits & 0x20) == 0 ) { if ( swap->connected == 0 ) basilisk_psockinit(swap,swap->I.iambob != 0); printf("B r%u/q%u swapstate.%x\n",swap->I.req.requestid,swap->I.req.quoteid,swap->I.statebits); basilisk_sendstate(swap,data,maxlen); basilisk_sendchoosei(swap,data,maxlen); basilisk_sendmostprivs(swap,data,maxlen); if ( basilisk_swapget(swap,0x20,data,maxlen,basilisk_verify_privkeys) == 0 ) { swap->I.statebits |= 0x20; break; } if ( swap->I.statebits == savestatebits && swap->I.otherstatebits == saveotherbits ) sleep(DEX_SLEEP + (swap->I.iambob == 0)*1); savestatebits = swap->I.statebits; saveotherbits = swap->I.otherstatebits; if ( time(NULL) > expiration ) break; } //myinfo->DEXactive = swap->I.expiration; if ( time(NULL) >= expiration ) { retval = -1; //myinfo->DEXactive = 0; } if ( swap->aborted != 0 ) { printf("swap aborted before tx sent\n"); retval = -1; } printf("C r%u/q%u swapstate.%x retval.%d\n",swap->I.req.requestid,swap->I.req.quoteid,swap->I.statebits,retval); iters = 0; while ( swap->aborted == 0 && retval == 0 && (swap->I.statebits & 0x40) == 0 && iters++ < 10 ) // send fee { if ( swap->connected == 0 ) basilisk_psockinit(swap,swap->I.iambob != 0); //printf("sendstate.%x\n",swap->I.statebits); basilisk_sendstate(swap,data,maxlen); //printf("swapget\n"); basilisk_swapget(swap,0x80000000,data,maxlen,basilisk_verify_otherstatebits); //printf("after swapget\n"); if ( swap->I.iambob != 0 && swap->bobdeposit.I.datalen == 0 ) { printf("bobscripts set\n"); if ( basilisk_bobscripts_set(swap,1,1) < 0 ) { sleep(DEX_SLEEP); printf("bobscripts set error\n"); continue; } } if ( swap->I.iambob == 0 ) { /*for (i=0; i<20; i++) printf("%02x",swap->secretAm[i]); printf(" <- secretAm\n"); for (i=0; i<32; i++) printf("%02x",swap->secretAm256[i]); printf(" <- secretAm256\n"); for (i=0; i<32; i++) printf("%02x",swap->pubAm.bytes[i]); printf(" <- pubAm\n"); for (i=0; i<20; i++) printf("%02x",swap->secretBn[i]); printf(" <- secretBn\n"); for (i=0; i<32; i++) printf("%02x",swap->secretBn256[i]); printf(" <- secretBn256\n"); for (i=0; i<32; i++) printf("%02x",swap->pubBn.bytes[i]); printf(" <- pubBn\n"); for (i=0; i<32; i++) printf("%02x",swap->pubA0.bytes[i]); printf(" <- pubA0\n"); for (i=0; i<32; i++) printf("%02x",swap->pubA1.bytes[i]); printf(" <- pubA1\n"); for (i=0; i<32; i++) printf("%02x",swap->pubB0.bytes[i]); printf(" <- pubB0\n"); for (i=0; i<32; i++) printf("%02x",swap->pubB1.bytes[i]); printf(" <- pubB1\n");*/ if ( (retval= basilisk_alicetxs(swap,data,maxlen)) != 0 ) { printf("basilisk_alicetxs error\n"); break; } } } if ( swap->I.iambob == 0 && (swap->I.statebits & 0x40) == 0 ) { printf("couldnt send fee\n"); retval = -8; } if ( retval == 0 ) { if ( swap->I.iambob == 0 && (swap->myfee.I.datalen == 0 || swap->alicepayment.I.datalen == 0 || swap->alicepayment.I.datalen == 0) ) { printf("ALICE's error %d %d %d\n",swap->myfee.I.datalen,swap->alicepayment.I.datalen,swap->alicepayment.I.datalen); retval = -7; } else if ( swap->I.iambob != 0 && swap->bobdeposit.I.datalen == 0 ) //swap->bobpayment.I.datalen == 0 { printf("BOB's error %d %d %d\n",swap->myfee.I.datalen,swap->bobpayment.I.datalen,swap->bobdeposit.I.datalen); retval = -7; } } while ( swap->aborted == 0 && retval == 0 && basilisk_swapiteration(swap,data,maxlen) == 0 ) { if ( swap->I.statebits == savestatebits && swap->I.otherstatebits == saveotherbits ) sleep(DEX_SLEEP + (swap->I.iambob == 0)*1); savestatebits = swap->I.statebits; saveotherbits = swap->I.otherstatebits; basilisk_sendstate(swap,data,maxlen); basilisk_swapget(swap,0x80000000,data,maxlen,basilisk_verify_otherstatebits); basilisk_swap_saveupdate(swap); if ( time(NULL) > swap->I.expiration ) break; } if ( swap->I.iambob != 0 && swap->bobdeposit.I.datalen != 0 && bits256_nonz(swap->bobdeposit.I.actualtxid) != 0 ) { printf("BOB waiting for confirm state.%x\n",swap->I.statebits); sleep(60); // wait for confirm/propagation of msig printf("BOB reclaims refund\n"); basilisk_bobdeposit_refund(swap,0); if ( LP_swapdata_rawtxsend(swap,0,data,maxlen,&swap->bobrefund,0x40000000,0) == 0 ) // use secretBn { printf("Bob submit error getting refund of deposit\n"); } else { // maybe wait for bobrefund to be confirmed for (j=datalen=0; j<32; j++) data[datalen++] = swap->I.privBn.bytes[j]; LP_swapsend(swap,0x40000000,data,datalen,0x40000000,swap->I.crcs_mypriv); } basilisk_swap_saveupdate(swap); } if ( retval != 0 ) basilisk_swap_sendabort(swap); printf("end of atomic swap\n"); if ( swapcompleted(swap) > 0 ) // only if swap completed { if ( swap->I.iambob != 0 ) tradebot_pendingadd(swapjson(swap),swap->I.req.src,dstr(swap->I.req.srcamount),swap->I.req.dest,dstr(swap->I.req.destamount)); else tradebot_pendingadd(swapjson(swap),swap->I.req.dest,dstr(swap->I.req.destamount),swap->I.req.src,dstr(swap->I.req.srcamount)); } printf("%s swap finished statebits %x\n",swap->I.iambob!=0?"BOB":"ALICE",swap->I.statebits); basilisk_swap_purge(swap); free(data); } #endif int32_t bitcoin_coinptrs(bits256 pubkey,struct iguana_info **bobcoinp,struct iguana_info **alicecoinp,char *src,char *dest,bits256 srchash,bits256 desthash) { struct iguana_info *coin = LP_coinfind(src); if ( coin == 0 || LP_coinfind(dest) == 0 ) return(0); *bobcoinp = *alicecoinp = 0; *bobcoinp = LP_coinfind(dest); *alicecoinp = LP_coinfind(src); if ( bits256_cmp(pubkey,srchash) == 0 ) { if ( strcmp(src,(*bobcoinp)->symbol) == 0 ) return(1); else if ( strcmp(dest,(*alicecoinp)->symbol) == 0 ) return(-1); else return(0); } else if ( bits256_cmp(pubkey,desthash) == 0 ) { if ( strcmp(src,(*bobcoinp)->symbol) == 0 ) return(-1); else if ( strcmp(dest,(*alicecoinp)->symbol) == 0 ) return(1); else return(0); } return(0); } struct LP_utxoinfo *LP_utxopairfind(int32_t iambob,bits256 txid,int32_t vout,bits256 txid2,int32_t vout2) { struct LP_utxoinfo *utxo=0; struct _LP_utxoinfo u; if ( (utxo= LP_utxofind(iambob,txid,vout)) != 0 ) { u = (iambob != 0) ? utxo->deposit : utxo->fee; if (vout2 == u.vout && bits256_cmp(u.txid,txid2) == 0 ) return(utxo); } return(0); } void LP_utxosetkey(uint8_t *key,bits256 txid,int32_t vout) { memcpy(key,txid.bytes,sizeof(txid)); memcpy(&key[sizeof(txid)],&vout,sizeof(vout)); } struct LP_utxoinfo *_LP_utxofind(int32_t iambob,bits256 txid,int32_t vout) { struct LP_utxoinfo *utxo=0; uint8_t key[sizeof(txid) + sizeof(vout)]; LP_utxosetkey(key,txid,vout); HASH_FIND(hh,G.LP_utxoinfos[iambob!=0],key,sizeof(key),utxo); return(utxo); } void _LP_utxo_delete(int32_t iambob,struct LP_utxoinfo *utxo) { HASH_DELETE(hh,G.LP_utxoinfos[iambob],utxo); } void _LP_utxo2_delete(int32_t iambob,struct LP_utxoinfo *utxo) { HASH_DELETE(hh,G.LP_utxoinfos2[iambob],utxo); } struct LP_utxoinfo *_LP_utxo2find(int32_t iambob,bits256 txid2,int32_t vout2) { struct LP_utxoinfo *utxo=0; uint8_t key2[sizeof(txid2) + sizeof(vout2)]; LP_utxosetkey(key2,txid2,vout2); HASH_FIND(hh2,G.LP_utxoinfos2[iambob],key2,sizeof(key2),utxo); return(utxo); } struct LP_utxoinfo *LP_utxofind(int32_t iambob,bits256 txid,int32_t vout) { struct LP_utxoinfo *utxo=0; /*if ( iambob != 0 ) { printf("LP_utxofind deprecated iambob\n"); return(0); }*/ portable_mutex_lock(&LP_utxomutex); utxo = _LP_utxofind(iambob,txid,vout); portable_mutex_unlock(&LP_utxomutex); return(utxo); } struct LP_utxoinfo *LP_utxo2find(int32_t iambob,bits256 txid2,int32_t vout2) { struct LP_utxoinfo *utxo=0; /*if ( iambob != 0 ) { printf("LP_utxo2find deprecated iambob\n"); return(0); }*/ portable_mutex_lock(&LP_utxomutex); utxo = _LP_utxo2find(iambob,txid2,vout2); portable_mutex_unlock(&LP_utxomutex); return(utxo); } /*void LP_privkeysloop(void *ctx) { strcpy(LP_privkeysloop_stats.name,"LP_privkeysloop"); LP_privkeysloop_stats.threshold = (LP_ORDERBOOK_DURATION * .8 * 1000) + 10000; sleep(20); while ( 1 ) { LP_millistats_update(&LP_privkeysloop_stats); //printf("LP_privkeysloop %u\n",LP_counter); LP_privkey_updates(ctx,LP_mypubsock,0); sleep(LP_ORDERBOOK_DURATION * .777); } }*/ /*void basilisk_swap_purge(struct basilisk_swap *swap) { int32_t i,n; // while still in orderbook, wait //return; portable_mutex_lock(&myinfo->DEX_swapmutex); n = myinfo->numswaps; for (i=0; iswaps[i] == swap ) { myinfo->swaps[i] = myinfo->swaps[--myinfo->numswaps]; myinfo->swaps[myinfo->numswaps] = 0; basilisk_swap_finished(swap); break; } portable_mutex_unlock(&myinfo->DEX_swapmutex); }*/ /*if ( bits256_nonz(Q.srchash) == 0 || bits256_cmp(Q.srchash,G.LP_mypub25519) == 0 || strcmp(butxo->coinaddr,coin->smartaddr) != 0 || bits256_nonz(butxo->payment.txid) == 0 || bits256_nonz(butxo->deposit.txid) == 0 ) { qprice = (double)Q.destsatoshis / Q.satoshis; strcpy(Q.gui,G.gui); strcpy(Q.coinaddr,coin->smartaddr); strcpy(butxo->coinaddr,coin->smartaddr); Q.srchash = G.LP_mypub25519; memset(&Q.txid,0,sizeof(Q.txid)); memset(&Q.txid2,0,sizeof(Q.txid2)); Q.vout = Q.vout2 = -1; recalc = 1; } else if ( (qprice= LP_quote_validate(autxo,butxo,&Q,1)) < SMALLVAL ) recalc = 1; else if ( price < qprice ) { char tmp[64]; if ( bits256_nonz(Q.txid) != 0 ) LP_utxos_remove(Q.txid,Q.vout); else recalc = 1; if ( bits256_nonz(Q.txid2) != 0 ) LP_utxos_remove(Q.txid2,Q.vout2); else recalc = 1; //printf("price %.8f qprice %.8f\n",price,qprice); if ( recalc == 0 ) { value = LP_txvalue(tmp,Q.srccoin,Q.txid,Q.vout); value2 = LP_txvalue(tmp,Q.srccoin,Q.txid2,Q.vout2); //printf("call LP_utxoadd.(%s) %.8f %.8f\n",Q.coinaddr,dstr(value),dstr(value2)); if ( (butxo= LP_utxoadd(1,coin->symbol,Q.txid,Q.vout,value,Q.txid2,Q.vout2,value2,Q.coinaddr,Q.srchash,G.gui,0,Q.satoshis)) == 0 ) recalc = 1; else if ( bits256_cmp(Q.txid,butxo->payment.txid) != 0 || Q.vout != butxo->payment.vout || bits256_cmp(Q.txid2,butxo->deposit.txid) != 0 || Q.vout2 != butxo->deposit.vout ) recalc = 1; } } else return(retval);*/ int32_t LP_isavailable(struct LP_utxoinfo *utxo) { struct _LP_utxoinfo u; u = (utxo->iambob != 0) ? utxo->deposit : utxo->fee; if ( LP_allocated(utxo->payment.txid,utxo->payment.vout) == 0 && LP_allocated(u.txid,u.vout) == 0 ) return(1); else return(0); } /*int32_t LP_priceping(int32_t pubsock,struct LP_utxoinfo *utxo,char *rel,double origprice) { double price,bid,ask; uint32_t now; cJSON *retjson; struct LP_quoteinfo Q; char *retstr; if ( (now= (uint32_t)time(NULL)) > utxo->T.swappending && utxo->S.swap == 0 ) utxo->T.swappending = 0; if ( now > utxo->T.published+60 && LP_isavailable(utxo) && (price= LP_myprice(&bid,&ask,utxo->coin,rel)) != 0. ) { if ( origprice < price ) price = origprice; if ( LP_quoteinfoinit(&Q,utxo,rel,price) < 0 ) return(-1); Q.timestamp = (uint32_t)time(NULL); retjson = LP_quotejson(&Q); jaddstr(retjson,"method","quote"); retstr = jprint(retjson,1); //printf("PING.(%s)\n",retstr); if ( pubsock >= 0 ) LP_send(pubsock,retstr,1); else { // verify it is in list // push if it isnt } utxo->T.published = now; return(0); } return(-1); }*/ /*if ( (butxo= LP_utxopairfind(1,Q.txid,Q.vout,Q.txid2,Q.vout2)) == 0 ) { value = LP_txvalue(Q.coinaddr,Q.srccoin,Q.txid,Q.vout); value2 = LP_txvalue(Q.coinaddr,Q.srccoin,Q.txid2,Q.vout2); if ( value == 0 || value2 == 0 ) { printf("zero value %.8f or value2 %.8f\n",dstr(value),dstr(value2)); return(clonestr("{\"error\":\"spent txid or txid2 for bob?\"}")); } if ( (butxo= LP_utxoadd(1,Q.srccoin,Q.txid,Q.vout,value,Q.txid2,Q.vout2,value2,Q.coinaddr,Q.srchash,LP_gui,0)) == 0 ) { printf("cant find or create butxo\n"); return(clonestr("{\"error\":\"cant find or create butxo\"}")); } if ( value < Q.satoshis ) { printf("butxo value %.8f less satoshis %.8f\n",dstr(value),dstr(Q.satoshis)); return(clonestr("{\"error\":\"butxo value less than satoshis\"}")); } }*/ /*if ( addflag != 0 && LP_utxofind(1,Q.txid,Q.vout) == 0 ) { LP_utxoadd(1,-1,Q.srccoin,Q.txid,Q.vout,Q.value,Q.txid2,Q.vout2,Q.value2,"",Q.srcaddr,Q.srchash,0.); LP_utxoadd(0,-1,Q.destcoin,Q.desttxid,Q.destvout,Q.destvalue,Q.feetxid,Q.feevout,Q.feevalu,"",Q.destaddr,Q.desthash,0.); }*/ /*struct LP_utxoinfo *utxo,*tmp; HASH_ITER(hh,LP_utxoinfos[1],utxo,tmp) { if ( LP_ismine(utxo) > 0 && strcmp(utxo->coin,base) == 0 ) LP_priceping(LP_mypubsock,utxo,rel,price * LP_profitratio); }*/ int32_t LP_ismine(struct LP_utxoinfo *utxo) { if ( utxo != 0 && bits256_cmp(utxo->pubkey,G.LP_mypub25519) == 0 ) return(1); else return(0); } queue_t utxosQ; struct LP_utxos_qitem { struct queueitem DL; cJSON *argjson; }; char *LP_postutxos_recv(cJSON *argjson) { struct LP_utxos_qitem *uitem; struct iguana_info *coin; char *coinaddr,*symbol; bits256 utxoshash,pubkey; cJSON *obj; struct LP_pubkey_info *pubp; printf("LP_postutxos_recv deprecated\n"); pubkey = jbits256(argjson,"pubkey"); pubp = LP_pubkeyfind(pubkey); if ( pubp != 0 && pubp->numerrors > LP_MAXPUBKEY_ERRORS ) return(clonestr("{\"error\":\"blacklisted\"}")); if ( (coinaddr= jstr(argjson,"coinaddr")) != 0 && (symbol= jstr(argjson,"coin")) != 0 && (coin= LP_coinfind(symbol)) != 0 ) { if ( strcmp(coinaddr,coin->smartaddr) == 0 ) { //printf("ignore my utxo from external source %s %s\n",symbol,coinaddr); return(clonestr("{\"result\":\"success\"}")); } } if ( (obj= jobj(argjson,"utxos")) != 0 ) { utxoshash = LP_utxoshash_calc(obj); //char str[65]; //printf("got utxoshash %s\n",bits256_str(str,utxoshash)); if ( LP_utxos_sigcheck(juint(argjson,"timestamp"),jstr(argjson,"sig"),jstr(argjson,"pubsecp"),pubkey,utxoshash) == 0 ) { uitem = calloc(1,sizeof(*uitem)); uitem->argjson = jduplicate(argjson); queue_enqueue("utxosQ",&utxosQ,&uitem->DL); return(clonestr("{\"result\":\"success\"}")); } //else printf("valid utxos sig %s\n",bits256_str(str,pubp->pubkey)); } return(clonestr("{\"error\":\"sig failure\"}")); } /*MERK d6071f9b03d1428b648d51ae1268f1605d97f44422ed55ad0335b13fa655f61a ht.518777 -> {"pos":1,"merkle":["526f8be81718beccc16a541a2c550b612123218d80fa884d9f080f18284e2bd8", "f68b03a7b6e418c9b306d8d8b21917ae5a584696f9b0b8cb0741733d7097fdfd"],"block_height":518777} root.(0000000000000000000000000000000000000000000000000000000000000000) MERK c007e9c1881a83be453cb6ed3d1bd3bda85efd3b5ce60532c2e20ae3f8a82543 ht.518777 -> {"pos":2,"merkle":["fdff0962fb95120a86a07ddf1ec784fcc5554a2d0a3791a8db2083d593920501", "8c116e974c842ad3ad8b3ddbd71da3debb150e3fe692f5bd628381bc167311a7"],"block_height":518777} root.(0000000000000000000000000000000000000000000000000000000000000000)*/ /*526f8be81718beccc16a541a2c550b612123218d80fa884d9f080f18284e2bd8 d6071f9b03d1428b648d51ae1268f1605d97f44422ed55ad0335b13fa655f61a c007e9c1881a83be453cb6ed3d1bd3bda85efd3b5ce60532c2e20ae3f8a82543 fdff0962fb95120a86a07ddf1ec784fcc5554a2d0a3791a8db2083d593920501*/ /*0: 526f8be81718beccc16a541a2c550b612123218d80fa884d9f080f18284e2bd8 1: d6071f9b03d1428b648d51ae1268f1605d97f44422ed55ad0335b13fa655f61a 2: c007e9c1881a83be453cb6ed3d1bd3bda85efd3b5ce60532c2e20ae3f8a82543 3: fdff0962fb95120a86a07ddf1ec784fcc5554a2d0a3791a8db2083d593920501 4: 8c116e974c842ad3ad8b3ddbd71da3debb150e3fe692f5bd628381bc167311a7 5: f68b03a7b6e418c9b306d8d8b21917ae5a584696f9b0b8cb0741733d7097fdfd 6: a87ee259560f20b20182760c0e7cc7896d44381f0ad58a2e755a2b6b895b01ec*/ /* 0 1 2 3 4 5 6 1 -> [0, 5] 2 -> [3, 4] if odd -> right, else left then /= 2 */ /*void testmerk() { bits256 tree[256],roothash,txid; int32_t i; char str[65]; memset(tree,0,sizeof(tree)); decode_hex(tree[0].bytes,32,"526f8be81718beccc16a541a2c550b612123218d80fa884d9f080f18284e2bd8"); decode_hex(tree[1].bytes,32,"d6071f9b03d1428b648d51ae1268f1605d97f44422ed55ad0335b13fa655f61a"); decode_hex(tree[2].bytes,32,"c007e9c1881a83be453cb6ed3d1bd3bda85efd3b5ce60532c2e20ae3f8a82543"); decode_hex(tree[3].bytes,32,"fdff0962fb95120a86a07ddf1ec784fcc5554a2d0a3791a8db2083d593920501"); roothash = iguana_merkle(tree,4); for (i=0; i<256; i++) { if ( bits256_nonz(tree[i]) == 0 ) break; printf("%d: %s\n",i,bits256_str(str,tree[i])); } memset(tree,0,sizeof(tree)); decode_hex(tree[0].bytes,32,"526f8be81718beccc16a541a2c550b612123218d80fa884d9f080f18284e2bd8"); decode_hex(tree[1].bytes,32,"f68b03a7b6e418c9b306d8d8b21917ae5a584696f9b0b8cb0741733d7097fdfd"); decode_hex(txid.bytes,32,"d6071f9b03d1428b648d51ae1268f1605d97f44422ed55ad0335b13fa655f61a"); roothash = validate_merkle(1,txid,tree,2); printf("validate 1: %s\n",bits256_str(str,roothash)); memset(tree,0,sizeof(tree)); decode_hex(tree[0].bytes,32,"fdff0962fb95120a86a07ddf1ec784fcc5554a2d0a3791a8db2083d593920501"); decode_hex(tree[1].bytes,32,"8c116e974c842ad3ad8b3ddbd71da3debb150e3fe692f5bd628381bc167311a7"); decode_hex(txid.bytes,32,"c007e9c1881a83be453cb6ed3d1bd3bda85efd3b5ce60532c2e20ae3f8a82543"); roothash = validate_merkle(2,txid,tree,2); printf("validate 2: %s\n",bits256_str(str,roothash)); }*/ /*else if ( (retstr= LP_orderbook(coin->symbol,"KMD",-1)) != 0 ) { if ( (orderbook= cJSON_Parse(retstr)) != 0 ) { if ( (asks= jarray(&numasks,orderbook,"asks")) != 0 && numasks > 0 ) { item = jitem(asks,0); price = ask = jdouble(item,"price"); //printf("%s/%s ask %.8f\n",coin->symbol,"KMD",ask); } if ( (bids= jarray(&numbids,orderbook,"bids")) != 0 && numbids > 0 ) { item = jitem(asks,0); bid = jdouble(item,"price"); if ( price == 0. ) price = bid; else price = (bid + ask) * 0.5; //printf("%s/%s bid %.8f ask %.8f price %.8f\n",coin->symbol,"KMD",bid,ask,price); } KMDvalue = price * balance; free_json(orderbook); } free(retstr); }*/ int32_t LP_utxosQ_process() { struct LP_utxos_qitem *uitem; int32_t n; char *symbol,*coinaddr; struct LP_address *ap; struct iguana_info *coin; cJSON *array; if ( (uitem= queue_dequeue(&utxosQ)) != 0 ) { //printf("LP_utxosQ_process.(%s)\n",jprint(uitem->argjson,0)); if ( (coinaddr= jstr(uitem->argjson,"coinaddr")) != 0 && (symbol= jstr(uitem->argjson,"coin")) != 0 && (coin= LP_coinfind(symbol)) != 0 ) // addsig { if ( coin->electrum == 0 || (ap= LP_addressfind(coin,coinaddr)) != 0 ) { if ( (array= jarray(&n,uitem->argjson,"utxos")) != 0 ) LP_unspents_array(coin,coinaddr,array); } else if ( (array= electrum_address_listunspent(symbol,coin->electrum,&array,coinaddr,1)) != 0 ) free_json(array); } free_json(uitem->argjson); free(uitem); return(1); } return(0); } else if ( strcmp(method,"postutxos") == 0 ) return(LP_postutxos_recv(argjson)); void utxosQ_loop(void *myipaddr) { strcpy(utxosQ_loop_stats.name,"utxosQ_loop"); utxosQ_loop_stats.threshold = 5000.; while ( 1 ) { LP_millistats_update(&utxosQ_loop_stats); if ( LP_utxosQ_process() == 0 ) usleep(50000); } } if ( OS_thread_create(malloc(sizeof(pthread_t)),NULL,(void *)utxosQ_loop,(void *)myipaddr) != 0 ) { printf("error launching utxosQ_loop for (%s)\n",myipaddr); exit(-1); } /* bestprice = 0.; if ( (array= LP_tradecandidates(base)) != 0 ) { printf("candidates.(%s)\nn.%d\n",jprint(array,0),cJSON_GetArraySize(array)); if ( (n= cJSON_GetArraySize(array)) > 0 ) { memset(prices,0,sizeof(prices)); memset(Q,0,sizeof(Q)); for (i=0; icoin,zero); Q[i].destsatoshis = price * Q[i].satoshis; } if ( (prices[i]= price) > SMALLVAL && (bestprice == 0. || price < bestprice) ) bestprice = price; char str[65]; printf("i.%d of %d: (%s) -> txid.%s price %.8f best %.8f dest %.8f\n",i,n,jprint(item,0),bits256_str(str,Q[i].txid),price,bestprice,dstr(Q[i].destsatoshis)); } if ( bestprice > SMALLVAL ) { bestmetric = 0.; besti = -1; for (i=0; i SMALLVAL && myutxo->S.satoshis >= Q[i].destsatoshis+Q[i].desttxfee ) { metric = price / bestprice; printf("%f %f %f %f ",price,metric,dstr(Q[i].destsatoshis),metric * metric * metric); if ( metric < 1.1 ) { metric = dstr(Q[i].destsatoshis) * metric * metric * metric; printf("%f\n",metric); if ( bestmetric == 0. || metric < bestmetric ) { besti = i; bestmetric = metric; } } } else printf("(%f %f) ",dstr(myutxo->S.satoshis),dstr(Q[i].destsatoshis)); } printf("metrics, best %f\n",bestmetric); */ int32_t LP_utxosquery(struct LP_peerinfo *mypeer,int32_t mypubsock,char *destipaddr,uint16_t destport,char *coin,int32_t lastn,char *myipaddr,uint16_t myport,int32_t maxentries) { char *retstr; struct LP_peerinfo *peer; uint32_t now; int32_t retval = -1; printf("deprecated LP_utxosquery\n"); return(-1); peer = LP_peerfind((uint32_t)calc_ipbits(destipaddr),destport); if ( coin == 0 ) coin = ""; //printf("utxo query.(%s)\n",destipaddr); if ( IAMLP != 0 ) retstr = issue_LP_getutxos(destipaddr,destport,coin,lastn,myipaddr,myport,mypeer != 0 ? mypeer->numpeers : 0,maxentries); else retstr = issue_LP_clientgetutxos(destipaddr,destport,coin,maxentries); if ( retstr != 0 ) { now = (uint32_t)time(NULL); retval = LP_utxosparse(destipaddr,destport,retstr,now); //printf("got.(%s)\n",retstr); free(retstr); } return(retval); } char *issue_LP_numutxos(char *destip,uint16_t destport,char *ipaddr,uint16_t port,int32_t numpeers,int32_t numutxos) { char url[512],*retstr; printf("deprecated issue_LP_numutxos\n"); return(0); sprintf(url,"http://%s:%u/api/stats/numutxos?ipaddr=%s&port=%u&numpeers=%d&numutxos=%d",destip,destport,ipaddr,port,numpeers,numutxos); retstr = LP_issue_curl("numutxos",destip,port,url); //printf("%s -> getpeers.(%s)\n",destip,retstr); return(retstr); } char *issue_LP_getutxos(char *destip,uint16_t destport,char *coin,int32_t lastn,char *ipaddr,uint16_t port,int32_t numpeers,int32_t numutxos) { char url[512]; printf("deprecated issue_LP_getutxos\n"); return(0); sprintf(url,"http://%s:%u/api/stats/getutxos?coin=%s&lastn=%d&ipaddr=%s&port=%u&numpeers=%d&numutxos=%d",destip,destport,coin,lastn,ipaddr,port,numpeers,numutxos); return(LP_issue_curl("getutxos",destip,destport,url)); //return(issue_curlt(url,LP_HTTP_TIMEOUT)); } char *issue_LP_clientgetutxos(char *destip,uint16_t destport,char *coin,int32_t lastn) { char url[512];//,*retstr; printf("deprecated issue_LP_clientgetutxos\n"); return(0); sprintf(url,"http://%s:%u/api/stats/getutxos?coin=%s&lastn=%d&ipaddr=127.0.0.1&port=0",destip,destport,coin,lastn); return(LP_issue_curl("clientgetutxos",destip,destport,url)); //retstr = issue_curlt(url,LP_HTTP_TIMEOUT); //printf("%s clientgetutxos.(%s)\n",url,retstr); //return(retstr); } void LP_address_monitor(struct LP_pubkeyinfo *pubp) { struct iguana_info *coin,*tmp; char coinaddr[64]; cJSON *retjson; struct LP_address *ap; return; HASH_ITER(hh,LP_coins,coin,tmp) { bitcoin_address(coinaddr,coin->taddr,coin->pubtype,pubp->rmd160,sizeof(pubp->rmd160)); portable_mutex_lock(&coin->addrmutex); if ( (ap= _LP_address(coin,coinaddr)) != 0 ) { ap->monitor = (uint32_t)time(NULL); } portable_mutex_unlock(&coin->addrmutex); if ( coin->electrum != 0 ) { if ( (retjson= electrum_address_subscribe(coin->symbol,coin->electrum,&retjson,coinaddr)) != 0 ) { printf("%s MONITOR.(%s) -> %s\n",coin->symbol,coinaddr,jprint(retjson,0)); free_json(retjson); } } } } /*else if ( strcmp(method,"ordermatch") == 0 ) { if ( price > SMALLVAL ) return(LP_ordermatch(base,j64bits(argjson,"txfee"),price,jdouble(argjson,"relvolume"),rel,jbits256(argjson,"txid"),jint(argjson,"vout"),jbits256(argjson,"feetxid"),jint(argjson,"feevout"),j64bits(argjson,"desttxfee"),jint(argjson,"duration"))); else return(clonestr("{\"error\":\"no price set\"}")); } else if ( strcmp(method,"trade") == 0 ) { struct LP_quoteinfo Q; if ( price > SMALLVAL || jobj(argjson,"quote") != 0 ) { LP_quoteparse(&Q,jobj(argjson,"quote")); return(LP_trade(ctx,myipaddr,pubsock,&Q,price,jint(argjson,"timeout"),jint(argjson,"duration"))); } else return(clonestr("{\"error\":\"no price set or no quote object\"}")); } else if ( strcmp(method,"autotrade") == 0 ) { if ( price > SMALLVAL ) { return(LP_autotrade(ctx,myipaddr,pubsock,base,rel,price,jdouble(argjson,"relvolume"),jint(argjson,"timeout"),jint(argjson,"duration"))); } else return(clonestr("{\"error\":\"no price set\"}")); }*/ if ( flag != 0 ) { // need to find the requestid/quoteid for aliceid if ( (retstr= basilisk_swapentries(bot->base,bot->rel,0)) != 0 ) { if ( (retjson= cJSON_Parse(retstr)) != 0 ) { if ( (n= cJSON_GetArraySize(retjson)) != 0 ) { for (flag=j=0; jnumtrades; i++) { if ( (tp= bot->trades[i]) != 0 && tp->finished == 0 && tp->requestid == 0 && tp->quoteid == 0 ) { if ( tp->aliceid == aliceid ) { tp->requestid = juint(item,"requestid"); tp->quoteid = juint(item,"quoteid"); printf("found aliceid.%llx to set requestid.%u quoteid.%u\n",(long long)aliceid,tp->requestid,tp->quoteid); flag = 1; break; } } } } } free_json(retjson); } free(retstr); } } // check for finished pending swap for (i=0; inumtrades; i++) { if ( (tp= bot->trades[i]) != 0 && tp->finished == 0 && tp->requestid != 0 && tp->quoteid != 0 ) { if ( (retstr= basilisk_swapentry(tp->requestid,tp->quoteid)) != 0 ) { if ( (retjson= cJSON_Parse(retstr)) != 0 ) { if ( (status= jstr(retjson,"status")) != 0 && strcmp(status,"finished") == 0 ) { bot->pendbasesum -= tp->basevol, bot->basesum += tp->basevol; bot->pendrelsum -= tp->relvol, bot->relsum += tp->relvol; bot->numpending--, bot->completed++; printf("detected completion aliceid.%llx r.%u q.%u\n",(long long)tp->aliceid,tp->requestid,tp->quoteid); tp->finished = (uint32_t)time(NULL); } free_json(retjson); } free(retstr); } } } } int32_t LP_utxopurge(int32_t allutxos) { char str[65]; struct LP_utxoinfo *utxo,*tmp; int32_t iambob,n = 0; printf("LP_utxopurge mypub.(%s)\n",bits256_str(str,LP_mypub25519)); portable_mutex_lock(&LP_utxomutex); for (iambob=0; iambob<=1; iambob++) { HASH_ITER(hh,LP_utxoinfos[iambob],utxo,tmp) { if ( LP_isavailable(utxo) > 0 ) { if ( allutxos != 0 || LP_ismine(utxo) > 0 ) { printf("iambob.%d delete.(%s)\n",iambob,bits256_str(str,utxo->payment.txid)); HASH_DELETE(hh,LP_utxoinfos[iambob],utxo); //free(utxo); let the LP_utxoinfos2 free the utxo, should be 1:1 } else n++; } else n++; } HASH_ITER(hh,LP_utxoinfos2[iambob],utxo,tmp) { if ( LP_isavailable(utxo) > 0 ) { if ( allutxos != 0 || LP_ismine(utxo) > 0 ) { printf("iambob.%d delete2.(%s)\n",iambob,bits256_str(str,utxo->payment.txid)); HASH_DELETE(hh2,LP_utxoinfos2[iambob],utxo); free(utxo); } else n++; } else n++; } } portable_mutex_unlock(&LP_utxomutex); return(n); } /*struct LP_utxoinfo *_LP_butxo_find(struct LP_utxoinfo *butxo) { int32_t i; struct LP_utxoinfo *utxo=0; uint32_t now = (uint32_t)time(NULL); //portable_mutex_lock(&LP_butxomutex); for (i=0; ipayment.vout == utxo->payment.vout && butxo->deposit.vout == utxo->deposit.vout && bits256_nonz(butxo->payment.txid) != 0 && bits256_nonz(butxo->deposit.txid) != 0 && bits256_cmp(butxo->payment.txid,utxo->payment.txid) == 0 && bits256_cmp(butxo->deposit.txid,utxo->deposit.txid) == 0 ) break; if ( utxo->S.swap == 0 && now > utxo->T.swappending ) memset(utxo,0,sizeof(*utxo)); utxo = 0; } //portable_mutex_unlock(&LP_butxomutex); return(utxo); } struct LP_utxoinfo *LP_butxo_add(struct LP_utxoinfo *butxo) { static struct LP_utxoinfo zeroes; int32_t i; struct LP_utxoinfo *utxo=0; portable_mutex_lock(&LP_butxomutex); if ( (utxo= _LP_butxo_find(butxo)) == 0 ) { for (i=0; iS = srcutxo->S; destutxo->T = srcutxo->T; } void LP_butxo_swapfields(struct LP_utxoinfo *butxo) { struct LP_utxoinfo *getutxo=0; portable_mutex_lock(&LP_butxomutex); if ( (getutxo= _LP_butxo_find(butxo)) != 0 ) LP_butxo_swapfields_copy(butxo,getutxo); portable_mutex_unlock(&LP_butxomutex); } void LP_butxo_swapfields_set(struct LP_utxoinfo *butxo) { struct LP_utxoinfo *setutxo; if ( (setutxo= LP_butxo_add(butxo)) != 0 ) { LP_butxo_swapfields_copy(setutxo,butxo); } }*/ /*struct LP_utxoinfo BUTXOS[100]; int32_t LP_butxo_findeither(bits256 txid,int32_t vout) { struct LP_utxoinfo *utxo; int32_t i,retval = 0; portable_mutex_lock(&LP_butxomutex); for (i=0; ipayment.vout && bits256_cmp(txid,utxo->payment.txid)) == 0 || (vout == utxo->deposit.vout && bits256_cmp(txid,utxo->deposit.txid) == 0) ) { retval = 1; break; } } portable_mutex_unlock(&LP_butxomutex); return(retval); }*/ struct LP_utxoinfo *LP_utxoaddjson(int32_t iambob,int32_t pubsock,cJSON *argjson) { struct LP_utxoinfo *utxo; if ( jobj(argjson,"iambob") == 0 || iambob != jint(argjson,"iambob") ) { printf("LP_utxoaddjson: iambob.%d != arg.%d obj.%p (%s)\n",iambob,jint(argjson,"iambob"),jobj(argjson,"iambob"),jprint(argjson,0)); return(0); } portable_mutex_lock(&LP_UTXOmutex); utxo = LP_utxoadd(iambob,pubsock,jstr(argjson,"coin"),jbits256(argjson,"txid"),jint(argjson,"vout"),j64bits(argjson,"value"),jbits256(argjson,"txid2"),jint(argjson,"vout2"),j64bits(argjson,"value2"),jstr(argjson,"script"),jstr(argjson,"address"),jbits256(argjson,"pubkey"),jstr(argjson,"gui"),juint(argjson,"session")); if ( LP_ismine(utxo) > 0 && utxo->T.lasttime == 0 ) { utxo->T.lasttime = (uint32_t)time(NULL); printf("set lasttime!\n"); } portable_mutex_unlock(&LP_UTXOmutex); return(utxo); } int32_t LP_utxosparse(char *destipaddr,uint16_t destport,char *retstr,uint32_t now) { struct LP_peerinfo *peer; uint32_t argipbits; char *argipaddr; uint16_t argport,pushport,subport; cJSON *array,*item; int32_t i,n=0; bits256 txid; struct LP_utxoinfo *utxo; //printf("parse.(%s)\n",retstr); if ( (array= cJSON_Parse(retstr)) != 0 ) { if ( (n= cJSON_GetArraySize(array)) > 0 ) { for (i=0; iT.lasttime = now; } } if ( (destpeer= LP_peerfind((uint32_t)calc_ipbits(destipaddr),destport)) != 0 ) { destpeer->numutxos = n; } } free_json(array); } return(n); } void LP_spentnotify(struct LP_utxoinfo *utxo,int32_t selector) { //cJSON *argjson; struct _LP_utxoinfo u; char *msg; if ( utxo == 0 ) return; utxo->T.spentflag = (uint32_t)time(NULL); /*if ( LP_mypeer != 0 && LP_mypeer->numutxos > 0 ) LP_mypeer->numutxos--; if ( LP_mypubsock >= 0 ) { argjson = cJSON_CreateObject(); jaddstr(argjson,"method","checktxid"); jaddbits256(argjson,"txid",utxo->payment.txid); jaddnum(argjson,"vout",utxo->payment.vout); if ( selector != 0 ) { if ( bits256_nonz(utxo->deposit.txid) != 0 ) u = utxo->deposit; else u = utxo->fee; jaddbits256(argjson,"checktxid",u.txid); jaddnum(argjson,"checkvout",u.vout); } msg = jprint(argjson,1); /LP_send(LP_mypubsock,msg,(int32_t)strlen(msg)+1,1); }*/ } char *LP_utxos(int32_t iambob,struct LP_peerinfo *mypeer,char *symbol,int32_t lastn) { int32_t i,n,m; uint64_t val,val2; struct _LP_utxoinfo u; struct LP_utxoinfo *utxo,*tmp; cJSON *utxosjson = cJSON_CreateArray(); printf("deprecated! LP_utxos\n"); //n = mypeer != 0 ? mypeer->numutxos : 0; if ( lastn <= 0 ) lastn = LP_PROPAGATION_SLACK * 2; HASH_ITER(hh,LP_utxoinfos[iambob],utxo,tmp) { //char str[65]; printf("check %s.%s\n",utxo->coin,bits256_str(str,utxo->payment.txid)); if ( (symbol == 0 || symbol[0] == 0 || strcmp(symbol,utxo->coin) == 0) && utxo->T.spentflag == 0 ) { u = (iambob != 0) ? utxo->deposit : utxo->fee; if ( LP_iseligible(&val,&val2,utxo->iambob,utxo->coin,utxo->payment.txid,utxo->payment.vout,utxo->S.satoshis,u.txid,u.vout) == 0 ) { char str[65]; printf("iambob.%d not eligible (%.8f %.8f) %s %s/v%d\n",iambob,dstr(val),dstr(val2),utxo->coin,bits256_str(str,utxo->payment.txid),utxo->payment.vout); continue; } else jaddi(utxosjson,LP_utxojson(utxo)); } } if ( (n= cJSON_GetArraySize(utxosjson)) > lastn ) { m = n - lastn; for (i=0; icoin)) == 0 ) return(0); if ( (up= LP_address_utxofind(coin,utxo->coinaddr,utxo->payment.txid,utxo->payment.vout)) != 0 && up->spendheight > 0 ) { utxo->T.spentflag = up->spendheight; return(0); } u = (utxo->iambob != 0) ? utxo->deposit : utxo->fee; if ( (up= LP_address_utxofind(coin,utxo->coinaddr,u.txid,u.vout)) != 0 && up->spendheight > 0 ) { utxo->T.spentflag = up->spendheight; return(0); } if ( utxo != 0 && utxo->T.spentflag == 0 && LP_isavailable(utxo) > 0 ) return(1); else return(0); } void LP_utxo_clientpublish(struct LP_utxoinfo *utxo) { bits256 zero; char *msg; if ( 0 && LP_isunspent(utxo) > 0 ) { memset(zero.bytes,0,sizeof(zero)); msg = jprint(LP_utxojson(utxo),1); LP_broadcast_message(LP_mypubsock,utxo->coin,"",zero,msg); } } /*char *LP_spentcheck(cJSON *argjson) { bits256 txid,checktxid; int32_t vout,checkvout; struct LP_utxoinfo *utxo; int32_t iambob,retval = 0; txid = jbits256(argjson,"txid"); vout = jint(argjson,"vout"); for (iambob=0; iambob<=1; iambob++) { if ( (utxo= LP_utxofind(iambob,txid,vout)) != 0 && utxo->T.spentflag == 0 ) { if ( jobj(argjson,"check") == 0 ) checktxid = txid, checkvout = vout; else { checktxid = jbits256(argjson,"checktxid"); checkvout = jint(argjson,"checkvout"); } if ( LP_txvalue(0,utxo->coin,checktxid,checkvout) == 0 ) { //if ( LP_mypeer != 0 && LP_mypeer->numutxos > 0 ) // LP_mypeer->numutxos--; utxo->T.spentflag = (uint32_t)time(NULL); retval++; printf("indeed txid was spent\n"); } } } if ( retval > 0 ) return(clonestr("{\"result\":\"marked as spent\"}")); return(clonestr("{\"error\":\"cant find txid to check spent status\"}")); }*/ /*char *LP_pricestr(char *base,char *rel,double origprice) { cJSON *retjson; double price = 0.; if ( base != 0 && base[0] != 0 && rel != 0 && rel[0] != 0 ) { price = LP_price(base,rel); if ( origprice > SMALLVAL && origprice < price ) price = origprice; } if ( LP_pricevalid(price) > 0 ) { retjson = cJSON_CreateObject(); jaddstr(retjson,"result","success"); jaddstr(retjson,"method","postprice"); jaddbits256(retjson,"pubkey",G.LP_mypub25519); jaddstr(retjson,"base",base); jaddstr(retjson,"rel",rel); jaddnum(retjson,"price",price); jadd(retjson,"theoretical",LP_priceinfomatrix(0)); jadd(retjson,"quotes",LP_priceinfomatrix(1)); return(jprint(retjson,1)); } else return(clonestr("{\"error\":\"cant find baserel pair\"}")); }*/ void LP_utxo_spentcheck(int32_t pubsock,struct LP_utxoinfo *utxo) { struct _LP_utxoinfo u; struct iguana_info *coin; char str[65]; uint32_t now = (uint32_t)time(NULL); if ( IAMLP != 0 && (coin= LP_coinfind(utxo->coin)) != 0 && coin->inactive != 0 ) return; //printf("%s lag.%d\n",bits256_str(str,utxo->txid),now-utxo->lastspentcheck); if ( utxo->T.spentflag == 0 && now > utxo->T.lastspentcheck+60 ) { u = (utxo->iambob != 0) ? utxo->deposit : utxo->fee; utxo->T.lastspentcheck = now; if ( LP_txvalue(0,utxo->coin,utxo->payment.txid,utxo->payment.vout) == 0 ) { printf("txid.%s %s/v%d %.8f has been spent\n",utxo->coin,bits256_str(str,utxo->payment.txid),utxo->payment.vout,dstr(utxo->payment.value)); LP_spentnotify(utxo,0); } else if ( LP_txvalue(0,utxo->coin,u.txid,u.vout) == 0 ) { printf("txid2.%s %s/v%d %.8f has been spent\n",utxo->coin,bits256_str(str,u.txid),u.vout,dstr(u.value)); LP_spentnotify(utxo,1); } } } int32_t LP_peer_utxosquery(struct LP_peerinfo *mypeer,uint16_t myport,int32_t pubsock,struct LP_peerinfo *peer,uint32_t now,int32_t interval,int32_t maxentries) { int32_t lastn,n = -1; if ( peer->lastutxos < now-interval ) { //lastn = peer->numutxos - mypeer->numutxos + LP_PROPAGATION_SLACK; //if ( lastn < LP_PROPAGATION_SLACK * 2 ) lastn = LP_PROPAGATION_SLACK * 2; if ( mypeer == 0 || strcmp(peer->ipaddr,mypeer->ipaddr) != 0 ) { peer->lastutxos = now; //printf("query utxos from %s\n",peer->ipaddr); n = LP_utxosquery(mypeer,pubsock,peer->ipaddr,peer->port,"",lastn,mypeer != 0 ? mypeer->ipaddr : "127.0.0.1",myport,maxentries); } } //else printf("LP_peer_utxosquery skip.(%s) %u\n",peer->ipaddr,peer->lastutxos); return(n); } bestitem = LP_quotejson(qp); if ( LP_pricevalid(price) > 0 ) { if ( price <= maxprice ) { LP_query(ctx,myipaddr,mypubsock,"connect",qp); //price = LP_pricecache(qp,qp->srccoin,qp->destcoin,qp->txid,qp->vout); LP_requestinit(&qp->R,qp->srchash,qp->desthash,qp->srccoin,qp->satoshis-2*qp->txfee,qp->destcoin,qp->destsatoshis-2*qp->desttxfee,qp->timestamp,qp->quotetime,DEXselector); while ( time(NULL) < expiration ) { if ( aliceutxo->S.swap != 0 ) break; sleep(3); } jaddnum(bestitem,"quotedprice",price); jaddnum(bestitem,"maxprice",maxprice); if ( (swap= aliceutxo->S.swap) == 0 ) { if ( (pubp= LP_pubkeyadd(qp->srchash)) != 0 ) pubp->numerrors++; jaddstr(bestitem,"status","couldnt establish connection"); } else { jaddstr(bestitem,"status","connected"); jaddnum(bestitem,"requestid",swap->I.req.requestid); jaddnum(bestitem,"quoteid",swap->I.req.quoteid); printf("Alice r.%u qp->%u\n",swap->I.req.requestid,swap->I.req.quoteid); } } else { jaddnum(bestitem,"quotedprice",price); jaddnum(bestitem,"maxprice",maxprice); jaddstr(bestitem,"status","too expensive"); } } else { printf("invalid price %.8f\n",price); jaddnum(bestitem,"maxprice",maxprice); jaddstr(bestitem,"status","no response to request"); } uint32_t LP_allocated(bits256 txid,int32_t vout) { struct LP_utxoinfo *utxo; if ( (utxo= _LP_utxofind(0,txid,vout)) != 0 && LP_isavailable(utxo) == 0 ) { //char str[65]; printf("%s/v%d not available\n",bits256_str(str,txid),vout); return(utxo); } if ( (utxo= _LP_utxo2find(0,txid,vout)) != 0 && LP_isavailable(utxo) == 0 ) { //char str[65]; printf("%s/v%d not available2\n",bits256_str(str,txid),vout); return(utxo); } if ( (utxo= _LP_utxofind(1,txid,vout)) != 0 && LP_isavailable(utxo) == 0 ) { //char str[65]; printf("%s/v%d not available\n",bits256_str(str,txid),vout); return(utxo); } if ( (utxo= _LP_utxo2find(1,txid,vout)) != 0 && LP_isavailable(utxo) == 0 ) { //char str[65]; printf("%s/v%d not available2\n",bits256_str(str,txid),vout); return(utxo); } return(0); } int32_t LP_isavailable(struct LP_utxoinfo *utxo) { if ( time(NULL) > utxo->T.swappending ) utxo->T.swappending = 0; if ( utxo != 0 && utxo->T.swappending == 0 && utxo->S.swap == 0 ) return(1); else return(0); } int32_t LP_utxoaddptrs(struct LP_utxoinfo *ptrs[],int32_t n,struct LP_utxoinfo *utxo) { int32_t i; for (i=0; ipayment.txid,refutxo->payment.vout)) != 0 && utxo != refutxo ) n = LP_utxoaddptrs(ptrs,n,utxo); if ( (utxo= _LP_utxo2find(iambob,refutxo->payment.txid,refutxo->payment.vout)) != 0 && utxo != refutxo ) n = LP_utxoaddptrs(ptrs,n,utxo); u = (refutxo->iambob != 0) ? refutxo->deposit : refutxo->fee; if ( (utxo= _LP_utxofind(iambob,u.txid,u.vout)) != 0 && utxo != refutxo ) n = LP_utxoaddptrs(ptrs,n,utxo); if ( (utxo= _LP_utxo2find(iambob,u.txid,u.vout)) != 0 && utxo != refutxo ) n = LP_utxoaddptrs(ptrs,n,utxo); } portable_mutex_unlock(&LP_utxomutex); if ( 0 && n > 0 ) printf("LP_utxocollisions n.%d\n",n); return(n); } int32_t _LP_availableset(struct LP_utxoinfo *utxo) { int32_t flag = 0; if ( utxo != 0 ) { if ( bits256_nonz(utxo->S.otherpubkey) != 0 ) flag = 1, memset(&utxo->S.otherpubkey,0,sizeof(utxo->S.otherpubkey)); if ( utxo->S.swap != 0 ) flag = 1, utxo->S.swap = 0; if ( utxo->T.swappending != 0 ) flag = 1, utxo->T.swappending = 0; return(flag); } return(0); } void _LP_unavailableset(struct LP_utxoinfo *utxo,bits256 otherpubkey) { if ( utxo != 0 ) { utxo->T.swappending = (uint32_t)(time(NULL) + LP_RESERVETIME); utxo->S.otherpubkey = otherpubkey; } } void LP_unavailableset(struct LP_utxoinfo *utxo,bits256 otherpubkey) { struct LP_utxoinfo *ptrs[8]; int32_t i,n; struct _LP_utxoinfo u; memset(ptrs,0,sizeof(ptrs)); if ( (n= LP_utxocollisions(ptrs,utxo)) > 0 ) { for (i=0; iiambob != 0) ? utxo->deposit : utxo->fee; char str[65],str2[65]; printf("UTXO.[%d] RESERVED %s/v%d %s/v%d collisions.%d\n",utxo->iambob,bits256_str(str,utxo->payment.txid),utxo->payment.vout,bits256_str(str2,u.txid),u.vout,n); _LP_unavailableset(utxo,otherpubkey); } void LP_availableset(struct LP_utxoinfo *utxo) { struct LP_utxoinfo *ptrs[8]; int32_t i,n,count = 0; struct _LP_utxoinfo u; if ( utxo != 0 ) { memset(ptrs,0,sizeof(ptrs)); if ( (n= LP_utxocollisions(ptrs,utxo)) > 0 ) { for (i=0; i 0 ) { u = (utxo->iambob != 0) ? utxo->deposit : utxo->fee; char str[65],str2[65]; printf("UTXO.[%d] AVAIL %s/v%d %s/v%d collisions.%d\n",utxo->iambob,bits256_str(str,utxo->payment.txid),utxo->payment.vout,bits256_str(str2,u.txid),u.vout,n); } } } cJSON *LP_inventoryjson(cJSON *item,struct LP_utxoinfo *utxo) { struct _LP_utxoinfo u; //jaddstr(item,"method","oldutxo"); if ( utxo == 0 ) return(item); if ( utxo->gui[0] != 0 ) jaddstr(item,"gui",utxo->gui); jaddstr(item,"coin",utxo->coin); //jaddnum(item,"now",time(NULL)); jaddnum(item,"iambob",utxo->iambob); jaddstr(item,"address",utxo->coinaddr); jaddbits256(item,"txid",utxo->payment.txid); jaddnum(item,"vout",utxo->payment.vout); jadd64bits(item,"value",utxo->payment.value); jadd64bits(item,"satoshis",utxo->S.satoshis); u = (utxo->iambob != 0) ? utxo->deposit : utxo->fee; if ( bits256_nonz(u.txid) != 0 ) { jaddbits256(item,"txid2",u.txid); jaddnum(item,"vout2",u.vout); jadd64bits(item,"value2",u.value); } if ( utxo->T.swappending != 0 ) jaddnum(item,"pending",utxo->T.swappending); if ( utxo->iambob != 0 ) { jaddbits256(item,"srchash",utxo->pubkey);//LP_mypub25519); if ( bits256_nonz(utxo->S.otherpubkey) != 0 ) jaddbits256(item,"desthash",utxo->S.otherpubkey); } else { jaddbits256(item,"desthash",utxo->pubkey);//LP_mypub25519); if ( bits256_nonz(utxo->S.otherpubkey) != 0 ) jaddbits256(item,"srchash",utxo->S.otherpubkey); } //if ( utxo->S.swap != 0 ) // jaddstr(item,"swap","in progress"); if ( utxo->T.spentflag != 0 ) jaddnum(item,"spent",utxo->T.spentflag); jaddnum(item,"session",utxo->T.sessionid); return(item); } cJSON *LP_utxojson(struct LP_utxoinfo *utxo) { cJSON *item = cJSON_CreateObject(); item = LP_inventoryjson(item,utxo); jaddbits256(item,"pubkey",utxo->pubkey); //jaddnum(item,"profit",utxo->S.profitmargin); jaddstr(item,"base",utxo->coin); //jaddstr(item,"script",utxo->spendscript); return(item); } struct LP_utxoinfo *LP_utxo_bestfit(char *symbol,uint64_t destsatoshis) { uint64_t srcvalue,srcvalue2; struct LP_utxoinfo *utxo,*tmp,*bestutxo = 0; int32_t bestsize,iambob = 0; if ( symbol == 0 || destsatoshis == 0 ) { printf("LP_utxo_bestfit error symbol.%p %.8f\n",symbol,dstr(destsatoshis)); return(0); } HASH_ITER(hh,G.LP_utxoinfos[iambob],utxo,tmp) { if ( strcmp(symbol,utxo->coin) != 0 ) continue; if ( LP_isavailable(utxo) > 0 && LP_ismine(utxo) > 0 ) { //printf("(%.8f %.8f %.8f)\n",dstr(utxo->payment.value),dstr(utxo->fee.value),dstr(utxo->S.satoshis)); //char str[65]; printf("s%u %d [%.8f vs %.8f] check %s.%s avail.%d ismine.%d >= %d\n",utxo->T.spentflag,LP_iseligible(&srcvalue,&srcvalue2,utxo->iambob,symbol,utxo->payment.txid,utxo->payment.vout,utxo->S.satoshis,utxo->fee.txid,utxo->fee.vout),dstr(destsatoshis),dstr(utxo->S.satoshis),utxo->coin,bits256_str(str,utxo->payment.txid),LP_isavailable(utxo) > 0,LP_ismine(utxo) > 0,utxo->S.satoshis >= destsatoshis); bestsize = 0; if ( bestutxo == 0 ) { if ( utxo->S.satoshis > destsatoshis/LP_MINCLIENTVOL ) bestsize = 1; } else { if ( bestutxo->S.satoshis < destsatoshis ) { if ( utxo->S.satoshis > destsatoshis ) bestsize = 1; else if ( utxo->S.satoshis > bestutxo->S.satoshis ) bestsize = 1; } else { if ( utxo->S.satoshis > destsatoshis && utxo->S.satoshis < bestutxo->S.satoshis ) bestsize = 1; } } if ( bestsize > 0 ) { //printf("bestsize.%d %.8f %.8f -> %.8f\n",bestsize,dstr(utxo->payment.value),dstr(utxo->fee.value),dstr(utxo->S.satoshis)); if ( LP_iseligible(&srcvalue,&srcvalue2,utxo->iambob,symbol,utxo->payment.txid,utxo->payment.vout,utxo->S.satoshis,utxo->fee.txid,utxo->fee.vout) == 0 ) { //if ( utxo->T.spentflag == 0 ) // utxo->T.spentflag = (uint32_t)time(NULL); continue; } bestutxo = utxo; } //else printf("skip alice utxo %.8f vs dest %.8f, bestsize.%d %p\n",dstr(utxo->S.satoshis),dstr(destsatoshis),bestsize,bestutxo); } } return(bestutxo); } void LP_spentnotify(struct LP_utxoinfo *utxo,int32_t selector) { if ( utxo == 0 ) return; utxo->T.spentflag = (uint32_t)time(NULL); } struct LP_utxoinfo *LP_utxofinds(int32_t iambob,bits256 txid,int32_t vout,bits256 txid2,int32_t vout2) { struct LP_utxoinfo *utxo; if ( (utxo= LP_utxofind(iambob,txid,vout)) != 0 || (utxo= LP_utxofind(iambob,txid2,vout2)) != 0 || (utxo= LP_utxo2find(iambob,txid,vout)) != 0 || (utxo= LP_utxo2find(iambob,txid2,vout2)) != 0 ) return(utxo); else return(0); } struct LP_utxoinfo *LP_utxoadd(int32_t iambob,char *symbol,bits256 txid,int32_t vout,int64_t value,bits256 txid2,int32_t vout2,int64_t value2,char *coinaddr,bits256 pubkey,char *gui,uint32_t sessionid,uint64_t satoshis) { uint64_t val,val2=0,txfee; int32_t spendvini,numconfirms,selector; bits256 spendtxid; struct iguana_info *coin; struct _LP_utxoinfo u; struct LP_utxoinfo *utxo = 0; if ( symbol == 0 || symbol[0] == 0 || coinaddr == 0 || coinaddr[0] == 0 || bits256_nonz(txid) == 0 || bits256_nonz(txid2) == 0 || vout < 0 || vout2 < 0 || value <= 0 || value2 <= 0 )//|| sessionid == 0 ) { char str[65],str2[65]; printf("REJECT (%s) iambob.%d %s utxoadd.(%.8f %.8f) %s/v%d %s/v%d\n",coinaddr,iambob,symbol,dstr(value),dstr(value2),bits256_str(str,txid),vout,bits256_str(str2,txid2),vout2); printf("session.%u addutxo %d %d %d %d %d %d %d %d\n",sessionid,symbol == 0,coinaddr == 0,bits256_nonz(txid) == 0,bits256_nonz(txid2) == 0,vout < 0,vout2 < 0,value <= 0,value2 <= 0); return(0); } if ( (coin= LP_coinfind(symbol)) == 0 || (IAMLP == 0 && coin->inactive != 0) ) { //printf("LP_utxoadd reject inactive %s\n",symbol); return(0); } txfee = LP_txfeecalc(coin,0,0); char str[65],str2[65],dispflag = 0;//(iambob == 0); if ( iambob == 0 && bits256_cmp(pubkey,G.LP_mypub25519) != 0 ) { printf("trying to add Alice utxo when not mine? %s/v%d\n",bits256_str(str,txid),vout); return(0); } if ( coin->inactive == 0 ) { if ( LP_iseligible(&val,&val2,iambob,symbol,txid,vout,satoshis,txid2,vout2) <= 0 ) { static uint32_t counter; if ( counter++ < 3 ) printf("iambob.%d utxoadd %s inactive.%u got ineligible txid value %.8f:%.8f, value2 %.8f:%.8f, tmpsatoshis %.8f\n",iambob,symbol,coin->inactive,dstr(value),dstr(val),dstr(value2),dstr(val2),dstr(satoshis)); return(0); } if ( (numconfirms= LP_numconfirms(symbol,coinaddr,txid,vout,0)) <= 0 ) { printf("LP_utxoadd reject numconfirms.%d %s.%s\n",numconfirms,symbol,bits256_str(str,txid)); return(0); } if ( (numconfirms= LP_numconfirms(symbol,coinaddr,txid2,vout2,0)) <= 0 ) { printf("LP_utxoadd reject2 numconfirms.%d %s %s/v%d\n",numconfirms,symbol,bits256_str(str,txid2),vout2); return(0); } } else { val = value; val2 = value2; } dispflag = 0; if ( dispflag != 0 ) printf("%.8f %.8f %s iambob.%d %s utxoadd.(%.8f %.8f) %s %s\n",dstr(val),dstr(val2),coinaddr,iambob,symbol,dstr(value),dstr(value2),bits256_str(str,txid),bits256_str(str2,txid2)); dispflag = 1; if ( (utxo= LP_utxofinds(iambob,txid,vout,txid2,vout2)) != 0 ) { if ( 0 && LP_ismine(utxo) == 0 ) { char str2[65],str3[65]; printf("iambob.%d %s %s utxoadd.(%.8f %.8f) %s %s\n",iambob,bits256_str(str3,pubkey),symbol,dstr(value),dstr(value2),bits256_str(str,txid),bits256_str(str2,txid2)); printf("duplicate %.8f %.8f %.8f vs utxo.(%.8f %.8f %.8f)\n",dstr(value),dstr(value2),dstr(satoshis),dstr(utxo->payment.value),dstr(utxo->deposit.value),dstr(utxo->S.satoshis)); } u = (utxo->iambob != 0) ? utxo->deposit : utxo->fee; if ( bits256_cmp(txid,utxo->payment.txid) != 0 || bits256_cmp(txid2,u.txid) != 0 || vout != utxo->payment.vout || value != utxo->payment.value || satoshis != utxo->S.satoshis || vout2 != u.vout || value2 != u.value || strcmp(symbol,utxo->coin) != 0 || strcmp(coinaddr,utxo->coinaddr) != 0 || bits256_cmp(pubkey,utxo->pubkey) != 0 ) { utxo->T.errors++; char str[65],str2[65],str3[65],str4[65],str5[65],str6[65]; if ( utxo->T.spentflag != 0 || LP_txvalue(0,utxo->coin,utxo->payment.txid,utxo->payment.vout) < utxo->payment.value || LP_txvalue(0,utxo->coin,u.txid,u.vout) < u.value ) { //if ( utxo->T.spentflag == 0 ) // utxo->T.spentflag = (uint32_t)time(NULL); printf("original utxo pair not valid\n"); if ( dispflag != 0 ) printf("error on subsequent utxo iambob.%d %.8f %.8f add.(%s %s) when.(%s %s) %d %d %d %d %d %d %d %d %d %d pubkeys.(%s vs %s)\n",iambob,dstr(val),dstr(val2),bits256_str(str,txid),bits256_str(str2,txid2),bits256_str(str3,utxo->payment.txid),bits256_str(str4,utxo->deposit.txid),bits256_cmp(txid,utxo->payment.txid) != 0,bits256_cmp(txid2,u.txid) != 0,vout != utxo->payment.vout,satoshis != utxo->S.satoshis,vout2 != u.vout,value2 != u.value,strcmp(symbol,utxo->coin) != 0,strcmp(coinaddr,utxo->coinaddr) != 0,bits256_cmp(pubkey,utxo->pubkey) != 0,value != utxo->payment.value,bits256_str(str5,pubkey),bits256_str(str6,utxo->pubkey)); utxo = 0; } } if ( utxo != 0 ) { if ( utxo->T.sessionid == 0 ) utxo->T.sessionid = sessionid; //else if ( profitmargin > SMALLVAL ) // utxo->S.profitmargin = profitmargin; utxo->T.lasttime = (uint32_t)time(NULL); //printf("return existing utxo[%d] %s %s\n",iambob,bits256_str(str,utxo->payment.txid),bits256_str(str2,iambob != 0 ? utxo->deposit.txid : utxo->fee.txid)); return(utxo); } } utxo = calloc(1,sizeof(*utxo)); //utxo->S.profitmargin = profitmargin; utxo->pubkey = pubkey; safecopy(utxo->gui,gui,sizeof(utxo->gui)); safecopy(utxo->coin,symbol,sizeof(utxo->coin)); safecopy(utxo->coinaddr,coinaddr,sizeof(utxo->coinaddr)); //safecopy(utxo->spendscript,spendscript,sizeof(utxo->spendscript)); utxo->payment.txid = txid; utxo->payment.vout = vout; utxo->payment.value = value; utxo->S.satoshis = satoshis; if ( (utxo->iambob= iambob) != 0 ) { utxo->deposit.txid = txid2; utxo->deposit.vout = vout2; utxo->deposit.value = value2; } else { utxo->fee.txid = txid2; utxo->fee.vout = vout2; utxo->fee.value = value2; } LP_utxosetkey(utxo->key,txid,vout); LP_utxosetkey(utxo->key2,txid2,vout2); if ( LP_ismine(utxo) > 0 ) utxo->T.sessionid = G.LP_sessionid; else utxo->T.sessionid = sessionid; if ( coin->inactive == 0 && (selector= LP_mempool_vinscan(&spendtxid,&spendvini,symbol,coinaddr,txid,vout,txid2,vout2)) >= 0 ) { printf("utxoadd selector.%d spent in mempool %s vini.%d",selector,bits256_str(str,spendtxid),spendvini); utxo->T.spentflag = (uint32_t)time(NULL); } //printf(" %s %.8f %.8f %p addutxo.%d (%s %s) session.%u iambob.%d <<<<<<<<<<<<<<< %.8f\n",symbol,dstr(value),dstr(value2),utxo,LP_ismine(utxo) > 0,bits256_str(str,utxo->payment.txid),bits256_str(str2,iambob != 0 ? utxo->deposit.txid : utxo->fee.txid),utxo->T.sessionid,iambob,dstr(satoshis)); portable_mutex_lock(&LP_utxomutex); HASH_ADD_KEYPTR(hh,G.LP_utxoinfos[iambob],utxo->key,sizeof(utxo->key),utxo); if ( _LP_utxo2find(iambob,txid2,vout2) == 0 ) HASH_ADD_KEYPTR(hh2,G.LP_utxoinfos2[iambob],utxo->key2,sizeof(utxo->key2),utxo); portable_mutex_unlock(&LP_utxomutex); if ( iambob != 0 ) { if ( LP_ismine(utxo) > 0 ) { //LP_utxo_clientpublish(utxo); if ( LP_mypeer != 0 ) utxo->T.lasttime = (uint32_t)time(NULL); } } return(utxo); } int32_t _LP_utxos_remove(bits256 txid,int32_t vout) { struct LP_utxoinfo *utxo,*utxo2; int32_t retval = 0,iambob = 1; utxo = utxo2 = 0; if ( (utxo= _LP_utxofind(iambob,txid,vout)) != 0 ) { if ( LP_isavailable(utxo) == 0 ) retval = -1; else { if ( (utxo2= _LP_utxo2find(iambob,txid,vout)) != 0 ) { if ( LP_isavailable(utxo) == 0 ) retval = -1; else { _LP_utxo_delete(iambob,utxo); _LP_utxo2_delete(iambob,utxo2); } } } } else if ( (utxo2= _LP_utxo2find(iambob,txid,vout)) != 0 ) { if ( LP_isavailable(utxo2) == 0 ) retval = -1; else _LP_utxo2_delete(iambob,utxo2); } return(retval); } int32_t LP_utxos_remove(bits256 txid,int32_t vout) { int32_t retval; portable_mutex_lock(&LP_utxomutex); retval = _LP_utxos_remove(txid,vout); portable_mutex_unlock(&LP_utxomutex); return(retval); } cJSON *LP_inventory(char *symbol) { struct LP_utxoinfo *utxo,*tmp; struct _LP_utxoinfo u; char *myipaddr; cJSON *array; uint64_t val,val2; int32_t iambob = 0; struct iguana_info *coin; array = cJSON_CreateArray(); if ( LP_mypeer != 0 ) myipaddr = LP_mypeer->ipaddr; else myipaddr = "127.0.0.1"; if ( (coin= LP_coinfind(symbol)) != 0 ) LP_listunspent_both(symbol,coin->smartaddr,0); HASH_ITER(hh,G.LP_utxoinfos[iambob],utxo,tmp) { char str[65]; //printf("iambob.%d iterate %s\n",iambob,bits256_str(str,LP_mypub25519)); if ( LP_isunspent(utxo) != 0 && strcmp(symbol,utxo->coin) == 0 && utxo->iambob == iambob && LP_ismine(utxo) > 0 ) { u = (iambob != 0) ? utxo->deposit : utxo->fee; if ( LP_iseligible(&val,&val2,iambob,utxo->coin,utxo->payment.txid,utxo->payment.vout,utxo->S.satoshis,u.txid,u.vout) == 0 ) { //if ( utxo->T.spentflag == 0 ) // utxo->T.spentflag = (uint32_t)time(NULL); //printf("%s %s ineligible %.8f %.8f\n",utxo->coin,bits256_str(str,u.txid),dstr(val),dstr(val2)); continue; } //if ( iambob != 0 ) // LP_utxo_clientpublish(utxo); jaddi(array,LP_inventoryjson(cJSON_CreateObject(),utxo)); } else if ( 0 && LP_ismine(utxo) > 0 && strcmp(symbol,utxo->coin) == 0 ) printf("skip %s %s %d %d %d %d\n",utxo->coin,bits256_str(str,utxo->payment.txid),LP_isunspent(utxo) != 0,strcmp(symbol,utxo->coin) == 0,utxo->iambob == iambob,LP_ismine(utxo) > 0); } return(array); } /*while ( time(NULL) < expiration ) { if ( (price= LP_pricecache(qp,qp->srccoin,qp->destcoin,qp->txid,qp->vout)) > SMALLVAL ) { printf("break out of price %.8f %s/%s\n",price,qp->srccoin,qp->destcoin); break; } sleep(1); }*/ //struct LP_pubkey_info *pubp,*ptmp; //cJSON *retjson; struct iguana_info *coin,*tmp; /*HASH_ITER(hh,LP_coins,coin,tmp) // firstrefht,firstscanht,lastscanht { if ( coin->electrum != 0 && time(NULL) > coin->lastunspent+30 ) { //printf("call electrum listunspent.%s\n",coin->symbol); if ( (retjson= electrum_address_listunspent(coin->symbol,coin->electrum,&retjson,coin->smartaddr,2)) != 0 ) free_json(retjson); coin->lastunspent = (uint32_t)time(NULL); } }*/ /*HASH_ITER(hh,LP_pubkeyinfos,pubp,ptmp) { pubp->dynamictrust = LP_dynamictrust(pubp->pubkey,0); }*/ /*void LP_prices_parse(struct LP_peerinfo *peer,cJSON *obj) { struct LP_pubkey_info *pubp; struct LP_priceinfo *basepp,*relpp; uint32_t timestamp; bits256 pubkey; cJSON *asks,*item; uint8_t rmd160[20]; int32_t i,n,relid,mismatch; char *base,*rel,*hexstr; double askprice; uint32_t now; now = (uint32_t)time(NULL); pubkey = jbits256(obj,"pubkey"); if ( bits256_nonz(pubkey) != 0 && (pubp= LP_pubkeyadd(pubkey)) != 0 ) { if ( (hexstr= jstr(obj,"rmd160")) != 0 && strlen(hexstr) == 2*sizeof(rmd160) ) decode_hex(rmd160,sizeof(rmd160),hexstr); if ( memcmp(pubp->rmd160,rmd160,sizeof(rmd160)) != 0 ) mismatch = 1; else mismatch = 0; if ( bits256_cmp(pubkey,G.LP_mypub25519) == 0 && mismatch == 0 ) peer->needping = 0; LP_pubkey_sigcheck(pubp,obj); timestamp = juint(obj,"timestamp"); if ( timestamp > now ) timestamp = now; if ( timestamp >= pubp->timestamp && (asks= jarray(&n,obj,"asks")) != 0 ) { for (i=0; i 0 ) { if ( (basepp= LP_priceinfoptr(&relid,base,rel)) != 0 ) { //char str[65]; printf("gotprice %s %s/%s (%d/%d) %.8f\n",bits256_str(str,pubkey),base,rel,basepp->ind,relid,askprice); pubp->matrix[basepp->ind][relid] = askprice; //pubp->timestamps[basepp->ind][relid] = timestamp; if ( (relpp= LP_priceinfofind(rel)) != 0 ) { dxblend(&basepp->relvals[relpp->ind],askprice,0.9); dxblend(&relpp->relvals[basepp->ind],1. / askprice,0.9); } } } } } } } void LP_peer_pricesquery(struct LP_peerinfo *peer) { char *retstr; cJSON *array; int32_t i,n; if ( strcmp(peer->ipaddr,LP_myipaddr) == 0 ) return; peer->needping = (uint32_t)time(NULL); if ( (retstr= issue_LP_getprices(peer->ipaddr,peer->port)) != 0 ) { if ( (array= cJSON_Parse(retstr)) != 0 ) { if ( is_cJSON_Array(array) && (n= cJSON_GetArraySize(array)) > 0 ) { for (i=0; ineedping != 0 ) { //printf("%s needs ping\n",peer->ipaddr); } }*/ if ( aliceutxo->S.swap == 0 ) LP_availableset(aliceutxo); return(jprint(bestitem,0)); } if ( 0 && (retstr= issue_LP_listunspent(peer->ipaddr,peer->port,coin->symbol,"")) != 0 ) { if ( (array2= cJSON_Parse(retstr)) != 0 ) { if ( (m= cJSON_GetArraySize(array2)) > 0 ) { for (j=0; j %.8f n.%d\n",coinaddr,dstr(metric>>16),(uint16_t)metric); if ( (ap= LP_addressfind(coin,coinaddr)) == 0 || _LP_unspents_metric(ap->total,ap->n) != metric ) { if ( ap == 0 || ap->n < (metric & 0xffff) ) { if ( (retstr2= issue_LP_listunspent(peer->ipaddr,peer->port,coin->symbol,coinaddr)) != 0 ) { if ( (array3= cJSON_Parse(retstr2)) != 0 ) { LP_unspents_array(coin,coinaddr,array3); //printf("pulled.(%s)\n",retstr2); free_json(array3); } free(retstr2); } } //else printf("wait for %s to pull %d vs %d\n",peer->ipaddr,ap!=0?ap->n:-1,(uint16_t)metric); } } } } free_json(array2); } //printf("processed.(%s)\n",retstr); free(retstr); } /*if ( time(NULL) > coin->lastmonitor+60 ) { //portable_mutex_lock(&coin->addrmutex); HASH_ITER(hh,coin->addresses,ap,atmp) { if ( coin->electrum == 0 ) { LP_listunspent_issue(coin->symbol,ap->coinaddr); DL_FOREACH_SAFE(ap->utxos,up,utmp) { if ( up->spendheight <= 0 ) { if ( LP_txvalue(0,coin->symbol,up->U.txid,up->U.vout) == 0 ) up->spendheight = 1; } } } } //portable_mutex_unlock(&coin->addrmutex); coin->lastmonitor = (uint32_t)time(NULL); }*/ /*cJSON *LP_tradecandidates(char *base) { struct LP_peerinfo *peer,*tmp; struct LP_quoteinfo Q; char *utxostr,coinstr[16]; cJSON *array,*retarray=0,*item; int32_t i,n,totaladded,added; totaladded = 0; HASH_ITER(hh,LP_peerinfos,peer,tmp) { printf("%s:%u %s\n",peer->ipaddr,peer->port,base); n = added = 0; if ( (utxostr= issue_LP_clientgetutxos(peer->ipaddr,peer->port,base,100)) != 0 ) { printf("%s:%u %s %s\n",peer->ipaddr,peer->port,base,utxostr); if ( (array= cJSON_Parse(utxostr)) != 0 ) { if ( is_cJSON_Array(array) != 0 && (n= cJSON_GetArraySize(array)) > 0 ) { retarray = cJSON_CreateArray(); for (i=0; i 0 ) { memset(&Q,0,sizeof(Q)); for (i=0; i 0 ) { printf("iterate through all locally generated quotes and update, or change to price feed\n"); // jl777: iterated Q's if ( strcmp(utxo->coin,"KMD") == 0 ) LP_priceping(pubsock,utxo,"BTC",profitmargin); else LP_priceping(pubsock,utxo,"KMD",profitmargin); }*/ /*if ( LP_txvalue(destaddr,symbol,searchtxid,searchvout) > 0 ) return(0); if ( (txobj= LP_gettx(symbol,searchtxid)) == 0 ) return(0); hash = jbits256(txobj,"blockhash"); free_json(txobj); if ( bits256_nonz(hash) == 0 ) return(0); if ( (blockjson= LP_getblock(symbol,hash)) == 0 ) return(0); loadheight = jint(blockjson,"height"); free_json(blockjson); if ( loadheight <= 0 ) return(0); while ( errs == 0 && *indp < 0 ) { //printf("search %s ht.%d\n",symbol,loadheight); if ( (blockjson= LP_blockjson(&h,symbol,0,loadheight)) != 0 && h == loadheight ) { if ( (txids= jarray(&numtxids,blockjson,"tx")) != 0 ) { for (i=0; i= 0 ) break; } } free_json(blockjson); } else errs++; loadheight++; } char str[65]; printf("reached %s ht.%d %s/v%d\n",symbol,loadheight,bits256_str(str,*spendtxidp),*indp); if ( bits256_nonz(*spendtxidp) != 0 && *indp >= 0 ) return(loadheight); else return(0);*/ /*if ( is_cJSON_Array(array) != 0 && (n= cJSON_GetArraySize(array)) > 0 ) { for (i=0; i= 0 ) return(selector); } }*/ /*int32_t LP_orderbookfind(struct LP_orderbookentry **array,int32_t num,bits256 txid,int32_t vout) { int32_t i; for (i=0; ivout == vout && bits256_cmp(array[i]->txid,txid) == 0) || (array[i]->vout2 == vout && bits256_cmp(array[i]->txid2,txid) == 0) ) return(i); return(-1); }*/ //char str[65],str2[65]; printf("check utxo.%s/v%d from %s\n",bits256_str(str,utxo->payment.txid),utxo->payment.vout,bits256_str(str2,utxo->pubkey)); //if ( strcmp(base,utxo->coin) == 0 && LP_isavailable(utxo) > 0 && pubp != 0 && (price= pubp->matrix[baseid][relid]) > SMALLVAL ) //if ( polarity > 0 ) // minsatoshis = utxo->S.satoshis; //else minsatoshis = utxo->S.satoshis * price; /*if ( LP_orderbookfind(*arrayp,cachednum,utxo->payment.txid,utxo->payment.vout) < 0 ) { if ( LP_iseligible(&val,&val2,utxo->iambob,utxo->coin,utxo->payment.txid,utxo->payment.vout,utxo->S.satoshis,utxo->deposit.txid,utxo->deposit.vout) == 0 ) continue; if ( polarity > 0 ) basesatoshis = utxo->S.satoshis; else basesatoshis = utxo->S.satoshis * price; //char str[65]; printf("found utxo not in orderbook %s/v%d %.8f %.8f\n",bits256_str(str,utxo->payment.txid),utxo->payment.vout,dstr(basesatoshis),polarity > 0 ? price : 1./price); if ( (op= LP_orderbookentry(base,rel,utxo->payment.txid,utxo->payment.vout,utxo->deposit.txid,utxo->deposit.vout,polarity > 0 ? price : 1./price,basesatoshis,utxo->pubkey,now - pubp->timestamp)) != 0 ) { *arrayp = realloc(*arrayp,sizeof(*(*arrayp)) * (num+1)); (*arrayp)[num++] = op; if ( LP_ismine(utxo) > 0 && utxo->T.lasttime == 0 ) LP_utxo_clientpublish(utxo); } }*/ void LP_price_broadcastloop(void *ctx) { struct LP_priceinfo *basepp,*relpp; double price; int32_t baseind,relind; sleep(30); while ( 1 ) { for (baseind=0; baseindsymbol[0] == 0 ) continue; for (relind=0; relindsymbol[0] == 0 ) continue; if ( basepp != 0 && relpp != 0 && (price= relpp->myprices[basepp->ind]) > SMALLVAL) { //printf("automated price broadcast %s/%s %.8f\n",relpp->symbol,basepp->symbol,price); LP_pricepings(ctx,LP_myipaddr,LP_mypubsock,relpp->symbol,basepp->symbol,price); } } } sleep(LP_ORDERBOOK_DURATION * .9); } } /*if ( expiration != 0 ) { redeemlen = LP_deposit_addr(vinaddr,redeemscript,coin->taddr,coin->p2shtype,expiration,G.LP_pubsecp); if ( strcmp(depositaddr,vinaddr) == 0 ) { claimtime = (uint32_t)time(NULL)-777; if ( claimtime <= expiration ) { printf("claimtime.%u vs locktime.%u, need to wait %d seconds\n",claimtime,timestamp,(int32_t)timestamp-claimtime); return(clonestr("{\"error\":\"need to wait to claim\"}")); } sum += LP_claimtx(ctx,coin,txids,utxotxid,utxovout,satoshis,vinaddr,claimtime,redeemscript,redeemlen); }*/ /*timestamp = (now / LP_WEEKMULT) * LP_WEEKMULT + LP_WEEKMULT; while ( timestamp > LP_FIRSTWEEKTIME ) { if ( expiration != 0 ) timestamp = expiration; else timestamp -= LP_WEEKMULT; redeemlen = LP_deposit_addr(vinaddr,redeemscript,coin->taddr,coin->p2shtype,timestamp,G.LP_pubsecp); if ( strcmp(depositaddr,vinaddr) == 0 ) { claimtime = (uint32_t)time(NULL)-777; if ( claimtime <= timestamp ) { printf("claimtime.%u vs locktime.%u, need to wait %d seconds\n",claimtime,timestamp,(int32_t)timestamp-claimtime); } else { printf("found %s at timestamp.%u\n",vinaddr,timestamp); memset(zero.bytes,0,sizeof(zero)); if ( (array= LP_listunspent(coin->symbol,vinaddr,zero,zero)) != 0 ) { //printf("unspents.(%s)\n",jprint(array,0)); if ( (n= cJSON_GetArraySize(array)) > 0 ) { for (i=0; i 0 ) { for (i=0; i> 1; if ( cipherlen <= sizeof(cipher) ) { decode_hex(cipher,cipherlen,cipherstr); LP_queuesend(calc_crc32(0,&cipher[2],cipherlen-2),LP_mypubsock,base,rel,cipher,cipherlen); } else retstr = clonestr("{\"error\":\"cipher too big\"}"); } else { memset(zero.bytes,0,sizeof(zero)); //printf("broadcast.(%s)\n",Broadcaststr); LP_reserved_msg(base!=0?base:jstr(argjson,"coin"),rel,zero,jprint(reqjson,0)); } retstr = clonestr("{\"result\":\"success\"}"); } else retstr = clonestr("{\"error\":\"couldnt dereference sendmessage\"}"); } else*/ /*relvol = bot->totalrelvolume * .1; p = LP_pricevol_invert(&v,bot->maxprice,relvol); if ( bot->dispdir > 0 ) { printf("simulated trade buy %s/%s maxprice %.8f volume %.8f, %.8f %s -> %s, price %.8f relvol %.8f\n",bot->base,bot->rel,bot->maxprice,bot->totalrelvolume - bot->relsum,relvol,bot->rel,bot->base,bot->maxprice,relvol); } else { minprice = LP_pricevol_invert(&basevol,bot->maxprice,bot->totalrelvolume - bot->relsum); printf("simulated trade sell %s/%s minprice %.8f volume %.8f, %.8f %s -> %s price %.8f relvol %.8f\n",bot->rel,bot->base,minprice,basevol,v,bot->base,bot->rel,p,relvol); } if ( (LP_rand() % 2) == 0 ) { bot->relsum += relvol; bot->basesum += v; bot->completed++; } else { bot->pendrelsum += relvol; bot->pendbasesum += v; bot->numpending++; } bot->numtrades++; */ #ifdef FROM_JS int32_t sentbytes,sock,peerind,maxind; if ( (maxind= LP_numpeers()) > 0 ) peerind = (LP_rand() % maxind) + 1; else peerind = 1; sock = LP_peerindsock(&peerind); if ( sock >= 0 ) { if ( (sentbytes= nn_send(sock,msg,msglen,0)) != msglen ) printf("LP_send sent %d instead of %d\n",sentbytes,msglen); else printf("sent %d bytes of %d to sock.%d\n",sentbytes,msglen,sock); } else printf("couldnt get valid sock\n"); #else void _LP_queuesend(uint32_t crc32,int32_t sock0,int32_t sock1,uint8_t *msg,int32_t msglen,int32_t needack) { int32_t maxind,peerind = 0; //sentbytes, if ( sock0 >= 0 || sock1 >= 0 ) { /* if ( sock0 >= 0 && LP_sockcheck(sock0) > 0 ) { if ( (sentbytes= nn_send(sock0,msg,msglen,0)) != msglen ) printf("_LP_queuesend0 sent %d instead of %d\n",sentbytes,msglen); else { printf("Q sent %u msglen.%d (%s)\n",crc32,msglen,msg); sock0 = -1; } } if ( sock1 >= 0 && LP_sockcheck(sock1) > 0 ) { if ( (sentbytes= nn_send(sock1,msg,msglen,0)) != msglen ) printf("_LP_queuesend1 sent %d instead of %d\n",sentbytes,msglen); else { printf("Q sent1 %u msglen.%d (%s)\n",crc32,msglen,msg); sock1 = -1; } } if ( sock0 < 0 && sock1 < 0 ) return;*/ } else { if ( (maxind= LP_numpeers()) > 0 ) peerind = (LP_rand() % maxind) + 1; else peerind = 1; sock0 = LP_peerindsock(&peerind); if ( (maxind= LP_numpeers()) > 0 ) peerind = (LP_rand() % maxind) + 1; else peerind = 1; sock1 = LP_peerindsock(&peerind); } if ( sock0 >= 0 ) _LP_sendqueueadd(crc32,sock0,msg,msglen,needack * peerind); if ( sock1 >= 0 ) _LP_sendqueueadd(crc32,sock1,msg,msglen,needack); } if ( 0 && OS_thread_create(malloc(sizeof(pthread_t)),NULL,(void *)LP_price_broadcastloop,(void *)ctx) != 0 ) { printf("error launching LP_swapsloop for port.%u\n",myport); exit(-1); } void LP_queuesend(uint32_t crc32,int32_t pubsock,char *base,char *rel,uint8_t *msg,int32_t msglen) { //struct iguana_info *coin; int32_t flag=0,socks[2]; portable_mutex_lock(&LP_networkmutex); if ( pubsock >= 0 ) { //socks[0] = socks[1] = -1; //if ( rel != 0 && rel[0] != 0 && (coin= LP_coinfind(rel)) != 0 && coin->bussock >= 0 ) // socks[flag++] = coin->bussock; //if ( base != 0 && base[0] != 0 && (coin= LP_coinfind(base)) != 0 && coin->bussock >= 0 ) // socks[flag++] = coin->bussock; //if ( flag == 0 && pubsock >= 0 ) _LP_queuesend(crc32,pubsock,-1,msg,msglen,0); //else _LP_queuesend(socks[0],socks[1],msg,msglen,0); } else _LP_queuesend(crc32,-1,-1,msg,msglen,1); portable_mutex_unlock(&LP_networkmutex); } #ifdef oldway struct LP_utxoinfo *LP_bestutxo(double *ordermatchpricep,int64_t *bestsatoshisp,int64_t *bestdestsatoshisp,struct LP_utxoinfo *autxo,char *base,double maxprice,int32_t duration,uint64_t txfee,uint64_t desttxfee,uint64_t maxdestsatoshis) { int64_t satoshis,destsatoshis; uint64_t val,val2; bits256 txid,pubkey; char *obookstr; cJSON *orderbook,*asks,*item; struct LP_utxoinfo *butxo,*bestutxo = 0; int32_t i,n,j,vout,numasks; double bestmetric=0.,metric,vol,price,qprice,bestprice = 0.; struct LP_pubkeyinfo *pubp; *ordermatchpricep = 0.; *bestsatoshisp = *bestdestsatoshisp = 0; if ( duration <= 0 ) duration = LP_ORDERBOOK_DURATION; if ( maxprice <= 0. || LP_priceinfofind(base) == 0 ) return(0); LP_txfees(&txfee,&desttxfee,base,autxo->coin); if ( (obookstr= LP_orderbook(base,autxo->coin,duration)) != 0 ) { if ( (orderbook= cJSON_Parse(obookstr)) != 0 ) { if ( (asks= jarray(&numasks,orderbook,"asks")) != 0 ) { for (i=0; i 0 && price <= maxprice ) { //price *= 1.0001; //if ( price > maxprice ) // price = maxprice; pubkey = jbits256(item,"pubkey"); if ( bits256_cmp(pubkey,LP_mypub25519) != 0 && (pubp= LP_pubkeyadd(pubkey)) != 0 && pubp->numerrors < LP_MAXPUBKEY_ERRORS ) { if ( bestprice == 0. ) // assumes price ordered asks bestprice = price; printf("item.[%d] %s\n",i,jprint(item,0)); txid = jbits256(item,"txid"); vout = jint(item,"vout"); vol = jdouble(item,"volume"); metric = price / bestprice; printf("maxdest %.8f metric %f vol %f add pings numutxos.%d min %.8f max %.8f\n",dstr(maxdestsatoshis),metric,vol,jint(item,"numutxos"),jdouble(item,"minvolume"),jdouble(item,"maxvolume")); // check utxos > 1 for pubkey, SPV validate recv'ed /*if ( (butxo= LP_utxofind(1,txid,vout)) != 0 && (long long)(vol*SATOSHIDEN) == butxo->S.satoshis && LP_isavailable(butxo) > 0 && LP_ismine(butxo) == 0 && butxo->T.bestflag == 0 ) { printf("got butxo? %p\n",butxo); if ( LP_iseligible(&val,&val2,butxo->iambob,butxo->coin,butxo->payment.txid,butxo->payment.vout,butxo->S.satoshis,butxo->deposit.txid,butxo->deposit.vout) > 0 ) { destsatoshis = ((butxo->S.satoshis - txfee) * price); satoshis = (destsatoshis / price + 0.49) - txfee; if ( satoshis <= 0 ) continue; qprice = (double)destsatoshis / satoshis; n = (int32_t)((double)destsatoshis / desttxfee); if ( n < 10 ) n = 10; else n = 3; for (j=0; jS.satoshis,txfee,autxo->payment.value,maxdestsatoshis,desttxfee)) > price+SMALLVAL ) break; } //printf("j.%d/%d qprice %.8f vs price %.8f best.(%.8f %.8f)\n",j,n,qprice,price,dstr(satoshis),dstr(destsatoshis)); if ( metric < 1.2 && destsatoshis > desttxfee && destsatoshis-desttxfee > (autxo->payment.value / LP_MINCLIENTVOL) && satoshis-txfee > (butxo->S.satoshis / LP_MINVOL) && satoshis <= butxo->payment.value-txfee ) { printf("value %.8f price %.8f/%.8f best %.8f destsatoshis %.8f * metric %.8f -> (%f)\n",dstr(autxo->payment.value),price,bestprice,bestmetric,dstr(destsatoshis),metric,dstr(destsatoshis) * metric * metric * metric); metric = dstr(destsatoshis) * metric * metric * metric; if ( bestmetric == 0. || metric < bestmetric ) { bestutxo = butxo; *ordermatchpricep = price; *bestdestsatoshisp = destsatoshis; *bestsatoshisp = satoshis; bestmetric = metric; printf("set best!\n"); } } // else printf("skip.(%d %d %d %d %d) metric %f destsatoshis %.8f value %.8f destvalue %.8f txfees %.8f %.8f sats %.8f\n",metric < 1.2,destsatoshis > desttxfee,destsatoshis-desttxfee > (autxo->payment.value / LP_MINCLIENTVOL),satoshis-txfee > (butxo->S.satoshis / LP_MINVOL),satoshis < butxo->payment.value-txfee,metric,dstr(destsatoshis),dstr(butxo->S.satoshis),dstr(autxo->payment.value),dstr(txfee),dstr(desttxfee),dstr(satoshis)); } else { printf("ineligible.(%.8f %.8f)\n",price,dstr(butxo->S.satoshis)); //if ( butxo->T.spentflag == 0 ) // butxo->T.spentflag = (uint32_t)time(NULL); } } else { if ( butxo != 0 ) printf("%llu %llu %d %d %d: ",(long long)(vol*SATOSHIDEN),(long long)butxo->S.satoshis,vol*SATOSHIDEN == butxo->S.satoshis,LP_isavailable(butxo) > 0,LP_ismine(butxo) == 0); printf("cant find butxo.%p or value mismatch %.8f != %.8f or bestflag.%d\n",butxo,vol,butxo!=0?dstr(butxo->S.satoshis):0,butxo->T.bestflag); }*/ } else printf("self trading or blacklisted peer\n"); } else { if ( i == 0 ) printf("maxprice %.8f vs %.8f\n",maxprice,price); break; } } if ( bestutxo == 0 ) { int32_t numrestraints; for (i=numrestraints=0; iT.bestflag = 0; //pubp->numerrors = 0; } } } printf("no bob utxo found -> cleared %d restraints\n",numrestraints); } } free_json(orderbook); } free(obookstr); } printf("bestutxo.%p %.8f %.8f\n",bestutxo,*ordermatchpricep,dstr(*bestdestsatoshisp)); if ( bestutxo == 0 || *ordermatchpricep == 0. || *bestdestsatoshisp == 0 ) return(0); bestutxo->T.bestflag = 1; int32_t changed; LP_mypriceset(&changed,autxo->coin,base,1. / *ordermatchpricep); return(bestutxo); } if ( (0) ) { ep = LP_electrum_info(&already,"BTC","88.198.241.196",50001,IGUANA_MAXPACKETSIZE * 10); if ( ep != 0 && OS_thread_create(malloc(sizeof(pthread_t)),NULL,(void *)LP_dedicatedloop,(void *)ep) != 0 ) { printf("error launching LP_dedicatedloop (%s:%u)\n",ep->ipaddr,ep->port); exit(-1); } else printf("launched.(%s:%u)\n",ep->ipaddr,ep->port); electrum_test(); } /*static int _LP_metric_eval(const void *a,const void *b) { #define aptr (*(struct LP_metricinfo **)a) #define bptr (*(struct LP_metricinfo **)b) if ( bptr->metric > aptr->metric ) return(1); else if ( bptr->metric < aptr->metric ) return(-1); return(0); #undef aptr #undef bptr }*/ /*if ( (utxo= LP_utxoadd(1,coin->symbol,up->U.txid,up->U.vout,up->U.value,up2->U.txid,up2->U.vout,up2->U.value,coinaddr,ap->pubkey,G.gui,0,targetval)) != 0 ) { utxo->S.satoshis = targetval; char str[65],str2[65]; printf("butxo.%p targetval %.8f, found val %.8f %s | targetval2 %.8f val2 %.8f %s\n",utxo,dstr(targetval),dstr(up->U.value),bits256_str(str,utxo->payment.txid),dstr(targetval2),dstr(up2->U.value),bits256_str(str2,utxo->deposit.txid)); return(butxo); }*/ /*if ( (sobj= jobj(v,"scriptPubKey")) != 0 ) { if ( (scriptstr= jstr(sobj,"hex")) != 0 ) { printf("amount64 %.8f vout.%d (%s) weeki.%d %.8f (%s)\n",dstr(amount64),vout,jprint(v,0),weeki,dstr(satoshis),scriptstr); len = (int32_t)strlen(scriptstr) >> 1; if ( len <= sizeof(spendscript)/sizeof(*spendscript) ) { decode_hex(spendscript,len,scriptstr); if ( spendscript[11] == 33 ) { pub33 = &spendscript[12]; redeemlen = LP_deposit_addr(p2shaddr,redeemscript,coin->taddr,coin->p2shtype,timestamp,pub33); if ( len == redeemlen && (timestamp % LP_WEEKMULT) == 0 ) { bitcoin_address(coinaddr,coin->taddr,coin->pubtype,pub33,33); printf("%s -> matched %s script t.%u weeki.%d deposit %.8f\n",coinaddr,p2shaddr,timestamp,(timestamp-LP_FIRSTWEEKTIME)/LP_WEEKMULT,dstr(satoshis)); // add to pubp->credits; } } } } }*/ /*portable_mutex_lock(&ep->pendingQ.mutex); if ( ep->pendingQ.list != 0 ) { printf("list %p\n",ep->pendingQ.list); DL_FOREACH_SAFE(ep->pendingQ.list,item,tmp) { printf("item.%p\n",item); if ( item->type == 0xffffffff ) { printf("%p purge %s",item,((struct stritem *)item)->str); DL_DELETE(ep->pendingQ.list,item); free(item); } } } DL_APPEND(ep->pendingQ.list,&sitem->DL); portable_mutex_unlock(&ep->pendingQ.mutex);*/ //printf("%p SENT.(%s) to %s:%u\n",sitem,sitem->str,ep->ipaddr,ep->port); #ifdef oldway struct LP_utxoinfo *LP_ordermatch_iter(struct LP_address_utxo **utxos,int32_t max,double *ordermatchpricep,int64_t *bestsatoshisp,int64_t *bestdestsatoshisp,struct iguana_info *basecoin,char *coinaddr,uint64_t asatoshis,double price,uint64_t txfee,uint64_t desttxfee,bits256 pubkey,char *gui) { uint64_t basesatoshis; struct LP_utxoinfo *bestutxo; basesatoshis = LP_basesatoshis(dstr(asatoshis),price,txfee,desttxfee); //printf("basesatoshis %.8f price %.8f txfee %.8f desttxfee %.8f\n",dstr(basesatoshis),price,dstr(txfee),dstr(desttxfee)); if ( basesatoshis != 0 && (bestutxo= LP_address_utxopair(0,utxos,max,basecoin,coinaddr,txfee,dstr(basesatoshis)*price,price,desttxfee)) != 0 ) { bestutxo->pubkey = pubkey; safecopy(bestutxo->gui,gui,sizeof(bestutxo->gui)); *bestsatoshisp = basesatoshis; *ordermatchpricep = price; *bestdestsatoshisp = asatoshis; return(bestutxo); } return(0); } struct LP_utxoinfo *LP_buyutxo(double *ordermatchpricep,int64_t *bestsatoshisp,int64_t *bestdestsatoshisp,struct LP_utxoinfo *autxo,char *base,double maxprice,int32_t duration,uint64_t txfee,uint64_t desttxfee,char *gui,bits256 *avoids,int32_t numavoids,bits256 destpubkey) { bits256 pubkey; char *obookstr,coinaddr[64]; cJSON *orderbook,*asks,*rawasks,*item; int32_t maxiters,i,j,numasks,max; struct LP_address_utxo **utxos; double price; struct LP_pubkey_info *pubp; uint64_t asatoshis; struct iguana_info *basecoin; struct LP_utxoinfo *bestutxo = 0; maxiters = 100; *ordermatchpricep = 0.; *bestsatoshisp = *bestdestsatoshisp = 0; basecoin = LP_coinfind(base); if ( duration <= 0 ) duration = LP_ORDERBOOK_DURATION; if ( maxprice <= 0. || LP_priceinfofind(base) == 0 || basecoin == 0 ) return(0); if ( basecoin->electrum == 0 ) max = 1000; else max = LP_MAXDESIRED_UTXOS; utxos = calloc(max,sizeof(*utxos)); LP_txfees(&txfee,&desttxfee,base,autxo->coin); printf("LP_buyutxo maxprice %.8f relvol %.8f %s/%s %.8f %.8f\n",maxprice,dstr(autxo->S.satoshis),base,autxo->coin,dstr(txfee),dstr(desttxfee)); if ( (obookstr= LP_orderbook(base,autxo->coin,duration)) != 0 ) { if ( (orderbook= cJSON_Parse(obookstr)) != 0 ) { if ( (rawasks= jarray(&numasks,orderbook,"asks")) != 0 ) { if ( (asks= LP_RTmetrics_sort(base,autxo->coin,rawasks,numasks,maxprice,dstr(autxo->S.satoshis))) == 0 ) asks = rawasks; for (i=0; i maxprice*0.8) // price = price * 0.9 + 0.1 * maxprice; //else price *= 1.005; pubkey = jbits256(item,"pubkey"); if ( bits256_nonz(destpubkey) != 0 && bits256_cmp(destpubkey,pubkey) != 0 ) continue; if ( LP_RTmetrics_blacklisted(pubkey) >= 0 ) continue; //printf("[%d/%d] %s pubcmp %d price %.8f vs maxprice %.8f asatoshis %.8f\n",i,numasks,jprint(item,0),bits256_cmp(pubkey,G.LP_mypub25519),price,maxprice,dstr(autxo->S.satoshis)); if ( LP_pricevalid(price) > 0 && price <= maxprice ) { if ( bits256_nonz(destpubkey) == 0 ) { for (j=0; jtaddr,basecoin->pubtype,pubp->rmd160,sizeof(pubp->rmd160)); asatoshis = autxo->S.satoshis; //LP_listunspent_query(base,coinaddr); for (j=0; jpubkey,gui)) != 0 ) { //printf("j.%d/%d ordermatch %.8f best satoshis %.8f destsatoshis %.8f txfees (%.8f %.8f)\n",j,maxiters,price,dstr(*bestsatoshisp),dstr(*bestdestsatoshisp),dstr(txfee),dstr(desttxfee)); break; } asatoshis = (asatoshis / 64) * 63; } if ( j < maxiters ) break; } else printf("self trading or blacklisted peer\n"); } else { if ( i == 0 ) printf("too expensive maxprice %.8f vs %.8f\n",maxprice,price); break; } } if ( asks != 0 && asks != rawasks ) free_json(asks); } free_json(orderbook); } free(obookstr); } free(utxos); if ( *ordermatchpricep == 0. || *bestdestsatoshisp == 0 ) return(0); int32_t changed; LP_mypriceset(&changed,autxo->coin,base,1. / *ordermatchpricep); return(bestutxo); } #endif #ifdef oldway //LP_RTmetrics_update(base,rel); while ( 1 ) { if ( (bestutxo= LP_buyutxo(&ordermatchprice,&bestsatoshis,&bestdestsatoshis,autxo,base,maxprice,duration,txfee,desttxfee,gui,pubkeys,numpubs,destpubkey)) == 0 || ordermatchprice == 0. || bestdestsatoshis == 0 ) { printf("bestutxo.%p ordermatchprice %.8f bestdestsatoshis %.8f\n",bestutxo,ordermatchprice,dstr(bestdestsatoshis)); return(clonestr("{\"error\":\"cant find ordermatch utxo, need to change relvolume to be closer to available\"}")); } pubkeys[numpubs++] = bestutxo->pubkey; if ( LP_quoteinfoinit(&Q,bestutxo,rel,ordermatchprice,bestsatoshis,bestdestsatoshis) < 0 ) return(clonestr("{\"error\":\"cant set ordermatch quote\"}")); if ( LP_quotedestinfo(&Q,autxo->payment.txid,autxo->payment.vout,autxo->fee.txid,autxo->fee.vout,G.LP_mypub25519,autxo->coinaddr) < 0 ) return(clonestr("{\"error\":\"cant set ordermatch quote info\"}")); maxiters = 200; qprice = 1. / SMALLVAL; for (i=0; i maxprice ) { printf("i.%d maxiters.%d qprice %.8f vs maxprice %.8f, no acceptable quote for this pubkey\n",i,maxiters,dstr(qprice),dstr(maxprice)); if ( bits256_nonz(destpubkey) == 0 ) continue; else return(clonestr("{\"error\":\"cant ordermatch to destpubkey\"}")); } printf("i.%d maxiters.%d qprice %.8f vs maxprice %.8f\n",i,maxiters,dstr(qprice),dstr(maxprice)); return(LP_trade(ctx,myipaddr,mypubsock,&Q,maxprice,timeout,duration,tradeid,destpubkey)); } return(clonestr("{\"error\":\"cant get here\"}")); #endif if ( 0 ) { char *p2sh = "bJVtQF2o8B6sdNjeXupzNw5rnidJUNwPJD",p2shaddr[64]; uint8_t script[512],pub33[33]; uint32_t timestamp; decode_hex(pub33,33,"03fe754763c176e1339a3f62ee6b9484720e17ee4646b65a119e9f6370c7004abc"); for (timestamp=1510934803-3600*24; timestamp<1510934803+3600*24; timestamp++) { LP_deposit_addr(p2shaddr,script,0,85,timestamp,pub33); if ( strcmp(p2shaddr,p2sh) == 0 ) { printf("matched timestamp.%u\n",timestamp); break; } else printf("%s ",p2shaddr); } } /*DL_FOREACH_SAFE(ap->utxos,up,tmp) { if ( up->spendheight <= 0 ) { if ( up->U.value > *maxp ) *maxp = up->U.value; if ( *minp == 0 || up->U.value < *minp ) *minp = up->U.value; *balancep += up->U.value; n++; } }*/ char *LP_ordermatch(char *base,int64_t txfee,double maxprice,double maxvolume,char *rel,bits256 txid,int32_t vout,bits256 feetxid,int32_t feevout,int64_t desttxfee,int32_t duration) { struct LP_quoteinfo Q; int64_t bestsatoshis=0,bestdestsatoshis = 0; double ordermatchprice = 0.; struct LP_utxoinfo *autxo,*bestutxo; txfee = LP_txfeecalc(LP_coinfind(base),txfee); desttxfee = LP_txfeecalc(LP_coinfind(rel),desttxfee); if ( (autxo= LP_utxopairfind(0,txid,vout,feetxid,feevout)) == 0 ) return(clonestr("{\"error\":\"cant find alice utxopair\"}")); if ( (bestutxo= LP_bestutxo(&ordermatchprice,&bestsatoshis,&bestdestsatoshis,autxo,base,maxprice,duration,txfee,desttxfee,SATOSHIDEN*maxvolume)) == 0 || ordermatchprice == 0. || bestdestsatoshis == 0 ) return(clonestr("{\"error\":\"cant find ordermatch utxo\"}")); if ( LP_quoteinfoinit(&Q,bestutxo,rel,ordermatchprice,bestsatoshis,bestdestsatoshis) < 0 ) return(clonestr("{\"error\":\"cant set ordermatch quote\"}")); if ( LP_quotedestinfo(&Q,autxo->payment.txid,autxo->payment.vout,autxo->fee.txid,autxo->fee.vout,LP_mypub25519,autxo->coinaddr) < 0 ) return(clonestr("{\"error\":\"cant set ordermatch quote info\"}")); return(jprint(LP_quotejson(&Q),1)); } char *LP_autotrade(void *ctx,char *myipaddr,int32_t mypubsock,char *base,char *rel,double maxprice,double relvolume,int32_t timeout,int32_t duration) { uint64_t desttxfee,txfee; int64_t bestsatoshis=0,bestdestsatoshis=0; struct LP_utxoinfo *autxo,*butxo,*bestutxo = 0; double qprice,ordermatchprice=0.; struct LP_quoteinfo Q; if ( duration <= 0 ) duration = LP_ORDERBOOK_DURATION; if ( timeout <= 0 ) timeout = LP_AUTOTRADE_TIMEOUT; if ( maxprice <= 0. || relvolume <= 0. || LP_priceinfofind(base) == 0 || LP_priceinfofind(rel) == 0 ) return(clonestr("{\"error\":\"invalid parameter\"}")); if ( (autxo= LP_utxo_bestfit(rel,SATOSHIDEN * relvolume)) == 0 ) return(clonestr("{\"error\":\"cant find utxo that is big enough\"}")); LP_txfees(&txfee,&desttxfee,base,rel); if ( (bestutxo= LP_bestutxo(&ordermatchprice,&bestsatoshis,&bestdestsatoshis,autxo,base,maxprice,duration,txfee,desttxfee,SATOSHIDEN*relvolume)) == 0 || ordermatchprice == 0. || bestdestsatoshis == 0 ) { printf("bestutxo.%p ordermatchprice %.8f bestdestsatoshis %.8f\n",bestutxo,ordermatchprice,dstr(bestdestsatoshis)); return(clonestr("{\"error\":\"cant find ordermatch utxo\"}")); } if ( LP_quoteinfoinit(&Q,bestutxo,rel,ordermatchprice,bestsatoshis,bestdestsatoshis) < 0 ) return(clonestr("{\"error\":\"cant set ordermatch quote\"}")); if ( LP_quotedestinfo(&Q,autxo->payment.txid,autxo->payment.vout,autxo->fee.txid,autxo->fee.vout,LP_mypub25519,autxo->coinaddr) < 0 ) return(clonestr("{\"error\":\"cant set ordermatch quote info\"}")); if ( (qprice= LP_quote_validate(&autxo,&butxo,&Q,0)) <= SMALLVAL ) { printf("quote validate error %.0f\n",qprice); return(clonestr("{\"error\":\"quote validation error\"}")); } printf("do quote.(%s)\n",jprint(LP_quotejson(&Q),1)); return(LP_trade(ctx,myipaddr,mypubsock,&Q,maxprice,timeout,duration)); } #endif