// Copyright (c) 2016-2024 The Hush developers
// Distributed under the GPLv3 software license, see the accompanying
// file COPYING or https://www.gnu.org/licenses/gpl-3.0.en.html
/******************************************************************************
 * Copyright © 2014-2019 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.            *
 *                                                                            *
 ******************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <memory.h>
#include "cJSON.c"

/*
NOTE: HUSH nor any Hush Smart Chain has any sprout outputs. This code is kept for historical and educational purposes.

 z_migrate: the purpose of z_migrate is to make converting of all sprout outputs into sapling. the usage would be for the user to specify a sapling address and call z_migrate zsaddr, until it returns that there is nothing left to be done.
 
 its main functionality is quite similar to a z_mergetoaddress ANY_ZADDR -> onetime_taddr followed by a z_sendmany onetime_taddr -> zsaddr
 
 since the z_mergetoaddress will take time, it would just queue up an async operation. When it starts, it should see if there are any onetime_taddr with 10000.0001 funds in it, that is a signal for it to do the sapling tx and it can just do that without async as it is fast enough, especially with a taddr input. Maybe it limits itself to one,  or it does all possible taddr -> sapling as fast as it can. either is fine as it will be called over and over anyway.
 
 It might be that there is nothing to do, but some operations are pending. in that case it would return such a status. as soon as the operation finishes, there would be more work to do.
 
 the amount sent to the taddr, should be 10000.0001
 
 The GUI or user would be expected to generate a sapling address and then call z_migrate saplingaddr in a loop, until it returns that it is all done. this loop should pause for 10 seconds or so, if z_migrate is just waiting for opid to complete.
 */

bits256 zeroid;

char hexbyte(int32_t c)
{
    c &= 0xf;
    if ( c < 10 )
        return('0'+c);
    else if ( c < 16 )
        return('a'+c-10);
    else return(0);
}

int32_t _unhex(char c)
{
    if ( c >= '0' && c <= '9' )
        return(c - '0');
    else if ( c >= 'a' && c <= 'f' )
        return(c - 'a' + 10);
    else if ( c >= 'A' && c <= 'F' )
        return(c - 'A' + 10);
    return(-1);
}

int32_t is_hexstr(char *str,int32_t n)
{
    int32_t i;
    if ( str == 0 || str[0] == 0 )
        return(0);
    for (i=0; str[i]!=0; i++)
    {
        if ( n > 0 && i >= n )
            break;
        if ( _unhex(str[i]) < 0 )
            break;
    }
    if ( n == 0 )
        return(i);
    return(i == n);
}

int32_t unhex(char c)
{
    int32_t hex;
    if ( (hex= _unhex(c)) < 0 )
    {
        //printf("unhex: illegal hexchar.(%c)\n",c);
    }
    return(hex);
}

unsigned char _decode_hex(char *hex) { return((unhex(hex[0])<<4) | unhex(hex[1])); }

int32_t decode_hex(unsigned char *bytes,int32_t n,char *hex)
{
    int32_t adjust,i = 0;
    //printf("decode.(%s)\n",hex);
    if ( is_hexstr(hex,n) <= 0 )
    {
        memset(bytes,0,n);
        return(n);
    }
    if ( hex[n-1] == '\n' || hex[n-1] == '\r' )
        hex[--n] = 0;
    if ( hex[n-1] == '\n' || hex[n-1] == '\r' )
        hex[--n] = 0;
    if ( n == 0 || (hex[n*2+1] == 0 && hex[n*2] != 0) )
    {
        if ( n > 0 )
        {
            bytes[0] = unhex(hex[0]);
            printf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex));
        }
        bytes++;
        hex++;
        adjust = 1;
    } else adjust = 0;
    if ( n > 0 )
    {
        for (i=0; i<n; i++)
            bytes[i] = _decode_hex(&hex[i*2]);
    }
    //bytes[i] = 0;
    return(n + adjust);
}

int32_t init_hexbytes_noT(char *hexbytes,unsigned char *message,long len)
{
    int32_t i;
    if ( len <= 0 )
    {
        hexbytes[0] = 0;
        return(1);
    }
    for (i=0; i<len; i++)
    {
        hexbytes[i*2] = hexbyte((message[i]>>4) & 0xf);
        hexbytes[i*2 + 1] = hexbyte(message[i] & 0xf);
        //printf("i.%d (%02x) [%c%c]\n",i,message[i],hexbytes[i*2],hexbytes[i*2+1]);
    }
    hexbytes[len*2] = 0;
    //printf("len.%ld\n",len*2+1);
    return((int32_t)len*2+1);
}

long _stripwhite(char *buf,int accept)
{
    int32_t i,j,c;
    if ( buf == 0 || buf[0] == 0 )
        return(0);
    for (i=j=0; buf[i]!=0; i++)
    {
        buf[j] = c = buf[i];
        if ( c == accept || (c != ' ' && c != '\n' && c != '\r' && c != '\t' && c != '\b') )
            j++;
    }
    buf[j] = 0;
    return(j);
}

char *clonestr(char *str)
{
    char *clone;
    if ( str == 0 || str[0]==0)
    {
        printf("warning cloning nullstr.%p\n",str);
        //#ifdef __APPLE__
        //        while ( 1 ) sleep(1);
        //#endif
        str = (char *)"<nullstr>";
    }
    clone = (char *)malloc(strlen(str)+16);
    strcpy(clone,str);
    return(clone);
}

int32_t safecopy(char *dest,char *src,long len)
{
    int32_t i = -1;
    if ( src != 0 && dest != 0 && src != dest )
    {
        if ( dest != 0 )
            memset(dest,0,len);
        for (i=0; i<len&&src[i]!=0; i++)
            dest[i] = src[i];
        if ( i == len )
        {
            printf("safecopy: %s too long %ld\n",src,len);
            //printf("divide by zero! %d\n",1/zeroval());
#ifdef __APPLE__
            //getchar();
#endif
            return(-1);
        }
        dest[i] = 0;
    }
    return(i);
}

char *bits256_str(char hexstr[65],bits256 x)
{
    init_hexbytes_noT(hexstr,x.bytes,sizeof(x));
    return(hexstr);
}

int64_t conv_floatstr(char *numstr)
{
    double val,corr;
    val = atof(numstr);
    corr = (val < 0.) ? -0.50000000001 : 0.50000000001;
    return((int64_t)(val * SATOSHIDEN + corr));
}

char *nonportable_path(char *str)
{
    int32_t i;
    for (i=0; str[i]!=0; i++)
        if ( str[i] == '/' )
            str[i] = '\\';
    return(str);
}

char *portable_path(char *str)
{
#ifdef _WIN32
    return(nonportable_path(str));
#else
#ifdef __PNACL
    /*int32_t i,n;
     if ( str[0] == '/' )
     return(str);
     else
     {
     n = (int32_t)strlen(str);
     for (i=n; i>0; i--)
     str[i] = str[i-1];
     str[0] = '/';
     str[n+1] = 0;
     }*/
#endif
    return(str);
#endif
}

