/****************************************************************************** * Copyright © 2014-2016 The SuperNET Developers. * * * * See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at * * the top-level directory of this distribution for the individual copyright * * holder information and the developer policies on copyright and licensing. * * * * Unless otherwise agreed in a custom licensing agreement, no part of the * * SuperNET software, including this file may be copied, modified, propagated * * or distributed except according to the terms contained in the LICENSE file * * * * Removal or modification of this copyright notice is prohibited. * * * ******************************************************************************/ //#define uthash_malloc(size) iguana_memalloc(&coin->RThashmem,size,1) //#define uthash_free(ptr,size) #include "iguana777.h" #include "exchanges/bitcoin.h" int32_t iguana_RTunspentind2txid(struct supernet_info *myinfo,struct iguana_info *coin,int32_t *spentheightp,bits256 *txidp,int32_t *voutp,struct iguana_outpoint outpt) { struct iguana_ramchaindata *rdata=0; struct iguana_bundle *bp=0; struct iguana_unspent *U,*u; struct iguana_txid *T,*t; struct iguana_RTunspent *unspent; struct iguana_RTtxid *parent; *voutp = *spentheightp = -1; memset(txidp,0,sizeof(*txidp)); if ( outpt.isptr != 0 && (unspent= outpt.ptr) != 0 ) { if ( (parent= unspent->parent) != 0 ) { *txidp = parent->txid; *spentheightp = parent->height; } *voutp = unspent->vout; return(0); } memset(txidp,0,sizeof(*txidp)); //if ( hdrsi == coin->bundlescount-1 ) // rdata = coin->RTramchain.H.data; //else if ( (bp= coin->bundles[hdrsi]) != 0 ) bp = coin->bundles[outpt.hdrsi]; rdata = bp->ramchain.H.data; while ( rdata != 0 && outpt.unspentind > 0 && outpt.unspentind < rdata->numunspents ) { U = RAMCHAIN_PTR(rdata,Uoffset); u = &U[outpt.unspentind]; if ( u->txidind > 0 && u->txidind < rdata->numtxids ) { T = RAMCHAIN_PTR(rdata,Toffset); t = &T[u->txidind]; if ( outpt.unspentind >= t->firstvout ) { *txidp = t->txid; *spentheightp = (outpt.hdrsi * coin->chain->bundlesize) + t->bundlei; *voutp = outpt.unspentind - t->firstvout; return(0); } } else if ( bp == 0 && (bp= coin->bundles[outpt.hdrsi]) != 0 ) rdata = bp->ramchain.H.data; else break; } return(-1); } int32_t iguana_unspentindfind(struct supernet_info *myinfo,struct iguana_info *coin,uint64_t *spentamountp,char *coinaddr,uint8_t *spendscript,int32_t *spendlenp,uint64_t *valuep,int32_t *heightp,bits256 txid,int32_t vout,int32_t lasthdrsi,int32_t mempool) { struct iguana_txid *tp,TX; struct gecko_memtx *memtx; struct iguana_pkhash *P; struct iguana_unspent *U; struct iguana_bundle *bp; struct iguana_ramchaindata *rdata; int64_t RTspend; int64_t value; struct iguana_outpoint spentpt; int32_t pkind,hdrsi,firstvout,spentheight,flag=0,unspentind = -1; //portable_mutex_lock(&coin->RTmutex); if ( valuep != 0 ) *valuep = 0; *spentamountp = 0; if ( coinaddr != 0 ) coinaddr[0] = 0; if ( coin->fastfind != 0 && (firstvout= iguana_txidfastfind(coin,heightp,txid,lasthdrsi)) >= 0 ) unspentind = (firstvout + vout); else if ( (tp= iguana_txidfind(coin,heightp,&TX,txid,lasthdrsi)) != 0 ) unspentind = (tp->firstvout + vout); if ( coinaddr != 0 && unspentind > 0 && (hdrsi= *heightp/coin->chain->bundlesize) >= 0 && hdrsi < coin->bundlescount && (bp= coin->bundles[hdrsi]) != 0 && (rdata= bp->ramchain.H.data) != 0 && unspentind < rdata->numunspents ) { if ( time(NULL) > bp->lastprefetch+777 ) { //fprintf(stderr,"pf.[%d] ",bp->hdrsi); iguana_ramchain_prefetch(coin,&bp->ramchain,0); bp->lastprefetch = (uint32_t)time(NULL); } U = RAMCHAIN_PTR(rdata,Uoffset); P = RAMCHAIN_PTR(rdata,Poffset); pkind = U[unspentind].pkind; if ( pkind > 0 && pkind < rdata->numpkinds ) { RTspend = 0; flag++; memset(&spentpt,0,sizeof(spentpt)); spentpt.hdrsi = bp->hdrsi; spentpt.unspentind = unspentind; bitcoin_address(coinaddr,iguana_addrtype(coin,U[unspentind].type),P[pkind].rmd160,sizeof(P[pkind].rmd160)); if ( iguana_RTspentflag(myinfo,coin,&RTspend,&spentheight,&bp->ramchain,spentpt,0,1,coin->longestchain,U[unspentind].value) == 0 ) //bp == coin->current ? &coin->RTramchain : { if ( valuep != 0 ) *valuep = U[unspentind].value; if ( spendscript != 0 && spendlenp != 0 ) *spendlenp = iguana_voutscript(coin,bp,spendscript,0,&U[unspentind],&P[pkind],1); } else *spentamountp = RTspend; } } if ( flag == 0 && mempool != 0 ) { if ( (memtx= gecko_unspentfind(0,coin,txid)) != 0 && vout < memtx->numoutputs ) { memcpy(&value,gecko_valueptr(memtx,vout),sizeof(value)); if ( value > 0 ) { *valuep = value; if ( spendlenp != 0 ) { *spendlenp = 1; spendscript = 0; printf("mempool unspentind doesnt support scriptlenp yet\n"); } } } } //portable_mutex_unlock(&coin->RTmutex); return(unspentind); } char *iguana_RTinputaddress(struct supernet_info *myinfo,struct iguana_info *coin,char *coinaddr,struct iguana_outpoint *spentp,cJSON *vinobj) { bits256 txid; int32_t vout,checkind,height; memset(spentp,0,sizeof(*spentp)); spentp->hdrsi = -1; if ( jobj(vinobj,"txid") != 0 && jobj(vinobj,"vout") != 0 ) { txid = jbits256(vinobj,"txid"); vout = jint(vinobj,"vout"); height = jint(vinobj,"height"); checkind = jint(vinobj,"checkind"); if ( (height != 0 && checkind != 0) || iguana_RTunspentindfind(myinfo,coin,spentp,coinaddr,0,0,0,&height,txid,vout,coin->bundlescount-1,0) == 0 ) { return(coinaddr); } else { char str[65]; printf("error finding (%s/%d) height.%d checkind.%d\n",bits256_str(str,txid),vout,height,checkind); } } return(0); } cJSON *ramchain_unspentjson(struct iguana_unspent *up,uint32_t unspentind) { cJSON *item = cJSON_CreateObject(); jaddnum(item,"hdrsi",up->hdrsi); jaddnum(item,"pkind",up->pkind); jaddnum(item,"unspentind",unspentind); jaddnum(item,"prevunspentind",up->prevunspentind); jadd64bits(item,"satoshis",up->value); jaddnum(item,"txidind",up->txidind); jaddnum(item,"vout",up->vout); jaddnum(item,"type",up->type); jaddnum(item,"fileid",up->fileid); jaddnum(item,"scriptpos",up->scriptpos); jaddnum(item,"scriptlen",up->scriptlen); return(item); } cJSON *ramchain_spentjson(struct supernet_info *myinfo,struct iguana_info *coin,int32_t spentheight,bits256 txid,int32_t vout,int64_t uvalue) { char coinaddr[64]; bits256 hash2,*X; struct iguana_txid T,*tx,*spentT,*spent_tx; struct iguana_bundle *bp; int32_t j,i,ind; struct iguana_block *block; int64_t value,total = 0; struct iguana_unspent *U,*u; struct iguana_pkhash *P; struct iguana_spend *S,*s; struct iguana_ramchaindata *rdata; cJSON *addrs,*item,*voutobj; item = cJSON_CreateObject(); hash2 = iguana_blockhash(coin,spentheight); if ( (block= iguana_blockfind("spent",coin,hash2)) != 0 && (bp= coin->bundles[spentheight/coin->chain->bundlesize]) != 0 && (rdata= bp->ramchain.H.data) != 0 ) { X = RAMCHAIN_PTR(rdata,Xoffset); S = RAMCHAIN_PTR(rdata,Soffset); U = RAMCHAIN_PTR(rdata,Uoffset); P = RAMCHAIN_PTR(rdata,Poffset); spentT = RAMCHAIN_PTR(rdata,Toffset); for (i=0; iRO.txn_count; i++) { if ( (tx= iguana_blocktx(coin,&T,block,i)) != 0 ) { s = &S[tx->firstvin]; for (j=0; jnumvins; j++,s++) { if ( s->prevout == vout ) { if ( s->external != 0 ) { ind = s->spendtxidind & 0xfffffff; if ( bits256_cmp(X[ind],txid) != 0 ) continue; } else { spent_tx = &spentT[s->spendtxidind]; if ( bits256_cmp(spent_tx->txid,txid) != 0 ) continue; } jaddbits256(item,"spentfrom",tx->txid); jaddnum(item,"vin",j); jaddnum(item,"timestamp",tx->timestamp); u = &U[tx->firstvout]; addrs = cJSON_CreateArray(); for (j=0; jnumvouts; j++,u++) { voutobj = cJSON_CreateObject(); bitcoin_address(coinaddr,iguana_addrtype(coin,u->type),P[u->pkind].rmd160,sizeof(P[u->pkind].rmd160)); jaddnum(voutobj,coinaddr,dstr(u->value)); jaddi(addrs,voutobj); total += u->value; } jadd(item,"vouts",addrs); jaddnum(item,"total",dstr(total)); jaddnum(item,"ratio",dstr(uvalue) / dstr(total+coin->txfee)); return(item); } } } } } else { struct iguana_RTtxid *RTptr,*tmp; struct iguana_RTspend *spend; HASH_ITER(hh,coin->RTdataset,RTptr,tmp) { for (i=0; inumvins; i++) { if ( (spend= RTptr->spends[i]) != 0 ) { if ( bits256_cmp(spend->prev_hash,txid) == 0 && spend->prev_vout == vout ) { value = iguana_txidamount(myinfo,coin,coinaddr,txid,vout); jaddnum(item,"total",dstr(value)); jaddbits256(item,"spentfrom",RTptr->txid); jaddnum(item,"vin",i); addrs = cJSON_CreateArray(); voutobj = cJSON_CreateObject(); jaddnum(voutobj,coinaddr,dstr(value)); jaddi(addrs,voutobj); jadd(item,"vouts",addrs); jaddnum(item,"timestamp",RTptr->timestamp); //printf("Found MATCH! (%s %.8f)\n",coinaddr,dstr(value)); return(item); } } } } } jaddstr(item,"error","couldnt find spent info"); return(item); } cJSON *iguana_RTunspentjson(struct supernet_info *myinfo,struct iguana_info *coin,struct iguana_outpoint outpt,bits256 txid,int32_t vout,int64_t value,struct iguana_unspent *up,uint8_t rmd160[20],char *coinaddr,uint8_t *pubkey33,int32_t spentheight,char *remoteaddr) { /*{ "txid" : "d54994ece1d11b19785c7248868696250ab195605b469632b7bd68130e880c9a", "vout" : 1, "address" : "mgnucj8nYqdrPFh2JfZSB1NmUThUGnmsqe", "account" : "test label", "scriptPubKey" : "76a9140dfc8bafc8419853b34d5e072ad37d1a5159f58488ac", "amount" : 0.00010000, "confirmations" : 6210, "spendable" : true },*/ struct iguana_waccount *wacct; struct iguana_waddress *waddr; int32_t height; char scriptstr[8192],asmstr[sizeof(scriptstr)+1024]; cJSON *item; uint32_t checkind; struct iguana_RTunspent *unspent; struct iguana_block *block; item = cJSON_CreateObject(); jaddbits256(item,"txid",txid); jaddnum(item,"vout",vout); jaddstr(item,"address",coinaddr); if ( outpt.isptr != 0 && (unspent= outpt.ptr) != 0 ) { if ( unspent->scriptlen > 0 ) { init_hexbytes_noT(scriptstr,unspent->script,unspent->scriptlen); jaddstr(item,"scriptPubKey",scriptstr); } } else { if ( iguana_scriptget(coin,scriptstr,asmstr,sizeof(scriptstr),outpt.hdrsi,outpt.unspentind,txid,vout,rmd160,up!=0?up->type:2,pubkey33) != 0 ) jaddstr(item,"scriptPubKey",scriptstr); } jaddnum(item,"amount",dstr(value)); //jaddnum(item,"timestamp",T[up->txidind].timestamp); if ( iguana_RTunspentindfind(myinfo,coin,&outpt,0,0,0,0,&height,txid,vout,coin->bundlescount-1,0) == 0 ) { checkind = outpt.unspentind; if ( (block= iguana_blockfind("unspentjson",coin,iguana_blockhash(coin,height))) != 0 && block->RO.timestamp != 0 ) jaddnum(item,"timestamp",block->RO.timestamp); jaddnum(item,"height",height); jaddnum(item,"confirmations",coin->blocks.hwmchain.height - height + 1); jaddnum(item,"checkind",checkind); } if ( remoteaddr == 0 || remoteaddr[0] == 0 ) { if ( (waddr= iguana_waddresssearch(myinfo,&wacct,coinaddr)) != 0 ) { jaddstr(item,"account",wacct->account); if ( spentheight == 0 ) jadd(item,"spendable",jtrue()); else jadd(item,"spendable",jfalse()); } else jadd(item,"spendable",jfalse()); } if ( spentheight > 0 ) { if ( up != 0 ) jadd(item,"spent",ramchain_unspentjson(up,outpt.unspentind)); jaddnum(item,"spentheight",spentheight); jadd(item,"dest",ramchain_spentjson(myinfo,coin,spentheight,txid,vout,value)); } else if ( up != 0 ) jadd(item,"unspent",ramchain_unspentjson(up,outpt.unspentind)); return(item); } struct iguana_pkhash *iguana_pkhashfind(struct iguana_info *coin,struct iguana_ramchain **ramchainp,int64_t *depositsp,struct iguana_outpoint *lastptp,struct iguana_pkhash *p,uint8_t rmd160[20],int32_t firsti,int32_t endi) { uint8_t *PKbits; struct iguana_pkhash *P; uint32_t pkind,numpkinds,i; struct iguana_bundle *bp; struct iguana_ramchain *ramchain; struct iguana_ramchaindata *rdata; struct iguana_account *ACCTS; struct iguana_RTaddr *RTaddr; *depositsp = 0; *ramchainp = 0; memset(lastptp,0,sizeof(*lastptp)); if ( firsti == coin->bundlescount && endi == firsti ) { if ( (RTaddr= iguana_RTaddrfind(coin,rmd160,0)) != 0 ) { *depositsp = RTaddr->credits; if ( (lastptp->ptr= RTaddr->lastunspent) != 0 ) lastptp->isptr = 1; memcpy(p->rmd160,rmd160,sizeof(p->rmd160)); p->pkind = 0; return(p); } else return(0); } for (i=firsti; ibundlescount&&i<=endi; i++) { if ( (bp= coin->bundles[i]) != 0 ) { if ( 0 && coin->RTramchain_busy != 0 ) { printf("iguana_pkhashfind: unexpected access when RTramchain_busy\n"); return(0); } ramchain = &bp->ramchain;//(bp != coin->current) ? &bp->ramchain : &coin->RTramchain; // prevent remote query access before RTmode if ( (rdata= ramchain->H.data) != 0 && time(NULL) > bp->emitfinish+10 ) { numpkinds = rdata->numpkinds; PKbits = RAMCHAIN_PTR(rdata,PKoffset); P = RAMCHAIN_PTR(rdata,Poffset); if ( bp == coin->current ) ACCTS = ramchain->A; else ACCTS = RAMCHAIN_PTR(rdata,Aoffset); if ( (pkind= iguana_sparseaddpk(PKbits,rdata->pksparsebits,rdata->numpksparse,rmd160,P,0,ramchain)) > 0 && pkind < numpkinds ) { *ramchainp = ramchain; *depositsp = ACCTS[pkind].total; lastptp->hdrsi = bp->hdrsi; lastptp->unspentind = ACCTS[pkind].lastunspentind; //printf("[%d] return pkind.%u of %u P.%p %.8f last.%u ACCTS.%p %p\n",i,pkind,numpkinds,P,dstr(*depositsp),*lastunspentindp,ACCTS,ramchain->A); if ( P != 0 ) *p = P[pkind]; return(p); } else if ( pkind != 0 ) printf("[%d] not found pkind.%d vs num.%d RT.%d rdata.%p\n",i,pkind,rdata->numpkinds,bp->isRT,rdata); } else if ( coin->spendvectorsaved > 1 && bp != coin->current && bp->bundleheight < coin->firstRTheight ) { //printf("%s.[%d] skip null rdata isRT.%d [%d]\n",coin->symbol,i,bp->isRT,coin->current!=0?coin->current->hdrsi:-1); } } } return(0); } int32_t iguana_uheight(struct iguana_info *coin,int32_t bundleheight,struct iguana_txid *T,int32_t numtxids,struct iguana_unspent *up) { if ( up->txidind > 0 && up->txidind < numtxids ) return(bundleheight + T[up->txidind].bundlei); else return(bundleheight); } int32_t iguana_outpt_set(struct iguana_info *coin,struct iguana_outpoint *outpt,struct iguana_unspent *u,uint32_t unspentind,int16_t hdrsi,bits256 txid,int32_t vout,uint8_t *rmd160,uint8_t *pubkey33) { char scriptstr[IGUANA_MAXSCRIPTSIZE*2+1],asmstr[16384]; memset(outpt,0,sizeof(*outpt)); outpt->txid = txid; outpt->vout = vout; outpt->hdrsi = hdrsi; outpt->isptr = 0; outpt->unspentind = unspentind; outpt->value = u->value; if ( iguana_scriptget(coin,scriptstr,asmstr,sizeof(scriptstr),outpt->hdrsi,outpt->unspentind,outpt->txid,outpt->vout,rmd160,u->type,pubkey33) != 0 ) { //printf("scriptstr.(%s)\n",scriptstr); outpt->spendlen = (int32_t)strlen(scriptstr) >> 1; if ( outpt->spendlen < sizeof(outpt->spendscript) ) decode_hex(outpt->spendscript,outpt->spendlen,scriptstr); else { outpt->spendlen = 0; printf("error scriptstr.(%s) is too big for %d\n",scriptstr,(int32_t)sizeof(outpt->spendscript)); return(-1); } } return(0); } int32_t iguana_datachain_scan(struct supernet_info *myinfo,struct iguana_info *coin,uint8_t rmd160[20]) { int64_t deposits,crypto777_payment; struct iguana_outpoint lastpt; uint32_t unspentind; int32_t i,j,num,uheight; struct iguana_bundle *bp; struct iguana_ramchain *ramchain; struct iguana_ramchaindata *rdata; struct iguana_pkhash *P,p; struct iguana_unspent *U,*u; struct iguana_txid *T,*tx; for (i=num=0; ibundlescount&&i*coin->chain->bundlesizefirstRTheight; i++) { if ( (bp= coin->bundles[i]) != 0 ) { ramchain = 0; memset(&lastpt,0,sizeof(lastpt)); if ( iguana_pkhashfind(coin,&ramchain,&deposits,&lastpt,&p,rmd160,i,i) != 0 ) { if ( ramchain != 0 && (rdata= ramchain->H.data) != 0 ) { unspentind = lastpt.unspentind; U = RAMCHAIN_PTR(rdata,Uoffset); T = RAMCHAIN_PTR(rdata,Toffset); P = RAMCHAIN_PTR(rdata,Poffset); while ( unspentind > 0 ) { tx = &T[U[unspentind].txidind]; u = &U[tx->firstvout]; uheight = iguana_uheight(coin,ramchain->height,T,rdata->numtxids,u); for (crypto777_payment=j=0; jnumvouts; j++,u++) { crypto777_payment = datachain_update(myinfo,0,coin,tx->timestamp,bp,P[u->pkind].rmd160,crypto777_payment,u->type,uheight,(((uint64_t)bp->hdrsi << 32) | unspentind),u->value,u->fileid,u->scriptpos,u->scriptlen,tx->txid,j); } num++; unspentind = U[unspentind].prevunspentind; } } } } } // do a RT scan here return(num); } int32_t iguana_RTscanunspents(struct supernet_info *myinfo,struct iguana_info *coin,char *remoteaddr,cJSON *array,int64_t *spentp,int64_t *depositsp,struct iguana_outpoint *unspents,int32_t max,uint8_t *rmd160,char *coinaddr,uint8_t *pubkey33,struct iguana_outpoint lastpt,int32_t lastheight) { int32_t spentheight,n = 0; struct iguana_outpoint outpt; bits256 txid; struct iguana_RTtxid *parent; struct iguana_RTunspent *unspent = lastpt.ptr; while ( unspent != 0 ) { if ( lastheight <= 0 || unspent->height < lastheight ) { if ( unspent->spend == 0 ) { spentheight = 0; memset(&outpt,0,sizeof(outpt)); memset(&txid,0,sizeof(txid)); if ( (parent= unspent->parent) != 0 ) txid = parent->txid; else printf("unspent has no parent?\n"); outpt.isptr = 1; outpt.ptr = unspent; outpt.txid = txid; outpt.vout = unspent->vout; outpt.value = unspent->value; outpt.hdrsi = unspent->height / coin->chain->bundlesize; if ( (outpt.spendlen= unspent->scriptlen) > 0 && outpt.spendlen < sizeof(outpt.spendscript) ) memcpy(outpt.spendscript,unspent->script,outpt.spendlen); else { printf("spendscript.%d doesnt fit into %d\n",outpt.spendlen,(int32_t)sizeof(outpt.spendscript)); outpt.spendlen = 0; } if ( array != 0 ) jaddi(array,iguana_RTunspentjson(myinfo,coin,outpt,txid,unspent->vout,unspent->value,0,rmd160,coinaddr,pubkey33,spentheight,remoteaddr)); *depositsp += unspent->value; if ( unspents != 0 ) unspents[n] = outpt; n++; } else *spentp += unspent->value; } unspent = unspent->prevunspent; } return(n); } int64_t iguana_RTpkhashbalance(struct supernet_info *myinfo,struct iguana_info *coin,cJSON *array,int64_t *spentp,struct iguana_outpoint *unspents,int32_t *nump,struct iguana_ramchain *ramchain,struct iguana_pkhash *p,struct iguana_outpoint lastpt,uint8_t rmd160[20],char *coinaddr,uint8_t *pubkey33,int32_t lastheight,int32_t minconf,int32_t maxconf,char *remoteaddr,int32_t includespent) { struct iguana_unspent *U; struct iguana_utxo *U2; int32_t spentflag,max,uheight,spentheight; uint32_t pkind=0,unspentind; int64_t spent = 0,checkval,deposits = 0; struct iguana_txid *T; struct iguana_account *A2; struct iguana_outpoint outpt; struct iguana_ramchaindata *rdata = 0; int64_t RTspend = 0; //struct iguana_spend *S; max = *nump; *spentp = *nump = 0; if ( 0 && coin->RTramchain_busy != 0 ) { printf("iguana_pkhashbalance: unexpected access when RTramchain_busy\n"); return(0); } if ( ramchain == 0 ) // RT search { if ( lastpt.isptr != 0 ) { *nump = iguana_RTscanunspents(myinfo,coin,remoteaddr,array,spentp,&deposits,unspents,max,rmd160,coinaddr,pubkey33,lastpt,lastheight); } else { printf("iguana_pkhashbalance: unexpected RT non-ptr lastpt\n"); coin->RTreset_needed = 1; } return(deposits - *spentp); } if ( ramchain->Uextras == 0 || (rdata= ramchain->H.data) == 0 ) { if ( ramchain->height < (coin->bundlescount-1)*coin->chain->bundlesize ) { //printf("iguana_pkhashbalance.[%d] %d: unexpected null spents.%p or rdata.%p\n",ramchain->height,(coin->bundlescount-1)*coin->chain->bundlesize,ramchain->Uextras,rdata); } else iguana_volatilesalloc(coin,ramchain,0); return(0); } unspentind = lastpt.unspentind; U = RAMCHAIN_PTR(rdata,Uoffset); T = RAMCHAIN_PTR(rdata,Toffset); RTspend = 0; if ( lastheight == 0 ) lastheight = IGUANA_MAXHEIGHT; while ( unspentind > 0 ) { uheight = iguana_uheight(coin,ramchain->height,T,rdata->numtxids,&U[unspentind]); if ( lastheight <= 0 || uheight < lastheight ) { //printf("u%u ",unspentind); deposits += U[unspentind].value; iguana_outpt_set(coin,&outpt,&U[unspentind],unspentind,lastpt.hdrsi,T[U[unspentind].txidind].txid,unspentind - T[U[unspentind].txidind].firstvout,p->rmd160,pubkey33); RTspend = 0; if ( iguana_RTspentflag(myinfo,coin,&RTspend,&spentheight,ramchain,outpt,lastheight,minconf,maxconf,U[unspentind].value) == 0 ) { if ( *nump < max && unspents != 0 ) unspents[*nump] = outpt; //printf("+%.8f ",dstr(U[unspentind].value)); (*nump)++; spentflag = 0; } else { //printf("-%.8f ",dstr(U[unspentind].value)); spent += U[unspentind].value; spentflag = 1; } if ( array != 0 && (spentflag == 0 || includespent != 0) ) jaddi(array,iguana_RTunspentjson(myinfo,coin,outpt,T[U[unspentind].txidind].txid,U[unspentind].vout,U[unspentind].value,&U[unspentind],rmd160,coinaddr,pubkey33,spentheight,remoteaddr)); if ( p->pkind != U[unspentind].pkind ) printf("warning: [%d] p->pkind.%u vs U->pkind.%u for u%d\n",lastpt.hdrsi,p->pkind,U[unspentind].pkind,unspentind); } // else printf("skip uheight.%d lastheight.%d\n",uheight,lastheight); pkind = p->pkind; unspentind = U[unspentind].prevunspentind; } if ( lastheight > 0 && (A2= ramchain->A2) != 0 && (U2= ramchain->Uextras) != 0 ) { //S = RAMCHAIN_PTR(rdata,Soffset); unspentind = A2[pkind].lastunspentind; checkval = 0; while ( unspentind > 0 ) { uheight = iguana_uheight(coin,ramchain->height,T,rdata->numtxids,&U[unspentind]); if ( uheight < lastheight ) { checkval += U[unspentind].value; //printf("u%u %.8f spentflag.%d prev.%u fromheight.%d\n",unspentind,dstr(U[unspentind].value),U2[unspentind].spentflag,U2[unspentind].prevunspentind,U2[unspentind].fromheight); } unspentind = U2[unspentind].prevunspentind; } if ( 0 && llabs(spent - checkval - RTspend) > SMALLVAL ) printf("spend %s: [%d] deposits %.8f spent %.8f check %.8f (%.8f) vs A2[%u] %.8f\n",lastheight==IGUANA_MAXHEIGHT?"checkerr":"",lastpt.hdrsi,dstr(deposits),dstr(spent),dstr(checkval)+dstr(RTspend),dstr(*spentp),pkind,dstr(A2[pkind].total)); } (*spentp) = spent; //printf("[%d] (%s) spent %.8f, RTspent %.8f deposits %.8f\n",ramchain->height/coin->chain->bundlesize,coinaddr,dstr(spent),dstr(RTspend),dstr(deposits)); return(deposits - spent); } int32_t iguana_RTpkhasharray(struct supernet_info *myinfo,struct iguana_info *coin,cJSON *array,int32_t minconf,int32_t maxconf,uint64_t *totalp,struct iguana_pkhash *P,int32_t max,uint8_t rmd160[20],char *coinaddr,uint8_t *pubkey33,int32_t lastheight,struct iguana_outpoint *unspents,int32_t *numunspentsp,int32_t maxunspents,char *remoteaddr,int32_t includespent) { int32_t i,n,m,numunspents; int64_t spent,deposits,netbalance,total; struct iguana_outpoint lastpt; struct iguana_pkhash *p,_p; struct iguana_ramchain *ramchain; struct iguana_bundle *bp; if ( coin->RTheight == 0 ) return(-1); if ( 0 && coin->RTramchain_busy != 0 ) { printf("iguana_pkhasharray: unexpected access when RTramchain_busy\n"); return(-1); } numunspents = numunspentsp != 0 ? *numunspentsp : 0; if ( lastheight == 0 ) lastheight = IGUANA_MAXHEIGHT; if ( max > coin->bundlescount ) max = coin->bundlescount; //printf("minconf.%d maxconf.%d max.%d addr.%s last.%d maxunspents.%d\n",minconf,maxconf,max,coinaddr,lastheight,maxunspents); for (total=n=i=0; i=coin->firstRTheight); i++) { bp = 0; if ( i != max && (bp= coin->bundles[i]) == 0 ) continue; if ( bp != 0 ) { if ( lastheight > 0 && bp->bundleheight > lastheight ) { //printf("lastheight.%d less than %d\n",lastheight,bp->bundleheight+bp->n); break; } if ( (coin->blocks.hwmchain.height - (bp->bundleheight + bp->n - 1)) > maxconf ) { //printf("%d more than minconf.%d\n",(coin->blocks.hwmchain.height - (bp->bundleheight + bp->n - 1)),maxconf); continue; } if ( (coin->blocks.hwmchain.height - bp->bundleheight) < minconf ) { //printf("%d less than minconf.%d\n",(coin->blocks.hwmchain.height - bp->bundleheight),minconf); break; } } if ( iguana_pkhashfind(coin,&ramchain,&deposits,&lastpt,P != 0 ? &P[n] : &_p,rmd160,i,i) != 0 ) { m = maxunspents; p = (P == 0) ? &_p : &P[n]; if ( (netbalance= iguana_RTpkhashbalance(myinfo,coin,array,&spent,unspents != 0 ? &unspents[numunspents] : 0,&m,ramchain,p,lastpt,rmd160,coinaddr,pubkey33,lastheight,minconf,maxconf,remoteaddr,includespent)) != deposits-spent && lastheight == IGUANA_MAXHEIGHT && minconf == 1 && maxconf > coin->blocks.hwmchain.height ) { printf("pkhash balance mismatch from m.%d check %.8f vs %.8f spent %.8f [%.8f]\n",m,dstr(netbalance),dstr(deposits),dstr(spent),dstr(deposits)-dstr(spent)); } else { //printf("%s pkhash balance.[%d] from m.%d check %.8f vs %.8f spent %.8f [%.8f]\n",coinaddr,i,m,dstr(netbalance),dstr(deposits),dstr(spent),dstr(deposits)-dstr(spent)); total += netbalance; n++; } if ( maxunspents > 0 ) { maxunspents -= m; if ( maxunspents <= 0 ) break; } numunspents += m; //printf("%d: balance %.8f, lastunspent.%u m.%d num.%d max.%d\n",i,dstr(total),lastunspentind,m,numunspents,maxunspents); } } if ( numunspentsp != 0 ) *numunspentsp = numunspents; //printf("numunspents.%d max.%d\n",numunspents,maxunspents); *totalp += total; return(n); } int64_t iguana_RTunspents(struct supernet_info *myinfo,struct iguana_info *coin,cJSON *array,int32_t minconf,int32_t maxconf,uint8_t *rmdarray,int32_t numrmds,int32_t lastheight,struct iguana_outpoint *unspents,int32_t *numunspentsp,char *remoteaddr,int32_t includespent) { uint64_t total=0,sum=0; struct iguana_pkhash *P; uint8_t *addrtypes,*pubkeys; int32_t i,j,numunspents,maxunspents,flag = 0; char coinaddr[64]; //portable_mutex_lock(&coin->RTmutex); while ( 0 && coin->RTramchain_busy != 0 ) { fprintf(stderr,"iguana_pkhasharray: %s unexpected access when RTramchain_busy\n",coin->symbol); sleep(1); } numunspents = 0; maxunspents = *numunspentsp; if ( rmdarray == 0 ) rmdarray = iguana_walletrmds(myinfo,coin,&numrmds), flag++; if ( numrmds > 0 && rmdarray != 0 ) { addrtypes = &rmdarray[numrmds * 20], pubkeys = &rmdarray[numrmds * 21]; P = calloc(coin->bundlescount,sizeof(*P)); for (i=0; ibundlescount,&rmdarray[i * 20],coinaddr,&pubkeys[33*i],lastheight,unspents != 0 ? &unspents[numunspents] : 0,numunspentsp,maxunspents,remoteaddr,includespent); //printf("iguana_unspents: i.%d of %d: %s %.8f numunspents.%d\n",i,numrmds,coinaddr,dstr(total),*numunspentsp); maxunspents -= *numunspentsp; numunspents += *numunspentsp; sum += total; } //printf("sum %.8f\n",dstr(sum)); free(P); } *numunspentsp = numunspents; if ( flag != 0 && rmdarray != 0 ) free(rmdarray); //portable_mutex_unlock(&coin->RTmutex); return(sum); } uint8_t *iguana_rmdarray(struct supernet_info *myinfo,struct iguana_info *coin,int32_t *numrmdsp,cJSON *array,int32_t firsti) { int32_t i,n,flag=0,k,j=0; char *coinaddr,rmdstr[41]; uint8_t addrtype,*addrtypes,*rmdarray = 0; *numrmdsp = 0; if ( array == 0 || cJSON_GetArraySize(array) == 0 ) array = iguana_getaddressesbyaccount(myinfo,coin,"*"); if ( array != 0 && (n= cJSON_GetArraySize(array)) > 0 ) { *numrmdsp = n - firsti; rmdarray = calloc(1,(n-firsti) * (21 + 33)); addrtypes = &rmdarray[(n-firsti) * 20]; for (i=firsti; ilongestchain); continue; } bitcoin_addr2rmd160(&addrtypes[j],&rmdarray[20 * j],coinaddr); for (k=0; k<20; k++) if ( rmdarray[20 * j + k] != 0 ) break; if ( k == 20 ) continue; init_hexbytes_noT(rmdstr,&rmdarray[20 * j],20); j++; } } //printf("rmdarray[%d]\n",n); } if ( flag != 0 ) free_json(array); return(rmdarray); } int64_t *iguana_PoS_weights(struct supernet_info *myinfo,struct iguana_info *coin,struct iguana_pkhash **Ptrp,int64_t *supplyp,int32_t *numacctsp,int32_t *nonzp,int32_t *errsp,int32_t lastheight) { int64_t balance,total,supply,*weights=0; uint32_t pkind; int32_t j,numrmds,minconf,neg,numunspents,nonz,num=0; struct iguana_bundle *bp; struct iguana_ramchaindata *rdata; struct iguana_pkhash *refP; uint8_t rmd160[20],*rmdarray; cJSON *array; char coinaddr[64]; //struct iguana_account *A2; struct iguana_utxo *U2; *supplyp = 0; *numacctsp = *nonzp = 0; *errsp = 1; (*Ptrp) = 0; if ( (bp= coin->bundles[lastheight / coin->chain->bundlesize]) == 0 || bp == coin->current ) return(0); if ( (rdata= bp->ramchain.H.data) == 0 ) return(0); (*Ptrp) = refP = RAMCHAIN_PTR(rdata,Poffset); if ( (num= rdata->numpkinds) > 0 ) { weights = calloc(num,sizeof(*weights)); minconf = coin->blocks.hwmchain.height - lastheight; for (pkind=1; pkindchain->pubtype,rmd160,sizeof(rmd160)); jaddistr(array,coinaddr); //bitcoin_address(coinaddr,coin->chain->p2shtype,rmd160,sizeof(rmd160)); //jaddistr(array,coinaddr); if ( (rmdarray= iguana_rmdarray(myinfo,coin,&numrmds,array,0)) != 0 ) { numunspents = 0; balance = iguana_RTunspents(myinfo,coin,0,minconf,(1 << 30),rmdarray,numrmds,lastheight,0,&numunspents,0,0); free(rmdarray); weights[pkind] += balance; if ( weights[pkind] != balance ) printf("PKIND.%d %s %.8f += %.8f\n",pkind,coinaddr,dstr(weights[pkind]),dstr(balance)); } free_json(array); } } nonz = neg = 0; supply = 0; for (pkind=1; pkindchain->bundlesize,num,nonz,neg,dstr(supply)); return(weights); } bits256 iguana_staker_hash2(bits256 refhash2,uint8_t *refrmd160,uint8_t *rmd160,int64_t weight) { bits256 hash2; vcalc_sha256cat(hash2.bytes,refhash2.bytes,sizeof(refhash2),rmd160,20); return(mpz_div64(hash2,weight)); } int _cmp_hashes(const void *a,const void *b) { #define hasha (*(bits256 *)a) #define hashb (*(bits256 *)b) return(bits256_cmp(hasha,hashb)); #undef hasha #undef hashb } int32_t iguana_staker_sort(struct iguana_info *coin,bits256 *hash2p,uint8_t *refrmd160,struct iguana_pkhash *refP,int64_t *weights,int32_t numweights,bits256 *sortbuf) { int32_t i,j,n = 0; bits256 ind,refhash2 = *hash2p; memset(sortbuf,0,sizeof(*sortbuf) * 2 * numweights); for (i=0; i 0 ) { memset(&ind,0,sizeof(ind)); for (j=0; j<20; j++) ind.bytes[j] = refP[i].rmd160[j]; ind.ulongs[3] = weights[i]; ind.uints[5] = i; sortbuf[n << 1] = iguana_staker_hash2(refhash2,refrmd160,ind.bytes,weights[i]); sortbuf[(n << 1) + 1] = ind; n++; } } if ( n > 0 ) qsort(sortbuf,n,sizeof(*sortbuf)*2,_cmp_hashes); vcalc_sha256cat(hash2p->bytes,refhash2.bytes,sizeof(refhash2),sortbuf[1].bytes,20); memcpy(refrmd160,sortbuf[1].bytes,20); { char str[65],coinaddr[64]; bitcoin_address(coinaddr,coin->chain->pubtype,refrmd160,20); printf("winner.%s %.8f: %s\n",coinaddr,dstr(sortbuf[1].ulongs[3]),bits256_str(str,sortbuf[0])); } return((int32_t)sortbuf[1].uints[5]); } int32_t iguana_markedunspents_find(struct iguana_info *coin,int32_t *firstslotp,bits256 txid,int32_t vout) { int32_t i; *firstslotp = -1; if ( bits256_nonz(txid) != 0 && vout >= 0 ) { txid.ushorts[0] = vout; // small chance of collision ok due to small timeframe for (i=0; imarkedunspents)/sizeof(*coin->markedunspents); i++) { if ( *firstslotp < 0 && bits256_nonz(coin->markedunspents[i]) == 0 ) *firstslotp = i; if ( bits256_cmp(txid,coin->markedunspents[i]) == 0 ) return(i); } } if ( *firstslotp < 0 ) { for (i=0; imarkedunspents)/sizeof(*coin->markedunspents); i++) if ( bits256_nonz(coin->markedunspents[i]) == 0 ) { *firstslotp = i; break; } } if ( *firstslotp < 0 ) *firstslotp = (rand() % (sizeof(coin->markedunspents)/sizeof(*coin->markedunspents))); return(-1); } void iguana_unspents_mark(struct supernet_info *myinfo,struct iguana_info *coin,cJSON *vins) { int32_t i,n,firstslot; int16_t vout; cJSON *item; bits256 txid; if ( (n= cJSON_GetArraySize(vins)) > 0 ) { for (i=0; i= 0 ) { if ( iguana_markedunspents_find(coin,&firstslot,txid,vout) < 0 ) { if ( firstslot >= 0 ) { char str[65]; printf("slot.[%d] <- %s/v%d\n",firstslot,bits256_str(str,txid),vout); coin->markedunspents[firstslot] = txid; } } } } } } int32_t iguana_RTunspent_check(struct supernet_info *myinfo,struct iguana_info *coin,struct iguana_outpoint outpt) { int32_t firstslot; if ( iguana_markedunspents_find(coin,&firstslot,outpt.txid,outpt.vout) < 0 ) return(0); return(-1); } int32_t iguana_RTaddr_unspents(struct supernet_info *myinfo,struct iguana_info *coin,uint64_t *sump,struct iguana_outpoint *unspents,int32_t max,char *coinaddr,char *remoteaddr,int32_t lastheight,int32_t includespent) { int32_t n,j,k,numunspents,minconf = 0; uint64_t total; uint8_t rmd160[20],pubkey[65],addrtype; total = 0; n = numunspents = 0; if ( iguana_addressvalidate(coin,&addrtype,coinaddr) < 0 ) { printf("illegal coinaddr.(%s) minconf.%d longest.%d diff.%d\n",coinaddr,minconf,coin->longestchain,coin->blocks.hwmchain.height - minconf); return(0); } bitcoin_addr2rmd160(&addrtype,rmd160,coinaddr); for (j=0; j<20; j++) if ( rmd160[j] != 0 ) break; if ( j == 20 ) return(0); iguana_RTpkhasharray(myinfo,coin,0,minconf,coin->longestchain,&total,0,coin->bundlescount,rmd160,coinaddr,pubkey,lastheight,unspents,&n,max-1000,remoteaddr,includespent); numunspents = n; for (k=0; kparent) != 0 ) { *txidp = parent->txid; return(unspent->vout); } if ( (bp= coin->bundles[outpt.hdrsi]) == 0 ) return(-1); ramchain = &bp->ramchain;//(bp == coin->current) ? &coin->RTramchain : &bp->ramchain; if ( (rdata= ramchain->H.data) != 0 ) { U = RAMCHAIN_PTR(rdata,Uoffset); T = RAMCHAIN_PTR(rdata,Toffset); if ( outpt.unspentind > 0 && outpt.unspentind < rdata->numunspents ) { u = &U[outpt.unspentind]; if ( u->txidind > 0 && u->txidind < rdata->numtxids ) { *txidp = T[u->txidind].txid; return(outpt.unspentind - T[u->txidind].firstvout); } } } return(-1); } uint64_t iguana_unspentavail(struct supernet_info *myinfo,struct iguana_info *coin,struct iguana_outpoint outpt,int32_t minconf,int32_t maxconf) { struct iguana_ramchain *ramchain; struct iguana_bundle *bp; int64_t RTspend=0; int32_t spentheight,spentflag; struct iguana_unspent *U,*u; struct iguana_ramchaindata *rdata; if ( (bp= coin->bundles[outpt.hdrsi]) == 0 ) return(-1); ramchain = &bp->ramchain;//(bp == coin->current) ? &coin->RTramchain : &bp->ramchain; if ( (rdata= ramchain->H.data) == 0 ) return(0); if ( (spentflag= iguana_RTspentflag(myinfo,coin,&RTspend,&spentheight,ramchain,outpt,0,minconf,maxconf,0)) > 0 ) { printf("[%d].u%d was already spent ht.%d\n",outpt.hdrsi,outpt.unspentind,spentheight); return(-1); } else if ( spentflag == 0 ) { U = RAMCHAIN_PTR(rdata,Uoffset); if ( outpt.unspentind > 0 && outpt.unspentind < rdata->numunspents ) { u = &U[outpt.unspentind]; return(u->value); } else { printf("%s illegal unspentind.%u vs %u [%d]\n",coin->symbol,outpt.unspentind,rdata->numunspents,bp->hdrsi); return(-2); } } else return(0); } int32_t iguana_unspentfindjson(cJSON *destarray,cJSON *item) { cJSON *destitem; int32_t i,n; if ( (n= cJSON_GetArraySize(destarray)) > 0 ) { for (i=0; iFULLNODE != 0 || coin->VALIDATENODE != 0 ) { retjson = cJSON_CreateArray(); rmdarray = iguana_rmdarray(myinfo,coin,&numrmds,argarray,0); total = iguana_RTunspents(myinfo,coin,retjson,minconf,maxconf,rmdarray,numrmds,(1 << 30),0,&numunspents,remoteaddr,includespends); if ( rmdarray != 0 ) free(rmdarray); } else { basilisk_unspents_update(myinfo,coin); portable_mutex_lock(&myinfo->bu_mutex); if ( (unspents= myinfo->Cunspents) != 0 && (array= jobj(unspents,coin->symbol)) != 0 ) unspents = jduplicate(array); portable_mutex_unlock(&myinfo->bu_mutex); retjson = cJSON_CreateArray(); if ( unspents != 0 ) { if ( (n= cJSON_GetArraySize(unspents)) > 0 && (m= cJSON_GetArraySize(argarray)) > 0 ) { for (i=0; i 0 ) { memset(hash.bytes,0,sizeof(hash)); vals = cJSON_CreateObject(); jaddstr(vals,"coin",coin->symbol); jaddnum(vals,"history",1); jaddnum(vals,"firstheight",0); jaddnum(vals,"fanout",MAX(5,(int32_t)sqrt(myinfo->NOTARY.NUMRELAYS)+1)); jaddnum(vals,"numrequired",MAX(5,(int32_t)sqrt(myinfo->NOTARY.NUMRELAYS)+1)); jadd(vals,"addresses",jduplicate(argarray)); if ( (retstr= basilisk_standardservice("BAL",myinfo,0,hash,vals,"",1)) != 0 ) { if ( (retarray= cJSON_Parse(retstr)) != 0 ) { if ( (n= cJSON_GetArraySize(retarray)) > 0 ) { for (i=0; itxfee; if ( coin->FULLNODE != 0 || coin->VALIDATENODE != 0 ) { for (i=numunspents=0; i 0 ) { for (i=0; i max || sum > required ) break; } } free_json(array); } } *totalp = sum; printf("numunspents.%d max.%d sum %.8f required %.8f\n",numunspents,max,dstr(sum),dstr(required)); return(numunspents); } #define UTXOADDR_ITEMSIZE 32 #define iguana_utxotable_numinds(ind) (((ind) == 0xffff) ? coin->utxoaddrlastcount : (coin->utxoaddroffsets[(ind) + 1] - coin->utxoaddroffsets[ind])) int32_t iguana_rwutxoaddr(int32_t rwflag,uint16_t ind,uint8_t *serialized,struct iguana_utxoaddr *utxoaddr) { uint32_t pkind=0; int32_t len = 0; len += iguana_rwnum(rwflag,&serialized[0],sizeof(utxoaddr->hdrsi),&utxoaddr->hdrsi); if ( rwflag == 0 ) { utxoaddr->rmd160[0] = (ind & 0xff); utxoaddr->rmd160[1] = ((ind >> 8) & 0xff); memcpy(&utxoaddr->rmd160[2],&serialized[2],18); } else memcpy(&serialized[2],&utxoaddr->rmd160[2],18); len += 18; if ( rwflag != 0 ) pkind = utxoaddr->pkind; len += iguana_rwnum(rwflag,&serialized[20],sizeof(pkind),&pkind); if ( rwflag == 0 ) utxoaddr->pkind = pkind; len += iguana_rwnum(rwflag,&serialized[24],sizeof(utxoaddr->histbalance),&utxoaddr->histbalance); return(len); } uint64_t iguana_utxoaddrtablefind(struct iguana_info *coin,int16_t search_hdrsi,uint32_t search_pkind,uint8_t rmd160[20]) { struct iguana_utxoaddr UA; int32_t ind,num,i; uint8_t *ptr; memset(&UA,0,sizeof(UA)); ind = rmd160[0] + ((uint32_t)rmd160[1] << 8); if ( coin->utxoaddroffsets != 0 && (num= iguana_utxotable_numinds(ind)) > 0 ) { for (i=0; iutxoaddrtable[(coin->utxoaddroffsets[ind] + i) * UTXOADDR_ITEMSIZE]; iguana_rwutxoaddr(0,ind,ptr,&UA); if ( (UA.pkind == search_pkind && UA.hdrsi == search_hdrsi) || memcmp(UA.rmd160,rmd160,20) == 0 ) return(UA.histbalance); } //printf("ind.%04x no [%d] p%u after num.%d\n",ind,search_hdrsi,search_pkind,num); } return(0); } struct iguana_utxoaddr *iguana_utxoaddrfind(int32_t createflag,struct iguana_info *coin,int16_t hdrsi,uint32_t pkind,uint8_t rmd160[20],struct iguana_utxoaddr **prevp) { struct iguana_utxoaddr *utxoaddr; char coinaddr[64]; HASH_FIND(hh,coin->utxoaddrs,rmd160,sizeof(utxoaddr->rmd160),utxoaddr); if ( utxoaddr == 0 && createflag != 0 ) { utxoaddr = calloc(1,sizeof(*utxoaddr)); ++coin->utxoaddrind; utxoaddr->hdrsi = hdrsi; utxoaddr->pkind = pkind; if ( coin->utxoaddrtable != 0 && coin->utxoaddroffsets != 0 ) { utxoaddr->searchedhist = 1; utxoaddr->histbalance = iguana_utxoaddrtablefind(coin,hdrsi,pkind,rmd160); } memcpy(utxoaddr->rmd160,rmd160,sizeof(utxoaddr->rmd160)); HASH_ADD_KEYPTR(hh,coin->utxoaddrs,utxoaddr->rmd160,sizeof(utxoaddr->rmd160),utxoaddr); if ( prevp != 0 ) { utxoaddr->hh.prev = *prevp; if ( *prevp != 0 ) (*prevp)->hh.next = utxoaddr; *prevp = utxoaddr; } HASH_FIND(hh,coin->utxoaddrs,rmd160,sizeof(utxoaddr->rmd160),utxoaddr); if ( utxoaddr == 0 ) { int32_t i; for (i=0; i<20; i++) printf("%02x",utxoaddr->rmd160[i]); bitcoin_address(coinaddr,coin->chain->pubtype,utxoaddr->rmd160,sizeof(utxoaddr->rmd160)); printf(" %d of %d: %s %.8f\n",coin->utxoaddrind,coin->utxodatasize,coinaddr,dstr(utxoaddr->histbalance)); printf("failed to find just added %d of %d\n",coin->utxoaddrind,coin->utxodatasize); } } return(utxoaddr); } uint64_t iguana_bundle_unspents(struct iguana_info *coin,struct iguana_bundle *bp,struct iguana_utxoaddr **prevp) { struct iguana_utxoaddr *utxoaddr; uint32_t unspentind,pkind; struct iguana_ramchaindata *rdata=0; struct iguana_pkhash *P; struct iguana_unspent *U; struct iguana_utxo *U2=0; uint64_t value,balance = 0; if ( bp == 0 || (rdata= bp->ramchain.H.data) == 0 || (U2= bp->ramchain.Uextras) == 0 ) { printf("missing ptr bp.%p rdata.%p U2.%p\n",bp,rdata,U2); return(0); } U = RAMCHAIN_PTR(rdata,Uoffset); P = RAMCHAIN_PTR(rdata,Poffset); for (unspentind=1; unspentindnumunspents; unspentind++) { value = U[unspentind].value; //printf("[%d] u%d: (p%u %.8f) from.%d lock.%d prev.%u spent.%d\n",bp->hdrsi,unspentind,U[unspentind].pkind,dstr(value),U2[unspentind].fromheight,U2[unspentind].lockedflag,U2[unspentind].prevunspentind,U2[unspentind].spentflag); if ( U2[unspentind].fromheight == 0 && U2[unspentind].lockedflag == 0 && U2[unspentind].prevunspentind == 0 && U2[unspentind].spentflag == 0 && value != 0 ) { if ( value <= 0 ) printf("[%d] u%u negative value %.8f??\n",bp->hdrsi,unspentind,dstr(value)); else { balance += value; if ( (pkind= U[unspentind].pkind) < rdata->numpkinds && pkind > 0 ) { if ( (utxoaddr= iguana_utxoaddrfind(1,coin,bp->hdrsi,pkind,P[pkind].rmd160,prevp)) != 0 ) { //printf("%.8f ",dstr(value)); utxoaddr->histbalance += value; } else printf("cant find pkind.%u for unspentind.%u hdrsi.%d\n",pkind,unspentind,bp->hdrsi); } else printf("illegal pkind.%u for unspentind.%u hdrsi.%d\n",pkind,unspentind,bp->hdrsi); } } // else printf("[%d] u%u spent %.8f\n",bp->hdrsi,unspentind,dstr(value)); } printf("[%d %.8f] ",bp->hdrsi,dstr(balance)); return(balance); } static int _utxoaddr_cmp(const void *a,const void *b) { #define item_a ((uint8_t *)a) #define item_b ((uint8_t *)b) uint16_t hdrsi_a,hdrsi_b; uint32_t pkind_a,pkind_b; iguana_rwnum(0,&item_a[0],sizeof(hdrsi_a),&hdrsi_a); iguana_rwnum(0,&item_a[20],sizeof(pkind_a),&pkind_a); iguana_rwnum(0,&item_b[0],sizeof(hdrsi_b),&hdrsi_b); iguana_rwnum(0,&item_b[20],sizeof(pkind_b),&pkind_b); if ( hdrsi_b > hdrsi_a ) return(1); else if ( hdrsi_b < hdrsi_a ) return(-1); else { if ( pkind_b > pkind_a ) return(1); else if ( pkind_b < pkind_a ) return(-1); else return(0); } #undef item_a #undef item_b } int32_t iguana_utxoaddr_save(struct iguana_info *coin,char *fname,uint64_t balance,uint32_t *counts,uint32_t *offsets,uint8_t *table) { FILE *fp; bits256 hash; int32_t retval = -1; if ( (fp= fopen(fname,"wb")) != 0 ) { fwrite(&balance,1,sizeof(balance),fp); fwrite(&counts[0xffff],1,sizeof(counts[0xffff]),fp); fwrite(&coin->utxoaddrind,1,sizeof(coin->utxoaddrind),fp); vcalc_sha256cat(hash.bytes,(void *)offsets,(int32_t)(0x10000 * sizeof(*offsets)),table,(int32_t)((coin->utxoaddrind+1) * UTXOADDR_ITEMSIZE)); if ( fwrite(hash.bytes,1,sizeof(hash),fp) == sizeof(hash) ) { if ( fwrite(offsets,1,0x10000 * sizeof(*offsets),fp) == 0x10000 * sizeof(*offsets) ) { if ( fwrite(table,1,(coin->utxoaddrind+1) * UTXOADDR_ITEMSIZE,fp) != (coin->utxoaddrind+1) * UTXOADDR_ITEMSIZE ) printf("error writing %s table\n",fname); else retval = 0; } else printf("error writing %s offsets\n",fname); } else printf("error writing %s hash\n",fname); fclose(fp); } else printf("error creating %s\n",fname); return(retval); } int32_t iguana_utxoaddr_map(struct iguana_info *coin,char *fname) { uint32_t ind,total=0,offset,size=0,last=0,lastcount=0,count,prevoffset=0; if ( (coin->utxoaddrfileptr= OS_mapfile(fname,&coin->utxoaddrfilesize,0)) != 0 && coin->utxoaddrfilesize > sizeof(bits256)+0x10000*sizeof(*coin->utxoaddroffsets) ) { memcpy(&coin->histbalance,coin->utxoaddrfileptr,sizeof(coin->histbalance)); memcpy(&last,(void *)((long)coin->utxoaddrfileptr+sizeof(uint64_t)),sizeof(last)); memcpy(&coin->utxoaddrind,(void *)((long)coin->utxoaddrfileptr+sizeof(uint64_t)+sizeof(uint32_t)),sizeof(coin->utxoaddrind)); memcpy(&coin->utxoaddrhash.bytes,(void *)((long)coin->utxoaddrfileptr+sizeof(uint64_t)+2*sizeof(uint32_t)),sizeof(coin->utxoaddrhash)); coin->utxoaddroffsets = (void *)((long)coin->utxoaddrfileptr + sizeof(uint64_t) + 2*sizeof(uint32_t) + sizeof(bits256)); for (ind=total=count=0; ind<0x10000; ind++) { if ( (offset= coin->utxoaddroffsets[ind]) != 0 ) { count = offset - prevoffset; prevoffset = offset; total += count; } } size = (uint32_t)((total+1)*UTXOADDR_ITEMSIZE); size += sizeof(uint64_t) + 2*sizeof(uint32_t) + sizeof(bits256); size += 0x10000 * sizeof(*coin->utxoaddroffsets); if ( size <= coin->utxoaddrfilesize ) { lastcount = (uint32_t)(coin->utxoaddrfilesize - size); if ( (lastcount % UTXOADDR_ITEMSIZE) == 0 ) { lastcount /= UTXOADDR_ITEMSIZE; coin->utxoaddrlastcount = lastcount; coin->utxoaddrtable = (void *)&coin->utxoaddroffsets[0x10000]; //iguana_utxoaddr_purge(coin); } } printf("%.8f LASTCOUNT %d vs total %d, last %d vs lastcount %d, size.%d %ld\n",dstr(coin->histbalance),coin->utxoaddrlastcount,total,last,lastcount,size,coin->utxoaddrfilesize); return(total + 1 + lastcount); } return(0); } void iguana_utxoaddr_purge(struct iguana_info *coin) { struct iguana_utxoaddr *utxoaddr,*tmp; if ( coin->utxoaddrs != 0 ) { printf("free %s utxoaddrs\n",coin->symbol); HASH_ITER(hh,coin->utxoaddrs,utxoaddr,tmp) { if ( utxoaddr != 0 ) { HASH_DELETE(hh,coin->utxoaddrs,utxoaddr); free(utxoaddr); } } coin->utxoaddrs = 0; } if ( coin->utxoaddrfileptr != 0 ) { OS_releasemap(coin->utxoaddrfileptr,coin->utxoaddrfilesize); coin->utxoaddrfileptr = 0; coin->utxoaddrtable = 0; coin->utxoaddroffsets = 0; } memset(coin->utxoaddrhash.bytes,0,sizeof(coin->utxoaddrhash)); coin->histbalance = 0; coin->utxoaddrlastcount = 0; coin->utxoaddrind = 0; coin->utxoaddrfilesize = 0; } int32_t iguana_utxoaddr_check(struct supernet_info *myinfo,struct iguana_info *coin,int32_t lastheight,struct iguana_outpoint *unspents,int32_t max,struct iguana_utxoaddr *utxoaddr) { static int32_t good,bad; char coinaddr[64]; uint64_t sum,checkbalance; int32_t iter,i,numunspents = 0; sum = 0; for (iter=0; iter<2; iter++) { bitcoin_address(coinaddr,iter == 0 ? coin->chain->pubtype : coin->chain->p2shtype,utxoaddr->rmd160,sizeof(utxoaddr->rmd160)); numunspents += iguana_RTaddr_unspents(myinfo,coin,&sum,&unspents[numunspents],max-numunspents,coinaddr,0,lastheight,0); if ( sum == utxoaddr->histbalance ) { checkbalance = iguana_utxoaddrtablefind(coin,0,0,utxoaddr->rmd160); if ( checkbalance != sum ) printf("%s checkbalance %.8f vs sum %.8f\n",coinaddr,dstr(checkbalance),dstr(sum)); break; } } if ( sum != utxoaddr->histbalance || checkbalance != sum ) { bad++; for (i=0; irmd160[i]); bitcoin_address(coinaddr,coin->chain->pubtype,utxoaddr->rmd160,sizeof(utxoaddr->rmd160)); printf(" %s: sum %.8f != %.8f numunspents.%d diff %.8f\n",coinaddr,dstr(sum),dstr(utxoaddr->histbalance),numunspents,dstr(utxoaddr->histbalance)-dstr(sum)); return(-1); } good++; if ( ((good + bad) % 1000) == 0 ) printf("%s total %d utxoaddr validate good.%d bad.%d%s\n",coin->symbol,coin->utxoaddrind,good,bad,strcmp(coin->symbol,"BTC") == 0 ? " | (if this is taking too long, just exit and restart iguana)" : ""); return(0); } int32_t iguana_utxoaddr_validate(struct supernet_info *myinfo,struct iguana_info *coin,int32_t lastheight) { struct iguana_outpoint *unspents; uint8_t *item; struct iguana_bundle *bp; struct iguana_utxoaddr UA; int32_t i,num,max,ind,total,errs=0; if ( coin->utxoaddrtable == 0 ) { printf("no utxoaddrtable to validate?\n"); return(-1); } for (i=0; ibundlescount; i++) if ( (bp= coin->bundles[i]) != 0 && bp != coin->current ) { iguana_volatilespurge(coin,&bp->ramchain); /*sprintf(fname,"%s/%s/accounts/debits.%d",GLOBAL_DBDIR,coin->symbol,bp->bundleheight); OS_removefile(fname,0); sprintf(fname,"%s/%s/accounts/lastspends.%d",GLOBAL_DBDIR,coin->symbol,bp->bundleheight); OS_removefile(fname,0);*/ iguana_volatilesmap(myinfo,coin,&bp->ramchain); } total = 0; max = 1024 * 1024 * 1024; unspents = calloc(1,max); max /= sizeof(*unspents); memset(&UA,0,sizeof(UA)); for (ind=0; ind<0x10000; ind++) { if ( (num= iguana_utxotable_numinds(ind)) > 0 ) { for (i=0; iutxoaddrtable[(coin->utxoaddroffsets[ind] + i) * UTXOADDR_ITEMSIZE]; iguana_rwutxoaddr(0,ind,item,&UA); errs += iguana_utxoaddr_check(myinfo,coin,lastheight,unspents,max,&UA); total++; } } } free(unspents); printf("validate errs.%d\n",errs); return(errs); } uint64_t iguana_RTstart(struct supernet_info *myinfo,struct iguana_info *coin,int32_t height) { //struct iguana_block *block; coin->firstRTheight = height; iguana_RTreset(coin); iguana_RTpurge(coin,coin->firstRTheight); basilisk_unspents_update(myinfo,coin); return(coin->histbalance); } uint64_t iguana_utxoaddr_gen(struct supernet_info *myinfo,struct iguana_info *coin,int32_t maxheight) { char fname[1024],fname2[1024],coinaddr[64],str[65],checkaddr[64]; struct iguana_utxoaddr *utxoaddr,UA,*tmp,*last=0; uint16_t hdrsi; uint8_t *table,item[UTXOADDR_ITEMSIZE]; uint32_t *counts,*offsets,offset,n; int32_t total,errs=0,height=0,j,k,ind,tablesize=0; struct iguana_bundle *bp; struct iguana_ramchaindata *rdata=0; uint64_t checkbalance=0,balance = 0; for (hdrsi=0; hdrsibundlescount-1; hdrsi++) { if ( (bp= coin->bundles[hdrsi]) != 0 && bp->bundleheight < maxheight ) height = bp->bundleheight + bp->n; } sprintf(fname2,"%s/%s/utxoaddrs.%d",GLOBAL_DBDIR,coin->symbol,height), OS_portable_path(fname2); if ( iguana_utxoaddr_map(coin,fname2) != 0 ) { if ( 0 && strcmp("BTC",coin->symbol) != 0 ) errs = iguana_utxoaddr_validate(myinfo,coin,height); printf("nogen %s HIST BALANCE %s %.8f errs %d\n",fname2,bits256_str(str,coin->utxoaddrhash),dstr(coin->histbalance),errs); if ( errs == 0 && coin->histbalance > 0 && height > 0 ) return(iguana_RTstart(myinfo,coin,height)); } printf("utxoaddr_gen.%d\n",maxheight); iguana_utxoaddr_purge(coin); HASH_ITER(hh,coin->utxoaddrs,utxoaddr,tmp) { checkbalance += utxoaddr->histbalance; } printf("balance after purge %.8f\n",dstr(checkbalance)); for (hdrsi=0; hdrsibundlescount-1; hdrsi++) if ( (bp= coin->bundles[hdrsi]) != 0 && bp->bundleheight < maxheight && (rdata= bp->ramchain.H.data) != 0 ) { tablesize += rdata->numpkinds; } printf("allocate UTXOADDRS[%d]\n",tablesize); coin->utxodatasize = tablesize; coin->utxoaddrind = 0; for (hdrsi=0; hdrsibundlescount-1; hdrsi++) { if ( (bp= coin->bundles[hdrsi]) != 0 && bp->bundleheight < maxheight ) { iguana_volatilespurge(coin,&bp->ramchain); if ( iguana_volatilesmap(myinfo,coin,&bp->ramchain) != 0 ) printf("error mapping bundle.[%d]\n",hdrsi); else { balance += iguana_bundle_unspents(coin,bp,&last); fprintf(stderr,"(%d %.8f) ",hdrsi,dstr(balance)); height = bp->bundleheight + bp->n; } } } sprintf(fname,"%s/%s/utxoaddrs",GLOBAL_DBDIR,coin->symbol), OS_portable_path(fname); fprintf(stderr,"%d bundles for iguana_utxoaddr_gen.[%d] max.%d ht.%d\n",hdrsi,coin->utxoaddrind,coin->utxodatasize,maxheight); counts = calloc(0x10000,sizeof(*counts)); checkbalance = 0; HASH_ITER(hh,coin->utxoaddrs,utxoaddr,tmp) { if ( utxoaddr->histbalance > 0 ) { checkbalance += utxoaddr->histbalance; ind = utxoaddr->rmd160[0] + ((uint32_t)utxoaddr->rmd160[1] << 8); counts[ind]++; } else printf("error neg or zero balance %.8f\n",dstr(utxoaddr->histbalance)); } for (ind=total=0; ind<0x10000; ind++) total += counts[ind]; printf("checkbalance %.8f vs %.8f, total %d\n",dstr(checkbalance),dstr(balance),total); if ( checkbalance == balance ) { table = calloc(coin->utxoaddrind+1,UTXOADDR_ITEMSIZE); offsets = calloc(0x10000,sizeof(*offsets)); offset = 0; for (ind=0; ind<0x10000; ind++) { n = counts[ind]; offsets[ind] = offset; counts[ind] = 0; offset += n; } printf("total %d offset %d\n",total,offset); total = 0; HASH_ITER(hh,coin->utxoaddrs,utxoaddr,tmp) { if ( utxoaddr->histbalance > 0 ) { bitcoin_address(coinaddr,coin->chain->pubtype,utxoaddr->rmd160,sizeof(utxoaddr->rmd160)); memset(item,0,UTXOADDR_ITEMSIZE); ind = utxoaddr->rmd160[0] + ((uint32_t)utxoaddr->rmd160[1] << 8); iguana_rwutxoaddr(1,ind,item,utxoaddr); memcpy(&table[(offsets[ind] + counts[ind]) * UTXOADDR_ITEMSIZE],item,UTXOADDR_ITEMSIZE); iguana_rwutxoaddr(0,ind,&table[(offsets[ind] + counts[ind]) * UTXOADDR_ITEMSIZE],&UA); iguana_rwutxoaddr(1,ind,item,&UA); bitcoin_address(checkaddr,coin->chain->pubtype,UA.rmd160,sizeof(UA.rmd160)); if ( strcmp(checkaddr,coinaddr) != 0 ) printf("rw coinaddr error %s != %s\n",coinaddr,checkaddr); //else printf("%d: ind.%04x %s %.8f %.8f %d\n",total,ind,coinaddr,dstr(UA.histbalance),dstr(utxoaddr->histbalance),counts[ind]); total++; if ( memcmp(&table[(offsets[ind] + counts[ind]) * UTXOADDR_ITEMSIZE],item,UTXOADDR_ITEMSIZE) != 0 ) printf("rwutxoaddr cmp error\n"); counts[ind]++; } else printf("error neg or zero balance %.8f\n",dstr(utxoaddr->histbalance)); } offset = 1; for (ind=0; ind<0x10000; ind++) offset += counts[ind]; if ( offset == coin->utxoaddrind+1 ) { for (ind=0; ind<0x10000; ind++) { if ( counts[ind] > 0 ) { qsort(&table[offsets[ind] * UTXOADDR_ITEMSIZE],counts[ind],UTXOADDR_ITEMSIZE,_utxoaddr_cmp); continue; for (j=0; jchain->pubtype,UA.rmd160,sizeof(UA.rmd160)); //printf(" [%4d] p%-5d %12.8f ind.%04x %d %s\n",UA.hdrsi,UA.pkind,dstr(UA.histbalance),ind,j,coinaddr); } } } if ( iguana_utxoaddr_save(coin,fname,balance,counts,offsets,table) == 0 ) { if ( OS_copyfile(fname,fname2,1) < 0 ) printf("error copying file %s to %s\n",fname,fname2); else { for (hdrsi=0; hdrsibundlescount-1; hdrsi++) { if ( (bp= coin->bundles[hdrsi]) != 0 && bp->bundleheight < maxheight ) bp->balancefinish = (uint32_t)time(NULL); } } } else printf("error saving %s\n",fname); } else printf("table has %d vs %d\n",offset,coin->utxoaddrind+1); free(offsets); free(table); iguana_utxoaddr_purge(coin); if ( iguana_utxoaddr_map(coin,fname) != 0 ) { printf("validating %s HIST BALANCE %s %.8f errs %d\n",fname2,bits256_str(str,coin->utxoaddrhash),dstr(coin->histbalance),errs); errs = 0;//iguana_utxoaddr_validate(myinfo,coin,height); printf("gen %s HIST BALANCE %s %.8f errs %d\n",fname2,bits256_str(str,coin->utxoaddrhash),dstr(coin->histbalance),errs); if ( errs != 0 || height == 0 ) { printf("delete bad utxoaddr files\n"); OS_removefile(fname,0); OS_removefile(fname2,0); } else return(iguana_RTstart(myinfo,coin,height)); } } free(counts); sprintf(fname,"%s/%s/balancecrc.%d",GLOBAL_DBDIR,coin->symbol,height/coin->chain->bundlesize - 1); OS_removefile(fname,0); sprintf(fname,"%s/%s/balancecrc.%d",GLOBAL_DBDIR,coin->symbol,height/coin->chain->bundlesize - 2); OS_removefile(fname,0); printf("return neg one remove %s\n",fname); return(0); } void iguana_utxoaddrs_purge(struct iguana_info *coin) { struct iguana_utxoaddr *utxoaddr,*tmp; coin->RTdebits = coin->RTdebits = 0; HASH_ITER(hh,coin->utxoaddrs,utxoaddr,tmp) { if ( utxoaddr != 0 ) { utxoaddr->histbalance = 0; // utxoaddr->RTcredits = utxoaddr->RTdebits = 0; } } }