LearnBitcoin

Bitcoin Dev Archive 2018

Graftroot: Private and efficient surrogate scripts under the taproot assumption

9 messages, 5 Feb to 30 Jun 2018. Started by Gregory Maxwell.

Gregory Maxwell (3), Ryan Grant (2), Jeremy Rubin (2), Daniel Edgecumbe, Sjors Provoost

Permalink Original thread on gnusha.org

Gregory Maxwell permalink original

In my post on taproot I showed a simple commitment scheme for scripts that is very efficient that there exists some collection of pubkeys (like an M-of-N or even N-of-N) whos authorization is an acceptable alternative to whatever other conditions we might want to impose on a coin. If this holds then when spends happen via the plain signature path the existence of the alternative is never revealed, providing privacy with improved efficiency compared to not being private at all.

Taproot suffers from a limitation that it only natively provides for one alternative. Trees or cascades of taproots can be done, but they have less privacy and efficiency than just a single level. E.g. a tree commitment has overhead that grows with the log of the number of alternatives.

However, under the taproot assumption-- that there exists some monotone function on plain public keys and nothing else that is sufficient to authorize a transaction-- we can do even better.

With graftroot, the participants establish a threshold key, optionally with a taproot alternative, just as they do with taproot. At any time, they can delegate their ability to sign to a surrogate script by signing that script (and just the script) with their taproot key, and sharing that delegation with whomever they choose. Later, when it comes time to spend the coin, if the signers aren't available and the script must be used, the redeeming party does whatever is required to satisfy the script (e.g. provides their own signature and a timelock, or whatnot) and presents that information along with the signer's signature of the script.

The result is that instead of allowing only one alternative an unlimited number of alternatives can be provided. All are executed with equal efficiency to a single alternative, and the number of them is hidden without overhead. Alternatives can be provided for existing coins too, without requiring they get moved-- movement is only required to destroy the ability to use alternatives by changing keys.

Allowing this kind of delegation makes sense because the same signers could have just signed the transaction outright. The new script simply stands in for them, if they're not available or cooperating. No special conditions are needed outside of the surrogate script on when the surrogate is allowed, because they can be written inside the surrogate.

We've discussed delegation in script back to at least 2012-- with speculation that enabling it may have been an original motivation behind codeseperator. ... but these design discussions have gotten mired in how to express and connect the levels of delegation. But the case where delegation is accomplished with a simple unconditional signature is an especially simple case, and under the taproot assumption the only case that is ever needed.

A naive implementation of this idea requires a complete signature every time a surrogate is used, which means 64 bytes of data (assuming 128 bit ECC). This is higher overhead than taproot.

However, the non-interactive schnorr aggregation trick[1] can be applied to merge the S values of all graftroots and signatures in a transaction into a single aggregate. With this approach only a single R value for each graftroot need be published, lowering the overhead to ~32 bytes-- the same as taproot. This has a side benefit of binding the published grafts to a particular transaction, which might help avoid some screwups.

In cases where the taproot assumption doesn't hold, taproot can still be used by setting the public key to a NUMS point, which preserves privacy (e.g. you can't distinguish txn where the key could never have been used.) A similar thing can be done for graftroot if the signature is not a proof of knowledge (commits to the public key): you select the signature in a NUMS manner, and then recover the applicable public key. Though this can't be done if the signature is a PoK, and it's probably a pretty good idea to make it a PoK.

The primary limitation of this approach compared to taproot alternatives and trees is that it requires that anyone who wants to make use of a particular surrogate to interact with the participants and store the resulting signature because a single party couldn't compute it again on their own from public data. For trees and taproot alternatives, the alternatives can be setup without any interaction with the participants. The primary advantage is that it scales to any number of alternatives with small constant overhead, can be delegated after the fact, and can still be spent by the participants without overhead.

Summarizing: A coin's authorizing contract is decomposed into a top level OR between a monotone function of pubkeys (such as N of N) and any number of arbitrary surrogate scripts which are acceptable authorizations. A key aggregate (see [2]) is formed, and is used to sign each of the the surrogates. Participants save these signatures. Later, when it comes time to spend the coin, if the pubkey holders are unwilling or unavailable, the spender presents and satisfies the relevant surrogate along with it's signature R-value and non-interactively aggregates the S-value into the transaction's overall aggregate signature. The result is 0-overhead if the signers cooperate, or ~32-byte overhead (plus the script) if they don't. This avoids the log() overhead of tree based schemes, and allows delegation to take place before or after the fact but requires storage. The potential for unexpected surrogate replay if keys are reused in foolish ways also needs to be kept in mind, though it may be somewhat mitigated by aggregation. The existence of unused surrogates is completely hidden.

I believe this general design is simple and powerful enough that it avoids the rathole that earlier delegation discussions have suffered.

[1] https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-May/014272.html And the secure construction at: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2017-May/014308.html