void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep)
{
    FILE *fp;
    long  filesize,buflen = *allocsizep;
    uint8_t *buf = *bufp;
    *lenp = 0;
    if ( (fp= fopen(portable_path(fname),"rb")) != 0 )
    {
        fseek(fp,0,SEEK_END);
        filesize = ftell(fp);
        if ( filesize == 0 )
        {
            fclose(fp);
            *lenp = 0;
            printf("loadfile null size.(%s)\n",fname);
            return(0);
        }
        if ( filesize > buflen )
        {
            *allocsizep = filesize;
            *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64);
        }
        rewind(fp);
        if ( buf == 0 )
            printf("Null buf ???\n");
        else
        {
            if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize )
                printf("error reading filesize.%ld\n",(long)filesize);
            buf[filesize] = 0;
        }
        fclose(fp);
        *lenp = filesize;
        //printf("loaded.(%s)\n",buf);
    } //else printf("OS_loadfile couldnt load.(%s)\n",fname);
    return(buf);
}

void *filestr(long *allocsizep,char *_fname)
{
    long filesize = 0; char *fname,*buf = 0; void *retptr;
    *allocsizep = 0;
    fname = malloc(strlen(_fname)+1);
    strcpy(fname,_fname);
    retptr = loadfile(fname,(uint8_t **)&buf,&filesize,allocsizep);
    free(fname);
    return(retptr);
}

char *send_curl(char *url,char *fname)
{
    long fsize; char curlstr[1024];
    sprintf(curlstr,"curl --url \"%s\" > %s",url,fname);
    system(curlstr);
    return(filestr(&fsize,fname));
}

cJSON *get_urljson(char *url,char *fname)
{
    char *jsonstr; cJSON *json = 0;
    if ( (jsonstr= send_curl(url,fname)) != 0 )
    {
        //printf("(%s) -> (%s)\n",url,jsonstr);
        json = cJSON_Parse(jsonstr);
        free(jsonstr);
    }
    return(json);
}

//////////////////////////////////////////////
// start of dapp
//////////////////////////////////////////////

char *REFCOIN_CLI;

cJSON *get_hushcli(char *refcoin,char **retstrp,char *acname,char *method,char *arg0,char *arg1,char *arg2,char *arg3)
{
    long fsize; cJSON *retjson = 0; char cmdstr[32768],*jsonstr,fname[256];
    sprintf(fname,"/tmp/zmigrate.%s",method);
    if ( acname[0] != 0 )
    {
        if ( refcoin[0] != 0 && strcmp(refcoin,"HUSH3") != 0 )
            printf("unexpected: refcoin.(%s) acname.(%s)\n",refcoin,acname);
        sprintf(cmdstr,"./hush-cli -ac_name=%s %s %s %s %s %s > %s\n",acname,method,arg0,arg1,arg2,arg3,fname);
    }
    else if ( strcmp(refcoin,"HUSH3") == 0 )
        sprintf(cmdstr,"./hush-cli %s %s %s %s %s > %s\n",method,arg0,arg1,arg2,arg3,fname);
    else if ( REFCOIN_CLI != 0 && REFCOIN_CLI[0] != 0 )
    {
        sprintf(cmdstr,"%s %s %s %s %s %s > %s\n",REFCOIN_CLI,method,arg0,arg1,arg2,arg3,fname);
        //printf("ref.(%s) REFCOIN_CLI (%s)\n",refcoin,cmdstr);
    }
    system(cmdstr);
    *retstrp = 0;
    if ( (jsonstr= filestr(&fsize,fname)) != 0 )
    {
        jsonstr[strlen(jsonstr)-1]='\0';
        //fprintf(stderr,"%s -> jsonstr.(%s)\n",cmdstr,jsonstr);
        if ( (jsonstr[0] != '{' && jsonstr[0] != '[') || (retjson= cJSON_Parse(jsonstr)) == 0 )
            *retstrp = jsonstr;
        else free(jsonstr);
    }
    return(retjson);
}

bits256 sendtoaddress(char *refcoin,char *acname,char *destaddr,int64_t satoshis)
{
    char numstr[32],*retstr,str[65]; cJSON *retjson; bits256 txid;
    memset(txid.bytes,0,sizeof(txid));
    sprintf(numstr,"%.8f",(double)satoshis/SATOSHIDEN);
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"sendtoaddress",destaddr,numstr,"","")) != 0 )
    {
        fprintf(stderr,"unexpected sendrawtransaction json.(%s)\n",jprint(retjson,0));
        free_json(retjson);
    }
    else if ( retstr != 0 )
    {
        if ( strlen(retstr) >= 64 )
        {
            retstr[64] = 0;
            decode_hex(txid.bytes,32,retstr);
        }
        fprintf(stderr,"sendtoaddress %s %.8f txid.(%s)\n",destaddr,(double)satoshis/SATOSHIDEN,bits256_str(str,txid));
        free(retstr);
    }
    return(txid);
}

int32_t get_coinheight(char *refcoin,char *acname)
{
    cJSON *retjson; char *retstr; int32_t height=0;
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"getblockchaininfo","","","","")) != 0 )
    {
        height = jint(retjson,"blocks");
        free_json(retjson);
    }
    else if ( retstr != 0 )
    {
        fprintf(stderr,"%s get_coinheight.(%s) error.(%s)\n",refcoin,acname,retstr);
        free(retstr);
    }
    return(height);
}

bits256 get_coinblockhash(char *refcoin,char *acname,int32_t height)
{
    cJSON *retjson; char *retstr,heightstr[32]; bits256 hash;
    memset(hash.bytes,0,sizeof(hash));
    sprintf(heightstr,"%d",height);
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"getblockhash",heightstr,"","","")) != 0 )
    {
        fprintf(stderr,"unexpected blockhash json.(%s)\n",jprint(retjson,0));
        free_json(retjson);
    }
    else if ( retstr != 0 )
    {
        if ( strlen(retstr) >= 64 )
        {
            retstr[64] = 0;
            decode_hex(hash.bytes,32,retstr);
        }
        free(retstr);
    }
    return(hash);
}

bits256 get_coinmerkleroot(char *refcoin,char *acname,bits256 blockhash)
{
    cJSON *retjson; char *retstr,str[65]; bits256 merkleroot;
    memset(merkleroot.bytes,0,sizeof(merkleroot));
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"getblockheader",bits256_str(str,blockhash),"","","")) != 0 )
    {
        merkleroot = jbits256(retjson,"merkleroot");
        //fprintf(stderr,"got merkleroot.(%s)\n",bits256_str(str,merkleroot));
        free_json(retjson);
    }
    else if ( retstr != 0 )
    {
        fprintf(stderr,"%s %s get_coinmerkleroot error.(%s)\n",refcoin,acname,retstr);
        free(retstr);
    }
    return(merkleroot);
}

