mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-06-22 17:21:07 +00:00
Finalized block event triggers tx maintanance (#12305)
* finalized block event triggers tx maintanance * tx-pool: enactment helper introduced * tx-pool: ChainApi: added tree_route method * enactment logic implemented + tests Signed-off-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> * Some additional tests * minor improvements * trigger CI job * fix compilation errors ChainApi::tree_route return type changed to Result<Option<..>>, as some implementations (tests) are not required to provide this tree route. * formatting * trait removed * implementation slightly simplified (thanks to @koute) * get rid of Arc<> in EnactmentState return value * minor improvement * Apply suggestions from code review Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * Apply suggestions from code review * comment updated + formatting * Apply suggestions from code review Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> Co-authored-by: Davide Galassi <davxy@datawok.net> * formatting * finalization notification bug fix + new test case + log::warn message when finalized block is being retracted by new event * added error message on tree_route failure * Apply suggestions from code review Co-authored-by: Bastian Köcher <git@kchr.de> * use provided tree_route in Finalized event * Option removed from ChainApi::tree_route * doc added, test and logs improved * handle_enactment aligned with original implementation * use async-await * Apply suggestions from code review Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Bastian Köcher <git@kchr.de> * formatting + warn->debug * compilation error fix * enactment_state initializers added * enactment_state: Option removed * manual-seal: compilation & tests fix * manual-seal: tests fixed * tests cleanup * another compilation error fixed * TreeRoute::new added * get rid of pub hack * one more test added * formatting * TreeRoute::new doc added + formatting * Apply suggestions from code review Co-authored-by: Davide Galassi <davxy@datawok.net> * (bool,Option) simplified to Option * log message improved * yet another review suggestions applied * get rid of hash in handle_enactment * Apply suggestions from code review Co-authored-by: Bastian Köcher <git@kchr.de> * Update client/transaction-pool/src/lib.rs Co-authored-by: Bastian Köcher <git@kchr.de> * minor corrections * EnactmentState moved to new file * File header corrected * error formatting aligned with codebase * Apply suggestions from code review Co-authored-by: Bastian Köcher <git@kchr.de> * remove commented code * small nits Signed-off-by: Michal Kucharczyk <1728078+michalkucharczyk@users.noreply.github.com> Co-authored-by: André Silva <123550+andresilva@users.noreply.github.com> Co-authored-by: Davide Galassi <davxy@datawok.net> Co-authored-by: Bastian Köcher <git@kchr.de> Co-authored-by: André Silva <andrerfosilva@gmail.com>
This commit is contained in:
committed by
GitHub
parent
023aa03fea
commit
62bca87f3a
@@ -0,0 +1,579 @@
|
||||
// This file is part of Substrate.
|
||||
|
||||
// Copyright (C) 2022 Parity Technologies (UK) Ltd.
|
||||
// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
//! Substrate transaction pool implementation.
|
||||
|
||||
use sc_transaction_pool_api::ChainEvent;
|
||||
use sp_blockchain::TreeRoute;
|
||||
use sp_runtime::traits::Block as BlockT;
|
||||
|
||||
/// Helper struct for keeping track of the current state of processed new best
|
||||
/// block and finalized events. The main purpose of keeping track of this state
|
||||
/// is to figure out if a transaction pool enactment is needed or not.
|
||||
///
|
||||
/// Given the following chain:
|
||||
///
|
||||
/// B1-C1-D1-E1
|
||||
/// /
|
||||
/// A
|
||||
/// \
|
||||
/// B2-C2-D2-E2
|
||||
///
|
||||
/// Some scenarios and expected behavior for sequence of `NewBestBlock` (`nbb`) and `Finalized`
|
||||
/// (`f`) events:
|
||||
///
|
||||
/// - `nbb(C1)`, `f(C1)` -> false (enactment was already performed in `nbb(C1))`
|
||||
/// - `f(C1)`, `nbb(C1)` -> false (enactment was already performed in `f(C1))`
|
||||
/// - `f(C1)`, `nbb(D2)` -> false (enactment was already performed in `f(C1)`,
|
||||
/// we should not retract finalized block)
|
||||
/// - `f(C1)`, `f(C2)`, `nbb(C1)` -> false
|
||||
/// - `nbb(C1)`, `nbb(C2)` -> true (switching fork is OK)
|
||||
/// - `nbb(B1)`, `nbb(B2)` -> true
|
||||
/// - `nbb(B1)`, `nbb(C1)`, `f(C1)` -> false (enactment was already performed in `nbb(B1)`)
|
||||
/// - `nbb(C1)`, `f(B1)` -> false (enactment was already performed in `nbb(B2)`)
|
||||
pub struct EnactmentState<Block>
|
||||
where
|
||||
Block: BlockT,
|
||||
{
|
||||
recent_best_block: Block::Hash,
|
||||
recent_finalized_block: Block::Hash,
|
||||
}
|
||||
|
||||
impl<Block> EnactmentState<Block>
|
||||
where
|
||||
Block: BlockT,
|
||||
{
|
||||
/// Returns a new `EnactmentState` initialized with the given parameters.
|
||||
pub fn new(recent_best_block: Block::Hash, recent_finalized_block: Block::Hash) -> Self {
|
||||
EnactmentState { recent_best_block, recent_finalized_block }
|
||||
}
|
||||
|
||||
/// Returns the recently finalized block.
|
||||
pub fn recent_finalized_block(&self) -> Block::Hash {
|
||||
self.recent_finalized_block
|
||||
}
|
||||
|
||||
/// Updates the state according to the given `ChainEvent`, returning
|
||||
/// `Some(tree_route)` with a tree route including the blocks that need to
|
||||
/// be enacted/retracted. If no enactment is needed then `None` is returned.
|
||||
pub fn update<F>(
|
||||
&mut self,
|
||||
event: &ChainEvent<Block>,
|
||||
tree_route: &F,
|
||||
) -> Result<Option<TreeRoute<Block>>, String>
|
||||
where
|
||||
F: Fn(Block::Hash, Block::Hash) -> Result<TreeRoute<Block>, String>,
|
||||
{
|
||||
let (new_hash, finalized) = match event {
|
||||
ChainEvent::NewBestBlock { hash, .. } => (*hash, false),
|
||||
ChainEvent::Finalized { hash, .. } => (*hash, true),
|
||||
};
|
||||
|
||||
// block was already finalized
|
||||
if self.recent_finalized_block == new_hash {
|
||||
log::debug!(target: "txpool", "handle_enactment: block already finalized");
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
// compute actual tree route from best_block to notified block, and use
|
||||
// it instead of tree_route provided with event
|
||||
let tree_route = tree_route(self.recent_best_block, new_hash)?;
|
||||
|
||||
log::debug!(
|
||||
target: "txpool",
|
||||
"resolve hash:{:?} finalized:{:?} tree_route:{:?} best_block:{:?} finalized_block:{:?}",
|
||||
new_hash, finalized, tree_route, self.recent_best_block, self.recent_finalized_block
|
||||
);
|
||||
|
||||
// check if recently finalized block is on retracted path. this could be
|
||||
// happening if we first received a finalization event and then a new
|
||||
// best event for some old stale best head.
|
||||
if tree_route.retracted().iter().any(|x| x.hash == self.recent_finalized_block) {
|
||||
log::debug!(
|
||||
target: "txpool",
|
||||
"Recently finalized block {} would be retracted by ChainEvent {}, skipping",
|
||||
self.recent_finalized_block, new_hash
|
||||
);
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
if finalized {
|
||||
self.recent_finalized_block = new_hash;
|
||||
|
||||
// if there are no enacted blocks in best_block -> hash tree_route,
|
||||
// it means that block being finalized was already enacted (this
|
||||
// case also covers best_block == new_hash), recent_best_block
|
||||
// remains valid.
|
||||
if tree_route.enacted().is_empty() {
|
||||
log::trace!(
|
||||
target: "txpool",
|
||||
"handle_enactment: no newly enacted blocks since recent best block"
|
||||
);
|
||||
return Ok(None)
|
||||
}
|
||||
|
||||
// otherwise enacted finalized block becomes best block...
|
||||
}
|
||||
|
||||
self.recent_best_block = new_hash;
|
||||
|
||||
Ok(Some(tree_route))
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod enactment_state_tests {
|
||||
use super::EnactmentState;
|
||||
use sc_transaction_pool_api::ChainEvent;
|
||||
use sp_blockchain::{HashAndNumber, TreeRoute};
|
||||
use std::sync::Arc;
|
||||
use substrate_test_runtime_client::runtime::{Block, Hash};
|
||||
|
||||
// some helpers for convenient blocks' hash naming
|
||||
fn a() -> HashAndNumber<Block> {
|
||||
HashAndNumber { number: 1, hash: Hash::from([0xAA; 32]) }
|
||||
}
|
||||
fn b1() -> HashAndNumber<Block> {
|
||||
HashAndNumber { number: 2, hash: Hash::from([0xB1; 32]) }
|
||||
}
|
||||
fn c1() -> HashAndNumber<Block> {
|
||||
HashAndNumber { number: 3, hash: Hash::from([0xC1; 32]) }
|
||||
}
|
||||
fn d1() -> HashAndNumber<Block> {
|
||||
HashAndNumber { number: 4, hash: Hash::from([0xD1; 32]) }
|
||||
}
|
||||
fn e1() -> HashAndNumber<Block> {
|
||||
HashAndNumber { number: 5, hash: Hash::from([0xE1; 32]) }
|
||||
}
|
||||
fn b2() -> HashAndNumber<Block> {
|
||||
HashAndNumber { number: 2, hash: Hash::from([0xB2; 32]) }
|
||||
}
|
||||
fn c2() -> HashAndNumber<Block> {
|
||||
HashAndNumber { number: 3, hash: Hash::from([0xC2; 32]) }
|
||||
}
|
||||
fn d2() -> HashAndNumber<Block> {
|
||||
HashAndNumber { number: 4, hash: Hash::from([0xD2; 32]) }
|
||||
}
|
||||
fn e2() -> HashAndNumber<Block> {
|
||||
HashAndNumber { number: 5, hash: Hash::from([0xE2; 32]) }
|
||||
}
|
||||
|
||||
/// mock tree_route computing function for simple two-forks chain
|
||||
fn tree_route(from: Hash, to: Hash) -> Result<TreeRoute<Block>, String> {
|
||||
let chain = vec![e1(), d1(), c1(), b1(), a(), b2(), c2(), d2(), e2()];
|
||||
let pivot = 4_usize;
|
||||
|
||||
let from = chain
|
||||
.iter()
|
||||
.position(|bn| bn.hash == from)
|
||||
.ok_or("existing block should be given")?;
|
||||
let to = chain
|
||||
.iter()
|
||||
.position(|bn| bn.hash == to)
|
||||
.ok_or("existing block should be given")?;
|
||||
|
||||
// B1-C1-D1-E1
|
||||
// /
|
||||
// A
|
||||
// \
|
||||
// B2-C2-D2-E2
|
||||
//
|
||||
// [E1 D1 C1 B1 A B2 C2 D2 E2]
|
||||
|
||||
let vec: Vec<HashAndNumber<Block>> = if from < to {
|
||||
chain.into_iter().skip(from).take(to - from + 1).collect()
|
||||
} else {
|
||||
chain.into_iter().skip(to).take(from - to + 1).rev().collect()
|
||||
};
|
||||
|
||||
let pivot = if from <= pivot && to <= pivot {
|
||||
if from < to {
|
||||
to - from
|
||||
} else {
|
||||
0
|
||||
}
|
||||
} else if from >= pivot && to >= pivot {
|
||||
if from < to {
|
||||
0
|
||||
} else {
|
||||
from - to
|
||||
}
|
||||
} else {
|
||||
if from < to {
|
||||
pivot - from
|
||||
} else {
|
||||
from - pivot
|
||||
}
|
||||
};
|
||||
|
||||
Ok(TreeRoute::new(vec, pivot))
|
||||
}
|
||||
|
||||
mod mock_tree_route_tests {
|
||||
use super::*;
|
||||
|
||||
/// asserts that tree routes are equal
|
||||
fn assert_treeroute_eq(expected: TreeRoute<Block>, result: TreeRoute<Block>) {
|
||||
assert_eq!(result.common_block().hash, expected.common_block().hash);
|
||||
assert_eq!(result.enacted().len(), expected.enacted().len());
|
||||
assert_eq!(result.retracted().len(), expected.retracted().len());
|
||||
assert!(result
|
||||
.enacted()
|
||||
.iter()
|
||||
.zip(expected.enacted().iter())
|
||||
.all(|(a, b)| a.hash == b.hash));
|
||||
assert!(result
|
||||
.retracted()
|
||||
.iter()
|
||||
.zip(expected.retracted().iter())
|
||||
.all(|(a, b)| a.hash == b.hash));
|
||||
}
|
||||
|
||||
// some tests for mock tree_route function
|
||||
#[test]
|
||||
fn tree_route_mock_test_01() {
|
||||
let result = tree_route(b1().hash, a().hash).expect("tree route exists");
|
||||
let expected = TreeRoute::new(vec![b1(), a()], 1);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_route_mock_test_02() {
|
||||
let result = tree_route(a().hash, b1().hash).expect("tree route exists");
|
||||
let expected = TreeRoute::new(vec![a(), b1()], 0);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_route_mock_test_03() {
|
||||
let result = tree_route(a().hash, c2().hash).expect("tree route exists");
|
||||
let expected = TreeRoute::new(vec![a(), b2(), c2()], 0);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_route_mock_test_04() {
|
||||
let result = tree_route(e2().hash, a().hash).expect("tree route exists");
|
||||
let expected = TreeRoute::new(vec![e2(), d2(), c2(), b2(), a()], 4);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_route_mock_test_05() {
|
||||
let result = tree_route(d1().hash, b1().hash).expect("tree route exists");
|
||||
let expected = TreeRoute::new(vec![d1(), c1(), b1()], 2);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_route_mock_test_06() {
|
||||
let result = tree_route(d2().hash, b2().hash).expect("tree route exists");
|
||||
let expected = TreeRoute::new(vec![d2(), c2(), b2()], 2);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_route_mock_test_07() {
|
||||
let result = tree_route(b1().hash, d1().hash).expect("tree route exists");
|
||||
let expected = TreeRoute::new(vec![b1(), c1(), d1()], 0);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_route_mock_test_08() {
|
||||
let result = tree_route(b2().hash, d2().hash).expect("tree route exists");
|
||||
let expected = TreeRoute::new(vec![b2(), c2(), d2()], 0);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_route_mock_test_09() {
|
||||
let result = tree_route(e2().hash, e1().hash).expect("tree route exists");
|
||||
let expected =
|
||||
TreeRoute::new(vec![e2(), d2(), c2(), b2(), a(), b1(), c1(), d1(), e1()], 4);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_route_mock_test_10() {
|
||||
let result = tree_route(e1().hash, e2().hash).expect("tree route exists");
|
||||
let expected =
|
||||
TreeRoute::new(vec![e1(), d1(), c1(), b1(), a(), b2(), c2(), d2(), e2()], 4);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
#[test]
|
||||
fn tree_route_mock_test_11() {
|
||||
let result = tree_route(b1().hash, c2().hash).expect("tree route exists");
|
||||
let expected = TreeRoute::new(vec![b1(), a(), b2(), c2()], 1);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_route_mock_test_12() {
|
||||
let result = tree_route(d2().hash, b1().hash).expect("tree route exists");
|
||||
let expected = TreeRoute::new(vec![d2(), c2(), b2(), a(), b1()], 3);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_route_mock_test_13() {
|
||||
let result = tree_route(c2().hash, e1().hash).expect("tree route exists");
|
||||
let expected = TreeRoute::new(vec![c2(), b2(), a(), b1(), c1(), d1(), e1()], 2);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_route_mock_test_14() {
|
||||
let result = tree_route(b1().hash, b1().hash).expect("tree route exists");
|
||||
let expected = TreeRoute::new(vec![b1()], 0);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_route_mock_test_15() {
|
||||
let result = tree_route(b2().hash, b2().hash).expect("tree route exists");
|
||||
let expected = TreeRoute::new(vec![b2()], 0);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn tree_route_mock_test_16() {
|
||||
let result = tree_route(a().hash, a().hash).expect("tree route exists");
|
||||
let expected = TreeRoute::new(vec![a()], 0);
|
||||
assert_treeroute_eq(result, expected);
|
||||
}
|
||||
}
|
||||
|
||||
fn trigger_new_best_block(
|
||||
state: &mut EnactmentState<Block>,
|
||||
from: HashAndNumber<Block>,
|
||||
acted_on: HashAndNumber<Block>,
|
||||
) -> bool {
|
||||
let (from, acted_on) = (from.hash, acted_on.hash);
|
||||
|
||||
let event_tree_route = tree_route(from, acted_on).expect("Tree route exists");
|
||||
|
||||
state
|
||||
.update(
|
||||
&ChainEvent::NewBestBlock {
|
||||
hash: acted_on,
|
||||
tree_route: Some(Arc::new(event_tree_route)),
|
||||
},
|
||||
&tree_route,
|
||||
)
|
||||
.unwrap()
|
||||
.is_some()
|
||||
}
|
||||
|
||||
fn trigger_finalized(
|
||||
state: &mut EnactmentState<Block>,
|
||||
from: HashAndNumber<Block>,
|
||||
acted_on: HashAndNumber<Block>,
|
||||
) -> bool {
|
||||
let (from, acted_on) = (from.hash, acted_on.hash);
|
||||
|
||||
let v = tree_route(from, acted_on)
|
||||
.expect("Tree route exists")
|
||||
.enacted()
|
||||
.iter()
|
||||
.map(|h| h.hash)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
state
|
||||
.update(&ChainEvent::Finalized { hash: acted_on, tree_route: v.into() }, &tree_route)
|
||||
.unwrap()
|
||||
.is_some()
|
||||
}
|
||||
|
||||
fn assert_es_eq(
|
||||
es: &EnactmentState<Block>,
|
||||
expected_best_block: HashAndNumber<Block>,
|
||||
expected_finalized_block: HashAndNumber<Block>,
|
||||
) {
|
||||
assert_eq!(es.recent_best_block, expected_best_block.hash);
|
||||
assert_eq!(es.recent_finalized_block, expected_finalized_block.hash);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enactment_helper() {
|
||||
sp_tracing::try_init_simple();
|
||||
let mut es = EnactmentState::new(a().hash, a().hash);
|
||||
|
||||
// B1-C1-D1-E1
|
||||
// /
|
||||
// A
|
||||
// \
|
||||
// B2-C2-D2-E2
|
||||
|
||||
let result = trigger_new_best_block(&mut es, a(), d1());
|
||||
assert!(result);
|
||||
assert_es_eq(&es, d1(), a());
|
||||
|
||||
let result = trigger_new_best_block(&mut es, d1(), e1());
|
||||
assert!(result);
|
||||
assert_es_eq(&es, e1(), a());
|
||||
|
||||
let result = trigger_finalized(&mut es, a(), d2());
|
||||
assert!(result);
|
||||
assert_es_eq(&es, d2(), d2());
|
||||
|
||||
let result = trigger_new_best_block(&mut es, d2(), e1());
|
||||
assert_eq!(result, false);
|
||||
assert_es_eq(&es, d2(), d2());
|
||||
|
||||
let result = trigger_finalized(&mut es, a(), b2());
|
||||
assert_eq!(result, false);
|
||||
assert_es_eq(&es, d2(), d2());
|
||||
|
||||
let result = trigger_finalized(&mut es, a(), b1());
|
||||
assert_eq!(result, false);
|
||||
assert_es_eq(&es, d2(), d2());
|
||||
|
||||
let result = trigger_new_best_block(&mut es, a(), d2());
|
||||
assert_eq!(result, false);
|
||||
assert_es_eq(&es, d2(), d2());
|
||||
|
||||
let result = trigger_finalized(&mut es, a(), d2());
|
||||
assert_eq!(result, false);
|
||||
assert_es_eq(&es, d2(), d2());
|
||||
|
||||
let result = trigger_new_best_block(&mut es, a(), c2());
|
||||
assert_eq!(result, false);
|
||||
assert_es_eq(&es, d2(), d2());
|
||||
|
||||
let result = trigger_new_best_block(&mut es, a(), c1());
|
||||
assert_eq!(result, false);
|
||||
assert_es_eq(&es, d2(), d2());
|
||||
|
||||
let result = trigger_new_best_block(&mut es, d2(), e2());
|
||||
assert!(result);
|
||||
assert_es_eq(&es, e2(), d2());
|
||||
|
||||
let result = trigger_finalized(&mut es, d2(), e2());
|
||||
assert_eq!(result, false);
|
||||
assert_es_eq(&es, e2(), e2());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enactment_helper_2() {
|
||||
sp_tracing::try_init_simple();
|
||||
let mut es = EnactmentState::new(a().hash, a().hash);
|
||||
|
||||
// A-B1-C1-D1-E1
|
||||
|
||||
let result = trigger_new_best_block(&mut es, a(), b1());
|
||||
assert!(result);
|
||||
assert_es_eq(&es, b1(), a());
|
||||
|
||||
let result = trigger_new_best_block(&mut es, b1(), c1());
|
||||
assert!(result);
|
||||
assert_es_eq(&es, c1(), a());
|
||||
|
||||
let result = trigger_new_best_block(&mut es, c1(), d1());
|
||||
assert!(result);
|
||||
assert_es_eq(&es, d1(), a());
|
||||
|
||||
let result = trigger_new_best_block(&mut es, d1(), e1());
|
||||
assert!(result);
|
||||
assert_es_eq(&es, e1(), a());
|
||||
|
||||
let result = trigger_finalized(&mut es, a(), c1());
|
||||
assert_eq!(result, false);
|
||||
assert_es_eq(&es, e1(), c1());
|
||||
|
||||
let result = trigger_finalized(&mut es, c1(), e1());
|
||||
assert_eq!(result, false);
|
||||
assert_es_eq(&es, e1(), e1());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enactment_helper_3() {
|
||||
sp_tracing::try_init_simple();
|
||||
let mut es = EnactmentState::new(a().hash, a().hash);
|
||||
|
||||
// A-B1-C1-D1-E1
|
||||
|
||||
let result = trigger_new_best_block(&mut es, a(), e1());
|
||||
assert!(result);
|
||||
assert_es_eq(&es, e1(), a());
|
||||
|
||||
let result = trigger_finalized(&mut es, a(), b1());
|
||||
assert_eq!(result, false);
|
||||
assert_es_eq(&es, e1(), b1());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enactment_helper_4() {
|
||||
sp_tracing::try_init_simple();
|
||||
let mut es = EnactmentState::new(a().hash, a().hash);
|
||||
|
||||
// A-B1-C1-D1-E1
|
||||
|
||||
let result = trigger_finalized(&mut es, a(), e1());
|
||||
assert!(result);
|
||||
assert_es_eq(&es, e1(), e1());
|
||||
|
||||
let result = trigger_finalized(&mut es, e1(), b1());
|
||||
assert_eq!(result, false);
|
||||
assert_es_eq(&es, e1(), e1());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enactment_helper_5() {
|
||||
sp_tracing::try_init_simple();
|
||||
let mut es = EnactmentState::new(a().hash, a().hash);
|
||||
|
||||
// B1-C1-D1-E1
|
||||
// /
|
||||
// A
|
||||
// \
|
||||
// B2-C2-D2-E2
|
||||
|
||||
let result = trigger_finalized(&mut es, a(), e1());
|
||||
assert!(result);
|
||||
assert_es_eq(&es, e1(), e1());
|
||||
|
||||
let result = trigger_finalized(&mut es, e1(), e2());
|
||||
assert_eq!(result, false);
|
||||
assert_es_eq(&es, e1(), e1());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_enactment_helper_6() {
|
||||
sp_tracing::try_init_simple();
|
||||
let mut es = EnactmentState::new(a().hash, a().hash);
|
||||
|
||||
// A-B1-C1-D1-E1
|
||||
|
||||
let result = trigger_new_best_block(&mut es, a(), b1());
|
||||
assert!(result);
|
||||
assert_es_eq(&es, b1(), a());
|
||||
|
||||
let result = trigger_finalized(&mut es, a(), d1());
|
||||
assert!(result);
|
||||
assert_es_eq(&es, d1(), d1());
|
||||
|
||||
let result = trigger_new_best_block(&mut es, a(), e1());
|
||||
assert!(result);
|
||||
assert_es_eq(&es, e1(), d1());
|
||||
|
||||
let result = trigger_new_best_block(&mut es, a(), c1());
|
||||
assert_eq!(result, false);
|
||||
assert_es_eq(&es, e1(), d1());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user