Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Explore covenants UX #607

Open
GBKS opened this issue Jan 9, 2024 · 26 comments
Open

Explore covenants UX #607

GBKS opened this issue Jan 9, 2024 · 26 comments
Assignees

Comments

@GBKS
Copy link
Contributor

GBKS commented Jan 9, 2024

There's interest in researching design solutions around covenants.

Briefly, if I understand correctly, covenants are changes to bitcoin that would allow for restricting how bitcoin can be spent. So you could define that certain bitcoin can only be spent to a specific list of other addresses. Right now, the only restriction is who can spend them (the person with the private key). There are several technical proposals to achieve this with different properties, with no clear consensus on which one the hive mind wants to move forward with. This feature is surprisingly powerful and can, for example, enable new scaling methods. Maybe we can help this process by evaluating this from a design perspective. Here's a good podcast with an overview.

Origin of this initiative is a post by Alexander Leishman, which was then picked up by Dan in this PR, which led to a discussion in a Bitcoin Design Guide Jam Session.

As far as a process, we could go for the classic 4Ds - discover, define, design, deliver.

  1. Discover: learn about covenants (goals, proposals, use cases, risks, history, etc)
  2. Define: broadly explore design concepts and approaches
  3. Design: narrow in on the most promising solutions and work through the details
  4. Deliver: present our findings and discuss broadly

Step 1 could simply be to set up a channel in Discord for conversation, start gathering resources in a Google Doc, and set up a Learning bitcoin & design call to discuss as a group.

How does that sound? Should we go for it?

I set up a first call here.

@yashrajd
Copy link

Attaching the google doc here . Please feel free to add guidance, inputs, questions or comments.

@sbddesign
Copy link
Collaborator

I'll just deposit this here (and in the google doc)

@GBKS
Copy link
Contributor Author

GBKS commented Jan 16, 2024

@sbddesign thanks for sharing. If I understand correctly, the exchange can do a very efficient batch transaction to many at once. But this is not a simple transaction where the funds arrive in the destination addresses right away, as we are used to. In this one, the recipient has to claim the funds via another transactions. This makes it more cost-efficient for the exchange, and allows for the recipient to claim whenever is good for them. Downside is that there's an extra transaction in the network, and the recipient now also has to pay a fee, and there needs to be extra communication by the exchange to let the recipient know how to claim the funds. Did I get that right?

@sbddesign
Copy link
Collaborator

Yeah, that's basically it. I think part of the challenge was similar to lightning state, e.g. the backup doesn't become a simple "store seed phrase", it's seed + lightning channel state or static channel backup. There was some additional data that need to be conveyed. But this is from an older implementation of CTV, and may not be applicable to all types of covenants or use-cases. Shared for funsies.

@owenkemeys
Copy link

I've written two threads trying to put covenants (specifically CTV) into more visual language to help people reason through how it works and onto the possibilities.
This thread covers the basic functionality.
The next thread extends onto 3 concrete use cases, which themselves are pretty much the core bulding blocks of most constructs.
I aim to join the call, and if you like I can give a high level walk through of these - with some analogies to concepts we already have to deal with in UX design today.

@moneyball
Copy link
Contributor

Let's discuss the congestion control use case. Let's say an exchange is servicing 100 customer's withdrawals, or, an employer is paying 100 employees their paycheck. These 100 payments can all be packaged up into a single transaction. Great! However, if I understand correctly, to create that transaction requires interactivity from all 100 customers and the exchange (or, 100 employees and their employer). Each customer must create a signature and share it with all other customers, because for any one customer to later claim their funds, they'll need a distribution transaction and 99 other signatures. This sounds like a worse UX to me than trying to deliver a good coinjoin UX. Can this be explored to see how good (or bad) it would be, so we can evaluate whether we think this would be compelling and adopted?

@owenkemeys
Copy link

owenkemeys commented Jan 20, 2024

That's not correct. Let's walk through the use case. I am going to interchangeably use the term "batched" and "zipped" which I think helps people understand the compress/decompress type logic we can do.