int32_t get_coinheader(char *refcoin,char *acname,bits256 *blockhashp,bits256 *merklerootp,int32_t prevheight)
{
    int32_t height = 0; char str[65];
    if ( prevheight == 0 )
        height = get_coinheight(refcoin,acname) - 20;
    else height = prevheight + 1;
    if ( height > 0 )
    {
        *blockhashp = get_coinblockhash(refcoin,acname,height);
        if ( bits256_nonz(*blockhashp) != 0 )
        {
            *merklerootp = get_coinmerkleroot(refcoin,acname,*blockhashp);
            if ( bits256_nonz(*merklerootp) != 0 )
                return(height);
        }
    }
    memset(blockhashp,0,sizeof(*blockhashp));
    memset(merklerootp,0,sizeof(*merklerootp));
    return(0);
}

cJSON *get_rawmempool(char *refcoin,char *acname)
{
    cJSON *retjson; char *retstr;
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"getrawmempool","","","","")) != 0 )
    {
        //printf("mempool.(%s)\n",jprint(retjson,0));
        return(retjson);
    }
    else if ( retstr != 0 )
    {
        fprintf(stderr,"get_rawmempool.(%s) error.(%s)\n",acname,retstr);
        free(retstr);
    }
    return(0);
}

cJSON *get_addressutxos(char *refcoin,char *acname,char *coinaddr)
{
    cJSON *retjson; char *retstr,jsonbuf[256];
    if ( refcoin[0] != 0 && strcmp(refcoin,"HUSH3") != 0 )
        printf("warning: assumes %s has addressindex enabled\n",refcoin);
    sprintf(jsonbuf,"{\\\"addresses\\\":[\\\"%s\\\"]}",coinaddr);
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"getaddressutxos",jsonbuf,"","","")) != 0 )
    {
        //printf("addressutxos.(%s)\n",jprint(retjson,0));
        return(retjson);
    }
    else if ( retstr != 0 )
    {
        fprintf(stderr,"get_addressutxos.(%s) error.(%s)\n",acname,retstr);
        free(retstr);
    }
    return(0);
}

cJSON *get_rawtransaction(char *refcoin,char *acname,bits256 txid)
{
    cJSON *retjson; char *retstr,str[65];
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"getrawtransaction",bits256_str(str,txid),"1","","")) != 0 )
    {
        return(retjson);
    }
    else if ( retstr != 0 )
    {
        fprintf(stderr,"get_rawtransaction.(%s) %s error.(%s)\n",refcoin,acname,retstr);
        free(retstr);
    }
    return(0);
}

cJSON *get_listunspent(char *refcoin,char *acname)
{
    cJSON *retjson; char *retstr,str[65];
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"listunspent","","","","")) != 0 )
    {
        return(retjson);
    }
    else if ( retstr != 0 )
    {
        fprintf(stderr,"get_listunspent.(%s) %s error.(%s)\n",refcoin,acname,retstr);
        free(retstr);
    }
    return(0);
}

cJSON *z_listunspent(char *refcoin,char *acname)
{
    cJSON *retjson; char *retstr,str[65];
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"z_listunspent","","","","")) != 0 )
    {
        return(retjson);
    }
    else if ( retstr != 0 )
    {
        fprintf(stderr,"z_listunspent.(%s) %s error.(%s)\n",refcoin,acname,retstr);
        free(retstr);
    }
    return(0);
}

cJSON *z_listoperationids(char *refcoin,char *acname)
{
    cJSON *retjson; char *retstr,str[65];
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"z_listoperationids","","","","")) != 0 )
    {
        return(retjson);
    }
    else if ( retstr != 0 )
    {
        fprintf(stderr,"z_listoperationids.(%s) %s error.(%s)\n",refcoin,acname,retstr);
        free(retstr);
    }
    return(0);
}

cJSON *z_getoperationstatus(char *refcoin,char *acname,char *opid)
{
    cJSON *retjson; char *retstr,str[65],params[512];
    sprintf(params,"'[\"%s\"]'",opid);
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"z_getoperationstatus",params,"","","")) != 0 )
    {
        //printf("got status (%s)\n",jprint(retjson,0));
        return(retjson);
    }
    else if ( retstr != 0 )
    {
        fprintf(stderr,"z_getoperationstatus.(%s) %s error.(%s)\n",refcoin,acname,retstr);
        free(retstr);
    }
    return(0);
}

cJSON *z_getoperationresult(char *refcoin,char *acname,char *opid)
{
    cJSON *retjson; char *retstr,str[65],params[512];
    sprintf(params,"'[\"%s\"]'",opid);
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"z_getoperationresult",params,"","","")) != 0 )
    {
        return(retjson);
    }
    else if ( retstr != 0 )
    {
        fprintf(stderr,"z_getoperationresult.(%s) %s error.(%s)\n",refcoin,acname,retstr);
        free(retstr);
    }
    return(0);
}

int32_t validateaddress(char *refcoin,char *acname,char *depositaddr, char* compare)
{
    cJSON *retjson; char *retstr; int32_t res=0;
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"validateaddress",depositaddr,"","","")) != 0 )
    {
        if (is_cJSON_True(jobj(retjson,compare)) != 0 ) res=1;
        free_json(retjson);
    }
    else if ( retstr != 0 )
    {
        fprintf(stderr,"validateaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr);
        free(retstr);
    }
    return (res);
}

int32_t z_validateaddress(char *refcoin,char *acname,char *depositaddr, char *compare)
{
    cJSON *retjson; char *retstr; int32_t res=0;
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"z_validateaddress",depositaddr,"","","")) != 0 )
    {
        if (is_cJSON_True(jobj(retjson,compare)) != 0 )
            res=1;
        free_json(retjson);
    }
    else if ( retstr != 0 )
    {
        fprintf(stderr,"z_validateaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr);
        free(retstr);
    }
    return (res);
}

int64_t z_getbalance(char *refcoin,char *acname,char *coinaddr)
{
    cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0;
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"z_getbalance",coinaddr,"","","")) != 0 )
    {
        fprintf(stderr,"z_getbalance.(%s) %s returned json!\n",refcoin,acname);
        free_json(retjson);
    }
    else if ( retstr != 0 )
    {
        amount = atof(retstr) * SATOSHIDEN;
        sprintf(cmpstr,"%.8f",dstr(amount));
        if ( strcmp(retstr,cmpstr) != 0 )
            amount++;
        //printf("retstr %s -> %.8f\n",retstr,dstr(amount));
        free(retstr);
    }
    return (amount);
}

int32_t z_exportkey(char *privkey,char *refcoin,char *acname,char *zaddr)
{
    cJSON *retjson; char *retstr,cmpstr[64]; int64_t amount=0;
    privkey[0] = 0;
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"z_exportkey",zaddr,"","","")) != 0 )
    {
        fprintf(stderr,"z_exportkey.(%s) %s returned json!\n",refcoin,acname);
        free_json(retjson);
        return(-1);
    }
    else if ( retstr != 0 )
    {
        //printf("retstr %s -> %.8f\n",retstr,dstr(amount));
        strcpy(privkey,retstr);
        free(retstr);
        return(0);
    }
    return(-1);
}

