Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

bip-tap: BIPs for the Taproot Assets Protocol #1489

Open
wants to merge 16 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
185 changes: 185 additions & 0 deletions bip-tap-addr.mediawiki
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
<pre>
BIP: ???
Layer: Applications
Title: Taproot Asset On Chain Addresses
Author: Olaoluwa Osuntokun <laolu32@gmail.com>
Comments-Summary: No comments yet.
Comments-URI: https://git
Status: Draft
Type: Standards Track
Created: 2021-12-10
License: BSD-2-Clause
</pre>

==Abstract==

This document describes a way to map a single-asset Taproot Asset send to a
familiar <code>bech32m</code> address, as well as a way to map that address into
a valid Taproot Asset script tree that can be included in a broadcast
transaction to complete a transfer.
Once the transaction has been broadcast, the receiver can use the
previous outpoint of the confirmed transaction to lookup the complete asset
proof in their chosen Universe.

==Copyright==

This document is licensed under the 2-clause BSD license.

==Motivation==

The Taproot Asset protocol needs an easy way to allow users to send each other
assets on-chain, without requiring several rounds of interaction to exchange and
validate proofs. By using the existing <code>bech32m</code> address
serialization standard, such addresses look distinct, while also looking
familiar enough based on the character set encoding. The described address
format also addresses a number of possible foot guns, by making it impossible
to send the wrong asset (based on an address) amongst other protections.

==Specification==

A Taproot Asset is uniquely defined by its <code>asset_genesis</code> as well as
the <code>asset_script_key</code> that serves as a predicate that must be
satisfied for transfers. These values, along with an internal Taproot key used
when creating the Bitcoin output that holds the Taproot Asset, are encoded into
a single address.

===Encoding an Address===

Let the human readable prefix (as specified by BIP 173) be:

* <code>tapbc</code> for mainnet
* <code>taptb</code> for testnet
* <code>taprt</code> for regtest
* <code>taptb</code> for the public signet
* <code>tapsb</code> for simnet

We refer to this value as the <code>taproot_asset_hrp</code>

Given the 32-byte <code>asset_id</code>, 33-byte compressed
<code>asset_script_key</code>, and 33-byte compressed internal public
key, 8-byte amount to send, an address is encoded as:
* <code>bech32m(hrp=taproot_asset_hrp, addr_tlv_payload)</code>

where <code>addr_tlv_payload</code> is a TLV payload composed of the following
types:
* type: 0 (<code>taproot_address_version</code>)
** value:
*** [<code>u8</code>:<code>version</code>]
* type: 2 (<code>taproot_asset_version</code>)
** value:
*** [<code>u8</code>:<code>version</code>]
* type: 4 (<code>asset_id</code>)
** value:
*** [<code>32*byte</code>:<code>asset_id</code>]
* type: 5 (<code>asset_group_key</code>)
** value:
*** [<code>33*byte</code>:<code>group_key</code>]
* type: 6 (<code>asset_script_key</code>)
** value:
*** [<code>33*byte</code>:<code>script_key</code>]
* type: 8 (<code>internal_key</code>)
** value:
*** [<code>33*byte</code>:<code>taproot_internal_key</code>]
* type: 9 (<code>taproot_sibling_preimage</code>)
** value:
*** [<code>...*byte</code>:<code>tapscript_preimage</code>]
* type: 10 (<code>amt</code>)
** value:
*** [<code>BigSize</code>:<code>amt_to_send</code>]
* type: 12 (<code>proof_courier_addr</code>)
** value:
*** [<code>...*byte</code>:<code>proof_courier_addr</code>]

Inspired by Lightning's BOLT specification, we adopt the "it's OK to be odd"
semantics here as well. This enables receivers to specify to the caller certain
information that MUST be known in order to properly complete a transfer.

The only odd keys specified in the current version
(<code>taproot_address_version = 0</code>) are the <code>asset_group_key</code>
type and the <code>taproot_sibling_preimage</code> field. The
<code>asset_group_key</code> field isn't always needed for assets that don't
allow for continual re-issuance.

