Hush Full Node software. We were censored from Github, this is where all development happens now. https://hush.is
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.
 
 
 
 
 
 

1383 lines
56 KiB

// Copyright (c) 2016-2020 The Hush developers
/******************************************************************************
* Copyright © 2014-2020 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. *
* *
******************************************************************************/
// build hushdex and put in path: git pull; gcc cc/dapps/hushdex.c -lm -o hushdex; cp hushdex /usr/bin
// alice sends relcoin and gets basecoin
#define DEXP2P_CHAIN ((char *)"HUSHDEX")
#define DEXP2P_PUBKEYS ((char *)"hushdex")
#include "dappinc.h"
// for OTC mode, the following 4 functions are the only ones that should be needed to support a new "coin"
//int64_t hushdex_getbalance(char *coin);
//bits256 hushdex_coinpayment(int32_t OTCmode,char *coin,char *destaddr,uint64_t paytoshis,char *memostr);
//cJSON *hushdex_txidwait(char *coin,bits256 txid,char *hexstr,int32_t numseconds);
//int64_t hushdex_verifypayment(char *coin,cJSON *rawtx,uint64_t destsatoshis,char *destaddr);
// TODO:
// address conversion
// new inventory types:
// anonsend
// bob nodes:
// mutex for bob instances
// "deposits" messages and approved bobs
// volume caps per coin and non-notarized exposure
// later:
// sharded storage
#define SUBATOMIC_OTCDEFAULT 1
#define SUBATOMIC_TIMEOUT 60
#define SUBATOMIC_LOCKTIME 3600
#define SUBATOMIC_TXFEE 10000
#define SUBATOMIC_PRIORITY 5
#define SUBATOMIC_OPENREQUEST 1
#define SUBATOMIC_APPROVED 2
#define SUBATOMIC_OPENED 3
#define SUBATOMIC_PAYMENT 4
#define SUBATOMIC_PAIDINFULL 5
#define SUBATOMIC_CLOSED 6
cJSON *SUBATOMIC_json;
int32_t SUBATOMIC_retval = -1;
struct abinfo
{
char pubkey[67],recvaddr[64],recvZaddr[128],secp[67];
};
struct coininfo
{
uint64_t satoshis,txfee,maxamount;
char istoken,iszaddr,isfile,isexternal,tokenid[65],coin[16],name[16],cli[256],acname[16],coinstr[16];
};
struct msginfo
{
UT_hash_handle hh;
bits256 bobpayment,alicepayment;
double price;
uint64_t gotpayment;
uint32_t origid,openrequestid,approvalid,openedid,paymentids[100],paidid,closedid,locktime;
int32_t bobflag,status,OTCmode;
char payload[128],approval[128],senderpub[67],msigaddr[64],redeemscript[256];
struct coininfo base,rel;
struct abinfo alice,bob;
} *Messages;
uint64_t hushdex_txfee(char *coin)
{
return(SUBATOMIC_TXFEE);
}
char *hushdex_checkname(char *tmpstr,struct msginfo *mp,int32_t baserel,char *coin)
{
int32_t i,n; cJSON *external,*item; char *coinstr,*clistr; struct coininfo *ptr;
ptr = (baserel == 0) ? &mp->base : &mp->rel;
if ( coin[0] == 0 )
return(coin);
if ( (external= jarray(&n,SUBATOMIC_json,"externalcoins")) != 0 && n > 0 )
{
for (i=0; i<n; i++)
{
item = jitem(external,i);
if ( (clistr= jstr(item,coin)) != 0 && strlen(clistr) < sizeof(ptr->cli) )
{
ptr->isexternal = 1;
strcpy(ptr->cli,clistr);
//fprintf(stderr,"found external coin %s %s\n",coin,clistr);
}
}
}
if ( coin[0] == '#' )
{
strcpy(ptr->coinstr,coin);
strcpy(ptr->acname,"");
ptr->isfile = 1;
return(coin);
}
else if ( coin[0] != 'z' )
{
for (i=1; coin[i]!=0; i++)
if ( coin[i] == '.' )
{
dpow_tokenregister(ptr->tokenid,0,coin,0);
if ( ptr->tokenid[0] != 0 )
{
strcpy(tmpstr,coin);
tmpstr[i] = 0;
//fprintf(stderr,"found a tokenmap %s -> %s %s\n",coin,tmpstr,ptr->tokenid);
ptr->istoken = 1;
strcpy(ptr->acname,coin);
strcpy(ptr->coinstr,"");
return(tmpstr);
}
}
if ( ptr->isexternal == 0 )
{
strcpy(ptr->acname,coin);
strcpy(ptr->coinstr,"");
strcpy(ptr->acname,"");
}
else
{
strcpy(ptr->coinstr,coin);
strcpy(ptr->acname,"");
}
return(coin);
}
else
{
for (i=1; coin[i]!=0; i++)
if ( isupper(coin[i]) == 0 )
return(coin);
ptr->iszaddr = 1;
return(coin+1);
}
}
int32_t hushdex_zonly(struct coininfo *coin)
{
if ( strcmp(coin->coin,"HUSH3") == 0 )
return(1);
if ( strcmp(coin->coin,"HUSHFILE") == 0 )
return(1);
return 0;
}
// //////////////////////////////// the four key functions needed to support a new item for hushdexs
int64_t _hushdex_getbalance(struct coininfo *coin)
{
cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0;
if ( (retjson= hushdex_cli(coin->cli,&retstr,"getbalance","","","","","","","")) != 0 )
{
fprintf(stderr,"_hushdex_getbalance.(%s) %s returned json!\n",coin->coinstr,coin->cli);
free_json(retjson);
}
else if ( retstr != 0 )
{
amount = atof(retstr) * SATOSHIDEN;
sprintf(cmpstr,"%.8f",dstr(amount));
if ( strcmp(retstr,cmpstr) != 0 )
amount++;
//printf("retstr %s -> %.8f\n",retstr,dstr(amount));
free(retstr);
}
return (amount);
}
bits256 _hushdex_sendtoaddress(struct coininfo *coin,char *destaddr,int64_t satoshis)
{
char numstr[32],*retstr,str[65]; cJSON *retjson; bits256 txid;
memset(txid.bytes,0,sizeof(txid));
sprintf(numstr,"%.8f",(double)satoshis/SATOSHIDEN);
if ( (retjson= hushdex_cli(coin->cli,&retstr,"sendtoaddress",destaddr,numstr,"false","","","","")) != 0 )
{
fprintf(stderr,"unexpected _hushdex_sendtoaddress json.(%s)\n",jprint(retjson,0));
free_json(retjson);
}
else if ( retstr != 0 )
{
if ( strlen(retstr) >= 64 )
{
retstr[64] = 0;
decode_hex(txid.bytes,32,retstr);
}
fprintf(stderr,"_hushdex_sendtoaddress %s %.8f txid.(%s)\n",destaddr,(double)satoshis/SATOSHIDEN,bits256_str(str,txid));
free(retstr);
}
return(txid);
}
cJSON *_hushdex_rawtransaction(struct coininfo *coin,bits256 txid)
{
cJSON *retjson; char *retstr,str[65];
if ( (retjson= hushdex_cli(coin->cli,&retstr,"getrawtransaction",bits256_str(str,txid),"1","","","","","")) != 0 )
{
return(retjson);
}
else if ( retstr != 0 )
{
fprintf(stderr,"_hushdex_rawtransaction.(%s) %s error.(%s)\n",coin->coin,coin->name,retstr);
free(retstr);
}
return(0);
}
int64_t hushdex_getbalance(struct coininfo *coin)
{
char *coinstr,*acname=""; FILE *fp; int64_t retval = 0;
coinstr = coin->coin;
if ( coin->isfile != 0 )
{
if ( (fp= fopen(coin->name+1,"rb")) != 0 ) // if alice, add bob pubkey to fname
{
fclose(fp);
retval = SATOSHIDEN;
}
return(retval);
} else if ( hushdex_zonly(coin) != 0 ) {
return(z_getbalance(coinstr,acname,DPOW_recvZaddr));
}
}
bits256 hushdex_coinpayment(uint32_t origid,int32_t OTCmode,struct coininfo *coin,char *destaddr,uint64_t paytoshis,char *memostr,char *destpub,char *senderpub)
{
bits256 txid; char opidstr[128],opretstr[32],str[65],*status,*coinstr,*acname=""; cJSON *retjson,*retjson2,*item,*res; int32_t i,pending=0;
memset(&txid,0,sizeof(txid));
if ( OTCmode == 0 )
{
fprintf(stderr,"micropayment channels are not supported yet\n");
return(txid);
}
if ( coin->isfile != 0 )
{
fprintf(stderr,"start broadcast of (%s)\n",coin->coin+1);
if ( (retjson= dpow_publish(SUBATOMIC_PRIORITY,coin->coin+1)) != 0 ) // spawn thread
{
sprintf(opretstr,"%08x",juint(retjson,"id"));
sprintf(opidstr,"%u",origid);
if ( (retjson2= dpow_broadcast(SUBATOMIC_PRIORITY,opretstr,"inbox",opidstr,senderpub,"","")) != 0 )
free_json(retjson2);
fprintf(stderr,"broadcast file.(%s) and send id.%u to alice (%s)\n",coin->coin+1,juint(retjson,"id"),jprint(retjson,0));
txid = jbits256(retjson,"filehash");
free_json(retjson);
}
fprintf(stderr,"end broadcast of (%s) to %s\n",coin->coin+1,senderpub);
return(txid);
}
else if ( hushdex_zonly(coin) != 0 )
{
if ( memostr[0] == 0 )
memostr = "beef";
z_sendmany(opidstr,"",coin->coin,DPOW_recvZaddr,destaddr,paytoshis,memostr);
for (i=0; i<SUBATOMIC_TIMEOUT; i++)
{
if ( (retjson= z_getoperationstatus("",coin->coin,opidstr)) != 0 )
{
item = jitem(retjson,0);
if ( (status= jstr(item,"status")) != 0 )
{
if ( strcmp(status,"executing") == 0 )
pending++;
else
{
res = jobj(item,"result");
txid = jbits256(res,"txid");
//fprintf(stderr,"got Ztx txid.%s\n",bits256_str(str,txid));
free_json(retjson);
break;
}
/*else if ( clearresults != 0 )
{
if ( (result= z_getoperationresult(coinstr,"",jstri(array,i))) != 0 )
{
free_json(result);
}
}*/
}
free_json(retjson);
}
sleep(1);
}
if ( i == 60 )
printf("%u timed out waiting for opid to finish\n",origid);
}
else
{
coinstr = coin->coin;
if ( coin->istoken != 0 )
txid = tokentransfer(coinstr,acname,coin->tokenid,destpub,paytoshis/SATOSHIDEN);
else if ( coin->isexternal == 0 )
{
sprintf(opretstr,"%08x",origid);
txid = sendtoaddress(coinstr,acname,destaddr,paytoshis,opretstr);
} else txid = _hushdex_sendtoaddress(coin,destaddr,paytoshis);
printf("%u got txid.%s\n",origid,bits256_str(str,txid));
}
return(txid);
}
cJSON *hushdex_txidwait(struct coininfo *coin,bits256 txid,char *hexstr,int32_t numseconds,char *senderpub)
{
int32_t i,zflag; char *coinstr,str[65],*acname=""; cJSON *rawtx; bits256 z; bits256 filehash;
memset(&z,0,sizeof(z));
if ( memcmp(&z,&txid,sizeof(txid)) == 0 )
return(0);
if ( hexstr != 0 && hexstr[0] != 0 ) // probably not worth doing and zaddr is a problem to decode
{
// compare against txid
// if matches, sendrawtransaction if OTC mode, decoode and return if channels mode
}
zflag = (hushdex_zonly(coin) != 0);
coinstr = coin->coin;
for (i=0; i<numseconds; i++)
{
if ( coin->isfile != 0 )
{
if ( (rawtx= dpow_subscribe(SUBATOMIC_PRIORITY,coin->coin+1,senderpub)) != 0 )
{
filehash = jbits256(rawtx,"filehash");
if ( memcmp(&filehash,&txid,sizeof(filehash)) != 0 )
{
fprintf(stderr,"waiting (%s) (%s)\n",coin->coin+1,jprint(rawtx,0));
free_json(rawtx);
rawtx = 0;
} else return(rawtx);
}
}
else if ( zflag != 0 )
rawtx = get_z_viewtransaction(coinstr,acname,txid);
else if ( coin->isexternal == 0 )
rawtx = get_rawtransaction(coinstr,acname,txid);
else rawtx = _hushdex_rawtransaction(coin,txid);
if ( rawtx != 0 )
return(rawtx);
sleep(1);
}
printf("%s/%s timeout waiting for %s\n",coin->name,coin->coin,bits256_str(str,txid));
return(0);
}
int64_t hushdex_verifypayment(struct coininfo *coin,cJSON *rawtx,uint64_t destsatoshis,char *destaddr,bits256 txid)
{
int32_t i,n,m,valid=0; bits256 tokenid,filehash,checkhash; cJSON *array,*item,*sobj,*a; char *addr,*acname,*coinstr,tokenaddr[64],*hex; uint8_t hexbuf[512],pub33[33]; uint64_t netval,recvsatoshis = 0;
if ( coin->isfile != 0 )
{
filehash = jbits256(rawtx,"filehash");
checkhash = jbits256(rawtx,"checkhash");
if ( memcmp(&txid,&filehash,sizeof(txid)) == 0 && memcmp(&txid,&checkhash,sizeof(txid)) == 0 )
{
fprintf(stderr,"verified file is matching the filehash (%s)\n",jprint(rawtx,0));
return(SATOSHIDEN);
} else return(0);
}
else if ( hushdex_zonly(coin) != 0 )
{
if ( (array= jarray(&n,rawtx,"outputs")) != 0 && n > 0 )
{
for (i=0; i<n; i++)
{
item = jitem(array,i);
if ( (addr= jstr(item,"address")) != 0 && strcmp(addr,destaddr) == 0 )
recvsatoshis += j64bits(item,"valueZat");
}
}
}
else if ( coin->istoken != 0 )
{
if ( (array= jarray(&n,rawtx,"vout")) != 0 && n > 0 )
{
item = jitem(array,0);
if ( (sobj= jobj(item,"scriptPubKey")) != 0 && (a= jarray(&m,sobj,"addresses")) != 0 && m == 1 )
{
coinstr = coin->coin;
if ( get_tokenaddress(coinstr,acname,tokenaddr) != 0 )
{
//fprintf(stderr,"tokenaddr.%s\n",tokenaddr);
if ( (addr= jstri(a,0)) != 0 && strcmp(addr,tokenaddr) == 0 )
recvsatoshis += SATOSHIDEN * (uint64_t)(jdouble(item,"value")*SATOSHIDEN + 0.000000004999);
else fprintf(stderr,"miscompare (%s) vs %s\n",jprint(sobj,0),addr);
}
}
item = jitem(array,n-1);
if ( (sobj= jobj(item,"scriptPubKey")) != 0 && (hex= jstr(sobj,"hex")) != 0 && (m= is_hexstr(hex,0)) > 1 && m/2 < sizeof(hexbuf) )
{
m >>= 1;
decode_hex(hexbuf,m,hex);
decode_hex(tokenid.bytes,32,coin->tokenid);
decode_hex(pub33,33,DPOW_secpkeystr);
// opret 69len EVAL_TOKENS 't' tokenid 1 33 pub33
if ( hexbuf[0] == 0x6a && hexbuf[1] == 0x45 && hexbuf[2] == 0xf2 && hexbuf[3] == 't' && memcmp(&hexbuf[4],&tokenid,sizeof(tokenid)) == 0 && hexbuf[4+32] == 1 && hexbuf[4+32+1] == 33 && memcmp(&hexbuf[4+32+2],pub33,33) == 0 )
{
valid = 1;
//fprintf(stderr,"validated it is a token transfer!\n");
} else fprintf(stderr,"need to validate tokentransfer.(%s) %s %d\n",hex,DPOW_secpkeystr,memcmp(&hexbuf[4+32+2],pub33,33) == 0);
//6a 45 f2 74 2b1feef719ecb526b07416dd432bce603ac6dc8bfe794cddf105cb52f6aae3cd 01 21 02b27de3ee5335518b06f69f4fbabb029cfc737613b100996841d5532b324a5a61
}
recvsatoshis *= valid;
}
}
else
{
if ( (array= jarray(&n,rawtx,"vout")) != 0 && n > 0 )
{
for (i=0; i<n; i++)
{
item = jitem(array,i);
if ( (sobj= jobj(item,"scriptPubKey")) != 0 && (a= jarray(&m,sobj,"addresses")) != 0 && m == 1 )
{
if ( (addr= jstri(a,0)) != 0 && strcmp(addr,destaddr) == 0 )
recvsatoshis += (uint64_t)(jdouble(item,"value")*SATOSHIDEN + 0.000000004999);
}
}
}
}
printf("%s received %.8f vs %.8f\n",destaddr,dstr(recvsatoshis),dstr(destsatoshis));
return(recvsatoshis - destsatoshis);
}
// //////////// end
struct msginfo *hushdex_find(uint32_t origid)
{
struct msginfo *mp;
HASH_FIND(hh,Messages,&origid,sizeof(origid),mp);
return(mp);
}
struct msginfo *hushdex_add(uint32_t origid)
{
struct msginfo *mp = calloc(1,sizeof(*mp));
mp->origid = origid;
HASH_ADD(hh,Messages,origid,sizeof(origid),mp);
return(mp);
}
int32_t hushdex_status(struct msginfo *mp,int32_t status)
{
static FILE *fp;
if ( fp == 0 )
{
int32_t i,oid,s,n,num,count; struct msginfo *m; long fsize;
if ( (fp= fopen("SUBATOMIC.DB","rb+")) == 0 )
{
if ( (fp= fopen("SUBATOMIC.DB","wb")) == 0 )
{
fprintf(stderr,"cant create SUBATOMIC.DB\n");
exit(-1);
}
}
else
{
fseek(fp,0,SEEK_END);
fsize = ftell(fp);
if ( (fsize % (sizeof(uint32_t)*2)) != 0 )
{
fprintf(stderr,"SUBATOMIC.DB illegal filesize.%ld\n",fsize);
exit(-1);
}
n = (int32_t)(fsize / (sizeof(uint32_t)*2));
rewind(fp);
for (i=num=count=0; i<n; i++)
{
if ( fread(&oid,1,sizeof(oid),fp) != sizeof(oid) || fread(&s,1,sizeof(s),fp) != sizeof(s) )
{
fprintf(stderr,"SUBATOMIC.DB corrupted at filepos.%ld\n",ftell(fp));
exit(-1);
}
if ( s < 0 || s > SUBATOMIC_CLOSED )
{
fprintf(stderr,"SUBATOMIC.DB corrupted at filepos.%ld: illegal status.%d\n",ftell(fp),s);
exit(-1);
}
//fprintf(stderr,"%u <- %d\n",oid,s);
if ( (m= hushdex_find(oid)) == 0 )
{
m = hushdex_add(oid);
count++;
}
if ( s > m->status )
{
m->status = s;
num++;
}
}
fprintf(stderr,"initialized %d messages, updated %d out of total.%d\n",count,num,n);
}
}
if ( mp->status >= status )
return(-1);
if ( fwrite(&mp->origid,1,sizeof(mp->origid),fp) != sizeof(mp->origid) || fwrite(&status,1,sizeof(status),fp) != sizeof(status) )
fprintf(stderr,"error updating SUBATOMIC.DB, risk of double spends\n");
fflush(fp);
mp->status = status;
return(0);
}
struct msginfo *hushdex_tracker(uint32_t origid)
{
struct msginfo *mp;
if ( (mp= hushdex_find(origid)) == 0 )
{
mp = hushdex_add(origid);
hushdex_status(mp,0);
}
return(mp);
}
char *hushdex_hexstr(char *jsonstr)
{
char *hexstr; int32_t i,c,n = (int32_t)strlen(jsonstr);
hexstr = malloc(2*n + 3);
strcpy(hexstr,jsonstr);
for (i=0; i<n; i++)
{
if ( (c= jsonstr[i]) == '"' )
c = '\'';
sprintf(&hexstr[i << 1],"%02x",c);
}
sprintf(&hexstr[i << 1],"00");
i++;
hexstr[i << 1] = 0;
return(hexstr);
}
cJSON *hushdex_mpjson(struct msginfo *mp)
{
cJSON *item;
item = cJSON_CreateObject();
jaddnum(item,"origid",mp->origid);
jaddnum(item,"price",mp->price);
jaddnum(item,"openrequest",mp->openrequestid);
jaddstr(item,"base",mp->base.name);
jaddstr(item,"basecoin",mp->base.coin);
jadd64bits(item,"basesatoshis",mp->base.satoshis);
jadd64bits(item,"basetxfee",mp->base.txfee);
jadd64bits(item,"maxbaseamount",mp->base.maxamount);
jaddstr(item,"rel",mp->rel.name);
jaddstr(item,"relcoin",mp->rel.coin);
jadd64bits(item,"relsatoshis",mp->rel.satoshis);
jadd64bits(item,"reltxfee",mp->rel.txfee);
jadd64bits(item,"maxrelamount",mp->rel.maxamount);
jaddstr(item,"alice",mp->alice.pubkey);
jaddstr(item,"alicesecp",mp->alice.secp);
jaddstr(item,"bob",mp->bob.pubkey);
jaddstr(item,"bobsecp",mp->bob.secp);
if ( hushdex_zonly(&mp->rel) != 0 )
jaddstr(item,"bobZaddr",mp->bob.recvZaddr);
else jaddstr(item,"bobaddr",mp->bob.recvaddr);
if ( mp->rel.istoken != 0 )
jaddstr(item,"bobtoken",mp->rel.tokenid);
if ( hushdex_zonly(&mp->base) != 0 )
jaddstr(item,"aliceZaddr",mp->alice.recvZaddr);
else jaddstr(item,"aliceaddr",mp->alice.recvaddr);
if ( mp->base.istoken != 0 )
jaddstr(item,"alicetoken",mp->base.tokenid);
return(item);
}
uint64_t hushdex_orderbook_mpset(struct msginfo *mp,char *basecheck)
{
cJSON *retjson; char *tagA,*tagB,*senderpub,*str,tmpstr[32]; int32_t matches=0; double volA,volB; int64_t txfee=0;
strcpy(mp->base.name,basecheck);
strcpy(mp->base.coin,hushdex_checkname(tmpstr,mp,0,basecheck));
mp->rel.txfee = hushdex_txfee(mp->rel.coin);
if ( (retjson= dpow_get(mp->origid)) != 0 )
{
//fprintf(stderr,"dpow_get.(%s) (%s/%s)\n",jprint(retjson,0),mp->base.coin,mp->rel.coin);
if ( (senderpub= jstr(retjson,"senderpub")) != 0 && is_hexstr(senderpub,0) == 66 && (tagA= jstr(retjson,"tagA")) != 0 && (tagB= jstr(retjson,"tagB")) != 0 && strncmp(tagB,mp->rel.name,strlen(mp->rel.name)) == 0 && strlen(tagA) < sizeof(mp->base.name) )
{
strcpy(mp->base.name,tagA);
strcpy(mp->base.coin,hushdex_checkname(tmpstr,mp,0,tagA));
if ( basecheck[0] == 0 || strncmp(basecheck,tagA,strlen(basecheck)) == 0 )
matches = 1;
else if ( strcmp(tagA,mp->base.name) == 0 )
matches = 1;
else if ( mp->bobflag != 0 && tagA[0] == '#' && strcmp(mp->base.name,"#allfiles") == 0 )
matches = 1;
if ( matches != 0 )
{
if ( (str= jstr(retjson,"decrypted")) != 0 && strlen(str) < 128 )
strcpy(mp->payload,str);
mp->locktime = juint(retjson,"timestamp") + SUBATOMIC_LOCKTIME;
mp->base.txfee = hushdex_txfee(mp->base.coin);
strcpy(mp->senderpub,senderpub);
volB = jdouble(retjson,"amountB");
volA = jdouble(retjson,"amountA");
mp->base.maxamount = volA*SATOSHIDEN + 0.0000000049999;
mp->rel.maxamount = volB*SATOSHIDEN + 0.0000000049999;
if ( 0 && mp->rel.istoken == 0 )
txfee = mp->rel.txfee;
if ( mp->base.maxamount != 0 && mp->rel.maxamount != 0 && volA > SMALLVAL && volB > SMALLVAL && mp->rel.satoshis <= mp->rel.maxamount )
{
mp->price = volA / volB;
mp->base.satoshis = (mp->rel.satoshis - txfee) * mp->price;
//fprintf(stderr,"base satoshis.%llu\n",(long long)mp->base.satoshis);
} else fprintf(stderr,"%u rel %llu vs (%llu %llu)\n",mp->origid,(long long)mp->rel.satoshis,(long long)mp->base.maxamount,(long long)mp->rel.maxamount);
} else printf("%u didnt match (%s) tagA.%s %s, tagB.%s %s %d %d\n",mp->origid,basecheck,tagA,mp->base.name,tagB,mp->rel.name,tagA[0] == '#', strcmp(mp->base.name,"#allfiles") == 0);
} else printf("%u didnt compare tagA.%s %s, tagB.%s %s\n",mp->origid,tagA,mp->base.name,tagB,mp->rel.name);
free_json(retjson);
}
return(mp->base.satoshis);
}
char *randhashstr(char *str)
{
bits256 rands; int32_t i;
for (i=0; i<32; i++)
rands.bytes[i] = rand() >> 17;
bits256_str(str,rands);
return(str);
}
void hushdex_extrafields(cJSON *dest,cJSON *src)
{
char *str;
if ( (str= jstr(src,"approval")) != 0 )
jaddstr(dest,"approval",str);
if ( (str= jstr(src,"opened")) != 0 )
jaddstr(dest,"opened",str);
if ( (str= jstr(src,"payamount")) != 0 )
jaddstr(dest,"payamount",str);
if ( (str= jstr(src,"destaddr")) != 0 )
jaddstr(dest,"destaddr",str);
if ( (str= jstr(src,"bobpayment")) != 0 )
jaddstr(dest,"bobpayment",str);
if ( (str= jstr(src,"alicepayment")) != 0 )
jaddstr(dest,"alicepayment",str);
if ( (str= jstr(src,"bobaddr")) != 0 )
jaddstr(dest,"bobaddr",str);
if ( (str= jstr(src,"bobZaddr")) != 0 )
jaddstr(dest,"bobZaddr",str);
if ( (str= jstr(src,"aliceaddr")) != 0 )
jaddstr(dest,"aliceaddr",str);
if ( (str= jstr(src,"aliceZaddr")) != 0 )
jaddstr(dest,"aliceZaddr",str);
if ( (str= jstr(src,"alicetoken")) != 0 )
jaddstr(dest,"alicetoken",str);
if ( (str= jstr(src,"bobtoken")) != 0 )
jaddstr(dest,"bobtoken",str);
}
char *hushdex_submit(cJSON *argjson,int32_t tobob)
{
char *jsonstr,*hexstr;
jaddnum(argjson,"tobob",tobob != 0);
jsonstr = jprint(argjson,1);
hexstr = hushdex_hexstr(jsonstr);
free(jsonstr);
return(hexstr);
}
#define SCRIPT_OP_IF 0x63
#define SCRIPT_OP_ELSE 0x67
#define SCRIPT_OP_DUP 0x76
#define SCRIPT_OP_ENDIF 0x68
#define SCRIPT_OP_TRUE 0x51
#define SCRIPT_OP_2 0x52
#define SCRIPT_OP_3 0x53
#define SCRIPT_OP_DROP 0x75
#define SCRIPT_OP_EQUALVERIFY 0x88
#define SCRIPT_OP_HASH160 0xa9
#define SCRIPT_OP_EQUAL 0x87
#define SCRIPT_OP_CHECKSIG 0xac
#define SCRIPT_OP_CHECKMULTISIG 0xae
#define SCRIPT_OP_CHECKMULTISIGVERIFY 0xaf
#define SCRIPT_OP_CHECKLOCKTIMEVERIFY 0xb1
int32_t hushdex_redeemscript(char *redeemscript,uint32_t locktime,char *pubkeyA,char *pubkeyB) // not needed
{
// if ( refund ) OP_HASH160 <2of2 multisig hash> OP_EQUAL // standard multisig
// else <locktime> CLTV OP_DROP <pubkeyA> OP_CHECKSIG // standard spend
uint8_t pubkeyAbytes[33],pubkeyBbytes[33],hex[4096]; int32_t i,n = 0;
decode_hex(pubkeyAbytes,33,pubkeyA);
decode_hex(pubkeyBbytes,33,pubkeyB);
hex[n++] = SCRIPT_OP_IF;
hex[n++] = SCRIPT_OP_2;
hex[n++] = 33, memcpy(&hex[n],pubkeyAbytes,33), n += 33;
hex[n++] = 33, memcpy(&hex[n],pubkeyBbytes,33), n += 33;
hex[n++] = SCRIPT_OP_2;
hex[n++] = SCRIPT_OP_CHECKMULTISIG;
hex[n++] = SCRIPT_OP_ELSE;
hex[n++] = 4;
hex[n++] = locktime & 0xff, locktime >>= 8;
hex[n++] = locktime & 0xff, locktime >>= 8;
hex[n++] = locktime & 0xff, locktime >>= 8;
hex[n++] = locktime & 0xff;
hex[n++] = SCRIPT_OP_CHECKLOCKTIMEVERIFY;
hex[n++] = SCRIPT_OP_DROP;
hex[n++] = 33; memcpy(&hex[n],pubkeyAbytes,33); n += 33;
hex[n++] = SCRIPT_OP_CHECKSIG;
hex[n++] = SCRIPT_OP_ENDIF;
for (i=0; i<n; i++)
{
redeemscript[i*2] = hexbyte((hex[i]>>4) & 0xf);
redeemscript[i*2 + 1] = hexbyte(hex[i] & 0xf);
}
redeemscript[n*2] = 0;
/*tmpbuf[0] = SCRIPT_OP_HASH160;
tmpbuf[1] = 20;
calc_OP_HASH160(scriptPubKey,tmpbuf+2,redeemscript);
tmpbuf[22] = SCRIPT_OP_EQUAL;
init_hexbytes_noT(scriptPubKey,tmpbuf,23);
if ( p2shaddr != 0 )
{
p2shaddr[0] = 0;
if ( (btc_addr= base58_encode_check(addrtype,true,tmpbuf+2,20)) != 0 )
{
if ( strlen(btc_addr->str) < 36 )
strcpy(p2shaddr,btc_addr->str);
cstr_free(btc_addr,true);
}
}*/
return(n);
}
int32_t hushdex_approved(struct msginfo *mp,cJSON *approval,cJSON *msgjson,char *senderpub)
{
char *hexstr,numstr[32],redeemscript[1024],*coin,*acname=""; cJSON *retjson,*decodejson; int32_t i,retval = 0;
hushdex_extrafields(approval,msgjson);
if ( mp->OTCmode == 0 )
{
coin = (mp->bobflag != 0) ? mp->base.coin : mp->rel.coin; // the other side gets this coin
if ( get_createmultisig2(coin,acname,mp->msigaddr,mp->redeemscript,mp->alice.secp,mp->bob.secp) != 0 )
{
hushdex_redeemscript(redeemscript,mp->locktime,mp->alice.secp,mp->bob.secp);
if ( (decodejson= get_decodescript(coin,acname,redeemscript)) != 0 )
{
fprintf(stderr,"%s %s msigaddr.%s %s -> %s %s\n",mp->bobflag!=0?"bob":"alice",(mp->bobflag != 0) ? mp->base.coin : mp->rel.coin,mp->msigaddr,mp->redeemscript,redeemscript,jprint(decodejson,0));
free(decodejson);
}
}
}
sprintf(numstr,"%u",mp->origid);
for (i=0; numstr[i]!=0; i++)
sprintf(&mp->approval[i<<1],"%02x",numstr[i]);
sprintf(&mp->approval[i<<1],"%02x",' ');
i++;
mp->approval[i<<1] = 0;
jaddstr(approval,"approval",mp->approval);
hexstr = hushdex_submit(approval,!mp->bobflag);
if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"approved",senderpub,"","")) != 0 )
{
if ( (mp->approvalid= juint(retjson,"id")) != 0 )
retval = 1;
printf("%u approvalid.%u (%s)\n",mp->origid,mp->approvalid,senderpub);
hushdex_status(mp,SUBATOMIC_APPROVED);
free_json(retjson);
}
free(hexstr);
return(retval);
}
int32_t hushdex_opened(struct msginfo *mp,cJSON *opened,cJSON *msgjson,char *senderpub)
{
char *hexstr,channelstr[65]; cJSON *retjson; int32_t retval = 0;
hushdex_extrafields(opened,msgjson);
jaddstr(opened,"opened",randhashstr(channelstr));
hexstr = hushdex_submit(opened,!mp->bobflag);
if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"opened",senderpub,"","")) != 0 )
{
if ( (mp->openedid= juint(retjson,"id")) != 0 )
retval = 1;
printf("%u openedid.%u\n",mp->origid,mp->openedid);
hushdex_status(mp,SUBATOMIC_OPENED);
free_json(retjson);
}
free(hexstr);
return(retval);
}
int32_t hushdex_payment(struct msginfo *mp,cJSON *payment,cJSON *msgjson,char *senderpub)
{
bits256 txid; uint64_t paytoshis; cJSON *retjson; char numstr[32],*coin,*dest,*hexstr; int32_t retval = 0;
if ( mp->bobflag == 0 )
{
coin = mp->rel.name;
paytoshis = mp->rel.satoshis;
if ( hushdex_zonly(&mp->rel) != 0 )
dest = mp->bob.recvZaddr;
else dest = mp->bob.recvaddr;
sprintf(numstr,"%llu",(long long)paytoshis);
jaddstr(payment,"alicepays",numstr);
jaddstr(payment,"bobdestaddr",dest);
txid = hushdex_coinpayment(mp->origid,mp->OTCmode,&mp->rel,dest,paytoshis,mp->approval,mp->bob.secp,senderpub);
jaddbits256(payment,"alicepayment",txid);
mp->alicepayment = txid;
hexstr = 0; // get it from rawtransaction of txid
jaddstr(payment,"alicetx",hexstr);
}
else
{
coin = mp->base.name;
paytoshis = mp->base.satoshis;
if ( hushdex_zonly(&mp->base) != 0 )
dest = mp->alice.recvZaddr;
else dest = mp->alice.recvaddr;
sprintf(numstr,"%llu",(long long)paytoshis);
jaddstr(payment,"bobpays",numstr);
jaddstr(payment,"alicedestaddr",dest);
txid = hushdex_coinpayment(mp->origid,mp->OTCmode,&mp->base,dest,paytoshis,mp->approval,mp->alice.secp,senderpub);
jaddbits256(payment,"bobpayment",txid);
mp->bobpayment = txid;
hexstr = 0; // get it from rawtransaction of txid
jaddstr(payment,"bobtx",hexstr);
}
hexstr = hushdex_submit(payment,!mp->bobflag);
if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"payment",senderpub,"","")) != 0 )
{
if ( (mp->paymentids[0]= juint(retjson,"id")) != 0 )
retval = 1;
printf("%u: %.8f %s -> %s, paymentid[0] %u\n",mp->origid,dstr(paytoshis),coin,dest,mp->paymentids[0]);
hushdex_status(mp,SUBATOMIC_PAYMENT);
free_json(retjson);
}
free(hexstr);
return(retval);
}
int32_t hushdex_paidinfull(struct msginfo *mp,cJSON *paid,cJSON *msgjson,char *senderpub)
{
char *hexstr; cJSON *retjson; int32_t retval = 0;
jaddstr(paid,"paid","in full");
hushdex_extrafields(paid,msgjson);
hexstr = hushdex_submit(paid,!mp->bobflag);
if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"paid",senderpub,"","")) != 0 )
{
if ( (mp->paidid= juint(retjson,"id")) != 0 )
retval = 1;
printf("%u paidid.%u\n",mp->origid,mp->paidid);
hushdex_status(mp,SUBATOMIC_PAIDINFULL);
free_json(retjson);
}
free(hexstr);
return(retval);
}
int32_t hushdex_closed(struct msginfo *mp,cJSON *closed,cJSON *msgjson,char *senderpub)
{
char *hexstr; cJSON *retjson; int32_t retval = 0;
jaddnum(closed,"closed",mp->origid);
hushdex_extrafields(closed,msgjson);
hexstr = hushdex_submit(closed,!mp->bobflag);
if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"closed",senderpub,"","")) != 0 )
{
if ( (mp->closedid= juint(retjson,"id")) != 0 )
retval = 1;
hushdex_status(mp,SUBATOMIC_CLOSED);
printf("%u closedid.%u\n",mp->origid,mp->closedid);
free_json(retjson);
}
free(hexstr);
return(retval);
}
uint32_t hushdex_alice_openrequest(struct msginfo *origmp)
{
struct msginfo *mp; cJSON *retjson,*openrequest; char *hexstr,*str,tmpstr[32];
mp = hushdex_tracker(origmp->origid);
mp->origid = origmp->origid;
mp->rel.satoshis = origmp->rel.satoshis;
mp->rel.istoken = origmp->rel.istoken;
strcpy(mp->rel.tokenid,origmp->rel.tokenid);
strcpy(mp->rel.name,origmp->rel.name);
strcpy(mp->rel.coin,hushdex_checkname(tmpstr,mp,1,origmp->rel.name));
strcpy(mp->alice.pubkey,DPOW_pubkeystr);
strcpy(mp->alice.secp,DPOW_secpkeystr);
strcpy(mp->alice.recvZaddr,DPOW_recvZaddr);
strcpy(mp->alice.recvaddr,DPOW_recvaddr);
printf("rel.%s/%s %s openrequest %u status.%d (%s/%s)\n",mp->rel.name,mp->rel.coin,mp->rel.tokenid,mp->origid,mp->status,mp->alice.recvaddr,mp->alice.recvZaddr);
if ( mp->status == 0 && hushdex_orderbook_mpset(mp,"") != 0 )
{
strcpy(mp->bob.pubkey,mp->senderpub);
if ( hushdex_zonly(&mp->base) != 0 || hushdex_zonly(&mp->rel) != 0 )
mp->OTCmode = 1;
else mp->OTCmode = SUBATOMIC_OTCDEFAULT;
strcpy(origmp->base.name,mp->base.name);
strcpy(origmp->base.coin,mp->base.coin);
origmp->base.istoken = mp->base.istoken;
strcpy(origmp->base.tokenid,mp->base.tokenid);
origmp->OTCmode = mp->OTCmode;
if ( mp->rel.istoken != 0 && ((mp->rel.satoshis % SATOSHIDEN) != 0 || mp->rel.iszaddr != 0) )
{
printf("%u cant do zaddr or fractional rel %s.%s tokens %.8f\n",mp->origid,mp->rel.coin,mp->rel.tokenid,dstr(mp->rel.satoshis));
return(0);
}
else if ( mp->base.istoken != 0 && ((mp->base.satoshis % SATOSHIDEN) != 0 || mp->base.iszaddr != 0 ) )
{
printf("%u cant do zaddr or fractional base %s.%s tokens %.8f\n",mp->origid,mp->base.coin,mp->base.tokenid,dstr(mp->base.satoshis));
return(0);
}
else if ( (openrequest= hushdex_mpjson(mp)) != 0 )
{
hexstr = hushdex_submit(openrequest,!mp->bobflag);
if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,(char *)"inbox",(char *)"openrequest",mp->bob.pubkey,"","")) != 0 )
{
mp->openrequestid = juint(retjson,"id");
printf("%u openrequest.%u -> (%s)\n",mp->origid,mp->openrequestid,mp->bob.pubkey);
hushdex_status(mp,SUBATOMIC_OPENREQUEST);
free_json(retjson);
}
free(hexstr);
}
}
return(mp->openrequestid);
}
void hushdex_bob_gotopenrequest(uint32_t inboxid,char *senderpub,cJSON *msgjson,char *basename,char *relname)
{
struct msginfo *mp; cJSON *approval; int32_t origid; char *addr,tmpstr[32],*coin,*acname="";
origid = juint(msgjson,"origid");
mp = hushdex_tracker(origid);
strcpy(mp->base.name,basename);
strcpy(mp->base.coin,hushdex_checkname(tmpstr,mp,0,basename));
strcpy(mp->rel.name,relname);
strcpy(mp->rel.coin,hushdex_checkname(tmpstr,mp,1,relname));
mp->origid = origid;
mp->rel.satoshis = j64bits(msgjson,"relsatoshis");
mp->bobflag = 1;
strcpy(mp->bob.pubkey,DPOW_pubkeystr);
strcpy(mp->bob.secp,DPOW_secpkeystr);
strcpy(mp->bob.recvZaddr,DPOW_recvZaddr);
strcpy(mp->bob.recvaddr,DPOW_recvaddr);
if ( (addr= jstr(msgjson,"aliceaddr")) != 0 )
strcpy(mp->alice.recvaddr,addr);
if ( (addr= jstr(msgjson,"aliceZaddr")) != 0 )
strcpy(mp->alice.recvZaddr,addr);
if ( (addr= jstr(msgjson,"alicesecp")) != 0 )
strcpy(mp->alice.secp,addr);
if ( hushdex_zonly(&mp->base) != 0 || hushdex_zonly(&mp->rel) != 0 )
mp->OTCmode = 1;
else mp->OTCmode = SUBATOMIC_OTCDEFAULT;
printf("%u got open request\n",mp->origid);
if ( mp->status == 0 && hushdex_orderbook_mpset(mp,basename) != 0 && (approval= hushdex_mpjson(mp)) != 0 )
{
if ( mp->rel.istoken != 0 && ((mp->rel.satoshis % SATOSHIDEN) != 0 || mp->rel.iszaddr != 0) )
{
printf("%u cant do zaddr or fractional rel %s.%s tokens %.8f\n",mp->origid,mp->rel.coin,mp->rel.tokenid,dstr(mp->rel.satoshis));
hushdex_closed(mp,approval,msgjson,senderpub);
return;
}
else if ( mp->base.istoken != 0 && ((mp->base.satoshis % SATOSHIDEN) != 0 || mp->base.iszaddr != 0 ) )
{
printf("%u cant do zaddr or fractional base %s.%s tokens %.8f\n",mp->origid,mp->base.coin,mp->base.tokenid,dstr(mp->base.satoshis));
hushdex_closed(mp,approval,msgjson,senderpub);
return;
}
else if ( hushdex_getbalance(&mp->base) < mp->base.satoshis )
{
printf("%u bob node low on %s funds! %.8f not enough for %.8f\n",mp->origid,mp->base.coin,dstr(hushdex_getbalance(&mp->base)),dstr(mp->base.satoshis));
hushdex_closed(mp,approval,msgjson,senderpub);
}
else
{
printf("%u bob (%s/%s) gotopenrequest origid.%u status.%d (%s/%s) SENDERPUB.(%s)\n",mp->origid,mp->base.name,mp->rel.name,mp->origid,mp->status,mp->bob.recvaddr,mp->bob.recvZaddr,senderpub);
hushdex_approved(mp,approval,msgjson,senderpub);
}
}
}
int32_t hushdex_channelapproved(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp)
{
struct msginfo *mp; cJSON *approval; char *addr,*coin,*acname; int32_t retval = 0;
mp = hushdex_tracker(juint(msgjson,"origid"));
if ( hushdex_orderbook_mpset(mp,mp->base.name) != 0 && (approval= hushdex_mpjson(mp)) != 0 )
{
printf("%u iambob.%d (%s/%s) channelapproved origid.%u status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->origid,mp->status);
if ( mp->bobflag == 0 && mp->status == SUBATOMIC_OPENREQUEST )
{
if ( (addr= jstr(msgjson,"bobaddr")) != 0 )
strcpy(mp->bob.recvaddr,addr);
if ( (addr= jstr(msgjson,"bobZaddr")) != 0 )
strcpy(mp->bob.recvZaddr,addr);
if ( (addr= jstr(msgjson,"bobsecp")) != 0 )
strcpy(mp->bob.secp,addr);
retval = hushdex_approved(mp,approval,msgjson,senderpub);
}
else if ( mp->bobflag != 0 && mp->status == SUBATOMIC_APPROVED )
retval = hushdex_opened(mp,approval,msgjson,senderpub);
}
return(retval);
}
int32_t hushdex_incomingopened(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp)
{
struct msginfo *mp; cJSON *payment; int32_t retval = 0;
mp = hushdex_tracker(juint(msgjson,"origid"));
if ( hushdex_orderbook_mpset(mp,mp->base.name) != 0 && (payment= hushdex_mpjson(mp)) != 0 )
{
printf("%u iambob.%d (%s/%s) incomingchannel status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status);
if ( mp->bobflag == 0 && mp->status == SUBATOMIC_APPROVED )
retval = hushdex_payment(mp,payment,msgjson,senderpub);
else if ( mp->bobflag != 0 && mp->status == SUBATOMIC_OPENED )
retval = 1; // nothing to do
}
return(retval);
}
int32_t hushdex_incomingpayment(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp)
{
static FILE *fp;
struct msginfo *mp; cJSON *pay,*rawtx,*retjson; bits256 txid; char str[65],*hexstr; int32_t retval = 0;
mp = hushdex_tracker(juint(msgjson,"origid"));
if ( hushdex_orderbook_mpset(mp,mp->base.name) != 0 && (pay= hushdex_mpjson(mp)) != 0 )
{
printf("%u iambob.%d (%s/%s) incomingpayment status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status);
if ( mp->bobflag == 0 )
{
txid = jbits256(msgjson,"bobpayment");
jaddbits256(msgjson,"alicepayment",mp->alicepayment);
printf("%u alice waits for %s.%s to be in mempool (%.8f -> %s)\n",mp->origid,mp->base.name,bits256_str(str,txid),dstr(mp->base.satoshis),hushdex_zonly(&mp->base) == 0 ? mp->alice.recvaddr : mp->alice.recvZaddr);
hexstr = jstr(msgjson,"bobtx");
if ( (rawtx= hushdex_txidwait(&mp->base,txid,hexstr,SUBATOMIC_TIMEOUT,senderpub)) != 0 )
{
if ( hushdex_verifypayment(&mp->base,rawtx,mp->base.satoshis,hushdex_zonly(&mp->base) == 0 ? mp->alice.recvaddr : mp->alice.recvZaddr,txid) >= 0 )
mp->gotpayment = 1;
free_json(rawtx);
}
if ( mp->gotpayment != 0 )
{
printf("%u SWAP COMPLETE <<<<<<<<<<<<<<<<\n",mp->origid);
SUBATOMIC_retval = 0;
if ( mp->base.iszaddr == 0 )
{
sprintf(str,"%u",mp->origid);
if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,bits256_str(str,mp->alicepayment),(char *)"completed",str,DPOW_pubkeystr,"","")) != 0 )
free_json(retjson);
}
}
else
{
printf("%u SWAP INCOMPLETE, waiting on %s.%s\n",mp->origid,mp->base.name,bits256_str(str,txid));
if ( (fp= fopen("SUBATOMIC.incomplete","a+")) != 0 )
{
char *jsonstr = jprint(msgjson,0);
fwrite(jsonstr,1,strlen(jsonstr),fp);
fputc('\n',fp);
fclose(fp);
free(jsonstr);
}
if ( mp->base.iszaddr == 0 )
{
sprintf(str,"%u",mp->origid);
if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,bits256_str(str,mp->alicepayment),(char *)"incomplete",str,DPOW_pubkeystr,"","")) != 0 )
free_json(retjson);
}
hushdex_closed(mp,pay,msgjson,senderpub);
exit(-1);
}
}
if ( mp->gotpayment != 0 )
retval = hushdex_paidinfull(mp,pay,msgjson,senderpub);
else
{
if ( mp->bobflag != 0 && mp->status == SUBATOMIC_OPENED )
{
txid = jbits256(msgjson,"alicepayment");
printf("%u bob waits for %s.%s to be in mempool (%.8f -> %s)\n",mp->origid,mp->rel.name,bits256_str(str,txid),dstr(mp->rel.satoshis),hushdex_zonly(&mp->rel) == 0 ? mp->bob.recvaddr : mp->bob.recvZaddr);
hexstr = jstr(msgjson,"alicetx");
if ( (rawtx= hushdex_txidwait(&mp->rel,txid,hexstr,SUBATOMIC_TIMEOUT,senderpub)) != 0 )
{
if ( hushdex_verifypayment(&mp->rel,rawtx,mp->rel.satoshis,hushdex_zonly(&mp->rel) == 0 ? mp->bob.recvaddr : mp->bob.recvZaddr,txid) >= 0 )
mp->gotpayment = 1;
free_json(rawtx);
}
if ( mp->gotpayment != 0 )
{
retval = hushdex_payment(mp,pay,msgjson,senderpub);
jaddbits256(msgjson,"bobpayment",mp->bobpayment);
if ( mp->rel.iszaddr == 0 )
{
sprintf(str,"%u",mp->origid);
if ( (retjson= dpow_broadcast(SUBATOMIC_PRIORITY,bits256_str(str,mp->bobpayment),(char *)"completed",str,DPOW_pubkeystr,"","")) != 0 )
free_json(retjson);
}
printf("%u SWAP COMPLETE <<<<<<<<<<<<<<<<\n",mp->origid);
if ( (fp= fopen("SUBATOMIC.proof","rb+")) == 0 )
fp = fopen("SUBATOMIC.proof","wb");
if ( fp != 0 )
{
char *jsonstr = jprint(msgjson,0);
fseek(fp,0,SEEK_END);
fwrite(jsonstr,1,strlen(jsonstr),fp);
fputc('\n',fp);
fflush(fp);
free(jsonstr);
}
} else printf("%u SWAP INCOMPLETE: %s\n",mp->origid,jprint(msgjson,0));
}
}
}
return(retval);
}
int32_t hushdex_incomingfullypaid(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp)
{
struct msginfo *mp; cJSON *closed; int32_t retval = 0;
mp = hushdex_tracker(juint(msgjson,"origid"));
if ( hushdex_orderbook_mpset(mp,mp->base.name) != 0 && (closed= hushdex_mpjson(mp)) != 0 )
{
printf("%u iambob.%d (%s/%s) incomingfullypaid status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status);
// error check msgjson vs M
if ( mp->bobflag == 0 && mp->status == SUBATOMIC_PAIDINFULL )
retval = hushdex_closed(mp,closed,msgjson,senderpub);
else if ( mp->bobflag != 0 && mp->status == SUBATOMIC_PAYMENT )
retval = hushdex_paidinfull(mp,closed,msgjson,senderpub);
}
return(retval);
}
int32_t hushdex_incomingclosed(uint32_t inboxid,char *senderpub,cJSON *msgjson,struct msginfo *origmp)
{
struct msginfo *mp; cJSON *closed; int32_t retval = 0;
mp = hushdex_tracker(juint(msgjson,"origid"));
if ( hushdex_orderbook_mpset(mp,mp->base.name) != 0 && (closed= hushdex_mpjson(mp)) != 0 )
{
printf("%u iambob.%d (%s/%s) incomingclose status.%d\n",mp->origid,mp->bobflag,mp->base.name,mp->rel.name,mp->status);
if ( mp->bobflag != 0 )
dpow_cancel(mp->origid);
if ( mp->status < SUBATOMIC_CLOSED )
{
retval = hushdex_closed(mp,closed,msgjson,senderpub);
hushdex_status(mp,SUBATOMIC_CLOSED);
}
retval = 1;
}
return(retval);
}
int32_t hushdex_ismine(int32_t bobflag,cJSON *json,char *basename,char *relname)
{
char *base,*rel;
if ( (base= jstr(json,"base")) != 0 && (rel= jstr(json,"rel")) != 0 )
{
if ( strcmp(base,basename) == 0 && strcmp(rel,relname) == 0 )
return(1);
if ( bobflag != 0 )
{
if ( strcmp(basename,"#allfiles") == 0 && base[0] == '#' )
return(1);
fprintf(stderr,"skip ismine (%s/%s) vs (%s/%s)\n",basename,relname,base,rel);
}
}
return(0);
}
void hushdex_tokensregister(int32_t priority)
{
char *token_name,*tokenid,existing[65]; cJSON *tokens,*token; int32_t i,numtokens;
if ( SUBATOMIC_json != 0 && (tokens= jarray(&numtokens,SUBATOMIC_json,"tokens")) != 0 )
{
for (i=0; i<numtokens; i++)
{
token = jitem(tokens,i);
if ( token != 0 )
{
token_name = jfieldname(token);
tokenid = jstr(token,token_name);
//fprintf(stderr,"TOKEN (%s %s)\n",token_name,tokenid);
if ( token_name != 0 && tokenid != 0 )
dpow_tokenregister(existing,priority,token_name,tokenid);
}
}
}
}
void hushdex_filesregister(int32_t priority)
{
char *fname,*tokenid,*coin,existing[512]; int64_t price; cJSON *files,*file,*prices; int32_t i,j,m,numfiles;
if ( SUBATOMIC_json != 0 && (files= jarray(&numfiles,SUBATOMIC_json,"files")) != 0 )
{
for (i=0; i<numfiles; i++)
{
file = jitem(files,i);
if ( file != 0 )
{
// {"filename":"hushd",prices:[{"HUSH":1}, {"ZEC:1"}]}
fname = jstr(file,"filename");
if ( (prices= jarray(&m,file,"prices")) != 0 && m > 0 )
{
for (j=0; j<m; j++)
{
coin = jfieldname(jitem(prices,j));
price = (jdouble(jitem(prices,j),coin)*SATOSHIDEN + 0.00000000499999);
//pricestr = jstr(jitem(prices,j),coin);
//fprintf(stderr,"%s %.8f, ",coin,dstr(price));
dpow_fileregister(existing,priority,fname,coin,price);
}
}
}
}
}
}
void hushdex_loop(struct msginfo *mp)
{
static char *tagBs[] = { "openrequest", "approved", "opened", "payment", "paid", "closed" };
static uint32_t stopats[sizeof(tagBs)/sizeof(*tagBs)];
struct inboxinfo **ptrs,*ptr; char *tagB; int32_t i,iter,n,msgs=0,mask=0; cJSON *inboxjson;
fprintf(stderr,"start hushdex_loop iambob.%d %s -> %s, %u %llu %u\n",mp->bobflag,mp->base.name,mp->rel.name,mp->origid,(long long)mp->rel.satoshis,mp->openrequestid);
while ( 1 )
{
if ( msgs == 0 )
{
sleep(1);
fflush(stdout);
if ( mp->bobflag != 0 )
{
dpow_pubkeyregister(SUBATOMIC_PRIORITY);
hushdex_tokensregister(SUBATOMIC_PRIORITY);
hushdex_filesregister(SUBATOMIC_PRIORITY);
}
}
msgs = 0;
for (iter=0; iter<(int32_t)(sizeof(tagBs)/sizeof(*tagBs)); iter++)
{
tagB = tagBs[iter];
if ( (ptrs= dpow_inboxcheck(&n,&stopats[iter],tagB)) != 0 )
{
for (i=0; i<n; i++)
{
msgs++;
if ( (ptr= ptrs[i]) != 0 )
{
if ( (inboxjson= cJSON_Parse(ptr->jsonstr)) != 0 )
{
if ( jint(inboxjson,"tobob") != mp->bobflag )
continue;
if ( hushdex_ismine(mp->bobflag,inboxjson,mp->base.name,mp->rel.name) != 0 )
{
if ( strcmp(tagB,"openrequest") == 0 && mp->bobflag != 0 )
hushdex_bob_gotopenrequest(ptr->shorthash,ptr->senderpub,inboxjson,mp->base.name,mp->rel.name);
else if ( strcmp(tagB,"approved") == 0 )
mask |= hushdex_channelapproved(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 0;
else if ( strcmp(tagB,"opened") == 0 )
mask |= hushdex_incomingopened(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 1;
else if ( strcmp(tagB,"payment") == 0 )
mask |= hushdex_incomingpayment(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 2;
else if ( strcmp(tagB,"paid") == 0 )
mask |= hushdex_incomingfullypaid(ptr->shorthash,ptr->senderpub,inboxjson,mp) << 3;
else if ( strcmp(tagB,"closed") == 0 )
mask |= hushdex_incomingclosed(ptr->shorthash,ptr->senderpub,inboxjson,mp) * 0x1f;
else fprintf(stderr,"iambob.%d unknown unexpected tagB.(%s)\n",mp->bobflag,tagB);
}
free_json(inboxjson);
} else fprintf(stderr,"hushdex iambob.%d loop got unparseable(%s)\n",mp->bobflag,ptr->jsonstr);
free(ptr);
ptrs[i] = 0;
}
}
free(ptrs);
}
}
if ( mp->bobflag == 0 && (mask & 0x1f) == 0x1f )
{
printf("alice %u %llu %u finished\n",mp->origid,(long long)mp->rel.satoshis,mp->openrequestid);
break;
}
}
}
int32_t main(int32_t argc,char **argv)
{
char *fname = "hushdex.json";
int32_t i,height; char *coin,*kcli,*hushdex,*hashstr,*acname=(char *)""; cJSON *retjson; bits256 blockhash; char checkstr[65],str[65],str2[65],tmpstr[32]; long fsize; struct msginfo M;
memset(&M,0,sizeof(M));
srand((int32_t)time(NULL));
if ( (hushdex= filestr(&fsize,fname)) == 0 )
{
fprintf(stderr,"cant load %s file\n",fname);
exit(-1);
}
if ( (SUBATOMIC_json= cJSON_Parse(hushdex)) == 0 )
{
fprintf(stderr,"cant parse hushdex.json file (%s)\n",hushdex);
exit(-1);
}
free(hushdex);
if ( argc >= 4 )
{
if ( dpow_pubkey() < 0 )
{
fprintf(stderr,"couldnt set pubkey for ZEX\n");
return(-1);
}
coin = (char *)argv[1];
if ( argv[2][0] != 0 ) {
REFCOIN_CLI = (char *)argv[2];
} else {
acname = coin;
}
hashstr = (char *)argv[3];
strcpy(M.rel.coin,hushdex_checkname(tmpstr,&M,1,coin));
strcpy(M.rel.name,coin);
if ( argc == 4 && strlen(hashstr) == 64 ) // for blocknotify usage, seems not needed
{
height = get_coinheight(coin,acname,&blockhash);
bits256_str(checkstr,blockhash);
if ( strcmp(checkstr,hashstr) == 0 )
{
fprintf(stderr,"%s: (%s) %s height.%d\n",coin,REFCOIN_CLI!=0?REFCOIN_CLI:"",checkstr,height);
if ( (retjson= dpow_ntzdata(coin,SUBATOMIC_PRIORITY,height,blockhash)) != 0 )
free_json(retjson);
} else fprintf(stderr,"coin.%s (%s) %s vs %s, height.%d\n",coin,REFCOIN_CLI!=0?REFCOIN_CLI:"",checkstr,hashstr,height);
if ( strcmp("BTC",coin) != 0 )
{
bits256 prevntzhash,ntzhash; int32_t prevntzheight,ntzheight; uint32_t ntztime,prevntztime; char hexstr[81]; cJSON *retjson2;
prevntzhash = dpow_ntzhash(coin,&prevntzheight,&prevntztime);
if ( (retjson= get_getinfo(coin,acname)) != 0 )
{
ntzheight = juint(retjson,"notarized");
ntzhash = jbits256(retjson,"notarizedhash");
if ( ntzheight > prevntzheight )
{
get_coinmerkleroot(coin,acname,ntzhash,&ntztime);
fprintf(stderr,"NOTARIZATION %s.%d %s t.%u\n",coin,ntzheight,bits256_str(str,ntzhash),ntztime);
bits256_str(hexstr,ntzhash);
sprintf(&hexstr[64],"%08x",ntzheight);
sprintf(&hexstr[72],"%08x",ntztime);
hexstr[80] = 0;
if ( (retjson2= dpow_broadcast(SUBATOMIC_PRIORITY,hexstr,coin,"notarizations",DPOW_pubkeystr,"","")) != 0 )
free_json(retjson2);
}
else if ( ntzheight == prevntzheight && memcmp(&prevntzhash,&ntzhash,32) != 0 )
fprintf(stderr,"NTZ ERROR %s.%d %s != %s\n",coin,ntzheight,bits256_str(str,ntzhash),bits256_str(str2,prevntzhash));
free_json(retjson);
}
}
}
else if ( argc == 5 && atol(hashstr) > 10000 )
{
char checkstr[32]; uint64_t mult = 1;
M.origid = (uint32_t)atol(hashstr);
sprintf(checkstr,"%u",M.origid);
if ( strcmp(checkstr,hashstr) == 0 ) // alice
{
M.rel.satoshis = (uint64_t)(atof(argv[4])*SATOSHIDEN+0.0000000049999);
for (i=0; M.rel.name[i]!=0; i++)
if ( M.rel.name[i] == '.' )
{
mult = SATOSHIDEN;
break;
}
if ( hushdex_getbalance(&M.rel) < M.rel.satoshis/mult )
{
fprintf(stderr,"not enough balance %s %.8f for %.8f\n",M.rel.coin,dstr(hushdex_getbalance(&M.rel)),dstr(M.rel.satoshis/mult));
return(-1);
}
fprintf(stderr,"hushdex_channel_alice (%s/%s) %s %u with %.8f %llu\n",M.rel.name,M.rel.coin,hashstr,M.origid,atof(argv[4]),(long long)M.rel.satoshis);
dpow_pubkeyregister(SUBATOMIC_PRIORITY);
M.openrequestid = hushdex_alice_openrequest(&M);
if ( M.openrequestid != 0 )
hushdex_loop(&M);
} else fprintf(stderr,"checkstr mismatch %s %s != %s\n",coin,hashstr,checkstr);
}
else
{
M.bobflag = 1;
strcpy(M.base.name,hashstr);
strcpy(M.base.coin,hushdex_checkname(tmpstr,&M,0,hashstr));
hushdex_loop(&M); // while ( 1 ) loop for each relcoin -> basecoin
}
}
return(SUBATOMIC_retval);
}