Skip to content

Commit

Permalink
Merge branch 'master' into eth2near-relay-configurable-tests
Browse files Browse the repository at this point in the history
  • Loading branch information
olga24912 committed Sep 22, 2022
2 parents ce4ea8d + 5a5cd65 commit b859cab
Show file tree
Hide file tree
Showing 11 changed files with 173 additions and 32 deletions.
4 changes: 3 additions & 1 deletion eth2near/contract_wrapper/src/dao_eth_client_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,9 @@ mod tests {
finalized_beacon_header,
current_sync_committee,
next_sync_committee,
signer_account_id.clone()
None,
None,
Some(eth_client.contract_wrapper.get_signer_account_id()),
);

let dao_contract_wrapper =
Expand Down
26 changes: 18 additions & 8 deletions eth2near/contract_wrapper/src/eth_client_contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ use std::error::Error;
use std::option::Option;
use std::string::String;
use std::vec::Vec;

use serde::Serialize;
pub struct EthClientContract {
last_slot: u64,
contract_wrapper: Box<dyn ContractWrapper>,
pub contract_wrapper: Box<dyn ContractWrapper>,
}

impl EthClientContract {
Expand All @@ -35,9 +35,11 @@ impl EthClientContract {
finalized_beacon_header: ExtendedBeaconBlockHeader,
current_sync_committee: SyncCommittee,
next_sync_committee: SyncCommittee,
trusted_signer: String,
hashes_gc_threshold: Option<u64>,
max_submitted_blocks_by_account: Option<u32>,
trusted_signer: Option<AccountId>,
) {
#[derive(BorshSerialize)]
#[derive(BorshSerialize, Serialize)]
pub struct InitInput {
pub network: String,
pub finalized_execution_header: eth_types::BlockHeader,
Expand All @@ -59,11 +61,16 @@ impl EthClientContract {
next_sync_committee,
validate_updates: true,
verify_bls_signatures: false,
hashes_gc_threshold: 51000,
max_submitted_blocks_by_account: 8000,
trusted_signer: Option::<AccountId>::Some(trusted_signer.parse().unwrap()),
hashes_gc_threshold: hashes_gc_threshold.unwrap_or(51_000),
max_submitted_blocks_by_account: max_submitted_blocks_by_account.unwrap_or(8000),
trusted_signer,
};

println!(
"Init eth2 client input: \n {}",
serde_json::to_string_pretty(&init_input).unwrap()
);

self.contract_wrapper
.call_change_method(
"init".to_string(),
Expand Down Expand Up @@ -193,6 +200,7 @@ mod tests {
use crate::sandbox_contract_wrapper::SandboxContractWrapper;
use eth_types::eth2::{ExtendedBeaconBlockHeader, LightClientUpdate, SyncCommittee};
use eth_types::BlockHeader;
use near_primitives::types::AccountId;
use tokio::runtime::Runtime;

// TODO: use a more clean approach to include binary
Expand Down Expand Up @@ -323,7 +331,9 @@ mod tests {
finalized_beacon_header,
current_sync_committee,
next_sync_committee,
trusted_signer,
None,
None,
Option::<AccountId>::Some(trusted_signer.parse().unwrap()),
);
eth_state.current_light_client_update = 1;
}
Expand Down
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 @@ -24,6 +24,7 @@ futures = { version = "0.3.21", default-features = false }
async-std = "1.12.0"
hex = "*"
toml = "0.5.9"
eth2-utility = { path = "../../contracts/near/eth2-utility" }
finality-update-verify = { path = "../finality-update-verify" }
atomic_refcell = "0.1.8"
bitvec = "*"
Expand Down
40 changes: 40 additions & 0 deletions eth2near/eth2near-block-relay-rs/src/beacon_rpc_client.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::execution_block_proof::ExecutionBlockProof;
use crate::light_client_snapshot_with_proof::LightClientSnapshotWithProof;
use crate::relay_errors::{
ExecutionPayloadError, FailOnGettingJson, MissSyncAggregationError, NoBlockForSlotError,
SignatureSlotNotFoundError,
Expand Down Expand Up @@ -37,6 +38,7 @@ impl BeaconRPCClient {
const URL_GET_LIGHT_CLIENT_UPDATE_API: &'static str = "eth/v1/beacon/light_client/updates";
const URL_FINALITY_LIGHT_CLIENT_UPDATE_PATH: &'static str =
"eth/v1/beacon/light_client/finality_update/";
const URL_GET_BOOTSTRAP: &'static str = "eth/v1/beacon/light_client/bootstrap";
const URL_STATE_PATH: &'static str = "eth/v2/debug/beacon/states";

const SLOTS_PER_EPOCH: u64 = 32;
Expand Down Expand Up @@ -139,6 +141,44 @@ impl BeaconRPCClient {
})
}

// Fetch a bootstrapping state with a proof to a trusted block root.
// The trusted block root should be fetched with similar means to a weak subjectivity checkpoint.
// Only block roots for checkpoints are guaranteed to be available.
pub fn get_bootstrap(
&self,
block_root: String,
) -> Result<LightClientSnapshotWithProof, Box<dyn Error>> {
let url = format!(
"{}/{}/{}",
self.endpoint_url,
Self::URL_GET_BOOTSTRAP,
block_root
);

let light_client_snapshot_json_str = self.get_json_from_raw_request(&url)?;
let parsed_json: Value = serde_json::from_str(&light_client_snapshot_json_str)?;
let beacon_header: BeaconBlockHeader =
serde_json::from_value(parsed_json["data"]["header"].clone())?;
let current_sync_committee: SyncCommittee =
serde_json::from_value(parsed_json["data"]["current_sync_committee"].clone())?;
let current_sync_committee_branch: Vec<H256> =
serde_json::from_value(parsed_json["data"]["current_sync_committee_branch"].clone())?;

Ok(LightClientSnapshotWithProof {
beacon_header,
current_sync_committee,
current_sync_committee_branch,
})
}

pub fn get_checkpoint_root(&self) -> Result<String, Box<dyn Error>> {
let url = format!("{}/eth/v1/beacon/states/finalized/finality_checkpoints", self.endpoint_url);
let checkpoint_json_str = self.get_json_from_raw_request(&url)?;
let parsed_json: Value = serde_json::from_str(&checkpoint_json_str)?;

Ok(trim_quotes(parsed_json["data"]["finalized"]["root"].to_string()))
}

/// Return the last finalized slot in the Beacon chain
pub fn get_last_finalized_slot_number(&self) -> Result<types::Slot, Box<dyn Error>> {
Ok(self.get_beacon_block_header_for_block_id("finalized")?.slot)
Expand Down
9 changes: 9 additions & 0 deletions eth2near/eth2near-block-relay-rs/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,15 @@ pub struct Config {

// Sleep time in seconds after blocks/light_client_update submission to client
pub sleep_time_after_submission_secs: u64,

/// Max number of stored blocks in the storage of the eth2 client contract.
/// Events that happen past this threshold cannot be verified by the client.
/// It is used on initialization of the Eth2 client.
pub hashes_gc_threshold: Option<u64>,

/// Max number of unfinalized blocks allowed to be stored by one submitter account.
/// It is used on initialization of the Eth2 client.
pub max_submitted_blocks_by_account: Option<u32>,
}

impl Config {
Expand Down
92 changes: 74 additions & 18 deletions eth2near/eth2near-block-relay-rs/src/init_contract.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,48 @@
use crate::beacon_rpc_client::BeaconRPCClient;
use crate::config::Config;
use crate::eth1_rpc_client::Eth1RPCClient;
use crate::light_client_snapshot_with_proof::LightClientSnapshotWithProof;
use contract_wrapper::eth_client_contract::EthClientContract;
use eth2_utility::consensus::{convert_branch, floorlog2, get_subtree_index};
use eth_types::eth2::ExtendedBeaconBlockHeader;
use eth_types::BlockHeader;
use log::info;
use std::{thread, time};
use tree_hash::TreeHash;

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

pub fn verify_light_client_snapshot(
block_root: String,
light_client_snapshot: &LightClientSnapshotWithProof,
) -> bool {
let expected_block_root = format!(
"{:#x}",
light_client_snapshot.beacon_header.tree_hash_root()
);

if block_root != expected_block_root {
return false;
}

let branch = convert_branch(&light_client_snapshot.current_sync_committee_branch);
merkle_proof::verify_merkle_proof(
light_client_snapshot
.current_sync_committee
.tree_hash_root(),
&branch,
CURRENT_SYNC_COMMITTEE_TREE_DEPTH.try_into().unwrap(),
CURRENT_SYNC_COMMITTEE_TREE_INDEX.try_into().unwrap(),
light_client_snapshot.beacon_header.state_root.0,
)
}

pub fn init_contract(
config: &Config,
eth_client_contract: &mut EthClientContract,
mut init_block_root: String,
) -> Result<(), Box<dyn std::error::Error>> {
info!(target: "relay", "=== Contract initialization ===");

Expand All @@ -18,22 +51,19 @@ pub fn init_contract(
config.eth_requests_timeout_seconds,
config.state_requests_timeout_seconds,
);
let eth1_rpc_client = Eth1RPCClient::new(&config.eth1_endpoint);

let start_slot = beacon_rpc_client.get_last_finalized_slot_number().unwrap();
let period = BeaconRPCClient::get_period_for_slot(start_slot.as_u64());
let eth1_rpc_client = Eth1RPCClient::new(&config.eth1_endpoint);

let light_client_update = beacon_rpc_client
.get_finality_light_client_update_with_sync_commity_update()
.unwrap();
let block_id = format!(
"{}",
light_client_update
.finality_update
.header_update
.beacon_header
.slot
);
let finality_slot = light_client_update
.finality_update
.header_update
.beacon_header
.slot;

let block_id = format!("{}", finality_slot);
let finalized_header: ExtendedBeaconBlockHeader =
ExtendedBeaconBlockHeader::from(light_client_update.finality_update.header_update);
let finalized_body = beacon_rpc_client
Expand All @@ -53,19 +83,45 @@ pub fn init_contract(
.sync_committee_update
.unwrap()
.next_sync_committee;
let prev_light_client_update = beacon_rpc_client.get_light_client_update(period - 1)?;
let current_sync_committee = prev_light_client_update
.sync_committee_update
.unwrap()
.next_sync_committee;

if init_block_root.is_empty() {
init_block_root = beacon_rpc_client
.get_checkpoint_root()
.expect("Fail to get last checkpoint");
}

let light_client_snapshot = beacon_rpc_client
.get_bootstrap(init_block_root.clone())
.expect("Unable to fetch bootstrap state");

info!(target: "relay", "init_block_root: {}", init_block_root);

if BeaconRPCClient::get_period_for_slot(light_client_snapshot.beacon_header.slot)
!= BeaconRPCClient::get_period_for_slot(finality_slot)
{
panic!("Period for init_block_root different from current period. Please use snapshot for current period");
}

if !verify_light_client_snapshot(init_block_root, &light_client_snapshot) {
return Err("Invalid light client snapshot".into());
}

eth_client_contract.init_contract(
config.network.to_string(),
finalized_execution_header,
finalized_header,
current_sync_committee,
light_client_snapshot.current_sync_committee,
next_sync_committee,
config.signer_account_id.clone(),
config.hashes_gc_threshold,
config.max_submitted_blocks_by_account,
Some(
config
.dao_contract_account_id
.as_ref()
.unwrap_or(&config.signer_account_id)
.parse()
.unwrap(),
),
);

thread::sleep(time::Duration::from_secs(30));
Expand Down
3 changes: 2 additions & 1 deletion eth2near/eth2near-block-relay-rs/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ pub mod execution_block_proof;
pub mod hand_made_finality_light_client_update;
pub mod init_contract;
pub mod last_slot_searcher;
pub mod light_client_snapshot_with_proof;
pub mod logger;
pub mod near_rpc_client;
pub mod prometheus_metrics;
pub mod relay_errors;

#[cfg(test)]
pub mod config_for_tests;

pub mod prometheus_metrics;
#[cfg(test)]
pub mod test_utils;
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
use eth_types::eth2::{BeaconBlockHeader, SyncCommittee};
use eth_types::H256;
use serde::Serialize;

#[derive(Serialize)]
pub struct LightClientSnapshotWithProof {
pub beacon_header: BeaconBlockHeader,
pub current_sync_committee: SyncCommittee,
pub current_sync_committee_branch: Vec<H256>,
}
7 changes: 6 additions & 1 deletion eth2near/eth2near-block-relay-rs/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@ struct Arguments {
/// The eth contract on Near will be initialized
init_contract: bool,

#[clap(long, action = ArgAction::Set, default_value = "")]
/// The trusted block root for checkpoint for contract initialization
/// e.g.: --init-contract --init-block-root 0x9cd0c5a8392d0659426b12384e8440c147510ab93eeaeccb08435a462d7bb1c7
init_block_root: String,

#[clap(long, default_value_t = String::from("info"))]
/// Log level (trace, debug, info, warn, error)
log_level: String,
Expand Down Expand Up @@ -101,7 +106,7 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {

if args.init_contract {
let mut eth_client_contract = EthClientContract::new(get_eth_contract_wrapper(&config));
init_contract(&config, &mut eth_client_contract).unwrap();
init_contract(&config, &mut eth_client_contract, args.init_block_root).unwrap();
} else {
let mut eth2near_relay = Eth2NearRelay::init(
&config,
Expand Down
12 changes: 9 additions & 3 deletions eth2near/eth2near-block-relay-rs/src/test_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,9 @@ pub fn init_contract_from_files(
finalized_beacon_header,
current_sync_committee,
next_sync_committee,
eth_client_contract.get_signature_account_id().to_string(),
None,
None,
Some(eth_client_contract.contract_wrapper.get_signer_account_id()),
);
thread::sleep(time::Duration::from_secs(30));
}
Expand Down Expand Up @@ -149,7 +151,9 @@ pub fn init_contract_from_specific_slot(
finalized_beacon_header,
current_sync_committee,
next_sync_committee,
eth_client_contract.get_signature_account_id().to_string(),
None,
None,
Some(eth_client_contract.contract_wrapper.get_signer_account_id()),
);

thread::sleep(time::Duration::from_secs(30));
Expand Down Expand Up @@ -191,6 +195,8 @@ fn get_config(config_for_test: &ConfigForTests) -> Config {
state_requests_timeout_seconds: 1000,
sleep_time_on_sync_secs: 0,
sleep_time_after_submission_secs: 5,
hashes_gc_threshold: None,
max_submitted_blocks_by_account: None,
}
}

Expand All @@ -208,7 +214,7 @@ 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(&config, &mut eth_client_contract).unwrap(),
false => init_contract(&config, &mut eth_client_contract, "".to_string()).unwrap(),
};

Box::new(eth_client_contract)
Expand Down

0 comments on commit b859cab

Please sign in to comment.