Skip to content

Commit

Permalink
feat: adjust tx proof calculation
Browse files Browse the repository at this point in the history
  • Loading branch information
liyukun committed Dec 10, 2023
1 parent 4b47f70 commit 69672ea
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 67 deletions.
4 changes: 1 addition & 3 deletions .github/workflows/ibc-test.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,7 @@ jobs:
timeout-minutes: 75
env:
SRC_DIR: ${{ github.workspace }}/ibc-test-src
# https://github.com/axonweb3/axon/commits/forcerelay-dev
# commit: 7ae97ae5ffd3429746315acea77a742f6bdb6f0c
AXON_COMMIT: forcerelay-dev
AXON_COMMIT: dd35f500fce0ec3adf28b59fea8c431a4a4087c8
IBC_CONTRACT_COMMIT: c5417573ec15c8aaab048caa1ec5f3bd50c2170e
strategy:
fail-fast: false
Expand Down
2 changes: 2 additions & 0 deletions crates/relayer/src/chain/ckb/communication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ pub trait CkbReader {

fn get_tip_header(&self) -> Response<HeaderView>;

fn get_header(&self, hash: &H256) -> Response<Option<HeaderView>>;

fn get_transaction(&self, hash: &H256) -> Response<Option<TransactionWithStatusResponse>>;

fn get_live_cell(&self, out_point: &OutPoint, with_data: bool) -> Response<CellWithStatus>;
Expand Down
4 changes: 4 additions & 0 deletions crates/relayer/src/chain/ckb/mock_rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ impl CkbReader for RpcClient {
Box::pin(async { Ok(resp) })
}

fn get_header(&self, _hash: &H256) -> Rpc<Option<HeaderView>> {
todo!()
}

fn get_transaction(&self, hash: &H256) -> Rpc<Option<TransactionWithStatusResponse>> {
let transaction = ResponseFormat::<TransactionView>::json(Default::default());
let resp = TransactionWithStatusResponse {
Expand Down
4 changes: 4 additions & 0 deletions crates/relayer/src/chain/ckb/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,10 @@ impl CkbReader for RpcClient {
jsonrpc!("get_tip_header", Target::CKB, self, HeaderView).boxed()
}

fn get_header(&self, hash: &H256) -> Rpc<Option<HeaderView>> {
jsonrpc!("get_header", Target::CKB, self, Option<HeaderView>, hash).boxed()
}

fn get_transaction(&self, hash: &H256) -> Rpc<Option<TransactionWithStatusResponse>> {
jsonrpc!(
"get_transaction",
Expand Down
30 changes: 5 additions & 25 deletions crates/relayer/src/chain/ckb4ibc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ use self::monitor::{Ckb4IbcEventMonitor, WriteAckMonitorCmd};
use self::utils::{
fetch_transaction_by_hash, generate_ibc_packet_event, generate_tx_proof_from_block,
get_channel_search_key, get_encoded_object, get_ibc_merkle_proof, get_packet_search_key,
get_prefix_search_key, get_search_key_with_sudt, transaction_to_event,
get_prefix_search_key, get_search_key_with_sudt, parse_transaction, transaction_to_event,
};

use super::ckb::rpc_client::RpcClient;
Expand Down Expand Up @@ -306,12 +306,7 @@ impl Ckb4IbcChain {
.ok_or(Error::query("ckb transaction unready".to_string()))?
.transaction
.unwrap();
let tx = match tx_resp.inner {
ckb_jsonrpc_types::Either::Left(tx) => tx,
ckb_jsonrpc_types::Either::Right(json_bytes) => {
serde_json::from_slice(json_bytes.as_bytes()).unwrap()
}
};
let tx = parse_transaction(tx_resp);
let channel_end = extract_channel_end_from_tx(&tx)?;
let input = CellInput::new_builder()
.previous_output(cell.out_point.clone().into())
Expand Down Expand Up @@ -373,12 +368,7 @@ impl Ckb4IbcChain {
.expect("empty transaction response")
.transaction
.expect("empty transaction view");
let tx = match tx.inner {
ckb_jsonrpc_types::Either::Left(tx) => tx,
ckb_jsonrpc_types::Either::Right(bytes) => {
serde_json::from_slice::<TransactionView>(bytes.as_bytes()).unwrap()
}
};
let tx = parse_transaction(tx);
let (connections, ibc_connection) = extract_connections_from_tx(&tx, &prefix)?;
cache.insert(
client_type,
Expand Down Expand Up @@ -1067,12 +1057,7 @@ impl ChainEndpoint for Ckb4IbcChain {
None
})
.flat_map(|tx| {
let tx = match tx.transaction.unwrap().inner {
ckb_jsonrpc_types::Either::Left(tx) => tx,
ckb_jsonrpc_types::Either::Right(bytes) => {
serde_json::from_slice::<TransactionView>(bytes.as_bytes()).unwrap()
}
};
let tx = parse_transaction(tx.transaction.unwrap());
extract_channel_end_from_tx(&tx)
})
.map(|(channel, _)| channel)
Expand Down Expand Up @@ -1348,12 +1333,7 @@ impl ChainEndpoint for Ckb4IbcChain {
let Some(tx) = tx.transaction else {
return Ok(vec![]);
};
let tx = match tx.inner {
ckb_jsonrpc_types::Either::Left(tx) => tx,
ckb_jsonrpc_types::Either::Right(json_bytes) => {
serde_json::from_slice(json_bytes.as_bytes()).unwrap()
}
};
let tx = parse_transaction(tx);
let event = transaction_to_event(&tx, &prefix)?;
vec![IbcEventWithHeight {
event,
Expand Down
123 changes: 84 additions & 39 deletions crates/relayer/src/chain/ckb4ibc/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ use ckb_ics_axon::consts::CHANNEL_ID_PREFIX;
use ckb_ics_axon::handler::IbcPacket;
use ckb_ics_axon::message::MsgType;
use ckb_ics_axon::{ChannelArgs, PacketArgs};
use ckb_jsonrpc_types::{TransactionAndWitnessProof, TransactionView};
use ckb_jsonrpc_types::{
MerkleProof as JsonMerkleProof, ResponseFormat, TransactionAndWitnessProof, TransactionView,
};
use ckb_sdk::constants::TYPE_ID_CODE_HASH;
use ckb_sdk::rpc::ckb_indexer::ScriptSearchMode;
use ckb_sdk::rpc::ckb_light_client::{ScriptType, SearchKey};
Expand All @@ -20,7 +22,7 @@ use ckb_sdk::NetworkType;
use ckb_types::core::ScriptHashType;
use ckb_types::packed::{Byte32, Bytes, BytesOpt, OutPoint, Script, Transaction};
use ckb_types::prelude::{Builder, Entity, Pack, Unpack};
use ckb_types::utilities::merkle_root;
use ckb_types::utilities::{merkle_root, MerkleProof};
use ckb_types::{h256, H256};
use ethers::abi::AbiEncode;
use ethers::contract::{EthAbiCodec, EthAbiType};
Expand Down Expand Up @@ -526,6 +528,15 @@ pub fn get_ibc_merkle_proof(height: Height, encoded: Vec<u8>) -> Result<Proofs,
.map_err(|err| Error::other_error(err.to_string()))
}

pub fn parse_transaction(tx: ResponseFormat<TransactionView>) -> TransactionView {
match tx.inner {
ckb_jsonrpc_types::Either::Left(tx) => tx,
ckb_jsonrpc_types::Either::Right(bytes) => {
serde_json::from_slice::<TransactionView>(bytes.as_bytes()).unwrap()
}
}
}

#[derive(EthAbiCodec, EthAbiType)]
struct AxonObjectProof {
pub ckb_transaction: Vec<u8>,
Expand All @@ -537,75 +548,109 @@ pub async fn generate_tx_proof_from_block(
rpc_client: &impl CkbReader,
tx_hash: &H256,
) -> Result<Option<Proofs>, Error> {
let block_hash = rpc_client
let result = rpc_client
.get_transaction(tx_hash)
.await?
.map(|v| v.tx_status.block_hash);
let Some(Some(block_hash)) = block_hash else {
.map(|v| (v.tx_status.block_hash, v.transaction));
let Some((Some(block_hash), Some(transaction))) = result else {
return Err(Error::other_error(format!(
"cannot find block_hash from tx {}",
hex::encode(tx_hash)
)));
};

// collect transaction hashes from block
let mut transaction: Option<Transaction> = None;
let block = rpc_client.get_block(&block_hash).await?;
let tx_hashes = block
.transactions
.iter()
.map(|tx| {
if &tx.hash == tx_hash {
transaction = Some(tx.inner.clone().into());
}
tx.hash.clone()
})
.collect_vec();
let witness_hashes = block
.transactions
.into_iter()
.map(|tx| Transaction::from(tx.inner).calc_witness_hash().unpack())
.collect_vec();

let Some(transaction) = transaction else {
return Ok(None);
};
// let mut transaction: Option<Transaction> = None;
// let block = rpc_client.get_block(&block_hash).await?;
// let tx_hashes = block
// .transactions
// .iter()
// .map(|tx| {
// if &tx.hash == tx_hash {
// transaction = Some(tx.inner.clone().into());
// }
// tx.hash.clone()
// })
// .collect_vec();
// let witness_hashes = block
// .transactions
// .into_iter()
// .map(|tx| Transaction::from(tx.inner).calc_witness_hash().unpack())
// .collect_vec();

// let Some(transaction) = transaction else {
// return Ok(None);
// };

let header = rpc_client
.get_header(&block_hash)
.await?
.expect("invalid block_hash");

// generate transaction proof
let TransactionAndWitnessProof {
block_hash,
transactions_proof: _,
witnesses_proof: proof,
transactions_proof,
witnesses_proof,
} = rpc_client
.get_transaction_and_witness_proof(tx_hashes.clone(), block.header.hash)
.get_transaction_and_witness_proof(vec![tx_hash.clone()], block_hash)
.await?;

let raw_transaction_root = merkle_root(&tx_hashes.iter().map(Pack::pack).collect_vec());
let witnesses_root = merkle_root(&witness_hashes.iter().map(Pack::pack).collect_vec());
let transaction = Transaction::from(parse_transaction(transaction).inner);
let transaction_hash = transaction.calc_tx_hash();
let witness_hash = transaction.calc_witness_hash();

let raw_transactions_root = jsonrpc_merkle_root(&transactions_proof, vec![transaction_hash])?;
let witnesses_root = jsonrpc_merkle_root(&witnesses_proof, vec![witness_hash.clone()])?;

let transactions_root = merkle_root(&[raw_transactions_root.pack(), witnesses_root.pack()]);
if transactions_root.unpack() != header.inner.transactions_root {
return Err(Error::other_error(
"unexpected transactions_root".to_owned(),
));
}

let proof_payload = VerifyProofPayload {
verify_type: 1, // to verify witness
transactions_root: block.header.inner.transactions_root.into(),
witnesses_root: witnesses_root.unpack().into(),
raw_transactions_root: raw_transaction_root.unpack().into(),
transactions_root: header.inner.transactions_root.into(),
witnesses_root,
raw_transactions_root,
proof: Proof {
indices: proof.indices.into_iter().map(Into::into).collect(),
lemmas: proof.lemmas.into_iter().map(Into::into).collect(),
leaves: witness_hashes.into_iter().map(Into::into).collect(),
indices: witnesses_proof
.indices
.into_iter()
.map(Into::into)
.collect_vec(),
lemmas: witnesses_proof.lemmas.into_iter().map(Into::into).collect(),
leaves: vec![witness_hash.unpack().into()],
},
};

verify_proof(proof_payload.clone())
.map_err(|err| Error::other_error(format!("proof payload verify failed: {err}")))?;

let object_proof = AxonObjectProof {
ckb_transaction: transaction.as_slice().to_owned(),
ckb_transaction: transaction.as_slice().into(),
block_hash: block_hash.into(),
proof_payload,
};

// assemble ibc-compatible proof
let block_number = Height::from_noncosmos_height(block.header.inner.number.into());
let block_number = Height::from_noncosmos_height(header.inner.number.into());
let proofs = get_ibc_merkle_proof(block_number, object_proof.encode())?;
Ok(Some(proofs))
}

fn jsonrpc_merkle_root(
merkle_proof: &JsonMerkleProof,
leaves: Vec<Byte32>,
) -> Result<[u8; 32], Error> {
let proof = merkle_proof.clone();
MerkleProof::new(
proof.indices.into_iter().map(Into::into).collect(),
proof.lemmas.into_iter().map(|v| v.pack()).collect(),
)
.root(&leaves)
.map(|v| v.unpack().into())
.ok_or(Error::other_error("invalid merkle proof".to_owned()))
}
11 changes: 11 additions & 0 deletions tools/ibc-test/src/rpc_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,17 @@ impl CkbReader for RpcClient {
jsonrpc!("get_tip_header", Target::CKB, self, HeaderView).boxed()
}

fn get_header(&self, hash: &H256) -> Rpc<Option<HeaderView>> {
jsonrpc!(
"get_tip_header",
Target::CKB,
self,
Option<HeaderView>,
hash
)
.boxed()
}

fn get_transaction(&self, hash: &H256) -> Rpc<Option<TransactionWithStatusResponse>> {
jsonrpc!(
"get_transaction",
Expand Down

0 comments on commit 69672ea

Please sign in to comment.