[bitcoin-dev] Should Graftroot be optional?

ZmnSCPxj ZmnSCPxj at protonmail.com
Wed Jun 20 12:12:28 UTC 2018

Good morning Pieter and Tim and all,

My understanding is that the idea now being discussed by Pieter and Tim is that the Graftroot signature is not `sign(P, script)` but instead `sign(P, sighash(tx))`, where `tx` is an "ordinary" transaction that spends the outpoint that pays to `P`, and a single output whose `scriptPubKey` is the Graftroot `script` and contains the entire value of the outpoint, and `sighash()` is the standard SegWit transaction digest algorithm used for signing (and is affected by other flags in the signature).

This has the advantage that the Graftroot signature commits to a single outpoint and cannot be used to spend all outpoints that happen to pay to the same `P` public key.

However I believe the ability to "immanentize" a Graftroot signature as a signature for a 1-input 1-output transaction is unsafe (so a Graftroot signature should probably not be "the same" as a signature for a 1-input 1-output transaction like the above).

Let us consider a simple CoinSwap protocol.  Let us focus on one half of the procedure, which is a simple ZKCP, i.e. Alice pays Bob for a hash preimage, with a timeout imposed so that Bob needs to provide the preimage, within a specified time.

1.  Alice and Bob generate a shared public key P which requires k_a (Alice secret key) and k_b (Bob secret key) to sign.

2.  Alice creates but does not sign a funding transaction that pays to public key P and gives its txid to Bob.  Alice also provides a standard P2WPKH return address that Alice controls.

3.  Bob creates a `nLockTime`-encumbered transaction (the timeout backoff transaction) on the agreed timeout, spending the above txid outpoint to the Alice return address, and provides its half of the signature to P signing the timeout backoff transaction to Alice.

4.  Alice keeps the above signature (verifying it is to the correct `nLockTime` and Alice return address), then signs and broadcasts the funding transaction.  Both wait for the funding transaction to confirm deeply.

5.  Alice then signs a Graftroot to the script `{ OP_HASH <hash> OP_EQUALVERIFY <P_b> OP_CHECKSIG }` and gives its half of the signature to P signing the Graftroot to Bob.  Bob keeps this signature.

6.  Bob provides the preimage to the hash directly to Alice and a standard P2WPKH destination address that Bob controls.

7.  Alice then signs a direct spend of the funding transaction outpoint (one that is not encumbered by `nLockTime`), spending the funding txid outpoint to the Bob destination address, and provides its half of the signature to P signing this transaction.  This completes Alice participation in the protocol (it has now received the preimage).

8.  Bob completes the signature to the destination transaction and broadcasts it to the blockchain layer.

If Alice or Bob stalls at step 5 or earlier then the transaction does not occur (Alice does not learn the preimage, Bob gets no money).

If Bob stalls at step 6, Alice can use the timeout backoff.

If Alice stalls at step 7, Bob can use the Graftroot signed at step 5 to claim its funds as long as the timeout is not reached.

Now if Graftroot signature is "actually" just a standard signature of a transaction that is elided from the blockchain, however, it means that this elided transaction can be immanentized on the blockchain with the specified script.  Even if this transaction has e.g. no fee then Bob could collude with a miner via sidefees to get the (valid) transaction onchain.

So Bob could take the signature made at 5 to create a transaction spending to the specified script, and prevent Alice from claiming the funds using the timeout backoff transaction.  Then Bob forever controls the UTXO and Alice cannot back out of the transaction, so even if the knowledge of the preimage ceases to be interesting, Alice has already paid Bob and Bob can provide the preimage at its leisure rather than constrained by the timeout.

Thus we should somehow disallow immanentizing the Graftroot signature.

An idea is that the Graftroot signature should sign a transaction with a specific special `nVersion`, that is then soft-forked to be invalid onchain (i.e. the `nVersion` is reserved for Graftroot and it is invalid for a transaction onchain to use that `nVersion`).  So the Graftroot signature can be used as a Graftroot spend, but not as a immanentized signature on an actual onchain transaction that could disable the timeout backoff transaction.

Utilities that can sign an arbitrary message using your private keys could check if the first four bytes match the Graftroot `nVersion` and refuse to sign such messages to prevent inadvertently giving a Graftroot signature.

Alternatively, we note that the "transaction" signed by Graftroot will not be referred to onchain anyway, and we could use a completely different `sighash()` algorithm, e.g. it could just be the outpoint being spent and the script to be executed, i.e. `sign(P, concat(txid, outnum, script))`.  This reduces code reuse, though.


More information about the bitcoin-dev mailing list