|
|
|
/******************************************************************************
|
|
|
|
* 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
|
|
|
|
|
|
|
|
#define KMD_EXPLORER_LAG 6
|
|
|
|
|
|
|
|
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,numvins; uint32_t timestamp;
|
|
|
|
struct kmd_voutinfo vouts[];
|
|
|
|
} PACKED;
|
|
|
|
|
|
|
|
struct kmd_transactionhh
|
|
|
|
{
|
|
|
|
UT_hash_handle hh;
|
|
|
|
struct kmd_transaction *tx;
|
|
|
|
long fpos;
|
|
|
|
int32_t numvouts,numvins;
|
|
|
|
struct kmd_transactionhh *ptrs[];
|
|
|
|
};
|
|
|
|
|
|
|
|
struct kmd_addresshh
|
|
|
|
{
|
|
|
|
UT_hash_handle hh;
|
|
|
|
struct kmd_transactionhh *prev,*lastprev;
|
|
|
|
uint8_t type_rmd160[21], pad;
|
|
|
|
};
|
|
|
|
|
|
|
|
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);
|
|
|
|
if ( addr != 0 && 0 )
|
|
|
|
{
|
|
|
|
char coinaddr[64];
|
|
|
|
bitcoin_address(coinaddr,type_rmd160[0],&type_rmd160[1],20);
|
|
|
|
printf("%s found (%s) %02x\n",coin->symbol,coinaddr,type_rmd160[0]);
|
|
|
|
}
|
|
|
|
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);
|
|
|
|
if ( 0 )
|
|
|
|
{
|
|
|
|
char coinaddr[64];
|
|
|
|
bitcoin_address(coinaddr,type_rmd160[0],&type_rmd160[1],20);
|
|
|
|
printf("%s NEW ADDRESS.(%s) %02x\n",coin->symbol,coinaddr,type_rmd160[0]);
|
|
|
|
}
|
|
|
|
portable_mutex_lock(&coin->kmdmutex);
|
|
|
|
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=0;
|
|
|
|
if ( bits256_nonz(txid) == 0 || vout < 0 )
|
|
|
|
return(0); // coinbase must be
|
|
|
|
if ( (ptr= kmd_transaction(coin,txid)) != 0 && vout < ptr->numvouts && (spendptr= kmd_transaction(coin,spendtxid)) != 0 )
|
|
|
|
{
|
|
|
|
ptr->ptrs[(vout<<1) + 1] = spendptr;
|
|
|
|
if ( bits256_cmp(ptr->tx->vouts[vout].spendtxid,spendtxid) != 0 || ptr->tx->vouts[vout].spendvini != vini )
|
|
|
|
{
|
|
|
|
if ( bits256_nonz(ptr->tx->vouts[vout].spendtxid) != 0 )
|
|
|
|
printf("ht.%d vout.%d overwriting nonz spend\n",ptr->tx->height,vout);
|
|
|
|
//uint8_t type_rmd160[21]; char str[65];
|
|
|
|
//bitcoin_addr2rmd160(&type_rmd160[0],&type_rmd160[1],"RR5yAkzaxJeCVTwvpgCGsNcSPAZjeq3av4");
|
|
|
|
//if ( memcmp(type_rmd160,ptr->tx->vouts[vout].type_rmd160,21) == 0 )
|
|
|
|
// printf("RR5yAkzaxJeCVTwvpgCGsNcSPAZjeq3av4 %p vout.%d spend %.8f by %s/%d %p\n",ptr,vout,dstr(ptr->tx->vouts[vout].amount),bits256_str(str,spendtxid),vini,spendptr);
|
|
|
|
ptr->tx->vouts[vout].spendtxid = spendtxid;
|
|
|
|
ptr->tx->vouts[vout].spendvini = vini;
|
|
|
|
}
|
|
|
|
return(0);
|
|
|
|
}
|
|
|
|
char str[65]; printf("%s.vin error %s vout.%d of %d vs ptr %p [%d] spent.%p\n",coin->symbol,bits256_str(str,txid),vout,ptr!=0?ptr->numvouts:-1,ptr,ptr!=0?ptr->numvouts:-1,spendptr);
|
|
|
|
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 ( 0 )
|
|
|
|
{
|
|
|
|
char coinaddr[64],str[65];
|
|
|
|
bitcoin_address(coinaddr,type_rmd160[0],&type_rmd160[1],20);
|
|
|
|
if ( strcmp(coinaddr,"RCsKEQ3r5Xxw4ZtK4CH9VzvfGpTFMdPpsh") == 0 )
|
|
|
|
printf("%s ht.%d %s VOUT %d %.8f\n",coinaddr,ptr->tx->height,bits256_str(str,ptr->tx->txid),vout,dstr(amount));
|
|
|
|
}
|
|
|
|
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 ( (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,int32_t numvins)
|
|
|
|
{
|
|
|
|
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->numvins = numvins;
|
|
|
|
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,int32_t numvins)
|
|
|
|
{
|
|
|
|
struct kmd_transaction *tx;
|
|
|
|
tx = calloc(1,sizeof(*tx) + sizeof(struct kmd_voutinfo)*numvouts);
|
|
|
|
tx->numvouts = numvouts;
|
|
|
|
tx->numvins = numvins;
|
|
|
|
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);
|
|
|
|
if ( coin->kmd_spendfp != 0 )
|
|
|
|
fflush(coin->kmd_spendfp);
|
|
|
|
}
|
|
|
|
|
|
|
|
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/TRANSACTIONS/%s",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,T.numvins)) != 0 )
|
|
|
|
{
|
|
|
|
//char str[65]; printf("INIT %s.[%d] vins.[%d] ht.%d %u\n",bits256_str(str,T.txid),T.numvouts,T.numvins,T.height,T.timestamp);
|
|
|
|
if ( (ptr= kmd_transactionadd(coin,tx,T.numvouts,T.numvins)) != 0 )
|
|
|
|
{
|
|
|
|
if ( ptr != kmd_transaction(coin,tx->txid) )
|
|
|
|
printf("%s ERROR: %p != %p for ht.%d\n",coin->symbol,ptr,kmd_transaction(coin,tx->txid),tx->height);
|
|
|
|
ptr->fpos = lastpos;
|
|
|
|
ptr->numvins = T.numvins;
|
|
|
|
ptr->numvouts = T.numvouts;
|
|
|
|
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
|
|
|
|
{
|
|
|
|
printf("%s error loading vout.%d ht.%d\n",coin->symbol,i,T.height);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if ( i == T.numvouts )
|
|
|
|
{
|
|
|
|
lastpos = ftell(fp);
|
|
|
|
if ( T.height > coin->kmd_height )
|
|
|
|
coin->kmd_height = T.height;
|
|
|
|
} else break;
|
|
|
|
}
|
|
|
|
} else break;
|
|
|
|
}
|
|
|
|
printf("%s finished txidinit fpos %ld vs lastpos %ld\n",coin->symbol,ftell(fp),lastpos);
|
|
|
|
fseek(fp,lastpos,SEEK_SET);
|
|
|
|
} else fp = fopen(fname,"wb+");
|
|
|
|
return(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
FILE *kmd_spendinit(struct iguana_info *coin)
|
|
|
|
{
|
|
|
|
int32_t i,numvins,spentvout; FILE *fp; char fname[1024],str[65]; bits256 txid,spenttxid; struct kmd_transactionhh *ptr,*tmp; struct kmd_voutinfo *vptr; long lastpos=0;
|
|
|
|
sprintf(fname,"%s/TRANSACTIONS/%s.spends",GLOBAL_DBDIR,coin->symbol);
|
|
|
|
if ( (fp= fopen(fname,"rb+")) != 0 )
|
|
|
|
{
|
|
|
|
while ( fread(&txid,1,sizeof(txid),fp) == sizeof(txid) )
|
|
|
|
{
|
|
|
|
if ( fread(&numvins,1,sizeof(numvins),fp) == sizeof(numvins) )
|
|
|
|
{
|
|
|
|
for (i=0; i<numvins; i++)
|
|
|
|
{
|
|
|
|
if ( fread(&spenttxid,1,sizeof(spenttxid),fp) == sizeof(spenttxid) &&
|
|
|
|
fread(&spentvout,1,sizeof(spentvout),fp) == sizeof(spentvout) )
|
|
|
|
{
|
|
|
|
if ( kmd_transactionvin(coin,txid,i,spenttxid,spentvout) < 0 )
|
|
|
|
{
|
|
|
|
printf("%s error adding spend %s %d of %d\n",coin->symbol,bits256_str(str,txid),i,numvins);
|
|
|
|
//break;
|
|
|
|
}
|
|
|
|
} else break;
|
|
|
|
}
|
|
|
|
if ( i == numvins )
|
|
|
|
lastpos = ftell(fp);
|
|
|
|
else break;
|
|
|
|
} else break;
|
|
|
|
}
|
|
|
|
printf("%s finished spendinit fpos %ld vs lastpos %ld\n",coin->symbol,ftell(fp),lastpos);
|
|
|
|
fseek(fp,lastpos,SEEK_SET);
|
|
|
|
HASH_ITER(hh,coin->kmd_transactions,ptr,tmp)
|
|
|
|
{
|
|
|
|
//printf("scan for spends ht.%d\n",ptr->tx->height);
|
|
|
|
for (i=0; i<ptr->numvouts; i++)
|
|
|
|
{
|
|
|
|
vptr = &ptr->tx->vouts[i];
|
|
|
|
if ( vptr->spendvini >= 0 && bits256_nonz(vptr->spendtxid) != 0 )
|
|
|
|
{
|
|
|
|
if ( ptr->ptrs[(i<<1) + 1] != kmd_transaction(coin,vptr->spendtxid) )
|
|
|
|
{
|
|
|
|
printf("%s mismatch %s spend.%d %p %p\n",coin->symbol,bits256_str(str,vptr->spendtxid),i,ptr->ptrs[(i<<1) + 1],kmd_transaction(coin,vptr->spendtxid));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else fp = fopen(fname,"wb+");
|
|
|
|
return(fp);
|
|
|
|
}
|
|
|
|
|
|
|
|
cJSON *kmd_transactionjson(int32_t height,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 supernet_info *myinfo,struct iguana_info *coin,int32_t height,struct kmd_transaction *tx,int32_t vout,int32_t is_listunspent)
|
|
|
|
{
|
|
|
|
char *script; cJSON *sobj,*txout,*item = cJSON_CreateObject();
|
|
|
|
jaddstr(item,"type","received");
|
|
|
|
jaddnum(item,"confirmations",height - tx->height);
|
|
|
|
jaddnum(item,"height",tx->height);
|
|
|
|
jaddnum(item,"timestamp",tx->timestamp);
|
|
|
|
jaddbits256(item,"txid",tx->txid);
|
|
|
|
jaddnum(item,"vout",vout);
|
|
|
|
jaddnum(item,"amount",dstr(tx->vouts[vout].amount));
|
|
|
|
if ( strcmp(coin->symbol,"KMD") == 0 )
|
|
|
|
jaddnum(item,"interest",dstr(_iguana_interest((uint32_t)time(NULL),coin->longestchain,tx->timestamp,tx->vouts[vout].amount)));
|
|
|
|
if ( is_listunspent != 0 )
|
|
|
|
{
|
|
|
|
//char str[65]; printf("get spendscriptstr for %s/v%d\n",bits256_str(str,tx->txid),vout);
|
|
|
|
if ( (txout= dpow_gettxout(myinfo,coin,tx->txid,vout)) != 0 )
|
|
|
|
{
|
|
|
|
//printf("got.(%s)\n",jprint(txout,0));
|
|
|
|
if ( (sobj= jobj(txout,"scriptPubKey")) != 0 && (script= jstr(sobj,"hex")) != 0 )
|
|
|
|
jaddstr(item,"scriptPubKey",script);
|
|
|
|
free_json(txout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(item);
|
|
|
|
}
|
|
|
|
|
|
|
|
cJSON *kmd_spentjson(int32_t height,struct kmd_transaction *tx,int32_t vout,struct kmd_transactionhh *spent)
|
|
|
|
{
|
|
|
|
cJSON *item = cJSON_CreateObject();
|
|
|
|
jaddstr(item,"type","sent");
|
|
|
|
jaddnum(item,"confirmations",height - tx->height);
|
|
|
|
jaddnum(item,"height",tx->height);
|
|
|
|
jaddnum(item,"timestamp",tx->timestamp);
|
|
|
|
jaddbits256(item,"txid",tx->txid);
|
|
|
|
jaddnum(item,"vout",vout);
|
|
|
|
jaddnum(item,"amount",dstr(tx->vouts[vout].amount));
|
|
|
|
jaddbits256(item,"spendtxid",tx->vouts[vout].spendtxid);
|
|
|
|
jaddnum(item,"vin",tx->vouts[vout].spendvini);
|
|
|
|
if ( spent != 0 )
|
|
|
|
{
|
|
|
|
jadd(item,"paid",kmd_transactionjson(height,spent,"paid"));
|
|
|
|
}
|
|
|
|
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_getinfo(coin->symbol,coin->chain->serverport,coin->chain->userpass,coin->getinfostr)) != 0 )
|
|
|
|
{
|
|
|
|
if ( (curljson= cJSON_Parse(curlstr)) != 0 )
|
|
|
|
{
|
|
|
|
height = juint(curljson,"blocks");
|
|
|
|
//printf("kmd_height.%d (%s)\n",height,jprint(curljson,0));
|
|
|
|
free_json(curljson);
|
|
|
|
}
|
|
|
|
free(curlstr);
|
|
|
|
}
|
|
|
|
return(height);
|
|
|
|
}
|
|
|
|
|
|
|
|
cJSON *kmd_gettxin(struct iguana_info *coin,bits256 txid,int32_t vout)
|
|
|
|
{
|
|
|
|
struct kmd_transactionhh *ptr,*spendptr; struct kmd_transaction *tx; cJSON *retjson;
|
|
|
|
if ( (ptr= kmd_transaction(coin,txid)) != 0 && (tx= ptr->tx) != 0 )
|
|
|
|
{
|
|
|
|
if ( vout >= ptr->numvouts )
|
|
|
|
return(cJSON_Parse("{\"error\":\"vout too big\"}"));
|
|
|
|
if ( (spendptr= ptr->ptrs[(vout << 1) + 1]) != 0 )
|
|
|
|
{
|
|
|
|
retjson = cJSON_CreateObject();
|
|
|
|
jaddstr(retjson,"result","success");
|
|
|
|
jaddstr(retjson,"status","spent");
|
|
|
|
jaddnum(retjson,"height",tx->height);
|
|
|
|
jaddnum(retjson,"timestamp",tx->timestamp);
|
|
|
|
jaddbits256(retjson,"txid",txid);
|
|
|
|
jaddnum(retjson,"vout",vout);
|
|
|
|
jaddnum(retjson,"value",dstr(tx->vouts[vout].amount));
|
|
|
|
jaddbits256(retjson,"spendtxid",tx->vouts[vout].spendtxid);
|
|
|
|
jaddnum(retjson,"vin",tx->vouts[vout].spendvini);
|
|
|
|
} else return(cJSON_Parse("{\"result\":\"success\",\"status\":\"unspent\"}"));
|
|
|
|
}
|
|
|
|
return(cJSON_Parse("{\"error\":\"txid not found\"}"));
|
|
|
|
}
|
|
|
|
|
|
|
|
cJSON *kmd_listaddress(struct supernet_info *myinfo,struct iguana_info *coin,char *coinaddr,int32_t mode,cJSON *array)
|
|
|
|
{
|
|
|
|
struct kmd_addresshh *addr; struct kmd_transactionhh *ptr=0,*spent,*prev=0; uint8_t type_rmd160[21]; int32_t i; char *retstr; cJSON *retjson;
|
|
|
|
if ( array == 0 )
|
|
|
|
array = cJSON_CreateArray();
|
|
|
|
//printf("%s listaddress.(%s)\n",coin->symbol,coinaddr);
|
|
|
|
if ( (retstr= bitcoinrpc_validateaddress(myinfo,coin,0,0,coinaddr)) != 0 )
|
|
|
|
{
|
|
|
|
if ( (retjson= cJSON_Parse(retstr)) != 0 )
|
|
|
|
{
|
|
|
|
if ( jobj(retjson,"error") != 0 && is_cJSON_False(jobj(retjson,"error")) == 0 )
|
|
|
|
{
|
|
|
|
printf("%s\n",retstr);
|
|
|
|
free(retstr);
|
|
|
|
return(retjson);
|
|
|
|
}
|
|
|
|
free_json(retjson);
|
|
|
|
}
|
|
|
|
free(retstr);
|
|
|
|
}
|
|
|
|
/*if ( time(NULL) > coin->kmd_lasttime+30 )
|
|
|
|
{
|
|
|
|
coin->kmd_lasttime = (uint32_t)time(NULL);
|
|
|
|
if ( (height= kmd_height(coin)) > coin->kmd_height+KMD_EXPLORER_LAG*2 )
|
|
|
|
{
|
|
|
|
printf("height.%d > kmd_height.%d\n",height,coin->kmd_height);
|
|
|
|
return(cJSON_Parse("[]"));
|
|
|
|
}
|
|
|
|
}*/
|
|
|
|
if ( strcmp("1111111111111111111114oLvT2",coinaddr) == 0 ) // null rmd160 from coinbase
|
|
|
|
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 )
|
|
|
|
{
|
|
|
|
while ( ptr != 0 )
|
|
|
|
{
|
|
|
|
prev = 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];
|
|
|
|
//if ( strcmp("RFpYbieWuKm2ZsTaKeWkrrEdeSkVzhqX8x",coinaddr) == 0 )
|
|
|
|
// printf("mode.%d [%d] %s ht.%d amount %.8f spent.%p\n",mode,coin->kmd_height,coinaddr,ptr->tx->height,dstr(ptr->tx->vouts[i].amount),spent);
|
|
|
|
if ( (mode == 0 && spent == 0) || (mode == 1 && spent != 0) || mode == 2 )
|
|
|
|
{
|
|
|
|
//if ( fulltx == 0 )
|
|
|
|
{
|
|
|
|
if ( mode == 0 )
|
|
|
|
jaddi(array,kmd_unspentjson(myinfo,coin,coin->kmd_height,ptr->tx,i,1));
|
|
|
|
else if ( mode == 1 )
|
|
|
|
jaddi(array,kmd_spentjson(coin->kmd_height,ptr->tx,i,spent));
|
|
|
|
else if ( mode == 2 )
|
|
|
|
{
|
|
|
|
if ( spent != 0 )
|
|
|
|
jaddi(array,kmd_spentjson(coin->kmd_height,ptr->tx,i,spent));
|
|
|
|
else jaddi(array,kmd_unspentjson(myinfo,coin,coin->kmd_height,ptr->tx,i,0));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
/*else if ( flag == 0 )
|
|
|
|
{
|
|
|
|
if ( mode == 0 )
|
|
|
|
jaddi(array,kmd_transactionjson(coin->kmd_height,ptr,"received"));
|
|
|
|
else if ( mode == 1 )
|
|
|
|
{
|
|
|
|
jaddi(array,kmd_transactionjson(coin->kmd_height,ptr,"received"));
|
|
|
|
jaddi(array,kmd_transactionjson(coin->kmd_height,spent,"sent"));
|
|
|
|
}
|
|
|
|
else if ( mode == 2 )
|
|
|
|
{
|
|
|
|
if ( spent != 0 )
|
|
|
|
jaddi(array,kmd_transactionjson(coin->kmd_height,ptr,"spent"));
|
|
|
|
else jaddi(array,kmd_transactionjson(coin->kmd_height,ptr,"received"));
|
|
|
|
}
|
|
|
|
flag = 1;
|
|
|
|
}*/
|
|
|
|
}
|
|
|
|
if ( ptr->ptrs[i<<1] != 0 )
|
|
|
|
{
|
|
|
|
if ( prev == 0 )
|
|
|
|
prev = ptr->ptrs[i<<1];
|
|
|
|
else if ( prev != ptr->ptrs[i<<1] )
|
|
|
|
printf("%s ht.%d prev.%p != %p\n",coinaddr,ptr->tx->height,prev,ptr->ptrs[i<<1]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
ptr = prev;
|
|
|
|
}
|
|
|
|
} //else printf("no valid entry for (%s) %p %p\n",coinaddr,addr,ptr);
|
|
|
|
return(array);
|
|
|
|
}
|
|
|
|
|
|
|
|
cJSON *kmd_listunspent(struct supernet_info *myinfo,struct iguana_info *coin,char *coinaddr)
|
|
|
|
{
|
|
|
|
cJSON *retjson;
|
|
|
|
retjson = kmd_listaddress(myinfo,coin,coinaddr,0,0);
|
|
|
|
//printf("KMD utxos.(%s)\n",jprint(retjson,0));
|
|
|
|
return(retjson);
|
|
|
|
}
|
|
|
|
|
|
|
|
cJSON *kmd_listspent(struct supernet_info *myinfo,struct iguana_info *coin,char *coinaddr)
|
|
|
|
{
|
|
|
|
return(kmd_listaddress(myinfo,coin,coinaddr,1,0));
|
|
|
|
}
|
|
|
|
|
|
|
|
cJSON *kmd_listtransactions(struct supernet_info *myinfo,struct iguana_info *coin,char *coinaddr,int32_t count,int32_t skip)
|
|
|
|
{
|
|
|
|
cJSON *array = cJSON_CreateArray();
|
|
|
|
//if ( (height= kmd_height(coin)) > coin->kmd_height+KMD_EXPLORER_LAG )
|
|
|
|
// return(cJSON_Parse("[]"));
|
|
|
|
if ( count == 0 )
|
|
|
|
count = 100;
|
|
|
|
array = kmd_listaddress(myinfo,coin,coinaddr,0,0);
|
|
|
|
array = kmd_listaddress(myinfo,coin,coinaddr,1,array);
|
|
|
|
return(array);
|
|
|
|
}
|
|
|
|
|
|
|
|
int64_t _kmd_getbalance(struct supernet_info *myinfo,struct iguana_info *coin,char *coinaddr,uint64_t *receivedp,uint64_t *sentp,uint64_t *interestp)
|
|
|
|
{
|
|
|
|
int32_t iter,i,n; cJSON *array,*item; uint64_t value;
|
|
|
|
for (iter=1; iter<=2; iter++)
|
|
|
|
{
|
|
|
|
if ( (array= kmd_listaddress(myinfo,coin,coinaddr,iter,0)) != 0 )
|
|
|
|
{
|
|
|
|
if ( (n= cJSON_GetArraySize(array)) > 0 )
|
|
|
|
{
|
|
|
|
for (i=0; i<n; i++)
|
|
|
|
{
|
|
|
|
item = jitem(array,i);
|
|
|
|
if ( (value= jdouble(item,"amount")*SATOSHIDEN) != 0 || (value= jdouble(item,"value")*SATOSHIDEN) != 0 )
|
|
|
|
{
|
|
|
|
if ( iter == 2 )
|
|
|
|
{
|
|
|
|
*receivedp += value;
|
|
|
|
*interestp += jdouble(item,"interest") * SATOSHIDEN;
|
|
|
|
} else *sentp += value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free_json(array);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return(*receivedp - *sentp);
|
|
|
|
}
|
|
|
|
|
|
|
|
cJSON *kmd_getbalance(struct supernet_info *myinfo,struct iguana_info *coin,char *coinaddr)
|
|
|
|
{
|
|
|
|
cJSON *retjson; double netbalance=0.,fbalance; uint64_t interest,i,s,r,sent=0,received=0; int64_t balance=0; struct kmd_addresshh *addr,*tmp; char address[64]; int32_t height = coin->kmd_height+1;
|
|
|
|
retjson = cJSON_CreateObject();
|
|
|
|
fbalance = 0.;
|
|
|
|
interest = 0;
|
|
|
|
if ( strcmp(coinaddr,"*") == 0 )
|
|
|
|
{
|
|
|
|
HASH_ITER(hh,coin->kmd_addresses,addr,tmp)
|
|
|
|
{
|
|
|
|
bitcoin_address(address,addr->type_rmd160[0],&addr->type_rmd160[1],20);
|
|
|
|
s = r = i = 0;
|
|
|
|
balance += _kmd_getbalance(myinfo,coin,address,&r,&s,&i);
|
|
|
|
netbalance += dstr(r);
|
|
|
|
netbalance -= dstr(s);
|
|
|
|
if ( (r - s) > 100000*SATOSHIDEN )
|
|
|
|
printf("{\"address\":\"%s\",\"received\":%.8f,\"sent\":%.8f,\"balance\":%.8f,\"supply\":%.8f,\"supplyf\":%.8f,\"interest\":%.8f}\n",address,dstr(r),dstr(s),dstr(r)-dstr(s),dstr(balance),netbalance,dstr(interest));
|
|
|
|
received += r;
|
|
|
|
sent += s;
|
|
|
|
interest += i;
|
|
|
|
}
|
|
|
|
if ( strcmp("KMD",coin->symbol) == 0 )
|
|
|
|
jaddnum(retjson,"interestpaid",dstr(balance) - 100000000 - (height*3));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
balance = _kmd_getbalance(myinfo,coin,coinaddr,&received,&sent,&interest);
|
|
|
|
netbalance = dstr(balance);
|
|
|
|
}
|
|
|
|
jaddstr(retjson,"result","success");
|
|
|
|
jaddnum(retjson,"received",dstr(received));
|
|
|
|
jaddnum(retjson,"sent",dstr(sent));
|
|
|
|
//if ( fabs(netbalance*SATOSHIDEN - balance) > 1 )
|
|
|
|
jaddnum(retjson,"balancef",netbalance+1./(SATOSHIDEN*2)-SMALLVAL);
|
|
|
|
//else
|
|
|
|
jaddnum(retjson,"balance",dstr(balance));
|
|
|
|
jaddnum(retjson,"interest",dstr(interest));
|
|
|
|
jaddnum(retjson,"height",height);
|
|
|
|
if ( strcmp("KMD",coin->symbol) == 0 )
|
|
|
|
jaddnum(retjson,"mined",height*3);
|
|
|
|
return(retjson);
|
|
|
|
}
|
|
|
|
|
|
|
|
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("%s couldnt get blockhash for %u, probably curl is disabled %p\n",coinstr,height,blockhashstr);
|
|
|
|
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,lag,i,n,j,iter,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 txid\n",coin->symbol);
|
|
|
|
else if ( (coin->kmd_spendfp= kmd_spendinit(coin)) == 0 )
|
|
|
|
printf("error initializing %s.kmd spend\n",coin->symbol);
|
|
|
|
coin->kmd_didinit = 1;
|
|
|
|
}
|
|
|
|
height = kmd_height(coin);
|
|
|
|
loadheight = coin->kmd_height+1;
|
|
|
|
//if ( strcmp(coin->symbol,"LTC") == 0 )
|
|
|
|
// lag = 3;
|
|
|
|
//else
|
|
|
|
lag = (strcmp(coin->symbol,"KMD") == 0 ? KMD_EXPLORER_LAG : 2);
|
|
|
|
while ( loadheight < height-lag )
|
|
|
|
{
|
|
|
|
flag = 0;
|
|
|
|
if ( (loadheight % 10000) == 0 )
|
|
|
|
printf("loading %s ht.%d vs height.%d - lag.%d kmdheight.%d\n",coin->symbol,loadheight,height,lag,coin->kmd_height);//,jprint(kmd_getbalance(coin,"*"),1));
|
|
|
|
if ( (blockjson= kmd_blockjson(&h,coin->symbol,coin->chain->serverport,coin->chain->userpass,0,loadheight)) != 0 )
|
|
|
|
{
|
|
|
|
if ( (txids= jarray(&numtxids,blockjson,"tx")) != 0 )
|
|
|
|
{
|
|
|
|
for (iter=0; iter<2; iter++)
|
|
|
|
for (i=0; i<numtxids; i++)
|
|
|
|
{
|
|
|
|
memset(&zero,0,sizeof(zero));
|
|
|
|
txid = jbits256(jitem(txids,i),0);
|
|
|
|
if ( iter == 0 && kmd_transaction(coin,txid) != 0 )
|
|
|
|
{
|
|
|
|
//printf("already have txid.%s\n",bits256_str(str,txid));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
sprintf(params,"[\"%s\", 1]",bits256_str(str,txid));
|
|
|
|
if ( (curlstr= bitcoind_passthru(coin->symbol,coin->chain->serverport,coin->chain->userpass,"getrawtransaction",params)) != 0 )
|
|
|
|
{
|
|
|
|
if ( (txjson= cJSON_Parse(curlstr)) != 0 )
|
|
|
|
{
|
|
|
|
if ( bits256_cmp(txid,jbits256(txjson,"txid")) != 0 )
|
|
|
|
{
|
|
|
|
printf("%s txid mismatch error ht.%d i.%d\n",coin->symbol,loadheight,i);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
vouts = jarray(&numvouts,txjson,"vout");
|
|
|
|
vins = jarray(&numvins,txjson,"vin");
|
|
|
|
tx = 0;
|
|
|
|
ptr = 0;
|
|
|
|
if ( iter == 0 )
|
|
|
|
{
|
|
|
|
if ( (tx= kmd_transactionalloc(txid,loadheight,jint(txjson,"blocktime"),numvouts,numvins)) != 0 )
|
|
|
|
ptr = kmd_transactionadd(coin,tx,numvouts,numvins);
|
|
|
|
else printf("error init tx ptr.%p tx.%p\n",ptr,tx);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( (ptr= kmd_transaction(coin,txid)) != 0 )
|
|
|
|
tx = ptr->tx;
|
|
|
|
}
|
|
|
|
if ( ptr != 0 && tx != 0 )
|
|
|
|
{
|
|
|
|
if ( iter == 0 )
|
|
|
|
{
|
|
|
|
sobj = addresses = 0;
|
|
|
|
for (j=0; j<numvouts; j++)
|
|
|
|
{
|
|
|
|
vout = jitem(vouts,j);
|
|
|
|
if ( (sobj= jobj(vout,"scriptPubKey")) != 0 && (addresses= jarray(&n,sobj,"addresses")) != 0 )
|
|
|
|
{
|
|
|
|
bitcoin_addr2rmd160(&type_rmd160[0],&type_rmd160[1],jstri(addresses,0));
|
|
|
|
kmd_transactionvout(coin,ptr,j,jdouble(vout,"value")*SATOSHIDEN,type_rmd160,zero,-1);
|
|
|
|
//fprintf(stderr,"%.8f ",jdouble(vout,"value"));
|
|
|
|
} // else printf("missing sobj.%p or addresses.%p (%s)\n",sobj,addresses,jprint(vout,0)); //likely OP_RETURN
|
|
|
|
sobj = addresses = 0;
|
|
|
|
}
|
|
|
|
//fprintf(stderr,"numvouts.%d ht.%d %s\n",numvouts,height,coin->symbol);
|
|
|
|
if ( coin->kmd_txidfp != 0 )
|
|
|
|
{
|
|
|
|
ptr->fpos = ftell(coin->kmd_txidfp);
|
|
|
|
fwrite(tx,1,sizeof(*tx) + tx->numvouts*sizeof(*tx->vouts),coin->kmd_txidfp);
|
|
|
|
fflush(coin->kmd_txidfp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if ( coin->kmd_spendfp != 0 )
|
|
|
|
{
|
|
|
|
fwrite(&txid,1,sizeof(txid),coin->kmd_spendfp);
|
|
|
|
fwrite(&numvins,1,sizeof(numvins),coin->kmd_spendfp);
|
|
|
|
fflush(coin->kmd_spendfp);
|
|
|
|
}
|
|
|
|
for (j=0; j<numvins; j++)
|
|
|
|
{
|
|
|
|
bits256 spenttxid; int32_t spentvout;
|
|
|
|
vin = jitem(vins,j);
|
|
|
|
spenttxid = jbits256(vin,"txid");
|
|
|
|
spentvout = jint(vin,"vout");
|
|
|
|
//if ( bits256_nonz(spenttxid) == 0 || spentvout < 0 )
|
|
|
|
// printf("null spenttxid ht.%d j.%d spentvout.%d\n",loadheight,j,spentvout);
|
|
|
|
if ( kmd_transactionvin(coin,txid,j,spenttxid,spentvout) < 0 )
|
|
|
|
{
|
|
|
|
printf("error i.%d of numvins.%d (%s)\n",j,numvins,jprint(vin,0));
|
|
|
|
flag++;
|
|
|
|
}
|
|
|
|
if ( coin->kmd_spendfp != 0 )
|
|
|
|
{
|
|
|
|
fwrite(&spenttxid,1,sizeof(spenttxid),coin->kmd_spendfp);
|
|
|
|
fwrite(&spentvout,1,sizeof(spentvout),coin->kmd_spendfp);
|
|
|
|
fflush(coin->kmd_spendfp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else printf("incomplete at ht.%d i.%d %p %p\n",loadheight,i,ptr,tx);
|
|
|
|
free_json(txjson);
|
|
|
|
} else printf("parseerror.(%s)\n",curlstr);
|
|
|
|
free(curlstr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
num++;
|
|
|
|
kmd_flushfiles(coin);
|
|
|
|
}
|
|
|
|
free_json(blockjson);
|
|
|
|
}
|
|
|
|
if ( flag != 0 || num > 100 )
|
|
|
|
break;
|
|
|
|
coin->kmd_height = loadheight++;
|
|
|
|
}
|
|
|
|
return(num);
|
|
|
|
}
|
|
|
|
|
|
|
|
void kmd_bitcoinscan()
|
|
|
|
{
|
|
|
|
char *retstr; cJSON *array; int32_t i,n; struct iguana_info *coin; // scan allcoins also
|
|
|
|
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 )
|
|
|
|
{
|
|
|
|
//if ( strcmp("KMD",coin->symbol) == 0 )
|
|
|
|
_kmd_bitcoinscan(coin);
|
|
|
|
sleep(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
free_json(array);
|
|
|
|
}
|
|
|
|
free(retstr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|