def test_pubpoint(self): # write a test that tests the public point for the following points = ( # secret, x, y ( 7, "045CBDF0646E5DB4EAA398F365F2EA7A0E3D419B7E0330E39CE92BDDEDCAC4F9BC6AEBCA40BA255960A3178D6D861A54DBA813D0B813FDE7B5A5082628087264DA", ), ( 1485, "04C982196A7466FBBBB0E27A940B6AF926C1A74D5AD07128C82824A11B5398AFDA7A91F9EAE64438AFB9CE6448A1C133DB2D8FB9254E4546B6F001637D50901F55", ), ( 2**128, "048F68B9D2F63B5F339239C1AD981F162EE88C5678723EA3351B7B444C9EC4C0DA662A9F2DBA063986DE1D90C2B6BE215DBBEA2CFE95510BFDF23CBF79501FFF82", ), ( 2**240 + 2**31, "049577FF57C8234558F293DF502CA4F09CBC65A6572C842B39B366F2171794511610B49C67FA9365AD7B90DAB070BE339A1DAF9052373EC30FFAE4F72D5E66D053", ), ) # iterate over points sum_secrets = 0 point_objects = [] for secret, sec in points: # initialize the secp256k1 point (S256Point) point = S256Point.parse(bytes.fromhex(sec)) # check that the secret*G is the same as the point self.assertEqual(secret * G, point) sum_secrets += secret point_objects.append(point) self.assertEqual(sum_secrets * G, S256Point.combine(point_objects))
def test_p2tr_empty_script_tree(self): tests = [ { "given": { "internalPubkey": "d6889cb081036e0faefa3a35157ad71086b123b2b144b649798b494c300a961d", }, "intermediary": { "tweak": "b86e7be8f39bab32a6f2c0443abbc210f0edac0e2c53d501b36b64437d9c6c70", "tweakedPubkey": "53a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343", }, "expected": { "scriptPubKey": "512053a1f6e454df1aa2776a2814a721372d6258050de330b3c6d10ee8f4e0dda343", "bip350Address": "bc1p2wsldez5mud2yam29q22wgfh9439spgduvct83k3pm50fcxa5dps59h4z5", }, }, ] for test in tests: point = S256Point.parse_bip340( 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( bytes.fromhex(test["intermediary"]["tweakedPubkey"]) ) self.assertEqual(tap_root.bip340(), tweak_point_want.bip340()) 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"])
def __init__(self, points, locktime=None, sequence=None): if locktime is not None and sequence is not None: raise ValueError( "Both locktime and sequence are defined. Only one of them should be." ) 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)) self.coefs = [ big_endian_to_int(hash_keyaggcoef(self.commitment + b)) for b in bip340s ] # 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)} # aggregate point self.point = S256Point.combine( [c * p for c, p in zip(self.coefs, self.points)]) 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 += [self.point.bip340(), 0xAC]
def test_parse(self): csec = bytes.fromhex( "0349fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278a" ) point = S256Point.parse(csec) usec = bytes.fromhex( "0449fc4e631e3624a545de3f89f5d8684c7b8138bd94bdd531d2e213bf016b278aa56c896489c71dfc65701ce25050f542f336893fb8cd15f4e8e5c124dbf58e47" ) self.assertEqual(point.sec(False), usec)
def test_signing(self): tests = [ ( "0", "0000000000000000000000000000000000000000000000000000000000000003", "F9308A019258C31049344F85F89D5229B531C845836F99B08601F113BCE036F9", "0000000000000000000000000000000000000000000000000000000000000000", "0000000000000000000000000000000000000000000000000000000000000000", "E907831F80848D1069A5371B402410364BDF1C5F8307B0084C55F1CE2DCA821525F66A4A85EA8B71E482A74F382D2CE5EBEEE8FDB2172F477DF4900D310536C0", "", ), ( "1", "B7E151628AED2A6ABF7158809CF4F3C762E7160F38B4DA56A784D9045190CFEF", "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "0000000000000000000000000000000000000000000000000000000000000001", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6896BD60EEAE296DB48A229FF71DFE071BDE413E6D43F917DC8DCF8C78DE33418906D11AC976ABCCB20B091292BFF4EA897EFCB639EA871CFA95F6DE339E4B0A", "", ), ( "2", "C90FDAA22168C234C4C6628B80DC1CD129024E088A67CC74020BBEA63B14E5C9", "DD308AFEC5777E13121FA72B9CC1B7CC0139715309B086C960E18FD969774EB8", "C87AA53824B4D7AE2EB035A2B5BBBCCC080E76CDC6D1692C4B0B62D798E6D906", "7E2D58D8B3BCDF1ABADEC7829054F90DDA9805AAB56C77333024B9D0A508B75C", "5831AAEED7B44BB74E5EAB94BA9D4294C49BCF2A60728D8B4C200F50DD313C1BAB745879A5AD954A72C45A91C3A51D3C7ADEA98D82F8481E0E1E03674A6F3FB7", "", ), ( "3", "0B432B2677937381AEF05BB02A66ECD012773062CF3FA2549E44F58ED2401710", "25D1DFF95105F5253C4022F628A996AD3A0D95FBF21D468A1B33F8C160D8F517", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "7EB0509757E246F19449885651611CB965ECC1A187DD51B64FDA1EDC9637D5EC97582B9CB13DB3933705B32BA982AF5AF25FD78881EBB32771FC5922EFC66EA3", "test fails if msg is reduced modulo p or n", ), ] for ( index, secret, bip340_pk, aux_rand, message, signature, comment, ) in tests: private_key = PrivateKey(secret=int(secret, 16)) public_key = S256Point.parse(bytes.fromhex(bip340_pk)) aux = bytes.fromhex(aux_rand) msg = bytes.fromhex(message) want_sig = SchnorrSignature.parse(bytes.fromhex(signature)) self.assertTrue(public_key.verify_schnorr(msg, want_sig)) sig = private_key.sign_schnorr(msg, aux) self.assertEqual(sig, want_sig)
def test_hd(self): point = S256Point.parse_bip340( bytes.fromhex( "cc8a4bc64d897bddc5fbc2f670f7a8ba0b386779106cf1223c6fc5d7cd6fc115" ) ) t = TapRoot(point) self.assertEqual( t.tweak_point.bip340().hex(), "a60869f0dbcf1dc659c9cecbaf8050135ea9e8cdc487053f1dc6880949dc684c", )
def parse(cls, b): b_len = len(b) if b_len % 32 != 1: raise ValueError( "There should be 32*m+1 bytes where m is an integer") if b_len < 33 or b_len > 33 + 128 * 32: 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]) 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)
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 test_verify(self): tests = [ ( "D69C3509BB99E412E68B0FE8544E72837DFA30746D8BE2AA65975F29D22DC7B9", "4DF3C3F68FCC83B27E9D42C90431A72499F17875C81A599B566C9889B9696703", "00000000000000000000003B78CE563F89A0ED9414F5AA28AD0D96D6795F9C6376AFB1548AF603B3EB45C9F8207DEE1060CB71C04E80F593060B07D28308D7F4", ), ( "d0fa46cb883e940ac3dc5421f05b03859972639f51ed2eccbf3dc5a62e2e1b15", "11864b0142c248fdb090d08893745e0b36a78f988a8334d2056814ad5f541596", "23b1d4ff27b16af4b0fcb9672df671701a1a7f5a6bb7352b051f461edbc614aa6068b3e5313a174f90f3d95dc4e06f69bebd9cf5a3098fde034b01e69e8e7889", ), ] for bip340_pk, message, signature in tests: public_key = S256Point.parse(bytes.fromhex(bip340_pk)) msg = bytes.fromhex(message) sig = SchnorrSignature.parse(bytes.fromhex(signature)) self.assertTrue(public_key.verify_schnorr(msg, sig))
def op_checkmultisig(stack, tx_obj, input_index): if len(stack) < 1: return False n = decode_num(stack.pop()) if len(stack) < n + 1: return False sec_pubkeys = [] for _ in range(n): sec_pubkeys.append(stack.pop()) m = decode_num(stack.pop()) if len(stack) < m + 1: return False der_signatures = [] for _ in range(m): tmp = stack.pop() der, hash_type = tmp[:-1], tmp[-1] der_signatures.append((der, hash_type)) # OP_CHECKMULTISIG bug stack.pop() try: # parse the sec pubkeys into an array of points points = [S256Point.parse(sec) for sec in sec_pubkeys] # loop through the signatures for der_signature, hash_type in der_signatures: sig = Signature.parse(der_signature) z = tx_obj.sig_hash(input_index, hash_type) # bail early if we don't have any points left if len(points) == 0: print("signatures no good or not in right order") return False # while we have points while points: # get the point at the front (points.pop(0)) point = points.pop(0) # see if this point can verify this sig with this z if point.verify(z, sig): # break if so, this sig is valid! break # if we made it this far, we have to add a 1 to the stack # use encode_num(1) stack.append(encode_num(1)) except (ValueError, SyntaxError): return False return True
def op_checksig(stack, tx_obj, input_index): # check to see if there's at least 2 elements if len(stack) < 2: return False # get the sec_pubkey with stack.pop() sec_pubkey = stack.pop() # get the der_signature with stack.pop()[:-1] (last byte is removed) tmp = stack.pop() der_signature, hash_type = tmp[:-1], tmp[-1] # parse the sec format pubkey with S256Point point = S256Point.parse(sec_pubkey) # parse the der format signature with Signature sig = Signature.parse(der_signature) z = tx_obj.sig_hash(input_index, hash_type) # verify using the point, z and signature # if verified add encode_num(1) to the end, otherwise encode_num(0) if point.verify(z, sig): stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True
def test_verify(self): tests = ( ( 0xEC208BAA0FC1C19F708A9CA96FDEFF3AC3F230BB4A7BA4AEDE4942AD003C0F60, "3045022100ac8d1c87e51d0d441be8b3dd5b05c8795b48875dffe00b7ffcfac23010d3a3950220068342ceff8935ededd102dd876ffd6ba72d6a427a3edb13d26eb0781cb423c4", "04887387e452b8eacc4acfde10d9aaf7f6d9a0f975aabb10d006e4da568744d06c61de6d95231cd89026e286df3b6ae4a894a3378e393e93a0f45b666329a0ae34", ), ( 0x7C076FF316692A3D7EB3C3BB0F8B1488CF72E1AFCD929E29307032997A838A3D, "3044022000eff69ef2b1bd93a66ed5219add4fb51e11a840f404876325a1e8ffe0529a2c022038df8011e682d839e75159debf909408cb3f12ae472b1d88cf6280cf01c6568b", "04887387e452b8eacc4acfde10d9aaf7f6d9a0f975aabb10d006e4da568744d06c61de6d95231cd89026e286df3b6ae4a894a3378e393e93a0f45b666329a0ae34", ), ( 0x2270CB0316E68389A3A23DE16023A03B8FC271A21B467B1DC97E0FC0E2CE97F7, "3045022100ea6d640d5275d091607e1f4ad5cdb214e45f8d17cca1095074894dde347605ba022029062e1ff0d9eee52da1f3621caf92436877d7076720e2b3d9f226bf853e2b75", "04f47dc2ac0ecaadda5ee2b3ab9bc4e02c3eafb2abcc426643686ad95f6d4e8c44e33fa47d96fc2dace0ef2f583965cf6a0f8faa7a070c0f8ee986d192e2d21835", ), ) for z, der_hex, sec in tests: point = S256Point.parse(bytes.fromhex(sec)) der = bytes.fromhex(der_hex) self.assertTrue(point.verify(z, Signature.parse(der)))
def op_checksig_schnorr(stack, tx_obj, input_index): # check to see if there's at least 2 elements if len(stack) < 2: return False pubkey = stack.pop() signature = stack.pop() point = S256Point.parse_bip340(pubkey) if len(signature) == 65: hash_type = signature[-1] signature = signature[:-1] elif len(signature) == 0: stack.append(encode_num(0)) return True else: hash_type = 0 sig = SchnorrSignature.parse(signature) msg = tx_obj.sig_hash(input_index, hash_type) if point.verify_schnorr(msg, sig): stack.append(encode_num(1)) else: stack.append(encode_num(0)) return True
def __init__(self, points, k, locktime=None, sequence=None): if locktime is not None and sequence is not None: raise ValueError( "Both locktime and sequence are defined. Only one of them should be." ) 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] 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] if len(points) > 1: for bip340 in bip340s[1:]: self.commands += [bip340, 0xBA] self.commands += [number_to_op_code(k), 0x87]
def test_p2tr_general(self): tests = [ { "given": { "internalPubkey": "187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27", "scriptTree": { "id": 0, "script": "20d85a959b0290bf19bb89ed43c916be835475d013da4b362117393e25a48229b8ac", "leafVersion": 192, }, }, "intermediary": { "leafHashes": [ "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21" ], "merkleRoot": "5b75adecf53548f3ec6ad7d78383bf84cc57b55a3127c72b9a2481752dd88b21", "tweak": "cbd8679ba636c1110ea247542cfbd964131a6be84f873f7f3b62a777528ed001", "tweakedPubkey": "147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3", }, "expected": { "scriptPubKey": "5120147c9c57132f6e7ecddba9800bb0c4449251c92a1e60371ee77557b6620f3ea3", "bip350Address": "bc1pz37fc4cn9ah8anwm4xqqhvxygjf9rjf2resrw8h8w4tmvcs0863sa2e586", "scriptPathControlBlocks": [ "c1187791b6f712a8ea41c8ecdd0ee77fab3e85263b37e1ec18a3651926b3a6cf27" ], }, }, { "given": { "internalPubkey": "93478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820", "scriptTree": { "id": 0, "script": "20b617298552a72ade070667e86ca63b8f5789a9fe8731ef91202a91c9f3459007ac", "leafVersion": 192, }, }, "intermediary": { "leafHashes": [ "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b" ], "merkleRoot": "c525714a7f49c28aedbbba78c005931a81c234b2f6c99a73e4d06082adc8bf2b", "tweak": "6af9e28dbf9d6aaf027696e2598a5b3d056f5fd2355a7fd5a37a0e5008132d30", "tweakedPubkey": "e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e", }, "expected": { "scriptPubKey": "5120e4d810fd50586274face62b8a807eb9719cef49c04177cc6b76a9a4251d5450e", "bip350Address": "bc1punvppl2stp38f7kwv2u2spltjuvuaayuqsthe34hd2dyy5w4g58qqfuag5", "scriptPathControlBlocks": [ "c093478e9488f956df2396be2ce6c5cced75f900dfa18e7dabd2428aae78451820" ], }, }, { "given": { "internalPubkey": "ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592", "scriptTree": [ { "id": 0, "script": "20387671353e273264c495656e27e39ba899ea8fee3bb69fb2a680e22093447d48ac", "leafVersion": 192, }, {"id": 1, "script": "06424950333431", "leafVersion": 250}, ], }, "intermediary": { "leafHashes": [ "8ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7", "f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a", ], "merkleRoot": "6c2dc106ab816b73f9d07e3cd1ef2c8c1256f519748e0813e4edd2405d277bef", "tweak": "9e0517edc8259bb3359255400b23ca9507f2a91cd1e4250ba068b4eafceba4a9", "tweakedPubkey": "712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5", }, "expected": { "scriptPubKey": "5120712447206d7a5238acc7ff53fbe94a3b64539ad291c7cdbc490b7577e4b17df5", "bip350Address": "bc1pwyjywgrd0ffr3tx8laflh6228dj98xkjj8rum0zfpd6h0e930h6saqxrrm", "scriptPathControlBlocks": [ "c0ee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf3786592f224a923cd0021ab202ab139cc56802ddb92dcfc172b9212261a539df79a112a", "faee4fe085983462a184015d1f782d6a5f8b9c2b60130aff050ce221ecf37865928ad69ec7cf41c2a4001fd1f738bf1e505ce2277acdcaa63fe4765192497f47a7", ], }, }, { "given": { "internalPubkey": "f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd8", "scriptTree": [ { "id": 0, "script": "2044b178d64c32c4a05cc4f4d1407268f764c940d20ce97abfd44db5c3592b72fdac", "leafVersion": 192, }, {"id": 1, "script": "07546170726f6f74", "leafVersion": 192}, ], }, "intermediary": { "leafHashes": [ "64512fecdb5afa04f98839b50e6f0cb7b1e539bf6f205f67934083cdcc3c8d89", "2cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb", ], "merkleRoot": "ab179431c28d3b68fb798957faf5497d69c883c6fb1e1cd9f81483d87bac90cc", "tweak": "639f0281b7ac49e742cd25b7f188657626da1ad169209078e2761cefd91fd65e", "tweakedPubkey": "77e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220", }, "expected": { "scriptPubKey": "512077e30a5522dd9f894c3f8b8bd4c4b2cf82ca7da8a3ea6a239655c39c050ab220", "bip350Address": "bc1pwl3s54fzmk0cjnpl3w9af39je7pv5ldg504x5guk2hpecpg2kgsqaqstjq", "scriptPathControlBlocks": [ "c1f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd82cb2b90daa543b544161530c925f285b06196940d6085ca9474d41dc3822c5cb", "c1f9f400803e683727b14f463836e1e78e1c64417638aa066919291a225f0e8dd864512fecdb5afa04f98839b50e6f0cb7b1e539bf6f205f67934083cdcc3c8d89", ], }, }, { "given": { "internalPubkey": "e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f", "scriptTree": [ { "id": 0, "script": "2072ea6adcf1d371dea8fba1035a09f3d24ed5a059799bae114084130ee5898e69ac", "leafVersion": 192, }, [ { "id": 1, "script": "202352d137f2f3ab38d1eaa976758873377fa5ebb817372c71e2c542313d4abda8ac", "leafVersion": 192, }, { "id": 2, "script": "207337c0dd4253cb86f2c43a2351aadd82cccb12a172cd120452b9bb8324f2186aac", "leafVersion": 192, }, ], ], }, "intermediary": { "leafHashes": [ "2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817", "ba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c", "9e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf6", ], "merkleRoot": "ccbd66c6f7e8fdab47b3a486f59d28262be857f30d4773f2d5ea47f7761ce0e2", "tweak": "b57bfa183d28eeb6ad688ddaabb265b4a41fbf68e5fed2c72c74de70d5a786f4", "tweakedPubkey": "91b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605", }, "expected": { "scriptPubKey": "512091b64d5324723a985170e4dc5a0f84c041804f2cd12660fa5dec09fc21783605", "bip350Address": "bc1pjxmy65eywgafs5tsunw95ruycpqcqnev6ynxp7jaasylcgtcxczs6n332e", "scriptPathControlBlocks": [ "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6fffe578e9ea769027e4f5a3de40732f75a88a6353a09d767ddeb66accef85e553", "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6f9e31407bffa15fefbf5090b149d53959ecdf3f62b1246780238c24501d5ceaf62645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817", "c0e0dfe2300b0dd746a3f8674dfd4525623639042569d829c7f0eed9602d263e6fba982a91d4fc552163cb1c0da03676102d5b7a014304c01f0c77b2b8e888de1c2645a02e0aac1fe69d69755733a9b7621b694bb5b5cde2bbfc94066ed62b9817", ], }, }, { "given": { "internalPubkey": "55adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d", "scriptTree": [ { "id": 0, "script": "2071981521ad9fc9036687364118fb6ccd2035b96a423c59c5430e98310a11abe2ac", "leafVersion": 192, }, [ { "id": 1, "script": "20d5094d2dbe9b76e2c245a2b89b6006888952e2faa6a149ae318d69e520617748ac", "leafVersion": 192, }, { "id": 2, "script": "20c440b462ad48c7a77f94cd4532d8f2119dcebbd7c9764557e62726419b08ad4cac", "leafVersion": 192, }, ], ], }, "intermediary": { "leafHashes": [ "f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d", "737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711", "d7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7", ], "merkleRoot": "2f6b2c5397b6d68ca18e09a3f05161668ffe93a988582d55c6f07bd5b3329def", "tweak": "6579138e7976dc13b6a92f7bfd5a2fc7684f5ea42419d43368301470f3b74ed9", "tweakedPubkey": "75169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831", }, "expected": { "scriptPubKey": "512075169f4001aa68f15bbed28b218df1d0a62cbbcf1188c6665110c293c907b831", "bip350Address": "bc1pw5tf7sqp4f50zka7629jrr036znzew70zxyvvej3zrpf8jg8hqcssyuewe", "scriptPathControlBlocks": [ "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d3cd369a528b326bc9d2133cbd2ac21451acb31681a410434672c8e34fe757e91", "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312dd7485025fceb78b9ed667db36ed8b8dc7b1f0b307ac167fa516fe4352b9f4ef7f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d", "c155adf4e8967fbd2e29f20ac896e60c3b0f1d5b0efa9d34941b5958c7b0a0312d737ed1fe30bc42b8022d717b44f0d93516617af64a64753b7a06bf16b26cd711f154e8e8e17c31d3462d7132589ed29353c6fafdb884c5a6e04ea938834f0d9d", ], }, }, ] def parse_item(item): if type(item) == dict: tapleaf_version = item["leafVersion"] tap_script = Script.parse( BytesIO(encode_varstr(bytes.fromhex(item["script"]))) ) tap_leaf = TapLeaf(tap_script, tapleaf_version) return tap_leaf else: return TapBranch(parse_item(item[0]), parse_item(item[1])) for test in tests: point = S256Point.parse_bip340( 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( bytes.fromhex(test["intermediary"]["tweakedPubkey"]) ) self.assertEqual(tap_root.bip340(), tweak_point_want.bip340()) 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"]) control_blocks = test["expected"]["scriptPathControlBlocks"] leaf_hashes = test["intermediary"]["leafHashes"] for control_block_hex, tap_leaf, leaf_hash in zip( control_blocks, tap_tree.leaves(), leaf_hashes ): 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) 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)
def test_errors(self): tests = [ ( "EEFDEA4CDB677750A420FEE807EACF21EB9898AE79B9768766E4FAA04A2D4A34", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", ValueError, "public key not on the curve", ), ( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "FFF97BD5755EEEA420453A14355235D382F6472F8568A18B2F057A14602975563CC27944640AC607CD107AE10923D9EF7A73C643E166BE5EBEAFA34B1AC553E2", AssertionError, "has_even_y(R) is false", ), ( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "1FA62E331EDBC21C394792D2AB1100A7B432B013DF3F6FF4F99FCB33E0E1515F28890B3EDB6E7189B630448B515CE4F8622A954CFE545735AAEA5134FCCDB2BD", AssertionError, "negated message", ), ( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769961764B3AA9B2FFCB6EF947B6887A226E8D7C93E00C5ED0C1834FF0D0C2E6DA6", AssertionError, "negated s value", ), ( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "0000000000000000000000000000000000000000000000000000000000000000123DDA8328AF9C23A94C1FEECFD123BA4FB73476F0D594DCB65C6425BD186051", AssertionError, "sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 0", ), ( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "00000000000000000000000000000000000000000000000000000000000000017615FBAF5AE28864013C099742DEADB4DBA87F11AC6754F93780D5A1837CF197", AssertionError, "sG - eP is infinite. Test fails in single verification if has_even_y(inf) is defined as true and x(inf) as 1", ), ( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "4A298DACAE57395A15D0795DDBFD1DCB564DA82B0F269BC70A74F8220429BA1D69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", ValueError, "sig[0:32] is not an X coordinate on the curve", ), ( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F69E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", ValueError, "sig[0:32] is equal to field size", ), ( "DFF1D77F2A671C5F36183726DB2341BE58FEAE1DA2DECED843240F7B502BA659", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E177769FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", ValueError, "sig[32:64] is equal to curve order", ), ( "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC30", "243F6A8885A308D313198A2E03707344A4093822299F31D0082EFA98EC4E6C89", "6CFF5C3BA86C69EA4B7376F31A9BCB4F74C1976089B2D9963DA2E5543E17776969E89B4C5564D00349106B8497785DD7D1D713A8AE82B32FA79D5F7FC407D39B", ValueError, "public key is not a valid X coordinate because it exceeds the field size", ), ] for bip340_pk, message, signature, error, comment in tests: with self.assertRaises(error): print(comment) public_key = S256Point.parse(bytes.fromhex(bip340_pk)) msg = bytes.fromhex(message) sig = SchnorrSignature.parse(bytes.fromhex(signature)) assert public_key.verify_schnorr(msg, sig)
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 nonce_sums(self, nonce_point_pairs): sum_1 = S256Point.combine([n[0] for n in nonce_point_pairs]) sum_2 = S256Point.combine([n[1] for n in nonce_point_pairs]) return sum_1, sum_2