int32_t getnewaddress(char *coinaddr,char *refcoin,char *acname)
{
    cJSON *retjson; char *retstr; int64_t amount=0; int32_t retval = -1;
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"getnewaddress","","","","")) != 0 )
    {
        fprintf(stderr,"getnewaddress.(%s) %s returned json!\n",refcoin,acname);
        free_json(retjson);
    }
    else if ( retstr != 0 )
    {
        strcpy(coinaddr,retstr);
        free(retstr);
        retval = 0;
    }
    return(retval);
}

int32_t z_getnewaddress(char *coinaddr,char *refcoin,char *acname,char *typestr)
{
    cJSON *retjson; char *retstr; int64_t amount=0; int32_t retval = -1;
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"z_getnewaddress",typestr,"","","")) != 0 )
    {
        fprintf(stderr,"z_getnewaddress.(%s) %s returned json!\n",refcoin,acname);
        free_json(retjson);
    }
    else if ( retstr != 0 )
    {
        strcpy(coinaddr,retstr);
        free(retstr);
        retval = 0;
    }
    return(retval);
}

int64_t find_onetime_amount(char *coinstr,char *coinaddr)
{
    cJSON *array,*item; int32_t i,n; char *addr; int64_t amount = 0;
    coinaddr[0] = 0;
    if ( (array= get_listunspent(coinstr,"")) != 0 )
    {
        //printf("got listunspent.(%s)\n",jprint(array,0));
        if ( (n= cJSON_GetArraySize(array)) > 0 )
        {
            for (i=0; i<n; i++)
            {
                item = jitem(array,i);
                if (is_cJSON_False(jobj(item, "spendable")) != 0)
                {
                    continue;
                }
                if ( (addr= jstr(item,"address")) != 0 )
                {
                    strcpy(coinaddr,addr);
                    amount = z_getbalance(coinstr,"",coinaddr);
                    printf("found address.(%s) with amount %.8f\n",coinaddr,dstr(amount));
                    break;
                }
            }
        }
        free_json(array);
    }
    return(amount);
}

int64_t find_sprout_amount(char *coinstr,char *zcaddr)
{
    cJSON *array,*item; int32_t i,n; char *addr; int64_t amount = 0;
    zcaddr[0] = 0;
    if ( (array= z_listunspent(coinstr,"")) != 0 )
    {
        if ( (n= cJSON_GetArraySize(array)) > 0 )
        {
            for (i=0; i<n; i++)
            {
                item = jitem(array,i);
                if ( (addr= jstr(item,"address")) != 0 && addr[0] == 'z' && addr[1] == 'c' )
                {
                    strcpy(zcaddr,addr);
                    amount = z_getbalance(coinstr,"",zcaddr);
                    printf("found address.(%s) with amount %.8f\n",zcaddr,dstr(amount));
                    break;
                }
            }
        }
        free_json(array);
    }
    return(amount);
}

