Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

CCIP Read ISM #2398

Merged
merged 49 commits into from
Jul 13, 2023
Merged
Show file tree
Hide file tree
Changes from 44 commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
c1ccd21
feat: initial ccip read contract verifying message body via a multi sig
AlexBHarley Jun 16, 2023
dad7b7d
refactor: abstract ccip read contract
AlexBHarley Jun 16, 2023
d7b3e34
refactor: clean up
AlexBHarley Jun 16, 2023
7999aa7
refactor: clean up
AlexBHarley Jun 16, 2023
27506d9
refactor: clean up
AlexBHarley Jun 16, 2023
26c1822
test: mock
AlexBHarley Jun 16, 2023
144e910
refactor: better function names
AlexBHarley Jun 16, 2023
72dcef5
doc: missing doc
AlexBHarley Jun 16, 2023
6a69e54
doc: missing doc
AlexBHarley Jun 16, 2023
c0e9eea
feat: initial cl aggregator
AlexBHarley Jun 17, 2023
ecd5382
chore: compiling
AlexBHarley Jun 17, 2023
6abcafe
feat: working chainlink aggregator
AlexBHarley Jun 17, 2023
638ba9c
refactor: bring more in line with Chainlink setup
AlexBHarley Jun 20, 2023
fe1b5e6
refactor: bring more in line with Chainlink setup
AlexBHarley Jun 20, 2023
f74364f
refactor: bring more in line with Chainlink setup
AlexBHarley Jun 20, 2023
0ab74b0
feat: little bit of cleanup and testing chainlink messages
AlexBHarley Jun 22, 2023
9cb7eaa
feat: initial ccip read ism relayer
AlexBHarley Jun 22, 2023
c85ff91
test: cleanup
AlexBHarley Jun 22, 2023
26021f2
feat: relayer working
AlexBHarley Jun 22, 2023
f73c6e7
fix: metadata decoding is hex
AlexBHarley Jun 23, 2023
6462fb4
Merge branch 'main' into alexbharley/ccip-read-ism
AlexBHarley Jun 23, 2023
48f820d
test: fix
AlexBHarley Jun 23, 2023
e138a3c
cargo fmt
AlexBHarley Jun 23, 2023
9d71a23
cargo clippy
AlexBHarley Jun 23, 2023
cdfeb41
refactor: simplify and move logic to different repo
AlexBHarley Jun 24, 2023
74a9182
refactor: simplify and move logic to different repo
AlexBHarley Jun 24, 2023
7579e4b
refactor: simplify and move logic to different repo
AlexBHarley Jun 24, 2023
4358fbd
refactor: simplify and move logic to different repo
AlexBHarley Jun 24, 2023
ce82b54
chore: bump msg size limit
AlexBHarley Jun 24, 2023
a7ab2ab
doc
AlexBHarley Jun 24, 2023
9f0ac76
chore: update abi
AlexBHarley Jun 24, 2023
32ff55c
refactor: clean up agent a little
AlexBHarley Jun 24, 2023
995f76c
fix: wrong if condition for POST CCIP Reads
AlexBHarley Jun 24, 2023
925582e
feat: successful relay
AlexBHarley Jun 25, 2023
9ce259c
doc: better wording
AlexBHarley Jun 26, 2023
1802faf
chore: remove old _offchainCalldata variable
AlexBHarley Jun 26, 2023
ff6b59d
fix
AlexBHarley Jun 26, 2023
1c1d442
docs & removing more logic from AbstractCcipReadIsm
AlexBHarley Jun 28, 2023
173e350
fixes
AlexBHarley Jun 28, 2023
b57fd4d
doc
AlexBHarley Jun 28, 2023
fc73c2b
fix: message, not message.body
AlexBHarley Jun 28, 2023
b6d1eec
revert: no need for huge messages when metadata can store info
AlexBHarley Jun 28, 2023
94ec4c5
Merge branch 'main' into alexbharley/ccip-read-ism
AlexBHarley Jul 3, 2023
8275a07
chore: undo comment
AlexBHarley Jul 3, 2023
56d6906
refactor: address review comments
AlexBHarley Jul 6, 2023
24cc2be
refactor: address review comments
AlexBHarley Jul 6, 2023
0b37cae
refactor: address review comments
AlexBHarley Jul 6, 2023
387576f
Merge branch 'main' into alexbharley/ccip-read-ism
AlexBHarley Jul 13, 2023
c4447fe
Merge branch 'main' into alexbharley/ccip-read-ism
nambrot Jul 13, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions rust/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 rust/agents/relayer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ tokio = { workspace = true, features = ["rt", "macros", "parking_lot"] }
tracing-futures.workspace = true
tracing-subscriber.workspace = true
tracing.workspace = true
regex = "1.5"

