diff --git a/setup.py b/setup.py index 8095e3b7f..d840c3437 100644 --- a/setup.py +++ b/setup.py @@ -55,6 +55,12 @@ def floorlog2(x: int) -> uint64: ''' +OPTIMIZED_BLS_AGGREGATE_PUBKEYS = ''' +def eth2_aggregate_pubkeys(pubkeys: Sequence[BLSPubkey]) -> BLSPubkey: + return bls.AggregatePKs(pubkeys) +''' + + class ProtocolDefinition(NamedTuple): # just function definitions currently. May expand with configuration vars in future. functions: Dict[str, str] @@ -305,6 +311,10 @@ class SpecBuilder(ABC): """ raise NotImplementedError() + @classmethod + def implement_optimizations(cls, functions: Dict[str, str]) -> Dict[str, str]: + return functions + @classmethod @abstractmethod def build_spec(cls, preset_name: str, @@ -482,6 +492,10 @@ assert ( TIMELY_HEAD_WEIGHT + TIMELY_SOURCE_WEIGHT + TIMELY_TARGET_WEIGHT + SYNC_REWARD_WEIGHT + PROPOSER_WEIGHT ) == WEIGHT_DENOMINATOR''' + @classmethod + def implement_optimizations(cls, functions: Dict[str, str]) -> Dict[str, str]: + if "eth2_aggregate_pubkeys" in functions: + return {**functions, **{"eth2_aggregate_pubkeys": OPTIMIZED_BLS_AGGREGATE_PUBKEYS.strip()}} # # MergeSpecBuilder @@ -588,7 +602,8 @@ def objects_to_spec(preset_name: str, for k in list(spec_object.functions): if "ceillog2" in k or "floorlog2" in k: del spec_object.functions[k] - functions_spec = '\n\n\n'.join(spec_object.functions.values()) + functions = builder.implement_optimizations(spec_object.functions) + functions_spec = '\n\n\n'.join(functions.values()) # Access global dict of config vars for runtime configurables for name in spec_object.config_vars.keys(): @@ -831,7 +846,7 @@ class PySpecCommand(Command): self.out_dir = 'pyspec_output' self.build_targets = """ minimal:presets/minimal:configs/minimal.yaml - mainnet:presets/mainnet:configs/mainnet.yaml + mainnet:presets/mainnet:configs/mainnet.yaml """ def finalize_options(self): diff --git a/specs/altair/beacon-chain.md b/specs/altair/beacon-chain.md index 7412a8490..cd877161c 100644 --- a/specs/altair/beacon-chain.md +++ b/specs/altair/beacon-chain.md @@ -26,6 +26,8 @@ - [`SyncAggregate`](#syncaggregate) - [`SyncCommittee`](#synccommittee) - [Helper functions](#helper-functions) + - [Crypto](#crypto) + - [BLS public keys](#bls-public-keys) - [`Predicates`](#predicates) - [`eth2_fast_aggregate_verify`](#eth2_fast_aggregate_verify) - [Misc](#misc-1) @@ -221,6 +223,32 @@ class SyncCommittee(Container): ## Helper functions + +### Crypto + +#### BLS public keys + +An additional function `AggregatePKs` is defined to extend the +[IETF BLS signature draft standard v4](https://tools.ietf.org/html/draft-irtf-cfrg-bls-signature-04) +spec referenced in the phase 0 document. + +```python +def eth2_aggregate_pubkeys(pubkeys: Sequence[BLSPubkey]) -> BLSPubkey: + """ + Return the aggregate public key for the public keys in ``pubkeys``. + + NOTE: the ``+`` operation should be interpreted as elliptic curve point addition, which takes as input + elliptic curve points that must be decoded from the input ``BLSPubkey``s. + This implementation is for demonstrative purposes only and ignores encoding/decoding concerns. + Refer to the BLS signature draft standard for more information. + """ + assert len(pubkeys) > 0 + result = copy(pubkeys[0]) + for pubkey in pubkeys[1:]: + result += pubkey + return result +``` + ### `Predicates` #### `eth2_fast_aggregate_verify` @@ -310,7 +338,7 @@ def get_next_sync_committee(state: BeaconState) -> SyncCommittee: """ indices = get_next_sync_committee_indices(state) pubkeys = [state.validators[index].pubkey for index in indices] - aggregate_pubkey = bls.AggregatePKs(pubkeys) + aggregate_pubkey = eth2_aggregate_pubkeys(pubkeys) return SyncCommittee(pubkeys=pubkeys, aggregate_pubkey=aggregate_pubkey) ```