Move SignableInput to clsag::Input

This commit is contained in:
Luke Parker 2022-04-28 17:29:56 -04:00
parent 7ed1fca270
commit c4b7cb71d7
No known key found for this signature in database
GPG key ID: F9F1386DB1E119B6
4 changed files with 78 additions and 73 deletions

View file

@ -1,6 +1,8 @@
use rand_core::{RngCore, CryptoRng};
use ff::Field;
use thiserror::Error;
use curve25519_dalek::{
constants::ED25519_BASEPOINT_TABLE,
scalar::Scalar,
@ -15,7 +17,6 @@ use monero::{
use crate::{
Commitment,
transaction::SignableInput,
c_verify_clsag,
random_scalar,
hash_to_scalar,
@ -27,11 +28,68 @@ mod multisig;
#[cfg(feature = "multisig")]
pub use multisig::Multisig;
#[derive(Error, Debug)]
pub enum Error {
#[error("internal error ({0})")]
InternalError(String),
#[error("invalid ring member (member {0}, ring size {1})")]
InvalidRingMember(u8, u8),
#[error("invalid commitment")]
InvalidCommitment
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct Input {
pub image: EdwardsPoint,
// Ring, the index we're signing for, and the actual commitment behind it
pub ring: Vec<[EdwardsPoint; 2]>,
pub i: usize,
pub commitment: Commitment
}
impl Input {
pub fn new(
image: EdwardsPoint,
ring: Vec<[EdwardsPoint; 2]>,
i: u8,
commitment: Commitment
) -> Result<Input, Error> {
let n = ring.len();
if n > u8::MAX.into() {
Err(Error::InternalError("max ring size in this library is u8 max".to_string()))?;
}
if i >= (n as u8) {
Err(Error::InvalidRingMember(i, n as u8))?;
}
let i: usize = i.into();
// Validate the commitment matches
if ring[i][1] != commitment.calculate() {
Err(Error::InvalidCommitment)?;
}
Ok(Input { image, ring, i, commitment })
}
#[cfg(feature = "multisig")]
pub fn context(&self) -> Vec<u8> {
let mut context = self.image.compress().to_bytes().to_vec();
for pair in &self.ring {
// Doesn't include mixins[i] as CLSAG doesn't care and won't be affected by it
context.extend(&pair[0].compress().to_bytes());
context.extend(&pair[1].compress().to_bytes());
}
context.extend(&u8::try_from(self.i).unwrap().to_le_bytes());
// Doesn't include commitment as the above ring + index includes the commitment
context
}
}
#[allow(non_snake_case)]
pub(crate) fn sign_core<R: RngCore + CryptoRng>(
rng: &mut R,
msg: &[u8; 32],
input: &SignableInput,
input: &Input,
mask: Scalar,
A: EdwardsPoint,
AH: EdwardsPoint
@ -148,7 +206,7 @@ pub(crate) fn sign_core<R: RngCore + CryptoRng>(
pub fn sign<R: RngCore + CryptoRng>(
rng: &mut R,
msg: [u8; 32],
inputs: &[(Scalar, SignableInput)],
inputs: &[(Scalar, Input)],
sum_outputs: Scalar
) -> Option<Vec<(Clsag, EdwardsPoint)>> {
if inputs.len() == 0 {

View file

@ -19,7 +19,7 @@ use monero::util::ringct::{Key, Clsag};
use crate::{
hash_to_point,
frost::{MultisigError, Ed25519, DLEqProof},
clsag::{SignableInput, sign_core, verify}
clsag::{Input, sign_core, verify}
};
#[allow(non_snake_case)]
@ -40,7 +40,7 @@ pub struct Multisig {
AH: dfg::EdwardsPoint,
msg: [u8; 32],
input: SignableInput,
input: Input,
interim: Option<ClsagSignInterim>
}
@ -49,7 +49,7 @@ impl Multisig {
pub fn new<R: RngCore + CryptoRng + SeedableRng>(
rng: &mut R,
msg: [u8; 32],
input: SignableInput
input: Input
) -> Result<Multisig, MultisigError> {
let mut seed = [0; 32];
rng.fill_bytes(&mut seed);

View file

@ -35,12 +35,6 @@ mod mixins;
#[derive(Error, Debug)]
pub enum TransactionError {
#[error("internal error ({0})")]
InternalError(String),
#[error("invalid ring member (member {0}, ring size {1})")]
InvalidRingMember(u8, u8),
#[error("invalid commitment")]
InvalidCommitment,
#[error("no inputs")]
NoInputs,
#[error("too many outputs")]
@ -51,6 +45,8 @@ pub enum TransactionError {
InvalidAddress,
#[error("rpc error ({0})")]
RpcError(RpcError),
#[error("clsag error ({0})")]
ClsagError(clsag::Error),
#[error("invalid transaction ({0})")]
InvalidTransaction(RpcError)
}
@ -122,55 +118,6 @@ pub fn scan_tx(tx: &Transaction, view: Scalar, spend: EdwardsPoint) -> Vec<Spend
res
}
#[derive(Clone, PartialEq, Eq, Debug)]
pub struct SignableInput {
pub(crate) image: EdwardsPoint,
mixins: Vec<u64>,
// Ring, the index we're signing for, and the actual commitment behind it
pub(crate) ring: Vec<[EdwardsPoint; 2]>,
pub(crate) i: usize,
pub(crate) commitment: Commitment
}
impl SignableInput {
pub fn new(
image: EdwardsPoint,
mixins: Vec<u64>,
ring: Vec<[EdwardsPoint; 2]>,
i: u8,
commitment: Commitment
) -> Result<SignableInput, TransactionError> {
let n = ring.len();
if n > u8::MAX.into() {
Err(TransactionError::InternalError("max ring size in this library is u8 max".to_string()))?;
}
if i >= (n as u8) {
Err(TransactionError::InvalidRingMember(i, n as u8))?;
}
let i: usize = i.into();
// Validate the commitment matches
if ring[i][1] != commitment.calculate() {
Err(TransactionError::InvalidCommitment)?;
}
Ok(SignableInput { image, mixins, ring, i, commitment })
}
#[cfg(feature = "multisig")]
pub fn context(&self) -> Vec<u8> {
let mut context = self.image.compress().to_bytes().to_vec();
for pair in &self.ring {
// Doesn't include mixins[i] as CLSAG doesn't care and won't be affected by it
context.extend(&pair[0].compress().to_bytes());
context.extend(&pair[1].compress().to_bytes());
}
context.extend(&u8::try_from(self.i).unwrap().to_le_bytes());
// Doesn't include commitment as the above ring + index includes the commitment
context
}
}
#[allow(non_snake_case)]
fn shared_key(s: Scalar, P: &EdwardsPoint, o: usize) -> Scalar {
let mut shared = (s * P).mul_by_cofactor().compress().to_bytes().to_vec();
@ -269,29 +216,30 @@ pub async fn send<R: RngCore + CryptoRng>(
));
// Handle inputs
let mut mixins = Vec::with_capacity(inputs.len());
let mut signable = Vec::with_capacity(inputs.len());
for input in inputs {
let (m, mixins) = mixins::select(
for (i, input) in inputs.iter().enumerate() {
let (m, mix) = mixins::select(
rpc.get_o_indexes(input.tx).await.map_err(|e| TransactionError::RpcError(e))?[input.o]
);
mixins.push(mix);
signable.push((
spend + input.key_offset,
SignableInput::new(
clsag::Input::new(
key_image::generate(&(spend + input.key_offset)),
mixins.clone(),
rpc.get_ring(&mixins).await.map_err(|e| TransactionError::RpcError(e))?,
rpc.get_ring(&mixins[i]).await.map_err(|e| TransactionError::RpcError(e))?,
m,
input.commitment
)?
).map_err(|e| TransactionError::ClsagError(e))?
));
}
let prefix = TransactionPrefix {
version: VarInt(2),
unlock_time: VarInt(0),
inputs: signable.iter().map(|input| TxIn::ToKey {
inputs: signable.iter().enumerate().map(|(i, input)| TxIn::ToKey {
amount: VarInt(0),
key_offsets: mixins::offset(&input.1.mixins).iter().map(|x| VarInt(*x)).collect(),
key_offsets: mixins::offset(&mixins[i]).iter().map(|x| VarInt(*x)).collect(),
k_image: KeyImage {
image: Hash(input.1.image.compress().to_bytes())
}

View file

@ -3,7 +3,7 @@ use rand_chacha::ChaCha12Rng;
use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar};
use monero_serai::{random_scalar, Commitment, frost::MultisigError, key_image, clsag, transaction::SignableInput};
use monero_serai::{random_scalar, Commitment, frost::MultisigError, key_image, clsag};
#[cfg(feature = "multisig")]
use ::frost::sign;
@ -47,9 +47,8 @@ fn test_single() {
msg,
&vec![(
secrets[0],
SignableInput::new(
clsag::Input::new(
image,
[0; RING_LEN as usize].to_vec(),
ring.clone(),
RING_INDEX,
Commitment::new(secrets[1], AMOUNT)
@ -113,7 +112,7 @@ fn test_multisig() -> Result<(), MultisigError> {
clsag::Multisig::new(
&mut ChaCha12Rng::seed_from_u64(1),
msg,
SignableInput::new(image, vec![], ring.clone(), RING_INDEX, Commitment::new(randomness, AMOUNT)).unwrap()
clsag::Input::new(image, ring.clone(), RING_INDEX, Commitment::new(randomness, AMOUNT)).unwrap()
).unwrap(),
keys[i - 1].clone(),
&(1 ..= t).collect::<Vec<usize>>()