hyperlane-core = { path = "../../hyperlane-core" }
hyperlane-base = { path = "../../hyperlane-base" }
Expand Down
12 changes: 10 additions & 2 deletions rust/agents/relayer/src/msg/metadata/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,16 @@ use hyperlane_base::{
};
use hyperlane_core::accumulator::merkle::Proof;
use hyperlane_core::{
Checkpoint, HyperlaneDomain, HyperlaneMessage, ModuleType, MultisigIsm, RoutingIsm,
ValidatorAnnounce, H160, H256,
CcipReadIsm, Checkpoint, HyperlaneDomain, HyperlaneMessage, ModuleType, MultisigIsm,
RoutingIsm, ValidatorAnnounce, H160, H256,
};

use crate::merkle_tree_builder::MerkleTreeBuilder;
use crate::msg::metadata::multisig::{
LegacyMultisigMetadataBuilder, MerkleRootMultisigMetadataBuilder,
MessageIdMultisigMetadataBuilder,
};
use crate::msg::metadata::CcipReadIsmMetadataBuilder;
use crate::msg::metadata::RoutingIsmMetadataBuilder;

#[derive(Debug, thiserror::Error)]
Expand Down Expand Up @@ -87,6 +88,7 @@ impl MetadataBuilder for BaseMetadataBuilder {
}
ModuleType::MessageIdMultisig => Box::new(MessageIdMultisigMetadataBuilder::new(base)),
ModuleType::Routing => Box::new(RoutingIsmMetadataBuilder::new(base)),
ModuleType::CcipRead => Box::new(CcipReadIsmMetadataBuilder::new(base)),
_ => return Err(MetadataBuilderError::UnsupportedModuleType(module_type).into()),
};
metadata_builder
Expand Down Expand Up @@ -150,6 +152,12 @@ impl BaseMetadataBuilder {
.await
}

pub async fn build_ccip_read_ism(&self, address: H256) -> Result<Box<dyn CcipReadIsm>> {
self.destination_chain_setup
.build_ccip_read_ism(address, &self.metrics)
.await
}

