Browse Source

kmd_lookup

etomic
jl777 8 years ago
parent
commit
222be34db3
  1. 1
      basilisk/basilisk.c
  2. 60
      basilisk/basilisk_bitcoin.c
  3. 4
      iguana/dPoW.h
  4. 1
      iguana/iguana_init.c
  5. 21
      iguana/iguana_notary.c
  6. 426
      iguana/kmd_lookup.h
  7. 2
      includes/iguana_apideclares.h
  8. 1
      includes/iguana_structs.h

1
basilisk/basilisk.c

@ -908,6 +908,7 @@ void basilisks_loop(void *arg)
}
endmilli = startmilli + 30;
}
kmd_bitcoinscan();
}
else
{

60
basilisk/basilisk_bitcoin.c

@ -22,36 +22,6 @@
};*/
#ifdef bitcoincancalculatebalances
int64_t bitcoin_value(struct iguana_info *coin,bits256 txid,int16_t vout,char *coinaddr)
{
char params[512],str[65]; char *curlstr; cJSON *txobj,*vouts,*item,*sobj,*addrs; int32_t j,m,n; int64_t value = 0;
sprintf(params,"[\"%s\", 1]",bits256_str(str,txid));
if ( (curlstr= bitcoind_passthru(coin->symbol,coin->chain->serverport,coin->chain->userpass,"getrawtransaction",params)) != 0 )
{
if ( (txobj= cJSON_Parse(curlstr)) != 0 )
{
if ( (vouts= jarray(&n,txobj,"vout")) != 0 && vout < n )
{
item = jitem(vouts,vout);
if ( (sobj= jobj(item,"scriptPubKey")) != 0 && (addrs= jarray(&m,sobj,"addresses")) != 0 )
{
for (j=0; j<m; j++)
{
if ( strcmp(jstri(addrs,j),coinaddr) == 0 )
{
value = SATOSHIDEN * jdouble(item,"satoshis");
break;
}
}
}
}
free_json(txobj);
}
free(curlstr);
}
return(value);
}
char *bitcoin_balance(struct iguana_info *coin,char *coinaddr,int32_t lastheight,int32_t minconf)
{
int32_t i,n,height,maxconf=1<<30; int64_t balance = 0; char params[512],*curlstr; cJSON *array,*retjson,*curljson;
@ -84,6 +54,36 @@ char *bitcoin_balance(struct iguana_info *coin,char *coinaddr,int32_t lastheight
return(jprint(retjson,1));
}
int64_t bitcoin_value(struct iguana_info *coin,bits256 txid,int16_t vout,char *coinaddr)
{
char params[512],str[65]; char *curlstr; cJSON *txobj,*vouts,*item,*sobj,*addrs; int32_t j,m,n; int64_t value = 0;
sprintf(params,"[\"%s\", 1]",bits256_str(str,txid));
if ( (curlstr= bitcoind_passthru(coin->symbol,coin->chain->serverport,coin->chain->userpass,"getrawtransaction",params)) != 0 )
{
if ( (txobj= cJSON_Parse(curlstr)) != 0 )
{
if ( (vouts= jarray(&n,txobj,"vout")) != 0 && vout < n )
{
item = jitem(vouts,vout);
if ( (sobj= jobj(item,"scriptPubKey")) != 0 && (addrs= jarray(&m,sobj,"addresses")) != 0 )
{
for (j=0; j<m; j++)
{
if ( strcmp(jstri(addrs,j),coinaddr) == 0 )
{
value = SATOSHIDEN * jdouble(item,"satoshis");
break;
}
}
}
}
free_json(txobj);
}
free(curlstr);
}
return(value);
}
char *basilisk_bitcoinblockhashstr(char *coinstr,char *serverport,char *userpass,int32_t height)
{
char numstr[128],*blockhashstr=0; bits256 hash2; struct iguana_info *coin;

4
iguana/dPoW.h

@ -189,5 +189,9 @@ int32_t komodo_notaries(char *symbol,uint8_t pubkeys[64][33],int32_t height);
cJSON *dpow_checkaddress(struct supernet_info *myinfo,struct iguana_info *coin,char *address);
void dex_channelsend(struct supernet_info *myinfo,bits256 srchash,bits256 desthash,uint32_t channel,uint32_t msgid,uint8_t *data,int32_t datalen);
void kmd_bitcoinscan();
struct iguana_info *iguana_coinfind(char *symbol);
cJSON *kmd_listtransactions(struct iguana_info *coin,char *coinaddr);
cJSON *kmd_listunspent(struct iguana_info *coin,char *coinaddr);
#endif

1
iguana/iguana_init.c

@ -77,6 +77,7 @@ void iguana_initcoin(struct iguana_info *coin,cJSON *argjson)
{
sprintf(dirname,"%s/%s",GLOBAL_TMPDIR,coin->symbol), OS_portable_path(dirname);
portable_mutex_init(&coin->RTmutex);
portable_mutex_init(&coin->kmdmutex);
portable_mutex_init(&coin->peers_mutex);
portable_mutex_init(&coin->blocks_mutex);
portable_mutex_init(&coin->special_mutex);

21
iguana/iguana_notary.c

@ -657,16 +657,6 @@ TWO_STRINGS(dex,validateaddress,symbol,address)
return(_dex_validateaddress(myinfo,symbol,address));
}
TWO_STRINGS(dex,listunspent,symbol,address)
{
return(_dex_listunspent(myinfo,symbol,address));
}
TWO_STRINGS_AND_TWO_DOUBLES(dex,listtransactions,symbol,address,count,skip)
{
return(_dex_listtransactions(myinfo,symbol,address,count,skip));
}
STRING_ARG(dex,getnotaries,symbol)
{
return(_dex_getnotaries(myinfo,symbol));
@ -698,6 +688,17 @@ THREE_STRINGS_AND_THREE_INTS(dex,kvupdate,symbol,key,value,flags,unused,unusedb)
} else return(clonestr("{\"error\":\"free updates only on KV chain\"}"));
}
#include "kmd_lookup.h"
TWO_STRINGS(dex,listunspent,symbol,address)
{
return(_dex_listunspent(myinfo,symbol,address));
}
TWO_STRINGS_AND_TWO_DOUBLES(dex,listtransactions,symbol,address,count,skip)
{
return(_dex_listtransactions(myinfo,symbol,address,count,skip));
}
#include "../includes/iguana_apiundefs.h"

426
iguana/kmd_lookup.h

@ -0,0 +1,426 @@
/******************************************************************************
* 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. *
* *
******************************************************************************/
#ifndef INCLUDE_KMDLOOKUP_H
#define INCLUDE_KMDLOOKUP_H
struct kmd_voutinfo
{
bits256 spendtxid;
uint64_t amount;
uint16_t spendvini;
uint8_t type_rmd160[21], pad;
} PACKED;
struct kmd_transaction
{
bits256 txid; int32_t height,numvouts; uint32_t timestamp,pad;
struct kmd_voutinfo vouts[];
};
struct kmd_transactionhh
{
UT_hash_handle hh;
struct kmd_transaction *tx;
int32_t numvouts;
struct kmd_transactionhh *ptrs[];
};
struct kmd_addresshh
{
UT_hash_handle hh;
uint8_t type_rmd160[21], pad;
struct kmd_transactionhh *prev,*lastprev;
};
struct kmd_addresshh *_kmd_address(struct iguana_info *coin,uint8_t type_rmd160[21])
{
struct kmd_addresshh *addr;
portable_mutex_lock(&coin->kmdmutex);
HASH_FIND(hh,coin->kmd_addresses,type_rmd160,21,addr);
portable_mutex_unlock(&coin->kmdmutex);
return(addr);
}
struct kmd_addresshh *_kmd_addressadd(struct iguana_info *coin,uint8_t type_rmd160[21])
{
struct kmd_addresshh *addr;
addr = calloc(1,sizeof(*addr));
memcpy(addr->type_rmd160,type_rmd160,21);
portable_mutex_lock(&coin->kmdmutex);
{
char coinaddr[64];
bitcoin_address(coinaddr,type_rmd160[0],&type_rmd160[1],20);
printf("%s NEW ADDRESS.(%s)\n",coin->symbol,coinaddr);
}
HASH_ADD_KEYPTR(hh,coin->kmd_addresses,addr->type_rmd160,21,addr);
portable_mutex_unlock(&coin->kmdmutex);
return(addr);
}
struct kmd_addresshh *kmd_address(struct iguana_info *coin,char *coinaddr)
{
uint8_t type_rmd160[21];
bitcoin_addr2rmd160(&type_rmd160[0],&type_rmd160[1],coinaddr);
return(_kmd_address(coin,type_rmd160));
}
struct kmd_transactionhh *kmd_transaction(struct iguana_info *coin,bits256 txid)
{
struct kmd_transactionhh *tx;
portable_mutex_lock(&coin->kmdmutex);
HASH_FIND(hh,coin->kmd_transactions,txid.bytes,sizeof(txid),tx);
portable_mutex_unlock(&coin->kmdmutex);
return(tx);
}
int32_t kmd_transactionvin(struct iguana_info *coin,bits256 spendtxid,int32_t vini,bits256 txid,int32_t vout)
{
struct kmd_transactionhh *ptr,*spendptr;
if ( (ptr= kmd_transaction(coin,txid)) != 0 && vout < ptr->numvouts && (spendptr= kmd_transaction(coin,spendtxid)) != 0 )
{
ptr->ptrs[(vout<<1) + 1] = spendptr;
ptr->tx->vouts[vout].spendtxid = spendtxid;
ptr->tx->vouts[vout].spendvini = vini;
return(0);
}
return(-1);
}
void kmd_transactionvout(struct iguana_info *coin,struct kmd_transactionhh *ptr,int32_t vout,uint64_t amount,uint8_t type_rmd160[21],bits256 spendtxid,int32_t spendvini)
{
struct kmd_addresshh *addr; struct kmd_transaction *tx = 0;
if ( vout < ptr->numvouts && (tx= ptr->tx) != 0 )
{
tx->vouts[vout].spendtxid = spendtxid;
tx->vouts[vout].spendvini = spendvini;
tx->vouts[vout].amount = amount;
memcpy(tx->vouts[vout].type_rmd160,type_rmd160,21);
if ( coin->kmd_didinit != 0 && coin->kmd_txidfp != 0 )
fwrite(tx,1,sizeof(*tx) + tx->numvouts*sizeof(*tx->vouts),coin->kmd_txidfp);
if ( (addr= _kmd_address(coin,type_rmd160)) == 0 )
addr = _kmd_addressadd(coin,type_rmd160);
if ( addr != 0 )
{
if ( addr->prev != ptr )
{
ptr->ptrs[vout << 1] = addr->prev;
addr->lastprev = addr->prev;
addr->prev = ptr;
}
else
{
printf("tricky case same address in different vouts, make sure backlink is right\n");
ptr->ptrs[vout<<1] = addr->lastprev;
}
} else printf("kmd_transactionvout unexpected null addr\n");
} else printf("vout.%d wont fit into numvouts.[%d] or null tx.%p\n",vout,ptr->numvouts,tx);
}
struct kmd_transactionhh *kmd_transactionadd(struct iguana_info *coin,struct kmd_transaction *tx,int32_t numvouts)
{
struct kmd_transactionhh *ptr; char str[65];
if ( (ptr= kmd_transaction(coin,tx->txid)) == 0 )
{
ptr = calloc(1,sizeof(*ptr) + (sizeof(*ptr->ptrs)*numvouts*2));
ptr->numvouts = numvouts;
ptr->tx = tx;
portable_mutex_lock(&coin->kmdmutex);
char str[65]; printf("%s ht.%d u.%u NEW TXID.(%s) vouts.[%d]\n",coin->symbol,tx->height,tx->timestamp,bits256_str(str,tx->txid),numvouts);
HASH_ADD_KEYPTR(hh,coin->kmd_transactions,tx->txid.bytes,sizeof(tx->txid),ptr);
portable_mutex_unlock(&coin->kmdmutex);
} else printf("warning adding already existing txid %s\n",bits256_str(str,tx->txid));
return(ptr);
}
struct kmd_transaction *kmd_transactionalloc(bits256 txid,int32_t height,uint32_t timestamp,int32_t numvouts)
{
struct kmd_transaction *tx;
tx = calloc(1,sizeof(*tx) + sizeof(struct kmd_voutinfo)*numvouts);
tx->numvouts = numvouts;
tx->txid = txid;
tx->height = height;
tx->timestamp = timestamp;
return(tx);
}
void kmd_flushfiles(struct iguana_info *coin)
{
if ( coin->kmd_txidfp != 0 )
fflush(coin->kmd_txidfp);
}
FILE *kmd_txidinit(struct iguana_info *coin)
{
int32_t i; FILE *fp; char fname[1024]; struct kmd_transactionhh *ptr; struct kmd_transaction T,*tx; struct kmd_voutinfo V; long lastpos=0;
sprintf(fname,"%s/%s/TRANSACTIONS",GLOBAL_DBDIR,coin->symbol);
if ( (fp= fopen(fname,"rb+")) != 0 )
{
while ( fread(&T,1,sizeof(T),fp) == sizeof(T) )
{
if ( (tx= kmd_transactionalloc(T.txid,T.height,T.timestamp,T.numvouts)) != 0 )
{
if ( (ptr= kmd_transactionadd(coin,tx,T.numvouts)) != 0 )
{
for (i=0; i<T.numvouts; i++)
{
if ( fread(&V,1,sizeof(V),fp) == sizeof(V) )
{
kmd_transactionvout(coin,ptr,i,V.amount,V.type_rmd160,V.spendtxid,V.spendvini);
} else break;
}
if ( i == T.numvouts )
{
lastpos = ftell(fp);
if ( T.height > coin->kmd_height )
coin->kmd_height = T.height;
}
else break;
}
} else break;
}
fseek(fp,lastpos,SEEK_SET);
} else fp = fopen(fname,"wb");
return(fp);
}
cJSON *kmd_transactionjson(struct kmd_transactionhh *ptr,char *typestr)
{
int32_t i; char coinaddr[64]; cJSON *item,*array,*obj = cJSON_CreateObject();
array = cJSON_CreateArray();
jaddstr(obj,"type",typestr);
jaddbits256(obj,"txid",ptr->tx->txid);
jaddnum(obj,"height",ptr->tx->height);
jaddnum(obj,"timestamp",ptr->tx->timestamp);
for (i=0; i<ptr->numvouts; i++)
{
item = cJSON_CreateObject();
bitcoin_address(coinaddr,ptr->tx->vouts[i].type_rmd160[0],&ptr->tx->vouts[i].type_rmd160[1],20);
jaddnum(item,coinaddr,dstr(ptr->tx->vouts[i].amount));
jaddi(array,item);
}
jadd(obj,"vouts",array);
return(obj);
}
cJSON *kmd_unspentjson(struct kmd_transaction *tx,int32_t vout)
{
cJSON *item = cJSON_CreateObject();
jaddbits256(item,"txid",tx->txid);
jaddnum(item,"vout",vout);
jaddnum(item,"amount",dstr(tx->vouts[vout].amount));
return(item);
}
int32_t kmd_height(struct iguana_info *coin)
{
char params[64],*curlstr; cJSON *curljson; int32_t height = 0;
strcpy(params,"[]");
if ( (curlstr= bitcoind_passthru(coin->symbol,coin->chain->serverport,coin->chain->userpass,"getinfo",params)) != 0 )
{
if ( (curljson= cJSON_Parse(curlstr)) != 0 )
{
height = juint(curljson,"blocks");
free_json(curljson);
}
free(curlstr);
}
return(height);
}
cJSON *kmd_listtransactions(struct iguana_info *coin,char *coinaddr)
{
struct kmd_addresshh *addr; struct kmd_transactionhh *ptr,*spent; uint8_t type_rmd160[21]; int32_t i,height; cJSON *array = cJSON_CreateArray();
if ( (height= kmd_height(coin)) > coin->kmd_height+3 )
return(cJSON_Parse("[]"));
bitcoin_addr2rmd160(&type_rmd160[0],&type_rmd160[1],coinaddr);
if ( (addr= _kmd_address(coin,type_rmd160)) != 0 && (ptr= addr->prev) != 0 && ptr->tx != 0 )
{
jaddi(array,kmd_transactionjson(ptr,"received"));
for (i=0; i<ptr->numvouts; i++)
{
if ( memcmp(ptr->tx->vouts[i].type_rmd160,type_rmd160,21) == 0 && (spent= ptr->ptrs[(i<<1)+1]) != 0 )
jaddi(array,kmd_transactionjson(spent,"sent"));
}
}
return(array);
}
cJSON *kmd_listunspent(struct iguana_info *coin,char *coinaddr)
{
struct kmd_addresshh *addr; struct kmd_transactionhh *ptr,*spent; uint8_t type_rmd160[21]; int32_t i,height; cJSON *array = cJSON_CreateArray();
if ( (height= kmd_height(coin)) > coin->kmd_height+3 )
return(cJSON_Parse("[]"));
bitcoin_addr2rmd160(&type_rmd160[0],&type_rmd160[1],coinaddr);
if ( (addr= _kmd_address(coin,type_rmd160)) != 0 && (ptr= addr->prev) != 0 && ptr->tx != 0 )
{
for (i=0; i<ptr->numvouts; i++)
{
if ( memcmp(ptr->tx->vouts[i].type_rmd160,type_rmd160,21) == 0 && (spent= ptr->ptrs[(i<<1)+1]) == 0 )
jaddi(array,kmd_unspentjson(ptr->tx,i));
}
}
return(array);
}
char *kmd_bitcoinblockhashstr(char *coinstr,char *serverport,char *userpass,int32_t height)
{
char numstr[128],*blockhashstr=0; bits256 hash2; struct iguana_info *coin;
sprintf(numstr,"%d",height);
if ( (blockhashstr= bitcoind_passthru(coinstr,serverport,userpass,"getblockhash",numstr)) == 0 )
return(0);
hash2 = bits256_conv(blockhashstr);
if ( blockhashstr == 0 || blockhashstr[0] == 0 || bits256_nonz(hash2) == 0 )
{
printf("couldnt get blockhash for %u, probably curl is disabled\n",height);
if ( blockhashstr != 0 )
free(blockhashstr);
if ( height == 0 )
{
if ( (coin= iguana_coinfind(coinstr)) != 0 )
{
bits256_str(numstr,*(bits256 *)coin->chain->genesis_hashdata);
return(clonestr(numstr));
}
}
return(0);
}
return(blockhashstr);
}
cJSON *kmd_blockjson(int32_t *heightp,char *coinstr,char *serverport,char *userpass,char *blockhashstr,int32_t height)
{
cJSON *json = 0; int32_t flag = 0; char buf[1024],*blocktxt = 0;
if ( blockhashstr == 0 )
blockhashstr = kmd_bitcoinblockhashstr(coinstr,serverport,userpass,height), flag = 1;
if ( blockhashstr != 0 )
{
sprintf(buf,"\"%s\"",blockhashstr);
blocktxt = bitcoind_passthru(coinstr,serverport,userpass,"getblock",buf);
//printf("get_blockjson.(%d %s) %s\n",height,blockhashstr,blocktxt);
if ( blocktxt != 0 && blocktxt[0] != 0 && (json= cJSON_Parse(blocktxt)) != 0 && heightp != 0 )
if ( (*heightp= juint(json,"height")) != height )
*heightp = -1;
if ( flag != 0 && blockhashstr != 0 )
free(blockhashstr);
if ( blocktxt != 0 )
free(blocktxt);
}
return(json);
}
int32_t _kmd_bitcoinscan(struct iguana_info *coin)
{
int32_t h,num=0,loadheight,i,n,numtxids,numvins,numvouts,flag=0,height=-1; cJSON *txjson,*vouts,*vins,*blockjson,*txids,*vout,*vin,*sobj,*addresses; bits256 zero,txid; char *curlstr,params[128],str[65]; struct kmd_transactionhh *ptr; struct kmd_transaction *tx; uint8_t type_rmd160[21];
if ( coin->kmd_didinit == 0 )
{
if ( (coin->kmd_txidfp= kmd_txidinit(coin)) == 0 )
printf("error initializing %s.kmd lookups\n",coin->symbol);
coin->kmd_didinit = 1;
}
height = kmd_height(coin);
loadheight = coin->kmd_height;
while ( loadheight < height )
{
if ( loadheight == 0 )
{
loadheight++;
continue;
}
flag = 0;
if ( (blockjson= kmd_blockjson(&h,coin->symbol,coin->chain->serverport,coin->chain->userpass,0,loadheight)) != 0 )
{
if ( (txids= jarray(&numtxids,blockjson,"tx")) != 0 )
{
for (i=0; i<numtxids; i++)
{
memset(&zero,0,sizeof(zero));
sprintf(params,"[\"%s, 1\"]",bits256_str(str,jbits256(jitem(txids,i),0)));
if ( (curlstr= bitcoind_passthru(coin->symbol,coin->chain->serverport,coin->chain->userpass,"getrawtransaction",params)) != 0 )
{
if ( (txjson= cJSON_Parse(curlstr)) != 0 )
{
txid = jbits256(txjson,"txid");
if ( kmd_transaction(coin,txid) != 0 )
{
printf("already have txid.%s\n",bits256_str(str,txid));
free_json(txjson);
free(curlstr);
continue;
}
vouts = jarray(&numvouts,txjson,"vout");
vins = jarray(&numvins,txjson,"vin");
if ( (tx= kmd_transactionalloc(txid,loadheight-jint(txjson,"confirmations"),jint(txjson,"blocktime"),numvouts)) != 0 )
{
if ( (ptr= kmd_transactionadd(coin,tx,numvouts)) != 0 )
{
for (i=0; i<numvouts; i++)
{
vout = jitem(vouts,i);
if ( (sobj= jobj(vout,"scriptPubKey")) != 0 && (addresses= jarray(&n,sobj,"addresses")) != 0 )
{
kmd_transactionvout(coin,ptr,i,jdouble(vout,"value")*SATOSHIDEN,type_rmd160,zero,-1);
}
}
for (i=0; i<numvins; i++)
{
vin = jitem(vins,i);
if ( kmd_transactionvin(coin,txid,i,jbits256(vin,"txid"),jint(vin,"vout")) < 0 )
{
printf("error i.%d of numvins.%d\n",i,numvins);
break;
}
}
}
}
free_json(txjson);
}
free(curlstr);
}
}
num++;
kmd_flushfiles(coin);
}
free_json(blockjson);
}
coin->kmd_height = loadheight++;
if ( flag == 0 || num > 100 )
break;
}
return(num);
}
void kmd_bitcoinscan()
{
char *retstr; cJSON *array; int32_t i,n; struct iguana_info *coin;
if ( (retstr= dpow_notarychains(0,0,0,0)) != 0 )
{
if ( (array= cJSON_Parse(retstr)) != 0 )
{
if ( (n= cJSON_GetArraySize(array)) > 0 )
{
for (i=0; i<n; i++)
{
if ( (coin= iguana_coinfind(jstri(array,i))) != 0 && strcmp(coin->symbol,"BTC") != 0 )
_kmd_bitcoinscan(coin);
}
}
free_json(array);
}
free(retstr);
}
}
#endif

2
includes/iguana_apideclares.h

@ -42,6 +42,8 @@ TWO_STRINGS(dex,listunspent,symbol,address);
TWO_STRINGS_AND_TWO_DOUBLES(dex,listtransactions,symbol,address,count,skip);
TWO_STRINGS(dex,kvsearch,symbol,key);
THREE_STRINGS_AND_THREE_INTS(dex,kvupdate,symbol,key,value,flags,unused,unusedb);
TWO_STRINGS(dex,listunspent2,symbol,coinaddr);
TWO_STRINGS(dex,listtransactions2,symbol,coinaddr);
TWO_STRINGS(zcash,passthru,function,hex);
TWO_STRINGS(komodo,passthru,function,hex);

1
includes/iguana_structs.h

@ -536,6 +536,7 @@ struct iguana_info
struct iguana_block *RTblocks[65536]; uint8_t *RTrawdata[65536]; int32_t RTrecvlens[65536],RTnumtx[65536];
struct iguana_RTtxid *RTdataset; struct iguana_RTaddr *RTaddrs;
struct hashstr_item *alladdresses;
struct kmd_transactionhh *kmd_transactions; struct kmd_addresshh *kmd_addresses; portable_mutex_t kmdmutex; FILE *kmd_txidfp; int32_t kmd_didinit,kmd_height;
};
struct vin_signer { bits256 privkey; char coinaddr[64]; uint8_t siglen,sig[80],rmd160[20],pubkey[66]; };

Loading…
Cancel
Save