Skip to content

Commit

Permalink
feat: add channel close feature
Browse files Browse the repository at this point in the history
  • Loading branch information
liyukun committed Nov 16, 2023
1 parent 64978b9 commit 08b6d52
Show file tree
Hide file tree
Showing 5 changed files with 175 additions and 17 deletions.
Binary file modified contracts/ibc-sudt-transfer
Binary file not shown.
Binary file modified contracts/ics-channel
Binary file not shown.
Binary file modified contracts/ics-packet
Binary file not shown.
87 changes: 72 additions & 15 deletions src/transaction.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
use anyhow::{ensure, Context, Result};
use ckb_ics_axon::{
consts::PACKET_CELL_CAPACITY,
get_channel_id_str,
handler::{IbcPacket, PacketStatus},
message::{Envelope, MsgConsumeAckPacket, MsgSendPacket, MsgType, MsgWriteAckPacket},
object::Packet,
message::{
Envelope, MsgChannelCloseInit, MsgConsumeAckPacket, MsgSendPacket, MsgType,
MsgWriteAckPacket,
},
object::{Packet, State},
ChannelArgs,
};
use ckb_types::{
core::{Capacity, TransactionBuilder, TransactionView},
Expand Down Expand Up @@ -59,6 +62,8 @@ pub fn assemble_send_packet_partial_transaction(
},
ack: None,
};
println!("sending packet\n{:?}", packet.packet);

