Skip to content

Commit c5e4865

Browse files
authored
Merge pull request #2978 from ljedrz/feat/testchain-generator-txs
[Feat] Allow the testchain-generator to be fed with pregenerated txs
2 parents 9201227 + c41a1be commit c5e4865

File tree

6 files changed

+155
-25
lines changed

6 files changed

+155
-25
lines changed

.circleci/config.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -684,21 +684,21 @@ jobs:
684684

685685
ledger-with-rocksdb-partition1:
686686
executor: rust-docker
687-
resource_class: << pipeline.parameters.large >>
687+
resource_class: << pipeline.parameters.xlarge >>
688688
steps:
689689
- run_test:
690690
workspace_member: snarkvm-ledger
691-
flags: --release --features=rocks --partition count:1/2 -- --test-threads=8
691+
flags: --release --features=rocks --partition count:1/2 -- --test-threads=10
692692
no_output_timeout: 20m
693693
timeout: 30m
694694

695695
ledger-with-rocksdb-partition2:
696696
executor: rust-docker
697-
resource_class: << pipeline.parameters.large >>
697+
resource_class: << pipeline.parameters.xlarge >>
698698
steps:
699699
- run_test:
700700
workspace_member: snarkvm-ledger
701-
flags: --release --features=rocks --partition count:2/2 -- --test-threads=8
701+
flags: --release --features=rocks --partition count:2/2 -- --test-threads=10
702702
no_output_timeout: 20m
703703
timeout: 30m
704704

Cargo.lock

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

ledger/src/test_helpers/chain_builder.rs

Lines changed: 74 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,31 @@ pub struct TestChainBuilder<N: Network> {
6868
}
6969

