Skip to content

Commit

Permalink
imp(relayer): enable mock relaying (#205)
Browse files Browse the repository at this point in the history
  • Loading branch information
srdtrk authored Jan 8, 2025
1 parent 88339e7 commit febaabb
Show file tree
Hide file tree
Showing 9 changed files with 199 additions and 47 deletions.
1 change: 0 additions & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ jobs:
- TestWithRelayerTestSuite/Test_5_TimeoutPacketFromEth_Plonk
- TestWithRelayerTestSuite/TestRecvPacketToCosmos
- TestWithRelayerTestSuite/Test_10_RecvPacketToCosmos
- TestWithRelayerTestSuite/Test_2_ConcurrentRecvPacketToEth_Groth16
- TestWithCosmosRelayerTestSuite/TestRelayerInfo
- TestWithCosmosRelayerTestSuite/TestICS20RecvAndAckPacket
- TestWithCosmosRelayerTestSuite/Test_10_ICS20RecvAndAckPacket
Expand Down
8 changes: 0 additions & 8 deletions e2e/interchaintestv8/e2esuite/suite.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,11 +126,3 @@ func (s *TestSuite) SetupSuite(ctx context.Context) {
s.proposalIDs[chain.Config().ChainID] = 1
}
}

// TODO: Remove when we have removed manual relaying and moved to
// common relaying methods that can choose the correct methods
func (s *TestSuite) SkipIfEthTestnetType(testnetType string) {
if os.Getenv(testvalues.EnvKeyEthTestnetType) == testnetType {
s.T().Skipf("Skipping test because Ethereum testnet type is %s", testnetType)
}
}
2 changes: 1 addition & 1 deletion e2e/interchaintestv8/e2esuite/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func (s *TestSuite) GetTxReciept(ctx context.Context, chain ethereum.Ethereum, h
s.Require().NoError(err)

var receipt *ethtypes.Receipt
err = testutil.WaitForCondition(time.Second*30, time.Second, func() (bool, error) {
err = testutil.WaitForCondition(time.Second*40, time.Second, func() (bool, error) {
receipt, err = ethClient.TransactionReceipt(ctx, hash)
if err != nil {
return false, nil
Expand Down
2 changes: 2 additions & 0 deletions e2e/interchaintestv8/relayer/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ type EthCosmosConfigInfo struct {
SP1PrivateKey string
// Signer address cosmos
SignerAddress string
// Whether we use the mock client in Cosmos
Mock bool
}

func (c *EthCosmosConfigInfo) GenerateEthCosmosConfigFile(path string) error {
Expand Down
5 changes: 3 additions & 2 deletions e2e/interchaintestv8/relayer/config.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"server": {
"log_level": "debug",
"log_level": "info",
"address": "127.0.0.1"
},
"modules": [
Expand All @@ -12,7 +12,8 @@
"ics26_address": "{{ .ICS26Address }}",
"eth_rpc_url": "{{ .EthRPC }}",
"eth_beacon_api_url": "{{ .BeaconAPI }}",
"signer_address": "{{ .SignerAddress }}"
"signer_address": "{{ .SignerAddress }}",
"mock": {{ .Mock }}
}
},
{
Expand Down
5 changes: 3 additions & 2 deletions e2e/interchaintestv8/relayer_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,7 @@ func (s *RelayerTestSuite) SetupSuite(ctx context.Context, proofType operator.Su
BeaconAPI: beaconAPI,
SP1PrivateKey: os.Getenv(testvalues.EnvKeySp1PrivateKey),
SignerAddress: s.SimdSubmitter.FormattedAddress(),
Mock: os.Getenv(testvalues.EnvKeyEthTestnetType) == testvalues.EthTestnetTypePoW,
}

err := configInfo.GenerateEthCosmosConfigFile(testvalues.RelayerConfigFilePath)
Expand Down Expand Up @@ -306,6 +307,8 @@ func (s *RelayerTestSuite) RecvPacketToEthTest(
}))
}

// TestConcurrentRecvPacketToEth_Groth16 tests the concurrent relaying of 2 packets from Cosmos to Ethereum
// NOTE: This test is not included in the CI pipeline as it is flaky
func (s *RelayerTestSuite) Test_2_ConcurrentRecvPacketToEth_Groth16() {
// I've noticed that the prover network drops the requests when sending too many
ctx := context.Background()
Expand Down Expand Up @@ -454,7 +457,6 @@ func (s *RelayerTestSuite) Test_5_BatchedAckPacketToEth_Plonk() {
func (s *RelayerTestSuite) ICS20TransferERC20TokenBatchedAckTest(
ctx context.Context, proofType operator.SupportedProofType, numOfTransfers int,
) {
s.SkipIfEthTestnetType(testvalues.EthTestnetTypePoW)
s.SetupSuite(ctx, proofType)

eth, simd := s.EthChain, s.CosmosChains[0]
Expand Down Expand Up @@ -794,7 +796,6 @@ func (s *RelayerTestSuite) Test_10_RecvPacketToCosmos() {
}

func (s *RelayerTestSuite) RecvPacketToCosmosTest(ctx context.Context, numOfTransfers int) {
s.SkipIfEthTestnetType(testvalues.EthTestnetTypePoW)
s.SetupSuite(ctx, operator.ProofTypeGroth16) // Doesn't matter, since we won't relay to eth in this test

eth, simd := s.EthChain, s.CosmosChains[0]
Expand Down
140 changes: 116 additions & 24 deletions packages/relayer-lib/src/tx_builder/eth_to_cosmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@ pub struct TxBuilder<T: Transport + Clone, P: Provider<T> + Clone> {
pub signer_address: String,
}

/// The `MockTxBuilder` produces txs to [`CosmosSdk`] based on events from [`EthEureka`]
/// for testing purposes.
pub struct MockTxBuilder<T: Transport + Clone, P: Provider<T> + Clone> {
/// The ETH API client.
pub eth_client: EthApiClient<T, P>,
/// The IBC Eureka router instance.
pub ics26_router: routerInstance<T, P>,
/// The signer address for the Cosmos messages.
pub signer_address: String,
}

impl<T: Transport + Clone, P: Provider<T> + Clone> TxBuilder<T, P> {
/// Create a new [`TxBuilder`] instance.
pub fn new(
Expand Down Expand Up @@ -207,6 +218,35 @@ where
);
tracing::debug!("Target block number: {}", target_block_number);

let target_height = Height {
revision_number: 0,
revision_height: target_block_number,
};

let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_secs();

let mut timeout_msgs = cosmos::target_events_to_timeout_msgs(
dest_events,
&target_channel_id,
&target_height,
&self.signer_address,
now,
);

let (mut recv_msgs, mut ack_msgs) = cosmos::src_events_to_recv_and_ack_msgs(
src_events,
&target_channel_id,
&target_height,
&self.signer_address,
now,
);

tracing::debug!("Timeout messages: #{}", timeout_msgs.len());
tracing::debug!("Recv messages: #{}", recv_msgs.len());
tracing::debug!("Ack messages: #{}", ack_msgs.len());

let ethereum_client_state = self
.ethereum_client_state(channel.client_id.clone())
.await?;
Expand All @@ -225,8 +265,8 @@ where
.await?;
tracing::debug!("light client updates: #{}", light_client_updates.len());

let mut headers = vec![];
let mut trusted_slot = ethereum_consensus_state.slot;
let mut headers = vec![];
let mut prev_pub_agg_key = BlsPublicKey::default();
for update in &light_client_updates {
tracing::debug!(
Expand Down Expand Up @@ -302,17 +342,78 @@ where

tracing::debug!("Headers assembled: #{}", headers.len());

let update_msgs_iter = headers
.iter()
.map(|header| serde_json::to_vec(header).expect("Failed to serialize header"))
.map(|header_bz| ClientMessage { data: header_bz })
.map(|msg| Any::from_msg(&msg).expect("Failed to convert to Any"))
.map(|client_msg| MsgUpdateClient {
client_id: channel.client_id.clone(),
client_message: Some(client_msg),
signer: self.signer_address.clone(),
cosmos::inject_ethereum_proofs(
&mut recv_msgs,
&mut ack_msgs,
&mut timeout_msgs,
&self.eth_client,
&ethereum_client_state.ibc_contract_address.to_string(),
ethereum_client_state.ibc_commitment_slot,
target_block_number,
trusted_slot,
)
.await?;

let update_msgs = headers
.into_iter()
.map(|header| -> Result<MsgUpdateClient> {
let header_bz = serde_json::to_vec(&header)?;
let client_msg = Any::from_msg(&ClientMessage { data: header_bz })?;
Ok(MsgUpdateClient {
client_id: channel.client_id.clone(),
client_message: Some(client_msg),
signer: self.signer_address.clone(),
})
})
.map(|msg| Any::from_msg(&msg));
.collect::<Result<Vec<_>, _>>()?;

let all_msgs = update_msgs
.into_iter()
.map(|m| Any::from_msg(&m))
.chain(timeout_msgs.into_iter().map(|m| Any::from_msg(&m)))
.chain(recv_msgs.into_iter().map(|m| Any::from_msg(&m)))
.chain(ack_msgs.into_iter().map(|m| Any::from_msg(&m)))
.collect::<Result<Vec<_>, _>>()?;

let tx_body = TxBody {
messages: all_msgs,
..Default::default()
};
Ok(tx_body.encode_to_vec())
}
}

impl<T: Transport + Clone, P: Provider<T> + Clone> MockTxBuilder<T, P> {
/// Create a new [`MockTxBuilder`] instance for testing.
pub fn new(ics26_address: Address, provider: P, signer_address: String) -> Self {
Self {
eth_client: EthApiClient::new(provider.clone()),
ics26_router: routerInstance::new(ics26_address, provider),
signer_address,
}
}
}

#[async_trait::async_trait]
impl<T, P> TxBuilderService<EthEureka, CosmosSdk> for MockTxBuilder<T, P>
where
T: Transport + Clone,
P: Provider<T> + Clone,
{
#[tracing::instrument(skip_all)]
async fn relay_events(
&self,
src_events: Vec<EurekaEvent>,
dest_events: Vec<EurekaEvent>,
target_channel_id: String,
) -> Result<Vec<u8>> {
let target_block_number = self.eth_client.get_block_number().await?;

tracing::info!(
"Relaying events from Ethereum to Cosmos for channel {}",
target_channel_id
);
tracing::debug!("Target block number: {}", target_block_number);

let target_height = Height {
revision_number: 0,
Expand Down Expand Up @@ -343,20 +444,11 @@ where
tracing::debug!("Recv messages: #{}", recv_msgs.len());
tracing::debug!("Ack messages: #{}", ack_msgs.len());

cosmos::inject_ethereum_proofs(
&mut recv_msgs,
&mut ack_msgs,
&mut timeout_msgs,
&self.eth_client,
&ethereum_client_state.ibc_contract_address.to_string(),
ethereum_client_state.ibc_commitment_slot,
target_block_number,
trusted_slot,
)
.await?;
cosmos::inject_mock_proofs(&mut recv_msgs, &mut ack_msgs, &mut timeout_msgs);

let all_msgs = update_msgs_iter
.chain(timeout_msgs.into_iter().map(|m| Any::from_msg(&m)))
let all_msgs = timeout_msgs
.into_iter()
.map(|m| Any::from_msg(&m))
.chain(recv_msgs.into_iter().map(|m| Any::from_msg(&m)))
.chain(ack_msgs.into_iter().map(|m| Any::from_msg(&m)))
.collect::<Result<Vec<_>, _>>()?;
Expand Down
21 changes: 21 additions & 0 deletions packages/relayer-lib/src/utils/cosmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,3 +276,24 @@ async fn get_commitment_proof<T: Transport + Clone, P: Provider<T> + Clone>(
proof: storage_proof.proof.clone(),
})
}

pub fn inject_mock_proofs(
recv_msgs: &mut [MsgRecvPacket],
ack_msgs: &mut [MsgAcknowledgement],
timeout_msgs: &mut [MsgTimeout],
) {
for msg in recv_msgs.iter_mut() {
msg.proof_commitment = b"mock".to_vec();
msg.proof_height = Some(Height::default());
}

for msg in ack_msgs.iter_mut() {
msg.proof_acked = b"mock".to_vec();
msg.proof_height = Some(Height::default());
}

for msg in timeout_msgs.iter_mut() {
msg.proof_unreceived = b"mock".to_vec();
msg.proof_height = Some(Height::default());
}
}
62 changes: 53 additions & 9 deletions programs/relayer/src/modules/eth_to_cosmos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use alloy::{
transports::BoxTransport,
};
use ibc_eureka_relayer_lib::{
events::EurekaEvent,
listener::{cosmos_sdk, eth_eureka, ChainListenerService},
tx_builder::{eth_to_cosmos, TxBuilderService},
};
Expand Down Expand Up @@ -35,7 +36,12 @@ struct EthToCosmosRelayerModuleServer {
/// The chain listener for Cosmos SDK.
pub tm_listener: cosmos_sdk::ChainListener,
/// The transaction builder for Ethereum to Cosmos.
pub tx_builder: eth_to_cosmos::TxBuilder<BoxTransport, RootProvider<BoxTransport>>,
pub tx_builder: EthToCosmosTxBuilder,
}

enum EthToCosmosTxBuilder {
Real(eth_to_cosmos::TxBuilder<BoxTransport, RootProvider<BoxTransport>>),
Mock(eth_to_cosmos::MockTxBuilder<BoxTransport, RootProvider<BoxTransport>>),
}

/// The configuration for the Cosmos to Cosmos relayer module.
Expand All @@ -53,6 +59,9 @@ pub struct EthToCosmosConfig {
/// The address of the submitter.
/// Required since cosmos messages require a signer address.
pub signer_address: String,
/// Whether to run in mock mode.
#[serde(default)]
pub mock: bool,
}

impl EthToCosmosRelayerModuleServer {
Expand All @@ -70,13 +79,21 @@ impl EthToCosmosRelayerModuleServer {
.expect("Failed to create tendermint HTTP client");
let tm_listener = cosmos_sdk::ChainListener::new(tm_client.clone());

let tx_builder = eth_to_cosmos::TxBuilder::new(
config.ics26_address,
provider,
config.eth_beacon_api_url,
tm_client,
config.signer_address,
);
let tx_builder = if config.mock {
EthToCosmosTxBuilder::Mock(eth_to_cosmos::MockTxBuilder::new(
config.ics26_address,
provider,
config.signer_address,
))
} else {
EthToCosmosTxBuilder::Real(eth_to_cosmos::TxBuilder::new(
config.ics26_address,
provider,
config.eth_beacon_api_url,
tm_client,
config.signer_address,
))
};

Self {
eth_listener,
Expand Down Expand Up @@ -111,7 +128,7 @@ impl RelayerService for EthToCosmosRelayerModuleServer {
.await
.map_err(|e| tonic::Status::from_error(e.to_string().into()))?,
ibc_version: "2".to_string(),
ibc_contract: self.tx_builder.ics26_router.address().to_string(),
ibc_contract: self.tx_builder.ics26_router_address().to_string(),
}),
}))
}
Expand Down Expand Up @@ -202,3 +219,30 @@ impl ModuleServer for EthToCosmosRelayerModule {
.await
}
}

impl EthToCosmosTxBuilder {
async fn relay_events(
&self,
src_events: Vec<EurekaEvent>,
target_events: Vec<EurekaEvent>,
target_channel_id: String,
) -> Result<Vec<u8>, anyhow::Error> {
match self {
Self::Real(tb) => {
tb.relay_events(src_events, target_events, target_channel_id)
.await
}
Self::Mock(tb) => {
tb.relay_events(src_events, target_events, target_channel_id)
.await
}
}
}

const fn ics26_router_address(&self) -> &Address {
match self {
Self::Real(tb) => tb.ics26_router.address(),
Self::Mock(tb) => tb.ics26_router.address(),
}
}
}

0 comments on commit febaabb

Please sign in to comment.