def test_compression(self): prv, pub = generate_keypair() encoded = pub.encode(compressed=True) self.assertEqual(PublicKey.decode(encoded), pub) encoded = pub.encode(compressed=False) self.assertEqual(PublicKey.decode(encoded), pub)
def test_digest_p2sh_p2wpkh(self): # https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#p2sh-p2wpkh tx = Transaction.from_hex( '0100000001db6b1b20aa0fd7b23880be2ecbd4a98130974cf4748fb66092ac4d3ceb1a54770100000000feffffff02b8b4eb0b000000001976a914a457b684d7f0d539a46a45bbc043f35b59d0d96388ac0008af2f000000001976a914fd270b1ee6abcaea97fea7ad0402e8bd8ad6d77c88ac92040000' ) ref = Output( 10 * 10**8, hex_to_bytes('a9144733f37cf4db86fbc2efed2500b4f4e49f31202387')) inp = tx.inputs[0] inp._referenced_output = ref inp.script = serialize( hex_to_bytes('001479091972186c449eb1ded22b78e40d009bdf0089')) sighash = tx.sighash(0) self.assertEqual( bytes_to_hex(sighash), '64f3b0f4dd2bb3aa1ce8566d220cc74dda9df97d8490cc81d89d735c92e59fb6') sig = Signature.from_hex( '3044022047ac8e878352d3ebbde1c94ce3a10d057c24175747116f8288e5d794d12d482f0220217f36a485cae903c713331d877c1f64677e3622ad4010726870540656fe9dcb' ) pub = PublicKey.from_hex( '03ad1d8e89212f0b92c74d23bb710c00662ad1470198ac48c43f7d6f93a2a26873' ) self.assertTrue(sig.verify_hash(sighash, pub))
def child(self, i: int) -> 'Xpub': hardened = i >= 1 << 31 if hardened: raise KeyDerivationError( 'Cannot derive a hardened key from an extended public key') I = hmac.new(key=self.code, msg=self.keydata() + int_to_bytes(i).rjust(4, b'\x00'), digestmod=hashlib.sha512).digest() I_L, I_R = I[:32], I[32:] key = PrivateKey(I_L).to_public().point + self.key.point ret_code = I_R path = self.path + f'/{i}' # TODO add point at infinity check return Xpub(PublicKey(key), ret_code, depth=self.depth + 1, i=i, parent=self.fingerprint(), path=path, addresstype=self.type.value)
def OP_CHECKSIG(self): """https://en.bitcoin.it/wiki/OP_CHECKSIG""" pub = PublicKey.decode(self.pop()) extended_sig = self.pop() sig = Signature.decode(extended_sig[:-1]) hashcode = SIGHASH(extended_sig[-1]) sighash = self.tx.sighash(i=self.index, hashcode=hashcode) self.push(sig.verify_hash(sighash, pub))
def test_p2wsh(self): """https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#examples""" pubkey = PublicKey.from_hex( '0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798' ) script = push(pubkey.encode( compressed=True)) + OP.CHECKSIG.byte # <key> <OP_CHECKSIG> address = script_to_address(script, 'P2WSH') self.assertEqual( address, 'bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3') self.assertEqual(address_type(address), ADDRESS.P2WSH)
def test_p2wpkh(self): """https://github.com/bitcoin/bips/blob/master/bip-0173.mediawiki#examples""" pubkey = PublicKey.from_hex( '0279BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798' ) self.assertEqual( bech32.encode(self.hrp, self.witver, hash160(pubkey.encode(compressed=True))), 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4') address = pubkey_to_address(pubkey, version='P2WPKH') self.assertEqual(address, 'bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4') self.assertEqual(address_type(address), ADDRESS.P2WPKH)
def deserialize(cls, bts: bytes) -> 'ExtendedKey': def read(n): nonlocal bts data, bts = bts[:n], bts[n:] return data net = read(4) is_private = net in network('extended_prv').values() is_public = net in network('extended_pub').values() assert is_public ^ is_private, f'Invalid network bytes : {bytes_to_hex(net)}' address_lookup = { val: key for key, val in (network('extended_prv') if is_private else network('extended_pub')).items() } constructor = Xprv if is_private else Xpub depth = bytes_to_int(read(1)) assert depth in range(256), f'Invalid depth : {depth}' fingerprint = read(4) i = bytes_to_int(read(4)) if depth == 0: i = None path = None else: ih = f'{i}' if i < 2**31 else f"{i - 2**31}h" path = '/'.join([constructor.root_path] + ['x' for _ in range(depth - 1)] + [ih]) code = read(32) key = read(33) key = PrivateKey(key) if is_private else PublicKey.decode(key) assert not bts, 'Leftover bytes' return constructor(key, code, depth=depth, i=i, parent=fingerprint, path=path, addresstype=address_lookup[net])
def OP_CHECKMULTISIG(self): # Multisig m out of n # The stack at this point should look something like this # ['', # '3045022100c38f1d0e340f4308b7f6e4bef0c8668e84793370924844a1076cc986f37047af02207cc29b61e85dc580ce85e01858e2e47eb3b8a80472ad784eb74538045e8172e801', # '30450221009a6abea495730976b69f255282ee0c488e49769138b7048e749dd5215bdf8120022069f690fcaf5dba05f0537911b16b2868087440eb55a19dc6e89bcb83f1f35c6501', # 2, # '02d271610ba72d9b0948ea0821fac77e0e6d10234a266b4828671a86a59073bb30', # '0359446555d1c389782468191250c007a98393eb6e9db64649cd7ed1e7f9ca0cf3', # '023779ee80b4a940503b1d630e7a3934503eecba5d571111f30841cdfbce0e8397', # 3] n = self.pop() keys = [PublicKey.decode(self.pop()) for _ in range(n)] m = self.pop() raw_signatures = [self.pop() for _ in range(m)] _ = self.pop( ) # extra bytes in stack due to original implementation bug valid_signatures = [] for raw_sig in raw_signatures: sig, hashcode = Signature.decode(raw_sig[:-1]), SIGHASH( raw_sig[-1]) sighash = self.tx.sighash(self.index, script=self.scriptPubKey, hashcode=hashcode) for pub in keys: valid = sig.verify_hash(sighash, pub) if valid: valid_signatures.append(valid) break else: valid_signatures.append(False) self.push(sum(valid_signatures) >= m)
def test_digest_p2wpkh(self): # https://github.com/bitcoin/bips/blob/master/bip-0143.mediawiki#native-p2wpkh tx = Transaction.from_hex( '0100000002fff7f7881a8099afa6940d42d1e7f6362bec38171ea3edf433541db4e4ad969f0000000000eeffffffef51e1b804cc89d182d279655c3aa89e815b1b309fe287d9b2b55d57b90ec68a0100000000ffffffff02202cb206000000001976a9148280b37df378db99f66f85c95a783a76ac7a6d5988ac9093510d000000001976a9143bde42dbee7e4dbe6a21b2d50ce2f0167faa815988ac11000000' ) ref = Output( 6 * 10**8, hex_to_bytes('00141d0f172a0ecb48aee1be1f2687d2963ae33f71a1')) tx.inputs[1]._referenced_output = ref sighash = sha256(sha256(tx.signature_form_segwit(1))) self.assertEqual( bytes_to_hex(sighash), 'c37af31116d1b27caf68aae9e3ac82f1477929014d5b917657d0eb49478cb670') public = PublicKey.from_hex( '025476c2e83188368da1ff3e292e7acafcdb3566bb0ad253f62fc70f07aeee6357' ) sig = Signature.from_hex( '304402203609e17b84f6a7d30c80bfa610b5b4542f32a8a0d5447a12fb1366d7f01cc44a0220573a954c4518331561406f90300e8f3358f51928d43c212a8caed02de67eebee' ) self.assertTrue(sig.verify_hash(sighash, public))
def pubkey_to_bech32(pub: PublicKey, witver: int) -> str: """https://github.com/bitcoin/bips/blob/master/bip-0141.mediawiki#witness-program""" witprog = hash160(pub.encode(compressed=True)) return bech32.encode(network('hrp'), witver, witprog)