Add SSZ support to validator block production endpoints (#4534)
## Issue Addressed #4531 ## Proposed Changes add SSZ support to the following block production endpoints: GET /eth/v2/validator/blocks/{slot} GET /eth/v1/validator/blinded_blocks/{slot} ## Additional Info i updated a few existing tests to use ssz instead of writing completely new tests
This commit is contained in:
parent
a0478da990
commit
992b476eac
@ -3002,6 +3002,7 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.and(warp::path::end())
|
||||
.and(not_while_syncing_filter.clone())
|
||||
.and(warp::query::<api_types::ValidatorBlocksQuery>())
|
||||
.and(warp::header::optional::<api_types::Accept>("accept"))
|
||||
.and(task_spawner_filter.clone())
|
||||
.and(chain_filter.clone())
|
||||
.and(log_filter.clone())
|
||||
@ -3009,6 +3010,7 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
|endpoint_version: EndpointVersion,
|
||||
slot: Slot,
|
||||
query: api_types::ValidatorBlocksQuery,
|
||||
accept_header: Option<api_types::Accept>,
|
||||
task_spawner: TaskSpawner<T::EthSpec>,
|
||||
chain: Arc<BeaconChain<T>>,
|
||||
log: Logger| {
|
||||
@ -3053,9 +3055,24 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.fork_name(&chain.spec)
|
||||
.map_err(inconsistent_fork_rejection)?;
|
||||
|
||||
fork_versioned_response(endpoint_version, fork_name, block)
|
||||
.map(|response| warp::reply::json(&response).into_response())
|
||||
.map(|res| add_consensus_version_header(res, fork_name))
|
||||
match accept_header {
|
||||
Some(api_types::Accept::Ssz) => Response::builder()
|
||||
.status(200)
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.body(block.as_ssz_bytes().into())
|
||||
.map(|res: Response<Bytes>| {
|
||||
add_consensus_version_header(res, fork_name)
|
||||
})
|
||||
.map_err(|e| {
|
||||
warp_utils::reject::custom_server_error(format!(
|
||||
"failed to create response: {}",
|
||||
e
|
||||
))
|
||||
}),
|
||||
_ => fork_versioned_response(endpoint_version, fork_name, block)
|
||||
.map(|response| warp::reply::json(&response).into_response())
|
||||
.map(|res| add_consensus_version_header(res, fork_name)),
|
||||
}
|
||||
})
|
||||
},
|
||||
);
|
||||
@ -3072,11 +3089,13 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.and(warp::path::end())
|
||||
.and(not_while_syncing_filter.clone())
|
||||
.and(warp::query::<api_types::ValidatorBlocksQuery>())
|
||||
.and(warp::header::optional::<api_types::Accept>("accept"))
|
||||
.and(task_spawner_filter.clone())
|
||||
.and(chain_filter.clone())
|
||||
.then(
|
||||
|slot: Slot,
|
||||
query: api_types::ValidatorBlocksQuery,
|
||||
accept_header: Option<api_types::Accept>,
|
||||
task_spawner: TaskSpawner<T::EthSpec>,
|
||||
chain: Arc<BeaconChain<T>>| {
|
||||
task_spawner.spawn_async_with_rejection(Priority::P0, async move {
|
||||
@ -3114,10 +3133,25 @@ pub fn serve<T: BeaconChainTypes>(
|
||||
.fork_name(&chain.spec)
|
||||
.map_err(inconsistent_fork_rejection)?;
|
||||
|
||||
// Pose as a V2 endpoint so we return the fork `version`.
|
||||
fork_versioned_response(V2, fork_name, block)
|
||||
.map(|response| warp::reply::json(&response).into_response())
|
||||
.map(|res| add_consensus_version_header(res, fork_name))
|
||||
match accept_header {
|
||||
Some(api_types::Accept::Ssz) => Response::builder()
|
||||
.status(200)
|
||||
.header("Content-Type", "application/octet-stream")
|
||||
.body(block.as_ssz_bytes().into())
|
||||
.map(|res: Response<Bytes>| {
|
||||
add_consensus_version_header(res, fork_name)
|
||||
})
|
||||
.map_err(|e| {
|
||||
warp_utils::reject::custom_server_error(format!(
|
||||
"failed to create response: {}",
|
||||
e
|
||||
))
|
||||
}),
|
||||
// Pose as a V2 endpoint so we return the fork `version`.
|
||||
_ => fork_versioned_response(V2, fork_name, block)
|
||||
.map(|response| warp::reply::json(&response).into_response())
|
||||
.map(|res| add_consensus_version_header(res, fork_name)),
|
||||
}
|
||||
})
|
||||
},
|
||||
);
|
||||
|
@ -2513,6 +2513,70 @@ impl ApiTester {
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn test_block_production_ssz(self) -> Self {
|
||||
let fork = self.chain.canonical_head.cached_head().head_fork();
|
||||
let genesis_validators_root = self.chain.genesis_validators_root;
|
||||
|
||||
for _ in 0..E::slots_per_epoch() * 3 {
|
||||
let slot = self.chain.slot().unwrap();
|
||||
let epoch = self.chain.epoch().unwrap();
|
||||
|
||||
let proposer_pubkey_bytes = self
|
||||
.client
|
||||
.get_validator_duties_proposer(epoch)
|
||||
.await
|
||||
.unwrap()
|
||||
.data
|
||||
.into_iter()
|
||||
.find(|duty| duty.slot == slot)
|
||||
.map(|duty| duty.pubkey)
|
||||
.unwrap();
|
||||
let proposer_pubkey = (&proposer_pubkey_bytes).try_into().unwrap();
|
||||
|
||||
let sk = self
|
||||
.validator_keypairs()
|
||||
.iter()
|
||||
.find(|kp| kp.pk == proposer_pubkey)
|
||||
.map(|kp| kp.sk.clone())
|
||||
.unwrap();
|
||||
|
||||
let randao_reveal = {
|
||||
let domain = self.chain.spec.get_domain(
|
||||
epoch,
|
||||
Domain::Randao,
|
||||
&fork,
|
||||
genesis_validators_root,
|
||||
);
|
||||
let message = epoch.signing_root(domain);
|
||||
sk.sign(message).into()
|
||||
};
|
||||
|
||||
let block_bytes = self
|
||||
.client
|
||||
.get_validator_blocks_ssz::<E, FullPayload<E>>(slot, &randao_reveal, None)
|
||||
.await
|
||||
.unwrap()
|
||||
.expect("block bytes");
|
||||
|
||||
let block =
|
||||
BeaconBlock::<E, FullPayload<E>>::from_ssz_bytes(&block_bytes, &self.chain.spec)
|
||||
.expect("block bytes can be decoded");
|
||||
|
||||
let signed_block = block.sign(&sk, &fork, genesis_validators_root, &self.chain.spec);
|
||||
|
||||
self.client
|
||||
.post_beacon_blocks_ssz(&signed_block)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(self.chain.head_beacon_block().as_ref(), &signed_block);
|
||||
|
||||
self.chain.slot_clock.set_slot(slot.as_u64() + 1);
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn test_block_production_no_verify_randao(self) -> Self {
|
||||
for _ in 0..E::slots_per_epoch() {
|
||||
let slot = self.chain.slot().unwrap();
|
||||
@ -2694,12 +2758,15 @@ impl ApiTester {
|
||||
sk.sign(message).into()
|
||||
};
|
||||
|
||||
let block = self
|
||||
let block_bytes = self
|
||||
.client
|
||||
.get_validator_blinded_blocks::<E, Payload>(slot, &randao_reveal, None)
|
||||
.get_validator_blinded_blocks_ssz::<E, Payload>(slot, &randao_reveal, None)
|
||||
.await
|
||||
.unwrap()
|
||||
.data;
|
||||
.expect("block bytes");
|
||||
|
||||
let block = BeaconBlock::<E, Payload>::from_ssz_bytes(&block_bytes, &self.chain.spec)
|
||||
.expect("block bytes can be decoded");
|
||||
|
||||
let signed_block = block.sign(&sk, &fork, genesis_validators_root, &self.chain.spec);
|
||||
|
||||
@ -4891,6 +4958,20 @@ async fn block_production_verify_randao_invalid() {
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn block_production_ssz_full_payload() {
|
||||
ApiTester::new().await.test_block_production_ssz().await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn block_production_ssz_with_skip_slots() {
|
||||
ApiTester::new()
|
||||
.await
|
||||
.skip_slots(E::slots_per_epoch() * 2)
|
||||
.test_block_production_ssz()
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn blinded_block_production_full_payload_premerge() {
|
||||
ApiTester::new()
|
||||
|
@ -120,6 +120,7 @@ pub struct Timeouts {
|
||||
pub get_beacon_blocks_ssz: Duration,
|
||||
pub get_debug_beacon_states: Duration,
|
||||
pub get_deposit_snapshot: Duration,
|
||||
pub get_validator_block_ssz: Duration,
|
||||
}
|
||||
|
||||
impl Timeouts {
|
||||
@ -135,6 +136,7 @@ impl Timeouts {
|
||||
get_beacon_blocks_ssz: timeout,
|
||||
get_debug_beacon_states: timeout,
|
||||
get_deposit_snapshot: timeout,
|
||||
get_validator_block_ssz: timeout,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1598,14 +1600,14 @@ impl BeaconNodeHttpClient {
|
||||
.await
|
||||
}
|
||||
|
||||
/// `GET v2/validator/blocks/{slot}`
|
||||
pub async fn get_validator_blocks_modular<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
/// returns `GET v2/validator/blocks/{slot}` URL path
|
||||
pub async fn get_validator_blocks_path<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
randao_reveal: &SignatureBytes,
|
||||
graffiti: Option<&Graffiti>,
|
||||
skip_randao_verification: SkipRandaoVerification,
|
||||
) -> Result<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
|
||||
) -> Result<Url, Error> {
|
||||
let mut path = self.eth_path(V2)?;
|
||||
|
||||
path.path_segments_mut()
|
||||
@ -1627,9 +1629,66 @@ impl BeaconNodeHttpClient {
|
||||
.append_pair("skip_randao_verification", "");
|
||||
}
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// `GET v2/validator/blocks/{slot}`
|
||||
pub async fn get_validator_blocks_modular<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
randao_reveal: &SignatureBytes,
|
||||
graffiti: Option<&Graffiti>,
|
||||
skip_randao_verification: SkipRandaoVerification,
|
||||
) -> Result<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
|
||||
let path = self
|
||||
.get_validator_blocks_path::<T, Payload>(
|
||||
slot,
|
||||
randao_reveal,
|
||||
graffiti,
|
||||
skip_randao_verification,
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `GET v2/validator/blocks/{slot}` in ssz format
|
||||
pub async fn get_validator_blocks_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
randao_reveal: &SignatureBytes,
|
||||
graffiti: Option<&Graffiti>,
|
||||
) -> Result<Option<Vec<u8>>, Error> {
|
||||
self.get_validator_blocks_modular_ssz::<T, Payload>(
|
||||
slot,
|
||||
randao_reveal,
|
||||
graffiti,
|
||||
SkipRandaoVerification::No,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
/// `GET v2/validator/blocks/{slot}` in ssz format
|
||||
pub async fn get_validator_blocks_modular_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
randao_reveal: &SignatureBytes,
|
||||
graffiti: Option<&Graffiti>,
|
||||
skip_randao_verification: SkipRandaoVerification,
|
||||
) -> Result<Option<Vec<u8>>, Error> {
|
||||
let path = self
|
||||
.get_validator_blocks_path::<T, Payload>(
|
||||
slot,
|
||||
randao_reveal,
|
||||
graffiti,
|
||||
skip_randao_verification,
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.get_bytes_opt_accept_header(path, Accept::Ssz, self.timeouts.get_validator_block_ssz)
|
||||
.await
|
||||
}
|
||||
|
||||
/// `GET v2/validator/blinded_blocks/{slot}`
|
||||
pub async fn get_validator_blinded_blocks<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
&self,
|
||||
@ -1646,17 +1705,14 @@ impl BeaconNodeHttpClient {
|
||||
.await
|
||||
}
|
||||
|
||||
/// `GET v1/validator/blinded_blocks/{slot}`
|
||||
pub async fn get_validator_blinded_blocks_modular<
|
||||
T: EthSpec,
|
||||
Payload: AbstractExecPayload<T>,
|
||||
>(
|
||||
/// returns `GET v1/validator/blinded_blocks/{slot}` URL path
|
||||
pub async fn get_validator_blinded_blocks_path<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
randao_reveal: &SignatureBytes,
|
||||
graffiti: Option<&Graffiti>,
|
||||
skip_randao_verification: SkipRandaoVerification,
|
||||
) -> Result<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
|
||||
) -> Result<Url, Error> {
|
||||
let mut path = self.eth_path(V1)?;
|
||||
|
||||
path.path_segments_mut()
|
||||
@ -1678,9 +1734,71 @@ impl BeaconNodeHttpClient {
|
||||
.append_key_only("skip_randao_verification");
|
||||
}
|
||||
|
||||
Ok(path)
|
||||
}
|
||||
|
||||
/// `GET v1/validator/blinded_blocks/{slot}`
|
||||
pub async fn get_validator_blinded_blocks_modular<
|
||||
T: EthSpec,
|
||||
Payload: AbstractExecPayload<T>,
|
||||
>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
randao_reveal: &SignatureBytes,
|
||||
graffiti: Option<&Graffiti>,
|
||||
skip_randao_verification: SkipRandaoVerification,
|
||||
) -> Result<ForkVersionedResponse<BeaconBlock<T, Payload>>, Error> {
|
||||
let path = self
|
||||
.get_validator_blinded_blocks_path::<T, Payload>(
|
||||
slot,
|
||||
randao_reveal,
|
||||
graffiti,
|
||||
skip_randao_verification,
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.get(path).await
|
||||
}
|
||||
|
||||
/// `GET v2/validator/blinded_blocks/{slot}` in ssz format
|
||||
pub async fn get_validator_blinded_blocks_ssz<T: EthSpec, Payload: AbstractExecPayload<T>>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
randao_reveal: &SignatureBytes,
|
||||
graffiti: Option<&Graffiti>,
|
||||
) -> Result<Option<Vec<u8>>, Error> {
|
||||
self.get_validator_blinded_blocks_modular_ssz::<T, Payload>(
|
||||
slot,
|
||||
randao_reveal,
|
||||
graffiti,
|
||||
SkipRandaoVerification::No,
|
||||
)
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn get_validator_blinded_blocks_modular_ssz<
|
||||
T: EthSpec,
|
||||
Payload: AbstractExecPayload<T>,
|
||||
>(
|
||||
&self,
|
||||
slot: Slot,
|
||||
randao_reveal: &SignatureBytes,
|
||||
graffiti: Option<&Graffiti>,
|
||||
skip_randao_verification: SkipRandaoVerification,
|
||||
) -> Result<Option<Vec<u8>>, Error> {
|
||||
let path = self
|
||||
.get_validator_blinded_blocks_path::<T, Payload>(
|
||||
slot,
|
||||
randao_reveal,
|
||||
graffiti,
|
||||
skip_randao_verification,
|
||||
)
|
||||
.await?;
|
||||
|
||||
self.get_bytes_opt_accept_header(path, Accept::Ssz, self.timeouts.get_validator_block_ssz)
|
||||
.await
|
||||
}
|
||||
|
||||
/// `GET validator/attestation_data?slot,committee_index`
|
||||
pub async fn get_validator_attestation_data(
|
||||
&self,
|
||||
|
@ -82,6 +82,7 @@ const HTTP_SYNC_DUTIES_TIMEOUT_QUOTIENT: u32 = 4;
|
||||
const HTTP_GET_BEACON_BLOCK_SSZ_TIMEOUT_QUOTIENT: u32 = 4;
|
||||
const HTTP_GET_DEBUG_BEACON_STATE_QUOTIENT: u32 = 4;
|
||||
const HTTP_GET_DEPOSIT_SNAPSHOT_QUOTIENT: u32 = 4;
|
||||
const HTTP_GET_VALIDATOR_BLOCK_SSZ_TIMEOUT_QUOTIENT: u32 = 4;
|
||||
|
||||
const DOPPELGANGER_SERVICE_NAME: &str = "doppelganger";
|
||||
|
||||
@ -309,6 +310,8 @@ impl<T: EthSpec> ProductionValidatorClient<T> {
|
||||
/ HTTP_GET_BEACON_BLOCK_SSZ_TIMEOUT_QUOTIENT,
|
||||
get_debug_beacon_states: slot_duration / HTTP_GET_DEBUG_BEACON_STATE_QUOTIENT,
|
||||
get_deposit_snapshot: slot_duration / HTTP_GET_DEPOSIT_SNAPSHOT_QUOTIENT,
|
||||
get_validator_block_ssz: slot_duration
|
||||
/ HTTP_GET_VALIDATOR_BLOCK_SSZ_TIMEOUT_QUOTIENT,
|
||||
}
|
||||
} else {
|
||||
Timeouts::set_all(slot_duration)
|
||||
|
Loading…
Reference in New Issue
Block a user