CHIP-2025-01 TXv5: Transaction Version 5

Podcast talking at length about this CHIP and the 2026 proposals: Fiendish & Friends #11 - Jason Dreyzehner talks 2026 CHIPS OP_EVAL, P2S, and Loops

On youtube:

Includes some discussion about how two components could reasonably be extracted from TXv5 (if BCH doesn’t have sufficient market dominance by 2026 to lock-in a transaction format upgrade), making this into 3 CHIPs:

  1. Read-only inputs – the implementation in the CHIP is already backwards compatible, the TXv5 part just adds a more efficient encoding
  2. Unified bytecode length limits
  3. The v5 encoding itself (everything else in this CHIP)

If an improved encoding format still looks too disruptive in early 2026, I’ll just propose the smaller, backwards-compatible items (1 and 2) for 2027, and re-propose the v5 encoding for 2028.

However, as I tried to get across in the podcast, I consider the 2026 proposals many times more important than any part of TXv5.

My estimated ranking:

  1. OP_EVAL – 1,000 - 10,000x improvements in algorithmically complex use cases, required for practical zero-knowledge proofs, and even a modest improvement for nearly all other BCH contracts
  2. P2S – unlocks new use cases and wallet patterns (e.g. covenants can operate without off-chain tracking of redeem bytecode, some interactions can be atomic rather than requiring a chain of transactions, eliminates need for sidecar inputs in many cases, etc.)
  3. Loops – less impactful than OP_EVAL, but in the cases where it can be used, improves compression by 20-30% vs. OP_EVAL (and makes compiled contracts easier to audit vs. recursion-based iteration)

On the other hand, the TXv5 improvements are long-term important, but not urgent:

  1. Detached signatures, non-push unlocking bytecode – Up to ~60% savings in the best cases (esp. CashFusion)
  2. Read-only inputs: seems like 10-50% reduction in the most complex covenant transaction sizes (on the higher end without the 2026 proposals, on the lower end with them)
  3. Other TX format trimming – Less than 10% reduction (but applies to nearly every v5 transaction, so the savings are substantial over years/decades)
  4. Unified bytecode length limits: saves ~40-75 bytes per 10,000 bytes (less than 1%, but reduces needless contract complexity)
5 Likes

Is the idea here to run some introspection opcodes to just copy stuff from some other input, thus avoiding the need to replicate common data across all inputs? Why did people of the past ever introduce this when they could have just prohibited OP_RETURN from appearing in unlocking bytecode?

Consider adding this to the roadmap, way for spenders to “buy” more bytes for the purpose of getting a bigger operation budget:

Same method could be used to add a byte to declare an input read-only. Speaking of which, read-only inputs would need a new set of introspection opcodes, right? Else they could be used to fool old contracts.

Also, I’d love it if we could reliably access mature headers (at greater security than SPV): something like <block_hash> OP_BLOCKHASHVERIFY that would pass only if referenced hash is 100 or more blocks deep, else fail the TX.

1 Like

Yes, or any other computation that more efficiently produces a particular stack item than pushing the raw bytes (e.g. providing a number via squaring rather than directly pushing the product).

Initially, IIRC just uncertainty about whether similar vulnerabilities existed. And they were right to be careful: non-push unlocking operations always allow third-party malleability without some way to sign unlocking bytecode (like TXv5 detached signatures).

Yes, thank you! That’s definitely something that should at least be reviewed in the rationale (even if left to a separate upgrade). Could also be set as sequence number bit 24, input bitfield bit 4, and an additional compact Uint input field (like age lock, but the value wouldn’t be inspectable via sequence number, so would still need an introspection operation; if either 6 or 8 bits could be sufficient, encoding directly in the sequence number enables full backwards compatibility and saves the codepoint).

I hope we’ll also get a better understanding of any use cases and requirements here over the next 1-2 years.

I don’t think so:

  • Existing UTXOs could only be referenced by a valid spend, so by definition, if you can reference a UTXO, you could have spent it in the same way.
  • Existing UTXOs can’t be negatively affected by unintended references, as references are not even observable by simultaneous and later transactions.
  • Any existing UTXOs inspecting for transfers of assets will simply fail if a read-only input fails to transfer an asset, just as it would fail if the transaction implicitly burned that asset (as mining fees for BCH, or simply burned for CashTokens).
  • So concern falls only to intended-sibling UTXOs that check for spends of known UTXOs or token identities in sibling inputs: here again, if you’re able to successfully reference a pre-upgrade UTXO, you could have simply spent it. Additionally, if you’re not checking the actual outputs for an asset transfer, you’re already susceptible to malicious burning.

I’m interested in others’ reviews of this question, but so far I think it’s hard to contrive a scenario in which read-only inputs impact an existing use case. The proposed implementation is even consistent with the precedent from BIP68 by defining unused sequence number bits.

