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.
 
 
 
 
 
 

259 lines
13 KiB

/******************************************************************************
* 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"
// 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(uint8_t pubkey[33],bits256 privkey,bits256 hash,int32_t n)
{
bits256 tmppriv,shared,iters; int32_t i;
iters = shared = curve25519_shared(privkey,hash);
for (i=0; i<n; i++)
iters = curve25519(iters,curve25519(iters,curve25519_basepoint9()));
vcalc_sha256cat(tmppriv.bytes,shared.bytes,sizeof(shared),iters.bytes,sizeof(iters));
return(bitcoin_pubkey33(pubkey,tmppriv));
}
int32_t instantdex_pubkeyargs(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; i<numpubs*100&&n<numpubs; i++)
{
tmp = instantdex_sharedpub256(pubkey,privkey,hash,i+1);
if ( pubkey[0] != firstbyte )
continue;
buf[1] = '0' + n++;
jaddbits256(argjson,buf,tmp);
}
return(n);
}
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);
}
/*struct bitcoin_unspent { bits256 txid,privkey; uint64_t value; int32_t vout; };
struct bitcoin_spend
{
char changeaddr[64];
int32_t numinputs;
uint64_t txfee,input_satoshis,satoshis;
struct bitcoin_unspent inputs[];
};*/
char *instantdex_bailintx(struct iguana_info *coin,bits256 *txidp,struct bitcoin_spend *spend,bits256 A0,bits256 B0,uint8_t x[20],int32_t isbob)
{
/*Input value: B + 2*fb + change
Input source: (From Bob's coins, multiple inputs are allowed)
Output 0 value: B
ScriptPubKey 0: OP_HASH160 Hash160(P2SH Redeem) OP_EQUAL
Output 1 value: fb
ScriptPubKey 1: OP_HASH160 Hash160(x) OP_EQUALVERIFY pub-A1 OP_CHECKSIG
Output 2 value: change
ScriptPubKey 2: <= 100 bytes
P2SH Redeem: OP_2 pub-A1 pub-B1 OP_2 OP_CHECKMULTISIG
Name: Alice.Bail.In
Input value: A + 2*fa + change
Input source: (From Alice's altcoins, multiple inputs are allowed)
Output 0 value: A
ScriptPubKey 0: OP_HASH160 Hash160(P2SH Redeem) OP_EQUAL
Output 1 value: fa
ScriptPubKey 1: OP_HASH160 Hash160(x) OP_EQUAL
Output 2 value: change
ScriptPubKey 2: <= 100 bytes*/
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 )
printf("signed bob_bailin.%s (%s)\n",bits256_str(str,signedtxid),signedtx);
else printf("error generating signedtx\n");
free_json(txobj);
*txidp = txid;
return(signedtx);
}
int32_t instantdex_calcx20(char hexstr[41],uint8_t *p2shscript,uint8_t firstbyte,bits256 pub3)
{
uint8_t pubkey[33],script[64],rmd160[20]; int32_t n; bits256 hash;
memcpy(pubkey+1,pub3.bytes,sizeof(pub3)), pubkey[0] = firstbyte;
n = bitcoin_pubkeyspend(p2shscript,0,pubkey);
vcalc_sha256(0,hash.bytes,script,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;
struct instantdex_accept A; cJSON *newjson; bits256 hash,pub3;
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_pubkeyargs(newjson,4,myinfo->persistent_priv,hash,0x03) != 4 )
return(clonestr("{\"error\":\"highly unlikely run of 02 pubkeys\"}"));
pub3 = jbits256(newjson,"B3");
jdelete(newjson,"B3");
instantdex_calcx20(xstr,xscript,0x03,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));
}
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 script[999],p2sh_rmd160[20],secret160[20],pubkey[36],addrtype;
bits256 hash,bailintxid,A0,B0; struct bitcoin_spend SPEND;
struct instantdex_accept *ap; uint64_t satoshis,othersatoshis,orderid;
char p2sh_coinaddr[64],*senderaddr,otheraddr[64],base[24],coinaddr[64],*retstr,*bailintx;
int32_t scriptlen,locktime,offerdir = 0; struct iguana_info *coinbtc,*other; cJSON *newjson;
retstr = 0;
memset(&SPEND,0,sizeof(SPEND));
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\"}"));
if ( strcmp(cmdstr,"offer") == 0 ) // sender is Bob, receiver is network (Alice)
{
// should add to orderbook if not accepted
if ( A->A.expiration < (time(NULL) + INSTANTDEX_DURATION) )
return(clonestr("{\"error\":\"instantdex_BTCswap offer too close to expiration\"}"));
printf("got offer.(%s) offerside.%d offerdir.%d\n",jprint(argjson,0),A->A.myside,A->A.acceptdir);
if ( (ap= instantdex_acceptable(exchange,A,myinfo->myaddr.nxt64bits)) != 0 )
{
ap->pendingvolume64 -= A->A.basevolume64;
satoshis = instantdex_relsatoshis(A->A.price64,A->A.basevolume64);
newjson = cJSON_CreateObject();
if ( instantdex_pubkeyargs(argjson,3,myinfo->persistent_priv,hash,0x02) != 3 )
return(clonestr("{\"error\":\"highly unlikely run of 03 pubkeys\"}"));
jadd64bits(newjson,"id",A->orderid);
jadd64bits(newjson,"BTC",satoshis);
jadd64bits(newjson,"v",A->A.basevolume64);
jaddstr(newjson,"b",other->symbol);
jaddstr(newjson,other->symbol,otheraddr);
jaddstr(newjson,"p2sh",p2sh_coinaddr);
bailintx = instantdex_bailintx(coinbtc,&bailintxid,&SPEND,A0,B0,secret160,1);
jaddstr(newjson,"bailin",bailintx);
jaddbits256(newjson,"bailintxid",bailintxid);
free(bailintx);
return(instantdex_sendcmd(myinfo,newjson,"proposal",myinfo->ipaddr,INSTANTDEX_HOPS));
} else printf("no matching trade.(%s)\n",jprint(argjson,0));
}
else if ( strcmp(cmdstr,"proposal") == 0 ) // sender is Alice, receiver is Bob
{
satoshis = j64bits(argjson,"BTC");
orderid = j64bits(argjson,"id");
othersatoshis = j64bits(argjson,"v");
senderaddr = myinfo->myaddr.BTC;
if ( jobj(argjson,other->symbol) != 0 )
safecopy(otheraddr,jstr(argjson,other->symbol),sizeof(otheraddr));
if ( jobj(argjson,"b") != 0 )
safecopy(base,jstr(argjson,"b"),sizeof(base));
printf("proposal orderid.%llu BTC satoshis %.8f for %s vol %.8f ps2h.%s\n",A->orderid,dstr(satoshis),base,dstr(othersatoshis),p2sh_coinaddr);
if ( A->orderid != orderid )
{
printf("orderid mismatch %llu vs %llu\n",(long long)orderid,(long long)A->orderid);
return(clonestr("{\"error\":\"instantdex_BTCswap orderid mismatch\"}"));
}
if ( senderaddr == 0 || strcmp(A->A.base,base) != 0 || strcmp(A->A.rel,"BTC") != 0 )
{
printf("senderaddr.%p base.(%s vs %s) rel.(%s vs %s)\n",senderaddr,A->A.base,base,A->A.rel,"BTC");
return(clonestr("{\"error\":\"instantdex_BTCswap base or rel mismatch\"}"));
}
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\"}"));
}
}
if ( satoshis != instantdex_relsatoshis(A->A.price64,A->A.basevolume64) )
{
printf("satoshis mismatch %llu vs %llu\n",(long long)satoshis,(long long)instantdex_relsatoshis(A->A.price64,A->A.basevolume64));
return(clonestr("{\"error\":\"instantdex_BTCswap satoshis mismatch\"}"));
}
if ( othersatoshis != A->A.basevolume64 )
{
printf("othersatoshis mismatch %llu vs %llu\n",(long long)satoshis,(long long)A->A.basevolume64);
return(clonestr("{\"error\":\"instantdex_BTCswap satoshis mismatch\"}"));
}
// return(instantdex_sendcmd(myinfo,newjson,"accept",myinfo->ipaddr,INSTANTDEX_HOPS));
}
else if ( strcmp(cmdstr,"accept") == 0 ) // sender is Bob, receiver is Alice
{
}
else if ( strcmp(cmdstr,"confirm") == 0 ) // both send and receive
{
}
else retstr = clonestr("{\"error\":\"BTC swap got unrecognized command\"}");
if ( retstr == 0 )
retstr = clonestr("{\"error\":\"BTC swap null retstr\"}");
return(retstr);
}