The <code>proof_courier_addr</code> is a mandatory URI (RFC 3986) that indicates
what proof courier to use when sending the proofs from the sender to the
recipient. The scheme (protocol) indicates the type of courier transport to use,
current valid values are <code>hashmail://</code> for Hashmail based couriers
and <code>universerpc://</code> for gRPC based transfer via a universe server.

===Decoding and Sending To An Address===

Given a valid Taproot Asset address, decompose the contents into the referenced
<code>asset_id</code>, <code>asset_script_key</code>, and
<code>internal_key</code>. Look up the full <code>asset_genesis</code> with the
<code>asset_id</code> in the appropriate Universe.

Construct a new blank Taproot Asset leaf according to the default
[[./bip-tap.mediawiki#asset-leaf-format|Asset Leaf Format]] with the following
values being set explicitly (and all other values being their default/zero
values):
* <code>taproot_asset_version</code>: <code>taproot_asset_version</code>
* <code>asset_genesis</code>: <code>asset_genesis</code>
* <code>amt</code>: <code>amt_to_send</code>
* <code>asset_script_version</code>: <code>0</code>
* <code>asset_script_key</code>: <code>asset_script_key</code>
* <code>asset_group_key</code>: <code>asset_group_key</code>

Create a valid tapscript root, using leaf version <code>0x0c</code> with the
sole leaf being the serialized TLV blob specified above.

Create the top-level taproot public key script, as a segwit v1 witness
program, as specified in BIP 341, using the included key as the internal key.

With the target taproot public key script constructed, the asset is sent to the
receiver with the execution of the following steps:
# Construct a valid transaction that spends an input that holds the referenced <code>asset_id</code> and ''exactly'' <code>amt</code> units of the asset.
# Create a new Taproot Asset output commitment based on the input commitment (this will be the change output), that now only commits to <code>S-A</code> units of <code>asset_id</code>, where <code>S</code> is the input amount, and <code>A</code> is the amount specified in the encoded Taproot Asset address.
## This new leaf MUST have a <code>split_commitment</code> specified that commits to the position (keyed by <code>sha256(output_index || asset_id || asset_script_key)</code> within the transaction of the newly created asset leaf for the receiver. This split commitment is omitted by the sender when serializing the leaf for inclusion in the asset tree, otherwise the tree wouldn't be predictable on the receiver side. This has a corresponding rule in the [[./bip-tap-vm.mediawiki|bip-tap-vm]] during the input mapping of the inclusion proof validation.
## Add an additional output that sends a de minimis (in practice this MUST be above dust) amount to the top-level taproot public key computed earlier.
## Broadcast and sign the transaction, submitting the resulting Taproot Asset state transition proof to a Universe of choice, also known by the receiver.
# Post the resulting state transition proof to the specified Universe. The submitted proof ''must'' contain the optional auxiliary value of the full <code>split_commitment</code> the receiver requires to spend the asset.

===Non-interactive full value send===

Sending assets to an address is inherently a non-interactive process as there is
no active communication between the sender and recipient other than the exchange
of the address in the first place.
Because of the above mentioned requirement that an asset leaf created to send to
an address MUST have a <code>split_commitment</code>, a special case exists if
there is no change going back to the sender (an asset output is fully consumed
by the transfer to an address): A special ''tombstone'' output with a value of
0 must be created for the split root asset (the <code>root_asset</code> of the
split) that holds the transfer witness. The <code>script_key</code> of the
split root asset output should be the well-known NUMS point (using the string
"taproot-assets" and the traditional "hash and increment" approach to generating
the point) to prove the output cannot be spent further. Such a tombstone output
can then be pruned from the tree when the UTXO is spent further.
More details about interactive and non-interactive sends and tombstone outputs
can be found in the [[./bip-tap-psbt.mediawiki|bip-tap-psbt]].

===Spending The Received Asset===

In order to spend (or simply confirm receipt) of the received asset, the
receiver should:
# Re-derive the taproot public key script created above that sends to their specified Taproot Asset leaf.
# Wait for a transaction creating the output to be confirmed in the blockchain.
## In practice this may be via light client protocols such as BIP 157/158, or simply a full node with an address index, or import public key.
# For each previous outpoint referenced in the transaction:
## Look up the previous outpoint as a key into the chosen canonical Universe/Multiverse.
### If the key is found, verify the inclusion proof of the value (as described in [[./bip-tap-proof-file.mediawiki|bip-tap-proof-file]]), and extract the <code>split_commitment</code> inclusion proof for the output.
# Walk the Universe tree backwards in time to incrementally construct the full provenance proof needed to spend the asset.

==Test Vectors==

Test vectors for [[Encoding an Address]] can be found here:
* [[bip-tap-addr/address_tlv_encoding_generated.json|Address TLV encoding test vectors]]
* [[bip-tap-addr/address_tlv_encoding_error_cases.json|Address TLV encoding error test vectors]]

The test vectors are automatically generated by
[https://github.com/lightninglabs/taproot-assets/tree/main/address unit tests in
the Taproot Assets GitHub repository].

==Reference Implementation==

github.com/lightninglabs/taproot-assets/tree/main/address

65 changes: 65 additions & 0 deletions bip-tap-addr/address_tlv_encoding_error_cases.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
{
"error_test_cases": [
{
"address": {},
"error": "missing chain params HRP"
},
{
"address": {
"chain_params_hrp": "bc"
},
"error": "invalid chain params HRP"
},
{
"address": {
"chain_params_hrp": "tapbc"
},
"error": "missing asset ID"
},
{
"address": {
"chain_params_hrp": "tapbc",
"asset_id": "0000000000000000000000000000000000000000000000000000000000000000"
},
"error": "missing script key"
},
{
"address": {
"chain_params_hrp": "tapbc",
"asset_id": "0000000000000000000000000000000000000000000000000000000000000000",
"script_key": "0000000000000000000000000000000000000000000000000000000000000000"
},
"error": "invalid script key length",
"comment": "script key must be 33 bytes (compressed)"
},
{
"address": {
"chain_params_hrp": "tapbc",
"asset_id": "0000000000000000000000000000000000000000000000000000000000000000",
"script_key": "000000000000000000000000000000000000000000000000000000000000000000"
},
"error": "missing internal key"
},
{
"address": {
"chain_params_hrp": "tapbc",
"asset_id": "0000000000000000000000000000000000000000000000000000000000000000",
"script_key": "000000000000000000000000000000000000000000000000000000000000000000",
"internal_key": "0000000000000000000000000000000000000000000000000000000000000000"
},
"error": "invalid internal key length",
"comment": "internal key must be 33 bytes (compressed)"
},
{
"address": {
"chain_params_hrp": "tapbc",
"asset_id": "0000000000000000000000000000000000000000000000000000000000000000",
"script_key": "000000000000000000000000000000000000000000000000000000000000000000",
"internal_key": "000000000000000000000000000000000000000000000000000000000000000000",
"group_key": "0000000000000000000000000000000000000000000000000000000000000000"
},
"error": "invalid group key length",
"comment": "group key must be 33 bytes (compressed)"
}
]
}
110 changes: 110 additions & 0 deletions bip-tap-addr/address_tlv_encoding_generated.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
{
"valid_test_cases": [
{
"address": {
"chain_params_hrp": "taprt",
"asset_version": 0,
"asset_id": "7a3811630bb33503c6536c3a223d3caecb93fe55f4b3439528edf27b10d38e93",
"group_key": "",
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "",
"amount": 5577006791947779410,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
},
"expected": "taprt1qqqsqq3q0gupzcctkv6s83jndsazy0fu4m9e8lj47je589fgahe8kyxn36fsgggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78svggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78ssz0lf4jcygg8ln74yz32dpshx6rdv95kcw309aexzmny9e5xzumgd4skjmpwwpex7mmx9e3k7atjd9jhyw35xseszxcn5n",
"comment": "valid regtest address"
},
{
"address": {
"chain_params_hrp": "tapsb",
"asset_version": 0,
"asset_id": "8acb5154261425dd613fda19dee00a51e95c6921df8af4085bc468706b946fec",
"group_key": "",
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "",
"amount": 3510942875414458836,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
},
"expected": "tapsb1qqqsqq3q3t94z4pxzsja6cflmgvaacq22854c6fpm790gzzmc358q6u5dlkqgggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78svggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78ssz0lxzu4luvrc3cagz32dpshx6rdv95kcw309aexzmny9e5xzumgd4skjmpwwpex7mmx9e3k7atjd9jhyw35xses89l6tn",
"comment": "valid simnet address"
},
{
"address": {
"chain_params_hrp": "taptb",
"asset_version": 0,
"asset_id": "60459cbb4d4e6a78b8c58545f2fe026ad217bf0fafeed5ff5e3b3379c658ddf3",
"group_key": "",
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "",
"amount": 2740103009342231109,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
},
"expected": "taptb1qqqsqq3qvpzeew6dfe483wx9s4zl9lszdtfp00c04lhdtl678vehn3jcmhesgggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78svggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78ssz0lycrv626h62fy2z32dpshx6rdv95kcw309aexzmny9e5xzumgd4skjmpwwpex7mmx9e3k7atjd9jhyw35xsesdgef4w",
"comment": "valid testnet address"
},
{
"address": {
"chain_params_hrp": "tapbc",
"asset_version": 0,
"asset_id": "f2c70e1261b761f098499918acf6ccf18d7d9cf7cd6be0c940630f0643ab832c",
"group_key": "",
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "",
"amount": 545291762129038907,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
},
"expected": "tapbc1qqqsqq3q7trsuynpkaslpxzfnyv2eakv7xxhm88he447pj2qvv8svsatsvkqgggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78svggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78ssz0lq7g58al55hhrkz32dpshx6rdv95kcw309aexzmny9e5xzumgd4skjmpwwpex7mmx9e3k7atjd9jhyw35xsesu23fxd",
"comment": "valid mainnet address"
},
{
"address": {
"chain_params_hrp": "taptb",
"asset_version": 0,
"asset_id": "7f3a94b3048ecbce4f2b1686e2df89bde52d5ead1aed011f75fa6578dcab0839",
"group_key": "03f32d239904d1addae728d1917a94bc1d20455b12b251a9222d035e5014a9f759",
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "",
"amount": 1,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
},
"expected": "taptb1qqqsqq3q0uaffvcy3m9uunetz6rw9hufhhjj6h4drtksz8m4lfjh3h9tpqusxggr7vkj8xgy6xka4eeg6xgh499ur5sy2kcjkfg6jg3dqd09q99f7avsgggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78svggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78ssqgppg4xsctndpkkz6tv8ghj7unpdejzu6rpwd5x6ctfdsh8qun0danzucm0w4exjetj8g6rgvcxm4yl6",
"comment": "signet group collectible"
},
{
"address": {
"chain_params_hrp": "tapsb",
"asset_version": 0,
"asset_id": "7da00bc74bfe9791807fa20dbeec226348904e6bfa8c10ab434922369e4d4747",
"group_key": "",
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "",
"amount": 1,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
},
"expected": "tapsb1qqqsqq3q0ksqh36tl6terqrl5gxmampzvdyfqnntl2xpp26rfy3rd8jdgarsgggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78svggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78ssqgppg4xsctndpkkz6tv8ghj7unpdejzu6rpwd5x6ctfdsh8qun0danzucm0w4exjetj8g6rgvcvmghpa",
"comment": "simnet collectible"
},
{
"address": {
"chain_params_hrp": "tapsb",
"asset_version": 0,
"asset_id": "b603ddaafac3b6253de23ae01935f108bee96363eddf77456c0e24ed6a67650f",
"group_key": "",
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
"tapscript_sibling": "00c0126e6f7420612076616c696420736372697074",
"amount": 1,
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
},
"expected": "tapsb1qqqsqq3qkcpam2h6cwmz200z8tspjd03pzlwjcmrah0hw3tvpcjw66n8v58sgggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78svggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78sw9gqcqfxumm5ypsjqanpd35kggrnvdexjur5pqqszz32dpshx6rdv95kcw309aexzmny9e5xzumgd4skjmpwwpex7mmx9e3k7atjd9jhyw35xseswj02c5",
"comment": "simnet collectible with sibling"
}
],
"error_test_cases": null
}