Skip to content

Commit c9ec687

Browse files
committed
Add annex data carrier option behind -annexcarrier option
Allow 0 to 126 byte pushes in annex of the format: 0x50 <1 byte data len> <len bytes>
1 parent 6eab309 commit c9ec687

File tree

5 files changed

+48
-3
lines changed

5 files changed

+48
-3
lines changed

src/init.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -567,6 +567,7 @@ void SetupServerArgs(ArgsManager& argsman)
567567
argsman.AddArg("-mempoolfullrbf", strprintf("Accept transaction replace-by-fee without requiring replaceability signaling (default: %u)", DEFAULT_MEMPOOL_FULL_RBF), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
568568
argsman.AddArg("-permitbaremultisig", strprintf("Relay non-P2SH multisig (default: %u)", DEFAULT_PERMIT_BAREMULTISIG), ArgsManager::ALLOW_ANY,
569569
OptionsCategory::NODE_RELAY);
570+
argsman.AddArg("-annexcarrier", strprintf("Relay and mine transactions with annex data, up to 255 bytes of payload (default: %u)", DEFAULT_ACCEPT_ANNEXDATA), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
570571
argsman.AddArg("-minrelaytxfee=<amt>", strprintf("Fees (in %s/kvB) smaller than this are considered zero fee for relaying, mining and transaction creation (default: %s)",
571572
CURRENCY_UNIT, FormatMoney(DEFAULT_MIN_RELAY_TX_FEE)), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
572573
argsman.AddArg("-whitelistforcerelay", strprintf("Add 'forcerelay' permission to whitelisted inbound peers with default permissions. This will relay transactions even if the transactions were already in the mempool. (default: %d)", DEFAULT_WHITELISTFORCERELAY), ArgsManager::ALLOW_ANY, OptionsCategory::NODE_RELAY);
@@ -981,6 +982,7 @@ bool AppInitParameterInteraction(const ArgsManager& args, bool use_syscall_sandb
981982

982983
if (!g_wallet_init_interface.ParameterInteraction()) return false;
983984

985+
accept_annex_data = args.GetBoolArg("-annexcarrier", DEFAULT_ACCEPT_ANNEXDATA);
984986
// Option to startup with mocktime set (used for regression testing):
985987
SetMockTime(args.GetIntArg("-mocktime", 0)); // SetMockTime(0) is a no-op
986988

src/policy/policy.cpp

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ bool AreInputsStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
215215

216216
bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
217217
{
218+
bool annex_data_already_found = false;
218219
if (tx.IsCoinBase())
219220
return true; // Coinbases are skipped
220221

@@ -271,8 +272,14 @@ bool IsWitnessStandard(const CTransaction& tx, const CCoinsViewCache& mapInputs)
271272
// Taproot spend (non-P2SH-wrapped, version 1, witness program size 32; see BIP 341)
272273
Span stack{tx.vin[i].scriptWitness.stack};
273274
if (stack.size() >= 2 && !stack.back().empty() && stack.back()[0] == ANNEX_TAG) {
274-
// Annexes are nonstandard as long as no semantics are defined for them.
275-
return false;
275+
const std::vector<unsigned char> &annex = stack.back();
276+
// For now allow 0 to 126 bytes push to allow drop-in replacement via BIP PR#1381
277+
// But only one annex per transaction to avoid witness stuffing.
278+
if (!accept_annex_data || annex_data_already_found ||
279+
annex.size() < 2 || annex.size() > 1 + 1 + 126 || (size_t)annex[1] != annex.size() - 2) {
280+
return false;
281+
}
282+
annex_data_already_found = true;
276283
}
277284
if (stack.size() >= 2) {
278285
// Script path spend (2 or more stack elements after removing optional annex)

src/script/standard.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
typedef std::vector<unsigned char> valtype;
1818

19+
bool accept_annex_data = DEFAULT_ACCEPT_ANNEXDATA;
1920
CScriptID::CScriptID(const CScript& in) : BaseHash(Hash160(in)) {}
2021
CScriptID::CScriptID(const ScriptHash& in) : BaseHash(static_cast<uint160>(in)) {}
2122

src/script/standard.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include <variant>
1818

1919
static const bool DEFAULT_ACCEPT_DATACARRIER = true;
20+
static const bool DEFAULT_ACCEPT_ANNEXDATA = false;
2021

2122
class CKeyID;
2223
class CScript;
@@ -38,6 +39,12 @@ class CScriptID : public BaseHash<uint160>
3839
*/
3940
static const unsigned int MAX_OP_RETURN_RELAY = 83;
4041

42+
/**
43+
* Allows up to one annex data to be embedded in a transaction, counting towards the
44+
* same data carrier limit as OP_RETURN values
45+
*/
46+
extern bool accept_annex_data;
47+
4148
/**
4249
* Mandatory script verification flags that all new blocks must comply with for
4350
* them to be valid. (but old blocks may not comply with) Currently just P2SH,

test/functional/mempool_datacarrier.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class DataCarrierTest(BitcoinTestFramework):
2424
def set_test_params(self):
2525
self.num_nodes = 3
2626
self.extra_args = [
27-
[],
27+
["-annexcarrier=1"],
2828
["-datacarrier=0"],
2929
["-datacarrier=1", f"-datacarriersize={MAX_OP_RETURN_RELAY - 1}"]
3030
]
@@ -42,6 +42,20 @@ def test_null_data_transaction(self, node: TestNode, data: bytes, success: bool)
4242
else:
4343
assert_raises_rpc_error(-26, "scriptpubkey", self.wallet.sendrawtransaction, from_node=node, tx_hex=tx_hex)
4444

45+
def test_annex_data_transaction(self, node: TestNode, data: bytes, success: bool) -> None:
46+
tx = self.wallet.create_self_transfer(fee_rate=0)["tx"]
47+
tx.wit.vtxinwit[0].scriptWitness.stack = tx.wit.vtxinwit[0].scriptWitness.stack + [bytes.fromhex("50") + len(data).to_bytes(1, 'little') + bytes.fromhex("ff"*len(data))]
48+
tx.vout[0].nValue -= tx.get_vsize() # simply pay 1sat/vbyte fee
49+
50+
tx_hex = tx.serialize().hex()
51+
52+
if success:
53+
self.wallet.sendrawtransaction(from_node=node, tx_hex=tx_hex)
54+
assert tx.rehash() in node.getrawmempool(True), f'{tx_hex} not in mempool'
55+
else:
56+
assert_raises_rpc_error(-26, "bad-witness-nonstandard", self.wallet.sendrawtransaction, from_node=node, tx_hex=tx_hex)
57+
58+
4559
def run_test(self):
4660
self.wallet = MiniWallet(self.nodes[0])
4761
self.wallet.rescan_utxos()
@@ -51,6 +65,20 @@ def run_test(self):
5165
too_long_data = random_bytes(MAX_OP_RETURN_RELAY - 2)
5266
small_data = random_bytes(MAX_OP_RETURN_RELAY - 4)
5367

68+
small_annex = random_bytes(0)
69+
large_annex = random_bytes(126)
70+
oversize_annex = random_bytes(127)
71+
72+
self.log.info("Testing annex data transaction with true -annexcarrier value.")
73+
self.test_annex_data_transaction(node=self.nodes[0], data=small_annex, success=True)
74+
self.test_annex_data_transaction(node=self.nodes[0], data=large_annex, success=True)
75+
self.test_annex_data_transaction(node=self.nodes[0], data=oversize_annex, success=False)
76+
77+
self.log.info("Testing annex data transaction with default -annexcarrier value.")
78+
self.test_annex_data_transaction(node=self.nodes[1], data=small_annex, success=False)
79+
self.test_annex_data_transaction(node=self.nodes[1], data=large_annex, success=False)
80+
self.test_annex_data_transaction(node=self.nodes[1], data=oversize_annex, success=False)
81+
5482
self.log.info("Testing null data transaction with default -datacarrier and -datacarriersize values.")
5583
self.test_null_data_transaction(node=self.nodes[0], data=default_size_data, success=True)
5684

0 commit comments

Comments
 (0)