From 6bf5c793ff0ead9cdd52120da2f9347426fe76c4 Mon Sep 17 00:00:00 2001 From: Mostafa Date: Sat, 9 May 2026 17:23:04 +0800 Subject: [PATCH] test: update crypto tests --- pactus/crypto/address.py | 7 ++- pactus/crypto/bls/private_key.py | 7 ++- pactus/crypto/bls/public_key.py | 10 ++-- tests/test_crypto_bls.py | 84 ++++++++++++++++++++++---------- tests/test_crypto_ed25519.py | 80 +++++++++++++++++++----------- tests/test_crypto_secp256k1.py | 66 +++++++++++++------------ 6 files changed, 160 insertions(+), 94 deletions(-) diff --git a/pactus/crypto/address.py b/pactus/crypto/address.py index 9ae4b79..f704cb3 100644 --- a/pactus/crypto/address.py +++ b/pactus/crypto/address.py @@ -40,7 +40,12 @@ def from_string(cls, text: str) -> Address: raise ValueError(msg) typ = AddressType(typ) - if typ in (AddressType.VALIDATOR, AddressType.BLS_ACCOUNT, AddressType.ED25519_ACCOUNT): + if typ in ( + AddressType.VALIDATOR, + AddressType.BLS_ACCOUNT, + AddressType.ED25519_ACCOUNT, + AddressType.SECP256K1_ACCOUNT, + ): if len(data) != 20: msg = f"Invalid length: {len(data) + 1}" raise ValueError(msg) diff --git a/pactus/crypto/bls/private_key.py b/pactus/crypto/bls/private_key.py index 48830a6..acff385 100644 --- a/pactus/crypto/bls/private_key.py +++ b/pactus/crypto/bls/private_key.py @@ -21,8 +21,8 @@ def __init__(self, scalar: int) -> None: self.scalar = scalar % curve_order @classmethod - def from_bytes(cls, buffer: bytes) -> PrivateKey: - return cls(int.from_bytes(buffer, "big")) + def from_bytes(cls, data: bytes) -> PrivateKey: + return cls(int.from_bytes(data, "big")) @classmethod def key_gen(cls, ikm: bytes = [], key_info: bytes = b"") -> PrivateKey: @@ -59,8 +59,7 @@ def from_string(cls, text: str) -> PrivateKey: msg = "Private key data must be 32 bytes long" raise ValueError(msg) - scalar = int.from_bytes(data, "big") - return cls(scalar) + return cls.from_bytes(data) def raw_bytes(self) -> bytes: return self.scalar.to_bytes(PRIVATE_KEY_SIZE, "big") diff --git a/pactus/crypto/bls/public_key.py b/pactus/crypto/bls/public_key.py index 5eae147..1af0e1b 100644 --- a/pactus/crypto/bls/public_key.py +++ b/pactus/crypto/bls/public_key.py @@ -19,6 +19,12 @@ class PublicKey: def __init__(self, point_g2: any) -> None: self.point_g2 = point_g2 + @classmethod + def from_bytes(cls, data: str) -> PublicKey: + point_g2 = deserialize(bytes(data), is_ell2=True) + + return cls(point_g2) + @classmethod def from_string(cls, text: str) -> PublicKey: hrp, typ, data = utils.decode_to_base256_with_type(text) @@ -35,9 +41,7 @@ def from_string(cls, text: str) -> PublicKey: msg = "Public key data must be 96 bytes long" raise ValueError(msg) - point_g2 = deserialize(bytes(data), is_ell2=True) - - return cls(point_g2) + return cls.from_bytes(data) @classmethod def aggregate(cls, pubs: list[PublicKey]) -> PublicKey: diff --git a/tests/test_crypto_bls.py b/tests/test_crypto_bls.py index 3d0b59e..36a16ed 100644 --- a/tests/test_crypto_bls.py +++ b/tests/test_crypto_bls.py @@ -3,44 +3,76 @@ from pactus.crypto.bls import PrivateKey as BLSPrivateKey from pactus.crypto.bls import PublicKey as BLSPublicKey from pactus.crypto.bls import Signature as BLSSignature +from pactus.crypto import Address class TestBLSCrypto(unittest.TestCase): - def test_private_key_to_public_key(self): - prv_str = "SECRET1PDRWTLP5PX0FAHDX39GXZJP7FKZFALML0D5U9TT9KVQHDUC99CMGQQJVK67" - expected_pub_str = "public1p4u8hfytl2pj6l9rj0t54gxcdmna4hq52ncqkkqjf3arha5mlk3x4mzpyjkhmdl20jae7f65aamjrvqcvf4sudcapz52ctcwc8r9wz3z2gwxs38880cgvfy49ta5ssyjut05myd4zgmjqstggmetyuyg7v5jhx47a" + def test_encoding(self): + prv_data = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + pub_data = "a7290fc800d2d14f2dc5e5cb416bebf3267dfed1c6c3a79c6edc4ebd1e657d956daa06a2fcaafd42c94b65b32d4d43ea1368f861006829c475b7d54763a502dfd717e9d51c5cc7deae2981e56090a821c9c5bcafc129b8599203ab99031f4ce7" + val_addr_data = "01c40b914373d4fc9c1e4611ad0acd5f23abf58a0d" + acc_addr_data = "02c40b914373d4fc9c1e4611ad0acd5f23abf58a0d" + + prv_str = "SECRET1PQQQSYQCYQ5RQWZQFPG9SCRGWPUGPZYSNZS23V9CCRYDPK8QARC0SEZYD4L" + pub_str = "public1p5u5sljqq6tg57tw9uh95z6lt7vn8mlk3cmp608rwm38t68n90k2km2sx5t724l2ze99ktvedf4p75ymglpssq6pfc36m0428vwjs9h7hzl5a28zucl02u2vpu4sfp2ppe8zmet7p9xu9nysr4wvsx86vuujrva2z" + val_addr_str = "pc1pcs9ezsmn6n7fc8jxzxks4n2lyw4ltzsdc9v8qn" + acc_addr_str = "pc1zcs9ezsmn6n7fc8jxzxks4n2lyw4ltzsd9wu6hw" prv = BLSPrivateKey.from_string(prv_str) - pub = prv.public_key() - pub_str = pub.string() + pub = BLSPublicKey.from_string(pub_str) + val_addr = Address.from_string(val_addr_str) + acc_addr = Address.from_string(acc_addr_str) - self.assertEqual(pub_str, expected_pub_str) + self.assertEqual(prv_data, prv.raw_bytes().hex()) + self.assertEqual(pub_data, pub.raw_bytes().hex()) + self.assertEqual(val_addr_data, val_addr.raw_bytes().hex()) + self.assertEqual(acc_addr_data, acc_addr.raw_bytes().hex()) - def test_public_key_to_address(self): - pub_str = "public1p4u8hfytl2pj6l9rj0t54gxcdmna4hq52ncqkkqjf3arha5mlk3x4mzpyjkhmdl20jae7f65aamjrvqcvf4sudcapz52ctcwc8r9wz3z2gwxs38880cgvfy49ta5ssyjut05myd4zgmjqstggmetyuyg7v5jhx47a" - expected_acc_addr_str = "pc1z5x2a0lkt5nrrdqe0rkcv6r4pfkmdhrr3mawvua" - expected_val_addr_str = "pc1p5x2a0lkt5nrrdqe0rkcv6r4pfkmdhrr3xk73tq" + msg = b"pactus" + sig = BLSSignature.from_string( + "8bdda74336efdf43b428a3811d3d6867a19e20889c91261b02a6b950b130f5bb22621394667c27660bfed2a8719d9c52" + ) - pub = BLSPublicKey.from_string(pub_str) - acc_add_str = pub.account_address().string() - val_add_str = pub.validator_address().string() + self.assertTrue(pub.verify(msg, sig)) + self.assertEqual(sig.raw_bytes(), prv.sign(msg).raw_bytes()) + self.assertEqual(pub.raw_bytes(), prv.public_key().raw_bytes()) + self.assertEqual(val_addr.raw_bytes(), pub.validator_address().raw_bytes()) + self.assertEqual(acc_addr.raw_bytes(), pub.account_address().raw_bytes()) - self.assertEqual(acc_add_str, expected_acc_addr_str) - self.assertEqual(val_add_str, expected_val_addr_str) + def test_sign_and_verify(self): + prv1 = BLSPrivateKey.random() + prv2 = BLSPrivateKey.random() + pub1 = prv1.public_key() + pub2 = prv2.public_key() - def test_sign(self): - prv_str = "SECRET1PDRWTLP5PX0FAHDX39GXZJP7FKZFALML0D5U9TT9KVQHDUC99CMGQQJVK67" - prv = BLSPrivateKey.from_string(prv_str) - prv.public_key() msg = b"pactus" - prv.sign(msg) - BLSSignature.from_string( - "923d67a8624cbb7972b29328e15ec76cc846076ccf00a9e94d991c677846f334ae4ba4551396fbcd6d1cab7593baf3b7" - ) + sig1 = prv1.sign(msg) + sig2 = prv2.sign(msg) + + self.assertTrue(pub1.verify(msg, sig1)) + self.assertTrue(pub2.verify(msg, sig2)) + self.assertFalse(pub1.verify(msg, sig2)) + self.assertFalse(pub2.verify(msg, sig1)) + + # Verify a signature for a different message is rejected + different_msg = b"different" + sig3 = prv1.sign(different_msg) + self.assertFalse(pub1.verify(msg, sig3)) + + def test_multiple_signatures(self): + # Test that multiple signatures for the same message and key are deterministic + prv = BLSPrivateKey.random() + pub = prv.public_key() + + msg = b"pactus" + sig1 = prv.sign(msg) + sig2 = prv.sign(msg) + + # Both signatures should verify + self.assertTrue(pub.verify(msg, sig1)) + self.assertTrue(pub.verify(msg, sig2)) - # self.assertEqual(sig.string(), expected_sig.string()) - # self.assertTrue(pub.verify(msg, sig)) - # self.assertFalse(pub.verify(b"foo", sig)) + self.assertEqual(sig1.raw_bytes(), sig2.raw_bytes()) def test_key_gen(self): tests = [ diff --git a/tests/test_crypto_ed25519.py b/tests/test_crypto_ed25519.py index 994090c..4863ec3 100644 --- a/tests/test_crypto_ed25519.py +++ b/tests/test_crypto_ed25519.py @@ -3,49 +3,71 @@ from pactus.crypto.ed25519 import PrivateKey as Ed25519PrivateKey from pactus.crypto.ed25519 import PublicKey as Ed25519PublicKey from pactus.crypto.ed25519 import Signature as Ed25519Signature +from pactus.crypto import Address class TestEd25519Crypto(unittest.TestCase): - def test_private_key_to_public_key(self): - prv_str = "SECRET1RJ6STNTA7Y3P2QLQF8A6QCX05F2H5TFNE5RSH066KZME4WVFXKE7QW097LG" - expected_pub_str = ( - "public1ry2cqw5yfhmr7ve8nctgzg6wgcyc73xqr2uud486jgsq7wu253egsx6msep" - ) + def test_encoding(self): + prv_data = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + pub_data = "03a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8" + addr_data = "0396a882c41ef85a07c75a6416a57fcce95aad4a3f" + + prv_str = "SECRET1RQQQSYQCYQ5RQWZQFPG9SCRGWPUGPZYSNZS23V9CCRYDPK8QARC0SW5D8X2" + pub_str = "public1rqwss00lnecgtu8tsm5vwwj7qn9n7f43snwjs6hcamjrxgyj4xxuq5agu5g" + addr_str = "pc1rj65g93q7lpdq0366vst22l7va9d26j3l2vr0em" prv = Ed25519PrivateKey.from_string(prv_str) - pub = prv.public_key() - pub_str = pub.string() + pub = Ed25519PublicKey.from_string(pub_str) + addr = Address.from_string(addr_str) - self.assertEqual(pub_str, expected_pub_str) + self.assertEqual(prv_data, prv.raw_bytes().hex()) + self.assertEqual(pub_data, pub.raw_bytes().hex()) + self.assertEqual(addr_data, addr.raw_bytes().hex()) - def test_public_key_to_address(self): - pub_str = "public1ry2cqw5yfhmr7ve8nctgzg6wgcyc73xqr2uud486jgsq7wu253egsx6msep" - expected_acc_addr_str = "pc1reer4damrrdxznmrrl7a9acy7x5cwe6dyt8ftv4" + msg = b"pactus" + sig = Ed25519Signature.from_string( + "1fc2c800499342d08242db9c3eb654027cb7b821e6af9ede56dfdb67e824f15bddb419d2db3fd5aaf3ef1a9ebb9a9deb749380f0d6a110cbe95319fe9f794305" + ) - pub = Ed25519PublicKey.from_string(pub_str) - acc_add_str = pub.account_address().string() + self.assertTrue(pub.verify(msg, sig)) + self.assertEqual(sig.raw_bytes(), prv.sign(msg).raw_bytes()) + self.assertEqual(pub.raw_bytes(), prv.public_key().raw_bytes()) + self.assertEqual(addr.raw_bytes(), pub.account_address().raw_bytes()) - self.assertEqual(acc_add_str, expected_acc_addr_str) + def test_sign_and_verify(self): + prv1 = Ed25519PrivateKey.random() + prv2 = Ed25519PrivateKey.random() + pub1 = prv1.public_key() + pub2 = prv2.public_key() - def test_sign(self): - prv_str = "secret1r0up878rawjec2evjnd4k42a4g4pcardesjk48jtn64qwjnfv7veqal53e2" - prv = Ed25519PrivateKey.from_string(prv_str) - pub = prv.public_key() msg = b"pactus" - sig = prv.sign(msg) + sig1 = prv1.sign(msg) + sig2 = prv2.sign(msg) - valid_sig = Ed25519Signature.from_string( - "55eaa9656158874bff726c8d62abe0a5b66d2434705f46b58c905a42f1b39fb95f640fbacac97e6e6862220fe7b4f249a0f79dc3d37b4460156c58580778b70e" - ) + self.assertTrue(pub1.verify(msg, sig1)) + self.assertTrue(pub2.verify(msg, sig2)) + self.assertFalse(pub1.verify(msg, sig2)) + self.assertFalse(pub2.verify(msg, sig1)) - invalid_sig = Ed25519Signature.from_string( - "5a61ecb3c08825010678f12c036cec2e1dd1b8767ed9fd95a97f560dfd6196b600fdc7bba22f13eae19ca578b920eb807eb6cd956d55f1d778fee75155d4ea07" - ) + # Verify a signature for a different message is rejected + different_msg = b"different" + sig3 = prv1.sign(different_msg) + self.assertFalse(pub1.verify(msg, sig3)) + + def test_multiple_signatures(self): + # Test that multiple signatures for the same message and key are deterministic + prv = Ed25519PrivateKey.random() + pub = prv.public_key() + + msg = b"pactus" + sig1 = prv.sign(msg) + sig2 = prv.sign(msg) + + # Both signatures should verify + self.assertTrue(pub.verify(msg, sig1)) + self.assertTrue(pub.verify(msg, sig2)) - self.assertTrue(pub.verify(msg, valid_sig)) - self.assertFalse(pub.verify(msg, invalid_sig)) - self.assertEqual(sig.string(), valid_sig.string()) - self.assertEqual(sig.raw_bytes(), valid_sig.raw_bytes()) + self.assertEqual(sig1.raw_bytes(), sig2.raw_bytes()) if __name__ == "__main__": diff --git a/tests/test_crypto_secp256k1.py b/tests/test_crypto_secp256k1.py index e6273ae..0a10b0c 100644 --- a/tests/test_crypto_secp256k1.py +++ b/tests/test_crypto_secp256k1.py @@ -3,52 +3,56 @@ from pactus.crypto.secp256k1 import PrivateKey as Secp256k1PrivateKey from pactus.crypto.secp256k1 import PublicKey as Secp256k1PublicKey from pactus.crypto.secp256k1 import Signature as Secp256k1Signature +from pactus.crypto import Address class TestSecp256k1Crypto(unittest.TestCase): - def test_private_key_to_public_key(self): - prv_str = "secret1yqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsd25y3e" - expected_pub_str = ( - "public1yqvdcf32k0vfxgsyet5ldt246q4jaw8scx3sysx0lnstlt6w4m5rc7k3ysjp" - ) - - prv = Secp256k1PrivateKey.from_string(prv_str) - pub = prv.public_key() - pub_str = pub.string() + def test_encoding(self): + prv_data = "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f" + pub_data = "036d6caac248af96f6afa7f904f550253a0f3ef3f5aa2fe6838a95b216691468e2" + addr_data = "042bc1db7e0797c45b918dc401093c9257c6012b4c" - self.assertEqual(pub_str, expected_pub_str) - - def test_public_key_to_address(self): - pub_str = "public1yqvdcf32k0vfxgsyet5ldt246q4jaw8scx3sysx0lnstlt6w4m5rc7k3ysjp" - expected_acc_addr_str = "pc1yj7ag28h54jf4e09nnednjhgmg60srnvj7uu39v" + prv_str = "SECRET1YQQQSYQCYQ5RQWZQFPG9SCRGWPUGPZYSNZS23V9CCRYDPK8QARC0SPVXU8Z" + pub_str = "public1yqdkke2kzfzheda405lusfa2sy5aq70hn7k4zle5r322my9nfz35wyfamrfs" + addr_str = "pc1y90qakls8jlz9hyvdcsqsj0yj2lrqz26vqu7l0z" + prv = Secp256k1PrivateKey.from_string(prv_str) pub = Secp256k1PublicKey.from_string(pub_str) - acc_add_str = pub.account_address().string() + addr = Address.from_string(addr_str) - self.assertEqual(acc_add_str, expected_acc_addr_str) + self.assertEqual(prv_data, prv.raw_bytes().hex()) + self.assertEqual(pub_data, pub.raw_bytes().hex()) + self.assertEqual(addr_data, addr.raw_bytes().hex()) - def test_sign_and_verify(self): - prv_str = "secret1yqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqsd25y3e" - prv = Secp256k1PrivateKey.from_string(prv_str) - pub = prv.public_key() msg = b"pactus" - sig = prv.sign(msg) - - # Verify the signature is correct length (64 bytes) - self.assertEqual(len(sig.raw_bytes()), 64) + sig = Secp256k1Signature.from_string( + "16e6f8bcdb92964a35773aae200628a5b470b6488d42ceef6538da0b4ffd3b42098dd821eea96f66ba02c9c4473443ab51c411ab78adfbb90d53b07ca1d6862b" + ) - # Verify the signature verifies correctly self.assertTrue(pub.verify(msg, sig)) + self.assertEqual(sig.raw_bytes(), prv.sign(msg).raw_bytes()) + self.assertEqual(pub.raw_bytes(), prv.public_key().raw_bytes()) + self.assertEqual(addr.raw_bytes(), pub.account_address().raw_bytes()) + + def test_sign_and_verify(self): + prv1 = Secp256k1PrivateKey.random() + prv2 = Secp256k1PrivateKey.random() + pub1 = prv1.public_key() + pub2 = prv2.public_key() + + msg = b"pactus" + sig1 = prv1.sign(msg) + sig2 = prv2.sign(msg) - # Verify an invalid signature is rejected - invalid_sig_bytes = b"\x00" * 64 - invalid_sig = Secp256k1Signature.from_string(invalid_sig_bytes.hex()) - self.assertFalse(pub.verify(msg, invalid_sig)) + self.assertTrue(pub1.verify(msg, sig1)) + self.assertTrue(pub2.verify(msg, sig2)) + self.assertFalse(pub1.verify(msg, sig2)) + self.assertFalse(pub2.verify(msg, sig1)) # Verify a signature for a different message is rejected different_msg = b"different" - sig2 = prv.sign(different_msg) - self.assertFalse(pub.verify(msg, sig2)) + sig3 = prv1.sign(different_msg) + self.assertFalse(pub1.verify(msg, sig3)) def test_key_generation(self): # Test random key generation