Skip to content

Commit b6413ef

Browse files
committed
switch to enum for block signing to allow differing types
1 parent d58bd3e commit b6413ef

File tree

3 files changed

+175
-132
lines changed

3 files changed

+175
-132
lines changed

validator_client/lighthouse_validator_store/src/lib.rs

Lines changed: 116 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,8 @@ use types::{
2424
VoluntaryExit,
2525
};
2626
use validator_store::{
27-
DoppelgangerStatus, Error as ValidatorStoreError, ProposalData, SignBlock, ValidatorStore,
27+
DoppelgangerStatus, Error as ValidatorStoreError, ProposalData, SignedBlock, UnsignedBlock,
28+
ValidatorStore,
2829
};
2930

3031
pub type Error = ValidatorStoreError<SigningError>;
@@ -420,6 +421,102 @@ impl<T: SlotClock + 'static, E: EthSpec> LighthouseValidatorStore<T, E> {
420421
)
421422
})
422423
}
424+
425+
async fn sign_abstract_block<Payload: AbstractExecPayload<E>>(
426+
&self,
427+
validator_pubkey: PublicKeyBytes,
428+
block: BeaconBlock<E, Payload>,
429+
current_slot: Slot,
430+
) -> Result<SignedBeaconBlock<E, Payload>, Error> {
431+
// Make sure the block slot is not higher than the current slot to avoid potential attacks.
432+
if block.slot() > current_slot {
433+
warn!(
434+
self.log,
435+
"Not signing block with slot greater than current slot";
436+
"block_slot" => block.slot().as_u64(),
437+
"current_slot" => current_slot.as_u64()
438+
);
439+
return Err(Error::GreaterThanCurrentSlot {
440+
slot: block.slot(),
441+
current_slot,
442+
});
443+
}
444+
445+
let signing_epoch = block.epoch();
446+
let signing_context = self.signing_context(Domain::BeaconProposer, signing_epoch);
447+
let domain_hash = signing_context.domain_hash(&self.spec);
448+
449+
let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?;
450+
451+
// Check for slashing conditions.
452+
let slashing_status = if signing_method
453+
.requires_local_slashing_protection(self.enable_web3signer_slashing_protection)
454+
{
455+
self.slashing_protection.check_and_insert_block_proposal(
456+
&validator_pubkey,
457+
&block.block_header(),
458+
domain_hash,
459+
)
460+
} else {
461+
Ok(Safe::Valid)
462+
};
463+
464+
match slashing_status {
465+
// We can safely sign this block without slashing.
466+
Ok(Safe::Valid) => {
467+
validator_metrics::inc_counter_vec(
468+
&validator_metrics::SIGNED_BLOCKS_TOTAL,
469+
&[validator_metrics::SUCCESS],
470+
);
471+
472+
let signature = signing_method
473+
.get_signature(
474+
SignableMessage::BeaconBlock(&block),
475+
signing_context,
476+
&self.spec,
477+
&self.task_executor,
478+
)
479+
.await?;
480+
Ok(SignedBeaconBlock::from_block(block, signature))
481+
}
482+
Ok(Safe::SameData) => {
483+
warn!(
484+
self.log,
485+
"Skipping signing of previously signed block";
486+
);
487+
validator_metrics::inc_counter_vec(
488+
&validator_metrics::SIGNED_BLOCKS_TOTAL,
489+
&[validator_metrics::SAME_DATA],
490+
);
491+
Err(Error::SameData)
492+
}
493+
Err(NotSafe::UnregisteredValidator(pk)) => {
494+
warn!(
495+
self.log,
496+
"Not signing block for unregistered validator";
497+
"msg" => "Carefully consider running with --init-slashing-protection (see --help)",
498+
"public_key" => format!("{:?}", pk)
499+
);
500+
validator_metrics::inc_counter_vec(
501+
&validator_metrics::SIGNED_BLOCKS_TOTAL,
502+
&[validator_metrics::UNREGISTERED],
503+
);
504+
Err(Error::Slashable(NotSafe::UnregisteredValidator(pk)))
505+
}
506+
Err(e) => {
507+
crit!(
508+
self.log,
509+
"Not signing slashable block";
510+
"error" => format!("{:?}", e)
511+
);
512+
validator_metrics::inc_counter_vec(
513+
&validator_metrics::SIGNED_BLOCKS_TOTAL,
514+
&[validator_metrics::SLASHABLE],
515+
);
516+
Err(Error::Slashable(e))
517+
}
518+
}
519+
}
423520
}
424521

