diff --git a/src/test/rpc_wallet_tests.cpp b/src/test/rpc_wallet_tests.cpp index edbcb6552..680cd0490 100644 --- a/src/test/rpc_wallet_tests.cpp +++ b/src/test/rpc_wallet_tests.cpp @@ -970,26 +970,26 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters) // Test constructor of AsyncRPCOperation_sendmany try { - std::shared_ptr operation(new AsyncRPCOperation_sendmany(mtx, "",{}, {}, -1)); + std::shared_ptr operation(new AsyncRPCOperation_sendmany(boost::none, mtx, "",{}, {}, -1)); } catch (const UniValue& objError) { BOOST_CHECK( find_error(objError, "Minconf cannot be negative")); } try { - std::shared_ptr operation(new AsyncRPCOperation_sendmany(mtx, "",{}, {}, 1)); + std::shared_ptr operation(new AsyncRPCOperation_sendmany(boost::none, mtx, "",{}, {}, 1)); } catch (const UniValue& objError) { BOOST_CHECK( find_error(objError, "From address parameter missing")); } try { - std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ", {}, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(boost::none, mtx, "tmRr6yJonqGK23UVhrKuyvTpF8qxQQjKigJ", {}, {}, 1) ); } catch (const UniValue& objError) { BOOST_CHECK( find_error(objError, "No recipients")); } try { std::vector recipients = { SendManyRecipient("dummy",1.0, "") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, "INVALID", recipients, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(boost::none, mtx, "INVALID", recipients, {}, 1) ); } catch (const UniValue& objError) { BOOST_CHECK( find_error(objError, "Invalid from address")); } @@ -997,7 +997,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters) // Testnet payment addresses begin with 'zt'. This test detects an incorrect prefix. try { std::vector recipients = { SendManyRecipient("dummy",1.0, "") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, "zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U", recipients, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(boost::none, mtx, "zcMuhvq8sEkHALuSU2i4NbNQxshSAYrpCExec45ZjtivYPbuiFPwk6WHy4SvsbeZ4siy1WheuRGjtaJmoD1J8bFqNXhsG6U", recipients, {}, 1) ); } catch (const UniValue& objError) { BOOST_CHECK( find_error(objError, "Invalid from address")); } @@ -1006,7 +1006,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_parameters) // invokes a method on pwalletMain, which is undefined in the google test environment. try { std::vector recipients = { SendManyRecipient("dummy",1.0, "") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, "ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP", recipients, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(boost::none, mtx, "ztjiDe569DPNbyTE6TSdJTaSDhoXEHLGvYoUnBU1wfVNU52TEyT6berYtySkd21njAeEoh8fFJUT42kua9r8EnhBaEKqCpP", recipients, {}, 1) ); } catch (const UniValue& objError) { BOOST_CHECK( find_error(objError, "no spending key found for zaddr")); } @@ -1039,7 +1039,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) // there are no utxos to spend { std::vector recipients = { SendManyRecipient(zaddr1,100.0, "DEADBEEF") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, taddr1, {}, recipients, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(boost::none, mtx, taddr1, {}, recipients, 1) ); operation->main(); BOOST_CHECK(operation->isFailed()); std::string msg = operation->getErrorMessage(); @@ -1050,7 +1050,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) { try { std::vector recipients = {SendManyRecipient(taddr1, 100.0, "DEADBEEF")}; - std::shared_ptr operation(new AsyncRPCOperation_sendmany(mtx, zaddr1, recipients, {}, 0)); + std::shared_ptr operation(new AsyncRPCOperation_sendmany(boost::none, mtx, zaddr1, recipients, {}, 0)); BOOST_CHECK(false); // Fail test if an exception is not thrown } catch (const UniValue& objError) { BOOST_CHECK(find_error(objError, "Minconf cannot be zero when sending from zaddr")); @@ -1061,7 +1061,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) // there are no unspent notes to spend { std::vector recipients = { SendManyRecipient(taddr1,100.0, "DEADBEEF") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, zaddr1, recipients, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(boost::none, mtx, zaddr1, recipients, {}, 1) ); operation->main(); BOOST_CHECK(operation->isFailed()); std::string msg = operation->getErrorMessage(); @@ -1071,7 +1071,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) // get_memo_from_hex_string()) { std::vector recipients = { SendManyRecipient(zaddr1,100.0, "DEADBEEF") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, zaddr1, recipients, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(boost::none, mtx, zaddr1, recipients, {}, 1) ); std::shared_ptr ptr = std::dynamic_pointer_cast (operation); TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr); @@ -1122,7 +1122,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) // add_taddr_change_output_to_tx() will append a vout to a raw transaction { std::vector recipients = { SendManyRecipient(zaddr1,100.0, "DEADBEEF") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, zaddr1, recipients, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(boost::none, mtx, zaddr1, recipients, {}, 1) ); std::shared_ptr ptr = std::dynamic_pointer_cast (operation); TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr); @@ -1151,7 +1151,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) SendManyRecipient("tmUSbHz3vxnwLvRyNDXbwkZxjVyDodMJEhh",CAmount(4.56), ""), SendManyRecipient("tmYZAXYPCP56Xa5JQWWPZuK7o7bfUQW6kkd",CAmount(7.89), ""), }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, zaddr1, recipients, {}, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(boost::none, mtx, zaddr1, recipients, {}, 1) ); std::shared_ptr ptr = std::dynamic_pointer_cast (operation); TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr); @@ -1174,7 +1174,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) // we have the spending key for the dummy recipient zaddr1 std::vector recipients = { SendManyRecipient(zaddr1, 0.0005, "ABCD") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, zaddr1, {}, recipients, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(boost::none, mtx, zaddr1, {}, recipients, 1) ); std::shared_ptr ptr = std::dynamic_pointer_cast (operation); TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr); @@ -1199,7 +1199,7 @@ BOOST_AUTO_TEST_CASE(rpc_z_sendmany_internals) // Dummy input so the operation object can be instantiated. std::vector recipients = { SendManyRecipient(zaddr1, 0.0005, "ABCD") }; - std::shared_ptr operation( new AsyncRPCOperation_sendmany(mtx, zaddr1, {}, recipients, 1) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(boost::none, mtx, zaddr1, {}, recipients, 1) ); std::shared_ptr ptr = std::dynamic_pointer_cast (operation); TEST_FRIEND_AsyncRPCOperation_sendmany proxy(ptr); diff --git a/src/transaction_builder.h b/src/transaction_builder.h index 0291c7ad1..90f2eff2d 100644 --- a/src/transaction_builder.h +++ b/src/transaction_builder.h @@ -69,6 +69,7 @@ private: boost::optional tChangeAddr; public: + TransactionBuilder() {} TransactionBuilder(const Consensus::Params& consensusParams, int nHeight, CKeyStore* keyStore = nullptr); void SetFee(CAmount fee); diff --git a/src/wallet/asyncrpcoperation_sendmany.cpp b/src/wallet/asyncrpcoperation_sendmany.cpp index 405cde231..661943762 100644 --- a/src/wallet/asyncrpcoperation_sendmany.cpp +++ b/src/wallet/asyncrpcoperation_sendmany.cpp @@ -53,6 +53,7 @@ int find_output(UniValue obj, int n) { } AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany( + boost::optional builder, CMutableTransaction contextualTx, std::string fromAddress, std::vector tOutputs, @@ -75,7 +76,13 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany( if (tOutputs.size() == 0 && zOutputs.size() == 0) { throw JSONRPCError(RPC_INVALID_PARAMETER, "No recipients"); } - + + isUsingBuilder_ = false; + if (builder) { + isUsingBuilder_ = true; + builder_ = builder.get(); + } + fromtaddr_ = DecodeDestination(fromAddress); isfromtaddr_ = IsValidDestination(fromtaddr_); isfromzaddr_ = false; diff --git a/src/wallet/asyncrpcoperation_sendmany.h b/src/wallet/asyncrpcoperation_sendmany.h index d3729804a..c4078160b 100644 --- a/src/wallet/asyncrpcoperation_sendmany.h +++ b/src/wallet/asyncrpcoperation_sendmany.h @@ -8,6 +8,7 @@ #include "asyncrpcoperation.h" #include "amount.h" #include "primitives/transaction.h" +#include "transaction_builder.h" #include "zcash/JoinSplit.hpp" #include "zcash/Address.hpp" #include "wallet.h" @@ -51,7 +52,15 @@ struct WitnessAnchorData { class AsyncRPCOperation_sendmany : public AsyncRPCOperation { public: - AsyncRPCOperation_sendmany(CMutableTransaction contextualTx, std::string fromAddress, std::vector tOutputs, std::vector zOutputs, int minDepth, CAmount fee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE, UniValue contextInfo = NullUniValue); + AsyncRPCOperation_sendmany( + boost::optional builder, + CMutableTransaction contextualTx, + std::string fromAddress, + std::vector tOutputs, + std::vector zOutputs, + int minDepth, + CAmount fee = ASYNC_RPC_OPERATION_DEFAULT_MINERS_FEE, + UniValue contextInfo = NullUniValue); virtual ~AsyncRPCOperation_sendmany(); // We don't want to be copied or moved around @@ -73,6 +82,7 @@ private: UniValue contextinfo_; // optional data to include in return value from getStatus() + bool isUsingBuilder_; // Indicates that no Sprout addresses are involved uint32_t consensusBranchId_; CAmount fee_; int mindepth_; @@ -93,7 +103,8 @@ private: std::vector z_outputs_; std::vector t_inputs_; std::vector z_inputs_; - + + TransactionBuilder builder_; CTransaction tx_; void add_taddr_change_output_to_tx(CAmount amount); diff --git a/src/wallet/rpcwallet.cpp b/src/wallet/rpcwallet.cpp index 133f4a5ea..0ce97276c 100644 --- a/src/wallet/rpcwallet.cpp +++ b/src/wallet/rpcwallet.cpp @@ -13,6 +13,7 @@ #include "netbase.h" #include "rpc/server.h" #include "timedata.h" +#include "transaction_builder.h" #include "util.h" #include "utilmoneystr.h" #include "wallet.h" @@ -3787,7 +3788,14 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) o.push_back(Pair("fee", std::stod(FormatMoney(nFee)))); UniValue contextInfo = o; + // Builder (used if Sapling addresses are involved) + boost::optional builder; + if (false) { // TODO: Sapling support + builder = TransactionBuilder(Params().GetConsensus(), nextBlockHeight, pwalletMain); + } + // Contextual transaction we will build on + // (used if no Sapling addresses are involved) CMutableTransaction contextualTx = CreateNewContextualCMutableTransaction(Params().GetConsensus(), nextBlockHeight); bool isShielded = !fromTaddr || zaddrRecipients.size() > 0; if (contextualTx.nVersion == 1 && isShielded) { @@ -3796,7 +3804,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp) // Create operation and add to global queue std::shared_ptr q = getAsyncRPCQueue(); - std::shared_ptr operation( new AsyncRPCOperation_sendmany(contextualTx, fromaddress, taddrRecipients, zaddrRecipients, nMinDepth, nFee, contextInfo) ); + std::shared_ptr operation( new AsyncRPCOperation_sendmany(builder, contextualTx, fromaddress, taddrRecipients, zaddrRecipients, nMinDepth, nFee, contextInfo) ); q->addOperation(operation); AsyncRPCOperationId operationId = operation->getId(); return operationId;