mirror of
https://github.com/serai-dex/serai.git
synced 2025-12-08 12:19:24 +00:00
Bind the signature scheme for tendermint-machine
This commit is contained in:
@@ -8,12 +8,15 @@ authors = ["Luke Parker <lukeparker5132@gmail.com>"]
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
async-trait = "0.1"
|
||||||
thiserror = "1"
|
thiserror = "1"
|
||||||
|
|
||||||
|
subtle = "^2"
|
||||||
zeroize = { version = "^1.5", optional = true }
|
zeroize = { version = "^1.5", optional = true }
|
||||||
rand_core = { version = "0.6", optional = true }
|
rand_core = { version = "0.6", optional = true }
|
||||||
|
|
||||||
blake2 = "0.10"
|
blake2 = "0.10"
|
||||||
|
transcript = { package = "flexible-transcript", path = "../../crypto/transcript", features = ["recommended"] }
|
||||||
|
|
||||||
ciphersuite = { package = "ciphersuite", path = "../../crypto/ciphersuite", features = ["ristretto"] }
|
ciphersuite = { package = "ciphersuite", path = "../../crypto/ciphersuite", features = ["ristretto"] }
|
||||||
schnorr = { package = "schnorr-signatures", path = "../../crypto/schnorr" }
|
schnorr = { package = "schnorr-signatures", path = "../../crypto/schnorr" }
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ mod transaction;
|
|||||||
pub use transaction::*;
|
pub use transaction::*;
|
||||||
|
|
||||||
mod provided;
|
mod provided;
|
||||||
pub use provided::*;
|
pub(crate) use provided::*;
|
||||||
|
|
||||||
mod block;
|
mod block;
|
||||||
pub use block::*;
|
pub use block::*;
|
||||||
@@ -18,6 +18,9 @@ pub use blockchain::*;
|
|||||||
mod mempool;
|
mod mempool;
|
||||||
pub use mempool::*;
|
pub use mempool::*;
|
||||||
|
|
||||||
|
mod tendermint;
|
||||||
|
pub use crate::tendermint::*;
|
||||||
|
|
||||||
#[cfg(any(test, feature = "tests"))]
|
#[cfg(any(test, feature = "tests"))]
|
||||||
pub mod tests;
|
pub mod tests;
|
||||||
|
|
||||||
|
|||||||
128
coordinator/tributary/src/tendermint.rs
Normal file
128
coordinator/tributary/src/tendermint.rs
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
use core::ops::Deref;
|
||||||
|
|
||||||
|
use subtle::ConstantTimeEq;
|
||||||
|
use zeroize::{Zeroize, Zeroizing};
|
||||||
|
|
||||||
|
use transcript::{Transcript, RecommendedTranscript};
|
||||||
|
|
||||||
|
use ciphersuite::{
|
||||||
|
group::{
|
||||||
|
GroupEncoding,
|
||||||
|
ff::{Field, PrimeField},
|
||||||
|
},
|
||||||
|
Ciphersuite, Ristretto,
|
||||||
|
};
|
||||||
|
use schnorr::SchnorrSignature;
|
||||||
|
|
||||||
|
use tendermint::ext::{Signer as SignerTrait, SignatureScheme as SignatureSchemeTrait};
|
||||||
|
|
||||||
|
fn challenge(
|
||||||
|
genesis: [u8; 32],
|
||||||
|
key: [u8; 32],
|
||||||
|
nonce: &[u8],
|
||||||
|
msg: &[u8],
|
||||||
|
) -> <Ristretto as Ciphersuite>::F {
|
||||||
|
let mut transcript = RecommendedTranscript::new(b"Tributary Chain Tendermint Message");
|
||||||
|
transcript.append_message(b"genesis", genesis);
|
||||||
|
transcript.append_message(b"key", key);
|
||||||
|
transcript.append_message(b"nonce", nonce);
|
||||||
|
transcript.append_message(b"message", msg);
|
||||||
|
|
||||||
|
<Ristretto as Ciphersuite>::F::from_bytes_mod_order_wide(&transcript.challenge(b"schnorr").into())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
struct Signer {
|
||||||
|
genesis: [u8; 32],
|
||||||
|
key: Zeroizing<<Ristretto as Ciphersuite>::F>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl SignerTrait for Signer {
|
||||||
|
type ValidatorId = [u8; 32];
|
||||||
|
type Signature = [u8; 64];
|
||||||
|
|
||||||
|
/// Returns the validator's current ID. Returns None if they aren't a current validator.
|
||||||
|
async fn validator_id(&self) -> Option<Self::ValidatorId> {
|
||||||
|
Some((Ristretto::generator() * self.key.deref()).to_bytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Sign a signature with the current validator's private key.
|
||||||
|
async fn sign(&self, msg: &[u8]) -> Self::Signature {
|
||||||
|
let mut nonce = Zeroizing::new(RecommendedTranscript::new(b"Tributary Chain Tendermint Nonce"));
|
||||||
|
nonce.append_message(b"genesis", self.genesis);
|
||||||
|
nonce.append_message(b"key", Zeroizing::new(self.key.deref().to_repr()).as_ref());
|
||||||
|
nonce.append_message(b"message", msg);
|
||||||
|
let mut nonce = nonce.challenge(b"nonce");
|
||||||
|
|
||||||
|
let mut nonce_arr = [0; 64];
|
||||||
|
nonce_arr.copy_from_slice(nonce.as_ref());
|
||||||
|
|
||||||
|
let nonce_ref: &mut [u8] = nonce.as_mut();
|
||||||
|
nonce_ref.zeroize();
|
||||||
|
let nonce_ref: &[u8] = nonce.as_ref();
|
||||||
|
assert_eq!(nonce_ref, [0; 64].as_ref());
|
||||||
|
|
||||||
|
let nonce =
|
||||||
|
Zeroizing::new(<Ristretto as Ciphersuite>::F::from_bytes_mod_order_wide(&nonce_arr));
|
||||||
|
nonce_arr.zeroize();
|
||||||
|
|
||||||
|
assert!(!bool::from(nonce.ct_eq(&<Ristretto as Ciphersuite>::F::ZERO)));
|
||||||
|
|
||||||
|
let challenge = challenge(
|
||||||
|
self.genesis,
|
||||||
|
(Ristretto::generator() * self.key.deref()).to_bytes(),
|
||||||
|
(Ristretto::generator() * nonce.deref()).to_bytes().as_ref(),
|
||||||
|
msg,
|
||||||
|
);
|
||||||
|
|
||||||
|
let sig = SchnorrSignature::<Ristretto>::sign(&self.key, nonce, challenge).serialize();
|
||||||
|
|
||||||
|
let mut res = [0; 64];
|
||||||
|
res.copy_from_slice(&sig);
|
||||||
|
res
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, PartialEq, Eq, Debug)]
|
||||||
|
struct SignatureScheme {
|
||||||
|
genesis: [u8; 32],
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SignatureSchemeTrait for SignatureScheme {
|
||||||
|
type ValidatorId = [u8; 32];
|
||||||
|
type Signature = [u8; 64];
|
||||||
|
// TODO: Use half-aggregation.
|
||||||
|
type AggregateSignature = Vec<[u8; 64]>;
|
||||||
|
type Signer = Signer;
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn verify(&self, validator: Self::ValidatorId, msg: &[u8], sig: &Self::Signature) -> bool {
|
||||||
|
let Ok(validator_point) = Ristretto::read_G::<&[u8]>(&mut validator.as_ref()) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
let Ok(actual_sig) = SchnorrSignature::<Ristretto>::read::<&[u8]>(&mut sig.as_ref()) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
actual_sig.verify(validator_point, challenge(self.genesis, validator, &sig[.. 32], msg))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn aggregate(sigs: &[Self::Signature]) -> Self::AggregateSignature {
|
||||||
|
sigs.to_vec()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[must_use]
|
||||||
|
fn verify_aggregate(
|
||||||
|
&self,
|
||||||
|
signers: &[Self::ValidatorId],
|
||||||
|
msg: &[u8],
|
||||||
|
sig: &Self::AggregateSignature,
|
||||||
|
) -> bool {
|
||||||
|
for (signer, sig) in signers.iter().zip(sig.iter()) {
|
||||||
|
if !self.verify(*signer, msg, sig) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user