Skip to content

Commit 8d83ced

Browse files
committed
bip-tap: BIPs for the Taproot Assets Protocol
1 parent e643d24 commit 8d83ced

23 files changed

+94843
-0
lines changed

bip-tap-addr.mediawiki

+182
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
<pre>
2+
BIP: ???
3+
Layer: Applications
4+
Title: Taproot Asset On Chain Addresses
5+
Author: Olaoluwa Osuntokun <[email protected]>
6+
Comments-Summary: No comments yet.
7+
Comments-URI: https://git
8+
Status: Draft
9+
Type: Standards Track
10+
Created: 2021-12-10
11+
License: BSD-2-Clause
12+
</pre>
13+
14+
==Abstract==
15+
16+
This document describes a way to map a single-asset Taproot Asset send to a
17+
familiar <code>bech32m</code> address, as well as a way to map that address into
18+
a valid Taproot Asset script tree that can be included in a broadcast
19+
transaction to complete a transfer.
20+
Once the transaction has been broadcast, the receiver can use the
21+
previous outpoint of the confirmed transaction to lookup the complete asset
22+
proof in their chosen Universe.
23+
24+
==Copyright==
25+
26+
This document is licensed under the 2-clause BSD license.
27+
28+
==Motivation==
29+
30+
The Taproot Asset protocol needs an easy way to allow users to send each other
31+
assets on-chain, without requiring several rounds of interaction to exchange and
32+
validate proofs. By using the existing <code>bech32m</code> address
33+
serialization standard, such addresses look distinct, while also looking
34+
familiar enough based on the character set encoding. The described address
35+
format also addresses a number of possible foot guns, by making it impossible
36+
to send the wrong asset (based on an address) amongst other protections.
37+
38+
==Specification==
39+
40+
A Taproot Asset is uniquely defined by its <code>asset_genesis</code> as well as
41+
the <code>asset_script_key</code> that serves as a predicate that must be
42+
satisfied for transfers. These values, along with an internal Taproot key used
43+
when creating the Bitcoin output that holds the Taproot Asset, are encoded into
44+
a single address.
45+
46+
===Encoding an Address===
47+
48+
Let the human readable prefix (as specified by BIP 173) be:
49+
50+
* <code>tapbc</code> for mainnet
51+
* <code>taptb</code> for testnet
52+
* <code>taprt</code> for regtest
53+
* <code>taptb</code> for the public signet
54+
* <code>tapsb</code> for simnet
55+
56+
We refer to this value as the <code>taproot_asset_hrp</code>
57+
58+
Given the 32-byte <code>asset_id</code>, 33-byte compressed
59+
<code>asset_script_key</code>, and 33-byte compressed internal public
60+
key, 8-byte amount to send, an address is encoded as:
61+
* <code>bech32m(hrp=taproot_asset_hrp, addr_tlv_payload)</code>
62+
63+
where <code>addr_tlv_payload</code> is a TLV payload composed of the following
64+
types:
65+
* type: 0 (<code>taproot_asset_version</code>)
66+
** value:
67+
*** [<code>u8</code>:<code>version</code>]
68+
* type: 2 (<code>asset_id</code>)
69+
** value:
70+
*** [<code>32*byte</code>:<code>asset_id</code>]
71+
* type: 3 (<code>asset_key_family</code>)
72+
** value:
73+
*** [<code>33*byte</code>:<code>family_key</code>]
74+
* type: 4 (<code>asset_script_key</code>)
75+
** value:
76+
*** [<code>33*byte</code>:<code>script_key</code>]
77+
* type: 6 (<code>internal_key</code>)
78+
** value:
79+
*** [<code>33*byte</code>:<code>taproot_internal_key</code>]
80+
* type: 7 (<code>taproot_sibling_preimage</code>)
81+
** value:
82+
*** [<code>...*byte</code>:<code>tapscript_preimage</code>]
83+
* type: 8 (<code>amt</code>)
84+
** value:
85+
*** [<code>BigSize</code>:<code>amt_to_send</code>]
86+
* type: 10 (<code>proof_courier_addr</code>)
87+
** value:
88+
*** [<code>...*byte</code>:<code>proof_courier_addr</code>]
89+
90+
Inspired by Lightning's BOLT specification, we adopt the "it's OK to be odd"
91+
semantics here as well. This enables receivers to specify to the caller certain
92+
information that MUST be known in order to properly complete a transfer.
93+
94+
The only odd keys specified in the current version are the
95+
<code>asset_key_family</code> type and the <code>asset_type</code> field. The
96+
<code>asset_key_family</code> field isn't always needed for assets that don't
97+
allow for continual re-issuance. Similarly, if the <code>asset_type</code>
98+
field isn't specified, then one can assume a normal asset is being sent.
99+
100+
The <code>proof_courier_addr</code> is a mandatory URI (RFC 3986) that indicates
101+
what proof courier to use when sending the proofs from the sender to the
102+
recipient. The scheme (protocol) indicates the type of courier transport to use,
103+
current valid values are <code>hashmail://</code> for Hashmail based couriers
104+
and <code>universerpc://</code> for gRPC based transfer via a universe server.
105+
106+
===Decoding and Sending To An Address===
107+
108+
Given a valid Taproot Asset address, decompose the contents into the referenced
109+
<code>asset_id</code>, <code>asset_script_key</code>, and
110+
<code>internal_key</code>. Look up the full <code>asset_genesis</code> with the
111+
<code>asset_id</code> in the appropriate Universe.
112+
113+
Construct a new blank Taproot Asset leaf according to the default
114+
[[./bip-tap.mediawiki#asset-leaf-format|Asset Leaf Format]] with the following
115+
values being set explicitly (and all other values being their default/zero
116+
values):
117+
* <code>taproot_asset_version</code>: <code>0</code>
118+
* <code>asset_genesis</code>: <code>asset_genesis</code>
119+
* <code>amt</code>: <code>amt_to_send</code>
120+
* <code>asset_script_version</code>: <code>0</code>
121+
* <code>asset_script_key</code>: <code>asset_script_key</code>
122+
* <code>asset_key_family</code>: <code>asset_key_family</code>
123+
124+
Create a valid tapscript root, using leaf version <code>0x0c</code> with the
125+
sole leaf being the serialized TLV blob specified above.
126+
127+
Create the top-level taproot public key script, as a segwit v1 witness
128+
program, as specified in BIP 341, using the included key as the internal key.
129+
130+
With the target taproot public key script constructed, the asset is sent to the
131+
receiver with the execution of the following steps:
132+
# 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.
133+
# 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.
134+
## 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.
135+
## 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.
136+
## Broadcast and sign the transaction, submitting the resulting Taproot Asset state transition proof to a Universe of choice, also known by the receiver.
137+
# 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.
138+
139+
===Non-interactive full value send===
140+
141+
Sending assets to an address is inherently a non-interactive process as there is
142+
no active communication between the sender and recipient other than the exchange
143+
of the address in the first place.
144+
Because of the above mentioned requirement that an asset leaf created to send to
145+
an address MUST have a <code>split_commitment</code>, a special case exists if
146+
there is no change going back to the sender (an asset output is fully consumed
147+
by the transfer to an address): A special ''tombstone'' output with a value of
148+
0 must be created for the split root asset (the <code>root_asset</code> of the
149+
split) that holds the transfer witness. The <code>script_key</code> of the
150+
split root asset output should be the well-known NUMS point (using the string
151+
"taproot-assets" and the traditional "hash and increment" approach to generating
152+
the point) to prove the output cannot be spent further. Such a tombstone output
153+
can then be pruned from the tree when the UTXO is spent further.
154+
More details about interactive and non-interactive sends and tombstone outputs
155+
can be found in the [[./bip-tap-psbt.mediawiki|bip-tap-psbt]].
156+
157+
===Spending The Received Asset===
158+
159+
In order to spend (or simply confirm receipt) of the received asset, the
160+
receiver should:
161+
# Re-derive the taproot public key script created above that sends to their specified Taproot Asset leaf.
162+
# Wait for a transaction creating the output to be confirmed in the blockchain.
163+
## 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.
164+
# For each previous outpoint referenced in the transaction:
165+
## Look up the previous outpoint as a key into the chosen canonical Universe/Multiverse.
166+
### 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.
167+
# Walk the Universe tree backwards in time to incrementally construct the full provenance proof needed to spend the asset.
168+
169+
==Test Vectors==
170+
171+
Test vectors for [[Encoding an Address]] can be found here:
172+
* [[bip-tap-addr/address_tlv_encoding_generated.json|Address TLV encoding test vectors]]
173+
* [[bip-tap-addr/address_tlv_encoding_error_cases.json|Address TLV encoding error test vectors]]
174+
175+
The test vectors are automatically generated by
176+
[https://github.com/lightninglabs/taproot-assets/tree/main/address unit tests in
177+
the Taproot Assets GitHub repository].
178+
179+
==Reference Implementation==
180+
181+
github.com/lightninglabs/taproot-assets/tree/main/address
182+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
{
2+
"error_test_cases": [
3+
{
4+
"address": {},
5+
"error": "missing chain params HRP"
6+
},
7+
{
8+
"address": {
9+
"chain_params_hrp": "bc"
10+
},
11+
"error": "invalid chain params HRP"
12+
},
13+
{
14+
"address": {
15+
"chain_params_hrp": "tapbc"
16+
},
17+
"error": "missing asset ID"
18+
},
19+
{
20+
"address": {
21+
"chain_params_hrp": "tapbc",
22+
"asset_id": "0000000000000000000000000000000000000000000000000000000000000000"
23+
},
24+
"error": "missing script key"
25+
},
26+
{
27+
"address": {
28+
"chain_params_hrp": "tapbc",
29+
"asset_id": "0000000000000000000000000000000000000000000000000000000000000000",
30+
"script_key": "0000000000000000000000000000000000000000000000000000000000000000"
31+
},
32+
"error": "invalid script key length",
33+
"comment": "script key must be 33 bytes (compressed)"
34+
},
35+
{
36+
"address": {
37+
"chain_params_hrp": "tapbc",
38+
"asset_id": "0000000000000000000000000000000000000000000000000000000000000000",
39+
"script_key": "000000000000000000000000000000000000000000000000000000000000000000"
40+
},
41+
"error": "missing internal key"
42+
},
43+
{
44+
"address": {
45+
"chain_params_hrp": "tapbc",
46+
"asset_id": "0000000000000000000000000000000000000000000000000000000000000000",
47+
"script_key": "000000000000000000000000000000000000000000000000000000000000000000",
48+
"internal_key": "0000000000000000000000000000000000000000000000000000000000000000"
49+
},
50+
"error": "invalid internal key length",
51+
"comment": "internal key must be 33 bytes (compressed)"
52+
},
53+
{
54+
"address": {
55+
"chain_params_hrp": "tapbc",
56+
"asset_id": "0000000000000000000000000000000000000000000000000000000000000000",
57+
"script_key": "000000000000000000000000000000000000000000000000000000000000000000",
58+
"internal_key": "000000000000000000000000000000000000000000000000000000000000000000",
59+
"group_key": "0000000000000000000000000000000000000000000000000000000000000000"
60+
},
61+
"error": "invalid group key length",
62+
"comment": "group key must be 33 bytes (compressed)"
63+
}
64+
]
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
{
2+
"valid_test_cases": [
3+
{
4+
"address": {
5+
"chain_params_hrp": "taprt",
6+
"asset_version": 0,
7+
"asset_id": "7a3811630bb33503c6536c3a223d3caecb93fe55f4b3439528edf27b10d38e93",
8+
"group_key": "",
9+
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
10+
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
11+
"tapscript_sibling": "",
12+
"amount": 5577006791947779410,
13+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
14+
},
15+
"expected": "taprt1qqqsqq3q0gupzcctkv6s83jndsazy0fu4m9e8lj47je589fgahe8kyxn36fsgggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78svggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78ssz0lf4jcygg8ln74yz32dpshx6rdv95kcw309aexzmny9e5xzumgd4skjmpwwpex7mmx9e3k7atjd9jhyw35xseszxcn5n",
16+
"comment": "valid regtest address"
17+
},
18+
{
19+
"address": {
20+
"chain_params_hrp": "tapsb",
21+
"asset_version": 0,
22+
"asset_id": "8acb5154261425dd613fda19dee00a51e95c6921df8af4085bc468706b946fec",
23+
"group_key": "",
24+
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
25+
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
26+
"tapscript_sibling": "",
27+
"amount": 3510942875414458836,
28+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
29+
},
30+
"expected": "tapsb1qqqsqq3q3t94z4pxzsja6cflmgvaacq22854c6fpm790gzzmc358q6u5dlkqgggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78svggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78ssz0lxzu4luvrc3cagz32dpshx6rdv95kcw309aexzmny9e5xzumgd4skjmpwwpex7mmx9e3k7atjd9jhyw35xses89l6tn",
31+
"comment": "valid simnet address"
32+
},
33+
{
34+
"address": {
35+
"chain_params_hrp": "taptb",
36+
"asset_version": 0,
37+
"asset_id": "60459cbb4d4e6a78b8c58545f2fe026ad217bf0fafeed5ff5e3b3379c658ddf3",
38+
"group_key": "",
39+
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
40+
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
41+
"tapscript_sibling": "",
42+
"amount": 2740103009342231109,
43+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
44+
},
45+
"expected": "taptb1qqqsqq3qvpzeew6dfe483wx9s4zl9lszdtfp00c04lhdtl678vehn3jcmhesgggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78svggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78ssz0lycrv626h62fy2z32dpshx6rdv95kcw309aexzmny9e5xzumgd4skjmpwwpex7mmx9e3k7atjd9jhyw35xsesdgef4w",
46+
"comment": "valid testnet address"
47+
},
48+
{
49+
"address": {
50+
"chain_params_hrp": "tapbc",
51+
"asset_version": 0,
52+
"asset_id": "f2c70e1261b761f098499918acf6ccf18d7d9cf7cd6be0c940630f0643ab832c",
53+
"group_key": "",
54+
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
55+
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
56+
"tapscript_sibling": "",
57+
"amount": 545291762129038907,
58+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
59+
},
60+
"expected": "tapbc1qqqsqq3q7trsuynpkaslpxzfnyv2eakv7xxhm88he447pj2qvv8svsatsvkqgggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78svggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78ssz0lq7g58al55hhrkz32dpshx6rdv95kcw309aexzmny9e5xzumgd4skjmpwwpex7mmx9e3k7atjd9jhyw35xsesu23fxd",
61+
"comment": "valid mainnet address"
62+
},
63+
{
64+
"address": {
65+
"chain_params_hrp": "taptb",
66+
"asset_version": 0,
67+
"asset_id": "7f3a94b3048ecbce4f2b1686e2df89bde52d5ead1aed011f75fa6578dcab0839",
68+
"group_key": "03f32d239904d1addae728d1917a94bc1d20455b12b251a9222d035e5014a9f759",
69+
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
70+
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
71+
"tapscript_sibling": "",
72+
"amount": 1,
73+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
74+
},
75+
"expected": "taptb1qqqsqq3q0uaffvcy3m9uunetz6rw9hufhhjj6h4drtksz8m4lfjh3h9tpqusxggr7vkj8xgy6xka4eeg6xgh499ur5sy2kcjkfg6jg3dqd09q99f7avsgggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78svggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78ssqgppg4xsctndpkkz6tv8ghj7unpdejzu6rpwd5x6ctfdsh8qun0danzucm0w4exjetj8g6rgvcxm4yl6",
76+
"comment": "signet group collectible"
77+
},
78+
{
79+
"address": {
80+
"chain_params_hrp": "tapsb",
81+
"asset_version": 0,
82+
"asset_id": "7da00bc74bfe9791807fa20dbeec226348904e6bfa8c10ab434922369e4d4747",
83+
"group_key": "",
84+
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
85+
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
86+
"tapscript_sibling": "",
87+
"amount": 1,
88+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
89+
},
90+
"expected": "tapsb1qqqsqq3q0ksqh36tl6terqrl5gxmampzvdyfqnntl2xpp26rfy3rd8jdgarsgggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78svggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78ssqgppg4xsctndpkkz6tv8ghj7unpdejzu6rpwd5x6ctfdsh8qun0danzucm0w4exjetj8g6rgvcvmghpa",
91+
"comment": "simnet collectible"
92+
},
93+
{
94+
"address": {
95+
"chain_params_hrp": "tapsb",
96+
"asset_version": 0,
97+
"asset_id": "b603ddaafac3b6253de23ae01935f108bee96363eddf77456c0e24ed6a67650f",
98+
"group_key": "",
99+
"script_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
100+
"internal_key": "02a0afeb165f0ec36880b68e0baabd9ad9c62fd1a69aa998bc30e9a346202e078f",
101+
"tapscript_sibling": "00c0126e6f7420612076616c696420736372697074",
102+
"amount": 1,
103+
"proof_courier_addr": "hashmail://rand.hashmail.proof.courier:443"
104+
},
105+
"expected": "tapsb1qqqsqq3qkcpam2h6cwmz200z8tspjd03pzlwjcmrah0hw3tvpcjw66n8v58sgggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78svggz5zh7k9jlpmpk3q9k3c9640v6m8rzl5dxn25e30psax35vgpwq78sw9gqcqfxumm5ypsjqanpd35kggrnvdexjur5pqqszz32dpshx6rdv95kcw309aexzmny9e5xzumgd4skjmpwwpex7mmx9e3k7atjd9jhyw35xseswj02c5",
106+
"comment": "simnet collectible with sibling"
107+
}
108+
],
109+
"error_test_cases": null
110+
}

0 commit comments

Comments
 (0)