mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-10 21:19:24 +00:00
Apply an initial set of rustfmt rules
This commit is contained in:
@@ -2,7 +2,10 @@ use std::string::ToString;
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, edwards::{EdwardsPoint, CompressedEdwardsY}};
|
||||
use curve25519_dalek::{
|
||||
constants::ED25519_BASEPOINT_TABLE,
|
||||
edwards::{EdwardsPoint, CompressedEdwardsY},
|
||||
};
|
||||
|
||||
use base58_monero::base58::{encode_check, decode_check};
|
||||
|
||||
@@ -12,14 +15,14 @@ use crate::wallet::ViewPair;
|
||||
pub enum Network {
|
||||
Mainnet,
|
||||
Testnet,
|
||||
Stagenet
|
||||
Stagenet,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub enum AddressType {
|
||||
Standard,
|
||||
Integrated([u8; 8]),
|
||||
Subaddress
|
||||
Subaddress,
|
||||
}
|
||||
|
||||
impl AddressType {
|
||||
@@ -27,7 +30,7 @@ impl AddressType {
|
||||
match network {
|
||||
Network::Mainnet => (18, 19, 42),
|
||||
Network::Testnet => (53, 54, 63),
|
||||
Network::Stagenet => (24, 25, 36)
|
||||
Network::Stagenet => (24, 25, 36),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,7 +39,7 @@ impl AddressType {
|
||||
pub struct AddressMeta {
|
||||
pub network: Network,
|
||||
pub kind: AddressType,
|
||||
pub guaranteed: bool
|
||||
pub guaranteed: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Error, Debug)]
|
||||
@@ -50,7 +53,7 @@ pub enum AddressError {
|
||||
#[error("different network than expected")]
|
||||
DifferentNetwork,
|
||||
#[error("invalid key")]
|
||||
InvalidKey
|
||||
InvalidKey,
|
||||
}
|
||||
|
||||
impl AddressMeta {
|
||||
@@ -59,7 +62,7 @@ impl AddressMeta {
|
||||
let byte = match self.kind {
|
||||
AddressType::Standard => bytes.0,
|
||||
AddressType::Integrated(_) => bytes.1,
|
||||
AddressType::Subaddress => bytes.2
|
||||
AddressType::Subaddress => bytes.2,
|
||||
};
|
||||
byte | (if self.guaranteed { 1 << 7 } else { 0 })
|
||||
}
|
||||
@@ -76,7 +79,7 @@ impl AddressMeta {
|
||||
_ if actual == standard => Some(AddressType::Standard),
|
||||
_ if actual == integrated => Some(AddressType::Integrated([0; 8])),
|
||||
_ if actual == subaddress => Some(AddressType::Subaddress),
|
||||
_ => None
|
||||
_ => None,
|
||||
} {
|
||||
meta = Some(AddressMeta { network, kind, guaranteed });
|
||||
break;
|
||||
@@ -91,19 +94,15 @@ impl AddressMeta {
|
||||
pub struct Address {
|
||||
pub meta: AddressMeta,
|
||||
pub spend: EdwardsPoint,
|
||||
pub view: EdwardsPoint
|
||||
pub view: EdwardsPoint,
|
||||
}
|
||||
|
||||
impl ViewPair {
|
||||
pub fn address(&self, network: Network, kind: AddressType, guaranteed: bool) -> Address {
|
||||
Address {
|
||||
meta: AddressMeta {
|
||||
network,
|
||||
kind,
|
||||
guaranteed
|
||||
},
|
||||
meta: AddressMeta { network, kind, guaranteed },
|
||||
spend: self.spend,
|
||||
view: &self.view * &ED25519_BASEPOINT_TABLE
|
||||
view: &self.view * &ED25519_BASEPOINT_TABLE,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -134,14 +133,18 @@ impl Address {
|
||||
|
||||
let len = match meta.kind {
|
||||
AddressType::Standard | AddressType::Subaddress => 65,
|
||||
AddressType::Integrated(_) => 73
|
||||
AddressType::Integrated(_) => 73,
|
||||
};
|
||||
if raw.len() != len {
|
||||
Err(AddressError::InvalidLength)?;
|
||||
}
|
||||
|
||||
let spend = CompressedEdwardsY(raw[1 .. 33].try_into().unwrap()).decompress().ok_or(AddressError::InvalidKey)?;
|
||||
let view = CompressedEdwardsY(raw[33 .. 65].try_into().unwrap()).decompress().ok_or(AddressError::InvalidKey)?;
|
||||
let spend = CompressedEdwardsY(raw[1 .. 33].try_into().unwrap())
|
||||
.decompress()
|
||||
.ok_or(AddressError::InvalidKey)?;
|
||||
let view = CompressedEdwardsY(raw[33 .. 65].try_into().unwrap())
|
||||
.decompress()
|
||||
.ok_or(AddressError::InvalidKey)?;
|
||||
|
||||
if let AddressType::Integrated(ref mut payment_id) = meta.kind {
|
||||
payment_id.copy_from_slice(&raw[65 .. 73]);
|
||||
|
||||
@@ -7,7 +7,11 @@ use rand_distr::{Distribution, Gamma};
|
||||
|
||||
use curve25519_dalek::edwards::EdwardsPoint;
|
||||
|
||||
use crate::{transaction::RING_LEN, wallet::SpendableOutput, rpc::{RpcError, Rpc}};
|
||||
use crate::{
|
||||
transaction::RING_LEN,
|
||||
wallet::SpendableOutput,
|
||||
rpc::{RpcError, Rpc},
|
||||
};
|
||||
|
||||
const LOCK_WINDOW: usize = 10;
|
||||
const MATURITY: u64 = 60;
|
||||
@@ -30,7 +34,7 @@ async fn select_n<R: RngCore + CryptoRng>(
|
||||
high: u64,
|
||||
per_second: f64,
|
||||
used: &mut HashSet<u64>,
|
||||
count: usize
|
||||
count: usize,
|
||||
) -> Result<Vec<(u64, [EdwardsPoint; 2])>, RpcError> {
|
||||
let mut iters = 0;
|
||||
let mut confirmed = Vec::with_capacity(count);
|
||||
@@ -94,7 +98,7 @@ fn offset(ring: &[u64]) -> Vec<u64> {
|
||||
pub struct Decoys {
|
||||
pub i: u8,
|
||||
pub offsets: Vec<u64>,
|
||||
pub ring: Vec<[EdwardsPoint; 2]>
|
||||
pub ring: Vec<[EdwardsPoint; 2]>,
|
||||
}
|
||||
|
||||
impl Decoys {
|
||||
@@ -106,14 +110,14 @@ impl Decoys {
|
||||
rng: &mut R,
|
||||
rpc: &Rpc,
|
||||
height: usize,
|
||||
inputs: &[SpendableOutput]
|
||||
inputs: &[SpendableOutput],
|
||||
) -> Result<Vec<Decoys>, RpcError> {
|
||||
// Convert the inputs in question to the raw output data
|
||||
let mut outputs = Vec::with_capacity(inputs.len());
|
||||
for input in inputs {
|
||||
outputs.push((
|
||||
rpc.get_o_indexes(input.tx).await?[usize::from(input.o)],
|
||||
[input.key, input.commitment.calculate()]
|
||||
[input.key, input.commitment.calculate()],
|
||||
));
|
||||
}
|
||||
|
||||
@@ -153,17 +157,10 @@ impl Decoys {
|
||||
}
|
||||
|
||||
// Select all decoys for this transaction, assuming we generate a sane transaction
|
||||
// We should almost never naturally generate an insane transaction, hence why this doesn't bother
|
||||
// with an overage
|
||||
let mut decoys = select_n(
|
||||
rng,
|
||||
rpc,
|
||||
height,
|
||||
high,
|
||||
per_second,
|
||||
&mut used,
|
||||
inputs.len() * DECOYS
|
||||
).await?;
|
||||
// We should almost never naturally generate an insane transaction, hence why this doesn't
|
||||
// bother with an overage
|
||||
let mut decoys =
|
||||
select_n(rng, rpc, height, high, per_second, &mut used, inputs.len() * DECOYS).await?;
|
||||
|
||||
let mut res = Vec::with_capacity(inputs.len());
|
||||
for o in outputs {
|
||||
@@ -178,8 +175,8 @@ impl Decoys {
|
||||
// 500 outputs since while itself not being a sufficiently mature blockchain
|
||||
// Considering Monero's p2p layer doesn't actually check transaction sanity, it should be
|
||||
// fine for us to not have perfectly matching rules, especially since this code will infinite
|
||||
// loop if it can't determine sanity, which is possible with sufficient inputs on sufficiently
|
||||
// small chains
|
||||
// loop if it can't determine sanity, which is possible with sufficient inputs on
|
||||
// sufficiently small chains
|
||||
if high > 500 {
|
||||
// Make sure the TX passes the sanity check that the median output is within the last 40%
|
||||
let target_median = high * 3 / 5;
|
||||
@@ -190,28 +187,30 @@ impl Decoys {
|
||||
if removed.0 == o.0 {
|
||||
ring.push(o);
|
||||
} else {
|
||||
// We could not remove this, saving CPU time and removing low values as possibilities, yet
|
||||
// it'd increase the amount of decoys required to create this transaction and some removed
|
||||
// outputs may be the best option (as we drop the first half, not just the bottom n)
|
||||
// We could not remove this, saving CPU time and removing low values as
|
||||
// possibilities, yet it'd increase the amount of decoys required to create this
|
||||
// transaction and some removed outputs may be the best option (as we drop the first
|
||||
// half, not just the bottom n)
|
||||
used.remove(&removed.0);
|
||||
}
|
||||
}
|
||||
|
||||
// Select new outputs until we have a full sized ring again
|
||||
ring.extend(
|
||||
select_n(rng, rpc, height, high, per_second, &mut used, RING_LEN - ring.len()).await?
|
||||
select_n(rng, rpc, height, high, per_second, &mut used, RING_LEN - ring.len()).await?,
|
||||
);
|
||||
ring.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
}
|
||||
|
||||
// The other sanity check rule is about duplicates, yet we already enforce unique ring members
|
||||
// The other sanity check rule is about duplicates, yet we already enforce unique ring
|
||||
// members
|
||||
}
|
||||
|
||||
res.push(Decoys {
|
||||
// Binary searches for the real spend since we don't know where it sorted to
|
||||
i: u8::try_from(ring.partition_point(|x| x.0 < o.0)).unwrap(),
|
||||
offsets: offset(&ring.iter().map(|output| output.0).collect::<Vec<_>>()),
|
||||
ring: ring.iter().map(|output| output.1).collect()
|
||||
ring: ring.iter().map(|output| output.1).collect(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
use curve25519_dalek::{scalar::Scalar, edwards::EdwardsPoint};
|
||||
|
||||
use crate::{
|
||||
hash, hash_to_scalar,
|
||||
serialize::write_varint,
|
||||
transaction::Input
|
||||
};
|
||||
use crate::{hash, hash_to_scalar, serialize::write_varint, transaction::Input};
|
||||
|
||||
pub mod address;
|
||||
|
||||
@@ -30,8 +26,10 @@ pub(crate) fn uniqueness(inputs: &[Input]) -> [u8; 32] {
|
||||
match input {
|
||||
// If Gen, this should be the only input, making this loop somewhat pointless
|
||||
// This works and even if there were somehow multiple inputs, it'd be a false negative
|
||||
Input::Gen(height) => { write_varint(&(*height).try_into().unwrap(), &mut u).unwrap(); },
|
||||
Input::ToKey { key_image, .. } => u.extend(key_image.compress().to_bytes())
|
||||
Input::Gen(height) => {
|
||||
write_varint(&(*height).try_into().unwrap(), &mut u).unwrap();
|
||||
}
|
||||
Input::ToKey { key_image, .. } => u.extend(key_image.compress().to_bytes()),
|
||||
}
|
||||
}
|
||||
hash(&u)
|
||||
@@ -39,7 +37,12 @@ pub(crate) fn uniqueness(inputs: &[Input]) -> [u8; 32] {
|
||||
|
||||
// Hs(8Ra || o) with https://github.com/monero-project/research-lab/issues/103 as an option
|
||||
#[allow(non_snake_case)]
|
||||
pub(crate) fn shared_key(uniqueness: Option<[u8; 32]>, s: Scalar, P: &EdwardsPoint, o: usize) -> Scalar {
|
||||
pub(crate) fn shared_key(
|
||||
uniqueness: Option<[u8; 32]>,
|
||||
s: Scalar,
|
||||
P: &EdwardsPoint,
|
||||
o: usize,
|
||||
) -> Scalar {
|
||||
// uniqueness
|
||||
let mut shared = uniqueness.map_or(vec![], |uniqueness| uniqueness.to_vec());
|
||||
// || 8Ra
|
||||
@@ -69,5 +72,5 @@ pub(crate) fn commitment_mask(shared_key: Scalar) -> Scalar {
|
||||
#[derive(Clone, Copy)]
|
||||
pub struct ViewPair {
|
||||
pub spend: EdwardsPoint,
|
||||
pub view: Scalar
|
||||
pub view: Scalar,
|
||||
}
|
||||
|
||||
@@ -1,10 +1,6 @@
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use curve25519_dalek::{
|
||||
constants::ED25519_BASEPOINT_TABLE,
|
||||
scalar::Scalar,
|
||||
edwards::EdwardsPoint
|
||||
};
|
||||
use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar, edwards::EdwardsPoint};
|
||||
|
||||
use monero::{consensus::deserialize, blockdata::transaction::ExtraField};
|
||||
|
||||
@@ -12,7 +8,7 @@ use crate::{
|
||||
Commitment,
|
||||
serialize::{write_varint, read_32, read_scalar, read_point},
|
||||
transaction::{Timelock, Transaction},
|
||||
wallet::{ViewPair, uniqueness, shared_key, amount_decryption, commitment_mask}
|
||||
wallet::{ViewPair, uniqueness, shared_key, amount_decryption, commitment_mask},
|
||||
};
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
@@ -21,7 +17,7 @@ pub struct SpendableOutput {
|
||||
pub o: u8,
|
||||
pub key: EdwardsPoint,
|
||||
pub key_offset: Scalar,
|
||||
pub commitment: Commitment
|
||||
pub commitment: Commitment,
|
||||
}
|
||||
|
||||
pub struct Timelocked(Timelock, Vec<SpendableOutput>);
|
||||
@@ -61,27 +57,26 @@ impl SpendableOutput {
|
||||
}
|
||||
|
||||
pub fn deserialize<R: std::io::Read>(r: &mut R) -> std::io::Result<SpendableOutput> {
|
||||
Ok(
|
||||
SpendableOutput {
|
||||
tx: read_32(r)?,
|
||||
o: { let mut o = [0; 1]; r.read_exact(&mut o)?; o[0] },
|
||||
key: read_point(r)?,
|
||||
key_offset: read_scalar(r)?,
|
||||
commitment: Commitment::new(
|
||||
read_scalar(r)?,
|
||||
{ let mut amount = [0; 8]; r.read_exact(&mut amount)?; u64::from_le_bytes(amount) }
|
||||
)
|
||||
}
|
||||
)
|
||||
Ok(SpendableOutput {
|
||||
tx: read_32(r)?,
|
||||
o: {
|
||||
let mut o = [0; 1];
|
||||
r.read_exact(&mut o)?;
|
||||
o[0]
|
||||
},
|
||||
key: read_point(r)?,
|
||||
key_offset: read_scalar(r)?,
|
||||
commitment: Commitment::new(read_scalar(r)?, {
|
||||
let mut amount = [0; 8];
|
||||
r.read_exact(&mut amount)?;
|
||||
u64::from_le_bytes(amount)
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Transaction {
|
||||
pub fn scan(
|
||||
&self,
|
||||
view: ViewPair,
|
||||
guaranteed: bool
|
||||
) -> Timelocked {
|
||||
pub fn scan(&self, view: ViewPair, guaranteed: bool) -> Timelocked {
|
||||
let mut extra = vec![];
|
||||
write_varint(&u64::try_from(self.prefix.extra.len()).unwrap(), &mut extra).unwrap();
|
||||
extra.extend(&self.prefix.extra);
|
||||
@@ -110,7 +105,7 @@ impl Transaction {
|
||||
Some(uniqueness(&self.prefix.inputs)).filter(|_| guaranteed),
|
||||
view.view,
|
||||
pubkey,
|
||||
o
|
||||
o,
|
||||
);
|
||||
// P - shared == spend
|
||||
if (output.key - (&key_offset * &ED25519_BASEPOINT_TABLE)) != view.spend {
|
||||
@@ -129,7 +124,7 @@ impl Transaction {
|
||||
Some(amount) => amount_decryption(*amount, key_offset),
|
||||
// This should never happen, yet it may be possible with miner transactions?
|
||||
// Using get just decreases the possibility of a panic and lets us move on in that case
|
||||
None => break
|
||||
None => break,
|
||||
};
|
||||
|
||||
// Rebuild the commitment to verify it
|
||||
@@ -147,7 +142,7 @@ impl Transaction {
|
||||
o: o.try_into().unwrap(),
|
||||
key: output.key,
|
||||
key_offset,
|
||||
commitment
|
||||
commitment,
|
||||
});
|
||||
}
|
||||
// Break to prevent public keys from being included multiple times, triggering multiple
|
||||
|
||||
@@ -3,11 +3,7 @@ use thiserror::Error;
|
||||
use rand_core::{RngCore, CryptoRng};
|
||||
use rand::seq::SliceRandom;
|
||||
|
||||
use curve25519_dalek::{
|
||||
constants::ED25519_BASEPOINT_TABLE,
|
||||
scalar::Scalar,
|
||||
edwards::EdwardsPoint
|
||||
};
|
||||
use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar, edwards::EdwardsPoint};
|
||||
|
||||
use monero::{consensus::Encodable, PublicKey, blockdata::transaction::SubField};
|
||||
|
||||
@@ -15,20 +11,20 @@ use monero::{consensus::Encodable, PublicKey, blockdata::transaction::SubField};
|
||||
use frost::FrostError;
|
||||
|
||||
use crate::{
|
||||
Commitment,
|
||||
random_scalar,
|
||||
Commitment, random_scalar,
|
||||
ringct::{
|
||||
generate_key_image,
|
||||
clsag::{ClsagError, ClsagInput, Clsag},
|
||||
bulletproofs::{MAX_OUTPUTS, Bulletproofs},
|
||||
RctBase, RctPrunable, RctSignatures
|
||||
RctBase, RctPrunable, RctSignatures,
|
||||
},
|
||||
transaction::{Input, Output, Timelock, TransactionPrefix, Transaction},
|
||||
rpc::{Rpc, RpcError},
|
||||
wallet::{
|
||||
address::{AddressType, Address}, SpendableOutput, Decoys,
|
||||
key_image_sort, uniqueness, shared_key, commitment_mask, amount_encryption
|
||||
}
|
||||
address::{AddressType, Address},
|
||||
SpendableOutput, Decoys, key_image_sort, uniqueness, shared_key, commitment_mask,
|
||||
amount_encryption,
|
||||
},
|
||||
};
|
||||
#[cfg(feature = "multisig")]
|
||||
use crate::frost::MultisigError;
|
||||
@@ -44,7 +40,7 @@ struct SendOutput {
|
||||
R: EdwardsPoint,
|
||||
dest: EdwardsPoint,
|
||||
commitment: Commitment,
|
||||
amount: [u8; 8]
|
||||
amount: [u8; 8],
|
||||
}
|
||||
|
||||
impl SendOutput {
|
||||
@@ -52,26 +48,24 @@ impl SendOutput {
|
||||
rng: &mut R,
|
||||
unique: [u8; 32],
|
||||
output: (Address, u64),
|
||||
o: usize
|
||||
o: usize,
|
||||
) -> SendOutput {
|
||||
let r = random_scalar(rng);
|
||||
let shared_key = shared_key(
|
||||
Some(unique).filter(|_| output.0.meta.guaranteed),
|
||||
r,
|
||||
&output.0.view,
|
||||
o
|
||||
);
|
||||
let shared_key =
|
||||
shared_key(Some(unique).filter(|_| output.0.meta.guaranteed), r, &output.0.view, o);
|
||||
|
||||
let spend = output.0.spend;
|
||||
SendOutput {
|
||||
R: match output.0.meta.kind {
|
||||
AddressType::Standard => &r * &ED25519_BASEPOINT_TABLE,
|
||||
AddressType::Integrated(_) => unimplemented!("SendOutput::new doesn't support Integrated addresses"),
|
||||
AddressType::Subaddress => &r * spend
|
||||
AddressType::Integrated(_) => {
|
||||
unimplemented!("SendOutput::new doesn't support Integrated addresses")
|
||||
}
|
||||
AddressType::Subaddress => &r * spend,
|
||||
},
|
||||
dest: ((&shared_key * &ED25519_BASEPOINT_TABLE) + spend),
|
||||
commitment: Commitment::new(commitment_mask(shared_key), output.1),
|
||||
amount: amount_encryption(output.1, shared_key)
|
||||
amount: amount_encryption(output.1, shared_key),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,7 +97,7 @@ pub enum TransactionError {
|
||||
FrostError(FrostError),
|
||||
#[cfg(feature = "multisig")]
|
||||
#[error("multisig error {0}")]
|
||||
MultisigError(MultisigError)
|
||||
MultisigError(MultisigError),
|
||||
}
|
||||
|
||||
async fn prepare_inputs<R: RngCore + CryptoRng>(
|
||||
@@ -111,7 +105,7 @@ async fn prepare_inputs<R: RngCore + CryptoRng>(
|
||||
rpc: &Rpc,
|
||||
inputs: &[SpendableOutput],
|
||||
spend: &Scalar,
|
||||
tx: &mut Transaction
|
||||
tx: &mut Transaction,
|
||||
) -> Result<Vec<(Scalar, EdwardsPoint, ClsagInput)>, TransactionError> {
|
||||
let mut signable = Vec::with_capacity(inputs.len());
|
||||
|
||||
@@ -120,34 +114,33 @@ async fn prepare_inputs<R: RngCore + CryptoRng>(
|
||||
rng,
|
||||
rpc,
|
||||
rpc.get_height().await.map_err(|e| TransactionError::RpcError(e))? - 10,
|
||||
inputs
|
||||
).await.map_err(|e| TransactionError::RpcError(e))?;
|
||||
inputs,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| TransactionError::RpcError(e))?;
|
||||
|
||||
for (i, input) in inputs.iter().enumerate() {
|
||||
signable.push((
|
||||
spend + input.key_offset,
|
||||
generate_key_image(spend + input.key_offset),
|
||||
ClsagInput::new(
|
||||
input.commitment,
|
||||
decoys[i].clone()
|
||||
).map_err(|e| TransactionError::ClsagError(e))?
|
||||
ClsagInput::new(input.commitment, decoys[i].clone())
|
||||
.map_err(|e| TransactionError::ClsagError(e))?,
|
||||
));
|
||||
|
||||
tx.prefix.inputs.push(Input::ToKey {
|
||||
amount: 0,
|
||||
key_offsets: decoys[i].offsets.clone(),
|
||||
key_image: signable[i].1
|
||||
key_image: signable[i].1,
|
||||
});
|
||||
}
|
||||
|
||||
signable.sort_by(|x, y| x.1.compress().to_bytes().cmp(&y.1.compress().to_bytes()).reverse());
|
||||
tx.prefix.inputs.sort_by(|x, y| if let (
|
||||
Input::ToKey { key_image: x, ..},
|
||||
Input::ToKey { key_image: y, ..}
|
||||
) = (x, y) {
|
||||
x.compress().to_bytes().cmp(&y.compress().to_bytes()).reverse()
|
||||
} else {
|
||||
panic!("Input wasn't ToKey")
|
||||
tx.prefix.inputs.sort_by(|x, y| {
|
||||
if let (Input::ToKey { key_image: x, .. }, Input::ToKey { key_image: y, .. }) = (x, y) {
|
||||
x.compress().to_bytes().cmp(&y.compress().to_bytes()).reverse()
|
||||
} else {
|
||||
panic!("Input wasn't ToKey")
|
||||
}
|
||||
});
|
||||
|
||||
Ok(signable)
|
||||
@@ -156,7 +149,7 @@ async fn prepare_inputs<R: RngCore + CryptoRng>(
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
pub struct Fee {
|
||||
pub per_weight: u64,
|
||||
pub mask: u64
|
||||
pub mask: u64,
|
||||
}
|
||||
|
||||
impl Fee {
|
||||
@@ -170,7 +163,7 @@ pub struct SignableTransaction {
|
||||
inputs: Vec<SpendableOutput>,
|
||||
payments: Vec<(Address, u64)>,
|
||||
outputs: Vec<SendOutput>,
|
||||
fee: u64
|
||||
fee: u64,
|
||||
}
|
||||
|
||||
impl SignableTransaction {
|
||||
@@ -178,15 +171,13 @@ impl SignableTransaction {
|
||||
inputs: Vec<SpendableOutput>,
|
||||
mut payments: Vec<(Address, u64)>,
|
||||
change_address: Option<Address>,
|
||||
fee_rate: Fee
|
||||
fee_rate: Fee,
|
||||
) -> Result<SignableTransaction, TransactionError> {
|
||||
// Make sure all addresses are valid
|
||||
let test = |addr: Address| {
|
||||
match addr.meta.kind {
|
||||
AddressType::Standard => Ok(()),
|
||||
AddressType::Integrated(..) => Err(TransactionError::InvalidAddress),
|
||||
AddressType::Subaddress => Ok(())
|
||||
}
|
||||
let test = |addr: Address| match addr.meta.kind {
|
||||
AddressType::Standard => Ok(()),
|
||||
AddressType::Integrated(..) => Err(TransactionError::InvalidAddress),
|
||||
AddressType::Subaddress => Ok(()),
|
||||
};
|
||||
|
||||
for payment in &payments {
|
||||
@@ -229,7 +220,8 @@ impl SignableTransaction {
|
||||
// If we have yet to add a change output, do so if it's economically viable
|
||||
if (!change) && change_address.is_some() && (in_amount != out_amount) {
|
||||
// Check even with the new fee, there's remaining funds
|
||||
let change_fee = fee_rate.calculate(Transaction::fee_weight(inputs.len(), outputs + 1, extra)) - fee;
|
||||
let change_fee =
|
||||
fee_rate.calculate(Transaction::fee_weight(inputs.len(), outputs + 1, extra)) - fee;
|
||||
if (out_amount + change_fee) < in_amount {
|
||||
change = true;
|
||||
outputs += 1;
|
||||
@@ -246,20 +238,13 @@ impl SignableTransaction {
|
||||
payments.push((change_address.unwrap(), in_amount - out_amount));
|
||||
}
|
||||
|
||||
Ok(
|
||||
SignableTransaction {
|
||||
inputs,
|
||||
payments,
|
||||
outputs: vec![],
|
||||
fee
|
||||
}
|
||||
)
|
||||
Ok(SignableTransaction { inputs, payments, outputs: vec![], fee })
|
||||
}
|
||||
|
||||
fn prepare_outputs<R: RngCore + CryptoRng>(
|
||||
&mut self,
|
||||
rng: &mut R,
|
||||
uniqueness: [u8; 32]
|
||||
uniqueness: [u8; 32],
|
||||
) -> (Vec<Commitment>, Scalar) {
|
||||
// Shuffle the payments
|
||||
self.payments.shuffle(rng);
|
||||
@@ -275,29 +260,23 @@ impl SignableTransaction {
|
||||
(commitments, sum)
|
||||
}
|
||||
|
||||
fn prepare_transaction(
|
||||
&self,
|
||||
commitments: &[Commitment],
|
||||
bp: Bulletproofs
|
||||
) -> Transaction {
|
||||
fn prepare_transaction(&self, commitments: &[Commitment], bp: Bulletproofs) -> Transaction {
|
||||
// Create the TX extra
|
||||
// TODO: Review this for canonicity with Monero
|
||||
let mut extra = vec![];
|
||||
SubField::TxPublicKey(
|
||||
PublicKey { point: self.outputs[0].R.compress() }
|
||||
).consensus_encode(&mut extra).unwrap();
|
||||
SubField::TxPublicKey(PublicKey { point: self.outputs[0].R.compress() })
|
||||
.consensus_encode(&mut extra)
|
||||
.unwrap();
|
||||
SubField::AdditionalPublickKey(
|
||||
self.outputs[1 ..].iter().map(|output| PublicKey { point: output.R.compress() }).collect()
|
||||
).consensus_encode(&mut extra).unwrap();
|
||||
self.outputs[1 ..].iter().map(|output| PublicKey { point: output.R.compress() }).collect(),
|
||||
)
|
||||
.consensus_encode(&mut extra)
|
||||
.unwrap();
|
||||
|
||||
let mut tx_outputs = Vec::with_capacity(self.outputs.len());
|
||||
let mut ecdh_info = Vec::with_capacity(self.outputs.len());
|
||||
for o in 0 .. self.outputs.len() {
|
||||
tx_outputs.push(Output {
|
||||
amount: 0,
|
||||
key: self.outputs[o].dest,
|
||||
tag: None
|
||||
});
|
||||
tx_outputs.push(Output { amount: 0, key: self.outputs[o].dest, tag: None });
|
||||
ecdh_info.push(self.outputs[o].amount);
|
||||
}
|
||||
|
||||
@@ -307,20 +286,20 @@ impl SignableTransaction {
|
||||
timelock: Timelock::None,
|
||||
inputs: vec![],
|
||||
outputs: tx_outputs,
|
||||
extra
|
||||
extra,
|
||||
},
|
||||
rct_signatures: RctSignatures {
|
||||
base: RctBase {
|
||||
fee: self.fee,
|
||||
ecdh_info,
|
||||
commitments: commitments.iter().map(|commitment| commitment.calculate()).collect()
|
||||
commitments: commitments.iter().map(|commitment| commitment.calculate()).collect(),
|
||||
},
|
||||
prunable: RctPrunable::Clsag {
|
||||
bulletproofs: vec![bp],
|
||||
clsags: vec![],
|
||||
pseudo_outs: vec![]
|
||||
}
|
||||
}
|
||||
pseudo_outs: vec![],
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@@ -328,7 +307,7 @@ impl SignableTransaction {
|
||||
&mut self,
|
||||
rng: &mut R,
|
||||
rpc: &Rpc,
|
||||
spend: &Scalar
|
||||
spend: &Scalar,
|
||||
) -> Result<Transaction, TransactionError> {
|
||||
let mut images = Vec::with_capacity(self.inputs.len());
|
||||
for input in &self.inputs {
|
||||
@@ -344,12 +323,11 @@ impl SignableTransaction {
|
||||
let (commitments, mask_sum) = self.prepare_outputs(
|
||||
rng,
|
||||
uniqueness(
|
||||
&images.iter().map(|image| Input::ToKey {
|
||||
amount: 0,
|
||||
key_offsets: vec![],
|
||||
key_image: *image
|
||||
}).collect::<Vec<_>>()
|
||||
)
|
||||
&images
|
||||
.iter()
|
||||
.map(|image| Input::ToKey { amount: 0, key_offsets: vec![], key_image: *image })
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
);
|
||||
|
||||
let mut tx = self.prepare_transaction(&commitments, Bulletproofs::new(rng, &commitments)?);
|
||||
@@ -361,7 +339,8 @@ impl SignableTransaction {
|
||||
RctPrunable::Null => panic!("Signing for RctPrunable::Null"),
|
||||
RctPrunable::Clsag { ref mut clsags, ref mut pseudo_outs, .. } => {
|
||||
clsags.append(&mut clsag_pairs.iter().map(|clsag| clsag.0.clone()).collect::<Vec<_>>());
|
||||
pseudo_outs.append(&mut clsag_pairs.iter().map(|clsag| clsag.1.clone()).collect::<Vec<_>>());
|
||||
pseudo_outs
|
||||
.append(&mut clsag_pairs.iter().map(|clsag| clsag.1.clone()).collect::<Vec<_>>());
|
||||
}
|
||||
}
|
||||
Ok(tx)
|
||||
|
||||
@@ -1,25 +1,38 @@
|
||||
use std::{io::{Read, Cursor}, sync::{Arc, RwLock}, collections::HashMap};
|
||||
use std::{
|
||||
io::{Read, Cursor},
|
||||
sync::{Arc, RwLock},
|
||||
collections::HashMap,
|
||||
};
|
||||
|
||||
use rand_core::{RngCore, CryptoRng, SeedableRng};
|
||||
use rand_chacha::ChaCha12Rng;
|
||||
|
||||
use curve25519_dalek::{traits::Identity, scalar::Scalar, edwards::{EdwardsPoint, CompressedEdwardsY}};
|
||||
use curve25519_dalek::{
|
||||
traits::Identity,
|
||||
scalar::Scalar,
|
||||
edwards::{EdwardsPoint, CompressedEdwardsY},
|
||||
};
|
||||
|
||||
use transcript::{Transcript, RecommendedTranscript};
|
||||
use frost::{
|
||||
curve::Ed25519,
|
||||
FrostError, FrostKeys,
|
||||
sign::{
|
||||
PreprocessMachine, SignMachine, SignatureMachine,
|
||||
AlgorithmMachine, AlgorithmSignMachine, AlgorithmSignatureMachine
|
||||
}
|
||||
PreprocessMachine, SignMachine, SignatureMachine, AlgorithmMachine, AlgorithmSignMachine,
|
||||
AlgorithmSignatureMachine,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
random_scalar, ringct::{clsag::{ClsagInput, ClsagDetails, ClsagMultisig}, bulletproofs::Bulletproofs, RctPrunable},
|
||||
random_scalar,
|
||||
ringct::{
|
||||
clsag::{ClsagInput, ClsagDetails, ClsagMultisig},
|
||||
bulletproofs::Bulletproofs,
|
||||
RctPrunable,
|
||||
},
|
||||
transaction::{Input, Transaction},
|
||||
rpc::Rpc,
|
||||
wallet::{TransactionError, SignableTransaction, Decoys, key_image_sort, uniqueness}
|
||||
wallet::{TransactionError, SignableTransaction, Decoys, key_image_sort, uniqueness},
|
||||
};
|
||||
|
||||
pub struct TransactionMachine {
|
||||
@@ -31,7 +44,7 @@ pub struct TransactionMachine {
|
||||
decoys: Vec<Decoys>,
|
||||
|
||||
inputs: Vec<Arc<RwLock<Option<ClsagDetails>>>>,
|
||||
clsags: Vec<AlgorithmMachine<Ed25519, ClsagMultisig>>
|
||||
clsags: Vec<AlgorithmMachine<Ed25519, ClsagMultisig>>,
|
||||
}
|
||||
|
||||
pub struct TransactionSignMachine {
|
||||
@@ -45,12 +58,12 @@ pub struct TransactionSignMachine {
|
||||
inputs: Vec<Arc<RwLock<Option<ClsagDetails>>>>,
|
||||
clsags: Vec<AlgorithmSignMachine<Ed25519, ClsagMultisig>>,
|
||||
|
||||
our_preprocess: Vec<u8>
|
||||
our_preprocess: Vec<u8>,
|
||||
}
|
||||
|
||||
pub struct TransactionSignatureMachine {
|
||||
tx: Transaction,
|
||||
clsags: Vec<AlgorithmSignatureMachine<Ed25519, ClsagMultisig>>
|
||||
clsags: Vec<AlgorithmSignatureMachine<Ed25519, ClsagMultisig>>,
|
||||
}
|
||||
|
||||
impl SignableTransaction {
|
||||
@@ -60,7 +73,7 @@ impl SignableTransaction {
|
||||
keys: FrostKeys<Ed25519>,
|
||||
mut transcript: RecommendedTranscript,
|
||||
height: usize,
|
||||
mut included: Vec<u16>
|
||||
mut included: Vec<u16>,
|
||||
) -> Result<TransactionMachine, TransactionError> {
|
||||
let mut inputs = vec![];
|
||||
for _ in 0 .. self.inputs.len() {
|
||||
@@ -80,9 +93,10 @@ impl SignableTransaction {
|
||||
// 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());
|
||||
// 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
|
||||
// Also include the spend_key as below only the key offset is included, so this transcripts the
|
||||
// sum product
|
||||
// Useful as transcripting the sum product effectively transcripts the key image, further
|
||||
// guaranteeing the one time properties noted below
|
||||
transcript.append_message(b"spend_key", &keys.group_key().0.compress().to_bytes());
|
||||
for input in &self.inputs {
|
||||
// These outputs can only be spent once. Therefore, it forces all RNGs derived from this
|
||||
@@ -110,14 +124,12 @@ impl SignableTransaction {
|
||||
|
||||
clsags.push(
|
||||
AlgorithmMachine::new(
|
||||
ClsagMultisig::new(
|
||||
transcript.clone(),
|
||||
input.key,
|
||||
inputs[i].clone()
|
||||
).map_err(|e| TransactionError::MultisigError(e))?,
|
||||
ClsagMultisig::new(transcript.clone(), input.key, inputs[i].clone())
|
||||
.map_err(|e| TransactionError::MultisigError(e))?,
|
||||
Arc::new(offset),
|
||||
&included
|
||||
).map_err(|e| TransactionError::FrostError(e))?
|
||||
&included,
|
||||
)
|
||||
.map_err(|e| TransactionError::FrostError(e))?,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -132,22 +144,22 @@ impl SignableTransaction {
|
||||
&mut ChaCha12Rng::from_seed(transcript.rng_seed(b"decoys")),
|
||||
rpc,
|
||||
height,
|
||||
&self.inputs
|
||||
).await.map_err(|e| TransactionError::RpcError(e))?;
|
||||
|
||||
Ok(
|
||||
TransactionMachine {
|
||||
signable: self,
|
||||
i: keys.params().i(),
|
||||
included,
|
||||
transcript,
|
||||
|
||||
decoys,
|
||||
|
||||
inputs,
|
||||
clsags
|
||||
}
|
||||
&self.inputs,
|
||||
)
|
||||
.await
|
||||
.map_err(|e| TransactionError::RpcError(e))?;
|
||||
|
||||
Ok(TransactionMachine {
|
||||
signable: self,
|
||||
i: keys.params().i(),
|
||||
included,
|
||||
transcript,
|
||||
|
||||
decoys,
|
||||
|
||||
inputs,
|
||||
clsags,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,18 +169,22 @@ impl PreprocessMachine for TransactionMachine {
|
||||
|
||||
fn preprocess<R: RngCore + CryptoRng>(
|
||||
mut self,
|
||||
rng: &mut R
|
||||
rng: &mut R,
|
||||
) -> (TransactionSignMachine, Vec<u8>) {
|
||||
// Iterate over each CLSAG calling preprocess
|
||||
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())
|
||||
self.clsags.len() * ((2 * (32 + 32)) + (2 * (32 + 32)) + ClsagMultisig::serialized_len()),
|
||||
);
|
||||
let clsags = self.clsags.drain(..).map(|clsag| {
|
||||
let (clsag, preprocess) = clsag.preprocess(rng);
|
||||
serialized.extend(&preprocess);
|
||||
clsag
|
||||
}).collect();
|
||||
let clsags = self
|
||||
.clsags
|
||||
.drain(..)
|
||||
.map(|clsag| {
|
||||
let (clsag, preprocess) = clsag.preprocess(rng);
|
||||
serialized.extend(&preprocess);
|
||||
clsag
|
||||
})
|
||||
.collect();
|
||||
let our_preprocess = serialized.clone();
|
||||
|
||||
// We could add further entropy here, and previous versions of this library did so
|
||||
@@ -194,7 +210,7 @@ impl PreprocessMachine for TransactionMachine {
|
||||
|
||||
our_preprocess,
|
||||
},
|
||||
serialized
|
||||
serialized,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -205,14 +221,12 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
||||
fn sign<Re: Read>(
|
||||
mut self,
|
||||
mut commitments: HashMap<u16, Re>,
|
||||
msg: &[u8]
|
||||
msg: &[u8],
|
||||
) -> Result<(TransactionSignatureMachine, Vec<u8>), FrostError> {
|
||||
if msg.len() != 0 {
|
||||
Err(
|
||||
FrostError::InternalError(
|
||||
"message was passed to the TransactionMachine when it generates its own"
|
||||
)
|
||||
)?;
|
||||
Err(FrostError::InternalError(
|
||||
"message was passed to the TransactionMachine when it generates its own",
|
||||
))?;
|
||||
}
|
||||
|
||||
// FROST commitments and their DLEqs, and the image and its DLEq
|
||||
@@ -220,34 +234,46 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
||||
|
||||
// Convert the unified commitments to a Vec of the individual commitments
|
||||
let mut images = vec![EdwardsPoint::identity(); self.clsags.len()];
|
||||
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);
|
||||
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)
|
||||
images[c] += CompressedEdwardsY(
|
||||
buf[(CLSAG_LEN - 96) .. (CLSAG_LEN - 64)].try_into().map_err(|_| FrostError::InvalidCommitment(*l))?
|
||||
).decompress().ok_or(FrostError::InvalidCommitment(*l))?;
|
||||
// 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)
|
||||
images[c] += CompressedEdwardsY(
|
||||
buf[(CLSAG_LEN - 96) .. (CLSAG_LEN - 64)]
|
||||
.try_into()
|
||||
.map_err(|_| FrostError::InvalidCommitment(*l))?,
|
||||
)
|
||||
.decompress()
|
||||
.ok_or(FrostError::InvalidCommitment(*l))?;
|
||||
|
||||
Ok((*l, Cursor::new(buf)))
|
||||
}).collect::<Result<HashMap<_, _>, _>>()
|
||||
}).collect::<Result<Vec<_>, _>>()?;
|
||||
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
|
||||
@@ -265,20 +291,20 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
||||
(commitments, output_masks) = self.signable.prepare_outputs(
|
||||
&mut ChaCha12Rng::from_seed(self.transcript.rng_seed(b"tx_keys")),
|
||||
uniqueness(
|
||||
&images.iter().map(|image| Input::ToKey {
|
||||
amount: 0,
|
||||
key_offsets: vec![],
|
||||
key_image: *image
|
||||
}).collect::<Vec<_>>()
|
||||
)
|
||||
&images
|
||||
.iter()
|
||||
.map(|image| Input::ToKey { amount: 0, key_offsets: vec![], key_image: *image })
|
||||
.collect::<Vec<_>>(),
|
||||
),
|
||||
);
|
||||
|
||||
self.signable.prepare_transaction(
|
||||
&commitments,
|
||||
Bulletproofs::new(
|
||||
&mut ChaCha12Rng::from_seed(self.transcript.rng_seed(b"bulletproofs")),
|
||||
&commitments
|
||||
).unwrap()
|
||||
&commitments,
|
||||
)
|
||||
.unwrap(),
|
||||
)
|
||||
};
|
||||
|
||||
@@ -291,7 +317,7 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
||||
self.decoys.swap_remove(0),
|
||||
self.inputs.swap_remove(0),
|
||||
self.clsags.swap_remove(0),
|
||||
commitments.swap_remove(0)
|
||||
commitments.swap_remove(0),
|
||||
));
|
||||
}
|
||||
sorted.sort_by(|x, y| key_image_sort(&x.0, &y.0));
|
||||
@@ -308,23 +334,18 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
||||
sum_pseudo_outs += mask;
|
||||
}
|
||||
|
||||
tx.prefix.inputs.push(
|
||||
Input::ToKey {
|
||||
amount: 0,
|
||||
key_offsets: value.2.offsets.clone(),
|
||||
key_image: value.0
|
||||
}
|
||||
);
|
||||
tx.prefix.inputs.push(Input::ToKey {
|
||||
amount: 0,
|
||||
key_offsets: value.2.offsets.clone(),
|
||||
key_image: value.0,
|
||||
});
|
||||
|
||||
*value.3.write().unwrap() = Some(
|
||||
ClsagDetails::new(
|
||||
ClsagInput::new(
|
||||
value.1.commitment,
|
||||
value.2
|
||||
).map_err(|_| panic!("Signing an input which isn't present in the ring we created for it"))?,
|
||||
mask
|
||||
)
|
||||
);
|
||||
*value.3.write().unwrap() = Some(ClsagDetails::new(
|
||||
ClsagInput::new(value.1.commitment, value.2).map_err(|_| {
|
||||
panic!("Signing an input which isn't present in the ring we created for it")
|
||||
})?,
|
||||
mask,
|
||||
));
|
||||
|
||||
self.clsags.push(value.4);
|
||||
commitments.push(value.5);
|
||||
@@ -334,11 +355,15 @@ impl SignMachine<Transaction> for TransactionSignMachine {
|
||||
|
||||
// Iterate over each CLSAG calling sign
|
||||
let mut serialized = Vec::with_capacity(self.clsags.len() * 32);
|
||||
let clsags = self.clsags.drain(..).map(|clsag| {
|
||||
let (clsag, share) = clsag.sign(commitments.remove(0), &msg)?;
|
||||
serialized.extend(&share);
|
||||
Ok(clsag)
|
||||
}).collect::<Result<_, _>>()?;
|
||||
let clsags = self
|
||||
.clsags
|
||||
.drain(..)
|
||||
.map(|clsag| {
|
||||
let (clsag, share) = clsag.sign(commitments.remove(0), &msg)?;
|
||||
serialized.extend(&share);
|
||||
Ok(clsag)
|
||||
})
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
Ok((TransactionSignatureMachine { tx, clsags }, serialized))
|
||||
}
|
||||
@@ -352,11 +377,14 @@ impl SignatureMachine<Transaction> for TransactionSignatureMachine {
|
||||
RctPrunable::Clsag { ref mut clsags, ref mut pseudo_outs, .. } => {
|
||||
for clsag in self.clsags {
|
||||
let (clsag, pseudo_out) = clsag.complete(
|
||||
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<_, _>, _>>()?
|
||||
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<_, _>, _>>()?,
|
||||
)?;
|
||||
clsags.push(clsag);
|
||||
pseudo_outs.push(pseudo_out);
|
||||
|
||||
Reference in New Issue
Block a user