wallet/rpc: add maxfeerate parameter to sendrawtransaction
This commit is contained in:
parent
e5efacb941
commit
6c0a6f73e3
13 changed files with 126 additions and 70 deletions
|
@ -92,6 +92,7 @@ static const CRPCConvertParam vRPCConvertParams[] =
|
|||
{ "signrawtransactionwithkey", 2, "prevtxs" },
|
||||
{ "signrawtransactionwithwallet", 1, "prevtxs" },
|
||||
{ "sendrawtransaction", 1, "allowhighfees" },
|
||||
{ "sendrawtransaction", 1, "maxfeerate" },
|
||||
{ "testmempoolaccept", 0, "rawtxs" },
|
||||
{ "testmempoolaccept", 1, "allowhighfees" },
|
||||
{ "combinerawtransaction", 0, "txs" },
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
#include <script/standard.h>
|
||||
#include <uint256.h>
|
||||
#include <util/bip32.h>
|
||||
#include <util/moneystr.h>
|
||||
#include <util/strencodings.h>
|
||||
#include <validation.h>
|
||||
#include <validationinterface.h>
|
||||
|
@ -1039,7 +1040,7 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
|
|||
"\nAlso see createrawtransaction and signrawtransactionwithkey calls.\n",
|
||||
{
|
||||
{"hexstring", RPCArg::Type::STR_HEX, RPCArg::Optional::NO, "The hex string of the raw transaction"},
|
||||
{"allowhighfees", RPCArg::Type::BOOL, /* default */ "false", "Allow high fees"},
|
||||
{"maxfeerate", RPCArg::Type::AMOUNT, /* default */ FormatMoney(maxTxFee), "Reject transactions whose fee rate is higher than the specified value, expressed in " + CURRENCY_UNIT + "/kB\n"},
|
||||
},
|
||||
RPCResult{
|
||||
"\"hex\" (string) The transaction hash in hex\n"
|
||||
|
@ -1056,7 +1057,10 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
|
|||
},
|
||||
}.ToString());
|
||||
|
||||
RPCTypeCheck(request.params, {UniValue::VSTR, UniValue::VBOOL});
|
||||
RPCTypeCheck(request.params, {
|
||||
UniValue::VSTR,
|
||||
UniValueType(), // NUM or BOOL, checked later
|
||||
});
|
||||
|
||||
// parse hex string from parameter
|
||||
CMutableTransaction mtx;
|
||||
|
@ -1064,12 +1068,24 @@ static UniValue sendrawtransaction(const JSONRPCRequest& request)
|
|||
throw JSONRPCError(RPC_DESERIALIZATION_ERROR, "TX decode failed");
|
||||
CTransactionRef tx(MakeTransactionRef(std::move(mtx)));
|
||||
|
||||
bool allowhighfees = false;
|
||||
if (!request.params[1].isNull()) allowhighfees = request.params[1].get_bool();
|
||||
const CAmount highfee{allowhighfees ? 0 : ::maxTxFee};
|
||||
CAmount max_raw_tx_fee = maxTxFee;
|
||||
// TODO: temporary migration code for old clients. Remove in v0.20
|
||||
if (request.params[1].isBool()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "Second argument must be numeric (maxfeerate) and no longer supports a boolean. To allow a transaction with high fees, set maxfeerate to 0.");
|
||||
} else if (request.params[1].isNum()) {
|
||||
size_t weight = GetTransactionWeight(*tx);
|
||||
CFeeRate fr(AmountFromValue(request.params[1]));
|
||||
// the +3/4 part rounds the value up, and is the same formula used when
|
||||
// calculating the fee for a transaction
|
||||
// (see GetVirtualTransactionSize)
|
||||
max_raw_tx_fee = fr.GetFee((weight+3)/4);
|
||||
} else if (!request.params[1].isNull()) {
|
||||
throw JSONRPCError(RPC_INVALID_PARAMETER, "second argument (maxfeerate) must be numeric");
|
||||
}
|
||||
|
||||
uint256 txid;
|
||||
std::string err_string;
|
||||
const TransactionError err = BroadcastTransaction(tx, txid, err_string, highfee);
|
||||
const TransactionError err = BroadcastTransaction(tx, txid, err_string, max_raw_tx_fee);
|
||||
if (TransactionError::OK != err) {
|
||||
throw JSONRPCTransactionError(err, err_string);
|
||||
}
|
||||
|
@ -2048,7 +2064,7 @@ static const CRPCCommand commands[] =
|
|||
{ "rawtransactions", "createrawtransaction", &createrawtransaction, {"inputs","outputs","locktime","replaceable"} },
|
||||
{ "rawtransactions", "decoderawtransaction", &decoderawtransaction, {"hexstring","iswitness"} },
|
||||
{ "rawtransactions", "decodescript", &decodescript, {"hexstring"} },
|
||||
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees"} },
|
||||
{ "rawtransactions", "sendrawtransaction", &sendrawtransaction, {"hexstring","allowhighfees|maxfeerate"} },
|
||||
{ "rawtransactions", "combinerawtransaction", &combinerawtransaction, {"txs"} },
|
||||
{ "hidden", "signrawtransaction", &signrawtransaction, {"hexstring","prevtxs","privkeys","sighashtype"} },
|
||||
{ "rawtransactions", "signrawtransactionwithkey", &signrawtransactionwithkey, {"hexstring","privkeys","prevtxs","sighashtype"} },
|
||||
|
|
|
@ -3130,6 +3130,26 @@ static int GetWitnessCommitmentIndex(const CBlock& block)
|
|||
return commitpos;
|
||||
}
|
||||
|
||||
// Compute at which vout of the block's coinbase transaction the signet
|
||||
// signature occurs, or -1 if not found.
|
||||
static int GetSignetSignatureIndex(const CBlock& block)
|
||||
{
|
||||
if (!block.vtx.empty()) {
|
||||
for (size_t o = 0; o < block.vtx[0]->vout.size(); o++) {
|
||||
if (block.vtx[0]->vout[o].scriptPubKey.size() >= 68 // at minimum 64 byte signature plus script/header data
|
||||
&& block.vtx[0]->vout[o].scriptPubKey[0] == OP_RETURN // unspendable
|
||||
&& block.vtx[0]->vout[o].scriptPubKey[1] == block.vtx[0]->vout[o].scriptPubKey.size() - 1 // push the rest
|
||||
&& block.vtx[0]->vout[o].scriptPubKey[2] == 0xec // 's' ^ 0x9f
|
||||
&& block.vtx[0]->vout[o].scriptPubKey[3] == 0xc7 // 'i' ^ 0xae
|
||||
&& block.vtx[0]->vout[o].scriptPubKey[4] == 0xda // 'g' ^ 0xbd
|
||||
&& block.vtx[0]->vout[o].scriptPubKey[5] == 0xa2) { // 'n' ^ 0xcc
|
||||
return (int)o;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void UpdateUncommittedBlockStructures(CBlock& block, const CBlockIndex* pindexPrev, const Consensus::Params& consensusParams)
|
||||
{
|
||||
int commitpos = GetWitnessCommitmentIndex(block);
|
||||
|
|
|
@ -65,7 +65,7 @@ def small_txpuzzle_randfee(from_node, conflist, unconflist, amount, min_fee, fee
|
|||
# the ScriptSig that will satisfy the ScriptPubKey.
|
||||
for inp in tx.vin:
|
||||
inp.scriptSig = SCRIPT_SIG[inp.prevout.n]
|
||||
txid = from_node.sendrawtransaction(ToHex(tx), True)
|
||||
txid = from_node.sendrawtransaction(hexstring=ToHex(tx), maxfeerate=0)
|
||||
unconflist.append({"txid": txid, "vout": 0, "amount": total_in - amount - fee})
|
||||
unconflist.append({"txid": txid, "vout": 1, "amount": amount})
|
||||
|
||||
|
@ -95,7 +95,7 @@ def split_inputs(from_node, txins, txouts, initial_split=False):
|
|||
else:
|
||||
tx.vin[0].scriptSig = SCRIPT_SIG[prevtxout["vout"]]
|
||||
completetx = ToHex(tx)
|
||||
txid = from_node.sendrawtransaction(completetx, True)
|
||||
txid = from_node.sendrawtransaction(hexstring=completetx, maxfeerate=0)
|
||||
txouts.append({"txid": txid, "vout": 0, "amount": half_change})
|
||||
txouts.append({"txid": txid, "vout": 1, "amount": rem_change})
|
||||
|
||||
|
|
|
@ -64,17 +64,17 @@ class NULLDUMMYTest(BitcoinTestFramework):
|
|||
|
||||
self.log.info("Test 1: NULLDUMMY compliant base transactions should be accepted to mempool and mined before activation [430]")
|
||||
test1txs = [create_transaction(self.nodes[0], coinbase_txid[0], self.ms_address, amount=49)]
|
||||
txid1 = self.nodes[0].sendrawtransaction(test1txs[0].serialize_with_witness().hex(), True)
|
||||
txid1 = self.nodes[0].sendrawtransaction(test1txs[0].serialize_with_witness().hex(), 0)
|
||||
test1txs.append(create_transaction(self.nodes[0], txid1, self.ms_address, amount=48))
|
||||
txid2 = self.nodes[0].sendrawtransaction(test1txs[1].serialize_with_witness().hex(), True)
|
||||
txid2 = self.nodes[0].sendrawtransaction(test1txs[1].serialize_with_witness().hex(), 0)
|
||||
test1txs.append(create_transaction(self.nodes[0], coinbase_txid[1], self.wit_ms_address, amount=49))
|
||||
txid3 = self.nodes[0].sendrawtransaction(test1txs[2].serialize_with_witness().hex(), True)
|
||||
txid3 = self.nodes[0].sendrawtransaction(test1txs[2].serialize_with_witness().hex(), 0)
|
||||
self.block_submit(self.nodes[0], test1txs, False, True)
|
||||
|
||||
self.log.info("Test 2: Non-NULLDUMMY base multisig transaction should not be accepted to mempool before activation")
|
||||
test2tx = create_transaction(self.nodes[0], txid2, self.ms_address, amount=47)
|
||||
trueDummy(test2tx)
|
||||
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test2tx.serialize_with_witness().hex(), True)
|
||||
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test2tx.serialize_with_witness().hex(), 0)
|
||||
|
||||
self.log.info("Test 3: Non-NULLDUMMY base transactions should be accepted in a block before activation [431]")
|
||||
self.block_submit(self.nodes[0], [test2tx], False, True)
|
||||
|
@ -83,19 +83,19 @@ class NULLDUMMYTest(BitcoinTestFramework):
|
|||
test4tx = create_transaction(self.nodes[0], test2tx.hash, self.address, amount=46)
|
||||
test6txs = [CTransaction(test4tx)]
|
||||
trueDummy(test4tx)
|
||||
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test4tx.serialize_with_witness().hex(), True)
|
||||
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test4tx.serialize_with_witness().hex(), 0)
|
||||
self.block_submit(self.nodes[0], [test4tx])
|
||||
|
||||
self.log.info("Test 5: Non-NULLDUMMY P2WSH multisig transaction invalid after activation")
|
||||
test5tx = create_transaction(self.nodes[0], txid3, self.wit_address, amount=48)
|
||||
test6txs.append(CTransaction(test5tx))
|
||||
test5tx.wit.vtxinwit[0].scriptWitness.stack[0] = b'\x01'
|
||||
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test5tx.serialize_with_witness().hex(), True)
|
||||
assert_raises_rpc_error(-26, NULLDUMMY_ERROR, self.nodes[0].sendrawtransaction, test5tx.serialize_with_witness().hex(), 0)
|
||||
self.block_submit(self.nodes[0], [test5tx], True)
|
||||
|
||||
self.log.info("Test 6: NULLDUMMY compliant base/witness transactions should be accepted to mempool and in block after activation [432]")
|
||||
for i in test6txs:
|
||||
self.nodes[0].sendrawtransaction(i.serialize_with_witness().hex(), True)
|
||||
self.nodes[0].sendrawtransaction(i.serialize_with_witness().hex(), 0)
|
||||
self.block_submit(self.nodes[0], test6txs, True, True)
|
||||
|
||||
def block_submit(self, node, txs, witness=False, accept=False):
|
||||
|
|
|
@ -46,7 +46,7 @@ def make_utxo(node, amount, confirmed=True, scriptPubKey=CScript([1])):
|
|||
|
||||
signed_tx = node.signrawtransactionwithwallet(txToHex(tx2))
|
||||
|
||||
txid = node.sendrawtransaction(signed_tx['hex'], True)
|
||||
txid = node.sendrawtransaction(signed_tx['hex'], 0)
|
||||
|
||||
# If requested, ensure txouts are confirmed.
|
||||
if confirmed:
|
||||
|
@ -136,7 +136,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
|
||||
tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
|
||||
tx1a_hex = txToHex(tx1a)
|
||||
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
|
||||
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
|
||||
|
||||
self.sync_all()
|
||||
|
||||
|
@ -147,9 +147,9 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx1b_hex = txToHex(tx1b)
|
||||
|
||||
# This will raise an exception due to insufficient fee
|
||||
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True)
|
||||
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
|
||||
# This will raise an exception due to transaction replacement being disabled
|
||||
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True)
|
||||
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, 0)
|
||||
|
||||
# Extra 0.1 BTC fee
|
||||
tx1b = CTransaction()
|
||||
|
@ -157,9 +157,9 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx1b.vout = [CTxOut(int(0.9 * COIN), CScript([b'b' * 35]))]
|
||||
tx1b_hex = txToHex(tx1b)
|
||||
# Replacement still disabled even with "enough fee"
|
||||
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, True)
|
||||
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[1].sendrawtransaction, tx1b_hex, 0)
|
||||
# Works when enabled
|
||||
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
|
||||
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
|
||||
|
||||
mempool = self.nodes[0].getrawmempool()
|
||||
|
||||
|
@ -188,7 +188,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx.vin = [CTxIn(prevout, nSequence=0)]
|
||||
tx.vout = [CTxOut(remaining_value, CScript([1, OP_DROP] * 15 + [1]))]
|
||||
tx_hex = txToHex(tx)
|
||||
txid = self.nodes[0].sendrawtransaction(tx_hex, True)
|
||||
txid = self.nodes[0].sendrawtransaction(tx_hex, 0)
|
||||
chain_txids.append(txid)
|
||||
prevout = COutPoint(int(txid, 16), 0)
|
||||
|
||||
|
@ -200,14 +200,14 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
dbl_tx_hex = txToHex(dbl_tx)
|
||||
|
||||
# This will raise an exception due to insufficient fee
|
||||
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True)
|
||||
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, 0)
|
||||
|
||||
# Accepted with sufficient fee
|
||||
dbl_tx = CTransaction()
|
||||
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
|
||||
dbl_tx.vout = [CTxOut(1 * COIN, CScript([1] * 35))]
|
||||
dbl_tx_hex = txToHex(dbl_tx)
|
||||
self.nodes[0].sendrawtransaction(dbl_tx_hex, True)
|
||||
self.nodes[0].sendrawtransaction(dbl_tx_hex, 0)
|
||||
|
||||
mempool = self.nodes[0].getrawmempool()
|
||||
for doublespent_txid in chain_txids:
|
||||
|
@ -237,7 +237,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx_hex = txToHex(tx)
|
||||
|
||||
assert len(tx.serialize()) < 100000
|
||||
txid = self.nodes[0].sendrawtransaction(tx_hex, True)
|
||||
txid = self.nodes[0].sendrawtransaction(tx_hex, 0)
|
||||
yield tx
|
||||
_total_txs[0] += 1
|
||||
|
||||
|
@ -261,14 +261,14 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
dbl_tx.vout = [CTxOut(initial_nValue - fee * n, CScript([1] * 35))]
|
||||
dbl_tx_hex = txToHex(dbl_tx)
|
||||
# This will raise an exception due to insufficient fee
|
||||
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, True)
|
||||
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, dbl_tx_hex, 0)
|
||||
|
||||
# 1 BTC fee is enough
|
||||
dbl_tx = CTransaction()
|
||||
dbl_tx.vin = [CTxIn(tx0_outpoint, nSequence=0)]
|
||||
dbl_tx.vout = [CTxOut(initial_nValue - fee * n - 1 * COIN, CScript([1] * 35))]
|
||||
dbl_tx_hex = txToHex(dbl_tx)
|
||||
self.nodes[0].sendrawtransaction(dbl_tx_hex, True)
|
||||
self.nodes[0].sendrawtransaction(dbl_tx_hex, 0)
|
||||
|
||||
mempool = self.nodes[0].getrawmempool()
|
||||
|
||||
|
@ -289,7 +289,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
dbl_tx.vout = [CTxOut(initial_nValue - 2 * fee * n, CScript([1] * 35))]
|
||||
dbl_tx_hex = txToHex(dbl_tx)
|
||||
# This will raise an exception
|
||||
assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, True)
|
||||
assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, dbl_tx_hex, 0)
|
||||
|
||||
for tx in tree_txs:
|
||||
tx.rehash()
|
||||
|
@ -303,7 +303,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
|
||||
tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
|
||||
tx1a_hex = txToHex(tx1a)
|
||||
self.nodes[0].sendrawtransaction(tx1a_hex, True)
|
||||
self.nodes[0].sendrawtransaction(tx1a_hex, 0)
|
||||
|
||||
# Higher fee, but the fee per KB is much lower, so the replacement is
|
||||
# rejected.
|
||||
|
@ -313,7 +313,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx1b_hex = txToHex(tx1b)
|
||||
|
||||
# This will raise an exception due to insufficient fee
|
||||
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True)
|
||||
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
|
||||
|
||||
def test_spends_of_conflicting_outputs(self):
|
||||
"""Replacements that spend conflicting tx outputs are rejected"""
|
||||
|
@ -324,7 +324,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx1a.vin = [CTxIn(utxo1, nSequence=0)]
|
||||
tx1a.vout = [CTxOut(int(1.1 * COIN), CScript([b'a' * 35]))]
|
||||
tx1a_hex = txToHex(tx1a)
|
||||
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
|
||||
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
|
||||
|
||||
tx1a_txid = int(tx1a_txid, 16)
|
||||
|
||||
|
@ -336,14 +336,14 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx2_hex = txToHex(tx2)
|
||||
|
||||
# This will raise an exception
|
||||
assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True)
|
||||
assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, 0)
|
||||
|
||||
# Spend tx1a's output to test the indirect case.
|
||||
tx1b = CTransaction()
|
||||
tx1b.vin = [CTxIn(COutPoint(tx1a_txid, 0), nSequence=0)]
|
||||
tx1b.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
|
||||
tx1b_hex = txToHex(tx1b)
|
||||
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
|
||||
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
|
||||
tx1b_txid = int(tx1b_txid, 16)
|
||||
|
||||
tx2 = CTransaction()
|
||||
|
@ -353,7 +353,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx2_hex = txToHex(tx2)
|
||||
|
||||
# This will raise an exception
|
||||
assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, True)
|
||||
assert_raises_rpc_error(-26, "bad-txns-spends-conflicting-tx", self.nodes[0].sendrawtransaction, tx2_hex, 0)
|
||||
|
||||
def test_new_unconfirmed_inputs(self):
|
||||
"""Replacements that add new unconfirmed inputs are rejected"""
|
||||
|
@ -364,7 +364,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx1.vin = [CTxIn(confirmed_utxo)]
|
||||
tx1.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
|
||||
tx1_hex = txToHex(tx1)
|
||||
self.nodes[0].sendrawtransaction(tx1_hex, True)
|
||||
self.nodes[0].sendrawtransaction(tx1_hex, 0)
|
||||
|
||||
tx2 = CTransaction()
|
||||
tx2.vin = [CTxIn(confirmed_utxo), CTxIn(unconfirmed_utxo)]
|
||||
|
@ -372,7 +372,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx2_hex = txToHex(tx2)
|
||||
|
||||
# This will raise an exception
|
||||
assert_raises_rpc_error(-26, "replacement-adds-unconfirmed", self.nodes[0].sendrawtransaction, tx2_hex, True)
|
||||
assert_raises_rpc_error(-26, "replacement-adds-unconfirmed", self.nodes[0].sendrawtransaction, tx2_hex, 0)
|
||||
|
||||
def test_too_many_replacements(self):
|
||||
"""Replacements that evict too many transactions are rejected"""
|
||||
|
@ -394,7 +394,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
splitting_tx.vout = outputs
|
||||
splitting_tx_hex = txToHex(splitting_tx)
|
||||
|
||||
txid = self.nodes[0].sendrawtransaction(splitting_tx_hex, True)
|
||||
txid = self.nodes[0].sendrawtransaction(splitting_tx_hex, 0)
|
||||
txid = int(txid, 16)
|
||||
|
||||
# Now spend each of those outputs individually
|
||||
|
@ -403,7 +403,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx_i.vin = [CTxIn(COutPoint(txid, i), nSequence=0)]
|
||||
tx_i.vout = [CTxOut(split_value - fee, CScript([b'a' * 35]))]
|
||||
tx_i_hex = txToHex(tx_i)
|
||||
self.nodes[0].sendrawtransaction(tx_i_hex, True)
|
||||
self.nodes[0].sendrawtransaction(tx_i_hex, 0)
|
||||
|
||||
# Now create doublespend of the whole lot; should fail.
|
||||
# Need a big enough fee to cover all spending transactions and have
|
||||
|
@ -418,14 +418,14 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
double_tx_hex = txToHex(double_tx)
|
||||
|
||||
# This will raise an exception
|
||||
assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, double_tx_hex, True)
|
||||
assert_raises_rpc_error(-26, "too many potential replacements", self.nodes[0].sendrawtransaction, double_tx_hex, 0)
|
||||
|
||||
# If we remove an input, it should pass
|
||||
double_tx = CTransaction()
|
||||
double_tx.vin = inputs[0:-1]
|
||||
double_tx.vout = [CTxOut(double_spend_value, CScript([b'a']))]
|
||||
double_tx_hex = txToHex(double_tx)
|
||||
self.nodes[0].sendrawtransaction(double_tx_hex, True)
|
||||
self.nodes[0].sendrawtransaction(double_tx_hex, 0)
|
||||
|
||||
def test_opt_in(self):
|
||||
"""Replacing should only work if orig tx opted in"""
|
||||
|
@ -436,7 +436,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0xffffffff)]
|
||||
tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
|
||||
tx1a_hex = txToHex(tx1a)
|
||||
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
|
||||
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
|
||||
|
||||
# This transaction isn't shown as replaceable
|
||||
assert_equal(self.nodes[0].getmempoolentry(tx1a_txid)['bip125-replaceable'], False)
|
||||
|
@ -448,7 +448,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx1b_hex = txToHex(tx1b)
|
||||
|
||||
# This will raise an exception
|
||||
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx1b_hex, True)
|
||||
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
|
||||
|
||||
tx1_outpoint = make_utxo(self.nodes[0], int(1.1*COIN))
|
||||
|
||||
|
@ -457,7 +457,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0xfffffffe)]
|
||||
tx2a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
|
||||
tx2a_hex = txToHex(tx2a)
|
||||
tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, True)
|
||||
tx2a_txid = self.nodes[0].sendrawtransaction(tx2a_hex, 0)
|
||||
|
||||
# Still shouldn't be able to double-spend
|
||||
tx2b = CTransaction()
|
||||
|
@ -466,7 +466,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx2b_hex = txToHex(tx2b)
|
||||
|
||||
# This will raise an exception
|
||||
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx2b_hex, True)
|
||||
assert_raises_rpc_error(-26, "txn-mempool-conflict", self.nodes[0].sendrawtransaction, tx2b_hex, 0)
|
||||
|
||||
# Now create a new transaction that spends from tx1a and tx2a
|
||||
# opt-in on one of the inputs
|
||||
|
@ -481,7 +481,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx3a.vout = [CTxOut(int(0.9*COIN), CScript([b'c'])), CTxOut(int(0.9*COIN), CScript([b'd']))]
|
||||
tx3a_hex = txToHex(tx3a)
|
||||
|
||||
tx3a_txid = self.nodes[0].sendrawtransaction(tx3a_hex, True)
|
||||
tx3a_txid = self.nodes[0].sendrawtransaction(tx3a_hex, 0)
|
||||
|
||||
# This transaction is shown as replaceable
|
||||
assert_equal(self.nodes[0].getmempoolentry(tx3a_txid)['bip125-replaceable'], True)
|
||||
|
@ -496,10 +496,10 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx3c.vout = [CTxOut(int(0.5 * COIN), CScript([b'f' * 35]))]
|
||||
tx3c_hex = txToHex(tx3c)
|
||||
|
||||
self.nodes[0].sendrawtransaction(tx3b_hex, True)
|
||||
self.nodes[0].sendrawtransaction(tx3b_hex, 0)
|
||||
# If tx3b was accepted, tx3c won't look like a replacement,
|
||||
# but make sure it is accepted anyway
|
||||
self.nodes[0].sendrawtransaction(tx3c_hex, True)
|
||||
self.nodes[0].sendrawtransaction(tx3c_hex, 0)
|
||||
|
||||
def test_prioritised_transactions(self):
|
||||
# Ensure that fee deltas used via prioritisetransaction are
|
||||
|
@ -512,7 +512,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx1a.vin = [CTxIn(tx0_outpoint, nSequence=0)]
|
||||
tx1a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
|
||||
tx1a_hex = txToHex(tx1a)
|
||||
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, True)
|
||||
tx1a_txid = self.nodes[0].sendrawtransaction(tx1a_hex, 0)
|
||||
|
||||
# Higher fee, but the actual fee per KB is much lower.
|
||||
tx1b = CTransaction()
|
||||
|
@ -521,13 +521,13 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx1b_hex = txToHex(tx1b)
|
||||
|
||||
# Verify tx1b cannot replace tx1a.
|
||||
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, True)
|
||||
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx1b_hex, 0)
|
||||
|
||||
# Use prioritisetransaction to set tx1a's fee to 0.
|
||||
self.nodes[0].prioritisetransaction(txid=tx1a_txid, fee_delta=int(-0.1*COIN))
|
||||
|
||||
# Now tx1b should be able to replace tx1a
|
||||
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, True)
|
||||
tx1b_txid = self.nodes[0].sendrawtransaction(tx1b_hex, 0)
|
||||
|
||||
assert tx1b_txid in self.nodes[0].getrawmempool()
|
||||
|
||||
|
@ -538,7 +538,7 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx2a.vin = [CTxIn(tx1_outpoint, nSequence=0)]
|
||||
tx2a.vout = [CTxOut(1 * COIN, CScript([b'a' * 35]))]
|
||||
tx2a_hex = txToHex(tx2a)
|
||||
self.nodes[0].sendrawtransaction(tx2a_hex, True)
|
||||
self.nodes[0].sendrawtransaction(tx2a_hex, 0)
|
||||
|
||||
# Lower fee, but we'll prioritise it
|
||||
tx2b = CTransaction()
|
||||
|
@ -548,13 +548,13 @@ class ReplaceByFeeTest(BitcoinTestFramework):
|
|||
tx2b_hex = txToHex(tx2b)
|
||||
|
||||
# Verify tx2b cannot replace tx2a.
|
||||
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, True)
|
||||
assert_raises_rpc_error(-26, "insufficient fee", self.nodes[0].sendrawtransaction, tx2b_hex, 0)
|
||||
|
||||
# Now prioritise tx2b to have a higher modified fee
|
||||
self.nodes[0].prioritisetransaction(txid=tx2b.hash, fee_delta=int(0.1*COIN))
|
||||
|
||||
# tx2b should now be accepted
|
||||
tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, True)
|
||||
tx2b_txid = self.nodes[0].sendrawtransaction(tx2b_hex, 0)
|
||||
|
||||
assert tx2b_txid in self.nodes[0].getrawmempool()
|
||||
|
||||
|
|
|
@ -536,7 +536,7 @@ class SegWitTest(BitcoinTestFramework):
|
|||
tx.vout.append(CTxOut(10000000, i))
|
||||
tx.rehash()
|
||||
signresults = self.nodes[0].signrawtransactionwithwallet(tx.serialize_without_witness().hex())['hex']
|
||||
txid = self.nodes[0].sendrawtransaction(signresults, True)
|
||||
txid = self.nodes[0].sendrawtransaction(signresults, 0)
|
||||
txs_mined[txid] = self.nodes[0].generate(1)[0]
|
||||
sync_blocks(self.nodes)
|
||||
watchcount = 0
|
||||
|
@ -588,7 +588,7 @@ class SegWitTest(BitcoinTestFramework):
|
|||
tx.vout.append(CTxOut(0, CScript()))
|
||||
tx.rehash()
|
||||
signresults = self.nodes[0].signrawtransactionwithwallet(tx.serialize_without_witness().hex())['hex']
|
||||
self.nodes[0].sendrawtransaction(signresults, True)
|
||||
self.nodes[0].sendrawtransaction(signresults, 0)
|
||||
self.nodes[0].generate(1)
|
||||
sync_blocks(self.nodes)
|
||||
|
||||
|
|
|
@ -68,7 +68,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
|||
inputs=[{'txid': coin['txid'], 'vout': coin['vout']}],
|
||||
outputs=[{node.getnewaddress(): 0.3}, {node.getnewaddress(): 49}],
|
||||
))['hex']
|
||||
txid_in_block = node.sendrawtransaction(hexstring=raw_tx_in_block, allowhighfees=True)
|
||||
txid_in_block = node.sendrawtransaction(hexstring=raw_tx_in_block, maxfeerate=0)
|
||||
node.generate(1)
|
||||
self.mempool_size = 0
|
||||
self.check_mempool_result(
|
||||
|
@ -101,9 +101,9 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
|||
self.check_mempool_result(
|
||||
result_expected=[{'txid': tx.rehash(), 'allowed': True}],
|
||||
rawtxs=[tx.serialize().hex()],
|
||||
allowhighfees=True,
|
||||
maxfeerate=0,
|
||||
)
|
||||
node.sendrawtransaction(hexstring=raw_tx_final, allowhighfees=True)
|
||||
node.sendrawtransaction(hexstring=raw_tx_final, maxfeerate=0)
|
||||
self.mempool_size += 1
|
||||
|
||||
self.log.info('A transaction in the mempool')
|
||||
|
@ -128,7 +128,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
|||
|
||||
self.log.info('A transaction that conflicts with an unconfirmed tx')
|
||||
# Send the transaction that replaces the mempool transaction and opts out of replaceability
|
||||
node.sendrawtransaction(hexstring=tx.serialize().hex(), allowhighfees=True)
|
||||
node.sendrawtransaction(hexstring=tx.serialize().hex(), maxfeerate=0)
|
||||
# take original raw_tx_0
|
||||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
|
||||
tx.vout[0].nValue -= int(4 * fee * COIN) # Set more fee
|
||||
|
@ -136,7 +136,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
|||
self.check_mempool_result(
|
||||
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '18: txn-mempool-conflict'}],
|
||||
rawtxs=[tx.serialize().hex()],
|
||||
allowhighfees=True,
|
||||
maxfeerate=0,
|
||||
)
|
||||
|
||||
self.log.info('A transaction with missing inputs, that never existed')
|
||||
|
@ -152,7 +152,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
|||
tx.deserialize(BytesIO(hex_str_to_bytes(raw_tx_0)))
|
||||
tx.vin[0].prevout.n = 1 # Set vout to 1, to spend the other outpoint (49 coins) of the in-chain-tx we want to double spend
|
||||
raw_tx_1 = node.signrawtransactionwithwallet(tx.serialize().hex())['hex']
|
||||
txid_1 = node.sendrawtransaction(hexstring=raw_tx_1, allowhighfees=True)
|
||||
txid_1 = node.sendrawtransaction(hexstring=raw_tx_1, maxfeerate=0)
|
||||
# Now spend both to "clearly hide" the outputs, ie. remove the coins from the utxo set by spending them
|
||||
raw_tx_spend_both = node.signrawtransactionwithwallet(node.createrawtransaction(
|
||||
inputs=[
|
||||
|
@ -161,7 +161,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
|||
],
|
||||
outputs=[{node.getnewaddress(): 0.1}]
|
||||
))['hex']
|
||||
txid_spend_both = node.sendrawtransaction(hexstring=raw_tx_spend_both, allowhighfees=True)
|
||||
txid_spend_both = node.sendrawtransaction(hexstring=raw_tx_spend_both, maxfeerate=0)
|
||||
node.generate(1)
|
||||
self.mempool_size = 0
|
||||
# Now see if we can add the coins back to the utxo set by sending the exact txs again
|
||||
|
@ -304,7 +304,7 @@ class MempoolAcceptanceTest(BitcoinTestFramework):
|
|||
self.check_mempool_result(
|
||||
result_expected=[{'txid': tx.rehash(), 'allowed': False, 'reject-reason': '64: non-BIP68-final'}],
|
||||
rawtxs=[tx.serialize().hex()],
|
||||
allowhighfees=True,
|
||||
maxfeerate=0,
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -90,7 +90,7 @@ class RpcCreateMultiSigTest(BitcoinTestFramework):
|
|||
rawtx3 = node2.signrawtransactionwithkey(rawtx2["hex"], [self.priv[-1]], prevtxs)
|
||||
|
||||
self.moved += outval
|
||||
tx = node0.sendrawtransaction(rawtx3["hex"], True)
|
||||
tx = node0.sendrawtransaction(rawtx3["hex"], 0)
|
||||
blk = node0.generate(1)[0]
|
||||
assert tx in node0.getblock(blk)["tx"]
|
||||
|
||||
|
|
|
@ -426,5 +426,24 @@ class RawTransactionsTest(BitcoinTestFramework):
|
|||
decrawtx = self.nodes[0].decoderawtransaction(rawtx)
|
||||
assert_equal(decrawtx['version'], 0x7fffffff)
|
||||
|
||||
self.log.info('sendrawtransaction with maxfeerate')
|
||||
|
||||
txId = self.nodes[0].sendtoaddress(self.nodes[2].getnewaddress(), 1.0)
|
||||
rawTx = self.nodes[0].getrawtransaction(txId, True)
|
||||
vout = next(o for o in rawTx['vout'] if o['value'] == Decimal('1.00000000'))
|
||||
|
||||
self.sync_all()
|
||||
inputs = [{ "txid" : txId, "vout" : vout['n'] }]
|
||||
outputs = { self.nodes[0].getnewaddress() : Decimal("0.99999000") } # 1000 sat fee
|
||||
rawTx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||
rawTxSigned = self.nodes[2].signrawtransactionwithwallet(rawTx)
|
||||
assert_equal(rawTxSigned['complete'], True)
|
||||
# 1000 sat fee, ~200 b transaction, fee rate should land around 5 sat/b = 0.00005000 BTC/kB
|
||||
# Thus, below call should fail
|
||||
assert_raises_rpc_error(-26, "absurdly-high-fee", self.nodes[2].sendrawtransaction, rawTxSigned['hex'], 0.00001000)
|
||||
# And below call should succeed
|
||||
self.nodes[2].sendrawtransaction(hexstring=rawTxSigned['hex'], maxfeerate=0.00007000)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
RawTransactionsTest().main()
|
||||
|
|
|
@ -467,7 +467,7 @@ def random_transaction(nodes, amount, min_fee, fee_increment, fee_variants):
|
|||
|
||||
rawtx = from_node.createrawtransaction(inputs, outputs)
|
||||
signresult = from_node.signrawtransactionwithwallet(rawtx)
|
||||
txid = from_node.sendrawtransaction(signresult["hex"], True)
|
||||
txid = from_node.sendrawtransaction(signresult["hex"], 0)
|
||||
|
||||
return (txid, signresult["hex"], fee)
|
||||
|
||||
|
@ -539,7 +539,7 @@ def create_lots_of_big_transactions(node, txouts, utxos, num, fee):
|
|||
newtx = newtx + txouts
|
||||
newtx = newtx + rawtx[94:]
|
||||
signresult = node.signrawtransactionwithwallet(newtx, None, "NONE")
|
||||
txid = node.sendrawtransaction(signresult["hex"], True)
|
||||
txid = node.sendrawtransaction(signresult["hex"], 0)
|
||||
txids.append(txid)
|
||||
return txids
|
||||
|
||||
|
|
|
@ -168,8 +168,8 @@ class WalletTest(BitcoinTestFramework):
|
|||
txns_to_send.append(self.nodes[0].signrawtransactionwithwallet(raw_tx))
|
||||
|
||||
# Have node 1 (miner) send the transactions
|
||||
self.nodes[1].sendrawtransaction(txns_to_send[0]["hex"], True)
|
||||
self.nodes[1].sendrawtransaction(txns_to_send[1]["hex"], True)
|
||||
self.nodes[1].sendrawtransaction(txns_to_send[0]["hex"], 0)
|
||||
self.nodes[1].sendrawtransaction(txns_to_send[1]["hex"], 0)
|
||||
|
||||
# Have node1 mine a block to confirm transactions:
|
||||
self.nodes[1].generate(1)
|
||||
|
|
|
@ -185,7 +185,7 @@ class ListTransactionsTest(BitcoinTestFramework):
|
|||
tx3_b.vout[0].nValue -= int(Decimal("0.004") * COIN) # bump the fee
|
||||
tx3_b = tx3_b.serialize().hex()
|
||||
tx3_b_signed = self.nodes[0].signrawtransactionwithwallet(tx3_b)['hex']
|
||||
txid_3b = self.nodes[0].sendrawtransaction(tx3_b_signed, True)
|
||||
txid_3b = self.nodes[0].sendrawtransaction(tx3_b_signed, 0)
|
||||
assert is_opt_in(self.nodes[0], txid_3b)
|
||||
|
||||
assert_array_result(self.nodes[0].listtransactions(), {"txid": txid_4}, {"bip125-replaceable": "unknown"})
|
||||
|
|
Loading…
Reference in a new issue