refactor into multiple modules
This commit is contained in:
parent
0c715d29a8
commit
37749df08a
|
@ -0,0 +1,149 @@
|
|||
use eyre::Result;
|
||||
use ssz_rs::prelude::*;
|
||||
use super::utils::*;
|
||||
use serde::de::Error;
|
||||
|
||||
pub struct ConsensusClient {
|
||||
rpc: String,
|
||||
}
|
||||
|
||||
impl ConsensusClient {
|
||||
pub fn new(rpc: &str) -> Self {
|
||||
ConsensusClient { rpc: rpc.to_string() }
|
||||
}
|
||||
|
||||
pub async fn get_bootstrap(&self, block_root: &str) -> Result<Bootstrap> {
|
||||
let req = format!("{}/eth/v0/beacon/light_client/bootstrap/{}", self.rpc, block_root);
|
||||
let res = reqwest::get(req).await?.json::<BootstrapResponse>().await?;
|
||||
Ok(res.data.v)
|
||||
}
|
||||
|
||||
pub async fn get_updates(&self, period: u64) -> Result<Vec<Update>> {
|
||||
let req = format!("{}/eth/v0/beacon/light_client/updates?start_period={}&count=1000", self.rpc, period);
|
||||
let res = reqwest::get(req).await?.json::<UpdateResponse>().await?;
|
||||
Ok(res.data)
|
||||
}
|
||||
|
||||
pub async fn get_finality_update(&self) -> Result<FinalityUpdate> {
|
||||
let req = format!("{}/eth/v0/beacon/light_client/finality_update", self.rpc);
|
||||
let res = reqwest::get(req).await?.json::<FinalityUpdateResponse>().await?;
|
||||
Ok(res.data)
|
||||
}
|
||||
}
|
||||
|
||||
pub type BLSPubKey = Vector<u8, 48>;
|
||||
pub type Bytes32 = Vector<u8, 32>;
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
pub struct Bootstrap {
|
||||
pub header: Header,
|
||||
pub current_sync_committee: SyncCommittee,
|
||||
#[serde(deserialize_with = "branch_deserialize")]
|
||||
pub current_sync_committee_branch: Vec<Bytes32>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, Clone)]
|
||||
pub struct Update {
|
||||
pub attested_header: Header,
|
||||
pub next_sync_committee: Option<SyncCommittee>,
|
||||
#[serde(deserialize_with = "branch_deserialize")]
|
||||
pub next_sync_committee_branch: Vec<Bytes32>,
|
||||
pub finalized_header: Header,
|
||||
#[serde(deserialize_with = "branch_deserialize")]
|
||||
pub finality_branch: Vec<Bytes32>,
|
||||
pub sync_aggregate: SyncAggregate,
|
||||
#[serde(deserialize_with = "u64_deserialize")]
|
||||
pub signature_slot: u64,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
pub struct FinalityUpdate {
|
||||
pub attested_header: Header,
|
||||
pub finalized_header: Header,
|
||||
#[serde(deserialize_with = "branch_deserialize")]
|
||||
pub finality_branch: Vec<Bytes32>,
|
||||
pub sync_aggregate: SyncAggregate,
|
||||
#[serde(deserialize_with = "u64_deserialize")]
|
||||
pub signature_slot: u64,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, Clone, Default, SimpleSerialize)]
|
||||
pub struct Header {
|
||||
#[serde(deserialize_with = "u64_deserialize")]
|
||||
pub slot: u64,
|
||||
#[serde(deserialize_with = "u64_deserialize")]
|
||||
pub proposer_index: u64,
|
||||
#[serde(deserialize_with = "bytes32_deserialize")]
|
||||
pub parent_root: Bytes32,
|
||||
#[serde(deserialize_with = "bytes32_deserialize")]
|
||||
pub state_root: Bytes32,
|
||||
#[serde(deserialize_with = "bytes32_deserialize")]
|
||||
pub body_root: Bytes32,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, SimpleSerialize, serde::Deserialize)]
|
||||
pub struct SyncCommittee {
|
||||
#[serde(deserialize_with = "pubkeys_deserialize")]
|
||||
pub pubkeys: Vector<BLSPubKey, 512>,
|
||||
#[serde(deserialize_with = "pubkey_deserialize")]
|
||||
pub aggregate_pubkey: BLSPubKey,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, Clone)]
|
||||
pub struct SyncAggregate {
|
||||
pub sync_committee_bits: String,
|
||||
pub sync_committee_signature: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
struct UpdateResponse {
|
||||
data: Vec<Update>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
struct FinalityUpdateResponse {
|
||||
data: FinalityUpdate,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
struct BootstrapResponse {
|
||||
data: BootstrapData,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
struct BootstrapData {
|
||||
v: Bootstrap,
|
||||
}
|
||||
|
||||
fn pubkey_deserialize<'de, D>(deserializer: D) -> Result<BLSPubKey, D::Error> where D: serde::Deserializer<'de> {
|
||||
let key: String = serde::Deserialize::deserialize(deserializer)?;
|
||||
let key_bytes = hex_str_to_bytes(&key).map_err(D::Error::custom)?;
|
||||
Ok(Vector::from_iter(key_bytes))
|
||||
}
|
||||
|
||||
fn pubkeys_deserialize<'de, D>(deserializer: D) -> Result<Vector<BLSPubKey, 512>, D::Error> where D: serde::Deserializer<'de> {
|
||||
let keys: Vec<String> = serde::Deserialize::deserialize(deserializer)?;
|
||||
Ok(keys.iter().map(|key| {
|
||||
let key_bytes = hex_str_to_bytes(key)?;
|
||||
Ok(Vector::from_iter(key_bytes))
|
||||
}).collect::<Result<Vector<BLSPubKey, 512>>>().map_err(D::Error::custom)?)
|
||||
}
|
||||
|
||||
fn branch_deserialize<'de, D>(deserializer: D) -> Result<Vec<Bytes32>, D::Error> where D: serde::Deserializer<'de> {
|
||||
let branch: Vec<String> = serde::Deserialize::deserialize(deserializer)?;
|
||||
Ok(branch.iter().map(|elem| {
|
||||
let elem_bytes = hex_str_to_bytes(elem)?;
|
||||
Ok(Vector::from_iter(elem_bytes))
|
||||
}).collect::<Result<_>>().map_err(D::Error::custom)?)
|
||||
}
|
||||
|
||||
fn u64_deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error> where D: serde::Deserializer<'de> {
|
||||
let val: String = serde::Deserialize::deserialize(deserializer)?;
|
||||
Ok(val.parse().unwrap())
|
||||
}
|
||||
|
||||
fn bytes32_deserialize<'de, D>(deserializer: D) -> Result<Bytes32, D::Error> where D: serde::Deserializer<'de> {
|
||||
let bytes: String = serde::Deserialize::deserialize(deserializer)?;
|
||||
let bytes = hex::decode(bytes.strip_prefix("0x").unwrap()).unwrap();
|
||||
Ok(bytes.to_vec().try_into().unwrap())
|
||||
}
|
356
src/main.rs
356
src/main.rs
|
@ -1,6 +1,11 @@
|
|||
use eyre::Result;
|
||||
use ssz_rs::prelude::*;
|
||||
use blst::{min_pk::*, BLST_ERROR};
|
||||
use consensus_client::*;
|
||||
use utils::*;
|
||||
|
||||
pub mod consensus_client;
|
||||
pub mod utils;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<()> {
|
||||
|
@ -16,7 +21,7 @@ async fn main() -> Result<()> {
|
|||
}
|
||||
|
||||
struct LightClient {
|
||||
nimbus_rpc: String,
|
||||
consensus_client: ConsensusClient,
|
||||
store: Store,
|
||||
}
|
||||
|
||||
|
@ -30,18 +35,18 @@ struct Store {
|
|||
impl LightClient {
|
||||
async fn new(nimbus_rpc: &str, checkpoint_block_root: &str) -> Result<LightClient> {
|
||||
|
||||
let mut bootstrap = Self::get_bootstrap(nimbus_rpc, checkpoint_block_root).await?;
|
||||
let consensus_client = ConsensusClient::new(nimbus_rpc);
|
||||
|
||||
let committee_hash = bootstrap.current_sync_committee.hash_tree_root()?;
|
||||
let root = Node::from_bytes(bootstrap.header.state_root);
|
||||
let committee_branch = branch_to_nodes(bootstrap.current_sync_committee_branch);
|
||||
let mut bootstrap = consensus_client.get_bootstrap(checkpoint_block_root).await?;
|
||||
|
||||
let committee_valid = is_valid_merkle_branch(&committee_hash, committee_branch.iter(), 5, 22, &root);
|
||||
println!("bootstrap committee valid: {}", committee_valid);
|
||||
let committee_valid = is_current_committee_proof_valid(
|
||||
&bootstrap.header,
|
||||
&mut bootstrap.current_sync_committee,
|
||||
&bootstrap.current_sync_committee_branch
|
||||
);
|
||||
|
||||
let header_hash = bootstrap.header.hash_tree_root()?;
|
||||
let header_valid = header_hash.to_string() == checkpoint_block_root.to_string();
|
||||
println!("bootstrap header valid: {}", header_valid);
|
||||
|
||||
if !(header_valid && committee_valid) {
|
||||
return Err(eyre::eyre!("Invalid Bootstrap"));
|
||||
|
@ -53,23 +58,20 @@ impl LightClient {
|
|||
next_sync_committee: None,
|
||||
};
|
||||
|
||||
Ok(LightClient { nimbus_rpc: nimbus_rpc.to_string(), store })
|
||||
Ok(LightClient { consensus_client, store })
|
||||
}
|
||||
|
||||
async fn sync(&mut self) -> Result<()> {
|
||||
|
||||
let current_period = calc_sync_period(self.store.header.slot);
|
||||
let next_period = current_period + 0;
|
||||
|
||||
let updates = self.get_updates(next_period).await?;
|
||||
let updates = self.consensus_client.get_updates(current_period).await?;
|
||||
|
||||
for mut update in updates {
|
||||
self.verify_update(&mut update)?;
|
||||
self.apply_update(&update);
|
||||
println!("================================");
|
||||
}
|
||||
|
||||
let finality_update = self.get_finality_update().await?;
|
||||
let finality_update = self.consensus_client.get_finality_update().await?;
|
||||
let mut finality_update_generic = Update {
|
||||
attested_header: finality_update.attested_header,
|
||||
next_sync_committee: None,
|
||||
|
@ -95,9 +97,6 @@ impl LightClient {
|
|||
let current_period = calc_sync_period(current_slot);
|
||||
let update_period = calc_sync_period(update_slot);
|
||||
|
||||
println!("current period: {}", current_period);
|
||||
println!("update period: {}", update_period);
|
||||
|
||||
if !(update_period == current_period + 1 || update_period == current_period) {
|
||||
return Err(eyre::eyre!("Invalid Update"));
|
||||
}
|
||||
|
@ -106,67 +105,46 @@ impl LightClient {
|
|||
return Err(eyre::eyre!("Invalid Update"));
|
||||
}
|
||||
|
||||
let finality_header_hash = update.finalized_header.hash_tree_root()?;
|
||||
let update_header_root = Node::from_bytes(update.attested_header.state_root);
|
||||
let finality_branch = branch_to_nodes(update.finality_branch.clone());
|
||||
let finality_branch_valid = is_valid_merkle_branch(&finality_header_hash, finality_branch.iter(), 6, 41, &update_header_root);
|
||||
println!("finality branch valid: {}", finality_branch_valid);
|
||||
let finality_branch_valid = is_finality_proof_valid(
|
||||
&update.attested_header,
|
||||
&mut update.finalized_header,
|
||||
&update.finality_branch
|
||||
);
|
||||
|
||||
if !(finality_branch_valid) {
|
||||
return Err(eyre::eyre!("Invalid Update"));
|
||||
}
|
||||
|
||||
if update.next_sync_committee.is_some() {
|
||||
let next_committee_hash = update.next_sync_committee.as_ref().unwrap().clone().hash_tree_root()?;
|
||||
let next_committee_branch = branch_to_nodes(update.next_sync_committee_branch.clone());
|
||||
let next_committee_branch_valid = is_valid_merkle_branch(&next_committee_hash, next_committee_branch.iter(), 5, 23, &update_header_root);
|
||||
println!("next sync committee branch valid: {}", next_committee_branch_valid);
|
||||
let next_committee_branch_valid = is_next_committee_proof_valid(
|
||||
&update.attested_header,
|
||||
&mut update.next_sync_committee.clone().unwrap(),
|
||||
&update.next_sync_committee_branch
|
||||
);
|
||||
|
||||
if !next_committee_branch_valid {
|
||||
return Err(eyre::eyre!("Invalid Update"));
|
||||
}
|
||||
}
|
||||
|
||||
if !(finality_branch_valid) {
|
||||
return Err(eyre::eyre!("Invalid Update"));
|
||||
}
|
||||
|
||||
let sync_committee = if current_period == update_period {
|
||||
&self.store.current_sync_committee
|
||||
} else {
|
||||
self.store.next_sync_committee.as_ref().unwrap()
|
||||
};
|
||||
|
||||
let bytes = hex::decode(update.sync_aggregate.sync_committee_bits.strip_prefix("0x").unwrap())?;
|
||||
let mut bits = String::new();
|
||||
let mut count = 0;
|
||||
for byte in bytes {
|
||||
let byte_str = format!("{:08b}", byte);
|
||||
byte_str.chars().for_each(|b| if b == '1' { count += 1 });
|
||||
bits.push_str(&byte_str.chars().rev().collect::<String>());
|
||||
}
|
||||
|
||||
let mut pks: Vec<PublicKey> = Vec::new();
|
||||
bits.chars().enumerate().for_each(|(i, bit)| {
|
||||
if bit == '1' {
|
||||
let pk = sync_committee.pubkeys[i].clone();
|
||||
let pk = PublicKey::from_bytes(&pk).unwrap();
|
||||
pks.push(pk)
|
||||
}
|
||||
});
|
||||
let pks = get_participating_keys(sync_committee, &update.sync_aggregate.sync_committee_bits)?;
|
||||
let pks: Vec<&PublicKey> = pks.iter().map(|pk| pk).collect();
|
||||
|
||||
let committee_quorum = count as f64 > 2.0 / 3.0 * 512.0;
|
||||
println!("sync committee quorum: {}", committee_quorum);
|
||||
|
||||
let committee_quorum = pks.len() as f64 > 2.0 / 3.0 * 512.0;
|
||||
if !committee_quorum {
|
||||
return Err(eyre::eyre!("Invalid Update"));
|
||||
}
|
||||
|
||||
let header_root = bytes_to_bytes32(update.attested_header.hash_tree_root()?.as_bytes());
|
||||
let signing_root = compute_committee_sign_root(header_root)?;
|
||||
|
||||
let sig_bytes = hex::decode(update.sync_aggregate.sync_committee_signature.strip_prefix("0x").unwrap())?;
|
||||
let sig = Signature::from_bytes(&sig_bytes).unwrap();
|
||||
let dst: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
|
||||
let is_valid_sig = sig.fast_aggregate_verify(true, signing_root.as_bytes(), dst, &pks) == BLST_ERROR::BLST_SUCCESS;
|
||||
println!("committee signature valid: {}", is_valid_sig);
|
||||
let sig_bytes = hex_str_to_bytes(&update.sync_aggregate.sync_committee_signature)?;
|
||||
let is_valid_sig = is_aggregate_valid(sig_bytes, signing_root.as_bytes(), &pks);
|
||||
|
||||
if !is_valid_sig {
|
||||
return Err(eyre::eyre!("Invalid Update"));
|
||||
|
@ -189,37 +167,124 @@ impl LightClient {
|
|||
self.store.next_sync_committee = Some(update.next_sync_committee.as_ref().unwrap().clone());
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_bootstrap(rpc: &str, block_root: &str) -> Result<Bootstrap> {
|
||||
let req = format!("{}/eth/v0/beacon/light_client/bootstrap/{}", rpc, block_root);
|
||||
let res = reqwest::get(req).await?.json::<BootstrapResponse>().await?;
|
||||
Ok(res.data.v)
|
||||
}
|
||||
|
||||
async fn get_updates(&self, period: u64) -> Result<Vec<Update>> {
|
||||
let req = format!("{}/eth/v0/beacon/light_client/updates?start_period={}&count=1000", self.nimbus_rpc, period);
|
||||
let res = reqwest::get(req).await?.json::<UpdateResponse>().await?;
|
||||
Ok(res.data)
|
||||
fn get_participating_keys(committee: &SyncCommittee, bitfield: &str) -> Result<Vec<PublicKey>> {
|
||||
let bytes = hex_str_to_bytes(bitfield)?;
|
||||
let mut pks: Vec<PublicKey> = Vec::new();
|
||||
bytes.iter().enumerate().for_each(|(i, byte)| {
|
||||
format!("{:08b}", byte).chars().rev().enumerate().for_each(|(j, bit)| {
|
||||
if bit == '1' {
|
||||
let index = 8 * i + j;
|
||||
let pk = &committee.pubkeys[index];
|
||||
let pk = PublicKey::from_bytes(&pk).unwrap();
|
||||
pks.push(pk);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Ok(pks)
|
||||
}
|
||||
|
||||
async fn get_finality_update(&self) -> Result<FinalityUpdate> {
|
||||
let req = format!("{}/eth/v0/beacon/light_client/finality_update", self.nimbus_rpc);
|
||||
let res = reqwest::get(req).await?.json::<FinalityUpdateResponse>().await?;
|
||||
Ok(res.data)
|
||||
fn is_aggregate_valid(sig_bytes: Vec<u8>, msg: &[u8], pks: &[&PublicKey]) -> bool {
|
||||
let dst: &[u8] = b"BLS_SIG_BLS12381G2_XMD:SHA-256_SSWU_RO_POP_";
|
||||
let sig_res = Signature::from_bytes(&sig_bytes);
|
||||
match sig_res {
|
||||
Ok(sig) => sig.fast_aggregate_verify(true, msg, dst, &pks) == BLST_ERROR::BLST_SUCCESS,
|
||||
Err(_) => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn is_finality_proof_valid(attested_header: &Header, finality_header: &mut Header, finality_branch: &Vec<Bytes32>) -> bool {
|
||||
let finality_header_hash_res = finality_header.hash_tree_root();
|
||||
if finality_header_hash_res.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let attested_header_state_root_res = bytes32_to_node(&attested_header.state_root);
|
||||
if attested_header_state_root_res.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let finality_branch_res = branch_to_nodes(finality_branch.clone());
|
||||
if finality_branch_res.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
is_valid_merkle_branch(
|
||||
&finality_header_hash_res.unwrap(),
|
||||
finality_branch_res.unwrap().iter(),
|
||||
6,
|
||||
41,
|
||||
&attested_header_state_root_res.unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
fn is_next_committee_proof_valid(
|
||||
attested_header: &Header,
|
||||
next_committee: &mut SyncCommittee,
|
||||
next_committee_branch: &Vec<Bytes32>
|
||||
) -> bool {
|
||||
let next_committee_hash_res = next_committee.hash_tree_root();
|
||||
if next_committee_hash_res.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let attested_header_state_root_res = bytes32_to_node(&attested_header.state_root);
|
||||
if attested_header_state_root_res.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let next_committee_branch_res = branch_to_nodes(next_committee_branch.clone());
|
||||
if next_committee_branch_res.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
is_valid_merkle_branch(
|
||||
&next_committee_hash_res.unwrap(),
|
||||
next_committee_branch_res.unwrap().iter(),
|
||||
5,
|
||||
23,
|
||||
&attested_header_state_root_res.unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
fn is_current_committee_proof_valid(
|
||||
attested_header: &Header,
|
||||
current_committee: &mut SyncCommittee,
|
||||
current_committee_branch: &Vec<Bytes32>
|
||||
) -> bool {
|
||||
let next_committee_hash_res = current_committee.hash_tree_root();
|
||||
if next_committee_hash_res.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let attested_header_state_root_res = bytes32_to_node(&attested_header.state_root);
|
||||
if attested_header_state_root_res.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
let next_committee_branch_res = branch_to_nodes(current_committee_branch.clone());
|
||||
if next_committee_branch_res.is_err() {
|
||||
return false;
|
||||
}
|
||||
|
||||
is_valid_merkle_branch(
|
||||
&next_committee_hash_res.unwrap(),
|
||||
next_committee_branch_res.unwrap().iter(),
|
||||
5,
|
||||
22,
|
||||
&attested_header_state_root_res.unwrap()
|
||||
)
|
||||
}
|
||||
|
||||
fn calc_sync_period(slot: u64) -> u64 {
|
||||
let epoch = slot / 32;
|
||||
epoch / 256
|
||||
}
|
||||
|
||||
fn branch_to_nodes(branch: Vec<Bytes32>) -> Vec<Node> {
|
||||
branch.iter().map(|elem| Node::from_bytes(*elem)).collect()
|
||||
}
|
||||
|
||||
fn bytes_to_bytes32(bytes: &[u8]) -> [u8; 32] {
|
||||
bytes.to_vec().try_into().unwrap()
|
||||
fn branch_to_nodes(branch: Vec<Bytes32>) -> Result<Vec<Node>> {
|
||||
branch.iter().map(|elem| bytes32_to_node(elem)).collect::<Result<Vec<Node>>>()
|
||||
}
|
||||
|
||||
fn compute_committee_sign_root(header: Bytes32) -> Result<Node> {
|
||||
|
@ -230,6 +295,18 @@ fn compute_committee_sign_root(header: Bytes32) -> Result<Node> {
|
|||
compute_signing_root(header, domain)
|
||||
}
|
||||
|
||||
#[derive(SimpleSerialize, Default, Debug)]
|
||||
struct SigningData {
|
||||
object_root: Bytes32,
|
||||
domain: Bytes32
|
||||
}
|
||||
|
||||
#[derive(SimpleSerialize, Default, Debug)]
|
||||
struct ForkData {
|
||||
current_version: Vector<u8, 4>,
|
||||
genesis_validator_root: Bytes32,
|
||||
}
|
||||
|
||||
fn compute_signing_root(object_root: Bytes32, domain: Bytes32) -> Result<Node> {
|
||||
let mut data = SigningData { object_root, domain };
|
||||
Ok(data.hash_tree_root()?)
|
||||
|
@ -249,132 +326,3 @@ fn compute_fork_data_root(current_version: Vector<u8, 4>, genesis_validator_root
|
|||
Ok(fork_data.hash_tree_root()?)
|
||||
}
|
||||
|
||||
#[derive(SimpleSerialize, Default, Debug)]
|
||||
struct ForkData {
|
||||
current_version: Vector<u8, 4>,
|
||||
genesis_validator_root: Bytes32,
|
||||
}
|
||||
|
||||
#[derive(SimpleSerialize, Default, Debug)]
|
||||
struct SigningData {
|
||||
object_root: Bytes32,
|
||||
domain: Bytes32
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
struct BootstrapResponse {
|
||||
data: BootstrapData,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
struct BootstrapData {
|
||||
v: Bootstrap,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
struct Bootstrap {
|
||||
header: Header,
|
||||
current_sync_committee: SyncCommittee,
|
||||
#[serde(deserialize_with = "branch_deserialize")]
|
||||
current_sync_committee_branch: Vec<Bytes32>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Default, SimpleSerialize, serde::Deserialize)]
|
||||
struct SyncCommittee {
|
||||
#[serde(deserialize_with = "pubkeys_deserialize")]
|
||||
pubkeys: Vector<BLSPubKey, 512>,
|
||||
#[serde(deserialize_with = "pubkey_deserialize")]
|
||||
aggregate_pubkey: BLSPubKey,
|
||||
}
|
||||
|
||||
type BLSPubKey = Vector<u8, 48>;
|
||||
type Bytes32 = [u8; 32];
|
||||
|
||||
fn pubkey_deserialize<'de, D>(deserializer: D) -> Result<BLSPubKey, D::Error> where D: serde::Deserializer<'de> {
|
||||
let key: String = serde::Deserialize::deserialize(deserializer)?;
|
||||
let key_bytes = hex::decode(key.strip_prefix("0x").unwrap()).unwrap();
|
||||
Ok(Vector::from_iter(key_bytes))
|
||||
}
|
||||
|
||||
fn pubkeys_deserialize<'de, D>(deserializer: D) -> Result<Vector<BLSPubKey, 512>, D::Error> where D: serde::Deserializer<'de> {
|
||||
let keys: Vec<String> = serde::Deserialize::deserialize(deserializer)?;
|
||||
Ok(keys.iter().map(|key| {
|
||||
let key_bytes = hex::decode(key.strip_prefix("0x").unwrap()).unwrap();
|
||||
Vector::from_iter(key_bytes)
|
||||
}).collect::<Vector<BLSPubKey, 512>>())
|
||||
}
|
||||
|
||||
fn branch_deserialize<'de, D>(deserializer: D) -> Result<Vec<Bytes32>, D::Error> where D: serde::Deserializer<'de> {
|
||||
let branch: Vec<String> = serde::Deserialize::deserialize(deserializer)?;
|
||||
Ok(branch.iter().map(|elem| {
|
||||
let bytes = hex::decode(elem.strip_prefix("0x").unwrap()).unwrap();
|
||||
bytes.to_vec().try_into().unwrap()
|
||||
}).collect())
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, Clone, Default, SimpleSerialize)]
|
||||
struct Header {
|
||||
#[serde(deserialize_with = "u64_deserialize")]
|
||||
slot: u64,
|
||||
#[serde(deserialize_with = "u64_deserialize")]
|
||||
proposer_index: u64,
|
||||
#[serde(deserialize_with = "bytes32_deserialize")]
|
||||
parent_root: Bytes32,
|
||||
#[serde(deserialize_with = "bytes32_deserialize")]
|
||||
state_root: Bytes32,
|
||||
#[serde(deserialize_with = "bytes32_deserialize")]
|
||||
body_root: Bytes32,
|
||||
}
|
||||
|
||||
fn u64_deserialize<'de, D>(deserializer: D) -> Result<u64, D::Error> where D: serde::Deserializer<'de> {
|
||||
let val: String = serde::Deserialize::deserialize(deserializer)?;
|
||||
Ok(val.parse().unwrap())
|
||||
}
|
||||
|
||||
fn bytes32_deserialize<'de, D>(deserializer: D) -> Result<Bytes32, D::Error> where D: serde::Deserializer<'de> {
|
||||
let bytes: String = serde::Deserialize::deserialize(deserializer)?;
|
||||
let bytes = hex::decode(bytes.strip_prefix("0x").unwrap()).unwrap();
|
||||
Ok(bytes.to_vec().try_into().unwrap())
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
struct UpdateResponse {
|
||||
data: Vec<Update>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, Clone)]
|
||||
struct Update {
|
||||
attested_header: Header,
|
||||
next_sync_committee: Option<SyncCommittee>,
|
||||
#[serde(deserialize_with = "branch_deserialize")]
|
||||
next_sync_committee_branch: Vec<Bytes32>,
|
||||
finalized_header: Header,
|
||||
#[serde(deserialize_with = "branch_deserialize")]
|
||||
finality_branch: Vec<Bytes32>,
|
||||
sync_aggregate: SyncAggregate,
|
||||
#[serde(deserialize_with = "u64_deserialize")]
|
||||
signature_slot: u64,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug, Clone)]
|
||||
struct SyncAggregate {
|
||||
sync_committee_bits: String,
|
||||
sync_committee_signature: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
struct FinalityUpdateResponse {
|
||||
data: FinalityUpdate,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, Debug)]
|
||||
struct FinalityUpdate {
|
||||
attested_header: Header,
|
||||
finalized_header: Header,
|
||||
#[serde(deserialize_with = "branch_deserialize")]
|
||||
finality_branch: Vec<Bytes32>,
|
||||
sync_aggregate: SyncAggregate,
|
||||
#[serde(deserialize_with = "u64_deserialize")]
|
||||
signature_slot: u64,
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
use eyre::Result;
|
||||
use ssz_rs::{Node, Vector};
|
||||
use super::consensus_client::Bytes32;
|
||||
|
||||
pub fn hex_str_to_bytes(s: &str) -> Result<Vec<u8>> {
|
||||
let stripped = s.strip_prefix("0x").unwrap_or(s);
|
||||
Ok(hex::decode(stripped)?)
|
||||
}
|
||||
|
||||
pub fn bytes32_to_node(bytes: &Bytes32) -> Result<Node> {
|
||||
Ok(Node::from_bytes(bytes.as_slice().try_into()?))
|
||||
}
|
||||
|
||||
pub fn bytes_to_bytes32(bytes: &[u8]) -> Bytes32 {
|
||||
Vector::from_iter(bytes.to_vec())
|
||||
}
|
Loading…
Reference in New Issue