Skip to content

Commit 3edd30e

Browse files
committed
simulacrum: ensure determinism when using a seeded rng
1 parent a3ae600 commit 3edd30e

File tree

2 files changed

+56
-0
lines changed

2 files changed

+56
-0
lines changed

crates/simulacrum/src/lib.rs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
//!
1111
//! [`Simulacrum`]: crate::Simulacrum
1212
13+
use std::num::NonZeroUsize;
14+
1315
use anyhow::{anyhow, Result};
1416
use rand::rngs::OsRng;
1517
use sui_config::{genesis, transaction_deny_config::TransactionDenyConfig};
@@ -91,6 +93,7 @@ where
9193
let config = ConfigBuilder::new_with_temp_dir()
9294
.rng(&mut rng)
9395
.with_chain_start_timestamp_ms(1)
96+
.deterministic_committee_size(NonZeroUsize::new(1).unwrap())
9497
.build();
9598
let keystore = KeyStore::from_newtork_config(&config);
9699
let store = InMemoryStore::new(&config.genesis);
@@ -358,6 +361,7 @@ impl<'a> CommitteeWithKeys<'a> {
358361
mod tests {
359362
use std::time::Duration;
360363

364+
use rand::{rngs::StdRng, SeedableRng};
361365
use shared_crypto::intent::Intent;
362366
use sui_types::{
363367
base_types::SuiAddress,
@@ -369,6 +373,36 @@ mod tests {
369373

370374
use super::*;
371375

376+
#[test]
377+
fn deterministic_genesis() {
378+
let rng = StdRng::from_seed([9; 32]);
379+
let chain1 = Simulacrum::new_with_rng(rng);
380+
let genesis_checkpoint_digest1 = chain1
381+
.store()
382+
.get_checkpoint_by_sequence_number(0)
383+
.unwrap()
384+
.digest();
385+
386+
let rng = StdRng::from_seed([9; 32]);
387+
let chain2 = Simulacrum::new_with_rng(rng);
388+
let genesis_checkpoint_digest2 = chain2
389+
.store()
390+
.get_checkpoint_by_sequence_number(0)
391+
.unwrap()
392+
.digest();
393+
394+
assert_eq!(genesis_checkpoint_digest1, genesis_checkpoint_digest2);
395+
396+
// Ensure the committees are different when using different seeds
397+
let rng = StdRng::from_seed([0; 32]);
398+
let chain3 = Simulacrum::new_with_rng(rng);
399+
400+
assert_ne!(
401+
chain1.store().get_committee_by_epoch(0),
402+
chain3.store().get_committee_by_epoch(0),
403+
);
404+
}
405+
372406
#[test]
373407
fn simple() {
374408
let steps = 10;

crates/sui-swarm-config/src/network_config_builder.rs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,9 @@ pub enum CommitteeConfig {
2020
Size(NonZeroUsize),
2121
Validators(Vec<ValidatorGenesisConfig>),
2222
AccountKeys(Vec<AccountKeyPair>),
23+
/// Indicates that a committee should be deterministically generated, useing the provided rng
24+
/// as a source of randomness as well as generating deterministic network port information.
25+
Deterministic(NonZeroUsize),
2326
}
2427

2528
pub type SupportedProtocolVersionsCallback = Arc<
@@ -86,6 +89,11 @@ impl<R> ConfigBuilder<R> {
8689
self
8790
}
8891

92+
pub fn deterministic_committee_size(mut self, committee_size: NonZeroUsize) -> Self {
93+
self.committee = CommitteeConfig::Deterministic(committee_size);
94+
self
95+
}
96+
8997
pub fn with_validator_account_keys(mut self, keys: Vec<AccountKeyPair>) -> Self {
9098
self.committee = CommitteeConfig::AccountKeys(keys);
9199
self
@@ -232,6 +240,20 @@ impl<R: rand::RngCore + rand::CryptoRng> ConfigBuilder<R> {
232240
})
233241
.collect::<Vec<_>>()
234242
}
243+
CommitteeConfig::Deterministic(size) => {
244+
let mut configs = vec![];
245+
for i in 0..size.into() {
246+
let port_offset = 8000 + i * 10;
247+
let mut builder = ValidatorGenesisConfigBuilder::new()
248+
.with_ip("127.0.0.1".to_owned())
249+
.with_deterministic_ports(port_offset as u16);
250+
if let Some(rgp) = self.reference_gas_price {
251+
builder = builder.with_gas_price(rgp);
252+
}
253+
configs.push(builder.build(&mut rng));
254+
}
255+
configs
256+
}
235257
};
236258

237259
let genesis_config = self

0 commit comments

Comments
 (0)