]> Git Repo - VerusCoin.git/commitdiff
Latest Zcash updates
authormiketout <[email protected]>
Thu, 20 Sep 2018 20:32:57 +0000 (13:32 -0700)
committermiketout <[email protected]>
Thu, 20 Sep 2018 20:32:57 +0000 (13:32 -0700)
14 files changed:
1  2 
qa/pull-tester/rpc-tests.sh
qa/rpc-tests/test_framework/util.py
src/keystore.h
src/komodo_bitcoind.h
src/main.cpp
src/main.h
src/net.cpp
src/rpc/rawtransaction.cpp
src/test/rpc_wallet_tests.cpp
src/wallet/asyncrpcoperation_sendmany.cpp
src/wallet/rpcdump.cpp
src/wallet/rpcwallet.cpp
src/wallet/wallet.cpp
src/wallet/wallet.h

Simple merge
Simple merge
diff --cc src/keystore.h
Simple merge
index 4bd321883b0dbd96bda15c39937d9124440434db,0000000000000000000000000000000000000000..263c79e431ec26cfc8717bb039ca435d85c4fc1c
mode 100644,000000..100644
--- /dev/null
@@@ -1,1823 -1,0 +1,1823 @@@
-                 printf("ERROR: invalid nonce value for PoS block\nnNonce: %s\nrawHash: %s\nposHash: %s\nvalue: %ull\n",
 +/******************************************************************************
 + * Copyright © 2014-2018 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.            *
 + *                                                                            *
 + ******************************************************************************/
 +
 +// komodo functions that interact with bitcoind C++
 +
 +#include <curl/curl.h>
 +#include <curl/easy.h>
 +#include "primitives/nonce.h"
 +#include "consensus/params.h"
 +#include "komodo_defs.h"
 +
 +int32_t komodo_notaries(uint8_t pubkeys[64][33],int32_t height,uint32_t timestamp);
 +int32_t komodo_electednotary(int32_t *numnotariesp,uint8_t *pubkey33,int32_t height,uint32_t timestamp);
 +unsigned int lwmaGetNextPOSRequired(const CBlockIndex* pindexLast, const Consensus::Params& params);
 +
 +//#define issue_curl(cmdstr) bitcoind_RPC(0,(char *)"curl",(char *)"http://127.0.0.1:7776",0,0,(char *)(cmdstr))
 +
 +struct MemoryStruct { char *memory; size_t size; };
 +struct return_string { char *ptr; size_t len; };
 +
 +// return data from the server
 +#define CURL_GLOBAL_ALL (CURL_GLOBAL_SSL|CURL_GLOBAL_WIN32)
 +#define CURL_GLOBAL_SSL (1<<0)
 +#define CURL_GLOBAL_WIN32 (1<<1)
 +
 +
 +/************************************************************************
 + *
 + * Initialize the string handler so that it is thread safe
 + *
 + ************************************************************************/
 +
 +void init_string(struct return_string *s)
 +{
 +    s->len = 0;
 +    s->ptr = (char *)calloc(1,s->len+1);
 +    if ( s->ptr == NULL )
 +    {
 +        fprintf(stderr,"init_string malloc() failed\n");
 +        exit(-1);
 +    }
 +    s->ptr[0] = '\0';
 +}
 +
 +/************************************************************************
 + *
 + * Use the "writer" to accumulate text until done
 + *
 + ************************************************************************/
 +
 +size_t accumulatebytes(void *ptr,size_t size,size_t nmemb,struct return_string *s)
 +{
 +    size_t new_len = s->len + size*nmemb;
 +    s->ptr = (char *)realloc(s->ptr,new_len+1);
 +    if ( s->ptr == NULL )
 +    {
 +        fprintf(stderr, "accumulate realloc() failed\n");
 +        exit(-1);
 +    }
 +    memcpy(s->ptr+s->len,ptr,size*nmemb);
 +    s->ptr[new_len] = '\0';
 +    s->len = new_len;
 +    return(size * nmemb);
 +}
 +
 +/************************************************************************
 + *
 + * return the current system time in milliseconds
 + *
 + ************************************************************************/
 +
 +#define EXTRACT_BITCOIND_RESULT  // if defined, ensures error is null and returns the "result" field
 +#ifdef EXTRACT_BITCOIND_RESULT
 +
 +/************************************************************************
 + *
 + * perform post processing of the results
 + *
 + ************************************************************************/
 +
 +char *post_process_bitcoind_RPC(char *debugstr,char *command,char *rpcstr,char *params)
 +{
 +    long i,j,len; char *retstr = 0; cJSON *json,*result,*error;
 +    //printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr);
 +    if ( command == 0 || rpcstr == 0 || rpcstr[0] == 0 )
 +    {
 +        if ( strcmp(command,"signrawtransaction") != 0 )
 +            printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s.[%s]\n",debugstr,command,rpcstr);
 +        return(rpcstr);
 +    }
 +    json = cJSON_Parse(rpcstr);
 +    if ( json == 0 )
 +    {
 +        printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC.%s can't parse.(%s) params.(%s)\n",debugstr,command,rpcstr,params);
 +        free(rpcstr);
 +        return(0);
 +    }
 +    result = cJSON_GetObjectItem(json,"result");
 +    error = cJSON_GetObjectItem(json,"error");
 +    if ( error != 0 && result != 0 )
 +    {
 +        if ( (error->type&0xff) == cJSON_NULL && (result->type&0xff) != cJSON_NULL )
 +        {
 +            retstr = cJSON_Print(result);
 +            len = strlen(retstr);
 +            if ( retstr[0] == '"' && retstr[len-1] == '"' )
 +            {
 +                for (i=1,j=0; i<len-1; i++,j++)
 +                    retstr[j] = retstr[i];
 +                retstr[j] = 0;
 +            }
 +        }
 +        else if ( (error->type&0xff) != cJSON_NULL || (result->type&0xff) != cJSON_NULL )
 +        {
 +            if ( strcmp(command,"signrawtransaction") != 0 )
 +                printf("<<<<<<<<<<< bitcoind_RPC: %s post_process_bitcoind_RPC (%s) error.%s\n",debugstr,command,rpcstr);
 +        }
 +        free(rpcstr);
 +    } else retstr = rpcstr;
 +    free_json(json);
 +    //fprintf(stderr,"<<<<<<<<<<< bitcoind_RPC: postprocess returns.(%s)\n",retstr);
 +    return(retstr);
 +}
 +#endif
 +
 +/************************************************************************
 + *
 + * perform the query
 + *
 + ************************************************************************/
 +
 +char *bitcoind_RPC(char **retstrp,char *debugstr,char *url,char *userpass,char *command,char *params)
 +{
 +    static int didinit,count,count2; static double elapsedsum,elapsedsum2;
 +    struct curl_slist *headers = NULL; struct return_string s; CURLcode res; CURL *curl_handle;
 +    char *bracket0,*bracket1,*databuf = 0; long len; int32_t specialcase,numretries; double starttime;
 +    if ( didinit == 0 )
 +    {
 +        didinit = 1;
 +        curl_global_init(CURL_GLOBAL_ALL); //init the curl session
 +    }
 +    numretries = 0;
 +    if ( debugstr != 0 && strcmp(debugstr,"BTCD") == 0 && command != 0 && strcmp(command,"SuperNET") ==  0 )
 +        specialcase = 1;
 +    else specialcase = 0;
 +    if ( url[0] == 0 )
 +        strcpy(url,"http://127.0.0.1:7876/nxt");
 +    if ( specialcase != 0 && 0 )
 +        printf("<<<<<<<<<<< bitcoind_RPC: debug.(%s) url.(%s) command.(%s) params.(%s)\n",debugstr,url,command,params);
 +try_again:
 +    if ( retstrp != 0 )
 +        *retstrp = 0;
 +    starttime = OS_milliseconds();
 +    curl_handle = curl_easy_init();
 +    init_string(&s);
 +    headers = curl_slist_append(0,"Expect:");
 +    
 +    curl_easy_setopt(curl_handle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )");
 +    curl_easy_setopt(curl_handle,CURLOPT_HTTPHEADER,  headers);
 +    curl_easy_setopt(curl_handle,CURLOPT_URL,         url);
 +    curl_easy_setopt(curl_handle,CURLOPT_WRITEFUNCTION,       (void *)accumulatebytes);               // send all data to this function
 +    curl_easy_setopt(curl_handle,CURLOPT_WRITEDATA,           &s);                    // we pass our 's' struct to the callback
 +    curl_easy_setopt(curl_handle,CURLOPT_NOSIGNAL,            1L);                    // supposed to fix "Alarm clock" and long jump crash
 +    curl_easy_setopt(curl_handle,CURLOPT_NOPROGRESS,  1L);                    // no progress callback
 +    if ( strncmp(url,"https",5) == 0 )
 +    {
 +        curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYPEER,0);
 +        curl_easy_setopt(curl_handle,CURLOPT_SSL_VERIFYHOST,0);
 +    }
 +    if ( userpass != 0 )
 +        curl_easy_setopt(curl_handle,CURLOPT_USERPWD, userpass);
 +    databuf = 0;
 +    if ( params != 0 )
 +    {
 +        if ( command != 0 && specialcase == 0 )
 +        {
 +            len = strlen(params);
 +            if ( len > 0 && params[0] == '[' && params[len-1] == ']' ) {
 +                bracket0 = bracket1 = (char *)"";
 +            }
 +            else
 +            {
 +                bracket0 = (char *)"[";
 +                bracket1 = (char *)"]";
 +            }
 +            
 +            databuf = (char *)malloc(256 + strlen(command) + strlen(params));
 +            sprintf(databuf,"{\"id\":\"jl777\",\"method\":\"%s\",\"params\":%s%s%s}",command,bracket0,params,bracket1);
 +            //printf("url.(%s) userpass.(%s) databuf.(%s)\n",url,userpass,databuf);
 +            //
 +        } //else if ( specialcase != 0 ) fprintf(stderr,"databuf.(%s)\n",params);
 +        curl_easy_setopt(curl_handle,CURLOPT_POST,1L);
 +        if ( databuf != 0 )
 +            curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,databuf);
 +        else curl_easy_setopt(curl_handle,CURLOPT_POSTFIELDS,params);
 +    }
 +    //laststart = milliseconds();
 +    res = curl_easy_perform(curl_handle);
 +    curl_slist_free_all(headers);
 +    curl_easy_cleanup(curl_handle);
 +    if ( databuf != 0 ) // clean up temporary buffer
 +    {
 +        free(databuf);
 +        databuf = 0;
 +    }
 +    if ( res != CURLE_OK )
 +    {
 +        numretries++;
 +        if ( specialcase != 0 )
 +        {
 +            printf("<<<<<<<<<<< bitcoind_RPC.(%s): BTCD.%s timeout params.(%s) s.ptr.(%s) err.%d\n",url,command,params,s.ptr,res);
 +            free(s.ptr);
 +            return(0);
 +        }
 +        else if ( numretries >= 1 )
 +        {
 +            //printf("Maximum number of retries exceeded!\n");
 +            free(s.ptr);
 +            return(0);
 +        }
 +        if ( (rand() % 1000) == 0 )
 +            printf( "curl_easy_perform() failed: %s %s.(%s %s), retries: %d\n",curl_easy_strerror(res),debugstr,url,command,numretries);
 +        free(s.ptr);
 +        sleep((1<<numretries));
 +        goto try_again;
 +        
 +    }
 +    else
 +    {
 +        if ( command != 0 && specialcase == 0 )
 +        {
 +            count++;
 +            elapsedsum += (OS_milliseconds() - starttime);
 +            if ( (count % 1000000) == 0)
 +                printf("%d: ave %9.6f | elapsed %.3f millis | bitcoind_RPC.(%s) url.(%s)\n",count,elapsedsum/count,(OS_milliseconds() - starttime),command,url);
 +            if ( retstrp != 0 )
 +            {
 +                *retstrp = s.ptr;
 +                return(s.ptr);
 +            }
 +            return(post_process_bitcoind_RPC(debugstr,command,s.ptr,params));
 +        }
 +        else
 +        {
 +            if ( 0 && specialcase != 0 )
 +                fprintf(stderr,"<<<<<<<<<<< bitcoind_RPC: BTCD.(%s) -> (%s)\n",params,s.ptr);
 +            count2++;
 +            elapsedsum2 += (OS_milliseconds() - starttime);
 +            if ( (count2 % 10000) == 0)
 +                printf("%d: ave %9.6f | elapsed %.3f millis | NXT calls.(%s) cmd.(%s)\n",count2,elapsedsum2/count2,(double)(OS_milliseconds() - starttime),url,command);
 +            return(s.ptr);
 +        }
 +    }
 +    printf("bitcoind_RPC: impossible case\n");
 +    free(s.ptr);
 +    return(0);
 +}
 +
 +static size_t WriteMemoryCallback(void *ptr,size_t size,size_t nmemb,void *data)
 +{
 +    size_t realsize = (size * nmemb);
 +    struct MemoryStruct *mem = (struct MemoryStruct *)data;
 +    mem->memory = (char *)((ptr != 0) ? realloc(mem->memory,mem->size + realsize + 1) : malloc(mem->size + realsize + 1));
 +    if ( mem->memory != 0 )
 +    {
 +        if ( ptr != 0 )
 +            memcpy(&(mem->memory[mem->size]),ptr,realsize);
 +        mem->size += realsize;
 +        mem->memory[mem->size] = 0;
 +    }
 +    //printf("got %d bytes\n",(int32_t)(size*nmemb));
 +    return(realsize);
 +}
 +
 +char *curl_post(CURL **cHandlep,char *url,char *userpass,char *postfields,char *hdr0,char *hdr1,char *hdr2,char *hdr3)
 +{
 +    struct MemoryStruct chunk; CURL *cHandle; long code; struct curl_slist *headers = 0;
 +    if ( (cHandle= *cHandlep) == NULL )
 +        *cHandlep = cHandle = curl_easy_init();
 +    else curl_easy_reset(cHandle);
 +    //#ifdef DEBUG
 +    //curl_easy_setopt(cHandle,CURLOPT_VERBOSE, 1);
 +    //#endif
 +    curl_easy_setopt(cHandle,CURLOPT_USERAGENT,"mozilla/4.0");//"Mozilla/4.0 (compatible; )");
 +    curl_easy_setopt(cHandle,CURLOPT_SSL_VERIFYPEER,0);
 +    //curl_easy_setopt(cHandle,CURLOPT_SSLVERSION,1);
 +    curl_easy_setopt(cHandle,CURLOPT_URL,url);
 +    curl_easy_setopt(cHandle,CURLOPT_CONNECTTIMEOUT,10);
 +    if ( userpass != 0 && userpass[0] != 0 )
 +        curl_easy_setopt(cHandle,CURLOPT_USERPWD,userpass);
 +    if ( postfields != 0 && postfields[0] != 0 )
 +    {
 +        curl_easy_setopt(cHandle,CURLOPT_POST,1);
 +        curl_easy_setopt(cHandle,CURLOPT_POSTFIELDS,postfields);
 +    }
 +    if ( hdr0 != NULL && hdr0[0] != 0 )
 +    {
 +        //printf("HDR0.(%s) HDR1.(%s) HDR2.(%s) HDR3.(%s)\n",hdr0!=0?hdr0:"",hdr1!=0?hdr1:"",hdr2!=0?hdr2:"",hdr3!=0?hdr3:"");
 +        headers = curl_slist_append(headers,hdr0);
 +        if ( hdr1 != 0 && hdr1[0] != 0 )
 +            headers = curl_slist_append(headers,hdr1);
 +        if ( hdr2 != 0 && hdr2[0] != 0 )
 +            headers = curl_slist_append(headers,hdr2);
 +        if ( hdr3 != 0 && hdr3[0] != 0 )
 +            headers = curl_slist_append(headers,hdr3);
 +    } //headers = curl_slist_append(0,"Expect:");
 +    if ( headers != 0 )
 +        curl_easy_setopt(cHandle,CURLOPT_HTTPHEADER,headers);
 +    //res = curl_easy_perform(cHandle);
 +    memset(&chunk,0,sizeof(chunk));
 +    curl_easy_setopt(cHandle,CURLOPT_WRITEFUNCTION,WriteMemoryCallback);
 +    curl_easy_setopt(cHandle,CURLOPT_WRITEDATA,(void *)&chunk);
 +    curl_easy_perform(cHandle);
 +    curl_easy_getinfo(cHandle,CURLINFO_RESPONSE_CODE,&code);
 +    if ( headers != 0 )
 +        curl_slist_free_all(headers);
 +    if ( code != 200 )
 +        printf("(%s) server responded with code %ld (%s)\n",url,code,chunk.memory);
 +    return(chunk.memory);
 +}
 +
 +char *komodo_issuemethod(char *userpass,char *method,char *params,uint16_t port)
 +{
 +    //static void *cHandle;
 +    char url[512],*retstr=0,*retstr2=0,postdata[8192];
 +    if ( params == 0 || params[0] == 0 )
 +        params = (char *)"[]";
 +    if ( strlen(params) < sizeof(postdata)-128 )
 +    {
 +        sprintf(url,(char *)"http://127.0.0.1:%u",port);
 +        sprintf(postdata,"{\"method\":\"%s\",\"params\":%s}",method,params);
 +        //printf("[%s] (%s) postdata.(%s) params.(%s) USERPASS.(%s)\n",ASSETCHAINS_SYMBOL,url,postdata,params,KMDUSERPASS);
 +        retstr2 = bitcoind_RPC(&retstr,(char *)"debug",url,userpass,method,params);
 +        //retstr = curl_post(&cHandle,url,USERPASS,postdata,0,0,0,0);
 +    }
 +    return(retstr2);
 +}
 +
 +int32_t notarizedtxid_height(char *dest,char *txidstr,int32_t *kmdnotarized_heightp)
 +{
 +    char *jsonstr,params[256],*userpass; uint16_t port; cJSON *json,*item; int32_t height = 0,txid_height = 0,txid_confirmations = 0;
 +    params[0] = 0;
 +    *kmdnotarized_heightp = 0;
 +    if ( strcmp(dest,"KMD") == 0 )
 +    {
 +        port = KMD_PORT;
 +        userpass = KMDUSERPASS;
 +    }
 +    else if ( strcmp(dest,"BTC") == 0 )
 +    {
 +        port = 8332;
 +        userpass = BTCUSERPASS;
 +    }
 +    else return(0);
 +    if ( userpass[0] != 0 )
 +    {
 +        if ( (jsonstr= komodo_issuemethod(userpass,(char *)"getinfo",params,port)) != 0 )
 +        {
 +            //printf("(%s)\n",jsonstr);
 +            if ( (json= cJSON_Parse(jsonstr)) != 0 )
 +            {
 +                if ( (item= jobj(json,(char *)"result")) != 0 )
 +                {
 +                    height = jint(item,(char *)"blocks");
 +                    *kmdnotarized_heightp = strcmp(dest,"KMD") == 0 ? jint(item,(char *)"notarized") : height;
 +                }
 +                free_json(json);
 +            }
 +            free(jsonstr);
 +        }
 +        sprintf(params,"[\"%s\", 1]",txidstr);
 +        if ( (jsonstr= komodo_issuemethod(userpass,(char *)"getrawtransaction",params,port)) != 0 )
 +        {
 +            //printf("(%s)\n",jsonstr);
 +            if ( (json= cJSON_Parse(jsonstr)) != 0 )
 +            {
 +                if ( (item= jobj(json,(char *)"result")) != 0 )
 +                {
 +                    txid_confirmations = jint(item,(char *)"confirmations");
 +                    if ( txid_confirmations > 0 && height > txid_confirmations )
 +                        txid_height = height - txid_confirmations;
 +                    else txid_height = height;
 +                    //printf("height.%d tconfs.%d txid_height.%d\n",height,txid_confirmations,txid_height);
 +                }
 +                free_json(json);
 +            }
 +            free(jsonstr);
 +        }
 +    }
 +    return(txid_height);
 +}
 +
 +int32_t komodo_verifynotarizedscript(int32_t height,uint8_t *script,int32_t len,uint256 NOTARIZED_HASH)
 +{
 +    int32_t i; uint256 hash; char params[256];
 +    for (i=0; i<32; i++)
 +        ((uint8_t *)&hash)[i] = script[2+i];
 +    if ( hash == NOTARIZED_HASH )
 +        return(1);
 +    for (i=0; i<32; i++)
 +        printf("%02x",((uint8_t *)&NOTARIZED_HASH)[i]);
 +    printf(" notarized, ");
 +    for (i=0; i<32; i++)
 +        printf("%02x",((uint8_t *)&hash)[i]);
 +    printf(" opreturn from [%s] ht.%d MISMATCHED\n",ASSETCHAINS_SYMBOL,height);
 +    return(-1);
 +}
 +
 +int32_t komodo_verifynotarization(char *symbol,char *dest,int32_t height,int32_t NOTARIZED_HEIGHT,uint256 NOTARIZED_HASH,uint256 NOTARIZED_DESTTXID)
 +{
 +    char params[256],*jsonstr,*hexstr; uint8_t *script,_script[8192]; int32_t n,len,retval = -1; cJSON *json,*txjson,*vouts,*vout,*skey;
 +    script = _script;
 +    /*params[0] = '[';
 +     params[1] = '"';
 +     for (i=0; i<32; i++)
 +     sprintf(&params[i*2 + 2],"%02x",((uint8_t *)&NOTARIZED_DESTTXID)[31-i]);
 +     strcat(params,"\", 1]");*/
 +    sprintf(params,"[\"%s\", 1]",NOTARIZED_DESTTXID.ToString().c_str());
 +    if ( strcmp(symbol,ASSETCHAINS_SYMBOL[0]==0?(char *)"KMD":ASSETCHAINS_SYMBOL) != 0 )
 +        return(0);
 +    if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 )
 +        printf("[%s] src.%s dest.%s params.[%s] ht.%d notarized.%d\n",ASSETCHAINS_SYMBOL,symbol,dest,params,height,NOTARIZED_HEIGHT);
 +    if ( strcmp(dest,"KMD") == 0 )
 +    {
 +        if ( KMDUSERPASS[0] != 0 )
 +        {
 +            if ( ASSETCHAINS_SYMBOL[0] != 0 )
 +            {
 +                jsonstr = komodo_issuemethod(KMDUSERPASS,(char *)"getrawtransaction",params,KMD_PORT);
 +                //printf("userpass.(%s) got (%s)\n",KMDUSERPASS,jsonstr);
 +            }
 +        }//else jsonstr = _dex_getrawtransaction();
 +        else return(0); // need universal way to issue DEX* API, since notaries mine most blocks, this ok
 +    }
 +    else if ( strcmp(dest,"BTC") == 0 )
 +    {
 +        if ( BTCUSERPASS[0] != 0 )
 +        {
 +            //printf("BTCUSERPASS.(%s)\n",BTCUSERPASS);
 +            jsonstr = komodo_issuemethod(BTCUSERPASS,(char *)"getrawtransaction",params,8332);
 +        }
 +        //else jsonstr = _dex_getrawtransaction();
 +        else return(0);
 +    }
 +    else
 +    {
 +        printf("[%s] verifynotarization error unexpected dest.(%s)\n",ASSETCHAINS_SYMBOL,dest);
 +        return(-1);
 +    }
 +    if ( jsonstr != 0 )
 +    {
 +        if ( (json= cJSON_Parse(jsonstr)) != 0 )
 +        {
 +            if ( (txjson= jobj(json,(char *)"result")) != 0 && (vouts= jarray(&n,txjson,(char *)"vout")) > 0 )
 +            {
 +                vout = jitem(vouts,n-1);
 +                if ( 0 && ASSETCHAINS_SYMBOL[0] != 0 )
 +                    printf("vout.(%s)\n",jprint(vout,0));
 +                if ( (skey= jobj(vout,(char *)"scriptPubKey")) != 0 )
 +                {
 +                    if ( (hexstr= jstr(skey,(char *)"hex")) != 0 )
 +                    {
 +                        //printf("HEX.(%s) vs hash.%s\n",hexstr,NOTARIZED_HASH.ToString().c_str());
 +                        len = strlen(hexstr) >> 1;
 +                        decode_hex(script,len,hexstr);
 +                        if ( script[1] == 0x4c )
 +                        {
 +                            script++;
 +                            len--;
 +                        }
 +                        else if ( script[1] == 0x4d )
 +                        {
 +                            script += 2;
 +                            len -= 2;
 +                        }
 +                        retval = komodo_verifynotarizedscript(height,script,len,NOTARIZED_HASH);
 +                    }
 +                }
 +            }
 +            free_json(txjson);
 +        }
 +        free(jsonstr);
 +    }
 +    return(retval);
 +}
 +
 +/*uint256 komodo_getblockhash(int32_t height)
 + {
 + uint256 hash; char params[128],*hexstr,*jsonstr; cJSON *result; int32_t i; uint8_t revbuf[32];
 + memset(&hash,0,sizeof(hash));
 + sprintf(params,"[%d]",height);
 + if ( (jsonstr= komodo_issuemethod(KMDUSERPASS,(char *)"getblockhash",params,BITCOIND_RPCPORT)) != 0 )
 + {
 + if ( (result= cJSON_Parse(jsonstr)) != 0 )
 + {
 + if ( (hexstr= jstr(result,(char *)"result")) != 0 )
 + {
 + if ( is_hexstr(hexstr,0) == 64 )
 + {
 + decode_hex(revbuf,32,hexstr);
 + for (i=0; i<32; i++)
 + ((uint8_t *)&hash)[i] = revbuf[31-i];
 + }
 + }
 + free_json(result);
 + }
 + printf("KMD hash.%d (%s) %x\n",height,jsonstr,*(uint32_t *)&hash);
 + free(jsonstr);
 + }
 + return(hash);
 + }
 + 
 + uint256 _komodo_getblockhash(int32_t height);*/
 +
 +uint64_t komodo_seed(int32_t height)
 +{
 +    uint64_t seed = 0;
 +    /*if ( 0 ) // problem during init time, seeds are needed for loading blockindex, so null seeds...
 +     {
 +     uint256 hash,zero; CBlockIndex *pindex;
 +     memset(&hash,0,sizeof(hash));
 +     memset(&zero,0,sizeof(zero));
 +     if ( height > 10 )
 +     height -= 10;
 +     if ( ASSETCHAINS_SYMBOL[0] == 0 )
 +     hash = _komodo_getblockhash(height);
 +     if ( memcmp(&hash,&zero,sizeof(hash)) == 0 )
 +     hash = komodo_getblockhash(height);
 +     int32_t i;
 +     for (i=0; i<32; i++)
 +     printf("%02x",((uint8_t *)&hash)[i]);
 +     printf(" seed.%d\n",height);
 +     seed = arith_uint256(hash.GetHex()).GetLow64();
 +     }
 +     else*/
 +    {
 +        seed = (height << 13) ^ (height << 2);
 +        seed <<= 21;
 +        seed |= (height & 0xffffffff);
 +        seed ^= (seed << 17) ^ (seed << 1);
 +    }
 +    return(seed);
 +}
 +
 +uint32_t komodo_txtime(uint64_t *valuep,uint256 hash, int32_t n, char *destaddr)
 +{
 +    CTxDestination address; CTransaction tx; uint256 hashBlock;
 +    *valuep = 0;
 +    if (!GetTransaction(hash, tx,
 +#ifndef KOMODO_ZCASH
 +                        Params().GetConsensus(),
 +#endif
 +                        hashBlock, true))
 +    {
 +        //fprintf(stderr,"ERROR: %s/v%d locktime.%u\n",hash.ToString().c_str(),n,(uint32_t)tx.nLockTime);
 +        return(0);
 +    }
 +    //fprintf(stderr,"%s/v%d locktime.%u\n",hash.ToString().c_str(),n,(uint32_t)tx.nLockTime);
 +    if ( n < tx.vout.size() )
 +    {
 +        *valuep = tx.vout[n].nValue;
 +        if (ExtractDestination(tx.vout[n].scriptPubKey, address))
 +            strcpy(destaddr,CBitcoinAddress(address).ToString().c_str());
 +    }
 +    return(tx.nLockTime);
 +}
 +
 +uint32_t komodo_txtime2(uint64_t *valuep,uint256 hash,int32_t n,char *destaddr)
 +{
 +    CTxDestination address; CBlockIndex *pindex; CTransaction tx; uint256 hashBlock; uint32_t txtime = 0;
 +    *valuep = 0;
 +    if (!GetTransaction(hash, tx,
 +#ifndef KOMODO_ZCASH
 +                        Params().GetConsensus(),
 +#endif
 +                        hashBlock, true))
 +    {
 +        //fprintf(stderr,"ERROR: %s/v%d locktime.%u\n",hash.ToString().c_str(),n,(uint32_t)tx.nLockTime);
 +        return(0);
 +    }
 +    if ( (pindex= mapBlockIndex[hashBlock]) != 0 )
 +        txtime = pindex->nTime;
 +    else txtime = tx.nLockTime;
 +    //fprintf(stderr,"%s/v%d locktime.%u\n",hash.ToString().c_str(),n,(uint32_t)tx.nLockTime);
 +    if ( n < tx.vout.size() )
 +    {
 +        *valuep = tx.vout[n].nValue;
 +        if (ExtractDestination(tx.vout[n].scriptPubKey, address))
 +            strcpy(destaddr,CBitcoinAddress(address).ToString().c_str());
 +    }
 +    return(txtime);
 +}
 +
 +int32_t komodo_isPoS(CBlock *pblock)
 +{
 +    int32_t n,vout; uint32_t txtime; uint64_t value; char voutaddr[64],destaddr[64]; CTxDestination voutaddress; uint256 txid;
 +    if ( ASSETCHAINS_STAKED != 0 )
 +    {
 +        if ( (n= pblock->vtx.size()) > 1 && pblock->vtx[n-1].vin.size() == 1 && pblock->vtx[n-1].vout.size() == 1 )
 +        {
 +            txid = pblock->vtx[n-1].vin[0].prevout.hash;
 +            vout = pblock->vtx[n-1].vin[0].prevout.n;
 +            txtime = komodo_txtime(&value,txid,vout,destaddr);
 +            if ( ExtractDestination(pblock->vtx[n-1].vout[0].scriptPubKey,voutaddress) )
 +            {
 +                strcpy(voutaddr,CBitcoinAddress(voutaddress).ToString().c_str());
 +                if ( strcmp(destaddr,voutaddr) == 0 && pblock->vtx[n-1].vout[0].nValue == value )
 +                {
 +                    //fprintf(stderr,"is PoS block!\n");
 +                    return(1);
 +                }
 +            }
 +        }
 +    }
 +    return(0);
 +}
 +
 +void komodo_disconnect(CBlockIndex *pindex,CBlock& block)
 +{
 +    char symbol[KOMODO_ASSETCHAIN_MAXLEN],dest[KOMODO_ASSETCHAIN_MAXLEN]; struct komodo_state *sp;
 +    //fprintf(stderr,"disconnect ht.%d\n",pindex->nHeight);
 +    komodo_init(pindex->nHeight);
 +    if ( (sp= komodo_stateptr(symbol,dest)) != 0 )
 +    {
 +        //sp->rewinding = pindex->nHeight;
 +        //fprintf(stderr,"-%d ",pindex->nHeight);
 +    } else printf("komodo_disconnect: ht.%d cant get komodo_state.(%s)\n",pindex->nHeight,ASSETCHAINS_SYMBOL);
 +}
 +
 +int32_t komodo_is_notarytx(const CTransaction& tx)
 +{
 +    uint8_t *ptr; static uint8_t crypto777[33];
 +    if ( tx.vout.size() > 0 )
 +    {
 +        ptr = (uint8_t *)&tx.vout[0].scriptPubKey[0];
 +        if ( ptr != 0 )
 +        {
 +            if ( crypto777[0] == 0 )
 +                decode_hex(crypto777,33,(char *)CRYPTO777_PUBSECPSTR);
 +            if ( memcmp(ptr+1,crypto777,33) == 0 )
 +            {
 +                //printf("found notarytx\n");
 +                return(1);
 +            }
 +        }
 +    }
 +    return(0);
 +}
 +
 +int32_t komodo_block2height(CBlock *block)
 +{
 +    static uint32_t match,mismatch;
 +    int32_t i,n,height2=-1,height = 0; uint8_t *ptr; CBlockIndex *pindex;
 +    if ( (pindex= mapBlockIndex[block->GetHash()]) != 0 )
 +    {
 +        height2 = (int32_t)pindex->nHeight;
 +        if ( height2 >= 0 )
 +            return(height2);
 +    }
 +    if ( block != 0 && block->vtx[0].vin.size() > 0 )
 +    {
 +        ptr = (uint8_t *)&block->vtx[0].vin[0].scriptSig[0];
 +        if ( ptr != 0 && block->vtx[0].vin[0].scriptSig.size() > 5 )
 +        {
 +            //for (i=0; i<6; i++)
 +            //    printf("%02x",ptr[i]);
 +            n = ptr[0];
 +            for (i=0; i<n; i++) // looks strange but this works
 +            {
 +                //03bb81000101(bb 187) (81 48001) (00 12288256)  <- coinbase.6 ht.12288256
 +                height += ((uint32_t)ptr[i+1] << (i*8));
 +                //printf("(%02x %x %d) ",ptr[i+1],((uint32_t)ptr[i+1] << (i*8)),height);
 +            }
 +            //printf(" <- coinbase.%d ht.%d\n",(int32_t)block->vtx[0].vin[0].scriptSig.size(),height);
 +        }
 +        //komodo_init(height);
 +    }
 +    if ( height != height2 )
 +    {
 +        //fprintf(stderr,"block2height height.%d vs height2.%d, match.%d mismatch.%d\n",height,height2,match,mismatch);
 +        mismatch++;
 +        if ( height2 >= 0 )
 +            height = height2;
 +    } else match++;
 +    return(height);
 +}
 +
 +int32_t komodo_block2pubkey33(uint8_t *pubkey33,CBlock *block)
 +{
 +    int32_t n;
 +    if ( KOMODO_LOADINGBLOCKS == 0 )
 +        memset(pubkey33,0xff,33);
 +    else memset(pubkey33,0,33);
 +    if ( block->vtx[0].vout.size() > 0 )
 +    {
 +        txnouttype whichType;
 +        vector<vector<unsigned char>> vch = vector<vector<unsigned char>>();
 +        if (Solver(block->vtx[0].vout[0].scriptPubKey, whichType, vch) && whichType == TX_PUBKEY)
 +        {
 +            CPubKey pubKey(vch[0]);
 +            if (pubKey.IsValid())
 +            {
 +                memcpy(pubkey33,vch[0].data(),33);
 +                return true;
 +            }
 +            else memset(pubkey33,0,33);
 +        }
 +        else memset(pubkey33,0,33);
 +    }
 +    return(0);
 +}
 +
 +int32_t komodo_blockload(CBlock& block,CBlockIndex *pindex)
 +{
 +    block.SetNull();
 +    // Open history file to read
 +    CAutoFile filein(OpenBlockFile(pindex->GetBlockPos(),true),SER_DISK,CLIENT_VERSION);
 +    if (filein.IsNull())
 +        return(-1);
 +    // Read block
 +    try { filein >> block; }
 +    catch (const std::exception& e)
 +    {
 +        fprintf(stderr,"readblockfromdisk err B\n");
 +        return(-1);
 +    }
 +    return(0);
 +}
 +
 +uint32_t komodo_chainactive_timestamp()
 +{
 +    if ( chainActive.LastTip() != 0 )
 +        return((uint32_t)chainActive.LastTip()->GetBlockTime());
 +    else return(0);
 +}
 +
 +CBlockIndex *komodo_chainactive(int32_t height)
 +{
 +    if ( chainActive.LastTip() != 0 )
 +    {
 +        if ( height <= chainActive.LastTip()->nHeight )
 +            return(chainActive[height]);
 +        // else fprintf(stderr,"komodo_chainactive height %d > active.%d\n",height,chainActive.LastTip()->nHeight);
 +    }
 +    //fprintf(stderr,"komodo_chainactive null chainActive.LastTip() height %d\n",height);
 +    return(0);
 +}
 +
 +uint32_t komodo_heightstamp(int32_t height)
 +{
 +    CBlockIndex *ptr;
 +    if ( height > 0 && (ptr= komodo_chainactive(height)) != 0 )
 +        return(ptr->nTime);
 +    //else fprintf(stderr,"komodo_heightstamp null ptr for block.%d\n",height);
 +    return(0);
 +}
 +
 +/*void komodo_pindex_init(CBlockIndex *pindex,int32_t height) gets data corrupted
 +{
 +    int32_t i,num; uint8_t pubkeys[64][33]; CBlock block;
 +    if ( pindex->didinit != 0 )
 +        return;
 +    //printf("pindex.%d komodo_pindex_init notary.%d from height.%d\n",pindex->nHeight,pindex->notaryid,height);
 +    if ( pindex->didinit == 0 )
 +    {
 +        pindex->notaryid = -1;
 +        if ( KOMODO_LOADINGBLOCKS == 0 )
 +            memset(pindex->pubkey33,0xff,33);
 +        else memset(pindex->pubkey33,0,33);
 +        if ( komodo_blockload(block,pindex) == 0 )
 +        {
 +            komodo_block2pubkey33(pindex->pubkey33,&block);
 +            //for (i=0; i<33; i++)
 +            //    fprintf(stderr,"%02x",pindex->pubkey33[i]);
 +            //fprintf(stderr," set pubkey at height %d/%d\n",pindex->nHeight,height);
 +            //if ( pindex->pubkey33[0] == 2 || pindex->pubkey33[0] == 3 )
 +            //    pindex->didinit = (KOMODO_LOADINGBLOCKS == 0);
 +        } // else fprintf(stderr,"error loading block at %d/%d",pindex->nHeight,height);
 +    }
 +    if ( pindex->didinit != 0 && pindex->nHeight >= 0 && (num= komodo_notaries(pubkeys,(int32_t)pindex->nHeight,(uint32_t)pindex->nTime)) > 0 )
 +    {
 +        for (i=0; i<num; i++)
 +        {
 +            if ( memcmp(pubkeys[i],pindex->pubkey33,33) == 0 )
 +            {
 +                pindex->notaryid = i;
 +                break;
 +            }
 +        }
 +        if ( 0 && i == num )
 +        {
 +            for (i=0; i<33; i++)
 +                fprintf(stderr,"%02x",pindex->pubkey33[i]);
 +            fprintf(stderr," unmatched pubkey at height %d/%d\n",pindex->nHeight,height);
 +        }
 +    }
 +}*/
 +
 +void komodo_index2pubkey33(uint8_t *pubkey33,CBlockIndex *pindex,int32_t height)
 +{
 +    int32_t num,i; CBlock block;
 +    memset(pubkey33,0,33);
 +    if ( pindex != 0 )
 +    {
 +        if ( komodo_blockload(block,pindex) == 0 )
 +            komodo_block2pubkey33(pubkey33,&block);
 +    }
 +}
 +
 +/*int8_t komodo_minerid(int32_t height,uint8_t *destpubkey33)
 +{
 +    int32_t num,i,numnotaries; CBlockIndex *pindex; uint32_t timestamp=0; uint8_t pubkey33[33],pubkeys[64][33];
 +    if ( (pindex= chainActive[height]) != 0 )
 +    {
 +        if ( pindex->didinit != 0 )
 +        {
 +            if ( destpubkey33 != 0 )
 +                memcpy(destpubkey33,pindex->pubkey33,33);
 +            return(pindex->notaryid);
 +        }
 +        komodo_index2pubkey33(pubkey33,pindex,height);
 +        if ( destpubkey33 != 0 )
 +            memcpy(destpubkey33,pindex->pubkey33,33);
 +        if ( pindex->didinit != 0 )
 +            return(pindex->notaryid);
 +        timestamp = pindex->GetBlockTime();
 +        if ( (num= komodo_notaries(pubkeys,height,timestamp)) > 0 )
 +        {
 +            for (i=0; i<num; i++)
 +                if ( memcmp(pubkeys[i],pubkey33,33) == 0 )
 +                    return(i);
 +        }
 +    }
 +    fprintf(stderr,"komodo_minerid height.%d null pindex\n",height);
 +    return(komodo_electednotary(&numnotaries,pubkey33,height,timestamp));
 +}*/
 +
 +int32_t komodo_eligiblenotary(uint8_t pubkeys[66][33],int32_t *mids,uint32_t blocktimes[66],int32_t *nonzpkeysp,int32_t height)
 +{
 +    int32_t i,j,n,duplicate; CBlock block; CBlockIndex *pindex; uint8_t notarypubs33[64][33];
 +    memset(mids,-1,sizeof(*mids)*66);
 +    n = komodo_notaries(notarypubs33,height,0);
 +    for (i=duplicate=0; i<66; i++)
 +    {
 +        if ( (pindex= komodo_chainactive(height-i)) != 0 )
 +        {
 +            blocktimes[i] = pindex->nTime;
 +            if ( komodo_blockload(block,pindex) == 0 )
 +            {
 +                komodo_block2pubkey33(pubkeys[i],&block);
 +                for (j=0; j<n; j++)
 +                {
 +                    if ( memcmp(notarypubs33[j],pubkeys[i],33) == 0 )
 +                    {
 +                        mids[i] = j;
 +                        (*nonzpkeysp)++;
 +                        break;
 +                    }
 +                }
 +            } else fprintf(stderr,"couldnt load block.%d\n",height);
 +            if ( mids[0] >= 0 && i > 0 && mids[i] == mids[0] )
 +                duplicate++;
 +        }
 +    }
 +    if ( i == 66 && duplicate == 0 && (height > 186233 || *nonzpkeysp > 0) )
 +        return(1);
 +    else return(0);
 +}
 +
 +int32_t komodo_minerids(uint8_t *minerids,int32_t height,int32_t width)
 +{
 +    int32_t i,j,n,nonz,numnotaries; CBlock block; CBlockIndex *pindex; uint8_t notarypubs33[64][33],pubkey33[33];
 +    numnotaries = komodo_notaries(notarypubs33,height,0);
 +    for (i=nonz=0; i<width; i++,n++)
 +    {
 +        if ( height-i <= 0 )
 +            continue;
 +        if ( (pindex= komodo_chainactive(height-width+i+1)) != 0 )
 +        {
 +            if ( komodo_blockload(block,pindex) == 0 )
 +            {
 +                komodo_block2pubkey33(pubkey33,&block);
 +                for (j=0; j<numnotaries; j++)
 +                {
 +                    if ( memcmp(notarypubs33[j],pubkey33,33) == 0 )
 +                    {
 +                        minerids[nonz++] = j;
 +                        break;
 +                    }
 +                }
 +                if ( j == numnotaries )
 +                    minerids[nonz++] = j;
 +            } else fprintf(stderr,"couldnt load block.%d\n",height);
 +        }
 +    }
 +    return(nonz);
 +}
 +
 +int32_t komodo_is_special(uint8_t pubkeys[66][33],int32_t mids[66],uint32_t blocktimes[66],int32_t height,uint8_t pubkey33[33],uint32_t blocktime)
 +{
 +    int32_t i,j,notaryid=0,minerid,limit,nid; uint8_t destpubkey33[33];
 +    komodo_chosennotary(&notaryid,height,pubkey33,blocktimes[0]);
 +    if ( height >= 82000 )
 +    {
 +        if ( notaryid >= 0 )
 +        {
 +            for (i=1; i<66; i++)
 +            {
 +                if ( mids[i] == notaryid )
 +                {
 +                    if ( height > 792000 )
 +                    {
 +                        for (j=0; j<66; j++)
 +                            fprintf(stderr,"%d ",mids[j]);
 +                        fprintf(stderr,"ht.%d repeat notaryid.%d in mids[%d]\n",height,notaryid,i);
 +                        return(-1);
 +                    } else break;
 +                }
 +            }
 +            if ( blocktime != 0 && blocktimes[1] != 0 && blocktime < blocktimes[1]+57 )
 +            {
 +                if ( height > 807000 )
 +                    return(-2);
 +            }
 +            return(1);
 +        } else return(0);
 +    }
 +    else
 +    {
 +        if ( height >= 34000 && notaryid >= 0 )
 +        {
 +            if ( height < 79693 )
 +                limit = 64;
 +            else if ( height < 82000 )
 +                limit = 8;
 +            else limit = 66;
 +            for (i=1; i<limit; i++)
 +            {
 +                komodo_chosennotary(&nid,height-i,pubkey33,blocktimes[i]);
 +                if ( nid == notaryid )
 +                {
 +                    //for (j=0; j<66; j++)
 +                    //    fprintf(stderr,"%d ",mids[j]);
 +                    //fprintf(stderr,"ht.%d repeat mids[%d] nid.%d notaryid.%d\n",height-i,i,nid,notaryid);
 +                    if ( height > 225000 )
 +                        return(-1);
 +                }
 +            }
 +            //fprintf(stderr,"special notaryid.%d ht.%d limit.%d\n",notaryid,height,limit);
 +            return(1);
 +        }
 +    }
 +    return(0);
 +}
 +
 +int32_t komodo_MoM(int32_t *notarized_heightp,uint256 *MoMp,uint256 *kmdtxidp,int32_t nHeight,uint256 *MoMoMp,int32_t *MoMoMoffsetp,int32_t *MoMoMdepthp,int32_t *kmdstartip,int32_t *kmdendip)
 +{
 +    int32_t depth,notarized_ht; uint256 MoM,kmdtxid;
 +    depth = komodo_MoMdata(&notarized_ht,&MoM,&kmdtxid,nHeight,MoMoMp,MoMoMoffsetp,MoMoMdepthp,kmdstartip,kmdendip);
 +    memset(MoMp,0,sizeof(*MoMp));
 +    memset(kmdtxidp,0,sizeof(*kmdtxidp));
 +    *notarized_heightp = 0;
 +    if ( depth != 0 && notarized_ht > 0 && nHeight > notarized_ht-depth && nHeight <= notarized_ht )
 +    {
 +        *MoMp = MoM;
 +        *notarized_heightp = notarized_ht;
 +        *kmdtxidp = kmdtxid;
 +    }
 +    return(depth);
 +}
 +
 +int32_t komodo_checkpoint(int32_t *notarized_heightp,int32_t nHeight,uint256 hash)
 +{
 +    int32_t notarized_height,MoMdepth; uint256 MoM,notarized_hash,notarized_desttxid; CBlockIndex *notary,*pindex;
 +    if ( (pindex= chainActive.LastTip()) == 0 )
 +        return(-1);
 +    notarized_height = komodo_notarizeddata(pindex->nHeight,&notarized_hash,&notarized_desttxid);
 +    *notarized_heightp = notarized_height;
 +    if ( notarized_height >= 0 && notarized_height <= pindex->nHeight && (notary= mapBlockIndex[notarized_hash]) != 0 )
 +    {
 +        //printf("nHeight.%d -> (%d %s)\n",pindex->Tip()->nHeight,notarized_height,notarized_hash.ToString().c_str());
 +        if ( notary->nHeight == notarized_height ) // if notarized_hash not in chain, reorg
 +        {
 +            if ( nHeight < notarized_height )
 +            {
 +                //fprintf(stderr,"[%s] nHeight.%d < NOTARIZED_HEIGHT.%d\n",ASSETCHAINS_SYMBOL,nHeight,notarized_height);
 +                return(-1);
 +            }
 +            else if ( nHeight == notarized_height && memcmp(&hash,&notarized_hash,sizeof(hash)) != 0 )
 +            {
 +                fprintf(stderr,"[%s] nHeight.%d == NOTARIZED_HEIGHT.%d, diff hash\n",ASSETCHAINS_SYMBOL,nHeight,notarized_height);
 +                return(-1);
 +            }
 +        } //else fprintf(stderr,"[%s] unexpected error notary_hash %s ht.%d at ht.%d\n",ASSETCHAINS_SYMBOL,notarized_hash.ToString().c_str(),notarized_height,notary->nHeight);
 +    }
 +    //else if ( notarized_height > 0 && notarized_height != 73880 && notarized_height >= 170000 )
 +    //    fprintf(stderr,"[%s] couldnt find notarized.(%s %d) ht.%d\n",ASSETCHAINS_SYMBOL,notarized_hash.ToString().c_str(),notarized_height,pindex->nHeight);
 +    return(0);
 +}
 +
 +uint32_t komodo_interest_args(uint32_t *txheighttimep,int32_t *txheightp,uint32_t *tiptimep,uint64_t *valuep,uint256 hash,int32_t n)
 +{
 +    LOCK(cs_main);
 +    CTransaction tx; uint256 hashBlock; CBlockIndex *pindex,*tipindex;
 +    *txheighttimep = *txheightp = *tiptimep = 0;
 +    *valuep = 0;
 +    if ( !GetTransaction(hash,tx,hashBlock,true) )
 +        return(0);
 +    uint32_t locktime = 0;
 +    if ( n < tx.vout.size() )
 +    {
 +        if ( (pindex= mapBlockIndex[hashBlock]) != 0 )
 +        {
 +            *valuep = tx.vout[n].nValue;
 +            *txheightp = pindex->nHeight;
 +            *txheighttimep = pindex->nTime;
 +            if ( *tiptimep == 0 && (tipindex= chainActive.LastTip()) != 0 )
 +                *tiptimep = (uint32_t)tipindex->nTime;
 +            locktime = tx.nLockTime;
 +            //fprintf(stderr,"tx locktime.%u %.8f height.%d | tiptime.%u\n",locktime,(double)*valuep/COIN,*txheightp,*tiptimep);
 +        }
 +    }
 +    return(locktime);
 +}
 +
 +uint64_t komodo_interest(int32_t txheight,uint64_t nValue,uint32_t nLockTime,uint32_t tiptime);
 +
 +uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 hash,int32_t n,int32_t checkheight,uint64_t checkvalue,int32_t tipheight)
 +{
 +    uint64_t value; uint32_t tiptime=0,txheighttimep; CBlockIndex *pindex;
 +    if ( (pindex= chainActive[tipheight]) != 0 )
 +        tiptime = (uint32_t)pindex->nTime;
 +    else fprintf(stderr,"cant find height[%d]\n",tipheight);
 +    if ( (*locktimep= komodo_interest_args(&txheighttimep,txheightp,&tiptime,&value,hash,n)) != 0 )
 +    {
 +        if ( (checkvalue == 0 || value == checkvalue) && (checkheight == 0 || *txheightp == checkheight) )
 +            return(komodo_interest(*txheightp,value,*locktimep,tiptime));
 +        //fprintf(stderr,"nValue %llu lock.%u:%u nTime.%u -> %llu\n",(long long)coins.vout[n].nValue,coins.nLockTime,timestamp,pindex->nTime,(long long)interest);
 +        else fprintf(stderr,"komodo_accrued_interest value mismatch %llu vs %llu or height mismatch %d vs %d\n",(long long)value,(long long)checkvalue,*txheightp,checkheight);
 +    }
 +    return(0);
 +}
 +
 +int32_t komodo_isrealtime(int32_t *kmdheightp)
 +{
 +    struct komodo_state *sp; CBlockIndex *pindex;
 +    if ( (sp= komodo_stateptrget((char *)"KMD")) != 0 )
 +        *kmdheightp = sp->CURRENT_HEIGHT;
 +    else *kmdheightp = 0;
 +    if ( (pindex= chainActive.LastTip()) != 0 && pindex->nHeight >= (int32_t)komodo_longestchain() )
 +        return(1);
 +    else return(0);
 +}
 +
 +int32_t komodo_validate_interest(const CTransaction &tx,int32_t txheight,uint32_t cmptime,int32_t dispflag)
 +{
 +    if ( KOMODO_REWIND == 0 && ASSETCHAINS_SYMBOL[0] == 0 && (int64_t)tx.nLockTime >= LOCKTIME_THRESHOLD ) //1473793441 )
 +    {
 +        if ( txheight > 246748 )
 +        {
 +            if ( txheight < 247205 )
 +                cmptime -= 16000;
 +            if ( (int64_t)tx.nLockTime < cmptime-KOMODO_MAXMEMPOOLTIME )
 +            {
 +                if ( tx.nLockTime != 1477258935 && dispflag != 0 )
 +                {
 +                    fprintf(stderr,"komodo_validate_interest.%d reject.%d [%d] locktime %u cmp2.%u\n",dispflag,txheight,(int32_t)(tx.nLockTime - (cmptime-KOMODO_MAXMEMPOOLTIME)),(uint32_t)tx.nLockTime,cmptime);
 +                }
 +                return(-1);
 +            }
 +            if ( 0 && dispflag != 0 )
 +                fprintf(stderr,"validateinterest.%d accept.%d [%d] locktime %u cmp2.%u\n",dispflag,(int32_t)txheight,(int32_t)(tx.nLockTime - (cmptime-KOMODO_MAXMEMPOOLTIME)),(int32_t)tx.nLockTime,cmptime);
 +        }
 +    }
 +    return(0);
 +}
 +
 +/*
 + komodo_checkPOW (fast) is called early in the process and should only refer to data immediately available. it is a filter to prevent bad blocks from going into the local DB. The more blocks we can filter out at this stage, the less junk in the local DB that will just get purged later on.
 + 
 + komodo_checkPOW (slow) is called right before connecting blocks so all prior blocks can be assumed to be there and all checks must pass
 + 
 + commission must be in coinbase.vout[1] and must be >= 10000 sats
 + PoS stake must be without txfee and in the last tx in the block at vout[0]
 + */
 +
 +uint64_t komodo_commission(const CBlock *pblock)
 +{
 +    int32_t i,j,n=0,txn_count; uint64_t commission,total = 0;
 +    txn_count = pblock->vtx.size();
 +    for (i=0; i<txn_count; i++)
 +    {
 +        n = pblock->vtx[i].vout.size();
 +        for (j=0; j<n; j++)
 +        {
 +            //fprintf(stderr,"(%d %.8f).%d ",i,dstr(block.vtx[i].vout[j].nValue),j);
 +            if ( i != 0 || j != 1 )
 +                total += pblock->vtx[i].vout[j].nValue;
 +        }
 +    }
 +    //fprintf(stderr,"txn.%d n.%d commission total %.8f -> %.8f\n",txn_count,n,dstr(total),dstr((total * ASSETCHAINS_COMMISSION) / COIN));
 +    commission = ((total * ASSETCHAINS_COMMISSION) / COIN);
 +    if ( commission < 10000 )
 +        commission = 0;
 +    return(commission);
 +}
 +
 +uint32_t komodo_segid32(char *coinaddr)
 +{
 +    bits256 addrhash;
 +    vcalc_sha256(0,(uint8_t *)&addrhash,(uint8_t *)coinaddr,(int32_t)strlen(coinaddr));
 +    return(addrhash.uints[0]);
 +}
 +
 +int8_t komodo_segid(int32_t nocache,int32_t height)
 +{
 +    CTxDestination voutaddress; CBlock block; CBlockIndex *pindex; uint64_t value; uint32_t txtime; char voutaddr[64],destaddr[64]; int32_t txn_count,vout; uint256 txid; int8_t segid = -1;
 +    if ( height > 0 && (pindex= komodo_chainactive(height)) != 0 )
 +    {
 +        if ( nocache == 0 && pindex->segid >= -1 )
 +            return(pindex->segid);
 +        if ( komodo_blockload(block,pindex) == 0 )
 +        {
 +            txn_count = block.vtx.size();
 +            if ( txn_count > 1 && block.vtx[txn_count-1].vin.size() == 1 && block.vtx[txn_count-1].vout.size() == 1 )
 +            {
 +                txid = block.vtx[txn_count-1].vin[0].prevout.hash;
 +                vout = block.vtx[txn_count-1].vin[0].prevout.n;
 +                txtime = komodo_txtime(&value,txid,vout,destaddr);
 +                if ( ExtractDestination(block.vtx[txn_count-1].vout[0].scriptPubKey,voutaddress) )
 +                {
 +                    strcpy(voutaddr,CBitcoinAddress(voutaddress).ToString().c_str());
 +                    if ( strcmp(destaddr,voutaddr) == 0 && block.vtx[txn_count-1].vout[0].nValue == value )
 +                    {
 +                        segid = komodo_segid32(voutaddr) & 0x3f;
 +                        //fprintf(stderr,"komodo_segid.(%d) -> %02x\n",height,segid);
 +                    }
 +                } else fprintf(stderr,"komodo_segid ht.%d couldnt extract voutaddress\n",height);
 +            }
 +        }
 +    }
 +    return(segid);
 +}
 +
 +int32_t komodo_segids(uint8_t *hashbuf,int32_t height,int32_t n)
 +{
 +    static uint8_t prevhashbuf[100]; static int32_t prevheight;
 +    int32_t i;
 +    if ( height == prevheight && n == 100 )
 +        memcpy(hashbuf,prevhashbuf,100);
 +    else
 +    {
 +        memset(hashbuf,0xff,n);
 +        for (i=0; i<n; i++)
 +        {
 +            hashbuf[i] = (uint8_t)komodo_segid(1,height+i);
 +            //fprintf(stderr,"%02x ",hashbuf[i]);
 +        }
 +        if ( n == 100 )
 +        {
 +            memcpy(prevhashbuf,hashbuf,100);
 +            prevheight = height;
 +            //fprintf(stderr,"prevsegids.%d\n",height+n);
 +        }
 +    }
 +}
 +
 +uint32_t komodo_stakehash(uint256 *hashp,char *address,uint8_t *hashbuf,uint256 txid,int32_t vout)
 +{
 +    bits256 addrhash;
 +    vcalc_sha256(0,(uint8_t *)&addrhash,(uint8_t *)address,(int32_t)strlen(address));
 +    memcpy(&hashbuf[100],&addrhash,sizeof(addrhash));
 +    memcpy(&hashbuf[100+sizeof(addrhash)],&txid,sizeof(txid));
 +    memcpy(&hashbuf[100+sizeof(addrhash)+sizeof(txid)],&vout,sizeof(vout));
 +    vcalc_sha256(0,(uint8_t *)hashp,hashbuf,100 + (int32_t)sizeof(uint256)*2 + sizeof(vout));
 +    return(addrhash.uints[0]);
 +}
 +
 +uint32_t komodo_stake(int32_t validateflag,arith_uint256 bnTarget,int32_t nHeight,uint256 txid,int32_t vout,uint32_t blocktime,uint32_t prevtime,char *destaddr)
 +{
 +    bool fNegative,fOverflow; uint8_t hashbuf[256]; char address[64]; bits256 addrhash; arith_uint256 hashval,mindiff,ratio,coinage256; uint256 hash,pasthash; int32_t diff=0,segid,minage,i,iter=0; uint32_t txtime,segid32,winner = 0 ; uint64_t value,coinage;
 +    txtime = komodo_txtime2(&value,txid,vout,address);
 +    if ( validateflag == 0 )
 +    {
 +        //fprintf(stderr,"blocktime.%u -> ",blocktime);
 +        if ( blocktime < prevtime+3 )
 +            blocktime = prevtime+3;
 +        if ( blocktime < GetAdjustedTime()-60 )
 +            blocktime = GetAdjustedTime()+30;
 +        //fprintf(stderr,"blocktime.%u txtime.%u\n",blocktime,txtime);
 +    }
 +    if ( value == 0 || txtime == 0 || blocktime == 0 || prevtime == 0 )
 +    {
 +        //fprintf(stderr,"komodo_stake null %.8f %u %u %u\n",dstr(value),txtime,blocktime,prevtime);
 +        return(0);
 +    }
 +    if ( value < SATOSHIDEN )
 +        return(0);
 +    value /= SATOSHIDEN;
 +    mindiff.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow);
 +    ratio = (mindiff / bnTarget);
 +    if ( (minage= nHeight*3) > 6000 ) // about 100 blocks
 +        minage = 6000;
 +    komodo_segids(hashbuf,nHeight-101,100);
 +    segid32 = komodo_stakehash(&hash,address,hashbuf,txid,vout);
 +    segid = ((nHeight + segid32) & 0x3f);
 +    for (iter=0; iter<600; iter++)
 +    {
 +        if ( blocktime+iter+segid*2 < txtime+minage )
 +            continue;
 +        diff = (iter + blocktime - txtime - minage);
 +        if ( diff < 0 )
 +            diff = 60;
 +        else if ( diff > 3600*24*30 )
 +        {
 +            //printf("diff.%d (iter.%d blocktime.%u txtime.%u minage.%d)\n",(int32_t)diff,iter,blocktime,txtime,(int32_t)minage);
 +            diff = 3600*24*30;
 +        }
 +        if ( iter > 0 )
 +            diff += segid*2;
 +        coinage = (value * diff);
 +        if ( blocktime+iter+segid*2 > prevtime+480 )
 +            coinage *= ((blocktime+iter+segid*2) - (prevtime+400));
 +        coinage256 = arith_uint256(coinage+1);
 +        hashval = ratio * (UintToArith256(hash) / coinage256);
 +        if ( hashval <= bnTarget )
 +        {
 +            winner = 1;
 +            if ( validateflag == 0 )
 +            {
 +                //fprintf(stderr,"winner blocktime.%u iter.%d segid.%d\n",blocktime,iter,segid);
 +                blocktime += iter;
 +                blocktime += segid * 2;
 +            }
 +            break;
 +        }
 +        if ( validateflag != 0 )
 +        {
 +            /*for (i=31; i>=24; i--)
 +                fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]);
 +            fprintf(stderr," vs ");
 +            for (i=31; i>=24; i--)
 +                fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]);
 +            fprintf(stderr," segid.%d iter.%d winner.%d coinage.%llu %d ht.%d t.%u v%d diff.%d\n",segid,iter,winner,(long long)coinage,(int32_t)(blocktime - txtime),nHeight,blocktime,(int32_t)value,(int32_t)diff);*/
 +            break;
 +        }
 +    }
 +    //fprintf(stderr,"iterated until i.%d winner.%d\n",i,winner);
 +    if ( 0 && validateflag != 0 )
 +    {
 +        for (i=31; i>=24; i--)
 +            fprintf(stderr,"%02x",((uint8_t *)&hashval)[i]);
 +        fprintf(stderr," vs ");
 +        for (i=31; i>=24; i--)
 +            fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]);
 +        fprintf(stderr," segid.%d iter.%d winner.%d coinage.%llu %d ht.%d t.%u v%d diff.%d ht.%d\n",segid,iter,winner,(long long)coinage,(int32_t)(blocktime - txtime),nHeight,blocktime,(int32_t)value,(int32_t)diff,nHeight);
 +    }
 +    if ( nHeight < 10 )
 +        return(blocktime);
 +    return(blocktime * winner);
 +}
 +
 +arith_uint256 komodo_PoWtarget(int32_t *percPoSp,arith_uint256 target,int32_t height,int32_t goalperc)
 +{
 +    int32_t oldflag = 0;
 +    CBlockIndex *pindex; arith_uint256 easydiff,bnTarget,hashval,sum,ave; bool fNegative,fOverflow; int32_t i,n,m,ht,percPoS,diff,val;
 +    *percPoSp = percPoS = 0;
 +    if ( height <= 10 || (ASSETCHAINS_STAKED == 100 && height <= 100) )
 +        return(target);
 +    sum = arith_uint256(0);
 +    ave = sum;
 +    easydiff.SetCompact(KOMODO_MINDIFF_NBITS,&fNegative,&fOverflow);
 +    for (i=n=m=0; i<100; i++)
 +    {
 +        ht = height - 100 + i;
 +        if ( ht <= 1 )
 +            continue;
 +        if ( (pindex= komodo_chainactive(ht)) != 0 )
 +        {
 +            if ( komodo_segid(0,ht) >= 0 )
 +            {
 +                n++;
 +                percPoS++;
 +                if ( ASSETCHAINS_STAKED < 100 )
 +                    fprintf(stderr,"0");
 +            }
 +            else
 +            {
 +                if ( ASSETCHAINS_STAKED < 100 )
 +                    fprintf(stderr,"1");
 +                sum += UintToArith256(pindex->GetBlockHash());
 +                m++;
 +            }
 +        }
 +        if ( ASSETCHAINS_STAKED < 100 && (i % 10) == 9 )
 +            fprintf(stderr," %d, ",percPoS);
 +    }
 +    if ( m+n < 100 )
 +        percPoS = ((percPoS * n) + (goalperc * (100-n))) / 100;
 +    if ( ASSETCHAINS_STAKED < 100 )
 +        fprintf(stderr," -> %d%% percPoS vs goalperc.%d ht.%d\n",percPoS,goalperc,height);
 +    *percPoSp = percPoS;
 +    if ( m > 0 )
 +    {
 +        ave = (sum / arith_uint256(m));
 +        if ( ave > target )
 +            ave = target;
 +    } else ave = target; //easydiff; //else return(target);
 +    if ( percPoS == 0 )
 +        percPoS = 1;
 +    if ( percPoS < goalperc ) // increase PoW diff -> lower bnTarget
 +    {
 +        //if ( oldflag != 0 )
 +        //    bnTarget = (ave * arith_uint256(percPoS * percPoS)) / arith_uint256(goalperc * goalperc * goalperc);
 +        if ( oldflag != 0 )
 +            bnTarget = (ave / arith_uint256(goalperc * goalperc * goalperc)) * arith_uint256(percPoS * percPoS);
 +        else bnTarget = (ave / arith_uint256(goalperc * goalperc * goalperc * goalperc)) * arith_uint256(percPoS * percPoS);
 +        if ( ASSETCHAINS_STAKED < 100 )
 +        {
 +            for (i=31; i>=24; i--)
 +                fprintf(stderr,"%02x",((uint8_t *)&ave)[i]);
 +            fprintf(stderr," increase diff -> ");
 +            for (i=31; i>=24; i--)
 +                fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]);
 +            fprintf(stderr," floor diff ");
 +            for (i=31; i>=24; i--)
 +                fprintf(stderr,"%02x",((uint8_t *)&target)[i]);
 +            fprintf(stderr," ht.%d percPoS.%d vs goal.%d -> diff %d\n",height,percPoS,goalperc,goalperc - percPoS);
 +        }
 +    }
 +    else if ( percPoS > goalperc ) // decrease PoW diff -> raise bnTarget
 +    {
 +        if ( oldflag != 0 )
 +        {
 +            bnTarget = ((ave * arith_uint256(goalperc)) + (easydiff * arith_uint256(percPoS))) / arith_uint256(percPoS + goalperc);
 +            //bnTarget = (bnTarget * arith_uint256(percPoS * percPoS * percPoS)) / arith_uint256(goalperc * goalperc);
 +            bnTarget = (bnTarget / arith_uint256(goalperc * goalperc)) * arith_uint256(percPoS * percPoS * percPoS);
 +        }
 +        else bnTarget = (ave / arith_uint256(goalperc * goalperc)) * arith_uint256(percPoS * percPoS * percPoS);
 +        if ( bnTarget > easydiff )
 +            bnTarget = easydiff;
 +        else if ( bnTarget < ave ) // overflow
 +        {
 +            bnTarget = ((ave * arith_uint256(goalperc)) + (easydiff * arith_uint256(percPoS))) / arith_uint256(percPoS + goalperc);
 +            if ( bnTarget < ave )
 +                bnTarget = ave;
 +        }
 +        if ( 1 )
 +        {
 +            for (i=31; i>=24; i--)
 +                fprintf(stderr,"%02x",((uint8_t *)&ave)[i]);
 +            fprintf(stderr," decrease diff -> ");
 +            for (i=31; i>=24; i--)
 +                fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]);
 +            fprintf(stderr," floor diff ");
 +            for (i=31; i>=24; i--)
 +                fprintf(stderr,"%02x",((uint8_t *)&target)[i]);
 +            fprintf(stderr," ht.%d percPoS.%d vs goal.%d -> diff %d\n",height,percPoS,goalperc,goalperc - percPoS);
 +        }
 +    }
 +    else bnTarget = ave; // recent ave is perfect
 +    return(bnTarget);
 +}
 +
 +int32_t komodo_is_PoSblock(int32_t slowflag,int32_t height,CBlock *pblock,arith_uint256 bnTarget,arith_uint256 bhash)
 +{
 +    CBlockIndex *previndex,*pindex; char voutaddr[64],destaddr[64]; uint256 txid; uint32_t txtime,prevtime=0; int32_t vout,PoSperc,txn_count,eligible=0,isPoS = 0,segid; uint64_t value; CTxDestination voutaddress;
 +    if ( ASSETCHAINS_STAKED == 100 && height <= 10 )
 +        return(1);
 +    pindex = mapBlockIndex[pblock->GetHash()];
 +    if ( pindex != 0 && pindex->segid >= -1 )
 +    {
 +        if ( pindex->segid == -1 )
 +            return(0);
 +        else return(1);
 +    }
 +    txn_count = pblock->vtx.size();
 +    if ( txn_count > 1 && pblock->vtx[txn_count-1].vin.size() == 1 && pblock->vtx[txn_count-1].vout.size() == 1 )
 +    {
 +        if ( prevtime == 0 )
 +        {
 +            if ( (previndex= mapBlockIndex[pblock->hashPrevBlock]) != 0 )
 +                prevtime = (uint32_t)previndex->nTime;
 +        }
 +        txid = pblock->vtx[txn_count-1].vin[0].prevout.hash;
 +        vout = pblock->vtx[txn_count-1].vin[0].prevout.n;
 +        if ( prevtime != 0 )
 +        {
 +            if ( komodo_isPoS(pblock) != 0 )
 +            {
 +                eligible = komodo_stake(1,bnTarget,height,txid,vout,pblock->nTime,prevtime+27,(char *)"");
 +            }
 +            if ( eligible == 0 || eligible > pblock->nTime )
 +            {
 +                if ( 0 && ASSETCHAINS_STAKED < 100 )
 +                    fprintf(stderr,"komodo_is_PoSblock PoS failure ht.%d eligible.%u vs blocktime.%u, lag.%d -> check to see if it is PoW block\n",height,eligible,(uint32_t)pblock->nTime,(int32_t)(eligible - pblock->nTime));
 +                if ( slowflag != 0 && pindex != 0 )
 +                {
 +                    pindex->segid = -1;
 +                    fprintf(stderr,"PoW block detected set segid.%d <- %d\n",height,pindex->segid);
 +                }
 +            }
 +            else
 +            {
 +                isPoS = 2; // 2 means staking utxo validated
 +                if ( slowflag != 0 && height > 100 )
 +                {
 +                    segid = -3;
 +                    if ( pindex != 0 && pindex->segid == -2 && (segid= komodo_segid(1,height)) >= 0 )
 +                    {
 +                        pindex->segid = segid;
 +                        fprintf(stderr,"B set segid.%d <- %d\n",height,pindex->segid);
 +                    } //else fprintf(stderr,"unexpected null pindex for slowflag set ht.%d segid.%d:%d\n",height,pindex!=0?pindex->segid:-3,segid);
 +                }
 +            }
 +        }
 +        if ( slowflag == 0 && isPoS == 0 ) // maybe previous block is not seen yet, do the best approx
 +        {
 +            if ( komodo_isPoS(pblock) != 0 )
 +                isPoS = 1;
 +        }
 +        if ( slowflag != 0 && isPoS != 0 )
 +        {
 +            if ( isPoS != 2 )
 +            {
 +                fprintf(stderr,"ht.%d isPoS.%d utxo not validated -> must be PoW fake\n",height,isPoS);
 +                isPoS = 0;
 +            }
 +            else
 +            {
 +                bnTarget = komodo_PoWtarget(&PoSperc,bnTarget,height,ASSETCHAINS_STAKED);
 +                if ( bhash < bnTarget )
 +                {
 +                    fprintf(stderr,"ht.%d isPoS but meets PoW diff!\n",height);
 +                    isPoS = 0;
 +                }
 +            }
 +        }
 +        //else return(-1);
 +    }
 +    //fprintf(stderr,"slow.%d ht.%d isPoS.%d\n",slowflag,height,isPoS);
 +    return(isPoS != 0);
 +}
 +
 +// for now, we will ignore slowFlag in the interest of keeping success/fail simpler for security purposes
 +bool verusCheckPOSBlock(int32_t slowflag, CBlock *pblock, int32_t height)
 +{
 +    CBlockIndex *pastBlockIndex;
 +    uint256 txid, blkHash;
 +    int32_t txn_count;
 +    uint32_t voutNum;
 +    CAmount value;
 +    bool isPOS = false;
 +    CTxDestination voutaddress, destaddress, cbaddress;
 +    arith_uint256 target, hash;
 +    CTransaction tx;
 +
 +    if (!pblock->IsVerusPOSBlock())
 +    {
 +        printf("%s, height %d not POS block\n", pblock->nNonce.GetHex().c_str(), height);
 +        //pblock->nNonce.SetPOSTarget(pblock->nNonce.GetPOSTarget());
 +        //printf("%s after setting POS target\n", pblock->nNonce.GetHex().c_str());
 +        return false;
 +    }
 +
 +    char voutaddr[64], destaddr[64], cbaddr[64];
 +
 +    txn_count = pblock->vtx.size();
 +
 +    if ( txn_count > 1 )
 +    {
 +        target.SetCompact(pblock->GetVerusPOSTarget());
 +        txid = pblock->vtx[txn_count-1].vin[0].prevout.hash;
 +        voutNum = pblock->vtx[txn_count-1].vin[0].prevout.n;
 +        value = pblock->vtx[txn_count-1].vout[0].nValue;
 +
 +        {
 +            bool validHash;
 +            bool enablePOSNonce = CPOSNonce::NewPOSActive(height);
 +            bool newPOSActive = enablePOSNonce || (Params().GetConsensus().vUpgrades[Consensus::UPGRADE_SAPLING].nActivationHeight <= height);
 +            uint256 rawHash;
 +            arith_uint256 posHash;
 +            if (newPOSActive)
 +            {
 +                validHash = pblock->GetRawVerusPOSHash(rawHash, height);
 +                posHash = UintToArith256(rawHash) / value;
 +                //if (slowflag)
 +                //{
 +                //    printf("first nNonce: %s, height: %d\n", pblock->nNonce.GetHex().c_str(), height);
 +                //    printf("Raw POShash:   %s\n", posHash.GetHex().c_str());
 +                //}
 +            }
 +            if (newPOSActive && !(validHash && posHash <= target))
 +            {
++                printf("ERROR: invalid nonce value for PoS block\nnNonce: %s\nrawHash: %s\nposHash: %s\nvalue: %lu\n",
 +                        pblock->nNonce.GetHex().c_str(), rawHash.GetHex().c_str(), posHash.GetHex().c_str(), value);
 +            }
 +            //else
 +            {
 +                if (slowflag == 0)
 +                {
 +                    isPOS = true;
 +                }
 +                else if (!(pastBlockIndex = komodo_chainactive(height - 100)))
 +                {
 +                    fprintf(stderr,"ERROR: chain not fully loaded or invalid PoS block %s - no past block found\n",blkHash.ToString().c_str());
 +                }
 +                else 
 +#ifndef KOMODO_ZCASH
 +                if (!GetTransaction(txid, tx, Params().GetConsensus(), blkHash, true))
 +#else
 +                if (!GetTransaction(txid, tx, blkHash, true))
 +#endif
 +                {
 +                    fprintf(stderr,"ERROR: invalid PoS block %s - no source transaction\n",blkHash.ToString().c_str());
 +                }
 +                else
 +                {
 +                    CBlockHeader bh = pastBlockIndex->GetBlockHeader();
 +                    uint256 pastHash = bh.GetVerusEntropyHash(height - 100);
 +
 +                    // if height is over when Nonce is required to be the new format, we check that the new format is correct
 +                    // if over when we have the new POS hash function, we validate that as well
 +                    // they are 100 blocks apart
 +                    CPOSNonce nonce = pblock->nNonce;
 +
 +                    //printf("before nNonce: %s, height: %d\n", pblock->nNonce.GetHex().c_str(), height);
 +                    //validHash = pblock->GetRawVerusPOSHash(rawHash, height);
 +                    //hash = UintToArith256(rawHash) / tx.vout[voutNum].nValue;
 +                    //printf("Raw POShash:   %s\n", hash.GetHex().c_str());
 +
 +                    hash = UintToArith256(tx.GetVerusPOSHash(&nonce, voutNum, height, pastHash));
 +
 +                    //printf("after nNonce:  %s, height: %d\n", nonce.GetHex().c_str(), height);
 +                    //printf("POShash:       %s\n\n", hash.GetHex().c_str());
 +
 +                    //if ((!newPOSActive || posHash == hash) && hash <= target)
 +                    if (hash <= target)
 +                    {
 +                        if ((mapBlockIndex.count(blkHash) == 0) ||
 +                            !(pastBlockIndex = mapBlockIndex[blkHash]) || 
 +                            (height - pastBlockIndex->nHeight) < VERUS_MIN_STAKEAGE)
 +                        {
 +                            fprintf(stderr,"ERROR: invalid PoS block %s - stake transaction too new\n",blkHash.ToString().c_str());
 +                        }
 +                        else
 +                        {
 +                            // make sure we have the right target
 +                            CBlockIndex *previndex;
 +                            if (!(previndex = mapBlockIndex[pblock->hashPrevBlock]))
 +                            {
 +                                fprintf(stderr,"ERROR: invalid PoS block %s - no prev block found\n",blkHash.ToString().c_str());
 +                            }
 +                            else
 +                            {
 +                                arith_uint256 cTarget;
 +                                uint32_t nBits = lwmaGetNextPOSRequired(previndex, Params().GetConsensus());
 +                                cTarget.SetCompact(nBits);
 +                                bool nonceOK = true;
 +
 +                                // check to see how many fail
 +                                //if (nonce != pblock->nNonce)
 +                                //    printf("Mismatched nNonce: %s\nblkHash: %s, height: %d\n", nonce.GetHex().c_str(), pblock->GetHash().GetHex().c_str(), height);
 +
 +                                if (CPOSNonce::NewNonceActive(height) && !nonce.CheckPOSEntropy(pastHash, txid, voutNum))
 +                                {
 +                                    fprintf(stderr,"ERROR: invalid PoS block %s - nonce entropy corrupted or forged\n",blkHash.ToString().c_str());
 +                                    nonceOK = false;
 +                                }
 +                                else
 +                                {
 +                                    if (cTarget != target)
 +                                    {
 +                                        fprintf(stderr,"ERROR: invalid PoS block %s - invalid diff target\n",blkHash.ToString().c_str());
 +                                        nonceOK = false;
 +                                    }
 +                                }
 +                                if ( nonceOK && ExtractDestination(pblock->vtx[txn_count-1].vout[0].scriptPubKey, voutaddress) &&
 +                                        ExtractDestination(tx.vout[voutNum].scriptPubKey, destaddress) &&
 +                                        CScriptExt::ExtractVoutDestination(pblock->vtx[0], 0, cbaddress) )
 +                                {
 +                                    strcpy(voutaddr, CBitcoinAddress(voutaddress).ToString().c_str());
 +                                    strcpy(destaddr, CBitcoinAddress(destaddress).ToString().c_str());
 +                                    strcpy(cbaddr, CBitcoinAddress(cbaddress).ToString().c_str());
 +                                    if ( !strcmp(destaddr,voutaddr) && ( !strcmp(destaddr,cbaddr) || (height < 17840)) )
 +                                    {
 +                                        isPOS = true;
 +                                    }
 +                                    else
 +                                    {
 +                                        fprintf(stderr,"ERROR: invalid PoS block %s - invalid stake or coinbase destination\n",blkHash.ToString().c_str());
 +                                    }
 +                                }
 +                            }
 +                        }
 +                    }
 +                }
 +            }
 +        }
 +    }
 +    return(isPOS);
 +}
 +
 +int64_t komodo_checkcommission(CBlock *pblock,int32_t height)
 +{
 +    int64_t checktoshis=0; uint8_t *script;
 +    if ( ASSETCHAINS_COMMISSION != 0 )
 +    {
 +        checktoshis = komodo_commission(pblock);
 +        if ( checktoshis > 10000 && pblock->vtx[0].vout.size() != 2 )
 +            return(-1);
 +        else if ( checktoshis != 0 )
 +        {
 +            script = (uint8_t *)&pblock->vtx[0].vout[1].scriptPubKey[0];
 +            if ( script[0] != 33 || script[34] != OP_CHECKSIG || memcmp(script+1,ASSETCHAINS_OVERRIDE_PUBKEY33,33) != 0 )
 +                return(-1);
 +            if ( pblock->vtx[0].vout[1].nValue != checktoshis )
 +            {
 +                fprintf(stderr,"ht.%d checktoshis %.8f vs actual vout[1] %.8f\n",height,dstr(checktoshis),dstr(pblock->vtx[0].vout[1].nValue));
 +                return(-1);
 +            }
 +        }
 +    }
 +    return(checktoshis);
 +}
 +
 +bool KOMODO_TEST_ASSETCHAIN_SKIP_POW = 0;
 +
 +int32_t komodo_checkPOW(int32_t slowflag,CBlock *pblock,int32_t height)
 +{
 +    uint256 hash; arith_uint256 bnTarget,bhash; bool fNegative,fOverflow; uint8_t *script,pubkey33[33],pubkeys[64][33]; int32_t i,possible,PoSperc,is_PoSblock=0,n,failed = 0,notaryid = -1; int64_t checktoshis,value; CBlockIndex *pprev;
 +    if ( KOMODO_TEST_ASSETCHAIN_SKIP_POW == 0 && Params().NetworkIDString() == "regtest" )
 +        KOMODO_TEST_ASSETCHAIN_SKIP_POW = 1;
 +    if ( !CheckEquihashSolution(pblock, Params()) )
 +    {
 +        fprintf(stderr,"komodo_checkPOW slowflag.%d ht.%d CheckEquihashSolution failed\n",slowflag,height);
 +        return(-1);
 +    }
 +    hash = pblock->GetHash();
 +    bnTarget.SetCompact(pblock->nBits,&fNegative,&fOverflow);
 +    bhash = UintToArith256(hash);
 +    possible = komodo_block2pubkey33(pubkey33,pblock);
 +    if ( height == 0 )
 +    {
 +        if ( slowflag != 0 )
 +        {
 +            fprintf(stderr,"height.%d slowflag.%d possible.%d cmp.%d\n",height,slowflag,possible,bhash > bnTarget);
 +            return(0);
 +        }
 +        if ( (pprev= mapBlockIndex[pblock->hashPrevBlock]) != 0 )
 +            height = pprev->nHeight + 1;
 +        if ( height == 0 )
 +            return(0);
 +    }
 +    if ( ASSETCHAINS_LWMAPOS != 0 && bhash > bnTarget )
 +    {
 +        // if proof of stake is active, check if this is a valid PoS block before we fail
 +        if (verusCheckPOSBlock(slowflag, pblock, height))
 +        {
 +            return(0);
 +        }
 +    }
 +    if ( (ASSETCHAINS_SYMBOL[0] != 0 || height > 792000) && bhash > bnTarget )
 +    {
 +        failed = 1;
 +        if ( height > 0 && ASSETCHAINS_SYMBOL[0] == 0 ) // for the fast case
 +        {
 +            if ( (n= komodo_notaries(pubkeys,height,pblock->nTime)) > 0 )
 +            {
 +                for (i=0; i<n; i++)
 +                    if ( memcmp(pubkey33,pubkeys[i],33) == 0 )
 +                    {
 +                        notaryid = i;
 +                        break;
 +                    }
 +            }
 +        }
 +        else if ( possible == 0 || ASSETCHAINS_SYMBOL[0] != 0 )
 +        {
 +            if ( KOMODO_TEST_ASSETCHAIN_SKIP_POW )
 +                return(0);
 +            if ( ASSETCHAINS_STAKED == 0 ) // komodo_is_PoSblock will check bnTarget for staked chains
 +                return(-1);
 +        }
 +    }
 +    if ( ASSETCHAINS_STAKED != 0 && height >= 2 ) // must PoS or have at least 16x better PoW
 +    {
 +        if ( (is_PoSblock= komodo_is_PoSblock(slowflag,height,pblock,bnTarget,bhash)) == 0 )
 +        {
 +            if ( ASSETCHAINS_STAKED == 100 && height > 100 )  // only PoS allowed! POSTEST64
 +                return(-1);
 +            else
 +            {
 +                if ( slowflag == 0 ) // need all past 100 blocks to calculate PoW target
 +                    return(0);
 +                if ( slowflag != 0 )
 +                    bnTarget = komodo_PoWtarget(&PoSperc,bnTarget,height,ASSETCHAINS_STAKED);
 +                if ( bhash > bnTarget )
 +                {
 +                    for (i=31; i>=16; i--)
 +                        fprintf(stderr,"%02x",((uint8_t *)&bhash)[i]);
 +                    fprintf(stderr," > ");
 +                    for (i=31; i>=16; i--)
 +                        fprintf(stderr,"%02x",((uint8_t *)&bnTarget)[i]);
 +                    fprintf(stderr," ht.%d PoW diff violation PoSperc.%d vs goalperc.%d\n",height,PoSperc,(int32_t)ASSETCHAINS_STAKED);
 +                    return(-1);
 +                } else failed = 0;
 +            }
 +        }
 +        else if ( is_PoSblock < 0 )
 +        {
 +            fprintf(stderr,"unexpected negative is_PoSblock.%d\n",is_PoSblock);
 +            return(-1);
 +        }
 +    }
 +    if ( failed == 0 && ASSETCHAINS_OVERRIDE_PUBKEY33[0] != 0 )
 +    {
 +        if ( height == 1 )
 +        {
 +            script = (uint8_t *)&pblock->vtx[0].vout[0].scriptPubKey[0];
 +            if ( script[0] != 33 || script[34] != OP_CHECKSIG || memcmp(script+1,ASSETCHAINS_OVERRIDE_PUBKEY33,33) != 0 )
 +                return(-1);
 +        }
 +        else
 +        {
 +            if ( komodo_checkcommission(pblock,height) < 0 )
 +                return(-1);
 +        }
 +    }
 +    //fprintf(stderr,"komodo_checkPOW possible.%d slowflag.%d ht.%d notaryid.%d failed.%d\n",possible,slowflag,height,notaryid,failed);
 +    if ( failed != 0 && possible == 0 && notaryid < 0 )
 +        return(-1);
 +    else return(0);
 +}
 +
 +int64_t komodo_newcoins(int64_t *zfundsp,int32_t nHeight,CBlock *pblock)
 +{
 +    CTxDestination address; int32_t i,j,m,n,vout; uint8_t *script; uint256 txid,hashBlock; int64_t zfunds=0,vinsum=0,voutsum=0;
 +    n = pblock->vtx.size();
 +    for (i=0; i<n; i++)
 +    {
 +        CTransaction vintx,&tx = pblock->vtx[i];
 +        zfunds += (tx.GetValueOut() - tx.GetShieldedValueIn());
 +        if ( (m= tx.vin.size()) > 0 )
 +        {
 +            for (j=0; j<m; j++)
 +            {
 +                if ( i == 0 )
 +                    continue;
 +                txid = tx.vin[j].prevout.hash;
 +                vout = tx.vin[j].prevout.n;
 +                if ( !GetTransaction(txid,vintx,hashBlock, false) || vout >= vintx.vout.size() )
 +                {
 +                    fprintf(stderr,"ERROR: %s/v%d cant find\n",txid.ToString().c_str(),vout);
 +                    return(0);
 +                }
 +                vinsum += vintx.vout[vout].nValue;
 +            }
 +        }
 +        if ( (m= tx.vout.size()) > 0 )
 +        {
 +            for (j=0; j<m-1; j++)
 +            {
 +                if ( ExtractDestination(tx.vout[j].scriptPubKey,address) != 0 && strcmp("RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY",CBitcoinAddress(address).ToString().c_str()) != 0 )
 +                    voutsum += tx.vout[j].nValue;
 +                else printf("skip %.8f -> %s\n",dstr(tx.vout[j].nValue),CBitcoinAddress(address).ToString().c_str());
 +            }
 +            script = (uint8_t *)&tx.vout[j].scriptPubKey[0];
 +            if ( script == 0 || script[0] != 0x6a )
 +            {
 +                if ( ExtractDestination(tx.vout[j].scriptPubKey,address) != 0 && strcmp("RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPVMY",CBitcoinAddress(address).ToString().c_str()) != 0 )
 +                    voutsum += tx.vout[j].nValue;
 +            }
 +        }
 +    }
 +    *zfundsp = zfunds;
 +    if ( ASSETCHAINS_SYMBOL[0] == 0 && (voutsum-vinsum) == 100003*SATOSHIDEN ) // 15 times
 +        return(3 * SATOSHIDEN);
 +    //if ( voutsum-vinsum+zfunds > 100000*SATOSHIDEN || voutsum-vinsum+zfunds < 0 )
 +    //.    fprintf(stderr,"ht.%d vins %.8f, vouts %.8f -> %.8f zfunds %.8f\n",nHeight,dstr(vinsum),dstr(voutsum),dstr(voutsum)-dstr(vinsum),dstr(zfunds));
 +    return(voutsum - vinsum);
 +}
 +
 +int64_t komodo_coinsupply(int64_t *zfundsp,int32_t height)
 +{
 +    CBlockIndex *pindex; CBlock block; int64_t zfunds=0,supply = 0;
 +    //fprintf(stderr,"coinsupply %d\n",height);
 +    *zfundsp = 0;
 +    if ( (pindex= komodo_chainactive(height)) != 0 )
 +    {
 +        while ( pindex != 0 && pindex->nHeight > 0 )
 +        {
 +            if ( pindex->newcoins == 0 && pindex->zfunds == 0 )
 +            {
 +                if ( komodo_blockload(block,pindex) == 0 )
 +                    pindex->newcoins = komodo_newcoins(&pindex->zfunds,pindex->nHeight,&block);
 +                else
 +                {
 +                    fprintf(stderr,"error loading block.%d\n",pindex->nHeight);
 +                    return(0);
 +                }
 +            }
 +            supply += pindex->newcoins;
 +            zfunds += pindex->zfunds;
 +            //printf("start ht.%d new %.8f -> supply %.8f zfunds %.8f -> %.8f\n",pindex->nHeight,dstr(pindex->newcoins),dstr(supply),dstr(pindex->zfunds),dstr(zfunds));
 +            pindex = pindex->pprev;
 +        }
 +    }
 +    *zfundsp = zfunds;
 +    return(supply);
 +}