I think this can safely be a separate CHIP and forum topic, I don’t think there’s much interaction with transaction encoding.

3 Likes

If we relax input bytecode rules then any spender could add some <0x00...00> OP_DROP somewhere in unlocking bytecode to get more budget, even if not anticipated by the locking bytecode, so that could work - but it would spend bandwidth. However, some compression at transport layer could address that just as well.

Sure, just thought to add here considering you laid out a general roadmap in post I was replying to.

1 Like

Probably the better idea is to make cash fusion be used more. This is already available today.
On top of that, if this massive upgrade can’t take place until bch is a top-10 coin, then any problems people may have with fusions are then also solved by having a massively bigger anonymity pool and number of people wanting to fuse with you.

A contract today where someone burns a NFT to redeem some coins could be exploited.
If the covenant checks that an input contains a specific NFT and that NFT is not in any of its outputs it’s implicitly burned. If read-only spends is possible that NFT can be spent endless times and drain the covenant. Or am I missing something?

4 Likes

Yes, you’re totally right. Thanks! Covenants can currently depend on implicit burning of assets, so any inspection of a read-only input can violate that assumption. :100:

Alright, so before any introspection opcode can inspect a read-only input, we’ll also need some means for the locking bytecode to have opted-in (e.g. a prefix).

3 Likes

Or, allow any UTXO to be referenced as read-only: the spender decides whether it’s to be consumed or to be referenced. In that case, you need a new set of introspection opcodes, or some VM state toggle to switch mode of existing introspection opcodes.

We need to answer some key design decision questions:

  1. Should UTXOs be declared read-onlyable at their creation? Why?
  2. Should read-only inputs’ scripts be evaluated or not? Why?

These will then drive detail design.

If 2. is No, that means contracts could require things like “prove that this address still holds a balance” - and the spender would simply reference the address UTXO(s) to show them to the contract (bring it into TX evaluation context but without affecting UTXO state), even if the spender doesn’t have authority to spend it (e.g. can’t produce signature for some P2PKH UTXO).
It would also mean you could create UTXOs that are unspendable on their own, with their purpose being just to be referenced by some TX as read-only and have their locking bytecode introspected and executed in another input’s script (using op_eval).

2 Likes

No. We don’t force the separate configuration of any other spending condition at UTXO creation, it would be very arbitrary to draw a new distinction here.

UTXO creators can always define such requirements in their contract: OP_INPUTINDEX OP_INPUTSEQUENCENUMBER test_read_only [OP_NOT] OP_VERIFY (where test_read_only depends on the ultimate encoding, just like the current behavior of age lock inspection). Though by definition, read-only references can’t impact later transactions via e.g. spend races or modifying the UTXO, so it only matters in situations where the UTXO creator wants to e.g. extract a rent by disallowing direct read-only references on-chain (like a pay-per-use on-chain oracle).

It’s critical that they can be evaluated, as those inputs are the ideal location for transactions to include signatures or proofs that are validated by the read-only input’s locking bytecode.

Given density-based VM limits, the proof material should ideally extend the limits for the actual contract performing the verification. Even assuming eventual OP_EVAL support, introspecting and evaluating the contract from a separate “consumed sidecar” input wastes at least 40 bytes (and might also complicate wallet implementations, as they’re forced to track the movement of the consumed sidecar input).

I can imagine it also being valuable for read-only inputs to optionally not be evaluated (i.e. the referencer couldn’t otherwise spend the UTXO, they’re just introspecting it as part of another contract). That is a much larger scope than proposed in the TXv5 CHIP (and could reasonably be a separate upgrade), but I’d be interested in more research on the topic! If we can prove it doesn’t break other existing contract assumptions, it could certainly give contract authors more flexibility.

Nit: if you’re just using the “script” to store a large push of data for introspection from another input (and the data isn’t just a representation of zero), empty unlocking bytecode already produces a successful evaluation, no need for the evaluation skipping behavior. (Also RE OP_EVAL: note again the above discussion of consumed sidecar inputs wasting 40 bytes if read-only evaluation isn’t available.)

4 Likes

Just finished my first proper read of this CHIP.

Read-only inputs are pretty radical! Almost spat out my coffee and it took me a few seconds to convince myself we’d still have a DAG! :rofl: It’s very novel and would be very powerful, but will definitely break some assumptions and mental models along the way. :exploding_head:

Something I don’t understand is how read-only inputs can “deduplicate bytecode across transactions”. My understanding is they can only deduplicate unlocking bytecode, so do we need P2S for dedup of large scripts or does this work with P2SH by somehow deduping redeem bytecode?

4 Likes

Thanks for the review @rnbrady!

