mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-19 21:04:41 +00:00
Kiln mev boost (#3062)
## Issue Addressed MEV boost compatibility ## Proposed Changes See #2987 ## Additional Info This is blocked on the stabilization of a couple specs, [here](https://github.com/ethereum/beacon-APIs/pull/194) and [here](https://github.com/flashbots/mev-boost/pull/20). Additional TODO's and outstanding questions - [ ] MEV boost JWT Auth - [ ] Will `builder_proposeBlindedBlock` return the revealed payload for the BN to propogate - [ ] Should we remove `private-tx-proposals` flag and communicate BN <> VC with blinded blocks by default once these endpoints enter the beacon-API's repo? This simplifies merge transition logic. Co-authored-by: realbigsean <seananderson33@gmail.com> Co-authored-by: realbigsean <sean@sigmaprime.io>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
use crate::beacon_node_fallback::{AllErrored, Error as FallbackError};
|
||||
use crate::{
|
||||
beacon_node_fallback::{BeaconNodeFallback, RequireSynced},
|
||||
graffiti_file::GraffitiFile,
|
||||
@@ -10,7 +11,30 @@ use slot_clock::SlotClock;
|
||||
use std::ops::Deref;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::mpsc;
|
||||
use types::{EthSpec, PublicKeyBytes, Slot};
|
||||
use types::{
|
||||
BlindedPayload, BlockType, Epoch, EthSpec, ExecPayload, FullPayload, PublicKeyBytes, Slot,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BlockError {
|
||||
Recoverable(String),
|
||||
Irrecoverable(String),
|
||||
}
|
||||
|
||||
impl From<AllErrored<BlockError>> for BlockError {
|
||||
fn from(e: AllErrored<BlockError>) -> Self {
|
||||
if e.0.iter().any(|(_, error)| {
|
||||
matches!(
|
||||
error,
|
||||
FallbackError::RequestFailed(BlockError::Irrecoverable(_))
|
||||
)
|
||||
}) {
|
||||
BlockError::Irrecoverable(e.to_string())
|
||||
} else {
|
||||
BlockError::Recoverable(e.to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `BlockService`.
|
||||
pub struct BlockServiceBuilder<T, E: EthSpec> {
|
||||
@@ -20,6 +44,7 @@ pub struct BlockServiceBuilder<T, E: EthSpec> {
|
||||
context: Option<RuntimeContext<E>>,
|
||||
graffiti: Option<Graffiti>,
|
||||
graffiti_file: Option<GraffitiFile>,
|
||||
private_tx_proposals: bool,
|
||||
}
|
||||
|
||||
impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
|
||||
@@ -31,6 +56,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
|
||||
context: None,
|
||||
graffiti: None,
|
||||
graffiti_file: None,
|
||||
private_tx_proposals: false,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,6 +90,11 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn private_tx_proposals(mut self, private_tx_proposals: bool) -> Self {
|
||||
self.private_tx_proposals = private_tx_proposals;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn build(self) -> Result<BlockService<T, E>, String> {
|
||||
Ok(BlockService {
|
||||
inner: Arc::new(Inner {
|
||||
@@ -81,6 +112,7 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockServiceBuilder<T, E> {
|
||||
.ok_or("Cannot build BlockService without runtime_context")?,
|
||||
graffiti: self.graffiti,
|
||||
graffiti_file: self.graffiti_file,
|
||||
private_tx_proposals: self.private_tx_proposals,
|
||||
}),
|
||||
})
|
||||
}
|
||||
@@ -94,6 +126,7 @@ pub struct Inner<T, E: EthSpec> {
|
||||
context: RuntimeContext<E>,
|
||||
graffiti: Option<Graffiti>,
|
||||
graffiti_file: Option<GraffitiFile>,
|
||||
private_tx_proposals: bool,
|
||||
}
|
||||
|
||||
/// Attempts to produce attestations for any block producer(s) at the start of the epoch.
|
||||
@@ -202,16 +235,46 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
|
||||
)
|
||||
}
|
||||
|
||||
let private_tx_proposals = self.private_tx_proposals;
|
||||
let merge_slot = self
|
||||
.context
|
||||
.eth2_config
|
||||
.spec
|
||||
.bellatrix_fork_epoch
|
||||
.unwrap_or_else(Epoch::max_value)
|
||||
.start_slot(E::slots_per_epoch());
|
||||
for validator_pubkey in proposers {
|
||||
let service = self.clone();
|
||||
let log = log.clone();
|
||||
self.inner.context.executor.spawn(
|
||||
async move {
|
||||
if let Err(e) = service.publish_block(slot, validator_pubkey).await {
|
||||
let publish_result = if private_tx_proposals && slot >= merge_slot {
|
||||
let mut result = service.clone()
|
||||
.publish_block::<BlindedPayload<E>>(slot, validator_pubkey)
|
||||
.await;
|
||||
match result.as_ref() {
|
||||
Err(BlockError::Recoverable(e)) => {
|
||||
error!(log, "Error whilst producing a blinded block, attempting to publish full block"; "error" => ?e);
|
||||
result = service
|
||||
.publish_block::<FullPayload<E>>(slot, validator_pubkey)
|
||||
.await;
|
||||
},
|
||||
Err(BlockError::Irrecoverable(e)) => {
|
||||
error!(log, "Error whilst producing a blinded block, cannot fallback because block was signed"; "error" => ?e);
|
||||
},
|
||||
_ => {},
|
||||
};
|
||||
result
|
||||
} else {
|
||||
service
|
||||
.publish_block::<FullPayload<E>>(slot, validator_pubkey)
|
||||
.await
|
||||
};
|
||||
if let Err(e) = publish_result {
|
||||
crit!(
|
||||
log,
|
||||
"Error whilst producing block";
|
||||
"message" => e
|
||||
"message" => ?e
|
||||
);
|
||||
}
|
||||
},
|
||||
@@ -223,25 +286,29 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
|
||||
}
|
||||
|
||||
/// Produce a block at the given slot for validator_pubkey
|
||||
async fn publish_block(
|
||||
async fn publish_block<Payload: ExecPayload<E>>(
|
||||
self,
|
||||
slot: Slot,
|
||||
validator_pubkey: PublicKeyBytes,
|
||||
) -> Result<(), String> {
|
||||
) -> Result<(), BlockError> {
|
||||
let log = self.context.log();
|
||||
let _timer =
|
||||
metrics::start_timer_vec(&metrics::BLOCK_SERVICE_TIMES, &[metrics::BEACON_BLOCK]);
|
||||
|
||||
let current_slot = self
|
||||
.slot_clock
|
||||
.now()
|
||||
.ok_or("Unable to determine current slot from clock")?;
|
||||
let current_slot = self.slot_clock.now().ok_or_else(|| {
|
||||
BlockError::Recoverable("Unable to determine current slot from clock".to_string())
|
||||
})?;
|
||||
|
||||
let randao_reveal = self
|
||||
.validator_store
|
||||
.randao_reveal(validator_pubkey, slot.epoch(E::slots_per_epoch()))
|
||||
.await
|
||||
.map_err(|e| format!("Unable to produce randao reveal signature: {:?}", e))?
|
||||
.map_err(|e| {
|
||||
BlockError::Recoverable(format!(
|
||||
"Unable to produce randao reveal signature: {:?}",
|
||||
e
|
||||
))
|
||||
})?
|
||||
.into();
|
||||
|
||||
let graffiti = self
|
||||
@@ -268,41 +335,86 @@ impl<T: SlotClock + 'static, E: EthSpec> BlockService<T, E> {
|
||||
&metrics::BLOCK_SERVICE_TIMES,
|
||||
&[metrics::BEACON_BLOCK_HTTP_GET],
|
||||
);
|
||||
let block = beacon_node
|
||||
.get_validator_blocks(slot, randao_reveal_ref, graffiti.as_ref())
|
||||
.await
|
||||
.map_err(|e| format!("Error from beacon node when producing block: {:?}", e))?
|
||||
.data;
|
||||
let block = match Payload::block_type() {
|
||||
BlockType::Full => {
|
||||
beacon_node
|
||||
.get_validator_blocks::<E, Payload>(
|
||||
slot,
|
||||
randao_reveal_ref,
|
||||
graffiti.as_ref(),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
BlockError::Recoverable(format!(
|
||||
"Error from beacon node when producing block: {:?}",
|
||||
e
|
||||
))
|
||||
})?
|
||||
.data
|
||||
}
|
||||
BlockType::Blinded => {
|
||||
beacon_node
|
||||
.get_validator_blinded_blocks::<E, Payload>(
|
||||
slot,
|
||||
randao_reveal_ref,
|
||||
graffiti.as_ref(),
|
||||
)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
BlockError::Recoverable(format!(
|
||||
"Error from beacon node when producing block: {:?}",
|
||||
e
|
||||
))
|
||||
})?
|
||||
.data
|
||||
}
|
||||
};
|
||||
drop(get_timer);
|
||||
|
||||
if proposer_index != Some(block.proposer_index()) {
|
||||
return Err(
|
||||
return Err(BlockError::Recoverable(
|
||||
"Proposer index does not match block proposer. Beacon chain re-orged"
|
||||
.to_string(),
|
||||
);
|
||||
));
|
||||
}
|
||||
|
||||
let signed_block = self_ref
|
||||
.validator_store
|
||||
.sign_block(*validator_pubkey_ref, block, current_slot)
|
||||
.sign_block::<Payload>(*validator_pubkey_ref, block, current_slot)
|
||||
.await
|
||||
.map_err(|e| format!("Unable to sign block: {:?}", e))?;
|
||||
.map_err(|e| {
|
||||
BlockError::Recoverable(format!("Unable to sign block: {:?}", e))
|
||||
})?;
|
||||
|
||||
let _post_timer = metrics::start_timer_vec(
|
||||
&metrics::BLOCK_SERVICE_TIMES,
|
||||
&[metrics::BEACON_BLOCK_HTTP_POST],
|
||||
);
|
||||
beacon_node
|
||||
.post_beacon_blocks(&signed_block)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
format!("Error from beacon node when publishing block: {:?}", e)
|
||||
})?;
|
||||
|
||||
Ok::<_, String>(signed_block)
|
||||
match Payload::block_type() {
|
||||
BlockType::Full => beacon_node
|
||||
.post_beacon_blocks(&signed_block)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
BlockError::Irrecoverable(format!(
|
||||
"Error from beacon node when publishing block: {:?}",
|
||||
e
|
||||
))
|
||||
})?,
|
||||
BlockType::Blinded => beacon_node
|
||||
.post_beacon_blinded_blocks(&signed_block)
|
||||
.await
|
||||
.map_err(|e| {
|
||||
BlockError::Irrecoverable(format!(
|
||||
"Error from beacon node when publishing block: {:?}",
|
||||
e
|
||||
))
|
||||
})?,
|
||||
}
|
||||
|
||||
Ok::<_, BlockError>(signed_block)
|
||||
})
|
||||
.await
|
||||
.map_err(|e| e.to_string())?;
|
||||
.await?;
|
||||
|
||||
info!(
|
||||
log,
|
||||
|
||||
@@ -258,4 +258,12 @@ pub fn cli_app<'a, 'b>() -> App<'a, 'b> {
|
||||
immediately.")
|
||||
.takes_value(false),
|
||||
)
|
||||
.arg(
|
||||
Arg::with_name("private-tx-proposals")
|
||||
.long("private-tx-proposals")
|
||||
.help("If this flag is set, Lighthouse will query the Beacon Node for only block \
|
||||
headers during proposals and will sign over headers. Useful for outsourcing \
|
||||
execution payload construction during proposals.")
|
||||
.takes_value(false),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -55,6 +55,7 @@ pub struct Config {
|
||||
/// If true, enable functionality that monitors the network for attestations or proposals from
|
||||
/// any of the validators managed by this client before starting up.
|
||||
pub enable_doppelganger_protection: bool,
|
||||
pub private_tx_proposals: bool,
|
||||
/// A list of custom certificates that the validator client will additionally use when
|
||||
/// connecting to a beacon node over SSL/TLS.
|
||||
pub beacon_nodes_tls_certs: Option<Vec<PathBuf>>,
|
||||
@@ -91,6 +92,7 @@ impl Default for Config {
|
||||
monitoring_api: None,
|
||||
enable_doppelganger_protection: false,
|
||||
beacon_nodes_tls_certs: None,
|
||||
private_tx_proposals: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,6 +308,10 @@ impl Config {
|
||||
config.enable_doppelganger_protection = true;
|
||||
}
|
||||
|
||||
if cli_args.is_present("private-tx-proposals") {
|
||||
config.private_tx_proposals = true;
|
||||
}
|
||||
|
||||
Ok(config)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -400,6 +400,7 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
.runtime_context(context.service_context("block".into()))
|
||||
.graffiti(config.graffiti)
|
||||
.graffiti_file(config.graffiti_file.clone())
|
||||
.private_tx_proposals(config.private_tx_proposals)
|
||||
.build()?;
|
||||
|
||||
let attestation_service = AttestationServiceBuilder::new()
|
||||
|
||||
@@ -33,9 +33,9 @@ pub enum Error {
|
||||
}
|
||||
|
||||
/// Enumerates all messages that can be signed by a validator.
|
||||
pub enum SignableMessage<'a, T: EthSpec> {
|
||||
pub enum SignableMessage<'a, T: EthSpec, Payload: ExecPayload<T> = FullPayload<T>> {
|
||||
RandaoReveal(Epoch),
|
||||
BeaconBlock(&'a BeaconBlock<T>),
|
||||
BeaconBlock(&'a BeaconBlock<T, Payload>),
|
||||
AttestationData(&'a AttestationData),
|
||||
SignedAggregateAndProof(&'a AggregateAndProof<T>),
|
||||
SelectionProof(Slot),
|
||||
@@ -47,7 +47,7 @@ pub enum SignableMessage<'a, T: EthSpec> {
|
||||
SignedContributionAndProof(&'a ContributionAndProof<T>),
|
||||
}
|
||||
|
||||
impl<'a, T: EthSpec> SignableMessage<'a, T> {
|
||||
impl<'a, T: EthSpec, Payload: ExecPayload<T>> SignableMessage<'a, T, Payload> {
|
||||
/// Returns the `SignedRoot` for the contained message.
|
||||
///
|
||||
/// The actual `SignedRoot` trait is not used since it also requires a `TreeHash` impl, which is
|
||||
@@ -113,9 +113,9 @@ impl SigningContext {
|
||||
|
||||
impl SigningMethod {
|
||||
/// Return the signature of `signable_message`, with respect to the `signing_context`.
|
||||
pub async fn get_signature<T: EthSpec>(
|
||||
pub async fn get_signature<T: EthSpec, Payload: ExecPayload<T>>(
|
||||
&self,
|
||||
signable_message: SignableMessage<'_, T>,
|
||||
signable_message: SignableMessage<'_, T, Payload>,
|
||||
signing_context: SigningContext,
|
||||
spec: &ChainSpec,
|
||||
executor: &TaskExecutor,
|
||||
|
||||
@@ -34,7 +34,7 @@ pub struct ForkInfo {
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
#[serde(bound = "T: EthSpec", rename_all = "snake_case")]
|
||||
pub enum Web3SignerObject<'a, T: EthSpec> {
|
||||
pub enum Web3SignerObject<'a, T: EthSpec, Payload: ExecPayload<T>> {
|
||||
AggregationSlot {
|
||||
slot: Slot,
|
||||
},
|
||||
@@ -42,7 +42,7 @@ pub enum Web3SignerObject<'a, T: EthSpec> {
|
||||
Attestation(&'a AttestationData),
|
||||
BeaconBlock {
|
||||
version: ForkName,
|
||||
block: &'a BeaconBlock<T>,
|
||||
block: &'a BeaconBlock<T, Payload>,
|
||||
},
|
||||
#[allow(dead_code)]
|
||||
Deposit {
|
||||
@@ -66,8 +66,8 @@ pub enum Web3SignerObject<'a, T: EthSpec> {
|
||||
ContributionAndProof(&'a ContributionAndProof<T>),
|
||||
}
|
||||
|
||||
impl<'a, T: EthSpec> Web3SignerObject<'a, T> {
|
||||
pub fn beacon_block(block: &'a BeaconBlock<T>) -> Result<Self, Error> {
|
||||
impl<'a, T: EthSpec, Payload: ExecPayload<T>> Web3SignerObject<'a, T, Payload> {
|
||||
pub fn beacon_block(block: &'a BeaconBlock<T, Payload>) -> Result<Self, Error> {
|
||||
let version = match block {
|
||||
BeaconBlock::Base(_) => ForkName::Phase0,
|
||||
BeaconBlock::Altair(_) => ForkName::Altair,
|
||||
@@ -99,7 +99,7 @@ impl<'a, T: EthSpec> Web3SignerObject<'a, T> {
|
||||
|
||||
#[derive(Debug, PartialEq, Serialize)]
|
||||
#[serde(bound = "T: EthSpec")]
|
||||
pub struct SigningRequest<'a, T: EthSpec> {
|
||||
pub struct SigningRequest<'a, T: EthSpec, Payload: ExecPayload<T>> {
|
||||
#[serde(rename = "type")]
|
||||
pub message_type: MessageType,
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
@@ -107,7 +107,7 @@ pub struct SigningRequest<'a, T: EthSpec> {
|
||||
#[serde(rename = "signingRoot")]
|
||||
pub signing_root: Hash256,
|
||||
#[serde(flatten)]
|
||||
pub object: Web3SignerObject<'a, T>,
|
||||
pub object: Web3SignerObject<'a, T, Payload>,
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Deserialize)]
|
||||
|
||||
@@ -18,10 +18,11 @@ use std::sync::Arc;
|
||||
use task_executor::TaskExecutor;
|
||||
use types::{
|
||||
attestation::Error as AttestationError, graffiti::GraffitiString, Address, AggregateAndProof,
|
||||
Attestation, BeaconBlock, ChainSpec, ContributionAndProof, Domain, Epoch, EthSpec, Fork,
|
||||
Graffiti, Hash256, Keypair, PublicKeyBytes, SelectionProof, Signature, SignedAggregateAndProof,
|
||||
SignedBeaconBlock, SignedContributionAndProof, Slot, SyncAggregatorSelectionData,
|
||||
SyncCommitteeContribution, SyncCommitteeMessage, SyncSelectionProof, SyncSubnetId,
|
||||
Attestation, BeaconBlock, BlindedPayload, ChainSpec, ContributionAndProof, Domain, Epoch,
|
||||
EthSpec, ExecPayload, Fork, Graffiti, Hash256, Keypair, PublicKeyBytes, SelectionProof,
|
||||
Signature, SignedAggregateAndProof, SignedBeaconBlock, SignedContributionAndProof, Slot,
|
||||
SyncAggregatorSelectionData, SyncCommitteeContribution, SyncCommitteeMessage,
|
||||
SyncSelectionProof, SyncSubnetId,
|
||||
};
|
||||
use validator_dir::ValidatorDir;
|
||||
|
||||
@@ -338,7 +339,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
let signing_context = self.signing_context(Domain::Randao, signing_epoch);
|
||||
|
||||
let signature = signing_method
|
||||
.get_signature::<E>(
|
||||
.get_signature::<E, BlindedPayload<E>>(
|
||||
SignableMessage::RandaoReveal(signing_epoch),
|
||||
signing_context,
|
||||
&self.spec,
|
||||
@@ -359,12 +360,12 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
.suggested_fee_recipient(validator_pubkey)
|
||||
}
|
||||
|
||||
pub async fn sign_block(
|
||||
pub async fn sign_block<Payload: ExecPayload<E>>(
|
||||
&self,
|
||||
validator_pubkey: PublicKeyBytes,
|
||||
block: BeaconBlock<E>,
|
||||
block: BeaconBlock<E, Payload>,
|
||||
current_slot: Slot,
|
||||
) -> Result<SignedBeaconBlock<E>, Error> {
|
||||
) -> Result<SignedBeaconBlock<E, Payload>, Error> {
|
||||
// Make sure the block slot is not higher than the current slot to avoid potential attacks.
|
||||
if block.slot() > current_slot {
|
||||
warn!(
|
||||
@@ -397,7 +398,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
|
||||
let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?;
|
||||
let signature = signing_method
|
||||
.get_signature(
|
||||
.get_signature::<E, Payload>(
|
||||
SignableMessage::BeaconBlock(&block),
|
||||
signing_context,
|
||||
&self.spec,
|
||||
@@ -466,7 +467,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
Ok(Safe::Valid) => {
|
||||
let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?;
|
||||
let signature = signing_method
|
||||
.get_signature::<E>(
|
||||
.get_signature::<E, BlindedPayload<E>>(
|
||||
SignableMessage::AttestationData(&attestation.data),
|
||||
signing_context,
|
||||
&self.spec,
|
||||
@@ -543,7 +544,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
|
||||
let signing_method = self.doppelganger_checked_signing_method(validator_pubkey)?;
|
||||
let signature = signing_method
|
||||
.get_signature(
|
||||
.get_signature::<E, BlindedPayload<E>>(
|
||||
SignableMessage::SignedAggregateAndProof(&message),
|
||||
signing_context,
|
||||
&self.spec,
|
||||
@@ -576,7 +577,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
let signing_method = self.doppelganger_bypassed_signing_method(validator_pubkey)?;
|
||||
|
||||
let signature = signing_method
|
||||
.get_signature::<E>(
|
||||
.get_signature::<E, BlindedPayload<E>>(
|
||||
SignableMessage::SelectionProof(slot),
|
||||
signing_context,
|
||||
&self.spec,
|
||||
@@ -615,7 +616,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
};
|
||||
|
||||
let signature = signing_method
|
||||
.get_signature::<E>(
|
||||
.get_signature::<E, BlindedPayload<E>>(
|
||||
SignableMessage::SyncSelectionProof(&message),
|
||||
signing_context,
|
||||
&self.spec,
|
||||
@@ -641,7 +642,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
let signing_method = self.doppelganger_bypassed_signing_method(*validator_pubkey)?;
|
||||
|
||||
let signature = signing_method
|
||||
.get_signature::<E>(
|
||||
.get_signature::<E, BlindedPayload<E>>(
|
||||
SignableMessage::SyncCommitteeSignature {
|
||||
beacon_block_root,
|
||||
slot,
|
||||
@@ -686,7 +687,7 @@ impl<T: SlotClock + 'static, E: EthSpec> ValidatorStore<T, E> {
|
||||
};
|
||||
|
||||
let signature = signing_method
|
||||
.get_signature(
|
||||
.get_signature::<E, BlindedPayload<E>>(
|
||||
SignableMessage::SignedContributionAndProof(&message),
|
||||
signing_context,
|
||||
&self.spec,
|
||||
|
||||
Reference in New Issue
Block a user