In a world where covenants are activated (specifically CTV for simplicity right now), exchanges could offer batched withdrawals, yes. For a batch withdrawal, they need to build:

  1. The second stage transaction(s) paying to the individuals,
  2. The initial covenant transaction, which appears on chain as a payment to a single address. It can only be spent by broadcasting that second stage transaction(s), which spends its output to all the final individuals. Remember, spending conditions are what generate the address itself.

So in practice, all the exchange needs from a user is: a withdrawal address (just as today), and their consent to being included in covenant batched transactions. Because if you haven't consented, then you don't know that batch transaction included any coins for you. It just looks like any other deposit address on chain. "What do you mean you've sent the payment? My wallet isn't showing anything! That's not my address!"

The receiving case then is fairly straightforward UX on the exchange side: a text box for an address as today, and a checkbox approving this to be included in a CTV batch, likely with a fee discount for doing so - because the exchange is only having to mine a very small transaction spending to 1 output, not 100 outputs.

On the recipient wallet side, under the hood there will need to be a data blob provided by the exchange that includes all the spending conditions of their batch withdrawal, with which you can satisfy yourself that your coins are included in there.

The wallet will also monitor the chain to see if anyone else breaks apart the batch first (remember, the only places the coins can go is to the pre-designated recipients and amounts - think of it like unzipping a file). If someone else does it first, then your coins got moved on to your pre-designated address, and the high time preference user paid the fee for everyone.

It's also worth noting that it doesn't have to be "either everyone exits or nobody does" which makes it very expensive for the 1 person who wants to exit, since he has to pay for a transaction with 100 outputs. Wise covenant users will build multiple optional unzip paths to optimise fees for their customers, such as "exit by yourself, keep the rest batched": meaning you only pay for 2 outputs. Or "exit yourself, split the remainder into 2 smaller batches", which is still only 3 outputs, but helps chunk up the big group. These configs will likely be platform policies.

So to circle back onto UX:
Before an "unzip", for the user we can simply include those coins in their simple balance summary, because nobody else can spend them anyway. But whilst still zipped, we have to communicate that those coins are a little more expensive to spend than coins just sitting in private key-controlled addresses, since you have to broadcast an unzip transaction first before your onward spend. Some ideas:

  • Keep them in a segregated area of the UI? "Reserves" or something.
  • Live estimates of fees alongside each coin, showing you "cost to get this coin to an onward destination" (which is surely all the user cares about)
  • Just a little counter alongside to abstract the cost, such as how many layers deep it is, or how many other outputs there are?

@moneyball
Copy link
Contributor

Am I misunderstanding this Optech description or is it wrong?

"Alice wants to pay a set of ten people but transaction fees are currently high so that she doesn’t want to send ten separate transactions or even use payment batching to send one transaction that includes an output for each of the receivers. Instead, she wants to trustlessly commit to paying them in the future without having to pay onchain fees for ten outputs now. So Alice asks each of the receivers for one of their public keys and creates an unsigned and unbroadcast setup transaction that pays those keys using a 10-of-10 multisig script. Then she creates a child transaction that spends from the multisig output to the 10 outputs she originally wanted to create. We’ll call this child transaction the distribution transaction. She asks all the receivers to sign this distribution transaction and she ensures each person receives everyone else’s signatures, then she signs and broadcasts the setup transaction."

https://bitcoinops.org/en/newsletters/2019/05/29/#proposed-transaction-output-commitments

@owenkemeys
Copy link

That's a very odd example that may be confusing you. I think that's demonstrating how you could achieve a batching effect without CTV.
Transaction A, the payment into the 10of10 multisig, is a 1-output TX, making it small and thus cheap.
Transaction B, the presigned transaction with all 10 signatures, distributed to all recipients BEFORE Transaction A is broadcast for mining, is their assurance they can all get their money out again.

With CTV you don't need all the signatures. You would get an address from each person (and their consent to being put in a tree), and then build a template.
Transaction A pays into the CTV address.
Transaction B spends the CTV address, only possible if it matches the Template that defines the CTV address. This Template happens to be "pay to these 10 addresses".

Again, the congestion control models are not that compelling on their own especially given the modern times of consistently high fees, but they are the most basic tree/pool structure, and we can do many more interesting things with those. Imagine for example that each of those 10 output addresses is a lightning channel - they can all be used right away as soon as Transaction A is mined, since they are committed now and must happen at some point. But the chain has only seen a single output UTXO.