let mut new_channel_state = channel.channel.clone();
new_channel_state.sequence.next_sequence_sends = new_channel_state
.sequence
Expand All @@ -76,12 +81,7 @@ pub fn assemble_send_packet_partial_transaction(
let packet_bytes = rlp::encode(&packet).freeze();
let packet_cell = packed::CellOutput::new_builder()
.lock(config.packet_cell_lock_script(packet.packet.sequence))
.capacity(
Capacity::bytes(PACKET_CELL_CAPACITY as usize)
.unwrap()
.pack(),
)
.build();
.build_exact_capacity(Capacity::bytes(32)?)?;
let packet_witness = packed::WitnessArgs::new_builder()
.output_type(Some(packet_bytes.clone()).pack())
.build();
Expand Down Expand Up @@ -148,12 +148,7 @@ pub fn assemble_write_ack_partial_transaction(
let packet_bytes = rlp::encode(&ack).freeze();
let packet_cell = packed::CellOutput::new_builder()
.lock(config.packet_cell_lock_script(ack.packet.sequence))
.capacity(
Capacity::bytes(PACKET_CELL_CAPACITY as usize)
.unwrap()
.pack(),
)
.build();
.build_exact_capacity(Capacity::bytes(32)?)?;
let packet_witness = packed::WitnessArgs::new_builder()
.input_type(Some(prev_packet_bytes).pack())
.output_type(Some(packet_bytes.clone()).pack())
Expand Down Expand Up @@ -214,3 +209,65 @@ pub fn assemble_consume_ack_packet_partial_transaction(

Ok((tx, envelope))
}

/// Assemble ChannelCloseInit partial transaction. It'll have channel
/// input/output/witness and client/channel contract cell dep.
///
/// The envelope need to be [added](`add_ibc_envelope`) after other witnesses.
///
/// This is a pure function.
pub fn assemble_channel_close_init_partial_transaction(
axon_metadata_cell_dep: packed::CellDep,
channel_contract_cell_dep: packed::CellDep,
config: &Config,
channel: IbcChannelCell,
) -> Result<(TransactionBuilder, Envelope)> {
ensure!(channel.channel.state != State::Closed);

let mut new_channel = channel.channel.clone();
new_channel.state = State::Closed;

let prev_channel_bytes = rlp::encode(&channel.channel).freeze();
let new_channel_bytes = rlp::encode(&new_channel).freeze();
let channel_witness = packed::WitnessArgs::new_builder()
.input_type(Some(prev_channel_bytes).pack())
.output_type(Some(new_channel_bytes.clone()).pack())
.build();

let client_id: [u8; 32] = config
.axon_metadata_type_script()
.args()
.raw_data()
.to_vec()
.as_slice()
.try_into()?;
let new_channel_script_args = ChannelArgs {
client_id,
open: false,
channel_id: new_channel.number,
port_id: config.port_id(),
};
let new_channel_script = packed::Script::from(channel.output.lock.clone())
.as_builder()
.args(new_channel_script_args.to_args().pack())
.build();
let new_channel_cell = packed::CellOutput::new_builder()
.lock(new_channel_script)
.build_exact_capacity(Capacity::bytes(32)?)?;

let tx = TransactionView::new_advanced_builder()
.cell_dep(axon_metadata_cell_dep)
.cell_dep(channel_contract_cell_dep)
// Channel.
.input(channel.as_input())
.output(new_channel_cell)
.output_data(keccak256(&new_channel_bytes)[..].pack())
.witness(channel_witness.as_bytes().pack());

let envelope = Envelope {
msg_type: MsgType::MsgChannelCloseInit,
content: rlp::encode(&MsgChannelCloseInit {}).to_vec(),
};

Ok((tx, envelope))
}
105 changes: 103 additions & 2 deletions tests/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ use forcerelay_ckb_sdk::{
config::{AddressOrScript, Config},
search::{IbcChannelCell, PacketCell},
transaction::{
add_ibc_envelope, assemble_consume_ack_packet_partial_transaction,
assemble_send_packet_partial_transaction, assemble_write_ack_partial_transaction,
add_ibc_envelope, assemble_channel_close_init_partial_transaction,
assemble_consume_ack_packet_partial_transaction, assemble_send_packet_partial_transaction,
assemble_write_ack_partial_transaction,
},
utils::keccak256,
};
Expand Down Expand Up @@ -346,6 +347,106 @@ fn test_consume_ack_packet() -> Result<()> {
Ok(())
}

#[test]
fn test_channel_close_init() -> Result<()> {
let mut context = Context::default();

let always_success_contract = context.deploy_cell(ALWAYS_SUCCESS.clone());
let always_success_lock = context
.build_script(&always_success_contract, Bytes::new())
.unwrap();
let always_success_cell = context.create_cell(
packed::CellOutput::new_builder()
.lock(always_success_lock.clone())
.build(),
Bytes::new(),
);

let axon_metadata_data = Metadata::new_builder().build().as_bytes();
let axon_metadata_cell = context.deploy_cell(axon_metadata_data);
let axon_metadata_type_script = context
.get_cell(&axon_metadata_cell)
.unwrap()
.0
.type_()
.to_opt()
.unwrap();
let axon_metadata_cell_dep = packed::CellDep::new_builder()
.out_point(axon_metadata_cell)
.build();

let channel_contract = context.deploy_cell(Bytes::from_static(include_bytes!(
"../contracts/ics-channel"
)));
let channel_contract_type_id_args: [u8; 32] = get_type_id_args(&context, &channel_contract);
let channel_contract_cell_dep = packed::CellDep::new_builder()
.out_point(channel_contract)
.build();

let packet_contract = context.deploy_cell(Bytes::from_static(include_bytes!(
"../contracts/ics-packet"
)));
let packet_contract_type_id_args: [u8; 32] = get_type_id_args(&context, &packet_contract);

let channel_id = 8;

let config = Config {
axon_metadata_type_script: AddressOrScript::Script(axon_metadata_type_script.into()),
channel_contract_type_id_args: channel_contract_type_id_args.into(),
channel_id,
confirmations: 1,
packet_contract_type_id_args: packet_contract_type_id_args.into(),
module_lock_script: AddressOrScript::Script(always_success_lock.into()),
};

let current_channel_state = IbcChannel {
number: channel_id,
state: State::Open,
port_id: config.port_id_string(),
..Default::default()
};
let current_channel_state_bytes = rlp::encode(&current_channel_state).freeze();
let channel_output = packed::CellOutput::new_builder()
.lock(config.channel_cell_lock_script())
.build_exact_capacity(Capacity::bytes(32).unwrap())
.unwrap();
let channel_out_point = context.create_cell(
channel_output.clone(),
Bytes::copy_from_slice(&keccak256(&current_channel_state_bytes)),
);
let channel_cell = IbcChannelCell {
channel: current_channel_state,
out_point: channel_out_point.into(),
output: channel_output.into(),
};

let (tx, envelope) = assemble_channel_close_init_partial_transaction(
axon_metadata_cell_dep,
channel_contract_cell_dep,
&config,
channel_cell,
)?;
let tx = tx.input(
packed::CellInput::new_builder()
.previous_output(always_success_cell)
.build(),
);
let tx = add_ibc_envelope(tx, &envelope).build();
let tx = context.complete_tx(tx);

// Test cell parsing.
IbcChannelCell::parse(&tx.clone().into(), 0)?;

context.set_capture_debug(true);
let r = context.verify_tx(&tx, u64::MAX);
for m in context.captured_messages() {
println!("{}", m.message);
}
r?;

Ok(())
}

/// # Panics
///
/// If the out point doesn't exist.
Expand Down

0 comments on commit 08b6d52

Please sign in to comment.