diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index 6281bc9..aafae80 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -53,8 +53,17 @@ jobs: name: unit-test-dev-test-pascal with: command: test - args: --release --features=dev --manifest-path light-client/Cargo.toml --lib test::dev_test_pascal + args: --release --features=dev --manifest-path light-client/Cargo.toml --lib test::dev_test_after_pascal env: MINIMUM_TIMESTAMP_SUPPORTED: 1 MINIMUM_HEIGHT_SUPPORTED: 1 - PASCAL_TIMESTAMP: 1 \ No newline at end of file + PASCAL_TIMESTAMP: 1 + - uses: actions-rs/cargo@v1 + name: unit-test-dev-test-pascal + with: + command: test + args: --release --features=dev --manifest-path light-client/Cargo.toml --lib test::dev_test_before_pascal + env: + MINIMUM_TIMESTAMP_SUPPORTED: 1 + MINIMUM_HEIGHT_SUPPORTED: 1 + PASCAL_TIMESTAMP: 1800000000 \ No newline at end of file diff --git a/light-client/src/errors.rs b/light-client/src/errors.rs index 1d49691..6e1531c 100644 --- a/light-client/src/errors.rs +++ b/light-client/src/errors.rs @@ -99,6 +99,8 @@ pub enum Error { UnexpectedCurrentValidatorsHashInEpoch(Height, Height, Hash, Hash), UnexpectedUntrustedValidators(BlockNumber, BlockNumber), MissingRequestsHash(BlockNumber), + UnexpectedRequestsHash(BlockNumber, Vec), + UnexpectedHeaderRLP(BlockNumber), // Vote attestation UnexpectedTooManyHeadersToFinalize(BlockNumber, usize), @@ -388,6 +390,12 @@ impl core::fmt::Display for Error { Error::MissingRequestsHash(e1) => { write!(f, "MissingRequestsHash : {}", e1) } + Error::UnexpectedRequestsHash(e1, e2) => { + write!(f, "UnexpectedRequestsHash : {} {:?}", e1, e2) + } + Error::UnexpectedHeaderRLP(e1) => { + write!(f, "UnexpectedHeaderRLP : {}", e1) + } } } } diff --git a/light-client/src/header/eth_header.rs b/light-client/src/header/eth_header.rs index 921f43f..c6b9919 100644 --- a/light-client/src/header/eth_header.rs +++ b/light-client/src/header/eth_header.rs @@ -421,7 +421,6 @@ impl TryFrom for ETHHeader { if nonce != EMPTY_NONCE { return Err(Error::UnexpectedNonce(number)); } - let hash: Hash = keccak_256(value.header.as_slice()); let epoch = if number % BLOCKS_PER_EPOCH == 0 { @@ -432,8 +431,21 @@ impl TryFrom for ETHHeader { }; #[allow(clippy::absurd_extreme_comparisons)] - if PASCAL_TIMESTAMP > 0 && timestamp >= PASCAL_TIMESTAMP && requests_hash.is_none() { - return Err(Error::MissingRequestsHash(number)); + if PASCAL_TIMESTAMP > 0 { + if timestamp >= PASCAL_TIMESTAMP { + if requests_hash.is_none() { + return Err(Error::MissingRequestsHash(number)); + } + // Ensure no more header element. + if rlp.try_next().is_ok() { + return Err(Error::UnexpectedHeaderRLP(number)); + } + } else if timestamp < PASCAL_TIMESTAMP && requests_hash.is_some() { + return Err(Error::UnexpectedRequestsHash( + number, + requests_hash.unwrap(), + )); + } } Ok(Self { @@ -838,7 +850,7 @@ pub(crate) mod test { } #[cfg(feature = "dev")] - mod dev_test_pascal { + mod dev_test_after_pascal { use crate::errors::Error; use crate::fixture::{decode_header, localnet}; use crate::header::eth_header::ETHHeader; @@ -847,7 +859,7 @@ pub(crate) mod test { #[test] fn test_error_missing_request_hash() { - // number = 401 + // timestamp = 1721396460 let raw_header = localnet().epoch_header_plus_1_rlp(); let raw_header = EthHeader { header: raw_header }; let result = ETHHeader::try_from(raw_header).unwrap_err(); @@ -857,10 +869,52 @@ pub(crate) mod test { } } + #[test] + fn test_error_invalid_header_rlp_length() { + let mut header = hex!("f90370a04a99d244666a287d9aaa1a81aa5bba573f156865369023eaa53a4ba8bb303ad1a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794e04db2de85453e0936b441c339a26d10cfa71b50a0d0a25a7c6b93d5d2e8f7e2075d2886fa62840f31c127b880b7cd503e2d364163a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000282071b8402625a008084678f4827b90111d883010503846765746888676f312e32332e35856c696e75780000002f5b9772f8ae0fb860959e5c417ecd8a5e5ddabd85485cf2cc4433f26beea076d77bbc6f461e4129881b8772bdae5fdd6ca927b571662ac5750d4abeca4f44a4406ab3254e0d98e6ee92b5b6396122853b45db2d18d24fb79e8397e253ca10a2a03b3b18e5961173b5f848820719a0e5ef3de482ecc3de5aea0efb17457d7edc5b1a39fc97c29cc5780b4665c9ca2082071aa04a99d244666a287d9aaa1a81aa5bba573f156865369023eaa53a4ba8bb303ad180aba9a203cbc9ac6e2eabbc44b15f7c526ec5f9d570a0addc005d5958d8415f760794e65762057ff9956dce68034d30cca6d9cc2ac3eb35f699d47c74931c470a01a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").to_vec(); + // add unnecessary data + header.push(0x80); + let raw_header = EthHeader { header }; + let result = ETHHeader::try_from(raw_header).unwrap_err(); + match result { + Error::UnexpectedHeaderRLP(_) => {} + _ => unreachable!(), + } + } + #[test] fn test_success_after_bep466() { + // timestamp=1737443367 let header = hex!("f90370a04a99d244666a287d9aaa1a81aa5bba573f156865369023eaa53a4ba8bb303ad1a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794e04db2de85453e0936b441c339a26d10cfa71b50a0d0a25a7c6b93d5d2e8f7e2075d2886fa62840f31c127b880b7cd503e2d364163a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000282071b8402625a008084678f4827b90111d883010503846765746888676f312e32332e35856c696e75780000002f5b9772f8ae0fb860959e5c417ecd8a5e5ddabd85485cf2cc4433f26beea076d77bbc6f461e4129881b8772bdae5fdd6ca927b571662ac5750d4abeca4f44a4406ab3254e0d98e6ee92b5b6396122853b45db2d18d24fb79e8397e253ca10a2a03b3b18e5961173b5f848820719a0e5ef3de482ecc3de5aea0efb17457d7edc5b1a39fc97c29cc5780b4665c9ca2082071aa04a99d244666a287d9aaa1a81aa5bba573f156865369023eaa53a4ba8bb303ad180aba9a203cbc9ac6e2eabbc44b15f7c526ec5f9d570a0addc005d5958d8415f760794e65762057ff9956dce68034d30cca6d9cc2ac3eb35f699d47c74931c470a01a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").to_vec(); decode_header(header); } } + + #[cfg(feature = "dev")] + mod dev_test_before_pascal { + use crate::errors::Error; + use crate::fixture::{decode_header, localnet}; + use crate::header::eth_header::ETHHeader; + use hex_literal::hex; + use parlia_ibc_proto::ibc::lightclients::parlia::v1::EthHeader; + + #[test] + fn test_error_request_hash() { + // timestamp=1737443367 + let header = hex!("f90370a04a99d244666a287d9aaa1a81aa5bba573f156865369023eaa53a4ba8bb303ad1a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794e04db2de85453e0936b441c339a26d10cfa71b50a0d0a25a7c6b93d5d2e8f7e2075d2886fa62840f31c127b880b7cd503e2d364163a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000282071b8402625a008084678f4827b90111d883010503846765746888676f312e32332e35856c696e75780000002f5b9772f8ae0fb860959e5c417ecd8a5e5ddabd85485cf2cc4433f26beea076d77bbc6f461e4129881b8772bdae5fdd6ca927b571662ac5750d4abeca4f44a4406ab3254e0d98e6ee92b5b6396122853b45db2d18d24fb79e8397e253ca10a2a03b3b18e5961173b5f848820719a0e5ef3de482ecc3de5aea0efb17457d7edc5b1a39fc97c29cc5780b4665c9ca2082071aa04a99d244666a287d9aaa1a81aa5bba573f156865369023eaa53a4ba8bb303ad180aba9a203cbc9ac6e2eabbc44b15f7c526ec5f9d570a0addc005d5958d8415f760794e65762057ff9956dce68034d30cca6d9cc2ac3eb35f699d47c74931c470a01a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000a0e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855").to_vec(); + let raw_header = EthHeader { header }; + let result = ETHHeader::try_from(raw_header).unwrap_err(); + match result { + Error::UnexpectedRequestsHash(_, _) => {} + _ => unreachable!(), + } + } + + #[test] + fn test_success_before_bep466() { + // timestamp=1721396460 + let raw_header = localnet().epoch_header_plus_1_rlp(); + decode_header(raw_header); + } + } }