From fa8af6e1273503b6ebb71969a5260dc705ebe305 Mon Sep 17 00:00:00 2001 From: Shing Him Ng Date: Sun, 20 Jul 2025 16:56:41 -0500 Subject: [PATCH] Introduce Arbitrary crate and add PublicKey impl --- Cargo-minimal.lock | 7 +++++++ Cargo-recent.lock | 7 +++++++ Cargo.toml | 2 ++ src/key.rs | 22 ++++++++++++++++++++++ 4 files changed, 38 insertions(+) diff --git a/Cargo-minimal.lock b/Cargo-minimal.lock index 1b944bb7b..43c294268 100644 --- a/Cargo-minimal.lock +++ b/Cargo-minimal.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" + [[package]] name = "arrayvec" version = "0.7.6" @@ -224,6 +230,7 @@ checksum = "ea6a9290e3c9cf0f18145ef7ffa62d68ee0bf5fcd651017e586dc7fd5da448c2" name = "secp256k1" version = "0.31.1" dependencies = [ + "arbitrary", "bincode", "bitcoin_hashes", "getrandom", diff --git a/Cargo-recent.lock b/Cargo-recent.lock index c33758e78..ffdc4af39 100644 --- a/Cargo-recent.lock +++ b/Cargo-recent.lock @@ -2,6 +2,12 @@ # It is not intended for manual editing. version = 3 +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" + [[package]] name = "arrayvec" version = "0.7.6" @@ -215,6 +221,7 @@ checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" name = "secp256k1" version = "0.31.1" dependencies = [ + "arbitrary", "bincode", "bitcoin_hashes", "getrandom", diff --git a/Cargo.toml b/Cargo.toml index 71acbf4a2..948a5769d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -32,10 +32,12 @@ global-context = ["std"] # if you are doing a no-std build, then this feature does nothing # and is not necessary.) global-context-less-secure = ["global-context"] +arbitrary = ["dep:arbitrary"] [dependencies] secp256k1-sys = { version = "0.11.0", default-features = false, path = "./secp256k1-sys" } +arbitrary = { version = "1.4", optional = true } hashes = { package = "bitcoin_hashes", version = "0.14", default-features = false, optional = true } rand = { version = "0.9", default-features = false, optional = true } serde = { version = "1.0.103", default-features = false, optional = true } diff --git a/src/key.rs b/src/key.rs index e446afe7f..5bb9c3810 100644 --- a/src/key.rs +++ b/src/key.rs @@ -6,6 +6,8 @@ use core::ops::{self, BitXor}; use core::{fmt, ptr, str}; +#[cfg(feature = "arbitrary")] +use arbitrary::{Arbitrary, Unstructured}; use secp256k1_sys::secp256k1_ec_pubkey_sort; #[cfg(feature = "serde")] use serde::ser::SerializeTuple; @@ -1653,6 +1655,26 @@ impl Secp256k1 { } } +#[cfg(feature = "arbitrary")] +impl<'a> Arbitrary<'a> for PublicKey { + fn arbitrary(u: &mut Unstructured<'a>) -> arbitrary::Result { + let mut bytes = [0u8; 33]; + loop { + // Unstructured::fill_buffer pads the buffer with zeroes if it runs out of data + if u.len() < 33 { + return Err(arbitrary::Error::NotEnoughData); + } + + bytes[0] = if u.arbitrary::()? { 0x02 } else { 0x03 }; + u.fill_buffer(&mut bytes[1..])?; + + if let Ok(pk) = PublicKey::from_slice(&bytes) { + return Ok(pk); + } + } + } +} + #[cfg(test)] #[allow(unused_imports)] mod test {