diff --git a/consensus/fuzz/fuzz_targets/simplex_bls12381_multisig_minpk.rs b/consensus/fuzz/fuzz_targets/simplex_bls12381_multisig_minpk.rs index 34096210fb..c062355376 100644 --- a/consensus/fuzz/fuzz_targets/simplex_bls12381_multisig_minpk.rs +++ b/consensus/fuzz/fuzz_targets/simplex_bls12381_multisig_minpk.rs @@ -15,8 +15,12 @@ impl Simplex for SimplexBls12381MultisigMinPk { type Scheme = bls12381_multisig::Scheme; type Elector = RoundRobin; - fn fixture(context: &mut deterministic::Context, n: u32) -> Fixture { - bls12381_multisig::fixture::(context, n) + fn fixture( + context: &mut deterministic::Context, + namespace: &[u8], + n: u32, + ) -> Fixture { + bls12381_multisig::fixture::(context, namespace, n) } } diff --git a/consensus/fuzz/fuzz_targets/simplex_bls12381_multisig_minsig.rs b/consensus/fuzz/fuzz_targets/simplex_bls12381_multisig_minsig.rs index 50a96f8526..ce224984b5 100644 --- a/consensus/fuzz/fuzz_targets/simplex_bls12381_multisig_minsig.rs +++ b/consensus/fuzz/fuzz_targets/simplex_bls12381_multisig_minsig.rs @@ -15,8 +15,12 @@ impl Simplex for SimplexBls12381MultisigMinSig { type Scheme = bls12381_multisig::Scheme; type Elector = RoundRobin; - fn fixture(context: &mut deterministic::Context, n: u32) -> Fixture { - bls12381_multisig::fixture::(context, n) + fn fixture( + context: &mut deterministic::Context, + namespace: &[u8], + n: u32, + ) -> Fixture { + bls12381_multisig::fixture::(context, namespace, n) } } diff --git a/consensus/fuzz/fuzz_targets/simplex_bls12381_threshold_minpk.rs b/consensus/fuzz/fuzz_targets/simplex_bls12381_threshold_minpk.rs index c0862e803e..f723b843b2 100644 --- a/consensus/fuzz/fuzz_targets/simplex_bls12381_threshold_minpk.rs +++ b/consensus/fuzz/fuzz_targets/simplex_bls12381_threshold_minpk.rs @@ -15,8 +15,12 @@ impl Simplex for SimplexBls12381MinPk { type Scheme = bls12381_threshold::Scheme; type Elector = Random; - fn fixture(context: &mut deterministic::Context, n: u32) -> Fixture { - bls12381_threshold::fixture::(context, n) + fn fixture( + context: &mut deterministic::Context, + namespace: &[u8], + n: u32, + ) -> Fixture { + bls12381_threshold::fixture::(context, namespace, n) } } diff --git a/consensus/fuzz/fuzz_targets/simplex_bls12381_threshold_minsig.rs b/consensus/fuzz/fuzz_targets/simplex_bls12381_threshold_minsig.rs index 813837434a..81f066ee40 100644 --- a/consensus/fuzz/fuzz_targets/simplex_bls12381_threshold_minsig.rs +++ b/consensus/fuzz/fuzz_targets/simplex_bls12381_threshold_minsig.rs @@ -15,8 +15,12 @@ impl Simplex for SimplexBls12381MinSig { type Scheme = bls12381_threshold::Scheme; type Elector = Random; - fn fixture(context: &mut deterministic::Context, n: u32) -> Fixture { - bls12381_threshold::fixture::(context, n) + fn fixture( + context: &mut deterministic::Context, + namespace: &[u8], + n: u32, + ) -> Fixture { + bls12381_threshold::fixture::(context, namespace, n) } } diff --git a/consensus/fuzz/fuzz_targets/simplex_ed25519.rs b/consensus/fuzz/fuzz_targets/simplex_ed25519.rs index 59257daa36..ffc1a50f0d 100644 --- a/consensus/fuzz/fuzz_targets/simplex_ed25519.rs +++ b/consensus/fuzz/fuzz_targets/simplex_ed25519.rs @@ -12,8 +12,12 @@ impl Simplex for SimplexEd25519 { type Scheme = ed25519::Scheme; type Elector = RoundRobin; - fn fixture(context: &mut deterministic::Context, n: u32) -> Fixture { - ed25519::fixture(context, n) + fn fixture( + context: &mut deterministic::Context, + namespace: &[u8], + n: u32, + ) -> Fixture { + ed25519::fixture(context, namespace, n) } } diff --git a/consensus/fuzz/fuzz_targets/simplex_secp256r1.rs b/consensus/fuzz/fuzz_targets/simplex_secp256r1.rs index dc343af3cb..f633499eb7 100644 --- a/consensus/fuzz/fuzz_targets/simplex_secp256r1.rs +++ b/consensus/fuzz/fuzz_targets/simplex_secp256r1.rs @@ -14,8 +14,12 @@ impl Simplex for SimplexSecp256r1 { type Scheme = secp256r1::Scheme; type Elector = RoundRobin; - fn fixture(context: &mut deterministic::Context, n: u32) -> Fixture { - secp256r1::fixture(context, n) + fn fixture( + context: &mut deterministic::Context, + namespace: &[u8], + n: u32, + ) -> Fixture { + secp256r1::fixture(context, namespace, n) } } diff --git a/consensus/fuzz/src/disrupter.rs b/consensus/fuzz/src/disrupter.rs index 6fc1bd65bb..21e9d3ba04 100644 --- a/consensus/fuzz/src/disrupter.rs +++ b/consensus/fuzz/src/disrupter.rs @@ -35,7 +35,6 @@ pub struct Disrupter validator: PublicKey, scheme: S, participants: Set, - namespace: Vec, fuzz_input: FuzzInput, last_vote: u64, last_finalized: u64, @@ -52,7 +51,6 @@ where validator: PublicKey, scheme: S, participants: Set, - namespace: Vec, fuzz_input: FuzzInput, ) -> Self { Self { @@ -64,7 +62,6 @@ where validator, scheme, participants, - namespace, fuzz_input, } } @@ -222,7 +219,7 @@ where let _ = sender.send(Recipients::All, mutated.into(), true).await; } else { let proposal = self.mutate_proposal(¬arize.proposal); - if let Some(v) = Notarize::sign(&self.scheme, &self.namespace, proposal) { + if let Some(v) = Notarize::sign(&self.scheme, proposal) { let msg = Vote::::Notarize(v).encode().into(); let _ = sender.send(Recipients::All, msg, true).await; } @@ -234,7 +231,7 @@ where let _ = sender.send(Recipients::All, mutated.into(), true).await; } else { let proposal = self.mutate_proposal(&finalize.proposal); - if let Some(v) = Finalize::sign(&self.scheme, &self.namespace, proposal) { + if let Some(v) = Finalize::sign(&self.scheme, proposal) { let msg = Vote::::Finalize(v).encode().into(); let _ = sender.send(Recipients::All, msg, true).await; } @@ -247,9 +244,7 @@ where } else { let v = self.random_view(self.last_vote); let round = Round::new(Epoch::new(EPOCH), View::new(v)); - if let Some(v) = - Nullify::::sign::(&self.scheme, &self.namespace, round) - { + if let Some(v) = Nullify::::sign::(&self.scheme, round) { let msg = Vote::::Nullify(v).encode().into(); let _ = sender.send(Recipients::All, msg, true).await; } @@ -338,13 +333,13 @@ where match self.message() { Message::Notarize => { - if let Some(vote) = Notarize::sign(&self.scheme, &self.namespace, proposal) { + if let Some(vote) = Notarize::sign(&self.scheme, proposal) { let msg = Vote::::Notarize(vote).encode().into(); let _ = sender.send(Recipients::All, msg, true).await; } } Message::Finalize => { - if let Some(vote) = Finalize::sign(&self.scheme, &self.namespace, proposal) { + if let Some(vote) = Finalize::sign(&self.scheme, proposal) { let msg = Vote::::Finalize(vote).encode().into(); let _ = sender.send(Recipients::All, msg, true).await; } @@ -354,9 +349,7 @@ where Epoch::new(EPOCH), View::new(self.random_view(self.last_vote)), ); - if let Some(vote) = - Nullify::::sign::(&self.scheme, &self.namespace, round) - { + if let Some(vote) = Nullify::::sign::(&self.scheme, round) { let msg = Vote::::Nullify(vote).encode().into(); let _ = sender.send(Recipients::All, msg, true).await; } diff --git a/consensus/fuzz/src/lib.rs b/consensus/fuzz/src/lib.rs index f3203965a5..988491fa6a 100644 --- a/consensus/fuzz/src/lib.rs +++ b/consensus/fuzz/src/lib.rs @@ -55,7 +55,11 @@ where { type Scheme: Scheme; type Elector: Elector; - fn fixture(context: &mut deterministic::Context, n: u32) -> Fixture; + fn fixture( + context: &mut deterministic::Context, + namespace: &[u8], + n: u32, + ) -> Fixture; } #[derive(Debug, Clone)] @@ -143,7 +147,6 @@ impl Arbitrary<'_> for FuzzInput { fn run(input: FuzzInput) { let (n, _, f) = input.configuration; let containers = input.containers; - let namespace = NAMESPACE.to_vec(); let cfg = deterministic::Config::new().with_seed(input.seed); let executor = deterministic::Runner::new(cfg); @@ -163,7 +166,7 @@ fn run(input: FuzzInput) { schemes, verifier: _, .. - } = P::fixture(&mut context, n); + } = P::fixture(&mut context, NAMESPACE, n); let mut registrations = register(&mut oracle, &participants).await; @@ -197,7 +200,6 @@ fn run(input: FuzzInput) { .clone() .try_into() .expect("public keys are unique"), - namespace.clone(), input.clone(), ); disrupter.start(vote_network, certificate_network); @@ -208,7 +210,6 @@ fn run(input: FuzzInput) { let context = context.with_label(&format!("validator_{validator}")); let elector = P::Elector::default(); let reporter_cfg = reporter::Config { - namespace: namespace.clone(), participants: participants .clone() .try_into() @@ -245,7 +246,6 @@ fn run(input: FuzzInput) { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(EPOCH), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), diff --git a/consensus/src/aggregation/config.rs b/consensus/src/aggregation/config.rs index 4545892109..f4bd2563f5 100644 --- a/consensus/src/aggregation/config.rs +++ b/consensus/src/aggregation/config.rs @@ -39,10 +39,6 @@ pub struct Config< /// Blocking is handled by [commonware_p2p]. pub blocker: B, - /// The application namespace used to sign over different types of messages. - /// Used to prevent replay attacks on other applications. - pub namespace: Vec, - /// Whether acks are sent as priority. pub priority_acks: bool, diff --git a/consensus/src/aggregation/engine.rs b/consensus/src/aggregation/engine.rs index 4b7165334f..5b08f0b0e8 100644 --- a/consensus/src/aggregation/engine.rs +++ b/consensus/src/aggregation/engine.rs @@ -86,10 +86,6 @@ pub struct Engine< reporter: Z, blocker: B, - // ---------- Namespace Constants ---------- - /// The namespace signatures. - namespace: Vec, - // Pruning /// A tuple representing the epochs to keep in memory. /// The first element is the number of old epochs to keep. @@ -176,7 +172,6 @@ impl< monitor: cfg.monitor, provider: cfg.provider, blocker: cfg.blocker, - namespace: cfg.namespace, epoch_bounds: cfg.epoch_bounds, window: cfg.window.into(), activity_timeout: cfg.activity_timeout, @@ -666,7 +661,7 @@ impl< } // Validate signature - if !ack.verify(&mut self.context, &*scheme, &self.namespace) { + if !ack.verify(&mut self.context, &*scheme) { return Err(Error::InvalidAckSignature); } @@ -703,8 +698,7 @@ impl< // Sign the item let item = Item { index, digest }; - let ack = Ack::sign(&*scheme, &self.namespace, self.epoch, item) - .ok_or(Error::NotSigner(self.epoch))?; + let ack = Ack::sign(&*scheme, self.epoch, item).ok_or(Error::NotSigner(self.epoch))?; // Journal the ack self.record(Activity::Ack(ack.clone())).await; diff --git a/consensus/src/aggregation/mocks/reporter.rs b/consensus/src/aggregation/mocks/reporter.rs index 3560ac60e8..7e05e87fbb 100644 --- a/consensus/src/aggregation/mocks/reporter.rs +++ b/consensus/src/aggregation/mocks/reporter.rs @@ -30,9 +30,6 @@ pub struct Reporter { // RNG used for signature verification with scheme. rng: R, - // Application namespace - namespace: Vec, - // Signing scheme for verification scheme: S, @@ -58,13 +55,12 @@ where S: scheme::Scheme, D: Digest, { - pub fn new(rng: R, namespace: &[u8], scheme: S) -> (Self, Mailbox) { + pub fn new(rng: R, scheme: S) -> (Self, Mailbox) { let (sender, receiver) = mpsc::channel(1024); ( Self { mailbox: receiver, rng, - namespace: namespace.to_vec(), scheme, acks: HashSet::new(), digests: BTreeMap::new(), @@ -81,7 +77,7 @@ where match msg { Message::Ack(ack) => { // Verify properly constructed (not needed in production) - assert!(ack.verify(&mut self.rng, &self.scheme, &self.namespace)); + assert!(ack.verify(&mut self.rng, &self.scheme)); // Test encoding/decoding let encoded = ack.encode(); @@ -95,7 +91,7 @@ where } Message::Certified(certificate) => { // Verify certificate - assert!(certificate.verify(&mut self.rng, &self.scheme, &self.namespace)); + assert!(certificate.verify(&mut self.rng, &self.scheme)); // Test encoding/decoding let encoded = certificate.encode(); diff --git a/consensus/src/aggregation/mod.rs b/consensus/src/aggregation/mod.rs index 46062b292f..be6dbc11c6 100644 --- a/consensus/src/aggregation/mod.rs +++ b/consensus/src/aggregation/mod.rs @@ -118,6 +118,7 @@ mod tests { const PAGE_SIZE: NonZeroUsize = NZUsize!(1024); const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(10); const TEST_QUOTA: Quota = Quota::per_second(NonZeroU32::MAX); + const TEST_NAMESPACE: &[u8] = b"my testing namespace"; /// Reliable network link configuration for testing. const RELIABLE_LINK: Link = Link { @@ -188,13 +189,11 @@ mod tests { } /// Spawn aggregation engines for all validators. - #[allow(clippy::too_many_arguments)] fn spawn_validator_engines>( context: Context, fixture: &Fixture, registrations: &mut Registrations, oracle: &mut Oracle, - namespace: &[u8], epoch: Epoch, rebroadcast_timeout: Duration, incorrect: Vec, @@ -221,7 +220,7 @@ mod tests { // Create reporter with verifier scheme let (reporter, reporter_mailbox) = - mocks::Reporter::new(context.clone(), namespace, fixture.verifier.clone()); + mocks::Reporter::new(context.clone(), fixture.verifier.clone()); context.with_label("reporter").spawn(|_| reporter.run()); reporters.insert(participant.clone(), reporter_mailbox.clone()); @@ -237,7 +236,6 @@ mod tests { automaton, reporter: reporter_mailbox, blocker, - namespace: namespace.to_vec(), priority_acks: false, rebroadcast_timeout: NonZeroDuration::new_panic(rebroadcast_timeout), epoch_bounds: (EpochDelta::new(1), EpochDelta::new(1)), @@ -314,14 +312,13 @@ mod tests { fn all_online(fixture: F) where S: Scheme, - F: FnOnce(&mut deterministic::Context, u32) -> Fixture, + F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture, { let runner = deterministic::Runner::timed(Duration::from_secs(30)); runner.start(|mut context| async move { let num_validators = 4; - let fixture = fixture(&mut context, num_validators); - let namespace = b"my testing namespace"; + let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators); let epoch = Epoch::new(111); let (mut oracle, mut registrations) = @@ -333,7 +330,6 @@ mod tests { &fixture, &mut registrations, &mut oracle, - namespace, epoch, Duration::from_secs(5), vec![], @@ -357,14 +353,13 @@ mod tests { fn byzantine_proposer(fixture: F) where S: Scheme, - F: FnOnce(&mut deterministic::Context, u32) -> Fixture, + F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture, { let runner = deterministic::Runner::timed(Duration::from_secs(30)); runner.start(|mut context| async move { let num_validators = 4; - let fixture = fixture(&mut context, num_validators); - let namespace = b"my testing namespace"; + let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators); let epoch = Epoch::new(111); let (mut oracle, mut registrations) = @@ -376,7 +371,6 @@ mod tests { &fixture, &mut registrations, &mut oracle, - namespace, epoch, Duration::from_secs(5), vec![0], @@ -399,7 +393,7 @@ mod tests { fn unclean_byzantine_shutdown(fixture: F) where S: Scheme, - F: Fn(&mut StdRng, u32) -> Fixture, + F: Fn(&mut StdRng, &[u8], u32) -> Fixture, { // Test parameters let num_validators = 4; @@ -414,7 +408,7 @@ mod tests { // Generate fixture once (persists across restarts) let mut rng = test_rng(); - let fixture = fixture(&mut rng, num_validators); + let fixture = fixture(&mut rng, TEST_NAMESPACE, num_validators); // Continue until shared reporter reaches target or max shutdowns exceeded let mut shutdown_count = 0; @@ -422,7 +416,6 @@ mod tests { let fixture = fixture.clone(); let f = move |mut context: Context| { async move { - let namespace = b"my testing namespace"; let epoch = Epoch::new(111); let (oracle, mut registrations) = initialize_simulation( @@ -436,7 +429,7 @@ mod tests { // // We rely on replay to populate this reporter with a contiguous history of certificates. let (reporter, mut reporter_mailbox) = - mocks::Reporter::new(context.clone(), namespace, fixture.verifier.clone()); + mocks::Reporter::new(context.clone(), fixture.verifier.clone()); context.with_label("reporter").spawn(|_| reporter.run()); // Spawn validator engines @@ -471,7 +464,6 @@ mod tests { automaton, reporter: reporter_mailbox.clone(), blocker, - namespace: namespace.to_vec(), priority_acks: false, rebroadcast_timeout, epoch_bounds: (EpochDelta::new(1), EpochDelta::new(1)), @@ -558,18 +550,17 @@ mod tests { fn unclean_shutdown_with_unsigned_index(fixture: F) where S: Scheme, - F: Fn(&mut StdRng, u32) -> Fixture, + F: Fn(&mut StdRng, &[u8], u32) -> Fixture, { // Test parameters let num_validators = 4; let skip_index = 50; // Index where no one will sign let window = 10; let target_index = 100; - let namespace = b"my testing namespace"; // Generate fixture once (persists across restarts) let mut rng = test_rng(); - let fixture = fixture(&mut rng, num_validators); + let fixture = fixture(&mut rng, TEST_NAMESPACE, num_validators); // First run: let validators skip signing at skip_index and reach beyond it let f = |context: Context| { @@ -587,7 +578,7 @@ mod tests { // Create a shared reporter let (reporter, mut reporter_mailbox) = - mocks::Reporter::new(context.clone(), namespace, fixture.verifier.clone()); + mocks::Reporter::new(context.clone(), fixture.verifier.clone()); context.with_label("reporter").spawn(|_| reporter.run()); // Start validator engines with Skip strategy for skip_index @@ -618,7 +609,6 @@ mod tests { automaton, reporter: reporter_mailbox.clone(), blocker, - namespace: namespace.to_vec(), priority_acks: false, rebroadcast_timeout: NonZeroDuration::new_panic(Duration::from_millis( 100, @@ -671,7 +661,7 @@ mod tests { // Create a shared reporter let (reporter, mut reporter_mailbox) = - mocks::Reporter::new(context.clone(), namespace, fixture.verifier.clone()); + mocks::Reporter::new(context.clone(), fixture.verifier.clone()); context.with_label("reporter").spawn(|_| reporter.run()); // Start validator engines with Correct strategy (will sign everything now) @@ -701,7 +691,6 @@ mod tests { automaton, reporter: reporter_mailbox.clone(), blocker, - namespace: namespace.to_vec(), priority_acks: false, rebroadcast_timeout: NonZeroDuration::new_panic(Duration::from_millis( 100, @@ -754,7 +743,7 @@ mod tests { fn slow_and_lossy_links(fixture: F, seed: u64) -> String where S: Scheme, - F: FnOnce(&mut deterministic::Context, u32) -> Fixture, + F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture, { let cfg = deterministic::Config::new() .with_seed(seed) @@ -763,8 +752,7 @@ mod tests { runner.start(|mut context| async move { let num_validators = 4; - let fixture = fixture(&mut context, num_validators); - let namespace = b"my testing namespace"; + let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators); let epoch = Epoch::new(111); // Use degraded network links with realistic conditions @@ -783,7 +771,6 @@ mod tests { &fixture, &mut registrations, &mut oracle, - namespace, epoch, Duration::from_secs(2), vec![], @@ -868,14 +855,13 @@ mod tests { fn one_offline(fixture: F) where S: Scheme, - F: FnOnce(&mut deterministic::Context, u32) -> Fixture, + F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture, { let runner = deterministic::Runner::timed(Duration::from_secs(30)); runner.start(|mut context| async move { let num_validators = 5; - let mut fixture = fixture(&mut context, num_validators); - let namespace = b"my testing namespace"; + let mut fixture = fixture(&mut context, TEST_NAMESPACE, num_validators); let epoch = Epoch::new(111); // Truncate to only 4 validators (one offline) @@ -891,7 +877,6 @@ mod tests { &fixture, &mut registrations, &mut oracle, - namespace, epoch, Duration::from_secs(5), vec![], @@ -915,14 +900,13 @@ mod tests { fn network_partition(fixture: F) where S: Scheme, - F: FnOnce(&mut deterministic::Context, u32) -> Fixture, + F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture, { let runner = deterministic::Runner::timed(Duration::from_secs(60)); runner.start(|mut context| async move { let num_validators = 4; - let fixture = fixture(&mut context, num_validators); - let namespace = b"my testing namespace"; + let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators); let epoch = Epoch::new(111); let (mut oracle, mut registrations) = @@ -934,7 +918,6 @@ mod tests { &fixture, &mut registrations, &mut oracle, - namespace, epoch, Duration::from_secs(5), vec![], @@ -982,14 +965,13 @@ mod tests { fn insufficient_validators(fixture: F) where S: Scheme, - F: FnOnce(&mut deterministic::Context, u32) -> Fixture, + F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture, { let runner = deterministic::Runner::timed(Duration::from_secs(15)); runner.start(|mut context| async move { let num_validators = 5; - let fixture = fixture(&mut context, num_validators); - let namespace = b"my testing namespace"; + let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators); let epoch = Epoch::new(111); // Set up simulated network @@ -1017,7 +999,7 @@ mod tests { // Create reporter with verifier scheme let (reporter, reporter_mailbox) = - mocks::Reporter::new(context.clone(), namespace, fixture.verifier.clone()); + mocks::Reporter::new(context.clone(), fixture.verifier.clone()); context.with_label("reporter").spawn(|_| reporter.run()); reporters.insert(participant.clone(), reporter_mailbox.clone()); @@ -1033,7 +1015,6 @@ mod tests { automaton, reporter: reporter_mailbox, blocker, - namespace: namespace.to_vec(), priority_acks: false, rebroadcast_timeout: NonZeroDuration::new_panic(Duration::from_secs(3)), epoch_bounds: (EpochDelta::new(1), EpochDelta::new(1)), diff --git a/consensus/src/aggregation/scheme.rs b/consensus/src/aggregation/scheme.rs index e923117a89..8a6415bad9 100644 --- a/consensus/src/aggregation/scheme.rs +++ b/consensus/src/aggregation/scheme.rs @@ -35,10 +35,10 @@ pub mod bls12381_multisig { //! This scheme is attributable: certificates are compact while still preserving //! per-validator attribution. - use super::Item; + use crate::aggregation::types::{Item, Namespace}; use commonware_cryptography::impl_certificate_bls12381_multisig; - impl_certificate_bls12381_multisig!(&'a Item); + impl_certificate_bls12381_multisig!(&'a Item, Namespace); } pub mod bls12381_threshold { @@ -48,10 +48,10 @@ pub mod bls12381_threshold { //! This scheme is non-attributable: partial signatures should not be exposed as //! third-party evidence. - use super::Item; + use crate::aggregation::types::{Item, Namespace}; use commonware_cryptography::impl_certificate_bls12381_threshold; - impl_certificate_bls12381_threshold!(&'a Item); + impl_certificate_bls12381_threshold!(&'a Item, Namespace); } pub mod ed25519 { @@ -61,10 +61,10 @@ pub mod ed25519 { //! This scheme is attributable: individual signatures can be safely exposed as //! evidence of liveness or faults. - use super::Item; + use crate::aggregation::types::{Item, Namespace}; use commonware_cryptography::impl_certificate_ed25519; - impl_certificate_ed25519!(&'a Item); + impl_certificate_ed25519!(&'a Item, Namespace); } pub mod secp256r1 { @@ -74,8 +74,8 @@ pub mod secp256r1 { //! This scheme is attributable: individual signatures can be safely exposed as //! evidence of liveness or faults. - use super::Item; + use crate::aggregation::types::{Item, Namespace}; use commonware_cryptography::impl_certificate_secp256r1; - impl_certificate_secp256r1!(&'a Item); + impl_certificate_secp256r1!(&'a Item, Namespace); } diff --git a/consensus/src/aggregation/types.rs b/consensus/src/aggregation/types.rs index 1fb9c12a2d..e580c2f48a 100644 --- a/consensus/src/aggregation/types.rs +++ b/consensus/src/aggregation/types.rs @@ -6,7 +6,7 @@ use commonware_codec::{ varint::UInt, Encode, EncodeSize, Error as CodecError, Read, ReadExt, Write, }; use commonware_cryptography::{ - certificate::{Attestation, Scheme, Subject}, + certificate::{self, Attestation, Scheme, Subject}, Digest, }; use commonware_utils::union; @@ -90,6 +90,19 @@ fn ack_namespace(namespace: &[u8]) -> Vec { union(namespace, ACK_SUFFIX) } +/// Namespace type for aggregation acknowledgments. +/// +/// This type encapsulates the pre-computed namespace bytes used for signing and +/// verifying acks. +#[derive(Clone, Debug)] +pub struct Namespace(Vec); + +impl certificate::Namespace for Namespace { + fn derive(namespace: &[u8]) -> Self { + Self(ack_namespace(namespace)) + } +} + /// Item represents a single element being aggregated in the protocol. /// Each item has a unique index and contains a digest that validators sign. #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -124,8 +137,14 @@ impl EncodeSize for Item { } impl Subject for &Item { - fn namespace_and_message(&self, namespace: &[u8]) -> (Bytes, Bytes) { - (ack_namespace(namespace).into(), self.encode().freeze()) + type Namespace = Namespace; + + fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] { + &derived.0 + } + + fn message(&self) -> Bytes { + self.encode().freeze() } } @@ -158,12 +177,12 @@ impl Ack { /// /// Returns `true` if the attestation is valid for the given namespace and public key. /// Domain separation is automatically applied to prevent signature reuse. - pub fn verify(&self, rng: &mut R, scheme: &S, namespace: &[u8]) -> bool + pub fn verify(&self, rng: &mut R, scheme: &S) -> bool where R: CryptoRngCore, S: scheme::Scheme, { - scheme.verify_attestation::<_, D>(rng, namespace, &self.item, &self.attestation) + scheme.verify_attestation::<_, D>(rng, &self.item, &self.attestation) } /// Creates a new acknowledgment by signing an item with a validator's key. @@ -173,11 +192,11 @@ impl Ack { /// # Determinism /// /// Signatures produced by this function are deterministic and safe for consensus. - pub fn sign(scheme: &S, namespace: &[u8], epoch: Epoch, item: Item) -> Option + pub fn sign(scheme: &S, epoch: Epoch, item: Item) -> Option where S: scheme::Scheme, { - let attestation = scheme.sign::(namespace, &item)?; + let attestation = scheme.sign::(&item)?; Some(Self { item, epoch, @@ -305,12 +324,12 @@ impl Certificate { } /// Verifies the recovered certificate for the item. - pub fn verify(&self, rng: &mut R, scheme: &S, namespace: &[u8]) -> bool + pub fn verify(&self, rng: &mut R, scheme: &S) -> bool where R: CryptoRngCore, S: scheme::Scheme, { - scheme.verify_certificate::<_, D>(rng, namespace, &self.item, &self.certificate) + scheme.verify_certificate::<_, D>(rng, &self.item, &self.certificate) } } @@ -458,10 +477,10 @@ mod tests { fn codec(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 4); + let fixture = fixture(&mut rng, NAMESPACE, 4); let schemes = &fixture.schemes; let item = Item { index: 100, @@ -473,7 +492,7 @@ mod tests { assert_eq!(item, restored_item); // Test Ack creation and codec - let ack = Ack::sign(&schemes[0], NAMESPACE, Epoch::new(1), item.clone()).unwrap(); + let ack = Ack::sign(&schemes[0], Epoch::new(1), item.clone()).unwrap(); let cfg = schemes[0].certificate_codec_config(); let encoded_ack = ack.encode(); let restored_ack: Ack = Ack::decode(encoded_ack).unwrap(); @@ -481,7 +500,7 @@ mod tests { // Verify the restored ack assert_eq!(restored_ack.item, item); assert_eq!(restored_ack.epoch, Epoch::new(1)); - assert!(restored_ack.verify(&mut rng, &schemes[0], NAMESPACE)); + assert!(restored_ack.verify(&mut rng, &schemes[0])); // Test TipAck codec let tip_ack = TipAck { @@ -511,11 +530,11 @@ mod tests { let acks: Vec<_> = schemes .iter() .take(schemes[0].participants().quorum() as usize) - .filter_map(|scheme| Ack::sign(scheme, NAMESPACE, Epoch::new(1), item.clone())) + .filter_map(|scheme| Ack::sign(scheme, Epoch::new(1), item.clone())) .collect(); let certificate = Certificate::from_acks(&schemes[0], &acks).unwrap(); - assert!(certificate.verify(&mut rng, &schemes[0], NAMESPACE)); + assert!(certificate.verify(&mut rng, &schemes[0])); let activity_certified = Activity::Certified(certificate.clone()); let encoded_certified = activity_certified.encode(); @@ -523,7 +542,7 @@ mod tests { Activity::decode_cfg(encoded_certified, &cfg).unwrap(); if let Activity::Certified(restored) = restored_activity_certified { assert_eq!(restored.item, item); - assert!(restored.verify(&mut rng, &schemes[0], NAMESPACE)); + assert!(restored.verify(&mut rng, &schemes[0])); } else { panic!("Expected Activity::Certified"); } @@ -553,9 +572,9 @@ mod tests { fn activity_invalid_enum(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { - let fixture = fixture(&mut test_rng(), 4); + let fixture = fixture(&mut test_rng(), NAMESPACE, 4); let mut buf = BytesMut::new(); 3u8.write(&mut buf); // Invalid discriminant diff --git a/consensus/src/marshal/actor.rs b/consensus/src/marshal/actor.rs index 6b0c0175ed..b24ae445f4 100644 --- a/consensus/src/marshal/actor.rs +++ b/consensus/src/marshal/actor.rs @@ -122,8 +122,6 @@ where provider: P, // Epoch configuration epocher: ES, - // Unique application namespace - namespace: Vec, // Minimum number of views to retain temporary data after the application processes a block view_retention_timeout: ViewDelta, // Maximum number of blocks to repair at once @@ -227,7 +225,6 @@ where mailbox, provider: config.provider, epocher: config.epocher, - namespace: config.namespace, view_retention_timeout: config.view_retention_timeout, max_repair: config.max_repair, block_codec_config: config.block_codec_config, @@ -643,7 +640,7 @@ where // Validation if block.height() != height || finalization.proposal.payload != block.commitment() - || !finalization.verify(&mut self.context, &scheme, &self.namespace) + || !finalization.verify(&mut self.context, &scheme) { let _ = response.send(false); continue; @@ -683,7 +680,7 @@ where // Validation if notarization.round() != round || notarization.proposal.payload != block.commitment() - || !notarization.verify(&mut self.context, &scheme, &self.namespace) + || !notarization.verify(&mut self.context, &scheme) { let _ = response.send(false); continue; diff --git a/consensus/src/marshal/config.rs b/consensus/src/marshal/config.rs index 66360dbf67..49f1380494 100644 --- a/consensus/src/marshal/config.rs +++ b/consensus/src/marshal/config.rs @@ -30,9 +30,6 @@ where /// Useful for keeping around information that peers may desire to have. pub view_retention_timeout: ViewDelta, - /// Namespace for proofs. - pub namespace: Vec, - /// Prunable archive partition prefix. pub prunable_items_per_section: NonZeroU64, diff --git a/consensus/src/marshal/mod.rs b/consensus/src/marshal/mod.rs index 9cb93e5f2e..3ae9268309 100644 --- a/consensus/src/marshal/mod.rs +++ b/consensus/src/marshal/mod.rs @@ -183,7 +183,6 @@ mod tests { provider, epocher: FixedEpocher::new(BLOCKS_PER_EPOCH), mailbox_size: 100, - namespace: NAMESPACE.to_vec(), view_retention_timeout: ViewDelta::new(10), max_repair: NZUsize!(10), block_codec_config: (), @@ -313,7 +312,7 @@ mod tests { let finalizes: Vec<_> = schemes .iter() .take(quorum as usize) - .map(|scheme| Finalize::sign(scheme, NAMESPACE, proposal.clone()).unwrap()) + .map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap()) .collect(); // Generate certificate signatures @@ -325,7 +324,7 @@ mod tests { let notarizes: Vec<_> = schemes .iter() .take(quorum as usize) - .map(|scheme| Notarize::sign(scheme, NAMESPACE, proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap()) .collect(); // Generate certificate signatures @@ -419,7 +418,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); // Initialize applications and actors let mut applications = BTreeMap::new(); @@ -564,7 +563,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); // Initialize applications and actors let mut applications = BTreeMap::new(); @@ -731,7 +730,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); let mut actors = Vec::new(); for (i, validator) in participants.iter().enumerate() { @@ -786,7 +785,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); let mut actors = Vec::new(); for (i, validator) in participants.iter().enumerate() { @@ -862,7 +861,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); let mut actors = Vec::new(); for (i, validator) in participants.iter().enumerate() { @@ -930,7 +929,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); let mut actors = Vec::new(); for (i, validator) in participants.iter().enumerate() { @@ -1041,7 +1040,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); // Single validator actor let me = participants[0].clone(); @@ -1101,7 +1100,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); // Single validator actor let me = participants[0].clone(); @@ -1183,7 +1182,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); let me = participants[0].clone(); let (application, mut actor, _processed_height) = setup_validator( @@ -1242,7 +1241,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); let me = participants[0].clone(); let (_application, mut actor, _processed_height) = setup_validator( @@ -1300,7 +1299,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); let me = participants[0].clone(); let (_application, mut actor, _processed_height) = setup_validator( @@ -1358,7 +1357,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); // Register the initial peer set let mut manager = oracle.manager(); @@ -1440,7 +1439,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); let me = participants[0].clone(); let (_application, mut actor, _processed_height) = setup_validator( @@ -1525,7 +1524,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); let me = participants[0].clone(); let (_base_app, marshal, _processed_height) = setup_validator( @@ -1657,7 +1656,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); // Set up two validators let mut actors = Vec::new(); @@ -1769,7 +1768,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); // Test 1: Fresh init should return processed height 0 let me = participants[0].clone(); @@ -1904,7 +1903,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); let me = participants[0].clone(); let (_base_app, marshal, _processed_height) = setup_validator( @@ -1969,7 +1968,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, NUM_VALIDATORS); + } = bls12381_threshold::fixture::(&mut context, NAMESPACE, NUM_VALIDATORS); // Set up one validator let (i, validator) = participants.iter().enumerate().next().unwrap(); diff --git a/consensus/src/ordered_broadcast/ack_manager.rs b/consensus/src/ordered_broadcast/ack_manager.rs index c3d3437d3b..16e7854900 100644 --- a/consensus/src/ordered_broadcast/ack_manager.rs +++ b/consensus/src/ordered_broadcast/ack_manager.rs @@ -164,6 +164,8 @@ mod tests { use helpers::Sha256Digest; use rand::{rngs::StdRng, SeedableRng as _}; + const NAMESPACE: &[u8] = b"1234"; + /// Aggregated helper functions to reduce duplication in tests. mod helpers { use super::*; @@ -172,8 +174,6 @@ mod tests { pub type Sha256Digest = ::Digest; - const NAMESPACE: &[u8] = b"1234"; - /// Create an Ack by signing with the provided scheme. pub fn create_ack( scheme: &S, @@ -188,7 +188,7 @@ mod tests { epoch, }; let attestation = scheme - .sign::(NAMESPACE, context) + .sign::(context) .expect("Failed to sign vote"); Ack { chunk, @@ -240,11 +240,11 @@ mod tests { fn chunk_different_payloads(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { // Use 8 validators so quorum is 6 let num_validators = 8; - let fixture = fixture(&mut test_rng(), num_validators); + let fixture = fixture(&mut test_rng(), NAMESPACE, num_validators); let mut acks = AckManager::::Digest>::new(); let sequencer = fixture.participants[1].clone(); let height = 10; @@ -290,10 +290,10 @@ mod tests { fn sequencer_different_heights(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let num_validators = 4; - let fixture = fixture(&mut test_rng(), num_validators); + let fixture = fixture(&mut test_rng(), NAMESPACE, num_validators); let mut acks = AckManager::::Digest>::new(); let sequencer = fixture.participants[1].clone(); let epoch = Epoch::new(10); @@ -337,10 +337,10 @@ mod tests { fn sequencer_contiguous_heights(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let num_validators = 4; - let fixture = fixture(&mut test_rng(), num_validators); + let fixture = fixture(&mut test_rng(), NAMESPACE, num_validators); let mut acks = AckManager::::Digest>::new(); let sequencer = fixture.participants[1].clone(); let epoch = Epoch::new(10); @@ -386,10 +386,10 @@ mod tests { fn chunk_different_epochs(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let num_validators = 4; - let fixture = fixture(&mut test_rng(), num_validators); + let fixture = fixture(&mut test_rng(), NAMESPACE, num_validators); let mut acks = AckManager::::Digest>::new(); let sequencer = fixture.participants[1].clone(); let height = 30; @@ -433,10 +433,10 @@ mod tests { fn add_certificate(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let num_validators = 4; - let fixture = fixture(&mut test_rng(), num_validators); + let fixture = fixture(&mut test_rng(), NAMESPACE, num_validators); let mut acks = AckManager::::Digest>::new(); let epoch = Epoch::new(99); let sequencer = fixture.participants[1].clone(); @@ -474,10 +474,10 @@ mod tests { fn duplicate_attestation_submission(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let num_validators = 4; - let fixture = fixture(&mut test_rng(), num_validators); + let fixture = fixture(&mut test_rng(), NAMESPACE, num_validators); let mut acks = AckManager::::Digest>::new(); let sequencer = fixture.participants[1].clone(); let epoch = Epoch::new(1); @@ -503,10 +503,10 @@ mod tests { fn subsequent_acks_after_certificate_reached(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let num_validators = 4; - let fixture = fixture(&mut test_rng(), num_validators); + let fixture = fixture(&mut test_rng(), NAMESPACE, num_validators); let mut acks = AckManager::::Digest>::new(); let sequencer = fixture.participants[1].clone(); let epoch = Epoch::new(1); @@ -541,10 +541,10 @@ mod tests { fn multiple_sequencers(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let num_validators = 4; - let fixture = fixture(&mut test_rng(), num_validators); + let fixture = fixture(&mut test_rng(), NAMESPACE, num_validators); let mut acks = AckManager::::Digest>::new(); let sequencer1 = fixture.participants[1].clone(); @@ -581,10 +581,10 @@ mod tests { fn incomplete_quorum(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let num_validators = 4; - let fixture = fixture(&mut test_rng(), num_validators); + let fixture = fixture(&mut test_rng(), NAMESPACE, num_validators); let mut acks = AckManager::::Digest>::new(); let sequencer = fixture.participants[1].clone(); let epoch = Epoch::new(1); @@ -612,13 +612,13 @@ mod tests { fn interleaved_payloads(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { // Use 20 validators so quorum is 14 // We'll have validators [0-13] vote for payload1 and [6-19] vote for payload2 // This gives us overlapping sets but each reaches quorum let num_validators = 20; - let fixture = fixture(&mut test_rng(), num_validators); + let fixture = fixture(&mut test_rng(), NAMESPACE, num_validators); let mut acks = AckManager::::Digest>::new(); let sequencer = fixture.participants[1].clone(); let epoch = Epoch::new(1); diff --git a/consensus/src/ordered_broadcast/config.rs b/consensus/src/ordered_broadcast/config.rs index 042e7244a5..b861cf647d 100644 --- a/consensus/src/ordered_broadcast/config.rs +++ b/consensus/src/ordered_broadcast/config.rs @@ -1,4 +1,4 @@ -use super::types::{Activity, Context, SequencersProvider}; +use super::types::{Activity, ChunkSigner, ChunkVerifier, Context, SequencersProvider}; use crate::{ types::{Epoch, EpochDelta}, Automaton, Monitor, Relay, Reporter, @@ -19,7 +19,15 @@ pub struct Config< M: Monitor, > { /// The signer used when this engine acts as a sequencer. - pub sequencer_signer: Option, + /// + /// Create with `ChunkSigner::new(namespace, signer)`. + pub sequencer_signer: Option>, + + /// Verifier for node signatures. + /// + /// Create with `ChunkVerifier::new(namespace)` using the same namespace + /// as the `ChunkSigner`. + pub chunk_verifier: ChunkVerifier, /// Provider for epoch-specific sequencers set. pub sequencers_provider: S, @@ -40,10 +48,6 @@ pub struct Config< /// be involved in the current broadcast attempt). pub monitor: M, - /// The application namespace used to sign over different types of messages. - /// Used to prevent replay attacks on other applications. - pub namespace: Vec, - /// Whether proposals are sent as priority. pub priority_proposals: bool, diff --git a/consensus/src/ordered_broadcast/engine.rs b/consensus/src/ordered_broadcast/engine.rs index 968cbb3491..3a326bd9ea 100644 --- a/consensus/src/ordered_broadcast/engine.rs +++ b/consensus/src/ordered_broadcast/engine.rs @@ -10,7 +10,8 @@ use super::{ metrics, scheme, types::{ - Ack, Activity, Chunk, Context, Error, Lock, Node, Parent, Proposal, SequencersProvider, + Ack, Activity, Chunk, ChunkSigner, ChunkVerifier, Context, Error, Lock, Node, Parent, + Proposal, SequencersProvider, }, AckManager, Config, TipManager, }; @@ -76,7 +77,7 @@ pub struct Engine< // Interfaces //////////////////////////////////////// context: ContextCell, - sequencer_signer: Option, + sequencer_signer: Option>, sequencers_provider: S, validators_provider: P, automaton: A, @@ -88,8 +89,8 @@ pub struct Engine< // Namespace Constants //////////////////////////////////////// - // The namespace signatures. - namespace: Vec, + // Verifier for chunk signatures. + chunk_verifier: ChunkVerifier, //////////////////////////////////////// // Timeouts @@ -223,7 +224,7 @@ impl< relay: cfg.relay, reporter: cfg.reporter, monitor: cfg.monitor, - namespace: cfg.namespace, + chunk_verifier: cfg.chunk_verifier, rebroadcast_timeout: cfg.rebroadcast_timeout, rebroadcast_deadline: None, epoch_bounds: cfg.epoch_bounds, @@ -533,12 +534,7 @@ impl< }; // Construct vote (if a validator) - let Some(ack) = Ack::sign( - &self.namespace, - scheme.as_ref(), - tip.chunk.clone(), - self.epoch, - ) else { + let Some(ack) = Ack::sign(scheme.as_ref(), tip.chunk.clone(), self.epoch) else { return Err(Error::NotSigner(self.epoch)); }; @@ -750,7 +746,7 @@ impl< } // Construct new node - let node = Node::sign(&self.namespace, signer, height, payload, parent); + let node = Node::sign(signer, height, payload, parent); // Deal with the chunk as if it were received over the network self.handle_node(&node).await; @@ -887,7 +883,7 @@ impl< // Verify the node node.verify( &mut self.context, - &self.namespace, + &self.chunk_verifier, &self.validators_provider, ) } @@ -946,7 +942,7 @@ impl< } // Validate the vote signature - if !ack.verify(&mut self.context, &self.namespace, scheme.as_ref()) { + if !ack.verify(&mut self.context, scheme.as_ref()) { return Err(Error::InvalidAckSignature); } diff --git a/consensus/src/ordered_broadcast/mocks/reporter.rs b/consensus/src/ordered_broadcast/mocks/reporter.rs index da8eddbb55..a2ff5ce30f 100644 --- a/consensus/src/ordered_broadcast/mocks/reporter.rs +++ b/consensus/src/ordered_broadcast/mocks/reporter.rs @@ -1,7 +1,7 @@ use crate::{ ordered_broadcast::{ scheme, - types::{Activity, Chunk, Lock, Proposal}, + types::{Activity, Chunk, ChunkVerifier, Lock, Proposal}, }, types::Epoch, }; @@ -29,8 +29,8 @@ pub struct Reporter { // RNG used for signature verification with scheme. rng: R, - // Application namespace - namespace: Vec, + // Verifier for node signatures. + chunk_verifier: ChunkVerifier, // Scheme for verification scheme: S, @@ -58,7 +58,7 @@ where { pub fn new( rng: R, - namespace: &[u8], + chunk_verifier: ChunkVerifier, scheme: S, limit_misses: Option, ) -> (Self, Mailbox) { @@ -67,7 +67,7 @@ where Self { rng, mailbox: receiver, - namespace: namespace.to_vec(), + chunk_verifier, scheme, proposals: HashSet::new(), limit_misses, @@ -88,7 +88,7 @@ where match msg { Message::Proposal(proposal) => { // Verify properly constructed (not needed in production) - if !proposal.verify(&self.namespace) { + if !proposal.verify(&self.chunk_verifier) { panic!("Invalid proof"); } @@ -101,7 +101,7 @@ where } Message::Locked(lock) => { // Verify properly constructed (not needed in production) - if !lock.verify(&mut self.rng, &self.namespace, &self.scheme) { + if !lock.verify(&mut self.rng, &self.scheme) { panic!("Invalid proof"); } diff --git a/consensus/src/ordered_broadcast/mod.rs b/consensus/src/ordered_broadcast/mod.rs index 21d385487d..7dbe461f0c 100644 --- a/consensus/src/ordered_broadcast/mod.rs +++ b/consensus/src/ordered_broadcast/mod.rs @@ -72,7 +72,11 @@ pub mod mocks; #[cfg(test)] mod tests { - use super::{mocks, Config, Engine}; + use super::{ + mocks, + types::{ChunkSigner, ChunkVerifier}, + Config, Engine, + }; use crate::{ ordered_broadcast::scheme::{ bls12381_multisig, bls12381_threshold, ed25519, secp256r1, Scheme, @@ -105,6 +109,7 @@ mod tests { const PAGE_SIZE: NonZeroUsize = NZUsize!(1024); const PAGE_CACHE_SIZE: NonZeroUsize = NZUsize!(10); const TEST_QUOTA: Quota = Quota::per_second(NonZeroU32::MAX); + const TEST_NAMESPACE: &[u8] = b"ordered_broadcast_test"; type Registrations

= BTreeMap< P, @@ -219,9 +224,10 @@ mod tests { assert!(validators_provider.register(epoch, fixture.schemes[idx].clone())); let automaton = mocks::Automaton::::new(invalid_when); + let chunk_verifier = ChunkVerifier::new(namespace); let (reporter, reporter_mailbox) = mocks::Reporter::new( context.clone(), - namespace, + chunk_verifier.clone(), fixture.verifier.clone(), misses_allowed, ); @@ -231,14 +237,17 @@ mod tests { let engine = Engine::new( context.with_label("engine"), Config { - sequencer_signer: Some(fixture.private_keys[idx].clone()), + sequencer_signer: Some(ChunkSigner::new( + namespace, + fixture.private_keys[idx].clone(), + )), + chunk_verifier, sequencers_provider: sequencers, validators_provider, automaton: automaton.clone(), relay: automaton.clone(), reporter: reporters.get(validator).unwrap().clone(), monitor, - namespace: namespace.to_vec(), priority_proposals: false, priority_acks: false, rebroadcast_timeout, @@ -335,14 +344,14 @@ mod tests { fn all_online(fixture: F) where S: Scheme, - F: FnOnce(&mut deterministic::Context, u32) -> Fixture, + F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture, { let runner = deterministic::Runner::timed(Duration::from_secs(120)); runner.start(|mut context| async move { let epoch = Epoch::new(111); let num_validators = 4; - let fixture = fixture(&mut context, num_validators); + let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators); let (_oracle, mut registrations) = initialize_simulation(context.with_label("simulation"), &fixture, RELIABLE_LINK) @@ -382,7 +391,7 @@ mod tests { fn unclean_shutdown(fixture: F) where S: Scheme, - F: Fn(&mut deterministic::Context, u32) -> Fixture + Clone, + F: Fn(&mut deterministic::Context, &[u8], u32) -> Fixture + Clone, { let mut prev_checkpoint = None; let epoch = Epoch::new(111); @@ -393,7 +402,7 @@ mod tests { loop { let fixture = fixture.clone(); let f = |mut context: deterministic::Context| async move { - let fixture = fixture(&mut context, num_validators); + let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators); let (network, mut oracle) = Network::new( context.with_label("network"), @@ -469,14 +478,14 @@ mod tests { fn network_partition(fixture: F) where S: Scheme, - F: FnOnce(&mut deterministic::Context, u32) -> Fixture, + F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture, { let runner = deterministic::Runner::timed(Duration::from_secs(60)); runner.start(|mut context| async move { let epoch = Epoch::new(111); let num_validators = 4; - let fixture = fixture(&mut context, num_validators); + let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators); // Configure the network let (mut oracle, mut registrations) = @@ -532,7 +541,7 @@ mod tests { fn slow_and_lossy_links(fixture: F, seed: u64) -> String where S: Scheme, - F: Fn(&mut deterministic::Context, u32) -> Fixture, + F: Fn(&mut deterministic::Context, &[u8], u32) -> Fixture, { let cfg = deterministic::Config::new() .with_seed(seed) @@ -542,7 +551,7 @@ mod tests { runner.start(|mut context| async move { let epoch = Epoch::new(111); let num_validators = 4; - let fixture = fixture(&mut context, num_validators); + let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators); let (mut oracle, mut registrations) = initialize_simulation(context.with_label("simulation"), &fixture, RELIABLE_LINK) @@ -656,14 +665,14 @@ mod tests { fn invalid_signature_injection(fixture: F) where S: Scheme, - F: FnOnce(&mut deterministic::Context, u32) -> Fixture, + F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture, { let runner = deterministic::Runner::timed(Duration::from_secs(30)); runner.start(|mut context| async move { let epoch = Epoch::new(111); let num_validators = 4; - let fixture = fixture(&mut context, num_validators); + let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators); let (_oracle, mut registrations) = initialize_simulation(context.with_label("simulation"), &fixture, RELIABLE_LINK) @@ -703,14 +712,14 @@ mod tests { fn updated_epoch(fixture: F) where S: Scheme, - F: FnOnce(&mut deterministic::Context, u32) -> Fixture, + F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture, { let runner = deterministic::Runner::timed(Duration::from_secs(60)); runner.start(|mut context| async move { let epoch = Epoch::new(111); let num_validators = 4; - let fixture = fixture(&mut context, num_validators); + let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators); // Setup network let (mut oracle, mut registrations) = @@ -736,9 +745,10 @@ mod tests { validators_providers.insert(validator.clone(), validators_provider.clone()); let automaton = mocks::Automaton::::new(|_| false); + let chunk_verifier = ChunkVerifier::new(namespace); let (reporter, reporter_mailbox) = mocks::Reporter::new( context.clone(), - namespace, + chunk_verifier.clone(), fixture.verifier.clone(), Some(5), ); @@ -748,14 +758,17 @@ mod tests { let engine = Engine::new( context.with_label("engine"), Config { - sequencer_signer: Some(fixture.private_keys[idx].clone()), + sequencer_signer: Some(ChunkSigner::new( + namespace, + fixture.private_keys[idx].clone(), + )), + chunk_verifier, sequencers_provider: sequencers, validators_provider, relay: automaton.clone(), automaton: automaton.clone(), reporter: reporters.get(validator).unwrap().clone(), monitor, - namespace: namespace.to_vec(), epoch_bounds: (EpochDelta::new(1), EpochDelta::new(1)), height_bound: 2, rebroadcast_timeout: Duration::from_secs(1), @@ -836,13 +849,13 @@ mod tests { fn external_sequencer(fixture: F) where S: Scheme, - F: FnOnce(&mut deterministic::Context, u32) -> Fixture, + F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture, { let runner = deterministic::Runner::timed(Duration::from_secs(60)); runner.start(|mut context| async move { let epoch = Epoch::new(111); let num_validators = 4; - let fixture = fixture(&mut context, num_validators); + let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators); // Generate sequencer (external, not a validator) let sequencer = PrivateKey::from_seed(u64::MAX); @@ -888,9 +901,10 @@ mod tests { let automaton = mocks::Automaton::::new(|_| false); + let chunk_verifier = ChunkVerifier::new(namespace); let (reporter, reporter_mailbox) = mocks::Reporter::new( context.clone(), - namespace, + chunk_verifier.clone(), fixture.verifier.clone(), Some(5), ); @@ -900,14 +914,14 @@ mod tests { let engine = Engine::new( context.with_label("engine"), Config { - sequencer_signer: None::, // Validators don't propose in this test + sequencer_signer: None::>, // Validators don't propose in this test + chunk_verifier, sequencers_provider: sequencers, validators_provider, relay: automaton.clone(), automaton: automaton.clone(), reporter: reporters.get(validator).unwrap().clone(), monitor, - namespace: namespace.to_vec(), epoch_bounds: (EpochDelta::new(1), EpochDelta::new(1)), height_bound: 2, rebroadcast_timeout: Duration::from_secs(5), @@ -930,9 +944,10 @@ mod tests { { let context = context.with_label("sequencer"); let automaton = mocks::Automaton::::new(|_| false); + let chunk_verifier = ChunkVerifier::new(namespace); let (reporter, reporter_mailbox) = mocks::Reporter::new( context.clone(), - namespace, + chunk_verifier.clone(), fixture.verifier.clone(), Some(5), ); @@ -947,7 +962,8 @@ mod tests { let engine = Engine::new( context.with_label("engine"), Config { - sequencer_signer: Some(sequencer.clone()), + sequencer_signer: Some(ChunkSigner::new(namespace, sequencer.clone())), + chunk_verifier, sequencers_provider: mocks::Sequencers::::new(vec![ sequencer.public_key() ]), @@ -956,7 +972,6 @@ mod tests { automaton, reporter: reporters.get(&sequencer.public_key()).unwrap().clone(), monitor: mocks::Monitor::new(epoch), - namespace: namespace.to_vec(), epoch_bounds: (EpochDelta::new(1), EpochDelta::new(1)), height_bound: 2, rebroadcast_timeout: Duration::from_secs(5), @@ -1002,7 +1017,7 @@ mod tests { fn run_1k(fixture: F) where S: Scheme, - F: FnOnce(&mut deterministic::Context, u32) -> Fixture, + F: FnOnce(&mut deterministic::Context, &[u8], u32) -> Fixture, { let cfg = deterministic::Config::new(); let runner = deterministic::Runner::new(cfg); @@ -1010,7 +1025,7 @@ mod tests { runner.start(|mut context| async move { let epoch = Epoch::new(111); let num_validators = 10; - let fixture = fixture(&mut context, num_validators); + let fixture = fixture(&mut context, TEST_NAMESPACE, num_validators); let delayed_link = Link { latency: Duration::from_millis(80), diff --git a/consensus/src/ordered_broadcast/scheme.rs b/consensus/src/ordered_broadcast/scheme.rs index 292422b423..e318c92250 100644 --- a/consensus/src/ordered_broadcast/scheme.rs +++ b/consensus/src/ordered_broadcast/scheme.rs @@ -41,10 +41,10 @@ pub mod bls12381_multisig { //! Certificates contain signer indices alongside an aggregated signature, //! enabling secure per-validator activity tracking and conflict detection. - use crate::ordered_broadcast::types::AckSubject; + use crate::ordered_broadcast::types::{AckNamespace, AckSubject}; use commonware_cryptography::impl_certificate_bls12381_multisig; - impl_certificate_bls12381_multisig!(AckSubject<'a, P, D>); + impl_certificate_bls12381_multisig!(AckSubject<'a, P, D>, AckNamespace); } pub mod bls12381_threshold { @@ -56,10 +56,10 @@ pub mod bls12381_threshold { //! enabling equivocation attacks. Because peer connections are authenticated, evidence can be used locally //! (as it must be sent by said participant) but can't be used by an external observer. - use crate::ordered_broadcast::types::AckSubject; + use crate::ordered_broadcast::types::{AckNamespace, AckSubject}; use commonware_cryptography::impl_certificate_bls12381_threshold; - impl_certificate_bls12381_threshold!(AckSubject<'a, P, D>); + impl_certificate_bls12381_threshold!(AckSubject<'a, P, D>, AckNamespace); } pub mod ed25519 { @@ -70,10 +70,10 @@ pub mod ed25519 { //! contain signer indices alongside individual signatures, enabling secure //! per-validator activity tracking and fault detection. - use crate::ordered_broadcast::types::AckSubject; + use crate::ordered_broadcast::types::{AckNamespace, AckSubject}; use commonware_cryptography::{ed25519, impl_certificate_ed25519}; - impl_certificate_ed25519!(AckSubject<'a, ed25519::PublicKey, D>); + impl_certificate_ed25519!(AckSubject<'a, ed25519::PublicKey, D>, AckNamespace); } pub mod secp256r1 { @@ -84,8 +84,8 @@ pub mod secp256r1 { //! contain signer indices alongside individual signatures, enabling secure //! per-validator activity tracking and fault detection. - use crate::ordered_broadcast::types::AckSubject; + use crate::ordered_broadcast::types::{AckNamespace, AckSubject}; use commonware_cryptography::impl_certificate_secp256r1; - impl_certificate_secp256r1!(AckSubject<'a, P, D>); + impl_certificate_secp256r1!(AckSubject<'a, P, D>, AckNamespace); } diff --git a/consensus/src/ordered_broadcast/tip_manager.rs b/consensus/src/ordered_broadcast/tip_manager.rs index 5f9a255acc..177a051d5d 100644 --- a/consensus/src/ordered_broadcast/tip_manager.rs +++ b/consensus/src/ordered_broadcast/tip_manager.rs @@ -58,7 +58,7 @@ mod tests { use super::*; use crate::ordered_broadcast::{ scheme::{bls12381_multisig, bls12381_threshold, ed25519, secp256r1, Scheme}, - types::Chunk, + types::{Chunk, ChunkSigner}, }; use commonware_cryptography::{ bls12381::primitives::variant::{MinPk, MinSig}, @@ -72,6 +72,8 @@ mod tests { use rand::{rngs::StdRng, SeedableRng}; use std::panic::catch_unwind; + const NAMESPACE: &[u8] = b"tip_manager_test"; + /// Creates a node for testing with a given scheme. fn create_node>( fixture: &Fixture, @@ -79,9 +81,6 @@ mod tests { height: u64, payload: &str, ) -> Node { - use crate::ordered_broadcast::types::chunk_namespace; - use commonware_codec::Encode; - let sequencer = fixture.participants[sequencer_idx].clone(); let digest = Sha256::hash(payload.as_bytes()); let chunk = Chunk::new(sequencer, height, digest); @@ -90,9 +89,8 @@ mod tests { // which is ed25519::Signature for our PublicKey type) let mut rng = StdRng::seed_from_u64(sequencer_idx as u64); let private_key = commonware_cryptography::ed25519::PrivateKey::random(&mut rng); - let namespace = chunk_namespace(b"test"); - let message = chunk.encode(); - let signature = private_key.sign(namespace.as_ref(), &message); + let mut signer = ChunkSigner::new(b"test", private_key); + let signature = signer.sign(&chunk); Node::new(chunk, signature, None) } @@ -106,9 +104,9 @@ mod tests { fn put_new_tip(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { - let fixture = fixture(&mut test_rng(), 4); + let fixture = fixture(&mut test_rng(), NAMESPACE, 4); let mut manager = TipManager::::new(); let node = create_node(&fixture, 0, 1, "payload"); let key = node.chunk.sequencer.clone(); @@ -132,9 +130,9 @@ mod tests { fn put_same_height_same_payload(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { - let fixture = fixture(&mut test_rng(), 4); + let fixture = fixture(&mut test_rng(), NAMESPACE, 4); let mut manager = TipManager::::new(); let node = create_node(&fixture, 0, 1, "payload"); let key = node.chunk.sequencer.clone(); @@ -159,9 +157,9 @@ mod tests { fn put_higher_tip(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { - let fixture = fixture(&mut test_rng(), 4); + let fixture = fixture(&mut test_rng(), NAMESPACE, 4); let mut manager = TipManager::::new(); let node1 = create_node(&fixture, 0, 1, "payload1"); let key = node1.chunk.sequencer.clone(); @@ -187,9 +185,9 @@ mod tests { fn put_lower_tip_panics(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { - let fixture = fixture(&mut test_rng(), 4); + let fixture = fixture(&mut test_rng(), NAMESPACE, 4); let mut manager = TipManager::::new(); let node1 = create_node(&fixture, 0, 2, "payload"); assert!(manager.put(&node1)); @@ -219,9 +217,9 @@ mod tests { fn put_same_height_different_payload_panics(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { - let fixture = fixture(&mut test_rng(), 4); + let fixture = fixture(&mut test_rng(), NAMESPACE, 4); let mut manager = TipManager::::new(); let node1 = create_node(&fixture, 0, 1, "payload1"); assert!(manager.put(&node1)); @@ -277,9 +275,9 @@ mod tests { fn multiple_sequencers(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { - let fixture = fixture(&mut test_rng(), 4); + let fixture = fixture(&mut test_rng(), NAMESPACE, 4); let mut manager = TipManager::::new(); let node1 = create_node(&fixture, 0, 1, "payload1"); let node2 = create_node(&fixture, 1, 2, "payload2"); @@ -307,9 +305,9 @@ mod tests { fn put_multiple_updates(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { - let fixture = fixture(&mut test_rng(), 4); + let fixture = fixture(&mut test_rng(), NAMESPACE, 4); let mut manager = TipManager::::new(); // Insert tip with height 1. diff --git a/consensus/src/ordered_broadcast/types.rs b/consensus/src/ordered_broadcast/types.rs index 6808d8fe37..9d1f07e7e6 100644 --- a/consensus/src/ordered_broadcast/types.rs +++ b/consensus/src/ordered_broadcast/types.rs @@ -7,7 +7,7 @@ use commonware_codec::{ varint::UInt, Encode, EncodeSize, Error as CodecError, Read, ReadExt, Write, }; use commonware_cryptography::{ - certificate::{self, Attestation, Provider, Scheme}, + certificate::{self, Attestation, Namespace, Provider, Scheme}, Digest, PublicKey, Signer, }; use commonware_utils::{ordered::Set, union}; @@ -158,7 +158,7 @@ pub const ACK_SUFFIX: &[u8] = b"_ACK"; /// This provides domain separation for signatures, preventing cross-protocol attacks /// by ensuring signatures for chunks cannot be reused for other message types. #[inline] -pub fn chunk_namespace(namespace: &[u8]) -> Vec { +fn chunk_namespace(namespace: &[u8]) -> Vec { union(namespace, CHUNK_SUFFIX) } @@ -171,6 +171,82 @@ pub fn ack_namespace(namespace: &[u8]) -> Vec { union(namespace, ACK_SUFFIX) } +/// Namespace type for chunk signing/verification. +/// +/// This type encapsulates the pre-computed namespace bytes used for signing and +/// verifying chunks (nodes and proposals). +#[derive(Clone, Debug)] +pub struct ChunkNamespace(Vec); + +impl Namespace for ChunkNamespace { + fn derive(namespace: &[u8]) -> Self { + Self(chunk_namespace(namespace)) + } +} + +/// Signer for chunk operations. +/// +/// The namespace is pre-computed at construction time. +#[derive(Clone)] +pub struct ChunkSigner { + signer: C, + namespace: ChunkNamespace, +} + +impl ChunkSigner { + /// Creates a new ChunkSigner with the given namespace and signer. + /// + /// The chunk namespace is pre-computed from the base namespace. + pub fn new(namespace: &[u8], signer: C) -> Self { + Self { + signer, + namespace: ChunkNamespace::derive(namespace), + } + } + + /// Returns the public key of the underlying signer. + pub fn public_key(&self) -> C::PublicKey { + self.signer.public_key() + } + + /// Signs a chunk and returns the signature. + pub fn sign(&mut self, chunk: &Chunk) -> C::Signature + where + P: PublicKey, + D: Digest, + { + self.signer.sign(&self.namespace.0, &chunk.encode()) + } +} + +/// Verifier for chunk operations. +/// +/// The namespace is pre-computed at construction time. +#[derive(Clone)] +pub struct ChunkVerifier { + namespace: ChunkNamespace, +} + +impl ChunkVerifier { + /// Creates a new ChunkVerifier with the given namespace. + pub fn new(namespace: &[u8]) -> Self { + Self { + namespace: ChunkNamespace::derive(namespace), + } + } + + /// Verifies a chunk signature. + pub fn verify( + &self, + chunk: &Chunk, + signature: &P::Signature, + ) -> bool { + chunk + .sequencer + .verify(&self.namespace.0, &chunk.encode(), signature) + } +} + /// Used as the [crate::Automaton::Context] type. /// /// Carries the necessary context for the automaton to verify a payload, including @@ -262,6 +338,19 @@ where } } +/// Namespace type for ordered broadcast acknowledgments. +/// +/// This type encapsulates the pre-computed namespace bytes used for signing and +/// verifying acks. +#[derive(Clone, Debug)] +pub struct AckNamespace(Vec); + +impl Namespace for AckNamespace { + fn derive(namespace: &[u8]) -> Self { + Self(ack_namespace(namespace)) + } +} + /// Context for signing/verifying validator acknowledgments. /// /// This is used as the context type for `Scheme` implementations for validators. @@ -274,14 +363,19 @@ pub struct AckSubject<'a, P: PublicKey, D: Digest> { pub epoch: Epoch, } -impl<'a, P: PublicKey, D: Digest> certificate::Subject for AckSubject<'a, P, D> { - fn namespace_and_message(&self, namespace: &[u8]) -> (Bytes, Bytes) { +impl certificate::Subject for AckSubject<'_, P, D> { + type Namespace = AckNamespace; + + fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] { + &derived.0 + } + + fn message(&self) -> Bytes { let mut message = BytesMut::with_capacity(self.chunk.encode_size() + self.epoch.encode_size()); self.chunk.write(&mut message); self.epoch.write(&mut message); - - (ack_namespace(namespace).into(), message.freeze()) + message.freeze() } } @@ -433,92 +527,6 @@ impl Node { } } - /// Verify the Node (and its parent). - /// - /// This ensures: - /// 1. The sequencer's signature over the chunk is valid - /// 2. For non-genesis nodes, the parent's certificate is valid - /// - /// If verification is successful, returns: - /// - None for genesis nodes - /// - Some(parent_chunk) for non-genesis nodes - /// - /// If verification fails, returns an appropriate error. - pub fn verify( - &self, - rng: &mut R, - namespace: &[u8], - provider: &impl Provider, - ) -> Result>, Error> - where - R: CryptoRngCore, - S: scheme::Scheme, - { - // Verify chunk - let chunk_namespace = chunk_namespace(namespace); - let message = self.chunk.encode(); - if !self - .chunk - .sequencer - .verify(chunk_namespace.as_ref(), &message, &self.signature) - { - return Err(Error::InvalidSequencerSignature); - } - let Some(parent) = &self.parent else { - return Ok(None); - }; - - // Verify parent (if present) - let parent_chunk = Chunk::new( - self.chunk.sequencer.clone(), - self.chunk - .height - .checked_sub(1) - .ok_or(Error::ParentMissing)?, - parent.digest, - ); - - // Verify parent certificate using the scheme for the parent's epoch - let parent_scheme = provider - .scoped(parent.epoch) - .ok_or(Error::UnknownScheme(parent.epoch))?; - let ack_namespace = ack_namespace(namespace); - let ack_ctx = AckSubject { - chunk: &parent_chunk, - epoch: parent.epoch, - }; - if !parent_scheme.verify_certificate::( - rng, - &ack_namespace, - ack_ctx, - &parent.certificate, - ) { - return Err(Error::InvalidCertificate); - } - Ok(Some(parent_chunk)) - } - - /// Generate a new node with the given chunk, signature, (and parent). - /// - /// This is used by sequencers to create and sign new nodes for broadcast. - /// For non-genesis nodes (height > 0), a parent with a certificate must be provided. - pub fn sign( - namespace: &[u8], - signer: &mut C, - height: u64, - payload: D, - parent: Option>, - ) -> Self - where - C: Signer, - { - let chunk_namespace = chunk_namespace(namespace); - let pub_key = signer.public_key(); - let chunk = Chunk::new(pub_key, height, payload); - let signature = signer.sign(chunk_namespace.as_ref(), &chunk.encode()); - Self::new(chunk, signature, parent) - } - /// Decode a Node from network bytes with epoch-aware certificate decoding. /// /// This method performs staged decoding: @@ -581,6 +589,74 @@ impl Node { parent, }) } + + /// Signs and creates a new Node. + /// + /// This is used by sequencers to create and sign new nodes for broadcast. + /// For non-genesis nodes (height > 0), a parent with a certificate must be provided. + pub fn sign( + signer: &mut ChunkSigner, + height: u64, + payload: D, + parent: Option>, + ) -> Self + where + C: Signer, + { + let chunk = Chunk::new(signer.public_key(), height, payload); + let signature = signer.sign(&chunk); + Self::new(chunk, signature, parent) + } + + /// Verifies a Node (and its parent). + /// + /// This ensures: + /// 1. The sequencer's signature over the chunk is valid + /// 2. For non-genesis nodes, the parent's certificate is valid + /// + /// If verification is successful, returns: + /// - None for genesis nodes + /// - Some(parent_chunk) for non-genesis nodes + /// + /// If verification fails, returns an appropriate error. + pub fn verify( + &self, + rng: &mut R, + verifier: &ChunkVerifier, + provider: &impl Provider, + ) -> Result>, Error> + where + S: scheme::Scheme, + { + // Verify chunk signature + if !verifier.verify(&self.chunk, &self.signature) { + return Err(Error::InvalidSequencerSignature); + } + let Some(parent) = &self.parent else { + return Ok(None); + }; + + // Verify parent (if present) + let parent_chunk = Chunk::new( + self.chunk.sequencer.clone(), + self.chunk + .height + .checked_sub(1) + .ok_or(Error::ParentMissing)?, + parent.digest, + ); + let parent_scheme = provider + .scoped(parent.epoch) + .ok_or(Error::UnknownScheme(parent.epoch))?; + let ctx = AckSubject { + chunk: &parent_chunk, + epoch: parent.epoch, + }; + if !parent_scheme.verify_certificate::(rng, ctx, &parent.certificate) { + return Err(Error::InvalidCertificate); + } + Ok(Some(parent_chunk)) + } } impl Hash for Node { @@ -706,33 +782,31 @@ impl Ack { /// using the provided scheme. /// /// Returns true if the attestation is valid, false otherwise. - pub fn verify(&self, rng: &mut R, namespace: &[u8], scheme: &S) -> bool + pub fn verify(&self, rng: &mut R, scheme: &S) -> bool where R: CryptoRngCore, S: scheme::Scheme, { - let ack_namespace = ack_namespace(namespace); let ctx = AckSubject { chunk: &self.chunk, epoch: self.epoch, }; - scheme.verify_attestation::<_, D>(rng, &ack_namespace, ctx, &self.attestation) + scheme.verify_attestation::<_, D>(rng, ctx, &self.attestation) } /// Generate a new Ack by signing with the provided scheme. /// /// This is used by validators to create and sign new acknowledgments for chunks. /// Returns None if the scheme cannot sign. - pub fn sign(namespace: &[u8], scheme: &S, chunk: Chunk, epoch: Epoch) -> Option + pub fn sign(scheme: &S, chunk: Chunk, epoch: Epoch) -> Option where S: scheme::Scheme, { - let ack_namespace = ack_namespace(namespace); let ctx = AckSubject { chunk: &chunk, epoch, }; - let attestation = scheme.sign::(&ack_namespace, ctx)?; + let attestation = scheme.sign::(ctx)?; Some(Self::new(chunk, epoch, attestation)) } } @@ -880,17 +954,11 @@ impl Proposal { Self { chunk, signature } } - /// Verify the Proposal. + /// Verifies the proposal's signature. /// - /// This ensures that the sequencer's signature over the chunk is valid. - /// Returns true if the signature is valid, false otherwise. - pub fn verify(&self, namespace: &[u8]) -> bool { - // Verify chunk - let chunk_namespace = chunk_namespace(namespace); - let message = self.chunk.encode(); - self.chunk - .sequencer - .verify(chunk_namespace.as_ref(), &message, &self.signature) + /// Returns true if the sequencer's signature over the chunk is valid. + pub fn verify(&self, verifier: &ChunkVerifier) -> bool { + verifier.verify(&self.chunk, &self.signature) } } @@ -986,17 +1054,16 @@ impl Lock { /// using the provided scheme. /// /// Returns true if the signature is valid, false otherwise. - pub fn verify(&self, rng: &mut R, namespace: &[u8], scheme: &S) -> bool + pub fn verify(&self, rng: &mut R, scheme: &S) -> bool where R: CryptoRngCore, S: scheme::Scheme, { - let ack_namespace = ack_namespace(namespace); let ctx = AckSubject { chunk: &self.chunk, epoch: self.epoch, }; - scheme.verify_certificate::(rng, &ack_namespace, ctx, &self.certificate) + scheme.verify_certificate::(rng, ctx, &self.certificate) } } @@ -1066,6 +1133,14 @@ mod tests { const NAMESPACE: &[u8] = b"test"; + fn chunk_verifier() -> ChunkVerifier { + ChunkVerifier::new(NAMESPACE) + } + + fn chunk_signer(signer: PrivateKey) -> ChunkSigner { + ChunkSigner::new(NAMESPACE, signer) + } + // Helper function to create a sample digest fn sample_digest(v: u8) -> Sha256Digest { Sha256Digest::from([v; 32]) // Simple fixed digest for testing @@ -1079,10 +1154,10 @@ mod tests { /// Generate a fixture using the provided generator function with a specific seed. fn setup_seeded(n: u32, seed: u64, fixture: F) -> Fixture where - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = StdRng::seed_from_u64(seed); - fixture(&mut rng, n) + fixture(&mut rng, NAMESPACE, n) } #[test] @@ -1098,9 +1173,9 @@ mod tests { fn parent_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { - let fixture = fixture(&mut test_rng(), 4); + let fixture = fixture(&mut test_rng(), NAMESPACE, 4); let chunk = Chunk::new(fixture.participants[0].clone(), 0, sample_digest(1)); let epoch = Epoch::new(5); let quorum = commonware_utils::quorum(fixture.schemes.len() as u32) as usize; @@ -1112,11 +1187,7 @@ mod tests { }; let attestations: Vec<_> = fixture.schemes[..quorum] .iter() - .map(|scheme| { - scheme - .sign::(&ack_namespace(NAMESPACE), ctx.clone()) - .unwrap() - }) + .map(|scheme| scheme.sign::(ctx.clone()).unwrap()) .collect(); // Assemble certificate @@ -1145,9 +1216,9 @@ mod tests { fn node_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { - let fixture = fixture(&mut test_rng(), 4); + let fixture = fixture(&mut test_rng(), NAMESPACE, 4); let ed_scheme = sample_scheme(0); let public_key = ed_scheme.public_key(); let chunk_namespace = chunk_namespace(NAMESPACE); @@ -1178,11 +1249,7 @@ mod tests { }; let parent_attestations: Vec<_> = fixture.schemes[..quorum] .iter() - .map(|scheme| { - scheme - .sign::(&ack_namespace(NAMESPACE), parent_ctx.clone()) - .unwrap() - }) + .map(|scheme| scheme.sign::(parent_ctx.clone()).unwrap()) .collect(); let parent_certificate = fixture.schemes[0] @@ -1224,9 +1291,9 @@ mod tests { fn node_read_staged(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { - let fixture = fixture(&mut test_rng(), 4); + let fixture = fixture(&mut test_rng(), NAMESPACE, 4); // Create a provider that returns the verifier for any epoch. // This simulates the normal case where the scheme is available. @@ -1265,11 +1332,7 @@ mod tests { // Collect signatures from a quorum of validators to form the parent certificate. let parent_attestations: Vec<_> = fixture.schemes[..quorum(4) as usize] .iter() - .map(|scheme| { - scheme - .sign::(&ack_namespace(NAMESPACE), parent_ctx.clone()) - .unwrap() - }) + .map(|scheme| scheme.sign::(parent_ctx.clone()).unwrap()) .collect(); let parent_certificate = fixture.schemes[0] .assemble(parent_attestations) @@ -1326,9 +1389,9 @@ mod tests { fn ack_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { - let fixture = fixture(&mut test_rng(), 4); + let fixture = fixture(&mut test_rng(), NAMESPACE, 4); let chunk = Chunk::new(fixture.participants[0].clone(), 42, sample_digest(1)); let epoch = Epoch::new(5); @@ -1337,7 +1400,7 @@ mod tests { epoch, }; let attestation = fixture.schemes[0] - .sign::(NAMESPACE, ctx) + .sign::(ctx) .expect("Should sign vote"); let ack = Ack:: { @@ -1367,10 +1430,10 @@ mod tests { fn activity_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 4); + let fixture = fixture(&mut rng, NAMESPACE, 4); let scheme = sample_scheme(0); let public_key = scheme.public_key(); let chunk_namespace = chunk_namespace(NAMESPACE); @@ -1405,11 +1468,7 @@ mod tests { }; let attestations: Vec<_> = fixture.schemes[..quorum] .iter() - .map(|scheme| { - scheme - .sign::(&ack_namespace(NAMESPACE), ctx.clone()) - .unwrap() - }) + .map(|scheme| scheme.sign::(ctx.clone()).unwrap()) .collect(); // Assemble certificate @@ -1421,7 +1480,7 @@ mod tests { let lock = Lock::::new(chunk.clone(), epoch, certificate); // Verify lock - assert!(lock.verify(&mut rng, NAMESPACE, &fixture.verifier)); + assert!(lock.verify(&mut rng, &fixture.verifier)); // Test activity with the lock let activity = Activity::::Lock(lock.clone()); @@ -1433,7 +1492,7 @@ mod tests { Activity::Lock(l) => { assert_eq!(l.chunk, chunk); assert_eq!(l.epoch, epoch); - assert!(l.verify(&mut rng, NAMESPACE, &fixture.verifier)); + assert!(l.verify(&mut rng, &fixture.verifier)); } _ => panic!("Decoded activity has wrong type"), } @@ -1468,16 +1527,17 @@ mod tests { assert_eq!(decoded.signature, proposal.signature); // Verify the decoded proposal - assert!(decoded.verify(NAMESPACE)); + let verifier = chunk_verifier(); + assert!(decoded.verify(&verifier)); } fn lock_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 4); + let fixture = fixture(&mut rng, NAMESPACE, 4); let public_key = sample_scheme(0).public_key(); let chunk = Chunk::new(public_key, 42, sample_digest(1)); let epoch = Epoch::new(5); @@ -1490,11 +1550,7 @@ mod tests { }; let attestations: Vec<_> = fixture.schemes[..quorum] .iter() - .map(|scheme| { - scheme - .sign::(&ack_namespace(NAMESPACE), ctx.clone()) - .unwrap() - }) + .map(|scheme| scheme.sign::(ctx.clone()).unwrap()) .collect(); // Assemble certificate @@ -1513,7 +1569,7 @@ mod tests { assert_eq!(decoded.epoch, lock.epoch); // Verify the signature in the decoded lock - assert!(decoded.verify(&mut rng, NAMESPACE, &fixture.verifier)); + assert!(decoded.verify(&mut rng, &fixture.verifier)); } #[test] @@ -1529,24 +1585,21 @@ mod tests { fn node_sign_verify(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 4); - let mut scheme = sample_scheme(0); + let fixture = fixture(&mut rng, NAMESPACE, 4); + let scheme = sample_scheme(0); let public_key = scheme.public_key(); + let mut signer = chunk_signer(scheme); + let verifier = chunk_verifier(); let quorum = commonware_utils::quorum(fixture.schemes.len() as u32) as usize; // Test genesis node (no parent) - let node = Node::::sign( - NAMESPACE, - &mut scheme, - 0, - sample_digest(1), - None, - ); + let node: Node = + Node::sign(&mut signer, 0, sample_digest(1), None); let provider = ConstantProvider::new(fixture.verifier.clone()); - let result = node.verify(&mut rng, NAMESPACE, &provider); + let result = node.verify(&mut rng, &verifier, &provider); assert!(result.is_ok()); assert!(result.unwrap().is_none()); @@ -1561,11 +1614,7 @@ mod tests { }; let parent_attestations: Vec<_> = fixture.schemes[..quorum] .iter() - .map(|scheme| { - scheme - .sign::(&ack_namespace(NAMESPACE), parent_ctx.clone()) - .unwrap() - }) + .map(|scheme| scheme.sign::(parent_ctx.clone()).unwrap()) .collect(); let parent_certificate = fixture.schemes[0] .assemble(parent_attestations) @@ -1576,15 +1625,10 @@ mod tests { parent_epoch, parent_certificate, )); - let node = Node::::sign( - NAMESPACE, - &mut scheme, - 1, - sample_digest(2), - parent, - ); + let node: Node = + Node::sign(&mut signer, 1, sample_digest(2), parent); - let result = node.verify(&mut rng, NAMESPACE, &provider); + let result = node.verify(&mut rng, &verifier, &provider); assert!(result.is_ok()); assert!(result.unwrap().is_some()); } @@ -1602,19 +1646,16 @@ mod tests { fn ack_sign_verify(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 4); + let fixture = fixture(&mut rng, NAMESPACE, 4); let public_key = sample_scheme(0).public_key(); let chunk = Chunk::new(public_key, 42, sample_digest(1)); let epoch = Epoch::new(5); - let ack = Ack::sign(NAMESPACE, &fixture.schemes[0], chunk, epoch).expect("Should sign ack"); - assert!(ack.verify(&mut rng, NAMESPACE, &fixture.verifier)); - - // Test that verification fails with wrong namespace - assert!(!ack.verify(&mut rng, b"wrong", &fixture.verifier)); + let ack = Ack::sign(&fixture.schemes[0], chunk, epoch).expect("Should sign ack"); + assert!(ack.verify(&mut rng, &fixture.verifier)); } #[test] @@ -1630,10 +1671,10 @@ mod tests { fn certificate_assembly(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 4); + let fixture = fixture(&mut rng, NAMESPACE, 4); let public_key = sample_scheme(0).public_key(); let chunk = Chunk::new(public_key, 42, sample_digest(1)); let epoch = Epoch::new(5); @@ -1646,11 +1687,7 @@ mod tests { }; let attestations: Vec<_> = fixture.schemes[..quorum] .iter() - .map(|scheme| { - scheme - .sign::(&ack_namespace(NAMESPACE), ctx.clone()) - .unwrap() - }) + .map(|scheme| scheme.sign::(ctx.clone()).unwrap()) .collect(); // Assemble certificate @@ -1662,7 +1699,7 @@ mod tests { let lock = Lock::::new(chunk, epoch, certificate); // Verify lock - assert!(lock.verify(&mut rng, NAMESPACE, &fixture.verifier)); + assert!(lock.verify(&mut rng, &fixture.verifier)); } #[test] @@ -1678,10 +1715,10 @@ mod tests { fn lock_verify(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 4); + let fixture = fixture(&mut rng, NAMESPACE, 4); let public_key = sample_scheme(0).public_key(); let chunk = Chunk::new(public_key, 42, sample_digest(1)); let epoch = Epoch::new(5); @@ -1694,11 +1731,7 @@ mod tests { }; let attestations: Vec<_> = fixture.schemes[..quorum] .iter() - .map(|scheme| { - scheme - .sign::(&ack_namespace(NAMESPACE), ctx.clone()) - .unwrap() - }) + .map(|scheme| scheme.sign::(ctx.clone()).unwrap()) .collect(); let certificate = fixture.schemes[0] .assemble(attestations) @@ -1708,10 +1741,7 @@ mod tests { let lock = Lock::::new(chunk, epoch, certificate); // Verify lock - assert!(lock.verify(&mut rng, NAMESPACE, &fixture.verifier)); - - // Test that verification fails with wrong namespace - assert!(!lock.verify(&mut rng, b"wrong", &fixture.verifier)); + assert!(lock.verify(&mut rng, &fixture.verifier)); } #[test] @@ -1736,19 +1766,21 @@ mod tests { let proposal = Proposal::::new(chunk, signature); // Verify proposal - assert!(proposal.verify(NAMESPACE)); + let verifier = chunk_verifier(); + assert!(proposal.verify(&verifier)); // Test that verification fails with wrong namespace - assert!(!proposal.verify(b"wrong")); + let wrong_verifier = ChunkVerifier::new(b"wrong"); + assert!(!proposal.verify(&wrong_verifier)); } fn node_verify_invalid_signature(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 4); + let fixture = fixture(&mut rng, NAMESPACE, 4); let scheme = sample_scheme(0); let public_key = scheme.public_key(); @@ -1765,7 +1797,8 @@ mod tests { // Verification should succeed let provider = ConstantProvider::new(fixture.verifier); - assert!(node.verify(&mut rng, NAMESPACE, &provider).is_ok()); + let verifier = chunk_verifier(); + assert!(node.verify(&mut rng, &verifier, &provider).is_ok()); // Now create a node with invalid signature let tampered_signature = scheme.sign(chunk_namespace.as_ref(), &node.encode()); @@ -1773,7 +1806,7 @@ mod tests { // Verification should fail assert!(matches!( - invalid_node.verify(&mut rng, NAMESPACE, &provider), + invalid_node.verify(&mut rng, &verifier, &provider), Err(Error::InvalidSequencerSignature) )); } @@ -1791,10 +1824,10 @@ mod tests { fn node_verify_invalid_parent_signature(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 4); + let fixture = fixture(&mut rng, NAMESPACE, 4); let scheme = sample_scheme(0); let public_key = scheme.public_key(); let quorum = commonware_utils::quorum(fixture.schemes.len() as u32) as usize; @@ -1811,11 +1844,7 @@ mod tests { }; let parent_attestations: Vec<_> = fixture.schemes[..quorum] .iter() - .map(|scheme| { - scheme - .sign::(&ack_namespace(NAMESPACE), parent_ctx.clone()) - .unwrap() - }) + .map(|scheme| scheme.sign::(parent_ctx.clone()).unwrap()) .collect(); let certificate = fixture.schemes[0] .assemble(parent_attestations) @@ -1836,7 +1865,8 @@ mod tests { // Verification should succeed let provider = ConstantProvider::new(fixture.verifier.clone()); - assert!(node.verify(&mut rng, NAMESPACE, &provider).is_ok()); + let verifier = chunk_verifier(); + assert!(node.verify(&mut rng, &verifier, &provider).is_ok()); // Now create a parent with invalid certificate // Generate certificate with the wrong keys (sign with schemes[1..] but pretend it's from schemes[0..]) @@ -1846,11 +1876,7 @@ mod tests { }; let wrong_attestations: Vec<_> = fixture.schemes[..quorum] .iter() - .map(|scheme| { - scheme - .sign::(&ack_namespace(NAMESPACE), wrong_ctx.clone()) - .unwrap() - }) + .map(|scheme| scheme.sign::(wrong_ctx.clone()).unwrap()) .collect(); let wrong_certificate = fixture.schemes[0] .assemble(wrong_attestations) @@ -1869,7 +1895,7 @@ mod tests { // Verification should fail because the parent certificate was signed for different epoch assert!(matches!( - node.verify(&mut rng, NAMESPACE, &provider), + node.verify(&mut rng, &verifier, &provider), Err(Error::InvalidCertificate) )); } @@ -1887,26 +1913,22 @@ mod tests { fn ack_verify_invalid_signature(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 4); + let fixture = fixture(&mut rng, NAMESPACE, 4); // Create a chunk and ack let public_key = sample_scheme(0).public_key(); let chunk = Chunk::new(public_key, 42, sample_digest(1)); let epoch = Epoch::new(5); // Create a valid ack - let ack = Ack::::sign( - NAMESPACE, - &fixture.schemes[0], - chunk.clone(), - epoch, - ) - .expect("Should sign ack"); + let ack = + Ack::::sign(&fixture.schemes[0], chunk.clone(), epoch) + .expect("Should sign ack"); // Verification should succeed - assert!(ack.verify(&mut rng, NAMESPACE, &fixture.verifier)); + assert!(ack.verify(&mut rng, &fixture.verifier)); // Create an ack with tampered signature by signing with a different scheme let ctx = AckSubject { @@ -1914,7 +1936,7 @@ mod tests { epoch, }; let mut tampered_vote = fixture.schemes[1] - .sign::(NAMESPACE, ctx) + .sign::(ctx) .expect("Should sign vote"); // Change the signer index to mismatch with the actual signature // The vote was signed by validator 1, but we claim it's from validator 0 @@ -1922,7 +1944,7 @@ mod tests { let invalid_ack = Ack::::new(chunk, epoch, tampered_vote); // Verification should fail because the signer index doesn't match the signature - assert!(!invalid_ack.verify(&mut rng, NAMESPACE, &fixture.verifier)); + assert!(!invalid_ack.verify(&mut rng, &fixture.verifier)); } #[test] @@ -1938,7 +1960,7 @@ mod tests { fn ack_verify_wrong_validator(f: F) where S: Scheme, - F: Fn(&mut StdRng, u32) -> Fixture, + F: Fn(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); let fixture = setup_seeded(4, 0, &f); @@ -1949,15 +1971,14 @@ mod tests { let epoch = Epoch::new(5); // Create a valid ack - let ack = - Ack::::sign(NAMESPACE, &fixture.schemes[0], chunk, epoch) - .expect("Should sign ack"); + let ack = Ack::::sign(&fixture.schemes[0], chunk, epoch) + .expect("Should sign ack"); // Verification should succeed with correct verifier - assert!(ack.verify(&mut rng, NAMESPACE, &fixture.verifier)); + assert!(ack.verify(&mut rng, &fixture.verifier)); // Verification should fail with wrong verifier - assert!(!ack.verify(&mut rng, NAMESPACE, &wrong_fixture.verifier)); + assert!(!ack.verify(&mut rng, &wrong_fixture.verifier)); } #[test] @@ -1973,7 +1994,7 @@ mod tests { fn lock_verify_invalid_signature(f: F) where S: Scheme, - F: Fn(&mut StdRng, u32) -> Fixture, + F: Fn(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); let fixture = setup_seeded(4, 0, &f); @@ -1990,11 +2011,7 @@ mod tests { }; let attestations: Vec<_> = fixture.schemes[..quorum_size] .iter() - .map(|scheme| { - scheme - .sign::(&ack_namespace(NAMESPACE), ctx.clone()) - .unwrap() - }) + .map(|scheme| scheme.sign::(ctx.clone()).unwrap()) .collect(); let certificate = fixture.schemes[0] .assemble(attestations) @@ -2004,16 +2021,12 @@ mod tests { let lock = Lock::::new(chunk.clone(), epoch, certificate); // Verification should succeed - assert!(lock.verify(&mut rng, NAMESPACE, &fixture.verifier)); + assert!(lock.verify(&mut rng, &fixture.verifier)); // Generate certificate with the wrong keys let wrong_attestations: Vec<_> = wrong_fixture.schemes[..quorum_size] .iter() - .map(|scheme| { - scheme - .sign::(&ack_namespace(NAMESPACE), ctx.clone()) - .unwrap() - }) + .map(|scheme| scheme.sign::(ctx.clone()).unwrap()) .collect(); let wrong_certificate = wrong_fixture.schemes[0] .assemble(wrong_attestations) @@ -2023,10 +2036,10 @@ mod tests { let wrong_lock = Lock::::new(chunk, epoch, wrong_certificate); // Verification should fail with the original public key - assert!(!wrong_lock.verify(&mut rng, NAMESPACE, &fixture.verifier)); + assert!(!wrong_lock.verify(&mut rng, &fixture.verifier)); // But succeed with the matching wrong verifier - assert!(wrong_lock.verify(&mut rng, NAMESPACE, &wrong_fixture.verifier)); + assert!(wrong_lock.verify(&mut rng, &wrong_fixture.verifier)); } #[test] @@ -2051,10 +2064,12 @@ mod tests { let proposal = Proposal::::new(chunk, signature); // Verify with correct namespace - should pass - assert!(proposal.verify(NAMESPACE)); + let verifier = chunk_verifier(); + assert!(proposal.verify(&verifier)); // Verify with wrong namespace - should fail - assert!(!proposal.verify(b"wrong_namespace")); + let wrong_verifier = ChunkVerifier::new(b"wrong_namespace"); + assert!(!proposal.verify(&wrong_verifier)); } #[test] @@ -2072,16 +2087,17 @@ mod tests { let proposal = Proposal::::new(chunk, signature); // Verification should fail because the signature doesn't match the sequencer's public key - assert!(!proposal.verify(NAMESPACE)); + let verifier = chunk_verifier(); + assert!(!proposal.verify(&verifier)); } fn node_genesis_with_parent_fails(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 4); + let fixture = fixture(&mut rng, NAMESPACE, 4); // Try to create a node with height 0 and a parent let public_key = sample_scheme(0).public_key(); let chunk = Chunk::new(public_key.clone(), 0, sample_digest(1)); @@ -2099,11 +2115,7 @@ mod tests { }; let attestations: Vec<_> = fixture.schemes[..quorum_size] .iter() - .map(|scheme| { - scheme - .sign::(&ack_namespace(NAMESPACE), ctx.clone()) - .unwrap() - }) + .map(|scheme| scheme.sign::(ctx.clone()).unwrap()) .collect(); let certificate = fixture.schemes[0] .assemble(attestations) @@ -2134,10 +2146,10 @@ mod tests { fn node_non_genesis_without_parent_fails(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 4); + let fixture = fixture(&mut rng, NAMESPACE, 4); // Try to create a non-genesis node without a parent let public_key = sample_scheme(0).public_key(); let chunk = Chunk::new(public_key, 1, sample_digest(1)); // Height > 0 @@ -2167,10 +2179,10 @@ mod tests { fn node_genesis_with_parent_panics(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 4); + let fixture = fixture(&mut rng, NAMESPACE, 4); // Try to create a genesis node (height 0) with a parent - should panic in Node::new let public_key = sample_scheme(0).public_key(); @@ -2188,11 +2200,7 @@ mod tests { }; let parent_attestations: Vec<_> = fixture.schemes[..quorum(4) as usize] .iter() - .map(|scheme| { - scheme - .sign::(&ack_namespace(NAMESPACE), parent_ctx.clone()) - .unwrap() - }) + .map(|scheme| scheme.sign::(parent_ctx.clone()).unwrap()) .collect(); let parent_certificate = fixture.schemes[0] .assemble(parent_attestations) @@ -2233,10 +2241,10 @@ mod tests { fn node_non_genesis_without_parent_panics(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 4); + let fixture = fixture(&mut rng, NAMESPACE, 4); // Try to create a non-genesis node (height > 0) without a parent - should panic on decode let public_key = sample_scheme(0).public_key(); diff --git a/consensus/src/simplex/actors/batcher/actor.rs b/consensus/src/simplex/actors/batcher/actor.rs index 7d6679fc88..0dd5c20830 100644 --- a/consensus/src/simplex/actors/batcher/actor.rs +++ b/consensus/src/simplex/actors/batcher/actor.rs @@ -43,7 +43,6 @@ pub struct Actor< activity_timeout: ViewDelta, skip_timeout: ViewDelta, epoch: Epoch, - namespace: Vec, mailbox_receiver: mpsc::Receiver>, @@ -114,7 +113,6 @@ impl< activity_timeout: cfg.activity_timeout, skip_timeout: cfg.skip_timeout, epoch: cfg.epoch, - namespace: cfg.namespace, mailbox_receiver: receiver, @@ -285,7 +283,6 @@ impl< if !notarization.verify( &mut self.context, &self.scheme, - &self.namespace, ) { warn!(?sender, %view, "blocking peer for invalid notarization"); self.blocker.block(sender).await; @@ -312,7 +309,6 @@ impl< if !nullification.verify::<_, D>( &mut self.context, &self.scheme, - &self.namespace, ) { warn!(?sender, %view, "blocking peer for invalid nullification"); self.blocker.block(sender).await; @@ -339,7 +335,6 @@ impl< if !finalization.verify( &mut self.context, &self.scheme, - &self.namespace, ) { warn!(?sender, %view, "blocking peer for invalid finalization"); self.blocker.block(sender).await; @@ -443,11 +438,11 @@ impl< // Batch verify votes if ready let mut timer = self.verify_latency.timer(); let verified = if round.ready_notarizes() { - Some(round.verify_notarizes(&mut self.context, &self.namespace)) + Some(round.verify_notarizes(&mut self.context)) } else if round.ready_nullifies() { - Some(round.verify_nullifies(&mut self.context, &self.namespace)) + Some(round.verify_nullifies(&mut self.context)) } else if round.ready_finalizes() { - Some(round.verify_finalizes(&mut self.context, &self.namespace)) + Some(round.verify_finalizes(&mut self.context)) } else { None }; diff --git a/consensus/src/simplex/actors/batcher/mod.rs b/consensus/src/simplex/actors/batcher/mod.rs index 6133c7799e..f18147d5da 100644 --- a/consensus/src/simplex/actors/batcher/mod.rs +++ b/consensus/src/simplex/actors/batcher/mod.rs @@ -23,7 +23,6 @@ pub struct Config { pub activity_timeout: ViewDelta, pub skip_timeout: ViewDelta, pub epoch: Epoch, - pub namespace: Vec, pub mailbox_size: usize, } @@ -67,14 +66,13 @@ mod tests { fn build_notarization>( schemes: &[S], - namespace: &[u8], proposal: &Proposal, count: usize, ) -> Notarization { let votes: Vec<_> = schemes .iter() .take(count) - .map(|scheme| Notarize::sign(scheme, namespace, proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap()) .collect(); Notarization::from_notarizes(&schemes[0], &votes) .expect("notarization requires a quorum of votes") @@ -82,14 +80,13 @@ mod tests { fn build_nullification>( schemes: &[S], - namespace: &[u8], round: Round, count: usize, ) -> Nullification { let votes: Vec<_> = schemes .iter() .take(count) - .map(|scheme| Nullify::sign::(scheme, namespace, round).unwrap()) + .map(|scheme| Nullify::sign::(scheme, round).unwrap()) .collect(); Nullification::from_nullifies(&schemes[0], &votes) .expect("nullification requires a quorum of votes") @@ -97,14 +94,13 @@ mod tests { fn build_finalization>( schemes: &[S], - namespace: &[u8], proposal: &Proposal, count: usize, ) -> Finalization { let votes: Vec<_> = schemes .iter() .take(count) - .map(|scheme| Finalize::sign(scheme, namespace, proposal.clone()).unwrap()) + .map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap()) .collect(); Finalization::from_finalizes(&schemes[0], &votes) .expect("finalization requires a quorum of votes") @@ -113,7 +109,7 @@ mod tests { fn certificate_forwarding_from_network(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, { let n = 5; let quorum = quorum(n) as usize; @@ -137,11 +133,10 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Setup reporter mock let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: schemes[0].participants().clone(), scheme: schemes[0].clone(), elector: ::default(), @@ -158,7 +153,6 @@ mod tests { activity_timeout: ViewDelta::new(10), skip_timeout: ViewDelta::new(5), epoch, - namespace: namespace.clone(), mailbox_size: 128, }; let (batcher, mut batcher_mailbox) = Actor::new(context.clone(), batcher_cfg); @@ -204,9 +198,9 @@ mod tests { let round = Round::new(epoch, view); let proposal = Proposal::new(round, View::zero(), Sha256::hash(b"test_payload")); - let notarization = build_notarization(&schemes, &namespace, &proposal, quorum); - let nullification = build_nullification(&schemes, &namespace, round, quorum); - let finalization = build_finalization(&schemes, &namespace, &proposal, quorum); + let notarization = build_notarization(&schemes, &proposal, quorum); + let nullification = build_nullification(&schemes, round, quorum); + let finalization = build_finalization(&schemes, &proposal, quorum); // Send notarization from network injector_sender @@ -276,7 +270,7 @@ mod tests { fn quorum_votes_construct_certificate(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, { let n = 5; let quorum_size = quorum(n) as usize; @@ -300,11 +294,10 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Setup reporter mock let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: schemes[0].participants().clone(), scheme: schemes[0].clone(), elector: ::default(), @@ -321,7 +314,6 @@ mod tests { activity_timeout: ViewDelta::new(10), skip_timeout: ViewDelta::new(5), epoch, - namespace: namespace.clone(), mailbox_size: 128, }; let (batcher, mut batcher_mailbox) = Actor::new(context.clone(), batcher_cfg); @@ -375,7 +367,7 @@ mod tests { // Participant 0's vote will be sent via mailbox.constructed() // Participant 1 is the leader, so their vote triggers proposal forwarding for i in 1..quorum_size { - let vote = Notarize::sign(&schemes[i], &namespace, proposal.clone()).unwrap(); + let vote = Notarize::sign(&schemes[i], proposal.clone()).unwrap(); if let Some(ref mut sender) = participant_senders[i] { sender .send( @@ -389,7 +381,7 @@ mod tests { } // Send our own vote via constructed message - let our_vote = Notarize::sign(&schemes[0], &namespace, proposal.clone()).unwrap(); + let our_vote = Notarize::sign(&schemes[0], proposal.clone()).unwrap(); batcher_mailbox .constructed(Vote::Notarize(our_vote)) .await; @@ -423,7 +415,7 @@ mod tests { fn votes_and_certificate_deduplication(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, { let n = 5; let quorum_size = quorum(n) as usize; @@ -447,11 +439,10 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Setup reporter mock let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: schemes[0].participants().clone(), scheme: schemes[0].clone(), elector: ::default(), @@ -468,7 +459,6 @@ mod tests { activity_timeout: ViewDelta::new(10), skip_timeout: ViewDelta::new(5), epoch, - namespace: namespace.clone(), mailbox_size: 128, }; let (batcher, mut batcher_mailbox) = Actor::new(context.clone(), batcher_cfg); @@ -527,12 +517,12 @@ mod tests { // Build proposal, votes, and certificate let round = Round::new(epoch, view); let proposal = Proposal::new(round, View::zero(), Sha256::hash(b"test_payload")); - let notarization = build_notarization(&schemes, &namespace, &proposal, quorum_size); + let notarization = build_notarization(&schemes, &proposal, quorum_size); // Send some votes (but not enough for quorum), starting with leader (participant 1) // This triggers proposal forwarding for i in 1..quorum_size - 1 { - let vote = Notarize::sign(&schemes[i], &namespace, proposal.clone()).unwrap(); + let vote = Notarize::sign(&schemes[i], proposal.clone()).unwrap(); if let Some(ref mut sender) = participant_senders[i] { sender .send( @@ -546,7 +536,7 @@ mod tests { } // Send our own vote - let our_vote = Notarize::sign(&schemes[0], &namespace, proposal.clone()).unwrap(); + let our_vote = Notarize::sign(&schemes[0], proposal.clone()).unwrap(); batcher_mailbox.constructed(Vote::Notarize(our_vote)).await; // Give network time to deliver votes @@ -577,7 +567,7 @@ mod tests { // Now send enough votes to reach quorum (this vote would complete quorum) let last_vote = - Notarize::sign(&schemes[quorum_size - 1], &namespace, proposal.clone()).unwrap(); + Notarize::sign(&schemes[quorum_size - 1], proposal.clone()).unwrap(); if let Some(ref mut sender) = participant_senders[quorum_size - 1] { sender .send( @@ -616,7 +606,7 @@ mod tests { fn conflicting_votes_dont_produce_invalid_certificate(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, { let n = 7; let namespace = b"batcher_test".to_vec(); @@ -639,11 +629,10 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Setup reporter mock let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: schemes[0].participants().clone(), scheme: schemes[0].clone(), elector: ::default(), @@ -660,7 +649,6 @@ mod tests { activity_timeout: ViewDelta::new(10), skip_timeout: ViewDelta::new(5), epoch, - namespace: namespace.clone(), mailbox_size: 128, }; let (batcher, mut batcher_mailbox) = Actor::new(context.clone(), batcher_cfg); @@ -713,7 +701,7 @@ mod tests { // Send vote for proposal_a from participant 1 (the leader) // This establishes proposal_a as the leader's proposal let leader_vote = - Notarize::sign(&schemes[1], &namespace, proposal_a.clone()).unwrap(); + Notarize::sign(&schemes[1], proposal_a.clone()).unwrap(); if let Some(ref mut sender) = participant_senders[1] { sender .send( @@ -738,7 +726,7 @@ mod tests { // Now send votes for proposal_b from participants 2, 3, 4, 5 (4 votes) // These are for a DIFFERENT proposal and should be filtered out by BatchVerifier for i in 2..=5 { - let vote = Notarize::sign(&schemes[i], &namespace, proposal_b.clone()).unwrap(); + let vote = Notarize::sign(&schemes[i], proposal_b.clone()).unwrap(); if let Some(ref mut sender) = participant_senders[i] { sender .send( @@ -771,13 +759,13 @@ mod tests { // Now send 4 more votes for proposal_a (from participants 0,2,3,4) // Participant 0 is us, use constructed - let our_vote = Notarize::sign(&schemes[0], &namespace, proposal_a.clone()).unwrap(); + let our_vote = Notarize::sign(&schemes[0], proposal_a.clone()).unwrap(); batcher_mailbox .constructed(Vote::Notarize(our_vote)) .await; // Participants 6 hasn't voted yet - use them for proposal_a - let vote6 = Notarize::sign(&schemes[6], &namespace, proposal_a.clone()).unwrap(); + let vote6 = Notarize::sign(&schemes[6], proposal_a.clone()).unwrap(); if let Some(ref mut sender) = participant_senders[6] { sender .send( @@ -821,7 +809,7 @@ mod tests { fn proposal_forwarded_after_leader_set(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, { let n = 5; let namespace = b"batcher_test".to_vec(); @@ -844,11 +832,10 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Setup reporter mock let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: schemes[0].participants().clone(), scheme: schemes[0].clone(), elector: ::default(), @@ -865,7 +852,6 @@ mod tests { activity_timeout: ViewDelta::new(10), skip_timeout: ViewDelta::new(5), epoch, - namespace: namespace.clone(), mailbox_size: 128, }; let (batcher, mut batcher_mailbox) = Actor::new(context.clone(), batcher_cfg); @@ -910,7 +896,7 @@ mod tests { // Build proposal and leader's vote let round = Round::new(epoch, view); let proposal = Proposal::new(round, View::zero(), Sha256::hash(b"test_payload")); - let leader_vote = Notarize::sign(&schemes[1], &namespace, proposal.clone()).unwrap(); + let leader_vote = Notarize::sign(&schemes[1], proposal.clone()).unwrap(); // Now send the leader's vote - this should trigger proposal forwarding leader_sender @@ -949,7 +935,7 @@ mod tests { fn proposal_forwarded_before_leader_set(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, { let n = 5; let namespace = b"batcher_test".to_vec(); @@ -972,11 +958,10 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Setup reporter mock let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: schemes[0].participants().clone(), scheme: schemes[0].clone(), elector: ::default(), @@ -993,7 +978,6 @@ mod tests { activity_timeout: ViewDelta::new(10), skip_timeout: ViewDelta::new(5), epoch, - namespace: namespace.clone(), mailbox_size: 128, }; let (batcher, mut batcher_mailbox) = Actor::new(context.clone(), batcher_cfg); @@ -1029,7 +1013,7 @@ mod tests { let view = View::new(1); let round = Round::new(epoch, view); let proposal = Proposal::new(round, View::zero(), Sha256::hash(b"test_payload")); - let leader_vote = Notarize::sign(&schemes[1], &namespace, proposal.clone()).unwrap(); + let leader_vote = Notarize::sign(&schemes[1], proposal.clone()).unwrap(); // Send the leader's vote BEFORE setting the leader leader_sender @@ -1078,7 +1062,7 @@ mod tests { fn leader_activity_detection(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, { let n = 5; let namespace = b"batcher_test".to_vec(); @@ -1102,11 +1086,10 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Setup reporter mock let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: schemes[0].participants().clone(), scheme: schemes[0].clone(), elector: ::default(), @@ -1123,7 +1106,6 @@ mod tests { activity_timeout: ViewDelta::new(10), skip_timeout: ViewDelta::new(skip_timeout), epoch, - namespace: namespace.clone(), mailbox_size: 128, }; let (batcher, mut batcher_mailbox) = Actor::new(context.clone(), batcher_cfg); @@ -1176,7 +1158,7 @@ mod tests { // Test 3: Send a vote from the leader for the current view (view 5) let round = Round::new(epoch, view); let proposal = Proposal::new(round, View::zero(), Sha256::hash(b"test_payload")); - let leader_vote = Notarize::sign(&schemes[1], &namespace, proposal).unwrap(); + let leader_vote = Notarize::sign(&schemes[1], proposal).unwrap(); leader_sender .send( Recipients::One(me.clone()), @@ -1228,7 +1210,7 @@ mod tests { fn votes_skipped_for_finalized_views(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, { let n = 5; let quorum_size = quorum(n) as usize; @@ -1252,11 +1234,10 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Setup reporter mock let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: schemes[0].participants().clone(), scheme: schemes[0].clone(), elector: ::default(), @@ -1273,7 +1254,6 @@ mod tests { activity_timeout: ViewDelta::new(10), skip_timeout: ViewDelta::new(5), epoch, - namespace: namespace.clone(), mailbox_size: 128, }; let (batcher, mut batcher_mailbox) = Actor::new(context.clone(), batcher_cfg); @@ -1333,7 +1313,7 @@ mod tests { let round1 = Round::new(epoch, view1); let proposal1 = Proposal::new(round1, View::zero(), Sha256::hash(b"payload1")); for i in 1..quorum_size { - let vote = Notarize::sign(&schemes[i], &namespace, proposal1.clone()).unwrap(); + let vote = Notarize::sign(&schemes[i], proposal1.clone()).unwrap(); if let Some(ref mut sender) = participant_senders[i] { sender .send( @@ -1347,7 +1327,7 @@ mod tests { } // Send our own notarize vote for view 1 via constructed - let our_notarize = Notarize::sign(&schemes[0], &namespace, proposal1.clone()).unwrap(); + let our_notarize = Notarize::sign(&schemes[0], proposal1.clone()).unwrap(); batcher_mailbox .constructed(Vote::Notarize(our_notarize)) .await; @@ -1379,7 +1359,7 @@ mod tests { let round2 = Round::new(epoch, view2); let proposal2 = Proposal::new(round2, view1, Sha256::hash(b"payload2")); for i in 1..quorum_size { - let vote = Notarize::sign(&schemes[i], &namespace, proposal2.clone()).unwrap(); + let vote = Notarize::sign(&schemes[i], proposal2.clone()).unwrap(); if let Some(ref mut sender) = participant_senders[i] { sender .send( @@ -1393,7 +1373,7 @@ mod tests { } // Send our own notarize vote for view 2 via constructed - let our_notarize2 = Notarize::sign(&schemes[0], &namespace, proposal2.clone()).unwrap(); + let our_notarize2 = Notarize::sign(&schemes[0], proposal2.clone()).unwrap(); batcher_mailbox .constructed(Vote::Notarize(our_notarize2)) .await; diff --git a/consensus/src/simplex/actors/batcher/round.rs b/consensus/src/simplex/actors/batcher/round.rs index a96fd3d259..923d41503f 100644 --- a/consensus/src/simplex/actors/batcher/round.rs +++ b/consensus/src/simplex/actors/batcher/round.rs @@ -312,9 +312,8 @@ impl< pub fn verify_notarizes( &mut self, rng: &mut E, - namespace: &[u8], ) -> (Vec>, Vec) { - self.verifier.verify_notarizes(rng, namespace) + self.verifier.verify_notarizes(rng) } pub fn ready_nullifies(&self) -> bool { @@ -328,9 +327,8 @@ impl< pub fn verify_nullifies( &mut self, rng: &mut E, - namespace: &[u8], ) -> (Vec>, Vec) { - self.verifier.verify_nullifies(rng, namespace) + self.verifier.verify_nullifies(rng) } pub fn ready_finalizes(&self) -> bool { @@ -344,9 +342,8 @@ impl< pub fn verify_finalizes( &mut self, rng: &mut E, - namespace: &[u8], ) -> (Vec>, Vec) { - self.verifier.verify_finalizes(rng, namespace) + self.verifier.verify_finalizes(rng) } /// Returns true if the leader was active in this round. diff --git a/consensus/src/simplex/actors/batcher/verifier.rs b/consensus/src/simplex/actors/batcher/verifier.rs index 0c8e9e9d3f..cc0ff0fd4f 100644 --- a/consensus/src/simplex/actors/batcher/verifier.rs +++ b/consensus/src/simplex/actors/batcher/verifier.rs @@ -188,7 +188,6 @@ impl, D: Digest> Verifier { /// # Arguments /// /// * `rng` - Randomness source used by schemes that require batching randomness. - /// * `namespace` - The namespace for signature domain separation. /// /// # Returns /// @@ -198,7 +197,6 @@ impl, D: Digest> Verifier { pub fn verify_notarizes( &mut self, rng: &mut R, - namespace: &[u8], ) -> (Vec>, Vec) { let notarizes = std::mem::take(&mut self.notarizes); @@ -216,7 +214,6 @@ impl, D: Digest> Verifier { let Verification { verified, invalid } = self.scheme.verify_attestations::<_, D, _>( rng, - namespace, Subject::Notarize { proposal }, attestations, ); @@ -283,7 +280,6 @@ impl, D: Digest> Verifier { /// # Arguments /// /// * `rng` - Randomness source used by schemes that require batching randomness. - /// * `namespace` - The namespace for signature domain separation. /// /// # Returns /// @@ -293,7 +289,6 @@ impl, D: Digest> Verifier { pub fn verify_nullifies( &mut self, rng: &mut R, - namespace: &[u8], ) -> (Vec>, Vec) { let nullifies = std::mem::take(&mut self.nullifies); @@ -306,7 +301,6 @@ impl, D: Digest> Verifier { let Verification { verified, invalid } = self.scheme.verify_attestations::<_, D, _>( rng, - namespace, Subject::Nullify { round }, nullifies.into_iter().map(|nullify| nullify.attestation), ); @@ -360,7 +354,6 @@ impl, D: Digest> Verifier { /// # Arguments /// /// * `rng` - Randomness source used by schemes that require batching randomness. - /// * `namespace` - The namespace for signature domain separation. /// /// # Returns /// @@ -370,7 +363,6 @@ impl, D: Digest> Verifier { pub fn verify_finalizes( &mut self, rng: &mut R, - namespace: &[u8], ) -> (Vec>, Vec) { let finalizes = std::mem::take(&mut self.finalizes); @@ -388,7 +380,6 @@ impl, D: Digest> Verifier { let Verification { verified, invalid } = self.scheme.verify_attestations::<_, D, _>( rng, - namespace, Subject::Finalize { proposal }, attestations, ); @@ -462,11 +453,8 @@ mod tests { ed25519::PublicKey, sha256::Digest as Sha256, }; - use commonware_utils::quorum_from_slice; - use rand::{ - rngs::{OsRng, StdRng}, - SeedableRng, - }; + use commonware_utils::{quorum_from_slice, test_rng}; + use rand::rngs::StdRng; const NAMESPACE: &[u8] = b"test"; @@ -483,12 +471,12 @@ mod tests { payload_val: u8, ) -> Notarize { let proposal = Proposal::new(round, parent_view, sample_digest(payload_val)); - Notarize::sign(scheme, NAMESPACE, proposal).unwrap() + Notarize::sign(scheme, proposal).unwrap() } // Helper to create a Nullify message for any signing scheme fn create_nullify>(scheme: &S, round: Round) -> Nullify { - Nullify::sign::(scheme, NAMESPACE, round).unwrap() + Nullify::sign::(scheme, round).unwrap() } // Helper to create a Finalize message for any signing scheme @@ -499,16 +487,16 @@ mod tests { payload_val: u8, ) -> Finalize { let proposal = Proposal::new(round, parent_view, sample_digest(payload_val)); - Finalize::sign(scheme, NAMESPACE, proposal).unwrap() + Finalize::sign(scheme, proposal).unwrap() } fn add_notarize(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(123); - let Fixture { schemes, .. } = fixture(&mut rng, 5); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 5); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); @@ -571,10 +559,10 @@ mod tests { fn set_leader(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(124); - let Fixture { schemes, .. } = fixture(&mut rng, 5); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 5); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); @@ -613,13 +601,12 @@ mod tests { fn ready_and_verify_notarizes(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(125); - let Fixture { schemes, .. } = fixture(&mut rng, 5); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 5); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); - let mut rng = OsRng; let round = Round::new(Epoch::new(0), View::new(1)); let notarizes: Vec<_> = schemes .iter() @@ -642,7 +629,7 @@ mod tests { assert!(verifier.ready_notarizes()); assert_eq!(verifier.notarizes.len(), 4); - let (verified_bulk, failed_bulk) = verifier.verify_notarizes(&mut rng, NAMESPACE); + let (verified_bulk, failed_bulk) = verifier.verify_notarizes(&mut rng); assert_eq!(verified_bulk.len(), 4); assert!(failed_bulk.is_empty()); assert_eq!(verifier.notarizes_verified, 4); @@ -666,7 +653,7 @@ mod tests { } assert!(verifier2.ready_notarizes()); - let (verified_second, failed_second) = verifier2.verify_notarizes(&mut rng, NAMESPACE); + let (verified_second, failed_second) = verifier2.verify_notarizes(&mut rng); assert!(verified_second .iter() .any(|v| matches!(v, Vote::Notarize(ref n) if n == &leader_vote))); @@ -686,10 +673,10 @@ mod tests { fn add_nullify(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(127); - let Fixture { schemes, .. } = fixture(&mut rng, 5); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 5); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); let round = Round::new(Epoch::new(0), View::new(1)); @@ -717,13 +704,12 @@ mod tests { fn ready_and_verify_nullifies(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(128); - let Fixture { schemes, .. } = fixture(&mut rng, 5); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 5); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); - let mut rng = OsRng; let round = Round::new(Epoch::new(0), View::new(1)); let nullifies: Vec<_> = schemes .iter() @@ -742,7 +728,7 @@ mod tests { assert!(verifier.ready_nullifies()); assert_eq!(verifier.nullifies.len(), 3); - let (verified, failed) = verifier.verify_nullifies(&mut rng, NAMESPACE); + let (verified, failed) = verifier.verify_nullifies(&mut rng); assert_eq!(verified.len(), 3); assert!(failed.is_empty()); assert_eq!(verifier.nullifies_verified, 4); @@ -763,10 +749,10 @@ mod tests { fn add_finalize(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(129); - let Fixture { schemes, .. } = fixture(&mut rng, 5); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 5); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); let round = Round::new(Epoch::new(0), View::new(1)); @@ -809,13 +795,12 @@ mod tests { fn ready_and_verify_finalizes(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(130); - let Fixture { schemes, .. } = fixture(&mut rng, 5); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 5); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); - let mut rng = OsRng; let round = Round::new(Epoch::new(0), View::new(1)); let finalizes: Vec<_> = schemes .iter() @@ -839,7 +824,7 @@ mod tests { verifier.add(Vote::Finalize(finalizes[3].clone()), false); assert!(verifier.ready_finalizes()); - let (verified, failed) = verifier.verify_finalizes(&mut rng, NAMESPACE); + let (verified, failed) = verifier.verify_finalizes(&mut rng); assert_eq!(verified.len(), 3); assert!(failed.is_empty()); assert_eq!(verifier.finalizes_verified, 4); @@ -860,20 +845,20 @@ mod tests { fn leader_proposal_filters_messages(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(201); - let Fixture { schemes, .. } = fixture(&mut rng, 3); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 3); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); let round = Round::new(Epoch::new(0), View::new(1)); let proposal_a = Proposal::new(round, View::new(0), sample_digest(10)); let proposal_b = Proposal::new(round, View::new(0), sample_digest(20)); - let notarize_a = Notarize::sign(&schemes[0], NAMESPACE, proposal_a.clone()).unwrap(); - let notarize_b = Notarize::sign(&schemes[1], NAMESPACE, proposal_b.clone()).unwrap(); - let finalize_a = Finalize::sign(&schemes[0], NAMESPACE, proposal_a.clone()).unwrap(); - let finalize_b = Finalize::sign(&schemes[1], NAMESPACE, proposal_b).unwrap(); + let notarize_a = Notarize::sign(&schemes[0], proposal_a.clone()).unwrap(); + let notarize_b = Notarize::sign(&schemes[1], proposal_b.clone()).unwrap(); + let finalize_a = Finalize::sign(&schemes[0], proposal_a.clone()).unwrap(); + let finalize_b = Finalize::sign(&schemes[1], proposal_b).unwrap(); verifier.add(Vote::Notarize(notarize_a.clone()), false); verifier.add(Vote::Notarize(notarize_b), false); @@ -904,10 +889,10 @@ mod tests { fn set_leader_twice_panics(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(212); - let Fixture { schemes, .. } = fixture(&mut rng, 3); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 3); let mut verifier = Verifier::::new(schemes[0].clone(), 3); verifier.set_leader(0); verifier.set_leader(1); @@ -952,13 +937,12 @@ mod tests { fn notarizes_wait_for_quorum(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(203); - let Fixture { schemes, .. } = fixture(&mut rng, 5); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 5); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); - let mut rng = OsRng; let round = Round::new(Epoch::new(0), View::new(1)); let leader_vote = create_notarize(&schemes[0], round, View::new(0), 1); @@ -979,7 +963,7 @@ mod tests { } assert!(verifier.ready_notarizes(), "Should be ready at quorum"); - let (verified, _) = verifier.verify_notarizes(&mut rng, NAMESPACE); + let (verified, _) = verifier.verify_notarizes(&mut rng); assert_eq!(verified.len(), quorum as usize); assert!(!verifier.ready_notarizes()); } @@ -997,10 +981,10 @@ mod tests { fn ready_notarizes_without_leader(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(204); - let Fixture { schemes, .. } = fixture(&mut rng, 3); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 3); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); let round = Round::new(Epoch::new(0), View::new(1)); @@ -1040,10 +1024,10 @@ mod tests { fn ready_finalizes_without_leader(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(205); - let Fixture { schemes, .. } = fixture(&mut rng, 3); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 3); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); let round = Round::new(Epoch::new(0), View::new(1)); @@ -1082,10 +1066,10 @@ mod tests { fn verify_notarizes_empty(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(206); - let Fixture { schemes, .. } = fixture(&mut rng, 3); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 3); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); let round = Round::new(Epoch::new(0), View::new(1)); @@ -1108,16 +1092,15 @@ mod tests { fn verify_nullifies_empty(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(207); - let Fixture { schemes, .. } = fixture(&mut rng, 3); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 3); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); - let mut rng = OsRng; assert!(verifier.nullifies.is_empty()); assert!(!verifier.ready_nullifies()); - let (verified, failed) = verifier.verify_nullifies(&mut rng, NAMESPACE); + let (verified, failed) = verifier.verify_nullifies(&mut rng); assert!(verified.is_empty()); assert!(failed.is_empty()); assert_eq!(verifier.nullifies_verified, 0); @@ -1136,17 +1119,16 @@ mod tests { fn verify_finalizes_empty(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(208); - let Fixture { schemes, .. } = fixture(&mut rng, 3); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 3); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); - let mut rng = OsRng; verifier.set_leader(0); assert!(verifier.finalizes.is_empty()); assert!(!verifier.ready_finalizes()); - let (verified, failed) = verifier.verify_finalizes(&mut rng, NAMESPACE); + let (verified, failed) = verifier.verify_finalizes(&mut rng); assert!(verified.is_empty()); assert!(failed.is_empty()); assert_eq!(verifier.finalizes_verified, 0); @@ -1165,13 +1147,12 @@ mod tests { fn ready_notarizes_exact_quorum(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(209); - let Fixture { schemes, .. } = fixture(&mut rng, 5); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 5); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); - let mut rng = OsRng; let round = Round::new(Epoch::new(0), View::new(1)); let leader_vote = create_notarize(&schemes[0], round, View::new(0), 1); @@ -1199,7 +1180,7 @@ mod tests { } } - let (verified, failed) = verifier.verify_notarizes(&mut rng, NAMESPACE); + let (verified, failed) = verifier.verify_notarizes(&mut rng); assert_eq!(verified.len(), quorum as usize - 1); assert!(failed.is_empty()); assert_eq!(verifier.notarizes_verified, quorum as usize); @@ -1219,10 +1200,10 @@ mod tests { fn ready_nullifies_exact_quorum(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(210); - let Fixture { schemes, .. } = fixture(&mut rng, 5); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 5); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); let round = Round::new(Epoch::new(0), View::new(1)); @@ -1259,10 +1240,10 @@ mod tests { fn ready_finalizes_exact_quorum(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(211); - let Fixture { schemes, .. } = fixture(&mut rng, 5); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 5); let quorum = quorum_from_slice(&schemes); let mut verifier = Verifier::::new(schemes[0].clone(), quorum); let round = Round::new(Epoch::new(0), View::new(1)); @@ -1304,10 +1285,10 @@ mod tests { fn ready_notarizes_quorum_already_met_by_verified(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(212); - let Fixture { schemes, .. } = fixture(&mut rng, 5); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 5); let quorum = quorum_from_slice(&schemes); assert!( schemes.len() > quorum as usize, @@ -1356,10 +1337,10 @@ mod tests { fn ready_nullifies_quorum_already_met_by_verified(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(213); - let Fixture { schemes, .. } = fixture(&mut rng, 5); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 5); let quorum = quorum_from_slice(&schemes); assert!( schemes.len() > quorum as usize, @@ -1400,10 +1381,10 @@ mod tests { fn ready_finalizes_quorum_already_met_by_verified(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, { - let mut rng = StdRng::seed_from_u64(214); - let Fixture { schemes, .. } = fixture(&mut rng, 5); + let mut rng = test_rng(); + let Fixture { schemes, .. } = fixture(&mut rng, NAMESPACE, 5); let quorum = quorum_from_slice(&schemes); assert!( schemes.len() > quorum as usize, diff --git a/consensus/src/simplex/actors/resolver/actor.rs b/consensus/src/simplex/actors/resolver/actor.rs index 94c470aa4e..24eed2d85d 100644 --- a/consensus/src/simplex/actors/resolver/actor.rs +++ b/consensus/src/simplex/actors/resolver/actor.rs @@ -36,7 +36,6 @@ pub struct Actor< blocker: Option, epoch: Epoch, - namespace: Vec, mailbox_size: usize, fetch_timeout: Duration, @@ -61,7 +60,6 @@ impl< blocker: Some(cfg.blocker), epoch: cfg.epoch, - namespace: cfg.namespace, mailbox_size: cfg.mailbox_size, fetch_timeout: cfg.fetch_timeout, @@ -176,7 +174,7 @@ impl< ); return None; } - if !notarization.verify(&mut self.context, &self.scheme, &self.namespace) { + if !notarization.verify(&mut self.context, &self.scheme) { debug!(%view, "notarization failed verification"); return None; } @@ -196,7 +194,7 @@ impl< ); return None; } - if !finalization.verify(&mut self.context, &self.scheme, &self.namespace) { + if !finalization.verify(&mut self.context, &self.scheme) { debug!(%view, "finalization failed verification"); return None; } @@ -216,7 +214,7 @@ impl< ); return None; } - if !nullification.verify::<_, D>(&mut self.context, &self.scheme, &self.namespace) { + if !nullification.verify::<_, D>(&mut self.context, &self.scheme) { debug!(%view, "nullification failed verification"); return None; } diff --git a/consensus/src/simplex/actors/resolver/mod.rs b/consensus/src/simplex/actors/resolver/mod.rs index 5ecbc0d654..01dfce0f0e 100644 --- a/consensus/src/simplex/actors/resolver/mod.rs +++ b/consensus/src/simplex/actors/resolver/mod.rs @@ -17,7 +17,6 @@ pub struct Config { pub blocker: B, pub epoch: Epoch, - pub namespace: Vec, pub mailbox_size: usize, pub fetch_concurrent: usize, pub fetch_timeout: Duration, diff --git a/consensus/src/simplex/actors/resolver/state.rs b/consensus/src/simplex/actors/resolver/state.rs index 2daba18d37..943378d3a5 100644 --- a/consensus/src/simplex/actors/resolver/state.rs +++ b/consensus/src/simplex/actors/resolver/state.rs @@ -298,7 +298,7 @@ mod tests { let mut rng = StdRng::seed_from_u64(42); let Fixture { schemes, verifier, .. - } = ed25519::fixture(&mut rng, 5); + } = ed25519::fixture(&mut rng, NAMESPACE, 5); (schemes, verifier) } @@ -310,7 +310,7 @@ mod tests { let round = Round::new(EPOCH, view); let votes: Vec<_> = schemes .iter() - .map(|scheme| Nullify::sign::(scheme, NAMESPACE, round).unwrap()) + .map(|scheme| Nullify::sign::(scheme, round).unwrap()) .collect(); Nullification::from_nullifies(verifier, &votes).expect("nullification quorum") } @@ -327,7 +327,7 @@ mod tests { ); let votes: Vec<_> = schemes .iter() - .map(|scheme| Notarize::sign(scheme, NAMESPACE, proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap()) .collect(); Notarization::from_notarizes(verifier, &votes).expect("notarization quorum") } @@ -344,7 +344,7 @@ mod tests { ); let votes: Vec<_> = schemes .iter() - .map(|scheme| Finalize::sign(scheme, NAMESPACE, proposal.clone()).unwrap()) + .map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap()) .collect(); Finalization::from_finalizes(verifier, &votes).expect("finalization quorum") } diff --git a/consensus/src/simplex/actors/voter/actor.rs b/consensus/src/simplex/actors/voter/actor.rs index 0e5ad75369..d8c64f1faf 100644 --- a/consensus/src/simplex/actors/voter/actor.rs +++ b/consensus/src/simplex/actors/voter/actor.rs @@ -169,7 +169,6 @@ impl< StateConfig { scheme: cfg.scheme, elector: cfg.elector, - namespace: cfg.namespace.clone(), epoch: cfg.epoch, activity_timeout: cfg.activity_timeout, leader_timeout: cfg.leader_timeout, diff --git a/consensus/src/simplex/actors/voter/mod.rs b/consensus/src/simplex/actors/voter/mod.rs index f5eae32da5..60bdafbd60 100644 --- a/consensus/src/simplex/actors/voter/mod.rs +++ b/consensus/src/simplex/actors/voter/mod.rs @@ -36,7 +36,6 @@ pub struct Config< pub partition: String, pub epoch: Epoch, - pub namespace: Vec, pub mailbox_size: usize, pub leader_timeout: Duration, pub notarization_timeout: Duration, @@ -86,7 +85,6 @@ mod tests { fn build_notarization>( schemes: &[S], - namespace: &[u8], proposal: &Proposal, count: u32, ) -> ( @@ -96,7 +94,7 @@ mod tests { let votes: Vec<_> = schemes .iter() .take(count as usize) - .map(|scheme| Notarize::sign(scheme, namespace, proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap()) .collect(); let certificate = Notarization::from_notarizes(&schemes[0], &votes) .expect("notarization requires a quorum of votes"); @@ -105,7 +103,6 @@ mod tests { fn build_finalization>( schemes: &[S], - namespace: &[u8], proposal: &Proposal, count: u32, ) -> ( @@ -115,7 +112,7 @@ mod tests { let votes: Vec<_> = schemes .iter() .take(count as usize) - .map(|scheme| Finalize::sign(scheme, namespace, proposal.clone()).unwrap()) + .map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap()) .collect(); let certificate = Finalization::from_finalizes(&schemes[0], &votes) .expect("finalization requires a quorum of votes"); @@ -131,7 +128,7 @@ mod tests { fn stale_backfill(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: ElectorConfig, { let n = 5; @@ -155,13 +152,12 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Initialize voter actor let me = participants[0].clone(); let elector = L::default(); let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[0].clone(), elector: elector.clone(), @@ -192,7 +188,6 @@ mod tests { reporter: reporter.clone(), partition: "test".to_string(), epoch: Epoch::new(333), - namespace: namespace.clone(), mailbox_size: 10, leader_timeout: Duration::from_secs(5), notarization_timeout: Duration::from_secs(5), @@ -250,7 +245,7 @@ mod tests { View::new(50), payload, ); - let (_, finalization) = build_finalization(&schemes, &namespace, &proposal, quorum); + let (_, finalization) = build_finalization(&schemes, &proposal, quorum); mailbox .recovered(Certificate::Finalization(finalization)) .await; @@ -295,7 +290,7 @@ mod tests { View::new(49), payload, ); - let (_, notarization) = build_notarization(&schemes, &namespace, &proposal, quorum); + let (_, notarization) = build_notarization(&schemes, &proposal, quorum); mailbox .recovered(Certificate::Notarization(notarization)) .await; @@ -307,7 +302,7 @@ mod tests { View::new(100), payload, ); - let (_, finalization) = build_finalization(&schemes, &namespace, &proposal, quorum); + let (_, finalization) = build_finalization(&schemes, &proposal, quorum); mailbox .recovered(Certificate::Finalization(finalization)) .await; @@ -369,7 +364,7 @@ mod tests { fn append_old_interesting_view(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: ElectorConfig, { let n = 5; @@ -394,14 +389,13 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Setup the target Voter actor (validator 0) let signing = schemes[0].clone(); let me = participants[0].clone(); let elector = L::default(); let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: signing.clone(), elector: elector.clone(), @@ -430,7 +424,6 @@ mod tests { reporter: reporter.clone(), partition: format!("voter_actor_test_{me}"), epoch: Epoch::new(333), - namespace: namespace.clone(), mailbox_size: 128, leader_timeout: Duration::from_millis(500), notarization_timeout: Duration::from_millis(1000), @@ -501,7 +494,7 @@ mod tests { lf_target.previous().unwrap(), Sha256::hash(b"test"), ); - let (_, finalization) = build_finalization(&schemes, &namespace, &proposal_lf, quorum); + let (_, finalization) = build_finalization(&schemes, &proposal_lf, quorum); mailbox .recovered(Certificate::Finalization(finalization)) .await; @@ -545,8 +538,7 @@ mod tests { journal_floor_target.previous().unwrap(), Sha256::hash(b"test2"), ); - let (_, notarization_for_floor) = - build_notarization(&schemes, &namespace, &proposal_jft, quorum); + let (_, notarization_for_floor) = build_notarization(&schemes, &proposal_jft, quorum); mailbox .recovered(Certificate::Notarization(notarization_for_floor)) .await; @@ -573,8 +565,7 @@ mod tests { problematic_view.previous().unwrap(), Sha256::hash(b"test3"), ); - let (_, notarization_for_bft) = - build_notarization(&schemes, &namespace, &proposal_bft, quorum); + let (_, notarization_for_bft) = build_notarization(&schemes, &proposal_bft, quorum); mailbox .recovered(Certificate::Notarization(notarization_for_bft)) .await; @@ -597,7 +588,7 @@ mod tests { View::new(99), Sha256::hash(b"test4"), ); - let (_, finalization) = build_finalization(&schemes, &namespace, &proposal_lf, quorum); + let (_, finalization) = build_finalization(&schemes, &proposal_lf, quorum); mailbox .recovered(Certificate::Finalization(finalization)) .await; @@ -651,7 +642,7 @@ mod tests { fn finalization_without_notarization_certificate(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: ElectorConfig, { let n = 5; @@ -675,12 +666,11 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Setup application mock let elector = L::default(); let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[0].clone(), elector: elector.clone(), @@ -711,7 +701,6 @@ mod tests { reporter: reporter.clone(), partition: "voter_finalization_test".to_string(), epoch: Epoch::new(333), - namespace: namespace.clone(), mailbox_size: 128, leader_timeout: Duration::from_millis(500), notarization_timeout: Duration::from_secs(1000), @@ -773,8 +762,7 @@ mod tests { view.previous().unwrap(), Sha256::hash(b"finalize_without_notarization"), ); - let (_, expected_finalization) = - build_finalization(&schemes, &namespace, &proposal, quorum); + let (_, expected_finalization) = build_finalization(&schemes, &proposal, quorum); // Send finalization certificate via voter mailbox mailbox @@ -836,7 +824,7 @@ mod tests { fn certificate_conflicts_proposal(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: ElectorConfig, { let n = 5; @@ -860,12 +848,11 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Setup application mock let elector = L::default(); let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[0].clone(), elector: elector.clone(), @@ -896,7 +883,6 @@ mod tests { reporter: reporter.clone(), partition: "voter_certificate_conflicts_proposal_test".to_string(), epoch: Epoch::new(333), - namespace: namespace.clone(), mailbox_size: 128, leader_timeout: Duration::from_millis(500), notarization_timeout: Duration::from_secs(1000), @@ -969,7 +955,7 @@ mod tests { view.previous().unwrap(), Sha256::hash(b"proposal_b"), ); - let (_, notarization_b) = build_notarization(&schemes, &namespace, &proposal_b, quorum); + let (_, notarization_b) = build_notarization(&schemes, &proposal_b, quorum); mailbox .recovered(Certificate::Notarization(notarization_b.clone())) @@ -1034,7 +1020,7 @@ mod tests { fn proposal_conflicts_certificate(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: ElectorConfig, { let n = 5; @@ -1056,11 +1042,10 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let elector = L::default(); let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[0].clone(), elector: elector.clone(), @@ -1090,7 +1075,6 @@ mod tests { reporter: reporter.clone(), partition: "voter_proposal_conflicts_certificate_test".to_string(), epoch: Epoch::new(333), - namespace: namespace.clone(), mailbox_size: 128, leader_timeout: Duration::from_millis(500), notarization_timeout: Duration::from_secs(1000), @@ -1148,7 +1132,7 @@ mod tests { ); // Send certificate for proposal A FIRST - let (_, notarization_a) = build_notarization(&schemes, &namespace, &proposal_a, quorum); + let (_, notarization_a) = build_notarization(&schemes, &proposal_a, quorum); mailbox .recovered(Certificate::Notarization(notarization_a.clone())) .await; @@ -1215,7 +1199,7 @@ mod tests { fn certificate_verifies_proposal(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: ElectorConfig, { let n = 5; @@ -1237,11 +1221,10 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let elector = L::default(); let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[0].clone(), elector: elector.clone(), @@ -1271,7 +1254,6 @@ mod tests { reporter: reporter.clone(), partition: "voter_certificate_verifies_proposal_test".to_string(), epoch: Epoch::new(333), - namespace: namespace.clone(), mailbox_size: 128, leader_timeout: Duration::from_millis(500), notarization_timeout: Duration::from_secs(1000), @@ -1330,7 +1312,7 @@ mod tests { context.sleep(Duration::from_millis(10)).await; // Send certificate for the SAME proposal - let (_, notarization) = build_notarization(&schemes, &namespace, &proposal, quorum); + let (_, notarization) = build_notarization(&schemes, &proposal, quorum); mailbox .recovered(Certificate::Notarization(notarization.clone())) .await; @@ -1395,7 +1377,7 @@ mod tests { fn drop_our_proposal_on_conflict(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, { let n = 5; let quorum = quorum(n); @@ -1420,7 +1402,7 @@ mod tests { schemes, verifier: _, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Figure out who the leader will be for view 2 let view2_round = Round::new(epoch, View::new(2)); @@ -1450,7 +1432,6 @@ mod tests { actor.start(); let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: leader_scheme.clone(), elector: elector_config.clone(), @@ -1468,7 +1449,6 @@ mod tests { reporter: reporter.clone(), partition: "voter_leader".to_string(), epoch, - namespace: namespace.clone(), mailbox_size: 128, leader_timeout: Duration::from_millis(500), notarization_timeout: Duration::from_secs(1000), @@ -1527,8 +1507,7 @@ mod tests { let view1_proposal = Proposal::new(view1_round, View::new(0), Sha256::hash(b"view1_payload")); - let (_, finalization) = - build_finalization(&schemes, &namespace, &view1_proposal, quorum); + let (_, finalization) = build_finalization(&schemes, &view1_proposal, quorum); mailbox .recovered(Certificate::Finalization(finalization)) .await; @@ -1581,7 +1560,7 @@ mod tests { // Add a notarization certificate for conflicting proposal let (_, conflicting_notarization) = - build_notarization(&schemes, &namespace, &conflicting_proposal, quorum); + build_notarization(&schemes, &conflicting_proposal, quorum); mailbox .recovered(Certificate::Notarization(conflicting_notarization.clone())) .await; @@ -1616,7 +1595,7 @@ mod tests { fn populate_resolver_on_restart(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: ElectorConfig, { let n = 5; @@ -1640,12 +1619,11 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Setup application mock let elector = L::default(); let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[0].clone(), elector: elector.clone(), @@ -1676,7 +1654,6 @@ mod tests { reporter: reporter.clone(), partition: "voter_populate_resolver_on_restart_test".to_string(), epoch: Epoch::new(333), - namespace: namespace.clone(), mailbox_size: 128, leader_timeout: Duration::from_millis(500), notarization_timeout: Duration::from_secs(1000), @@ -1738,8 +1715,7 @@ mod tests { view.previous().unwrap(), Sha256::hash(b"finalize_without_notarization"), ); - let (_, expected_finalization) = - build_finalization(&schemes, &namespace, &proposal, quorum); + let (_, expected_finalization) = build_finalization(&schemes, &proposal, quorum); // Send finalization certificate via voter mailbox mailbox @@ -1768,7 +1744,6 @@ mod tests { reporter: reporter.clone(), partition: "voter_populate_resolver_on_restart_test".to_string(), epoch: Epoch::new(333), - namespace: namespace.clone(), mailbox_size: 128, leader_timeout: Duration::from_millis(500), notarization_timeout: Duration::from_secs(1000), @@ -1847,7 +1822,7 @@ mod tests { fn finalization_from_resolver(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: ElectorConfig, { // This is a regression test as the resolver didn't use to send @@ -1873,12 +1848,11 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Setup application mock let elector = L::default(); let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[0].clone(), elector: elector.clone(), @@ -1909,7 +1883,6 @@ mod tests { reporter: reporter.clone(), partition: "finalization_from_resolver".to_string(), epoch: Epoch::new(333), - namespace: namespace.clone(), mailbox_size: 128, leader_timeout: Duration::from_millis(500), notarization_timeout: Duration::from_secs(1000), @@ -1971,7 +1944,7 @@ mod tests { view.previous().unwrap(), Sha256::hash(b"finalization_from_resolver"), ); - let (_, finalization) = build_finalization(&schemes, &namespace, &proposal, quorum); + let (_, finalization) = build_finalization(&schemes, &proposal, quorum); mailbox .recovered(Certificate::Finalization(finalization.clone())) .await; @@ -2013,7 +1986,7 @@ mod tests { fn no_resolver_boomerang(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: ElectorConfig, { let n = 5; @@ -2037,12 +2010,11 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Setup application mock let elector = L::default(); let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[0].clone(), elector: elector.clone(), @@ -2073,7 +2045,6 @@ mod tests { reporter: reporter.clone(), partition: "no_resolver_boomerang".to_string(), epoch: Epoch::new(333), - namespace: namespace.clone(), mailbox_size: 128, leader_timeout: Duration::from_millis(500), notarization_timeout: Duration::from_secs(1000), @@ -2135,7 +2106,7 @@ mod tests { view.previous().unwrap(), Sha256::hash(b"no_resolver_boomerang"), ); - let (_, finalization) = build_finalization(&schemes, &namespace, &proposal, quorum); + let (_, finalization) = build_finalization(&schemes, &proposal, quorum); mailbox .resolved(Certificate::Finalization(finalization.clone())) .await; @@ -2188,7 +2159,7 @@ mod tests { fn verification_failure_emits_nullify_immediately(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: ElectorConfig, { let n = 5; @@ -2213,14 +2184,13 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Use participant[0] as the voter let signing = schemes[0].clone(); let me = participants[0].clone(); let elector = L::default(); let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: signing.clone(), elector: elector.clone(), @@ -2254,7 +2224,6 @@ mod tests { reporter: reporter.clone(), partition: format!("voter_verify_fail_test_{me}"), epoch: Epoch::new(333), - namespace: namespace.clone(), mailbox_size: 128, // Use long timeouts to prove nullify comes immediately, not from timeout leader_timeout: Duration::from_secs(10), @@ -2306,7 +2275,7 @@ mod tests { let (target_view, leader) = loop { // Send finalization to advance to next view let (_, finalization) = - build_finalization(&schemes, &namespace, &prev_proposal, quorum); + build_finalization(&schemes, &prev_proposal, quorum); mailbox .resolved(Certificate::Finalization(finalization)) .await; @@ -2415,7 +2384,7 @@ mod tests { fn no_recertification_after_replay(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: ElectorConfig, { let n = 5; @@ -2437,7 +2406,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); // Track certify calls across restarts let certify_calls: Arc>> = Arc::new(Mutex::new(Vec::new())); @@ -2445,7 +2414,6 @@ mod tests { let elector = L::default(); let reporter_cfg = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[0].clone(), elector: elector.clone(), @@ -2481,7 +2449,6 @@ mod tests { reporter: reporter.clone(), partition: "no_recertification_after_replay".to_string(), epoch: Epoch::new(333), - namespace: namespace.clone(), mailbox_size: 128, leader_timeout: Duration::from_millis(500), notarization_timeout: Duration::from_secs(1000), @@ -2526,7 +2493,7 @@ mod tests { View::new(1), Sha256::hash(b"finalized_payload"), ); - let (_, finalization) = build_finalization(&schemes, &namespace, &proposal2, quorum); + let (_, finalization) = build_finalization(&schemes, &proposal2, quorum); mailbox .recovered(Certificate::Finalization(finalization)) .await; @@ -2561,7 +2528,7 @@ mod tests { mailbox.proposal(proposal3.clone()).await; // Send notarization - let (_, notarization) = build_notarization(&schemes, &namespace, &proposal3, quorum); + let (_, notarization) = build_notarization(&schemes, &proposal3, quorum); mailbox .recovered(Certificate::Notarization(notarization)) .await; @@ -2615,7 +2582,6 @@ mod tests { reporter: reporter.clone(), partition: "no_recertification_after_replay".to_string(), epoch: Epoch::new(333), - namespace: namespace.clone(), mailbox_size: 128, leader_timeout: Duration::from_millis(500), notarization_timeout: Duration::from_secs(1000), diff --git a/consensus/src/simplex/actors/voter/round.rs b/consensus/src/simplex/actors/voter/round.rs index 1046a40bf2..f09e850677 100644 --- a/consensus/src/simplex/actors/voter/round.rs +++ b/consensus/src/simplex/actors/voter/round.rs @@ -550,7 +550,7 @@ mod tests { participants, verifier, .. - } = ed25519::fixture(&mut rng, 4); + } = ed25519::fixture(&mut rng, namespace, 4); let proposal_a = Proposal::new( Rnd::new(Epoch::new(1), View::new(1)), View::new(0), @@ -577,7 +577,7 @@ mod tests { let notarization_votes: Vec<_> = schemes .iter() .skip(1) - .map(|scheme| Notarize::sign(scheme, namespace, proposal_b.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal_b.clone()).unwrap()) .collect(); let certificate = Notarization::from_notarizes(&verifier, notarization_votes.iter()).unwrap(); @@ -603,7 +603,7 @@ mod tests { participants, verifier, .. - } = ed25519::fixture(&mut rng, 4); + } = ed25519::fixture(&mut rng, namespace, 4); let proposal_a = Proposal::new( Rnd::new(Epoch::new(1), View::new(1)), View::new(0), @@ -630,7 +630,7 @@ mod tests { let finalization_votes: Vec<_> = schemes .iter() .skip(1) - .map(|scheme| Finalize::sign(scheme, namespace, proposal_b.clone()).unwrap()) + .map(|scheme| Finalize::sign(scheme, proposal_b.clone()).unwrap()) .collect(); let certificate = Finalization::from_finalizes(&verifier, finalization_votes.iter()).unwrap(); @@ -644,7 +644,7 @@ mod tests { let notarization_votes: Vec<_> = schemes .iter() .skip(1) - .map(|scheme| Notarize::sign(scheme, namespace, proposal_b.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal_b.clone()).unwrap()) .collect(); let certificate = Notarization::from_notarizes(&verifier, notarization_votes.iter()).unwrap(); @@ -666,7 +666,7 @@ mod tests { let namespace = b"ns"; let Fixture { schemes, verifier, .. - } = ed25519::fixture(&mut rng, 4); + } = ed25519::fixture(&mut rng, namespace, 4); let proposal = Proposal::new( Rnd::new(Epoch::new(1), View::new(1)), View::new(0), @@ -682,7 +682,7 @@ mod tests { // Add matching notarization certificate let notarization_votes: Vec<_> = schemes .iter() - .map(|scheme| Notarize::sign(scheme, namespace, proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap()) .collect(); let certificate = Notarization::from_notarizes(&verifier, notarization_votes.iter()).unwrap(); @@ -694,10 +694,10 @@ mod tests { #[test] fn replay_message_sets_broadcast_flags() { let mut rng = StdRng::seed_from_u64(2029); + let namespace = b"ns"; let Fixture { schemes, verifier, .. - } = ed25519::fixture(&mut rng, 4); - let namespace = b"ns"; + } = ed25519::fixture(&mut rng, namespace, 4); let local_scheme = schemes[0].clone(); // Setup round and proposal @@ -707,31 +707,28 @@ mod tests { let proposal = Proposal::new(round, View::new(0), Sha256Digest::from([40u8; 32])); // Create notarization - let notarize_local = - Notarize::sign(&local_scheme, namespace, proposal.clone()).expect("notarize"); + let notarize_local = Notarize::sign(&local_scheme, proposal.clone()).expect("notarize"); let notarize_votes: Vec<_> = schemes .iter() - .map(|scheme| Notarize::sign(scheme, namespace, proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap()) .collect(); let notarization = Notarization::from_notarizes(&verifier, notarize_votes.iter()).expect("notarization"); // Create nullification - let nullify_local = - Nullify::sign::(&local_scheme, namespace, round).expect("nullify"); + let nullify_local = Nullify::sign::(&local_scheme, round).expect("nullify"); let nullify_votes: Vec<_> = schemes .iter() - .map(|scheme| Nullify::sign::(scheme, namespace, round).expect("nullify")) + .map(|scheme| Nullify::sign::(scheme, round).expect("nullify")) .collect(); let nullification = Nullification::from_nullifies(&verifier, &nullify_votes).expect("nullification"); // Create finalize - let finalize_local = - Finalize::sign(&local_scheme, namespace, proposal.clone()).expect("finalize"); + let finalize_local = Finalize::sign(&local_scheme, proposal.clone()).expect("finalize"); let finalize_votes: Vec<_> = schemes .iter() - .map(|scheme| Finalize::sign(scheme, namespace, proposal.clone()).unwrap()) + .map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap()) .collect(); let finalization = Finalization::from_finalizes(&verifier, finalize_votes.iter()).expect("finalization"); @@ -764,8 +761,8 @@ mod tests { #[test] fn construct_nullify_blocked_by_finalize() { let mut rng = StdRng::seed_from_u64(2029); - let Fixture { schemes, .. } = ed25519::fixture(&mut rng, 4); let namespace = b"ns"; + let Fixture { schemes, .. } = ed25519::fixture(&mut rng, namespace, 4); let local_scheme = schemes[0].clone(); // Setup round and proposal @@ -775,7 +772,7 @@ mod tests { let proposal = Proposal::new(round_info, View::new(0), Sha256Digest::from([40u8; 32])); // Create finalized vote - let finalize_local = Finalize::sign(&local_scheme, namespace, proposal).expect("finalize"); + let finalize_local = Finalize::sign(&local_scheme, proposal).expect("finalize"); // Replay finalize and verify nullify is blocked let mut round = Round::new(local_scheme, round_info, now); diff --git a/consensus/src/simplex/actors/voter/state.rs b/consensus/src/simplex/actors/voter/state.rs index 02655a95a0..da38847c80 100644 --- a/consensus/src/simplex/actors/voter/state.rs +++ b/consensus/src/simplex/actors/voter/state.rs @@ -32,7 +32,6 @@ const GENESIS_VIEW: View = View::zero(); pub struct Config> { pub scheme: S, pub elector: L, - pub namespace: Vec, pub epoch: Epoch, pub activity_timeout: ViewDelta, pub leader_timeout: Duration, @@ -48,7 +47,6 @@ pub struct State, L: ElectorCon context: E, scheme: S, elector: L::Elector, - namespace: Vec, epoch: Epoch, activity_timeout: ViewDelta, leader_timeout: Duration, @@ -82,7 +80,6 @@ impl, L: ElectorConfig, D: D context, scheme: cfg.scheme, elector, - namespace: cfg.namespace, epoch: cfg.epoch, activity_timeout: cfg.activity_timeout, leader_timeout: cfg.leader_timeout, @@ -201,7 +198,7 @@ impl, L: ElectorConfig, D: D let Some(retry) = self.create_round(view).construct_nullify() else { return (false, None, None); }; - let nullify = Nullify::sign::(&self.scheme, &self.namespace, Rnd::new(self.epoch, view)); + let nullify = Nullify::sign::(&self.scheme, Rnd::new(self.epoch, view)); // If was retry, we need to get entry certificates for the previous view let entry_view = view.previous().unwrap_or(GENESIS_VIEW); @@ -273,7 +270,7 @@ impl, L: ElectorConfig, D: D // Signing can only fail if we are a verifier, so we don't need to worry about // unwinding our broadcast toggle. - Notarize::sign(&self.scheme, &self.namespace, candidate) + Notarize::sign(&self.scheme, candidate) } /// Construct a finalize vote if the round provides a candidate. @@ -285,7 +282,7 @@ impl, L: ElectorConfig, D: D // Signing can only fail if we are a verifier, so we don't need to worry about // unwinding our broadcast toggle. - Finalize::sign(&self.scheme, &self.namespace, candidate) + Finalize::sign(&self.scheme, candidate) } /// Construct a notarization certificate once the round has quorum. @@ -609,16 +606,15 @@ mod tests { fn certificate_candidates_respect_force_flag() { let runtime = deterministic::Runner::default(); runtime.start(|mut context| async move { + let namespace = b"ns".to_vec(); let Fixture { schemes, verifier, .. - } = ed25519::fixture(&mut context, 4); - let namespace = b"ns".to_vec(); + } = ed25519::fixture(&mut context, &namespace, 4); let mut state = State::new( context, Config { scheme: verifier.clone(), elector: ::default(), - namespace: namespace.clone(), epoch: Epoch::new(11), activity_timeout: ViewDelta::new(6), leader_timeout: Duration::from_secs(1), @@ -635,9 +631,7 @@ mod tests { Proposal::new(notarize_round, GENESIS_VIEW, Sha256Digest::from([50u8; 32])); let notarize_votes: Vec<_> = schemes .iter() - .map(|scheme| { - Notarize::sign(scheme, &namespace, notarize_proposal.clone()).unwrap() - }) + .map(|scheme| Notarize::sign(scheme, notarize_proposal.clone()).unwrap()) .collect(); let notarization = Notarization::from_notarizes(&verifier, notarize_votes.iter()) .expect("notarization"); @@ -654,8 +648,7 @@ mod tests { let nullify_votes: Vec<_> = schemes .iter() .map(|scheme| { - Nullify::sign::(scheme, &namespace, nullify_round) - .expect("nullify") + Nullify::sign::(scheme, nullify_round).expect("nullify") }) .collect(); let nullification = @@ -674,9 +667,7 @@ mod tests { Proposal::new(finalize_round, GENESIS_VIEW, Sha256Digest::from([51u8; 32])); let finalize_votes: Vec<_> = schemes .iter() - .map(|scheme| { - Finalize::sign(scheme, &namespace, finalize_proposal.clone()).unwrap() - }) + .map(|scheme| Finalize::sign(scheme, finalize_proposal.clone()).unwrap()) .collect(); let finalization = Finalization::from_finalizes(&verifier, finalize_votes.iter()) .expect("finalization"); @@ -693,14 +684,13 @@ mod tests { fn timeout_helpers_reuse_and_reset_deadlines() { let runtime = deterministic::Runner::default(); runtime.start(|mut context| async move { - let Fixture { schemes, .. } = ed25519::fixture(&mut context, 4); let namespace = b"ns".to_vec(); + let Fixture { schemes, .. } = ed25519::fixture(&mut context, &namespace, 4); let local_scheme = schemes[0].clone(); // leader of view 1 let retry = Duration::from_secs(3); let cfg = Config { scheme: local_scheme.clone(), elector: ::default(), - namespace: namespace.clone(), epoch: Epoch::new(4), activity_timeout: ViewDelta::new(2), leader_timeout: Duration::from_secs(1), @@ -754,11 +744,10 @@ mod tests { let namespace = b"ns".to_vec(); let Fixture { schemes, verifier, .. - } = ed25519::fixture(&mut context, 4); + } = ed25519::fixture(&mut context, &namespace, 4); let cfg = Config { scheme: schemes[0].clone(), elector: ::default(), - namespace: namespace.clone(), epoch: Epoch::new(7), activity_timeout: ViewDelta::new(10), leader_timeout: Duration::from_secs(1), @@ -781,7 +770,7 @@ mod tests { ); let finalization_votes: Vec<_> = schemes .iter() - .map(|scheme| Finalize::sign(scheme, &namespace, proposal_a.clone()).unwrap()) + .map(|scheme| Finalize::sign(scheme, proposal_a.clone()).unwrap()) .collect(); let finalization = Finalization::from_finalizes(&verifier, finalization_votes.iter()) .expect("finalization"); @@ -807,15 +796,14 @@ mod tests { fn parent_payload_returns_parent_digest() { let runtime = deterministic::Runner::default(); runtime.start(|mut context| async move { + let namespace = b"ns".to_vec(); let Fixture { schemes, verifier, .. - } = ed25519::fixture(&mut context, 4); - let namespace = b"ns".to_vec(); + } = ed25519::fixture(&mut context, &namespace, 4); let local_scheme = schemes[2].clone(); // leader of view 1 let cfg = Config { scheme: local_scheme, elector: ::default(), - namespace: namespace.clone(), epoch: Epoch::new(4), activity_timeout: ViewDelta::new(2), leader_timeout: Duration::from_secs(1), @@ -845,7 +833,7 @@ mod tests { // Add notarization certificate let notarization_votes: Vec<_> = schemes .iter() - .map(|scheme| Notarize::sign(scheme, &namespace, parent_proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, parent_proposal.clone()).unwrap()) .collect(); let notarization = Notarization::from_notarizes(&verifier, notarization_votes.iter()).unwrap(); @@ -865,15 +853,14 @@ mod tests { fn parent_certificate_prefers_finalization() { let runtime = deterministic::Runner::default(); runtime.start(|mut context| async move { + let namespace = b"ns".to_vec(); let Fixture { schemes, verifier, .. - } = ed25519::fixture(&mut context, 4); - let namespace = b"ns".to_vec(); + } = ed25519::fixture(&mut context, &namespace, 4); let local_scheme = schemes[1].clone(); // leader of view 2 let cfg = Config { scheme: local_scheme, elector: ::default(), - namespace: namespace.clone(), epoch: Epoch::new(7), activity_timeout: ViewDelta::new(3), leader_timeout: Duration::from_secs(1), @@ -889,7 +876,7 @@ mod tests { Proposal::new(parent_round, GENESIS_VIEW, Sha256Digest::from([11u8; 32])); let notarize_votes: Vec<_> = schemes .iter() - .map(|scheme| Notarize::sign(scheme, &namespace, parent_proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, parent_proposal.clone()).unwrap()) .collect(); let notarization = Notarization::from_notarizes(&verifier, notarize_votes.iter()) .expect("notarization"); @@ -910,7 +897,7 @@ mod tests { // Add finalization for the same parent view let finalize_votes: Vec<_> = schemes .iter() - .map(|scheme| Finalize::sign(scheme, &namespace, parent_proposal.clone()).unwrap()) + .map(|scheme| Finalize::sign(scheme, parent_proposal.clone()).unwrap()) .collect(); let finalization = Finalization::from_finalizes(&verifier, finalize_votes.iter()) .expect("finalization"); @@ -926,14 +913,13 @@ mod tests { fn parent_payload_errors_without_nullification() { let runtime = deterministic::Runner::default(); runtime.start(|mut context| async move { + let namespace = b"ns".to_vec(); let Fixture { schemes, verifier, .. - } = ed25519::fixture(&mut context, 4); - let namespace = b"ns".to_vec(); + } = ed25519::fixture(&mut context, &namespace, 4); let cfg = Config { scheme: verifier.clone(), elector: ::default(), - namespace: namespace.clone(), epoch: Epoch::new(1), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), @@ -952,7 +938,7 @@ mod tests { ); let notarization_votes: Vec<_> = schemes .iter() - .map(|scheme| Notarize::sign(scheme, &namespace, parent_proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, parent_proposal.clone()).unwrap()) .collect(); let notarization = Notarization::from_notarizes(&verifier, notarization_votes.iter()).unwrap(); @@ -973,14 +959,13 @@ mod tests { fn parent_payload_returns_genesis_payload() { let runtime = deterministic::Runner::default(); runtime.start(|mut context| async move { + let namespace = b"ns".to_vec(); let Fixture { schemes, verifier, .. - } = ed25519::fixture(&mut context, 4); - let namespace = b"ns".to_vec(); + } = ed25519::fixture(&mut context, &namespace, 4); let cfg = Config { scheme: verifier.clone(), elector: ::default(), - namespace: namespace.clone(), epoch: Epoch::new(1), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), @@ -994,12 +979,8 @@ mod tests { let nullify_votes: Vec<_> = schemes .iter() .map(|scheme| { - Nullify::sign::( - scheme, - &namespace, - Rnd::new(Epoch::new(1), View::new(1)), - ) - .unwrap() + Nullify::sign::(scheme, Rnd::new(Epoch::new(1), View::new(1))) + .unwrap() }) .collect(); let nullification = Nullification::from_nullifies(&verifier, &nullify_votes).unwrap(); @@ -1024,11 +1005,10 @@ mod tests { let namespace = b"ns".to_vec(); let Fixture { schemes, verifier, .. - } = ed25519::fixture(&mut context, 4); + } = ed25519::fixture(&mut context, &namespace, 4); let cfg = Config { scheme: verifier.clone(), elector: ::default(), - namespace: namespace.clone(), epoch: Epoch::new(1), activity_timeout: ViewDelta::new(5), leader_timeout: Duration::from_secs(1), @@ -1046,7 +1026,7 @@ mod tests { ); let finalization_votes: Vec<_> = schemes .iter() - .map(|scheme| Finalize::sign(scheme, &namespace, proposal_a.clone()).unwrap()) + .map(|scheme| Finalize::sign(scheme, proposal_a.clone()).unwrap()) .collect(); let finalization = Finalization::from_finalizes(&verifier, finalization_votes.iter()) .expect("finalization"); @@ -1066,10 +1046,10 @@ mod tests { fn replay_restores_conflict_state() { let runtime = deterministic::Runner::default(); runtime.start(|mut context| async move { + let namespace = b"ns".to_vec(); let Fixture { schemes, verifier, .. - } = ed25519::fixture(&mut context, 4); - let namespace = b"ns".to_vec(); + } = ed25519::fixture(&mut context, &namespace, 4); let mut scheme_iter = schemes.into_iter(); let local_scheme = scheme_iter.next().unwrap(); let other_schemes: Vec<_> = scheme_iter.collect(); @@ -1079,7 +1059,6 @@ mod tests { Config { scheme: local_scheme.clone(), elector: ::default(), - namespace: namespace.clone(), epoch: Epoch::new(1), activity_timeout: ViewDelta::new(5), leader_timeout: Duration::from_secs(1), @@ -1092,7 +1071,7 @@ mod tests { let round = Rnd::new(epoch, view); let proposal_a = Proposal::new(round, GENESIS_VIEW, Sha256Digest::from([21u8; 32])); let proposal_b = Proposal::new(round, GENESIS_VIEW, Sha256Digest::from([22u8; 32])); - let local_vote = Notarize::sign(&local_scheme, &namespace, proposal_a).unwrap(); + let local_vote = Notarize::sign(&local_scheme, proposal_a).unwrap(); // Replay local notarize vote state.replay(&Artifact::Notarize(local_vote.clone())); @@ -1101,7 +1080,7 @@ mod tests { let votes_b: Vec<_> = other_schemes .iter() .take(3) - .map(|scheme| Notarize::sign(scheme, &namespace, proposal_b.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal_b.clone()).unwrap()) .collect(); let conflicting = Notarization::from_notarizes(&verifier, votes_b.iter()).expect("certificate"); @@ -1117,7 +1096,6 @@ mod tests { Config { scheme: local_scheme, elector: ::default(), - namespace, epoch: Epoch::new(1), activity_timeout: ViewDelta::new(5), leader_timeout: Duration::from_secs(1), @@ -1140,11 +1118,10 @@ mod tests { let runtime = deterministic::Runner::default(); runtime.start(|mut context| async move { let namespace = b"ns".to_vec(); - let Fixture { schemes, .. } = ed25519::fixture(&mut context, 4); + let Fixture { schemes, .. } = ed25519::fixture(&mut context, &namespace, 4); let cfg = Config { scheme: schemes[0].clone(), elector: ::default(), - namespace, epoch: Epoch::new(1), activity_timeout: ViewDelta::new(5), leader_timeout: Duration::from_secs(1), diff --git a/consensus/src/simplex/config.rs b/consensus/src/simplex/config.rs index 7e5bf50d8a..4636fa66aa 100644 --- a/consensus/src/simplex/config.rs +++ b/consensus/src/simplex/config.rs @@ -68,9 +68,6 @@ pub struct Config< /// Epoch for the consensus engine. Each running engine should have a unique epoch. pub epoch: Epoch, - /// Prefix for all signed messages to prevent replay attacks. - pub namespace: Vec, - /// Number of bytes to buffer when replaying during startup. pub replay_buffer: NonZeroUsize, diff --git a/consensus/src/simplex/elector.rs b/consensus/src/simplex/elector.rs index 415132c813..aefecaf959 100644 --- a/consensus/src/simplex/elector.rs +++ b/consensus/src/simplex/elector.rs @@ -233,13 +233,15 @@ mod tests { use commonware_utils::{quorum_from_slice, TryFromIterator}; use rand::{rngs::StdRng, SeedableRng}; + const NAMESPACE: &[u8] = b"test"; + type ThresholdScheme = bls12381_threshold::Scheme; #[test] fn round_robin_rotates_through_participants() { let mut rng = StdRng::seed_from_u64(42); - let Fixture { participants, .. } = ed25519::fixture(&mut rng, 4); + let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 4); let participants = Set::try_from_iter(participants).unwrap(); let n = participants.len(); let elector: RoundRobinElector = @@ -262,7 +264,7 @@ mod tests { #[test] fn round_robin_cycles_through_epochs() { let mut rng = StdRng::seed_from_u64(42); - let Fixture { participants, .. } = ed25519::fixture(&mut rng, 5); + let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 5); let participants = Set::try_from_iter(participants).unwrap(); let n = participants.len(); let elector: RoundRobinElector = @@ -288,7 +290,7 @@ mod tests { #[test] fn round_robin_shuffled_changes_order() { let mut rng = StdRng::seed_from_u64(42); - let Fixture { participants, .. } = ed25519::fixture(&mut rng, 5); + let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 5); let participants = Set::try_from_iter(participants).unwrap(); let elector_no_seed: RoundRobinElector = @@ -329,7 +331,7 @@ mod tests { #[test] fn round_robin_same_seed_is_deterministic() { let mut rng = StdRng::seed_from_u64(42); - let Fixture { participants, .. } = ed25519::fixture(&mut rng, 5); + let Fixture { participants, .. } = ed25519::fixture(&mut rng, NAMESPACE, 5); let participants = Set::try_from_iter(participants).unwrap(); let elector1: RoundRobinElector = @@ -355,7 +357,8 @@ mod tests { #[test] fn random_falls_back_to_round_robin_for_view_1() { let mut rng = StdRng::seed_from_u64(42); - let Fixture { participants, .. } = bls12381_threshold::fixture::(&mut rng, 5); + let Fixture { participants, .. } = + bls12381_threshold::fixture::(&mut rng, NAMESPACE, 5); let participants = Set::try_from_iter(participants).unwrap(); let n = participants.len(); let elector: RandomElector = Random.build(&participants); @@ -384,7 +387,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut rng, 5); + } = bls12381_threshold::fixture::(&mut rng, NAMESPACE, 5); let participants = Set::try_from_iter(participants).unwrap(); let elector: RandomElector = Random.build(&participants); let quorum = quorum_from_slice(&schemes) as usize; @@ -395,7 +398,7 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(b"test", Subject::Nullify { round: round1 }) + s.sign::(Subject::Nullify { round: round1 }) .unwrap() }) .collect(); @@ -407,7 +410,7 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(b"test", Subject::Nullify { round: round2 }) + s.sign::(Subject::Nullify { round: round2 }) .unwrap() }) .collect(); @@ -438,7 +441,8 @@ mod tests { #[should_panic] fn random_panics_on_none_certificate_after_view_1() { let mut rng = StdRng::seed_from_u64(42); - let Fixture { participants, .. } = bls12381_threshold::fixture::(&mut rng, 5); + let Fixture { participants, .. } = + bls12381_threshold::fixture::(&mut rng, NAMESPACE, 5); let participants = Set::try_from_iter(participants).unwrap(); let elector: RandomElector = Random.build(&participants); diff --git a/consensus/src/simplex/engine.rs b/consensus/src/simplex/engine.rs index 7548fc3097..753d02555d 100644 --- a/consensus/src/simplex/engine.rs +++ b/consensus/src/simplex/engine.rs @@ -59,7 +59,6 @@ impl< blocker: cfg.blocker.clone(), reporter: cfg.reporter.clone(), epoch: cfg.epoch, - namespace: cfg.namespace.clone(), mailbox_size: cfg.mailbox_size, activity_timeout: cfg.activity_timeout, skip_timeout: cfg.skip_timeout, @@ -79,7 +78,6 @@ impl< partition: cfg.partition, mailbox_size: cfg.mailbox_size, epoch: cfg.epoch, - namespace: cfg.namespace.clone(), leader_timeout: cfg.leader_timeout, notarization_timeout: cfg.notarization_timeout, nullify_retry: cfg.nullify_retry, @@ -98,7 +96,6 @@ impl< scheme: cfg.scheme, mailbox_size: cfg.mailbox_size, epoch: cfg.epoch, - namespace: cfg.namespace, fetch_concurrent: cfg.fetch_concurrent, fetch_timeout: cfg.fetch_timeout, }, diff --git a/consensus/src/simplex/mocks/conflicter.rs b/consensus/src/simplex/mocks/conflicter.rs index 56adb3fd8e..4e7a28330c 100644 --- a/consensus/src/simplex/mocks/conflicter.rs +++ b/consensus/src/simplex/mocks/conflicter.rs @@ -15,13 +15,11 @@ use tracing::debug; pub struct Config { pub scheme: S, - pub namespace: Vec, } pub struct Conflicter { context: ContextCell, scheme: S, - namespace: Vec, _hasher: PhantomData, } @@ -35,7 +33,6 @@ where Self { context: ContextCell::new(context), scheme: cfg.scheme, - namespace: cfg.namespace, _hasher: PhantomData, } } @@ -63,15 +60,12 @@ where let payload = H::Digest::random(&mut self.context); let proposal = Proposal::new(notarize.round(), notarize.proposal.parent, payload); - let n = - Notarize::::sign(&self.scheme, &self.namespace, proposal).unwrap(); + let n = Notarize::::sign(&self.scheme, proposal).unwrap(); let msg = Vote::Notarize(n).encode().into(); sender.send(Recipients::All, msg, true).await.unwrap(); // Notarize received digest - let n = - Notarize::::sign(&self.scheme, &self.namespace, notarize.proposal) - .unwrap(); + let n = Notarize::::sign(&self.scheme, notarize.proposal).unwrap(); let msg = Vote::Notarize(n).encode().into(); sender.send(Recipients::All, msg, true).await.unwrap(); } @@ -80,15 +74,12 @@ where let payload = H::Digest::random(&mut self.context); let proposal = Proposal::new(finalize.round(), finalize.proposal.parent, payload); - let f = - Finalize::::sign(&self.scheme, &self.namespace, proposal).unwrap(); + let f = Finalize::::sign(&self.scheme, proposal).unwrap(); let msg = Vote::Finalize(f).encode().into(); sender.send(Recipients::All, msg, true).await.unwrap(); // Finalize provided digest - let f = - Finalize::::sign(&self.scheme, &self.namespace, finalize.proposal) - .unwrap(); + let f = Finalize::::sign(&self.scheme, finalize.proposal).unwrap(); let msg = Vote::Finalize(f).encode().into(); sender.send(Recipients::All, msg, true).await.unwrap(); } diff --git a/consensus/src/simplex/mocks/equivocator.rs b/consensus/src/simplex/mocks/equivocator.rs index a886583f27..08076d2e6e 100644 --- a/consensus/src/simplex/mocks/equivocator.rs +++ b/consensus/src/simplex/mocks/equivocator.rs @@ -19,7 +19,6 @@ use std::{collections::HashSet, sync::Arc}; pub struct Config, H: Hasher> { pub scheme: S, pub elector: L, - pub namespace: Vec, pub epoch: Epoch, pub relay: Arc>, pub hasher: H, @@ -34,7 +33,6 @@ pub struct Equivocator< context: ContextCell, scheme: S, elector: L::Elector, - namespace: Vec, epoch: Epoch, relay: Arc>, hasher: H, @@ -51,7 +49,6 @@ impl, L: ElectorConfig, H: Has Self { context: ContextCell::new(context), scheme: cfg.scheme, - namespace: cfg.namespace, epoch: cfg.epoch, relay: cfg.relay, hasher: cfg.hasher, @@ -147,8 +144,7 @@ impl, L: ElectorConfig, H: Has self.relay.broadcast(me, (digest_b, payload_b.into())).await; // Notarize proposal A and send it to victim only - let notarize_a = Notarize::::sign(&self.scheme, &self.namespace, proposal_a) - .expect("sign failed"); + let notarize_a = Notarize::::sign(&self.scheme, proposal_a).expect("sign failed"); vote_sender .send( Recipients::One(victim.clone()), @@ -159,8 +155,7 @@ impl, L: ElectorConfig, H: Has .expect("send failed"); // Notarize proposal B and send it to everyone else - let notarize_b = Notarize::::sign(&self.scheme, &self.namespace, proposal_b) - .expect("sign failed"); + let notarize_b = Notarize::::sign(&self.scheme, proposal_b).expect("sign failed"); let non_victims: Vec<_> = self .scheme .participants() diff --git a/consensus/src/simplex/mocks/impersonator.rs b/consensus/src/simplex/mocks/impersonator.rs index d1ef4ece13..a438de5a31 100644 --- a/consensus/src/simplex/mocks/impersonator.rs +++ b/consensus/src/simplex/mocks/impersonator.rs @@ -14,15 +14,12 @@ use tracing::debug; pub struct Config { pub scheme: S, - pub namespace: Vec, } pub struct Impersonator { context: ContextCell, scheme: S, - namespace: Vec, - _hasher: PhantomData, } @@ -34,8 +31,6 @@ impl, H: Hasher context: ContextCell::new(context), scheme: cfg.scheme, - namespace: cfg.namespace, - _hasher: PhantomData, } } @@ -60,8 +55,7 @@ impl, H: Hasher match msg { Vote::Notarize(notarize) => { // Notarize received digest - let mut n = - Notarize::sign(&self.scheme, &self.namespace, notarize.proposal).unwrap(); + let mut n = Notarize::sign(&self.scheme, notarize.proposal).unwrap(); // Manipulate index if n.attestation.signer == 0 { @@ -76,8 +70,7 @@ impl, H: Hasher } Vote::Finalize(finalize) => { // Finalize provided digest - let mut f = - Finalize::sign(&self.scheme, &self.namespace, finalize.proposal).unwrap(); + let mut f = Finalize::sign(&self.scheme, finalize.proposal).unwrap(); // Manipulate signature if f.attestation.signer == 0 { diff --git a/consensus/src/simplex/mocks/nuller.rs b/consensus/src/simplex/mocks/nuller.rs index 2d33171500..d5f9aa1cab 100644 --- a/consensus/src/simplex/mocks/nuller.rs +++ b/consensus/src/simplex/mocks/nuller.rs @@ -13,13 +13,11 @@ use tracing::debug; pub struct Config { pub scheme: S, - pub namespace: Vec, } pub struct Nuller { context: ContextCell, scheme: S, - namespace: Vec, _hasher: PhantomData, } @@ -33,7 +31,6 @@ where Self { context: ContextCell::new(context), scheme: cfg.scheme, - namespace: cfg.namespace, _hasher: PhantomData, } } @@ -58,14 +55,13 @@ where match msg { Vote::Notarize(notarize) => { // Nullify - let n = Nullify::sign(&self.scheme, &self.namespace, notarize.round()).unwrap(); + let n = Nullify::sign(&self.scheme, notarize.round()).unwrap(); let msg = Vote::::Nullify(n).encode().into(); sender.send(Recipients::All, msg, true).await.unwrap(); // Finalize digest let proposal = notarize.proposal; - let f = - Finalize::::sign(&self.scheme, &self.namespace, proposal).unwrap(); + let f = Finalize::::sign(&self.scheme, proposal).unwrap(); let msg = Vote::Finalize(f).encode().into(); sender.send(Recipients::All, msg, true).await.unwrap(); } diff --git a/consensus/src/simplex/mocks/nullify_only.rs b/consensus/src/simplex/mocks/nullify_only.rs index b279a1e42b..8fdab3cb82 100644 --- a/consensus/src/simplex/mocks/nullify_only.rs +++ b/consensus/src/simplex/mocks/nullify_only.rs @@ -17,13 +17,11 @@ use tracing::debug; pub struct Config { pub scheme: S, - pub namespace: Vec, } pub struct NullifyOnly, H: Hasher> { context: ContextCell, scheme: S, - namespace: Vec, _hasher: PhantomData, } @@ -32,7 +30,6 @@ impl, H: Hasher> NullifyOnly { Self { context: ContextCell::new(context), scheme: cfg.scheme, - namespace: cfg.namespace, _hasher: PhantomData, } } @@ -55,9 +52,7 @@ impl, H: Hasher> NullifyOnly { // Respond with only a `Nullify` vote when a proposal is observed. if let Vote::Notarize(notarize) = msg { - let nullify = - Nullify::sign::(&self.scheme, &self.namespace, notarize.round()) - .unwrap(); + let nullify = Nullify::sign::(&self.scheme, notarize.round()).unwrap(); let msg = Vote::::Nullify(nullify).encode().into(); sender.send(Recipients::All, msg, true).await.unwrap(); } diff --git a/consensus/src/simplex/mocks/outdated.rs b/consensus/src/simplex/mocks/outdated.rs index efb942a163..c3a9f3b6e1 100644 --- a/consensus/src/simplex/mocks/outdated.rs +++ b/consensus/src/simplex/mocks/outdated.rs @@ -18,7 +18,6 @@ use tracing::debug; pub struct Config { pub scheme: S, - pub namespace: Vec, pub view_delta: ViewDelta, } @@ -26,8 +25,6 @@ pub struct Outdated { context: ContextCell, scheme: S, - namespace: Vec, - history: HashMap>, view_delta: ViewDelta, @@ -45,8 +42,6 @@ where context: ContextCell::new(context), scheme: cfg.scheme, - namespace: cfg.namespace, - history: HashMap::new(), view_delta: cfg.view_delta, @@ -83,8 +78,7 @@ where continue; }; debug!(%view, "notarizing old proposal"); - let n = Notarize::::sign(&self.scheme, &self.namespace, proposal.clone()) - .unwrap(); + let n = Notarize::::sign(&self.scheme, proposal.clone()).unwrap(); let msg = Vote::Notarize(n).encode().into(); sender.send(Recipients::All, msg, true).await.unwrap(); } @@ -98,8 +92,7 @@ where continue; }; debug!(%view, "finalizing old proposal"); - let f = Finalize::::sign(&self.scheme, &self.namespace, proposal.clone()) - .unwrap(); + let f = Finalize::::sign(&self.scheme, proposal.clone()).unwrap(); let msg = Vote::Finalize(f).encode().into(); sender.send(Recipients::All, msg, true).await.unwrap(); } diff --git a/consensus/src/simplex/mocks/reconfigurer.rs b/consensus/src/simplex/mocks/reconfigurer.rs index b101d52904..ab947a367e 100644 --- a/consensus/src/simplex/mocks/reconfigurer.rs +++ b/consensus/src/simplex/mocks/reconfigurer.rs @@ -15,13 +15,11 @@ use tracing::debug; pub struct Config { pub scheme: S, - pub namespace: Vec, } pub struct Reconfigurer { context: ContextCell, scheme: S, - namespace: Vec, _hasher: PhantomData, } @@ -35,7 +33,6 @@ where Self { context: ContextCell::new(context), scheme: cfg.scheme, - namespace: cfg.namespace, _hasher: PhantomData, } } @@ -66,7 +63,7 @@ where proposal.round = (new_epoch, old_round.view()).into(); // Sign and broadcast - let n = Notarize::sign(&self.scheme, &self.namespace, proposal).unwrap(); + let n = Notarize::sign(&self.scheme, proposal).unwrap(); let msg = Vote::Notarize(n).encode().into(); sender.send(Recipients::All, msg, true).await.unwrap(); } @@ -78,7 +75,7 @@ where proposal.round = (new_epoch, old_round.view()).into(); // Sign and broadcast - let f = Finalize::sign(&self.scheme, &self.namespace, proposal).unwrap(); + let f = Finalize::sign(&self.scheme, proposal).unwrap(); let msg = Vote::Finalize(f).encode().into(); sender.send(Recipients::All, msg, true).await.unwrap(); } @@ -88,7 +85,7 @@ where let new_epoch = old_round.epoch().next(); let new_round = (new_epoch, old_round.view()).into(); - let n = Nullify::sign(&self.scheme, &self.namespace, new_round).unwrap(); + let n = Nullify::sign(&self.scheme, new_round).unwrap(); let msg = Vote::::Nullify(n).encode().into(); sender.send(Recipients::All, msg, true).await.unwrap(); } diff --git a/consensus/src/simplex/mocks/reporter.rs b/consensus/src/simplex/mocks/reporter.rs index 6b183ef309..3e91d378f5 100644 --- a/consensus/src/simplex/mocks/reporter.rs +++ b/consensus/src/simplex/mocks/reporter.rs @@ -30,7 +30,6 @@ type Faults = HashMap<::PublicKey, HashMap> { - pub namespace: Vec, pub participants: Set, pub scheme: S, pub elector: L, @@ -43,8 +42,6 @@ pub struct Reporter, D: Digest> scheme: S, elector: L::Elector, - namespace: Vec, - pub leaders: Arc>>, pub certified: Arc>>, pub notarizes: Arc>>, @@ -73,7 +70,6 @@ where Self { context, - namespace: cfg.namespace, participants: cfg.participants, scheme: cfg.scheme, elector, @@ -122,7 +118,7 @@ where let verified = activity.verified(); match &activity { Activity::Notarize(notarize) => { - if !notarize.verify(&mut self.context, &self.scheme, &self.namespace) { + if !notarize.verify(&mut self.context, &self.scheme) { assert!(!verified); *self.invalid.lock().unwrap() += 1; return; @@ -144,7 +140,6 @@ where let view = notarization.view(); if !self.scheme.verify_certificate::<_, D>( &mut self.context, - &self.namespace, Subject::Notarize { proposal: ¬arization.proposal, }, @@ -164,7 +159,7 @@ where self.certified(notarization.round(), ¬arization.certificate); } Activity::Nullify(nullify) => { - if !nullify.verify(&mut self.context, &self.scheme, &self.namespace) { + if !nullify.verify(&mut self.context, &self.scheme) { assert!(!verified); *self.invalid.lock().unwrap() += 1; return; @@ -184,7 +179,6 @@ where let view = nullification.view(); if !self.scheme.verify_certificate::<_, D>( &mut self.context, - &self.namespace, Subject::Nullify { round: nullification.round, }, @@ -204,7 +198,7 @@ where self.certified(nullification.round, &nullification.certificate); } Activity::Finalize(finalize) => { - if !finalize.verify(&mut self.context, &self.scheme, &self.namespace) { + if !finalize.verify(&mut self.context, &self.scheme) { assert!(!verified); *self.invalid.lock().unwrap() += 1; return; @@ -226,7 +220,6 @@ where let view = finalization.view(); if !self.scheme.verify_certificate::<_, D>( &mut self.context, - &self.namespace, Subject::Finalize { proposal: &finalization.proposal, }, @@ -254,7 +247,7 @@ where } Activity::ConflictingNotarize(conflicting) => { let view = conflicting.view(); - if !conflicting.verify(&mut self.context, &self.scheme, &self.namespace) { + if !conflicting.verify(&mut self.context, &self.scheme) { assert!(!verified); *self.invalid.lock().unwrap() += 1; return; @@ -273,7 +266,7 @@ where } Activity::ConflictingFinalize(conflicting) => { let view = conflicting.view(); - if !conflicting.verify(&mut self.context, &self.scheme, &self.namespace) { + if !conflicting.verify(&mut self.context, &self.scheme) { assert!(!verified); *self.invalid.lock().unwrap() += 1; return; @@ -292,7 +285,7 @@ where } Activity::NullifyFinalize(conflicting) => { let view = conflicting.view(); - if !conflicting.verify(&mut self.context, &self.scheme, &self.namespace) { + if !conflicting.verify(&mut self.context, &self.scheme) { assert!(!verified); *self.invalid.lock().unwrap() += 1; return; diff --git a/consensus/src/simplex/mod.rs b/consensus/src/simplex/mod.rs index a8a57940eb..a8104c34be 100644 --- a/consensus/src/simplex/mod.rs +++ b/consensus/src/simplex/mod.rs @@ -538,7 +538,7 @@ mod tests { fn all_online(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -568,7 +568,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -590,7 +590,6 @@ mod tests { // Configure engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx].clone(), elector: elector.clone(), @@ -623,7 +622,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -791,7 +789,7 @@ mod tests { fn observer(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -821,7 +819,7 @@ mod tests { schemes, verifier, .. - } = fixture(&mut context, n_active); + } = fixture(&mut context, &namespace, n_active); // Add observer (no share) let private_key_observer = PrivateKey::from_seed(n_active as u64); @@ -859,7 +857,6 @@ mod tests { schemes[idx].clone() }; let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: signing.clone(), elector: elector.clone(), @@ -892,7 +889,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -957,7 +953,7 @@ mod tests { fn unclean_shutdown(mut fixture: F) where S: Scheme, - F: FnMut(&mut StdRng, u32) -> Fixture, + F: FnMut(&mut StdRng, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -978,7 +974,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut rng, n); + } = fixture(&mut rng, &namespace, n); // Create block relay, shared across restarts. let relay = Arc::new(mocks::relay::Relay::::new()); @@ -987,7 +983,6 @@ mod tests { let rng = rng.clone(); let participants = participants.clone(); let schemes = schemes.clone(); - let namespace = namespace.clone(); let shutdowns = shutdowns.clone(); let supervised = supervised.clone(); let relay = relay.clone(); @@ -1029,7 +1024,6 @@ mod tests { // Configure engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx].clone(), elector: elector.clone(), @@ -1061,7 +1055,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -1157,7 +1150,7 @@ mod tests { fn backfill(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -1186,7 +1179,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators except first @@ -1219,7 +1212,6 @@ mod tests { // Configure engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx_scheme].clone(), elector: elector.clone(), @@ -1252,7 +1244,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -1340,7 +1331,6 @@ mod tests { // Configure engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[0].clone(), elector: elector.clone(), @@ -1373,7 +1363,6 @@ mod tests { partition: me.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -1418,7 +1407,7 @@ mod tests { fn one_offline(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -1449,7 +1438,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators except first @@ -1482,7 +1471,6 @@ mod tests { // Configure engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx_scheme].clone(), elector: elector.clone(), @@ -1515,7 +1503,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -1682,7 +1669,7 @@ mod tests { fn slow_validator(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -1711,7 +1698,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -1733,7 +1720,6 @@ mod tests { // Configure engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx_scheme].clone(), elector: elector.clone(), @@ -1778,7 +1764,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -1872,7 +1857,7 @@ mod tests { fn all_recovery(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -1901,7 +1886,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -1923,7 +1908,6 @@ mod tests { // Configure engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx].clone(), elector: elector.clone(), @@ -1956,7 +1940,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -2087,7 +2070,7 @@ mod tests { fn partition(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -2116,7 +2099,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -2138,7 +2121,6 @@ mod tests { // Configure engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx].clone(), elector: elector.clone(), @@ -2171,7 +2153,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -2290,7 +2271,7 @@ mod tests { fn slow_and_lossy_links(seed: u64, mut fixture: F) -> String where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -2322,7 +2303,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -2350,7 +2331,6 @@ mod tests { // Configure engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx].clone(), elector: elector.clone(), @@ -2383,7 +2363,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -2522,7 +2501,7 @@ mod tests { fn conflicter(seed: u64, mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -2554,7 +2533,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -2575,7 +2554,6 @@ mod tests { // Start engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx_scheme].clone(), elector: elector.clone(), @@ -2587,7 +2565,6 @@ mod tests { .expect("validator should be registered"); if idx_scheme == 0 { let cfg = mocks::conflicter::Config { - namespace: namespace.clone(), scheme: schemes[idx_scheme].clone(), }; @@ -2624,7 +2601,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -2711,7 +2687,7 @@ mod tests { fn invalid(seed: u64, mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -2743,7 +2719,14 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); + + // Create scheme with wrong namespace for byzantine node (index 0) + let Fixture { + schemes: wrong_schemes, + .. + } = fixture(&mut context, b"wrong-namespace", n); + let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -2762,15 +2745,15 @@ mod tests { // Create scheme context let context = context.with_label(&format!("validator_{}", *validator)); - // Byzantine node (idx 0) uses empty namespace to produce invalid signatures - let engine_namespace = if idx_scheme == 0 { - vec![] + // Byzantine node (idx 0) uses wrong namespace scheme to produce invalid signatures + // Honest nodes use correct namespace schemes + let scheme = if idx_scheme == 0 { + wrong_schemes[idx_scheme].clone() } else { - namespace.clone() + schemes[idx_scheme].clone() }; let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), // Reporter always uses correct namespace participants: participants.clone().try_into().unwrap(), scheme: schemes[idx_scheme].clone(), elector: elector.clone(), @@ -2795,7 +2778,7 @@ mod tests { actor.start(); let blocker = oracle.control(validator.clone()); let cfg = config::Config { - scheme: schemes[idx_scheme].clone(), + scheme, elector: elector.clone(), blocker, automaton: application.clone(), @@ -2804,7 +2787,6 @@ mod tests { partition: validator.clone().to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: engine_namespace, leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -2883,7 +2865,7 @@ mod tests { fn impersonator(seed: u64, mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -2915,7 +2897,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -2936,7 +2918,6 @@ mod tests { // Start engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx_scheme].clone(), elector: elector.clone(), @@ -2949,7 +2930,6 @@ mod tests { if idx_scheme == 0 { let cfg = mocks::impersonator::Config { scheme: schemes[idx_scheme].clone(), - namespace: namespace.clone(), }; let engine: mocks::impersonator::Impersonator<_, _, Sha256> = @@ -2985,7 +2965,6 @@ mod tests { partition: validator.clone().to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -3056,7 +3035,7 @@ mod tests { fn equivocator(seed: u64, mut fixture: F) -> bool where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -3088,7 +3067,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -3110,7 +3089,6 @@ mod tests { // Start engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx_scheme].clone(), elector: elector.clone(), @@ -3123,7 +3101,6 @@ mod tests { .expect("validator should be registered"); if idx_scheme == 0 { let cfg = mocks::equivocator::Config { - namespace: namespace.clone(), scheme: schemes[idx_scheme].clone(), epoch: Epoch::new(333), relay: relay.clone(), @@ -3162,7 +3139,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -3218,7 +3194,6 @@ mod tests { // Start engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx].clone(), elector: elector.clone(), @@ -3253,7 +3228,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -3363,7 +3337,7 @@ mod tests { fn reconfigurer(seed: u64, mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -3395,7 +3369,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -3416,7 +3390,6 @@ mod tests { // Start engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx_scheme].clone(), elector: elector.clone(), @@ -3429,7 +3402,6 @@ mod tests { if idx_scheme == 0 { let cfg = mocks::reconfigurer::Config { scheme: schemes[idx_scheme].clone(), - namespace: namespace.clone(), }; let engine: mocks::reconfigurer::Reconfigurer<_, _, Sha256> = mocks::reconfigurer::Reconfigurer::new( @@ -3464,7 +3436,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -3535,7 +3506,7 @@ mod tests { fn nuller(seed: u64, mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -3567,7 +3538,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -3588,7 +3559,6 @@ mod tests { // Start engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx_scheme].clone(), elector: elector.clone(), @@ -3600,7 +3570,6 @@ mod tests { .expect("validator should be registered"); if idx_scheme == 0 { let cfg = mocks::nuller::Config { - namespace: namespace.clone(), scheme: schemes[idx_scheme].clone(), }; let engine: mocks::nuller::Nuller<_, _, Sha256> = @@ -3633,7 +3602,6 @@ mod tests { partition: validator.clone().to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -3717,7 +3685,7 @@ mod tests { fn outdated(seed: u64, mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -3749,7 +3717,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -3770,7 +3738,6 @@ mod tests { // Start engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx_scheme].clone(), elector: elector.clone(), @@ -3783,7 +3750,6 @@ mod tests { if idx_scheme == 0 { let cfg = mocks::outdated::Config { scheme: schemes[idx_scheme].clone(), - namespace: namespace.clone(), view_delta: ViewDelta::new(activity_timeout.get().saturating_mul(4)), }; let engine: mocks::outdated::Outdated<_, _, Sha256> = @@ -3816,7 +3782,6 @@ mod tests { partition: validator.clone().to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -3882,7 +3847,7 @@ mod tests { fn run_1k(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -3912,7 +3877,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -3934,7 +3899,6 @@ mod tests { // Configure engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx].clone(), elector: elector.clone(), @@ -3967,7 +3931,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -4060,7 +4023,7 @@ mod tests { fn engine_shutdown(mut fixture: F, graceful: bool) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -4086,7 +4049,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link the single validator to itself (no-ops for completeness) @@ -4100,7 +4063,6 @@ mod tests { // Create engine let elector = L::default(); let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[0].clone(), elector: elector.clone(), @@ -4133,7 +4095,6 @@ mod tests { partition: participants[0].clone().to_string(), mailbox_size: 64, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_millis(50), notarization_timeout: Duration::from_millis(100), nullify_retry: Duration::from_millis(250), @@ -4231,7 +4192,7 @@ mod tests { fn attributable_reporter_filtering(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { let n = 3; @@ -4257,7 +4218,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -4276,7 +4237,6 @@ mod tests { let context = context.with_label(&format!("validator_{}", *validator)); let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx].clone(), elector: elector.clone(), @@ -4290,7 +4250,6 @@ mod tests { let attributable_reporter = scheme::reporter::AttributableReporter::new( context.with_label("rng"), schemes[idx].clone(), - namespace.clone(), mock_reporter.clone(), true, // Enable verification ); @@ -4321,7 +4280,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -4433,7 +4391,7 @@ mod tests { fn split_views_no_lockup(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Scenario: @@ -4489,7 +4447,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // ========== Create engines ========== @@ -4509,7 +4467,6 @@ mod tests { // Byzantine engines let cfg = mocks::nullify_only::Config { scheme: schemes[idx].clone(), - namespace: namespace.clone(), }; let engine: mocks::nullify_only::NullifyOnly<_, _, Sha256> = mocks::nullify_only::NullifyOnly::new( @@ -4523,7 +4480,6 @@ mod tests { } else { // Honest engines let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx].clone(), elector: elector.clone(), @@ -4559,7 +4515,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(10), notarization_timeout: Duration::from_secs(10), nullify_retry: Duration::from_secs(10), @@ -4582,20 +4537,20 @@ mod tests { // Helper: assemble finalization from explicit signer indices let build_finalization = |proposal: &Proposal| -> TFinalization<_, D> { let votes: Vec<_> = (0..=quorum) - .map(|i| TFinalize::sign(&schemes[i], &namespace, proposal.clone()).unwrap()) + .map(|i| TFinalize::sign(&schemes[i], proposal.clone()).unwrap()) .collect(); TFinalization::from_finalizes(&schemes[0], &votes).expect("finalization quorum") }; // Helper: assemble notarization from explicit signer indices let build_notarization = |proposal: &Proposal| -> TNotarization<_, D> { let votes: Vec<_> = (0..=quorum) - .map(|i| TNotarize::sign(&schemes[i], &namespace, proposal.clone()).unwrap()) + .map(|i| TNotarize::sign(&schemes[i], proposal.clone()).unwrap()) .collect(); TNotarization::from_notarizes(&schemes[0], &votes).expect("notarization quorum") }; let build_nullification = |round: Round| -> TNullification<_> { let votes: Vec<_> = (0..=quorum) - .map(|i| TNullify::sign::(&schemes[i], &namespace, round).unwrap()) + .map(|i| TNullify::sign::(&schemes[i], round).unwrap()) .collect(); TNullification::from_nullifies(&schemes[0], &votes).expect("nullification quorum") }; @@ -4835,7 +4790,7 @@ mod tests { participants, schemes, .. - } = bls12381_threshold::fixture::(&mut context, n); + } = bls12381_threshold::fixture::(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -4858,7 +4813,6 @@ mod tests { // Store first reporter for monitoring let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx].clone(), elector: elector.clone(), @@ -4896,7 +4850,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_millis(100), notarization_timeout: Duration::from_millis(200), nullify_retry: Duration::from_millis(500), @@ -4922,7 +4875,7 @@ mod tests { let message = b"Secret message for future view10"; // 32 bytes // Encrypt message - let ciphertext = schemes[0].encrypt(&mut context, &namespace, target, *message); + let ciphertext = schemes[0].encrypt(&mut context, target, *message); // Wait for consensus to reach the target view and then decrypt let reporter = monitor_reporter.lock().unwrap().clone().unwrap(); @@ -4963,7 +4916,7 @@ mod tests { ) -> String where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { // Create context @@ -4992,7 +4945,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let mut registrations = register_validators(&mut oracle, &participants).await; // Link all validators @@ -5014,7 +4967,6 @@ mod tests { // Configure engine let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.clone().try_into().unwrap(), scheme: schemes[idx].clone(), elector: elector.clone(), @@ -5047,7 +4999,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -5143,7 +5094,6 @@ mod tests { partition: validator.to_string(), mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -5371,7 +5321,7 @@ mod tests { fn twins(seed: u64, n: u32, strategy: Strategy, link: Link, mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { let faults = max_faults(n); @@ -5398,7 +5348,7 @@ mod tests { participants, schemes, .. - } = fixture(&mut context, n); + } = fixture(&mut context, &namespace, n); let participants: Arc<[_]> = participants.into(); let mut registrations = register_validators(&mut oracle, &participants).await; link_validators(&mut oracle, &participants, Action::Link(link), None).await; @@ -5513,7 +5463,6 @@ mod tests { let context = context.with_label(&label); let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.as_ref().try_into().unwrap(), scheme: schemes[idx].clone(), elector: elector.clone(), @@ -5550,7 +5499,6 @@ mod tests { partition: label, mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -5573,7 +5521,6 @@ mod tests { let context = context.with_label(&label); let reporter_config = mocks::reporter::Config { - namespace: namespace.clone(), participants: participants.as_ref().try_into().unwrap(), scheme: schemes[idx].clone(), elector: elector.clone(), @@ -5608,7 +5555,6 @@ mod tests { partition: label, mailbox_size: 1024, epoch: Epoch::new(333), - namespace: namespace.clone(), leader_timeout: Duration::from_secs(1), notarization_timeout: Duration::from_secs(2), nullify_retry: Duration::from_secs(10), @@ -5690,7 +5636,7 @@ mod tests { fn test_twins(mut fixture: F) where S: Scheme, - F: FnMut(&mut deterministic::Context, u32) -> Fixture, + F: FnMut(&mut deterministic::Context, &[u8], u32) -> Fixture, L: Elector, { for strategy in [ @@ -5712,7 +5658,9 @@ mod tests { success_rate: 0.75, }, ] { - twins::(0, 5, strategy, link, |context, n| fixture(context, n)); + twins::(0, 5, strategy, link, |namespace, context, n| { + fixture(namespace, context, n) + }); } } } diff --git a/consensus/src/simplex/scheme/bls12381_multisig.rs b/consensus/src/simplex/scheme/bls12381_multisig.rs index 2d7aa6a6bf..0a8acf3811 100644 --- a/consensus/src/simplex/scheme/bls12381_multisig.rs +++ b/consensus/src/simplex/scheme/bls12381_multisig.rs @@ -5,7 +5,7 @@ //! Certificates contain signer indices alongside an aggregated signature, //! enabling secure per-validator activity tracking and conflict detection. -use crate::simplex::types::Subject; +use crate::simplex::{scheme::Namespace, types::Subject}; use commonware_cryptography::impl_certificate_bls12381_multisig; -impl_certificate_bls12381_multisig!(Subject<'a, D>); +impl_certificate_bls12381_multisig!(Subject<'a, D>, Namespace); diff --git a/consensus/src/simplex/scheme/bls12381_threshold.rs b/consensus/src/simplex/scheme/bls12381_threshold.rs index e2cc0cafa7..ce24881a93 100644 --- a/consensus/src/simplex/scheme/bls12381_threshold.rs +++ b/consensus/src/simplex/scheme/bls12381_threshold.rs @@ -8,10 +8,7 @@ use crate::{ simplex::{ - scheme::{ - finalize_namespace, notarize_namespace, nullify_namespace, seed_namespace, - seed_namespace_and_message, vote_namespace_and_message, - }, + scheme::{seed_namespace, Namespace}, types::{Finalization, Notarization, Subject}, }, types::{Epoch, Round, View}, @@ -29,7 +26,7 @@ use commonware_cryptography::{ }, tle, }, - certificate::{self, Attestation, Verification}, + certificate::{self, Attestation, Subject as CertificateSubject, Verification}, Digest, PublicKey, }; use commonware_utils::ordered::Set; @@ -53,16 +50,22 @@ pub enum Scheme { polynomial: Sharing, /// Local share used to generate partial signatures. share: Share, + /// Pre-computed namespaces for domain separation. + namespace: Namespace, }, Verifier { /// Participants in the committee. participants: Set

, /// The public polynomial, used for the group identity, and partial signatures. polynomial: Sharing, + /// Pre-computed namespaces for domain separation. + namespace: Namespace, }, CertificateVerifier { /// Public identity of the committee (constant across reshares). identity: V::Public, + /// Pre-computed namespaces for domain separation. + namespace: Namespace, }, } @@ -75,10 +78,16 @@ impl Scheme { /// /// Returns `None` if the share's public key does not match any participant. /// + /// * `namespace` - base namespace for domain separation /// * `participants` - ordered set of participant identity keys /// * `polynomial` - public polynomial for threshold verification /// * `share` - local threshold share for signing - pub fn signer(participants: Set

, polynomial: Sharing, share: Share) -> Option { + pub fn signer( + namespace: &[u8], + participants: Set

, + polynomial: Sharing, + share: Share, + ) -> Option { assert_eq!( polynomial.total().get() as usize, participants.len(), @@ -93,6 +102,7 @@ impl Scheme { participants, polynomial, share, + namespace: Namespace::new(namespace), }) } else { None @@ -105,9 +115,10 @@ impl Scheme { /// The polynomial can be evaluated to obtain public verification keys for partial /// signatures produced by committee members. /// + /// * `namespace` - base namespace for domain separation /// * `participants` - ordered set of participant identity keys /// * `polynomial` - public polynomial for threshold verification - pub fn verifier(participants: Set

, polynomial: Sharing) -> Self { + pub fn verifier(namespace: &[u8], participants: Set

, polynomial: Sharing) -> Self { assert_eq!( polynomial.total().get() as usize, participants.len(), @@ -118,6 +129,7 @@ impl Scheme { Self::Verifier { participants, polynomial, + namespace: Namespace::new(namespace), } } @@ -126,9 +138,13 @@ impl Scheme { /// This lightweight verifier can authenticate recovered threshold certificates but cannot /// verify individual votes or partial signatures. /// + /// * `namespace` - base namespace for domain separation /// * `identity` - public identity of the committee (constant across reshares) - pub const fn certificate_verifier(identity: V::Public) -> Self { - Self::CertificateVerifier { identity } + pub fn certificate_verifier(namespace: &[u8], identity: V::Public) -> Self { + Self::CertificateVerifier { + identity, + namespace: Namespace::new(namespace), + } } /// Returns the ordered set of participant public identity keys in the committee. @@ -166,6 +182,15 @@ impl Scheme { } } + /// Returns the pre-computed namespaces. + const fn namespace(&self) -> &Namespace { + match self { + Self::Signer { namespace, .. } => namespace, + Self::Verifier { namespace, .. } => namespace, + Self::CertificateVerifier { namespace, .. } => namespace, + } + } + /// Encrypts a message for a target round using Timelock Encryption ([TLE](tle)). /// /// The encrypted message can only be decrypted using the seed signature @@ -174,11 +199,17 @@ impl Scheme { pub fn encrypt( &self, rng: &mut R, - namespace: &[u8], target: Round, message: impl Into, ) -> tle::Ciphertext { - encrypt(rng, *self.identity(), namespace, target, message) + let block = message.into(); + let target_message = target.encode(); + tle::encrypt( + rng, + *self.identity(), + (&self.namespace().seed, &target_message), + &block, + ) } } @@ -207,6 +238,7 @@ pub fn encrypt( #[cfg(feature = "mocks")] pub fn fixture( rng: &mut R, + namespace: &[u8], n: u32, ) -> commonware_cryptography::certificate::mocks::Fixture< Scheme, @@ -217,6 +249,7 @@ where { commonware_cryptography::bls12381::certificate::threshold::mocks::fixture::<_, V, _>( rng, + namespace, n, Scheme::signer, Scheme::verifier, @@ -286,13 +319,12 @@ impl Seed { } /// Verifies the threshold signature on this [Seed]. - pub fn verify(&self, scheme: &Scheme, namespace: &[u8]) -> bool { - let seed_namespace = seed_namespace(namespace); + pub fn verify(&self, scheme: &Scheme) -> bool { let seed_message = self.round.encode(); ops::verify_message::( scheme.identity(), - &seed_namespace, + &scheme.namespace().seed, &seed_message, &self.signature, ) @@ -389,6 +421,18 @@ impl Seedable for Finalization(subject: &Subject<'_, D>) -> bytes::Bytes { + match subject { + Subject::Notarize { proposal } | Subject::Finalize { proposal } => { + proposal.round.encode().freeze() + } + Subject::Nullify { round } => round.encode().freeze(), + } +} + impl certificate::Scheme for Scheme { type Subject<'a, D: Digest> = Subject<'a, D>; type PublicKey = P; @@ -406,22 +450,18 @@ impl certificate::Scheme for Scheme { self.participants() } - fn sign( - &self, - namespace: &[u8], - subject: Subject<'_, D>, - ) -> Option> { + fn sign(&self, subject: Subject<'_, D>) -> Option> { let share = self.share()?; - let (vote_namespace, vote_message) = vote_namespace_and_message(namespace, &subject); + let namespace = self.namespace(); + let vote_namespace = subject.namespace(namespace); + let vote_message = subject.message(); let vote_signature = - threshold::sign_message::(share, vote_namespace.as_ref(), vote_message.as_ref()) - .value; + threshold::sign_message::(share, vote_namespace, &vote_message).value; - let (seed_namespace, seed_message) = seed_namespace_and_message(namespace, &subject); + let seed_message = seed_message_from_subject(&subject); let seed_signature = - threshold::sign_message::(share, seed_namespace.as_ref(), seed_message.as_ref()) - .value; + threshold::sign_message::(share, &namespace.seed, &seed_message).value; let signature = Signature { vote_signature, @@ -437,7 +477,6 @@ impl certificate::Scheme for Scheme { fn verify_attestation( &self, rng: &mut R, - namespace: &[u8], subject: Subject<'_, D>, attestation: &Attestation, ) -> bool @@ -449,20 +488,22 @@ impl certificate::Scheme for Scheme { return false; }; - let (vote_namespace, vote_message) = vote_namespace_and_message(namespace, &subject); - let (seed_namespace, seed_message) = seed_namespace_and_message(namespace, &subject); + let namespace = self.namespace(); + let vote_namespace = subject.namespace(namespace); + let vote_message = subject.message(); + let seed_message = seed_message_from_subject(&subject); batch::verify_same_signer::<_, V, _>( rng, &evaluated, &[ ( - vote_namespace.as_ref(), + vote_namespace, vote_message.as_ref(), attestation.signature.vote_signature, ), ( - seed_namespace.as_ref(), + &namespace.seed, seed_message.as_ref(), attestation.signature.seed_signature, ), @@ -475,7 +516,6 @@ impl certificate::Scheme for Scheme { fn verify_attestations( &self, rng: &mut R, - namespace: &[u8], subject: Subject<'_, D>, attestations: I, ) -> Verification @@ -484,6 +524,7 @@ impl certificate::Scheme for Scheme { D: Digest, I: IntoIterator>, { + let namespace = self.namespace(); let mut invalid = BTreeSet::new(); let (vote_partials, seed_partials): (Vec<_>, Vec<_>) = attestations .into_iter() @@ -502,12 +543,13 @@ impl certificate::Scheme for Scheme { .unzip(); let polynomial = self.polynomial(); - let (vote_namespace, vote_message) = vote_namespace_and_message(namespace, &subject); + let vote_namespace = subject.namespace(namespace); + let vote_message = subject.message(); if let Err(errs) = threshold::batch_verify_same_message::<_, V, _>( rng, polynomial, - vote_namespace.as_ref(), - vote_message.as_ref(), + vote_namespace, + &vote_message, vote_partials.iter(), ) { for partial in errs { @@ -515,12 +557,12 @@ impl certificate::Scheme for Scheme { } } - let (seed_namespace, seed_message) = seed_namespace_and_message(namespace, &subject); + let seed_message = seed_message_from_subject(&subject); if let Err(errs) = threshold::batch_verify_same_message::<_, V, _>( rng, polynomial, - seed_namespace.as_ref(), - seed_message.as_ref(), + &namespace.seed, + &seed_message, seed_partials .iter() .filter(|partial| !invalid.contains(&partial.index)), @@ -584,26 +626,27 @@ impl certificate::Scheme for Scheme { fn verify_certificate( &self, rng: &mut R, - namespace: &[u8], subject: Subject<'_, D>, certificate: &Self::Certificate, ) -> bool { let identity = self.identity(); + let namespace = self.namespace(); - let (vote_namespace, vote_message) = vote_namespace_and_message(namespace, &subject); - let (seed_namespace, seed_message) = seed_namespace_and_message(namespace, &subject); + let vote_namespace = subject.namespace(namespace); + let vote_message = subject.message(); + let seed_message = seed_message_from_subject(&subject); batch::verify_same_signer::<_, V, _>( rng, identity, &[ ( - vote_namespace.as_ref(), + vote_namespace, vote_message.as_ref(), certificate.vote_signature, ), ( - seed_namespace.as_ref(), + &namespace.seed, seed_message.as_ref(), certificate.seed_signature, ), @@ -613,55 +656,23 @@ impl certificate::Scheme for Scheme { .is_ok() } - fn verify_certificates<'a, R, D, I>( - &self, - rng: &mut R, - namespace: &[u8], - certificates: I, - ) -> bool + fn verify_certificates<'a, R, D, I>(&self, rng: &mut R, certificates: I) -> bool where R: CryptoRngCore, D: Digest, I: Iterator, &'a Self::Certificate)>, { let identity = self.identity(); + let namespace = self.namespace(); let mut seeds = HashMap::new(); let mut entries: Vec<_> = Vec::new(); - let notarize_namespace = notarize_namespace(namespace); - let nullify_namespace = nullify_namespace(namespace); - let finalize_namespace = finalize_namespace(namespace); - let seed_namespace = seed_namespace(namespace); - for (context, certificate) in certificates { - let vote_entry = match context { - Subject::Notarize { proposal } => { - let notarize_message = proposal.encode(); - ( - notarize_namespace.clone(), - notarize_message, - certificate.vote_signature, - ) - } - Subject::Nullify { round } => { - let nullify_message = round.encode(); - ( - nullify_namespace.clone(), - nullify_message, - certificate.vote_signature, - ) - } - Subject::Finalize { proposal } => { - let finalize_message = proposal.encode(); - ( - finalize_namespace.clone(), - finalize_message, - certificate.vote_signature, - ) - } - }; - entries.push(vote_entry); + // Prepare vote message with context-specific namespace + let vote_namespace = context.namespace(namespace); + let vote_message = context.message(); + entries.push((vote_namespace, vote_message, certificate.vote_signature)); // Seed signatures are per-view, so multiple certificates for the same view // (e.g., notarization and finalization) share the same seed. We only include @@ -672,18 +683,8 @@ impl certificate::Scheme for Scheme { return false; } } else { - let seed_message = match context { - Subject::Notarize { proposal } | Subject::Finalize { proposal } => { - proposal.round.encode() - } - Subject::Nullify { round } => round.encode(), - }; - - entries.push(( - seed_namespace.clone(), - seed_message, - certificate.seed_signature, - )); + let seed_message = seed_message_from_subject(&context); + entries.push((&namespace.seed, seed_message, certificate.seed_signature)); seeds.insert(context.view(), certificate.seed_signature); } } @@ -692,7 +693,7 @@ impl certificate::Scheme for Scheme { // than computing the aggregate signature and verifying it. let entries_refs: Vec<_> = entries .iter() - .map(|(ns, msg, sig)| (ns.as_ref(), msg.as_ref(), *sig)) + .map(|(ns, msg, sig)| (*ns, msg.as_ref(), *sig)) .collect(); batch::verify_same_signer::<_, V, _>(rng, identity, &entries_refs, 1).is_ok() } @@ -749,7 +750,7 @@ mod tests { let mut rng = StdRng::seed_from_u64(seed); let Fixture { schemes, verifier, .. - } = bls12381_threshold::fixture::(&mut rng, n); + } = bls12381_threshold::fixture::(&mut rng, NAMESPACE, n); (schemes, verifier) } @@ -768,7 +769,12 @@ mod tests { let (polynomial, mut shares) = dkg::deal_anonymous::(&mut rng, Default::default(), NZU32!(4)); shares[0].index = 999; - Scheme::::signer(participants.keys().clone(), polynomial, shares[0].clone()); + Scheme::::signer( + NAMESPACE, + participants.keys().clone(), + polynomial, + shares[0].clone(), + ); } #[test] @@ -786,7 +792,12 @@ mod tests { let mut rng = StdRng::seed_from_u64(7); let participants = ed25519_participants(&mut rng, 5); let (polynomial, shares) = deal_anonymous::(&mut rng, Default::default(), NZU32!(4)); - Scheme::::signer(participants.keys().clone(), polynomial, shares[0].clone()); + Scheme::::signer( + NAMESPACE, + participants.keys().clone(), + polynomial, + shares[0].clone(), + ); } #[test] @@ -805,7 +816,7 @@ mod tests { let mut rng = StdRng::seed_from_u64(7); let participants = ed25519_participants(&mut rng, 5); let (polynomial, _) = deal_anonymous::(&mut rng, Default::default(), NZU32!(4)); - Scheme::::verifier(participants.keys().clone(), polynomial); + Scheme::::verifier(NAMESPACE, participants.keys().clone(), polynomial); } #[test] @@ -839,16 +850,12 @@ mod tests { let proposal = sample_proposal(Epoch::new(0), View::new(2), 1); let notarize_vote = scheme - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal, + }) .unwrap(); assert!(scheme.verify_attestation::<_, Sha256Digest>( &mut rng, - NAMESPACE, Subject::Notarize { proposal: &proposal, }, @@ -856,16 +863,12 @@ mod tests { )); let nullify_vote = scheme - .sign::( - NAMESPACE, - Subject::Nullify { - round: proposal.round, - }, - ) + .sign::(Subject::Nullify { + round: proposal.round, + }) .unwrap(); assert!(scheme.verify_attestation::<_, Sha256Digest>( &mut rng, - NAMESPACE, Subject::Nullify { round: proposal.round, }, @@ -873,16 +876,12 @@ mod tests { )); let finalize_vote = scheme - .sign( - NAMESPACE, - Subject::Finalize { - proposal: &proposal, - }, - ) + .sign(Subject::Finalize { + proposal: &proposal, + }) .unwrap(); assert!(scheme.verify_attestation::<_, Sha256Digest>( &mut rng, - NAMESPACE, Subject::Finalize { proposal: &proposal, }, @@ -902,12 +901,9 @@ mod tests { let proposal = sample_proposal(Epoch::new(0), View::new(3), 2); assert!( verifier - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal, + }) .is_none(), "verifier should not produce signatures" ); @@ -923,16 +919,12 @@ mod tests { let (schemes, verifier) = setup_signers::(4, 11); let proposal = sample_proposal(Epoch::new(0), View::new(3), 2); let vote = schemes[1] - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal, + }) .unwrap(); assert!(verifier.verify_attestation::<_, Sha256Digest>( &mut test_rng(), - NAMESPACE, Subject::Notarize { proposal: &proposal, }, @@ -957,19 +949,15 @@ mod tests { .take(quorum) .map(|scheme| { scheme - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal, + }) .unwrap() }) .collect(); let verification = schemes[0].verify_attestations( &mut rng, - NAMESPACE, Subject::Notarize { proposal: &proposal, }, @@ -981,7 +969,6 @@ mod tests { votes[0].signer = 999; let verification = schemes[0].verify_attestations( &mut rng, - NAMESPACE, Subject::Notarize { proposal: &proposal, }, @@ -1007,12 +994,9 @@ mod tests { .take(quorum - 1) .map(|scheme| { scheme - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal, + }) .unwrap() }) .collect(); @@ -1036,12 +1020,9 @@ mod tests { .take(quorum) .map(|scheme| { scheme - .sign( - NAMESPACE, - Subject::Finalize { - proposal: &proposal, - }, - ) + .sign(Subject::Finalize { + proposal: &proposal, + }) .unwrap() }) .collect(); @@ -1050,7 +1031,6 @@ mod tests { assert!(verifier.verify_certificate( &mut test_rng(), - NAMESPACE, Subject::Finalize { proposal: &proposal, }, @@ -1075,12 +1055,9 @@ mod tests { .take(quorum) .map(|scheme| { scheme - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal, + }) .unwrap() }) .collect(); @@ -1089,7 +1066,6 @@ mod tests { assert!(verifier.verify_certificate( &mut rng, - NAMESPACE, Subject::Notarize { proposal: &proposal, }, @@ -1100,7 +1076,6 @@ mod tests { corrupted.vote_signature = corrupted.seed_signature; assert!(!verifier.verify_certificate( &mut rng, - NAMESPACE, Subject::Notarize { proposal: &proposal, }, @@ -1124,12 +1099,9 @@ mod tests { .take(quorum) .map(|scheme| { scheme - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal, + }) .unwrap() }) .collect(); @@ -1158,12 +1130,9 @@ mod tests { .take(quorum) .map(|scheme| { scheme - .sign( - NAMESPACE, - Subject::Finalize { - proposal: &proposal, - }, - ) + .sign(Subject::Finalize { + proposal: &proposal, + }) .unwrap() }) .collect(); @@ -1193,12 +1162,9 @@ mod tests { .take(quorum) .map(|scheme| { scheme - .sign( - NAMESPACE, - Subject::Finalize { - proposal: &proposal, - }, - ) + .sign(Subject::Finalize { + proposal: &proposal, + }) .unwrap() }) .collect(); @@ -1207,7 +1173,7 @@ mod tests { let seed = Seed::new(proposal.round, certificate.seed_signature); - assert!(seed.verify(&schemes[0], NAMESPACE)); + assert!(seed.verify(&schemes[0])); // Create an invalid seed with a mismatched round let invalid_seed = Seed::new( @@ -1215,7 +1181,7 @@ mod tests { certificate.seed_signature, ); - assert!(!invalid_seed.verify(&schemes[0], NAMESPACE)); + assert!(!invalid_seed.verify(&schemes[0])); } #[test] @@ -1232,7 +1198,7 @@ mod tests { let notarizes: Vec<_> = schemes .iter() .take(quorum) - .map(|scheme| Notarize::sign(scheme, NAMESPACE, proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap()) .collect(); let notarization = Notarization::from_notarizes(&schemes[0], ¬arizes).unwrap(); @@ -1240,13 +1206,13 @@ mod tests { let finalizes: Vec<_> = schemes .iter() .take(quorum) - .map(|scheme| Finalize::sign(scheme, NAMESPACE, proposal.clone()).unwrap()) + .map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap()) .collect(); let finalization = Finalization::from_finalizes(&schemes[0], &finalizes).unwrap(); assert_eq!(notarization.seed(), finalization.seed()); - assert!(notarization.seed().verify(&schemes[0], NAMESPACE)); + assert!(notarization.seed().verify(&schemes[0])); } #[test] @@ -1262,24 +1228,18 @@ mod tests { assert!( signer - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal, + }) .is_some(), "signer should produce votes" ); assert!( verifier - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal, + }) .is_none(), "verifier should not produce votes" ); @@ -1301,33 +1261,27 @@ mod tests { .take(quorum) .map(|scheme| { scheme - .sign( - NAMESPACE, - Subject::Finalize { - proposal: &proposal, - }, - ) + .sign(Subject::Finalize { + proposal: &proposal, + }) .unwrap() }) .collect(); let certificate = schemes[0].assemble(votes).expect("assemble certificate"); - let certificate_verifier = Scheme::::certificate_verifier(*schemes[0].identity()); + let certificate_verifier = + Scheme::::certificate_verifier(NAMESPACE, *schemes[0].identity()); assert!( certificate_verifier - .sign( - NAMESPACE, - Subject::Finalize { - proposal: &proposal, - }, - ) + .sign(Subject::Finalize { + proposal: &proposal, + }) .is_none(), "certificate verifier should not produce votes" ); assert!(certificate_verifier.verify_certificate( &mut test_rng(), - NAMESPACE, Subject::Finalize { proposal: &proposal, }, @@ -1343,20 +1297,17 @@ mod tests { fn certificate_verifier_panics_on_vote() { let (schemes, _) = setup_signers::(4, 37); - let certificate_verifier = Scheme::::certificate_verifier(*schemes[0].identity()); + let certificate_verifier = + Scheme::::certificate_verifier(NAMESPACE, *schemes[0].identity()); let proposal = sample_proposal(Epoch::new(0), View::new(15), 8); let vote = schemes[1] - .sign( - NAMESPACE, - Subject::Finalize { - proposal: &proposal, - }, - ) + .sign(Subject::Finalize { + proposal: &proposal, + }) .unwrap(); certificate_verifier.verify_attestation::<_, Sha256Digest>( &mut test_rng(), - NAMESPACE, Subject::Finalize { proposal: &proposal, }, @@ -1386,12 +1337,9 @@ mod tests { .take(quorum) .map(|scheme| { scheme - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal, + }) .unwrap() }) .collect(); @@ -1418,12 +1366,9 @@ mod tests { .take(quorum) .map(|scheme| { scheme - .sign::( - NAMESPACE, - Subject::Nullify { - round: proposal.round, - }, - ) + .sign::(Subject::Nullify { + round: proposal.round, + }) .unwrap() }) .collect(); @@ -1448,12 +1393,9 @@ mod tests { let proposal = sample_proposal(Epoch::new(0), View::new(23), 12); let vote = scheme - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal, + }) .unwrap(); let notarize_namespace = notarize_namespace(NAMESPACE); @@ -1493,12 +1435,9 @@ mod tests { .take(quorum) .map(|scheme| { scheme - .sign::( - NAMESPACE, - Subject::Nullify { - round: proposal.round, - }, - ) + .sign::(Subject::Nullify { + round: proposal.round, + }) .unwrap() }) .collect(); @@ -1507,7 +1446,6 @@ mod tests { assert!(verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, Subject::Nullify { round: proposal.round, }, @@ -1518,7 +1456,6 @@ mod tests { corrupted.seed_signature = corrupted.vote_signature; assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, Subject::Nullify { round: proposal.round, }, @@ -1544,17 +1481,17 @@ mod tests { let target = Round::new(Epoch::new(333), View::new(10)); // Encrypt using the scheme - let ciphertext = schemes[0].encrypt(&mut rng, NAMESPACE, target, *message); + let ciphertext = schemes[0].encrypt(&mut rng, target, *message); // Can also encrypt with the verifier scheme - let ciphertext_verifier = verifier.encrypt(&mut rng, NAMESPACE, target, *message); + let ciphertext_verifier = verifier.encrypt(&mut rng, target, *message); // Generate notarization for the target round to get the seed let proposal = sample_proposal(target.epoch(), target.view(), 14); let notarizes: Vec<_> = schemes .iter() .take(quorum) - .map(|scheme| Notarize::sign(scheme, NAMESPACE, proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap()) .collect(); let notarization = Notarization::from_notarizes(&schemes[0], ¬arizes).unwrap(); @@ -1580,17 +1517,13 @@ mod tests { let proposal = sample_proposal(Epoch::new(0), View::new(27), 14); let attestation = schemes[0] - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal, + }) .unwrap(); assert!(schemes[0].verify_attestation::<_, Sha256Digest>( &mut rng, - NAMESPACE, Subject::Notarize { proposal: &proposal, }, @@ -1616,7 +1549,6 @@ mod tests { assert!( !schemes[0].verify_attestation::<_, Sha256Digest>( &mut rng, - NAMESPACE, Subject::Notarize { proposal: &proposal, }, @@ -1638,25 +1570,18 @@ mod tests { let proposal = sample_proposal(Epoch::new(0), View::new(29), 15); let attestation1 = schemes[0] - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal, + }) .unwrap(); let attestation2 = schemes[1] - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal, + }) .unwrap(); let verification = schemes[0].verify_attestations( &mut rng, - NAMESPACE, Subject::Notarize { proposal: &proposal, }, @@ -1693,7 +1618,6 @@ mod tests { let verification = schemes[0].verify_attestations( &mut rng, - NAMESPACE, Subject::Notarize { proposal: &proposal, }, @@ -1722,12 +1646,9 @@ mod tests { .take(quorum) .map(|scheme| { scheme - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal, + }) .unwrap() }) .collect(); @@ -1736,7 +1657,6 @@ mod tests { assert!(verifier.verify_certificate( &mut rng, - NAMESPACE, Subject::Notarize { proposal: &proposal, }, @@ -1757,7 +1677,6 @@ mod tests { assert!( !verifier.verify_certificate( &mut rng, - NAMESPACE, Subject::Notarize { proposal: &proposal, }, @@ -1785,12 +1704,9 @@ mod tests { .take(quorum) .map(|scheme| { scheme - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal1, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal1, + }) .unwrap() }) .collect(); @@ -1799,12 +1715,9 @@ mod tests { .take(quorum) .map(|scheme| { scheme - .sign( - NAMESPACE, - Subject::Notarize { - proposal: &proposal2, - }, - ) + .sign(Subject::Notarize { + proposal: &proposal2, + }) .unwrap() }) .collect(); @@ -1814,7 +1727,6 @@ mod tests { assert!(verifier.verify_certificates::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, [ ( Subject::Notarize { @@ -1854,7 +1766,6 @@ mod tests { assert!( !verifier.verify_certificates::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, [ ( Subject::Notarize { diff --git a/consensus/src/simplex/scheme/ed25519.rs b/consensus/src/simplex/scheme/ed25519.rs index 5444460b10..678b98f3ad 100644 --- a/consensus/src/simplex/scheme/ed25519.rs +++ b/consensus/src/simplex/scheme/ed25519.rs @@ -5,7 +5,7 @@ //! contain signer indices alongside individual signatures, enabling secure //! per-validator activity tracking and fault detection. -use crate::simplex::types::Subject; +use crate::simplex::{scheme::Namespace, types::Subject}; use commonware_cryptography::impl_certificate_ed25519; -impl_certificate_ed25519!(Subject<'a, D>); +impl_certificate_ed25519!(Subject<'a, D>, Namespace); diff --git a/consensus/src/simplex/scheme/mod.rs b/consensus/src/simplex/scheme/mod.rs index 62960fbbb8..fdb7225348 100644 --- a/consensus/src/simplex/scheme/mod.rs +++ b/consensus/src/simplex/scheme/mod.rs @@ -35,9 +35,56 @@ pub mod secp256r1; #[cfg(not(target_arch = "wasm32"))] pub mod reporter; +/// Pre-computed namespaces for simplex voting subjects. +/// +/// This struct holds the pre-computed namespace bytes for each vote type. +#[derive(Clone, Debug)] +pub struct Namespace { + /// Namespace for notarize votes/certificates. + pub notarize: Vec, + /// Namespace for nullify votes/certificates. + pub nullify: Vec, + /// Namespace for finalize votes/certificates. + pub finalize: Vec, + /// Namespace for seed signatures (used by threshold schemes). + pub seed: Vec, +} + +impl Namespace { + /// Creates a new SimplexNamespace from a base namespace. + pub fn new(namespace: &[u8]) -> Self { + Self { + notarize: notarize_namespace(namespace), + nullify: nullify_namespace(namespace), + finalize: finalize_namespace(namespace), + seed: seed_namespace(namespace), + } + } +} + +impl certificate::Namespace for Namespace { + fn derive(namespace: &[u8]) -> Self { + Self::new(namespace) + } +} + impl<'a, D: Digest> certificate::Subject for Subject<'a, D> { - fn namespace_and_message(&self, namespace: &[u8]) -> (Bytes, Bytes) { - vote_namespace_and_message(namespace, self) + type Namespace = Namespace; + + fn namespace<'b>(&self, derived: &'b Self::Namespace) -> &'b [u8] { + match self { + Self::Notarize { .. } => &derived.notarize, + Self::Nullify { .. } => &derived.nullify, + Self::Finalize { .. } => &derived.finalize, + } + } + + fn message(&self) -> Bytes { + match self { + Self::Notarize { proposal } => proposal.encode().freeze(), + Self::Nullify { round } => round.encode().freeze(), + Self::Finalize { proposal } => proposal.encode().freeze(), + } } } @@ -87,47 +134,3 @@ pub(crate) fn nullify_namespace(namespace: &[u8]) -> Vec { pub(crate) fn finalize_namespace(namespace: &[u8]) -> Vec { union(namespace, FINALIZE_SUFFIX) } - -/// Produces the vote namespace and message bytes for a given vote context. -/// -/// Returns the final namespace (with the context-specific suffix) and the -/// serialized message to sign or verify. -#[inline] -pub(crate) fn vote_namespace_and_message( - namespace: &[u8], - subject: &Subject<'_, D>, -) -> (Bytes, Bytes) { - match subject { - Subject::Notarize { proposal } => ( - notarize_namespace(namespace).into(), - proposal.encode().freeze(), - ), - Subject::Nullify { round } => { - (nullify_namespace(namespace).into(), round.encode().freeze()) - } - Subject::Finalize { proposal } => ( - finalize_namespace(namespace).into(), - proposal.encode().freeze(), - ), - } -} - -/// Produces the seed namespace and message bytes for a given vote context. -/// -/// Returns the final namespace (with the seed suffix) and the serialized -/// message to sign or verify. -#[inline] -pub(crate) fn seed_namespace_and_message( - namespace: &[u8], - subject: &Subject<'_, D>, -) -> (Bytes, Bytes) { - ( - seed_namespace(namespace).into(), - match subject { - Subject::Notarize { proposal } | Subject::Finalize { proposal } => { - proposal.round.encode().freeze() - } - Subject::Nullify { round } => round.encode().freeze(), - }, - ) -} diff --git a/consensus/src/simplex/scheme/reporter.rs b/consensus/src/simplex/scheme/reporter.rs index af40f66b34..427f2a1227 100644 --- a/consensus/src/simplex/scheme/reporter.rs +++ b/consensus/src/simplex/scheme/reporter.rs @@ -41,8 +41,6 @@ pub struct AttributableReporter< rng: E, /// Signing scheme for verification scheme: S, - /// Namespace for domain separation in verification - namespace: Vec, /// Inner reporter that receives filtered activities reporter: R, /// Whether to always verify peer activities @@ -57,11 +55,10 @@ impl< > AttributableReporter { /// Creates a new `AttributableReporter` that wraps an inner reporter. - pub const fn new(rng: E, scheme: S, namespace: Vec, reporter: R, verify: bool) -> Self { + pub const fn new(rng: E, scheme: S, reporter: R, verify: bool) -> Self { Self { rng, scheme, - namespace, reporter, verify, } @@ -79,10 +76,7 @@ impl< async fn report(&mut self, activity: Self::Activity) { // Verify peer activities if verification is enabled - if self.verify - && !activity.verified() - && !activity.verify(&mut self.rng, &self.scheme, &self.namespace) - { + if self.verify && !activity.verified() && !activity.verify(&mut self.rng, &self.scheme) { // Drop unverified peer activity return; } @@ -176,9 +170,13 @@ mod tests { fn test_invalid_peer_activity_dropped() { // Invalid peer activities should be dropped when verification is enabled let mut rng = StdRng::seed_from_u64(42); + let Fixture { verifier, .. } = ed25519::fixture(&mut rng, NAMESPACE, 4); + + // Create a scheme with wrong namespace to generate invalid signatures let Fixture { - schemes, verifier, .. - } = ed25519::fixture(&mut rng, 4); + schemes: wrong_schemes, + .. + } = ed25519::fixture(&mut rng, b"wrong-namespace", 4); assert!( ed25519::Scheme::is_attributable(), @@ -186,18 +184,14 @@ mod tests { ); let mock = MockReporter::new(); - let mut reporter = - AttributableReporter::new(rng, verifier, NAMESPACE.to_vec(), mock.clone(), true); + let mut reporter = AttributableReporter::new(rng, verifier, mock.clone(), true); - // Create an invalid activity (wrong namespace) + // Create an invalid activity (signed with wrong namespace scheme) let proposal = create_proposal(0, 1); - let attestation = schemes[1] - .sign::( - &[], // Invalid namespace - Subject::Notarize { - proposal: &proposal, - }, - ) + let attestation = wrong_schemes[1] + .sign::(Subject::Notarize { + proposal: &proposal, + }) .expect("signing failed"); let notarize = Notarize { proposal, @@ -215,9 +209,13 @@ mod tests { fn test_skip_verification() { // When verification is disabled, invalid activities pass through let mut rng = StdRng::seed_from_u64(42); + let Fixture { verifier, .. } = ed25519::fixture(&mut rng, NAMESPACE, 4); + + // Create a scheme with wrong namespace to generate invalid signatures let Fixture { - schemes, verifier, .. - } = ed25519::fixture(&mut rng, 4); + schemes: wrong_schemes, + .. + } = ed25519::fixture(&mut rng, b"wrong-namespace", 4); assert!( ed25519::Scheme::is_attributable(), @@ -228,20 +226,16 @@ mod tests { let mut reporter = AttributableReporter::new( rng, verifier, - NAMESPACE.to_vec(), mock.clone(), false, // Disable verification ); - // Create an invalid activity (wrong namespace) + // Create an invalid activity (signed with wrong namespace scheme) let proposal = create_proposal(0, 1); - let attestation = schemes[1] - .sign::( - &[], // Invalid namespace - Subject::Notarize { - proposal: &proposal, - }, - ) + let attestation = wrong_schemes[1] + .sign::(Subject::Notarize { + proposal: &proposal, + }) .expect("signing failed"); let notarize = Notarize { proposal, @@ -263,7 +257,7 @@ mod tests { let mut rng = StdRng::seed_from_u64(42); let Fixture { schemes, verifier, .. - } = bls12381_threshold::fixture::(&mut rng, 4); + } = bls12381_threshold::fixture::(&mut rng, NAMESPACE, 4); assert!( !bls12381_threshold::Scheme::::is_attributable(), @@ -271,8 +265,7 @@ mod tests { ); let mock = MockReporter::new(); - let mut reporter = - AttributableReporter::new(rng, verifier, NAMESPACE.to_vec(), mock.clone(), true); + let mut reporter = AttributableReporter::new(rng, verifier, mock.clone(), true); // Create a certificate from multiple validators let proposal = create_proposal(0, 1); @@ -280,12 +273,9 @@ mod tests { .iter() .map(|scheme| { scheme - .sign::( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign::(Subject::Notarize { + proposal: &proposal, + }) .expect("signing failed") }) .collect(); @@ -314,7 +304,7 @@ mod tests { let mut rng = StdRng::seed_from_u64(42); let Fixture { schemes, verifier, .. - } = bls12381_threshold::fixture::(&mut rng, 4); + } = bls12381_threshold::fixture::(&mut rng, NAMESPACE, 4); assert!( !bls12381_threshold::Scheme::::is_attributable(), @@ -322,18 +312,14 @@ mod tests { ); let mock = MockReporter::new(); - let mut reporter = - AttributableReporter::new(rng, verifier, NAMESPACE.to_vec(), mock.clone(), true); + let mut reporter = AttributableReporter::new(rng, verifier, mock.clone(), true); // Create peer activity (from validator 1) let proposal = create_proposal(0, 1); let attestation = schemes[1] - .sign::( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign::(Subject::Notarize { + proposal: &proposal, + }) .expect("signing failed"); let notarize = Notarize { @@ -354,7 +340,7 @@ mod tests { let mut rng = StdRng::seed_from_u64(42); let Fixture { schemes, verifier, .. - } = ed25519::fixture(&mut rng, 4); + } = ed25519::fixture(&mut rng, NAMESPACE, 4); assert!( ed25519::Scheme::is_attributable(), @@ -362,18 +348,14 @@ mod tests { ); let mock = MockReporter::new(); - let mut reporter = - AttributableReporter::new(rng, verifier, NAMESPACE.to_vec(), mock.clone(), true); + let mut reporter = AttributableReporter::new(rng, verifier, mock.clone(), true); // Create a peer activity (from validator 1) let proposal = create_proposal(0, 1); let attestation = schemes[1] - .sign::( - NAMESPACE, - Subject::Notarize { - proposal: &proposal, - }, - ) + .sign::(Subject::Notarize { + proposal: &proposal, + }) .expect("signing failed"); let notarize = Notarize { diff --git a/consensus/src/simplex/scheme/secp256r1.rs b/consensus/src/simplex/scheme/secp256r1.rs index 56f6748670..5d63e241ee 100644 --- a/consensus/src/simplex/scheme/secp256r1.rs +++ b/consensus/src/simplex/scheme/secp256r1.rs @@ -9,7 +9,7 @@ //! so the batcher will verify signatures immediately as they arrive rather than //! waiting to batch them. -use crate::simplex::types::Subject; +use crate::simplex::{scheme::Namespace, types::Subject}; use commonware_cryptography::impl_certificate_secp256r1; -impl_certificate_secp256r1!(Subject<'a, D>); +impl_certificate_secp256r1!(Subject<'a, D>, Namespace); diff --git a/consensus/src/simplex/types.rs b/consensus/src/simplex/types.rs index 029674e3c5..2872beab7d 100644 --- a/consensus/src/simplex/types.rs +++ b/consensus/src/simplex/types.rs @@ -769,16 +769,13 @@ pub struct Notarize { impl Notarize { /// Signs a notarize vote for the provided proposal. - pub fn sign(scheme: &S, namespace: &[u8], proposal: Proposal) -> Option + pub fn sign(scheme: &S, proposal: Proposal) -> Option where S: scheme::Scheme, { - let attestation = scheme.sign::( - namespace, - Subject::Notarize { - proposal: &proposal, - }, - )?; + let attestation = scheme.sign::(Subject::Notarize { + proposal: &proposal, + })?; Some(Self { proposal, @@ -789,14 +786,13 @@ impl Notarize { /// Verifies the notarize vote against the provided signing scheme. /// /// This ensures that the notarize signature is valid for the claimed proposal. - pub fn verify(&self, rng: &mut R, scheme: &S, namespace: &[u8]) -> bool + pub fn verify(&self, rng: &mut R, scheme: &S) -> bool where R: CryptoRngCore, S: scheme::Scheme, { scheme.verify_attestation::<_, D>( rng, - namespace, Subject::Notarize { proposal: &self.proposal, }, @@ -919,13 +915,12 @@ impl Notarization { /// Verifies the notarization certificate against the provided signing scheme. /// /// This ensures that the certificate is valid for the claimed proposal. - pub fn verify(&self, rng: &mut R, scheme: &S, namespace: &[u8]) -> bool + pub fn verify(&self, rng: &mut R, scheme: &S) -> bool where S: scheme::Scheme, { scheme.verify_certificate::<_, D>( rng, - namespace, Subject::Notarize { proposal: &self.proposal, }, @@ -1036,11 +1031,11 @@ impl Hash for Nullify { impl Nullify { /// Signs a nullify vote for the given round. - pub fn sign(scheme: &S, namespace: &[u8], round: Round) -> Option + pub fn sign(scheme: &S, round: Round) -> Option where S: scheme::Scheme, { - let attestation = scheme.sign::(namespace, Subject::Nullify { round })?; + let attestation = scheme.sign::(Subject::Nullify { round })?; Some(Self { round, attestation }) } @@ -1048,14 +1043,13 @@ impl Nullify { /// Verifies the nullify vote against the provided signing scheme. /// /// This ensures that the nullify signature is valid for the given round. - pub fn verify(&self, rng: &mut R, scheme: &S, namespace: &[u8]) -> bool + pub fn verify(&self, rng: &mut R, scheme: &S) -> bool where R: CryptoRngCore, S: scheme::Scheme, { scheme.verify_attestation::<_, D>( rng, - namespace, Subject::Nullify { round: self.round }, &self.attestation, ) @@ -1147,18 +1141,12 @@ impl Nullification { /// Verifies the nullification certificate against the provided signing scheme. /// /// This ensures that the certificate is valid for the claimed round. - pub fn verify( - &self, - rng: &mut R, - scheme: &S, - namespace: &[u8], - ) -> bool + pub fn verify(&self, rng: &mut R, scheme: &S) -> bool where S: scheme::Scheme, { scheme.verify_certificate::<_, D>( rng, - namespace, Subject::Nullify { round: self.round }, &self.certificate, ) @@ -1246,16 +1234,13 @@ pub struct Finalize { impl Finalize { /// Signs a finalize vote for the provided proposal. - pub fn sign(scheme: &S, namespace: &[u8], proposal: Proposal) -> Option + pub fn sign(scheme: &S, proposal: Proposal) -> Option where S: scheme::Scheme, { - let attestation = scheme.sign::( - namespace, - Subject::Finalize { - proposal: &proposal, - }, - )?; + let attestation = scheme.sign::(Subject::Finalize { + proposal: &proposal, + })?; Some(Self { proposal, @@ -1266,14 +1251,13 @@ impl Finalize { /// Verifies the finalize vote against the provided signing scheme. /// /// This ensures that the finalize signature is valid for the claimed proposal. - pub fn verify(&self, rng: &mut R, scheme: &S, namespace: &[u8]) -> bool + pub fn verify(&self, rng: &mut R, scheme: &S) -> bool where R: CryptoRngCore, S: scheme::Scheme, { scheme.verify_attestation::<_, D>( rng, - namespace, Subject::Finalize { proposal: &self.proposal, }, @@ -1396,13 +1380,12 @@ impl Finalization { /// Verifies the finalization certificate against the provided signing scheme. /// /// This ensures that the certificate is valid for the claimed proposal. - pub fn verify(&self, rng: &mut R, scheme: &S, namespace: &[u8]) -> bool + pub fn verify(&self, rng: &mut R, scheme: &S) -> bool where S: scheme::Scheme, { scheme.verify_certificate::<_, D>( rng, - namespace, Subject::Finalize { proposal: &self.proposal, }, @@ -1666,7 +1649,7 @@ impl Response { } /// Verifies the certificates contained in this response against the signing scheme. - pub fn verify(&self, rng: &mut R, scheme: &S, namespace: &[u8]) -> bool + pub fn verify(&self, rng: &mut R, scheme: &S) -> bool where S: scheme::Scheme, { @@ -1691,7 +1674,7 @@ impl Response { (context, &nullification.certificate) }); - scheme.verify_certificates::<_, D, _>(rng, namespace, notarizations.chain(nullifications)) + scheme.verify_certificates::<_, D, _>(rng, notarizations.chain(nullifications)) } } @@ -1900,21 +1883,21 @@ impl Activity { /// This method **always** performs verification regardless of whether the activity has been /// previously verified. Callers can use [`Activity::verified`] to check if verification is /// necessary before calling this method. - pub fn verify(&self, rng: &mut R, scheme: &S, namespace: &[u8]) -> bool + pub fn verify(&self, rng: &mut R, scheme: &S) -> bool where S: scheme::Scheme, { match self { - Self::Notarize(n) => n.verify(rng, scheme, namespace), - Self::Notarization(n) => n.verify(rng, scheme, namespace), - Self::Certification(n) => n.verify(rng, scheme, namespace), - Self::Nullify(n) => n.verify(rng, scheme, namespace), - Self::Nullification(n) => n.verify(rng, scheme, namespace), - Self::Finalize(f) => f.verify(rng, scheme, namespace), - Self::Finalization(f) => f.verify(rng, scheme, namespace), - Self::ConflictingNotarize(c) => c.verify(rng, scheme, namespace), - Self::ConflictingFinalize(c) => c.verify(rng, scheme, namespace), - Self::NullifyFinalize(c) => c.verify(rng, scheme, namespace), + Self::Notarize(n) => n.verify(rng, scheme), + Self::Notarization(n) => n.verify(rng, scheme), + Self::Certification(n) => n.verify(rng, scheme), + Self::Nullify(n) => n.verify(rng, scheme), + Self::Nullification(n) => n.verify(rng, scheme), + Self::Finalize(f) => f.verify(rng, scheme), + Self::Finalization(f) => f.verify(rng, scheme), + Self::ConflictingNotarize(c) => c.verify(rng, scheme), + Self::ConflictingFinalize(c) => c.verify(rng, scheme), + Self::NullifyFinalize(c) => c.verify(rng, scheme), } } } @@ -2164,13 +2147,12 @@ impl ConflictingNotarize { } /// Verifies that both conflicting signatures are valid, proving Byzantine behavior. - pub fn verify(&self, rng: &mut R, scheme: &S, namespace: &[u8]) -> bool + pub fn verify(&self, rng: &mut R, scheme: &S) -> bool where R: CryptoRngCore, S: scheme::Scheme, { - self.notarize_1.verify(rng, scheme, namespace) - && self.notarize_2.verify(rng, scheme, namespace) + self.notarize_1.verify(rng, scheme) && self.notarize_2.verify(rng, scheme) } } @@ -2280,13 +2262,12 @@ impl ConflictingFinalize { } /// Verifies that both conflicting signatures are valid, proving Byzantine behavior. - pub fn verify(&self, rng: &mut R, scheme: &S, namespace: &[u8]) -> bool + pub fn verify(&self, rng: &mut R, scheme: &S) -> bool where R: CryptoRngCore, S: scheme::Scheme, { - self.finalize_1.verify(rng, scheme, namespace) - && self.finalize_2.verify(rng, scheme, namespace) + self.finalize_1.verify(rng, scheme) && self.finalize_2.verify(rng, scheme) } } @@ -2394,12 +2375,12 @@ impl NullifyFinalize { } /// Verifies that both the nullify and finalize signatures are valid, proving Byzantine behavior. - pub fn verify(&self, rng: &mut R, scheme: &S, namespace: &[u8]) -> bool + pub fn verify(&self, rng: &mut R, scheme: &S) -> bool where R: CryptoRngCore, S: scheme::Scheme, { - self.nullify.verify(rng, scheme, namespace) && self.finalize.verify(rng, scheme, namespace) + self.nullify.verify(rng, scheme) && self.finalize.verify(rng, scheme) } } @@ -2493,10 +2474,18 @@ mod tests { /// Generate a fixture using the provided generator function with a specific seed. fn setup_seeded(n: u32, seed: u64, fixture: F) -> Fixture where - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, + { + setup_seeded_ns(n, seed, NAMESPACE, fixture) + } + + /// Generate a fixture using the provided generator function with a specific seed and namespace. + fn setup_seeded_ns(n: u32, seed: u64, namespace: &[u8], fixture: F) -> Fixture + where + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = StdRng::seed_from_u64(seed); - fixture(&mut rng, n) + fixture(&mut rng, namespace, n) } #[test] @@ -2514,19 +2503,19 @@ mod tests { fn notarize_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 5); + let fixture = fixture(&mut rng, NAMESPACE, 5); let round = Round::new(Epoch::new(0), View::new(10)); let proposal = Proposal::new(round, View::new(5), sample_digest(1)); - let notarize = Notarize::sign(&fixture.schemes[0], NAMESPACE, proposal).unwrap(); + let notarize = Notarize::sign(&fixture.schemes[0], proposal).unwrap(); let encoded = notarize.encode(); let decoded = Notarize::decode(encoded).unwrap(); assert_eq!(notarize, decoded); - assert!(decoded.verify(&mut rng, &fixture.schemes[0], NAMESPACE)); + assert!(decoded.verify(&mut rng, &fixture.schemes[0])); } #[test] @@ -2542,10 +2531,10 @@ mod tests { fn notarization_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 5); + let fixture = fixture(&mut rng, NAMESPACE, 5); let proposal = Proposal::new( Round::new(Epoch::new(0), View::new(10)), View::new(5), @@ -2554,14 +2543,14 @@ mod tests { let notarizes: Vec<_> = fixture .schemes .iter() - .map(|scheme| Notarize::sign(scheme, NAMESPACE, proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap()) .collect(); let notarization = Notarization::from_notarizes(&fixture.schemes[0], ¬arizes).unwrap(); let encoded = notarization.encode(); let cfg = fixture.schemes[0].certificate_codec_config(); let decoded = Notarization::decode_cfg(encoded, &cfg).unwrap(); assert_eq!(notarization, decoded); - assert!(decoded.verify(&mut OsRng, &fixture.schemes[0], NAMESPACE)); + assert!(decoded.verify(&mut OsRng, &fixture.schemes[0])); } #[test] @@ -2577,16 +2566,16 @@ mod tests { fn nullify_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 5); + let fixture = fixture(&mut rng, NAMESPACE, 5); let round = Round::new(Epoch::new(0), View::new(10)); - let nullify = Nullify::sign::(&fixture.schemes[0], NAMESPACE, round).unwrap(); + let nullify = Nullify::sign::(&fixture.schemes[0], round).unwrap(); let encoded = nullify.encode(); let decoded = Nullify::decode(encoded).unwrap(); assert_eq!(nullify, decoded); - assert!(decoded.verify::<_, Sha256>(&mut rng, &fixture.schemes[0], NAMESPACE)); + assert!(decoded.verify::<_, Sha256>(&mut rng, &fixture.schemes[0])); } #[test] @@ -2602,22 +2591,22 @@ mod tests { fn nullification_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 5); + let fixture = fixture(&mut rng, NAMESPACE, 5); let round = Round::new(Epoch::new(333), View::new(10)); let nullifies: Vec<_> = fixture .schemes .iter() - .map(|scheme| Nullify::sign::(scheme, NAMESPACE, round).unwrap()) + .map(|scheme| Nullify::sign::(scheme, round).unwrap()) .collect(); let nullification = Nullification::from_nullifies(&fixture.schemes[0], &nullifies).unwrap(); let encoded = nullification.encode(); let cfg = fixture.schemes[0].certificate_codec_config(); let decoded = Nullification::decode_cfg(encoded, &cfg).unwrap(); assert_eq!(nullification, decoded); - assert!(decoded.verify::<_, Sha256>(&mut OsRng, &fixture.schemes[0], NAMESPACE)); + assert!(decoded.verify::<_, Sha256>(&mut OsRng, &fixture.schemes[0])); } #[test] @@ -2633,17 +2622,17 @@ mod tests { fn finalize_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 5); + let fixture = fixture(&mut rng, NAMESPACE, 5); let round = Round::new(Epoch::new(0), View::new(10)); let proposal = Proposal::new(round, View::new(5), sample_digest(1)); - let finalize = Finalize::sign(&fixture.schemes[0], NAMESPACE, proposal).unwrap(); + let finalize = Finalize::sign(&fixture.schemes[0], proposal).unwrap(); let encoded = finalize.encode(); let decoded = Finalize::decode(encoded).unwrap(); assert_eq!(finalize, decoded); - assert!(decoded.verify(&mut rng, &fixture.schemes[0], NAMESPACE)); + assert!(decoded.verify(&mut rng, &fixture.schemes[0])); } #[test] @@ -2659,23 +2648,23 @@ mod tests { fn finalization_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 5); + let fixture = fixture(&mut rng, NAMESPACE, 5); let round = Round::new(Epoch::new(0), View::new(10)); let proposal = Proposal::new(round, View::new(5), sample_digest(1)); let finalizes: Vec<_> = fixture .schemes .iter() - .map(|scheme| Finalize::sign(scheme, NAMESPACE, proposal.clone()).unwrap()) + .map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap()) .collect(); let finalization = Finalization::from_finalizes(&fixture.schemes[0], &finalizes).unwrap(); let encoded = finalization.encode(); let cfg = fixture.schemes[0].certificate_codec_config(); let decoded = Finalization::decode_cfg(encoded, &cfg).unwrap(); assert_eq!(finalization, decoded); - assert!(decoded.verify(&mut OsRng, &fixture.schemes[0], NAMESPACE)); + assert!(decoded.verify(&mut OsRng, &fixture.schemes[0])); } #[test] @@ -2691,10 +2680,10 @@ mod tests { fn backfiller_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 5); + let fixture = fixture(&mut rng, NAMESPACE, 5); let cfg = fixture.schemes[0].certificate_codec_config(); let request = Request::new( 1, @@ -2712,14 +2701,14 @@ mod tests { let notarizes: Vec<_> = fixture .schemes .iter() - .map(|scheme| Notarize::sign(scheme, NAMESPACE, proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap()) .collect(); let notarization = Notarization::from_notarizes(&fixture.schemes[0], ¬arizes).unwrap(); let nullifies: Vec<_> = fixture .schemes .iter() - .map(|scheme| Nullify::sign::(scheme, NAMESPACE, round).unwrap()) + .map(|scheme| Nullify::sign::(scheme, round).unwrap()) .collect(); let nullification = Nullification::from_nullifies(&fixture.schemes[0], &nullifies).unwrap(); @@ -2755,24 +2744,24 @@ mod tests { fn response_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 5); + let fixture = fixture(&mut rng, NAMESPACE, 5); let round = Round::new(Epoch::new(0), View::new(10)); let proposal = Proposal::new(round, View::new(5), sample_digest(1)); let notarizes: Vec<_> = fixture .schemes .iter() - .map(|scheme| Notarize::sign(scheme, NAMESPACE, proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap()) .collect(); let notarization = Notarization::from_notarizes(&fixture.schemes[0], ¬arizes).unwrap(); let nullifies: Vec<_> = fixture .schemes .iter() - .map(|scheme| Nullify::sign::(scheme, NAMESPACE, round).unwrap()) + .map(|scheme| Nullify::sign::(scheme, round).unwrap()) .collect(); let nullification = Nullification::from_nullifies(&fixture.schemes[0], &nullifies).unwrap(); @@ -2785,13 +2774,13 @@ mod tests { assert_eq!(response.nullifications.len(), decoded.nullifications.len()); let mut rng = OsRng; - assert!(decoded.verify(&mut rng, &fixture.schemes[0], NAMESPACE)); + assert!(decoded.verify(&mut rng, &fixture.schemes[0])); decoded.nullifications[0].round = Round::new( decoded.nullifications[0].round.epoch(), decoded.nullifications[0].round.view().next(), ); - assert!(!decoded.verify(&mut rng, &fixture.schemes[0], NAMESPACE)); + assert!(!decoded.verify(&mut rng, &fixture.schemes[0])); } #[test] @@ -2807,10 +2796,10 @@ mod tests { fn conflicting_notarize_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 5); + let fixture = fixture(&mut rng, NAMESPACE, 5); let proposal1 = Proposal::new( Round::new(Epoch::new(0), View::new(10)), View::new(5), @@ -2821,15 +2810,15 @@ mod tests { View::new(5), sample_digest(2), ); - let notarize1 = Notarize::sign(&fixture.schemes[0], NAMESPACE, proposal1).unwrap(); - let notarize2 = Notarize::sign(&fixture.schemes[0], NAMESPACE, proposal2).unwrap(); + let notarize1 = Notarize::sign(&fixture.schemes[0], proposal1).unwrap(); + let notarize2 = Notarize::sign(&fixture.schemes[0], proposal2).unwrap(); let conflicting = ConflictingNotarize::new(notarize1, notarize2); let encoded = conflicting.encode(); let decoded = ConflictingNotarize::::decode(encoded).unwrap(); assert_eq!(conflicting, decoded); - assert!(decoded.verify(&mut rng, &fixture.schemes[0], NAMESPACE)); + assert!(decoded.verify(&mut rng, &fixture.schemes[0])); } #[test] @@ -2845,10 +2834,10 @@ mod tests { fn conflicting_finalize_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 5); + let fixture = fixture(&mut rng, NAMESPACE, 5); let proposal1 = Proposal::new( Round::new(Epoch::new(0), View::new(10)), View::new(5), @@ -2859,15 +2848,15 @@ mod tests { View::new(5), sample_digest(2), ); - let finalize1 = Finalize::sign(&fixture.schemes[0], NAMESPACE, proposal1).unwrap(); - let finalize2 = Finalize::sign(&fixture.schemes[0], NAMESPACE, proposal2).unwrap(); + let finalize1 = Finalize::sign(&fixture.schemes[0], proposal1).unwrap(); + let finalize2 = Finalize::sign(&fixture.schemes[0], proposal2).unwrap(); let conflicting = ConflictingFinalize::new(finalize1, finalize2); let encoded = conflicting.encode(); let decoded = ConflictingFinalize::::decode(encoded).unwrap(); assert_eq!(conflicting, decoded); - assert!(decoded.verify(&mut rng, &fixture.schemes[0], NAMESPACE)); + assert!(decoded.verify(&mut rng, &fixture.schemes[0])); } #[test] @@ -2883,21 +2872,21 @@ mod tests { fn nullify_finalize_encode_decode(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 5); + let fixture = fixture(&mut rng, NAMESPACE, 5); let round = Round::new(Epoch::new(0), View::new(10)); let proposal = Proposal::new(round, View::new(5), sample_digest(1)); - let nullify = Nullify::sign::(&fixture.schemes[0], NAMESPACE, round).unwrap(); - let finalize = Finalize::sign(&fixture.schemes[0], NAMESPACE, proposal).unwrap(); + let nullify = Nullify::sign::(&fixture.schemes[0], round).unwrap(); + let finalize = Finalize::sign(&fixture.schemes[0], proposal).unwrap(); let conflict = NullifyFinalize::new(nullify, finalize); let encoded = conflict.encode(); let decoded = NullifyFinalize::::decode(encoded).unwrap(); assert_eq!(conflict, decoded); - assert!(decoded.verify(&mut rng, &fixture.schemes[0], NAMESPACE)); + assert!(decoded.verify(&mut rng, &fixture.schemes[0])); } #[test] @@ -2910,19 +2899,21 @@ mod tests { nullify_finalize_encode_decode(bls12381_threshold::fixture::); } - fn notarize_verify_wrong_namespace(fixture: F) + fn notarize_verify_wrong_namespace(f: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: Fn(&mut StdRng, &[u8], u32) -> Fixture, { + // Create two fixtures with different namespaces let mut rng = test_rng(); - let fixture = fixture(&mut rng, 5); + let fixture = setup_seeded_ns(5, 0, NAMESPACE, &f); + let wrong_fixture = setup_seeded_ns(5, 0, b"wrong_namespace", &f); let round = Round::new(Epoch::new(0), View::new(10)); let proposal = Proposal::new(round, View::new(5), sample_digest(1)); - let notarize = Notarize::sign(&fixture.schemes[0], NAMESPACE, proposal).unwrap(); + let notarize = Notarize::sign(&fixture.schemes[0], proposal).unwrap(); - assert!(notarize.verify(&mut rng, &fixture.schemes[0], NAMESPACE)); - assert!(!notarize.verify(&mut rng, &fixture.schemes[0], b"wrong_namespace")); + assert!(notarize.verify(&mut rng, &fixture.schemes[0])); + assert!(!notarize.verify(&mut rng, &wrong_fixture.schemes[0])); } #[test] @@ -2938,17 +2929,17 @@ mod tests { fn notarize_verify_wrong_scheme(f: F) where S: Scheme, - F: Fn(&mut StdRng, u32) -> Fixture, + F: Fn(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); let fixture = setup_seeded(5, 0, &f); let wrong_fixture = setup_seeded(5, 1, &f); let round = Round::new(Epoch::new(0), View::new(10)); let proposal = Proposal::new(round, View::new(5), sample_digest(2)); - let notarize = Notarize::sign(&fixture.schemes[0], NAMESPACE, proposal).unwrap(); + let notarize = Notarize::sign(&fixture.schemes[0], proposal).unwrap(); - assert!(notarize.verify(&mut rng, &fixture.schemes[0], NAMESPACE)); - assert!(!notarize.verify(&mut rng, &wrong_fixture.verifier, NAMESPACE)); + assert!(notarize.verify(&mut rng, &fixture.schemes[0])); + assert!(!notarize.verify(&mut rng, &wrong_fixture.verifier)); } #[test] @@ -2964,7 +2955,7 @@ mod tests { fn notarization_verify_wrong_scheme(f: F) where S: Scheme, - F: Fn(&mut StdRng, u32) -> Fixture, + F: Fn(&mut StdRng, &[u8], u32) -> Fixture, { let fixture = setup_seeded(5, 0, &f); let wrong_fixture = setup_seeded(5, 1, &f); @@ -2975,16 +2966,16 @@ mod tests { .schemes .iter() .take(quorum) - .map(|scheme| Notarize::sign(scheme, NAMESPACE, proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap()) .collect(); let notarization = Notarization::from_notarizes(&fixture.schemes[0], ¬arizes) .expect("quorum notarization"); let mut rng = OsRng; - assert!(notarization.verify(&mut rng, &fixture.schemes[0], NAMESPACE)); + assert!(notarization.verify(&mut rng, &fixture.schemes[0])); let mut rng = OsRng; - assert!(!notarization.verify(&mut rng, &wrong_fixture.verifier, NAMESPACE)); + assert!(!notarization.verify(&mut rng, &wrong_fixture.verifier)); } #[test] @@ -2997,13 +2988,15 @@ mod tests { notarization_verify_wrong_scheme(bls12381_threshold::fixture::); } - fn notarization_verify_wrong_namespace(fixture: F) + fn notarization_verify_wrong_namespace(f: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: Fn(&mut StdRng, &[u8], u32) -> Fixture, { + // Create two fixtures with different namespaces + let fixture = setup_seeded_ns(5, 0, NAMESPACE, &f); + let wrong_fixture = setup_seeded_ns(5, 0, b"wrong_namespace", &f); let mut rng = test_rng(); - let fixture = fixture(&mut rng, 5); let round = Round::new(Epoch::new(0), View::new(10)); let proposal = Proposal::new(round, View::new(5), sample_digest(4)); let quorum = quorum_from_slice(&fixture.schemes) as usize; @@ -3011,16 +3004,14 @@ mod tests { .schemes .iter() .take(quorum) - .map(|scheme| Notarize::sign(scheme, NAMESPACE, proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap()) .collect(); let notarization = Notarization::from_notarizes(&fixture.schemes[0], ¬arizes) .expect("quorum notarization"); - let mut rng = OsRng; - assert!(notarization.verify(&mut rng, &fixture.schemes[0], NAMESPACE)); + assert!(notarization.verify(&mut rng, &fixture.schemes[0])); - let mut rng = OsRng; - assert!(!notarization.verify(&mut rng, &fixture.schemes[0], b"wrong_namespace")); + assert!(!notarization.verify(&mut rng, &wrong_fixture.schemes[0])); } #[test] @@ -3036,10 +3027,10 @@ mod tests { fn notarization_recover_insufficient_signatures(fixture: F) where S: Scheme, - F: FnOnce(&mut StdRng, u32) -> Fixture, + F: FnOnce(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); - let fixture = fixture(&mut rng, 5); + let fixture = fixture(&mut rng, NAMESPACE, 5); let quorum_size = quorum(fixture.schemes.len() as u32) as usize; assert!(quorum_size > 1, "test requires quorum larger than one"); let round = Round::new(Epoch::new(0), View::new(10)); @@ -3048,7 +3039,7 @@ mod tests { .schemes .iter() .take(quorum_size - 1) - .map(|scheme| Notarize::sign(scheme, NAMESPACE, proposal.clone()).unwrap()) + .map(|scheme| Notarize::sign(scheme, proposal.clone()).unwrap()) .collect(); assert!( @@ -3070,22 +3061,24 @@ mod tests { fn conflicting_notarize_detection(f: F) where S: Scheme, - F: Fn(&mut StdRng, u32) -> Fixture, + F: Fn(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); let fixture = setup_seeded(5, 0, &f); - let wrong_fixture = setup_seeded(5, 1, &f); + let wrong_ns_fixture = setup_seeded_ns(5, 0, b"wrong_namespace", &f); + let wrong_scheme_fixture = setup_seeded(5, 1, &f); + let round = Round::new(Epoch::new(0), View::new(10)); let proposal1 = Proposal::new(round, View::new(5), sample_digest(6)); let proposal2 = Proposal::new(round, View::new(5), sample_digest(7)); - let notarize1 = Notarize::sign(&fixture.schemes[0], NAMESPACE, proposal1).unwrap(); - let notarize2 = Notarize::sign(&fixture.schemes[0], NAMESPACE, proposal2).unwrap(); + let notarize1 = Notarize::sign(&fixture.schemes[0], proposal1).unwrap(); + let notarize2 = Notarize::sign(&fixture.schemes[0], proposal2).unwrap(); let conflict = ConflictingNotarize::new(notarize1, notarize2); - assert!(conflict.verify(&mut rng, &fixture.schemes[0], NAMESPACE)); - assert!(!conflict.verify(&mut rng, &fixture.schemes[0], b"wrong_namespace")); - assert!(!conflict.verify(&mut rng, &wrong_fixture.verifier, NAMESPACE)); + assert!(conflict.verify(&mut rng, &fixture.schemes[0])); + assert!(!conflict.verify(&mut rng, &wrong_ns_fixture.schemes[0])); + assert!(!conflict.verify(&mut rng, &wrong_scheme_fixture.verifier)); } #[test] @@ -3101,21 +3094,23 @@ mod tests { fn nullify_finalize_detection(f: F) where S: Scheme, - F: Fn(&mut StdRng, u32) -> Fixture, + F: Fn(&mut StdRng, &[u8], u32) -> Fixture, { let mut rng = test_rng(); let fixture = setup_seeded(5, 0, &f); - let wrong_fixture = setup_seeded(5, 1, &f); + let wrong_ns_fixture = setup_seeded_ns(5, 0, b"wrong_namespace", &f); + let wrong_scheme_fixture = setup_seeded(5, 1, &f); + let round = Round::new(Epoch::new(0), View::new(10)); let proposal = Proposal::new(round, View::new(5), sample_digest(8)); - let nullify = Nullify::sign::(&fixture.schemes[0], NAMESPACE, round).unwrap(); - let finalize = Finalize::sign(&fixture.schemes[0], NAMESPACE, proposal).unwrap(); + let nullify = Nullify::sign::(&fixture.schemes[0], round).unwrap(); + let finalize = Finalize::sign(&fixture.schemes[0], proposal).unwrap(); let conflict = NullifyFinalize::new(nullify, finalize); - assert!(conflict.verify(&mut rng, &fixture.schemes[0], NAMESPACE)); - assert!(!conflict.verify(&mut rng, &fixture.schemes[0], b"wrong_namespace")); - assert!(!conflict.verify(&mut rng, &wrong_fixture.verifier, NAMESPACE)); + assert!(conflict.verify(&mut rng, &fixture.schemes[0])); + assert!(!conflict.verify(&mut rng, &wrong_ns_fixture.schemes[0])); + assert!(!conflict.verify(&mut rng, &wrong_scheme_fixture.verifier)); } #[test] @@ -3131,7 +3126,7 @@ mod tests { fn finalization_verify_wrong_scheme(f: F) where S: Scheme, - F: Fn(&mut StdRng, u32) -> Fixture, + F: Fn(&mut StdRng, &[u8], u32) -> Fixture, { let fixture = setup_seeded(5, 0, &f); let wrong_fixture = setup_seeded(5, 1, &f); @@ -3142,16 +3137,16 @@ mod tests { .schemes .iter() .take(quorum) - .map(|scheme| Finalize::sign(scheme, NAMESPACE, proposal.clone()).unwrap()) + .map(|scheme| Finalize::sign(scheme, proposal.clone()).unwrap()) .collect(); let finalization = Finalization::from_finalizes(&fixture.schemes[0], &finalizes) .expect("quorum finalization"); let mut rng = OsRng; - assert!(finalization.verify(&mut rng, &fixture.schemes[0], NAMESPACE)); + assert!(finalization.verify(&mut rng, &fixture.schemes[0])); let mut rng = OsRng; - assert!(!finalization.verify(&mut rng, &wrong_fixture.verifier, NAMESPACE)); + assert!(!finalization.verify(&mut rng, &wrong_fixture.verifier)); } #[test] diff --git a/cryptography/src/bls12381/certificate/multisig/mocks.rs b/cryptography/src/bls12381/certificate/multisig/mocks.rs index 4a58d6c3ba..7337d4fe75 100644 --- a/cryptography/src/bls12381/certificate/multisig/mocks.rs +++ b/cryptography/src/bls12381/certificate/multisig/mocks.rs @@ -12,9 +12,10 @@ use rand::{CryptoRng, RngCore}; /// Builds ed25519 identities and matching BLS12-381 multisig schemes. pub fn fixture( rng: &mut R, + namespace: &[u8], n: u32, - signer: impl Fn(BiMap, Private) -> Option, - verifier: impl Fn(BiMap) -> S, + signer: impl Fn(&[u8], BiMap, Private) -> Option, + verifier: impl Fn(&[u8], BiMap) -> S, ) -> Fixture where V: Variant, @@ -50,9 +51,11 @@ where let schemes = bls_privates .into_iter() - .map(|sk| signer(signers.clone(), sk).expect("scheme signer must be a participant")) + .map(|sk| { + signer(namespace, signers.clone(), sk).expect("scheme signer must be a participant") + }) .collect(); - let verifier = verifier(signers); + let verifier = verifier(namespace, signers); Fixture { participants: participants_vec, diff --git a/cryptography/src/bls12381/certificate/multisig/mod.rs b/cryptography/src/bls12381/certificate/multisig/mod.rs index 158ad100da..102bf39606 100644 --- a/cryptography/src/bls12381/certificate/multisig/mod.rs +++ b/cryptography/src/bls12381/certificate/multisig/mod.rs @@ -12,7 +12,7 @@ use crate::{ ops::{self, aggregate, batch}, variant::Variant, }, - certificate::{Attestation, Scheme, Signers, Subject, Verification}, + certificate::{Attestation, Namespace, Scheme, Signers, Subject, Verification}, Digest, PublicKey, }; #[cfg(not(feature = "std"))] @@ -30,14 +30,16 @@ use std::collections::BTreeSet; /// context types. It can be reused across different protocols (simplex, aggregation, etc.) /// by wrapping it with protocol-specific trait implementations via the macro. #[derive(Clone, Debug)] -pub struct Generic { +pub struct Generic { /// Participants in the committee. pub participants: BiMap, /// Key used for generating signatures. pub signer: Option<(u32, Private)>, + /// Pre-computed namespace(s) for this subject type. + pub namespace: N, } -impl Generic { +impl Generic { /// Creates a new scheme instance with the provided key material. /// /// Participants have both an identity key and a signing key. The identity key @@ -46,7 +48,11 @@ impl Generic { /// /// Returns `None` if the provided private key does not match any signing key /// in the participant set. - pub fn signer(participants: BiMap, private_key: Private) -> Option { + pub fn signer( + namespace: &[u8], + participants: BiMap, + private_key: Private, + ) -> Option { let public_key = ops::compute_public::(&private_key); let signer = participants .values() @@ -57,6 +63,7 @@ impl Generic { Some(Self { participants, signer: Some(signer), + namespace: N::derive(namespace), }) } @@ -65,10 +72,11 @@ impl Generic { /// Participants have both an identity key and a signing key. The identity key /// is used for participant set ordering and indexing, while the signing key is used for /// verification. - pub const fn verifier(participants: BiMap) -> Self { + pub fn verifier(namespace: &[u8], participants: BiMap) -> Self { Self { participants, signer: None, + namespace: N::derive(namespace), } } @@ -83,15 +91,19 @@ impl Generic { } /// Signs a subject and returns the attestation. - pub fn sign(&self, namespace: &[u8], subject: S::Subject<'_, D>) -> Option> + pub fn sign<'a, S, D>(&self, subject: S::Subject<'a, D>) -> Option> where S: Scheme, + S::Subject<'a, D>: Subject, D: Digest, { let (index, private_key) = self.signer.as_ref()?; - let (namespace, message) = subject.namespace_and_message(namespace); - let signature = ops::sign_message::(private_key, namespace.as_ref(), message.as_ref()); + let signature = ops::sign_message::( + private_key, + subject.namespace(&self.namespace), + &subject.message(), + ); Some(Attestation { signer: *index, @@ -100,40 +112,39 @@ impl Generic { } /// Verifies a single attestation from a signer. - pub fn verify_attestation( + pub fn verify_attestation<'a, S, D>( &self, - namespace: &[u8], - subject: S::Subject<'_, D>, + subject: S::Subject<'a, D>, attestation: &Attestation, ) -> bool where S: Scheme, + S::Subject<'a, D>: Subject, D: Digest, { let Some(public_key) = self.participants.value(attestation.signer as usize) else { return false; }; - let (namespace, message) = subject.namespace_and_message(namespace); ops::verify_message::( public_key, - namespace.as_ref(), - message.as_ref(), + subject.namespace(&self.namespace), + &subject.message(), &attestation.signature, ) .is_ok() } /// Batch-verifies attestations and returns verified attestations and invalid signers. - pub fn verify_attestations( + pub fn verify_attestations<'a, S, R, D, I>( &self, rng: &mut R, - namespace: &[u8], - subject: S::Subject<'_, D>, + subject: S::Subject<'a, D>, attestations: I, ) -> Verification where S: Scheme, + S::Subject<'a, D>: Subject, R: CryptoRngCore, D: Digest, I: IntoIterator>, @@ -157,9 +168,10 @@ impl Generic { } // Verify attestations and return any invalid ones. - let (namespace, message) = subject.namespace_and_message(namespace); + let namespace = subject.namespace(&self.namespace); + let message = subject.message(); let invalid_indices = - batch::verify_same_message::<_, V>(rng, namespace.as_ref(), message.as_ref(), &entries); + batch::verify_same_message::<_, V>(rng, namespace, message.as_ref(), &entries); // Mark invalid attestations. for idx in invalid_indices { @@ -203,15 +215,15 @@ impl Generic { } /// Verifies a certificate. - pub fn verify_certificate( + pub fn verify_certificate<'a, S, R, D>( &self, _rng: &mut R, - namespace: &[u8], - subject: S::Subject<'_, D>, + subject: S::Subject<'a, D>, certificate: &Certificate, ) -> bool where S: Scheme, + S::Subject<'a, D>: Subject, R: CryptoRngCore, D: Digest, { @@ -236,32 +248,27 @@ impl Generic { } // Verify the aggregate signature. - let (namespace, message) = subject.namespace_and_message(namespace); let agg_public = aggregate::combine_public_keys::(&publics); aggregate::verify_same_message::( &agg_public, - namespace.as_ref(), - message.as_ref(), + subject.namespace(&self.namespace), + &subject.message(), &certificate.signature, ) .is_ok() } /// Verifies multiple certificates (no batch optimization for BLS multisig). - pub fn verify_certificates<'a, S, R, D, I>( - &self, - rng: &mut R, - namespace: &[u8], - certificates: I, - ) -> bool + pub fn verify_certificates<'a, S, R, D, I>(&self, rng: &mut R, certificates: I) -> bool where S: Scheme, + S::Subject<'a, D>: Subject, R: CryptoRngCore, D: Digest, I: Iterator, &'a Certificate)>, { for (subject, certificate) in certificates { - if !self.verify_certificate::(rng, namespace, subject, certificate) { + if !self.verify_certificate::(rng, subject, certificate) { return false; } } @@ -343,15 +350,30 @@ mod macros { /// /// This macro creates a complete wrapper struct with constructors, `Scheme` trait /// implementation, and a `fixture` function for testing. - /// The only required parameter is the `Subject` type, which varies per protocol. + /// + /// # Parameters + /// + /// - `$subject`: The subject type used as `Scheme::Subject<'a, D>`. Use `'a` and `D` + /// in the subject type to bind to the GAT lifetime and digest type parameters. + /// + /// - `$namespace`: The namespace type that implements [`Namespace`](crate::certificate::Namespace). + /// This type pre-computes and stores any protocol-specific namespace bytes derived from + /// a base namespace. The scheme calls `$namespace::derive(base)` at construction time + /// to create the namespace, then passes it to `Subject::namespace()` during signing + /// and verification. For simple protocols with only a base namespace, `Vec` can be used directly. + /// For protocols with multiple message types, a custom struct can pre-compute all variants. /// /// # Example /// ```ignore - /// impl_certificate_bls12381_multisig!(VoteSubject<'a, D>); + /// // For non-generic subject types with a single namespace: + /// impl_certificate_bls12381_multisig!(MySubject, Vec); + /// + /// // For protocols with generic subject types: + /// impl_certificate_bls12381_multisig!(Subject<'a, D>, Namespace); /// ``` #[macro_export] macro_rules! impl_certificate_bls12381_multisig { - ($subject:ty) => { + ($subject:ty, $namespace:ty) => { /// Generates a test fixture with Ed25519 identities and BLS12-381 multisig schemes. /// /// Returns a [`commonware_cryptography::certificate::mocks::Fixture`] whose keys and @@ -360,6 +382,7 @@ mod macros { #[allow(dead_code)] pub fn fixture( rng: &mut R, + namespace: &[u8], n: u32, ) -> $crate::certificate::mocks::Fixture> where @@ -368,6 +391,7 @@ mod macros { { $crate::bls12381::certificate::multisig::mocks::fixture::<_, V, _>( rng, + namespace, n, Scheme::signer, Scheme::verifier, @@ -380,7 +404,7 @@ mod macros { P: $crate::PublicKey, V: $crate::bls12381::primitives::variant::Variant, > { - generic: $crate::bls12381::certificate::multisig::Generic, + generic: $crate::bls12381::certificate::multisig::Generic, } impl< @@ -389,11 +413,13 @@ mod macros { > Scheme { /// Creates a new scheme instance with the provided key material. pub fn signer( + namespace: &[u8], participants: commonware_utils::ordered::BiMap, private_key: $crate::bls12381::primitives::group::Private, ) -> Option { Some(Self { generic: $crate::bls12381::certificate::multisig::Generic::signer( + namespace, participants, private_key, )?, @@ -401,11 +427,13 @@ mod macros { } /// Builds a verifier that can authenticate signatures and certificates. - pub const fn verifier( + pub fn verifier( + namespace: &[u8], participants: commonware_utils::ordered::BiMap, ) -> Self { Self { generic: $crate::bls12381::certificate::multisig::Generic::verifier( + namespace, participants, ), } @@ -431,16 +459,14 @@ mod macros { fn sign( &self, - namespace: &[u8], subject: Self::Subject<'_, D>, ) -> Option<$crate::certificate::Attestation> { - self.generic.sign::<_, D>(namespace, subject) + self.generic.sign::<_, D>(subject) } fn verify_attestation( &self, _rng: &mut R, - namespace: &[u8], subject: Self::Subject<'_, D>, attestation: &$crate::certificate::Attestation, ) -> bool @@ -448,13 +474,13 @@ mod macros { R: rand_core::CryptoRngCore, D: $crate::Digest, { - self.generic.verify_attestation::<_, D>(namespace, subject, attestation) + self.generic + .verify_attestation::<_, D>(subject, attestation) } fn verify_attestations( &self, rng: &mut R, - namespace: &[u8], subject: Self::Subject<'_, D>, attestations: I, ) -> $crate::certificate::Verification @@ -463,7 +489,8 @@ mod macros { D: $crate::Digest, I: IntoIterator>, { - self.generic.verify_attestations::<_, _, D, _>(rng, namespace, subject, attestations) + self.generic + .verify_attestations::<_, _, D, _>(rng, subject, attestations) } fn assemble(&self, attestations: I) -> Option @@ -479,33 +506,29 @@ mod macros { >( &self, rng: &mut R, - namespace: &[u8], subject: Self::Subject<'_, D>, certificate: &Self::Certificate, ) -> bool { - self.generic.verify_certificate::(rng, namespace, subject, certificate) + self.generic + .verify_certificate::(rng, subject, certificate) } - fn verify_certificates<'a, R, D, I>( - &self, - rng: &mut R, - namespace: &[u8], - certificates: I, - ) -> bool + fn verify_certificates<'a, R, D, I>(&self, rng: &mut R, certificates: I) -> bool where R: rand_core::CryptoRngCore, D: $crate::Digest, I: Iterator, &'a Self::Certificate)>, { - self.generic.verify_certificates::(rng, namespace, certificates) + self.generic + .verify_certificates::(rng, certificates) } fn is_attributable() -> bool { - $crate::bls12381::certificate::multisig::Generic::::is_attributable() + $crate::bls12381::certificate::multisig::Generic::::is_attributable() } fn is_batchable() -> bool { - $crate::bls12381::certificate::multisig::Generic::::is_batchable() + $crate::bls12381::certificate::multisig::Generic::::is_batchable() } fn certificate_codec_config( @@ -515,7 +538,7 @@ mod macros { } fn certificate_codec_config_unbounded() -> ::Cfg { - $crate::bls12381::certificate::multisig::Generic::::certificate_codec_config_unbounded() + $crate::bls12381::certificate::multisig::Generic::::certificate_codec_config_unbounded() } } }; @@ -547,18 +570,24 @@ mod tests { /// Test context type for generic scheme tests. #[derive(Clone, Debug)] - pub struct TestSubject<'a> { - pub message: &'a [u8], + pub struct TestSubject { + pub message: Bytes, } - impl<'a> Subject for TestSubject<'a> { - fn namespace_and_message(&self, namespace: &[u8]) -> (Bytes, Bytes) { - (namespace.to_vec().into(), self.message.to_vec().into()) + impl Subject for TestSubject { + type Namespace = Vec; + + fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] { + derived + } + + fn message(&self) -> Bytes { + self.message.clone() } } // Use the macro to generate the test scheme - impl_certificate_bls12381_multisig!(TestSubject<'a>); + impl_certificate_bls12381_multisig!(TestSubject, Vec); fn setup_signers( rng: &mut impl CryptoRngCore, @@ -583,27 +612,27 @@ mod tests { let signers = consensus_keys .into_iter() - .map(|sk| Scheme::signer(participants.clone(), sk).unwrap()) + .map(|sk| Scheme::signer(NAMESPACE, participants.clone(), sk).unwrap()) .collect(); - let verifier = Scheme::verifier(participants); + let verifier = Scheme::verifier(NAMESPACE, participants); (signers, verifier) } #[test] fn test_is_attributable() { - assert!(Generic::::is_attributable()); + assert!(Generic::>::is_attributable()); assert!(Scheme::::is_attributable()); - assert!(Generic::::is_attributable()); + assert!(Generic::>::is_attributable()); assert!(Scheme::::is_attributable()); } #[test] fn test_is_batchable() { - assert!(Generic::::is_batchable()); + assert!(Generic::>::is_batchable()); assert!(Scheme::::is_batchable()); - assert!(Generic::::is_batchable()); + assert!(Generic::>::is_batchable()); assert!(Scheme::::is_batchable()); } @@ -613,12 +642,15 @@ mod tests { let scheme = &schemes[0]; let attestation = scheme - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(); assert!(scheme.verify_attestation::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &attestation )); } @@ -633,7 +665,9 @@ mod tests { let mut rng = test_rng(); let (_, verifier) = setup_signers::(&mut rng, 4); assert!(verifier - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE) + }) .is_none()); } @@ -652,15 +686,18 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); let result = schemes[0].verify_attestations::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, attestations.clone(), ); assert!(result.invalid.is_empty()); @@ -671,8 +708,9 @@ mod tests { attestations_corrupted[0].signer = 999; let result = schemes[0].verify_attestations::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, attestations_corrupted, ); assert_eq!(result.invalid, vec![999]); @@ -683,8 +721,9 @@ mod tests { attestations_corrupted[0].signature = attestations_corrupted[1].signature; let result = schemes[0].verify_attestations::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, attestations_corrupted, ); assert_eq!(result.invalid.len(), 1); @@ -706,8 +745,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -732,13 +773,19 @@ mod tests { // Create attestations in reverse sorted order (guaranteed non-sorted) let attestations = vec![ schemes[indexed[2].1] - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(), schemes[indexed[1].1] - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(), schemes[indexed[0].1] - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(), ]; @@ -764,8 +811,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -773,8 +822,9 @@ mod tests { assert!(verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE) + }, &certificate )); } @@ -794,8 +844,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -804,8 +856,9 @@ mod tests { // Valid certificate passes assert!(verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); @@ -814,8 +867,9 @@ mod tests { corrupted.signature = aggregate::Signature::zero(); assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &corrupted )); } @@ -835,8 +889,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -862,8 +918,10 @@ mod tests { .iter() .take(sub_quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -885,8 +943,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -911,8 +971,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -925,8 +987,9 @@ mod tests { assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); } @@ -946,8 +1009,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -959,8 +1024,9 @@ mod tests { assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); } @@ -976,7 +1042,10 @@ mod tests { let (schemes, verifier) = setup_signers::(&mut rng, 4); let quorum = quorum(schemes.len() as u32) as usize; - let messages = [b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()]; + let messages: Vec = [b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()] + .into_iter() + .map(Bytes::copy_from_slice) + .collect(); let mut certificates = Vec::new(); for msg in &messages { @@ -984,19 +1053,25 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: msg }) - .unwrap() + s.sign::(TestSubject { + message: msg.clone(), + }) + .unwrap() }) .collect(); certificates.push(schemes[0].assemble(attestations).unwrap()); } - let certs_iter = messages - .iter() - .zip(&certificates) - .map(|(msg, cert)| (TestSubject { message: msg }, cert)); + let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| { + ( + TestSubject { + message: msg.clone(), + }, + cert, + ) + }); - assert!(verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, NAMESPACE, certs_iter)); + assert!(verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, certs_iter)); } #[test] @@ -1010,7 +1085,10 @@ mod tests { let (schemes, verifier) = setup_signers::(&mut rng, 4); let quorum = quorum(schemes.len() as u32) as usize; - let messages = [b"msg1".as_slice(), b"msg2".as_slice()]; + let messages: Vec = [b"msg1".as_slice(), b"msg2".as_slice()] + .into_iter() + .map(Bytes::copy_from_slice) + .collect(); let mut certificates = Vec::new(); for msg in &messages { @@ -1018,8 +1096,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: msg }) - .unwrap() + s.sign::(TestSubject { + message: msg.clone(), + }) + .unwrap() }) .collect(); certificates.push(schemes[0].assemble(attestations).unwrap()); @@ -1028,14 +1108,16 @@ mod tests { // Corrupt second certificate certificates[1].signature = aggregate::Signature::zero(); - let certs_iter = messages - .iter() - .zip(&certificates) - .map(|(msg, cert)| (TestSubject { message: msg }, cert)); + let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| { + ( + TestSubject { + message: msg.clone(), + }, + cert, + ) + }); - assert!( - !verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, NAMESPACE, certs_iter) - ); + assert!(!verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, certs_iter)); } #[test] @@ -1052,8 +1134,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -1084,7 +1168,9 @@ mod tests { let signer = schemes[0].clone(); assert!( signer - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE) + }) .is_some(), "cloned signer should retain signing capability" ); @@ -1092,7 +1178,9 @@ mod tests { // A verifier cannot produce votes assert!( verifier - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE) + }) .is_none(), "verifier must not sign votes" ); @@ -1113,8 +1201,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -1158,8 +1248,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -1172,8 +1264,9 @@ mod tests { assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate, )); } @@ -1189,16 +1282,21 @@ mod tests { let (schemes, _) = setup_signers::(&mut rng, 4); let attestation1 = schemes[0] - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(); let attestation2 = schemes[1] - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(); let verification = schemes[0].verify_attestations::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, vec![attestation1.clone(), attestation2.clone()], ); assert!(verification.invalid.is_empty()); @@ -1221,8 +1319,9 @@ mod tests { let verification = schemes[0].verify_attestations::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, vec![forged_attestation1, forged_attestation2], ); assert!( diff --git a/cryptography/src/bls12381/certificate/threshold/mocks.rs b/cryptography/src/bls12381/certificate/threshold/mocks.rs index cb95e2084d..7ba89244f6 100644 --- a/cryptography/src/bls12381/certificate/threshold/mocks.rs +++ b/cryptography/src/bls12381/certificate/threshold/mocks.rs @@ -3,7 +3,7 @@ use crate::{ bls12381::{ dkg::deal, - primitives::{sharing::Sharing, variant::Variant}, + primitives::{group::Share, sharing::Sharing, variant::Variant}, }, certificate::{mocks::Fixture, Scheme}, ed25519, @@ -14,13 +14,10 @@ use rand::{CryptoRng, RngCore}; /// Builds ed25519 identities and matching BLS12-381 threshold schemes. pub fn fixture( rng: &mut R, + namespace: &[u8], n: u32, - signer: impl Fn( - Set, - Sharing, - crate::bls12381::primitives::group::Share, - ) -> Option, - verifier: impl Fn(Set, Sharing) -> S, + signer: impl Fn(&[u8], Set, Sharing, Share) -> Option, + verifier: impl Fn(&[u8], Set, Sharing) -> S, ) -> Fixture where V: Variant, @@ -29,7 +26,7 @@ where { assert!(n > 0); - let associated = crate::ed25519::certificate::mocks::participants(rng, n); + let associated = ed25519::certificate::mocks::participants(rng, n); let participants = associated.keys().clone(); let participants_vec: Vec<_> = participants.clone().into(); let private_keys: Vec<_> = participants_vec @@ -49,11 +46,11 @@ where let schemes = shares .into_iter() .map(|(_, share)| { - signer(participants.clone(), polynomial.clone(), share) + signer(namespace, participants.clone(), polynomial.clone(), share) .expect("scheme signer must be a participant") }) .collect(); - let verifier = verifier(participants, polynomial); + let verifier = verifier(namespace, participants, polynomial); Fixture { participants: participants_vec, diff --git a/cryptography/src/bls12381/certificate/threshold/mod.rs b/cryptography/src/bls12381/certificate/threshold/mod.rs index 6d76af4927..ecdf184832 100644 --- a/cryptography/src/bls12381/certificate/threshold/mod.rs +++ b/cryptography/src/bls12381/certificate/threshold/mod.rs @@ -18,7 +18,7 @@ use crate::{ sharing::Sharing, variant::{PartialSignature, Variant}, }, - certificate::{Attestation, Scheme, Subject, Verification}, + certificate::{Attestation, Namespace, Scheme, Subject, Verification}, Digest, PublicKey, }; use ::core::fmt::Debug; @@ -39,7 +39,7 @@ use std::collections::BTreeSet; /// a verifier (with evaluated public polynomial), or an external verifier that /// only checks recovered certificates. #[derive(Clone, Debug)] -pub enum Generic { +pub enum Generic { Signer { /// Participants in the committee. participants: Set

, @@ -47,20 +47,26 @@ pub enum Generic { polynomial: Sharing, /// Local share used to generate partial signatures. share: Share, + /// Pre-computed namespace(s) for this subject type. + namespace: N, }, Verifier { /// Participants in the committee. participants: Set

, /// The public polynomial, used for the group identity, and partial signatures. polynomial: Sharing, + /// Pre-computed namespace(s) for this subject type. + namespace: N, }, CertificateVerifier { /// Public identity of the committee (constant across reshares). identity: V::Public, + /// Pre-computed namespace(s) for this subject type. + namespace: N, }, } -impl Generic { +impl Generic { /// Constructs a signer instance with a private share and evaluated public polynomial. /// /// The participant identity keys are used for committee ordering and indexing. @@ -69,10 +75,16 @@ impl Generic { /// /// Returns `None` if the share's public key does not match any participant. /// + /// * `namespace` - base namespace for domain separation /// * `participants` - ordered set of participant identity keys /// * `polynomial` - public polynomial for threshold verification /// * `share` - local threshold share for signing - pub fn signer(participants: Set

, polynomial: Sharing, share: Share) -> Option { + pub fn signer( + namespace: &[u8], + participants: Set

, + polynomial: Sharing, + share: Share, + ) -> Option { assert_eq!( polynomial.total().get() as usize, participants.len(), @@ -88,6 +100,7 @@ impl Generic { participants, polynomial, share, + namespace: N::derive(namespace), }) } else { None @@ -100,9 +113,10 @@ impl Generic { /// The polynomial can be evaluated to obtain public verification keys for partial /// signatures produced by committee members. /// + /// * `namespace` - base namespace for domain separation /// * `participants` - ordered set of participant identity keys /// * `polynomial` - public polynomial for threshold verification - pub fn verifier(participants: Set

, polynomial: Sharing) -> Self { + pub fn verifier(namespace: &[u8], participants: Set

, polynomial: Sharing) -> Self { assert_eq!( polynomial.total().get() as usize, participants.len(), @@ -114,6 +128,7 @@ impl Generic { Self::Verifier { participants, polynomial, + namespace: N::derive(namespace), } } @@ -122,9 +137,13 @@ impl Generic { /// This lightweight verifier can authenticate recovered threshold certificates but cannot /// verify individual signatures or partial signatures. /// + /// * `namespace` - base namespace for domain separation /// * `identity` - public identity of the committee (constant across reshares) - pub const fn certificate_verifier(identity: V::Public) -> Self { - Self::CertificateVerifier { identity } + pub fn certificate_verifier(namespace: &[u8], identity: V::Public) -> Self { + Self::CertificateVerifier { + identity, + namespace: N::derive(namespace), + } } /// Returns the ordered set of participant public identity keys in the committee. @@ -162,6 +181,15 @@ impl Generic { } } + /// Returns the pre-computed namespace. + const fn namespace(&self) -> &N { + match self { + Self::Signer { namespace, .. } => namespace, + Self::Verifier { namespace, .. } => namespace, + Self::CertificateVerifier { namespace, .. } => namespace, + } + } + /// Returns the index of "self" in the participant set, if available. pub const fn me(&self) -> Option { match self { @@ -171,16 +199,20 @@ impl Generic { } /// Signs a subject and returns the attestation. - pub fn sign(&self, namespace: &[u8], subject: S::Subject<'_, D>) -> Option> + pub fn sign<'a, S, D>(&self, subject: S::Subject<'a, D>) -> Option> where S: Scheme, + S::Subject<'a, D>: Subject, D: Digest, { let share = self.share()?; - let (namespace, message) = subject.namespace_and_message(namespace); - let signature = - threshold::sign_message::(share, namespace.as_ref(), message.as_ref()).value; + let signature = threshold::sign_message::( + share, + subject.namespace(self.namespace()), + &subject.message(), + ) + .value; Some(Attestation { signer: share.index, @@ -189,40 +221,39 @@ impl Generic { } /// Verifies a single attestation from a signer. - pub fn verify_attestation( + pub fn verify_attestation<'a, S, D>( &self, - namespace: &[u8], - subject: S::Subject<'_, D>, + subject: S::Subject<'a, D>, attestation: &Attestation, ) -> bool where S: Scheme, + S::Subject<'a, D>: Subject, D: Digest, { let Ok(evaluated) = self.polynomial().partial_public(attestation.signer) else { return false; }; - let (namespace, message) = subject.namespace_and_message(namespace); ops::verify_message::( &evaluated, - namespace.as_ref(), - message.as_ref(), + subject.namespace(self.namespace()), + &subject.message(), &attestation.signature, ) .is_ok() } /// Batch-verifies attestations and returns verified attestations and invalid signers. - pub fn verify_attestations( + pub fn verify_attestations<'a, S, R, D, I>( &self, rng: &mut R, - namespace: &[u8], - subject: S::Subject<'_, D>, + subject: S::Subject<'a, D>, attestations: I, ) -> Verification where S: Scheme, + S::Subject<'a, D>: Subject, R: CryptoRngCore, D: Digest, I: IntoIterator>, @@ -237,12 +268,11 @@ impl Generic { .collect(); let polynomial = self.polynomial(); - let (namespace, message) = subject.namespace_and_message(namespace); if let Err(errs) = threshold::batch_verify_same_message::<_, V, _>( rng, polynomial, - namespace.as_ref(), - message.as_ref(), + subject.namespace(self.namespace()), + &subject.message(), partials.iter(), ) { for partial in errs { @@ -285,44 +315,42 @@ impl Generic { } /// Verifies a certificate. - pub fn verify_certificate( + pub fn verify_certificate<'a, S, R, D>( &self, _rng: &mut R, - namespace: &[u8], - subject: S::Subject<'_, D>, + subject: S::Subject<'a, D>, certificate: &V::Signature, ) -> bool where S: Scheme, + S::Subject<'a, D>: Subject, R: CryptoRngCore, D: Digest, { - let identity = self.identity(); - let (namespace, message) = subject.namespace_and_message(namespace); - ops::verify_message::(identity, namespace.as_ref(), message.as_ref(), certificate) - .is_ok() + ops::verify_message::( + self.identity(), + subject.namespace(self.namespace()), + &subject.message(), + certificate, + ) + .is_ok() } /// Verifies multiple certificates in a batch. - pub fn verify_certificates<'a, S, R, D, I>( - &self, - rng: &mut R, - namespace: &[u8], - certificates: I, - ) -> bool + pub fn verify_certificates<'a, S, R, D, I>(&self, rng: &mut R, certificates: I) -> bool where S: Scheme, + S::Subject<'a, D>: Subject, R: CryptoRngCore, D: Digest, I: Iterator, &'a V::Signature)>, { - let identity = self.identity(); - let mut entries: Vec<_> = Vec::new(); for (subject, certificate) in certificates { - let (ns, message) = subject.namespace_and_message(namespace); - entries.push((ns.to_vec(), message.to_vec(), *certificate)); + let namespace = subject.namespace(self.namespace()); + let message = subject.message(); + entries.push((namespace.to_vec(), message.to_vec(), *certificate)); } if entries.is_empty() { @@ -334,7 +362,7 @@ impl Generic { .map(|(ns, msg, sig)| (ns.as_ref(), msg.as_ref(), *sig)) .collect(); - batch::verify_same_signer::<_, V, _>(rng, identity, &entries_refs, 1).is_ok() + batch::verify_same_signer::<_, V, _>(rng, self.identity(), &entries_refs, 1).is_ok() } pub const fn is_attributable() -> bool { @@ -355,15 +383,30 @@ mod macros { /// /// This macro creates a complete wrapper struct with constructors, `Scheme` trait /// implementation, and a `fixture` function for testing. - /// The only required parameter is the `Subject` type, which varies per protocol. + /// + /// # Parameters + /// + /// - `$subject`: The subject type used as `Scheme::Subject<'a, D>`. Use `'a` and `D` + /// in the subject type to bind to the GAT lifetime and digest type parameters. + /// + /// - `$namespace`: The namespace type that implements [`Namespace`](crate::certificate::Namespace). + /// This type pre-computes and stores any protocol-specific namespace bytes derived from + /// a base namespace. The scheme calls `$namespace::derive(base)` at construction time + /// to create the namespace, then passes it to `Subject::namespace()` during signing + /// and verification. For simple protocols with only a base namespace, `Vec` can be used directly. + /// For protocols with multiple message types, a custom struct can pre-compute all variants. /// /// # Example /// ```ignore - /// impl_certificate_bls12381_threshold!(VoteSubject<'a, D>); + /// // For non-generic subject types with a single namespace: + /// impl_certificate_bls12381_threshold!(MySubject, Vec); + /// + /// // For protocols with generic subject types: + /// impl_certificate_bls12381_threshold!(Subject<'a, D>, Namespace); /// ``` #[macro_export] macro_rules! impl_certificate_bls12381_threshold { - ($subject:ty) => { + ($subject:ty, $namespace:ty) => { /// Generates a test fixture with Ed25519 identities and BLS12-381 threshold schemes. /// /// Returns a [`commonware_cryptography::certificate::mocks::Fixture`] whose keys and @@ -372,6 +415,7 @@ mod macros { #[allow(dead_code)] pub fn fixture( rng: &mut R, + namespace: &[u8], n: u32, ) -> $crate::certificate::mocks::Fixture> where @@ -380,6 +424,7 @@ mod macros { { $crate::bls12381::certificate::threshold::mocks::fixture::<_, V, _>( rng, + namespace, n, Scheme::signer, Scheme::verifier, @@ -392,7 +437,7 @@ mod macros { P: $crate::PublicKey, V: $crate::bls12381::primitives::variant::Variant, > { - generic: $crate::bls12381::certificate::threshold::Generic, + generic: $crate::bls12381::certificate::threshold::Generic, } impl< @@ -401,12 +446,14 @@ mod macros { > Scheme { /// Creates a new signer instance with a private share and evaluated public polynomial. pub fn signer( + namespace: &[u8], participants: commonware_utils::ordered::Set

, polynomial: $crate::bls12381::primitives::sharing::Sharing, share: $crate::bls12381::primitives::group::Share, ) -> Option { Some(Self { generic: $crate::bls12381::certificate::threshold::Generic::signer( + namespace, participants, polynomial, share, @@ -416,11 +463,13 @@ mod macros { /// Creates a verifier that can authenticate partial signatures. pub fn verifier( + namespace: &[u8], participants: commonware_utils::ordered::Set

, polynomial: $crate::bls12381::primitives::sharing::Sharing, ) -> Self { Self { generic: $crate::bls12381::certificate::threshold::Generic::verifier( + namespace, participants, polynomial, ), @@ -428,9 +477,10 @@ mod macros { } /// Creates a lightweight verifier that only checks recovered certificates. - pub const fn certificate_verifier(identity: V::Public) -> Self { + pub fn certificate_verifier(namespace: &[u8], identity: V::Public) -> Self { Self { generic: $crate::bls12381::certificate::threshold::Generic::certificate_verifier( + namespace, identity, ), } @@ -466,16 +516,14 @@ mod macros { fn sign( &self, - namespace: &[u8], subject: Self::Subject<'_, D>, ) -> Option<$crate::certificate::Attestation> { - self.generic.sign::<_, D>(namespace, subject) + self.generic.sign::<_, D>(subject) } fn verify_attestation( &self, _rng: &mut R, - namespace: &[u8], subject: Self::Subject<'_, D>, attestation: &$crate::certificate::Attestation, ) -> bool @@ -483,13 +531,13 @@ mod macros { R: rand_core::CryptoRngCore, D: $crate::Digest, { - self.generic.verify_attestation::<_, D>(namespace, subject, attestation) + self.generic + .verify_attestation::<_, D>(subject, attestation) } fn verify_attestations( &self, rng: &mut R, - namespace: &[u8], subject: Self::Subject<'_, D>, attestations: I, ) -> $crate::certificate::Verification @@ -498,7 +546,8 @@ mod macros { D: $crate::Digest, I: IntoIterator>, { - self.generic.verify_attestations::<_, _, D, _>(rng, namespace, subject, attestations) + self.generic + .verify_attestations::<_, _, D, _>(rng, subject, attestations) } fn assemble(&self, attestations: I) -> Option @@ -508,39 +557,32 @@ mod macros { self.generic.assemble(attestations) } - fn verify_certificate< - R: rand::Rng + rand::CryptoRng, - D: $crate::Digest, - >( + fn verify_certificate( &self, rng: &mut R, - namespace: &[u8], subject: Self::Subject<'_, D>, certificate: &Self::Certificate, ) -> bool { - self.generic.verify_certificate::(rng, namespace, subject, certificate) + self.generic + .verify_certificate::(rng, subject, certificate) } - fn verify_certificates<'a, R, D, I>( - &self, - rng: &mut R, - namespace: &[u8], - certificates: I, - ) -> bool + fn verify_certificates<'a, R, D, I>(&self, rng: &mut R, certificates: I) -> bool where R: rand::Rng + rand::CryptoRng, D: $crate::Digest, I: Iterator, &'a Self::Certificate)>, { - self.generic.verify_certificates::(rng, namespace, certificates) + self.generic + .verify_certificates::(rng, certificates) } fn is_attributable() -> bool { - $crate::bls12381::certificate::threshold::Generic::::is_attributable() + $crate::bls12381::certificate::threshold::Generic::::is_attributable() } fn is_batchable() -> bool { - $crate::bls12381::certificate::threshold::Generic::::is_batchable() + $crate::bls12381::certificate::threshold::Generic::::is_batchable() } fn certificate_codec_config( @@ -551,7 +593,7 @@ mod macros { fn certificate_codec_config_unbounded( ) -> ::Cfg { - $crate::bls12381::certificate::threshold::Generic::::certificate_codec_config_unbounded() + $crate::bls12381::certificate::threshold::Generic::::certificate_codec_config_unbounded() } } }; @@ -585,18 +627,24 @@ mod tests { /// Test context type for generic scheme tests. #[derive(Clone, Debug)] - pub struct TestSubject<'a> { - pub message: &'a [u8], + pub struct TestSubject { + pub message: Bytes, } - impl<'a> Subject for TestSubject<'a> { - fn namespace_and_message(&self, namespace: &[u8]) -> (Bytes, Bytes) { - (namespace.to_vec().into(), self.message.to_vec().into()) + impl Subject for TestSubject { + type Namespace = Vec; + + fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] { + derived + } + + fn message(&self) -> Bytes { + self.message.clone() } } // Use the macro to generate the test scheme - impl_certificate_bls12381_threshold!(TestSubject<'a>); + impl_certificate_bls12381_threshold!(TestSubject, Vec); #[allow(clippy::type_complexity)] fn setup_signers( @@ -623,10 +671,12 @@ mod tests { let signers = shares .into_iter() - .map(|share| Scheme::signer(participants.clone(), polynomial.clone(), share).unwrap()) + .map(|share| { + Scheme::signer(NAMESPACE, participants.clone(), polynomial.clone(), share).unwrap() + }) .collect(); - let verifier = Scheme::verifier(participants, polynomial.clone()); + let verifier = Scheme::verifier(NAMESPACE, participants, polynomial.clone()); (signers, verifier, polynomial) } @@ -637,12 +687,15 @@ mod tests { let scheme = &schemes[0]; let attestation = scheme - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(); assert!(scheme.verify_attestation::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &attestation )); } @@ -657,7 +710,9 @@ mod tests { let mut rng = test_rng(); let (_, verifier, _) = setup_signers::(&mut rng, 4); assert!(verifier - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .is_none()); } @@ -676,15 +731,18 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); let result = schemes[0].verify_attestations::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, attestations.clone(), ); assert!(result.invalid.is_empty()); @@ -695,8 +753,9 @@ mod tests { attestations_corrupted[0].signer = 999; let result = schemes[0].verify_attestations::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, attestations_corrupted, ); assert_eq!(result.invalid, vec![999]); @@ -707,8 +766,9 @@ mod tests { attestations_corrupted[0].signature = attestations_corrupted[1].signature; let result = schemes[0].verify_attestations::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, attestations_corrupted, ); assert_eq!(result.invalid.len(), 1); @@ -730,8 +790,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -740,8 +802,9 @@ mod tests { // Verify the assembled certificate assert!(verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); } @@ -761,8 +824,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -770,8 +835,9 @@ mod tests { assert!(verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); } @@ -791,8 +857,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -801,8 +869,9 @@ mod tests { // Valid certificate passes assert!(verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); @@ -810,8 +879,9 @@ mod tests { let corrupted = V::Signature::zero(); assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &corrupted )); } @@ -831,8 +901,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -857,8 +929,10 @@ mod tests { .iter() .take(sub_quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -876,7 +950,11 @@ mod tests { let (schemes, verifier, _) = setup_signers::(&mut rng, 4); let quorum = quorum(schemes.len() as u32) as usize; - let messages = [b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()]; + let messages: [Bytes; 3] = [ + Bytes::from_static(b"msg1"), + Bytes::from_static(b"msg2"), + Bytes::from_static(b"msg3"), + ]; let mut certificates = Vec::new(); for msg in &messages { @@ -884,19 +962,25 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: msg }) - .unwrap() + s.sign::(TestSubject { + message: msg.clone(), + }) + .unwrap() }) .collect(); certificates.push(schemes[0].assemble(attestations).unwrap()); } - let certs_iter = messages - .iter() - .zip(&certificates) - .map(|(msg, cert)| (TestSubject { message: msg }, cert)); + let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| { + ( + TestSubject { + message: msg.clone(), + }, + cert, + ) + }); - assert!(verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, NAMESPACE, certs_iter)); + assert!(verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, certs_iter)); } #[test] @@ -910,7 +994,7 @@ mod tests { let (schemes, verifier, _) = setup_signers::(&mut rng, 4); let quorum = quorum(schemes.len() as u32) as usize; - let messages = [b"msg1".as_slice(), b"msg2".as_slice()]; + let messages: [Bytes; 2] = [Bytes::from_static(b"msg1"), Bytes::from_static(b"msg2")]; let mut certificates = Vec::new(); for msg in &messages { @@ -918,8 +1002,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: msg }) - .unwrap() + s.sign::(TestSubject { + message: msg.clone(), + }) + .unwrap() }) .collect(); certificates.push(schemes[0].assemble(attestations).unwrap()); @@ -928,14 +1014,16 @@ mod tests { // Corrupt second certificate certificates[1] = V::Signature::zero(); - let certs_iter = messages - .iter() - .zip(&certificates) - .map(|(msg, cert)| (TestSubject { message: msg }, cert)); + let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| { + ( + TestSubject { + message: msg.clone(), + }, + cert, + ) + }); - assert!( - !verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, NAMESPACE, certs_iter) - ); + assert!(!verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, certs_iter)); } #[test] @@ -953,8 +1041,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -962,19 +1052,23 @@ mod tests { // Create a certificate-only verifier using the identity from the polynomial let identity = polynomial.public(); - let cert_verifier = Scheme::::certificate_verifier(*identity); + let cert_verifier = + Scheme::::certificate_verifier(NAMESPACE, *identity); // Should be able to verify certificates assert!(cert_verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); // Should not be able to sign assert!(cert_verifier - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .is_none()); } @@ -986,17 +1080,17 @@ mod tests { #[test] fn test_is_not_attributable() { - assert!(!Generic::::is_attributable()); + assert!(!Generic::>::is_attributable()); assert!(!Scheme::::is_attributable()); - assert!(!Generic::::is_attributable()); + assert!(!Generic::>::is_attributable()); assert!(!Scheme::::is_attributable()); } #[test] fn test_is_batchable() { - assert!(Generic::::is_batchable()); + assert!(Generic::>::is_batchable()); assert!(Scheme::::is_batchable()); - assert!(Generic::::is_batchable()); + assert!(Generic::>::is_batchable()); assert!(Scheme::::is_batchable()); } @@ -1005,12 +1099,15 @@ mod tests { let (schemes, verifier, _) = setup_signers::(&mut rng, 4); let vote = schemes[1] - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(); assert!(verifier.verify_attestation::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &vote )); } @@ -1029,7 +1126,9 @@ mod tests { let signer = schemes[0].clone(); assert!( signer - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .is_some(), "signer should produce votes" ); @@ -1037,7 +1136,9 @@ mod tests { // A verifier cannot produce votes assert!( verifier - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .is_none(), "verifier should not produce votes" ); @@ -1052,18 +1153,23 @@ mod tests { fn certificate_verifier_panics_on_vote() { let mut rng = test_rng(); let (schemes, _, _) = setup_signers::(&mut rng, 4); - let certificate_verifier = - Scheme::::certificate_verifier(*schemes[0].identity()); + let certificate_verifier = Scheme::::certificate_verifier( + NAMESPACE, + *schemes[0].identity(), + ); let vote = schemes[1] - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(); // CertificateVerifier should panic when trying to verify a vote certificate_verifier.verify_attestation::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &vote, ); } @@ -1096,7 +1202,12 @@ mod tests { let (polynomial, mut shares) = dkg::deal_anonymous::(&mut rng, Default::default(), NZU32!(4)); shares[0].index = 999; - Scheme::::signer(participants, polynomial, shares[0].clone()); + Scheme::::signer( + NAMESPACE, + participants, + polynomial, + shares[0].clone(), + ); } #[test] @@ -1129,7 +1240,12 @@ mod tests { // quorum(5) = 4, but polynomial.required() = 2, so this should panic let (polynomial, shares) = dkg::deal_anonymous::(&mut rng, Default::default(), NZU32!(2)); - Scheme::::signer(participants, polynomial, shares[0].clone()); + Scheme::::signer( + NAMESPACE, + participants, + polynomial, + shares[0].clone(), + ); } #[test] @@ -1150,7 +1266,7 @@ mod tests { // Create a polynomial with threshold 2, but quorum of 5 participants is 4 // quorum(5) = 4, but polynomial.required() = 2, so this should panic let (polynomial, _) = dkg::deal_anonymous::(&mut rng, Default::default(), NZU32!(2)); - Scheme::::verifier(participants, polynomial); + Scheme::::verifier(NAMESPACE, participants, polynomial); } #[test] @@ -1174,8 +1290,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -1197,7 +1315,9 @@ mod tests { let scheme = &schemes[0]; let signature = scheme - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(); // Verify the partial signature matches what we'd get from direct signing diff --git a/cryptography/src/certificate.rs b/cryptography/src/certificate.rs index 6663f74aad..08e4f00459 100644 --- a/cryptography/src/certificate.rs +++ b/cryptography/src/certificate.rs @@ -150,10 +150,31 @@ impl Verification { } } +/// Trait for namespace types that can derive themselves from a base namespace. +/// +/// This trait is implemented by namespace types to define how they are computed +/// from a base namespace string. +pub trait Namespace: Clone + Send + Sync { + /// Derive a namespace from the given base. + fn derive(namespace: &[u8]) -> Self; +} + +impl Namespace for Vec { + fn derive(namespace: &[u8]) -> Self { + namespace.to_vec() + } +} + /// Identifies the subject of a signature or certificate. pub trait Subject: Clone + Debug + Send + Sync { - /// Returns the namespace and message for the subject, given some base namespace. - fn namespace_and_message(&self, namespace: &[u8]) -> (Bytes, Bytes); + /// Pre-computed namespace(s) for this subject type. + type Namespace: Namespace; + + /// Get the namespace bytes for this subject instance. + fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8]; + + /// Get the message bytes for this subject instance. + fn message(&self) -> Bytes; } /// Cryptographic surface for multi-party certificate schemes. @@ -179,19 +200,14 @@ pub trait Scheme: Clone + Debug + Send + Sync + 'static { /// Returns the ordered set of participant public identity keys managed by the scheme. fn participants(&self) -> &Set; - /// Signs a subject using the supplied namespace for domain separation. + /// Signs a subject. /// Returns `None` if the scheme cannot sign (e.g. it's a verifier-only instance). - fn sign( - &self, - namespace: &[u8], - subject: Self::Subject<'_, D>, - ) -> Option>; + fn sign(&self, subject: Self::Subject<'_, D>) -> Option>; /// Verifies a single attestation against the participant material managed by the scheme. fn verify_attestation( &self, rng: &mut R, - namespace: &[u8], subject: Self::Subject<'_, D>, attestation: &Attestation, ) -> bool @@ -206,7 +222,6 @@ pub trait Scheme: Clone + Debug + Send + Sync + 'static { fn verify_attestations( &self, rng: &mut R, - namespace: &[u8], subject: Self::Subject<'_, D>, attestations: I, ) -> Verification @@ -218,7 +233,7 @@ pub trait Scheme: Clone + Debug + Send + Sync + 'static { let mut invalid = BTreeSet::new(); let verified = attestations.into_iter().filter_map(|attestation| { - if self.verify_attestation(&mut *rng, namespace, subject.clone(), &attestation) { + if self.verify_attestation(&mut *rng, subject.clone(), &attestation) { Some(attestation) } else { invalid.insert(attestation.signer); @@ -240,25 +255,19 @@ pub trait Scheme: Clone + Debug + Send + Sync + 'static { fn verify_certificate( &self, rng: &mut R, - namespace: &[u8], subject: Self::Subject<'_, D>, certificate: &Self::Certificate, ) -> bool; /// Verifies a stream of certificates, returning `false` at the first failure. - fn verify_certificates<'a, R, D, I>( - &self, - rng: &mut R, - namespace: &[u8], - certificates: I, - ) -> bool + fn verify_certificates<'a, R, D, I>(&self, rng: &mut R, certificates: I) -> bool where R: CryptoRngCore, D: Digest, I: Iterator, &'a Self::Certificate)>, { for (subject, certificate) in certificates { - if !self.verify_certificate(rng, namespace, subject, certificate) { + if !self.verify_certificate(rng, subject, certificate) { return false; } } @@ -517,18 +526,24 @@ mod tests { /// Test context type for generic scheme tests. #[derive(Clone, Debug)] - pub struct TestSubject<'a> { - pub message: &'a [u8], + pub struct TestSubject { + pub message: Bytes, } - impl<'a> Subject for TestSubject<'a> { - fn namespace_and_message(&self, namespace: &[u8]) -> (Bytes, Bytes) { - (namespace.to_vec().into(), self.message.to_vec().into()) + impl Subject for TestSubject { + type Namespace = Vec; + + fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] { + derived + } + + fn message(&self) -> Bytes { + self.message.clone() } } // Use the macro to generate the test scheme (signer/verifier are unused in conformance tests) - impl_certificate_ed25519!(TestSubject<'a>); + impl_certificate_ed25519!(TestSubject, Vec); commonware_conformance::conformance_tests! { CodecConformance, diff --git a/cryptography/src/ed25519/certificate/mocks.rs b/cryptography/src/ed25519/certificate/mocks.rs index e2a82b8aab..1a8bbef102 100644 --- a/cryptography/src/ed25519/certificate/mocks.rs +++ b/cryptography/src/ed25519/certificate/mocks.rs @@ -6,7 +6,10 @@ use crate::{ Signer as _, }; use commonware_math::algebra::Random; -use commonware_utils::{ordered::BiMap, TryCollect as _}; +use commonware_utils::{ + ordered::{BiMap, Set}, + TryCollect as _, +}; use rand::{CryptoRng, RngCore}; /// Generates ed25519 identity participants. @@ -27,9 +30,10 @@ where /// Builds ed25519 identities alongside a caller-provided ed25519 certificate scheme wrapper. pub fn fixture( rng: &mut R, + namespace: &[u8], n: u32, - signer: impl Fn(commonware_utils::ordered::Set, PrivateKey) -> Option, - verifier: impl Fn(commonware_utils::ordered::Set) -> S, + signer: impl Fn(&[u8], Set, PrivateKey) -> Option, + verifier: impl Fn(&[u8], Set) -> S, ) -> Fixture where R: RngCore + CryptoRng, @@ -54,9 +58,12 @@ where let schemes = private_keys .iter() .cloned() - .map(|sk| signer(participants.clone(), sk).expect("scheme signer must be a participant")) + .map(|sk| { + signer(namespace, participants.clone(), sk) + .expect("scheme signer must be a participant") + }) .collect(); - let verifier = verifier(participants); + let verifier = verifier(namespace, participants); Fixture { participants: participants_vec, diff --git a/cryptography/src/ed25519/certificate/mod.rs b/cryptography/src/ed25519/certificate/mod.rs index 16d1940ef2..b44dbd7976 100644 --- a/cryptography/src/ed25519/certificate/mod.rs +++ b/cryptography/src/ed25519/certificate/mod.rs @@ -12,7 +12,7 @@ use super::{PrivateKey, PublicKey, Signature as Ed25519Signature}; #[cfg(feature = "std")] use crate::{certificate::Verification, BatchVerifier}; use crate::{ - certificate::{Attestation, Scheme, Signers, Subject}, + certificate::{Attestation, Namespace, Scheme, Signers, Subject}, Digest, Signer as _, Verifier as _, }; #[cfg(not(feature = "std"))] @@ -30,16 +30,22 @@ use std::collections::BTreeSet; /// context types. It can be reused across different protocols (simplex, aggregation, etc.) /// by wrapping it with protocol-specific trait implementations via the macro. #[derive(Clone, Debug)] -pub struct Generic { +pub struct Generic { /// Participants in the committee. pub participants: Set, /// Key used for generating signatures. pub signer: Option<(u32, PrivateKey)>, + /// Pre-computed namespace(s) for this subject type. + pub namespace: N, } -impl Generic { +impl Generic { /// Creates a new generic Ed25519 scheme instance. - pub fn signer(participants: Set, private_key: PrivateKey) -> Option { + pub fn signer( + namespace: &[u8], + participants: Set, + private_key: PrivateKey, + ) -> Option { let signer = participants .index(&private_key.public_key()) .map(|index| (index, private_key))?; @@ -47,14 +53,16 @@ impl Generic { Some(Self { participants, signer: Some(signer), + namespace: N::derive(namespace), }) } /// Builds a verifier that can authenticate signatures without generating them. - pub const fn verifier(participants: Set) -> Self { + pub fn verifier(namespace: &[u8], participants: Set) -> Self { Self { participants, signer: None, + namespace: N::derive(namespace), } } @@ -64,15 +72,15 @@ impl Generic { } /// Signs a subject and returns the signer index and signature. - pub fn sign(&self, namespace: &[u8], subject: S::Subject<'_, D>) -> Option> + pub fn sign<'a, S, D>(&self, subject: S::Subject<'a, D>) -> Option> where S: Scheme, + S::Subject<'a, D>: Subject, D: Digest, { let (index, private_key) = self.signer.as_ref()?; - let (namespace, message) = subject.namespace_and_message(namespace); - let signature = private_key.sign(namespace.as_ref(), message.as_ref()); + let signature = private_key.sign(subject.namespace(&self.namespace), &subject.message()); Some(Attestation { signer: *index, @@ -81,40 +89,44 @@ impl Generic { } /// Verifies a single attestation from a signer. - pub fn verify_attestation( + pub fn verify_attestation<'a, S, D>( &self, - namespace: &[u8], - subject: S::Subject<'_, D>, + subject: S::Subject<'a, D>, attestation: &Attestation, ) -> bool where S: Scheme, + S::Subject<'a, D>: Subject, D: Digest, { let Some(public_key) = self.participants.key(attestation.signer) else { return false; }; - let (namespace, message) = subject.namespace_and_message(namespace); - public_key.verify(namespace.as_ref(), message.as_ref(), &attestation.signature) + public_key.verify( + subject.namespace(&self.namespace), + &subject.message(), + &attestation.signature, + ) } /// Batch-verifies attestations and returns verified attestations and invalid signers. #[cfg(feature = "std")] - pub fn verify_attestations( + pub fn verify_attestations<'a, S, R, D, I>( &self, rng: &mut R, - namespace: &[u8], - subject: S::Subject<'_, D>, + subject: S::Subject<'a, D>, attestations: I, ) -> Verification where S: Scheme, + S::Subject<'a, D>: Subject, R: CryptoRngCore, D: Digest, I: IntoIterator>, { - let (namespace, message) = subject.namespace_and_message(namespace); + let namespace = subject.namespace(&self.namespace); + let message = subject.message(); let mut invalid = BTreeSet::new(); let mut candidates = Vec::new(); @@ -126,21 +138,14 @@ impl Generic { continue; }; - batch.add( - namespace.as_ref(), - message.as_ref(), - public_key, - &attestation.signature, - ); - + batch.add(namespace, &message, public_key, &attestation.signature); candidates.push((attestation, public_key)); } if !candidates.is_empty() && !batch.verify(rng) { // Batch failed: fall back to per-signer verification to isolate faulty attestations. for (attestation, public_key) in &candidates { - if !public_key.verify(namespace.as_ref(), message.as_ref(), &attestation.signature) - { + if !public_key.verify(namespace, &message, &attestation.signature) { invalid.insert(attestation.signer); } } @@ -162,20 +167,21 @@ impl Generic { /// Verifies attestations one-by-one and returns verified attestations and invalid signers. #[cfg(not(feature = "std"))] - pub fn verify_attestations( + pub fn verify_attestations<'a, S, R, D, I>( &self, _rng: &mut R, - namespace: &[u8], - subject: S::Subject<'_, D>, + subject: S::Subject<'a, D>, attestations: I, ) -> crate::certificate::Verification where S: Scheme, + S::Subject<'a, D>: Subject, R: CryptoRngCore, D: Digest, I: IntoIterator>, { - let (namespace, message) = subject.namespace_and_message(namespace); + let namespace = subject.namespace(&self.namespace); + let message = subject.message(); let mut invalid = alloc::collections::BTreeSet::new(); let mut verified = Vec::new(); @@ -186,7 +192,7 @@ impl Generic { continue; }; - if public_key.verify(namespace.as_ref(), message.as_ref(), &attestation.signature) { + if public_key.verify(namespace, &message, &attestation.signature) { verified.push(attestation); } else { invalid.insert(attestation.signer); @@ -230,15 +236,15 @@ impl Generic { /// /// Returns false if the certificate structure is invalid. #[cfg(feature = "std")] - fn batch_verify_certificate( + fn batch_verify_certificate<'a, S, D>( &self, batch: &mut Batch, - namespace: &[u8], - subject: S::Subject<'_, D>, + subject: S::Subject<'a, D>, certificate: &Certificate, ) -> bool where S: Scheme, + S::Subject<'a, D>: Subject, D: Digest, { // If the certificate signers length does not match the participant set, return false. @@ -257,13 +263,14 @@ impl Generic { } // Add the certificate to the batch. - let (namespace, message) = subject.namespace_and_message(namespace); + let namespace = subject.namespace(&self.namespace); + let message = subject.message(); for (signer, signature) in certificate.signers.iter().zip(&certificate.signatures) { let Some(public_key) = self.participants.key(signer) else { return false; }; - batch.add(namespace.as_ref(), message.as_ref(), public_key, signature); + batch.add(namespace, &message, public_key, signature); } true @@ -271,20 +278,20 @@ impl Generic { /// Verifies a certificate using batch verification. #[cfg(feature = "std")] - pub fn verify_certificate( + pub fn verify_certificate<'a, S, R, D>( &self, rng: &mut R, - namespace: &[u8], - subject: S::Subject<'_, D>, + subject: S::Subject<'a, D>, certificate: &Certificate, ) -> bool where S: Scheme, + S::Subject<'a, D>: Subject, R: CryptoRngCore, D: Digest, { let mut batch = Batch::new(); - if !self.batch_verify_certificate::(&mut batch, namespace, subject, certificate) { + if !self.batch_verify_certificate::(&mut batch, subject, certificate) { return false; } @@ -293,15 +300,15 @@ impl Generic { /// Verifies a certificate by checking each signature individually. #[cfg(not(feature = "std"))] - pub fn verify_certificate( + pub fn verify_certificate<'a, S, R, D>( &self, _rng: &mut R, - namespace: &[u8], - subject: S::Subject<'_, D>, + subject: S::Subject<'a, D>, certificate: &Certificate, ) -> bool where S: Scheme, + S::Subject<'a, D>: Subject, R: CryptoRngCore, D: Digest, { @@ -315,12 +322,13 @@ impl Generic { return false; } - let (namespace, message) = subject.namespace_and_message(namespace); + let namespace = subject.namespace(&self.namespace); + let message = subject.message(); for (signer, signature) in certificate.signers.iter().zip(&certificate.signatures) { let Some(public_key) = self.participants.key(signer) else { return false; }; - if !public_key.verify(namespace.as_ref(), message.as_ref(), signature) { + if !public_key.verify(namespace, &message, signature) { return false; } } @@ -330,21 +338,17 @@ impl Generic { /// Verifies multiple certificates in a batch. #[cfg(feature = "std")] - pub fn verify_certificates<'a, S, R, D, I>( - &self, - rng: &mut R, - namespace: &[u8], - certificates: I, - ) -> bool + pub fn verify_certificates<'a, S, R, D, I>(&self, rng: &mut R, certificates: I) -> bool where S: Scheme, + S::Subject<'a, D>: Subject, R: CryptoRngCore, D: Digest, I: Iterator, &'a Certificate)>, { let mut batch = Batch::new(); for (subject, certificate) in certificates { - if !self.batch_verify_certificate::(&mut batch, namespace, subject, certificate) { + if !self.batch_verify_certificate::(&mut batch, subject, certificate) { return false; } } @@ -354,20 +358,16 @@ impl Generic { /// Verifies multiple certificates one-by-one. #[cfg(not(feature = "std"))] - pub fn verify_certificates<'a, S, R, D, I>( - &self, - rng: &mut R, - namespace: &[u8], - certificates: I, - ) -> bool + pub fn verify_certificates<'a, S, R, D, I>(&self, rng: &mut R, certificates: I) -> bool where S: Scheme, + S::Subject<'a, D>: Subject, R: CryptoRngCore, D: Digest, I: Iterator, &'a Certificate)>, { for (subject, certificate) in certificates { - if !self.verify_certificate::(rng, namespace, subject, certificate) { + if !self.verify_certificate::(rng, subject, certificate) { return false; } } @@ -462,27 +462,47 @@ mod macros { /// /// This macro creates a complete wrapper struct with constructors, `Scheme` trait /// implementation, and a `fixture` function for testing. - /// The only required parameter is the `Subject` type, which varies per protocol. + /// + /// # Parameters + /// + /// - `$subject`: The subject type used as `Scheme::Subject<'a, D>`. Use `'a` and `D` + /// in the subject type to bind to the GAT lifetime and digest type parameters. + /// + /// - `$namespace`: The namespace type that implements [`Namespace`](crate::certificate::Namespace). + /// This type pre-computes and stores any protocol-specific namespace bytes derived from + /// a base namespace. The scheme calls `$namespace::derive(base)` at construction time + /// to create the namespace, then passes it to `Subject::namespace()` during signing + /// and verification. For simple protocols with only a base namespace, `Vec` can be used directly. + /// For protocols with multiple message types, a custom struct can pre-compute all variants. /// /// # Example /// ```ignore - /// impl_certificate_ed25519!(VoteSubject<'a, D>); + /// // For non-generic subject types with a single namespace: + /// impl_certificate_ed25519!(MySubject, Vec); + /// + /// // For protocols with generic subject types: + /// impl_certificate_ed25519!(Subject<'a, D>, Namespace); /// ``` #[macro_export] macro_rules! impl_certificate_ed25519 { - ($subject:ty) => { + ($subject:ty, $namespace:ty) => { /// Generates a test fixture with Ed25519 identities and signing schemes. /// /// Returns a [`commonware_cryptography::certificate::mocks::Fixture`] whose keys and /// scheme instances share a consistent ordering. #[cfg(feature = "mocks")] #[allow(dead_code)] - pub fn fixture(rng: &mut R, n: u32) -> $crate::certificate::mocks::Fixture + pub fn fixture( + rng: &mut R, + namespace: &[u8], + n: u32, + ) -> $crate::certificate::mocks::Fixture where R: rand::RngCore + rand::CryptoRng, { $crate::ed25519::certificate::mocks::fixture( rng, + namespace, n, Scheme::signer, Scheme::verifier, @@ -492,7 +512,7 @@ mod macros { /// Ed25519 signing scheme wrapper. #[derive(Clone, Debug)] pub struct Scheme { - generic: $crate::ed25519::certificate::Generic, + generic: $crate::ed25519::certificate::Generic<$namespace>, } impl Scheme { @@ -506,11 +526,13 @@ mod macros { /// Returns `None` if the provided private key does not match any participant /// in the participant set. pub fn signer( + namespace: &[u8], participants: commonware_utils::ordered::Set<$crate::ed25519::PublicKey>, private_key: $crate::ed25519::PrivateKey, ) -> Option { Some(Self { generic: $crate::ed25519::certificate::Generic::signer( + namespace, participants, private_key, )?, @@ -520,11 +542,15 @@ mod macros { /// Builds a verifier that can authenticate signatures without generating them. /// /// Participants use the same key for both identity and signing. - pub const fn verifier( + pub fn verifier( + namespace: &[u8], participants: commonware_utils::ordered::Set<$crate::ed25519::PublicKey>, ) -> Self { Self { - generic: $crate::ed25519::certificate::Generic::verifier(participants), + generic: $crate::ed25519::certificate::Generic::verifier( + namespace, + participants, + ), } } } @@ -545,16 +571,14 @@ mod macros { fn sign( &self, - namespace: &[u8], subject: Self::Subject<'_, D>, ) -> Option<$crate::certificate::Attestation> { - self.generic.sign::<_, D>(namespace, subject) + self.generic.sign::<_, D>(subject) } fn verify_attestation( &self, _rng: &mut R, - namespace: &[u8], subject: Self::Subject<'_, D>, attestation: &$crate::certificate::Attestation, ) -> bool @@ -563,13 +587,12 @@ mod macros { D: $crate::Digest, { self.generic - .verify_attestation::<_, D>(namespace, subject, attestation) + .verify_attestation::<_, D>(subject, attestation) } fn verify_attestations( &self, rng: &mut R, - namespace: &[u8], subject: Self::Subject<'_, D>, attestations: I, ) -> $crate::certificate::Verification @@ -578,12 +601,8 @@ mod macros { D: $crate::Digest, I: IntoIterator>, { - self.generic.verify_attestations::<_, _, D, _>( - rng, - namespace, - subject, - attestations, - ) + self.generic + .verify_attestations::<_, _, D, _>(rng, subject, attestations) } fn assemble(&self, attestations: I) -> Option @@ -596,39 +615,29 @@ mod macros { fn verify_certificate( &self, rng: &mut R, - namespace: &[u8], subject: Self::Subject<'_, D>, certificate: &Self::Certificate, ) -> bool { - self.generic.verify_certificate::( - rng, - namespace, - subject, - certificate, - ) + self.generic + .verify_certificate::(rng, subject, certificate) } - fn verify_certificates<'a, R, D, I>( - &self, - rng: &mut R, - namespace: &[u8], - certificates: I, - ) -> bool + fn verify_certificates<'a, R, D, I>(&self, rng: &mut R, certificates: I) -> bool where R: rand::Rng + rand::CryptoRng, D: $crate::Digest, I: Iterator, &'a Self::Certificate)>, { self.generic - .verify_certificates::(rng, namespace, certificates) + .verify_certificates::(rng, certificates) } fn is_attributable() -> bool { - $crate::ed25519::certificate::Generic::is_attributable() + $crate::ed25519::certificate::Generic::<$namespace>::is_attributable() } fn is_batchable() -> bool { - $crate::ed25519::certificate::Generic::is_batchable() + $crate::ed25519::certificate::Generic::<$namespace>::is_batchable() } fn certificate_codec_config( @@ -639,7 +648,7 @@ mod macros { fn certificate_codec_config_unbounded( ) -> ::Cfg { - $crate::ed25519::certificate::Generic::certificate_codec_config_unbounded() + $crate::ed25519::certificate::Generic::<$namespace>::certificate_codec_config_unbounded() } } }; @@ -662,18 +671,24 @@ mod tests { /// Test context type for generic scheme tests. #[derive(Clone, Debug)] - pub struct TestSubject<'a> { - pub message: &'a [u8], + pub struct TestSubject { + pub message: Bytes, } - impl<'a> Subject for TestSubject<'a> { - fn namespace_and_message(&self, namespace: &[u8]) -> (Bytes, Bytes) { - (namespace.to_vec().into(), self.message.to_vec().into()) + impl Subject for TestSubject { + type Namespace = Vec; + + fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] { + derived.as_ref() + } + + fn message(&self) -> Bytes { + self.message.clone() } } // Use the macro to generate the test scheme - impl_certificate_ed25519!(TestSubject<'a>); + impl_certificate_ed25519!(TestSubject, Vec); fn setup_signers(rng: &mut impl CryptoRngCore, n: u32) -> (Vec, Scheme) { let private_keys: Vec<_> = (0..n).map(|_| PrivateKey::random(&mut *rng)).collect(); @@ -685,24 +700,24 @@ mod tests { let signers = private_keys .into_iter() - .map(|sk| Scheme::signer(participants.clone(), sk).unwrap()) + .map(|sk| Scheme::signer(NAMESPACE, participants.clone(), sk).unwrap()) .collect(); - let verifier = Scheme::verifier(participants); + let verifier = Scheme::verifier(NAMESPACE, participants); (signers, verifier) } #[test] fn test_is_attributable() { - assert!(Generic::is_attributable()); + assert!(Generic::>::is_attributable()); assert!(Scheme::is_attributable()); } #[test] #[cfg(feature = "std")] fn test_is_batchable() { - assert!(Generic::is_batchable()); + assert!(Generic::>::is_batchable()); assert!(Scheme::is_batchable()); } @@ -720,12 +735,15 @@ mod tests { let scheme = &schemes[0]; let attestation = scheme - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(); assert!(scheme.verify_attestation::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &attestation )); } @@ -735,7 +753,9 @@ mod tests { let mut rng = test_rng(); let (_, verifier) = setup_signers(&mut rng, 4); assert!(verifier - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE) + }) .is_none()); } @@ -749,15 +769,18 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); let result = schemes[0].verify_attestations::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, attestations.clone(), ); assert!(result.invalid.is_empty()); @@ -768,8 +791,9 @@ mod tests { attestations_corrupted[0].signer = 999; let result = schemes[0].verify_attestations::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, attestations_corrupted, ); assert_eq!(result.invalid, vec![999]); @@ -780,8 +804,9 @@ mod tests { attestations_corrupted[0].signature = attestations_corrupted[1].signature.clone(); let result = schemes[0].verify_attestations::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, attestations_corrupted, ); // Batch verification may detect either signer 0 (wrong sig) or signer 1 (duplicate sig) @@ -799,8 +824,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -823,13 +850,19 @@ mod tests { // Create attestations in reverse sorted order (guaranteed non-sorted) let attestations = vec![ schemes[indexed[2].1] - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(), schemes[indexed[1].1] - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(), schemes[indexed[0].1] - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(), ]; @@ -850,8 +883,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -859,8 +894,9 @@ mod tests { assert!(verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE) + }, &certificate )); } @@ -875,8 +911,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -885,8 +923,9 @@ mod tests { // Valid certificate passes assert!(verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); @@ -895,8 +934,9 @@ mod tests { corrupted.signatures[0] = corrupted.signatures[1].clone(); assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &corrupted )); } @@ -911,8 +951,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -932,8 +974,10 @@ mod tests { .iter() .take(sub_quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -950,8 +994,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -971,8 +1017,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -986,8 +1034,9 @@ mod tests { assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); } @@ -1001,8 +1050,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -1013,8 +1064,9 @@ mod tests { assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); } @@ -1025,7 +1077,10 @@ mod tests { let (schemes, verifier) = setup_signers(&mut rng, 4); let quorum = quorum(schemes.len() as u32) as usize; - let messages = [b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()]; + let messages: Vec = [b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()] + .into_iter() + .map(Bytes::copy_from_slice) + .collect(); let mut certificates = Vec::new(); for msg in &messages { @@ -1033,19 +1088,25 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: msg }) - .unwrap() + s.sign::(TestSubject { + message: msg.clone(), + }) + .unwrap() }) .collect(); certificates.push(schemes[0].assemble(attestations).unwrap()); } - let certs_iter = messages - .iter() - .zip(&certificates) - .map(|(msg, cert)| (TestSubject { message: msg }, cert)); + let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| { + ( + TestSubject { + message: msg.clone(), + }, + cert, + ) + }); - assert!(verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, NAMESPACE, certs_iter)); + assert!(verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, certs_iter)); } #[test] @@ -1054,7 +1115,10 @@ mod tests { let (schemes, verifier) = setup_signers(&mut rng, 4); let quorum = quorum(schemes.len() as u32) as usize; - let messages = [b"msg1".as_slice(), b"msg2".as_slice()]; + let messages: Vec = [b"msg1".as_slice(), b"msg2".as_slice()] + .into_iter() + .map(Bytes::copy_from_slice) + .collect(); let mut certificates = Vec::new(); for msg in &messages { @@ -1062,8 +1126,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: msg }) - .unwrap() + s.sign::(TestSubject { + message: msg.clone(), + }) + .unwrap() }) .collect(); certificates.push(schemes[0].assemble(attestations).unwrap()); @@ -1072,14 +1138,16 @@ mod tests { // Corrupt second certificate certificates[1].signatures[0] = certificates[1].signatures[1].clone(); - let certs_iter = messages - .iter() - .zip(&certificates) - .map(|(msg, cert)| (TestSubject { message: msg }, cert)); + let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| { + ( + TestSubject { + message: msg.clone(), + }, + cert, + ) + }); - assert!( - !verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, NAMESPACE, certs_iter) - ); + assert!(!verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, certs_iter)); } #[test] @@ -1092,8 +1160,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -1114,16 +1184,20 @@ mod tests { let signer = schemes[0].clone(); assert!( signer - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .is_some(), "signer should produce votes" ); // A verifier cannot produce votes - let verifier = Scheme::verifier(participants); + let verifier = Scheme::verifier(NAMESPACE, participants); assert!( verifier - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .is_none(), "verifier should not produce votes" ); @@ -1139,8 +1213,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -1188,8 +1264,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -1205,8 +1283,9 @@ mod tests { assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate, )); } @@ -1221,8 +1300,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -1231,8 +1312,9 @@ mod tests { // Valid certificate passes assert!(verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate, )); @@ -1243,8 +1325,9 @@ mod tests { // Certificate verification should fail due to size mismatch assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate, )); } @@ -1259,8 +1342,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -1275,8 +1360,9 @@ mod tests { assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); } diff --git a/cryptography/src/ed25519/scheme.rs b/cryptography/src/ed25519/scheme.rs index a2e2abbcb2..d8f5e8f13b 100644 --- a/cryptography/src/ed25519/scheme.rs +++ b/cryptography/src/ed25519/scheme.rs @@ -594,7 +594,7 @@ mod tests { fn bad_signature() { let (private_key, public_key, message, _) = vector_1(); let private_key_2 = PrivateKey::random(&mut OsRng); - let bad_signature = private_key_2.sign_inner(None, message.as_ref()); + let bad_signature = private_key_2.sign_inner(None, &message); test_sign_and_verify(private_key, public_key, &message, bad_signature); } diff --git a/cryptography/src/secp256r1/certificate/mocks.rs b/cryptography/src/secp256r1/certificate/mocks.rs index e25c4b96c8..44ea937dfb 100644 --- a/cryptography/src/secp256r1/certificate/mocks.rs +++ b/cryptography/src/secp256r1/certificate/mocks.rs @@ -13,9 +13,10 @@ use rand::{CryptoRng, RngCore}; /// Builds ed25519 identities and matching Secp256r1 signing schemes. pub fn fixture( rng: &mut R, + namespace: &[u8], n: u32, - signer: impl Fn(BiMap, PrivateKey) -> Option, - verifier: impl Fn(BiMap) -> S, + signer: impl Fn(&[u8], BiMap, PrivateKey) -> Option, + verifier: impl Fn(&[u8], BiMap) -> S, ) -> Fixture where R: RngCore + CryptoRng, @@ -47,9 +48,11 @@ where let schemes = secp_privates .into_iter() - .map(|sk| signer(signers.clone(), sk).expect("scheme signer must be a participant")) + .map(|sk| { + signer(namespace, signers.clone(), sk).expect("scheme signer must be a participant") + }) .collect(); - let verifier = verifier(signers); + let verifier = verifier(namespace, signers); Fixture { participants: participants_vec, diff --git a/cryptography/src/secp256r1/certificate/mod.rs b/cryptography/src/secp256r1/certificate/mod.rs index 77ab2f4d3d..dfcb9ab550 100644 --- a/cryptography/src/secp256r1/certificate/mod.rs +++ b/cryptography/src/secp256r1/certificate/mod.rs @@ -7,9 +7,9 @@ pub mod mocks; use crate::{ - certificate::{Attestation, Scheme, Signers, Subject, Verification}, + certificate::{Attestation, Namespace, Scheme, Signers, Subject, Verification}, secp256r1::standard::{PrivateKey, PublicKey, Signature as Secp256r1Signature}, - Digest, PublicKey as PublicKeyTrait, Signer as _, Verifier as _, + Digest, Signer as _, Verifier as _, }; #[cfg(not(feature = "std"))] use alloc::{collections::BTreeSet, vec::Vec}; @@ -26,14 +26,16 @@ use std::collections::BTreeSet; /// context types. It can be reused across different protocols (simplex, aggregation, etc.) /// by wrapping it with protocol-specific trait implementations via the macro. #[derive(Clone, Debug)] -pub struct Generic { +pub struct Generic { /// Participants in the committee. pub participants: BiMap, /// Key used for generating signatures. pub signer: Option<(u32, PrivateKey)>, + /// Pre-computed namespace(s) for this subject type. + pub namespace: N, } -impl Generic

{ +impl Generic { /// Creates a new scheme instance with the provided key material. /// /// Participants have both an identity key and a signing key. The identity key @@ -42,7 +44,11 @@ impl Generic

{ /// /// Returns `None` if the provided private key does not match any signing key /// in the participant set. - pub fn signer(participants: BiMap, private_key: PrivateKey) -> Option { + pub fn signer( + namespace: &[u8], + participants: BiMap, + private_key: PrivateKey, + ) -> Option { let public_key = private_key.public_key(); let signer = participants .values() @@ -53,6 +59,7 @@ impl Generic

{ Some(Self { participants, signer: Some(signer), + namespace: N::derive(namespace), }) } @@ -61,10 +68,11 @@ impl Generic

{ /// Participants have both an identity key and a signing key. The identity key /// is used for participant set ordering and indexing, while the signing key is used for /// verification. - pub const fn verifier(participants: BiMap) -> Self { + pub fn verifier(namespace: &[u8], participants: BiMap) -> Self { Self { participants, signer: None, + namespace: N::derive(namespace), } } @@ -79,15 +87,15 @@ impl Generic

{ } /// Signs a subject and returns the attestation. - pub fn sign(&self, namespace: &[u8], subject: S::Subject<'_, D>) -> Option> + pub fn sign<'a, S, D>(&self, subject: S::Subject<'a, D>) -> Option> where S: Scheme, + S::Subject<'a, D>: Subject, D: Digest, { let (index, private_key) = self.signer.as_ref()?; - let (namespace, message) = subject.namespace_and_message(namespace); - let signature = private_key.sign(namespace.as_ref(), message.as_ref()); + let signature = private_key.sign(subject.namespace(&self.namespace), &subject.message()); Some(Attestation { signer: *index, @@ -96,39 +104,43 @@ impl Generic

{ } /// Verifies a single attestation from a signer. - pub fn verify_attestation( + pub fn verify_attestation<'a, S, D>( &self, - namespace: &[u8], - subject: S::Subject<'_, D>, + subject: S::Subject<'a, D>, attestation: &Attestation, ) -> bool where S: Scheme, + S::Subject<'a, D>: Subject, D: Digest, { let Some(public_key) = self.participants.value(attestation.signer as usize) else { return false; }; - let (namespace, message) = subject.namespace_and_message(namespace); - public_key.verify(namespace.as_ref(), message.as_ref(), &attestation.signature) + public_key.verify( + subject.namespace(&self.namespace), + &subject.message(), + &attestation.signature, + ) } /// Verifies attestations one-by-one and returns verified attestations and invalid signers. - pub fn verify_attestations( + pub fn verify_attestations<'a, S, R, D, I>( &self, _rng: &mut R, - namespace: &[u8], - subject: S::Subject<'_, D>, + subject: S::Subject<'a, D>, attestations: I, ) -> Verification where S: Scheme, + S::Subject<'a, D>: Subject, R: Rng + CryptoRng, D: Digest, I: IntoIterator>, { - let (namespace, message) = subject.namespace_and_message(namespace); + let namespace = subject.namespace(&self.namespace); + let message = subject.message(); let mut invalid = BTreeSet::new(); let mut verified = Vec::new(); @@ -139,7 +151,7 @@ impl Generic

{ continue; }; - if public_key.verify(namespace.as_ref(), message.as_ref(), &attestation.signature) { + if public_key.verify(namespace, &message, &attestation.signature) { verified.push(attestation); } else { invalid.insert(attestation.signer); @@ -180,15 +192,15 @@ impl Generic

{ } /// Verifies a certificate by checking each signature individually. - pub fn verify_certificate( + pub fn verify_certificate<'a, S, R, D>( &self, _rng: &mut R, - namespace: &[u8], - subject: S::Subject<'_, D>, + subject: S::Subject<'a, D>, certificate: &Certificate, ) -> bool where S: Scheme, + S::Subject<'a, D>: Subject, R: Rng + CryptoRng, D: Digest, { @@ -207,12 +219,13 @@ impl Generic

{ return false; } - let (namespace, message) = subject.namespace_and_message(namespace); + let namespace = subject.namespace(&self.namespace); + let message = subject.message(); for (signer, signature) in certificate.signers.iter().zip(&certificate.signatures) { let Some(public_key) = self.participants.value(signer as usize) else { return false; }; - if !public_key.verify(namespace.as_ref(), message.as_ref(), signature) { + if !public_key.verify(namespace, &message, signature) { return false; } } @@ -313,7 +326,7 @@ mod macros { /// ``` #[macro_export] macro_rules! impl_certificate_secp256r1 { - ($subject:ty) => { + ($subject:ty, $namespace:ty) => { /// Generates a test fixture with Ed25519 identities and Secp256r1 signing schemes. /// /// Returns a [`commonware_cryptography::certificate::mocks::Fixture`] whose keys and @@ -322,28 +335,37 @@ mod macros { #[allow(dead_code)] pub fn fixture( rng: &mut R, + namespace: &[u8], n: u32, ) -> $crate::certificate::mocks::Fixture> where R: rand::RngCore + rand::CryptoRng, { - $crate::secp256r1::certificate::mocks::fixture(rng, n, Scheme::signer, Scheme::verifier) + $crate::secp256r1::certificate::mocks::fixture( + rng, + namespace, + n, + Scheme::signer, + Scheme::verifier, + ) } /// Secp256r1 signing scheme wrapper. #[derive(Clone, Debug)] pub struct Scheme { - generic: $crate::secp256r1::certificate::Generic

, + generic: $crate::secp256r1::certificate::Generic, } impl Scheme

{ /// Creates a new scheme instance with the provided key material. pub fn signer( + namespace: &[u8], participants: commonware_utils::ordered::BiMap, private_key: $crate::secp256r1::standard::PrivateKey, ) -> Option { Some(Self { generic: $crate::secp256r1::certificate::Generic::signer( + namespace, participants, private_key, )?, @@ -351,11 +373,13 @@ mod macros { } /// Builds a verifier that can authenticate signatures and certificates. - pub const fn verifier( + pub fn verifier( + namespace: &[u8], participants: commonware_utils::ordered::BiMap, ) -> Self { Self { generic: $crate::secp256r1::certificate::Generic::verifier( + namespace, participants, ), } @@ -378,16 +402,14 @@ mod macros { fn sign( &self, - namespace: &[u8], subject: Self::Subject<'_, D>, ) -> Option<$crate::certificate::Attestation> { - self.generic.sign::<_, D>(namespace, subject) + self.generic.sign::<_, D>(subject) } fn verify_attestation( &self, _rng: &mut R, - namespace: &[u8], subject: Self::Subject<'_, D>, attestation: &$crate::certificate::Attestation, ) -> bool @@ -396,13 +418,12 @@ mod macros { D: $crate::Digest, { self.generic - .verify_attestation::<_, D>(namespace, subject, attestation) + .verify_attestation::<_, D>(subject, attestation) } fn verify_attestations( &self, rng: &mut R, - namespace: &[u8], subject: Self::Subject<'_, D>, attestations: I, ) -> $crate::certificate::Verification @@ -413,7 +434,6 @@ mod macros { { self.generic.verify_attestations::<_, _, D, _>( rng, - namespace, subject, attestations, ) @@ -429,24 +449,22 @@ mod macros { fn verify_certificate( &self, rng: &mut R, - namespace: &[u8], subject: Self::Subject<'_, D>, certificate: &Self::Certificate, ) -> bool { self.generic.verify_certificate::( rng, - namespace, subject, certificate, ) } fn is_attributable() -> bool { - $crate::secp256r1::certificate::Generic::

::is_attributable() + $crate::secp256r1::certificate::Generic::::is_attributable() } fn is_batchable() -> bool { - $crate::secp256r1::certificate::Generic::

::is_batchable() + $crate::secp256r1::certificate::Generic::::is_batchable() } fn certificate_codec_config( @@ -455,10 +473,8 @@ mod macros { self.generic.certificate_codec_config() } - fn certificate_codec_config_unbounded( - ) -> ::Cfg { - $crate::secp256r1::certificate::Generic::

::certificate_codec_config_unbounded( - ) + fn certificate_codec_config_unbounded() -> ::Cfg { + $crate::secp256r1::certificate::Generic::::certificate_codec_config_unbounded() } } }; @@ -482,18 +498,24 @@ mod tests { /// Test context type for generic scheme tests. #[derive(Clone, Debug)] - pub struct TestSubject<'a> { - pub message: &'a [u8], + pub struct TestSubject { + pub message: Bytes, } - impl<'a> Subject for TestSubject<'a> { - fn namespace_and_message(&self, namespace: &[u8]) -> (Bytes, Bytes) { - (namespace.to_vec().into(), self.message.to_vec().into()) + impl Subject for TestSubject { + type Namespace = Vec; + + fn namespace<'a>(&self, derived: &'a Self::Namespace) -> &'a [u8] { + derived.as_ref() + } + + fn message(&self) -> Bytes { + self.message.clone() } } // Use the macro to generate the test scheme - impl_certificate_secp256r1!(TestSubject<'a>); + impl_certificate_secp256r1!(TestSubject, Vec); fn setup_signers( rng: &mut impl CryptoRngCore, @@ -513,23 +535,23 @@ mod tests { let signers = private_keys .into_iter() - .map(|sk| Scheme::signer(participants.clone(), sk).unwrap()) + .map(|sk| Scheme::signer(NAMESPACE, participants.clone(), sk).unwrap()) .collect(); - let verifier = Scheme::verifier(participants); + let verifier = Scheme::verifier(NAMESPACE, participants); (signers, verifier) } #[test] fn test_is_attributable() { - assert!(Generic::::is_attributable()); + assert!(Generic::>::is_attributable()); assert!(Scheme::::is_attributable()); } #[test] fn test_is_not_batchable() { - assert!(!Generic::::is_batchable()); + assert!(!Generic::>::is_batchable()); assert!(!Scheme::::is_batchable()); } @@ -540,12 +562,15 @@ mod tests { let scheme = &schemes[0]; let attestation = scheme - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(); assert!(scheme.verify_attestation::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &attestation )); } @@ -555,7 +580,9 @@ mod tests { let mut rng = test_rng(); let (_, verifier) = setup_signers(&mut rng, 4); assert!(verifier - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .is_none()); } @@ -569,15 +596,18 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); let result = schemes[0].verify_attestations::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, attestations.clone(), ); assert!(result.invalid.is_empty()); @@ -588,8 +618,9 @@ mod tests { attestations_corrupted[0].signer = 999; let result = schemes[0].verify_attestations::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, attestations_corrupted, ); assert_eq!(result.invalid, vec![999]); @@ -601,8 +632,9 @@ mod tests { attestations_corrupted[0].signature = attestations_corrupted[1].signature.clone(); let result = schemes[0].verify_attestations::<_, Sha256Digest, _>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, attestations_corrupted, ); // Without batch verification, we detect exactly which signer has invalid sig @@ -620,8 +652,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -644,13 +678,19 @@ mod tests { // Create attestations in reverse sorted order (guaranteed non-sorted) let attestations = vec![ schemes[indexed[2].1] - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(), schemes[indexed[1].1] - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(), schemes[indexed[0].1] - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .unwrap(), ]; @@ -671,8 +711,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -680,8 +722,9 @@ mod tests { assert!(verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); } @@ -696,8 +739,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -706,8 +751,9 @@ mod tests { // Valid certificate passes assert!(verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); @@ -716,8 +762,9 @@ mod tests { corrupted.signatures[0] = corrupted.signatures[1].clone(); assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &corrupted )); } @@ -732,8 +779,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -753,8 +802,10 @@ mod tests { .iter() .take(sub_quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -771,8 +822,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -792,8 +845,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -807,8 +862,9 @@ mod tests { assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); } @@ -822,8 +878,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -834,8 +892,9 @@ mod tests { assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); } @@ -846,7 +905,10 @@ mod tests { let (schemes, verifier) = setup_signers(&mut rng, 4); let quorum = quorum(schemes.len() as u32) as usize; - let messages = [b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()]; + let messages: Vec = [b"msg1".as_slice(), b"msg2".as_slice(), b"msg3".as_slice()] + .into_iter() + .map(Bytes::copy_from_slice) + .collect(); let mut certificates = Vec::new(); for msg in &messages { @@ -854,19 +916,25 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: msg }) - .unwrap() + s.sign::(TestSubject { + message: msg.clone(), + }) + .unwrap() }) .collect(); certificates.push(schemes[0].assemble(attestations).unwrap()); } - let certs_iter = messages - .iter() - .zip(&certificates) - .map(|(msg, cert)| (TestSubject { message: msg }, cert)); + let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| { + ( + TestSubject { + message: msg.clone(), + }, + cert, + ) + }); - assert!(verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, NAMESPACE, certs_iter)); + assert!(verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, certs_iter)); } #[test] @@ -875,7 +943,10 @@ mod tests { let (schemes, verifier) = setup_signers(&mut rng, 4); let quorum = quorum(schemes.len() as u32) as usize; - let messages = [b"msg1".as_slice(), b"msg2".as_slice()]; + let messages: Vec = [b"msg1".as_slice(), b"msg2".as_slice()] + .into_iter() + .map(Bytes::copy_from_slice) + .collect(); let mut certificates = Vec::new(); for msg in &messages { @@ -883,8 +954,10 @@ mod tests { .iter() .take(quorum) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: msg }) - .unwrap() + s.sign::(TestSubject { + message: msg.clone(), + }) + .unwrap() }) .collect(); certificates.push(schemes[0].assemble(attestations).unwrap()); @@ -893,14 +966,16 @@ mod tests { // Corrupt second certificate certificates[1].signatures[0] = certificates[1].signatures[1].clone(); - let certs_iter = messages - .iter() - .zip(&certificates) - .map(|(msg, cert)| (TestSubject { message: msg }, cert)); + let certs_iter = messages.iter().zip(&certificates).map(|(msg, cert)| { + ( + TestSubject { + message: msg.clone(), + }, + cert, + ) + }); - assert!( - !verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, NAMESPACE, certs_iter) - ); + assert!(!verifier.verify_certificates::<_, Sha256Digest, _>(&mut rng, certs_iter)); } #[test] @@ -913,8 +988,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -935,16 +1012,20 @@ mod tests { let signer = schemes[0].clone(); assert!( signer - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .is_some(), "signer should produce votes" ); // A verifier cannot produce votes - let verifier = Scheme::verifier(participants); + let verifier = Scheme::verifier(NAMESPACE, participants); assert!( verifier - .sign::(NAMESPACE, TestSubject { message: MESSAGE }) + .sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) .is_none(), "verifier should not produce votes" ); @@ -960,8 +1041,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -1009,8 +1092,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -1026,8 +1111,9 @@ mod tests { assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate, )); } @@ -1042,8 +1128,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -1052,8 +1140,9 @@ mod tests { // Valid certificate passes assert!(verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate, )); @@ -1064,8 +1153,9 @@ mod tests { // Certificate verification should fail due to size mismatch assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate, )); } @@ -1080,8 +1170,10 @@ mod tests { .iter() .take(3) .map(|s| { - s.sign::(NAMESPACE, TestSubject { message: MESSAGE }) - .unwrap() + s.sign::(TestSubject { + message: Bytes::from_static(MESSAGE), + }) + .unwrap() }) .collect(); @@ -1096,8 +1188,9 @@ mod tests { assert!(!verifier.verify_certificate::<_, Sha256Digest>( &mut rng, - NAMESPACE, - TestSubject { message: MESSAGE }, + TestSubject { + message: Bytes::from_static(MESSAGE), + }, &certificate )); } diff --git a/examples/bridge/src/application/actor.rs b/examples/bridge/src/application/actor.rs index a44e5f2389..a03cee8854 100644 --- a/examples/bridge/src/application/actor.rs +++ b/examples/bridge/src/application/actor.rs @@ -30,9 +30,8 @@ const GENESIS: &[u8] = b"commonware is neat"; pub struct Application { context: R, indexer: (Sender, Receiver), - namespace: Vec, - public: ::Public, - other_certificate_verifier: Scheme, + this_network: ::Public, + other_network: Scheme, hasher: H, mailbox: mpsc::Receiver>, } @@ -41,18 +40,17 @@ impl Application) -> (Self, Scheme, Mailbox) { let (sender, mailbox) = mpsc::channel(config.mailbox_size); + let this_network = *config.this_network.identity(); ( Self { context, indexer: config.indexer, - namespace: config.namespace, - public: *config.identity.public(), - other_certificate_verifier: Scheme::certificate_verifier(config.other_public), + this_network, + other_network: config.other_network, hasher: config.hasher, mailbox, }, - Scheme::signer(config.participants, config.identity, config.share) - .expect("share must be in participants"), + config.this_network, Mailbox::new(sender), ) } @@ -82,7 +80,7 @@ impl Application(inbound::GetFinalization { - network: *self.other_certificate_verifier.identity(), + network: *self.other_network.identity(), }) .encode(); indexer_sender @@ -106,11 +104,7 @@ impl Application Application(inbound::PutBlock { - network: self.public, + network: self.this_network, block, }) .encode(); @@ -154,7 +148,7 @@ impl Application { // Fetch payload from indexer let msg = Inbound::GetBlock(inbound::GetBlock { - network: self.public, + network: self.this_network, digest: payload, }) .encode(); @@ -183,11 +177,8 @@ impl Application { - let result = finalization.verify( - &mut self.context, - &self.other_certificate_verifier, - &self.namespace, - ); + let result = + finalization.verify(&mut self.context, &self.other_network); let _ = response.send(result); } } @@ -210,7 +201,7 @@ impl Application(inbound::PutFinalization { - network: self.public, + network: self.this_network, finalization, }) .encode(); diff --git a/examples/bridge/src/application/mod.rs b/examples/bridge/src/application/mod.rs index 8fd9c57844..9cfa64c1b1 100644 --- a/examples/bridge/src/application/mod.rs +++ b/examples/bridge/src/application/mod.rs @@ -2,21 +2,13 @@ //! This includes things like how to produce/verify blocks and how to identify which //! participants are active at a given view. -use commonware_cryptography::{ - bls12381::primitives::{ - group, - sharing::Sharing, - variant::{MinSig, Variant}, - }, - ed25519::PublicKey, - Hasher, -}; +use crate::Scheme; +use commonware_cryptography::Hasher; mod actor; pub use actor::Application; use commonware_runtime::{Sink, Stream}; use commonware_stream::{Receiver, Sender}; -use commonware_utils::ordered::Set; mod ingress; /// Configuration for the application. @@ -26,14 +18,11 @@ pub struct Config { /// Hashing scheme to use. pub hasher: H, - pub namespace: Vec, - pub identity: Sharing, - pub other_public: ::Public, + /// Signing scheme for this network. + pub this_network: Scheme, - /// Participants active in consensus. - pub participants: Set, - - pub share: group::Share, + /// Certificate verifier for the other network. + pub other_network: Scheme, /// Number of messages from consensus to hold in our backlog /// before blocking. diff --git a/examples/bridge/src/bin/indexer.rs b/examples/bridge/src/bin/indexer.rs index 7775b21812..0d884192ad 100644 --- a/examples/bridge/src/bin/indexer.rs +++ b/examples/bridge/src/bin/indexer.rs @@ -116,7 +116,7 @@ fn main() { .expect("public keys are unique"); // Configure networks - let mut namespaces: HashMap)> = HashMap::new(); + let mut verifiers: HashMap = HashMap::new(); let mut blocks: HashMap>> = HashMap::new(); let mut finalizations: HashMap>> = HashMap::new(); @@ -131,7 +131,7 @@ fn main() { let public = ::Public::decode(network.as_ref()).expect("Network not well-formed"); let namespace = union(APPLICATION_NAMESPACE, CONSENSUS_SUFFIX); - namespaces.insert(public, (Scheme::certificate_verifier(public), namespace)); + verifiers.insert(public, Scheme::certificate_verifier(&namespace, public)); blocks.insert(public, HashMap::new()); finalizations.insert(public, BTreeMap::new()); } @@ -183,11 +183,11 @@ fn main() { }; // Verify signature - let Some((verifier, namespace)) = namespaces.get(&incoming.network) else { + let Some(verifier) = verifiers.get(&incoming.network) else { let _ = response.send(false); continue; }; - if !incoming.finalization.verify(&mut ctx, verifier, namespace) { + if !incoming.finalization.verify(&mut ctx, verifier) { let _ = response.send(false); continue; } diff --git a/examples/bridge/src/bin/validator.rs b/examples/bridge/src/bin/validator.rs index b6eac326af..be6e9f6f96 100644 --- a/examples/bridge/src/bin/validator.rs +++ b/examples/bridge/src/bin/validator.rs @@ -1,6 +1,6 @@ use clap::{value_parser, Arg, Command}; use commonware_bridge::{ - application, APPLICATION_NAMESPACE, CONSENSUS_SUFFIX, INDEXER_NAMESPACE, P2P_SUFFIX, + application, Scheme, APPLICATION_NAMESPACE, CONSENSUS_SUFFIX, INDEXER_NAMESPACE, P2P_SUFFIX, }; use commonware_codec::{Decode, DecodeExt}; use commonware_consensus::{ @@ -218,17 +218,18 @@ fn main() { // Initialize application let consensus_namespace = union(APPLICATION_NAMESPACE, CONSENSUS_SUFFIX); + let this_network = + Scheme::signer(&consensus_namespace, validators.clone(), identity, share) + .expect("share must be in participants"); + let other_network = Scheme::certificate_verifier(&consensus_namespace, other_public); let (application, scheme, mailbox) = application::Application::new( context.with_label("application"), application::Config { indexer, - namespace: consensus_namespace.clone(), - identity, - other_public, hasher: Sha256::default(), + this_network, + other_network, mailbox_size: 1024, - participants: validators.clone(), - share, }, ); @@ -245,7 +246,6 @@ fn main() { partition: String::from("log"), mailbox_size: 1024, epoch: Epoch::zero(), - namespace: consensus_namespace, replay_buffer: NZUsize!(1024 * 1024), write_buffer: NZUsize!(1024 * 1024), leader_timeout: Duration::from_secs(1), diff --git a/examples/log/src/application/actor.rs b/examples/log/src/application/actor.rs index 48070643fd..235084c5ad 100644 --- a/examples/log/src/application/actor.rs +++ b/examples/log/src/application/actor.rs @@ -35,8 +35,7 @@ impl Application { hasher: config.hasher, mailbox, }, - Scheme::signer(config.participants, config.private_key) - .expect("private key must be in participants"), + config.scheme, Reporter::new(), Mailbox::new(sender), ) diff --git a/examples/log/src/application/mod.rs b/examples/log/src/application/mod.rs index 8f94218abf..d1b9630d36 100644 --- a/examples/log/src/application/mod.rs +++ b/examples/log/src/application/mod.rs @@ -2,11 +2,7 @@ //! This includes things like how to produce/verify blocks and how to identify which //! participants are active at a given view. -use commonware_cryptography::{ - ed25519::{PrivateKey, PublicKey}, - Hasher, -}; -use commonware_utils::ordered::Set; +use commonware_cryptography::Hasher; mod actor; pub use actor::Application; @@ -20,11 +16,8 @@ pub struct Config { /// Hashing scheme to use. pub hasher: H, - /// Participants active in consensus. - pub participants: Set, - - /// Our private key. - pub private_key: PrivateKey, + /// Signing scheme for this network. + pub scheme: Scheme, /// Number of messages from consensus to hold in our backlog /// before blocking. diff --git a/examples/log/src/main.rs b/examples/log/src/main.rs index 7ed7f3b9c6..db93410646 100644 --- a/examples/log/src/main.rs +++ b/examples/log/src/main.rs @@ -192,13 +192,14 @@ fn main() { // Initialize application let namespace = union(APPLICATION_NAMESPACE, b"_CONSENSUS"); + let scheme = application::Scheme::signer(&namespace, validators.clone(), signer.clone()) + .expect("private key must be in participants"); let (application, scheme, reporter, mailbox) = application::Application::new( context.with_label("application"), application::Config { hasher: Sha256::default(), + scheme, mailbox_size: 1024, - participants: validators.clone(), - private_key: signer.clone(), }, ); @@ -210,7 +211,6 @@ fn main() { automaton: mailbox.clone(), relay: mailbox.clone(), reporter: reporter.clone(), - namespace, partition: String::from("log"), mailbox_size: 1024, epoch: Epoch::zero(), diff --git a/examples/reshare/src/application/scheme.rs b/examples/reshare/src/application/scheme.rs index 15a73f5b9c..ec288dd010 100644 --- a/examples/reshare/src/application/scheme.rs +++ b/examples/reshare/src/application/scheme.rs @@ -26,14 +26,16 @@ pub type EdScheme = simplex::scheme::ed25519::Scheme; #[derive(Clone)] pub struct Provider { schemes: Arc>>>, + namespace: Vec, certificate_verifier: Option>, signer: C, } impl Provider { - pub fn new(signer: C, certificate_verifier: Option) -> Self { + pub fn new(namespace: Vec, signer: C, certificate_verifier: Option) -> Self { Self { schemes: Arc::new(Mutex::new(HashMap::new())), + namespace, certificate_verifier: certificate_verifier.map(Arc::new), signer, } @@ -88,6 +90,7 @@ pub trait EpochProvider { /// Returns `None` for schemes that don't support epoch-independent verification /// (Ed25519 during the initial DKG requires the full participant list to verify certificates). fn certificate_verifier( + namespace: &[u8], output: &dkg::Output, ) -> Option; } @@ -104,6 +107,7 @@ impl EpochProvider for Provider, ed25519::Private transition.share.as_ref().map_or_else( || { ThresholdScheme::verifier( + &self.namespace, transition.dealers.clone(), transition .poly @@ -113,6 +117,7 @@ impl EpochProvider for Provider, ed25519::Private }, |share| { ThresholdScheme::signer( + &self.namespace, transition.dealers.clone(), transition .poly @@ -126,9 +131,11 @@ impl EpochProvider for Provider, ed25519::Private } fn certificate_verifier( + namespace: &[u8], output: &dkg::Output, ) -> Option { Some(ThresholdScheme::certificate_verifier( + namespace, *output.public().public(), )) } @@ -143,11 +150,16 @@ impl EpochProvider for Provider { &self, transition: &EpochTransition, ) -> Self::Scheme { - EdScheme::signer(transition.dealers.clone(), self.signer.clone()) - .unwrap_or_else(|| EdScheme::verifier(transition.dealers.clone())) + EdScheme::signer( + &self.namespace, + transition.dealers.clone(), + self.signer.clone(), + ) + .unwrap_or_else(|| EdScheme::verifier(&self.namespace, transition.dealers.clone())) } fn certificate_verifier( + _namespace: &[u8], _output: &dkg::Output, ) -> Option { // Ed25519 doesn't support epoch-independent certificate verification diff --git a/examples/reshare/src/engine.rs b/examples/reshare/src/engine.rs index 12167e9bb1..40c4ae8b7a 100644 --- a/examples/reshare/src/engine.rs +++ b/examples/reshare/src/engine.rs @@ -220,11 +220,14 @@ where // Create the certificate verifier from the initial output (if available). // This allows epoch-independent certificate verification after the DKG is complete. - let certificate_verifier = config - .output - .as_ref() - .and_then( as EpochProvider>::certificate_verifier); - let provider = Provider::new(config.signer.clone(), certificate_verifier); + let certificate_verifier = config.output.as_ref().and_then(|output| { + as EpochProvider>::certificate_verifier(&consensus_namespace, output) + }); + let provider = Provider::new( + consensus_namespace.clone(), + config.signer.clone(), + certificate_verifier, + ); let (marshal, marshal_mailbox, _processed_height) = marshal::Actor::init( context.with_label("marshal"), @@ -240,7 +243,6 @@ where .get() .saturating_mul(SYNCER_ACTIVITY_TIMEOUT_MULTIPLIER), ), - namespace: consensus_namespace.clone(), prunable_items_per_section: PRUNABLE_ITEMS_PER_SECTION, buffer_pool: buffer_pool.clone(), replay_buffer: REPLAY_BUFFER, @@ -265,7 +267,6 @@ where application, provider, marshal: marshal_mailbox, - namespace: consensus_namespace, muxer_size: MAILBOX_SIZE, mailbox_size: MAILBOX_SIZE, partition_prefix: format!("{}_consensus", config.partition_prefix), diff --git a/examples/reshare/src/orchestrator/actor.rs b/examples/reshare/src/orchestrator/actor.rs index b0bec0015f..888e1acda9 100644 --- a/examples/reshare/src/orchestrator/actor.rs +++ b/examples/reshare/src/orchestrator/actor.rs @@ -45,7 +45,6 @@ where pub provider: Provider, pub marshal: marshal::Mailbox>, - pub namespace: Vec, pub muxer_size: usize, pub mailbox_size: usize, @@ -76,7 +75,6 @@ where marshal: marshal::Mailbox>, provider: Provider, - namespace: Vec, muxer_size: usize, partition_prefix: String, pool_ref: PoolRef, @@ -111,7 +109,6 @@ where oracle: config.oracle, marshal: config.marshal, provider: config.provider, - namespace: config.namespace, muxer_size: config.muxer_size, partition_prefix: config.partition_prefix, pool_ref, @@ -302,7 +299,6 @@ where partition: format!("{}_consensus_{}", self.partition_prefix, epoch), mailbox_size: 1024, epoch, - namespace: self.namespace.clone(), replay_buffer: NZUsize!(1024 * 1024), write_buffer: NZUsize!(1024 * 1024), leader_timeout: Duration::from_secs(1),