You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
954 lines
37 KiB
954 lines
37 KiB
|
|
/******************************************************************************
|
|
* Copyright © 2014-2017 The SuperNET Developers. *
|
|
* *
|
|
* See the AUTHORS, DEVELOPER-AGREEMENT and LICENSE files at *
|
|
* the top-level directory of this distribution for the individual copyright *
|
|
* holder information and the developer policies on copyright and licensing. *
|
|
* *
|
|
* Unless otherwise agreed in a custom licensing agreement, no part of the *
|
|
* SuperNET software, including this file may be copied, modified, propagated *
|
|
* or distributed except according to the terms contained in the LICENSE file *
|
|
* *
|
|
* Removal or modification of this copyright notice is prohibited. *
|
|
* *
|
|
******************************************************************************/
|
|
//
|
|
// LP_utxo.c
|
|
// marketmaker
|
|
//
|
|
|
|
// listunspent: valid for local node, mostly valid for electrum
|
|
|
|
// full node + electrum for external listunspent, gettxout to validate
|
|
// pruned node + electrum for external listunspent, gettxout to validate
|
|
// full node, network for external listunspent, gettxout to validate
|
|
// pruned node, network for external listunspent, gettxout to validate
|
|
// electrum only, network for gettxout
|
|
|
|
// handle spurious errors
|
|
// handle invalid data
|
|
|
|
//REJECT KMD ccee27b53b52ca61bbc9fdc7de5feb0a12c14d4d92639414d372f002cc3d092f/v0 value.468169379 vs 468169380 ({"bestblock":"080400d4216c02d100004fd2f4f3505ddb507b643785e02703d3412feba39fb1","confirmations":2356,"value":4.68169380,"scriptPubKey":{"asm":"0224e31f93eff0cc30eaf0b2389fbc591085c0e122c4d11862c1729d090106c842 OP_CHECKSIG","hex":"210224e31f93eff0cc30eaf0b2389fbc591085c0e122c4d11862c1729d090106c842ac","reqSigs":1,"type":"pubkey","addresses":["RFssbc211PJdVy1bvcvAG5X2N4ovPAoy5o"]},"version":1,"coinbase":true})
|
|
|
|
uint64_t LP_value_extract(cJSON *obj,int32_t addinterest)
|
|
{
|
|
double val = 0.; uint64_t value = 0; int32_t electrumflag;
|
|
electrumflag = (jobj(obj,"tx_hash") != 0);
|
|
if ( electrumflag == 0 )
|
|
{
|
|
if ( (val= jdouble(obj,"amount")) < SMALLVAL )
|
|
val = jdouble(obj,"value");
|
|
value = (val + 0.0000000049) * SATOSHIDEN;
|
|
} else value = j64bits(obj,"value");
|
|
if ( value != 0 )
|
|
{
|
|
if ( addinterest != 0 && jobj(obj,"interest") != 0 )
|
|
value += (jdouble(obj,"interest") * SATOSHIDEN);
|
|
}
|
|
return(value);
|
|
}
|
|
|
|
int32_t LP_destaddr(char *destaddr,cJSON *item)
|
|
{
|
|
int32_t m,retval = -1; cJSON *addresses,*skey; char *addr;
|
|
if ( (skey= jobj(item,"scriptPubKey")) != 0 && (addresses= jarray(&m,skey,"addresses")) != 0 )
|
|
{
|
|
item = jitem(addresses,0);
|
|
if ( (addr= jstr(item,0)) != 0 )
|
|
{
|
|
safecopy(destaddr,addr,64);
|
|
retval = 0;
|
|
}
|
|
//printf("item.(%s) -> dest.(%s)\n",jprint(item,0),destaddr);
|
|
}
|
|
return(retval);
|
|
}
|
|
|
|
int32_t LP_txdestaddr(char *destaddr,bits256 txid,int32_t vout,cJSON *txobj)
|
|
{
|
|
int32_t n,retval = -1; cJSON *vouts;
|
|
if ( (vouts= jarray(&n,txobj,"vout")) != 0 && vout < n )
|
|
retval = LP_destaddr(destaddr,jitem(vouts,vout));
|
|
return(retval);
|
|
}
|
|
|
|
struct LP_address *_LP_address(struct iguana_info *coin,char *coinaddr)
|
|
{
|
|
struct LP_address *ap = 0;
|
|
if ( (ap= _LP_addressfind(coin,coinaddr)) == 0 )
|
|
ap = _LP_addressadd(coin,coinaddr);
|
|
return(ap);
|
|
}
|
|
|
|
struct LP_address *LP_addressfind(struct iguana_info *coin,char *coinaddr)
|
|
{
|
|
struct LP_address *ap = 0;
|
|
if ( coin != 0 )
|
|
{
|
|
portable_mutex_lock(&coin->addrmutex);
|
|
ap = _LP_addressfind(coin,coinaddr);
|
|
portable_mutex_unlock(&coin->addrmutex);
|
|
}
|
|
return(ap);
|
|
}
|
|
|
|
struct LP_address *LP_address(struct iguana_info *coin,char *coinaddr)
|
|
{
|
|
struct LP_address *ap = 0;
|
|
if ( coin != 0 )
|
|
{
|
|
portable_mutex_lock(&coin->addrmutex);
|
|
ap = _LP_address(coin,coinaddr);
|
|
portable_mutex_unlock(&coin->addrmutex);
|
|
}
|
|
return(ap);
|
|
}
|
|
|
|
int32_t LP_address_minmax(uint64_t *minp,uint64_t *maxp,struct LP_address *ap)
|
|
{
|
|
struct LP_address_utxo *up,*tmp; int32_t n = 0;
|
|
*minp = *maxp = 0;
|
|
DL_FOREACH_SAFE(ap->utxos,up,tmp)
|
|
{
|
|
if ( up->spendheight <= 0 )
|
|
{
|
|
if ( up->U.value > *maxp )
|
|
*maxp = up->U.value;
|
|
if ( *minp == 0 || up->U.value < *minp )
|
|
*minp = up->U.value;
|
|
n++;
|
|
}
|
|
}
|
|
if ( 0 && n > 0 )
|
|
printf("n.%d %s min %.8f max %.8f\n",n,ap->coinaddr,dstr(*minp),dstr(*maxp));
|
|
return(n);
|
|
}
|
|
|
|
struct LP_utxoinfo *LP_allocated(bits256 txid,int32_t vout)
|
|
{
|
|
struct LP_utxoinfo *utxo;
|
|
if ( (utxo= _LP_utxofind(0,txid,vout)) != 0 && LP_isavailable(utxo) == 0 )
|
|
{
|
|
char str[65]; printf("%s/v%d not available\n",bits256_str(str,txid),vout);
|
|
return(utxo);
|
|
}
|
|
if ( (utxo= _LP_utxo2find(0,txid,vout)) != 0 && LP_isavailable(utxo) == 0 )
|
|
{
|
|
char str[65]; printf("%s/v%d not available2\n",bits256_str(str,txid),vout);
|
|
return(utxo);
|
|
}
|
|
if ( (utxo= _LP_utxofind(1,txid,vout)) != 0 && LP_isavailable(utxo) == 0 )
|
|
{
|
|
char str[65]; printf("%s/v%d not available\n",bits256_str(str,txid),vout);
|
|
return(utxo);
|
|
}
|
|
if ( (utxo= _LP_utxo2find(1,txid,vout)) != 0 && LP_isavailable(utxo) == 0 )
|
|
{
|
|
char str[65]; printf("%s/v%d not available2\n",bits256_str(str,txid),vout);
|
|
return(utxo);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int32_t LP_address_utxo_ptrs(int32_t iambob,struct LP_address_utxo **utxos,int32_t max,struct LP_address *ap,char *coinaddr)
|
|
{
|
|
struct LP_address_utxo *up,*tmp; int32_t n = 0;
|
|
//printf("LP_address_utxo_ptrs for (%s).(%s)\n",ap->coinaddr,coinaddr);
|
|
if ( strcmp(ap->coinaddr,coinaddr) != 0 )
|
|
printf("UNEXPECTED coinaddr mismatch (%s) != (%s)\n",ap->coinaddr,coinaddr);
|
|
portable_mutex_lock(&LP_utxomutex);
|
|
DL_FOREACH_SAFE(ap->utxos,up,tmp)
|
|
{
|
|
//char str[65]; printf("LP_address_utxo_ptrs %s n.%d %.8f %s v%d spendheight.%d allocated.%p\n",ap->coinaddr,n,dstr(up->U.value),bits256_str(str,up->U.txid),up->U.vout,up->spendheight,LP_allocated(up->U.txid,up->U.vout));
|
|
if ( up->spendheight <= 0 )
|
|
{
|
|
if ( LP_allocated(up->U.txid,up->U.vout) == 0 )
|
|
{
|
|
utxos[n++] = up;
|
|
if ( n >= max )
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
portable_mutex_unlock(&LP_utxomutex);
|
|
//printf("return n.%d\n",n);
|
|
return(n);
|
|
}
|
|
|
|
struct LP_address_utxo *LP_address_utxofind(struct iguana_info *coin,char *coinaddr,bits256 txid,int32_t vout)
|
|
{
|
|
struct LP_address *ap; struct LP_address_utxo *up,*tmp;
|
|
//printf("LP_address_utxofind %s add addr.%s\n",coin->symbol,coinaddr);
|
|
if ( (ap= _LP_address(coin,coinaddr)) != 0 )
|
|
{
|
|
DL_FOREACH_SAFE(ap->utxos,up,tmp)
|
|
{
|
|
if ( vout == up->U.vout && bits256_cmp(up->U.txid,txid) == 0 )
|
|
return(up);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int32_t LP_address_utxoadd(struct iguana_info *coin,char *coinaddr,bits256 txid,int32_t vout,uint64_t value,int32_t height,int32_t spendheight)
|
|
{
|
|
struct LP_address *ap; cJSON *txobj; struct LP_address_utxo *up,*tmp; int32_t flag,retval = 0; char str[65];
|
|
if ( coin == 0 )
|
|
return(0);
|
|
if ( spendheight > 0 ) // dont autocreate entries for spends we dont care about
|
|
ap = LP_addressfind(coin,coinaddr);
|
|
else ap = LP_address(coin,coinaddr);
|
|
//printf("%s add addr.%s ht.%d ap.%p\n",coin->symbol,coinaddr,height,ap);
|
|
if ( ap != 0 )
|
|
{
|
|
flag = 0;
|
|
DL_FOREACH_SAFE(ap->utxos,up,tmp)
|
|
{
|
|
if ( vout == up->U.vout && bits256_cmp(up->U.txid,txid) == 0 )
|
|
{
|
|
flag = 1;
|
|
if ( height > 0 && up->U.height != height && up->SPV <= 0 )
|
|
{
|
|
if ( up->U.height > 0 )
|
|
printf("%s SPV.%d update %s/v%d up->U.height %d <- %d\n",coin->symbol,up->SPV,bits256_str(str,up->U.txid),up->U.vout,up->U.height,height);
|
|
up->U.height = height, flag |= 2;
|
|
}
|
|
if ( spendheight > 0 && up->spendheight != spendheight )
|
|
up->spendheight = spendheight, flag |= 4;
|
|
if ( up->U.value == 0 && up->U.value != value )
|
|
up->U.value = value, flag |= 8;
|
|
//printf("found >>>>>>>>>> %s %s %s/v%d ht.%d %.8f\n",coin->symbol,coinaddr,bits256_str(str,txid),vout,height,dstr(value));
|
|
break;
|
|
}
|
|
}
|
|
if ( flag == 0 )
|
|
{
|
|
if ( coin->electrum == 0 )
|
|
{
|
|
if ( (txobj= LP_gettxout(coin->symbol,coinaddr,txid,vout)) == 0 )
|
|
{
|
|
//printf("prevent utxoadd since gettxout %s %s %s/v%d missing\n",coin->symbol,coinaddr,bits256_str(str,txid),vout);
|
|
return(0);
|
|
} else free_json(txobj);
|
|
}
|
|
up = calloc(1,sizeof(*up));
|
|
up->U.txid = txid;
|
|
up->U.vout = vout;
|
|
up->U.height = height;
|
|
up->U.value = value;
|
|
up->spendheight = spendheight;
|
|
portable_mutex_lock(&coin->addrmutex);
|
|
DL_APPEND(ap->utxos,up);
|
|
portable_mutex_unlock(&coin->addrmutex);
|
|
retval = 1;
|
|
if ( 0 && height > 0 && strcmp("REVS",coin->symbol) == 0 )
|
|
printf("ADD UTXO >> %s %s %s/v%d ht.%d %.8f\n",coin->symbol,coinaddr,bits256_str(str,txid),vout,height,dstr(value));
|
|
}
|
|
} // else printf("cant get ap %s %s\n",coin->symbol,coinaddr);
|
|
//printf("done %s add addr.%s ht.%d\n",coin->symbol,coinaddr,height);
|
|
return(retval);
|
|
}
|
|
|
|
cJSON *LP_address_item(struct iguana_info *coin,struct LP_address_utxo *up,int32_t electrumret)
|
|
{
|
|
cJSON *item = cJSON_CreateObject();
|
|
if ( electrumret == 0 )
|
|
{
|
|
jaddbits256(item,"txid",up->U.txid);
|
|
jaddnum(item,"vout",up->U.vout);
|
|
jaddnum(item,"confirmations",LP_getheight(coin) - up->U.height + 1);
|
|
jaddnum(item,"amount",dstr(up->U.value));
|
|
jaddstr(item,"scriptPubKey","");
|
|
}
|
|
else
|
|
{
|
|
jaddbits256(item,"tx_hash",up->U.txid);
|
|
jaddnum(item,"tx_pos",up->U.vout);
|
|
jaddnum(item,"height",up->U.height);
|
|
jadd64bits(item,"value",up->U.value);
|
|
}
|
|
return(item);
|
|
}
|
|
|
|
uint64_t _LP_unspents_metric(uint64_t total,int32_t n) { return((total<<16) | (n & 0xffff)); }
|
|
|
|
bits256 iguana_merkle(bits256 *tree,int32_t txn_count)
|
|
{
|
|
int32_t i,n=0,prev; uint8_t serialized[sizeof(bits256) * 2];
|
|
if ( txn_count == 1 )
|
|
return(tree[0]);
|
|
prev = 0;
|
|
while ( txn_count > 1 )
|
|
{
|
|
if ( (txn_count & 1) != 0 )
|
|
tree[prev + txn_count] = tree[prev + txn_count-1], txn_count++;
|
|
n += txn_count;
|
|
for (i=0; i<txn_count; i+=2)
|
|
{
|
|
iguana_rwbignum(1,serialized,sizeof(*tree),tree[prev + i].bytes);
|
|
iguana_rwbignum(1,&serialized[sizeof(*tree)],sizeof(*tree),tree[prev + i + 1].bytes);
|
|
tree[n + (i >> 1)] = bits256_doublesha256(0,serialized,sizeof(serialized));
|
|
}
|
|
prev = n;
|
|
txn_count >>= 1;
|
|
}
|
|
return(tree[n]);
|
|
}
|
|
|
|
bits256 validate_merkle(int32_t pos,bits256 txid,cJSON *proofarray,int32_t proofsize)
|
|
{
|
|
int32_t i; uint8_t serialized[sizeof(bits256) * 2]; bits256 hash,proof;
|
|
hash = txid;
|
|
for (i=0; i<proofsize; i++)
|
|
{
|
|
proof = jbits256i(proofarray,i);
|
|
if ( (pos & 1) == 0 )
|
|
{
|
|
iguana_rwbignum(1,&serialized[0],sizeof(hash),hash.bytes);
|
|
iguana_rwbignum(1,&serialized[sizeof(hash)],sizeof(proof),proof.bytes);
|
|
}
|
|
else
|
|
{
|
|
iguana_rwbignum(1,&serialized[0],sizeof(proof),proof.bytes);
|
|
iguana_rwbignum(1,&serialized[sizeof(hash)],sizeof(hash),hash.bytes);
|
|
}
|
|
hash = bits256_doublesha256(0,serialized,sizeof(serialized));
|
|
pos >>= 1;
|
|
}
|
|
return(hash);
|
|
}
|
|
|
|
int32_t LP_merkleproof(struct iguana_info *coin,struct electrum_info *ep,bits256 txid,int32_t height)
|
|
{
|
|
cJSON *merkobj,*merkles,*hdrobj; bits256 roothash,merkleroot; int32_t m,SPV = 0;
|
|
if ( (merkobj= electrum_getmerkle(coin->symbol,ep,&merkobj,txid,height)) != 0 )
|
|
{
|
|
char str[65],str2[65],str3[65];
|
|
SPV = -1;
|
|
memset(roothash.bytes,0,sizeof(roothash));
|
|
if ( (merkles= jarray(&m,merkobj,"merkle")) != 0 )
|
|
{
|
|
roothash = validate_merkle(jint(merkobj,"pos"),txid,merkles,m);
|
|
if ( (hdrobj= electrum_getheader(coin->symbol,ep,&hdrobj,height)) != 0 )
|
|
{
|
|
if ( jobj(hdrobj,"merkle_root") != 0 )
|
|
{
|
|
merkleroot = jbits256(hdrobj,"merkle_root");
|
|
if ( bits256_cmp(merkleroot,roothash) == 0 )
|
|
{
|
|
SPV = height;
|
|
//printf("validated MERK %s ht.%d -> %s root.(%s)\n",bits256_str(str,up->U.txid),up->U.height,jprint(merkobj,0),bits256_str(str2,roothash));
|
|
}
|
|
else printf("ERROR MERK %s ht.%d -> %s root.(%s) vs %s (%s)\n",bits256_str(str,txid),height,jprint(merkobj,0),bits256_str(str2,roothash),bits256_str(str3,merkleroot),jprint(hdrobj,0));
|
|
} else SPV = 0;
|
|
free_json(hdrobj);
|
|
} else printf("couldnt get header for ht.%d\n",height);
|
|
}
|
|
if ( SPV < 0 )
|
|
printf("MERKLE DIDNT VERIFY.(%s)\n",jprint(merkobj,0));
|
|
free_json(merkobj);
|
|
}
|
|
return(SPV);
|
|
}
|
|
|
|
cJSON *LP_address_utxos(struct iguana_info *coin,char *coinaddr,int32_t electrumret)
|
|
{
|
|
cJSON *array,*item; int32_t n; uint64_t total; struct LP_address *ap=0,*atmp; struct LP_address_utxo *up,*tmp; cJSON *txobj; struct electrum_info *ep,*backupep=0;
|
|
array = cJSON_CreateArray();
|
|
if ( coinaddr != 0 && coinaddr[0] != 0 )
|
|
{
|
|
if ( (ep= coin->electrum) != 0 )
|
|
{
|
|
if ( (backupep= ep->prev) == 0 )
|
|
backupep = ep;
|
|
}
|
|
//portable_mutex_lock(&coin->addrmutex);
|
|
if ( (ap= _LP_addressfind(coin,coinaddr)) != 0 )
|
|
{
|
|
total = n = 0;
|
|
DL_FOREACH_SAFE(ap->utxos,up,tmp)
|
|
{
|
|
if ( up->spendheight <= 0 && up->U.height > 0 )
|
|
{
|
|
if ( coin->electrum == 0 )
|
|
{
|
|
if ( (txobj= LP_gettxout(coin->symbol,coinaddr,up->U.txid,up->U.vout)) == 0 )
|
|
up->spendheight = 1;
|
|
else free_json(txobj);
|
|
}
|
|
if ( up->spendheight <= 0 )
|
|
{
|
|
if ( up->SPV >= 0 )
|
|
{
|
|
jaddi(array,LP_address_item(coin,up,electrumret));
|
|
n++;
|
|
total += up->U.value;
|
|
}
|
|
}
|
|
//printf("new array %s\n",jprint(array,0));
|
|
}
|
|
}
|
|
ap->total = total;
|
|
ap->n = n;
|
|
}
|
|
//portable_mutex_unlock(&coin->addrmutex);
|
|
}
|
|
else
|
|
{
|
|
HASH_ITER(hh,coin->addresses,ap,atmp)
|
|
{
|
|
if ( ap->total > 0 && ap->n > 0 )
|
|
{
|
|
item = cJSON_CreateObject();
|
|
jadd64bits(item,ap->coinaddr,_LP_unspents_metric(ap->total,ap->n));
|
|
jaddi(array,item);
|
|
}
|
|
}
|
|
}
|
|
//printf("%s %s utxos.(%s) ap.%p\n",coin->symbol,coinaddr,jprint(array,0),ap);
|
|
return(array);
|
|
}
|
|
|
|
cJSON *LP_address_balance(struct iguana_info *coin,char *coinaddr,int32_t electrumret)
|
|
{
|
|
cJSON *array,*retjson; int32_t i,n; uint64_t balance = 0;
|
|
if ( (array= LP_address_utxos(coin,coinaddr,1)) != 0 )
|
|
{
|
|
if ( (n= cJSON_GetArraySize(array)) > 0 )
|
|
{
|
|
for (i=0; i<n; i++)
|
|
balance += j64bits(jitem(array,i),"value");
|
|
}
|
|
}
|
|
retjson = cJSON_CreateObject();
|
|
jaddstr(retjson,"result","success");
|
|
jaddstr(retjson,"coin",coin->symbol);
|
|
jaddstr(retjson,"address",coinaddr);
|
|
jaddnum(retjson,"balance",dstr(balance));
|
|
return(retjson);
|
|
}
|
|
|
|
void LP_postutxos(char *symbol,char *coinaddr)
|
|
{
|
|
bits256 zero; struct iguana_info *coin; cJSON *array,*reqjson = cJSON_CreateObject();
|
|
if ( (coin= LP_coinfind(symbol)) != 0 && (array= LP_address_utxos(coin,coinaddr,1)) != 0 )
|
|
{
|
|
//printf("LP_postutxos pubsock.%d %s %s\n",pubsock,symbol,coin->smartaddr);
|
|
if ( cJSON_GetArraySize(array) == 0 )
|
|
free_json(array);
|
|
else
|
|
{
|
|
memset(zero.bytes,0,sizeof(zero));
|
|
jaddstr(reqjson,"method","postutxos");
|
|
jaddstr(reqjson,"coin",symbol);
|
|
jaddstr(reqjson,"coinaddr",coinaddr);
|
|
jadd(reqjson,"utxos",array);
|
|
//printf("post (%s) -> %d\n",msg,LP_mypubsock);
|
|
LP_reserved_msg(symbol,symbol,zero,jprint(reqjson,1));
|
|
}
|
|
}
|
|
}
|
|
|
|
int32_t LP_unspents_array(struct iguana_info *coin,char *coinaddr,cJSON *array)
|
|
{
|
|
int32_t i,n,v,ht,errs,height,count=0; uint64_t value,val; cJSON *item,*txobj; bits256 txid;
|
|
if ( (n= cJSON_GetArraySize(array)) <= 0 )
|
|
return(0);
|
|
//printf("%s %s LP_unspents.(%s)\n",coin->symbol,coinaddr,jprint(array,0));
|
|
for (i=0; i<n; i++)
|
|
{
|
|
errs = 0;
|
|
item = jitem(array,i);
|
|
txid = jbits256(item,"tx_hash");
|
|
v = jint(item,"tx_pos");
|
|
height = jint(item,"height");
|
|
val = j64bits(item,"value");
|
|
if ( coin->electrum == 0 && (txobj= LP_gettxout(coin->symbol,coinaddr,txid,v)) != 0 )
|
|
{
|
|
value = LP_value_extract(txobj,0);
|
|
if ( value != 0 && value != val )
|
|
{
|
|
char str[65]; printf("REJECT %s %s/v%d value.%llu vs %llu (%s)\n",coin->symbol,bits256_str(str,txid),v,(long long)value,(long long)val,jprint(txobj,0));
|
|
errs++;
|
|
}
|
|
ht = LP_txheight(coin,txid);
|
|
//if ( coin->height != 0 )
|
|
// ht = LP_getheight(coin) - jint(txobj,"confirmations") + 1;
|
|
//else ht = 0;
|
|
/*if ( ht != 0 && ht < height-2 )
|
|
{
|
|
printf("REJECT %s %s/v%d ht.%d vs %d confs.%d (%s)\n",symbol,bits256_str(str,txid),v,ht,height,jint(txobj,"confirmations"),jprint(item,0));
|
|
errs++;
|
|
}*/
|
|
free_json(txobj);
|
|
}
|
|
if ( errs == 0 )
|
|
{
|
|
//printf("from LP_unspents_array\n");
|
|
LP_address_utxoadd(coin,coinaddr,txid,v,val,height,-1);
|
|
count++;
|
|
}
|
|
}
|
|
return(count);
|
|
}
|
|
|
|
char *LP_postedutxos(cJSON *argjson)
|
|
{
|
|
int32_t n; char *symbol,*coinaddr; struct LP_address *ap; struct iguana_info *coin; cJSON *array;
|
|
//printf("posted.(%s)\n",jprint(argjson,0));
|
|
if ( (coinaddr= jstr(argjson,"coinaddr")) != 0 && (symbol= jstr(argjson,"coin")) != 0 && (coin= LP_coinfind(symbol)) != 0 ) // addsig
|
|
{
|
|
if ( coin->electrum == 0 || (ap= LP_addressfind(coin,coinaddr)) != 0 )
|
|
{
|
|
if ( (array= jarray(&n,argjson,"utxos")) != 0 )
|
|
LP_unspents_array(coin,coinaddr,array);
|
|
}
|
|
else if ( (array= electrum_address_listunspent(symbol,coin->electrum,&array,coinaddr,1)) != 0 )
|
|
free_json(array);
|
|
}
|
|
return(clonestr("{\"result\":\"success\"}"));
|
|
}
|
|
|
|
void LP_utxosetkey(uint8_t *key,bits256 txid,int32_t vout)
|
|
{
|
|
memcpy(key,txid.bytes,sizeof(txid));
|
|
memcpy(&key[sizeof(txid)],&vout,sizeof(vout));
|
|
}
|
|
|
|
struct LP_utxoinfo *_LP_utxofind(int32_t iambob,bits256 txid,int32_t vout)
|
|
{
|
|
struct LP_utxoinfo *utxo=0; uint8_t key[sizeof(txid) + sizeof(vout)];
|
|
/*if ( iambob != 0 )
|
|
{
|
|
static uint32_t counter;
|
|
if ( counter++ < 3 )
|
|
printf("_LP_utxofind deprecated iambob\n");
|
|
return(0);
|
|
}*/
|
|
LP_utxosetkey(key,txid,vout);
|
|
HASH_FIND(hh,G.LP_utxoinfos[iambob],key,sizeof(key),utxo);
|
|
return(utxo);
|
|
}
|
|
|
|
struct LP_utxoinfo *_LP_utxo2find(int32_t iambob,bits256 txid2,int32_t vout2)
|
|
{
|
|
struct LP_utxoinfo *utxo=0; uint8_t key2[sizeof(txid2) + sizeof(vout2)];
|
|
/*if ( iambob != 0 )
|
|
{
|
|
printf("_LP_utxo2find deprecated iambob\n");
|
|
return(0);
|
|
}*/
|
|
LP_utxosetkey(key2,txid2,vout2);
|
|
HASH_FIND(hh2,G.LP_utxoinfos2[iambob],key2,sizeof(key2),utxo);
|
|
return(utxo);
|
|
}
|
|
|
|
struct LP_utxoinfo *LP_utxofind(int32_t iambob,bits256 txid,int32_t vout)
|
|
{
|
|
struct LP_utxoinfo *utxo=0;
|
|
/*if ( iambob != 0 )
|
|
{
|
|
printf("LP_utxofind deprecated iambob\n");
|
|
return(0);
|
|
}*/
|
|
portable_mutex_lock(&LP_utxomutex);
|
|
utxo = _LP_utxofind(iambob,txid,vout);
|
|
portable_mutex_unlock(&LP_utxomutex);
|
|
return(utxo);
|
|
}
|
|
|
|
struct LP_utxoinfo *LP_utxo2find(int32_t iambob,bits256 txid2,int32_t vout2)
|
|
{
|
|
struct LP_utxoinfo *utxo=0;
|
|
/*if ( iambob != 0 )
|
|
{
|
|
printf("LP_utxo2find deprecated iambob\n");
|
|
return(0);
|
|
}*/
|
|
portable_mutex_lock(&LP_utxomutex);
|
|
utxo = _LP_utxo2find(iambob,txid2,vout2);
|
|
portable_mutex_unlock(&LP_utxomutex);
|
|
return(utxo);
|
|
}
|
|
|
|
struct LP_transaction *LP_transactionfind(struct iguana_info *coin,bits256 txid)
|
|
{
|
|
struct LP_transaction *tx;
|
|
portable_mutex_lock(&coin->txmutex);
|
|
HASH_FIND(hh,coin->transactions,txid.bytes,sizeof(txid),tx);
|
|
portable_mutex_unlock(&coin->txmutex);
|
|
return(tx);
|
|
}
|
|
|
|
struct LP_transaction *LP_transactionadd(struct iguana_info *coin,bits256 txid,int32_t height,int32_t numvouts,int32_t numvins)
|
|
{
|
|
struct LP_transaction *tx; int32_t i;
|
|
if ( (tx= LP_transactionfind(coin,txid)) == 0 )
|
|
{
|
|
//char str[65]; printf("%s ht.%d u.%u NEW TXID.(%s) vouts.[%d]\n",coin->symbol,height,timestamp,bits256_str(str,txid),numvouts);
|
|
//if ( bits256_nonz(txid) == 0 && tx->height == 0 )
|
|
// getchar();
|
|
tx = calloc(1,sizeof(*tx) + (sizeof(*tx->outpoints) * numvouts));
|
|
for (i=0; i<numvouts; i++)
|
|
tx->outpoints[i].spendvini = -1;
|
|
tx->height = height;
|
|
tx->numvouts = numvouts;
|
|
tx->numvins = numvins;
|
|
//tx->timestamp = timestamp;
|
|
tx->txid = txid;
|
|
portable_mutex_lock(&coin->txmutex);
|
|
HASH_ADD_KEYPTR(hh,coin->transactions,tx->txid.bytes,sizeof(tx->txid),tx);
|
|
portable_mutex_unlock(&coin->txmutex);
|
|
} // else printf("warning adding already existing txid %s\n",bits256_str(str,tx->txid));
|
|
return(tx);
|
|
}
|
|
|
|
uint64_t LP_txinterestvalue(uint64_t *interestp,char *destaddr,struct iguana_info *coin,bits256 txid,int32_t vout)
|
|
{
|
|
uint64_t interest,value = 0; cJSON *txobj;
|
|
*interestp = 0;
|
|
destaddr[0] = 0;
|
|
if ( (txobj= LP_gettxout(coin->symbol,destaddr,txid,vout)) != 0 )
|
|
{
|
|
if ( (value= LP_value_extract(txobj,0)) == 0 )
|
|
{
|
|
char str[65]; printf("%s LP_txvalue.%s strange utxo.(%s) vout.%d\n",coin->symbol,bits256_str(str,txid),jprint(txobj,0),vout);
|
|
}
|
|
else if ( strcmp(coin->symbol,"KMD") == 0 )
|
|
{
|
|
if ( (interest= jdouble(txobj,"interest")) != 0. )
|
|
{
|
|
//printf("add interest of %.8f to %.8f\n",interest,dstr(value));
|
|
*interestp = SATOSHIDEN * interest;
|
|
}
|
|
}
|
|
LP_destaddr(destaddr,txobj);
|
|
//char str[65]; printf("dest.(%s) %.8f <- %s.(%s) txobj.(%s)\n",destaddr,dstr(value),coin->symbol,bits256_str(str,txid),jprint(txobj,0));
|
|
free_json(txobj);
|
|
} //else { char str[65]; printf("null gettxout return %s/v%d\n",bits256_str(str,txid),vout); }
|
|
return(value);
|
|
}
|
|
|
|
cJSON *LP_transactioninit(struct iguana_info *coin,bits256 txid,int32_t iter,cJSON *txobj)
|
|
{
|
|
struct LP_transaction *tx; int32_t i,height,numvouts,numvins,spentvout; cJSON *vins,*vouts,*vout,*vin; bits256 spenttxid; char str[65];
|
|
if ( coin->inactive != 0 )
|
|
return(0);
|
|
if ( txobj != 0 || (txobj= LP_gettx(coin->symbol,txid)) != 0 )
|
|
{
|
|
if ( coin->electrum == 0 )
|
|
height = LP_txheight(coin,txid);
|
|
else height = -1;
|
|
vins = jarray(&numvins,txobj,"vin");
|
|
vouts = jarray(&numvouts,txobj,"vout");
|
|
// maybe filter so only addresses we care about are using RAM
|
|
if ( iter == 0 && vouts != 0 && (tx= LP_transactionadd(coin,txid,height,numvouts,numvins)) != 0 )
|
|
{
|
|
//printf("create txid %s numvouts.%d numvins.%d\n",bits256_str(str,txid),numvouts,numvins);
|
|
for (i=0; i<numvouts; i++)
|
|
{
|
|
vout = jitem(vouts,i);
|
|
tx->outpoints[i].value = LP_value_extract(vout,0);
|
|
tx->outpoints[i].interest = SATOSHIDEN * jdouble(vout,"interest");
|
|
LP_destaddr(tx->outpoints[i].coinaddr,vout);
|
|
//printf("from transaction init\n");
|
|
//LP_address_utxoadd(coin,tx->outpoints[i].coinaddr,txid,i,tx->outpoints[i].value,height,-1);
|
|
}
|
|
//printf("numvouts.%d\n",numvouts);
|
|
}
|
|
if ( iter == 1 && vins != 0 )
|
|
{
|
|
for (i=0; i<numvins; i++)
|
|
{
|
|
vin = jitem(vins,i);
|
|
spenttxid = jbits256(vin,"txid");
|
|
spentvout = jint(vin,"vout");
|
|
if ( i == 0 && bits256_nonz(spenttxid) == 0 )
|
|
continue;
|
|
if ( (tx= LP_transactionfind(coin,spenttxid)) != 0 )
|
|
{
|
|
if ( spentvout < tx->numvouts )
|
|
{
|
|
if ( tx->outpoints[spentvout].spendheight <= 0 )
|
|
{
|
|
tx->outpoints[spentvout].spendtxid = txid;
|
|
tx->outpoints[spentvout].spendvini = i;
|
|
tx->outpoints[spentvout].spendheight = height > 0 ? height : 1;
|
|
LP_address_utxoadd(coin,tx->outpoints[spentvout].coinaddr,spenttxid,spentvout,tx->outpoints[spentvout].value,-1,height>0?height:1);
|
|
if ( 0 && strcmp(coin->symbol,"BTC") != 0 )
|
|
printf("spend %s %s/v%d at ht.%d\n",coin->symbol,bits256_str(str,tx->txid),spentvout,height);
|
|
}
|
|
} else printf("LP_transactioninit: %s spentvout.%d < numvouts.%d spendheight.%d\n",bits256_str(str,spenttxid),spentvout,tx->numvouts,tx->outpoints[spentvout].spendheight);
|
|
} //else printf("LP_transactioninit: couldnt find (%s) ht.%d %s\n",bits256_str(str,spenttxid),height,jprint(vin,0));
|
|
if ( bits256_cmp(spenttxid,txid) == 0 )
|
|
printf("spending same tx's %p vout ht.%d %s.[%d] s%d\n",tx,height,bits256_str(str,txid),tx!=0?tx->numvouts:0,spentvout);
|
|
}
|
|
}
|
|
return(txobj);
|
|
} //else printf("LP_transactioninit error for %s %s\n",coin->symbol,bits256_str(str,txid));
|
|
return(0);
|
|
}
|
|
|
|
int32_t LP_txheight(struct iguana_info *coin,bits256 txid)
|
|
{
|
|
bits256 blockhash; struct LP_transaction *tx; cJSON *blockobj,*txobj; int32_t height = 0;
|
|
if ( coin == 0 )
|
|
return(-1);
|
|
if ( coin->electrum == 0 )
|
|
{
|
|
if ( (txobj= LP_gettx(coin->symbol,txid)) != 0 )
|
|
{
|
|
//*timestampp = juint(txobj,"locktime");
|
|
//*blocktimep = juint(txobj,"blocktime");
|
|
blockhash = jbits256(txobj,"blockhash");
|
|
if ( bits256_nonz(blockhash) != 0 && (blockobj= LP_getblock(coin->symbol,blockhash)) != 0 )
|
|
{
|
|
height = jint(blockobj,"height");
|
|
//char str[65];
|
|
//if ( strcmp(coin->symbol,"CHIPS") != 0 && strcmp(coin->symbol,"BTC") != 0 )
|
|
// printf("%s %s LP_txheight.%d\n",coin->symbol,bits256_str(str,txid),height);
|
|
free_json(blockobj);
|
|
} // else printf("%s LP_txheight error (%s)\n",coin->symbol,jprint(txobj,0)); likely just unconfirmed
|
|
free_json(txobj);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( (tx= LP_transactionfind(coin,txid)) != 0 )
|
|
height = tx->height;
|
|
}
|
|
return(height);
|
|
}
|
|
|
|
int32_t LP_numconfirms(char *symbol,char *coinaddr,bits256 txid,int32_t vout,int32_t mempool)
|
|
{
|
|
struct iguana_info *coin; int32_t ht,numconfirms = 100;
|
|
cJSON *txobj;
|
|
if ( (coin= LP_coinfind(symbol)) == 0 || coin->inactive != 0 )
|
|
return(-1);
|
|
if ( coin->electrum == 0 )
|
|
{
|
|
numconfirms = -1;
|
|
if ( (txobj= LP_gettxout(symbol,coinaddr,txid,vout)) != 0 )
|
|
{
|
|
numconfirms = jint(txobj,"confirmations");
|
|
free_json(txobj);
|
|
}
|
|
else if ( mempool != 0 && LP_mempoolscan(symbol,txid) >= 0 )
|
|
numconfirms = 0;
|
|
}
|
|
else
|
|
{
|
|
LP_listunspent_issue(symbol,coinaddr,1);
|
|
if ( (ht= LP_txheight(coin,txid)) > 0 && ht <= coin->height )
|
|
numconfirms = (LP_getheight(coin) - ht + 1);
|
|
else if ( mempool != 0 )
|
|
{
|
|
if ( LP_waitmempool(symbol,coinaddr,txid,vout,30) >= 0 )
|
|
numconfirms = 0;
|
|
}
|
|
}
|
|
return(numconfirms);
|
|
}
|
|
|
|
int64_t basilisk_txvalue(char *symbol,bits256 txid,int32_t vout)
|
|
{
|
|
char destaddr[64]; uint64_t value,interest = 0; struct iguana_info *coin;
|
|
if ( (coin= LP_coinfind(symbol)) == 0 || coin->inactive != 0 )
|
|
return(0);
|
|
//char str[65]; printf("%s txvalue.(%s)\n",symbol,bits256_str(str,txid));
|
|
value = LP_txinterestvalue(&interest,destaddr,coin,txid,vout);
|
|
return(value + interest);
|
|
}
|
|
|
|
uint64_t LP_txvalue(char *coinaddr,char *symbol,bits256 txid,int32_t vout)
|
|
{
|
|
struct LP_transaction *tx; cJSON *txobj=0; uint64_t value; struct iguana_info *coin; char str[65],_coinaddr[65];
|
|
if ( bits256_nonz(txid) == 0 )
|
|
return(0);
|
|
if ( (coin= LP_coinfind(symbol)) == 0 || coin->inactive != 0 )
|
|
return(0);
|
|
if ( coinaddr != 0 )
|
|
coinaddr[0] = 0;
|
|
if ( (tx= LP_transactionfind(coin,txid)) == 0 )
|
|
{
|
|
txobj = LP_transactioninit(coin,txid,0,0);
|
|
txobj = LP_transactioninit(coin,txid,1,txobj);
|
|
if ( txobj != 0 )
|
|
free_json(txobj);
|
|
tx = LP_transactionfind(coin,txid);
|
|
}
|
|
if ( tx != 0 )
|
|
{
|
|
if ( vout < tx->numvouts )
|
|
{
|
|
if ( bits256_nonz(tx->outpoints[vout].spendtxid) != 0 )
|
|
{
|
|
//printf("LP_txvalue %s/v%d is spent at %s\n",bits256_str(str,txid),vout,bits256_str(str2,tx->outpoints[vout].spendtxid));
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
if ( coinaddr != 0 )
|
|
{
|
|
//if ( tx->outpoints[vout].coinaddr[0] == 0 )
|
|
// tx->outpoints[vout].value = LP_txinterestvalue(&tx->outpoints[vout].interest,tx->outpoints[vout].coinaddr,coin,txid,vout);
|
|
strcpy(coinaddr,tx->outpoints[vout].coinaddr);
|
|
//printf("(%s) return value %.8f + interest %.8f\n",coinaddr,dstr(tx->outpoints[vout].value),dstr(tx->outpoints[vout].interest));
|
|
}
|
|
return(tx->outpoints[vout].value + 0*tx->outpoints[vout].interest);
|
|
}
|
|
} else printf("LP_txvalue vout.%d >= tx->numvouts.%d\n",vout,tx->numvouts);
|
|
}
|
|
else
|
|
{
|
|
if ( (txobj= LP_gettxout(coin->symbol,coinaddr,txid,vout)) != 0 )
|
|
{
|
|
value = LP_value_extract(txobj,0);//SATOSHIDEN * (jdouble(txobj,"value") + jdouble(txobj,"interest"));
|
|
if ( coinaddr == 0 )
|
|
coinaddr = _coinaddr;
|
|
LP_destaddr(coinaddr,txobj);
|
|
//printf("pruned node? LP_txvalue couldnt find %s tx %s, but gettxout %.8f\n",coin->symbol,bits256_str(str,txid),dstr(value));
|
|
if ( value != 0 )
|
|
{
|
|
free_json(txobj);
|
|
return(value);
|
|
}
|
|
}
|
|
printf("pruned node? LP_txvalue couldnt find %s tx %s/v%d (%s)\n",coin->symbol,bits256_str(str,txid),vout,txobj!=0?jprint(txobj,0):"");
|
|
if ( txobj != 0 )
|
|
free_json(txobj);
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int32_t LP_iseligible(uint64_t *valp,uint64_t *val2p,int32_t iambob,char *symbol,bits256 txid,int32_t vout,uint64_t satoshis,bits256 txid2,int32_t vout2)
|
|
{
|
|
//struct LP_utxoinfo *utxo;
|
|
struct LP_address_utxo *up; uint64_t val,val2=0,txfee,threshold=0; int32_t bypass = 0; char destaddr[64],destaddr2[64],str[65]; struct iguana_info *coin = LP_coinfind(symbol);
|
|
if ( bits256_nonz(txid) == 0 || bits256_nonz(txid2) == 0 )
|
|
{
|
|
printf("null txid not eligible\n");
|
|
return(-1);
|
|
}
|
|
destaddr[0] = destaddr2[0] = 0;
|
|
if ( coin != 0 && IAMLP != 0 && coin->inactive != 0 )
|
|
bypass = 1;
|
|
if ( bypass != 0 )
|
|
val = satoshis;
|
|
else val = LP_txvalue(destaddr,symbol,txid,vout);
|
|
if ( (up= LP_address_utxofind(coin,destaddr,txid,vout)) != 0 && up->spendheight > 0 )
|
|
{
|
|
//printf("%s/v%d spent %d\n",bits256_str(str,txid),vout,up->spendheight);
|
|
return(-2);
|
|
}
|
|
if ( (up= LP_address_utxofind(coin,destaddr,txid2,vout2)) != 0 && up->spendheight > 0 )
|
|
{
|
|
//printf("%s/v%d spent %d\n",bits256_str(str,txid2),vout2,up->spendheight);
|
|
return(-3);
|
|
}
|
|
txfee = LP_txfeecalc(LP_coinfind(symbol),0,0);
|
|
if ( val >= satoshis && val > (1+LP_MINSIZE_TXFEEMULT)*txfee )
|
|
{
|
|
threshold = (iambob != 0) ? LP_DEPOSITSATOSHIS(satoshis) : (LP_DEXFEE(satoshis) + txfee);
|
|
if ( bypass != 0 )
|
|
val2 = threshold;
|
|
else val2 = LP_txvalue(destaddr2,symbol,txid2,vout2);
|
|
if ( val2 >= threshold )
|
|
{
|
|
if ( bypass == 0 && strcmp(destaddr,destaddr2) != 0 )
|
|
printf("mismatched %s destaddr (%s) vs (%s)\n",symbol,destaddr,destaddr2);
|
|
else if ( bypass == 0 && ((iambob == 0 && val2 > val) || (iambob != 0 && val2 <= satoshis)) )
|
|
printf("iambob.%d ineligible due to offsides: val %.8f and val2 %.8f vs %.8f diff %lld\n",iambob,dstr(val),dstr(val2),dstr(satoshis),(long long)(val2 - val));
|
|
else
|
|
{
|
|
*valp = val;
|
|
*val2p = val2;
|
|
return(1);
|
|
}
|
|
} // else printf("no val2\n");
|
|
}
|
|
char str2[65];
|
|
if ( val != 0 && val2 != 0 )
|
|
printf("spent.%d %s txid or value %.8f < %.8f or val2 %.8f < %.8f, %s/v%d %s/v%d or < 10x txfee %.8f\n",iambob,symbol,dstr(val),dstr(satoshis),dstr(val2),dstr(threshold),bits256_str(str,txid),vout,bits256_str(str2,txid2),vout2,dstr(txfee));
|
|
if ( val == 0 )
|
|
LP_address_utxoadd(coin,destaddr,txid,vout,satoshis,-1,1);
|
|
if ( val2 == 0 )
|
|
LP_address_utxoadd(coin,destaddr,txid2,vout2,threshold,-1,1);
|
|
/*for (iter=0; iter<2; iter++)
|
|
{
|
|
if ( (utxo= LP_utxofind(iter,txid,vout)) != 0 )
|
|
{
|
|
//printf("iambob.%d case 00\n",iter);
|
|
if ( utxo->T.spentflag == 0 )
|
|
utxo->T.spentflag = (uint32_t)time(NULL);
|
|
}
|
|
if ( (utxo= LP_utxo2find(iter,txid,vout)) != 0 )
|
|
{
|
|
//printf("iambob.%d case 01\n",iter);
|
|
if ( utxo->T.spentflag == 0 )
|
|
utxo->T.spentflag = (uint32_t)time(NULL);
|
|
}
|
|
if ( (utxo= LP_utxofind(iter,txid2,vout2)) != 0 )
|
|
{
|
|
//printf("iambob.%d case 10\n",iter);
|
|
if ( utxo->T.spentflag == 0 )
|
|
utxo->T.spentflag = (uint32_t)time(NULL);
|
|
}
|
|
if ( (utxo= LP_utxo2find(iter,txid2,vout2)) != 0 )
|
|
{
|
|
//printf("iambob.%d case 11\n",iter);
|
|
if ( utxo->T.spentflag == 0 )
|
|
utxo->T.spentflag = (uint32_t)time(NULL);
|
|
}
|
|
}*/
|
|
*valp = val;
|
|
*val2p = val2;
|
|
return(0);
|
|
}
|
|
|
|
int32_t LP_inventory_prevent(int32_t iambob,char *symbol,bits256 txid,int32_t vout)
|
|
{
|
|
struct LP_utxoinfo *utxo; struct LP_transaction *tx; struct iguana_info *coin;
|
|
if ( (utxo= LP_utxofind(iambob,txid,vout)) != 0 || (utxo= LP_utxo2find(iambob,txid,vout)) != 0 )
|
|
{
|
|
if ( (coin= LP_coinfind(symbol)) != 0 && (tx= LP_transactionfind(coin,txid)) != 0 )
|
|
{
|
|
if ( tx->outpoints[vout].spendheight > 0 )
|
|
utxo->T.spentflag = tx->outpoints[vout].spendheight;
|
|
else utxo->T.spentflag = 0;
|
|
}
|
|
if ( utxo->T.spentflag != 0 )
|
|
{
|
|
//char str[65]; printf("prevent adding iambob.%d %s/v%d to inventory\n",iambob,bits256_str(str,txid),vout);
|
|
return(1);
|
|
}
|
|
}
|
|
return(0);
|
|
}
|
|
|
|
int32_t LP_undospends(struct iguana_info *coin,int32_t lastheight)
|
|
{
|
|
int32_t i,ht,num = 0; struct LP_transaction *tx,*tmp;
|
|
HASH_ITER(hh,coin->transactions,tx,tmp)
|
|
{
|
|
for (i=0; i<tx->numvouts; i++)
|
|
{
|
|
if ( bits256_nonz(tx->outpoints[i].spendtxid) == 0 )
|
|
continue;
|
|
if ( (ht= tx->outpoints[i].spendheight) == 0 )
|
|
{
|
|
tx->outpoints[i].spendheight = LP_txheight(coin,tx->outpoints[i].spendtxid);
|
|
}
|
|
if ( (ht= tx->outpoints[i].spendheight) != 0 && ht > lastheight )
|
|
{
|
|
char str[65]; printf("clear spend %s/v%d at ht.%d > lastheight.%d\n",bits256_str(str,tx->txid),i,ht,lastheight);
|
|
tx->outpoints[i].spendheight = 0;
|
|
tx->outpoints[i].spendvini = -1;
|
|
memset(tx->outpoints[i].spendtxid.bytes,0,sizeof(bits256));
|
|
}
|
|
}
|
|
}
|
|
return(num);
|
|
}
|
|
|