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:
Mac L
2022-07-25 08:23:00 +00:00
parent 21dec6f603
commit bb5a6d2cca
18 changed files with 1227 additions and 693 deletions

View File

@@ -332,7 +332,7 @@ impl BeaconNodeHttpClient {
pub async fn get_beacon_states_root(
&self,
state_id: StateId,
) -> Result<Option<GenericResponse<RootData>>, Error> {
) -> Result<Option<ExecutionOptimisticResponse<RootData>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -351,7 +351,7 @@ impl BeaconNodeHttpClient {
pub async fn get_beacon_states_fork(
&self,
state_id: StateId,
) -> Result<Option<GenericResponse<Fork>>, Error> {
) -> Result<Option<ExecutionOptimisticResponse<Fork>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -370,7 +370,7 @@ impl BeaconNodeHttpClient {
pub async fn get_beacon_states_finality_checkpoints(
&self,
state_id: StateId,
) -> Result<Option<GenericResponse<FinalityCheckpointsData>>, Error> {
) -> Result<Option<ExecutionOptimisticResponse<FinalityCheckpointsData>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -390,7 +390,7 @@ impl BeaconNodeHttpClient {
&self,
state_id: StateId,
ids: Option<&[ValidatorId]>,
) -> Result<Option<GenericResponse<Vec<ValidatorBalanceData>>>, Error> {
) -> Result<Option<ExecutionOptimisticResponse<Vec<ValidatorBalanceData>>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -420,7 +420,7 @@ impl BeaconNodeHttpClient {
state_id: StateId,
ids: Option<&[ValidatorId]>,
statuses: Option<&[ValidatorStatus]>,
) -> Result<Option<GenericResponse<Vec<ValidatorData>>>, Error> {
) -> Result<Option<ExecutionOptimisticResponse<Vec<ValidatorData>>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -460,7 +460,7 @@ impl BeaconNodeHttpClient {
slot: Option<Slot>,
index: Option<u64>,
epoch: Option<Epoch>,
) -> Result<Option<GenericResponse<Vec<CommitteeData>>>, Error> {
) -> Result<Option<ExecutionOptimisticResponse<Vec<CommitteeData>>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -493,7 +493,7 @@ impl BeaconNodeHttpClient {
&self,
state_id: StateId,
epoch: Option<Epoch>,
) -> Result<GenericResponse<SyncCommitteeByValidatorIndices>, Error> {
) -> Result<ExecutionOptimisticResponse<SyncCommitteeByValidatorIndices>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -518,7 +518,7 @@ impl BeaconNodeHttpClient {
&self,
state_id: StateId,
validator_id: &ValidatorId,
) -> Result<Option<GenericResponse<ValidatorData>>, Error> {
) -> Result<Option<ExecutionOptimisticResponse<ValidatorData>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -539,7 +539,7 @@ impl BeaconNodeHttpClient {
&self,
slot: Option<Slot>,
parent_root: Option<Hash256>,
) -> Result<Option<GenericResponse<Vec<BlockHeaderData>>>, Error> {
) -> Result<Option<ExecutionOptimisticResponse<Vec<BlockHeaderData>>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -566,7 +566,7 @@ impl BeaconNodeHttpClient {
pub async fn get_beacon_headers_block_id(
&self,
block_id: BlockId,
) -> Result<Option<GenericResponse<BlockHeaderData>>, Error> {
) -> Result<Option<ExecutionOptimisticResponse<BlockHeaderData>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -635,7 +635,7 @@ impl BeaconNodeHttpClient {
pub async fn get_beacon_blocks<T: EthSpec>(
&self,
block_id: BlockId,
) -> Result<Option<ForkVersionedResponse<SignedBeaconBlock<T>>>, Error> {
) -> Result<Option<ExecutionOptimisticForkVersionedResponse<SignedBeaconBlock<T>>>, Error> {
let path = self.get_beacon_blocks_path(block_id)?;
let response = match self.get_response(path, |b| b).await.optional()? {
Some(res) => res,
@@ -644,20 +644,31 @@ impl BeaconNodeHttpClient {
// If present, use the fork provided in the headers to decode the block. Gracefully handle
// missing and malformed fork names by falling back to regular deserialisation.
let (block, version) = match response.fork_name_from_header() {
let (block, version, execution_optimistic) = match response.fork_name_from_header() {
Ok(Some(fork_name)) => {
map_fork_name_with!(fork_name, SignedBeaconBlock, {
let ForkVersionedResponse { version, data } = response.json().await?;
(data, version)
})
let (data, (version, execution_optimistic)) =
map_fork_name_with!(fork_name, SignedBeaconBlock, {
let ExecutionOptimisticForkVersionedResponse {
version,
execution_optimistic,
data,
} = response.json().await?;
(data, (version, execution_optimistic))
});
(data, version, execution_optimistic)
}
Ok(None) | Err(_) => {
let ForkVersionedResponse { version, data } = response.json().await?;
(data, version)
let ExecutionOptimisticForkVersionedResponse {
version,
execution_optimistic,
data,
} = response.json().await?;
(data, version, execution_optimistic)
}
};
Ok(Some(ForkVersionedResponse {
Ok(Some(ExecutionOptimisticForkVersionedResponse {
version,
execution_optimistic,
data: block,
}))
}
@@ -702,7 +713,7 @@ impl BeaconNodeHttpClient {
pub async fn get_beacon_blocks_root(
&self,
block_id: BlockId,
) -> Result<Option<GenericResponse<RootData>>, Error> {
) -> Result<Option<ExecutionOptimisticResponse<RootData>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -721,7 +732,7 @@ impl BeaconNodeHttpClient {
pub async fn get_beacon_blocks_attestations<T: EthSpec>(
&self,
block_id: BlockId,
) -> Result<Option<GenericResponse<Vec<Attestation<T>>>>, Error> {
) -> Result<Option<ExecutionOptimisticResponse<Vec<Attestation<T>>>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -1123,7 +1134,7 @@ impl BeaconNodeHttpClient {
pub async fn get_debug_beacon_states<T: EthSpec>(
&self,
state_id: StateId,
) -> Result<Option<ForkVersionedResponse<BeaconState<T>>>, Error> {
) -> Result<Option<ExecutionOptimisticForkVersionedResponse<BeaconState<T>>>, Error> {
let path = self.get_debug_beacon_states_path(state_id)?;
self.get_opt(path).await
}
@@ -1132,7 +1143,7 @@ impl BeaconNodeHttpClient {
pub async fn get_debug_beacon_states_v1<T: EthSpec>(
&self,
state_id: StateId,
) -> Result<Option<ForkVersionedResponse<BeaconState<T>>>, Error> {
) -> Result<Option<ExecutionOptimisticForkVersionedResponse<BeaconState<T>>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()
@@ -1160,9 +1171,24 @@ impl BeaconNodeHttpClient {
.transpose()
}
/// `GET debug/beacon/heads`
/// `GET v2/debug/beacon/heads`
pub async fn get_debug_beacon_heads(
&self,
) -> Result<GenericResponse<Vec<ChainHeadData>>, Error> {
let mut path = self.eth_path(V2)?;
path.path_segments_mut()
.map_err(|()| Error::InvalidUrl(self.server.clone()))?
.push("debug")
.push("beacon")
.push("heads");
self.get(path).await
}
/// `GET v1/debug/beacon/heads` (LEGACY)
pub async fn get_debug_beacon_heads_v1(
&self,
) -> Result<GenericResponse<Vec<ChainHeadData>>, Error> {
let mut path = self.eth_path(V1)?;
@@ -1494,7 +1520,7 @@ impl BeaconNodeHttpClient {
&self,
epoch: Epoch,
indices: &[u64],
) -> Result<GenericResponse<Vec<SyncDuty>>, Error> {
) -> Result<ExecutionOptimisticResponse<Vec<SyncDuty>>, Error> {
let mut path = self.eth_path(V1)?;
path.path_segments_mut()

View File

@@ -189,6 +189,14 @@ impl fmt::Display for StateId {
#[serde(bound = "T: Serialize + serde::de::DeserializeOwned")]
pub struct DutiesResponse<T: Serialize + serde::de::DeserializeOwned> {
pub dependent_root: Hash256,
pub execution_optimistic: Option<bool>,
pub data: T,
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
#[serde(bound = "T: Serialize + serde::de::DeserializeOwned")]
pub struct ExecutionOptimisticResponse<T: Serialize + serde::de::DeserializeOwned> {
pub execution_optimistic: Option<bool>,
pub data: T,
}
@@ -204,6 +212,18 @@ impl<T: Serialize + serde::de::DeserializeOwned> From<T> for GenericResponse<T>
}
}
impl<T: Serialize + serde::de::DeserializeOwned> GenericResponse<T> {
pub fn add_execution_optimistic(
self,
execution_optimistic: bool,
) -> ExecutionOptimisticResponse<T> {
ExecutionOptimisticResponse {
execution_optimistic: Some(execution_optimistic),
data: self.data,
}
}
}
#[derive(Debug, PartialEq, Clone, Serialize)]
#[serde(bound = "T: Serialize")]
pub struct GenericResponseRef<'a, T: Serialize> {
@@ -216,6 +236,14 @@ impl<'a, T: Serialize> From<&'a T> for GenericResponseRef<'a, T> {
}
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct ExecutionOptimisticForkVersionedResponse<T> {
#[serde(skip_serializing_if = "Option::is_none")]
pub version: Option<ForkName>,
pub execution_optimistic: Option<bool>,
pub data: T,
}
#[derive(Debug, PartialEq, Clone, Serialize, Deserialize)]
pub struct ForkVersionedResponse<T> {
#[serde(skip_serializing_if = "Option::is_none")]
@@ -495,6 +523,8 @@ pub struct DepositContractData {
pub struct ChainHeadData {
pub slot: Slot,
pub root: Hash256,
#[serde(skip_serializing_if = "Option::is_none")]
pub execution_optimistic: Option<bool>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
@@ -794,6 +824,7 @@ pub struct PeerCount {
pub struct SseBlock {
pub slot: Slot,
pub block: Hash256,
pub execution_optimistic: bool,
}
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
@@ -801,6 +832,7 @@ pub struct SseFinalizedCheckpoint {
pub block: Hash256,
pub state: Hash256,
pub epoch: Epoch,
pub execution_optimistic: bool,
}
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
@@ -811,6 +843,7 @@ pub struct SseHead {
pub current_duty_dependent_root: Hash256,
pub previous_duty_dependent_root: Hash256,
pub epoch_transition: bool,
pub execution_optimistic: bool,
}
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
@@ -823,6 +856,7 @@ pub struct SseChainReorg {
pub new_head_block: Hash256,
pub new_head_state: Hash256,
pub epoch: Epoch,
pub execution_optimistic: bool,
}
#[derive(PartialEq, Debug, Serialize, Deserialize, Clone)]
@@ -837,6 +871,7 @@ pub struct SseLateHead {
pub observed_delay: Option<Duration>,
pub imported_delay: Option<Duration>,
pub set_as_head_delay: Option<Duration>,
pub execution_optimistic: bool,
}
#[derive(PartialEq, Debug, Serialize, Clone)]