Merge branch 'unstable' into vc-fallback

This commit is contained in:
Mac L
2024-01-31 19:24:52 +11:00
119 changed files with 2579 additions and 1054 deletions

View File

@@ -378,26 +378,27 @@ impl BeaconNodeHttpClient {
if let Some(timeout) = timeout {
builder = builder.timeout(timeout);
}
let response = builder.json(body).send().await?;
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>(
/// Does not include Content-Type application/json in the request header.
async fn post_generic_json_without_content_type_header<T: Serialize, U: IntoUrl>(
&self,
url: U,
body: T,
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?;
let serialized_body = serde_json::to_vec(body).map_err(Error::InvalidJson)?;
let response = builder.body(serialized_body).send().await?;
ok_or_error(response).await
}
@@ -871,10 +872,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?;
@@ -915,8 +917,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(())
}
@@ -1082,8 +1089,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);
};
@@ -1258,7 +1275,8 @@ impl BeaconNodeHttpClient {
.push("pool")
.push("attester_slashings");
self.post(path, slashing).await?;
self.post_generic_json_without_content_type_header(path, slashing, None)
.await?;
Ok(())
}

View File

@@ -8,15 +8,12 @@ mod standard_block_rewards;
mod sync_committee_rewards;
use crate::{
ok_or_error,
types::{
BeaconState, ChainSpec, DepositTreeSnapshot, Epoch, EthSpec, FinalizedExecutionBlock,
GenericResponse, ValidatorId,
DepositTreeSnapshot, Epoch, EthSpec, FinalizedExecutionBlock, GenericResponse, ValidatorId,
},
BeaconNodeHttpClient, DepositData, Error, Eth1Data, Hash256, Slot, StateId, StatusCode,
BeaconNodeHttpClient, DepositData, Error, Eth1Data, Hash256, Slot,
};
use proto_array::core::ProtoArray;
use reqwest::IntoUrl;
use serde::{Deserialize, Serialize};
use ssz::four_byte_option_impl;
use ssz_derive::{Decode, Encode};
@@ -371,27 +368,6 @@ pub struct DatabaseInfo {
}
impl BeaconNodeHttpClient {
/// Perform a HTTP GET request, returning `None` on a 404 error.
async fn get_bytes_opt<U: IntoUrl>(&self, url: U) -> Result<Option<Vec<u8>>, Error> {
let response = self.client.get(url).send().await.map_err(Error::from)?;
match ok_or_error(response).await {
Ok(resp) => Ok(Some(
resp.bytes()
.await
.map_err(Error::from)?
.into_iter()
.collect::<Vec<_>>(),
)),
Err(err) => {
if err.status() == Some(StatusCode::NOT_FOUND) {
Ok(None)
} else {
Err(err)
}
}
}
}
/// `GET lighthouse/health`
pub async fn get_lighthouse_health(&self) -> Result<GenericResponse<Health>, Error> {
let mut path = self.server.full.clone();
@@ -516,28 +492,6 @@ impl BeaconNodeHttpClient {
self.get(path).await
}
/// `GET lighthouse/beacon/states/{state_id}/ssz`
pub async fn get_lighthouse_beacon_states_ssz<E: EthSpec>(
&self,
state_id: &StateId,
spec: &ChainSpec,
) -> Result<Option<BeaconState<E>>, Error> {
let mut path = self.server.full.clone();
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("lighthouse")
.push("beacon")
.push("states")
.push(&state_id.to_string())
.push("ssz");
self.get_bytes_opt(path)
.await?
.map(|bytes| BeaconState::from_ssz_bytes(&bytes, spec).map_err(Error::InvalidSsz))
.transpose()
}
/// `GET lighthouse/staking`
pub async fn get_lighthouse_staking(&self) -> Result<bool, Error> {
let mut path = self.server.full.clone();

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::*;
@@ -1479,15 +1480,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(_)));
}
@@ -1503,11 +1504,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(_)));
}
}
@@ -1642,7 +1646,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)
}
}
@@ -1673,7 +1677,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,
@@ -1728,12 +1735,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 {
@@ -1746,23 +1753,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);
@@ -1771,16 +1767,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
@@ -1810,14 +1810,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)) => {
@@ -1826,7 +1826,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,
@@ -1836,10 +1836,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(_)
@@ -1860,7 +1860,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>,