diff --cc src/main.cpp
index 3248a6e2d2abeaca7804ad616a66003167fd0e8d,2921f657d1bd83a4eb272300b97f079d487da485..1c0c19badb2f966b241b722703e3a982ec004b85
@@@ -235,302 -222,287 +235,301 @@@ namespace 
  //
  
  namespace {
 -
 -struct CBlockReject {
 -    unsigned char chRejectCode;
 -    string strRejectReason;
 -    uint256 hashBlock;
 -};
 -
 -/**
 - * Maintain validation-specific state about nodes, protected by cs_main, instead
 - * by CNode's own locks. This simplifies asynchronous operation, where
 - * processing of incoming data is done after the ProcessMessage call returns,
 - * and we're no longer holding the node's locks.
 - */
 -struct CNodeState {
 -    //! The peer's address
 -    CService address;
 -    //! Whether we have a fully established connection.
 -    bool fCurrentlyConnected;
 -    //! Accumulated misbehaviour score for this peer.
 -    int nMisbehavior;
 -    //! Whether this peer should be disconnected and banned (unless whitelisted).
 -    bool fShouldBan;
 -    //! String name of this peer (debugging/logging purposes).
 -    std::string name;
 -    //! List of asynchronously-determined block rejections to notify this peer about.
 -    std::vector<CBlockReject> rejects;
 -    //! The best known block we know this peer has announced.
 -    CBlockIndex *pindexBestKnownBlock;
 -    //! The hash of the last unknown block this peer has announced.
 -    uint256 hashLastUnknownBlock;
 -    //! The last full block we both have.
 -    CBlockIndex *pindexLastCommonBlock;
 -    //! Whether we've started headers synchronization with this peer.
 -    bool fSyncStarted;
 -    //! Since when we're stalling block download progress (in microseconds), or 0.
 -    int64_t nStallingSince;
 -    list<QueuedBlock> vBlocksInFlight;
 -    int nBlocksInFlight;
 -    int nBlocksInFlightValidHeaders;
 -    //! Whether we consider this a preferred download peer.
 -    bool fPreferredDownload;
 -
 -    CNodeState() {
 -        fCurrentlyConnected = false;
 -        nMisbehavior = 0;
 -        fShouldBan = false;
 -        pindexBestKnownBlock = NULL;
 -        hashLastUnknownBlock.SetNull();
 -        pindexLastCommonBlock = NULL;
 -        fSyncStarted = false;
 -        nStallingSince = 0;
 -        nBlocksInFlight = 0;
 -        nBlocksInFlightValidHeaders = 0;
 -        fPreferredDownload = false;
 +    
 +    struct CBlockReject {
 +        unsigned char chRejectCode;
 +        string strRejectReason;
 +        uint256 hashBlock;
 +    };
 +    
 +    /**
 +     * Maintain validation-specific state about nodes, protected by cs_main, instead
 +     * by CNode's own locks. This simplifies asynchronous operation, where
 +     * processing of incoming data is done after the ProcessMessage call returns,
 +     * and we're no longer holding the node's locks.
 +     */
 +    struct CNodeState {
 +        //! The peer's address
 +        CService address;
 +        //! Whether we have a fully established connection.
 +        bool fCurrentlyConnected;
 +        //! Accumulated misbehaviour score for this peer.
 +        int nMisbehavior;
 +        //! Whether this peer should be disconnected and banned (unless whitelisted).
 +        bool fShouldBan;
 +        //! String name of this peer (debugging/logging purposes).
 +        std::string name;
 +        //! List of asynchronously-determined block rejections to notify this peer about.
 +        std::vector<CBlockReject> rejects;
 +        //! The best known block we know this peer has announced.
 +        CBlockIndex *pindexBestKnownBlock;
 +        //! The hash of the last unknown block this peer has announced.
 +        uint256 hashLastUnknownBlock;
 +        //! The last full block we both have.
 +        CBlockIndex *pindexLastCommonBlock;
 +        //! Whether we've started headers synchronization with this peer.
 +        bool fSyncStarted;
 +        //! Since when we're stalling block download progress (in microseconds), or 0.
 +        int64_t nStallingSince;
 +        list<QueuedBlock> vBlocksInFlight;
 +        int nBlocksInFlight;
 +        int nBlocksInFlightValidHeaders;
 +        //! Whether we consider this a preferred download peer.
 +        bool fPreferredDownload;
 +        
 +        CNodeState() {
 +            fCurrentlyConnected = false;
 +            nMisbehavior = 0;
 +            fShouldBan = false;
 +            pindexBestKnownBlock = NULL;
 +            hashLastUnknownBlock.SetNull();
 +            pindexLastCommonBlock = NULL;
 +            fSyncStarted = false;
 +            nStallingSince = 0;
 +            nBlocksInFlight = 0;
 +            nBlocksInFlightValidHeaders = 0;
 +            fPreferredDownload = false;
 +        }
 +    };
 +    
 +    /** Map maintaining per-node state. Requires cs_main. */
 +    map<NodeId, CNodeState> mapNodeState;
 +    
 +    // Requires cs_main.
 +    CNodeState *State(NodeId pnode) {
 +        map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode);
 +        if (it == mapNodeState.end())
 +            return NULL;
 +        return &it->second;
      }
 -};
 -
 -/** Map maintaining per-node state. Requires cs_main. */
 -map<NodeId, CNodeState> mapNodeState;
 -
 -// Requires cs_main.
 -CNodeState *State(NodeId pnode) {
 -    map<NodeId, CNodeState>::iterator it = mapNodeState.find(pnode);
 -    if (it == mapNodeState.end())
 -        return NULL;
 -    return &it->second;
 -}
 -
 -int GetHeight()
 -{
 -    LOCK(cs_main);
 -    return chainActive.Height();
 -}
 -
 -void UpdatePreferredDownload(CNode* node, CNodeState* state)
 -{
 -    nPreferredDownload -= state->fPreferredDownload;
 -
 -    // Whether this node should be marked as a preferred download node.
 -    state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient;
 -
 -    nPreferredDownload += state->fPreferredDownload;
 -}
 -
 -// Returns time at which to timeout block request (nTime in microseconds)
 -int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consensus::Params &consensusParams)
 -{
 -    return nTime + 500000 * consensusParams.nPowTargetSpacing * (4 + nValidatedQueuedBefore);
 -}
 -
 -void InitializeNode(NodeId nodeid, const CNode *pnode) {
 -    LOCK(cs_main);
 -    CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second;
 -    state.name = pnode->addrName;
 -    state.address = pnode->addr;
 -}
 -
 -void FinalizeNode(NodeId nodeid) {
 -    LOCK(cs_main);
 -    CNodeState *state = State(nodeid);
 -
 -    if (state->fSyncStarted)
 -        nSyncStarted--;
 -
 -    if (state->nMisbehavior == 0 && state->fCurrentlyConnected) {
 -        AddressCurrentlyConnected(state->address);
 +    
 +    int GetHeight()
 +    {
-         LOCK(cs_main);
-         return chainActive.Height();
++        return chainActive.LastTip()->nHeight;
      }
 -
 -    BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight)
 +    
 +    void UpdatePreferredDownload(CNode* node, CNodeState* state)
 +    {
 +        nPreferredDownload -= state->fPreferredDownload;
 +        
 +        // Whether this node should be marked as a preferred download node.
 +        state->fPreferredDownload = (!node->fInbound || node->fWhitelisted) && !node->fOneShot && !node->fClient;
 +        
 +        nPreferredDownload += state->fPreferredDownload;
 +    }
 +    
 +    // Returns time at which to timeout block request (nTime in microseconds)
 +    int64_t GetBlockTimeout(int64_t nTime, int nValidatedQueuedBefore, const Consensus::Params &consensusParams)
 +    {
 +        return nTime + 500000 * consensusParams.nPowTargetSpacing * (4 + nValidatedQueuedBefore);
 +    }
 +    
 +    void InitializeNode(NodeId nodeid, const CNode *pnode) {
 +        LOCK(cs_main);
 +        CNodeState &state = mapNodeState.insert(std::make_pair(nodeid, CNodeState())).first->second;
 +        state.name = pnode->addrName;
 +        state.address = pnode->addr;
 +    }
 +    
 +    void FinalizeNode(NodeId nodeid) {
 +        LOCK(cs_main);
 +        CNodeState *state = State(nodeid);
 +        
 +        if (state->fSyncStarted)
 +            nSyncStarted--;
 +        
 +        if (state->nMisbehavior == 0 && state->fCurrentlyConnected) {
 +            AddressCurrentlyConnected(state->address);
 +        }
 +        
 +        BOOST_FOREACH(const QueuedBlock& entry, state->vBlocksInFlight)
          mapBlocksInFlight.erase(entry.hash);
 -    EraseOrphansFor(nodeid);
 -    nPreferredDownload -= state->fPreferredDownload;
 -
 -    mapNodeState.erase(nodeid);
 -}
 -
 -// Requires cs_main.
 -// Returns a bool indicating whether we requested this block.
 -bool MarkBlockAsReceived(const uint256& hash) {
 -    map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
 -    if (itInFlight != mapBlocksInFlight.end()) {
 -        CNodeState *state = State(itInFlight->second.first);
 -        nQueuedValidatedHeaders -= itInFlight->second.second->fValidatedHeaders;
 -        state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders;
 -        state->vBlocksInFlight.erase(itInFlight->second.second);
 -        state->nBlocksInFlight--;
 -        state->nStallingSince = 0;
 -        mapBlocksInFlight.erase(itInFlight);
 -        return true;
 +        EraseOrphansFor(nodeid);
 +        nPreferredDownload -= state->fPreferredDownload;
 +        
 +        mapNodeState.erase(nodeid);
      }
 -    return false;
 -}
 -
 -// Requires cs_main.
 -void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL) {
 -    CNodeState *state = State(nodeid);
 -    assert(state != NULL);
 -
 -    // Make sure it's not listed somewhere already.
 -    MarkBlockAsReceived(hash);
 -
 -    int64_t nNow = GetTimeMicros();
 -    QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams)};
 -    nQueuedValidatedHeaders += newentry.fValidatedHeaders;
 -    list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry);
 -    state->nBlocksInFlight++;
 -    state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders;
 -    mapBlocksInFlight[hash] = std::make_pair(nodeid, it);
 -}
 -
 -/** Check whether the last unknown block a peer advertized is not yet known. */
 -void ProcessBlockAvailability(NodeId nodeid) {
 -    CNodeState *state = State(nodeid);
 -    assert(state != NULL);
 -
 -    if (!state->hashLastUnknownBlock.IsNull()) {
 -        BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock);
 -        if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0) {
 -            if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
 -                state->pindexBestKnownBlock = itOld->second;
 -            state->hashLastUnknownBlock.SetNull();
 -        }
 +    
 +    void LimitMempoolSize(CTxMemPool& pool, size_t limit, unsigned long age)
 +    {
 +        /*    int expired = pool.Expire(GetTime() - age);
 +         if (expired != 0)
 +         LogPrint("mempool", "Expired %i transactions from the memory pool\n", expired);
 +         
 +         std::vector<uint256> vNoSpendsRemaining;
 +         pool.TrimToSize(limit, &vNoSpendsRemaining);
 +         BOOST_FOREACH(const uint256& removed, vNoSpendsRemaining)
 +         pcoinsTip->Uncache(removed);*/
      }
 -}
 -
 -/** Update tracking information about which blocks a peer is assumed to have. */
 -void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
 -    CNodeState *state = State(nodeid);
 -    assert(state != NULL);
 -
 -    ProcessBlockAvailability(nodeid);
 -
 -    BlockMap::iterator it = mapBlockIndex.find(hash);
 -    if (it != mapBlockIndex.end() && it->second->nChainWork > 0) {
 -        // An actually better block was announced.
 -        if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
 -            state->pindexBestKnownBlock = it->second;
 -    } else {
 -        // An unknown block was announced; just assume that the latest one is the best one.
 -        state->hashLastUnknownBlock = hash;
 +    
 +    // Requires cs_main.
 +    // Returns a bool indicating whether we requested this block.
 +    bool MarkBlockAsReceived(const uint256& hash) {
 +        map<uint256, pair<NodeId, list<QueuedBlock>::iterator> >::iterator itInFlight = mapBlocksInFlight.find(hash);
 +        if (itInFlight != mapBlocksInFlight.end()) {
 +            CNodeState *state = State(itInFlight->second.first);
 +            nQueuedValidatedHeaders -= itInFlight->second.second->fValidatedHeaders;
 +            state->nBlocksInFlightValidHeaders -= itInFlight->second.second->fValidatedHeaders;
 +            state->vBlocksInFlight.erase(itInFlight->second.second);
 +            state->nBlocksInFlight--;
 +            state->nStallingSince = 0;
 +            mapBlocksInFlight.erase(itInFlight);
 +            return true;
 +        }
 +        return false;
      }
 -}
 -
 -/** Find the last common ancestor two blocks have.
 - *  Both pa and pb must be non-NULL. */
 -CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) {
 -    if (pa->nHeight > pb->nHeight) {
 -        pa = pa->GetAncestor(pb->nHeight);
 -    } else if (pb->nHeight > pa->nHeight) {
 -        pb = pb->GetAncestor(pa->nHeight);
 +    
 +    // Requires cs_main.
 +    void MarkBlockAsInFlight(NodeId nodeid, const uint256& hash, const Consensus::Params& consensusParams, CBlockIndex *pindex = NULL) {
 +        CNodeState *state = State(nodeid);
 +        assert(state != NULL);
 +        
 +        // Make sure it's not listed somewhere already.
 +        MarkBlockAsReceived(hash);
 +        
 +        int64_t nNow = GetTimeMicros();
 +        QueuedBlock newentry = {hash, pindex, nNow, pindex != NULL, GetBlockTimeout(nNow, nQueuedValidatedHeaders, consensusParams)};
 +        nQueuedValidatedHeaders += newentry.fValidatedHeaders;
 +        list<QueuedBlock>::iterator it = state->vBlocksInFlight.insert(state->vBlocksInFlight.end(), newentry);
 +        state->nBlocksInFlight++;
 +        state->nBlocksInFlightValidHeaders += newentry.fValidatedHeaders;
 +        mapBlocksInFlight[hash] = std::make_pair(nodeid, it);
      }
 -
 -    while (pa != pb && pa && pb) {
 -        pa = pa->pprev;
 -        pb = pb->pprev;
 +    
 +    /** Check whether the last unknown block a peer advertized is not yet known. */
 +    void ProcessBlockAvailability(NodeId nodeid) {
 +        CNodeState *state = State(nodeid);
 +        assert(state != NULL);
 +        
 +        if (!state->hashLastUnknownBlock.IsNull()) {
 +            BlockMap::iterator itOld = mapBlockIndex.find(state->hashLastUnknownBlock);
 +            if (itOld != mapBlockIndex.end() && itOld->second->nChainWork > 0)
 +            {
 +                if (state->pindexBestKnownBlock == NULL || itOld->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
 +                    state->pindexBestKnownBlock = itOld->second;
 +                state->hashLastUnknownBlock.SetNull();
 +            }
 +        }
      }
 -
 -    // Eventually all chain branches meet at the genesis block.
 -    assert(pa == pb);
 -    return pa;
 -}
 -
 -/** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
 - *  at most count entries. */
 -void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBlockIndex*>& vBlocks, NodeId& nodeStaller) {
 -    if (count == 0)
 -        return;
 -
 -    vBlocks.reserve(vBlocks.size() + count);
 -    CNodeState *state = State(nodeid);
 -    assert(state != NULL);
 -
 -    // Make sure pindexBestKnownBlock is up to date, we'll need it.
 -    ProcessBlockAvailability(nodeid);
 -
 -    if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) {
 -        // This peer has nothing interesting.
 -        return;
 +    
 +    /** Update tracking information about which blocks a peer is assumed to have. */
 +    void UpdateBlockAvailability(NodeId nodeid, const uint256 &hash) {
 +        CNodeState *state = State(nodeid);
 +        assert(state != NULL);
 +        
 +        /*ProcessBlockAvailability(nodeid);
 +         
 +         BlockMap::iterator it = mapBlockIndex.find(hash);
 +         if (it != mapBlockIndex.end() && it->second->nChainWork > 0) {
 +         // An actually better block was announced.
 +         if (state->pindexBestKnownBlock == NULL || it->second->nChainWork >= state->pindexBestKnownBlock->nChainWork)
 +         state->pindexBestKnownBlock = it->second;
 +         } else*/
 +        {
 +            // An unknown block was announced; just assume that the latest one is the best one.
 +            state->hashLastUnknownBlock = hash;
 +        }
      }
 -
 -    if (state->pindexLastCommonBlock == NULL) {
 -        // Bootstrap quickly by guessing a parent of our best tip is the forking point.
 -        // Guessing wrong in either direction is not a problem.
 -        state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())];
 +    
 +    /** Find the last common ancestor two blocks have.
 +     *  Both pa and pb must be non-NULL. */
 +    CBlockIndex* LastCommonAncestor(CBlockIndex* pa, CBlockIndex* pb) {
 +        if (pa->nHeight > pb->nHeight) {
 +            pa = pa->GetAncestor(pb->nHeight);
 +        } else if (pb->nHeight > pa->nHeight) {
 +            pb = pb->GetAncestor(pa->nHeight);
 +        }
 +        
 +        while (pa != pb && pa && pb) {
 +            pa = pa->pprev;
 +            pb = pb->pprev;
 +        }
 +        
 +        // Eventually all chain branches meet at the genesis block.
 +        assert(pa == pb);
 +        return pa;
      }
 -
 -    // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor
 -    // of its current tip anymore. Go back enough to fix that.
 -    state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock);
 -    if (state->pindexLastCommonBlock == state->pindexBestKnownBlock)
 -        return;
 -
 -    std::vector<CBlockIndex*> vToFetch;
 -    CBlockIndex *pindexWalk = state->pindexLastCommonBlock;
 -    // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last
 -    // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to
 -    // download that next block if the window were 1 larger.
 -    int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW;
 -    int nMaxHeight = std::min<int>(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1);
 -    NodeId waitingfor = -1;
 -    while (pindexWalk->nHeight < nMaxHeight) {
 -        // Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards
 -        // pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive
 -        // as iterating over ~100 CBlockIndex* entries anyway.
 -        int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max<int>(count - vBlocks.size(), 128));
 -        vToFetch.resize(nToFetch);
 -        pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch);
 -        vToFetch[nToFetch - 1] = pindexWalk;
 -        for (unsigned int i = nToFetch - 1; i > 0; i--) {
 -            vToFetch[i - 1] = vToFetch[i]->pprev;
 -        }
 -
 -        // Iterate over those blocks in vToFetch (in forward direction), adding the ones that
 -        // are not yet downloaded and not in flight to vBlocks. In the meantime, update
 -        // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's
 -        // already part of our chain (and therefore don't need it even if pruned).
 -        BOOST_FOREACH(CBlockIndex* pindex, vToFetch) {
 -            if (!pindex->IsValid(BLOCK_VALID_TREE)) {
 -                // We consider the chain that this peer is on invalid.
 -                return;
 +    
 +    /** Update pindexLastCommonBlock and add not-in-flight missing successors to vBlocks, until it has
 +     *  at most count entries. */
 +    void FindNextBlocksToDownload(NodeId nodeid, unsigned int count, std::vector<CBlockIndex*>& vBlocks, NodeId& nodeStaller) {
 +        if (count == 0)
 +            return;
 +        
 +        vBlocks.reserve(vBlocks.size() + count);
 +        CNodeState *state = State(nodeid);
 +        assert(state != NULL);
 +        
 +        // Make sure pindexBestKnownBlock is up to date, we'll need it.
 +        ProcessBlockAvailability(nodeid);
 +        
 +        if (state->pindexBestKnownBlock == NULL || state->pindexBestKnownBlock->nChainWork < chainActive.Tip()->nChainWork) {
 +            // This peer has nothing interesting.
 +            return;
 +        }
 +        
 +        if (state->pindexLastCommonBlock == NULL) {
 +            // Bootstrap quickly by guessing a parent of our best tip is the forking point.
 +            // Guessing wrong in either direction is not a problem.
 +            state->pindexLastCommonBlock = chainActive[std::min(state->pindexBestKnownBlock->nHeight, chainActive.Height())];
 +        }
 +        
 +        // If the peer reorganized, our previous pindexLastCommonBlock may not be an ancestor
 +        // of its current tip anymore. Go back enough to fix that.
 +        state->pindexLastCommonBlock = LastCommonAncestor(state->pindexLastCommonBlock, state->pindexBestKnownBlock);
 +        if (state->pindexLastCommonBlock == state->pindexBestKnownBlock)
 +            return;
 +        
 +        std::vector<CBlockIndex*> vToFetch;
 +        CBlockIndex *pindexWalk = state->pindexLastCommonBlock;
 +        // Never fetch further than the best block we know the peer has, or more than BLOCK_DOWNLOAD_WINDOW + 1 beyond the last
 +        // linked block we have in common with this peer. The +1 is so we can detect stalling, namely if we would be able to
 +        // download that next block if the window were 1 larger.
 +        int nWindowEnd = state->pindexLastCommonBlock->nHeight + BLOCK_DOWNLOAD_WINDOW;
 +        int nMaxHeight = std::min<int>(state->pindexBestKnownBlock->nHeight, nWindowEnd + 1);
 +        NodeId waitingfor = -1;
 +        while (pindexWalk->nHeight < nMaxHeight) {
 +            // Read up to 128 (or more, if more blocks than that are needed) successors of pindexWalk (towards
 +            // pindexBestKnownBlock) into vToFetch. We fetch 128, because CBlockIndex::GetAncestor may be as expensive
 +            // as iterating over ~100 CBlockIndex* entries anyway.
 +            int nToFetch = std::min(nMaxHeight - pindexWalk->nHeight, std::max<int>(count - vBlocks.size(), 128));
 +            vToFetch.resize(nToFetch);
 +            pindexWalk = state->pindexBestKnownBlock->GetAncestor(pindexWalk->nHeight + nToFetch);
 +            vToFetch[nToFetch - 1] = pindexWalk;
 +            for (unsigned int i = nToFetch - 1; i > 0; i--) {
 +                vToFetch[i - 1] = vToFetch[i]->pprev;
              }
 -            if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) {
 -                if (pindex->nChainTx)
 -                    state->pindexLastCommonBlock = pindex;
 -            } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
 -                // The block is not already downloaded, and not yet in flight.
 -                if (pindex->nHeight > nWindowEnd) {
 -                    // We reached the end of the window.
 -                    if (vBlocks.size() == 0 && waitingfor != nodeid) {
 -                        // We aren't able to fetch anything, but we would be if the download window was one larger.
 -                        nodeStaller = waitingfor;
 -                    }
 +            
 +            // Iterate over those blocks in vToFetch (in forward direction), adding the ones that
 +            // are not yet downloaded and not in flight to vBlocks. In the meantime, update
 +            // pindexLastCommonBlock as long as all ancestors are already downloaded, or if it's
 +            // already part of our chain (and therefore don't need it even if pruned).
 +            BOOST_FOREACH(CBlockIndex* pindex, vToFetch) {
 +                if (!pindex->IsValid(BLOCK_VALID_TREE)) {
 +                    // We consider the chain that this peer is on invalid.
                      return;
                  }
 -                vBlocks.push_back(pindex);
 -                if (vBlocks.size() == count) {
 -                    return;
 +                if (pindex->nStatus & BLOCK_HAVE_DATA || chainActive.Contains(pindex)) {
 +                    if (pindex->nChainTx)
 +                        state->pindexLastCommonBlock = pindex;
 +                } else if (mapBlocksInFlight.count(pindex->GetBlockHash()) == 0) {
 +                    // The block is not already downloaded, and not yet in flight.
 +                    if (pindex->nHeight > nWindowEnd) {
 +                        // We reached the end of the window.
 +                        if (vBlocks.size() == 0 && waitingfor != nodeid) {
 +                            // We aren't able to fetch anything, but we would be if the download window was one larger.
 +                            nodeStaller = waitingfor;
 +                        }
 +                        return;
 +                    }
 +                    vBlocks.push_back(pindex);
 +                    if (vBlocks.size() == count) {
 +                        return;
 +                    }
 +                } else if (waitingfor == -1) {
 +                    // This is the first already-in-flight block.
 +                    waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first;
                  }
 -            } else if (waitingfor == -1) {
 -                // This is the first already-in-flight block.
 -                waitingfor = mapBlocksInFlight[pindex->GetBlockHash()].first;
              }
          }
      }
@@@ -6302,16 -5021,16 +6307,16 @@@ bool static ProcessMessage(CNode* pfrom
              pfrom->fDisconnect = true;
              return false;
          }
-         
-         // When Overwinter is active, reject incoming connections from non-Overwinter nodes
+         // Reject incoming connections from nodes that don't know about the current epoch
          const Consensus::Params& params = Params().GetConsensus();
-         if (NetworkUpgradeActive(GetHeight(), params, Consensus::UPGRADE_OVERWINTER)
-             && nVersion < params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion)
+         auto currentEpoch = CurrentEpoch(GetHeight(), params);
+         if (pfrom->nVersion < params.vUpgrades[currentEpoch].nProtocolVersion)
          {
 -            LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
 +            LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, nVersion);
              pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
-                                strprintf("Version must be %d or greater",
-                                          params.vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion));
+                             strprintf("Version must be %d or greater",
+                             params.vUpgrades[currentEpoch].nProtocolVersion));
              pfrom->fDisconnect = true;
              return false;
          }
              State(pfrom->GetId())->fCurrentlyConnected = true;
          }
      }
 -
 -
 +    
 +    
      // Disconnect existing peer connection when:
      // 1. The version message has been received
