You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

683 lines
30 KiB

8 years ago
/******************************************************************************
* Copyright © 2014-2017 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. *
* *
******************************************************************************/
8 years ago
8 years ago
//
8 years ago
// LP_ordermatch.c
8 years ago
// marketmaker
//
struct basilisk_request *LP_requestinit(struct basilisk_request *rp,bits256 srchash,bits256 desthash,char *src,uint64_t srcsatoshis,char *dest,uint64_t destsatoshis,uint32_t timestamp,uint32_t quotetime,int32_t DEXselector)
{
struct basilisk_request R;
memset(rp,0,sizeof(*rp));
rp->srchash = srchash;
rp->srcamount = srcsatoshis;
rp->timestamp = timestamp;
rp->DEXselector = DEXselector;
safecopy(rp->src,src,sizeof(rp->src));
safecopy(rp->dest,dest,sizeof(rp->dest));
R = *rp;
rp->requestid = basilisk_requestid(rp);
rp->quotetime = quotetime;
rp->desthash = desthash;
rp->destamount = destsatoshis;
rp->quoteid = basilisk_quoteid(rp);
8 years ago
printf("r.%u %u, q.%u %u: %s %.8f -> %s %.8f\n",rp->timestamp,rp->requestid,rp->quotetime,rp->quoteid,rp->src,dstr(rp->srcamount),rp->dest,dstr(rp->destamount));
8 years ago
return(rp);
}
cJSON *LP_quotejson(struct LP_quoteinfo *qp)
{
double price; cJSON *retjson = cJSON_CreateObject();
jaddstr(retjson,"base",qp->srccoin);
jaddstr(retjson,"rel",qp->destcoin);
if ( qp->coinaddr[0] != 0 )
jaddstr(retjson,"address",qp->coinaddr);
if ( qp->timestamp != 0 )
jaddnum(retjson,"timestamp",qp->timestamp);
if ( bits256_nonz(qp->txid) != 0 )
{
jaddbits256(retjson,"txid",qp->txid);
jaddnum(retjson,"vout",qp->vout);
}
if ( bits256_nonz(qp->srchash) != 0 )
jaddbits256(retjson,"srchash",qp->srchash);
if ( qp->txfee != 0 )
jadd64bits(retjson,"txfee",qp->txfee);
if ( qp->quotetime != 0 )
jaddnum(retjson,"quotetime",qp->quotetime);
if ( qp->satoshis != 0 )
jadd64bits(retjson,"satoshis",qp->satoshis);
if ( bits256_nonz(qp->desthash) != 0 )
jaddbits256(retjson,"desthash",qp->desthash);
if ( bits256_nonz(qp->txid2) != 0 )
{
jaddbits256(retjson,"txid2",qp->txid2);
jaddnum(retjson,"vout2",qp->vout2);
}
8 years ago
if ( bits256_nonz(qp->desttxid) != 0 )
8 years ago
{
if ( qp->destaddr[0] != 0 )
jaddstr(retjson,"destaddr",qp->destaddr);
jaddbits256(retjson,"desttxid",qp->desttxid);
jaddnum(retjson,"destvout",qp->destvout);
}
if ( bits256_nonz(qp->feetxid) != 0 )
{
jaddbits256(retjson,"feetxid",qp->feetxid);
jaddnum(retjson,"feevout",qp->feevout);
}
if ( qp->desttxfee != 0 )
jadd64bits(retjson,"desttxfee",qp->desttxfee);
8 years ago
if ( qp->destsatoshis != 0 )
{
jadd64bits(retjson,"destsatoshis",qp->destsatoshis);
if ( qp->satoshis != 0 )
{
8 years ago
price = (double)qp->destsatoshis / qp->satoshis;
8 years ago
jaddnum(retjson,"price",price);
}
}
8 years ago
return(retjson);
}
int32_t LP_quoteparse(struct LP_quoteinfo *qp,cJSON *argjson)
{
safecopy(qp->srccoin,jstr(argjson,"base"),sizeof(qp->srccoin));
safecopy(qp->coinaddr,jstr(argjson,"address"),sizeof(qp->coinaddr));
safecopy(qp->destcoin,jstr(argjson,"rel"),sizeof(qp->destcoin));
safecopy(qp->destaddr,jstr(argjson,"destaddr"),sizeof(qp->destaddr));
qp->timestamp = juint(argjson,"timestamp");
qp->quotetime = juint(argjson,"quotetime");
qp->txid = jbits256(argjson,"txid");
qp->txid2 = jbits256(argjson,"txid2");
qp->vout = jint(argjson,"vout");
qp->vout2 = jint(argjson,"vout2");
qp->feevout = jint(argjson,"feevout");
qp->srchash = jbits256(argjson,"srchash");
qp->desttxid = jbits256(argjson,"desttxid");
qp->feetxid = jbits256(argjson,"feetxid");
qp->destvout = jint(argjson,"destvout");
qp->desthash = jbits256(argjson,"desthash");
8 years ago
qp->satoshis = j64bits(argjson,"satoshis");
qp->destsatoshis = j64bits(argjson,"destsatoshis");
8 years ago
qp->txfee = j64bits(argjson,"txfee");
qp->desttxfee = j64bits(argjson,"desttxfee");
return(0);
}
8 years ago
int32_t LP_quoteinfoinit(struct LP_quoteinfo *qp,struct LP_utxoinfo *utxo,char *destcoin,double price,uint64_t destsatoshis)
8 years ago
{
memset(qp,0,sizeof(*qp));
8 years ago
if ( qp->timestamp == 0 )
qp->timestamp = (uint32_t)time(NULL);
8 years ago
safecopy(qp->destcoin,destcoin,sizeof(qp->destcoin));
8 years ago
if ( (qp->txfee= LP_getestimatedrate(utxo->coin)*LP_AVETXSIZE) < LP_MIN_TXFEE )
qp->txfee = LP_MIN_TXFEE;
8 years ago
qp->satoshis = destsatoshis / price + 0.49;
8 years ago
if ( utxo->iambob == 0 || qp->txfee >= qp->satoshis || qp->txfee >= utxo->deposit.value || utxo->deposit.value < LP_DEPOSITSATOSHIS(qp->satoshis) )
8 years ago
{
8 years ago
printf("quoteinit error.(%d %d %d %d) %.8f vs %.8f\n",utxo->iambob == 0,qp->txfee >= qp->satoshis,qp->txfee >= utxo->deposit.value,utxo->deposit.value < LP_DEPOSITSATOSHIS(qp->satoshis),dstr(utxo->deposit.value),dstr(LP_DEPOSITSATOSHIS(qp->satoshis)));
8 years ago
return(-1);
8 years ago
}
8 years ago
qp->txid = utxo->payment.txid;
qp->vout = utxo->payment.vout;
qp->txid2 = utxo->deposit.txid;
qp->vout2 = utxo->deposit.vout;
8 years ago
qp->destsatoshis = destsatoshis;
8 years ago
if ( (qp->desttxfee= LP_getestimatedrate(qp->destcoin) * LP_AVETXSIZE) < LP_MIN_TXFEE )
qp->desttxfee = LP_MIN_TXFEE;
8 years ago
if ( qp->desttxfee >= qp->destsatoshis )
8 years ago
{
printf("quoteinit desttxfee %.8f < %.8f destsatoshis\n",dstr(qp->desttxfee),dstr(qp->destsatoshis));
8 years ago
return(-2);
8 years ago
}
8 years ago
safecopy(qp->srccoin,utxo->coin,sizeof(qp->srccoin));
safecopy(qp->coinaddr,utxo->coinaddr,sizeof(qp->coinaddr));
8 years ago
qp->srchash = utxo->pubkey;
8 years ago
return(0);
}
8 years ago
int32_t LP_quotedestinfo(struct LP_quoteinfo *qp,bits256 desttxid,int32_t destvout,bits256 feetxid,int32_t feevout,bits256 desthash,char *destaddr)
8 years ago
{
qp->desttxid = desttxid;
qp->destvout = destvout;
qp->desthash = desthash;
8 years ago
qp->feetxid = feetxid;
qp->feevout = feevout;
8 years ago
safecopy(qp->destaddr,destaddr,sizeof(qp->destaddr));
return(0);
}
char *LP_quotereceived(cJSON *argjson)
{
struct LP_cacheinfo *ptr; double price; struct LP_quoteinfo Q;
LP_quoteparse(&Q,argjson);
price = (double)(Q.destsatoshis + Q.desttxfee) / (Q.satoshis + Q.txfee);
if ( (ptr= LP_cacheadd(Q.srccoin,Q.destcoin,Q.txid,Q.vout,price,&Q)) != 0 )
{
ptr->Q = Q;
8 years ago
printf(">>>>>>>>>> received quote %s/%s %.8f\n",Q.srccoin,Q.destcoin,price);
8 years ago
return(clonestr("{\"result\":\"updated\"}"));
} else return(clonestr("{\"error\":\"nullptr\"}"));
}
8 years ago
char *LP_pricepings(void *ctx,char *myipaddr,int32_t pubsock,double profitmargin,char *base,char *rel,double price)
8 years ago
{
bits256 zero; cJSON *reqjson = cJSON_CreateObject();
jaddbits256(reqjson,"pubkey",LP_mypubkey);
jaddstr(reqjson,"base",base);
jaddstr(reqjson,"rel",rel);
jaddnum(reqjson,"price",price);
if ( pubsock >= 0 )
{
jaddstr(reqjson,"method","postprice");
8 years ago
//printf("%d pricepings.(%s)\n",pubsock,jprint(reqjson,0));
8 years ago
LP_send(pubsock,jprint(reqjson,1),1);
}
else
{
jaddstr(reqjson,"method","forward");
jaddstr(reqjson,"method2","postprice");
memset(zero.bytes,0,sizeof(zero));
8 years ago
LP_forward(ctx,myipaddr,pubsock,profitmargin,zero,jprint(reqjson,1),1);
8 years ago
}
return(clonestr("{\"result\":\"success\"}"));
}
char *LP_postedprice(cJSON *argjson)
{
8 years ago
bits256 pubkey; double price; char *base,*rel;
8 years ago
//printf("PRICE POSTED.(%s)\n",jprint(argjson,0));
8 years ago
if ( (base= jstr(argjson,"base")) != 0 && (rel= jstr(argjson,"rel")) != 0 && (price= jdouble(argjson,"price")) > SMALLVAL )
{
pubkey = jbits256(argjson,"pubkey");
if ( bits256_nonz(pubkey) != 0 )
{
LP_pricefeedupdate(pubkey,base,rel,price);
return(clonestr("{\"result\":\"success\"}"));
}
}
return(clonestr("{\"error\":\"missing fields in posted price\"}"));
}
8 years ago
int32_t LP_quote_checkmempool(struct LP_quoteinfo *qp)
8 years ago
{
8 years ago
int32_t selector,spendvini; bits256 spendtxid;
if ( (selector= LP_mempool_vinscan(&spendtxid,&spendvini,qp->srccoin,qp->txid,qp->vout,qp->txid2,qp->vout2)) >= 0 )
{
char str[65]; printf("LP_tradecommand selector.%d in mempool %s vini.%d",selector,bits256_str(str,spendtxid),spendvini);
return(-1);
}
if ( (selector= LP_mempool_vinscan(&spendtxid,&spendvini,qp->destcoin,qp->desttxid,qp->destvout,qp->feetxid,qp->feevout)) >= 0 )
{
char str[65]; printf("LP_tradecommand dest selector.%d in mempool %s vini.%d",selector,bits256_str(str,spendtxid),spendvini);
return(-1);
}
return(0);
}
8 years ago
double LP_quote_validate(struct LP_utxoinfo **autxop,struct LP_utxoinfo **butxop,struct LP_quoteinfo *qp,int32_t iambob)
8 years ago
{
double qprice; uint64_t srcvalue,srcvalue2,destvalue,destvalue2;
*autxop = *butxop = 0;
8 years ago
if ( LP_iseligible(&srcvalue,&srcvalue2,1,qp->srccoin,qp->txid,qp->vout,qp->satoshis,qp->txid2,qp->vout2,qp->srchash) == 0 )
8 years ago
{
printf("bob not eligible\n");
return(-2);
}
8 years ago
if ( LP_iseligible(&destvalue,&destvalue2,0,qp->destcoin,qp->desttxid,qp->destvout,qp->destsatoshis,qp->feetxid,qp->feevout,qp->desthash) == 0 )
8 years ago
{
8 years ago
char str[65]; printf("alice not eligible (%.8f %.8f) %s/v%d\n",dstr(destvalue),dstr(destvalue2),bits256_str(str,qp->feetxid),qp->feevout);
8 years ago
return(-3);
}
if ( LP_quote_checkmempool(qp) < 0 )
return(-4);
if ( (*butxop= LP_utxofind(1,qp->txid,qp->vout)) == 0 )
return(-5);
if ( bits256_cmp((*butxop)->deposit.txid,qp->txid2) != 0 || (*butxop)->deposit.vout != qp->vout2 )
return(-6);
if ( strcmp((*butxop)->coinaddr,qp->coinaddr) != 0 )
return(-7);
if ( iambob == 0 )
{
if ( (*autxop= LP_utxofind(0,qp->desttxid,qp->destvout)) == 0 )
return(-8);
if ( bits256_cmp((*autxop)->fee.txid,qp->feetxid) != 0 || (*autxop)->fee.vout != qp->feevout )
return(-9);
if ( strcmp((*autxop)->coinaddr,qp->destaddr) != 0 )
return(-10);
}
8 years ago
if ( destvalue < qp->desttxfee+qp->destsatoshis || srcvalue < qp->txfee+qp->satoshis )
8 years ago
{
8 years ago
printf("destvalue %.8f srcvalue %.8f, destsatoshis %.8f or satoshis %.8f is too small txfees %.8f %.8f?\n",dstr(destvalue),dstr(srcvalue),dstr(qp->destsatoshis),dstr(qp->satoshis),dstr(qp->desttxfee),dstr(qp->txfee));
8 years ago
return(-11);
}
8 years ago
qprice = ((double)qp->destsatoshis / qp->satoshis);
8 years ago
if ( qp->satoshis < (srcvalue >> LP_MINVOL) )
8 years ago
{
printf("utxo payment %.8f is less than half covered by Q %.8f\n",dstr(srcvalue),dstr(qp->satoshis));
return(-12);
}
8 years ago
if ( qp->destsatoshis < (destvalue >> LP_MINVOL) )
8 years ago
{
printf("destsatoshis %.8f is less than half of value %.8f\n",dstr(qp->destsatoshis),dstr(destvalue));
return(-13);
}
8 years ago
printf("qprice %.8f <- %.8f/%.8f txfees.(%.8f %.8f)\n",qprice,dstr(qp->destsatoshis),dstr(qp->satoshis),dstr(qp->txfee),dstr(qp->desttxfee));
8 years ago
return(qprice);
8 years ago
}
8 years ago
int32_t LP_arrayfind(cJSON *array,bits256 txid,int32_t vout)
{
int32_t i,n = cJSON_GetArraySize(array); cJSON *item;
for (i=0; i<n; i++)
{
item = jitem(array,i);
if ( vout == jint(item,"vout") && bits256_cmp(txid,jbits256(item,"txid")) == 0 )
return(i);
}
return(-1);
}
8 years ago
double LP_query(void *ctx,char *myipaddr,int32_t mypubsock,double profitmargin,char *method,struct LP_quoteinfo *qp)
8 years ago
{
8 years ago
cJSON *reqjson; int32_t i,flag = 0; double price = 0.; struct LP_utxoinfo *utxo;
if ( strcmp(method,"request") == 0 )
{
if ( (utxo= LP_utxofind(0,qp->desttxid,qp->destvout)) != 0 && LP_ismine(utxo) > 0 && LP_isavailable(utxo) > 0 )
LP_unavailableset(utxo,qp->srchash);
else
{
printf("couldnt find my txid to make request\n");
return(0.);
}
}
reqjson = LP_quotejson(qp);
if ( bits256_nonz(qp->desthash) != 0 )
flag = 1;
8 years ago
//printf("QUERY.(%s)\n",jprint(reqjson,0));
8 years ago
if ( IAMLP != 0 )
{
jaddstr(reqjson,"method",method);
LP_send(LP_mypubsock,jprint(reqjson,1),1);
}
else
{
jaddstr(reqjson,"method2",method);
jaddstr(reqjson,"method","forward");
8 years ago
jaddbits256(reqjson,"pubkey",qp->srchash);
8 years ago
LP_forward(ctx,myipaddr,mypubsock,profitmargin,qp->srchash,jprint(reqjson,1),1);
8 years ago
}
8 years ago
for (i=0; i<30; i++)
8 years ago
{
8 years ago
if ( (price= LP_pricecache(qp,qp->srccoin,qp->destcoin,qp->txid,qp->vout)) > SMALLVAL )
8 years ago
{
8 years ago
if ( flag == 0 || bits256_nonz(qp->desthash) != 0 )
8 years ago
{
8 years ago
//printf("break out of loop.%d price %.8f\n",i,price);
break;
8 years ago
}
}
8 years ago
usleep(100000);
}
return(price);
}
8 years ago
int32_t LP_nanobind(void *ctx,char *pairstr,char *myipaddr)
8 years ago
{
8 years ago
int32_t i,timeout,r,pairsock = -1; uint16_t mypullport; char bindaddr[128];
if ( LP_canbind != 0 )
8 years ago
{
8 years ago
if ( strcmp(myipaddr,"127.0.0.1") == 0 )
{
if ( LP_mypeer != 0 )
myipaddr = LP_mypeer->ipaddr;
}
if ( strcmp(myipaddr,"127.0.0.1") == 0 )
{
printf("cant nanobind to localhost\n");
return(-1);
}
8 years ago
if ( (pairsock= nn_socket(AF_SP,NN_PAIR)) < 0 )
printf("error creating utxo->pair\n");
else
8 years ago
{
8 years ago
for (i=0; i<10; i++)
{
r = (10000 + (rand() % 50000)) & 0xffff;
nanomsg_transportname(0,pairstr,myipaddr,r);
nanomsg_transportname(1,bindaddr,myipaddr,r);
if ( nn_bind(pairsock,bindaddr) >= 0 )
{
timeout = 100;
nn_setsockopt(pairsock,NN_SOL_SOCKET,NN_SNDTIMEO,&timeout,sizeof(timeout));
nn_setsockopt(pairsock,NN_SOL_SOCKET,NN_RCVTIMEO,&timeout,sizeof(timeout));
printf("nanobind %s to %d\n",pairstr,pairsock);
return(pairsock);
} else printf("error binding to %s for %s\n",bindaddr,pairstr);
}
}
8 years ago
} else pairsock = LP_initpublicaddr(ctx,&mypullport,pairstr,myipaddr,0,1);
8 years ago
return(pairsock);
8 years ago
}
8 years ago
int32_t LP_connectstartbob(void *ctx,int32_t pubsock,struct LP_utxoinfo *utxo,cJSON *argjson,char *myipaddr,char *base,char *rel,double profitmargin,double price,struct LP_quoteinfo *qp)
8 years ago
{
8 years ago
char pairstr[512]; cJSON *retjson; bits256 privkey; int32_t pair=-1,retval = -1,DEXselector = 0; struct basilisk_swap *swap; struct iguana_info *coin;
8 years ago
printf("LP_connectstartbob.(%s) with.(%s)\n",myipaddr,jprint(argjson,0));
8 years ago
qp->quotetime = (uint32_t)time(NULL);
8 years ago
if ( (coin= LP_coinfind(utxo->coin)) == 0 )
{
printf("cant find coin.%s\n",utxo->coin);
return(-1);
}
privkey = LP_privkey(utxo->coinaddr,coin->taddr);
8 years ago
if ( bits256_nonz(privkey) != 0 && qp->quotetime >= qp->timestamp-3 && qp->quotetime <= utxo->T.swappending && bits256_cmp(LP_mypubkey,qp->srchash) == 0 )
{
8 years ago
if ( (pair= LP_nanobind(ctx,pairstr,myipaddr)) >= 0 )
8 years ago
{
8 years ago
LP_requestinit(&qp->R,qp->srchash,qp->desthash,base,qp->satoshis,rel,qp->destsatoshis,qp->timestamp,qp->quotetime,DEXselector);
swap = LP_swapinit(1,0,privkey,&qp->R,qp);
swap->N.pair = pair;
utxo->S.swap = swap;
swap->utxo = utxo;
if ( OS_thread_create(malloc(sizeof(pthread_t)),NULL,(void *)LP_bobloop,(void *)swap) == 0 )
8 years ago
{
8 years ago
retjson = LP_quotejson(qp);
jaddstr(retjson,"method","connected");
jaddstr(retjson,"pair",pairstr);
jaddnum(retjson,"requestid",qp->R.requestid);
jaddnum(retjson,"quoteid",qp->R.quoteid);
char str[65]; printf("BOB pubsock.%d sends to (%s)\n",pubsock,bits256_str(str,utxo->S.otherpubkey));
if ( pubsock >= 0 )
LP_send(pubsock,jprint(retjson,0),1);
jdelete(retjson,"method");
jaddstr(retjson,"method2","connected");
jaddstr(retjson,"method","forward");
8 years ago
LP_forward(ctx,myipaddr,pubsock,profitmargin,utxo->S.otherpubkey,jprint(retjson,1),1);
8 years ago
retval = 0;
} else printf("error launching swaploop\n");
} else printf("couldnt bind to any port %s\n",pairstr);
}
else
{
printf("dest %.8f vs required %.8f (%d %d %d %d %d)\n",dstr(qp->destsatoshis),dstr(price*(utxo->S.satoshis-qp->txfee)),bits256_nonz(privkey) != 0 ,qp->timestamp == utxo->T.swappending-LP_RESERVETIME,qp->quotetime >= qp->timestamp-3,qp->quotetime < utxo->T.swappending,bits256_cmp(LP_mypubkey,qp->srchash) == 0);
}
8 years ago
if ( retval < 0 )
{
if ( pair >= 0 )
nn_close(pair);
LP_availableset(utxo);
8 years ago
} else LP_unavailableset(utxo,utxo->S.otherpubkey);
8 years ago
return(retval);
8 years ago
}
8 years ago
char *LP_connectedalice(cJSON *argjson) // alice
8 years ago
{
8 years ago
cJSON *retjson; double bid,ask,price,qprice; int32_t timeout,pairsock = -1; char *pairstr; int32_t DEXselector = 0; struct LP_utxoinfo *autxo,*butxo; struct LP_quoteinfo Q; struct basilisk_swap *swap; struct iguana_info *coin;
8 years ago
if ( LP_quoteparse(&Q,argjson) < 0 )
clonestr("{\"error\":\"cant parse quote\"}");
if ( bits256_cmp(Q.desthash,LP_mypubkey) != 0 )
return(clonestr("{\"result\",\"update stats\"}"));
8 years ago
printf("CONNECTED.(%s)\n",jprint(argjson,0));
8 years ago
if ( (qprice= LP_quote_validate(&autxo,&butxo,&Q,0)) <= SMALLVAL )
{
LP_availableset(autxo);
printf("quote validate error %.0f\n",qprice);
return(clonestr("{\"error\":\"quote validation error\"}"));
}
8 years ago
if ( (price= LP_myprice(&bid,&ask,Q.destcoin,Q.srccoin)) <= SMALLVAL || ask <= SMALLVAL )
8 years ago
{
8 years ago
printf("this node has no price for %s/%s (%.8f %.8f)\n",Q.destcoin,Q.srccoin,bid,ask);
8 years ago
LP_availableset(autxo);
return(clonestr("{\"error\":\"no price set\"}"));
}
8 years ago
price = 1. / ask;
8 years ago
if ( qprice > price+0.00000001 )
8 years ago
{
8 years ago
printf("qprice %.8f too big vs %.8f\n",qprice,price);
8 years ago
LP_availableset(autxo);
return(clonestr("{\"error\":\"quote price too expensive\"}"));
}
8 years ago
if ( (coin= LP_coinfind(Q.destcoin)) == 0 )
{
return(clonestr("{\"error\":\"cant get alicecoin\"}"));
}
Q.privkey = LP_privkey(Q.destaddr,coin->taddr);
8 years ago
if ( bits256_nonz(Q.privkey) != 0 && Q.quotetime >= Q.timestamp-3 )
8 years ago
{
8 years ago
retjson = cJSON_CreateObject();
if ( (pairstr= jstr(argjson,"pair")) == 0 || (pairsock= nn_socket(AF_SP,NN_PAIR)) < 0 )
jaddstr(retjson,"error","couldnt create pairsock");
else if ( nn_connect(pairsock,pairstr) >= 0 )
8 years ago
{
8 years ago
timeout = 100;
nn_setsockopt(pairsock,NN_SOL_SOCKET,NN_SNDTIMEO,&timeout,sizeof(timeout));
nn_setsockopt(pairsock,NN_SOL_SOCKET,NN_RCVTIMEO,&timeout,sizeof(timeout));
8 years ago
LP_requestinit(&Q.R,Q.srchash,Q.desthash,Q.srccoin,Q.satoshis,Q.destcoin,Q.destsatoshis,Q.timestamp,Q.quotetime,DEXselector);
swap = LP_swapinit(0,0,Q.privkey,&Q.R,&Q);
swap->N.pair = pairsock;
8 years ago
autxo->S.swap = swap;
swap->utxo = autxo;
8 years ago
printf("alice pairstr.(%s) pairsock.%d\n",pairstr,pairsock);
8 years ago
if ( OS_thread_create(malloc(sizeof(pthread_t)),NULL,(void *)LP_aliceloop,(void *)swap) == 0 )
8 years ago
{
8 years ago
jaddstr(retjson,"result","success");
jadd(retjson,"trade",LP_quotejson(&Q));
jaddnum(retjson,"requestid",Q.R.requestid);
jaddnum(retjson,"quoteid",Q.R.quoteid);
} else jaddstr(retjson,"error","couldnt aliceloop");
8 years ago
}
8 years ago
printf("connected result.(%s)\n",jprint(retjson,0));
8 years ago
if ( jobj(retjson,"error") != 0 )
LP_availableset(autxo);
8 years ago
return(jprint(retjson,1));
8 years ago
}
else
{
8 years ago
LP_availableset(autxo);
8 years ago
printf("no privkey found\n");
8 years ago
return(clonestr("{\"error\",\"no privkey\"}"));
8 years ago
}
8 years ago
}
8 years ago
int32_t LP_tradecommand(void *ctx,char *myipaddr,int32_t pubsock,cJSON *argjson,uint8_t *data,int32_t datalen,double profitmargin)
8 years ago
{
8 years ago
char *method; cJSON *retjson; double qprice,price,bid,ask; struct LP_utxoinfo *autxo,*butxo; int32_t retval = -1; struct LP_quoteinfo Q;
8 years ago
if ( (method= jstr(argjson,"method")) != 0 && (strcmp(method,"request") == 0 ||strcmp(method,"connect") == 0) )
8 years ago
{
8 years ago
//printf("TRADECOMMAND.(%s)\n",jprint(argjson,0));
8 years ago
retval = 1;
8 years ago
if ( LP_quoteparse(&Q,argjson) == 0 && bits256_cmp(LP_mypubkey,Q.srchash) == 0 )
8 years ago
{
8 years ago
if ( (price= LP_myprice(&bid,&ask,Q.srccoin,Q.destcoin)) <= SMALLVAL || ask <= SMALLVAL )
8 years ago
{
8 years ago
printf("this node has no price for %s/%s\n",Q.srccoin,Q.destcoin);
return(-3);
8 years ago
}
8 years ago
price = ask;
if ( (qprice= LP_quote_validate(&autxo,&butxo,&Q,1)) <= SMALLVAL )
8 years ago
{
8 years ago
printf("quote validate error %.0f\n",qprice);
return(-4);
8 years ago
}
8 years ago
if ( qprice < price-0.00000001 )
8 years ago
{
8 years ago
printf("(%.8f %.8f) quote price %.8f too low vs %.8f for %s/%s\n",bid,ask,qprice,price,Q.srccoin,Q.destcoin);
8 years ago
return(-5);
}
if ( butxo->S.swap == 0 && time(NULL) > butxo->T.swappending )
butxo->T.swappending = 0;
8 years ago
if ( strcmp(method,"request") == 0 ) // bob needs apayment + fee tx's
8 years ago
{
8 years ago
if ( LP_isavailable(butxo) > 0 )
8 years ago
{
8 years ago
butxo->T.swappending = Q.timestamp + LP_RESERVETIME;
retjson = LP_quotejson(&Q);
butxo->S.otherpubkey = jbits256(argjson,"desthash");
LP_unavailableset(butxo,butxo->S.otherpubkey);
jaddnum(retjson,"quotetime",juint(argjson,"quotetime"));
jaddnum(retjson,"pending",butxo->T.swappending);
jaddbits256(retjson,"desthash",butxo->S.otherpubkey);
jaddbits256(retjson,"pubkey",butxo->S.otherpubkey);
jaddstr(retjson,"method","reserved");
if ( pubsock >= 0 )
LP_send(pubsock,jprint(retjson,0),1);
jdelete(retjson,"method");
jaddstr(retjson,"method2","reserved");
jaddstr(retjson,"method","forward");
8 years ago
LP_forward(ctx,myipaddr,pubsock,profitmargin,butxo->S.otherpubkey,jprint(retjson,1),1);
8 years ago
butxo->T.lasttime = (uint32_t)time(NULL);
printf("set swappending.%u accept qprice %.8f, min %.8f\n",butxo->T.swappending,qprice,price);
} else printf("warning swappending.%u swap.%p\n",butxo->T.swappending,butxo->S.swap);
8 years ago
}
else if ( strcmp(method,"connect") == 0 ) // bob
{
retval = 4;
8 years ago
if ( butxo->T.swappending != 0 && butxo->S.swap == 0 )
8 years ago
LP_connectstartbob(ctx,pubsock,butxo,argjson,myipaddr,Q.srccoin,Q.destcoin,profitmargin,qprice,&Q);
8 years ago
else printf("pend.%u swap %p when connect came in (%s)\n",butxo->T.swappending,butxo->S.swap,jprint(argjson,0));
8 years ago
}
}
}
return(retval);
}
8 years ago
char *LP_autotrade(void *ctx,char *myipaddr,int32_t mypubsock,double profitmargin,char *base,char *rel,double maxprice,double volume,int32_t timeout)
8 years ago
{
8 years ago
int64_t satoshis,destsatoshis,desttxfee,txfee,bestdestsatoshis=0; bits256 txid,pubkey; char *obookstr; cJSON *orderbook,*asks,*item,*bestitem=0; struct LP_utxoinfo *autxo,*butxo,*bestutxo = 0; int32_t i,vout,numasks,DEXselector=0; uint32_t expiration; double ordermatchprice,bestmetric,metric,bestprice=0.,vol,price; struct LP_quoteinfo Q;
8 years ago
if ( maxprice <= 0. || volume <= 0. || LP_priceinfofind(base) == 0 || LP_priceinfofind(rel) == 0 )
return(clonestr("{\"error\":\"invalid parameter\"}"));
8 years ago
if ( (autxo= LP_utxo_bestfit(rel,SATOSHIDEN * volume)) == 0 )
8 years ago
return(clonestr("{\"error\":\"cant find utxo that is big enough\"}"));
bestmetric = ordermatchprice = 0.;
8 years ago
if ( (desttxfee= LP_getestimatedrate(rel) * LP_AVETXSIZE) < LP_MIN_TXFEE )
8 years ago
desttxfee = LP_MIN_TXFEE;
8 years ago
if ( (txfee= LP_getestimatedrate(base) * LP_AVETXSIZE) < LP_MIN_TXFEE )
8 years ago
txfee = LP_MIN_TXFEE;
8 years ago
if ( timeout == 0 )
timeout = LP_AUTOTRADE_TIMEOUT;
8 years ago
if ( (obookstr= LP_orderbook(base,rel)) != 0 )
{
if ( (orderbook= cJSON_Parse(obookstr)) != 0 )
{
if ( (asks= jarray(&numasks,orderbook,"asks")) != 0 )
{
for (i=0; i<numasks; i++)
8 years ago
{
8 years ago
item = jitem(asks,i);
if ( (price= jdouble(item,"price")) > SMALLVAL && price <= maxprice )
8 years ago
{
8 years ago
price *= 1.0001;
if ( price > maxprice )
price = maxprice;
8 years ago
pubkey = jbits256(item,"pubkey");
if ( bits256_cmp(pubkey,LP_mypubkey) != 0 )
8 years ago
{
8 years ago
if ( bestprice == 0. ) // assumes price ordered asks
bestprice = price;
8 years ago
//printf("item.[%d] %s\n",i,jprint(item,0));
8 years ago
txid = jbits256(item,"txid");
vout = jint(item,"vout");
vol = jdouble(item,"volume");
8 years ago
metric = price / bestprice;
8 years ago
if ( (butxo= LP_utxofind(1,txid,vout)) != 0 && (long long)(vol*SATOSHIDEN) == butxo->S.satoshis && LP_isavailable(butxo) > 0 && LP_ismine(butxo) == 0 )
8 years ago
{
8 years ago
destsatoshis = (butxo->S.satoshis * price);
8 years ago
if ( destsatoshis > autxo->payment.value-desttxfee-1 )
destsatoshis = autxo->payment.value-desttxfee-1;
8 years ago
satoshis = destsatoshis / price;
8 years ago
if ( metric < 1.2 && destsatoshis > desttxfee && destsatoshis-desttxfee > (autxo->payment.value >> LP_MINVOL) && satoshis-txfee > (butxo->S.satoshis >> LP_MINVOL) && satoshis < butxo->payment.value-txfee )
8 years ago
{
8 years ago
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);
8 years ago
metric = dstr(destsatoshis) * metric * metric * metric;
if ( bestmetric == 0. || metric < bestmetric )
8 years ago
{
8 years ago
bestutxo = butxo;
ordermatchprice = price;
bestdestsatoshis = destsatoshis;
bestmetric = metric;
printf("set best!\n");
}
8 years ago
} else printf("skip.(%d %d) metric %f destsatoshis %.8f value %.8f destvalue %.8f txfees %.8f %.8f\n",destsatoshis > (autxo->payment.value >> LP_MINVOL),destsatoshis/price > (butxo->S.satoshis >> LP_MINVOL),metric,dstr(destsatoshis),dstr(butxo->S.satoshis),dstr(autxo->payment.value),dstr(txfee),dstr(desttxfee));
8 years ago
}
else
{
if ( butxo != 0 )
8 years ago
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);
8 years ago
printf("cant find butxo.%p or value mismatch %.8f != %.8f\n",butxo,vol,butxo!=0?dstr(butxo->S.satoshis):0);
}
8 years ago
}
8 years ago
} else break;
8 years ago
}
}
8 years ago
free_json(orderbook);
8 years ago
}
8 years ago
free(obookstr);
8 years ago
}
8 years ago
if ( bestutxo == 0 || ordermatchprice == 0. || bestdestsatoshis == 0 )
8 years ago
return(clonestr("{\"error\":\"cant find ordermatch utxo\"}"));
8 years ago
if ( LP_quoteinfoinit(&Q,bestutxo,rel,ordermatchprice,bestdestsatoshis) < 0 )
8 years ago
return(clonestr("{\"error\":\"cant set ordermatch quote\"}"));
8 years ago
if ( LP_quotedestinfo(&Q,autxo->payment.txid,autxo->payment.vout,autxo->fee.txid,autxo->fee.vout,LP_mypubkey,autxo->coinaddr) < 0 )
8 years ago
return(clonestr("{\"error\":\"cant set ordermatch quote info\"}"));
8 years ago
price = LP_query(ctx,myipaddr,mypubsock,profitmargin,"request",&Q);
8 years ago
bestitem = LP_quotejson(&Q);
8 years ago
if ( price > SMALLVAL )
8 years ago
{
8 years ago
if ( price <= maxprice )
8 years ago
{
8 years ago
price = LP_query(ctx,myipaddr,mypubsock,profitmargin,"connect",&Q);
8 years ago
LP_requestinit(&Q.R,Q.srchash,Q.desthash,base,Q.satoshis,Q.destcoin,Q.destsatoshis,Q.timestamp,Q.quotetime,DEXselector);
expiration = (uint32_t)time(NULL) + timeout;
while ( time(NULL) < expiration )
{
if ( autxo->S.swap != 0 )
break;
sleep(1);
}
if ( autxo->S.swap == 0 )
jaddstr(bestitem,"status","couldnt establish connection");
else jaddstr(bestitem,"status","connected");
jaddnum(bestitem,"quotedprice",price);
jaddnum(bestitem,"maxprice",maxprice);
jaddnum(bestitem,"requestid",Q.R.requestid);
jaddnum(bestitem,"quoteid",Q.R.quoteid);
printf("Alice r.%u q.%u\n",Q.R.requestid,Q.R.quoteid);
8 years ago
}
8 years ago
else
8 years ago
{
8 years ago
jaddnum(bestitem,"quotedprice",price);
jaddnum(bestitem,"maxprice",maxprice);
jaddstr(bestitem,"status","too expensive");
8 years ago
}
8 years ago
}
else
{
jaddnum(bestitem,"maxprice",maxprice);
8 years ago
jaddstr(bestitem,"status","no response to request");
8 years ago
}
8 years ago
if ( autxo->S.swap == 0 )
LP_availableset(autxo);
8 years ago
return(jprint(bestitem,0));
8 years ago
}