Skip to content

Commit

Permalink
full light client impl (#20)
Browse files Browse the repository at this point in the history
* full light client impl

* full light client impl

* full light client impl
  • Loading branch information
malik672 authored Feb 12, 2025
1 parent dea71f5 commit 292c598
Show file tree
Hide file tree
Showing 19 changed files with 1,166 additions and 373 deletions.
2 changes: 2 additions & 0 deletions crates/mordor/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ pub const SLOT_DURATION: u64 = 12;
pub const SLOTS_PER_EPOCH: u64 = 32;
/// Duration of each epoch in seconds
pub const EPOCH_DURATION: u64 = SLOT_DURATION * SLOTS_PER_EPOCH;
/// Number of slots in a period
pub const SLOTS_PER_PERIOD: u64 = 8192;

pub type SlotTiming = (u64, u8, u8);

Expand Down
2 changes: 1 addition & 1 deletion crates/parser/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2021"

[dependencies]

mordor = { path = "../mordor" }
alloy.workspace = true
alloy-primitives.workspace = true
serde.workspace = true
Expand Down
2 changes: 1 addition & 1 deletion crates/parser/src/beacon/beacon_finality_checkpoints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ impl<'a> BeaconFinalityData {
} else {
let code = find_field(input, b"\"code\":", b",")?;
let code_str = std::str::from_utf8(&input[code.0..code.1]).ok()?;
Some(Self{
Some(Self {
previous_justified: FinalityCheckpoint::default(),
current_justified: FinalityCheckpoint::default(),
finalized: FinalityCheckpoint::default(),
Expand Down
1 change: 0 additions & 1 deletion crates/parser/src/beacon/beacon_genesis_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ impl<'a> RawJsonResponse<'a> {
});
}


let pos = data_start + data_marker.len();
let mut bracket_depth = 0;
let mut data_end = pos;
Expand Down
47 changes: 19 additions & 28 deletions crates/parser/src/beacon/light_client_bootstrap.rs
Original file line number Diff line number Diff line change
@@ -1,47 +1,36 @@
use alloy_primitives::{B256, U64};

use crate::{find_field, hex_to_b256};

#[derive(Debug, Default)]
pub struct LightClientBootstrap<'a> {
pub version: &'a str,
use crate::{
find_field, hex_to_b256,
types::{Beacon, SyncCommittee},
};

#[derive(Debug, Default, Clone)]
pub struct LightClientBootstrap {
pub version: String,
pub header: Header,
pub current_sync_committee: CurrentSyncCommittee,
pub current_sync_committee: SyncCommittee,
pub current_sync_committee_branch: Vec<B256>,
pub code: Option<u16>,
}

#[derive(Debug, Default)]
#[derive(Debug, Default, Clone)]
pub struct Header {
pub beacon: Beacon,
}

#[derive(Debug, Default)]
pub struct Beacon {
pub slot: U64,
pub proposer_index: U64,
pub parent_root: B256,
pub state_root: B256,
pub body_root: B256,
}

#[derive(Debug, Default)]
pub struct CurrentSyncCommittee {
pub pub_keys: Vec<B256>,
pub aggregate_pubkey: B256,
}

impl<'a> LightClientBootstrap<'a> {
pub fn parse(input: &'a [u8]) -> Option<Self> {
if memchr::memmem::find(input, b"\"code\":").is_some() {
let code = find_field(input, b"\"code\":", b",")?;
impl LightClientBootstrap {
pub fn parse(input: &[u8]) -> Option<Self> {
if let Some(_pos) = memchr::memmem::find(input, b"\"code\":") {
let code = find_field(input, b"\"code\":", b"}")?;
let code_str = std::str::from_utf8(&input[code.0..code.1]).ok()?;
return Some(Self {
code: Some(code_str.parse().ok()?),
..Default::default()
});
}


let version = find_field(input, b"\"version\":\"", b"\"")?;
let slot = find_field(input, b"\"slot\":\"", b"\"")?;
let proposer_index = find_field(input, b"\"proposer_index\":\"", b"\"")?;
Expand All @@ -64,7 +53,7 @@ impl<'a> LightClientBootstrap<'a> {
body_root: hex_to_b256(&input[body_root.0..body_root.1]),
};

let current_sync_committee = CurrentSyncCommittee {
let current_sync_committee = SyncCommittee {
pub_keys: Self::parse_pub_keys_array(input)?
.iter()
.map(|&(start, end)| hex_to_b256(&input[start..end]))
Expand All @@ -80,7 +69,9 @@ impl<'a> LightClientBootstrap<'a> {
let header = Header { beacon };

Some(LightClientBootstrap {
version: std::str::from_utf8(&input[version.0..version.1]).ok()?,
version: std::str::from_utf8(&input[version.0..version.1])
.ok()?
.to_string(),
header,
current_sync_committee,
current_sync_committee_branch,
Expand Down
212 changes: 212 additions & 0 deletions crates/parser/src/beacon/light_client_update.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
use alloy_primitives::{B256, U64};

use crate::{
find_field, hex_to_b256, hex_to_u64,
types::{Beacon, SyncCommittee},
};

#[derive(Debug, Default, Clone)]
pub struct Updates {
pub version: String,
pub attested_header: Beacon,
pub next_sync_committee_branch: Vec<B256>,
pub next_sync_committee: SyncCommittee,
pub finalized_header: Beacon,
pub finality_branch: Vec<B256>,
pub sync_aggregate: SyncAggregate,
pub signature_slot: U64,
pub code: Option<u16>,
}

#[derive(Debug, Default, Clone, Copy)]
pub struct SyncAggregate {
pub sync_committee_bits: U64,
pub sync_committee_signature: B256,
}

impl<'a> Updates {
pub fn parse(input: &'a [u8]) -> Option<Self> {
if let Some(_pos) = memchr::memmem::find(input, b"\"code\":") {
let code = find_field(input, b"\"code\":", b"}")?;
let code_str = std::str::from_utf8(&input[code.0..code.1]).ok()?;
return Some(Self {
code: Some(code_str.parse().ok()?),
..Default::default()
});
}

let version = find_field(input, b"\"version\":\"", b"\"")?;

let signature_key = find_field(input, b"\"signature_slot\":\"", b"\"")?;

let finalized_header = Self::parse_header(input, b"\"finalized_header\":")?;
let attested_header = Self::parse_header(input, b"\"attested_header\":")?;
let sync_committee_bits = find_field(input, b"\"sync_committee_bits\":\"", b"\"")?;
let sync_committee_signatures =
find_field(input, b"\"sync_committee_signatures\":\"", b"\"")?;

let finality_branch: Vec<B256> = Self::finality_branch(input)?
.iter()
.map(|&(start, end)| hex_to_b256(&input[start..end]))
.collect();

let next_sync_committee_branch: Vec<B256> = Self::next_sync_committee_branch(input)?
.iter()
.map(|&(start, end)| hex_to_b256(&input[start..end]))
.collect();

let pubkeys: Vec<B256> = Self::pubkeys(input)?
.iter()
.map(|&(start, end)| hex_to_b256(&input[start..end]))
.collect();

let beacon_a = Self::parse_beacon(&input[attested_header.0..attested_header.1])?;

let aggregate_pub_key = find_field(input, b"\"aggregate_pubkey\":\"", b"\"")?;

let beacon_f = Self::parse_beacon(&input[finalized_header.0..finalized_header.1])?;

let sync_aggregate = SyncAggregate {
sync_committee_bits: hex_to_u64(&input[sync_committee_bits.0..sync_committee_bits.1]),
sync_committee_signature: hex_to_b256(
&input[sync_committee_signatures.0..sync_committee_signatures.1],
),
};

let next_sync_committee = SyncCommittee {
pub_keys: pubkeys,
aggregate_pubkey: hex_to_b256(&input[aggregate_pub_key.0..aggregate_pub_key.1]),
};

Some(Updates {
version: std::str::from_utf8(&input[version.0..version.1])
.ok()?
.to_string(),
attested_header: beacon_a,
finalized_header: beacon_f,
finality_branch,
sync_aggregate,
signature_slot: hex_to_u64(&input[signature_key.0..signature_key.1]),
next_sync_committee,
next_sync_committee_branch,
code: None,
})
}

fn parse_header(input: &[u8], key: &[u8]) -> Option<(usize, usize)> {
let start = memchr::memmem::find(input, key)? + key.len();
let mut depth = 0;
let mut end = start;

for (i, &b) in input[start..].iter().enumerate() {
match b {
b'{' => depth += 1,
b'}' => {
depth -= 1;
if depth == 0 {
end = start + i + 1;
break;
}
}
_ => {}
}
}
Some((start, end))
}

fn parse_beacon(input: &[u8]) -> Option<Beacon> {
let slot = find_field(input, b"\"slot\":\"", b"\"")?;
let proposer_index = find_field(input, b"\"proposer_index\":\"", b"\"")?;
let parent_root = find_field(input, b"\"parent_root\":\"", b"\"")?;
let state_root = find_field(input, b"\"state_root\":\"", b"\"")?;
let body_root = find_field(input, b"\"body_root\":\"", b"\"")?;

let slot = &input[slot.0..slot.1];
let proposer_index = &input[proposer_index.0..proposer_index.1];
Some(Beacon {
slot: std::str::from_utf8(slot).unwrap().parse().unwrap(),
proposer_index: std::str::from_utf8(proposer_index)
.unwrap()
.parse()
.unwrap(),
parent_root: hex_to_b256(&input[parent_root.0..parent_root.1]),
state_root: hex_to_b256(&input[state_root.0..state_root.1]),
body_root: hex_to_b256(&input[body_root.0..body_root.1]),
})
}

pub fn finality_branch(data: &[u8]) -> Option<Vec<(usize, usize)>> {
let start = memchr::memmem::find(data, b"\"finality_branch\":[")?;
let mut pos = start + b"\"finality_branch\":[".len();
let mut result = Vec::new();

while data[pos] != b']' {
while data[pos] != b'"' && data[pos] != b']' {
pos += 1;
}
if data[pos] == b']' {
break;
}
pos += 1;
let committee_start = pos;

while data[pos] != b'"' {
pos += 1;
}
result.push((committee_start, pos));
pos += 1;
}

Some(result)
}

pub fn pubkeys(data: &[u8]) -> Option<Vec<(usize, usize)>> {
let start = memchr::memmem::find(data, b"\"pubkeys\":[")?;
let mut pos = start + b"\"pubkeys\":[".len();
let mut result = Vec::new();

while data[pos] != b']' {
while data[pos] != b'"' && data[pos] != b']' {
pos += 1;
}
if data[pos] == b']' {
break;
}
pos += 1;
let committee_start = pos;

while data[pos] != b'"' {
pos += 1;
}
result.push((committee_start, pos));
pos += 1;
}

Some(result)
}

pub fn next_sync_committee_branch(data: &[u8]) -> Option<Vec<(usize, usize)>> {
let start = memchr::memmem::find(data, b"\"next_sync_committee_branch\":[")?;
let mut pos = start + b"\"next_sync_committee_branch\":[".len();
let mut result = Vec::new();

while data[pos] != b']' {
while data[pos] != b'"' && data[pos] != b']' {
pos += 1;
}
if data[pos] == b']' {
break;
}
pos += 1;
let committee_start = pos;

while data[pos] != b'"' {
pos += 1;
}
result.push((committee_start, pos));
pos += 1;
}

Some(result)
}
}
Loading

0 comments on commit 292c598

Please sign in to comment.