425522
impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore for LighthouseValidatorStore<T, E> {
@@ -578,6 +675,24 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore for LighthouseValidatorS
578675
.set_index(validator_pubkey, index);
579676
}
580677

678+
async fn sign_block(
679+
&self,
680+
validator_pubkey: PublicKeyBytes,
681+
block: UnsignedBlock<E>,
682+
current_slot: Slot,
683+
) -> Result<SignedBlock<E>, Error> {
684+
match block {
685+
UnsignedBlock::Full(block) => self
686+
.sign_abstract_block(validator_pubkey, block, current_slot)
687+
.await
688+
.map(SignedBlock::Full),
689+
UnsignedBlock::Blinded(block) => self
690+
.sign_abstract_block(validator_pubkey, block, current_slot)
691+
.await
692+
.map(SignedBlock::Blinded),
693+
}
694+
}
695+
581696
async fn sign_attestation(
582697
&self,
583698
validator_pubkey: PublicKeyBytes,
@@ -999,103 +1114,3 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore for LighthouseValidatorS
9991114
})
10001115
}
10011116
}
1002-
1003-
impl<T: SlotClock + 'static, E: EthSpec, P: AbstractExecPayload<E>>
1004-
SignBlock<E, P, signing_method::Error> for LighthouseValidatorStore<T, E>
1005-
{
1006-
async fn sign_block(
1007-
&self,
1008-
validator_pubkey: PublicKeyBytes,
1009-
block: BeaconBlock<E, P>,
1010-
current_slot: Slot,
1011-
) -> Result<SignedBeaconBlock<E, P>, Error> {
1012-
// Make sure the block slot is not higher than the current slot to avoid potential attacks.
1013-
if block.slot() > current_slot {
1014-
warn!(
1015-
self.log,
1016-
"Not signing block with slot greater than current slot";
1017-
"block_slot" => block.slot().as_u64(),
1018-
"current_slot" => current_slot.as_u64()
1019-
);
1020-
return Err(Error::GreaterThanCurrentSlot {
1021-
slot: block.slot(),
1022-
current_slot,
1023-
});
1024-
}
1025-
1026-
let signing_epoch = block.epoch();
1027-
let signing_context = self.signing_context(Domain::BeaconProposer, signing_epoch);
1028-
let domain_hash = signing_context.domain_hash(&self.spec);
1029-
1030-
let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?;
1031-
1032-
// Check for slashing conditions.
1033-
let slashing_status = if signing_method
1034-
.requires_local_slashing_protection(self.enable_web3signer_slashing_protection)
1035-
{
1036-
self.slashing_protection.check_and_insert_block_proposal(
1037-
&validator_pubkey,
1038-
&block.block_header(),
1039-
domain_hash,
1040-
)
1041-
} else {
1042-
Ok(Safe::Valid)
1043-
};
1044-
1045-
match slashing_status {
1046-
// We can safely sign this block without slashing.
1047-
Ok(Safe::Valid) => {
1048-
validator_metrics::inc_counter_vec(
1049-
&validator_metrics::SIGNED_BLOCKS_TOTAL,
1050-
&[validator_metrics::SUCCESS],
1051-
);
1052-
1053-
let signature = signing_method
1054-
.get_signature(
1055-
SignableMessage::BeaconBlock(&block),
1056-
signing_context,
1057-
&self.spec,
1058-
&self.task_executor,
1059-
)
1060-
.await?;
1061-
Ok(SignedBeaconBlock::from_block(block, signature))
1062-
}
1063-
Ok(Safe::SameData) => {
1064-
warn!(
1065-
self.log,
1066-
"Skipping signing of previously signed block";
1067-
);
1068-
validator_metrics::inc_counter_vec(
1069-
&validator_metrics::SIGNED_BLOCKS_TOTAL,
1070-
&[validator_metrics::SAME_DATA],
1071-
);
1072-
Err(Error::SameData)
1073-
}
1074-
Err(NotSafe::UnregisteredValidator(pk)) => {
1075-
warn!(
1076-
self.log,
1077-
"Not signing block for unregistered validator";
1078-
"msg" => "Carefully consider running with --init-slashing-protection (see --help)",
1079-
"public_key" => format!("{:?}", pk)
1080-
);
1081-
validator_metrics::inc_counter_vec(
1082-
&validator_metrics::SIGNED_BLOCKS_TOTAL,
1083-
&[validator_metrics::UNREGISTERED],
1084-
);
1085-
Err(Error::Slashable(NotSafe::UnregisteredValidator(pk)))
1086-
}
1087-
Err(e) => {
1088-
crit!(
1089-
self.log,
1090-
"Not signing slashable block";
1091-
"error" => format!("{:?}", e)
1092-
);
1093-
validator_metrics::inc_counter_vec(
1094-
&validator_metrics::SIGNED_BLOCKS_TOTAL,
1095-
&[validator_metrics::SLASHABLE],
1096-
);
1097-
Err(Error::Slashable(e))
1098-
}
1099-
}
1100-
}
1101-
}

