You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1546 lines
66 KiB
1546 lines
66 KiB
/******************************************************************************
|
|
* Copyright © 2014-2017 The SuperNET Developers. *
|
|
* *
|
|
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
|
* the top-level directory of this distribution for the individual copyright *
|
|
* holder information and the developer policies on copyright and licensing. *
|
|
* *
|
|
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
|
* SuperNET software, including this file may be copied, modified, propagated *
|
|
* or distributed except according to the terms contained in the LICENSE file *
|
|
* *
|
|
* Removal or modification of this copyright notice is prohibited. *
|
|
* *
|
|
******************************************************************************/
|
|
|
|
// compare multiple rawtx returns
|
|
|
|
#include "iguana777.h"
|
|
|
|
char *iguana_APIrequest(struct iguana_info *coin,bits256 blockhash,bits256 txid,int32_t seconds)
|
|
{
|
|
int32_t i,len; char *retstr = 0; uint8_t serialized[1024]; char str[65];
|
|
coin->APIblockhash = blockhash;
|
|
coin->APItxid = txid;
|
|
printf("request block.(%s) txid.%llx\n",bits256_str(str,blockhash),(long long)txid.txid);
|
|
if ( (len= iguana_getdata(coin,serialized,MSG_BLOCK,&blockhash,1)) > 0 )
|
|
{
|
|
for (i=0; i<seconds; i++)
|
|
{
|
|
if ( i == 0 )
|
|
iguana_send(coin,0,serialized,len);
|
|
if ( coin->APIblockstr != 0 )
|
|
{
|
|
retstr = coin->APIblockstr;
|
|
coin->APIblockstr = 0;
|
|
memset(&coin->APIblockhash,0,sizeof(coin->APIblockhash));
|
|
memset(&coin->APItxid,0,sizeof(coin->APItxid));
|
|
return(retstr);
|
|
}
|
|
sleep(1);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
bits256 iguana_str2priv(struct supernet_info *myinfo,struct iguana_info *coin,char *str)
|
|
{
|
|
bits256 privkey; int32_t n; uint8_t addrtype; struct iguana_waccount *wacct=0; struct iguana_waddress *waddr;
|
|
memset(&privkey,0,sizeof(privkey));
|
|
if ( str != 0 )
|
|
{
|
|
n = (int32_t)strlen(str) >> 1;
|
|
if ( n == sizeof(bits256) && is_hexstr(str,sizeof(bits256)) > 0 )
|
|
decode_hex(privkey.bytes,sizeof(privkey),str);
|
|
else if ( bitcoin_wif2priv(&addrtype,&privkey,str) != sizeof(bits256) )
|
|
{
|
|
if ( (waddr= iguana_waddresssearch(myinfo,&wacct,str)) != 0 )
|
|
privkey = waddr->privkey;
|
|
else memset(privkey.bytes,0,sizeof(privkey));
|
|
}
|
|
}
|
|
return(privkey);
|
|
}
|
|
|
|
int32_t iguana_pubkeyget(struct supernet_info *myinfo,struct iguana_info *coin,uint8_t *pubkey33,char *str)
|
|
{
|
|
bits256 privkey,pubkey; uint8_t pubkeydata[128]; int32_t len,plen= -1; struct iguana_waccount *wacct; struct iguana_waddress *waddr;
|
|
len = (int32_t)strlen(str);
|
|
if ( is_hexstr(str,len) == 0 )
|
|
{
|
|
if ( (waddr= iguana_waddresssearch(myinfo,&wacct,str)) != 0 )
|
|
{
|
|
if ( (plen= bitcoin_pubkeylen(waddr->pubkey)) > 0 )
|
|
memcpy(pubkeydata,waddr->pubkey,plen);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
decode_hex(pubkeydata,len,str);
|
|
plen = bitcoin_pubkeylen(pubkeydata);
|
|
}
|
|
if ( plen <= 0 )
|
|
{
|
|
privkey = iguana_str2priv(myinfo,coin,str);
|
|
if ( bits256_nonz(privkey) == 0 )
|
|
return(-1);
|
|
else
|
|
{
|
|
pubkey = bitcoin_pubkey33(myinfo->ctx,pubkeydata,privkey);
|
|
if ( bits256_nonz(pubkey) == 0 )
|
|
return(-1);
|
|
}
|
|
}
|
|
if ( (plen= bitcoin_pubkeylen(pubkeydata)) > 0 )
|
|
memcpy(pubkey33,pubkeydata,plen);
|
|
return(0);
|
|
}
|
|
|
|
cJSON *iguana_p2shjson(struct supernet_info *myinfo,struct iguana_info *coin,cJSON *retjson,struct iguana_waddress *waddr)
|
|
{
|
|
char str[4096]; uint8_t type; struct iguana_waccount *wacct; bits256 debugtxid; struct vin_info V; cJSON *privkeys,*pubkeys,*addresses; int32_t i,plen;
|
|
if ( retjson == 0 )
|
|
retjson = cJSON_CreateObject();
|
|
init_hexbytes_noT(str,waddr->redeemScript,waddr->scriptlen);
|
|
jaddstr(retjson,"redeemScript",str);
|
|
memset(debugtxid.bytes,0,sizeof(debugtxid));
|
|
if ( (type= iguana_calcrmd160(coin,0,&V,waddr->redeemScript,waddr->scriptlen, debugtxid,-1,0xffffffff)) >= 0 )
|
|
{
|
|
privkeys = cJSON_CreateArray();
|
|
pubkeys = cJSON_CreateArray();
|
|
addresses = cJSON_CreateArray();
|
|
for (i=0; i<V.N; i++)
|
|
{
|
|
if ( V.signers[i].coinaddr[0] != 0 && (waddr= iguana_waddresssearch(myinfo,&wacct,V.signers[i].coinaddr)) != 0 && waddr->wifstr[0] != 0 )
|
|
jaddistr(privkeys,waddr->wifstr);
|
|
else jaddistr(privkeys,"");
|
|
if ( (plen= bitcoin_pubkeylen(V.signers[i].pubkey)) > 0 )
|
|
{
|
|
init_hexbytes_noT(str,V.signers[i].pubkey,plen);
|
|
jaddistr(pubkeys,str);
|
|
} else jaddistr(pubkeys,"");
|
|
jaddistr(addresses,V.signers[i].coinaddr);
|
|
}
|
|
jaddstr(retjson,"result",V.coinaddr);
|
|
jaddnum(retjson,"M",V.M);
|
|
jaddnum(retjson,"N",V.N);
|
|
jadd(retjson,"pubkeys",pubkeys);
|
|
jadd(retjson,"privkeys",privkeys);
|
|
jadd(retjson,"addresses",addresses);
|
|
}
|
|
return(retjson);
|
|
}
|
|
|
|
cJSON *iguana_scriptobj(struct iguana_info *coin,uint8_t rmd160[20],char *coinaddr,char *asmstr,uint8_t *script,int32_t scriptlen)
|
|
{
|
|
struct vin_info V; int32_t i,plen,asmtype; char pubkeystr[130],rmdstr[41]; cJSON *addrobj,*scriptobj=cJSON_CreateObject();
|
|
if ( (asmtype= iguana_calcrmd160(coin,asmstr,&V,script,scriptlen,rand256(0),1,0xffffffff)) >= 0 )
|
|
{
|
|
if ( asmstr != 0 && asmstr[0] != 0 )
|
|
jaddstr(scriptobj,"asm",asmstr);
|
|
jaddnum(scriptobj,"iguanatype",asmtype);
|
|
jaddnum(scriptobj,"scriptlen",scriptlen);
|
|
jaddnum(scriptobj,"reqSigs",V.M);
|
|
if ( (plen= bitcoin_pubkeylen(V.signers[0].pubkey)) > 0 )
|
|
{
|
|
init_hexbytes_noT(pubkeystr,V.signers[0].pubkey,plen);
|
|
jaddstr(scriptobj,"pubkey",pubkeystr);
|
|
init_hexbytes_noT(rmdstr,V.signers[0].rmd160,20);
|
|
jaddstr(scriptobj,"rmd160",rmdstr);
|
|
}
|
|
addrobj = cJSON_CreateArray();
|
|
for (i=0; i<V.N; i++)
|
|
jaddistr(addrobj,V.signers[i].coinaddr);
|
|
jadd(scriptobj,"addresses",addrobj);
|
|
if ( scriptlen != 0 )
|
|
{
|
|
bitcoin_address(coinaddr,coin->chain->p2shtype,script,scriptlen);
|
|
jaddstr(scriptobj,"p2sh",coinaddr);
|
|
}
|
|
memcpy(rmd160,V.rmd160,20);
|
|
}
|
|
return(scriptobj);
|
|
}
|
|
|
|
int32_t iguana_RTbestunspent(struct supernet_info *myinfo,struct iguana_info *coin,int32_t *aboveip,int64_t *abovep,int32_t *belowip,int64_t *belowp,struct iguana_outpoint *unspents,int32_t numunspents,uint64_t value,int32_t maxmode)
|
|
{
|
|
int32_t i,abovei,belowi; int64_t above,below,gap,atx_value,maxvalue = 0;
|
|
abovei = belowi = -1;
|
|
for (above=below=i=0; i<numunspents; i++)
|
|
{
|
|
if ( (atx_value= unspents[i].value) <= 0 )
|
|
{
|
|
//printf("illegal value.%d\n",i);
|
|
continue;
|
|
}
|
|
if ( iguana_RTunspent_check(myinfo,coin,unspents[i]) != 0 )
|
|
{
|
|
printf("(%d u%d) %.8f already used\n",unspents[i].hdrsi,unspents[i].unspentind,dstr(atx_value));
|
|
continue;
|
|
}
|
|
if ( maxmode == 0 )
|
|
{
|
|
//printf("(%.8f vs %.8f)\n",dstr(atx_value),dstr(value));
|
|
if ( atx_value == value )
|
|
{
|
|
*aboveip = *belowip = i;
|
|
*abovep = *belowp = 0;
|
|
return(i);
|
|
}
|
|
else if ( atx_value > value )
|
|
{
|
|
gap = (atx_value - value);
|
|
if ( above == 0 || gap < above )
|
|
{
|
|
above = gap;
|
|
abovei = i;
|
|
}
|
|
} else gap = (value - atx_value);
|
|
if ( below == 0 || gap < below )
|
|
{
|
|
below = gap;
|
|
belowi = i;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
//printf("(%.8f vs %.8f)\n",dstr(atx_value),dstr(maxvalue));
|
|
if ( atx_value > maxvalue )
|
|
{
|
|
maxvalue = atx_value;
|
|
above = (atx_value - value);
|
|
abovei = i;
|
|
}
|
|
}
|
|
}
|
|
*aboveip = abovei;
|
|
*abovep = above;
|
|
*belowip = belowi;
|
|
*belowp = below;
|
|
//printf("above.%d below.%d\n",abovei,belowi);
|
|
return(abovei >= 0 ? abovei : belowi);
|
|
}
|
|
|
|
cJSON *iguana_inputjson(bits256 txid,int32_t vout,uint8_t *spendscript,int32_t spendlen)
|
|
{
|
|
char hexstr[IGUANA_MAXSCRIPTSIZE*2 + 1]; cJSON *sobj,*item = cJSON_CreateObject();
|
|
jaddbits256(item,"txid",txid);
|
|
jaddnum(item,"vout",vout);
|
|
sobj = cJSON_CreateObject();
|
|
init_hexbytes_noT(hexstr,spendscript,spendlen);
|
|
jaddstr(sobj,"hex",hexstr);
|
|
jadd(item,"scriptPubKey",sobj);
|
|
return(item);
|
|
}
|
|
|
|
cJSON *iguana_RTinputsjson(struct supernet_info *myinfo,struct iguana_info *coin,uint64_t *totalp,uint64_t amount,struct iguana_outpoint *unspents,int32_t num,int32_t maxmode)
|
|
{
|
|
struct iguana_outpoint outpt; cJSON *vins; int32_t abovei,belowi,i,ind; int64_t above,below,total = 0,remains = amount;
|
|
*totalp = 0;
|
|
vins = cJSON_CreateArray();
|
|
for (i=0; i<num; i++)
|
|
{
|
|
below = above = 0;
|
|
if ( iguana_RTbestunspent(myinfo,coin,&abovei,&above,&belowi,&below,unspents,num,remains,maxmode) < 0 )
|
|
{
|
|
printf("error finding unspent i.%d of %d, %.8f vs %.8f\n",i,num,dstr(remains),dstr(amount));
|
|
free_json(vins);
|
|
return(0);
|
|
}
|
|
if ( belowi < 0 || (num == 0 && abovei >= 0) )
|
|
ind = abovei;
|
|
else ind = belowi;
|
|
outpt = unspents[ind];
|
|
memset(&unspents[ind],0,sizeof(unspents[ind]));
|
|
jaddi(vins,iguana_inputjson(outpt.txid,outpt.vout,outpt.spendscript,outpt.spendlen));
|
|
total += outpt.value;
|
|
remains -= outpt.value;
|
|
//printf("%s value %.8f -> remains %.8f\n",coinaddr,dstr(value),dstr(remains));
|
|
if ( remains <= 0 )
|
|
break;
|
|
}
|
|
*totalp = total;
|
|
return(vins);
|
|
}
|
|
|
|
char *iguana_signrawtx(struct supernet_info *myinfo,struct iguana_info *coin,int32_t height,bits256 *signedtxidp,int32_t *completedp,cJSON *vins,char *rawtx,cJSON *privkeys,struct vin_info *V)
|
|
{
|
|
char *signedtx = 0; struct iguana_msgtx msgtx; int32_t numinputs,flagV = 0,flag = 0;
|
|
*completedp = 0;
|
|
if ( privkeys == 0 )
|
|
privkeys = iguana_privkeysjson(myinfo,coin,vins), flag = 1;
|
|
if ( (numinputs= cJSON_GetArraySize(vins)) > 0 && privkeys != 0 )
|
|
{
|
|
memset(&msgtx,0,sizeof(msgtx));
|
|
if ( V == 0 )
|
|
V = calloc(numinputs,sizeof(*V)), flagV = 1;
|
|
//printf("SIGN.(%s) priv.(%s) %llx %llx\n",jprint(vins,0),jprint(privkeys,0),(long long)V->signers[0].privkey.txid,(long long)V->signers[1].privkey.txid);
|
|
if ( V != 0 )
|
|
{
|
|
if ( iguana_signrawtransaction(myinfo,coin,height,&msgtx,&signedtx,signedtxidp,V,numinputs,rawtx,vins,privkeys) > 0 )
|
|
*completedp = 1;
|
|
else printf("signrawtransaction incomplete\n");
|
|
if ( flagV != 0 )
|
|
free(V);
|
|
}
|
|
if ( flag != 0 )
|
|
free_json(privkeys);
|
|
}
|
|
//char str[65]; printf("completed.%d %s signed.(%s)\n",*completedp,bits256_str(str,*signedtxidp),signedtx!=0?signedtx:"");
|
|
return(signedtx);
|
|
}
|
|
|
|
bits256 iguana_sendrawtransaction(struct supernet_info *myinfo,struct iguana_info *coin,char *signedtx)
|
|
{
|
|
bits256 txid,checktxid; uint8_t *serialized; int32_t i,len,n; struct iguana_peer *addr; cJSON *vals; char *str;
|
|
len = (int32_t)strlen(signedtx) >> 1;
|
|
serialized = calloc(1,sizeof(struct iguana_msghdr) + len);
|
|
decode_hex(&serialized[sizeof(struct iguana_msghdr)],len,signedtx);
|
|
txid = bits256_doublesha256(0,&serialized[sizeof(struct iguana_msghdr)],len);
|
|
if ( coin->FULLNODE < 0 || coin->notarychain >= 0 )
|
|
{
|
|
if ( coin->FULLNODE < 0 )
|
|
str = dpow_sendrawtransaction(myinfo,coin,signedtx);
|
|
else str = _dex_sendrawtransaction(myinfo,coin->symbol,signedtx);
|
|
if ( str != 0 )
|
|
{
|
|
if ( is_hexstr(str,0) == sizeof(checktxid)*2 )
|
|
{
|
|
decode_hex(checktxid.bytes,sizeof(checktxid),str);
|
|
if ( bits256_cmp(txid,checktxid) == 0 )
|
|
{
|
|
free(str);
|
|
return(txid);
|
|
}
|
|
}
|
|
free(str);
|
|
memset(txid.bytes,0,sizeof(txid));
|
|
return(txid);
|
|
}
|
|
}
|
|
if ( coin->peers != 0 && (n= coin->peers->numranked) > 0 )
|
|
{
|
|
for (i=0; i<8 && i<n; i++)
|
|
{
|
|
if ( (addr= coin->peers->ranked[i]) != 0 && addr->dead == 0 && addr->usock >= 0 )
|
|
iguana_queue_send(addr,0,serialized,"tx",len);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
vals = cJSON_CreateObject();
|
|
jaddstr(vals,"symbol",coin->symbol);
|
|
if ( (str= gecko_sendrawtransaction(myinfo,coin->symbol,serialized,len,txid,vals,signedtx)) != 0 )
|
|
free(str);
|
|
free_json(vals);
|
|
}
|
|
free(serialized);
|
|
return(txid);
|
|
}
|
|
|
|
uint64_t iguana_interest(struct supernet_info *myinfo,struct iguana_info *coin,bits256 txid,int32_t vout,uint64_t value)
|
|
{
|
|
char *retstr; int32_t height; cJSON *retjson; struct iguana_txid T,*tx;
|
|
int32_t minutes; uint64_t numerator=0,denominator=0,interest = 0;
|
|
if ( coin->FULLNODE < 0 ) // komodod is running
|
|
{
|
|
if ( (retjson= dpow_gettxout(myinfo,coin,txid,vout)) != 0 )
|
|
{
|
|
interest = jdouble(retjson,"interest") * SATOSHIDEN;
|
|
free_json(retjson);
|
|
}
|
|
}
|
|
else if ( coin->FULLNODE == 0 ) // basilisk mode -> use DEX* API
|
|
{
|
|
if ( (retstr= _dex_gettxout(myinfo,coin->symbol,txid,vout)) != 0 )
|
|
{
|
|
if ( (retjson= cJSON_Parse(retstr)) != 0 )
|
|
{
|
|
interest = jdouble(retjson,"interest") * SATOSHIDEN;
|
|
free_json(retjson);
|
|
}
|
|
free(retstr);
|
|
}
|
|
}
|
|
else // we have it local
|
|
{
|
|
if ( (tx= iguana_txidfind(coin,&height,&T,txid,coin->bundlescount)) != 0 && tx->locktime >LOCKTIME_THRESHOLD )
|
|
{
|
|
if ( (minutes= ((uint32_t)time(NULL) - 60 - tx->locktime) / 60) >= 60 )
|
|
{
|
|
denominator = (((uint64_t)365 * 24 * 60) / minutes);
|
|
if ( denominator == 0 )
|
|
denominator = 1; // max KOMODO_INTEREST per transfer, do it at least annually!
|
|
if ( value > 25000LL*SATOSHIDEN && height > 155949 )
|
|
{
|
|
numerator = (value / 20); // assumes 5%!
|
|
interest = (numerator / denominator);
|
|
}
|
|
else if ( value >= 10*SATOSHIDEN )
|
|
{
|
|
numerator = (value * KOMODO_INTEREST);
|
|
interest = (numerator / denominator) / SATOSHIDEN;
|
|
}
|
|
fprintf(stderr,"komodo_interest %lld %.8f nLockTime.%u tiptime.%u minutes.%d interest %lld %.8f (%llu / %llu)\n",(long long)value,(double)value/SATOSHIDEN,tx->locktime,(uint32_t)time(NULL),minutes,(long long)interest,(double)interest/SATOSHIDEN,(long long)numerator,(long long)denominator);
|
|
}
|
|
}
|
|
}
|
|
char str[65]; printf("interest for %s.v%d %.8f %.8f\n",bits256_str(str,txid),vout,dstr(value),dstr(interest));
|
|
return(interest);
|
|
}
|
|
|
|
uint64_t iguana_interests(struct supernet_info *myinfo,struct iguana_info *coin,cJSON *vins)
|
|
{
|
|
int32_t i,n; cJSON *item; uint64_t value,interest = 0;
|
|
if ( is_cJSON_Array(vins) != 0 && (n= cJSON_GetArraySize(vins)) > 0 )
|
|
{
|
|
for (i=0; i<n; i++)
|
|
{
|
|
item = jitem(vins,i);
|
|
if ( (value= jdouble(item,"value")*SATOSHIDEN) == 0 )
|
|
value = jdouble(item,"amount")*SATOSHIDEN;
|
|
interest += iguana_interest(myinfo,coin,jbits256(item,"txid"),jint(item,"vout"),value);
|
|
}
|
|
}
|
|
return(interest);
|
|
}
|
|
|
|
char *iguana_calcrawtx(struct supernet_info *myinfo,struct iguana_info *coin,cJSON **vinsp,cJSON *txobj,int64_t satoshis,char *changeaddr,int64_t txfee,cJSON *addresses,int32_t minconf,uint8_t *opreturn,int32_t oplen,int64_t burnamount,char *remoteaddr,struct vin_info *V,int32_t maxmode)
|
|
{
|
|
uint8_t addrtype,rmd160[20],spendscript[IGUANA_MAXSCRIPTSIZE]; char *coinaddr; int32_t allocflag=0,max,i,j,m,n,num,spendlen; char *spendscriptstr,*rawtx=0; bits256 txid; cJSON *vins=0,*array,*item; uint64_t value,avail=0,total,change; struct iguana_outpoint *unspents = 0;
|
|
*vinsp = 0;
|
|
max = 0;//10000;
|
|
satoshis += burnamount;
|
|
if ( (n= cJSON_GetArraySize(addresses)) == 0 )
|
|
return(0);
|
|
for (i=0; i<n; i++)
|
|
{
|
|
coinaddr = jstri(addresses,i);
|
|
if ( (array= basilisk_unspents(myinfo,coin,coinaddr)) != 0 )
|
|
{
|
|
//printf("unspents.(%s) %s\n",coinaddr,jprint(array,0));
|
|
if ( (m= cJSON_GetArraySize(array)) > 0 )
|
|
{
|
|
for (j=0; j<m; j++)
|
|
{
|
|
item = jitem(array,j);
|
|
if ( coin->FULLNODE != 0 && is_cJSON_False(jobj(item,"spendable")) != 0 )
|
|
continue;
|
|
if ( (spendscriptstr= jstr(item,"scriptPubKey")) == 0 )
|
|
{
|
|
printf("no spendscriptstr.(%s)\n",jprint(item,0));
|
|
continue;
|
|
}
|
|
unspents = realloc(unspents,(1 + max) * sizeof(*unspents));
|
|
value = jdouble(item,"amount") * SATOSHIDEN;
|
|
iguana_outptset(myinfo,coin,&unspents[max++],jbits256(item,"txid"),jint(item,"vout"),value,spendscriptstr);
|
|
avail += value;
|
|
}
|
|
}
|
|
free_json(array);
|
|
}
|
|
}
|
|
if ( unspents == 0 )
|
|
return(0);
|
|
num = max;
|
|
/*unspents = calloc(max,sizeof(*unspents));
|
|
if ( (num= iguana_RTunspentslists(myinfo,coin,&avail,unspents,max,satoshis+txfee,minconf,addresses,remoteaddr)) <= 0 )
|
|
{
|
|
free(unspents);
|
|
return(0);
|
|
}*/
|
|
printf("avail %.8f satoshis %.8f, txfee %.8f burnamount %.8f vin0.scriptlen %d\n",dstr(avail),dstr(satoshis),dstr(txfee),dstr(burnamount),unspents[0].spendlen);
|
|
if ( txobj != 0 && avail >= satoshis+txfee )
|
|
{
|
|
if ( (vins= iguana_RTinputsjson(myinfo,coin,&total,satoshis + txfee,unspents,num,maxmode)) != 0 )
|
|
{
|
|
if ( strcmp(coin->symbol,"KMD") == 0 )
|
|
total += iguana_interests(myinfo,coin,vins);
|
|
if ( total < (satoshis + txfee) )
|
|
{
|
|
free_json(vins);
|
|
free(unspents);
|
|
printf("insufficient total %.8f vs (%.8f + %.8f)\n",dstr(total),dstr(satoshis),dstr(txfee));
|
|
return(0);
|
|
}
|
|
if ( (change= (total - (satoshis + txfee))) > 10000 && (changeaddr == 0 || changeaddr[0] == 0) )
|
|
{
|
|
printf("no changeaddr for %.8f\n",dstr(change));
|
|
free_json(vins);
|
|
free(unspents);
|
|
return(0);
|
|
}
|
|
iguana_createvins(myinfo,coin,txobj,vins);
|
|
if ( change > 10000 )
|
|
{
|
|
if ( iguana_addressvalidate(coin,&addrtype,changeaddr) < 0 )
|
|
{
|
|
free_json(vins);
|
|
free(unspents);
|
|
printf("illegal destination address.(%s)\n",changeaddr);
|
|
return(0);
|
|
}
|
|
bitcoin_addr2rmd160(&addrtype,rmd160,changeaddr);
|
|
spendlen = bitcoin_standardspend(spendscript,0,rmd160);
|
|
bitcoin_txoutput(txobj,spendscript,spendlen,change);
|
|
if ( opreturn != 0 )
|
|
{
|
|
int32_t i;
|
|
for (i=0; i<oplen; i++)
|
|
printf("%02x",opreturn[i]);
|
|
printf(" <- got opret\n");
|
|
bitcoin_txoutput(txobj,opreturn,oplen,burnamount);
|
|
}
|
|
}
|
|
if ( vins != 0 && V == 0 )
|
|
{
|
|
V = calloc(cJSON_GetArraySize(vins),sizeof(*V)), allocflag = 1;
|
|
//iguana_vinprivkeys(myinfo,coin,V,vins);
|
|
}
|
|
rawtx = bitcoin_json2hex(myinfo,coin,&txid,txobj,V);
|
|
if ( allocflag != 0 )
|
|
free(V);
|
|
}
|
|
}
|
|
free(unspents);
|
|
*vinsp = vins;
|
|
return(rawtx);
|
|
}
|
|
|
|
void iguana_RTunspentslock(struct supernet_info *myinfo,struct iguana_info *coin,cJSON *vins)
|
|
{
|
|
struct iguana_outpoint spentpt; char coinaddr[64]; int32_t i,RTspentflag,num,spentheight,lockedflag;
|
|
if ( coin->MAXPEERS == 1 || coin->FULLNODE > 0 || coin->VALIDATENODE > 0 )
|
|
{
|
|
num = cJSON_GetArraySize(vins);
|
|
for (i=0; i<num; i++)
|
|
{
|
|
if ( iguana_RTinputaddress(myinfo,coin,coinaddr,&spentpt,jitem(vins,i)) != 0 )
|
|
iguana_RTutxofunc(coin,&spentheight,&lockedflag,spentpt,&RTspentflag,1,0); // last arg should be spentheight
|
|
}
|
|
}
|
|
}
|
|
|
|
char *sendtoaddress(struct supernet_info *myinfo,struct iguana_info *coin,char *remoteaddr,char *destaddr,uint64_t satoshis,uint64_t txfee,char *comment,char *comment2,int32_t minconf,char *account)
|
|
{
|
|
uint8_t addrtype,spendscript[1024],rmd160[20]; int32_t completed; char *retstr,spendscriptstr[4096],*rawtx=0,*signedtx = 0; bits256 signedtxid,senttxid; cJSON *retjson,*vins,*addresses,*valsobj; uint32_t spendlen,locktime = 0; uint32_t basilisktag; struct vin_info *V = 0;
|
|
//sendtoaddress <bitcoinaddress> <amount> [comment] [comment-to] <amount> is a real and is rounded to 8 decimal places. Returns the transaction ID <txid> if successful. Y
|
|
if ( coin->RTheight == 0 )
|
|
return(clonestr("{\"error\":\"need to get to realtime blocks to send transaction\"}"));
|
|
if ( account == 0 || account[0] == 0 )
|
|
account = "*";
|
|
addresses = iguana_getaddressesbyaccount(myinfo,coin,account);
|
|
if ( coin->changeaddr[0] == 0 )
|
|
{
|
|
bitcoin_address(coin->changeaddr,coin->chain->pubtype,myinfo->persistent_pubkey33,33);
|
|
printf("%s change %s\n",coin->symbol,coin->changeaddr);
|
|
}
|
|
if ( destaddr != 0 && destaddr[0] != 0 && satoshis != 0 )
|
|
{
|
|
if ( iguana_addressvalidate(coin,&addrtype,destaddr) < 0 )
|
|
return(clonestr("{\"error\":\"invalid coin address\"}"));
|
|
bitcoin_addr2rmd160(&addrtype,rmd160,destaddr);
|
|
spendlen = bitcoin_standardspend(spendscript,0,rmd160);
|
|
init_hexbytes_noT(spendscriptstr,spendscript,spendlen);
|
|
basilisktag = (uint32_t)rand();
|
|
valsobj = cJSON_CreateObject();
|
|
jadd(valsobj,"addresses",addresses);
|
|
jaddstr(valsobj,"coin",coin->symbol);
|
|
jaddstr(valsobj,"changeaddr",coin->changeaddr);
|
|
jaddstr(valsobj,"spendscript",spendscriptstr);
|
|
jadd64bits(valsobj,"satoshis",satoshis);
|
|
jadd64bits(valsobj,"txfee",txfee);
|
|
jaddnum(valsobj,"minconf",minconf);
|
|
jaddnum(valsobj,"basilisktag",basilisktag);
|
|
jaddnum(valsobj,"locktime",locktime);
|
|
jaddnum(valsobj,"timeout",30000);
|
|
if ( 0 && comment != 0 && is_hexstr(comment,0) > 0 )
|
|
jaddstr(valsobj,"opreturn",comment);
|
|
if ( (retstr= basilisk_bitcoinrawtx(myinfo,coin,remoteaddr,basilisktag,jint(valsobj,"timeout"),valsobj,V)) != 0 )
|
|
{
|
|
if ( (retjson= cJSON_Parse(retstr)) != 0 )
|
|
{
|
|
if ( (rawtx= jstr(retjson,"rawtx")) != 0 && (vins= jobj(retjson,"vins")) != 0 )
|
|
{
|
|
if ( (signedtx= iguana_signrawtx(myinfo,coin,coin->blocks.hwmchain.height,&signedtxid,&completed,vins,rawtx,0,V)) != 0 )
|
|
{
|
|
iguana_RTunspentslock(myinfo,coin,vins);
|
|
retjson = cJSON_CreateObject();
|
|
jaddbits256(retjson,"result",signedtxid);
|
|
jaddstr(retjson,"signedtx",signedtx);
|
|
jadd(retjson,"complete",completed != 0 ? jtrue() : jfalse());
|
|
if ( completed != 0 )
|
|
{
|
|
senttxid = iguana_sendrawtransaction(myinfo,coin,signedtx);
|
|
if ( bits256_cmp(senttxid,signedtxid) == 0 )
|
|
{
|
|
jaddstr(retjson,"sendrawtransaction","success");
|
|
iguana_unspents_mark(myinfo,coin,vins);
|
|
} else jaddbits256(retjson,"senderror",senttxid);
|
|
}
|
|
free_json(vins);
|
|
free(signedtx);
|
|
return(jprint(retjson,1));
|
|
}
|
|
else
|
|
{
|
|
free_json(vins);
|
|
return(clonestr("{\"error\":\"couldnt sign rawtx\"}"));
|
|
}
|
|
}
|
|
free_json(retjson);
|
|
}
|
|
free(retstr);
|
|
return(clonestr("{\"error\":\"couldnt create rawtx\"}"));
|
|
} else return(clonestr("{\"error\":\"couldnt create rawtx\"}"));
|
|
}
|
|
return(clonestr("{\"error\":\"need address and amount\"}"));
|
|
}
|
|
|
|
#include "../includes/iguana_apidefs.h"
|
|
#include "../includes/iguana_apideclares.h"
|
|
|
|
STRING_AND_INT(bitcoinrpc,sendrawtransaction,rawtx,allowhighfees)
|
|
{
|
|
cJSON *retjson = cJSON_CreateObject(); bits256 txid;
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
txid = iguana_sendrawtransaction(myinfo,coin,rawtx);
|
|
jaddbits256(retjson,"result",txid);
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
STRING_ARG(bitcoinrpc,submitblock,rawbytes)
|
|
{
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
cJSON *retjson = cJSON_CreateObject();
|
|
// send to all peers
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
ZERO_ARGS(iguana,makekeypair)
|
|
{
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
bits256 privkey; char str[67]; cJSON *retjson = cJSON_CreateObject();
|
|
privkey = rand256(1);
|
|
jaddstr(retjson,"result","success");
|
|
jaddstr(retjson,"privkey",bits256_str(str,privkey));
|
|
jadd(retjson,"rosetta",SuperNET_rosettajson(myinfo,privkey,1));
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
STRING_ARG(bitcoinrpc,validatepubkey,pubkeystr)
|
|
{
|
|
uint8_t pubkey[65],addrtype = 0; int32_t plen; char coinaddr[128],*str; cJSON *retjson;
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
plen = (int32_t)strlen(pubkeystr) >> 1;
|
|
if ( plen >= 33 && plen <= 65 && coin != 0 && coin->chain != 0 )
|
|
{
|
|
addrtype = coin->chain->pubtype;
|
|
decode_hex(pubkey,plen,pubkeystr);
|
|
if ( (str= bitcoin_address(coinaddr,addrtype,pubkey,plen)) != 0 )
|
|
{
|
|
if ( iguana_addressvalidate(coin,&addrtype,coinaddr) < 0 )
|
|
return(clonestr("{\"error\":\"invalid coin address\"}"));
|
|
retjson = cJSON_CreateObject();
|
|
jaddstr(retjson,"result","success");
|
|
jaddstr(retjson,"pubkey",pubkeystr);
|
|
jaddstr(retjson,"address",coinaddr);
|
|
jaddstr(retjson,"coin",coin->symbol);
|
|
return(jprint(retjson,1));
|
|
}
|
|
}
|
|
return(clonestr("{\"error\":\"invalid pubkey\"}"));
|
|
}
|
|
|
|
STRING_ARG(bitcoinrpc,decodescript,scriptstr)
|
|
{
|
|
int32_t scriptlen; uint8_t script[IGUANA_MAXSCRIPTSIZE],rmd160[20]; char coinaddr[128],asmstr[IGUANA_MAXSCRIPTSIZE*2+1]; cJSON *scriptobj,*retjson = cJSON_CreateObject();
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
if ( scriptstr != 0 && coin != 0 && (scriptlen= (int32_t)strlen(scriptstr)>>1) < sizeof(script) )
|
|
{
|
|
decode_hex(script,scriptlen,scriptstr);
|
|
if ( (scriptobj= iguana_scriptobj(coin,rmd160,coinaddr,asmstr,script,scriptlen)) != 0 )
|
|
jadd(retjson,"result",scriptobj);
|
|
}
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
INT_ARRAY_STRING(bitcoinrpc,createmultisig,M,pubkeys,ignore)
|
|
{
|
|
cJSON *retjson,*pkjson,*addresses; uint8_t script[2048],p2sh_rmd160[20]; char pubkeystr[256],msigaddr[64],*pkstr,scriptstr[sizeof(script)*2+1]; struct vin_info V; int32_t i,plen,len,n = cJSON_GetArraySize(pubkeys);
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
if ( n < 0 || n > 16 || M < 0 || M > n )
|
|
return(clonestr("{\"error\":\"illegal number of pubkeys\"}"));
|
|
memset(&V,0,sizeof(V));
|
|
V.M = M, V.N = n;
|
|
pkjson = cJSON_CreateArray();
|
|
addresses = cJSON_CreateArray();
|
|
for (i=0; i<n; i++)
|
|
{
|
|
if ( (pkstr= jstr(jitem(pubkeys,i),0)) != 0 )
|
|
{
|
|
if ( iguana_pubkeyget(myinfo,coin,V.signers[i].pubkey,pkstr) < 0 )
|
|
break;
|
|
if ( (plen= bitcoin_pubkeylen(V.signers[i].pubkey)) <= 0 )
|
|
break;
|
|
bitcoin_address(V.signers[i].coinaddr,coin->chain->pubtype,V.signers[i].pubkey,plen);
|
|
jaddistr(addresses,V.signers[i].coinaddr);
|
|
init_hexbytes_noT(pubkeystr,V.signers[i].pubkey,plen);
|
|
jaddistr(pkjson,pubkeystr);
|
|
} else break;
|
|
}
|
|
retjson = cJSON_CreateObject();
|
|
if ( i == n )
|
|
{
|
|
len = bitcoin_MofNspendscript(p2sh_rmd160,script,0,&V);
|
|
bitcoin_address(msigaddr,coin->chain->p2shtype,p2sh_rmd160,sizeof(p2sh_rmd160));
|
|
jaddstr(retjson,"result","success");
|
|
jaddstr(retjson,"address",msigaddr);
|
|
init_hexbytes_noT(scriptstr,script,len);
|
|
jaddstr(retjson,"redeemScript",scriptstr);
|
|
jaddnum(retjson,"M",M);
|
|
jaddnum(retjson,"N",n);
|
|
jadd(retjson,"pubkeys",pkjson);
|
|
jadd(retjson,"addresses",addresses);
|
|
}
|
|
else
|
|
{
|
|
jaddstr(retjson,"error","couldnt get all pubkeys");
|
|
free_json(pkjson);
|
|
}
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
INT_ARRAY_STRING(bitcoinrpc,addmultisigaddress,M,pubkeys,account) //
|
|
{
|
|
cJSON *retjson,*tmpjson,*setjson=0; char *retstr,*str=0,*msigaddr,*redeemScript;
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
if ( myinfo->expiration == 0 )
|
|
return(clonestr("{\"error\":\"need to unlock wallet\"}"));
|
|
myinfo->expiration++;
|
|
if ( (retstr= bitcoinrpc_createmultisig(IGUANA_CALLARGS,M,pubkeys,account)) != 0 )
|
|
{
|
|
//printf("CREATEMULTISIG.(%s)\n",retstr);
|
|
if ( (retjson= cJSON_Parse(retstr)) != 0 )
|
|
{
|
|
if ( (msigaddr= jstr(retjson,"address")) != 0 )
|
|
{
|
|
if ( (redeemScript= jstr(retjson,"redeemScript")) == 0 || (str= setaccount(myinfo,coin,0,account,msigaddr,redeemScript)) == 0 || (setjson= cJSON_Parse(str)) == 0 || jobj(setjson,"error") != 0 )
|
|
{
|
|
if ( jobj(retjson,"result") != 0 )
|
|
jdelete(retjson,"result");
|
|
if ( jobj(retjson,"error") == 0 )
|
|
jaddstr(retjson,"error","couldnt add multisig address to account");
|
|
}
|
|
else
|
|
{
|
|
tmpjson = cJSON_CreateObject();
|
|
jaddstr(tmpjson,"result",msigaddr);
|
|
free_json(retjson);
|
|
free(retstr);
|
|
retjson = tmpjson;
|
|
}
|
|
}
|
|
if ( setjson != 0 )
|
|
free_json(setjson);
|
|
if ( str != 0 )
|
|
free(str);
|
|
return(jprint(retjson,1));
|
|
} else return(clonestr("{\"error\":\"couldnt parse retstr from createmultisig\"}"));
|
|
} else return(clonestr("{\"error\":\"no retstr from createmultisig\"}"));
|
|
}
|
|
|
|
HASH_AND_TWOINTS(bitcoinrpc,gettxout,txid,vout,mempool)
|
|
{
|
|
uint8_t script[IGUANA_MAXSCRIPTSIZE],rmd160[20],pubkey33[33]; char coinaddr[128],asmstr[IGUANA_MAXSCRIPTSIZE*2+1]; struct iguana_bundle *bp; int32_t firstslot,minconf,scriptlen,unspentind,height,spentheight=-1; struct iguana_RTtxid *ptr; uint64_t RTspend,value; struct iguana_ramchaindata *rdata; struct iguana_pkhash *P; struct iguana_txid *T; struct iguana_unspent *U; struct iguana_outpoint outpt; struct iguana_ramchain *ramchain; cJSON *scriptobj,*retjson = cJSON_CreateObject();
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
if ( coin != 0 )
|
|
{
|
|
if ( (value= _RTgettxout(coin,&ptr,&height,&scriptlen,script,rmd160,coinaddr,txid,vout,mempool)) > 0 )
|
|
{
|
|
jaddbits256(retjson,"bestblock",coin->blocks.hwmchain.RO.hash2);
|
|
jaddnum(retjson,"bestheight",coin->blocks.hwmchain.height);
|
|
jaddnum(retjson,"height",height);
|
|
jaddnum(retjson,"confirmations",coin->blocks.hwmchain.height - height + 1);
|
|
jaddnum(retjson,"value",dstr(value));
|
|
jaddnum(retjson,"amount",dstr(value));
|
|
if ( (height % coin->chain->bundlesize) == 0 && vout == 0 )
|
|
jadd(retjson,"coinbase",jtrue());
|
|
else jadd(retjson,"coinbase",jfalse());
|
|
asmstr[0] = 0;
|
|
if ( (scriptobj= iguana_scriptobj(coin,rmd160,coinaddr,asmstr,script,scriptlen)) != 0 )
|
|
jadd(retjson,"scriptPubKey",scriptobj);
|
|
return(jprint(retjson,1));
|
|
}
|
|
minconf = (mempool != 0) ? 0 : 1;
|
|
if ( iguana_RTunspentindfind(myinfo,coin,&outpt,0,0,0,0,&height,txid,vout,coin->bundlescount-1,0) == 0 && outpt.isptr == 0 )
|
|
{
|
|
unspentind = outpt.unspentind;
|
|
if ( height >= 0 && height < coin->longestchain && (bp= coin->bundles[height / coin->chain->bundlesize]) != 0 )
|
|
{
|
|
ramchain = &bp->ramchain;
|
|
if ( (rdata= ramchain->H.data) != 0 )
|
|
{
|
|
U = RAMCHAIN_PTR(rdata,Uoffset);
|
|
P = RAMCHAIN_PTR(rdata,Poffset);
|
|
T = RAMCHAIN_PTR(rdata,Toffset);
|
|
RTspend = 0;
|
|
memset(&outpt,0,sizeof(outpt));
|
|
outpt.hdrsi = bp->hdrsi;
|
|
outpt.unspentind = unspentind;
|
|
if ( iguana_markedunspents_find(coin,&firstslot,txid,vout) < 0 && iguana_RTspentflag(myinfo,coin,&RTspend,&spentheight,ramchain,outpt,height,minconf,coin->longestchain,U[unspentind].value) == 0 )
|
|
{
|
|
jaddbits256(retjson,"bestblock",coin->blocks.hwmchain.RO.hash2);
|
|
jaddnum(retjson,"bestheight",coin->blocks.hwmchain.height);
|
|
jaddnum(retjson,"height",height);
|
|
jaddnum(retjson,"confirmations",coin->blocks.hwmchain.height - height + 1);
|
|
jaddnum(retjson,"value",dstr(U[unspentind].value));
|
|
memset(rmd160,0,sizeof(rmd160));
|
|
memset(pubkey33,0,sizeof(pubkey33));
|
|
memset(coinaddr,0,sizeof(coinaddr));
|
|
if ( (scriptlen= iguana_voutscript(coin,bp,script,0,&U[unspentind],&P[U[unspentind].pkind],vout)) > 0 )
|
|
{
|
|
if ( (scriptobj= iguana_scriptobj(coin,rmd160,coinaddr,asmstr,script,scriptlen)) != 0 )
|
|
jadd(retjson,"scriptPubKey",scriptobj);
|
|
}
|
|
jadd(retjson,"iguana",iguana_RTunspentjson(myinfo,coin,outpt,T[U[unspentind].txidind].txid,unspentind-T[U[unspentind].txidind].firstvout,U[unspentind].value,&U[unspentind],rmd160,coinaddr,pubkey33,spentheight,remoteaddr));
|
|
if ( (height % coin->chain->bundlesize) == 0 && vout == 0 )
|
|
jadd(retjson,"coinbase",jtrue());
|
|
else jadd(retjson,"coinbase",jfalse());
|
|
}
|
|
else
|
|
{
|
|
jaddstr(retjson,"error","already spent");
|
|
jaddnum(retjson,"spentheight",spentheight);
|
|
jaddnum(retjson,"unspentind",unspentind);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
bits256 iguana_messagehash2(char *message,char *messagemagic)
|
|
{
|
|
int32_t n,len; uint8_t *messagebuf; bits256 hash2;
|
|
n = (int32_t)strlen(message) >> 1;
|
|
len = (int32_t)strlen(messagemagic);
|
|
if ( message[0] == '0' && message[1] == 'x' && is_hexstr(message+2,n-2) > 0 )
|
|
{
|
|
messagebuf = malloc(n-2 + len);
|
|
memcpy(messagebuf,messagemagic,len);
|
|
decode_hex(messagebuf+len,n-2,message+2);
|
|
n--;
|
|
}
|
|
else
|
|
{
|
|
n <<= 1;
|
|
messagebuf = malloc(n + len + 1);
|
|
memcpy(messagebuf,messagemagic,len);
|
|
strcpy((void *)&messagebuf[len],message);
|
|
//printf("MESSAGE.(%s)\n",(void *)messagebuf);
|
|
}
|
|
n += len;
|
|
hash2 = bits256_doublesha256(0,messagebuf,n);
|
|
//for (i=0; i<sizeof(hash2); i++)
|
|
// revhash2.bytes[i] = hash2.bytes[sizeof(hash2) - 1 - i];
|
|
if ( messagebuf != (void *)message )
|
|
free(messagebuf);
|
|
return(hash2);
|
|
}
|
|
|
|
TWO_STRINGS(bitcoinrpc,signmessage,address,message)
|
|
{
|
|
bits256 privkey,hash2; int32_t len,siglen; char sigstr[256],sig65str[256]; uint8_t sig[128]; cJSON *retjson = cJSON_CreateObject();
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
if ( myinfo->expiration == 0 )
|
|
return(clonestr("{\"error\":\"need to unlock wallet\"}"));
|
|
if ( coin != 0 )
|
|
{
|
|
privkey = iguana_str2priv(myinfo,coin,address);
|
|
if ( bits256_nonz(privkey) != 0 )
|
|
{
|
|
hash2 = iguana_messagehash2(message,coin->chain->messagemagic);
|
|
if ( (siglen= bitcoin_sign(coin->ctx,coin->symbol,sig,hash2,privkey,1)) > 0 )
|
|
{
|
|
sigstr[0] = sig65str[0] = 0;
|
|
len = nn_base64_encode(sig,siglen,sig65str,sizeof(sig65str));
|
|
sig65str[len] = 0;
|
|
jaddstr(retjson,"result",sig65str);
|
|
}
|
|
} else jaddstr(retjson,"error","invalid address (can be wif, wallet address or privkey hex)");
|
|
}
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
THREE_STRINGS(bitcoinrpc,verifymessage,address,sig,message)
|
|
{
|
|
int32_t len,plen; uint8_t sigbuf[256],pubkey[65]; char str[4096]; bits256 hash2; cJSON *retjson = cJSON_CreateObject();
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
if ( strlen(sig) < sizeof(sigbuf)*8/6 )
|
|
{
|
|
len = (int32_t)strlen(sig);
|
|
len = nn_base64_decode(sig,len,sigbuf,sizeof(sigbuf));
|
|
//int32_t i; for (i=0; i<len; i++)
|
|
// printf("%02x",sigbuf[i]);
|
|
//printf(" siglen.%d [%d] address.(%s) sig.(%s) message.(%s)\n",len,sigbuf[0],address,sig,message);
|
|
hash2 = iguana_messagehash2(message,coin->chain->messagemagic);
|
|
if ( bitcoin_recoververify(myinfo->ctx,coin->symbol,sigbuf,hash2,pubkey,0) == 0 )
|
|
jadd(retjson,"result",jtrue());
|
|
else jadd(retjson,"result",jfalse());
|
|
jaddstr(retjson,"coin",coin->symbol);
|
|
jaddstr(retjson,"address",address);
|
|
jaddstr(retjson,"message",message);
|
|
if ( (plen= bitcoin_pubkeylen(pubkey)) > 0 )
|
|
{
|
|
init_hexbytes_noT(str,pubkey,plen);
|
|
jaddstr(retjson,"pubkey",str);
|
|
}
|
|
init_hexbytes_noT(str,sigbuf,len);
|
|
jaddstr(retjson,"sighex",str);
|
|
jaddbits256(retjson,"messagehash",hash2);
|
|
return(jprint(retjson,1));
|
|
} else return(clonestr("{\"error\":\"sig is too long\"}"));
|
|
}
|
|
|
|
int64_t iguana_txdetails(struct supernet_info *myinfo,struct iguana_info *coin,cJSON *item,bits256 txid,int32_t vout,int32_t height)
|
|
{
|
|
struct iguana_block *block; bits256 hash2; uint64_t amount = 0; char coinaddr[64],account[512];
|
|
/*{
|
|
"category": "receive",
|
|
"amount": 0.50000000,
|
|
"label": "",
|
|
"confirmations": 24466,
|
|
"blockhash": "00000000000000000517ce625737579f91162c46ad9eaccad0f52ca13715b156",
|
|
"blockindex": 78,
|
|
"blocktime": 1448045745,
|
|
}*/
|
|
jaddbits256(item,"txid",txid);
|
|
if ( vout >= 0 )
|
|
{
|
|
jaddnum(item,"vout",vout);
|
|
if ( (amount= iguana_txidamount(myinfo,coin,coinaddr,txid,vout)) != 0 )
|
|
jaddnum(item,"amount",dstr(amount));
|
|
jaddstr(item,"category",iguana_txidcategory(myinfo,coin,account,coinaddr,txid,vout));
|
|
}
|
|
else
|
|
{
|
|
if ( vout == -1 )
|
|
jadd(item,"coinbase",jtrue());
|
|
vout = 0;
|
|
}
|
|
if ( account[0] != 0 && jobj(item,"account") == 0 )
|
|
jaddstr(item,"account",account);
|
|
if ( coinaddr[0] != 0 )
|
|
jaddstr(item,"address",coinaddr);
|
|
hash2 = iguana_blockhash(coin,height);
|
|
jaddbits256(item,"blockhash",hash2);
|
|
if ( (block= iguana_blockfind("rawtx",coin,hash2)) != 0 )
|
|
jaddnum(item,"blocktime",block->RO.timestamp);
|
|
jaddnum(item,"height",height);
|
|
jaddnum(item,"confirmations",coin->blocks.hwmchain.height - height);
|
|
return(amount);
|
|
}
|
|
|
|
HASH_AND_INT(bitcoinrpc,getrawtransaction,txid,verbose)
|
|
{
|
|
struct iguana_txid *tx,T; char *txbytes; bits256 checktxid; int32_t len=0,height,extralen=65536; cJSON *retjson,*txobj; uint8_t *extraspace; struct iguana_RTtxid *RTptr;
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
HASH_FIND(hh,coin->RTdataset,txid.bytes,sizeof(txid),RTptr);
|
|
if ( RTptr != 0 && RTptr->rawtxbytes != 0 && RTptr->txlen > 0 )
|
|
{
|
|
checktxid = RTptr->txid;
|
|
height = RTptr->height;
|
|
len = RTptr->txlen;
|
|
memcpy(coin->blockspace,RTptr->rawtxbytes,len);
|
|
}
|
|
else if ( (tx= iguana_txidfind(coin,&height,&T,txid,coin->bundlescount-1)) != 0 )
|
|
{
|
|
len = iguana_ramtxbytes(coin,coin->blockspace,coin->blockspacesize,&checktxid,tx,height,0,0,0);
|
|
}
|
|
retjson = cJSON_CreateObject();
|
|
if ( len > 0 )
|
|
{
|
|
txbytes = calloc(1,len*2+1);
|
|
init_hexbytes_noT(txbytes,coin->blockspace,len);
|
|
if ( verbose != 0 )
|
|
{
|
|
extraspace = calloc(1,extralen);
|
|
txobj = bitcoin_hex2json(coin,coin->blocks.hwmchain.height,&checktxid,0,txbytes,extraspace,extralen,0,0,0);
|
|
free(extraspace);
|
|
free(txbytes);
|
|
if ( txobj != 0 )
|
|
{
|
|
iguana_txdetails(myinfo,coin,txobj,checktxid,-2,height);
|
|
return(jprint(txobj,1));
|
|
}
|
|
}
|
|
jaddstr(retjson,"result",txbytes);
|
|
char str[65]; printf("txbytes.(%s) len.%d (%s) %s\n",txbytes,len,jprint(retjson,0),bits256_str(str,checktxid));
|
|
free(txbytes);
|
|
return(jprint(retjson,1));
|
|
}
|
|
else if ( height >= 0 )
|
|
{
|
|
if ( coin->APIblockstr != 0 )
|
|
jaddstr(retjson,"error","already have pending request");
|
|
else
|
|
{
|
|
int32_t datalen; uint8_t *data; char *blockstr; bits256 blockhash;
|
|
blockhash = iguana_blockhash(coin,height);
|
|
if ( (blockstr= iguana_APIrequest(coin,blockhash,txid,2)) != 0 )
|
|
{
|
|
datalen = (int32_t)(strlen(blockstr) >> 1);
|
|
data = malloc(datalen);
|
|
decode_hex(data,datalen,blockstr);
|
|
if ( (txbytes= iguana_txscan(myinfo,coin,verbose != 0 ? retjson : 0,data,datalen,txid)) != 0 )
|
|
{
|
|
jaddstr(retjson,"result",txbytes);
|
|
jaddbits256(retjson,"blockhash",blockhash);
|
|
jaddnum(retjson,"height",height);
|
|
free(txbytes);
|
|
}
|
|
else if ( coin->RTheight > 0 )
|
|
jaddstr(retjson,"error","cant find txid in block");
|
|
else jaddstr(retjson,"error","not in realtime mode yet");
|
|
free(blockstr);
|
|
free(data);
|
|
} else jaddstr(retjson,"error","cant find blockhash");
|
|
return(jprint(retjson,1));
|
|
}
|
|
}
|
|
if ( coin->RTheight > 0 )
|
|
return(clonestr("{\"error\":\"cant find txid\"}"));
|
|
else return(clonestr("{\"error\":\"not in realtime mode yet\"}"));
|
|
}
|
|
|
|
int64_t iguana_lockval(int32_t finalized,int64_t locktime)
|
|
{
|
|
int64_t lockval = -1;
|
|
if ( finalized == 0 )
|
|
return(locktime);
|
|
return(lockval);
|
|
}
|
|
|
|
char *iguana_validaterawtx(struct supernet_info *myinfo,struct iguana_info *coin,int32_t height,struct iguana_msgtx *msgtx,uint8_t *extraspace,int32_t extralen,char *rawtx,int32_t mempool,int32_t suppress_pubkeys)
|
|
{
|
|
bits256 signedtxid,txid; struct iguana_outpoint outpt; struct iguana_msgvin vin; cJSON *log,*vins,*vouts,*txobj,*retjson; char *checkstr,*signedtx; int32_t plen,finalized = 1,i,len,maxsize,numinputs,numoutputs,complete; struct vin_info *V; uint8_t *serialized,*serialized2; uint32_t sigsize,pubkeysize,p2shsize,suffixlen; int64_t inputsum,outputsum,lockval;
|
|
retjson = cJSON_CreateObject();
|
|
inputsum = outputsum = numinputs = numoutputs = 0;
|
|
if ( rawtx != 0 && rawtx[0] != 0 && coin != 0 )
|
|
{
|
|
if ( (strlen(rawtx) & 1) != 0 )
|
|
return(clonestr("{\"error\":\"rawtx hex has odd length\"}"));
|
|
memset(msgtx,0,sizeof(*msgtx));
|
|
if ( (txobj= bitcoin_hex2json(coin,coin->blocks.hwmchain.height,&msgtx->txid,msgtx,rawtx,extraspace,extralen,0,0,suppress_pubkeys)) != 0 )
|
|
{
|
|
//printf("txobj.(%s)\n",jprint(txobj,0));
|
|
if ( 0 && (checkstr= bitcoin_json2hex(myinfo,coin,&txid,txobj,0)) != 0 )
|
|
{
|
|
// no guarantee byte for byte identical tx is recreated
|
|
if ( strcmp(rawtx,checkstr) != 0 )
|
|
{
|
|
jaddstr(retjson,"error","converting from hex2json and json2hex mismatch");
|
|
jaddstr(retjson,"original",rawtx);
|
|
jaddstr(retjson,"checkstr",checkstr);
|
|
for (i=0; rawtx[i]!=0 && checkstr[i]!=0; i++)
|
|
if ( rawtx[i] != checkstr[i] )
|
|
break;
|
|
jaddnum(retjson,"mismatch position",i);
|
|
jadd(retjson,"origtx",txobj);
|
|
if ( 0 && (txobj= bitcoin_hex2json(coin,coin->blocks.hwmchain.height,&txid,msgtx,checkstr,extraspace,extralen,0,0,suppress_pubkeys)) != 0 )
|
|
jadd(retjson,"checktx",txobj);
|
|
free(checkstr);
|
|
return(jprint(retjson,1));
|
|
}
|
|
free(checkstr);
|
|
}
|
|
if ( (vouts= jarray(&numoutputs,txobj,"vout")) > 0 )
|
|
{
|
|
struct iguana_msgvout vout; uint8_t voutdata[IGUANA_MAXSCRIPTSIZE];
|
|
for (i=0; i<numoutputs; i++)
|
|
{
|
|
if ( iguana_parsevoutobj(coin,voutdata,sizeof(voutdata),&vout,jitem(vouts,i)) > 0 )
|
|
outputsum += vout.value;
|
|
}
|
|
}
|
|
if ( (vins= jarray(&numinputs,txobj,"vin")) > 0 )
|
|
{
|
|
maxsize = (int32_t)strlen(rawtx);
|
|
serialized = malloc(maxsize);
|
|
serialized2 = malloc(maxsize);
|
|
len = 0;
|
|
V = calloc(numinputs,sizeof(*V));
|
|
for (i=0; i<numinputs; i++)
|
|
{
|
|
len += iguana_parsevinobj(myinfo,coin,&serialized[len],maxsize-len,&vin,jitem(vins,i),&V[i]);
|
|
if ( iguana_RTunspentindfind(myinfo,coin,&outpt,V[i].coinaddr,V[i].spendscript,&V[i].spendlen,&V[i].amount,&V[i].height,msgtx->vins[i].prev_hash,msgtx->vins[i].prev_vout,coin->bundlescount-1,mempool) == 0 )
|
|
{
|
|
V[i].suppress_pubkeys = suppress_pubkeys;
|
|
V[i].unspentind = outpt.unspentind;
|
|
inputsum += V[i].amount;
|
|
msgtx->vins[i].spendscript = V[i].spendscript;
|
|
if ( (msgtx->vins[i].spendlen= V[i].spendlen) == 35 )
|
|
{
|
|
if ( (plen= bitcoin_pubkeylen(msgtx->vins[i].spendscript+1)) > 0 )
|
|
{
|
|
memcpy(V[i].signers[0].pubkey,msgtx->vins[i].spendscript+1,plen);
|
|
V[i].suppress_pubkeys = 1;
|
|
}
|
|
}
|
|
V[i].hashtype = iguana_vinscriptparse(coin,&V[i],&sigsize,&pubkeysize,&p2shsize,&suffixlen,msgtx->vins[i].vinscript,msgtx->vins[i].scriptlen);
|
|
//if ( (V[i].signers[0].siglen= sigsize) > 0 )
|
|
// memcpy(V[i].signers[0].sig,msgtx->vins[i].vinscript+1,sigsize);
|
|
V[i].userdatalen = suffixlen;
|
|
memcpy(V[i].spendscript,msgtx->vins[i].spendscript,msgtx->vins[i].spendlen);
|
|
V[i].spendlen = msgtx->vins[i].spendlen;
|
|
if ( msgtx->vins[i].sequence < IGUANA_SEQUENCEID_FINAL )
|
|
finalized = 0;
|
|
if ( V[i].M == 0 )
|
|
V[i].M = 1;
|
|
if ( V[i].N < V[i].M )
|
|
V[i].N = V[i].M;
|
|
//printf("V %dof%d %.8f (%s) spendscript.[%d] scriptlen.%d\n",V[i].M,V[i].N,dstr(V[i].amount),V[i].coinaddr,V[i].spendlen,V[i].spendlen);
|
|
} else printf("couldnt find spendscript\n");
|
|
}
|
|
complete = 0;
|
|
bitcoin_verifyvins(coin,height,&signedtxid,&signedtx,msgtx,serialized2,maxsize,V,1,0,suppress_pubkeys);
|
|
msgtx->txid = signedtxid;
|
|
log = cJSON_CreateArray();
|
|
lockval = iguana_lockval(finalized,jint(txobj,"locktime"));
|
|
if ( iguana_interpreter(coin,log,lockval,V,numinputs) < 0 )
|
|
jaddstr(retjson,"error","interpreter rejects tx");
|
|
else complete = 1;
|
|
jadd(retjson,"interpreter",log);
|
|
jaddnum(retjson,"complete",complete);
|
|
free(serialized), free(serialized2);
|
|
if ( signedtx != 0 )
|
|
free(signedtx);
|
|
}
|
|
}
|
|
//char str[65]; printf("got txid.(%s)\n",bits256_str(str,txid));
|
|
}
|
|
msgtx->inputsum = inputsum;
|
|
msgtx->numinputs = numinputs;
|
|
msgtx->outputsum = outputsum;
|
|
msgtx->numoutputs = numoutputs;
|
|
msgtx->txfee = (inputsum - outputsum);
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
STRING_AND_INT(bitcoinrpc,validaterawtransaction,rawtx,suppress)
|
|
{
|
|
uint8_t *extraspace; int32_t extralen=65536; char *retstr; struct iguana_msgtx msgtx;
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
extraspace = calloc(1,extralen);
|
|
retstr = iguana_validaterawtx(myinfo,coin,coin->blocks.hwmchain.height,&msgtx,extraspace,extralen,rawtx,0,suppress);
|
|
free(extraspace);
|
|
return(rawtx);
|
|
}
|
|
|
|
int32_t iguana_validatesigs(struct supernet_info *myinfo,struct iguana_info *coin,uint8_t *serialized,int32_t datalen)
|
|
{
|
|
uint8_t *extraspace; cJSON *retjson; int32_t extralen=65536; char *retstr,*rawtx; struct iguana_msgtx msgtx; int32_t suppress=0,retval = -1;
|
|
rawtx = calloc(1,datalen*2 + 1);
|
|
init_hexbytes_noT(rawtx,serialized,datalen);
|
|
extraspace = calloc(1,extralen);
|
|
for (suppress=0; suppress<1; suppress++)
|
|
{
|
|
if ( (retstr= iguana_validaterawtx(myinfo,coin,coin->blocks.hwmchain.height,&msgtx,extraspace,extralen,rawtx,0,suppress)) != 0 )
|
|
{
|
|
if ( (retjson= cJSON_Parse(retstr)) != 0 )
|
|
{
|
|
if ( jobj(retjson,"error") == 0 )
|
|
{
|
|
retval = 0;
|
|
//char str[65]; printf("%s %s sigs validated\n",coin->symbol,bits256_str(str,msgtx.txid));
|
|
coin->sigsvalidated++;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
printf("ERROR.(%s)\n",retstr);
|
|
coin->sigserrs++;
|
|
}
|
|
free_json(retjson);
|
|
}
|
|
free(retstr);
|
|
}
|
|
}
|
|
free(rawtx);
|
|
free(extraspace);
|
|
return(retval);
|
|
}
|
|
|
|
STRING_AND_INT(bitcoinrpc,decoderawtransaction,rawtx,suppress)
|
|
{
|
|
cJSON *txobj = 0; bits256 txid; uint8_t *extraspace; int32_t extralen = 65536;
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
if ( rawtx != 0 && rawtx[0] != 0 )
|
|
{
|
|
if ( (strlen(rawtx) & 1) != 0 )
|
|
return(clonestr("{\"error\":\"rawtx hex has odd length\"}"));
|
|
extraspace = calloc(1,extralen);
|
|
txobj = bitcoin_hex2json(coin,coin->blocks.hwmchain.height,&txid,0,rawtx,extraspace,extralen,0,0,suppress);
|
|
free(extraspace);
|
|
//char str[65]; printf("got txid.(%s)\n",bits256_str(str,txid));
|
|
}
|
|
if ( txobj == 0 )
|
|
txobj = cJSON_CreateObject();
|
|
return(jprint(txobj,1));
|
|
}
|
|
|
|
HASH_ARG(bitcoinrpc,gettransaction,txid)
|
|
{
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
return(bitcoinrpc_getrawtransaction(IGUANA_CALLARGS,txid,1));
|
|
}
|
|
|
|
cJSON *iguana_createvins(struct supernet_info *myinfo,struct iguana_info *coin,cJSON *txobj,cJSON *vins)
|
|
{
|
|
int32_t i,n,vout,p2shlen=0,spendlen=0,height; uint64_t satoshis; char coinaddr[128],pubkeystr[256],scriptstr[IGUANA_MAXSCRIPTSIZE*2],*str,*hexstr; cJSON *pubkeys,*item,*obj,*newvin,*newvins; uint32_t sequenceid; bits256 txid; uint8_t spendscript[IGUANA_MAXSCRIPTSIZE],redeemscript[IGUANA_MAXSCRIPTSIZE]; struct iguana_waccount *wacct; struct iguana_waddress *waddr; struct iguana_outpoint outpt;
|
|
newvins = cJSON_CreateArray();
|
|
if ( (n= cJSON_GetArraySize(vins)) > 0 )
|
|
{
|
|
for (i=0; i<n; i++)
|
|
{
|
|
pubkeys = cJSON_CreateArray();
|
|
newvin = cJSON_CreateObject();
|
|
item = jitem(vins,i);
|
|
txid = jbits256(item,"txid");
|
|
vout = jint(item,"vout");
|
|
jaddbits256(newvin,"txid",txid);
|
|
jaddnum(newvin,"vout",vout);
|
|
p2shlen = spendlen = 0;
|
|
if ( ((str= jstr(item,"scriptPub")) != 0 || (str= jstr(item,"scriptPubKey")) != 0) && is_hexstr(str,(int32_t)strlen(str)) > 0 )
|
|
{
|
|
spendlen = (int32_t)strlen(str) >> 1;
|
|
decode_hex(spendscript,spendlen,str);
|
|
}
|
|
else if ( ((obj= jobj(item,"scriptPub")) != 0 || (obj= jobj(item,"scriptPubKey")) != 0) && (hexstr= jstr(obj,"hex")) != 0 )
|
|
{
|
|
spendlen = (int32_t)strlen(hexstr) >> 1;
|
|
decode_hex(spendscript,spendlen,hexstr);
|
|
}
|
|
if ( iguana_RTunspentindfind(myinfo,coin,&outpt,coinaddr,spendscript,&spendlen,&satoshis,&height,txid,vout,coin->bundlescount-1,0) == 0 )
|
|
{
|
|
//printf("[%d] unspentind.%d (%s) spendlen.%d %.8f\n",height/coin->chain->bundlesize,unspentind,coinaddr,spendlen,dstr(satoshis));
|
|
if ( coinaddr[0] != 0 && (waddr= iguana_waddresssearch(myinfo,&wacct,coinaddr)) != 0 )
|
|
{
|
|
init_hexbytes_noT(pubkeystr,waddr->pubkey,bitcoin_pubkeylen(waddr->pubkey));
|
|
jaddistr(pubkeys,pubkeystr);
|
|
}
|
|
}
|
|
if ( spendlen > 0 )
|
|
{
|
|
init_hexbytes_noT(scriptstr,spendscript,spendlen);
|
|
jaddstr(newvin,"scriptPubKey",scriptstr);
|
|
}
|
|
if ( (str= jstr(item,"redeemScript")) != 0 )
|
|
{
|
|
p2shlen = (int32_t)strlen(str) >> 1;
|
|
decode_hex(redeemscript,p2shlen,str);
|
|
init_hexbytes_noT(scriptstr,redeemscript,p2shlen);
|
|
jaddstr(newvin,"redeemScript",scriptstr);
|
|
}
|
|
if ( jint(txobj,"locktime") > 0 )
|
|
sequenceid = (uint32_t)time(NULL); // any value < 0xfffffffe should be fine
|
|
else
|
|
{
|
|
if ( jobj(item,"sequence") != 0 )
|
|
sequenceid = juint(item,"sequence");
|
|
else sequenceid = 0xffffffff;
|
|
}
|
|
jaddnum(newvin,"sequence",sequenceid);
|
|
bitcoin_txinput(coin,txobj,txid,vout,sequenceid,spendscript,spendlen,redeemscript,p2shlen,0,0,0,0);
|
|
jadd(newvin,"pubkeys",pubkeys);
|
|
jaddi(newvins,newvin);
|
|
}
|
|
}
|
|
return(newvins);
|
|
}
|
|
|
|
ARRAY_OBJ_INT(bitcoinrpc,createrawtransaction,vins,vouts,locktime)
|
|
{
|
|
bits256 txid; int32_t offset,spendlen=0,n; uint8_t addrtype,rmd160[20],spendscript[IGUANA_MAXSCRIPTSIZE]; uint64_t satoshis; char *hexstr,*field,*txstr; cJSON *txobj,*item,*obj,*retjson = cJSON_CreateObject();
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
if ( coin != 0 && (txobj= bitcoin_txcreate(coin->symbol,coin->chain->isPoS,locktime,1,0)) != 0 )
|
|
{
|
|
iguana_createvins(myinfo,coin,txobj,vins);
|
|
if ( (n= cJSON_GetArraySize(vouts)) > 0 )
|
|
{
|
|
if ( is_cJSON_Array(vouts) != 0 && n == 1 && (item= jitem(vouts,0)) != 0 )
|
|
item = item->child;
|
|
else item = vouts->child;
|
|
while ( item != 0 )
|
|
{
|
|
if ( (field= jfieldname(item)) != 0 )
|
|
{
|
|
if ( strcmp(field,"data") == 0 )
|
|
{
|
|
if ( (hexstr= jstr(item,0)) != 0 )
|
|
{
|
|
spendlen = (int32_t)strlen(hexstr) >> 1;
|
|
offset = 0;
|
|
if ( is_hexstr(hexstr,spendlen) > 0 )
|
|
{
|
|
decode_hex(spendscript+4,spendlen,hexstr);
|
|
spendscript[3] = SCRIPT_OPRETURN;
|
|
spendlen++;
|
|
/* 1-75 0x01-0x4b (special) data The next opcode bytes is data to be pushed onto the stack
|
|
OP_PUSHDATA1 76 0x4c (special) data The next byte contains the number of bytes to be pushed onto the stack.
|
|
OP_PUSHDATA2 77 0x4d*/
|
|
if ( spendlen < 76 )
|
|
{
|
|
spendscript[2] = spendlen;
|
|
offset = 2;
|
|
spendlen++;
|
|
}
|
|
else if ( spendlen <= 0xff )
|
|
{
|
|
spendscript[2] = spendlen;
|
|
spendscript[1] = 0x4c;
|
|
offset = 1;
|
|
spendlen += 2;
|
|
}
|
|
else if ( spendlen <= 0xffff )
|
|
{
|
|
spendscript[2] = ((spendlen >> 8) & 0xff);
|
|
spendscript[1] = (spendlen & 0xff);
|
|
spendscript[0] = 0x4d;
|
|
offset = 0;
|
|
spendlen += 3;
|
|
}
|
|
else continue;
|
|
if ( (obj= jobj(item,"amount")) != 0 )
|
|
satoshis = jdouble(obj,0) * SATOSHIDEN;
|
|
else satoshis = 0;
|
|
bitcoin_txoutput(txobj,spendscript+offset,spendlen,satoshis);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
if ( bitcoin_addr2rmd160(&addrtype,rmd160,field) == sizeof(rmd160) )
|
|
{
|
|
spendlen = bitcoin_standardspend(spendscript,0,rmd160);
|
|
satoshis = jdouble(item,0) * SATOSHIDEN;
|
|
bitcoin_txoutput(txobj,spendscript,spendlen,satoshis);
|
|
}
|
|
}
|
|
}
|
|
item = item->next;
|
|
}
|
|
}
|
|
if ( (txstr= bitcoin_json2hex(myinfo,coin,&txid,txobj,0)) != 0 )
|
|
{
|
|
jaddstr(retjson,"result",txstr);
|
|
free(txstr);
|
|
}
|
|
}
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
cJSON *iguana_listunspents(struct supernet_info *myinfo,struct iguana_info *coin,cJSON *array,int32_t minconf,int32_t maxconf,char *remoteaddr)
|
|
{
|
|
cJSON *retjson; int32_t flag = 0;
|
|
if ( array == 0 || is_cJSON_Array(array) == 0 || cJSON_GetArraySize(array) <= 0 )
|
|
{
|
|
array = iguana_getaddressesbyaccount(myinfo,coin,"*");
|
|
flag = 1;
|
|
//printf("listunspent.(%s)\n",jprint(array,0));
|
|
}
|
|
if ( minconf == 0 )
|
|
minconf = 1;
|
|
if ( maxconf == 0 )
|
|
maxconf = (1 << 30);
|
|
retjson = iguana_RTlistunspent(myinfo,coin,array,minconf,maxconf,remoteaddr,0);
|
|
if ( array != 0 && flag != 0 )
|
|
free_json(array);
|
|
return(retjson);
|
|
}
|
|
|
|
TWOINTS_AND_ARRAY(bitcoinrpc,listunspent,minconf,maxconf,array)
|
|
{
|
|
//int32_t numrmds,numunspents=0; uint8_t *rmdarray; cJSON *retjson = cJSON_CreateArray();
|
|
cJSON *retjson;
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
retjson = iguana_listunspents(myinfo,coin,array,minconf,maxconf,remoteaddr);
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
ZERO_ARGS(bitcoinrpc,getrawchangeaddress)
|
|
{
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
cJSON *retjson = cJSON_CreateObject();
|
|
jaddstr(retjson,"result",coin->changeaddr);
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
INT_AND_ARRAY(bitcoinrpc,lockunspent,flag,array)
|
|
{
|
|
struct iguana_outpoint outpt; int32_t RTspendflag,vout,i,n,height,spentheight,lockedflag; cJSON *item,*retjson; bits256 txid;
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
retjson = cJSON_CreateObject();
|
|
if ( array != 0 && (n= cJSON_GetArraySize(array)) > 0 )
|
|
{
|
|
for (i=0; i<n; i++)
|
|
{
|
|
item = jitem(array,i);
|
|
if ( jobj(item,"txid") != 0 && jobj(item,"vout") != 0 )
|
|
{
|
|
txid = jbits256(item,"txid");
|
|
vout = jint(item,"vout");
|
|
if ( iguana_RTunspentindfind(myinfo,coin,&outpt,0,0,0,0,&height,txid,vout,coin->bundlescount-1,0) == 0 )
|
|
{
|
|
//outpt.hdrsi = height / coin->chain->bundlesize;
|
|
//outpt.unspentind = unspentind;
|
|
iguana_RTutxofunc(coin,&spentheight,&lockedflag,outpt,&RTspendflag,!flag,0);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
ZERO_ARGS(bitcoinrpc,listlockunspent)
|
|
{
|
|
cJSON *array,*retjson; //int32_t vout; //struct iguana_outpoint outpt;
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
array = cJSON_CreateArray();
|
|
retjson = cJSON_CreateObject();
|
|
printf("need to port listlockunspent to new RT method\n");
|
|
jaddstr(retjson,"error","need to port listlockunspent to new RT method");
|
|
/*if ( coin->utxotable != 0 )
|
|
{
|
|
HASH_ITER(hh,coin->utxotable,hhutxo,tmputxo)
|
|
{
|
|
item = cJSON_CreateObject();
|
|
//if ( (vout= iguana_RTuvaltxid(myinfo,&txid,coin,hhutxo->outpt)) >= 0 )
|
|
{
|
|
jaddbits256(item,"txid",txid);
|
|
jaddnum(item,"vout",vout);
|
|
jaddi(array,item);
|
|
}
|
|
}
|
|
}*/
|
|
jadd(retjson,"result",array);
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
DOUBLE_ARG(bitcoinrpc,settxfee,amount)
|
|
{
|
|
cJSON *retjson;
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
if ( myinfo->expiration == 0 )
|
|
return(clonestr("{\"error\":\"need to unlock wallet\"}"));
|
|
myinfo->expiration++;
|
|
coin->txfee_perkb = amount * SATOSHIDEN;
|
|
retjson = cJSON_CreateObject();
|
|
jadd(retjson,"result",jtrue());
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
S_D_SS(bitcoinrpc,sendtoaddress,address,amount,comment,comment2)
|
|
{
|
|
char *retstr;
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
if ( myinfo->expiration == 0 )
|
|
return(clonestr("{\"error\":\"need to unlock wallet\"}"));
|
|
myinfo->expiration++;
|
|
//iguana_unspentset(myinfo,coin);
|
|
if ( (retstr= sendtoaddress(myinfo,coin,remoteaddr,address,amount * SATOSHIDEN,coin->txfee,comment,comment2,coin->minconfirms,0)) != 0 )
|
|
printf("SEND.(%s)\n",retstr);
|
|
return(retstr);
|
|
}
|
|
|
|
SS_D_I_SS(bitcoinrpc,sendfrom,fromaccount,toaddress,amount,minconf,comment,comment2)
|
|
{
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
if ( myinfo->expiration == 0 )
|
|
return(clonestr("{\"error\":\"need to unlock wallet\"}"));
|
|
myinfo->expiration++;
|
|
//iguana_unspentset(myinfo,coin);
|
|
return(sendtoaddress(myinfo,coin,remoteaddr,toaddress,amount * SATOSHIDEN,coin->txfee,comment,comment2,minconf,fromaccount));
|
|
}
|
|
|
|
S_A_I_S(bitcoinrpc,sendmany,fromaccount,payments,minconf,comment)
|
|
{
|
|
cJSON *retjson,*item; int32_t i,n; char *coinaddr,*str; int64_t required,val; double amount;
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
if ( myinfo->expiration == 0 )
|
|
return(clonestr("{\"error\":\"need to unlock wallet\"}"));
|
|
myinfo->expiration++;
|
|
//iguana_unspentset(myinfo,coin);
|
|
n = cJSON_GetArraySize(payments);
|
|
item = payments->child;
|
|
retjson = cJSON_CreateArray();
|
|
for (required=i=0; i<n; i++)
|
|
{
|
|
if ( item != 0 && (coinaddr= item->string) != 0 )
|
|
{
|
|
amount = jdouble(item,0);
|
|
val = amount * SATOSHIDEN;
|
|
printf("(%s %.8f) ",coinaddr,dstr(val));
|
|
if ( (str= sendtoaddress(myinfo,coin,remoteaddr,coinaddr,val,coin->txfee,comment,"",minconf,fromaccount)) != 0 )
|
|
{
|
|
jaddistr(retjson,str);
|
|
}
|
|
required += val;
|
|
}
|
|
item = item->next;
|
|
}
|
|
printf("required %.8f\n",dstr(required));
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
THREE_INTS(iguana,splitfunds,satoshis,duplicates,sendflag)
|
|
{
|
|
char *rawtx; uint8_t pubkey33[33]; int32_t completed; cJSON *retjson,*addresses; bits256 signedtxid;
|
|
if ( remoteaddr != 0 )
|
|
return(clonestr("{\"error\":\"no remote\"}"));
|
|
if ( myinfo->expiration == 0 )
|
|
return(clonestr("{\"error\":\"need to unlock wallet\"}"));
|
|
if ( coin == 0 )
|
|
return(clonestr("{\"error\":\"need active coin\"}"));
|
|
retjson = cJSON_CreateObject();
|
|
bitcoin_pubkey33(myinfo->ctx,pubkey33,myinfo->persistent_priv);
|
|
addresses = iguana_getaddressesbyaccount(myinfo,coin,"*");
|
|
if ( (rawtx= iguana_utxoduplicates(myinfo,coin,pubkey33,satoshis,duplicates,&completed,&signedtxid,sendflag,addresses)) != 0 )
|
|
{
|
|
jaddstr(retjson,"result",rawtx);
|
|
jaddbits256(retjson,"txid",signedtxid);
|
|
jadd(retjson,"completed",completed != 0 ? jtrue() : jfalse());
|
|
free(rawtx);
|
|
} else jaddstr(retjson,"error","couldnt create duplicates tx");
|
|
if ( addresses != 0 )
|
|
free_json(addresses);
|
|
return(jprint(retjson,1));
|
|
}
|
|
|
|
#include "../includes/iguana_apiundefs.h"
|
|
|