@ -112,7 +112,6 @@ void WalletTxToJSON(const CWalletTx& wtx, UniValue& entry)
entry . push_back ( Pair ( " blocktime " , mapBlockIndex [ wtx . hashBlock ] - > GetBlockTime ( ) ) ) ;
entry . push_back ( Pair ( " expiryheight " , ( int64_t ) wtx . nExpiryHeight ) ) ;
} else entry . push_back ( Pair ( " confirmations " , confirms ) ) ;
uint256 hash = wtx . GetHash ( ) ;
entry . push_back ( Pair ( " txid " , hash . GetHex ( ) ) ) ;
UniValue conflicts ( UniValue : : VARR ) ;
@ -1769,6 +1768,7 @@ void ListTransactions(const CWalletTx& wtx, const string& strAccount, int nMinDe
BOOST_FOREACH ( const COutputEntry & r , listReceived )
{
string account ;
//fprintf(stderr,"recv iter %s\n",wtx.GetHash().GetHex().c_str());
if ( pwalletMain - > mapAddressBook . count ( r . destination ) )
account = pwalletMain - > mapAddressBook [ r . destination ] . name ;
if ( fAllAccounts | | ( account = = strAccount ) )
@ -1919,7 +1919,10 @@ UniValue listtransactions(const UniValue& params, bool fHelp)
{
CWalletTx * const pwtx = ( * it ) . second . first ;
if ( pwtx ! = 0 )
{
//fprintf(stderr,"pwtx iter.%d %s\n",(int32_t)pwtx->nOrderPos,pwtx->GetHash().GetHex().c_str());
ListTransactions ( * pwtx , strAccount , 0 , true , ret , filter ) ;
} //else fprintf(stderr,"null pwtx\n");
CAccountingEntry * const pacentry = ( * it ) . second . second ;
if ( pacentry ! = 0 )
AcentryToJSON ( * pacentry , strAccount , ret ) ;
@ -2445,6 +2448,7 @@ UniValue walletlock(const UniValue& params, bool fHelp)
return NullUniValue ;
}
int32_t komodo_acpublic ( uint32_t tiptime ) ;
UniValue encryptwallet ( const UniValue & params , bool fHelp )
{
@ -2452,7 +2456,8 @@ UniValue encryptwallet(const UniValue& params, bool fHelp)
return NullUniValue ;
string enableArg = " developerencryptwallet " ;
auto fEnableWalletEncryption = fExperimentalMode & & GetBoolArg ( " - " + enableArg , false ) ;
int32_t flag = ( komodo_acpublic ( 0 ) | | ASSETCHAINS_SYMBOL [ 0 ] = = 0 ) ;
auto fEnableWalletEncryption = fExperimentalMode & & GetBoolArg ( " - " + enableArg , flag ) ;
std : : string strWalletEncryptionDisabledMsg = " " ;
if ( ! fEnableWalletEncryption ) {
@ -2952,8 +2957,8 @@ UniValue z_listunspent(const UniValue& params, bool fHelp)
" \n Examples \n "
+ HelpExampleCli ( " z_listunspent " , " " )
+ HelpExampleCli ( " z_listunspent " , " 6 9999999 false \" [ \\ \" ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9 \\ \" , \\ \" ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf \\ \" ] \" " )
+ HelpExampleRpc ( " z_listunspent " , " 6 9999999 false \" [ \\ \" ztbx5DLDxa5ZLFTchHhoPNkKs57QzSyib6UqXpEdy76T1aUdFxJt1w9318Z8DJ73XzbnWHKEZP9Yjg712N5kMmP4QzS9iC9 \\ \" , \\ \" ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf \\ \" ] \" " )
+ HelpExampleCli ( " z_listunspent " , " 6 9999999 false \" [ \\ \" zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37 \\ \" , \\ \" zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37 \\ \" ] \" " )
+ HelpExampleRpc ( " z_listunspent " , " 6 9999999 false \" [ \\ \" zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37 \\ \" , \\ \" zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37 \\ \" ] \" " )
) ;
RPCTypeCheck ( params , boost : : assign : : list_of ( UniValue : : VNUM ) ( UniValue : : VNUM ) ( UniValue : : VBOOL ) ( UniValue : : VARR ) ) ;
@ -3789,8 +3794,8 @@ UniValue z_listreceivedbyaddress(const UniValue& params, bool fHelp)
" \" change \" : true|false, (boolean) true if the address that received the note is also one of the sending addresses \n "
" } \n "
" \n Examples: \n "
+ HelpExampleCli ( " z_listreceivedbyaddress " , " \" ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf \" " )
+ HelpExampleRpc ( " z_listreceivedbyaddress " , " \" ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf \" " )
+ HelpExampleCli ( " z_listreceivedbyaddress " , " \" zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37 \" " )
+ HelpExampleRpc ( " z_listreceivedbyaddress " , " \" zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37 \" " )
) ;
LOCK2 ( cs_main , pwalletMain - > cs_wallet ) ;
@ -4122,8 +4127,8 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
" \n Result: \n "
" \" operationid \" (string) An operationid to pass to z_getoperationstatus to get the result of the operation. \n "
" \n Examples: \n "
+ HelpExampleCli ( " z_sendmany " , " \" RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV \" '[{ \" address \" : \" ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf \" , \" amount \" : 5.0}]' " )
+ HelpExampleRpc ( " z_sendmany " , " \" RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV \" , [{ \" address \" : \" ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf \" , \" amount \" : 5.0}] " )
+ HelpExampleCli ( " z_sendmany " , " \" RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV \" '[{ \" address \" : \" zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37 \" , \" amount \" : 5.0}]' " )
+ HelpExampleRpc ( " z_sendmany " , " \" RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV \" , [{ \" address \" : \" zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37 \" , \" amount \" : 5.0}] " )
) ;
LOCK2 ( cs_main , pwalletMain - > cs_wallet ) ;
@ -4213,15 +4218,10 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
}
// If we are sending from a shielded address, all recipient
// shielded addresses must be of the same type.
if ( fromSprout & & toSapling ) {
throw JSONRPCError (
RPC_INVALID_PARAMETER ,
" Cannot send from a Sprout address to a Sapling address using z_sendmany " ) ;
}
if ( fromSapling & & toSprout ) {
if ( ( fromSprout & & toSapling ) | | ( fromSapling & & toSprout ) ) {
throw JSONRPCError (
RPC_INVALID_PARAMETER ,
" Cannot send from a Sapling address to a Sprout addres s using z_sendmany " ) ;
" Cannot send between Sprout and Sapling addresses using z_sendmany " ) ;
}
} else {
throw JSONRPCError ( RPC_INVALID_PARAMETER , string ( " Invalid parameter, unknown address format: " ) + address ) ;
@ -4433,8 +4433,8 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp)
" \" opid \" : xxx (string) An operationid to pass to z_getoperationstatus to get the result of the operation. \n "
" } \n "
" \n Examples: \n "
+ HelpExampleCli ( " z_shieldcoinbase " , " \" RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV \" \" ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf \" " )
+ HelpExampleRpc ( " z_shieldcoinbase " , " \" RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV \" , \" ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf \" " )
+ HelpExampleCli ( " z_shieldcoinbase " , " \" RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV \" \" zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37 \" " )
+ HelpExampleRpc ( " z_shieldcoinbase " , " \" RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV \" , \" zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37 \" " )
) ;
LOCK2 ( cs_main , pwalletMain - > cs_wallet ) ;
@ -4622,11 +4622,13 @@ UniValue z_shieldcoinbase(const UniValue& params, bool fHelp)
return o ;
}
# define MERGE_TO_ADDRESS_DEFAULT_TRANSPARENT_LIMIT 50
# define MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT 10
# define MERGE_TO_ADDRESS_DEFAULT_SPROUT_LIMIT 10
# define MERGE_TO_ADDRESS_DEFAULT_SAPLING_LIMIT 90
# define JOINSPLIT_SIZE GetSerializeSize(JSDescription(), SER_NETWORK, PROTOCOL_VERSION)
# define OUTPUTDESCRIPTION_SIZE GetSerializeSize(OutputDescription(), SER_NETWORK, PROTOCOL_VERSION)
# define SPENDDESCRIPTION_SIZE GetSerializeSize(SpendDescription(), SER_NETWORK, PROTOCOL_VERSION)
UniValue z_mergetoaddress ( const UniValue & params , bool fHelp )
{
@ -4634,11 +4636,12 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
return NullUniValue ;
string enableArg = " zmergetoaddress " ;
auto fEnableMergeToAddress = fExperimentalMode & & GetBoolArg ( " - " + enableArg , fals e) ;
auto fEnableMergeToAddress = fExperimentalMode & & GetBoolArg ( " - " + enableArg , tru e) ;
std : : string strDisabledMsg = " " ;
if ( ! fEnableMergeToAddress ) {
strDisabledMsg = experimentalDisabledHelpMsg ( " z_mergetoaddress " , enableArg ) ;
}
< < < < < < < HEAD
if ( fHelp | | params . size ( ) < 2 | | params . size ( ) > 7 )
throw runtime_error (
@ -4691,20 +4694,70 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
+ HelpExampleRpc ( " z_mergetoaddress " , " [ \" RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV \" ], \" ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf \" " )
) ;
= = = = = = =
if ( fHelp | | params . size ( ) < 2 | | params . size ( ) > 6 )
throw runtime_error (
" z_mergetoaddress [ \" fromaddress \" , ... ] \" toaddress \" ( fee ) ( transparent_limit ) ( shielded_limit ) ( memo ) \n "
+ strDisabledMsg +
" \n Merge multiple UTXOs and notes into a single UTXO or note. Coinbase UTXOs are ignored; use `z_shieldcoinbase` "
" \n to combine those into a single note. "
" \n \n This is an asynchronous operation, and UTXOs selected for merging will be locked. If there is an error, they "
" \n are unlocked. The RPC call `listlockunspent` can be used to return a list of locked UTXOs. "
" \n \n The number of UTXOs and notes selected for merging can be limited by the caller. If the transparent limit "
" \n parameter is set to zero, and Overwinter is not yet active, the -mempooltxinputlimit option will determine the "
" \n number 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 "
" \n Arguments: \n "
" 1. fromaddresses (string, required) A JSON array with addresses. \n "
" The following special strings are accepted inside the array: \n "
" - \" ANY_TADDR \" : Merge UTXOs from any t-addrs belonging to the wallet. \n "
" - \" ANY_SPROUT \" : Merge notes from any Sprout z-addrs belonging to the wallet. \n "
" - \" ANY_SAPLING \" : Merge notes from any Sapling 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 Sprout or %d Sapling Notes " , MERGE_TO_ADDRESS_DEFAULT_SPROUT_LIMIT , MERGE_TO_ADDRESS_DEFAULT_SAPLING_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 "
" \n Result: \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 "
" \n Examples: \n "
+ HelpExampleCli ( " z_mergetoaddress " , " '[ \" RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV \" ]' ztfaW34Gj9FrnGUEf833ywDVL62NWXBM81u6EQnM6VR45eYnXhwztecW1SjxA7JrmAXKJhxhj3vDNEpVCQoSvVoSpmbhtjf " )
+ HelpExampleRpc ( " z_mergetoaddress " , " [ \" RD6GgnrMpPaTSMn8vai6yiGA7mN4QGPV \" ], \" zs14d8tc0hl9q0vg5l28uec5vk6sk34fkj2n8s7jalvw5fxpy6v39yn4s2ga082lymrkjk0x2nqg37 \" " )
) ;
> > > > > > > e719e666307adb77fb4b79c7737256ea959fe188
if ( ! fEnableMergeToAddress ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Error: z_mergetoaddress is disabled. " ) ;
}
LOCK2 ( cs_main , pwalletMain - > cs_wallet ) ;
bool useAny = false ;
bool useAnyUTXO = false ;
bool useAnyNote = false ;
bool useAnySprout = false ;
bool useAnySapling = false ;
std : : set < CTxDestination > taddrs = { } ;
std : : set < libzcash : : PaymentAddress > 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 array is empty. " ) ;
@ -4713,39 +4766,28 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
std : : set < std : : string > setAddress ;
// Sources
bool containsSaplingZaddrSource = false ;
for ( const UniValue & o : addresses . getValues ( ) ) {
if ( ! o . isStr ( ) )
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Invalid parameter, expected string " ) ;
std : : string address = o . get_str ( ) ;
if ( address = = " * " ) {
useAny = true ;
} else if ( address = = " ANY_TADDR " ) {
if ( address = = " ANY_TADDR " ) {
useAnyUTXO = true ;
} else if ( address = = " ANY_ZADDR " ) {
useAnyNote = true ;
} else if ( address = = " ANY_SPROUT " ) {
useAnySprout = true ;
} else if ( address = = " ANY_SAPLING " ) {
useAnySapling = true ;
} else {
CTxDestination taddr = DecodeDestination ( address ) ;
if ( IsValidDestination ( taddr ) ) {
// Ignore any listed t-addrs if we are using all of them
if ( ! ( useAny | | useAnyUTXO ) ) {
taddrs . insert ( taddr ) ;
}
taddrs . insert ( taddr ) ;
} else {
auto zaddr = DecodePaymentAddress ( address ) ;
if ( IsValidPaymentAddress ( zaddr , branchId ) ) {
// Ignore listed z-addrs if we are using all of them
if ( ! ( useAny | | useAnyNote ) ) {
zaddrs . insert ( zaddr ) ;
}
// Check if z-addr is Sapling
bool isSapling = boost : : get < libzcash : : SaplingPaymentAddress > ( & zaddr ) ! = nullptr ;
containsSaplingZaddrSource | = isSapling ;
if ( IsValidPaymentAddress ( zaddr ) ) {
zaddrs . insert ( zaddr ) ;
} else {
throw JSONRPCError (
RPC_INVALID_PARAMETER ,
string ( " Invalid parameter, unknown address format: " ) + address ) ;
throw JSONRPCError ( RPC_INVALID_PARAMETER , string ( " Unknown address format: " ) + address ) ;
}
}
}
@ -4755,28 +4797,38 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
setAddress . insert ( address ) ;
}
if ( useAnyUTXO & & taddrs . size ( ) > 0 ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Cannot specify specific t-addrs when using \" ANY_TADDR \" " ) ;
}
if ( ( useAnySprout | | useAnySapling ) & & zaddrs . size ( ) > 0 ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Cannot specify specific z-addrs when using \" ANY_SPROUT \" or \" ANY_SAPLING \" " ) ;
}
const int nextBlockHeight = chainActive . Height ( ) + 1 ;
const bool overwinterActive = NetworkUpgradeActive ( nextBlockHeight , Params ( ) . GetConsensus ( ) , Consensus : : UPGRADE_OVERWINTER ) ;
const bool saplingActive = NetworkUpgradeActive ( nextBlockHeight , Params ( ) . GetConsensus ( ) , Consensus : : UPGRADE_SAPLING ) ;
// Validate the destination address
auto destaddress = params [ 1 ] . get_str ( ) ;
bool isToZaddr = false ;
bool isToSprout Zaddr = 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 < libzcash : : SaplingPaymentAddress > ( & res ) ! = nullptr ;
auto decodeAddr = DecodePaymentAddress ( destaddress ) ;
if ( IsValidPaymentAddress ( decodeAddr ) ) {
if ( boost : : get < libzcash : : SaplingPaymentAddress > ( & decodeAddr ) ! = nullptr ) {
isToSaplingZaddr = true ;
// If Sapling is not active, do not allow sending to a sapling addresses.
if ( ! saplingActive ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Invalid parameter, Sapling has not activated " ) ;
}
} else {
throw JSONRPCError ( RPC_INVALID_PARAMETER , string ( " Invalid parameter, unknown address format: " ) + destaddress ) ;
isToSproutZaddr = true ;
}
} 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 ;
@ -4796,12 +4848,15 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
}
}
int nNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SHIELDED_LIMIT ;
int sproutNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SPROUT_LIMIT ;
int saplingNoteLimit = MERGE_TO_ADDRESS_DEFAULT_SAPLING_LIMIT ;
if ( params . size ( ) > 4 ) {
nNoteLimit = params [ 4 ] . get_int ( ) ;
int nNoteLimit = params [ 4 ] . get_int ( ) ;
if ( nNoteLimit < 0 ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Limit on maximum number of notes cannot be negative " ) ;
}
sproutNoteLimit = nNoteLimit ;
saplingNoteLimit = nNoteLimit ;
}
CAmount maximum_utxo_size ;
@ -4817,7 +4872,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
std : : string memo ;
if ( params . size ( ) > 6 ) {
memo = params [ 6 ] . get_str ( ) ;
if ( ! isToZaddr ) {
if ( ! ( isToSprout Zaddr | | isToSaplingZaddr ) ) {
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. " ) ;
@ -4829,50 +4884,29 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
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 < MergeToAddressInputUTXO > utxoInputs ;
std : : vector < MergeToAddressInputNote > noteInputs ;
std : : vector < MergeToAddressInputSproutNote > sproutNoteInputs ;
std : : vector < MergeToAddressInputSaplingNote > saplingNoteInputs ;
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 ) ) ;
unsigned int max_tx_size = saplingActive ? MAX_TX_SIZE_AFTER_SAPLING : MAX_TX_SIZE_BEFORE_SAPLING ;
size_t estimatedTxSize = 200 ; // tx overhead + wiggle room
if ( isToZaddr ) {
if ( isToSproutZaddr ) {
estimatedTxSize + = JOINSPLIT_SIZE ;
} else if ( isToSaplingZaddr ) {
estimatedTxSize + = OUTPUTDESCRIPTION_SIZE ;
}
if ( useAny | | useAny UTXO | | taddrs . size ( ) > 0 ) {
if ( useAnyUTXO | | taddrs . size ( ) > 0 ) {
// Get available utxos
vector < COutput > vecOutputs ;
pwalletMain - > AvailableCoins ( vecOutputs , true , NULL , false , false ) ;
@ -4883,8 +4917,10 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
continue ;
}
CScript scriptPubKey = out . tx - > vout [ out . i ] . scriptPubKey ;
CTxDestination address ;
if ( ! ExtractDestination ( out . tx - > vout [ out . i ] . scriptPubKey , address ) ) {
if ( ! ExtractDestination ( scriptPubKey , address ) ) {
continue ;
}
// If taddr is not wildcard "*", filter utxos
@ -4915,7 +4951,7 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
} else {
estimatedTxSize + = increase ;
COutPoint utxo ( out . tx - > GetHash ( ) , out . i ) ;
utxoInputs . emplace_back ( utxo , nValue ) ;
utxoInputs . emplace_back ( utxo , nValue , scriptPubKey ) ;
mergedUTXOValue + = nValue ;
}
}
@ -4926,23 +4962,40 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
}
}
if ( useAny | | useAnyNote | | zaddrs . size ( ) > 0 ) {
if ( useAnySprout | | useAnySapling | | zaddrs . size ( ) > 0 ) {
// Get available notes
std : : vector < CSproutNotePlaintextEntry > sproutEntries ;
std : : vector < SaplingNoteEntry > saplingEntries ;
pwalletMain - > GetFilteredNotes ( sproutEntries , saplingEntries , zaddrs ) ;
// If Sapling is not active, do not allow sending from a sapling addresses.
if ( ! saplingActive & & saplingEntries . size ( ) > 0 ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Invalid parameter, Sapling has not activated " ) ;
}
// Sending from both Sprout and Sapling is currently unsupported using z_mergetoaddress
if ( sproutEntries . size ( ) > 0 & & saplingEntries . size ( ) > 0 ) {
throw JSONRPCError (
RPC_INVALID_PARAMETER ,
" Cannot send from both Sprout and Sapling addresses using z_mergetoaddress " ) ;
}
// If sending between shielded addresses, they must be the same type
if ( ( saplingEntries . size ( ) > 0 & & isToSproutZaddr ) | | ( sproutEntries . size ( ) > 0 & & isToSaplingZaddr ) ) {
throw JSONRPCError (
RPC_INVALID_PARAMETER ,
" Cannot send between Sprout and Sapling addresses using z_mergetoaddress " ) ;
}
// Find unspent notes and update estimated size
for ( CSproutNotePlaintextEntry & entry : sproutEntries ) {
for ( const 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 ;
size_t increase = ( sproutN oteInputs. empty ( ) & & ! isToSprout Zaddr ) | | ( sproutN oteInputs. size ( ) % 2 = = 0 ) ? JOINSPLIT_SIZE : 0 ;
if ( estimatedTxSize + increase > = max_tx_size | |
( nNoteLimit > 0 & & noteCounter > nNoteLimit ) )
( sprout NoteLimit > 0 & & noteCounter > sprout NoteLimit) )
{
maxedOutNotesFlag = true ;
} else {
@ -4950,7 +5003,32 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
auto zaddr = entry . address ;
SproutSpendingKey zkey ;
pwalletMain - > GetSproutSpendingKey ( zaddr , zkey ) ;
noteInputs . emplace_back ( entry . jsop , entry . plaintext . note ( zaddr ) , nValue , zkey ) ;
sproutNoteInputs . emplace_back ( entry . jsop , entry . plaintext . note ( zaddr ) , nValue , zkey ) ;
mergedNoteValue + = nValue ;
}
}
if ( maxedOutNotesFlag ) {
remainingNoteValue + = nValue ;
}
}
for ( const SaplingNoteEntry & entry : saplingEntries ) {
noteCounter + + ;
CAmount nValue = entry . note . value ( ) ;
if ( ! maxedOutNotesFlag ) {
size_t increase = SPENDDESCRIPTION_SIZE ;
if ( estimatedTxSize + increase > = max_tx_size | |
( saplingNoteLimit > 0 & & noteCounter > saplingNoteLimit ) )
{
maxedOutNotesFlag = true ;
} else {
estimatedTxSize + = increase ;
libzcash : : SaplingExtendedSpendingKey extsk ;
if ( ! pwalletMain - > GetSaplingExtendedSpendingKey ( entry . address , extsk ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Could not find spending key for payment address. " ) ;
}
saplingNoteInputs . emplace_back ( entry . op , entry . note , nValue , extsk . expsk ) ;
mergedNoteValue + = nValue ;
}
}
@ -4959,17 +5037,10 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
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
size_t numNotes = sproutNoteInputs . size ( ) + saplingNoteInputs . size ( ) ;
if ( numUtxos < 2 & & numNotes = = 0 ) {
throw JSONRPCError ( RPC_WALLET_INSUFFICIENT_FUNDS , " Could not find any funds to merge. " ) ;
@ -4986,8 +5057,8 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
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 ) ) ) ;
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)
@ -5004,17 +5075,22 @@ UniValue z_mergetoaddress(const UniValue& params, bool fHelp)
// Contextual transaction we will build on
CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction (
Params ( ) . GetConsensus ( ) ,
nextBlockHeight ) ;
bool isShielded = numNotes > 0 | | isToZaddr ;
if ( contextualTx . nVersion = = 1 & & isShielded ) {
Params ( ) . GetConsensus ( ) ,
nextBlockHeight ) ;
bool isSproutS hielded = sproutNoteInputs . size ( ) > 0 | | isToSprout Zaddr ;
if ( contextualTx . nVersion = = 1 & & isSproutS hielded ) {
contextualTx . nVersion = 2 ; // Tx format should support vjoinsplit
}
// Builder (used if Sapling addresses are involved)
boost : : optional < TransactionBuilder > builder ;
if ( isToSaplingZaddr | | saplingNoteInputs . size ( ) > 0 ) {
builder = TransactionBuilder ( Params ( ) . GetConsensus ( ) , nextBlockHeight , pwalletMain ) ;
}
// Create operation and add to global queue
std : : shared_ptr < AsyncRPCQueue > q = getAsyncRPCQueue ( ) ;
std : : shared_ptr < AsyncRPCOperation > operation (
new AsyncRPCOperation_mergetoaddress ( contextualTx , utxoInputs , noteInputs , recipient , nFee , contextInfo ) ) ;
new AsyncRPCOperation_mergetoaddress ( builder , contextualTx , utxoInputs , sproutNoteI nputs , saplingN oteInputs, recipient , nFee , contextInfo ) ) ;
q - > addOperation ( operation ) ;
AsyncRPCOperationId operationId = operation - > getId ( ) ;