From 3be3c2212bc681d71074ccf6831636338ac9ad71 Mon Sep 17 00:00:00 2001 From: mhchia Date: Fri, 24 May 2019 18:34:51 +0800 Subject: [PATCH 1/8] Add wrapper for bls from Chia network --- py_ecc/bls_chia/__init__.py | 8 ++ py_ecc/bls_chia/api.py | 114 +++++++++++++++++++++++ tests/test_bls_chia.py | 176 ++++++++++++++++++++++++++++++++++++ 3 files changed, 298 insertions(+) create mode 100644 py_ecc/bls_chia/__init__.py create mode 100644 py_ecc/bls_chia/api.py create mode 100644 tests/test_bls_chia.py diff --git a/py_ecc/bls_chia/__init__.py b/py_ecc/bls_chia/__init__.py new file mode 100644 index 00000000..f52c3de1 --- /dev/null +++ b/py_ecc/bls_chia/__init__.py @@ -0,0 +1,8 @@ +from .api import ( # noqa: F401 + aggregate_pubkeys, + aggregate_signatures, + privtopub, + sign, + verify, + verify_multiple, +) diff --git a/py_ecc/bls_chia/api.py b/py_ecc/bls_chia/api.py new file mode 100644 index 00000000..9100a523 --- /dev/null +++ b/py_ecc/bls_chia/api.py @@ -0,0 +1,114 @@ +from typing import ( + Dict, + Sequence, +) + +import blspy as bls_chia + +from eth_typing import ( + BLSPubkey, + BLSSignature, + Hash32, +) +from eth_utils import ( + ValidationError, +) + + +# FIXME: Temporarily introduced to workaround the de/serialization issue when +# aggregating signatures +_sig_map: Dict[bytes, bls_chia.Signature] = {} + + +def _domain_to_bytes(domain: int) -> bytes: + return domain.to_bytes(8, 'big') # bytes8 + + +def _privkey_int_to_bytes(privkey: int) -> bytes: + return (privkey + 1).to_bytes(bls_chia.PrivateKey.PRIVATE_KEY_SIZE, "big") + + +def _hash_to_be_signed(message_hash: Hash32, domain: int) -> bytes: + domain_in_bytes = _domain_to_bytes(domain) + return message_hash + domain_in_bytes + + +def sign(message_hash: Hash32, + privkey: int, + domain: int) -> BLSSignature: + privkey_chia = bls_chia.PrivateKey.from_bytes(_privkey_int_to_bytes(privkey)) + hash_to_be_signed = _hash_to_be_signed(message_hash, domain) + sig_chia = privkey_chia.sign(hash_to_be_signed) + sig_chia_bytes = sig_chia.serialize() + # FIXME: Temporarily introduced to workaround the de/serialization issue when + # aggregating signatures + global _sig_map + _sig_map[sig_chia_bytes] = sig_chia + return sig_chia_bytes + + +def privtopub(k: int) -> BLSPubkey: + privkey_chia = bls_chia.PrivateKey.from_bytes(_privkey_int_to_bytes(k)) + return privkey_chia.get_public_key().serialize() + + +def verify(message_hash: Hash32, pubkey: BLSPubkey, signature: BLSSignature, domain: int) -> bool: + hash_to_be_signed = _hash_to_be_signed(message_hash, domain) + pubkey_chia = bls_chia.PublicKey.from_bytes(pubkey) + signature_chia = bls_chia.Signature.from_bytes(signature) + signature_chia.set_aggregation_info( + bls_chia.AggregationInfo.from_msg(pubkey_chia, hash_to_be_signed) + ) + return signature_chia.verify() + + +def aggregate_signatures(signatures: Sequence[BLSSignature]) -> BLSSignature: + # FIXME: Temporarily introduced to workaround the de/serialization issue when + # aggregating signatures + global _sig_map + signatures_chia = list( + map( + lambda x: _sig_map[x] if x in _sig_map else bls_chia.Signature.from_bytes(x), + signatures, + ) + ) + aggregated_signature = bls_chia.Signature.aggregate(signatures_chia) + aggregated_signature_bytes = aggregated_signature.serialize() + _sig_map[aggregated_signature_bytes] = aggregated_signature + return aggregated_signature_bytes + + +def aggregate_pubkeys(pubkeys: Sequence[BLSPubkey]) -> BLSPubkey: + pubkeys_chia = list(map(bls_chia.PublicKey.from_bytes, pubkeys)) + aggregated_pubkey_chia = bls_chia.PublicKey.aggregate(pubkeys_chia) + return aggregated_pubkey_chia.serialize() + + +def verify_multiple(pubkeys: Sequence[BLSPubkey], + message_hashes: Sequence[Hash32], + signature: BLSSignature, + domain: int) -> bool: + + len_msgs = len(message_hashes) + + if len(pubkeys) != len_msgs: + raise ValidationError( + "len(pubkeys) (%s) should be equal to len(message_hashes) (%s)" % ( + len(pubkeys), len_msgs + ) + ) + + message_hashes_with_domain = [ + message_hash + _domain_to_bytes(domain) + for message_hash in message_hashes + ] + pubkeys_chia = map(bls_chia.PublicKey.from_bytes, pubkeys) + aggregate_infos = [ + bls_chia.AggregationInfo.from_msg(pubkey_chia, message_hash) + for pubkey_chia, message_hash in zip(pubkeys_chia, message_hashes_with_domain) + ] + merged_info = bls_chia.AggregationInfo.merge_infos(aggregate_infos) + + signature_chia = bls_chia.Signature.from_bytes(signature) + signature_chia.set_aggregation_info(merged_info) + return signature_chia.verify() diff --git a/tests/test_bls_chia.py b/tests/test_bls_chia.py new file mode 100644 index 00000000..f88cf4fe --- /dev/null +++ b/tests/test_bls_chia.py @@ -0,0 +1,176 @@ +import blspy as bls_chia + +from eth_utils import ( + big_endian_to_int, +) +import pytest + +from py_ecc.bls.hash import ( + hash_eth2, +) +from py_ecc.bls.constants import ( + POW_2_381, + POW_2_382, + POW_2_383, +) +from py_ecc.fields import ( + optimized_bls12_381_FQ as FQ, + optimized_bls12_381_FQ2 as FQ2, +) +from py_ecc.optimized_bls12_381 import ( + G1, + G2, + Z1, + Z2, + b, + b2, + is_on_curve, + multiply, + normalize, + field_modulus as q, +) + +from py_ecc.bls_bindings.api import ( + _domain_to_bytes, + _privkey_int_to_bytes, + privtopub, + sign, + verify, + verify_multiple, + aggregate_pubkeys, + aggregate_signatures, +) + + +def assert_privkey(obj): + assert isinstance(obj, int) and obj < 2 ** (8 * 32) + + +def assert_pubkey(obj): + assert isinstance(obj, bytes) and len(obj) == 48 + + +def assert_signature(obj): + assert isinstance(obj, bytes) and len(obj) == 96 + + +def test_sanity(): + msg_0 = b"\x32" * 32 + domain = 123 + + # Test: Verify the basic sign/verify process + privkey_0 = 5566 + sig_0 = sign(msg_0, privkey_0, domain) + assert_signature(sig_0) + pubkey_0 = privtopub(privkey_0) + assert_pubkey(pubkey_0) + assert verify(msg_0, pubkey_0, sig_0, domain) + + privkey_1 = 5567 + sig_1 = sign(msg_0, privkey_1, domain) + pubkey_1 = privtopub(privkey_1) + sig_1_chia_from_bytes = bls_chia.Signature.from_bytes(sig_1) + assert verify(msg_0, pubkey_1, sig_1, domain) + + # Test: Verify signatures are correctly aggregated + aggregated_signature = aggregate_signatures([sig_0, sig_1]) + assert_signature(aggregated_signature) + + # Test: Verify pubkeys are correctly aggregated + aggregated_pubkey = aggregate_pubkeys([pubkey_0, pubkey_1]) + assert_pubkey(aggregated_pubkey) + + # Test: Verify with `aggregated_signature` and `aggregated_pubkey` + assert verify(msg_0, aggregated_pubkey, aggregated_signature, domain) + + # Test: `verify_multiple` + msg_1 = b"x22" * 32 + privkey_2 = 55688 + sig_2 = sign(msg_1, privkey_2, domain) + assert_signature(sig_2) + pubkey_2 = privtopub(privkey_2) + assert_pubkey(pubkey_2) + sig_1_2 = aggregate_signatures([sig_1, sig_2]) + assert verify_multiple( + pubkeys=[pubkey_1, pubkey_2], + message_hashes=[msg_0, msg_1], + signature=sig_1_2, + domain=domain, + ) + + +@pytest.mark.parametrize( + 'privkey', + [ + (1), + (5), + (124), + (735), + (127409812145), + (90768492698215092512159), + (0), + ] +) +def test_bls_core(privkey): + domain = 0 + p1 = multiply(G1, privkey) + p2 = multiply(G2, privkey) + msg = str(privkey).encode('utf-8') + sig = sign(msg, privkey, domain=domain) + pub = privtopub(privkey) + assert verify(msg, pub, sig, domain=domain) + + +@pytest.mark.parametrize( + 'msg, privkeys', + [ + (b'\x12' * 32, [1, 5, 124, 735, 127409812145, 90768492698215092512159, 0]), + (b'\x34' * 32, [42, 666, 1274099945, 4389392949595]), + ] +) +def test_signature_aggregation(msg, privkeys): + domain = 0 + sigs = [sign(msg, k, domain=domain) for k in privkeys] + pubs = [privtopub(k) for k in privkeys] + aggsig = aggregate_signatures(sigs) + aggpub = aggregate_pubkeys(pubs) + assert verify(msg, aggpub, aggsig, domain=domain) + + +@pytest.mark.parametrize( + 'msg_1, msg_2', + [ + (b'\x12' * 32, b'\x34' * 32) + ] +) +@pytest.mark.parametrize( + 'privkeys_1, privkeys_2', + [ + (tuple(range(10)), tuple(range(10))), + ((0, 1, 2, 3), (4, 5, 6, 7)), + ((0, 1, 2, 3), (2, 3, 4, 5)), + ] +) +def test_multi_aggregation(msg_1, msg_2, privkeys_1, privkeys_2): + domain = 0 + + sigs_1 = [sign(msg_1, k, domain=domain) for k in privkeys_1] # signatures to msg_1 + pubs_1 = [privtopub(k) for k in privkeys_1] + aggsig_1 = aggregate_signatures(sigs_1) + aggpub_1 = aggregate_pubkeys(pubs_1) # sig_1 to msg_1 + + sigs_2 = [sign(msg_2, k, domain=domain) for k in privkeys_2] # signatures to msg_2 + pubs_2 = [privtopub(k) for k in privkeys_2] + aggsig_2 = aggregate_signatures(sigs_2) + aggpub_2 = aggregate_pubkeys(pubs_2) # sig_2 to msg_2 + + message_hashes = [msg_1, msg_2] + pubs = [aggpub_1, aggpub_2] + aggsig = aggregate_signatures([aggsig_1, aggsig_2]) + + assert verify_multiple( + pubkeys=pubs, + message_hashes=message_hashes, + signature=aggsig, + domain=domain, + ) From 6bb527f47c72ff4774346d4c2aa092d07d48fddd Mon Sep 17 00:00:00 2001 From: mhchia Date: Mon, 27 May 2019 19:27:38 +0800 Subject: [PATCH 2/8] Fix wrong import path, and mypy --- py_ecc/bls_chia/__init__.py | 8 -------- py_ecc/bls_chia/api.py | 24 ++++++++++++------------ tests/test_bls_chia.py | 2 +- 3 files changed, 13 insertions(+), 21 deletions(-) diff --git a/py_ecc/bls_chia/__init__.py b/py_ecc/bls_chia/__init__.py index f52c3de1..e69de29b 100644 --- a/py_ecc/bls_chia/__init__.py +++ b/py_ecc/bls_chia/__init__.py @@ -1,8 +0,0 @@ -from .api import ( # noqa: F401 - aggregate_pubkeys, - aggregate_signatures, - privtopub, - sign, - verify, - verify_multiple, -) diff --git a/py_ecc/bls_chia/api.py b/py_ecc/bls_chia/api.py index 9100a523..0a3e0cc7 100644 --- a/py_ecc/bls_chia/api.py +++ b/py_ecc/bls_chia/api.py @@ -1,6 +1,7 @@ from typing import ( Dict, Sequence, + cast, ) import blspy as bls_chia @@ -25,6 +26,7 @@ def _domain_to_bytes(domain: int) -> bytes: def _privkey_int_to_bytes(privkey: int) -> bytes: + # FIXME: workaround due to the privkey in Chia-Network BLS starts from 1 return (privkey + 1).to_bytes(bls_chia.PrivateKey.PRIVATE_KEY_SIZE, "big") @@ -44,12 +46,12 @@ def sign(message_hash: Hash32, # aggregating signatures global _sig_map _sig_map[sig_chia_bytes] = sig_chia - return sig_chia_bytes + return cast(BLSSignature, sig_chia_bytes) def privtopub(k: int) -> BLSPubkey: privkey_chia = bls_chia.PrivateKey.from_bytes(_privkey_int_to_bytes(k)) - return privkey_chia.get_public_key().serialize() + return cast(BLSPubkey, privkey_chia.get_public_key().serialize()) def verify(message_hash: Hash32, pubkey: BLSPubkey, signature: BLSSignature, domain: int) -> bool: @@ -59,29 +61,27 @@ def verify(message_hash: Hash32, pubkey: BLSPubkey, signature: BLSSignature, dom signature_chia.set_aggregation_info( bls_chia.AggregationInfo.from_msg(pubkey_chia, hash_to_be_signed) ) - return signature_chia.verify() + return cast(bool, signature_chia.verify()) def aggregate_signatures(signatures: Sequence[BLSSignature]) -> BLSSignature: # FIXME: Temporarily introduced to workaround the de/serialization issue when # aggregating signatures global _sig_map - signatures_chia = list( - map( - lambda x: _sig_map[x] if x in _sig_map else bls_chia.Signature.from_bytes(x), - signatures, - ) - ) + signatures_chia = [ + signature if signature in _sig_map else bls_chia.Signature.from_bytes(signature) + for signature in signatures + ] aggregated_signature = bls_chia.Signature.aggregate(signatures_chia) aggregated_signature_bytes = aggregated_signature.serialize() _sig_map[aggregated_signature_bytes] = aggregated_signature - return aggregated_signature_bytes + return cast(BLSSignature, aggregated_signature_bytes) def aggregate_pubkeys(pubkeys: Sequence[BLSPubkey]) -> BLSPubkey: pubkeys_chia = list(map(bls_chia.PublicKey.from_bytes, pubkeys)) aggregated_pubkey_chia = bls_chia.PublicKey.aggregate(pubkeys_chia) - return aggregated_pubkey_chia.serialize() + return cast(BLSPubkey, aggregated_pubkey_chia.serialize()) def verify_multiple(pubkeys: Sequence[BLSPubkey], @@ -111,4 +111,4 @@ def verify_multiple(pubkeys: Sequence[BLSPubkey], signature_chia = bls_chia.Signature.from_bytes(signature) signature_chia.set_aggregation_info(merged_info) - return signature_chia.verify() + return cast(bool, signature_chia.verify()) diff --git a/tests/test_bls_chia.py b/tests/test_bls_chia.py index f88cf4fe..65e6b710 100644 --- a/tests/test_bls_chia.py +++ b/tests/test_bls_chia.py @@ -30,7 +30,7 @@ field_modulus as q, ) -from py_ecc.bls_bindings.api import ( +from py_ecc.bls_chia.api import ( _domain_to_bytes, _privkey_int_to_bytes, privtopub, From c7d5dfbc5bc8e5851833c0647e8217d25f6734b8 Mon Sep 17 00:00:00 2001 From: mhchia Date: Mon, 27 May 2019 19:28:03 +0800 Subject: [PATCH 3/8] Add a simple benchmark for api.py --- py_ecc/scripts/benchmark_api.py | 88 +++++++++++++++++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 py_ecc/scripts/benchmark_api.py diff --git a/py_ecc/scripts/benchmark_api.py b/py_ecc/scripts/benchmark_api.py new file mode 100644 index 00000000..9b0c1828 --- /dev/null +++ b/py_ecc/scripts/benchmark_api.py @@ -0,0 +1,88 @@ +import argparse +import secrets +import timeit + +from py_ecc.bls_chia import api as bls_chia_api +from py_ecc.bls import api as pyecc_api + + +# +# Choose module to benchmark +# +parser = argparse.ArgumentParser( + description="Perform benchmark for the bls implementations", +) +parser.add_argument("module", help="which module you would like to test against", type=str) +args = parser.parse_args() +if args.module == "py_ecc": + module = pyecc_api +elif args.module == "chia": + module = bls_chia_api +else: + raise ValueError(f"{args.module} is not supported") + + +# +# Setup +# +num_keys = 20 +privkeys = [ + # FIXME: should be 8*32, workaround errors with smaller privkeys + secrets.randbits(7 * 32) for _ in range(num_keys) +] +msgs = [ + secrets.randbits(8 * 32).to_bytes(32, 'big') for _ in range(num_keys) +] +domain = 5566 +pubkeys = [module.privtopub(privkey) for privkey in privkeys] +signatures = [ + [module.sign(msg, privkey, domain) for privkey in privkeys] + for msg in msgs +] +aggregate_multiple_sig = module.aggregate_signatures( + [ + signature + for msg_signatures in signatures + for signature in msg_signatures + ] +) +default_number_bench = 100 + + +def bench(func, number=default_number_bench): + # stmt = f"{func.__name__}()".format(func.__name__) + setup = f"from __main__ import bls_chia_api, pyecc_api, {func.__name__}" + res = timeit.timeit(f"{func.__name__}()", setup=setup, number=number) + print(f"{func.__name__}: {res}") + + +def privtopub(): + module.privtopub(privkeys[0]) + + +def verify(): + module.verify(msgs[0], pubkeys[0], signatures[0][0], domain) + + +def aggregate_signatures(): + module.aggregate_signatures(signatures[0]) + + +def aggregate_pubkeys(): + module.aggregate_pubkeys(pubkeys) + + +def verify_multiple(): + module.verify_multiple( + pubkeys=pubkeys, + message_hashes=msgs, + signature=aggregate_multiple_sig, + domain=domain, + ) + + +bench(privtopub) +bench(verify) +bench(aggregate_signatures) +bench(aggregate_pubkeys) +bench(verify_multiple) From 34bbee767bf04a9d411e0c0b7260f71e24f313fb Mon Sep 17 00:00:00 2001 From: mhchia Date: Mon, 27 May 2019 20:20:03 +0800 Subject: [PATCH 4/8] Use importlib to import api module And fix a bug in `aggregate_signatures`: make sure bytes are converted to `bls_chia.Signature` --- py_ecc/bls_chia/api.py | 2 +- py_ecc/scripts/benchmark_api.py | 35 +++++++++++++-------------------- 2 files changed, 15 insertions(+), 22 deletions(-) diff --git a/py_ecc/bls_chia/api.py b/py_ecc/bls_chia/api.py index 0a3e0cc7..b73fedb2 100644 --- a/py_ecc/bls_chia/api.py +++ b/py_ecc/bls_chia/api.py @@ -69,7 +69,7 @@ def aggregate_signatures(signatures: Sequence[BLSSignature]) -> BLSSignature: # aggregating signatures global _sig_map signatures_chia = [ - signature if signature in _sig_map else bls_chia.Signature.from_bytes(signature) + _sig_map[signature] if signature in _sig_map else bls_chia.Signature.from_bytes(signature) for signature in signatures ] aggregated_signature = bls_chia.Signature.aggregate(signatures_chia) diff --git a/py_ecc/scripts/benchmark_api.py b/py_ecc/scripts/benchmark_api.py index 9b0c1828..02dc68aa 100644 --- a/py_ecc/scripts/benchmark_api.py +++ b/py_ecc/scripts/benchmark_api.py @@ -1,26 +1,19 @@ import argparse +import importlib import secrets import timeit -from py_ecc.bls_chia import api as bls_chia_api -from py_ecc.bls import api as pyecc_api - # -# Choose module to benchmark +# Parse which module to benchmark # parser = argparse.ArgumentParser( description="Perform benchmark for the bls implementations", ) parser.add_argument("module", help="which module you would like to test against", type=str) args = parser.parse_args() -if args.module == "py_ecc": - module = pyecc_api -elif args.module == "chia": - module = bls_chia_api -else: - raise ValueError(f"{args.module} is not supported") - +module_path = f"py_ecc.{args.module}.api" +api_module = importlib.import_module(module_path) # # Setup @@ -34,12 +27,12 @@ secrets.randbits(8 * 32).to_bytes(32, 'big') for _ in range(num_keys) ] domain = 5566 -pubkeys = [module.privtopub(privkey) for privkey in privkeys] +pubkeys = [api_module.privtopub(privkey) for privkey in privkeys] signatures = [ - [module.sign(msg, privkey, domain) for privkey in privkeys] + [api_module.sign(msg, privkey, domain) for privkey in privkeys] for msg in msgs ] -aggregate_multiple_sig = module.aggregate_signatures( +aggregate_multiple_sig = api_module.aggregate_signatures( [ signature for msg_signatures in signatures @@ -50,30 +43,29 @@ def bench(func, number=default_number_bench): - # stmt = f"{func.__name__}()".format(func.__name__) - setup = f"from __main__ import bls_chia_api, pyecc_api, {func.__name__}" + setup = f"from __main__ import api_module, {func.__name__}" res = timeit.timeit(f"{func.__name__}()", setup=setup, number=number) print(f"{func.__name__}: {res}") def privtopub(): - module.privtopub(privkeys[0]) + api_module.privtopub(privkeys[0]) def verify(): - module.verify(msgs[0], pubkeys[0], signatures[0][0], domain) + api_module.verify(msgs[0], pubkeys[0], signatures[0][0], domain) def aggregate_signatures(): - module.aggregate_signatures(signatures[0]) + api_module.aggregate_signatures(signatures[0]) def aggregate_pubkeys(): - module.aggregate_pubkeys(pubkeys) + api_module.aggregate_pubkeys(pubkeys) def verify_multiple(): - module.verify_multiple( + api_module.verify_multiple( pubkeys=pubkeys, message_hashes=msgs, signature=aggregate_multiple_sig, @@ -81,6 +73,7 @@ def verify_multiple(): ) +print(f"Module {module_path}") bench(privtopub) bench(verify) bench(aggregate_signatures) From 02002e4f2cab8b11286ce3c224f8b14196fb3b2e Mon Sep 17 00:00:00 2001 From: mhchia Date: Mon, 27 May 2019 20:53:44 +0800 Subject: [PATCH 5/8] Remove unused imports in the test --- tests/test_bls_chia.py | 27 --------------------------- 1 file changed, 27 deletions(-) diff --git a/tests/test_bls_chia.py b/tests/test_bls_chia.py index 65e6b710..921dee7e 100644 --- a/tests/test_bls_chia.py +++ b/tests/test_bls_chia.py @@ -1,38 +1,15 @@ import blspy as bls_chia -from eth_utils import ( - big_endian_to_int, -) import pytest -from py_ecc.bls.hash import ( - hash_eth2, -) -from py_ecc.bls.constants import ( - POW_2_381, - POW_2_382, - POW_2_383, -) -from py_ecc.fields import ( - optimized_bls12_381_FQ as FQ, - optimized_bls12_381_FQ2 as FQ2, -) from py_ecc.optimized_bls12_381 import ( G1, G2, - Z1, - Z2, b, - b2, - is_on_curve, multiply, - normalize, - field_modulus as q, ) from py_ecc.bls_chia.api import ( - _domain_to_bytes, - _privkey_int_to_bytes, privtopub, sign, verify, @@ -42,10 +19,6 @@ ) -def assert_privkey(obj): - assert isinstance(obj, int) and obj < 2 ** (8 * 32) - - def assert_pubkey(obj): assert isinstance(obj, bytes) and len(obj) == 48 From e3640ce4edebc80b7ce425f17358d9644b0910f1 Mon Sep 17 00:00:00 2001 From: mhchia Date: Mon, 27 May 2019 21:22:52 +0800 Subject: [PATCH 6/8] Add blspy as the deps for CI --- setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/setup.py b/setup.py index 6ecf91d4..e381ea5f 100644 --- a/setup.py +++ b/setup.py @@ -48,6 +48,7 @@ "eth-typing>=2.1.0,<3.0.0", "eth-utils>=1.3.0,<2", "mypy-extensions>=0.4.1", + "blspy>=0.1.8,<1", # for `bls_chia` ], python_requires='>=3.5, <4', extras_require=extras_require, From c3a9aa14d3edd73187afd8b8e262fa58ea270b38 Mon Sep 17 00:00:00 2001 From: mhchia Date: Thu, 6 Jun 2019 19:14:44 -0400 Subject: [PATCH 7/8] Remove cache for signed signatures Making use of `InsecureSignature.aggregate` --- py_ecc/bls_chia/api.py | 24 +++++------------------- tests/test_bls_chia.py | 1 - 2 files changed, 5 insertions(+), 20 deletions(-) diff --git a/py_ecc/bls_chia/api.py b/py_ecc/bls_chia/api.py index b73fedb2..edeabf6b 100644 --- a/py_ecc/bls_chia/api.py +++ b/py_ecc/bls_chia/api.py @@ -1,5 +1,4 @@ from typing import ( - Dict, Sequence, cast, ) @@ -16,11 +15,6 @@ ) -# FIXME: Temporarily introduced to workaround the de/serialization issue when -# aggregating signatures -_sig_map: Dict[bytes, bls_chia.Signature] = {} - - def _domain_to_bytes(domain: int) -> bytes: return domain.to_bytes(8, 'big') # bytes8 @@ -40,12 +34,8 @@ def sign(message_hash: Hash32, domain: int) -> BLSSignature: privkey_chia = bls_chia.PrivateKey.from_bytes(_privkey_int_to_bytes(privkey)) hash_to_be_signed = _hash_to_be_signed(message_hash, domain) - sig_chia = privkey_chia.sign(hash_to_be_signed) + sig_chia = privkey_chia.sign_insecure(hash_to_be_signed) sig_chia_bytes = sig_chia.serialize() - # FIXME: Temporarily introduced to workaround the de/serialization issue when - # aggregating signatures - global _sig_map - _sig_map[sig_chia_bytes] = sig_chia return cast(BLSSignature, sig_chia_bytes) @@ -65,22 +55,18 @@ def verify(message_hash: Hash32, pubkey: BLSPubkey, signature: BLSSignature, dom def aggregate_signatures(signatures: Sequence[BLSSignature]) -> BLSSignature: - # FIXME: Temporarily introduced to workaround the de/serialization issue when - # aggregating signatures - global _sig_map signatures_chia = [ - _sig_map[signature] if signature in _sig_map else bls_chia.Signature.from_bytes(signature) + bls_chia.InsecureSignature.from_bytes(signature) for signature in signatures ] - aggregated_signature = bls_chia.Signature.aggregate(signatures_chia) + aggregated_signature = bls_chia.InsecureSignature.aggregate(signatures_chia) aggregated_signature_bytes = aggregated_signature.serialize() - _sig_map[aggregated_signature_bytes] = aggregated_signature return cast(BLSSignature, aggregated_signature_bytes) def aggregate_pubkeys(pubkeys: Sequence[BLSPubkey]) -> BLSPubkey: pubkeys_chia = list(map(bls_chia.PublicKey.from_bytes, pubkeys)) - aggregated_pubkey_chia = bls_chia.PublicKey.aggregate(pubkeys_chia) + aggregated_pubkey_chia = bls_chia.PublicKey.aggregate_insecure(pubkeys_chia) return cast(BLSPubkey, aggregated_pubkey_chia.serialize()) @@ -99,7 +85,7 @@ def verify_multiple(pubkeys: Sequence[BLSPubkey], ) message_hashes_with_domain = [ - message_hash + _domain_to_bytes(domain) + _hash_to_be_signed(message_hash, domain) for message_hash in message_hashes ] pubkeys_chia = map(bls_chia.PublicKey.from_bytes, pubkeys) diff --git a/tests/test_bls_chia.py b/tests/test_bls_chia.py index 921dee7e..57af0bbf 100644 --- a/tests/test_bls_chia.py +++ b/tests/test_bls_chia.py @@ -42,7 +42,6 @@ def test_sanity(): privkey_1 = 5567 sig_1 = sign(msg_0, privkey_1, domain) pubkey_1 = privtopub(privkey_1) - sig_1_chia_from_bytes = bls_chia.Signature.from_bytes(sig_1) assert verify(msg_0, pubkey_1, sig_1, domain) # Test: Verify signatures are correctly aggregated From fa11bdae33a63b3073485f261328c8b603f3f4c2 Mon Sep 17 00:00:00 2001 From: mhchia Date: Thu, 6 Jun 2019 23:36:51 -0400 Subject: [PATCH 8/8] Fix `_privkey_int_to_bytes` Don't add 1 to the `private_key`. The range of private key should be (0, curve_order - 1]. --- py_ecc/bls_chia/api.py | 2 +- tests/test_bls_chia.py | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/py_ecc/bls_chia/api.py b/py_ecc/bls_chia/api.py index edeabf6b..b7135954 100644 --- a/py_ecc/bls_chia/api.py +++ b/py_ecc/bls_chia/api.py @@ -21,7 +21,7 @@ def _domain_to_bytes(domain: int) -> bytes: def _privkey_int_to_bytes(privkey: int) -> bytes: # FIXME: workaround due to the privkey in Chia-Network BLS starts from 1 - return (privkey + 1).to_bytes(bls_chia.PrivateKey.PRIVATE_KEY_SIZE, "big") + return privkey.to_bytes(bls_chia.PrivateKey.PRIVATE_KEY_SIZE, "big") def _hash_to_be_signed(message_hash: Hash32, domain: int) -> bytes: diff --git a/tests/test_bls_chia.py b/tests/test_bls_chia.py index 57af0bbf..773e1f77 100644 --- a/tests/test_bls_chia.py +++ b/tests/test_bls_chia.py @@ -6,6 +6,7 @@ G1, G2, b, + curve_order, multiply, ) @@ -80,7 +81,7 @@ def test_sanity(): (735), (127409812145), (90768492698215092512159), - (0), + (curve_order - 1), ] ) def test_bls_core(privkey): @@ -96,7 +97,7 @@ def test_bls_core(privkey): @pytest.mark.parametrize( 'msg, privkeys', [ - (b'\x12' * 32, [1, 5, 124, 735, 127409812145, 90768492698215092512159, 0]), + (b'\x12' * 32, [1, 5, 124, 735, 127409812145, 90768492698215092512159, curve_order - 1]), (b'\x34' * 32, [42, 666, 1274099945, 4389392949595]), ] ) @@ -118,9 +119,9 @@ def test_signature_aggregation(msg, privkeys): @pytest.mark.parametrize( 'privkeys_1, privkeys_2', [ - (tuple(range(10)), tuple(range(10))), - ((0, 1, 2, 3), (4, 5, 6, 7)), - ((0, 1, 2, 3), (2, 3, 4, 5)), + (tuple(range(1, 11)), tuple(range(1, 11))), + ((1, 2, 3), (4, 5, 6, 7)), + ((1, 2, 3), (2, 3, 4, 5)), ] ) def test_multi_aggregation(msg_1, msg_2, privkeys_1, privkeys_2):