Linked below, a high level paper on how covenants could be introduced to Bitcoin without a soft fork using a mix of Functional Encryption and Zero Knowledge Proofs, posted here for discussion.

Hi Jeremy,

thanks for this, looks very interesting. As Iâ€™m reading Iâ€™m seeing something that looks like a typo:

"Next, encrypt m " - should that be p ? and,

UniqueSecKey(p, TX) = Add(m, CTVHash(TX))

Same, shouldnâ€™t m be p here?

Also, a clarifying question for the end of that section:

You write:

" \sigma \leftarrow C_F(C_p, Encrypt(M, TX))

\sigma can be used as a signature of u over TX".

Would this be correct?

\sigma = Decrypt(C_F, C_p, Encrypt(M, TX))

â€¦ where Iâ€™m taking the definition of Decrypt from the start of the paper and extending to the two-argument case, i.e. defining for an encryption public key of M, Decrypt(q, c_1, c_2) = X where the function has been previously defined F(a, b) = X and c_1, c_2 are ciphertexts of a, b and q is the output of EncryptFunc(M, F).

So anyway, if I got that right, let me try to translate into English what this machine does:

The basic building block is the ability to create a decryption key that decrypts a given ciphertext to the *output* of F when its plaintext is used as input.

The way youâ€™re using it here (seems pretty clever!) is that you define a function which is " F(private key, transaction) = the ECDSA signature of a transaction by a private key *tweaked by the hash of the transaction*". This means that given the ciphertext of that tweaked private key, and â€śgivenâ€ť the ciphertext for the transaction (anyone can generate ciphertexts; this is public key cryptography), you can pass those two arguments into the output of the â€śbasic building block aboveâ€ť; youâ€™ll get a signature on that transaction. But, because of the tweak, if you try to do the same with any other transaction, itâ€™ll fail.

(Deleted earlier misunderstanding)

So have I got this right:

Given C_F, the â€śfunctional encryptionâ€ť and C_p, it allows *anyone* to create a signature for *only the specified transaction for a given tweaked key*.

So the flow is like:

- C_F and C_p are created by the owner of (m,M) and (p,P)
- m and p are deleted
- Then anyone could calculate a covenant output destination pubkey as a tweak of P + Hash(tx)
- They would know that the only way it can be signed over is by doing \sigma = C_F(C_p, Encrypt(M, TX)). Of course
*anyone*could do that but thatâ€™s no different than today, we know how to lock with additional conditions in Script

The point being that this process is repeatable, for any transaction so it doesnâ€™t require statically signing in advance, expecting certain outcomes.

astute read â€“ yes itâ€™s a typo.

yes this is correct understanding, and I think your decrypt function looks correct.

I think itâ€™s more intuitive to think of Decrypt as just the actual function call, but the normal FE papers donâ€™t do this for various reasons.

Yeah that sounds right.

The whole program instance stuff is designed to handle cases where the covenant instance (the program key) and the transaction are a one-key-to-many-transaction case.

@AdamISZ the deleting/editing is kind of confusing. But I think one other point to consider is normal signing federations have to actively sign this stuff, ongoing trust concern.

Whereas with FEâ€™d Up Covenants, itâ€™s a one-time compiler build for all eternity (until quantum computers tell us we canâ€™t have fun ever again?).

My spookchains writeup isâ€¦ spookily similar to this kind of setup, so maybe worth reading for a more thorough discussion (end section " Commentary on Trust and Covenantiness") Spookchains: Drivechain Analog with Trusted Setup & APO Â· Jeremy Rubin