Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
46 commits
Select commit Hold shift + click to select a range
1074c97
add milli_timestamp
Mar 5, 2025
524c4a1
make several header field private
Mar 5, 2025
dc10b25
update test
Mar 5, 2025
30c669e
Merge branch 'feature/fork_state' into feature/rolentz_msec
Mar 17, 2025
f089201
change epoch length
Mar 17, 2025
c3981a8
fix clippy
Mar 17, 2025
66ff49c
fix conflict
Mar 17, 2025
4752948
refactor
Mar 17, 2025
b4600e1
refactor
Mar 17, 2025
ac5c3af
refactor
Mar 17, 2025
aa0f97d
modify all
Mar 19, 2025
5b00406
update
Mar 19, 2025
405a56d
test
Mar 19, 2025
736a66c
fmt
Mar 19, 2025
171e424
fix test
Mar 19, 2025
b4caad3
fix lint
Mar 19, 2025
92a2cd8
add test
Mar 21, 2025
f1d7a7a
add test
Mar 21, 2025
954d09c
add test
Mar 21, 2025
bafe41f
add test
Mar 21, 2025
232af0e
add test
Mar 21, 2025
9c2b70a
fix clippy
Mar 21, 2025
60be31f
change comment
Mar 21, 2025
dfb4097
change turn length threshold
Mar 21, 2025
7abf528
remove how to change epoch length
Mar 22, 2025
aa04e8d
change turn length validation
Mar 22, 2025
6a471be
Merge branch 'feature/rolentz_msec' into feature/lorentz_seemless
Mar 22, 2025
4f406d0
add test
Mar 23, 2025
fe882f2
fix fmt
Mar 23, 2025
0084d73
change canonicalize
Mar 23, 2025
1f6c1df
tweak
Mar 24, 2025
a49bf94
check strict
Mar 26, 2025
3f7931f
fix-lint
Mar 26, 2025
bb8a848
add max_turn_length into ForkSpec
Mar 26, 2025
23b2e38
add test
Mar 27, 2025
24a05f2
refactor
Mar 27, 2025
ea54f2c
fix clippy
Mar 27, 2025
857cc4c
change comment
Mar 27, 2025
54064d6
Merge branch 'feature/fork_state' into feature/rolentz_msec
Apr 1, 2025
a479930
fix conflict
Apr 1, 2025
4689816
fix copilot review
Apr 1, 2025
48ae609
add constant
Apr 7, 2025
b9685da
Merge branch 'feature/fork_state' into feature/rolentz_msec
Apr 7, 2025
4a57acf
fix conflict
Apr 7, 2025
a149c01
add comment
Apr 7, 2025
2ab7fc1
Merge pull request #86 from datachainlab/feature/lorentz_seemless
yoshidan Apr 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 4 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,19 +19,11 @@ NOTE: This project is currently under heavy development. Features may change or
Environment variables can be used to change settings.
Each configuration must be determined at build time, not at run time.

### Blocks per epoch
You can change the blocks per epoch for localnet.
This is available in dev feature only.

```sh
BSC_BLOCKS_PER_EPOCH=20 cargo build --features=dev
```

### Build Parameters

Parameters can be specified to check for acceptable headers at build time.

| Name | Description |
| --- |-----------------------------------------------------------------------------------------------------------------------------|
| `MINIMUM_TIMESTAMP_SUPPORTED` | Timestamp of the lowest header this light client will accept. All the ForkSpec must be greater than or equal to this value. |
| `MINIMUM_HEIGHT_SUPPORTED` | Height of the lowest header this light client will accept. All the ForkSpec must be greater than or equal to this value. |
| Name | Description |
| --- |------------------------------------------------------------------------------------------------------------------------------|
| `MINIMUM_TIMESTAMP_SUPPORTED` | Timestamp(millisecond) of the lowest header this light client will accept. All the ForkSpec must be greater than or equal to this value. |
| `MINIMUM_HEIGHT_SUPPORTED` | Height of the lowest header this light client will accept. All the ForkSpec must be greater than or equal to this value. |
14 changes: 0 additions & 14 deletions light-client/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,6 @@ fn main() {
use std::io::Write;
let mut file = std::fs::File::create("src/header/constant.rs").unwrap();
let mut values: Vec<String> = vec![];
#[cfg(feature = "dev")]
{
let blocks_per_epoch =
std::env::var("BSC_BLOCKS_PER_EPOCH").unwrap_or_else(|_| "200".to_string());
values.push(format!(
"pub const BLOCKS_PER_EPOCH: u64 = {};",
blocks_per_epoch
));
}
#[cfg(not(feature = "dev"))]
{
values.push("pub const BLOCKS_PER_EPOCH: u64 = 200;".to_string());
}

let minimum_time_stamp_supported =
std::env::var("MINIMUM_TIMESTAMP_SUPPORTED").unwrap_or_else(|_| "0".to_string());
values.push(format!(
Expand Down
129 changes: 110 additions & 19 deletions light-client/src/client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ impl LightClient for ParliaLightClient {
}
}

const NANO_TO_MILLIS: u128 = 1_000_000;

struct InnerLightClient;

impl InnerLightClient {
Expand All @@ -166,11 +168,13 @@ impl InnerLightClient {

let height = client_state.latest_height;
let timestamp = consensus_state.timestamp;
let milli_timestamp = (timestamp.as_unix_timestamp_nanos() / NANO_TO_MILLIS) as u64;

#[allow(clippy::absurd_extreme_comparisons)]
if timestamp.as_unix_timestamp_secs() < MINIMUM_TIMESTAMP_SUPPORTED {
if milli_timestamp < 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));
Expand Down Expand Up @@ -369,13 +373,14 @@ impl InnerLightClient {
return Err(Error::ClientFrozen(client_id));
}

let mut misbehaviour = misbehaviour;
let trusted_consensus_state1 = ConsensusState::try_from(any_consensus_state1)?;
let trusted_consensus_state2 = ConsensusState::try_from(any_consensus_state2)?;
let new_client_state = client_state.check_misbehaviour_and_update_state(
ctx.host_timestamp(),
&trusted_consensus_state1,
&trusted_consensus_state2,
&misbehaviour,
&mut misbehaviour,
)?;

let prev_state = self.make_prev_states(
Expand Down Expand Up @@ -515,25 +520,18 @@ mod test {
use rstest::rstest;
use time::macros::datetime;

use crate::client::ParliaLightClient;
use crate::client::{ParliaLightClient, NANO_TO_MILLIS};
use crate::client_state::ClientState;
use crate::consensus_state::ConsensusState;

use crate::fixture::{localnet, Network};
use crate::fixture::{fork_spec_after_lorentz, fork_spec_after_pascal, localnet, Network};
use crate::header::Header;

use crate::fork_spec::{ForkSpec, HeightOrTimestamp};
use crate::fork_spec::HeightOrTimestamp;
use crate::misbehaviour::Misbehaviour;
use crate::misc::{new_height, Address, ChainId, Hash};
use alloc::boxed::Box;

fn after_pascal() -> ForkSpec {
ForkSpec {
height_or_timestamp: HeightOrTimestamp::Height(0),
additional_header_item_count: 1, // requestsHash
}
}

impl Default for ClientState {
fn default() -> Self {
ClientState {
Expand All @@ -546,7 +544,7 @@ mod test {
max_clock_drift: core::time::Duration::new(1, 0),
latest_height: Default::default(),
frozen: false,
fork_specs: vec![after_pascal()],
fork_specs: vec![fork_spec_after_pascal(), fork_spec_after_lorentz()],
}
}
}
Expand Down Expand Up @@ -638,10 +636,13 @@ mod test {
assert_eq!(data.post_height, result.height);

let cs = ConsensusState::try_from(any_consensus_state).unwrap();
assert_eq!(data.timestamp.as_unix_timestamp_secs(), timestamp);
assert_eq!(
data.timestamp.as_unix_timestamp_secs(),
cs.timestamp.as_unix_timestamp_secs()
(data.timestamp.as_unix_timestamp_nanos() / NANO_TO_MILLIS) as u64,
timestamp
);
assert_eq!(
data.timestamp.as_unix_timestamp_nanos() / NANO_TO_MILLIS,
cs.timestamp.as_unix_timestamp_nanos() / NANO_TO_MILLIS
);
assert_eq!(data.emitted_states[0].0, result.height);
assert_eq!(data.emitted_states[0].1, any_client_state);
Expand Down Expand Up @@ -801,6 +802,90 @@ mod test {
};
}

#[rstest]
#[case::localnet(localnet())]
fn test_error_update_state_non_epoch_boundary_epochs_is_timestamp(
#[case] hp: Box<dyn Network>,
) {
let input = hp.success_update_client_non_epoch_input();
let any: Any = input.header.try_into().unwrap();
let header = Header::try_from(any.clone()).unwrap();

let client = ParliaLightClient;
let client_id = ClientId::new(&client.client_type(), 1).unwrap();
let mut mock_consensus_state = BTreeMap::new();
let trusted_cs = ConsensusState {
current_validators_hash: input.trusted_current_validators_hash,
previous_validators_hash: input.trusted_previous_validators_hash,
..Default::default()
};
mock_consensus_state.insert(Height::new(0, input.trusted_height), trusted_cs.clone());

// Set fork spec is boundary timestamp
let mut boundary_fs = fork_spec_after_lorentz();
boundary_fs.height_or_timestamp =
HeightOrTimestamp::Time(header.eth_header().target.milli_timestamp());
let cs = ClientState {
chain_id: hp.network(),
ibc_store_address: hp.ibc_store_address(),
latest_height: Height::new(0, input.trusted_height),
fork_specs: vec![fork_spec_after_pascal(), boundary_fs],
..Default::default()
};
let ctx = MockClientReader {
client_state: Some(cs.clone()),
consensus_state: mock_consensus_state,
};
let err = client.update_client(&ctx, client_id, any).unwrap_err();
assert_err(err, "MissingForkHeightInBoundaryCalculation");
}

#[rstest]
#[case::localnet(localnet())]
fn test_success_update_state_non_epoch_update_fork_height(#[case] hp: Box<dyn Network>) {
let input = hp.success_update_client_non_epoch_input();
let any: Any = input.header.try_into().unwrap();
let header = Header::try_from(any.clone()).unwrap();

let client = ParliaLightClient;
let client_id = ClientId::new(&client.client_type(), 1).unwrap();
let mut mock_consensus_state = BTreeMap::new();
let trusted_cs = ConsensusState {
current_validators_hash: input.trusted_current_validators_hash,
previous_validators_hash: input.trusted_previous_validators_hash,
..Default::default()
};
mock_consensus_state.insert(Height::new(0, input.trusted_height), trusted_cs.clone());

// Set fork spec boundary timestamp is all[1]
let mut boundary_fs = fork_spec_after_lorentz();
boundary_fs.height_or_timestamp =
HeightOrTimestamp::Time(header.eth_header().all[1].milli_timestamp());
let cs = ClientState {
chain_id: hp.network(),
ibc_store_address: hp.ibc_store_address(),
latest_height: Height::new(0, input.trusted_height),
fork_specs: vec![fork_spec_after_pascal(), boundary_fs],
..Default::default()
};
let ctx = MockClientReader {
client_state: Some(cs.clone()),
consensus_state: mock_consensus_state,
};
let result = client.update_client(&ctx, client_id, any).unwrap();
let data = match result {
UpdateClientResult::UpdateState(data) => data,
_ => unreachable!("invalid client result"),
};
let new_client_state = ClientState::try_from(data.new_any_client_state).unwrap();

// update fork height
assert_eq!(
new_client_state.fork_specs[1].height_or_timestamp,
HeightOrTimestamp::Height(header.eth_header().all[1].number)
);
}

#[rstest]
#[case::localnet(localnet())]
fn test_success_update_state_continuous(#[case] hp: Box<dyn Network>) {
Expand Down Expand Up @@ -950,7 +1035,7 @@ mod test {
assert_err(
err,
&format!(
"UnexpectedTrustedHeight: {}",
"UnexpectedTrustedEpoch: {}",
trusted_height.revision_height()
),
);
Expand Down Expand Up @@ -1100,7 +1185,7 @@ mod test {
);
let ctx = MockClientReader {
client_state: Some(ClientState {
fork_specs: vec![after_pascal()],
fork_specs: vec![fork_spec_after_pascal(), fork_spec_after_lorentz()],
..Default::default()
}),
consensus_state: mock_consensus_state,
Expand Down Expand Up @@ -1171,7 +1256,7 @@ mod test {
let ctx = MockClientReader {
client_state: Some(ClientState {
chain_id: ChainId::new(9999),
fork_specs: vec![after_pascal()],
fork_specs: vec![fork_spec_after_pascal(), fork_spec_after_lorentz()],
..Default::default()
}),
consensus_state: mock_consensus_state.clone(),
Expand Down Expand Up @@ -1282,6 +1367,8 @@ mod test {
client_state.fork_specs = vec![ForkSpec {
height_or_timestamp: HeightOrTimestamp::Height(MINIMUM_HEIGHT_SUPPORTED - 1),
additional_header_item_count: 0,
epoch_length: 200,
max_turn_length: 9,
}];
(client_state, cons_state)
}));
Expand All @@ -1293,6 +1380,8 @@ mod test {
client_state.fork_specs = vec![ForkSpec {
height_or_timestamp: HeightOrTimestamp::Time(MINIMUM_TIMESTAMP_SUPPORTED - 1),
additional_header_item_count: 0,
epoch_length: 200,
max_turn_length: 9,
}];
(client_state, cons_state)
}));
Expand All @@ -1305,6 +1394,8 @@ mod test {
client_state.fork_specs = vec![ForkSpec {
height_or_timestamp: HeightOrTimestamp::Time(MINIMUM_TIMESTAMP_SUPPORTED),
additional_header_item_count: 0,
epoch_length: 200,
max_turn_length: 9,
}];
(client_state, cons_state)
}))
Expand Down
Loading
Loading