Merge pull request #5919 from nuttycom/feature/z_sendmany_tx_limit
Add -orchardactionlimit parameter to guard against memory exhaustion.
This commit is contained in:
commit
8ca33e06f7
|
@ -61,6 +61,12 @@ Option handling
|
||||||
to specify the number of blocks back from the chain tip that anchors will be
|
to specify the number of blocks back from the chain tip that anchors will be
|
||||||
selected from when spending notes. By default, anchors will now be selected
|
selected from when spending notes. By default, anchors will now be selected
|
||||||
to have 3 confirmations. Values greater than 100 are not supported.
|
to have 3 confirmations. Values greater than 100 are not supported.
|
||||||
|
- A new `-orchardactionlimit` option has been added to allow the user to
|
||||||
|
override the default maximum of 50 Orchard actions per transaction.
|
||||||
|
Transactions that contain large numbers of Orchard actions can use
|
||||||
|
large amounts of memory for proving, so the 50-action default limit is
|
||||||
|
imposed to guard against memory exhaustion. Systems with more than 16G
|
||||||
|
of memory can safely set this parameter to allow 200 actions or more.
|
||||||
|
|
||||||
RPC Interface
|
RPC Interface
|
||||||
-------------
|
-------------
|
||||||
|
|
|
@ -290,9 +290,23 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
||||||
"Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` "
|
"Resubmit with the `privacyPolicy` parameter set to `AllowRevealedAmounts` "
|
||||||
"or weaker if you wish to allow this transaction to proceed anyway.");
|
"or weaker if you wish to allow this transaction to proceed anyway.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sending from Orchard to transparent will be caught above in the
|
// Sending from Orchard to transparent will be caught above in the
|
||||||
// AllowRevealedRecipients check; sending to Sprout is disallowed
|
// AllowRevealedRecipients check; sending to Sprout is disallowed
|
||||||
// entirely.
|
// entirely.
|
||||||
|
|
||||||
|
if (spendable.orchardNoteMetadata.size() > nOrchardActionLimit) {
|
||||||
|
throw JSONRPCError(
|
||||||
|
RPC_INVALID_PARAMETER,
|
||||||
|
strprintf(
|
||||||
|
"Attempting to spend %u Orchard notes would exceed the current limit "
|
||||||
|
"of %u notes, which exists to prevent memory exhaustion. Restart with "
|
||||||
|
"`-orchardactionlimit=N` where N >= %u to allow the wallet to attempt "
|
||||||
|
"to construct this transaction.",
|
||||||
|
spendable.orchardNoteMetadata.size(),
|
||||||
|
nOrchardActionLimit,
|
||||||
|
spendable.orchardNoteMetadata.size()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
spendable.LogInputs(getId());
|
spendable.LogInputs(getId());
|
||||||
|
@ -553,7 +567,6 @@ uint256 AsyncRPCOperation_sendmany::main_impl() {
|
||||||
[&](const libzcash::OrchardRawAddress& addr) {
|
[&](const libzcash::OrchardRawAddress& addr) {
|
||||||
auto value = r.amount;
|
auto value = r.amount;
|
||||||
auto memo = r.memo.has_value() ? std::optional(get_memo_from_hex_string(r.memo.value())) : std::nullopt;
|
auto memo = r.memo.has_value() ? std::optional(get_memo_from_hex_string(r.memo.value())) : std::nullopt;
|
||||||
|
|
||||||
builder_.AddOrchardOutput(ovks.second, addr, value, memo);
|
builder_.AddOrchardOutput(ovks.second, addr, value, memo);
|
||||||
}
|
}
|
||||||
}, r.address);
|
}, r.address);
|
||||||
|
|
|
@ -5064,6 +5064,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
|
||||||
std::set<RecipientAddress> recipientAddrs;
|
std::set<RecipientAddress> recipientAddrs;
|
||||||
std::vector<SendManyRecipient> recipients;
|
std::vector<SendManyRecipient> recipients;
|
||||||
CAmount nTotalOut = 0;
|
CAmount nTotalOut = 0;
|
||||||
|
size_t nOrchardOutputs = 0;
|
||||||
for (const UniValue& o : outputs.getValues()) {
|
for (const UniValue& o : outputs.getValues()) {
|
||||||
if (!o.isObject())
|
if (!o.isObject())
|
||||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object");
|
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, expected object");
|
||||||
|
@ -5129,6 +5130,22 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
|
||||||
involvesUnifiedAddress = true;
|
involvesUnifiedAddress = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (std::holds_alternative<libzcash::OrchardRawAddress>(addr.value())) {
|
||||||
|
nOrchardOutputs += 1;
|
||||||
|
if (nOrchardOutputs > nOrchardActionLimit) {
|
||||||
|
throw JSONRPCError(
|
||||||
|
RPC_INVALID_PARAMETER,
|
||||||
|
strprintf(
|
||||||
|
"Attempting to create %u Orchard outputs would exceed the current limit "
|
||||||
|
"of %u notes, which exists to prevent memory exhaustion. Restart with "
|
||||||
|
"`-orchardactionlimit=N` where N >= %u to allow the wallet to attempt "
|
||||||
|
"to construct this transaction.",
|
||||||
|
nOrchardOutputs,
|
||||||
|
nOrchardActionLimit,
|
||||||
|
nOrchardOutputs));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
recipients.push_back(SendManyRecipient(ua, addr.value(), nAmount, memo));
|
recipients.push_back(SendManyRecipient(ua, addr.value(), nAmount, memo));
|
||||||
nTotalOut += nAmount;
|
nTotalOut += nAmount;
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,6 +52,7 @@ bool bSpendZeroConfChange = DEFAULT_SPEND_ZEROCONF_CHANGE;
|
||||||
bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS;
|
bool fSendFreeTransactions = DEFAULT_SEND_FREE_TRANSACTIONS;
|
||||||
bool fPayAtLeastCustomFee = true;
|
bool fPayAtLeastCustomFee = true;
|
||||||
unsigned int nAnchorConfirmations = DEFAULT_ANCHOR_CONFIRMATIONS;
|
unsigned int nAnchorConfirmations = DEFAULT_ANCHOR_CONFIRMATIONS;
|
||||||
|
unsigned int nOrchardActionLimit = DEFAULT_ORCHARD_ACTION_LIMIT;
|
||||||
|
|
||||||
const char * DEFAULT_WALLET_DAT = "wallet.dat";
|
const char * DEFAULT_WALLET_DAT = "wallet.dat";
|
||||||
|
|
||||||
|
@ -6333,6 +6334,7 @@ std::string CWallet::GetWalletHelpString(bool showDebug)
|
||||||
strUsage += HelpMessageOpt("-migrationdestaddress=<zaddr>", _("Set the Sapling migration address"));
|
strUsage += HelpMessageOpt("-migrationdestaddress=<zaddr>", _("Set the Sapling migration address"));
|
||||||
strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"),
|
strUsage += HelpMessageOpt("-mintxfee=<amt>", strprintf(_("Fees (in %s/kB) smaller than this are considered zero fee for transaction creation (default: %s)"),
|
||||||
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)));
|
CURRENCY_UNIT, FormatMoney(DEFAULT_TRANSACTION_MINFEE)));
|
||||||
|
strUsage += HelpMessageOpt("-orchardactionlimit=<n>", strprintf(_("Set the maximum number of Orchard actions permitted in a transaction (default %u)"), DEFAULT_ORCHARD_ACTION_LIMIT));
|
||||||
strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"),
|
strUsage += HelpMessageOpt("-paytxfee=<amt>", strprintf(_("Fee (in %s/kB) to add to transactions you send (default: %s)"),
|
||||||
CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK())));
|
CURRENCY_UNIT, FormatMoney(payTxFee.GetFeePerK())));
|
||||||
strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup"));
|
strUsage += HelpMessageOpt("-rescan", _("Rescan the block chain for missing wallet transactions on startup"));
|
||||||
|
@ -6603,6 +6605,13 @@ bool CWallet::ParameterInteraction(const CChainParams& params)
|
||||||
}
|
}
|
||||||
nAnchorConfirmations = confirmations;
|
nAnchorConfirmations = confirmations;
|
||||||
}
|
}
|
||||||
|
if (mapArgs.count("-orchardactionlimit")) {
|
||||||
|
int64_t limit = atoi64(mapArgs["-orchardactionlimit"]);
|
||||||
|
if (limit < 1) {
|
||||||
|
return UIError(strprintf(_("Invalid value for -orchardactionlimit='%u' (must be least 1)"), limit));
|
||||||
|
}
|
||||||
|
nOrchardActionLimit = limit;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,6 +54,9 @@ extern bool bSpendZeroConfChange;
|
||||||
extern bool fSendFreeTransactions;
|
extern bool fSendFreeTransactions;
|
||||||
extern bool fPayAtLeastCustomFee;
|
extern bool fPayAtLeastCustomFee;
|
||||||
extern unsigned int nAnchorConfirmations;
|
extern unsigned int nAnchorConfirmations;
|
||||||
|
// The maximum number of Orchard actions permitted within a single transaction.
|
||||||
|
// This can be overridden with the -orchardactionlimit config option
|
||||||
|
extern unsigned int nOrchardActionLimit;
|
||||||
|
|
||||||
static const unsigned int DEFAULT_KEYPOOL_SIZE = 100;
|
static const unsigned int DEFAULT_KEYPOOL_SIZE = 100;
|
||||||
//! -paytxfee default
|
//! -paytxfee default
|
||||||
|
@ -82,6 +85,8 @@ static const size_t WALLET_MNEMONIC_ENTROPY_LENGTH = 32;
|
||||||
static const unsigned int DEFAULT_ANCHOR_CONFIRMATIONS = 3;
|
static const unsigned int DEFAULT_ANCHOR_CONFIRMATIONS = 3;
|
||||||
// Default minimum number of confirmations for note selection
|
// Default minimum number of confirmations for note selection
|
||||||
static const unsigned int DEFAULT_NOTE_CONFIRMATIONS = 10;
|
static const unsigned int DEFAULT_NOTE_CONFIRMATIONS = 10;
|
||||||
|
//! -orchardactionlimit default
|
||||||
|
static const unsigned int DEFAULT_ORCHARD_ACTION_LIMIT = 50;
|
||||||
|
|
||||||
extern const char * DEFAULT_WALLET_DAT;
|
extern const char * DEFAULT_WALLET_DAT;
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue