980 lines
50 KiB

9 years ago
/******************************************************************************
* 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. *
* *
******************************************************************************/
9 years ago
#include "../exchanges/bitcoin.h"
9 years ago
/* https://bitcointalk.org/index.php?topic=1340621.msg13828271#msg13828271
Tier Nolan's approach is followed with the following changes:
a) instead of cutting 1000 keypairs, only 777 are a
b) instead of sending the entire 256 bits, it is truncated to 64 bits. With odds of collision being so low, it is dwarfed by the ~0.1% insurance factor.
c) D is set to 100x the insurance rate of 1/777 12.87% + BTC amount
d) insurance is added to Bob's payment, which is after the deposit and bailin
e) BEFORE Bob broadcasts deposit, Alice broadcasts BTC denominated fee in cltv so if trade isnt done fee is reclaimed
*/
int32_t instantdex_outputinsurance(struct iguana_info *coin,cJSON *txobj,int64_t insurance,uint64_t nonce)
{
uint8_t rmd160[20],script[128]; int32_t n;
decode_hex(rmd160,sizeof(rmd160),(nonce % 10) == 0 ? TIERNOLAN_RMD160 : INSTANTDEX_RMD160);
n = bitcoin_standardspend(script,0,rmd160);
bitcoin_addoutput(coin,txobj,script,n,insurance);
return(n);
}
9 years ago
/*
9 years ago
Alice fee:
OP_IF
<now + 1 days> OP_CLTV OP_DROP INSTANTDEX OP_CHECKSIG
OP_ELSE
<now + 2 days> OP_CLTV OP_DROP OP_HASH160 <hash(alice_priv_m)> OP_EQUALVERIFY <alice_pub_key_1001> OP_CHECKSIG
OP_ENDIF
Bob deposit:
instantdex_bobscript(script,0,(uint32_t)(time(NULL)+INSTANTDEX_LOCKTIME*2),pubA0,privBn,pubB0);
OP_IF
<now + 2 days> OP_CLTV OP_DROP <alice_pub_1001> OP_CHECKSIG
OP_ELSE
OP_HASH160 <hash(bob_priv_n)> OP_EQUALVERIFY <bob_pub_1001> OP_CHECKSIG
OP_ENDIF
Bobpays:
instantdex_bobscript(script,0,(uint32_t)(time(NULL)+INSTANTDEX_LOCKTIME),pubB1,privAm,pubA0);
OP_IF
<now + 1 day> OP_CLTV OP_DROP <bob_pub_1002> OP_CHECKSIG
OP_ELSE
OP_HASH160 <hash(alice_priv_m)> OP_EQUALVERIFY <alice_pub_key_1001> OP_CHECKSIG
OP_ENDIF
*/
int32_t instantdex_bobscript(uint8_t *script,int32_t n,int32_t *secretstartp,uint32_t locktime,bits256 cltvpub,uint8_t secret160[20],bits256 destpub)
9 years ago
{
9 years ago
uint8_t pubkeyA[33],pubkeyB[33];
memcpy(pubkeyA+1,cltvpub.bytes,sizeof(cltvpub)), pubkeyA[0] = 0x02;
memcpy(pubkeyB+1,destpub.bytes,sizeof(destpub)), pubkeyB[0] = 0x03;
script[n++] = SCRIPT_OP_IF;
n = bitcoin_checklocktimeverify(script,n,locktime);
n = bitcoin_pubkeyspend(script,n,pubkeyA);
script[n++] = SCRIPT_OP_ELSE;
if ( secretstartp != 0 )
*secretstartp = n + 2;
n = bitcoin_revealsecret160(script,n,secret160);
n = bitcoin_pubkeyspend(script,n,pubkeyB);
script[n++] = SCRIPT_OP_ENDIF;
return(n);
}
// OP_2 <alice_pub_m> <bob_pub_n> OP_2 OP_CHECKMULTISIG
int32_t instantdex_alicescript(uint8_t *script,int32_t n,char *msigaddr,uint8_t altps2h,bits256 pubAm,bits256 pubBn)
{
uint8_t p2sh160[20]; struct vin_info V;
memset(&V,0,sizeof(V));
memcpy(&V.signers[0].pubkey[1],pubAm.bytes,sizeof(pubAm)), V.signers[0].pubkey[0] = 0x02;
memcpy(&V.signers[1].pubkey[1],pubBn.bytes,sizeof(pubBn)), V.signers[1].pubkey[0] = 0x03;
V.M = V.N = 2;
n = bitcoin_MofNspendscript(p2sh160,script,n,&V);
bitcoin_address(msigaddr,altps2h,p2sh160,sizeof(p2sh160));
return(n);
}
char *instantdex_bobtx(struct supernet_info *myinfo,struct iguana_info *coin,bits256 *txidp,bits256 pubA0,bits256 pubB0,bits256 privBn,uint32_t reftime,int64_t amount,int32_t depositflag)
{
cJSON *txobj; int32_t n,secretstart; char *signedtx = 0;
uint8_t script[1024],secret[20]; struct bitcoin_spend *spend; int64_t insurance; uint32_t locktime;
locktime = (uint32_t)(reftime + INSTANTDEX_LOCKTIME * (1 + depositflag));
txobj = bitcoin_createtx(coin,locktime);
insurance = (amount * INSTANTDEX_INSURANCERATE + coin->chain->txfee); // txfee prevents dust attack
if ( (spend= iguana_spendset(myinfo,coin,amount + insurance,coin->chain->txfee)) != 0 )
9 years ago
{
9 years ago
calc_rmd160_sha256(secret,privBn.bytes,sizeof(privBn));
n = instantdex_bobscript(script,0,&secretstart,locktime,pubA0,secret,pubB0);
bitcoin_addoutput(coin,txobj,script,n,amount + depositflag*insurance*100);
if ( depositflag == 0 )
instantdex_outputinsurance(coin,txobj,insurance,pubB0.txid);
txobj = iguana_signtx(coin,txidp,&signedtx,spend,txobj);
if ( signedtx != 0 )
printf("bob deposit.%s\n",signedtx);
else printf("error signing bobdeposit numinputs.%d\n",spend->numinputs);
free(spend);
9 years ago
}
9 years ago
free_json(txobj);
return(signedtx);
9 years ago
}
9 years ago
uint64_t instantdex_relsatoshis(uint64_t price,uint64_t volume)
{
if ( volume > price )
return(price * dstr(volume));
else return(dstr(price) * volume);
}
9 years ago
int32_t instantdex_paymentverify(struct supernet_info *myinfo,struct iguana_info *coin,struct bitcoin_swapinfo *swap,struct instantdex_accept *A,cJSON *argjson,int32_t depositflag)
{
cJSON *txobj; bits256 txid; uint32_t n,locktime; int32_t i,secretstart,retval = -1; uint64_t x;
struct iguana_msgtx msgtx; uint8_t script[512],rmd160[20]; int64_t insurance,relsatoshis,amount;
if ( jstr(argjson,depositflag != 0 ? "deposit" : "payment") != 0 )
{
9 years ago
relsatoshis = instantdex_relsatoshis(A->offer.price64,A->offer.basevolume64);
9 years ago
insurance = (relsatoshis * INSTANTDEX_INSURANCERATE + coin->chain->txfee); // txfee prevents dust attack
if ( depositflag != 0 )
{
swap->deposit = clonestr(jstr(argjson,"deposit"));
swap->dtxid = jbits256(argjson,"dtxid");
swap->pubBn = jbits256(argjson,"pubBn");
insurance *= 100;
}
amount = relsatoshis + insurance;
if ( (txobj= bitcoin_hex2json(coin,&txid,&msgtx,swap->deposit)) != 0 )
{
9 years ago
locktime = A->offer.expiration;
9 years ago
if ( depositflag == 0 )
memset(rmd160,0,sizeof(rmd160));
else calc_rmd160_sha256(rmd160,swap->privkeys[0].bytes,sizeof(rmd160));
n = instantdex_bobscript(script,0,&secretstart,locktime,swap->mypubs[0],rmd160,swap->otherpubs[0]);
if ( msgtx.lock_time == locktime && msgtx.vouts[0].value == amount && n == msgtx.vouts[0].pk_scriptlen )
{
memcpy(&script[secretstart],&msgtx.vouts[0].pk_script[secretstart],20);
if ( memcmp(script,msgtx.vouts[0].pk_script,n) == 0 )
{
iguana_rwnum(0,&script[secretstart],sizeof(x),&x);
printf("deposit script verified x.%llx vs otherscut %llx\n",(long long)x,(long long)swap->otherscut[swap->choosei][0]);
if ( x == swap->otherscut[swap->choosei][0] )
{
if ( depositflag == 0 )
{
decode_hex(rmd160,sizeof(rmd160),(swap->otherpubs[0].txid % 10) == 0 ? TIERNOLAN_RMD160 : INSTANTDEX_RMD160);
n = bitcoin_standardspend(script,0,rmd160);
if ( msgtx.vouts[1].value == insurance && n == msgtx.vouts[1].pk_scriptlen && memcmp(script,msgtx.vouts[1].pk_script,n) == 0 )
retval = 0;
} else retval = 0;
}
else printf("deposit script verified but secret mismatch x.%llx vs otherscut %llx\n",(long long)x,(long long)swap->otherscut[swap->choosei][0]);
}
else
{
for (i=0; i<n; i++)
printf("%02x ",script[i]);
printf("script\n");
for (i=0; i<n; i++)
printf("%02x ",msgtx.vouts[0].pk_script[i]);
printf("deposit\n");
}
}
free_json(txobj);
}
}
return(retval);
}
char *instantdex_alicetx(struct supernet_info *myinfo,struct iguana_info *altcoin,char *msigaddr,bits256 *txidp,bits256 pubAm,bits256 pubBn,int64_t amount)
{
cJSON *txobj; int32_t n; char *signedtx = 0;
uint8_t script[1024]; struct bitcoin_spend *spend; int64_t insurance;
txobj = bitcoin_createtx(altcoin,0);
insurance = (amount * INSTANTDEX_INSURANCERATE + altcoin->chain->txfee); // txfee prevents dust attack
if ( (spend= iguana_spendset(myinfo,altcoin,amount + insurance,altcoin->chain->txfee)) != 0 )
{
//instantdex_outputinsurance(altcoin,txobj,insurance);
n = instantdex_alicescript(script,0,msigaddr,altcoin->chain->p2shtype,pubAm,pubBn);
bitcoin_addoutput(altcoin,txobj,script,n,amount);
txobj = iguana_signtx(altcoin,txidp,&signedtx,spend,txobj);
if ( signedtx != 0 )
printf("alice payment.%s\n",signedtx);
else printf("error signing alicetx numinputs.%d\n",spend->numinputs);
free(spend);
}
free_json(txobj);
return(signedtx);
}
int32_t instantdex_altpaymentverify(struct supernet_info *myinfo,struct iguana_info *coin,struct bitcoin_swapinfo *swap,struct instantdex_accept *A,cJSON *argjson)
{
cJSON *txobj; bits256 txid; uint32_t n; int32_t i,retval = -1;
struct iguana_msgtx msgtx; uint8_t script[512]; char *altmsigaddr,msigaddr[64];
if ( jstr(argjson,"altpayment") != 0 && (altmsigaddr= jstr(argjson,"altmsigaddr")) != 0 )
{
swap->altpayment = clonestr(jstr(argjson,"altpayment"));
swap->aptxid = jbits256(argjson,"aptxid");
swap->pubAm = jbits256(argjson,"pubAm");
if ( (txobj= bitcoin_hex2json(coin,&txid,&msgtx,swap->altpayment)) != 0 )
{
n = instantdex_alicescript(script,0,msigaddr,coin->chain->p2shtype,swap->pubAm,swap->pubBn);
if ( strcmp(msigaddr,altmsigaddr) == 0 && n == msgtx.vouts[0].pk_scriptlen )
{
if ( memcmp(script,msgtx.vouts[0].pk_script,n) == 0 )
{
printf("deposit script verified\n");
}
else
{
for (i=0; i<n; i++)
printf("%02x ",script[i]);
printf("altscript\n");
for (i=0; i<n; i++)
printf("%02x ",msgtx.vouts[0].pk_script[i]);
printf("altpayment\n");
}
}
free_json(txobj);
}
}
return(retval);
}
9 years ago
void instantdex_pendingnotice(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,uint64_t basevolume64)
9 years ago
{
9 years ago
// printf("need to start monitoring thread\n");
9 years ago
ap->pendingvolume64 -= basevolume64;
9 years ago
}
9 years ago
bits256 instantdex_derivekeypair(bits256 *newprivp,uint8_t pubkey[33],bits256 privkey,bits256 orderhash)
9 years ago
{
9 years ago
bits256 sharedsecret;
sharedsecret = curve25519_shared(privkey,orderhash);
vcalc_sha256cat(newprivp->bytes,orderhash.bytes,sizeof(orderhash),sharedsecret.bytes,sizeof(sharedsecret));
return(bitcoin_pubkey33(pubkey,*newprivp));
9 years ago
}
9 years ago
int32_t instantdex_pubkeyargs(struct bitcoin_swapinfo *swap,cJSON *newjson,int32_t numpubs,bits256 privkey,bits256 hash,int32_t firstbyte)
9 years ago
{
9 years ago
char buf[3]; int32_t i,n,m,len=0; bits256 pubi; uint64_t txid; uint8_t secret160[20],pubkey[33];
9 years ago
sprintf(buf,"%c0",'A' - 0x02 + firstbyte);
9 years ago
for (i=n=m=0; i<numpubs*100 && n<numpubs; i++)
9 years ago
{
9 years ago
pubi = instantdex_derivekeypair(&swap->privkeys[n],pubkey,privkey,hash);
privkey = swap->privkeys[n];
9 years ago
//printf("i.%d n.%d numpubs.%d %02x vs %02x\n",i,n,numpubs,pubkey[0],firstbyte);
9 years ago
if ( pubkey[0] != firstbyte )
continue;
9 years ago
if ( n < 2 && numpubs > 2 )
{
sprintf(buf+1,"%d",n);
9 years ago
if ( jobj(newjson,buf) == 0 )
jaddbits256(newjson,buf,pubi);
9 years ago
}
else
{
9 years ago
calc_rmd160_sha256(secret160,swap->privkeys[n].bytes,sizeof(swap->privkeys[n]));
9 years ago
memcpy(&txid,secret160,sizeof(txid));
9 years ago
txid = (m+1) | ((m+1)<<16);
txid <<= 32;
txid = (m+1) | ((m+1)<<16);
pubi.txid = (m+1) | ((m+1)<<16);
pubi.txid <<= 32;
pubi.txid = (m+1) | ((m+1)<<16);
9 years ago
len += iguana_rwnum(1,(uint8_t *)&swap->deck[m][0],sizeof(txid),&txid);
len += iguana_rwnum(1,(uint8_t *)&swap->deck[m][1],sizeof(pubi.txid),&pubi.txid);
m++;
9 years ago
}
n++;
}
9 years ago
return(n);
}
9 years ago
char *instantdex_choosei(struct bitcoin_swapinfo *swap,cJSON *newjson,cJSON *argjson,uint8_t *serdata,int32_t datalen)
9 years ago
{
9 years ago
int32_t i,j,max,len = 0; uint64_t x;
if ( swap->choosei < 0 && serdata != 0 && datalen == sizeof(swap->deck) )
9 years ago
{
max = (int32_t)(sizeof(swap->otherscut) / sizeof(*swap->otherscut));
9 years ago
for (i=0; i<max; i++)
for (j=0; j<2; j++)
len += iguana_rwnum(1,(uint8_t *)&swap->otherscut[i][j],sizeof(x),&serdata[len]);
9 years ago
OS_randombytes((uint8_t *)&swap->choosei,sizeof(swap->choosei));
9 years ago
if ( swap->choosei < 0 )
swap->choosei = -swap->choosei;
9 years ago
swap->choosei %= max;
jaddnum(newjson,"mychoosei",swap->choosei);
9 years ago
printf("%llu/%llu %s send mychoosei.%d of max.%d\n",(long long)swap->bidid,(long long)swap->askid,swap->isbob!=0?"BOB":"alice",swap->choosei,max);
9 years ago
return(0);
9 years ago
}
else
{
9 years ago
printf("invalid datalen.%d vs %ld\n",datalen,sizeof(swap->deck));
9 years ago
return(clonestr("{\"error\":\"instantdex_BTCswap offer no cut\"}"));
}
9 years ago
}
void instantdex_getpubs(struct bitcoin_swapinfo *swap,cJSON *argjson,cJSON *newjson)
{
char fields[2][2][3]; int32_t i,j,myind,otherind;
memset(fields,0,sizeof(fields));
fields[0][0][0] = fields[0][1][0] = 'A';
fields[1][0][0] = fields[1][1][0] = 'B';
for (i=0; i<2; i++)
for (j=0; j<2; j++)
fields[i][j][1] = '0' + j;
myind = swap->isbob;
otherind = (myind ^ 1);
for (j=0; j<2; j++)
{
if ( bits256_nonz(swap->mypubs[j]) == 0 && jobj(argjson,fields[myind][j]) != 0 )
swap->mypubs[j] = jbits256(newjson,fields[myind][j]);
if ( bits256_nonz(swap->otherpubs[j]) == 0 && jobj(argjson,fields[otherind][j]) != 0 )
swap->otherpubs[j] = jbits256(argjson,fields[otherind][j]);
}
9 years ago
}
9 years ago
void instantdex_privkeysextract(struct supernet_info *myinfo,struct bitcoin_swapinfo *swap,uint8_t *serdata,int32_t serdatalen)
9 years ago
{
9 years ago
int32_t i,wrongfirstbyte,errs,len = 0; bits256 hashpriv,otherpriv,pubi; uint8_t otherpubkey[33];
printf("got instantdex_privkeysextract serdatalen.%d choosei.%d cutverified.%d\n",serdatalen,swap->choosei,swap->cutverified);
if ( swap->cutverified == 0 && swap->choosei >= 0 && serdatalen == sizeof(swap->privkeys) )
{
for (i=wrongfirstbyte=errs=0; i<sizeof(swap->privkeys)/sizeof(*swap->privkeys); i++)
{
len += iguana_rwbignum(0,&serdata[len],sizeof(bits256),otherpriv.bytes);
if ( i == swap->choosei )
{
if ( bits256_nonz(otherpriv) != 0 )
{
printf("got privkey in slot.%d my choosi??\n",i);
errs++;
}
continue;
}
pubi = bitcoin_pubkey33(otherpubkey,otherpriv);
vcalc_sha256(0,hashpriv.bytes,otherpriv.bytes,sizeof(otherpriv));
if ( otherpubkey[0] != (swap->isbob ^ 1) + 0x02 )
{
wrongfirstbyte++;
printf("wrongfirstbyte[%d] %02x\n",i,otherpubkey[0]);
}
else if ( swap->otherscut[i][0] != hashpriv.txid )
{
printf("otherscut[%d] priv mismatch %llx != %llx\n",i,(long long)swap->otherscut[i][0],(long long)hashpriv.txid);
errs++;
}
else if ( swap->otherscut[i][1] != pubi.txid )
{
printf("otherscut[%d] priv mismatch %llx != %llx\n",i,(long long)swap->otherscut[i][1],(long long)pubi.txid);
errs++;
}
}
if ( errs == 0 && wrongfirstbyte == 0 )
swap->cutverified = 1;
else printf("failed verification: wrong firstbyte.%d errs.%d\n",wrongfirstbyte,errs);
}
}
cJSON *instantdex_newjson(struct supernet_info *myinfo,struct bitcoin_swapinfo *swap,cJSON *argjson,bits256 hash,struct instantdex_accept *A,int32_t flag777)
{
cJSON *newjson;
newjson = cJSON_CreateObject();
9 years ago
//printf("acceptsend.(%s)\n",jprint(newjson,0));
9 years ago
if ( swap->otherschoosei < 0 && jobj(argjson,"mychoosei") != 0 )
{
9 years ago
//printf("otherschoosei.%d\n",swap->otherschoosei);
9 years ago
if ( (swap->otherschoosei= juint(argjson,"mychoosei")) >= sizeof(swap->otherscut)/sizeof(*swap->otherscut) )
swap->otherschoosei = -1;
}
if ( juint(argjson,"verified") != 0 )
swap->otherverifiedcut = 1;
9 years ago
jaddnum(newjson,"verified",swap->otherverifiedcut);
9 years ago
if ( instantdex_pubkeyargs(swap,newjson,2,myinfo->persistent_priv,swap->orderhash,0x02+swap->isbob) == 2 )
instantdex_getpubs(swap,argjson,newjson);
else printf("ERROR: couldnt generate pubkeys\n");
return(newjson);
}
cJSON *BOB_processfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson,uint8_t **serdatap,int32_t *serdatalenp)
{
cJSON *newjson=0; uint8_t *serdata = *serdatap; int32_t serdatalen = *serdatalenp;
*serdatap = 0, *serdatalenp = 0;
uint32_t reftime;
reftime = (uint32_t)(A->offer.expiration - INSTANTDEX_LOCKTIME*2);
if ( serdata != 0 && serdatalen > 0 )
9 years ago
{
9 years ago
serdata[serdatalen-1] = 0;
9 years ago
}
return(newjson);
}
9 years ago
cJSON *ALICE_processfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson,uint8_t **serdatap,int32_t *serdatalenp)
9 years ago
{
9 years ago
cJSON *newjson=0; uint8_t *serdata = *serdatap; int32_t serdatalen = *serdatalenp;
*serdatap = 0, *serdatalenp = 0;
if ( serdata != 0 && serdatalen > 0 )
{
serdata[serdatalen-1] = 0;
}
return(newjson);
}
struct instantdex_stateinfo *BTC_initFSM(int32_t *n)
{
struct instantdex_stateinfo *s = 0;
*n = 0;
// Four initial states are BOB_sentoffer, ALICE_gotoffer, ALICE_sentoffer, BOB_gotoffer
// the initiator includes signed feetx and deck of 777 keypairs
// the responder chooses one of 777 and returns it with "BTCchose" message
//
// "BTCabcde are message events from other party (message events capped at length 8)
// "lowercas" are special events, <TX> types: <fee>, <dep>osit, <alt>payment, <acl> is altcoin claim
// "<TX>found" means the other party's is confirmed at user specified confidence level
// BTC_cleanup state just unwinds pending swap as nothing has been committed yet
9 years ago
// states
9 years ago
s = instantdex_statecreate(s,n,"BTC_cleanup",BOB_processfunc,0,0,0);
s = instantdex_statecreate(s,n,"BOB_claimdeposit",BOB_processfunc,0,0,0);
s = instantdex_statecreate(s,n,"ALICE_reclaim",BOB_processfunc,0,0,0);
s = instantdex_statecreate(s,n,"BOB_sentoffer",BOB_processfunc,0,"BTC_cleanup",0);
s = instantdex_statecreate(s,n,"BOB_sentprivs",BOB_processfunc,0,"BTC_cleanup",0);
9 years ago
s = instantdex_statecreate(s,n,"BOB_gotoffer",BOB_processfunc,0,"BTC_cleanup",0);
9 years ago
s = instantdex_statecreate(s,n,"ALICE_sentoffer",ALICE_processfunc,0,"BTC_cleanup",0);
9 years ago
s = instantdex_statecreate(s,n,"ALICE_gotoffer",ALICE_processfunc,0,"BTC_cleanup",0);
s = instantdex_statecreate(s,n,"ALICE_sentprivs",ALICE_processfunc,0,"BTC_cleanup",0);
s = instantdex_statecreate(s,n,"ALICE_wait3",ALICE_processfunc,0,"BTC_cleanup",0);
s = instantdex_statecreate(s,n,"ALICE_waitfee_privs",ALICE_processfunc,0,"BTC_cleanup",0);
s = instantdex_statecreate(s,n,"ALICE_waitdeposit_privs",ALICE_processfunc,0,"BTC_cleanup",0);
s = instantdex_statecreate(s,n,"ALICE_waitfee_deposit",ALICE_processfunc,0,"BTC_cleanup",0);
s = instantdex_statecreate(s,n,"ALICE_waitprivs",ALICE_processfunc,0,"BTC_cleanup",0);
s = instantdex_statecreate(s,n,"ALICE_waitfee",ALICE_processfunc,0,"BTC_cleanup",0);
s = instantdex_statecreate(s,n,"ALICE_waitdeposit",ALICE_processfunc,0,"BTC_cleanup",0);
s = instantdex_statecreate(s,n,"BOB_sentdeposit",BOB_processfunc,0,"BTC_claimdeposit",0);
s = instantdex_statecreate(s,n,"BOB_altconfirm",BOB_processfunc,0,"BTC_claimdeposit",0);
s = instantdex_statecreate(s,n,"ALICE_sentalt",ALICE_processfunc,0,"BTC_claimdeposit",0);
s = instantdex_statecreate(s,n,"ALICE_waitconfirms",ALICE_processfunc,0,"BTC_claimdeposit",0);
s = instantdex_statecreate(s,n,"BOB_sentpayment",BOB_processfunc,0,"BTC_claimdeposit",0);
// events
instantdex_addevent(s,*n,"BOB_sentoffer","BTCchose","BTCprivs","BOB_sentprivs");
instantdex_addevent(s,*n,"BOB_sentprivs","feefound","BTCdeptx","BOB_sentdeposit");
9 years ago
instantdex_addevent(s,*n,"ALICE_sentoffer","BTCchose","BTCprivs","ALICE_sentprivs");
// gotoffer states have sent BTCchose already
instantdex_addevent(s,*n,"BOB_gotoffer","BTCchose","BTCprivs","BOB_sentprivs");
instantdex_addevent(s,*n,"ALICE_gotoffer","BTCchose","BTCprivs","ALICE_sentprivs");
// alice needs to wait for various items
instantdex_addevent(s,*n,"ALICE_sentprivs","BTCdeptx",0,"ALICE_wait3");
9 years ago
// following states cover all permutations of the three required events to make altpayment
instantdex_addevent(s,*n,"ALICE_wait3","feefound",0,"ALICE_waitdeposit_privs");
instantdex_addevent(s,*n,"ALICE_wait3","depfound",0,"ALICE_waitfee_privs");
instantdex_addevent(s,*n,"ALICE_wait3","BTCprivs",0,"ALICE_waitfee_deposit");
instantdex_addevent(s,*n,"ALICE_waitfee_privs","feefound",0,"ALICE_waitprivs");
instantdex_addevent(s,*n,"ALICE_waitfee_privs","BTCprivs",0,"ALICE_waitfee");
instantdex_addevent(s,*n,"ALICE_waitdeposit_privs","depfound",0,"ALICE_waitprivs");
instantdex_addevent(s,*n,"ALICE_waitdeposit_privs","BTCprivs",0,"ALICE_waitdeposit");
instantdex_addevent(s,*n,"ALICE_waitfee_deposit","depfound",0,"ALICE_waitfee");
instantdex_addevent(s,*n,"ALICE_waitfee_deposit","feefound",0,"ALICE_waitdeposit");
// wait for last event and send out altpayment
instantdex_addevent(s,*n,"ALICE_waitprivs","BTCprivs","BTCalttx","ALICE_sentalt");
instantdex_addevent(s,*n,"ALICE_waitfee","feefound","BTCalttx","ALICE_sentalt");
instantdex_addevent(s,*n,"ALICE_waitdeposit","depfound","BTCalttx","ALICE_sentalt");
// now Bob's turn to make sure altpayment is confirmed and send real payment
instantdex_addevent(s,*n,"BOB_sentdeposit","BTCalttx",0,"BOB_altconfirm");
instantdex_addevent(s,*n,"BOB_altconfirm","altfound","BTCpaytx","BOB_sentpayment");
instantdex_addevent(s,*n,"ALICE_sentalt","BTCpaytx",0,"ALICE_waitconfirms");
instantdex_addevent(s,*n,"ALICE_waitconfirms","bobfound",0,"ALICE_reclaim");
instantdex_addevent(s,*n,"ALICE_waitconfirms","payfound","BTCprivM","ALICE_claimedbtc");
// if BTCprivM doesnt come in, altcoin needs to be monitored for alice's claim
instantdex_addevent(s,*n,"BOB_sentpayment","aclfound","BTCdone","BOB_claimedalt");
instantdex_addevent(s,*n,"BOB_sentpayment","BTCprivM","BTCdone","BOB_claimedalt");
return(s);
}
char *instantdex_statemachine(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,char *cmdstr,cJSON *argjson,uint8_t *serdata,int32_t serdatalen)
{
uint32_t i; struct iguana_info *altcoin,*coinbtc; cJSON *newjson;
struct bitcoin_swapinfo *swap = A->info; struct instantdex_stateinfo *state = swap->state;
if ( state == 0 || swap == 0 || (coinbtc= iguana_coinfind("BTC")) == 0 || (altcoin= iguana_coinfind(A->offer.base)) == 0 )
return(clonestr("{\"error\":\"instantdex_BTCswap missing coin info\"}"));
printf("%llu/%llu cmd.(%s) state.(%s)\n",(long long)swap->bidid,(long long)swap->askid,cmdstr,swap->state->name);
if ( swap->expiration != 0 && time(NULL) > swap->expiration )
{
swap->state = state->timeoutevent;
if ( (newjson= (*state->timeout)(myinfo,exchange,A,argjson,&serdata,&serdatalen)) == 0 )
return(clonestr("{\"error\":\"instantdex_BTCswap null return from timeoutfunc\"}"));
return(jprint(newjson,1));
}
for (i=0; i<state->numevents; i++)
{
if ( strcmp(cmdstr,state->events[i].cmdstr) == 0 )
{
if ( (newjson= (*state->process)(myinfo,exchange,A,argjson,&serdata,&serdatalen)) == 0 )
{
swap->state = state->errorevent;
return(clonestr("{\"error\":\"instantdex_statemachine: null return\"}"));
}
else
{
swap->state = state->events[i].nextstate;
if ( state->events[i].sendcmd != 0 )
return(instantdex_sendcmd(myinfo,&A->offer,newjson,state->events[i].sendcmd,swap->othertrader,INSTANTDEX_HOPS,serdata,serdatalen));
else return(clonestr("{\"result\":\"instantdex_statemachine: processed\"}"));
}
}
}
return(clonestr("{\"error\":\"instantdex_statemachine: unexpected state\"}"));
}
#ifdef xxx
9 years ago
if ( strcmp(cmdstr,"step1") == 0 && strcmp(swap->nextstate,cmdstr) == 0 ) // either
9 years ago
{
9 years ago
printf("%s got step1, should have other's choosei\n",swap->isbob!=0?"BOB":"alice");
9 years ago
if ( (newjson= instantdex_newjson(myinfo,swap,argjson,swap->orderhash,A,0)) == 0 )
return(clonestr("{\"error\":\"instantdex_BTCswap step1 null newjson\"}"));
else if ( swap->otherschoosei < 0 )
return(clonestr("{\"error\":\"instantdex_BTCswap step1, no didnt choosei\"}"));
9 years ago
else
9 years ago
{
9 years ago
printf("%s chose.%d\n",swap->isbob==0?"BOB":"alice",swap->otherschoosei);
9 years ago
if ( swap->isbob == 0 )
swap->privAm = swap->privkeys[swap->otherschoosei];
else swap->privBn = swap->privkeys[swap->otherschoosei];
memset(&swap->privkeys[swap->otherschoosei],0,sizeof(swap->privkeys[swap->otherschoosei]));
if ( (retstr= instantdex_choosei(swap,newjson,argjson,serdata,serdatalen)) != 0 )
return(retstr);
9 years ago
/*if ( swap->isbob == 0 )
9 years ago
{
9 years ago
if ( (swap->feetx= instantdex_bobtx(myinfo,coinbtc,&swap->ftxid,swap->otherpubs[0],swap->mypubs[0],swap->privkeys[swap->otherschoosei],reftime,swap->insurance,1)) != 0 )
9 years ago
{
9 years ago
jaddstr(newjson,"feetx",swap->feetx);
jaddbits256(newjson,"ftxid",swap->ftxid);
// broadcast to network
9 years ago
}
9 years ago
}*/
9 years ago
if ( swap->isbob != 0 )
9 years ago
{
strcpy(swap->nextstate,"step4");
printf("BOB sends (%s), next.(%s)\n","BTCstep3",swap->nextstate);
}
else
{
strcpy(swap->nextstate,"step3");
printf("Alice sends (%s), next.(%s)\n","BTCstep2",swap->nextstate);
}
9 years ago
return(instantdex_sendcmd(myinfo,&A->offer,newjson,swap->isbob != 0 ? "BTCstep3" : "BTCstep2",swap->othertrader,INSTANTDEX_HOPS,swap->privkeys,sizeof(swap->privkeys)));
9 years ago
}
9 years ago
}
9 years ago
else if ( strcmp(cmdstr,"step2") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // bob
9 years ago
{
9 years ago
printf("%s got step2, should have other's privkeys\n",swap->isbob!=0?"BOB":"alice");
9 years ago
if ( (newjson= instantdex_newjson(myinfo,swap,argjson,swap->orderhash,A,0)) == 0 )
return(clonestr("{\"error\":\"instantdex_BTCswap step2 null newjson\"}"));
9 years ago
else
9 years ago
{
9 years ago
instantdex_privkeysextract(myinfo,swap,serdata,serdatalen);
if ( swap->cutverified == 0 || swap->otherverifiedcut == 0 )
return(clonestr("{\"error\":\"instantdex_BTCswap step2, both sides didnt validate\"}"));
9 years ago
else
9 years ago
{
9 years ago
if ( (swap->deposit= instantdex_bobtx(myinfo,coinbtc,&swap->dtxid,swap->otherpubs[0],swap->mypubs[0],swap->privkeys[swap->otherschoosei],reftime,swap->satoshis[swap->isbob],1)) != 0 )
9 years ago
{
9 years ago
jaddstr(newjson,"deposit",swap->deposit);
jaddbits256(newjson,"dtxid",swap->dtxid);
//jaddbits256(newjson,"pubBn",bitcoin_pubkey33(pubkey,swap->pubBn));
// broadcast to network
9 years ago
strcpy(swap->nextstate,"step4");
9 years ago
printf("BOB sends (%s), next.(%s)\n","BTCstep3",swap->nextstate);
9 years ago
return(instantdex_sendcmd(myinfo,&A->offer,newjson,"BTCstep3",swap->othertrader,INSTANTDEX_HOPS,0,0));
} else return(clonestr("{\"error\":\"instantdex_BTCswap Bob step2, cant create deposit\"}"));
9 years ago
}
9 years ago
} //else return(clonestr("{\"error\":\"instantdex_BTCswap step2 invalid fee\"}"));
}
9 years ago
else if ( strcmp(cmdstr,"step3") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // alice
9 years ago
{
printf("Alice got step3 should have Bob's choosei\n");
if ( (newjson= instantdex_newjson(myinfo,swap,argjson,swap->orderhash,A,0)) == 0 )
return(clonestr("{\"error\":\"instantdex_BTCswap Alice step3 null newjson\"}"));
else
{
instantdex_privkeysextract(myinfo,swap,serdata,serdatalen);
if ( swap->cutverified == 0 || swap->otherverifiedcut == 0 || bits256_nonz(swap->pubBn) == 0 )
return(clonestr("{\"error\":\"instantdex_BTCswap step3, both sides didnt validate\"}"));
else if ( instantdex_paymentverify(myinfo,coinbtc,swap,A,argjson,1) == 0 )
{
//swap->pubAm = bitcoin_pubkey33(pubkey,swap->privkeys[swap->otherschoosei]);
if ( (swap->altpayment= instantdex_alicetx(myinfo,altcoin,swap->altmsigaddr,&swap->aptxid,swap->pubAm,swap->pubBn,swap->satoshis[swap->isbob])) != 0 )
{
jaddstr(newjson,"altpayment",swap->altpayment);
jaddstr(newjson,"altmsigaddr",swap->altmsigaddr);
jaddbits256(newjson,"aptxid",swap->aptxid);
jaddbits256(newjson,"pubAm",swap->pubAm);
// broadcast to network
9 years ago
strcpy(swap->nextstate,"step5");
9 years ago
printf("Alice sends (%s), next.(%s)\n","BTCstep4",swap->nextstate);
9 years ago
return(instantdex_sendcmd(myinfo,&A->offer,newjson,"BTCstep4",swap->othertrader,INSTANTDEX_HOPS,0,0));
} else return(clonestr("{\"error\":\"instantdex_BTCswap Alice step3, error making altpay\"}"));
} else return(clonestr("{\"error\":\"instantdex_BTCswap Alice step3, invalid deposit\"}"));
9 years ago
}
}
9 years ago
else if ( strcmp(cmdstr,"step4") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // bob
9 years ago
{
printf("Bob got step4 should have Alice's altpayment\n");
if ( (newjson= instantdex_newjson(myinfo,swap,argjson,swap->orderhash,A,0)) == 0 )
return(clonestr("{\"error\":\"instantdex_BTCswap Bob step4 null newjson\"}"));
else if ( bits256_nonz(swap->pubAm) == 0 )
return(clonestr("{\"error\":\"instantdex_BTCswap step4, no pubAm\"}"));
else if ( instantdex_altpaymentverify(myinfo,altcoin,swap,A,argjson) == 0 )
{
if ( (swap->deposit= instantdex_bobtx(myinfo,coinbtc,&swap->ptxid,swap->mypubs[1],swap->otherpubs[0],swap->privkeys[swap->otherschoosei],reftime,swap->satoshis[swap->isbob],0)) != 0 )
{
jaddstr(newjson,"payment",swap->payment);
jaddbits256(newjson,"ptxid",swap->ptxid);
// broadcast to network
9 years ago
strcpy(swap->nextstate,"step6");
9 years ago
return(instantdex_sendcmd(myinfo,&A->offer,newjson,"BTCstep5",swap->othertrader,INSTANTDEX_HOPS,0,0));
} else return(clonestr("{\"error\":\"instantdex_BTCswap Bob step4, cant create payment\"}"));
} else return(clonestr("{\"error\":\"instantdex_BTCswap Alice step3, invalid deposit\"}"));
}
9 years ago
else if ( strcmp(cmdstr,"step5") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // alice
9 years ago
{
printf("Alice got step5 should have Bob's payment\n");
if ( (newjson= instantdex_newjson(myinfo,swap,argjson,swap->orderhash,A,0)) == 0 )
return(clonestr("{\"error\":\"instantdex_BTCswap Alice step5 null newjson\"}"));
else if ( instantdex_paymentverify(myinfo,coinbtc,swap,A,argjson,0) == 0 )
{
9 years ago
strcpy(swap->nextstate,"step7");
9 years ago
/*if ( (swap->spendtx= instantdex_spendpayment(myinfo,coinbtc,&swap->stxid,swap,argjson,newjson)) != 0 )
{
// broadcast to network
return(instantdex_sendcmd(myinfo,&A->A,newjson,"BTCstep6",swap->othertrader,INSTANTDEX_HOPS));
} else return(clonestr("{\"error\":\"instantdex_BTCswap Alice step5, cant spend payment\"}"));*/
} else return(clonestr("{\"error\":\"instantdex_BTCswap Bob step6, invalid payment\"}"));
}
9 years ago
else if ( strcmp(cmdstr,"step6") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // bob
9 years ago
{
printf("Bob got step6 should have Alice's privkey\n");
if ( (newjson= instantdex_newjson(myinfo,swap,argjson,swap->orderhash,A,0)) == 0 )
return(clonestr("{\"error\":\"instantdex_BTCswap Bob step6 null newjson\"}"));
9 years ago
strcpy(swap->nextstate,"step7");
9 years ago
/*else if ( instantdex_spendverify(myinfo,coinbtc,swap,A,argjson,0) == 0 )
{
if ( (swap->altspend= instantdex_spendaltpayment(myinfo,altcoin,&swap->astxid,swap,argjson,newjson)) != 0 )
{
jaddstr(newjson,"altspend",swap->altspend);
jaddbits256(newjson,"astxid",swap->astxid);
// broadcast to network
return(clonestr("{\"result\":\"Bob finished atomic swap\"}"));
} else return(clonestr("{\"error\":\"instantdex_BTCswap Bob step6, cant spend altpayment\"}"));
} else return(clonestr("{\"error\":\"instantdex_BTCswap Bob step6, invalid spend\"}"));*/
}
9 years ago
else if ( strcmp(cmdstr,"step7") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // both
{
// update status, goto refund if thresholds exceeded
retstr = clonestr("{\"result\":\"BTC swap updated state\"}");
}
9 years ago
else retstr = clonestr("{\"error\":\"BTC swap got unrecognized command\"}");
if ( retstr == 0 )
retstr = clonestr("{\"error\":\"BTC swap null retstr\"}");
if ( swap != 0 )
9 years ago
printf("BTCSWAP next.(%s) (%s) isbob.%d nextstate.%s verified.(%d %d)\n",swap->nextstate,cmdstr,swap->isbob,swap->nextstate,swap->cutverified,swap->otherverifiedcut);
9 years ago
else printf("BTCSWAP.(%s)\n",retstr);
return(retstr);
9 years ago
#endif
9 years ago
#ifdef oldway
// https://github.com/TierNolan/bips/blob/bip4x/bip-atom.mediawiki
int32_t bitcoin_2of2spendscript(int32_t *paymentlenp,uint8_t *paymentscript,uint8_t *msigscript,bits256 pub0,bits256 pub1)
{
struct vin_info V; uint8_t p2sh_rmd160[20]; int32_t p2shlen;
memset(&V,0,sizeof(V));
V.M = V.N = 2;
memcpy(V.signers[0].pubkey+1,pub0.bytes,sizeof(pub0)), V.signers[0].pubkey[0] = 0x02;
memcpy(V.signers[1].pubkey+1,pub1.bytes,sizeof(pub1)), V.signers[1].pubkey[0] = 0x03;
p2shlen = bitcoin_MofNspendscript(p2sh_rmd160,msigscript,0,&V);
*paymentlenp = bitcoin_p2shspend(paymentscript,0,p2sh_rmd160);
return(p2shlen);
9 years ago
}
/*
9 years ago
Name: Bob.Bail.In
9 years ago
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
9 years ago
Name: Alice.Bail.In
9 years ago
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
9 years ago
*/
9 years ago
char *instantdex_bailintx(struct iguana_info *coin,bits256 *txidp,struct bitcoin_spend *spend,bits256 A0,bits256 B0,uint8_t x[20],int32_t isbob)
{
9 years ago
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; i<spend->numinputs; 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; i<spend->numinputs; i++)
V[i].signers[0].privkey = spend->inputs[i].privkey;
bitcoin_verifytx(coin,&signedtxid,&signedtx,rawtxstr,V);
free(rawtxstr), free(V);
if ( signedtx != 0 )
9 years ago
printf("signed %s_bailin.%s (%s)\n",isbob!=0?"bob":"alice",bits256_str(str,signedtxid),signedtx);
9 years ago
else printf("error generating signedtx\n");
free_json(txobj);
*txidp = txid;
return(signedtx);
9 years ago
}
9 years ago
9 years ago
cJSON *instantdex_bailinspend(struct iguana_info *coin,bits256 privkey,uint64_t amount)
{
9 years ago
int32_t n; cJSON *txobj;
9 years ago
int32_t scriptv0len; uint8_t p2shscript[256],rmd160[20],scriptv0[128],pubkey[35];
bitcoin_pubkey33(pubkey,privkey);
n = bitcoin_pubkeyspend(p2shscript,0,pubkey);
9 years ago
calc_rmd160_sha256(rmd160,p2shscript,n);
9 years ago
scriptv0len = bitcoin_p2shspend(scriptv0,0,rmd160);
txobj = bitcoin_createtx(coin,0);
bitcoin_addoutput(coin,txobj,scriptv0,scriptv0len,amount);
return(txobj);
}
/*
9 years ago
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
9 years ago
Name: Alice.Payout
9 years ago
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
*/
9 years ago
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));
}
/*
9 years ago
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))
9 years ago
9 years ago
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)
*/
9 years ago
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)
9 years ago
{
9 years ago
uint8_t pubkey[33],rmd160[20]; int32_t n;
9 years ago
memcpy(pubkey+1,pub.bytes,sizeof(pub)), pubkey[0] = firstbyte;
9 years ago
n = bitcoin_pubkeyspend(p2shscript,0,pubkey);
9 years ago
calc_rmd160_sha256(rmd160,p2shscript,n);
9 years ago
init_hexbytes_noT(hexstr,rmd160,sizeof(rmd160));
return(n);
}
9 years ago
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 )
{
9 years ago
if ( (spend= instantdex_spendset(myinfo,coin,A->offer.basevolume64,INSTANTDEX_DONATION)) != 0 )
9 years ago
{
bailintx = instantdex_bailintx(coin,&bailintxid,spend,A0,B0,secret160,0);
9 years ago
refundtx = instantdex_refundtx(coin,&refundtxid,sharedprivs[0],sharedprivs[2],bailintxid,A->offer.basevolume64,coin->chain->txfee,isbob);
9 years ago
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);
9 years ago
return(instantdex_sendcmd(myinfo,&A->A,newjson,nextcmd,swap->othertrader,INSTANTDEX_HOPS));
9 years ago
} 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;
9 years ago
if ( (newjson= instantdex_newjson(myinfo,A0p,B0p,sharedprivs,secret160,isbob,argjson,hash,A)) == 0 )
9 years ago
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];
9 years ago
if ( (newjson= instantdex_newjson(myinfo,&A0,&B0,sharedprivs,secret160,isbob,argjson,hash,A)) == 0 )
9 years ago
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 )
9 years ago
return(instantdex_sendcmd(myinfo,&A->A,newjson,nextstate,swap->othertrader,INSTANTDEX_HOPS));
9 years ago
else return(clonestr("{\"result\":\"instantdex_BTCswap advance complete, wait or refund\"}"));
} else return(clonestr("{\"error\":\"instantdex_BTCswap advance cant find statusjson\"}"));
}
9 years ago
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
{
9 years ago
uint8_t secret160[20]; bits256 hash,traderpub,A0,B0,sharedprivs[4]; uint64_t satoshis[2];
cJSON *newjson; struct instantdex_accept *ap; char *retstr=0,*str;
9 years ago
int32_t locktime,isbob=0,offerdir = 0; struct iguana_info *coinbtc,*other;
9 years ago
if ( exchange == 0 )
return(clonestr("{\"error\":\"instantdex_BTCswap null exchange ptr\"}"));
9 years ago
offerdir = instantdex_bidaskdir(A);
9 years ago
if ( (other= iguana_coinfind(A->offer.base)) == 0 || (coinbtc= iguana_coinfind("BTC")) == 0 )
9 years ago
{
9 years ago
printf("other.%p coinbtc.%p (%s/%s)\n",other,coinbtc,A->offer.base,A->offer.rel);
9 years ago
return(clonestr("{\"error\":\"instantdex_BTCswap cant find btc or other coin info\"}"));
9 years ago
}
9 years ago
locktime = (uint32_t)(A->offer.expiration + INSTANTDEX_LOCKTIME);
if ( strcmp(A->offer.rel,"BTC") != 0 )
9 years ago
return(clonestr("{\"error\":\"instantdex_BTCswap offer non BTC rel\"}"));
9 years ago
vcalc_sha256(0,hash.bytes,(void *)&A->A,sizeof(ap->offer));
9 years ago
if ( hash.txid != A->orderid )
return(clonestr("{\"error\":\"txid mismatches orderid\"}"));
9 years ago
satoshis[0] = A->offer.basevolume64;
satoshis[1] = instantdex_relsatoshis(A->offer.price64,A->offer.basevolume64);
9 years ago
//printf("got offer.(%s) offerside.%d offerdir.%d\n",jprint(argjson,0),A->offer.myside,A->offer.acceptdir);
9 years ago
if ( strcmp(cmdstr,"offer") == 0 ) // sender is Bob, receiver is network (Alice)
9 years ago
{
9 years ago
if ( A->offer.expiration < (time(NULL) + INSTANTDEX_DURATION) )
9 years ago
return(clonestr("{\"error\":\"instantdex_BTCswap offer too close to expiration\"}"));
9 years ago
if ( (ap= instantdex_acceptable(exchange,A,myinfo->myaddr.nxt64bits)) != 0 )
9 years ago
{
9 years ago
isbob = 0;
9 years ago
if ( (newjson= instantdex_newjson(myinfo,&A0,&B0,sharedprivs,secret160,isbob,argjson,hash,A)) == 0 )
9 years ago
return(clonestr("{\"error\":\"instantdex_BTCswap offer null newjson\"}"));
else
9 years ago
{
9 years ago
// 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));
9 years ago
}
}
9 years ago
else
9 years ago
{
9 years ago
printf("no matching trade.(%s)\n",jprint(argjson,0));
9 years ago
if ( (str= InstantDEX_minaccept(myinfo,0,argjson,0,A->offer.base,"BTC",dstr(A->offer.price64),dstr(A->offer.basevolume64))) != 0 )
9 years ago
free(str);
9 years ago
}
9 years ago
}
9 years ago
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));
9 years ago
}
9 years ago
else if ( strcmp(cmdstr,"accept") == 0 ) // sender is Bob, receiver is Alice
9 years ago
{
9 years ago
isbob = 0;
newjson = instantdex_payout(myinfo,other,exchange,A,secret160,isbob,&A0,&B0,sharedprivs,hash,satoshis,argjson);
9 years ago
return(instantdex_sendcmd(myinfo,&A->A,newjson,"BTCconfirm",swap->othertrader,INSTANTDEX_HOPS));
9 years ago
}
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);
9 years ago
return(instantdex_sendcmd(myinfo,&A->A,newjson,"BTCbroadcast",swap->othertrader,INSTANTDEX_HOPS));
9 years ago
}
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));
9 years ago
}
9 years ago
else if ( strcmp(cmdstr,"complete") == 0 ) // sender is Bob, receiver is Alice
9 years ago
{
9 years ago
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));
9 years ago
}
else retstr = clonestr("{\"error\":\"BTC swap got unrecognized command\"}");
9 years ago
if ( retstr == 0 )
retstr = clonestr("{\"error\":\"BTC swap null retstr\"}");
9 years ago
return(retstr);
}
9 years ago
#endif