Add Transaction::sign.

While I don't love the introduction of empty_signed, it's practically fine.
This commit is contained in:
Luke Parker 2023-04-23 01:25:45 -04:00
parent 3f6565588f
commit 710e6e5217
No known key found for this signature in database
6 changed files with 67 additions and 75 deletions

1
Cargo.lock generated
View file

@ -10769,7 +10769,6 @@ dependencies = [
"parity-scale-codec", "parity-scale-codec",
"rand 0.8.5", "rand 0.8.5",
"rand_chacha 0.3.1", "rand_chacha 0.3.1",
"rand_core 0.6.4",
"schnorr-signatures", "schnorr-signatures",
"serai-db", "serai-db",
"subtle", "subtle",

View file

@ -17,6 +17,7 @@ rustdoc-args = ["--cfg", "docsrs"]
async-trait = "0.1" async-trait = "0.1"
zeroize = "^1.5" zeroize = "^1.5"
rand_core = "0.6"
blake2 = "0.10" blake2 = "0.10"
@ -40,6 +41,4 @@ log = "0.4"
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
[dev-dependencies] [dev-dependencies]
rand_core = "0.6"
tributary = { package = "tributary-chain", path = "./tributary", features = ["tests"] } tributary = { package = "tributary-chain", path = "./tributary", features = ["tests"] }

View file

@ -1,11 +1,9 @@
use core::time::Duration; use core::time::Duration;
use zeroize::Zeroizing; use zeroize::Zeroizing;
use rand_core::{RngCore, OsRng}; use rand_core::{RngCore, OsRng};
use ciphersuite::{group::ff::Field, Ciphersuite, Ristretto}; use ciphersuite::{Ciphersuite, Ristretto};
use schnorr::SchnorrSignature;
use frost::Participant; use frost::Participant;
use tokio::time::sleep; use tokio::time::sleep;
@ -17,7 +15,7 @@ use processor_messages::{
CoordinatorMessage, CoordinatorMessage,
}; };
use tributary::{Signed, Transaction as TransactionTrait, Tributary}; use tributary::Tributary;
use crate::{ use crate::{
processor::MemProcessor, processor::MemProcessor,
@ -39,41 +37,14 @@ async fn dkg_commitments_test() {
let mut txs = vec![]; let mut txs = vec![];
// Create DKG commitments for each key // Create DKG commitments for each key
for key in &keys { for key in &keys {
let pub_key = Ristretto::generator() * **key;
let attempt = 0; let attempt = 0;
let mut commitments = vec![0; 256]; let mut commitments = vec![0; 256];
OsRng.fill_bytes(&mut commitments); OsRng.fill_bytes(&mut commitments);
// Create the TX with a null signature so we can get its sig hash let mut tx =
let tx = Transaction::DkgCommitments( Transaction::DkgCommitments(attempt, commitments.clone(), Transaction::empty_signed());
attempt, tx.sign(&mut OsRng, spec.genesis(), key, 0);
commitments.clone(), txs.push(tx);
Signed {
signer: pub_key,
nonce: 0,
signature: SchnorrSignature::<Ristretto> {
R: Ristretto::generator(),
s: <Ristretto as Ciphersuite>::F::ZERO,
},
},
);
// Re-create it with the actual signature
// We could mutate the existing one, we'd just have to match to the DkgCommitments enum variant
txs.push(Transaction::DkgCommitments(
attempt,
commitments,
Signed {
signer: pub_key,
nonce: 0,
signature: SchnorrSignature::<Ristretto>::sign(
key,
Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng)),
tx.sig_hash(spec.genesis()),
),
},
));
} }
let mut last_block = tributaries[0].1.tip(); let mut last_block = tributaries[0].1.tip();

View file

@ -1,17 +1,12 @@
use core::time::Duration; use core::time::Duration;
use zeroize::Zeroizing;
use rand_core::{RngCore, OsRng}; use rand_core::{RngCore, OsRng};
use ciphersuite::{group::ff::Field, Ciphersuite, Ristretto};
use schnorr::SchnorrSignature;
use tokio::time::sleep; use tokio::time::sleep;
use serai_db::MemDb; use serai_db::MemDb;
use tributary::{Signed, Transaction as TransactionTrait, Tributary}; use tributary::Tributary;
use crate::{ use crate::{
LocalP2p, LocalP2p,
@ -33,41 +28,15 @@ async fn tx_test() {
let sender = let sender =
usize::try_from(OsRng.next_u64() % u64::try_from(tributaries.len()).unwrap()).unwrap(); usize::try_from(OsRng.next_u64() % u64::try_from(tributaries.len()).unwrap()).unwrap();
let key = keys[sender].clone(); let key = keys[sender].clone();
let pub_key = Ristretto::generator() * *key;
let attempt = 0; let attempt = 0;
let mut commitments = vec![0; 256]; let mut commitments = vec![0; 256];
OsRng.fill_bytes(&mut commitments); OsRng.fill_bytes(&mut commitments);
// Create the TX with a null signature so we can get its sig hash // Create the TX with a null signature so we can get its sig hash
let tx = Transaction::DkgCommitments( let mut tx =
attempt, Transaction::DkgCommitments(attempt, commitments.clone(), Transaction::empty_signed());
commitments.clone(), tx.sign(&mut OsRng, spec.genesis(), &key, 0);
Signed {
signer: pub_key,
nonce: 0,
signature: SchnorrSignature::<Ristretto> {
R: Ristretto::generator(),
s: <Ristretto as Ciphersuite>::F::ZERO,
},
},
);
// Re-create it with the actual signature
// We could mutate the existing one, we'd just have to match to the DkgCommitments enum variant
let tx = Transaction::DkgCommitments(
attempt,
commitments,
Signed {
signer: pub_key,
nonce: 0,
signature: SchnorrSignature::<Ristretto>::sign(
&key,
Zeroizing::new(<Ristretto as Ciphersuite>::F::random(&mut OsRng)),
tx.sig_hash(spec.genesis()),
),
},
);
assert!(tributaries[sender].1.add_transaction(tx.clone()).await); assert!(tributaries[sender].1.add_transaction(tx.clone()).await);
// Sleep for two blocks // Sleep for two blocks

View file

@ -1,9 +1,14 @@
use core::ops::Deref;
use std::{io, collections::HashMap}; use std::{io, collections::HashMap};
use zeroize::Zeroizing;
use rand_core::{RngCore, CryptoRng};
use blake2::{Digest, Blake2s256}; use blake2::{Digest, Blake2s256};
use transcript::{Transcript, RecommendedTranscript}; use transcript::{Transcript, RecommendedTranscript};
use ciphersuite::{Ciphersuite, Ristretto}; use ciphersuite::{group::ff::Field, Ciphersuite, Ristretto};
use schnorr::SchnorrSignature;
use frost::Participant; use frost::Participant;
use scale::Encode; use scale::Encode;
@ -349,3 +354,53 @@ impl TransactionTrait for Transaction {
Ok(()) Ok(())
} }
} }
impl Transaction {
// Used to initially construct transactions so we can then get sig hashes and perform signing
pub fn empty_signed() -> Signed {
Signed {
signer: Ristretto::generator(),
nonce: 0,
signature: SchnorrSignature::<Ristretto> {
R: Ristretto::generator(),
s: <Ristretto as Ciphersuite>::F::ZERO,
},
}
}
// Sign a transaction
pub fn sign<R: RngCore + CryptoRng>(
&mut self,
rng: &mut R,
genesis: [u8; 32],
key: &Zeroizing<<Ristretto as Ciphersuite>::F>,
nonce: u32,
) {
fn signed(tx: &mut Transaction) -> &mut Signed {
match tx {
Transaction::DkgCommitments(_, _, ref mut signed) => signed,
Transaction::DkgShares(_, _, ref mut signed) => signed,
Transaction::ExternalBlock(_) => panic!("signing ExternalBlock"),
Transaction::SubstrateBlock(_) => panic!("signing SubstrateBlock"),
Transaction::BatchPreprocess(ref mut data) => &mut data.signed,
Transaction::BatchShare(ref mut data) => &mut data.signed,
Transaction::SignPreprocess(ref mut data) => &mut data.signed,
Transaction::SignShare(ref mut data) => &mut data.signed,
}
}
let signed_ref = signed(self);
signed_ref.signer = Ristretto::generator() * key.deref();
signed_ref.nonce = nonce;
let sig_hash = self.sig_hash(genesis);
signed(self).signature = SchnorrSignature::<Ristretto>::sign(
key,
Zeroizing::new(<Ristretto as Ciphersuite>::F::random(rng)),
sig_hash,
);
}
}

View file

@ -36,7 +36,6 @@ tokio = { version = "1", features = ["macros", "sync", "time", "rt"] }
[dev-dependencies] [dev-dependencies]
zeroize = "^1.5" zeroize = "^1.5"
rand_core = "0.6"
[features] [features]
tests = [] tests = []