Skip to content

Commit 0cdd064

Browse files
committed
[dag] support holes in the order rule test
1 parent b1ea020 commit 0cdd064

File tree

2 files changed

+91
-45
lines changed

2 files changed

+91
-45
lines changed

consensus/src/dag/storage.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use anyhow::Ok;
1010
use aptos_crypto::HashValue;
1111
use std::collections::HashMap;
1212

13-
pub trait DAGStorage {
13+
pub trait DAGStorage: Send + Sync {
1414
fn save_node(&self, node: &Node) -> anyhow::Result<()>;
1515

1616
fn delete_node(&self, digest: HashValue) -> anyhow::Result<()>;

consensus/src/dag/tests/order_rule_tests.rs

Lines changed: 90 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use crate::{
1313
test_utils::placeholder_ledger_info,
1414
};
1515
use aptos_consensus_types::common::Author;
16-
use aptos_infallible::RwLock;
16+
use aptos_infallible::{Mutex, RwLock};
1717
use aptos_types::{
1818
aggregate_signature::AggregateSignature, epoch_state::EpochState,
1919
validator_verifier::random_validator_verifier,
@@ -23,25 +23,50 @@ use proptest::prelude::*;
2323
use std::sync::Arc;
2424

2525
/// Generate a virtual dag that first layer represents round
26-
/// second layer represents nodes
27-
/// third layer is a bitmask that represents strong links (true => linked, false => not linked)
26+
/// second layer represents nodes, Some => node exist, None => not exist
27+
/// third layer is a bitmask that represents compressed strong links (true => linked, false => not linked),
28+
/// the bitmask ignores non-existing nodes
2829
fn generate_virtual_dag(
29-
num_validators: usize,
30+
num_nodes: usize,
31+
num_holes: usize,
3032
round: u64,
31-
) -> impl Strategy<Value = Vec<Vec<Vec<bool>>>> {
32-
let f = (num_validators - 1) / 3;
33-
let bitmask: Vec<bool> = std::iter::repeat(false)
34-
.take(f)
35-
.chain(std::iter::repeat(true).take(num_validators - f))
33+
) -> impl Strategy<Value = Vec<Vec<Option<Vec<bool>>>>> {
34+
let num_strong_links = num_nodes * 2 / 3 + 1;
35+
assert!(num_holes <= num_nodes - num_strong_links);
36+
// This only has length of num_nodes - num_holes which ignores holes
37+
let strong_links: Vec<bool> = std::iter::repeat(false)
38+
.take(num_nodes - num_holes - num_strong_links)
39+
.chain(std::iter::repeat(true).take(num_strong_links))
3640
.collect();
41+
// This has length of num_nodes
42+
let nodes: Vec<bool> = std::iter::repeat(false)
43+
.take(num_holes)
44+
.chain(std::iter::repeat(true).take(num_nodes - num_holes))
45+
.collect();
46+
// For every round, we shuffle the nodes bitmask to generate holes
47+
// For every node, we shuffle the compressed strong links if the node is not a hole
3748
proptest::collection::vec(
38-
proptest::collection::vec(Just(bitmask).prop_shuffle(), num_validators),
49+
Just(nodes).prop_shuffle().prop_flat_map(move |nodes| {
50+
nodes
51+
.into_iter()
52+
.map(|exist| {
53+
if exist {
54+
Just(strong_links.clone())
55+
.prop_shuffle()
56+
.prop_map(Some)
57+
.boxed()
58+
} else {
59+
Just(None).boxed()
60+
}
61+
})
62+
.collect::<Vec<_>>()
63+
}),
3964
round as usize,
4065
)
4166
}
4267

43-
/// Generate `num_per` random permutations of how nodes are processed by the order rule
44-
/// Imagine we have 4 nodes, this generates a permutation of [0, 1, 2, 3]
68+
/// Generate `num_perm` random permutations of how nodes are processed by the order rule
69+
/// Imagine we have 4 nodes, this generates `num_perm` permutations of [0, 1, 2, 3]
4570
fn generate_permutations(
4671
num_perm: usize,
4772
total_number: usize,
@@ -52,25 +77,39 @@ fn generate_permutations(
5277
)
5378
}
5479

55-
fn generate_dag_nodes(dag: &[Vec<Vec<bool>>], validators: &[Author]) -> Vec<Vec<CertifiedNode>> {
80+
/// Generate certified nodes for dag given the virtual dag
81+
fn generate_dag_nodes(
82+
dag: &[Vec<Option<Vec<bool>>>],
83+
validators: &[Author],
84+
) -> Vec<Vec<Option<CertifiedNode>>> {
5685
let mut nodes = vec![];
57-
let mut previous_round: Vec<CertifiedNode> = vec![];
86+
let mut previous_round: Vec<Option<CertifiedNode>> = vec![];
5887
for (round, round_nodes) in dag.iter().enumerate() {
5988
let mut nodes_at_round = vec![];
6089
for (idx, author) in validators.iter().enumerate() {
61-
let bitmask = &round_nodes[idx];
62-
let parents: Vec<_> = previous_round
63-
.iter()
64-
.enumerate()
65-
.filter(|(idx, _)| bitmask[*idx])
66-
.map(|(_, node)| {
67-
NodeCertificate::new(node.metadata().clone(), AggregateSignature::empty())
68-
})
69-
.collect();
70-
if round > 1 {
71-
assert_eq!(parents.len(), NUM_VALIDATORS * 2 / 3 + 1);
90+
if let Some(bitmask) = &round_nodes[idx] {
91+
// the bitmask is compressed (without the holes), we need to flatten the previous round nodes
92+
// to match the index
93+
let parents: Vec<_> = previous_round
94+
.iter()
95+
.flatten()
96+
.enumerate()
97+
.filter(|(idx, _)| *bitmask.get(*idx).unwrap_or(&false))
98+
.map(|(_, node)| {
99+
NodeCertificate::new(node.metadata().clone(), AggregateSignature::empty())
100+
})
101+
.collect();
102+
if round > 1 {
103+
assert_eq!(parents.len(), NUM_VALIDATORS * 2 / 3 + 1);
104+
}
105+
nodes_at_round.push(Some(new_certified_node(
106+
(round + 1) as u64,
107+
*author,
108+
parents,
109+
)));
110+
} else {
111+
nodes_at_round.push(None);
72112
}
73-
nodes_at_round.push(new_certified_node((round + 1) as u64, *author, parents));
74113
}
75114
previous_round = nodes_at_round.clone();
76115
nodes.push(nodes_at_round);
@@ -93,49 +132,56 @@ fn create_order_rule(
93132
)
94133
}
95134

135+
const NUM_HOLES: usize = 1;
96136
const NUM_VALIDATORS: usize = 4;
97137
const NUM_ROUNDS: u64 = 50;
98138
const NUM_PERMUTATION: usize = 100;
99139

100140
proptest! {
101141
#[test]
102142
fn test_order_rule(
103-
dag in generate_virtual_dag(NUM_VALIDATORS, NUM_ROUNDS),
104-
sequences in generate_permutations(NUM_PERMUTATION, NUM_VALIDATORS * NUM_ROUNDS as usize)
143+
mut dag_with_holes in generate_virtual_dag(NUM_VALIDATORS, NUM_HOLES, NUM_ROUNDS),
144+
mut dag in generate_virtual_dag(NUM_VALIDATORS, 0, NUM_ROUNDS),
145+
sequences in generate_permutations(NUM_PERMUTATION, (NUM_VALIDATORS - NUM_HOLES) * NUM_ROUNDS as usize)
105146
) {
106147
let (_, validator_verifier) = random_validator_verifier(NUM_VALIDATORS, None, false);
107148
let validators = validator_verifier.get_ordered_account_addresses();
108149
let author_indexes = validator_verifier.address_to_validator_index().clone();
150+
dag.append(&mut dag_with_holes);
109151
let nodes = generate_dag_nodes(&dag, &validators);
110152
let epoch_state = Arc::new(EpochState {
111153
epoch: 1,
112154
verifier: validator_verifier,
113155
});
114156
let mut dag = Dag::new(epoch_state.clone(), Arc::new(MockStorage::new()));
115157
for round_nodes in &nodes {
116-
for node in round_nodes {
158+
for node in round_nodes.iter().flatten() {
117159
dag.add_node(node.clone()).unwrap();
118160
}
119161
}
120-
let flatten_nodes: Vec<_> = nodes.into_iter().flatten().collect();
121-
let mut all_ordered = vec![];
122-
for seq in sequences {
123-
let dag = Arc::new(RwLock::new(dag.clone()));
124-
let (mut order_rule, mut receiver) = create_order_rule(epoch_state.clone(), dag.clone());
125-
for idx in seq {
126-
order_rule.process_new_node(&flatten_nodes[idx]);
162+
let flatten_nodes: Vec<_> = nodes.into_iter().flatten().flatten().collect();
163+
let all_ordered = Arc::new(Mutex::new(vec![]));
164+
rayon::scope(|s| {
165+
for seq in sequences {
166+
s.spawn(|_| {
167+
let dag = Arc::new(RwLock::new(dag.clone()));
168+
let (mut order_rule, mut receiver) = create_order_rule(epoch_state.clone(), dag);
169+
for idx in seq {
170+
order_rule.process_new_node(&flatten_nodes[idx]);
171+
}
172+
let mut ordered = vec![];
173+
while let Ok(Some(mut ordered_nodes)) = receiver.try_next() {
174+
ordered.append(&mut ordered_nodes);
175+
}
176+
all_ordered.lock().push(ordered);
177+
});
127178
}
128-
let mut ordered = vec![];
129-
while let Ok(Some(mut ordered_nodes)) = receiver.try_next() {
130-
ordered.append(&mut ordered_nodes);
131-
}
132-
all_ordered.push(ordered);
133-
}
179+
});
134180
let display = |node: &Arc<CertifiedNode>| {
135181
(node.metadata().round(), *author_indexes.get(node.metadata().author()).unwrap())
136182
};
137-
let longest: Vec<_> = all_ordered.iter().max_by(|v1, v2| v1.len().cmp(&v2.len())).unwrap().iter().map(display).collect();
138-
for ordered in all_ordered {
183+
let longest: Vec<_> = all_ordered.lock().iter().max_by(|v1, v2| v1.len().cmp(&v2.len())).unwrap().iter().map(display).collect();
184+
for ordered in all_ordered.lock().iter() {
139185
let a: Vec<_> = ordered.iter().map(display).collect();
140186
assert_eq!(a, longest[..a.len()]);
141187
}

0 commit comments

Comments
 (0)