diff --git a/Cargo.lock b/Cargo.lock index a95a7e2561d6..2135fe64e6f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -513,7 +513,7 @@ dependencies = [ [[package]] name = "ark-secret-scalar" version = "0.0.2" -source = "git+https://github.com/w3f/ring-vrf?rev=3119f51#3119f51b54b69308abfb0671f6176cb125ae1bf1" +source = "git+https://github.com/w3f/ring-vrf?rev=f4fe253#f4fe2534ccc6d916cd10d9c16891e673728ec8b4" dependencies = [ "ark-ec", "ark-ff", @@ -561,7 +561,7 @@ dependencies = [ [[package]] name = "ark-transcript" version = "0.0.2" -source = "git+https://github.com/w3f/ring-vrf?rev=3119f51#3119f51b54b69308abfb0671f6176cb125ae1bf1" +source = "git+https://github.com/w3f/ring-vrf?rev=f4fe253#f4fe2534ccc6d916cd10d9c16891e673728ec8b4" dependencies = [ "ark-ff", "ark-serialize", @@ -1240,13 +1240,12 @@ dependencies = [ [[package]] name = "bandersnatch_vrfs" version = "0.0.1" -source = "git+https://github.com/w3f/ring-vrf?rev=3119f51#3119f51b54b69308abfb0671f6176cb125ae1bf1" +source = "git+https://github.com/w3f/ring-vrf?rev=f4fe253#f4fe2534ccc6d916cd10d9c16891e673728ec8b4" dependencies = [ "ark-bls12-381", "ark-ec", "ark-ed-on-bls12-381-bandersnatch", "ark-ff", - "ark-scale", "ark-serialize", "ark-std", "dleq_vrf", @@ -2705,7 +2704,7 @@ dependencies = [ [[package]] name = "common" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof?rev=0e948f3#0e948f3c28cbacecdd3020403c4841c0eb339213" +source = "git+https://github.com/w3f/ring-proof?rev=8657210#86572101f4210647984ab4efedba6b3fcc890895" dependencies = [ "ark-ec", "ark-ff", @@ -2714,6 +2713,7 @@ dependencies = [ "ark-std", "fflonk", "merlin 3.0.0", + "rand_chacha 0.3.1", ] [[package]] @@ -4434,7 +4434,7 @@ checksum = "86e3bdc80eee6e16b2b6b0f87fbc98c04bee3455e35174c0de1a125d0688c632" [[package]] name = "dleq_vrf" version = "0.0.2" -source = "git+https://github.com/w3f/ring-vrf?rev=3119f51#3119f51b54b69308abfb0671f6176cb125ae1bf1" +source = "git+https://github.com/w3f/ring-vrf?rev=f4fe253#f4fe2534ccc6d916cd10d9c16891e673728ec8b4" dependencies = [ "ark-ec", "ark-ff", @@ -14030,13 +14030,14 @@ dependencies = [ [[package]] name = "ring" version = "0.1.0" -source = "git+https://github.com/w3f/ring-proof?rev=0e948f3#0e948f3c28cbacecdd3020403c4841c0eb339213" +source = "git+https://github.com/w3f/ring-proof?rev=8657210#86572101f4210647984ab4efedba6b3fcc890895" dependencies = [ "ark-ec", "ark-ff", "ark-poly", "ark-serialize", "ark-std", + "blake2", "common", "fflonk", "merlin 3.0.0", @@ -17052,7 +17053,6 @@ name = "sp-core" version = "21.0.0" dependencies = [ "array-bytes", - "arrayvec 0.7.4", "bandersnatch_vrfs", "bitflags 1.3.2", "blake2", diff --git a/substrate/primitives/core/Cargo.toml b/substrate/primitives/core/Cargo.toml index 4e5186f16878..9f1f0127d7cd 100644 --- a/substrate/primitives/core/Cargo.toml +++ b/substrate/primitives/core/Cargo.toml @@ -13,7 +13,6 @@ documentation = "https://docs.rs/sp-core" targets = ["x86_64-unknown-linux-gnu"] [dependencies] -arrayvec = { version = "0.7.2", default-features = false } codec = { package = "parity-scale-codec", version = "3.6.1", default-features = false, features = ["derive","max-encoded-len"] } scale-info = { version = "2.5.0", default-features = false, features = ["derive"] } log = { version = "0.4.17", default-features = false } @@ -58,7 +57,7 @@ sp-runtime-interface = { path = "../runtime-interface", default-features = false # bls crypto w3f-bls = { version = "0.1.3", default-features = false, optional = true} # bandersnatch crypto -bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", rev = "3119f51", default-features = false, optional = true } +bandersnatch_vrfs = { git = "https://github.com/w3f/ring-vrf", rev = "f4fe253", default-features = false, optional = true } [dev-dependencies] criterion = "0.4.0" @@ -76,7 +75,6 @@ bench = false default = [ "std" ] std = [ "array-bytes", - "arrayvec/std", "bandersnatch_vrfs/getrandom", "blake2/std", "bounded-collections/std", diff --git a/substrate/primitives/core/src/bandersnatch.rs b/substrate/primitives/core/src/bandersnatch.rs index 01f3538188a4..832ef6c77bb3 100644 --- a/substrate/primitives/core/src/bandersnatch.rs +++ b/substrate/primitives/core/src/bandersnatch.rs @@ -53,9 +53,8 @@ const SEED_SERIALIZED_LEN: usize = 32; // Short-Weierstrass form serialized sizes. const PUBLIC_SERIALIZED_LEN: usize = 33; const SIGNATURE_SERIALIZED_LEN: usize = 65; +const RING_SIGNATURE_SERIALIZED_LEN: usize = 755; const PREOUT_SERIALIZED_LEN: usize = 33; -const PEDERSEN_SIGNATURE_SERIALIZED_LEN: usize = 163; -const RING_PROOF_SERIALIZED_LEN: usize = 592; // Max size of serialized ring-vrf context params. // @@ -69,7 +68,7 @@ const RING_PROOF_SERIALIZED_LEN: usize = 592; // 2048 → 295 KB // NOTE: This is quite big but looks like there is an upcoming fix // in the backend. -const RING_CONTEXT_SERIALIZED_LEN: usize = 147752; +const RING_CONTEXT_SERIALIZED_LEN: usize = 147748; /// Bandersnatch public key. #[cfg_attr(feature = "full_crypto", derive(Hash))] @@ -278,7 +277,7 @@ impl TraitPair for Pair { let mut raw = [0; PUBLIC_SERIALIZED_LEN]; public .serialize_compressed(raw.as_mut_slice()) - .expect("key buffer length is good; qed"); + .expect("serialization length is constant and checked by test; qed"); Public::unchecked_from(raw) } @@ -356,7 +355,7 @@ pub mod vrf { let mut bytes = [0; PREOUT_SERIALIZED_LEN]; self.0 .serialize_compressed(bytes.as_mut_slice()) - .expect("preout serialization can't fail"); + .expect("serialization length is constant and checked by test; qed"); bytes.encode() } } @@ -506,7 +505,7 @@ pub mod vrf { } fn vrf_output(&self, input: &Self::VrfInput) -> Self::VrfOutput { - let output = self.secret.0.vrf_preout(&input.0); + let output = self.secret.vrf_preout(&input.0); VrfOutput(output) } } @@ -539,24 +538,26 @@ pub mod vrf { #[cfg(feature = "full_crypto")] impl Pair { fn vrf_sign_gen(&self, data: &VrfSignData) -> VrfSignature { - let ios: Vec<_> = data - .inputs - .iter() - .map(|i| self.secret.clone().0.vrf_inout(i.0.clone())) - .collect(); + let ios = core::array::from_fn(|i| { + let input = data.inputs[i].0.clone(); + self.secret.vrf_inout(input) + }); - let signature: ThinVrfSignature = - self.secret.sign_thin_vrf(data.transcript.clone(), ios.as_slice()); + let thin_signature: ThinVrfSignature = + self.secret.sign_thin_vrf(data.transcript.clone(), &ios); - let mut sign_bytes = [0; SIGNATURE_SERIALIZED_LEN]; - signature - .signature - .serialize_compressed(sign_bytes.as_mut_slice()) - .expect("serialization can't fail"); - - let outputs: Vec<_> = signature.preoutputs.into_iter().map(VrfOutput).collect(); + let outputs: Vec<_> = thin_signature.preouts.into_iter().map(VrfOutput).collect(); let outputs = VrfIosVec::truncate_from(outputs); - VrfSignature { signature: Signature(sign_bytes), outputs } + + let mut signature = + VrfSignature { signature: Signature([0; SIGNATURE_SERIALIZED_LEN]), outputs }; + + thin_signature + .proof + .serialize_compressed(signature.signature.0.as_mut_slice()) + .expect("serialization length is constant and checked by test; qed"); + + signature } /// Generate an arbitrary number of bytes from the given `context` and VRF `input`. @@ -566,7 +567,7 @@ pub mod vrf { input: &VrfInput, ) -> [u8; N] { let transcript = Transcript::new_labeled(context); - let inout = self.secret.clone().0.vrf_inout(input.0.clone()); + let inout = self.secret.vrf_inout(input.0.clone()); inout.vrf_output_bytes(transcript) } } @@ -581,30 +582,23 @@ pub mod vrf { return false }; - let Ok(preouts) = signature - .outputs - .iter() - .map(|o| o.0.clone()) - .collect::>() - .into_inner() - else { - return false - }; + let preouts: [bandersnatch_vrfs::VrfPreOut; N] = + core::array::from_fn(|i| signature.outputs[i].0.clone()); // Deserialize only the proof, the rest has already been deserialized // This is another hack used because backend signature type is generic over // the number of ios. - let Ok(signature) = + let Ok(proof) = ThinVrfSignature::<0>::deserialize_compressed(signature.signature.as_ref()) - .map(|s| s.signature) + .map(|s| s.proof) else { return false }; - let signature = ThinVrfSignature { signature, preoutputs: preouts }; + let signature = ThinVrfSignature { proof, preouts }; let inputs = data.inputs.iter().map(|i| i.0.clone()); - signature.verify_thin_vrf(data.transcript.clone(), inputs, &public).is_ok() + public.verify_thin_vrf(data.transcript.clone(), inputs, &signature).is_ok() } } @@ -627,7 +621,7 @@ pub mod vrf { pub mod ring_vrf { use super::{vrf::*, *}; pub use bandersnatch_vrfs::ring::{RingProof, RingProver, RingVerifier, KZG}; - use bandersnatch_vrfs::{CanonicalDeserialize, PedersenVrfSignature, PublicKey}; + use bandersnatch_vrfs::{CanonicalDeserialize, PublicKey}; /// Context used to produce ring signatures. #[derive(Clone)] @@ -649,7 +643,7 @@ pub mod ring_vrf { let mut pks = Vec::with_capacity(public_keys.len()); for public_key in public_keys { let pk = PublicKey::deserialize_compressed(public_key.as_slice()).ok()?; - pks.push(pk.0 .0.into()); + pks.push(pk.0.into()); } let prover_key = self.0.prover_key(pks); @@ -662,7 +656,7 @@ pub mod ring_vrf { let mut pks = Vec::with_capacity(public_keys.len()); for public_key in public_keys { let pk = PublicKey::deserialize_compressed(public_key.as_slice()).ok()?; - pks.push(pk.0 .0.into()); + pks.push(pk.0.into()); } let verifier_key = self.0.verifier_key(pks); @@ -676,7 +670,7 @@ pub mod ring_vrf { let mut buf = Box::new([0; RING_CONTEXT_SERIALIZED_LEN]); self.0 .serialize_compressed(buf.as_mut_slice()) - .expect("preout serialization can't fail"); + .expect("serialization length is constant and checked by test; qed"); buf.encode() } } @@ -711,10 +705,8 @@ pub mod ring_vrf { pub struct RingVrfSignature { /// VRF (pre)outputs. pub outputs: VrfIosVec, - /// Pedersen VRF signature. - pub signature: [u8; PEDERSEN_SIGNATURE_SERIALIZED_LEN], - /// Ring proof. - pub ring_proof: [u8; RING_PROOF_SERIALIZED_LEN], + /// Ring signature. + pub signature: [u8; RING_SIGNATURE_SERIALIZED_LEN], } #[cfg(feature = "full_crypto")] @@ -741,31 +733,27 @@ pub mod ring_vrf { data: &VrfSignData, prover: &RingProver, ) -> RingVrfSignature { - let ios: Vec<_> = data - .inputs - .iter() - .map(|i| self.secret.clone().0.vrf_inout(i.0.clone())) - .collect(); + let ios = core::array::from_fn(|i| { + let input = data.inputs[i].0.clone(); + self.secret.vrf_inout(input) + }); let ring_signature: bandersnatch_vrfs::RingVrfSignature = - self.secret.sign_ring_vrf(data.transcript.clone(), ios.as_slice(), prover); + bandersnatch_vrfs::RingProver { ring_prover: prover, secret: &self.secret } + .sign_ring_vrf(data.transcript.clone(), &ios); - let outputs: Vec<_> = ring_signature.preoutputs.into_iter().map(VrfOutput).collect(); + let outputs: Vec<_> = ring_signature.preouts.into_iter().map(VrfOutput).collect(); let outputs = VrfIosVec::truncate_from(outputs); - let mut signature = [0; PEDERSEN_SIGNATURE_SERIALIZED_LEN]; - ring_signature - .signature - .serialize_compressed(signature.as_mut_slice()) - .expect("ped-signature serialization can't fail"); + let mut signature = + RingVrfSignature { outputs, signature: [0; RING_SIGNATURE_SERIALIZED_LEN] }; - let mut ring_proof = [0; RING_PROOF_SERIALIZED_LEN]; ring_signature - .ring_proof - .serialize_compressed(ring_proof.as_mut_slice()) - .expect("ring-proof serialization can't fail"); + .proof + .serialize_compressed(signature.signature.as_mut_slice()) + .expect("serialization length is constant and checked by test; qed"); - RingVrfSignature { outputs, signature, ring_proof } + signature } } @@ -774,7 +762,7 @@ pub mod ring_vrf { /// /// The signature is verifiable if it has been produced by a member of the ring /// from which the [`RingVerifier`] has been constructed. - pub fn verify(&self, data: &VrfSignData, verifier: &RingVerifier) -> bool { + pub fn ring_vrf_verify(&self, data: &VrfSignData, verifier: &RingVerifier) -> bool { const _: () = assert!(MAX_VRF_IOS == 3, "`MAX_VRF_IOS` expected to be 3"); let preouts_len = self.outputs.len(); if preouts_len != data.inputs.len() { @@ -782,43 +770,37 @@ pub mod ring_vrf { } // Workaround to overcome backend signature generic over the number of IOs. match preouts_len { - 0 => self.verify_gen::<0>(data, verifier), - 1 => self.verify_gen::<1>(data, verifier), - 2 => self.verify_gen::<2>(data, verifier), - 3 => self.verify_gen::<3>(data, verifier), + 0 => self.ring_vrf_verify_gen::<0>(data, verifier), + 1 => self.ring_vrf_verify_gen::<1>(data, verifier), + 2 => self.ring_vrf_verify_gen::<2>(data, verifier), + 3 => self.ring_vrf_verify_gen::<3>(data, verifier), _ => unreachable!(), } } - fn verify_gen(&self, data: &VrfSignData, verifier: &RingVerifier) -> bool { - let Ok(preoutputs) = self - .outputs - .iter() - .map(|o| o.0.clone()) - .collect::>() - .into_inner() - else { - return false - }; - - let Ok(signature) = - PedersenVrfSignature::deserialize_compressed(self.signature.as_slice()) + fn ring_vrf_verify_gen( + &self, + data: &VrfSignData, + verifier: &RingVerifier, + ) -> bool { + let Ok(vrf_signature) = + bandersnatch_vrfs::RingVrfSignature::<0>::deserialize_compressed( + self.signature.as_slice(), + ) else { return false }; - let Ok(ring_proof) = RingProof::deserialize_compressed(self.ring_proof.as_slice()) - else { - return false - }; + let preouts: [bandersnatch_vrfs::VrfPreOut; N] = + core::array::from_fn(|i| self.outputs[i].0.clone()); - let ring_signature = - bandersnatch_vrfs::RingVrfSignature { signature, preoutputs, ring_proof }; + let signature = + bandersnatch_vrfs::RingVrfSignature { proof: vrf_signature.proof, preouts }; let inputs = data.inputs.iter().map(|i| i.0.clone()); - ring_signature - .verify_ring_vrf(data.transcript.clone(), inputs, verifier) + bandersnatch_vrfs::RingVerifier(verifier) + .verify_ring_vrf(data.transcript.clone(), inputs, &signature) .is_ok() } } @@ -840,16 +822,41 @@ mod tests { } #[test] - fn assumptions_sanity_check() { - // Backend - let ring_ctx = RingContext::new_testing(); - let pair = SecretKey::from_seed(DEV_SEED); - let public = pair.to_public(); + fn backend_assumptions_sanity_check() { + let kzg = KZG::testing_kzg_setup([0; 32], RING_DOMAIN_SIZE as u32); + assert_eq!(kzg.max_keyset_size(), RING_DOMAIN_SIZE - 257); + assert_eq!(kzg.compressed_size(), RING_CONTEXT_SERIALIZED_LEN); + + let pks: Vec<_> = (0..16) + .map(|i| SecretKey::from_seed(&[i as u8; 32]).to_public().0.into()) + .collect(); - assert_eq!(public.0.size_of_serialized(), PUBLIC_SERIALIZED_LEN); - assert_eq!(ring_ctx.max_keyset_size(), RING_DOMAIN_SIZE - 257); + let secret = SecretKey::from_seed(&[0u8; 32]); - // Wrapper + let public = secret.to_public(); + assert_eq!(public.compressed_size(), PUBLIC_SERIALIZED_LEN); + + let input = VrfInput::new(b"foo", &[]); + let preout = secret.vrf_preout(&input.0); + assert_eq!(preout.compressed_size(), PREOUT_SERIALIZED_LEN); + + let prover_key = kzg.prover_key(pks); + let ring_prover = kzg.init_ring_prover(prover_key, 0); + + let data = VrfSignData::new_unchecked(b"mydata", &[b"tdata"], None); + + let thin_signature: bandersnatch_vrfs::ThinVrfSignature<0> = + secret.sign_thin_vrf(data.transcript.clone(), &[]); + assert_eq!(thin_signature.compressed_size(), SIGNATURE_SERIALIZED_LEN); + + let ring_signature: bandersnatch_vrfs::RingVrfSignature<0> = + bandersnatch_vrfs::RingProver { ring_prover: &ring_prover, secret: &secret } + .sign_ring_vrf(data.transcript.clone(), &[]); + assert_eq!(ring_signature.compressed_size(), RING_SIGNATURE_SERIALIZED_LEN); + } + + #[test] + fn max_vrf_ios_bound_respected() { let inputs: Vec<_> = (0..MAX_VRF_IOS - 1).map(|_| VrfInput::new(b"", &[])).collect(); let mut sign_data = VrfSignData::new(b"", &[b""], inputs).unwrap(); let res = sign_data.push_vrf_input(VrfInput::new(b"", b"")); @@ -987,7 +994,7 @@ mod tests { let signature = pair.ring_vrf_sign(&data, &prover); let verifier = ring_ctx.verifier(&pks).unwrap(); - assert!(signature.verify(&data, &verifier)); + assert!(signature.ring_vrf_verify(&data, &verifier)); } #[test] @@ -1006,7 +1013,7 @@ mod tests { let signature = pair.ring_vrf_sign(&data, &prover); let verifier = ring_ctx.verifier(&pks).unwrap(); - assert!(!signature.verify(&data, &verifier)); + assert!(!signature.ring_vrf_verify(&data, &verifier)); } #[test] @@ -1062,10 +1069,8 @@ mod tests { let bytes = expected.encode(); - let expected_len = data.inputs.len() * PREOUT_SERIALIZED_LEN + - PEDERSEN_SIGNATURE_SERIALIZED_LEN + - RING_PROOF_SERIALIZED_LEN + - 1; + let expected_len = + data.inputs.len() * PREOUT_SERIALIZED_LEN + RING_SIGNATURE_SERIALIZED_LEN + 1; assert_eq!(bytes.len(), expected_len); let decoded = RingVrfSignature::decode(&mut bytes.as_slice()).unwrap();