What is Nostr?
Anthony Towns [ARCHIVE] /
npub17rl…9l2h
2023-06-07 23:20:00
in reply to nevent1q…yaks

Anthony Towns [ARCHIVE] on Nostr: 📅 Original date posted:2023-03-07 🗒️ Summary of this message: A proposal to ...

📅 Original date posted:2023-03-07
🗒️ Summary of this message: A proposal to introduce the concept of "forwarding" input amounts to specified outputs with various restrictions on output scripts using new opcodes.
📝 Original message:On Mon, Mar 06, 2023 at 10:25:38AM -0500, James O'Beirne via bitcoin-dev wrote:
> What Greg is proposing above is to in essence TLUV-ify this proposal.

FWIW, the way I'm thinking about this is that the "OP_VAULT" concept is
introducing two things:

a) the concept of "forwarding" the input amount to specified
outputs in a way that elegantly allows merging/splitting

b) various restrictions on the form of the output scripts

These concepts go together well, because restricting an output script is
only an interesting thing to do if you're moving value from this input
into it. And then it's just a matter of figuring out a nice way to pick
opcodes that combine those two concepts in interesting ways.

This is different from TLUV, in that TLUV only did part (b), and
assumed you'd do part (a) manually somehow, eg via "OP_IN_OUT_AMOUNT"
and arithmetic opcodes. The advantage of this new approach over that
one is that it makes it really easy to get the logic right (I often
forgot to include the IN_OUT_AMOUNT checks at all, for instance), and
also makes spending multiple inputs to a single output really simple,
something that would otherwise require kind-of gnarly logic.

I think there are perhaps four opcodes that are interesting in this class:

idx sPK OP_FORWARD_TARGET
-- sends the value to a particular output (given by idx), and
requires that output have a particular scriptPubKey (given
by sPK).

idx [...] n script OP_FORWARD_LEAF_UPDATE
-- sends the value to a particular output (given by idx), and
requires that output to have almost the same scriptPubKey as this
input, _except_ that the current leaf is replaced by "script",
with that script prefixed by "n" pushes (of values given by [...])

idx OP_FORWARD_SELF
-- sends the value to a particular output (given by idx), and
requires that output to have the same scriptPubKey as this input

amt OP_FORWARD_PARTIAL
-- modifies the next OP_FORWARD_* opcode to only affect "amt",
rather than the entire balance. opcodes after that affect the
remaining balance, after "amt" has been subtracted. if "amt" is
0, the next OP_FORWARD_* becomes a no-op.

Then each time you see OP_FORWARD_TARGET or OP_FORWARD_LEAF_UPDATE, you
accumulate the value that's expected to be forwarded to the output by
each input, and verify that the amount for that output is greater-or-equal
to the accumulated value.

> ## Required opcodes
> - OP_VAULT: spent to trigger withdrawal
> - OP_VAULT_RECOVER: spent to recover

Naming here is OP_VAULT ~= OP_FORWARD_LEAF_UPDATE; OP_VAULT_RECOVER ~=
OP_FORWARD_TARGET.

> For each vault, vaulted coins are spent to an output with the taproot
> structure
>
> taproot(internal_key, {$recovery_leaf, $trigger_leaf, ...})
>
> where
>
> $trigger_leaf =
> <trigger> <auth> <script> <spend-delay> OP_VAULT

With the opcodes above, the trigger_leaf (for spending some of the
vaulted funds via your hot wallet) script might look like:

OP_FORWARD_PARTIAL OP_FORWARD_SELF
1 "288 OP_CSV OP_DROP OP_CTV" OP_FORWARD_LEAF_UPDATE
key CHECKSIG

So if you have 2.0 BTC in a vault utxo, you might spend 0.4 BTC by
supplying the witness data:

160000000 (1.6BTC in sats)
0 (output 0 puts 1.6BTC goes back into the vault)
<ctvhash> (the outputs where you want the remaining 0.4 BTC to end up)
1 (the output idx that will be spend via the CTV path once the CSV
delay is done)
<sig> (a signature of this transaction via the hot wallet "key")

That is, the script becomes:

160000000 FORWARD_PARTIAL
0 FORWARD_SELF
1 <ctvhash> 1 "288 CSV DROP CTV" FORWARD_LEAF_UPDATE
sig key CHECKSIG

Output 1 would then have a tapscript of "<ctvhash> 288 OP_CSV OP_DROP
OP_CTV", satisfied with an empty witness stack (along with the recovery
path, etc).

Output 0 is just 1.6BTC back in your vault, and immediately available
for use.

Other inputs/outputs (for fees etc) would still be committed to by <sig>,
so nothing here is malleable. The script here is about 45 bytes (compared
to 34 for a simple "key CHECKSIG") and the witness data is about 105 bytes
(compared to 65 bytes for just a signature), which seems pretty nice.

> ... =
> other (optional) leaves in the taptree

This would allow you to have multiple hot wallets (if any of them are
compromised you can still use the recovery path to avoid loss of funds;
but if some hot wallet becomes temporarily *inaccessible* you can still
easily spend the funds via one of the alternative hot wallets), or,
if you have multiple watchtowers validating your spends and recovering
funds to your cold wallet on a violation, you could have multiple recovery
paths to provide some auditability for who triggered the recovery.

> Happens via script-path spend to $expr_withdraw, i.e. a timelocked
> OP_CTV.

Note that if you calculated the OP_CTV incorrectly (eg, you don't set a
correct nSequence timelock, so that any tx that passes OP_CTV won't pass
the OP_CSV check, and vice-versa) then this spend path becomes invalid,
and the funds can only be reclaimed via some other path (key path spend,
recovery tapscript, potentially an alternative hotwallet script path).

OP_FORWARD_LEAF_UPDATE is equivalent to a very specific form of TLUV,
namely "FALSE <h> 2 TLUV", where "<h>" is calculated by building the
script, prefixing the pushes, then doing the Hash_TapLeaf calculation.

Not being able to tweak the internal public key ("FALSE" rather than
"<x>") means this can't be used to build a coinpool with unilateral
exit -- you can't remove your key from the key path, which screws over
everyone who's still in the coinpool.

On the other hand, not tweaking the internal public key avoids introducing
all the x-only pubkey complications, and keeps it relatively simple,
which is nice, and keeping things simple and targeted now means there's
still plenty of OP_SUCCESS opcodes available later for something more
general, if that turns out to be desirable.

Cheers,
aj
Author Public Key
npub17rld56k4365lfphyd8u8kwuejey5xcazdxptserx03wc4jc9g24stx9l2h