mirror of
https://github.com/bitcoin/bitcoin.git
synced 2026-05-31 16:24:48 +02:00
Merge bitcoin/bitcoin#25943: rpc: Add a parameter to sendrawtransaction which sets a maximum value for unspendable outputs.
7013da07fbAdd release note for PR#25943 (David Gumberg)04f270b435Add test for unspendable transactions and parameter 'maxburnamount' to sendrawtransaction. (David Gumberg) Pull request description: This PR adds a user configurable, zero by default parameter — `maxburnamount` — to `sendrawtransaction`. This PR makes bitcoin core reject transactions that contain unspendable outputs which exceed `maxburnamount`. closes #25899. As a result of this PR, `sendrawtransaction` will by default block 3 kinds of transactions: 1. Those that begin with `OP_RETURN` - (datacarriers) 2. Those whose lengths exceed the script limit. 3. Those that contain invalid opcodes. The user is able to configure a `maxburnamount` that will override this check and allow a user to send a potentially unspendable output into the mempool. I see two legitimate use cases for this override: 1. Users that deliberately use `OP_RETURN` for datacarrier transactions that embed data into the blockchain. 2. Users that refuse to update, or are unable to update their bitcoin core client would be able to make use of new opcodes that their client doesn't know about. ACKs for top commit: glozow: reACK7013da07fbachow101: re-ACK7013da07fbTree-SHA512: f786a796fb71a587d30313c96717fdf47e1106ab4ee0c16d713695e6c31ed6f6732dff6cbc91ca9841d66232166eb058f96028028e75c1507324426309ee4525
This commit is contained in:
@@ -156,9 +156,10 @@ class CoinStatsIndexTest(BitcoinTestFramework):
|
||||
|
||||
# Generate and send another tx with an OP_RETURN output (which is unspendable)
|
||||
tx2 = self.wallet.create_self_transfer(utxo_to_spend=tx1_out_21)['tx']
|
||||
tx2.vout = [CTxOut(int(Decimal('20.99') * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
|
||||
tx2_val = '20.99'
|
||||
tx2.vout = [CTxOut(int(Decimal(tx2_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
|
||||
tx2_hex = tx2.serialize().hex()
|
||||
self.nodes[0].sendrawtransaction(tx2_hex)
|
||||
self.nodes[0].sendrawtransaction(tx2_hex, 0, tx2_val)
|
||||
|
||||
# Include both txs in a block
|
||||
self.generate(self.nodes[0], 1)
|
||||
|
||||
@@ -18,9 +18,17 @@ from itertools import product
|
||||
|
||||
from test_framework.messages import (
|
||||
MAX_BIP125_RBF_SEQUENCE,
|
||||
COIN,
|
||||
CTransaction,
|
||||
CTxOut,
|
||||
tx_from_hex,
|
||||
)
|
||||
from test_framework.script import (
|
||||
CScript,
|
||||
OP_FALSE,
|
||||
OP_INVALIDOPCODE,
|
||||
OP_RETURN,
|
||||
)
|
||||
from test_framework.test_framework import BitcoinTestFramework
|
||||
from test_framework.util import (
|
||||
assert_equal,
|
||||
@@ -331,6 +339,57 @@ class RawTransactionsTest(BitcoinTestFramework):
|
||||
rawtx = self.nodes[2].createrawtransaction(inputs, outputs)
|
||||
assert_raises_rpc_error(-25, "bad-txns-inputs-missingorspent", self.nodes[2].sendrawtransaction, rawtx)
|
||||
|
||||
self.log.info("Test sendrawtransaction exceeding, falling short of, and equaling maxburnamount")
|
||||
max_burn_exceeded = "Unspendable output exceeds maximum configured by user (maxburnamount)"
|
||||
|
||||
|
||||
# Test that spendable transaction with default maxburnamount (0) gets sent
|
||||
tx = self.wallet.create_self_transfer()['tx']
|
||||
tx_hex = tx.serialize().hex()
|
||||
self.nodes[2].sendrawtransaction(hexstring=tx_hex)
|
||||
|
||||
# Test that datacarrier transaction with default maxburnamount (0) does not get sent
|
||||
tx = self.wallet.create_self_transfer()['tx']
|
||||
tx_val = 0.001
|
||||
tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
|
||||
tx_hex = tx.serialize().hex()
|
||||
assert_raises_rpc_error(-25, max_burn_exceeded, self.nodes[2].sendrawtransaction, tx_hex)
|
||||
|
||||
# Test that oversized script gets rejected by sendrawtransaction
|
||||
tx = self.wallet.create_self_transfer()['tx']
|
||||
tx_val = 0.001
|
||||
tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_FALSE] * 10001))]
|
||||
tx_hex = tx.serialize().hex()
|
||||
assert_raises_rpc_error(-25, max_burn_exceeded, self.nodes[2].sendrawtransaction, tx_hex)
|
||||
|
||||
# Test that script containing invalid opcode gets rejected by sendrawtransaction
|
||||
tx = self.wallet.create_self_transfer()['tx']
|
||||
tx_val = 0.01
|
||||
tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_INVALIDOPCODE]))]
|
||||
tx_hex = tx.serialize().hex()
|
||||
assert_raises_rpc_error(-25, max_burn_exceeded, self.nodes[2].sendrawtransaction, tx_hex)
|
||||
|
||||
# Test a transaction where our burn exceeds maxburnamount
|
||||
tx = self.wallet.create_self_transfer()['tx']
|
||||
tx_val = 0.001
|
||||
tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
|
||||
tx_hex = tx.serialize().hex()
|
||||
assert_raises_rpc_error(-25, max_burn_exceeded, self.nodes[2].sendrawtransaction, tx_hex, 0, 0.0009)
|
||||
|
||||
# Test a transaction where our burn falls short of maxburnamount
|
||||
tx = self.wallet.create_self_transfer()['tx']
|
||||
tx_val = 0.001
|
||||
tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
|
||||
tx_hex = tx.serialize().hex()
|
||||
self.nodes[2].sendrawtransaction(hexstring=tx_hex, maxfeerate='0', maxburnamount='0.0011')
|
||||
|
||||
# Test a transaction where our burn equals maxburnamount
|
||||
tx = self.wallet.create_self_transfer()['tx']
|
||||
tx_val = 0.001
|
||||
tx.vout = [CTxOut(int(Decimal(tx_val) * COIN), CScript([OP_RETURN] + [OP_FALSE] * 30))]
|
||||
tx_hex = tx.serialize().hex()
|
||||
self.nodes[2].sendrawtransaction(hexstring=tx_hex, maxfeerate='0', maxburnamount='0.001')
|
||||
|
||||
def sendrawtransaction_testmempoolaccept_tests(self):
|
||||
self.log.info("Test sendrawtransaction/testmempoolaccept with maxfeerate")
|
||||
fee_exceeds_max = "Fee exceeds maximum configured by user (e.g. -maxtxfee, maxfeerate)"
|
||||
|
||||
Reference in New Issue
Block a user