diff --git a/buidl/blinding.py b/buidl/blinding.py index 6cba612..c16d415 100644 --- a/buidl/blinding.py +++ b/buidl/blinding.py @@ -19,7 +19,7 @@ def secure_secret_path(depth=4): 8: 248 9: 279 """ - if type(depth) != int: + if not isinstance(depth, int): raise ValueError(f"depth must be an int: {depth}") if depth >= 32: raise ValueError( diff --git a/buidl/cecc.py b/buidl/cecc.py index bf23bfc..4046854 100644 --- a/buidl/cecc.py +++ b/buidl/cecc.py @@ -2,6 +2,7 @@ import hmac import secrets +from buidl.hash import hash_taptweak from buidl.helper import ( big_endian_to_int, encode_base58_checksum, @@ -86,6 +87,12 @@ def __add__(self, scalar): raise RuntimeError("libsecp256k1 serialize error") return self.__class__(usec=bytes(serialized)) + def even_point(self): + if self.parity: + return -1 * self + else: + return self + def verify(self, z, sig): msg = int_to_big_endian(z, 32) sig_data = sig.cdata() @@ -134,8 +141,8 @@ def sec(self, compressed=True): self.usec = bytes(ffi.buffer(serialized, 65)) return self.usec - def bip340(self): - # returns the binary version of BIP340 pubkey + def xonly(self): + # returns the binary version of XONLY pubkey xonly_key = ffi.new("secp256k1_xonly_pubkey *") if not lib.secp256k1_xonly_pubkey_from_pubkey( GLOBAL_CTX, xonly_key, ffi.NULL, self.c @@ -146,6 +153,24 @@ def bip340(self): raise RuntimeError("libsecp256k1 xonly serialize error") return bytes(ffi.buffer(output32, 32)) + def tweak(self, merkle_root=b""): + """returns the tweak for use in p2tr""" + # take the hash_taptweak of the xonly and the merkle root + tweak = hash_taptweak(self.xonly() + merkle_root) + return tweak + + def tweaked_key(self, merkle_root=b"", tweak=None): + """Creates the tweaked external key for a particular merkle root/tweak.""" + # Get the tweak with the merkle root + if tweak is None: + tweak = self.tweak(merkle_root) + # t is the tweak interpreted as a big endian integer + t = big_endian_to_int(tweak) + # Q = P + tG + external_key = self.even_point() + t + # return the external key + return external_key + def hash160(self, compressed=True): # get the sec sec = self.sec(compressed) @@ -172,12 +197,13 @@ def p2sh_p2wpkh_redeem_script(self): """Returns the RedeemScript for a p2sh-p2wpkh redemption""" return self.p2wpkh_script().redeem_script() - def p2tr_script(self): + def p2tr_script(self, merkle_root=b"", tweak=None): """Returns the p2tr Script object""" + external_pubkey = self.tweaked_key(merkle_root, tweak) # avoid circular dependency - from buidl.taproot import TapRoot + from buidl.script import P2TRScriptPubKey - return TapRoot(self).script_pubkey() + return P2TRScriptPubKey(external_pubkey) def p2pk_tap_script(self): """Returns the p2tr Script object""" @@ -198,9 +224,9 @@ def p2sh_p2wpkh_address(self, network="mainnet"): """Returns the p2sh-p2wpkh base58 address string""" return self.p2wpkh_script().p2sh_address(network) - def p2tr_address(self, network="mainnet"): + def p2tr_address(self, merkle_root=b"", tweak=None, network="mainnet"): """Returns the p2tr bech32m address string""" - return self.p2tr_script().address(network) + return self.p2tr_script(merkle_root, tweak).address(network) def verify_message(self, message, sig): """Verify a message in the form of bytes. Assumes that the z @@ -214,9 +240,9 @@ def verify_message(self, message, sig): @classmethod def parse(cls, binary): - """returns a Point object from a SEC or BIP340 pubkey""" + """returns a Point object from a SEC or XONLY pubkey""" if len(binary) == 32: - return cls.parse_bip340(binary) + return cls.parse_xonly(binary) elif len(binary) in (33, 65): return cls.parse_sec(binary) else: @@ -231,7 +257,7 @@ def parse_sec(cls, sec_bin): return cls(csec=sec_bin) @classmethod - def parse_bip340(cls, binary): + def parse_xonly(cls, binary): sec_bin = b"\x02" + binary return cls(csec=sec_bin) @@ -348,6 +374,12 @@ def __init__(self, secret, network="mainnet", compressed=True): def hex(self): return "{:x}".format(self.secret).zfill(64) + def even_secret(self): + if self.point.parity: + return N - self.secret + else: + return self.secret + def sign(self, z): # per libsecp256k1 documentation, this helps against side-channel attacks if not lib.secp256k1_context_randomize( @@ -435,12 +467,15 @@ def wif(self, compressed=True): # encode_base58_checksum the whole thing return encode_base58_checksum(prefix + secret_bytes + suffix) - def tweaked(self, tweak): - if self.point.parity: - s = N - self.secret - else: - s = self.secret - new_secret = (s + tweak) % N + def tweaked_key(self, merkle_root=b""): + e = self.even_secret() + # get the tweak from the point's tweak method + tweak = self.point.tweak(merkle_root) + # t is the tweak interpreted as big endian + t = big_endian_to_int(tweak) + # new secret is the secret plus t (make sure to mod by N) + new_secret = (e + t) % N + # create a new instance of this class using self.__class__ return self.__class__(new_secret, network=self.network) @classmethod diff --git a/buidl/compactfilter.py b/buidl/compactfilter.py index b53f39f..4659f97 100644 --- a/buidl/compactfilter.py +++ b/buidl/compactfilter.py @@ -254,7 +254,6 @@ def parse(cls, s): class GetCFCheckPointMessage: - command = b"getcfcheckpt" define_network = False diff --git a/buidl/descriptor.py b/buidl/descriptor.py index 5f6ca0c..40c5dd3 100644 --- a/buidl/descriptor.py +++ b/buidl/descriptor.py @@ -151,7 +151,6 @@ def parse_any_key_record(key_record_str): class P2WSHSortedMulti: - # TODO: make an inheritable base descriptor class that this inherits from def __init__( diff --git a/buidl/hd.py b/buidl/hd.py index 9585ad1..1c23de6 100644 --- a/buidl/hd.py +++ b/buidl/hd.py @@ -332,9 +332,8 @@ def get_private_key(self, purpose, account_num=0, is_external=True, address_num= return hd_priv.private_key def _get_address(self, purpose, account_num=0, is_external=True, address_num=0): - """Returns the proper address among purposes 44', 49' and 84'. - p2pkh for 44', p2sh-p2wpkh for 49' and p2wpkh for 84'.""" - # if purpose is not one of 44', 49' or 84', raise ValueError + """Returns the proper address among purposes 44', 49', 84' and 86'. + p2pkh for 44', p2sh-p2wpkh for 49', p2wpkh for 84', and p2tr for 86'.""" point = self.get_private_key( purpose=purpose, account_num=account_num, @@ -353,6 +352,7 @@ def _get_address(self, purpose, account_num=0, is_external=True, address_num=0): # if 86', return the p2tr_address elif purpose == "86'": return point.p2tr_address(network=self.network) + # if purpose is not one of 44', 49', 84' or 86', raise ValueError else: raise ValueError( f"Cannot create an address without a proper purpose: {purpose}" diff --git a/buidl/libsec_build.py b/buidl/libsec_build.py old mode 100644 new mode 100755 index 95851ce..43c349e --- a/buidl/libsec_build.py +++ b/buidl/libsec_build.py @@ -13,5 +13,10 @@ ffi = FFI() ffi.cdef(source) -ffi.set_source("_libsec", header, libraries=["secp256k1"]) +ffi.set_source( + "_libsec", + header, + libraries=["secp256k1"], + include_dirs=["/opt/homebrew/Cellar/libsecp256k1/0.1/include"], +) ffi.compile(verbose=True) diff --git a/buidl/mnemonic.py b/buidl/mnemonic.py index de7180e..a95fc4f 100644 --- a/buidl/mnemonic.py +++ b/buidl/mnemonic.py @@ -21,7 +21,7 @@ def secure_mnemonic(num_bits=256, extra_entropy=0): """ if num_bits not in (128, 160, 192, 224, 256): raise ValueError(f"Invalid num_bits: {num_bits}") - if type(extra_entropy) is not int: + if not isinstance(extra_entropy, int): raise TypeError(f"extra_entropy must be an int: {extra_entropy}") if extra_entropy < 0: raise ValueError(f"extra_entropy cannot be negative: {extra_entropy}") @@ -115,9 +115,9 @@ def __init__(self, filename, num_words): self.lookup[word[:4]] = i def __getitem__(self, key): - if type(key) == str: + if isinstance(key, str): return self.lookup[key] - elif type(key) == int: + elif isinstance(key, int): return self.words[key] else: raise KeyError("key needs to be a str or int") diff --git a/buidl/op.py b/buidl/op.py index b89a66e..f97b20a 100644 --- a/buidl/op.py +++ b/buidl/op.py @@ -748,7 +748,7 @@ def op_checksig_schnorr(stack, tx_obj, input_index): return False pubkey = stack.pop() signature = stack.pop() - point = S256Point.parse_bip340(pubkey) + point = S256Point.parse_xonly(pubkey) if len(signature) == 65: hash_type = signature[-1] signature = signature[:-1] @@ -777,7 +777,7 @@ def op_checksigadd_schnorr(stack, tx_obj, input_index): pubkey = stack.pop() n = decode_num(stack.pop()) signature = stack.pop() - point = S256Point.parse_bip340(pubkey) + point = S256Point.parse_xonly(pubkey) if len(signature) == 65: hash_type = signature[-1] signature = signature[:-1] diff --git a/buidl/pecc.py b/buidl/pecc.py index 1e794ac..fdce158 100644 --- a/buidl/pecc.py +++ b/buidl/pecc.py @@ -7,6 +7,7 @@ hash_aux, hash_challenge, hash_nonce, + hash_taptweak, ) from buidl.helper import ( big_endian_to_int, @@ -216,7 +217,7 @@ def sqrt(self): class S256Point(Point): def __init__(self, x, y, a=None, b=None): a, b = S256Field(A), S256Field(B) - if type(x) == int: + if isinstance(x, int): super().__init__(x=S256Field(x), y=S256Field(y), a=a, b=b) else: super().__init__(x=x, y=y, a=a, b=b) @@ -243,11 +244,17 @@ def __rmul__(self, coefficient): def __add__(self, other): """If other is an int, multiplies scalar by generator, adds result to current point""" - if type(other) == int: + if isinstance(other, int): return super().__add__(other * G) else: return super().__add__(other) + def even_point(self): + if self.parity: + return -1 * self + else: + return self + def sec(self, compressed=True): # returns the binary version of the sec format, NOT hex # if compressed, starts with b'\x02' if self.y.num is even, b'\x03' if self.y is odd @@ -264,12 +271,30 @@ def sec(self, compressed=True): y = int_to_big_endian(self.y.num, 32) return b"\x04" + x + y - def bip340(self): - # returns the binary version of BIP340 pubkey + def xonly(self): + # returns the binary version of XONLY pubkey if self.x is None: return int_to_big_endian(0, 32) return int_to_big_endian(self.x.num, 32) + def tweak(self, merkle_root=b""): + """returns the tweak for use in p2tr if there's no script path""" + # take the hash_taptweak of the xonly and the merkle root + tweak = hash_taptweak(self.xonly() + merkle_root) + return tweak + + def tweaked_key(self, merkle_root=b"", tweak=None): + """Creates the tweaked external key for a particular tweak.""" + # Get the tweak with the merkle root + if tweak is None: + tweak = self.tweak(merkle_root) + # t is the tweak interpreted as a big endian integer + t = big_endian_to_int(tweak) + # Q = P + tG + external_key = self.even_point() + t + # return the external key + return external_key + def hash160(self, compressed=True): # get the sec sec = self.sec(compressed) @@ -296,12 +321,13 @@ def p2sh_p2wpkh_redeem_script(self): """Returns the RedeemScript for a p2sh-p2wpkh redemption""" return self.p2wpkh_script().redeem_script() - def p2tr_script(self): + def p2tr_script(self, merkle_root=b"", tweak=None): """Returns the p2tr Script object""" + external_pubkey = self.tweaked_key(merkle_root, tweak) # avoid circular dependency - from buidl.taproot import TapRoot + from buidl.script import P2TRScriptPubKey - return TapRoot(self).script_pubkey() + return P2TRScriptPubKey(external_pubkey) def p2pk_tap_script(self): """Returns the p2tr Script object""" @@ -322,9 +348,9 @@ def p2sh_p2wpkh_address(self, network="mainnet"): """Returns the p2sh-p2wpkh base58 address string""" return self.p2wpkh_script().p2sh_address(network) - def p2tr_address(self, network="mainnet"): + def p2tr_address(self, merkle_root=b"", tweak=None, network="mainnet"): """Returns the p2tr bech32m address string""" - return self.p2tr_script().address(network) + return self.p2tr_script(merkle_root, tweak).address(network) def verify(self, z, sig): # remember sig.r and sig.s are the main things we're checking @@ -355,20 +381,20 @@ def verify_schnorr(self, msg, schnorr_sig): point = self if schnorr_sig.r.x is None: return False - message = schnorr_sig.r.bip340() + point.bip340() + msg + message = schnorr_sig.r.xonly() + point.xonly() + msg challenge = big_endian_to_int(hash_challenge(message)) % N result = -challenge * point + schnorr_sig.s if result.x is None: return False if result.parity: return False - return result.bip340() == schnorr_sig.r.bip340() + return result.xonly() == schnorr_sig.r.xonly() @classmethod def parse(cls, binary): - """returns a Point object from a SEC or BIP340 pubkey""" + """returns a Point object from a SEC or XONLY pubkey""" if len(binary) == 32: - return cls.parse_bip340(binary) + return cls.parse_xonly(binary) elif len(binary) in (33, 65): return cls.parse_sec(binary) else: @@ -399,9 +425,9 @@ def parse_sec(cls, sec_bin): return cls(x, odd_beta) @classmethod - def parse_bip340(cls, bip340_bin): - """returns a Point object from a BIP340 pubkey""" - n = big_endian_to_int(bip340_bin) + def parse_xonly(cls, xonly_bin): + """returns a Point object from a XONLY pubkey""" + n = big_endian_to_int(xonly_bin) if n == 0: # point at infinity return cls(None, None) @@ -498,7 +524,7 @@ def __eq__(self, other): return self.r == other.r and self.s == other.s def serialize(self): - return self.r.bip340() + int_to_big_endian(self.s, 32) + return self.r.xonly() + int_to_big_endian(self.s, 32) @classmethod def parse(cls, signature_bin): @@ -537,28 +563,55 @@ def sign(self, z): # Signature(r, s) return Signature(r, s) - def sign_schnorr(self, msg, aux): + def even_secret(self): if self.point.parity: - d = N - self.secret + return N - self.secret else: - d = self.secret + return self.secret + + def bip340_k(self, msg, aux=None): + # k is generated using the aux variable, which can be set + # to a known value to make k deterministic + # the idea of k generation here is to mix in the private key + # and the msg to ensure it's unique and not reused + if aux is None: + aux = b"\x00" * 32 + e = self.even_secret() if len(msg) != 32: raise ValueError("msg needs to be 32 bytes") if len(aux) != 32: raise ValueError("aux needs to be 32 bytes") - t = xor_bytes(int_to_big_endian(d, 32), hash_aux(aux)) - k = big_endian_to_int(hash_nonce(t + self.point.bip340() + msg)) % N + # t contains the secret, msg is added so it's unique to the + # message and private key + t = xor_bytes(int_to_big_endian(e, 32), hash_aux(aux)) + return big_endian_to_int(hash_nonce(t + self.point.xonly() + msg)) % N + + def sign_schnorr(self, msg, aux=None): + # e is the secret that generates an even y with the even_secret method + e = self.even_secret() + # get k using the self.bip340_k method + k = self.bip340_k(msg, aux) + # get the resulting R=kG point r = k * G + # if R's y coordinate is odd (use parity property), flip the k if r.parity: + # set k to N - k k = N - k + # recalculate R r = k * G - message = r.bip340() + self.point.bip340() + msg - e = big_endian_to_int(hash_challenge(message)) % N - s = (k + e * d) % N - sig = SchnorrSignature(r, s) - if not self.point.verify_schnorr(msg, sig): + # calculate the commitment which is: R || P || msg + commitment = r.xonly() + self.point.xonly() + msg + # h is hash_challenge of the commitment as a big endian integer mod N + h = big_endian_to_int(hash_challenge(commitment)) % N + # calculate s which is (k+eh) mod N + s = (k + e * h) % N + # create a SchnorrSignature object using the R and s + schnorr = SchnorrSignature(r, s) + # check that this schnorr signature verifies + if not self.point.verify_schnorr(msg, schnorr): raise RuntimeError("Bad Signature") - return sig + # return the signature + return schnorr def deterministic_k(self, z): k = b"\x00" * 32 @@ -607,12 +660,15 @@ def wif(self, compressed=True): # encode_base58_checksum the whole thing return encode_base58_checksum(prefix + secret_bytes + suffix) - def tweaked(self, tweak): - if self.point.parity: - s = N - self.secret - else: - s = self.secret - new_secret = (s + tweak) % N + def tweaked_key(self, merkle_root=b""): + e = self.even_secret() + # get the tweak from the point's tweak method + tweak = self.point.tweak(merkle_root) + # t is the tweak interpreted as big endian + t = big_endian_to_int(tweak) + # new secret is the secret plus t (make sure to mod by N) + new_secret = (e + t) % N + # create a new instance of this class using self.__class__ return self.__class__(new_secret, network=self.network) @classmethod diff --git a/buidl/psbt.py b/buidl/psbt.py index eace3af..34485c0 100644 --- a/buidl/psbt.py +++ b/buidl/psbt.py @@ -665,7 +665,6 @@ def replace_root_xfps(self, xfp_map): raise ValueError(f"xfp_hex {xfp_to_hide} not found in psbt") def _describe_basic_multisig_inputs(self, hdpubkey_map): - # These will be used for all inputs and change outputs inputs_quorum_m, inputs_quorum_n = None, None @@ -791,7 +790,6 @@ def _describe_basic_multisig_outputs( expected_quorum_n, hdpubkey_map={}, ): - # intialize variable we'll loop through to set outputs_desc = [] spend_addr, spend_sats = "", 0 @@ -1608,7 +1606,7 @@ def finalize(self): # for each command in the RedeemScript for command in self.redeem_script.commands: # skip if the command is an integer - if type(command) == int: + if isinstance(command, int): continue # grab the sig for the pubkey sig = self.sigs.get(command) diff --git a/buidl/psbt_helper.py b/buidl/psbt_helper.py index 3d405d3..6fff0f0 100644 --- a/buidl/psbt_helper.py +++ b/buidl/psbt_helper.py @@ -80,7 +80,6 @@ def create_multisig_psbt( tx_ins, total_input_sats = [], 0 for cnt, input_dict in enumerate(input_dicts): - # Prev tx stuff prev_tx_dict = input_dict["prev_tx_dict"] prev_tx_obj = Tx.parse_hex(prev_tx_dict["hex"], network=network) diff --git a/buidl/script.py b/buidl/script.py index 9494163..0952b1e 100644 --- a/buidl/script.py +++ b/buidl/script.py @@ -37,7 +37,7 @@ def __init__(self, commands=None): def __repr__(self): result = "" for command in self.commands: - if type(command) == int: + if isinstance(command, int): if OP_CODE_NAMES.get(command): name = OP_CODE_NAMES.get(command) else: @@ -123,7 +123,7 @@ def raw_serialize(self): # go through each command for command in self.commands: # if the command is an integer, it's an op code - if type(command) == int: + if isinstance(command, int): # turn the command into a single byte integer using int_to_byte result += int_to_byte(command) else: @@ -166,7 +166,7 @@ def evaluate(self, tx_obj, input_index): op_lookup = OP_CODE_FUNCTIONS while len(commands) > 0: command = commands.pop(0) - if type(command) == int: + if isinstance(command, int): # do what the op code says operation = op_lookup[command] if command in (99, 100): @@ -198,7 +198,7 @@ def evaluate(self, tx_obj, input_index): if ( len(commands) == 3 and commands[0] == 0xA9 - and type(commands[1]) == bytes + and isinstance(commands[1], bytes) and len(commands[1]) == 20 and commands[2] == 0x87 ): @@ -260,13 +260,13 @@ def evaluate(self, tx_obj, input_index): elif len(witness) > 1: # this is a script path spend control_block = witness.control_block() - tap_leaf = witness.tap_leaf() - tweak_point = control_block.tweak_point(tap_leaf) + tap_script = witness.tap_script() + tweak_point = control_block.external_pubkey(tap_script) # the tweak point should be what's on the stack if tweak_point.parity != control_block.parity: print("bad tweak point parity") return False - if tweak_point.bip340() != stack.pop(): + if tweak_point.xonly() != stack.pop(): print("bad tweak point") return False # pop off the 1 and start fresh @@ -290,7 +290,7 @@ def is_p2pkh(self): len(self.commands) == 5 and self.commands[0] == 0x76 and self.commands[1] == 0xA9 - and type(self.commands[2]) == bytes + and isinstance(self.commands[2], bytes) and len(self.commands[2]) == 20 and self.commands[3] == 0x88 and self.commands[4] == 0xAC @@ -304,7 +304,7 @@ def is_p2sh(self): return ( len(self.commands) == 3 and self.commands[0] == 0xA9 - and type(self.commands[1]) == bytes + and isinstance(self.commands[1], bytes) and len(self.commands[1]) == 20 and self.commands[2] == 0x87 ) @@ -315,7 +315,7 @@ def is_p2wpkh(self): return ( len(self.commands) == 2 and self.commands[0] == 0x00 - and type(self.commands[1]) == bytes + and isinstance(self.commands[1], bytes) and len(self.commands[1]) == 20 ) @@ -325,7 +325,7 @@ def is_p2wsh(self): return ( len(self.commands) == 2 and self.commands[0] == 0x00 - and type(self.commands[1]) == bytes + and isinstance(self.commands[1], bytes) and len(self.commands[1]) == 32 ) @@ -335,7 +335,7 @@ def is_p2tr(self): return ( len(self.commands) == 2 and self.commands[0] == 0x51 - and type(self.commands[1]) == bytes + and isinstance(self.commands[1], bytes) and len(self.commands[1]) == 32 ) @@ -376,7 +376,7 @@ def redeem_script(self): class P2PKHScriptPubKey(ScriptPubKey): def __init__(self, h160): super().__init__() - if type(h160) != bytes: + if not isinstance(h160, bytes): raise TypeError("To initialize P2PKHScriptPubKey, a hash160 is needed") self.commands = [0x76, 0xA9, h160, 0x88, 0xAC] @@ -395,7 +395,7 @@ def address(self, network="mainnet"): class P2SHScriptPubKey(ScriptPubKey): def __init__(self, h160): super().__init__() - if type(h160) != bytes: + if not isinstance(h160, bytes): raise TypeError("To initialize P2SHScriptPubKey, a hash160 is needed") self.commands = [0xA9, h160, 0x87] @@ -516,7 +516,7 @@ def p2sh_address(self, network="mainnet"): class P2WPKHScriptPubKey(SegwitPubKey): def __init__(self, h160): super().__init__() - if type(h160) != bytes: + if not isinstance(h160, bytes): raise TypeError("To initialize P2WPKHScriptPubKey, a hash160 is needed") self.commands = [0x00, h160] @@ -524,7 +524,7 @@ def __init__(self, h160): class P2WSHScriptPubKey(SegwitPubKey): def __init__(self, s256): super().__init__() - if type(s256) != bytes: + if not isinstance(s256, bytes): raise TypeError("To initialize P2WSHScriptPubKey, a sha256 is needed") self.commands = [0x00, s256] @@ -532,9 +532,9 @@ def __init__(self, s256): class P2TRScriptPubKey(ScriptPubKey): def __init__(self, point): super().__init__() - if type(point) == S256Point: - raw_point = point.bip340() - elif type(point) == bytes: + if isinstance(point, S256Point): + raw_point = point.xonly() + elif isinstance(point, bytes): raw_point = point else: raise TypeError( @@ -586,8 +586,8 @@ def p2sh_address(self, network="mainnet"): def is_p2wsh_multisig(self): return ( OP_CODE_NAMES[self.commands[-1]] == "OP_CHECKMULTISIG" - and type(self.commands[0]) == int - and type(self.commands[-2]) == int + and isinstance(self.commands[0], int) + and isinstance(self.commands[-2], int) ) def get_quorum(self): diff --git a/buidl/shamir.py b/buidl/shamir.py index 355be9e..703329b 100644 --- a/buidl/shamir.py +++ b/buidl/shamir.py @@ -339,7 +339,8 @@ def split_secret(cls, secret, k, n): @classmethod def generate_shares(cls, mnemonic, k, n, passphrase=b"", exponent=0): """Takes a BIP39 mnemonic along with k, n, passphrase and exponent. - Returns a list of SLIP39 mnemonics, any k of of which, along with the passphrase, recover the secret""" + Returns a list of SLIP39 mnemonics, any k of of which, along with the passphrase, recover the secret + """ # convert mnemonic to a shared secret secret = mnemonic_to_bytes(mnemonic) num_bits = len(secret) * 8 diff --git a/buidl/taproot.py b/buidl/taproot.py index 20db811..5d08d9f 100644 --- a/buidl/taproot.py +++ b/buidl/taproot.py @@ -9,7 +9,6 @@ hash_musignonce, hash_tapbranch, hash_tapleaf, - hash_taptweak, ) from buidl.helper import ( big_endian_to_int, @@ -20,24 +19,26 @@ encode_minimal_num, number_to_op_code, ) -from buidl.script import ScriptPubKey, P2TRScriptPubKey +from buidl.script import Script, ScriptPubKey from buidl.timelock import Locktime, Sequence def locktime_commands(locktime): - assert type(locktime) == Locktime, f"{locktime} needs to be Locktime" + assert isinstance(locktime, Locktime), f"{locktime} needs to be Locktime" # 0xB1 is OP_CLTV, 0x75 is OP_DROP return [encode_minimal_num(locktime), 0xB1, 0x75] def sequence_commands(sequence): - assert type(sequence) == Sequence, f"{sequence} needs to be Sequence" + assert isinstance(sequence, Sequence), f"{sequence} needs to be Sequence" # 0xB2 is OP_CSV, 0x75 is OP_DROP return [encode_minimal_num(sequence), 0xB2, 0x75] class TapLeaf: def __init__(self, tap_script, tapleaf_version=0xC0): + if not isinstance(tap_script, Script): + raise ValueError("Script is required") self.tap_script = tap_script self.tapleaf_version = tapleaf_version @@ -46,7 +47,7 @@ def __repr__(self): def __eq__(self, other): return ( - type(self) == type(other) + type(self) is type(other) and self.tapleaf_version == other.tapleaf_version and self.tap_script == other.tap_script ) @@ -60,7 +61,23 @@ def leaves(self): return [self] def path_hashes(self, leaf): - return b"" + return [] + + def external_pubkey(self, internal_pubkey): + return internal_pubkey.tweaked_key(self.hash()) + + def control_block(self, internal_pubkey, tap_leaf=None): + """Assumes that this TapLeaf is the Merkle Root and constructs the + control block""" + if tap_leaf is not None and tap_leaf != self: + return None + external_pubkey = self.external_pubkey(internal_pubkey) + return ControlBlock( + self.tapleaf_version, + external_pubkey.parity, + internal_pubkey, + self.path_hashes(None), + ) class TapBranch: @@ -91,12 +108,28 @@ def leaves(self): def path_hashes(self, leaf): if leaf in self.left.leaves(): - return self.left.path_hashes(leaf) + self.right.hash() + return [*self.left.path_hashes(leaf), self.right.hash()] elif leaf in self.right.leaves(): - return self.right.path_hashes(leaf) + self.left.hash() + return [*self.right.path_hashes(leaf), self.left.hash()] else: return None + def external_pubkey(self, internal_pubkey): + return internal_pubkey.tweaked_key(self.hash()) + + def control_block(self, internal_pubkey, leaf): + """Assumes this TapBranch is the Merkle Root and returns the control + block. Also requires the leaf to be one of the descendents""" + if leaf not in self.leaves(): + return None + external_pubkey = self.external_pubkey(internal_pubkey) + return ControlBlock( + leaf.tapleaf_version, + external_pubkey.parity, + internal_pubkey, + self.path_hashes(leaf), + ) + @classmethod def combine(cls, nodes): if len(nodes) == 1: @@ -107,49 +140,6 @@ def combine(cls, nodes): return cls(left, right) -class TapRoot: - def __init__(self, internal_pubkey, tap_node=None, merkle_root=None): - self.internal_pubkey = S256Point.parse_bip340(internal_pubkey.bip340()) - self.tap_node = tap_node - if merkle_root is not None: - self.tweak = big_endian_to_int( - hash_taptweak(internal_pubkey.bip340() + merkle_root) - ) - elif tap_node is None: - self.tweak = big_endian_to_int(hash_taptweak(internal_pubkey.bip340())) - else: - self.tweak = big_endian_to_int( - hash_taptweak(internal_pubkey.bip340() + tap_node.hash()) - ) - self.tweak_point = self.internal_pubkey + self.tweak - self.parity = self.tweak_point.parity - - def address(self, network="mainnet"): - return self.script_pubkey().address(network=network) - - def bip340(self): - return self.tweak_point.bip340() - - def leaves(self): - if self.tap_node: - return self.tap_node.leaves() - else: - return [] - - def script_pubkey(self): - return P2TRScriptPubKey(self.tweak_point) - - def control_block(self, leaf): - if self.tap_node is None: - return None - if leaf not in self.tap_node.leaves(): - return None - raw = int_to_byte(leaf.tapleaf_version + self.tweak_point.parity) - raw += self.internal_pubkey.bip340() - raw += self.tap_node.path_hashes(leaf) - return ControlBlock.parse(raw) - - class ControlBlock: def __init__(self, tapleaf_version, parity, internal_pubkey, hashes): self.tapleaf_version = tapleaf_version @@ -163,24 +153,30 @@ def __repr__(self): def __eq__(self, other): return self.serialize() == other.serialize() - def merkle_root(self, leaf): + def merkle_root(self, tap_script): + # create a TapLeaf from the tap_script and the tapleaf version in the control block + leaf = TapLeaf(tap_script, self.tapleaf_version) + # initialize the hash with the leaf's hash current = leaf.hash() + # go through the hashes in self.hashes for h in self.hashes: + # set the current hash as the hash_tapbranch of the sorted hashes if current < h: current = hash_tapbranch(current + h) else: current = hash_tapbranch(h + current) + # return the current hash return current - def tweak(self, leaf): - return hash_taptweak(self.internal_pubkey.bip340() + self.merkle_root(leaf)) - - def tweak_point(self, leaf): - return self.internal_pubkey + big_endian_to_int(self.tweak(leaf)) + def external_pubkey(self, tap_script): + # get the Merkle Root using self.merkle_root + merkle_root = self.merkle_root(tap_script) + # return the external pubkey using the tweaked_key method of internal pubkey + return self.internal_pubkey.tweaked_key(merkle_root) def serialize(self): s = int_to_byte(self.tapleaf_version + self.parity) - s += self.internal_pubkey.bip340() + s += self.internal_pubkey.xonly() for h in self.hashes: s += h return s @@ -194,7 +190,7 @@ def parse(cls, b): raise ValueError(f"length is outside the bounds {b_len}") tapleaf_version = b[0] & 0xFE parity = b[0] & 1 - internal_pubkey = S256Point.parse_bip340(b[1:33]) + internal_pubkey = S256Point.parse_xonly(b[1:33]) m = (b_len - 33) // 32 hashes = [b[33 + 32 * i : 65 + 32 * i] for i in range(m)] return cls(tapleaf_version, parity, internal_pubkey, hashes) @@ -208,9 +204,9 @@ def tap_leaf(self): class P2PKTapScript(TapScript): def __init__(self, point): super().__init__() - if type(point) == S256Point: - raw_point = point.bip340() - elif type(point) == bytes: + if isinstance(point, S256Point): + raw_point = point.xonly() + elif isinstance(point, bytes): raw_point = point else: raise TypeError("To initialize P2PKTapScript, a point is needed") @@ -226,18 +222,18 @@ def __init__(self, points, k, locktime=None, sequence=None): super().__init__() if len(points) == 0: raise ValueError("To initialize MultiSigTapScript at least one point") - bip340s = sorted([p.bip340() for p in points]) - self.points = [S256Point.parse_bip340(b) for b in bip340s] + xonlys = sorted([p.xonly() for p in points]) + self.points = [S256Point.parse_xonly(b) for b in xonlys] if locktime is not None: self.commands = locktime_commands(locktime) elif sequence is not None: self.commands = sequence_commands(sequence) else: self.commands = [] - self.commands += [bip340s[0], 0xAC] + self.commands += [xonlys[0], 0xAC] if len(points) > 1: - for bip340 in bip340s[1:]: - self.commands += [bip340, 0xBA] + for xonly in xonlys[1:]: + self.commands += [xonly, 0xBA] self.commands += [number_to_op_code(k), 0x87] @@ -250,15 +246,15 @@ def __init__(self, points, locktime=None, sequence=None): super().__init__() if len(points) == 0: raise ValueError("Need at least one public key") - bip340s = sorted([p.bip340() for p in points]) - self.points = [S256Point.parse_bip340(b) for b in bip340s] - self.commitment = hash_keyagglist(b"".join(bip340s)) + xonlys = sorted([p.xonly() for p in points]) + self.points = [S256Point.parse_xonly(b) for b in xonlys] + self.commitment = hash_keyagglist(b"".join(xonlys)) self.coefs = [ - big_endian_to_int(hash_keyaggcoef(self.commitment + b)) for b in bip340s + big_endian_to_int(hash_keyaggcoef(self.commitment + b)) for b in xonlys ] # the second unique public key has a coefficient of 1 self.coefs[1] = 1 - self.coef_lookup = {b: c for c, b in zip(self.coefs, bip340s)} + self.coef_lookup = {b: c for c, b in zip(self.coefs, xonlys)} # aggregate point self.point = S256Point.combine([c * p for c, p in zip(self.coefs, self.points)]) if locktime is not None: @@ -267,13 +263,7 @@ def __init__(self, points, locktime=None, sequence=None): self.commands = sequence_commands(sequence) else: self.commands = [] - self.commands += [self.point.bip340(), 0xAC] - - def get_tweak_point(self, tweak): - if self.point.parity: - return -1 * self.point + tweak - else: - return self.point + tweak + self.commands += [self.point.xonly(), 0xAC] def generate_nonces(self): k_1, k_2 = randbelow(N), randbelow(N) @@ -287,7 +277,7 @@ def nonce_sums(self, nonce_point_pairs): def compute_coefficient(self, nonce_sums, sig_hash): bytes_to_hash = ( - nonce_sums[0].sec() + nonce_sums[1].sec() + self.point.bip340() + sig_hash + nonce_sums[0].sec() + nonce_sums[1].sec() + self.point.xonly() + sig_hash ) return big_endian_to_int(hash_musignonce(bytes_to_hash)) @@ -299,13 +289,16 @@ def compute_r(self, nonce_sums, sig_hash): h = self.compute_coefficient(nonce_sums, sig_hash) return S256Point.combine([nonce_sums[0], h * nonce_sums[1]]) - def sign(self, private_key, k, r, sig_hash, tweak=0): - tweak_point = self.get_tweak_point(tweak) - msg = r.bip340() + tweak_point.bip340() + sig_hash + def sign(self, private_key, k, r, sig_hash, merkle_root=b""): + if merkle_root: + external_pubkey = self.point.tweaked_key(merkle_root) + else: + external_pubkey = self.point.even_point() + msg = r.xonly() + external_pubkey.xonly() + sig_hash challenge = big_endian_to_int(hash_challenge(msg)) % N - h_i = self.coef_lookup[private_key.point.bip340()] + h_i = self.coef_lookup[private_key.point.xonly()] c_i = h_i * challenge % N - if r.parity == tweak_point.parity: + if r.parity == external_pubkey.parity: k_real = k else: k_real = -k @@ -315,21 +308,22 @@ def sign(self, private_key, k, r, sig_hash, tweak=0): secret = -private_key.secret return (k_real + c_i * secret) % N - def get_signature(self, s_sum, r, sig_hash, tweak=0): - tweak_point = self.get_tweak_point(tweak) - if tweak: - msg = r.bip340() + tweak_point.bip340() + sig_hash + def get_signature(self, s_sum, r, sig_hash, merkle_root=b""): + if merkle_root: + external_pubkey = self.point.tweaked_key(merkle_root) + tweak = big_endian_to_int(self.point.tweak(merkle_root)) + msg = r.xonly() + external_pubkey.xonly() + sig_hash challenge = big_endian_to_int(hash_challenge(msg)) % N - if tweak_point.parity: + if external_pubkey.parity: s = (-s_sum - challenge * tweak) % N else: s = (s_sum + challenge * tweak) % N else: + external_pubkey = self.point.even_point() s = s_sum % N - s_raw = int_to_big_endian(s, 32) - sig = r.bip340() + s_raw - schnorrsig = SchnorrSignature.parse(sig) - if not tweak_point.verify_schnorr(sig_hash, schnorrsig): + serialized = r.xonly() + int_to_big_endian(s, 32) + schnorrsig = SchnorrSignature.parse(serialized) + if not external_pubkey.verify_schnorr(sig_hash, schnorrsig): raise ValueError("Invalid signature") return schnorrsig @@ -349,66 +343,42 @@ def single_leaf(self, locktime=None, sequence=None): ) return tap_script.tap_leaf() - def single_leaf_tap_root(self, internal_pubkey=None, locktime=None, sequence=None): - if internal_pubkey is None: - internal_pubkey = self.default_internal_pubkey - return TapRoot( - internal_pubkey, self.single_leaf(locktime=locktime, sequence=sequence) - ) - - def multi_leaf_tap_node(self, locktime=None, sequence=None): + def multi_leaf_tree(self, locktime=None, sequence=None): leaves = [] for pubkeys in combinations(self.points, self.k): tap_script = MultiSigTapScript(pubkeys, self.k, locktime, sequence) leaves.append(tap_script.tap_leaf()) return TapBranch.combine(leaves) - def multi_leaf_tap_root(self, internal_pubkey=None, locktime=None, sequence=None): - if internal_pubkey is None: - internal_pubkey = self.default_internal_pubkey - return TapRoot( - internal_pubkey, - self.multi_leaf_tap_node(locktime=locktime, sequence=sequence), - ) - - def musig_tap_node(self, locktime=None, sequence=None): + def musig_tree(self, locktime=None, sequence=None): leaves = [] for pubkeys in combinations(self.points, self.k): tap_script = MuSigTapScript(pubkeys, locktime=locktime, sequence=sequence) leaves.append(tap_script.tap_leaf()) return TapBranch.combine(leaves) - def musig_tap_root(self, internal_pubkey=None, locktime=None, sequence=None): - if internal_pubkey is None: - internal_pubkey = self.default_internal_pubkey - return TapRoot( - internal_pubkey, self.musig_tap_node(locktime=locktime, sequence=sequence) - ) - - def musig_and_single_leaf_tap_root( + def musig_and_single_leaf_tree( self, internal_pubkey=None, locktime=None, sequence=None ): if internal_pubkey is None: internal_pubkey = self.default_internal_pubkey - node = TapBranch( + return TapBranch( self.single_leaf(locktime=locktime, sequence=sequence), - self.musig_tap_node(locktime=locktime, sequence=sequence), + self.musig_tree(locktime=locktime, sequence=sequence), ) - return TapRoot(internal_pubkey, node) - def everything_tap_root(self, internal_pubkey=None, locktime=None, sequence=None): + def everything_tree(self, internal_pubkey=None, locktime=None, sequence=None): if internal_pubkey is None: internal_pubkey = self.default_internal_pubkey - node = TapBranch( + return TapBranch( self.single_leaf(locktime=locktime, sequence=sequence), TapBranch( - self.multi_leaf_tap_node(locktime=locktime, sequence=sequence), - self.musig_tap_node(locktime=locktime, sequence=sequence), + self.multi_leaf_tree(locktime=locktime, sequence=sequence), + self.musig_tree(locktime=locktime, sequence=sequence), ), ) - return TapRoot(internal_pubkey, node) - def degrading_multisig_tap_node( + def degrading_multisig_tree( self, sequence_block_interval=None, sequence_time_interval=None ): """Can unlock with multisig as k-of-n, or (k-1)-of-n after a @@ -434,19 +404,3 @@ def degrading_multisig_tap_node( ) leaves.append(tap_script.tap_leaf()) return TapBranch.combine(leaves) - - def degrading_multisig_tap_root( - self, - internal_pubkey=None, - sequence_block_interval=None, - sequence_time_interval=None, - ): - if internal_pubkey is None: - internal_pubkey = self.default_internal_pubkey - return TapRoot( - internal_pubkey, - self.degrading_multisig_tap_node( - sequence_block_interval=sequence_block_interval, - sequence_time_interval=sequence_time_interval, - ), - ) diff --git a/buidl/test/test_ecc.py b/buidl/test/test_ecc.py index 260fc43..ef9653c 100644 --- a/buidl/test/test_ecc.py +++ b/buidl/test/test_ecc.py @@ -1,3 +1,5 @@ +from os import urandom +from random import randint from unittest import TestCase from buidl.ecc import G, N, S256Point, PrivateKey, Signature, SchnorrSignature @@ -5,8 +7,6 @@ from buidl.hash import hash_challenge from buidl.helper import big_endian_to_int, int_to_big_endian -from random import randint - class S256Test(TestCase): def test_pubpoint(self): @@ -212,24 +212,25 @@ def test_sign_schnorr(self): msg = int_to_big_endian(randint(1, N), 32) sig = pk.sign_schnorr(msg, aux=b"\x00" * 32) self.assertTrue(pk.point.verify_schnorr(msg, sig)) - # tweak - tweak = randint(1, N) - tweak_point = pk.tweaked(tweak).point + # merkle root + merkle_root = urandom(32) + tweak = big_endian_to_int(pk.point.tweak(merkle_root)) + external_pubkey = pk.tweaked_key(merkle_root).point k = randint(1, N) r = k * G if r.parity: k = N - k r = k * G - message = r.bip340() + tweak_point.bip340() + msg + message = r.xonly() + external_pubkey.xonly() + msg challenge = big_endian_to_int(hash_challenge(message)) % N - if pk.point.parity == tweak_point.parity: + if pk.point.parity == external_pubkey.parity: secret = pk.secret else: secret = -pk.secret s = (k + challenge * secret) % N - if tweak_point.parity: + if external_pubkey.parity: s = (s - challenge * tweak) % N else: s = (s + challenge * tweak) % N - sig = SchnorrSignature.parse(r.bip340() + int_to_big_endian(s, 32)) - self.assertTrue(tweak_point.verify_schnorr(msg, sig)) + sig = SchnorrSignature.parse(r.xonly() + int_to_big_endian(s, 32)) + self.assertTrue(external_pubkey.verify_schnorr(msg, sig)) diff --git a/buidl/test/test_musig.py b/buidl/test/test_musig.py index 4772bc7..05fedd6 100644 --- a/buidl/test/test_musig.py +++ b/buidl/test/test_musig.py @@ -89,7 +89,7 @@ def test_musig2_compute_r(self): self.assertEqual(nonce_sums[1], r_2_sum) self.assertEqual(musig.compute_coefficient(nonce_sums, msg), test[-2]) r = musig.compute_r(nonce_sums, msg) - self.assertEqual(r.bip340().hex(), want) + self.assertEqual(r.xonly().hex(), want) def test_musig2_point_aggregation(self): tests = [ @@ -144,9 +144,9 @@ def test_musig2_point_aggregation(self): ] for test in tests: want = bytes.fromhex(test[-1]) - points = [S256Point.parse_bip340(bytes.fromhex(h)) for h in test[:-1]] + points = [S256Point.parse_xonly(bytes.fromhex(h)) for h in test[:-1]] tap_script = MuSigTapScript(points) - self.assertEqual(tap_script.point.bip340(), want) + self.assertEqual(tap_script.point.xonly(), want) def test_single_leaf_multisig(self): hd_priv_key = HDPrivateKey.from_mnemonic( @@ -158,10 +158,11 @@ def test_single_leaf_multisig(self): ] points = [priv.point for priv in private_keys] tr_multisig = TapRootMultiSig(points, 3) - tap_root_input = tr_multisig.single_leaf_tap_root() - leaf = tap_root_input.tap_node + internal_pubkey = tr_multisig.default_internal_pubkey + leaf = tr_multisig.single_leaf() + merkle_root = leaf.hash() self.assertEqual( - tap_root_input.address(network="signet"), + internal_pubkey.p2tr_address(merkle_root, network="signet"), "tb1ppjjz7akunmrldfsldkhe5m7vhyx58g85pv7pw79jcere9tl4huqqdqezny", ) prev_tx = bytes.fromhex( @@ -171,7 +172,7 @@ def test_single_leaf_multisig(self): for prev_index in range(11): tx_in = TxIn(prev_tx, prev_index) tx_in._value = 1000000 - tx_in._script_pubkey = tap_root_input.script_pubkey() + tx_in._script_pubkey = internal_pubkey.p2tr_script(merkle_root) tx_ins.append(tx_in) change_script = address_to_script_pubkey( "tb1q7kn55vf3mmd40gyj46r245lw87dc6us5n50lrg" @@ -184,8 +185,8 @@ def test_single_leaf_multisig(self): tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), - tap_root_input.tap_node.tap_script, + leaf.control_block(internal_pubkey, leaf), + leaf.tap_script, ) sigs = [] for priv in private_keys: @@ -211,8 +212,8 @@ def test_single_leaf_multisig(self): s_sum = 0 for nonce_secrets, priv in zip(nonce_secret_pairs, private_keys): k = musig.compute_k(nonce_secrets, nonce_sums, sig_hash) - s_sum += musig.sign(priv, k, r, sig_hash, tweak=tap_root_input.tweak) - schnorr = musig.get_signature(s_sum, r, sig_hash, tap_root_input.tweak) + s_sum += musig.sign(priv, k, r, sig_hash, merkle_root) + schnorr = musig.get_signature(s_sum, r, sig_hash, merkle_root) tx_in.finalize_p2tr_keypath(schnorr.serialize()) self.assertTrue(tx_obj.verify_input(input_index)) self.assertEqual(tx_obj.vbytes(), tx_obj.fee()) @@ -229,10 +230,11 @@ def test_single_leaf_multisig_locktime(self): points = [priv.point for priv in private_keys] tr_multisig = TapRootMultiSig(points, 3) current_locktime = Locktime(1643332867) - tap_root_input = tr_multisig.single_leaf_tap_root(locktime=current_locktime) - leaf = tap_root_input.tap_node + internal_pubkey = tr_multisig.default_internal_pubkey + leaf = tr_multisig.single_leaf(locktime=current_locktime) + merkle_root = leaf.hash() self.assertEqual( - tap_root_input.address(network="signet"), + internal_pubkey.p2tr_address(merkle_root, network="signet"), "tb1pg67krjn69exwkdam3um0w5devyk4qs7eetzkm3twzfnqrk58xdlsyd62rg", ) prev_tx = bytes.fromhex( @@ -242,7 +244,7 @@ def test_single_leaf_multisig_locktime(self): for prev_index in range(11): tx_in = TxIn(prev_tx, prev_index, sequence=0xFFFFFFFE) tx_in._value = 1000000 - tx_in._script_pubkey = tap_root_input.script_pubkey() + tx_in._script_pubkey = internal_pubkey.p2tr_script(merkle_root) tx_ins.append(tx_in) change_script = address_to_script_pubkey( "tb1q7kn55vf3mmd40gyj46r245lw87dc6us5n50lrg" @@ -257,8 +259,8 @@ def test_single_leaf_multisig_locktime(self): tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), - tap_root_input.tap_node.tap_script, + leaf.control_block(internal_pubkey, leaf), + leaf.tap_script, ) sigs = [] for priv in private_keys: @@ -284,8 +286,8 @@ def test_single_leaf_multisig_locktime(self): s_sum = 0 for nonce_secrets, priv in zip(nonce_secret_pairs, private_keys): k = musig.compute_k(nonce_secrets, nonce_sums, sig_hash) - s_sum += musig.sign(priv, k, r, sig_hash, tweak=tap_root_input.tweak) - schnorr = musig.get_signature(s_sum, r, sig_hash, tap_root_input.tweak) + s_sum += musig.sign(priv, k, r, sig_hash, merkle_root) + schnorr = musig.get_signature(s_sum, r, sig_hash, merkle_root) tx_in.finalize_p2tr_keypath(schnorr.serialize()) self.assertTrue(tx_obj.verify_input(input_index)) self.assertEqual(tx_obj.vbytes(), tx_obj.fee()) @@ -296,8 +298,8 @@ def test_single_leaf_multisig_locktime(self): tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), - tap_root_input.tap_node.tap_script, + leaf.control_block(internal_pubkey, leaf), + leaf.tap_script, ) sigs = [] for priv in private_keys: @@ -320,10 +322,11 @@ def test_single_leaf_multisig_sequence(self): points = [priv.point for priv in private_keys] tr_multisig = TapRootMultiSig(points, 3) current_sequence = Sequence.from_relative_time(3000) - tap_root_input = tr_multisig.single_leaf_tap_root(sequence=current_sequence) - leaf = tap_root_input.tap_node + internal_pubkey = tr_multisig.default_internal_pubkey + leaf = tr_multisig.single_leaf(sequence=current_sequence) + merkle_root = leaf.hash() self.assertEqual( - tap_root_input.address(network="signet"), + internal_pubkey.p2tr_address(merkle_root, network="signet"), "tb1p64cjudufu6699ddtw767zwjepl3ktaax8mgkgrpc9trclghtv9dqmazesf", ) prev_tx = bytes.fromhex( @@ -333,7 +336,7 @@ def test_single_leaf_multisig_sequence(self): for prev_index in range(11): tx_in = TxIn(prev_tx, prev_index, sequence=current_sequence) tx_in._value = 1000000 - tx_in._script_pubkey = tap_root_input.script_pubkey() + tx_in._script_pubkey = internal_pubkey.p2tr_script(merkle_root) tx_ins.append(tx_in) change_script = address_to_script_pubkey( "tb1q7kn55vf3mmd40gyj46r245lw87dc6us5n50lrg" @@ -346,8 +349,8 @@ def test_single_leaf_multisig_sequence(self): tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), - tap_root_input.tap_node.tap_script, + leaf.control_block(internal_pubkey, leaf), + leaf.tap_script, ) sigs = [] for priv in private_keys: @@ -373,8 +376,8 @@ def test_single_leaf_multisig_sequence(self): s_sum = 0 for nonce_secrets, priv in zip(nonce_secret_pairs, private_keys): k = musig.compute_k(nonce_secrets, nonce_sums, sig_hash) - s_sum += musig.sign(priv, k, r, sig_hash, tweak=tap_root_input.tweak) - schnorr = musig.get_signature(s_sum, r, sig_hash, tap_root_input.tweak) + s_sum += musig.sign(priv, k, r, sig_hash, merkle_root) + schnorr = musig.get_signature(s_sum, r, sig_hash, merkle_root) tx_in.finalize_p2tr_keypath(schnorr.serialize()) self.assertTrue(tx_obj.verify_input(input_index)) self.assertEqual(tx_obj.vbytes(), tx_obj.fee()) @@ -385,8 +388,8 @@ def test_single_leaf_multisig_sequence(self): tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), - tap_root_input.tap_node.tap_script, + leaf.control_block(internal_pubkey, leaf), + leaf.tap_script, ) sigs = [] for priv in private_keys: @@ -407,10 +410,12 @@ def test_multi_leaf_multisig(self): hd_priv_key.get_p2tr_receiving_privkey(address_num=i) for i in range(5) ] points = [priv.point for priv in private_keys] - multisig = TapRootMultiSig(points, 3) - tap_root_input = multisig.multi_leaf_tap_root() + tr_multisig = TapRootMultiSig(points, 3) + internal_pubkey = tr_multisig.default_internal_pubkey + branch = tr_multisig.multi_leaf_tree() + merkle_root = branch.hash() self.assertEqual( - tap_root_input.address(network="signet"), + internal_pubkey.p2tr_address(merkle_root, network="signet"), "tb1pkyedped6jxzamm88nmf4wc22jaz4rqxnsh8h2kjz7t6fnjl87q5qdfjmwf", ) prev_tx = bytes.fromhex( @@ -420,7 +425,7 @@ def test_multi_leaf_multisig(self): for prev_index in range(11): tx_in = TxIn(prev_tx, prev_index) tx_in._value = 1000000 - tx_in._script_pubkey = tap_root_input.script_pubkey() + tx_in._script_pubkey = internal_pubkey.p2tr_script(merkle_root) tx_ins.append(tx_in) change_script = address_to_script_pubkey( "tb1q7kn55vf3mmd40gyj46r245lw87dc6us5n50lrg" @@ -432,9 +437,11 @@ def test_multi_leaf_multisig(self): tx_in = tx_ins[input_index] tx_in.witness.items = [] leaf = MultiSigTapScript(pubkeys, 3).tap_leaf() - self.assertTrue(tap_root_input.control_block(leaf)) + self.assertTrue(branch.control_block(internal_pubkey, leaf)) tx_obj.initialize_p2tr_multisig( - input_index, tap_root_input.control_block(leaf), leaf.tap_script + input_index, + branch.control_block(internal_pubkey, leaf), + leaf.tap_script, ) sigs = [] for priv in private_keys: @@ -458,8 +465,8 @@ def test_multi_leaf_multisig(self): s_sum = 0 for nonce_secrets, priv in zip(nonce_secret_pairs, private_keys): k = musig.compute_k(nonce_secrets, nonce_sums, sig_hash) - s_sum += musig.sign(priv, k, r, sig_hash, tweak=tap_root_input.tweak) - schnorr = musig.get_signature(s_sum, r, sig_hash, tap_root_input.tweak) + s_sum += musig.sign(priv, k, r, sig_hash, merkle_root) + schnorr = musig.get_signature(s_sum, r, sig_hash, merkle_root) tx_in.finalize_p2tr_keypath(schnorr.serialize()) self.assertTrue(tx_obj.verify_input(input_index)) self.assertEqual(tx_obj.vbytes(), tx_obj.fee()) @@ -475,10 +482,12 @@ def test_multi_leaf_multisig_locktime(self): ] points = [priv.point for priv in private_keys] tr_multisig = TapRootMultiSig(points, 3) + internal_pubkey = tr_multisig.default_internal_pubkey current_locktime = Locktime(74930) - tap_root_input = tr_multisig.multi_leaf_tap_root(locktime=current_locktime) + branch = tr_multisig.multi_leaf_tree(locktime=current_locktime) + merkle_root = branch.hash() self.assertEqual( - tap_root_input.address(network="signet"), + internal_pubkey.p2tr_address(merkle_root, network="signet"), "tb1p7glmm24y2gqsf58emquv6mzy9m6msnwtenj5s77df33t3cal3wkssw5320", ) prev_tx = bytes.fromhex( @@ -488,7 +497,7 @@ def test_multi_leaf_multisig_locktime(self): for prev_index in range(11): tx_in = TxIn(prev_tx, prev_index, sequence=0xFFFFFFFE) tx_in._value = 1000000 - tx_in._script_pubkey = tap_root_input.script_pubkey() + tx_in._script_pubkey = internal_pubkey.p2tr_script(merkle_root) tx_ins.append(tx_in) change_script = address_to_script_pubkey( "tb1q7kn55vf3mmd40gyj46r245lw87dc6us5n50lrg" @@ -500,12 +509,12 @@ def test_multi_leaf_multisig_locktime(self): ) for input_index, pubkeys in enumerate(combinations(points, 3)): leaf = MultiSigTapScript(pubkeys, 3, locktime=current_locktime).tap_leaf() - self.assertTrue(tap_root_input.control_block(leaf)) + self.assertTrue(branch.control_block(internal_pubkey, leaf)) tx_in = tx_ins[input_index] tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] @@ -532,8 +541,8 @@ def test_multi_leaf_multisig_locktime(self): s_sum = 0 for nonce_secrets, priv in zip(nonce_secret_pairs, private_keys): k = musig.compute_k(nonce_secrets, nonce_sums, sig_hash) - s_sum += musig.sign(priv, k, r, sig_hash, tweak=tap_root_input.tweak) - schnorr = musig.get_signature(s_sum, r, sig_hash, tap_root_input.tweak) + s_sum += musig.sign(priv, k, r, sig_hash, merkle_root) + schnorr = musig.get_signature(s_sum, r, sig_hash, merkle_root) tx_in.finalize_p2tr_keypath(schnorr.serialize()) self.assertTrue(tx_obj.verify_input(input_index)) self.assertEqual(tx_obj.vbytes(), tx_obj.fee()) @@ -545,7 +554,7 @@ def test_multi_leaf_multisig_locktime(self): tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] @@ -568,10 +577,12 @@ def test_multi_leaf_multisig_sequence(self): ] points = [priv.point for priv in private_keys] tr_multisig = TapRootMultiSig(points, 3) + internal_pubkey = tr_multisig.default_internal_pubkey current_sequence = Sequence.from_relative_blocks(50) - tap_root_input = tr_multisig.multi_leaf_tap_root(sequence=current_sequence) + branch = tr_multisig.multi_leaf_tree(sequence=current_sequence) + merkle_root = branch.hash() self.assertEqual( - tap_root_input.address(network="signet"), + internal_pubkey.p2tr_address(merkle_root, network="signet"), "tb1p2qgnkwl2ny40d0xcy0fldljzmsk5vpj8navrhh5y2vsvle6xnj5ssvzrck", ) prev_tx = bytes.fromhex( @@ -581,7 +592,7 @@ def test_multi_leaf_multisig_sequence(self): for prev_index in range(11): tx_in = TxIn(prev_tx, prev_index, sequence=current_sequence) tx_in._value = 1000000 - tx_in._script_pubkey = tap_root_input.script_pubkey() + tx_in._script_pubkey = internal_pubkey.p2tr_script(merkle_root) tx_ins.append(tx_in) change_script = address_to_script_pubkey( "tb1q7kn55vf3mmd40gyj46r245lw87dc6us5n50lrg" @@ -591,12 +602,12 @@ def test_multi_leaf_multisig_sequence(self): tx_obj = Tx(2, tx_ins, [tx_out], 0, network="signet", segwit=True) for input_index, pubkeys in enumerate(combinations(points, 3)): leaf = MultiSigTapScript(pubkeys, 3, sequence=current_sequence).tap_leaf() - self.assertTrue(tap_root_input.control_block(leaf)) + self.assertTrue(branch.control_block(internal_pubkey, leaf)) tx_in = tx_ins[input_index] tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] @@ -623,8 +634,8 @@ def test_multi_leaf_multisig_sequence(self): s_sum = 0 for nonce_secrets, priv in zip(nonce_secret_pairs, private_keys): k = musig.compute_k(nonce_secrets, nonce_sums, sig_hash) - s_sum += musig.sign(priv, k, r, sig_hash, tweak=tap_root_input.tweak) - schnorr = musig.get_signature(s_sum, r, sig_hash, tap_root_input.tweak) + s_sum += musig.sign(priv, k, r, sig_hash, merkle_root) + schnorr = musig.get_signature(s_sum, r, sig_hash, merkle_root) tx_in.finalize_p2tr_keypath(schnorr.serialize()) self.assertTrue(tx_obj.verify_input(input_index)) self.assertEqual(tx_obj.vbytes(), tx_obj.fee()) @@ -636,7 +647,7 @@ def test_multi_leaf_multisig_sequence(self): tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] @@ -659,9 +670,11 @@ def test_musig(self): private_keys.append(hd_priv_key.get_p2tr_receiving_privkey(address_num=i)) points = [priv.point for priv in private_keys] tr_multisig = TapRootMultiSig(points, 3) - tap_root_input = tr_multisig.musig_tap_root() + internal_pubkey = tr_multisig.default_internal_pubkey + branch = tr_multisig.musig_tree() + merkle_root = branch.hash() self.assertEqual( - tap_root_input.address(network="signet"), + internal_pubkey.p2tr_address(merkle_root, network="signet"), "tb1pd4rlyggzaj902pa92awwdrryftmyjeyd0unmuqf2atsg09jdv90qrp7pzs", ) prev_tx = bytes.fromhex( @@ -671,7 +684,7 @@ def test_musig(self): for prev_index in range(11): tx_in = TxIn(prev_tx, prev_index) tx_in._value = 1000000 - tx_in._script_pubkey = tap_root_input.script_pubkey() + tx_in._script_pubkey = internal_pubkey.p2tr_script(merkle_root) tx_ins.append(tx_in) change_script = address_to_script_pubkey( "tb1q7kn55vf3mmd40gyj46r245lw87dc6us5n50lrg" @@ -683,7 +696,7 @@ def test_musig(self): tx_in = tx_obj.tx_ins[input_index] musig = MuSigTapScript(pubkeys) leaf = musig.tap_leaf() - cb = tap_root_input.control_block(leaf) + cb = branch.control_block(internal_pubkey, leaf) self.assertTrue(cb) tx_in.witness.items = [leaf.tap_script.raw_serialize(), cb.serialize()] sig_hash = tx_obj.sig_hash(input_index, SIGHASH_DEFAULT) @@ -720,8 +733,8 @@ def test_musig(self): s_sum = 0 for nonce_secrets, priv in zip(nonce_secret_pairs, private_keys): k = musig.compute_k(nonce_secrets, nonce_sums, sig_hash) - s_sum += musig.sign(priv, k, r, sig_hash, tweak=tap_root_input.tweak) - schnorr = musig.get_signature(s_sum, r, sig_hash, tap_root_input.tweak) + s_sum += musig.sign(priv, k, r, sig_hash, merkle_root) + schnorr = musig.get_signature(s_sum, r, sig_hash, merkle_root) tx_in.finalize_p2tr_keypath(schnorr.serialize()) self.assertTrue(tx_obj.verify_input(input_index)) self.assertEqual(tx_obj.vbytes(), tx_obj.fee()) @@ -737,10 +750,12 @@ def test_musig_locktime(self): ] points = [priv.point for priv in private_keys] tr_multisig = TapRootMultiSig(points, 3) + internal_pubkey = tr_multisig.default_internal_pubkey current_locktime = Locktime(1643332867) - tap_root_input = tr_multisig.musig_tap_root(locktime=current_locktime) + branch = tr_multisig.musig_tree(locktime=current_locktime) + merkle_root = branch.hash() self.assertEqual( - tap_root_input.address(network="signet"), + internal_pubkey.p2tr_address(merkle_root, network="signet"), "tb1pc357vk4y2qadgtkdfy3xqtmf5hfqh9apdqs3ajaw7kwspthtjt5sj8l4rh", ) prev_tx = bytes.fromhex( @@ -750,7 +765,7 @@ def test_musig_locktime(self): for prev_index in range(11): tx_in = TxIn(prev_tx, prev_index, sequence=0xFFFFFFFE) tx_in._value = 1000000 - tx_in._script_pubkey = tap_root_input.script_pubkey() + tx_in._script_pubkey = internal_pubkey.p2tr_script(merkle_root) tx_ins.append(tx_in) change_script = address_to_script_pubkey( "tb1q7kn55vf3mmd40gyj46r245lw87dc6us5n50lrg" @@ -764,7 +779,7 @@ def test_musig_locktime(self): musig = MuSigTapScript(pubkeys, locktime=current_locktime) leaf = musig.tap_leaf() tx_in = tx_ins[input_index] - cb = tap_root_input.control_block(leaf) + cb = branch.control_block(internal_pubkey, leaf) self.assertTrue(cb) tx_in.witness.items = [leaf.tap_script.raw_serialize(), cb.serialize()] sig_hash = tx_obj.sig_hash(input_index, SIGHASH_DEFAULT) @@ -801,8 +816,8 @@ def test_musig_locktime(self): s_sum = 0 for nonce_secrets, priv in zip(nonce_secret_pairs, private_keys): k = musig.compute_k(nonce_secrets, nonce_sums, sig_hash) - s_sum += musig.sign(priv, k, r, sig_hash, tweak=tap_root_input.tweak) - schnorr = musig.get_signature(s_sum, r, sig_hash, tap_root_input.tweak) + s_sum += musig.sign(priv, k, r, sig_hash, merkle_root) + schnorr = musig.get_signature(s_sum, r, sig_hash, merkle_root) tx_in.finalize_p2tr_keypath(schnorr.serialize()) self.assertTrue(tx_obj.verify_input(input_index)) self.assertEqual(tx_obj.vbytes(), tx_obj.fee()) @@ -812,7 +827,7 @@ def test_musig_locktime(self): musig = MuSigTapScript(pubkeys, locktime=current_locktime) leaf = musig.tap_leaf() tx_in = tx_ins[input_index] - cb = tap_root_input.control_block(leaf) + cb = branch.control_block(internal_pubkey, leaf) self.assertTrue(cb) tx_in.witness.items = [leaf.tap_script.raw_serialize(), cb.serialize()] sig_hash = tx_obj.sig_hash(input_index, SIGHASH_DEFAULT) @@ -844,10 +859,12 @@ def test_musig_sequence(self): ] points = [priv.point for priv in private_keys] tr_multisig = TapRootMultiSig(points, 3) + internal_pubkey = tr_multisig.default_internal_pubkey current_sequence = Sequence.from_relative_time(3000) - tap_root_input = tr_multisig.musig_tap_root(sequence=current_sequence) + branch = tr_multisig.musig_tree(sequence=current_sequence) + merkle_root = branch.hash() self.assertEqual( - tap_root_input.address(network="signet"), + internal_pubkey.p2tr_address(merkle_root, network="signet"), "tb1ppqsvmxljh0e6tjaedamk52ft5k549nxd5wcgzc0luh6rxgwf7djs8s6h0p", ) prev_tx = bytes.fromhex( @@ -857,7 +874,7 @@ def test_musig_sequence(self): for prev_index in range(11): tx_in = TxIn(prev_tx, prev_index, sequence=current_sequence) tx_in._value = 1000000 - tx_in._script_pubkey = tap_root_input.script_pubkey() + tx_in._script_pubkey = internal_pubkey.p2tr_script(merkle_root) tx_ins.append(tx_in) change_script = address_to_script_pubkey( "tb1q7kn55vf3mmd40gyj46r245lw87dc6us5n50lrg" @@ -869,7 +886,7 @@ def test_musig_sequence(self): musig = MuSigTapScript(pubkeys, sequence=current_sequence) leaf = musig.tap_leaf() tx_in = tx_ins[input_index] - cb = tap_root_input.control_block(leaf) + cb = branch.control_block(internal_pubkey, leaf) self.assertTrue(cb) tx_in.witness.items = [leaf.tap_script.raw_serialize(), cb.serialize()] sig_hash = tx_obj.sig_hash(input_index, SIGHASH_DEFAULT) @@ -906,8 +923,8 @@ def test_musig_sequence(self): s_sum = 0 for nonce_secrets, priv in zip(nonce_secret_pairs, private_keys): k = musig.compute_k(nonce_secrets, nonce_sums, sig_hash) - s_sum += musig.sign(priv, k, r, sig_hash, tweak=tap_root_input.tweak) - schnorr = musig.get_signature(s_sum, r, sig_hash, tap_root_input.tweak) + s_sum += musig.sign(priv, k, r, sig_hash, merkle_root) + schnorr = musig.get_signature(s_sum, r, sig_hash, merkle_root) tx_in.finalize_p2tr_keypath(schnorr.serialize()) self.assertTrue(tx_obj.verify_input(input_index)) self.assertEqual(tx_obj.vbytes(), tx_obj.fee()) @@ -917,7 +934,7 @@ def test_musig_sequence(self): leaf = musig.tap_leaf() tx_in = tx_ins[input_index] tx_in.sequence = Sequence(current_sequence - 1) - cb = tap_root_input.control_block(leaf) + cb = branch.control_block(internal_pubkey, leaf) self.assertTrue(cb) tx_in.witness.items = [leaf.tap_script.raw_serialize(), cb.serialize()] sig_hash = tx_obj.sig_hash(input_index, SIGHASH_DEFAULT) @@ -949,11 +966,13 @@ def test_internal_pubkey_musig(self): private_keys.append(hd_priv_key.get_p2tr_receiving_privkey(address_num=i)) points = [priv.point for priv in private_keys] tr_multisig = TapRootMultiSig(points, 3) + internal_pubkey = tr_multisig.default_internal_pubkey musig = MuSigTapScript(points) - tap_root_input = tr_multisig.musig_tap_root() - self.assertEqual(tr_multisig.default_internal_pubkey, musig.point) + branch = tr_multisig.musig_tree() + merkle_root = branch.hash() + self.assertEqual(internal_pubkey, musig.point) self.assertEqual( - tap_root_input.address(network="signet"), + internal_pubkey.p2tr_address(merkle_root, network="signet"), "tb1pd4rlyggzaj902pa92awwdrryftmyjeyd0unmuqf2atsg09jdv90qrp7pzs", ) prev_tx = bytes.fromhex( @@ -963,7 +982,7 @@ def test_internal_pubkey_musig(self): for prev_index in range(3): tx_in = TxIn(prev_tx, prev_index) tx_in._value = 1000000 - tx_in._script_pubkey = tap_root_input.script_pubkey() + tx_in._script_pubkey = internal_pubkey.p2tr_script(merkle_root) tx_ins.append(tx_in) change_script = address_to_script_pubkey( "tb1q7kn55vf3mmd40gyj46r245lw87dc6us5n50lrg" @@ -984,8 +1003,8 @@ def test_internal_pubkey_musig(self): s_sum = 0 for nonce_secrets, priv in zip(nonce_secret_pairs, private_keys): k = musig.compute_k(nonce_secrets, nonce_sums, sig_hash) - s_sum += musig.sign(priv, k, r, sig_hash, tweak=tap_root_input.tweak) - schnorr = musig.get_signature(s_sum, r, sig_hash, tap_root_input.tweak) + s_sum += musig.sign(priv, k, r, sig_hash, merkle_root) + schnorr = musig.get_signature(s_sum, r, sig_hash, merkle_root) tx_in.witness = Witness([schnorr.serialize()]) self.assertTrue(tx_obj.verify_input(input_index)) self.assertEqual(tx_obj.vbytes(), tx_obj.fee()) @@ -1001,10 +1020,11 @@ def test_musig_and_single_leaf_multisig(self): ] points = [priv.point for priv in private_keys] tr_multisig = TapRootMultiSig(points, 3) - tap_root_input = tr_multisig.musig_and_single_leaf_tap_root() - leaf = tap_root_input.tap_node + internal_pubkey = tr_multisig.default_internal_pubkey + branch = tr_multisig.musig_and_single_leaf_tree() + merkle_root = branch.hash() self.assertEqual( - tap_root_input.address(network="signet"), + internal_pubkey.p2tr_address(merkle_root, network="signet"), "tb1pxspe3588dj53da5krsln9uszhudprt4qcc7srahn583j3q34cnts7pazqd", ) prev_tx = bytes.fromhex( @@ -1014,7 +1034,7 @@ def test_musig_and_single_leaf_multisig(self): for prev_index in range(21): tx_in = TxIn(prev_tx, prev_index) tx_in._value = 1000000 - tx_in._script_pubkey = tap_root_input.script_pubkey() + tx_in._script_pubkey = internal_pubkey.p2tr_script(merkle_root) tx_ins.append(tx_in) change_script = address_to_script_pubkey( "tb1q7kn55vf3mmd40gyj46r245lw87dc6us5n50lrg" @@ -1028,7 +1048,7 @@ def test_musig_and_single_leaf_multisig(self): leaf = tr_multisig.single_leaf() tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] @@ -1043,7 +1063,7 @@ def test_musig_and_single_leaf_multisig(self): tx_in = tx_obj.tx_ins[input_index + 10] musig = MuSigTapScript(pubkeys) leaf = musig.tap_leaf() - cb = tap_root_input.control_block(leaf) + cb = branch.control_block(internal_pubkey, leaf) self.assertTrue(cb) tx_in.witness.items = [leaf.tap_script.raw_serialize(), cb.serialize()] sig_hash = tx_obj.sig_hash(input_index + 10, SIGHASH_DEFAULT) @@ -1067,7 +1087,7 @@ def test_musig_and_single_leaf_multisig(self): input_index = len(tx_ins) - 1 tx_in = tx_ins[input_index] musig = MuSigTapScript(points) - self.assertEqual(tr_multisig.default_internal_pubkey, musig.point) + self.assertEqual(internal_pubkey, musig.point) nonce_secret_pairs = [] nonce_point_pairs = [] for _ in points: @@ -1080,8 +1100,8 @@ def test_musig_and_single_leaf_multisig(self): s_sum = 0 for nonce_secrets, priv in zip(nonce_secret_pairs, private_keys): k = musig.compute_k(nonce_secrets, nonce_sums, sig_hash) - s_sum += musig.sign(priv, k, r, sig_hash, tweak=tap_root_input.tweak) - schnorr = musig.get_signature(s_sum, r, sig_hash, tap_root_input.tweak) + s_sum += musig.sign(priv, k, r, sig_hash, merkle_root=merkle_root) + schnorr = musig.get_signature(s_sum, r, sig_hash, merkle_root) tx_in.finalize_p2tr_keypath(schnorr.serialize()) self.assertTrue(tx_obj.verify_input(input_index)) self.assertEqual(tx_obj.vbytes(), tx_obj.fee()) @@ -1097,10 +1117,11 @@ def test_everything_multisig(self): ] points = [priv.point for priv in private_keys] tr_multisig = TapRootMultiSig(points, 3) - tap_root_input = tr_multisig.everything_tap_root() - leaf = tap_root_input.tap_node + branch = tr_multisig.everything_tree() + merkle_root = branch.hash() + internal_pubkey = tr_multisig.default_internal_pubkey self.assertEqual( - tap_root_input.address(network="signet"), + internal_pubkey.p2tr_address(merkle_root, network="signet"), "tb1pttvce83hlk5ktd248tj5dw0xtge9qfcnkhlfs86p4wzvtzzt79hq2h0p77", ) prev_tx = bytes.fromhex( @@ -1110,7 +1131,7 @@ def test_everything_multisig(self): for prev_index in range(31): tx_in = TxIn(prev_tx, prev_index) tx_in._value = 1000000 - tx_in._script_pubkey = tap_root_input.script_pubkey() + tx_in._script_pubkey = internal_pubkey.p2tr_script(merkle_root) tx_ins.append(tx_in) change_script = address_to_script_pubkey( "tb1q7kn55vf3mmd40gyj46r245lw87dc6us5n50lrg" @@ -1124,7 +1145,7 @@ def test_everything_multisig(self): leaf = tr_multisig.single_leaf() tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] @@ -1141,9 +1162,11 @@ def test_everything_multisig(self): tx_in = tx_obj.tx_ins[input_index] tx_in.witness.items = [] leaf = MultiSigTapScript(pubkeys, 3).tap_leaf() - self.assertTrue(tap_root_input.control_block(leaf)) + self.assertTrue(branch.control_block(internal_pubkey, leaf)) tx_obj.initialize_p2tr_multisig( - input_index, tap_root_input.control_block(leaf), leaf.tap_script + input_index, + branch.control_block(internal_pubkey, leaf), + leaf.tap_script, ) sigs = [] for priv in private_keys: @@ -1157,7 +1180,7 @@ def test_everything_multisig(self): tx_in = tx_obj.tx_ins[input_index] musig = MuSigTapScript(pubkeys) leaf = musig.tap_leaf() - cb = tap_root_input.control_block(leaf) + cb = branch.control_block(internal_pubkey, leaf) self.assertTrue(cb) tx_in.witness.items = [leaf.tap_script.raw_serialize(), cb.serialize()] sig_hash = tx_obj.sig_hash(input_index, SIGHASH_DEFAULT) @@ -1181,7 +1204,7 @@ def test_everything_multisig(self): input_index = len(tx_ins) - 1 tx_in = tx_ins[input_index] musig = MuSigTapScript(points) - self.assertEqual(tr_multisig.default_internal_pubkey, musig.point) + self.assertEqual(internal_pubkey, musig.point) nonce_secret_pairs = [] nonce_point_pairs = [] for _ in points: @@ -1194,8 +1217,8 @@ def test_everything_multisig(self): s_sum = 0 for nonce_secrets, priv in zip(nonce_secret_pairs, private_keys): k = musig.compute_k(nonce_secrets, nonce_sums, sig_hash) - s_sum += musig.sign(priv, k, r, sig_hash, tweak=tap_root_input.tweak) - schnorr = musig.get_signature(s_sum, r, sig_hash, tap_root_input.tweak) + s_sum += musig.sign(priv, k, r, sig_hash, merkle_root) + schnorr = musig.get_signature(s_sum, r, sig_hash, merkle_root) tx_in.finalize_p2tr_keypath(schnorr.serialize()) self.assertTrue(tx_obj.verify_input(input_index)) self.assertEqual(tx_obj.vbytes(), tx_obj.fee()) @@ -1211,11 +1234,11 @@ def test_degrading_multisig(self): ] points = [priv.point for priv in private_keys] tr_multisig = TapRootMultiSig(points, 3) - tap_root_input = tr_multisig.degrading_multisig_tap_root( - sequence_time_interval=18 * 512 - ) + branch = tr_multisig.degrading_multisig_tree(sequence_time_interval=18 * 512) + merkle_root = branch.hash() + internal_pubkey = tr_multisig.default_internal_pubkey self.assertEqual( - tap_root_input.address(network="signet"), + internal_pubkey.p2tr_address(merkle_root, network="signet"), "tb1p80glz296ahqunke7tzp7d3s5swjfa5c8ff4vs5xqymee02gs9dkqs27p8u", ) prev_tx = bytes.fromhex( @@ -1225,7 +1248,7 @@ def test_degrading_multisig(self): for prev_index in range(26): tx_in = TxIn(prev_tx, prev_index) tx_in._value = 1000000 - tx_in._script_pubkey = tap_root_input.script_pubkey() + tx_in._script_pubkey = internal_pubkey.p2tr_script(merkle_root) tx_ins.append(tx_in) sequence_1 = Sequence.from_relative_time(18 * 512) sequence_2 = Sequence.from_relative_time(18 * 512 * 2) @@ -1241,12 +1264,12 @@ def test_degrading_multisig(self): tx_obj = Tx(2, tx_ins, [tx_out], 0, network="signet", segwit=True) for input_index, pubkeys in enumerate(combinations(points, 3)): leaf = MultiSigTapScript(pubkeys, 3).tap_leaf() - self.assertTrue(tap_root_input.control_block(leaf)) + self.assertTrue(branch.control_block(internal_pubkey, leaf)) tx_in = tx_ins[input_index] tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] @@ -1261,12 +1284,12 @@ def test_degrading_multisig(self): for i, pubkeys in enumerate(combinations(points, 2)): input_index = i + 10 leaf = MultiSigTapScript(pubkeys, 2, sequence=sequence_1).tap_leaf() - self.assertTrue(tap_root_input.control_block(leaf)) + self.assertTrue(branch.control_block(internal_pubkey, leaf)) tx_in = tx_ins[input_index] tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] @@ -1281,12 +1304,12 @@ def test_degrading_multisig(self): for i, pubkeys in enumerate(combinations(points, 1)): input_index = i + 20 leaf = MultiSigTapScript(pubkeys, 1, sequence=sequence_2).tap_leaf() - self.assertTrue(tap_root_input.control_block(leaf)) + self.assertTrue(branch.control_block(internal_pubkey, leaf)) tx_in = tx_ins[input_index] tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] @@ -1314,8 +1337,8 @@ def test_degrading_multisig(self): s_sum = 0 for nonce_secrets, priv in zip(nonce_secret_pairs, private_keys): k = musig.compute_k(nonce_secrets, nonce_sums, sig_hash) - s_sum += musig.sign(priv, k, r, sig_hash, tweak=tap_root_input.tweak) - schnorr = musig.get_signature(s_sum, r, sig_hash, tap_root_input.tweak) + s_sum += musig.sign(priv, k, r, sig_hash, merkle_root) + schnorr = musig.get_signature(s_sum, r, sig_hash, merkle_root) tx_in.finalize_p2tr_keypath(schnorr.serialize()) self.assertTrue(tx_obj.verify_input(input_index)) self.assertEqual(tx_obj.vbytes(), tx_obj.fee()) @@ -1331,7 +1354,7 @@ def test_degrading_multisig(self): tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] @@ -1350,7 +1373,7 @@ def test_degrading_multisig(self): tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] @@ -1373,11 +1396,11 @@ def test_degrading_multisig_2(self): ] points = [priv.point for priv in private_keys] tr_multisig = TapRootMultiSig(points, 3) - tap_root_input = tr_multisig.degrading_multisig_tap_root( - sequence_block_interval=18 - ) + branch = tr_multisig.degrading_multisig_tree(sequence_block_interval=18) + merkle_root = branch.hash() + internal_pubkey = tr_multisig.default_internal_pubkey self.assertEqual( - tap_root_input.address(network="signet"), + internal_pubkey.p2tr_address(merkle_root, network="signet"), "tb1ps00xd4jzdw982gjmmjgxzrhsca8ez5yfu6p4l6rt83ueqzlfns2qxf3qat", ) prev_tx = bytes.fromhex( @@ -1387,7 +1410,7 @@ def test_degrading_multisig_2(self): for prev_index in range(26): tx_in = TxIn(prev_tx, prev_index) tx_in._value = 1000000 - tx_in._script_pubkey = tap_root_input.script_pubkey() + tx_in._script_pubkey = internal_pubkey.p2tr_script(merkle_root) tx_ins.append(tx_in) sequence_1 = Sequence.from_relative_blocks(18) sequence_2 = Sequence.from_relative_blocks(18 * 2) @@ -1403,12 +1426,12 @@ def test_degrading_multisig_2(self): tx_obj = Tx(2, tx_ins, [tx_out], 0, network="signet", segwit=True) for input_index, pubkeys in enumerate(combinations(points, 3)): leaf = MultiSigTapScript(pubkeys, 3).tap_leaf() - self.assertTrue(tap_root_input.control_block(leaf)) + self.assertTrue(branch.control_block(internal_pubkey, leaf)) tx_in = tx_ins[input_index] tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] @@ -1423,12 +1446,12 @@ def test_degrading_multisig_2(self): for i, pubkeys in enumerate(combinations(points, 2)): input_index = 10 + i leaf = MultiSigTapScript(pubkeys, 2, sequence=sequence_1).tap_leaf() - self.assertTrue(tap_root_input.control_block(leaf)) + self.assertTrue(branch.control_block(internal_pubkey, leaf)) tx_in = tx_ins[input_index] tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] @@ -1443,12 +1466,12 @@ def test_degrading_multisig_2(self): for i, pubkeys in enumerate(combinations(points, 1)): input_index = 20 + i leaf = MultiSigTapScript(pubkeys, 1, sequence=sequence_2).tap_leaf() - self.assertTrue(tap_root_input.control_block(leaf)) + self.assertTrue(branch.control_block(internal_pubkey, leaf)) tx_in = tx_ins[input_index] tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] @@ -1476,8 +1499,8 @@ def test_degrading_multisig_2(self): s_sum = 0 for nonce_secrets, priv in zip(nonce_secret_pairs, private_keys): k = musig.compute_k(nonce_secrets, nonce_sums, sig_hash) - s_sum += musig.sign(priv, k, r, sig_hash, tweak=tap_root_input.tweak) - schnorr = musig.get_signature(s_sum, r, sig_hash, tap_root_input.tweak) + s_sum += musig.sign(priv, k, r, sig_hash, merkle_root) + schnorr = musig.get_signature(s_sum, r, sig_hash, merkle_root) tx_in.finalize_p2tr_keypath(schnorr.serialize()) self.assertTrue(tx_obj.verify_input(input_index)) self.assertEqual(tx_obj.vbytes(), tx_obj.fee()) @@ -1493,7 +1516,7 @@ def test_degrading_multisig_2(self): tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] @@ -1512,7 +1535,7 @@ def test_degrading_multisig_2(self): tx_in.witness.items = [] tx_obj.initialize_p2tr_multisig( input_index, - tap_root_input.control_block(leaf), + branch.control_block(internal_pubkey, leaf), leaf.tap_script, ) sigs = [] diff --git a/buidl/test/test_psbt_helper.py b/buidl/test/test_psbt_helper.py index 78dbf3a..03de3b9 100644 --- a/buidl/test/test_psbt_helper.py +++ b/buidl/test/test_psbt_helper.py @@ -39,7 +39,6 @@ def test_receive_1of2(self): # https://blockstream.info/testnet/tx/4412d2a7664d01bb784a0a359e9aacf160ee436067c6a42dca355da4817ca7da def test_sweep_1of2_p2sh(self): - # This test will produce a validly signed TX for the 1-of-2 p2sh using either key, which will result in a different TX ID # d8172be9981a4f57e6e4ebe0f4785f5f2035aee40ffbb2d6f1200810a879d490 is the one that was broadcast to the testnet blockchain: # https://blockstream.info/testnet/tx/d8172be9981a4f57e6e4ebe0f4785f5f2035aee40ffbb2d6f1200810a879d490 @@ -108,7 +107,6 @@ def test_sweep_1of2_p2sh(self): root_path_to_use = None for cnt, psbt_in in enumerate(psbt_obj.psbt_ins): - self.assertEqual(psbt_in.redeem_script.get_quorum(), (1, 2)) # For this TX there is only one psbt_in (1 input) @@ -140,7 +138,6 @@ def test_sweep_1of2_p2sh(self): create_multisig_psbt(**kwargs, script_type="p2sh") def test_sweep_1of2_p2sh_with_non_BIP67_input(self): - # This test will produce a validly signed TX for the 1-of-2 p2sh using either key, which will result in a different TX ID # d8172be9981a4f57e6e4ebe0f4785f5f2035aee40ffbb2d6f1200810a879d490 is the one that was broadcast to the testnet blockchain: # https://blockstream.info/testnet/tx/d8172be9981a4f57e6e4ebe0f4785f5f2035aee40ffbb2d6f1200810a879d490 @@ -209,7 +206,6 @@ def test_sweep_1of2_p2sh_with_non_BIP67_input(self): root_path_to_use = None for cnt, psbt_in in enumerate(psbt_obj.psbt_ins): - self.assertEqual(psbt_in.redeem_script.get_quorum(), (1, 2)) # For this TX there is only one psbt_in (1 input) @@ -233,7 +229,6 @@ def test_sweep_1of2_p2sh_with_non_BIP67_input(self): self.assertEqual(psbt_obj.final_tx().hash().hex(), signed_tx_hash_hex) def test_spend_1of2_with_change(self): - kwargs = { # this part is unchanged from the previous "public_key_records": [ @@ -316,7 +311,6 @@ def test_spend_1of2_with_change(self): root_path_to_use = None for cnt, psbt_in in enumerate(psbt_obj.psbt_ins): - self.assertEqual(psbt_in.redeem_script.get_quorum(), (1, 2)) # For this TX there is only one psbt_in (1 input) @@ -579,7 +573,6 @@ def test_full_wallet_functionality(self): psbt_desc_want, expected_tx_hash, ) in test_outputs: - # Return all the funds to the faucet address psbt_obj = create_multisig_psbt( public_key_records=pubkey_records, diff --git a/buidl/test/test_schnorr.py b/buidl/test/test_schnorr.py index 69883a7..d2b989d 100644 --- a/buidl/test/test_schnorr.py +++ b/buidl/test/test_schnorr.py @@ -46,14 +46,14 @@ def test_signing(self): for ( index, secret, - bip340_pk, + xonly_pk, aux_rand, message, signature, comment, ) in tests: private_key = PrivateKey(secret=int(secret, 16)) - public_key = S256Point.parse(bytes.fromhex(bip340_pk)) + public_key = S256Point.parse(bytes.fromhex(xonly_pk)) aux = bytes.fromhex(aux_rand) msg = bytes.fromhex(message) want_sig = SchnorrSignature.parse(bytes.fromhex(signature)) @@ -74,8 +74,8 @@ def test_verify(self): "23b1d4ff27b16af4b0fcb9672df671701a1a7f5a6bb7352b051f461edbc614aa6068b3e5313a174f90f3d95dc4e06f69bebd9cf5a3098fde034b01e69e8e7889", ), ] - for bip340_pk, message, signature in tests: - public_key = S256Point.parse(bytes.fromhex(bip340_pk)) + for xonly_pk, message, signature in tests: + public_key = S256Point.parse(bytes.fromhex(xonly_pk)) msg = bytes.fromhex(message) sig = SchnorrSignature.parse(bytes.fromhex(signature)) self.assertTrue(public_key.verify_schnorr(msg, sig)) @@ -153,10 +153,10 @@ def test_errors(self): "public key is not a valid X coordinate because it exceeds the field size", ), ] - for bip340_pk, message, signature, error, comment in tests: + for xonly_pk, message, signature, error, comment in tests: with self.assertRaises(error): print(comment) - public_key = S256Point.parse(bytes.fromhex(bip340_pk)) + public_key = S256Point.parse(bytes.fromhex(xonly_pk)) msg = bytes.fromhex(message) sig = SchnorrSignature.parse(bytes.fromhex(signature)) assert public_key.verify_schnorr(msg, sig) diff --git a/buidl/test/test_script.py b/buidl/test/test_script.py index 7412692..c0c8de4 100644 --- a/buidl/test/test_script.py +++ b/buidl/test/test_script.py @@ -197,7 +197,6 @@ def test_p2sh_address(self): self.assertEqual(witness_script.p2sh_address(network="signet"), want) def test_p2wsh_with_quorum(self): - p2wsh_script_hex = "ad51210236a6cf4254c8290a168ecab4aee771018d357ea87154a5b5fea9ed9baee2585e210355ec1001c2c4f1dce2de940beacbdcb7d7746140281a9283000aa46d251d46312103833d6e7c4121180fb79180b78a0573ad57c299825f18f49f6942cb38b6bf023a2103a9e341c32d8870706115443cf163bfc3d2da0ca8515a29bcc1a500c65cfb23bb2103b2ac11803043c0db884dcddfdcff02599324d5e747b26e4235f57b8019fae04155ae" witness_script = WitnessScript.parse(BytesIO(bytes.fromhex((p2wsh_script_hex)))) diff --git a/buidl/test/test_taproot.py b/buidl/test/test_taproot.py index 9b325b2..479e8db 100644 --- a/buidl/test/test_taproot.py +++ b/buidl/test/test_taproot.py @@ -11,7 +11,7 @@ ControlBlock, TapLeaf, TapBranch, - TapRoot, + TapScript, ) from buidl.tx import Tx from buidl.witness import Witness @@ -19,14 +19,13 @@ class TaprootTest(TestCase): def test_hd(self): - point = S256Point.parse_bip340( + point = S256Point.parse_xonly( bytes.fromhex( "cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115" ) ) - t = TapRoot(point) self.assertEqual( - t.tweak_point.bip340().hex(), + point.tweaked_key().xonly().hex(), "a60869f0dbcf1dc659c9cecbaf8050135ea9e8cdc487053f1dc6880949dc684c", ) @@ -58,22 +57,23 @@ def test_p2tr_empty_script_tree(self): }, ] for test in tests: - point = S256Point.parse_bip340( + point = S256Point.parse_xonly( bytes.fromhex(test["given"]["internalPubkey"]) ) raw_tweak = bytes.fromhex(test["intermediary"]["tweak"]) - tap_root = TapRoot(point) - self.assertEqual(tap_root.tweak, big_endian_to_int(raw_tweak)) - tweak_point_want = S256Point.parse_bip340( + self.assertEqual(point.tweak(), raw_tweak) + external_pubkey_want = S256Point.parse_xonly( bytes.fromhex(test["intermediary"]["tweakedPubkey"]) ) - self.assertEqual(tap_root.bip340(), tweak_point_want.bip340()) - stream = BytesIO( - encode_varstr(bytes.fromhex(test["expected"]["scriptPubKey"])) + external_pubkey = point.tweaked_key(tweak=raw_tweak) + self.assertEqual(external_pubkey.xonly(), external_pubkey_want.xonly()) + script_pubkey_want = bytes.fromhex(test["expected"]["scriptPubKey"]) + self.assertEqual( + point.p2tr_script(tweak=raw_tweak).raw_serialize(), script_pubkey_want + ) + self.assertEqual( + point.p2tr_address(tweak=raw_tweak), test["expected"]["bip350Address"] ) - script_pubkey_want = ScriptPubKey.parse(stream) - self.assertEqual(tap_root.script_pubkey(), script_pubkey_want) - self.assertEqual(tap_root.address(), test["expected"]["bip350Address"]) def test_p2tr_general(self): tests = [ @@ -276,9 +276,9 @@ def test_p2tr_general(self): ] def parse_item(item): - if type(item) == dict: + if isinstance(item, dict): tapleaf_version = item["leafVersion"] - tap_script = Script.parse( + tap_script = TapScript.parse( BytesIO(encode_varstr(bytes.fromhex(item["script"]))) ) tap_leaf = TapLeaf(tap_script, tapleaf_version) @@ -287,26 +287,28 @@ def parse_item(item): return TapBranch(parse_item(item[0]), parse_item(item[1])) for test in tests: - point = S256Point.parse_bip340( + point = S256Point.parse_xonly( bytes.fromhex(test["given"]["internalPubkey"]) ) tap_tree = parse_item(test["given"]["scriptTree"]) merkle_root = tap_tree.hash() merkle_root_want = bytes.fromhex(test["intermediary"]["merkleRoot"]) self.assertEqual(merkle_root, merkle_root_want) - tap_root = TapRoot(point, tap_tree) raw_tweak = bytes.fromhex(test["intermediary"]["tweak"]) - self.assertEqual(tap_root.tweak, big_endian_to_int(raw_tweak)) - tweak_point_want = S256Point.parse_bip340( + self.assertEqual(point.tweak(merkle_root), raw_tweak) + tweak_point_want = S256Point.parse_xonly( bytes.fromhex(test["intermediary"]["tweakedPubkey"]) ) - self.assertEqual(tap_root.bip340(), tweak_point_want.bip340()) + external_pubkey = point.tweaked_key(merkle_root) + self.assertEqual(external_pubkey.xonly(), tweak_point_want.xonly()) stream = BytesIO( encode_varstr(bytes.fromhex(test["expected"]["scriptPubKey"])) ) script_pubkey_want = ScriptPubKey.parse(stream) - self.assertEqual(tap_root.script_pubkey(), script_pubkey_want) - self.assertEqual(tap_root.address(), test["expected"]["bip350Address"]) + self.assertEqual(point.p2tr_script(merkle_root), script_pubkey_want) + self.assertEqual( + point.p2tr_address(merkle_root), test["expected"]["bip350Address"] + ) control_blocks = test["expected"]["scriptPathControlBlocks"] leaf_hashes = test["intermediary"]["leafHashes"] for control_block_hex, tap_leaf, leaf_hash in zip( @@ -315,16 +317,19 @@ def parse_item(item): self.assertEqual(tap_leaf.hash(), bytes.fromhex(leaf_hash)) control_block_raw = bytes.fromhex(control_block_hex) control_block_want = ControlBlock.parse(control_block_raw) - control_block = tap_root.control_block(tap_leaf) + control_block = tap_tree.control_block(point, tap_leaf) self.assertEqual(control_block, control_block_want) self.assertEqual( tap_leaf.tapleaf_version, control_block.tapleaf_version ) - self.assertEqual(tap_root.parity, control_block.parity) self.assertEqual(control_block.serialize(), control_block_raw) self.assertEqual(control_block.internal_pubkey, point) - self.assertEqual(control_block.merkle_root(tap_leaf), merkle_root) - self.assertEqual(control_block.tweak(tap_leaf), raw_tweak) + self.assertEqual( + control_block.merkle_root(tap_leaf.tap_script), merkle_root + ) + self.assertEqual( + control_block.internal_pubkey.tweak(merkle_root), raw_tweak + ) def test_p2tr_spending(self): test = { @@ -579,19 +584,16 @@ def test_p2tr_spending(self): pubkey = private_key.point hash_type = input_data["given"]["hashType"] self.assertEqual( - pubkey.bip340().hex(), input_data["intermediary"]["internalPubkey"] + pubkey.xonly().hex(), input_data["intermediary"]["internalPubkey"] ) mr_hex = input_data["given"]["merkleRoot"] if mr_hex is None: - merkle_root = None + merkle_root = b"" else: merkle_root = bytes.fromhex(mr_hex) - tap_root = TapRoot(pubkey, merkle_root=merkle_root) - tweak_want = big_endian_to_int( - bytes.fromhex(input_data["intermediary"]["tweak"]) - ) - self.assertEqual(tap_root.tweak, tweak_want) - tweaked_private_key = private_key.tweaked(tap_root.tweak) + tweak_want = bytes.fromhex(input_data["intermediary"]["tweak"]) + self.assertEqual(pubkey.tweak(merkle_root), tweak_want) + tweaked_private_key = private_key.tweaked_key(merkle_root) tweaked_want = big_endian_to_int( bytes.fromhex(input_data["intermediary"]["tweakedPrivkey"]) ) diff --git a/buidl/timelock.py b/buidl/timelock.py index 8751da7..3c75183 100644 --- a/buidl/timelock.py +++ b/buidl/timelock.py @@ -42,7 +42,7 @@ def is_comparable(self, other): ) def __lt__(self, other): - if type(other) == int: + if type(other) is int: return super().__lt__(other) if self.is_comparable(other): return super().__lt__(other) @@ -108,7 +108,7 @@ def is_comparable(self, other): ) def __lt__(self, other): - if type(other) == int: + if type(other) is int: return super().__lt__(other) if self.is_comparable(other): return self & SEQUENCE_MASK < other & SEQUENCE_MASK diff --git a/buidl/tx.py b/buidl/tx.py index f4c4613..3837759 100644 --- a/buidl/tx.py +++ b/buidl/tx.py @@ -649,7 +649,7 @@ def initialize_p2tr_multisig(self, input_index, control_block, tap_script): tx_in.witness = Witness( [tap_script.raw_serialize(), control_block.serialize()] ) - if type(tap_script) != MultiSigTapScript: + if type(tap_script) is not MultiSigTapScript: raise RuntimeError("tap script must be MultiSigTapScript") tx_in.tap_script = tap_script diff --git a/test_multiwallet.py b/test_multiwallet.py index 22321b5..c247fbc 100644 --- a/test_multiwallet.py +++ b/test_multiwallet.py @@ -15,7 +15,6 @@ def expect(self, text): """ buffer = "" while True: - try: # This will error out at the end of the buffer latest_char = self.child.read(1) diff --git a/test_singlesweep.py b/test_singlesweep.py index 235c998..2a0450e 100644 --- a/test_singlesweep.py +++ b/test_singlesweep.py @@ -18,7 +18,6 @@ def expect(self, text): """ buffer = "" while True: - try: # This will error out at the end of the buffer latest_char = self.child.read(1)