From: Rusty Russell <bitcoin-dev@rustcorp•com.au>
To: bitcoindev@googlegroups.com
Cc: Julian Moik <julianmoik@gmail•com>
Subject: [bitcoindev] [3/4] OP_TX
Date: Sat, 27 Sep 2025 20:59:04 +0930 [thread overview]
Message-ID: <87y0q0m8vz.fsf@rustcorp.com.au> (raw)
In-Reply-To: <871pnsnnhh.fsf@rustcorp.com.au>
Hi all,
This BIP presents an implementation of generic introspection, giving
a UTXO greater control over the conditions under which it may be spent.
It is less polished than the previous two BIP drafts, but also simpler.
But there's plenty of room to debate the control word!
Three things to note: firstly, this opcode would be possible without
the previous two BIPs. However, it would be severely limited by 31 bit
arithmetic and the 520 byte limit (the `OP_TXHASH` proposal worked
around this by hashing the result, for example, which is awkward for
anything other than direct comparisons of large parts of the
transaction).
Secondly, the reason this is specified now is that its existence
clarifies the requirements for varops: if an input can query the rest of
the transaction, giving it a budget based on its own weight would be
overly restrictive, so the varops budget (unlike the sigops budget) is
transaction-wide.
Finally, note this contains a runtime OP_SUCCESS semantic, for
unknown control word bits. This is the most future-proof option, and as
the control word must always be moderated by the script (usually, stated
outright), does not seem to leave room for accidental success.
Looking forward to your feedback!
Rusty.
<pre>
BIP: ?
Layer: Consensus (soft fork)
Title: OP_TX
Author: Rusty Russell <rusty@rustcorp•com.au>
Comments-URI: TBA
Status: Draft
Type: Standards Track
Created: 2025-06-16
License: BSD-3-Clause
</pre>
==Introduction==
===Abstract===
This BIP introduces a new opcode (OP_TX) for tapscript v2, which allows for convenient and explicit introspection of the current transaction. It redefines the OP_SUCCESS189 tapscript opcode (0xbd) as OP_TX.
===Copyright===
This document is licensed under the 3-clause BSD license.
===Motivation===
The original Bitcoin design was to avoid reliance on trusted third parties, and the scripting system was a way of ensuring this: that the network itself would enforce the spending conditions for an output. This aim is undermined if users cannot specify, with all possible precision and power, the conditions under which their coins can be spent.
[[bip-unknown-script-restoration.mediawiki|BIP-scriptrestore]] restores full programmability to Bitcoin's scripting system, but does not make it convenient. The main missing piece is the ability to directly query the transaction attempting to spend an output, beyond merely checking that it is signed by a given set of keys. Many proposals to use this functionality have emerged, including new signature modes, spending restrictions even in case of leaked key material, and more complicated multiparty protocols.
This opcode allows directly extracting each piece of the current transaction's contents (and some of its context) onto the stack. This allows arbitrary arithmetic on values and examination of scripts, but an opcode which only allowed extraction of a single type of value at a time would be inefficient in the very common case where specific fields of the transaction are required to be specific exact values. So we explicitly support extraction of multiple values into a single, concatenated stack element, ready for hashing and comparison.
==OP_TX==
OP_TX operates as follows:
- If the stack is empty, fail validation.
- Pop the ''selection_vector'' off the stack (as a little-endian unsigned variable-length integer).
- If ''selection_vector'' contains bits not defined in the table below:
- Immediately succeed script validation.
- Extract the two input selector bits from ''selection_vector'':
- TXSELECT_INPUTSELECT_CURRENT: ''first_input'' = current input, ''num_inputs'' = 1
- TXSELECT_INPUTSELECT_ALL: ''first_input'' = 0, ''num_inputs'' = number of inputs
- TXSELECT_INPUTSELECT_SINGLE:
- if the stack is empty, fail validation.
- otherwise pop ''first_input'' off the stack. ''num_inputs'' = 1.
- if the input numbered ''first_input'' does not exist, fail validation.
- TXSELECT_INPUTSELECT_RANGE (pop start, number from stack)
- if the stack has less than 2 elements, fail validation.
- otherwise pop ''num_inputs'' then ''first_input'' off the stack.
- Extract the two output selector bits from ''selection_vector'':
- TXSELECT_OUTPUTSELECT_CURRENT: ''first_output'' = current input, ''num_outputs'' = 1
- TXSELECT_OUTPUTSELECT_ALL: ''first_output'' = 0, ''num_outputs'' = number of outputs
- TXSELECT_OUTPUTSELECT_SINGLE:
- if the stack is empty, fail validation.
- otherwise pop ''first_output'' off the stack. ''num_outputs'' = 1.
- if the output numbered ''first_output'' does not exist, fail validation.
- TXSELECT_OUTPUTSELECT_RANGE (pop start, number from stack)
- if the stack has less than 2 elements, fail validation.
- otherwise pop ''num_outputs'' then ''first_output'' off the stack.
- If TXSELECT_COLLATE is set in ''selection_vector'':
- push an empty element onto the stack.
- "output" means "concatenate to the top stack element".
- "varint or int" means output the value as a CompactSize
- "variable size object" means the length as a CompactSize, then the object
- Otherwise:
- "output" means "push a new element on the stack under the previous output elements" (i.e. the last output will be the top stack element).
- "varint or int" means output the value as a minimally-encoded little-endian integer
- "variable size object" means output the object (without length prepended)
Examine bits in ''selection_vector'':
- If TXSELECT_VERSION is set:
- output the nVersion (32 bits)
- If TXSELECT_NUM_INPUTS is set:
- output the number of inputs as varint or int
- For each existing input ''i'' in the range ''first_input'' to ''first_input'' + ''num_inputs'':
- If TXSELECT_INPUT_OUTPOINT_HASH is set:
- output input ''i''s outpoint hash (32 bytes)
- If TXSELECT_INPUT_OUTPOINT_INDEX is set:
- output input ''i''s outpoint index (32 bits)
- If TXSELECT_INPUT_SCRIPT is set:
- output input ''i''s script as a variable size object
- If TXSELECT_INPUT_SEQUENCE is set:
- output input ''i''s nSequence (32 bits)
- If TXSELECT_INPUT_AMOUNT is set:
- output input ''i''s input amount (64 bits)
- If TXSELECT_INPUT_SCRIPTPUBKEY is set:
- output input ''i''s scriptPubkey as a variable size object
- If TXSELECT_INPUT_NUM_WITNESSES is set:
- output input ''i''s number of witness elements (including any annex) as varint or int.
- For each witness stack element (including any annex) in input ''i'':
- If TXSELECT_INPUT_WITNESS is set:
- output the witness stack element as a variable size object
- If TXSELECT_NUM_OUTPUTS is set:
- output the number of outputs as varint or int
- For each existing output in the range ''first_output'' to ''first_output'' + ''num_outputs'':
- If TXSELECT_OUTPUT_AMOUNT is set:
- output this output's amount (64 bits)
- If TXSELECT_OUTPUT_SCRIPT is set:
- output this output's scriptPubkey as a variable size object
- If TXSELECT_LOCKTIME is set:
v - output the nLocktime field (32 bits)
- If TXSELECT_CURR_INPUT_NUMBER is set:
- output the current 0-based input number as varint or int.
- The varops cost for each operation is 2 units per byte output.
- As always, validation fails if:
- the top stack element exceeds 4,000,000 bytes, or
- the total bytes on the stack exceeds 8,000,000 bytes, or
- the total number of stack entries exceeds 1000, or
- the varops cost exceeds the remaining varops budget.
===Rationale===
The runtime success semantics for unknown bits in ''section_vector'' improves upgradability, by allowing other bits to be specified in future in a backwards-compatible manner. Unfortunately, unlike OP_SUCCESS semantics defined in [[bip-0342.mediawiki|BIP342]], this cannot generally be evaluated before script execution begins, because it's possible (and occasionally useful) to allow ''section_vector'' to be altered at runtime. Where this is done, it needs to be constrained to a certain subset anyway (such as setting TXSELECT_COLLATE), so preventing unknown bits is not a significant additional burden on the script.
The use of four selection values for inputs and outputs recognises the common cases of either all, or only the current input (and, SIGHASH_SINGLE-style, the matching output) while allowing more flexibility for the general case of requesting specific inputs or outputs.
The TXSELECT_COLLATE option is an optimization for concatenation; the representation of numbers changes from arithmetic-convenient to linearization-convenient, and size prepended to variable length objects, so that there is no possible ambiguity over the results.
The order of field output is txid order, with fields which do not appear in the txid appended appropriately. Without a strong demonstrated reason for a different ordering in practice, using established ordering should be preferred.
The varops cost is slightly higher than simply stack copying, reflecting a conservative view that some fields are not as immediately available as stack elements. It is still cheap enough to allow accessing the entire transaction many times over: the weight of the OP_TX opcode itself provides enough budget to access 2600 bytes.
===Values For TXID-Relevant Selectors===
These selectors enumerate parts of the transaction which are hashed into the txid:
{|
! Selector Name
! Selector Value
|-
|TXSELECT_COLLATE
|0x1
|-
|TXSELECT_VERSION
|0x2
|-
|TX1SELECT_NUM_INPUTS
|0x4
|-
|TXSELECT_INPUT_OUTPOINT_HASH
|0x8
|-
|TXSELECT_INPUT_OUTPOINT_INDEX
|0x10
|-
|TXSELECT_INPUT_SCRIPT
|0x20
|-
|TXSELECT_INPUT_SEQUENCE
|0x40
|-
|TXSELECT_NUM_OUTPUTS
|0x80
|-
|TXSELECT_OUTPUT_AMOUNT
|0x100
|-
|TXSELECT_OUTPUT_SCRIPT
|0x200
|-
|TXSELECT_OUTPUT_LOCKTIME
|0x400
|}
===Values For Input Selector and Output Selector Bits===
The input selectors, controlling which inputs are enumerated, are bits 11 and 12:
{|
! Selector Name
! Selector Value
|-
|TXSELECT_INPUTSELECT_ALL
|0
|-
|TXSELECT_INPUTSELECT_CURRENT
|0x800
|-
|TXSELECT_INPUTSELECT_SINGLE
|0x1000
|-
|TXSELECT_INPUTSELECT_RANGE
|0x1800
|}
The output selectors, controlling which outputs are enumerated, are bits 13 and 14:
{|
! Selector Name
! Selector Value
|-
|TXSELECT_OUTPUTSELECT_ALL
|0
|-
|TXSELECT_OUTPUTSELECT_CURRENT
|0x2000
|-
|TXSELECT_OUTPUTSELECT_SINGLE
|0x4000
|-
|TXSELECT_OUTPUTSELECT_RANGE
|0x6000
|}
===Values For Input Metadata Selection===
These select additional data about the input which is not committed in the txid, as well as the current input number:
{|
! Selector Name
! Selector Value
|-
|TXSELECT_CURR_INPUT_NUMBER
|0x8000
|-
|TXSELECT_INPUT_AMOUNT
|0x10000
|-
|TXSELECT_INPUT_SCRIPTPUBKEY
|0x20000
|-
|TXSELECT_INPUT_NUM_WITNESSES
|0x40000
|-
|TXSELECT_INPUT_WITNESS
|0x80000
|}
==Reference Implementation==
None as yet.
==Thanks==
I stand here on the shoulder of giants, including Russell O'Connor's original OP_TXHASH proposal and Steven Roose's subsequent elaboration of that work. I also credit Jeremy Rubin for his tireless work exploring the uses of covenants and his OP_CHECKTEMPLATEVERIFY proposal.
While I ultimately found those proposal unsatisfying, it was their daunting intellectual prowess which forced me to slowly develop the groundwork for a proposal to meet the high standards they have set.
==TODO==
The ordering of bits in the ''selection_vector'' is unoptimized: if a mask only uses the first four bits it can be put on the stack in a single opcode byte, two opcode bytes for eight bits, three for sixteen. This is ripe for consideration by greater minds (aka "bikeshedding").
In particular, an all-zero mask for OP_TX makes no sense, so it could be defined to mean some pre-determined mask: for example, the OP_CHECKTEMPLATEVERIFY proposal commits to the data in (though not the form of) COLLATE|VERSION|NUM_INPUTS|TXSELECT_INPUT_SCRIPT|NUM_OUTPUTS|OUTPUT_AMOUNT|OUTPUT_SCRIPT|OUTPUT_LOCKTIME|CURR_INPUT_NUMBER.
--
You received this message because you are subscribed to the Google Groups "Bitcoin Development Mailing List" group.
To unsubscribe from this group and stop receiving emails from it, send an email to bitcoindev+unsubscribe@googlegroups•com.
To view this discussion visit https://groups.google.com/d/msgid/bitcoindev/87y0q0m8vz.fsf%40rustcorp.com.au.
next prev parent reply other threads:[~2025-09-27 13:23 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-27 8:12 [bitcoindev] [0/4] A Bitcoin Scripting Proposal BIP Quartet Rusty Russell
2025-09-27 11:27 ` [bitcoindev] [1/4] Varops Budget For Script Runtime Constraint Rusty Russell
2025-09-27 11:28 ` [bitcoindev] [2/4] Restoration of disabled script functionality (Tapscript v2) Rusty Russell
2025-09-27 11:29 ` Rusty Russell [this message]
2025-09-27 11:29 ` [bitcoindev] [4/4] New Opcodes for Tapscript v2 Rusty Russell
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=87y0q0m8vz.fsf@rustcorp.com.au \
--to=bitcoin-dev@rustcorp$(echo .)com.au \
--cc=bitcoindev@googlegroups.com \
--cc=julianmoik@gmail$(echo .)com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox