mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-06 10:11:44 +00:00
Add execution_optimistic flag to HTTP responses (#3070)
## Issue Addressed #3031 ## Proposed Changes Updates the following API endpoints to conform with https://github.com/ethereum/beacon-APIs/pull/190 and https://github.com/ethereum/beacon-APIs/pull/196 - [x] `beacon/states/{state_id}/root` - [x] `beacon/states/{state_id}/fork` - [x] `beacon/states/{state_id}/finality_checkpoints` - [x] `beacon/states/{state_id}/validators` - [x] `beacon/states/{state_id}/validators/{validator_id}` - [x] `beacon/states/{state_id}/validator_balances` - [x] `beacon/states/{state_id}/committees` - [x] `beacon/states/{state_id}/sync_committees` - [x] `beacon/headers` - [x] `beacon/headers/{block_id}` - [x] `beacon/blocks/{block_id}` - [x] `beacon/blocks/{block_id}/root` - [x] `beacon/blocks/{block_id}/attestations` - [x] `debug/beacon/states/{state_id}` - [x] `debug/beacon/heads` - [x] `validator/duties/attester/{epoch}` - [x] `validator/duties/proposer/{epoch}` - [x] `validator/duties/sync/{epoch}` Updates the following Server-Sent Events: - [x] `events?topics=head` - [x] `events?topics=block` - [x] `events?topics=finalized_checkpoint` - [x] `events?topics=chain_reorg` ## Backwards Incompatible There is a very minor breaking change with the way the API now handles requests to `beacon/blocks/{block_id}/root` and `beacon/states/{state_id}/root` when `block_id` or `state_id` is the `Root` variant of `BlockId` and `StateId` respectively. Previously a request to a non-existent root would simply echo the root back to the requester: ``` curl "http://localhost:5052/eth/v1/beacon/states/0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/root" {"data":{"root":"0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"}} ``` Now it will return a `404`: ``` curl "http://localhost:5052/eth/v1/beacon/blocks/0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/root" {"code":404,"message":"NOT_FOUND: beacon block with root 0xaaaa…aaaa","stacktraces":[]} ``` In addition to this is the block root `0x0000000000000000000000000000000000000000000000000000000000000000` previously would return the genesis block. It will now return a `404`: ``` curl "http://localhost:5052/eth/v1/beacon/blocks/0x0000000000000000000000000000000000000000000000000000000000000000" {"code":404,"message":"NOT_FOUND: beacon block with root 0x0000…0000","stacktraces":[]} ``` ## Additional Info - `execution_optimistic` is always set, and will return `false` pre-Bellatrix. I am also open to the idea of doing something like `#[serde(skip_serializing_if = "Option::is_none")]`. - The value of `execution_optimistic` is set to `false` where possible. Any computation that is reliant on the `head` will simply use the `ExecutionStatus` of the head (unless the head block is pre-Bellatrix). Co-authored-by: Paul Hauner <paul@paulhauner.com>
This commit is contained in:
@@ -8,13 +8,15 @@ use environment::null_logger;
|
||||
use eth2::{
|
||||
mixin::{RequestAccept, ResponseForkName, ResponseOptional},
|
||||
reqwest::RequestBuilder,
|
||||
types::*,
|
||||
types::{BlockId as CoreBlockId, StateId as CoreStateId, *},
|
||||
BeaconNodeHttpClient, Error, StatusCode, Timeouts,
|
||||
};
|
||||
use futures::stream::{Stream, StreamExt};
|
||||
use futures::FutureExt;
|
||||
use http_api::{BlockId, StateId};
|
||||
use lighthouse_network::{Enr, EnrExt, PeerId};
|
||||
use network::NetworkMessage;
|
||||
use proto_array::ExecutionStatus;
|
||||
use sensitive_url::SensitiveUrl;
|
||||
use slot_clock::SlotClock;
|
||||
use state_processing::per_slot_processing;
|
||||
@@ -25,8 +27,8 @@ use tokio::time::Duration;
|
||||
use tree_hash::TreeHash;
|
||||
use types::application_domain::ApplicationDomain;
|
||||
use types::{
|
||||
AggregateSignature, BeaconState, BitList, Domain, EthSpec, Hash256, Keypair, MainnetEthSpec,
|
||||
RelativeEpoch, SelectionProof, SignedRoot, Slot,
|
||||
AggregateSignature, BitList, Domain, EthSpec, ExecutionBlockHash, Hash256, Keypair,
|
||||
MainnetEthSpec, RelativeEpoch, SelectionProof, SignedRoot, Slot,
|
||||
};
|
||||
|
||||
type E = MainnetEthSpec;
|
||||
@@ -74,6 +76,19 @@ impl ApiTester {
|
||||
Self::new_from_spec(spec).await
|
||||
}
|
||||
|
||||
pub async fn new_with_hard_forks(altair: bool, bellatrix: bool) -> Self {
|
||||
let mut spec = E::default_spec();
|
||||
spec.shard_committee_period = 2;
|
||||
// Set whether the chain has undergone each hard fork.
|
||||
if altair {
|
||||
spec.altair_fork_epoch = Some(Epoch::new(0));
|
||||
}
|
||||
if bellatrix {
|
||||
spec.bellatrix_fork_epoch = Some(Epoch::new(0));
|
||||
}
|
||||
Self::new_from_spec(spec).await
|
||||
}
|
||||
|
||||
pub async fn new_from_spec(spec: ChainSpec) -> Self {
|
||||
let harness = Arc::new(
|
||||
BeaconChainHarness::builder(MainnetEthSpec)
|
||||
@@ -325,99 +340,43 @@ impl ApiTester {
|
||||
|
||||
fn interesting_state_ids(&self) -> Vec<StateId> {
|
||||
let mut ids = vec![
|
||||
StateId::Head,
|
||||
StateId::Genesis,
|
||||
StateId::Finalized,
|
||||
StateId::Justified,
|
||||
StateId::Slot(Slot::new(0)),
|
||||
StateId::Slot(Slot::new(32)),
|
||||
StateId::Slot(Slot::from(SKIPPED_SLOTS[0])),
|
||||
StateId::Slot(Slot::from(SKIPPED_SLOTS[1])),
|
||||
StateId::Slot(Slot::from(SKIPPED_SLOTS[2])),
|
||||
StateId::Slot(Slot::from(SKIPPED_SLOTS[3])),
|
||||
StateId::Root(Hash256::zero()),
|
||||
StateId(CoreStateId::Head),
|
||||
StateId(CoreStateId::Genesis),
|
||||
StateId(CoreStateId::Finalized),
|
||||
StateId(CoreStateId::Justified),
|
||||
StateId(CoreStateId::Slot(Slot::new(0))),
|
||||
StateId(CoreStateId::Slot(Slot::new(32))),
|
||||
StateId(CoreStateId::Slot(Slot::from(SKIPPED_SLOTS[0]))),
|
||||
StateId(CoreStateId::Slot(Slot::from(SKIPPED_SLOTS[1]))),
|
||||
StateId(CoreStateId::Slot(Slot::from(SKIPPED_SLOTS[2]))),
|
||||
StateId(CoreStateId::Slot(Slot::from(SKIPPED_SLOTS[3]))),
|
||||
StateId(CoreStateId::Root(Hash256::zero())),
|
||||
];
|
||||
ids.push(StateId::Root(
|
||||
ids.push(StateId(CoreStateId::Root(
|
||||
self.chain.canonical_head.cached_head().head_state_root(),
|
||||
));
|
||||
)));
|
||||
ids
|
||||
}
|
||||
|
||||
fn interesting_block_ids(&self) -> Vec<BlockId> {
|
||||
let mut ids = vec![
|
||||
BlockId::Head,
|
||||
BlockId::Genesis,
|
||||
BlockId::Finalized,
|
||||
BlockId::Justified,
|
||||
BlockId::Slot(Slot::new(0)),
|
||||
BlockId::Slot(Slot::new(32)),
|
||||
BlockId::Slot(Slot::from(SKIPPED_SLOTS[0])),
|
||||
BlockId::Slot(Slot::from(SKIPPED_SLOTS[1])),
|
||||
BlockId::Slot(Slot::from(SKIPPED_SLOTS[2])),
|
||||
BlockId::Slot(Slot::from(SKIPPED_SLOTS[3])),
|
||||
BlockId::Root(Hash256::zero()),
|
||||
BlockId(CoreBlockId::Head),
|
||||
BlockId(CoreBlockId::Genesis),
|
||||
BlockId(CoreBlockId::Finalized),
|
||||
BlockId(CoreBlockId::Justified),
|
||||
BlockId(CoreBlockId::Slot(Slot::new(0))),
|
||||
BlockId(CoreBlockId::Slot(Slot::new(32))),
|
||||
BlockId(CoreBlockId::Slot(Slot::from(SKIPPED_SLOTS[0]))),
|
||||
BlockId(CoreBlockId::Slot(Slot::from(SKIPPED_SLOTS[1]))),
|
||||
BlockId(CoreBlockId::Slot(Slot::from(SKIPPED_SLOTS[2]))),
|
||||
BlockId(CoreBlockId::Slot(Slot::from(SKIPPED_SLOTS[3]))),
|
||||
BlockId(CoreBlockId::Root(Hash256::zero())),
|
||||
];
|
||||
ids.push(BlockId::Root(
|
||||
ids.push(BlockId(CoreBlockId::Root(
|
||||
self.chain.canonical_head.cached_head().head_block_root(),
|
||||
));
|
||||
)));
|
||||
ids
|
||||
}
|
||||
|
||||
fn get_state(&self, state_id: StateId) -> Option<BeaconState<E>> {
|
||||
match state_id {
|
||||
StateId::Head => Some(
|
||||
self.chain
|
||||
.head_snapshot()
|
||||
.beacon_state
|
||||
.clone_with_only_committee_caches(),
|
||||
),
|
||||
StateId::Genesis => self
|
||||
.chain
|
||||
.get_state(&self.chain.genesis_state_root, None)
|
||||
.unwrap(),
|
||||
StateId::Finalized => {
|
||||
let finalized_slot = self
|
||||
.chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
.finalized_checkpoint()
|
||||
.epoch
|
||||
.start_slot(E::slots_per_epoch());
|
||||
|
||||
let root = self
|
||||
.chain
|
||||
.state_root_at_slot(finalized_slot)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
self.chain.get_state(&root, Some(finalized_slot)).unwrap()
|
||||
}
|
||||
StateId::Justified => {
|
||||
let justified_slot = self
|
||||
.chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
.justified_checkpoint()
|
||||
.epoch
|
||||
.start_slot(E::slots_per_epoch());
|
||||
|
||||
let root = self
|
||||
.chain
|
||||
.state_root_at_slot(justified_slot)
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
self.chain.get_state(&root, Some(justified_slot)).unwrap()
|
||||
}
|
||||
StateId::Slot(slot) => {
|
||||
let root = self.chain.state_root_at_slot(slot).unwrap().unwrap();
|
||||
|
||||
self.chain.get_state(&root, Some(slot)).unwrap()
|
||||
}
|
||||
StateId::Root(root) => self.chain.get_state(&root, None).unwrap(),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn test_beacon_genesis(self) -> Self {
|
||||
let result = self.client.get_beacon_genesis().await.unwrap().data;
|
||||
|
||||
@@ -437,39 +396,15 @@ impl ApiTester {
|
||||
for state_id in self.interesting_state_ids() {
|
||||
let result = self
|
||||
.client
|
||||
.get_beacon_states_root(state_id)
|
||||
.get_beacon_states_root(state_id.0)
|
||||
.await
|
||||
.unwrap()
|
||||
.map(|res| res.data.root);
|
||||
|
||||
let expected = match state_id {
|
||||
StateId::Head => Some(self.chain.canonical_head.cached_head().head_state_root()),
|
||||
StateId::Genesis => Some(self.chain.genesis_state_root),
|
||||
StateId::Finalized => {
|
||||
let finalized_slot = self
|
||||
.chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
.finalized_checkpoint()
|
||||
.epoch
|
||||
.start_slot(E::slots_per_epoch());
|
||||
|
||||
self.chain.state_root_at_slot(finalized_slot).unwrap()
|
||||
}
|
||||
StateId::Justified => {
|
||||
let justified_slot = self
|
||||
.chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
.justified_checkpoint()
|
||||
.epoch
|
||||
.start_slot(E::slots_per_epoch());
|
||||
|
||||
self.chain.state_root_at_slot(justified_slot).unwrap()
|
||||
}
|
||||
StateId::Slot(slot) => self.chain.state_root_at_slot(slot).unwrap(),
|
||||
StateId::Root(root) => Some(root),
|
||||
};
|
||||
let expected = state_id
|
||||
.root(&self.chain)
|
||||
.ok()
|
||||
.map(|(root, _execution_optimistic)| root);
|
||||
|
||||
assert_eq!(result, expected, "{:?}", state_id);
|
||||
}
|
||||
@@ -481,12 +416,12 @@ impl ApiTester {
|
||||
for state_id in self.interesting_state_ids() {
|
||||
let result = self
|
||||
.client
|
||||
.get_beacon_states_fork(state_id)
|
||||
.get_beacon_states_fork(state_id.0)
|
||||
.await
|
||||
.unwrap()
|
||||
.map(|res| res.data);
|
||||
|
||||
let expected = self.get_state(state_id).map(|state| state.fork());
|
||||
let expected = state_id.fork(&self.chain).ok();
|
||||
|
||||
assert_eq!(result, expected, "{:?}", state_id);
|
||||
}
|
||||
@@ -498,18 +433,20 @@ impl ApiTester {
|
||||
for state_id in self.interesting_state_ids() {
|
||||
let result = self
|
||||
.client
|
||||
.get_beacon_states_finality_checkpoints(state_id)
|
||||
.get_beacon_states_finality_checkpoints(state_id.0)
|
||||
.await
|
||||
.unwrap()
|
||||
.map(|res| res.data);
|
||||
|
||||
let expected = self
|
||||
.get_state(state_id)
|
||||
.map(|state| FinalityCheckpointsData {
|
||||
previous_justified: state.previous_justified_checkpoint(),
|
||||
current_justified: state.current_justified_checkpoint(),
|
||||
finalized: state.finalized_checkpoint(),
|
||||
});
|
||||
let expected =
|
||||
state_id
|
||||
.state(&self.chain)
|
||||
.ok()
|
||||
.map(|(state, _execution_optimistic)| FinalityCheckpointsData {
|
||||
previous_justified: state.previous_justified_checkpoint(),
|
||||
current_justified: state.current_justified_checkpoint(),
|
||||
finalized: state.finalized_checkpoint(),
|
||||
});
|
||||
|
||||
assert_eq!(result, expected, "{:?}", state_id);
|
||||
}
|
||||
@@ -520,9 +457,9 @@ impl ApiTester {
|
||||
pub async fn test_beacon_states_validator_balances(self) -> Self {
|
||||
for state_id in self.interesting_state_ids() {
|
||||
for validator_indices in self.interesting_validator_indices() {
|
||||
let state_opt = self.get_state(state_id);
|
||||
let state_opt = state_id.state(&self.chain).ok();
|
||||
let validators: Vec<Validator> = match state_opt.as_ref() {
|
||||
Some(state) => state.validators().clone().into(),
|
||||
Some((state, _execution_optimistic)) => state.validators().clone().into(),
|
||||
None => vec![],
|
||||
};
|
||||
let validator_index_ids = validator_indices
|
||||
@@ -545,7 +482,7 @@ impl ApiTester {
|
||||
let result_index_ids = self
|
||||
.client
|
||||
.get_beacon_states_validator_balances(
|
||||
state_id,
|
||||
state_id.0,
|
||||
Some(validator_index_ids.as_slice()),
|
||||
)
|
||||
.await
|
||||
@@ -554,14 +491,14 @@ impl ApiTester {
|
||||
let result_pubkey_ids = self
|
||||
.client
|
||||
.get_beacon_states_validator_balances(
|
||||
state_id,
|
||||
state_id.0,
|
||||
Some(validator_pubkey_ids.as_slice()),
|
||||
)
|
||||
.await
|
||||
.unwrap()
|
||||
.map(|res| res.data);
|
||||
|
||||
let expected = state_opt.map(|state| {
|
||||
let expected = state_opt.map(|(state, _execution_optimistic)| {
|
||||
let mut validators = Vec::with_capacity(validator_indices.len());
|
||||
|
||||
for i in validator_indices {
|
||||
@@ -588,7 +525,10 @@ impl ApiTester {
|
||||
for state_id in self.interesting_state_ids() {
|
||||
for statuses in self.interesting_validator_statuses() {
|
||||
for validator_indices in self.interesting_validator_indices() {
|
||||
let state_opt = self.get_state(state_id);
|
||||
let state_opt = state_id
|
||||
.state(&self.chain)
|
||||
.ok()
|
||||
.map(|(state, _execution_optimistic)| state);
|
||||
let validators: Vec<Validator> = match state_opt.as_ref() {
|
||||
Some(state) => state.validators().clone().into(),
|
||||
None => vec![],
|
||||
@@ -613,7 +553,7 @@ impl ApiTester {
|
||||
let result_index_ids = self
|
||||
.client
|
||||
.get_beacon_states_validators(
|
||||
state_id,
|
||||
state_id.0,
|
||||
Some(validator_index_ids.as_slice()),
|
||||
None,
|
||||
)
|
||||
@@ -624,7 +564,7 @@ impl ApiTester {
|
||||
let result_pubkey_ids = self
|
||||
.client
|
||||
.get_beacon_states_validators(
|
||||
state_id,
|
||||
state_id.0,
|
||||
Some(validator_pubkey_ids.as_slice()),
|
||||
None,
|
||||
)
|
||||
@@ -675,7 +615,10 @@ impl ApiTester {
|
||||
|
||||
pub async fn test_beacon_states_validator_id(self) -> Self {
|
||||
for state_id in self.interesting_state_ids() {
|
||||
let state_opt = self.get_state(state_id);
|
||||
let state_opt = state_id
|
||||
.state(&self.chain)
|
||||
.ok()
|
||||
.map(|(state, _execution_optimistic)| state);
|
||||
let validators = match state_opt.as_ref() {
|
||||
Some(state) => state.validators().clone().into(),
|
||||
None => vec![],
|
||||
@@ -690,7 +633,7 @@ impl ApiTester {
|
||||
for validator_id in validator_ids {
|
||||
let result = self
|
||||
.client
|
||||
.get_beacon_states_validator_id(state_id, validator_id)
|
||||
.get_beacon_states_validator_id(state_id.0, validator_id)
|
||||
.await
|
||||
.unwrap()
|
||||
.map(|res| res.data);
|
||||
@@ -727,12 +670,15 @@ impl ApiTester {
|
||||
|
||||
pub async fn test_beacon_states_committees(self) -> Self {
|
||||
for state_id in self.interesting_state_ids() {
|
||||
let mut state_opt = self.get_state(state_id);
|
||||
let mut state_opt = state_id
|
||||
.state(&self.chain)
|
||||
.ok()
|
||||
.map(|(state, _execution_optimistic)| state);
|
||||
|
||||
let epoch_opt = state_opt.as_ref().map(|state| state.current_epoch());
|
||||
let results = self
|
||||
.client
|
||||
.get_beacon_states_committees(state_id, None, None, epoch_opt)
|
||||
.get_beacon_states_committees(state_id.0, None, None, epoch_opt)
|
||||
.await
|
||||
.unwrap()
|
||||
.map(|res| res.data);
|
||||
@@ -769,37 +715,6 @@ impl ApiTester {
|
||||
self
|
||||
}
|
||||
|
||||
fn get_block_root(&self, block_id: BlockId) -> Option<Hash256> {
|
||||
match block_id {
|
||||
BlockId::Head => Some(self.chain.canonical_head.cached_head().head_block_root()),
|
||||
BlockId::Genesis => Some(self.chain.genesis_block_root),
|
||||
BlockId::Finalized => Some(
|
||||
self.chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
.finalized_checkpoint()
|
||||
.root,
|
||||
),
|
||||
BlockId::Justified => Some(
|
||||
self.chain
|
||||
.canonical_head
|
||||
.cached_head()
|
||||
.justified_checkpoint()
|
||||
.root,
|
||||
),
|
||||
BlockId::Slot(slot) => self
|
||||
.chain
|
||||
.block_root_at_slot(slot, WhenSlotSkipped::None)
|
||||
.unwrap(),
|
||||
BlockId::Root(root) => Some(root),
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_block(&self, block_id: BlockId) -> Option<SignedBeaconBlock<E>> {
|
||||
let root = self.get_block_root(block_id)?;
|
||||
self.chain.get_block(&root).await.unwrap()
|
||||
}
|
||||
|
||||
pub async fn test_beacon_headers_all_slots(self) -> Self {
|
||||
for slot in 0..CHAIN_LENGTH {
|
||||
let slot = Slot::from(slot);
|
||||
@@ -877,14 +792,17 @@ impl ApiTester {
|
||||
for block_id in self.interesting_block_ids() {
|
||||
let result = self
|
||||
.client
|
||||
.get_beacon_headers_block_id(block_id)
|
||||
.get_beacon_headers_block_id(block_id.0)
|
||||
.await
|
||||
.unwrap()
|
||||
.map(|res| res.data);
|
||||
|
||||
let block_root_opt = self.get_block_root(block_id);
|
||||
let block_root_opt = block_id
|
||||
.root(&self.chain)
|
||||
.ok()
|
||||
.map(|(root, _execution_optimistic)| root);
|
||||
|
||||
if let BlockId::Slot(slot) = block_id {
|
||||
if let CoreBlockId::Slot(slot) = block_id.0 {
|
||||
if block_root_opt.is_none() {
|
||||
assert!(SKIPPED_SLOTS.contains(&slot.as_u64()));
|
||||
} else {
|
||||
@@ -892,11 +810,11 @@ impl ApiTester {
|
||||
}
|
||||
}
|
||||
|
||||
let block_opt = if let Some(root) = block_root_opt {
|
||||
self.chain.get_block(&root).await.unwrap()
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let block_opt = block_id
|
||||
.full_block(&self.chain)
|
||||
.await
|
||||
.ok()
|
||||
.map(|(block, _execution_optimistic)| block);
|
||||
|
||||
if block_opt.is_none() && result.is_none() {
|
||||
continue;
|
||||
@@ -934,13 +852,16 @@ impl ApiTester {
|
||||
for block_id in self.interesting_block_ids() {
|
||||
let result = self
|
||||
.client
|
||||
.get_beacon_blocks_root(block_id)
|
||||
.get_beacon_blocks_root(block_id.0)
|
||||
.await
|
||||
.unwrap()
|
||||
.map(|res| res.data.root);
|
||||
|
||||
let expected = self.get_block_root(block_id);
|
||||
if let BlockId::Slot(slot) = block_id {
|
||||
let expected = block_id
|
||||
.root(&self.chain)
|
||||
.ok()
|
||||
.map(|(root, _execution_optimistic)| root);
|
||||
if let CoreBlockId::Slot(slot) = block_id.0 {
|
||||
if expected.is_none() {
|
||||
assert!(SKIPPED_SLOTS.contains(&slot.as_u64()));
|
||||
} else {
|
||||
@@ -982,9 +903,13 @@ impl ApiTester {
|
||||
|
||||
pub async fn test_beacon_blocks(self) -> Self {
|
||||
for block_id in self.interesting_block_ids() {
|
||||
let expected = self.get_block(block_id).await;
|
||||
let expected = block_id
|
||||
.full_block(&self.chain)
|
||||
.await
|
||||
.ok()
|
||||
.map(|(block, _execution_optimistic)| block);
|
||||
|
||||
if let BlockId::Slot(slot) = block_id {
|
||||
if let CoreBlockId::Slot(slot) = block_id.0 {
|
||||
if expected.is_none() {
|
||||
assert!(SKIPPED_SLOTS.contains(&slot.as_u64()));
|
||||
} else {
|
||||
@@ -993,10 +918,10 @@ impl ApiTester {
|
||||
}
|
||||
|
||||
// Check the JSON endpoint.
|
||||
let json_result = self.client.get_beacon_blocks(block_id).await.unwrap();
|
||||
let json_result = self.client.get_beacon_blocks(block_id.0).await.unwrap();
|
||||
|
||||
if let (Some(json), Some(expected)) = (&json_result, &expected) {
|
||||
assert_eq!(json.data, *expected, "{:?}", block_id);
|
||||
assert_eq!(&json.data, expected.as_ref(), "{:?}", block_id);
|
||||
assert_eq!(
|
||||
json.version,
|
||||
Some(expected.fork_name(&self.chain.spec).unwrap())
|
||||
@@ -1009,23 +934,28 @@ impl ApiTester {
|
||||
// Check the SSZ endpoint.
|
||||
let ssz_result = self
|
||||
.client
|
||||
.get_beacon_blocks_ssz(block_id, &self.chain.spec)
|
||||
.get_beacon_blocks_ssz(block_id.0, &self.chain.spec)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(ssz_result, expected, "{:?}", block_id);
|
||||
assert_eq!(
|
||||
ssz_result.as_ref(),
|
||||
expected.as_ref().map(|b| b.as_ref()),
|
||||
"{:?}",
|
||||
block_id
|
||||
);
|
||||
|
||||
// Check that the legacy v1 API still works but doesn't return a version field.
|
||||
let v1_result = self.client.get_beacon_blocks_v1(block_id).await.unwrap();
|
||||
let v1_result = self.client.get_beacon_blocks_v1(block_id.0).await.unwrap();
|
||||
if let (Some(v1_result), Some(expected)) = (&v1_result, &expected) {
|
||||
assert_eq!(v1_result.version, None);
|
||||
assert_eq!(v1_result.data, *expected);
|
||||
assert_eq!(&v1_result.data, expected.as_ref());
|
||||
} else {
|
||||
assert_eq!(v1_result, None);
|
||||
assert_eq!(expected, None);
|
||||
}
|
||||
|
||||
// Check that version headers are provided.
|
||||
let url = self.client.get_beacon_blocks_path(block_id).unwrap();
|
||||
let url = self.client.get_beacon_blocks_path(block_id.0).unwrap();
|
||||
|
||||
let builders: Vec<fn(RequestBuilder) -> RequestBuilder> = vec![
|
||||
|b| b,
|
||||
@@ -1060,17 +990,18 @@ impl ApiTester {
|
||||
for block_id in self.interesting_block_ids() {
|
||||
let result = self
|
||||
.client
|
||||
.get_beacon_blocks_attestations(block_id)
|
||||
.get_beacon_blocks_attestations(block_id.0)
|
||||
.await
|
||||
.unwrap()
|
||||
.map(|res| res.data);
|
||||
|
||||
let expected = self
|
||||
.get_block(block_id)
|
||||
.await
|
||||
.map(|block| block.message().body().attestations().clone().into());
|
||||
let expected = block_id.full_block(&self.chain).await.ok().map(
|
||||
|(block, _execution_optimistic)| {
|
||||
block.message().body().attestations().clone().into()
|
||||
},
|
||||
);
|
||||
|
||||
if let BlockId::Slot(slot) = block_id {
|
||||
if let CoreBlockId::Slot(slot) = block_id.0 {
|
||||
if expected.is_none() {
|
||||
assert!(SKIPPED_SLOTS.contains(&slot.as_u64()));
|
||||
} else {
|
||||
@@ -1473,9 +1404,16 @@ impl ApiTester {
|
||||
|
||||
pub async fn test_get_debug_beacon_states(self) -> Self {
|
||||
for state_id in self.interesting_state_ids() {
|
||||
let result_json = self.client.get_debug_beacon_states(state_id).await.unwrap();
|
||||
let result_json = self
|
||||
.client
|
||||
.get_debug_beacon_states(state_id.0)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut expected = self.get_state(state_id);
|
||||
let mut expected = state_id
|
||||
.state(&self.chain)
|
||||
.ok()
|
||||
.map(|(state, _execution_optimistic)| state);
|
||||
expected.as_mut().map(|state| state.drop_all_caches());
|
||||
|
||||
if let (Some(json), Some(expected)) = (&result_json, &expected) {
|
||||
@@ -1492,7 +1430,7 @@ impl ApiTester {
|
||||
// Check SSZ API.
|
||||
let result_ssz = self
|
||||
.client
|
||||
.get_debug_beacon_states_ssz(state_id, &self.chain.spec)
|
||||
.get_debug_beacon_states_ssz(state_id.0, &self.chain.spec)
|
||||
.await
|
||||
.unwrap();
|
||||
assert_eq!(result_ssz, expected, "{:?}", state_id);
|
||||
@@ -1500,7 +1438,7 @@ impl ApiTester {
|
||||
// Check legacy v1 API.
|
||||
let result_v1 = self
|
||||
.client
|
||||
.get_debug_beacon_states_v1(state_id)
|
||||
.get_debug_beacon_states_v1(state_id.0)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@@ -1513,7 +1451,10 @@ impl ApiTester {
|
||||
}
|
||||
|
||||
// Check that version headers are provided.
|
||||
let url = self.client.get_debug_beacon_states_path(state_id).unwrap();
|
||||
let url = self
|
||||
.client
|
||||
.get_debug_beacon_states_path(state_id.0)
|
||||
.unwrap();
|
||||
|
||||
let builders: Vec<fn(RequestBuilder) -> RequestBuilder> =
|
||||
vec![|b| b, |b| b.accept(Accept::Ssz)];
|
||||
@@ -1791,6 +1732,7 @@ impl ApiTester {
|
||||
|
||||
let expected = DutiesResponse {
|
||||
data: expected_duties,
|
||||
execution_optimistic: Some(false),
|
||||
dependent_root,
|
||||
};
|
||||
|
||||
@@ -2391,11 +2333,14 @@ impl ApiTester {
|
||||
for state_id in self.interesting_state_ids() {
|
||||
let result = self
|
||||
.client
|
||||
.get_lighthouse_beacon_states_ssz(&state_id, &self.chain.spec)
|
||||
.get_lighthouse_beacon_states_ssz(&state_id.0, &self.chain.spec)
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let mut expected = self.get_state(state_id);
|
||||
let mut expected = state_id
|
||||
.state(&self.chain)
|
||||
.ok()
|
||||
.map(|(state, _execution_optimistic)| state);
|
||||
expected.as_mut().map(|state| state.drop_all_caches());
|
||||
|
||||
assert_eq!(result, expected, "{:?}", state_id);
|
||||
@@ -2562,6 +2507,7 @@ impl ApiTester {
|
||||
let expected_block = EventKind::Block(SseBlock {
|
||||
block: block_root,
|
||||
slot: next_slot,
|
||||
execution_optimistic: false,
|
||||
});
|
||||
|
||||
let expected_head = EventKind::Head(SseHead {
|
||||
@@ -2575,6 +2521,7 @@ impl ApiTester {
|
||||
.unwrap()
|
||||
.unwrap(),
|
||||
epoch_transition: true,
|
||||
execution_optimistic: false,
|
||||
});
|
||||
|
||||
let finalized_block_root = self
|
||||
@@ -2593,6 +2540,7 @@ impl ApiTester {
|
||||
block: finalized_block_root,
|
||||
state: finalized_state_root,
|
||||
epoch: Epoch::new(3),
|
||||
execution_optimistic: false,
|
||||
});
|
||||
|
||||
self.client
|
||||
@@ -2621,6 +2569,7 @@ impl ApiTester {
|
||||
new_head_block: self.reorg_block.canonical_root(),
|
||||
new_head_state: self.reorg_block.state_root(),
|
||||
epoch: self.next_block.slot().epoch(E::slots_per_epoch()),
|
||||
execution_optimistic: false,
|
||||
});
|
||||
|
||||
self.client
|
||||
@@ -2687,6 +2636,7 @@ impl ApiTester {
|
||||
let expected_block = EventKind::Block(SseBlock {
|
||||
block: block_root,
|
||||
slot: next_slot,
|
||||
execution_optimistic: false,
|
||||
});
|
||||
|
||||
let expected_head = EventKind::Head(SseHead {
|
||||
@@ -2696,6 +2646,7 @@ impl ApiTester {
|
||||
current_duty_dependent_root: self.chain.genesis_block_root,
|
||||
previous_duty_dependent_root: self.chain.genesis_block_root,
|
||||
epoch_transition: false,
|
||||
execution_optimistic: false,
|
||||
});
|
||||
|
||||
self.client
|
||||
@@ -2708,6 +2659,40 @@ impl ApiTester {
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn test_check_optimistic_responses(&mut self) {
|
||||
// Check responses are not optimistic.
|
||||
let result = self
|
||||
.client
|
||||
.get_beacon_headers_block_id(CoreBlockId::Head)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result.execution_optimistic, Some(false));
|
||||
|
||||
// Change head to be optimistic.
|
||||
self.chain
|
||||
.canonical_head
|
||||
.fork_choice_write_lock()
|
||||
.proto_array_mut()
|
||||
.core_proto_array_mut()
|
||||
.nodes
|
||||
.last_mut()
|
||||
.map(|head_node| {
|
||||
head_node.execution_status = ExecutionStatus::Optimistic(ExecutionBlockHash::zero())
|
||||
});
|
||||
|
||||
// Check responses are now optimistic.
|
||||
let result = self
|
||||
.client
|
||||
.get_beacon_headers_block_id(CoreBlockId::Head)
|
||||
.await
|
||||
.unwrap()
|
||||
.unwrap();
|
||||
|
||||
assert_eq!(result.execution_optimistic, Some(true));
|
||||
}
|
||||
}
|
||||
|
||||
async fn poll_events<S: Stream<Item = Result<EventKind<T>, eth2::Error>> + Unpin, T: EthSpec>(
|
||||
@@ -3105,3 +3090,11 @@ async fn lighthouse_endpoints() {
|
||||
.test_post_lighthouse_liveness()
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test(flavor = "multi_thread", worker_threads = 2)]
|
||||
async fn optimistic_responses() {
|
||||
ApiTester::new_with_hard_forks(true, true)
|
||||
.await
|
||||
.test_check_optimistic_responses()
|
||||
.await;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user