2022-07-13 02:38:29 -04:00
|
|
|
use std::{io::{Read, Cursor}, sync::{Arc, RwLock}, collections::HashMap};
|
2022-04-30 04:32:19 -04:00
|
|
|
|
2022-05-06 07:33:08 -04:00
|
|
|
use rand_core::{RngCore, CryptoRng, SeedableRng};
|
|
|
|
|
use rand_chacha::ChaCha12Rng;
|
2022-04-30 04:32:19 -04:00
|
|
|
|
2022-05-17 19:15:53 -04:00
|
|
|
use curve25519_dalek::{traits::Identity, scalar::Scalar, edwards::{EdwardsPoint, CompressedEdwardsY}};
|
2022-04-30 04:32:19 -04:00
|
|
|
|
2022-06-24 18:58:24 -04:00
|
|
|
use transcript::{Transcript, RecommendedTranscript};
|
2022-06-24 08:40:14 -04:00
|
|
|
use frost::{
|
2022-06-24 19:47:19 -04:00
|
|
|
curve::Ed25519,
|
2022-06-28 00:06:12 -04:00
|
|
|
FrostError, FrostKeys,
|
2022-06-24 08:40:14 -04:00
|
|
|
sign::{
|
|
|
|
|
PreprocessMachine, SignMachine, SignatureMachine,
|
|
|
|
|
AlgorithmMachine, AlgorithmSignMachine, AlgorithmSignatureMachine
|
|
|
|
|
}
|
|
|
|
|
};
|
2022-05-03 07:20:24 -04:00
|
|
|
|
2022-04-30 04:32:19 -04:00
|
|
|
use crate::{
|
2022-05-22 02:24:24 -04:00
|
|
|
random_scalar, ringct::{clsag::{ClsagInput, ClsagDetails, ClsagMultisig}, bulletproofs::Bulletproofs, RctPrunable},
|
|
|
|
|
transaction::{Input, Transaction},
|
2022-04-30 04:32:19 -04:00
|
|
|
rpc::Rpc,
|
2022-05-22 01:56:17 -04:00
|
|
|
wallet::{TransactionError, SignableTransaction, Decoys, key_image_sort, uniqueness}
|
2022-04-30 04:32:19 -04:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pub struct TransactionMachine {
|
|
|
|
|
signable: SignableTransaction,
|
2022-05-24 21:41:14 -04:00
|
|
|
i: u16,
|
|
|
|
|
included: Vec<u16>,
|
2022-06-24 18:58:24 -04:00
|
|
|
transcript: RecommendedTranscript,
|
2022-05-06 07:33:08 -04:00
|
|
|
|
2022-05-06 19:07:37 -04:00
|
|
|
decoys: Vec<Decoys>,
|
|
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
inputs: Vec<Arc<RwLock<Option<ClsagDetails>>>>,
|
|
|
|
|
clsags: Vec<AlgorithmMachine<Ed25519, ClsagMultisig>>
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
pub struct TransactionSignMachine {
|
|
|
|
|
signable: SignableTransaction,
|
|
|
|
|
i: u16,
|
|
|
|
|
included: Vec<u16>,
|
2022-06-24 18:58:24 -04:00
|
|
|
transcript: RecommendedTranscript,
|
2022-06-24 08:40:14 -04:00
|
|
|
|
|
|
|
|
decoys: Vec<Decoys>,
|
2022-05-22 01:56:17 -04:00
|
|
|
|
2022-06-05 07:33:15 -04:00
|
|
|
inputs: Vec<Arc<RwLock<Option<ClsagDetails>>>>,
|
2022-06-24 08:40:14 -04:00
|
|
|
clsags: Vec<AlgorithmSignMachine<Ed25519, ClsagMultisig>>,
|
|
|
|
|
|
|
|
|
|
our_preprocess: Vec<u8>
|
|
|
|
|
}
|
2022-05-06 19:07:37 -04:00
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
pub struct TransactionSignatureMachine {
|
|
|
|
|
tx: Transaction,
|
|
|
|
|
clsags: Vec<AlgorithmSignatureMachine<Ed25519, ClsagMultisig>>
|
2022-04-30 04:32:19 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl SignableTransaction {
|
2022-06-19 12:03:01 -04:00
|
|
|
pub async fn multisig(
|
|
|
|
|
self,
|
2022-04-30 04:32:19 -04:00
|
|
|
rpc: &Rpc,
|
2022-06-28 00:06:12 -04:00
|
|
|
keys: FrostKeys<Ed25519>,
|
2022-06-24 18:58:24 -04:00
|
|
|
mut transcript: RecommendedTranscript,
|
2022-06-05 15:10:50 -04:00
|
|
|
height: usize,
|
2022-05-24 21:41:14 -04:00
|
|
|
mut included: Vec<u16>
|
2022-04-30 04:32:19 -04:00
|
|
|
) -> Result<TransactionMachine, TransactionError> {
|
2022-05-04 06:24:52 -04:00
|
|
|
let mut inputs = vec![];
|
2022-05-18 00:53:13 -04:00
|
|
|
for _ in 0 .. self.inputs.len() {
|
|
|
|
|
// Doesn't resize as that will use a single Rc for the entire Vec
|
2022-06-05 07:33:15 -04:00
|
|
|
inputs.push(Arc::new(RwLock::new(None)));
|
2022-05-18 00:53:13 -04:00
|
|
|
}
|
2022-05-06 19:07:37 -04:00
|
|
|
let mut clsags = vec![];
|
2022-05-04 06:24:52 -04:00
|
|
|
|
|
|
|
|
// Create a RNG out of the input shared keys, which either requires the view key or being every
|
|
|
|
|
// sender, and the payments (address and amount), which a passive adversary may be able to know
|
2022-05-06 07:33:08 -04:00
|
|
|
// depending on how these transactions are coordinated
|
2022-06-03 01:37:12 -04:00
|
|
|
// Being every sender would already let you note rings which happen to use your transactions
|
|
|
|
|
// multiple times, already breaking privacy there
|
2022-05-06 07:33:08 -04:00
|
|
|
|
2022-05-23 03:24:33 -04:00
|
|
|
transcript.domain_separate(b"monero_transaction");
|
2022-05-22 01:56:17 -04:00
|
|
|
// Include the height we're using for our data
|
|
|
|
|
// The data itself will be included, making this unnecessary, yet a lot of this is technically
|
|
|
|
|
// unnecessary. Anything which further increases security at almost no cost should be followed
|
|
|
|
|
transcript.append_message(b"height", &u64::try_from(height).unwrap().to_le_bytes());
|
2022-05-06 19:07:37 -04:00
|
|
|
// Also include the spend_key as below only the key offset is included, so this confirms the sum product
|
|
|
|
|
// Useful as confirming the sum product confirms the key image, further guaranteeing the one time
|
|
|
|
|
// properties noted below
|
|
|
|
|
transcript.append_message(b"spend_key", &keys.group_key().0.compress().to_bytes());
|
2022-04-30 04:32:19 -04:00
|
|
|
for input in &self.inputs {
|
2022-05-06 07:33:08 -04:00
|
|
|
// These outputs can only be spent once. Therefore, it forces all RNGs derived from this
|
|
|
|
|
// transcript (such as the one used to create one time keys) to be unique
|
2022-05-21 15:33:35 -04:00
|
|
|
transcript.append_message(b"input_hash", &input.tx);
|
2022-05-26 03:51:27 -04:00
|
|
|
transcript.append_message(b"input_output_index", &[input.o]);
|
2022-05-06 07:33:08 -04:00
|
|
|
// Not including this, with a doxxed list of payments, would allow brute forcing the inputs
|
|
|
|
|
// to determine RNG seeds and therefore the true spends
|
2022-05-06 01:35:23 -04:00
|
|
|
transcript.append_message(b"input_shared_key", &input.key_offset.to_bytes());
|
2022-05-04 06:24:52 -04:00
|
|
|
}
|
|
|
|
|
for payment in &self.payments {
|
2022-06-28 00:01:20 -04:00
|
|
|
transcript.append_message(b"payment_address", &payment.0.to_string().as_bytes());
|
2022-05-06 01:35:23 -04:00
|
|
|
transcript.append_message(b"payment_amount", &payment.1.to_le_bytes());
|
2022-05-04 06:24:52 -04:00
|
|
|
}
|
2022-04-30 04:32:19 -04:00
|
|
|
|
2022-05-24 21:41:14 -04:00
|
|
|
// Sort included before cloning it around
|
|
|
|
|
included.sort_unstable();
|
|
|
|
|
|
2022-05-04 06:24:52 -04:00
|
|
|
for (i, input) in self.inputs.iter().enumerate() {
|
2022-06-09 04:05:57 -04:00
|
|
|
// Check this the right set of keys
|
|
|
|
|
let offset = keys.offset(dalek_ff_group::Scalar(input.key_offset));
|
|
|
|
|
if offset.group_key().0 != input.key {
|
|
|
|
|
Err(TransactionError::WrongPrivateKey)?;
|
|
|
|
|
}
|
|
|
|
|
|
2022-04-30 04:32:19 -04:00
|
|
|
clsags.push(
|
|
|
|
|
AlgorithmMachine::new(
|
2022-05-21 15:33:35 -04:00
|
|
|
ClsagMultisig::new(
|
2022-05-06 07:33:08 -04:00
|
|
|
transcript.clone(),
|
2022-07-12 01:28:01 -04:00
|
|
|
input.key,
|
2022-05-18 00:53:13 -04:00
|
|
|
inputs[i].clone()
|
2022-04-30 04:32:19 -04:00
|
|
|
).map_err(|e| TransactionError::MultisigError(e))?,
|
2022-06-09 04:05:57 -04:00
|
|
|
Arc::new(offset),
|
2022-05-24 21:41:14 -04:00
|
|
|
&included
|
2022-04-30 04:32:19 -04:00
|
|
|
).map_err(|e| TransactionError::FrostError(e))?
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-09 04:05:57 -04:00
|
|
|
// Select decoys
|
|
|
|
|
// Ideally, this would be done post entropy, instead of now, yet doing so would require sign
|
|
|
|
|
// to be async which isn't preferable. This should be suitably competent though
|
|
|
|
|
// While this inability means we can immediately create the input, moving it out of the
|
|
|
|
|
// Arc RwLock, keeping it within an Arc RwLock keeps our options flexible
|
|
|
|
|
let decoys = Decoys::select(
|
|
|
|
|
// Using a seeded RNG with a specific height, committed to above, should make these decoys
|
|
|
|
|
// committed to. They'll also be committed to later via the TX message as a whole
|
|
|
|
|
&mut ChaCha12Rng::from_seed(transcript.rng_seed(b"decoys")),
|
|
|
|
|
rpc,
|
|
|
|
|
height,
|
|
|
|
|
&self.inputs
|
|
|
|
|
).await.map_err(|e| TransactionError::RpcError(e))?;
|
|
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
Ok(
|
|
|
|
|
TransactionMachine {
|
|
|
|
|
signable: self,
|
|
|
|
|
i: keys.params().i(),
|
|
|
|
|
included,
|
|
|
|
|
transcript,
|
2022-05-06 19:07:37 -04:00
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
decoys,
|
2022-05-06 19:07:37 -04:00
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
inputs,
|
|
|
|
|
clsags
|
|
|
|
|
}
|
|
|
|
|
)
|
2022-04-30 04:32:19 -04:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
impl PreprocessMachine for TransactionMachine {
|
2022-04-30 04:32:19 -04:00
|
|
|
type Signature = Transaction;
|
2022-06-24 08:40:14 -04:00
|
|
|
type SignMachine = TransactionSignMachine;
|
2022-04-30 04:32:19 -04:00
|
|
|
|
|
|
|
|
fn preprocess<R: RngCore + CryptoRng>(
|
2022-06-24 08:40:14 -04:00
|
|
|
mut self,
|
2022-04-30 04:32:19 -04:00
|
|
|
rng: &mut R
|
2022-06-24 08:40:14 -04:00
|
|
|
) -> (TransactionSignMachine, Vec<u8>) {
|
2022-04-30 04:32:19 -04:00
|
|
|
// Iterate over each CLSAG calling preprocess
|
2022-07-12 01:28:01 -04:00
|
|
|
let mut serialized = Vec::with_capacity(
|
|
|
|
|
// D_{G, H}, E_{G, H}, DLEqs, key image addendum
|
|
|
|
|
self.clsags.len() * ((2 * (32 + 32)) + (2 * (32 + 32)) + ClsagMultisig::serialized_len())
|
|
|
|
|
);
|
2022-06-24 08:40:14 -04:00
|
|
|
let clsags = self.clsags.drain(..).map(|clsag| {
|
|
|
|
|
let (clsag, preprocess) = clsag.preprocess(rng);
|
|
|
|
|
serialized.extend(&preprocess);
|
|
|
|
|
clsag
|
|
|
|
|
}).collect();
|
|
|
|
|
let our_preprocess = serialized.clone();
|
2022-04-30 04:32:19 -04:00
|
|
|
|
2022-05-22 01:56:17 -04:00
|
|
|
// We could add further entropy here, and previous versions of this library did so
|
|
|
|
|
// As of right now, the multisig's key, the inputs being spent, and the FROST data itself
|
|
|
|
|
// will be used for RNG seeds. In order to recreate these RNG seeds, breaking privacy,
|
|
|
|
|
// counterparties must have knowledge of the multisig, either the view key or access to the
|
|
|
|
|
// coordination layer, and then access to the actual FROST signing process
|
|
|
|
|
// If the commitments are sent in plain text, then entropy here also would be, making it not
|
|
|
|
|
// increase privacy. If they're not sent in plain text, or are otherwise inaccessible, they
|
|
|
|
|
// already offer sufficient entropy. That's why further entropy is not included
|
2022-04-30 04:32:19 -04:00
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
(
|
|
|
|
|
TransactionSignMachine {
|
|
|
|
|
signable: self.signable,
|
|
|
|
|
i: self.i,
|
|
|
|
|
included: self.included,
|
|
|
|
|
transcript: self.transcript,
|
|
|
|
|
|
|
|
|
|
decoys: self.decoys,
|
|
|
|
|
|
|
|
|
|
inputs: self.inputs,
|
|
|
|
|
clsags,
|
|
|
|
|
|
|
|
|
|
our_preprocess,
|
|
|
|
|
},
|
|
|
|
|
serialized
|
|
|
|
|
)
|
2022-04-30 04:32:19 -04:00
|
|
|
}
|
2022-06-24 08:40:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl SignMachine<Transaction> for TransactionSignMachine {
|
|
|
|
|
type SignatureMachine = TransactionSignatureMachine;
|
2022-04-30 04:32:19 -04:00
|
|
|
|
2022-07-13 02:38:29 -04:00
|
|
|
fn sign<Re: Read>(
|
2022-06-24 08:40:14 -04:00
|
|
|
mut self,
|
2022-07-13 02:38:29 -04:00
|
|
|
mut commitments: HashMap<u16, Re>,
|
2022-06-10 00:20:59 -04:00
|
|
|
msg: &[u8]
|
2022-06-24 08:40:14 -04:00
|
|
|
) -> Result<(TransactionSignatureMachine, Vec<u8>), FrostError> {
|
2022-06-10 00:20:59 -04:00
|
|
|
if msg.len() != 0 {
|
|
|
|
|
Err(
|
|
|
|
|
FrostError::InternalError(
|
2022-07-13 02:38:29 -04:00
|
|
|
"message was passed to the TransactionMachine when it generates its own"
|
2022-06-10 00:20:59 -04:00
|
|
|
)
|
|
|
|
|
)?;
|
|
|
|
|
}
|
|
|
|
|
|
2022-07-12 01:28:01 -04:00
|
|
|
// FROST commitments and their DLEqs, and the image and its DLEq
|
2022-07-13 02:38:29 -04:00
|
|
|
const CLSAG_LEN: usize = (2 * (32 + 32)) + (2 * (32 + 32)) + ClsagMultisig::serialized_len();
|
2022-05-22 01:56:17 -04:00
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
// Convert the unified commitments to a Vec of the individual commitments
|
|
|
|
|
let mut images = vec![EdwardsPoint::identity(); self.clsags.len()];
|
2022-07-13 02:38:29 -04:00
|
|
|
let mut commitments = (0 .. self.clsags.len()).map(|c| {
|
|
|
|
|
let mut buf = [0; CLSAG_LEN];
|
|
|
|
|
(&self.included).iter().map(|l| {
|
|
|
|
|
// Add all commitments to the transcript for their entropy
|
|
|
|
|
// While each CLSAG will do this as they need to for security, they have their own transcripts
|
|
|
|
|
// cloned from this TX's initial premise's transcript. For our TX transcript to have the CLSAG
|
|
|
|
|
// data for entropy, it'll have to be added ourselves here
|
|
|
|
|
self.transcript.append_message(b"participant", &(*l).to_be_bytes());
|
|
|
|
|
if *l == self.i {
|
|
|
|
|
buf.copy_from_slice(self.our_preprocess.drain(.. CLSAG_LEN).as_slice());
|
|
|
|
|
} else {
|
|
|
|
|
commitments.get_mut(l).ok_or(FrostError::MissingParticipant(*l))?
|
|
|
|
|
.read_exact(&mut buf).map_err(|_| FrostError::InvalidCommitment(*l))?;
|
|
|
|
|
}
|
|
|
|
|
self.transcript.append_message(b"preprocess", &buf);
|
|
|
|
|
|
|
|
|
|
// While here, calculate the key image
|
|
|
|
|
// Clsag will parse/calculate/validate this as needed, yet doing so here as well provides
|
|
|
|
|
// the easiest API overall, as this is where the TX is (which needs the key images in its
|
|
|
|
|
// message), along with where the outputs are determined (where our outputs may need
|
|
|
|
|
// these in order to guarantee uniqueness)
|
2022-06-24 08:40:14 -04:00
|
|
|
images[c] += CompressedEdwardsY(
|
2022-07-13 02:38:29 -04:00
|
|
|
buf[(CLSAG_LEN - 96) .. (CLSAG_LEN - 64)].try_into().map_err(|_| FrostError::InvalidCommitment(*l))?
|
2022-05-24 21:41:14 -04:00
|
|
|
).decompress().ok_or(FrostError::InvalidCommitment(*l))?;
|
2022-07-13 02:38:29 -04:00
|
|
|
|
|
|
|
|
Ok((*l, Cursor::new(buf)))
|
|
|
|
|
}).collect::<Result<HashMap<_, _>, _>>()
|
|
|
|
|
}).collect::<Result<Vec<_>, _>>()?;
|
|
|
|
|
|
|
|
|
|
// Remove our preprocess which shouldn't be here. It was just the easiest way to implement the
|
|
|
|
|
// above
|
|
|
|
|
for map in commitments.iter_mut() {
|
|
|
|
|
map.remove(&self.i);
|
2022-05-18 00:53:13 -04:00
|
|
|
}
|
2022-04-30 04:32:19 -04:00
|
|
|
|
2022-05-22 01:56:17 -04:00
|
|
|
// Create the actual transaction
|
2022-06-24 08:40:14 -04:00
|
|
|
let output_masks;
|
2022-05-22 01:56:17 -04:00
|
|
|
let mut tx = {
|
2022-06-24 08:40:14 -04:00
|
|
|
let mut sorted_images = images.clone();
|
|
|
|
|
sorted_images.sort_by(key_image_sort);
|
2022-05-22 01:56:17 -04:00
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
let commitments;
|
|
|
|
|
(commitments, output_masks) = self.signable.prepare_outputs(
|
2022-05-31 02:12:14 -04:00
|
|
|
&mut ChaCha12Rng::from_seed(self.transcript.rng_seed(b"tx_keys")),
|
2022-05-22 01:56:17 -04:00
|
|
|
uniqueness(
|
|
|
|
|
&images.iter().map(|image| Input::ToKey {
|
|
|
|
|
amount: 0,
|
|
|
|
|
key_offsets: vec![],
|
|
|
|
|
key_image: *image
|
|
|
|
|
}).collect::<Vec<_>>()
|
|
|
|
|
)
|
2022-06-19 12:03:01 -04:00
|
|
|
);
|
2022-05-22 01:56:17 -04:00
|
|
|
|
|
|
|
|
self.signable.prepare_transaction(
|
|
|
|
|
&commitments,
|
|
|
|
|
Bulletproofs::new(
|
2022-05-31 02:12:14 -04:00
|
|
|
&mut ChaCha12Rng::from_seed(self.transcript.rng_seed(b"bulletproofs")),
|
2022-05-22 01:56:17 -04:00
|
|
|
&commitments
|
|
|
|
|
).unwrap()
|
|
|
|
|
)
|
|
|
|
|
};
|
|
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
// Sort the inputs, as expected
|
|
|
|
|
let mut sorted = Vec::with_capacity(self.clsags.len());
|
|
|
|
|
while self.clsags.len() != 0 {
|
2022-05-18 00:53:13 -04:00
|
|
|
sorted.push((
|
2022-06-24 08:40:14 -04:00
|
|
|
images.swap_remove(0),
|
2022-05-18 00:53:13 -04:00
|
|
|
self.signable.inputs.swap_remove(0),
|
|
|
|
|
self.decoys.swap_remove(0),
|
|
|
|
|
self.inputs.swap_remove(0),
|
|
|
|
|
self.clsags.swap_remove(0),
|
|
|
|
|
commitments.swap_remove(0)
|
|
|
|
|
));
|
|
|
|
|
}
|
2022-06-24 08:40:14 -04:00
|
|
|
sorted.sort_by(|x, y| key_image_sort(&x.0, &y.0));
|
2022-05-18 00:53:13 -04:00
|
|
|
|
2022-05-31 02:12:14 -04:00
|
|
|
let mut rng = ChaCha12Rng::from_seed(self.transcript.rng_seed(b"pseudo_out_masks"));
|
2022-05-18 00:53:13 -04:00
|
|
|
let mut sum_pseudo_outs = Scalar::zero();
|
|
|
|
|
while sorted.len() != 0 {
|
|
|
|
|
let value = sorted.remove(0);
|
2022-05-06 19:07:37 -04:00
|
|
|
|
|
|
|
|
let mut mask = random_scalar(&mut rng);
|
2022-05-18 00:53:13 -04:00
|
|
|
if sorted.len() == 0 {
|
2022-06-24 08:40:14 -04:00
|
|
|
mask = output_masks - sum_pseudo_outs;
|
2022-05-06 19:07:37 -04:00
|
|
|
} else {
|
|
|
|
|
sum_pseudo_outs += mask;
|
|
|
|
|
}
|
|
|
|
|
|
2022-05-18 00:53:13 -04:00
|
|
|
tx.prefix.inputs.push(
|
2022-05-21 15:33:35 -04:00
|
|
|
Input::ToKey {
|
|
|
|
|
amount: 0,
|
2022-06-24 08:40:14 -04:00
|
|
|
key_offsets: value.2.offsets.clone(),
|
|
|
|
|
key_image: value.0
|
2022-05-18 00:53:13 -04:00
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
|
2022-06-05 07:33:15 -04:00
|
|
|
*value.3.write().unwrap() = Some(
|
|
|
|
|
ClsagDetails::new(
|
|
|
|
|
ClsagInput::new(
|
2022-06-24 08:40:14 -04:00
|
|
|
value.1.commitment,
|
|
|
|
|
value.2
|
2022-06-05 07:33:15 -04:00
|
|
|
).map_err(|_| panic!("Signing an input which isn't present in the ring we created for it"))?,
|
|
|
|
|
mask
|
2022-05-06 19:07:37 -04:00
|
|
|
)
|
|
|
|
|
);
|
|
|
|
|
|
2022-05-18 00:53:13 -04:00
|
|
|
self.clsags.push(value.4);
|
|
|
|
|
commitments.push(value.5);
|
2022-04-30 04:32:19 -04:00
|
|
|
}
|
|
|
|
|
|
2022-05-21 15:33:35 -04:00
|
|
|
let msg = tx.signature_hash();
|
2022-04-30 04:32:19 -04:00
|
|
|
|
|
|
|
|
// Iterate over each CLSAG calling sign
|
|
|
|
|
let mut serialized = Vec::with_capacity(self.clsags.len() * 32);
|
2022-06-24 08:40:14 -04:00
|
|
|
let clsags = self.clsags.drain(..).map(|clsag| {
|
|
|
|
|
let (clsag, share) = clsag.sign(commitments.remove(0), &msg)?;
|
|
|
|
|
serialized.extend(&share);
|
|
|
|
|
Ok(clsag)
|
|
|
|
|
}).collect::<Result<_, _>>()?;
|
2022-04-30 04:32:19 -04:00
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
Ok((TransactionSignatureMachine { tx, clsags }, serialized))
|
2022-04-30 04:32:19 -04:00
|
|
|
}
|
2022-06-24 08:40:14 -04:00
|
|
|
}
|
2022-04-30 04:32:19 -04:00
|
|
|
|
2022-06-24 08:40:14 -04:00
|
|
|
impl SignatureMachine<Transaction> for TransactionSignatureMachine {
|
2022-07-13 02:38:29 -04:00
|
|
|
fn complete<Re: Read>(self, mut shares: HashMap<u16, Re>) -> Result<Transaction, FrostError> {
|
2022-06-24 08:40:14 -04:00
|
|
|
let mut tx = self.tx;
|
2022-05-21 15:33:35 -04:00
|
|
|
match tx.rct_signatures.prunable {
|
|
|
|
|
RctPrunable::Null => panic!("Signing for RctPrunable::Null"),
|
|
|
|
|
RctPrunable::Clsag { ref mut clsags, ref mut pseudo_outs, .. } => {
|
2022-06-24 08:40:14 -04:00
|
|
|
for clsag in self.clsags {
|
|
|
|
|
let (clsag, pseudo_out) = clsag.complete(
|
2022-07-13 02:38:29 -04:00
|
|
|
shares.iter_mut().map(|(l, shares)| {
|
|
|
|
|
let mut buf = [0; 32];
|
|
|
|
|
shares.read_exact(&mut buf).map_err(|_| FrostError::InvalidShare(*l))?;
|
|
|
|
|
Ok((*l, Cursor::new(buf)))
|
|
|
|
|
}).collect::<Result<HashMap<_, _>, _>>()?
|
2022-06-24 08:40:14 -04:00
|
|
|
)?;
|
2022-05-21 15:33:35 -04:00
|
|
|
clsags.push(clsag);
|
|
|
|
|
pseudo_outs.push(pseudo_out);
|
|
|
|
|
}
|
|
|
|
|
}
|
2022-04-30 04:32:19 -04:00
|
|
|
}
|
|
|
|
|
Ok(tx)
|
|
|
|
|
}
|
|
|
|
|
}
|