|
|
|
|
|
|
|
/******************************************************************************
|
|
|
|
* 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. *
|
|
|
|
* *
|
|
|
|
******************************************************************************/
|
|
|
|
|
|
|
|
//
|
|
|
|
// LP_signatures.c
|
|
|
|
// 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);
|
|
|
|
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));
|
|
|
|
return(rp);
|
|
|
|
}
|
|
|
|
|
|
|
|
cJSON *LP_quotejson(struct LP_quoteinfo *qp)
|
|
|
|
{
|
|
|
|
double price; cJSON *retjson = cJSON_CreateObject();
|
|
|
|
jaddstr(retjson,"gui",qp->gui[0] != 0 ? qp->gui : LP_gui);
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
if ( bits256_nonz(qp->desttxid) != 0 )
|
|
|
|
{
|
|
|
|
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);
|
|
|
|
if ( qp->destsatoshis != 0 )
|
|
|
|
{
|
|
|
|
jadd64bits(retjson,"destsatoshis",qp->destsatoshis);
|
|
|
|
if ( qp->satoshis != 0 )
|
|
|
|
{
|
|
|
|
price = (double)qp->destsatoshis / (qp->satoshis - qp->txfee);
|
|
|
|
jaddnum(retjson,"price",price);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( qp->R.requestid != 0 )
|
|
|
|
jaddnum(retjson,"requestid",qp->R.requestid);
|
|
|
|
if ( qp->R.quoteid != 0 )
|
|
|
|
jaddnum(retjson,"quoteid",qp->R.quoteid);
|
|
|
|
return(retjson);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_quoteparse(struct LP_quoteinfo *qp,cJSON *argjson)
|
|
|
|
{
|
|
|
|
uint32_t rid,qid;
|
|
|
|
safecopy(qp->gui,LP_gui,sizeof(qp->gui));
|
|
|
|
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");
|
|
|
|
qp->satoshis = j64bits(argjson,"satoshis");
|
|
|
|
qp->destsatoshis = j64bits(argjson,"destsatoshis");
|
|
|
|
qp->txfee = j64bits(argjson,"txfee");
|
|
|
|
qp->desttxfee = j64bits(argjson,"desttxfee");
|
|
|
|
qp->R.requestid = juint(argjson,"requestid");
|
|
|
|
qp->R.quoteid = juint(argjson,"quoteid");
|
|
|
|
if ( qp->R.requestid == 0 )
|
|
|
|
{
|
|
|
|
rid= basilisk_requestid(&qp->R);
|
|
|
|
//printf("requestid.%u -> %u\n",qp->R.requestid,rid);
|
|
|
|
qp->R.requestid = rid;
|
|
|
|
}
|
|
|
|
if ( qp->R.quoteid == 0 )
|
|
|
|
{
|
|
|
|
qid= basilisk_quoteid(&qp->R);
|
|
|
|
//printf("quoteid.%u -> %u\n",qp->R.quoteid,qid);
|
|
|
|
qp->R.quoteid = qid;
|
|
|
|
}
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LP_txfees(uint64_t *txfeep,uint64_t *desttxfeep,char *base,char *rel)
|
|
|
|
{
|
|
|
|
*txfeep = LP_txfeecalc(LP_coinfind(base),0,0);
|
|
|
|
*desttxfeep = LP_txfeecalc(LP_coinfind(rel),0,0);
|
|
|
|
printf("LP_txfees(%.8f %.8f)\n",dstr(*txfeep),dstr(*desttxfeep));
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_quoteinfoinit(struct LP_quoteinfo *qp,struct LP_utxoinfo *utxo,char *destcoin,double price,uint64_t satoshis,uint64_t destsatoshis)
|
|
|
|
{
|
|
|
|
memset(qp,0,sizeof(*qp));
|
|
|
|
if ( qp->timestamp == 0 )
|
|
|
|
qp->timestamp = (uint32_t)time(NULL);
|
|
|
|
safecopy(qp->destcoin,destcoin,sizeof(qp->destcoin));
|
|
|
|
LP_txfees(&qp->txfee,&qp->desttxfee,utxo->coin,qp->destcoin);
|
|
|
|
qp->satoshis = satoshis;//(destsatoshis / price) + 0.49;
|
|
|
|
qp->destsatoshis = destsatoshis;
|
|
|
|
/*if ( qp->txfee >= qp->satoshis || qp->txfee >= utxo->deposit.value || utxo->deposit.value < LP_DEPOSITSATOSHIS(qp->satoshis) ) //utxo->iambob == 0 ||
|
|
|
|
{
|
|
|
|
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)));
|
|
|
|
return(-1);
|
|
|
|
}*/
|
|
|
|
qp->txid = utxo->payment.txid;
|
|
|
|
qp->vout = utxo->payment.vout;
|
|
|
|
qp->txid2 = utxo->deposit.txid;
|
|
|
|
qp->vout2 = utxo->deposit.vout;
|
|
|
|
if ( qp->desttxfee >= qp->destsatoshis )
|
|
|
|
{
|
|
|
|
printf("quoteinit desttxfee %.8f < %.8f destsatoshis\n",dstr(qp->desttxfee),dstr(qp->destsatoshis));
|
|
|
|
return(-2);
|
|
|
|
}
|
|
|
|
safecopy(qp->srccoin,utxo->coin,sizeof(qp->srccoin));
|
|
|
|
safecopy(qp->coinaddr,utxo->coinaddr,sizeof(qp->coinaddr));
|
|
|
|
qp->srchash = utxo->pubkey;
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_quotedestinfo(struct LP_quoteinfo *qp,bits256 desttxid,int32_t destvout,bits256 feetxid,int32_t feevout,bits256 desthash,char *destaddr)
|
|
|
|
{
|
|
|
|
qp->desttxid = desttxid;
|
|
|
|
qp->destvout = destvout;
|
|
|
|
qp->desthash = desthash;
|
|
|
|
qp->feetxid = feetxid;
|
|
|
|
qp->feevout = feevout;
|
|
|
|
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.satoshis - Q.txfee);
|
|
|
|
if ( (ptr= LP_cacheadd(Q.srccoin,Q.destcoin,Q.txid,Q.vout,price,&Q)) != 0 )
|
|
|
|
{
|
|
|
|
ptr->Q = Q;
|
|
|
|
printf(">>>>>>>>>> received quote %s/%s %.8f\n",Q.srccoin,Q.destcoin,price);
|
|
|
|
return(clonestr("{\"result\":\"updated\"}"));
|
|
|
|
} else return(clonestr("{\"error\":\"nullptr\"}"));
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_bitcoinsig_add(cJSON *item,bits256 priv,uint8_t *pubsecp,bits256 sighash)
|
|
|
|
{
|
|
|
|
static void *ctx; int32_t i,j,siglen; uint8_t pub33[33],sig[65]; char sigstr[128];
|
|
|
|
if ( ctx == 0 )
|
|
|
|
ctx = bitcoin_ctx();
|
|
|
|
for (j=0; j<100; j++)
|
|
|
|
{
|
|
|
|
if ( (siglen= bitcoin_sign(ctx,"sigadd",sig,sighash,priv,1)) > 0 && siglen == 65 )
|
|
|
|
{
|
|
|
|
memset(pub33,0,33);
|
|
|
|
if ( bitcoin_recoververify(ctx,"test",sig,sighash,pub33,0) == 0 && memcmp(pub33,pubsecp,33) == 0 )
|
|
|
|
{
|
|
|
|
init_hexbytes_noT(sigstr,sig,siglen);
|
|
|
|
jaddstr(item,"sig",sigstr);
|
|
|
|
return(siglen);
|
|
|
|
}
|
|
|
|
if ( 0 )
|
|
|
|
{
|
|
|
|
for (i=0; i<33; i++)
|
|
|
|
printf("%02x",pubsecp[i]);
|
|
|
|
printf(" pubsecp -> ");
|
|
|
|
for (i=0; i<33; i++)
|
|
|
|
printf("%02x",pub33[i]);
|
|
|
|
printf(" mismatched recovered pubkey.%d of %d\n",j,100);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(-1);
|
|
|
|
}
|
|
|
|
|
|
|
|
bits256 LP_price_sighash(uint32_t timestamp,uint8_t *pubsecp,bits256 pubkey,char *base,char *rel,uint64_t price64)
|
|
|
|
{
|
|
|
|
uint8_t buf[sizeof(pubkey) + 33 + sizeof(uint64_t)*3 + sizeof(timestamp)]; uint64_t basebits,relbits; bits256 sighash;
|
|
|
|
basebits = stringbits(base);
|
|
|
|
relbits = stringbits(rel);
|
|
|
|
memcpy(buf,pubkey.bytes,sizeof(pubkey));
|
|
|
|
memcpy(&buf[sizeof(pubkey)],pubsecp,33);
|
|
|
|
memcpy(&buf[sizeof(pubkey)+33],&price64,sizeof(price64));
|
|
|
|
memcpy(&buf[sizeof(pubkey)+33+sizeof(price64)],&basebits,sizeof(basebits));
|
|
|
|
memcpy(&buf[sizeof(pubkey)+33+sizeof(price64)+sizeof(basebits)],&relbits,sizeof(relbits));
|
|
|
|
memcpy(&buf[sizeof(pubkey)+33+sizeof(price64)+sizeof(basebits)+sizeof(relbits)],×tamp,sizeof(timestamp));
|
|
|
|
vcalc_sha256(0,sighash.bytes,buf,sizeof(buf));
|
|
|
|
return(sighash);
|
|
|
|
}
|
|
|
|
|
|
|
|
bits256 LP_pubkey_sighash(uint32_t timestamp,bits256 pubkey,uint8_t *rmd160,uint8_t *pubsecp)
|
|
|
|
{
|
|
|
|
uint8_t buf[sizeof(pubkey) + 20 + 33 + sizeof(timestamp)]; bits256 sighash;
|
|
|
|
memcpy(buf,pubkey.bytes,sizeof(pubkey));
|
|
|
|
memcpy(&buf[sizeof(pubkey)],rmd160,20);
|
|
|
|
memcpy(&buf[sizeof(pubkey)+20],pubsecp,33);
|
|
|
|
memcpy(&buf[sizeof(pubkey)+20+33],×tamp,sizeof(timestamp));
|
|
|
|
vcalc_sha256(0,sighash.bytes,buf,sizeof(buf));
|
|
|
|
return(sighash);
|
|
|
|
}
|
|
|
|
|
|
|
|
bits256 LP_utxos_sighash(uint32_t timestamp,uint8_t *pubsecp,bits256 pubkey,bits256 utxoshash)
|
|
|
|
{
|
|
|
|
uint8_t buf[sizeof(pubkey)+sizeof(utxoshash)+33+sizeof(timestamp)]; bits256 sighash;
|
|
|
|
memcpy(buf,pubkey.bytes,sizeof(pubkey));
|
|
|
|
memcpy(&buf[sizeof(pubkey)],pubsecp,33);
|
|
|
|
memcpy(&buf[sizeof(pubkey)+33],×tamp,sizeof(timestamp));
|
|
|
|
memcpy(&buf[sizeof(pubkey)+33+sizeof(timestamp)],utxoshash.bytes,sizeof(utxoshash));
|
|
|
|
vcalc_sha256(0,sighash.bytes,buf,sizeof(buf));
|
|
|
|
return(sighash);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_utxos_sigcheck(uint32_t timestamp,char *sigstr,char *pubsecpstr,bits256 pubkey,bits256 utxoshash)
|
|
|
|
{
|
|
|
|
static void *ctx; int32_t retval=-1; uint8_t pub33[33],pubsecp[33],sig[65]; bits256 sighash; char str[65];
|
|
|
|
if ( ctx == 0 )
|
|
|
|
ctx = bitcoin_ctx();
|
|
|
|
if ( sigstr != 0 && pubsecpstr != 0 && strlen(sigstr) == 65*2 && strlen(pubsecpstr) == 33 *2 )
|
|
|
|
{
|
|
|
|
decode_hex(sig,65,sigstr);
|
|
|
|
decode_hex(pubsecp,33,pubsecpstr);
|
|
|
|
sighash = LP_utxos_sighash(timestamp,pubsecp,pubkey,utxoshash);
|
|
|
|
retval = bitcoin_recoververify(ctx,"utxos",sig,sighash,pub33,0);
|
|
|
|
if ( memcmp(pub33,pubsecp,33) != 0 || retval != 0 )
|
|
|
|
{
|
|
|
|
static uint32_t counter;
|
|
|
|
if ( counter++ < 100 )
|
|
|
|
printf("LP_utxos_sigcheck failure, probably from %s with older version\n",bits256_str(str,pubkey));
|
|
|
|
retval = -1;
|
|
|
|
} else retval = 0;
|
|
|
|
}
|
|
|
|
return(retval);
|
|
|
|
}
|
|
|
|
|
|
|
|
bits256 LP_utxoshash_calc(cJSON *array)
|
|
|
|
{
|
|
|
|
int32_t i,j,n; bits256 utxoshash,txid; cJSON *item;
|
|
|
|
memset(utxoshash.bytes,0,sizeof(utxoshash));
|
|
|
|
if ( (n= cJSON_GetArraySize(array)) > 0 )
|
|
|
|
{
|
|
|
|
for (i=0; i<n; i++)
|
|
|
|
{
|
|
|
|
item = jitem(array,i);
|
|
|
|
txid = jbits256(item,"tx_hash");
|
|
|
|
for (j=0; j<4; j++)
|
|
|
|
utxoshash.ulongs[j] ^= txid.ulongs[j];
|
|
|
|
utxoshash.uints[0] ^= jint(item,"tx_pos");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(utxoshash);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_utxos_sigadd(cJSON *item,uint32_t timestamp,bits256 priv,uint8_t *pubsecp,bits256 pubkey,bits256 utxoshash)
|
|
|
|
{
|
|
|
|
bits256 sighash;
|
|
|
|
sighash = LP_utxos_sighash(timestamp,pubsecp,pubkey,utxoshash);
|
|
|
|
return(LP_bitcoinsig_add(item,priv,pubsecp,sighash));
|
|
|
|
}
|
|
|
|
|
|
|
|
void LP_postutxos(char *symbol,char *coinaddr)
|
|
|
|
{
|
|
|
|
bits256 zero; uint32_t timestamp; bits256 utxoshash; char pubsecpstr[67]; struct iguana_info *coin; cJSON *array,*reqjson = cJSON_CreateObject();
|
|
|
|
if ( (coin= LP_coinfind(symbol)) != 0 && (array= LP_address_utxos(coin,coinaddr,1)) != 0 )
|
|
|
|
{
|
|
|
|
//printf("LP_postutxos pubsock.%d %s %s\n",pubsock,symbol,coin->smartaddr);
|
|
|
|
if ( cJSON_GetArraySize(array) == 0 )
|
|
|
|
free_json(array);
|
|
|
|
else
|
|
|
|
{
|
|
|
|
memset(zero.bytes,0,sizeof(zero));
|
|
|
|
jaddstr(reqjson,"method","postutxos");
|
|
|
|
jaddstr(reqjson,"coin",symbol);
|
|
|
|
jaddstr(reqjson,"coinaddr",coinaddr);
|
|
|
|
jadd(reqjson,"utxos",array);
|
|
|
|
timestamp = (uint32_t)time(NULL);
|
|
|
|
jaddnum(reqjson,"timetamp",timestamp);
|
|
|
|
init_hexbytes_noT(pubsecpstr,G.LP_pubsecp,33);
|
|
|
|
jaddstr(reqjson,"pubsecp",pubsecpstr);
|
|
|
|
jaddbits256(reqjson,"pubkey",G.LP_mypub25519);
|
|
|
|
utxoshash = LP_utxoshash_calc(array);
|
|
|
|
//char str[65]; printf("utxoshash add %s\n",bits256_str(str,utxoshash));
|
|
|
|
LP_utxos_sigadd(reqjson,timestamp,G.LP_privkey,G.LP_pubsecp,G.LP_mypub25519,utxoshash);
|
|
|
|
//printf("post (%s) -> %d\n",msg,LP_mypubsock);
|
|
|
|
LP_reserved_msg(symbol,symbol,zero,jprint(reqjson,1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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; cJSON *obj;
|
|
|
|
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"),jbits256(argjson,"pubkey"),utxoshash) == 0 )
|
|
|
|
{
|
|
|
|
uitem = calloc(1,sizeof(*uitem));
|
|
|
|
uitem->argjson = jduplicate(argjson);
|
|
|
|
queue_enqueue("utxosQ",&utxosQ,&uitem->DL);
|
|
|
|
return(clonestr("{\"result\":\"success\"}"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(clonestr("{\"error\":\"sig failure\"}"));
|
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_price_sigcheck(uint32_t timestamp,char *sigstr,char *pubsecpstr,bits256 pubkey,char *base,char *rel,uint64_t price64)
|
|
|
|
{
|
|
|
|
static void *ctx; int32_t retval=-1; uint8_t pub33[33],pubsecp[33],sig[65]; bits256 sighash;
|
|
|
|
if ( ctx == 0 )
|
|
|
|
ctx = bitcoin_ctx();
|
|
|
|
if ( sigstr != 0 && pubsecpstr != 0 && strlen(sigstr) == 65*2 && strlen(pubsecpstr) == 33 *2 )
|
|
|
|
{
|
|
|
|
decode_hex(sig,65,sigstr);
|
|
|
|
decode_hex(pubsecp,33,pubsecpstr);
|
|
|
|
sighash = LP_price_sighash(timestamp,pubsecp,pubkey,base,rel,price64);
|
|
|
|
retval = bitcoin_recoververify(ctx,"price",sig,sighash,pub33,0);
|
|
|
|
if ( memcmp(pub33,pubsecp,33) != 0 || retval != 0 )
|
|
|
|
{
|
|
|
|
printf("LP_price_sigcheck failure\n");
|
|
|
|
retval = -1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
retval = 0;
|
|
|
|
//printf("valid price sig %s/%s %.8f\n",base,rel,dstr(price64));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(retval);
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_price_sigadd(cJSON *item,uint32_t timestamp,bits256 priv,uint8_t *pubsecp,bits256 pubkey,char *base,char *rel,uint64_t price64)
|
|
|
|
{
|
|
|
|
bits256 sighash;
|
|
|
|
sighash = LP_price_sighash(timestamp,pubsecp,pubkey,base,rel,price64);
|
|
|
|
return(LP_bitcoinsig_add(item,priv,pubsecp,sighash));
|
|
|
|
}
|
|
|
|
|
|
|
|
char *LP_pricepings(void *ctx,char *myipaddr,int32_t pubsock,char *base,char *rel,double price)
|
|
|
|
{
|
|
|
|
struct iguana_info *basecoin,*relcoin; char pubsecpstr[67]; uint32_t timestamp; uint64_t price64; bits256 zero; cJSON *reqjson = cJSON_CreateObject();
|
|
|
|
// LP_addsig
|
|
|
|
if ( (basecoin= LP_coinfind(base)) != 0 && (relcoin= LP_coinfind(rel)) != 0 && basecoin->electrum == 0 && relcoin->electrum == 0 )
|
|
|
|
{
|
|
|
|
memset(zero.bytes,0,sizeof(zero));
|
|
|
|
jaddbits256(reqjson,"pubkey",G.LP_mypub25519);
|
|
|
|
jaddstr(reqjson,"base",base);
|
|
|
|
jaddstr(reqjson,"rel",rel);
|
|
|
|
price64 = price * SATOSHIDEN + 0.0000000049;
|
|
|
|
jaddnum(reqjson,"price",price);
|
|
|
|
jadd64bits(reqjson,"price64",price64);
|
|
|
|
jaddstr(reqjson,"method","postprice");
|
|
|
|
timestamp = (uint32_t)time(NULL);
|
|
|
|
jaddnum(reqjson,"timestamp",timestamp);
|
|
|
|
init_hexbytes_noT(pubsecpstr,G.LP_pubsecp,33);
|
|
|
|
jaddstr(reqjson,"pubsecp",pubsecpstr);
|
|
|
|
LP_price_sigadd(reqjson,timestamp,G.LP_privkey,G.LP_pubsecp,G.LP_mypub25519,base,rel,price64);
|
|
|
|
LP_reserved_msg(base,rel,zero,jprint(reqjson,1));
|
|
|
|
return(clonestr("{\"result\":\"success\"}"));
|
|
|
|
} else return(clonestr("{\"error\":\"electrum node cant post bob asks\"}"));
|
|
|
|
}
|
|
|
|
|
|
|
|
char *LP_postprice_recv(cJSON *argjson)
|
|
|
|
{
|
|
|
|
bits256 pubkey; double price; char *base,*rel;
|
|
|
|
//printf("PRICE POSTED.(%s)\n",jprint(argjson,0));
|
|
|
|
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 )
|
|
|
|
{
|
|
|
|
if ( LP_price_sigcheck(juint(argjson,"timestamp"),jstr(argjson,"sig"),jstr(argjson,"pubsecp"),pubkey,base,rel,j64bits(argjson,"price64")) == 0 )
|
|
|
|
{
|
|
|
|
LP_pricefeedupdate(pubkey,base,rel,price);
|
|
|
|
return(clonestr("{\"result\":\"success\"}"));
|
|
|
|
} else return(clonestr("{\"error\":\"sig failure\"}"));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(clonestr("{\"error\":\"missing fields in posted price\"}"));
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t _LP_pubkey_sigcheck(uint8_t *sig,int32_t siglen,uint32_t timestamp,bits256 pub,uint8_t *rmd160,uint8_t *pubsecp)
|
|
|
|
{
|
|
|
|
static void *ctx; uint8_t pub33[33]; bits256 sighash;
|
|
|
|
if ( ctx == 0 )
|
|
|
|
ctx = bitcoin_ctx();
|
|
|
|
sighash = LP_pubkey_sighash(timestamp,pub,rmd160,pubsecp);
|
|
|
|
return(bitcoin_recoververify(ctx,"pubkey",sig,sighash,pub33,0));
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_pubkey_sigadd(cJSON *item,uint32_t timestamp,bits256 priv,bits256 pub,uint8_t *rmd160,uint8_t *pubsecp)
|
|
|
|
{
|
|
|
|
bits256 sighash;
|
|
|
|
sighash = LP_pubkey_sighash(timestamp,pub,rmd160,pubsecp);
|
|
|
|
return(LP_bitcoinsig_add(item,priv,pubsecp,sighash));
|
|
|
|
}
|
|
|
|
|
|
|
|
int32_t LP_pubkey_sigcheck(struct LP_pubkeyinfo *pubp,cJSON *item)
|
|
|
|
{
|
|
|
|
int32_t i,len,siglen,retval=-1; uint8_t rmd160[20],checkrmd160[20],pubsecp[33],sig[65],zeroes[20]; char *sigstr,*hexstr,*pubsecpstr;
|
|
|
|
if ( (hexstr= jstr(item,"rmd160")) != 0 && strlen(hexstr) == 2*sizeof(rmd160) )
|
|
|
|
{
|
|
|
|
decode_hex(rmd160,sizeof(rmd160),hexstr);
|
|
|
|
memset(zeroes,0,sizeof(zeroes));
|
|
|
|
if ( memcmp(zeroes,rmd160,sizeof(rmd160)) != 0 )
|
|
|
|
{
|
|
|
|
if ( memcmp(rmd160,pubp->rmd160,20) != 0 )
|
|
|
|
{
|
|
|
|
if ( (pubsecpstr= jstr(item,"pubsecp")) != 0 && is_hexstr(pubsecpstr,0) == 66 )
|
|
|
|
{
|
|
|
|
decode_hex(pubsecp,sizeof(pubsecp),pubsecpstr);
|
|
|
|
calc_rmd160_sha256(checkrmd160,pubsecp,33);
|
|
|
|
if ( memcmp(checkrmd160,rmd160,20) == 0 )
|
|
|
|
{
|
|
|
|
if ( (sigstr= jstr(item,"sig")) != 0 && (len= is_hexstr(sigstr,0)) == 65*2 )
|
|
|
|
{
|
|
|
|
siglen = len >> 1;
|
|
|
|
decode_hex(sig,siglen,sigstr);
|
|
|
|
if ( _LP_pubkey_sigcheck(sig,siglen,juint(item,"timestamp"),pubp->pubkey,rmd160,pubsecp) == 0 )
|
|
|
|
{
|
|
|
|
for (i=0; i<20; i++)
|
|
|
|
printf("%02x",pubp->rmd160[i]);
|
|
|
|
memcpy(pubp->rmd160,rmd160,sizeof(pubp->rmd160));
|
|
|
|
memcpy(pubp->pubsecp,pubsecp,sizeof(pubp->pubsecp));
|
|
|
|
memcpy(pubp->sig,sig,sizeof(pubp->sig));
|
|
|
|
pubp->siglen = siglen;
|
|
|
|
char str[65]; printf(" -> rmd160.(%s) for %s (%s) sig.%s\n",hexstr,bits256_str(str,pubp->pubkey),pubsecpstr,sigstr);
|
|
|
|
retval = 0;
|
|
|
|
pubp->timestamp = (uint32_t)time(NULL);
|
|
|
|
} //else printf("sig %s error pub33.%s\n",sigstr,pubsecpstr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for (i=0; i<20; i++)
|
|
|
|
printf("%02x",rmd160[i]);
|
|
|
|
printf(" rmd160 vs ");
|
|
|
|
for (i=0; i<20; i++)
|
|
|
|
printf("%02x",checkrmd160[i]);
|
|
|
|
printf(" for %s\n",pubsecpstr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else pubp->timestamp = (uint32_t)time(NULL);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(retval);
|
|
|
|
}
|
|
|
|
|
|
|
|
void LP_notify_pubkeys(void *ctx,int32_t pubsock)
|
|
|
|
{
|
|
|
|
bits256 zero; uint32_t timestamp; char secpstr[67]; cJSON *reqjson = cJSON_CreateObject();
|
|
|
|
memset(zero.bytes,0,sizeof(zero));
|
|
|
|
jaddstr(reqjson,"method","notify");
|
|
|
|
jaddstr(reqjson,"rmd160",G.LP_myrmd160str);
|
|
|
|
jaddbits256(reqjson,"pub",G.LP_mypub25519);
|
|
|
|
init_hexbytes_noT(secpstr,G.LP_pubsecp,33);
|
|
|
|
jaddstr(reqjson,"pubsecp",secpstr);
|
|
|
|
timestamp = (uint32_t)time(NULL);
|
|
|
|
jaddnum(reqjson,"timestamp",timestamp);
|
|
|
|
LP_pubkey_sigadd(reqjson,timestamp,G.LP_privkey,G.LP_mypub25519,G.LP_myrmd160,G.LP_pubsecp);
|
|
|
|
LP_reserved_msg("","",zero,jprint(reqjson,1));
|
|
|
|
}
|
|
|
|
|
|
|
|
char *LP_notify_recv(cJSON *argjson)
|
|
|
|
{
|
|
|
|
bits256 pub; struct LP_pubkeyinfo *pubp;
|
|
|
|
pub = jbits256(argjson,"pub");
|
|
|
|
if ( bits256_nonz(pub) != 0 )
|
|
|
|
{
|
|
|
|
if ( (pubp= LP_pubkeyadd(pub)) != 0 )
|
|
|
|
LP_pubkey_sigcheck(pubp,argjson);
|
|
|
|
//char str[65]; printf("%.3f NOTIFIED pub %s rmd160 %s\n",OS_milliseconds()-millis,bits256_str(str,pub),rmd160str);
|
|
|
|
}
|
|
|
|
return(clonestr("{\"result\":\"success\",\"notify\":\"received\"}"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void LP_smartutxos_push(struct iguana_info *coin)
|
|
|
|
{
|
|
|
|
struct LP_peerinfo *peer,*tmp; uint64_t value; bits256 zero,txid; int32_t i,vout,height,n; char *retstr; cJSON *array,*item,*req;
|
|
|
|
if ( coin->smartaddr[0] == 0 )
|
|
|
|
return;
|
|
|
|
LP_notify_pubkeys(coin->ctx,LP_mypubsock);
|
|
|
|
if ( (array= LP_address_utxos(coin,coin->smartaddr,1)) != 0 )
|
|
|
|
{
|
|
|
|
memset(zero.bytes,0,sizeof(zero));
|
|
|
|
if ( (n= cJSON_GetArraySize(array)) > 0 )
|
|
|
|
{
|
|
|
|
//printf("PUSH %s %s\n",coin->symbol,coin->smartaddr);
|
|
|
|
for (i=0; i<n; i++)
|
|
|
|
{
|
|
|
|
item = jitem(array,i);
|
|
|
|
txid = jbits256(item,"tx_hash");
|
|
|
|
vout = jint(item,"tx_pos");
|
|
|
|
value = j64bits(item,"value");
|
|
|
|
height = jint(item,"height");
|
|
|
|
if ( 0 && (rand() % 100) == 0 && IAMLP == 0 )
|
|
|
|
{
|
|
|
|
HASH_ITER(hh,LP_peerinfos,peer,tmp)
|
|
|
|
{
|
|
|
|
if ( (retstr= issue_LP_uitem(peer->ipaddr,peer->port,coin->symbol,coin->smartaddr,txid,vout,height,value)) != 0 )
|
|
|
|
free(retstr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
req = cJSON_CreateObject();
|
|
|
|
jaddstr(req,"method","uitem");
|
|
|
|
jaddstr(req,"coin",coin->symbol);
|
|
|
|
jaddstr(req,"coinaddr",coin->smartaddr);
|
|
|
|
jaddbits256(req,"txid",txid);
|
|
|
|
jaddnum(req,"vout",vout);
|
|
|
|
jaddnum(req,"ht",height);
|
|
|
|
jadd64bits(req,"value",value);
|
|
|
|
//printf("ADDR_UNSPENTS[] <- %s\n",jprint(req,0));
|
|
|
|
LP_reserved_msg("","",zero,jprint(req,1));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free_json(array);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
char *LP_uitem_recv(cJSON *argjson)
|
|
|
|
{
|
|
|
|
bits256 txid; int32_t vout,height; uint64_t value; struct iguana_info *coin; char *coinaddr,*symbol;
|
|
|
|
txid = jbits256(argjson,"txid");
|
|
|
|
vout = jint(argjson,"vout");
|
|
|
|
height = jint(argjson,"ht");
|
|
|
|
value = j64bits(argjson,"value");
|
|
|
|
coinaddr = jstr(argjson,"coinaddr");
|
|
|
|
if ( (symbol= jstr(argjson,"coin")) != 0 && coinaddr != 0 && (coin= LP_coinfind(symbol)) != 0 )
|
|
|
|
{
|
|
|
|
//char str[65]; printf("uitem %s %s %s/v%d %.8f ht.%d\n",symbol,coinaddr,bits256_str(str,txid),vout,dstr(value),height);
|
|
|
|
if ( strcmp(coin->smartaddr,coinaddr) != 0 )
|
|
|
|
LP_address_utxoadd("LP_uitem,recv",coin,coinaddr,txid,vout,value,height,-1);
|
|
|
|
else printf("ignore external uitem %s %s\n",symbol,coin->smartaddr);
|
|
|
|
}
|
|
|
|
return(clonestr("{\"result\":\"success\"}"));
|
|
|
|
}
|
|
|
|
|
|
|
|
void LP_listunspent_query(char *symbol,char *coinaddr)
|
|
|
|
{
|
|
|
|
bits256 zero; cJSON *reqjson = cJSON_CreateObject();
|
|
|
|
memset(zero.bytes,0,sizeof(zero));
|
|
|
|
jaddstr(reqjson,"method","addr_unspents");
|
|
|
|
jaddstr(reqjson,"coin",symbol);
|
|
|
|
jaddstr(reqjson,"address",coinaddr);
|
|
|
|
LP_reserved_msg("","",zero,jprint(reqjson,1));
|
|
|
|
}
|
|
|
|
|
|
|
|
void LP_query(void *ctx,char *myipaddr,int32_t mypubsock,char *method,struct LP_quoteinfo *qp)
|
|
|
|
{
|
|
|
|
cJSON *reqjson; bits256 zero; char *msg,*msg2; int32_t flag = 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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
reqjson = LP_quotejson(qp);
|
|
|
|
if ( bits256_nonz(qp->desthash) != 0 )
|
|
|
|
flag = 1;
|
|
|
|
jaddbits256(reqjson,"pubkey",qp->srchash);
|
|
|
|
jaddstr(reqjson,"method",method);
|
|
|
|
jaddnum(reqjson,"timestamp",time(NULL));
|
|
|
|
msg = jprint(reqjson,1);
|
|
|
|
msg2 = clonestr(msg);
|
|
|
|
// LP_addsig
|
|
|
|
printf("QUERY.(%s)\n",msg);
|
|
|
|
memset(&zero,0,sizeof(zero));
|
|
|
|
portable_mutex_lock(&LP_reservedmutex);
|
|
|
|
if ( num_Reserved_msgs < sizeof(Reserved_msgs)/sizeof(*Reserved_msgs)-2 )
|
|
|
|
{
|
|
|
|
Reserved_msgs[num_Reserved_msgs++] = msg;
|
|
|
|
//Reserved_msgs[num_Reserved_msgs++] = msg2;
|
|
|
|
}
|
|
|
|
LP_broadcast_message(LP_mypubsock,qp->srccoin,qp->destcoin,zero,msg2);
|
|
|
|
portable_mutex_unlock(&LP_reservedmutex);
|
|
|
|
}
|
|
|
|
|