Note: Taproot descriptors in this chapter and associated demo library are illustrative of the ongoing design intent, but do not represent the final version. The taproot descriptors described below are based on this proposal.
Tapscript is a new Bitcoin output script language which is evaluated when the script path is used during the spending of a Taproot output. With a few noted exceptions (CHECKSIG opcodes), it carries many of the familiar op_codes
and evaluation logic which applies to Bitcoin script.
Nonetheless, for most use-cases, a set of standard tapscripts can be described with the following tapscript descriptors. Tapscript descriptors are encapsulated with ts()
.
- Pay to public key:
ts(pk(key))
- Pay to n keys:
ts(csa(key0, key1, ... ))
- Pay to musig key:
ts(musig(key0+key1+...+keyn))
Other tapscripts can be expressed with the raw hex representation of the script.
- Raw script:
ts(raw(hex))
Tapscript descriptors allow us to conveniently describe the tapscript spending conditions and also encode the tapscript in human-readable form. This is useful for the taproot-aware wallet, as it can conveniently recreate outputs it needs to be spend or watch.
The most basic tapscript is the one that is spendable by a single private key. We can construct a tp(pk(key))
tapscript object from a compressed public key as shown below.
Constructing a Pay-to-Pubkey Tapscript (Github):
sk = ECKey()
sk.generate()
pk = sk.get_pubkey()
print("Pubkey from pk tapscript: ", pk.get_bytes().hex())
ts_pk = TapLeaf()
ts_pk.from_keys([pk])
# Print output descriptor.
print(ts_pk.desc)
> Pubkey from pk tapscript:
> 0323955476dfac69df00f049dfa3d5eaeb56447fcae71d1f7aca4be0fa521b7376
> ts(pk(0323955476dfac69df00f049dfa3d5eaeb56447fcae71d1f7aca4be0fa521b7376))
Given a tapscript descriptor in string form we can decode it into a tapscript object.
Constructing a Pay-to-Pubkey tapscript from a descriptor (Github):
ts_desc = "ts(pk(026bf6d12e669cb96afb170daedcc0affe36fad226e9bf2b49c2ef9519361bb882))"
ts = TapLeaf()
ts.from_desc(ts_desc)
# Assert descriptor decoding and encoding result in same string.
assert(ts.desc == ts_desc)
# Print out tapscript operation by operation.
for op in ts.script:
if isinstance(op, bytes):
print(op.hex())
else:
print(op)
> 026bf6d12e669cb96afb170daedcc0affe36fad226e9bf2b49c2ef9519361bb882
> OP_CHECKSIG
In tapscript, OP_CHECKMULTISIG
has been disabled, since the signature order could otherwise be provided in any order, often resulting in an inefficient re-evaluation of signatures, thus making Schnorr batch signature verification impossible (correct order of signatures must be known before hand).
Instead, OP_CHECKSIGADD
has been to tapscript, which can be used to create a n-of-n output spending condition.
- Tapscript descriptor:
tp(csa(key0, key1, key2, ... keyN))
- Tapscript opcodes:
[pk0]
[pk1]
[pk2]
...[pkN]
OP_CHECKSIGASDD
N
OP_EQUAL
- Spending Witness Elements:
[sigN]
...[sig2]
[sig1]
[sig0]
TODO: Other checksig changes.
Constructing Pay to n-Pubkeys Tapscript (Github):
sks = []
pks = []
for i in range(3):
sks.append(ECKey())
sks[i].generate()
pks.append(sks[i].get_pubkey())
ts_csa = TapLeaf()
ts_csa.from_keys(pks)
print(ts_csa.desc)
for op in ts_csa.script:
if isinstance(op, bytes):
print(op.hex())
else:
print(op)
> ts(csa(02b0dbca6dd91141fd94834e99f63ac0f39462f20e3307d55420284189d4403daf,03006c05f3af04c9d781160bb3c49565feb30a70da52c14eb3bd1a857f8060ae27,03ef3d4679ebd1744a5db35d5d26a971a7525ecf86916a074948e683f020ba4d13))
> 02b0dbca6dd91141fd94834e99f63ac0f39462f20e3307d55420284189d4403daf
> OP_CHECKSIG
> 03006c05f3af04c9d781160bb3c49565feb30a70da52c14eb3bd1a857f8060ae27
> OP_CHECKSIGADD
> 03ef3d4679ebd1744a5db35d5d26a971a7525ecf86916a074948e683f020ba4d13
> OP_CHECKSIGADD
> 3
Generating n-of-m Pubkeys Threshold Tapscripts (Github):
In order to create a n-of-m threshold spending condition, we can simply determine all necessary combinations of n signers from m possible spenders, and create corresponding pay-to-n-pubkey tapscripts, which can only be executed exclusively in separate tapscripts.
sks = []
pks = []
for i in range(4):
sks.append(ECKey())
sks[i].generate()
pks.append(sks[i].get_pubkey())
tss , pk_map = TapLeaf.generate_threshold_csa(2, pks)
for ts in tss:
print(ts.desc)
> ts(csa(022563c72d2c2d465b72e775b80b444ba7e5346b71722f473a8f30cfc9f908c15d,02a7af595138c84d4d9f96e57362eaff10506468381efa84a20588bcb2455862c6,038eb9e77c488e2c94eb16589e018364715a8eb2c5bee36f3b77d073d15498df36))
> ts(csa(022563c72d2c2d465b72e775b80b444ba7e5346b71722f473a8f30cfc9f908c15d,02a7af595138c84d4d9f96e57362eaff10506468381efa84a20588bcb2455862c6,0398378227136efb07e26c1586b6bdc52d165363fa62d8db945d5757d525a31aae))
> ts(csa(022563c72d2c2d465b72e775b80b444ba7e5346b71722f473a8f30cfc9f908c15d,02a7af595138c84d4d9f96e57362eaff10506468381efa84a20588bcb2455862c6,039f303a852a65a6b3e92d87e1bffa4591feb27351c64c72c8db54dc604295e51b))
> ts(csa(022563c72d2c2d465b72e775b80b444ba7e5346b71722f473a8f30cfc9f908c15d,038eb9e77c488e2c94eb16589e018364715a8eb2c5bee36f3b77d073d15498df36,0398378227136efb07e26c1586b6bdc52d165363fa62d8db945d5757d525a31aae))
> ts(csa(022563c72d2c2d465b72e775b80b444ba7e5346b71722f473a8f30cfc9f908c15d,038eb9e77c488e2c94eb16589e018364715a8eb2c5bee36f3b77d073d15498df36,039f303a852a65a6b3e92d87e1bffa4591feb27351c64c72c8db54dc604295e51b))
> ts(csa(022563c72d2c2d465b72e775b80b444ba7e5346b71722f473a8f30cfc9f908c15d,0398378227136efb07e26c1586b6bdc52d165363fa62d8db945d5757d525a31aae,039f303a852a65a6b3e92d87e1bffa4591feb27351c64c72c8db54dc604295e51b))
> ts(csa(02a7af595138c84d4d9f96e57362eaff10506468381efa84a20588bcb2455862c6,038eb9e77c488e2c94eb16589e018364715a8eb2c5bee36f3b77d073d15498df36,0398378227136efb07e26c1586b6bdc52d165363fa62d8db945d5757d525a31aae))
> ts(csa(02a7af595138c84d4d9f96e57362eaff10506468381efa84a20588bcb2455862c6,038eb9e77c488e2c94eb16589e018364715a8eb2c5bee36f3b77d073d15498df36,039f303a852a65a6b3e92d87e1bffa4591feb27351c64c72c8db54dc604295e51b))
> ts(csa(02a7af595138c84d4d9f96e57362eaff10506468381efa84a20588bcb2455862c6,0398378227136efb07e26c1586b6bdc52d165363fa62d8db945d5757d525a31aae,039f303a852a65a6b3e92d87e1bffa4591feb27351c64c72c8db54dc604295e51b))
> ts(csa(038eb9e77c488e2c94eb16589e018364715a8eb2c5bee36f3b77d073d15498df36,0398378227136efb07e26c1586b6bdc52d165363fa62d8db945d5757d525a31aae,039f303a852a65a6b3e92d87e1bffa4591feb27351c64c72c8db54dc604295e51b))