-     // 2. Overwinter is active
-     // 3. Peer version is pre-Overwinter
-     else if (NetworkUpgradeActive(GetHeight(), chainparams.GetConsensus(), Consensus::UPGRADE_OVERWINTER)
-              && (pfrom->nVersion < chainparams.GetConsensus().vUpgrades[Consensus::UPGRADE_OVERWINTER].nProtocolVersion))
+     // 2. Peer version is below the minimum version for the current epoch
+     else if (pfrom->nVersion < chainparams.GetConsensus().vUpgrades[
+         CurrentEpoch(GetHeight(), chainparams.GetConsensus())].nProtocolVersion)
      {
          LogPrintf("peer=%d using obsolete version %i; disconnecting\n", pfrom->id, pfrom->nVersion);
          pfrom->PushMessage("reject", strCommand, REJECT_OBSOLETE,
diff --cc src/main.h
Simple merge
diff --cc src/net.cpp
Simple merge
index 1649e36f2a54bfefe5b678df4cd0a5a291cfeb8f,242403b19a623d47d4a214e644975cfac76f2a19..f4fbbc0584ee4771d5cfa60ddccb2aea5a1adfc7
@@@ -120,138 -115,48 +121,182 @@@ UniValue TxJoinSplitToJSON(const CTrans
      return vjoinsplit;
  }
  
-         out.push_back(Pair("valueZat", txout.nValue));
 +uint64_t komodo_accrued_interest(int32_t *txheightp,uint32_t *locktimep,uint256 hash,int32_t n,int32_t checkheight,uint64_t checkvalue,int32_t tipheight);
 +
+ UniValue TxShieldedSpendsToJSON(const CTransaction& tx) {
+     UniValue vdesc(UniValue::VARR);
+     for (const SpendDescription& spendDesc : tx.vShieldedSpend) {
+         UniValue obj(UniValue::VOBJ);
+         obj.push_back(Pair("cv", spendDesc.cv.GetHex()));
+         obj.push_back(Pair("anchor", spendDesc.anchor.GetHex()));
+         obj.push_back(Pair("nullifier", spendDesc.nullifier.GetHex()));
+         obj.push_back(Pair("rk", spendDesc.rk.GetHex()));
+         obj.push_back(Pair("proof", HexStr(spendDesc.zkproof.begin(), spendDesc.zkproof.end())));
+         obj.push_back(Pair("spendAuthSig", HexStr(spendDesc.spendAuthSig.begin(), spendDesc.spendAuthSig.end())));
+         vdesc.push_back(obj);
+     }
+     return vdesc;
+ }
+ UniValue TxShieldedOutputsToJSON(const CTransaction& tx) {
+     UniValue vdesc(UniValue::VARR);
+     for (const OutputDescription& outputDesc : tx.vShieldedOutput) {
+         UniValue obj(UniValue::VOBJ);
+         obj.push_back(Pair("cv", outputDesc.cv.GetHex()));
+         obj.push_back(Pair("cmu", outputDesc.cm.GetHex()));
+         obj.push_back(Pair("ephemeralKey", outputDesc.ephemeralKey.GetHex()));
+         obj.push_back(Pair("encCiphertext", HexStr(outputDesc.encCiphertext.begin(), outputDesc.encCiphertext.end())));
+         obj.push_back(Pair("outCiphertext", HexStr(outputDesc.outCiphertext.begin(), outputDesc.outCiphertext.end())));
+         obj.push_back(Pair("proof", HexStr(outputDesc.zkproof.begin(), outputDesc.zkproof.end())));
+         vdesc.push_back(obj);
+     }
+     return vdesc;
+ }
 +int32_t myIsutxo_spent(uint256 &spenttxid,uint256 txid,int32_t vout)
 +{
 +    CSpentIndexValue spentInfo; CSpentIndexKey spentKey(txid,vout);
 +    if ( GetSpentIndex(spentKey,spentInfo) )
 +    {
 +        spenttxid = spentInfo.txid;
 +        return((int32_t)spentInfo.inputIndex);
 +        // out.push_back(Pair("spentHeight", spentInfo.blockHeight));
 +    }
 +    memset(&spenttxid,0,sizeof(spenttxid));
 +    return(-1);
 +}
 +
 +void TxToJSONExpanded(const CTransaction& tx, const uint256 hashBlock, UniValue& entry, int nHeight = 0, int nConfirmations = 0, int nBlockTime = 0)
 +{
 +    uint256 txid = tx.GetHash();
 +    entry.push_back(Pair("txid", txid.GetHex()));
 +    entry.push_back(Pair("overwintered", tx.fOverwintered));
 +    entry.push_back(Pair("version", tx.nVersion));
 +    if (tx.fOverwintered) {
 +        entry.push_back(Pair("versiongroupid", HexInt(tx.nVersionGroupId)));
 +    }
 +    entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
 +    if (tx.fOverwintered) {
 +        entry.push_back(Pair("expiryheight", (int64_t)tx.nExpiryHeight));
 +    }
 +    UniValue vin(UniValue::VARR);
 +    BOOST_FOREACH(const CTxIn& txin, tx.vin) {
 +        UniValue in(UniValue::VOBJ);
 +        if (tx.IsCoinBase())
 +            in.push_back(Pair("coinbase", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
 +        else if (tx.IsCoinImport()) {
 +            in.push_back(Pair("is_import", "1"));
 +        }
 +        else {
 +            in.push_back(Pair("txid", txin.prevout.hash.GetHex()));
 +            in.push_back(Pair("vout", (int64_t)txin.prevout.n));
 +            {
 +                uint256 hash; CTransaction tx; CTxDestination address;
 +                if (GetTransaction(txin.prevout.hash,tx,hash,false))
 +                {
 +                    if (ExtractDestination(tx.vout[txin.prevout.n].scriptPubKey, address))
 +                        in.push_back(Pair("address", CBitcoinAddress(address).ToString()));
 +                }
 +            }
 +            UniValue o(UniValue::VOBJ);
 +            o.push_back(Pair("asm", ScriptToAsmStr(txin.scriptSig, true)));
 +            o.push_back(Pair("hex", HexStr(txin.scriptSig.begin(), txin.scriptSig.end())));
 +            in.push_back(Pair("scriptSig", o));
 +
 +            // Add address and value info if spentindex enabled
 +            CSpentIndexValue spentInfo;
 +            CSpentIndexKey spentKey(txin.prevout.hash, txin.prevout.n);
 +            if (GetSpentIndex(spentKey, spentInfo)) {
 +                in.push_back(Pair("value", ValueFromAmount(spentInfo.satoshis)));
 +                in.push_back(Pair("valueSat", spentInfo.satoshis));
 +                if (spentInfo.addressType == 1) {
 +                    in.push_back(Pair("address", CBitcoinAddress(CKeyID(spentInfo.addressHash)).ToString()));
 +                }
 +                else if (spentInfo.addressType == 2)  {
 +                    in.push_back(Pair("address", CBitcoinAddress(CScriptID(spentInfo.addressHash)).ToString()));
 +                }
 +            }
 +        }
 +        in.push_back(Pair("sequence", (int64_t)txin.nSequence));
 +        vin.push_back(in);
 +    }
 +    entry.push_back(Pair("vin", vin));
 +    BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
 +    CBlockIndex *tipindex,*pindex = it->second;
 +    uint64_t interest;
 +    UniValue vout(UniValue::VARR);
 +    for (unsigned int i = 0; i < tx.vout.size(); i++)
 +    {
 +        const CTxOut& txout = tx.vout[i];
 +        UniValue out(UniValue::VOBJ);
 +        out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
 +        if ( ASSETCHAINS_SYMBOL[0] == 0 && pindex != 0 && tx.nLockTime >= 500000000 && (tipindex= chainActive.LastTip()) != 0 )
 +        {
 +            int64_t interest; int32_t txheight; uint32_t locktime;
 +            interest = komodo_accrued_interest(&txheight,&locktime,tx.GetHash(),i,0,txout.nValue,(int32_t)tipindex->nHeight);
 +            out.push_back(Pair("interest", ValueFromAmount(interest)));
 +        }
 +        out.push_back(Pair("valueSat", txout.nValue)); // [+] Decker
 +        out.push_back(Pair("n", (int64_t)i));
 +        UniValue o(UniValue::VOBJ);
 +        ScriptPubKeyToJSON(txout.scriptPubKey, o, true);
 +        out.push_back(Pair("scriptPubKey", o));
 +
 +        // Add spent information if spentindex is enabled
 +        CSpentIndexValue spentInfo;
 +        CSpentIndexKey spentKey(txid, i);
 +        if (GetSpentIndex(spentKey, spentInfo)) {
 +            out.push_back(Pair("spentTxId", spentInfo.txid.GetHex()));
 +            out.push_back(Pair("spentIndex", (int)spentInfo.inputIndex));
 +            out.push_back(Pair("spentHeight", spentInfo.blockHeight));
 +        }
 +
 +        vout.push_back(out);
 +    }
 +    entry.push_back(Pair("vout", vout));
 +
 +    UniValue vjoinsplit = TxJoinSplitToJSON(tx);
 +    entry.push_back(Pair("vjoinsplit", vjoinsplit));
 +
++    if (tx.fOverwintered && tx.nVersion >= SAPLING_TX_VERSION) {
++        entry.push_back(Pair("valueBalance", ValueFromAmount(tx.valueBalance)));
++        UniValue vspenddesc = TxShieldedSpendsToJSON(tx);
++        entry.push_back(Pair("vShieldedSpend", vspenddesc));
++        UniValue voutputdesc = TxShieldedOutputsToJSON(tx);
++        entry.push_back(Pair("vShieldedOutput", voutputdesc));
++        if (!(vspenddesc.empty() && voutputdesc.empty())) {
++            entry.push_back(Pair("bindingSig", HexStr(tx.bindingSig.begin(), tx.bindingSig.end())));
++        }
++    }
++
 +    if (!hashBlock.IsNull()) {
 +        entry.push_back(Pair("blockhash", hashBlock.GetHex()));
 +
 +        if (nConfirmations > 0) {
 +            entry.push_back(Pair("height", nHeight));
 +            entry.push_back(Pair("confirmations", nConfirmations));
 +            entry.push_back(Pair("time", nBlockTime));
 +            entry.push_back(Pair("blocktime", nBlockTime));
 +        } else {
 +            entry.push_back(Pair("height", -1));
 +            entry.push_back(Pair("confirmations", 0));
 +        }
 +    }
 +
 +}
 +
  void TxToJSON(const CTransaction& tx, const uint256 hashBlock, UniValue& entry)
  {
-     uint256 txid = tx.GetHash();
-     entry.push_back(Pair("txid", txid.GetHex()));
-     entry.push_back(Pair("size", (int)::GetSerializeSize(tx, SER_NETWORK, PROTOCOL_VERSION)));
+     entry.push_back(Pair("txid", tx.GetHash().GetHex()));
+     entry.push_back(Pair("overwintered", tx.fOverwintered));
      entry.push_back(Pair("version", tx.nVersion));
+     if (tx.fOverwintered) {
+         entry.push_back(Pair("versiongroupid", HexInt(tx.nVersionGroupId)));
+     }
      entry.push_back(Pair("locktime", (int64_t)tx.nLockTime));
+     if (tx.fOverwintered) {
+         entry.push_back(Pair("expiryheight", (int64_t)tx.nExpiryHeight));
+     }
      UniValue vin(UniValue::VARR);
      BOOST_FOREACH(const CTxIn& txin, tx.vin) {
          UniValue in(UniValue::VOBJ);
          vin.push_back(in);
      }
      entry.push_back(Pair("vin", vin));
      UniValue vout(UniValue::VARR);
++    BlockMap::iterator it = mapBlockIndex.find(pcoinsTip->GetBestBlock());
++    CBlockIndex *tipindex,*pindex = it->second;
++    uint64_t interest;
      for (unsigned int i = 0; i < tx.vout.size(); i++) {
          const CTxOut& txout = tx.vout[i];
          UniValue out(UniValue::VOBJ);
          out.push_back(Pair("value", ValueFromAmount(txout.nValue)));
-         out.push_back(Pair("valueSat", txout.nValue));
++        if ( ASSETCHAINS_SYMBOL[0] == 0 && pindex != 0 && tx.nLockTime >= 500000000 && (tipindex= chainActive.LastTip()) != 0 )
++        {
++            int64_t interest; int32_t txheight; uint32_t locktime;
++            interest = komodo_accrued_interest(&txheight,&locktime,tx.GetHash(),i,0,txout.nValue,(int32_t)tipindex->nHeight);
++            out.push_back(Pair("interest", ValueFromAmount(interest)));
++        }        
+         out.push_back(Pair("valueZat", txout.nValue));
          out.push_back(Pair("n", (int64_t)i));
          UniValue o(UniValue::VOBJ);
          ScriptPubKeyToJSON(txout.scriptPubKey, o, true);
Simple merge
index 2d2d9a606ce0efd8612dcd50a47e685ba20748df,81e2969d5c4b0246e083d494ea08ede2d210d1b5..e5bb0c42762e8e5b24d7baadf6c071426b429e69
@@@ -506,10 -501,16 +501,16 @@@ UniValue dumpwallet_impl(const UniValue
      std::sort(vKeyBirth.begin(), vKeyBirth.end());
  
      // produce output
 -    file << strprintf("# Wallet dump created by Zcash %s (%s)\n", CLIENT_BUILD, CLIENT_DATE);
 +    file << strprintf("# Wallet dump created by Komodo %s (%s)\n", CLIENT_BUILD, CLIENT_DATE);
      file << strprintf("# * Created on %s\n", EncodeDumpTime(GetTime()));
-     file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.LastTip()->GetBlockHash().ToString());
-     file << strprintf("#   mined on %s\n", EncodeDumpTime(chainActive.LastTip()->GetBlockTime()));
+     file << strprintf("# * Best block at time of backup was %i (%s),\n", chainActive.Height(), chainActive.Tip()->GetBlockHash().ToString());
+     file << strprintf("#   mined on %s\n", EncodeDumpTime(chainActive.Tip()->GetBlockTime()));
+     {
+         HDSeed hdSeed;
+         pwalletMain->GetHDSeed(hdSeed);
+         file << strprintf("# HDSeed=%s fingerprint=%s", pwalletMain->GetHDChain().seedFp.GetHex(), hdSeed.Fingerprint().GetHex());
+         file << "\n";
+     }
      file << "\n";
      for (std::vector<std::pair<int64_t, CKeyID> >::const_iterator it = vKeyBirth.begin(); it != vKeyBirth.end(); it++) {
          const CKeyID &keyid = it->second;
Simple merge
Simple merge
index c254f98ea5c5ddd9e8661f011fdf83443e2ca497,50acb23947933a738b1e03dc7869bb3c86bedac1..17d4b8d97795f035c83d87e8d90bbb74bbfc2bc8
@@@ -1387,46 -1376,38 +1387,80 @@@ public
      boost::optional<libzcash::SpendingKey> operator()(const libzcash::InvalidEncoding& no) const;
  };
  
 +class GetPubKeyForPubKey : public boost::static_visitor<CPubKey> {
 +private:
 +    const CKeyStore &keystore;
 +
 +public:
 +    GetPubKeyForPubKey(const CKeyStore &keystoreIn) : keystore(keystoreIn) {}
 +
 +    CPubKey operator()(const CKeyID &id) const {
 +        return CPubKey();
 +    }
 +
 +    CPubKey operator()(const CPubKey &key) const {
 +        return key;
 +    }
 +
 +    CPubKey operator()(const CScriptID &sid) const {
 +        return CPubKey();
 +    }
 +
 +    CPubKey operator()(const CNoDestination &no) const {
 +        return CPubKey();
 +    }
 +};
 +
 +class AddressVisitorString : public boost::static_visitor<std::string>
 +{
 +public:
 +    std::string operator()(const CNoDestination &dest) const { return ""; }
 +
 +    std::string operator()(const CKeyID &keyID) const {
 +        return "key hash: " + keyID.ToString();
 +    }
 +
 +    std::string operator()(const CPubKey &key) const {
 +        return "public key: " + HexStr(key);
 +    }
 +
 +    std::string operator()(const CScriptID &scriptID) const {
 +        return "script hash: " + scriptID.ToString();
 +    }
 +};
 +
+ enum SpendingKeyAddResult {
+     KeyAlreadyExists,
+     KeyAdded,
+     KeyNotAdded,
+ };
+ class AddSpendingKeyToWallet : public boost::static_visitor<SpendingKeyAddResult>
+ {
+ private:
+     CWallet *m_wallet;
+     const Consensus::Params &params;
+     int64_t nTime;
+     boost::optional<std::string> hdKeypath; // currently sapling only
+     boost::optional<std::string> seedFpStr; // currently sapling only
+     bool log;
+ public: 
+     AddSpendingKeyToWallet(CWallet *wallet, const Consensus::Params &params) :
+         m_wallet(wallet), params(params), nTime(1), hdKeypath(boost::none), seedFpStr(boost::none), log(false) {}
+     AddSpendingKeyToWallet(
+         CWallet *wallet,
+         const Consensus::Params &params,
+         int64_t _nTime,
+         boost::optional<std::string> _hdKeypath,
+         boost::optional<std::string> _seedFp,
+         bool _log
+     ) : m_wallet(wallet), params(params), nTime(_nTime), hdKeypath(_hdKeypath), seedFpStr(_seedFp), log(_log) {}
+     SpendingKeyAddResult operator()(const libzcash::SproutSpendingKey &sk) const;
+     SpendingKeyAddResult operator()(const libzcash::SaplingExtendedSpendingKey &sk) const;
+     SpendingKeyAddResult operator()(const libzcash::InvalidEncoding& no) const;    
+ };
  #endif // BITCOIN_WALLET_WALLET_H
This page took 0.115656 seconds and 4 git commands to generate.