@@ -35,15 +35,56 @@ OperationResult SaplingOperation::checkTxValues(TxValues& txValues, bool isFromt
3535 return OperationResult (true );
3636}
3737
38+ OperationResult loadKeysFromShieldedFrom (const libzcash::SaplingPaymentAddress &addr,
39+ libzcash::SaplingExpandedSpendingKey& expskOut,
40+ uint256& ovkOut)
41+ {
42+ // Get spending key for address
43+ libzcash::SaplingExtendedSpendingKey sk;
44+ if (!pwalletMain->GetSaplingExtendedSpendingKey (addr, sk)) {
45+ return errorOut (" Spending key not in the wallet" );
46+ }
47+ expskOut = sk.expsk ;
48+ ovkOut = expskOut.full_viewing_key ().ovk ;
49+ return OperationResult (true );
50+ }
51+
52+ TxValues calculateTarget (std::vector<SendManyRecipient>& taddrRecipients,
53+ std::vector<SendManyRecipient>& shieldedAddrRecipients,
54+ CAmount fee)
55+ {
56+ TxValues txValues;
57+ for (SendManyRecipient &t : taddrRecipients) {
58+ txValues.transOutTotal += t.amount ;
59+ }
60+
61+ // Add shielded outputs
62+ for (const SendManyRecipient &t : shieldedAddrRecipients) {
63+ txValues.shieldedOutTotal += t.amount ;
64+ }
65+ txValues.target = txValues.shieldedOutTotal + txValues.transOutTotal + fee;
66+ return txValues;
67+ }
68+
3869OperationResult SaplingOperation::build ()
3970{
4071
41- bool isFromtAddress = fromAddress.isFromTAddress () || selectFromtaddrs ;
72+ bool isFromtAddress = fromAddress.isFromTAddress ();
4273 bool isFromShielded = fromAddress.isFromSapAddress ();
4374
44- // It needs to have a from (for now at least)
4575 if (!isFromtAddress && !isFromShielded) {
46- return errorOut (" From address parameter missing" );
76+ isFromtAddress = selectFromtaddrs;
77+ isFromShielded = selectFromShield;
78+
79+ // It needs to have a from.
80+ if (!isFromtAddress && !isFromShielded) {
81+ return errorOut (" From address parameter missing" );
82+ }
83+
84+ // Cannot be from both
85+ if (isFromtAddress && isFromShielded) {
86+ return errorOut (" From address type cannot be shielded and transparent" );
87+ }
4788 }
4889
4990 if (taddrRecipients.empty () && shieldedAddrRecipients.empty ()) {
@@ -54,17 +95,25 @@ OperationResult SaplingOperation::build()
5495 return errorOut (" Minconf cannot be zero when sending from shielded address" );
5596 }
5697
57- // Get necessary keys
98+ // First calculate target values
99+ TxValues txValues = calculateTarget (taddrRecipients, shieldedAddrRecipients, fee);
100+ OperationResult result (false );
101+ // Necessary keys
58102 libzcash::SaplingExpandedSpendingKey expsk;
59103 uint256 ovk;
60104 if (isFromShielded) {
61- // Get spending key for address
62- libzcash::SaplingExtendedSpendingKey sk;
63- if (!pwalletMain->GetSaplingExtendedSpendingKey (fromAddress.fromSapAddr .get (), sk)) {
64- return errorOut (" Spending key not in the wallet" );
105+ // Try to get the sk and ovk if we know the address from, if we don't know it then this will be loaded in loadUnspentNotes
106+ // using the sk of the first note input of the transaction.
107+ if (fromAddress.isFromSapAddress ()) {
108+ // Get spending key for address
109+ auto loadKeyRes = loadKeysFromShieldedFrom (fromAddress.fromSapAddr .get (), expsk, ovk);
110+ if (!loadKeyRes) return loadKeyRes;
111+ }
112+
113+ // Load and select notes to spend
114+ if (!(result = loadUnspentNotes (txValues, expsk, ovk))) {
115+ return result;
65116 }
66- expsk = sk.expsk ;
67- ovk = expsk.full_viewing_key ().ovk ;
68117 } else {
69118 // Sending from a t-address, which we don't have an ovk for. Instead,
70119 // generate a common one from the HD seed. This ensures the data is
@@ -73,17 +122,13 @@ OperationResult SaplingOperation::build()
73122 ovk = pwalletMain->GetSaplingScriptPubKeyMan ()->getCommonOVKFromSeed ();
74123 }
75124
76- // Results
77- TxValues txValues;
78125 // Add transparent outputs
79126 for (SendManyRecipient &t : taddrRecipients) {
80- txValues.transOutTotal += t.amount ;
81127 txBuilder.AddTransparentOutput (DecodeDestination (t.address ), t.amount );
82128 }
83129
84130 // Add shielded outputs
85131 for (const SendManyRecipient &t : shieldedAddrRecipients) {
86- txValues.shieldedOutTotal += t.amount ;
87132 auto addr = KeyIO::DecodePaymentAddress (t.address );
88133 assert (IsValidPaymentAddress (addr));
89134 auto to = boost::get<libzcash::SaplingPaymentAddress>(addr);
@@ -94,25 +139,13 @@ OperationResult SaplingOperation::build()
94139 txBuilder.AddSaplingOutput (ovk, to, t.amount , memo);
95140 }
96141
97- // Load total
98- txValues.target = txValues.shieldedOutTotal + txValues.transOutTotal + fee;
99- OperationResult result (false );
100-
101142 // If from address is a taddr, select UTXOs to spend
102143 // note: when spending coinbase utxos, you can only specify a single shielded addr as the change must go somewhere
103144 // and if there are multiple shielded addrs, we don't know where to send it.
104145 if (isFromtAddress && !(result = loadUtxos (txValues))) {
105146 return result;
106147 }
107148
108- // If from a shielded addr, select notes to spend
109- if (isFromShielded) {
110- // Load notes
111- if (!(result = loadUnspentNotes (txValues, expsk))) {
112- return result;
113- }
114- }
115-
116149 const auto & retCalc = checkTxValues (txValues, isFromtAddress, isFromShielded);
117150 if (!retCalc) return retCalc;
118151
@@ -245,11 +278,12 @@ OperationResult SaplingOperation::loadUtxos(TxValues& txValues)
245278 return OperationResult (true );
246279}
247280
248- OperationResult SaplingOperation::loadUnspentNotes (TxValues& txValues, const libzcash::SaplingExpandedSpendingKey& expsk)
281+ OperationResult SaplingOperation::loadUnspentNotes (TxValues& txValues,
282+ libzcash::SaplingExpandedSpendingKey& expsk,
283+ uint256& ovk)
249284{
250285 std::vector<SaplingNoteEntry> saplingEntries;
251- libzcash::PaymentAddress paymentAddress (fromAddress.fromSapAddr .get ());
252- pwalletMain->GetSaplingScriptPubKeyMan ()->GetFilteredNotes (saplingEntries, paymentAddress, mindepth);
286+ pwalletMain->GetSaplingScriptPubKeyMan ()->GetFilteredNotes (saplingEntries, fromAddress.fromSapAddr , mindepth);
253287
254288 for (const auto & entry : saplingEntries) {
255289 shieldedInputs.emplace_back (entry);
@@ -277,6 +311,11 @@ OperationResult SaplingOperation::loadUnspentNotes(TxValues& txValues, const lib
277311 std::vector<libzcash::SaplingNote> notes;
278312 CAmount sum = 0 ;
279313 for (const auto & t : shieldedInputs) {
314+ // if null, load the first input sk
315+ if (expsk.IsNull ()) {
316+ auto resLoadKeys = loadKeysFromShieldedFrom (t.address , expsk, ovk);
317+ if (!resLoadKeys) return resLoadKeys;
318+ }
280319 ops.emplace_back (t.op );
281320 notes.emplace_back (t.note );
282321 sum += t.note .value ();
0 commit comments