Created
July 4, 2024 14:59
-
-
Save sourabhniyogi/7097609935fc7a584d71731acdf32027 to your computer and use it in GitHub Desktop.
Working Safrole jamtestvector E_T signature verification
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
use ark_ec_vrfs::suites::bandersnatch::edwards as bandersnatch; | |
use ark_ec_vrfs::{prelude::ark_serialize, suites::bandersnatch::edwards::RingContext}; | |
use bandersnatch::{IetfProof, Input, Output, Public, RingProof, Secret}; | |
use ark_serialize::{CanonicalDeserialize, CanonicalSerialize}; | |
const RING_SIZE: usize = 6; | |
// This is the IETF `Prove` procedure output as described in section 2.2 | |
// of the Bandersnatch VRFs specification | |
#[derive(CanonicalSerialize, CanonicalDeserialize)] | |
struct IetfVrfSignature { | |
output: Output, | |
proof: IetfProof, | |
} | |
// This is the IETF `Prove` procedure output as described in section 4.2 | |
// of the Bandersnatch VRFs specification | |
#[derive(CanonicalSerialize, CanonicalDeserialize)] | |
struct RingVrfSignature { | |
output: Output, | |
// This contains both the Pedersen proof and actual ring proof. | |
proof: RingProof, | |
} | |
// "Static" ring context data | |
fn ring_context() -> &'static RingContext { | |
use std::sync::OnceLock; | |
static RING_CTX: OnceLock<RingContext> = OnceLock::new(); | |
RING_CTX.get_or_init(|| { | |
use bandersnatch::PcsParams; | |
use std::{fs::File, io::Read}; | |
let manifest_dir = | |
std::env::var("CARGO_MANIFEST_DIR").expect("CARGO_MANIFEST_DIR is not set"); | |
let filename = format!("{}/data/zcash-srs-2-11-uncompressed.bin", manifest_dir); | |
println!("ring_context loaded from {}", filename); | |
let mut file = File::open(filename).unwrap(); | |
let mut buf = Vec::new(); | |
file.read_to_end(&mut buf).unwrap(); | |
let pcs_params = PcsParams::deserialize_uncompressed_unchecked(&mut &buf[..]).unwrap(); | |
RingContext::from_srs(pcs_params, RING_SIZE).unwrap() | |
}) | |
} | |
// Construct VRF Input Point from arbitrary data (section 1.2) | |
fn vrf_input_point(vrf_input_data: &[u8]) -> Input { | |
let point = | |
<bandersnatch::BandersnatchSha512Ell2 as ark_ec_vrfs::Suite>::data_to_point(vrf_input_data) | |
.unwrap(); | |
Input::from(point) | |
} | |
// Prover actor. | |
pub struct Prover { | |
pub prover_idx: usize, | |
pub secret: Secret, | |
pub ring: Vec<Public>, | |
} | |
type RingCommitment = ark_ec_vrfs::ring::RingCommitment<bandersnatch::BandersnatchSha512Ell2>; | |
// Verifier actor. | |
pub struct Verifier { | |
pub commitment: RingCommitment, | |
pub ring: Vec<Public>, | |
} | |
impl Verifier { | |
pub fn new(ring: Vec<Public>) -> Self { | |
// Backend currently requires the wrapped type (plain affine points) | |
let pts: Vec<_> = ring.iter().map(|pk| pk.0).collect(); | |
let verifier_key = ring_context().verifier_key(&pts); | |
let commitment = verifier_key.commitment(); | |
Self { ring, commitment } | |
} | |
/// Anonymous VRF signature verification. | |
/// | |
/// Used for tickets verification. | |
pub fn ring_vrf_verify( | |
&self, | |
vrf_input_data: &[u8], | |
aux_data: &[u8], | |
signature: &[u8], | |
) -> bool { | |
use ark_ec_vrfs::ring::prelude::fflonk::pcs::PcsParams; | |
use ark_ec_vrfs::ring::Verifier as _; | |
use bandersnatch::VerifierKey; | |
let signature = RingVrfSignature::deserialize_compressed(signature).unwrap(); | |
let input = vrf_input_point(vrf_input_data); | |
let output = signature.output; | |
let ring_ctx = ring_context(); | |
// The verifier key is reconstructed from the commitment and the constant | |
// verifier key component of the SRS in order to verify some proof. | |
// As an alternative we can construct the verifier key using the | |
// RingContext::verifier_key() method, but is more expensive. | |
// In other words, we prefer computing the commitment once, when the keyset changes. | |
let verifier_key = VerifierKey::from_commitment_and_kzg_vk( | |
self.commitment.clone(), | |
ring_ctx.pcs_params.raw_vk(), | |
); | |
let verifier = ring_ctx.verifier(verifier_key); | |
let result = Public::verify(input, output, aux_data, &signature.proof, &verifier).is_ok(); | |
// Print vrf_input_data, aux_data, and signature in hex form | |
println!("vrf_input_data: {}", hex::encode(vrf_input_data)); | |
println!("aux_data: {}", hex::encode(aux_data)); | |
let mut signature_buf = Vec::new(); | |
signature.serialize_compressed(&mut signature_buf).unwrap(); | |
println!("signature: {}", hex::encode(signature_buf)); | |
if !result { | |
println!("Ring signature verification failure"); | |
return result; | |
} | |
println!("Ring signature verified"); | |
// This truncated hash is the actual value used as ticket-id/score | |
println!(" vrf-output-hash: {}", hex::encode(&output.hash()[..32])); | |
result | |
} | |
} | |
#[cfg(test)] | |
mod tests { | |
use super::*; | |
#[test] | |
fn test_tiny_extrinsic() { | |
// build ring from next_epoch pub keys in gamma_k | |
// https://github.com/w3f/jamtestvectors/blob/826e64f74a352a059f8b172447af7927f02e0fae/safrole/tiny/publish-tickets-no-mark-2.json#L108-L144 | |
let ring_set_hex = [ | |
"5e465beb01dbafe160ce8216047f2155dd0569f058afd52dcea601025a8d161d", | |
"3d5e5a51aab2b048f8686ecd79712a80e3265a114cc73f14bdb2a59233fb66d0", | |
"aa2b95f7572875b0d0f186552ae745ba8222fc0b5bd456554bfe51c68938f8bc", | |
"7f6190116d118d643a98878e294ccf62b509e214299931aad8ff9764181a4e33", | |
"48e5fcdce10e0b64ec4eebd0d9211c7bac2f27ce54bca6f7776ff6fee86ab3e3", | |
"f16e5352840afb47e206b5c89f560f2611835855cf2e6ebad1acc9520a72591d", | |
]; | |
let ring_set: Vec<Public> = ring_set_hex | |
.iter() | |
.map(|hex_str| { | |
let bytes = hex::decode(hex_str).expect("Decoding hex string failed"); | |
let point = bandersnatch::Public::deserialize_compressed(&bytes[..]) | |
.expect("Deserialization failed"); | |
point | |
}) | |
.collect(); | |
// eta_2 from https://github.com/w3f/jamtestvectors/blob/1ff3f1ad9ae5df30157d5b11f9808849de246877/safrole/tiny/publish-tickets-no-mark-2.json#L214 | |
let mut vrf_input_data = Vec::from(b"jam_ticket_seal"); | |
vrf_input_data.extend_from_slice( | |
&hex::decode("bb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aa") | |
.expect("Decoding hex string failed"), | |
); | |
// attempt 1 from https://github.com/w3f/jamtestvectors/blob/826e64f74a352a059f8b172447af7927f02e0fae/safrole/tiny/publish-tickets-no-mark-2.json#L8 | |
vrf_input_data.push(1); | |
// no data | |
let aux_data = vec![]; | |
// https://github.com/w3f/jamtestvectors/blob/1ff3f1ad9ae5df30157d5b11f9808849de246877/safrole/tiny/publish-tickets-no-mark-2.json#L8C31-L9C13 | |
let signature_hex = "b342bf8f6fa69c745daad2e99c92929b1da2b840f67e5e8015ac22dd1076343ea95c5bb4b69c197bfdc1b7d2f484fe455fb19bba7e8d17fcaf309ba5814bf54f3a74d75b408da8d3b99bf07f7cde373e4fd757061b1c99e0aac4847f1e393e892b566c14a7f8643a5d976ced0a18d12e32c660d59c66c271332138269cb0fe9c2462d5b3c1a6e9f5ed330ff0d70f64218010ff337b0b69b531f916c67ec564097cd842306df1b4b44534c95ff4efb73b17a14476057fdf8678683b251dc78b0b94712179345c794b6bd99aa54b564933651aee88c93b648e91a613c87bc3f445fff571452241e03e7d03151600a6ee259051a23086b408adec7c112dd94bd8123cf0bed88fddac46b7f891f34c29f13bf883771725aa234d398b13c39fd2a871894f1b1e2dbc7fffbc9c65c49d1e9fd5ee0da133bef363d4ebebe63de2b50328b5d7e020303499d55c07cae617091e33a1ee72ba1b65f940852e93e2905fdf577adcf62be9c74ebda9af59d3f11bece8996773f392a2b35693a45a5a042d88a3dc816b689fe596762d4ea7c6024da713304f56dc928be6e8048c651766952b6c40d0f48afc067ca7cbd77763a2d4f11e88e16033b3343f39bf519fe734db8a139d148ccead4331817d46cf469befa64ae153b5923869144dfa669da36171c20e1f757ed5231fa5a08827d83f7b478ddfb44c9bceb5c6c920b8761ff1e3edb03de48fb55884351f0ac5a7a1805b9b6c49c0529deb97e994deaf2dfd008825e8704cdc04b621f316b505fde26ab71b31af7becbc1154f9979e43e135d35720b93b367bedbe6c6182bb6ed99051f28a3ad6d348ba5b178e3ea0ec0bb4a03fe36604a9eeb609857f8334d3b4b34867361ed2ff9163acd9a27fa20303abe9fc29f2d6c921a8ee779f7f77d940b48bc4fce70a58eed83a206fb7db4c1c7ebe7658603495bb40f6a581dd9e235ba0583165b1569052f8fb4a3e604f2dd74ad84531c6b96723c867b06b6fdd1c4ba150cf9080aa6bbf44cc29041090973d56913b9dc755960371568ef1cf03f127fe8eca209db5d18829f5bfb5826f98833e3f42472b47fad995a9a8bb0e41a1df45ead20285a8"; | |
// https://github.com/w3f/jamtestvectors/blob/1ff3f1ad9ae5df30157d5b11f9808849de246877/safrole/tiny/publish-tickets-no-mark-2.json#L201 | |
let signature_bytes = hex::decode(signature_hex).expect("Decoding hex string failed"); | |
// Create the Verifier | |
let verifier = Verifier::new(ring_set); | |
let expected_gamma_z = "96a4b479612d8d2770d6a4785fa2f44e0befca6f008b176de51e309e2ee796d2b596e315fcb044495b75c3cb5c7fd4cdae0959758cac93d4ab8789c6aec4ba8f683c6b103cf6888f70edfcb8dcbbc1d85a8fa3832e0cd4503c7a1796c8d0c3f792e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf"; | |
let mut buf2 = Vec::new(); | |
verifier.commitment.serialize_compressed(&mut buf2).unwrap(); | |
let gamma_z = hex::encode(buf2); | |
assert_eq!( | |
expected_gamma_z, gamma_z, | |
"The gamma_z does not match the expected value!" | |
); | |
println!("gamma_z (commitment): {}", gamma_z); | |
let res = verifier.ring_vrf_verify(&vrf_input_data, &aux_data, &signature_bytes); | |
assert!(res); | |
println!("Verification result: {}", res); | |
} | |
} | |
/* | |
# cargo test -- --nocapture | |
Compiling bandersnatch v0.1.0 (/root/go/src/github.com/colorfulnotion/jam/bandersnatch) | |
Finished test [unoptimized + debuginfo] target(s) in 2.14s | |
Running unittests src/lib.rs (target/debug/deps/bandersnatch-fc73acc45ed52f71) | |
running 1 test | |
ring_context loaded from /root/go/src/github.com/colorfulnotion/jam/bandersnatch/data/zcash-srs-2-11-uncompressed.bin | |
gamma_z (commitment): 96a4b479612d8d2770d6a4785fa2f44e0befca6f008b176de51e309e2ee796d2b596e315fcb044495b75c3cb5c7fd4cdae0959758cac93d4ab8789c6aec4ba8f683c6b103cf6888f70edfcb8dcbbc1d85a8fa3832e0cd4503c7a1796c8d0c3f792e630ae2b14e758ab0960e372172203f4c9a41777dadd529971d7ab9d23ab29fe0e9c85ec450505dde7f5ac038274cf | |
vrf_input_data: 6a616d5f7469636b65745f7365616cbb30a42c1e62f0afda5f0a4e8a562f7a13a24cea00ee81917b86b89e801314aa01 | |
aux_data: | |
signature: b342bf8f6fa69c745daad2e99c92929b1da2b840f67e5e8015ac22dd1076343ea95c5bb4b69c197bfdc1b7d2f484fe455fb19bba7e8d17fcaf309ba5814bf54f3a74d75b408da8d3b99bf07f7cde373e4fd757061b1c99e0aac4847f1e393e892b566c14a7f8643a5d976ced0a18d12e32c660d59c66c271332138269cb0fe9c2462d5b3c1a6e9f5ed330ff0d70f64218010ff337b0b69b531f916c67ec564097cd842306df1b4b44534c95ff4efb73b17a14476057fdf8678683b251dc78b0b94712179345c794b6bd99aa54b564933651aee88c93b648e91a613c87bc3f445fff571452241e03e7d03151600a6ee259051a23086b408adec7c112dd94bd8123cf0bed88fddac46b7f891f34c29f13bf883771725aa234d398b13c39fd2a871894f1b1e2dbc7fffbc9c65c49d1e9fd5ee0da133bef363d4ebebe63de2b50328b5d7e020303499d55c07cae617091e33a1ee72ba1b65f940852e93e2905fdf577adcf62be9c74ebda9af59d3f11bece8996773f392a2b35693a45a5a042d88a3dc816b689fe596762d4ea7c6024da713304f56dc928be6e8048c651766952b6c40d0f48afc067ca7cbd77763a2d4f11e88e16033b3343f39bf519fe734db8a139d148ccead4331817d46cf469befa64ae153b5923869144dfa669da36171c20e1f757ed5231fa5a08827d83f7b478ddfb44c9bceb5c6c920b8761ff1e3edb03de48fb55884351f0ac5a7a1805b9b6c49c0529deb97e994deaf2dfd008825e8704cdc04b621f316b505fde26ab71b31af7becbc1154f9979e43e135d35720b93b367bedbe6c6182bb6ed99051f28a3ad6d348ba5b178e3ea0ec0bb4a03fe36604a9eeb609857f8334d3b4b34867361ed2ff9163acd9a27fa20303abe9fc29f2d6c921a8ee779f7f77d940b48bc4fce70a58eed83a206fb7db4c1c7ebe7658603495bb40f6a581dd9e235ba0583165b1569052f8fb4a3e604f2dd74ad84531c6b96723c867b06b6fdd1c4ba150cf9080aa6bbf44cc29041090973d56913b9dc755960371568ef1cf03f127fe8eca209db5d18829f5bfb5826f98833e3f42472b47fad995a9a8bb0e41a1df45ead20285a8 | |
Ring signature verified | |
vrf-output-hash: 09a696b142112c0af1cd2b5f91726f2c050112078e3ef733198c5f43daa20d2b | |
Verification result: true | |
test tests::test_tiny_extrinsic ... ok | |
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.63s | |
*/ |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment