diff --git a/src/cc/dapps/zmigrate.c b/src/cc/dapps/zmigrate.c new file mode 100644 index 000000000..cbc4b5ad9 --- /dev/null +++ b/src/cc/dapps/zmigrate.c @@ -0,0 +1,656 @@ +/****************************************************************************** + * 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. * + * * + ******************************************************************************/ + +#include +#include +#include +#include +#include "cJSON.c" + +bits256 zeroid; + +char hexbyte(int32_t c) +{ + c &= 0xf; + if ( c < 10 ) + return('0'+c); + else if ( c < 16 ) + return('a'+c-10); + else return(0); +} + +int32_t _unhex(char c) +{ + if ( c >= '0' && c <= '9' ) + return(c - '0'); + else if ( c >= 'a' && c <= 'f' ) + return(c - 'a' + 10); + else if ( c >= 'A' && c <= 'F' ) + return(c - 'A' + 10); + return(-1); +} + +int32_t is_hexstr(char *str,int32_t n) +{ + int32_t i; + if ( str == 0 || str[0] == 0 ) + return(0); + for (i=0; str[i]!=0; i++) + { + if ( n > 0 && i >= n ) + break; + if ( _unhex(str[i]) < 0 ) + break; + } + if ( n == 0 ) + return(i); + return(i == n); +} + +int32_t unhex(char c) +{ + int32_t hex; + if ( (hex= _unhex(c)) < 0 ) + { + //printf("unhex: illegal hexchar.(%c)\n",c); + } + return(hex); +} + +unsigned char _decode_hex(char *hex) { return((unhex(hex[0])<<4) | unhex(hex[1])); } + +int32_t decode_hex(unsigned char *bytes,int32_t n,char *hex) +{ + int32_t adjust,i = 0; + //printf("decode.(%s)\n",hex); + if ( is_hexstr(hex,n) <= 0 ) + { + memset(bytes,0,n); + return(n); + } + if ( hex[n-1] == '\n' || hex[n-1] == '\r' ) + hex[--n] = 0; + if ( hex[n-1] == '\n' || hex[n-1] == '\r' ) + hex[--n] = 0; + if ( n == 0 || (hex[n*2+1] == 0 && hex[n*2] != 0) ) + { + if ( n > 0 ) + { + bytes[0] = unhex(hex[0]); + printf("decode_hex n.%d hex[0] (%c) -> %d hex.(%s) [n*2+1: %d] [n*2: %d %c] len.%ld\n",n,hex[0],bytes[0],hex,hex[n*2+1],hex[n*2],hex[n*2],(long)strlen(hex)); + } + bytes++; + hex++; + adjust = 1; + } else adjust = 0; + if ( n > 0 ) + { + for (i=0; i>4) & 0xf); + hexbytes[i*2 + 1] = hexbyte(message[i] & 0xf); + //printf("i.%d (%02x) [%c%c]\n",i,message[i],hexbytes[i*2],hexbytes[i*2+1]); + } + hexbytes[len*2] = 0; + //printf("len.%ld\n",len*2+1); + return((int32_t)len*2+1); +} + +long _stripwhite(char *buf,int accept) +{ + int32_t i,j,c; + if ( buf == 0 || buf[0] == 0 ) + return(0); + for (i=j=0; buf[i]!=0; i++) + { + buf[j] = c = buf[i]; + if ( c == accept || (c != ' ' && c != '\n' && c != '\r' && c != '\t' && c != '\b') ) + j++; + } + buf[j] = 0; + return(j); +} + +char *clonestr(char *str) +{ + char *clone; + if ( str == 0 || str[0]==0) + { + printf("warning cloning nullstr.%p\n",str); + //#ifdef __APPLE__ + // while ( 1 ) sleep(1); + //#endif + str = (char *)""; + } + clone = (char *)malloc(strlen(str)+16); + strcpy(clone,str); + return(clone); +} + +int32_t safecopy(char *dest,char *src,long len) +{ + int32_t i = -1; + if ( src != 0 && dest != 0 && src != dest ) + { + if ( dest != 0 ) + memset(dest,0,len); + for (i=0; i0; i--) + str[i] = str[i-1]; + str[0] = '/'; + str[n+1] = 0; + }*/ +#endif + return(str); +#endif +} + +void *loadfile(char *fname,uint8_t **bufp,long *lenp,long *allocsizep) +{ + FILE *fp; + long filesize,buflen = *allocsizep; + uint8_t *buf = *bufp; + *lenp = 0; + if ( (fp= fopen(portable_path(fname),"rb")) != 0 ) + { + fseek(fp,0,SEEK_END); + filesize = ftell(fp); + if ( filesize == 0 ) + { + fclose(fp); + *lenp = 0; + printf("loadfile null size.(%s)\n",fname); + return(0); + } + if ( filesize > buflen ) + { + *allocsizep = filesize; + *bufp = buf = (uint8_t *)realloc(buf,(long)*allocsizep+64); + } + rewind(fp); + if ( buf == 0 ) + printf("Null buf ???\n"); + else + { + if ( fread(buf,1,(long)filesize,fp) != (unsigned long)filesize ) + printf("error reading filesize.%ld\n",(long)filesize); + buf[filesize] = 0; + } + fclose(fp); + *lenp = filesize; + //printf("loaded.(%s)\n",buf); + } //else printf("OS_loadfile couldnt load.(%s)\n",fname); + return(buf); +} + +void *filestr(long *allocsizep,char *_fname) +{ + long filesize = 0; char *fname,*buf = 0; void *retptr; + *allocsizep = 0; + fname = malloc(strlen(_fname)+1); + strcpy(fname,_fname); + retptr = loadfile(fname,(uint8_t **)&buf,&filesize,allocsizep); + free(fname); + return(retptr); +} + +char *send_curl(char *url,char *fname) +{ + long fsize; char curlstr[1024]; + sprintf(curlstr,"curl --url \"%s\" > %s",url,fname); + system(curlstr); + return(filestr(&fsize,fname)); +} + +cJSON *get_urljson(char *url,char *fname) +{ + char *jsonstr; cJSON *json = 0; + if ( (jsonstr= send_curl(url,fname)) != 0 ) + { + //printf("(%s) -> (%s)\n",url,jsonstr); + json = cJSON_Parse(jsonstr); + free(jsonstr); + } + return(json); +} + +////////////////////////////////////////////// +// start of dapp +////////////////////////////////////////////// + +char *REFCOIN_CLI; + +cJSON *get_komodocli(char *refcoin,char **retstrp,char *acname,char *method,char *arg0,char *arg1,char *arg2,char *arg3) +{ + long fsize; cJSON *retjson = 0; char cmdstr[32768],*jsonstr,fname[256]; + sprintf(fname,"/tmp/zmigrate.%s",method); + if ( acname[0] != 0 ) + { + if ( refcoin[0] != 0 && strcmp(refcoin,"KMD") != 0 ) + printf("unexpected: refcoin.(%s) acname.(%s)\n",refcoin,acname); + sprintf(cmdstr,"./komodo-cli -ac_name=%s %s %s %s %s %s > %s\n",acname,method,arg0,arg1,arg2,arg3,fname); + } + else if ( strcmp(refcoin,"KMD") == 0 ) + sprintf(cmdstr,"./komodo-cli %s %s %s %s %s > %s\n",method,arg0,arg1,arg2,arg3,fname); + else if ( REFCOIN_CLI != 0 && REFCOIN_CLI[0] != 0 ) + { + sprintf(cmdstr,"%s %s %s %s %s %s > %s\n",REFCOIN_CLI,method,arg0,arg1,arg2,arg3,fname); + printf("ref.(%s) REFCOIN_CLI (%s)\n",refcoin,cmdstr); + } + system(cmdstr); + *retstrp = 0; + if ( (jsonstr= filestr(&fsize,fname)) != 0 ) + { + jsonstr[strlen(jsonstr)-1]='\0'; + //fprintf(stderr,"%s -> jsonstr.(%s)\n",cmdstr,jsonstr); + if ( (jsonstr[0] != '{' && jsonstr[0] != '[') || (retjson= cJSON_Parse(jsonstr)) == 0 ) + *retstrp = jsonstr; + else free(jsonstr); + } + return(retjson); +} + +bits256 komodobroadcast(char *refcoin,char *acname,cJSON *hexjson) +{ + char *hexstr,*retstr,str[65]; cJSON *retjson; bits256 txid; + memset(txid.bytes,0,sizeof(txid)); + if ( (hexstr= jstr(hexjson,"hex")) != 0 ) + { + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"sendrawtransaction",hexstr,"","","")) != 0 ) + { + //fprintf(stderr,"broadcast.(%s)\n",jprint(retjson,0)); + free_json(retjson); + } + else if ( retstr != 0 ) + { + if ( strlen(retstr) >= 64 ) + { + retstr[64] = 0; + decode_hex(txid.bytes,32,retstr); + } + fprintf(stderr,"broadcast %s txid.(%s)\n",strlen(acname)>0?acname:refcoin,bits256_str(str,txid)); + free(retstr); + } + } + return(txid); +} + +bits256 sendtoaddress(char *refcoin,char *acname,char *destaddr,int64_t satoshis) +{ + char numstr[32],*retstr,str[65]; cJSON *retjson; bits256 txid; + memset(txid.bytes,0,sizeof(txid)); + sprintf(numstr,"%.8f",(double)satoshis/SATOSHIDEN); + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"sendtoaddress",destaddr,numstr,"","")) != 0 ) + { + fprintf(stderr,"unexpected sendrawtransaction json.(%s)\n",jprint(retjson,0)); + free_json(retjson); + } + else if ( retstr != 0 ) + { + if ( strlen(retstr) >= 64 ) + { + retstr[64] = 0; + decode_hex(txid.bytes,32,retstr); + } + fprintf(stderr,"sendtoaddress %s %.8f txid.(%s)\n",destaddr,(double)satoshis/SATOSHIDEN,bits256_str(str,txid)); + free(retstr); + } + return(txid); +} + +int32_t get_coinheight(char *refcoin,char *acname) +{ + cJSON *retjson; char *retstr; int32_t height=0; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getblockchaininfo","","","","")) != 0 ) + { + height = jint(retjson,"blocks"); + free_json(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"%s get_coinheight.(%s) error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return(height); +} + +bits256 get_coinblockhash(char *refcoin,char *acname,int32_t height) +{ + cJSON *retjson; char *retstr,heightstr[32]; bits256 hash; + memset(hash.bytes,0,sizeof(hash)); + sprintf(heightstr,"%d",height); + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getblockhash",heightstr,"","","")) != 0 ) + { + fprintf(stderr,"unexpected blockhash json.(%s)\n",jprint(retjson,0)); + free_json(retjson); + } + else if ( retstr != 0 ) + { + if ( strlen(retstr) >= 64 ) + { + retstr[64] = 0; + decode_hex(hash.bytes,32,retstr); + } + free(retstr); + } + return(hash); +} + +bits256 get_coinmerkleroot(char *refcoin,char *acname,bits256 blockhash) +{ + cJSON *retjson; char *retstr,str[65]; bits256 merkleroot; + memset(merkleroot.bytes,0,sizeof(merkleroot)); + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getblockheader",bits256_str(str,blockhash),"","","")) != 0 ) + { + merkleroot = jbits256(retjson,"merkleroot"); + //fprintf(stderr,"got merkleroot.(%s)\n",bits256_str(str,merkleroot)); + free_json(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"%s %s get_coinmerkleroot error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return(merkleroot); +} + +int32_t get_coinheader(char *refcoin,char *acname,bits256 *blockhashp,bits256 *merklerootp,int32_t prevheight) +{ + int32_t height = 0; char str[65]; + if ( prevheight == 0 ) + height = get_coinheight(refcoin,acname) - 20; + else height = prevheight + 1; + if ( height > 0 ) + { + *blockhashp = get_coinblockhash(refcoin,acname,height); + if ( bits256_nonz(*blockhashp) != 0 ) + { + *merklerootp = get_coinmerkleroot(refcoin,acname,*blockhashp); + if ( bits256_nonz(*merklerootp) != 0 ) + return(height); + } + } + memset(blockhashp,0,sizeof(*blockhashp)); + memset(merklerootp,0,sizeof(*merklerootp)); + return(0); +} + +cJSON *get_rawmempool(char *refcoin,char *acname) +{ + cJSON *retjson; char *retstr; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getrawmempool","","","","")) != 0 ) + { + //printf("mempool.(%s)\n",jprint(retjson,0)); + return(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"get_rawmempool.(%s) error.(%s)\n",acname,retstr); + free(retstr); + } + return(0); +} + +cJSON *get_addressutxos(char *refcoin,char *acname,char *coinaddr) +{ + cJSON *retjson; char *retstr,jsonbuf[256]; + if ( refcoin[0] != 0 && strcmp(refcoin,"KMD") != 0 ) + printf("warning: assumes %s has addressindex enabled\n",refcoin); + sprintf(jsonbuf,"{\\\"addresses\\\":[\\\"%s\\\"]}",coinaddr); + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getaddressutxos",jsonbuf,"","","")) != 0 ) + { + //printf("addressutxos.(%s)\n",jprint(retjson,0)); + return(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"get_addressutxos.(%s) error.(%s)\n",acname,retstr); + free(retstr); + } + return(0); +} + +cJSON *get_rawtransaction(char *refcoin,char *acname,bits256 txid) +{ + cJSON *retjson; char *retstr,str[65]; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"getrawtransaction",bits256_str(str,txid),"1","","")) != 0 ) + { + return(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"get_rawtransaction.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return(0); +} + +int32_t validateaddress(char *refcoin,char *acname,char *depositaddr, char* compare) +{ + cJSON *retjson; char *retstr; int32_t res=0; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"validateaddress",depositaddr,"","","")) != 0 ) + { + if (is_cJSON_True(jobj(retjson,compare)) != 0 ) res=1; + free_json(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"validateaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return (res); +} + +int32_t z_validateaddress(char *refcoin,char *acname,char *depositaddr, char *compare) +{ + cJSON *retjson; char *retstr; int32_t res=0; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"z_validateaddress",depositaddr,"","","")) != 0 ) + { + if (is_cJSON_True(jobj(retjson,compare)) != 0 ) + res=1; + free_json(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"z_validateaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } + return (res); +} + +void importaddress(char *refcoin,char *acname,char *depositaddr) +{ + cJSON *retjson; char *retstr; + if ( (retjson= get_komodocli(refcoin,&retstr,acname,"importaddress",depositaddr,"","true","")) != 0 ) + { + printf("importaddress.(%s)\n",jprint(retjson,0)); + free_json(retjson); + } + else if ( retstr != 0 ) + { + fprintf(stderr,"importaddress.(%s) %s error.(%s)\n",refcoin,acname,retstr); + free(retstr); + } +} + +cJSON *getinputarray(int64_t *totalp,cJSON *unspents,int64_t required) +{ + cJSON *vin,*item,*vins = cJSON_CreateArray(); int32_t i,n,v; int64_t satoshis; bits256 txid; + *totalp = 0; + if ( (n= cJSON_GetArraySize(unspents)) > 0 ) + { + for (i=0; i= required ) + break; + } + } + } + return(vins); +} + +int32_t tx_has_voutaddress(char *refcoin,char *acname,bits256 txid,char *coinaddr) +{ + cJSON *txobj,*vouts,*vout,*vins,*vin,*sobj,*addresses; char *addr,str[65]; int32_t i,j,n,numarray,retval = 0, hasvout=0; + if ( (txobj= get_rawtransaction(refcoin,acname,txid)) != 0 ) + { + if ( (vouts= jarray(&numarray,txobj,"vout")) != 0 ) + { + for (i=0; i 6) - throw runtime_error( - "z_migrate [\"fromaddress\", ... ] \"toaddress\" ( fee ) ( transparent_limit ) ( shielded_limit ) ( memo )\n" - + strDisabledMsg + - "\nMerge multiple UTXOs and notes into a single UTXO or note. Coinbase UTXOs are ignored; use `z_shieldcoinbase`" - "\nto combine those into a single note." - "\n\nThis is an asynchronous operation, and UTXOs selected for merging will be locked. If there is an error, they" - "\nare unlocked. The RPC call `listlockunspent` can be used to return a list of locked UTXOs." - "\n\nThe number of UTXOs and notes selected for merging can be limited by the caller. If the transparent limit" - "\nparameter is set to zero, and Overwinter is not yet active, the -mempooltxinputlimit option will determine the" - "\nnumber of UTXOs. Any limit is constrained by the consensus rule defining a maximum transaction size of" - + strprintf("\n%d bytes before Sapling, and %d bytes once Sapling activates.", MAX_TX_SIZE_BEFORE_SAPLING, MAX_TX_SIZE_AFTER_SAPLING) - + HelpRequiringPassphrase() + "\n" - "\nArguments:\n" - "1. fromaddresses (string, required) A JSON array with addresses.\n" - " The following special strings are accepted inside the array:\n" - " - \"*\": Merge both UTXOs and notes from all addresses belonging to the wallet.\n" - " - \"ANY_TADDR\": Merge UTXOs from all t-addrs belonging to the wallet.\n" - " - \"ANY_ZADDR\": Merge notes from all z-addrs belonging to the wallet.\n" - " If a special string is given, any given addresses of that type will be ignored.\n" - " [\n" - " \"address\" (string) Can be a t-addr or a z-addr\n" - " ,...\n" - " ]\n" - "2. \"toaddress\" (string, required) The t-addr or z-addr to send the funds to.\n" - "3. fee (numeric, optional, default=" - + strprintf("%s", FormatMoney(MERGE_TO_ADDRESS_OPERATION_DEFAULT_MINERS_FEE)) + ") The fee amount to attach to this transaction.\n" - "4. transparent_limit (numeric, optional, default=" - + strprintf("%d", MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT) + ") Limit on the maximum number of UTXOs to merge. Set to 0 to use node option -mempooltxinputlimit (before Overwinter), or as many as will fit in the transaction (after Overwinter).\n" - "4. shielded_limit (numeric, optional, default=" - + strprintf("%d", MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT) + ") Limit on the maximum number of notes to merge. Set to 0 to merge as many as will fit in the transaction.\n" - "5. \"memo\" (string, optional) Encoded as hex. When toaddress is a z-addr, this will be stored in the memo field of the new note.\n" - "\nResult:\n" - "{\n" - " \"remainingUTXOs\": xxx (numeric) Number of UTXOs still available for merging.\n" - " \"remainingTransparentValue\": xxx (numeric) Value of UTXOs still available for merging.\n" - " \"remainingNotes\": xxx (numeric) Number of notes still available for merging.\n" - " \"remainingShieldedValue\": xxx (numeric) Value of notes still available for merging.\n" - " \"mergingUTXOs\": xxx (numeric) Number of UTXOs being merged.\n" - " \"mergingTransparentValue\": xxx (numeric) Value of UTXOs being merged.\n" - " \"mergingNotes\": xxx (numeric) Number of notes being merged.\n" - " \"mergingShieldedValue\": xxx (numeric) Value of notes being merged.\n" - " \"opid\": xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation.\n" - "}\n" - "\nExamples:\n" - + HelpExampleCli("z_migrate", "'[\"ANY_ZADDR\"]' ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf") - ); - - if (!fEnableMergeToAddress) { - throw JSONRPCError(RPC_WALLET_ERROR, "Error: z_migrate is disabled."); - } - - LOCK2(cs_main, pwalletMain->cs_wallet); - - bool useAny = false; - bool useAnyUTXO = false; - bool useAnyNote = false; - std::set taddrs = {}; - std::set zaddrs = {}; - - uint32_t branchId = CurrentEpochBranchId(chainActive.Height(), Params().GetConsensus()); - - UniValue addresses = params[0].get_array(); - if (addresses.size()!=0) - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, fromaddresses isnt empty."); - - // Keep track of addresses to spot duplicates - std::set setAddress; - - // Sources - bool containsSaplingZaddrSource = false; - useAnyNote = true; - - // Validate the destination address - auto destaddress = params[1].get_str(); - bool isToZaddr = false; - bool isToSaplingZaddr = false; - CTxDestination taddr = DecodeDestination(destaddress); - if (!IsValidDestination(taddr)) { - if (IsValidPaymentAddressString(destaddress, branchId)) { - isToZaddr = true; - - // Is this a Sapling address? - auto res = DecodePaymentAddress(destaddress); - if (IsValidPaymentAddress(res)) { - isToSaplingZaddr = boost::get(&res) != nullptr; - } else { - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); - } - } else { - throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ") + destaddress ); - } - } - //else if ( ASSETCHAINS_PRIVATE != 0 ) - // throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "cant use transparent addresses in private chain"); - - // Convert fee from currency format to zatoshis - CAmount nFee = SHIELD_COINBASE_DEFAULT_MINERS_FEE; - if (params.size() > 2) { - if (params[2].get_real() == 0.0) { - nFee = 0; - } else { - nFee = AmountFromValue( params[2] ); - } - } - - int nUTXOLimit = MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT; - if (params.size() > 3) { - nUTXOLimit = params[3].get_int(); - if (nUTXOLimit < 0) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of UTXOs cannot be negative"); - } - } - - int nNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT; - if (params.size() > 4) { - nNoteLimit = params[4].get_int(); - if (nNoteLimit < 0) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Limit on maximum number of notes cannot be negative"); - } - } - - std::string memo; - if (params.size() > 5) { - memo = params[5].get_str(); - if (!isToZaddr) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo can not be used with a taddr. It can only be used with a zaddr."); - } else if (!IsHex(memo)) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format."); - } - if (memo.length() > ZC_MEMO_SIZE*2) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", ZC_MEMO_SIZE )); - } - } - - MergeToAddressRecipient recipient(destaddress, memo); - - int nextBlockHeight = chainActive.Height() + 1; - bool overwinterActive = NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER); - unsigned int max_tx_size = MAX_TX_SIZE_AFTER_SAPLING; - if (!NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) { - max_tx_size = MAX_TX_SIZE_BEFORE_SAPLING; - } - - // This RPC does not support Sapling yet. - if (isToSaplingZaddr || containsSaplingZaddrSource) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling is not supported yet by z_mergetoadress"); - } - - // If this RPC does support Sapling... - // If Sapling is not active, do not allow sending from or sending to Sapling addresses. - if (!NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) { - if (isToSaplingZaddr || containsSaplingZaddrSource) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, Sapling has not activated"); - } - } - - // Prepare to get UTXOs and notes - std::vector utxoInputs; - std::vector noteInputs; - CAmount mergedUTXOValue = 0; - CAmount mergedNoteValue = 0; - CAmount remainingUTXOValue = 0; - CAmount remainingNoteValue = 0; -#ifdef __LP64__ - uint64_t utxoCounter = 0; - uint64_t noteCounter = 0; -#else - size_t utxoCounter = 0; - size_t noteCounter = 0; -#endif - bool maxedOutUTXOsFlag = false; - bool maxedOutNotesFlag = false; - size_t mempoolLimit = (nUTXOLimit != 0) ? nUTXOLimit : (overwinterActive ? 0 : (size_t)GetArg("-mempooltxinputlimit", 0)); - - size_t estimatedTxSize = 200; // tx overhead + wiggle room - if (isToZaddr) { - estimatedTxSize += JOINSPLIT_SIZE; - } - - if (useAny || useAnyUTXO || taddrs.size() > 0) { - // Get available utxos - vector vecOutputs; - pwalletMain->AvailableCoins(vecOutputs, true, NULL, false, false); - - // Find unspent utxos and update estimated size - for (const COutput& out : vecOutputs) { - if (!out.fSpendable) { - continue; - } - - CTxDestination address; - if (!ExtractDestination(out.tx->vout[out.i].scriptPubKey, address)) { - continue; - } - // If taddr is not wildcard "*", filter utxos - if (taddrs.size() > 0 && !taddrs.count(address)) { - continue; - } - - utxoCounter++; - CAmount nValue = out.tx->vout[out.i].nValue; - - if (!maxedOutUTXOsFlag) { - size_t increase = (boost::get(&address) != nullptr) ? CTXIN_SPEND_P2SH_SIZE : CTXIN_SPEND_DUST_SIZE; - if (estimatedTxSize + increase >= max_tx_size || - (mempoolLimit > 0 && utxoCounter > mempoolLimit)) - { - maxedOutUTXOsFlag = true; - } else { - estimatedTxSize += increase; - COutPoint utxo(out.tx->GetHash(), out.i); - utxoInputs.emplace_back(utxo, nValue); - mergedUTXOValue += nValue; - } - } - - if (maxedOutUTXOsFlag) { - remainingUTXOValue += nValue; - } - } - } - - if (useAny || useAnyNote || zaddrs.size() > 0) { - // Get available notes - std::vector sproutEntries; - std::vector saplingEntries; - pwalletMain->GetFilteredNotes(sproutEntries, saplingEntries, zaddrs); - - // Find unspent notes and update estimated size - for (CSproutNotePlaintextEntry& entry : sproutEntries) { - noteCounter++; - CAmount nValue = entry.plaintext.value(); - - if (!maxedOutNotesFlag) { - // If we haven't added any notes yet and the merge is to a - // z-address, we have already accounted for the first JoinSplit. - size_t increase = (noteInputs.empty() && !isToZaddr) || (noteInputs.size() % 2 == 0) ? JOINSPLIT_SIZE : 0; - if (estimatedTxSize + increase >= max_tx_size || - (nNoteLimit > 0 && noteCounter > nNoteLimit)) - { - maxedOutNotesFlag = true; - } else { - estimatedTxSize += increase; - auto zaddr = entry.address; - SproutSpendingKey zkey; - pwalletMain->GetSproutSpendingKey(zaddr, zkey); - noteInputs.emplace_back(entry.jsop, entry.plaintext.note(zaddr), nValue, zkey); - mergedNoteValue += nValue; - } - } - - if (maxedOutNotesFlag) { - remainingNoteValue += nValue; - } - } - // TODO: Add Sapling support - } - -#ifdef __LP64__ - uint64_t numUtxos = utxoInputs.size(); //ca333 - uint64_t numNotes = noteInputs.size(); -#else - size_t numUtxos = utxoInputs.size(); - size_t numNotes = noteInputs.size(); -#endif - - - if (numUtxos == 0 && numNotes == 0) { - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Could not find any funds to merge."); - } - - // Sanity check: Don't do anything if: - // - We only have one from address - // - It's equal to toaddress - // - The address only contains a single UTXO or note - if (setAddress.size() == 1 && setAddress.count(destaddress) && (numUtxos + numNotes) == 1) { - throw JSONRPCError(RPC_INVALID_PARAMETER, "Destination address is also the only source address, and all its funds are already merged."); - } - - CAmount mergedValue = mergedUTXOValue + mergedNoteValue; - if (mergedValue < nFee) { - throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, - strprintf("Insufficient funds, have %s, which is less than miners fee %s", - FormatMoney(mergedValue), FormatMoney(nFee))); - } - - // Check that the user specified fee is sane (if too high, it can result in error -25 absurd fee) - CAmount netAmount = mergedValue - nFee; - if (nFee > netAmount) { - throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Fee %s is greater than the net amount to be shielded %s", FormatMoney(nFee), FormatMoney(netAmount))); - } - - // Keep record of parameters in context object - UniValue contextInfo(UniValue::VOBJ); - contextInfo.push_back(Pair("fromaddresses", params[0])); - contextInfo.push_back(Pair("toaddress", params[1])); - contextInfo.push_back(Pair("fee", ValueFromAmount(nFee))); - - // Contextual transaction we will build on - CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction( - Params().GetConsensus(), - nextBlockHeight); - bool isShielded = numNotes > 0 || isToZaddr; - if (contextualTx.nVersion == 1 && isShielded) { - contextualTx.nVersion = 2; // Tx format should support vjoinsplit - } - - // Create operation and add to global queue - std::shared_ptr q = getAsyncRPCQueue(); - std::shared_ptr operation( - new AsyncRPCOperation_mergetoaddress(contextualTx, utxoInputs, noteInputs, recipient, nFee, contextInfo) ); - q->addOperation(operation); - AsyncRPCOperationId operationId = operation->getId(); - - // and queue z_sendmany taddr -> sapling 10000 - - // Return continuation information - UniValue o(UniValue::VOBJ); - o.push_back(Pair("remainingUTXOs", static_cast(utxoCounter - numUtxos))); - o.push_back(Pair("remainingTransparentValue", ValueFromAmount(remainingUTXOValue))); - o.push_back(Pair("remainingNotes", static_cast(noteCounter - numNotes))); - o.push_back(Pair("remainingShieldedValue", ValueFromAmount(remainingNoteValue))); - o.push_back(Pair("mergingUTXOs", static_cast(numUtxos))); - o.push_back(Pair("mergingTransparentValue", ValueFromAmount(mergedUTXOValue))); - o.push_back(Pair("mergingNotes", static_cast(numNotes))); - o.push_back(Pair("mergingShieldedValue", ValueFromAmount(mergedNoteValue))); - o.push_back(Pair("opid", operationId)); - return o; -} - UniValue z_listoperationids(const UniValue& params, bool fHelp) { if (!EnsureWalletIsAvailable(fHelp))