CTV+CSFS: Can we reach consensus on a first step towards covenants?

The post you are linking to mentions OP_VAULT. It seems inappropriate to use as motivation for a proposal, a statement in favour of a separate proposal.

This post does not mention OP_CTV either. Also, AnchorWatch is great but it’s a company that launched less than 3 months ago.

Meanwhile Bob McElrath stated that from his experience researching vaults for years at Fidelity Digital Asset, one of the largest Bitcoin custodians in the world, vaults sounded like a good idea but were not in practice. The CEO of Nunchuk, a popular Bitcoin wallet in operation since 2021, stated that after investigating vaults he did not believe those were practical for end users (“plebs”). The CEO of Wizardsardine, who spent years developing vaults and otherwise developing advanced custody products also stated that in practice the revealed preferences of people is that they’d rather not take on the burden that comes with managing a vault. The reasons stated by every single of these persons (as well as myself) for their skepticism of the value of vaults in practice is all the infrastructure that comes with it (watchtowers, etc). These opinions were informed by their experience in actually building all that comes with a product targeting real-life end users, and this would also apply to an ideal vault architecture with an opcode dedicated to it[1].

Now, all these discussions you brought up are about vaults that the proposal at hand does not enable. So i hope we can “put this matter to rest” and come back on topic to usecases actually enabled by the combination of OP_CTV and OP_CSFS. So far i see three:

  • Make LN-symmetry a possible option for Lightning developers
  • Make it easier for Lightning to upgrade to PTLCs (regardless of LN-symmetry usage)
  • Improve Ark (@stevenroose could you lay down the exact improvements?)
  • Reduce interactivity in various (potentially future or not yet known) protocols and applications
  • In theory be helpful for UTxO management for Liquid watchmen, which would be fair to generalize to custodial sidechains in general

  1. Something like OP_VAULT seems to have other interesting usecases beyond vault, but again, this is off-topic for this thread. ↩︎

A prerequisite for any vault is unambiguously locking the coins to a certain destination during the trigger stage. CTV is the most basic way of doing this; in fact, anything that is capable of accomplishing this is either CTV or a generalization. So an argument for the value of vaults is in essence an argument for CTV (or something more general that encompasses it as one mode).

One (or three) custodians saying “I’m not going to use taproot, it doesn’t make sense for me” isn’t an argument against taproot. Especially if you have a number of other custodians (who are more current and not working on competing products) saying, “yes, I’d really like this, it would make a difference for my business.”

1 Like

I disagree. If people are proposing to change Bitcoin in X manner, they should make the case for X. Not for a future theoretical Y change that X combines well with. Y should only matter to argue that X won’t be rendered obsolete in the near future (which Steven did pretty convincingly in OP), to convince people it is worth going through a soft fork for X without waiting for Y.

1 Like

There are several other developers and users who need vaults with covenants. This includes companies with successful products and services.

What do others achieve by blocking someone from using different types of vaults?

VERIFY is also important for upgradeability; if you don’t have the <32-byte-hash> parameter, you can’t make the opcode upgradeable.

Now maybe in a tapscript context that’s okay because you’ve got so much unused opcode space.

I’m not averse to it, I’m just trying to figure out why you want it, since all the uses of CTV I’m familiar with are VERIFYs - I gather it’s to make ln-symm more efficient or something? I’m not trying to be glib here, I just think the motivation for what you suggested wasn’t explicitly mentioned.

I’m totally fine with having adding a tapscript-only OP_CHECKTEMPLATEHASH that pushes the hash to the stack if it helps someone, that’s not a lot of marginal complexity. But I think the idea that it would replace CTV as-is is sort of a different proposal.

Again, not trying to be glib here but is there a more specific reason than “each reviewer has to review about 15 marginal lines of not-substantially-novel code?” What about legacy is so complicated that it really moves the needle?

I understand the argument that we only have so much NOP-space left, but given the bare CTV case is “most compact way possible to commit to n future spends,” I think it probably justifies the allocation.

Please grant me the goodwill to admit it’s a reasonable thought that if chainspace ever does become massively short, congestion control is a good escape hatch to have on hand - even outside of the use for miners today.

