Add transaction size and zaddr output limit checks to z_sendmany.
This commit is contained in:
parent
fbc69d3d33
commit
3920292b43
|
@ -244,6 +244,52 @@ class WalletTest (BitcoinTestFramework):
|
||||||
myvjoinsplits = mytxdetails["vjoinsplit"]
|
myvjoinsplits = mytxdetails["vjoinsplit"]
|
||||||
assert_equal(0, len(myvjoinsplits))
|
assert_equal(0, len(myvjoinsplits))
|
||||||
|
|
||||||
|
# z_sendmany is expected to fail if tx size breaks limit
|
||||||
|
myzaddr = self.nodes[0].z_getnewaddress()
|
||||||
|
|
||||||
|
recipients = []
|
||||||
|
num_t_recipients = 3000
|
||||||
|
amount_per_recipient = Decimal('0.00000001')
|
||||||
|
errorString = ''
|
||||||
|
for i in xrange(0,num_t_recipients):
|
||||||
|
newtaddr = self.nodes[2].getnewaddress()
|
||||||
|
recipients.append({"address":newtaddr, "amount":amount_per_recipient})
|
||||||
|
try:
|
||||||
|
self.nodes[0].z_sendmany(myzaddr, recipients)
|
||||||
|
except JSONRPCException,e:
|
||||||
|
errorString = e.error['message']
|
||||||
|
assert("Too many outputs, size of raw transaction" in errorString)
|
||||||
|
|
||||||
|
recipients = []
|
||||||
|
num_t_recipients = 2000
|
||||||
|
num_z_recipients = 50
|
||||||
|
amount_per_recipient = Decimal('0.00000001')
|
||||||
|
errorString = ''
|
||||||
|
for i in xrange(0,num_t_recipients):
|
||||||
|
newtaddr = self.nodes[2].getnewaddress()
|
||||||
|
recipients.append({"address":newtaddr, "amount":amount_per_recipient})
|
||||||
|
for i in xrange(0,num_z_recipients):
|
||||||
|
newzaddr = self.nodes[2].z_getnewaddress()
|
||||||
|
recipients.append({"address":newzaddr, "amount":amount_per_recipient})
|
||||||
|
try:
|
||||||
|
self.nodes[0].z_sendmany(myzaddr, recipients)
|
||||||
|
except JSONRPCException,e:
|
||||||
|
errorString = e.error['message']
|
||||||
|
assert("size of raw transaction would be larger than limit" in errorString)
|
||||||
|
|
||||||
|
recipients = []
|
||||||
|
num_z_recipients = 100
|
||||||
|
amount_per_recipient = Decimal('0.00000001')
|
||||||
|
errorString = ''
|
||||||
|
for i in xrange(0,num_z_recipients):
|
||||||
|
newzaddr = self.nodes[2].z_getnewaddress()
|
||||||
|
recipients.append({"address":newzaddr, "amount":amount_per_recipient})
|
||||||
|
try:
|
||||||
|
self.nodes[0].z_sendmany(myzaddr, recipients)
|
||||||
|
except JSONRPCException,e:
|
||||||
|
errorString = e.error['message']
|
||||||
|
assert("Invalid parameter, too many zaddr outputs" in errorString)
|
||||||
|
|
||||||
# add zaddr to node 2
|
# add zaddr to node 2
|
||||||
myzaddr = self.nodes[2].z_getnewaddress()
|
myzaddr = self.nodes[2].z_getnewaddress()
|
||||||
|
|
||||||
|
|
|
@ -3166,6 +3166,17 @@ Value z_getoperationstatus_IMPL(const Array& params, bool fRemoveFinishedOperati
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// 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 ((MAX_TX_SIZE / JSDescription().GetSerializeSize(SER_NETWORK, PROTOCOL_VERSION)) - 1)
|
||||||
|
|
||||||
|
// transaction.h comment: spending taddr output requires CTxIn >= 148 bytes and typical taddr txout is 34 bytes
|
||||||
|
#define CTXIN_SPEND_DUST_SIZE 148
|
||||||
|
#define CTXOUT_REGULAR_SIZE 34
|
||||||
|
|
||||||
Value z_sendmany(const Array& params, bool fHelp)
|
Value z_sendmany(const Array& params, bool fHelp)
|
||||||
{
|
{
|
||||||
if (!EnsureWalletIsAvailable(fHelp))
|
if (!EnsureWalletIsAvailable(fHelp))
|
||||||
|
@ -3177,6 +3188,7 @@ Value z_sendmany(const Array& params, bool fHelp)
|
||||||
"\nSend multiple times. Amounts are double-precision floating point numbers."
|
"\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."
|
"\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 alllowed. The entire value of the UTXO(s) must be consumed."
|
"\nWhen sending coinbase UTXOs to a zaddr, change is not alllowed. 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)
|
||||||
+ HelpRequiringPassphrase() + "\n"
|
+ HelpRequiringPassphrase() + "\n"
|
||||||
"\nArguments:\n"
|
"\nArguments:\n"
|
||||||
"1. \"fromaddress\" (string, required) The taddr or zaddr to send the funds from.\n"
|
"1. \"fromaddress\" (string, required) The taddr or zaddr to send the funds from.\n"
|
||||||
|
@ -3284,6 +3296,30 @@ Value z_sendmany(const Array& params, bool fHelp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Check the number of zaddr outputs does not exceed the limit.
|
||||||
|
if (zaddrRecipients.size() > Z_SENDMANY_MAX_ZADDR_OUTPUTS) {
|
||||||
|
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++) {
|
||||||
|
mtx.vjoinsplit.push_back(JSDescription());
|
||||||
|
}
|
||||||
|
CTransaction tx(mtx);
|
||||||
|
txsize += tx.GetSerializeSize(SER_NETWORK, tx.nVersion);
|
||||||
|
if (fromTaddr) {
|
||||||
|
txsize += CTXIN_SPEND_DUST_SIZE;
|
||||||
|
txsize += CTXOUT_REGULAR_SIZE; // There will probably be taddr change
|
||||||
|
}
|
||||||
|
txsize += CTXOUT_REGULAR_SIZE * taddrRecipients.size();
|
||||||
|
if (txsize > MAX_TX_SIZE) {
|
||||||
|
throw JSONRPCError(RPC_INVALID_PARAMETER, strprintf("Too many outputs, size of raw transaction would be larger than limit of %d bytes", MAX_TX_SIZE ));
|
||||||
|
}
|
||||||
|
|
||||||
// Minimum confirmations
|
// Minimum confirmations
|
||||||
int nMinDepth = 1;
|
int nMinDepth = 1;
|
||||||
if (params.size() > 2) {
|
if (params.size() > 2) {
|
||||||
|
|
Loading…
Reference in New Issue