pub async fn build_checkpoint_syncer(
&self,
validators: &[H256],
Expand Down
102 changes: 102 additions & 0 deletions rust/agents/relayer/src/msg/metadata/ccip_read.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
use async_trait::async_trait;
use hyperlane_ethereum::OffchainLookup;
use reqwest::Client;
use serde::{Deserialize, Serialize};
use serde_json::json;
use std::ops::Deref;

use derive_new::new;
use eyre::Context;
use tracing::{info, instrument};

use super::{BaseMetadataBuilder, MetadataBuilder};
use ethers::abi::AbiDecode;
use ethers::core::utils::hex::decode as hex_decode;
use hyperlane_core::{HyperlaneMessage, RawHyperlaneMessage, H256};
use regex::Regex;

#[derive(Serialize, Deserialize)]
struct OffchainResponse {
data: String,
}

#[derive(Clone, Debug, new)]
pub struct CcipReadIsmMetadataBuilder {
base: BaseMetadataBuilder,
}

impl Deref for CcipReadIsmMetadataBuilder {
type Target = BaseMetadataBuilder;

fn deref(&self) -> &Self::Target {
&self.base
}
}

#[async_trait]
impl MetadataBuilder for CcipReadIsmMetadataBuilder {
#[instrument(err, skip(self))]
async fn build(
&self,
ism_address: H256,
message: &HyperlaneMessage,
) -> eyre::Result<Option<Vec<u8>>> {
const CTX: &str = "When fetching CcipRead metadata";
let ism = self.build_ccip_read_ism(ism_address).await.context(CTX)?;

let response = ism
.get_offchain_verify_info(RawHyperlaneMessage::from(message).to_vec())
.await;
let info: OffchainLookup = match response {
Ok(_) => {
info!("incorrectly configured getOffchainVerifyInfo, expected revert");
return Ok(None);
}
Err(raw_error) => {
AlexBHarley marked this conversation as resolved.
Show resolved Hide resolved
let matching_regex = Regex::new(r"0x[[:xdigit:]]+")?;
if let Some(matching) = &matching_regex.captures(&raw_error.to_string()) {
OffchainLookup::decode(hex_decode(&matching[0][2..])?)?
} else {
info!("unable to parse custom error out of revert");
return Ok(None);
}
}
};

for url in info.urls.iter() {
let interpolated_url = url
.replace("{sender}", &info.sender.to_string())
.replace("{data}", &info.call_data.to_string());
let res = if !url.contains("{data}") {
let body = json!({
"data": info.call_data.to_string(),
"sender": info.sender.to_string(),
});
Client::new()
.post(interpolated_url)
.header("Content-Type", "application/json")
.json(&body)
.send()
.await?
} else {
reqwest::get(interpolated_url).await?
};

let json: Result<OffchainResponse, reqwest::Error> = res.json().await;

match json {
Ok(result) => {
// remove leading 0x which hex_decode doesn't like
let metadata = hex_decode(&result.data[2..])?;
return Ok(Some(metadata));
}
Err(_err) => {
// try the next URL
}
}
}

// No metadata endpoints or endpoints down
Ok(None)
}
}
2 changes: 2 additions & 0 deletions rust/agents/relayer/src/msg/metadata/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
mod base;
mod ccip_read;
mod multisig;
mod routing;

pub(crate) use base::BaseMetadataBuilder;
pub(crate) use base::MetadataBuilder;
use ccip_read::CcipReadIsmMetadataBuilder;
use routing::RoutingIsmMetadataBuilder;
89 changes: 89 additions & 0 deletions rust/chains/hyperlane-ethereum/abis/ICcipReadIsm.abi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
[
{
"inputs": [
{
"internalType": "address",
"name": "sender",
"type": "address"
},
{
"internalType": "string[]",
"name": "urls",
"type": "string[]"
},
{
"internalType": "bytes",
"name": "callData",
"type": "bytes"
},
{
"internalType": "bytes4",
"name": "callbackFunction",
"type": "bytes4"
},
{
"internalType": "bytes",
"name": "extraData",
"type": "bytes"
}
],
"name": "OffchainLookup",
"type": "error"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "_message",
"type": "bytes"
}
],
"name": "getOffchainVerifyInfo",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "moduleType",
"outputs": [
{
"internalType": "uint8",
"name": "",
"type": "uint8"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "bytes",
"name": "_metadata",
"type": "bytes"
},
{
"internalType": "bytes",
"name": "_message",
"type": "bytes"
}
],
"name": "verify",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "nonpayable",
"type": "function"
}
]
110 changes: 110 additions & 0 deletions rust/chains/hyperlane-ethereum/src/ccip_read_ism.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
#![allow(clippy::enum_variant_names)]
#![allow(missing_docs)]

use std::collections::HashMap;
use std::sync::Arc;

use async_trait::async_trait;
use ethers::providers::Middleware;
use tracing::instrument;