@owenkemeys
Copy link

owenkemeys commented Jan 21, 2024

I built a Figma UX demo of Phoenix wallet with CTV powered Cold Channels, you can check it out here:
https://www.figma.com/proto/o4sxu0rEIR1zmletrxtXRn/Phoenix-CTV?type=design&node-id=2-60&t=A12KsqXSncbdgstk-1&scaling=contain&page-id=0%3A1&starting-point-node-id=2%3A60&mode=design
This is a working document so bear with me if anything is broken.

@moneyball
Copy link
Contributor

That Optech summary is specifically about CTV, formerly called COSHV.

@owenkemeys
Copy link

Perhaps something got upgraded in the move to calling it CTV then, all I can tell you is the structure described in your quote does not require, take advantage of, or even benefit from CTV in any way.

Jeremy Rubin's own page has some of the best writing about CTV but it can be difficult to get your head around.
https://utxos.org/uses/

And he wrote a lot of longer form pieces about various use cases in his 2021 Advent Calendar series:
https://rubin.io/advent21/

I wouldn't put much stock in any writing older than 2021.

@ajtowns
Copy link

ajtowns commented Jan 21, 2024

That Optech summary is specifically about CTV, formerly called COSHV.

That section is preceded by the comment "Before we look at the new opcode itself, let’s take a moment to look at how you might accomplish something similar using current Bitcoin transaction features."

The CTV behaviour is described below:

Comparing this to our example above, Alice would again ask each of the participants for a public key (such as a Taproot address2). Similar to before, she’d create 10 outputs which each paid one of the receivers—but she wouldn’t need to form this into a specific distribution transaction. Instead, she’d just hash the ten outputs together and use the resultant digest to create a tapleaf script containing COSHV. That would be the only tapleaf in this Taproot commitment. Alice could also use the participants’ public keys to form the taproot internal key to allow them to cooperatively spend the money without revealing the Taproot script path.

The setup is: "I want to pay pubkeys B1, B2, .., B10. I hash those addresses together along with the amounts and misc other details to get a ctv hash X=Hash(B1,..B10), and I pay to " CTV". Sometime later, when one of the recipients wants to actually spend the funds, they post two transactions: the first one, CTVTX, spending the " CTV" utxo to 10 new outputs, spending the appropriate funds to B1,..,B10, and a second one spending their output, B3, to wherever they want. Depending on how CTVTX is constructed, they may need a third, initial tx, that creates an output that can be completely consumed by CTVTX in order to pay its fees; though maybe package relay or ephemeral anchors could also be a solution there.

There's two sets of interactivity here: getting the B1,..,B10 addresses in the first place (easy), and telling each of the owners of those addresses the details of X so that they can construct CTVTX when they want.

@GBKS
Copy link
Contributor Author

GBKS commented Jan 23, 2024

I built a Figma UX demo of Phoenix wallet with CTV powered Cold Channels

Awesome work there. It will be really tricky to weave this smoothly into the experience without putting explainers everywhere. For cold channels, I like the idea of a user just stating their desired spending balance, and the wallet helping them achieve it. If it's low, maybe it recommends a cold channel. If it's high, maybe it recommends moving funds out. Personally, there is an amount range of cash that I comfortably carry in my wallet. Too low, I go to the ATM (too high never really happens as I just use cash for spending). Just a tough thing to have users make these choices every time they go to the receive tab. I'll have to mock this up to validate the idea.

