def test_one_prvkey_multiple_addresses(self): msg = "Paolo is afraid of ephemeral random numbers" # Compressed WIF wif = b"Kx45GeUBSMPReYQwgXiKhG9FzNXrnCeutJp4yjTd5kKxCitadm3C" pubkey, network = pubkeyinfo_from_prvkey(wif) address1 = p2pkh(pubkey, network) address2 = p2wpkh_p2sh(pubkey, network) address3 = p2wpkh(pubkey, network) # sign with no address (or compressed P2PKH) sig1 = bms.sign(msg, wif) # True for Bitcoin Core self.assertTrue(bms.verify(msg, address1, sig1)) # True for Electrum p2wpkh_p2sh self.assertTrue(bms.verify(msg, address2, sig1)) # True for Electrum p2wpkh self.assertTrue(bms.verify(msg, address3, sig1)) # sign with p2wpkh_p2sh address (BIP137) sig2 = bms.sign(msg, wif, address2) # False for Bitcoin Core self.assertFalse(bms.verify(msg, address1, sig2)) # True for BIP137 p2wpkh_p2sh self.assertTrue(bms.verify(msg, address2, sig2)) # False for BIP137 p2wpkh self.assertFalse(bms.verify(msg, address3, sig2)) # sign with p2wpkh address (BIP137) sig3 = bms.sign(msg, wif, address3) # False for Bitcoin Core self.assertFalse(bms.verify(msg, address1, sig3)) # False for BIP137 p2wpkh_p2sh self.assertFalse(bms.verify(msg, address2, sig3)) # True for BIP137 p2wpkh self.assertTrue(bms.verify(msg, address3, sig3)) # uncompressed WIF / P2PKH address q, network, _ = prvkeyinfo_from_prvkey(wif) wif2 = wif_from_prvkey(q, network, False) pubkey, network = pubkeyinfo_from_prvkey(wif2) address4 = p2pkh(pubkey, network) # sign with uncompressed P2PKH sig4 = bms.sign(msg, wif2, address4) # False for Bitcoin Core compressed p2pkh self.assertFalse(bms.verify(msg, address1, sig4)) # False for BIP137 p2wpkh_p2sh self.assertFalse(bms.verify(msg, address2, sig4)) # False for BIP137 p2wpkh self.assertFalse(bms.verify(msg, address3, sig4)) # True for Bitcoin Core uncompressed p2pkh self.assertTrue(bms.verify(msg, address4, sig4)) self.assertRaises(ValueError, bms.sign, msg, wif2, address1) self.assertRaises(ValueError, bms.sign, msg, wif, address4)
def test_msgsign_p2pkh(self): msg = "test message" # sigs are taken from (Electrum and) Bitcoin Core q = "ca978112ca1bbdcafac231b39a23dc4da786eff8147c4e72b9807785afee48bb" # uncompressed wif1u = wif_from_prvkey(q, "mainnet", False) self.assertEqual( wif1u, b"5KMWWy2d3Mjc8LojNoj8Lcz9B1aWu8bRofUgGwQk959Dw5h2iyw") pubkey1u, _ = pubkeyinfo_from_prvkey(wif1u) add1u = base58address.p2pkh(pubkey1u) self.assertEqual(add1u, b"1HUBHMij46Hae75JPdWjeZ5Q7KaL7EFRSD") sig1u = bms.sign(msg, wif1u) self.assertTrue(bms.verify(msg, add1u, sig1u)) self.assertEqual(sig1u[0], 27) exp_sig1u = ("G/iew/NhHV9V9MdUEn/LFOftaTy1ivGPKPKyMlr8OSokNC755fAxpST" "hNRivwTNsyY9vPUDTRYBPc2cmGd5d4y4=").encode() self.assertEqual(bms.serialize(*sig1u), exp_sig1u) # compressed wif1c = wif_from_prvkey(q, "mainnet", True) self.assertEqual( wif1c, b"L41XHGJA5QX43QRG3FEwPbqD5BYvy6WxUxqAMM9oQdHJ5FcRHcGk") pubkey1c, _ = pubkeyinfo_from_prvkey(wif1c) add1c = base58address.p2pkh(pubkey1c) self.assertEqual(add1c, b"14dD6ygPi5WXdwwBTt1FBZK3aD8uDem1FY") sig1c = bms.sign(msg, wif1c) self.assertTrue(bms.verify(msg, add1c, sig1c)) self.assertEqual(sig1c[0], 31) exp_sig1c = ("H/iew/NhHV9V9MdUEn/LFOftaTy1ivGPKPKyMlr8OSokNC755fAxpST" "hNRivwTNsyY9vPUDTRYBPc2cmGd5d4y4=").encode() self.assertEqual(bms.serialize(*sig1c), exp_sig1c) self.assertFalse(bms.verify(msg, add1c, sig1u)) self.assertFalse(bms.verify(msg, add1u, sig1c)) rf, r, s, = sig1c sig1c_malleated_rf = bms.serialize(rf + 1, r, s) self.assertFalse(bms.verify(msg, add1c, sig1c_malleated_rf)) sig1c_malleated_s = bms.serialize(rf, r, ec.n - s) self.assertFalse(bms.verify(msg, add1c, sig1c_malleated_s)) sig1c_malleated_rf_s = bms.serialize(rf + 1, r, ec.n - s) self.assertTrue(bms.verify(msg, add1c, sig1c_malleated_rf_s))
def test_p2pkh_from_wif(self): seed = b"00" * 32 # better be random rxprv = bip32.rootxprv_from_seed(seed) path = "m/0h/0h/12" xprv = bip32.derive(rxprv, path) wif = wif_from_prvkey(xprv) self.assertEqual( wif, b"KyLk7s6Z1FtgYEVp3bPckPVnXvLUWNCcVL6wNt3gaT96EmzTKZwP") pubkey, _ = pubkeyinfo_from_prvkey(wif) address = p2pkh(pubkey) xpub = bip32.xpub_from_xprv(xprv) address2 = slip32.address_from_xpub(xpub) self.assertEqual(address, address2) self.assertRaises(ValueError, wif_from_prvkey, xpub)
def test_p2pkh_from_wif() -> None: seed = b"\x00" * 32 # better be a documented test case rxprv = bip32.rootxprv_from_seed(seed) path = "m/0h/0h/12" xprv = bip32.derive(rxprv, path) wif = wif_from_prvkey(xprv) assert wif == b"L2L1dqRmkmVtwStNf5wg8nnGaRn3buoQr721XShM4VwDbTcn9bpm" pubkey, _ = pubkeyinfo_from_prvkey(wif) address = p2pkh(pubkey) xpub = bip32.xpub_from_xprv(xprv) address2 = slip132.address_from_xpub(xpub) assert address == address2 err_msg = "not a private key: " with pytest.raises(ValueError, match=err_msg): wif_from_prvkey(xpub)
def test_p2pkh_from_wif() -> None: seed = b"00" * 32 # better be a documented test case rxprv = bip32.rootxprv_from_seed(seed) path = "m/0h/0h/12" xprv = bip32.derive(rxprv, path) wif = wif_from_prvkey(xprv) assert wif == b"KyLk7s6Z1FtgYEVp3bPckPVnXvLUWNCcVL6wNt3gaT96EmzTKZwP" pubkey, _ = pubkeyinfo_from_prvkey(wif) address = p2pkh(pubkey) xpub = bip32.xpub_from_xprv(xprv) address2 = slip132.address_from_xpub(xpub) assert address == address2 err_msg = "not a private key: " with pytest.raises(ValueError, match=err_msg): wif_from_prvkey(xpub)
def test_segwit(self): msg = "test" wif = "L4xAvhKR35zFcamyHME2ZHfhw5DEyeJvEMovQHQ7DttPTM8NLWCK" pubkey, _ = pubkeyinfo_from_prvkey(wif) p2pkh = base58address.p2pkh(pubkey) p2wpkh = bech32address.p2wpkh(pubkey) p2wpkh_p2sh = base58address.p2wpkh_p2sh(pubkey) # p2pkh base58 address (Core, Electrum, BIP137) exp_sig = ("IBFyn+h9m3pWYbB4fBFKlRzBD4eJKojgCIZSNdhLKKHPSV2/WkeV7R7IO" "I0dpo3uGAEpCz9eepXLrA5kF35MXuU=").encode() self.assertTrue(bms.verify(msg, p2pkh, exp_sig)) sig = bms.sign(msg, wif) # no address: p2pkh assumed self.assertTrue(bms.verify(msg, p2pkh, sig)) self.assertEqual(bms.serialize(*sig), exp_sig) # p2wpkh-p2sh base58 address (Electrum) self.assertTrue(bms.verify(msg, p2wpkh_p2sh, sig)) # p2wpkh bech32 address (Electrum) self.assertTrue(bms.verify(msg, p2wpkh, sig)) # p2wpkh-p2sh base58 address (BIP137) # different first letter in sig because of different rf exp_sig = ("JBFyn+h9m3pWYbB4fBFKlRzBD4eJKojgCIZSNdhLKKHPSV2/WkeV7R7IO" "I0dpo3uGAEpCz9eepXLrA5kF35MXuU=").encode() self.assertTrue(bms.verify(msg, p2wpkh_p2sh, exp_sig)) sig = bms.sign(msg, wif, p2wpkh_p2sh) self.assertTrue(bms.verify(msg, p2wpkh_p2sh, sig)) self.assertEqual(bms.serialize(*sig), exp_sig) # p2wpkh bech32 address (BIP137) # different first letter in sig because of different rf exp_sig = ("KBFyn+h9m3pWYbB4fBFKlRzBD4eJKojgCIZSNdhLKKHPSV2/WkeV7R7IO" "I0dpo3uGAEpCz9eepXLrA5kF35MXuU=").encode() self.assertTrue(bms.verify(msg, p2wpkh, exp_sig)) sig = bms.sign(msg, wif, p2wpkh) self.assertTrue(bms.verify(msg, p2wpkh, sig)) self.assertEqual(bms.serialize(*sig), exp_sig)
# No part of btclib including this file, may be copied, modified, propagated, # or distributed except according to the terms contained in the LICENSE file. from btclib.base58address import p2pkh, p2wpkh_p2sh from btclib.base58wif import wif_from_prvkey from btclib.bech32address import p2wpkh from btclib.bms import encode, sign, verify from btclib.to_prvkey import prvkeyinfo_from_prvkey from btclib.to_pubkey import pubkeyinfo_from_prvkey msg = "Paolo is afraid of ephemeral random numbers" print("\n0. Message:", msg) wif = b"Kx45GeUBSMPReYQwgXiKhG9FzNXrnCeutJp4yjTd5kKxCitadm3C" print("1. Compressed WIF:", wif.decode()) pubkey, network = pubkeyinfo_from_prvkey(wif) print("2. Addresses") address1 = p2pkh(pubkey) print(" p2pkh:", address1.decode()) address2 = p2wpkh_p2sh(pubkey) print("p2wpkh_p2sh:", address2.decode()) address3 = p2wpkh(pubkey) print(" p2wpkh:", address3.decode()) print("\n3. Sign message with no address (or with compressed p2pkh address):") sig1 = sign(msg, wif) print(f"rf1: {sig1[0]}") print(f" r1: {hex(sig1[1]).upper()}") print(f" s1: {hex(sig1[2]).upper()}")
def test_exceptions(self): msg = "test" wif = "KwELaABegYxcKApCb3kJR9ymecfZZskL9BzVUkQhsqFiUKftb4tu" pubkey, _ = pubkeyinfo_from_prvkey(wif) address = base58address.p2pkh(pubkey) exp_sig = ("IHdKsFF1bUrapA8GMoQUbgI+Ad0ZXyX1c/yAZHmJn5hSNBi7J+TrI1615" "FG3g9JEOPGVvcfDWIFWrg2exLNtoVc=").encode() self.assertTrue(bms.verify(msg, address, exp_sig)) # Invalid recovery flag: 26 _, r, s = bms.deserialize(exp_sig) self.assertRaises(ValueError, bms.serialize, 26, r, s) # bms.serialize(26, r, s) # short exp_sig exp_sig = ("IHdKsFF1bUrapA8GMoQUbgI+Ad0ZXyX1c/yAZHmJn5hNBi7J+TrI1615F" "G3g9JEOPGVvcfDWIFWrg2exLoVc=").encode() self.assertRaises(ValueError, bms.assert_as_valid, msg, address, exp_sig) self.assertFalse(bms.verify(msg, address, exp_sig)) # Invalid recovery flag: 26 exp_sig = ("GpNLHqEKSzwXV+KwwBfQthQ848mn5qSkmGDXpqshDuPYJELOnSuRYGQQg" "BR4PpI+w2tJdD4v+hxElvAaUSqv2eU=").encode() self.assertRaises(ValueError, bms.assert_as_valid, msg, address, exp_sig) self.assertFalse(bms.verify(msg, address, exp_sig)) # bms.assert_as_valid(msg, address, exp_sig) # Invalid recovery flag: 66 exp_sig = ("QpNLHqEKSzwXV+KwwBfQthQ848mn5qSkmGDXpqshDuPYJELOnSuRYGQQg" "BR4PpI+w2tJdD4v+hxElvAaUSqv2eU=").encode() self.assertRaises(ValueError, bms.assert_as_valid, msg, address, exp_sig) self.assertFalse(bms.verify(msg, address, exp_sig)) # bms.assert_as_valid(msg, address, exp_sig) # Pubkey mismatch: compressed wif, uncompressed address wif = "Ky1XfDK2v6wHPazA6ECaD8UctEoShXdchgABjpU9GWGZDxVRDBMJ" address = "19f7adDYqhHSJm2v7igFWZAqxXHj1vUa3T" self.assertRaises(ValueError, bms.sign, msg, wif, address) # bms.sign(msg, wif, address) # Pubkey mismatch: uncompressed wif, compressed address wif = "5JDopdKaxz5bXVYXcAnfno6oeSL8dpipxtU1AhfKe3Z58X48srn" address = "1DAag8qiPLHh6hMFVu9qJQm9ro1HtwuyK5" self.assertRaises(ValueError, bms.sign, msg, wif, address) # bms.sign(msg, wif, address) msg = "test" wif = "L4xAvhKR35zFcamyHME2ZHfhw5DEyeJvEMovQHQ7DttPTM8NLWCK" pubkey, _ = pubkeyinfo_from_prvkey(wif) p2pkh = base58address.p2pkh(pubkey) p2wpkh = bech32address.p2wpkh(pubkey) p2wpkh_p2sh = base58address.p2wpkh_p2sh(pubkey) wif = "Ky1XfDK2v6wHPazA6ECaD8UctEoShXdchgABjpU9GWGZDxVRDBMJ" # Mismatch between p2pkh address and key pair self.assertRaises(ValueError, bms.sign, msg, wif, p2pkh) # bms.sign(msg, wif, p2pkh) # Mismatch between p2wpkh address and key pair self.assertRaises(ValueError, bms.sign, msg, wif, p2wpkh) # bms.sign(msg, wif, p2wpkh) # Mismatch between p2wpkh_p2sh address and key pair self.assertRaises(ValueError, bms.sign, msg, wif, p2wpkh_p2sh) # bms.sign(msg, wif, p2wpkh_p2sh) # Invalid recovery flag (39) for base58 address exp_sig = ("IHdKsFF1bUrapA8GMoQUbgI+Ad0ZXyX1c/yAZHmJn5hSNBi7J+TrI1615" "FG3g9JEOPGVvcfDWIFWrg2exLNtoVc=").encode() _, r, s = bms.deserialize(exp_sig) sig = bms.serialize(39, r, s) self.assertRaises(ValueError, bms.assert_as_valid, msg, p2pkh, sig) # bms.assert_as_valid(msg, p2pkh, sig) # Invalid recovery flag (35) for bech32 address exp_sig = ("IBFyn+h9m3pWYbB4fBFKlRzBD4eJKojgCIZSNdhLKKHPSV2/WkeV7R7IO" "I0dpo3uGAEpCz9eepXLrA5kF35MXuU=").encode() _, r, s = bms.deserialize(exp_sig) sig = bms.serialize(35, r, s) self.assertRaises(ValueError, bms.assert_as_valid, msg, p2wpkh, sig)
def test_address_from_wif(self): # uncompressed mainnet wif1 = "5J1geo9kcAUSM6GJJmhYRX1eZEjvos9nFyWwPstVziTVueRJYvW" pubkey, network = pubkeyinfo_from_prvkey(wif1) b58 = p2pkh(pubkey) self.assertEqual(b58, b"1LPM8SZ4RQDMZymUmVSiSSvrDfj1UZY9ig") self.assertRaises(ValueError, p2wpkh, pubkey) self.assertRaises(ValueError, p2wpkh_p2sh, pubkey) # compressed mainnet wif2 = "Kx621phdUCp6sgEXPSHwhDTrmHeUVrMkm6T95ycJyjyxbDXkr162" pubkey, network = pubkeyinfo_from_prvkey(wif2) b58 = p2pkh(pubkey) self.assertEqual(b58, b"1HJC7kFvXHepkSzdc8RX6khQKkAyntdfkB") b32 = p2wpkh(pubkey) self.assertEqual( h160_from_b58address(b58)[1:], witness_from_b32address(b32)[1:]) h160 = h160_from_b58address(b58)[1] b = p2wpkh_p2sh(pubkey) self.assertEqual(hash160(b"\x00\x14" + h160), h160_from_b58address(b)[1]) self.assertEqual( prvkeyinfo_from_prvkey(wif1)[0], prvkeyinfo_from_prvkey(wif2)[0]) # uncompressed testnet wif1 = "91gGn1HgSap6CbU12F6z3pJri26xzp7Ay1VW6NHCoEayNXwRpu2" pubkey, network = pubkeyinfo_from_prvkey(wif1) b58 = p2pkh(pubkey, network, None) self.assertEqual(b58, b"mvgbzkCSgKbYgaeG38auUzR7otscEGi8U7") self.assertRaises(ValueError, p2wpkh, pubkey) self.assertRaises(ValueError, p2wpkh_p2sh, pubkey) # compressed testnet wif2 = "cMzLdeGd5vEqxB8B6VFQoRopQ3sLAAvEzDAoQgvX54xwofSWj1fx" pubkey, network = pubkeyinfo_from_prvkey(wif2) b58 = p2pkh(pubkey, network, None) self.assertEqual(b58, b"n1KSZGmQgB8iSZqv6UVhGkCGUbEdw8Lm3Q") b32 = p2wpkh(pubkey, network) self.assertEqual( h160_from_b58address(b58)[1:], witness_from_b32address(b32)[1:]) h160 = h160_from_b58address(b58)[1] b = p2wpkh_p2sh(pubkey, network) self.assertEqual(hash160(b"\x00\x14" + h160), h160_from_b58address(b)[1]) self.assertEqual( prvkeyinfo_from_prvkey(wif1)[0], prvkeyinfo_from_prvkey(wif2)[0]) # uncompressed mainnet, trailing/leading spaces in string wif1 = " 5J1geo9kcAUSM6GJJmhYRX1eZEjvos9nFyWwPstVziTVueRJYvW" pubkey, network = pubkeyinfo_from_prvkey(wif1) b58 = p2pkh(pubkey) self.assertEqual(b58, b"1LPM8SZ4RQDMZymUmVSiSSvrDfj1UZY9ig") self.assertRaises(ValueError, p2wpkh, pubkey) self.assertRaises(ValueError, p2wpkh_p2sh, pubkey) # compressed mainnet, trailing/leading spaces in string wif2 = "Kx621phdUCp6sgEXPSHwhDTrmHeUVrMkm6T95ycJyjyxbDXkr162 " pubkey, network = pubkeyinfo_from_prvkey(wif2) b58 = p2pkh(pubkey) self.assertEqual(b58, b"1HJC7kFvXHepkSzdc8RX6khQKkAyntdfkB") b32 = p2wpkh(pubkey) self.assertEqual( h160_from_b58address(b58)[1:], witness_from_b32address(b32)[1:]) h160 = h160_from_b58address(b58)[1] b = p2wpkh_p2sh(pubkey) self.assertEqual(hash160(b"\x00\x14" + h160), h160_from_b58address(b)[1])