mirror of
https://github.com/sigp/lighthouse.git
synced 2026-03-18 04:13:00 +00:00
Merge remote-tracking branch 'origin/unstable' into tree-states
This commit is contained in:
148
beacon_node/execution_layer/src/engine_api/auth.rs
Normal file
148
beacon_node/execution_layer/src/engine_api/auth.rs
Normal file
@@ -0,0 +1,148 @@
|
||||
use jsonwebtoken::{encode, get_current_timestamp, Algorithm, EncodingKey, Header};
|
||||
use rand::Rng;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use zeroize::Zeroize;
|
||||
|
||||
/// Default algorithm used for JWT token signing.
|
||||
const DEFAULT_ALGORITHM: Algorithm = Algorithm::HS256;
|
||||
|
||||
/// JWT secret length in bytes.
|
||||
pub const JWT_SECRET_LENGTH: usize = 32;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
JWT(jsonwebtoken::errors::Error),
|
||||
InvalidToken,
|
||||
}
|
||||
|
||||
impl From<jsonwebtoken::errors::Error> for Error {
|
||||
fn from(e: jsonwebtoken::errors::Error) -> Self {
|
||||
Error::JWT(e)
|
||||
}
|
||||
}
|
||||
|
||||
/// Provides wrapper around `[u8; JWT_SECRET_LENGTH]` that implements `Zeroize`.
|
||||
#[derive(Zeroize)]
|
||||
#[zeroize(drop)]
|
||||
pub struct JwtKey([u8; JWT_SECRET_LENGTH as usize]);
|
||||
|
||||
impl JwtKey {
|
||||
/// Wrap given slice in `Self`. Returns an error if slice.len() != `JWT_SECRET_LENGTH`.
|
||||
pub fn from_slice(key: &[u8]) -> Result<Self, String> {
|
||||
if key.len() != JWT_SECRET_LENGTH {
|
||||
return Err(format!(
|
||||
"Invalid key length. Expected {} got {}",
|
||||
JWT_SECRET_LENGTH,
|
||||
key.len()
|
||||
));
|
||||
}
|
||||
let mut res = [0; JWT_SECRET_LENGTH];
|
||||
res.copy_from_slice(key);
|
||||
Ok(Self(res))
|
||||
}
|
||||
|
||||
/// Generate a random secret.
|
||||
pub fn random() -> Self {
|
||||
Self(rand::thread_rng().gen::<[u8; JWT_SECRET_LENGTH]>())
|
||||
}
|
||||
|
||||
/// Returns a reference to the underlying byte array.
|
||||
pub fn as_bytes(&self) -> &[u8] {
|
||||
&self.0
|
||||
}
|
||||
|
||||
/// Returns the hex encoded `String` for the secret.
|
||||
pub fn hex_string(&self) -> String {
|
||||
hex::encode(self.0)
|
||||
}
|
||||
}
|
||||
|
||||
/// Contains the JWT secret and claims parameters.
|
||||
pub struct Auth {
|
||||
key: EncodingKey,
|
||||
id: Option<String>,
|
||||
clv: Option<String>,
|
||||
}
|
||||
|
||||
impl Auth {
|
||||
pub fn new(secret: JwtKey, id: Option<String>, clv: Option<String>) -> Self {
|
||||
Self {
|
||||
key: EncodingKey::from_secret(secret.as_bytes()),
|
||||
id,
|
||||
clv,
|
||||
}
|
||||
}
|
||||
|
||||
/// Generate a JWT token with `claims.iat` set to current time.
|
||||
pub fn generate_token(&self) -> Result<String, Error> {
|
||||
let claims = self.generate_claims_at_timestamp();
|
||||
self.generate_token_with_claims(&claims)
|
||||
}
|
||||
|
||||
/// Generate a JWT token with the given claims.
|
||||
fn generate_token_with_claims(&self, claims: &Claims) -> Result<String, Error> {
|
||||
let header = Header::new(DEFAULT_ALGORITHM);
|
||||
Ok(encode(&header, claims, &self.key)?)
|
||||
}
|
||||
|
||||
/// Generate a `Claims` struct with `iat` set to current time
|
||||
fn generate_claims_at_timestamp(&self) -> Claims {
|
||||
Claims {
|
||||
iat: get_current_timestamp(),
|
||||
id: self.id.clone(),
|
||||
clv: self.clv.clone(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate a JWT token given the secret key and return the originally signed `TokenData`.
|
||||
pub fn validate_token(
|
||||
token: &str,
|
||||
secret: &JwtKey,
|
||||
) -> Result<jsonwebtoken::TokenData<Claims>, Error> {
|
||||
let mut validation = jsonwebtoken::Validation::new(DEFAULT_ALGORITHM);
|
||||
validation.validate_exp = false;
|
||||
validation.required_spec_claims.remove("exp");
|
||||
|
||||
jsonwebtoken::decode::<Claims>(
|
||||
token,
|
||||
&jsonwebtoken::DecodingKey::from_secret(secret.as_bytes()),
|
||||
&validation,
|
||||
)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
/// Claims struct as defined in https://github.com/ethereum/execution-apis/blob/main/src/engine/authentication.md#jwt-claims
|
||||
#[derive(Debug, Serialize, Deserialize, PartialEq)]
|
||||
pub struct Claims {
|
||||
/// issued-at claim. Represented as seconds passed since UNIX_EPOCH.
|
||||
iat: u64,
|
||||
/// Optional unique identifier for the CL node.
|
||||
id: Option<String>,
|
||||
/// Optional client version for the CL node.
|
||||
clv: Option<String>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use crate::test_utils::JWT_SECRET;
|
||||
|
||||
#[test]
|
||||
fn test_roundtrip() {
|
||||
let auth = Auth::new(
|
||||
JwtKey::from_slice(&JWT_SECRET).unwrap(),
|
||||
Some("42".into()),
|
||||
Some("Lighthouse".into()),
|
||||
);
|
||||
let claims = auth.generate_claims_at_timestamp();
|
||||
let token = auth.generate_token_with_claims(&claims).unwrap();
|
||||
|
||||
assert_eq!(
|
||||
Auth::validate_token(&token, &JwtKey::from_slice(&JWT_SECRET).unwrap())
|
||||
.unwrap()
|
||||
.claims,
|
||||
claims
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
//! Contains an implementation of `EngineAPI` using the JSON-RPC API via HTTP.
|
||||
|
||||
use super::*;
|
||||
use crate::auth::Auth;
|
||||
use crate::json_structures::*;
|
||||
use async_trait::async_trait;
|
||||
use eth1::http::EIP155_ERROR_STR;
|
||||
@@ -36,9 +37,15 @@ pub const ENGINE_GET_PAYLOAD_TIMEOUT: Duration = Duration::from_secs(2);
|
||||
pub const ENGINE_FORKCHOICE_UPDATED_V1: &str = "engine_forkchoiceUpdatedV1";
|
||||
pub const ENGINE_FORKCHOICE_UPDATED_TIMEOUT: Duration = Duration::from_millis(500);
|
||||
|
||||
pub const ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_V1: &str =
|
||||
"engine_exchangeTransitionConfigurationV1";
|
||||
pub const ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_V1_TIMEOUT: Duration =
|
||||
Duration::from_millis(500);
|
||||
|
||||
pub struct HttpJsonRpc {
|
||||
pub client: Client,
|
||||
pub url: SensitiveUrl,
|
||||
auth: Option<Auth>,
|
||||
}
|
||||
|
||||
impl HttpJsonRpc {
|
||||
@@ -46,6 +53,15 @@ impl HttpJsonRpc {
|
||||
Ok(Self {
|
||||
client: Client::builder().build()?,
|
||||
url,
|
||||
auth: None,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn new_with_auth(url: SensitiveUrl, auth: Auth) -> Result<Self, Error> {
|
||||
Ok(Self {
|
||||
client: Client::builder().build()?,
|
||||
url,
|
||||
auth: Some(auth),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -62,17 +78,19 @@ impl HttpJsonRpc {
|
||||
id: STATIC_ID,
|
||||
};
|
||||
|
||||
let body: JsonResponseBody = self
|
||||
let mut request = self
|
||||
.client
|
||||
.post(self.url.full.clone())
|
||||
.timeout(timeout)
|
||||
.header(CONTENT_TYPE, "application/json")
|
||||
.json(&body)
|
||||
.send()
|
||||
.await?
|
||||
.error_for_status()?
|
||||
.json()
|
||||
.await?;
|
||||
.json(&body);
|
||||
|
||||
// Generate and add a jwt token to the header if auth is defined.
|
||||
if let Some(auth) = &self.auth {
|
||||
request = request.bearer_auth(auth.generate_token()?);
|
||||
};
|
||||
|
||||
let body: JsonResponseBody = request.send().await?.error_for_status()?.json().await?;
|
||||
|
||||
match (body.result, body.error) {
|
||||
(result, None) => serde_json::from_value(result).map_err(Into::into),
|
||||
@@ -179,12 +197,30 @@ impl EngineApi for HttpJsonRpc {
|
||||
|
||||
Ok(response.into())
|
||||
}
|
||||
|
||||
async fn exchange_transition_configuration_v1(
|
||||
&self,
|
||||
transition_configuration: TransitionConfigurationV1,
|
||||
) -> Result<TransitionConfigurationV1, Error> {
|
||||
let params = json!([transition_configuration]);
|
||||
|
||||
let response = self
|
||||
.rpc_request(
|
||||
ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_V1,
|
||||
params,
|
||||
ENGINE_EXCHANGE_TRANSITION_CONFIGURATION_V1_TIMEOUT,
|
||||
)
|
||||
.await?;
|
||||
|
||||
Ok(response)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use super::auth::JwtKey;
|
||||
use super::*;
|
||||
use crate::test_utils::MockServer;
|
||||
use crate::test_utils::{MockServer, JWT_SECRET};
|
||||
use std::future::Future;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
@@ -197,14 +233,25 @@ mod test {
|
||||
}
|
||||
|
||||
impl Tester {
|
||||
pub fn new() -> Self {
|
||||
pub fn new(with_auth: bool) -> Self {
|
||||
let server = MockServer::unit_testing();
|
||||
|
||||
let rpc_url = SensitiveUrl::parse(&server.url()).unwrap();
|
||||
let rpc_client = Arc::new(HttpJsonRpc::new(rpc_url).unwrap());
|
||||
|
||||
let echo_url = SensitiveUrl::parse(&format!("{}/echo", server.url())).unwrap();
|
||||
let echo_client = Arc::new(HttpJsonRpc::new(echo_url).unwrap());
|
||||
// Create rpc clients that include JWT auth headers if `with_auth` is true.
|
||||
let (rpc_client, echo_client) = if with_auth {
|
||||
let rpc_auth = Auth::new(JwtKey::from_slice(&JWT_SECRET).unwrap(), None, None);
|
||||
let echo_auth = Auth::new(JwtKey::from_slice(&JWT_SECRET).unwrap(), None, None);
|
||||
(
|
||||
Arc::new(HttpJsonRpc::new_with_auth(rpc_url, rpc_auth).unwrap()),
|
||||
Arc::new(HttpJsonRpc::new_with_auth(echo_url, echo_auth).unwrap()),
|
||||
)
|
||||
} else {
|
||||
(
|
||||
Arc::new(HttpJsonRpc::new(rpc_url).unwrap()),
|
||||
Arc::new(HttpJsonRpc::new(echo_url).unwrap()),
|
||||
)
|
||||
};
|
||||
|
||||
Self {
|
||||
server,
|
||||
@@ -235,6 +282,22 @@ mod test {
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn assert_auth_failure<R, F, T>(self, request_func: R) -> Self
|
||||
where
|
||||
R: Fn(Arc<HttpJsonRpc>) -> F,
|
||||
F: Future<Output = Result<T, Error>>,
|
||||
T: std::fmt::Debug,
|
||||
{
|
||||
let res = request_func(self.echo_client.clone()).await;
|
||||
if !matches!(res, Err(Error::Auth(_))) {
|
||||
panic!(
|
||||
"No authentication provided, rpc call should have failed.\nResult: {:?}",
|
||||
res
|
||||
)
|
||||
}
|
||||
self
|
||||
}
|
||||
|
||||
pub async fn with_preloaded_responses<R, F>(
|
||||
self,
|
||||
preloaded_responses: Vec<serde_json::Value>,
|
||||
@@ -288,7 +351,7 @@ mod test {
|
||||
"stateRoot": HASH_01,
|
||||
"receiptsRoot": HASH_00,
|
||||
"logsBloom": LOGS_BLOOM_01,
|
||||
"random": HASH_01,
|
||||
"prevRandao": HASH_01,
|
||||
"blockNumber": "0x0",
|
||||
"gasLimit": "0x1",
|
||||
"gasUsed": "0x2",
|
||||
@@ -391,7 +454,7 @@ mod test {
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_block_by_number_request() {
|
||||
Tester::new()
|
||||
Tester::new(true)
|
||||
.assert_request_equals(
|
||||
|client| async move {
|
||||
let _ = client
|
||||
@@ -406,11 +469,19 @@ mod test {
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
Tester::new(false)
|
||||
.assert_auth_failure(|client| async move {
|
||||
client
|
||||
.get_block_by_number(BlockByNumberQuery::Tag(LATEST_TAG))
|
||||
.await
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_block_by_hash_request() {
|
||||
Tester::new()
|
||||
Tester::new(true)
|
||||
.assert_request_equals(
|
||||
|client| async move {
|
||||
let _ = client
|
||||
@@ -425,11 +496,19 @@ mod test {
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
Tester::new(false)
|
||||
.assert_auth_failure(|client| async move {
|
||||
client
|
||||
.get_block_by_hash(ExecutionBlockHash::repeat_byte(1))
|
||||
.await
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn forkchoice_updated_v1_with_payload_attributes_request() {
|
||||
Tester::new()
|
||||
Tester::new(true)
|
||||
.assert_request_equals(
|
||||
|client| async move {
|
||||
let _ = client
|
||||
@@ -441,7 +520,7 @@ mod test {
|
||||
},
|
||||
Some(PayloadAttributes {
|
||||
timestamp: 5,
|
||||
random: Hash256::zero(),
|
||||
prev_randao: Hash256::zero(),
|
||||
suggested_fee_recipient: Address::repeat_byte(0),
|
||||
}),
|
||||
)
|
||||
@@ -458,17 +537,36 @@ mod test {
|
||||
},
|
||||
{
|
||||
"timestamp":"0x5",
|
||||
"random": HASH_00,
|
||||
"prevRandao": HASH_00,
|
||||
"suggestedFeeRecipient": ADDRESS_00
|
||||
}]
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
Tester::new(false)
|
||||
.assert_auth_failure(|client| async move {
|
||||
client
|
||||
.forkchoice_updated_v1(
|
||||
ForkChoiceState {
|
||||
head_block_hash: ExecutionBlockHash::repeat_byte(1),
|
||||
safe_block_hash: ExecutionBlockHash::repeat_byte(1),
|
||||
finalized_block_hash: ExecutionBlockHash::zero(),
|
||||
},
|
||||
Some(PayloadAttributes {
|
||||
timestamp: 5,
|
||||
prev_randao: Hash256::zero(),
|
||||
suggested_fee_recipient: Address::repeat_byte(0),
|
||||
}),
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn get_payload_v1_request() {
|
||||
Tester::new()
|
||||
Tester::new(true)
|
||||
.assert_request_equals(
|
||||
|client| async move {
|
||||
let _ = client.get_payload_v1::<MainnetEthSpec>([42; 8]).await;
|
||||
@@ -481,11 +579,17 @@ mod test {
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
Tester::new(false)
|
||||
.assert_auth_failure(|client| async move {
|
||||
client.get_payload_v1::<MainnetEthSpec>([42; 8]).await
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn new_payload_v1_request() {
|
||||
Tester::new()
|
||||
Tester::new(true)
|
||||
.assert_request_equals(
|
||||
|client| async move {
|
||||
let _ = client
|
||||
@@ -495,7 +599,7 @@ mod test {
|
||||
state_root: Hash256::repeat_byte(1),
|
||||
receipts_root: Hash256::repeat_byte(0),
|
||||
logs_bloom: vec![1; 256].into(),
|
||||
random: Hash256::repeat_byte(1),
|
||||
prev_randao: Hash256::repeat_byte(1),
|
||||
block_number: 0,
|
||||
gas_limit: 1,
|
||||
gas_used: 2,
|
||||
@@ -517,7 +621,7 @@ mod test {
|
||||
"stateRoot": HASH_01,
|
||||
"receiptsRoot": HASH_00,
|
||||
"logsBloom": LOGS_BLOOM_01,
|
||||
"random": HASH_01,
|
||||
"prevRandao": HASH_01,
|
||||
"blockNumber": "0x0",
|
||||
"gasLimit": "0x1",
|
||||
"gasUsed": "0x2",
|
||||
@@ -530,11 +634,34 @@ mod test {
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
Tester::new(false)
|
||||
.assert_auth_failure(|client| async move {
|
||||
client
|
||||
.new_payload_v1::<MainnetEthSpec>(ExecutionPayload {
|
||||
parent_hash: ExecutionBlockHash::repeat_byte(0),
|
||||
fee_recipient: Address::repeat_byte(1),
|
||||
state_root: Hash256::repeat_byte(1),
|
||||
receipts_root: Hash256::repeat_byte(0),
|
||||
logs_bloom: vec![1; 256].into(),
|
||||
prev_randao: Hash256::repeat_byte(1),
|
||||
block_number: 0,
|
||||
gas_limit: 1,
|
||||
gas_used: 2,
|
||||
timestamp: 42,
|
||||
extra_data: vec![].into(),
|
||||
base_fee_per_gas: Uint256::from(1),
|
||||
block_hash: ExecutionBlockHash::repeat_byte(1),
|
||||
transactions: vec![].into(),
|
||||
})
|
||||
.await
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn forkchoice_updated_v1_request() {
|
||||
Tester::new()
|
||||
Tester::new(true)
|
||||
.assert_request_equals(
|
||||
|client| async move {
|
||||
let _ = client
|
||||
@@ -560,6 +687,21 @@ mod test {
|
||||
}),
|
||||
)
|
||||
.await;
|
||||
|
||||
Tester::new(false)
|
||||
.assert_auth_failure(|client| async move {
|
||||
client
|
||||
.forkchoice_updated_v1(
|
||||
ForkChoiceState {
|
||||
head_block_hash: ExecutionBlockHash::repeat_byte(0),
|
||||
safe_block_hash: ExecutionBlockHash::repeat_byte(0),
|
||||
finalized_block_hash: ExecutionBlockHash::repeat_byte(1),
|
||||
},
|
||||
None,
|
||||
)
|
||||
.await
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
fn str_to_payload_id(s: &str) -> PayloadId {
|
||||
@@ -583,7 +725,7 @@ mod test {
|
||||
/// The `id` field has been modified on these vectors to match the one we use.
|
||||
#[tokio::test]
|
||||
async fn geth_test_vectors() {
|
||||
Tester::new()
|
||||
Tester::new(true)
|
||||
.assert_request_equals(
|
||||
// engine_forkchoiceUpdatedV1 (prepare payload) REQUEST validation
|
||||
|client| async move {
|
||||
@@ -596,7 +738,7 @@ mod test {
|
||||
},
|
||||
Some(PayloadAttributes {
|
||||
timestamp: 5,
|
||||
random: Hash256::zero(),
|
||||
prev_randao: Hash256::zero(),
|
||||
suggested_fee_recipient: Address::from_str("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(),
|
||||
})
|
||||
)
|
||||
@@ -613,7 +755,7 @@ mod test {
|
||||
},
|
||||
{
|
||||
"timestamp":"0x5",
|
||||
"random": HASH_00,
|
||||
"prevRandao": HASH_00,
|
||||
"suggestedFeeRecipient":"0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b"
|
||||
}]
|
||||
})
|
||||
@@ -643,7 +785,7 @@ mod test {
|
||||
},
|
||||
Some(PayloadAttributes {
|
||||
timestamp: 5,
|
||||
random: Hash256::zero(),
|
||||
prev_randao: Hash256::zero(),
|
||||
suggested_fee_recipient: Address::from_str("0xa94f5374fce5edbc8e2a8697c15331677e6ebf0b").unwrap(),
|
||||
})
|
||||
)
|
||||
@@ -687,7 +829,7 @@ mod test {
|
||||
"stateRoot":"0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45",
|
||||
"receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"logsBloom": LOGS_BLOOM_00,
|
||||
"random": HASH_00,
|
||||
"prevRandao": HASH_00,
|
||||
"blockNumber":"0x1",
|
||||
"gasLimit":"0x1c95111",
|
||||
"gasUsed":"0x0",
|
||||
@@ -710,7 +852,7 @@ mod test {
|
||||
state_root: Hash256::from_str("0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45").unwrap(),
|
||||
receipts_root: Hash256::from_str("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").unwrap(),
|
||||
logs_bloom: vec![0; 256].into(),
|
||||
random: Hash256::zero(),
|
||||
prev_randao: Hash256::zero(),
|
||||
block_number: 1,
|
||||
gas_limit: u64::from_str_radix("1c95111",16).unwrap(),
|
||||
gas_used: 0,
|
||||
@@ -735,7 +877,7 @@ mod test {
|
||||
state_root: Hash256::from_str("0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45").unwrap(),
|
||||
receipts_root: Hash256::from_str("0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421").unwrap(),
|
||||
logs_bloom: vec![0; 256].into(),
|
||||
random: Hash256::zero(),
|
||||
prev_randao: Hash256::zero(),
|
||||
block_number: 1,
|
||||
gas_limit: u64::from_str_radix("1c9c380",16).unwrap(),
|
||||
gas_used: 0,
|
||||
@@ -757,7 +899,7 @@ mod test {
|
||||
"stateRoot":"0xca3149fa9e37db08d1cd49c9061db1002ef1cd58db2210f2115c8c989b2bdf45",
|
||||
"receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
|
||||
"logsBloom": LOGS_BLOOM_00,
|
||||
"random": HASH_00,
|
||||
"prevRandao": HASH_00,
|
||||
"blockNumber":"0x1",
|
||||
"gasLimit":"0x1c9c380",
|
||||
"gasUsed":"0x0",
|
||||
|
||||
@@ -65,7 +65,7 @@ pub struct JsonExecutionPayloadV1<T: EthSpec> {
|
||||
pub receipts_root: Hash256,
|
||||
#[serde(with = "serde_logs_bloom")]
|
||||
pub logs_bloom: FixedVector<u8, T::BytesPerLogsBloom>,
|
||||
pub random: Hash256,
|
||||
pub prev_randao: Hash256,
|
||||
#[serde(with = "eth2_serde_utils::u64_hex_be")]
|
||||
pub block_number: u64,
|
||||
#[serde(with = "eth2_serde_utils::u64_hex_be")]
|
||||
@@ -92,7 +92,7 @@ impl<T: EthSpec> From<ExecutionPayload<T>> for JsonExecutionPayloadV1<T> {
|
||||
state_root,
|
||||
receipts_root,
|
||||
logs_bloom,
|
||||
random,
|
||||
prev_randao,
|
||||
block_number,
|
||||
gas_limit,
|
||||
gas_used,
|
||||
@@ -109,7 +109,7 @@ impl<T: EthSpec> From<ExecutionPayload<T>> for JsonExecutionPayloadV1<T> {
|
||||
state_root,
|
||||
receipts_root,
|
||||
logs_bloom,
|
||||
random,
|
||||
prev_randao,
|
||||
block_number,
|
||||
gas_limit,
|
||||
gas_used,
|
||||
@@ -131,7 +131,7 @@ impl<T: EthSpec> From<JsonExecutionPayloadV1<T>> for ExecutionPayload<T> {
|
||||
state_root,
|
||||
receipts_root,
|
||||
logs_bloom,
|
||||
random,
|
||||
prev_randao,
|
||||
block_number,
|
||||
gas_limit,
|
||||
gas_used,
|
||||
@@ -148,7 +148,7 @@ impl<T: EthSpec> From<JsonExecutionPayloadV1<T>> for ExecutionPayload<T> {
|
||||
state_root,
|
||||
receipts_root,
|
||||
logs_bloom,
|
||||
random,
|
||||
prev_randao,
|
||||
block_number,
|
||||
gas_limit,
|
||||
gas_used,
|
||||
@@ -166,7 +166,7 @@ impl<T: EthSpec> From<JsonExecutionPayloadV1<T>> for ExecutionPayload<T> {
|
||||
pub struct JsonPayloadAttributesV1 {
|
||||
#[serde(with = "eth2_serde_utils::u64_hex_be")]
|
||||
pub timestamp: u64,
|
||||
pub random: Hash256,
|
||||
pub prev_randao: Hash256,
|
||||
pub suggested_fee_recipient: Address,
|
||||
}
|
||||
|
||||
@@ -175,13 +175,13 @@ impl From<PayloadAttributes> for JsonPayloadAttributesV1 {
|
||||
// Use this verbose deconstruction pattern to ensure no field is left unused.
|
||||
let PayloadAttributes {
|
||||
timestamp,
|
||||
random,
|
||||
prev_randao,
|
||||
suggested_fee_recipient,
|
||||
} = p;
|
||||
|
||||
Self {
|
||||
timestamp,
|
||||
random,
|
||||
prev_randao,
|
||||
suggested_fee_recipient,
|
||||
}
|
||||
}
|
||||
@@ -192,13 +192,13 @@ impl From<JsonPayloadAttributesV1> for PayloadAttributes {
|
||||
// Use this verbose deconstruction pattern to ensure no field is left unused.
|
||||
let JsonPayloadAttributesV1 {
|
||||
timestamp,
|
||||
random,
|
||||
prev_randao,
|
||||
suggested_fee_recipient,
|
||||
} = j;
|
||||
|
||||
Self {
|
||||
timestamp,
|
||||
random,
|
||||
prev_randao,
|
||||
suggested_fee_recipient,
|
||||
}
|
||||
}
|
||||
@@ -364,6 +364,15 @@ impl From<ForkchoiceUpdatedResponse> for JsonForkchoiceUpdatedV1Response {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TransitionConfigurationV1 {
|
||||
pub terminal_total_difficulty: Uint256,
|
||||
pub terminal_block_hash: ExecutionBlockHash,
|
||||
#[serde(with = "eth2_serde_utils::u64_hex_be")]
|
||||
pub terminal_block_number: u64,
|
||||
}
|
||||
|
||||
/// Serializes the `logs_bloom` field of an `ExecutionPayload`.
|
||||
pub mod serde_logs_bloom {
|
||||
use super::*;
|
||||
|
||||
Reference in New Issue
Block a user