7070
/// Additional options you can pass to the builder when generating a set of blocks.
71-
#[derive(Clone, Default)]
72-
pub struct GenerateBlocksOptions {
71+
#[derive(Clone)]
72+
pub struct GenerateBlocksOptions<N: Network> {
7373
/// Do not include votes to the previous leader certificate
7474
pub skip_votes: bool,
7575
/// Do not generate certificates for the specific node indices (to simulate a partition).
7676
pub skip_nodes: Vec<usize>,
77+
/// A flag indicating that a number of initial "placeholder blocks" should be baked
78+
/// wthout transactions in order to skip to the latest version of consensus.
79+
pub skip_to_current_version: bool,
80+
/// The number of validators.
81+
pub num_validators: usize,
82+
/// Preloaded transactions to populate the blocks with.
83+
pub transactions: Vec<Transaction<N>>,
84+
}
85+
86+
impl<N: Network> Default for GenerateBlocksOptions<N> {
87+
fn default() -> Self {
88+
Self {
89+
skip_votes: false,
90+
skip_nodes: Default::default(),
91+
skip_to_current_version: false,
92+
num_validators: 0,
93+
transactions: Default::default(),
94+
}
95+
}
7796
}
7897

7998
/// Additional options you can pass to the builder when generating a single block.
@@ -178,7 +197,6 @@ impl<N: Network> TestChainBuilder<N> {
178197
Ok(Self {
179198
private_keys,
180199
ledger,
181-
182200
genesis_block,
183201
last_batch_round: Default::default(),
184202
last_committed_batch_round: Default::default(),
@@ -190,7 +208,9 @@ impl<N: Network> TestChainBuilder<N> {
190208

191209
/// Create multiple blocks, with fully-connected DAGs.
192210
pub fn generate_blocks(&mut self, num_blocks: usize, rng: &mut TestRng) -> Result<Vec<Block<N>>> {
193-
self.generate_blocks_with_opts(num_blocks, GenerateBlocksOptions::default(), rng)
211+
let num_validators = self.private_keys.len();
212+
213+
self.generate_blocks_with_opts(num_blocks, GenerateBlocksOptions { num_validators, ..Default::default() }, rng)
194214
}
195215

196216
/// Create multiple blocks, with additional parameters.
@@ -200,20 +220,53 @@ impl<N: Network> TestChainBuilder<N> {
200220
pub fn generate_blocks_with_opts(
201221
&mut self,
202222
num_blocks: usize,
203-
options: GenerateBlocksOptions,
223+
mut options: GenerateBlocksOptions<N>,
204224
rng: &mut TestRng,
205225
) -> Result<Vec<Block<N>>> {
206226
assert!(num_blocks > 0, "Need to build at least one block");
207227

208-
let options = GenerateBlockOptions {
209-
skip_votes: options.skip_votes,
210-
skip_nodes: options.skip_nodes,
211-
..Default::default()
212-
};
213-
214228
let mut result = vec![];
215-
for _ in 0..num_blocks {
216-
let block = self.generate_block_with_opts(options.clone(), rng)?;
229+
230+
// If configured, skip enough blocks to reach the current consensus version.
231+
if options.skip_to_current_version {
232+
let (version, target_height) = TEST_CONSENSUS_VERSION_HEIGHTS.last().unwrap();
233+
let mut current_height = self.ledger.latest_height();
234+
235+
let diff = target_height.saturating_sub(current_height);
236+
237+
if diff > 0 {
238+
println!("Skipping {diff} blocks to reach {version}");
239+
240+
while current_height < *target_height && result.len() < num_blocks {
241+
let options = GenerateBlockOptions {
242+
skip_votes: options.skip_votes,
243+
skip_nodes: options.skip_nodes.clone(),
244+
..Default::default()
245+
};
246+
247+
let block = self.generate_block_with_opts(options, rng)?;
248+
current_height = block.height();
249+
result.push(block);
250+
}
251+
252+
println!("Advanced to the current consensus version at height {target_height}");
253+
} else {
254+
debug!("Already at the current consensus version. No blocks to skip.");
255+
}
256+
}
257+
258+
while result.len() < num_blocks {
259+
let num_txs = (BatchHeader::<N>::MAX_TRANSMISSIONS_PER_BATCH * options.num_validators)
260+
.min(options.transactions.len());
261+
262+
let options = GenerateBlockOptions {
263+
skip_votes: options.skip_votes,
264+
skip_nodes: options.skip_nodes.clone(),
265+
transactions: options.transactions.drain(..num_txs).collect(),
266+
..Default::default()
267+
};
268+
269+
let block = self.generate_block_with_opts(options, rng)?;
217270
result.push(block);
218271
}
219272

@@ -262,8 +315,6 @@ impl<N: Network> TestChainBuilder<N> {
262315
transmissions.insert(transmission_id, transmission);
263316
}
264317

265-
let transmission_ids: IndexSet<_> = transmissions.keys().copied().collect();
266-
267318
// =======================================
268319
// Create certificates for the new block.
269320
// =======================================
@@ -303,6 +354,13 @@ impl<N: Network> TestChainBuilder<N> {
303354
continue;
304355
}
305356

357+
let transmission_ids: IndexSet<_> = transmissions
358+
.keys()
359+
.skip(key1_idx * BatchHeader::<N>::MAX_TRANSMISSIONS_PER_BATCH)
360+
.take(BatchHeader::<N>::MAX_TRANSMISSIONS_PER_BATCH)
361+
.copied()
362+
.collect();
363+
306364
let batch_header = BatchHeader::new(
307365
private_key_1,
308366
round,
@@ -404,6 +462,7 @@ impl<N: Network> TestChainBuilder<N> {
404462

405463
// Construct the block.
406464
let subdag = Subdag::from(subdag_map).unwrap();
465+
407466
let block = self.ledger.prepare_advance_to_next_quorum_block(subdag, transmissions, rng)?;
408467

409468
// Skip to increase performance.

ledger/src/tests.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3390,7 +3390,7 @@ fn test_subdag_with_long_branch() -> Result<()> {
33903390
#[test]
33913391
fn test_subdag_with_gc_length() -> Result<()> {
33923392
let rng = &mut TestRng::default();
3393-
let mut chain_builder = TestChainBuilder::new(rng)?;
3393+
let mut chain_builder = TestChainBuilder::new(rng).unwrap();
33943394

33953395
let blocks = chain_builder.generate_blocks_with_opts(
33963396
BatchHeader::<CurrentNetwork>::MAX_GC_ROUNDS / 2,

ledger/testchain-generator/Cargo.toml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ workspace = true
2727

2828
[dependencies.snarkvm-console]
2929
workspace = true
30+
features = [ "test_consensus_heights" ]
3031

3132
[dependencies.snarkvm-ledger]
3233
workspace = true
@@ -35,6 +36,9 @@ features = [ "test-helpers", "rocks" ]
3536
[dependencies.anyhow]
3637
workspace = true
3738

39+
[dependencies.tracing]
40+
workspace = true
41+
3842
[dependencies.tracing-subscriber]
3943
version = "0.3"
4044

ledger/testchain-generator/src/main.rs

Lines changed: 71 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,27 @@
1313
// See the License for the specific language governing permissions and
1414
// limitations under the License.
1515

16-
use snarkvm_console::prelude::{CanaryV0, MainnetV0, Network, TestRng, TestnetV0, ToBytes};
17-
use snarkvm_ledger::{Ledger, store::helpers::rocksdb::ConsensusDB, test_helpers::TestChainBuilder};
16+
use snarkvm_console::prelude::{
17+
CanaryV0,
18+
MainnetV0,
19+
Network,
20+
TEST_CONSENSUS_VERSION_HEIGHTS,
21+
TestRng,
22+
TestnetV0,
23+
ToBytes,
24+
};
25+
use snarkvm_ledger::{
26+
Ledger,
27+
Transaction,
28+
store::helpers::rocksdb::ConsensusDB,
29+
test_helpers::{TestChainBuilder, chain_builder::GenerateBlocksOptions},
30+
};
1831

1932
use aleo_std::StorageMode;
2033
use anyhow::{Context, Result, bail};
2134
use clap::{Parser, builder::PossibleValuesParser};
22-
use std::fs;
35+
use std::{fs, path::Path, str::FromStr};
36+
use tracing::debug;
2337

2438
#[derive(Parser)]
2539
struct Args {
@@ -38,6 +52,10 @@ struct Args {
3852
/// Remove existing ledger if it already exists.
3953
#[clap(long, short = 'f')]
4054
force: bool,
55+
/// Load the transactions to be used with the generated blocks. They are expected to be
56+
/// stored in a JSON-encoded format.
57+
#[clap(long)]
58+
txs_path: Option<String>,
4159
/// The name of the network to generate the chain for.
4260
#[clap(long, value_parser=PossibleValuesParser::new(vec![CanaryV0::SHORT_NAME, TestnetV0::SHORT_NAME, MainnetV0::SHORT_NAME]), default_value=TestnetV0::SHORT_NAME)]
4361
network: String,
@@ -95,6 +113,32 @@ fn generate_testchain<N: Network>(args: Args) -> Result<()> {
95113

96114
remove_ledger(N::ID, &storage_mode, args.force)?;
97115

116+
let mut txs = if let Some(path) = args.txs_path {
117+
let path = Path::new(&path);
118+
119+
if path.is_dir() {
120+
println!("Attempting to load transactions from \"{}\"", path.display());
121+
} else {
122+
bail!("Cannot load transactions from \"{}\": not a valid directory", path.display());
123+
}
124+
125+
let mut txs = Vec::new();
126+
for entry in fs::read_dir(path)? {
127+
let entry = entry?;
128+
let path = entry.path();
129+
130+
let buffer = fs::read_to_string(path)?;
131+
let tx = Transaction::<N>::from_str(&buffer)?;
132+
133+
txs.push(tx);
134+
}
135+
136+
println!("Loaded {} tranactionss from \"{}\"", txs.len(), path.display());
137+
txs
138+
} else {
139+
Default::default()
140+
};
141+
98142
let num_validators = args.num_validators;
99143
let num_blocks = args.num_blocks;
100144

@@ -110,9 +154,31 @@ fn generate_testchain<N: Network>(args: Args) -> Result<()> {
110154
let mut pos = 0;
111155
let mut blocks = vec![];
112156

157+
// How many blocks to generate in a single batch.
158+
const BATCH_SIZE: usize = 100;
159+
160+
// How many transactions to insert per block.
161+
let latest_consensus_height = TEST_CONSENSUS_VERSION_HEIGHTS.last().unwrap().1 as usize;
162+
let num_txn_blocks = num_blocks.saturating_sub(latest_consensus_height);
163+
let txns_per_block = txs.len().div_ceil(num_txn_blocks);
164+
113165
while blocks.len() < num_blocks {
114-
let batch_size = (num_blocks - blocks.len()).min(100);
115-
let mut batch = builder.generate_blocks(batch_size, &mut rng).with_context(|| "Failed to generate blocks")?;
166+
let current_height = blocks.len();
167+
// How many blocks to generate in this batch.
168+
let batch_size = (num_blocks - current_height).min(BATCH_SIZE);
169+
// Generate set of transactions to insert in this batch.
170+
let num_empty_blocks = latest_consensus_height.saturating_sub(current_height);
171+
let num_txns = (batch_size.saturating_sub(num_empty_blocks)) * txns_per_block;
172+
let transactions = txs.drain(..num_txns).collect();
173+
174+
debug!("Generating next batch with {batch_size} blocks and {num_txns} transactions");
175+
let mut batch = builder
176+
.generate_blocks_with_opts(
177+
batch_size,
178+
GenerateBlocksOptions { transactions, skip_to_current_version: true, ..Default::default() },
179+
&mut rng,
180+
)
181+
.with_context(|| "Failed to generate blocks")?;
116182

117183
pos += batch_size;
118184
println!("Generated {pos} of {num_blocks} blocks");

0 commit comments

Comments
 (0)