/****************************************************************************** * Copyright © 2014-2016 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. * * * ******************************************************************************/ #include "../exchanges/bitcoin.h" /* bitcoin_pubkey33(pubkey,myinfo->persistent_priv); bitcoin_address(coinaddr,other->chain->pubtype,pubkey,sizeof(pubkey)); bitcoin_addr2rmd160(&addrtype,secret160,coinaddr); scriptlen = bitcoin_cltvscript(coinbtc->chain->p2shtype,p2sh_coinaddr,p2sh_rmd160,script,0,senderaddr,otheraddr,secret160,locktime); if ( jobj(argjson,"p2sh") != 0 ) { if ( strcmp(jstr(argjson,"p2sh"),p2sh_coinaddr) != 0 ) { printf("mismatched p2sh.(%s) vs (%s)\n",jstr(argjson,"p2sh"),p2sh_coinaddr); return(clonestr("{\"error\":\"instantdex_BTCswap base or rel mismatch\"}")); } } */ #define INSTANTDEX_DONATION 2000 // https://github.com/TierNolan/bips/blob/bip4x/bip-atom.mediawiki uint64_t instantdex_relsatoshis(uint64_t price,uint64_t volume) { if ( volume > price ) return(price * dstr(volume)); else return(dstr(price) * volume); } bits256 instantdex_sharedpub256(bits256 *sharedprivp,uint8_t pubkey[33],bits256 privkey,bits256 hash,int32_t n) { bits256 shared,iters; int32_t i; iters = shared = curve25519_shared(privkey,hash); for (i=0; ibytes,shared.bytes,sizeof(shared),iters.bytes,sizeof(iters)); return(bitcoin_pubkey33(pubkey,*sharedprivp)); } int32_t instantdex_pubkeyargs(bits256 *sharedprivs,cJSON *argjson,int32_t numpubs,bits256 privkey,bits256 hash,int32_t firstbyte) { char buf[3]; int32_t i,n; bits256 tmp; uint8_t pubkey[33]; sprintf(buf,"%c0",'A' - 0x02 + firstbyte); for (i=n=0; ivalue; //printf("(%.8f vs %.8f)\n",dstr(atx_value),dstr(value)); if ( atx_value == value ) return(vin); else if ( atx_value > value ) { gap = (atx_value - value); if ( above == 0 || gap < above ) { above = gap; abovevin = vin; } } else if ( mode == 0 ) { gap = (value - atx_value); if ( below == 0 || gap < below ) { below = gap; belowvin = vin; } } } if ( (vin= (abovevin != 0) ? abovevin : belowvin) == 0 && mode == 1 ) vin = unspents; return(vin); } struct bitcoin_unspent *iguana_unspentsget(struct supernet_info *myinfo,struct iguana_info *coin,int32_t *numunspentsp) { struct bitcoin_unspent *ups = calloc(1,sizeof(*ups)); uint8_t addrtype; // struct bitcoin_unspent { bits256 txid,privkey; uint64_t value; int32_t vout; }; *numunspentsp = 0; return(ups); } struct bitcoin_spend *instantdex_spendset(struct supernet_info *myinfo,struct iguana_info *coin,uint64_t satoshis,uint64_t donation) { int32_t i,mode,numunspents,maxinputs = 1024; int64_t remains; struct bitcoin_unspent *ptr,*up; struct bitcoin_unspent *ups; struct bitcoin_spend *spend; if ( (ups= iguana_unspentsget(myinfo,coin,&numunspents)) == 0 ) return(0); spend = calloc(1,sizeof(*spend) + sizeof(*spend->inputs) * maxinputs); spend->satoshis = satoshis; spend->txfee = coin->chain->txfee; if ( strcmp(coin->symbol,"BTC") == 0 ) remains = spend->txfee + spend->satoshis + donation; ptr = spend->inputs; for (i=0; i=0; mode--) if ( (up= instantdex_bestfit(coin,ups,numunspents,remains,mode)) != 0 ) break; if ( up != 0 ) { spend->input_satoshis += up->value; spend->inputs[spend->numinputs++] = *up; if ( spend->input_satoshis >= satoshis ) { spend->netamount = (spend->input_satoshis - spend->txfee - donation); spend->change = (spend->input_satoshis - spend->netamount); printf("numinputs %d sum %.8f vs satoshis %.8f change %.8f -> txfee %.8f\n",spend->numinputs,dstr(spend->input_satoshis),dstr(satoshis),dstr(spend->change),dstr(spend->input_satoshis - spend->change - spend->netamount)); break; } remains -= up->value; } else break; } if ( spend->input_satoshis >= (satoshis + spend->txfee) ) { spend = realloc(spend,sizeof(*spend) + sizeof(*spend->inputs) * spend->numinputs); return(spend); } else { free(spend); return(0); } } /* Name: Bob.Bail.In Input value: B + 2*fb + change Input source: (From Bob's coins, multiple inputs are allowed) vout0 value: B, ScriptPubKey 0: OP_HASH160 Hash160(P2SH Redeem) OP_EQUAL vout1 value: fb, ScriptPubKey 1: OP_HASH160 Hash160(x) OP_EQUALVERIFY pub-A1 OP_CHECKSIG vout2 value: change, ScriptPubKey 2: <= 100 bytes P2SH Redeem: OP_2 pub-A1 pub-B1 OP_2 OP_CHECKMULTISIG Name: Alice.Bail.In vins: A + 2*fa + change, Input source: (From Alice's altcoins, multiple inputs are allowed) vout0 value: A, ScriptPubKey 0: OP_HASH160 Hash160(P2SH Redeem) OP_EQUAL vout1 value: fa, ScriptPubKey 1: OP_HASH160 Hash160(x) OP_EQUAL vout2 value: change, ScriptPubKey 2: <= 100 bytes */ char *instantdex_bailintx(struct iguana_info *coin,bits256 *txidp,struct bitcoin_spend *spend,bits256 A0,bits256 B0,uint8_t x[20],int32_t isbob) { uint64_t change; char *rawtxstr,*signedtx; struct vin_info *V; bits256 txid,signedtxid; int32_t p2shlen,i; cJSON *txobj; int32_t scriptv0len,scriptv1len,scriptv2len; uint8_t p2shscript[256],scriptv0[128],scriptv1[128],changescript[128],pubkey[35]; p2shlen = bitcoin_2of2spendscript(&scriptv0len,scriptv0,p2shscript,A0,B0); txobj = bitcoin_createtx(coin,0); bitcoin_addoutput(coin,txobj,scriptv0,scriptv0len,spend->satoshis); if ( isbob != 0 ) { scriptv1len = bitcoin_revealsecret160(scriptv1,0,x); scriptv1len = bitcoin_pubkeyspend(scriptv1,scriptv1len,pubkey); } else scriptv1len = bitcoin_p2shspend(scriptv1,0,x); bitcoin_addoutput(coin,txobj,scriptv1,scriptv1len,spend->txfee); if ( (scriptv2len= bitcoin_changescript(coin,changescript,0,&change,spend->changeaddr,spend->input_satoshis,spend->satoshis,spend->txfee)) > 0 ) bitcoin_addoutput(coin,txobj,changescript,scriptv2len,change); for (i=0; inuminputs; i++) bitcoin_addinput(coin,txobj,spend->inputs[i].txid,spend->inputs[i].vout,0xffffffff); rawtxstr = bitcoin_json2hex(coin,&txid,txobj); char str[65]; printf("%s_bailin.%s (%s)\n",isbob!=0?"bob":"alice",bits256_str(str,txid),rawtxstr); V = calloc(spend->numinputs,sizeof(*V)); for (i=0; inuminputs; i++) V[i].signers[0].privkey = spend->inputs[i].privkey; bitcoin_verifytx(coin,&signedtxid,&signedtx,rawtxstr,V); free(rawtxstr), free(V); if ( signedtx != 0 ) printf("signed %s_bailin.%s (%s)\n",isbob!=0?"bob":"alice",bits256_str(str,signedtxid),signedtx); else printf("error generating signedtx\n"); free_json(txobj); *txidp = txid; return(signedtx); } cJSON *instantdex_bailinspend(struct iguana_info *coin,bits256 privkey,uint64_t amount) { bits256 hash; int32_t n; cJSON *txobj; int32_t scriptv0len; uint8_t p2shscript[256],rmd160[20],scriptv0[128],pubkey[35]; bitcoin_pubkey33(pubkey,privkey); n = bitcoin_pubkeyspend(p2shscript,0,pubkey); vcalc_sha256(0,hash.bytes,p2shscript,n); calc_rmd160(0,rmd160,hash.bytes,sizeof(hash.bytes)); scriptv0len = bitcoin_p2shspend(scriptv0,0,rmd160); txobj = bitcoin_createtx(coin,0); bitcoin_addoutput(coin,txobj,scriptv0,scriptv0len,amount); return(txobj); } /* Name: Bob.Payout vin0: A, Input source: Alice.Bail.In:0 vin1: fa, Input source: Alice.Bail.In:1 vout0: A, ScriptPubKey: OP_HASH160 Hash160(P2SH Redeem) OP_EQUAL; P2SH Redeem: pub-B2 OP_CHECKSIG Name: Alice.Payout vin0: B, Input source: Bob.Bail.In:0 vin1: fb, Input source: Bob.Bail.In:1 vout0: B, ScriptPubKey: OP_HASH160 Hash160(P2SH Redeem) OP_EQUAL; P2SH Redeem: pub-A2 OP_CHECKSIG */ char *instantdex_bailinsign(struct iguana_info *coin,bits256 bailinpriv,char *sigstr,int32_t *siglenp,bits256 *txidp,struct vin_info *V,cJSON *txobj,int32_t isbob) { char *rawtxstr,*signedtx; rawtxstr = bitcoin_json2hex(coin,txidp,txobj); char str[65]; printf("%s_payout.%s (%s)\n",isbob!=0?"bob":"alice",bits256_str(str,*txidp),rawtxstr); V->signers[isbob].privkey = bailinpriv; bitcoin_verifytx(coin,txidp,&signedtx,rawtxstr,V); *siglenp = V->signers[isbob].siglen; init_hexbytes_noT(sigstr,V->signers[isbob].sig,*siglenp); free(rawtxstr); if ( signedtx != 0 ) printf("signed %s_payout.%s (%s) sig.%s\n",isbob!=0?"bob":"alice",bits256_str(str,*txidp),signedtx,sigstr); else printf("error generating signedtx\n"); free_json(txobj); return(signedtx); } char *instantdex_payouttx(struct iguana_info *coin,char *sigstr,int32_t *siglenp,bits256 *txidp,bits256 *sharedprivs,bits256 bailintxid,int64_t amount,int64_t txfee,int32_t isbob,char *othersigstr) { struct vin_info V; cJSON *txobj; txobj = instantdex_bailinspend(coin,sharedprivs[1],amount); bitcoin_addinput(coin,txobj,bailintxid,0,0xffffffff); bitcoin_addinput(coin,txobj,bailintxid,1,0xffffffff); memset(&V,0,sizeof(V)); if ( othersigstr != 0 ) { printf("OTHERSIG.(%s)\n",othersigstr); V.signers[isbob ^ 1].siglen = (int32_t)strlen(othersigstr) >> 1; decode_hex(V.signers[isbob ^ 1].sig,V.signers[isbob ^ 1].siglen,othersigstr); } return(instantdex_bailinsign(coin,sharedprivs[0],sigstr,siglenp,txidp,&V,txobj,isbob)); } /* Name: Alice.Refund vin0: A, Input source: Alice.Bail.In:0 vout0: A - fa, ScriptPubKey: OP_HASH160 Hash160(P2SH) OP_EQUAL; P2SH Redeem: pub-A3 OP_CHECKSIG Locktime: current block height + ((T/2)/(altcoin block rate)) Name: Bob.Refund vin0: B, Input source: Bob.Bail.In:0 vout0: B - fb, ScriptPubKey: OP_HASH160 Hash160(P2SH Redeem) OP_EQUAL; P2SH Redeem: pub-B3 OP_CHECKSIG Locktime: (current block height) + (T / 10 minutes) */ char *instantdex_refundtx(struct iguana_info *coin,bits256 *txidp,bits256 bailinpriv,bits256 priv2,bits256 bailintxid,int64_t amount,int64_t txfee,int32_t isbob) { char sigstr[256]; int32_t siglen; struct vin_info V; cJSON *txobj; txobj = instantdex_bailinspend(coin,priv2,amount - txfee); bitcoin_addinput(coin,txobj,bailintxid,0,0xffffffff); return(instantdex_bailinsign(coin,bailinpriv,sigstr,&siglen,txidp,&V,txobj,isbob)); } int32_t instantdex_calcx20(char hexstr[41],uint8_t *p2shscript,uint8_t firstbyte,bits256 pub) { uint8_t pubkey[33],rmd160[20]; int32_t n; bits256 hash; memcpy(pubkey+1,pub.bytes,sizeof(pub)), pubkey[0] = firstbyte; n = bitcoin_pubkeyspend(p2shscript,0,pubkey); vcalc_sha256(0,hash.bytes,p2shscript,n); calc_rmd160(0,rmd160,hash.bytes,sizeof(hash.bytes)); init_hexbytes_noT(hexstr,rmd160,sizeof(rmd160)); return(n); } char *instantdex_btcoffer(struct supernet_info *myinfo,struct exchange_info *exchange,char *othercoin,double othervolume,double maxprice) // Bob sending to network (Alice) { char *str,coinaddr[64],xstr[41]; uint8_t xscript[64]; struct iguana_info *other; int32_t isbob = 1; struct instantdex_accept checkA,A; cJSON *newjson; bits256 hash,pub3,sharedprivs[4]; if ( othercoin == 0 || (other= iguana_coinfind(othercoin)) == 0 ) return(clonestr("{\"error\":\"invalid othercoin\"}")); hash = instantdex_acceptset(&A,othercoin,"BTC",INSTANTDEX_OFFERDURATION,1,-1,maxprice,othervolume,myinfo->myaddr.nxt64bits); newjson = instantdex_acceptsendjson(&A); if ( instantdex_acceptextract(&checkA,newjson) < 0 ) { int32_t i; for (i=0; ipersistent_priv,hash,0x02+isbob) != 4 ) return(clonestr("{\"error\":\"highly unlikely run of 02 pubkeys\"}")); pub3 = jbits256(newjson,"B3"); jdelete(newjson,"B3"); instantdex_calcx20(xstr,xscript,0x02+isbob,pub3); jaddstr(newjson,"x",xstr); if ( coinaddr[0] != 0 ) jaddstr(newjson,othercoin,coinaddr); if ( maxprice > 0. ) { if ( (str= InstantDEX_maxaccept(myinfo,0,newjson,0,othercoin,"BTC",maxprice,othervolume)) != 0 ) free(str); } return(instantdex_sendcmd(myinfo,newjson,"BTCoffer",myinfo->ipaddr,INSTANTDEX_HOPS)); } void instantdex_pendingnotice(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,struct instantdex_accept *A) { printf("need to start monitoring thread\n"); ap->pendingvolume64 -= A->A.basevolume64; } cJSON *instantdex_newjson(struct supernet_info *myinfo,bits256 *A0p,bits256 *B0p,bits256 *sharedprivs,uint8_t secret160[20],int32_t isbob,cJSON *argjson,bits256 hash,struct instantdex_accept *A) { cJSON *newjson; char *xstr; newjson = instantdex_acceptsendjson(A); if ( instantdex_pubkeyargs(sharedprivs,newjson,3+isbob,myinfo->persistent_priv,hash,0x02+isbob) != 3 ) return(0); if ( isbob == 0 ) *A0p = jbits256(newjson,"A0"); else *A0p = jbits256(argjson,"A0"); *B0p = jbits256(argjson,"B0"); if ( (xstr= jstr(argjson,"x")) != 0 ) { decode_hex(secret160,20,xstr); jaddstr(newjson,"x",xstr); } return(newjson); } char *instantdex_bailinrefund(struct supernet_info *myinfo,struct iguana_info *coin,struct exchange_info *exchange,struct instantdex_accept *A,char *nextcmd,uint8_t secret160[20],cJSON *newjson,int32_t isbob,bits256 A0,bits256 B0,bits256 *sharedprivs) { struct bitcoin_spend *spend; char *bailintx,*refundtx,field[64]; bits256 bailintxid,refundtxid; if ( bits256_nonz(A0) > 0 && bits256_nonz(B0) > 0 ) { if ( (spend= instantdex_spendset(myinfo,coin,A->A.basevolume64,INSTANTDEX_DONATION)) != 0 ) { bailintx = instantdex_bailintx(coin,&bailintxid,spend,A0,B0,secret160,0); refundtx = instantdex_refundtx(coin,&refundtxid,sharedprivs[0],sharedprivs[2],bailintxid,A->A.basevolume64,coin->chain->txfee,isbob); if ( A->statusjson == 0 ) A->statusjson = cJSON_CreateObject(); sprintf(field,"bailin%c",'A'+isbob), jaddstr(A->statusjson,field,bailintx), free(bailintx); sprintf(field,"refund%c",'A'+isbob), jaddstr(A->statusjson,field,refundtx), free(refundtx); sprintf(field,"bailintx%c",'A'+isbob), jaddbits256(A->statusjson,field,bailintxid); sprintf(field,"bailintxid%c",'A'+isbob), jaddbits256(newjson,field,bailintxid); free(spend); return(instantdex_sendcmd(myinfo,newjson,nextcmd,myinfo->ipaddr,INSTANTDEX_HOPS)); } else return(clonestr("{\"error\":\"couldnt create bailintx\"}")); } else return(clonestr("{\"error\":\"dont have pubkey0 pair\"}")); } cJSON *instantdex_payout(struct supernet_info *myinfo,struct iguana_info *coin,struct exchange_info *exchange,struct instantdex_accept *A,uint8_t secret160[20],int32_t isbob,bits256 *A0p,bits256 *B0p,bits256 *sharedprivs,bits256 hash,uint64_t satoshis[2],cJSON *argjson) { cJSON *newjson; char field[32],payoutsigstr[256],*signedpayout; int32_t payoutsiglen; bits256 payouttxid,bailintxid; if ( (newjson= instantdex_newjson(myinfo,A0p,B0p,sharedprivs,secret160,isbob,argjson,hash,A)) == 0 ) return(0); sprintf(field,"bailintxid%c",'A' + (isbob^1)), bailintxid = jbits256(argjson,field); sprintf(field,"payoutsig%c",'A' + (isbob^1)); if ( (signedpayout= instantdex_payouttx(coin,payoutsigstr,&payoutsiglen,&payouttxid,sharedprivs,bailintxid,satoshis[isbob],coin->chain->txfee,isbob,jstr(argjson,field))) != 0 ) { sprintf(field,"payoutsig%c",'A'+isbob), jaddstr(newjson,field,payoutsigstr); if ( A->statusjson == 0 ) A->statusjson = cJSON_CreateObject(); sprintf(field,"payout%c",'A'+isbob), jaddstr(A->statusjson,field,signedpayout); free(signedpayout); } return(newjson); } char *instantdex_advance(struct supernet_info *myinfo,bits256 *sharedprivs,int32_t isbob,cJSON *argjson,bits256 hash,char *addfield,char *nextstate,struct instantdex_accept *A) { cJSON *newjson; bits256 A0,B0; uint8_t secret160[20]; if ( (newjson= instantdex_newjson(myinfo,&A0,&B0,sharedprivs,secret160,isbob,argjson,hash,A)) == 0 ) return(clonestr("{\"error\":\"instantdex_BTCswap offer null newjson\"}")); if ( A->statusjson != 0 && jstr(A->statusjson,addfield) != 0 ) { jaddstr(newjson,addfield,jstr(A->statusjson,addfield)); if ( nextstate != 0 ) return(instantdex_sendcmd(myinfo,newjson,nextstate,myinfo->ipaddr,INSTANTDEX_HOPS)); else return(clonestr("{\"result\":\"instantdex_BTCswap advance complete, wait or refund\"}")); } else return(clonestr("{\"error\":\"instantdex_BTCswap advance cant find statusjson\"}")); } char *instantdex_BTCswap(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,char *cmdstr,struct instantdex_msghdr *msg,cJSON *argjson,char *remoteaddr,uint64_t signerbits,uint8_t *data,int32_t datalen) // receiving side { uint8_t secret160[20]; bits256 hash,A0,B0,sharedprivs[4]; uint64_t satoshis[2]; cJSON *newjson; struct instantdex_accept *ap; char *retstr=0,*str; int32_t locktime,isbob=0,offerdir = 0; struct iguana_info *coinbtc,*other; if ( exchange == 0 ) return(clonestr("{\"error\":\"instantdex_BTCswap null exchange ptr\"}")); offerdir = instantdex_bidaskdir(A); if ( (other= iguana_coinfind(A->A.base)) == 0 || (coinbtc= iguana_coinfind("BTC")) == 0 ) return(clonestr("{\"error\":\"instantdex_BTCswap cant find btc or other coin info\"}")); locktime = (uint32_t)(A->A.expiration + INSTANTDEX_OFFERDURATION); if ( A->A.rel == 0 || strcmp(A->A.rel,"BTC") != 0 ) return(clonestr("{\"error\":\"instantdex_BTCswap offer non BTC rel\"}")); vcalc_sha256(0,hash.bytes,(void *)&A->A,sizeof(ap->A)); if ( hash.txid != A->orderid ) return(clonestr("{\"error\":\"txid mismatches orderid\"}")); satoshis[0] = A->A.basevolume64; satoshis[1] = instantdex_relsatoshis(A->A.price64,A->A.basevolume64); printf("got offer.(%s) offerside.%d offerdir.%d\n",jprint(argjson,0),A->A.myside,A->A.acceptdir); if ( strcmp(cmdstr,"offer") == 0 ) // sender is Bob, receiver is network (Alice) { if ( A->A.expiration < (time(NULL) + INSTANTDEX_DURATION) ) return(clonestr("{\"error\":\"instantdex_BTCswap offer too close to expiration\"}")); if ( (ap= instantdex_acceptable(exchange,A,myinfo->myaddr.nxt64bits)) != 0 ) { isbob = 0; if ( (newjson= instantdex_newjson(myinfo,&A0,&B0,sharedprivs,secret160,isbob,argjson,hash,A)) == 0 ) return(clonestr("{\"error\":\"instantdex_BTCswap offer null newjson\"}")); else { // should add to orderbook if not accepted instantdex_pendingnotice(myinfo,exchange,ap,A); return(instantdex_bailinrefund(myinfo,other,exchange,A,"proposal",secret160,newjson,isbob,A0,B0,sharedprivs)); } } else { printf("no matching trade.(%s)\n",jprint(argjson,0)); if ( (str= InstantDEX_minaccept(myinfo,0,argjson,0,A->A.base,"BTC",dstr(A->A.price64),dstr(A->A.basevolume64))) != 0 ) free(str); } } else if ( strcmp(cmdstr,"proposal") == 0 ) // sender is Alice, receiver is Bob { isbob = 1; newjson = instantdex_payout(myinfo,coinbtc,exchange,A,secret160,isbob,&A0,&B0,sharedprivs,hash,satoshis,argjson); return(instantdex_bailinrefund(myinfo,coinbtc,exchange,A,"BTCaccept",secret160,newjson,isbob,A0,B0,sharedprivs)); } else if ( strcmp(cmdstr,"accept") == 0 ) // sender is Bob, receiver is Alice { isbob = 0; newjson = instantdex_payout(myinfo,other,exchange,A,secret160,isbob,&A0,&B0,sharedprivs,hash,satoshis,argjson); return(instantdex_sendcmd(myinfo,newjson,"BTCconfirm",myinfo->ipaddr,INSTANTDEX_HOPS)); } else if ( strcmp(cmdstr,"confirm") == 0 ) // sender is Alice, receiver is Bob { isbob = 1; newjson = instantdex_payout(myinfo,coinbtc,exchange,A,secret160,isbob,&A0,&B0,sharedprivs,hash,satoshis,argjson); return(instantdex_sendcmd(myinfo,newjson,"BTCbroadcast",myinfo->ipaddr,INSTANTDEX_HOPS)); } else if ( strcmp(cmdstr,"broadcast") == 0 ) // sender is Bob, receiver is Alice { isbob = 0; return(instantdex_advance(myinfo,sharedprivs,isbob,argjson,hash,"bailintxA","BTCcommit",A)); } else if ( strcmp(cmdstr,"commit") == 0 ) // sender is Alice, receiver is Bob { isbob = 1; // go into refund state, ie watch for payouts to complete or get refund return(instantdex_advance(myinfo,sharedprivs,isbob,argjson,hash,"payoutB","BTCcomplete",A)); } else if ( strcmp(cmdstr,"complete") == 0 ) // sender is Bob, receiver is Alice { isbob = 0; // go into refund state, ie watch for payouts to complete or get refund return(instantdex_advance(myinfo,sharedprivs,isbob,argjson,hash,"payoutA",0,A)); } else retstr = clonestr("{\"error\":\"BTC swap got unrecognized command\"}"); if ( retstr == 0 ) retstr = clonestr("{\"error\":\"BTC swap null retstr\"}"); return(retstr); }