Read-only inputs have actually been deployed on various UTXO networks for years (and really, Ethereum too in the form of read-only state access): Corda “reference states” (2016), Hyperledger Fabric “read sets” (2017), Ergo “data inputs” (2019), and Cardano “reference inputs” (2022).

They’re a very natural evolution of UTXO systems though (“why do we have to delete and recreate it every time?”), so I’m sure lots of researchers independently stumble on it. I don’t know of any sources that previously identified it as a solution to the covenant UTXO recycling issue (which – unsolved – leaves more and more unspendable dust in the UTXO set forever), but other chains also have different dust/fee policies and behaviors – that might be less applicable outside of Bitcoin (Cash).

Right, P2SH doesn’t make much sense for most public covenants, you save bytes in both the creation and spend TXs by skipping the P2SH wrapper with P2S. (Doesn’t waste storage/bandwidth/fees + simpler for wallets: no need to track separate P2SH hashes in addition to contract params.)

Off the top of my head, I’m not coming up with a plausible case for deduplication with P2SH; you can always save ~40+ bytes by omitting a read-only input and pushing all the redeem bytecode in just one input, so I think it’s probably only relevant for deduplicating locking bytecode. (Though detached signatures and non-push unlocking bytecode can definitely deduplicate a lot of redeem bytecode! E.g. 62% of this CashFusion TX.)

4 Likes

A topic I’d like to think more about: if we were to expand the scope this way – allow unevaluated reading of inputs (whereas TXv5 currently requires you to be able to spend the input) – without an explicit method for inputs to “opt in”, there seem to be additional strategies for interfering with incentives of particular contracts.

E.g. is it easier to create trustless “pay-to-censor” systems? Like a UTXO that pays once per block via anyone/miner-can-spend if the referenced UTXO remains unspent. If you point such a contract at some critical UTXO in a DEX system, it’d be a little like tossing a wheel clamp on the DEX.

While it’s always possible to pay miners to censor (out-of-band or likely even via some on-chain ZKP approaches), it’s logistically much easier and more likely to be effective if honest network nodes assist with the discovery and relaying (in the same way that widespread RBF eliminated practical zero-conf security on BTC) and particularly if miners can run software to automatically find and claim relevant bounties without adding new security assumptions.

Anyways, if unevaluated read-only inputs were allowed, I think it would probably be wise to require UTXOs to opt-in to the behavior, and read-only inputs would require evaluation by default (i.e. the referencer would otherwise be authorized to actually spend the input).

1 Like

I think this is a conversation worth revisiting now that we have successfully locked in the may 2026 upgrade and discussions/brainstorming for next upgrade cycle slowly starts.

To me the " Unified bytecode length limits" seems straightforward if we want to enable onchain ZKP verification.

There was also the idea during the discussions on the loops chip to potentially lower the base opcost (from 100 to 10)

3 Likes

Just to drop few thoughts re. read-only inputs. What problem are we aiming to solve?

  1. Give contracts indiscriminate read-only access to UTXO state (with open question of whether there should be a flag to toggle direct evaluation or just reference it for other input’s use through introspection)
  2. Global function tables

If we have 1) we can emulate 2), but if we want 2) why not just go for 2)? Becuase 1) is a kind of hacky way to get 2). Having both would be useful: 1) for read-only access to general UTXO state 2) for having an efficient global function table

Global Function Tables

Extend the UTXO model with special global function definition UTXOs. Imagine if you could create an output with locking bytecode: PFX_DEFINE <bytes> <lifetime>. This output skips the UTXO database and is treated as unspendable (similar to OP_RETURNs).

Once mined, the output is added to another database, a global function table, a simple key-value store: hash(bytes), bytes, sum(lifetime). Each re-definition (people creating outputs with exact same <bytes>) just extends the lifetime. If the lifetime expires, we could allow garbage collection with a special input unlocking bytecode: PFX_UNDEFINE <hash(bytes)> that adds to TX fee. This would allow the option for the network to price adding these definitions (min. dust requirement based on lifetime & size) and expired balance would be paid to miners. It could be re-defined later, and it will again get the same hash. So contracts relying on it can’t be broken by the dependency being purged - you can just create a new TX to re-define the functions your contract needs.

Usage: <hash(bytes)> OP_INVOKE from any Script. If the entry exists and hasn’t expired then the function is retrieved and evaluated. If the entry has expired or doesn’t exist then the OP_INVOKE call fails.

Alternative to read-only inputs: global UTXO introspection opcodes

This avoids having to carve exceptions to avoid old contracts introspecting other inputs being fooled by read only inputs. Just allow direct read opcodes, without having to add input references to TX at all:

  • <txid:vout> OP_EXTERNALUTXOVALUE
  • <txid:vout> OP_EXTERNALUTXOBYTECODE
  • <txid:vout> OP_EXTERNALUTXOTOKENCATEGORY
  • <txid:vout> OP_EXTERNALUTXOTOKENCOMMITMENT
  • <txid:vout> OP_EXTERNALUTXOTOKENAMOUNT

