mirror of
https://github.com/serai-dex/serai.git
synced 2025-01-09 20:39:29 +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 rand_core::{RngCore, CryptoRng};
|
||||||
use ff::Field;
|
use ff::Field;
|
||||||
|
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
use curve25519_dalek::{
|
use curve25519_dalek::{
|
||||||
constants::ED25519_BASEPOINT_TABLE,
|
constants::ED25519_BASEPOINT_TABLE,
|
||||||
scalar::Scalar,
|
scalar::Scalar,
|
||||||
|
@ -15,7 +17,6 @@ use monero::{
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Commitment,
|
Commitment,
|
||||||
transaction::SignableInput,
|
|
||||||
c_verify_clsag,
|
c_verify_clsag,
|
||||||
random_scalar,
|
random_scalar,
|
||||||
hash_to_scalar,
|
hash_to_scalar,
|
||||||
|
@ -27,11 +28,68 @@ mod multisig;
|
||||||
#[cfg(feature = "multisig")]
|
#[cfg(feature = "multisig")]
|
||||||
pub use multisig::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)]
|
#[allow(non_snake_case)]
|
||||||
pub(crate) fn sign_core<R: RngCore + CryptoRng>(
|
pub(crate) fn sign_core<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
msg: &[u8; 32],
|
msg: &[u8; 32],
|
||||||
input: &SignableInput,
|
input: &Input,
|
||||||
mask: Scalar,
|
mask: Scalar,
|
||||||
A: EdwardsPoint,
|
A: EdwardsPoint,
|
||||||
AH: EdwardsPoint
|
AH: EdwardsPoint
|
||||||
|
@ -148,7 +206,7 @@ pub(crate) fn sign_core<R: RngCore + CryptoRng>(
|
||||||
pub fn sign<R: RngCore + CryptoRng>(
|
pub fn sign<R: RngCore + CryptoRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
msg: [u8; 32],
|
msg: [u8; 32],
|
||||||
inputs: &[(Scalar, SignableInput)],
|
inputs: &[(Scalar, Input)],
|
||||||
sum_outputs: Scalar
|
sum_outputs: Scalar
|
||||||
) -> Option<Vec<(Clsag, EdwardsPoint)>> {
|
) -> Option<Vec<(Clsag, EdwardsPoint)>> {
|
||||||
if inputs.len() == 0 {
|
if inputs.len() == 0 {
|
||||||
|
|
|
@ -19,7 +19,7 @@ use monero::util::ringct::{Key, Clsag};
|
||||||
use crate::{
|
use crate::{
|
||||||
hash_to_point,
|
hash_to_point,
|
||||||
frost::{MultisigError, Ed25519, DLEqProof},
|
frost::{MultisigError, Ed25519, DLEqProof},
|
||||||
clsag::{SignableInput, sign_core, verify}
|
clsag::{Input, sign_core, verify}
|
||||||
};
|
};
|
||||||
|
|
||||||
#[allow(non_snake_case)]
|
#[allow(non_snake_case)]
|
||||||
|
@ -40,7 +40,7 @@ pub struct Multisig {
|
||||||
AH: dfg::EdwardsPoint,
|
AH: dfg::EdwardsPoint,
|
||||||
|
|
||||||
msg: [u8; 32],
|
msg: [u8; 32],
|
||||||
input: SignableInput,
|
input: Input,
|
||||||
|
|
||||||
interim: Option<ClsagSignInterim>
|
interim: Option<ClsagSignInterim>
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,7 @@ impl Multisig {
|
||||||
pub fn new<R: RngCore + CryptoRng + SeedableRng>(
|
pub fn new<R: RngCore + CryptoRng + SeedableRng>(
|
||||||
rng: &mut R,
|
rng: &mut R,
|
||||||
msg: [u8; 32],
|
msg: [u8; 32],
|
||||||
input: SignableInput
|
input: Input
|
||||||
) -> Result<Multisig, MultisigError> {
|
) -> Result<Multisig, MultisigError> {
|
||||||
let mut seed = [0; 32];
|
let mut seed = [0; 32];
|
||||||
rng.fill_bytes(&mut seed);
|
rng.fill_bytes(&mut seed);
|
||||||
|
|
|
@ -35,12 +35,6 @@ mod mixins;
|
||||||
|
|
||||||
#[derive(Error, Debug)]
|
#[derive(Error, Debug)]
|
||||||
pub enum TransactionError {
|
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")]
|
#[error("no inputs")]
|
||||||
NoInputs,
|
NoInputs,
|
||||||
#[error("too many outputs")]
|
#[error("too many outputs")]
|
||||||
|
@ -51,6 +45,8 @@ pub enum TransactionError {
|
||||||
InvalidAddress,
|
InvalidAddress,
|
||||||
#[error("rpc error ({0})")]
|
#[error("rpc error ({0})")]
|
||||||
RpcError(RpcError),
|
RpcError(RpcError),
|
||||||
|
#[error("clsag error ({0})")]
|
||||||
|
ClsagError(clsag::Error),
|
||||||
#[error("invalid transaction ({0})")]
|
#[error("invalid transaction ({0})")]
|
||||||
InvalidTransaction(RpcError)
|
InvalidTransaction(RpcError)
|
||||||
}
|
}
|
||||||
|
@ -122,55 +118,6 @@ pub fn scan_tx(tx: &Transaction, view: Scalar, spend: EdwardsPoint) -> Vec<Spend
|
||||||
res
|
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)]
|
#[allow(non_snake_case)]
|
||||||
fn shared_key(s: Scalar, P: &EdwardsPoint, o: usize) -> Scalar {
|
fn shared_key(s: Scalar, P: &EdwardsPoint, o: usize) -> Scalar {
|
||||||
let mut shared = (s * P).mul_by_cofactor().compress().to_bytes().to_vec();
|
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
|
// Handle inputs
|
||||||
|
let mut mixins = Vec::with_capacity(inputs.len());
|
||||||
let mut signable = Vec::with_capacity(inputs.len());
|
let mut signable = Vec::with_capacity(inputs.len());
|
||||||
for input in inputs {
|
for (i, input) in inputs.iter().enumerate() {
|
||||||
let (m, mixins) = mixins::select(
|
let (m, mix) = mixins::select(
|
||||||
rpc.get_o_indexes(input.tx).await.map_err(|e| TransactionError::RpcError(e))?[input.o]
|
rpc.get_o_indexes(input.tx).await.map_err(|e| TransactionError::RpcError(e))?[input.o]
|
||||||
);
|
);
|
||||||
|
mixins.push(mix);
|
||||||
signable.push((
|
signable.push((
|
||||||
spend + input.key_offset,
|
spend + input.key_offset,
|
||||||
SignableInput::new(
|
clsag::Input::new(
|
||||||
key_image::generate(&(spend + input.key_offset)),
|
key_image::generate(&(spend + input.key_offset)),
|
||||||
mixins.clone(),
|
rpc.get_ring(&mixins[i]).await.map_err(|e| TransactionError::RpcError(e))?,
|
||||||
rpc.get_ring(&mixins).await.map_err(|e| TransactionError::RpcError(e))?,
|
|
||||||
m,
|
m,
|
||||||
input.commitment
|
input.commitment
|
||||||
)?
|
).map_err(|e| TransactionError::ClsagError(e))?
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
let prefix = TransactionPrefix {
|
let prefix = TransactionPrefix {
|
||||||
version: VarInt(2),
|
version: VarInt(2),
|
||||||
unlock_time: VarInt(0),
|
unlock_time: VarInt(0),
|
||||||
inputs: signable.iter().map(|input| TxIn::ToKey {
|
inputs: signable.iter().enumerate().map(|(i, input)| TxIn::ToKey {
|
||||||
amount: VarInt(0),
|
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 {
|
k_image: KeyImage {
|
||||||
image: Hash(input.1.image.compress().to_bytes())
|
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 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")]
|
#[cfg(feature = "multisig")]
|
||||||
use ::frost::sign;
|
use ::frost::sign;
|
||||||
|
@ -47,9 +47,8 @@ fn test_single() {
|
||||||
msg,
|
msg,
|
||||||
&vec![(
|
&vec![(
|
||||||
secrets[0],
|
secrets[0],
|
||||||
SignableInput::new(
|
clsag::Input::new(
|
||||||
image,
|
image,
|
||||||
[0; RING_LEN as usize].to_vec(),
|
|
||||||
ring.clone(),
|
ring.clone(),
|
||||||
RING_INDEX,
|
RING_INDEX,
|
||||||
Commitment::new(secrets[1], AMOUNT)
|
Commitment::new(secrets[1], AMOUNT)
|
||||||
|
@ -113,7 +112,7 @@ fn test_multisig() -> Result<(), MultisigError> {
|
||||||
clsag::Multisig::new(
|
clsag::Multisig::new(
|
||||||
&mut ChaCha12Rng::seed_from_u64(1),
|
&mut ChaCha12Rng::seed_from_u64(1),
|
||||||
msg,
|
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(),
|
).unwrap(),
|
||||||
keys[i - 1].clone(),
|
keys[i - 1].clone(),
|
||||||
&(1 ..= t).collect::<Vec<usize>>()
|
&(1 ..= t).collect::<Vec<usize>>()
|
||||||
|
|
Loading…
Reference in a new issue