// Copyright 2019-2020 Parity Technologies (UK) Ltd. // This file is part of Parity Bridges Common. // Parity Bridges Common 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. // Parity Bridges Common 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 Parity Bridges Common. If not, see . // Copyright 2019 Parity Technologies (UK) Ltd. // This file is part of Parity-Bridge. // Parity-Bridge 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. // Parity-Bridge 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 Parity-Bridge. If not, see . use crate::error::Error; use crate::validators::step_validator; use crate::{AuraConfiguration, ImportContext, Storage}; use primitives::{public_to_address, Address, Header, SealedEmptyStep, H256, H520, U128, U256}; use sp_io::crypto::secp256k1_ecdsa_recover; /// Verify header by Aura rules. pub fn verify_aura_header( storage: &S, params: &AuraConfiguration, header: &Header, ) -> Result { // let's do the lightest check first contextless_checks(params, header)?; // the rest of checks requires parent let context = storage .import_context(&header.parent_hash) .ok_or(Error::MissingParentBlock)?; let validators = context.validators(); let header_step = header.step().ok_or(Error::MissingStep)?; let parent_step = context.parent_header().step().ok_or(Error::MissingStep)?; // Ensure header is from the step after context. if header_step == parent_step || (header.number >= params.validate_step_transition && header_step <= parent_step) { return Err(Error::DoubleVote); } // If empty step messages are enabled we will validate the messages in the seal, missing messages are not // reported as there's no way to tell whether the empty step message was never sent or simply not included. let empty_steps_len = match header.number >= params.empty_steps_transition { true => { let strict_empty_steps = header.number >= params.strict_empty_steps_transition; let empty_steps = header.empty_steps().ok_or(Error::MissingEmptySteps)?; let empty_steps_len = empty_steps.len(); let mut prev_empty_step = 0; for empty_step in empty_steps { if empty_step.step <= parent_step || empty_step.step >= header_step { return Err(Error::InsufficientProof); } if !verify_empty_step(&header.parent_hash, &empty_step, validators) { return Err(Error::InsufficientProof); } if strict_empty_steps { if empty_step.step <= prev_empty_step { return Err(Error::InsufficientProof); } prev_empty_step = empty_step.step; } } empty_steps_len } false => 0, }; // Validate chain score. if header.number >= params.validate_score_transition { let expected_difficulty = calculate_score(parent_step, header_step, empty_steps_len as _); if header.difficulty != expected_difficulty { return Err(Error::InvalidDifficulty); } } let expected_validator = step_validator(validators, header_step); if header.author != expected_validator { return Err(Error::NotValidator); } let validator_signature = header.signature().ok_or(Error::MissingSignature)?; let header_seal_hash = header .seal_hash(header.number >= params.empty_steps_transition) .ok_or(Error::MissingEmptySteps)?; let is_invalid_proposer = !verify_signature(&expected_validator, &validator_signature, &header_seal_hash); if is_invalid_proposer { return Err(Error::NotValidator); } Ok(context) } /// Perform basic checks that only require header iteself. fn contextless_checks(config: &AuraConfiguration, header: &Header) -> Result<(), Error> { let expected_seal_fields = expected_header_seal_fields(config, header); if header.seal.len() != expected_seal_fields { return Err(Error::InvalidSealArity); } if header.number >= u64::max_value() { return Err(Error::RidiculousNumber); } if header.gas_used > header.gas_limit { return Err(Error::TooMuchGasUsed); } if header.gas_limit < config.min_gas_limit { return Err(Error::InvalidGasLimit); } if header.gas_limit > config.max_gas_limit { return Err(Error::InvalidGasLimit); } if header.number != 0 && header.extra_data.len() as u64 > config.maximum_extra_data_size { return Err(Error::ExtraDataOutOfBounds); } // we can't detect if block is from future in runtime // => let's only do an overflow check if header.timestamp > i32::max_value() as u64 { return Err(Error::TimestampOverflow); } Ok(()) } /// Returns expected number of seal fields in the header. fn expected_header_seal_fields(config: &AuraConfiguration, header: &Header) -> usize { if header.number >= config.empty_steps_transition { 3 } else { 2 } } /// Verify single sealed empty step. fn verify_empty_step(parent_hash: &H256, step: &SealedEmptyStep, validators: &[Address]) -> bool { let expected_validator = step_validator(validators, step.step); let message = step.message(parent_hash); verify_signature(&expected_validator, &step.signature, &message) } /// Chain scoring: total weight is sqrt(U256::max_value())*height - step fn calculate_score(parent_step: u64, current_step: u64, current_empty_steps: usize) -> U256 { U256::from(U128::max_value()) + U256::from(parent_step) - U256::from(current_step) + U256::from(current_empty_steps) } /// Verify that the signature over message has been produced by given validator. fn verify_signature(expected_validator: &Address, signature: &H520, message: &H256) -> bool { secp256k1_ecdsa_recover(signature.as_fixed_bytes(), message.as_fixed_bytes()) .map(|public| public_to_address(&public)) .map(|address| *expected_validator == address) .unwrap_or(false) } #[cfg(test)] mod tests { use super::*; use crate::kovan_aura_config; use crate::tests::{genesis, signed_header, validator, validators_addresses, InMemoryStorage}; use parity_crypto::publickey::{sign, KeyPair}; use primitives::{rlp_encode, H520}; fn sealed_empty_step(validators: &[KeyPair], parent_hash: &H256, step: u64) -> SealedEmptyStep { let mut empty_step = SealedEmptyStep { step, signature: Default::default(), }; let message = empty_step.message(parent_hash); let validator_index = (step % validators.len() as u64) as usize; let signature: [u8; 65] = sign(validators[validator_index].secret(), &message.as_fixed_bytes().into()) .unwrap() .into(); empty_step.signature = signature.into(); empty_step } fn verify_with_config(config: &AuraConfiguration, header: &Header) -> Result { let storage = InMemoryStorage::new(genesis(), validators_addresses(3)); verify_aura_header(&storage, &config, header) } fn default_verify(header: &Header) -> Result { verify_with_config(&kovan_aura_config(), header) } #[test] fn verifies_seal_count() { // when there are no seals at all let mut header = Header::default(); assert_eq!(default_verify(&header), Err(Error::InvalidSealArity)); // when there's single seal (we expect 2 or 3 seals) header.seal = vec![vec![].into()]; assert_eq!(default_verify(&header), Err(Error::InvalidSealArity)); // when there's 3 seals (we expect 2 on Kovan) header.seal = vec![vec![].into(), vec![].into(), vec![].into()]; assert_eq!(default_verify(&header), Err(Error::InvalidSealArity)); // when there's 2 seals header.seal = vec![vec![].into(), vec![].into()]; assert_ne!(default_verify(&header), Err(Error::InvalidSealArity)); } #[test] fn verifies_header_number() { // when number is u64::max_value() let mut header = Header { seal: vec![vec![].into(), vec![].into(), vec![].into()], number: u64::max_value(), ..Default::default() }; assert_eq!(default_verify(&header), Err(Error::RidiculousNumber)); // when header is < u64::max_value() header.seal = vec![vec![].into(), vec![].into()]; header.number -= 1; assert_ne!(default_verify(&header), Err(Error::RidiculousNumber)); } #[test] fn verifies_gas_used() { // when gas used is larger than gas limit let mut header = Header { seal: vec![vec![].into(), vec![].into()], gas_used: 1.into(), gas_limit: 0.into(), ..Default::default() }; assert_eq!(default_verify(&header), Err(Error::TooMuchGasUsed)); // when gas used is less than gas limit header.gas_limit = 1.into(); assert_ne!(default_verify(&header), Err(Error::TooMuchGasUsed)); } #[test] fn verifies_gas_limit() { let mut config = kovan_aura_config(); config.min_gas_limit = 100.into(); config.max_gas_limit = 200.into(); // when limit is lower than expected let mut header = Header { seal: vec![vec![].into(), vec![].into()], gas_limit: 50.into(), ..Default::default() }; assert_eq!(verify_with_config(&config, &header), Err(Error::InvalidGasLimit)); // when limit is larger than expected header.gas_limit = 250.into(); assert_eq!(verify_with_config(&config, &header), Err(Error::InvalidGasLimit)); // when limit is within expected range header.gas_limit = 150.into(); assert_ne!(verify_with_config(&config, &header), Err(Error::InvalidGasLimit)); } #[test] fn verifies_extra_data_len() { // when extra data is too large let mut header = Header { seal: vec![vec![].into(), vec![].into()], gas_limit: kovan_aura_config().min_gas_limit, extra_data: std::iter::repeat(42).take(1000).collect::>().into(), number: 1, ..Default::default() }; assert_eq!(default_verify(&header), Err(Error::ExtraDataOutOfBounds)); // when extra data size is OK header.extra_data = std::iter::repeat(42).take(10).collect::>().into(); assert_ne!(default_verify(&header), Err(Error::ExtraDataOutOfBounds)); } #[test] fn verifies_timestamp() { // when timestamp overflows i32 let mut header = Header { seal: vec![vec![].into(), vec![].into()], gas_limit: kovan_aura_config().min_gas_limit, timestamp: i32::max_value() as u64 + 1, ..Default::default() }; assert_eq!(default_verify(&header), Err(Error::TimestampOverflow)); // when timestamp doesn't overflow i32 header.timestamp -= 1; assert_ne!(default_verify(&header), Err(Error::TimestampOverflow)); } #[test] fn verifies_parent_existence() { // when there's no parent in the storage let mut header = Header { seal: vec![vec![].into(), vec![].into()], gas_limit: kovan_aura_config().min_gas_limit, ..Default::default() }; assert_eq!(default_verify(&header), Err(Error::MissingParentBlock)); // when parent is in the storage header.parent_hash = genesis().hash(); assert_ne!(default_verify(&header), Err(Error::MissingParentBlock)); } #[test] fn verifies_step() { // when step is missing from seals let mut header = Header { seal: vec![vec![].into(), vec![].into()], gas_limit: kovan_aura_config().min_gas_limit, parent_hash: genesis().hash(), ..Default::default() }; assert_eq!(default_verify(&header), Err(Error::MissingStep)); // when step is the same as for the parent block header.seal = vec![vec![42].into(), vec![].into()]; assert_eq!(default_verify(&header), Err(Error::DoubleVote)); // when step is OK header.seal = vec![vec![43].into(), vec![].into()]; assert_ne!(default_verify(&header), Err(Error::DoubleVote)); // now check with validate_step check enabled let mut config = kovan_aura_config(); config.validate_step_transition = 0; // when step is lesser that for the parent block header.seal = vec![vec![40].into(), vec![].into()]; assert_eq!(verify_with_config(&config, &header), Err(Error::DoubleVote)); // when step is OK header.seal = vec![vec![44].into(), vec![].into()]; assert_ne!(verify_with_config(&config, &header), Err(Error::DoubleVote)); } #[test] fn verifies_empty_step() { let validators = vec![validator(0), validator(1), validator(2)]; let mut config = kovan_aura_config(); config.empty_steps_transition = 0; // when empty step duplicates parent step let mut header = Header { seal: vec![ vec![45].into(), vec![142].into(), SealedEmptyStep::rlp_of(&[sealed_empty_step(&validators, &genesis().hash(), 42)]), ], gas_limit: kovan_aura_config().min_gas_limit, parent_hash: genesis().hash(), ..Default::default() }; assert_eq!(verify_with_config(&config, &header), Err(Error::InsufficientProof)); // when empty step signature check fails let mut wrong_sealed_empty_step = sealed_empty_step(&validators, &genesis().hash(), 43); wrong_sealed_empty_step.signature = Default::default(); header.seal[2] = SealedEmptyStep::rlp_of(&[wrong_sealed_empty_step]); assert_eq!(verify_with_config(&config, &header), Err(Error::InsufficientProof)); // when we are accepting strict empty steps and they come not in order config.strict_empty_steps_transition = 0; header.seal[2] = SealedEmptyStep::rlp_of(&[ sealed_empty_step(&validators, &genesis().hash(), 44), sealed_empty_step(&validators, &genesis().hash(), 43), ]); assert_eq!(verify_with_config(&config, &header), Err(Error::InsufficientProof)); // when empty steps are OK header.seal[2] = SealedEmptyStep::rlp_of(&[ sealed_empty_step(&validators, &genesis().hash(), 43), sealed_empty_step(&validators, &genesis().hash(), 44), ]); assert_ne!(verify_with_config(&config, &header), Err(Error::InsufficientProof)); } #[test] fn verifies_chain_score() { let mut config = kovan_aura_config(); config.validate_score_transition = 0; // when chain score is invalid let mut header = Header { seal: vec![vec![43].into(), vec![].into()], gas_limit: kovan_aura_config().min_gas_limit, parent_hash: genesis().hash(), ..Default::default() }; assert_eq!(verify_with_config(&config, &header), Err(Error::InvalidDifficulty)); // when chain score is accepted header.difficulty = calculate_score(42, 43, 0); assert_ne!(verify_with_config(&config, &header), Err(Error::InvalidDifficulty)); } #[test] fn verifies_validator() { let validators = vec![validator(0), validator(1), validator(2)]; let good_header = signed_header( &validators, Header { author: validators[1].address().as_fixed_bytes().into(), seal: vec![vec![43].into(), vec![].into()], gas_limit: kovan_aura_config().min_gas_limit, parent_hash: genesis().hash(), ..Default::default() }, 43, ); // when header author is invalid let mut header = good_header.clone(); header.author = Default::default(); assert_eq!(default_verify(&header), Err(Error::NotValidator)); // when header signature is invalid let mut header = good_header.clone(); header.seal[1] = rlp_encode(&H520::default()); assert_eq!(default_verify(&header), Err(Error::NotValidator)); // when everything is OK assert_eq!(default_verify(&good_header).map(|_| ()), Ok(())); } }