From 883167f7e6c17fe73ea94b4007479b7dca96b041 Mon Sep 17 00:00:00 2001 From: jl777 Date: Mon, 15 Feb 2016 22:30:12 -0300 Subject: [PATCH] revised FSM --- deprecated/obsolete.h | 156 ++++++ iguana/SuperNET.h | 1 + iguana/exchanges/bitcoin.c | 6 +- iguana/exchanges777.h | 5 +- iguana/iguana_exchanges.c | 13 +- iguana/iguana_init.c | 18 +- iguana/iguana_instantdex.c | 207 +++++--- iguana/main.c | 2 +- iguana/swaps/iguana_BTCswap.c | 828 +++++++++++++++++++------------- iguana/swaps/iguana_PAXswap.c | 23 + includes/secp256k1.h | 453 +++++++++++++++++ includes/secp256k1_ecdh.h | 30 ++ includes/secp256k1_rangeproof.h | 186 +++++++ includes/secp256k1_schnorr.h | 173 +++++++ 14 files changed, 1654 insertions(+), 447 deletions(-) create mode 100644 includes/secp256k1.h create mode 100644 includes/secp256k1_ecdh.h create mode 100644 includes/secp256k1_rangeproof.h create mode 100644 includes/secp256k1_schnorr.h diff --git a/deprecated/obsolete.h b/deprecated/obsolete.h index 2916d767d..42938968c 100644 --- a/deprecated/obsolete.h +++ b/deprecated/obsolete.h @@ -13565,5 +13565,161 @@ len = 0; swap->minperc = minperc; return(instantdex_statemachine(myinfo,exchange,A,cmdstr,swap,argjson,serdata,serdatalen,altcoin,coinbtc)); } + +#ifdef xxx + if ( strcmp(cmdstr,"step1") == 0 && strcmp(swap->nextstate,cmdstr) == 0 ) // either + { + printf("%s got step1, should have other's choosei\n",swap->isbob!=0?"BOB":"alice"); + 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\"}")); + else + { + printf("%s chose.%d\n",swap->isbob==0?"BOB":"alice",swap->otherschoosei); + 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); + /*if ( swap->isbob == 0 ) + { + if ( (swap->feetx= instantdex_bobtx(myinfo,coinbtc,&swap->ftxid,swap->otherpubs[0],swap->mypubs[0],swap->privkeys[swap->otherschoosei],reftime,swap->insurance,1)) != 0 ) + { + jaddstr(newjson,"feetx",swap->feetx); + jaddbits256(newjson,"ftxid",swap->ftxid); + // broadcast to network + } + }*/ + if ( swap->isbob != 0 ) + { + 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); + } + return(instantdex_sendcmd(myinfo,&A->offer,newjson,swap->isbob != 0 ? "BTCstep3" : "BTCstep2",swap->othertrader,INSTANTDEX_HOPS,swap->privkeys,sizeof(swap->privkeys))); + } + } + else if ( strcmp(cmdstr,"step2") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // bob + { + printf("%s got step2, should have other's privkeys\n",swap->isbob!=0?"BOB":"alice"); + if ( (newjson= instantdex_newjson(myinfo,swap,argjson,swap->orderhash,A,0)) == 0 ) + return(clonestr("{\"error\":\"instantdex_BTCswap step2 null newjson\"}")); + else + { + instantdex_privkeysextract(myinfo,swap,serdata,serdatalen); + if ( swap->cutverified == 0 || swap->otherverifiedcut == 0 ) + return(clonestr("{\"error\":\"instantdex_BTCswap step2, both sides didnt validate\"}")); + else + { + 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 ) + { + jaddstr(newjson,"deposit",swap->deposit); + jaddbits256(newjson,"dtxid",swap->dtxid); + //jaddbits256(newjson,"pubBn",bitcoin_pubkey33(pubkey,swap->pubBn)); + // broadcast to network + strcpy(swap->nextstate,"step4"); + printf("BOB sends (%s), next.(%s)\n","BTCstep3",swap->nextstate); + 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\"}")); + } + } //else return(clonestr("{\"error\":\"instantdex_BTCswap step2 invalid fee\"}")); + } + else if ( strcmp(cmdstr,"step3") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // alice + { + 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 + strcpy(swap->nextstate,"step5"); + printf("Alice sends (%s), next.(%s)\n","BTCstep4",swap->nextstate); + 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\"}")); + } + } + else if ( strcmp(cmdstr,"step4") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // bob + { + 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 + strcpy(swap->nextstate,"step6"); + 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\"}")); + } + else if ( strcmp(cmdstr,"step5") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // alice + { + 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 ) + { + strcpy(swap->nextstate,"step7"); + /*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\"}")); + } + else if ( strcmp(cmdstr,"step6") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // bob + { + 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\"}")); + strcpy(swap->nextstate,"step7"); + /*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\"}"));*/ + } + 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\"}"); + } + else retstr = clonestr("{\"error\":\"BTC swap got unrecognized command\"}"); + if ( retstr == 0 ) + retstr = clonestr("{\"error\":\"BTC swap null retstr\"}"); + if ( swap != 0 ) + printf("BTCSWAP next.(%s) (%s) isbob.%d nextstate.%s verified.(%d %d)\n",swap->nextstate,cmdstr,swap->isbob,swap->nextstate,swap->cutverified,swap->otherverifiedcut); + else printf("BTCSWAP.(%s)\n",retstr); + return(retstr); +#endif #endif diff --git a/iguana/SuperNET.h b/iguana/SuperNET.h index a12b396cf..a61cb40b0 100644 --- a/iguana/SuperNET.h +++ b/iguana/SuperNET.h @@ -165,6 +165,7 @@ void category_posthexmsg(struct supernet_info *myinfo,bits256 categoryhash,bits2 void *category_subscribe(struct supernet_info *myinfo,bits256 category,bits256 subhash); struct category_msg *category_gethexmsg(struct supernet_info *myinfo,bits256 categoryhash,bits256 subhash); char *SuperNET_htmlstr(char *fname,char *htmlstr,int32_t maxsize,char *agentstr); +queue_t *category_Q(bits256 categoryhash,bits256 subhash); char *SuperNET_categorymulticast(struct supernet_info *myinfo,int32_t surveyflag,bits256 categoryhash,bits256 subhash,char *message,int32_t maxdelay,int32_t broadcastflag,int32_t plaintext,cJSON *argjson,char *remoteaddr); bits256 calc_categoryhashes(bits256 *subhashp,char *category,char *subcategory); diff --git a/iguana/exchanges/bitcoin.c b/iguana/exchanges/bitcoin.c index 5b4d80fe5..0a36526bb 100755 --- a/iguana/exchanges/bitcoin.c +++ b/iguana/exchanges/bitcoin.c @@ -1541,15 +1541,15 @@ uint64_t TRADE(int32_t dotrade,char **retstrp,struct exchange_info *exchange,cha jaddnum(json,"volume",volume); jaddstr(json,"BTC",myinfo->myaddr.BTC); //printf("trade dir.%d (%s/%s) %.6f vol %.8f\n",dir,base,"BTC",price,volume); - if ( (str= instantdex_queueaccept(myinfo,&ap,exchange,base,"BTC",price,volume,-dir,dir > 0 ? "BTC" : base,INSTANTDEX_OFFERDURATION,myinfo->myaddr.nxt64bits,0)) != 0 ) + if ( (str= instantdex_queueaccept(myinfo,&ap,exchange,base,"BTC",price,volume,-dir,dir > 0 ? "BTC" : base,INSTANTDEX_OFFERDURATION,myinfo->myaddr.nxt64bits,0)) != 0 && ap != 0 ) { if ( (tmp= cJSON_Parse(str)) != 0 ) { txid = j64bits(json,"orderid"); - if ( (str= instantdex_btcoffer(myinfo,exchange,ap,json)) != 0 ) + if ( (str= instantdex_sendoffer(myinfo,exchange,ap,json)) != 0 ) // adds to statemachine { json = cJSON_CreateObject(); - jaddstr(json,"BTCoffer",str); + jaddstr(json,"BTCoffer",instantdex_selectqueue(exchange,ap,str)); } else printf("null return from btcoffer\n"); free_json(tmp); } else printf("queueaccept return parse error.(%s)\n",str); diff --git a/iguana/exchanges777.h b/iguana/exchanges777.h index 1e1e10907..1bababbf9 100755 --- a/iguana/exchanges777.h +++ b/iguana/exchanges777.h @@ -52,7 +52,7 @@ struct exchange_info uint32_t exchangeid,pollgap,lastpoll; uint64_t lastnonce,exchangebits; double commission; void *privatedata; - CURL *cHandle; queue_t requestQ,pricesQ,pendingQ,tradebotsQ,acceptableQ; + CURL *cHandle; queue_t requestQ,pricesQ,statemachineQ,tradebotsQ,acceptableQ; }; struct instantdex_msghdr @@ -120,6 +120,7 @@ double instaforex_price(struct exchange_info *exchange,char *base,char *rel,stru char *instantdex_queueaccept(struct supernet_info *myinfo,struct instantdex_accept **aptrp,struct exchange_info *exchange,char *base,char *rel,double price,double basevolume,int32_t acceptdir,char *mysidestr,int32_t duration,uint64_t txid,int32_t queueflag); void instantdex_update(struct supernet_info *myinfo); char *instantdex_sendcmd(struct supernet_info *myinfo,struct instantdex_offer *offer,cJSON *argjson,char *cmdstr,bits256 desthash,int32_t hops,void *extra,int32_t extralen); -char *instantdex_btcoffer(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson); // Bob sending to network (Alice) +char *instantdex_sendoffer(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson); // Bob sending to network (Alice) +char *instantdex_selectqueue(struct exchange_info *exchange,struct instantdex_accept *ap,char *retstr); #endif diff --git a/iguana/iguana_exchanges.c b/iguana/iguana_exchanges.c index ff5cf9cf9..4391c2a02 100755 --- a/iguana/iguana_exchanges.c +++ b/iguana/iguana_exchanges.c @@ -660,7 +660,7 @@ void exchanges777_loop(void *ptr) { if ( strcmp(exchange->name,"bitcoin") == 0 ) { - //instantdex_update(SuperNET_MYINFO(0)); + instantdex_update(SuperNET_MYINFO(0)); //printf("InstantDEX call update\n"); } if ( (req= queue_dequeue(&exchange->pricesQ,0)) != 0 ) @@ -890,7 +890,7 @@ struct exchange_info *exchange_create(char *exchangestr,cJSON *argjson) iguana_initQ(&exchange->requestQ,"request"); iguana_initQ(&exchange->acceptableQ,"acceptable"); iguana_initQ(&exchange->tradebotsQ,"tradebots"); - iguana_initQ(&exchange->pendingQ,"pending"); + iguana_initQ(&exchange->statemachineQ,"statemachineQ"); exchange->exchangeid = exchangeid; safecopy(exchange->name,exchangestr,sizeof(exchange->name)); exchange->exchangebits = stringbits(exchange->name); @@ -967,7 +967,6 @@ THREE_STRINGS_AND_THREE_INTS(InstantDEX,orderbook,exchange,base,rel,depth,allfie struct exchange_info *ptr; if ( remoteaddr == 0 ) { - //instantdex_update(myinfo); if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 ) return(exchanges777_Qprices(ptr,base,rel,juint(json,"maxseconds"),allfields,depth,json,0,ptr->commission)); else return(clonestr("{\"error\":\"cant find or create exchange\"}")); @@ -979,7 +978,6 @@ THREE_STRINGS_AND_THREE_DOUBLES(InstantDEX,buy,exchange,base,rel,price,volume,do struct exchange_info *ptr; if ( remoteaddr == 0 ) { - //instantdex_update(myinfo); if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 ) return(exchanges777_Qtrade(ptr,base,rel,juint(json,"maxseconds"),dotrade,1,price,volume,json)); else return(clonestr("{\"error\":\"cant find or create exchange\"}")); @@ -991,7 +989,6 @@ THREE_STRINGS_AND_THREE_DOUBLES(InstantDEX,sell,exchange,base,rel,price,volume,d struct exchange_info *ptr; if ( remoteaddr == 0 ) { - //instantdex_update(myinfo); if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 ) return(exchanges777_Qtrade(ptr,base,rel,juint(json,"maxseconds"),dotrade,-1,price,volume,json)); else return(clonestr("{\"error\":\"cant find or create exchange\"}")); @@ -1003,7 +1000,6 @@ THREE_STRINGS_AND_DOUBLE(InstantDEX,withdraw,exchange,base,destaddr,amount) struct exchange_info *ptr; if ( remoteaddr == 0 ) { - //instantdex_update(myinfo); if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 ) return(exchanges777_Qrequest(ptr,'W',base,0,juint(json,"maxseconds"),0,destaddr,amount,json)); else return(clonestr("{\"error\":\"cant find or create exchange\"}")); @@ -1015,7 +1011,6 @@ TWO_STRINGS(InstantDEX,balance,exchange,base) struct exchange_info *ptr; if ( remoteaddr == 0 ) { - //instantdex_update(myinfo); if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 ) return(exchanges777_Qrequest(ptr,'B',base,0,juint(json,"maxseconds"),0,0,0,json)); else return(clonestr("{\"error\":\"cant find or create exchange\"}")); @@ -1027,7 +1022,6 @@ TWO_STRINGS(InstantDEX,orderstatus,exchange,orderid) struct exchange_info *ptr; if ( remoteaddr == 0 ) { - //instantdex_update(myinfo); if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 ) return(exchanges777_Qrequest(ptr,'P',0,0,juint(json,"maxseconds"),calc_nxt64bits(orderid),0,0,json)); else return(clonestr("{\"error\":\"cant find or create exchange\"}")); @@ -1039,7 +1033,6 @@ TWO_STRINGS(InstantDEX,cancelorder,exchange,orderid) struct exchange_info *ptr; if ( remoteaddr == 0 ) { - //instantdex_update(myinfo); if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 ) return(exchanges777_Qrequest(ptr,'C',0,0,juint(json,"maxseconds"),calc_nxt64bits(orderid),0,0,json)); else return(clonestr("{\"error\":\"cant find or create exchange\"}")); @@ -1051,7 +1044,6 @@ STRING_ARG(InstantDEX,openorders,exchange) struct exchange_info *ptr; if ( remoteaddr == 0 ) { - //instantdex_update(myinfo); if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 ) return(exchanges777_Qrequest(ptr,'O',0,0,juint(json,"maxseconds"),0,0,0,json)); else return(clonestr("{\"error\":\"cant find or create exchange\"}")); @@ -1063,7 +1055,6 @@ STRING_ARG(InstantDEX,tradehistory,exchange) struct exchange_info *ptr; if ( remoteaddr == 0 ) { - //instantdex_update(myinfo); if ( (ptr= exchanges777_info(exchange,1,json,remoteaddr)) != 0 ) return(exchanges777_Qrequest(ptr,'H',0,0,juint(json,"maxseconds"),0,0,0,json)); else return(clonestr("{\"error\":\"cant find or create exchange\"}")); diff --git a/iguana/iguana_init.c b/iguana/iguana_init.c index 325d512e5..bf4e3b1ff 100755 --- a/iguana/iguana_init.c +++ b/iguana/iguana_init.c @@ -252,7 +252,7 @@ void iguana_parseline(struct iguana_info *coin,int32_t iter,FILE *fp) if ( strcmp(checkstr,line+k+1 + 64 + 1) == 0 ) { init_hexbytes_noT(checkstr,hash2.bytes,sizeof(hash2)); - //char str[65],str2[65]; printf(">>>> bundle.%d got (%s)/(%s) allhash.(%s)\n",height,bits256_str(str,hash2),checkstr,bits256_str(str2,allhash)); + char str[65],str2[65]; printf(">>>> bundle.%d got (%s)/(%s) allhash.(%s)\n",height,bits256_str(str,hash2),checkstr,bits256_str(str2,allhash)); if ( (bp= iguana_bundlecreate(coin,&bundlei,height,hash2,allhash,0)) != 0 ) { bp->bundleheight = height; @@ -275,22 +275,6 @@ void iguana_parseline(struct iguana_info *coin,int32_t iter,FILE *fp) } } } - /*init_hexbytes_noT(checkstr,hash2.bytes,sizeof(hash2)); - if ( strncmp(checkstr,line+k+1,64) == 0 ) - { - if ( (height % coin->chain->bundlesize) == 1 ) - { - if ( (bp= coin->bundles[height/coin->chain->bundlesize]) != 0 ) - { - if ( iguana_bundlehash2add(coin,0,bp,1,hash2) == 0 ) - { - //printf("add bundle.%d:%d (%s)\n",bundleheight,bp->hdrsi,bits256_str(str,hash2)); - if ( (block= iguana_blockfind(coin,hash2)) != 0 ) - block->mainchain = 1, block->height = bundleheight+1; - } - } - } - }*/ } } } diff --git a/iguana/iguana_instantdex.c b/iguana/iguana_instantdex.c index bdcd66954..8c019404c 100755 --- a/iguana/iguana_instantdex.c +++ b/iguana/iguana_instantdex.c @@ -33,8 +33,8 @@ struct instantdex_event { char cmdstr[24],sendcmd[16]; struct instantdex_statein struct instantdex_stateinfo { char name[24]; - cJSON *(*process)(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson,uint8_t **serdatap,int32_t *serdatalenp); - cJSON *(*timeout)(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson,uint8_t **serdatap,int32_t *serdatalenp); + cJSON *(*process)(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp); + cJSON *(*timeout)(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp); struct instantdex_stateinfo *timeoutevent,*errorevent; struct instantdex_event *events; int32_t numevents; }; @@ -42,18 +42,37 @@ struct instantdex_stateinfo struct bitcoin_swapinfo { bits256 privkeys[777],mypubs[2],otherpubs[2],privAm,pubAm,privBn,pubBn; - bits256 orderhash,dtxid,ptxid,aptxid,astxid,stxid,ftxid,othertrader; + bits256 orderhash,deposittxid,paymenttxid,altpaymenttxid,myfeetxid,otherfeetxid,othertrader; uint64_t otherscut[777][2],deck[777][2],satoshis[2],insurance,bidid,askid; int32_t isbob,choosei,otherschoosei,cutverified,otherverifiedcut; - double minperc; - char altmsigaddr[64],*deposit,*payment,*altpayment,*altspend,*spendtx,*feetx; + char altmsigaddr[64],expectedcmdstr[16],*deposit,*payment,*altpayment,*myfeetx,*otherfeetx; + double minperc,depositconfirms,paymentconfirms,altpaymentconfirms,myfeeconfirms,otherfeeconfirms; struct instantdex_stateinfo *state; uint32_t expiration; }; struct instantdex_stateinfo *BTC_states; int32_t BTC_numstates; -cJSON *instantdex_defaultprocess(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson,uint8_t **serdatap,int32_t *serdatalenp) +void instantdex_swapfree(struct instantdex_accept *A,struct bitcoin_swapinfo *swap) { - cJSON *newjson=0; uint8_t *serdata = *serdatap; int32_t serdatalen = *serdatalenp; + if ( A != 0 ) + free(A); + if ( swap != 0 ) + { + if ( swap->deposit != 0 ) + free(swap->deposit); + if ( swap->payment != 0 ) + free(swap->payment); + if ( swap->altpayment != 0 ) + free(swap->altpayment); + if ( swap->myfeetx != 0 ) + free(swap->myfeetx); + if ( swap->otherfeetx != 0 ) + free(swap->otherfeetx); + } +} + +cJSON *instantdex_defaultprocess(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + uint8_t *serdata = *serdatap; int32_t serdatalen = *serdatalenp; *serdatap = 0, *serdatalenp = 0; if ( serdata != 0 && serdatalen > 0 ) { @@ -62,9 +81,9 @@ cJSON *instantdex_defaultprocess(struct supernet_info *myinfo,struct exchange_in return(newjson); } -cJSON *instantdex_defaulttimeout(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson,uint8_t **serdatap,int32_t *serdatalenp) +cJSON *instantdex_defaulttimeout(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) { - cJSON *newjson=0; uint8_t *serdata = *serdatap; int32_t serdatalen = *serdatalenp; + uint8_t *serdata = *serdatap; int32_t serdatalen = *serdatalenp; *serdatap = 0, *serdatalenp = 0; if ( serdata != 0 && serdatalen > 0 ) { @@ -90,7 +109,7 @@ struct instantdex_stateinfo *instantdex_statefind(struct instantdex_stateinfo *s return(0); } -struct instantdex_stateinfo *instantdex_statecreate(struct instantdex_stateinfo *states,int32_t *numstatesp,char *name,cJSON *(*process_func)(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson,uint8_t **serdatap,int32_t *serdatalenp),cJSON *(*timeout_func)(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson,uint8_t **serdatap,int32_t *serdatalenp),char *timeoutstr,char *errorstr) +struct instantdex_stateinfo *instantdex_statecreate(struct instantdex_stateinfo *states,int32_t *numstatesp,char *name,cJSON *(*process_func)(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp),cJSON *(*timeout_func)(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp),char *timeoutstr,char *errorstr) { struct instantdex_stateinfo *timeoutstate,*errorstate,*state = 0; if ( (state= instantdex_statefind(states,*numstatesp,name)) == 0 ) @@ -433,13 +452,13 @@ cJSON *instantdex_acceptjson(struct instantdex_accept *ap) return(item); } -struct instantdex_accept *instantdex_pendingfind(struct supernet_info *myinfo,struct exchange_info *exchange,uint64_t orderid) +struct instantdex_accept *instantdex_statemachinefind(struct supernet_info *myinfo,struct exchange_info *exchange,uint64_t orderid) { struct instantdex_accept PAD,*ap,*retap = 0; uint32_t now; now = (uint32_t)time(NULL); memset(&PAD,0,sizeof(PAD)); - queue_enqueue("pendingQ",&exchange->pendingQ,&PAD.DL,0); - while ( (ap= queue_dequeue(&exchange->pendingQ,0)) != 0 && ap != &PAD ) + queue_enqueue("statemachineQ",&exchange->statemachineQ,&PAD.DL,0); + while ( (ap= queue_dequeue(&exchange->statemachineQ,0)) != 0 && ap != &PAD ) { if ( now < ap->offer.expiration && ap->dead == 0 ) { @@ -450,7 +469,7 @@ struct instantdex_accept *instantdex_pendingfind(struct supernet_info *myinfo,st { printf("expired pending, need to take action\n"); } - queue_enqueue("pendingQ",&exchange->pendingQ,&PAD.DL,0); + queue_enqueue("statemachineQ",&exchange->statemachineQ,&PAD.DL,0); } return(retap); } @@ -525,7 +544,7 @@ struct instantdex_accept *instantdex_acceptable(struct supernet_info *myinfo,str } } } - if ( ap != retap) + if ( ap != retap ) queue_enqueue("acceptableQ",&exchange->acceptableQ,&ap->DL,0); } else free(ap); } @@ -631,7 +650,7 @@ char *instantdex_swapset(struct supernet_info *myinfo,struct instantdex_accept * struct bitcoin_swapinfo *swap; bits256 orderhash,traderpub; struct iguana_info *coinbtc; if ( (swap= A->info) == 0 ) return(clonestr("{\"error\":\"no swapinfo set\"}")); - relsatoshis = instantdex_relsatoshis(A->offer.price64,A->offer.basevolume64); + relsatoshis = instantdex_BTCsatoshis(A->offer.price64,A->offer.basevolume64); traderpub = jbits256(argjson,"traderpub"); if ( (minperc= jdouble(argjson,"p")) < INSTANTDEX_MINPERC ) minperc = INSTANTDEX_MINPERC; @@ -665,48 +684,50 @@ char *instantdex_swapset(struct supernet_info *myinfo,struct instantdex_accept * return(0); } -char *instantdex_btcoffer(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson) // Bob sending to network (Alice) +char *instantdex_addfeetx(struct supernet_info *myinfo,cJSON *newjson,struct instantdex_accept *ap,struct bitcoin_swapinfo *swap,char *bobstate,char *alicestate) { - struct iguana_info *other; struct bitcoin_swapinfo *swap; int32_t isbob; cJSON *newjson; char *retstr; - if ( strcmp(A->offer.rel,"BTC") != 0 ) + if ( (swap->myfeetx= instantdex_feetx(myinfo,&swap->myfeetxid,ap)) != 0 ) { - printf("rel not BTC?!\n"); - return(clonestr("{\"error\":\"invalid othercoin\"}")); + jaddstr(newjson,"feetx",swap->myfeetx); + jaddbits256(newjson,"feetxid",swap->myfeetxid); + swap->state = instantdex_statefind(BTC_states,BTC_numstates,swap->isbob != 0 ? bobstate : alicestate); + return(0); } - else if ( (other= iguana_coinfind(A->offer.base)) == 0 ) + return(clonestr("{\"error\":\"couldnt create feetx\"}")); +} + +char *instantdex_sendoffer(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson) // Bob sending to network (Alice) +{ + struct iguana_info *other; struct bitcoin_swapinfo *swap; int32_t isbob; cJSON *newjson; char *retstr; + if ( strcmp(ap->offer.rel,"BTC") != 0 ) return(clonestr("{\"error\":\"invalid othercoin\"}")); - else if ( A->offer.price64 <= 0 || A->offer.basevolume64 <= 0 ) - { - printf("illegal price %.8f or volume %.8f\n",dstr(A->offer.price64),dstr(A->offer.basevolume64)); + else if ( (other= iguana_coinfind(ap->offer.base)) == 0 ) + return(clonestr("{\"error\":\"invalid othercoin\"}")); + else if ( ap->offer.price64 <= 0 || ap->offer.basevolume64 <= 0 ) return(clonestr("{\"error\":\"illegal price or volume\"}")); - } - isbob = (A->offer.myside == 1); - swap = calloc(1,sizeof(struct bitcoin_swapinfo)), swap->isbob = isbob, swap->choosei = swap->otherschoosei = -1; - A->info = swap; - if ( (retstr= instantdex_swapset(myinfo,A,argjson)) != 0 ) + isbob = (ap->offer.myside == 1); + swap = calloc(1,sizeof(struct bitcoin_swapinfo)); + swap->isbob = isbob; + swap->expiration = (uint32_t)(time(NULL) + INSTANTDEX_LOCKTIME*isbob); + swap->choosei = swap->otherschoosei = -1; + swap->depositconfirms = swap->paymentconfirms = swap->altpaymentconfirms = swap->myfeeconfirms = swap->otherfeeconfirms = -1; + ap->info = swap; + if ( (retstr= instantdex_swapset(myinfo,ap,argjson)) != 0 ) return(retstr); - A->orderid = swap->orderhash.txid; - if ( (newjson= instantdex_newjson(myinfo,swap,argjson,swap->orderhash,A,1)) == 0 ) + ap->orderid = swap->orderhash.txid; + if ( (newjson= instantdex_parseargjson(myinfo,exchange,ap,argjson,1)) == 0 ) return(clonestr("{\"error\":\"instantdex_BTCswap offer null newjson\"}")); - if ( instantdex_pubkeyargs(swap,newjson,777+2,myinfo->persistent_priv,swap->orderhash,0x02+isbob) != 777+2 ) - { - printf("error from pubkeyargs\n"); - free(swap), free(A); - return(clonestr("{\"error\":\"highly unlikely run of 02 pubkeys\"}")); - } - else + else if ( (retstr= instantdex_addfeetx(myinfo,newjson,ap,swap,"BOB_sentoffer","ALICE_sentoffer")) == 0 ) { - queue_enqueue("pendingQ",&exchange->pendingQ,&A->DL,0); - swap->state = instantdex_statefind(BTC_states,BTC_numstates,swap->isbob != 0 ? "BOB_sentoffer" : "ALICE_sentoffer"); - return(instantdex_sendcmd(myinfo,&A->offer,newjson,"BTCoffer",GENESIS_PUBKEY,INSTANTDEX_HOPS,swap->deck,sizeof(swap->deck))); + return(instantdex_sendcmd(myinfo,&ap->offer,newjson,"BTCoffer",GENESIS_PUBKEY,INSTANTDEX_HOPS,swap->deck,sizeof(swap->deck))); } + else return(retstr); } -char *instantdex_gotoffer(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,struct instantdex_msghdr *msg,cJSON *argjson,char *remoteaddr,uint64_t signerbits,uint8_t *serdata,int32_t serdatalen) // receiving side +char *instantdex_gotoffer(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,struct instantdex_msghdr *msg,cJSON *argjson,char *remoteaddr,uint64_t signerbits,uint8_t *serdata,int32_t serdatalen) // receiving side { - struct bitcoin_swapinfo *swap = 0; bits256 orderhash,traderpub; - struct iguana_info *coinbtc,*altcoin; cJSON *newjson=0; char *retstr=0; - swap = A->info; + struct bitcoin_swapinfo *swap = 0; bits256 traderpub; struct iguana_info *coinbtc,*altcoin; cJSON *newjson=0; char *retstr=0; + swap = ap->info; coinbtc = iguana_coinfind("BTC"); traderpub = jbits256(argjson,"traderpub"); if ( bits256_cmp(traderpub,myinfo->myaddr.persistent) == 0 ) @@ -714,53 +735,64 @@ char *instantdex_gotoffer(struct supernet_info *myinfo,struct exchange_info *exc printf("got my own packet\n"); return(clonestr("{\"result\":\"got my own packet\"}")); } - printf("T.%d got (%s/%s) %.8f vol %.8f %llu offerside.%d offerdir.%d swap.%p decksize.%ld/datalen.%d\n",bits256_cmp(traderpub,myinfo->myaddr.persistent),A->offer.base,A->offer.rel,dstr(A->offer.price64),dstr(A->offer.basevolume64),(long long)A->orderid,A->offer.myside,A->offer.acceptdir,A->info,sizeof(swap->deck),serdatalen); + printf("T.%d got (%s/%s) %.8f vol %.8f %llu offerside.%d offerdir.%d swap.%p decksize.%ld/datalen.%d\n",bits256_cmp(traderpub,myinfo->myaddr.persistent),ap->offer.base,ap->offer.rel,dstr(ap->offer.price64),dstr(ap->offer.basevolume64),(long long)ap->orderid,ap->offer.myside,ap->offer.acceptdir,ap->info,sizeof(swap->deck),serdatalen); if ( exchange == 0 ) return(clonestr("{\"error\":\"instantdex_BTCswap null exchange ptr\"}")); - if ( (altcoin= iguana_coinfind(A->offer.base)) == 0 || coinbtc == 0 ) - { - printf("other.%p coinbtc.%p (%s/%s)\n",altcoin,coinbtc,A->offer.base,A->offer.rel); + if ( (altcoin= iguana_coinfind(ap->offer.base)) == 0 || coinbtc == 0 ) return(clonestr("{\"error\":\"instantdex_BTCswap cant find btc or other coin info\"}")); - } - if ( strcmp(A->offer.rel,"BTC") != 0 ) + if ( strcmp(ap->offer.rel,"BTC") != 0 ) return(clonestr("{\"error\":\"instantdex_BTCswap offer non BTC rel\"}")); - if ( A->offer.expiration < (time(NULL) + INSTANTDEX_DURATION) ) + if ( ap->offer.expiration < (time(NULL) + INSTANTDEX_DURATION) ) return(clonestr("{\"error\":\"instantdex_BTCswap offer too close to expiration\"}")); - if ( A->info == 0 ) + if ( ap->info == 0 ) { - A->info = swap = calloc(1,sizeof(struct bitcoin_swapinfo)); + ap->info = swap = calloc(1,sizeof(struct bitcoin_swapinfo)); swap->choosei = swap->otherschoosei = -1; - swap->isbob = (A->offer.myside ^ 1); - if ( (retstr= instantdex_swapset(myinfo,A,argjson)) != 0 ) + swap->depositconfirms = swap->paymentconfirms = swap->altpaymentconfirms = swap->myfeeconfirms = swap->otherfeeconfirms = -1; + swap->isbob = (ap->offer.myside ^ 1); + if ( (retstr= instantdex_swapset(myinfo,ap,argjson)) != 0 ) return(retstr); if ( instantdex_pubkeyargs(swap,newjson,2+777,myinfo->persistent_priv,swap->orderhash,0x02 + swap->isbob) != 2+777 ) - { - printf("error generating pubkeyargs\n"); - return(0); - } - char str[65]; printf("GOT OFFER! %p (%s/%s) other.%s myside.%d\n",A->info,A->offer.base,A->offer.rel,bits256_str(str,traderpub),swap->isbob); - if ( (newjson= instantdex_newjson(myinfo,swap,argjson,orderhash,A,1)) == 0 ) + return(clonestr("{\"error\":\"instantdex_BTCswap error creating pubkeyargs\"}")); + char str[65]; printf("GOT OFFER! %p (%s/%s) other.%s myside.%d\n",ap->info,ap->offer.base,ap->offer.rel,bits256_str(str,traderpub),swap->isbob); + if ( (newjson= instantdex_parseargjson(myinfo,exchange,ap,argjson,1)) == 0 ) return(clonestr("{\"error\":\"instantdex_BTCswap offer null newjson\"}")); - else + else if ( (retstr= instantdex_addfeetx(myinfo,newjson,ap,swap,"BOB_gotoffer","ALICE_gotoffer")) == 0 ) { - // verify feetx - instantdex_pendingnotice(myinfo,exchange,A,A->offer.basevolume64); + //instantdex_pendingnotice(myinfo,exchange,A,ap->offer.basevolume64); if ( (retstr= instantdex_choosei(swap,newjson,argjson,serdata,serdatalen)) != 0 ) return(retstr); else { - // generate feetx to send - swap->state = instantdex_statefind(BTC_states,BTC_numstates,swap->isbob != 0 ? "BOB_gotoffer" : "ALICE_gotoffer"); - return(instantdex_sendcmd(myinfo,&A->offer,newjson,"BTCchose",traderpub,INSTANTDEX_HOPS,swap->deck,sizeof(swap->deck))); + return(instantdex_sendcmd(myinfo,&ap->offer,newjson,"BTCchose",traderpub,INSTANTDEX_HOPS,swap->deck,sizeof(swap->deck))); } - } + } else return(retstr); } else return(clonestr("{\"error\":\"couldnt allocate swap info\"}")); } +char *instantdex_selectqueue(struct exchange_info *exchange,struct instantdex_accept *ap,char *retstr) +{ + cJSON *retjson; int32_t flag = 0; + if ( (retjson= cJSON_Parse(retstr)) != 0 ) + { + if ( jobj(retjson,"error") != 0 ) + { + printf("requeue gotoffer error.(%s)\n",jprint(retjson,0)); + instantdex_swapfree(0,ap->info); + ap->info = 0; + flag++; + queue_enqueue("acceptableQ",&exchange->acceptableQ,&ap->DL,0); + } + free_json(retjson); + } + if ( flag == 0 ) + queue_enqueue("statemachineQ",&exchange->statemachineQ,&ap->DL,0); + return(retstr); +} + char *instantdex_parse(struct supernet_info *myinfo,struct instantdex_msghdr *msg,cJSON *argjson,char *remoteaddr,uint64_t signerbits,struct instantdex_offer *offer,bits256 orderhash,uint8_t *serdata,int32_t serdatalen) { - char cmdstr[16]; struct exchange_info *exchange; double minperc; - struct instantdex_accept A,*ap = 0; bits256 traderpub; + char cmdstr[16],*retstr; struct exchange_info *exchange; double minperc; struct instantdex_accept A,*ap = 0; bits256 traderpub; cJSON *newjson; if ( BTC_states == 0 ) BTC_states = BTC_initFSM(&BTC_numstates); exchange = exchanges777_find("bitcoin"); @@ -782,7 +814,8 @@ char *instantdex_parse(struct supernet_info *myinfo,struct instantdex_msghdr *ms minperc = INSTANTDEX_MINPERC; if ( (ap= instantdex_acceptable(myinfo,exchange,&A,acct777_nxt64bits(traderpub),minperc)) != 0 ) { - return(instantdex_gotoffer(myinfo,exchange,ap,msg,argjson,remoteaddr,signerbits,serdata,serdatalen)); + if ( (retstr= instantdex_gotoffer(myinfo,exchange,ap,msg,argjson,remoteaddr,signerbits,serdata,serdatalen)) != 0 ) // adds to statemachine if no error + return(instantdex_selectqueue(exchange,ap,retstr)); } else { @@ -796,14 +829,15 @@ char *instantdex_parse(struct supernet_info *myinfo,struct instantdex_msghdr *ms } else return(clonestr("{\"result\":\"order was already in orderbook\"}")); } } - else if ( (ap= instantdex_pendingfind(myinfo,exchange,A.orderid)) != 0 ) + else if ( (ap= instantdex_statemachinefind(myinfo,exchange,A.orderid)) != 0 ) { if ( ap->info == 0 ) { printf("null swap for orderid.%llu\n",(long long)ap->orderid); return(clonestr("{\"error\":\"no swap for orderid\"}")); } - return(instantdex_statemachine(myinfo,exchange,ap,cmdstr,argjson,serdata,serdatalen)); + newjson = instantdex_parseargjson(myinfo,exchange,ap,argjson,0); + return(instantdex_statemachine(myinfo,exchange,ap,cmdstr,argjson,newjson,serdata,serdatalen)); } else { @@ -917,8 +951,27 @@ char *instantdex_queueaccept(struct supernet_info *myinfo,struct instantdex_acce void instantdex_update(struct supernet_info *myinfo) { - struct instantdex_msghdr *pm; struct category_msg *m; int32_t iter; bits256 instantdexhash; char *str,remote[64]; + struct instantdex_msghdr *pm; struct category_msg *m; bits256 instantdexhash; char *str,remote[64]; queue_t *Q; struct queueitem *item; instantdexhash = calc_categoryhashes(0,"InstantDEX",0); + //char str[65]; printf("getmsg.(%s) %llx\n",bits256_str(str,categoryhash),(long long)subhash.txid); + if ( (Q= category_Q(instantdexhash,myinfo->myaddr.persistent)) != 0 && queue_size(Q) > 0 && (item= Q->list) != 0 ) + { + m = (void *)item; + if ( m->remoteipbits == 0 && (m= queue_dequeue(Q,0)) ) + { + if ( (void *)m == (void *)item ) + { + pm = (struct instantdex_msghdr *)m->msg; + if ( m->remoteipbits != 0 ) + expand_ipbits(remote,m->remoteipbits); + else remote[0] = 0; + if ( (str= InstantDEX_hexmsg(myinfo,pm,m->len,remote)) != 0 ) + free(str); + } else printf("instantdex_update: unexpected m.%p changed item.%p\n",m,item); + free(m); + } + } +/* for (iter=0; iter<2; iter++) { while ( (m= category_gethexmsg(myinfo,instantdexhash,iter == 0 ? GENESIS_PUBKEY : myinfo->myaddr.persistent)) != 0 ) @@ -932,7 +985,7 @@ void instantdex_update(struct supernet_info *myinfo) free(str); free(m); } - } + }*/ } #include "../includes/iguana_apidefs.h" diff --git a/iguana/main.c b/iguana/main.c index 201c31bf4..1d8f749f1 100644 --- a/iguana/main.c +++ b/iguana/main.c @@ -1035,7 +1035,7 @@ void iguana_main(void *arg) mycalloc(0,0,0); myinfo = SuperNET_MYINFO(0); - FILE *fp; int32_t iter; + FILE *fp; int32_t iter; void ztest(); ztest(); strcpy(myinfo->NXTAPIURL,"http://127.0.0.1:7876/nxt"); for (iter=0; iter<2; iter++) { diff --git a/iguana/swaps/iguana_BTCswap.c b/iguana/swaps/iguana_BTCswap.c index 0e42f487f..979d90fbb 100755 --- a/iguana/swaps/iguana_BTCswap.c +++ b/iguana/swaps/iguana_BTCswap.c @@ -15,6 +15,7 @@ #include "../exchanges/bitcoin.h" /* https://bitcointalk.org/index.php?topic=1340621.msg13828271#msg13828271 + https://bitcointalk.org/index.php?topic=1364951 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. @@ -23,37 +24,35 @@ Tier Nolan's approach is followed with the following changes: 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) +int64_t instantdex_BTCsatoshis(int64_t price,int64_t volume) { - 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); + if ( volume > price ) + return(price * dstr(volume)); + else return(dstr(price) * volume); +} + +int64_t instantdex_insurance(struct iguana_info *coin,int64_t amount) +{ + return(amount * INSTANTDEX_INSURANCERATE + coin->chain->txfee); // insurance prevents attack } /* - Alice fee: - OP_IF - OP_CLTV OP_DROP INSTANTDEX OP_CHECKSIG - OP_ELSE - OP_CLTV OP_DROP OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG - OP_ENDIF - - Bob deposit: - instantdex_bobscript(script,0,(uint32_t)(time(NULL)+INSTANTDEX_LOCKTIME*2),pubA0,privBn,pubB0); - OP_IF - OP_CLTV OP_DROP OP_CHECKSIG - OP_ELSE - OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG - OP_ENDIF +both fees are standard payments: OP_DUP OP_HASH160 FEE_RMD160 OP_EQUALVERIFY OP_CHECKSIG + +Alice altpayment: OP_2 OP_2 OP_CHECKMULTISIG + + Bob deposit: if ( (swap->deposit= instantdex_bobtx(myinfo,coinbtc,&swap->deposittxid,swap->otherpubs[0],swap->mypubs[0],swap->privkeys[swap->choosei],reftime,swap->satoshis[1],1)) != 0 ) +OP_IF + OP_CLTV OP_DROP OP_CHECKSIG +OP_ELSE + OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG +OP_ENDIF -Bobpays: - instantdex_bobscript(script,0,(uint32_t)(time(NULL)+INSTANTDEX_LOCKTIME),pubB1,privAm,pubA0); + Bob paytx: if ( (swap->payment= instantdex_bobtx(myinfo,coinbtc,&swap->deposittxid,swap->mypubs[1],swap->otherpubs[0],swap->privkeys[swap->otherschoosei],reftime,swap->satoshis[1],0)) != 0 ) OP_IF - OP_CLTV OP_DROP OP_CHECKSIG + OP_CLTV OP_DROP OP_CHECKSIG OP_ELSE - OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG + OP_HASH160 OP_EQUALVERIFY OP_CHECKSIG OP_ENDIF */ @@ -74,7 +73,6 @@ int32_t instantdex_bobscript(uint8_t *script,int32_t n,int32_t *secretstartp,uin return(n); } -// OP_2 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; @@ -87,20 +85,84 @@ int32_t instantdex_alicescript(uint8_t *script,int32_t n,char *msigaddr,uint8_t 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) +int32_t instantdex_outputinsurance(struct iguana_info *coin,cJSON *txobj,int64_t insurance,uint64_t orderid) +{ + uint8_t rmd160[20],script[128]; int32_t n = 0; + decode_hex(rmd160,sizeof(rmd160),(orderid % 10) == 0 ? TIERNOLAN_RMD160 : INSTANTDEX_RMD160); + script[n++] = sizeof(uint64_t); + n += iguana_rwnum(1,&script[n],sizeof(orderid),&orderid); + script[n++] = OP_DROP; + n = bitcoin_standardspend(script,n,rmd160); + bitcoin_addoutput(coin,txobj,script,n,insurance); + return(n); +} + +char *instantdex_feetx(struct supernet_info *myinfo,bits256 *txidp,struct instantdex_accept *A) +{ + int32_t n; char *feetx = 0; struct iguana_info *coinbtc; cJSON *txobj; struct bitcoin_spend *spend; int64_t insurance; + if ( (coinbtc= iguana_coinfind("BTC")) != 0 ) + { + insurance = instantdex_insurance(coinbtc,instantdex_BTCsatoshis(A->offer.price64,A->offer.basevolume64)); + if ( (spend= iguana_spendset(myinfo,coinbtc,insurance,coinbtc->chain->txfee)) != 0 ) + { + txobj = bitcoin_createtx(coinbtc,0); + n = instantdex_outputinsurance(coinbtc,txobj,insurance,A->orderid); + txobj = iguana_signtx(coinbtc,txidp,&feetx,spend,txobj); + if ( feetx != 0 ) + printf("%s feetx.%s\n",A->offer.myside != 0 ? "BOB" : "ALICE",feetx); + else printf("error signing %s feetx numinputs.%d\n",A->offer.myside != 0 ? "BOB" : "ALICE",spend->numinputs); + free(spend); + } + } + return(feetx); +} + +int32_t instantdex_feetxverify(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; int64_t insurance; + struct iguana_msgtx msgtx; uint8_t script[512]; + if ( swap->otherfeetx != 0 ) + { + if ( (txobj= bitcoin_hex2json(coin,&txid,&msgtx,swap->otherfeetx)) != 0 ) + { + insurance = instantdex_insurance(coin,instantdex_BTCsatoshis(A->offer.price64,A->offer.basevolume64)); + n = instantdex_outputinsurance(coin,txobj,insurance,A->orderid); + if ( n == msgtx.vouts[0].pk_scriptlen ) + { + if ( memcmp(script,msgtx.vouts[0].pk_script,n) == 0 ) + { + printf("feetx script verified\n"); + } + else + { + for (i=0; ichain->txfee); // txfee prevents dust attack + insurance = instantdex_insurance(coin,amount); if ( (spend= iguana_spendset(myinfo,coin,amount + insurance,coin->chain->txfee)) != 0 ) { - calc_rmd160_sha256(secret,privBn.bytes,sizeof(privBn)); - n = instantdex_bobscript(script,0,&secretstart,locktime,pubA0,secret,pubB0); + calc_rmd160_sha256(secret,priv.bytes,sizeof(priv)); + n = instantdex_bobscript(script,0,&secretstart,locktime,pub1,secret,pub2); 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); @@ -111,28 +173,15 @@ char *instantdex_bobtx(struct supernet_info *myinfo,struct iguana_info *coin,bit return(signedtx); } -uint64_t instantdex_relsatoshis(uint64_t price,uint64_t volume) -{ - if ( volume > price ) - return(price * dstr(volume)); - else return(dstr(price) * volume); -} - 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 ) + struct iguana_msgtx msgtx; uint8_t script[512],rmd160[20]; int64_t relsatoshis,amount,insurance = 0; + if ( coin != 0 && jstr(argjson,depositflag != 0 ? "deposit" : "payment") != 0 ) { - relsatoshis = instantdex_relsatoshis(A->offer.price64,A->offer.basevolume64); - insurance = (relsatoshis * INSTANTDEX_INSURANCERATE + coin->chain->txfee); // txfee prevents dust attack + relsatoshis = instantdex_BTCsatoshis(A->offer.price64,A->offer.basevolume64); if ( depositflag != 0 ) - { - swap->deposit = clonestr(jstr(argjson,"deposit")); - swap->dtxid = jbits256(argjson,"dtxid"); - swap->pubBn = jbits256(argjson,"pubBn"); - insurance *= 100; - } + insurance = 100 * (relsatoshis * INSTANTDEX_INSURANCERATE + coin->chain->txfee); amount = relsatoshis + insurance; if ( (txobj= bitcoin_hex2json(coin,&txid,&msgtx,swap->deposit)) != 0 ) { @@ -149,15 +198,7 @@ int32_t instantdex_paymentverify(struct supernet_info *myinfo,struct iguana_info 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; - } + 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 @@ -176,36 +217,12 @@ int32_t instantdex_paymentverify(struct supernet_info *myinfo,struct iguana_info 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); @@ -231,10 +248,54 @@ int32_t instantdex_altpaymentverify(struct supernet_info *myinfo,struct iguana_i return(retval); } -void instantdex_pendingnotice(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,uint64_t basevolume64) +char *instantdex_alicetx(struct supernet_info *myinfo,struct iguana_info *altcoin,char *msigaddr,bits256 *txidp,bits256 pubAm,bits256 pubBn,int64_t amount) { -// printf("need to start monitoring thread\n"); - ap->pendingvolume64 -= basevolume64; + cJSON *txobj; int32_t n; char *signedtx = 0; uint8_t script[1024]; struct bitcoin_spend *spend; + if ( altcoin != 0 && (spend= iguana_spendset(myinfo,altcoin,amount,altcoin->chain->txfee)) != 0 ) + { + txobj = bitcoin_createtx(altcoin,0); + 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); +} + +cJSON *BOB_reclaimfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + *serdatap = 0, *serdatalenp = 0; struct bitcoin_swapinfo *swap = ap->info; + printf("reclaim deposit.(%s) to %s\n",swap->deposit,myinfo->myaddr.BTC); + // reclaim deposit + return(newjson); +} + +cJSON *BOB_claimaltfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + *serdatap = 0, *serdatalenp = 0; struct bitcoin_swapinfo *swap = ap->info; char altcoinaddr[64]; + printf("spend altpayment.(%s) -> %s\n",swap->altpayment,altcoinaddr); + // spend altpayment + return(newjson); +} + +cJSON *ALICE_reclaimfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + *serdatap = 0, *serdatalenp = 0; struct bitcoin_swapinfo *swap = ap->info; char altcoinaddr[64]; + // reclaim altpayment + printf("reclaim altpayment.(%s) -> %s\n",swap->altpayment,altcoinaddr); + return(newjson); +} + +cJSON *ALICE_claimbtcfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + *serdatap = 0, *serdatalenp = 0; struct bitcoin_swapinfo *swap = ap->info; + printf("spend BTC payment.(%s) -> %s\n",swap->payment,myinfo->myaddr.BTC); + // spend BTC + return(newjson); } bits256 instantdex_derivekeypair(bits256 *newprivp,uint8_t pubkey[33],bits256 privkey,bits256 orderhash) @@ -325,12 +386,12 @@ void instantdex_getpubs(struct bitcoin_swapinfo *swap,cJSON *argjson,cJSON *newj } } -void instantdex_privkeysextract(struct supernet_info *myinfo,struct bitcoin_swapinfo *swap,uint8_t *serdata,int32_t serdatalen) +void instantdex_privkeyextract(struct supernet_info *myinfo,struct bitcoin_swapinfo *swap,uint8_t *serdata,int32_t serdatalen) { 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) ) { + printf("got instantdex_privkeyextract serdatalen.%d choosei.%d cutverified.%d\n",serdatalen,swap->choosei,swap->cutverified); for (i=wrongfirstbyte=errs=0; iprivkeys)/sizeof(*swap->privkeys); i++) { len += iguana_rwbignum(0,&serdata[len],sizeof(bits256),otherpriv.bytes); @@ -367,149 +428,385 @@ void instantdex_privkeysextract(struct supernet_info *myinfo,struct bitcoin_swap } } -cJSON *instantdex_newjson(struct supernet_info *myinfo,struct bitcoin_swapinfo *swap,cJSON *argjson,bits256 hash,struct instantdex_accept *A,int32_t flag777) +void instantdex_swaptxupdate(char **ptrp,bits256 *txidp,cJSON *argjson,char *txname,char *txidfield) +{ + char *str; + if ( (str= jstr(argjson,txname)) != 0 ) + { + if ( *ptrp != 0 ) + { + printf("got replacement %s? (%s)\n",txname,str); + free(*ptrp); + } + *txidp = jbits256(argjson,txidfield); + *ptrp = clonestr(str); + } +} + +void instantdex_swapbits256update(bits256 *txidp,cJSON *argjson,char *fieldname) +{ + bits256 txid; char str[65]; + txid = jbits256(argjson,fieldname); + if ( bits256_nonz(txid) > 0 ) + { + if ( bits256_nonz(*txidp) > 0 ) + printf("swapbits256: %s sent again\n",bits256_str(str,*txidp)); + *txidp = txid; + } +} + +cJSON *instantdex_parseargjson(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,int32_t deckflag) { - cJSON *newjson; + cJSON *newjson; struct bitcoin_swapinfo *swap; newjson = cJSON_CreateObject(); - //printf("acceptsend.(%s)\n",jprint(newjson,0)); - if ( swap->otherschoosei < 0 && jobj(argjson,"mychoosei") != 0 ) + if ( (swap= ap->info) == 0 ) + jaddstr(newjson,"error","missing swap info"); + else { - //printf("otherschoosei.%d\n",swap->otherschoosei); - if ( (swap->otherschoosei= juint(argjson,"mychoosei")) >= sizeof(swap->otherscut)/sizeof(*swap->otherscut) ) - swap->otherschoosei = -1; + if ( swap->isbob != 0 ) + { + instantdex_swapbits256update(&swap->otherpubs[0],argjson,"pubA0"); + instantdex_swapbits256update(&swap->otherpubs[1],argjson,"pubA1"); + instantdex_swapbits256update(&swap->pubAm,argjson,"pubAm"); + instantdex_swapbits256update(&swap->privAm,argjson,"privAm"); + instantdex_swaptxupdate(&swap->altpayment,&swap->altpaymenttxid,argjson,"altpayment","altpaymenttxid"); + } + else + { + instantdex_swapbits256update(&swap->otherpubs[0],argjson,"pubB0"); + instantdex_swapbits256update(&swap->otherpubs[1],argjson,"pubB1"); + instantdex_swapbits256update(&swap->pubBn,argjson,"pubBn"); + instantdex_swapbits256update(&swap->privBn,argjson,"privBn"); + instantdex_swaptxupdate(&swap->deposit,&swap->deposittxid,argjson,"deposit","deposittxid"); + instantdex_swaptxupdate(&swap->payment,&swap->paymenttxid,argjson,"payment","paymenttxid"); + } + instantdex_swaptxupdate(&swap->otherfeetx,&swap->otherfeetxid,argjson,"feetx","feetxid"); + if ( swap->otherschoosei < 0 && jobj(argjson,"mychoosei") != 0 ) + { + //printf("otherschoosei.%d\n",swap->otherschoosei); + if ( (swap->otherschoosei= juint(argjson,"mychoosei")) >= sizeof(swap->otherscut)/sizeof(*swap->otherscut) ) + swap->otherschoosei = -1; + } + if ( juint(argjson,"verified") != 0 ) + swap->otherverifiedcut = 1; + jaddnum(newjson,"verified",swap->otherverifiedcut); + if ( instantdex_pubkeyargs(swap,newjson,2 + deckflag*777,myinfo->persistent_priv,swap->orderhash,0x02+swap->isbob) == 2 ) + instantdex_getpubs(swap,argjson,newjson); + else printf("ERROR: couldnt generate pubkeys\n"); } - if ( juint(argjson,"verified") != 0 ) - swap->otherverifiedcut = 1; - jaddnum(newjson,"verified",swap->otherverifiedcut); - 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) +double iguana_numconfs(struct iguana_info *coin,bits256 txid,int32_t height) +{ + if ( coin->longestchain >= height ) + return((double)coin->longestchain - height); + else return(0.); // 0.5 if zeroconfs +} + +char *BTC_txconfirmed(struct supernet_info *myinfo,struct iguana_info *coin,struct instantdex_accept *ap,cJSON *newjson,bits256 txid,double *numconfirmsp,char *virtualevent,double requiredconfs) { - cJSON *newjson=0; uint8_t *serdata = *serdatap; int32_t serdatalen = *serdatalenp; + struct iguana_txid *tx,T; struct bitcoin_swapinfo *swap; int32_t height; char *retstr; double confs; + swap = ap->info; + *numconfirmsp = -1.; + if ( coin != 0 && *numconfirmsp < 0 ) + { + if ( (tx= iguana_txidfind(coin,&height,&T,txid)) != 0 && (confs= iguana_numconfs(coin,txid,height)) >= requiredconfs ) + { + *numconfirmsp = confs; + if ( (retstr= instantdex_sendcmd(myinfo,&ap->offer,newjson,virtualevent,myinfo->myaddr.persistent,0,0,0)) != 0 ) + return(retstr); + } + } + return(0); +} + +cJSON *BTC_waitdeckCfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + *serdatap = 0, *serdatalenp = 0; struct bitcoin_swapinfo *swap = ap->info; + strcmp(swap->expectedcmdstr,"BTCdeckC"); + if ( instantdex_feetxverify(myinfo,iguana_coinfind("BTC"),swap,ap,argjson) != 0 ) + return(cJSON_Parse("{\"error\":\"feetx didnt verify\"}")); + return(newjson); +} + +cJSON *BTC_waitprivCfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + struct bitcoin_swapinfo *swap = ap->info; + strcmp(swap->expectedcmdstr,"BTCprivC"); + instantdex_privkeyextract(myinfo,swap,*serdatap,*serdatalenp); + if ( instantdex_feetxverify(myinfo,iguana_coinfind("BTC"),swap,ap,argjson) != 0 ) + return(cJSON_Parse("{\"error\":\"feetx didnt verify\"}")); *serdatap = 0, *serdatalenp = 0; - uint32_t reftime; - reftime = (uint32_t)(A->offer.expiration - INSTANTDEX_LOCKTIME*2); - if ( serdata != 0 && serdatalen > 0 ) + return(newjson); +} + +cJSON *BOB_waitBTCalttxfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + *serdatap = 0, *serdatalenp = 0; struct bitcoin_swapinfo *swap = ap->info; + strcmp(swap->expectedcmdstr,"BTCalttx"); + if ( instantdex_altpaymentverify(myinfo,iguana_coinfind(ap->offer.base),swap,ap,argjson) != 0 ) + return(cJSON_Parse("{\"error\":\"altpayment didnt verify\"}")); + return(newjson); +} + +cJSON *ALICE_waitBTCpaytxfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + *serdatap = 0, *serdatalenp = 0; struct bitcoin_swapinfo *swap = ap->info; + strcmp(swap->expectedcmdstr,"BTCpaytx"); + return(newjson); +} + +cJSON *BOB_waitfeefunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + char *retstr; struct bitcoin_swapinfo *swap = ap->info; struct iguana_info *coinbtc; uint32_t reftime; + coinbtc = iguana_coinfind("BTC"); + *serdatap = 0, *serdatalenp = 0; + reftime = (uint32_t)(ap->offer.expiration - INSTANTDEX_LOCKTIME*2); + if ( coinbtc != 0 && swap->deposit == 0 && (retstr= BTC_txconfirmed(myinfo,coinbtc,ap,newjson,swap->otherfeetxid,&swap->otherfeeconfirms,"feefound",0)) != 0 ) { - serdata[serdatalen-1] = 0; + jaddstr(newjson,"feefound",retstr); + if ( (swap->deposit= instantdex_bobtx(myinfo,coinbtc,&swap->deposittxid,swap->otherpubs[0],swap->mypubs[0],swap->privkeys[swap->choosei],reftime,swap->satoshis[1],1)) != 0 ) + { + // broadcast deposit + jaddstr(newjson,"deposit",swap->deposit); + jaddbits256(newjson,"deposittxid",swap->deposittxid); + } } return(newjson); } -cJSON *ALICE_processfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,cJSON *argjson,uint8_t **serdatap,int32_t *serdatalenp) +cJSON *BOB_waitprivMfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + char *retstr; struct bitcoin_swapinfo *swap = ap->info; + strcmp(swap->expectedcmdstr,"BTCprivM"); + if ( swap->payment != 0 && (retstr= BTC_txconfirmed(myinfo,iguana_coinfind("BTC"),ap,newjson,swap->paymenttxid,&swap->paymentconfirms,"btcfound",0)) != 0 ) + jaddstr(newjson,"btcfound",retstr); + printf("search for payment spend in blockchain\n"); + *serdatap = 0, *serdatalenp = 0; + return(newjson); +} + +cJSON *BOB_waitaltconfirmfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) { - cJSON *newjson=0; uint8_t *serdata = *serdatap; int32_t serdatalen = *serdatalenp; + char *retstr; struct bitcoin_swapinfo *swap = ap->info; struct iguana_info *altcoin; uint32_t reftime; + altcoin = iguana_coinfind(ap->offer.base); *serdatap = 0, *serdatalenp = 0; - if ( serdata != 0 && serdatalen > 0 ) + reftime = (uint32_t)(ap->offer.expiration - INSTANTDEX_LOCKTIME*2); + if ( altcoin != 0 && swap->altpayment != 0 && (retstr= BTC_txconfirmed(myinfo,altcoin,ap,newjson,swap->altpaymenttxid,&swap->altpaymentconfirms,"altfound",altcoin->chain->minconfirms)) != 0 ) { - serdata[serdatalen-1] = 0; + jaddstr(newjson,"altfound",retstr); + if ( (swap->payment= instantdex_bobtx(myinfo,altcoin,&swap->deposittxid,swap->mypubs[1],swap->otherpubs[0],swap->privkeys[swap->otherschoosei],reftime,swap->satoshis[1],0)) != 0 ) + { + // broadcast payment + jaddstr(newjson,"payment",swap->payment); + jaddbits256(newjson,"paymenttxid",swap->paymenttxid); + } } return(newjson); } +cJSON *BTC_waitprivsfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + *serdatap = 0, *serdatalenp = 0; struct bitcoin_swapinfo *swap = ap->info; + strcmp(swap->expectedcmdstr,"BTCprivs"); + instantdex_privkeyextract(myinfo,swap,*serdatap,*serdatalenp); + return(newjson); +} + +cJSON *ALICE_waitfeefunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + char *retstr; struct iguana_info *coinbtc; struct bitcoin_swapinfo *swap = ap->info; + coinbtc = iguana_coinfind("BTC"); + *serdatap = 0, *serdatalenp = 0; + if ( swap->otherfeetx != 0 && (retstr= BTC_txconfirmed(myinfo,coinbtc,ap,newjson,swap->otherfeetxid,&swap->otherfeeconfirms,"feefound",0)) != 0 ) + jaddstr(newjson,"feefound",retstr); + return(newjson); +} + +cJSON *ALICE_waitfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + char *retstr; struct iguana_info *coinbtc; struct bitcoin_swapinfo *swap = ap->info; + coinbtc = iguana_coinfind("BTC"); + *serdatap = 0, *serdatalenp = 0; + if ( swap->deposit != 0 && (retstr= BTC_txconfirmed(myinfo,coinbtc,ap,newjson,swap->deposittxid,&swap->depositconfirms,"depfound",0.5)) != 0 ) + jaddstr(newjson,"depfound",retstr); + return(newjson); +} + +cJSON *ALICE_waitsendfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + char *retstr; struct iguana_info *coinbtc,*altcoin; struct bitcoin_swapinfo *swap = ap->info; + coinbtc = iguana_coinfind("BTC"); + altcoin = iguana_coinfind(ap->offer.rel); + *serdatap = 0, *serdatalenp = 0; + if ( swap->deposit != 0 && (retstr= BTC_txconfirmed(myinfo,coinbtc,ap,newjson,swap->deposittxid,&swap->depositconfirms,"depfound",0.5)) != 0 ) + { + jaddstr(newjson,"depfound",retstr); + if ( instantdex_paymentverify(myinfo,iguana_coinfind("BTC"),swap,ap,argjson,1) != 0 ) + return(cJSON_Parse("{\"error\":\"deposit didnt verify\"}")); + if ( (swap->altpayment= instantdex_alicetx(myinfo,altcoin,swap->altmsigaddr,&swap->altpaymenttxid,swap->pubAm,swap->pubBn,swap->satoshis[0])) != 0 ) + { + // broadcast altpayment + jaddstr(newjson,"altpayment",swap->altpayment); + jaddbits256(newjson,"altpaymenttxid",swap->altpaymenttxid); + } else return(cJSON_Parse("{\"error\":\"couldnt create altpayment\"}")); + } + return(newjson); +} + +cJSON *ALICE_waitpayconf_or_bobreclaimfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + char *retstr; double btcconfirms; struct iguana_info *coinbtc; struct bitcoin_swapinfo *swap = ap->info; + coinbtc = iguana_coinfind("BTC"); + *serdatap = 0, *serdatalenp = 0; + if ( swap->satoshis[1] < SATOSHIDEN/10 ) + btcconfirms = 0; + else btcconfirms = 1. + sqrt((double)swap->satoshis[1] / SATOSHIDEN); + if ( swap->payment != 0 && (retstr= BTC_txconfirmed(myinfo,coinbtc,ap,newjson,swap->paymenttxid,&swap->paymentconfirms,"payfound",btcconfirms)) != 0 ) + jaddstr(newjson,"payfound",retstr); + // if bobreclaimed is there, then reclaim altpayment + printf("search for Bob's reclaim in blockchain\n"); + return(newjson); +} + +cJSON *BTC_cleanupfunc(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,cJSON *argjson,cJSON *newjson,uint8_t **serdatap,int32_t *serdatalenp) +{ + *serdatap = 0, *serdatalenp = 0; + jaddstr(newjson,"error","need to cleanup"); + ap->dead = (uint32_t)time(NULL); + 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, types: , osit, payment, is altcoin claim // "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 // states instantdex_statecreate(s,n,,handlerfunc,errorhandler,, - // a given state has a couple of handlers and custom events, with timeouts and errors invoking a bypass 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); - s = instantdex_statecreate(s,n,"BOB_gotoffer",BOB_processfunc,0,"BTC_cleanup",0); - - s = instantdex_statecreate(s,n,"ALICE_sentoffer",ALICE_processfunc,0,"BTC_cleanup",0); - 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); + // a given state has a couple of handlers and custom events, with timeouts and errors invoking a bypass + s = instantdex_statecreate(s,n,"BTC_cleanup",BTC_cleanupfunc,0,0,0); // from states without any commits - 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); - - s = instantdex_statecreate(s,n,"ALICE_claimedbtc",BOB_processfunc,0,0,0); - s = instantdex_statecreate(s,n,"BOB_claimedalt",BOB_processfunc,0,0,0); + s = instantdex_statecreate(s,n,"BOB_reclaim",BOB_reclaimfunc,0,0,0); // Bob's gets his deposit back + instantdex_addevent(s,*n,"BOB_reclaim","brcfound","poll","BTC_cleanup"); + instantdex_addevent(s,*n,"BOB_reclaim","poll","poll","BOB_reclaim"); - // events instantdex_addevent(s,*n,,,,) - instantdex_addevent(s,*n,"BOB_sentoffer","BTCchose","BTCprivs","BOB_sentprivs"); - instantdex_addevent(s,*n,"BOB_sentprivs","feefound","BTCdeptx","BOB_sentdeposit"); - 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"); + s = instantdex_statecreate(s,n,"ALICE_reclaim",ALICE_reclaimfunc,0,0,0); // Alice retrieves alt payment + instantdex_addevent(s,*n,"ALICE_reclaim","arcfound","poll","BTC_cleanup"); + instantdex_addevent(s,*n,"ALICE_reclaim","poll","poll","ALICE_reclaim"); + + s = instantdex_statecreate(s,n,"ALICE_claimedbtc",ALICE_claimbtcfunc,0,0,0); // mainstream cases + instantdex_addevent(s,*n,"ALICE_claimedbtc","aclfound","poll","BTC_cleanup"); + instantdex_addevent(s,*n,"ALICE_claimedbtc","poll","poll","ALICE_claimedbtc"); - // alice needs to wait for various items - instantdex_addevent(s,*n,"ALICE_sentprivs","BTCdeptx",0,"ALICE_wait3"); + s = instantdex_statecreate(s,n,"BOB_claimedalt",BOB_claimaltfunc,0,0,0); + instantdex_addevent(s,*n,"BOB_claimedalt","bclfound","poll","BTC_cleanup"); + instantdex_addevent(s,*n,"BOB_claimedalt","poll","poll","BOB_claimedalt"); + + // need to create states before they can be referred to, that way a one pass FSM compile is possible + s = instantdex_statecreate(s,n,"BOB_sentprivs",BTC_waitprivsfunc,0,"BTC_cleanup",0); + s = instantdex_statecreate(s,n,"BOB_waitfee",BOB_waitfeefunc,0,"BTC_cleanup",0); + s = instantdex_statecreate(s,n,"BOB_sentdeposit",BOB_waitBTCalttxfunc,0,"BOB_reclaim",0); + s = instantdex_statecreate(s,n,"BOB_altconfirm",BOB_waitaltconfirmfunc,0,"BTC_claimdeposit",0); + s = instantdex_statecreate(s,n,"BOB_sentpayment",BOB_waitprivMfunc,0,"BTC_claimdeposit",0); + s = instantdex_statecreate(s,n,"ALICE_sentprivs",BTC_waitprivsfunc,0,"BTC_cleanup",0); + s = instantdex_statecreate(s,n,"ALICE_waitfee_deposit",ALICE_waitfunc,0,"BTC_cleanup",0); + s = instantdex_statecreate(s,n,"Alice_waitfee",ALICE_waitsendfunc,0,"BTC_cleanup",0); + s = instantdex_statecreate(s,n,"ALICE_waitdeposit",ALICE_waitsendfunc,0,"BTC_cleanup",0); + s = instantdex_statecreate(s,n,"ALICE_sentalt",ALICE_waitBTCpaytxfunc,0,"BTC_claimdeposit",0); + s = instantdex_statecreate(s,n,"ALICE_waitconfirms",ALICE_waitpayconf_or_bobreclaimfunc,0,"BTC_claimdeposit",0); - // 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"); + // events instantdex_addevent(s,*n,,,,) + if ( 0 ) // following are implicit states and events handled externally to setup datastructures + { + //s = instantdex_statecreate(s,n,"BOB_idle",BTC_idlefunc,0,0,0); + //s = instantdex_statecreate(s,n,"ALICE_idle",BTC_idlefunc,0,0,0); + instantdex_addevent(s,*n,"BOB_idle","usrorder","BTCoffer","BOB_sentoffer"); // send deck + instantdex_addevent(s,*n,"ALICE_idle","usrorder","BTCoffer","ALICE_sentoffer"); + instantdex_addevent(s,*n,"BOB_idle","BTCoffer","BTCdeckC","BOB_gotoffer"); // send deck + Chose + instantdex_addevent(s,*n,"ALICE_idle","BTCoffer","BTCdeckC","ALICE_gotoffer"); + } + // after offer is sent, wait for other side to choose and sent their deck, then send privs + s = instantdex_statecreate(s,n,"BOB_sentoffer",BTC_waitdeckCfunc,0,"BTC_cleanup",0); + s = instantdex_statecreate(s,n,"ALICE_sentoffer",BTC_waitdeckCfunc,0,"BTC_cleanup",0); + instantdex_addevent(s,*n,"BOB_sentoffer","BTCdeckC","BTCprivC","BOB_sentprivs"); // send privs + Chose + instantdex_addevent(s,*n,"ALICE_sentoffer","BTCdeckC","BTCprivC","ALICE_sentprivs"); - instantdex_addevent(s,*n,"ALICE_waitfee_privs","feefound",0,"ALICE_waitprivs"); - instantdex_addevent(s,*n,"ALICE_waitfee_privs","BTCprivs",0,"ALICE_waitfee"); + // gotoffer states have received deck and sent BTCchose already (along with deck) + s = instantdex_statecreate(s,n,"BOB_gotoffer",BTC_waitprivCfunc,0,"BTC_cleanup",0); + s = instantdex_statecreate(s,n,"ALICE_gotoffer",BTC_waitprivCfunc,0,"BTC_cleanup",0); + instantdex_addevent(s,*n,"BOB_gotoffer","BTCprivC","BTCprivs","BOB_sentprivs"); // send privs + instantdex_addevent(s,*n,"ALICE_gotoffer","BTCprivC","BTCprivs","ALICE_sentprivs"); - instantdex_addevent(s,*n,"ALICE_waitdeposit_privs","depfound",0,"ALICE_waitprivs"); - instantdex_addevent(s,*n,"ALICE_waitdeposit_privs","BTCprivs",0,"ALICE_waitdeposit"); + // to reach sentprivs, all paths must have sent/recv deck and Chose and verified cut and choose + s = instantdex_statecreate(s,n,"BOB_sentprivs",BTC_waitprivsfunc,0,"BTC_cleanup",0); + instantdex_addevent(s,*n,"BOB_sentprivs","BTCprivs","poll","BOB_waitfee"); + instantdex_addevent(s,*n,"BOB_sentprivs","feefound","poll","BOB_sentprivs"); - instantdex_addevent(s,*n,"ALICE_waitfee_deposit","depfound",0,"ALICE_waitfee"); - instantdex_addevent(s,*n,"ALICE_waitfee_deposit","feefound",0,"ALICE_waitdeposit"); + s = instantdex_statecreate(s,n,"ALICE_sentprivs",BTC_waitprivsfunc,0,"BTC_cleanup",0); + instantdex_addevent(s,*n,"ALICE_sentprivs","BTCprivs","poll","Alice_waitfee_deposit"); + + s = instantdex_statecreate(s,n,"BOB_waitfee",BOB_waitfeefunc,0,"BTC_cleanup",0); + instantdex_addevent(s,*n,"BOB_waitfee","feefound","BTCdeptx","BOB_sentdeposit"); + instantdex_addevent(s,*n,"BOB_waitfee","poll","poll","BOB_waitfee"); + // following states cover all permutations of the two required events to make altpayment + s = instantdex_statecreate(s,n,"ALICE_waitfee_deposit",ALICE_waitfunc,0,"BTC_cleanup",0); + instantdex_addevent(s,*n,"ALICE_waitfee_deposit","depfound","poll","ALICE_waitfee"); + instantdex_addevent(s,*n,"ALICE_waitfee_deposit","feefound","poll","ALICE_waitdeposit"); + instantdex_addevent(s,*n,"ALICE_waitfee_deposit","poll","poll","ALICE_waitfee_deposit"); + // 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"); + s = instantdex_statecreate(s,n,"Alice_waitfee",ALICE_waitsendfunc,0,"BTC_cleanup",0); + instantdex_addevent(s,*n,"Alice_waitfee","feefound","BTCalttx","ALICE_sentalt"); + instantdex_addevent(s,*n,"Alice_waitfee","poll","poll","Alice_waitfee"); + + s = instantdex_statecreate(s,n,"ALICE_waitdeposit",ALICE_waitsendfunc,0,"BTC_cleanup",0); instantdex_addevent(s,*n,"ALICE_waitdeposit","depfound","BTCalttx","ALICE_sentalt"); + instantdex_addevent(s,*n,"ALICE_waitdeposit","poll","poll","ALICE_waitdeposit"); // now Bob's turn to make sure altpayment is confirmed and send real payment - instantdex_addevent(s,*n,"BOB_sentdeposit","BTCalttx",0,"BOB_altconfirm"); + s = instantdex_statecreate(s,n,"BOB_sentdeposit",BOB_waitBTCalttxfunc,0,"BOB_reclaim",0); + instantdex_addevent(s,*n,"BOB_sentdeposit","BTCalttx","poll","BOB_altconfirm"); + + s = instantdex_statecreate(s,n,"BOB_altconfirm",BOB_waitaltconfirmfunc,0,"BTC_claimdeposit",0); instantdex_addevent(s,*n,"BOB_altconfirm","altfound","BTCpaytx","BOB_sentpayment"); + instantdex_addevent(s,*n,"BOB_altconfirm","poll","poll","BOB_altconfirm"); - instantdex_addevent(s,*n,"ALICE_sentalt","BTCpaytx",0,"ALICE_waitconfirms"); - instantdex_addevent(s,*n,"ALICE_waitconfirms","bobfound",0,"ALICE_reclaim"); + // now Alice's turn to make sure payment is confrmed and send in claim or see bob's reclaim and reclaim + s = instantdex_statecreate(s,n,"ALICE_sentalt",ALICE_waitBTCpaytxfunc,0,"BTC_claimdeposit",0); + instantdex_addevent(s,*n,"ALICE_sentalt","BTCpaytx","poll","ALICE_waitconfirms"); + + s = instantdex_statecreate(s,n,"ALICE_waitconfirms",ALICE_waitpayconf_or_bobreclaimfunc,0,"BTC_claimdeposit",0); + instantdex_addevent(s,*n,"ALICE_waitconfirms","bobfound","poll","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,"ALICE_waitconfirms","poll","poll","ALICE_waitconfirms"); + + // Bob waits for privM either from Alice or alt blockchain + s = instantdex_statecreate(s,n,"BOB_sentpayment",BOB_waitprivMfunc,0,"BTC_claimdeposit",0); + instantdex_addevent(s,*n,"BOB_sentpayment","btcfound","BTCdone","BOB_claimedalt"); instantdex_addevent(s,*n,"BOB_sentpayment","BTCprivM","BTCdone","BOB_claimedalt"); - + instantdex_addevent(s,*n,"BOB_sentpayment","poll","poll","BOB_sentpayment"); 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) +char *instantdex_statemachine(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *A,char *cmdstr,cJSON *argjson,cJSON *newjson,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 ) + uint32_t i; struct iguana_info *altcoin,*coinbtc; struct bitcoin_swapinfo *swap; struct instantdex_stateinfo *state; + if ( (swap= A->info) == 0 || (state= swap->state) == 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 ) + if ( (newjson= (*state->timeout)(myinfo,exchange,A,argjson,newjson,&serdata,&serdatalen)) == 0 ) return(clonestr("{\"error\":\"instantdex_BTCswap null return from timeoutfunc\"}")); return(jprint(newjson,1)); } @@ -517,179 +814,33 @@ char *instantdex_statemachine(struct supernet_info *myinfo,struct exchange_info { if ( strcmp(cmdstr,state->events[i].cmdstr) == 0 ) { - if ( (newjson= (*state->process)(myinfo,exchange,A,argjson,&serdata,&serdatalen)) == 0 ) + if ( (newjson= (*state->process)(myinfo,exchange,A,argjson,newjson,&serdata,&serdatalen)) == 0 ) { - swap->state = state->errorevent; - return(clonestr("{\"error\":\"instantdex_statemachine: null return\"}")); + if ( strcmp("poll",state->events[i].sendcmd) == 0 ) + { + printf("poll event\n"); + return(instantdex_sendcmd(myinfo,&A->offer,newjson,state->events[i].sendcmd,myinfo->myaddr.persistent,0,serdata,serdatalen)); + } + else + { + printf("null return from non-poll event\n"); + swap->state = state->errorevent; + return(clonestr("{\"error\":\"instantdex_statemachine: null return\"}")); + } } else { - swap->state = state->events[i].nextstate; if ( state->events[i].sendcmd != 0 ) + { + swap->state = state->events[i].nextstate; return(instantdex_sendcmd(myinfo,&A->offer,newjson,state->events[i].sendcmd,swap->othertrader,INSTANTDEX_HOPS,serdata,serdatalen)); - else return(clonestr("{\"result\":\"instantdex_statemachine: processed\"}")); + } else return(clonestr("{\"result\":\"instantdex_statemachine: processed\"}")); } } } return(clonestr("{\"error\":\"instantdex_statemachine: unexpected state\"}")); } -#ifdef xxx - if ( strcmp(cmdstr,"step1") == 0 && strcmp(swap->nextstate,cmdstr) == 0 ) // either - { - printf("%s got step1, should have other's choosei\n",swap->isbob!=0?"BOB":"alice"); - 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\"}")); - else - { - printf("%s chose.%d\n",swap->isbob==0?"BOB":"alice",swap->otherschoosei); - 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); - /*if ( swap->isbob == 0 ) - { - if ( (swap->feetx= instantdex_bobtx(myinfo,coinbtc,&swap->ftxid,swap->otherpubs[0],swap->mypubs[0],swap->privkeys[swap->otherschoosei],reftime,swap->insurance,1)) != 0 ) - { - jaddstr(newjson,"feetx",swap->feetx); - jaddbits256(newjson,"ftxid",swap->ftxid); - // broadcast to network - } - }*/ - if ( swap->isbob != 0 ) - { - 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); - } - return(instantdex_sendcmd(myinfo,&A->offer,newjson,swap->isbob != 0 ? "BTCstep3" : "BTCstep2",swap->othertrader,INSTANTDEX_HOPS,swap->privkeys,sizeof(swap->privkeys))); - } - } - else if ( strcmp(cmdstr,"step2") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // bob - { - printf("%s got step2, should have other's privkeys\n",swap->isbob!=0?"BOB":"alice"); - if ( (newjson= instantdex_newjson(myinfo,swap,argjson,swap->orderhash,A,0)) == 0 ) - return(clonestr("{\"error\":\"instantdex_BTCswap step2 null newjson\"}")); - else - { - instantdex_privkeysextract(myinfo,swap,serdata,serdatalen); - if ( swap->cutverified == 0 || swap->otherverifiedcut == 0 ) - return(clonestr("{\"error\":\"instantdex_BTCswap step2, both sides didnt validate\"}")); - else - { - 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 ) - { - jaddstr(newjson,"deposit",swap->deposit); - jaddbits256(newjson,"dtxid",swap->dtxid); - //jaddbits256(newjson,"pubBn",bitcoin_pubkey33(pubkey,swap->pubBn)); - // broadcast to network - strcpy(swap->nextstate,"step4"); - printf("BOB sends (%s), next.(%s)\n","BTCstep3",swap->nextstate); - 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\"}")); - } - } //else return(clonestr("{\"error\":\"instantdex_BTCswap step2 invalid fee\"}")); - } - else if ( strcmp(cmdstr,"step3") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // alice - { - 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 - strcpy(swap->nextstate,"step5"); - printf("Alice sends (%s), next.(%s)\n","BTCstep4",swap->nextstate); - 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\"}")); - } - } - else if ( strcmp(cmdstr,"step4") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // bob - { - 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 - strcpy(swap->nextstate,"step6"); - 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\"}")); - } - else if ( strcmp(cmdstr,"step5") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // alice - { - 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 ) - { - strcpy(swap->nextstate,"step7"); - /*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\"}")); - } - else if ( strcmp(cmdstr,"step6") == 0 && strcmp(swap->nextstate,"cmdstr") == 0 ) // bob - { - 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\"}")); - strcpy(swap->nextstate,"step7"); - /*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\"}"));*/ - } - 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\"}"); - } - else retstr = clonestr("{\"error\":\"BTC swap got unrecognized command\"}"); - if ( retstr == 0 ) - retstr = clonestr("{\"error\":\"BTC swap null retstr\"}"); - if ( swap != 0 ) - printf("BTCSWAP next.(%s) (%s) isbob.%d nextstate.%s verified.(%d %d)\n",swap->nextstate,cmdstr,swap->isbob,swap->nextstate,swap->cutverified,swap->otherverifiedcut); - else printf("BTCSWAP.(%s)\n",retstr); - return(retstr); -#endif - #ifdef oldway // https://github.com/TierNolan/bips/blob/bip4x/bip-atom.mediawiki @@ -892,6 +1043,12 @@ char *instantdex_advance(struct supernet_info *myinfo,bits256 *sharedprivs,int32 } else return(clonestr("{\"error\":\"instantdex_BTCswap advance cant find statusjson\"}")); } +void instantdex_pendingnotice(struct supernet_info *myinfo,struct exchange_info *exchange,struct instantdex_accept *ap,uint64_t basevolume64) +{ + // printf("need to start monitoring thread\n"); + ap->pendingvolume64 -= basevolume64; +} + 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,traderpub,A0,B0,sharedprivs[4]; uint64_t satoshis[2]; @@ -912,7 +1069,7 @@ char *instantdex_BTCswap(struct supernet_info *myinfo,struct exchange_info *exch if ( hash.txid != A->orderid ) return(clonestr("{\"error\":\"txid mismatches orderid\"}")); satoshis[0] = A->offer.basevolume64; - satoshis[1] = instantdex_relsatoshis(A->offer.price64,A->offer.basevolume64); + satoshis[1] = instantdex_BTCsatoshis(A->offer.price64,A->offer.basevolume64); //printf("got offer.(%s) offerside.%d offerdir.%d\n",jprint(argjson,0),A->offer.myside,A->offer.acceptdir); if ( strcmp(cmdstr,"offer") == 0 ) // sender is Bob, receiver is network (Alice) { @@ -925,8 +1082,7 @@ char *instantdex_BTCswap(struct supernet_info *myinfo,struct exchange_info *exch return(clonestr("{\"error\":\"instantdex_BTCswap offer null newjson\"}")); else { - // should add to orderbook if not accepted - instantdex_pendingnotice(myinfo,exchange,ap,A); + //instantdex_pendingnotice(myinfo,exchange,ap,A); return(instantdex_bailinrefund(myinfo,other,exchange,A,"proposal",secret160,newjson,isbob,A0,B0,sharedprivs)); } } diff --git a/iguana/swaps/iguana_PAXswap.c b/iguana/swaps/iguana_PAXswap.c index 7e53bca9f..0d2fdfb80 100755 --- a/iguana/swaps/iguana_PAXswap.c +++ b/iguana/swaps/iguana_PAXswap.c @@ -47,3 +47,26 @@ char *instantdex_PAXswap(struct supernet_info *myinfo,struct exchange_info *exch else retstr = clonestr("{\"error\":\"PAX swap got unrecognized command\"}"); return(retstr); } + +#include "../../includes/secp256k1.h" +//#include "../../crypto777/secp256k1/modules/rangeproof/pedersen_impl.h" +//#include "../../crypto777/secp256k1/modules/rangeproof/borromean_impl.h" +//#include "../../crypto777/secp256k1/modules/rangeproof/rangeproof_impl.h" +void secp256k1_pedersen_context_initialize(secp256k1_context_t *ctx); +int secp256k1_pedersen_commit(const secp256k1_context_t* ctx, unsigned char *commit, unsigned char *blind, uint64_t value); +// ./configure --enable-module-ecdh --enable-module-schnorr --enable-module-rangeproof +void ztest() +{ +#ifdef __APPLE__ + printf("ztests\n"); + secp256k1_context_t *ctx; uint8_t commit[33],blind[32]; int32_t i,retval; + ctx = secp256k1_context_create(SECP256K1_CONTEXT_SIGN | SECP256K1_CONTEXT_VERIFY); + secp256k1_pedersen_context_initialize(ctx); + retval = secp256k1_pedersen_commit(ctx,commit,blind,0x14234); + OS_randombytes(blind,sizeof(blind)); + for (i=0; i<33; i++) + printf("%02x",commit[i]); + printf(" pederson commit.%d\n",retval); + //getchar(); +#endif +} \ No newline at end of file diff --git a/includes/secp256k1.h b/includes/secp256k1.h new file mode 100644 index 000000000..1b8361e00 --- /dev/null +++ b/includes/secp256k1.h @@ -0,0 +1,453 @@ +#ifndef _SECP256K1_ +# define _SECP256K1_ + +# ifdef __cplusplus +extern "C" { +# endif + +# if !defined(SECP256K1_GNUC_PREREQ) +# if defined(__GNUC__)&&defined(__GNUC_MINOR__) +# define SECP256K1_GNUC_PREREQ(_maj,_min) \ + ((__GNUC__<<16)+__GNUC_MINOR__>=((_maj)<<16)+(_min)) +# else +# define SECP256K1_GNUC_PREREQ(_maj,_min) 0 +# endif +# endif + +# if (!defined(__STDC_VERSION__) || (__STDC_VERSION__ < 199901L) ) +# if SECP256K1_GNUC_PREREQ(2,7) +# define SECP256K1_INLINE __inline__ +# elif (defined(_MSC_VER)) +# define SECP256K1_INLINE __inline +# else +# define SECP256K1_INLINE +# endif +# else +# define SECP256K1_INLINE inline +# endif + +/**Warning attributes + * NONNULL is not used if SECP256K1_BUILD is set to avoid the compiler optimizing out + * some paranoid null checks. */ +# if defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4) +# define SECP256K1_WARN_UNUSED_RESULT __attribute__ ((__warn_unused_result__)) +# else +# define SECP256K1_WARN_UNUSED_RESULT +# endif +# if !defined(SECP256K1_BUILD) && defined(__GNUC__) && SECP256K1_GNUC_PREREQ(3, 4) +# define SECP256K1_ARG_NONNULL(_x) __attribute__ ((__nonnull__(_x))) +# else +# define SECP256K1_ARG_NONNULL(_x) +# endif + +/** Opaque data structure that holds context information (precomputed tables etc.). + * Only functions that take a pointer to a non-const context require exclusive + * access to it. Multiple functions that take a pointer to a const context may + * run simultaneously. + */ +typedef struct secp256k1_context_struct secp256k1_context_t; + +/** Flags to pass to secp256k1_context_create. */ +# define SECP256K1_CONTEXT_VERIFY (1 << 0) +# define SECP256K1_CONTEXT_SIGN (1 << 1) + +/** Create a secp256k1 context object. + * Returns: a newly created context object. + * In: flags: which parts of the context to initialize. + */ +secp256k1_context_t* secp256k1_context_create( + int flags +) SECP256K1_WARN_UNUSED_RESULT; + +/** Copies a secp256k1 context object. + * Returns: a newly created context object. + * In: ctx: an existing context to copy + */ +secp256k1_context_t* secp256k1_context_clone( + const secp256k1_context_t* ctx +) SECP256K1_WARN_UNUSED_RESULT; + +/** Destroy a secp256k1 context object. + * The context pointer may not be used afterwards. + */ +void secp256k1_context_destroy( + secp256k1_context_t* ctx +) SECP256K1_ARG_NONNULL(1); + +/** Set a callback function to be called when an illegal argument is passed to + * an API call. The philosophy is that these shouldn't be dealt with through a + * specific return value, as calling code should not have branches to deal with + * the case that this code itself is broken. + * On the other hand, during debug stage, one would want to be informed about + * such mistakes, and the default (crashing) may be inadvisable. + * When this callback is triggered, the API function called is guaranteed not + * to cause a crash, though its return value and output arguments are + * undefined. + */ +void secp256k1_context_set_illegal_callback( + secp256k1_context_t* ctx, + void (*fun)(const char* message, void* data), + void* data +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Set a callback function to be called when an internal consistency check + * fails. The default is crashing. + * This can only trigger in case of a hardware failure, miscompilation, + * memory corruption, serious bug in the library, or other error would can + * otherwise result in undefined behaviour. It will not trigger due to mere + * incorrect usage of the API (see secp256k1_context_set_illegal_callback + * for that). After this callback returns, anything may happen, including + * crashing. + */ +void secp256k1_context_set_error_callback( + secp256k1_context_t* ctx, + void (*fun)(const char* message, void* data), + void* data +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Data type to hold a parsed and valid public key. + This data type should be considered opaque to the user, and only created + through API functions. It is not guaranteed to be compatible between + different implementations. If you need to convert to a format suitable + for storage or transmission, use secp256k1_ec_pubkey_serialize and + secp256k1_ec_pubkey_parse. + */ +typedef struct { + unsigned char data[64]; +} secp256k1_pubkey_t; + +/** Parse a variable-length public key into the pubkey object. + * Returns: 1 if the public key was fully valid. + * 0 if the public key could not be parsed or is invalid. + * In: ctx: a secp256k1 context object. + * input: pointer to a serialized public key + * inputlen: length of the array pointed to by input + * Out: pubkey: pointer to a pubkey object. If 1 is returned, it is set to a + * parsed version of input. If not, its value is undefined. + * This function supports parsing compressed (33 bytes, header byte 0x02 or + * 0x03), uncompressed (65 bytes, header byte 0x04), or hybrid (65 bytes, header + * byte 0x06 or 0x07) format public keys. + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_parse( + const secp256k1_context_t* ctx, + secp256k1_pubkey_t* pubkey, + const unsigned char *input, + int inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize a pubkey object into a serialized byte sequence. + * Returns: 1 always. + * In: ctx: a secp256k1 context object. + * pubkey: a pointer to a secp256k1_pubkey_t containing an initialized + * public key. + * compressed: whether to serialize in compressed format. + * Out: output: a pointer to a 65-byte (if compressed==0) or 33-byte (if + * compressed==1) byte array to place the serialized key in. + * outputlen: a pointer to an integer which will contain the serialized + * size. + */ +int secp256k1_ec_pubkey_serialize( + const secp256k1_context_t* ctx, + unsigned char *output, + int *outputlen, + const secp256k1_pubkey_t* pubkey, + int compressed +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Data type to hold a parsed ECDSA signature, optionally supporting pubkey + * recovery. + This data type should be considered opaque to the user, and only created + through API functions. It is not guaranteed to be compatible between + different implementations. If you need to convert to a format suitable + for storage or transmission, use secp256k1_ecdsa_signature_serialize_* and + secp256k1_ecdsa_signature_parse_* functions. */ +typedef struct { + unsigned char data[65]; +} secp256k1_ecdsa_signature_t; + +/** Parse a DER ECDSA signature. + * Returns: 1 when the signature could be parsed, 0 otherwise. + * In: ctx: a secp256k1 context object + * input: a pointer to the signature to be parsed + * inputlen: the length of the array pointed to be input + * Out: sig: a pointer to a signature object + * + * Note that this function also supports some violations of DER. + * + * The resulting signature object will not support pubkey recovery. + */ +int secp256k1_ecdsa_signature_parse_der( + const secp256k1_context_t* ctx, + secp256k1_ecdsa_signature_t* sig, + const unsigned char *input, + int inputlen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Parse a compact ECDSA signature (64 bytes + recovery id). + * Returns: 1 when the signature could be parsed, 0 otherwise + * In: ctx: a secp256k1 context object + * input64: a pointer to a 64-byte compact signature + * recid: the recovery id (0, 1, 2 or 3, or -1 for unknown) + * Out: sig: a pointer to a signature object + * + * If recid is not -1, the resulting signature object will support pubkey + * recovery. + */ +int secp256k1_ecdsa_signature_parse_compact( + const secp256k1_context_t* ctx, + secp256k1_ecdsa_signature_t* sig, + const unsigned char *input64, + int recid +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Serialize an ECDSA signature in DER format. + * Returns: 1 if enough space was available to serialize, 0 otherwise + * In: ctx: a secp256k1 context object + * sig: a pointer to an initialized signature object + * Out: output: a pointer to an array to store the DER serialization + * In/Out: outputlen: a pointer to a length integer. Initially, this integer + * should be set to the length of output. After the call + * it will be set to the length of the serialization (even + * if 0 was returned). + */ +int secp256k1_ecdsa_signature_serialize_der( + const secp256k1_context_t* ctx, + unsigned char *output, + int *outputlen, + const secp256k1_ecdsa_signature_t* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Serialize an ECDSA signature in compact format (64 bytes + recovery id). + * Returns: 1 + * In: ctx: a secp256k1 context object + * sig: a pointer to an initialized signature object (cannot be NULL) + * Out: output64: a pointer to a 64-byte array of the compact signature (cannot be NULL) + * recid: a pointer to an integer to hold the recovery id (can be NULL). + * + * If recid is not NULL, the signature must support pubkey recovery. + */ +int secp256k1_ecdsa_signature_serialize_compact( + const secp256k1_context_t* ctx, + unsigned char *output64, + int *recid, + const secp256k1_ecdsa_signature_t* sig +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +/** Verify an ECDSA signature. + * Returns: 1: correct signature + * 0: incorrect or unparseable signature + * In: ctx: a secp256k1 context object, initialized for verification. + * msg32: the 32-byte message hash being verified (cannot be NULL) + * sig: the signature being verified (cannot be NULL) + * pubkey: pointer to an initialized public key to verify with (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_verify( + const secp256k1_context_t* ctx, + const unsigned char *msg32, + const secp256k1_ecdsa_signature_t *sig, + const secp256k1_pubkey_t *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** A pointer to a function to deterministically generate a nonce. + * Returns: 1 if a nonce was successfully generated. 0 will cause signing to fail. + * In: msg32: the 32-byte message hash being verified (will not be NULL) + * key32: pointer to a 32-byte secret key (will not be NULL) + * algo16: pointer to a 16-byte array describing the signature + * algorithm (will be NULL for ECDSA for compatibility). + * attempt: how many iterations we have tried to find a nonce. + * This will almost always be 0, but different attempt values + * are required to result in a different nonce. + * data: Arbitrary data pointer that is passed through. + * Out: nonce32: pointer to a 32-byte array to be filled by the function. + * Except for test cases, this function should compute some cryptographic hash of + * the message, the key and the attempt. + */ +typedef int (*secp256k1_nonce_function_t)( + unsigned char *nonce32, + const unsigned char *msg32, + const unsigned char *key32, + const unsigned char *algo16, + unsigned int attempt, + const void *data +); + +/** An implementation of RFC6979 (using HMAC-SHA256) as nonce generation function. + * If a data pointer is passed, it is assumed to be a pointer to 32 bytes of + * extra entropy. + */ +extern const secp256k1_nonce_function_t secp256k1_nonce_function_rfc6979; + +/** A default safe nonce generation function (currently equal to secp256k1_nonce_function_rfc6979). */ +extern const secp256k1_nonce_function_t secp256k1_nonce_function_default; + +/** Create an ECDSA signature. + * Returns: 1: signature created + * 0: the nonce generation function failed, or the private key was invalid. + * In: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * msg32: the 32-byte message hash being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * noncefp:pointer to a nonce generation function. If NULL, secp256k1_nonce_function_default is used + * ndata: pointer to arbitrary data used by the nonce generation function (can be NULL) + * Out: sig: pointer to an array where the signature will be placed (cannot be NULL) + * + * The resulting signature will support pubkey recovery. + * + * The sig always has an s value in the lower half of the range (From 0x1 + * to 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0, + * inclusive), unlike many other implementations. + * With ECDSA a third-party can can forge a second distinct signature + * of the same message given a single initial signature without knowing + * the key by setting s to its additive inverse mod-order, 'flipping' the + * sign of the random point R which is not included in the signature. + * Since the forgery is of the same message this isn't universally + * problematic, but in systems where message malleability or uniqueness + * of signatures is important this can cause issues. This forgery can be + * blocked by all verifiers forcing signers to use a canonical form. The + * lower-S form reduces the size of signatures slightly on average when + * variable length encodings (such as DER) are used and is cheap to + * verify, making it a good choice. Security of always using lower-S is + * assured because anyone can trivially modify a signature after the + * fact to enforce this property. Adjusting it inside the signing + * function avoids the need to re-serialize or have curve specific + * constants outside of the library. By always using a canonical form + * even in applications where it isn't needed it becomes possible to + * impose a requirement later if a need is discovered. + * No other forms of ECDSA malleability are known and none seem likely, + * but there is no formal proof that ECDSA, even with this additional + * restriction, is free of other malleability. Commonly used serialization + * schemes will also accept various non-unique encodings, so care should + * be taken when this property is required for an application. + */ +int secp256k1_ecdsa_sign( + const secp256k1_context_t* ctx, + const unsigned char *msg32, + secp256k1_ecdsa_signature_t *sig, + const unsigned char *seckey, + secp256k1_nonce_function_t noncefp, + const void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Recover an ECDSA public key from a signature. + * Returns: 1: public key successfully recovered (which guarantees a correct signature). + * 0: otherwise. + * In: ctx: pointer to a context object, initialized for verification (cannot be NULL) + * msg32: the 32-byte message hash assumed to be signed (cannot be NULL) + * sig64: pointer to initialized signature that supports pubkey recovery (cannot be NULL) + * Out: pubkey: pointer to the recoved public key (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdsa_recover( + const secp256k1_context_t* ctx, + const unsigned char *msg32, + const secp256k1_ecdsa_signature_t *sig, + secp256k1_pubkey_t *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Verify an ECDSA secret key. + * Returns: 1: secret key is valid + * 0: secret key is invalid + * In: ctx: pointer to a context object (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_seckey_verify( + const secp256k1_context_t* ctx, + const unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2); + +/** Compute the public key for a secret key. + * In: ctx: pointer to a context object, initialized for signing (cannot be NULL) + * seckey: pointer to a 32-byte private key (cannot be NULL) + * Out: pubkey: pointer to the created public key (cannot be NULL) + * Returns: 1: secret was valid, public key stores + * 0: secret was invalid, try again + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_create( + const secp256k1_context_t* ctx, + secp256k1_pubkey_t *pubkey, + const unsigned char *seckey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Export a private key in DER format. + * In: ctx: pointer to a context object, initialized for signing (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_export( + const secp256k1_context_t* ctx, + const unsigned char *seckey, + unsigned char *privkey, + int *privkeylen, + int compressed +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Import a private key in DER format. */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_import( + const secp256k1_context_t* ctx, + unsigned char *seckey, + const unsigned char *privkey, + int privkeylen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a private key by adding tweak to it. */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_add( + const secp256k1_context_t* ctx, + unsigned char *seckey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a public key by adding tweak times the generator to it. + * In: ctx: pointer to a context object, initialized for verification (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_add( + const secp256k1_context_t* ctx, + secp256k1_pubkey_t *pubkey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a private key by multiplying it with tweak. */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_privkey_tweak_mul( + const secp256k1_context_t* ctx, + unsigned char *seckey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Tweak a public key by multiplying it with tweak. + * In: ctx: pointer to a context object, initialized for verification (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_tweak_mul( + const secp256k1_context_t* ctx, + secp256k1_pubkey_t *pubkey, + const unsigned char *tweak +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Updates the context randomization. + * Returns: 1: randomization successfully updated + * 0: error + * In: ctx: pointer to a context object (cannot be NULL) + * seed32: pointer to a 32-byte random seed (NULL resets to initial state) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_context_randomize( + secp256k1_context_t* ctx, + const unsigned char *seed32 +) SECP256K1_ARG_NONNULL(1); + +/** Add a number of public keys together. + * Returns: 1: the sum of the public keys is valid. + * 0: the sum of the public keys is not valid. + * In: ctx: pointer to a context object + * out: pointer to pubkey for placing the resulting public key + * (cannot be NULL) + * n: the number of public keys to add together (must be at least 1) + * ins: pointer to array of pointers to public keys (cannot be NULL) + * Use secp256k1_ec_pubkey_compress and secp256k1_ec_pubkey_decompress if the + * uncompressed format is needed. + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_ec_pubkey_combine( + const secp256k1_context_t* ctx, + secp256k1_pubkey_t *out, + int n, + const secp256k1_pubkey_t * const * ins +) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/includes/secp256k1_ecdh.h b/includes/secp256k1_ecdh.h new file mode 100644 index 000000000..671c393fa --- /dev/null +++ b/includes/secp256k1_ecdh.h @@ -0,0 +1,30 @@ +#ifndef _SECP256K1_ECDH_ +# define _SECP256K1_ECDH_ + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +/** Compute an EC Diffie-Hellman secret in constant time + * Returns: 1: exponentiation was successful + * 0: scalar was invalid (zero or overflow) + * In: ctx: pointer to a context object (cannot be NULL) + * point: pointer to a public point + * scalar: a 32-byte scalar with which to multiply the point + * Out: result: a 32-byte array which will be populated by an ECDH + * secret computed from the point and scalar + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_ecdh( + const secp256k1_context_t* ctx, + unsigned char *result, + const secp256k1_pubkey_t *point, + const unsigned char *scalar +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/includes/secp256k1_rangeproof.h b/includes/secp256k1_rangeproof.h new file mode 100644 index 000000000..02d66fcc1 --- /dev/null +++ b/includes/secp256k1_rangeproof.h @@ -0,0 +1,186 @@ +#ifndef _SECP256K1_RANGEPROOF_ +# define _SECP256K1_RANGEPROOF_ + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +#include + +/** Initialize a context for usage with Pedersen commitments. */ +int secp256k1_pedersen_context_initialize(secp256k1_context_t* ctx); + +/** Generate a pedersen commitment. + * Returns 1: commitment successfully created. + * 0: error + * In: ctx: pointer to a context object, initialized for signing and Pedersen commitment (cannot be NULL) + * blind: pointer to a 32-byte blinding factor (cannot be NULL) + * value: unsigned 64-bit integer value to commit to. + * Out: commit: pointer to a 33-byte array for the commitment (cannot be NULL) + * + * Blinding factors can be generated and verified in the same way as secp256k1 private keys for ECDSA. + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_commit( + const secp256k1_context_t* ctx, + unsigned char *commit, + unsigned char *blind, + uint64_t value +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Computes the sum of multiple positive and negative blinding factors. + * Returns 1: sum successfully computed. + * 0: error + * In: ctx: pointer to a context object (cannot be NULL) + * blinds: pointer to pointers to 32-byte character arrays for blinding factors. (cannot be NULL) + * n: number of factors pointed to by blinds. + * nneg: how many of the initial factors should be treated with a positive sign. + * Out: blind_out: pointer to a 32-byte array for the sum (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_blind_sum( + const secp256k1_context_t* ctx, + unsigned char *blind_out, + const unsigned char * const *blinds, + int n, + int npositive +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3); + +/** Verify a tally of pedersen commitments + * Returns 1: commitments successfully sum to zero. + * 0: Commitments do not sum to zero or other error. + * In: ctx: pointer to a context object, initialized for Pedersen commitment (cannot be NULL) + * commits: pointer to pointers to 33-byte character arrays for the commitments. (cannot be NULL if pcnt is non-zero) + * pcnt: number of commitments pointed to by commits. + * ncommits: pointer to pointers to 33-byte character arrays for negative commitments. (cannot be NULL if ncnt is non-zero) + * ncnt: number of commitments pointed to by ncommits. + * excess: signed 64bit amount to add to the total to bring it to zero, can be negative. + * + * This computes sum(commit[0..pcnt)) - sum(ncommit[0..ncnt)) - excess*H == 0. + * + * A pedersen commitment is xG + vH where G and H are generators for the secp256k1 group and x is a blinding factor, + * while v is the committed value. For a collection of commitments to sum to zero both their blinding factors and + * values must sum to zero. + * + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_pedersen_verify_tally( + const secp256k1_context_t* ctx, + const unsigned char * const *commits, + int pcnt, + const unsigned char * const *ncommits, + int ncnt, + int64_t excess +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +/** Initialize a context for usage with Pedersen commitments. */ +int secp256k1_rangeproof_context_initialize(secp256k1_context_t* ctx); + +/** Verify a proof that a committed value is within a range. + * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs. + * 0: Proof failed or other error. + * In: ctx: pointer to a context object, initialized for range-proof and commitment (cannot be NULL) + * commit: the 33-byte commitment being proved. (cannot be NULL) + * proof: pointer to character array with the proof. (cannot be NULL) + * plen: length of proof in bytes. + * Out: min_value: pointer to a unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) + * max_value: pointer to a unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_verify( + const secp256k1_context_t* ctx, + uint64_t *min_value, + uint64_t *max_value, + const unsigned char *commit, + const unsigned char *proof, + int plen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +/** Verify a range proof proof and rewind the proof to recover information sent by its author. + * Returns 1: Value is within the range [0..2^64), the specifically proven range is in the min/max value outputs, and the value and blinding were recovered. + * 0: Proof failed, rewind failed, or other error. + * In: ctx: pointer to a context object, initialized for range-proof and Pedersen commitment (cannot be NULL) + * commit: the 33-byte commitment being proved. (cannot be NULL) + * proof: pointer to character array with the proof. (cannot be NULL) + * plen: length of proof in bytes. + * nonce: 32-byte secret nonce used by the prover (cannot be NULL) + * In/Out: blind_out: storage for the 32-byte blinding factor used for the commitment + * value_out: pointer to an unsigned int64 which has the exact value of the commitment. + * message_out: pointer to a 4096 byte character array to receive message data from the proof author. + * outlen: length of message data written to message_out. + * min_value: pointer to an unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) + * max_value: pointer to an unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_rewind( + const secp256k1_context_t* ctx, + unsigned char *blind_out, + uint64_t *value_out, + unsigned char *message_out, + int *outlen, + const unsigned char *nonce, + uint64_t *min_value, + uint64_t *max_value, + const unsigned char *commit, + const unsigned char *proof, + int plen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7) SECP256K1_ARG_NONNULL(8) SECP256K1_ARG_NONNULL(9) SECP256K1_ARG_NONNULL(10); + +/** Author a proof that a committed value is within a range. + * Returns 1: Proof successfully created. + * 0: Error + * In: ctx: pointer to a context object, initialized for range-proof, signing, and Pedersen commitment (cannot be NULL) + * proof: pointer to array to receive the proof, can be up to 5134 bytes. (cannot be NULL) + * min_value: constructs a proof where the verifer can tell the minimum value is at least the specified amount. + * commit: 33-byte array with the commitment being proved. + * blind: 32-byte blinding factor used by commit. + * nonce: 32-byte secret nonce used to initialize the proof (value can be reverse-engineered out of the proof if this secret is known.) + * exp: Base-10 exponent. Digits below above will be made public, but the proof will be made smaller. Allowed range is -1 to 18. + * (-1 is a special case that makes the value public. 0 is the most private.) + * min_bits: Number of bits of the value to keep private. (0 = auto/minimal, - 64). + * value: Actual value of the commitment. + * In/out: plen: point to an integer with the size of the proof buffer and the size of the constructed proof. + * + * If min_value or exp is non-zero then the value must be on the range [0, 2^63) to prevent the proof range from spanning past 2^64. + * + * If exp is -1 the value is revealed by the proof (e.g. it proves that the proof is a blinding of a specific value, without revealing the blinding key.) + * + * This can randomly fail with probability around one in 2^100. If this happens, buy a lottery ticket and retry with a different nonce or blinding. + * + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_sign( + const secp256k1_context_t* ctx, + unsigned char *proof, + int *plen, + uint64_t min_value, + const unsigned char *commit, + const unsigned char *blind, + const unsigned char *nonce, + int exp, + int min_bits, + uint64_t value +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7); + +/** Extract some basic information from a range-proof. + * Returns 1: Information successfully extracted. + * 0: Decode failed. + * In: ctx: pointer to a context object + * proof: pointer to character array with the proof. + * plen: length of proof in bytes. + * Out: exp: Exponent used in the proof (-1 means the value isn't private). + * mantissa: Number of bits covered by the proof. + * min_value: pointer to an unsigned int64 which will be updated with the minimum value that commit could have. (cannot be NULL) + * max_value: pointer to an unsigned int64 which will be updated with the maximum value that commit could have. (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_rangeproof_info( + const secp256k1_context_t* ctx, + int *exp, + int *mantissa, + uint64_t *min_value, + uint64_t *max_value, + const unsigned char *proof, + int plen +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5); + +# ifdef __cplusplus +} +# endif + +#endif diff --git a/includes/secp256k1_schnorr.h b/includes/secp256k1_schnorr.h new file mode 100644 index 000000000..ff7b977f4 --- /dev/null +++ b/includes/secp256k1_schnorr.h @@ -0,0 +1,173 @@ +#ifndef _SECP256K1_SCHNORR_ +# define _SECP256K1_SCHNORR_ + +# include "secp256k1.h" + +# ifdef __cplusplus +extern "C" { +# endif + +/** Create a signature using a custom EC-Schnorr-SHA256 construction. It + * produces non-malleable 64-byte signatures which support public key recovery + * batch validation, and multiparty signing. + * Returns: 1: signature created + * 0: the nonce generation function failed, or the private key was + * invalid. + * In: ctx: pointer to a context object, initialized for signing + * (cannot be NULL) + * msg32: the 32-byte message hash being signed (cannot be NULL) + * seckey: pointer to a 32-byte secret key (cannot be NULL) + * noncefp:pointer to a nonce generation function. If NULL, + * secp256k1_nonce_function_default is used + * ndata: pointer to arbitrary data used by the nonce generation + * function (can be NULL) + * Out: sig64: pointer to a 64-byte array where the signature will be + * placed (cannot be NULL) + */ +int secp256k1_schnorr_sign( + const secp256k1_context_t* ctx, + const unsigned char *msg32, + unsigned char *sig64, + const unsigned char *seckey, + secp256k1_nonce_function_t noncefp, + const void *ndata +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Verify a signature created by secp256k1_schnorr_sign. + * Returns: 1: correct signature + * 0: incorrect signature + * In: ctx: a secp256k1 context object, initialized for verification. + * msg32: the 32-byte message hash being verified (cannot be NULL) + * sig64: the 64-byte signature being verified (cannot be NULL) + * pubkey: the public key to verify with (cannot be NULL) + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_verify( + const secp256k1_context_t* ctx, + const unsigned char *msg32, + const unsigned char *sig64, + const secp256k1_pubkey_t *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Recover an EC public key from a Schnorr signature created using + * secp256k1_schnorr_sign. + * Returns: 1: public key successfully recovered (which guarantees a correct + * signature). + * 0: otherwise. + * In: ctx: pointer to a context object, initialized for + * verification (cannot be NULL) + * msg32: the 32-byte message hash assumed to be signed (cannot + * be NULL) + * sig64: signature as 64 byte array (cannot be NULL) + * Out: pubkey: pointer to a pubkey to set to the recovered public key + * (cannot be NULL). + */ +int secp256k1_schnorr_recover( + const secp256k1_context_t* ctx, + const unsigned char *msg32, + const unsigned char *sig64, + secp256k1_pubkey_t *pubkey +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4); + +/** Generate a nonce pair deterministically for use with + * secp256k1_schnorr_partial_sign. + * Returns: 1: valid nonce pair was generated. + * 0: otherwise (nonce generation function failed) + * In: ctx: pointer to a context object, initialized for signing + * (cannot be NULL) + * msg32: the 32-byte message hash assumed to be signed (cannot + * be NULL) + * sec32: the 32-byte private key (cannot be NULL) + * noncefp: pointer to a nonce generation function. If NULL, + * secp256k1_nonce_function_default is used + * noncedata: pointer to arbitrary data used by the nonce generation + * function (can be NULL) + * Out: pubnonce: public side of the nonce (cannot be NULL) + * privnonce32: private side of the nonce (32 byte) (cannot be NULL) + * + * Do not use the output as a private/public key pair for signing/validation. + */ +int secp256k1_schnorr_generate_nonce_pair( + const secp256k1_context_t* ctx, + const unsigned char *msg32, + const unsigned char *sec32, + secp256k1_nonce_function_t noncefp, + const void* noncedata, + secp256k1_pubkey_t *pubnonce, + unsigned char *privnonce32 +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(6) SECP256K1_ARG_NONNULL(7); + +/** Produce a partial Schnorr signature, which can be combined using + * secp256k1_schnorr_partial_combine, to end up with a full signature that is + * verifiable using secp256k1_schnorr_verify. + * Returns: 1: signature created succesfully. + * 0: no valid signature exists with this combination of keys, nonces + * and message (chance around 1 in 2^128) + * -1: invalid private key, nonce, or public nonces. + * In: ctx: pointer to context object, initialized for signing (cannot + * be NULL) + * msg32: pointer to 32-byte message to sign + * sec32: pointer to 32-byte private key + * secnonce32: pointer to 32-byte array containing our nonce + * pubnonce_others: pointer to pubkey containing the sum of the other's + * nonces (see secp256k1_ec_pubkey_combine) + * Out: sig64: pointer to 64-byte array to put partial signature in + * + * The intended procedure for creating a multiparty signature is: + * - Each signer S[i] with private key x[i] and public key Q[i] runs + * secp256k1_schnorr_generate_nonce_pair to produce a pair (k[i],R[i]) of + * private/public nonces. + * - All signers communicate their public nonces to each other (revealing your + * private nonce can lead to discovery of your private key, so it should be + * considered secret). + * - All signers combine all the public nonces they received (excluding their + * own) using secp256k1_ec_pubkey_combine to obtain an + * Rall[i] = sum(R[0..i-1,i+1..n]). + * - All signers produce a partial signature using + * secp256k1_schnorr_partial_sign, passing in their own private key x[i], + * their own private nonce k[i], and the sum of the others' public nonces + * Rall[i]. + * - All signers communicate their partial signatures to each other. + * - Someone combines all partial signatures using + * secp256k1_schnorr_partial_combine, to obtain a full signature. + * - The resulting signature is validatable using secp256k1_schnorr_verify, with + * public key equal to the result of secp256k1_ec_pubkey_combine of the + * signers' public keys (sum(Q[0..n])). + * + * Note that secp256k1_schnorr_partial_combine and secp256k1_ec_pubkey_combine + * function take their arguments in any order, and it is possible to + * pre-combine several inputs already with one call, and add more inputs later + * by calling the function again (they are commutative and associative). + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_sign( + const secp256k1_context_t* ctx, + const unsigned char *msg32, + unsigned char *sig64, + const unsigned char *sec32, + const unsigned char *secnonce32, + const secp256k1_pubkey_t *pubnonce_others +) SECP256K1_ARG_NONNULL(1) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(3) SECP256K1_ARG_NONNULL(4) SECP256K1_ARG_NONNULL(5) SECP256K1_ARG_NONNULL(6); + +/** Combine multiple Schnorr partial signatures. + * Returns: 1: the passed signatures were succesfully combined. + * 0: the resulting signature is not valid (chance of 1 in 2^256) + * -1: some inputs were invalid, or the signatures were not created + * using the same set of nonces + * In: ctx: pointer to a context object + * sig64: pointer to a 64-byte array to place the combined signature + * (cannot be NULL) + * n: the number of signatures to combine (at least 1) + * Out: sig64sin: pointer to an array of n pointers to 64-byte input + * signatures + */ +SECP256K1_WARN_UNUSED_RESULT int secp256k1_schnorr_partial_combine( + const secp256k1_context_t* ctx, + unsigned char *sig64, + int n, + const unsigned char * const * sig64sin +) SECP256K1_ARG_NONNULL(2) SECP256K1_ARG_NONNULL(4); + +# ifdef __cplusplus +} +# endif + +#endif