use hyperlane_core::{
CcipReadIsm, ChainResult, ContractLocator, HyperlaneAbi, HyperlaneChain, HyperlaneContract,
HyperlaneDomain, HyperlaneProvider, H256,
};

pub use crate::contracts::i_ccip_read_ism::{
ICcipReadIsm as EthereumCcipReadIsmInternal, OffchainLookup, ICCIPREADISM_ABI,
};
use crate::trait_builder::BuildableWithProvider;
use crate::EthereumProvider;

pub struct CcipReadIsmBuilder {}

#[async_trait]
impl BuildableWithProvider for CcipReadIsmBuilder {
type Output = Box<dyn CcipReadIsm>;

async fn build_with_provider<M: Middleware + 'static>(
&self,
provider: M,
locator: &ContractLocator,
) -> Self::Output {
Box::new(EthereumCcipReadIsm::new(Arc::new(provider), locator))
}
}

/// A reference to an CcipReadIsm contract on some Ethereum chain
#[derive(Debug)]
pub struct EthereumCcipReadIsm<M>
where
M: Middleware,
{
contract: Arc<EthereumCcipReadIsmInternal<M>>,
domain: HyperlaneDomain,
}

impl<M> EthereumCcipReadIsm<M>
where
M: Middleware + 'static,
{
/// Create a reference to a mailbox at a specific Ethereum address on some
/// chain
pub fn new(provider: Arc<M>, locator: &ContractLocator) -> Self {
Self {
contract: Arc::new(EthereumCcipReadIsmInternal::new(locator.address, provider)),
domain: locator.domain.clone(),
}
}
}

impl<M> HyperlaneChain for EthereumCcipReadIsm<M>
where
M: Middleware + 'static,
{
fn domain(&self) -> &HyperlaneDomain {
&self.domain
}

fn provider(&self) -> Box<dyn HyperlaneProvider> {
Box::new(EthereumProvider::new(
self.contract.client(),
self.domain.clone(),
))
}
}

impl<M> HyperlaneContract for EthereumCcipReadIsm<M>
where
M: Middleware + 'static,
{
fn address(&self) -> H256 {
self.contract.address().into()
}
}

#[async_trait]
impl<M> CcipReadIsm for EthereumCcipReadIsm<M>
where
M: Middleware + 'static,
{
#[instrument(err)]
async fn get_offchain_verify_info(&self, message: Vec<u8>) -> ChainResult<bool> {
let info: bool = self
.contract
.get_offchain_verify_info(message.into())
.call()
.await?;
Ok(info)
}
}

pub struct EthereumCcipReadIsmAbi;

impl HyperlaneAbi for EthereumCcipReadIsmAbi {
const SELECTOR_SIZE_BYTES: usize = 4;

fn fn_map() -> HashMap<Vec<u8>, &'static str> {
super::extract_fn_map(&ICCIPREADISM_ABI)
}
}
10 changes: 7 additions & 3 deletions rust/chains/hyperlane-ethereum/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@ use ethers::prelude::{abi, Lazy, Middleware};

#[cfg(not(doctest))]
pub use self::{
config::*, interchain_gas::*, interchain_security_module::*, mailbox::*, multisig_ism::*,
provider::*, routing_ism::*, rpc_clients::*, signers::*, singleton_signer::*, trait_builder::*,
validator_announce::*,
ccip_read_ism::*, config::*, interchain_gas::*, interchain_security_module::*, mailbox::*,
multisig_ism::*, provider::*, routing_ism::*, rpc_clients::*, signers::*, singleton_signer::*,
trait_builder::*, validator_announce::*,
};

#[cfg(not(doctest))]
Expand Down Expand Up @@ -45,6 +45,10 @@ mod multisig_ism;
#[cfg(not(doctest))]
mod routing_ism;

/// CcipReadIsm abi
#[cfg(not(doctest))]
mod ccip_read_ism;

/// ValidatorAnnounce abi
#[cfg(not(doctest))]
mod validator_announce;
Expand Down
Loading
Loading