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

When possible, use IBC types from ibc-rs #150

Open
Tracked by #123
soareschen opened this issue Dec 19, 2024 · 1 comment
Open
Tracked by #123

When possible, use IBC types from ibc-rs #150

soareschen opened this issue Dec 19, 2024 · 1 comment
Assignees
Labels
Milestone

Comments

@soareschen
Copy link
Collaborator

We should reuse types like ClientId, ConnectionId, Packet, etc. That would make it significantly easier to reuse the Cosmos implementations, which assume that the counterparty has these types. Otherwise, we have to copy over the Cosmos implementations, and perform minor tweaks to convert between the types.

The way we implement Cairo encoding means that we can easily implement them for non-owned types from ibc-rs. The main difference is that we will need to implement FieldGetter for the ibc-rs types, and then use encoders like EncodeFieldWithGetter to pass in the field getters explicitly.

@rnbguy
Copy link
Member

rnbguy commented Jan 10, 2025

I will list down some current examples of dirty hacks. This issue should resolve them. Almost all Cairo domain types should reuse ibc-rs types.

Packet

fn from_cosmos_to_cairo_packet<Encoding>(packet: &IbcPacket, encoding: &Encoding) -> CairoPacket
where
Encoding: CanEncode<ViaCairo, CairoTransferPacketData> + HasEncodedType<Encoded = Vec<Felt>>,
{
let sequence = packet.seq_on_a.value();
let src_port_id = packet.port_id_on_a.to_string();
let src_channel_id = packet.chan_id_on_a.to_string();
let dst_port_id = packet.port_id_on_b.to_string();
let dst_channel_id = packet.chan_id_on_b.to_string();
// TODO(rano): the packet data needs to serialized to Vec<felt>.
// to do that, we assume PacketData struct (i.e. ICS20) and construct it.
// ideally, Cairo contract should accept the serialized data directly.
// deserialize to ibc ics20 packet message
let ibc_ics20_packet_data: IbcIcs20PacketData = serde_json::from_slice(&packet.data).unwrap();
// convert to cairo packet message
// TODO(rano): can't iter. need fix at ibc-rs side
// for now, using json hack
let trace_path_json =
serde_json::to_string(&ibc_ics20_packet_data.token.denom.trace_path).unwrap();
#[derive(serde::Deserialize)]
struct DummyTracePath {
pub port_id: String,
pub channel_id: String,
}
let trace_path: Vec<DummyTracePath> = serde_json::from_str(&trace_path_json).unwrap();
let denom = PrefixedDenom {
trace_path: trace_path
.into_iter()
.map(
|DummyTracePath {
port_id,
channel_id,
}| TracePrefix {
port_id,
channel_id,
},
)
.collect(),
base: Denom::Hosted(
ibc_ics20_packet_data
.token
.denom
.base_denom
.as_str()
.to_string(),
),
};
let amount = {
let bytes = ibc_ics20_packet_data.token.amount.as_ref().0;
crypto_bigint::U256::from(bytes).into()
};
let sender_string = ibc_ics20_packet_data.sender.as_ref().to_string();
let receiver_string = ibc_ics20_packet_data.receiver.as_ref().to_string();
// TODO(rano): the following is a hack
// do we really need Participant variants?
let sender = sender_string
.parse()
.map(Participant::Native)
.unwrap_or_else(|_| Participant::External(sender_string));
let receiver = receiver_string
.parse()
.map(Participant::Native)
.unwrap_or_else(|_| Participant::External(receiver_string));
match (&sender, &receiver) {
(Participant::Native(_), Participant::Native(_)) => {
panic!("Native to Native transfer is not supported")
}
(Participant::External(_), Participant::External(_)) => {
panic!("External to External transfer is not supported")
}
_ => {}
}
let memo = ibc_ics20_packet_data.memo.as_ref().to_string();
let cairo_ics20_packet_data = CairoTransferPacketData {
denom,
amount,
sender,
receiver,
memo,
};
// serialize to vec<felt>
let data_felt = encoding.encode(&cairo_ics20_packet_data).unwrap();
let timeout_height = match packet.timeout_height_on_b {
TimeoutHeight::Never => CairoHeight {
revision_number: 0,
revision_height: 0,
},
TimeoutHeight::At(height) => CairoHeight {
revision_number: height.revision_number(),
revision_height: height.revision_height(),
},
};
let timeout_timestamp = match packet.timeout_timestamp_on_b {
TimeoutTimestamp::Never => 0,
TimeoutTimestamp::At(timeout_timestamp) => timeout_timestamp.nanoseconds() / 1_000_000_000,
};
CairoPacket {
sequence,
src_port_id,
src_channel_id,
dst_port_id,
dst_channel_id,
data: data_felt,
timeout_height,
timeout_timestamp,
}
}

TracePath

let trace_path: Vec<DummyTracePath> = serde_json::from_str(&trace_path_json).unwrap();

Connection Version

// TODO(rano): dirty hack; make fields public at ibc-rs and tidy it up
let json_connection_version =
serde_json::to_value(&init_connection_options.connection_version)
.map_err(Chain::raise_error)?;
let identifier = json_connection_version
.pointer("/identifier")
.ok_or_else(|| Chain::raise_error("connection version does not have a features field"))?
.as_str()
.ok_or_else(|| Chain::raise_error("connection version identifier is not a string"))?;
let features = json_connection_version
.pointer("/features")
.ok_or_else(|| Chain::raise_error("connection version does not have a features field"))?
.as_array()
.ok_or_else(|| Chain::raise_error("connection version features is not an array"))?
.iter()
.map(|value| {
value
.as_str()
.ok_or_else(|| Chain::raise_error("connection version feature is not a string"))
})
.collect::<Result<Vec<&str>, Chain::Error>>()?;
if features.len() != 2 {
return Err(Chain::raise_error(
"connection version features must have 2 elements",
));
}
let cairo_connection_version = ConnectionVersion {
identifier: identifier.to_string(),
features: [features[0].to_string(), features[1].to_string()],
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants