Skip to content

Commit 3845875

Browse files
committed
chore: cl_header provider
1 parent 8951156 commit 3845875

File tree

6 files changed

+173
-114
lines changed

6 files changed

+173
-114
lines changed

.github/workflows/prove.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ jobs:
3737
env:
3838
RPC_URL: ${{ secrets.RPC_URL }}
3939
run: |
40-
echo $RPC_URL
4140
cd program
4241
cargo run -r
4342

elf/riscv32im-succinct-zkvm-elf

-38.6 KB
Binary file not shown.
Lines changed: 6 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,12 @@
11
use super::BeaconHeader;
22
use super::ClHeaderMemorizer;
3+
use crate::cl_header::BeaconHeaderClient;
34
use crate::memorizer::values::BeaconHeaderMemorizerValue;
45
use crate::memorizer::values::MemorizerValue;
56
use crate::memorizer::MemorizerError;
67
use crate::memorizer::{keys::BeaconHeaderKey, Memorizer};
7-
use alloy_primitives::hex;
8-
use ssz_rs::Vector;
98
use tokio::runtime::Runtime;
109

11-
use regex::Regex;
12-
use reqwest::Client;
13-
use scraper::{Html, Selector};
14-
use serde::Deserialize;
15-
1610
const SEPOLIA_POS_TRANSITION_BLOCK_NUMBER: u64 = 1450409;
1711
const MAINNET_POS_TRANSITION_BLOCK_NUMBER: u64 = 15537393;
1812

@@ -35,92 +29,15 @@ impl ClHeaderMemorizer for Memorizer {
3529
}
3630
}
3731

32+
let rpc_url = self.rpc_url.clone().unwrap();
33+
3834
let rt = Runtime::new().unwrap();
3935
let header: BeaconHeader = rt.block_on(async {
40-
let client = Client::new();
41-
42-
let etherscan_url = format!(
43-
"https://{}etherscan.io/block/{}#consensusinfo",
44-
if key.chain_id == 11155111 {
45-
"sepolia."
46-
} else {
47-
""
48-
},
49-
key.block_number
50-
);
51-
52-
let response = reqwest::get(etherscan_url)
36+
let client = BeaconHeaderClient::default();
37+
client
38+
.get_cl_header(rpc_url.to_string(), &key)
5339
.await
5440
.unwrap()
55-
.text()
56-
.await
57-
.unwrap();
58-
59-
let document = Html::parse_document(&response);
60-
61-
// Define a selector to target the div containing "Block proposed on slot"
62-
let selector = Selector::parse("#ContentPlaceHolder1_divhSlotEpoch .col-md-9").unwrap();
63-
64-
let slot_text = document
65-
.select(&selector)
66-
.next()
67-
.unwrap()
68-
.text()
69-
.collect::<Vec<_>>()
70-
.concat();
71-
72-
let slot = Regex::new(r"slot (\d+),")
73-
.unwrap()
74-
.captures(slot_text.as_str())
75-
.unwrap()
76-
.get(1)
77-
.unwrap()
78-
.as_str();
79-
let slot = slot.parse::<u64>().unwrap();
80-
81-
// if let Some(element) = document.select(&selector).next() {
82-
// let slot_text = element.text().collect::<Vec<_>>().concat();
83-
// println!("Block proposed on slot: {}", slot_text);
84-
// } else {
85-
// println!("Could not find the block slot information.");
86-
// }
87-
88-
// let slot = key.block_number / 32; // TODO fix this
89-
90-
let url = format!(
91-
"{}/eth/v1/beacon/headers?slot={}",
92-
self.rpc_url.clone().unwrap(),
93-
slot
94-
);
95-
96-
// Sending GET request to the specified URL
97-
let response = client
98-
.get(&url)
99-
.header("accept", "application/json")
100-
.send()
101-
.await
102-
.expect("Failed to send request")
103-
.json::<BeaconHeaderApiResponse>()
104-
.await
105-
.expect("Failed to parse response");
106-
107-
// Extracting the first header in the `data` array
108-
let header_data = &response.data[0].header.message;
109-
110-
let parent_root =
111-
Vector::<u8, 32>::try_from(hex::decode(&header_data.parent_root).unwrap()).unwrap();
112-
let state_root =
113-
Vector::<u8, 32>::try_from(hex::decode(&header_data.state_root).unwrap()).unwrap();
114-
let body_root =
115-
Vector::<u8, 32>::try_from(hex::decode(&header_data.body_root).unwrap()).unwrap();
116-
// Converting response data to BeaconHeader struct
117-
BeaconHeader {
118-
slot: header_data.slot.parse().unwrap(),
119-
proposer_index: header_data.proposer_index.parse().unwrap(),
120-
parent_root,
121-
state_root,
122-
body_root,
123-
}
12441
});
12542

