rgb-archive / spec

[OLD!] RGB Protocol specifications for Bitcoin-based digital assets
https://rgb-org.github.io/
147 stars 26 forks source link

Idea: RGB-extended Bitcoin transaction validation #63

Closed ZmnSCPxj closed 5 years ago

ZmnSCPxj commented 6 years ago

It seems the details of this protocol have not been fixed yet, so I shall express here, an idea that I hope will allow RGB to be useable with future extensions to Bitcoin, and also with any Bitcoin-expressable smart contract, including offchain protocols like Lightning Network and CoinSwap and CoinJoin.

In transaction validation, much of the effort is signature validation, as controlled by a Bitcoin SCRIPT execution. In actuality, for native P2WPKH, there is not even any Bitcoin SCRIPT execution, and only a validation of public key and signature, uncontrolled by Bitcoin SCRIPT.

An RGB transaction is simply a Bitcoin transaction, extended with RGB-specific data.

RGB coins are colored by using the pay-to-contract construction, where a public key is tweaked. At the Bitcoin level, the tweaked key is used, but at the RGB level, the pre-tweaked public key and the RGB contract is used instead.

An RGB transaction includes an additional rgbWitness stack. This rgbWitness is additional witness data that an RGB protocol will validate.

Suppose we have a P2WPKH. At the Bitcoin level, the witness should be the signature pubKey. Roughly speaking, P2WPKH performs OP_DUP OP_HASH160 <pubKeyHash> OP_EQUALVERIFY OP_CHECKSIG. This is implicitly done by P2WPKH (in pre-SegWit P2PKH the script is explicitly written in scriptPubKey).

What I propose is that any time the Bitcoin level executes an OP_CHECKSIG or OP_CHECKMULTISIG (or equivalent operation as the implicit OP_CHECKSIG in P2WPKH) and takes pubkey(s) and signature(s) from the witness stack, the RGB level (which also runs the same verifications that Bitcoin does) will, in addition, take two items from the rgbWitness stack.

Those two items are (1) at stack top, a proof showing that the Bitcoin-level public key is the tweaked key Q committing to P and a contract that outputs some RGB-level assets, and (2) at stack top - 1, a signature of P signing the RGB-level transaction that takes the previous output and distributes it to the current transaction outputs.

For instance, suppose we already exist a unique cryptokitty whose sole purpose is to consume space on the blockchain, and that cryptokitty is hidden in a pre-existing UTXO that is a simple P2PKH. The public key secretly commits to the cryptokitty, and has the contract "1 cute ZmnSCPxj kitty" coming from ..., where ... is the client-side validated history (RGB-transactions) of the kitty.

To transfer this kitty to a 2-of-2 P2WSH, we create a Bitcoin-level transaction that spends the P2PKH normally (providing signature pubKey in the scriptSig as per pre-SegWit). Suppose we would like to put it in output 1 and some of our normal Bitcoin funds into output 0. Then the output 1 of the Bitcoin-level transaction would commit to a 2-of-2 multisig P2WSH, with the care point that the public keys in the multisig would have to commit to an RGB-level transaction that has as input the "1 cute ZmnSCPxj kitty", and two outputs, one with "0 cute ZmnSCPxj kitties" (output 0, which is a plain Bitcoin output), and the other with "1 cute ZmnSCPxj kitty" (output 1, where we want to move our kitty).

The public keys in the scriptPubKey commit to the RGB-level transaction, excluding the rgbWitness stack (since the rgbWitness stack itself contains signatures committing to the RGB-level transaction, and similar to how SegWit works where the txid does not commit to the witness stack).

So let us turn back to our cryptokitty. There exists a "1 cute ZmnSCPxj kittty" output on some RGB-level transaction R1, that is committed to by some Bitcoin-level transaction T1. Some pubkey Q1 on an output of T1 commits to the RGB-level transaction R1. The RGB-level transaction R2 that moves the kitty from the P2PKH to output 1 P2WSH. The RGB-level transaction then has a rgbWitness stack showing that Q1 commits to P1 and the RGB-level transaction R1, and also has a signature signed with P1 with the message R2. R2 has two outputs, one with "0 cute ZmnSCPxj kitty" and "1 cute ZmnSCPxj kitty". The Bitcoin-level T2 has a P2WSH whose public keys both commit to R2. To properly transfer the kitty from the 2-of-2 P2WSH to a different address, we would require another R3/T3 that shows that public key commits to R2, and contains signatures for R3 in its own rgbWitness space.

