Skip to content

Commit

Permalink
Changes in ArbitraryGenerator (#502)
Browse files Browse the repository at this point in the history
* Changes in ArbitraryGenerator
- Used 128 bytes of default buffer size vs 2^24 bytes buffer size earlier.
- Used thread_rng to generate random number in each call as compared to using slices from pre-initialized huge buffer
- Updated unit tests accordingly to work with new changes.

* refactor: arbitrary generator

* used OsRng by default
* provision of user preferred RNGs
* supported old interface

* docs: arbitrary generator

* code refactor and docs
  • Loading branch information
purusang authored Dec 11, 2024
1 parent 8c6ed31 commit 5e0edea
Show file tree
Hide file tree
Showing 12 changed files with 74 additions and 46 deletions.
16 changes: 8 additions & 8 deletions bin/strata-client/src/extractor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ mod tests {
max_txs_per_block: usize,
needle: (Vec<u8>, ProtocolOperation),
) -> usize {
let arb = ArbitraryGenerator::new();
let mut arb = ArbitraryGenerator::new();
assert!(
num_blocks.gt(&0) && max_txs_per_block.gt(&0),
"num_blocks and max_tx_per_block must be at least 1"
Expand Down Expand Up @@ -400,7 +400,7 @@ mod tests {

/// Create a known transaction that should be present in some block.
fn get_needle() -> (Vec<u8>, ProtocolOperation, DepositInfo) {
let arb = ArbitraryGenerator::new();
let mut arb = ArbitraryGenerator::new();
let network = Network::Regtest;

let el_address: [u8; 20] = arb.generate();
Expand Down Expand Up @@ -472,20 +472,20 @@ mod tests {
/// So, you can assume that this flag represents whether the pair is valid (true) or invalid
/// (false).
fn generate_mock_tx() -> (Vec<u8>, ProtocolOperation, bool) {
let arb = ArbitraryGenerator::new();
let mut arb = ArbitraryGenerator::new();

let should_be_valid: bool = arb.generate();

if should_be_valid.not() {
let (invalid_tx, invalid_protocol_op) = generate_invalid_tx(&arb);
let (invalid_tx, invalid_protocol_op) = generate_invalid_tx(&mut arb);
return (invalid_tx, invalid_protocol_op, should_be_valid);
}

let (valid_tx, valid_protocol_op) = generate_valid_tx(&arb);
let (valid_tx, valid_protocol_op) = generate_valid_tx(&mut arb);
(valid_tx, valid_protocol_op, should_be_valid)
}

fn generate_invalid_tx(arb: &ArbitraryGenerator) -> (Vec<u8>, ProtocolOperation) {
fn generate_invalid_tx(arb: &mut ArbitraryGenerator) -> (Vec<u8>, ProtocolOperation) {
let random_protocol_op: ProtocolOperation = arb.generate();

// true => tx invalid
Expand All @@ -512,7 +512,7 @@ mod tests {
(tx_with_invalid_script_pubkey, random_protocol_op)
}

fn generate_valid_tx(arb: &ArbitraryGenerator) -> (Vec<u8>, ProtocolOperation) {
fn generate_valid_tx(arb: &mut ArbitraryGenerator) -> (Vec<u8>, ProtocolOperation) {
let (tx, spend_info, script_to_spend) = generate_mock_unsigned_tx();

let random_hash = *spend_info
Expand Down Expand Up @@ -568,7 +568,7 @@ mod tests {
let empty_deposits = empty_chain_state.deposits_table_mut();
let mut deposits_table = DepositsTable::new_empty();

let arb = ArbitraryGenerator::new();
let mut arb = ArbitraryGenerator::new();

let mut operators: Vec<OperatorIdx> = arb.generate();
loop {
Expand Down
2 changes: 1 addition & 1 deletion crates/bridge-relay/src/relayer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ mod tests {
let processed_msgs = RecentMessageTracker::new();

// Create valid BridgeMsgId instances for testing
let ag = ArbitraryGenerator::new();
let mut ag = ArbitraryGenerator::new();
let cur_message_id: BridgeMsgId = ag.generate();
let old_message_id: BridgeMsgId = ag.generate();
assert_ne!(cur_message_id, old_message_id);
Expand Down
12 changes: 6 additions & 6 deletions crates/consensus-logic/src/duty/block_assembly.rs
Original file line number Diff line number Diff line change
Expand Up @@ -444,7 +444,7 @@ mod tests {

#[test]
fn test_find_pivot_noop() {
let ag = ArbitraryGenerator::new_with_size(1 << 12);
let mut ag = ArbitraryGenerator::new_with_size(1 << 12);

let blkids: [L1BlockId; 10] = ag.generate();
eprintln!("{blkids:#?}");
Expand All @@ -467,7 +467,7 @@ mod tests {

#[test]
fn test_find_pivot_noop_offset() {
let ag = ArbitraryGenerator::new_with_size(1 << 12);
let mut ag = ArbitraryGenerator::new_with_size(1 << 12);

let blkids: [L1BlockId; 10] = ag.generate();
eprintln!("{blkids:#?}");
Expand All @@ -490,7 +490,7 @@ mod tests {

#[test]
fn test_find_pivot_simple_extend() {
let ag = ArbitraryGenerator::new_with_size(1 << 12);
let mut ag = ArbitraryGenerator::new_with_size(1 << 12);

let blkids1: [L1BlockId; 10] = ag.generate();
let mut blkids2 = Vec::from(blkids1);
Expand Down Expand Up @@ -522,7 +522,7 @@ mod tests {

#[test]
fn test_find_pivot_typical_reorg() {
let ag = ArbitraryGenerator::new_with_size(1 << 16);
let mut ag = ArbitraryGenerator::new_with_size(1 << 16);

let mut blkids1: Vec<L1BlockId> = Vec::new();
for _ in 0..10 {
Expand Down Expand Up @@ -559,7 +559,7 @@ mod tests {

#[test]
fn test_find_pivot_cur_shorter_reorg() {
let ag = ArbitraryGenerator::new_with_size(1 << 16);
let mut ag = ArbitraryGenerator::new_with_size(1 << 16);

let mut blkids1: Vec<L1BlockId> = Vec::new();
for _ in 0..10 {
Expand Down Expand Up @@ -600,7 +600,7 @@ mod tests {

#[test]
fn test_find_pivot_disjoint() {
let ag = ArbitraryGenerator::new_with_size(1 << 16);
let mut ag = ArbitraryGenerator::new_with_size(1 << 16);

let mut blkids1: Vec<L1BlockId> = Vec::new();
for _ in 0..10 {
Expand Down
2 changes: 1 addition & 1 deletion crates/evmexec/src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ mod tests {

let el_payload = random_el_payload();

let arb = strata_test_utils::ArbitraryGenerator::new();
let mut arb = strata_test_utils::ArbitraryGenerator::new();
let l2block: L2Block = arb.generate();
let accessory = L2BlockAccessory::new(borsh::to_vec(&el_payload).unwrap());
let l2block_bundle = L2BlockBundle::new(l2block, accessory);
Expand Down
2 changes: 1 addition & 1 deletion crates/primitives/src/l1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1364,7 +1364,7 @@ mod tests {

#[test]
fn test_bitcoin_txid_serialize_deserialize() {
let generator = ArbitraryGenerator::new();
let mut generator = ArbitraryGenerator::new();
let txid: BitcoinTxid = generator.generate();

let serialized_txid =
Expand Down
2 changes: 1 addition & 1 deletion crates/primitives/src/relay/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,7 @@ mod tests {

#[test]
fn test_message_signer_serde() {
let generator = ArbitraryGenerator::new();
let mut generator = ArbitraryGenerator::new();
let txid: BitcoinTxid = generator.generate();
let scope = Scope::V0PubNonce(txid);
let payload: Musig2PubNonce = generator.generate();
Expand Down
4 changes: 2 additions & 2 deletions crates/rocksdb-store/src/bridge/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ mod tests {
fn test_bridge_duty_status_db() {
let db = setup_duty_db();

let arb = ArbitraryGenerator::new();
let mut arb = ArbitraryGenerator::new();

let duty_status: BridgeDutyStatus = arb.generate();
let txid: BitcoinTxid = arb.generate();
Expand Down Expand Up @@ -315,7 +315,7 @@ mod tests {
fn test_bridge_duty_index_db() {
let db = setup_bridge_duty_index_db();

let arb = ArbitraryGenerator::new();
let mut arb = ArbitraryGenerator::new();

let checkpoint: u64 = arb.generate();

Expand Down
2 changes: 1 addition & 1 deletion crates/rocksdb-store/src/bridge_relay/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ mod tests {
}

fn make_bridge_msg() -> (u128, BridgeMessage) {
let arb = ArbitraryGenerator::new();
let mut arb = ArbitraryGenerator::new();

let msg: BridgeMessage = arb.generate();

Expand Down
2 changes: 1 addition & 1 deletion crates/rocksdb-store/src/l1/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ mod tests {
db: &L1Db,
num_txs: usize,
) -> (L1BlockManifest, Vec<L1Tx>, CompactMmr) {
let arb = ArbitraryGenerator::new();
let mut arb = ArbitraryGenerator::new();

// TODO maybe tweak this to make it a bit more realistic?
let mf: L1BlockManifest = arb.generate();
Expand Down
2 changes: 1 addition & 1 deletion crates/rocksdb-store/src/l2/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ mod tests {
use crate::test_utils::get_rocksdb_tmp_instance;

fn get_mock_data() -> L2BlockBundle {
let arb = ArbitraryGenerator::new();
let mut arb = ArbitraryGenerator::new();
let l2_block: L2BlockBundle = arb.generate();

l2_block
Expand Down
2 changes: 1 addition & 1 deletion crates/test-utils/src/l2.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ use strata_state::{
use crate::{bitcoin::get_btc_chain, ArbitraryGenerator};

pub fn gen_block(parent: Option<&SignedL2BlockHeader>) -> L2BlockBundle {
let arb = ArbitraryGenerator::new();
let mut arb = ArbitraryGenerator::new();
let header: L2BlockHeader = arb.generate();
let body: L2BlockBody = arb.generate();
let accessory: L2BlockAccessory = arb.generate();
Expand Down
72 changes: 50 additions & 22 deletions crates/test-utils/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,74 @@
use std::sync::atomic::{AtomicUsize, Ordering};

use arbitrary::{Arbitrary, Unstructured};
use rand::{rngs::OsRng, RngCore};
use rand::{rngs::OsRng, CryptoRng, RngCore};

pub mod bitcoin;
pub mod bridge;
pub mod evm_ee;
pub mod l2;

const ARB_GEN_LEN: usize = 1 << 24; // 16 MiB
/// The default buffer size for the `ArbitraryGenerator`.
const ARB_GEN_LEN: usize = 1024;

pub struct ArbitraryGenerator {
buf: Vec<u8>,
off: AtomicUsize,
buf: Vec<u8>, // Persistent buffer
}

impl Default for ArbitraryGenerator {
fn default() -> Self {
Self::new()
}
}

impl ArbitraryGenerator {
/// Creates a new `ArbitraryGenerator` with a default buffer size.
///
/// # Returns
///
/// A new instance of `ArbitraryGenerator`.
pub fn new() -> Self {
Self::new_with_size(ARB_GEN_LEN)
}

pub fn new_with_size(n: usize) -> Self {
let mut buf = vec![0; n];
OsRng.fill_bytes(&mut buf); // 128 wasn't enough
let off = AtomicUsize::new(0);
ArbitraryGenerator { buf, off }
/// Creates a new `ArbitraryGenerator` with a specified buffer size.
///
/// # Arguments
///
/// * `s` - The size of the buffer to be used.
///
/// # Returns
///
/// A new instance of `ArbitraryGenerator` with the specified buffer size.
pub fn new_with_size(s: usize) -> Self {
Self { buf: vec![0u8; s] }
}

/// Generates an arbitrary instance of type `T` using the default RNG, [`OsRng`].
///
/// # Returns
///
/// An arbitrary instance of type `T`.
pub fn generate<'a, T>(&'a mut self) -> T
where
T: Arbitrary<'a> + Clone,
{
self.generate_with_rng::<T, OsRng>(&mut OsRng)
}

pub fn generate<'a, T: Arbitrary<'a> + Clone>(&'a self) -> T {
// Doing hacky atomics to make this actually be reusable, this is pretty bad.
let off = self.off.load(Ordering::Relaxed);
let mut u = Unstructured::new(&self.buf[off..]);
let prev_off = u.len();
let inst = T::arbitrary(&mut u).expect("failed to generate arbitrary instance");
let additional_off = prev_off - u.len();
self.off.store(off + additional_off, Ordering::Relaxed);
inst
/// Generates an arbitrary instance of type `T`.
///
/// # Arguments
///
/// * `rng` - An RNG to be used for generating the arbitrary instance. Provided RNG must
/// implement the [`RngCore`] and [`CryptoRng`] traits.
///
/// # Returns
///
/// An arbitrary instance of type `T`.
pub fn generate_with_rng<'a, T, R>(&'a mut self, rng: &mut R) -> T
where
T: Arbitrary<'a> + Clone,
R: RngCore + CryptoRng,
{
rng.fill_bytes(&mut self.buf);
let mut u = Unstructured::new(&self.buf);
T::arbitrary(&mut u).expect("Failed to generate arbitrary instance")
}
}

0 comments on commit 5e0edea

Please sign in to comment.