Merge remote-tracking branch 'origin/unstable' into tree-states

This commit is contained in:
Michael Sproul
2024-01-25 15:10:19 +11:00
91 changed files with 2255 additions and 654 deletions

View File

@@ -46,6 +46,9 @@ pub const EXECUTION_PAYLOAD_BLINDED_HEADER: &str = "Eth-Execution-Payload-Blinde
pub const EXECUTION_PAYLOAD_VALUE_HEADER: &str = "Eth-Execution-Payload-Value";
pub const CONSENSUS_BLOCK_VALUE_HEADER: &str = "Eth-Consensus-Block-Value";
pub const CONTENT_TYPE_HEADER: &str = "Content-Type";
pub const SSZ_CONTENT_TYPE_HEADER: &str = "application/octet-stream";
#[derive(Debug)]
pub enum Error {
/// The `reqwest` client raised an error.
@@ -374,25 +377,6 @@ impl BeaconNodeHttpClient {
ok_or_error(response).await
}
/// Generic POST function supporting arbitrary responses and timeouts.
async fn post_generic_with_ssz_body<T: Into<Body>, U: IntoUrl>(
&self,
url: U,
body: T,
timeout: Option<Duration>,
) -> Result<Response, Error> {
let mut builder = self.client.post(url);
if let Some(timeout) = timeout {
builder = builder.timeout(timeout);
}
let response = builder
.header("Content-Type", "application/octet-stream")
.body(body)
.send()
.await?;
ok_or_error(response).await
}
/// Generic POST function supporting arbitrary responses and timeouts.
async fn post_generic_with_consensus_version<T: Serialize, U: IntoUrl>(
&self,
@@ -863,10 +847,11 @@ impl BeaconNodeHttpClient {
.push("beacon")
.push("blocks");
self.post_generic_with_ssz_body(
self.post_generic_with_consensus_version_and_ssz_body(
path,
block_contents.as_ssz_bytes(),
Some(self.timeouts.proposal),
block_contents.signed_block().fork_name_unchecked(),
)
.await?;
@@ -907,8 +892,13 @@ impl BeaconNodeHttpClient {
.push("beacon")
.push("blinded_blocks");
self.post_generic_with_ssz_body(path, block.as_ssz_bytes(), Some(self.timeouts.proposal))
.await?;
self.post_generic_with_consensus_version_and_ssz_body(
path,
block.as_ssz_bytes(),
Some(self.timeouts.proposal),
block.fork_name_unchecked(),
)
.await?;
Ok(())
}
@@ -1074,8 +1064,18 @@ impl BeaconNodeHttpClient {
pub async fn get_blobs<T: EthSpec>(
&self,
block_id: BlockId,
indices: Option<&[u64]>,
) -> Result<Option<GenericResponse<BlobSidecarList<T>>>, Error> {
let path = self.get_blobs_path(block_id)?;
let mut path = self.get_blobs_path(block_id)?;
if let Some(indices) = indices {
let indices_string = indices
.iter()
.map(|i| i.to_string())
.collect::<Vec<_>>()
.join(",");
path.query_pairs_mut()
.append_pair("indices", &indices_string);
}
let Some(response) = self.get_response(path, |b| b).await.optional()? else {
return Ok(None);
};

View File

@@ -483,12 +483,15 @@ impl ValidatorClientHttpClient {
}
/// `PATCH lighthouse/validators/{validator_pubkey}`
#[allow(clippy::too_many_arguments)]
pub async fn patch_lighthouse_validators(
&self,
voting_pubkey: &PublicKeyBytes,
enabled: Option<bool>,
gas_limit: Option<u64>,
builder_proposals: Option<bool>,
builder_boost_factor: Option<u64>,
prefer_builder_proposals: Option<bool>,
graffiti: Option<GraffitiString>,
) -> Result<(), Error> {
let mut path = self.server.full.clone();
@@ -505,6 +508,8 @@ impl ValidatorClientHttpClient {
enabled,
gas_limit,
builder_proposals,
builder_boost_factor,
prefer_builder_proposals,
graffiti,
},
)

View File

@@ -32,6 +32,12 @@ pub struct ValidatorRequest {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub builder_proposals: Option<bool>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub builder_boost_factor: Option<u64>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub prefer_builder_proposals: Option<bool>,
#[serde(with = "serde_utils::quoted_u64")]
pub deposit_gwei: u64,
}
@@ -86,6 +92,12 @@ pub struct ValidatorPatchRequest {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub graffiti: Option<GraffitiString>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub builder_boost_factor: Option<u64>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub prefer_builder_proposals: Option<bool>,
}
#[derive(Clone, PartialEq, Serialize, Deserialize)]
@@ -105,6 +117,12 @@ pub struct KeystoreValidatorsPostRequest {
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub builder_proposals: Option<bool>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub builder_boost_factor: Option<u64>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub prefer_builder_proposals: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@@ -135,6 +153,12 @@ pub struct Web3SignerValidatorRequest {
pub client_identity_path: Option<PathBuf>,
#[serde(skip_serializing_if = "Option::is_none")]
pub client_identity_password: Option<String>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub builder_boost_factor: Option<u64>,
#[serde(default)]
#[serde(skip_serializing_if = "Option::is_none")]
pub prefer_builder_proposals: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]

View File

@@ -15,6 +15,7 @@ use ssz_derive::{Decode, Encode};
use std::convert::TryFrom;
use std::fmt::{self, Display};
use std::str::{from_utf8, FromStr};
use std::sync::Arc;
use std::time::Duration;
use types::beacon_block_body::KzgCommitments;
pub use types::*;
@@ -1480,15 +1481,15 @@ mod tests {
type E = MainnetEthSpec;
let spec = ForkName::Capella.make_genesis_spec(E::default_spec());
let block: PublishBlockRequest<E> = SignedBeaconBlock::from_block(
let block: PublishBlockRequest<E> = Arc::new(SignedBeaconBlock::from_block(
BeaconBlock::<E>::Capella(BeaconBlockCapella::empty(&spec)),
Signature::empty(),
)
))
.try_into()
.expect("should convert into signed block contents");
let decoded: PublishBlockRequest<E> =
PublishBlockRequest::from_ssz_bytes(&block.as_ssz_bytes(), &spec)
PublishBlockRequest::from_ssz_bytes(&block.as_ssz_bytes(), ForkName::Capella)
.expect("should decode Block");
assert!(matches!(decoded, PublishBlockRequest::Block(_)));
}
@@ -1504,11 +1505,14 @@ mod tests {
);
let blobs = BlobsList::<E>::from(vec![Blob::<E>::default()]);
let kzg_proofs = KzgProofs::<E>::from(vec![KzgProof::empty()]);
let signed_block_contents = PublishBlockRequest::new(block, Some((kzg_proofs, blobs)));
let signed_block_contents =
PublishBlockRequest::new(Arc::new(block), Some((kzg_proofs, blobs)));
let decoded: PublishBlockRequest<E> =
PublishBlockRequest::from_ssz_bytes(&signed_block_contents.as_ssz_bytes(), &spec)
.expect("should decode BlockAndBlobSidecars");
let decoded: PublishBlockRequest<E> = PublishBlockRequest::from_ssz_bytes(
&signed_block_contents.as_ssz_bytes(),
ForkName::Deneb,
)
.expect("should decode BlockAndBlobSidecars");
assert!(matches!(decoded, PublishBlockRequest::BlockContents(_)));
}
}
@@ -1643,7 +1647,7 @@ impl<T: EthSpec> FullBlockContents<T> {
) -> PublishBlockRequest<T> {
let (block, maybe_blobs) = self.deconstruct();
let signed_block = block.sign(secret_key, fork, genesis_validators_root, spec);
PublishBlockRequest::new(signed_block, maybe_blobs)
PublishBlockRequest::new(Arc::new(signed_block), maybe_blobs)
}
}
@@ -1674,7 +1678,10 @@ impl<T: EthSpec> Into<BeaconBlock<T>> for FullBlockContents<T> {
}
}
pub type SignedBlockContentsTuple<T> = (SignedBeaconBlock<T>, Option<(KzgProofs<T>, BlobsList<T>)>);
pub type SignedBlockContentsTuple<T> = (
Arc<SignedBeaconBlock<T>>,
Option<(KzgProofs<T>, BlobsList<T>)>,
);
fn parse_required_header<T>(
headers: &HeaderMap,
@@ -1729,12 +1736,12 @@ impl TryFrom<&HeaderMap> for ProduceBlockV3Metadata {
#[ssz(enum_behaviour = "transparent")]
pub enum PublishBlockRequest<T: EthSpec> {
BlockContents(SignedBlockContents<T>),
Block(SignedBeaconBlock<T>),
Block(Arc<SignedBeaconBlock<T>>),
}
impl<T: EthSpec> PublishBlockRequest<T> {
pub fn new(
block: SignedBeaconBlock<T>,
block: Arc<SignedBeaconBlock<T>>,
blob_items: Option<(KzgProofs<T>, BlobsList<T>)>,
) -> Self {
match blob_items {
@@ -1747,23 +1754,12 @@ impl<T: EthSpec> PublishBlockRequest<T> {
}
}
/// SSZ decode with fork variant determined by slot.
pub fn from_ssz_bytes(bytes: &[u8], spec: &ChainSpec) -> Result<Self, ssz::DecodeError> {
let slot_len = <Slot as Decode>::ssz_fixed_len();
let slot_bytes = bytes
.get(0..slot_len)
.ok_or(DecodeError::InvalidByteLength {
len: bytes.len(),
expected: slot_len,
})?;
let slot = Slot::from_ssz_bytes(slot_bytes)?;
let fork_at_slot = spec.fork_name_at_slot::<T>(slot);
match fork_at_slot {
/// SSZ decode with fork variant determined by `fork_name`.
pub fn from_ssz_bytes(bytes: &[u8], fork_name: ForkName) -> Result<Self, ssz::DecodeError> {
match fork_name {
ForkName::Base | ForkName::Altair | ForkName::Merge | ForkName::Capella => {
SignedBeaconBlock::from_ssz_bytes(bytes, spec)
.map(|block| PublishBlockRequest::Block(block))
SignedBeaconBlock::from_ssz_bytes_for_fork(bytes, fork_name)
.map(|block| PublishBlockRequest::Block(Arc::new(block)))
}
ForkName::Deneb => {
let mut builder = ssz::SszDecoderBuilder::new(bytes);
@@ -1772,16 +1768,20 @@ impl<T: EthSpec> PublishBlockRequest<T> {
builder.register_type::<BlobsList<T>>()?;
let mut decoder = builder.build()?;
let block = decoder
.decode_next_with(|bytes| SignedBeaconBlock::from_ssz_bytes(bytes, spec))?;
let block = decoder.decode_next_with(|bytes| {
SignedBeaconBlock::from_ssz_bytes_for_fork(bytes, fork_name)
})?;
let kzg_proofs = decoder.decode_next()?;
let blobs = decoder.decode_next()?;
Ok(PublishBlockRequest::new(block, Some((kzg_proofs, blobs))))
Ok(PublishBlockRequest::new(
Arc::new(block),
Some((kzg_proofs, blobs)),
))
}
}
}
pub fn signed_block(&self) -> &SignedBeaconBlock<T> {
pub fn signed_block(&self) -> &Arc<SignedBeaconBlock<T>> {
match self {
PublishBlockRequest::BlockContents(block_and_sidecars) => {
&block_and_sidecars.signed_block
@@ -1811,14 +1811,14 @@ pub fn into_full_block_and_blobs<T: EthSpec>(
let signed_block = blinded_block
.try_into_full_block(None)
.ok_or("Failed to build full block with payload".to_string())?;
Ok(PublishBlockRequest::new(signed_block, None))
Ok(PublishBlockRequest::new(Arc::new(signed_block), None))
}
// This variant implies a pre-deneb block
Some(FullPayloadContents::Payload(execution_payload)) => {
let signed_block = blinded_block
.try_into_full_block(Some(execution_payload))
.ok_or("Failed to build full block with payload".to_string())?;
Ok(PublishBlockRequest::new(signed_block, None))
Ok(PublishBlockRequest::new(Arc::new(signed_block), None))
}
// This variant implies a post-deneb block
Some(FullPayloadContents::PayloadAndBlobs(payload_and_blobs)) => {
@@ -1827,7 +1827,7 @@ pub fn into_full_block_and_blobs<T: EthSpec>(
.ok_or("Failed to build full block with payload".to_string())?;
Ok(PublishBlockRequest::new(
signed_block,
Arc::new(signed_block),
Some((
payload_and_blobs.blobs_bundle.proofs,
payload_and_blobs.blobs_bundle.blobs,
@@ -1837,10 +1837,10 @@ pub fn into_full_block_and_blobs<T: EthSpec>(
}
}
impl<T: EthSpec> TryFrom<SignedBeaconBlock<T>> for PublishBlockRequest<T> {
impl<T: EthSpec> TryFrom<Arc<SignedBeaconBlock<T>>> for PublishBlockRequest<T> {
type Error = &'static str;
fn try_from(block: SignedBeaconBlock<T>) -> Result<Self, Self::Error> {
match block {
fn try_from(block: Arc<SignedBeaconBlock<T>>) -> Result<Self, Self::Error> {
match *block {
SignedBeaconBlock::Base(_)
| SignedBeaconBlock::Altair(_)
| SignedBeaconBlock::Merge(_)
@@ -1861,7 +1861,7 @@ impl<T: EthSpec> From<SignedBlockContentsTuple<T>> for PublishBlockRequest<T> {
#[derive(Debug, Clone, Serialize, Deserialize, Encode)]
#[serde(bound = "T: EthSpec")]
pub struct SignedBlockContents<T: EthSpec> {
pub signed_block: SignedBeaconBlock<T>,
pub signed_block: Arc<SignedBeaconBlock<T>>,
pub kzg_proofs: KzgProofs<T>,
#[serde(with = "ssz_types::serde_utils::list_of_hex_fixed_vec")]
pub blobs: BlobsList<T>,