@ -42,7 +42,7 @@ int mta_find_output(UniValue obj, int n)
if ( ! outputMapValue . isArray ( ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Missing outputmap for JoinSplit operation " ) ;
}
UniValue outputMap = outputMapValue . get_array ( ) ;
assert ( outputMap . size ( ) = = ZC_NUM_JS_OUTPUTS ) ;
for ( size_t i = 0 ; i < outputMap . size ( ) ; i + + ) {
@ -50,7 +50,7 @@ int mta_find_output(UniValue obj, int n)
return i ;
}
}
throw std : : logic_error ( " n is not present in outputmap " ) ;
}
@ -69,33 +69,33 @@ saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), context
if ( fee < 0 | | fee > MAX_MONEY ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Fee is out of range " ) ;
}
if ( utxoInputs . empty ( ) & & sproutNoteInputs . empty ( ) & & saplingNoteInputs . empty ( ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " No inputs " ) ;
}
if ( std : : get < 0 > ( recipient ) . size ( ) = = 0 ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Recipient parameter missing " ) ;
}
if ( sproutNoteInputs . size ( ) > 0 & & saplingNoteInputs . size ( ) > 0 ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Cannot send from both Sprout and Sapling addresses using z_mergetoaddress " ) ;
}
if ( sproutNoteInputs . size ( ) > 0 & & builder ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Sprout notes are not supported by the TransactionBuilder " ) ;
}
isUsingBuilder_ = false ;
if ( builder ) {
isUsingBuilder_ = true ;
builder_ = builder . get ( ) ;
}
toTaddr_ = DecodeDestination ( std : : get < 0 > ( recipient ) ) ;
isToTaddr_ = IsValidDestination ( toTaddr_ ) ;
isToZaddr_ = false ;
if ( ! isToTaddr_ ) {
auto address = DecodePaymentAddress ( std : : get < 0 > ( recipient ) ) ;
if ( IsValidPaymentAddress ( address ) ) {
@ -105,18 +105,18 @@ saplingNoteInputs_(saplingNoteInputs), recipient_(recipient), fee_(fee), context
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid recipient address " ) ;
}
}
// Log the context info i.e. the call parameters to z_mergetoaddress
if ( LogAcceptCategory ( " zrpcunsafe " ) ) {
LogPrint ( " zrpcunsafe " , " %s: z_mergetoaddress initialized (params=%s) \n " , getId ( ) , contextInfo . write ( ) ) ;
} else {
LogPrint ( " zrpc " , " %s: z_mergetoaddress initialized \n " , getId ( ) ) ;
}
// Lock UTXOs
lock_utxos ( ) ;
lock_notes ( ) ;
// Enable payment disclosure if requested
paymentDisclosureMode = fExperimentalMode & & GetBoolArg ( " -paymentdisclosure " , true ) ;
}
@ -132,12 +132,12 @@ void AsyncRPCOperation_mergetoaddress::main()
unlock_notes ( ) ;
return ;
}
set_state ( OperationStatus : : EXECUTING ) ;
start_execution_clock ( ) ;
bool success = false ;
# ifdef ENABLE_MINING
# ifdef ENABLE_WALLET
GenerateBitcoins ( false , NULL , 0 ) ;
@ -145,7 +145,7 @@ void AsyncRPCOperation_mergetoaddress::main()
GenerateBitcoins ( false , 0 ) ;
# endif
# endif
try {
success = main_impl ( ) ;
} catch ( const UniValue & objError ) {
@ -166,7 +166,7 @@ void AsyncRPCOperation_mergetoaddress::main()
set_error_code ( - 2 ) ;
set_error_message ( " unknown error " ) ;
}
# ifdef ENABLE_MINING
# ifdef ENABLE_WALLET
GenerateBitcoins ( GetBoolArg ( " -gen " , false ) , pwalletMain , GetArg ( " -genproclimit " , 1 ) ) ;
@ -174,15 +174,15 @@ void AsyncRPCOperation_mergetoaddress::main()
GenerateBitcoins ( GetBoolArg ( " -gen " , false ) , GetArg ( " -genproclimit " , 1 ) ) ;
# endif
# endif
stop_execution_clock ( ) ;
if ( success ) {
set_state ( OperationStatus : : SUCCESS ) ;
} else {
set_state ( OperationStatus : : FAILED ) ;
}
std : : string s = strprintf ( " %s: z_mergetoaddress finished (status=%s " , getId ( ) , getStateAsString ( ) ) ;
if ( success ) {
s + = strprintf ( " , txid=%s) \n " , tx_ . GetHash ( ) . ToString ( ) ) ;
@ -190,10 +190,10 @@ void AsyncRPCOperation_mergetoaddress::main()
s + = strprintf ( " , error=%s) \n " , getErrorMessage ( ) ) ;
}
LogPrintf ( " %s " , s ) ;
unlock_utxos ( ) ; // clean up
unlock_notes ( ) ; // clean up
// !!! Payment disclosure START
if ( success & & paymentDisclosureMode & & paymentDisclosureData_ . size ( ) > 0 ) {
uint256 txidhash = tx_ . GetHash ( ) ;
@ -216,12 +216,12 @@ void AsyncRPCOperation_mergetoaddress::main()
bool AsyncRPCOperation_mergetoaddress : : main_impl ( )
{
assert ( isToTaddr_ ! = isToZaddr_ ) ;
bool isPureTaddrOnlyTx = ( sproutNoteInputs_ . empty ( ) & & saplingNoteInputs_ . empty ( ) & & isToTaddr_ ) ;
CAmount minersFee = fee_ ;
size_t numInputs = utxoInputs_ . size ( ) ;
// Check mempooltxinputlimit to avoid creating a transaction which the local mempool rejects
size_t limit = ( size_t ) GetArg ( " -mempooltxinputlimit " , 0 ) ;
{
@ -235,31 +235,31 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
strprintf ( " Number of transparent inputs %d is greater than mempooltxinputlimit of %d " ,
numInputs , limit ) ) ;
}
CAmount t_inputs_total = 0 ;
for ( MergeToAddressInputUTXO & t : utxoInputs_ ) {
t_inputs_total + = std : : get < 1 > ( t ) ;
}
CAmount z_inputs_total = 0 ;
for ( const MergeToAddressInputSproutNote & t : sproutNoteInputs_ ) {
z_inputs_total + = std : : get < 2 > ( t ) ;
}
for ( const MergeToAddressInputSaplingNote & t : saplingNoteInputs_ ) {
z_inputs_total + = std : : get < 2 > ( t ) ;
}
CAmount targetAmount = z_inputs_total + t_inputs_total ;
if ( targetAmount < = minersFee ) {
throw JSONRPCError ( RPC_WALLET_INSUFFICIENT_FUNDS ,
strprintf ( " Insufficient funds, have %s and miners fee is %s " ,
FormatMoney ( targetAmount ) , FormatMoney ( minersFee ) ) ) ;
}
CAmount sendAmount = targetAmount - minersFee ;
// update the transaction with the UTXO inputs and output (if any)
if ( ! isUsingBuilder_ ) {
CMutableTransaction rawTx ( tx_ ) ;
@ -274,7 +274,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
}
tx_ = CTransaction ( rawTx ) ;
}
LogPrint ( isPureTaddrOnlyTx ? " zrpc " : " zrpcunsafe " , " %s: spending %s to send %s with fee %s \n " ,
getId ( ) , FormatMoney ( targetAmount ) , FormatMoney ( sendAmount ) , FormatMoney ( minersFee ) ) ;
LogPrint ( " zrpc " , " %s: transparent input: %s \n " , getId ( ) , FormatMoney ( t_inputs_total ) ) ;
@ -285,13 +285,13 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
LogPrint ( " zrpcunsafe " , " %s: private output: %s \n " , getId ( ) , FormatMoney ( sendAmount ) ) ;
}
LogPrint ( " zrpc " , " %s: fee: %s \n " , getId ( ) , FormatMoney ( minersFee ) ) ;
// Grab the current consensus branch ID
{
LOCK ( cs_main ) ;
consensusBranchId_ = CurrentEpochBranchId ( chainActive . Height ( ) + 1 , Params ( ) . GetConsensus ( ) ) ;
}
/**
* SCENARIO # 0
*
@ -301,15 +301,15 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
*/
if ( isUsingBuilder_ ) {
builder_ . SetFee ( minersFee ) ;
for ( const MergeToAddressInputUTXO & t : utxoInputs_ ) {
COutPoint outPoint = std : : get < 0 > ( t ) ;
CAmount amount = std : : get < 1 > ( t ) ;
CScript scriptPubKey = std : : get < 2 > ( t ) ;
builder_ . AddTransparentInput ( outPoint , scriptPubKey , amount ) ;
}
boost : : optional < uint256 > ovk ;
// Select Sapling notes
std : : vector < SaplingOutPoint > saplingOPs ;
@ -324,7 +324,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
ovk = expsk . full_viewing_key ( ) . ovk ;
}
}
// Fetch Sapling anchor and witnesses
uint256 anchor ;
std : : vector < boost : : optional < SaplingWitness > > witnesses ;
@ -332,7 +332,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
LOCK2 ( cs_main , pwalletMain - > cs_wallet ) ;
pwalletMain - > GetSaplingNoteWitnesses ( saplingOPs , witnesses , anchor ) ;
}
// Add Sapling spends
for ( size_t i = 0 ; i < saplingNotes . size ( ) ; i + + ) {
if ( ! witnesses [ i ] ) {
@ -340,7 +340,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
}
assert ( builder_ . AddSaplingSpend ( expsks [ i ] , saplingNotes [ i ] , anchor , witnesses [ i ] . get ( ) ) ) ;
}
if ( isToTaddr_ ) {
if ( ! builder_ . AddTransparentOutput ( toTaddr_ , sendAmount ) ) {
throw JSONRPCError ( RPC_INVALID_ADDRESS_OR_KEY , " Invalid output address, not a valid taddr. " ) ;
@ -372,15 +372,15 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
}
builder_ . AddSaplingOutput ( ovk . get ( ) , * saplingPaymentAddress , sendAmount , hexMemo ) ;
}
// Build the transaction
auto maybe_tx = builder_ . Build ( ) ;
if ( ! maybe_tx ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Failed to build transaction. " ) ;
}
tx_ = maybe_tx . get ( ) ;
// Send the transaction
// TODO: Use CWallet::CommitTransaction instead of sendrawtransaction
auto signedtxn = EncodeHexTx ( tx_ ) ;
@ -391,9 +391,9 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
if ( sendResultValue . isNull ( ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " sendrawtransaction did not return an error or a txid. " ) ;
}
auto txid = sendResultValue . get_str ( ) ;
UniValue o ( UniValue : : VOBJ ) ;
o . push_back ( Pair ( " txid " , txid ) ) ;
set_result ( o ) ;
@ -405,14 +405,14 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
o . push_back ( Pair ( " hex " , signedtxn ) ) ;
set_result ( o ) ;
}
return true ;
}
/**
* END SCENARIO # 0
*/
/**
* SCENARIO # 1
*
@ -429,16 +429,16 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
/**
* END SCENARIO # 1
*/
// Prepare raw transaction to handle JoinSplits
CMutableTransaction mtx ( tx_ ) ;
crypto_sign_keypair ( joinSplitPubKey_ . begin ( ) , joinSplitPrivKey_ ) ;
mtx . joinSplitPubKey = joinSplitPubKey_ ;
tx_ = CTransaction ( mtx ) ;
std : : string hexMemo = std : : get < 1 > ( recipient_ ) ;
/**
* SCENARIO # 2
*
@ -451,13 +451,13 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
MergeToAddressJSInfo info ;
info . vpub_old = sendAmount ;
info . vpub_new = 0 ;
JSOutput jso = JSOutput ( boost : : get < libzcash : : SproutPaymentAddress > ( toPaymentAddress_ ) , sendAmount ) ;
if ( hexMemo . size ( ) > 0 ) {
jso . memo = get_memo_from_hex_string ( hexMemo ) ;
}
info . vjsout . push_back ( jso ) ;
UniValue obj ( UniValue : : VOBJ ) ;
obj = perform_joinsplit ( info ) ;
sign_send_raw_transaction ( obj ) ;
@ -466,14 +466,14 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
/**
* END SCENARIO # 2
*/
// Copy zinputs to more flexible containers
std : : deque < MergeToAddressInputSproutNote > zInputsDeque ;
for ( const auto & o : sproutNoteInputs_ ) {
zInputsDeque . push_back ( o ) ;
}
// When spending notes, take a snapshot of note witnesses and anchors as the treestate will
// change upon arrival of new blocks which contain joinsplit transactions. This is likely
// to happen as creating a chained joinsplit transaction can take longer than the block interval.
@ -488,7 +488,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
jsopWitnessAnchorMap [ jso . ToString ( ) ] = MergeToAddressWitnessAnchorData { vInputWitnesses [ 0 ] , inputAnchor } ;
}
}
/**
* SCENARIO # 3
*
@ -507,12 +507,12 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
int changeOutputIndex = - 1 ; // this is updated after each joinsplit if jsChange > 0
bool vpubOldProcessed = false ; // updated when vpub_old for taddr inputs is set in first joinsplit
bool vpubNewProcessed = false ; // updated when vpub_new for miner fee and taddr outputs is set in last joinsplit
// At this point, we are guaranteed to have at least one input note.
// Use address of first input note as the temporary change address.
SproutSpendingKey changeKey = std : : get < 3 > ( zInputsDeque . front ( ) ) ;
SproutPaymentAddress changeAddress = changeKey . address ( ) ;
CAmount vpubOldTarget = 0 ;
CAmount vpubNewTarget = 0 ;
if ( isToTaddr_ ) {
@ -524,16 +524,16 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
vpubOldTarget = t_inputs_total - minersFee ;
}
}
// Keep track of treestate within this transaction
boost : : unordered_map < uint256 , SproutMerkleTree , CCoinsKeyHasher > intermediates ;
std : : vector < uint256 > previousCommitments ;
while ( ! vpubNewProcessed ) {
MergeToAddressJSInfo info ;
info . vpub_old = 0 ;
info . vpub_new = 0 ;
// Set vpub_old in the first joinsplit
if ( ! vpubOldProcessed ) {
if ( t_inputs_total < vpubOldTarget ) {
@ -544,30 +544,30 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
info . vpub_old + = vpubOldTarget ; // funds flowing from public pool
vpubOldProcessed = true ;
}
CAmount jsInputValue = 0 ;
uint256 jsAnchor ;
std : : vector < boost : : optional < SproutWitness > > witnesses ;
JSDescription prevJoinSplit ;
// Keep track of previous JoinSplit and its commitments
if ( tx_ . vjoinsplit . size ( ) > 0 ) {
prevJoinSplit = tx_ . vjoinsplit . back ( ) ;
}
// If there is no change, the chain has terminated so we can reset the tracked treestate.
if ( jsChange = = 0 & & tx_ . vjoinsplit . size ( ) > 0 ) {
intermediates . clear ( ) ;
previousCommitments . clear ( ) ;
}
//
// Consume change as the first input of the JoinSplit.
//
if ( jsChange > 0 ) {
LOCK2 ( cs_main , pwalletMain - > cs_wallet ) ;
// Update tree state with previous joinsplit
SproutMerkleTree tree ;
auto it = intermediates . find ( prevJoinSplit . anchor ) ;
@ -576,7 +576,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
} else if ( ! pcoinsTip - > GetSproutAnchorAt ( prevJoinSplit . anchor , tree ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Could not find previous JoinSplit anchor " ) ;
}
assert ( changeOutputIndex ! = - 1 ) ;
boost : : optional < SproutWitness > changeWitness ;
int n = 0 ;
@ -594,7 +594,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
}
jsAnchor = tree . root ( ) ;
intermediates . insert ( std : : make_pair ( tree . root ( ) , tree ) ) ; // chained js are interstitial (found in between block boundaries)
// Decrypt the change note's ciphertext to retrieve some data we need
ZCNoteDecryption decryptor ( changeKey . receiving_key ( ) ) ;
auto hSig = prevJoinSplit . h_sig ( * pzcashParams , tx_ . joinSplitPubKey ) ;
@ -605,23 +605,23 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
prevJoinSplit . ephemeralKey ,
hSig ,
( unsigned char ) changeOutputIndex ) ;
SproutNote note = plaintext . note ( changeAddress ) ;
info . notes . push_back ( note ) ;
info . zkeys . push_back ( changeKey ) ;
jsInputValue + = plaintext . value ( ) ;
LogPrint ( " zrpcunsafe " , " %s: spending change (amount=%s) \n " ,
getId ( ) ,
FormatMoney ( plaintext . value ( ) ) ) ;
} catch ( const std : : exception & e ) {
throw JSONRPCError ( RPC_WALLET_ERROR , strprintf ( " Error decrypting output note of previous JoinSplit: %s " , e . what ( ) ) ) ;
}
}
//
// Consume spendable non-change notes
//
@ -638,7 +638,7 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
CAmount noteFunds = std : : get < 2 > ( t ) ;
SproutSpendingKey zkey = std : : get < 3 > ( t ) ;
zInputsDeque . pop_front ( ) ;
MergeToAddressWitnessAnchorData wad = jsopWitnessAnchorMap [ jso . ToString ( ) ] ;
vInputWitnesses . push_back ( wad . witness ) ;
if ( inputAnchor . IsNull ( ) ) {
@ -646,13 +646,13 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
} else if ( inputAnchor ! = wad . anchor ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Selected input notes do not share the same anchor " ) ;
}
vOutPoints . push_back ( jso ) ;
vInputNotes . push_back ( note ) ;
vInputZKeys . push_back ( zkey ) ;
jsInputValue + = noteFunds ;
int wtxHeight = - 1 ;
int wtxDepth = - 1 ;
{
@ -674,13 +674,13 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
wtxHeight ,
wtxDepth ) ;
}
// Add history of previous commitments to witness
if ( vInputNotes . size ( ) > 0 ) {
if ( vInputWitnesses . size ( ) = = 0 ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Could not find witness for note commitment " ) ;
}
for ( auto & optionalWitness : vInputWitnesses ) {
if ( ! optionalWitness ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Witness for note commitment is null " ) ;
@ -696,20 +696,20 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
}
witnesses . push_back ( w ) ;
}
// The jsAnchor is null if this JoinSplit is at the start of a new chain
if ( jsAnchor . IsNull ( ) ) {
jsAnchor = inputAnchor ;
}
// Add spendable notes as inputs
std : : copy ( vInputNotes . begin ( ) , vInputNotes . end ( ) , std : : back_inserter ( info . notes ) ) ;
std : : copy ( vInputZKeys . begin ( ) , vInputZKeys . end ( ) , std : : back_inserter ( info . zkeys ) ) ;
}
// Accumulate change
jsChange = jsInputValue + info . vpub_old ;
// Set vpub_new in the last joinsplit (when there are no more notes to spend)
if ( zInputsDeque . empty ( ) ) {
assert ( ! vpubNewProcessed ) ;
@ -724,10 +724,10 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
// If we are merging to a t-addr, there should be no change
if ( isToTaddr_ ) assert ( jsChange = = 0 ) ;
}
// create dummy output
info . vjsout . push_back ( JSOutput ( ) ) ; // dummy output while we accumulate funds into a change note for vpub_new
// create output for any change
if ( jsChange > 0 ) {
std : : string outputType = " change " ;
@ -741,24 +741,24 @@ bool AsyncRPCOperation_mergetoaddress::main_impl()
}
}
info . vjsout . push_back ( jso ) ;
LogPrint ( " zrpcunsafe " , " %s: generating note for %s (amount=%s) \n " ,
getId ( ) ,
outputType ,
FormatMoney ( jsChange ) ) ;
}
obj = perform_joinsplit ( info , witnesses , jsAnchor ) ;
if ( jsChange > 0 ) {
changeOutputIndex = mta_find_output ( obj , 1 ) ;
}
}
// Sanity check in case changes to code block above exits loop by invoking 'break'
assert ( zInputsDeque . size ( ) = = 0 ) ;
assert ( vpubNewProcessed ) ;
sign_send_raw_transaction ( obj ) ;
return true ;
}
@ -778,7 +778,7 @@ void AsyncRPCOperation_mergetoaddress::sign_send_raw_transaction(UniValue obj)
throw JSONRPCError ( RPC_WALLET_ERROR , " Missing hex data for raw transaction " ) ;
}
std : : string rawtxn = rawtxnValue . get_str ( ) ;
UniValue params = UniValue ( UniValue : : VARR ) ;
params . push_back ( rawtxn ) ;
UniValue signResultValue = signrawtransaction ( params , false ) ;
@ -789,13 +789,13 @@ void AsyncRPCOperation_mergetoaddress::sign_send_raw_transaction(UniValue obj)
// TODO: #1366 Maybe get "errors" and print array vErrors into a string
throw JSONRPCError ( RPC_WALLET_ENCRYPTION_FAILED , " Failed to sign transaction " ) ;
}
UniValue hexValue = find_value ( signResultObject , " hex " ) ;
if ( hexValue . isNull ( ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Missing hex data for signed transaction " ) ;
}
std : : string signedtxn = hexValue . get_str ( ) ;
// Send the signed transaction
if ( ! testmode ) {
params . clear ( ) ;
@ -805,26 +805,26 @@ void AsyncRPCOperation_mergetoaddress::sign_send_raw_transaction(UniValue obj)
if ( sendResultValue . isNull ( ) ) {
throw JSONRPCError ( RPC_WALLET_ERROR , " Send raw transaction did not return an error or a txid. " ) ;
}
std : : string txid = sendResultValue . get_str ( ) ;
UniValue o ( UniValue : : VOBJ ) ;
o . push_back ( Pair ( " txid " , txid ) ) ;
set_result ( o ) ;
} else {
// Test mode does not send the transaction to the network.
CDataStream stream ( ParseHex ( signedtxn ) , SER_NETWORK , PROTOCOL_VERSION ) ;
CTransaction tx ;
stream > > tx ;
UniValue o ( UniValue : : VOBJ ) ;
o . push_back ( Pair ( " test " , 1 ) ) ;
o . push_back ( Pair ( " txid " , tx . GetHash ( ) . ToString ( ) ) ) ;
o . push_back ( Pair ( " hex " , signedtxn ) ) ;
set_result ( o ) ;
}
// Keep the signed transaction so we can hash to the same txid
CDataStream stream ( ParseHex ( signedtxn ) , SER_NETWORK , PROTOCOL_VERSION ) ;
CTransaction tx ;
@ -864,52 +864,52 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
if ( anchor . IsNull ( ) ) {
throw std : : runtime_error ( " anchor is null " ) ;
}
if ( witnesses . size ( ) ! = info . notes . size ( ) ) {
throw runtime_error ( " number of notes and witnesses do not match " ) ;
}
if ( info . notes . size ( ) ! = info . zkeys . size ( ) ) {
throw runtime_error ( " number of notes and spending keys do not match " ) ;
}
for ( size_t i = 0 ; i < witnesses . size ( ) ; i + + ) {
if ( ! witnesses [ i ] ) {
throw runtime_error ( " joinsplit input could not be found in tree " ) ;
}
info . vjsin . push_back ( JSInput ( * witnesses [ i ] , info . notes [ i ] , info . zkeys [ i ] ) ) ;
}
// Make sure there are two inputs and two outputs
while ( info . vjsin . size ( ) < ZC_NUM_JS_INPUTS ) {
info . vjsin . push_back ( JSInput ( ) ) ;
}
while ( info . vjsout . size ( ) < ZC_NUM_JS_OUTPUTS ) {
info . vjsout . push_back ( JSOutput ( ) ) ;
}
if ( info . vjsout . size ( ) ! = ZC_NUM_JS_INPUTS | | info . vjsin . size ( ) ! = ZC_NUM_JS_OUTPUTS ) {
throw runtime_error ( " unsupported joinsplit input/output counts " ) ;
}
CMutableTransaction mtx ( tx_ ) ;
LogPrint ( " zrpcunsafe " , " %s: creating joinsplit at index %d (vpub_old=%s, vpub_new=%s, in[0]=%s, in[1]=%s, out[0]=%s, out[1]=%s) \n " ,
getId ( ) ,
tx_ . vjoinsplit . size ( ) ,
FormatMoney ( info . vpub_old ) , FormatMoney ( info . vpub_new ) ,
FormatMoney ( info . vjsin [ 0 ] . note . value ( ) ) , FormatMoney ( info . vjsin [ 1 ] . note . value ( ) ) ,
FormatMoney ( info . vjsout [ 0 ] . value ) , FormatMoney ( info . vjsout [ 1 ] . value ) ) ;
// Generate the proof, this can take over a minute.
std : : array < libzcash : : JSInput , ZC_NUM_JS_INPUTS > inputs { info . vjsin [ 0 ] , info . vjsin [ 1 ] } ;
std : : array < libzcash : : JSOutput , ZC_NUM_JS_OUTPUTS > outputs { info . vjsout [ 0 ] , info . vjsout [ 1 ] } ;
std : : array < size_t , ZC_NUM_JS_INPUTS > inputMap ;
std : : array < size_t , ZC_NUM_JS_OUTPUTS > outputMap ;
uint256 esk ; // payment disclosure - secret
JSDescription jsdesc = JSDescription : : Randomized (
mtx . fOverwintered & & ( mtx . nVersion > = SAPLING_TX_VERSION ) ,
* pzcashParams ,
@ -929,34 +929,34 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
throw std : : runtime_error ( " error verifying joinsplit " ) ;
}
}
mtx . vjoinsplit . push_back ( jsdesc ) ;
// Empty output script.
CScript scriptCode ;
CTransaction signTx ( mtx ) ;
uint256 dataToBeSigned = SignatureHash ( scriptCode , signTx , NOT_AN_INPUT , SIGHASH_ALL , 0 , consensusBranchId_ ) ;
// Add the signature
if ( ! ( crypto_sign_detached ( & mtx . joinSplitSig [ 0 ] , NULL ,
dataToBeSigned . begin ( ) , 32 ,
joinSplitPrivKey_ ) = = 0 ) ) {
throw std : : runtime_error ( " crypto_sign_detached failed " ) ;
}
// Sanity check
if ( ! ( crypto_sign_verify_detached ( & mtx . joinSplitSig [ 0 ] ,
dataToBeSigned . begin ( ) , 32 ,
mtx . joinSplitPubKey . begin ( ) ) = = 0 ) ) {
throw std : : runtime_error ( " crypto_sign_verify_detached failed " ) ;
}
CTransaction rawTx ( mtx ) ;
tx_ = rawTx ;
CDataStream ss ( SER_NETWORK , PROTOCOL_VERSION ) ;
ss < < rawTx ;
std : : string encryptedNote1 ;
std : : string encryptedNote2 ;
{
@ -965,7 +965,7 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
ss2 < < jsdesc . ephemeralKey ;
ss2 < < jsdesc . ciphertexts [ 0 ] ;
ss2 < < jsdesc . h_sig ( * pzcashParams , joinSplitPubKey_ ) ;
encryptedNote1 = HexStr ( ss2 . begin ( ) , ss2 . end ( ) ) ;
}
{
@ -974,10 +974,10 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
ss2 < < jsdesc . ephemeralKey ;
ss2 < < jsdesc . ciphertexts [ 1 ] ;
ss2 < < jsdesc . h_sig ( * pzcashParams , joinSplitPubKey_ ) ;
encryptedNote2 = HexStr ( ss2 . begin ( ) , ss2 . end ( ) ) ;
}
UniValue arrInputMap ( UniValue : : VARR ) ;
UniValue arrOutputMap ( UniValue : : VARR ) ;
for ( size_t i = 0 ; i < ZC_NUM_JS_INPUTS ; i + + ) {
@ -986,8 +986,8 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
for ( size_t i = 0 ; i < ZC_NUM_JS_OUTPUTS ; i + + ) {
arrOutputMap . push_back ( static_cast < uint64_t > ( outputMap [ i ] ) ) ;
}
// !!! Payment disclosure START
unsigned char buffer [ 32 ] = { 0 } ;
memcpy ( & buffer [ 0 ] , & joinSplitPrivKey_ [ 0 ] , 32 ) ; // private key in first half of 64 byte buffer
@ -1003,11 +1003,11 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
libzcash : : SproutPaymentAddress zaddr = output . addr ; // randomized output
PaymentDisclosureInfo pdInfo = { PAYMENT_DISCLOSURE_VERSION_EXPERIMENTAL , esk , joinSplitPrivKey , zaddr } ;
paymentDisclosureData_ . push_back ( PaymentDisclosureKeyInfo ( pdKey , pdInfo ) ) ;
LogPrint ( " paymentdisclosure " , " %s: Payment Disclosure: js=%d, n=%d, zaddr=%s \n " , getId ( ) , js_index , int ( mapped_index ) , EncodePaymentAddress ( zaddr ) ) ;
}
// !!! Payment disclosure END
UniValue obj ( UniValue : : VOBJ ) ;
obj . push_back ( Pair ( " encryptednote1 " , encryptedNote1 ) ) ;
obj . push_back ( Pair ( " encryptednote2 " , encryptedNote2 ) ) ;
@ -1020,19 +1020,19 @@ UniValue AsyncRPCOperation_mergetoaddress::perform_joinsplit(
std : : array < unsigned char , ZC_MEMO_SIZE > AsyncRPCOperation_mergetoaddress : : get_memo_from_hex_string ( std : : string s )
{
std : : array < unsigned char , ZC_MEMO_SIZE > memo = { { 0x00 } } ;
std : : vector < unsigned char > rawMemo = ParseHex ( s . c_str ( ) ) ;
// If ParseHex comes across a non-hex char, it will stop but still return results so far.
size_t slen = s . length ( ) ;
if ( slen % 2 ! = 0 | | ( slen > 0 & & rawMemo . size ( ) ! = slen / 2 ) ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , " Memo must be in hexadecimal format " ) ;
}
if ( rawMemo . size ( ) > ZC_MEMO_SIZE ) {
throw JSONRPCError ( RPC_INVALID_PARAMETER , strprintf ( " Memo size of %d is too big, maximum allowed is %d " , rawMemo . size ( ) , ZC_MEMO_SIZE ) ) ;
}
// copy vector into boost array
int lenMemo = rawMemo . size ( ) ;
for ( int i = 0 ; i < ZC_MEMO_SIZE & & i < lenMemo ; i + + ) {
@ -1050,7 +1050,7 @@ UniValue AsyncRPCOperation_mergetoaddress::getStatus() const
if ( contextinfo_ . isNull ( ) ) {
return v ;
}
UniValue obj = v . get_obj ( ) ;
obj . push_back ( Pair ( " method " , " z_mergetoaddress " ) ) ;
obj . push_back ( Pair ( " params " , contextinfo_ ) ) ;