wallet: extract ParseSendManyRecipients from z_sendmany
Second slice of the z_sendmany split. The ~60-line loop that parses and validates the "outputs" array — rejecting unknown keys, bad addresses, memos on taddrs, oversize memos and negative amounts, and sorting recipients into taddr/zaddr lists while accumulating nTotalOut — is lifted verbatim into ParseSendManyRecipients(outputs, branchId, taddrRecipients, zaddrRecipients, nTotalOut). Verbatim extract-method (the loop text was moved unchanged, not retyped); the helper reads only outputs + branchId and writes the three by-reference outputs the loop already produced, which the clean compile proves self-contained (a reference to any other z_sendmany local would fail to link). Built clean; verifychain=true. Together with the SelectAnyZaddrSource slice, z_sendmany is now ~135 lines shorter, with source-address resolution and recipient parsing separated out. Remaining: note selection, Sietch padding, and tx assembly (later slices, best validated against a synced node since z_sendmany's linkability guard blocks the send path on a peerless node). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -5189,6 +5189,77 @@ static std::string SelectAnyZaddrSource(const UniValue& outputs, const UniValue&
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "No single zaddr currently has enough funds to make that transaction, you may need to wait for confirmations.");
|
||||
}
|
||||
|
||||
// Parse and validate the z_sendmany "outputs" array into taddr/zaddr recipient lists
|
||||
// (accumulating nTotalOut). Extracted verbatim from z_sendmany; throws JSONRPCError on
|
||||
// any malformed entry (unknown key, bad address, memo misuse/oversize, negative amount).
|
||||
static void ParseSendManyRecipients(const UniValue& outputs, uint32_t branchId,
|
||||
std::vector<SendManyRecipient>& taddrRecipients,
|
||||
std::vector<SendManyRecipient>& zaddrRecipients,
|
||||
CAmount& nTotalOut)
|
||||
{
|
||||
for (const UniValue& o : outputs.getValues()) {
|
||||
if (!o.isObject())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object");
|
||||
|
||||
// sanity check, report error if unknown key-value pairs
|
||||
for (const string& name_ : o.getKeys()) {
|
||||
std::string s = name_;
|
||||
if (s != "address" && s != "amount" && s!="memo") {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown key: ")+s);
|
||||
}
|
||||
}
|
||||
|
||||
string address = find_value(o, "address").get_str();
|
||||
bool isZaddr = false;
|
||||
CTxDestination taddr = DecodeDestination(address);
|
||||
if (!IsValidDestination(taddr)) {
|
||||
auto res = DecodePaymentAddress(address);
|
||||
if (IsValidPaymentAddress(res, branchId)) {
|
||||
isZaddr = true;
|
||||
} else {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ")+address );
|
||||
}
|
||||
}// else if ( ASSETCHAINS_PRIVATE != 0 && hush_isnotaryvout((char *)address.c_str()) == 0 ) {
|
||||
// throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Extreme Privacy! You must send to a zaddr");
|
||||
//}
|
||||
|
||||
UniValue memoValue = find_value(o, "memo");
|
||||
string memo;
|
||||
if (!memoValue.isNull()) {
|
||||
memo = memoValue.get_str();
|
||||
if (!isZaddr) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo cannot be used with a taddr. It can only be used with a zaddr.");
|
||||
} else if(memo.substr(0,5) == "utf8:") {
|
||||
// Support a prefix "utf8:" which allows giving utf8 text instead of hex
|
||||
auto str = memo.substr(5);
|
||||
if (utf8::is_valid(str)) {
|
||||
memo = HexStr(str);
|
||||
} else {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid utf8 in memo");
|
||||
}
|
||||
} else if (!IsHex(memo)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format or to use 'utf8:' prefix.");
|
||||
}
|
||||
if (memo.length() > HUSH_MEMO_SIZE*2) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", HUSH_MEMO_SIZE ));
|
||||
}
|
||||
}
|
||||
|
||||
UniValue av = find_value(o, "amount");
|
||||
CAmount nAmount = AmountFromValue( av );
|
||||
if (nAmount < 0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amount must be positive");
|
||||
|
||||
if (isZaddr) {
|
||||
zaddrRecipients.push_back( SendManyRecipient(address, nAmount, memo) );
|
||||
} else {
|
||||
taddrRecipients.push_back( SendManyRecipient(address, nAmount, memo) );
|
||||
}
|
||||
|
||||
nTotalOut += nAmount;
|
||||
}
|
||||
}
|
||||
|
||||
UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
||||
{
|
||||
if (!EnsureWalletIsAvailable(fHelp))
|
||||
@@ -5296,67 +5367,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp, const CPubKey& mypk)
|
||||
opret << OP_RETURN << ParseHex(opretValue.get_str().c_str());
|
||||
}
|
||||
|
||||
for (const UniValue& o : outputs.getValues()) {
|
||||
if (!o.isObject())
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object");
|
||||
|
||||
// sanity check, report error if unknown key-value pairs
|
||||
for (const string& name_ : o.getKeys()) {
|
||||
std::string s = name_;
|
||||
if (s != "address" && s != "amount" && s!="memo") {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown key: ")+s);
|
||||
}
|
||||
}
|
||||
|
||||
string address = find_value(o, "address").get_str();
|
||||
bool isZaddr = false;
|
||||
CTxDestination taddr = DecodeDestination(address);
|
||||
if (!IsValidDestination(taddr)) {
|
||||
auto res = DecodePaymentAddress(address);
|
||||
if (IsValidPaymentAddress(res, branchId)) {
|
||||
isZaddr = true;
|
||||
} else {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, string("Invalid parameter, unknown address format: ")+address );
|
||||
}
|
||||
}// else if ( ASSETCHAINS_PRIVATE != 0 && hush_isnotaryvout((char *)address.c_str()) == 0 ) {
|
||||
// throw JSONRPCError(RPC_INVALID_ADDRESS_OR_KEY, "Extreme Privacy! You must send to a zaddr");
|
||||
//}
|
||||
|
||||
UniValue memoValue = find_value(o, "memo");
|
||||
string memo;
|
||||
if (!memoValue.isNull()) {
|
||||
memo = memoValue.get_str();
|
||||
if (!isZaddr) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Memo cannot be used with a taddr. It can only be used with a zaddr.");
|
||||
} else if(memo.substr(0,5) == "utf8:") {
|
||||
// Support a prefix "utf8:" which allows giving utf8 text instead of hex
|
||||
auto str = memo.substr(5);
|
||||
if (utf8::is_valid(str)) {
|
||||
memo = HexStr(str);
|
||||
} else {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid utf8 in memo");
|
||||
}
|
||||
} else if (!IsHex(memo)) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected memo data in hexadecimal format or to use 'utf8:' prefix.");
|
||||
}
|
||||
if (memo.length() > HUSH_MEMO_SIZE*2) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Invalid parameter, size of memo is larger than maximum allowed %d", HUSH_MEMO_SIZE ));
|
||||
}
|
||||
}
|
||||
|
||||
UniValue av = find_value(o, "amount");
|
||||
CAmount nAmount = AmountFromValue( av );
|
||||
if (nAmount < 0)
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, amount must be positive");
|
||||
|
||||
if (isZaddr) {
|
||||
zaddrRecipients.push_back( SendManyRecipient(address, nAmount, memo) );
|
||||
} else {
|
||||
taddrRecipients.push_back( SendManyRecipient(address, nAmount, memo) );
|
||||
}
|
||||
|
||||
nTotalOut += nAmount;
|
||||
}
|
||||
ParseSendManyRecipients(outputs, branchId, taddrRecipients, zaddrRecipients, nTotalOut);
|
||||
|
||||
std::vector<SaplingNoteEntry> saplingEntries;
|
||||
// find all unspent and unlocked notes in this zaddr
|
||||
|
||||
Reference in New Issue
Block a user