Making my first post at Delving (only because I’m specifically referenced, I just lurk otherwise) to say the post does not mention OP_CTV specifically, but the need to make a commitment to a future transaction like the primitive of what OP_CTV enables would be a necessary part of the vaulting structure from my view.

Most of my reading of OP_VAULT is from the official BIP 345 which makes several references to OP_CTV.

I’m in support of CTV + CSFS.

3 Likes

EDIT: To avoid derailing this conversation with questions and suggestions on the below reply, I created a separate topic here where the post is copied and reactions can be posted :pray:

Because it was requested, let me make the Ark case for CTV in a bit more detail, because I think it is significant. One can also refer to ark-protocol.org for more detail.

TL;DR: send-to-others, automatic vtxo reissuance, lightning receive and mass payouts

For those that don’t know, in Ark users hold off-chain utxos that we call virtual utxos or vtxos. In the original design, which uses CTV, these vtxos are constructed using a tree of transactions where each transaction uses a CTV covenant to get into the next transaction. Much like congestion control.

Generalized, a vtxo can be any construction that has:

  1. a chain or one or more txs with policy ctv OR (pk(S) AND after(T)) (S being the Ark server’s key and T being the vtxo’s expiry time). Each ctv comitting to the next tx in the chain

  2. followed by a tx with the following policy (pk(A) AND pk(S)) OR (pk(A) AND older(delta)) (A being the key of the vtxo’s owner and delta being a relative timelock which gives the other policy enough time to react (something like 24h or 28h).

Using CTV, this construction can be constructed entirely non-interactively, meaning that knowing the Ark’s parameters S and delta, anyone can issue their own vtxo that expires at time T. This is how “onboarding” works, i.e. entering the Ark.

When we developing clArk (covenant-less Ark), we replace the ctv policy with a MuSig2 pre-signature of S and all the users of the leaves below that node. The intermediate nodes from step 1. above become then pk(S+A+B+C+..) OR (pk(S) AND after(T)). Apart from additional signing and storage overhead, this has one strong limitation:

Co-signed (clArk) vtxos cannot be issued without the presence of the eventual owner, otherwise the construction is not safe. This has led our team (at Second) to only designate Ark rounds to be used for users that have expiring vtxos and need them to be refreshed, in effect “sending them to themselves”. Because senders always have to be present in either Ark construction (they need to sign forfeit txs), if the receivers are also the senders, they are already present during the process.

But being able to issue vtxos for others has several significant benefits:

  • The most obvious is simply being able to send vtxos to others without requiring the receiver to participate in Ark rounds. We currently only support users sending to each other using out-of-round (Arkoor) transactions, which has an additional security assumption. With CTV we could again support sending vtxos to others in rounds.

  • It allows the server to issue vtxos for users. We currently want to do this in two occasions:

    • A good server will want to re-issue expired vtxos automatically. This is obviously not secure, but the server can continuously provide proofs that it hasn’t claimed any expired vtxos and for some smaller-value vtxos, this can be enough security for certain users.
    • Receiving Lightning payments in Ark (without having virtual channels) is not easy. One way it could be done is by having the user notify the server that it is expecting an inbound payment, the server will accept the HTLC as a hodl HTLC and issue an HTLC for the user in a vtxo in the next round. The user can then reveal the preimage which grants him the vtxo fully and allows the server to claim the hodl HTLC. (With various anti-abuse measures in place.) Without CTV the receiver would have to participate in a round in order to receive his HTLC.
  • It would allow non-interactive onboards, but since for an onboard, the onboarder is usually the receiver anyway this seems quite uninteresting, but it equivalently allows any party to non-interactively issue any number of vtxos with a single on-chain output. We think this can be a very powerful tool for exchanges or DCA providers that want to regularly payout amounts to their users. Using CTV, they could independently construct a tree of vtxos (using the Ark’s parameters), fund the root tx and inform all their users. Since these vtxos are no different than vtxos created in Ark rounds, the recipients can use them as if they were any other type of vtxo in this Ark.

At Second we believe these benefits are quite significant for both the user experience more broadly and the viability of participating in Ark rounds a the mobile setting in the first place. We have little data currently to back up this latter claim, but we are working hard to put up our implementation of co-signed Ark to the test on mobile clients.

2 Likes

Good point, but you’re correct I’ve basically stopped thinking about these kinds of details because we have so many upgrade hooks. Let’s use them!

The use-cases I’m thinking about that involve the CTV + CSFS combo means we can elide bringing the hash into the witness data, saving ~32WU. State channels, payment channels, statechains, pre-signed HTLC transactions, PTLC transactions. Probably more, but probably most of the CTV + CSFS intersection of functionality.

The tradeoff here is 1WU(?) in the tapscript CTV case, which would apply to things like Ark, Timeout Trees, settlement path in ln-symmetry(et al.), anywhere where authorization isn’t required, just precommitment. I don’t find this a compelling savings if we have to choose just one.

And if VERIFY behavior is still desired, you could still softfork a NOP, just in tapscript circumstances.

I’m much more interested in CTV + CSFS than CTV alone, so there lies some design bias.

I’m thinking about it from another direction: Designing BIP119 from scratch, how should we build it? Legacy script (short-hand for non-segwit, not NOPs) is a dumpster fire, and it shouldn’t be touched unless necessary.

If it saves no vbytes, makes no operations safer, doesn’t make the changes easier to understand for reviewers, why would we build it this way?

I think bare CTV in any form is one of the least motivated proposals I’ve seen in a long while, but if it’s going to happen, I want it to happen in the way that’s least damaging in terms of maintenance of Bitcoin script. I believe my proposal is one way to do it without changing any known capabilities from the original.

Okay so yes to be precise, it was more on the theoretical / primitive design space technical options, in the sense that I don’t think we have seen primitives breakthrough since OP_TLUV, OP_EVICT, MATT or PT2R merkle tree editors since few years ago. The only recent breakthrough have been constructions like BitVM or ColliderScript, to attempt to achieve the same for some use-cases, without consensus changes.

Though yes, getting industry’s opinions on how they would see vaults in practice, and not only on whiteboard is a notable move forward, I do not disagree. IMHO, the more interesting question, I was laying is if HW / secure enclave vendors would be ready to have CTV-like support on the enclave-side, otherwise it’s “blind signing” for the key ceremonies (cf. “Blind Signing Considered Harmful”).

In my perspective, the only security property improvement, that one can argue on is immutability, that once the coins are deposited on-chain beyond reorgs, there is no more reliance on correct and secure key-deletion for the intermediary unvaulting transactions.

From browsing the Nunchuk, McElrath and Wizardsardine viewpoints, if I’m understanding correctly they’re saying either (a) vault it’s hard because reactive model (so chain monitoring or watchtower) or (b) vault it’s hard because duplicate set of keys. To have researched in the past extensively second-layers threats models (cf. L2 Transaction-Relay Workshops), vault security model is not more complex than Lightning, and while LN is plagued by security vulnerabilities and limitations, courageous wallet vendors have succeeded to integrate it on mobile and desktop wallets. Of course, key ceremonies is a hard thing for vaults, i.e dealing well the transition from cold storage, though it’s that harder than managing seeds transfers on multiple platforms for LN, and inadvertently broadcasting an already revoked state ? I don’t think so…

Contrary to LN, vaults are not stateful, in the sense of off-chain update with a counterparty. This is already far less complexity, imo.

Fwiw, if we wish to have LN-Symmetry / Eltoo, we should just do ANYPREVOUT. I think OP_CSFS enhances the range of TxWithhold contract, as you can pass now a signature for a tx, you’re not a counterparty to (i.e not a pubkey in a 2-of-2 P2WSH), which can be concerned for already deployed L2s like Lightning.

I do not disagree that we should avoid to go for legacy script, avoids making the DoS surface worst (e.g worst-block case). I’m re-reading BIP119 though there is no footnote to explain why OP_NOP4 vs another upgrade path (e.g tapleaf version or OP_SUCCESS).

Doing an OP_PUSH <template> instead of a OP_CHECK <template>, that means now you have a comparison on the Script stack to be done between <pushed_template> and <reference_template>. I do no think something in terms of <template> bad malleability it’s an issue (hmm…), however it’s more memory allocated among the different script ops on the stack by each full-nodes. Not sure it’s a concern.

I believe for the very understandable reason that it was prior to the taproot BIP being written.

Also bare CTV seems pretty useful and economic for certain cases. Has no “address” to send to by accident from wallets which could result in burned funds.

I believe the original idea was indeed developed before publication of the taproot and tapscript BIPs, but drafts of those BIPs were published about two weeks before OP_COSHV, the predecessor for CTV. Versions of both the COSHV and STB BIPs linked to the draft Tapscript BIP and depended on it making OP_RESERVED1 (0x89) opcode into an OP_SUCCESSx in tapscript.

2 Likes

I suppose this alternative is more likely: If you’re not considering composibility with other future opcodes, there’s not much to be done with a “push to stack” variant except equality checks.

1 Like

I always thought the original author of BIP119 (@JeremyRubin) had or did a version on top of Taproot. But didn’t read the BIP recently.

Anyway, try a quick-didn’t-check-it-compiles-because-im-too-lazy version of OP_CHECKTEMPLATEVERIFY on top of bitcoin-inquisition 28.x (cf. the full branch).

case OP_CTV:
{
    if (flags & SCRIPT_VERIFY_DISCOURAGE_CHECK_TEMPLATE_VERIFY_HASH) {
        return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_CTV);
    }

    // if flags not enabled; treat as a NOP4
    if (!(flags & SCRIPT_VERIFY_OP_CTV)) {
        break;
    }

    if (stack.size() < 1) {
        return set_error(serror, SCRIPT_ERR_INVALID_STACK_OPERATION);
    }

    // If the argument was not 32 bytes, treat as OP_NOP4:
    switch (stack.back().size()) {
        case 32:
        {
            const Span<const unsigned char> hash{stack.back()};
            if (!checker.CheckDefaultCheckTemplateVerifyHash(hash)) {
                return set_error(serror, SCRIPT_ERR_TEMPLATE_MISMATCH);
            }
            break;
        }
        default:
            // future upgrade can add semantics for this opcode with different length args
            // so discourage use when applicable
            if (flags & SCRIPT_VERIFY_DISCOURAGE_UPGRADABLE_CHECK_TEMPLATE_VERIFY_HASH) {
                return set_error(serror, SCRIPT_ERR_DISCOURAGE_UPGRADABLE_TEMPLATE);
            }
    }
}
break;