validator_client/validator_services/src/block_service.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -333,22 +333,27 @@ impl<S: ValidatorStore + 'static, T: SlotClock + 'static> BlockService<S, T> {
333333
) -> Result<(), BlockError> {
334334
let signing_timer = validator_metrics::start_timer(&validator_metrics::BLOCK_SIGNING_TIMES);
335335

336-
let res = match unsigned_block {
336+
let (block, maybe_blobs) = match unsigned_block {
337337
UnsignedBlock::Full(block_contents) => {
338338
let (block, maybe_blobs) = block_contents.deconstruct();
339-
self.validator_store
340-
.sign_block(*validator_pubkey, block, slot)
341-
.await
342-
.map(|b| SignedBlock::Full(PublishBlockRequest::new(Arc::new(b), maybe_blobs)))
339+
(block.into(), maybe_blobs)
343340
}
344-
UnsignedBlock::Blinded(block) => self
345-
.validator_store
346-
.sign_block(*validator_pubkey, block, slot)
347-
.await
348-
.map(Arc::new)
349-
.map(SignedBlock::Blinded),
341+
UnsignedBlock::Blinded(block) => (block.into(), None),
350342
};
351343

344+
let res = self
345+
.validator_store
346+
.sign_block(*validator_pubkey, block, slot)
347+
.await
348+
.map(|block| match block {
349+
validator_store::SignedBlock::Full(block) => {
350+
SignedBlock::Full(PublishBlockRequest::new(Arc::new(block), maybe_blobs))
351+
}
352+
validator_store::SignedBlock::Blinded(block) => {
353+
SignedBlock::Blinded(Arc::new(block))
354+
}
355+
});
356+
352357
let signed_block = match res {
353358
Ok(block) => block,
354359
Err(ValidatorStoreError::UnknownPubkey(pubkey)) => {

validator_client/validator_store/src/lib.rs

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ use slashing_protection::NotSafe;
22
use std::fmt::Debug;
33
use std::future::Future;
44
use types::{
5-
AbstractExecPayload, Address, Attestation, AttestationError, BeaconBlock, BlindedPayload,
6-
Epoch, EthSpec, FullPayload, Graffiti, Hash256, PublicKeyBytes, SelectionProof, Signature,
7-
SignedAggregateAndProof, SignedBeaconBlock, SignedContributionAndProof,
5+
Address, Attestation, AttestationError, BeaconBlock, BlindedBeaconBlock, Epoch, EthSpec,
6+
Graffiti, Hash256, PublicKeyBytes, SelectionProof, Signature, SignedAggregateAndProof,
7+
SignedBeaconBlock, SignedBlindedBeaconBlock, SignedContributionAndProof,
88
SignedValidatorRegistrationData, SignedVoluntaryExit, Slot, SyncCommitteeContribution,
99
SyncCommitteeMessage, SyncSelectionProof, SyncSubnetId, ValidatorRegistrationData,
1010
VoluntaryExit,
@@ -37,12 +37,7 @@ pub struct ProposalData {
3737
pub builder_proposals: bool,
3838
}
3939

40-
pub trait ValidatorStore:
41-
SignBlock<Self::E, FullPayload<Self::E>, Self::Error>
42-
+ SignBlock<Self::E, BlindedPayload<Self::E>, Self::Error>
43-
+ Send
44-
+ Sync
45-
{
40+
pub trait ValidatorStore: Send + Sync {
4641
type Error: Debug + Send + Sync;
4742
type E: EthSpec;
4843

@@ -99,6 +94,13 @@ pub trait ValidatorStore:
9994

10095
fn set_validator_index(&self, validator_pubkey: &PublicKeyBytes, index: u64);
10196

97+
fn sign_block(
98+
&self,
99+
validator_pubkey: PublicKeyBytes,
100+
block: UnsignedBlock<Self::E>,
101+
current_slot: Slot,
102+
) -> impl Future<Output = Result<SignedBlock<Self::E>, Error<Self::Error>>> + Send;
103+
102104
fn sign_attestation(
103105
&self,
104106
validator_pubkey: PublicKeyBytes,
@@ -175,17 +177,38 @@ pub trait ValidatorStore:
175177
fn proposal_data(&self, pubkey: &PublicKeyBytes) -> Option<ProposalData>;
176178
}
177179

178-
// This is a workaround: directly adding this fn into `ValidatorStore`, abstract over P, causes
179-
// weird compiler errors which I suspect are compiler bugs
180-
// Another advantage of this separate trait is to allow implementors separate implementations for
181-
// different payload
182-
pub trait SignBlock<E: EthSpec, P: AbstractExecPayload<E>, Err> {
183-
fn sign_block(
184-
&self,
185-
validator_pubkey: PublicKeyBytes,
186-
block: BeaconBlock<E, P>,
187-
current_slot: Slot,
188-
) -> impl Future<Output = Result<SignedBeaconBlock<E, P>, Error<Err>>> + Send;
180+
pub enum UnsignedBlock<E: EthSpec> {
181+
Full(BeaconBlock<E>),
182+
Blinded(BlindedBeaconBlock<E>),
183+
}
184+
185+
impl<E: EthSpec> From<BeaconBlock<E>> for UnsignedBlock<E> {
186+
fn from(block: BeaconBlock<E>) -> Self {
187+
UnsignedBlock::Full(block)
188+
}
189+
}
190+
191+
impl<E: EthSpec> From<BlindedBeaconBlock<E>> for UnsignedBlock<E> {
192+
fn from(block: BlindedBeaconBlock<E>) -> Self {
193+
UnsignedBlock::Blinded(block)
194+
}
195+
}
196+
197+
pub enum SignedBlock<E: EthSpec> {
198+
Full(SignedBeaconBlock<E>),
199+
Blinded(SignedBlindedBeaconBlock<E>),
200+
}
201+
202+
impl<E: EthSpec> From<SignedBeaconBlock<E>> for SignedBlock<E> {
203+
fn from(block: SignedBeaconBlock<E>) -> Self {
204+
SignedBlock::Full(block)
205+
}
206+
}
207+
208+
impl<E: EthSpec> From<SignedBlindedBeaconBlock<E>> for SignedBlock<E> {
209+
fn from(block: SignedBlindedBeaconBlock<E>) -> Self {
210+
SignedBlock::Blinded(block)
211+
}
189212
}
190213

191214
/// A wrapper around `PublicKeyBytes` which encodes information about the status of a validator

0 commit comments

Comments
 (0)