[2] https://eprint.iacr.org/2018/068
Ryan Grant in reply to Gregory Maxwell permalink original

Am I reading correctly that this allows unilateral key rotation (to a previously unknown key), without invalidating the interests of other parties in the existing multisig (or even requiring any on-chain transaction), at the cost of storing the signed delegation?

Gregory Maxwell in reply to Ryan Grant permalink original

On Mon, Feb 5, 2018 at 3:56 PM, Ryan Grant <bitcoin-dev@rgrant•org> wrote:

Quoted text (4 lines)
> Am I reading correctly that this allows unilateral key rotation (to a
> previously unknown key), without invalidating the interests of other
> parties in the existing multisig (or even requiring any on-chain
> transaction), at the cost of storing the signed delegation?

Yes, though I'd avoid the word rotation because as you note it doesn't invalidate the interests of any key, the original setup remains able to sign. You could allow a new key of yours (plus everyone else) to sign, assuming the other parties agree... but the old one could also still sign.

Jeremy Rubin in reply to Gregory Maxwell permalink original

This might be unpopular because of bad re-org behavior, but I believe the utility of this construction can be improved if we introduce functionality that makes a script invalid after a certain time (correct me if I'm wrong, I believe all current timelocks are valid after a certain time and invalid before, this is the inverse).

Then you can exclude old delegates by timing/block height arguments, or even pre-sign delegates for different periods of time (e.g., if this happens in the next 100 blocks require y, before the next 1000 blocks but after the first 100 require z, etc).

Jeremy Rubin in reply to Jeremy Rubin permalink original

I'm also highly interested in the case where you sign a delegate conditional on another delegate being signed, e.g. a bilateral agreement.

In order for this to work nicely you also need internally something like segwit so that you can refer to one side's delegation by a signature-stable identity.

I don't have a suggestion of a nice way to do this at this time, but will stew on it.

Ryan Grant in reply to Jeremy Rubin permalink original

On Fri, Feb 9, 2018 at 7:29 AM, Jeremy <jlrubin@mit•edu> wrote:

Quoted text (2 lines)
> utility of this construction can be improved if we introduce functionality
> that makes a script invalid after a certain time

Tagging this thread with "nExpiryTime". Search archives for more.

Daniel Edgecumbe in reply to Ryan Grant permalink original
Quoted text (1 line)
> However,  the non-interactive schnorr aggregation trick[1] can be

applied to merge the S values of all graftroots and signatures in a transaction into a single aggregate. With this approach only a single R value for each graftroot need be published, lowering the overhead to ~32 bytes-- the same as taproot. This has a side benefit of binding the published grafts to a particular transaction, which might help avoid some screwups.

I don't think that binding grafts to a particular transaction requires this aggregation. It seems to me that you could just sign H(txid, script) rather than H(script). I'm not aware of whether this would break aggregation.

--- Daniel Edgecumbe / esotericnonsense esotericnonsense@esotericnonsense•com https://esotericnonsense.com https://danedgecumbe.com

Gregory Maxwell in reply to Daniel Edgecumbe permalink original

On Thu, Feb 22, 2018 at 7:44 PM, Daniel Edgecumbe via bitcoin-dev <bitcoin-dev@lists•linuxfoundation•org> wrote:

Quoted text (3 lines)
> I don't think that binding grafts to a particular transaction requires this aggregation.
> It seems to me that you could just sign H(txid, script) rather than H(script).
> I'm not aware of whether this would break aggregation.

That would require that you know the txid in advance. Sometimes you do-- and a graftroot sighash flag could handle that... but usually you wouldn't. The case where you already do know it can sort of be covered today without using the graftroot: Sign a transaction spending the multisig coin to the graft. This isn't a strict alternative however, because it's not atomic: you could imagine that txn being announced and then the graft not being spent, while someone would like to spend a different graft. That non-atomiticity could be addressed by making the graft spends an OR of all the other graft spends but that isn't scalable or private. Regardless, still doesn't work if the graft isn't created after the fact.

The aggregation bit has the property of working just in time, even on grafts created in advance.

Sjors Provoost in reply to Ryan Grant permalink original
Quoted text (7 lines)
> Op 22 feb. 2018, om 13:19 heeft Ryan Grant via bitcoin-dev <bitcoin-dev@lists•linuxfoundation•org> het volgende geschreven:
>
> On Fri, Feb 9, 2018 at 7:29 AM, Jeremy <jlrubin@mit•edu> wrote:
>> utility of this construction can be improved if we introduce functionality
>> that makes a script invalid after a certain time
>
> Tagging this thread with "nExpiryTime".  Search archives for more.

Fill-or-kill transaction was the term used on the list: https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2015-September/011042.html <https://lists.linuxfoundation.org/pipermail/bitcoin-dev/2015-September/011042.html>

Sjors