Skip to content

Commit d496380

Browse files
committed
Merge branch 'yuji/validate-sent-coin' (#4733)
* origin/yuji/validate-sent-coin: add test changelog validate the base denom in MsgTransfer
2 parents 0f23902 + f5ddb4b commit d496380

File tree

3 files changed

+200
-3
lines changed

3 files changed

+200
-3
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
- Validate the base denom in MsgTransfer
2+
([\#4733](https://github.com/anoma/namada/issues/4733))

crates/ibc/src/context/token_transfer.rs

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,22 @@ where
5555
self.is_shielded = true;
5656
}
5757

58+
fn validate_sent_coin(&self, coin: &PrefixedCoin) -> Result<(), HostError> {
59+
// The base denom should not be an IBC token address because an IBC
60+
// token address has been already encoded and other chains can't extract
61+
// the trace paths
62+
match Address::decode(coin.denom.base_denom.as_str()) {
63+
Ok(Address::Internal(InternalAddress::IbcToken(_))) => {
64+
Err(HostError::Other {
65+
description: "The base denom should not be an IBC token \
66+
address"
67+
.to_string(),
68+
})
69+
}
70+
_ => Ok(()),
71+
}
72+
}
73+
5874
/// Get the token address and the amount from PrefixedCoin. If the base
5975
/// denom is not an address, it returns `IbcToken`
6076
fn get_token_amount(
@@ -201,9 +217,11 @@ where
201217
_from_account: &Self::AccountId,
202218
_port_id: &PortId,
203219
_channel_id: &ChannelId,
204-
_coin: &PrefixedCoin,
220+
coin: &PrefixedCoin,
205221
_memo: &Memo,
206222
) -> Result<(), HostError> {
223+
self.validate_sent_coin(coin)?;
224+
207225
// validated by Multitoken VP
208226
Ok(())
209227
}
@@ -231,9 +249,11 @@ where
231249
fn burn_coins_validate(
232250
&self,
233251
_account: &Self::AccountId,
234-
_coin: &PrefixedCoin,
252+
coin: &PrefixedCoin,
235253
_memo: &Memo,
236254
) -> Result<(), HostError> {
255+
self.validate_sent_coin(coin)?;
256+
237257
// validated by Multitoken VP
238258
Ok(())
239259
}

crates/ibc/src/vp/mod.rs

Lines changed: 176 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -476,7 +476,9 @@ mod tests {
476476
use std::str::FromStr;
477477

478478
use assert_matches::assert_matches;
479+
use ibc::apps::transfer::types::error::TokenTransferError;
479480
use ibc::core::channel::types::timeout::TimeoutTimestamp;
481+
use ibc::core::host::types::error::HostError;
480482
use ibc::primitives::IntoTimestamp;
481483
use ibc_testkit::testapp::ibc::clients::mock::client_state::{
482484
MOCK_CLIENT_TYPE, MockClientState, client_type,
@@ -485,7 +487,7 @@ mod tests {
485487
use ibc_testkit::testapp::ibc::clients::mock::header::MockHeader;
486488
use namada_core::address::InternalAddress;
487489
use namada_core::address::testing::{
488-
established_address_1, established_address_2, nam,
490+
apfel, established_address_1, established_address_2, nam,
489491
};
490492
use namada_core::borsh::{BorshDeserialize, BorshSerializeExt};
491493
use namada_core::chain::testing::get_dummy_header;
@@ -3507,4 +3509,177 @@ mod tests {
35073509
Ok(_)
35083510
);
35093511
}
3512+
3513+
#[test]
3514+
fn test_send_invalid_base_denom() {
3515+
let mut keys_changed = BTreeSet::new();
3516+
let mut state = init_storage();
3517+
insert_init_client(&mut state);
3518+
3519+
// insert an open connection
3520+
let conn_key = connection_key(&get_connection_id());
3521+
let conn = get_connection(ConnState::Open);
3522+
let bytes = conn.encode_vec();
3523+
let _ = state
3524+
.write_log_mut()
3525+
.write(&conn_key, bytes)
3526+
.expect("write failed");
3527+
// insert an Open channel
3528+
let channel_key = channel_key(&get_port_id(), &get_channel_id());
3529+
let channel = get_channel(ChanState::Open, Order::Unordered);
3530+
let bytes = channel.encode_vec();
3531+
let _ = state
3532+
.write_log_mut()
3533+
.write(&channel_key, bytes)
3534+
.expect("write failed");
3535+
// init balance
3536+
let sender = established_address_1();
3537+
let ibc_token = ibc_token(format!(
3538+
"{}/{}/{}",
3539+
get_port_id(),
3540+
get_channel_id(),
3541+
apfel()
3542+
));
3543+
let balance_key = balance_key(&ibc_token, &sender);
3544+
let amount = Amount::from_u64(100);
3545+
let _ = state
3546+
.write_log_mut()
3547+
.write(&balance_key, amount.serialize_to_vec())
3548+
.expect("write failed");
3549+
state.write_log_mut().commit_batch_and_current_tx();
3550+
state.commit_block().expect("commit failed");
3551+
// for next block
3552+
state
3553+
.in_mem_mut()
3554+
.set_header(get_dummy_header())
3555+
.expect("Setting a dummy header shouldn't fail");
3556+
state.in_mem_mut().begin_block(BlockHeight(2)).unwrap();
3557+
3558+
// prepare data with an invalid message
3559+
let msg = IbcMsgTransfer {
3560+
port_id_on_a: get_port_id(),
3561+
chan_id_on_a: get_channel_id(),
3562+
packet_data: PacketData {
3563+
token: PrefixedCoin {
3564+
// Invalid denom with IbcToken address
3565+
denom: ibc_token.to_string().parse().unwrap(),
3566+
amount: amount.into(),
3567+
},
3568+
sender: sender.to_string().into(),
3569+
receiver: "receiver".to_string().into(),
3570+
memo: "memo".to_string().into(),
3571+
},
3572+
timeout_height_on_b: TimeoutHeight::At(Height::new(0, 10).unwrap()),
3573+
timeout_timestamp_on_b: TimeoutTimestamp::Never,
3574+
};
3575+
3576+
// the sequence send
3577+
let seq_key = next_sequence_send_key(&get_port_id(), &get_channel_id());
3578+
let sequence = get_next_seq(&state, &seq_key);
3579+
let _ = state
3580+
.write_log_mut()
3581+
.write(&seq_key, (u64::from(sequence) + 1).to_be_bytes().to_vec())
3582+
.expect("write failed");
3583+
keys_changed.insert(seq_key);
3584+
// packet commitment
3585+
let packet =
3586+
packet_from_message(&msg, sequence, &get_channel_counterparty());
3587+
let commitment_key =
3588+
commitment_key(&msg.port_id_on_a, &msg.chan_id_on_a, sequence);
3589+
let commitment = commitment(&packet);
3590+
let bytes = commitment.into_vec();
3591+
let _ = state
3592+
.write_log_mut()
3593+
.write(&commitment_key, bytes)
3594+
.expect("write failed");
3595+
keys_changed.insert(commitment_key);
3596+
// withdraw
3597+
let withdraw_key = withdraw_key(&ibc_token);
3598+
let bytes = amount.serialize_to_vec();
3599+
let _ = state
3600+
.write_log_mut()
3601+
.write(&withdraw_key, bytes)
3602+
.expect("write failed");
3603+
keys_changed.insert(withdraw_key);
3604+
// event
3605+
let transfer_event = TransferEvent {
3606+
sender: msg.packet_data.sender.clone(),
3607+
receiver: msg.packet_data.receiver.clone(),
3608+
amount: msg.packet_data.token.amount,
3609+
denom: msg.packet_data.token.denom.clone(),
3610+
memo: msg.packet_data.memo.clone(),
3611+
};
3612+
let event = RawIbcEvent::Module(ModuleEvent::from(transfer_event));
3613+
state
3614+
.write_log_mut()
3615+
.emit_event::<IbcEvent>(event.try_into().unwrap());
3616+
let event = RawIbcEvent::SendPacket(SendPacket::new(
3617+
packet,
3618+
Order::Unordered,
3619+
get_connection_id(),
3620+
));
3621+
let message_event = RawIbcEvent::Message(MessageEvent::Channel);
3622+
state
3623+
.write_log_mut()
3624+
.emit_event::<IbcEvent>(message_event.try_into().unwrap());
3625+
state
3626+
.write_log_mut()
3627+
.emit_event::<IbcEvent>(event.try_into().unwrap());
3628+
let message_event =
3629+
RawIbcEvent::Message(MessageEvent::Module("transfer".to_owned()));
3630+
state
3631+
.write_log_mut()
3632+
.emit_event::<IbcEvent>(message_event.try_into().unwrap());
3633+
3634+
let tx_index = TxIndex::default();
3635+
let tx_code = vec![];
3636+
let tx_data = MsgTransfer::<Transfer> {
3637+
message: msg,
3638+
transfer: None,
3639+
}
3640+
.serialize_to_vec();
3641+
3642+
let mut tx = Tx::new(state.in_mem().chain_id.clone(), None);
3643+
tx.add_code(tx_code, None)
3644+
.add_serialized_data(tx_data)
3645+
.sign_wrapper(keypair_1());
3646+
3647+
let gas_meter = RefCell::new(VpGasMeter::new_from_tx_meter(
3648+
&TxGasMeter::new(TX_GAS_LIMIT, GAS_SCALE),
3649+
));
3650+
let (vp_wasm_cache, _vp_cache_dir) =
3651+
wasm::compilation_cache::common::testing::vp_cache();
3652+
3653+
let verifiers = BTreeSet::new();
3654+
let batched_tx = tx.batch_ref_first_tx().unwrap();
3655+
let ctx = Ctx::new(
3656+
&ADDRESS,
3657+
&state,
3658+
batched_tx.tx,
3659+
batched_tx.cmt,
3660+
&tx_index,
3661+
&gas_meter,
3662+
&keys_changed,
3663+
&verifiers,
3664+
vp_wasm_cache,
3665+
GasMeterKind::MutGlobal,
3666+
);
3667+
let ibc = Ibc::new(ctx);
3668+
// this should fail because of the invalid message
3669+
let result = ibc
3670+
.validate_tx(&batched_tx, &keys_changed, &verifiers)
3671+
.unwrap_err();
3672+
println!("DEBUG: {result:?}");
3673+
let error = result.downcast_ref::<ActionError>().unwrap();
3674+
3675+
// check the error is related to "base denom"
3676+
match error {
3677+
ActionError::TokenTransfer(TokenTransferError::Host(
3678+
HostError::Other { description: e },
3679+
)) => {
3680+
assert!(e.contains("base denom"));
3681+
}
3682+
_ => panic!("Unexpected error: {error}"),
3683+
}
3684+
}
35103685
}

0 commit comments

Comments
 (0)