Rework z_sendmany z-address recipient limit

From Sapling, the z-address recipients could require either JSDescriptions or
OutputDescriptions. Instead of trying to give an exact number in the help text,
rely on transaction size estimation to guide user behaviour.
This commit is contained in:
Jack Grigg 2018-05-01 11:23:11 +01:00
parent 25fee3509a
commit 892ae945f7
No known key found for this signature in database
GPG Key ID: 665DBCD284F7DAFF
1 changed files with 22 additions and 12 deletions

View File

@ -3485,12 +3485,13 @@ UniValue z_getoperationstatus_IMPL(const UniValue& params, bool fRemoveFinishedO
}
// JSDescription size depends on the transaction version
#define V3_JS_DESCRIPTION_SIZE (GetSerializeSize(JSDescription(), SER_NETWORK, (OVERWINTER_TX_VERSION | (1 << 31))))
// Here we define the maximum number of zaddr outputs that can be included in a transaction.
// If input notes are small, we might actually require more than one joinsplit per zaddr output.
// For now though, we assume we use one joinsplit per zaddr output (and the second output note is change).
// We reduce the result by 1 to ensure there is room for non-joinsplit CTransaction data.
#define Z_SENDMANY_MAX_ZADDR_OUTPUTS_BEFORE_SAPLING ((MAX_TX_SIZE_BEFORE_SAPLING / GetSerializeSize(JSDescription(), SER_NETWORK, PROTOCOL_VERSION)) - 1)
#define Z_SENDMANY_MAX_ZADDR_OUTPUTS ((MAX_TX_SIZE_AFTER_SAPLING / GetSerializeSize(JSDescription(), SER_NETWORK, PROTOCOL_VERSION)) - 1)
#define Z_SENDMANY_MAX_ZADDR_OUTPUTS_BEFORE_SAPLING ((MAX_TX_SIZE_BEFORE_SAPLING / V3_JS_DESCRIPTION_SIZE) - 1)
// transaction.h comment: spending taddr output requires CTxIn >= 148 bytes and typical taddr txout is 34 bytes
#define CTXIN_SPEND_DUST_SIZE 148
@ -3507,7 +3508,7 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
"\nSend multiple times. Amounts are double-precision floating point numbers."
"\nChange from a taddr flows to a new taddr address, while change from zaddr returns to itself."
"\nWhen sending coinbase UTXOs to a zaddr, change is not allowed. The entire value of the UTXO(s) must be consumed."
+ strprintf("\nCurrently, the maximum number of zaddr outputs is %d due to transaction size limits.\n", Z_SENDMANY_MAX_ZADDR_OUTPUTS)
+ strprintf("\nBefore Sapling activates, the maximum number of zaddr outputs is %d due to transaction size limits.\n", Z_SENDMANY_MAX_ZADDR_OUTPUTS_BEFORE_SAPLING)
+ HelpRequiringPassphrase() + "\n"
"\nArguments:\n"
"1. \"fromaddress\" (string, required) The taddr or zaddr to send the funds from.\n"
@ -3622,24 +3623,33 @@ UniValue z_sendmany(const UniValue& params, bool fHelp)
}
int nextBlockHeight = chainActive.Height() + 1;
size_t max_zaddr_outputs = Z_SENDMANY_MAX_ZADDR_OUTPUTS;
CMutableTransaction mtx;
mtx.fOverwintered = true;
mtx.nVersionGroupId = SAPLING_VERSION_GROUP_ID;
mtx.nVersion = SAPLING_TX_VERSION;
unsigned int max_tx_size = MAX_TX_SIZE_AFTER_SAPLING;
if (!NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_SAPLING)) {
max_zaddr_outputs = Z_SENDMANY_MAX_ZADDR_OUTPUTS_BEFORE_SAPLING;
max_tx_size = MAX_TX_SIZE_BEFORE_SAPLING;
}
if (NetworkUpgradeActive(nextBlockHeight, Params().GetConsensus(), Consensus::UPGRADE_OVERWINTER)) {
mtx.nVersionGroupId = OVERWINTER_VERSION_GROUP_ID;
mtx.nVersion = OVERWINTER_TX_VERSION;
} else {
mtx.fOverwintered = false;
mtx.nVersion = 2;
}
// Check the number of zaddr outputs does not exceed the limit.
if (zaddrRecipients.size() > max_zaddr_outputs) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, too many zaddr outputs");
max_tx_size = MAX_TX_SIZE_BEFORE_SAPLING;
// Check the number of zaddr outputs does not exceed the limit.
if (zaddrRecipients.size() > Z_SENDMANY_MAX_ZADDR_OUTPUTS_BEFORE_SAPLING) {
throw JSONRPCError(RPC_INVALID_PARAMETER, "Invalid parameter, too many zaddr outputs");
}
}
// As a sanity check, estimate and verify that the size of the transaction will be valid.
// Depending on the input notes, the actual tx size may turn out to be larger and perhaps invalid.
size_t txsize = 0;
CMutableTransaction mtx;
mtx.nVersion = 2;
for (int i = 0; i < zaddrRecipients.size(); i++) {
// TODO Check whether the recipient is a Sprout or Sapling address
mtx.vjoinsplit.push_back(JSDescription());
}
CTransaction tx(mtx);