mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-04-27 09:17:58 +00:00
[BEEFY] Avoid missing voting sessions during node restart (#3074)
Related to https://github.com/paritytech/polkadot-sdk/issues/3003 and https://github.com/paritytech/polkadot-sdk/issues/2842 --------- Co-authored-by: Adrian Catangiu <adrian@parity.io>
This commit is contained in:
@@ -28,10 +28,12 @@ use crate::{
|
||||
},
|
||||
request_response::{on_demand_justifications_protocol_config, BeefyJustifsRequestHandler},
|
||||
},
|
||||
error::Error,
|
||||
gossip_protocol_name,
|
||||
justification::*,
|
||||
load_or_init_voter_state, wait_for_runtime_pallet, BeefyRPCLinks, BeefyVoterLinks, KnownPeers,
|
||||
PersistedState,
|
||||
wait_for_runtime_pallet,
|
||||
worker::{BeefyWorkerBase, PersistedState},
|
||||
BeefyRPCLinks, BeefyVoterLinks, KnownPeers,
|
||||
};
|
||||
use futures::{future, stream::FuturesUnordered, Future, FutureExt, StreamExt};
|
||||
use parking_lot::Mutex;
|
||||
@@ -363,7 +365,7 @@ async fn voter_init_setup(
|
||||
net: &mut BeefyTestNet,
|
||||
finality: &mut futures::stream::Fuse<FinalityNotifications<Block>>,
|
||||
api: &TestApi,
|
||||
) -> sp_blockchain::Result<PersistedState<Block>> {
|
||||
) -> Result<PersistedState<Block>, Error> {
|
||||
let backend = net.peer(0).client().as_backend();
|
||||
let known_peers = Arc::new(Mutex::new(KnownPeers::new()));
|
||||
let (gossip_validator, _) = GossipValidator::new(known_peers);
|
||||
@@ -378,7 +380,14 @@ async fn voter_init_setup(
|
||||
);
|
||||
let (beefy_genesis, best_grandpa) =
|
||||
wait_for_runtime_pallet(api, &mut gossip_engine, finality).await.unwrap();
|
||||
load_or_init_voter_state(&*backend, api, beefy_genesis, best_grandpa, 1).await
|
||||
let mut worker_base = BeefyWorkerBase {
|
||||
backend,
|
||||
runtime: Arc::new(api.clone()),
|
||||
key_store: None.into(),
|
||||
metrics: None,
|
||||
_phantom: Default::default(),
|
||||
};
|
||||
worker_base.load_or_init_state(beefy_genesis, best_grandpa, 1).await
|
||||
}
|
||||
|
||||
// Spawns beefy voters. Returns a future to spawn on the runtime.
|
||||
@@ -1072,9 +1081,15 @@ async fn should_initialize_voter_at_custom_genesis() {
|
||||
);
|
||||
let (beefy_genesis, best_grandpa) =
|
||||
wait_for_runtime_pallet(&api, &mut gossip_engine, &mut finality).await.unwrap();
|
||||
let persisted_state = load_or_init_voter_state(&*backend, &api, beefy_genesis, best_grandpa, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
let mut worker_base = BeefyWorkerBase {
|
||||
backend: backend.clone(),
|
||||
runtime: Arc::new(api),
|
||||
key_store: None.into(),
|
||||
metrics: None,
|
||||
_phantom: Default::default(),
|
||||
};
|
||||
let persisted_state =
|
||||
worker_base.load_or_init_state(beefy_genesis, best_grandpa, 1).await.unwrap();
|
||||
|
||||
// Test initialization at session boundary.
|
||||
// verify voter initialized with single session starting at block `custom_pallet_genesis` (7)
|
||||
@@ -1107,10 +1122,15 @@ async fn should_initialize_voter_at_custom_genesis() {
|
||||
// the network state persists and uses the old `GossipEngine` initialized for `peer(0)`
|
||||
let (beefy_genesis, best_grandpa) =
|
||||
wait_for_runtime_pallet(&api, &mut gossip_engine, &mut finality).await.unwrap();
|
||||
let mut worker_base = BeefyWorkerBase {
|
||||
backend: backend.clone(),
|
||||
runtime: Arc::new(api),
|
||||
key_store: None.into(),
|
||||
metrics: None,
|
||||
_phantom: Default::default(),
|
||||
};
|
||||
let new_persisted_state =
|
||||
load_or_init_voter_state(&*backend, &api, beefy_genesis, best_grandpa, 1)
|
||||
.await
|
||||
.unwrap();
|
||||
worker_base.load_or_init_state(beefy_genesis, best_grandpa, 1).await.unwrap();
|
||||
|
||||
// verify voter initialized with single session starting at block `new_pallet_genesis` (10)
|
||||
let sessions = new_persisted_state.voting_oracle().sessions();
|
||||
@@ -1285,6 +1305,104 @@ async fn should_initialize_voter_at_custom_genesis_when_state_unavailable() {
|
||||
assert_eq!(state, persisted_state);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn should_catch_up_when_loading_saved_voter_state() {
|
||||
let keys = &[BeefyKeyring::Alice];
|
||||
let validator_set = ValidatorSet::new(make_beefy_ids(keys), 0).unwrap();
|
||||
let mut net = BeefyTestNet::new(1);
|
||||
let backend = net.peer(0).client().as_backend();
|
||||
|
||||
// push 30 blocks with `AuthorityChange` digests every 10 blocks
|
||||
let hashes = net.generate_blocks_and_sync(30, 10, &validator_set, false).await;
|
||||
let mut finality = net.peer(0).client().as_client().finality_notification_stream().fuse();
|
||||
// finalize 13 without justifications
|
||||
net.peer(0).client().as_client().finalize_block(hashes[13], None).unwrap();
|
||||
|
||||
let api = TestApi::with_validator_set(&validator_set);
|
||||
|
||||
// load persistent state - nothing in DB, should init at genesis
|
||||
//
|
||||
// NOTE: code from `voter_init_setup()` is moved here because the new network event system
|
||||
// doesn't allow creating a new `GossipEngine` as the notification handle is consumed by the
|
||||
// first `GossipEngine`
|
||||
let known_peers = Arc::new(Mutex::new(KnownPeers::new()));
|
||||
let (gossip_validator, _) = GossipValidator::new(known_peers);
|
||||
let gossip_validator = Arc::new(gossip_validator);
|
||||
let mut gossip_engine = sc_network_gossip::GossipEngine::new(
|
||||
net.peer(0).network_service().clone(),
|
||||
net.peer(0).sync_service().clone(),
|
||||
net.peer(0).take_notification_service(&beefy_gossip_proto_name()).unwrap(),
|
||||
"/beefy/whatever",
|
||||
gossip_validator,
|
||||
None,
|
||||
);
|
||||
let (beefy_genesis, best_grandpa) =
|
||||
wait_for_runtime_pallet(&api, &mut gossip_engine, &mut finality).await.unwrap();
|
||||
let mut worker_base = BeefyWorkerBase {
|
||||
backend: backend.clone(),
|
||||
runtime: Arc::new(api.clone()),
|
||||
key_store: None.into(),
|
||||
metrics: None,
|
||||
_phantom: Default::default(),
|
||||
};
|
||||
let persisted_state =
|
||||
worker_base.load_or_init_state(beefy_genesis, best_grandpa, 1).await.unwrap();
|
||||
|
||||
// Test initialization at session boundary.
|
||||
// verify voter initialized with two sessions starting at blocks 1 and 10
|
||||
let sessions = persisted_state.voting_oracle().sessions();
|
||||
assert_eq!(sessions.len(), 2);
|
||||
assert_eq!(sessions[0].session_start(), 1);
|
||||
assert_eq!(sessions[1].session_start(), 10);
|
||||
let rounds = persisted_state.active_round().unwrap();
|
||||
assert_eq!(rounds.session_start(), 1);
|
||||
assert_eq!(rounds.validator_set_id(), validator_set.id());
|
||||
|
||||
// verify next vote target is mandatory block 1
|
||||
assert_eq!(persisted_state.best_beefy(), 0);
|
||||
assert_eq!(persisted_state.best_grandpa_number(), 13);
|
||||
assert_eq!(persisted_state.voting_oracle().voting_target(), Some(1));
|
||||
|
||||
// verify state also saved to db
|
||||
assert!(verify_persisted_version(&*backend));
|
||||
let state = load_persistent(&*backend).unwrap().unwrap();
|
||||
assert_eq!(state, persisted_state);
|
||||
|
||||
// now let's consider that the node goes offline, and then it restarts after a while
|
||||
|
||||
// finalize 25 without justifications
|
||||
net.peer(0).client().as_client().finalize_block(hashes[25], None).unwrap();
|
||||
// load persistent state - state preset in DB
|
||||
// the network state persists and uses the old `GossipEngine` initialized for `peer(0)`
|
||||
let (beefy_genesis, best_grandpa) =
|
||||
wait_for_runtime_pallet(&api, &mut gossip_engine, &mut finality).await.unwrap();
|
||||
let mut worker_base = BeefyWorkerBase {
|
||||
backend: backend.clone(),
|
||||
runtime: Arc::new(api),
|
||||
key_store: None.into(),
|
||||
metrics: None,
|
||||
_phantom: Default::default(),
|
||||
};
|
||||
let persisted_state =
|
||||
worker_base.load_or_init_state(beefy_genesis, best_grandpa, 1).await.unwrap();
|
||||
|
||||
// Verify voter initialized with old sessions plus a new one starting at block 20.
|
||||
// There shouldn't be any duplicates.
|
||||
let sessions = persisted_state.voting_oracle().sessions();
|
||||
assert_eq!(sessions.len(), 3);
|
||||
assert_eq!(sessions[0].session_start(), 1);
|
||||
assert_eq!(sessions[1].session_start(), 10);
|
||||
assert_eq!(sessions[2].session_start(), 20);
|
||||
let rounds = persisted_state.active_round().unwrap();
|
||||
assert_eq!(rounds.session_start(), 1);
|
||||
assert_eq!(rounds.validator_set_id(), validator_set.id());
|
||||
|
||||
// verify next vote target is mandatory block 1
|
||||
assert_eq!(persisted_state.best_beefy(), 0);
|
||||
assert_eq!(persisted_state.best_grandpa_number(), 25);
|
||||
assert_eq!(persisted_state.voting_oracle().voting_target(), Some(1));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn beefy_finalizing_after_pallet_genesis() {
|
||||
sp_tracing::try_init_simple();
|
||||
|
||||
Reference in New Issue
Block a user