diff --git a/substrate/primitives/consensus/sassafras/src/vrf.rs b/substrate/primitives/consensus/sassafras/src/vrf.rs index bdbac0aae03774d589f52054906f56ee6c7056a9..afcb1d09f8f6832f5b794543f1f2896366859a4e 100644 --- a/substrate/primitives/consensus/sassafras/src/vrf.rs +++ b/substrate/primitives/consensus/sassafras/src/vrf.rs @@ -23,10 +23,16 @@ use sp_consensus_slots::Slot; use sp_std::vec::Vec; pub use sp_core::bandersnatch::{ - ring_vrf::{RingContext, RingProver, RingVerifier, RingVerifierData, RingVrfSignature}, + ring_vrf::{RingProver, RingVerifier, RingVerifierData, RingVrfSignature}, vrf::{VrfInput, VrfOutput, VrfSignData, VrfSignature}, }; +/// Ring VRF domain size for Sassafras consensus. +pub const RING_VRF_DOMAIN_SIZE: u32 = 2048; + +/// Bandersnatch VRF [`RingContext`] specialization for Sassafras using [`RING_VRF_DOMAIN_SIZE`]. +pub type RingContext = sp_core::bandersnatch::ring_vrf::RingContext<RING_VRF_DOMAIN_SIZE>; + fn vrf_input_from_data( domain: &[u8], data: impl IntoIterator<Item = impl AsRef<[u8]>>, diff --git a/substrate/primitives/core/src/bandersnatch.rs b/substrate/primitives/core/src/bandersnatch.rs index 1d666f13b6275dea2cc88ba8216844a703e26045..b91c001a7de03952dc87b16984d315852958a825 100644 --- a/substrate/primitives/core/src/bandersnatch.rs +++ b/substrate/primitives/core/src/bandersnatch.rs @@ -39,7 +39,7 @@ use codec::{Decode, Encode, EncodeLike, MaxEncodedLen}; use scale_info::TypeInfo; use sp_runtime_interface::pass_by::PassByInner; -use sp_std::{boxed::Box, vec::Vec}; +use sp_std::{vec, vec::Vec}; /// Identifier used to match public keys against bandersnatch-vrf keys. pub const CRYPTO_ID: CryptoTypeId = CryptoTypeId(*b"band"); @@ -622,31 +622,25 @@ pub mod ring_vrf { pub use bandersnatch_vrfs::ring::{RingProof, RingProver, RingVerifier, KZG}; use bandersnatch_vrfs::{ring::VerifierKey, CanonicalDeserialize, PublicKey}; - /// Ring max size (keyset max size). - pub const RING_MAX_SIZE: u32 = RING_DOMAIN_MAX_SIZE - RING_DOMAIN_OVERHEAD; + /// Overhead in the domain size with respect to the supported ring size. + /// + /// Some bits of the domain are reserved for the zk-proof to work. + pub const RING_DOMAIN_OVERHEAD: u32 = 257; - /// Ring domain max size. - pub const RING_DOMAIN_MAX_SIZE: u32 = 2048; + // Max size of serialized ring-vrf context given `domain_len`. + pub(crate) const fn ring_context_serialized_size(domain_len: u32) -> usize { + // const G1_POINT_COMPRESSED_SIZE: usize = 48; + // const G2_POINT_COMPRESSED_SIZE: usize = 96; + const G1_POINT_UNCOMPRESSED_SIZE: usize = 96; + const G2_POINT_UNCOMPRESSED_SIZE: usize = 192; + const OVERHEAD_SIZE: usize = 20; + const G2_POINTS_NUM: usize = 2; + let g1_points_num = 3 * domain_len as usize + 1; - /// Overhead in the domain size over the max ring size. - /// - /// Some bits of the domain are reserved for the zk proof to work. - pub(crate) const RING_DOMAIN_OVERHEAD: u32 = 257; - - // Max size of serialized ring-vrf context params. - // - // The actual size is dependent on the ring domain size and this value - // has been computed for `RING_DOMAIN_MAX_SIZE` with compression disabled - // for performance reasons. - // - // 1024 uncompressed - // pub(crate) const RING_CONTEXT_SERIALIZED_MAX_SIZE: usize = 295412; - // 1024 compressed - // pub(crate) const RING_CONTEXT_SERIALIZED_MAX_SIZE: usize = 147716; - // 2048 uncompressed - pub(crate) const RING_CONTEXT_SERIALIZED_MAX_SIZE: usize = 590324; - // 2048 compressed - // pub(crate) const RING_CONTEXT_SERIALIZED_MAX_SIZE: usize = 295172; + OVERHEAD_SIZE + + g1_points_num * G1_POINT_UNCOMPRESSED_SIZE + + G2_POINTS_NUM * G2_POINT_UNCOMPRESSED_SIZE + } pub(crate) const RING_VERIFIER_DATA_SERIALIZED_SIZE: usize = 388; pub(crate) const RING_SIGNATURE_SERIALIZED_SIZE: usize = 755; @@ -705,15 +699,17 @@ pub mod ring_vrf { } /// Context used to construct ring prover and verifier. + /// + /// Generic parameter `D` represents the ring domain size and drives + /// the max number of supported ring members [`RingContext::max_keyset_size`] + /// which is equal to `D - RING_DOMAIN_OVERHEAD`. #[derive(Clone)] - pub struct RingContext(KZG); + pub struct RingContext<const D: u32>(KZG); - impl RingContext { + impl<const D: u32> RingContext<D> { /// Build an dummy instance for testing purposes. - /// - /// `domain_size` is set to `RING_DOMAIN_MAX_SIZE`. pub fn new_testing() -> Self { - Self(KZG::testing_kzg_setup([0; 32], RING_DOMAIN_MAX_SIZE)) + Self(KZG::testing_kzg_setup([0; 32], D)) } /// Get the keyset max size. @@ -761,38 +757,45 @@ pub mod ring_vrf { } } - impl Encode for RingContext { + impl<const D: u32> Encode for RingContext<D> { fn encode(&self) -> Vec<u8> { - let mut buf = Box::new([0; RING_CONTEXT_SERIALIZED_MAX_SIZE]); + let mut buf = vec![0; ring_context_serialized_size(D)]; self.0 .serialize_uncompressed(buf.as_mut_slice()) .expect("serialization length is constant and checked by test; qed"); - buf.encode() + buf } } - impl Decode for RingContext { - fn decode<R: codec::Input>(i: &mut R) -> Result<Self, codec::Error> { - let buf = <Box<[u8; RING_CONTEXT_SERIALIZED_MAX_SIZE]>>::decode(i)?; + impl<const D: u32> Decode for RingContext<D> { + fn decode<R: codec::Input>(input: &mut R) -> Result<Self, codec::Error> { + let mut buf = vec![0; ring_context_serialized_size(D)]; + input.read(&mut buf[..])?; let kzg = KZG::deserialize_uncompressed_unchecked(buf.as_slice()) .map_err(|_| "KZG decode error")?; Ok(RingContext(kzg)) } } - impl EncodeLike for RingContext {} + impl<const D: u32> EncodeLike for RingContext<D> {} - impl MaxEncodedLen for RingContext { + impl<const D: u32> MaxEncodedLen for RingContext<D> { fn max_encoded_len() -> usize { - <[u8; RING_CONTEXT_SERIALIZED_MAX_SIZE]>::max_encoded_len() + ring_context_serialized_size(D) } } - impl TypeInfo for RingContext { - type Identity = [u8; RING_CONTEXT_SERIALIZED_MAX_SIZE]; + impl<const D: u32> TypeInfo for RingContext<D> { + type Identity = Self; fn type_info() -> scale_info::Type { - Self::Identity::type_info() + let path = scale_info::Path::new("RingContext", module_path!()); + let array_type_def = scale_info::TypeDefArray { + len: ring_context_serialized_size(D) as u32, + type_param: scale_info::MetaType::new::<u8>(), + }; + let type_def = scale_info::TypeDef::Array(array_type_def); + scale_info::Type { path, type_params: Vec::new(), type_def, docs: Vec::new() } } } @@ -903,7 +906,11 @@ pub mod ring_vrf { mod tests { use super::{ring_vrf::*, vrf::*, *}; use crate::crypto::{VrfPublic, VrfSecret, DEV_PHRASE}; + const DEV_SEED: &[u8; SEED_SERIALIZED_SIZE] = &[0xcb; SEED_SERIALIZED_SIZE]; + const TEST_DOMAIN_SIZE: u32 = 1024; + + type TestRingContext = RingContext<TEST_DOMAIN_SIZE>; #[allow(unused)] fn b2h(bytes: &[u8]) -> String { @@ -916,10 +923,10 @@ mod tests { #[test] fn backend_assumptions_sanity_check() { - let kzg = KZG::testing_kzg_setup([0; 32], RING_DOMAIN_MAX_SIZE); - assert_eq!(kzg.max_keyset_size() as u32, RING_MAX_SIZE); + let kzg = KZG::testing_kzg_setup([0; 32], TEST_DOMAIN_SIZE); + assert_eq!(kzg.max_keyset_size() as u32, TEST_DOMAIN_SIZE - RING_DOMAIN_OVERHEAD); - assert_eq!(kzg.uncompressed_size(), RING_CONTEXT_SERIALIZED_MAX_SIZE); + assert_eq!(kzg.uncompressed_size(), ring_context_serialized_size(TEST_DOMAIN_SIZE)); let pks: Vec<_> = (0..16) .map(|i| SecretKey::from_seed(&[i as u8; 32]).to_public().0.into()) @@ -1071,7 +1078,7 @@ mod tests { #[test] fn ring_vrf_sign_verify() { - let ring_ctx = RingContext::new_testing(); + let ring_ctx = TestRingContext::new_testing(); let mut pks: Vec<_> = (0..16).map(|i| Pair::from_seed(&[i as u8; 32]).public()).collect(); assert!(pks.len() <= ring_ctx.max_keyset_size()); @@ -1097,7 +1104,7 @@ mod tests { #[test] fn ring_vrf_sign_verify_with_out_of_ring_key() { - let ring_ctx = RingContext::new_testing(); + let ring_ctx = TestRingContext::new_testing(); let pks: Vec<_> = (0..16).map(|i| Pair::from_seed(&[i as u8; 32]).public()).collect(); let pair = Pair::from_seed(DEV_SEED); @@ -1116,7 +1123,7 @@ mod tests { #[test] fn ring_vrf_make_bytes_matches() { - let ring_ctx = RingContext::new_testing(); + let ring_ctx = TestRingContext::new_testing(); let mut pks: Vec<_> = (0..16).map(|i| Pair::from_seed(&[i as u8; 32]).public()).collect(); assert!(pks.len() <= ring_ctx.max_keyset_size()); @@ -1145,7 +1152,7 @@ mod tests { #[test] fn encode_decode_ring_vrf_signature() { - let ring_ctx = RingContext::new_testing(); + let ring_ctx = TestRingContext::new_testing(); let mut pks: Vec<_> = (0..16).map(|i| Pair::from_seed(&[i as u8; 32]).public()).collect(); assert!(pks.len() <= ring_ctx.max_keyset_size()); @@ -1177,13 +1184,15 @@ mod tests { #[test] fn encode_decode_ring_vrf_context() { - let ctx1 = RingContext::new_testing(); + let ctx1 = TestRingContext::new_testing(); let enc1 = ctx1.encode(); - assert_eq!(enc1.len(), RING_CONTEXT_SERIALIZED_MAX_SIZE); - assert_eq!(RingContext::max_encoded_len(), RING_CONTEXT_SERIALIZED_MAX_SIZE); + let _ti = <TestRingContext as TypeInfo>::type_info(); + + assert_eq!(enc1.len(), ring_context_serialized_size(TEST_DOMAIN_SIZE)); + assert_eq!(enc1.len(), TestRingContext::max_encoded_len()); - let ctx2 = RingContext::decode(&mut enc1.as_slice()).unwrap(); + let ctx2 = TestRingContext::decode(&mut enc1.as_slice()).unwrap(); let enc2 = ctx2.encode(); assert_eq!(enc1, enc2); @@ -1191,7 +1200,7 @@ mod tests { #[test] fn encode_decode_verifier_data() { - let ring_ctx = RingContext::new_testing(); + let ring_ctx = TestRingContext::new_testing(); let pks: Vec<_> = (0..16).map(|i| Pair::from_seed(&[i as u8; 32]).public()).collect(); assert!(pks.len() <= ring_ctx.max_keyset_size()); diff --git a/substrate/primitives/keystore/src/testing.rs b/substrate/primitives/keystore/src/testing.rs index 08110e8e497919db42d52d3d6e75194e0d4e3130..9b59b6d09ea08cdae24eb90ee06d8d2b34aef20c 100644 --- a/substrate/primitives/keystore/src/testing.rs +++ b/substrate/primitives/keystore/src/testing.rs @@ -525,7 +525,7 @@ mod tests { let store = MemoryKeystore::new(); - let ring_ctx = bandersnatch::ring_vrf::RingContext::new_testing(); + let ring_ctx = bandersnatch::ring_vrf::RingContext::<1024>::new_testing(); let mut pks: Vec<_> = (0..16) .map(|i| bandersnatch::Pair::from_seed(&[i as u8; 32]).public())