Merge rust-bitcoin/rust-secp256k1#376: Add examples to `key` module

aa828f01a5 Improve documentation in the key module (Tobin Harding)
9e46d6f122 Add examples to types and methods in key module (Tobin Harding)
a7f3d9bcfd Improve key module docs (Tobin Harding)
6d23614467 Improve lib.rs rustdocs (Tobin Harding)
4c4268f1ad Improve docs on method generate_keypair (Tobin Harding)

Pull request description:

  This PR is an initial attempt to more thoroughly test our public API.

  Add examples to various types/methods/functions in the key module.

  I'm not entirely sure when is enough, do we want an example on every single public method, function, and type or is this overkill. In this PR I tried to find a balance by doing ever method/function that took an argument that is a custom type from this lib. I think this should be extended to include return values too though ...

  Thanks to @thomaseizinger for the idea!

  First 2 patches are docs improvements to `lib.rs`.

ACKs for top commit:
  apoelstra:
    ACK aa828f01a5

Tree-SHA512: 9383ad263469f98ce7e988d47edc1482a09a0ce82f43d3991bd80aabdf621430f4a3c86be4debf33232dcb1d60d3e81f2c6d930ea7de7aa0e34b037accd7bc98
This commit is contained in:
Andrew Poelstra 2022-01-25 12:34:38 +00:00
commit f7d637e6aa
No known key found for this signature in database
GPG Key ID: C588D63CE41B97C1
2 changed files with 277 additions and 66 deletions

View File

