mirror of
https://github.com/pezkuwichain/pezkuwi-subxt.git
synced 2026-05-30 12:51:02 +00:00
Egress queue validation in runtime (#155)
* Add fn to validate egress routes * Add blank tests * Reject routing to non existent parachains * Reject if routing to self * Reject if egress route parachain ids out of order * Extract method for checking egress routes * Reject empty egress routes * Extract test method * Generate empty trie root constant in build script * Remove unwraps * Hardcode EMPTY_TRIE_ROOT and add test to verify * Const not pu
This commit is contained in:
committed by
Robert Habermeier
parent
bef6069cc9
commit
0a25c425b0
@@ -6,5 +6,6 @@ polkadot/runtime/wasm/target/
|
|||||||
**/._*
|
**/._*
|
||||||
.idea
|
.idea
|
||||||
.vscode
|
.vscode
|
||||||
|
.idea
|
||||||
polkadot.*
|
polkadot.*
|
||||||
.DS_Store
|
.DS_Store
|
||||||
|
|||||||
Generated
+2
@@ -2319,7 +2319,9 @@ dependencies = [
|
|||||||
"substrate-keyring 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"substrate-keyring 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"substrate-primitives 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"substrate-serializer 0.1.0 (git+https://github.com/paritytech/substrate)",
|
"substrate-serializer 0.1.0 (git+https://github.com/paritytech/substrate)",
|
||||||
|
"substrate-trie 0.4.0 (git+https://github.com/paritytech/substrate)",
|
||||||
"tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
"tiny-keccak 1.4.2 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
"trie-db 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ hex-literal = "0.1.0"
|
|||||||
libsecp256k1 = "0.2.1"
|
libsecp256k1 = "0.2.1"
|
||||||
tiny-keccak = "1.4.2"
|
tiny-keccak = "1.4.2"
|
||||||
substrate-keyring = { git = "https://github.com/paritytech/substrate" }
|
substrate-keyring = { git = "https://github.com/paritytech/substrate" }
|
||||||
|
substrate-trie = { git = "https://github.com/paritytech/substrate" }
|
||||||
|
trie-db = "0.11"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
|||||||
@@ -70,6 +70,9 @@ extern crate polkadot_primitives as primitives;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
extern crate substrate_keyring as keyring;
|
extern crate substrate_keyring as keyring;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
extern crate substrate_trie;
|
||||||
|
|
||||||
mod parachains;
|
mod parachains;
|
||||||
mod claims;
|
mod claims;
|
||||||
|
|
||||||
|
|||||||
@@ -40,6 +40,12 @@ use system::ensure_inherent;
|
|||||||
|
|
||||||
pub trait Trait: session::Trait {}
|
pub trait Trait: session::Trait {}
|
||||||
|
|
||||||
|
// result of <NodeCodec<Blake2Hasher> as trie_db::NodeCodec<Blake2Hasher>>::hashed_null_node()
|
||||||
|
const EMPTY_TRIE_ROOT: [u8; 32] = [
|
||||||
|
3, 23, 10, 46, 117, 151, 183, 183, 227, 216, 76, 5, 57, 29, 19, 154,
|
||||||
|
98, 177, 87, 231, 135, 134, 216, 192, 130, 242, 157, 207, 76, 17, 19, 20
|
||||||
|
];
|
||||||
|
|
||||||
decl_storage! {
|
decl_storage! {
|
||||||
trait Store for Module<T: Trait> as Parachains {
|
trait Store for Module<T: Trait> as Parachains {
|
||||||
// Vector of all parachain IDs.
|
// Vector of all parachain IDs.
|
||||||
@@ -110,13 +116,14 @@ decl_module! {
|
|||||||
"Submitted candidate for unregistered or out-of-order parachain {}"
|
"Submitted candidate for unregistered or out-of-order parachain {}"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Self::check_egress_queue_roots(&head, &active_parachains)?;
|
||||||
|
|
||||||
last_id = Some(head.parachain_index());
|
last_id = Some(head.parachain_index());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::check_attestations(&heads)?;
|
Self::check_attestations(&heads)?;
|
||||||
|
|
||||||
|
|
||||||
for head in heads {
|
for head in heads {
|
||||||
let id = head.parachain_index();
|
let id = head.parachain_index();
|
||||||
<Heads<T>>::insert(id, head.candidate.head_data.0);
|
<Heads<T>>::insert(id, head.candidate.head_data.0);
|
||||||
@@ -248,6 +255,39 @@ impl<T: Trait> Module<T> {
|
|||||||
.collect())
|
.collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn check_egress_queue_roots(head: &AttestedCandidate, active_parachains: &[ParaId]) -> Result {
|
||||||
|
let mut last_egress_id = None;
|
||||||
|
let mut iter = active_parachains.iter();
|
||||||
|
for (egress_para_id, root) in &head.candidate.egress_queue_roots {
|
||||||
|
// egress routes should be ascending order by parachain ID without duplicate.
|
||||||
|
ensure!(
|
||||||
|
last_egress_id.as_ref().map_or(true, |x| x < &egress_para_id),
|
||||||
|
"Egress routes out of order by ID"
|
||||||
|
);
|
||||||
|
|
||||||
|
// a parachain can't route to self
|
||||||
|
ensure!(
|
||||||
|
*egress_para_id != head.candidate.parachain_index,
|
||||||
|
"Parachain routing to self"
|
||||||
|
);
|
||||||
|
|
||||||
|
// no empty trie roots
|
||||||
|
ensure!(
|
||||||
|
*root != EMPTY_TRIE_ROOT.into(),
|
||||||
|
"Empty trie root included"
|
||||||
|
);
|
||||||
|
|
||||||
|
// can't route to a parachain which doesn't exist
|
||||||
|
ensure!(
|
||||||
|
iter.find(|x| x == &egress_para_id).is_some(),
|
||||||
|
"Routing to non-existent parachain"
|
||||||
|
);
|
||||||
|
|
||||||
|
last_egress_id = Some(egress_para_id)
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
// check the attestations on these candidates. The candidates should have been checked
|
// check the attestations on these candidates. The candidates should have been checked
|
||||||
// that each candidates' chain ID is valid.
|
// that each candidates' chain ID is valid.
|
||||||
fn check_attestations(attested_candidates: &[AttestedCandidate]) -> Result {
|
fn check_attestations(attested_candidates: &[AttestedCandidate]) -> Result {
|
||||||
@@ -424,6 +464,7 @@ mod tests {
|
|||||||
use super::*;
|
use super::*;
|
||||||
use sr_io::{TestExternalities, with_externalities};
|
use sr_io::{TestExternalities, with_externalities};
|
||||||
use substrate_primitives::{H256, Blake2Hasher};
|
use substrate_primitives::{H256, Blake2Hasher};
|
||||||
|
use substrate_trie::NodeCodec;
|
||||||
use sr_primitives::{generic, BuildStorage};
|
use sr_primitives::{generic, BuildStorage};
|
||||||
use sr_primitives::traits::{BlakeTwo256, IdentityLookup};
|
use sr_primitives::traits::{BlakeTwo256, IdentityLookup};
|
||||||
use primitives::{parachain::{CandidateReceipt, HeadData, ValidityAttestation}, SessionKey};
|
use primitives::{parachain::{CandidateReceipt, HeadData, ValidityAttestation}, SessionKey};
|
||||||
@@ -533,6 +574,22 @@ mod tests {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn new_candidate_with_egress_roots(egress_queue_roots: Vec<(ParaId, H256)>) -> AttestedCandidate {
|
||||||
|
AttestedCandidate {
|
||||||
|
validity_votes: vec![],
|
||||||
|
candidate: CandidateReceipt {
|
||||||
|
parachain_index: 0.into(),
|
||||||
|
collator: Default::default(),
|
||||||
|
signature: Default::default(),
|
||||||
|
head_data: HeadData(vec![1, 2, 3]),
|
||||||
|
balance_uploads: vec![],
|
||||||
|
egress_queue_roots,
|
||||||
|
fees: 0,
|
||||||
|
block_data_hash: Default::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn active_parachains_should_work() {
|
fn active_parachains_should_work() {
|
||||||
let parachains = vec![
|
let parachains = vec![
|
||||||
@@ -789,4 +846,110 @@ mod tests {
|
|||||||
assert_eq!(Parachains::ingress(ParaId::from(99)), Some(Vec::new()));
|
assert_eq!(Parachains::ingress(ParaId::from(99)), Some(Vec::new()));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn egress_routed_to_non_existent_parachain_is_rejected() {
|
||||||
|
// That no parachain is routed to which doesn't exist
|
||||||
|
let parachains = vec![
|
||||||
|
(0u32.into(), vec![], vec![]),
|
||||||
|
(1u32.into(), vec![], vec![]),
|
||||||
|
];
|
||||||
|
|
||||||
|
with_externalities(&mut new_test_ext(parachains), || {
|
||||||
|
system::Module::<Test>::set_random_seed([0u8; 32].into());
|
||||||
|
// parachain 99 does not exist
|
||||||
|
let non_existent = vec![(99.into(), [1; 32].into())];
|
||||||
|
let mut candidate = new_candidate_with_egress_roots(non_existent);
|
||||||
|
|
||||||
|
make_attestations(&mut candidate);
|
||||||
|
|
||||||
|
let result = Parachains::dispatch(
|
||||||
|
Call::set_heads(vec![candidate.clone()]),
|
||||||
|
Origin::INHERENT,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(Err("Routing to non-existent parachain"), result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn egress_routed_to_self_is_rejected() {
|
||||||
|
// That the parachain doesn't route to self
|
||||||
|
let parachains = vec![
|
||||||
|
(0u32.into(), vec![], vec![]),
|
||||||
|
(1u32.into(), vec![], vec![]),
|
||||||
|
];
|
||||||
|
|
||||||
|
with_externalities(&mut new_test_ext(parachains), || {
|
||||||
|
system::Module::<Test>::set_random_seed([0u8; 32].into());
|
||||||
|
// parachain 0 is self
|
||||||
|
let to_self = vec![(0.into(), [1; 32].into())];
|
||||||
|
let mut candidate = new_candidate_with_egress_roots(to_self);
|
||||||
|
|
||||||
|
make_attestations(&mut candidate);
|
||||||
|
|
||||||
|
let result = Parachains::dispatch(
|
||||||
|
Call::set_heads(vec![candidate.clone()]),
|
||||||
|
Origin::INHERENT,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(Err("Parachain routing to self"), result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn egress_queue_roots_out_of_order_rejected() {
|
||||||
|
// That the list of egress queue roots is in ascending order by `ParaId`.
|
||||||
|
let parachains = vec![
|
||||||
|
(0u32.into(), vec![], vec![]),
|
||||||
|
(1u32.into(), vec![], vec![]),
|
||||||
|
];
|
||||||
|
|
||||||
|
with_externalities(&mut new_test_ext(parachains), || {
|
||||||
|
system::Module::<Test>::set_random_seed([0u8; 32].into());
|
||||||
|
// parachain 0 is self
|
||||||
|
let out_of_order = vec![(1.into(), [1; 32].into()), ((0.into(), [1; 32].into()))];
|
||||||
|
let mut candidate = new_candidate_with_egress_roots(out_of_order);
|
||||||
|
|
||||||
|
make_attestations(&mut candidate);
|
||||||
|
|
||||||
|
let result = Parachains::dispatch(
|
||||||
|
Call::set_heads(vec![candidate.clone()]),
|
||||||
|
Origin::INHERENT,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(Err("Egress routes out of order by ID"), result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn egress_queue_roots_empty_trie_roots_rejected() {
|
||||||
|
let parachains = vec![
|
||||||
|
(0u32.into(), vec![], vec![]),
|
||||||
|
(1u32.into(), vec![], vec![]),
|
||||||
|
(2u32.into(), vec![], vec![]),
|
||||||
|
];
|
||||||
|
|
||||||
|
with_externalities(&mut new_test_ext(parachains), || {
|
||||||
|
system::Module::<Test>::set_random_seed([0u8; 32].into());
|
||||||
|
// parachain 0 is self
|
||||||
|
let contains_empty_trie_root = vec![(1.into(), [1; 32].into()), ((2.into(), EMPTY_TRIE_ROOT.into()))];
|
||||||
|
let mut candidate = new_candidate_with_egress_roots(contains_empty_trie_root);
|
||||||
|
|
||||||
|
make_attestations(&mut candidate);
|
||||||
|
|
||||||
|
let result = Parachains::dispatch(
|
||||||
|
Call::set_heads(vec![candidate.clone()]),
|
||||||
|
Origin::INHERENT,
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(Err("Empty trie root included"), result);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn empty_trie_root_const_is_blake2_hashed_null_node() {
|
||||||
|
let hashed_null_node = <NodeCodec<Blake2Hasher> as trie_db::NodeCodec<Blake2Hasher>>::hashed_null_node();
|
||||||
|
assert_eq!(hashed_null_node, EMPTY_TRIE_ROOT.into())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user