Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions beacon_node/execution_layer/src/engine_api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,11 @@ use crate::engines::ForkchoiceState;
use crate::http::{
ENGINE_FORKCHOICE_UPDATED_V1, ENGINE_FORKCHOICE_UPDATED_V2, ENGINE_FORKCHOICE_UPDATED_V3,
ENGINE_FORKCHOICE_UPDATED_V4, ENGINE_GET_BLOBS_V2, ENGINE_GET_CLIENT_VERSION_V1,
ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1, ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1,
ENGINE_GET_PAYLOAD_V1, ENGINE_GET_PAYLOAD_V2, ENGINE_GET_PAYLOAD_V3, ENGINE_GET_PAYLOAD_V4,
ENGINE_GET_PAYLOAD_V5, ENGINE_GET_PAYLOAD_V6, ENGINE_NEW_PAYLOAD_V1, ENGINE_NEW_PAYLOAD_V2,
ENGINE_NEW_PAYLOAD_V3, ENGINE_NEW_PAYLOAD_V4, ENGINE_NEW_PAYLOAD_V5,
ENGINE_GET_INCLUSION_LIST_V1, ENGINE_GET_PAYLOAD_BODIES_BY_HASH_V1,
ENGINE_GET_PAYLOAD_BODIES_BY_RANGE_V1, ENGINE_GET_PAYLOAD_V1, ENGINE_GET_PAYLOAD_V2,
ENGINE_GET_PAYLOAD_V3, ENGINE_GET_PAYLOAD_V4, ENGINE_GET_PAYLOAD_V5, ENGINE_GET_PAYLOAD_V6,
ENGINE_NEW_PAYLOAD_V1, ENGINE_NEW_PAYLOAD_V2, ENGINE_NEW_PAYLOAD_V3, ENGINE_NEW_PAYLOAD_V4,
ENGINE_NEW_PAYLOAD_V5,
};
use eth2::types::{
BlobsBundle, SsePayloadAttributes, SsePayloadAttributesV1, SsePayloadAttributesV2,
Expand Down Expand Up @@ -614,6 +615,7 @@ pub struct EngineCapabilities {
pub get_client_version_v1: bool,
pub get_blobs_v2: bool,
pub get_blobs_v3: bool,
pub get_inclusion_list_v1: bool,
}

impl EngineCapabilities {
Expand Down Expand Up @@ -676,6 +678,9 @@ impl EngineCapabilities {
if self.get_blobs_v2 {
response.push(ENGINE_GET_BLOBS_V2);
}
if self.get_inclusion_list_v1 {
response.push(ENGINE_GET_INCLUSION_LIST_V1);
}

response
}
Expand Down
95 changes: 94 additions & 1 deletion beacon_node/execution_layer/src/engine_api/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use std::sync::LazyLock;
use tokio::sync::Mutex;
use tracing::Span;
use tracing_opentelemetry::OpenTelemetrySpanExt;
use types::ProgressiveTransactions;

use std::time::{Duration, Instant};

Expand Down Expand Up @@ -67,6 +68,9 @@ pub const ENGINE_GET_BLOBS_V2: &str = "engine_getBlobsV2";
pub const ENGINE_GET_BLOBS_V3: &str = "engine_getBlobsV3";
pub const ENGINE_GET_BLOBS_TIMEOUT: Duration = Duration::from_secs(1);

pub const ENGINE_GET_INCLUSION_LIST_V1: &str = "engine_getInclusionListV1";
pub const ENGINE_GET_INCLUSION_LIST_TIMEOUT: Duration = Duration::from_secs(1);

/// This error is returned during a `chainId` call by Geth.
pub const EIP155_ERROR_STR: &str = "chain not synced beyond EIP-155 replay-protection fork block";
/// This code is returned by all clients when a method is not supported
Expand Down Expand Up @@ -94,6 +98,7 @@ pub static LIGHTHOUSE_CAPABILITIES: &[&str] = &[
ENGINE_GET_CLIENT_VERSION_V1,
ENGINE_GET_BLOBS_V2,
ENGINE_GET_BLOBS_V3,
ENGINE_GET_INCLUSION_LIST_V1,
];

/// We opt to initialize the JsonClientVersionV1 rather than the ClientVersionV1
Expand Down Expand Up @@ -752,6 +757,21 @@ impl HttpJsonRpc {
.await
}

pub async fn get_inclusion_list_v1(
&self,
block_hash: ExecutionBlockHash,
) -> Result<ProgressiveTransactions, Error> {
let params = json!([block_hash]);

self.rpc_request::<JsonInclusionListV1>(
ENGINE_GET_INCLUSION_LIST_V1,
params,
ENGINE_GET_INCLUSION_LIST_TIMEOUT * self.execution_timeout_multiplier,
)
.await
.map(|response| response.0)
}

pub async fn get_block_by_number(
&self,
query: BlockByNumberQuery<'_>,
Expand Down Expand Up @@ -1251,6 +1271,7 @@ impl HttpJsonRpc {
get_client_version_v1: capabilities.contains(ENGINE_GET_CLIENT_VERSION_V1),
get_blobs_v2: capabilities.contains(ENGINE_GET_BLOBS_V2),
get_blobs_v3: capabilities.contains(ENGINE_GET_BLOBS_V3),
get_inclusion_list_v1: capabilities.contains(ENGINE_GET_INCLUSION_LIST_V1),
})
}

Expand Down Expand Up @@ -1529,7 +1550,7 @@ mod test {
use super::*;
use crate::test_utils::{DEFAULT_JWT_SECRET, MockServer};
use fixed_bytes::FixedBytesExtended;
use ssz_types::VariableList;
use ssz_types::{ProgressiveVariableList, VariableList};
use std::future::Future;
use std::str::FromStr;
use std::sync::Arc;
Expand Down Expand Up @@ -1709,6 +1730,15 @@ mod test {
txs
}

fn generate_progressive_transactions(spec: &[usize]) -> ProgressiveTransactions {
let mut txs = ProgressiveTransactions::empty();
for &num_bytes in spec {
txs.push(ProgressiveVariableList::new(vec![0; num_bytes]));
}

txs
}

#[test]
fn transaction_serde() {
assert_transactions_serde::<MainnetEthSpec>(
Expand Down Expand Up @@ -1757,6 +1787,42 @@ mod test {
);
}

fn assert_inclusion_list_serde(
name: &str,
as_obj: ProgressiveTransactions,
as_json: serde_json::Value,
) {
assert_eq!(
serde_json::to_value(JsonInclusionListV1(as_obj.clone())).unwrap(),
as_json,
"encoding for {}",
name
);
assert_eq!(
serde_json::from_value::<JsonInclusionListV1>(as_json)
.unwrap()
.0,
as_obj,
"decoding for {}",
name
);
}

#[test]
fn inclusion_list_serde() {
assert_inclusion_list_serde("empty", generate_progressive_transactions(&[]), json!([]));
assert_inclusion_list_serde(
"one empty tx",
generate_progressive_transactions(&[0]),
json!(["0x"]),
);
assert_inclusion_list_serde(
"mixed bag",
generate_progressive_transactions(&[0, 1, 3, 0]),
json!(["0x", "0x00", "0x000000", "0x"]),
);
}

#[tokio::test]
async fn get_block_by_number_request() {
Tester::new(true)
Expand Down Expand Up @@ -1784,6 +1850,33 @@ mod test {
.await;
}

#[tokio::test]
async fn get_inclusion_list_v1_request() {
Tester::new(true)
.assert_request_equals(
|client| async move {
let _ = client
.get_inclusion_list_v1(ExecutionBlockHash::repeat_byte(1))
.await;
},
json!({
"id": STATIC_ID,
"jsonrpc": JSONRPC_VERSION,
"method": ENGINE_GET_INCLUSION_LIST_V1,
"params": [HASH_01]
}),
)
.await;

Tester::new(false)
.assert_auth_failure(|client| async move {
client
.get_inclusion_list_v1(ExecutionBlockHash::repeat_byte(1))
.await
})
.await;
}

#[tokio::test]
async fn forkchoice_updated_v1_with_payload_attributes_request() {
Tester::new(true)
Expand Down
7 changes: 7 additions & 0 deletions beacon_node/execution_layer/src/engine_api/json_structures.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1313,6 +1313,13 @@ impl TryFrom<JsonClientVersionV1> for ClientVersionV1 {
}
}

#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
#[serde(transparent)]
pub struct JsonInclusionListV1(
#[serde(with = "ssz_types::serde_utils::prog_list_of_hex_prog_var_list")]
pub ProgressiveTransactions,
);

#[cfg(test)]
mod tests {
use bls::{PublicKeyBytes, SignatureBytes};
Expand Down
21 changes: 20 additions & 1 deletion beacon_node/execution_layer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ use types::execution::BlockProductionVersion;
use types::kzg_ext::{KzgCommitments, ProgressiveKzgCommitments};
use types::{
AbstractExecPayload, BlobsList, ExecutionPayloadDeneb, ExecutionRequests,
ExecutionRequestsElectra, ExecutionRequestsGloas, KzgProofs, SignedBlindedBeaconBlock,
ExecutionRequestsElectra, ExecutionRequestsGloas, KzgProofs, ProgressiveTransactions,
SignedBlindedBeaconBlock,
};
use types::{
BeaconStateError, BlindedPayload, ChainSpec, Epoch, ExecPayload, ExecutionPayloadBellatrix,
Expand Down Expand Up @@ -155,6 +156,7 @@ pub enum Error {
ZeroLengthTransaction,
PayloadBodiesByRangeNotSupported,
GetBlobsNotSupported,
GetInclusionListNotSupported,
InvalidJWTSecret(String),
InvalidForkForPayload,
InvalidPayloadBody(String),
Expand Down Expand Up @@ -1776,6 +1778,23 @@ impl<E: EthSpec> ExecutionLayer<E> {
}
}

pub async fn get_inclusion_list_v1(
&self,
block_hash: ExecutionBlockHash,
) -> Result<ProgressiveTransactions, Error> {
let capabilities = self.get_engine_capabilities(None).await?;

if capabilities.get_inclusion_list_v1 {
self.engine()
.request(|engine| async move { engine.api.get_inclusion_list_v1(block_hash).await })
.await
.map_err(Box::new)
.map_err(Error::EngineError)
} else {
Err(Error::GetInclusionListNotSupported)
}
}

pub async fn get_block_by_number(
&self,
query: BlockByNumberQuery<'_>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use types::{
Blob, ChainSpec, EthSpec, ExecutionBlockHash, ExecutionPayload, ExecutionPayloadBellatrix,
ExecutionPayloadCapella, ExecutionPayloadDeneb, ExecutionPayloadElectra, ExecutionPayloadFulu,
ExecutionPayloadGloas, ExecutionPayloadHeader, ExecutionPayloadHeze, ExecutionRequests,
ForkName, Hash256, KzgProofs, Transaction, Transactions, Uint256,
ForkName, Hash256, KzgProofs, ProgressiveTransactions, Transaction, Transactions, Uint256,
};

const TEST_BLOB_BUNDLE: &[u8] = include_bytes!("fixtures/mainnet/test_blobs_bundle.ssz");
Expand Down Expand Up @@ -178,6 +178,11 @@ pub struct ExecutionBlockGenerator<E: EthSpec> {
/// execution requests with the generated payload ID.
next_execution_requests: Option<ExecutionRequests<E>>,
generate_blobs: bool,
/*
* Inclusion lists (heze+)
*/
/// The transactions returned by `getInclusionList` for any known block.
inclusion_list: ProgressiveTransactions,
}

fn make_rng() -> Arc<Mutex<StdRng>> {
Expand Down Expand Up @@ -221,6 +226,7 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
execution_requests: <_>::default(),
next_execution_requests: None,
generate_blobs: true,
inclusion_list: <_>::default(),
};

generator.insert_pow_block(0).unwrap();
Expand Down Expand Up @@ -489,6 +495,21 @@ impl<E: EthSpec> ExecutionBlockGenerator<E> {
self.next_execution_requests = Some(requests);
}

/// Return the configured inclusion list transactions for the provided block.
pub fn get_inclusion_list(
&self,
block_hash: ExecutionBlockHash,
) -> Option<ProgressiveTransactions> {
self.blocks
.contains_key(&block_hash)
.then(|| self.inclusion_list.clone())
}

/// Set the transactions returned by `getInclusionList`.
pub fn set_inclusion_list(&mut self, transactions: ProgressiveTransactions) {
self.inclusion_list = transactions;
}

/// Look up a blob and proof by versioned hash across all stored bundles.
pub fn get_blob_and_proof(&self, versioned_hash: &Hash256) -> Option<BlobAndProof<E>> {
self.blobs_bundles
Expand Down
16 changes: 16 additions & 0 deletions beacon_node/execution_layer/src/test_utils/handle_rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,22 @@ pub async fn handle_rpc<E: EthSpec>(
let response: Option<Vec<BlobAndProofV2<E>>> = results.into_iter().collect();
Ok(serde_json::to_value(response).unwrap())
}
ENGINE_GET_INCLUSION_LIST_V1 => {
let block_hash = get_param::<ExecutionBlockHash>(params, 0)
.map_err(|s| (s, BAD_PARAMS_ERROR_CODE))?;
let transactions = ctx
.execution_block_generator
.read()
.get_inclusion_list(block_hash)
.ok_or_else(|| {
(
format!("no block for hash {:?}", block_hash),
UNKNOWN_PAYLOAD_ERROR_CODE,
)
})?;

Ok(serde_json::to_value(JsonInclusionListV1(transactions)).unwrap())
}
ENGINE_FORKCHOICE_UPDATED_V1
| ENGINE_FORKCHOICE_UPDATED_V2
| ENGINE_FORKCHOICE_UPDATED_V3
Expand Down
1 change: 1 addition & 0 deletions beacon_node/execution_layer/src/test_utils/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ pub const DEFAULT_ENGINE_CAPABILITIES: EngineCapabilities = EngineCapabilities {
get_client_version_v1: true,
get_blobs_v2: true,
get_blobs_v3: true,
get_inclusion_list_v1: true,
};

pub static DEFAULT_CLIENT_VERSION: LazyLock<JsonClientVersionV1> =
Expand Down