Refactor to use wallet note tracking from commit a72379
This commit is contained in:
parent
97b6f365a1
commit
87f7c98795
|
@ -120,7 +120,7 @@ void AsyncRPCOperation_sendmany::main() {
|
|||
// Notes:
|
||||
// 1. Currently there is no limit set on the number of joinsplits, so size of tx could be invalid.
|
||||
// 2. Note selection is not optimal
|
||||
// 3. Spendable notes are not locked, so another operation could also try to use them
|
||||
// 3. Spendable notes are not locked, so an operation running in parallel could also try to use them
|
||||
bool AsyncRPCOperation_sendmany::main_impl() {
|
||||
|
||||
bool isSingleZaddrOutput = (t_outputs_.size()==0 && z_outputs_.size()==1);
|
||||
|
@ -142,8 +142,8 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||
}
|
||||
|
||||
CAmount z_inputs_total = 0;
|
||||
for (SendManyInputNPT & p : z_inputs_) {
|
||||
z_inputs_total += p.second;
|
||||
for (SendManyInputJSOP & t : z_inputs_) {
|
||||
z_inputs_total += std::get<2>(t);
|
||||
}
|
||||
|
||||
CAmount t_outputs_total = 0;
|
||||
|
@ -198,6 +198,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||
tx_ = CTransaction(rawTx);
|
||||
}
|
||||
|
||||
// TODO: Replace with logging to debug.log
|
||||
#if 1
|
||||
std::cout << "t_inputs_total: " << t_inputs_total << std::endl;
|
||||
std::cout << "z_inputs_total: " << z_inputs_total << std::endl;
|
||||
|
@ -244,7 +245,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||
tx_ = CTransaction(mtx);
|
||||
|
||||
// Copy zinputs and zoutputs to more flexible containers
|
||||
std::deque<SendManyInputNPT> zInputsDeque;
|
||||
std::deque<SendManyInputJSOP> zInputsDeque;
|
||||
for (auto o : z_inputs_) {
|
||||
zInputsDeque.push_back(o);
|
||||
}
|
||||
|
@ -348,18 +349,17 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||
AsyncJoinSplitInfo info;
|
||||
info.vpub_old = 0;
|
||||
info.vpub_new = 0;
|
||||
std::vector<JSOutPoint> outPoints;
|
||||
int n = 0;
|
||||
while (n++ < 2 && taddrTargetAmount > 0) {
|
||||
SendManyInputNPT o = zInputsDeque.front();
|
||||
NotePlaintext npt = o.first;
|
||||
CAmount noteFunds = o.second;
|
||||
SendManyInputJSOP o = zInputsDeque.front();
|
||||
JSOutPoint outPoint = std::get<0>(o);
|
||||
Note note = std::get<1>(o);
|
||||
CAmount noteFunds = std::get<2>(o);
|
||||
zInputsDeque.pop_front();
|
||||
|
||||
libzcash::Note inputNote = npt.note(frompaymentaddress_);
|
||||
uint256 inputCommitment = inputNote.cm();
|
||||
info.notes.push_back(inputNote);
|
||||
info.commitments.push_back(inputCommitment);
|
||||
info.keys.push_back(spendingkey_);
|
||||
info.notes.push_back(note);
|
||||
outPoints.push_back(outPoint);
|
||||
|
||||
// Put value back into the value pool
|
||||
if (noteFunds >= taddrTargetAmount) {
|
||||
|
@ -380,7 +380,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||
info.vjsout.push_back(JSOutput(frompaymentaddress_, jsChange));
|
||||
}
|
||||
|
||||
obj = perform_joinsplit(info);
|
||||
obj = perform_joinsplit(info, outPoints);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -444,10 +444,6 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||
jsAnchor = changeWitness.root();
|
||||
uint256 changeCommitment = prevJoinSplit.commitments[changeOutputIndex];
|
||||
intermediates.insert(std::make_pair(tree.root(), tree));
|
||||
|
||||
// Update info with this change
|
||||
info.commitments.push_back(changeCommitment);
|
||||
info.keys.push_back(spendingkey_);
|
||||
witnesses.push_back(changeWitness);
|
||||
|
||||
// Decrypt the change note's ciphertext to retrieve some data we need
|
||||
|
@ -466,8 +462,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||
|
||||
jsInputValue += plaintext.value;
|
||||
} catch (const std::exception e) {
|
||||
std::cout << "exception: " << e.what() << std::endl;
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Could not decrypt output note of previous join split in chain");
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, strprintf("Error decrypting output note of previous JoinSplit: %s", e.what()));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -476,21 +471,18 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||
// Consume spendable non-change notes
|
||||
//
|
||||
std::vector<Note> vInputNotes;
|
||||
std::vector<uint256> vInputCommitments;
|
||||
std::vector<SpendingKey> vInputSpendingKey;
|
||||
std::vector<JSOutPoint> vOutPoints;
|
||||
uint256 inputAnchor;
|
||||
int numInputsNeeded = (jsChange>0) ? 1 : 0;
|
||||
while (numInputsNeeded++ < 2 && zInputsDeque.size() > 0) {
|
||||
SendManyInputNPT o = zInputsDeque.front();
|
||||
NotePlaintext npt = o.first;
|
||||
CAmount noteFunds = o.second;
|
||||
SendManyInputJSOP t = zInputsDeque.front();
|
||||
JSOutPoint jso = std::get<0>(t);
|
||||
Note note = std::get<1>(t);
|
||||
CAmount noteFunds = std::get<2>(t);
|
||||
zInputsDeque.pop_front();
|
||||
|
||||
libzcash::Note inputNote = npt.note(frompaymentaddress_);
|
||||
uint256 inputCommitment = inputNote.cm();
|
||||
vInputNotes.push_back(inputNote);
|
||||
vInputCommitments.push_back(inputCommitment);
|
||||
vInputSpendingKey.push_back(spendingkey_);
|
||||
vOutPoints.push_back(jso);
|
||||
vInputNotes.push_back(note);
|
||||
|
||||
jsInputValue += noteFunds;
|
||||
}
|
||||
|
@ -500,7 +492,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||
std::vector<boost::optional<ZCIncrementalWitness>> vInputWitnesses;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
pwalletMain->WitnessNoteCommitment(vInputCommitments, vInputWitnesses, inputAnchor);
|
||||
pwalletMain->GetNoteWitnesses(vOutPoints, vInputWitnesses, inputAnchor);
|
||||
}
|
||||
|
||||
if (vInputWitnesses.size()==0) {
|
||||
|
@ -528,10 +520,8 @@ bool AsyncRPCOperation_sendmany::main_impl() {
|
|||
jsAnchor = inputAnchor;
|
||||
}
|
||||
|
||||
// Populate info struct with note inputs
|
||||
std::copy(vInputCommitments.begin(), vInputCommitments.end(), std::back_inserter(info.commitments));
|
||||
// Add spendable notes as inputs
|
||||
std::copy(vInputNotes.begin(), vInputNotes.end(), std::back_inserter(info.notes));
|
||||
std::copy(vInputSpendingKey.begin(), vInputSpendingKey.end(), std::back_inserter(info.keys));
|
||||
}
|
||||
|
||||
|
||||
|
@ -717,7 +707,6 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() {
|
|||
for (auto & pair : mapNoteData) {
|
||||
JSOutPoint jsop = pair.first;
|
||||
CNoteData nd = pair.second;
|
||||
|
||||
PaymentAddress pa = nd.address;
|
||||
|
||||
// skip notes which belong to a different payment address in the wallet
|
||||
|
@ -725,11 +714,22 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() {
|
|||
continue;
|
||||
}
|
||||
|
||||
// skip note which has been spent
|
||||
if (pwalletMain->IsSpent(nd.nullifier)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
int i = jsop.js; // Index into CTransaction.vjoinsplit
|
||||
int j = jsop.n; // Index into JSDescription.ciphertexts
|
||||
|
||||
// determine amount of funds in the note and if it has been spent
|
||||
ZCNoteDecryption decryptor(spendingkey_.viewing_key());
|
||||
// Get cached decryptor
|
||||
ZCNoteDecryption decryptor;
|
||||
if (!pwalletMain->GetNoteDecryptor(pa, decryptor)) {
|
||||
// Note decryptors are created when the wallet is loaded, so it should always exist
|
||||
throw JSONRPCError(RPC_WALLET_ERROR, "Could not find note decryptor");
|
||||
}
|
||||
|
||||
// determine amount of funds in the note
|
||||
auto hSig = wtx.vjoinsplit[i].h_sig(*pzcashParams, wtx.joinSplitPubKey);
|
||||
try {
|
||||
NotePlaintext plaintext = NotePlaintext::decrypt(
|
||||
|
@ -739,28 +739,15 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() {
|
|||
hSig,
|
||||
(unsigned char) j);
|
||||
|
||||
uint256 nullifier = plaintext.note(frompaymentaddress_).nullifier(spendingkey_);
|
||||
bool isSpent = pwalletMain->IsSpent(nullifier);
|
||||
|
||||
if (isSpent) {
|
||||
|
||||
std::cout << "Found SPENT note at txid : " << wtx.GetTxid().ToString() << std::endl;
|
||||
std::cout << "... vjoinsplit index: " << i << std::endl;
|
||||
std::cout << "... jsdescription index: " << j << std::endl;
|
||||
std::cout << "... amount: " << FormatMoney(plaintext.value, false) << std::endl;
|
||||
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
z_inputs_.push_back(SendManyInputNPT(plaintext, CAmount(plaintext.value)));
|
||||
z_inputs_.push_back(SendManyInputJSOP(jsop, plaintext.note(pa), CAmount(plaintext.value)));
|
||||
|
||||
// TODO: Replace with logging to debug.log
|
||||
#if 1
|
||||
std::cout << "Found note at txid : " << wtx.GetTxid().ToString() << std::endl;
|
||||
std::cout << "... vjoinsplit index: " << i << std::endl;
|
||||
std::cout << "... jsdescription index: " << j << std::endl;
|
||||
std::cout << "... payment address: " << CZCPaymentAddress(pa).ToString() << std::endl;
|
||||
std::cout << "... spent: " << isSpent << std::endl;
|
||||
std::cout << "... spent: " << pwalletMain->IsSpent(nd.nullifier) << std::endl;
|
||||
std::string data(plaintext.memo.begin(), plaintext.memo.end());
|
||||
std::cout << "... memo: " << HexStr(data) << std::endl;
|
||||
std::cout << "... amount: " << FormatMoney(plaintext.value, false) << std::endl;
|
||||
|
@ -777,8 +764,8 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() {
|
|||
}
|
||||
|
||||
// sort in descending order, so big notes appear first
|
||||
std::sort(z_inputs_.begin(), z_inputs_.end(), [](SendManyInputNPT i, SendManyInputNPT j) -> bool {
|
||||
return (i.second > j.second);
|
||||
std::sort(z_inputs_.begin(), z_inputs_.end(), [](SendManyInputJSOP i, SendManyInputJSOP j) -> bool {
|
||||
return ( std::get<2>(i) > std::get<2>(j));
|
||||
});
|
||||
|
||||
return true;
|
||||
|
@ -787,14 +774,21 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() {
|
|||
Object AsyncRPCOperation_sendmany::perform_joinsplit(AsyncJoinSplitInfo & info) {
|
||||
std::vector<boost::optional < ZCIncrementalWitness>> witnesses;
|
||||
uint256 anchor;
|
||||
{
|
||||
LOCK2(cs_main, pwalletMain->cs_wallet);
|
||||
anchor = pcoinsTip->GetBestAnchor(); // As there are no inputs, ask the wallet for the best anchor
|
||||
}
|
||||
return perform_joinsplit(info, witnesses, anchor);
|
||||
}
|
||||
|
||||
// Lock critical section (accesses blockchain)
|
||||
|
||||
Object AsyncRPCOperation_sendmany::perform_joinsplit(AsyncJoinSplitInfo & info, std::vector<JSOutPoint> & outPoints) {
|
||||
std::vector<boost::optional < ZCIncrementalWitness>> witnesses;
|
||||
uint256 anchor;
|
||||
{
|
||||
LOCK(cs_main);
|
||||
pwalletMain->WitnessNoteCommitment(info.commitments, witnesses, anchor);
|
||||
pwalletMain->GetNoteWitnesses(outPoints, witnesses, anchor);
|
||||
}
|
||||
// Unlock critical section
|
||||
|
||||
return perform_joinsplit(info, witnesses, anchor);
|
||||
}
|
||||
|
||||
|
@ -803,15 +797,19 @@ Object AsyncRPCOperation_sendmany::perform_joinsplit(
|
|||
std::vector<boost::optional < ZCIncrementalWitness>> witnesses,
|
||||
uint256 anchor)
|
||||
{
|
||||
if (!(witnesses.size() == info.notes.size()) || !(info.notes.size() == info.keys.size())) {
|
||||
throw runtime_error("number of notes and witnesses and keys do not match");
|
||||
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");
|
||||
}
|
||||
|
||||
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.keys[i]));
|
||||
info.vjsin.push_back(JSInput(*witnesses[i], info.notes[i], spendingkey_));
|
||||
}
|
||||
|
||||
// Make sure there are two inputs and two outputs
|
||||
|
@ -829,6 +827,7 @@ Object AsyncRPCOperation_sendmany::perform_joinsplit(
|
|||
|
||||
CMutableTransaction mtx(tx_);
|
||||
|
||||
// TODO: Replace with logging to debug.log
|
||||
#if 1
|
||||
std::cout << "joinsplit chain length = " << tx_.vjoinsplit.size() << std::endl;
|
||||
std::cout << "vpub_old: " << info.vpub_old << std::endl;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "zcash/JoinSplit.hpp"
|
||||
#include "zcash/Address.hpp"
|
||||
#include "json/json_spirit_value.h"
|
||||
#include "wallet.h"
|
||||
|
||||
#include <tuple>
|
||||
|
||||
|
@ -27,17 +28,15 @@ typedef std::tuple<std::string, CAmount, std::string> SendManyRecipient;
|
|||
// Input UTXO is a tuple (quadruple) of txid, vout, amount, coinbase)
|
||||
typedef std::tuple<uint256, int, CAmount, bool> SendManyInputUTXO;
|
||||
|
||||
// Input NPT is a pair of the plaintext note and amount
|
||||
typedef std::pair<NotePlaintext, CAmount> SendManyInputNPT;
|
||||
// Input JSOP is a tuple of JSOutpoint, note and amount
|
||||
typedef std::tuple<JSOutPoint, Note, CAmount> SendManyInputJSOP;
|
||||
|
||||
// Package of info needed to perform a joinsplit
|
||||
// Package of info which is passed to perform_joinsplit methods.
|
||||
struct AsyncJoinSplitInfo
|
||||
{
|
||||
std::vector<JSInput> vjsin;
|
||||
std::vector<JSOutput> vjsout;
|
||||
std::vector<Note> notes;
|
||||
std::vector<SpendingKey> keys;
|
||||
std::vector<uint256> commitments;
|
||||
CAmount vpub_old = 0;
|
||||
CAmount vpub_new = 0;
|
||||
};
|
||||
|
@ -73,7 +72,7 @@ private:
|
|||
std::vector<SendManyRecipient> t_outputs_;
|
||||
std::vector<SendManyRecipient> z_outputs_;
|
||||
std::vector<SendManyInputUTXO> t_inputs_;
|
||||
std::vector<SendManyInputNPT> z_inputs_;
|
||||
std::vector<SendManyInputJSOP> z_inputs_;
|
||||
|
||||
CTransaction tx_;
|
||||
|
||||
|
@ -83,11 +82,19 @@ private:
|
|||
bool find_utxos(bool fAcceptCoinbase);
|
||||
boost::array<unsigned char, ZC_MEMO_SIZE> get_memo_from_hex_string(std::string s);
|
||||
bool main_impl();
|
||||
|
||||
// JoinSplit without any input notes to spend
|
||||
Object perform_joinsplit(AsyncJoinSplitInfo &);
|
||||
|
||||
// JoinSplit with input notes to spend (JSOutPoints))
|
||||
Object perform_joinsplit(AsyncJoinSplitInfo &, std::vector<JSOutPoint> & );
|
||||
|
||||
// JoinSplit where you have the witnesses and anchor
|
||||
Object perform_joinsplit(
|
||||
AsyncJoinSplitInfo & info,
|
||||
std::vector<boost::optional < ZCIncrementalWitness>> witnesses,
|
||||
uint256 anchor);
|
||||
|
||||
void sign_send_raw_transaction(Object obj); // throws exception if there was an error
|
||||
|
||||
};
|
||||
|
|
Loading…
Reference in New Issue