1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
|
from .errors import MiniscriptError
from .base import DescriptorBase
from .miniscript import Miniscript
from ..hashes import tagged_hash
from ..script import Script
class TapLeaf(DescriptorBase):
def __init__(self, miniscript=None, version=0xC0):
self.miniscript = miniscript
self.version = version
def __str__(self):
return str(self.miniscript)
@classmethod
def read_from(cls, s):
ms = Miniscript.read_from(s, taproot=True)
return cls(ms)
def serialize(self):
if self.miniscript is None:
return b""
return bytes([self.version]) + Script(self.miniscript.compile()).serialize()
@property
def keys(self):
return self.miniscript.keys
def derive(self, *args, **kwargs):
if self.miniscript is None:
return type(self)(None, version=self.version)
return type(self)(
self.miniscript.derive(*args, **kwargs),
self.version,
)
def branch(self, *args, **kwargs):
if self.miniscript is None:
return type(self)(None, version=self.version)
return type(self)(
self.miniscript.branch(*args, **kwargs),
self.version,
)
def to_public(self, *args, **kwargs):
if self.miniscript is None:
return type(self)(None, version=self.version)
return type(self)(
self.miniscript.to_public(*args, **kwargs),
self.version,
)
def _tweak_helper(tree):
# https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki#constructing-and-spending-taproot-outputs
if isinstance(tree, TapTree):
tree = tree.tree
if isinstance(tree, TapLeaf):
# one leaf on this branch
h = tagged_hash("TapLeaf", tree.serialize())
return ([(tree, b"")], h)
left, left_h = _tweak_helper(tree[0])
right, right_h = _tweak_helper(tree[1])
ret = [(leaf, c + right_h) for leaf, c in left] + [
(leaf, c + left_h) for leaf, c in right
]
if right_h < left_h:
left_h, right_h = right_h, left_h
return (ret, tagged_hash("TapBranch", left_h + right_h))
class TapTree(DescriptorBase):
def __init__(self, tree=None):
"""tree can be None, TapLeaf or a tuple (taptree, taptree)"""
self.tree = tree
# make sure all keys are taproot
for k in self.keys:
k.taproot = True
def __bool__(self):
return bool(self.tree)
def tweak(self):
if self.tree is None:
return b""
_, h = _tweak_helper(self.tree)
return h
@property
def keys(self):
if self.tree is None:
return []
if isinstance(self.tree, TapLeaf):
return self.tree.keys
left, right = self.tree
return left.keys + right.keys
@classmethod
def read_from(cls, s):
c = s.read(1)
if len(c) == 0:
return cls()
if c == b"{": # more than one miniscript
left = cls.read_from(s)
c = s.read(1)
if c == b"}":
return left
if c != b",":
raise MiniscriptError("Invalid taptree syntax: expected ','")
right = cls.read_from(s)
if s.read(1) != b"}":
raise MiniscriptError("Invalid taptree syntax: expected '}'")
return cls((left, right))
s.seek(-1, 1)
ms = TapLeaf.read_from(s)
return cls(ms)
def derive(self, *args, **kwargs):
if self.tree is None:
return type(self)(None)
if isinstance(self.tree, TapLeaf):
return type(self)(self.tree.derive(*args, **kwargs))
left, right = self.tree
return type(self)((left.derive(*args, **kwargs), right.derive(*args, **kwargs)))
def branch(self, *args, **kwargs):
if self.tree is None:
return type(self)(None)
if isinstance(self.tree, TapLeaf):
return type(self)(self.tree.branch(*args, **kwargs))
left, right = self.tree
return type(self)((left.branch(*args, **kwargs), right.branch(*args, **kwargs)))
def to_public(self, *args, **kwargs):
if self.tree is None:
return type(self)(None)
if isinstance(self.tree, TapLeaf):
return type(self)(self.tree.to_public(*args, **kwargs))
left, right = self.tree
return type(self)(
(left.to_public(*args, **kwargs), right.to_public(*args, **kwargs))
)
def __str__(self):
if self.tree is None:
return ""
if isinstance(self.tree, TapLeaf):
return str(self.tree)
(left, right) = self.tree
return "{%s,%s}" % (left, right)
|