|
| 1 | +//! Provide all the binding functions and methods to be used from the perspective of the |
| 2 | +//! silentpayments sender. |
| 3 | +use crate::ffi::{self, CPtr}; |
| 4 | +use crate::{Keypair, PublicKey, Secp256k1, SecretKey, XOnlyPublicKey}; |
| 5 | + |
| 6 | +/// Struct to store recipient data. |
| 7 | +#[repr(transparent)] |
| 8 | +#[derive(Debug, Clone, Copy, Eq, PartialEq)] |
| 9 | +pub struct Recipient(ffi::silentpayments::Recipient); |
| 10 | + |
| 11 | +impl Recipient { |
| 12 | + /// Get a new [`Recipient`] |
| 13 | + pub fn new(scan_pubkey: &PublicKey, spend_pubkey: &PublicKey, index: usize) -> Self { |
| 14 | + unsafe { |
| 15 | + Self(ffi::silentpayments::Recipient::new( |
| 16 | + &*scan_pubkey.as_c_ptr(), |
| 17 | + &*spend_pubkey.as_c_ptr(), |
| 18 | + index, |
| 19 | + )) |
| 20 | + } |
| 21 | + } |
| 22 | +} |
| 23 | + |
| 24 | +/// Error creating silent payment ouput x-only public keys. |
| 25 | +#[derive(Debug, Clone, Copy, Eq, PartialEq, PartialOrd, Ord, Hash)] |
| 26 | +pub struct DerivationError; |
| 27 | + |
| 28 | +#[cfg(feature = "std")] |
| 29 | +impl std::error::Error for DerivationError {} |
| 30 | + |
| 31 | +impl core::fmt::Display for DerivationError { |
| 32 | + fn fmt(&self, f: &mut core::fmt::Formatter) -> Result<(), core::fmt::Error> { |
| 33 | + match self { |
| 34 | + DerivationError => { |
| 35 | + write!(f, "Failed while deriving silent payment output x-only public keys") |
| 36 | + } |
| 37 | + } |
| 38 | + } |
| 39 | +} |
| 40 | + |
| 41 | +/// Create Silent Payment outputs for recipient(s). |
| 42 | +/// |
| 43 | +/// Given a list of n secret keys a_1...a_n (one for each silent payment |
| 44 | +/// eligible input to spend), a serialized outpoint, and a list of recipients, |
| 45 | +/// create the taproot outputs. Inputs with conditional branches or multiple |
| 46 | +/// public keys are excluded from silent payments eligible inputs; see BIP352 |
| 47 | +/// for more information. |
| 48 | +/// |
| 49 | +/// `lexmin_outpoint` refers to the smallest outpoint lexicographically |
| 50 | +/// from the transaction inputs (both silent payments eligible and non-eligible |
| 51 | +/// inputs). This value MUST be the smallest outpoint out of ALL of the |
| 52 | +/// transaction inputs, otherwise the recipient will be unable to find the |
| 53 | +/// payment. Determining the smallest outpoint from the list of transaction |
| 54 | +/// inputs is the responsibility of the caller. It is strongly recommended |
| 55 | +/// that implementations ensure they are doing this correctly by using the |
| 56 | +/// test vectors from BIP352. |
| 57 | +/// |
| 58 | +/// When creating more than one generated output, all of the generated outputs |
| 59 | +/// MUST be included in the final transaction. Dropping any of the generated |
| 60 | +/// outputs from the final transaction may make all or some of the outputs |
| 61 | +/// unfindable by the recipient. |
| 62 | +/// |
| 63 | +/// # Arguments |
| 64 | +/// * `recipients` - slice of [`Recipient`] mutable references. The index indicates |
| 65 | +/// its position in the original ordering. The recipients will be grouped by scan public key in |
| 66 | +/// place (as specified in BIP0352), but generated outputs are saved in the `generated_outputs` |
| 67 | +/// array to match the original ordering (using the index field). This ensures the caller is able |
| 68 | +/// to match the generated outputs to the correct silent payment addresses. The same recipient can |
| 69 | +/// be passed multiple times to create multiple outputs for the same recipient. |
| 70 | +/// * `lexmin_outpoint` - serialized (36-byte) smallest outpoint (lexicographically) from the transaction inputs |
| 71 | +/// * `taproot_seckeys` - optionally a slice of [`Keypair`] references of taproot inputs. |
| 72 | +/// * `plain_seckeys` - optionally a slice of [`SecretKey`] references of non-taproot inputs. |
| 73 | +/// |
| 74 | +/// # Returns |
| 75 | +/// A vector to xonly public keys, one per recipient. Outputs are ordered to match the original |
| 76 | +/// ordering of the recipient objects, i.e., the vector element zero is the generated output for |
| 77 | +/// the [`Recipient`] struct with index = 0. |
| 78 | +/// |
| 79 | +/// # Errors |
| 80 | +/// * [`SilentpaymentDerivationError`] - This is expected only with an adversarially chosen |
| 81 | +/// recipient spend key. Specifically, failure occurs when: |
| 82 | +/// - Input secret keys sum to 0 or the negation of a spend key (negligible probability if at least |
| 83 | +/// one of the input secret keys is uniformly random and independent of all other keys). |
| 84 | +/// - A hash output is not a valid scalar (negligible probability per hash evaluation). |
| 85 | +pub fn create_outputs( |
| 86 | + recipients: &[&mut Recipient], |
| 87 | + lexmin_outpoint: &[u8; 36], |
| 88 | + taproot_seckeys: Option<&[&Keypair]>, |
| 89 | + plain_seckeys: Option<&[&SecretKey]>, |
| 90 | +) -> Result<Vec<XOnlyPublicKey>, DerivationError> { |
| 91 | + let mut seed = [0u8; 32]; |
| 92 | + |
| 93 | + let (ffi_taproot_seckeys, n_taproot_seckeys) = match taproot_seckeys { |
| 94 | + Some(keys) => { |
| 95 | + for key in keys { |
| 96 | + for (this, that) in seed.iter_mut().zip(key.to_secret_bytes().iter()) { |
| 97 | + *this ^= *that; |
| 98 | + } |
| 99 | + } |
| 100 | + (keys.as_c_ptr() as *const *const ffi::Keypair, keys.len()) |
| 101 | + } |
| 102 | + None => |
| 103 | + (core::ptr::null::<*const *const ffi::Keypair>() as *const *const ffi::Keypair, 0_usize), |
| 104 | + }; |
| 105 | + |
| 106 | + let (ffi_plain_seckeys, n_plain_seckeys) = match plain_seckeys { |
| 107 | + Some(keys) => { |
| 108 | + for key in keys { |
| 109 | + for (this, that) in seed.iter_mut().zip(key.to_secret_bytes().iter()) { |
| 110 | + *this ^= *that; |
| 111 | + } |
| 112 | + } |
| 113 | + ( |
| 114 | + keys.iter() |
| 115 | + .map(|key| key.to_secret_bytes().as_c_ptr()) |
| 116 | + .collect::<Vec<*const u8>>() |
| 117 | + .as_c_ptr(), |
| 118 | + keys.len(), |
| 119 | + ) |
| 120 | + } |
| 121 | + None => (core::ptr::null::<*const *const u8>() as *const *const u8, 0_usize), |
| 122 | + }; |
| 123 | + |
| 124 | + let mut ffi_generated_outputs = unsafe { vec![ffi::XOnlyPublicKey::new(); recipients.len()] }; |
| 125 | + let mut ffi_generated_outputs_refs = |
| 126 | + ffi_generated_outputs.iter_mut().map(|k| k as *mut _).collect::<Vec<_>>(); |
| 127 | + |
| 128 | + let res = crate::with_global_context( |
| 129 | + |secp: &Secp256k1<crate::AllPreallocated>| unsafe { |
| 130 | + ffi::silentpayments::secp256k1_silentpayments_sender_create_outputs( |
| 131 | + secp.ctx().as_ptr(), |
| 132 | + ffi_generated_outputs_refs.as_mut_c_ptr(), |
| 133 | + recipients.as_c_ptr() as *const *mut ffi::silentpayments::Recipient, |
| 134 | + recipients.len(), |
| 135 | + lexmin_outpoint.as_c_ptr(), |
| 136 | + ffi_taproot_seckeys, |
| 137 | + n_taproot_seckeys, |
| 138 | + ffi_plain_seckeys, |
| 139 | + n_plain_seckeys, |
| 140 | + ) |
| 141 | + }, |
| 142 | + Some(&seed), |
| 143 | + ); |
| 144 | + |
| 145 | + if res == 1 { |
| 146 | + let generated_outputs = { |
| 147 | + let mut generated_outputs_drop = core::mem::ManuallyDrop::new(ffi_generated_outputs); |
| 148 | + let (ptr, len, cap) = ( |
| 149 | + generated_outputs_drop.as_mut_c_ptr(), |
| 150 | + generated_outputs_drop.len(), |
| 151 | + generated_outputs_drop.capacity(), |
| 152 | + ); |
| 153 | + // Transfer control of memory to new vector |
| 154 | + unsafe { Vec::from_raw_parts(ptr as *mut XOnlyPublicKey, len, cap) } |
| 155 | + }; |
| 156 | + Ok(generated_outputs) |
| 157 | + } else { |
| 158 | + Err(DerivationError) |
| 159 | + } |
| 160 | +} |
0 commit comments