Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 29 additions & 28 deletions .github/workflows/zombienet_polkadot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -229,34 +229,6 @@ jobs:
build-id: ${{ needs.preflight.outputs.BUILD_RUN_ID }}
ref-slug: ${{ needs.preflight.outputs.SOURCE_REF_SLUG }}


#
zombienet-polkadot-functional-0010-validator-disabling:
needs: [preflight]
if: ${{ (needs.preflight.outputs.changes_substrate || needs.preflight.outputs.changes_polkadot) && ! contains(needs.preflight.outputs.FLAKY_TESTS, 'zombienet-polkadot-functional-0010-validator-disabling') }}
runs-on: ${{ needs.preflight.outputs.ZOMBIENET_LARGE_RUNNER }}
timeout-minutes: 60
container:
image: ${{ needs.preflight.outputs.ZOMBIENET_IMAGE }}
options: -v /tmp/zombienet:/tmp/zombienet
env:
ZOMBIENET_INTEGRATION_TEST_IMAGE: "${{ needs.preflight.outputs.TEMP_IMAGES_BASE }}/polkadot-debug:${{ needs.preflight.outputs.DOCKER_IMAGES_VERSION }}"
COL_IMAGE: "${{ needs.preflight.outputs.TEMP_IMAGES_BASE }}/colander:${{ needs.preflight.outputs.DOCKER_IMAGES_VERSION }}"
MALUS_IMAGE: "${{ needs.preflight.outputs.TEMP_IMAGES_BASE }}/malus:${{ needs.preflight.outputs.DOCKER_IMAGES_VERSION }}"
DEBUG: ${{ needs.preflight.outputs.DEBUG }}
ZOMBIENET_PROVIDER: ${{ needs.preflight.outputs.ZOMBIENET_PROVIDER }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: zombienet_test
uses: ./.github/actions/zombienet
with:
test: "0010-validator-disabling.zndsl"
local-dir: "${{ env.LOCAL_DIR }}/functional"
gh-token: ${{ secrets.GITHUB_TOKEN }}
build-id: ${{ needs.preflight.outputs.BUILD_RUN_ID }}
ref-slug: ${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
#
#
zombienet-polkadot-functional-0013-systematic-chunk-recovery:
Expand Down Expand Up @@ -1024,6 +996,35 @@ jobs:
test: "functional::approved_peer_mixed_validators::approved_peer_mixed_validators_test"
prefix: "polkadot"

#
#
zombienet-polkadot-functional-validator-disabling:
needs: [preflight]
if: ${{ (needs.preflight.outputs.changes_substrate || needs.preflight.outputs.changes_polkadot) && ! contains(needs.preflight.outputs.FLAKY_TESTS, 'zombienet-polkadot-functional-0010-validator-disabling') }}
runs-on: ${{ needs.preflight.outputs.ZOMBIENET_LARGE_RUNNER }}
timeout-minutes: 60
container:
image: ${{ needs.preflight.outputs.ZOMBIENET_SDK_IMAGE }}
env:
POLKADOT_IMAGE: "${{ needs.preflight.outputs.TEMP_IMAGES_BASE }}/polkadot-debug:${{ needs.preflight.outputs.DOCKER_IMAGES_VERSION }}"
COL_IMAGE: "${{ needs.preflight.outputs.TEMP_IMAGES_BASE }}/colander:${{ needs.preflight.outputs.DOCKER_IMAGES_VERSION }}"
CUMULUS_IMAGE: "${{ needs.preflight.outputs.TEMP_IMAGES_BASE }}/polkadot-parachain-debug:${{ needs.preflight.outputs.DOCKER_IMAGES_VERSION }}"
MALUS_IMAGE: "${{ needs.preflight.outputs.TEMP_IMAGES_BASE }}/malus:${{ needs.preflight.outputs.DOCKER_IMAGES_VERSION }}"
RUST_LOG: ${{ needs.preflight.outputs.RUST_LOG }}
ZOMBIE_PROVIDER: ${{ needs.preflight.outputs.ZOMBIE_PROVIDER }}
steps:
- name: Checkout
uses: actions/checkout@v4

- name: zombienet_test
uses: ./.github/actions/zombienet-sdk
with:
gh-token: ${{ secrets.GITHUB_TOKEN }}
build-id: ${{ needs.preflight.outputs.BUILD_RUN_ID }}
ref-slug: ${{ needs.preflight.outputs.SOURCE_REF_SLUG }}
test: "functional::validator_disabling::validator_disabling_test"
prefix: "polkadot"

#
#
zombienet-polkadot-shared-core-idle-parachain:
Expand Down
1 change: 1 addition & 0 deletions polkadot/zombienet-sdk-tests/tests/functional/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ mod duplicate_collations;
mod shared_core_idle_parachain;
mod spam_statement_distribution_requests;
mod sync_backing;
mod validator_disabling;
164 changes: 164 additions & 0 deletions polkadot/zombienet-sdk-tests/tests/functional/validator_disabling.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
// Copyright (C) Parity Technologies (UK) Ltd.
// SPDX-License-Identifier: Apache-2.0

// Test checks that misbehaving validators disabled.
use anyhow::anyhow;
use cumulus_zombienet_sdk_helpers::assert_para_throughput;
use polkadot_primitives::{
BlockNumber, CandidateHash, DisputeState, SessionIndex, ValidatorId, ValidatorIndex,
};
use serde_json::json;
use zombienet_sdk::{
subxt::{OnlineClient, PolkadotConfig},
NetworkConfigBuilder,
};

#[tokio::test(flavor = "multi_thread")]
async fn validator_disabling_test() -> Result<(), anyhow::Error> {
let _ = env_logger::try_init_from_env(
env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
);
let images = zombienet_sdk::environment::get_images_from_env();
let config_builder = NetworkConfigBuilder::new()
.with_relaychain(|r| {
let r = r
.with_chain("westend-local") // Use westend-local so the disabling can take effect.
.with_default_command("polkadot")
.with_default_image(images.polkadot.as_str())
.with_default_args(vec![("-lparachain=debug").into()])
.with_genesis_overrides(json!({
"configuration": {
"config": {
"scheduler_params": {
"group_rotation_frequency": 10,
"max_validators_per_core": 1
},
"needed_approvals": 2,
}
}
}))
// Adding malicious validator.
.with_node(|node| {
node.with_name("malus-validator")
.with_image(
std::env::var("MALUS_IMAGE")
.unwrap_or("docker.io/paritypr/malus".to_string())
.as_str(),
)
.with_command("malus")
.with_subcommand("suggest-garbage-candidate")
.with_args(vec![
"-lMALUS=trace".into(),
// Without this the malus validator won't run on macOS.
"--insecure-validator-i-know-what-i-do".into(),
])
// Make it vulenrable so disabling really happens
.invulnerable(false)
});
// Also honest validators.
let r = (0..3).fold(r, |acc, i| {
acc.with_node(|node| {
node.with_name(&format!("honest-validator-{i}"))
.with_args(vec![("-lparachain=debug,runtime::staking=debug".into())])
.invulnerable(false)
})
});
r
})
.with_parachain(|p| {
p.with_id(1000)
.with_default_command("adder-collator")
.cumulus_based(false)
.with_default_image(images.cumulus.as_str())
.with_default_args(vec!["-lparachain=debug".into()])
.with_collator(|n| n.with_name("alice"))
})
.build()
.map_err(|e| {
let errors = e.into_iter().map(|e| e.to_string()).collect::<Vec<_>>().join(" ");
anyhow!("config errors: {errors}")
})?;

let spawn_fn = zombienet_sdk::environment::get_spawn_fn();
log::info!("Spawning network");
let network = spawn_fn(config_builder).await?;

log::info!("Waiting for parablocks to be produced");
let honest_validator = network.get_node("honest-validator-0")?;
let relay_client: OnlineClient<PolkadotConfig> = honest_validator.wait_client().await?;
assert_para_throughput(
&relay_client,
20,
[(polkadot_primitives::Id::from(1000), 10..30)].into_iter().collect(),
)
.await?;

log::info!("Wait for a dispute to be initialized.");
let mut best_blocks = relay_client.blocks().subscribe_best().await?;
let mut dispute_session: u32 = u32::MAX;
let mut block_hash = None;
// Check next new block from the current best fork
while let Some(block) = best_blocks.next().await {
let current_hash = block?.hash();
let disputes = relay_client
.runtime_api()
.at(current_hash)
.call_raw::<Vec<(SessionIndex, CandidateHash, DisputeState<BlockNumber>)>>(
"ParachainHost_disputes",
None,
)
.await?;
if let Some((session, _, _)) = disputes.first() {
block_hash = Some(current_hash);
dispute_session = *session;
break;
}
}

assert_ne!(dispute_session, u32::MAX);
log::info!("Dispute initiated.");
let concluded_dispute_metric =
"polkadot_parachain_candidate_dispute_concluded{validity=\"invalid\"}";
let parachain_candidate_dispute_metric = "parachain_candidate_disputes_total";
// Check that we have at least one dispute
honest_validator
.wait_metric_with_timeout(parachain_candidate_dispute_metric, |d| d >= 1.0, 600_u64)
.await?;
// Check that we have at least one concluded dispute.
honest_validator
.wait_metric_with_timeout(concluded_dispute_metric, |d| d >= 1.0, 200_u64)
.await?;

let disabled_validators = relay_client
.runtime_api()
.at(block_hash.unwrap())
.call_raw::<Vec<ValidatorIndex>>("ParachainHost_disabled_validators", None)
.await?;
// We should have at least one disable validator.
assert!(!disabled_validators.is_empty());

let session_validators = relay_client
.runtime_api()
.at(block_hash.unwrap())
.call_raw::<Vec<ValidatorId>>("ParachainHost_validators", None)
.await?;
// We have a single malicious node, hence the index of the malus-node is the first
// entry in the disabled validators list.
let disabled_node_public_address = &session_validators[(disabled_validators[0].0) as usize];

let disabled_node_public_key_hex = disabled_node_public_address
.clone()
.into_inner()
.0
.iter()
.map(|byte| format!("{byte:02x}"))
.collect::<String>();

let json_value: serde_json::Value =
serde_json::to_value(network.get_node("malus-validator")?.spec())?;
let malus_public_address =
json_value.pointer("/accounts/accounts/sr/public_key").unwrap().to_string();

assert_eq!(disabled_node_public_key_hex, malus_public_address.trim_matches('"'));
Ok(())
}
41 changes: 0 additions & 41 deletions polkadot/zombienet_tests/functional/0010-validator-disabling.toml

This file was deleted.

21 changes: 0 additions & 21 deletions polkadot/zombienet_tests/functional/0010-validator-disabling.zndsl

This file was deleted.

8 changes: 8 additions & 0 deletions prdoc/pr_9128.prdoc
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
title: Rewrite validator disabling test with zombienet-sdk

doc:
- audience: Node Dev
description: |-
Migrate validator disabling test to the new version of zombienet

crates: []
Loading