From e9f4b00d02a5626b17f3348b1d4ecba3c8ffabe9 Mon Sep 17 00:00:00 2001 From: quake Date: Fri, 19 Sep 2025 15:45:37 +0900 Subject: [PATCH 01/27] wip --- contracts/commitment-lock/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/contracts/commitment-lock/README.md b/contracts/commitment-lock/README.md index 20a76ed..c793500 100644 --- a/contracts/commitment-lock/README.md +++ b/contracts/commitment-lock/README.md @@ -7,22 +7,22 @@ The lock script args is concatenated by the following fields: - `pubkey_hash`: 20 bytes, hash result of blake160(x only aggregated public key) - `delay_epoch`: 8 bytes, u64 in little endian, must be a relative [EpochNumberWithFraction](https://github.com/nervosnetwork/ckb/blob/develop/rpc/README.md#type-epochnumberwithfraction) - `version`: 8 bytes, u64 in big-endian -- `htlcs`: 20 bytes, hash result of blake160(pending_htlc_count || N * pending_htlc), optional +- `settlement_hash`: 20 bytes, hash result of blake160(non_pending_count || N * non_pending || pending_htlc_count || N * pending_htlc) To unlock this lock, the transaction must provide the following fields in the witness: - `empty_witness_args`: 16 bytes, fixed to 0x10000000100000001000000010000000, for compatibility with the xudt -- `unlock_type`: 1 byte, 0x00 ~ 0xFD for pending HTLC unlock, 0xFE for non-pending HTLC unlock, 0xFF for revocation unlock +- `unlock_type`: 1 byte, 0x00 ~ 0xFC for pending HTLC unlock, 0xFD and 0xFE for non-pending HTLC unlock, 0xFF for revocation unlock For revocation unlock process, the transaction must provide the following fields in the witness: - `version`: 8 bytes, u64 in big-endian, must be the same or greater than the version in the lock args - `pubkey`: 32 bytes, x only aggregated public key - `signature`: 64 bytes, aggregated signature -For non-pending HTLC unlock process, the transaction must provide the following fields in the witness: -- `pubkey`: 32 bytes, x only aggregated public key -- `signature`: 64 bytes, aggregated signature - -For pending HTLC unlock process, the transaction must provide the following fields in the witness: +For non-pending and pending HTLC unlock process, the transaction must provide the following fields in the witness: +- `non_pending_count`: 1 byte, 0, 1 or 2 +- `non_pending`: A group of non pending outputs amounts, contains: + - `payment_amount`: 16 bytes, u128 in little endian + - `pubkey_hash`: 20 bytes, hash result of blake160(pubkey) - `pending_htlc_count`: 1 byte, the count of pending HTLCs - `pending_htlc`: A group of pending HTLCS, each HTLC is 85 bytes, contains: - `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) From b803bb4d4aa66c47d54b8203e0d9b86b7a0410c1 Mon Sep 17 00:00:00 2001 From: quake Date: Mon, 22 Sep 2025 11:26:54 +0900 Subject: [PATCH 02/27] chore: update doc --- contracts/commitment-lock/README.md | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/contracts/commitment-lock/README.md b/contracts/commitment-lock/README.md index c793500..09fe8d1 100644 --- a/contracts/commitment-lock/README.md +++ b/contracts/commitment-lock/README.md @@ -7,22 +7,18 @@ The lock script args is concatenated by the following fields: - `pubkey_hash`: 20 bytes, hash result of blake160(x only aggregated public key) - `delay_epoch`: 8 bytes, u64 in little endian, must be a relative [EpochNumberWithFraction](https://github.com/nervosnetwork/ckb/blob/develop/rpc/README.md#type-epochnumberwithfraction) - `version`: 8 bytes, u64 in big-endian -- `settlement_hash`: 20 bytes, hash result of blake160(non_pending_count || N * non_pending || pending_htlc_count || N * pending_htlc) +- `settlement_hash`: 20 bytes, hash result of blake160(pending_htlc_count || N * pending_htlc || settlement_one_pubkey_hash || settlement_one_amount || settlement_two_pubkey_hash || settlement_two_amount) To unlock this lock, the transaction must provide the following fields in the witness: - `empty_witness_args`: 16 bytes, fixed to 0x10000000100000001000000010000000, for compatibility with the xudt -- `unlock_type`: 1 byte, 0x00 ~ 0xFC for pending HTLC unlock, 0xFD and 0xFE for non-pending HTLC unlock, 0xFF for revocation unlock +- `unlocks`: 1 byte, 0xFF for revocation unlock, 0x00 ~ 0xFE for settlement unlocks count. For revocation unlock process, the transaction must provide the following fields in the witness: - `version`: 8 bytes, u64 in big-endian, must be the same or greater than the version in the lock args - `pubkey`: 32 bytes, x only aggregated public key - `signature`: 64 bytes, aggregated signature -For non-pending and pending HTLC unlock process, the transaction must provide the following fields in the witness: -- `non_pending_count`: 1 byte, 0, 1 or 2 -- `non_pending`: A group of non pending outputs amounts, contains: - - `payment_amount`: 16 bytes, u128 in little endian - - `pubkey_hash`: 20 bytes, hash result of blake160(pubkey) +For settlement unlock process, the transaction must provide the following fields in the witness: - `pending_htlc_count`: 1 byte, the count of pending HTLCs - `pending_htlc`: A group of pending HTLCS, each HTLC is 85 bytes, contains: - `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) @@ -31,8 +27,16 @@ For non-pending and pending HTLC unlock process, the transaction must provide th - `remote_htlc_pubkey_hash`: 20 bytes, hash result of blake160(remote_htlc_pubkey) - `local_htlc_pubkey_hash`: 20 bytes, hash result of blake160(local_htlc_pubkey) - `htlc_expiry`: 8 bytes, u64 in little endian, must be an absolute timestamp [since](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0017-tx-valid-since/0017-tx-valid-since.md) -- `signature`: 65 bytes, the signature of the xxx_pubkey -- `preimage`: 32 bytes, an optional field to provide the preimage of the payment_hash +- `settlement_one_pubkey_hash`: 20 bytes, hash result of blake160(pubkey) +- `settlement_one_amount`: 16 bytes, u128 in little endian +- `settlement_two_pubkey_hash`: 20 bytes, hash result of blake160(pubkey) +- `settlement_two_amount`: 16 bytes, u128 in little endian + +- `unlocks`: A group of settlement unlock signature and preimage + - `unlock_type`: 0x00 ~ 0xFC for pending htlc group, 0xFD for settlement one, 0xFE for settlement two. + - `with_preimage`: 0x00 without preimage, 0x01 with preimage + - `signature`: 65 bytes, the signature of the xxx_pubkey + - `preimage`: 32 bytes, an optional field to provide the preimage of the payment_hash To know more about the transaction building process, please refer to the `test_commitment_lock_*` unit test. From bb4ee0e377482ee9be1d2db9d035bc4d314b8244 Mon Sep 17 00:00:00 2001 From: quake Date: Mon, 22 Sep 2025 11:31:52 +0900 Subject: [PATCH 03/27] chore: upgrade deps --- Cargo.lock | 476 +++++++++++++++----------- contracts/commitment-lock/Cargo.toml | 8 +- contracts/commitment-lock/build.rs | 2 +- contracts/commitment-lock/src/main.rs | 4 +- contracts/funding-lock/Cargo.toml | 8 +- contracts/funding-lock/build.rs | 2 +- rust-toolchain.toml | 2 +- tests/Cargo.toml | 6 +- tests/src/tests.rs | 130 ++++--- 9 files changed, 373 insertions(+), 265 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e7fcd6e..f527593 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,6 +40,12 @@ version = "1.0.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0952808a6c2afd1aa8947271f3a60f1a6763c7b912d210184c5149b5cf147247" +[[package]] +name = "arrayvec" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + [[package]] name = "autocfg" version = "1.1.0" @@ -80,16 +86,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" [[package]] -name = "bitflags" -version = "1.3.2" +name = "bitcoin-io" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b47c4ab7a93edb0c7198c5535ed9b52b63095f4e9b45279c6736cec4b856baf" + +[[package]] +name = "bitcoin_hashes" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "bb18c03d0db0247e147a21a6faafd5a7eb851c743db062de72018b6b7e8e4d16" +dependencies = [ + "bitcoin-io", + "hex-conservative", +] [[package]] name = "bitflags" -version = "2.8.0" +version = "2.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" +checksum = "2261d10cca569e4643e526d8dc2e62e433cc8aba21ab764233731f8d369bf394" [[package]] name = "blake2b-ref" @@ -118,15 +134,15 @@ dependencies = [ [[package]] name = "buddy-alloc" -version = "0.5.1" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f0d2da64a6a895d5a7e0724882825d50f83c13396b1b9f1878e19a024bab395" +checksum = "ee741d62dcaf41ca303576ef890989ccb01d5dd77f8ce1a6d6c7846ab5d09efb" [[package]] -name = "buddy-alloc" -version = "0.6.0" +name = "bumpalo" +version = "3.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee741d62dcaf41ca303576ef890989ccb01d5dd77f8ce1a6d6c7846ab5d09efb" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" @@ -145,9 +161,9 @@ dependencies = [ [[package]] name = "cacache" -version = "12.0.0" +version = "13.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "142316461ed3a3dfcba10417317472da5bfd0461e4d276bf7c07b330766d9490" +checksum = "5c5063741c7b2e260bbede781cf4679632dd90e2718e99f7715e46824b65670b" dependencies = [ "digest", "either", @@ -190,16 +206,16 @@ checksum = "8b3b72a38c9920a29990df12002c4d069a147c8782f0c211f8a01b2df8f42bfd" [[package]] name = "ckb-chain-spec" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "084b4b3c2d0d5ca51e47812130615535cad47772005af3535f3d4e6a63e947ae" +checksum = "e2e8e0a043138e2dadf851d7c7c3377846951a9f35e28de0d27b1810fe759a70" dependencies = [ "cacache", "ckb-constant", "ckb-crypto", "ckb-dao-utils", "ckb-error", - "ckb-hash 0.119.0", + "ckb-hash", "ckb-jsonrpc-types", "ckb-logger", "ckb-pow", @@ -213,38 +229,37 @@ dependencies = [ [[package]] name = "ckb-channel" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4c7f5530737f8a02329075581b29ab7003a72d6ee747d1b2ea9d2239faea7a" +checksum = "dcdf3f5242842f1735bfa1e38755f679ac071b2b451e0e8b87b9a80bf6147767" dependencies = [ "crossbeam-channel", ] [[package]] name = "ckb-constant" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e1424bf7490c14cdbd13697629ece8c1ba0ed02ff1c8a5b14a8713431ec6ff8" +checksum = "b8f4de41ddef453148c70bde26afc1cc26d2e22143e08555d7865e9cb0f523b1" [[package]] name = "ckb-crypto" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee335e672a67e4d951a65d5a3b03072f038dc1487e559133953912bcbe41d4d9" +checksum = "05df423a624b1d082ca0820c355d10b8855bb9699bf05154d613bf6693dbc1b7" dependencies = [ "ckb-fixed-hash", "faster-hex", - "lazy_static", "rand 0.8.5", - "secp256k1 0.29.1", + "secp256k1 0.30.0", "thiserror", ] [[package]] name = "ckb-dao" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "956a82fae564f5d207118f8a152f7c8daf1db4119ac4d3a18dfe5695bcd9b0e4" +checksum = "41d534da0d312cf36dfa097adb59f06959349686787d3ce4be6d74afa630752c" dependencies = [ "byteorder", "ckb-chain-spec", @@ -255,9 +270,9 @@ dependencies = [ [[package]] name = "ckb-dao-utils" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "766da195cb9a17f5a625de6924058ef1f12241317835cc83a09368018074366a" +checksum = "75356f7744ea9c0012a1eb48a46e37b23beeaa59e575defc4dab59a27506d1b7" dependencies = [ "byteorder", "ckb-error", @@ -266,21 +281,21 @@ dependencies = [ [[package]] name = "ckb-error" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "109adb3c26e697861e3f57c1ca8cf2f2a399c46e64c97be2b05f751535ac1b75" +checksum = "c44f328a850d94e1d7a8843b7107fa245a083bcb263f5eb7da2e8892daa766c6" dependencies = [ "anyhow", "ckb-occupied-capacity", - "derive_more", + "derive_more 1.0.0", "thiserror", ] [[package]] name = "ckb-fixed-hash" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71ea8f4896f945ecdb473cc8b747a47a9f282393a37a681bcbe0cdde94894bfc" +checksum = "b41ef9f1e9495ffa6cdc9694b7f438b0af360b958b543112c2d96b01ce3ca6f9" dependencies = [ "ckb-fixed-hash-core", "ckb-fixed-hash-macros", @@ -288,9 +303,9 @@ dependencies = [ [[package]] name = "ckb-fixed-hash-core" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1c265cd6b0ec00b8dc671b9344906a2428f9b756e4e789660c71f535252fe2d" +checksum = "a5338f04fcdd0b217203c43d05af4b5cc2971ce4f1ecb0df2162e06aa3779def" dependencies = [ "ckb_schemars", "faster-hex", @@ -300,56 +315,36 @@ dependencies = [ [[package]] name = "ckb-fixed-hash-macros" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "976b10df5474be0ff33b22a84b44875e065679fc41155350c11e420124910ca1" +checksum = "5de2b2aeebd799da3170220c88dffc3f50257e657d276d59ffe38f533de26459" dependencies = [ "ckb-fixed-hash-core", "proc-macro2", "quote", - "syn 1.0.109", -] - -[[package]] -name = "ckb-gen-types" -version = "0.112.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a12020d50dd3757cde0fdc88d3837b7a2ab503fe38bd11be86ddace11318c77" -dependencies = [ - "cfg-if", - "ckb-hash 0.112.1", - "molecule 0.7.5", + "syn 2.0.58", ] [[package]] name = "ckb-gen-types" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bc221d4b9d6d39215b1d62be855861b8b0c8d668ca29874903b0bf5d0b4d9fa" +checksum = "255da9cda52578622b7b8a437eb22942bfdbe90d7896f86a002fa8e4cc1804c8" dependencies = [ "cfg-if", "ckb-error", "ckb-fixed-hash", - "ckb-hash 0.119.0", + "ckb-hash", "ckb-occupied-capacity", - "molecule 0.8.0", + "molecule", "numext-fixed-uint", ] [[package]] name = "ckb-hash" -version = "0.112.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "25af660fc8746f7c756444e6aa47ede9a874206563b6a1ce1b230a5b86519392" -dependencies = [ - "blake2b-ref", -] - -[[package]] -name = "ckb-hash" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "99ba7c72f86f239b3e0154f51d6cd5d0d83bbaa8775fdc7b6bcac459ae24b6fd" +checksum = "0a74fb43c0f092a5ea388d31e4579cada2cc14e45dd5818b996f348613efe202" dependencies = [ "blake2b-ref", "blake2b-rs", @@ -357,9 +352,9 @@ dependencies = [ [[package]] name = "ckb-jsonrpc-types" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3983584cc6e269125c3bf502fa6d84a4f6e47d5b4c1e3a4070db86382ed4ba15" +checksum = "2e1fb6611ec4626ec5876b7dac828027faac9ab0289c90f630109eae23f1e3da" dependencies = [ "ckb-types", "ckb_schemars", @@ -370,9 +365,9 @@ dependencies = [ [[package]] name = "ckb-logger" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b19523bb7b582ccd98615a48c687e41ce1511e72d903520ea36100ef5c5ba9a3" +checksum = "7dee2e03aff24f041876ba9e8c6c3389edd003041e82ab59c57e6e0674806fbc" dependencies = [ "log", ] @@ -388,9 +383,9 @@ dependencies = [ [[package]] name = "ckb-mock-tx-types" -version = "0.119.0" +version = "0.202.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bce36d06097bca7df141a89ba970566bbf4075945c6dd95a0771e8830c2c1a8" +checksum = "723e0733d87808cbd50faf27a29e428a4d52e975fc9387ab6c09a81a52299e06" dependencies = [ "ckb-jsonrpc-types", "ckb-traits", @@ -400,9 +395,9 @@ dependencies = [ [[package]] name = "ckb-occupied-capacity" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6b706bce252b627543ce3bac5240f4d2f1e5d73daca9e451c88db44c2ea94bb" +checksum = "a7d80645639dd38cf0c91d06cd2e8bd3489557479968e6fb8ed2d35d7fdde5dd" dependencies = [ "ckb-occupied-capacity-core", "ckb-occupied-capacity-macros", @@ -410,32 +405,32 @@ dependencies = [ [[package]] name = "ckb-occupied-capacity-core" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "510608e5c7c2f3bf025c6ae80ed7782a1cd12897e15dc930167c8dbd3165ece6" +checksum = "af825d54b795fab2b9e934ab5b5f9fa1090f69b98cc60e3a6d363336b5ee1726" dependencies = [ "serde", ] [[package]] name = "ckb-occupied-capacity-macros" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b79a3fd71708b5068fb377497d6be6dbca53725f1a13d174335105c4bd39ffd5" +checksum = "ca553126a0269427c31b3dbca8d720a4c79267a80c9a0e6c589a86d460a6201b" dependencies = [ "ckb-occupied-capacity-core", "quote", - "syn 1.0.109", + "syn 2.0.58", ] [[package]] name = "ckb-pow" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40266a0bbf46eb4d92fc7137c93b22b8518d7a5a8991d6b52931b1fc43cb9863" +checksum = "e8b4deffe39636e0f55bb888612b48026db58dd10dca1efadf7a54827b8b61fe" dependencies = [ "byteorder", - "ckb-hash 0.119.0", + "ckb-hash", "ckb-types", "eaglesong", "log", @@ -444,9 +439,9 @@ dependencies = [ [[package]] name = "ckb-rational" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91835c60dba878e54da2dfcfad62369638b88f910d9e8c7eb31f9c357a331ff7" +checksum = "dd60f92eafb05be8fd7ec091841c7e28503a61e061b54e1010b2d0f3a0b13763" dependencies = [ "numext-fixed-uint", "serde", @@ -454,9 +449,9 @@ dependencies = [ [[package]] name = "ckb-resource" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ec19c5f0d6e96a0347492acdf992f6db7838f70b1c3ae4d32e9d3558f0557f0" +checksum = "893c593af418c218c4f2cea26d6964e3289651a887b049c0370fbb14392e92c1" dependencies = [ "ckb-system-scripts", "ckb-types", @@ -469,14 +464,14 @@ dependencies = [ [[package]] name = "ckb-script" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c247ea3580c240a7eb82516258c8523c7c22219b6681016be83284ebb15cd22" +checksum = "e392689735f78ecd624b5583c9c9be560c2ff444e0f9114461fa061d5ff4c1be" dependencies = [ "byteorder", "ckb-chain-spec", "ckb-error", - "ckb-hash 0.119.0", + "ckb-hash", "ckb-logger", "ckb-traits", "ckb-types", @@ -488,26 +483,15 @@ dependencies = [ [[package]] name = "ckb-std" -version = "0.15.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6f72eb58dfefcc444621dc97020ef96af0636aa59e26b9c110359cb6e37db59" -dependencies = [ - "buddy-alloc 0.5.1", - "cc", - "ckb-gen-types 0.112.1", - "gcd", -] - -[[package]] -name = "ckb-std" -version = "0.16.4" +version = "0.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a673595baadfa1712ff03a36e1519f28015cf9944282410863d5e256336f5b9" +checksum = "161ac10a3d0c70ff689a0cf739cf5d91329eb4da085001e887aa636a2e729d84" dependencies = [ - "buddy-alloc 0.6.0", + "buddy-alloc", "cc", - "ckb-gen-types 0.119.0", + "ckb-gen-types", "gcd", + "int-enum", ] [[package]] @@ -525,21 +509,24 @@ dependencies = [ [[package]] name = "ckb-systemtime" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0acbeae9e64b10d26ea477dc982caf643d4143f086cb5cf69f2d0f94e4fa2e4" +checksum = "a190f4c7f21836db1a9649934d883030b8debabf870257c9a8a17e390b8effc5" +dependencies = [ + "web-time", +] [[package]] name = "ckb-testtool" -version = "0.14.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3455a3c22f26dbc83cb5d0b6225aefda45377e50432a55ed84b969580418515d" +checksum = "1e2172778bdfd38d90b773fa0cfa069a58475b524d06733933f86d9740abbbc7" dependencies = [ "ckb-always-success-script", "ckb-chain-spec", "ckb-crypto", "ckb-error", - "ckb-hash 0.119.0", + "ckb-hash", "ckb-jsonrpc-types", "ckb-mock-tx-types", "ckb-resource", @@ -554,18 +541,18 @@ dependencies = [ [[package]] name = "ckb-traits" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab91cb32bd5655b5b4c6574d6f07dd919d93759dc9e07f9ee6a2a9823848c4dc" +checksum = "80fcf192e9832c5b1213a2f4dcc58812ad77ae9599aec3170652db07d9c2fb0f" dependencies = [ "ckb-types", ] [[package]] name = "ckb-types" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f02dc76ea18e9838ec996c0f1f8a822d65176d2c9f052b3855c1d0a2d0a4d885" +checksum = "32bd5b5b4a2d991ee05852641c8272498df27abd0cef0f16979f6af6cb5fd97d" dependencies = [ "bit-vec", "bytes", @@ -573,25 +560,24 @@ dependencies = [ "ckb-constant", "ckb-error", "ckb-fixed-hash", - "ckb-gen-types 0.119.0", - "ckb-hash 0.119.0", + "ckb-gen-types", + "ckb-hash", "ckb-merkle-mountain-range", "ckb-occupied-capacity", "ckb-rational", - "derive_more", + "derive_more 1.0.0", "golomb-coded-set", "merkle-cbt", - "molecule 0.8.0", + "molecule", "numext-fixed-uint", - "once_cell", "paste", ] [[package]] name = "ckb-verification" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45211151ba0f3d949739f7e4b0bb3368064f6388022c977c2fb316373ccf5f3" +checksum = "6fc0d24b27bd2ccb209cae0accf7c6b47ce22c7122d1fb1bbf3ef9bac2420c17" dependencies = [ "ckb-chain-spec", "ckb-dao", @@ -603,32 +589,32 @@ dependencies = [ "ckb-traits", "ckb-types", "ckb-verification-traits", - "derive_more", + "derive_more 1.0.0", "lru", "tokio", ] [[package]] name = "ckb-verification-traits" -version = "0.119.0" +version = "0.202.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "468d29a43ebacfdecb5cd5a7b9f90a9dc6fb610380a4251208e567fdcce88c0d" +checksum = "1e3711e30c9f6724d97d4ee73b61f854e2a3cbe8e49f4508011a3b72ece7736d" dependencies = [ - "bitflags 1.3.2", + "bitflags", "ckb-error", ] [[package]] name = "ckb-vm" -version = "0.24.12" +version = "0.24.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddff96029d3298cb630e95f29d4b9a93384e938a0b75758684aa8794b53bdd1a" +checksum = "ad137e2f1c9a363ce19a883a2113b1dfcc00a936945e34b62e3726c49e7171fb" dependencies = [ "byteorder", "bytes", "cc", "ckb-vm-definitions", - "derive_more", + "derive_more 0.99.17", "goblin 0.2.3", "goblin 0.4.0", "rand 0.7.3", @@ -638,18 +624,18 @@ dependencies = [ [[package]] name = "ckb-vm-definitions" -version = "0.24.12" +version = "0.24.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c280bf1d589d23ab0358f58601c2187fc6be86a131644583ef72ea96a0a13ddd" +checksum = "0b436017fd6676bea413d54e07a5a9cc1d7c4b5c02e4ab07d3527225a5de6677" dependencies = [ "paste", ] [[package]] name = "ckb_schemars" -version = "0.8.19" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f21f99fca82a4eb8708e406e99246987b087ecc1e1babeece1a0b1d5238b1750" +checksum = "c37520aaae28169f4b29cd754f414d01ef32b8209c85d4473bc89d760250b990" dependencies = [ "ckb_schemars_derive", "dyn-clone", @@ -659,9 +645,9 @@ dependencies = [ [[package]] name = "ckb_schemars_derive" -version = "0.8.19" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40c813b4fadbdd9f33b1cf02a1ddfa9537d955c8d2fbe150d1fc1684dbf78e73" +checksum = "1506d63311ded0645342c052b1eb21ba272177b32f55d8eb7e11255aed3e74c6" dependencies = [ "proc-macro2", "quote", @@ -673,9 +659,9 @@ dependencies = [ name = "commitment-lock" version = "0.1.0" dependencies = [ - "ckb-gen-types 0.119.0", - "ckb-hash 0.119.0", - "ckb-std 0.16.4", + "ckb-gen-types", + "ckb-hash", + "ckb-std", "hex", "sha2", ] @@ -748,6 +734,27 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_more" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a9b99b9cbbe49445b21764dc0625032a89b145a2642e67603e1c936f5458d05" +dependencies = [ + "derive_more-impl", +] + +[[package]] +name = "derive_more-impl" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7330aeadfbe296029522e6c40f315320aba36fc43a5b3632f3795348f3bd22" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", + "unicode-xid", +] + [[package]] name = "digest" version = "0.10.7" @@ -784,7 +791,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" dependencies = [ "libc", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -813,9 +820,9 @@ dependencies = [ name = "funding-lock" version = "0.1.0" dependencies = [ - "ckb-gen-types 0.119.0", - "ckb-hash 0.119.0", - "ckb-std 0.16.4", + "ckb-gen-types", + "ckb-hash", + "ckb-std", "hex", ] @@ -875,7 +882,7 @@ checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.58", ] [[package]] @@ -1019,6 +1026,15 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hex-conservative" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5313b072ce3c597065a808dbf612c4c8e8590bdbf8b579508bf7a762c5eae6cd" +dependencies = [ + "arrayvec", +] + [[package]] name = "hmac" version = "0.12.1" @@ -1049,12 +1065,34 @@ dependencies = [ "walkdir", ] +[[package]] +name = "int-enum" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e366a1634cccc76b4cfd3e7580de9b605e4d93f1edac48d786c1f867c0def495" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.58", +] + [[package]] name = "itoa" version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b1a46d1a171d865aa5f83f92695765caa047a9b4cbae2cbf37dbd613a793fd4c" +[[package]] +name = "js-sys" +version = "0.3.80" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852f13bec5eba4ba9afbeb93fd7c13fe56147f055939ae21c43a29a0ecb2702e" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -1132,7 +1170,7 @@ checksum = "49e7bc1560b95a3c4a25d03de42fe76ca718ab92d1a22a55b9b4cf67b3ae635c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.58", ] [[package]] @@ -1153,26 +1191,6 @@ dependencies = [ "adler2", ] -[[package]] -name = "mio" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" -dependencies = [ - "libc", - "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.52.0", -] - -[[package]] -name = "molecule" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fd9767ab5e5f2ea40f71ff4c8bdb633c50509052e093c2fdd0e390a749dfa3" -dependencies = [ - "cfg-if", -] - [[package]] name = "molecule" version = "0.8.0" @@ -1337,6 +1355,18 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", + "version_check", +] + [[package]] name = "quote" version = "1.0.35" @@ -1460,11 +1490,11 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.8.0", + "bitflags", "errno", "libc", "linux-raw-sys", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] @@ -1526,10 +1556,12 @@ dependencies = [ [[package]] name = "secp256k1" -version = "0.29.1" +version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" +checksum = "b50c5943d326858130af85e049f2661ba3c78b26589b8ab98e65e80ae44a1252" dependencies = [ + "bitcoin_hashes", + "rand 0.8.5", "secp256k1-sys 0.10.1", ] @@ -1574,7 +1606,7 @@ checksum = "7eb0b34b42edc17f6b7cac84a52a1c5f0e1bb2227e997ca9011ea3dd34e8610b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.58", ] [[package]] @@ -1632,15 +1664,6 @@ dependencies = [ "digest", ] -[[package]] -name = "signal-hook-registry" -version = "1.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" -dependencies = [ - "libc", -] - [[package]] name = "siphasher" version = "0.3.11" @@ -1692,9 +1715,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.52" +version = "2.0.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" +checksum = "44cfb93f38070beee36b3fef7d4f5a16f27751d94b187b666a5cc5e9b0d30687" dependencies = [ "proc-macro2", "quote", @@ -1712,14 +1735,14 @@ dependencies = [ "getrandom 0.3.1", "once_cell", "rustix", - "windows-sys 0.59.0", + "windows-sys", ] [[package]] name = "tests" version = "0.1.0" dependencies = [ - "ckb-std 0.15.1", + "ckb-std", "ckb-testtool", "musig2", "secp256k1 0.28.2", @@ -1744,7 +1767,7 @@ checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.58", ] [[package]] @@ -1755,12 +1778,8 @@ checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", - "libc", - "mio", "pin-project-lite", - "signal-hook-registry", "tokio-macros", - "windows-sys 0.52.0", ] [[package]] @@ -1771,7 +1790,7 @@ checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.58", ] [[package]] @@ -1812,6 +1831,12 @@ version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "version_check" version = "0.9.4" @@ -1849,6 +1874,74 @@ dependencies = [ "wit-bindgen-rt", ] +[[package]] +name = "wasm-bindgen" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab10a69fbd0a177f5f649ad4d8d3305499c42bab9aef2f7ff592d0ec8f833819" +dependencies = [ + "cfg-if", + "once_cell", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bb702423545a6007bbc368fde243ba47ca275e549c8a28617f56f6ba53b1d1c" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.58", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc65f4f411d91494355917b605e1480033152658d71f722a90647f56a70c88a0" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffc003a991398a8ee604a401e194b6b3a39677b3173d6e74495eb51b82e99a32" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.58", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "293c37f4efa430ca14db3721dfbe48d8c33308096bd44d80ebaa775ab71ba1cf" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -1911,7 +2004,7 @@ checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.58", ] [[package]] @@ -1922,7 +2015,7 @@ checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01" dependencies = [ "proc-macro2", "quote", - "syn 2.0.52", + "syn 2.0.58", ] [[package]] @@ -1949,15 +2042,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.52.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.59.0" @@ -2101,7 +2185,7 @@ version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" dependencies = [ - "bitflags 2.8.0", + "bitflags", ] [[package]] diff --git a/contracts/commitment-lock/Cargo.toml b/contracts/commitment-lock/Cargo.toml index a5c17ed..58026bb 100644 --- a/contracts/commitment-lock/Cargo.toml +++ b/contracts/commitment-lock/Cargo.toml @@ -1,13 +1,13 @@ [package] name = "commitment-lock" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] -ckb-std = "0.16" -ckb-hash = { version = "0.119.0", default-features = false, features = ["ckb-contract"] } +ckb-std = "0.18" +ckb-hash = { version = "0.202.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.119.0" +ckb-gen-types = "0.202.0" diff --git a/contracts/commitment-lock/build.rs b/contracts/commitment-lock/build.rs index d572411..2e036ae 100644 --- a/contracts/commitment-lock/build.rs +++ b/contracts/commitment-lock/build.rs @@ -1,6 +1,6 @@ use ckb_gen_types::{packed::CellOutput, prelude::*}; use std::env; -use std::fs::{read, File}; +use std::fs::{File, read}; use std::io::{BufWriter, Write}; use std::path::Path; diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index be9fe3e..5ddfe4a 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -18,8 +18,8 @@ use ckb_std::{ ckb_types::{bytes::Bytes, core::ScriptHashType, prelude::*}, error::SysError, high_level::{ - exec_cell, load_cell, load_cell_capacity, load_cell_data, load_cell_lock, load_cell_type, - load_input_since, load_script, load_transaction, load_witness, QueryIter, + QueryIter, exec_cell, load_cell, load_cell_capacity, load_cell_data, load_cell_lock, + load_cell_type, load_input_since, load_script, load_transaction, load_witness, }, since::{EpochNumberWithFraction, LockValue, Since}, }; diff --git a/contracts/funding-lock/Cargo.toml b/contracts/funding-lock/Cargo.toml index 9343915..0286c97 100644 --- a/contracts/funding-lock/Cargo.toml +++ b/contracts/funding-lock/Cargo.toml @@ -1,12 +1,12 @@ [package] name = "funding-lock" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] -ckb-std = "0.16" -ckb-hash = { version = "0.119.0", default-features = false, features = ["ckb-contract"] } +ckb-std = "0.18" +ckb-hash = { version = "0.202.0", default-features = false, features = ["ckb-contract"] } hex = { version = "0.4", default-features = false, features = ["alloc"]} [build-dependencies] -ckb-gen-types = "0.119.0" +ckb-gen-types = "0.202.0" diff --git a/contracts/funding-lock/build.rs b/contracts/funding-lock/build.rs index d572411..2e036ae 100644 --- a/contracts/funding-lock/build.rs +++ b/contracts/funding-lock/build.rs @@ -1,6 +1,6 @@ use ckb_gen_types::{packed::CellOutput, prelude::*}; use std::env; -use std::fs::{read, File}; +use std::fs::{File, read}; use std::io::{BufWriter, Write}; use std::path::Path; diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 1de01fa..c1bc0a6 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.81.0" +channel = "1.85.0" diff --git a/tests/Cargo.toml b/tests/Cargo.toml index d8d82d0..091b268 100644 --- a/tests/Cargo.toml +++ b/tests/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "tests" version = "0.1.0" -edition = "2021" +edition = "2024" [dependencies] -ckb-testtool = "0.14.0" -ckb-std = "0.15" +ckb-testtool = "0.16" +ckb-std = "0.18" serde_json = "1.0" secp256k1 = { version = "0.28", features = ["rand-std"] } musig2 = "0.0.11" diff --git a/tests/src/tests.rs b/tests/src/tests.rs index 4130be7..aba2ef0 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -15,8 +15,8 @@ use musig2::{ BinaryEncoding, CompactSignature, FirstRound, KeyAggContext, PartialSignature, SecNonceSpices, }; use secp256k1::{ - rand::{self, RngCore}, PublicKey, Secp256k1, SecretKey, + rand::{self, RngCore}, }; use sha2::{Digest, Sha256}; @@ -491,10 +491,12 @@ fn test_commitment_lock_with_two_pending_htlcs() { .as_builder() .args(new_args.pack()) .build(); - let outputs = vec![CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS - payment_amount1 as u64).pack()) - .lock(new_lock_script.clone()) - .build()]; + let outputs = vec![ + CellOutput::new_builder() + .capacity((1000 * BYTE_SHANNONS - payment_amount1 as u64).pack()) + .lock(new_lock_script.clone()) + .build(), + ]; let outputs_data = [Bytes::new()]; let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) @@ -569,10 +571,12 @@ fn test_commitment_lock_with_two_pending_htlcs() { .since(since.as_u64().pack()) .build(); let inputs = vec![input, delay_epoch_input.clone()]; - let outputs = vec![CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS - payment_amount1 as u64).pack()) - .lock(new_lock_script.clone()) - .build()]; + let outputs = vec![ + CellOutput::new_builder() + .capacity((1000 * BYTE_SHANNONS - payment_amount1 as u64).pack()) + .lock(new_lock_script.clone()) + .build(), + ]; let outputs_data = [Bytes::new()]; let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) @@ -667,10 +671,12 @@ fn test_commitment_lock_with_two_pending_htlcs() { ] .concat(); let new_lock_script = lock_script.as_builder().args(new_args.pack()).build(); - let outputs = vec![CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS - payment_amount2 as u64).pack()) - .lock(new_lock_script.clone()) - .build()]; + let outputs = vec![ + CellOutput::new_builder() + .capacity((1000 * BYTE_SHANNONS - payment_amount2 as u64).pack()) + .lock(new_lock_script.clone()) + .build(), + ]; let outputs_data = [Bytes::new()]; let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) @@ -716,10 +722,12 @@ fn test_commitment_lock_with_two_pending_htlcs() { .since(half_delay_epoch.as_u64().pack()) .build(), ]; - let outputs = vec![CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS - payment_amount2 as u64).pack()) - .lock(new_lock_script.clone()) - .build()]; + let outputs = vec![ + CellOutput::new_builder() + .capacity((1000 * BYTE_SHANNONS - payment_amount2 as u64).pack()) + .lock(new_lock_script.clone()) + .build(), + ]; let outputs_data = [Bytes::new()]; let tx = TransactionBuilder::default() .cell_deps(cell_deps) @@ -929,15 +937,19 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { .as_builder() .args(new_args.pack()) .build(); - let outputs = vec![CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS).pack()) - .lock(new_lock_script.clone()) - .type_(Some(type_script.clone()).pack()) - .build()]; - let outputs_data: Vec = vec![(total_sudt_amount - payment_amount1) - .to_le_bytes() - .to_vec() - .into()]; + let outputs = vec![ + CellOutput::new_builder() + .capacity((1000 * BYTE_SHANNONS).pack()) + .lock(new_lock_script.clone()) + .type_(Some(type_script.clone()).pack()) + .build(), + ]; + let outputs_data: Vec = vec![ + (total_sudt_amount - payment_amount1) + .to_le_bytes() + .to_vec() + .into(), + ]; let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) .inputs(inputs) @@ -979,15 +991,19 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { .since(since.as_u64().pack()) .build(); let inputs = vec![input, delay_epoch_input.clone()]; - let outputs = vec![CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS).pack()) - .lock(new_lock_script.clone()) - .type_(Some(type_script.clone()).pack()) - .build()]; - let outputs_data: Vec = vec![(total_sudt_amount - payment_amount1) - .to_le_bytes() - .to_vec() - .into()]; + let outputs = vec![ + CellOutput::new_builder() + .capacity((1000 * BYTE_SHANNONS).pack()) + .lock(new_lock_script.clone()) + .type_(Some(type_script.clone()).pack()) + .build(), + ]; + let outputs_data: Vec = vec![ + (total_sudt_amount - payment_amount1) + .to_le_bytes() + .to_vec() + .into(), + ]; let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) .inputs(inputs) @@ -1045,15 +1061,19 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { ] .concat(); let new_lock_script = lock_script.as_builder().args(new_args.pack()).build(); - let outputs = vec![CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS).pack()) - .lock(new_lock_script.clone()) - .type_(Some(type_script.clone()).pack()) - .build()]; - let outputs_data: Vec = vec![(total_sudt_amount - payment_amount2) - .to_le_bytes() - .to_vec() - .into()]; + let outputs = vec![ + CellOutput::new_builder() + .capacity((1000 * BYTE_SHANNONS).pack()) + .lock(new_lock_script.clone()) + .type_(Some(type_script.clone()).pack()) + .build(), + ]; + let outputs_data: Vec = vec![ + (total_sudt_amount - payment_amount2) + .to_le_bytes() + .to_vec() + .into(), + ]; let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) .inputs(inputs) @@ -1098,15 +1118,19 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { .since(half_delay_epoch.as_u64().pack()) .build(), ]; - let outputs = vec![CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS).pack()) - .lock(new_lock_script.clone()) - .type_(Some(type_script.clone()).pack()) - .build()]; - let outputs_data: Vec = vec![(total_sudt_amount - payment_amount2) - .to_le_bytes() - .to_vec() - .into()]; + let outputs = vec![ + CellOutput::new_builder() + .capacity((1000 * BYTE_SHANNONS).pack()) + .lock(new_lock_script.clone()) + .type_(Some(type_script.clone()).pack()) + .build(), + ]; + let outputs_data: Vec = vec![ + (total_sudt_amount - payment_amount2) + .to_le_bytes() + .to_vec() + .into(), + ]; let tx = TransactionBuilder::default() .cell_deps(cell_deps) .inputs(inputs) From 7174c8cde5fc6e78ee01f98801c57c4ea3c5e417 Mon Sep 17 00:00:00 2001 From: quake Date: Mon, 22 Sep 2025 15:20:54 +0900 Subject: [PATCH 04/27] wip: refactor commitment lock --- contracts/commitment-lock/src/main.rs | 327 ++++++++++++++------------ 1 file changed, 179 insertions(+), 148 deletions(-) diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index 5ddfe4a..ce37a13 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -38,6 +38,8 @@ pub enum Error { MultipleInputs, InvalidSince, InvalidUnlockType, + InvalidWithPreimageFlag, + InvalidSettlementCount, InvalidExpiry, ArgsLenError, WitnessLenError, @@ -93,7 +95,7 @@ enum PaymentHashType { struct Htlc<'a>(&'a [u8]); impl<'a> Htlc<'a> { - pub fn htlc_type(&self) -> HtlcType { + fn htlc_type(&self) -> HtlcType { if self.0[0] & 0b00000001 == 0 { HtlcType::Offered } else { @@ -101,7 +103,7 @@ impl<'a> Htlc<'a> { } } - pub fn payment_hash_type(&self) -> PaymentHashType { + fn payment_hash_type(&self) -> PaymentHashType { if (self.0[0] >> 1) & 0b0000001 == 0 { PaymentHashType::Blake2b } else { @@ -109,27 +111,47 @@ impl<'a> Htlc<'a> { } } - pub fn payment_amount(&self) -> u128 { + fn payment_amount(&self) -> u128 { u128::from_le_bytes(self.0[1..17].try_into().unwrap()) } - pub fn payment_hash(&self) -> &'a [u8] { + fn payment_hash(&self) -> &'a [u8] { &self.0[17..37] } - pub fn remote_htlc_pubkey_hash(&self) -> &'a [u8] { - &self.0[37..57] + fn remote_htlc_pubkey_hash(&self) -> [u8; 20] { + self.0[37..57].try_into().unwrap() } - pub fn local_htlc_pubkey_hash(&self) -> &'a [u8] { - &self.0[57..77] + fn local_htlc_pubkey_hash(&self) -> [u8; 20] { + self.0[57..77].try_into().unwrap() } - pub fn htlc_expiry(&self) -> u64 { + fn htlc_expiry(&self) -> u64 { u64::from_le_bytes(self.0[77..85].try_into().unwrap()) } } +struct Settlement<'a>(&'a [u8]); + +impl<'a> Settlement<'a> { + fn unlock_type(&self) -> u8 { + self.0[0] + } + + fn with_preimage(&self) -> bool { + self.0[1] == 1 + } + + fn signature(&self) -> [u8; SIGNATURE_LEN] { + self.0[2..2 + SIGNATURE_LEN].try_into().unwrap() + } + + fn preimage(&self) -> &[u8] { + &self.0[2 + SIGNATURE_LEN..2 + SIGNATURE_LEN + PREIMAGE_LEN] + } +} + fn auth() -> Result<(), Error> { // since local_delay_pubkey is derived, the scripts are usually unique, to simplify the implementation of the following unlocking logic, we check the number of inputs should be 1 if load_input_since(1, Source::GroupInput).is_ok() { @@ -141,7 +163,7 @@ fn auth() -> Result<(), Error> { let script = load_script()?; let args: Bytes = script.args().unpack(); - if args.len() != 36 && args.len() != 56 { + if args.len() != 56 { return Err(Error::ArgsLenError); } @@ -153,8 +175,8 @@ fn auth() -> Result<(), Error> { { return Err(Error::EmptyWitnessArgsError); } - let unlock_type = witness.remove(0); - if unlock_type == 0xFF { + let unlocks = witness.remove(0); + if unlocks == 0xFF { // revocation unlock process // verify version @@ -194,62 +216,49 @@ fn auth() -> Result<(), Error> { exec_cell(&AUTH_CODE_HASH, ScriptHashType::Data1, &args).map_err(|_| Error::AuthError)?; Ok(()) - } else if unlock_type == 0xFE { - // non-pending HTLC unlock process - - let raw_since_value = load_input_since(0, Source::GroupInput)?; - let since = Since::new(raw_since_value); - let delay_epoch = Since::new(u64::from_le_bytes(args[20..28].try_into().unwrap())); - if since >= delay_epoch { - // verify signature, we are using the same signature verification logic as open tx, only hash the 1st and 2nd output - // TODO we may add a check to ensure the 3rd output cell's lock script is same as the 1st output cell's lock script, to resolve the issue of pending timeout HTLC - let to_local_output = load_cell(0, Source::Output)?; - let to_local_output_data = load_cell_data(0, Source::Output)?; - let to_remote_output = load_cell(1, Source::Output)?; - let to_remote_output_data = load_cell_data(1, Source::Output)?; - let message = blake2b_256( - [ - to_local_output.as_slice(), - (to_local_output_data.len() as u32).to_le_bytes().as_ref(), - to_local_output_data.as_slice(), - to_remote_output.as_slice(), - (to_remote_output_data.len() as u32).to_le_bytes().as_ref(), - to_remote_output_data.as_slice(), - &args[0..36], - ] - .concat(), - ); - let pubkey_hash = &args[0..20]; - - // AuthAlgorithmIdSchnorr = 7 - let algorithm_id_str = CString::new(encode([7u8])).unwrap(); - let signature_str = CString::new(encode(&witness)).unwrap(); - let message_str = CString::new(encode(message)).unwrap(); - let pubkey_hash_str = CString::new(encode(pubkey_hash)).unwrap(); - - let args = [ - algorithm_id_str.as_c_str(), - signature_str.as_c_str(), - message_str.as_c_str(), - pubkey_hash_str.as_c_str(), - ]; - - exec_cell(&AUTH_CODE_HASH, ScriptHashType::Data1, &args) - .map_err(|_| Error::AuthError)?; - return Ok(()); - } else { - return Err(Error::InvalidSince); - } } else { - // pending HTLC unlock process - // verify the unlock_htlc out of bound or not + // settlement unlock process + let pending_htlc_count = witness[0] as usize; - let unlock_htlc = unlock_type as usize; - if unlock_htlc >= pending_htlc_count { - return Err(Error::InvalidUnlockType); + // 1 (pending_htlc_count) + pending_htlc_count * HTLC_SCRIPT_LEN + let pending_htlcs_len = 1 + pending_htlc_count * HTLC_SCRIPT_LEN; + // settlement_one_pubkey_hash + settlement_one_amount + settlement_two_pubkey_hash + settlement_two_amount + let settlement_script_len = pending_htlcs_len + 72; + if witness.len() < settlement_script_len { + return Err(Error::WitnessLenError); + } + // verify the selettlement script hash is equal to the script args + if blake2b_256(&witness[0..settlement_script_len])[0..20] != args[36..56] { + return Err(Error::WitnessHashError); + } + + let mut settlements = Vec::new(); + let mut settlement_htlc_count = 0; + let mut i = pending_htlc_count; + while witness.len() > i { + let unlock_type = witness[pending_htlc_count]; + if unlock_type >= pending_htlc_count as u8 && unlock_type != 0xFD && unlock_type != 0xFE + { + return Err(Error::InvalidUnlockType); + } else { + settlement_htlc_count += 1; + } + let with_preimage = witness[pending_htlc_count + 1]; + if with_preimage == 0 { + settlements.push(Settlement(&witness[i..i + 2 + SIGNATURE_LEN])); + i += 2 + SIGNATURE_LEN; + } else if with_preimage == 1 { + settlements.push(Settlement( + &witness[i..i + 2 + SIGNATURE_LEN + PREIMAGE_LEN], + )); + i += 2 + SIGNATURE_LEN + PREIMAGE_LEN; + } else { + return Err(Error::InvalidWithPreimageFlag); + } } let raw_since_value = load_input_since(0, Source::GroupInput)?; + let delay_epoch = Since::new(u64::from_le_bytes(args[20..28].try_into().unwrap())); let message = { let tx = load_transaction()? .raw() @@ -258,29 +267,6 @@ fn auth() -> Result<(), Error> { .build(); blake2b_256(tx.as_slice()) }; - let mut signature = [0u8; 65]; - let mut pubkey_hash = [0u8; 20]; - - let witness_len = witness.len(); - let pending_htlcs_len = 1 + pending_htlc_count * HTLC_SCRIPT_LEN; - // verify the hash of the pending htlcs part is equal to the script args - if blake2b_256(&witness[0..pending_htlcs_len])[0..20] != args[36..56] { - return Err(Error::WitnessHashError); - } - if raw_since_value == 0 { - let expected_witness_len = pending_htlcs_len + SIGNATURE_LEN + PREIMAGE_LEN; - if witness_len != expected_witness_len { - return Err(Error::WitnessLenError); - } - signature - .copy_from_slice(&witness[pending_htlcs_len..pending_htlcs_len + SIGNATURE_LEN]); - } else { - let expected_witness_len = pending_htlcs_len + SIGNATURE_LEN; - if witness_len != expected_witness_len { - return Err(Error::WitnessLenError); - } - signature.copy_from_slice(&witness[pending_htlcs_len..]); - } let mut new_amount = if type_script.is_some() { let input_cell_data = load_cell_data(0, Source::GroupInput)?; @@ -288,17 +274,19 @@ fn auth() -> Result<(), Error> { } else { load_cell_capacity(0, Source::GroupInput)? as u128 }; - let mut new_pending_htlcs: Vec<&[u8]> = Vec::new(); - let new_pending_htlc_count = [(pending_htlc_count - 1) as u8]; - new_pending_htlcs.push(&new_pending_htlc_count); - let delay_epoch = Since::new(u64::from_le_bytes(args[20..28].try_into().unwrap())); + let mut new_settlement_script: Vec<&[u8]> = Vec::new(); + let new_pending_htlc_count = [(pending_htlc_count - settlement_htlc_count) as u8]; + new_settlement_script.push(&new_pending_htlc_count); + + let mut signatures_to_verify = Vec::new(); + for (i, htlc_script) in witness[1..pending_htlcs_len] .chunks(HTLC_SCRIPT_LEN) .enumerate() { - let htlc = Htlc(htlc_script); - if unlock_htlc == i { + if settlements[0].unlock_type() == i as u8 { + let htlc = Htlc(htlc_script); match htlc.htlc_type() { HtlcType::Offered => { if raw_since_value == 0 { @@ -307,7 +295,7 @@ fn auth() -> Result<(), Error> { return Err(Error::InvalidSince); } // when input since is 0, it means the unlock logic is for remote_htlc pubkey and preimage - let preimage = &witness[pending_htlcs_len + SIGNATURE_LEN..]; + let preimage = settlements[0].preimage(); if match htlc.payment_hash_type() { PaymentHashType::Blake2b => { htlc.payment_hash() != &blake2b_256(preimage)[0..20] @@ -319,7 +307,10 @@ fn auth() -> Result<(), Error> { return Err(Error::PreimageError); } new_amount -= htlc.payment_amount(); - pubkey_hash.copy_from_slice(htlc.remote_htlc_pubkey_hash()); + signatures_to_verify.push(( + settlements.remove(0).signature(), + htlc.remote_htlc_pubkey_hash(), + )); } else { // Expiry unlock delay_epoch should be shorter than non-pending unlock and longer than the preimage unlock, we use 2/3 of delay_epoch if !check_input_since(mul(delay_epoch, 2, 3)) { @@ -330,7 +321,10 @@ fn auth() -> Result<(), Error> { let htlc_expiry = Since::new(htlc.htlc_expiry()); if since >= htlc_expiry { new_amount -= htlc.payment_amount(); - pubkey_hash.copy_from_slice(htlc.local_htlc_pubkey_hash()); + signatures_to_verify.push(( + settlements.remove(0).signature(), + htlc.local_htlc_pubkey_hash(), + )); } else { return Err(Error::InvalidExpiry); } @@ -355,7 +349,10 @@ fn auth() -> Result<(), Error> { return Err(Error::PreimageError); } new_amount -= htlc.payment_amount(); - pubkey_hash.copy_from_slice(htlc.local_htlc_pubkey_hash()); + signatures_to_verify.push(( + settlements.remove(0).signature(), + htlc.local_htlc_pubkey_hash(), + )); } else { // Expiry unlock delay_epoch should be shorter than non-pending unlock and longer than the preimage unlock, we use 2/3 of delay_epoch if !check_input_since(mul(delay_epoch, 2, 3)) { @@ -366,7 +363,10 @@ fn auth() -> Result<(), Error> { 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()); + signatures_to_verify.push(( + settlements.remove(0).signature(), + htlc.remote_htlc_pubkey_hash(), + )); } else { return Err(Error::InvalidExpiry); } @@ -374,72 +374,103 @@ fn auth() -> Result<(), Error> { } } } else { - new_pending_htlcs.push(htlc_script); + new_settlement_script.push(htlc_script); } } - // verify the first output cell's lock script is correct - let output_lock = load_cell_lock(0, Source::Output)?; - let expected_lock_args = if new_pending_htlcs.len() == 1 { - args[0..36].pack() + // settlement for one or two parties + let zero = [0u8; 16]; + if settlements.len() == 1 { + let settlement = settlements.remove(0); + match settlement.unlock_type() { + 0xFD => { + let settlement_one_pubkey_hash: [u8; 20] = witness + [pending_htlcs_len..pending_htlcs_len + 20] + .try_into() + .unwrap(); + let settlement_one_amount = u128::from_le_bytes( + witness[pending_htlcs_len + 20..pending_htlcs_len + 36] + .try_into() + .unwrap(), + ); + new_amount -= settlement_one_amount; + + new_settlement_script.push(&witness[pending_htlcs_len..pending_htlcs_len + 20]); + new_settlement_script.push(zero.as_slice()); + new_settlement_script.push(&args[pending_htlcs_len + 36..]); + signatures_to_verify.push((settlement.signature(), settlement_one_pubkey_hash)); + } + 0xFE => { + let settlement_two_pubkey_hash: [u8; 20] = witness + [pending_htlcs_len + 36..pending_htlcs_len + 56] + .try_into() + .unwrap(); + let settlement_two_amount = u128::from_le_bytes( + witness[pending_htlcs_len + 56..pending_htlcs_len + 72] + .try_into() + .unwrap(), + ); + new_amount -= settlement_two_amount; + + new_settlement_script.push(&witness[pending_htlcs_len..pending_htlcs_len + 56]); + new_settlement_script.push(zero.as_slice()); + signatures_to_verify.push((settlement.signature(), settlement_two_pubkey_hash)); + } + _ => return Err(Error::InvalidUnlockType), + } + } else if settlements.len() == 0 { + new_settlement_script.push(&witness[pending_htlcs_len..]); } else { - [ + return Err(Error::InvalidSettlementCount); + } + + // verify the first output cell's lock script is correct + if new_amount > 0 { + let output_lock = load_cell_lock(0, Source::Output)?; + let expected_lock_args = [ &args[0..36], - blake2b_256(new_pending_htlcs.concat())[0..20].as_ref(), + blake2b_256(new_settlement_script.concat())[0..20].as_ref(), ] .concat() - .pack() - }; - if output_lock.code_hash() != script.code_hash() - || output_lock.hash_type() != script.hash_type() - || output_lock.args() != expected_lock_args - { - return Err(Error::OutputLockError); - } + .pack(); + if output_lock.code_hash() != script.code_hash() + || output_lock.hash_type() != script.hash_type() + || output_lock.args() != expected_lock_args + { + return Err(Error::OutputLockError); + } - match type_script { - Some(udt_script) => { - // verify the first output cell's capacity, type script and udt amount are correct - let output_capacity = load_cell_capacity(0, Source::Output)?; - let input_capacity = load_cell_capacity(0, Source::GroupInput)?; - if output_capacity != input_capacity { - return Err(Error::OutputCapacityError); - } + match type_script { + Some(udt_script) => { + // verify the first output cell's capacity, type script and udt amount are correct + let output_capacity = load_cell_capacity(0, Source::Output)?; + let input_capacity = load_cell_capacity(0, Source::GroupInput)?; + if output_capacity != input_capacity { + return Err(Error::OutputCapacityError); + } - let output_type = load_cell_type(0, Source::Output)?; - if output_type != Some(udt_script) { - return Err(Error::OutputTypeError); - } + let output_type = load_cell_type(0, Source::Output)?; + if output_type != Some(udt_script) { + return Err(Error::OutputTypeError); + } - let output_data = load_cell_data(0, Source::Output)?; - let output_amount = u128::from_le_bytes(output_data[0..16].try_into().unwrap()); - if output_amount != new_amount { - return Err(Error::OutputUdtAmountError); + let output_data = load_cell_data(0, Source::Output)?; + let output_amount = u128::from_le_bytes(output_data[0..16].try_into().unwrap()); + if output_amount != new_amount { + return Err(Error::OutputUdtAmountError); + } } - } - None => { - // verify the first output cell's capacity is correct - let output_capacity = load_cell_capacity(0, Source::Output)? as u128; - if output_capacity != new_amount { - return Err(Error::OutputCapacityError); + None => { + // verify the first output cell's capacity is correct + let output_capacity = load_cell_capacity(0, Source::Output)? as u128; + if output_capacity != new_amount { + return Err(Error::OutputCapacityError); + } } } } - // AuthAlgorithmIdCkb = 0 - let algorithm_id_str = CString::new(encode([0u8])).unwrap(); - let signature_str = CString::new(encode(signature)).unwrap(); - let message_str = CString::new(encode(message)).unwrap(); - let pubkey_hash_str = CString::new(encode(pubkey_hash)).unwrap(); - - let args = [ - algorithm_id_str.as_c_str(), - signature_str.as_c_str(), - message_str.as_c_str(), - pubkey_hash_str.as_c_str(), - ]; - - exec_cell(&AUTH_CODE_HASH, ScriptHashType::Data1, &args).map_err(|_| Error::AuthError)?; + // TODO call spawn auth to verify multiple signatures Ok(()) } } From 7e834d46bc438d2735698fc71075fa377a1032a2 Mon Sep 17 00:00:00 2001 From: quake Date: Mon, 22 Sep 2025 15:50:59 +0900 Subject: [PATCH 05/27] wip --- contracts/commitment-lock/README.md | 2 +- contracts/commitment-lock/src/main.rs | 39 +++++++--- tests/src/tests.rs | 104 +++++++++++++++----------- 3 files changed, 88 insertions(+), 57 deletions(-) diff --git a/contracts/commitment-lock/README.md b/contracts/commitment-lock/README.md index 09fe8d1..4bb7f71 100644 --- a/contracts/commitment-lock/README.md +++ b/contracts/commitment-lock/README.md @@ -11,7 +11,7 @@ The lock script args is concatenated by the following fields: To unlock this lock, the transaction must provide the following fields in the witness: - `empty_witness_args`: 16 bytes, fixed to 0x10000000100000001000000010000000, for compatibility with the xudt -- `unlocks`: 1 byte, 0xFF for revocation unlock, 0x00 ~ 0xFE for settlement unlocks count. +- `unlocks`: 1 byte, 0x00 for revocation unlock, 0x01 ~ 0xFF for settlement unlocks count. For revocation unlock process, the transaction must provide the following fields in the witness: - `version`: 8 bytes, u64 in big-endian, must be the same or greater than the version in the lock args diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index ce37a13..95f4567 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -14,14 +14,9 @@ default_alloc!(); use alloc::{ffi::CString, vec::Vec}; use ckb_std::{ - ckb_constants::Source, - ckb_types::{bytes::Bytes, core::ScriptHashType, prelude::*}, - error::SysError, - high_level::{ - QueryIter, exec_cell, load_cell, load_cell_capacity, load_cell_data, load_cell_lock, - load_cell_type, load_input_since, load_script, load_transaction, load_witness, - }, - since::{EpochNumberWithFraction, LockValue, Since}, + ckb_constants::Source, ckb_types::{bytes::Bytes, core::ScriptHashType, prelude::*}, debug, error::SysError, high_level::{ + exec_cell, load_cell, load_cell_capacity, load_cell_data, load_cell_lock, load_cell_type, load_input_since, load_script, load_transaction, load_witness, QueryIter + }, since::{EpochNumberWithFraction, LockValue, Since} }; use hex::encode; use sha2::{Digest, Sha256}; @@ -176,7 +171,7 @@ fn auth() -> Result<(), Error> { return Err(Error::EmptyWitnessArgsError); } let unlocks = witness.remove(0); - if unlocks == 0xFF { + if unlocks == 0x00 { // revocation unlock process // verify version @@ -218,7 +213,7 @@ fn auth() -> Result<(), Error> { Ok(()) } else { // settlement unlock process - + debug!("unlocks: {}", unlocks); let pending_htlc_count = witness[0] as usize; // 1 (pending_htlc_count) + pending_htlc_count * HTLC_SCRIPT_LEN let pending_htlcs_len = 1 + pending_htlc_count * HTLC_SCRIPT_LEN; @@ -239,6 +234,7 @@ fn auth() -> Result<(), Error> { let unlock_type = witness[pending_htlc_count]; if unlock_type >= pending_htlc_count as u8 && unlock_type != 0xFD && unlock_type != 0xFE { + debug!("Invalid unlock type 1: {}", unlock_type); return Err(Error::InvalidUnlockType); } else { settlement_htlc_count += 1; @@ -416,7 +412,10 @@ fn auth() -> Result<(), Error> { new_settlement_script.push(zero.as_slice()); signatures_to_verify.push((settlement.signature(), settlement_two_pubkey_hash)); } - _ => return Err(Error::InvalidUnlockType), + unlock => { + debug!("Invalid unlock type 2: {}", unlock); + return Err(Error::InvalidUnlockType); + } } } else if settlements.len() == 0 { new_settlement_script.push(&witness[pending_htlcs_len..]); @@ -470,7 +469,23 @@ fn auth() -> Result<(), Error> { } } // AuthAlgorithmIdCkb = 0 - // TODO call spawn auth to verify multiple signatures + for (signature, pubkey_hash) in signatures_to_verify { + let algorithm_id_str = CString::new(encode([0u8])).unwrap(); + let signature_str = CString::new(encode(&signature)).unwrap(); + let message_str = CString::new(encode(message)).unwrap(); + let pubkey_hash_str = CString::new(encode(&pubkey_hash)).unwrap(); + + let args = [ + algorithm_id_str.as_c_str(), + signature_str.as_c_str(), + message_str.as_c_str(), + pubkey_hash_str.as_c_str(), + ]; + + // TODO use spawn_cell + exec_cell(&AUTH_CODE_HASH, ScriptHashType::Data1, &args) + .map_err(|_| Error::AuthError)?; + } Ok(()) } } diff --git a/tests/src/tests.rs b/tests/src/tests.rs index aba2ef0..9d0a7f1 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -213,10 +213,26 @@ fn test_commitment_lock_no_pending_htlcs() { let delay_epoch = Since::from_epoch(EpochNumberWithFraction::new(10, 1, 2), false); // 42 hours let commitment_tx_version = 42u64; + let mut generator = Generator::new(); + let local_settlement_key = generator.gen_keypair(); + let remote_settlement_key = generator.gen_keypair(); + let local_amount = 500u128; + let remote_amount = 500u128; + + let settlement_script = [ + [0].to_vec(), + blake2b_256(local_settlement_key.1.serialize())[0..20].to_vec(), + local_amount.to_le_bytes().to_vec(), + blake2b_256(remote_settlement_key.1.serialize())[0..20].to_vec(), + remote_amount.to_le_bytes().to_vec(), + ] + .concat(); + let args = [ &pubkey_hash[0..20], delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), + &blake2b_256(&settlement_script)[0..20] ] .concat(); @@ -279,7 +295,7 @@ fn test_commitment_lock_no_pending_htlcs() { let signature = multisig(sec_key_1, sec_key_2, key_agg_ctx.clone(), message); let witness = [ EMPTY_WITNESS_ARGS.to_vec(), - vec![0xFF], + vec![0x00], commitment_tx_new_version.to_be_bytes().to_vec(), x_only_pubkey.to_vec(), signature, @@ -295,67 +311,67 @@ fn test_commitment_lock_no_pending_htlcs() { .expect("pass verification"); println!("consume cycles: {}", cycles); - // build transaction with non-pending HTLC unlock logic - let to_local_output_script = Script::new_builder() - .args(Bytes::from("to_local_output").pack()) - .build(); - let to_local_output = CellOutput::new_builder() - .capacity(500u64.pack()) - .lock(to_local_output_script) - .build(); - let to_local_output_data = Bytes::from("to_local_output_data").pack(); - let to_remote_output_script = Script::new_builder() - .args(Bytes::from("to_remote_output").pack()) - .build(); - let to_remote_output = CellOutput::new_builder() - .capacity(500u64.pack()) - .lock(to_remote_output_script) - .build(); - let to_remote_output_data = Bytes::from("to_remote_output_data").pack(); + // build transaction with settlement unlock logic + let new_settlement_script = [ + [0].to_vec(), + blake2b_256(local_settlement_key.1.serialize())[0..20].to_vec(), + 0u128.to_le_bytes().to_vec(), + blake2b_256(remote_settlement_key.1.serialize())[0..20].to_vec(), + remote_amount.to_le_bytes().to_vec(), + ].concat(); + + let new_args = [ + &pubkey_hash[0..20], + delay_epoch.as_u64().to_le_bytes().as_slice(), + commitment_tx_version.to_be_bytes().as_slice(), + &blake2b_256(&new_settlement_script)[0..20], + ] + .concat(); - let outputs = vec![to_local_output.clone(), to_remote_output.clone()]; - let outputs_data = vec![to_local_output_data.clone(), to_remote_output_data.clone()]; + let new_lock_script = lock_script + .clone() + .as_builder() + .args(new_args.pack()) + .build(); + let outputs = vec![ + CellOutput::new_builder() + .capacity((1000 * BYTE_SHANNONS - local_amount as u64).pack()) + .lock(new_lock_script.clone()) + .build(), + ]; + let outputs_data = [Bytes::new()]; - let since = Since::from_epoch(EpochNumberWithFraction::new(12, 0, 1), false); // 48 hours let input = CellInput::new_builder() - .previous_output(input_out_point) - .since(since.as_u64().pack()) + .previous_output(input_out_point.clone()) .build(); let tx = TransactionBuilder::default() - .cell_deps(cell_deps) + .cell_deps(cell_deps.clone()) .input(input) .outputs(outputs) .outputs_data(outputs_data.pack()) .build(); - // sign and add witness - let message = blake2b_256( - [ - to_local_output.as_slice(), - to_local_output_data.as_slice(), - to_remote_output.as_slice(), - to_remote_output_data.as_slice(), - &args, - ] - .concat(), - ); + // sign with local_settlement_key + let message: [u8; 32] = compute_tx_message(&tx); - let signature = multisig(sec_key_1, sec_key_2, key_agg_ctx, message); + let signature = local_settlement_key + .0 + .sign_recoverable(&message.into()) + .unwrap() + .serialize(); let witness = [ EMPTY_WITNESS_ARGS.to_vec(), - vec![0xFE], - x_only_pubkey.to_vec(), - signature, + vec![0x01], + settlement_script.clone(), + vec![0xFD], + signature.clone(), ] .concat(); - let tx = tx.as_advanced_builder().witness(witness.pack()).build(); - println!("tx: {:?}", tx); - - // run + let success_tx = tx.as_advanced_builder().witness(witness.pack()).build(); let cycles = context - .verify_tx(&tx, MAX_CYCLES) + .verify_tx(&success_tx, MAX_CYCLES) .expect("pass verification"); println!("consume cycles: {}", cycles); } From 2971088a57cc7abc6aac4f95195e81026884b917 Mon Sep 17 00:00:00 2001 From: quake Date: Mon, 22 Sep 2025 16:05:45 +0900 Subject: [PATCH 06/27] wip --- contracts/commitment-lock/src/main.rs | 12 +++++++----- tests/src/tests.rs | 2 +- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index 95f4567..88e282e 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -229,17 +229,18 @@ fn auth() -> Result<(), Error> { let mut settlements = Vec::new(); let mut settlement_htlc_count = 0; - let mut i = pending_htlc_count; + let mut i = settlement_script_len; while witness.len() > i { - let unlock_type = witness[pending_htlc_count]; + debug!("i: {}, len: {}", i, witness.len()); + let unlock_type = witness[i]; if unlock_type >= pending_htlc_count as u8 && unlock_type != 0xFD && unlock_type != 0xFE { - debug!("Invalid unlock type 1: {}", unlock_type); + debug!("Invalid unlock type 1: {:?}", witness); return Err(Error::InvalidUnlockType); - } else { + } else if unlock_type < pending_htlc_count as u8 { settlement_htlc_count += 1; } - let with_preimage = witness[pending_htlc_count + 1]; + let with_preimage = witness[i + 1]; if with_preimage == 0 { settlements.push(Settlement(&witness[i..i + 2 + SIGNATURE_LEN])); i += 2 + SIGNATURE_LEN; @@ -249,6 +250,7 @@ fn auth() -> Result<(), Error> { )); i += 2 + SIGNATURE_LEN + PREIMAGE_LEN; } else { + debug!("Invalid with_preimage flag: {}", with_preimage); return Err(Error::InvalidWithPreimageFlag); } } diff --git a/tests/src/tests.rs b/tests/src/tests.rs index 9d0a7f1..b529437 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -364,7 +364,7 @@ fn test_commitment_lock_no_pending_htlcs() { EMPTY_WITNESS_ARGS.to_vec(), vec![0x01], settlement_script.clone(), - vec![0xFD], + vec![0xFD, 0x00], // unlock with local settlement key, no preimage signature.clone(), ] .concat(); From f7fcbcbdc3d45cf1cdcc41f9e5c2d5090823aad1 Mon Sep 17 00:00:00 2001 From: quake Date: Tue, 23 Sep 2025 11:56:32 +0900 Subject: [PATCH 07/27] fix test1 --- contracts/commitment-lock/README.md | 2 +- contracts/commitment-lock/src/main.rs | 27 ++++++++---- tests/src/tests.rs | 61 ++++++++++++++++++++++++--- 3 files changed, 74 insertions(+), 16 deletions(-) diff --git a/contracts/commitment-lock/README.md b/contracts/commitment-lock/README.md index 4bb7f71..da4d78e 100644 --- a/contracts/commitment-lock/README.md +++ b/contracts/commitment-lock/README.md @@ -11,7 +11,7 @@ The lock script args is concatenated by the following fields: To unlock this lock, the transaction must provide the following fields in the witness: - `empty_witness_args`: 16 bytes, fixed to 0x10000000100000001000000010000000, for compatibility with the xudt -- `unlocks`: 1 byte, 0x00 for revocation unlock, 0x01 ~ 0xFF for settlement unlocks count. +- `unlock_count`: 1 byte, 0x00 for revocation unlock, 0x01 ~ 0xFF for settlement unlocks count. For revocation unlock process, the transaction must provide the following fields in the witness: - `version`: 8 bytes, u64 in big-endian, must be the same or greater than the version in the lock args diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index 88e282e..df00815 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -14,9 +14,16 @@ default_alloc!(); use alloc::{ffi::CString, vec::Vec}; use ckb_std::{ - ckb_constants::Source, ckb_types::{bytes::Bytes, core::ScriptHashType, prelude::*}, debug, error::SysError, high_level::{ - exec_cell, load_cell, load_cell_capacity, load_cell_data, load_cell_lock, load_cell_type, load_input_since, load_script, load_transaction, load_witness, QueryIter - }, since::{EpochNumberWithFraction, LockValue, Since} + ckb_constants::Source, + ckb_types::{bytes::Bytes, core::ScriptHashType, prelude::*}, + debug, + error::SysError, + high_level::{ + QueryIter, exec_cell, load_cell, load_cell_capacity, load_cell_data, load_cell_lock, + load_cell_type, load_input_since, load_script, load_transaction, load_witness, + }, + since::{EpochNumberWithFraction, LockValue, Since}, + syscalls::debug, }; use hex::encode; use sha2::{Digest, Sha256}; @@ -170,8 +177,8 @@ fn auth() -> Result<(), Error> { { return Err(Error::EmptyWitnessArgsError); } - let unlocks = witness.remove(0); - if unlocks == 0x00 { + let unlock_count = witness.remove(0); + if unlock_count == 0x00 { // revocation unlock process // verify version @@ -213,7 +220,7 @@ fn auth() -> Result<(), Error> { Ok(()) } else { // settlement unlock process - debug!("unlocks: {}", unlocks); + debug!("unlock_count: {}", unlock_count); let pending_htlc_count = witness[0] as usize; // 1 (pending_htlc_count) + pending_htlc_count * HTLC_SCRIPT_LEN let pending_htlcs_len = 1 + pending_htlc_count * HTLC_SCRIPT_LEN; @@ -395,7 +402,8 @@ fn auth() -> Result<(), Error> { new_settlement_script.push(&witness[pending_htlcs_len..pending_htlcs_len + 20]); new_settlement_script.push(zero.as_slice()); - new_settlement_script.push(&args[pending_htlcs_len + 36..]); + new_settlement_script + .push(&witness[pending_htlcs_len + 36..pending_htlcs_len + 72]); signatures_to_verify.push((settlement.signature(), settlement_one_pubkey_hash)); } 0xFE => { @@ -425,7 +433,10 @@ fn auth() -> Result<(), Error> { return Err(Error::InvalidSettlementCount); } - // verify the first output cell's lock script is correct + debug!("new_settlement_script: {:x?}", new_settlement_script); + debug!("new_amount: {}", new_amount); + + // verify the first output cell's lock script and capacity are correct if new_amount > 0 { let output_lock = load_cell_lock(0, Source::Output)?; let expected_lock_args = [ diff --git a/tests/src/tests.rs b/tests/src/tests.rs index b529437..aa39cec 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -216,8 +216,8 @@ fn test_commitment_lock_no_pending_htlcs() { let mut generator = Generator::new(); let local_settlement_key = generator.gen_keypair(); let remote_settlement_key = generator.gen_keypair(); - let local_amount = 500u128; - let remote_amount = 500u128; + let local_amount = 600u128; + let remote_amount = 400u128; let settlement_script = [ [0].to_vec(), @@ -232,7 +232,7 @@ fn test_commitment_lock_no_pending_htlcs() { &pubkey_hash[0..20], delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), - &blake2b_256(&settlement_script)[0..20] + &blake2b_256(&settlement_script)[0..20], ] .concat(); @@ -311,14 +311,15 @@ fn test_commitment_lock_no_pending_htlcs() { .expect("pass verification"); println!("consume cycles: {}", cycles); - // build transaction with settlement unlock logic + // test with settlement unlock logic (local settlement key) let new_settlement_script = [ [0].to_vec(), blake2b_256(local_settlement_key.1.serialize())[0..20].to_vec(), 0u128.to_le_bytes().to_vec(), blake2b_256(remote_settlement_key.1.serialize())[0..20].to_vec(), remote_amount.to_le_bytes().to_vec(), - ].concat(); + ] + .concat(); let new_args = [ &pubkey_hash[0..20], @@ -335,7 +336,7 @@ fn test_commitment_lock_no_pending_htlcs() { .build(); let outputs = vec![ CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS - local_amount as u64).pack()) + .capacity((1000 - local_amount as u64).pack()) .lock(new_lock_script.clone()) .build(), ]; @@ -348,7 +349,7 @@ fn test_commitment_lock_no_pending_htlcs() { let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) .input(input) - .outputs(outputs) + .outputs(outputs.clone()) .outputs_data(outputs_data.pack()) .build(); @@ -374,6 +375,52 @@ fn test_commitment_lock_no_pending_htlcs() { .verify_tx(&success_tx, MAX_CYCLES) .expect("pass verification"); println!("consume cycles: {}", cycles); + + // test with settlement unlock logic (remote settlement key) + let input_out_point = context.create_cell(outputs[0].clone(), Bytes::new()); + + let input = CellInput::new_builder() + .previous_output(input_out_point) + .since(delay_epoch.as_u64().pack()) + .build(); + + let outputs = vec![ + CellOutput::new_builder() + .capacity((remote_amount as u64).pack()) + .lock(Script::new_builder().build()) + .build(), + ]; + let outputs_data = [Bytes::new()]; + + let tx = TransactionBuilder::default() + .cell_deps(cell_deps.clone()) + .input(input) + .outputs(outputs) + .outputs_data(outputs_data.pack()) + .build(); + + // sign with local_settlement_key + let message: [u8; 32] = compute_tx_message(&tx); + + let signature = remote_settlement_key + .0 + .sign_recoverable(&message.into()) + .unwrap() + .serialize(); + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + vec![0x01], + new_settlement_script.clone(), + vec![0xFE, 0x00], // unlock with remote settlement key, no preimage + signature.clone(), + ] + .concat(); + + let success_tx = tx.as_advanced_builder().witness(witness.pack()).build(); + let cycles = context + .verify_tx(&success_tx, MAX_CYCLES) + .expect("pass verification"); + println!("consume cycles: {}", cycles); } #[test] From c9edcc12012867cfc5d33e05dfd6fbdd161fc365 Mon Sep 17 00:00:00 2001 From: quake Date: Tue, 23 Sep 2025 16:24:48 +0900 Subject: [PATCH 08/27] fix test2 --- contracts/commitment-lock/README.md | 2 +- contracts/commitment-lock/src/main.rs | 18 ++++- tests/src/tests.rs | 112 +++++++++++++++++--------- 3 files changed, 89 insertions(+), 43 deletions(-) diff --git a/contracts/commitment-lock/README.md b/contracts/commitment-lock/README.md index da4d78e..1453505 100644 --- a/contracts/commitment-lock/README.md +++ b/contracts/commitment-lock/README.md @@ -33,7 +33,7 @@ For settlement unlock process, the transaction must provide the following fields - `settlement_two_amount`: 16 bytes, u128 in little endian - `unlocks`: A group of settlement unlock signature and preimage - - `unlock_type`: 0x00 ~ 0xFC for pending htlc group, 0xFD for settlement one, 0xFE for settlement two. + - `unlock_type`: 0x00 ~ 0xFC for pending htlc group index, 0xFD for settlement one, 0xFE for settlement two. - `with_preimage`: 0x00 without preimage, 0x01 with preimage - `signature`: 65 bytes, the signature of the xxx_pubkey - `preimage`: 32 bytes, an optional field to provide the preimage of the payment_hash diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index df00815..88fd414 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -150,7 +150,11 @@ impl<'a> Settlement<'a> { } fn preimage(&self) -> &[u8] { - &self.0[2 + SIGNATURE_LEN..2 + SIGNATURE_LEN + PREIMAGE_LEN] + if self.with_preimage() { + &self.0[2 + SIGNATURE_LEN..2 + SIGNATURE_LEN + PREIMAGE_LEN] + } else { + &[] + } } } @@ -222,6 +226,8 @@ fn auth() -> Result<(), Error> { // settlement unlock process debug!("unlock_count: {}", unlock_count); let pending_htlc_count = witness[0] as usize; + debug!("pending_htlc_count: {}", pending_htlc_count); + debug!("witness: {:x?}", witness); // 1 (pending_htlc_count) + pending_htlc_count * HTLC_SCRIPT_LEN let pending_htlcs_len = 1 + pending_htlc_count * HTLC_SCRIPT_LEN; // settlement_one_pubkey_hash + settlement_one_amount + settlement_two_pubkey_hash + settlement_two_amount @@ -262,6 +268,10 @@ fn auth() -> Result<(), Error> { } } + if settlements.len() == 0 { + return Err(Error::InvalidSettlementCount); + } + let raw_since_value = load_input_since(0, Source::GroupInput)?; let delay_epoch = Since::new(u64::from_le_bytes(args[20..28].try_into().unwrap())); let message = { @@ -290,7 +300,7 @@ fn auth() -> Result<(), Error> { .chunks(HTLC_SCRIPT_LEN) .enumerate() { - if settlements[0].unlock_type() == i as u8 { + if settlements.len() > 0 && settlements[0].unlock_type() == i as u8 { let htlc = Htlc(htlc_script); match htlc.htlc_type() { HtlcType::Offered => { @@ -342,7 +352,7 @@ fn auth() -> Result<(), Error> { return Err(Error::InvalidSince); } // when input since is 0, it means the unlock logic is for local_htlc pubkey and preimage - let preimage = &witness[pending_htlcs_len + SIGNATURE_LEN..]; + let preimage = settlements[0].preimage(); if match htlc.payment_hash_type() { PaymentHashType::Blake2b => { htlc.payment_hash() != &blake2b_256(preimage)[0..20] @@ -428,7 +438,7 @@ fn auth() -> Result<(), Error> { } } } else if settlements.len() == 0 { - new_settlement_script.push(&witness[pending_htlcs_len..]); + new_settlement_script.push(&witness[pending_htlcs_len..pending_htlcs_len + 72]); } else { return Err(Error::InvalidSettlementCount); } diff --git a/tests/src/tests.rs b/tests/src/tests.rs index aa39cec..6fd51bb 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -216,8 +216,8 @@ fn test_commitment_lock_no_pending_htlcs() { let mut generator = Generator::new(); let local_settlement_key = generator.gen_keypair(); let remote_settlement_key = generator.gen_keypair(); - let local_amount = 600u128; - let remote_amount = 400u128; + let local_amount = (600 * BYTE_SHANNONS) as u128; + let remote_amount = (400 * BYTE_SHANNONS) as u128; let settlement_script = [ [0].to_vec(), @@ -250,7 +250,7 @@ fn test_commitment_lock_no_pending_htlcs() { // prepare cells let input_out_point = context.create_cell( CellOutput::new_builder() - .capacity(1000u64.pack()) + .capacity(((local_amount + remote_amount) as u64).pack()) .lock(lock_script.clone()) .build(), Bytes::new(), @@ -261,7 +261,7 @@ fn test_commitment_lock_no_pending_htlcs() { .args(Bytes::from("to_local_output").pack()) .build(); let to_revocation_output = CellOutput::new_builder() - .capacity(1000u64.pack()) + .capacity(((local_amount + remote_amount) as u64).pack()) .lock(to_revocation_lock) .build(); let to_revocation_output_data = Bytes::from("to_revocation_output_data").pack(); @@ -336,7 +336,7 @@ fn test_commitment_lock_no_pending_htlcs() { .build(); let outputs = vec![ CellOutput::new_builder() - .capacity((1000 - local_amount as u64).pack()) + .capacity((remote_amount as u64).pack()) .lock(new_lock_script.clone()) .build(), ]; @@ -444,6 +444,11 @@ fn test_commitment_lock_with_two_pending_htlcs() { let commitment_tx_version = 42u64; let mut generator = Generator::new(); + let local_settlement_key = generator.gen_keypair(); + let remote_settlement_key = generator.gen_keypair(); + let local_amount = (600 * BYTE_SHANNONS) as u128; + let remote_amount = (400 * BYTE_SHANNONS) as u128; + let remote_htlc_key1 = generator.gen_keypair(); let remote_htlc_key2 = generator.gen_keypair(); let local_htlc_key1 = generator.gen_keypair(); @@ -474,11 +479,21 @@ fn test_commitment_lock_with_two_pending_htlcs() { ] .concat(); + let two_party_settlement = [ + blake2b_256(local_settlement_key.1.serialize())[0..20].to_vec(), + local_amount.to_le_bytes().to_vec(), + blake2b_256(remote_settlement_key.1.serialize())[0..20].to_vec(), + remote_amount.to_le_bytes().to_vec(), + ] + .concat(); + + let settlement_script = [pending_htlcs.clone(), two_party_settlement.clone()].concat(); + let args = [ &pubkey_hash[0..20], delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), - &blake2b_256(&pending_htlcs)[0..20], + &blake2b_256(&settlement_script)[0..20], ] .concat(); @@ -502,7 +517,9 @@ fn test_commitment_lock_with_two_pending_htlcs() { // prepare cells let input_out_point = context.create_cell( CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS).pack()) + .capacity( + ((local_amount + remote_amount + payment_amount1 + payment_amount2) as u64).pack(), + ) .lock(lock_script.clone()) .build(), Bytes::new(), @@ -542,11 +559,12 @@ fn test_commitment_lock_with_two_pending_htlcs() { ] .concat(); + let new_settlement_script = [new_pending_htlcs.clone(), two_party_settlement.clone()].concat(); let new_args = [ &pubkey_hash[0..20], delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), - &blake2b_256(new_pending_htlcs)[0..20], + &blake2b_256(new_settlement_script)[0..20], ] .concat(); let new_lock_script = lock_script @@ -556,7 +574,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { .build(); let outputs = vec![ CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS - payment_amount1 as u64).pack()) + .capacity(((local_amount + remote_amount + payment_amount2) as u64).pack()) .lock(new_lock_script.clone()) .build(), ]; @@ -568,7 +586,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { .outputs_data(outputs_data.pack()) .build(); - // sign with remote_htlc_pubkey + // sign with remote_htlc_key1 let message: [u8; 32] = compute_tx_message(&tx); let signature = remote_htlc_key1 @@ -578,8 +596,9 @@ fn test_commitment_lock_with_two_pending_htlcs() { .serialize(); let witness = [ EMPTY_WITNESS_ARGS.to_vec(), - vec![0x00], - pending_htlcs.clone(), + vec![0x01], + settlement_script.clone(), + [0x00, 0x01].to_vec(), // unlock with remote_htlc_key1 and preimage signature.clone(), preimage1.to_vec(), ] @@ -591,11 +610,12 @@ fn test_commitment_lock_with_two_pending_htlcs() { .expect("pass verification"); println!("consume cycles: {}", cycles); - // sign with remote_htlc_pubkey and wrong preimage should fail + // sign with remote_htlc_key1 and wrong preimage should fail let witness = [ EMPTY_WITNESS_ARGS.to_vec(), - vec![0x00], - pending_htlcs.clone(), + vec![0x01], + settlement_script.clone(), + [0x00, 0x01].to_vec(), // unlock with remote_htlc_key1 and preimage signature.clone(), preimage2.to_vec(), ] @@ -608,13 +628,15 @@ fn test_commitment_lock_with_two_pending_htlcs() { .verify_tx(&fail_tx, MAX_CYCLES) .expect_err("wrong preimage should fail"); println!("error: {}", error); + assert!(error.to_string().contains("#21")); // PreimageError // sign with remote_htlc_pubkey and empty preimage should fail let witness = [ EMPTY_WITNESS_ARGS.to_vec(), - vec![0x00], - pending_htlcs.clone(), - signature, + vec![0x01], + settlement_script.clone(), + [0x00, 0x00].to_vec(), // unlock with remote_htlc_key1 and no preimage + signature.clone(), ] .concat(); @@ -625,6 +647,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { .verify_tx(&fail_tx, MAX_CYCLES) .expect_err("empty preimage should fail"); println!("error: {}", error); + assert!(error.to_string().contains("#21")); // PreimageError // build transaction with local_htlc_pubkey unlock offered pending htlc 1 let since = Since::from_timestamp(1711976400 + 1000, true).unwrap(); @@ -636,7 +659,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { let inputs = vec![input, delay_epoch_input.clone()]; let outputs = vec![ CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS - payment_amount1 as u64).pack()) + .capacity(((local_amount + remote_amount + payment_amount2) as u64).pack()) .lock(new_lock_script.clone()) .build(), ]; @@ -658,8 +681,9 @@ fn test_commitment_lock_with_two_pending_htlcs() { .serialize(); let witness = [ EMPTY_WITNESS_ARGS.to_vec(), - vec![0x00], - pending_htlcs.clone(), + vec![0x01], + settlement_script.clone(), + [0x00, 0x00].to_vec(), // unlock with local_htlc_key1 and no preimage signature.clone(), ] .concat(); @@ -696,9 +720,10 @@ fn test_commitment_lock_with_two_pending_htlcs() { .serialize(); let witness = [ EMPTY_WITNESS_ARGS.to_vec(), - vec![0x00], - pending_htlcs.clone(), - signature, + vec![0x01], + settlement_script.clone(), + [0x00, 0x00].to_vec(), // unlock with local_htlc_key1 and no preimage + signature.clone(), ] .concat(); @@ -707,8 +732,9 @@ fn test_commitment_lock_with_two_pending_htlcs() { .verify_tx(&fail_tx, MAX_CYCLES) .expect_err("none-expired since should fail"); println!("error: {}", error); + assert!(error.to_string().contains("#10")); // InvalidExpiry - // build transaction with remote_htlc_pubkey unlock received pending htlc 2 + // build transaction with remote_htlc_pubkey2 unlock received pending htlc 2 let since = Since::from_timestamp(1712062800 + 1000, true).unwrap(); let input = CellInput::new_builder() .since(since.as_u64().pack()) @@ -726,17 +752,20 @@ fn test_commitment_lock_with_two_pending_htlcs() { expiry1.as_u64().to_le_bytes().to_vec(), ] .concat(); + let new_settlement_script = [new_pending_htlcs.clone(), two_party_settlement.clone()].concat(); + + println!("new_settlement_script: {:x?}", new_settlement_script); let new_args = [ &pubkey_hash[0..20], delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), - &blake2b_256(new_pending_htlcs)[0..20], + &blake2b_256(new_settlement_script)[0..20], ] .concat(); let new_lock_script = lock_script.as_builder().args(new_args.pack()).build(); let outputs = vec![ CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS - payment_amount2 as u64).pack()) + .capacity(((local_amount + remote_amount + payment_amount1) as u64).pack()) .lock(new_lock_script.clone()) .build(), ]; @@ -748,7 +777,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { .outputs_data(outputs_data.pack()) .build(); - // sign with remote_htlc_pubkey + // sign with remote_htlc_pubkey2 let message: [u8; 32] = compute_tx_message(&tx); let signature = remote_htlc_key2 @@ -759,8 +788,9 @@ fn test_commitment_lock_with_two_pending_htlcs() { let witness = [ EMPTY_WITNESS_ARGS.to_vec(), vec![0x01], - pending_htlcs.clone(), - signature, + settlement_script.clone(), + [0x01, 0x00].to_vec(), // unlock with remote_htlc_pubkey2 and no preimage + signature.clone(), ] .concat(); @@ -773,7 +803,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { .expect("pass verification"); println!("consume cycles: {}", cycles); - // build transaction with local_htlc_pubkey unlock received pending htlc 2 + // build transaction with local_htlc_pubkey2 unlock received pending htlc 2 let input = CellInput::new_builder() .previous_output(input_out_point.clone()) .build(); @@ -787,7 +817,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { ]; let outputs = vec![ CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS - payment_amount2 as u64).pack()) + .capacity(((local_amount + remote_amount + payment_amount1) as u64).pack()) .lock(new_lock_script.clone()) .build(), ]; @@ -799,7 +829,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { .outputs_data(outputs_data.pack()) .build(); - // sign with local_htlc_pubkey + // sign with local_htlc_key2 let message: [u8; 32] = compute_tx_message(&tx); let signature = local_htlc_key2 @@ -810,7 +840,8 @@ fn test_commitment_lock_with_two_pending_htlcs() { let witness = [ EMPTY_WITNESS_ARGS.to_vec(), vec![0x01], - pending_htlcs.clone(), + settlement_script.clone(), + [0x01, 0x01].to_vec(), // unlock with local_htlc_key2 and preimage signature.clone(), preimage2.to_vec(), ] @@ -822,11 +853,12 @@ fn test_commitment_lock_with_two_pending_htlcs() { .expect("pass verification"); println!("consume cycles: {}", cycles); - // sign with local_htlc_pubkey and wrong preimage should fail + // sign with local_htlc_key2 and wrong preimage should fail let witness = [ EMPTY_WITNESS_ARGS.to_vec(), vec![0x01], - pending_htlcs.clone(), + settlement_script.clone(), + [0x01, 0x01].to_vec(), // unlock with local_htlc_key2 and preimage signature.clone(), preimage1.to_vec(), ] @@ -837,12 +869,14 @@ fn test_commitment_lock_with_two_pending_htlcs() { .verify_tx(&fail_tx, MAX_CYCLES) .expect_err("wrong preimage should fail"); println!("error: {}", error); + assert!(error.to_string().contains("#21")); // PreimageError - // sign with local_htlc_pubkey and empty preimage should fail + // sign with local_htlc_key2 and empty preimage should fail let witness = [ EMPTY_WITNESS_ARGS.to_vec(), vec![0x01], - pending_htlcs.clone(), + settlement_script.clone(), + [0x01, 0x00].to_vec(), // unlock with local_htlc_key2 and no preimage signature, ] .concat(); @@ -852,8 +886,10 @@ fn test_commitment_lock_with_two_pending_htlcs() { .verify_tx(&fail_tx, MAX_CYCLES) .expect_err("empty preimage should fail"); println!("error: {}", error); + assert!(error.to_string().contains("#21")); // PreimageError } +#[ignore] #[test] fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { // deploy contract From f2e3b65ed95ef57851a594f713050dbbc52c3f3a Mon Sep 17 00:00:00 2001 From: quake Date: Tue, 23 Sep 2025 16:31:55 +0900 Subject: [PATCH 09/27] fix test3 --- contracts/commitment-lock/src/main.rs | 1 - tests/src/tests.rs | 190 ++++++++++++++++---------- 2 files changed, 120 insertions(+), 71 deletions(-) diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index 88fd414..d4d6818 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -23,7 +23,6 @@ use ckb_std::{ load_cell_type, load_input_since, load_script, load_transaction, load_witness, }, since::{EpochNumberWithFraction, LockValue, Since}, - syscalls::debug, }; use hex::encode; use sha2::{Digest, Sha256}; diff --git a/tests/src/tests.rs b/tests/src/tests.rs index 6fd51bb..8638762 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -889,7 +889,6 @@ fn test_commitment_lock_with_two_pending_htlcs() { assert!(error.to_string().contains("#21")); // PreimageError } -#[ignore] #[test] fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { // deploy contract @@ -913,6 +912,11 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { let commitment_tx_version = 42u64; let mut generator = Generator::new(); + let local_settlement_key = generator.gen_keypair(); + let remote_settlement_key = generator.gen_keypair(); + let local_amount = 11111111111111111111u128; + let remote_amount = 22222222222222222222u128; + let remote_htlc_key1 = generator.gen_keypair(); let remote_htlc_key2 = generator.gen_keypair(); let local_htlc_key1 = generator.gen_keypair(); @@ -943,11 +947,21 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { ] .concat(); + let two_party_settlement = [ + blake2b_256(local_settlement_key.1.serialize())[0..20].to_vec(), + local_amount.to_le_bytes().to_vec(), + blake2b_256(remote_settlement_key.1.serialize())[0..20].to_vec(), + remote_amount.to_le_bytes().to_vec(), + ] + .concat(); + + let settlement_script = [pending_htlcs.clone(), two_party_settlement.clone()].concat(); + let args = [ &pubkey_hash[0..20], delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), - &blake2b_256(&pending_htlcs)[0..20], + &blake2b_256(&settlement_script)[0..20], ] .concat(); @@ -981,7 +995,7 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { .pack(); // prepare cells - let total_sudt_amount = 424242424242424242u128; + let total_sudt_amount = local_amount + remote_amount + payment_amount1 + payment_amount2; let input_out_point = context.create_cell( CellOutput::new_builder() .capacity((1000 * BYTE_SHANNONS).pack()) @@ -1024,11 +1038,12 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { expiry2.as_u64().to_le_bytes().to_vec(), ] .concat(); + let new_settlement_script = [new_pending_htlcs.clone(), two_party_settlement.clone()].concat(); let new_args = [ &pubkey_hash[0..20], delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), - &blake2b_256(&new_pending_htlcs)[0..20], + &blake2b_256(&new_settlement_script)[0..20], ] .concat(); let new_lock_script = lock_script @@ -1052,11 +1067,11 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) .inputs(inputs) - .outputs(outputs) + .outputs(outputs.clone()) .outputs_data(outputs_data.pack()) .build(); - // sign with remote_htlc_pubkey + // sign with remote_htlc_key1 let message: [u8; 32] = compute_tx_message(&tx); let signature = remote_htlc_key1 @@ -1066,22 +1081,53 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { .serialize(); let witness = [ EMPTY_WITNESS_ARGS.to_vec(), - vec![0x00], - pending_htlcs.clone(), - signature, + vec![0x01], + settlement_script.clone(), + [0x00, 0x01].to_vec(), // unlock with remote_htlc_key1 and preimage + signature.clone(), preimage1.to_vec(), ] .concat(); - let tx = tx.as_advanced_builder().witness(witness.pack()).build(); - println!("tx: {:?}", tx); - - // run + let success_tx = tx.as_advanced_builder().witness(witness.pack()).build(); let cycles = context - .verify_tx(&tx, MAX_CYCLES) + .verify_tx(&success_tx, MAX_CYCLES) .expect("pass verification"); println!("consume cycles: {}", cycles); + // sign with remote_htlc_key1 and wrong preimage should fail + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + vec![0x01], + settlement_script.clone(), + [0x00, 0x01].to_vec(), // unlock with remote_htlc_key1 and preimage + signature.clone(), + preimage2.to_vec(), + ] + .concat(); + + let fail_tx = tx.as_advanced_builder().witness(witness.pack()).build(); + let err = context + .verify_tx(&fail_tx, MAX_CYCLES) + .expect_err("wrong preimage should fail"); + assert!(err.to_string().contains("#21")); // PreimageError + + // sign with remote_htlc_key1 and empty preimage should fail + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + vec![0x01], + settlement_script.clone(), + [0x00, 0x00].to_vec(), // unlock with remote_htlc_key1 and no preimage + signature.clone(), + ] + .concat(); + + let fail_tx = tx.as_advanced_builder().witness(witness.pack()).build(); + let err = context + .verify_tx(&fail_tx, MAX_CYCLES) + .expect_err("empty preimage should fail"); + assert!(err.to_string().contains("#21")); // PreimageError + // build transaction with local_htlc_pubkey unlock offered pending htlc 1 let since = Since::from_timestamp(1711976400 + 1000, true).unwrap(); @@ -1090,27 +1136,15 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { .since(since.as_u64().pack()) .build(); let inputs = vec![input, delay_epoch_input.clone()]; - let outputs = vec![ - CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS).pack()) - .lock(new_lock_script.clone()) - .type_(Some(type_script.clone()).pack()) - .build(), - ]; - let outputs_data: Vec = vec![ - (total_sudt_amount - payment_amount1) - .to_le_bytes() - .to_vec() - .into(), - ]; + let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) .inputs(inputs) - .outputs(outputs) + .outputs(outputs.clone()) .outputs_data(outputs_data.pack()) .build(); - // sign with local_htlc_pubkey + // sign with local_htlc_key1 let message: [u8; 32] = compute_tx_message(&tx); let signature = local_htlc_key1 @@ -1120,22 +1154,20 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { .serialize(); let witness = [ EMPTY_WITNESS_ARGS.to_vec(), - vec![0x00], - pending_htlcs.clone(), - signature, + vec![0x01], + settlement_script.clone(), + [0x00, 0x00].to_vec(), // unlock with local_htlc_key1 and no preimage + signature.clone(), ] .concat(); - let tx = tx.as_advanced_builder().witness(witness.pack()).build(); - println!("tx: {:?}", tx); - - // run + let success_tx = tx.as_advanced_builder().witness(witness.pack()).build(); let cycles = context - .verify_tx(&tx, MAX_CYCLES) + .verify_tx(&success_tx, MAX_CYCLES) .expect("pass verification"); println!("consume cycles: {}", cycles); - // build transaction with remote_htlc_pubkey unlock received pending htlc 2 + // build transaction with remote_htlc_pubkey2 unlock received pending htlc 2 let since = Since::from_timestamp(1712062800 + 1000, true).unwrap(); let input = CellInput::new_builder() .since(since.as_u64().pack()) @@ -1152,11 +1184,12 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { expiry1.as_u64().to_le_bytes().to_vec(), ] .concat(); + let new_settlement_script = [new_pending_htlcs.clone(), two_party_settlement.clone()].concat(); let new_args = [ &pubkey_hash[0..20], delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), - &blake2b_256(&new_pending_htlcs)[0..20], + &blake2b_256(&new_settlement_script)[0..20], ] .concat(); let new_lock_script = lock_script.as_builder().args(new_args.pack()).build(); @@ -1176,11 +1209,11 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) .inputs(inputs) - .outputs(outputs) + .outputs(outputs.clone()) .outputs_data(outputs_data.pack()) .build(); - // sign with remote_htlc_pubkey + // sign with remote_htlc_key2 let message: [u8; 32] = compute_tx_message(&tx); let signature = remote_htlc_key2 @@ -1191,21 +1224,19 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { let witness = [ EMPTY_WITNESS_ARGS.to_vec(), vec![0x01], - pending_htlcs.clone(), - signature, + settlement_script.clone(), + [0x01, 0x00].to_vec(), // unlock with remote_htlc_key2 and no preimage + signature.clone(), ] .concat(); - let tx = tx.as_advanced_builder().witness(witness.pack()).build(); - println!("tx: {:?}", tx); - - // run + let success_tx = tx.as_advanced_builder().witness(witness.pack()).build(); let cycles = context - .verify_tx(&tx, MAX_CYCLES) + .verify_tx(&success_tx, MAX_CYCLES) .expect("pass verification"); println!("consume cycles: {}", cycles); - // // build transaction with local_htlc_pubkey unlock received pending htlc 2 + // build transaction with local_htlc_pubkey2 unlock received pending htlc 2 let input = CellInput::new_builder() .previous_output(input_out_point.clone()) .build(); @@ -1217,19 +1248,7 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { .since(half_delay_epoch.as_u64().pack()) .build(), ]; - let outputs = vec![ - CellOutput::new_builder() - .capacity((1000 * BYTE_SHANNONS).pack()) - .lock(new_lock_script.clone()) - .type_(Some(type_script.clone()).pack()) - .build(), - ]; - let outputs_data: Vec = vec![ - (total_sudt_amount - payment_amount2) - .to_le_bytes() - .to_vec() - .into(), - ]; + let tx = TransactionBuilder::default() .cell_deps(cell_deps) .inputs(inputs) @@ -1237,7 +1256,7 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { .outputs_data(outputs_data.pack()) .build(); - // sign with local_htlc_pubkey + // sign with local_htlc_key2 let message: [u8; 32] = compute_tx_message(&tx); let signature = local_htlc_key2 @@ -1248,18 +1267,49 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { let witness = [ EMPTY_WITNESS_ARGS.to_vec(), vec![0x01], - pending_htlcs, - signature, + settlement_script.clone(), + [0x01, 0x01].to_vec(), // unlock with local_htlc_key2 and preimage + signature.clone(), preimage2.to_vec(), ] .concat(); - let tx = tx.as_advanced_builder().witness(witness.pack()).build(); - println!("tx: {:?}", tx); - - // run + let success_tx = tx.as_advanced_builder().witness(witness.pack()).build(); let cycles = context - .verify_tx(&tx, MAX_CYCLES) + .verify_tx(&success_tx, MAX_CYCLES) .expect("pass verification"); println!("consume cycles: {}", cycles); + + // sign with local_htlc_key2 and wrong preimage should fail + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + vec![0x01], + settlement_script.clone(), + [0x01, 0x01].to_vec(), // unlock with local_htlc_key2 and preimage + signature.clone(), + preimage1.to_vec(), + ] + .concat(); + + let fail_tx = tx.as_advanced_builder().witness(witness.pack()).build(); + let err = context + .verify_tx(&fail_tx, MAX_CYCLES) + .expect_err("wrong preimage should fail"); + assert!(err.to_string().contains("#21")); // PreimageError + + // sign with local_htlc_key2 and empty preimage should fail + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + vec![0x01], + settlement_script.clone(), + [0x01, 0x00].to_vec(), // unlock with local_htlc_key2 and no preimage + signature, + ] + .concat(); + + let fail_tx = tx.as_advanced_builder().witness(witness.pack()).build(); + let err = context + .verify_tx(&fail_tx, MAX_CYCLES) + .expect_err("empty preimage should fail"); + assert!(err.to_string().contains("#21")); // PreimageError } From f856d7b04beb9b1e2225e26229a73f3f8d9ccce6 Mon Sep 17 00:00:00 2001 From: quake Date: Tue, 23 Sep 2025 16:33:35 +0900 Subject: [PATCH 10/27] make clippy happy --- contracts/commitment-lock/src/main.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index d4d6818..6aaa3ab 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -135,7 +135,7 @@ impl<'a> Htlc<'a> { struct Settlement<'a>(&'a [u8]); -impl<'a> Settlement<'a> { +impl Settlement<'_> { fn unlock_type(&self) -> u8 { self.0[0] } @@ -267,7 +267,7 @@ fn auth() -> Result<(), Error> { } } - if settlements.len() == 0 { + if settlements.is_empty() { return Err(Error::InvalidSettlementCount); } @@ -299,7 +299,7 @@ fn auth() -> Result<(), Error> { .chunks(HTLC_SCRIPT_LEN) .enumerate() { - if settlements.len() > 0 && settlements[0].unlock_type() == i as u8 { + if !settlements.is_empty() && settlements[0].unlock_type() == i as u8 { let htlc = Htlc(htlc_script); match htlc.htlc_type() { HtlcType::Offered => { @@ -436,7 +436,7 @@ fn auth() -> Result<(), Error> { return Err(Error::InvalidUnlockType); } } - } else if settlements.len() == 0 { + } else if settlements.is_empty() { new_settlement_script.push(&witness[pending_htlcs_len..pending_htlcs_len + 72]); } else { return Err(Error::InvalidSettlementCount); @@ -493,9 +493,9 @@ fn auth() -> Result<(), Error> { // AuthAlgorithmIdCkb = 0 for (signature, pubkey_hash) in signatures_to_verify { let algorithm_id_str = CString::new(encode([0u8])).unwrap(); - let signature_str = CString::new(encode(&signature)).unwrap(); + let signature_str = CString::new(encode(signature)).unwrap(); let message_str = CString::new(encode(message)).unwrap(); - let pubkey_hash_str = CString::new(encode(&pubkey_hash)).unwrap(); + let pubkey_hash_str = CString::new(encode(pubkey_hash)).unwrap(); let args = [ algorithm_id_str.as_c_str(), From 2b5c1d094d56184c904fb0e0fc8df6b4b3d459b0 Mon Sep 17 00:00:00 2001 From: quake Date: Tue, 23 Sep 2025 17:04:41 +0900 Subject: [PATCH 11/27] wip --- tests/src/tests.rs | 157 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 155 insertions(+), 2 deletions(-) diff --git a/tests/src/tests.rs b/tests/src/tests.rs index 8638762..7ccaf23 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -762,7 +762,11 @@ fn test_commitment_lock_with_two_pending_htlcs() { &blake2b_256(new_settlement_script)[0..20], ] .concat(); - let new_lock_script = lock_script.as_builder().args(new_args.pack()).build(); + let new_lock_script = lock_script + .clone() + .as_builder() + .args(new_args.pack()) + .build(); let outputs = vec![ CellOutput::new_builder() .capacity(((local_amount + remote_amount + payment_amount1) as u64).pack()) @@ -823,7 +827,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { ]; let outputs_data = [Bytes::new()]; let tx = TransactionBuilder::default() - .cell_deps(cell_deps) + .cell_deps(cell_deps.clone()) .inputs(inputs) .outputs(outputs) .outputs_data(outputs_data.pack()) @@ -887,6 +891,155 @@ fn test_commitment_lock_with_two_pending_htlcs() { .expect_err("empty preimage should fail"); println!("error: {}", error); assert!(error.to_string().contains("#21")); // PreimageError + + // test with settlement unlock logic (local settlement key) + let new_two_party_settlement = [ + blake2b_256(local_settlement_key.1.serialize())[0..20].to_vec(), + 0u128.to_le_bytes().to_vec(), + blake2b_256(remote_settlement_key.1.serialize())[0..20].to_vec(), + remote_amount.to_le_bytes().to_vec(), + ] + .concat(); + + let new_settlement_script = [pending_htlcs.clone(), new_two_party_settlement.clone()].concat(); + let new_args = [ + &pubkey_hash[0..20], + delay_epoch.as_u64().to_le_bytes().as_slice(), + commitment_tx_version.to_be_bytes().as_slice(), + &blake2b_256(&new_settlement_script)[0..20], + ] + .concat(); + + let new_lock_script = lock_script + .clone() + .as_builder() + .args(new_args.pack()) + .build(); + let outputs = vec![ + CellOutput::new_builder() + .capacity(((remote_amount + payment_amount1 + payment_amount2) as u64).pack()) + .lock(new_lock_script.clone()) + .build(), + ]; + + let outputs_data = [Bytes::new()]; + let input = CellInput::new_builder() + .previous_output(input_out_point.clone()) + .build(); + + let tx = TransactionBuilder::default() + .cell_deps(cell_deps.clone()) + .input(input) + .outputs(outputs.clone()) + .outputs_data(outputs_data.pack()) + .build(); + + // sign with local_settlement_key + let message: [u8; 32] = compute_tx_message(&tx); + + let signature = local_settlement_key + .0 + .sign_recoverable(&message.into()) + .unwrap() + .serialize(); + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + vec![0x01], + settlement_script.clone(), + vec![0xFD, 0x00], // unlock with local settlement key, + signature.clone(), + ] + .concat(); + + let success_tx = tx.as_advanced_builder().witness(witness.pack()).build(); + let cycles = context + .verify_tx(&success_tx, MAX_CYCLES) + .expect("pass verification"); + println!("consume cycles: {}", cycles); + + // test with batch unlock logic (local settlement key + local_htlc_key1) + let new_pending_htlcs = [ + [1].to_vec(), + [0b00000011].to_vec(), + payment_amount2.to_le_bytes().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(), + ] + .concat(); + + let new_settlement_script = [new_pending_htlcs.clone(), new_two_party_settlement].concat(); + let new_args = [ + &pubkey_hash[0..20], + delay_epoch.as_u64().to_le_bytes().as_slice(), + commitment_tx_version.to_be_bytes().as_slice(), + &blake2b_256(&new_settlement_script)[0..20], + ] + .concat(); + + let new_lock_script = lock_script + .clone() + .as_builder() + .args(new_args.pack()) + .build(); + + let outputs = vec![ + CellOutput::new_builder() + .capacity(((remote_amount + payment_amount2) as u64).pack()) + .lock(new_lock_script.clone()) + .build(), + ]; + let outputs_data = [Bytes::new()]; + let input = CellInput::new_builder() + .previous_output(input_out_point.clone()) + .build(); + let inputs = vec![ + input, + delay_epoch_input + .clone() + .as_builder() + .since(half_delay_epoch.as_u64().pack()) + .build(), + ]; + let tx = TransactionBuilder::default() + .cell_deps(cell_deps.clone()) + .inputs(inputs) + .outputs(outputs.clone()) + .outputs_data(outputs_data.pack()) + .build(); + // sign with local_settlement_key and local_htlc_key1 + let message: [u8; 32] = compute_tx_message(&tx); + + let signature1 = local_htlc_key1 + .0 + .sign_recoverable(&message.into()) + .unwrap() + .serialize(); + + let signature2 = local_settlement_key + .0 + .sign_recoverable(&message.into()) + .unwrap() + .serialize(); + + let witness = [ + EMPTY_WITNESS_ARGS.to_vec(), + vec![0x02], + settlement_script.clone(), + [0x00, 0x01].to_vec(), // unlock with local_htlc_key1 and preimage + signature1.clone(), + preimage1.to_vec(), + [0xFD, 0x00].to_vec(), // unlock with local settlement + signature2.clone(), + ] + .concat(); + + let success_tx = tx.as_advanced_builder().witness(witness.pack()).build(); + let cycles = context + .verify_tx(&success_tx, MAX_CYCLES) + .expect("pass verification"); + println!("consume cycles: {}", cycles); } #[test] From d568829991968baee0bf0fba35c6f5ac5c5149cd Mon Sep 17 00:00:00 2001 From: quake Date: Tue, 23 Sep 2025 19:32:20 +0900 Subject: [PATCH 12/27] test: add batch unlock test --- tests/src/tests.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/tests/src/tests.rs b/tests/src/tests.rs index 7ccaf23..bab411c 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -957,7 +957,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { .expect("pass verification"); println!("consume cycles: {}", cycles); - // test with batch unlock logic (local settlement key + local_htlc_key1) + // test with batch unlock logic (remote settlement key + remote htlc key1) let new_pending_htlcs = [ [1].to_vec(), [0b00000011].to_vec(), @@ -969,6 +969,14 @@ fn test_commitment_lock_with_two_pending_htlcs() { ] .concat(); + let new_two_party_settlement = [ + blake2b_256(local_settlement_key.1.serialize())[0..20].to_vec(), + local_amount.to_le_bytes().to_vec(), + blake2b_256(remote_settlement_key.1.serialize())[0..20].to_vec(), + 0u128.to_le_bytes().to_vec(), + ] + .concat(); + let new_settlement_script = [new_pending_htlcs.clone(), new_two_party_settlement].concat(); let new_args = [ &pubkey_hash[0..20], @@ -986,7 +994,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { let outputs = vec![ CellOutput::new_builder() - .capacity(((remote_amount + payment_amount2) as u64).pack()) + .capacity(((local_amount + payment_amount2) as u64).pack()) .lock(new_lock_script.clone()) .build(), ]; @@ -1008,16 +1016,16 @@ fn test_commitment_lock_with_two_pending_htlcs() { .outputs(outputs.clone()) .outputs_data(outputs_data.pack()) .build(); - // sign with local_settlement_key and local_htlc_key1 + // sign with remote_settlement_key and remote_htlc_key1 let message: [u8; 32] = compute_tx_message(&tx); - let signature1 = local_htlc_key1 + let signature1 = remote_htlc_key1 .0 .sign_recoverable(&message.into()) .unwrap() .serialize(); - let signature2 = local_settlement_key + let signature2 = remote_settlement_key .0 .sign_recoverable(&message.into()) .unwrap() @@ -1027,10 +1035,10 @@ fn test_commitment_lock_with_two_pending_htlcs() { EMPTY_WITNESS_ARGS.to_vec(), vec![0x02], settlement_script.clone(), - [0x00, 0x01].to_vec(), // unlock with local_htlc_key1 and preimage + [0x00, 0x01].to_vec(), // unlock with remote_htlc_key1 and preimage signature1.clone(), preimage1.to_vec(), - [0xFD, 0x00].to_vec(), // unlock with local settlement + [0xFE, 0x00].to_vec(), // unlock with remote settlement signature2.clone(), ] .concat(); From e46a0b7dd6020d0ecd6efaffd305b336194347a8 Mon Sep 17 00:00:00 2001 From: quake Date: Tue, 23 Sep 2025 19:51:37 +0900 Subject: [PATCH 13/27] fix: settlement should check delay_epoch --- contracts/commitment-lock/src/main.rs | 8 ++++++ tests/src/tests.rs | 41 ++++++++++++++++++++++++--- 2 files changed, 45 insertions(+), 4 deletions(-) diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index 6aaa3ab..a1d244b 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -398,6 +398,10 @@ fn auth() -> Result<(), Error> { let settlement = settlements.remove(0); match settlement.unlock_type() { 0xFD => { + // local settlement should wait for delay_epoch + if !check_input_since(delay_epoch) { + return Err(Error::InvalidSince); + } let settlement_one_pubkey_hash: [u8; 20] = witness [pending_htlcs_len..pending_htlcs_len + 20] .try_into() @@ -416,6 +420,10 @@ fn auth() -> Result<(), Error> { signatures_to_verify.push((settlement.signature(), settlement_one_pubkey_hash)); } 0xFE => { + // remote settlement should wait for delay_epoch + if !check_input_since(delay_epoch) { + return Err(Error::InvalidSince); + } let settlement_two_pubkey_hash: [u8; 20] = witness [pending_htlcs_len + 36..pending_htlcs_len + 56] .try_into() diff --git a/tests/src/tests.rs b/tests/src/tests.rs index bab411c..9b8288b 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -204,6 +204,7 @@ fn test_commitment_lock_no_pending_htlcs() { let auth_bin = loader.load_binary("../../deps/auth"); let commitment_lock_out_point = context.deploy_cell(commitment_lock_bin); let auth_out_point = context.deploy_cell(auth_bin); + let always_success_out_point = context.deploy_cell(ALWAYS_SUCCESS.clone()); // prepare script let (sec_key_1, sec_key_2, key_agg_ctx) = generate_multisig_keys(); @@ -239,13 +240,19 @@ fn test_commitment_lock_no_pending_htlcs() { let lock_script = context .build_script(&commitment_lock_out_point, args.clone().into()) .expect("script"); + let always_success_script = context + .build_script(&always_success_out_point, Bytes::new()) + .expect("script"); // prepare cell deps let commitment_lock_dep = CellDep::new_builder() .out_point(commitment_lock_out_point) .build(); let auth_dep = CellDep::new_builder().out_point(auth_out_point).build(); - let cell_deps = vec![commitment_lock_dep, auth_dep].pack(); + let always_success_dep = CellDep::new_builder() + .out_point(always_success_out_point) + .build(); + let cell_deps = vec![commitment_lock_dep, auth_dep, always_success_dep].pack(); // prepare cells let input_out_point = context.create_cell( @@ -255,6 +262,12 @@ fn test_commitment_lock_no_pending_htlcs() { .build(), Bytes::new(), ); + let delay_input_out_point = context.create_cell( + CellOutput::new_builder() + .lock(always_success_script) + .build(), + Bytes::new(), + ); // build transaction with revocation unlock logic let to_revocation_lock = Script::new_builder() @@ -345,10 +358,22 @@ fn test_commitment_lock_no_pending_htlcs() { let input = CellInput::new_builder() .previous_output(input_out_point.clone()) .build(); + let delay_epoch_input = CellInput::new_builder() + .previous_output(delay_input_out_point.clone()) + .since(delay_epoch.as_u64().pack()) + .build(); + let inputs = vec![ + input, + delay_epoch_input + .clone() + .as_builder() + .since(delay_epoch.as_u64().pack()) + .build(), + ]; let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) - .input(input) + .inputs(inputs) .outputs(outputs.clone()) .outputs_data(outputs_data.pack()) .build(); @@ -926,10 +951,18 @@ fn test_commitment_lock_with_two_pending_htlcs() { let input = CellInput::new_builder() .previous_output(input_out_point.clone()) .build(); + let inputs = vec![ + input, + delay_epoch_input + .clone() + .as_builder() + .since(delay_epoch.as_u64().pack()) + .build(), + ]; let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) - .input(input) + .inputs(inputs) .outputs(outputs.clone()) .outputs_data(outputs_data.pack()) .build(); @@ -1007,7 +1040,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { delay_epoch_input .clone() .as_builder() - .since(half_delay_epoch.as_u64().pack()) + .since(delay_epoch.as_u64().pack()) .build(), ]; let tx = TransactionBuilder::default() From 591b6f4a07904947df147677250833ba50d48021 Mon Sep 17 00:00:00 2001 From: quake Date: Tue, 23 Sep 2025 19:57:42 +0900 Subject: [PATCH 14/27] chore: update README --- contracts/commitment-lock/README.md | 62 +++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/contracts/commitment-lock/README.md b/contracts/commitment-lock/README.md index 1453505..afa030e 100644 --- a/contracts/commitment-lock/README.md +++ b/contracts/commitment-lock/README.md @@ -2,6 +2,7 @@ This is a commitment lock script for ckb fiber network, which implements [daric] protocol. +## Lock Script Args and Witness Structure The lock script args is concatenated by the following fields: - `pubkey_hash`: 20 bytes, hash result of blake160(x only aggregated public key) @@ -38,6 +39,67 @@ For settlement unlock process, the transaction must provide the following fields - `signature`: 65 bytes, the signature of the xxx_pubkey - `preimage`: 32 bytes, an optional field to provide the preimage of the payment_hash +## Settlement Unlock Process and New Lock Script Args Generation + +During the settlement unlock process, when HTLCs are settled or parties claim their funds, a new output cell with updated lock script args is generated. The new lock script args follow the same structure but with updated `settlement_hash`: + +### New Settlement Script Generation + +The new settlement script is constructed by: + +1. **Updated pending HTLCs**: Remove settled HTLCs from the original list + - `new_pending_htlc_count`: Decremented count after settling HTLCs + - Remaining unsettled HTLCs in the same 85-byte format + +2. **Updated settlement amounts**: Adjust party amounts based on settlements + - For local settlement (unlock_type = 0xFD): Set settlement_one_amount to 0 + - For remote settlement (unlock_type = 0xFE): Set settlement_two_amount to 0 + - For HTLC settlements: Deduct payment amounts from total available funds + +### New Lock Script Args Construction + +The new lock script args are generated as: +``` +new_args = [ + pubkey_hash, // Same as original (20 bytes) + delay_epoch, // Same as original (8 bytes) + version, // Same as original (8 bytes) + new_settlement_hash // Updated hash (20 bytes) +] +``` + +Where `new_settlement_hash = blake2b_256(new_settlement_script)[0..20]` + +### Examples from Tests + +1. **Local Settlement**: When local party settles, their settlement amount becomes 0: + ```rust + new_settlement_script = [ + new_pending_htlc_count, + remaining_htlcs..., + local_pubkey_hash, + 0u128.to_le_bytes(), // Local amount set to 0 + remote_pubkey_hash, + remaining_remote_amount.to_le_bytes() + ] + ``` + +2. **HTLC Settlement**: When HTLCs are settled, they're removed from pending list: + ```rust + new_settlement_script = [ + (original_count - settled_count), + unsettled_htlcs..., + settlement_party_data... + ] + ``` + +3. **Batch Settlement**: Multiple HTLCs and party settlements can be processed together, with all changes reflected in the new settlement script. + +The verification logic ensures that: +- The new lock script uses the same code_hash and hash_type +- The new args match the expected format with updated settlement_hash +- Output capacity/UDT amount reflects the settled amounts correctly + To know more about the transaction building process, please refer to the `test_commitment_lock_*` unit test. *This contract was bootstrapped with [ckb-script-templates].* From 4080d2231d0a3030b5d8adec2f6312fda6e7c98b Mon Sep 17 00:00:00 2001 From: quake Date: Wed, 24 Sep 2025 08:17:28 +0900 Subject: [PATCH 15/27] fix: ci --- rust-toolchain.toml | 2 +- scripts/reproducible_build_docker | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/rust-toolchain.toml b/rust-toolchain.toml index c1bc0a6..00822fd 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.85.0" +channel = "1.85.1" diff --git a/scripts/reproducible_build_docker b/scripts/reproducible_build_docker index e7471cb..6f802bd 100755 --- a/scripts/reproducible_build_docker +++ b/scripts/reproducible_build_docker @@ -7,8 +7,8 @@ set -ex DOCKER="${DOCKER:-docker}" -# docker pull docker.io/cryptape/llvm-n-rust:20250117 -DOCKER_IMAGE="${DOCKER_IMAGE:-docker.io/cryptape/llvm-n-rust@sha256:12e7821cb9c7cbc8988d5b1d60bcc87da4cedcf3eea32df1d8833328c5a69f88}" +# docker pull docker.io/cryptape/llvm-n-rust:20250617 +DOCKER_IMAGE="${DOCKER_IMAGE:-docker.io/cryptape/llvm-n-rust@sha256:d6d1f9a6656039273210de91913c828f5b4aa4a3282d2c93ed19bcb7bbf728fe}" CHECKSUM_FILE_PATH="${CHECKSUM_FILE_PATH:-checksums.txt}" # We are parsing command line arguments based on tips from: From e8e2bc9da18204a02e2ce085653dcf2074418859 Mon Sep 17 00:00:00 2001 From: quake Date: Wed, 24 Sep 2025 11:45:02 +0900 Subject: [PATCH 16/27] chore: use spawn --- contracts/commitment-lock/src/main.rs | 13 ++++++++----- deps/README.md | 6 +++--- deps/auth | Bin 150904 -> 150904 bytes 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index a1d244b..911e35a 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -19,10 +19,9 @@ use ckb_std::{ debug, error::SysError, high_level::{ - QueryIter, exec_cell, load_cell, load_cell_capacity, load_cell_data, load_cell_lock, - load_cell_type, load_input_since, load_script, load_transaction, load_witness, + exec_cell, load_cell, load_cell_capacity, load_cell_data, load_cell_lock, load_cell_type, load_input_since, load_script, load_transaction, load_witness, spawn_cell, QueryIter }, - since::{EpochNumberWithFraction, LockValue, Since}, + since::{EpochNumberWithFraction, LockValue, Since}, syscalls::wait, }; use hex::encode; use sha2::{Digest, Sha256}; @@ -512,9 +511,13 @@ fn auth() -> Result<(), Error> { pubkey_hash_str.as_c_str(), ]; - // TODO use spawn_cell - exec_cell(&AUTH_CODE_HASH, ScriptHashType::Data1, &args) + let pid = spawn_cell(&AUTH_CODE_HASH, ScriptHashType::Data1, &args, &[]) .map_err(|_| Error::AuthError)?; + let result = wait(pid).map_err(|_| Error::AuthError)?; + debug!("auth result: {}", result); + if result != 0 { + return Err(Error::AuthError); + } } Ok(()) } diff --git a/deps/README.md b/deps/README.md index f3616b9..11500b2 100644 --- a/deps/README.md +++ b/deps/README.md @@ -4,10 +4,10 @@ You may rebuild it by running following commands: ```bash git clone https://github.com/nervosnetwork/ckb-auth.git cd ckb-auth -git checkout 68c93a3a07a462eb4e42a293fe94d759ed1b21ee +git checkout b95f62ce597a5b8678196e2733c0d4dbd07f67f4 git submodule update --init make all-via-docker -cp build/auth ckb-pcn-scripts/deps +cp build/auth fiber-scripts/deps ``` `simple_udt` is a binary built from the `ckb-production-scripts` project. It's used in unit tests only for udt related functions. @@ -18,5 +18,5 @@ git clone https://github.com/nervosnetwork/ckb-production-scripts.git cd ckb-production-scripts git submodule update --init --recursive make all-via-docker -cp build/simple_udt ckb-pcn-scripts/deps +cp build/simple_udt fiber-scripts/deps ``` diff --git a/deps/auth b/deps/auth index e59d00fba3cceb39bdcc75366a29302123c8d2c1..4c4b885b8a07cd5c8e73fe56ee84181d71497837 100755 GIT binary patch delta 15323 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 MyI8m6WjqD{A2dp#O#lD@ delta 15152 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 tMVMRxlp-h_p=9ZIsc^fiep4vUvq2HW Date: Tue, 30 Sep 2025 16:06:54 +0900 Subject: [PATCH 17/27] wip --- contracts/commitment-lock/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/contracts/commitment-lock/README.md b/contracts/commitment-lock/README.md index afa030e..d7d8458 100644 --- a/contracts/commitment-lock/README.md +++ b/contracts/commitment-lock/README.md @@ -8,7 +8,7 @@ The lock script args is concatenated by the following fields: - `pubkey_hash`: 20 bytes, hash result of blake160(x only aggregated public key) - `delay_epoch`: 8 bytes, u64 in little endian, must be a relative [EpochNumberWithFraction](https://github.com/nervosnetwork/ckb/blob/develop/rpc/README.md#type-epochnumberwithfraction) - `version`: 8 bytes, u64 in big-endian -- `settlement_hash`: 20 bytes, hash result of blake160(pending_htlc_count || N * pending_htlc || settlement_one_pubkey_hash || settlement_one_amount || settlement_two_pubkey_hash || settlement_two_amount) +- `settlement_hash`: 20 bytes, hash result of blake160(pending_htlc_count || N * pending_htlc || settlement_remote_pubkey_hash || settlement_remote_amount || settlement_local_pubkey_hash || settlement_local_amount) To unlock this lock, the transaction must provide the following fields in the witness: - `empty_witness_args`: 16 bytes, fixed to 0x10000000100000001000000010000000, for compatibility with the xudt @@ -28,13 +28,13 @@ For settlement unlock process, the transaction must provide the following fields - `remote_htlc_pubkey_hash`: 20 bytes, hash result of blake160(remote_htlc_pubkey) - `local_htlc_pubkey_hash`: 20 bytes, hash result of blake160(local_htlc_pubkey) - `htlc_expiry`: 8 bytes, u64 in little endian, must be an absolute timestamp [since](https://github.com/nervosnetwork/rfcs/blob/master/rfcs/0017-tx-valid-since/0017-tx-valid-since.md) -- `settlement_one_pubkey_hash`: 20 bytes, hash result of blake160(pubkey) -- `settlement_one_amount`: 16 bytes, u128 in little endian -- `settlement_two_pubkey_hash`: 20 bytes, hash result of blake160(pubkey) -- `settlement_two_amount`: 16 bytes, u128 in little endian +- `settlement_remote_pubkey_hash`: 20 bytes, hash result of blake160(pubkey) +- `settlement_remote_amount`: 16 bytes, u128 in little endian +- `settlement_local_pubkey_hash`: 20 bytes, hash result of blake160(pubkey) +- `settlement_local_amount`: 16 bytes, u128 in little endian - `unlocks`: A group of settlement unlock signature and preimage - - `unlock_type`: 0x00 ~ 0xFC for pending htlc group index, 0xFD for settlement one, 0xFE for settlement two. + - `unlock_type`: 0x00 ~ 0xFC for pending htlc group index, 0xFD for settlement local, 0xFE for settlement remote. - `with_preimage`: 0x00 without preimage, 0x01 with preimage - `signature`: 65 bytes, the signature of the xxx_pubkey - `preimage`: 32 bytes, an optional field to provide the preimage of the payment_hash @@ -52,8 +52,8 @@ The new settlement script is constructed by: - Remaining unsettled HTLCs in the same 85-byte format 2. **Updated settlement amounts**: Adjust party amounts based on settlements - - For local settlement (unlock_type = 0xFD): Set settlement_one_amount to 0 - - For remote settlement (unlock_type = 0xFE): Set settlement_two_amount to 0 + - For local settlement (unlock_type = 0xFD): Set settlement_remote_amount to 0 + - For remote settlement (unlock_type = 0xFE): Set settlement_local_amount to 0 - For HTLC settlements: Deduct payment amounts from total available funds ### New Lock Script Args Construction From c65b274fa1246ca7be422d443c430607cb335d75 Mon Sep 17 00:00:00 2001 From: quake Date: Tue, 30 Sep 2025 16:19:55 +0900 Subject: [PATCH 18/27] wip: todo add more test --- contracts/commitment-lock/src/main.rs | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index 911e35a..5310bff 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -19,9 +19,11 @@ use ckb_std::{ debug, error::SysError, high_level::{ - exec_cell, load_cell, load_cell_capacity, load_cell_data, load_cell_lock, load_cell_type, load_input_since, load_script, load_transaction, load_witness, spawn_cell, QueryIter + QueryIter, exec_cell, load_cell, load_cell_capacity, load_cell_data, load_cell_lock, + load_cell_type, load_input_since, load_script, load_transaction, load_witness, spawn_cell, }, - since::{EpochNumberWithFraction, LockValue, Since}, syscalls::wait, + since::{EpochNumberWithFraction, LockValue, Since}, + syscalls::wait, }; use hex::encode; use sha2::{Digest, Sha256}; @@ -392,6 +394,7 @@ fn auth() -> Result<(), Error> { } // settlement for one or two parties + let mut two_parties_all_settled = false; let zero = [0u8; 16]; if settlements.len() == 1 { let settlement = settlements.remove(0); @@ -410,6 +413,11 @@ fn auth() -> Result<(), Error> { .try_into() .unwrap(), ); + let settlement_two_amount = u128::from_le_bytes( + witness[pending_htlcs_len + 56..pending_htlcs_len + 72] + .try_into() + .unwrap(), + ); new_amount -= settlement_one_amount; new_settlement_script.push(&witness[pending_htlcs_len..pending_htlcs_len + 20]); @@ -417,6 +425,8 @@ fn auth() -> Result<(), Error> { new_settlement_script .push(&witness[pending_htlcs_len + 36..pending_htlcs_len + 72]); signatures_to_verify.push((settlement.signature(), settlement_one_pubkey_hash)); + + two_parties_all_settled = settlement_two_amount == 0; } 0xFE => { // remote settlement should wait for delay_epoch @@ -432,11 +442,18 @@ fn auth() -> Result<(), Error> { .try_into() .unwrap(), ); + let settlement_one_amount = u128::from_le_bytes( + witness[pending_htlcs_len + 20..pending_htlcs_len + 36] + .try_into() + .unwrap(), + ); new_amount -= settlement_two_amount; new_settlement_script.push(&witness[pending_htlcs_len..pending_htlcs_len + 56]); new_settlement_script.push(zero.as_slice()); signatures_to_verify.push((settlement.signature(), settlement_two_pubkey_hash)); + + two_parties_all_settled = settlement_one_amount == 0; } unlock => { debug!("Invalid unlock type 2: {}", unlock); @@ -453,7 +470,7 @@ fn auth() -> Result<(), Error> { debug!("new_amount: {}", new_amount); // verify the first output cell's lock script and capacity are correct - if new_amount > 0 { + if new_amount > 0 && !two_parties_all_settled { let output_lock = load_cell_lock(0, Source::Output)?; let expected_lock_args = [ &args[0..36], From d026ded462dd4e21d8f6e0f75a8777e3793f980e Mon Sep 17 00:00:00 2001 From: quake Date: Tue, 30 Sep 2025 19:52:19 +0900 Subject: [PATCH 19/27] chore: tweak two party settlement script --- contracts/commitment-lock/README.md | 14 +++---- contracts/commitment-lock/src/main.rs | 56 ++++++++++++--------------- tests/src/tests.rs | 43 ++++++++++---------- 3 files changed, 51 insertions(+), 62 deletions(-) diff --git a/contracts/commitment-lock/README.md b/contracts/commitment-lock/README.md index d7d8458..c7a54d2 100644 --- a/contracts/commitment-lock/README.md +++ b/contracts/commitment-lock/README.md @@ -34,7 +34,7 @@ For settlement unlock process, the transaction must provide the following fields - `settlement_local_amount`: 16 bytes, u128 in little endian - `unlocks`: A group of settlement unlock signature and preimage - - `unlock_type`: 0x00 ~ 0xFC for pending htlc group index, 0xFD for settlement local, 0xFE for settlement remote. + - `unlock_type`: 0x00 ~ 0xFD for pending htlc group index, 0xFE for settlement remote, 0xFF for settlement local. - `with_preimage`: 0x00 without preimage, 0x01 with preimage - `signature`: 65 bytes, the signature of the xxx_pubkey - `preimage`: 32 bytes, an optional field to provide the preimage of the payment_hash @@ -52,8 +52,8 @@ The new settlement script is constructed by: - Remaining unsettled HTLCs in the same 85-byte format 2. **Updated settlement amounts**: Adjust party amounts based on settlements - - For local settlement (unlock_type = 0xFD): Set settlement_remote_amount to 0 - - For remote settlement (unlock_type = 0xFE): Set settlement_local_amount to 0 + - For remote settlement (unlock_type = 0xFE): Set settlement_local_amount to 0 and pubkey hash to 20 bytes zeros + - For local settlement (unlock_type = 0xFF): Set settlement_remote_amount to 0 and pubkey hash to 20 bytes zeros - For HTLC settlements: Deduct payment amounts from total available funds ### New Lock Script Args Construction @@ -72,15 +72,15 @@ Where `new_settlement_hash = blake2b_256(new_settlement_script)[0..20]` ### Examples from Tests -1. **Local Settlement**: When local party settles, their settlement amount becomes 0: +1. **Local Settlement**: When local party settles, their settlement amount becomes 0 and pubkey hash is updated to 20 bytes zeros: ```rust new_settlement_script = [ new_pending_htlc_count, remaining_htlcs..., - local_pubkey_hash, - 0u128.to_le_bytes(), // Local amount set to 0 remote_pubkey_hash, - remaining_remote_amount.to_le_bytes() + remaining_remote_amount.to_le_bytes(), + [0u8; 20], // Local pubkey hash set to 20 bytes zeros + 0u128.to_le_bytes(), // Local amount set to 0 ] ``` diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index 5310bff..83401fe 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -230,7 +230,7 @@ fn auth() -> Result<(), Error> { debug!("witness: {:x?}", witness); // 1 (pending_htlc_count) + pending_htlc_count * HTLC_SCRIPT_LEN let pending_htlcs_len = 1 + pending_htlc_count * HTLC_SCRIPT_LEN; - // settlement_one_pubkey_hash + settlement_one_amount + settlement_two_pubkey_hash + settlement_two_amount + // settlement_remote_pubkey_hash + settlement_remote_amount + settlement_local_pubkey_hash + settlement_local_amount let settlement_script_len = pending_htlcs_len + 72; if witness.len() < settlement_script_len { return Err(Error::WitnessLenError); @@ -246,7 +246,7 @@ fn auth() -> Result<(), Error> { while witness.len() > i { debug!("i: {}, len: {}", i, witness.len()); let unlock_type = witness[i]; - if unlock_type >= pending_htlc_count as u8 && unlock_type != 0xFD && unlock_type != 0xFE + if unlock_type >= pending_htlc_count as u8 && unlock_type != 0xFE && unlock_type != 0xFF { debug!("Invalid unlock type 1: {:?}", witness); return Err(Error::InvalidUnlockType); @@ -393,67 +393,59 @@ fn auth() -> Result<(), Error> { } } - // settlement for one or two parties + // settlement for local or remote parties let mut two_parties_all_settled = false; - let zero = [0u8; 16]; if settlements.len() == 1 { let settlement = settlements.remove(0); match settlement.unlock_type() { - 0xFD => { - // local settlement should wait for delay_epoch + 0xFE => { + // remote settlement should wait for delay_epoch if !check_input_since(delay_epoch) { return Err(Error::InvalidSince); } - let settlement_one_pubkey_hash: [u8; 20] = witness + let settlement_remote_pubkey_hash: [u8; 20] = witness [pending_htlcs_len..pending_htlcs_len + 20] .try_into() .unwrap(); - let settlement_one_amount = u128::from_le_bytes( + let settlement_remote_amount = u128::from_le_bytes( witness[pending_htlcs_len + 20..pending_htlcs_len + 36] .try_into() .unwrap(), ); - let settlement_two_amount = u128::from_le_bytes( - witness[pending_htlcs_len + 56..pending_htlcs_len + 72] - .try_into() - .unwrap(), - ); - new_amount -= settlement_one_amount; + new_amount -= settlement_remote_amount; - new_settlement_script.push(&witness[pending_htlcs_len..pending_htlcs_len + 20]); - new_settlement_script.push(zero.as_slice()); + new_settlement_script.push(&[0u8; 36]); new_settlement_script .push(&witness[pending_htlcs_len + 36..pending_htlcs_len + 72]); - signatures_to_verify.push((settlement.signature(), settlement_one_pubkey_hash)); + signatures_to_verify + .push((settlement.signature(), settlement_remote_pubkey_hash)); - two_parties_all_settled = settlement_two_amount == 0; + two_parties_all_settled = + witness[pending_htlcs_len + 36..pending_htlcs_len + 72] == [0u8; 36]; } - 0xFE => { - // remote settlement should wait for delay_epoch + 0xFF => { + // local settlement should wait for delay_epoch if !check_input_since(delay_epoch) { return Err(Error::InvalidSince); } - let settlement_two_pubkey_hash: [u8; 20] = witness + let settlement_local_pubkey_hash: [u8; 20] = witness [pending_htlcs_len + 36..pending_htlcs_len + 56] .try_into() .unwrap(); - let settlement_two_amount = u128::from_le_bytes( + let settlement_local_amount = u128::from_le_bytes( witness[pending_htlcs_len + 56..pending_htlcs_len + 72] .try_into() .unwrap(), ); - let settlement_one_amount = u128::from_le_bytes( - witness[pending_htlcs_len + 20..pending_htlcs_len + 36] - .try_into() - .unwrap(), - ); - new_amount -= settlement_two_amount; + new_amount -= settlement_local_amount; - new_settlement_script.push(&witness[pending_htlcs_len..pending_htlcs_len + 56]); - new_settlement_script.push(zero.as_slice()); - signatures_to_verify.push((settlement.signature(), settlement_two_pubkey_hash)); + new_settlement_script.push(&witness[pending_htlcs_len..pending_htlcs_len + 36]); + new_settlement_script.push(&[0u8; 36]); + signatures_to_verify + .push((settlement.signature(), settlement_local_pubkey_hash)); - two_parties_all_settled = settlement_one_amount == 0; + two_parties_all_settled = + witness[pending_htlcs_len..pending_htlcs_len + 36] == [0u8; 36]; } unlock => { debug!("Invalid unlock type 2: {}", unlock); diff --git a/tests/src/tests.rs b/tests/src/tests.rs index 9b8288b..21a9e73 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -215,17 +215,17 @@ fn test_commitment_lock_no_pending_htlcs() { let commitment_tx_version = 42u64; let mut generator = Generator::new(); - let local_settlement_key = generator.gen_keypair(); let remote_settlement_key = generator.gen_keypair(); - let local_amount = (600 * BYTE_SHANNONS) as u128; let remote_amount = (400 * BYTE_SHANNONS) as u128; + let local_settlement_key = generator.gen_keypair(); + let local_amount = (600 * BYTE_SHANNONS) as u128; let settlement_script = [ [0].to_vec(), - blake2b_256(local_settlement_key.1.serialize())[0..20].to_vec(), - local_amount.to_le_bytes().to_vec(), blake2b_256(remote_settlement_key.1.serialize())[0..20].to_vec(), remote_amount.to_le_bytes().to_vec(), + blake2b_256(local_settlement_key.1.serialize())[0..20].to_vec(), + local_amount.to_le_bytes().to_vec(), ] .concat(); @@ -327,10 +327,9 @@ fn test_commitment_lock_no_pending_htlcs() { // test with settlement unlock logic (local settlement key) let new_settlement_script = [ [0].to_vec(), - blake2b_256(local_settlement_key.1.serialize())[0..20].to_vec(), - 0u128.to_le_bytes().to_vec(), blake2b_256(remote_settlement_key.1.serialize())[0..20].to_vec(), remote_amount.to_le_bytes().to_vec(), + [0u8; 36].to_vec(), ] .concat(); @@ -390,7 +389,7 @@ fn test_commitment_lock_no_pending_htlcs() { EMPTY_WITNESS_ARGS.to_vec(), vec![0x01], settlement_script.clone(), - vec![0xFD, 0x00], // unlock with local settlement key, no preimage + vec![0xFF, 0x00], // unlock with local settlement key, no preimage signature.clone(), ] .concat(); @@ -469,10 +468,10 @@ fn test_commitment_lock_with_two_pending_htlcs() { let commitment_tx_version = 42u64; let mut generator = Generator::new(); - let local_settlement_key = generator.gen_keypair(); let remote_settlement_key = generator.gen_keypair(); - let local_amount = (600 * BYTE_SHANNONS) as u128; let remote_amount = (400 * BYTE_SHANNONS) as u128; + let local_settlement_key = generator.gen_keypair(); + let local_amount = (600 * BYTE_SHANNONS) as u128; let remote_htlc_key1 = generator.gen_keypair(); let remote_htlc_key2 = generator.gen_keypair(); @@ -505,10 +504,10 @@ fn test_commitment_lock_with_two_pending_htlcs() { .concat(); let two_party_settlement = [ - blake2b_256(local_settlement_key.1.serialize())[0..20].to_vec(), - local_amount.to_le_bytes().to_vec(), blake2b_256(remote_settlement_key.1.serialize())[0..20].to_vec(), remote_amount.to_le_bytes().to_vec(), + blake2b_256(local_settlement_key.1.serialize())[0..20].to_vec(), + local_amount.to_le_bytes().to_vec(), ] .concat(); @@ -917,12 +916,11 @@ fn test_commitment_lock_with_two_pending_htlcs() { println!("error: {}", error); assert!(error.to_string().contains("#21")); // PreimageError - // test with settlement unlock logic (local settlement key) + // test with settlement unlock logic (remote settlement key) let new_two_party_settlement = [ + [0u8; 36].to_vec(), blake2b_256(local_settlement_key.1.serialize())[0..20].to_vec(), - 0u128.to_le_bytes().to_vec(), - blake2b_256(remote_settlement_key.1.serialize())[0..20].to_vec(), - remote_amount.to_le_bytes().to_vec(), + local_amount.to_le_bytes().to_vec(), ] .concat(); @@ -942,7 +940,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { .build(); let outputs = vec![ CellOutput::new_builder() - .capacity(((remote_amount + payment_amount1 + payment_amount2) as u64).pack()) + .capacity(((local_amount + payment_amount1 + payment_amount2) as u64).pack()) .lock(new_lock_script.clone()) .build(), ]; @@ -967,10 +965,10 @@ fn test_commitment_lock_with_two_pending_htlcs() { .outputs_data(outputs_data.pack()) .build(); - // sign with local_settlement_key + // sign with remote_settlement_key let message: [u8; 32] = compute_tx_message(&tx); - let signature = local_settlement_key + let signature = remote_settlement_key .0 .sign_recoverable(&message.into()) .unwrap() @@ -979,7 +977,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { EMPTY_WITNESS_ARGS.to_vec(), vec![0x01], settlement_script.clone(), - vec![0xFD, 0x00], // unlock with local settlement key, + vec![0xFE, 0x00], // unlock with remote settlement key, signature.clone(), ] .concat(); @@ -1003,10 +1001,9 @@ fn test_commitment_lock_with_two_pending_htlcs() { .concat(); let new_two_party_settlement = [ + [0u8; 36].to_vec(), blake2b_256(local_settlement_key.1.serialize())[0..20].to_vec(), local_amount.to_le_bytes().to_vec(), - blake2b_256(remote_settlement_key.1.serialize())[0..20].to_vec(), - 0u128.to_le_bytes().to_vec(), ] .concat(); @@ -1106,10 +1103,10 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { let commitment_tx_version = 42u64; let mut generator = Generator::new(); - let local_settlement_key = generator.gen_keypair(); let remote_settlement_key = generator.gen_keypair(); - let local_amount = 11111111111111111111u128; let remote_amount = 22222222222222222222u128; + let local_settlement_key = generator.gen_keypair(); + let local_amount = 11111111111111111111u128; let remote_htlc_key1 = generator.gen_keypair(); let remote_htlc_key2 = generator.gen_keypair(); From 904a9acaa74a6b3e5639238fe3342606869853ae Mon Sep 17 00:00:00 2001 From: quake Date: Wed, 15 Oct 2025 13:39:02 +0900 Subject: [PATCH 20/27] fix overflow --- contracts/commitment-lock/src/main.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index 83401fe..f811bb7 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -321,7 +321,7 @@ fn auth() -> Result<(), Error> { } { return Err(Error::PreimageError); } - new_amount -= htlc.payment_amount(); + new_amount = new_amount.saturating_sub(htlc.payment_amount()); signatures_to_verify.push(( settlements.remove(0).signature(), htlc.remote_htlc_pubkey_hash(), @@ -335,7 +335,7 @@ fn auth() -> Result<(), Error> { let since = Since::new(raw_since_value); let htlc_expiry = Since::new(htlc.htlc_expiry()); if since >= htlc_expiry { - new_amount -= htlc.payment_amount(); + new_amount = new_amount.saturating_sub(htlc.payment_amount()); signatures_to_verify.push(( settlements.remove(0).signature(), htlc.local_htlc_pubkey_hash(), @@ -363,7 +363,7 @@ fn auth() -> Result<(), Error> { } { return Err(Error::PreimageError); } - new_amount -= htlc.payment_amount(); + new_amount = new_amount.saturating_sub(htlc.payment_amount()); signatures_to_verify.push(( settlements.remove(0).signature(), htlc.local_htlc_pubkey_hash(), @@ -377,7 +377,7 @@ fn auth() -> Result<(), Error> { let since = Since::new(raw_since_value); let htlc_expiry = Since::new(htlc.htlc_expiry()); if since >= htlc_expiry { - new_amount -= htlc.payment_amount(); + new_amount = new_amount.saturating_sub(htlc.payment_amount()); signatures_to_verify.push(( settlements.remove(0).signature(), htlc.remote_htlc_pubkey_hash(), @@ -412,7 +412,7 @@ fn auth() -> Result<(), Error> { .try_into() .unwrap(), ); - new_amount -= settlement_remote_amount; + new_amount = new_amount.saturating_sub(settlement_remote_amount); new_settlement_script.push(&[0u8; 36]); new_settlement_script @@ -437,7 +437,7 @@ fn auth() -> Result<(), Error> { .try_into() .unwrap(), ); - new_amount -= settlement_local_amount; + new_amount = new_amount.saturating_sub(settlement_local_amount); new_settlement_script.push(&witness[pending_htlcs_len..pending_htlcs_len + 36]); new_settlement_script.push(&[0u8; 36]); From 7c718ddeb8e1202a6b65c764bf00dff9fd683427 Mon Sep 17 00:00:00 2001 From: quake Date: Wed, 5 Nov 2025 16:12:22 +0900 Subject: [PATCH 21/27] chore: fix udt capacity check --- contracts/commitment-lock/src/main.rs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index f811bb7..dc7a899 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -479,13 +479,7 @@ fn auth() -> Result<(), Error> { match type_script { Some(udt_script) => { - // verify the first output cell's capacity, type script and udt amount are correct - let output_capacity = load_cell_capacity(0, Source::Output)?; - let input_capacity = load_cell_capacity(0, Source::GroupInput)?; - if output_capacity != input_capacity { - return Err(Error::OutputCapacityError); - } - + // verify the first output cell's type script and udt amount are correct let output_type = load_cell_type(0, Source::Output)?; if output_type != Some(udt_script) { return Err(Error::OutputTypeError); From 5d8fe2707d81a528bad7f0a7bf7c91916a730529 Mon Sep 17 00:00:00 2001 From: quake Date: Thu, 6 Nov 2025 13:02:57 +0900 Subject: [PATCH 22/27] chore: add more check for capacity --- contracts/commitment-lock/src/main.rs | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index dc7a899..52962fa 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -395,6 +395,7 @@ fn auth() -> Result<(), Error> { // settlement for local or remote parties let mut two_parties_all_settled = false; + let mut is_party_settlement = false; if settlements.len() == 1 { let settlement = settlements.remove(0); match settlement.unlock_type() { @@ -422,6 +423,7 @@ fn auth() -> Result<(), Error> { two_parties_all_settled = witness[pending_htlcs_len + 36..pending_htlcs_len + 72] == [0u8; 36]; + is_party_settlement = true; } 0xFF => { // local settlement should wait for delay_epoch @@ -446,6 +448,7 @@ fn auth() -> Result<(), Error> { two_parties_all_settled = witness[pending_htlcs_len..pending_htlcs_len + 36] == [0u8; 36]; + is_party_settlement = true; } unlock => { debug!("Invalid unlock type 2: {}", unlock); @@ -479,7 +482,15 @@ fn auth() -> Result<(), Error> { match type_script { Some(udt_script) => { - // verify the first output cell's type script and udt amount are correct + // verify the first output cell's capacity, type script and udt amount are correct + if !is_party_settlement { + let output_capacity = load_cell_capacity(0, Source::Output)?; + let input_capacity = load_cell_capacity(0, Source::GroupInput)?; + if output_capacity != input_capacity { + return Err(Error::OutputCapacityError); + } + } + let output_type = load_cell_type(0, Source::Output)?; if output_type != Some(udt_script) { return Err(Error::OutputTypeError); From ce76822ddf62b9ae4bb1888207089edf89c2a96a Mon Sep 17 00:00:00 2001 From: quake Date: Thu, 6 Nov 2025 13:20:12 +0900 Subject: [PATCH 23/27] chore: fix release build warning --- contracts/commitment-lock/src/main.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index 52962fa..d278a69 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -450,8 +450,8 @@ fn auth() -> Result<(), Error> { witness[pending_htlcs_len..pending_htlcs_len + 36] == [0u8; 36]; is_party_settlement = true; } - unlock => { - debug!("Invalid unlock type 2: {}", unlock); + _unlock => { + debug!("Invalid unlock type 2: {}", _unlock); return Err(Error::InvalidUnlockType); } } From 1f226ee0dbef38f54782f057d62307b2083c2645 Mon Sep 17 00:00:00 2001 From: quake Date: Thu, 13 Nov 2025 19:42:14 +0900 Subject: [PATCH 24/27] fix: swap expiry since and delay epoch since inputs position check --- contracts/commitment-lock/src/main.rs | 30 +++--- tests/src/tests.rs | 128 ++++++++++---------------- 2 files changed, 66 insertions(+), 92 deletions(-) diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index d278a69..d355e51 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -272,7 +272,7 @@ fn auth() -> Result<(), Error> { return Err(Error::InvalidSettlementCount); } - let raw_since_value = load_input_since(0, Source::GroupInput)?; + let raw_since_value = load_expiry_raw_since_value(); let delay_epoch = Since::new(u64::from_le_bytes(args[20..28].try_into().unwrap())); let message = { let tx = load_transaction()? @@ -537,17 +537,25 @@ fn auth() -> Result<(), Error> { } } -// Check if any input `since` is a relative epoch and >= `delay_epoch`. +// Check if input `since` is a relative epoch and >= `delay_epoch`. fn check_input_since(delay_epoch: Since) -> bool { - QueryIter::new(load_input_since, Source::Input).any(|since| { - let since = Since::new(since); - since - .extract_lock_value() - .map(|lock_value| matches!(lock_value, LockValue::EpochNumberWithFraction(_))) - .unwrap_or_default() - && since.is_relative() - && since >= delay_epoch - }) + let raw_since = load_input_since(0, Source::GroupInput).unwrap_or_default(); + let since = Since::new(raw_since); + since + .extract_lock_value() + .map(|lock_value| matches!(lock_value, LockValue::EpochNumberWithFraction(_))) + .unwrap_or_default() + && since.is_relative() + && since >= delay_epoch +} + +fn load_expiry_raw_since_value() -> u64 { + QueryIter::new(load_input_since, Source::Input) + .find(|&raw_since| { + let since = Since::new(raw_since); + matches!(since.extract_lock_value(), Some(LockValue::Timestamp(_)) if since.is_absolute()) + }) + .unwrap_or_default() } // Calculate the product of delay_epoch and a fraction diff --git a/tests/src/tests.rs b/tests/src/tests.rs index 21a9e73..62ee5de 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -240,9 +240,6 @@ fn test_commitment_lock_no_pending_htlcs() { let lock_script = context .build_script(&commitment_lock_out_point, args.clone().into()) .expect("script"); - let always_success_script = context - .build_script(&always_success_out_point, Bytes::new()) - .expect("script"); // prepare cell deps let commitment_lock_dep = CellDep::new_builder() @@ -262,12 +259,6 @@ fn test_commitment_lock_no_pending_htlcs() { .build(), Bytes::new(), ); - let delay_input_out_point = context.create_cell( - CellOutput::new_builder() - .lock(always_success_script) - .build(), - Bytes::new(), - ); // build transaction with revocation unlock logic let to_revocation_lock = Script::new_builder() @@ -356,19 +347,9 @@ fn test_commitment_lock_no_pending_htlcs() { let input = CellInput::new_builder() .previous_output(input_out_point.clone()) - .build(); - let delay_epoch_input = CellInput::new_builder() - .previous_output(delay_input_out_point.clone()) .since(delay_epoch.as_u64().pack()) .build(); - let inputs = vec![ - input, - delay_epoch_input - .clone() - .as_builder() - .since(delay_epoch.as_u64().pack()) - .build(), - ]; + let inputs = vec![input]; let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) @@ -558,19 +539,9 @@ fn test_commitment_lock_with_two_pending_htlcs() { // build transaction with remote_htlc_pubkey unlock offered pending htlc 1 let input = CellInput::new_builder() .previous_output(input_out_point.clone()) - .build(); - let delay_epoch_input = CellInput::new_builder() - .previous_output(delay_input_out_point.clone()) .since(delay_epoch.as_u64().pack()) .build(); - let inputs = vec![ - input, - delay_epoch_input - .clone() - .as_builder() - .since(half_delay_epoch.as_u64().pack()) - .build(), - ]; + let inputs = vec![input]; let new_pending_htlcs = [ [1].to_vec(), @@ -678,6 +649,10 @@ fn test_commitment_lock_with_two_pending_htlcs() { let input = CellInput::new_builder() .previous_output(input_out_point.clone()) + .since(delay_epoch.as_u64().pack()) + .build(); + let delay_epoch_input = CellInput::new_builder() + .previous_output(delay_input_out_point.clone()) .since(since.as_u64().pack()) .build(); let inputs = vec![input, delay_epoch_input.clone()]; @@ -723,9 +698,16 @@ fn test_commitment_lock_with_two_pending_htlcs() { let input = CellInput::new_builder() .previous_output(input_out_point.clone()) - .since(since.as_u64().pack()) + .since(delay_epoch.as_u64().pack()) .build(); - let inputs = vec![input, delay_epoch_input.clone()]; + let inputs = vec![ + input, + delay_epoch_input + .clone() + .as_builder() + .since(since.as_u64().pack()) + .build(), + ]; let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) @@ -761,10 +743,17 @@ fn test_commitment_lock_with_two_pending_htlcs() { // build transaction with remote_htlc_pubkey2 unlock received pending htlc 2 let since = Since::from_timestamp(1712062800 + 1000, true).unwrap(); let input = CellInput::new_builder() - .since(since.as_u64().pack()) + .since(delay_epoch.as_u64().pack()) .previous_output(input_out_point.clone()) .build(); - let inputs = vec![input, delay_epoch_input.clone()]; + let inputs = vec![ + input, + delay_epoch_input + .clone() + .as_builder() + .since(since.as_u64().pack()) + .build(), + ]; let new_pending_htlcs = [ [1].to_vec(), @@ -834,15 +823,9 @@ fn test_commitment_lock_with_two_pending_htlcs() { // build transaction with local_htlc_pubkey2 unlock received pending htlc 2 let input = CellInput::new_builder() .previous_output(input_out_point.clone()) + .since(half_delay_epoch.as_u64().pack()) .build(); - let inputs = vec![ - input, - delay_epoch_input - .clone() - .as_builder() - .since(half_delay_epoch.as_u64().pack()) - .build(), - ]; + let inputs = vec![input]; let outputs = vec![ CellOutput::new_builder() .capacity(((local_amount + remote_amount + payment_amount1) as u64).pack()) @@ -948,15 +931,9 @@ fn test_commitment_lock_with_two_pending_htlcs() { let outputs_data = [Bytes::new()]; let input = CellInput::new_builder() .previous_output(input_out_point.clone()) + .since(delay_epoch.as_u64().pack()) .build(); - let inputs = vec![ - input, - delay_epoch_input - .clone() - .as_builder() - .since(delay_epoch.as_u64().pack()) - .build(), - ]; + let inputs = vec![input]; let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) @@ -1031,15 +1008,9 @@ fn test_commitment_lock_with_two_pending_htlcs() { let outputs_data = [Bytes::new()]; let input = CellInput::new_builder() .previous_output(input_out_point.clone()) + .since(delay_epoch.as_u64().pack()) .build(); - let inputs = vec![ - input, - delay_epoch_input - .clone() - .as_builder() - .since(delay_epoch.as_u64().pack()) - .build(), - ]; + let inputs = vec![input]; let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) .inputs(inputs) @@ -1205,19 +1176,9 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { // build transaction with remote_htlc_pubkey unlock offered pending htlc 1 let input = CellInput::new_builder() .previous_output(input_out_point.clone()) + .since(half_delay_epoch.as_u64().pack()) .build(); - let delay_epoch_input = CellInput::new_builder() - .previous_output(delay_input_out_point.clone()) - .since(delay_epoch.as_u64().pack()) - .build(); - let inputs = vec![ - input, - delay_epoch_input - .clone() - .as_builder() - .since(half_delay_epoch.as_u64().pack()) - .build(), - ]; + let inputs = vec![input]; let new_pending_htlcs = [ [1].to_vec(), @@ -1324,6 +1285,10 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { let input = CellInput::new_builder() .previous_output(input_out_point.clone()) + .since(delay_epoch.as_u64().pack()) + .build(); + let delay_epoch_input = CellInput::new_builder() + .previous_output(delay_input_out_point.clone()) .since(since.as_u64().pack()) .build(); let inputs = vec![input, delay_epoch_input.clone()]; @@ -1361,10 +1326,17 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { // build transaction with remote_htlc_pubkey2 unlock received pending htlc 2 let since = Since::from_timestamp(1712062800 + 1000, true).unwrap(); let input = CellInput::new_builder() - .since(since.as_u64().pack()) + .since(delay_epoch.as_u64().pack()) .previous_output(input_out_point.clone()) .build(); - let inputs = vec![input, delay_epoch_input.clone()]; + let inputs = vec![ + input, + delay_epoch_input + .clone() + .as_builder() + .since(since.as_u64().pack()) + .build(), + ]; let new_pending_htlcs = [ [1].to_vec(), [0b00000000].to_vec(), @@ -1430,15 +1402,9 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { // build transaction with local_htlc_pubkey2 unlock received pending htlc 2 let input = CellInput::new_builder() .previous_output(input_out_point.clone()) + .since(half_delay_epoch.as_u64().pack()) .build(); - let inputs = vec![ - input, - delay_epoch_input - .clone() - .as_builder() - .since(half_delay_epoch.as_u64().pack()) - .build(), - ]; + let inputs = vec![input]; let tx = TransactionBuilder::default() .cell_deps(cell_deps) From 8387efd0d1740c8f090b4045ccbe53584bb20b3b Mon Sep 17 00:00:00 2001 From: quake Date: Mon, 17 Nov 2025 19:27:13 +0900 Subject: [PATCH 25/27] chore: add settlement_flag --- contracts/commitment-lock/README.md | 1 + contracts/commitment-lock/src/main.rs | 16 +++++++++------- tests/src/tests.rs | 10 ++++++++++ 3 files changed, 20 insertions(+), 7 deletions(-) diff --git a/contracts/commitment-lock/README.md b/contracts/commitment-lock/README.md index c7a54d2..1b81593 100644 --- a/contracts/commitment-lock/README.md +++ b/contracts/commitment-lock/README.md @@ -9,6 +9,7 @@ The lock script args is concatenated by the following fields: - `delay_epoch`: 8 bytes, u64 in little endian, must be a relative [EpochNumberWithFraction](https://github.com/nervosnetwork/ckb/blob/develop/rpc/README.md#type-epochnumberwithfraction) - `version`: 8 bytes, u64 in big-endian - `settlement_hash`: 20 bytes, hash result of blake160(pending_htlc_count || N * pending_htlc || settlement_remote_pubkey_hash || settlement_remote_amount || settlement_local_pubkey_hash || settlement_local_amount) +- `settlement_flag`: 1 byte, 0x00 means this cell is created for first funding cell unlock, 0x01 means this cell is created for subsequent commitment cell unlock, others are reserved To unlock this lock, the transaction must provide the following fields in the witness: - `empty_witness_args`: 16 bytes, fixed to 0x10000000100000001000000010000000, for compatibility with the xudt diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index d355e51..fc50fef 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -169,9 +169,10 @@ fn auth() -> Result<(), Error> { let script = load_script()?; let args: Bytes = script.args().unpack(); - if args.len() != 56 { + if args.len() != 57 { return Err(Error::ArgsLenError); } + let is_first_settlement = args[56] == 0x00; let mut witness = load_witness(0, Source::GroupInput)?; if witness @@ -306,7 +307,7 @@ fn auth() -> Result<(), Error> { HtlcType::Offered => { if raw_since_value == 0 { // Preimage unlock delay_epoch should be shorter than the expiry unlock, we use 1/3 of delay_epoch - if !check_input_since(mul(delay_epoch, 1, 3)) { + if is_first_settlement && !check_input_since(mul(delay_epoch, 1, 3)) { return Err(Error::InvalidSince); } // when input since is 0, it means the unlock logic is for remote_htlc pubkey and preimage @@ -328,7 +329,7 @@ fn auth() -> Result<(), Error> { )); } else { // Expiry unlock delay_epoch should be shorter than non-pending unlock and longer than the preimage unlock, we use 2/3 of delay_epoch - if !check_input_since(mul(delay_epoch, 2, 3)) { + if is_first_settlement && !check_input_since(mul(delay_epoch, 2, 3)) { return Err(Error::InvalidSince); } // when input since is not 0, it means the unlock logic is for local_htlc pubkey and htlc expiry @@ -348,7 +349,7 @@ fn auth() -> Result<(), Error> { HtlcType::Received => { if raw_since_value == 0 { // Preimage unlock delay_epoch should be shorter than the expiry unlock, we use 1/3 of delay_epoch - if !check_input_since(mul(delay_epoch, 1, 3)) { + if is_first_settlement && !check_input_since(mul(delay_epoch, 1, 3)) { return Err(Error::InvalidSince); } // when input since is 0, it means the unlock logic is for local_htlc pubkey and preimage @@ -370,7 +371,7 @@ fn auth() -> Result<(), Error> { )); } else { // Expiry unlock delay_epoch should be shorter than non-pending unlock and longer than the preimage unlock, we use 2/3 of delay_epoch - if !check_input_since(mul(delay_epoch, 2, 3)) { + if is_first_settlement && !check_input_since(mul(delay_epoch, 2, 3)) { return Err(Error::InvalidSince); } // when input since is not 0, it means the unlock logic is for remote_htlc pubkey and htlc expiry @@ -401,7 +402,7 @@ fn auth() -> Result<(), Error> { match settlement.unlock_type() { 0xFE => { // remote settlement should wait for delay_epoch - if !check_input_since(delay_epoch) { + if is_first_settlement && !check_input_since(delay_epoch) { return Err(Error::InvalidSince); } let settlement_remote_pubkey_hash: [u8; 20] = witness @@ -427,7 +428,7 @@ fn auth() -> Result<(), Error> { } 0xFF => { // local settlement should wait for delay_epoch - if !check_input_since(delay_epoch) { + if is_first_settlement && !check_input_since(delay_epoch) { return Err(Error::InvalidSince); } let settlement_local_pubkey_hash: [u8; 20] = witness @@ -470,6 +471,7 @@ fn auth() -> Result<(), Error> { let expected_lock_args = [ &args[0..36], blake2b_256(new_settlement_script.concat())[0..20].as_ref(), + &[0x01], // 0x01 means new cell is created for subsequent commitment cell unlock ] .concat() .pack(); diff --git a/tests/src/tests.rs b/tests/src/tests.rs index 62ee5de..29b9106 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -234,6 +234,7 @@ fn test_commitment_lock_no_pending_htlcs() { delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), &blake2b_256(&settlement_script)[0..20], + &[0x00], ] .concat(); @@ -329,6 +330,7 @@ fn test_commitment_lock_no_pending_htlcs() { delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), &blake2b_256(&new_settlement_script)[0..20], + &[0x01], ] .concat(); @@ -499,6 +501,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), &blake2b_256(&settlement_script)[0..20], + &[0x00], ] .concat(); @@ -560,6 +563,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), &blake2b_256(new_settlement_script)[0..20], + &[0x01], ] .concat(); let new_lock_script = lock_script @@ -773,6 +777,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), &blake2b_256(new_settlement_script)[0..20], + &[0x01], ] .concat(); let new_lock_script = lock_script @@ -913,6 +918,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), &blake2b_256(&new_settlement_script)[0..20], + &[0x01], ] .concat(); @@ -990,6 +996,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), &blake2b_256(&new_settlement_script)[0..20], + &[0x01], ] .concat(); @@ -1124,6 +1131,7 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), &blake2b_256(&settlement_script)[0..20], + &[0x00], ] .concat(); @@ -1196,6 +1204,7 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), &blake2b_256(&new_settlement_script)[0..20], + &[0x01], ] .concat(); let new_lock_script = lock_script @@ -1353,6 +1362,7 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { delay_epoch.as_u64().to_le_bytes().as_slice(), commitment_tx_version.to_be_bytes().as_slice(), &blake2b_256(&new_settlement_script)[0..20], + &[0x01], ] .concat(); let new_lock_script = lock_script.as_builder().args(new_args.pack()).build(); From 3137b478e03b4f7f91c6816461ffb01e667e72ae Mon Sep 17 00:00:00 2001 From: quake Date: Thu, 20 Nov 2025 14:35:03 +0900 Subject: [PATCH 26/27] chore: remove debug and apply review comment --- contracts/commitment-lock/src/main.rs | 18 ++++++------------ tests/src/tests.rs | 18 +++++++++--------- 2 files changed, 15 insertions(+), 21 deletions(-) diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index fc50fef..d64b453 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -16,7 +16,6 @@ use alloc::{ffi::CString, vec::Vec}; use ckb_std::{ ckb_constants::Source, ckb_types::{bytes::Bytes, core::ScriptHashType, prelude::*}, - debug, error::SysError, high_level::{ QueryIter, exec_cell, load_cell, load_cell_capacity, load_cell_data, load_cell_lock, @@ -42,6 +41,7 @@ pub enum Error { InvalidUnlockType, InvalidWithPreimageFlag, InvalidSettlementCount, + InvalidUnlockCount, InvalidExpiry, ArgsLenError, WitnessLenError, @@ -225,10 +225,7 @@ fn auth() -> Result<(), Error> { Ok(()) } else { // settlement unlock process - debug!("unlock_count: {}", unlock_count); let pending_htlc_count = witness[0] as usize; - debug!("pending_htlc_count: {}", pending_htlc_count); - debug!("witness: {:x?}", witness); // 1 (pending_htlc_count) + pending_htlc_count * HTLC_SCRIPT_LEN let pending_htlcs_len = 1 + pending_htlc_count * HTLC_SCRIPT_LEN; // settlement_remote_pubkey_hash + settlement_remote_amount + settlement_local_pubkey_hash + settlement_local_amount @@ -245,11 +242,9 @@ fn auth() -> Result<(), Error> { let mut settlement_htlc_count = 0; let mut i = settlement_script_len; while witness.len() > i { - debug!("i: {}, len: {}", i, witness.len()); let unlock_type = witness[i]; if unlock_type >= pending_htlc_count as u8 && unlock_type != 0xFE && unlock_type != 0xFF { - debug!("Invalid unlock type 1: {:?}", witness); return Err(Error::InvalidUnlockType); } else if unlock_type < pending_htlc_count as u8 { settlement_htlc_count += 1; @@ -264,7 +259,6 @@ fn auth() -> Result<(), Error> { )); i += 2 + SIGNATURE_LEN + PREIMAGE_LEN; } else { - debug!("Invalid with_preimage flag: {}", with_preimage); return Err(Error::InvalidWithPreimageFlag); } } @@ -452,7 +446,6 @@ fn auth() -> Result<(), Error> { is_party_settlement = true; } _unlock => { - debug!("Invalid unlock type 2: {}", _unlock); return Err(Error::InvalidUnlockType); } } @@ -462,9 +455,6 @@ fn auth() -> Result<(), Error> { return Err(Error::InvalidSettlementCount); } - debug!("new_settlement_script: {:x?}", new_settlement_script); - debug!("new_amount: {}", new_amount); - // verify the first output cell's lock script and capacity are correct if new_amount > 0 && !two_parties_all_settled { let output_lock = load_cell_lock(0, Source::Output)?; @@ -513,6 +503,11 @@ fn auth() -> Result<(), Error> { } } } + + if signatures_to_verify.is_empty() { + return Err(Error::InvalidUnlockCount); + } + // AuthAlgorithmIdCkb = 0 for (signature, pubkey_hash) in signatures_to_verify { let algorithm_id_str = CString::new(encode([0u8])).unwrap(); @@ -530,7 +525,6 @@ fn auth() -> Result<(), Error> { let pid = spawn_cell(&AUTH_CODE_HASH, ScriptHashType::Data1, &args, &[]) .map_err(|_| Error::AuthError)?; let result = wait(pid).map_err(|_| Error::AuthError)?; - debug!("auth result: {}", result); if result != 0 { return Err(Error::AuthError); } diff --git a/tests/src/tests.rs b/tests/src/tests.rs index 29b9106..4fba48e 100644 --- a/tests/src/tests.rs +++ b/tests/src/tests.rs @@ -627,7 +627,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { .verify_tx(&fail_tx, MAX_CYCLES) .expect_err("wrong preimage should fail"); println!("error: {}", error); - assert!(error.to_string().contains("#21")); // PreimageError + assert!(error.to_string().contains("#22")); // PreimageError // sign with remote_htlc_pubkey and empty preimage should fail let witness = [ @@ -646,7 +646,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { .verify_tx(&fail_tx, MAX_CYCLES) .expect_err("empty preimage should fail"); println!("error: {}", error); - assert!(error.to_string().contains("#21")); // PreimageError + assert!(error.to_string().contains("#22")); // PreimageError // build transaction with local_htlc_pubkey unlock offered pending htlc 1 let since = Since::from_timestamp(1711976400 + 1000, true).unwrap(); @@ -742,7 +742,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { .verify_tx(&fail_tx, MAX_CYCLES) .expect_err("none-expired since should fail"); println!("error: {}", error); - assert!(error.to_string().contains("#10")); // InvalidExpiry + assert!(error.to_string().contains("#11")); // InvalidExpiry // build transaction with remote_htlc_pubkey2 unlock received pending htlc 2 let since = Since::from_timestamp(1712062800 + 1000, true).unwrap(); @@ -885,7 +885,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { .verify_tx(&fail_tx, MAX_CYCLES) .expect_err("wrong preimage should fail"); println!("error: {}", error); - assert!(error.to_string().contains("#21")); // PreimageError + assert!(error.to_string().contains("#22")); // PreimageError // sign with local_htlc_key2 and empty preimage should fail let witness = [ @@ -902,7 +902,7 @@ fn test_commitment_lock_with_two_pending_htlcs() { .verify_tx(&fail_tx, MAX_CYCLES) .expect_err("empty preimage should fail"); println!("error: {}", error); - assert!(error.to_string().contains("#21")); // PreimageError + assert!(error.to_string().contains("#22")); // PreimageError // test with settlement unlock logic (remote settlement key) let new_two_party_settlement = [ @@ -1271,7 +1271,7 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { let err = context .verify_tx(&fail_tx, MAX_CYCLES) .expect_err("wrong preimage should fail"); - assert!(err.to_string().contains("#21")); // PreimageError + assert!(err.to_string().contains("#22")); // PreimageError // sign with remote_htlc_key1 and empty preimage should fail let witness = [ @@ -1287,7 +1287,7 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { let err = context .verify_tx(&fail_tx, MAX_CYCLES) .expect_err("empty preimage should fail"); - assert!(err.to_string().contains("#21")); // PreimageError + assert!(err.to_string().contains("#22")); // PreimageError // build transaction with local_htlc_pubkey unlock offered pending htlc 1 let since = Since::from_timestamp(1711976400 + 1000, true).unwrap(); @@ -1462,7 +1462,7 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { let err = context .verify_tx(&fail_tx, MAX_CYCLES) .expect_err("wrong preimage should fail"); - assert!(err.to_string().contains("#21")); // PreimageError + assert!(err.to_string().contains("#22")); // PreimageError // sign with local_htlc_key2 and empty preimage should fail let witness = [ @@ -1478,5 +1478,5 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { let err = context .verify_tx(&fail_tx, MAX_CYCLES) .expect_err("empty preimage should fail"); - assert!(err.to_string().contains("#21")); // PreimageError + assert!(err.to_string().contains("#22")); // PreimageError } From ea84410878048a142c506d71c32ee0d1f0fc1c20 Mon Sep 17 00:00:00 2001 From: quake Date: Thu, 20 Nov 2025 18:02:23 +0900 Subject: [PATCH 27/27] chore: code cleanup and update checksum --- checksums.txt | 8 ++++---- contracts/commitment-lock/src/main.rs | 11 ++++++++--- 2 files changed, 12 insertions(+), 7 deletions(-) diff --git a/checksums.txt b/checksums.txt index 3d2ee1f..32b3959 100644 --- a/checksums.txt +++ b/checksums.txt @@ -1,4 +1,4 @@ -80d0b47a9e4763ae70f478844c0b68f10fbaca8fe4f4a487708af26f1be9ebbc build/release/commitment-lock -c83ac363f2b21cd46a49c0aa23bec6ca70a5e85a2f97f352b809c8d0e26800eb build/release/commitment-lock.debug -a9fab24fdde52628ef5d9e6a492c2d0e85bbb4c8645cb70c0bc668b1935adb3a build/release/funding-lock -c454bbe2febfe3dc05e84ae3ddbcd8ad2e1bf638a06f1adf1dfea4d8e2e62a7a build/release/funding-lock.debug +43f698790b28c433607bc5a2a2b9cbe5ed86ed43a753d014c9a0670f9274ce8d build/release/commitment-lock +41de10902949fd46cb07107068b57e32300fb3659311716c34f3392ca82d7fde build/release/commitment-lock.debug +831f164aad1509db5483dd50c50bf4ed5cb6601f941600287ffdb5e79a29cf9e build/release/funding-lock +291bdd4a48783ce75012a6e21935090806014d33bf6c52b1b2374e8feeaa91f0 build/release/funding-lock.debug diff --git a/contracts/commitment-lock/src/main.rs b/contracts/commitment-lock/src/main.rs index d64b453..ac03e6e 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -18,8 +18,8 @@ use ckb_std::{ ckb_types::{bytes::Bytes, core::ScriptHashType, prelude::*}, error::SysError, high_level::{ - QueryIter, exec_cell, load_cell, load_cell_capacity, load_cell_data, load_cell_lock, - load_cell_type, load_input_since, load_script, load_transaction, load_witness, spawn_cell, + QueryIter, load_cell, load_cell_capacity, load_cell_data, load_cell_lock, load_cell_type, + load_input_since, load_script, load_transaction, load_witness, spawn_cell, }, since::{EpochNumberWithFraction, LockValue, Since}, syscalls::wait, @@ -221,7 +221,12 @@ fn auth() -> Result<(), Error> { pubkey_hash_str.as_c_str(), ]; - exec_cell(&AUTH_CODE_HASH, ScriptHashType::Data1, &args).map_err(|_| Error::AuthError)?; + let pid = spawn_cell(&AUTH_CODE_HASH, ScriptHashType::Data1, &args, &[]) + .map_err(|_| Error::AuthError)?; + let result = wait(pid).map_err(|_| Error::AuthError)?; + if result != 0 { + return Err(Error::AuthError); + } Ok(()) } else { // settlement unlock process