diff --git a/Cargo.lock b/Cargo.lock index 0aeb99d..8f5f424 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -661,6 +661,7 @@ dependencies = [ "ckb-hash 0.114.0", "ckb-std", "hex", + "sha2", ] [[package]] @@ -1463,6 +1464,7 @@ dependencies = [ "musig2", "secp256k1 0.28.2", "serde_json", + "sha2", ] [[package]] diff --git a/contracts/commitment-lock/Cargo.toml b/contracts/commitment-lock/Cargo.toml index f251152..05e8cbc 100644 --- a/contracts/commitment-lock/Cargo.toml +++ b/contracts/commitment-lock/Cargo.toml @@ -7,6 +7,7 @@ edition = "2021" ckb-std = "0.15" ckb-hash = { version = "0.114.0", default-features = false, features = ["ckb-contract"] } hex = { version = "0.4", default-features = false, features = ["alloc"]} +sha2 = { version = "0.10", default-features = false } [build-dependencies] ckb-gen-types = "0.114.0" diff --git a/contracts/commitment-lock/README.md b/contracts/commitment-lock/README.md index 4b32a8c..bfa3dd0 100644 --- a/contracts/commitment-lock/README.md +++ b/contracts/commitment-lock/README.md @@ -9,7 +9,7 @@ The lock script args is the hash result of blake160(local_delay_epoch || local_d - `local_delay_pubkey_hash`: 20 bytes, hash result of blake160(local_delay_pubkey) - `revocation_pubkey_hash`: 20 bytes, hash result of blake160(revocation_pubkey) - `pending_htlc`: A group of pending HTLCS, each HTLC is 85 bytes, contains: - - `htlc_type`: 1 byte, 0x00 for offered HTLC, 0x01 for received HTLC + - `htlc_type`: 1 byte, high 7 bits for payment hash type (0000000 for blake2b, 0000001 for sha256), low 1 bit for offered or received type (0 for offered HTLC, 1 for received HTLC) - `payment_amount`: 16 bytes, u128 in little endian - `payment_hash`: 20 bytes - `remote_htlc_pubkey_hash`: 20 bytes, hash result of blake160(remote_htlc_pubkey) diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index d60d2d8..c940374 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -24,6 +24,7 @@ use ckb_std::{ since::Since, }; use hex::encode; +use sha2::{Digest, Sha256}; include!(concat!(env!("OUT_DIR"), "/auth_code_hash.rs")); @@ -80,11 +81,33 @@ const UNLOCK_WITH_SIGNATURE_LEN: usize = 66; const PREIMAGE_LEN: usize = 32; const MIN_WITNESS_LEN: usize = MIN_WITNESS_SCRIPT_LEN + UNLOCK_WITH_SIGNATURE_LEN; +enum HtlcType { + Offered, + Received, +} + +enum PaymentHashType { + Blake2b, + Sha256, +} + struct Htlc<'a>(&'a [u8]); impl<'a> Htlc<'a> { - pub fn htlc_type(&self) -> u8 { - self.0[0] + pub fn htlc_type(&self) -> HtlcType { + if self.0[0] & 0b00000001 == 0 { + HtlcType::Offered + } else { + HtlcType::Received + } + } + + pub fn payment_hash_type(&self) -> PaymentHashType { + if (self.0[0] >> 1) & 0b0000001 == 0 { + PaymentHashType::Blake2b + } else { + PaymentHashType::Sha256 + } } pub fn payment_amount(&self) -> u128 { @@ -202,54 +225,67 @@ fn auth() -> Result<(), Error> { { let htlc = Htlc(htlc_script); if unlock_htlc == i { - if htlc.htlc_type() == 0 { - // offered HTLC - let raw_since_value = load_input_since(0, Source::GroupInput)?; - if raw_since_value == 0 { - // when input since is 0, it means the unlock logic is for remote_htlc pubkey and preimage - if preimage - .map(|p| htlc.payment_hash() != &blake2b_256(p)[0..20]) - .unwrap_or(true) - { - return Err(Error::PreimageError); - } - new_amount -= htlc.payment_amount(); - pubkey_hash.copy_from_slice(htlc.remote_htlc_pubkey_hash()); - } else { - // when input since is not 0, it means the unlock logic is for local_htlc pubkey and htlc expiry - let since = Since::new(raw_since_value); - let htlc_expiry = Since::new(htlc.htlc_expiry()); - if since >= htlc_expiry { - pubkey_hash.copy_from_slice(htlc.local_htlc_pubkey_hash()); + match htlc.htlc_type() { + HtlcType::Offered => { + let raw_since_value = load_input_since(0, Source::GroupInput)?; + if raw_since_value == 0 { + // when input since is 0, it means the unlock logic is for remote_htlc pubkey and preimage + if preimage + .map(|p| match htlc.payment_hash_type() { + PaymentHashType::Blake2b => { + htlc.payment_hash() != &blake2b_256(p)[0..20] + } + PaymentHashType::Sha256 => { + htlc.payment_hash() != &Sha256::digest(p)[0..20] + } + }) + .unwrap_or(true) + { + return Err(Error::PreimageError); + } + new_amount -= htlc.payment_amount(); + pubkey_hash.copy_from_slice(htlc.remote_htlc_pubkey_hash()); } else { - return Err(Error::InvalidSince); + // when input since is not 0, it means the unlock logic is for local_htlc pubkey and htlc expiry + let since = Since::new(raw_since_value); + let htlc_expiry = Since::new(htlc.htlc_expiry()); + if since >= htlc_expiry { + pubkey_hash.copy_from_slice(htlc.local_htlc_pubkey_hash()); + } else { + return Err(Error::InvalidSince); + } } } - } else if htlc.htlc_type() == 1 { - // received HTLC - let raw_since_value = load_input_since(0, Source::GroupInput)?; - if raw_since_value == 0 { - // when input since is 0, it means the unlock logic is for local_htlc pubkey and preimage - if preimage - .map(|p| htlc.payment_hash() != &blake2b_256(p)[0..20]) - .unwrap_or(true) - { - return Err(Error::PreimageError); - } - pubkey_hash.copy_from_slice(htlc.local_htlc_pubkey_hash()); - } else { - // when input since is not 0, it means the unlock logic is for remote_htlc pubkey and htlc expiry - let since = Since::new(raw_since_value); - let htlc_expiry = Since::new(htlc.htlc_expiry()); - if since >= htlc_expiry { - new_amount -= htlc.payment_amount(); - pubkey_hash.copy_from_slice(htlc.remote_htlc_pubkey_hash()); + HtlcType::Received => { + let raw_since_value = load_input_since(0, Source::GroupInput)?; + if raw_since_value == 0 { + // when input since is 0, it means the unlock logic is for local_htlc pubkey and preimage + if preimage + .map(|p| match htlc.payment_hash_type() { + PaymentHashType::Blake2b => { + htlc.payment_hash() != &blake2b_256(p)[0..20] + } + PaymentHashType::Sha256 => { + htlc.payment_hash() != &Sha256::digest(p)[0..20] + } + }) + .unwrap_or(true) + { + return Err(Error::PreimageError); + } + pubkey_hash.copy_from_slice(htlc.local_htlc_pubkey_hash()); } else { - return Err(Error::InvalidSince); + // when input since is not 0, it means the unlock logic is for remote_htlc pubkey and htlc expiry + let since = Since::new(raw_since_value); + let htlc_expiry = Since::new(htlc.htlc_expiry()); + if since >= htlc_expiry { + new_amount -= htlc.payment_amount(); + pubkey_hash.copy_from_slice(htlc.remote_htlc_pubkey_hash()); + } else { + return Err(Error::InvalidSince); + } } } - } else { - return Err(Error::InvalidHtlcType); } } else { new_witness_script.push(htlc_script); diff --git a/tests/Cargo.toml b/tests/Cargo.toml index 52cb9e3..253344d 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -9,3 +9,4 @@ ckb-std = "0.15" serde_json = "1.0" secp256k1 = { version = "0.28", features = ["rand-std"] } musig2 = "0.0.11" +sha2 = "0.10" diff --git a/tests/src/tests.rs b/tests/src/tests.rs index d920f89..4a72c5b 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -13,6 +13,7 @@ use secp256k1::{ rand::{self, RngCore}, PublicKey, Secp256k1, SecretKey, }; +use sha2::{Digest, Sha256}; const MAX_CYCLES: u64 = 10_000_000; const BYTE_SHANNONS: u64 = 100_000_000; @@ -342,15 +343,15 @@ fn test_commitment_lock_with_two_pending_htlcs() { local_delay_epoch.as_u64().to_le_bytes().to_vec(), blake2b_256(local_delay_epoch_key.1.serialize())[0..20].to_vec(), blake2b_256(revocation_key.1.serialize())[0..20].to_vec(), - [0u8].to_vec(), + [0b00000000].to_vec(), payment_amount1.to_le_bytes().to_vec(), blake2b_256(preimage1)[0..20].to_vec(), blake2b_256(remote_htlc_key1.1.serialize())[0..20].to_vec(), blake2b_256(local_htlc_key1.1.serialize())[0..20].to_vec(), expiry1.as_u64().to_le_bytes().to_vec(), - [1u8].to_vec(), + [0b00000011].to_vec(), payment_amount2.to_le_bytes().to_vec(), - blake2b_256(preimage2)[0..20].to_vec(), + Sha256::digest(preimage2)[0..20].to_vec(), blake2b_256(remote_htlc_key2.1.serialize())[0..20].to_vec(), blake2b_256(local_htlc_key2.1.serialize())[0..20].to_vec(), expiry2.as_u64().to_le_bytes().to_vec(), @@ -479,9 +480,9 @@ fn test_commitment_lock_with_two_pending_htlcs() { local_delay_epoch.as_u64().to_le_bytes().to_vec(), blake2b_256(local_delay_epoch_key.1.serialize())[0..20].to_vec(), blake2b_256(revocation_key.1.serialize())[0..20].to_vec(), - [1u8].to_vec(), + [0b00000011].to_vec(), payment_amount2.to_le_bytes().to_vec(), - blake2b_256(preimage2)[0..20].to_vec(), + Sha256::digest(preimage2)[0..20].to_vec(), blake2b_256(remote_htlc_key2.1.serialize())[0..20].to_vec(), blake2b_256(local_htlc_key2.1.serialize())[0..20].to_vec(), expiry2.as_u64().to_le_bytes().to_vec(), @@ -650,7 +651,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { local_delay_epoch.as_u64().to_le_bytes().to_vec(), blake2b_256(local_delay_epoch_key.1.serialize())[0..20].to_vec(), blake2b_256(revocation_key.1.serialize())[0..20].to_vec(), - [0u8].to_vec(), + [0b00000000].to_vec(), payment_amount1.to_le_bytes().to_vec(), blake2b_256(preimage1)[0..20].to_vec(), blake2b_256(remote_htlc_key1.1.serialize())[0..20].to_vec(), @@ -805,13 +806,13 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { local_delay_epoch.as_u64().to_le_bytes().to_vec(), blake2b_256(local_delay_epoch_key.1.serialize())[0..20].to_vec(), blake2b_256(revocation_key.1.serialize())[0..20].to_vec(), - [0u8].to_vec(), + [0b00000000].to_vec(), payment_amount1.to_le_bytes().to_vec(), blake2b_256(preimage1)[0..20].to_vec(), blake2b_256(remote_htlc_key1.1.serialize())[0..20].to_vec(), blake2b_256(local_htlc_key1.1.serialize())[0..20].to_vec(), expiry1.as_u64().to_le_bytes().to_vec(), - [1u8].to_vec(), + [0b00000001].to_vec(), payment_amount2.to_le_bytes().to_vec(), blake2b_256(preimage2)[0..20].to_vec(), blake2b_256(remote_htlc_key2.1.serialize())[0..20].to_vec(), @@ -953,7 +954,7 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { local_delay_epoch.as_u64().to_le_bytes().to_vec(), blake2b_256(local_delay_epoch_key.1.serialize())[0..20].to_vec(), blake2b_256(revocation_key.1.serialize())[0..20].to_vec(), - [1u8].to_vec(), + [0b00000001].to_vec(), payment_amount2.to_le_bytes().to_vec(), blake2b_256(preimage2)[0..20].to_vec(), blake2b_256(remote_htlc_key2.1.serialize())[0..20].to_vec(), @@ -1065,7 +1066,7 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { local_delay_epoch.as_u64().to_le_bytes().to_vec(), blake2b_256(local_delay_epoch_key.1.serialize())[0..20].to_vec(), blake2b_256(revocation_key.1.serialize())[0..20].to_vec(), - [0u8].to_vec(), + [0b00000000].to_vec(), payment_amount1.to_le_bytes().to_vec(), blake2b_256(preimage1)[0..20].to_vec(), blake2b_256(remote_htlc_key1.1.serialize())[0..20].to_vec(),