RGB clients would need to pass around the entire history of any assets they transfer (or possibly you use the RGB proof server).

This has the massive disadvantage that you would require reimplementing a Bitcoin client, since you need to hook into the CHECKSIG and CHECKMULTISIG operations, including those points where they are called automatically outside of script (e.g. P2WPKH). Any new Bitcoin improvements will also require updating the RGB client.

This however has the massive advantage that RGB can be made to work with any valid Bitcoin transaction. This means that any future offchain protocol can, with some effort, be made to work with RGB assets.

ZmnSCPxj commented 5 years ago

Continuing from this thought:

  1. The pay-to-contract construction is placed on outputs.
  2. Signatures, which commit to how an output will be distributed, are placed on inputs.

If we were to use pay-to-contract, then it becomes difficult to extend RGB to all possible future extensions of Bitcoin, and systems on top of Bitcoin.

So, remembering that the below involves novel cryptography that is made by a non-mathematician:

Dual-commitment ECDSA Signatures

ECDSA signing requires generation of a random point R from a secret r scalar. The x-coordinate of this R is then part of the final signature.

We can instead generate R from the below:

q = random()
Q = q * G
R = Q + h(Q || rgb_sighash) * G
r = q + h(Q || rgb_sighash)

Where rgb_sighash is similar to the SegWit sighash of a transaction, except the RGB-level transaction includes additional fields (number of assets in each input, number of assets in each output).

The resulting signature (R.x, s) commits not only to the Bitcoin-level transaction onchain (via s), but also to the RGB-level transaction (via R.x). Third-party malleability can only affect the sign of s but not the sign of R.x.

Onchain, SegWit witnesses are committed to in the witness Merkle tree on the coinbase.

Someone sending an RGB asset MUST then provide the chain of RGB transactions, and the points the equivalent Bitcoin-level transaction are confirmed onchain.

The witnesses for the Bitcoin-level transaction, the signatures, includes a commitment to the RGB-level transaction.

gabridome commented 5 years ago

This however has the massive advantage that RGB can be made to work with any valid Bitcoin transaction. This means that any future offchain protocol can, with some effort, be made to work with RGB assets.

With my poor understanding, I don't see why a modular approach based on a subsequent validation outside of Core, could not be adapted as well. What I'm missing?

ZmnSCPxj commented 5 years ago

It has been some time since I wrote my above blathering nonsense, but if I remember my thinking correctly:

  1. Ideally, the paint applied by RGB should be invisible to Core.
  2. However, the paint applied by RGB should be visible (possibly with some additional paint-identification information) to RGB.
  3. Ideally, the paint applied by RGB should be applicable to any contract encodable in Bitcoin SCRIPT, not just some subset of scripts.

A "plain" painting scheme would be to require payment to a P2PKH whose public key is computed using pay-to-contract.

However, it would only apply to P2PKH. If we wish to use some other contract type, then it is incompatible with this painting scheme.

The initial idea is a hare-brained thought to allow a signature to commit, not just to a Bitcoin transaction, but also to an RGB paint color. If paint is carried by signatures rather than particular SCRIPT templates, then Bitcoin Core is unaware of the color, but it is possible to show to an RGB implementation that yes, the signature does indeed commit to this output having a particular paint, which is also applied to this output and that output.

gabridome commented 5 years ago

Thank you to try to explain. It seems to me I got something more.

So what you are saying is, if I understand it correctly, that the present design of RGB is applicable only to particular script templates (namely P2PKH) and not to a generic transaction (for instance) P2SH, because the signature commit to the transaction but not to the RGB paint color.

If we put the paint in the signature, then it becomes "script agnostic" but this would require a "forked" version of Bitcoin Core and it would not be possible/convenient to put the verification of the color in an "external" program.

This, from a maintenance perspective would be a big burden, because it would require a lot of additional and very critic rebases. It is a big choice. Needs pondering. Thank you again for your thoughts and your explanations.

ZmnSCPxj commented 5 years ago

If we put the paint in the signature, then it becomes "script agnostic" but this would require a "forked" version of Bitcoin Core and it would not be possible/convenient to put the verification of the color in an "external" program.

