mirror of
https://github.com/serai-dex/serai.git
synced 2025-01-09 12:29:27 +00:00
Move SignableInput to clsag::Input
This commit is contained in:
parent
7ed1fca270
commit
c4b7cb71d7
4 changed files with 78 additions and 73 deletions
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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>>()
|
||||
|
|
Loading…
Reference in a new issue