I think other upgradable paths could be tested for CTV, e.g upgradable tapscript version, to something that is not 0xC0 as a leaf head byte.

This point deserves better discussions, as I think it’s the opposite. Due to the immutability of any chain of transactions generated with CTV (i.e with a sig-based escape path), the whole chain of transaction should be verified by any recipient (e.g verify that intermediary tx in a congestion control tree is not nLocktime=<year_2109> i.e practically freezing your funds). It’s more likely that good practice of using CTV should come with enhanced “address” carrying enough info to any recipient to verify the semantic of the chain of transaction, which is in itself a use-case specific thing.

So if you can do equality checks on a template and one can pre-compute the result of equality checks on an outpoint (txid:output_index) and pre-commit them in a redeemScript, it’s already an interesting primitive to do adversarial tx-withholding. However, I don’t think this is still a non-collaborative utxo oracle, as the templated tx would have been to be built with a valid witness for the probed utxo. So somehow there is an opt-in of the owner of the probed utxo for the spend to be integated in the CTV template. But I’m not sure.

FYI: after discussion a while back, I hacked together very small loc changes that:

  1. Makes bare CTV a first class citizen
  2. Converts CTV into a push-onto-stack taproot-only opcode

It’s a very small set of changes, does not change the hash digest formula for BIP119 (nor invalidate the unit tests), cheaper to use in conjunction with CSFS for any floating signature scheme.

edit: I also tried another alternative where bare CTV follows exactly he format today, but only softforks for that OP_NOP template, to reduce the scope of the softforking behavior where we only have to test the single bare CTV case vs additional bare scripts. It was messy and not worth it.

1 Like

I’m +1 for taproot-only CTV as an op_success for future composability. No opinion on an OP_PUSHTEMPLATE + OP_EQUALVERIFY or OP_CHECKTEMPLATEVERIFY approach.

By the way, I believe there might be some DoS surface that has been not considered in the BIP: