From 85904caecae0f6ffa13490ba56cbfc81d1ce4e20 Mon Sep 17 00:00:00 2001 From: quake Date: Thu, 20 Jun 2024 14:46:24 +0900 Subject: [PATCH 1/2] fix: try to resolve xudt compatibility issue --- contracts/funding-lock/README.md | 1 + contracts/funding-lock/src/main.rs | 13 +++++++++---- tests/src/tests.rs | 1 + 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/contracts/funding-lock/README.md b/contracts/funding-lock/README.md index 499c30b..94147ee 100644 --- a/contracts/funding-lock/README.md +++ b/contracts/funding-lock/README.md @@ -4,6 +4,7 @@ This is a simple funding lock script for ckb fiber network. It utilizes the [ckb The lock script args is a blake160 hash of the aggregated public key of the two parties, to unlock this lock, the transaction must provide following fields in the witness: +- `empty_witness_args`: 16 bytes, fixed to 0x10000000100000001000000010000000, for compatibility with the xudt - `version`: 8 bytes, u64 in little-endian - `funding_out_point`: 36 bytes, out point of the funding transaction - `pubkey`: 32 bytes, x only aggregated public key diff --git a/contracts/funding-lock/src/main.rs b/contracts/funding-lock/src/main.rs index 1f296db..b258697 100644 --- a/contracts/funding-lock/src/main.rs +++ b/contracts/funding-lock/src/main.rs @@ -34,6 +34,7 @@ pub enum Error { // Add customized errors here... MultipleInputs, WitnessLenError, + EmptyWitnessArgsError, FundingOutPointError, AuthError, } @@ -63,18 +64,22 @@ fn auth() -> Result<(), Error> { return Err(Error::MultipleInputs); } let witness = load_witness(0, Source::GroupInput)?; - if witness.len() != 8 + 36 + 32 + 64 { + if witness.len() != 16 + 8 + 36 + 32 + 64 { return Err(Error::WitnessLenError); } let tx_hash = load_tx_hash()?; - let version = witness[0..8].to_vec(); - let funding_out_point = witness[8..44].to_vec(); + let empty_witness_args = witness[0..16].to_vec(); + let version = witness[16..24].to_vec(); + let funding_out_point = witness[24..60].to_vec(); let input_out_point = load_input_out_point(0, Source::GroupInput)?; + if empty_witness_args != [16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0] { + return Err(Error::EmptyWitnessArgsError); + } if input_out_point.as_slice() != funding_out_point.as_slice() { return Err(Error::FundingOutPointError); } // Schnorr signature cannot recover the public key, so we need to provide the public key - let pubkey_and_signature = witness[44..].to_vec(); + let pubkey_and_signature = witness[60..].to_vec(); let message = blake2b_256([version, funding_out_point, tx_hash.to_vec()].concat()); let mut pubkey_hash = [0u8; 20]; diff --git a/tests/src/tests.rs b/tests/src/tests.rs index ce943b7..9782d88 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -152,6 +152,7 @@ fn test_funding_lock() { println!("signature: {:?}", aggregated_signature_1.to_bytes()); let witness = [ + [16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0].to_vec(), version.to_vec(), funding_out_point.to_vec(), x_only_pub_key.to_vec(), From 04dcdaf25f66e3717f6f3bc4064fff519300ad50 Mon Sep 17 00:00:00 2001 From: quake Date: Fri, 21 Jun 2024 07:36:35 +0900 Subject: [PATCH 2/2] fix: try to resolve xudt compatibility issue --- contracts/commitment-lock/README.md | 1 + contracts/commitment-lock/src/main.rs | 12 ++- contracts/funding-lock/src/main.rs | 26 ++++--- tests/src/tests.rs | 107 ++++++++++++++++++++++---- 4 files changed, 121 insertions(+), 25 deletions(-) diff --git a/contracts/commitment-lock/README.md b/contracts/commitment-lock/README.md index 7539906..4b32a8c 100644 --- a/contracts/commitment-lock/README.md +++ b/contracts/commitment-lock/README.md @@ -4,6 +4,7 @@ This is a simple commitment lock script for ckb fiber network. The lock script args is the hash result of blake160(local_delay_epoch || local_delay_pubkey_hash || revocation_pubkey_hash || N * pending_htlc), to unlock this lock, the transaction must provide following fields in the witness: +- `empty_witness_args`: 16 bytes, fixed to 0x10000000100000001000000010000000, for compatibility with the xudt - `local_delay_epoch`: 8 bytes, u64 in little endian, must be a relative EpochNumberWithFraction - `local_delay_pubkey_hash`: 20 bytes, hash result of blake160(local_delay_pubkey) - `revocation_pubkey_hash`: 20 bytes, hash result of blake160(revocation_pubkey) diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index ed798c0..d60d2d8 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -40,6 +40,7 @@ pub enum Error { InvalidHtlcType, ArgsLenError, WitnessLenError, + EmptyWitnessArgsError, WitnessHashError, OutputCapacityError, OutputLockError, @@ -68,6 +69,8 @@ pub fn program_entry() -> i8 { } } +// a placeholder for empty witness args, to resolve the issue of xudt compatibility +const EMPTY_WITNESS_ARGS: [u8; 16] = [16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0]; // min witness script length: 8 (local_delay_epoch) + 20 (local_delay_pubkey_hash) + 20 (revocation_pubkey_hash) = 48 const MIN_WITNESS_SCRIPT_LEN: usize = 48; // HTLC script length: 1 (htlc_type) + 16 (payment_amount) + 20 (payment_hash) + 20 (remote_htlc_pubkey_hash) + 20 (local_htlc_pubkey_hash) + 8 (htlc_expiry) = 85 @@ -120,7 +123,14 @@ fn auth() -> Result<(), Error> { if args.len() != 20 { return Err(Error::ArgsLenError); } - let witness = load_witness(0, Source::GroupInput)?; + let mut witness = load_witness(0, Source::GroupInput)?; + if witness + .drain(0..EMPTY_WITNESS_ARGS.len()) + .collect::>() + != EMPTY_WITNESS_ARGS + { + return Err(Error::EmptyWitnessArgsError); + } let witness_len = witness.len(); if witness_len < MIN_WITNESS_LEN { return Err(Error::WitnessLenError); diff --git a/contracts/funding-lock/src/main.rs b/contracts/funding-lock/src/main.rs index b258697..69ff725 100644 --- a/contracts/funding-lock/src/main.rs +++ b/contracts/funding-lock/src/main.rs @@ -12,7 +12,7 @@ ckb_std::entry!(program_entry); #[cfg(not(test))] default_alloc!(); -use alloc::ffi::CString; +use alloc::{ffi::CString, vec::Vec}; use ckb_std::{ ckb_constants::Source, ckb_types::{bytes::Bytes, core::ScriptHashType, prelude::*}, @@ -58,28 +58,34 @@ pub fn program_entry() -> i8 { } } +// a placeholder for empty witness args, to resolve the issue of xudt compatibility +const EMPTY_WITNESS_ARGS: [u8; 16] = [16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0]; + fn auth() -> Result<(), Error> { // funding lock will be unlocked by the commitment transaction, it should only have one input if load_input_since(1, Source::GroupInput).is_ok() { return Err(Error::MultipleInputs); } - let witness = load_witness(0, Source::GroupInput)?; - if witness.len() != 16 + 8 + 36 + 32 + 64 { + let mut witness = load_witness(0, Source::GroupInput)?; + if witness + .drain(0..EMPTY_WITNESS_ARGS.len()) + .collect::>() + != EMPTY_WITNESS_ARGS + { + return Err(Error::EmptyWitnessArgsError); + } + if witness.len() != 8 + 36 + 32 + 64 { return Err(Error::WitnessLenError); } let tx_hash = load_tx_hash()?; - let empty_witness_args = witness[0..16].to_vec(); - let version = witness[16..24].to_vec(); - let funding_out_point = witness[24..60].to_vec(); + let version = witness[0..8].to_vec(); + let funding_out_point = witness[8..44].to_vec(); let input_out_point = load_input_out_point(0, Source::GroupInput)?; - if empty_witness_args != [16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0] { - return Err(Error::EmptyWitnessArgsError); - } if input_out_point.as_slice() != funding_out_point.as_slice() { return Err(Error::FundingOutPointError); } // Schnorr signature cannot recover the public key, so we need to provide the public key - let pubkey_and_signature = witness[60..].to_vec(); + let pubkey_and_signature = witness[44..].to_vec(); let message = blake2b_256([version, funding_out_point, tx_hash.to_vec()].concat()); let mut pubkey_hash = [0u8; 20]; diff --git a/tests/src/tests.rs b/tests/src/tests.rs index 9782d88..d920f89 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -15,8 +15,8 @@ use secp256k1::{ }; const MAX_CYCLES: u64 = 10_000_000; - const BYTE_SHANNONS: u64 = 100_000_000; +const EMPTY_WITNESS_ARGS: [u8; 16] = [16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0]; #[test] fn test_funding_lock() { @@ -152,7 +152,7 @@ fn test_funding_lock() { println!("signature: {:?}", aggregated_signature_1.to_bytes()); let witness = [ - [16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0, 16, 0, 0, 0].to_vec(), + EMPTY_WITNESS_ARGS.to_vec(), version.to_vec(), funding_out_point.to_vec(), x_only_pub_key.to_vec(), @@ -251,7 +251,13 @@ fn test_commitment_lock_no_pending_htlcs() { .sign_recoverable(&message.into()) .unwrap() .serialize(); - let witness = [witness_script.clone(), vec![0xFF], signature].concat(); + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + witness_script.clone(), + vec![0xFF], + signature, + ] + .concat(); let tx = tx.as_advanced_builder().witness(witness.pack()).build(); println!("tx: {:?}", tx); @@ -285,7 +291,13 @@ fn test_commitment_lock_no_pending_htlcs() { .sign_recoverable(&message.into()) .unwrap() .serialize(); - let witness = [witness_script, vec![0xFF], signature].concat(); + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + witness_script, + vec![0xFF], + signature, + ] + .concat(); let tx = tx.as_advanced_builder().witness(witness.pack()).build(); println!("tx: {:?}", tx); @@ -401,7 +413,13 @@ fn test_commitment_lock_with_two_pending_htlcs() { .sign_recoverable(&message.into()) .unwrap() .serialize(); - let witness = [witness_script.clone(), vec![0xFF], signature].concat(); + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + witness_script.clone(), + vec![0xFF], + signature, + ] + .concat(); let tx = tx.as_advanced_builder().witness(witness.pack()).build(); println!("tx: {:?}", tx); @@ -435,7 +453,13 @@ fn test_commitment_lock_with_two_pending_htlcs() { .sign_recoverable(&message.into()) .unwrap() .serialize(); - let witness = [witness_script.clone(), vec![0xFF], signature].concat(); + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + witness_script.clone(), + vec![0xFF], + signature, + ] + .concat(); let tx = tx.as_advanced_builder().witness(witness.pack()).build(); println!("tx: {:?}", tx); @@ -489,6 +513,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { .unwrap() .serialize(); let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), witness_script.clone(), vec![0x00], signature.clone(), @@ -504,6 +529,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { // sign with remote_htlc_pubkey and wrong preimage should fail let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), witness_script.clone(), vec![0x00], signature.clone(), @@ -520,7 +546,13 @@ fn test_commitment_lock_with_two_pending_htlcs() { println!("error: {}", error); // sign with remote_htlc_pubkey and empty preimage should fail - let witness = [witness_script.clone(), vec![0x00], signature].concat(); + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + witness_script.clone(), + vec![0x00], + signature, + ] + .concat(); let fail_tx = tx.as_advanced_builder().witness(witness.pack()).build(); @@ -557,7 +589,13 @@ fn test_commitment_lock_with_two_pending_htlcs() { .sign_recoverable(&message.into()) .unwrap() .serialize(); - let witness = [witness_script.clone(), vec![0x00], signature.clone()].concat(); + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + witness_script.clone(), + vec![0x00], + signature.clone(), + ] + .concat(); let success_tx = tx.as_advanced_builder().witness(witness.pack()).build(); let cycles = context @@ -587,7 +625,13 @@ fn test_commitment_lock_with_two_pending_htlcs() { .sign_recoverable(&message.into()) .unwrap() .serialize(); - let witness = [witness_script.clone(), vec![0x00], signature].concat(); + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + witness_script.clone(), + vec![0x00], + signature, + ] + .concat(); let fail_tx = tx.as_advanced_builder().witness(witness.pack()).build(); let error = context @@ -638,7 +682,13 @@ fn test_commitment_lock_with_two_pending_htlcs() { .sign_recoverable(&message.into()) .unwrap() .serialize(); - let witness = [witness_script.clone(), vec![0x01], signature].concat(); + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + witness_script.clone(), + vec![0x01], + signature, + ] + .concat(); let tx = tx.as_advanced_builder().witness(witness.pack()).build(); println!("tx: {:?}", tx); @@ -674,6 +724,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { .unwrap() .serialize(); let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), witness_script.clone(), vec![0x01], signature.clone(), @@ -689,6 +740,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { // sign with local_htlc_pubkey and wrong preimage should fail let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), witness_script.clone(), vec![0x01], signature.clone(), @@ -703,7 +755,13 @@ fn test_commitment_lock_with_two_pending_htlcs() { println!("error: {}", error); // sign with local_htlc_pubkey and empty preimage should fail - let witness = [witness_script.clone(), vec![0x01], signature].concat(); + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + witness_script.clone(), + vec![0x01], + signature, + ] + .concat(); let fail_tx = tx.as_advanced_builder().witness(witness.pack()).build(); let error = context @@ -829,7 +887,13 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { .sign_recoverable(&message.into()) .unwrap() .serialize(); - let witness = [witness_script.clone(), vec![0xFF], signature].concat(); + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + witness_script.clone(), + vec![0xFF], + signature, + ] + .concat(); let tx = tx.as_advanced_builder().witness(witness.pack()).build(); println!("tx: {:?}", tx); @@ -863,7 +927,13 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { .sign_recoverable(&message.into()) .unwrap() .serialize(); - let witness = [witness_script.clone(), vec![0xFF], signature].concat(); + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + witness_script.clone(), + vec![0xFF], + signature, + ] + .concat(); let tx = tx.as_advanced_builder().witness(witness.pack()).build(); println!("tx: {:?}", tx); @@ -921,6 +991,7 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { .unwrap() .serialize(); let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), witness_script.clone(), vec![0x00], signature, @@ -966,6 +1037,7 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { .unwrap() .serialize(); let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), witness_script.clone(), vec![0x00], signature, @@ -1029,7 +1101,13 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { .sign_recoverable(&message.into()) .unwrap() .serialize(); - let witness = [witness_script.clone(), vec![0x01], signature].concat(); + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + witness_script.clone(), + vec![0x01], + signature, + ] + .concat(); let tx = tx.as_advanced_builder().witness(witness.pack()).build(); println!("tx: {:?}", tx); @@ -1066,6 +1144,7 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { .unwrap() .serialize(); let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), witness_script.clone(), vec![0x01], signature,