As for the payment tree leaf import, what is the information a user would receive? Is there a standardized file format the wallet could recognize? Would be ideal if this could be a BIP21 URL, or some other format the wallet could auto-detect. The you might just need URL and clipboard detection and maybe file import. Basic idea is that you can just throw whatever you have at the wallet and it will make sense of it and present appropriate info and options (even if you don't know any of the terms).

@GBKS
Copy link
Contributor Author

GBKS commented Jan 23, 2024

I scheduled another call for February 2 at 14:00 UTC. Let me know if this works, and preference for which use cases (or other aspects of covenants) you'd like to discuss.

@owenkemeys
Copy link

Awesome work there. It will be really tricky to weave this smoothly into the experience without putting explainers everywhere. For cold channels, I like the idea of a user just stating their desired spending balance, and the wallet helping them achieve it. If it's low, maybe it recommends a cold channel. If it's high, maybe it recommends moving funds out.

Interesting perspective, I like it generally. I don't think we really need many explainers in the long run, so much can be hidden by making inferences, but a UX demo that just has "send/receive" isn't a very good teaching tool!
I don't think you actually want to move funds out, since they will still be hot, just held someplace else - not much of a win. Unless you make an on-chain transaction to splice them out, but I think we ought to be already designing for really minimal chain usage.
With the cold channels, in the very long run I picture we might even have tiered degrees of cold. In the demo so far, the private key for the dormant cold channel is held by your Phoenix instance - the funds are safe from the channel partner, but if your phone is stolen and the thief gets into it, they could activate and spend them. But hypothetically you could have the channel key be generated and held by a hardware wallet, meaning Phoenix can't spend it until that key is revealed from your hardware wallet.

So it is better to think of these cold channels as pre-connected to Lightning, but not turned on yet; and the turning-on is just through sharing secrets - not by making on-chain transactions.

As for the payment tree leaf import, what is the information a user would receive? Is there a standardized file format the wallet could recognize?

There isn't a format yet (that I know of) but in making this demo it became obvious to me that there ultimately will be. The wallet just needs to know which UTXO is the tree root and all the necessary info to navigate and exit it, which fundamentally speaking is just a collection of unlock scripts and PSBTs relevant to you - much like proving some data is embedded in a Merkle tree. A standard format for copy-paste makes complete sense and should be pretty trivial, just needs a spec.

@moneyball
Copy link
Contributor

@ajtowns and @owenkemeys thank you for the correction and pointing out my (dumb) comprehension! Appreciate it.

@GBKS
Copy link
Contributor Author

GBKS commented Feb 2, 2024

We just had the second call (#623). Continuing our discussion from our first call, we dove into how covenants might play out in scenarios around chaining transactions, payment pools, and cold lightning channels. We made good progress, but also still have questions. A good next step might be to follow in Owen's footsteps and mock up UIs for more user flows to validate our understanding and get hands-on. Recordings are up on BitcoinTV and YouTube.

@GBKS
Copy link
Contributor Author

GBKS commented Feb 8, 2024

Something I am curious about are the failure/risk/attack scenarios of these constructs.

Broken claim chains
If I understand correctly, then we are essentially creating claims and only the claim creator and the claim recipient have the required information to execute it. If it's possible to chain several claims in a row, and some people earlier in the chain forget their secrets, are the bitcoin for the rest of the chain then stuck forever? Or we do always bake in a time-locked exit route or something like that?

Secret spending rules
Can the claim creator bake in another rule for spending bitcoin they promised to me in a different way?

I am sure this has been considered, but I could not think of obvious answers. What are some other problematic scenarios that might occur?

@owenkemeys
Copy link

owenkemeys commented Feb 8, 2024

If it's possible to chain several claims in a row, and some people earlier in the chain forget their secrets, are the bitcoin for the rest of the chain then stuck forever? Or we do always bake in a time-locked exit route or something like that?

Before considering a payment to be received, a recipient should satisfy themselves they have every piece of information necessary to update the blockchain such that the sats are withdrawn to their sole custody, without needing anyone's assistance. So if you were unwise enough to not do this and rely on others earlier than you, and those people lost their ability to broadcast their updates, then yes you're screwed - so don't consider yourself paid if you're dependent on anyone else.

Can the claim creator bake in another rule for spending bitcoin they promised to me in a different way?

You should not consider yourself paid until you've seen everything required to satisfy yourself that this is not possible. If there are multiple spend paths, and the sender only reveals one to you, then you must presume he's trying to trick you. The address is a hash of the spend script, so if what you're given does not hash to the address, you know you're missing some info - AND (unless my technical understanding is mistaken) you would not be able to spend if you tried, since you don't have the entire script.

@GBKS
Copy link
Contributor Author

GBKS commented Feb 9, 2024

That could make for some awkward conversations:

  • Alice: I paid you, here's the transaction and the information you need to claim it.
  • Robert: Oh, so I guess I now have to pay the fee, thanks. And looks like this is dependent on another claim from Charles. I don't like this.
  • Alice: Well, I can't undo it. Let me talk to Charles.
  • ... time passes ...
  • Alice: Sorry, I can't get a hold of Charles, you're just going to have to wait.
  • Robert: No, pay me now properly. And pay me extra for fees, so once Charles claim comes through and I can claim your first payment, that I can refund it to you.

Not sure if I'm getting it right, but seems like the idea of scaling via covenants highly depends on this nesting, which then might cause these other dependency issues. Avoid those probably means that there is a limit to how much scale can be achieved. And scale in this case is more like batching/bundling.

AND (unless my technical understanding is mistaken) you would not be able to spend if you tried, since you don't have the entire script.

That would make sense. Also means I have to reveal all transactions baked in to each party, potentially giving them some insight into what else I am up to.

@GBKS GBKS changed the title Investigate covenants Explore covenants UX Feb 9, 2024
@owenkemeys
Copy link

Not sure if I'm getting it right, but seems like the idea of scaling via covenants highly depends on this nesting, which then might cause these other dependency issues. Avoid those probably means that there is a limit to how much scale can be achieved. And scale in this case is more like batching/bundling.

What you've described is a very crude and clunky usage, yes!
The effective way to use them for scaling is payment pools, which functionally speaking looks like a custodian that everyone has an irrevocable, unilateral withdraw claim from, and the custodian is unable to steal. In practice the pool operator may be an individual or business, or may be the collective of pool members collaborating off-chain, say via Nostr, to synchronise their transacting and minimise their fees. Also, remember that this is all much more effective if the payments are lightning channel opens/closes, rather than one-time payments.
It's definitely hard to think about. John Law's Timeout Trees proposal is a technically well fleshed out pool concept if you can make the time to read it, it should help you understand the limits and tradeoffs.
https://bitcoinmagazine.com/technical/timeout-trees-a-solution-to-scaling-lightning-network-lsps

@moneyball
Copy link
Contributor

One drawback to note on the Timeout Trees is that it substantially raises the bar on what can be considered non-custodial. With a regular LN channel, you need to have a minimum of the on-chain fees in order to have any non-custodial value. With TT, you have to pay O(log N) on-chain fees (because it requires O(log N) transactions to go on-chain), where N is the # of users in a TT. So if on-chain fees are $20, a TT user would need to pay something like $200 on-chain fees if a large tree.

@GBKS
Copy link
Contributor Author

GBKS commented Feb 19, 2024

Timeout trees do look pretty promising. I just cleaned up our notes document (link in Discord), it now has a better organized list of use cases. Which ones do we think have the highest potential impact that would also benefit from design explorations?

@moneyball
Copy link
Contributor

@GBKS curious to hear takes on the fact Timeout Trees have something like a 10x higher threshold than basic LN for non-custodial payments. eg if only payments over $200 are actually trustless, why not just use ecash for sub-$200 payments?

@GBKS
Copy link
Contributor Author

GBKS commented Feb 19, 2024

This is such a tricky conversation where timelines and possible future projections are relevant. What if onchain fees go to $500 to several thousand, as Pete discusses in this podcast? If we're building for that scenario, does it even make sense to consider much of the other onchain tech that is being worked on for consumer products? Or do we need to have bridge solutions that facilitate that later stage, but which we know will become impractical/uneconomic at some point?

A timeout tree design exploration could consider a range. Looking at the 200x difference in global incomes, this lack of affordability might even be true today and we don't have to wait for much higher fees. We could design for some scenarios:

  1. High-income individuals which can participate in trees via their LSPs for a good amount of payment activity
  2. Mid-income individuals where it highly depends on payment use case (personal channels, tree channels, Ecash)
  3. Low-income individuals that are Ecash only (we instead design Ecash admin UX for trees since they will be the primary users, creating those with other LSPs)

Purely from a practical design exploration perspective, it is sometimes helpful to mock up the simplest scenario and work step-by-step towards the more complex ones.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: In Progress
Status: In Progress 🏗️
Development

No branches or pull requests

7 participants
@ajtowns @moneyball @GBKS @sbddesign @owenkemeys @yashrajd and others