Skip to content

Commit

Permalink
Merge pull request #75 from datachainlab/audit_BELC5
Browse files Browse the repository at this point in the history
BELC5
  • Loading branch information
yoshidan authored Nov 27, 2024
2 parents 44ff185 + 3e72104 commit 2952ced
Show file tree
Hide file tree
Showing 7 changed files with 193 additions and 3 deletions.
8 changes: 8 additions & 0 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,11 @@ jobs:
with:
command: test
args: --release --features=std --manifest-path light-client/Cargo.toml
- uses: actions-rs/cargo@v1
name: unit-test-dev-test
with:
command: test
args: --release --features=dev --manifest-path light-client/Cargo.toml --lib test::dev_test
env:
MINIMUM_TIMESTAMP_SUPPORTED: 1731495592
MINIMUM_HEIGHT_SUPPORTED: 100
18 changes: 17 additions & 1 deletion light-client/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,24 @@ fn main() {
writeln!(
file,
"pub const BLOCKS_PER_EPOCH: u64 = {};",
blocks_per_epoch
blocks_per_epoch,
)
.unwrap();
}

{
use std::io::Write;
let mut file = std::fs::File::create("src/header/hardfork.rs").unwrap();
let minimum_time_stamp_supported =
std::env::var("MINIMUM_TIMESTAMP_SUPPORTED").unwrap_or_else(|_| "0".to_string());
let minimum_height_supported =
std::env::var("MINIMUM_HEIGHT_SUPPORTED").unwrap_or_else(|_| "0".to_string());
writeln!(
file,
"pub const MINIMUM_TIMESTAMP_SUPPORTED: u64 = {};\npub const MINIMUM_HEIGHT_SUPPORTED: u64 = {};",
minimum_time_stamp_supported,
minimum_height_supported
)
.unwrap();
}
}
76 changes: 75 additions & 1 deletion light-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ use crate::commitment::{
};
use crate::consensus_state::ConsensusState;
use crate::errors::{ClientError, Error};