or just overload existing opcodes:

  • <txid:vout> OP_UTXOVALUE
  • <txid:vout> OP_UTXOBYTECODE
  • <txid:vout> OP_UTXOTOKENCATEGORY
  • <txid:vout> OP_UTXOTOKENCOMMITMENT
  • <txid:vout> OP_UTXOTOKENAMOUNT

which would be forward-compatible with any future fields/introspection we may add to UTXOs.

Implementations could cache these external UTXO retrievals, load them once into context on first call, and with VM limits framework in place we could budget these calls by number of unique UTXOs loaded.

With this we lose garbage collection. If used for global function definitions and to avoid contracts getting broken users would have the incentive to create indestructible UTXOs for their function definitions, so there’d be a monotonically growing number of these indestructible zombie UTXOs. Probably won’t ever become a problem, how many of these could people possibly create?

6 Likes

I’m excited about read-only inputs but need to research them further. For now, I’m for just going with Jason’s suggestion for read-only inputs for the following reasons:

  1. The implementation is simple and consists of, as far as I know, just small tweaks to how things are done today. This simplifies validation of the design, makes the implementation low-risk, and leaves the node implementation no more complex than today. This makes it easier to get consensus for including read-only inputs already for May 2027!

  2. Read-only inputs on their own still deliver a lot of value. They are like functions at the Tx level. It looks like Jason believes having read-only inputs is enough to allow for ‘Zero-Knowledge Proof Covenants’ with cheap transactions. This is a big win in itself.

    https://github.com/bitjson/bch-txv5?tab=readme-ov-file#zero-knowledge-proof-covenants

    From what I understand, this would then also open up for privacy covenants with low fees.

  3. If there are cases where lock-scripts really need to have access to “global functions” via OP_INVOKE then, as you mention, it can be emulated by using introspection to bring in code from read-only inputs into the local function table. If the read-only input is bespoke for the lock-script in question (or if the functions in the read-only input don’t reference each other) then this can be done without relocation of function slot references in the library, which makes the procedure straight forward. As opcode cost is reduced, this procedure becomes less of a big deal. And if we were to later add OP_EVAL it is possible to write read-only input code that is broken down into functions but does not require relocation.

We are still limited by the 201 byte ‘Locking Bytecode Length’ limit for read-only inputs though, right? Not sure how keen people are on raising that. The same question would apply to global function tables too.

3 Likes

Fiendish & Friends episode is dead on Twitter but still available here on RSS for anyone like me who wants to review it.

1 Like

Regarding relocation of function references. The fact that the Functions CHIP moved away from having function references be integers (0-999) to instead allowing arbitrary byte strings (0 to 7 bytes in size) makes relocation much less likely to be needed. The VM only allows for one thousand functions, but the set of valid function identifiers is massive. This allows for name spacing. So a ZK verifier library (brought in via read-only inputs) could have all its functions prefixed with “zk” (reflected in its internal self references) and that way avoid conflict with function names in contracts using it.

2 Likes

I deployed a Proof of Concept for “external libraries as read-only inputs” on Chipnet:

  • TX that deploys the libraries and the contract:

    • 9d4e2a4c0bc0a958352b397869a1c3ea6c5214a5ab515b9908f68637fe51cd21
  • TX that spends the funds in the contract:

    • dd2896bfcbdab85422ffc388aa6fd6dfe4a8c1c771e25f090c4753cc5b291d8b

The contract uses functions that it imports from the library UTXOs. Since we don’t have read-only inputs on Chipnet the library UTXOs do get consumed. But this illustrates the concept.

More details here: AlbaDsl & albaVm: Haskell based DSL and VM for Bitcoin Cash 2025 contract programming - #34 by albaDsl

4 Likes

One thing which I would also like to see in a new transaction version (tx v5) is support for natively buying compute budget, without the need for empty byte padding to the transaction. I assume this would have to be a native field in the transaction structure for this.

I believe @bitcoincashautist has also brought it up that it is silly that densely compute-packed scripts need to bloat the tx size, which affects bandwith usage and storage, for no benefit to anyone.

We’d need to introduce the concept of “virtual bytes” which would be included in the compute budget calculation.

On verifier.cash, the benchmarking for BCH native Groth16 verifiers, i see that the zero-byte padding is currently 25% of the tx size for BN254 and 19% for Groth16 BLS12-381

note that these are the most simple ZKP constructions, this would be a floor measure for advanced contracts

5 Likes

Yeah buying additional bytespace instead of padding transactions seems like such a clear and obvious upgrade now that the VM limits have been set and people are using more advanced scripts with all the fresh OP_codes.