use std::{
cell::{Cell, RefCell},
collections::{HashMap, HashSet, VecDeque},
rc::Rc,
};
use crate::{
context::Context,
snowman::block::{Block, SnowmanBlock},
};
use avalanche_types::{
choices::status::Status,
errors::{Error, Result},
ids::{bag::Bag, Id},
};
pub struct Topological<B: Block> {
#[allow(dead_code)]
ctx: Context,
parameters: crate::Parameters,
poll_number: Cell<u64>,
height: Cell<u64>,
preferred_ids: Rc<RefCell<HashSet<Id>>>,
last_preferred: Cell<Id>,
head: Cell<Id>,
tail: Cell<Id>,
blocks: SnowmanBlocks<B>,
currently_on_preferred_branch: Cell<bool>,
leaves: Rc<RefCell<HashSet<Id>>>,
kahn_nodes: Rc<RefCell<HashMap<Id, KahnNode>>>,
}
type SnowmanBlocks<B> = Rc<RefCell<HashMap<Id, Rc<RefCell<SnowmanBlock<B>>>>>>;
struct KahnNode {
in_degree: Cell<i64>,
votes: Bag,
}
impl KahnNode {
fn new() -> Self {
Self {
in_degree: Cell::new(0),
votes: Bag::new(),
}
}
}
impl Clone for KahnNode {
fn clone(&self) -> Self {
Self {
in_degree: Cell::new(self.in_degree.get()),
votes: self.votes.deep_copy(),
}
}
}
struct Votes {
parent_id: Id,
votes: Bag,
}
impl Votes {
fn new(parent_id: Id, votes: Bag) -> Self {
Self { parent_id, votes }
}
}
impl<B> Topological<B>
where
B: Block,
{
pub fn new(
ctx: Context,
parameters: crate::Parameters,
genesis_blk_id: Id,
genesis_blk_height: u64,
) -> Result<(Self, Rc<RefCell<SnowmanBlock<B>>>)> {
parameters.verify()?;
let genesis_blk = SnowmanBlock::new(parameters.clone());
let genesis_blk_rc = Rc::new(RefCell::new(genesis_blk));
let mut blocks: HashMap<Id, Rc<RefCell<SnowmanBlock<B>>>> = HashMap::new();
blocks.insert(genesis_blk_id, Rc::clone(&genesis_blk_rc));
log::debug!(
"creating a new Topological with genesis block {}",
genesis_blk_id
);
Ok((
Self {
ctx,
parameters,
poll_number: Cell::new(0),
height: Cell::new(genesis_blk_height),
preferred_ids: Rc::new(RefCell::new(HashSet::new())),
last_preferred: Cell::new(Id::empty()),
head: Cell::new(genesis_blk_id),
tail: Cell::new(genesis_blk_id),
blocks: Rc::new(RefCell::new(blocks)),
currently_on_preferred_branch: Cell::new(false),
leaves: Rc::new(RefCell::new(HashSet::new())),
kahn_nodes: Rc::new(RefCell::new(HashMap::new())),
},
Rc::clone(&genesis_blk_rc),
))
}
pub fn parameters(&self) -> crate::Parameters {
self.parameters.clone()
}
pub fn preference(&self) -> Id {
self.tail.get()
}
pub fn height(&self) -> u64 {
self.height.get()
}
pub fn finalized(&self) -> bool {
self.blocks.borrow().len() == 1
}
pub fn num_processing(&self) -> i64 {
(self.blocks.borrow().len() - 1) as i64
}
pub fn block_processing(&self, blk_id: Id) -> bool {
if blk_id == self.head.get() {
return false;
}
self.blocks.borrow().contains_key(&blk_id)
}
pub fn block_preferred(&self, blk: Rc<RefCell<SnowmanBlock<B>>>) -> bool {
if blk.borrow().accepted() {
return true;
}
self.preferred_ids
.borrow()
.contains(&blk.borrow().id().unwrap())
}
pub fn block_decided(&self, blk: Rc<RefCell<SnowmanBlock<B>>>) -> bool {
if blk.borrow().status().is_none() {
return true;
}
if blk.borrow().status().unwrap().decided() {
return true;
}
blk.borrow().status().unwrap() == Status::Processing
&& blk.borrow().height().unwrap() <= self.height()
}
pub fn add_block(&self, blk: B) -> Result<Rc<RefCell<SnowmanBlock<B>>>> {
let blk_id = blk.id();
if self.block_processing(blk_id) {
return Err(Error::Other {
message: "duplicate block add".to_string(),
retryable: false,
});
}
let mut borrowed_mut_blks = self.blocks.borrow_mut();
let parent_blk_id = blk.parent();
let blk_rc = Rc::new(RefCell::new(blk));
let snowman_blk_rc = Rc::new(RefCell::new(SnowmanBlock::new_with_block(
self.parameters(),
Rc::clone(&blk_rc),
)));
if self.block_decided(Rc::clone(&snowman_blk_rc)) {
return Err(Error::Other {
message: "duplicate block add".to_string(),
retryable: false,
});
}
let res = borrowed_mut_blks.get(&parent_blk_id);
if res.is_none() {
log::debug!(
"rejecting {} due to missing parent block {}",
blk_id,
parent_blk_id
);
blk_rc.borrow_mut().reject()?;
return Ok(Rc::clone(&snowman_blk_rc));
}
log::debug!("adding {} as a child to {}", blk_id, parent_blk_id);
let parent_blk = res.unwrap();
parent_blk.borrow_mut().add_child(Rc::clone(&blk_rc));
borrowed_mut_blks.insert(blk_id, Rc::clone(&snowman_blk_rc));
if self.tail.get() == parent_blk_id {
self.tail.set(blk_id);
self.preferred_ids.borrow_mut().insert(blk_id);
}
Ok(Rc::clone(&snowman_blk_rc))
}
pub fn record_poll(&self, votes_bag: Bag) -> Result<()> {
self.poll_number.set(self.poll_number.get() + 1);
let mut votes_stack = {
if votes_bag.len() >= self.parameters.alpha as u32 {
self.calculate_in_degree(votes_bag);
self.push_votes()
} else {
Vec::new()
}
};
self.apply_votes_stack(&mut votes_stack)?;
let last_preferred = self.last_preferred.get();
if self.preferred_ids.borrow().contains(&last_preferred) {
return Ok(());
}
self.preferred_ids.borrow_mut().clear();
self.tail.set(last_preferred);
let borrowed_blks = self.blocks.borrow();
let mut cursor = borrowed_blks.get(&self.tail.get()).expect("unexpected missing block for tail (possibly hash collsion of previously accepted block)").borrow();
while !cursor.accepted() {
let blk_id = cursor.blk.as_ref().unwrap().borrow().id();
self.preferred_ids.borrow_mut().insert(blk_id);
let parent_id = cursor.blk.as_ref().unwrap().borrow().parent();
cursor = borrowed_blks.get(&parent_id).unwrap().borrow();
}
let mut cursor = borrowed_blks.get(&self.tail.get()).expect("unexpected missing block for tail (possibly hash collsion of previously accepted block)").borrow();
while cursor.sb.is_some() {
let cur_preference = cursor.sb.as_ref().unwrap().preference();
self.tail.set(cur_preference);
self.preferred_ids.borrow_mut().insert(cur_preference);
cursor = borrowed_blks.get(&cur_preference).unwrap().borrow();
}
Ok(())
}
fn calculate_in_degree(&self, votes_bag: Bag) {
let borrowed_blks = self.blocks.borrow();
self.leaves.borrow_mut().clear();
self.kahn_nodes.borrow_mut().clear();
for vote in votes_bag.list() {
let res = borrowed_blks.get(&vote);
if res.is_none() {
continue;
}
let voted_block = res.unwrap();
if voted_block.borrow().accepted() {
continue;
}
let parent_blk_id = voted_block.borrow().blk.as_ref().unwrap().borrow().parent();
let num_votes = votes_bag.count(&vote);
let (parent_kahn, previously_seen) = {
let kahn = self.kahn_nodes.borrow_mut().remove(&parent_blk_id);
if kahn.is_some() {
let kahn = kahn.unwrap();
kahn.votes.add_count(&vote, num_votes);
(kahn, true)
} else {
let kahn = KahnNode::new();
kahn.votes.add_count(&vote, num_votes);
(kahn, false)
}
};
self.kahn_nodes
.borrow_mut()
.insert(parent_blk_id, parent_kahn);
if previously_seen {
continue;
}
self.leaves.borrow_mut().insert(parent_blk_id);
let mut cursor = parent_blk_id;
while let Some(blk) = borrowed_blks.get(&cursor) {
if blk.borrow().accepted() {
break;
}
cursor = blk.borrow().blk.as_ref().unwrap().borrow().parent();
let in_degree = {
let mut borrowed_mut_kahn_nodes = self.kahn_nodes.borrow_mut();
if let Some(kahn) = borrowed_mut_kahn_nodes.get(&cursor) {
kahn.in_degree.set(kahn.in_degree.get() + 1);
kahn.in_degree.get()
} else {
let kahn_node = KahnNode::new();
kahn_node.in_degree.set(kahn_node.in_degree.get() + 1);
let in_degree = kahn_node.in_degree.get();
borrowed_mut_kahn_nodes.insert(cursor, kahn_node);
in_degree
}
};
if in_degree != 1 {
break;
}
self.leaves.borrow_mut().remove(&cursor);
}
}
}
fn push_votes(&self) -> Vec<Votes> {
let borrowed_blks = self.blocks.borrow();
let mut leaves: VecDeque<Id> = self.leaves.borrow_mut().drain().collect();
let mut votes_stack: Vec<Votes> = Vec::with_capacity(self.kahn_nodes.borrow().len());
while !leaves.is_empty() {
let leaf_blk_id = leaves.pop_front().unwrap();
let mut borrowed_mut_kahn_nodes = self.kahn_nodes.borrow_mut();
let kahn = borrowed_mut_kahn_nodes.get(&leaf_blk_id).unwrap();
let kahn_votes = kahn.votes.deep_copy();
let kahn_votes_len = kahn_votes.len();
if kahn_votes.len() >= self.parameters.alpha as u32 {
votes_stack.push(Votes::new(leaf_blk_id, kahn_votes));
}
let blk = borrowed_blks.get(&leaf_blk_id).unwrap();
if blk.borrow().accepted() {
continue;
}
let parent_blk_id = blk.borrow().blk.as_ref().unwrap().borrow().parent();
let parent_kahn = borrowed_mut_kahn_nodes.get_mut(&parent_blk_id).unwrap();
parent_kahn.in_degree.set(parent_kahn.in_degree.get() - 1);
parent_kahn.votes.add_count(&leaf_blk_id, kahn_votes_len);
if parent_kahn.in_degree.get() == 0 {
leaves.push_back(parent_blk_id);
}
}
if !leaves.is_empty() {
let mut borrowed_mut_leaves = self.leaves.borrow_mut();
for id in leaves {
borrowed_mut_leaves.insert(id);
}
}
votes_stack
}
fn apply_votes_stack(&self, votes_stack: &mut Vec<Votes>) -> Result<()> {
if votes_stack.is_empty() {
let borrowed_blks = self.blocks.borrow();
let head_blk = borrowed_blks
.get(&self.head.get())
.expect("head block not found in 'blocks'");
head_blk.borrow_mut().should_falter.set(true);
let num_processing = self.num_processing();
if num_processing > 0 {
log::debug!(
"no progress was made after a vote with {} pending blocks",
num_processing
);
}
self.last_preferred.set(self.tail.get());
return Ok(());
}
self.last_preferred.set(self.head.get());
self.currently_on_preferred_branch.set(true);
let mut poll_successful = false;
while let Some(votes) = votes_stack.pop() {
if !self.block_exists(&votes.parent_id) {
break;
}
let (prev_should_falter, votes_finalized, poll_success) = self.record_votes(&votes);
poll_successful = poll_successful || poll_success;
let mut pending_removals: HashSet<Id> = HashSet::new();
if votes_finalized && self.head.get() == votes.parent_id {
let mut rejected = self.accept_preferred_child(&votes.parent_id)?;
while let Some(reject_id) = rejected.pop() {
pending_removals.insert(reject_id);
let pending_rejects = self.reject_block_transitively(&reject_id)?;
rejected.extend_from_slice(&pending_rejects);
}
pending_removals.insert(votes.parent_id);
}
let next_id = {
if votes_stack.is_empty() {
Id::empty()
} else {
votes_stack.last().unwrap().parent_id
}
};
self.update_preferences(prev_should_falter, &votes.parent_id, next_id)?;
self.remove_blocks(&pending_removals);
}
if poll_successful {
log::debug!("poll was successful");
} else {
log::debug!("poll was not successful");
}
Ok(())
}
fn block_exists(&self, blk_id: &Id) -> bool {
self.blocks.borrow().get(blk_id).is_some()
}
fn remove_blocks(&self, blk_ids: &HashSet<Id>) {
if blk_ids.is_empty() {
return;
}
let mut borrowed_mut_blks = self.blocks.borrow_mut();
for blk_id in blk_ids.iter() {
borrowed_mut_blks.remove(blk_id);
}
}
fn record_votes(&self, votes: &Votes) -> (bool, bool, bool) {
let borrowed_blks = self.blocks.borrow();
let parent_blk = borrowed_blks
.get(&votes.parent_id)
.expect("votes.parent_id not found in 'blocks', must be rejected");
let prev_should_falter = parent_blk.borrow().should_falter.get();
if prev_should_falter {
log::debug!("resetting confidence below parent Id {}", votes.parent_id);
let borrowed_parent_blk = parent_blk.borrow();
let sb = borrowed_parent_blk.sb.as_ref().unwrap();
sb.record_unsuccessful_poll();
borrowed_parent_blk.should_falter.set(false);
}
let mut borrowed_mut_parent_blk = parent_blk.borrow_mut();
let sb = borrowed_mut_parent_blk.sb.as_mut().unwrap();
let poll_successful = sb.record_poll(&votes.votes);
(prev_should_falter, sb.finalized(), poll_successful)
}
fn accept_preferred_child(&self, parent_blk_id: &Id) -> Result<Vec<Id>> {
let borrowed_blks = self.blocks.borrow();
let parent_blk = borrowed_blks
.get(parent_blk_id)
.expect("block Id not found in 'blocks', must be rejected");
let borrowed_parent_blk = parent_blk.borrow();
let preference = borrowed_parent_blk
.sb
.as_ref()
.expect("unexpected None snowball for SnowmanBlock")
.preference();
let children = borrowed_parent_blk
.children
.as_ref()
.expect("unexpected None children for SnowmanBlock");
let mut borrowed_mut_children = children.borrow_mut();
let child_blk = borrowed_mut_children.get_mut(&preference).expect(
format!(
"missing child in SnowmanBlock for preference {}",
preference
)
.as_str(),
);
log::info!("accepting the block {}", preference);
child_blk.borrow_mut().accept()?;
self.head.set(preference);
self.height.set(child_blk.borrow().height());
let mut preferred_ids = self.preferred_ids.take();
preferred_ids.remove(&preference);
if borrowed_mut_children.is_empty() {
return Ok(Vec::new());
}
let mut rejected: Vec<Id> = Vec::with_capacity(borrowed_mut_children.len() - 1);
for (child_id, child) in borrowed_mut_children.iter_mut() {
if *child_id == preference {
continue;
}
log::debug!(
"rejecting {} due to conflict with the accepted block {}",
child_id,
preference
);
child.borrow_mut().reject()?;
rejected.push(*child_id);
}
Ok(rejected)
}
fn reject_block_transitively(&self, blk_id: &Id) -> Result<Vec<Id>> {
let borrowed_blks = self.blocks.borrow();
let blk = borrowed_blks.get(blk_id).unwrap();
let mut pending_rejects: Vec<Id> = Vec::new();
let mut borrowed_mut_blk = blk.borrow_mut();
if let Some(children) = borrowed_mut_blk.children.as_mut() {
for (child_id, child) in children.borrow_mut().iter_mut() {
log::debug!(
"rejecting the child {} for transitivity from its parent {}",
child_id,
blk_id
);
child.borrow_mut().reject()?;
pending_rejects.push(*child_id);
}
}
Ok(pending_rejects)
}
fn update_preferences(
&self,
prev_should_falter: bool,
parent_id: &Id,
next_id: Id,
) -> Result<()> {
let borrowed_blks = self.blocks.borrow();
let parent_blk = borrowed_blks.get(parent_id).unwrap();
let borrowed_parent_blk = parent_blk.borrow();
let sb = borrowed_parent_blk.sb.as_ref().unwrap();
let parent_preference = sb.preference();
if self.currently_on_preferred_branch.get() {
self.last_preferred.set(parent_preference);
}
self.currently_on_preferred_branch
.set(self.currently_on_preferred_branch.get() && next_id == parent_preference);
let children = borrowed_parent_blk.children.as_ref().unwrap();
for (child_id, _) in children.borrow().iter() {
if !prev_should_falter && *child_id == next_id {
continue;
}
let res = borrowed_blks.get(child_id);
if res.is_none() {
continue;
}
let child_blk = res.unwrap();
log::debug!(
"deferring confidence reset of {}, voting for {}",
child_id,
next_id
);
child_blk.borrow().should_falter.set(true);
}
Ok(())
}
}
#[test]
fn test_topological_initialize() {
use crate::snowman::block::test_block::TestBlock;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Info)
.is_test(true)
.try_init();
let genesis_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 3,
beta_rogue: 5,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, genesis_blk) =
Topological::<TestBlock>::new(Context::default(), params, genesis_id, genesis_height)
.expect("failed to create Topological");
assert!(genesis_blk.borrow().accepted());
assert_eq!(tp.parameters().k, 1);
assert_eq!(tp.parameters().alpha, 1);
assert_eq!(tp.parameters().beta_virtuous, 3);
assert_eq!(tp.parameters().beta_rogue, 5);
assert_eq!(tp.parameters().concurrent_repolls, 1);
assert_eq!(tp.parameters().optimal_processing, 1);
assert_eq!(tp.parameters().max_outstanding_items, 1);
assert_eq!(tp.parameters().max_item_processing_time, 1);
assert_eq!(tp.preference(), genesis_id);
assert_eq!(tp.height(), genesis_height);
assert_eq!(tp.finalized(), true);
}
#[test]
fn test_topological_num_processing() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 1,
beta_rogue: 1,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) =
Topological::<TestBlock>::new(Context::default(), params, genesis_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.num_processing(), 0);
let blk_id = Id::empty().prefix(&[1]).unwrap();
let blk = TestBlock::new(
TestDecidable::new(blk_id, Status::Processing),
genesis_id.clone(),
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk.id(), blk_id);
assert_eq!(blk.parent(), genesis_id);
assert!(tp.add_block(blk.clone()).is_ok());
assert_eq!(tp.num_processing(), 1);
let votes_bag = Bag::new();
votes_bag.add_count(&blk.id(), 1);
assert!(tp.record_poll(votes_bag).is_ok());
assert_eq!(tp.num_processing(), 0);
}
#[test]
fn test_topological_add_to_tail() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 3,
beta_rogue: 5,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) = Topological::<TestBlock>::new(
Context::default(),
params.clone(),
genesis_blk_id,
genesis_height,
)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk1 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk1.id(), Id::empty().prefix(&[1]).unwrap());
let blk1_rc = Rc::new(RefCell::new(SnowmanBlock::new_with_block(
params,
Rc::new(RefCell::new(blk1.clone())),
)));
let blk2 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[2]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk2.id(), Id::empty().prefix(&[2]).unwrap());
assert!(tp.add_block(blk1.clone()).is_ok());
assert_eq!(tp.preference(), blk1.id());
assert!(tp.block_preferred(Rc::clone(&blk1_rc)));
assert!(tp.add_block(blk2.clone()).is_ok());
assert_eq!(tp.preference(), blk1.id());
}
#[test]
fn test_topological_add_to_non_tail() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 3,
beta_rogue: 5,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) = Topological::<TestBlock>::new(
Context::default(),
params.clone(),
genesis_blk_id,
genesis_height,
)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk1 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk1.id(), Id::empty().prefix(&[1]).unwrap());
let blk1_rc = Rc::new(RefCell::new(SnowmanBlock::new_with_block(
params,
Rc::new(RefCell::new(blk1.clone())),
)));
let blk2 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[2]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk2.id(), Id::empty().prefix(&[2]).unwrap());
assert!(tp.add_block(blk1.clone()).is_ok());
assert_eq!(tp.preference(), blk1.id());
assert!(tp.block_preferred(Rc::clone(&blk1_rc)));
assert!(tp.add_block(blk2.clone()).is_ok());
assert_eq!(tp.preference(), blk1.id());
}
#[test]
fn test_topological_add_unknown() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 3,
beta_rogue: 5,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let parent_blk = TestBlock::new(
TestDecidable::new(
Id::empty().prefix(&[1]).unwrap(),
Status::Unknown(String::from("...")),
),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(parent_blk.id(), Id::empty().prefix(&[1]).unwrap());
assert_eq!(parent_blk.parent(), genesis_blk_id);
let blk = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[2]).unwrap(), Status::Processing),
parent_blk.id(),
Ok(()),
Bytes::new(),
parent_blk.height() + 1,
0,
);
assert_eq!(blk.id(), Id::empty().prefix(&[2]).unwrap());
assert_eq!(blk.parent(), parent_blk.id());
let added_blk_rc = tp.add_block(blk.clone()).unwrap();
assert_eq!(added_blk_rc.borrow().status().unwrap(), Status::Rejected);
assert_eq!(tp.preference(), genesis_blk_id);
}
#[test]
fn test_topological_status_or_processing_previously_accepted() {
use crate::snowman::block::test_block::TestBlock;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 3,
beta_rogue: 5,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, genesis_blk) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let borrowed_genesis_blk = genesis_blk.borrow();
assert!(borrowed_genesis_blk.accepted());
assert!(!tp.block_processing(genesis_blk_id));
assert!(tp.block_decided(Rc::clone(&genesis_blk)));
assert!(tp.block_preferred(Rc::clone(&genesis_blk)));
}
#[test]
fn test_topological_status_or_processing_previously_rejected() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 3,
beta_rogue: 5,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) = Topological::<TestBlock>::new(
Context::default(),
params.clone(),
genesis_blk_id,
genesis_height,
)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Rejected),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk.id(), Id::empty().prefix(&[1]).unwrap());
assert_eq!(blk.status(), Status::Rejected);
let blk_rc = Rc::new(RefCell::new(SnowmanBlock::new_with_block(
params,
Rc::new(RefCell::new(blk.clone())),
)));
assert!(!tp.block_processing(blk.id()));
assert!(tp.block_decided(Rc::clone(&blk_rc)));
assert!(!tp.block_preferred(Rc::clone(&blk_rc)));
}
#[test]
fn test_topological_status_or_processing_unissued() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 3,
beta_rogue: 5,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) = Topological::<TestBlock>::new(
Context::default(),
params.clone(),
genesis_blk_id,
genesis_height,
)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk.id(), Id::empty().prefix(&[1]).unwrap());
assert_eq!(blk.status(), Status::Processing);
let snowman_blk_rc = Rc::new(RefCell::new(SnowmanBlock::new_with_block(
params,
Rc::new(RefCell::new(blk.clone())),
)));
assert!(!tp.block_processing(blk.id()));
assert!(!tp.block_decided(Rc::clone(&snowman_blk_rc)));
assert!(!tp.block_preferred(Rc::clone(&snowman_blk_rc)));
}
#[test]
fn test_topological_status_or_processing_issued() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 3,
beta_rogue: 5,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) = Topological::<TestBlock>::new(
Context::default(),
params.clone(),
genesis_blk_id,
genesis_height,
)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk.id(), Id::empty().prefix(&[1]).unwrap());
assert_eq!(blk.status(), Status::Processing);
let added_blk_rc = tp.add_block(blk.clone()).unwrap();
assert_eq!(added_blk_rc.borrow().status().unwrap(), Status::Processing);
assert!(tp.block_processing(blk.id()));
assert!(!tp.block_decided(Rc::clone(&added_blk_rc)));
assert!(tp.block_preferred(Rc::clone(&added_blk_rc)));
}
#[test]
fn test_topological_record_poll_accept_single_block() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 2,
beta_rogue: 3,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) = Topological::<TestBlock>::new(
Context::default(),
params.clone(),
genesis_blk_id,
genesis_height,
)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk.id(), Id::empty().prefix(&[1]).unwrap());
assert_eq!(blk.status(), Status::Processing);
let added_blk_rc = tp.add_block(blk.clone()).unwrap();
assert_eq!(added_blk_rc.borrow().status().unwrap(), Status::Processing);
let votes_bag = Bag::new();
votes_bag.add_count(&blk.id(), 1);
assert!(tp.record_poll(votes_bag).is_ok());
assert!(!tp.finalized());
assert_eq!(tp.preference(), blk.id());
assert_eq!(added_blk_rc.borrow().status().unwrap(), Status::Processing);
let votes_bag = Bag::new();
votes_bag.add_count(&blk.id(), 1);
assert!(tp.record_poll(votes_bag).is_ok());
assert!(tp.finalized());
assert_eq!(tp.preference(), blk.id());
assert_eq!(added_blk_rc.borrow().status().unwrap(), Status::Accepted);
}
#[test]
fn test_topological_record_poll_accept_and_reject() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 1,
beta_rogue: 2,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk1 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk1.id(), Id::empty().prefix(&[1]).unwrap());
assert_eq!(blk1.status(), Status::Processing);
let blk2 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[2]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk2.id(), Id::empty().prefix(&[2]).unwrap());
assert_eq!(blk2.status(), Status::Processing);
let added_blk1_rc = tp.add_block(blk1.clone()).unwrap();
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
let added_blk2_rc = tp.add_block(blk2.clone()).unwrap();
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
let votes_bag = Bag::new();
votes_bag.add_count(&blk1.id(), 1);
assert!(tp.record_poll(votes_bag).is_ok());
assert!(!tp.finalized());
assert_eq!(tp.preference(), blk1.id());
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
let votes_bag = Bag::new();
votes_bag.add_count(&blk1.id(), 1);
assert!(tp.record_poll(votes_bag).is_ok());
assert!(tp.finalized());
assert_eq!(tp.preference(), blk1.id());
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Accepted);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Rejected);
}
#[test]
fn test_topological_record_poll_split_vote_no_change() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 2,
alpha: 2,
beta_virtuous: 1,
beta_rogue: 2,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk1 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk1.id(), Id::empty().prefix(&[1]).unwrap());
assert_eq!(blk1.status(), Status::Processing);
let blk2 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[2]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk2.id(), Id::empty().prefix(&[2]).unwrap());
assert_eq!(blk2.status(), Status::Processing);
let added_blk1_rc = tp.add_block(blk1.clone()).unwrap();
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
let added_blk2_rc = tp.add_block(blk2.clone()).unwrap();
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
let votes_bag = Bag::new();
votes_bag.add_count(&blk1.id(), 1);
votes_bag.add_count(&blk2.id(), 1);
assert!(tp.record_poll(votes_bag.clone()).is_ok());
assert!(!tp.finalized());
assert_eq!(tp.preference(), blk1.id());
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
assert!(tp.record_poll(votes_bag).is_ok());
assert!(!tp.finalized());
assert_eq!(tp.preference(), blk1.id());
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
}
#[test]
fn test_topological_record_poll_when_finalized() {
use crate::snowman::block::test_block::TestBlock;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 1,
beta_rogue: 2,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, added_genesis_blk_rc) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
assert!(added_genesis_blk_rc.borrow().id().is_none());
let votes_bag = Bag::new();
votes_bag.add_count(&genesis_blk_id, 1);
assert!(tp.record_poll(votes_bag).is_ok());
assert!(tp.finalized());
assert_eq!(tp.preference(), genesis_blk_id);
}
#[test]
fn test_topological_record_poll_reject_transitively() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 1,
beta_rogue: 1,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) = Topological::<TestBlock>::new(
Context::default(),
params.clone(),
genesis_blk_id,
genesis_height,
)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk0 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk0.id(), Id::empty().prefix(&[1]).unwrap());
assert_eq!(blk0.status(), Status::Processing);
let blk1 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[2]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk1.id(), Id::empty().prefix(&[2]).unwrap());
assert_eq!(blk1.status(), Status::Processing);
let blk2 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[3]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk2.id(), Id::empty().prefix(&[3]).unwrap());
assert_eq!(blk2.status(), Status::Processing);
let added_blk0_rc = tp.add_block(blk0.clone()).unwrap();
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Processing);
let added_blk1_rc = tp.add_block(blk1.clone()).unwrap();
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
let added_blk2_rc = tp.add_block(blk2.clone()).unwrap();
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
let votes_bag = Bag::new();
votes_bag.add_count(&blk0.id(), 1);
assert!(tp.record_poll(votes_bag).is_ok());
assert!(tp.finalized());
assert_eq!(tp.preference(), blk0.id());
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Accepted);
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Rejected);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Rejected);
}
#[test]
fn test_topological_record_poll_transitively_reset_confidence() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 2,
beta_rogue: 2,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) = Topological::<TestBlock>::new(
Context::default(),
params.clone(),
genesis_blk_id,
genesis_height,
)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk0 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk0.id(), Id::empty().prefix(&[1]).unwrap());
assert_eq!(blk0.status(), Status::Processing);
let blk1 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[2]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk1.id(), Id::empty().prefix(&[2]).unwrap());
assert_eq!(blk1.status(), Status::Processing);
let blk2 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[3]).unwrap(), Status::Processing),
blk1.id(),
Ok(()),
Bytes::new(),
blk1.height() + 1,
0,
);
assert_eq!(blk2.id(), Id::empty().prefix(&[3]).unwrap());
assert_eq!(blk2.status(), Status::Processing);
let blk3 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[4]).unwrap(), Status::Processing),
blk1.id(),
Ok(()),
Bytes::new(),
blk1.height() + 1,
0,
);
assert_eq!(blk3.id(), Id::empty().prefix(&[4]).unwrap());
assert_eq!(blk3.status(), Status::Processing);
let added_blk0_rc = tp.add_block(blk0.clone()).unwrap();
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Processing);
let added_blk1_rc = tp.add_block(blk1.clone()).unwrap();
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
let added_blk2_rc = tp.add_block(blk2.clone()).unwrap();
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
let added_blk3_rc = tp.add_block(blk3.clone()).unwrap();
assert_eq!(added_blk3_rc.borrow().status().unwrap(), Status::Processing);
let votes_bag_for_2 = Bag::new();
votes_bag_for_2.add_count(&blk2.id(), 1);
assert!(tp.record_poll(votes_bag_for_2).is_ok());
assert!(!tp.finalized());
assert_eq!(tp.preference(), blk2.id());
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk3_rc.borrow().status().unwrap(), Status::Processing);
let votes_bag_empty = Bag::new();
assert!(tp.record_poll(votes_bag_empty).is_ok());
assert!(!tp.finalized());
assert_eq!(tp.preference(), blk2.id());
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk3_rc.borrow().status().unwrap(), Status::Processing);
let votes_bag_for_2 = Bag::new();
votes_bag_for_2.add_count(&blk2.id(), 1);
assert!(tp.record_poll(votes_bag_for_2).is_ok());
assert!(!tp.finalized());
assert_eq!(tp.preference(), blk2.id());
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk3_rc.borrow().status().unwrap(), Status::Processing);
let votes_bag_for_3 = Bag::new();
votes_bag_for_3.add_count(&blk3.id(), 1);
assert!(tp.record_poll(votes_bag_for_3).is_ok());
assert!(!tp.finalized());
assert_eq!(tp.preference(), blk2.id());
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Rejected);
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Accepted);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk3_rc.borrow().status().unwrap(), Status::Processing);
let votes_bag_for_3 = Bag::new();
votes_bag_for_3.add_count(&blk3.id(), 1);
assert!(tp.record_poll(votes_bag_for_3).is_ok());
assert!(tp.finalized());
assert_eq!(tp.preference(), blk3.id());
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Rejected);
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Accepted);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Rejected);
assert_eq!(added_blk3_rc.borrow().status().unwrap(), Status::Accepted);
}
#[test]
fn test_topological_record_poll_invalid_vote() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 2,
beta_rogue: 2,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk.id(), Id::empty().prefix(&[1]).unwrap());
assert_eq!(blk.status(), Status::Processing);
let added_blk_rc = tp.add_block(blk.clone()).unwrap();
assert_eq!(added_blk_rc.borrow().status().unwrap(), Status::Processing);
let valid_votes_bag = Bag::new();
valid_votes_bag.add_count(&blk.id(), 1);
assert!(tp.record_poll(valid_votes_bag).is_ok());
let invalid_votes_bag = Bag::new();
let unknown_blk_id = Id::empty().prefix(&[2]).unwrap();
invalid_votes_bag.add_count(&unknown_blk_id, 1);
assert!(tp.record_poll(invalid_votes_bag).is_ok());
let valid_votes_bag = Bag::new();
valid_votes_bag.add_count(&blk.id(), 1);
assert!(tp.record_poll(valid_votes_bag).is_ok());
assert!(!tp.finalized());
assert_eq!(tp.preference(), blk.id());
}
#[test]
fn test_topological_record_poll_transitive_voting() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 3,
alpha: 3,
beta_virtuous: 1,
beta_rogue: 1,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk0 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk0.id(), Id::empty().prefix(&[1]).unwrap());
assert_eq!(blk0.status(), Status::Processing);
let blk1 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[2]).unwrap(), Status::Processing),
blk0.id(),
Ok(()),
Bytes::new(),
blk0.height() + 1,
0,
);
assert_eq!(blk1.id(), Id::empty().prefix(&[2]).unwrap());
assert_eq!(blk1.status(), Status::Processing);
let blk2 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[3]).unwrap(), Status::Processing),
blk1.id(),
Ok(()),
Bytes::new(),
blk1.height() + 1,
0,
);
assert_eq!(blk2.id(), Id::empty().prefix(&[3]).unwrap());
assert_eq!(blk2.status(), Status::Processing);
let blk3 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[4]).unwrap(), Status::Processing),
blk0.id(),
Ok(()),
Bytes::new(),
blk0.height() + 1,
0,
);
assert_eq!(blk3.id(), Id::empty().prefix(&[4]).unwrap());
assert_eq!(blk3.status(), Status::Processing);
let blk4 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[5]).unwrap(), Status::Processing),
blk3.id(),
Ok(()),
Bytes::new(),
blk3.height() + 1,
0,
);
assert_eq!(blk4.id(), Id::empty().prefix(&[5]).unwrap());
assert_eq!(blk4.status(), Status::Processing);
let added_blk0_rc = tp.add_block(blk0.clone()).unwrap();
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Processing);
let added_blk1_rc = tp.add_block(blk1.clone()).unwrap();
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
let added_blk2_rc = tp.add_block(blk2.clone()).unwrap();
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
let added_blk3_rc = tp.add_block(blk3.clone()).unwrap();
assert_eq!(added_blk3_rc.borrow().status().unwrap(), Status::Processing);
let added_blk4_rc = tp.add_block(blk4.clone()).unwrap();
assert_eq!(added_blk4_rc.borrow().status().unwrap(), Status::Processing);
let votes_0_2_4 = Bag::new();
votes_0_2_4.add_count(&blk0.id(), 1);
votes_0_2_4.add_count(&blk2.id(), 1);
votes_0_2_4.add_count(&blk4.id(), 1);
assert!(tp.record_poll(votes_0_2_4).is_ok());
assert!(!tp.finalized());
assert_eq!(tp.preference(), blk2.id());
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Accepted);
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk3_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk4_rc.borrow().status().unwrap(), Status::Processing);
let dep_2_2_2 = Bag::new();
dep_2_2_2.add_count(&blk2.id(), 3);
assert!(tp.record_poll(dep_2_2_2).is_ok());
assert!(tp.finalized());
assert_eq!(tp.preference(), blk2.id());
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Accepted);
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Accepted);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Accepted);
assert_eq!(added_blk3_rc.borrow().status().unwrap(), Status::Rejected);
assert_eq!(added_blk4_rc.borrow().status().unwrap(), Status::Rejected);
}
#[test]
fn test_topological_record_poll_diverged_voting() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Trace)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 1,
beta_rogue: 2,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk0 = TestBlock::new(
TestDecidable::new(
Id::from_slice(&[0x0f]), Status::Processing,
),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk0.id(), Id::from_slice(&[0x0f])); assert_eq!(blk0.status(), Status::Processing);
let blk1 = TestBlock::new(
TestDecidable::new(
Id::from_slice(&[0x08]), Status::Processing,
),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk1.id(), Id::from_slice(&[0x08])); assert_eq!(blk1.status(), Status::Processing);
let blk2 = TestBlock::new(
TestDecidable::new(
Id::from_slice(&[0x01]), Status::Processing,
),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk2.id(), Id::from_slice(&[0x01])); assert_eq!(blk2.status(), Status::Processing);
let blk3 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
blk2.id(),
Ok(()),
Bytes::new(),
blk2.height() + 1,
0,
);
assert_eq!(blk3.id(), Id::empty().prefix(&[1]).unwrap());
assert_eq!(blk3.status(), Status::Processing);
let added_blk0_rc = tp.add_block(blk0.clone()).unwrap();
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Processing);
let added_blk1_rc = tp.add_block(blk1.clone()).unwrap();
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
let votes_0 = Bag::new();
votes_0.add_count(&blk0.id(), 1);
assert!(tp.record_poll(votes_0).is_ok());
let added_blk2_rc = tp.add_block(blk2.clone()).unwrap();
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
let added_blk3_rc = tp.add_block(blk3.clone()).unwrap();
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk3_rc.borrow().status().unwrap(), Status::Processing);
let votes_3 = Bag::new();
votes_3.add_count(&blk3.id(), 1);
assert!(tp.record_poll(votes_3).is_ok());
assert!(tp.finalized());
assert_eq!(tp.preference(), blk0.id());
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Accepted);
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Rejected);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Rejected);
assert_eq!(added_blk3_rc.borrow().status().unwrap(), Status::Rejected);
}
#[test]
fn test_topological_record_poll_diverged_voting_with_no_conflicting_bit() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Trace)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 1,
beta_rogue: 2,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk0 = TestBlock::new(
TestDecidable::new(
Id::from_slice(&[0x06]), Status::Processing,
),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk0.id(), Id::from_slice(&[0x06])); assert_eq!(blk0.status(), Status::Processing);
let blk1 = TestBlock::new(
TestDecidable::new(
Id::from_slice(&[0x08]), Status::Processing,
),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk1.id(), Id::from_slice(&[0x08])); assert_eq!(blk1.status(), Status::Processing);
let blk2 = TestBlock::new(
TestDecidable::new(
Id::from_slice(&[0x01]), Status::Processing,
),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
assert_eq!(blk2.id(), Id::from_slice(&[0x01])); assert_eq!(blk2.status(), Status::Processing);
let blk3 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
blk2.id(),
Ok(()),
Bytes::new(),
blk2.height() + 1,
0,
);
assert_eq!(blk3.id(), Id::empty().prefix(&[1]).unwrap());
assert_eq!(blk3.status(), Status::Processing);
let added_blk0_rc = tp.add_block(blk0.clone()).unwrap();
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Processing);
let added_blk1_rc = tp.add_block(blk1.clone()).unwrap();
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
let votes_0 = Bag::new();
votes_0.add_count(&blk0.id(), 1);
assert!(tp.record_poll(votes_0).is_ok());
let added_blk2_rc = tp.add_block(blk2.clone()).unwrap();
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
let added_blk3_rc = tp.add_block(blk3.clone()).unwrap();
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk3_rc.borrow().status().unwrap(), Status::Processing);
let votes_3 = Bag::new();
votes_3.add_count(&blk3.id(), 1);
assert!(tp.record_poll(votes_3).is_ok());
assert!(!tp.finalized());
assert_eq!(tp.preference(), blk0.id());
assert_eq!(added_blk0_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk1_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk2_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(added_blk3_rc.borrow().status().unwrap(), Status::Processing);
}
#[test]
fn test_topological_record_poll_change_preferred_chain() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 10,
beta_rogue: 10,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let a1 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
let b1 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[2]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
let a2 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[3]).unwrap(), Status::Processing),
a1.id(),
Ok(()),
Bytes::new(),
a1.height() + 1,
0,
);
let b2 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[4]).unwrap(), Status::Processing),
b1.id(),
Ok(()),
Bytes::new(),
b1.height() + 1,
0,
);
let a1_rc = tp.add_block(a1.clone()).unwrap();
assert_eq!(a1_rc.borrow().status().unwrap(), Status::Processing);
let a2_rc = tp.add_block(a2.clone()).unwrap();
assert_eq!(a2_rc.borrow().status().unwrap(), Status::Processing);
let b1_rc = tp.add_block(b1.clone()).unwrap();
assert_eq!(b1_rc.borrow().status().unwrap(), Status::Processing);
let b2_rc = tp.add_block(b2.clone()).unwrap();
assert_eq!(b2_rc.borrow().status().unwrap(), Status::Processing);
assert_eq!(tp.preference(), a2.id());
assert!(tp.block_preferred(Rc::clone(&a1_rc)));
assert!(tp.block_preferred(Rc::clone(&a2_rc)));
assert!(!tp.block_preferred(Rc::clone(&b1_rc)));
assert!(!tp.block_preferred(Rc::clone(&b2_rc)));
let votes_b2 = Bag::new();
votes_b2.add_count(&b2.id(), 1);
assert!(tp.record_poll(votes_b2).is_ok());
assert_eq!(tp.preference(), b2.id());
assert!(!tp.block_preferred(Rc::clone(&a1_rc)));
assert!(!tp.block_preferred(Rc::clone(&a2_rc)));
assert!(tp.block_preferred(Rc::clone(&b1_rc)));
assert!(tp.block_preferred(Rc::clone(&b2_rc)));
let votes_a1 = Bag::new();
votes_a1.add_count(&a1.id(), 1);
assert!(tp.record_poll(votes_a1).is_ok());
let votes_a1 = Bag::new();
votes_a1.add_count(&a1.id(), 1);
assert!(tp.record_poll(votes_a1).is_ok());
assert_eq!(tp.preference(), a2.id());
assert!(tp.block_preferred(Rc::clone(&a1_rc)));
assert!(tp.block_preferred(Rc::clone(&a2_rc)));
assert!(!tp.block_preferred(Rc::clone(&b1_rc)));
assert!(!tp.block_preferred(Rc::clone(&b2_rc)));
}
#[test]
fn test_topological_error_on_initial_reject() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use avalanche_types::errors::Error;
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 1,
beta_rogue: 1,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let rejected_blk = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Rejected),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
let mut rejected_decidable =
TestDecidable::new(Id::empty().prefix(&[2]).unwrap(), Status::Processing);
rejected_decidable.set_reject_result(Err(Error::Other {
message: "test error".to_string(),
retryable: false,
}));
let blk = TestBlock::new(
rejected_decidable,
rejected_blk.id(),
Ok(()),
Bytes::new(),
rejected_blk.height() + 1,
0,
);
let res = tp.add_block(blk.clone());
assert!(res.is_err());
}
#[test]
fn test_topological_error_on_initial_accept() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use avalanche_types::errors::Error;
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 1,
beta_rogue: 1,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let mut failing_decidable =
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing);
failing_decidable.set_accept_result(Err(Error::Other {
message: "test error".to_string(),
retryable: false,
}));
let blk = TestBlock::new(
failing_decidable,
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
let added_blk = tp.add_block(blk.clone()).unwrap();
assert_eq!(added_blk.borrow().status().unwrap(), Status::Processing);
let votes = Bag::new();
votes.add_count(&blk.id(), 1);
assert!(tp.record_poll(votes).is_err());
}
#[test]
fn test_topological_error_on_reject_sibling() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use avalanche_types::errors::Error;
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 1,
beta_rogue: 1,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk0 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
let mut failing_decidable =
TestDecidable::new(Id::empty().prefix(&[2]).unwrap(), Status::Processing);
failing_decidable.set_reject_result(Err(Error::Other {
message: "test error".to_string(),
retryable: false,
}));
let blk1 = TestBlock::new(
failing_decidable,
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
let added_blk0 = tp.add_block(blk0.clone()).unwrap();
assert_eq!(added_blk0.borrow().status().unwrap(), Status::Processing);
let added_blk1 = tp.add_block(blk1.clone()).unwrap();
assert_eq!(added_blk1.borrow().status().unwrap(), Status::Processing);
let votes0 = Bag::new();
votes0.add_count(&blk0.id(), 1);
assert!(tp.record_poll(votes0).is_err());
}
#[test]
fn test_topological_error_on_transitive_reject() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use avalanche_types::errors::Error;
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 1,
beta_rogue: 1,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk0 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[1]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
let blk1 = TestBlock::new(
TestDecidable::new(Id::empty().prefix(&[2]).unwrap(), Status::Processing),
genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
let mut failing_decidable =
TestDecidable::new(Id::empty().prefix(&[3]).unwrap(), Status::Processing);
failing_decidable.set_reject_result(Err(Error::Other {
message: "test error".to_string(),
retryable: false,
}));
let blk2 = TestBlock::new(
failing_decidable,
blk1.id(),
Ok(()),
Bytes::new(),
blk1.height() + 1,
0,
);
let added_blk0 = tp.add_block(blk0.clone()).unwrap();
assert_eq!(added_blk0.borrow().status().unwrap(), Status::Processing);
let added_blk1 = tp.add_block(blk1.clone()).unwrap();
assert_eq!(added_blk1.borrow().status().unwrap(), Status::Processing);
let added_blk2 = tp.add_block(blk2.clone()).unwrap();
assert_eq!(added_blk2.borrow().status().unwrap(), Status::Processing);
let votes0 = Bag::new();
votes0.add_count(&blk0.id(), 1);
assert!(tp.record_poll(votes0).is_err());
}
#[test]
fn test_topological_error_on_decided_block() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::test_decidable::TestDecidable;
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 1,
beta_rogue: 1,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk = TestBlock::new(
TestDecidable::new(Id::from_slice(&[0x03]), Status::Accepted), genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
let res = tp.add_block(blk.clone());
assert!(res.is_err());
match res {
Ok(_) => panic!("unexpected Ok"),
Err(e) => assert!(e.contains(&"duplicate block add")),
}
}
#[test]
fn test_topological_error_on_add_duplicate_block_id() {
use crate::snowman::block::test_block::TestBlock;
use avalanche_types::choices::{decidable::Decidable, test_decidable::TestDecidable};
use bytes::Bytes;
let _ = env_logger::builder()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let genesis_blk_id = Id::empty().prefix(&[0]).unwrap();
let genesis_height = 0_u64;
let params = crate::Parameters {
k: 1,
alpha: 1,
beta_virtuous: 1,
beta_rogue: 1,
concurrent_repolls: 1,
optimal_processing: 1,
max_outstanding_items: 1,
max_item_processing_time: 1,
mixed_query_num_push_to_validators: 0,
mixed_query_num_push_to_non_validators: 0,
};
let (tp, _) =
Topological::<TestBlock>::new(Context::default(), params, genesis_blk_id, genesis_height)
.expect("failed to create Topological");
assert_eq!(tp.preference(), genesis_blk_id);
let blk0 = TestBlock::new(
TestDecidable::new(Id::from_slice(&[0x03]), Status::Processing), genesis_blk_id,
Ok(()),
Bytes::new(),
genesis_height + 1,
0,
);
let blk1 = TestBlock::new(
TestDecidable::new(Id::from_slice(&[0x03]), Status::Processing), blk0.id(),
Ok(()),
Bytes::new(),
blk0.height() + 1,
0,
);
let added_blk0 = tp.add_block(blk0.clone()).unwrap();
assert_eq!(added_blk0.borrow().status().unwrap(), Status::Processing);
let res = tp.add_block(blk1.clone());
assert!(res.is_err());
match res {
Ok(_) => panic!("unexpected Ok"),
Err(e) => assert!(e.contains(&"duplicate block add")),
}
}
#[test]
fn test_topological_randomized_consistency() {
}
#[test]
fn test_topological_metrics_processing_error() {
}
#[test]
fn test_topological_metrics_accepted_error() {
}
#[test]
fn test_topological_metrics_rejected_error() {
}