@ -13,7 +13,8 @@
// If not, see <http://creativecommons.org/publicdomain/zero/1.0/>.
//
//! # Public and secret keys
//! Public and secret keys.
//!
#[cfg(any(test, feature = "rand"))] use rand::Rng;
@ -27,7 +28,20 @@ use Verification;
use constants;
use ffi::{self, CPtr};
/// Secret 256-bit key used as `x` in an ECDSA signature
/// Secret 256-bit key used as `x` in an ECDSA signature.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # #[cfg(feature="rand")] {
/// use secp256k1::{rand, Secp256k1, SecretKey};
///
/// let secp = Secp256k1::new();
/// let secret_key = SecretKey::new(&mut rand::thread_rng());
/// # }
/// ```
pub struct SecretKey([u8; constants::SECRET_KEY_SIZE]);
impl_array_newtype!(SecretKey, u8, constants::SECRET_KEY_SIZE);
impl_display_secret!(SecretKey);
@ -49,7 +63,19 @@ pub const ONE_KEY: SecretKey = SecretKey([0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 1]);
/// A Secp256k1 public key, used for verification of signatures
/// A Secp256k1 public key, used for verification of signatures.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// use secp256k1::{SecretKey, Secp256k1, PublicKey};
///
/// let secp = Secp256k1::new();
/// let secret_key = SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order");
/// let public_key = PublicKey::from_secret_key(&secp, &secret_key);
/// ```
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)]
#[repr(transparent)]
pub struct PublicKey(ffi::PublicKey);
@ -97,6 +123,15 @@ fn random_32_bytes<R: Rng + ?Sized>(rng: &mut R) -> [u8; 32] {
impl SecretKey {
/// Generates a new random secret key.
///
/// # Examples
///
/// ```
/// # #[cfg(feature="rand")] {
/// use secp256k1::{rand, SecretKey};
/// let secret_key = SecretKey::new(&mut rand::thread_rng());
/// # }
/// ```
#[inline]
#[cfg(any(test, feature = "rand"))]
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
@ -114,7 +149,14 @@ impl SecretKey {
SecretKey(data)
}
/// Converts a `SECRET_KEY_SIZE`-byte slice to a secret key
/// Converts a `SECRET_KEY_SIZE`-byte slice to a secret key.
///
/// # Examples
///
/// ```
/// use secp256k1::SecretKey;
/// let sk = SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order");
/// ```
#[inline]
pub fn from_slice(data: &[u8])-> Result<SecretKey, Error> {
match data.len() {
@ -136,7 +178,19 @@ impl SecretKey {
}
}
/// Creates a new secret key using data from BIP-340 [`KeyPair`]
/// Creates a new secret key using data from BIP-340 [`KeyPair`].
///
/// # Examples
///
/// ```
/// # #[cfg(feature="rand")] {
/// use secp256k1::{rand, Secp256k1, SecretKey, KeyPair};
///
/// let secp = Secp256k1::new();
/// let key_pair = KeyPair::new(&secp, &mut rand::thread_rng());
/// let secret_key = SecretKey::from_keypair(&key_pair);
/// # }
/// ```
#[inline]
pub fn from_keypair(keypair: &KeyPair) -> Self {
let mut sk = [0u8; constants::SECRET_KEY_SIZE];
@ -151,7 +205,7 @@ impl SecretKey {
SecretKey(sk)
}
/// Serialize the secret key as byte value
/// Serializes the secret key as byte value.
#[inline]
pub fn serialize_secret(&self) -> [u8; constants::SECRET_KEY_SIZE] {
self.0
@ -172,9 +226,12 @@ impl SecretKey {
}
#[inline]
/// Adds one secret key to another, modulo the curve order. WIll
/// return an error if the resulting key would be invalid or if
/// the tweak was not a 32-byte length slice.
/// Adds one secret key to another, modulo the curve order.
///
/// # Errors
///
/// Returns an error if the resulting key would be invalid or if the tweak was not a 32-byte
/// length slice.
pub fn add_assign(
&mut self,
other: &[u8],
@ -253,19 +310,31 @@ impl<'de> ::serde::Deserialize<'de> for SecretKey {
}
impl PublicKey {
/// Obtains a raw const pointer suitable for use with FFI functions
/// Obtains a raw const pointer suitable for use with FFI functions.
#[inline]
pub fn as_ptr(&self) -> *const ffi::PublicKey {
&self.0
}
/// Obtains a raw mutable pointer suitable for use with FFI functions
/// Obtains a raw mutable pointer suitable for use with FFI functions.
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut ffi::PublicKey {
&mut self.0
}
/// Creates a new public key from a secret key.
/// Creates a new public key from a [`SecretKey`].
///
/// # Examples
///
/// ```
/// # #[cfg(feature="rand")] {
/// use secp256k1::{rand, Secp256k1, SecretKey, PublicKey};
///
/// let secp = Secp256k1::new();
/// let secret_key = SecretKey::new(&mut rand::thread_rng());
/// let public_key = PublicKey::from_secret_key(&secp, &secret_key);
/// # }
/// ```
#[inline]
pub fn from_secret_key<C: Signing>(secp: &Secp256k1<C>,
sk: &SecretKey)
@ -280,7 +349,7 @@ impl PublicKey {
}
}
/// Creates a public key directly from a slice
/// Creates a public key directly from a slice.
#[inline]
pub fn from_slice(data: &[u8]) -> Result<PublicKey, Error> {
if data.is_empty() {return Err(Error::InvalidPublicKey);}
@ -302,6 +371,18 @@ impl PublicKey {
}
/// Creates a new compressed public key using data from BIP-340 [`KeyPair`].
///
/// # Examples
///
/// ```
/// # #[cfg(feature="rand")] {
/// use secp256k1::{rand, Secp256k1, PublicKey, KeyPair};
///
/// let secp = Secp256k1::new();
/// let key_pair = KeyPair::new(&secp, &mut rand::thread_rng());
/// let public_key = PublicKey::from_keypair(&key_pair);
/// # }
/// ```
#[inline]
pub fn from_keypair(keypair: &KeyPair) -> Self {
unsafe {
@ -317,9 +398,8 @@ impl PublicKey {
}
#[inline]
/// Serialize the key as a byte-encoded pair of values. In compressed form
/// the y-coordinate is represented by only a single bit, as x determines
/// it up to one bit.
/// Serializes the key as a byte-encoded pair of values. In compressed form the y-coordinate is
/// represented by only a single bit, as x determines it up to one bit.
pub fn serialize(&self) -> [u8; constants::PUBLIC_KEY_SIZE] {
let mut ret = [0u8; constants::PUBLIC_KEY_SIZE];
@ -338,7 +418,7 @@ impl PublicKey {
ret
}
/// Serialize the key as a byte-encoded pair of values, in uncompressed form
/// Serializes the key as a byte-encoded pair of values, in uncompressed form.
pub fn serialize_uncompressed(&self) -> [u8; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE] {
let mut ret = [0u8; constants::UNCOMPRESSED_PUBLIC_KEY_SIZE];
@ -358,8 +438,7 @@ impl PublicKey {
}
#[inline]
/// Negates the pk to the pk `self` in place
/// Will return an error if the pk would be invalid.
/// Negates the public key in place.
pub fn negate_assign<C: Verification>(
&mut self,
secp: &Secp256k1<C>
@ -371,9 +450,12 @@ impl PublicKey {
}
#[inline]
/// Adds the pk corresponding to `other` to the pk `self` in place
/// Will return an error if the resulting key would be invalid or
/// if the tweak was not a 32-byte length slice.
/// Adds the `other` public key to `self` in place.
///
/// # Errors
///
/// Returns an error if the resulting key would be invalid or if the tweak was not a 32-byte
/// length slice.
pub fn add_exp_assign<C: Verification>(
&mut self,
secp: &Secp256k1<C>,
@ -392,9 +474,12 @@ impl PublicKey {
}
#[inline]
/// Muliplies the pk `self` in place by the scalar `other`
/// Will return an error if the resulting key would be invalid or
/// if the tweak was not a 32-byte length slice.
/// Muliplies the public key in place by the scalar `other`.
///
/// # Errors
///
/// Returns an error if the resulting key would be invalid or if the tweak was not a 32-byte
/// length slice.
pub fn mul_assign<C: Verification>(
&mut self,
secp: &Secp256k1<C>,
@ -412,17 +497,52 @@ impl PublicKey {
}
}
/// Adds a second key to this one, returning the sum. Returns an error if
/// the result would be the point at infinity, i.e. we are adding this point
/// to its own negation
/// Adds a second key to this one, returning the sum.
///
/// # Errors
///
/// If the result would be the point at infinity, i.e. adding this point to its own negation.
///
/// # Examples
///
/// ```
/// # #[cfg(feature="rand")] {
/// use secp256k1::{rand, Secp256k1};
///
/// let secp = Secp256k1::new();
/// let mut rng = rand::thread_rng();
/// let (_, pk1) = secp.generate_keypair(&mut rng);
/// let (_, pk2) = secp.generate_keypair(&mut rng);
/// let sum = pk1.combine(&pk2).expect("It's improbable to fail for 2 random public keys");
/// # }
///```
pub fn combine(&self, other: &PublicKey) -> Result<PublicKey, Error> {
PublicKey::combine_keys(&[self, other])
}
/// Adds the keys in the provided slice together, returning the sum. Returns
/// an error if the result would be the point at infinity, i.e. we are adding
/// a point to its own negation, if the provided slice has no element in it,
/// or if the number of element it contains is greater than i32::MAX.
/// Adds the keys in the provided slice together, returning the sum.
///
/// # Errors
///
/// Errors under any of the following conditions:
/// - The result would be the point at infinity, i.e. adding a point to its own negation.
/// - The provided slice is empty.
/// - The number of elements in the provided slice is greater than `i32::MAX`.
///
/// # Examples
///
/// ```
/// # #[cfg(feature="rand")] {
/// use secp256k1::{rand, Secp256k1, PublicKey};
///
/// let secp = Secp256k1::new();
/// let mut rng = rand::thread_rng();
/// let (_, pk1) = secp.generate_keypair(&mut rng);
/// let (_, pk2) = secp.generate_keypair(&mut rng);
/// let (_, pk3) = secp.generate_keypair(&mut rng);
/// let sum = PublicKey::combine_keys(&[&pk1, &pk2, &pk3]).expect("It's improbable to fail for 3 random public keys");
/// # }
/// ```
pub fn combine_keys(keys: &[&PublicKey]) -> Result<PublicKey, Error> {
use core::mem::transmute;
use core::i32::MAX;
@ -514,6 +634,7 @@ impl Ord for PublicKey {
/// Opaque data structure that holds a keypair consisting of a secret and a public key.
///
/// # Serde support
///
/// [`Serialize`] and [`Deserialize`] are not implemented for this type, even with the `serde`
/// feature active. This is due to security considerations, see the [`serde_keypair`] documentation
/// for details.
@ -522,29 +643,41 @@ impl Ord for PublicKey {
/// deserialized by annotating them with `#[serde(with = "secp256k1::serde_keypair")]`
/// inside structs or enums for which [`Serialize`] and [`Deserialize`] are being derived.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # #[cfg(feature="rand")] {
/// use secp256k1::{rand, KeyPair, Secp256k1};
///
/// let secp = Secp256k1::new();
/// let (secret_key, public_key) = secp.generate_keypair(&mut rand::thread_rng());
/// let key_pair = KeyPair::from_secret_key(&secp, secret_key);
/// # }
/// ```
/// [`Deserialize`]: serde::Deserialize
/// [`Serialize`]: serde::Serialize
// Should secrets implement Copy: https://github.com/rust-bitcoin/rust-secp256k1/issues/363
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct KeyPair(ffi::KeyPair);
impl_display_secret!(KeyPair);
impl KeyPair {
/// Obtains a raw const pointer suitable for use with FFI functions
/// Obtains a raw const pointer suitable for use with FFI functions.
#[inline]
pub fn as_ptr(&self) -> *const ffi::KeyPair {
&self.0
}
/// Obtains a raw mutable pointer suitable for use with FFI functions
/// Obtains a raw mutable pointer suitable for use with FFI functions.
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut ffi::KeyPair {
&mut self.0
}
/// Creates a Schnorr KeyPair directly from generic Secp256k1 secret key
/// Creates a Schnorr [`KeyPair`] directly from generic Secp256k1 secret key.
///
/// # Panic
/// # Panics
///
/// Panics if internal representation of the provided [`SecretKey`] does not hold correct secret
/// key value obtained from Secp256k1 library previously, specifically when secret key value is
@ -564,7 +697,7 @@ impl KeyPair {
}
}
/// Creates a Schnorr KeyPair directly from a secret key slice.
/// Creates a Schnorr [`KeyPair`] directly from a secret key slice.
///
/// # Errors
///
@ -589,7 +722,7 @@ impl KeyPair {
}
}
/// Creates a Schnorr KeyPair directly from a secret key string
/// Creates a Schnorr [`KeyPair`] directly from a secret key string.
///
/// # Errors
///
@ -606,6 +739,16 @@ impl KeyPair {
}
/// Generates a new random secret key.
/// # Examples
///
/// ```
/// # #[cfg(feature="rand")] {
/// use secp256k1::{rand, Secp256k1, SecretKey, KeyPair};
///
/// let secp = Secp256k1::new();
/// let key_pair = KeyPair::new(&secp, &mut rand::thread_rng());
/// # }
/// ```
#[inline]
#[cfg(any(test, feature = "rand"))]
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]
@ -625,20 +768,38 @@ impl KeyPair {
}
}
/// Serialize the key pair as a secret key byte value
/// Serializes the key pair as a secret key byte value.
#[inline]
pub fn serialize_secret(&self) -> [u8; constants::SECRET_KEY_SIZE] {
*SecretKey::from_keypair(self).as_ref()
}
/// Tweak a keypair by adding the given tweak to the secret key and updating the public key
/// Tweaks a keypair by adding the given tweak to the secret key and updating the public key
/// accordingly.
///
/// Will return an error if the resulting key would be invalid or if the tweak was not a 32-byte
/// # Errors
///
/// Returns an error if the resulting key would be invalid or if the tweak was not a 32-byte
/// length slice.
///
/// NB: Will not error if the tweaked public key has an odd value and can't be used for
/// BIP 340-342 purposes.
///
/// # Examples
///
/// ```
/// # #[cfg(feature="rand")] {
/// use secp256k1::{Secp256k1, KeyPair};
/// use secp256k1::rand::{RngCore, thread_rng};
///
/// let secp = Secp256k1::new();
/// let mut tweak = [0u8; 32];
/// thread_rng().fill_bytes(&mut tweak);
///
/// let mut key_pair = KeyPair::new(&secp, &mut thread_rng());
/// key_pair.tweak_add_assign(&secp, &tweak).expect("Improbable to fail with a randomly generated tweak");
/// # }
/// ```
// TODO: Add checked implementation
#[inline]
pub fn tweak_add_assign<C: Verification>(
@ -744,7 +905,21 @@ impl<'de> ::serde::Deserialize<'de> for KeyPair {
}
}
/// A x-only public key, used for verification of Schnorr signatures and serialized according to BIP-340.
/// An x-only public key, used for verification of Schnorr signatures and serialized according to BIP-340.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// # #[cfg(feature="rand")] {
/// use secp256k1::{rand, Secp256k1, KeyPair, XOnlyPublicKey};
///
/// let secp = Secp256k1::new();
/// let key_pair = KeyPair::new(&secp, &mut rand::thread_rng());
/// let xonly = XOnlyPublicKey::from_keypair(&key_pair);
/// # }
/// ```
#[derive(Copy, Clone, PartialEq, Eq, Debug, PartialOrd, Ord, Hash)]
pub struct XOnlyPublicKey(ffi::XOnlyPublicKey);
@ -778,13 +953,13 @@ impl str::FromStr for XOnlyPublicKey {
}
impl XOnlyPublicKey {
/// Obtains a raw const pointer suitable for use with FFI functions
/// Obtains a raw const pointer suitable for use with FFI functions.
#[inline]
pub fn as_ptr(&self) -> *const ffi::XOnlyPublicKey {
&self.0
}
/// Obtains a raw mutable pointer suitable for use with FFI functions
/// Obtains a raw mutable pointer suitable for use with FFI functions.
#[inline]
pub fn as_mut_ptr(&mut self) -> *mut ffi::XOnlyPublicKey {
&mut self.0
@ -807,12 +982,12 @@ impl XOnlyPublicKey {
}
}
/// Creates a Schnorr public key directly from a slice
/// Creates a Schnorr public key directly from a slice.
///
/// # Errors
///
/// Returns [`Error::InvalidPublicKey`] if the length of the data slice is not 32 bytes or the
/// slice does not represent a valid Secp256k1 point x coordinate
/// slice does not represent a valid Secp256k1 point x coordinate.
#[inline]
pub fn from_slice(data: &[u8]) -> Result<XOnlyPublicKey, Error> {
if data.is_empty() || data.len() != constants::SCHNORRSIG_PUBLIC_KEY_SIZE {
@ -835,7 +1010,7 @@ impl XOnlyPublicKey {
}
#[inline]
/// Serialize the key as a byte-encoded x coordinate value (32 bytes).
/// Serializes the key as a byte-encoded x coordinate value (32 bytes).
pub fn serialize(&self) -> [u8; constants::SCHNORRSIG_PUBLIC_KEY_SIZE] {
let mut ret = [0u8; constants::SCHNORRSIG_PUBLIC_KEY_SIZE];
@ -850,15 +1025,34 @@ impl XOnlyPublicKey {
ret
}
/// Tweak an x-only PublicKey by adding the generator multiplied with the given tweak to it.
/// Tweaks an x-only PublicKey by adding the generator multiplied with the given tweak to it.
///
/// # Returns
///
/// # Return
/// An opaque type representing the parity of the tweaked key, this should be provided to
/// `tweak_add_check` which can be used to verify a tweak more efficiently than regenerating
/// it and checking equality.
///
/// # Error
/// # Errors
///
/// If the resulting key would be invalid or if the tweak was not a 32-byte length slice.
///
/// # Examples
///
/// ```
/// # #[cfg(feature="rand")] {
/// use secp256k1::{Secp256k1, KeyPair};
/// use secp256k1::rand::{RngCore, thread_rng};
///
/// let secp = Secp256k1::new();
/// let mut tweak = [0u8; 32];
/// thread_rng().fill_bytes(&mut tweak);
///
/// let mut key_pair = KeyPair::new(&secp, &mut thread_rng());
/// let mut public_key = key_pair.public_key();
/// public_key.tweak_add_assign(&secp, &tweak).expect("Improbable to fail with a randomly generated tweak");
/// # }
/// ```
pub fn tweak_add_assign<V: Verification>(
&mut self,
secp: &Secp256k1<V>,
@ -895,18 +1089,37 @@ impl XOnlyPublicKey {
}
}
/// Verify that a tweak produced by `tweak_add_assign` was computed correctly.
/// Verifies that a tweak produced by [`XOnlyPublicKey::tweak_add_assign`] was computed correctly.
///
/// Should be called on the original untweaked key. Takes the tweaked key and
/// output parity from `tweak_add_assign` as input.
/// Should be called on the original untweaked key. Takes the tweaked key and output parity from
/// [`XOnlyPublicKey::tweak_add_assign`] as input.
///
/// Currently this is not much more efficient than just recomputing the tweak
/// and checking equality. However, in future this API will support batch
/// verification, which is significantly faster, so it is wise to design
/// protocols with this in mind.
/// Currently this is not much more efficient than just recomputing the tweak and checking
/// equality. However, in future this API will support batch verification, which is
/// significantly faster, so it is wise to design protocols with this in mind.
///
/// # Returns
///
/// # Return
/// True if tweak and check is successful, false otherwise.
///
/// # Examples
///
/// ```
/// # #[cfg(feature="rand")] {
/// use secp256k1::{Secp256k1, KeyPair};
/// use secp256k1::rand::{thread_rng, RngCore};
///
/// let secp = Secp256k1::new();
/// let mut tweak = [0u8; 32];
/// thread_rng().fill_bytes(&mut tweak);
///
/// let mut key_pair = KeyPair::new(&secp, &mut thread_rng());
/// let mut public_key = key_pair.public_key();
/// let original = public_key;
/// let parity = public_key.tweak_add_assign(&secp, &tweak).expect("Improbable to fail with a randomly generated tweak");
/// assert!(original.tweak_add_check(&secp, &public_key, parity, tweak));
/// # }
/// ```
pub fn tweak_add_check<V: Verification>(
&self,
secp: &Secp256k1<V>,
@ -1039,7 +1252,7 @@ impl CPtr for XOnlyPublicKey {
}
}
/// Creates a new Schnorr public key from a FFI x-only public key
/// Creates a new Schnorr public key from a FFI x-only public key.
impl From<ffi::XOnlyPublicKey> for XOnlyPublicKey {
#[inline]
fn from(pk: ffi::XOnlyPublicKey) -> XOnlyPublicKey {

View File

@ -58,13 +58,13 @@
//! Alternately, keys and messages can be parsed from slices, like
//!
//! ```rust
//! use self::secp256k1::{Secp256k1, Message, SecretKey, PublicKey};
//! use secp256k1::{Secp256k1, Message, SecretKey, PublicKey};
//!
//! let secp = Secp256k1::new();
//! let secret_key = SecretKey::from_slice(&[0xcd; 32]).expect("32 bytes, within curve order");
//! let public_key = PublicKey::from_secret_key(&secp, &secret_key);
//! // This is unsafe unless the supplied byte slice is the output of a cryptographic hash function.
//! // See the above example for how to use this library together with bitcoin_hashes.
//! // See the above example for how to use this library together with `bitcoin_hashes`.
//! let message = Message::from_slice(&[0xab; 32]).expect("32 bytes");
//!
//! let sig = secp.sign_ecdsa(&message, &secret_key);
@ -439,10 +439,8 @@ impl<C: Context> Secp256k1<C> {
}
impl<C: Signing> Secp256k1<C> {
/// Generates a random keypair. Convenience function for `key::SecretKey::new`
/// and `key::PublicKey::from_secret_key`; call those functions directly for
/// batch key generation. Requires a signing-capable context. Requires compilation
/// with the "rand" feature.
/// Generates a random keypair. Convenience function for [`SecretKey::new`] and
/// [`PublicKey::from_secret_key`].
#[inline]
#[cfg(any(test, feature = "rand"))]
#[cfg_attr(docsrs, doc(cfg(feature = "rand")))]