code/pallets/drand/src/verifier.rs
/*
* Copyright 2024 by Ideal Labs, LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//! A collection of verifiers
//!
//!
use crate::{
bls12_381,
types::{BeaconConfiguration, Pulse, RoundNumber},
};
use alloc::{format, string::String, vec::Vec};
use ark_ec::{AffineRepr, hashing::HashToCurve};
use ark_serialize::CanonicalSerialize;
use codec::Decode;
use sha2::{Digest, Sha256};
use sp_crypto_ec_utils::bls12_381::{G1Affine as G1AffineOpt, G2Affine as G2AffineOpt};
use tle::curves::drand::TinyBLS381;
use w3f_bls::engine::EngineBLS;
const USAGE: ark_scale::Usage = ark_scale::WIRE;
pub type ArkScale<T> = ark_scale::ArkScale<T, USAGE>;
/// construct a message (e.g. signed by drand)
fn message(current_round: RoundNumber, prev_sig: &[u8]) -> Vec<u8> {
let mut hasher = Sha256::default();
hasher.update(prev_sig);
hasher.update(current_round.to_be_bytes());
hasher.finalize().to_vec()
}
/// something to verify beacon pulses
pub trait Verifier {
/// verify the given pulse using beacon_config
fn verify(beacon_config: BeaconConfiguration, pulse: Pulse) -> Result<bool, String>;
}
/// A verifier to check values received from quicknet. It outputs true if valid, false otherwise
///
/// [Quicknet](https://drand.love/blog/quicknet-is-live-on-the-league-of-entropy-mainnet) operates in an unchained mode,
/// so messages contain only the round number. in addition, public keys are in G2 and signatures are
/// in G1
///
/// Values are valid if the pairing equality holds:
/// $e(sig, g_2) == e(msg_on_curve, pk)$
/// where $sig \in \mathbb{G}_1$ is the signature
/// $g_2 \in \mathbb{G}_2$ is a generator
/// $msg_on_curve \in \mathbb{G}_1$ is a hash of the message that drand signed
/// (hash(round_number)) $pk \in \mathbb{G}_2$ is the public key, read from the input public
/// parameters
pub struct QuicknetVerifier;
impl Verifier for QuicknetVerifier {
fn verify(beacon_config: BeaconConfiguration, pulse: Pulse) -> Result<bool, String> {
// decode public key (pk)
let pk =
ArkScale::<G2AffineOpt>::decode(&mut beacon_config.public_key.into_inner().as_slice())
.map_err(|e| format!("Failed to decode public key: {e}"))?;
// decode signature (sigma)
let signature =
ArkScale::<G1AffineOpt>::decode(&mut pulse.signature.into_inner().as_slice())
.map_err(|e| format!("Failed to decode signature: {e}"))?;
// m = sha256({} || {round})
let message = message(pulse.round, &[]);
let hasher = <TinyBLS381 as EngineBLS>::hash_to_curve_map();
// H(m) \in G1
let message_hash = hasher
.hash(&message)
.map_err(|e| format!("Failed to hash message: {e}"))?;
let mut bytes = Vec::new();
message_hash
.serialize_compressed(&mut bytes)
.map_err(|e| format!("Failed to serialize message hash: {e}"))?;
let message_on_curve = ArkScale::<G1AffineOpt>::decode(&mut &bytes[..])
.map_err(|e| format!("Failed to decode message on curve: {e}"))?;
let g2 = G2AffineOpt::generator();
Ok(bls12_381::fast_pairing_opt(
signature.0,
g2,
message_on_curve.0,
pk.0,
))
}
}
/// The unsafe skip verifier is just a pass-through verification, always returns true
pub struct UnsafeSkipVerifier;
impl Verifier for UnsafeSkipVerifier {
fn verify(_beacon_config: BeaconConfiguration, _pulse: Pulse) -> Result<bool, String> {
Ok(true)
}
}