12643
self.map.insert(
@@ -133,27 +50,3 @@ impl ClHeaderMemorizer for Memorizer {
13350
Ok(header)
13451
}
13552
}
136-
137-
#[derive(Deserialize)]
138-
struct BeaconHeaderApiResponse {
139-
data: Vec<BeaconHeaderData>,
140-
}
141-
142-
#[derive(Deserialize)]
143-
struct BeaconHeaderData {
144-
header: BeaconHeaderDetail,
145-
}
146-
147-
#[derive(Deserialize)]
148-
struct BeaconHeaderDetail {
149-
message: BeaconHeaderMessage,
150-
}
151-
152-
#[derive(Deserialize)]
153-
struct BeaconHeaderMessage {
154-
slot: String,
155-
proposer_index: String,
156-
parent_root: String,
157-
state_root: String,
158-
body_root: String,
159-
}

lib/src/provider/cl_header.rs

Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
use alloy_primitives::hex;
2+
use regex::Regex;
3+
use scraper::{Html, Selector};
4+
use serde::Deserialize;
5+
use ssz_rs::Vector;
6+
7+
use crate::memorizer::{BeaconHeader, BeaconHeaderKey};
8+
9+
#[derive(Deserialize, Debug)]
10+
struct BeaconHeaderApiResponse {
11+
data: Vec<BeaconHeaderData>,
12+
}
13+
14+
#[derive(Deserialize, Debug)]
15+
struct BeaconHeaderData {
16+
header: BeaconHeaderDetail,
17+
}
18+
19+
#[derive(Deserialize, Debug)]
20+
struct BeaconHeaderDetail {
21+
message: BeaconHeaderMessage,
22+
}
23+
24+
#[derive(Deserialize, Debug)]
25+
struct BeaconHeaderMessage {
26+
slot: String,
27+
proposer_index: String,
28+
parent_root: String,
29+
state_root: String,
30+
body_root: String,
31+
}
32+
33+
pub struct BeaconHeaderClient {
34+
pub client: reqwest::Client,
35+
}
36+
37+
impl Default for BeaconHeaderClient {
38+
fn default() -> Self {
39+
Self::new()
40+
}
41+
}
42+
43+
impl BeaconHeaderClient {
44+
pub fn new() -> Self {
45+
let client = reqwest::Client::new();
46+
Self { client }
47+
}
48+
49+
pub async fn get_cl_header(
50+
&self,
51+
rpc_url: String,
52+
key: &BeaconHeaderKey,
53+
) -> Result<BeaconHeader, reqwest::Error> {
54+
let etherscan_url = format!(
55+
"https://{}etherscan.io/block/{}#consensusinfo",
56+
if key.chain_id == 11155111 {
57+
"sepolia."
58+
} else {
59+
""
60+
},
61+
key.block_number
62+
);
63+
64+
let response = reqwest::get(etherscan_url)
65+
.await
66+
.unwrap()
67+
.text()
68+
.await
69+
.unwrap();
70+
71+
let document = Html::parse_document(&response);
72+
73+
// Define a selector to target the div containing "Block proposed on slot"
74+
let selector = Selector::parse("#ContentPlaceHolder1_divhSlotEpoch .col-md-9").unwrap();
75+
76+
let slot_text = document
77+
.select(&selector)
78+
.next()
79+
.unwrap()
80+
.text()
81+
.collect::<Vec<_>>()
82+
.concat();
83+
84+
let slot = Regex::new(r"slot (\d+),")
85+
.unwrap()
86+
.captures(slot_text.as_str())
87+
.unwrap()
88+
.get(1)
89+
.unwrap()
90+
.as_str();
91+
let slot = slot.parse::<u64>().unwrap();
92+
93+
println!("slot: {}", slot);
94+
95+
// if let Some(element) = document.select(&selector).next() {
96+
// let slot_text = element.text().collect::<Vec<_>>().concat();
97+
// println!("Block proposed on slot: {}", slot_text);
98+
// } else {
99+
// println!("Could not find the block slot information.");
100+
// }
101+
102+
// let slot = key.block_number / 32; // TODO fix this
103+
104+
let url = format!("{}/eth/v1/beacon/headers?slot={}", rpc_url.clone(), slot);
105+
println!("url: {}", url);
106+
107+
// Sending GET request to the specified URL
108+
let response = self
109+
.client
110+
.get(&url)
111+
.header("accept", "application/json")
112+
.send()
113+
.await
114+
.expect("Failed to send request")
115+
.json::<BeaconHeaderApiResponse>()
116+
.await
117+
.expect("Failed to parse response");
118+
119+
println!("response: {:?}", response.data);
120+
121+
// Extracting the first header in the `data` array
122+
let header_data = &response.data[0].header.message;
123+
124+
let parent_root =
125+
Vector::<u8, 32>::try_from(hex::decode(&header_data.parent_root).unwrap()).unwrap();
126+
let state_root =
127+
Vector::<u8, 32>::try_from(hex::decode(&header_data.state_root).unwrap()).unwrap();
128+
let body_root =
129+
Vector::<u8, 32>::try_from(hex::decode(&header_data.body_root).unwrap()).unwrap();
130+
// Converting response data to BeaconHeader struct
131+
Ok(BeaconHeader {
132+
slot: header_data.slot.parse().unwrap(),
133+
proposer_index: header_data.proposer_index.parse().unwrap(),
134+
parent_root,
135+
state_root,
136+
body_root,
137+
})
138+
}
139+
}
140+
141+
#[cfg(test)]
142+
mod tests {
143+
use super::*;
144+
145+
#[tokio::test]
146+
#[ignore = "CL support public rpc"]
147+
async fn test_get_cl_header() {
148+
let beacon_header_client = BeaconHeaderClient::default();
149+
150+
let key = BeaconHeaderKey {
151+
chain_id: 11155111,
152+
block_number: 5244652,
153+
};
154+
155+
// TODO: need public rpc url for beacon chain https://ethereum-sepolia-beacon-api.publicnode.com/eth/v1/beacon/headers?slot=4304885
156+
let res = beacon_header_client
157+
.get_cl_header(
158+
"https://ethereum-sepolia-beacon-api.publicnode.com".to_string(),
159+
&key,
160+
)
161+
.await
162+
.unwrap();
163+
println!("{:?}", res);
164+
}
165+
}

lib/src/provider/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
pub mod cl_header;
12
pub mod header;
23
pub mod transaction;

program/src/main.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ pub fn main() {
2424
};
2525
let _ = memorizer.get_header(header_key_plus_one).unwrap();
2626

27+
// TODO: to use CL header, provide RPC that support beacon header
2728
// let cl_header_key = BeaconHeaderKey {
2829
// block_number,
2930
// chain_id: 11155111,

0 commit comments

Comments
 (0)