Skip to content

Commit

Permalink
Eth2-to-Near-relay: fix LightClientUpdate for new period (#837)
Browse files Browse the repository at this point in the history
* add a test with proof validation.
* fix getting a light client update for a new period.
* formatting fixes.

Co-authored-by: Kirill <[email protected]>
Co-authored-by: Karim <[email protected]>
  • Loading branch information
3 people authored Oct 12, 2022
1 parent f015f8f commit cd2c9ef
Show file tree
Hide file tree
Showing 10 changed files with 128 additions and 83 deletions.
1 change: 1 addition & 0 deletions eth2near/eth2near-block-relay-rs/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions eth2near/eth2near-block-relay-rs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ hex = "*"
toml = "0.5.9"
atomic_refcell = "0.1.8"
bitvec = "*"
primitive-types = "0.7.3"

near-jsonrpc-client = "=0.4.0-beta.0"
near-crypto = "0.14.0"
Expand Down
47 changes: 6 additions & 41 deletions eth2near/eth2near-block-relay-rs/src/beacon_rpc_client.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
use crate::execution_block_proof::ExecutionBlockProof;
use crate::light_client_snapshot_with_proof::LightClientSnapshotWithProof;
use contract_wrapper::utils;
use crate::relay_errors::{
ErrorOnJsonParse, ExecutionPayloadError, FailOnGettingJson, MissSyncAggregationError,
NoBlockForSlotError, SignatureSlotNotFoundError,
};
use contract_wrapper::utils;
use eth_types::eth2::BeaconBlockHeader;
use eth_types::eth2::FinalizedHeaderUpdate;
use eth_types::eth2::HeaderUpdate;
Expand Down Expand Up @@ -198,7 +198,8 @@ impl BeaconRPCClient {
&self,
beacon_block_hash: H256,
) -> Result<u64, Box<dyn Error>> {
let beacon_block_hash_str: String = utils::trim_quotes(serde_json::to_string(&beacon_block_hash)?);
let beacon_block_hash_str: String =
utils::trim_quotes(serde_json::to_string(&beacon_block_hash)?);

let url = format!(
"{}/{}/{}",
Expand Down Expand Up @@ -251,43 +252,8 @@ impl BeaconRPCClient {
pub fn get_finality_light_client_update_with_sync_commity_update(
&self,
) -> Result<LightClientUpdate, Box<dyn Error>> {
let url_finality = format!(
"{}/{}",
self.endpoint_url,
Self::URL_FINALITY_LIGHT_CLIENT_UPDATE_PATH
);
let last_period = Self::get_period_for_slot(self.get_last_slot_number()?.as_u64());
let url_update = format!(
"{}/{}?start_period={}&count=1",
self.endpoint_url,
Self::URL_GET_LIGHT_CLIENT_UPDATE_API,
last_period
);
let finality_light_client_update_json_str =
self.get_json_from_raw_request(&url_finality)?;
let light_client_update_json_str = self.get_json_from_raw_request(&url_update)?;

let v: Value = serde_json::from_str(&finality_light_client_update_json_str)?;
let finality_light_client_update_json_str =
serde_json::to_string(&json!({"data": [v["data"]]}))?;

Ok(LightClientUpdate {
attested_beacon_header: Self::get_attested_header_from_light_client_update_json_str(
&finality_light_client_update_json_str,
)?,
sync_aggregate: Self::get_sync_aggregate_from_light_client_update_json_str(
&finality_light_client_update_json_str,
)?,
signature_slot: self.get_signature_slot(&finality_light_client_update_json_str)?,
finality_update: self.get_finality_update_from_light_client_update_json_str(
&finality_light_client_update_json_str,
)?,
sync_committee_update: Some(
Self::get_sync_committee_update_from_light_client_update_json_str(
&light_client_update_json_str,
)?,
),
})
self.get_light_client_update(last_period)
}

pub fn get_beacon_state(
Expand Down Expand Up @@ -490,14 +456,13 @@ impl BeaconRPCClient {
Err(err) => match err.downcast_ref::<NoBlockForSlotError>() {
Some(_) => continue,
None => return Err(err),
}
},
}
}

Err(format!(
"Unable to get non empty beacon block in range [`{}`-`{}`)",
start_slot,
finalized_slot
start_slot, finalized_slot
))?
}

Expand Down
73 changes: 71 additions & 2 deletions eth2near/eth2near-block-relay-rs/src/eth2near_relay.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
use std::cmp;
use crate::beacon_rpc_client::BeaconRPCClient;
use crate::config::Config;
use crate::eth1_rpc_client::Eth1RPCClient;
Expand All @@ -19,6 +18,7 @@ use eth_types::eth2::LightClientUpdate;
use eth_types::BlockHeader;
use log::{debug, info, trace, warn};
use near_primitives::views::FinalExecutionStatus;
use std::cmp;
use std::cmp::{max, min};
use std::error::Error;
use std::thread;
Expand Down Expand Up @@ -466,7 +466,8 @@ impl Eth2NearRelay {
last_finalized_slot_on_eth: u64,
) -> bool {
if (last_submitted_slot as i64) - (last_finalized_slot_on_near as i64)
< (ONE_EPOCH_IN_SLOTS * self.interval_between_light_client_updates_submission_in_epochs) as i64
< (ONE_EPOCH_IN_SLOTS * self.interval_between_light_client_updates_submission_in_epochs)
as i64
{
info!(target: "relay", "Light client update were send less then {} epochs ago. Skipping sending light client update", self.interval_between_light_client_updates_submission_in_epochs);
return false;
Expand Down Expand Up @@ -725,6 +726,7 @@ mod tests {
use eth_types::BlockHeader;
use std::thread::sleep;
use std::time::Duration;
use tree_hash::TreeHash;

const TIMEOUT_SECONDS: u64 = 30;
const TIMEOUT_STATE_SECONDS: u64 = 1000;
Expand Down Expand Up @@ -839,6 +841,73 @@ mod tests {
assert_eq!(finalized_slot, finalized_slot_1);
}

#[test]
fn test_finality_light_client_update_correctness() {
const TREE_FINALITY_DEPTH: usize = 6;
const TREE_FINALITY_INDEX: usize = 41;
const TREE_NEXT_SYNC_COMMITTEE_DEPTH: usize = 5;
const TREE_NEXT_SYNC_COMMITTEE_INDEX: usize = 23;

let config_for_test = get_test_config();

let relay = get_relay(true, true, &config_for_test);

let light_client_update = relay
.beacon_rpc_client
.get_finality_light_client_update_with_sync_commity_update()
.unwrap();

let branch: Vec<ethereum_types::H256> = light_client_update
.finality_update
.finality_branch
.iter()
.map(|h| h.0)
.collect();
assert!(
merkle_proof::verify_merkle_proof(
light_client_update
.finality_update
.header_update
.beacon_header
.tree_hash_root(),
branch.as_slice(),
TREE_FINALITY_DEPTH,
TREE_FINALITY_INDEX,
light_client_update.attested_beacon_header.state_root.0
),
"Incorrect proof of inclusion the finality checkpoint to attested beacon state"
);

let branch = light_client_update
.sync_committee_update
.as_ref()
.unwrap()
.next_sync_committee_branch
.iter()
.map(|h| h.0)
.collect::<Vec<ethereum_types::H256>>();
assert!(
merkle_proof::verify_merkle_proof(
light_client_update
.sync_committee_update
.as_ref()
.unwrap()
.next_sync_committee
.tree_hash_root(),
branch.as_slice(),
TREE_NEXT_SYNC_COMMITTEE_DEPTH,
TREE_NEXT_SYNC_COMMITTEE_INDEX,
light_client_update
.finality_update
.header_update
.beacon_header
.state_root
.0
),
"Incorrect proof of inclusion the next sync committee to finality beacon state"
);
}

#[test]
#[ignore]
fn test_hand_made_light_client_update() {
Expand Down
5 changes: 3 additions & 2 deletions eth2near/eth2near-block-relay-rs/src/execution_block_proof.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::beacon_block_body_merkle_tree::{BeaconBlockBodyMerkleTree, ExecutionPayloadMerkleTree};
use eth2_hashing;
use crate::relay_errors::MissExecutionPayload;
use eth2_hashing;
use ethereum_types::H256;
use std::error::Error;
use std::fmt;
Expand Down Expand Up @@ -122,7 +122,8 @@ impl ExecutionBlockProof {
for (i, leaf) in branch.iter().enumerate().take(depth) {
let ith_bit = (index >> i) & 0x01;
if ith_bit == 1 {
merkle_root = eth2_hashing::hash32_concat(leaf.as_bytes(), &merkle_root)[..].to_vec();
merkle_root =
eth2_hashing::hash32_concat(leaf.as_bytes(), &merkle_root)[..].to_vec();
} else {
let mut input = merkle_root;
input.extend_from_slice(leaf.as_bytes());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,7 @@ impl HandMadeFinalityLightClientUpdate {
let sync_aggregate = signature_beacon_body
.sync_aggregate()
.map_err(|_| MissSyncAggregationError)?;
let sync_committee_bits: [u8; 64] =
Self::get_sync_committee_bits(sync_aggregate)?;
let sync_committee_bits: [u8; 64] = Self::get_sync_committee_bits(sync_aggregate)?;
let sync_committee_bits_sum: u32 = sync_committee_bits
.into_iter()
.map(|x| x.count_ones())
Expand Down
3 changes: 2 additions & 1 deletion eth2near/eth2near-block-relay-rs/src/init_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ use tree_hash::TreeHash;

const CURRENT_SYNC_COMMITTEE_INDEX: u32 = 54;
const CURRENT_SYNC_COMMITTEE_TREE_DEPTH: u32 = consensus::floorlog2(CURRENT_SYNC_COMMITTEE_INDEX);
const CURRENT_SYNC_COMMITTEE_TREE_INDEX: u32 = consensus::get_subtree_index(CURRENT_SYNC_COMMITTEE_INDEX);
const CURRENT_SYNC_COMMITTEE_TREE_INDEX: u32 =
consensus::get_subtree_index(CURRENT_SYNC_COMMITTEE_INDEX);

pub fn verify_light_client_snapshot(
block_root: String,
Expand Down
72 changes: 40 additions & 32 deletions eth2near/eth2near-block-relay-rs/src/last_slot_searcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,7 @@ impl LastSlotSearcher {
Err(err) => match err.downcast_ref::<NoBlockForSlotError>() {
Some(_) => slot += 1,
None => return Err(err),
}
},
}
}

Expand Down Expand Up @@ -333,7 +333,7 @@ impl LastSlotSearcher {
Err(err) => match err.downcast_ref::<NoBlockForSlotError>() {
Some(_) => slot -= 1,
None => return Err(err),
}
},
}
}

Expand Down Expand Up @@ -618,12 +618,14 @@ mod tests {
finalized_slot + 2,
);

let last_submitted_block = last_slot_searcher.linear_search_backward(
finalized_slot + 1,
finalized_slot + 10,
&beacon_rpc_client,
&eth_client_contract,
).unwrap();
let last_submitted_block = last_slot_searcher
.linear_search_backward(
finalized_slot + 1,
finalized_slot + 10,
&beacon_rpc_client,
&eth_client_contract,
)
.unwrap();
assert_eq!(last_submitted_block, finalized_slot + 2);

send_execution_blocks(
Expand All @@ -634,12 +636,14 @@ mod tests {
config_for_test.slot_without_block - 1,
);

let last_submitted_block = last_slot_searcher.linear_search_backward(
finalized_slot + 1,
config_for_test.right_bound_in_slot_search,
&beacon_rpc_client,
&eth_client_contract,
).unwrap();
let last_submitted_block = last_slot_searcher
.linear_search_backward(
finalized_slot + 1,
config_for_test.right_bound_in_slot_search,
&beacon_rpc_client,
&eth_client_contract,
)
.unwrap();
assert_eq!(last_submitted_block, config_for_test.slot_without_block);
}

Expand Down Expand Up @@ -669,15 +673,17 @@ mod tests {
config_for_test.slot_without_block - 2,
);

let last_block_on_near = last_slot_searcher.linear_search_forward(
eth_client_contract
.get_finalized_beacon_block_slot()
.unwrap()
+ 1,
config_for_test.right_bound_in_slot_search,
&beacon_rpc_client,
&eth_client_contract,
).unwrap();
let last_block_on_near = last_slot_searcher
.linear_search_forward(
eth_client_contract
.get_finalized_beacon_block_slot()
.unwrap()
+ 1,
config_for_test.right_bound_in_slot_search,
&beacon_rpc_client,
&eth_client_contract,
)
.unwrap();

assert_eq!(last_block_on_near, config_for_test.slot_without_block - 2);

Expand All @@ -689,15 +695,17 @@ mod tests {
config_for_test.slot_without_block - 1,
);

let last_block_on_near = last_slot_searcher.linear_search_forward(
eth_client_contract
.get_finalized_beacon_block_slot()
.unwrap()
+ 1,
config_for_test.right_bound_in_slot_search,
&beacon_rpc_client,
&eth_client_contract,
).unwrap();
let last_block_on_near = last_slot_searcher
.linear_search_forward(
eth_client_contract
.get_finalized_beacon_block_slot()
.unwrap()
+ 1,
config_for_test.right_bound_in_slot_search,
&beacon_rpc_client,
&eth_client_contract,
)
.unwrap();

assert_eq!(last_block_on_near, config_for_test.slot_without_block);
}
Expand Down
2 changes: 0 additions & 2 deletions eth2near/eth2near-block-relay-rs/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
extern crate core;

pub mod beacon_block_body_merkle_tree;
pub mod beacon_rpc_client;
pub mod config;
Expand Down
4 changes: 3 additions & 1 deletion eth2near/eth2near-block-relay-rs/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,9 @@ pub fn get_client_contract(

match from_file {
true => test_utils::init_contract_from_files(&mut eth_client_contract, config_for_test),
false => init_contract::init_contract(&config, &mut eth_client_contract, "".to_string()).unwrap(),
false => {
init_contract::init_contract(&config, &mut eth_client_contract, "".to_string()).unwrap()
}
};

Box::new(eth_client_contract)
Expand Down

0 comments on commit cd2c9ef

Please sign in to comment.