use crate::header::hardfork::{MINIMUM_HEIGHT_SUPPORTED, MINIMUM_TIMESTAMP_SUPPORTED};
use crate::header::Header;
use crate::message::ClientMessage;
use crate::misbehaviour::Misbehaviour;
Expand Down Expand Up @@ -165,6 +165,14 @@ impl InnerLightClient {
let height = client_state.latest_height;
let timestamp = consensus_state.timestamp;

#[allow(clippy::absurd_extreme_comparisons)]
if timestamp.as_unix_timestamp_secs() < MINIMUM_TIMESTAMP_SUPPORTED {
return Err(Error::UnsupportedMinimumTimestamp(timestamp));
}
#[allow(clippy::absurd_extreme_comparisons)]
if height.revision_height() < MINIMUM_HEIGHT_SUPPORTED {
return Err(Error::UnsupportedMinimumHeight(height));
}
if height.revision_height() == 0 {
return Err(Error::UnexpectedRevisionHeight(height.revision_height()));
}
Expand Down Expand Up @@ -1143,4 +1151,70 @@ mod test {
fn assert_err(err: light_client::Error, contains: &str) {
assert!(format!("{:?}", err).contains(contains), "{}", err);
}

#[cfg(feature = "dev")]
mod dev_test {
use crate::client::test::MockClientReader;
use crate::client::ParliaLightClient;
use crate::client_state::ClientState;
use crate::consensus_state::ConsensusState;
use crate::header::hardfork::{MINIMUM_HEIGHT_SUPPORTED, MINIMUM_TIMESTAMP_SUPPORTED};
use crate::misc::{new_height, new_timestamp};
use hex_literal::hex;
use light_client::{types::Any, LightClient};
use std::collections::BTreeMap;

#[test]
fn test_supported_timestamp() {
let client_state = hex!("0a272f6962632e6c69676874636c69656e74732e7061726c69612e76312e436c69656e745374617465124d08381214151f3951fa218cac426edfe078fa9e5c6dcea5001a2000000000000000000000000000000000000000000000000000000000000000002205109b9ea90f2a040880a305320410c0843d").to_vec();
let consensus_state = hex!("0a2a2f6962632e6c69676874636c69656e74732e7061726c69612e76312e436f6e73656e7375735374617465126c0a2056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42110de82d5a8061a209c59cf0b5717cb6e2bd8620b7f3481605c8abcd45636bdf45c86db06338f0c5e22207a1dede35f5c835fecdc768324928cd0d9d9161e8529e1ba1e60451f3a9d088a").to_vec();
let client = ParliaLightClient::default();
let mock_consensus_state = BTreeMap::new();
let ctx = MockClientReader {
client_state: None,
consensus_state: mock_consensus_state,
};
let any_client_state: Any = client_state.try_into().unwrap();
let any_consensus_state: Any = consensus_state.try_into().unwrap();
let err = client
.create_client(&ctx, any_client_state.clone(), any_consensus_state.clone())
.unwrap_err();
assert!(
format!("{:?}", err).contains("UnsupportedMinimumTimestamp"),
"{}",
err
);
}

#[test]
fn test_supported_height() {
let client_state = hex!("0a272f6962632e6c69676874636c69656e74732e7061726c69612e76312e436c69656e745374617465124d08381214151f3951fa218cac426edfe078fa9e5c6dcea5001a2000000000000000000000000000000000000000000000000000000000000000002205109b9ea90f2a040880a305320410c0843d").to_vec();
let consensus_state = hex!("0a2a2f6962632e6c69676874636c69656e74732e7061726c69612e76312e436f6e73656e7375735374617465126c0a2056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42110de82d5a8061a209c59cf0b5717cb6e2bd8620b7f3481605c8abcd45636bdf45c86db06338f0c5e22207a1dede35f5c835fecdc768324928cd0d9d9161e8529e1ba1e60451f3a9d088a").to_vec();
let client = ParliaLightClient::default();
let mock_consensus_state = BTreeMap::new();
let ctx = MockClientReader {
client_state: None,
consensus_state: mock_consensus_state,
};
let any_client_state: Any = client_state.try_into().unwrap();
let any_consensus_state: Any = consensus_state.try_into().unwrap();

let mut client_state: ClientState = any_client_state.try_into().unwrap();
client_state.latest_height = new_height(0, MINIMUM_HEIGHT_SUPPORTED - 1);
let mut consensus_state: ConsensusState = any_consensus_state.try_into().unwrap();
consensus_state.timestamp = new_timestamp(MINIMUM_TIMESTAMP_SUPPORTED).unwrap();

let any_client_state: Any = client_state.try_into().unwrap();
let any_consensus_state: Any = consensus_state.try_into().unwrap();

let err = client
.create_client(&ctx, any_client_state.clone(), any_consensus_state.clone())
.unwrap_err();
assert!(
format!("{:?}", err).contains("UnsupportedMinimumHeight"),
"{}",
err
);
}
}
}
79 changes: 78 additions & 1 deletion light-client/src/client_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use parlia_ibc_proto::ibc::lightclients::parlia::v1::ClientState as RawClientSta
use crate::commitment::resolve_account;
use crate::consensus_state::ConsensusState;
use crate::errors::Error;
use crate::header::hardfork::{MINIMUM_HEIGHT_SUPPORTED, MINIMUM_TIMESTAMP_SUPPORTED};
use crate::header::Header;
use crate::misbehaviour::Misbehaviour;
use crate::misc::{new_height, Address, ChainId, Hash};
Expand Down Expand Up @@ -97,12 +98,23 @@ impl ClientState {
}

fn check_header(&self, now: Time, cs: &ConsensusState, header: &Header) -> Result<(), Error> {
// Ensure header has supported timestamp
let ts = header.timestamp()?;

#[allow(clippy::absurd_extreme_comparisons)]
if ts.as_unix_timestamp_secs() < MINIMUM_TIMESTAMP_SUPPORTED {
return Err(Error::UnsupportedMinimumTimestamp(ts));
}
#[allow(clippy::absurd_extreme_comparisons)]
if header.height().revision_height() < MINIMUM_HEIGHT_SUPPORTED {
return Err(Error::UnsupportedMinimumHeight(header.height()));
}
// Ensure last consensus state is within the trusting period
validate_within_trusting_period(
now,
self.trusting_period,
self.max_clock_drift,
header.timestamp()?,
ts,
cs.timestamp,
)?;

Expand Down Expand Up @@ -693,4 +705,69 @@ mod test {
panic!("expected error");
}
}
#[cfg(feature = "dev")]
mod dev_test {
use crate::client_state::ClientState;
use crate::consensus_state::ConsensusState;
use crate::errors::Error;
use crate::fixture::localnet;
use crate::header::eth_headers::ETHHeaders;
use crate::header::hardfork::{MINIMUM_HEIGHT_SUPPORTED, MINIMUM_TIMESTAMP_SUPPORTED};
use crate::header::Header;
use crate::misc::new_timestamp;
use parlia_ibc_proto::ibc::core::client::v1::Height;

#[test]
fn test_supported_timestamp() {
let header = Header::new(
vec![1],
ETHHeaders {
target: localnet().previous_epoch_header(),
all: vec![],
},
Height::default(),
localnet().previous_epoch_header().epoch.unwrap(),
localnet().epoch_header().epoch.unwrap(),
);
let cs = ClientState::default();
let cons_state = ConsensusState::default();
let err = cs
.check_header(new_timestamp(0).unwrap(), &cons_state, &header)
.unwrap_err();
match err {
Error::UnsupportedMinimumTimestamp(e1) => {
assert_eq!(e1, header.timestamp().unwrap());
}
err => unreachable!("{:?}", err),
}
}

#[test]
fn test_supported_height() {
let mut header = Header::new(
vec![1],
ETHHeaders {
target: localnet().previous_epoch_header(),
all: vec![],
},
Height::default(),
localnet().previous_epoch_header().epoch.unwrap(),
localnet().epoch_header().epoch.unwrap(),
);
header.eth_header_mut().target.timestamp = MINIMUM_TIMESTAMP_SUPPORTED;
header.eth_header_mut().target.number = MINIMUM_HEIGHT_SUPPORTED - 1;

let cs = ClientState::default();
let cons_state = ConsensusState::default();
let err = cs
.check_header(new_timestamp(0).unwrap(), &cons_state, &header)
.unwrap_err();
match err {
Error::UnsupportedMinimumHeight(e1) => {
assert_eq!(e1, header.height());
}
err => unreachable!("{:?}", err),
}
}
}
}
8 changes: 8 additions & 0 deletions light-client/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ pub enum Error {
UnexpectedValidatorsHashSize(Vec<u8>),

// Header error
UnsupportedMinimumTimestamp(Time),
UnsupportedMinimumHeight(Height),
MissingPreviousValidators(BlockNumber),
MissingCurrentValidators(BlockNumber),
OutOfTrustingPeriod(Time, Time),
Expand Down Expand Up @@ -377,6 +379,12 @@ impl core::fmt::Display for Error {
e1, e2, e3, e4
)
}
Error::UnsupportedMinimumTimestamp(e1) => {
write!(f, "UnsupportedMinimumTimestamp : {:?}", e1)
}
Error::UnsupportedMinimumHeight(e1) => {
write!(f, "UnsupportedMinimumHeight : {:?}", e1)
}
Error::UnexpectedRevisionHeight(e1) => {
write!(f, "UnexpectedRevisionHeight : {}", e1)
}
Expand Down
2 changes: 2 additions & 0 deletions light-client/src/header/hardfork.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pub const MINIMUM_TIMESTAMP_SUPPORTED: u64 = 0;
pub const MINIMUM_HEIGHT_SUPPORTED: u64 = 0;
5 changes: 5 additions & 0 deletions light-client/src/header/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ pub mod validator_set;
pub mod vote_attestation;

pub mod epoch;
pub mod hardfork;

#[derive(Clone, Debug, PartialEq)]
pub struct Header {
Expand Down Expand Up @@ -273,6 +274,10 @@ pub(crate) mod test {
&self.headers
}

pub(crate) fn eth_header_mut(&mut self) -> &mut ETHHeaders {

Check warning on line 277 in light-client/src/header/mod.rs

View workflow job for this annotation

GitHub Actions / ci

method `eth_header_mut` is never used
&mut self.headers
}

pub(crate) fn new(
account_proof: Vec<u8>,
headers: ETHHeaders,
Expand Down

0 comments on commit 2952ced

Please sign in to comment.