Bandersnatch: ring-context generic over domain size (#2581)

Serialized length is now statically computed depending on the domain
size.

Opens the primitive to more generic usages not related to Sassafras
expectations

Address one point of
https://github.com/paritytech/polkadot-sdk/issues/2364
This commit is contained in:
Davide Galassi
2023-12-01 22:50:34 +01:00
committed by GitHub
parent 5789d6a7b4
commit ecfdb2b74f
3 changed files with 70 additions and 55 deletions
+62 -53
View File
@@ -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;
/// Ring domain max size.
pub const RING_DOMAIN_MAX_SIZE: u32 = 2048;
/// Overhead in the domain size over the max ring size.
/// 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(crate) const RING_DOMAIN_OVERHEAD: u32 = 257;
/// Some bits of the domain are reserved for the zk-proof to work.
pub 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;
// 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_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();
let ctx2 = RingContext::decode(&mut enc1.as_slice()).unwrap();
assert_eq!(enc1.len(), ring_context_serialized_size(TEST_DOMAIN_SIZE));
assert_eq!(enc1.len(), TestRingContext::max_encoded_len());
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());