diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index de3b7f8..45d0b19 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -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 diff --git a/SPEC.md b/SPEC.md index 302dfb8..c2db61f 100644 --- a/SPEC.md +++ b/SPEC.md @@ -140,7 +140,7 @@ interface ETHHeader { } ``` -## Misbehavior +## misbehaviour The `Misbehaviour` type is used for detecting misbehaviour and freezing the client - to prevent further packet flow - if applicable. Parlia client `Misbehaviour` consists of two headers at the same height both of which the light client would have considered valid. @@ -244,15 +244,15 @@ Primary verification according to BEP-126's finality rule involves: However, there may be cases where the VoteAttestation cannot directly determine the finality of the submitted header. In such cases, a valid descendant header is verified, which is included in the `headers` and can directly confirm its finality through VoteAttestation. -## Misbehavior predicate +## misbehaviour predicate -The predicate will check if a submission contains evidence of Misbehavior. +The predicate will check if a submission contains evidence of misbehaviour. If there are two different valid headers for the same height, the client will be frozen, preventing any further state updates. ```typescript function submitMisbehaviour( clientId: ClientId, - misbehaviour: Misbehavior + misbehaviour: misbehaviour ): ClientState { // assert heights are equal assert(misbehaviour.header1.getHeight() == misbehaviour.header2.getHeight()) diff --git a/light-client/build.rs b/light-client/build.rs index feae427..f326f41 100644 --- a/light-client/build.rs +++ b/light-client/build.rs @@ -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(); + } } diff --git a/light-client/src/client.rs b/light-client/src/client.rs index 4d064a7..60e9f68 100644 --- a/light-client/src/client.rs +++ b/light-client/src/client.rs @@ -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; @@ -165,6 +165,18 @@ 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())); + } + Ok(CreateClientResult { height, message: UpdateStateProxyMessage { @@ -189,9 +201,9 @@ impl InnerLightClient { ) -> Result { match ClientMessage::try_from(any_message.clone())? { ClientMessage::Header(header) => Ok(self.update_state(ctx, client_id, header)?.into()), - ClientMessage::Misbehaviour(misbehavior) => { + ClientMessage::Misbehaviour(misbehaviour) => { let (client_state, prev_states, context) = - self.submit_misbehaviour(ctx, client_id, misbehavior)?; + self.submit_misbehaviour(ctx, client_id, misbehaviour)?; Ok(MisbehaviourData { new_any_client_state: client_state.try_into()?, message: MisbehaviourProxyMessage { @@ -559,7 +571,7 @@ mod test { fn test_success_create_client() { let client_state = hex!("0a272f6962632e6c69676874636c69656e74732e7061726c69612e76312e436c69656e745374617465124d08381214151f3951fa218cac426edfe078fa9e5c6dcea5001a2000000000000000000000000000000000000000000000000000000000000000002205109b9ea90f2a040880a305320410c0843d").to_vec(); let consensus_state = hex!("0a2a2f6962632e6c69676874636c69656e74732e7061726c69612e76312e436f6e73656e7375735374617465126c0a2056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42110de82d5a8061a209c59cf0b5717cb6e2bd8620b7f3481605c8abcd45636bdf45c86db06338f0c5e22207a1dede35f5c835fecdc768324928cd0d9d9161e8529e1ba1e60451f3a9d088a").to_vec(); - let client = ParliaLightClient::default(); + let client = ParliaLightClient; let mock_consensus_state = BTreeMap::new(); let ctx = MockClientReader { client_state: None, @@ -590,6 +602,26 @@ mod test { _ => unreachable!("invalid commitment"), } } + #[test] + fn test_error_create_client() { + 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 mut any_client_state: Any = client_state.try_into().unwrap(); + let mut client_state = ClientState::try_from(any_client_state.clone()).unwrap(); + client_state.latest_height = Height::new(0, 0); + any_client_state = client_state.try_into().unwrap(); + let any_consensus_state: Any = consensus_state.try_into().unwrap(); + let result = client + .create_client(&ctx, any_client_state.clone(), any_consensus_state.clone()) + .unwrap_err(); + assert_err(result, "UnexpectedRevisionHeight"); + } #[rstest] #[case::localnet(localnet())] @@ -641,7 +673,7 @@ mod test { ) { let any: Any = header.try_into().unwrap(); let header = Header::try_from(any.clone()).unwrap(); - let client = ParliaLightClient::default(); + let client = ParliaLightClient; let client_id = ClientId::new(&client.client_type(), 1).unwrap(); let mut mock_consensus_state = BTreeMap::new(); let trusted_cs = ConsensusState { @@ -711,7 +743,7 @@ mod test { #[rstest] #[case::localnet(localnet())] fn test_success_update_state_continuous(#[case] hp: Box) { - let client = ParliaLightClient::default(); + let client = ParliaLightClient; let client_id = ClientId::new(&client.client_type(), 1).unwrap(); let header_groups = hp.success_update_client_continuous_input(); @@ -768,7 +800,7 @@ mod test { let header = input.header; let any: Any = header.try_into().unwrap(); - let client = ParliaLightClient::default(); + let client = ParliaLightClient; let client_id = ClientId::new(&client.client_type(), 1).unwrap(); let mut mock_consensus_state = BTreeMap::new(); @@ -845,7 +877,7 @@ mod test { let header = Header::try_from(input.clone()).unwrap(); let trusted_height = header.trusted_height(); - let client = ParliaLightClient::default(); + let client = ParliaLightClient; let client_id = ClientId::new(&client.client_type(), 1).unwrap(); let mut mock_consensus_state = BTreeMap::new(); mock_consensus_state.insert(trusted_height, ConsensusState::default()); @@ -945,7 +977,7 @@ mod test { latest_height: Height, frozen: bool, ) -> Result { - let client = ParliaLightClient::default(); + let client = ParliaLightClient; let client_id = ClientId::new(client.client_type().as_str(), 0).unwrap(); let mut mock_consensus_state = BTreeMap::new(); mock_consensus_state.insert( @@ -975,29 +1007,29 @@ mod test { } #[test] - fn test_success_submit_misbehavior() { - let client = ParliaLightClient::default(); + fn test_success_submit_misbehaviour() { + let client = ParliaLightClient; let client_id = ClientId::new(client.client_type().as_str(), 1).unwrap(); - // Detect misbehavior + // Detect misbehaviour // Use blocks of two local nets with the same ChainID(=9999) and validator set. let any = hex!("0a282f6962632e6c69676874636c69656e74732e7061726c69612e76312e4d69736265686176696f757212ad3b0a0b78782d7061726c69612d3112c11c0ad5060ad206f9034fa0df813ab13cd8fa64526bab2d574bd83199f358cf875b5a7ca34c55cc59fb7841a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794a7876ea32e7a748c697d01345145485561305b24a0347db7085bed92889205f4566fdeb71b36981b2662282e5b49f74f146e1b2920a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000282022d8402625a00808466a21174b90111d98301040c846765746889676f312e32312e3132856c696e7578000060b2e37bf8ae03b860b42b097ae5430552430062d80b2015710882c0520cf58dc6b987d0aa042840525bab13a5098af712b844bccf36b2ccb009fa3796f7088dec9a5dfcc0db6e4ab502fcca21d59598bd4f7504415662ec094b6ba1555c7faa0389468e45e11e8655f84882022ba0fe631870634a3823f363cdb9c6bd572b1a3ecaa9daeb98a784610a1cd1107e4882022ca0df813ab13cd8fa64526bab2d574bd83199f358cf875b5a7ca34c55cc59fb78418008487b8053f33e79d8758655252a23e25ce9f3440557d0ed67c80700633730674c5c682fa86414f136befd54de6023cbbb74bc48edc438ad136bc34f520b264100a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a000000000000000000000000000000000000000000000000000000000000000000ad5060ad206f9034fa09e0419600f424df6786dbbdef3f774c0a3d9a9785c8e3aec85ec4d95a2a526ada01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794a7876ea32e7a748c697d01345145485561305b24a0347db7085bed92889205f4566fdeb71b36981b2662282e5b49f74f146e1b2920a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000282022e8402625a00808466a21177b90111d98301040c846765746889676f312e32312e3132856c696e7578000060b2e37bf8ae03b86099480987f69ba68b91ea25863b7cc7881fa57156c57f04b9a1ce33fa6766522d1ddafbb4b0ebd913bedee575cf18790f163d68c5d8fc5d389206ec1baf04b7daf1b17c9b5e688f29e71bfc5c40c5327dd6a36a7fd6e043305f6fe01da6fc77bff84882022ca0df813ab13cd8fa64526bab2d574bd83199f358cf875b5a7ca34c55cc59fb784182022da09e0419600f424df6786dbbdef3f774c0a3d9a9785c8e3aec85ec4d95a2a526ad80aab31b654039b4edef9af34f1bfbff4999aafd9c007bb3925378e865a8fde7d9693646834fc0323d375029594e363a4d96d49d0d5bc1dd6d98ed87d13fcfc4a700a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a000000000000000000000000000000000000000000000000000000000000000000ad5060ad206f9034fa0d71391dfe369c3f939c86aa6c3a25649a5ce9c298549e075981d65dc3278fadda01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794a7876ea32e7a748c697d01345145485561305b24a0347db7085bed92889205f4566fdeb71b36981b2662282e5b49f74f146e1b2920a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000282022f8402625a00808466a2117ab90111d98301040c846765746889676f312e32312e3132856c696e7578000060b2e37bf8ae03b860acd5509943efb944b23870db31f09b536731993d75078450bedefa5df77ea416d2d54be798d452d4562b57571fcf3ec00596cb4516d86b82a52081275c9ff9a35aed899b63791acbff7000f3c907c9d9f61e386c8c0de5ea79d67fd49c5675abf84882022da09e0419600f424df6786dbbdef3f774c0a3d9a9785c8e3aec85ec4d95a2a526ad82022ea0d71391dfe369c3f939c86aa6c3a25649a5ce9c298549e075981d65dc3278fadd8076a78528fffc86138b8155eaf036c0ccced82091d1cd7be5e7445d9d44b512907766334023ba1ac6b5376b39619e7198467598e549b284dcd065b4088ee0be0600a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000120310ac041a9506f90312f901f1a094e7dcd05ddcc3923085b451330f3aa5ce5a628d6685506d99cb09b3aef0e11ea0cb21354718f7d012561dacf2274405cdd9bccd532afe18206bd26bf60e96c853a0b116ef7733a93eed23f018027c116e60436a228a9f9173bb9b0c40eb71216da6a09e8d71308bd38616ac52a39498cee9fcf29c440d9aacfedfc7143772c77ce54480a0a74e307420bf9966d5db9a83ce48edc32c7a8e43328c6373de6875cefa04ad7aa004371241b9d6f35e1f361f2109d19a9192a9c0c749b2bd15fcb131a1a6ce5e3ba00577e3e2c4649c5a23cbdabe0bbfed7cdf6e85c136d84d58127cdec86264ad6ea070c0f30031f40c8017dbfc2ef008e6a3aae2e3105a654e0c5439b6104752882ea08f81903ec8515875682785142e1f92bdeaf65fccd5d0cf78b1ff2905a07e5883a03052420ba2d24a04d3f830584d3dbd6907b6d82bab84ddd806d03470e2c9d51ca06fb1a1498c2c8f93944a4f672ff4e982480ad181c835c0d8078159c517c7977aa013a426820f7b7249edc97cc5c002e653ac84b437f3ac12ac940c3d4b09e09827a09fbc54eac488b27315b09a1afa8d12f168e4c4cb5aea2d9a6ab5e7266da2f7e8a077c5e5cd5bd518bc509ee5e71790f1e42e492e23875b097e565cff8e809e7c8aa0d4182165b298d7b52b0f2064d19ab8bdad2b3955fa5b3d85c00673beb97e124480f8b18080a0dc77b6ae50b675036e77b31973c79ec60c28c0d2c57b03ad99c2acfff2f0cd4e80a03f47312ca98ac1f6ae9db9752c2a33529723b64e654d87f7847cee0382edfb55a05e0f116451aaa1baab3f3abff2793c8318050eeed6bf62d464d343a11d86eb2880808080808080a0abbb1987d09a71106f586030d1ab913bae0008e2a7dec0d08f2d60cd30fb2ac8a096c706907bfc6472dd88315cb8e21ee6f60a661cd8050065e2ba387023ee96858080f869a020b1e2b1f9852058ee0aaadca3c963f77f6483a1a51c644d79386bcada360583b846f8440180a0e39304f0ec064a98e4b0a96432dfb0a9e4c7fd0f26a6bbcf9c75bff68c51a7a9a0b3d632130dcb5cb583b47ec0623e59ca3703e6e2564f144272b597f3e3511ba822448fdaaa7e6631e438625ca25c857a3727ea28e56592b50b17b5348f1aebe4c50d87c59b55223abe57c9c3df76cb4a96211bb393aacd440bac5f71ea3fc481abff6270142d2244a7876ea32e7a748c697d01345145485561305b24aed19f6cbeb659e104b4657c2e41822087506cb58b037aba5599314c2c4bea0ef4d3cbbc449b24abf396cfd71ac07af42a448fdaaa7e6631e438625ca25c857a3727ea28e56592b50b17b5348f1aebe4c50d87c59b55223abe57c9c3df76cb4a96211bb393aacd440bac5f71ea3fc481abff6270142d2a44d9a13701eafb76870cb220843b8c6476824bfa1595bd9f541fdb277d2bdd5ff223f95eb83f8d2b465c1bac3cf2d0bc551ad01a9037a4af083d66eff89b34200745db92cc300538061ad91e0ad5060ad206f9034fa0aeef6b8a15e0109c9838875b033b5c54d21c356cb5ddf395eb109b9b29ae694fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794a7876ea32e7a748c697d01345145485561305b24a03e5bfca057d64cd5c54c82f5e213f8e7993411a5a490c0b93358f6def507e8d4a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000282022d8402625a00808466a2116eb90111d98301040c846765746889676f312e32312e3132856c696e75780000e0299c2ff8ae0fb86082b910df7cee75733375fb7716c34b14c1ce27f283c2d87ea0e2b2a0de42d18bdd724766b3a73d5daaa25befaa814df815605e8881819d421c8703100b7a93d1a22c89693625b4370d4d87b85ed676661ce65d46ef0c5c8325696892abf0d5d5f84882022ba03986751eb813b176c13e29485332e635df862b515516501e87113f50b655d80c82022ca0aeef6b8a15e0109c9838875b033b5c54d21c356cb5ddf395eb109b9b29ae694f80af43c5cc71b487cbe16b6771635dc751cbd6231274a3bbf87ae8b38c3319e5f54eb4e0a8186070727a19eb749b2e70068b7c505b1ed8b0dd7d6a595e2bf5db3601a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a000000000000000000000000000000000000000000000000000000000000000000ad5060ad206f9034fa04a9481f17c19c739ab1961ffdba488b234d37254da0ec7e95d8cf5d4ad54893ba01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794a7876ea32e7a748c697d01345145485561305b24a03e5bfca057d64cd5c54c82f5e213f8e7993411a5a490c0b93358f6def507e8d4a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000282022e8402625a00808466a21171b90111d98301040c846765746889676f312e32312e3132856c696e75780000e0299c2ff8ae0fb860a41a9347a1766d7b59a6f4eb4434f89a5ff5e52df18b90baf4588374a65b578fdf85c4c605b325468883f22a49ce28bf1194955ae44b6de878a8def6b4c36cd5bb669be37d4a31bb7b48e45c7e0fe0d8fd43e86ba60871b6e1b7c731498d0df4f84882022ca0aeef6b8a15e0109c9838875b033b5c54d21c356cb5ddf395eb109b9b29ae694f82022da04a9481f17c19c739ab1961ffdba488b234d37254da0ec7e95d8cf5d4ad54893b80292058b3eeab2626c29d676db526a6a0d8b739a59f9d7971e5fd3e96192321e4759aa81a6bfdd5a211e3dca1d29f20fc5feffdb3261fb2afdd2fd9b4f9607fb201a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a000000000000000000000000000000000000000000000000000000000000000000ad5060ad206f9034fa04a6b366f150065827cf5954904fdca9da5073531b4aa5462e86636feb61890aca01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794a7876ea32e7a748c697d01345145485561305b24a03e5bfca057d64cd5c54c82f5e213f8e7993411a5a490c0b93358f6def507e8d4a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b90100000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000282022f8402625a00808466a21174b90111d98301040c846765746889676f312e32312e3132856c696e75780000e0299c2ff8ae0fb860a440c540974a6217ff340cbe742520fce50a18f18553f4816fdb43d9a74092ebb237ce4cce2a10f0e6d7b6ec58cf53990b91b8d2279eec1391748eab3a605cf1d900fa267dfc21cc8ebf35cb138c10287d49b3a4c1c5d18bd7727f74fa64e46ef84882022da04a9481f17c19c739ab1961ffdba488b234d37254da0ec7e95d8cf5d4ad54893b82022ea04a6b366f150065827cf5954904fdca9da5073531b4aa5462e86636feb61890ac806014de9b2c697f9b78ac9c86cdcc45bd19b3388569814a24bcbbe570a47561fd713af78490598f2d211f13cf1a5ed4bf299c1c1df8c677bd018b695c9f8339bf01a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000120310ab041a9506f90312f901f1a094e7dcd05ddcc3923085b451330f3aa5ce5a628d6685506d99cb09b3aef0e11ea0d1c5c078d5c2983649e8200a84d8fef4d4c1ca8a06ed8f7be21f82c23a910edaa0b116ef7733a93eed23f018027c116e60436a228a9f9173bb9b0c40eb71216da6a0f9858d51200cb595d4f158c848af32e29fa61db4ddea7cde4006a7cbc306ef5880a03dc9ef066e7332efc09e4f620aab588d5465c1ed3b813c912928f1ffa44503a6a0a4aa3b0d5510f907876be1bff95c5867b088e8464e4ae75e45d016c5ac21c0faa00577e3e2c4649c5a23cbdabe0bbfed7cdf6e85c136d84d58127cdec86264ad6ea070c0f30031f40c8017dbfc2ef008e6a3aae2e3105a654e0c5439b6104752882ea08f81903ec8515875682785142e1f92bdeaf65fccd5d0cf78b1ff2905a07e5883a03052420ba2d24a04d3f830584d3dbd6907b6d82bab84ddd806d03470e2c9d51ca06fb1a1498c2c8f93944a4f672ff4e982480ad181c835c0d8078159c517c7977aa013a426820f7b7249edc97cc5c002e653ac84b437f3ac12ac940c3d4b09e09827a09fbc54eac488b27315b09a1afa8d12f168e4c4cb5aea2d9a6ab5e7266da2f7e8a077c5e5cd5bd518bc509ee5e71790f1e42e492e23875b097e565cff8e809e7c8aa076094ead9021f5472bb12b03aeb521e09d71029dfa5dac90afa7cb44cbdfc5bd80f8b18080a0dc77b6ae50b675036e77b31973c79ec60c28c0d2c57b03ad99c2acfff2f0cd4e80a0579a97aa89dbeb98396792baa31c4a7ea8e2f41da084140f07bb4bd655b72dd5a05e0f116451aaa1baab3f3abff2793c8318050eeed6bf62d464d343a11d86eb2880808080808080a0abbb1987d09a71106f586030d1ab913bae0008e2a7dec0d08f2d60cd30fb2ac8a096c706907bfc6472dd88315cb8e21ee6f60a661cd8050065e2ba387023ee96858080f869a020b1e2b1f9852058ee0aaadca3c963f77f6483a1a51c644d79386bcada360583b846f8440180a0e39304f0ec064a98e4b0a96432dfb0a9e4c7fd0f26a6bbcf9c75bff68c51a7a9a0b3d632130dcb5cb583b47ec0623e59ca3703e6e2564f144272b597f3e3511ba822448fdaaa7e6631e438625ca25c857a3727ea28e565a8c9cbefc0c78b71cdf18eeb8206601e665778faa6b4dd23740f8dd8eeb0c7bcc8f49a1c900396aee02d1639ab7897ff2244a7876ea32e7a748c697d01345145485561305b24b3f0af694696c1b453b1174a1ea8c1135c247eb168864c9731254bc2f28077f99fe195b477ee699d79f40468aa30c2b62244b2e42bc54d19116d2348ac83461e2e0915d508adaf6af61cafa835c72fe6e9feb717dd4ddf95424e29b6384bd27c0d6c9c616d15cf7fdf0f2f9fc1a4fa9197251b196be32244e04db2de85453e0936b441c339a26d10cfa71b50996157a84dd67ec797a9ea61e4023c01d5bb28cce995c4c8329900b7b96e5402c02696f6f7b62972af4ee81f3f58afc12a448fdaaa7e6631e438625ca25c857a3727ea28e565a8c9cbefc0c78b71cdf18eeb8206601e665778faa6b4dd23740f8dd8eeb0c7bcc8f49a1c900396aee02d1639ab7897ff2a44b2e42bc54d19116d2348ac83461e2e0915d508adaf6af61cafa835c72fe6e9feb717dd4ddf95424e29b6384bd27c0d6c9c616d15cf7fdf0f2f9fc1a4fa9197251b196be32a44d9a13701eafb76870cb220843b8c6476824bfa15b822666ed599d40aa3bc13feb3ca5d5bc9d3ebe23d3460078c755a5ddbd85a0a58a217860fb0ea675b708a82b288f4b32a44e04db2de85453e0936b441c339a26d10cfa71b50996157a84dd67ec797a9ea61e4023c01d5bb28cce995c4c8329900b7b96e5402c02696f6f7b62972af4ee81f3f58afc130083808").to_vec(); let any: Any = any.try_into().unwrap(); - let misbehavior = Misbehaviour::try_from(any.clone()).unwrap(); + let misbehaviour = Misbehaviour::try_from(any.clone()).unwrap(); let mut mock_consensus_state = BTreeMap::new(); mock_consensus_state.insert( - misbehavior.header_1.trusted_height(), + misbehaviour.header_1.trusted_height(), ConsensusState { - current_validators_hash: misbehavior.header_1.current_epoch_validators_hash(), - previous_validators_hash: misbehavior.header_1.previous_epoch_validators_hash(), + current_validators_hash: misbehaviour.header_1.current_epoch_validators_hash(), + previous_validators_hash: misbehaviour.header_1.previous_epoch_validators_hash(), ..Default::default() }, ); mock_consensus_state.insert( - misbehavior.header_2.trusted_height(), + misbehaviour.header_2.trusted_height(), ConsensusState { - current_validators_hash: misbehavior.header_2.current_epoch_validators_hash(), - previous_validators_hash: misbehavior.header_2.previous_epoch_validators_hash(), + current_validators_hash: misbehaviour.header_2.current_epoch_validators_hash(), + previous_validators_hash: misbehaviour.header_2.previous_epoch_validators_hash(), ..Default::default() }, ); @@ -1014,8 +1046,8 @@ mod test { let context = mdt.message.context; assert!(expected_cs.frozen); assert_eq!(prev_state.len(), 2); - assert_eq!(prev_state[0].height, misbehavior.header_1.trusted_height()); - assert_eq!(prev_state[1].height, misbehavior.header_2.trusted_height()); + assert_eq!(prev_state[0].height, misbehaviour.header_1.trusted_height()); + assert_eq!(prev_state[1].height, misbehaviour.header_2.trusted_height()); if let ValidationContext::Empty = context { unreachable!("invalid validation context"); } @@ -1024,22 +1056,25 @@ mod test { }; // assert fixture validity - assert_eq!(misbehavior.client_id, client_id); - assert_eq!(misbehavior.header_2.height(), misbehavior.header_1.height()); + assert_eq!(misbehaviour.client_id, client_id); + assert_eq!( + misbehaviour.header_2.height(), + misbehaviour.header_1.height() + ); assert_ne!( - misbehavior.header_2.block_hash(), - misbehavior.header_1.block_hash() + misbehaviour.header_2.block_hash(), + misbehaviour.header_1.block_hash() ); } #[test] - fn test_error_submit_misbehavior() { + fn test_error_submit_misbehaviour() { let ctx = MockClientReader { client_state: Some(ClientState::default()), consensus_state: BTreeMap::new(), }; - let client = ParliaLightClient::default(); + let client = ParliaLightClient; let client_id = ClientId::new(client.client_type().as_str(), 1).unwrap(); // fail: exactly same block @@ -1047,7 +1082,7 @@ mod test { let any2= hex!("7c8f289cda6dcbc5edbb26400e1306c2de06f52a9a583dab99300138011ade1b0ab4060ab106f9032ea0c3ca2cd851054700e22f8313521399aacfcc225541cde7dfeca894f9b57d7c22a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948fdaaa7e6631e438625ca25c857a3727ea28e565a0d269e8b47a345f3a77343f46943f78480a96ff82a8466963fb4a29c0e2b66091a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002820c1c8402625a0080846699bcdab90111d98301040b846765746889676f312e32312e3132856c696e75780000ffcc693ef8ae03b86086dcdfb683f4965de8062063dd843775eeb55a1271b317a6d65ef7aadabb1e10a9327c9face4c69c331b914f3b24aeb4010b30d4c4a7007c67d7de812e9c7cca598c9b3b1e94679e90aeb94b790b19dce6bee8662126eecdb3b539efee6468b7f848820c1aa049430f5e612a0b37a9bab1b1c5003f02f3586a4e2ea9e84d8de511f879f30561820c1ba0c3ca2cd851054700e22f8313521399aacfcc225541cde7dfeca894f9b57d7c22803135a7c114e967442e0bc0996b3b083aae7638e142b3a145b8d966267fecb84141fcf58023341e1aeaf333bfe4d91349db198c157947780f46b1d476a892c25900a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42180800ab4060ab106f9032ea02c07474042f3b317665fe4a233454a1ec921770d6429dd3eb0a23e812429d595a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794a7876ea32e7a748c697d01345145485561305b24a0d269e8b47a345f3a77343f46943f78480a96ff82a8466963fb4a29c0e2b66091a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002820c1d8402625a0080846699bcddb90111d98301040b846765746889676f312e32312e3132856c696e75780000ffcc693ef8ae03b860a410689613510c2087d762d49d42acb83f84b136e417e0567c8fbd693fd25cb36f794ae231b79101b94d700457f511120b7b2c8c0294c33b2d9728fa5d0a81a9c02901e28c3df1c840e7a4f7f6b5f150c3d7276b84a5f05d57a582ac6659692ff848820c1ba0c3ca2cd851054700e22f8313521399aacfcc225541cde7dfeca894f9b57d7c22820c1ca02c07474042f3b317665fe4a233454a1ec921770d6429dd3eb0a23e812429d59580dfca46ba06e40ca0191deacc05e2185acbfb04ca3ce23355f2b3d1be19667d096f33e5174260e4e5be857a49307dcf824f1b7e353c3be3e7bb4202c5bf4ae32b00a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42180800ab4060ab106f9032ea0c0e82c0cda470def9ae9b03f55755b9cf0af015ae255bbb90a7969296918514aa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347948fdaaa7e6631e438625ca25c857a3727ea28e565a0d269e8b47a345f3a77343f46943f78480a96ff82a8466963fb4a29c0e2b66091a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002820c1e8402625a0080846699bce0b90111d98301040b846765746889676f312e32312e3132856c696e75780000ffcc693ef8ae03b860856d586a1618c0da1e906937b0a4c2596598a2d34e5cbf62063fc52ca9e5078fc962443b3a53166fabf69548b8d885ec08260a0d01293a3e61ba14d82e976f477f6ab2db2a8ae7c641cca0077c1770f472e7306ab43f8fecf762a95bc45ca1a5f848820c1ca02c07474042f3b317665fe4a233454a1ec921770d6429dd3eb0a23e812429d595820c1da0c0e82c0cda470def9ae9b03f55755b9cf0af015ae255bbb90a7969296918514a802a8efd919b1fe8bd679c8db7976de46350cfe3c2bdf67ec67e95c2f1c02909736bbb1bcdd798058072adc2e3cea6a16d519d1630e121539b8ccd77b1a7f84a1c00a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42180801203109b181a9506f90312f901f1a094e7dcd05ddcc3923085b451330f3aa5ce5a628d6685506d99cb09b3aef0e11ea0d8a8f0aaba741477e7d0c15b6a1759b3d842e346b35afd14219ecdd18188718ea0b116ef7733a93eed23f018027c116e60436a228a9f9173bb9b0c40eb71216da6a0bd63cc1cadd9ef7ffd41bb496484bff7c6ddbc3b29cf1a8b82d1f8f12bc6347680a0a74e307420bf9966d5db9a83ce48edc32c7a8e43328c6373de6875cefa04ad7aa0e502f113ade32bee3770b5a0117fa799c8812e5ab5a239e3878660c59c3537fda00577e3e2c4649c5a23cbdabe0bbfed7cdf6e85c136d84d58127cdec86264ad6ea070c0f30031f40c8017dbfc2ef008e6a3aae2e3105a654e0c5439b6104752882ea08f81903ec8515875682785142e1f92bdeaf65fccd5d0cf78b1ff2905a07e5883a03052420ba2d24a04d3f830584d3dbd6907b6d82bab84ddd806d03470e2c9d51ca06fb1a1498c2c8f93944a4f672ff4e982480ad181c835c0d8078159c517c7977aa013a426820f7b7249edc97cc5c002e653ac84b437f3ac12ac940c3d4b09e09827a09fbc54eac488b27315b09a1afa8d12f168e4c4cb5aea2d9a6ab5e7266da2f7e8a077c5e5cd5bd518bc509ee5e71790f1e42e492e23875b097e565cff8e809e7c8aa0d4182165b298d7b52b0f2064d19ab8bdad2b3955fa5b3d85c00673beb97e124480f8b18080a0dc77b6ae50b675036e77b31973c79ec60c28c0d2c57b03ad99c2acfff2f0cd4e80a03f47312ca98ac1f6ae9db9752c2a33529723b64e654d87f7847cee0382edfb55a05e0f116451aaa1baab3f3abff2793c8318050eeed6bf62d464d343a11d86eb2880808080808080a0abbb1987d09a71106f586030d1ab913bae0008e2a7dec0d08f2d60cd30fb2ac8a096c706907bfc6472dd88315cb8e21ee6f60a661cd8050065e2ba387023ee96858080f869a020b1e2b1f9852058ee0aaadca3c963f77f6483a1a51c644d79386bcada360583b846f8440180a0e39304f0ec064a98e4b0a96432dfb0a9e4c7fd0f26a6bbcf9c75bff68c51a7a9a0b3d632130dcb5cb583b47ec0623e59ca3703e6e2564f144272b597f3e3511ba822448fdaaa7e6631e438625ca25c857a3727ea28e56593428ee663799df81ea82bc8445a7d93c891ef324b5f4438eb766bcf75fc405fb79d3f618fcdd17f107b374368ef512f2244a7876ea32e7a748c697d01345145485561305b24903201f874819815e1a3f183c4addc814b71ec0e573e07f79ee9082926d82dd1711d6c45a9cc8841916d8563c2f80baa2a448fdaaa7e6631e438625ca25c857a3727ea28e56593428ee663799df81ea82bc8445a7d93c891ef324b5f4438eb766bcf75fc405fb79d3f618fcdd17f107b374368ef512f2a44d9a13701eafb76870cb220843b8c6476824bfa15a82968379b116362f75bdb7cc4be8ca0ceea7c0f2e74be7c8f289cda6dcbc5edbb26400e1306c2de06f52a9a583dab9930013801").to_vec(); any.extend(any2); let any: Any = any.try_into().unwrap(); - // check if misbehavior + // check if misbehaviour let err = client .update_client(&ctx, client_id.clone(), any) .unwrap_err(); @@ -1077,7 +1112,7 @@ mod test { let any2 = hex!("f79ee9082926d82dd1711d6c45a9cc8841916d8563c2f80baa300138011ade1b0ab4060ab106f9032ea0268b5cdf7f6414cb54b00626c619892e5f3b693d568d546ff76198667f9e487ba01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794d9a13701eafb76870cb220843b8c6476824bfa15a00000000000000000000000000000000000000000000000000000000000000000a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002820d1b8402625a0080846699bfd7b90111d98301040b846765746889676f312e32312e3132856c696e75780000ffcc693ef8ae03b860b0b171c0b05dca088e0a163582339da1076da795a317c65f77441585d1446fda6692ce27df7b2b01ef2d419c2f8eb2f50b4e2f5856d9fb438f073c3e0aea72930333a6775d59096ce7560320504bb9bb4b17725edc5221491a3ac0bf64ec1f7ff848820d19a0e73c8182c6ff44e893b468600184209f9ffc5017be041533385658b98ee5675a820d1aa0268b5cdf7f6414cb54b00626c619892e5f3b693d568d546ff76198667f9e487b80649d4b1ea49d34532d24281cff713c1a1ffa9ad10ceaae118d02b19ec8a57bb4137e8ec449a3798de7cbec3a22d43d1377c84f045550e73e7100bb377df2753f01a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42180800ab4060ab106f9032ea018b177b16b907b473a20b82a7c61d76f505a1f740c540e950f93c4210319a33ba01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794a7876ea32e7a748c697d01345145485561305b24a0225560657909c76bbd1111cfa8a11f7ade19c80a315f013a920031ce76562e3ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002820d1c8402625a0080846699bfdab90111d98301040b846765746889676f312e32312e3132856c696e75780000ffcc693ef8ae03b86083fcfb7f3c9d99995d330e6b953282f6c349af68cd4c523e1d635aa51ec49a2db651d824974ab9e7cf254e7e1b7e15cb0611ceb6adda365b8ee54d04c222b7f64dcacf94b4eb61671653396e1fe8840737444731d6c8d144d553a757e2327825f848820d1aa0268b5cdf7f6414cb54b00626c619892e5f3b693d568d546ff76198667f9e487b820d1ba018b177b16b907b473a20b82a7c61d76f505a1f740c540e950f93c4210319a33b80ddc0dfa730aa9a6de6089c20d1728803b04b4d7872a2b01c90e7cad663379a8a3f8676574fb5341ec662c890cae04fed92eaed9689c3b0c5421ea85fc3e42b5801a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42180800ab4060ab106f9032ea0220755141e26b1c68c8f887b41172aa93e3267f9f0ae7b49cfd1c7002558bcb4a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794d9a13701eafb76870cb220843b8c6476824bfa15a0225560657909c76bbd1111cfa8a11f7ade19c80a315f013a920031ce76562e3ea056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b901000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002820d1d8402625a0080846699bfddb90111d98301040b846765746889676f312e32312e3132856c696e75780000ffcc693ef8ae03b860af867e0589e50a2571bfdec447fae755aade11b113b67c7928ef8bd3225a6aa29a0e29b7ab24d4952ebcbfd320258a41071e5875b547f4265c7eeb45ab4989294328db719d94157619bca873e451c3709447ecb0fa349df1084da580bb46a425f848820d1ba018b177b16b907b473a20b82a7c61d76f505a1f740c540e950f93c4210319a33b820d1ca0220755141e26b1c68c8f887b41172aa93e3267f9f0ae7b49cfd1c7002558bcb4804613f2a8116641990a7d1ce90f946e079891aa1d7904c01e078df492f1ef4dff120465d060478da7542c7cd65621d68d47c35e20482385ca5af9d58329eeac1e00a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b42180801203109a1a1a9506f90312f901f1a094e7dcd05ddcc3923085b451330f3aa5ce5a628d6685506d99cb09b3aef0e11ea0eec2a9e5f0342238c9790975e393915d8ef68ade66edfeb220210a2b13a89464a0b116ef7733a93eed23f018027c116e60436a228a9f9173bb9b0c40eb71216da6a08851197273e914fa4bdd7b07c2fec3f243a63d2298ed39c2b0a78c4f7da9ddd880a0a74e307420bf9966d5db9a83ce48edc32c7a8e43328c6373de6875cefa04ad7aa0e502f113ade32bee3770b5a0117fa799c8812e5ab5a239e3878660c59c3537fda00577e3e2c4649c5a23cbdabe0bbfed7cdf6e85c136d84d58127cdec86264ad6ea070c0f30031f40c8017dbfc2ef008e6a3aae2e3105a654e0c5439b6104752882ea08f81903ec8515875682785142e1f92bdeaf65fccd5d0cf78b1ff2905a07e5883a03052420ba2d24a04d3f830584d3dbd6907b6d82bab84ddd806d03470e2c9d51ca06fb1a1498c2c8f93944a4f672ff4e982480ad181c835c0d8078159c517c7977aa013a426820f7b7249edc97cc5c002e653ac84b437f3ac12ac940c3d4b09e09827a09fbc54eac488b27315b09a1afa8d12f168e4c4cb5aea2d9a6ab5e7266da2f7e8a077c5e5cd5bd518bc509ee5e71790f1e42e492e23875b097e565cff8e809e7c8aa0d4182165b298d7b52b0f2064d19ab8bdad2b3955fa5b3d85c00673beb97e124480f8b18080a0dc77b6ae50b675036e77b31973c79ec60c28c0d2c57b03ad99c2acfff2f0cd4e80a03f47312ca98ac1f6ae9db9752c2a33529723b64e654d87f7847cee0382edfb55a05e0f116451aaa1baab3f3abff2793c8318050eeed6bf62d464d343a11d86eb2880808080808080a0abbb1987d09a71106f586030d1ab913bae0008e2a7dec0d08f2d60cd30fb2ac8a096c706907bfc6472dd88315cb8e21ee6f60a661cd8050065e2ba387023ee96858080f869a020b1e2b1f9852058ee0aaadca3c963f77f6483a1a51c644d79386bcada360583b846f8440180a0e39304f0ec064a98e4b0a96432dfb0a9e4c7fd0f26a6bbcf9c75bff68c51a7a9a0b3d632130dcb5cb583b47ec0623e59ca3703e6e2564f144272b597f3e3511ba82244a7876ea32e7a748c697d01345145485561305b24903201f874819815e1a3f183c4addc814b71ec0e573e07f79ee9082926d82dd1711d6c45a9cc8841916d8563c2f80baa2244d9a13701eafb76870cb220843b8c6476824bfa15a82968379b116362f75bdb7cc4be8ca0ceea7c0f2e74be7c8f289cda6dcbc5edbb26400e1306c2de06f52a9a583dab992a448fdaaa7e6631e438625ca25c857a3727ea28e56593428ee663799df81ea82bc8445a7d93c891ef324b5f4438eb766bcf75fc405fb79d3f618fcdd17f107b374368ef512f2a44a7876ea32e7a748c697d01345145485561305b24903201f874819815e1a3f183c4addc814b71ec0e573e07f79ee9082926d82dd1711d6c45a9cc8841916d8563c2f80baa30013801").to_vec(); any.extend(any2); let any: Any = any.try_into().unwrap(); - // check if misbehavior + // check if misbehaviour let _ = Misbehaviour::try_from(any.clone()).unwrap(); let err = client .update_client(&ctx, client_id.clone(), any.clone()) @@ -1119,4 +1154,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 + ); + } + } } diff --git a/light-client/src/client_state.rs b/light-client/src/client_state.rs index ee00f71..60fe11f 100644 --- a/light-client/src/client_state.rs +++ b/light-client/src/client_state.rs @@ -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}; @@ -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, )?; @@ -159,6 +171,13 @@ impl TryFrom for ClientState { let chain_id = ChainId::new(value.chain_id); + if chain_id.version() != raw_latest_height.revision_number { + return Err(Error::UnexpectedLatestHeightRevision( + chain_id.version(), + raw_latest_height.revision_number, + )); + } + let latest_height = new_height( raw_latest_height.revision_number, raw_latest_height.revision_height, @@ -489,6 +508,19 @@ mod test { err => unreachable!("{:?}", err), } + cs.latest_height = Some(Height { + revision_number: 1, + revision_height: 0, + }); + let err = ClientState::try_from(cs.clone()).unwrap_err(); + match err { + Error::UnexpectedLatestHeightRevision(e1, e2) => { + assert_eq!(e1, 0); + assert_eq!(e2, 1); + } + err => unreachable!("{:?}", err), + } + cs.latest_height = Some(Height::default()); let err = ClientState::try_from(cs.clone()).unwrap_err(); match err { @@ -673,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), + } + } + } } diff --git a/light-client/src/errors.rs b/light-client/src/errors.rs index 7c152ce..fc843c2 100644 --- a/light-client/src/errors.rs +++ b/light-client/src/errors.rs @@ -32,6 +32,7 @@ pub enum Error { UnexpectedCommitmentSlot(Vec), ClientFrozen(ClientId), UnexpectedProofHeight(Height, Height), + UnexpectedRevisionHeight(u64), // ConsensusState error AccountNotFound(Address), @@ -50,6 +51,8 @@ pub enum Error { UnexpectedValidatorsHashSize(Vec), // Header error + UnsupportedMinimumTimestamp(Time), + UnsupportedMinimumHeight(Height), MissingPreviousValidators(BlockNumber), MissingCurrentValidators(BlockNumber), OutOfTrustingPeriod(Time, Time), @@ -60,6 +63,7 @@ pub enum Error { UnexpectedTrustedHeight(BlockNumber, BlockNumber), EmptyHeader, UnexpectedHeaderRevision(u64, u64), + UnexpectedLatestHeightRevision(u64, u64), UnexpectedSignature(BlockNumber, signature::Error), MissingVanityInExtraData(BlockNumber, usize, usize), MissingSignatureInExtraData(BlockNumber, usize, usize), @@ -80,11 +84,10 @@ pub enum Error { MissingTurnLengthInEpochBlock(BlockNumber), MissingEpochInfoInEpochBlock(BlockNumber), MissingNextValidatorSet(BlockNumber), - MissingCurrentValidatorSet(BlockNumber), UnexpectedPreviousValidatorsHash(Height, Height, Hash, Hash), UnexpectedCurrentValidatorsHash(Height, Height, Hash, Hash), InvalidVerifyingHeaderLength(BlockNumber, usize), - InsufficientTrustedValidatorsInUntrustedValidators(Hash, usize, usize), + InsufficientHonestValidator(Hash, usize, usize), MissingValidatorToVerifySeal(BlockNumber), MissingValidatorToVerifyVote(BlockNumber), UnexpectedNextCheckpointHeader(BlockNumber, BlockNumber), @@ -94,6 +97,7 @@ pub enum Error { UnexpectedDifficultyNoTurn(BlockNumber, u64, usize), UnexpectedUntrustedValidatorsHashInEpoch(Height, Height, Hash, Hash), UnexpectedCurrentValidatorsHashInEpoch(Height, Height, Hash, Hash), + UnexpectedUntrustedValidators(BlockNumber, BlockNumber), // Vote attestation UnexpectedTooManyHeadersToFinalize(BlockNumber, usize), @@ -163,6 +167,9 @@ impl core::fmt::Display for Error { Error::UnexpectedHeaderRevision(e1, e2) => { write!(f, "UnexpectedHeaderRevision: {} {}", e1, e2) } + Error::UnexpectedLatestHeightRevision(e1, e2) => { + write!(f, "UnexpectedLatestHeightRevision: {} {}", e1, e2) + } Error::UnexpectedSignature(e1, e2) => write!(f, "UnexpectedSignature: {} {}", e1, e2), Error::MissingVanityInExtraData(e1, e2, e3) => { write!(f, "MissingVanityInExtraData: {} {} {}", e1, e2, e3) @@ -309,19 +316,12 @@ impl core::fmt::Display for Error { Error::UnexpectedVoteRelation(e1, e2, e3) => { write!(f, "UnexpectedVoteRelation : {} {} {:?}", e1, e2, e3) } - Error::InsufficientTrustedValidatorsInUntrustedValidators(e1, e2, e3) => { - write!( - f, - "InsufficientTrustedValidatorsInUntrustedValidators : {:?} {} {}", - e1, e2, e3 - ) + Error::InsufficientHonestValidator(e1, e2, e3) => { + write!(f, "InsufficientHonestValidator : {:?} {} {}", e1, e2, e3) } Error::MissingNextValidatorSet(e1) => { write!(f, "MissingNextValidatorSet : {}", e1) } - Error::MissingCurrentValidatorSet(e1) => { - write!(f, "MissingCurrentValidatorSet : {}", e1) - } Error::MissingValidatorToVerifySeal(e1) => { write!(f, "MissingValidatorToVerifySeal : {:?}", e1) } @@ -372,6 +372,18 @@ impl core::fmt::Display for Error { e1, e2, e3, e4 ) } + Error::UnexpectedUntrustedValidators(e1, e2) => { + write!(f, "UnexpectedUntrustedValidators : {} {}", e1, e2) + } + Error::UnsupportedMinimumTimestamp(e1) => { + write!(f, "UnsupportedMinimumTimestamp : {:?}", e1) + } + Error::UnsupportedMinimumHeight(e1) => { + write!(f, "UnsupportedMinimumHeight : {:?}", e1) + } + Error::UnexpectedRevisionHeight(e1) => { + write!(f, "UnexpectedRevisionHeight : {}", e1) + } } } } diff --git a/light-client/src/header/epoch.rs b/light-client/src/header/epoch.rs index d6a4a01..f2333bc 100644 --- a/light-client/src/header/epoch.rs +++ b/light-client/src/header/epoch.rs @@ -57,53 +57,56 @@ impl<'a> TrustedEpoch<'a> { pub fn new(inner: &'a Epoch) -> Self { Self { inner } } -} -#[derive(Clone, Debug, PartialEq)] -pub struct UntrustedEpoch<'a> { - inner: &'a Epoch, -} - -impl<'a> UntrustedEpoch<'a> { - pub fn new(inner: &'a Epoch) -> Self { - Self { inner } - } - pub fn checkpoint(&self) -> u64 { - self.inner.checkpoint() - } - pub fn try_borrow(&'a self, trusted_epoch: &TrustedEpoch) -> Result<&'a Epoch, Error> { - let (result, found, required) = self.contains(trusted_epoch); + pub fn verify_untrusted_voters(&self, untrusted_voter: &Validators) -> Result<(), Error> { + let (result, found, required) = + self.contains_at_least_one_honest_validator(untrusted_voter); if result { - return Ok(self.inner); + return Ok(()); } - Err(Error::InsufficientTrustedValidatorsInUntrustedValidators( + Err(Error::InsufficientHonestValidator( self.inner.hash, found, required, )) } - fn contains(&self, trusted_epoch: &TrustedEpoch) -> (bool, usize, usize) { - let trusted_validators = trusted_epoch.validators(); + pub fn contains_at_least_one_honest_validator( + &self, + untrusted_voters: &Validators, + ) -> (bool, usize, usize) { let mut trusted_validator_count = 0; - for x1 in self.inner.validators() { - if trusted_validators.contains(x1) { + for x1 in untrusted_voters { + if self.validators().contains(x1) { trusted_validator_count += 1; } } - let required = Self::threshold(trusted_validators.len()); + let required = Self::threshold(self.validators().len()); ( trusted_validator_count >= required, trusted_validator_count, required, ) } - fn threshold(validators_len: usize) -> usize { validators_len - ceil_div(validators_len * 2, 3) + 1 } } +#[derive(Clone, Debug, PartialEq)] +pub struct UntrustedEpoch<'a> { + inner: &'a Epoch, +} + +impl<'a> UntrustedEpoch<'a> { + pub fn new(inner: &'a Epoch) -> Self { + Self { inner } + } + pub fn checkpoint(&self) -> u64 { + self.inner.checkpoint() + } +} + #[derive(Clone, Debug, PartialEq)] pub enum EitherEpoch<'a> { Trusted(TrustedEpoch<'a>), @@ -117,16 +120,23 @@ impl<'a> EitherEpoch<'a> { EitherEpoch::Untrusted(v) => v.checkpoint(), } } + + pub fn epoch(&self) -> &'a Epoch { + match self { + EitherEpoch::Trusted(v) => v.inner, + EitherEpoch::Untrusted(v) => v.inner, + } + } } #[cfg(test)] mod test { use crate::errors::Error; - use crate::header::epoch::{Epoch, TrustedEpoch, UntrustedEpoch, ValidatorSet}; + use crate::header::epoch::{Epoch, TrustedEpoch, ValidatorSet}; #[test] - pub fn test_untrusted_epoch_try_borrow() { - let mut _assert_trusted = |x, y, c_val_borrowable| { + pub fn test_verify_voter() { + let mut _assert_trusted = |x, y, success: bool| { let trusted_validators: ValidatorSet = vec![ vec![1], vec![2], @@ -139,34 +149,18 @@ mod test { .into(); let trusted_epoch = Epoch::new(trusted_validators, 1); let trusted_epoch = TrustedEpoch::new(&trusted_epoch); - let untrusted_epoch = Epoch::new( - ValidatorSet { - validators: x, - hash: [0; 32], - }, - 1, - ); - let untrusted_epoch = UntrustedEpoch::new(&untrusted_epoch); - let (result, count, required) = untrusted_epoch.contains(&trusted_epoch); - assert_eq!(result, c_val_borrowable); + let (result, count, required) = + trusted_epoch.contains_at_least_one_honest_validator(&x); + assert_eq!(result, success); assert_eq!(count, y); assert_eq!(required, 3); - match untrusted_epoch.try_borrow(&trusted_epoch) { - Ok(borrowed) => { - if c_val_borrowable { - assert_eq!(borrowed, untrusted_epoch.inner); - } else { - unreachable!("unexpected borrowed") - } - } + match trusted_epoch.verify_untrusted_voters(&x) { + Ok(_) => assert!(success), Err(e) => { - if c_val_borrowable { - unreachable!("unexpected error {:?}", e); - } else { - match e { - Error::InsufficientTrustedValidatorsInUntrustedValidators(_, _, _) => {} - e => unreachable!("unexpected error type {:?}", e), - } + assert!(!success); + match e { + Error::InsufficientHonestValidator(_, _, _) => {} + e => unreachable!("unexpected error type {:?}", e), } } } @@ -302,15 +296,15 @@ mod test { #[test] pub fn test_trust_threshold() { - assert_eq!(1, UntrustedEpoch::threshold(1)); - assert_eq!(1, UntrustedEpoch::threshold(2)); - assert_eq!(2, UntrustedEpoch::threshold(3)); - assert_eq!(2, UntrustedEpoch::threshold(4)); - assert_eq!(2, UntrustedEpoch::threshold(5)); - assert_eq!(3, UntrustedEpoch::threshold(6)); - assert_eq!(3, UntrustedEpoch::threshold(7)); - assert_eq!(3, UntrustedEpoch::threshold(8)); - assert_eq!(4, UntrustedEpoch::threshold(9)); - assert_eq!(8, UntrustedEpoch::threshold(21)); + assert_eq!(1, TrustedEpoch::threshold(1)); + assert_eq!(1, TrustedEpoch::threshold(2)); + assert_eq!(2, TrustedEpoch::threshold(3)); + assert_eq!(2, TrustedEpoch::threshold(4)); + assert_eq!(2, TrustedEpoch::threshold(5)); + assert_eq!(3, TrustedEpoch::threshold(6)); + assert_eq!(3, TrustedEpoch::threshold(7)); + assert_eq!(3, TrustedEpoch::threshold(8)); + assert_eq!(4, TrustedEpoch::threshold(9)); + assert_eq!(8, TrustedEpoch::threshold(21)); } } diff --git a/light-client/src/header/eth_header.rs b/light-client/src/header/eth_header.rs index 7291d3a..0355878 100644 --- a/light-client/src/header/eth_header.rs +++ b/light-client/src/header/eth_header.rs @@ -152,6 +152,8 @@ impl ETHHeader { epoch_count * BLOCKS_PER_EPOCH } + /// Verifies that all headers in the `ETHHeader` struct have valid cascading fields. + /// /// https://github.com/bnb-chain/bsc/blob/b4773e8b5080f37e1c65c083b543f60c895abb70/consensus/parlia/parlia.go#L380 pub fn verify_cascading_fields(&self, parent: ÐHeader) -> Result<(), Error> { if self.gas_used > self.gas_limit { @@ -189,6 +191,8 @@ impl ETHHeader { Ok(()) } + /// Verifies the seal of the current `ETHHeader`. + /// /// https://github.com/bnb-chain/bsc/blob/7a19cd27b61b342d24a1584efc7fa00de4a5b4f5/consensus/parlia/parlia.go#L755 pub fn verify_seal( &self, @@ -218,6 +222,12 @@ impl ETHHeader { Ok(signer) } + /// Verifies the validator rotation for the current `ETHHeader`. + /// + /// This function checks if the validator rotation is correct by comparing the coinbase address + /// with the expected in-turn validator address based on the current block number and epoch. + /// It ensures that the difficulty corresponds to the turn-ness of the signer. + /// fn verify_validator_rotation(&self, epoch: &Epoch) -> Result<(), Error> { let offset = (self.number / epoch.turn_length() as u64) as usize % epoch.validators().len(); let inturn_validator = &epoch.validators()[offset][0..VALIDATOR_BYTES_LENGTH_BEFORE_LUBAN]; @@ -239,6 +249,11 @@ impl ETHHeader { Ok(()) } + /// Verifies the target attestation of the current `ETHHeader` against its parent header. + /// + /// This function checks the target vote attestation of the current header to ensure that + /// the target block is the direct parent of the current block. + /// pub fn verify_target_attestation(&self, parent: ÐHeader) -> Result { let target_vote_attestation = self.get_vote_attestation()?; let target_data = &target_vote_attestation.data; @@ -255,6 +270,8 @@ impl ETHHeader { Ok(target_vote_attestation) } + /// Verifies the vote attestation of the current `ETHHeader` against its parent header. + /// /// https://github.com/bnb-chain/bsc/blob/7a19cd27b61b342d24a1584efc7fa00de4a5b4f5/consensus/parlia/parlia.go#L416 pub fn verify_vote_attestation(&self, parent: ÐHeader) -> Result { let vote_attestation = self.verify_target_attestation(parent)?; @@ -303,7 +320,7 @@ impl ETHHeader { } } -pub fn get_validator_bytes_and_tern_term(extra_data: &[u8]) -> Result<(Validators, u8), Error> { +pub fn get_validator_bytes_and_turn_length(extra_data: &[u8]) -> Result<(Validators, u8), Error> { if extra_data.len() <= EXTRA_VANITY + EXTRA_SEAL { return Err(Error::UnexpectedExtraDataLength(extra_data.len())); } @@ -466,7 +483,7 @@ impl TryFrom for ETHHeader { let hash: Hash = keccak_256(&buffer_vec); let epoch = if number % BLOCKS_PER_EPOCH == 0 { - let (validators, turn_length) = get_validator_bytes_and_tern_term(&extra_data)?; + let (validators, turn_length) = get_validator_bytes_and_turn_length(&extra_data)?; Some(Epoch::new(validators.into(), turn_length)) } else { None diff --git a/light-client/src/header/eth_headers.rs b/light-client/src/header/eth_headers.rs index c25cf3d..6281647 100644 --- a/light-client/src/header/eth_headers.rs +++ b/light-client/src/header/eth_headers.rs @@ -1,13 +1,12 @@ use alloc::vec::Vec; - use parlia_ibc_proto::ibc::lightclients::parlia::v1::EthHeader; use crate::errors::Error; use crate::errors::Error::MissingEpochInfoInEpochBlock; use crate::header::epoch::EitherEpoch::{Trusted, Untrusted}; -use crate::header::epoch::{EitherEpoch, Epoch, TrustedEpoch, UntrustedEpoch}; +use crate::header::epoch::{EitherEpoch, Epoch, TrustedEpoch}; -use crate::misc::{BlockNumber, ChainId}; +use crate::misc::{BlockNumber, ChainId, Validators}; use super::eth_header::ETHHeader; use super::BLOCKS_PER_EPOCH; @@ -19,6 +18,15 @@ pub struct ETHHeaders { } impl ETHHeaders { + /// Verifies the headers in the `ETHHeaders` struct. + /// + /// This function performs several checks to ensure the validity of the headers: + /// 1. Ensures the header after the next or next checkpoint does not exist. + /// 2. Verifies the size of the headers within the specified epoch range. + /// 3. Ensures all headers are successfully chained. + /// 4. Validates the seals of all headers. + /// 5. Ensures the target header is finalized. + /// 6. Ensures the BLS signature is correct. pub fn verify( &self, chain_id: &ChainId, @@ -29,13 +37,7 @@ impl ETHHeaders { let epoch = self.target.number / BLOCKS_PER_EPOCH; let checkpoint = epoch * BLOCKS_PER_EPOCH + previous_epoch.checkpoint(); let next_checkpoint = (epoch + 1) * BLOCKS_PER_EPOCH + current_epoch.checkpoint(); - let (c_val, n_val) = self.verify_header_size( - epoch, - checkpoint, - next_checkpoint, - previous_epoch, - current_epoch, - )?; + let n_val = self.verify_header_size(epoch, checkpoint, next_checkpoint, current_epoch)?; // Ensure all the headers are successfully chained. self.verify_cascading_fields()?; @@ -46,7 +48,7 @@ impl ETHHeaders { if h.number >= next_checkpoint { h.verify_seal(unwrap_n_val(h.number, &n_val)?, chain_id)?; } else if h.number >= checkpoint { - h.verify_seal(unwrap_c_val(h.number, &c_val)?, chain_id)?; + h.verify_seal(current_epoch.epoch(), chain_id)?; } else { h.verify_seal(previous_epoch.epoch(), chain_id)?; } @@ -57,19 +59,35 @@ impl ETHHeaders { // Ensure BLS signature is collect // At the just checkpoint BLS signature uses previous validator set. + let mut last_voters: Validators = Vec::new(); for h in &[child, grand_child] { let vote = h.get_vote_attestation()?; - if h.number > next_checkpoint { - vote.verify(h.number, unwrap_n_val(h.number, &n_val)?.validators())?; + last_voters = if h.number > next_checkpoint { + vote.verify(h.number, unwrap_n_val(h.number, &n_val)?.validators())? } else if h.number > checkpoint { - vote.verify(h.number, unwrap_c_val(h.number, &c_val)?.validators())?; + vote.verify(h.number, current_epoch.epoch().validators())? } else { - vote.verify(h.number, p_val)?; - } + vote.verify(h.number, p_val)? + }; } + + // Ensure voters for grand child are valid + verify_voters( + &last_voters, + grand_child, + next_checkpoint, + checkpoint, + current_epoch, + previous_epoch, + )?; + Ok(()) } + /// Verifies that all headers in the `all` vector have valid cascading fields. + /// + /// This function iterates through the `all` vector of `ETHHeader` objects and ensures that each + /// header (except the last one) has valid cascading fields with its subsequent header. fn verify_cascading_fields(&self) -> Result<(), Error> { for (i, header) in self.all.iter().enumerate() { if i < self.all.len() - 1 { @@ -80,6 +98,10 @@ impl ETHHeaders { Ok(()) } + /// Verifies that the headers are finalized. + /// + /// Only one set of three consecutive valid headers must exist. + /// This means that if [x, x+1, x+2] is valid then x+3 must not exist. fn verify_finalized(&self) -> Result<(ÐHeader, ÐHeader), Error> { if self.all.len() < 3 { return Err(Error::InvalidVerifyingHeaderLength( @@ -112,18 +134,21 @@ impl ETHHeaders { )) } - fn verify_header_size<'a, 'b>( - &'b self, + /// Verifies the size of the headers within the specified epoch range. + /// + /// This function filters the headers to include only those that are within the specified + /// checkpoint range and ensures that they meet the size requirements for the current and next epochs. + fn verify_header_size( + &self, epoch: u64, checkpoint: u64, next_checkpoint: u64, - previous_epoch: &TrustedEpoch, - current_epoch: &'a EitherEpoch, - ) -> Result<(Option<&'a Epoch>, Option<&'b Epoch>), Error> { + current_epoch: &EitherEpoch, + ) -> Result, Error> { let hs: Vec<ÐHeader> = self.all.iter().filter(|h| h.number >= checkpoint).collect(); match current_epoch { - // ex) t=200 then 200 <= h < 411 (c_val(200) can be borrowed by p_val) - Untrusted(untrusted) => { + // ex) t=200 then 200 <= h < 411 (at least 1 honest c_val(200)' can be in p_val) + Untrusted(_) => { // Ensure headers are before the next_checkpoint if hs.iter().any(|h| h.number >= next_checkpoint) { return Err(Error::UnexpectedNextCheckpointHeader( @@ -131,35 +156,27 @@ impl ETHHeaders { next_checkpoint, )); } - - // Ensure c_val is validated by trusted p_val when the checkpoint header is found - if hs.is_empty() { - Ok((None, None)) - } else { - Ok((Some(untrusted.try_borrow(previous_epoch)?), None)) - } + Ok(None) } - // ex) t=201 then 201 <= h < 611 (n_val(400) can be borrowed by c_val(200)) - Trusted(trusted) => { + // ex) t=201 then 201 <= h < 611 (at least 1 honest n_val(400) can be in c_val(200)) + Trusted(_) => { // Get next_epoch if epoch after checkpoint ex) 400 let next_epoch = match hs.iter().find(|h| h.is_epoch()) { Some(h) => h .epoch .as_ref() .ok_or_else(|| MissingEpochInfoInEpochBlock(h.number))?, - None => return Ok((Some(trusted.epoch()), None)), + None => return Ok(None), }; // Finish if no headers over next checkpoint were found let hs: Vec<&ÐHeader> = hs.iter().filter(|h| h.number >= next_checkpoint).collect(); if hs.is_empty() { - return Ok((Some(trusted.epoch()), None)); + return Ok(None); } - // Ensure n_val(400) can be borrowed by c_val(200) let next_next_checkpoint = (epoch + 2) * BLOCKS_PER_EPOCH + next_epoch.checkpoint(); - UntrustedEpoch::new(next_epoch).try_borrow(trusted)?; // Ensure headers are before the next_next_checkpoint if hs.iter().any(|h| h.number >= next_next_checkpoint) { @@ -168,7 +185,7 @@ impl ETHHeaders { next_next_checkpoint, )); } - Ok((Some(trusted.epoch()), Some(next_epoch))) + Ok(Some(next_epoch)) } } } @@ -222,8 +239,30 @@ fn unwrap_n_val<'a>(n: BlockNumber, n_val: &'a Option<&'a Epoch>) -> Result<&'a n_val.ok_or_else(|| Error::MissingNextValidatorSet(n)) } -fn unwrap_c_val<'a>(n: BlockNumber, c_val: &'a Option<&'a Epoch>) -> Result<&'a Epoch, Error> { - c_val.ok_or_else(|| Error::MissingCurrentValidatorSet(n)) +fn verify_voters( + voters: &Validators, + h: ÐHeader, + next_checkpoint: BlockNumber, + checkpoint: BlockNumber, + current_epoch: &EitherEpoch, + previous_epoch: &TrustedEpoch, +) -> Result<(), Error> { + if h.number > next_checkpoint { + match current_epoch { + Trusted(e) => e.verify_untrusted_voters(voters)?, + _ => { + return Err(Error::UnexpectedUntrustedValidators( + h.number, + next_checkpoint, + )) + } + } + } else if h.number > checkpoint { + if let Untrusted(_) = current_epoch { + previous_epoch.verify_untrusted_voters(voters)?; + } + } + Ok(()) } #[cfg(test)] @@ -231,8 +270,8 @@ mod test { use crate::errors::Error; use crate::header::constant::BLOCKS_PER_EPOCH; - use crate::header::eth_header::{get_validator_bytes_and_tern_term, ETHHeader}; - use crate::header::eth_headers::ETHHeaders; + use crate::header::eth_header::{get_validator_bytes_and_turn_length, ETHHeader}; + use crate::header::eth_headers::{verify_voters, ETHHeaders}; use crate::fixture::*; use crate::header::epoch::{EitherEpoch, Epoch, TrustedEpoch, UntrustedEpoch}; @@ -412,6 +451,95 @@ mod test { header.headers.verify_finalized().unwrap(); } + #[test] + fn test_success_verify_voters() { + let mut h = localnet().previous_epoch_header(); + let p_vals = vec![vec![1], vec![2]]; + let p_epoch = Epoch::new(p_vals.into(), 1); + let pt_epoch = TrustedEpoch::new(&p_epoch); + let c_vals = vec![vec![1], vec![2]]; + let c_epoch = Epoch::new(c_vals.into(), 1); + + // after next checkpoint + h.number = 412; + verify_voters( + &vec![vec![1]], + &h, + 411, + 211, + &EitherEpoch::Trusted(TrustedEpoch::new(&c_epoch)), + &pt_epoch, + ) + .unwrap(); + + // after checkpoint + h.number = 212; + verify_voters( + &vec![vec![1]], + &h, + 411, + 211, + &EitherEpoch::Untrusted(UntrustedEpoch::new(&c_epoch)), + &pt_epoch, + ) + .unwrap(); + + // other + h.number = 211; + verify_voters( + &vec![vec![1]], + &h, + 411, + 211, + &EitherEpoch::Untrusted(UntrustedEpoch::new(&c_epoch)), + &pt_epoch, + ) + .unwrap(); + } + + #[test] + fn test_error_verify_voters() { + let mut h = localnet().previous_epoch_header(); + let p_vals = vec![vec![1], vec![2]]; + let p_epoch = Epoch::new(p_vals.into(), 1); + let pt_epoch = TrustedEpoch::new(&p_epoch); + let c_vals = vec![vec![1], vec![2]]; + let c_epoch = Epoch::new(c_vals.into(), 1); + + // after next checkpoint + h.number = 412; + verify_voters( + &vec![vec![1]], + &h, + 411, + 211, + &EitherEpoch::Untrusted(UntrustedEpoch::new(&c_epoch)), + &pt_epoch, + ) + .unwrap_err(); + verify_voters( + &vec![vec![0]], + &h, + 411, + 211, + &EitherEpoch::Trusted(TrustedEpoch::new(&c_epoch)), + &pt_epoch, + ) + .unwrap_err(); + + // after checkpoint + h.number = 212; + verify_voters( + &vec![vec![0]], + &h, + 411, + 211, + &EitherEpoch::Untrusted(UntrustedEpoch::new(&c_epoch)), + &pt_epoch, + ) + .unwrap_err(); + } + #[test] fn test_error_verify_finalized_no_finalized_header() { let header= hex!("0a222f6962632e6c69676874636c69656e74732e7061726c69612e76312e48656164657212ed200ae9080ae608f90463a0794978ac680964fb5ada43366fa4d33a490c93ec6893304ddee68a59f2cafabaa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794d9a13701eafb76870cb220843b8c6476824bfa15a0558eacf75665a00d1eef186ffc4f79985db5e5fcb1aa24892df5d600ae869313a09f0bb93d54df1fcbfd84d4173496de9cff0f403319bbbaf15791ccde774b73d8a03cd1ebc99cd975182c58de47be968c97658cff4c465e20654185f408a851403cb9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028211308402625a008229a884669fb2e3b90223d98301040c846765746889676f312e32312e3132856c696e75780000d24ec9e8048fdaaa7e6631e438625ca25c857a3727ea28e565b876532dd999985816f1df35a5d6359177f1d49bfb3c20e25d6760197246ad0b6b8efb77ad316a0f31360c3733cabd6ca7876ea32e7a748c697d01345145485561305b24b6c305acd27ad7aff76367fd3a1dfe8da19afba969c8464f37a29e60923c3a85cfacbdef18daa782d5724f13d415f98cb2e42bc54d19116d2348ac83461e2e0915d508ad976963272de9af796035a7c68771d03c92709aa174ce1e8723cb6d7d1f6d960790e83c59e1f9867721e6302520a30a44e04db2de85453e0936b441c339a26d10cfa71b50b359d8b4d1e5fd24f5a99712ed2e5a8f7180621828a1ae567b86ff60792ff27f2fd62d410aa8b9b858316495867f833309f8ae0fb860834538868d2c79371ead2f10fc7229fd3b3aaf1d7d8607fd7e1d2efba7df1008c105fdab4ad8029f86cfde1e3aa7dd24194fcf07502bb6c281fc3cbc08ad8cb467de57cbfd1bb93fabe72f77ece8f991a2b7a4d7fb301d54547c6cb4b612d06ff84882112ea042dfe9761fb9a677b088a868f237a171d511bea581f643844a1c98267902391882112fa0794978ac680964fb5ada43366fa4d33a490c93ec6893304ddee68a59f2cafaba80321f35c8454a2691a2efff6894cae6277ea06390978294fe0d10fe03f432fd07440d802ca0858a2d6138230b37d1a9148a3d76fbe8492854e637c9aad7494e3401a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a000000000000000000000000000000000000000000000000000000000000000000ad5060ad206f9034fa09696424e13500cdc742b049c6459c0bc4cb357eab5d9fb48a2e79787c8897a1fa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794d9a13701eafb76870cb220843b8c6476824bfa15a0558eacf75665a00d1eef186ffc4f79985db5e5fcb1aa24892df5d600ae869313a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028211318402625a008084669fb2e6b90111d98301040c846765746889676f312e32312e3132856c696e75780000d24ec9e8f8ae0fb860b6de9ffe941751cd11463bbd4bde69d3bc2b79868f669f9e2c4c327f036e6d558b013469a91670607dd88d63dc50b95d00719e118a17e5b72418b6ae4bef7aebbcdaa44b79f8c0677ac32c71312d84db44e6e9216bd84fb97c7d0701a4a03431f84882112fa0794978ac680964fb5ada43366fa4d33a490c93ec6893304ddee68a59f2cafaba821130a09696424e13500cdc742b049c6459c0bc4cb357eab5d9fb48a2e79787c8897a1f80e5619874ecb4463f8d86981d939ddb4d0eaa151c6080c7067788e06699789d0a415e921b8824e8adc7907e5bbccec9373b22c7fe8a48527e9348aa957871b63300a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a000000000000000000000000000000000000000000000000000000000000000000ad5060ad206f9034fa0e3aa9bc64f82ccd7e70ec415d73263d9da9f3bb44b78bed500033379df9be8aaa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d4934794d9a13701eafb76870cb220843b8c6476824bfa15a0558eacf75665a00d1eef186ffc4f79985db5e5fcb1aa24892df5d600ae869313a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421b9010000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000028211328402625a008084669fb2e9b90111d98301040c846765746889676f312e32312e3132856c696e75780000d24ec9e8f8ae0fb86095a056c77bdeb59da26e665d69e75cc5667cd43df693a55f20f5f248483a3ce10cdd996b29c7fd6ba3f9e31cb4ac089c0b5bc13e77d83f17096d88f280b8d47ffd8b58b66bd760d0ccd4d26e0c2a7bb3ec168a06a815cfbdde9a2e18c32ac74ef848821130a09696424e13500cdc742b049c6459c0bc4cb357eab5d9fb48a2e79787c8897a1f821131a0e3aa9bc64f82ccd7e70ec415d73263d9da9f3bb44b78bed500033379df9be8aa80c04c6f70fa142c7486f7bed7e29a247f1203d58c7481c9aefef1193e6404112e32486f8d4dc8c7c558ae0b1bf860f6e05d425b7726ba7a4e2dac6e3e3eb83d3d00a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000120310e8201a9506f90312f901f1a094e7dcd05ddcc3923085b451330f3aa5ce5a628d6685506d99cb09b3aef0e11ea065b83aa9b59125f9b090432f556c6ff947b5708eb11ca5ea26342392860be00aa0b116ef7733a93eed23f018027c116e60436a228a9f9173bb9b0c40eb71216da6a064c0a17f12a753c3fc032723866ac267ad8b7e05e7aa2e75bb680175d936617580a01a41c640130c53b3c90b1b5c691ed467218cee97aade5aac9306e72865851e27a004371241b9d6f35e1f361f2109d19a9192a9c0c749b2bd15fcb131a1a6ce5e3ba00577e3e2c4649c5a23cbdabe0bbfed7cdf6e85c136d84d58127cdec86264ad6ea070c0f30031f40c8017dbfc2ef008e6a3aae2e3105a654e0c5439b6104752882ea08f81903ec8515875682785142e1f92bdeaf65fccd5d0cf78b1ff2905a07e5883a03052420ba2d24a04d3f830584d3dbd6907b6d82bab84ddd806d03470e2c9d51ca06fb1a1498c2c8f93944a4f672ff4e982480ad181c835c0d8078159c517c7977aa013a426820f7b7249edc97cc5c002e653ac84b437f3ac12ac940c3d4b09e09827a09fbc54eac488b27315b09a1afa8d12f168e4c4cb5aea2d9a6ab5e7266da2f7e8a077c5e5cd5bd518bc509ee5e71790f1e42e492e23875b097e565cff8e809e7c8aa0a1575ef06513a19d2a28390e83958d2a3ffe166b530255b0fb5559d33409914d80f8b18080a0dc77b6ae50b675036e77b31973c79ec60c28c0d2c57b03ad99c2acfff2f0cd4e80a063a8a6161448a60a47ddbafa00899bed224e9f80072b35a1dbc64a82e85cd9b5a05e0f116451aaa1baab3f3abff2793c8318050eeed6bf62d464d343a11d86eb2880808080808080a0abbb1987d09a71106f586030d1ab913bae0008e2a7dec0d08f2d60cd30fb2ac8a096c706907bfc6472dd88315cb8e21ee6f60a661cd8050065e2ba387023ee96858080f869a020b1e2b1f9852058ee0aaadca3c963f77f6483a1a51c644d79386bcada360583b846f8440180a0e39304f0ec064a98e4b0a96432dfb0a9e4c7fd0f26a6bbcf9c75bff68c51a7a9a0b3d632130dcb5cb583b47ec0623e59ca3703e6e2564f144272b597f3e3511ba822448fdaaa7e6631e438625ca25c857a3727ea28e565b876532dd999985816f1df35a5d6359177f1d49bfb3c20e25d6760197246ad0b6b8efb77ad316a0f31360c3733cabd6c2244a7876ea32e7a748c697d01345145485561305b24b6c305acd27ad7aff76367fd3a1dfe8da19afba969c8464f37a29e60923c3a85cfacbdef18daa782d5724f13d415f98c2244b2e42bc54d19116d2348ac83461e2e0915d508ad976963272de9af796035a7c68771d03c92709aa174ce1e8723cb6d7d1f6d960790e83c59e1f9867721e6302520a30a442244e04db2de85453e0936b441c339a26d10cfa71b50b359d8b4d1e5fd24f5a99712ed2e5a8f7180621828a1ae567b86ff60792ff27f2fd62d410aa8b9b858316495867f83332a448fdaaa7e6631e438625ca25c857a3727ea28e565b876532dd999985816f1df35a5d6359177f1d49bfb3c20e25d6760197246ad0b6b8efb77ad316a0f31360c3733cabd6c2a44b2e42bc54d19116d2348ac83461e2e0915d508ad976963272de9af796035a7c68771d03c92709aa174ce1e8723cb6d7d1f6d960790e83c59e1f9867721e6302520a30a442a44d9a13701eafb76870cb220843b8c6476824bfa15b9ebdc1d1a70721d7f9c57622e0a5d1175df1e09672ab1e8909bf9a9433592107024bd8a3ad47fbbdca199ede96c50d22a44e04db2de85453e0936b441c339a26d10cfa71b50b359d8b4d1e5fd24f5a99712ed2e5a8f7180621828a1ae567b86ff60792ff27f2fd62d410aa8b9b858316495867f833330093808").to_vec(); @@ -429,6 +557,51 @@ mod test { } } + #[test] + fn test_success_verify_finalized_with_many_headers() { + let v = vec![ + //https://testnet.bscscan.com/block/45214600 + decode_header(hex!("f9057ea06e94ae7ef8cc013f1ddff4519d44823bdb508e10cec2d8c2e191d27f741cbf8ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479440d3256eb0babe89f0ea54edaa398513136612f5a0766f9938068e5a9fa592451105996d65e587e49b90cc931b243db46891830fc4a0f070fccfa4d96d319030acf9bd07ad61df24cf24ae4c581df633739a6bb2d33da0fea7ba2221ca1582bece7533eb42f6f295d4dbc03e62f14478168e287aad5734b90100040010400040000000000040001000000241000800000000c0000000020400000000104800010000008210000000000000000000020000400100040000200000000000200004000300000008000200002110480000000000a00200004020000004480028a0020001000400000400450008600400000000000020001200000800000808000000000000000000000000000000040000000002240000008000082802008040000000200820000002800042000000000000100002002000000001000000000200000000100804200000000042400000000002c00010480200000000001008000000000001100084000001040008c000008200004040200000000000028402b1eb888405efeb20830ba24a846723446fb90338d88301040e846765746888676f312e32312e34856c696e75780000000299d9bc0808265da01e1a65d62b903c7b34c08cb389bf3d9996f763f030b1adcfb369c5a5df4a18e1529baffe7feaec66db3dbd1bc06810f7f6f88b7be6645418a7e2a2a3f40514c21a3d9d7a717d64e6088ac937d5aacdd3e20ca963979974cd8ff90cbf097023dc8c448245ceff671e965d57d82eaf9be91478cfa0f24d2993e0c5f43a6c5a4cd99850023040d3256eb0babe89f0ea54edaa398513136612f5a334b49d766ebe3eb9f6bdc163bd2c19aa7e8cee1667851ae0c1651f01c4cf7cf2cfcf8475bff3e99cab25b05631472d53387f3321fd69d1e030bb921230dfb188826affaa39ebf1c38b190851e4db0588a3e90142c5299041fb8a0db3bb9a1fa4bdf0dae84ca37ee12a6b8c26caab775f0e007b76d76ee8823de52a1a431884c2ca930c5e72bff3803af79641cf964cc001671017f0b680f93b7dde085b24bbc67b2a562a216f903ac878c5477641328172a353f1e493cf7f5f2cf1aec83bf0c74df566a41aa7ed65ea84ea99e3849ef31887c0f880a0feb92f356f58fbd023a82f5311fc87a5883a662e9ebbbefc90bf13aa533c2438a4113804bfd447b49cd040d20bc21e49ffea6487f5638e4346ad9fc6d1ec30e28016d3892b51a7898bd354cfe78643453fd3868410da412de7f2883180d0a2840111ad2e043fa403ebf9a1db0d6f22bd78ffaeccbc8f47c83df9fbdbcfaade0f78a6b92b38c9f6d45ce8fb01da2b800100201cf0936b6b4b14c98af22edbe27df8aa197fca733891b5b6ca95db04f8b381fbb860a12aed22e41385aa96efa4536ed81355b069fae16992fd46f866f4b2767c09436517e5dbc349d48c2f863cccc472b3ce096d0376779428dd5cd1b28078d5022e529e51c1c97db640b7f1c5b11b1ffb7e7208acf95986a45a0d400a46f0c4c11cf84c8402b1eb86a01125d5208ff92b1ea97d01dadcd5e277f52e143916288d671d5fec8860c3e6b48402b1eb87a06e94ae7ef8cc013f1ddff4519d44823bdb508e10cec2d8c2e191d27f741cbf8e80daa9d3b546986bceb93f5afd160958d6d32f6d9ef81a70b21802a455dad7b9157accef88cd0e459bffd3c1105a0dcb53d877091d8bf84d706274fe7f43c5093f01a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218302000080a00000000000000000000000000000000000000000000000000000000000000000").to_vec()), + decode_header(hex!("f90359a04e47d8db81e515d75a96f36ee409190c402038c06cf05dd5bca71d1099c7cab6a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479440d3256eb0babe89f0ea54edaa398513136612f5a0447b10cfc9066c11310db781e39fcebcb9740ba67d4dc550e977be01907defc9a0f56e8363a75117d54a2fe8a7116b5044cb194495745d593d975cab1de018970ba057caa07d7d270fd85713bfdc42240142e48a0cc2458139e862b81413bbc11571b901000000000000000000200000400000100000000000000000000000000020000000000010000000000000000000000000000000000000800000010000000000000000000020000000020001000800000000201000000000000080000000000008000008002020020000000000000000c000084000000000000000000010000000000000400000000000000000000000000000000400000000000400000000000020000080000000002008000000020000000000000000000000020020000000000000000002000000000000000000000000000000000000008000105002002000000000000000000000010000040000030010008400000800004000000000010000028402b1eb898405e9fb36831bdde78467234472b90116d88301040e846765746888676f312e32312e34856c696e75780000000299d9bcf8b381fbb860a8ea6d387b7800147ad2155132dfa95d187f56ebd1c9ece1c8364df305761dfbd516d93423f8c57045228b7ef52fcde104c094aee3794c72efcd475311f88b6d3082f3ee399fd2b9cf58cd3c63350c7cba410fb43e219f419b4b57102daa6378f84c8402b1eb87a06e94ae7ef8cc013f1ddff4519d44823bdb508e10cec2d8c2e191d27f741cbf8e8402b1eb88a04e47d8db81e515d75a96f36ee409190c402038c06cf05dd5bca71d1099c7cab6807a03301635f5c73d1ee0f095f33c697badc2560110a3add7f7d59c995eefac4910b711317255a198ba93410bb88c7e97f860c906d1dc60229a06c551760b3bce00a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000").to_vec()), + decode_header(hex!("f902a3a0a845b19d537d80fc8ae29c4aed49bd6578f4eb5089632d40f708588f6970ebf4a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479440d3256eb0babe89f0ea54edaa398513136612f5a04b50b580bbd86f6e198fd9c085f837109c6ff39053625f4a11c7aee0e2cfb63aa0abbe999c7c87fe7427d44e0b0aaf8918a723b17744678bd9ba209368cc82f8a2a08189d28f2391444d7c4868cea83360c6109abe993f55a7d2429d383632c6947bb9010000000000800000000000004000100200000000000004000000000000000800000081100000000000000000000000000000000400000000000100000000000002008000200000000200000008000400002010000000000000800000000000000000080020201200001000000000004000084000100000000008000010800000000400000400000000000004000000000000000480000000000440000000008020400080000000002108000000020000000040000000000000020020000000000000000002000000000000000000000000000000000000008020105402000000000000000000000000010000040000030000008400000000204100000000010800028402b1eb8a8405e4113c8306e52c8467234475b861d88301040e846765746888676f312e32312e34856c696e75780000000299d9bcd0a2b2dcf96ddbb4e8c201a2845fb962fa5a8ea571b6f3fc5c6f30f515cbcc4470ea5545b435334fcb6165032a91423c6803d3771c9874a9a69107e2a27e484e00a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000").to_vec()), + decode_header(hex!("f90359a00113d3d7778af93b122871e02432ded0cc60c8c9fe1cedab69465bc2ed750f29a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479440d3256eb0babe89f0ea54edaa398513136612f5a01303affe211de58db87dca446d098e0dee67f3aa4edda393b2e7435f1d706283a0118805b4e132609bbab79efe56918cc539694253ed4555b2d13fe2165480ddf1a0f642c960ec442a236bb3f0749e5be8484773a16493bd9d429ed6929662248001b9010004000000000000000000004000000004000000000000000080000000000400000000100000010000008000000000000000000010000000000100000000200000000000200200800200000008000020002010000000000001800000000000000000080020200200000000000100004000084000000100000000000010000000000000000000000000000000000000000100000400000000000400000080000020020080000000022008000000020000000000000000000000020020000000010010000082000000200040000000000000020000002000008000104042000000000010000000000000010000040000014000008000000000006000000000000000028402b1eb8b8405de2d2c830424308467234478b90116d88301040e846765746888676f312e32312e34856c696e75780000000299d9bcf8b381fbb860b4fa72afe0a0b517bee1ae6ccec530926fedf8abda9f2155d70b4591d6a1cc941d7589410a3e3e61cb004d890c8b4b8002854de11c87173ba7a9dcfd32b63a58b3ffa1f3ac4847a63a4cda1aed80e3b74bafeb1350e9ab5210d81658860757c8f84c8402b1eb88a04e47d8db81e515d75a96f36ee409190c402038c06cf05dd5bca71d1099c7cab68402b1eb8aa00113d3d7778af93b122871e02432ded0cc60c8c9fe1cedab69465bc2ed750f29808b1762cfd0533f76a6853bc389b57c07d84255fcdffb8ec429753ed0cf941e4a1646277e4462b3136f00536f86910b861312fc3cb44070db94e5dccbd035de8400a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000").to_vec()), + decode_header(hex!("f9035aa0dec5be85f92346bb546787db811d23fd88c001d05403f19295273d27a517efdfa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479453387f3321fd69d1e030bb921230dfb188826affa06200e3a8d1b95f53f80ede9a9a721e873085f8df88cbc0b26635b3ee4448ae6aa0430269c352181fe7f46adcc0792dc6973264279fd29bd56af95f9dbf7d42137aa05bdd6a4d4323421b7ade5c786051b9b8b82d8d383c9fd4e4239ce415308e61edb90100b7bef4feffafbcfbdfdfbbf6f6fed3e7bd3effabd7fff76e7bbf73dfdffff5f4edfffe7f5ffedffd7def9f7feb8ebf6ff7bfff9edf7fff7b7ffefb7fbffe77fefe7a9fbfdff7ffff7f97bdf9cf3d6df777fd7ebf6f6ed7eef7d37f756ff3fefffedef7ea3a76f271dd8793b3abc7cfff3ffdeebbf979defff76befdfbbbef3fff9fafbcffffffff2af7eb7bf7ffdffffffffb5ebdbfddd8e9fbbf7fcdfcffdefdeeab71f9ff86f7f7f5e67dff3bedeeaffffedfdbb7bf5e7bb9dffa7f7ff57f773f3f19fffe7bfbc7fd77fbe5bb9ffe7cfeebf9dadfbbbd85fffffefd9ebbd5bff7ffffcd7fbefbf3f77fbbfdff57ff1fed8fffebabbf3fffbf7dd2effbffffe028402b1eb8c8405e40b5884013b6464846723447bb90116d98301040f846765746889676f312e32312e3133856c696e757800000299d9bcf8b381fbb8609126e078b6533f67623a500cbde29ac8153b1e5752d415aa71d75c0046c934da40407f23baa58a466a355a493b9e39c7107ab54ec3a1de320f26c0db35b3f54a4757f3564679f1619d52d4fd26d7ce680f60cf5fa6f67ec01f7e1c34b8b4986af84c8402b1eb8aa00113d3d7778af93b122871e02432ded0cc60c8c9fe1cedab69465bc2ed750f298402b1eb8ba0dec5be85f92346bb546787db811d23fd88c001d05403f19295273d27a517efdf80a7a4d25360dc7cac1c8c8a247e8d6b0a34bd97c689b382ef3d9ec726a74d9c4f3ea17ae295877d17b8a1f4228788fe98ddd627cf68ee27b973660a670ea5581f01a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000").to_vec()), + ]; + let headers = ETHHeaders { + target: v[0].clone(), + all: v.clone(), + }; + let result = headers.verify_finalized().unwrap(); + assert_eq!(result.0.number, v[3].number); + assert_eq!(result.1.number, v[4].number); + } + + #[test] + fn test_error_verify_finalized_with_many_headers() { + let v = vec![ + //https://testnet.bscscan.com/block/45214600 + decode_header(hex!("f9057ea06e94ae7ef8cc013f1ddff4519d44823bdb508e10cec2d8c2e191d27f741cbf8ea01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479440d3256eb0babe89f0ea54edaa398513136612f5a0766f9938068e5a9fa592451105996d65e587e49b90cc931b243db46891830fc4a0f070fccfa4d96d319030acf9bd07ad61df24cf24ae4c581df633739a6bb2d33da0fea7ba2221ca1582bece7533eb42f6f295d4dbc03e62f14478168e287aad5734b90100040010400040000000000040001000000241000800000000c0000000020400000000104800010000008210000000000000000000020000400100040000200000000000200004000300000008000200002110480000000000a00200004020000004480028a0020001000400000400450008600400000000000020001200000800000808000000000000000000000000000000040000000002240000008000082802008040000000200820000002800042000000000000100002002000000001000000000200000000100804200000000042400000000002c00010480200000000001008000000000001100084000001040008c000008200004040200000000000028402b1eb888405efeb20830ba24a846723446fb90338d88301040e846765746888676f312e32312e34856c696e75780000000299d9bc0808265da01e1a65d62b903c7b34c08cb389bf3d9996f763f030b1adcfb369c5a5df4a18e1529baffe7feaec66db3dbd1bc06810f7f6f88b7be6645418a7e2a2a3f40514c21a3d9d7a717d64e6088ac937d5aacdd3e20ca963979974cd8ff90cbf097023dc8c448245ceff671e965d57d82eaf9be91478cfa0f24d2993e0c5f43a6c5a4cd99850023040d3256eb0babe89f0ea54edaa398513136612f5a334b49d766ebe3eb9f6bdc163bd2c19aa7e8cee1667851ae0c1651f01c4cf7cf2cfcf8475bff3e99cab25b05631472d53387f3321fd69d1e030bb921230dfb188826affaa39ebf1c38b190851e4db0588a3e90142c5299041fb8a0db3bb9a1fa4bdf0dae84ca37ee12a6b8c26caab775f0e007b76d76ee8823de52a1a431884c2ca930c5e72bff3803af79641cf964cc001671017f0b680f93b7dde085b24bbc67b2a562a216f903ac878c5477641328172a353f1e493cf7f5f2cf1aec83bf0c74df566a41aa7ed65ea84ea99e3849ef31887c0f880a0feb92f356f58fbd023a82f5311fc87a5883a662e9ebbbefc90bf13aa533c2438a4113804bfd447b49cd040d20bc21e49ffea6487f5638e4346ad9fc6d1ec30e28016d3892b51a7898bd354cfe78643453fd3868410da412de7f2883180d0a2840111ad2e043fa403ebf9a1db0d6f22bd78ffaeccbc8f47c83df9fbdbcfaade0f78a6b92b38c9f6d45ce8fb01da2b800100201cf0936b6b4b14c98af22edbe27df8aa197fca733891b5b6ca95db04f8b381fbb860a12aed22e41385aa96efa4536ed81355b069fae16992fd46f866f4b2767c09436517e5dbc349d48c2f863cccc472b3ce096d0376779428dd5cd1b28078d5022e529e51c1c97db640b7f1c5b11b1ffb7e7208acf95986a45a0d400a46f0c4c11cf84c8402b1eb86a01125d5208ff92b1ea97d01dadcd5e277f52e143916288d671d5fec8860c3e6b48402b1eb87a06e94ae7ef8cc013f1ddff4519d44823bdb508e10cec2d8c2e191d27f741cbf8e80daa9d3b546986bceb93f5afd160958d6d32f6d9ef81a70b21802a455dad7b9157accef88cd0e459bffd3c1105a0dcb53d877091d8bf84d706274fe7f43c5093f01a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218302000080a00000000000000000000000000000000000000000000000000000000000000000").to_vec()), + decode_header(hex!("f90359a04e47d8db81e515d75a96f36ee409190c402038c06cf05dd5bca71d1099c7cab6a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479440d3256eb0babe89f0ea54edaa398513136612f5a0447b10cfc9066c11310db781e39fcebcb9740ba67d4dc550e977be01907defc9a0f56e8363a75117d54a2fe8a7116b5044cb194495745d593d975cab1de018970ba057caa07d7d270fd85713bfdc42240142e48a0cc2458139e862b81413bbc11571b901000000000000000000200000400000100000000000000000000000000020000000000010000000000000000000000000000000000000800000010000000000000000000020000000020001000800000000201000000000000080000000000008000008002020020000000000000000c000084000000000000000000010000000000000400000000000000000000000000000000400000000000400000000000020000080000000002008000000020000000000000000000000020020000000000000000002000000000000000000000000000000000000008000105002002000000000000000000000010000040000030010008400000800004000000000010000028402b1eb898405e9fb36831bdde78467234472b90116d88301040e846765746888676f312e32312e34856c696e75780000000299d9bcf8b381fbb860a8ea6d387b7800147ad2155132dfa95d187f56ebd1c9ece1c8364df305761dfbd516d93423f8c57045228b7ef52fcde104c094aee3794c72efcd475311f88b6d3082f3ee399fd2b9cf58cd3c63350c7cba410fb43e219f419b4b57102daa6378f84c8402b1eb87a06e94ae7ef8cc013f1ddff4519d44823bdb508e10cec2d8c2e191d27f741cbf8e8402b1eb88a04e47d8db81e515d75a96f36ee409190c402038c06cf05dd5bca71d1099c7cab6807a03301635f5c73d1ee0f095f33c697badc2560110a3add7f7d59c995eefac4910b711317255a198ba93410bb88c7e97f860c906d1dc60229a06c551760b3bce00a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000").to_vec()), + decode_header(hex!("f902a3a0a845b19d537d80fc8ae29c4aed49bd6578f4eb5089632d40f708588f6970ebf4a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479440d3256eb0babe89f0ea54edaa398513136612f5a04b50b580bbd86f6e198fd9c085f837109c6ff39053625f4a11c7aee0e2cfb63aa0abbe999c7c87fe7427d44e0b0aaf8918a723b17744678bd9ba209368cc82f8a2a08189d28f2391444d7c4868cea83360c6109abe993f55a7d2429d383632c6947bb9010000000000800000000000004000100200000000000004000000000000000800000081100000000000000000000000000000000400000000000100000000000002008000200000000200000008000400002010000000000000800000000000000000080020201200001000000000004000084000100000000008000010800000000400000400000000000004000000000000000480000000000440000000008020400080000000002108000000020000000040000000000000020020000000000000000002000000000000000000000000000000000000008020105402000000000000000000000000010000040000030000008400000000204100000000010800028402b1eb8a8405e4113c8306e52c8467234475b861d88301040e846765746888676f312e32312e34856c696e75780000000299d9bcd0a2b2dcf96ddbb4e8c201a2845fb962fa5a8ea571b6f3fc5c6f30f515cbcc4470ea5545b435334fcb6165032a91423c6803d3771c9874a9a69107e2a27e484e00a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000").to_vec()), + decode_header(hex!("f90359a00113d3d7778af93b122871e02432ded0cc60c8c9fe1cedab69465bc2ed750f29a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479440d3256eb0babe89f0ea54edaa398513136612f5a01303affe211de58db87dca446d098e0dee67f3aa4edda393b2e7435f1d706283a0118805b4e132609bbab79efe56918cc539694253ed4555b2d13fe2165480ddf1a0f642c960ec442a236bb3f0749e5be8484773a16493bd9d429ed6929662248001b9010004000000000000000000004000000004000000000000000080000000000400000000100000010000008000000000000000000010000000000100000000200000000000200200800200000008000020002010000000000001800000000000000000080020200200000000000100004000084000000100000000000010000000000000000000000000000000000000000100000400000000000400000080000020020080000000022008000000020000000000000000000000020020000000010010000082000000200040000000000000020000002000008000104042000000000010000000000000010000040000014000008000000000006000000000000000028402b1eb8b8405de2d2c830424308467234478b90116d88301040e846765746888676f312e32312e34856c696e75780000000299d9bcf8b381fbb860b4fa72afe0a0b517bee1ae6ccec530926fedf8abda9f2155d70b4591d6a1cc941d7589410a3e3e61cb004d890c8b4b8002854de11c87173ba7a9dcfd32b63a58b3ffa1f3ac4847a63a4cda1aed80e3b74bafeb1350e9ab5210d81658860757c8f84c8402b1eb88a04e47d8db81e515d75a96f36ee409190c402038c06cf05dd5bca71d1099c7cab68402b1eb8aa00113d3d7778af93b122871e02432ded0cc60c8c9fe1cedab69465bc2ed750f29808b1762cfd0533f76a6853bc389b57c07d84255fcdffb8ec429753ed0cf941e4a1646277e4462b3136f00536f86910b861312fc3cb44070db94e5dccbd035de8400a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000").to_vec()), + decode_header(hex!("f9035aa0dec5be85f92346bb546787db811d23fd88c001d05403f19295273d27a517efdfa01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479453387f3321fd69d1e030bb921230dfb188826affa06200e3a8d1b95f53f80ede9a9a721e873085f8df88cbc0b26635b3ee4448ae6aa0430269c352181fe7f46adcc0792dc6973264279fd29bd56af95f9dbf7d42137aa05bdd6a4d4323421b7ade5c786051b9b8b82d8d383c9fd4e4239ce415308e61edb90100b7bef4feffafbcfbdfdfbbf6f6fed3e7bd3effabd7fff76e7bbf73dfdffff5f4edfffe7f5ffedffd7def9f7feb8ebf6ff7bfff9edf7fff7b7ffefb7fbffe77fefe7a9fbfdff7ffff7f97bdf9cf3d6df777fd7ebf6f6ed7eef7d37f756ff3fefffedef7ea3a76f271dd8793b3abc7cfff3ffdeebbf979defff76befdfbbbef3fff9fafbcffffffff2af7eb7bf7ffdffffffffb5ebdbfddd8e9fbbf7fcdfcffdefdeeab71f9ff86f7f7f5e67dff3bedeeaffffedfdbb7bf5e7bb9dffa7f7ff57f773f3f19fffe7bfbc7fd77fbe5bb9ffe7cfeebf9dadfbbbd85fffffefd9ebbd5bff7ffffcd7fbefbf3f77fbbfdff57ff1fed8fffebabbf3fffbf7dd2effbffffe028402b1eb8c8405e40b5884013b6464846723447bb90116d98301040f846765746889676f312e32312e3133856c696e757800000299d9bcf8b381fbb8609126e078b6533f67623a500cbde29ac8153b1e5752d415aa71d75c0046c934da40407f23baa58a466a355a493b9e39c7107ab54ec3a1de320f26c0db35b3f54a4757f3564679f1619d52d4fd26d7ce680f60cf5fa6f67ec01f7e1c34b8b4986af84c8402b1eb8aa00113d3d7778af93b122871e02432ded0cc60c8c9fe1cedab69465bc2ed750f298402b1eb8ba0dec5be85f92346bb546787db811d23fd88c001d05403f19295273d27a517efdf80a7a4d25360dc7cac1c8c8a247e8d6b0a34bd97c689b382ef3d9ec726a74d9c4f3ea17ae295877d17b8a1f4228788fe98ddd627cf68ee27b973660a670ea5581f01a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218080a00000000000000000000000000000000000000000000000000000000000000000").to_vec()), + // needless extra header 45214605 + decode_header(hex!("f9035ca09b8a254f9d47b514499f4a04cbefcdd56187a817bb0c63bac0ce4d13286109c0a01dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d493479453387f3321fd69d1e030bb921230dfb188826affa0421c3102871a05a9979c407de975640782787a7b8f7a392961c7c64e3c85e325a08cf4795784183f5c2ec8042a2da4881f794035a0f0aff1849e664268fd7591ffa086c1602ec35d3220bd9e102fd1b6a2ac3041a92b0a0b4ec3efd33d05bc7792a7b901001620000a28280d08851831c5860a2f14310001205813003088a10c0038240c285213596b11a1091000f86014002000005438633a104008000121003a41300c41c04244012a50c400c03c149878acb110a830d80851081c07881040052342104018180e68a802e10204404483040884200871c20419102302844065be030480328425c072500290600528008801000a0140441420021a644905051c888d20d1a006108c2531418733aa83840002812a0898000d04a20240a186122438040011011692d08322c214001440122693e611c4c81b0002c21482c58410444a2ca482000c320a806420045e0321081460320578804a90090113082071840020ca196304028402b1eb8d8405e9ef628339ba31846723447eb90116d98301040f846765746889676f312e32312e3133856c696e757800000299d9bcf8b381fbb86092d8322498605243d598d94daa1e8811ea1dca1329a85b5e469c5db21ccacc0d8b98ceb7d2c38f03f9e28aa9491c7b5808e6186eda83665c1e44642e84505974fe94cad46da0fda27cc8226c669b2c4ba4665fcf5a5080d420d08bfb7f1ef657f84c8402b1eb8ba0dec5be85f92346bb546787db811d23fd88c001d05403f19295273d27a517efdf8402b1eb8ca09b8a254f9d47b514499f4a04cbefcdd56187a817bb0c63bac0ce4d13286109c08011235fdd3e6e156439d7bf240c25457ac219d545129108ead575663444f503d6244e3f979c3a7f12040eb5f9d33832c03be3ed674467897d2294e4aedfd96c3800a0000000000000000000000000000000000000000000000000000000000000000088000000000000000080a056e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b4218304000080a00000000000000000000000000000000000000000000000000000000000000000").to_vec()), + ]; + let headers = ETHHeaders { + target: v[0].clone(), + all: v.clone(), + }; + let result = headers.verify_finalized(); + match result.unwrap_err() { + Error::UnexpectedTooManyHeadersToFinalize(e1, e2) => { + assert_eq!(e1, headers.target.number, "block error"); + assert_eq!(e2, headers.all.len(), "header size"); + } + e => unreachable!("{:?}", e), + } + } + #[rstest] #[case::localnet(localnet())] fn test_error_verify_too_many_headers_to_seal(#[case] hp: Box) { @@ -532,7 +705,7 @@ mod test { // set n_val next.extra_data = n_val_header.extra_data.clone(); let (validators, turn_length) = - get_validator_bytes_and_tern_term(&next.extra_data).unwrap(); + get_validator_bytes_and_turn_length(&next.extra_data).unwrap(); next.epoch = Some(Epoch::new(validators.into(), turn_length)); } headers.all.push(next); diff --git a/light-client/src/header/hardfork.rs b/light-client/src/header/hardfork.rs new file mode 100644 index 0000000..2b2db29 --- /dev/null +++ b/light-client/src/header/hardfork.rs @@ -0,0 +1,2 @@ +pub const MINIMUM_TIMESTAMP_SUPPORTED: u64 = 0; +pub const MINIMUM_HEIGHT_SUPPORTED: u64 = 0; diff --git a/light-client/src/header/mod.rs b/light-client/src/header/mod.rs index f87c04f..534f949 100644 --- a/light-client/src/header/mod.rs +++ b/light-client/src/header/mod.rs @@ -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 { @@ -92,6 +93,12 @@ impl Header { } } +/// Verifies the vote attestation of the current `ETHHeader` against its parent header. +/// +/// This function checks the vote attestation of the current header to ensure that +/// the target block is the direct parent of the current block and the source block +/// is the highest justified block. +/// fn verify_epoch<'a>( consensus_state: &ConsensusState, target: ÐHeader, @@ -273,6 +280,10 @@ pub(crate) mod test { &self.headers } + pub(crate) fn eth_header_mut(&mut self) -> &mut ETHHeaders { + &mut self.headers + } + pub(crate) fn new( account_proof: Vec, headers: ETHHeaders, @@ -455,7 +466,7 @@ pub(crate) mod test { let trusted_height = new_height(0, 201); let current_epoch = &hp.epoch_header().epoch.unwrap(); let previous_epoch = &Epoch::new(to_validator_set([1u8; 32]), 1); - let (c_val, p_val) = verify_epoch( + let (c_val, _) = verify_epoch( &cs, &hp.epoch_header(), height, @@ -465,9 +476,7 @@ pub(crate) mod test { ) .unwrap(); match c_val { - EitherEpoch::Untrusted(r) => { - assert!(r.try_borrow(&p_val).is_err()) - } + EitherEpoch::Untrusted(_r) => {} _ => unreachable!("unexpected trusted"), } diff --git a/light-client/src/header/vote_attestation.rs b/light-client/src/header/vote_attestation.rs index 470bd87..fb05570 100644 --- a/light-client/src/header/vote_attestation.rs +++ b/light-client/src/header/vote_attestation.rs @@ -45,7 +45,11 @@ pub struct VoteAttestation { } impl VoteAttestation { - pub fn verify(&self, number: BlockNumber, validators: &Validators) -> Result<(), Error> { + pub fn verify( + &self, + number: BlockNumber, + validators: &Validators, + ) -> Result { if self.vote_address_set.count() > validators.len() { return Err(Error::UnexpectedVoteAddressCount( number, @@ -53,6 +57,7 @@ impl VoteAttestation { validators.len(), )); } + let mut voted = Vec::new(); let mut voted_addr = Vec::new(); for (i, val) in validators.iter().enumerate() { if !self.vote_address_set.get(i) { @@ -62,6 +67,7 @@ impl VoteAttestation { let bls_pub_key = PublicKey::from_bytes(bls_pub_key_bytes) .map_err(|e| Error::UnexpectedBLSPubkey(number, e))?; voted_addr.push(bls_pub_key); + voted.push(val.clone()) } let required = ceil_div(validators.len() * 2, 3); @@ -82,7 +88,7 @@ impl VoteAttestation { pub_keys_ref.len(), )); } - Ok(()) + Ok(voted) } } @@ -148,7 +154,7 @@ impl<'a> TryFrom> for VoteAttestation { mod test { use crate::errors::Error; use crate::fixture::*; - use crate::header::eth_header::{get_validator_bytes_and_tern_term, ETHHeader}; + use crate::header::eth_header::{get_validator_bytes_and_turn_length, ETHHeader}; use crate::header::vote_attestation::{ VoteAddressBitSet, VoteAttestation, VoteData, BLS_SIGNATURE_LENGTH, MAX_ATTESTATION_EXTRA_LENGTH, @@ -239,7 +245,7 @@ mod test { hash: [0u8; 32], epoch: None, }; - let (val, turn) = get_validator_bytes_and_tern_term(&header.extra_data).unwrap(); + let (val, turn) = get_validator_bytes_and_turn_length(&header.extra_data).unwrap(); assert_eq!(val.len(), 8); assert_eq!(turn, 4); let err = header.get_vote_attestation().unwrap_err();