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/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/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/README.md b/contracts/commitment-lock/README.md index 20a76ed..1b81593 100644 --- a/contracts/commitment-lock/README.md +++ b/contracts/commitment-lock/README.md @@ -2,27 +2,25 @@ 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) - `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(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 -- `unlock_type`: 1 byte, 0x00 ~ 0xFD for pending HTLC unlock, 0xFE for non-pending HTLC unlock, 0xFF for revocation unlock +- `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 - `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 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 +29,77 @@ For pending HTLC unlock process, the transaction must provide the following fiel - `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_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 ~ 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 + +## 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 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 + +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 and pubkey hash is updated to 20 bytes zeros: + ```rust + new_settlement_script = [ + new_pending_htlc_count, + remaining_htlcs..., + remote_pubkey_hash, + 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 + ] + ``` + +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. 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..ac03e6e 100644 --- a/contracts/commitment-lock/src/main.rs +++ b/contracts/commitment-lock/src/main.rs @@ -18,10 +18,11 @@ 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, 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, }; use hex::encode; use sha2::{Digest, Sha256}; @@ -38,6 +39,9 @@ pub enum Error { MultipleInputs, InvalidSince, InvalidUnlockType, + InvalidWithPreimageFlag, + InvalidSettlementCount, + InvalidUnlockCount, InvalidExpiry, ArgsLenError, WitnessLenError, @@ -93,7 +97,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 +105,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 +113,51 @@ 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 Settlement<'_> { + 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] { + if self.with_preimage() { + &self.0[2 + SIGNATURE_LEN..2 + SIGNATURE_LEN + PREIMAGE_LEN] + } else { + &[] + } + } +} + 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,9 +169,10 @@ fn auth() -> Result<(), Error> { let script = load_script()?; let args: Bytes = script.args().unpack(); - if args.len() != 36 && 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 @@ -153,8 +182,8 @@ fn auth() -> Result<(), Error> { { return Err(Error::EmptyWitnessArgsError); } - let unlock_type = witness.remove(0); - if unlock_type == 0xFF { + let unlock_count = witness.remove(0); + if unlock_count == 0x00 { // revocation unlock process // verify version @@ -192,64 +221,59 @@ fn auth() -> Result<(), Error> { pubkey_hash_str.as_c_str(), ]; - 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); + 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 { - // 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_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); + } + // 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 = settlement_script_len; + while witness.len() > i { + let unlock_type = witness[i]; + if unlock_type >= pending_htlc_count as u8 && unlock_type != 0xFE && unlock_type != 0xFF + { + return Err(Error::InvalidUnlockType); + } else if unlock_type < pending_htlc_count as u8 { + settlement_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; + } 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)?; + if settlements.is_empty() { + return Err(Error::InvalidSettlementCount); + } + + 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()? .raw() @@ -258,29 +282,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,26 +289,28 @@ 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.is_empty() && settlements[0].unlock_type() == i as u8 { + let htlc = Htlc(htlc_script); match htlc.htlc_type() { 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 - 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] @@ -318,19 +321,25 @@ fn auth() -> Result<(), Error> { } { return Err(Error::PreimageError); } - new_amount -= htlc.payment_amount(); - pubkey_hash.copy_from_slice(htlc.remote_htlc_pubkey_hash()); + new_amount = new_amount.saturating_sub(htlc.payment_amount()); + 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)) { + 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 let since = Since::new(raw_since_value); let htlc_expiry = Since::new(htlc.htlc_expiry()); if since >= htlc_expiry { - new_amount -= htlc.payment_amount(); - pubkey_hash.copy_from_slice(htlc.local_htlc_pubkey_hash()); + new_amount = new_amount.saturating_sub(htlc.payment_amount()); + signatures_to_verify.push(( + settlements.remove(0).signature(), + htlc.local_htlc_pubkey_hash(), + )); } else { return Err(Error::InvalidExpiry); } @@ -339,11 +348,11 @@ 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 - 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] @@ -354,19 +363,25 @@ fn auth() -> Result<(), Error> { } { return Err(Error::PreimageError); } - new_amount -= htlc.payment_amount(); - pubkey_hash.copy_from_slice(htlc.local_htlc_pubkey_hash()); + new_amount = new_amount.saturating_sub(htlc.payment_amount()); + 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)) { + 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 let since = Since::new(raw_since_value); let htlc_expiry = Since::new(htlc.htlc_expiry()); if since >= htlc_expiry { - new_amount -= htlc.payment_amount(); - pubkey_hash.copy_from_slice(htlc.remote_htlc_pubkey_hash()); + new_amount = new_amount.saturating_sub(htlc.payment_amount()); + signatures_to_verify.push(( + settlements.remove(0).signature(), + htlc.remote_htlc_pubkey_hash(), + )); } else { return Err(Error::InvalidExpiry); } @@ -374,87 +389,174 @@ 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 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() { + 0xFE => { + // remote settlement should wait for delay_epoch + if is_first_settlement && !check_input_since(delay_epoch) { + return Err(Error::InvalidSince); + } + let settlement_remote_pubkey_hash: [u8; 20] = witness + [pending_htlcs_len..pending_htlcs_len + 20] + .try_into() + .unwrap(); + let settlement_remote_amount = u128::from_le_bytes( + witness[pending_htlcs_len + 20..pending_htlcs_len + 36] + .try_into() + .unwrap(), + ); + new_amount = new_amount.saturating_sub(settlement_remote_amount); + + 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_remote_pubkey_hash)); + + 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 + if is_first_settlement && !check_input_since(delay_epoch) { + return Err(Error::InvalidSince); + } + let settlement_local_pubkey_hash: [u8; 20] = witness + [pending_htlcs_len + 36..pending_htlcs_len + 56] + .try_into() + .unwrap(); + let settlement_local_amount = u128::from_le_bytes( + witness[pending_htlcs_len + 56..pending_htlcs_len + 72] + .try_into() + .unwrap(), + ); + 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]); + signatures_to_verify + .push((settlement.signature(), settlement_local_pubkey_hash)); + + two_parties_all_settled = + witness[pending_htlcs_len..pending_htlcs_len + 36] == [0u8; 36]; + is_party_settlement = true; + } + _unlock => { + return Err(Error::InvalidUnlockType); + } + } + } else if settlements.is_empty() { + new_settlement_script.push(&witness[pending_htlcs_len..pending_htlcs_len + 72]); } else { - [ + return Err(Error::InvalidSettlementCount); + } + + // 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)?; + 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(), + &[0x01], // 0x01 means new cell is created for subsequent commitment cell unlock ] .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 + 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); - } + 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); + } } } } + if signatures_to_verify.is_empty() { + return Err(Error::InvalidUnlockCount); + } + // 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(); + 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(), - ]; + 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)?; + 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(()) } } -// 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/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/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 e59d00f..4c4b885 100755 Binary files a/deps/auth and b/deps/auth differ diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 1de01fa..00822fd 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -1,2 +1,2 @@ [toolchain] -channel = "1.81.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: 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..4fba48e 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}; @@ -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(); @@ -213,10 +214,27 @@ 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 remote_settlement_key = generator.gen_keypair(); + 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(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(); + 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], + &[0x00], ] .concat(); @@ -229,12 +247,15 @@ fn test_commitment_lock_no_pending_htlcs() { .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( CellOutput::new_builder() - .capacity(1000u64.pack()) + .capacity(((local_amount + remote_amount) as u64).pack()) .lock(lock_script.clone()) .build(), Bytes::new(), @@ -245,7 +266,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(); @@ -279,7 +300,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 +316,116 @@ 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) + // test with settlement unlock logic (local settlement key) + let new_settlement_script = [ + [0].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(); + + 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], + &[0x01], + ] + .concat(); + + let new_lock_script = lock_script + .clone() + .as_builder() + .args(new_args.pack()) .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()) + let outputs = vec![ + CellOutput::new_builder() + .capacity((remote_amount 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()) + .since(delay_epoch.as_u64().pack()) .build(); - let to_remote_output = CellOutput::new_builder() - .capacity(500u64.pack()) - .lock(to_remote_output_script) + let inputs = vec![input]; + + let tx = TransactionBuilder::default() + .cell_deps(cell_deps.clone()) + .inputs(inputs) + .outputs(outputs.clone()) + .outputs_data(outputs_data.pack()) .build(); - let to_remote_output_data = Bytes::from("to_remote_output_data").pack(); - 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()]; + // 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![0xFF, 0x00], // unlock with local 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 with settlement unlock logic (remote settlement key) + let input_out_point = context.create_cell(outputs[0].clone(), 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()) + .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) + .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 = remote_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], + new_settlement_script.clone(), + vec![0xFE, 0x00], // unlock with remote settlement key, 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); } @@ -381,6 +451,11 @@ fn test_commitment_lock_with_two_pending_htlcs() { let commitment_tx_version = 42u64; let mut generator = Generator::new(); + let remote_settlement_key = generator.gen_keypair(); + 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(); let local_htlc_key1 = generator.gen_keypair(); @@ -411,11 +486,22 @@ fn test_commitment_lock_with_two_pending_htlcs() { ] .concat(); + let two_party_settlement = [ + 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(); + + 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], + &[0x00], ] .concat(); @@ -439,7 +525,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(), @@ -454,19 +542,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(), @@ -479,11 +557,13 @@ 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], + &[0x01], ] .concat(); let new_lock_script = lock_script @@ -491,10 +571,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(((local_amount + remote_amount + 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()) @@ -503,7 +585,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 @@ -513,8 +595,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(), ] @@ -526,11 +609,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(), ] @@ -543,13 +627,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("#22")); // 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(); @@ -560,19 +646,26 @@ 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("#22")); // PreimageError // build transaction with local_htlc_pubkey unlock offered pending htlc 1 let since = Since::from_timestamp(1711976400 + 1000, true).unwrap(); 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()]; - 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(((local_amount + remote_amount + 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()) @@ -591,8 +684,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(); @@ -608,9 +702,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()) @@ -629,9 +730,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(); @@ -640,14 +742,22 @@ 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("#11")); // 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()) + .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(), @@ -659,18 +769,28 @@ 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], + &[0x01], ] .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 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()) + .lock(new_lock_script.clone()) + .build(), + ]; let outputs_data = [Bytes::new()]; let tx = TransactionBuilder::default() .cell_deps(cell_deps.clone()) @@ -679,7 +799,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 @@ -690,8 +810,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(); @@ -704,31 +825,27 @@ 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()) + .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()) + let inputs = vec![input]; + let outputs = vec![ + CellOutput::new_builder() + .capacity(((local_amount + remote_amount + payment_amount1) 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) + .cell_deps(cell_deps.clone()) .inputs(inputs) .outputs(outputs) .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 @@ -739,7 +856,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(), ] @@ -751,11 +869,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(), ] @@ -766,12 +885,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("#22")); // 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(); @@ -781,6 +902,160 @@ 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("#22")); // PreimageError + + // 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(), + local_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], + &[0x01], + ] + .concat(); + + let new_lock_script = lock_script + .clone() + .as_builder() + .args(new_args.pack()) + .build(); + let outputs = vec![ + CellOutput::new_builder() + .capacity(((local_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()) + .since(delay_epoch.as_u64().pack()) + .build(); + let inputs = vec![input]; + + let tx = TransactionBuilder::default() + .cell_deps(cell_deps.clone()) + .inputs(inputs) + .outputs(outputs.clone()) + .outputs_data(outputs_data.pack()) + .build(); + + // sign with remote_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], + settlement_script.clone(), + vec![0xFE, 0x00], // unlock with remote 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 (remote settlement key + remote 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_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(), + ] + .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], + &[0x01], + ] + .concat(); + + let new_lock_script = lock_script + .clone() + .as_builder() + .args(new_args.pack()) + .build(); + + let outputs = vec![ + CellOutput::new_builder() + .capacity(((local_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()) + .since(delay_epoch.as_u64().pack()) + .build(); + let inputs = vec![input]; + let tx = TransactionBuilder::default() + .cell_deps(cell_deps.clone()) + .inputs(inputs) + .outputs(outputs.clone()) + .outputs_data(outputs_data.pack()) + .build(); + // sign with remote_settlement_key and remote_htlc_key1 + let message: [u8; 32] = compute_tx_message(&tx); + + let signature1 = remote_htlc_key1 + .0 + .sign_recoverable(&message.into()) + .unwrap() + .serialize(); + + let signature2 = remote_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 remote_htlc_key1 and preimage + signature1.clone(), + preimage1.to_vec(), + [0xFE, 0x00].to_vec(), // unlock with remote 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] @@ -806,6 +1081,11 @@ fn test_commitment_lock_with_two_pending_htlcs_and_sudt() { let commitment_tx_version = 42u64; let mut generator = Generator::new(); + let remote_settlement_key = generator.gen_keypair(); + 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(); let local_htlc_key1 = generator.gen_keypair(); @@ -836,11 +1116,22 @@ 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], + &[0x00], ] .concat(); @@ -874,7 +1165,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()) @@ -893,19 +1184,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(), @@ -917,11 +1198,13 @@ 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], + &[0x01], ] .concat(); let new_lock_script = lock_script @@ -929,23 +1212,27 @@ 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) - .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 @@ -955,47 +1242,74 @@ 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("#22")); // 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("#22")); // PreimageError + // build transaction with local_htlc_pubkey unlock offered pending htlc 1 let since = Since::from_timestamp(1711976400 + 1000, true).unwrap(); 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()]; - 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 @@ -1005,28 +1319,33 @@ 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()) + .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(), @@ -1037,31 +1356,37 @@ 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], + &[0x01], ] .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) - .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 @@ -1072,41 +1397,25 @@ 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()) + .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 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 inputs = vec![input]; + let tx = TransactionBuilder::default() .cell_deps(cell_deps) .inputs(inputs) @@ -1114,7 +1423,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 @@ -1125,18 +1434,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("#22")); // 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("#22")); // PreimageError }