mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-09 04:39:24 +00:00
Working multisig TXs
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
use std::{rc::Rc, cell::RefCell};
|
||||
|
||||
use rand::{RngCore, rngs::OsRng};
|
||||
|
||||
use curve25519_dalek::{constants::ED25519_BASEPOINT_TABLE, scalar::Scalar};
|
||||
@@ -50,20 +52,6 @@ fn test_single() {
|
||||
assert!(clsag::verify(&clsag, &msg, image, &ring, pseudo_out));
|
||||
}
|
||||
|
||||
#[cfg(feature = "multisig")]
|
||||
#[derive(Clone, Debug)]
|
||||
struct TransactionData;
|
||||
#[cfg(feature = "multisig")]
|
||||
impl clsag::TransactionData for TransactionData {
|
||||
fn msg(&self) -> [u8; 32] {
|
||||
[1; 32]
|
||||
}
|
||||
|
||||
fn mask_sum(&self) -> Scalar {
|
||||
Scalar::from(21u64)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "multisig")]
|
||||
#[test]
|
||||
fn test_multisig() -> Result<(), MultisigError> {
|
||||
@@ -94,7 +82,8 @@ fn test_multisig() -> Result<(), MultisigError> {
|
||||
sign::AlgorithmMachine::new(
|
||||
clsag::Multisig::new(
|
||||
clsag::Input::new(ring.clone(), RING_INDEX, Commitment::new(randomness, AMOUNT)).unwrap(),
|
||||
TransactionData
|
||||
Rc::new(RefCell::new([1; 32])),
|
||||
Rc::new(RefCell::new(Scalar::from(42u64)))
|
||||
).unwrap(),
|
||||
keys[i - 1].clone(),
|
||||
&(1 ..= THRESHOLD).collect::<Vec<usize>>()
|
||||
|
||||
@@ -2,11 +2,10 @@
|
||||
|
||||
use std::rc::Rc;
|
||||
|
||||
use rand_core::{RngCore, CryptoRng};
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use ff::Field;
|
||||
use dalek_ff_group::{ED25519_BASEPOINT_TABLE, Scalar, EdwardsPoint};
|
||||
use dalek_ff_group::{ED25519_BASEPOINT_TABLE, Scalar};
|
||||
|
||||
pub use frost::{
|
||||
FrostError, MultisigParams, MultisigKeys,
|
||||
@@ -15,50 +14,8 @@ pub use frost::{
|
||||
|
||||
use monero_serai::frost::Ed25519;
|
||||
|
||||
pub const THRESHOLD: usize = 5;
|
||||
pub const PARTICIPANTS: usize = 8;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct DummyAlgorithm;
|
||||
impl Algorithm<Ed25519> for DummyAlgorithm {
|
||||
type Signature = ();
|
||||
|
||||
fn addendum_commit_len() -> usize { unimplemented!() }
|
||||
|
||||
fn preprocess_addendum<R: RngCore + CryptoRng>(
|
||||
_: &mut R,
|
||||
_: &sign::ParamsView<Ed25519>,
|
||||
_: &[Scalar; 2],
|
||||
) -> Vec<u8> { unimplemented!() }
|
||||
|
||||
fn process_addendum(
|
||||
&mut self,
|
||||
_: &sign::ParamsView<Ed25519>,
|
||||
_: usize,
|
||||
_: &[EdwardsPoint; 2],
|
||||
_: &[u8],
|
||||
) -> Result<(), FrostError> { unimplemented!() }
|
||||
|
||||
fn context(&self) -> Vec<u8> { unimplemented!() }
|
||||
|
||||
fn sign_share(
|
||||
&mut self,
|
||||
_: &sign::ParamsView<Ed25519>,
|
||||
_: EdwardsPoint,
|
||||
_: Scalar,
|
||||
_: Scalar,
|
||||
_: &[u8],
|
||||
) -> Scalar { unimplemented!() }
|
||||
|
||||
fn verify(&self, _: EdwardsPoint, _: EdwardsPoint, _: Scalar) -> Option<Self::Signature> { unimplemented!() }
|
||||
|
||||
fn verify_share(
|
||||
&self,
|
||||
_: EdwardsPoint,
|
||||
_: EdwardsPoint,
|
||||
_: Scalar,
|
||||
) -> bool { unimplemented!() }
|
||||
}
|
||||
pub const THRESHOLD: usize = 3;
|
||||
pub const PARTICIPANTS: usize = 5;
|
||||
|
||||
pub fn generate_keys() -> (Vec<Rc<MultisigKeys<Ed25519>>>, Scalar) {
|
||||
let mut params = vec![];
|
||||
|
||||
@@ -4,15 +4,13 @@ use rand::{RngCore, rngs::OsRng};
|
||||
|
||||
use curve25519_dalek::{traits::Identity, edwards::EdwardsPoint};
|
||||
|
||||
use monero_serai::{frost::MultisigError, key_image};
|
||||
|
||||
use ::frost::sign;
|
||||
use monero_serai::key_image;
|
||||
|
||||
mod frost;
|
||||
use crate::frost::{THRESHOLD, PARTICIPANTS, DummyAlgorithm, generate_keys};
|
||||
use crate::frost::{THRESHOLD, PARTICIPANTS, generate_keys};
|
||||
|
||||
#[test]
|
||||
fn test() -> Result<(), MultisigError> {
|
||||
fn test() {
|
||||
let (keys, group_private) = generate_keys();
|
||||
let image = key_image::generate(&group_private);
|
||||
|
||||
@@ -27,16 +25,16 @@ fn test() -> Result<(), MultisigError> {
|
||||
for i in 1 ..= PARTICIPANTS {
|
||||
if included.contains(&i) {
|
||||
// If they were included, include their view
|
||||
views.push(sign::Params::new(DummyAlgorithm, keys[i - 1].clone(), &included).unwrap().view());
|
||||
views.push(keys[i - 1].view(&included).unwrap());
|
||||
let share = key_image::generate_share(&mut OsRng, &views[i - 1]);
|
||||
let mut serialized = share.0;
|
||||
let mut serialized = share.0.compress().to_bytes().to_vec();
|
||||
serialized.extend(b"abc");
|
||||
serialized.extend(&share.1);
|
||||
shares.push(serialized);
|
||||
} else {
|
||||
// If they weren't included, include dummy data
|
||||
// If they weren't included, include dummy data to fill the Vec
|
||||
// Uses the view of someone actually included as Params::new verifies inclusion
|
||||
views.push(sign::Params::new(DummyAlgorithm, keys[included[0] - 1].clone(), &included).unwrap().view());
|
||||
views.push(keys[included[0] - 1].view(&included).unwrap());
|
||||
shares.push(vec![]);
|
||||
}
|
||||
}
|
||||
@@ -50,6 +48,4 @@ fn test() -> Result<(), MultisigError> {
|
||||
}
|
||||
assert_eq!(image, multi_image);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
73
coins/monero/tests/send_multisig.rs
Normal file
73
coins/monero/tests/send_multisig.rs
Normal file
@@ -0,0 +1,73 @@
|
||||
#![cfg(feature = "multisig")]
|
||||
|
||||
use rand::rngs::OsRng;
|
||||
|
||||
use blake2::{digest::Update, Digest, Blake2b512};
|
||||
|
||||
use curve25519_dalek::constants::ED25519_BASEPOINT_TABLE;
|
||||
use dalek_ff_group::Scalar;
|
||||
|
||||
use monero::{
|
||||
cryptonote::hash::Hashable,
|
||||
network::Network,
|
||||
util::{key::PublicKey, address::Address}
|
||||
};
|
||||
|
||||
use monero_serai::{transaction::{self, SignableTransaction}, rpc::Rpc};
|
||||
|
||||
mod rpc;
|
||||
use crate::rpc::mine_block;
|
||||
|
||||
mod frost;
|
||||
use crate::frost::{THRESHOLD, generate_keys, sign};
|
||||
|
||||
#[tokio::test]
|
||||
pub async fn send_multisig() {
|
||||
let rpc = Rpc::new("http://127.0.0.1:18081".to_string());
|
||||
|
||||
let fee_per_byte = 50000000;
|
||||
let fee = fee_per_byte * 2000;
|
||||
|
||||
let (keys, _) = generate_keys();
|
||||
let t = keys[0].params().t();
|
||||
|
||||
// Generate an address
|
||||
let view = Scalar::from_hash(Blake2b512::new().chain("Serai DEX")).0;
|
||||
let spend = keys[0].group_key().0;
|
||||
let addr = Address::standard(
|
||||
Network::Mainnet,
|
||||
PublicKey { point: spend.compress() },
|
||||
PublicKey { point: (&view * &ED25519_BASEPOINT_TABLE).compress() }
|
||||
);
|
||||
|
||||
// Mine blocks to that address
|
||||
let start = rpc.get_height().await.unwrap();
|
||||
for _ in 0 .. 7 {
|
||||
mine_block(&rpc, addr.to_string()).await.unwrap();
|
||||
}
|
||||
|
||||
// Get the input TX
|
||||
let tx = rpc.get_block_transactions(start).await.unwrap().swap_remove(0);
|
||||
let output = transaction::scan(&tx, view, spend).swap_remove(0);
|
||||
let amount = output.commitment.amount - fee;
|
||||
|
||||
let mut machines = Vec::with_capacity(t);
|
||||
for i in 1 ..= t {
|
||||
machines.push(
|
||||
SignableTransaction::new(
|
||||
vec![output.clone()], vec![(addr, amount)], addr, fee_per_byte
|
||||
).unwrap().multisig(
|
||||
&mut OsRng,
|
||||
&rpc,
|
||||
keys[i - 1].clone(),
|
||||
&(1 ..= THRESHOLD).collect::<Vec<usize>>()
|
||||
).await.unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
let txs = sign(&mut machines, keys);
|
||||
for s in 0 .. (t - 1) {
|
||||
assert_eq!(txs[s].hash(), txs[0].hash());
|
||||
}
|
||||
rpc.publish_transaction(&txs[0]).await.unwrap();
|
||||
}
|
||||
Reference in New Issue
Block a user