What I am proposing is to fork only the SCRIPT interpreter, not the entire Bitcoin Core.

After all, if you have an instance of Bitcoin Core that already considers the transaction as valid (for Bitcoin), then you can have a separate external program that gets the SCRIPT, the witness stack, and a version of the transaction that includes RGB-specific data (e.g. asset types, how many assets go to which outputs) and outputs whether the transaction is valid (under RGB rules) or not.

You can keep the RGB-specific data in an (untrusted!) peer, and verify that the signatures in the witness stack commit to the full RGB-level transaction, while the signatures themselves are also valid for the Bitcoin-level transaction.

In particular, there is no consensus change in Bitcoin Core. Bitcoin Core is aware only of Bitcoin-level transactions. If an RGB transaction is valid at Bitcoin-level but is invalid at RGB-level, then the RGB assets have been burned, but the only one who can do this burning is the owner of the asset, so it is indistinguishable from the case where somebody acquires an asset, then loses the private keys in an information-theoretically-unrecoverable manner.

Admittedly, if improvements like Simplicity pan out and get into Bitcoin Core we would have to also include it, but as I understand it the Simplicity code itself is relatively modular. The only change needed is a change in the behavior of CHECKSIG-equivalent to check both Bitcoin-level and RGB-level transactions.

This allows us to have no special casing (at the RGB-onchain-level; obviously RGB-aware Lightning implementations still need to special case it) when RGB assets go through Lightning, especially if Lightning changes how channels are implemented.


This assumes that reimplementing Bitcoin SCRIPT (and only Bitcoin SCRIPT) is not so hard, and that changes in SCRIPTing are not so hard.

SCRIPT is a stack machine and is relatively easy to implement. Of course, Bitcoin history means that there are many many tweaks to the SCRIPT interpretation.

I would like to point out also that RGB-validity is dependent on Bitcoin-vaildity. So it is OK if RGB reimplementation of SCRIPT accepts more transactions as valid than Bitcoin does; if the RGB transaction is not accepted (after being stripped of RGB-specific data) by a stock Bitcoin Core, then it is still RGB-invalid even if the SCRIPT execution in RGB accepts it. Thus, small deviations between RGB SCRIPT and Bitcoin SCRIPT interpreters can be acceptable.

A caveat, again, is that putting the paint in the signature is an idea made by a non-mathematician and you should really find a mathematician to check if that idea is at all good. For all you know I am just trying to backdoor you.


So let me propose the following concrete process:

  1. An RGB implementation receives an RGB transaction that purports to be valid.
  2. That RGB transaction has an equivalent Bitcoin transaction after stripping of RGB-specific data (i.e. if the RGB-specific data (asset colors, asset issuance, what assets go to which outputs) is just concatenated to a standard Bitcoin tx, stripping is trivial).
  3. The RGB implementation checks if the equivalent Bitcoin transaction is valid according to a stock Bitcoin Core implementation.
  4. This is verified if (a) the Bitcoin tx is in a block that Core considers valid (b) the tx is in the Core mempool or (c) doing sendrawtransaction adds it to the Core mempool.
  5. The RGB implementation then checks if the RGB transaction is valid under RGB rules.
  6. The RGB implementation checks if the RGB transaction spends from a known-valid RGB transaction and that the asset types and asset issuances match up.
  7. Then SCRIPT from the inputs is executed, with the modification that CHECKSIG and similar operations verify the RGB transaction is committed to in the signature via the R.

This allows the RGB implementation to be separate from a stock Bitcoin Core.

afilini commented 5 years ago

I'm not a mathematician either, but I also like the idea of "painting" signatures (fun fact: that was actually in the spec originally as you can see from this commit 774d38ef5593553453c4d6edc6358c4b60370e83).

The reason why we migrated to pay to contract is that what you described works only as long as you have a single signature script. In the case of multisig (well, today's multisig, I'm ignoring Schnorr at the moment) it's impossible to enforce, at the Bitcoin level, that all signatures commit to the same data. So, even though none of the parties in a multisig address could steal the tokens, the last signer could sign with a wrong commitment (or just don't reveal the parameters needed to check it) effectively burn the asset for everybody.

You can imagine that we don't want this to happen, especially in a protocol that is meant to run on Lightning Network, aka "Multisig with random people on Tor" :smile:

ZmnSCPxj commented 5 years ago

I understand.