diff --git a/contracts/ibc-sudt-transfer b/contracts/ibc-sudt-transfer index d4a176a..fddc24f 100644 Binary files a/contracts/ibc-sudt-transfer and b/contracts/ibc-sudt-transfer differ diff --git a/contracts/ics-channel b/contracts/ics-channel index 3d2b990..27a709f 100644 Binary files a/contracts/ics-channel and b/contracts/ics-channel differ diff --git a/contracts/ics-packet b/contracts/ics-packet index ccb3df1..8a53bee 100644 Binary files a/contracts/ics-packet and b/contracts/ics-packet differ diff --git a/src/transaction.rs b/src/transaction.rs index 31d8117..2b9b1be 100644 --- a/src/transaction.rs +++ b/src/transaction.rs @@ -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}, @@ -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 @@ -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(); @@ -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()) @@ -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)) +} diff --git a/tests/transaction.rs b/tests/transaction.rs index ff87ae7..3cd2bb3 100644 --- a/tests/transaction.rs +++ b/tests/transaction.rs @@ -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, }; @@ -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(¤t_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(¤t_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.