diff --git a/contrib/test.sh b/contrib/test.sh index 6ccb6fd9..180364d0 100755 --- a/contrib/test.sh +++ b/contrib/test.sh @@ -8,6 +8,7 @@ then alias cargo="cargo +$TOOLCHAIN" fi + cargo --version rustc --version diff --git a/q b/q new file mode 100644 index 00000000..b8aa628c --- /dev/null +++ b/q @@ -0,0 +1,957 @@ +commit 7ea5e06f02cff76de59e57b8ba3caa16a9a7ffc8 (HEAD -> pr_29_backup) +Author: GeneFerneau +Date: Thu Aug 19 02:10:15 2021 +0000 + + zkp: Add Musig2 module + + squash! zkp: Add Musig2 module + + Remove agg_pk from KeyAggCache + + Co-authored-by: sanket1729 + +commit 7caec153cf689d88bf929487e7648b4d72d1b980 +Author: GeneFerneau +Date: Thu Aug 19 02:09:33 2021 +0000 + + secp256k1-zkp-sys: Add Rust FFI for Musig2 module + + Co-authored-by: sanket1729 + +commit c61a9820bd57162914d24642beb2405c1870bf6d (origin/update_secp_03_2022, update_secp_03_2022) +Author: sanket1729 +Date: Mon Mar 14 12:50:52 2022 -0700 + + Fix warnings + + 1) SECP256k1_BUILD is already set, so setting to Some("") avoids the + duplicate warning + 2) Misc rust unused warnings in various feature flag combinations + +commit 46a2c48748a66b4b2f6bda795f80b2a50b899ed0 +Author: sanket1729 +Date: Mon Mar 14 11:27:03 2022 -0700 + + Updates upstream to 725d895fc54cf82da1c2a9c69048656405da556d + See also the changed patch: secp256k1.h.patch + +commit b1a3048a50eb0fe10d6efd4e186ba6e0920ef2b5 +Author: sanket1729 +Date: Mon Mar 14 12:27:21 2022 -0700 + + Update rust upstream + +commit 3db533c38ff8fc393719daf329ae9ebb44f019d0 +Author: sanket1729 +Date: Wed Jan 26 10:31:28 2022 +0530 + + Remove custom hash derives to fix build errors + + The upstream impl_array_newtype now implements a core::hash::Hash. This + caused a breaking changed here :( despite it being a minor release. + +commit ed645685dab52a7d59061fe76ae67fa60a810504 +Author: sanket1729 +Date: Tue Mar 15 07:31:44 2022 -0700 + + Comment out Wasm build on CI + +commit cdba7dc7dd83200f44fc301f3de86c9fe9a6b4e9 +Author: Jonas Nick +Date: Thu Nov 4 21:29:20 2021 +0000 + + Prepare for -sys release 0.5.0 + +commit c5a074a7edcad2a063ab9f2b28bb542b789771b5 +Merge: ddc5e26 582a5a0 +Author: Jonas Nick +Date: Thu Nov 4 09:27:09 2021 +0000 + + Merge ElementsProject/rust-secp256k1-zkp#41: Prepare for release 0.5.0 + + 582a5a0408e775fc38d5e7475d70c693750018ac Prepare for release 0.5.0 (Jonas Nick) + + Pull request description: + + ACKs for top commit: + thomaseizinger: + ACK 582a5a0408e775fc38d5e7475d70c693750018ac + + Tree-SHA512: 5a7f2fefc8cee12cd0337b5594bf7b8efc155d2803f49ddc50eb5eb91a05af7263f0507a371f33f2161cddad26530dd92535c534f936620a872bde274aff1e03 + +commit 582a5a0408e775fc38d5e7475d70c693750018ac +Author: Jonas Nick +Date: Fri Oct 22 13:23:45 2021 +0000 + + Prepare for release 0.5.0 + +commit ddc5e2629797ca00633a7f9807574edf3d61f3d3 +Merge: c8ff901 02f1666 +Author: Jonas Nick +Date: Fri Oct 22 12:51:25 2021 +0000 + + Merge ElementsProject/rust-secp256k1-zkp#38: whitelist: Fix occasional overflow in unit test + + 02f16667fb6dafcba5b96b6dd2e6723e9ff80f2c whitelist: Fix occasional overflow in unit test (Steven Roose) + + Pull request description: + + ACKs for top commit: + jonasnick: + utACK 02f16667fb6dafcba5b96b6dd2e6723e9ff80f2c + + Tree-SHA512: 2eef146993378914658981f595680ae1d0616b60844dae306f53dd44cdde7042166008bd5d512dfb91583d574663bc5a78ad14048e1fcd55cc9095ae7e8cc296 + +commit 02f16667fb6dafcba5b96b6dd2e6723e9ff80f2c +Author: Steven Roose +Date: Wed Oct 20 14:36:49 2021 +0100 + + whitelist: Fix occasional overflow in unit test + +commit c8ff901516c9c299c9cc7331b15a7a14a05ced89 +Merge: ab7f6d8 e869210 +Author: Jonas Nick +Date: Wed Oct 20 13:11:42 2021 +0000 + + Merge ElementsProject/rust-secp256k1-zkp#37: Encrypt ECDSA signatures in release builds + + e8692101e357fbda0a613023e0cd3e36457d9bf4 Encrypt ECDSA adaptor signatures in release builds (Mariusz Klochowicz) + 150669b932a95105770fbd78b40f05eecbd88668 Add CI job for running tests in release mode to show failing testcases (Mariusz Klochowicz) + + Pull request description: + + ACKs for top commit: + thomaseizinger: + ACK e8692101e357fbda0a613023e0cd3e36457d9bf4 + jonasnick: + ACK e8692101e357fbda0a613023e0cd3e36457d9bf4 + + Tree-SHA512: 9d9a9b5af1f8bf8effa9bf7b96755a4b08e096febc764e7e4ba36e5e77c987a632200de018f1cf292ae156008ee2b2d03462a03537faebeaca1890e0f769b460 + +commit e8692101e357fbda0a613023e0cd3e36457d9bf4 +Author: Mariusz Klochowicz +Date: Wed Oct 20 13:16:07 2021 +1030 + + Encrypt ECDSA adaptor signatures in release builds + + Previously, the adaptor signatures were not being encrypted due to the call + being placed inside debug_assert! macro. Move the function call outside the + macro, and debug_assert! the result instead. + +commit 150669b932a95105770fbd78b40f05eecbd88668 +Author: Mariusz Klochowicz +Date: Wed Oct 20 13:06:15 2021 +1030 + + Add CI job for running tests in release mode to show failing testcases + +commit ab7f6d8f5e74fd7bf5348d4fc3593bcc1dd532ad +Merge: 0dba4c0 9144faa +Author: Steven Roose +Date: Wed Oct 13 18:47:21 2021 +0100 + + Merge pull request #36 from stevenroose/whitelist + + Add whitelist support + +commit 9144faa929dcbddc71ac692edfba7389bc47daee +Author: Steven Roose +Date: Mon Oct 4 15:30:33 2021 -0100 + + Add whitelist support + +commit 0dba4c033a929164dd21c488d2a5a9790919f65c +Merge: 0601fad f089aea +Author: Jonas Nick +Date: Thu Sep 16 13:51:39 2021 +0000 + + Merge ElementsProject/rust-secp256k1-zkp#34: Implement serde for EcdsaAdaptorSignature + + f089aea8a5f3004938782b633f0ad4b12ca186db Implement serde for EcdsaAdaptorSignature (Lucas Soriano del Pino) + 5a4f874df1c5215128685de14064326bae29cb02 Define deserialization from string and bytes generically (Lucas Soriano del Pino) + + Pull request description: + + ACKs for top commit: + thomaseizinger: + ACK f089aea8a5f3004938782b633f0ad4b12ca186db + jonasnick: + ACK f089aea8a5f3004938782b633f0ad4b12ca186db + + Tree-SHA512: c1cbfda5b2b1e54973140cda8e6a1a4aa5b12730e78ded37a4686b16cb4f337bd4bda6fcceba308965cef7c191a37667c25b23732bc0a21e6582fa70f56f3c6f + +commit 0601fadbf1267083d3ed49b1386ac54b504455b1 +Merge: 3dbfb32 e12f226 +Author: Thomas Eizinger +Date: Thu Sep 16 10:54:00 2021 +1000 + + Merge pull request #33 from luckysori/fix-feature-removal + +commit f089aea8a5f3004938782b633f0ad4b12ca186db +Author: Lucas Soriano del Pino +Date: Tue Sep 14 13:33:11 2021 +1000 + + Implement serde for EcdsaAdaptorSignature + +commit 5a4f874df1c5215128685de14064326bae29cb02 +Author: Lucas Soriano del Pino +Date: Tue Sep 14 13:25:42 2021 +1000 + + Define deserialization from string and bytes generically + + We replace a bunch of specific, redundant serde visitors with generic + ones defined in a `serde_util` private module. This pattern (and most + of the code) is copied from `rust-secp256k1`. + +commit e12f22673741ac3b486b818e8d8271b7bec84392 +Author: Lucas Soriano del Pino +Date: Tue Sep 14 10:43:12 2021 +1000 + + Use newly renamed bitcoin_hashes feature everywhere + + We forgot to rename all instances of `hashes` to `bitcoin_hashes` in + patch e5d82e728a0bcc1d81b0824e87e70efcf309b9b0. + +commit 3dbfb32c1862552931791f412a58b969d71ca0e4 +Merge: a04a953 30692dc +Author: Jonas Nick +Date: Thu Sep 9 14:10:42 2021 +0000 + + Merge ElementsProject/rust-secp256k1-zkp#31: Mention adaptor signatures in README + + 30692dc1dd57134b82d8e55e3a201e65e5f13c8c Mention adaptor signatures in README (Thomas Eizinger) + + Pull request description: + + Top commit has no ACKs. + + Tree-SHA512: a5a559fcd514966cad81004b31787de0f2df2f67b79d510c4a84e4521ab7c7ef9d1d05465c8cf7ad13b4960e1457121c2908d7226c3956814277ed0b5d50d212 + +commit a04a953dc4e24e564032415561c71ecba0a16475 +Merge: f101a0d e5d82e7 +Author: Jonas Nick +Date: Thu Sep 9 14:08:58 2021 +0000 + + Merge ElementsProject/rust-secp256k1-zkp#30: Remove explicit bitcoin_hashes dependency + + e5d82e728a0bcc1d81b0824e87e70efcf309b9b0 Rename feature to align with `rust-secp256k1` (Thomas Eizinger) + eb64059d85fe630167dd112a1fa702166378b6fa Fix formatting of CHANGELOGs (Thomas Eizinger) + 809d87154cc96051d76a889f902aa94824f56004 Drop explicit bitcoin_hashes dependency (Thomas Eizinger) + + Pull request description: + + ACKs for top commit: + jonasnick: + ACK e5d82e728a0bcc1d81b0824e87e70efcf309b9b0 + + Tree-SHA512: 0c5c31214bdd0205f902a374d9d36addec8a4bdeab6d27d35f2996dde9af48ba333d9f7d4679343e5a997e1c8cbd982e2285e561e05c31eb4ea199c7d30f1b09 + +commit 30692dc1dd57134b82d8e55e3a201e65e5f13c8c +Author: Thomas Eizinger +Date: Thu Sep 9 23:57:57 2021 +1000 + + Mention adaptor signatures in README + +commit e5d82e728a0bcc1d81b0824e87e70efcf309b9b0 +Author: Thomas Eizinger +Date: Wed Sep 8 15:39:55 2021 +1000 + + Rename feature to align with `rust-secp256k1` + +commit eb64059d85fe630167dd112a1fa702166378b6fa +Author: Thomas Eizinger +Date: Wed Sep 8 15:31:12 2021 +1000 + + Fix formatting of CHANGELOGs + +commit 809d87154cc96051d76a889f902aa94824f56004 +Author: Thomas Eizinger +Date: Wed Sep 8 15:30:19 2021 +1000 + + Drop explicit bitcoin_hashes dependency + + To align with the naming in `rust-bitcoin`, we rename the exported + module to `hashes`. + +commit f101a0d083a23e99b3fbf8ddf3b38c43487468b5 +Merge: f610090 9a31a51 +Author: Jonas Nick +Date: Wed Aug 4 19:59:54 2021 +0000 + + Merge ElementsProject/rust-secp256k1-zkp#25: Errors are swallowed in CI + + 9a31a51f07aac2198b02e2c4aed2fe1b9d1c8d7f Propogate instead of swallowing error (rishflab) + + Pull request description: + + ACKs for top commit: + jonasnick: + ACK 9a31a51f07aac2198b02e2c4aed2fe1b9d1c8d7f + + Tree-SHA512: b9013df3bf3e5af763c6bad655f3e058cb32d8e9c70499ff84e4ffcabd3305a10c1d5d3c9ce20cd3f71280c71c463295990cd4ee30bcc3709b9da0b7c67d7936 + +commit f61009027af6675dafe468726c93d63c97e1cf6b +Merge: 2d097c2 149d686 +Author: Jonas Nick +Date: Tue Aug 3 20:58:38 2021 +0000 + + Merge ElementsProject/rust-secp256k1-zkp#24: Build and test wasm on MacOS in CI + + 149d68657fa5135f0eacf10ff3d7e391a5410f2a Build and test wasm on MacOS in CI (rishflab) + + Pull request description: + + ACKs for top commit: + jonasnick: + ACK 149d68657fa5135f0eacf10ff3d7e391a5410f2a + + Tree-SHA512: a7a8f414912d2b03cc3a0a328fa33b0f0e3948694b39d5ebefb66b361ca58ec50a2909e82902ae2f1bc737d5305bc8628f0e4aab2e1681203cb23faf6dd6f506 + +commit 9a31a51f07aac2198b02e2c4aed2fe1b9d1c8d7f +Author: rishflab +Date: Wed Jul 21 12:57:41 2021 +1000 + + Propogate instead of swallowing error + + Failing commands were being ignored in CI. This is not good. + +commit 2d097c2936bdc470f56379630946b7ef2e734bab +Merge: 5e21a5f 687bb98 +Author: Jonas Nick +Date: Tue Jul 20 12:28:29 2021 +0000 + + Merge ElementsProject/rust-secp256k1-zkp#23: Implement AsRef for EcdsaAdaptorSignature + + 687bb98f3db50617f18fbc366a83504b65e983d1 Implement AsRef for EcdsaAdaptorSignature (Tibo-lg) + + Pull request description: + + ACKs for top commit: + jonasnick: + ACK 687bb98f3db50617f18fbc366a83504b65e983d1 + + Tree-SHA512: 629ccc559cccd1e483299fd6e1cd61b9ff4c4de3fe83871dc7a1840a99d323acf6770845095d9340d575d90649d241ea8818a307d5aa6730d0a7ce1eef0760c7 + +commit 149d68657fa5135f0eacf10ff3d7e391a5410f2a +Author: rishflab +Date: Thu Jul 15 20:05:34 2021 +1000 + + Build and test wasm on MacOS in CI + + The default clang installation provided in MacOS does not work. We fixed + this locally by installing clang via llvm through brew and setting the + CC and AR env variables to point at the new clang installation.The CI + MacOS image seems to already have llvm installed so we did not need to + install llvm on the CI. + + See https://github.com/rust-bitcoin/rust-secp256k1/pull/254 for more + details + +commit 687bb98f3db50617f18fbc366a83504b65e983d1 +Author: Tibo-lg +Date: Mon Jul 12 14:47:38 2021 +0900 + + Implement AsRef for EcdsaAdaptorSignature + +commit 5e21a5f1e3de678a21bc6173b3d5cce031b41fe7 +Merge: 54b53b0 a46af47 +Author: Andrew Poelstra +Date: Tue May 4 21:41:17 2021 +0000 + + Merge pull request #22 from sanket1729/release + + Update secp256k1-zkp sys crate + +commit a46af47aacacbec9e6cf289fc62b7ced7b5d61e0 (origin/release, release) +Author: sanket1729 +Date: Tue May 4 13:45:13 2021 -0700 + + Update sys crate to 0.4.0 + +commit bff9d6603583ed574ceaadfb1b5d7c2f94827e96 +Author: sanket1729 +Date: Tue May 4 13:40:55 2021 -0700 + + Update secp256k1-zkp + + Update to rev f3708a1ecb445b1b05a0f8fcd1da6a88f83d89c4 on master + +commit 54b53b07658e039ae5dfd0893692691801209e87 +Merge: 93014cb 86e25ae +Author: Andrew Poelstra +Date: Tue May 4 17:02:41 2021 +0000 + + Merge pull request #21 from sanket1729/release-0.4.0 + + Release 0.4.0 + +commit 86e25ae2cc4114732e20584c744877044f7331de (tag: 0.4.0, origin/release-0.4.0) +Author: sanket1729 +Date: Tue May 4 09:41:11 2021 -0700 + + Release 0.4.0 + +commit 93014cb6833864ead1f2ac2f4f11bc6f3e986ac7 +Merge: 1a646e7 d670e5d +Author: Andrew Poelstra +Date: Tue May 4 16:16:30 2021 +0000 + + Merge pull request #20 from sanket1729/fmt + + cargo fmt + +commit d670e5d4553abed05d24808eae44f40a36572fcc (origin/fmt, fmt) +Author: sanket1729 +Date: Fri Apr 30 03:13:02 2021 -0700 + + enforce cargo fmt + +commit 2db471dbf335fcb9057da303487ac611d76659b6 +Author: sanket1729 +Date: Fri Apr 30 03:05:19 2021 -0700 + + cargo fmt + +commit 1a646e7f3dae4b7ba5ebc5f5b223b1cca39fe52f +Merge: 7c2fe0f 5a68fb3 +Author: Jonas Nick +Date: Fri Apr 30 17:30:49 2021 +0000 + + Merge #15: Use aux rand for ecdsa adaptor encrypt + + 5a68fb38fc3a4f574303ddf4b802fe4efe9a867e Use aux rand for ecdsa adaptor encrypt (Tibo-lg) + + Pull request description: + + ACKs for top commit: + thomaseizinger: + utACK 5a68fb38fc3a4f574303ddf4b802fe4efe9a867e + jonasnick: + ACK 5a68fb38fc3a4f574303ddf4b802fe4efe9a867e + + Tree-SHA512: 5163f066e6c0fefa5bfdd9fb6b5d74c0820b114d8945f1d46c04b4f2def10155c93982afaffe11aceb3220af582efc8dcdcae4efac5964a833f392d98d9fb7cf + +commit 5a68fb38fc3a4f574303ddf4b802fe4efe9a867e +Author: Tibo-lg +Date: Fri Apr 23 14:30:23 2021 +0900 + + Use aux rand for ecdsa adaptor encrypt + +commit 7c2fe0f40d9419c23e1b08efd9e89d4d04d11365 (pset_depends, gmt) +Merge: a3f0611 4d568e4 +Author: Andrew Poelstra +Date: Tue Apr 27 21:18:48 2021 +0000 + + Merge pull request #18 from sanket1729/pset_depends + + Some utilities required for PSET + +commit 4d568e454f251c1d3a41dd3134a92aaaaa0a4b75 (origin/pset_depends) +Author: sanket1729 +Date: Mon Apr 26 17:19:15 2021 -0700 + + Add surjection proof len + +commit b2287837d9f3e621fd273a7a6963b8665814dff6 +Author: sanket1729 +Date: Mon Apr 26 14:12:52 2021 -0700 + + Add derives for surjectionproofs + +commit a3f06115f2376da9b77bdac8278527d0f148672d +Merge: 2700187 0fd14bc +Author: Andrew Poelstra +Date: Tue Apr 27 21:05:11 2021 +0000 + + Merge pull request #16 from sanket1729/zero_blinds + + Replace `SecretKey` based tweak methods with a new `Tweak type` + +commit 0fd14bc5f123c7d9ef4d9e957b9125c982f4db8b (origin/zero_blinds, zero_blinds) +Author: sanket1729 +Date: Sun Apr 25 23:16:54 2021 -0700 + + Delegate implementations of unblinded functions using zero tweaks + +commit 53d1947d91731ba9b8371900b5a1fd26ee064c0d +Author: sanket1729 +Date: Sun Apr 25 17:13:05 2021 -0700 + + Add Tweak type + + Assuming we care about constant time, we only care about constant time properties for valid keys. With the above code, we guarantee the constant time property for all valid tweaks but zero. + Although this is not ideal, this suffices in elements use-case where zero is usually as explicit commitment. + + Checking this upfront might however leak information about valid Tweaks (based on the position of first 1). I can however move the code afterward with a short circuit return the valid non-zero tweak in the unsafe block. + +commit 27001871247a40cf0c0ed8ca03649ba9401bdf0e +Merge: 34455c4 e580d30 +Author: Andrew Poelstra +Date: Thu Apr 22 17:16:56 2021 +0000 + + Merge pull request #14 from sanket1729/zero_blinds + + Add support for creating unblinded generators/commitments + +commit e580d30ce7c14170fd5eeb3070195e8ef3576290 +Author: sanket1729 +Date: Tue Apr 20 21:45:29 2021 -0700 + + Add support for creating unblinded generators/commitments + +commit 34455c4255a81fa172264e1d16dc62d924ffe641 (tag: 0.3.0) +Merge: 8baed9d 5b27507 +Author: Jonas Nick +Date: Tue Apr 20 12:14:08 2021 +0000 + + Merge #13: Prepare for minor release 0.3.0 + + 5b275077ca98a39a2ef8a0dc3305ef20b4427272 Version bump 0.2.1 -> 0.3.0 (Jonas Nick) + 68e3bba367441af36b25441565d015a7f6254eb4 secp256k1-zkp-sys: bump 0.2.0 -> 0.3.0 (Jonas Nick) + + Pull request description: + + Top commit has no ACKs. + + Tree-SHA512: 6005c977a94a576d8995ced7ac0c20afe01c7eda3ee14d48ce5b2d16e05b84c93c6673b0932f7eb404a200b85edff287b7847da30a0292efb9bfdc45ab82a44f + +commit 5b275077ca98a39a2ef8a0dc3305ef20b4427272 +Author: Jonas Nick +Date: Mon Apr 19 19:27:08 2021 +0000 + + Version bump 0.2.1 -> 0.3.0 + + Due to ECDSA adaptor signature support. + +commit 68e3bba367441af36b25441565d015a7f6254eb4 +Author: Jonas Nick +Date: Mon Apr 19 19:26:09 2021 +0000 + + secp256k1-zkp-sys: bump 0.2.0 -> 0.3.0 + + Due to ECDSA adaptor signature support. The symbols were updated by + running the following command: + ./vendor-libsecp.sh ./depend 0_3_0 f3708a1ecb445b1b05a0f8fcd1da6a88f83d89c4 + +commit 8baed9dc80321666f67c07c8eb64ca5a62927efe +Merge: a7409c9 309bbb4 +Author: Jonas Nick +Date: Mon Apr 19 17:03:45 2021 +0000 + + Merge #11: Add ecdsa adaptor + + 309bbb4f452e7b129d08c060d06054ec0c5740d5 Add ecdsa adaptor support in rust-secp256k1-zkp (Tibo-lg) + 747347da7a5fd951046aa95ac4e2bd923f3d1c4f Add ecdsa adaptor support in sys (Tibo-lg) + f9b8dc7a19b4cfa897a7776c9f3d192ebe5dd236 Update depend folder to latest (Tibo-lg) + + Pull request description: + + ACKs for top commit: + thomaseizinger: + tACK 309bbb4 + jonasnick: + ACK 309bbb4f452e7b129d08c060d06054ec0c5740d5 + + Tree-SHA512: f5ba386f93e87f65dc4f9533deb839aaae7595ea27f90137eae29f6b4e6a608839eb6c0cedd054632ed277b561c60d602234615f5382f85c8be326f80eec9557 + +commit 309bbb4f452e7b129d08c060d06054ec0c5740d5 +Author: Tibo-lg +Date: Wed Apr 7 17:11:00 2021 +0900 + + Add ecdsa adaptor support in rust-secp256k1-zkp + +commit 747347da7a5fd951046aa95ac4e2bd923f3d1c4f +Author: Tibo-lg +Date: Wed Apr 7 17:10:29 2021 +0900 + + Add ecdsa adaptor support in sys + +commit f9b8dc7a19b4cfa897a7776c9f3d192ebe5dd236 +Author: Tibo-lg +Date: Wed Apr 7 17:10:07 2021 +0900 + + Update depend folder to latest + +commit a7409c9bcfe121c1c0ad5bc7661a6b8cf8da5b3c (tag: 0.2.1) +Merge: 033c5db 8fa7f5b +Author: Andrew Poelstra +Date: Tue Apr 13 22:09:34 2021 +0000 + + Merge pull request #12 from sanket1729/2021-04-minor-release + + Prepare for minor release 0.2.1 + +commit 8fa7f5b92ee4eb85cb8602ecb6bbd58af485d6ca (origin/2021-04-minor-release, 2021-04-minor-release) +Author: sanket1729 +Date: Tue Apr 13 15:00:17 2021 -0700 + + Minor release 0.2.1 + +commit 033c5db1c2188048dd1ce7f2a270fa767baf9784 +Merge: 5b9601f 104d5a8 +Author: Thomas Eizinger +Date: Mon Apr 12 13:57:03 2021 +1000 + + Merge pull request #10 from luckysori/fix-pedersen-deser + + Fix Pedersen Commitment deserialization + +commit 104d5a800f8ea422c121ecaff6f2073600f8dced +Author: Lucas Soriano del Pino +Date: Tue Apr 6 09:25:44 2021 +1000 + + Fix PedersenCommitment deserialization bug + + We serialise Pedersen commitments as 33-byte arrays (and 66-character + hex strings), yet we were deserializing them as 64-byte arrays (or + 128-character hex strings). + + This bug was coming from the implementation of `FromStr`. We add a + test, to ensure that we also uphold the invariant that `Display` and + `FromStr` are round-trippable. + +commit 5b9601f1a082e9af695b6f8a36e544c6344d30ed (tag: 0.2.0) +Merge: dd88a52 1148ea4 +Author: Jonas Nick +Date: Tue Jan 12 12:15:40 2021 +0000 + + Merge #9: Prepare for new releases after replacing master + + 1148ea4fdebdf3b591a4224cc950b73422ea0922 Update secp256k1-zkp to 0.2.0 (Jonas Nick) + 22a4958872748a47540f40beae507192c92a8838 Update secp256k1-zkp-sys to 0.2.0 (Jonas Nick) + 9fd60f799a21f456bf0ab4517aed2e2349c01f3d Let metadata and CI badge point to the correct repo (Jonas Nick) + + Pull request description: + + ACKs for top commit: + thomaseizinger: + tACK 1148ea4 + + Tree-SHA512: 5aeca2f287186cccb9c4c6bfd648045541aa6320107dfac8f9015b8be84ea2890dbcfc49b22e6abd2e2c9a991eeaf54521a3dda7518454a22b5b65151b394c9e + +commit 1148ea4fdebdf3b591a4224cc950b73422ea0922 +Author: Jonas Nick +Date: Wed Jan 6 18:13:54 2021 +0000 + + Update secp256k1-zkp to 0.2.0 + +commit 22a4958872748a47540f40beae507192c92a8838 +Author: Jonas Nick +Date: Wed Jan 6 17:28:35 2021 +0000 + + Update secp256k1-zkp-sys to 0.2.0 + + Also revendor depends/secp256k1 without changing the HEAD revision to update the + symbols to 0.2.0. + +commit 9fd60f799a21f456bf0ab4517aed2e2349c01f3d +Author: Jonas Nick +Date: Wed Jan 6 17:09:17 2021 +0000 + + Let metadata and CI badge point to the correct repo + +commit dd88a52d16caaaabdab20a54aeeb01ef0d146878 +Merge: 73526ee ac1100e +Author: Thomas Eizinger +Date: Wed Jan 6 14:01:34 2021 +1100 + + Merge pull request #10 from jonasnick/fixes + +commit ac1100eff32ca4de3be052ab20394419ecd146ae +Author: Jonas Nick +Date: Tue Jan 5 22:12:34 2021 +0000 + + Fix Makefile test target + + Otherwise, no tests are being run. + +commit 1f2342b38bc6095577fce069c35f0ac13e05240a +Author: Jonas Nick +Date: Tue Jan 5 18:45:08 2021 +0000 + + rangeproof: improve testing of rewound message + + There was no upstream bug, but the documentation was confusing and has since + been improved. + +commit fb017c5de63b161a40509d1cd4675c1958da2770 +Author: Jonas Nick +Date: Tue Jan 5 18:42:57 2021 +0000 + + sys: remove endomorphism feature metadata + +commit aeef3843d036137caf8869b2477df35a37ca76d7 +Author: Jonas Nick +Date: Tue Jan 5 18:52:07 2021 +0000 + + sys: disable unused modules recovery and ECDH in C build + +commit 73526ee20b8448ab5eaa36eee656cf6bc9e4a96a +Merge: 7397ca9 2723ec7 +Author: Thomas Eizinger +Date: Mon Jan 4 14:30:31 2021 +1100 + + Merge pull request #9 from comit-network/update-upstream-dependency + +commit 2723ec73d0e6004e57408e89d9f365aa3041ffa8 +Author: Thomas Eizinger +Date: Mon Jan 4 11:41:34 2021 +1100 + + Add missing documentation + +commit a9df02354264182af85c484d4aa4173afedf045f +Author: Thomas Eizinger +Date: Mon Jan 4 11:36:01 2021 +1100 + + Silence clippy about unused code + + We only use this within code that is feature-flagged behind `rand`. + +commit bcb1d827c3952ef47db300adeefd4ef7028debea +Author: Thomas Eizinger +Date: Mon Jan 4 11:34:47 2021 +1100 + + Remove unnecessary transmute + + Thanks clippy! + +commit 309bec5f598ad28f4b57c3dd25c7f6a99629d03b +Author: Thomas Eizinger +Date: Mon Jan 4 11:34:05 2021 +1100 + + Fix clippy warning about useless reference + +commit d8e9a61819d5e38adc6fc8da94110fe38739e121 +Author: Thomas Eizinger +Date: Mon Jan 4 11:33:38 2021 +1100 + + Fix clippy error about derived Hash and manual PartialEq + +commit 049a53ea091ccad8db0c0865859f77bca0fd7e79 +Author: Thomas Eizinger +Date: Mon Jan 4 11:13:06 2021 +1100 + + Update to rust-secp256k1 0.20 + + In this version, the feature for fuzzing and external symbols have + been replaced by conditional compilation statements. As such, the + examples no longer work because we compile rustdoc with the dummy + context for fuzzing as well. + + Those examples are for the upstream library anyway so we can remove + them here. + +commit 7397ca919b0097c73c808c7cb02f15f7716a4b0d +Merge: 1ee2d80 583c6ce +Author: Thomas Eizinger +Date: Mon Dec 21 16:05:03 2020 +1100 + + Merge pull request #8 from bonomat/macos + +commit 583c6ceaf7735541dd8e1239bd96aaf8317fbb2c +Author: Philipp Hoenisch +Date: Mon Dec 21 15:39:31 2020 +1100 + + Apply scratch_impl.h.patch to remove malloc functions + +commit a6b72820a7f342aaffb054533c24ca6001d7c749 +Author: Philipp Hoenisch +Date: Mon Dec 21 14:29:57 2020 +1100 + + Build matrix added: build tests on macos + +commit 1ee2d80363f9971d4988ebe2e33bb30113b816c3 +Merge: 39776cc 942c3f3 +Author: Thomas Eizinger +Date: Thu Dec 17 14:48:15 2020 +1100 + + Merge pull request #7 from comit-network/remove-malloc-free-calls + +commit 942c3f325e1bed6df22dfc698b80f7f9e3054fc1 +Author: Thomas Eizinger +Date: Thu Dec 17 14:40:53 2020 +1100 + + Run all test in WASM + +commit 0d0df623ca87cfe88e53e5ecce214008f03b2cb5 +Author: Thomas Eizinger +Date: Thu Dec 17 14:17:10 2020 +1100 + + Re-run vendor script to remove surjection proof API with allocations + +commit 1fabed6e350d418f52bb833a2a87c54be2a08657 +Author: Thomas Eizinger +Date: Thu Dec 17 14:14:19 2020 +1100 + + Patch surjection/main_impl.h to remove code that allocates + +commit 39776ccbe34149a49c8d3c3b035ce1a5fcf6789e +Merge: b0ee41a e0a4d2c +Author: Thomas Eizinger +Date: Thu Dec 17 11:57:31 2020 +1100 + + Merge pull request #6 from comit-network/remove-wasm-constants + +commit e0a4d2c1a108dfe4af43a31d2f291f909ea2de0e +Author: Thomas Eizinger +Date: Thu Dec 17 11:54:10 2020 +1100 + + Optimize CI by not compiling wasm-pack from source + +commit 7055f4f463c61d4565e4208cf1a26b600832df09 +Author: Thomas Eizinger +Date: Thu Dec 17 11:44:25 2020 +1100 + + Remove WASM constants from stdio.h + + We are depending on rust-secp256k1 which already defines these + constants. Redefining them here leads to duplicated symbols at + link time. + +commit b0ee41a6fdb023dacce1d2f82ab22d5fb9d0d146 +Merge: b2a7d2a dc3099c +Author: Thomas Eizinger +Date: Thu Dec 17 11:23:16 2020 +1100 + + Merge pull request #5 from comit-network/actually-compile-to-wasm + +commit dc3099ce8482e504523b6077b298feb3944f1b7f +Author: Thomas Eizinger +Date: Thu Dec 17 11:14:11 2020 +1100 + + Add CI check for compiling to WASM + +commit cd39b15b9ffa68515247f685752d4c0bae98406f +Author: Thomas Eizinger +Date: Thu Dec 17 11:10:26 2020 +1100 + + Run vendor script to remove include of "assert.h" + +commit 55ad6ae3b2695e132b6eb13c98c0d6da9b4a9638 +Author: Thomas Eizinger +Date: Thu Dec 17 11:09:32 2020 +1100 + + Include patches to remove "assert.h" include + + These assertions are not actually used but prevent us from compiling + to WASM. + +commit 3483b2edc5187fff47ea1ca3d8b1f19311626b4d +Author: Thomas Eizinger +Date: Thu Dec 17 11:08:10 2020 +1100 + + Guard header files from being included several times + + For some reason, this is required when targeting wasm but is fine + on x86 (maybe a difference between clang and gcc?). + + In any case, it doesn't hurt to #ifdef guard these headers. + +commit 65a4e00a8fba944cb540b846c351db211ff9347e +Author: Thomas Eizinger +Date: Thu Dec 17 11:07:12 2020 +1100 + + Enable external callbacks + + Without external callbacks, the C code uses `printf` which is not + defined in a WASM environment. + +commit b2a7d2af2fe2d15c23274bcba767bad7f5a3d4a7 +Author: Thomas Eizinger +Date: Tue Dec 15 12:11:54 2020 +1100 + + Update crate metadata and documentation + +commit 844a7a276e8e929a82d4465cdd9da12c94017dca +Author: Thomas Eizinger +Date: Tue Dec 15 11:29:21 2020 +1100 + + Disable external callbacks + + Enabling this causes the address sanitizer to fail because we are not + declaring the callbacks because we are reusing the context from + libsecp256k1. + +commit 792da9297130295a8f01110806943eb79450dc96 +Author: Thomas Eizinger +Date: Tue Dec 15 09:57:09 2020 +1100 + + Properly forward all features + + Unfortunately, we have to introduce additional features because + cargo doesn't allow us to forward features that are created through + optional dependencies. + +commit 5948996e486a8a7731e1d7bdf26deb38d81d6a13 +Author: Thomas Eizinger +Date: Tue Dec 15 09:33:17 2020 +1100 + + Unconditionally enable the `endomorphism` feature + + In order to re-use the context struct from libsecp256k1, we need to + unconditionally enable the endomorphism feature otherwise the structs + don't have the same layout. + +commit 795a59d40b6529c7ade882ed9f1a72273f1210fe +Author: Thomas Eizinger +Date: Mon Dec 14 16:12:55 2020 +1100 + + Add zkp bindings + +commit 1317a04eb5692ec64f6faa54d7b1b9c9d4962531 +Author: Thomas Eizinger +Date: Tue Dec 15 09:59:31 2020 +1100 + + Add dprint configuration file + +commit 60adfd29bb3dcd2638fd306bc1779e2781941eb2 +Author: Thomas Eizinger +Date: Mon Dec 14 16:11:27 2020 +1100 + + Rename crate and replace content with re-export from upstream + +commit 008bfb09b6af695071dcc8c39560a018fa804f2a +Author: Thomas Eizinger +Date: Mon Dec 14 15:22:09 2020 +1100 + + Vendor latest HEAD of secp256k1-zkp + +commit 619336016bb56a1e6f05dbc8a82c3cbb94f75693 +Author: Thomas Eizinger +Date: Mon Dec 14 15:14:26 2020 +1100 + + Update vendor script and link references to secp256k1zkp + +commit f61b2d0eec939ac5ab042a97aa08ea868a57cb17 +Author: Thomas Eizinger +Date: Mon Dec 14 15:07:07 2020 +1100 + + Rename secp256k1 crate to secp256k1-zkp + +commit a65ffe625e302f9681a769ed8e55142435013e94 +Author: Thomas Eizinger +Date: Mon Dec 14 15:05:09 2020 +1100 + + Rename secp256k1-sys directory to secp256k1-zkp-sys + +commit bc16bc5df875e00ca7f41be7b35c1a0331a4b937 +Author: Thomas Eizinger +Date: Mon Dec 14 15:00:59 2020 +1100 + + Delete examples and no_std_test + + We don't need those in our fork. + +commit 9a8be680beb6758e514b38e096e1387d6622433a +Author: Thomas Eizinger +Date: Tue Dec 15 10:42:51 2020 +1100 + + Initialize repository + + This commit represents the state of https://github.com/rust-bitcoin/rust-secp256k1 + at commit 11e9641d21c861ef33272bdde7bf01bec73e4e7d. + + We initialize this repository like this for convenience and easy of + review. + + We don't want the `-zkp` variant of the bindings to be a literaly fork + because we are going to re-export most of what upstream defines. diff --git a/src/zkp/mod.rs b/src/zkp/mod.rs index d16ad2aa..70860c33 100644 --- a/src/zkp/mod.rs +++ b/src/zkp/mod.rs @@ -1,5 +1,10 @@ mod ecdsa_adaptor; mod generator; +#[cfg(feature = "std")] +mod musig; +#[cfg(feature = "std")] +pub use self::musig::new_musig_nonce_pair; + #[cfg(feature = "std")] mod pedersen; #[cfg(feature = "std")] @@ -12,6 +17,8 @@ mod whitelist; pub use self::ecdsa_adaptor::*; pub use self::generator::*; #[cfg(feature = "std")] +pub use self::musig::*; +#[cfg(feature = "std")] pub use self::pedersen::*; #[cfg(feature = "std")] pub use self::rangeproof::*; diff --git a/src/zkp/musig.rs b/src/zkp/musig.rs new file mode 100644 index 00000000..13dc70a5 --- /dev/null +++ b/src/zkp/musig.rs @@ -0,0 +1,1762 @@ +use core::fmt; +///! This module implements high-level Rust bindings for a Schnorr-based +///! multi-signature scheme called MuSig2 (https://eprint.iacr.org/2020/1261). +///! It is compatible with bip-schnorr. +///! +///! The module also supports adaptor signatures as described in +///! https://github.com/ElementsProject/scriptless-scripts/pull/24 +///! +///! The documentation in this include file is for reference and may not be sufficient +///! for users to begin using the library. A full description of the C API usage can be found +///! in [C-musig.md](secp256k1-sys/depend/secp256k1/src/modules/musig/musig.md), and Rust API +///! usage can be found in [Rust-musig.md](USAGE.md). +use {core, std}; + +use ffi::{self, CPtr}; +use secp256k1::Parity; +use ZERO_TWEAK; +use {schnorr, KeyPair, XOnlyPublicKey}; +use {Message, PublicKey, Secp256k1, SecretKey, Tweak}; +use {Signing, Verification}; + +/// Data structure containing auxiliary data generated in `pubkey_agg` and +/// required for `session_*_init`. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct MusigKeyAggCache(ffi::MusigKeyaggCache, XOnlyPublicKey); + +impl CPtr for MusigKeyAggCache { + type Target = ffi::MusigKeyaggCache; + + fn as_c_ptr(&self) -> *const Self::Target { + self.as_ptr() + } + + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { + self.as_mut_ptr() + } +} + +impl MusigKeyAggCache { + /// Create a new [`MusigKeyAggCache`] by supplying a list of PublicKeys used in the session + /// + /// Computes a combined public key and the hash of the given public keys. + /// + /// Different orders of `pubkeys` result in different `agg_pk`s. + /// + /// The pubkeys can be sorted lexicographically before combining with which + /// ensures the same resulting `agg_pk` for the same multiset of pubkeys. + /// This is useful to do before aggregating pubkeys, such that the order of pubkeys + /// does not affect the combined public key. + /// + /// # Returns + /// + /// A pair ([`MusigKeyAggCache`], [`XOnlyPublicKey`]) where the first element is the `key_agg_cache`. + /// This can be used to [`MusigKeyAggCache::nonce_gen`] and [`MusigKeyAggCache::nonce_process`]. The second + /// element is the resultant Musig aggregated public key. + /// + /// #Args: + /// + /// * `secp` - Secp256k1 context object initialized for verification + /// * `pubkeys` - Input array of public keys to combine. The order is important; a + /// different order will result in a different combined public key + /// + /// Example: + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{MusigKeyAggCache, Secp256k1, SecretKey, KeyPair, XOnlyPublicKey}; + /// let secp = Secp256k1::new(); + /// let keypair1 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key1 = XOnlyPublicKey::from_keypair(&keypair1); + /// let keypair2 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key2 = XOnlyPublicKey::from_keypair(&keypair2); + /// + /// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key1, pub_key2]); + /// let _agg_pk = key_agg_cache.agg_pk(); + /// # } + /// ``` + pub fn new(secp: &Secp256k1, pubkeys: &[XOnlyPublicKey]) -> Self { + let cx = *secp.ctx(); + let xonly_ptrs = pubkeys.iter().map(|k| k.as_ptr()).collect::>(); + let mut key_agg_cache = ffi::MusigKeyaggCache::new(); + + unsafe { + let mut agg_pk = XOnlyPublicKey::from(ffi::XOnlyPublicKey::new()); + if ffi::secp256k1_musig_pubkey_agg( + cx, + // FIXME: passing null pointer to ScratchSpace uses less efficient algorithm + // Need scratch_space_{create,destroy} exposed in public C API to safely handle + // memory + core::ptr::null_mut(), + agg_pk.as_mut_ptr(), + &mut key_agg_cache, + xonly_ptrs.as_ptr() as *const *const _, + xonly_ptrs.len(), + ) == 0 + { + // Returns 0 only if the keys are malformed that never happens in safe rust type system. + unreachable!("Invalid XOnlyPublicKey in input pubkeys") + } else { + MusigKeyAggCache(key_agg_cache, agg_pk) + } + } + } + + /// Obtains the aggregate public key for this [`MusigKeyAggCache`] + pub fn agg_pk(&self) -> XOnlyPublicKey { + self.1 + } + + /// Apply ordinary "EC" tweaking to a public key in a [`MusigKeyAggCache`] by + /// adding the generator multiplied with `tweak32` to it. Returns the tweaked [`PublicKey`]. + /// This is useful for deriving child keys from an aggregate public key via BIP32. + /// + /// This function is required if you want to _sign_ for a tweaked aggregate key. + /// On the other hand, if you are only computing a public key, but not intending + /// to create a signature for it, use [`secp256k1::PublicKey::add_exp_assign`] + /// instead. + /// + /// # Arguments: + /// + /// * `secp` : [`Secp256k1`] context object initialized for verification + /// * `tweak`: tweak of type [`SecretKey`] with which to tweak the aggregated key + /// + /// # Errors: + /// + /// If resulting public key would be invalid (only when the tweak is the negation of the corresponding + /// secret key). For uniformly random 32-byte arrays(for example, in BIP 32 derivation) the chance of + /// being invalid is negligible (around 1 in 2^128). + /// + /// Example: + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{MusigKeyAggCache, Secp256k1, SecretKey, KeyPair, XOnlyPublicKey}; + /// let secp = Secp256k1::new(); + /// let keypair1 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key1 = XOnlyPublicKey::from_keypair(&keypair1); + /// let keypair2 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key2 = XOnlyPublicKey::from_keypair(&keypair2); + /// + /// let mut key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key1, pub_key2]); + /// + /// let tweak = SecretKey::from_slice(&[2; 32]).unwrap(); + /// let _tweaked_key = key_agg_cache.pubkey_ec_tweak_add(&secp, tweak).unwrap(); + /// # } + /// ``` + pub fn pubkey_ec_tweak_add( + &mut self, + secp: &Secp256k1, + tweak: SecretKey, + ) -> Result { + let cx = *secp.ctx(); + unsafe { + let mut out = PublicKey::from(ffi::PublicKey::new()); + if ffi::secp256k1_musig_pubkey_ec_tweak_add( + cx, + out.as_mut_ptr(), + self.as_mut_ptr(), + tweak.as_ptr(), + ) == 0 + { + Err(MusigTweakErr::InvalidTweak) + } else { + Ok(out) + } + } + } + + /// Apply "x-only" tweaking to a public key in a [`MusigKeyAggCache`] by + /// adding the generator multiplied with `tweak32` to it. Returns the tweaked [`XOnlyPublicKey`]. + /// This is useful in creating taproot outputs. + /// + /// This function is required if you want to _sign_ for a tweaked aggregate key. + /// On the other hand, if you are only computing a public key, but not intending + /// to create a signature for it, you can just use [`XOnlyPublicKey::tweak_add_assign`] + /// + /// # Arguments: + /// + /// * `secp` : [`Secp256k1`] context object initialized for verification + /// * `tweak`: tweak of type [`SecretKey`] with which to tweak the aggregated key + /// + /// # Errors: + /// + /// If resulting public key would be invalid (only when the tweak is the negation of the corresponding + /// secret key). For uniformly random 32-byte arrays(for example, in BIP341 taproot tweaks) the chance of + /// being invalid is negligible (around 1 in 2^128) + /// + /// Example: + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{MusigKeyAggCache, Secp256k1, SecretKey, KeyPair, XOnlyPublicKey}; + /// let secp = Secp256k1::new(); + /// let keypair1 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key1 = XOnlyPublicKey::from_keypair(&keypair1); + /// let keypair2 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key2 = XOnlyPublicKey::from_keypair(&keypair2); + /// + /// let mut key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key1, pub_key2]); + /// + /// let tweak = SecretKey::from_slice(&[2; 32]).unwrap(); + /// let _x_only_key_tweaked = key_agg_cache.pubkey_xonly_tweak_add(&secp, tweak).unwrap(); + /// # } + /// ``` + pub fn pubkey_xonly_tweak_add( + &mut self, + secp: &Secp256k1, + tweak: SecretKey, + ) -> Result { + let cx = *secp.ctx(); + unsafe { + let mut out = XOnlyPublicKey::from(ffi::XOnlyPublicKey::new()); + if ffi::secp256k1_musig_pubkey_xonly_tweak_add( + cx, + out.as_mut_ptr(), + self.as_mut_ptr(), + tweak.as_ptr(), + ) == 0 + { + Err(MusigTweakErr::InvalidTweak) + } else { + Ok(out) + } + } + } + + /// Starts a signing session by generating a nonce + /// + /// This function outputs a secret nonce that will be required for signing and a + /// corresponding public nonce that is intended to be sent to other signers. + /// + /// MuSig differs from regular Schnorr signing in that implementers _must_ take + /// special care to not reuse a nonce. If you cannot provide a `sec_key`, `session_id` + /// UNIFORMLY RANDOM AND KEPT SECRET (even from other signers). + /// Refer to libsecp256k1-zkp documentation for additional considerations. + /// + /// Musig2 nonces can be precomputed without knowing the aggregate public key, the message to sign. + /// However, for maximal mis-use resistance, this API requires user to have already + /// have [`SecretKey`], [`Message`] and [`MusigKeyAggCache`]. See the `new_nonce_pair` method + /// that allows generating [`MusigSecNonce`] and [`MusigPubNonce`] with only the `session_id` field. + /// + /// Remember that nonce reuse will immediately leak the secret key! + /// + /// # Returns: + /// + /// A pair of ([`MusigSecNonce`], [`MusigPubNonce`]) that can be later used signing and aggregation + /// + /// # Arguments: + /// + /// * `secp` : [`Secp256k1`] context object initialized for signing + /// * `session_id`: Uniform random identifier for this session. This _must_ never be re-used. + /// If this is not sampled uniformly at random, this can leak the private key + /// * `sec_key`: [`SecretKey`] that we will use to sign to a create partial signature. + /// * `msg`: [`Message`] that will be signed later on. + /// * `extra_rand`: Additional randomness for mis-use resistance + /// + /// /// # Errors: + /// + /// * `ZeroSession`: if the `session_id` is supplied is all zeros. + /// + /// Example: + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{Message, KeyPair, MusigKeyAggCache, XOnlyPublicKey, Secp256k1, SecretKey}; + /// let secp = Secp256k1::new(); + /// let keypair1 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key1 = XOnlyPublicKey::from_keypair(&keypair1); + /// let keypair2 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key2 = XOnlyPublicKey::from_keypair(&keypair2); + /// + /// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key1, pub_key2]); + /// // The session id must be sampled at random. Read documentation for more details. + /// let mut session_id = [0; 32]; + /// thread_rng().fill_bytes(&mut session_id); + /// + /// // Generate the nonce for party with `keypair1`. + /// let sec_key = SecretKey::from_keypair(&keypair1); + /// let msg = Message::from_slice(&[3; 32]).unwrap(); + /// + /// // Provide the current time for mis-use resistance + /// let extra_rand : Option<[u8; 32]> = None; + /// let (_sec_nonce, _pub_nonce) = key_agg_cache.nonce_gen(&secp, session_id, sec_key, msg, None) + /// .expect("non zero session id"); + /// # } + /// ``` + pub fn nonce_gen( + &self, + secp: &Secp256k1, + session_id: [u8; 32], + sec_key: SecretKey, + msg: Message, + extra_rand: Option<[u8; 32]>, + ) -> Result<(MusigSecNonce, MusigPubNonce), MusigNonceGenError> { + new_musig_nonce_pair( + secp, + session_id, + Some(&self), + Some(sec_key), + Some(msg), + extra_rand, + ) + } + + /// Get a const pointer to the inner MusigKeyAggCache + pub fn as_ptr(&self) -> *const ffi::MusigKeyaggCache { + &self.0 + } + + /// Get a mut pointer to the inner MusigKeyAggCache + pub fn as_mut_ptr(&mut self) -> *mut ffi::MusigKeyaggCache { + &mut self.0 + } +} + +/// Musig tweaking related errors. +#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub enum MusigTweakErr { + /// Invalid tweak (tweak is the negation of the corresponding secret key). + InvalidTweak, +} + +#[cfg(feature = "std")] +impl std::error::Error for MusigTweakErr {} + +impl fmt::Display for MusigTweakErr { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match self { + MusigTweakErr::InvalidTweak => write!( + f, + "Invalid Tweak: This only happens when + tweak is negation of secret key" + ), + } + } +} + +/// Musig tweaking related errors. +#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub enum MusigNonceGenError { + /// Invalid tweak (tweak is the negation of the corresponding secret key). + ZeroSession, +} + +#[cfg(feature = "std")] +impl std::error::Error for MusigNonceGenError {} + +impl fmt::Display for MusigNonceGenError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match self { + MusigNonceGenError::ZeroSession => write!(f, "Supplied a zero session id"), + } + } +} +/// Starts a signing session by generating a nonce. Use [`MusigKeyAggCache::nonce_gen`] whenever +/// possible. This API provides full flexibility in providing +/// +/// This function outputs a secret nonce that will be required for signing and a +/// corresponding public nonce that is intended to be sent to other signers. +/// +/// MuSig differs from regular Schnorr signing in that implementers _must_ take +/// special care to not reuse a nonce. If you cannot provide a `sec_key`, `session_id` +/// UNIFORMLY RANDOM AND KEPT SECRET (even from other signers). Refer to libsecp256k1-zkp +/// documentation for additional considerations. +/// +/// Musig2 nonces can be precomputed without knowing the aggregate public key, the message to sign. +/// +/// +/// # Arguments: +/// +/// * `secp` : [`Secp256k1`] context object initialized for signing +/// * `session_id`: Uniform random identifier for this session. This _must_ never be re-used. +/// If this is not sampled uniformly at random, this can leak the private key +/// * `sec_key`: Optional [`SecretKey`] that we will use to sign to a create partial signature. Provide this +/// for maximal mis-use resistance. +/// * `msg`: Optional [`Message`] that will be signed later on. Provide this for maximal misuse resistance. +/// * `extra_rand`: Additional randomness for mis-use resistance. Provide this for maximal misuse resistance +/// +/// Remember that nonce reuse will immediately leak the secret key! +/// +/// # Errors: +/// +/// * `ZeroSession`: if the `session_id` is supplied is all zeros. +/// +/// Example: +/// +/// ```rust +/// # # [cfg(any(test, feature = "rand-std"))] { +/// # use secp256k1_zkp::rand::{thread_rng, RngCore}; +/// # use secp256k1_zkp::{Message, KeyPair, MusigKeyAggCache, XOnlyPublicKey, Secp256k1, SecretKey, new_musig_nonce_pair}; +/// let secp = Secp256k1::new(); +/// // The session id must be sampled at random. Read documentation for more details. +/// let mut session_id = [0; 32]; +/// thread_rng().fill_bytes(&mut session_id); +/// +/// // Supply extra auxillary randomness to prevent misuse(for example, time of day) +/// let extra_rand : Option<[u8; 32]> = None; +/// +/// let (_sec_nonce, _pub_nonce) = new_musig_nonce_pair(&secp, session_id, None, None, None, None) +/// .expect("non zero session id"); +/// # } +/// ``` +pub fn new_musig_nonce_pair( + secp: &Secp256k1, + session_id: [u8; 32], + key_agg_cache: Option<&MusigKeyAggCache>, + sec_key: Option, + msg: Option, + extra_rand: Option<[u8; 32]>, +) -> Result<(MusigSecNonce, MusigPubNonce), MusigNonceGenError> { + let cx = *secp.ctx(); + let extra_ptr = extra_rand + .as_ref() + .map(|e| e.as_ptr()) + .unwrap_or(core::ptr::null()); + let sk_ptr = sec_key + .as_ref() + .map(|e| e.as_ptr()) + .unwrap_or(core::ptr::null()); + let msg_ptr = msg + .as_ref() + .map(|ref e| e.as_ptr()) + .unwrap_or(core::ptr::null()); + let cache_ptr = key_agg_cache + .map(|e| e.as_ptr()) + .unwrap_or(core::ptr::null()); + unsafe { + let mut sec_nonce = MusigSecNonce(ffi::MusigSecNonce::new()); + let mut pub_nonce = MusigPubNonce(ffi::MusigPubNonce::new()); + if ffi::secp256k1_musig_nonce_gen( + cx, + sec_nonce.as_mut_ptr(), + pub_nonce.as_mut_ptr(), + (&session_id).as_ref().as_ptr(), + sk_ptr, + msg_ptr, + cache_ptr, + extra_ptr, + ) == 0 + { + // Rust type system guarantees that + // - input secret key is valid + // - msg is 32 bytes + // - Key agg cache is valid + // - extra input is 32 bytes + // This can only happen when the session id is all zeros + Err(MusigNonceGenError::ZeroSession) + } else { + Ok((sec_nonce, pub_nonce)) + } + } +} + +/// Opaque data structure that holds a partial MuSig signature. +/// +/// Serialized and parsed with [`MusigPartialSignature::serialize`] and +/// [`MusigPartialSignature::from_slice`]. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct MusigPartialSignature(ffi::MusigPartialSignature); + +impl CPtr for MusigPartialSignature { + type Target = ffi::MusigPartialSignature; + + fn as_c_ptr(&self) -> *const Self::Target { + self.as_ptr() + } + + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { + self.as_mut_ptr() + } +} + +impl MusigPartialSignature { + /// Serialize a MuSigPartialSignature or adaptor signature + /// + /// # Returns + /// + /// 32-byte array when the signature could be serialized + /// + /// Example: + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{Message, KeyPair, MusigAggNonce, MusigKeyAggCache, MusigSession, XOnlyPublicKey, Secp256k1, SecretKey}; + /// let secp = Secp256k1::new(); + /// let keypair1 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key1 = XOnlyPublicKey::from_keypair(&keypair1); + /// let keypair2 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key2 = XOnlyPublicKey::from_keypair(&keypair2); + /// + /// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key1, pub_key2]); + /// // The session id must be sampled at random. Read documentation for more details. + /// let mut session_id = [0; 32]; + /// thread_rng().fill_bytes(&mut session_id); + /// + /// // Generate the nonce for party with `keypair1`. + /// let sec_key1 = SecretKey::from_keypair(&keypair1); + /// let msg = Message::from_slice(&[3; 32]).unwrap(); + /// let (mut sec_nonce1, pub_nonce1) = key_agg_cache.nonce_gen(&secp, session_id, sec_key1, msg, None) + /// .expect("non zero session id"); + /// + /// // Generate the nonce for party with `keypair2`. + /// let sec_key2 = SecretKey::from_keypair(&keypair2); + /// let (_sec_nonce, pub_nonce2) = key_agg_cache.nonce_gen(&secp, session_id, sec_key2, msg, None) + /// .expect("non zero session id"); + /// + /// let aggnonce = MusigAggNonce::new(&secp, &[pub_nonce1, pub_nonce2]); + /// let session = MusigSession::new( + /// &secp, + /// &key_agg_cache, + /// aggnonce, + /// msg, + /// None, + /// ); + /// + /// let partial_sig = session.partial_sign( + /// &secp, + /// &mut sec_nonce1, + /// &keypair1, + /// &key_agg_cache, + /// ).unwrap(); + /// + /// let _ser_sig = partial_sig.serialize(); + /// # } + /// ``` + pub fn serialize(&self) -> [u8; 32] { + let mut data = [0; 32]; + unsafe { + if ffi::secp256k1_musig_partial_sig_serialize( + ffi::secp256k1_context_no_precomp, + data.as_mut_ptr(), + self.as_ptr(), + ) == 0 + { + // Only fails if args are null pointer which is possible in safe rust + unreachable!("Serialization cannot fail") + } else { + data + } + } + } + + /// Deserialize a MusigPartialSignature from bytes. + /// + /// # Errors: + /// + /// - ArgLenMismatch: If the signature is not 32 bytes + /// - MalformedArg: If the signature is 32 bytes, but out of curve order + /// + /// Example: + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{ + /// # Message, MusigAggNonce, MusigPartialSignature, MusigKeyAggCache, MusigSession, Secp256k1, SecretKey, XOnlyPublicKey, KeyPair + /// # }; + /// let secp = Secp256k1::new(); + /// let keypair1 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key1 = XOnlyPublicKey::from_keypair(&keypair1); + /// let keypair2 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key2 = XOnlyPublicKey::from_keypair(&keypair2); + /// + /// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key1, pub_key2]); + /// // The session id must be sampled at random. Read documentation for more details. + /// let mut session_id = [0; 32]; + /// thread_rng().fill_bytes(&mut session_id); + /// + /// // Generate the nonce for party with `keypair1`. + /// let sec_key1 = SecretKey::from_keypair(&keypair1); + /// let msg = Message::from_slice(&[3; 32]).unwrap(); + /// let (mut sec_nonce1, pub_nonce1) = key_agg_cache.nonce_gen(&secp, session_id, sec_key1, msg, None) + /// .expect("non zero session id"); + /// + /// // Generate the nonce for party with `keypair2`. + /// let sec_key2 = SecretKey::from_keypair(&keypair2); + /// let (_sec_nonce, pub_nonce2) = key_agg_cache.nonce_gen(&secp, session_id, sec_key2, msg, None) + /// .expect("non zero session id"); + /// + /// let aggnonce = MusigAggNonce::new(&secp, &[pub_nonce1, pub_nonce2]); + /// let session = MusigSession::new( + /// &secp, + /// &key_agg_cache, + /// aggnonce, + /// msg, + /// None, + /// ); + /// + /// let partial_sig = session.partial_sign( + /// &secp, + /// &mut sec_nonce1, + /// &keypair1, + /// &key_agg_cache, + /// ).unwrap(); + /// + /// let ser_sig = partial_sig.serialize(); + /// let _parsed_sig = MusigPartialSignature::from_slice(&ser_sig).unwrap(); + /// # } + /// ``` + pub fn from_slice(data: &[u8]) -> Result { + let mut part_sig = MusigPartialSignature(ffi::MusigPartialSignature::new()); + if data.len() != 32 { + return Err(ParseError::ArgLenMismatch { + expected: 32, + got: data.len(), + }); + } + unsafe { + if ffi::secp256k1_musig_partial_sig_parse( + ffi::secp256k1_context_no_precomp, + part_sig.as_mut_ptr(), + data.as_ptr(), + ) == 0 + { + Err(ParseError::MalformedArg) + } else { + Ok(part_sig) + } + } + } + + /// Get a const pointer to the inner MusigPartialSignature + pub fn as_ptr(&self) -> *const ffi::MusigPartialSignature { + &self.0 + } + + /// Get a mut pointer to the inner MusigPartialSignature + pub fn as_mut_ptr(&mut self) -> *mut ffi::MusigPartialSignature { + &mut self.0 + } +} + +/// Musig partial signature parsing errors +#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub enum ParseError { + /// Length mismatch + ArgLenMismatch { + /// Expected size. + expected: usize, + /// Actual size. + got: usize, + }, + /// Parse Argument is malformed. This might occur if the point is on the secp order, + /// or if the secp scalar is outside of group order + MalformedArg, +} + +#[cfg(feature = "std")] +impl std::error::Error for ParseError {} + +impl fmt::Display for ParseError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match *self { + ParseError::ArgLenMismatch { expected, got } => { + write!(f, "Argument must be {} bytes, got {}", expected, got) + } + ParseError::MalformedArg => write!(f, "Malformed parse argument"), + } + } +} + +/// Creates a signature from a pre-signature(not to be confused with [`MusigPartialSignature`]) +/// and an adaptor. +/// +/// # Arguments: +/// +/// * `pre_sig` : [`schnorr::Signature`] to which the adaptor is to be added +/// * `sec_adaptor` : Secret adaptor of [`Tweak`] type to add to pre signature +/// * `nonce_parity`: The [`Parity`] obtained by [`MusigSession::nonce_parity`] for the session +/// used to compute `pre_sig`. +/// +/// # Returns: +/// +/// The [`schnorr::Signature`] with the adaptor applied. +/// +/// Example: +/// +/// ```rust +/// # # [cfg(any(test, feature = "rand-std"))] { +/// # use secp256k1_zkp::rand::{thread_rng, RngCore}; +/// # use secp256k1_zkp::{adapt, schnorr, Tweak, Message, MusigAggNonce, MusigKeyAggCache, MusigSession, XOnlyPublicKey, Secp256k1, SecretKey, PublicKey, KeyPair}; +/// let secp = Secp256k1::new(); +/// let keypair1 = KeyPair::new(&secp, &mut thread_rng()); +/// let pub_key1 = XOnlyPublicKey::from_keypair(&keypair1); +/// let keypair2 = KeyPair::new(&secp, &mut thread_rng()); +/// let pub_key2 = XOnlyPublicKey::from_keypair(&keypair2); +/// +/// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key1, pub_key2]); +/// let agg_pk = key_agg_cache.agg_pk(); +/// // The session id must be sampled at random. Read documentation for more details. +/// let mut session_id = [0; 32]; +/// thread_rng().fill_bytes(&mut session_id); +/// +/// // Generate the nonce for party with `keypair1`. +/// let sec_key1 = SecretKey::from_keypair(&keypair1); +/// let msg = Message::from_slice(&[3; 32]).unwrap(); +/// let mut extra_rand = [0u8; 32]; +/// thread_rng().fill_bytes(&mut extra_rand); +/// let (mut sec_nonce1, pub_nonce1) = key_agg_cache.nonce_gen(&secp, session_id, sec_key1, msg, None) +/// .expect("non zero session id"); +/// +/// // Generate the nonce for party with `keypair2`. +/// let sec_key2 = SecretKey::from_keypair(&keypair2); +/// let mut extra_rand = [0u8; 32]; +/// thread_rng().fill_bytes(&mut extra_rand); +/// let (mut sec_nonce2, pub_nonce2) = key_agg_cache.nonce_gen(&secp, session_id, sec_key2, msg, None) +/// .expect("non zero session id"); +/// +/// let aggnonce = MusigAggNonce::new(&secp, &[pub_nonce1, pub_nonce2]); +/// +/// // Tweak with a secret adaptor +/// let mut adapt_bytes = [0; 32]; +/// thread_rng().fill_bytes(&mut adapt_bytes); +/// let adapt_sec = SecretKey::from_slice(&adapt_bytes).unwrap(); +/// let adapt_pub = PublicKey::from_secret_key(&secp, &adapt_sec); +/// let adapt_sec = Tweak::from_slice(adapt_sec.as_ref()).unwrap(); +/// +/// let session = MusigSession::new( +/// &secp, +/// &key_agg_cache, +/// aggnonce, +/// msg, +/// Some(adapt_pub), // adaptor here +/// ); +/// +/// let partial_sig1 = session.partial_sign( +/// &secp, +/// &mut sec_nonce1, +/// &keypair1, +/// &key_agg_cache, +/// ).unwrap(); +/// +/// // Other party creates the other partial signature +/// let partial_sig2 = session.partial_sign( +/// &secp, +/// &mut sec_nonce2, +/// &keypair2, +/// &key_agg_cache, +/// ).unwrap(); +/// +/// let nonce_parity = session.nonce_parity(); +/// let pre_sig = session.partial_sig_agg(&[partial_sig1, partial_sig2]); +/// +/// // Note that without the adaptor, the aggregated signature will fail verification +/// +/// assert!(secp.verify_schnorr(&pre_sig, &msg, &agg_pk).is_err()); +/// // Get the final schnorr signature +/// let schnorr_sig = adapt(pre_sig, adapt_sec, nonce_parity); +/// assert!(secp.verify_schnorr(&schnorr_sig, &msg, &agg_pk).is_ok()); +/// # } +/// ``` +pub fn adapt( + pre_sig: schnorr::Signature, + sec_adaptor: Tweak, + nonce_parity: Parity, +) -> schnorr::Signature { + unsafe { + let mut sig = pre_sig; + if ffi::secp256k1_musig_adapt( + ffi::secp256k1_context_no_precomp, + sig.as_mut_ptr(), + pre_sig.as_ptr(), + sec_adaptor.as_ptr(), + nonce_parity.to_i32(), + ) == 0 + { + // Only fails when the arguments are invalid which is not possible in safe rust + unreachable!("Arguments must be valid and well-typed") + } else { + schnorr::Signature::from_slice(sig.as_ref()) + .expect("Adapted signatures from pre-sig must be valid schnorr signatures") + } + } +} + +/// Extracts a secret adaptor from a MuSig, given all parties' partial +/// signatures. This function will not fail unless given grossly invalid data; if it +/// is merely given signatures that do not verify, the returned value will be +/// nonsense. It is therefore important that all data be verified at earlier steps of +/// any protocol that uses this function. +/// +/// # Arguments: +/// +/// * `sig`: the [`schnorr::Signature`] with the adaptor applied. +/// * `pre_sig` : Secret adaptor of [`SecretKey`] type to add to pre signature +/// corresponding to `sig`. This is the aggregation of all [`MusigPartialSignature`] without +/// the adaptor +/// * `nonce_parity`: The [`Parity`] obtained by [`MusigSession::nonce_parity`] for the session +/// used to compute `pre_sig64`. +/// +/// # Returns: +/// +/// The adaptor secret of [`Tweak`]. The [`Tweak`] type is like [`SecretKey`], but also +/// allows for representing the zero value. +/// +/// Example: +/// +/// ```rust +/// # # [cfg(any(test, feature = "rand-std"))] { +/// # use secp256k1_zkp::rand::{thread_rng, RngCore}; +/// # use secp256k1_zkp::{adapt, extract_adaptor}; +/// # use secp256k1_zkp::{Message, KeyPair, PublicKey, MusigAggNonce, MusigKeyAggCache, MusigSession, XOnlyPublicKey, Secp256k1, SecretKey, Tweak}; +/// let secp = Secp256k1::new(); +/// let keypair1 = KeyPair::new(&secp, &mut thread_rng()); +/// let pub_key1 = XOnlyPublicKey::from_keypair(&keypair1); +/// let keypair2 = KeyPair::new(&secp, &mut thread_rng()); +/// let pub_key2 = XOnlyPublicKey::from_keypair(&keypair2); +/// +/// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key1, pub_key2]); +/// // The session id must be sampled at random. Read documentation for more details. +/// let mut session_id = [0; 32]; +/// thread_rng().fill_bytes(&mut session_id); +/// +/// // Generate the nonce for party with `keypair1`. +/// let sec_key1 = SecretKey::from_keypair(&keypair1); +/// let msg = Message::from_slice(&[3; 32]).unwrap(); +/// let mut extra_rand = [0u8; 32]; +/// thread_rng().fill_bytes(&mut extra_rand); +/// let (mut sec_nonce1, pub_nonce1) = key_agg_cache.nonce_gen(&secp, session_id, sec_key1, msg, None) +/// .expect("non zero session id"); +/// +/// // Generate the nonce for party with `keypair2`. +/// let sec_key2 = SecretKey::from_keypair(&keypair2); +/// let mut extra_rand = [0u8; 32]; +/// thread_rng().fill_bytes(&mut extra_rand); +/// let (mut sec_nonce2, pub_nonce2) = key_agg_cache.nonce_gen(&secp, session_id, sec_key2, msg, None) +/// .expect("non zero session id"); +/// +/// let aggnonce = MusigAggNonce::new(&secp, &[pub_nonce1, pub_nonce2]); +/// +/// // Tweak with a secret adaptor +/// let mut adapt_bytes = [0; 32]; +/// thread_rng().fill_bytes(&mut adapt_bytes); +/// let adapt_sec = SecretKey::from_slice(&adapt_bytes).unwrap(); +/// let adapt_pub = PublicKey::from_secret_key(&secp, &adapt_sec); +/// let adapt_sec = Tweak::from_slice(adapt_sec.as_ref()).unwrap(); +/// +/// let session = MusigSession::new( +/// &secp, +/// &key_agg_cache, +/// aggnonce, +/// msg, +/// Some(adapt_pub), // adaptor here +/// ); +/// +/// let partial_sig1 = session.partial_sign( +/// &secp, +/// &mut sec_nonce1, +/// &keypair1, +/// &key_agg_cache, +/// ).unwrap(); +/// +/// // Other party creates the other partial signature +/// let partial_sig2 = session.partial_sign( +/// &secp, +/// &mut sec_nonce2, +/// &keypair2, +/// &key_agg_cache, +/// ).unwrap(); +/// +/// let nonce_parity = session.nonce_parity(); +/// let pre_sig = session.partial_sig_agg(&[partial_sig1, partial_sig2]); +/// +/// let schnorr_sig = adapt(pre_sig, adapt_sec, nonce_parity); +/// let extracted_sec = extract_adaptor( +/// schnorr_sig, +/// pre_sig, +/// nonce_parity, +/// ); +/// assert_eq!(extracted_sec, adapt_sec); +/// # } +/// ``` +pub fn extract_adaptor( + sig: schnorr::Signature, + pre_sig: schnorr::Signature, + nonce_parity: Parity, +) -> Tweak { + unsafe { + let mut secret = ZERO_TWEAK; + if ffi::secp256k1_musig_extract_adaptor( + ffi::secp256k1_context_no_precomp, + secret.as_mut_ptr(), + sig.as_ptr(), + pre_sig.as_ptr(), + nonce_parity.to_i32(), + ) == 0 + { + // Only fails when the arguments are invalid which is not possible in safe rust + unreachable!("Arguments must be valid and well-typed") + } else { + secret + } + } +} + +/// This structure MUST NOT be copied or +/// read or written to it directly. A signer who is online throughout the whole +/// process and can keep this structure in memory can use the provided API +/// functions for a safe standard workflow. See +/// https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/ for +/// more details about the risks associated with serializing or deserializing +/// this structure. There are no serialization and parsing functions (yet). +/// +/// Note this deliberately does not implement `Copy` or `Clone`. After creation, the only +/// use of this nonce is [`MusigSession::partial_sign`] API that takes a mutable reference +/// and overwrites this nonce with zero. +/// +/// A signer who is online throughout the whole process and can keep this +/// structure in memory can use the provided API functions for a safe standard +/// workflow. See +/// https://blockstream.com/2019/02/18/musig-a-new-multisignature-standard/ for +/// more details about the risks associated with serializing or deserializing +/// this structure. +/// +/// Signers that pre-computes and saves these nonces are not yet supported. Users +/// who want to serialize this must use unsafe rust to do so. +#[derive(Debug, Eq, PartialEq)] +pub struct MusigSecNonce(ffi::MusigSecNonce); + +impl CPtr for MusigSecNonce { + type Target = ffi::MusigSecNonce; + + fn as_c_ptr(&self) -> *const Self::Target { + self.as_ptr() + } + + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { + self.as_mut_ptr() + } +} + +impl MusigSecNonce { + /// Get a const pointer to the inner MusigKeyAggCache + pub fn as_ptr(&self) -> *const ffi::MusigSecNonce { + &self.0 + } + + /// Get a mut pointer to the inner MusigKeyAggCache + pub fn as_mut_ptr(&mut self) -> *mut ffi::MusigSecNonce { + &mut self.0 + } +} + +/// Opaque data structure that holds a MuSig public nonce. +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct MusigPubNonce(ffi::MusigPubNonce); + +impl CPtr for MusigPubNonce { + type Target = ffi::MusigPubNonce; + + fn as_c_ptr(&self) -> *const Self::Target { + self.as_ptr() + } + + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { + self.as_mut_ptr() + } +} + +impl MusigPubNonce { + /// Serialize a MusigPubNonce + /// + /// Example: + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{Message, KeyPair, MusigKeyAggCache, MusigPubNonce, PublicKey, Secp256k1, SecretKey, XOnlyPublicKey}; + /// let secp = Secp256k1::new(); + /// let sec_key = SecretKey::from_slice([1; 32].as_ref()).unwrap(); + /// let keypair = KeyPair::from_secret_key(&secp, sec_key); + /// let pub_key = XOnlyPublicKey::from_keypair(&keypair); + /// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key]); + /// let msg = Message::from_slice(&[3; 32]).unwrap(); + /// let session_id = [2; 32]; + /// let (mut secnonce, pubnonce) = key_agg_cache.nonce_gen(&secp, session_id, sec_key, msg, None) + /// .expect("non zero session id"); + /// + /// let _pubnonce_ser = pubnonce.serialize(); + /// # } + /// ``` + pub fn serialize(&self) -> [u8; ffi::MUSIG_PUBNONCE_LEN] { + let mut data = [0; ffi::MUSIG_PUBNONCE_LEN]; + unsafe { + if ffi::secp256k1_musig_pubnonce_serialize( + ffi::secp256k1_context_no_precomp, + data.as_mut_ptr(), + self.as_ptr(), + ) == 0 + { + // Only fails when the arguments are invalid which is not possible in safe rust + unreachable!("Arguments must be valid and well-typed") + } else { + data + } + } + } + + /// Deserialize a MusigPubNonce from a portable byte representation + /// + /// # Errors: + /// + /// - ArgLenMismatch: If the [`MusigPubNonce`] is not 132 bytes + /// - MalformedArg: If the [`MusigPubNonce`] is 132 bytes, but out of curve order + /// Example: + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{Message, KeyPair, MusigKeyAggCache, MusigPubNonce, PublicKey, Secp256k1, SecretKey, XOnlyPublicKey}; + /// let secp = Secp256k1::new(); + /// let sec_key = SecretKey::from_slice([1; 32].as_ref()).unwrap(); + /// let keypair = KeyPair::from_secret_key(&secp, sec_key); + /// let pub_key = XOnlyPublicKey::from_keypair(&keypair); + /// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key]); + /// let msg = Message::from_slice(&[3; 32]).unwrap(); + /// let session_id = [2; 32]; + /// let (mut secnonce, pubnonce) = key_agg_cache.nonce_gen(&secp, session_id, sec_key, msg, None) + /// .expect("non zero session id"); + /// + /// let pubnonce_ser = pubnonce.serialize(); + /// let parsed_pubnonce = MusigPubNonce::from_slice(&pubnonce_ser).unwrap(); + /// assert_eq!(parsed_pubnonce, pubnonce); + /// # } + /// ``` + pub fn from_slice(data: &[u8]) -> Result { + let mut pubnonce = MusigPubNonce(ffi::MusigPubNonce::new()); + if data.len() != ffi::MUSIG_PUBNONCE_LEN { + return Err(ParseError::ArgLenMismatch { + expected: ffi::MUSIG_PUBNONCE_LEN, + got: data.len(), + }); + } + unsafe { + if ffi::secp256k1_musig_pubnonce_parse( + ffi::secp256k1_context_no_precomp, + pubnonce.as_mut_ptr(), + data.as_ptr(), + ) == 0 + { + Err(ParseError::MalformedArg) + } else { + Ok(pubnonce) + } + } + } + + /// Get a const pointer to the inner MusigPubNonce + pub fn as_ptr(&self) -> *const ffi::MusigPubNonce { + &self.0 + } + + /// Get a mut pointer to the inner MusigPubNonce + pub fn as_mut_ptr(&mut self) -> *mut ffi::MusigPubNonce { + &mut self.0 + } +} + +/// Opaque data structure that holds a MuSig aggregated nonce. +/// +/// There are no serialization and parsing functions (yet). +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct MusigAggNonce(ffi::MusigAggNonce); + +impl CPtr for MusigAggNonce { + type Target = ffi::MusigAggNonce; + + fn as_c_ptr(&self) -> *const Self::Target { + self.as_ptr() + } + + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { + self.as_mut_ptr() + } +} + +impl MusigAggNonce { + /// Combine received public nonces into a single aggregated nonce + /// + /// This is useful to reduce the communication between signers, because instead + /// of everyone sending nonces to everyone else, there can be one party + /// receiving all nonces, combining the nonces with this function and then + /// sending only the combined nonce back to the signers. The pubnonces argument + /// of [MusigKeyAggCache::nonce_process] then simply becomes an array whose sole + /// element is this combined nonce. + /// + /// Example: + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{Message, KeyPair, MusigAggNonce, MusigKeyAggCache, MusigPubNonce, MusigSession, Secp256k1, SecretKey, XOnlyPublicKey}; + /// let secp = Secp256k1::new(); + /// let keypair1 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key1 = XOnlyPublicKey::from_keypair(&keypair1); + /// let keypair2 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key2 = XOnlyPublicKey::from_keypair(&keypair2); + /// + /// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key1, pub_key2]); + /// // The session id must be sampled at random. Read documentation for more details. + /// let mut session_id = [0; 32]; + /// thread_rng().fill_bytes(&mut session_id); + /// + /// // Generate the nonce for party with `keypair1`. + /// let sec_key1 = SecretKey::from_keypair(&keypair1); + /// let msg = Message::from_slice(&[3; 32]).unwrap(); + /// let (mut sec_nonce1, pub_nonce1) = key_agg_cache.nonce_gen(&secp, session_id, sec_key1, msg, None) + /// .expect("non zero session id"); + /// + /// // Generate the nonce for party with `keypair2`. + /// let sec_key2 = SecretKey::from_keypair(&keypair2); + /// let (_sec_nonce, pub_nonce2) = key_agg_cache.nonce_gen(&secp, session_id, sec_key2, msg, None) + /// .expect("non zero session id"); + /// + /// let aggnonce = MusigAggNonce::new(&secp, &[pub_nonce1, pub_nonce2]); + /// # } + /// ``` + pub fn new(secp: &Secp256k1, nonces: &[MusigPubNonce]) -> Self { + let mut aggnonce = MusigAggNonce(ffi::MusigAggNonce::new()); + let nonce_ptrs = nonces.iter().map(|n| n.as_ptr()).collect::>(); + unsafe { + if ffi::secp256k1_musig_nonce_agg( + *secp.ctx(), + aggnonce.as_mut_ptr(), + nonce_ptrs.as_ptr(), + nonce_ptrs.len(), + ) == 0 + { + // This can only crash if the individual nonces are invalid which is not possible is rust. + // Note that even if aggregate nonce is point at infinity, the musig spec sets it as `G` + unreachable!("Public key nonces are well-formed and valid in rust typesystem") + } else { + aggnonce + } + } + } + + /// Serialize a MusigAggNonce + /// + /// Example: + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{Message, KeyPair, MusigAggNonce, MusigKeyAggCache, MusigPubNonce, MusigSession, Secp256k1, SecretKey, XOnlyPublicKey}; + /// let secp = Secp256k1::new(); + /// let sec_key = SecretKey::from_slice([1; 32].as_ref()).unwrap(); + /// let keypair = KeyPair::from_secret_key(&secp, sec_key); + /// let pub_key = XOnlyPublicKey::from_keypair(&keypair); + /// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key]); + /// let msg = Message::from_slice(&[3; 32]).unwrap(); + /// + /// let session_id = [2; 32]; + /// let (mut secnonce, pubnonce) = key_agg_cache.nonce_gen(&secp, session_id, sec_key, msg, None) + /// .expect("non zero session id"); + /// let aggnonce = MusigAggNonce::new(&secp, &[pubnonce]); + /// + /// let _aggnonce_ser = aggnonce.serialize(); + /// # } + /// ``` + pub fn serialize(&self) -> [u8; ffi::MUSIG_AGGNONCE_LEN] { + let mut data = [0; ffi::MUSIG_AGGNONCE_LEN]; + unsafe { + if ffi::secp256k1_musig_aggnonce_serialize( + ffi::secp256k1_context_no_precomp, + data.as_mut_ptr(), + self.as_ptr(), + ) == 0 + { + // Only fails when the arguments are invalid which is not possible in safe rust + unreachable!("Arguments must be valid and well-typed") + } else { + data + } + } + } + + /// Deserialize a MusigAggNonce from byte slice + /// + /// # Errors: + /// + /// - ArgLenMismatch: If the slice is not 132 bytes + /// - MalformedArg: If the byte slice is 132 bytes, but the [`MusigAggNonce`] is invalid + /// + /// Example: + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{Message, KeyPair, MusigAggNonce, MusigKeyAggCache, Secp256k1, SecretKey, XOnlyPublicKey}; + /// let secp = Secp256k1::new(); + /// let sec_key = SecretKey::from_slice([1; 32].as_ref()).unwrap(); + /// let keypair = KeyPair::from_secret_key(&secp, sec_key); + /// let pub_key = XOnlyPublicKey::from_keypair(&keypair); + /// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key]); + /// let msg = Message::from_slice(&[3; 32]).unwrap(); + /// + /// let session_id = [2; 32]; + /// let (mut secnonce, pubnonce) = key_agg_cache.nonce_gen(&secp, session_id, sec_key, msg, None) + /// .expect("non zero session id"); + /// let aggnonce = MusigAggNonce::new(&secp, &[pubnonce]); + /// + /// let aggnonce_ser = aggnonce.serialize(); + /// let parsed_aggnonce = MusigAggNonce::from_slice(&aggnonce_ser).unwrap(); + /// assert_eq!(parsed_aggnonce, aggnonce); + /// # } + /// ``` + pub fn from_slice(data: &[u8]) -> Result { + if data.len() != ffi::MUSIG_AGGNONCE_LEN { + return Err(ParseError::ArgLenMismatch { + expected: ffi::MUSIG_AGGNONCE_LEN, + got: data.len(), + }); + } + let mut aggnonce = MusigAggNonce(ffi::MusigAggNonce::new()); + unsafe { + if ffi::secp256k1_musig_aggnonce_parse( + ffi::secp256k1_context_no_precomp, + aggnonce.as_mut_ptr(), + data.as_ptr(), + ) == 0 + { + Err(ParseError::MalformedArg) + } else { + Ok(aggnonce) + } + } + } + + /// Get a const pointer to the inner MusigAggNonce + pub fn as_ptr(&self) -> *const ffi::MusigAggNonce { + &self.0 + } + + /// Get a mut pointer to the inner MusigAggNonce + pub fn as_mut_ptr(&mut self) -> *mut ffi::MusigAggNonce { + &mut self.0 + } +} + +/// Musig session data structure containing the +/// secret and public nonce used in a multi-signature signing session +#[derive(Debug, Clone, Copy, Eq, PartialEq)] +pub struct MusigSession(ffi::MusigSession); + +impl CPtr for MusigSession { + type Target = ffi::MusigSession; + + fn as_c_ptr(&self) -> *const Self::Target { + self.as_ptr() + } + + fn as_mut_c_ptr(&mut self) -> *mut Self::Target { + self.as_mut_ptr() + } +} + +impl MusigSession { + /// Takes the public nonces of all signers and computes a session that is + /// required for signing and verification of partial signatures. + /// + /// If the adaptor argument is [`Option::Some`], then the output of + /// partial signature aggregation will be a pre-signature which is not a valid Schnorr + /// signature. In order to create a valid signature, the pre-signature and the + /// secret adaptor must be provided to `musig_adapt`. + /// + /// # Returns: + /// + /// A [`MusigSession`] that can be later used for signing. + /// + /// # Arguments: + /// + /// * `secp` : [`Secp256k1`] context object initialized for signing + /// * `key_agg_cache`: [`MusigKeyAggCache`] to be used for this session + /// * `agg_nonce`: [`MusigAggNonce`], the aggregate nonce + /// * `msg`: [`Message`] that will be signed later on. + /// * `adaptor`: The adaptor of type [`PublicKey`] if this is signing session is a part of + /// an adaptor signature protocol. + /// + /// Example: + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{Message, KeyPair, MusigAggNonce, MusigKeyAggCache, MusigSession, XOnlyPublicKey, Secp256k1, SecretKey}; + /// let secp = Secp256k1::new(); + /// let keypair1 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key1 = XOnlyPublicKey::from_keypair(&keypair1); + /// let keypair2 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key2 = XOnlyPublicKey::from_keypair(&keypair2); + /// + /// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key1, pub_key2]); + /// let agg_pk = key_agg_cache.agg_pk(); + /// // The session id must be sampled at random. Read documentation for more details. + /// let mut session_id = [0; 32]; + /// thread_rng().fill_bytes(&mut session_id); + /// + /// // Generate the nonce for party with `keypair1`. + /// let sec_key1 = SecretKey::from_keypair(&keypair1); + /// let msg = Message::from_slice(&[3; 32]).unwrap(); + /// let (mut sec_nonce1, pub_nonce1) = key_agg_cache.nonce_gen(&secp, session_id, sec_key1, msg, None) + /// .expect("non zero session id"); + /// + /// // Generate the nonce for party with `keypair2`. + /// let sec_key2 = SecretKey::from_keypair(&keypair2); + /// let (mut sec_nonce2, pub_nonce2) = key_agg_cache.nonce_gen(&secp, session_id, sec_key2, msg, None) + /// .expect("non zero session id"); + /// + /// let aggnonce = MusigAggNonce::new(&secp, &[pub_nonce1, pub_nonce2]); + /// + /// let session = MusigSession::new( + /// &secp, + /// &key_agg_cache, + /// aggnonce, + /// msg, + /// None, // adaptor here + /// ); + /// # } + /// ``` + pub fn new( + secp: &Secp256k1, + key_agg_cache: &MusigKeyAggCache, + agg_nonce: MusigAggNonce, + msg: Message, + adaptor: Option, + ) -> Self { + let mut session = MusigSession(ffi::MusigSession::new()); + let adaptor_ptr = match adaptor { + Some(a) => a.as_ptr(), + None => core::ptr::null(), + }; + unsafe { + if ffi::secp256k1_musig_nonce_process( + *secp.ctx(), + session.as_mut_ptr(), + agg_nonce.as_ptr(), + msg.as_ptr(), + key_agg_cache.as_ptr(), + adaptor_ptr, + ) == 0 + { + // Only fails on cryptographically unreachable codes or if the args are invalid. + // None of which can occur in safe rust. + unreachable!("Impossible to construct invalid arguments in safe rust. + Also reaches here if R1 + R2*b == point at infinity, but only occurs with 1/1^128 probability") + } else { + session + } + } + } + + /// Produces a partial signature for a given key pair and secret nonce. + /// + /// Remember that nonce reuse will immediately leak the secret key! + /// + /// # Returns: + /// + /// A [`MusigPartialSignature`] that can be later be aggregated into a [`schnorr::Signature`] + /// + /// # Arguments: + /// + /// * `secp` : [`Secp256k1`] context object initialized for signing + /// * `sec_nonce`: [`MusigSecNonce`] to be used for this session that has never + /// been used before. For mis-use resistance, this API takes a mutable reference + /// to `sec_nonce` and sets it to zero even if the partial signing fails. + /// * `key_pair`: The [`KeyPair`] to sign the message + /// * `key_agg_cache`: [`MusigKeyAggCache`] containing the aggregate pubkey used in + /// the creation of this session + /// + /// # Errors: + /// + /// - If the provided [`MusigSecNonce`] has already been used for signing + /// + /// # Example: + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{Message, KeyPair, MusigAggNonce, MusigKeyAggCache, MusigSession, Secp256k1, SecretKey, XOnlyPublicKey}; + /// let secp = Secp256k1::new(); + /// let keypair1 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key1 = XOnlyPublicKey::from_keypair(&keypair1); + /// let keypair2 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key2 = XOnlyPublicKey::from_keypair(&keypair2); + /// + /// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key1, pub_key2]); + /// // The session id must be sampled at random. Read documentation for more details. + /// let mut session_id = [0; 32]; + /// thread_rng().fill_bytes(&mut session_id); + /// + /// // Generate the nonce for party with `keypair1`. + /// let sec_key1 = SecretKey::from_keypair(&keypair1); + /// let msg = Message::from_slice(&[3; 32]).unwrap(); + /// let (mut sec_nonce1, pub_nonce1) = key_agg_cache.nonce_gen(&secp, session_id, sec_key1, msg, None) + /// .expect("non zero session id"); + /// + /// // Generate the nonce for party with `keypair2`. + /// let sec_key2 = SecretKey::from_keypair(&keypair2); + /// let (mut sec_nonce2, pub_nonce2) = key_agg_cache.nonce_gen(&secp, session_id, sec_key2, msg, None) + /// .expect("non zero session id"); + /// + /// let aggnonce = MusigAggNonce::new(&secp, &[pub_nonce1, pub_nonce2]); + /// + /// let session = MusigSession::new( + /// &secp, + /// &key_agg_cache, + /// aggnonce, + /// msg, + /// None, // adaptor here + /// ); + /// + /// let _partial_sig = session.partial_sign( + /// &secp, + /// &mut sec_nonce1, + /// &keypair1, + /// &key_agg_cache, + /// ).unwrap(); + /// # } + /// ``` + pub fn partial_sign( + &self, + secp: &Secp256k1, + secnonce: &mut MusigSecNonce, + keypair: &KeyPair, + key_agg_cache: &MusigKeyAggCache, + ) -> Result { + unsafe { + let mut partial_sig = MusigPartialSignature(ffi::MusigPartialSignature::new()); + if ffi::secp256k1_musig_partial_sign( + *secp.ctx(), + partial_sig.as_mut_ptr(), + secnonce.as_mut_ptr(), + keypair.as_ptr(), + key_agg_cache.as_ptr(), + self.as_ptr(), + ) == 0 + { + // Since the arguments in rust are always session_valid, the only reason + // this will fail if the nonce was reused. + Err(MusigSignError::NonceReuse) + } else { + Ok(partial_sig) + } + } + } + + /// Checks that an individual partial signature verifies + /// + /// This function is essential when using protocols with adaptor signatures. + /// However, it is not essential for regular MuSig's, in the sense that if any + /// partial signatures does not verify, the full signature will also not verify, so the + /// problem will be caught. But this function allows determining the specific party + /// who produced an invalid signature, so that signing can be restarted without them. + /// + /// # Returns: + /// + /// true if the partial signature successfully verifies, otherwise returns false + /// + /// # Arguments: + /// + /// * `secp` : [`Secp256k1`] context object initialized for signing + /// * `key_agg_cache`: [`MusigKeyAggCache`] containing the aggregate pubkey used in + /// the creation of this session + /// * `partial_sig`: [`MusigPartialSignature`] sent by the signer associated with + /// the given `pub_nonce` and `pubkey` + /// * `pub_nonce`: The [`MusigPubNonce`] of the signer associated with the `partial_sig` + /// and `pub_key` + /// * `pub_key`: The [`XOnlyPublicKey`] of the signer associated with the given + /// `partial_sig` and `pub_nonce` + /// + /// Example: + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{Message, KeyPair, MusigAggNonce, MusigKeyAggCache, MusigSession, Secp256k1, SecretKey, XOnlyPublicKey}; + /// let secp = Secp256k1::new(); + /// let keypair1 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key1 = XOnlyPublicKey::from_keypair(&keypair1); + /// let keypair2 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key2 = XOnlyPublicKey::from_keypair(&keypair2); + /// + /// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key1, pub_key2]); + /// // The session id must be sampled at random. Read documentation for more details. + /// let mut session_id = [0; 32]; + /// thread_rng().fill_bytes(&mut session_id); + /// + /// // Generate the nonce for party with `keypair1`. + /// let sec_key1 = SecretKey::from_keypair(&keypair1); + /// let msg = Message::from_slice(&[3; 32]).unwrap(); + /// let (mut sec_nonce1, pub_nonce1) = key_agg_cache.nonce_gen(&secp, session_id, sec_key1, msg, None) + /// .expect("non zero session id"); + /// + /// // Generate the nonce for party with `keypair2`. + /// let sec_key2 = SecretKey::from_keypair(&keypair2); + /// let (mut sec_nonce2, pub_nonce2) = key_agg_cache.nonce_gen(&secp, session_id, sec_key2, msg, None) + /// .expect("non zero session id"); + /// + /// let aggnonce = MusigAggNonce::new(&secp, &[pub_nonce1, pub_nonce2]); + /// + /// let session = MusigSession::new( + /// &secp, + /// &key_agg_cache, + /// aggnonce, + /// msg, + /// None, // adaptor here + /// ); + /// + /// let partial_sig1 = session.partial_sign( + /// &secp, + /// &mut sec_nonce1, + /// &keypair1, + /// &key_agg_cache, + /// ).unwrap(); + /// + /// assert!(session.partial_verify( + /// &secp, + /// &key_agg_cache, + /// partial_sig1, + /// pub_nonce1, + /// pub_key1, + /// )); + /// # } + /// ``` + pub fn partial_verify( + &self, + secp: &Secp256k1, + key_agg_cache: &MusigKeyAggCache, + partial_sig: MusigPartialSignature, + pub_nonce: MusigPubNonce, + pub_key: XOnlyPublicKey, + ) -> bool { + let cx = *secp.ctx(); + unsafe { + ffi::secp256k1_musig_partial_sig_verify( + cx, + partial_sig.as_ptr(), + pub_nonce.as_ptr(), + pub_key.as_ptr(), + key_agg_cache.as_ptr(), + self.as_ptr(), + ) == 1 + } + } + + /// Aggregate partial signatures for this session into a single [`schnorr::Signature`] + /// + /// # Returns: + /// + /// A single [`schnorr::Signature`]. Note that this does *NOT* mean that the signature verifies with respect to the + /// aggregate public key. + /// + /// # Arguments: + /// + /// * `partial_sigs`: Array of [`MusigPartialSignature`] to be aggregated + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{Message, KeyPair, MusigAggNonce, MusigKeyAggCache, MusigSession, Secp256k1, SecretKey, XOnlyPublicKey}; + /// let secp = Secp256k1::new(); + /// let keypair1 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key1 = XOnlyPublicKey::from_keypair(&keypair1); + /// let keypair2 = KeyPair::new(&secp, &mut thread_rng()); + /// let pub_key2 = XOnlyPublicKey::from_keypair(&keypair2); + /// + /// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key1, pub_key2]); + /// let agg_pk = key_agg_cache.agg_pk(); + /// // The session id must be sampled at random. Read documentation for more details. + /// let mut session_id = [0; 32]; + /// thread_rng().fill_bytes(&mut session_id); + /// + /// // Generate the nonce for party with `keypair1`. + /// let sec_key1 = SecretKey::from_keypair(&keypair1); + /// let msg = Message::from_slice(&[3; 32]).unwrap(); + /// let (mut sec_nonce1, pub_nonce1) = key_agg_cache.nonce_gen(&secp, session_id, sec_key1, msg, None) + /// .expect("non zero session id"); + /// + /// // Generate the nonce for party with `keypair2`. + /// let sec_key2 = SecretKey::from_keypair(&keypair2); + /// let (mut sec_nonce2, pub_nonce2) = key_agg_cache.nonce_gen(&secp, session_id, sec_key2, msg, None) + /// .expect("non zero session id"); + /// + /// let aggnonce = MusigAggNonce::new(&secp, &[pub_nonce1, pub_nonce2]); + /// + /// + /// let session = MusigSession::new( + /// &secp, + /// &key_agg_cache, + /// aggnonce, + /// msg, + /// None, + /// ); + /// + /// let partial_sig1 = session.partial_sign( + /// &secp, + /// &mut sec_nonce1, + /// &keypair1, + /// &key_agg_cache, + /// ).unwrap(); + /// + /// // Other party creates the other partial signature + /// let partial_sig2 = session.partial_sign( + /// &secp, + /// &mut sec_nonce2, + /// &keypair2, + /// &key_agg_cache, + /// ).unwrap(); + /// + /// let nonce_parity = session.nonce_parity(); + /// let schnorr_sig = session.partial_sig_agg(&[partial_sig1, partial_sig2]); + /// + /// // Get the final schnorr signature + /// assert!(secp.verify_schnorr(&schnorr_sig, &msg, &agg_pk).is_ok()) + /// # } + /// ``` + pub fn partial_sig_agg(&self, partial_sigs: &[MusigPartialSignature]) -> schnorr::Signature { + let part_sigs = partial_sigs.iter().map(|s| s.as_ptr()).collect::>(); + let mut sig = [0u8; 64]; + unsafe { + if ffi::secp256k1_musig_partial_sig_agg( + ffi::secp256k1_context_no_precomp, + sig.as_mut_ptr(), + self.as_ptr(), + part_sigs.as_ptr(), + part_sigs.len(), + ) == 0 + { + // All arguments are well-typed partial signatures + unreachable!("Impossible to construct invalid(not well-typed) partial signatures") + } else { + // Resulting signature must be well-typed. Does not mean that will be succeed verification + schnorr::Signature::from_slice(&sig) + .expect("Resulting signature must be well-typed") + } + } + } + + /// Extracts the nonce_parity bit from a session + /// + /// This is used for adaptor signatures + /// + /// Example: + /// + /// ```rust + /// # # [cfg(any(test, feature = "rand-std"))] { + /// # use secp256k1_zkp::rand::{thread_rng, RngCore}; + /// # use secp256k1_zkp::{Message, KeyPair, MusigAggNonce, MusigKeyAggCache, MusigSession, Secp256k1, SecretKey, XOnlyPublicKey}; + /// let secp = Secp256k1::new(); + /// let sec_key = SecretKey::from_slice([1; 32].as_ref()).unwrap(); + /// let keypair = KeyPair::from_secret_key(&secp, sec_key); + /// let pub_key = XOnlyPublicKey::from_keypair(&keypair); + /// let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key]); + /// let msg = Message::from_slice(&[3; 32]).unwrap(); + /// let session_id = [1; 32]; + /// let (mut secnonce, pubnonce) = key_agg_cache.nonce_gen(&secp, session_id, sec_key, msg, None) + /// .expect("non zero session id"); + /// let aggnonce = MusigAggNonce::new(&secp, &[pubnonce]); + /// let session = MusigSession::new( + /// &secp, + /// &key_agg_cache, + /// aggnonce, + /// msg, + /// None, + /// ); + /// + /// let _parity = session.nonce_parity(); + /// # } + /// ``` + pub fn nonce_parity(&self) -> Parity { + let mut parity = 0i32; + unsafe { + if ffi::secp256k1_musig_nonce_parity( + ffi::secp256k1_context_no_precomp, + &mut parity, + self.as_ptr(), + ) == 0 + { + unreachable!("Well-typed and valid arguments to the function") + } else { + Parity::from_i32(parity).expect("Parity guaranteed to be binary") + } + } + } + + /// Get a const pointer to the inner MusigSession + pub fn as_ptr(&self) -> *const ffi::MusigSession { + &self.0 + } + + /// Get a mut pointer to the inner MusigSession + pub fn as_mut_ptr(&mut self) -> *mut ffi::MusigSession { + &mut self.0 + } +} + +/// Musig tweaking related errors. +#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)] +pub enum MusigSignError { + /// Musig nonce re-used. + /// When creating a partial signature, nonce is cleared and set to all zeros. + /// This error is caused when we create a partial signature with zero nonce. + // Note: Because of the current borrowing rules around nonce, this should be impossible. + // Maybe, we can just unwrap this and not have error at all? + NonceReuse, +} + +#[cfg(feature = "std")] +impl std::error::Error for MusigSignError {} + +impl fmt::Display for MusigSignError { + fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> { + match self { + MusigSignError::NonceReuse => write!(f, "Musig signing nonce re-used"), + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + use rand::{thread_rng, RngCore}; + use {KeyPair, XOnlyPublicKey}; + + #[test] + fn test_key_agg_cache() { + let secp = Secp256k1::new(); + let mut sec_bytes = [0; 32]; + thread_rng().fill_bytes(&mut sec_bytes); + let sec_key = SecretKey::from_slice(&sec_bytes).unwrap(); + let keypair = KeyPair::from_secret_key(&secp, sec_key); + let pub_key = XOnlyPublicKey::from_keypair(&keypair); + + let _key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key, pub_key]); + } + + #[test] + fn test_nonce_parsing() { + let secp = Secp256k1::new(); + let sec_bytes = [1; 32]; + let sec_key = SecretKey::from_slice(&sec_bytes).unwrap(); + let keypair = KeyPair::from_secret_key(&secp, sec_key); + let pub_key = XOnlyPublicKey::from_keypair(&keypair); + + let key_agg_cache = MusigKeyAggCache::new(&secp, &[pub_key, pub_key]); + let msg = Message::from_slice(&[3; 32]).unwrap(); + let session_id = [2; 32]; + let sec_key = SecretKey::from_slice(&[4; 32]).unwrap(); + let (_secnonce, pubnonce) = key_agg_cache + .nonce_gen(&secp, session_id, sec_key, msg, None) + .expect("non zero session id"); + let pubnonce_ser = pubnonce.serialize(); + let parsed_pubnonce = MusigPubNonce::from_slice(&pubnonce_ser).unwrap(); + + assert_eq!(parsed_pubnonce, pubnonce); + } +}