Skip to content

Commit

Permalink
rest
Browse files Browse the repository at this point in the history
  • Loading branch information
ncitron committed Dec 11, 2024
1 parent 0d6e793 commit 426d468
Show file tree
Hide file tree
Showing 3 changed files with 99 additions and 69 deletions.
7 changes: 7 additions & 0 deletions opstack/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct OpStackClientBuilder {
consensus_rpc: Option<Url>,
execution_rpc: Option<Url>,
rpc_socket: Option<SocketAddr>,
verify_unsafe_singer: Option<bool>,
}

impl OpStackClientBuilder {
Expand Down Expand Up @@ -49,6 +50,11 @@ impl OpStackClientBuilder {
self
}

pub fn verify_unsafe_singer(mut self, value: bool) -> Self {
self.verify_unsafe_singer = Some(value);
self
}

pub fn build(self) -> Result<OpStackClient> {
let config = if let Some(config) = self.config {
config
Expand All @@ -72,6 +78,7 @@ impl OpStackClientBuilder {
chain: NetworkConfig::from(network).chain,
load_external_fallback: None,
checkpoint: None,
verify_unsafe_signer: self.verify_unsafe_singer.unwrap_or_default(),
}
};
let consensus = ConsensusClient::new(&config);
Expand Down
6 changes: 6 additions & 0 deletions opstack/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub struct Config {
pub chain: ChainConfig,
pub load_external_fallback: Option<bool>,
pub checkpoint: Option<B256>,
pub verify_unsafe_signer: bool,
}

#[derive(Serialize, Deserialize, Clone)]
Expand All @@ -35,6 +36,7 @@ pub struct ChainConfig {
pub struct NetworkConfig {
pub consensus_rpc: Option<Url>,
pub chain: ChainConfig,
pub verify_unsafe_signer: bool,
}

#[derive(Copy, Clone, Debug)]
Expand Down Expand Up @@ -85,6 +87,7 @@ impl From<Network> for NetworkConfig {
system_config_contract: address!("229047fed2591dbec1eF1118d64F7aF3dB9EB290"),
eth_network: EthNetwork::MAINNET,
},
verify_unsafe_signer: false,
},
Network::Base => NetworkConfig {
consensus_rpc: Some("https://base.operationsolarstorm.org".parse().unwrap()),
Expand All @@ -94,6 +97,7 @@ impl From<Network> for NetworkConfig {
system_config_contract: address!("73a79Fab69143498Ed3712e519A88a918e1f4072"),
eth_network: EthNetwork::MAINNET,
},
verify_unsafe_signer: false,
},
Network::Worldchain => NetworkConfig {
consensus_rpc: Some(
Expand All @@ -107,6 +111,7 @@ impl From<Network> for NetworkConfig {
system_config_contract: address!("6ab0777fD0e609CE58F939a7F70Fe41F5Aa6300A"),
eth_network: EthNetwork::MAINNET,
},
verify_unsafe_signer: false,
},
Network::Zora => NetworkConfig {
consensus_rpc: Some("https://zora.operationsolarstorm.org".parse().unwrap()),
Expand All @@ -116,6 +121,7 @@ impl From<Network> for NetworkConfig {
system_config_contract: address!("A3cAB0126d5F504B071b81a3e8A2BBBF17930d86"),
eth_network: EthNetwork::MAINNET,
},
verify_unsafe_signer: false,
},
}
}
Expand Down
155 changes: 86 additions & 69 deletions opstack/src/consensus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ impl ConsensusClient {
finalized_block_send,
};

verify_unsafe_signer(config.clone(), inner.unsafe_signer.clone());
if config.verify_unsafe_signer {
verify_unsafe_signer(config.clone(), inner.unsafe_signer.clone());
}

#[cfg(not(target_arch = "wasm32"))]
let run = tokio::spawn;
Expand Down Expand Up @@ -164,76 +166,91 @@ fn verify_unsafe_signer(config: Config, signer: Arc<Mutex<Address>>) {
let run = wasm_bindgen_futures::spawn_local;

run(async move {
let mut eth_config = config.chain.eth_network.to_base_config();
eth_config.load_external_fallback = config.load_external_fallback.unwrap_or(false);
if let Some(checkpoint) = config.checkpoint {
eth_config.default_checkpoint = checkpoint;
}
let mut eth_consensus = EthConsensusClient::<MainnetConsensusSpec, HttpRpc, ConfigDB>::new(
&eth_config
.consensus_rpc
.clone()
.ok_or_else(|| eyre!("missing consensus rpc"))?,
Arc::new(eth_config.into()),
)?;
let block = eth_consensus
.block_recv()
.unwrap()
.recv()
.await
.ok_or_eyre("failed to receive block")?;
// Query proof from op consensus server
let req = format!("{}unsafe_signer_proof/{}", config.consensus_rpc, block.hash);
let proof = reqwest::get(req)
.await?
.json::<EIP1186AccountProofResponse>()
.await?;
let fut = async move {
let mut eth_config = config.chain.eth_network.to_base_config();
eth_config.load_external_fallback = config.load_external_fallback.unwrap_or(false);

// Verify unsafe signer
// with account proof
let account_path = keccak256(proof.address).to_vec();
let account_encoded = encode_account(&proof);
let is_valid = verify_proof(
&proof.account_proof,
block.state_root.as_slice(),
&account_path,
&account_encoded,
);
if !is_valid {
warn!(target: "helios::opstack", "account proof invalid");
return Err(eyre!("account proof invalid"));
}
// with storage proof
let storage_proof = proof.storage_proof[0].clone();
let key = storage_proof.key.0;
if key != B256::from_str(UNSAFE_SIGNER_SLOT)? {
warn!(target: "helios::opstack", "account proof invalid");
return Err(eyre!("account proof invalid"));
}
let key_hash = keccak256(key);
let value = encode(storage_proof.value);
let is_valid = verify_proof(
&storage_proof.proof,
proof.storage_hash.as_slice(),
key_hash.as_slice(),
&value,
);
if !is_valid {
warn!(target: "helios::opstack", "storage proof invalid");
return Err(eyre!("storage proof invalid"));
}
// Replace unsafe signer if different
let verified_signer = Address::from_slice(&storage_proof.value.to_be_bytes::<32>()[12..32]);
{
let mut curr_signer = signer.lock().map_err(|_| eyre!("failed to lock signer"))?;
if verified_signer != *curr_signer {
info!(target: "helios::opstack", "unsafe signer updated: {}", verified_signer);
*curr_signer = verified_signer;
if let Some(checkpoint) = config.checkpoint {
eth_config.default_checkpoint = checkpoint;
}
}
// Shutdown eth consensus client
eth_consensus.shutdown()?;
Ok(())

let mut eth_consensus = EthConsensusClient::<MainnetConsensusSpec, HttpRpc, ConfigDB>::new(
&eth_config
.consensus_rpc
.clone()
.ok_or_else(|| eyre!("missing consensus rpc"))?,
Arc::new(eth_config.into()),
)?;

let block = eth_consensus
.block_recv()
.unwrap()
.recv()
.await
.ok_or_eyre("failed to receive block")?;

// Query proof from op consensus server
let req = format!("{}unsafe_signer_proof/{}", config.consensus_rpc, block.hash);
let proof = reqwest::get(req)
.await?
.json::<EIP1186AccountProofResponse>()
.await?;

// Verify unsafe signer
// with account proof
let account_path = keccak256(proof.address).to_vec();
let account_encoded = encode_account(&proof);
let is_valid = verify_proof(
&proof.account_proof,
block.state_root.as_slice(),
&account_path,
&account_encoded,
);

if !is_valid {
warn!(target: "helios::opstack", "account proof invalid");
return Err(eyre!("account proof invalid"));
}

// with storage proof
let storage_proof = proof.storage_proof[0].clone();
let key = storage_proof.key.0;
if key != B256::from_str(UNSAFE_SIGNER_SLOT)? {
warn!(target: "helios::opstack", "account proof invalid");
return Err(eyre!("account proof invalid"));
}

let key_hash = keccak256(key);
let value = encode(storage_proof.value);
let is_valid = verify_proof(
&storage_proof.proof,
proof.storage_hash.as_slice(),
key_hash.as_slice(),
&value,
);

if !is_valid {
warn!(target: "helios::opstack", "storage proof invalid");
return Err(eyre!("storage proof invalid"));
}

// Replace unsafe signer if different
let verified_signer = Address::from_slice(&storage_proof.value.to_be_bytes::<32>()[12..32]);
{
let mut curr_signer = signer.lock().map_err(|_| eyre!("failed to lock signer"))?;
if verified_signer != *curr_signer {
info!(target: "helios::opstack", "unsafe signer updated: {}", verified_signer);
*curr_signer = verified_signer;
}
}

// Shutdown eth consensus client
eth_consensus.shutdown()?;

Ok(())
};

_ = fut.await;
});
}

Expand Down

0 comments on commit 426d468

Please sign in to comment.