void importaddress(char *refcoin,char *acname,char *depositaddr)
{
    cJSON *retjson; char *retstr;
    if ( (retjson= get_hushcli(refcoin,&retstr,acname,"importaddress",depositaddr,"","true","")) != 0 )
    {
        printf("importaddress.(%s)\n",jprint(retjson,0));
        free_json(retjson);
    }
    else if ( retstr != 0 )
    {
        fprintf(stderr,"importaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr);
        free(retstr);
    }
}

int32_t z_sendmany(char *opidstr,char *coinstr,char *acname,char *srcaddr,char *destaddr,int64_t amount)
{
    cJSON *retjson; char *retstr,params[1024],addr[128]; int32_t retval = -1;
    sprintf(params,"'[{\"address\":\"%s\",\"amount\":%.8f}]'",destaddr,dstr(amount));
    sprintf(addr,"\"%s\"",srcaddr);
    printf("z_sendmany from.(%s) -> %s\n",srcaddr,params);
    if ( (retjson= get_hushcli(coinstr,&retstr,acname,"z_sendmany",addr,params,"","")) != 0 )
    {
        printf("unexpected json z_sendmany.(%s)\n",jprint(retjson,0));
        free_json(retjson);
    }
    else if ( retstr != 0 )
    {
        fprintf(stderr,"z_sendmany.(%s) -> opid.(%s)\n",coinstr,retstr);
        strcpy(opidstr,retstr);
        free(retstr);
        retval = 0;
    }
    return(retval);
}

int32_t z_mergetoaddress(char *opidstr,char *coinstr,char *acname,char *destaddr)
{
    cJSON *retjson; char *retstr,addr[128],*opstr; int32_t retval = -1;
    sprintf(addr,"[\\\"ANY_SPROUT\\\"]");
    if ( (retjson= get_hushcli(coinstr,&retstr,acname,"z_mergetoaddress",addr,destaddr,"","")) != 0 )
    {
        if ( (opstr= jstr(retjson,"opid")) != 0 )
            strcpy(opidstr,opstr);
        retval = jint(retjson,"remainingNotes");
        fprintf(stderr,"%s\n",jprint(retjson,0));
        free_json(retjson);
    }
    else if ( retstr != 0 )
    {
        fprintf(stderr,"z_mergetoaddress.(%s) -> opid.(%s)\n",coinstr,retstr);
        strcpy(opidstr,retstr);
        free(retstr);
    }
    return(retval);
}

int32_t empty_mempool(char *coinstr,char *acname)
{
    cJSON *array; int32_t n;
    if ( (array= get_rawmempool(coinstr,acname)) != 0 )
    {
        if ( (n= cJSON_GetArraySize(array)) > 0 )
            return(0);
        free_json(array);
        return(1);
    }
    return(-1);
}

cJSON *getinputarray(int64_t *totalp,cJSON *unspents,int64_t required)
{
    cJSON *vin,*item,*vins = cJSON_CreateArray(); int32_t i,n,v; int64_t satoshis; bits256 txid;
    *totalp = 0;
    if ( (n= cJSON_GetArraySize(unspents)) > 0 )
    {
        for (i=0; i<n; i++)
        {
            item = jitem(unspents,i);
            satoshis = jdouble(item,"amount") * SATOSHIDEN;
            txid = jbits256(item,"txid");
            v = jint(item,"vout");
            if ( bits256_nonz(txid) != 0 )
            {
                vin = cJSON_CreateObject();
                jaddbits256(vin,"txid",txid);
                jaddnum(vin,"vout",v);
                jaddi(vins,vin);
                *totalp += satoshis;
                if ( (*totalp) >= required )
                    break;
            }
        }
    }
    return(vins);
}

int32_t tx_has_voutaddress(char *refcoin,char *acname,bits256 txid,char *coinaddr)
{
    cJSON *txobj,*vouts,*vout,*vins,*vin,*sobj,*addresses; char *addr,str[65]; int32_t i,j,n,numarray,retval = 0, hasvout=0;
    if ( (txobj= get_rawtransaction(refcoin,acname,txid)) != 0 )
    {
        if ( (vouts= jarray(&numarray,txobj,"vout")) != 0 )
        {
            for (i=0; i<numarray; i++)
            {
                if ((vout = jitem(vouts,i)) !=0 && (sobj= jobj(vout,"scriptPubKey")) != 0 )
                {
                    if ( (addresses= jarray(&n,sobj,"addresses")) != 0 )
                    {
                        for (j=0; j<n; j++)
                        {
                            addr = jstri(addresses,j);
                            if ( strcmp(addr,coinaddr) == 0 )
                            {
                                //fprintf(stderr,"found %s in %s v%d\n",coinaddr,bits256_str(str,txid),i);
                                hasvout = 1;
                                break;
                            }
                        }
                    }
                }
                if (hasvout==1) break;
            }
        }
        // if (hasvout==1 && (vins=jarray(&numarray,txobj,"vin"))!=0)
        // {
        //     for (int i=0;i<numarray;i++)
        //     {
        //         if ((vin=jitem(vins,i))!=0 && validateaddress(refcoin,acname,jstr(vin,"address"),"ismine")!=0)
        //         {
        //             retval=1;
        //             break;
        //         }
        //     }
        // }
        free_json(txobj);
    }
    return(hasvout);
}

int32_t have_pending_opid(char *coinstr,int32_t clearresults)
{
    cJSON *array,*status,*result; int32_t i,n,j,m,pending = 0; char *statusstr;
    if ( (array= z_listoperationids(coinstr,"")) != 0 )
    {
        if ( (n= cJSON_GetArraySize(array)) > 0 )
        {
            for (i=0; i<n; i++)
            {
                if ( (status= z_getoperationstatus(coinstr,"",jstri(array,i))) != 0 )
                {
                    if ( (m= cJSON_GetArraySize(status)) > 0 )
                    {
                        for (j=0; j<m; j++)
                        {
                            if ( (statusstr= jstr(jitem(status,j),"status")) != 0 )
                            {
                                if ( strcmp(statusstr,"executing") == 0 )
                                {
                                    pending++;
                                    //printf("pending.%d\n",pending);
                                }
                                else if ( clearresults != 0 )
                                {
                                    if ( (result= z_getoperationresult(coinstr,"",jstri(array,i))) != 0 )
                                    {
                                        free_json(result);
                                    }
                                }
                            }
                        }
                    }
                    free_json(status);
                }
            }
        }
        free_json(array);
    }
    return(pending);
}

int64_t utxo_value(char *refcoin,char *srcaddr,bits256 txid,int32_t v)
{
    cJSON *txjson,*vouts,*vout,*sobj,*array; int32_t numvouts,numaddrs; int64_t val,value = 0; char *addr,str[65];
    srcaddr[0] = 0;
    if ( (txjson= get_rawtransaction(refcoin,"",txid)) != 0 )
    {
        if ( (vouts= jarray(&numvouts,txjson,"vout")) != 0 && v < numvouts )
        {
            vout = jitem(vouts,v);
            if ( (val= jdouble(vout,"value")*SATOSHIDEN) != 0 && (sobj= jobj(vout,"scriptPubKey")) != 0 )
            {
                if ( (array= jarray(&numaddrs,sobj,"addresses")) != 0 && numaddrs == 1 && (addr= jstri(array,0)) != 0 && strlen(addr) < 64 )
                {
                    strcpy(srcaddr,addr);
                    value = val;
                } else printf("couldnt get unique address for %s/%d\n",bits256_str(str,txid),v);
            } else printf("error getting value for %s/v%d\n",bits256_str(str,txid),v);
        }
    }
    return(value);
}

int32_t verify_vin(char *refcoin,bits256 txid,int32_t v,char *cmpaddr)
{
    cJSON *txjson,*vins,*vin; int32_t numvins; char vinaddr[64],str[65];
    vinaddr[0] = 0;
    if ( (txjson= get_rawtransaction(refcoin,"",txid)) != 0 )
    {
        if ( (vins= jarray(&numvins,txjson,"vin")) != 0 && v < numvins )
        {
            vin = jitem(vins,v);
            if ( utxo_value(refcoin,vinaddr,jbits256(vin,"txid"),jint(vin,"vout")) > 0 && strcmp(vinaddr,cmpaddr) == 0 )
                return(0);
            printf("mismatched vinaddr.(%s) vs %s\n",vinaddr,cmpaddr);
        }
    }
    return(-1);
}

int32_t txid_in_vins(char *refcoin,bits256 txid,bits256 cmptxid)
{
    cJSON *txjson,*vins,*vin; int32_t numvins,v,vinvout; bits256 vintxid; char str[65];
    if ( (txjson= get_rawtransaction(refcoin,"",txid)) != 0 )
    {
        if ( (vins= jarray(&numvins,txjson,"vin")) != 0 )
        {
            for (v=0; v<numvins; v++)
            {
                vin = jitem(vins,v);
                vintxid = jbits256(vin,"txid");
                vinvout = jint(vin,"vout");
                if ( memcmp(&vintxid,&cmptxid,sizeof(vintxid)) == 0 && vinvout == 0 )
                {
                    return(0);
                }
            }
        }
    }
    return(-1);
}

void genpayout(char *coinstr,char *destaddr,int32_t amount)
{
    char cmd[1024];
    sprintf(cmd,"curl -s --url \"http://127.0.0.1:7783\" --data \"{\\\"userpass\\\":\\\"$userpass\\\",\\\"method\\\":\\\"withdraw\\\",\\\"coin\\\":\\\"%s\\\",\\\"outputs\\\":[{\\\"%s\\\":%.8f},{\\\"RWXL82m4xnBTg1kk6PuS2xekonu7oEeiJG\\\":0.0002}],\\\"broadcast\\\":1}\"\nsleep 3\n",coinstr,destaddr,dstr(amount+20000));
    printf("%s",cmd);
}

bits256 SECONDVIN; int32_t SECONDVOUT;

void genrefund(char *cmd,char *coinstr,bits256 vintxid,char *destaddr,int64_t amount)
{
    char str[65],str2[65];
    sprintf(cmd,"curl -s --url \"http://127.0.0.1:7783\" --data \"{\\\"userpass\\\":\\\"$userpass\\\",\\\"method\\\":\\\"withdraw\\\",\\\"coin\\\":\\\"%s\\\",\\\"onevin\\\":2,\\\"utxotxid\\\":\\\"%s\\\",\\\"utxovout\\\":1,\\\"utxotxid2\\\":\\\"%s\\\",\\\"utxovout2\\\":%d,\\\"outputs\\\":[{\\\"%s\\\":%.8f}],\\\"broadcast\\\":1}\"\nsleep 3\n",coinstr,bits256_str(str,vintxid),bits256_str(str2,SECONDVIN),SECONDVOUT,destaddr,dstr(amount));
    system(cmd);
}

struct addritem
{
    int64_t total,numutxos;
    char addr[64];
    
} ADDRESSES[1200];

struct claimitem
{
    bits256 txid;
    int64_t total,refundvalue;
    int32_t numutxos,disputed,approved;
    char oldaddr[64],destaddr[64],username[64];
    
} CLAIMS[10000];

int32_t NUM_ADDRESSES,NUM_CLAIMS;

int32_t itemvalid(char *refcoin,int64_t *refundedp,int64_t *waitingp,struct claimitem *item)
{
    cJSON *curljson,*txids; int32_t i,numtxids; char str[65],str2[65],url[1000],*retstr; bits256 txid;
    *refundedp = *waitingp = 0;
    if ( item->refundvalue < 0 )
        return(-1);

    sprintf(url,"https://explorer.hush.is/api/addr/%s",item->destaddr);
    if ( (retstr= send_curl(url,"/tmp/itemvalid")) != 0 )
    {
        if ( (curljson= cJSON_Parse(retstr)) != 0 )
        {
            if ( (txids= jarray(&numtxids,curljson,"transactions")) != 0 )
            {
                for (i=0; i<numtxids; i++)
                {
                    txid = jbits256i(txids,i);
                    if ( txid_in_vins(refcoin,txid,item->txid) == 0 )
                    {
                        printf("found item->txid %s inside %s\n",bits256_str(str,item->txid),bits256_str(str2,txid));
                        item->approved = 1;
                        break;
                    }
                }
            }
            free_json(curljson);
        }
        printf("%s\n",retstr);
        free(retstr);
    }
    if ( item->approved != 0 )
        return(1);
    *waitingp = item->refundvalue;
    return(-1);
}

void scan_claims(int32_t issueflag,char *refcoin,int32_t batchid)
{
    char str[65]; int32_t i,num,numstolen=0,numcandidates=0,numinvalids=0,numrefunded=0,numwaiting=0; struct claimitem *item; int64_t batchmin,batchmax,waiting,refunded,possiblerefund=0,possiblestolen = 0,invalidsum=0,totalrefunded=0,waitingsum=0;
    if ( batchid == 0 )
    {
        batchmin = 0;
        batchmax = 7 * SATOSHIDEN;
    }
    else if ( batchid == 1 )
    {
        batchmin = 7 * SATOSHIDEN;
        batchmax = 777 * SATOSHIDEN;
    }
    else if ( batchid == 2 )
    {
        batchmin = 1;//777 * SATOSHIDEN;
        batchmax = 5000 * SATOSHIDEN;
    }
    else if ( batchid == 3 )
    {
        batchmin = 1;//117777 * SATOSHIDEN;
        batchmax = 10000000 * SATOSHIDEN;
    }
    for (i=0; i<NUM_CLAIMS; i++)
    {
        item = &CLAIMS[i];
        if ( item->refundvalue < batchmin || item->refundvalue >= batchmax )
            continue;
        printf("check.%d %s %.8f vs refund %.8f -> %s\n",batchid,item->oldaddr,dstr(item->total),dstr(item->refundvalue),item->destaddr);
        if ( itemvalid(refcoin,&refunded,&waiting,item) < 0 )
        {
            if ( refunded != 0 )
            {
                numrefunded++;
                totalrefunded += refunded;
            }
            else if ( waiting != 0 )
            {
                numwaiting++;
                waitingsum += waiting;
            }
            else
            {
                invalidsum += item->refundvalue;
                numinvalids++;
            }
            continue;
        }
        if ( item->total > item->refundvalue*1.1 + 10*SATOSHIDEN )
        {
            printf("possible.%d stolen %s %.8f vs refund %.8f -> %.8f\n",batchid,item->oldaddr,dstr(item->total),dstr(item->refundvalue),dstr(item->total)-dstr(item->refundvalue));
            numstolen++;
            possiblestolen += (item->total - item->refundvalue);
            item->approved = 0;
        }
        else
        {
            printf("candidate.%d %s %.8f vs refund %.8f -> %s\n",batchid,item->oldaddr,dstr(item->total),dstr(item->refundvalue),item->destaddr);
            numcandidates++;
            possiblerefund += item->refundvalue;
        }
    }
    printf("batchid.%d TOTAL exposure %d %.8f, possible refund %d %.8f, invalids %d %.8f, numrefunded %d %.8f, waiting %d %.8f\n",batchid,numstolen,dstr(possiblestolen),numcandidates,dstr(possiblerefund),numinvalids,dstr(invalidsum),numrefunded,dstr(totalrefunded),numwaiting,dstr(waitingsum));
    for (i=num=0; i<NUM_CLAIMS; i++)
    {
        item = &CLAIMS[i];
        if ( item->approved != 0 )
        {
            printf("%d.%d: approved.%d %s %.8f vs refund %.8f -> %s\n",i,num,batchid,item->oldaddr,dstr(item->total),dstr(item->refundvalue),item->destaddr);
            num++;
            if ( issueflag != 0 )
            {
                static FILE *fp; char cmd[1024];
                if ( fp == 0 )
                    fp = fopen("refund.log","wb");
                genrefund(cmd,refcoin,item->txid,item->destaddr,item->refundvalue);
                if ( fp != 0 )
                {
                    fprintf(fp,"%s,%s,%s,%s,%s,%.8f,%s\n",item->username,refcoin,bits256_str(str,item->txid),item->oldaddr,item->destaddr,dstr(item->refundvalue),cmd);
                    fflush(fp);
                }
                memset(&SECONDVIN,0,sizeof(SECONDVIN));
                SECONDVOUT = 1;
//printf(">>>>>>>>>>>>>>>>>> getchar after (%s)\n",cmd);
//getchar();
            }
        }
    }
}

int32_t update_claimvalue(int32_t *disputedp,char *addr,int64_t amount,bits256 txid)
{
    int32_t i; struct claimitem *item;
    *disputedp = 0;
    for (i=0; i<NUM_CLAIMS; i++)
    {
        if ( strcmp(addr,CLAIMS[i].oldaddr) == 0 )
        {
            item = &CLAIMS[i];
            item->refundvalue = amount;
            if ( bits256_nonz(item->txid) != 0 )
                printf("disputed.%d (%s) %s claimed %.8f vs %.8f\n",item->disputed,item->username,addr,dstr(item->total),dstr(amount));
            item->txid = txid;
            if ( item->disputed != 0 )
                *disputedp = 1;
            return(i);
        }
    }
    return(-1);
}

int64_t update_claimstats(char *username,char *oldaddr,char *destaddr,int64_t amount)
{
    int32_t i; struct claimitem *item;
    printf("claim user.(%s) (%s) -> (%s) %.8f\n",username,oldaddr,destaddr,dstr(amount));
    for (i=0; i<NUM_CLAIMS; i++)
    {
        if ( strcmp(oldaddr,CLAIMS[i].oldaddr) == 0 )
        {
            item = &CLAIMS[i];
            if ( strcmp(destaddr,item->destaddr) != 0 )//|| strcmp(username,item->username) != 0 )
            {
                item->disputed++;
                printf("disputed.%d claim.%-4d: (%36s -> [%36s] %s) vs. (%36s -> [%36s] %s) \n",item->disputed,i,oldaddr,destaddr,username,item->oldaddr,item->destaddr,item->username);
            }
            item->numutxos++;
            item->total += amount;
            return(amount);
        }
    }
    item = &CLAIMS[NUM_CLAIMS++];
    item->total = amount;
    item->numutxos = 1;
    strncpy(item->oldaddr,oldaddr,sizeof(item->oldaddr));
    strncpy(item->destaddr,destaddr,sizeof(item->destaddr));
    strncpy(item->username,username,sizeof(item->username));
    printf("new claim.%-4d: %36s %16.8f -> %36s %s\n",NUM_CLAIMS,oldaddr,dstr(amount),destaddr,username);
    return(amount);
}

int32_t update_addrstats(char *srcaddr,int64_t amount)
{
    int32_t i; struct addritem *item;
    for (i=0; i<NUM_ADDRESSES; i++)
    {
        if ( strcmp(srcaddr,ADDRESSES[i].addr) == 0 )
        {
            ADDRESSES[i].numutxos++;
            ADDRESSES[i].total += amount;
            return(i);
        }
    }
    item = &ADDRESSES[NUM_ADDRESSES++];
    item->total = amount;
    item->numutxos = 1;
    strcpy(item->addr,srcaddr);
    printf("%d new address %s\n",NUM_ADDRESSES,srcaddr);
    return(-1);
}

int64_t sum_of_vins(char *refcoin,int32_t *totalvinsp,int32_t *uniqaddrsp,bits256 txid)
{
    cJSON *txjson,*vins,*vin; char str[65],srcaddr[64]; int32_t i,numarray; int64_t amount,total = 0;
    if ( (txjson= get_rawtransaction(refcoin,"",txid)) != 0 )
    {
        if ( (vins= jarray(&numarray,txjson,"vin")) != 0)
        {
            for (i=0; i<numarray; i++)
            {
                if ( (vin= jitem(vins,i)) != 0 )
                {
                    if ( (amount= utxo_value(refcoin,srcaddr,jbits256(vin,"txid"),jint(vin,"vout"))) == 0 )
                        printf("error getting value from %s/v%d\n",bits256_str(str,jbits256(vin,"txid")),jint(vin,"vout"));
                    else
                    {
                        if ( update_addrstats(srcaddr,amount) < 0 )
                            (*uniqaddrsp)++;
                        //printf("add %s <- %.8f\n",srcaddr,dstr(amount));
                        total += amount;
                        (*totalvinsp)++;
                    }
                }
            }
        }
    }
    if ( total == 0 )
        printf("sum_of_vins error %s\n",bits256_str(str,txid));
    return(total);
}

void reconcile_claims(char *refcoin,char *fname)
{
    FILE *fp; double amount; int32_t i,n,numlines = 0; char buf[1024],fields[16][256],*str; int64_t total = 0;
    if ( (fp= fopen(fname,"rb")) != 0 )
    {
        while ( fgets(buf,sizeof(buf),fp) > 0 )
        {
            printf("%d.(%s)\n",numlines,buf);
            str = buf;
            n = i = 0;
            memset(fields,0,sizeof(fields));
            while ( *str != 0 )
            {
                if ( *str == ',' || *str == '\n' || *str == '\r' )
                {
                    fields[n][i] = 0;
                    i = 0;
                    if ( n > 1 )
                    {
                        printf("(%16s) ",fields[n]);
                    }
                    n++;
                    if ( *str == '\n' || *str == '\r' )
                        break;
                }
                if ( *str == ',' || *str == ' ' )
                    str++;
                else fields[n][i++] = *str++;
            }
            printf("%s\n",fields[1]);
            total += update_claimstats(fields[1],fields[3],fields[5 + (strcmp("HUSH3",refcoin)==0)],atof(fields[4])*SATOSHIDEN + 0.0000000049);
            numlines++;
        }
        fclose(fp);
    }
    printf("total claims %.8f\n",dstr(total));
}

int32_t main(int32_t argc,char **argv)
{
    char *coinstr,*acstr,*addr,buf[64],srcaddr[64],str[65]; cJSON *retjson,*item; int32_t i,n,disputed,numdisputed,numsmall=0,numpayouts=0,numclaims=0,num=0,totalvins=0,uniqaddrs=0; int64_t amount,total = 0,total2 = 0,payout,maxpayout,smallpayout=0,totalpayout = 0,totaldisputed = 0,totaldisputed2 = 0,fundingamount = 0;
    if ( argc != 2 )
    {
        printf("argc needs to be 2: <prog> coin\n");
        return(-1);
    }
    if ( strcmp(argv[1],"HUSH3") == 0 )
    {
        REFCOIN_CLI = "./hush-cli";
        coinstr = clonestr("HUSH3");
        acstr = "";
    }
    else
    {
        sprintf(buf,"./hush-cli -ac_name=%s",argv[1]);
        REFCOIN_CLI = clonestr(buf);
        coinstr = clonestr(argv[1]);
        acstr = coinstr;
    }
    if ( 1 )
    {
        sprintf(buf,"%s-Claims.csv",coinstr);
        reconcile_claims(coinstr,buf);
        for (i=0; i<NUM_CLAIMS; i++)
        {
            if ( CLAIMS[i].disputed != 0 )
            {
                totaldisputed += CLAIMS[i].total;
                printf("disputed %s %.8f\n",CLAIMS[i].oldaddr,dstr(CLAIMS[i].total));
            }
        }
        printf("total disputed %.8f\n",dstr(totaldisputed));
        totaldisputed2 = 0;
        if ( (retjson=  get_listunspent(coinstr,acstr)) != 0 )
        {
            if ( (n= cJSON_GetArraySize(retjson)) > 0 )
            {
                for (i=0; i<n; i++)
                {
                    item = jitem(retjson,i);
                    amount = jdouble(item,"amount")*SATOSHIDEN;
                    if ( (addr= jstr(item,"address")) != 0 && strcmp(addr,"RWXL82m4xnBTg1kk6PuS2xekonu7oEeiJG") == 0 )
                    {
                        if ( amount != 20000 )
                        {
                            if ( amount > fundingamount )
                            {
                                fundingamount = amount;
                                SECONDVIN = jbits256(item,"txid");
                                SECONDVOUT = jint(item,"vout");
                                printf("set SECONDVIN to %s/v%d %.8f\n",bits256_str(str,SECONDVIN),SECONDVOUT,dstr(amount));
                            }
                            continue;
                        }
                        if ( strcmp(coinstr,"HUSH3") == 0 && verify_vin(coinstr,jbits256(item,"txid"),0,"R9JCEd6xnCxNUSpLrHEWvzPSh7CNXm7z75") < 0 )
                        {
                            printf("WARNING: imposter dust detected! %s\n",bits256_str(str,jbits256(item,"txid")));
                            continue;
                        }
                        else if ( strcmp(coinstr,"HUSH3") != 0 && verify_vin(coinstr,jbits256(item,"txid"),0,"R9MUnxXijovvSeT9sFuUX23TiFtVvZEGjT") < 0 )
                        {
                            printf("WARNING: imposter dust detected! %s\n",bits256_str(str,jbits256(item,"txid")));
                            continue;
                        }
                        amount = (utxo_value(coinstr,srcaddr,jbits256(item,"txid"),0) - 20000) * SATOSHIDEN;
                        //printf("%d: %s claimvalue %.8f\n",num,srcaddr,dstr(amount));
                        num++;
                        total2 += amount;
                        if ( update_claimvalue(&disputed,srcaddr,amount,jbits256(item,"txid")) >= 0 )
                        {
                            if ( disputed != 0 )
                            {
                                totaldisputed2 += amount;
                                numdisputed++;
                            }
                            else
                            {
                                numclaims++;
                                total += amount;
                            }
                        }
                    }
                }
            }
            free_json(retjson);
            printf("remaining refunds.%d %.8f, numclaims.%d %.8f, numdisputed.%d %.8f\n",num,dstr(total2),numclaims,dstr(total),numdisputed,dstr(totaldisputed2));
        }
        //scan_claims(0,coinstr,0);
        //scan_claims(0,coinstr,1);
        //scan_claims(0,coinstr,2);
        scan_claims(1,coinstr,3);
    }
    else if ( (retjson= get_listunspent(coinstr,acstr)) != 0 )
    {
        if ( (n= cJSON_GetArraySize(retjson)) > 0 )
        {
            for (i=0; i<n; i++)
            {
                item = jitem(retjson,i);
                if ( (addr= jstr(item,"address")) != 0 && strcmp(addr,"RSgD2cmm3niFRu2kwwtrEHoHMywJdkbkeF") == 0 )
                {
                    if ( (amount= jdouble(item,"amount")*SATOSHIDEN) != 0 )
                    {
                        num++;
                        total += amount;
                        total2 += sum_of_vins(coinstr,&totalvins,&uniqaddrs,jbits256(item,"txid"));
                    }
                }
            }
        }
        free_json(retjson);
        maxpayout = 0;
        for (i=0; i<NUM_ADDRESSES; i++)
        {
            if ( ADDRESSES[i].total >= SATOSHIDEN )
            {
                payout = ADDRESSES[i].total / SATOSHIDEN;
                if ( payout > maxpayout )
                    maxpayout = payout;
                totalpayout += payout;
                numpayouts++;
                //if ( payout >= 7 )
                //{
                //  numsmall++;
                //smallpayout += payout;
                    genpayout(coinstr,ADDRESSES[i].addr,payout);
                //}
                //printf("%-4d: %-64s numutxos.%-4lld %llu\n",i,ADDRESSES[i].addr,ADDRESSES[i].numutxos,(long long)payout);
            }
        }
        printf("num.%d total %.8f vs vintotal %.8f, totalvins.%d uniqaddrs.%d:%d totalpayout %llu maxpayout %llu numpayouts.%d numsmall.%d %llu\n",num,dstr(total),dstr(total2),totalvins,uniqaddrs,NUM_ADDRESSES,(long long)totalpayout,(long long)maxpayout,numpayouts,numsmall,(long long)smallpayout);
    }
}
    
int32_t zmigratemain(int32_t argc,char **argv)
{
    char buf[1024],*zsaddr,*coinstr;
    if ( argc != 3 )
    {
        printf("argc needs to be 3\n");
        return(-1);
    }
    if ( strcmp(argv[1],"HUSH3") == 0 )
    {
        REFCOIN_CLI = "./hush-cli";
        coinstr = clonestr("HUSH3");
    }
    else
    {
        sprintf(buf,"./hush-cli -ac_name=%s",argv[1]);
        REFCOIN_CLI = clonestr(buf);
        coinstr = clonestr(argv[1]);
    }
    if ( argv[2][0] != 'z' || argv[2][1] != 's' )
    {
        printf("invalid sapling address (%s)\n",argv[2]);
        return(-2);
    }
    if ( z_validateaddress(coinstr,"",argv[2],"ismine") == 0 )
    {
        printf("invalid sapling address (%s)\n",argv[2]);
        return(-3);
    }
    zsaddr = clonestr(argv[2]);
    printf("%s: %s %s\n",REFCOIN_CLI,coinstr,zsaddr);
    uint32_t lastopid; char coinaddr[64],privkey[1024],zcaddr[128],opidstr[128]; int32_t finished; int64_t amount,stdamount,txfee;
    //stdamount = 500 * SATOSHIDEN;
    txfee = 10000;
again:
    if ( z_getnewaddress(zcaddr,coinstr,"","sprout") == 0 )
    {
        z_exportkey(privkey,coinstr,"",zcaddr);
        printf("zcaddr.(%s) -> z_exportkey.(%s)\n",zcaddr,privkey);
        while ( 1 )
        {
            if ( have_pending_opid(coinstr,0) != 0 )
            {
                sleep(10);
                continue;
            }
            if ( z_mergetoaddress(opidstr,coinstr,"",zcaddr) <= 0 )
                break;
        }
    }
    printf("start processing zmigrate\n");
    lastopid = (uint32_t)time(NULL);
    finished = 0;
    while ( 1 )
    {
        if ( have_pending_opid(coinstr,0) != 0 )
        {
            sleep(10);
            continue;
        }
        if ( (amount= find_onetime_amount(coinstr,coinaddr)) > txfee )
        {
            // find taddr with funds and send all to zsaddr
            z_sendmany(opidstr,coinstr,"",coinaddr,zsaddr,amount-txfee);
            lastopid = (uint32_t)time(NULL);
            sleep(1);
            continue;
        }
        if ( (amount= find_sprout_amount(coinstr,zcaddr)) > txfee )
        {
            // generate taddr, send max of 10000.0001
            static int64_t lastamount,lastamount2,lastamount3,lastamount4,refamount = 5000 * SATOSHIDEN;
            stdamount = refamount;
            if ( amount == lastamount && amount == lastamount2 )
            {
                stdamount /= 10;
                if ( amount == lastamount3 && amount == lastamount4 )
                    stdamount /= 10;
            }
            if ( stdamount < SATOSHIDEN )
            {
                stdamount = SATOSHIDEN;
                refamount = SATOSHIDEN * 50;
            }
            if ( stdamount < refamount )
                refamount = stdamount;
            lastamount4 = lastamount3;
            lastamount3 = lastamount2;
            lastamount2 = lastamount;
            lastamount = amount;
            if ( amount > stdamount+2*txfee )
                amount = stdamount + 2*txfee;
            if ( getnewaddress(coinaddr,coinstr,"") == 0 )
            {
                z_sendmany(opidstr,coinstr,"",zcaddr,coinaddr,amount-txfee);
                lastopid = (uint32_t)time(NULL);
            } else printf("couldnt getnewaddress!\n");
            sleep(3);
            continue;
        }
        if ( time(NULL) > lastopid+600 )
            break;
    }
    sleep(3);
    printf("%s %s ALLDONE! taddr %.8f sprout %.8f mempool empty.%d\n",coinstr,zsaddr,dstr(find_onetime_amount(coinstr,coinaddr)),dstr(find_sprout_amount(coinstr,zcaddr)),empty_mempool(coinstr,""));
    sleep(3);
    if ( find_onetime_amount(coinstr,coinaddr) == 0 && find_sprout_amount(coinstr,zcaddr) == 0 )
    {
        printf("about to purge all opid results!. ctrl-C to abort, <enter> to proceed\n");
        getchar();
        have_pending_opid(coinstr,1);
    } else goto again;
    return(0);
}
