lockzins test finally passes because z_sendmany correctly locks notes now

This commit is contained in:
Duke
2025-08-21 16:59:33 -04:00
parent 8148c4f625
commit eb4fc52273
4 changed files with 91 additions and 23 deletions

View File

@@ -63,11 +63,12 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
std::string fromAddress,
std::vector<SendManyRecipient> tOutputs,
std::vector<SendManyRecipient> zOutputs,
std::vector<SendManyInputSaplingNote> saplingNoteInputs,
int minDepth,
CAmount fee,
UniValue contextInfo,
CScript opret) :
tx_(contextualTx), fromaddress_(fromAddress), t_outputs_(tOutputs), z_outputs_(zOutputs), mindepth_(minDepth), fee_(fee), contextinfo_(contextInfo), opret_(opret)
tx_(contextualTx), fromaddress_(fromAddress), t_outputs_(tOutputs), z_outputs_(zOutputs), saplingNoteInputs_(saplingNoteInputs), mindepth_(minDepth), fee_(fee), contextinfo_(contextInfo), opret_(opret)
{
assert(fee_ >= 0);
@@ -120,6 +121,10 @@ AsyncRPCOperation_sendmany::AsyncRPCOperation_sendmany(
LogPrint("zrpc", "%s: z_sendmany initialized\n", getId());
}
//TODO: lock_utxos() ?
// Lock shielded input notes (zins) stored in saplingNoteInputs
lock_notes();
LogPrintf("%s: %s z_sendmany input notes locked\n", __func__, getId());
}
AsyncRPCOperation_sendmany::~AsyncRPCOperation_sendmany() {
@@ -171,7 +176,7 @@ void AsyncRPCOperation_sendmany::main() {
unlock_notes();
unlock_utxos();
LogPrintf("%s: z_sendmany input notes+utxos unlocked\n", __func__, getId());
LogPrintf("%s: %s z_sendmany input notes+utxos unlocked\n", __func__, getId());
#ifdef ENABLE_MINING
#ifdef ENABLE_WALLET
@@ -204,7 +209,6 @@ void AsyncRPCOperation_sendmany::main() {
// 1. #1159 Currently there is no limit set on the number of shielded spends, so size of tx could be invalid.
// 2. #1277 Spendable notes are not locked, so an operation running in parallel could also try to use them
bool AsyncRPCOperation_sendmany::main_impl() {
assert(isfromtaddr_ != isfromzaddr_);
bool isSingleZaddrOutput = (t_outputs_.size()==0 && z_outputs_.size()==1);
@@ -233,9 +237,11 @@ bool AsyncRPCOperation_sendmany::main_impl() {
}
}
/*
if (isfromzaddr_ && !find_unspent_notes()) {
throw JSONRPCError(RPC_WALLET_INSUFFICIENT_FUNDS, "Insufficient funds, no unspent notes found for zaddr from address.");
}
*/
// Lock UTXOs
lock_utxos();
@@ -246,8 +252,9 @@ bool AsyncRPCOperation_sendmany::main_impl() {
}
CAmount z_inputs_total = 0;
for (auto t : z_sapling_inputs_) {
z_inputs_total += t.note.value();
for (auto t : saplingNoteInputs_) {
//z_inputs_total += t.note.value();
z_inputs_total += std::get<1>(t).value();
}
CAmount t_outputs_total = 0;
@@ -261,8 +268,11 @@ bool AsyncRPCOperation_sendmany::main_impl() {
z_outputs_total += std::get<1>(t);
}
LogPrintf("%s: z_inputs_total=%s z_outputs_total=%s\n", __func__, FormatMoney(z_inputs_total), FormatMoney(z_outputs_total) );
CAmount sendAmount = z_outputs_total + t_outputs_total;
CAmount targetAmount = sendAmount + minersFee;
LogPrintf("%s: targetAmount=%s sendAmount=%s minersFee=%s\n", __func__, FormatMoney(targetAmount), FormatMoney(sendAmount), FormatMoney(minersFee) );
assert(!isfromtaddr_ || z_inputs_total == 0);
assert(!isfromzaddr_ || t_inputs_total == 0);
@@ -410,6 +420,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
std::vector<SaplingNote> notes;
CAmount sum = 0;
/*
//NOTE: z_sapling_inputs_ is a list of all potential notes to spend
// saplingNoteInputs_ is a list of notes we will actually spend
// and need to lock. It is a subset of z_sapling_inputs_
@@ -431,9 +442,11 @@ bool AsyncRPCOperation_sendmany::main_impl() {
break;
}
}
*/
// Lock shielded input notes (zins) stored in saplingNoteInputs
lock_notes();
for(const auto t : saplingNoteInputs_) {
ops.push_back(std::get<0>(t));
}
// Fetch Sapling anchor and witnesses
//LogPrintf("%s: Gathering anchors and witnesses\n", __FUNCTION__);
@@ -444,14 +457,30 @@ bool AsyncRPCOperation_sendmany::main_impl() {
pwalletMain->GetSaplingNoteWitnesses(ops, witnesses, anchor);
}
LogPrintf("%s: ops.size=%d witnesses.size=%d\n", __func__, ops.size(), witnesses.size() );
// Add Sapling spends
for (size_t i = 0; i < notes.size(); i++) {
//TODO: should be using saplingNoteInputs_
for (size_t i = 0; i < saplingNoteInputs_.size(); i++) {
//LOCK2(cs_main, pwalletMain->cs_wallet);
if (!witnesses[i]) {
throw JSONRPCError(RPC_WALLET_ERROR,
strprintf( "Missing witness for Sapling note at outpoint %s", z_sapling_inputs_[i].op.ToString())
//strprintf( "Missing witness for Sapling note at outpoint %s", saplingNoteInputs_[i].op.ToString())
strprintf( "Missing witness for Sapling note at outpoint %s", std::get<0>(saplingNoteInputs_[i]).ToString())
);
}
assert(builder_.AddSaplingSpend(expsk, notes[i], anchor, witnesses[i].get()));
if(fZdebug)
LogPrintf("%s: Adding Sapling spend\n", __func__);
assert(builder_.AddSaplingSpend(expsk, std::get<1>(saplingNoteInputs_[i]), anchor, witnesses[i].get()));
/*
// notes we are currently spending should be locked
if(pwalletMain->IsLockedNote(ops[i])) {
} else {
throw JSONRPCError(RPC_WALLET_ERROR, "Note we are spending is not locked!" );
}
*/
}
// Add Sapling outputs
@@ -463,7 +492,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
assert(boost::get<libzcash::SaplingPaymentAddress>(&addr) != nullptr);
auto to = boost::get<libzcash::SaplingPaymentAddress>(addr);
if(fZdebug)
LogPrintf("%s: Adding Sapling output to address %s\n", __FUNCTION__, address.c_str());
LogPrintf("%s: Adding Sapling output with value=%s to address %s\n", __func__, FormatMoney(value), address.c_str());
auto memo = get_memo_from_hex_string(hexMemo);
@@ -497,6 +526,7 @@ bool AsyncRPCOperation_sendmany::main_impl() {
// Send the transaction
// TODO: Use CWallet::CommitTransaction instead of sendrawtransaction
auto signedtxn = EncodeHexTx(tx_);
if (!testmode) {
UniValue params = UniValue(UniValue::VARR);
params.push_back(signedtxn);
@@ -668,6 +698,7 @@ bool AsyncRPCOperation_sendmany::find_utxos(bool fAcceptCoinbase=false) {
return t_inputs_.size() > 0;
}
/*
// find unspent notes which are also unlocked
bool AsyncRPCOperation_sendmany::find_unspent_notes() {
if(fZdebug)
@@ -708,6 +739,7 @@ bool AsyncRPCOperation_sendmany::find_unspent_notes() {
return true;
}
*/
void AsyncRPCOperation_sendmany::add_taddr_outputs_to_tx() {
@@ -828,17 +860,27 @@ void AsyncRPCOperation_sendmany::unlock_utxos() {
// Lock input notes
void AsyncRPCOperation_sendmany::lock_notes() {
LOCK2(cs_main, pwalletMain->cs_wallet);
fprintf(stderr,"%s: found %lu notes to lock\n", __func__, saplingNoteInputs_.size() );
LogPrintf("%s: found %lu notes to lock\n", __func__, saplingNoteInputs_.size() );
for (auto note : saplingNoteInputs_) {
pwalletMain->LockNote(std::get<0>(note));
if(pwalletMain->IsLockedNote(std::get<0>(note))) {
//TODO: deal with this
LogPrintf("%s: note already locked!\n", __func__);
} else {
pwalletMain->LockNote(std::get<0>(note));
}
}
}
// Unlock input notes
void AsyncRPCOperation_sendmany::unlock_notes() {
LOCK2(cs_main, pwalletMain->cs_wallet);
fprintf(stderr,"%s: found %lu notes to unlock\n", __func__, saplingNoteInputs_.size() );
LogPrintf("%s: found %lu notes to unlock\n", __func__, saplingNoteInputs_.size() );
for (auto note : saplingNoteInputs_) {
pwalletMain->UnlockNote(std::get<0>(note));
if(pwalletMain->IsLockedNote(std::get<0>(note))) {
//TODO: deal with this
pwalletMain->UnlockNote(std::get<0>(note));
} else {
LogPrintf("%s: note already unlocked!\n", __func__);
}
}
}