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

fix(bridge-contracts): fix memo transaction hash encoding #1428

Merged
merged 14 commits into from
Sep 16, 2024
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
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
29 changes: 25 additions & 4 deletions crates/astria-bridge-contracts/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ use astria_withdrawer::{
SequencerWithdrawalFilter,
};
use ethers::{
self,
abi::AbiEncode,
contract::EthEvent,
providers::Middleware,
types::{
Expand Down Expand Up @@ -378,10 +380,16 @@ where
.ok_or_else(|| GetWithdrawalActionsError::log_without_block_number(&log))?
.as_u64();

let rollup_withdrawal_event_id = log
let transaction_hash = log
.transaction_hash
.ok_or_else(|| GetWithdrawalActionsError::log_without_transaction_hash(&log))?
.to_string();
.encode_hex();
let event_index = log
.log_index
.ok_or_else(|| GetWithdrawalActionsError::log_without_log_index(&log))?
.encode_hex();

let rollup_withdrawal_event_id = format!("{transaction_hash}.{event_index}");

let event = decode_log::<Ics20WithdrawalFilter>(log)
.map_err(GetWithdrawalActionsError::decode_log)?;
Expand Down Expand Up @@ -436,10 +444,16 @@ where
.ok_or_else(|| GetWithdrawalActionsError::log_without_block_number(&log))?
.as_u64();

let rollup_withdrawal_event_id = log
let transaction_hash = log
.transaction_hash
.ok_or_else(|| GetWithdrawalActionsError::log_without_transaction_hash(&log))?
.to_string();
.encode_hex();
let event_index = log
.log_index
.ok_or_else(|| GetWithdrawalActionsError::log_without_log_index(&log))?
.encode_hex();

let rollup_withdrawal_event_id = format!("{transaction_hash}.{event_index}");

let event = decode_log::<SequencerWithdrawalFilter>(log)
.map_err(GetWithdrawalActionsError::decode_log)?;
Expand Down Expand Up @@ -502,6 +516,11 @@ impl GetWithdrawalActionsError {
fn log_without_transaction_hash(_log: &Log) -> Self {
Self(GetWithdrawalActionsErrorKind::LogWithoutTransactionHash)
}

// XXX: Somehow identify the log?
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

don't think this error should ever happen, but maybe put the tx hash?

fn log_without_log_index(_log: &Log) -> Self {
Self(GetWithdrawalActionsErrorKind::LogWithoutLogIndex)
}
}

#[derive(Debug, thiserror::Error)]
Expand All @@ -518,6 +537,8 @@ enum GetWithdrawalActionsErrorKind {
LogWithoutBlockNumber,
#[error("log did not contain a transaction hash")]
LogWithoutTransactionHash,
#[error("log did not contain a log index")]
LogWithoutLogIndex,
#[error(transparent)]
CalculateWithdrawalAmount(CalculateWithdrawalAmountError),
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,10 @@ pub use self::{
test_bridge_withdrawer::{
assert_actions_eq,
default_sequencer_address,
make_bridge_unlock_action,
make_ics20_withdrawal_action,
make_erc20_bridge_unlock_action,
make_erc20_ics20_withdrawal_action,
make_native_bridge_unlock_action,
make_native_ics20_withdrawal_action,
TestBridgeWithdrawerConfig,
},
};
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ use astria_core::{
},
},
};
use ethers::types::TransactionReceipt;
use ethers::{
abi::AbiEncode,
types::TransactionReceipt,
};
use futures::Future;
use ibc_types::core::{
channel::ChannelId,
Expand Down Expand Up @@ -427,13 +430,66 @@ impl From<Ics20Withdrawal> for SubsetOfIcs20Withdrawal {
}

#[must_use]
pub fn make_bridge_unlock_action(receipt: &TransactionReceipt) -> Action {
pub fn make_native_bridge_unlock_action(receipt: &TransactionReceipt) -> Action {
let denom = default_native_asset();
let rollup_transaction_hash = receipt.transaction_hash.encode_hex();
let event_index = receipt.logs[0].log_index.unwrap().encode_hex();

let inner = BridgeUnlockAction {
to: default_sequencer_address(),
amount: 1_000_000u128,
rollup_block_number: receipt.block_number.unwrap().as_u64(),
rollup_withdrawal_event_id: format!("{rollup_transaction_hash}.{event_index}"),
memo: String::new(),
fee_asset: denom,
bridge_address: default_bridge_address(),
};
Action::BridgeUnlock(inner)
}

#[must_use]
pub fn make_native_ics20_withdrawal_action(receipt: &TransactionReceipt) -> Action {
let timeout_height = IbcHeight::new(u64::MAX, u64::MAX).unwrap();
let timeout_time = make_ibc_timeout_time();
let denom = default_ibc_asset();
let rollup_transaction_hash = receipt.transaction_hash.encode_hex();
let event_index = receipt.logs[0].log_index.unwrap().encode_hex();

let inner = Ics20Withdrawal {
denom: denom.clone(),
destination_chain_address: default_sequencer_address().to_string(),
return_address: default_bridge_address(),
amount: 1_000_000u128,
memo: serde_json::to_string(&Ics20WithdrawalFromRollup {
memo: "nootwashere".to_string(),
rollup_return_address: receipt.from.to_string(),
rollup_block_number: receipt.block_number.unwrap().as_u64(),
rollup_withdrawal_event_id: format!("{rollup_transaction_hash}.{event_index}"),
})
.unwrap(),
fee_asset: denom,
timeout_height,
timeout_time,
source_channel: "channel-0".parse().unwrap(),
bridge_address: Some(default_bridge_address()),
use_compat_address: false,
};

Action::Ics20Withdrawal(inner)
}

#[must_use]
pub fn make_erc20_bridge_unlock_action(receipt: &TransactionReceipt) -> Action {
let denom = default_native_asset();
let rollup_transaction_hash = receipt.transaction_hash.encode_hex();
// use the second event because the erc20 transfer also emits an event
let event_index = receipt.logs[1].log_index.unwrap().encode_hex();

let inner = BridgeUnlockAction {
to: default_sequencer_address(),
amount: 1_000_000u128,
rollup_block_number: receipt.block_number.unwrap().as_u64(),
rollup_withdrawal_event_id: receipt.transaction_hash.to_string(),
rollup_withdrawal_event_id: format!("{rollup_transaction_hash}.{event_index}"),
memo: String::new(),
fee_asset: denom,
bridge_address: default_bridge_address(),
Expand All @@ -442,10 +498,14 @@ pub fn make_bridge_unlock_action(receipt: &TransactionReceipt) -> Action {
}

#[must_use]
pub fn make_ics20_withdrawal_action(receipt: &TransactionReceipt) -> Action {
pub fn make_erc20_ics20_withdrawal_action(receipt: &TransactionReceipt) -> Action {
let timeout_height = IbcHeight::new(u64::MAX, u64::MAX).unwrap();
let timeout_time = make_ibc_timeout_time();
let denom = default_ibc_asset();
let rollup_transaction_hash = receipt.transaction_hash.encode_hex();
// use the second event because the erc20 transfer also emits an event
let event_index = receipt.logs[1].log_index.unwrap().encode_hex();

let inner = Ics20Withdrawal {
denom: denom.clone(),
destination_chain_address: default_sequencer_address().to_string(),
Expand All @@ -455,7 +515,7 @@ pub fn make_ics20_withdrawal_action(receipt: &TransactionReceipt) -> Action {
memo: "nootwashere".to_string(),
rollup_return_address: receipt.from.to_string(),
rollup_block_number: receipt.block_number.unwrap().as_u64(),
rollup_withdrawal_event_id: receipt.transaction_hash.to_string(),
rollup_withdrawal_event_id: format!("{rollup_transaction_hash}.{event_index}"),
})
.unwrap(),
fee_asset: denom,
Expand Down
48 changes: 35 additions & 13 deletions crates/astria-bridge-withdrawer/tests/blackbox/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ use astria_core::protocol::transaction::v1alpha1::Action;
use helpers::{
assert_actions_eq,
default_sequencer_address,
make_bridge_unlock_action,
make_ics20_withdrawal_action,
make_erc20_bridge_unlock_action,
make_erc20_ics20_withdrawal_action,
make_native_bridge_unlock_action,
make_native_ics20_withdrawal_action,
signed_tx_from_request,
TestBridgeWithdrawerConfig,
};
Expand Down Expand Up @@ -39,7 +41,7 @@ async fn native_sequencer_withdraw_success() {
)
.await;

assert_contract_receipt_action_matches_broadcast_action::<BridgeUnlock>(
assert_contract_receipt_action_matches_broadcast_action::<BridgeUnlock, NativeAsset>(
&broadcast_guard.received_requests().await,
&receipt,
);
Expand Down Expand Up @@ -76,7 +78,7 @@ async fn native_ics20_withdraw_success() {
)
.await;

assert_contract_receipt_action_matches_broadcast_action::<Ics20>(
assert_contract_receipt_action_matches_broadcast_action::<Ics20, NativeAsset>(
&broadcast_guard.received_requests().await,
&receipt,
);
Expand Down Expand Up @@ -119,7 +121,7 @@ async fn erc20_sequencer_withdraw_success() {
)
.await;

assert_contract_receipt_action_matches_broadcast_action::<BridgeUnlock>(
assert_contract_receipt_action_matches_broadcast_action::<BridgeUnlock, Erc20>(
&broadcast_guard.received_requests().await,
&receipt,
);
Expand Down Expand Up @@ -162,34 +164,54 @@ async fn erc20_ics20_withdraw_success() {
)
.await;

assert_contract_receipt_action_matches_broadcast_action::<Ics20>(
assert_contract_receipt_action_matches_broadcast_action::<Ics20, Erc20>(
&broadcast_guard.received_requests().await,
&receipt,
);
}

trait ActionFromReceipt {
trait ActionFromReceipt<TAsset> {
fn action_from_receipt(receipt: &ethers::types::TransactionReceipt) -> Action;
}

struct NativeAsset;
struct Erc20;

struct BridgeUnlock;
impl ActionFromReceipt for BridgeUnlock {
impl ActionFromReceipt<NativeAsset> for BridgeUnlock {
#[track_caller]
fn action_from_receipt(receipt: &ethers::types::TransactionReceipt) -> Action {
make_native_bridge_unlock_action(receipt)
}
}

impl ActionFromReceipt<Erc20> for BridgeUnlock {
#[track_caller]
fn action_from_receipt(receipt: &ethers::types::TransactionReceipt) -> Action {
make_bridge_unlock_action(receipt)
make_erc20_bridge_unlock_action(receipt)
}
}

struct Ics20;
impl ActionFromReceipt for Ics20 {
impl ActionFromReceipt<NativeAsset> for Ics20 {
#[track_caller]
fn action_from_receipt(receipt: &ethers::types::TransactionReceipt) -> Action {
make_native_ics20_withdrawal_action(receipt)
}
}

impl ActionFromReceipt<Erc20> for Ics20 {
#[track_caller]
fn action_from_receipt(receipt: &ethers::types::TransactionReceipt) -> Action {
make_ics20_withdrawal_action(receipt)
make_erc20_ics20_withdrawal_action(receipt)
}
}

#[track_caller]
fn assert_contract_receipt_action_matches_broadcast_action<T: ActionFromReceipt>(
fn assert_contract_receipt_action_matches_broadcast_action<
TAction: ActionFromReceipt<TAsset>,
TAsset,
>(
received_broadcasts: &[wiremock::Request],
receipt: &ethers::types::TransactionReceipt,
) {
Expand All @@ -201,6 +223,6 @@ fn assert_contract_receipt_action_matches_broadcast_action<T: ActionFromReceipt>
.first()
.expect("the signed transaction should contain at least one action");

let expected = T::action_from_receipt(receipt);
let expected = TAction::action_from_receipt(receipt);
assert_actions_eq(&expected, actual);
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl ActionHandler for BridgeUnlockAction {
"rollup withdrawal event id must be non-empty",
);
ensure!(
self.rollup_withdrawal_event_id.len() <= 64,
self.rollup_withdrawal_event_id.len() <= 256,
"rollup withdrawal event id must not be more than 64 bytes",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

update error message to 256

);
ensure!(
Expand Down
Loading