[bitcoin-dev] maximum block height on transaction

ZmnSCPxj ZmnSCPxj at protonmail.com
Mon May 3 02:30:07 UTC 2021


Good morning Billy, and list,

> -   Using an opcode would greatly increase CPU usage because the script cache would need to be reworked (and probably cannot be made to work).
> -   Adding a field would greatly increase the code complexity to the level of SegWit, without all the important bugfixes+features (tx malleability, quadratic sighash, well-defined extensible outputs) that SegWit provides.

Sometimes, the only way out is through.

A general idea to get around this would be:

* Define a "hidden" field of a transaction, which is not existent in *any* serialization of the transaction.
* Set a default value for this field that would be compatible with pre-softfork rules.
* Have an opcode that manipulates this field, carefully designed so it is idempotent.

The above general idea is not original to me, I believe.
I think I have seen it elsewhere on the list, possibly in discussions around sidechains, though my primary cache is unable to fetch and additional searches through unindexed storage is taking too long.

So, for this particular case, here is a (non-serious) proposal to implement a maximum block height on transactions.

* Create a new field `u32 nMaxHeight` on `CTransaction` that is not serialized in any transaction format.
  * A block is not valid if any transaction in it has an `nMaxHeight` larger than the block height of the block.
  * Default value is `0xFFFFFFFF`.
* Add a new opcode `OP_SETMAXHEIGHT` that replaces an existing `OP_NOP`.
  * The opcode must be followed by an `OP_PUSH` of a 32-bit value, else script validation fails.
    * This prevents using a computed value, instead the value must be given as a constant in the script text.
      This is a precaution to reduce the risk that execution of the script at a different time or different computer or etc will result in a different value that the `OP_SETMAXHEIGHT` opcode uses, which can cause consensus divergence.
      If we figure out later that this precaution is not necessary, we can just use another `OP_NOP` for `OP_SETMAXHEIGHTFROMSTACK`.
  * If the current `nMaxHeight` is larger than the given value, then the `nMaxHeight` is set to the given value.

The above avoids issues with opcodes --- the script interpreter can continue to be executed in the only place it is in, i.e. at entry into the mempool.
It also avoids some of the code complexity with fields, since the field is non-existent in any serialization of the transaction, but is instead implied by the scripts that the transaction causes to be executed, reducing the need to identify pre-softfork peers and baby-talk to them --- the baby-talk simply contains "parental bonuses" that are understood by upgraded nodes who are "in the know".

Additional complications, such as the need for an index of `nMaxHeight` for transactions in the mempool (to remove transactions whose `nMaxHeight` is now in the past), and the additional checks needed when receiving an in-block transaction that is not in the mempool, are left to the reader.
Similar field and opcode for `CTransactionInput` for a relative-time max height are also left as an exercise to the reader.

> -   You can do what you want with a second `nLockTime`d transaction that spends the output anyway.

The advantage of this functionality is that you can be safely offline at the time the timeout occurs in any complicated timeout-based contract.

Typically, when using say an HTLC, the contractor who holds lien on the timelock branch, has to be online at the time the timelock becomes valid, in order to impose a timeout on the hashlock branch.
However, if instead the hashlock branch includes an `OP_SETMAXHEIGHT`, then the contractor holding lien on the timelock branch does not have this risk.

However, the contractor holding the lien on the hashlock branch now has increased risk.
If the timeout is approaching, and suddenly there is high mempool usage at the time, then a claim of the hashlock branch may fall off the mempool due to `nMaxHeight` violation.
But the transaction claiming the hashlock branch has been published and the preimage has been published in mempools all over the world, thus the contractor holding lien on the hashlock branch risks not being compensated for revelation of the preimage.

Whereas with the current way things are, the timelock-holder is at risk, and the hashlock-holder has reduced risk since even if the timeout arrives, there is still the possibility that the hashlock branch is what gets confirmed, whereas with `OP_SETMAXHEIGHT` the hashlock-holder has 0 chance of getting the hashlock branch confirmed in case of sudden spike in onchain usaage.

Thus it seems to me that this scheme does not really *improve* Bitcoin significantly, it only moves risks from one participant to another in a two-participant contract.
Thus, this proposal is not particularly serious.

Regards,
ZmnSCPxj


More information about the bitcoin-dev mailing list