From 99a98aa6b86e632e8efdd2640550a41b30838deb Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Thu, 18 Nov 2021 21:16:35 -0800 Subject: [PATCH 1/4] Align Eth2 node interfaces to beacon-APIs --- eth2_node.py | 66 ++++++++++++++++++++++++--------- spec.py | 46 +++++++++-------------- test.py | 101 +++++++++++++++++++++++++++++++++------------------ 3 files changed, 132 insertions(+), 81 deletions(-) diff --git a/eth2_node.py b/eth2_node.py index 240919b..3b85356 100644 --- a/eth2_node.py +++ b/eth2_node.py @@ -1,27 +1,57 @@ +from typing import List from eth2spec.phase0.mainnet import ( - Container, + Container, uint64, + BLSPubkey, Slot, + Epoch, AttestationData, Attestation, + ValidatorIndex, CommitteeIndex ) class AttestationDuty(Container): - # TODO: Update schema to include committee_index etc. as defined - # in https://ethereum.github.io/beacon-APIs/#/ + pubkey: BLSPubkey + validator_index: ValidatorIndex + committee_index: CommitteeIndex + committee_length: uint64 + committees_at_slot: uint64 + validator_committee_index: ValidatorIndex # TODO: Is this the correct datatype? slot: Slot -class BeaconNodeTemplate: - def get_next_attestation_duty(self) -> AttestationDuty: - pass - - def broadcast_attestation(self, attestation: Attestation) -> None: - pass - -class ValidatorClientTemplate: - def is_slashable(self, attestation_data: AttestationData) -> bool: - pass - - # TODO: What object does the VC sign? - # Is it the same object that the BN accepts for broadcast? - def sign_attestation(self, attestation_data: AttestationData) -> AttestationData: - pass + +# Beacon Node Interface + +def bn_get_attestation_duties_for_epoch(self, validator_indices: List[ValidatorIndex], epoch: Epoch) -> List[AttestationDuty]: + # TODO: Define typing here: + # What's the size of validator_indices & the returned attestation_duties? + """Fetch attestation duties for the validator indices in the epoch. + Uses https://ethereum.github.io/beacon-APIs/#/ValidatorRequiredApi/getAttesterDuties + """ + pass + +def bn_get_attestation_data(self, slot: Slot, committee_index: CommitteeIndex) -> AttestationData: + """Produces attestation data for the given slot & committee index. + Uses https://ethereum.github.io/beacon-APIs/#/ValidatorRequiredApi/produceAttestationData + """ + pass + +def bn_submit_attestation(self, attestation: Attestation) -> None: + """Submit attestation to BN for p2p gossip. + Uses https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttestations + """ + pass + + +# Validator Client Interface + +def vc_is_slashable(self, attestation_data: AttestationData, validator_pubkey: BLSPubkey) -> bool: + """Checks whether the attestation data is slashable according to the anti-slashing database. + This is a new endpoint for the VC. + """ + pass + +def vc_sign_attestation(self, attestation_data: AttestationData, attestation_duty: AttestationDuty) -> Attestation: + """Returns a signed attestations that is constructed using the given attestation data & attestation duty. + """ + # See note about attestation construction here: https://github.com/ethereum/beacon-APIs/blame/05c1bc142e1a3fb2a63c79098743776241341d08/validator-flow.md#L35-L37 + pass diff --git a/spec.py b/spec.py index 0c10dae..a9e7a54 100644 --- a/spec.py +++ b/spec.py @@ -1,41 +1,31 @@ from test import ( - get_current_time, - BeaconNode, - ValidatorClient, - calculate_attestation_time, + bn_get_attestation_duties_for_epoch, + bn_get_attestation_data, + bn_submit_attestation, + vc_is_slashable, + vc_sign_attestation, consensus, ) -from eth2spec.phase0.mainnet import ( - Attestation, -) -def attestation_duty_loop(): - # run in a loop forever - attestation_duty = beacon_node.get_next_attestation_duty() - while get_current_time() < calculate_attestation_time(attestation_duty.slot): - pass +""" +1. At the start of every epoch, get attestation duties for epoch+1 by running + bn_get_attestation_duties_for_epoch(validator_indices, epoch+1) +2. For each attestation_duty recevied in Step 1, schedule + serve_attestation_duty(attestation_duty) at 1/3rd way through the slot + attestation_duty.slot +""" +def serve_attestation_duty(attestation_duty): # Obtain lock on consensus process here - only a single consensus instance # should be running at any given time - attestation_data = consensus(attestation_duty.slot) + attestation_data = consensus(attestation_duty) # 1. Threshold sign attestation from local VC - threshold_signed_attestation_data = validator_client.sign_attestation(attestation_data) + threshold_signed_attestation = vc_sign_attestation(attestation_data, attestation_duty) # 2. Broadcast threshold signed attestation - # TODO # 3. Reconstruct complete signed attestation by combining threshold signed attestations - complete_signed_attestation_data = threshold_signed_attestation_data - complete_signed_attestation = Attestation(data=complete_signed_attestation_data) + complete_signed_attestation = threshold_signed_attestation # 4. Send complete signed attestation to BN for broadcast - beacon_node.broadcast_attestation(complete_signed_attestation) - - # Release lock on consensus process here - - print( - f"Duty Slot: {attestation_duty.slot}, Attestation Slot: {attestation_data.slot}") + bn_submit_attestation(complete_signed_attestation) -beacon_node = BeaconNode() -validator_client = ValidatorClient() - -while True: - attestation_duty_loop() + # Release lock on consensus process here diff --git a/test.py b/test.py index 0718f92..f739cf6 100644 --- a/test.py +++ b/test.py @@ -1,70 +1,101 @@ -from eth2_node import ( - AttestationData, - AttestationDuty, - BeaconNodeTemplate, - ValidatorClientTemplate, -) +import random from eth2spec.phase0.mainnet import ( + SLOTS_PER_EPOCH, TARGET_COMMITTEE_SIZE, + Checkpoint, is_slashable_attestation_data, + compute_start_slot_at_epoch, + compute_epoch_at_slot, + config, ) +from eth2_node import ( + List, + Slot, Epoch, BLSPubkey, ValidatorIndex, CommitteeIndex, + Attestation, AttestationData, + AttestationDuty, +) + + +VALIDATOR_INDICES = [1, 2, 3] +GENESIS_TIME = 0 def time_generator(): - time = 0 + time = GENESIS_TIME while True: yield time time += 1 - timer = time_generator() - def get_current_time(): # Returns current time return next(timer) -class BeaconNode(BeaconNodeTemplate): - def __init__(self): - self.attestation_duty_source = self.attestation_duty_generator() +# Beacon Node Methods + +def bn_get_attestation_duties_for_epoch(validator_indices: List[ValidatorIndex], epoch: Epoch) -> List[AttestationDuty]: + attestation_duties = [] + for validator_index in validator_indices: + start_slot_at_epoch = compute_start_slot_at_epoch(epoch) + attestation_slot = start_slot_at_epoch + random.randrange(SLOTS_PER_EPOCH) + attestation_duty = AttestationDuty(validator_index=validator_index, + committee_length=TARGET_COMMITTEE_SIZE, + validator_committee_index=random.randrange(TARGET_COMMITTEE_SIZE), + slot=attestation_slot) + attestation_duties.append(attestation_duty) + return attestation_duties + +def bn_get_attestation_data(slot: Slot, committee_index: CommitteeIndex) -> AttestationData: + attestation_data = AttestationData( slot=slot, + index=committee_index, + source=Checkpoint(epoch=min(compute_epoch_at_slot(slot) - 1, 0)), + target=Checkpoint(epoch=compute_epoch_at_slot(slot))) + return attestation_data + +def bn_submit_attestation(attestation: Attestation) -> None: + pass - def attestation_duty_generator(self): - slot = 0 - while True: - yield slot - slot += 1 - def get_next_attestation_duty(self) -> AttestationDuty: - slot = next(self.attestation_duty_source) - return AttestationDuty(slot=slot) +# Validator Client Methods +slashing_db = {} -class ValidatorClient(ValidatorClientTemplate): - def __init__(self): - self.slashing_db = set() +def update_slashing_db(attestation_data, validator_pubkey): + """Check that the attestation data is not slashable for the validator and + add attestation to slashing DB. + """ + if validator_pubkey not in slashing_db: + slashing_db[validator_pubkey] = set() + assert not vc_is_slashable(attestation_data, validator_pubkey) + slashing_db[validator_pubkey].add(attestation_data) - def is_slashable(self, attestation_data: AttestationData) -> bool: - for past_attestation_data in self.slashing_db: +def vc_is_slashable(attestation_data: AttestationData, validator_pubkey: BLSPubkey) -> bool: + if validator_pubkey in slashing_db: + for past_attestation_data in slashing_db[validator_pubkey]: if is_slashable_attestation_data(past_attestation_data, attestation_data): return True - return False + return False + +def vc_sign_attestation(attestation_data: AttestationData, attestation_duty) -> Attestation: + update_slashing_db(attestation_data, attestation_duty.pubkey) + attestation = Attestation(data=attestation_data) + attestation.aggregation_bits[attestation_duty.validator_committee_index] = 1 + return attestation - def sign_attestation(self, attestation_data: AttestationData) -> AttestationData: - assert not self.is_slashable(attestation_data) - self.slashing_db.add(attestation_data) - return attestation_data +# Misc. Methods def calculate_attestation_time(slot): - return 12 * slot + 4 + return config.SECONDS_PER_SLOT * slot + GENESIS_TIME def is_valid_attestation_data(slot, attestation_data): # Determines if `attestation_data` is valid for `slot` return attestation_data.slot == slot -def consensus(slot): +def consensus(attestation_duty): # Returns decided value for consensus instance at `slot` attestation_data = AttestationData() - attestation_data.slot = slot - attestation_data.target.epoch = slot - is_valid_attestation_data(slot, attestation_data) + attestation_data.slot = attestation_duty.slot + attestation_data.target.epoch = attestation_duty.slot + is_valid_attestation_data(attestation_duty.slot, attestation_data) return attestation_data From 5910ba92f0fc1bd98e94f0eb00d3ecc082bc79b4 Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Fri, 19 Nov 2021 18:29:18 -0800 Subject: [PATCH 2/4] Add block proposal interfaces --- eth2_node.py | 58 ++++++++++++++++++++++++++++++++----------- spec.py | 20 ++++++++++++++- test.py | 70 ++++++++++++++++++++++++++++++++++++++++------------ 3 files changed, 117 insertions(+), 31 deletions(-) diff --git a/eth2_node.py b/eth2_node.py index 3b85356..74c0e43 100644 --- a/eth2_node.py +++ b/eth2_node.py @@ -1,12 +1,11 @@ from typing import List from eth2spec.phase0.mainnet import ( - Container, uint64, - BLSPubkey, - Slot, - Epoch, - AttestationData, - Attestation, - ValidatorIndex, CommitteeIndex + Container, SignedBeaconBlock, uint64, Bytes32, + BLSPubkey, BLSSignature, + Slot, Epoch, + ValidatorIndex, CommitteeIndex, + AttestationData, Attestation, + BeaconBlock, SignedBeaconBlock, ) class AttestationDuty(Container): @@ -18,10 +17,15 @@ class AttestationDuty(Container): validator_committee_index: ValidatorIndex # TODO: Is this the correct datatype? slot: Slot +class ProposerDuty(Container): + pubkey: BLSPubkey + validator_index: ValidatorIndex + slot: Slot + # Beacon Node Interface -def bn_get_attestation_duties_for_epoch(self, validator_indices: List[ValidatorIndex], epoch: Epoch) -> List[AttestationDuty]: +def bn_get_attestation_duties_for_epoch(validator_indices: List[ValidatorIndex], epoch: Epoch) -> List[AttestationDuty]: # TODO: Define typing here: # What's the size of validator_indices & the returned attestation_duties? """Fetch attestation duties for the validator indices in the epoch. @@ -29,29 +33,55 @@ def bn_get_attestation_duties_for_epoch(self, validator_indices: List[ValidatorI """ pass -def bn_get_attestation_data(self, slot: Slot, committee_index: CommitteeIndex) -> AttestationData: +def bn_get_attestation_data(slot: Slot, committee_index: CommitteeIndex) -> AttestationData: """Produces attestation data for the given slot & committee index. Uses https://ethereum.github.io/beacon-APIs/#/ValidatorRequiredApi/produceAttestationData """ pass -def bn_submit_attestation(self, attestation: Attestation) -> None: +def bn_submit_attestation(attestation: Attestation) -> None: """Submit attestation to BN for p2p gossip. Uses https://ethereum.github.io/beacon-APIs/#/Beacon/submitPoolAttestations """ pass +def bn_get_proposer_duties_for_epoch(epoch: Epoch) -> List[ProposerDuty]: + """Fetch proposer duties for all proposers in the epoch. + Uses https://ethereum.github.io/beacon-APIs/#/ValidatorRequiredApi/getProposerDuties + """ + pass + +def bn_produce_block(slot: Slot, randao_reveal: BLSSignature, graffiti: Bytes32) -> BeaconBlock: + """Produces valid block for given slot using provided data + Uses https://ethereum.github.io/beacon-APIs/#/ValidatorRequiredApi/produceBlockV2 + """ + pass + # Validator Client Interface -def vc_is_slashable(self, attestation_data: AttestationData, validator_pubkey: BLSPubkey) -> bool: +def vc_is_slashable_attestation_data(attestation_data: AttestationData, validator_pubkey: BLSPubkey) -> bool: """Checks whether the attestation data is slashable according to the anti-slashing database. - This is a new endpoint for the VC. + This endpoint does not exist in beacon-APIs. """ pass -def vc_sign_attestation(self, attestation_data: AttestationData, attestation_duty: AttestationDuty) -> Attestation: +def vc_sign_attestation(attestation_data: AttestationData, attestation_duty: AttestationDuty) -> Attestation: """Returns a signed attestations that is constructed using the given attestation data & attestation duty. + This endpoint does not exist in beacon-APIs. """ - # See note about attestation construction here: https://github.com/ethereum/beacon-APIs/blame/05c1bc142e1a3fb2a63c79098743776241341d08/validator-flow.md#L35-L37 + # See note about attestation construction here: + # https://github.com/ethereum/beacon-APIs/blame/05c1bc142e1a3fb2a63c79098743776241341d08/validator-flow.md#L35-L37 pass + +def vc_is_slashable_block(block: BeaconBlock, validator_pubkey: BLSPubkey) -> bool: + """Checks whether the block is slashable according to the anti-slashing database. + This endpoint does not exist in beacon-APIs. + """ + pass + +def vc_sign_block(block: BeaconBlock, proposer_duty: ProposerDuty) -> SignedBeaconBlock: + """Returns a signed beacon block using the validator index given in the proposer duty. + This endpoint does not exist in beacon-APIs. + """ + pass \ No newline at end of file diff --git a/spec.py b/spec.py index a9e7a54..82bc0de 100644 --- a/spec.py +++ b/spec.py @@ -2,17 +2,21 @@ bn_get_attestation_duties_for_epoch, bn_get_attestation_data, bn_submit_attestation, - vc_is_slashable, + vc_is_slashable_attestation_data, vc_sign_attestation, consensus, ) """ +Attestation Production Process: 1. At the start of every epoch, get attestation duties for epoch+1 by running bn_get_attestation_duties_for_epoch(validator_indices, epoch+1) 2. For each attestation_duty recevied in Step 1, schedule serve_attestation_duty(attestation_duty) at 1/3rd way through the slot attestation_duty.slot + +See notes here: +https://github.com/ethereum/beacon-APIs/blob/05c1bc142e1a3fb2a63c79098743776241341d08/validator-flow.md#attestation """ def serve_attestation_duty(attestation_duty): @@ -29,3 +33,17 @@ def serve_attestation_duty(attestation_duty): bn_submit_attestation(complete_signed_attestation) # Release lock on consensus process here + +""" +Block Production Process: +1. At the start of every epoch, get proposer duties for epoch+1 by running + bn_get_proposer_duties_for_epoch(epoch+1) +2. For each proposer_duty recevied in Step 1 for our validators, schedule + serve_proposer_duty(proposer_duty) at beginning of slot proposer_duty.slot + +See notes here: +https://github.com/ethereum/beacon-APIs/blob/05c1bc142e1a3fb2a63c79098743776241341d08/validator-flow.md#block-proposing +""" + +def serve_proposer_duty(proposer_duty): + pass \ No newline at end of file diff --git a/test.py b/test.py index f739cf6..0d2457b 100644 --- a/test.py +++ b/test.py @@ -9,12 +9,16 @@ ) from eth2_node import ( List, - Slot, Epoch, BLSPubkey, ValidatorIndex, CommitteeIndex, - Attestation, AttestationData, - AttestationDuty, + AttestationDuty, ProposerDuty, + Container, SignedBeaconBlock, uint64, Bytes32, + BLSPubkey, BLSSignature, + Slot, Epoch, + ValidatorIndex, CommitteeIndex, + AttestationData, Attestation, + BeaconBlock, SignedBeaconBlock, ) - +VALIDATOR_SET_SIZE = SLOTS_PER_EPOCH VALIDATOR_INDICES = [1, 2, 3] GENESIS_TIME = 0 @@ -55,35 +59,69 @@ def bn_get_attestation_data(slot: Slot, committee_index: CommitteeIndex) -> Atte def bn_submit_attestation(attestation: Attestation) -> None: pass +def bn_get_proposer_duties_for_epoch(epoch: Epoch) -> List[ProposerDuty]: + proposer_duties = [] + validator_indices = [x for x in range(VALIDATOR_SET_SIZE)] + random.shuffle(validator_indices) + for i in range(SLOTS_PER_EPOCH): + proposer_duties.append(ProposerDuty(validator_index=validator_indices[i], slot=i)) + return proposer_duties + +def bn_produce_block(slot: Slot, randao_reveal: BLSSignature, graffiti: Bytes32) -> BeaconBlock: + block = BeaconBlock() + block.slot = slot + block.body.randao_reveal = randao_reveal + block.body.graffiti = graffiti + return block # Validator Client Methods -slashing_db = {} +attestation_slashing_db = {} -def update_slashing_db(attestation_data, validator_pubkey): +def update_attestation_slashing_db(attestation_data, validator_pubkey): """Check that the attestation data is not slashable for the validator and add attestation to slashing DB. """ - if validator_pubkey not in slashing_db: - slashing_db[validator_pubkey] = set() - assert not vc_is_slashable(attestation_data, validator_pubkey) - slashing_db[validator_pubkey].add(attestation_data) - -def vc_is_slashable(attestation_data: AttestationData, validator_pubkey: BLSPubkey) -> bool: - if validator_pubkey in slashing_db: - for past_attestation_data in slashing_db[validator_pubkey]: + if validator_pubkey not in attestation_slashing_db: + attestation_slashing_db[validator_pubkey] = set() + assert not vc_is_slashable_attestation_data(attestation_data, validator_pubkey) + attestation_slashing_db[validator_pubkey].add(attestation_data) + +def vc_is_slashable_attestation_data(attestation_data: AttestationData, validator_pubkey: BLSPubkey) -> bool: + if validator_pubkey in attestation_slashing_db: + for past_attestation_data in attestation_slashing_db[validator_pubkey]: if is_slashable_attestation_data(past_attestation_data, attestation_data): return True return False def vc_sign_attestation(attestation_data: AttestationData, attestation_duty) -> Attestation: - update_slashing_db(attestation_data, attestation_duty.pubkey) + update_attestation_slashing_db(attestation_data, attestation_duty.pubkey) attestation = Attestation(data=attestation_data) attestation.aggregation_bits[attestation_duty.validator_committee_index] = 1 return attestation -# Misc. Methods +block_slashing_db = {} + +def update_block_slashing_db(block, validator_pubkey): + """Check that the block is not slashable for the validator and + add block to slashing DB. + """ + if validator_pubkey not in block_slashing_db: + block_slashing_db[validator_pubkey] = set() + assert not vc_is_slashable_block(block, validator_pubkey) + block_slashing_db[validator_pubkey].add(block) + +def vc_is_slashable_block(block: BeaconBlock, validator_pubkey: BLSPubkey) -> bool: + if validator_pubkey in block_slashing_db: + for past_block in block_slashing_db[validator_pubkey]: + if past_block.slot == block.slot: + return True + return False +def vc_sign_block(block: BeaconBlock, proposer_duty: ProposerDuty) -> SignedBeaconBlock: + return SignedBeaconBlock(message=block) + +# Misc. Methods def calculate_attestation_time(slot): return config.SECONDS_PER_SLOT * slot + GENESIS_TIME From 9c373280624a383822173b142c593abdc7ef2ee8 Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Fri, 19 Nov 2021 18:59:26 -0800 Subject: [PATCH 3/4] Moved consensus functions to spec.py --- spec.py | 47 ++++++++++++++++++++++++++++++++++++++++++++--- test.py | 19 +------------------ 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/spec.py b/spec.py index 82bc0de..046b6df 100644 --- a/spec.py +++ b/spec.py @@ -1,12 +1,53 @@ -from test import ( +from eth2_node import ( + AttestationDuty, ProposerDuty, + AttestationData, BeaconBlock, bn_get_attestation_duties_for_epoch, bn_get_attestation_data, bn_submit_attestation, + bn_get_proposer_duties_for_epoch, + bn_produce_block, vc_is_slashable_attestation_data, vc_sign_attestation, - consensus, + vc_is_slashable_block, + vc_sign_block, ) + +""" +Consensus Specification +""" + +def consensus_is_valid_attestation_data(attestation_data: AttestationData, attestation_duty: AttestationDuty) -> bool: + """Determines if the given attestation is valid for the attestation duty. + """ + assert attestation_data.slot == attestation_duty.slot + assert attestation_data.committee_index == attestation_duty.committee_index + assert not vc_is_slashable_attestation_data(attestation_data, attestation_duty.pubkey) + +def consensus_on_attestation(attestation_duty: AttestationDuty) -> AttestationData: + """Consensus protocol between distributed validator nodes for attestation values. + Returns the decided value. + The consensus protocol must use `consensus_is_valid_attestation_data` to determine + validity of the proposed attestation value. + """ + pass + +def consensus_is_valid_block(block: BeaconBlock, proposer_duty: ProposerDuty) -> bool: + """Determines if the given block is valid for the proposer duty. + """ + assert block.slot == proposer_duty.slot + # TODO: Assert correct block.proposer_index + assert not vc_is_slashable_block(block, proposer_duty.pubkey) + +def consensus_on_block(proposer_duty: ProposerDuty) -> AttestationData: + """Consensus protocol between distributed validator nodes for block values. + Returns the decided value. + The consensus protocol must use `consensus_is_valid_block` to determine + validity of the proposed block value. + """ + pass + + """ Attestation Production Process: 1. At the start of every epoch, get attestation duties for epoch+1 by running @@ -22,7 +63,7 @@ def serve_attestation_duty(attestation_duty): # Obtain lock on consensus process here - only a single consensus instance # should be running at any given time - attestation_data = consensus(attestation_duty) + attestation_data = consensus_on_attestation(attestation_duty) # 1. Threshold sign attestation from local VC threshold_signed_attestation = vc_sign_attestation(attestation_data, attestation_duty) diff --git a/test.py b/test.py index 0d2457b..cbc9bb5 100644 --- a/test.py +++ b/test.py @@ -119,21 +119,4 @@ def vc_is_slashable_block(block: BeaconBlock, validator_pubkey: BLSPubkey) -> bo return False def vc_sign_block(block: BeaconBlock, proposer_duty: ProposerDuty) -> SignedBeaconBlock: - return SignedBeaconBlock(message=block) - -# Misc. Methods - -def calculate_attestation_time(slot): - return config.SECONDS_PER_SLOT * slot + GENESIS_TIME - -def is_valid_attestation_data(slot, attestation_data): - # Determines if `attestation_data` is valid for `slot` - return attestation_data.slot == slot - -def consensus(attestation_duty): - # Returns decided value for consensus instance at `slot` - attestation_data = AttestationData() - attestation_data.slot = attestation_duty.slot - attestation_data.target.epoch = attestation_duty.slot - is_valid_attestation_data(attestation_duty.slot, attestation_data) - return attestation_data + return SignedBeaconBlock(message=block) \ No newline at end of file From 60ab1fc8b67b18b78e060d2dc55653a0c2c71c30 Mon Sep 17 00:00:00 2001 From: Aditya Asgaonkar Date: Fri, 19 Nov 2021 19:05:23 -0800 Subject: [PATCH 4/4] Define block production process --- eth2_node.py | 6 ++++++ spec.py | 23 +++++++++++++++++++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/eth2_node.py b/eth2_node.py index 74c0e43..4397abd 100644 --- a/eth2_node.py +++ b/eth2_node.py @@ -57,6 +57,12 @@ def bn_produce_block(slot: Slot, randao_reveal: BLSSignature, graffiti: Bytes32) """ pass +def bn_submit_block(block: SignedBeaconBlock) -> None: + """Submit block to BN for p2p gossip. + Uses https://ethereum.github.io/beacon-APIs/#/ValidatorRequiredApi/publishBlock + """ + pass + # Validator Client Interface diff --git a/spec.py b/spec.py index 046b6df..d30f777 100644 --- a/spec.py +++ b/spec.py @@ -6,6 +6,7 @@ bn_submit_attestation, bn_get_proposer_duties_for_epoch, bn_produce_block, + bn_submit_block, vc_is_slashable_attestation_data, vc_sign_attestation, vc_is_slashable_block, @@ -61,8 +62,9 @@ def consensus_on_block(proposer_duty: ProposerDuty) -> AttestationData: """ def serve_attestation_duty(attestation_duty): - # Obtain lock on consensus process here - only a single consensus instance - # should be running at any given time + # Obtain lock on consensus_on_attestation here. + # Only a single consensus_on_attestation instance should be + # running at any given time attestation_data = consensus_on_attestation(attestation_duty) # 1. Threshold sign attestation from local VC @@ -73,7 +75,7 @@ def serve_attestation_duty(attestation_duty): # 4. Send complete signed attestation to BN for broadcast bn_submit_attestation(complete_signed_attestation) - # Release lock on consensus process here + # Release lock on consensus_on_attestation here. """ Block Production Process: @@ -87,4 +89,17 @@ def serve_attestation_duty(attestation_duty): """ def serve_proposer_duty(proposer_duty): - pass \ No newline at end of file + # Obtain lock on consensus_on_block here. + # Only a single consensus_on_block instance should be + # running at any given time + block = consensus_on_block(proposer_duty) + + # 1. Threshold sign block from local VC + threshold_signed_block = vc_sign_block(block, proposer_duty) + # 2. Broadcast threshold signed block + # 3. Reconstruct complete signed block by combining threshold signed blocks + complete_signed_block = threshold_signed_block + # 4. Send complete signed block to BN for broadcast + bn_submit_block(complete_signed_block) + + # Release lock on consensus_on_block here. \ No newline at end of file