AJ, Interesting stuff! Just a couple thoughts on these proposed opcodes, at least one we discussed elsewhere:
1) OP_FORWARD_SELF is a JET of OP_FLU in the revaulting common case. Maybe obvious but I missed this initially and thought it was useful to be pointed out. 2) Using these extended primitives, you can do rate-limiting of the two step unvaulting, or just a single step vault by committing to the partial values. For the single stage case it's something like: $recovery = Same As Before $withdrawal = <deposit-delay> OP_CSV OP_DROP <pubkey> OP_CHECKSIG OP_DUP <V> OP_LESSTHANOREQUAL OP_VERIFY OP_FORWARD_PARTIAL OP_FORWARD_TARGET OP_FORWARD_SELF $withdrawal is spent by: <self-idx> <target-idx> <spk> <0<=v<=V> <signature> where "V" is the max allowed withdrawal value, and "deposit-delay" the required gap in withdrawals Due to the OP_LEQ, you are bound to ~21 BTC in value for this operation, but for larger vaults it's pretty trivial to add larder fixed denominations to "peel out" value until you get to your final ~21 BTC. This rate-limiting(in either the two-stage or one-stage scheme) can limit the risk of theft during a watchtower outage to a constant value per utxo per time period of watchtower failure. As we've seen in the past with LN infrastructure, software risks are often correlated, so it's a good idea to build in belt and suspenders where we can or at least have them available when possible. Cheers, Greg On Tue, Mar 7, 2023 at 7:45 AM Anthony Towns <a...@erisian.com.au> wrote: > 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 >
_______________________________________________ bitcoin-dev mailing list bitcoin-dev@lists.linuxfoundation.org https://lists.linuxfoundation.org/mailman/listinfo/bitcoin-dev