Skip to content

Commit ca42d6e

Browse files
committed
wip
1 parent b95d236 commit ca42d6e

File tree

12 files changed

+419
-145
lines changed

12 files changed

+419
-145
lines changed

Cargo.lock

Lines changed: 25 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ members = [
192192
"voyager/modules/finality/tendermint",
193193
"voyager/modules/finality/trusted-evm",
194194
"voyager/modules/finality/sui",
195+
"voyager/modules/finality/attested-evm",
195196

196197
"voyager/plugins/client-update/base",
197198
"voyager/plugins/client-update/bob",

cosmwasm/ibc-union/lightclient/attested/src/contract.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::{
99
client::AttestedLightClient,
1010
contract::{
1111
execute::{add_attestor, attest, confirm_attestation, remove_attestor, set_quorum},
12-
query::{attested_value, attestors, quorum, timestamp_at_height},
12+
query::{attested_value, attestors, latest_height, quorum, timestamp_at_height},
1313
},
1414
errors::Error,
1515
msg::{ExecuteMsg, QueryMsg, RestrictedExecuteMsg},
@@ -64,6 +64,7 @@ pub fn query(deps: Deps, env: Env, msg: QueryMsg) -> Result<Binary, Error> {
6464
QueryMsg::TimestampAtHeight { chain_id, height } => Ok(to_json_binary(
6565
&timestamp_at_height(deps, chain_id, height)?,
6666
)?),
67+
QueryMsg::LatestHeight { chain_id } => Ok(to_json_binary(&latest_height(deps, chain_id)?)?),
6768
QueryMsg::LightClient(msg) => {
6869
ibc_union_light_client::query::<AttestedLightClient>(deps, env, msg)
6970
.map_err(StdError::from)

cosmwasm/ibc-union/lightclient/attested/src/contract/query.rs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use std::{collections::BTreeSet, num::NonZero};
33
use cosmwasm_std::{Deps, Order};
44
use depolama::{Bytes, StorageExt};
55
use ibc_union_light_client::spec::Timestamp;
6+
use serde::{Deserialize, Serialize};
67
use unionlabs::primitives::H256;
78

89
use crate::{
@@ -52,3 +53,22 @@ pub fn timestamp_at_height(
5253
.maybe_read::<HeightTimestamps>(&(chain_id, height))
5354
.map_err(Into::into)
5455
}
56+
57+
pub fn latest_height(deps: Deps, chain_id: String) -> Result<Option<LatestHeight>, Error> {
58+
deps.storage
59+
.iter_range::<HeightTimestamps>(
60+
Order::Descending,
61+
(chain_id.clone(), 0)..=(chain_id.clone(), u64::MAX),
62+
)
63+
.next()
64+
.map(|r| r.map(|((_, height), timestamp)| LatestHeight { height, timestamp }))
65+
.transpose()
66+
.map_err(Into::into)
67+
}
68+
69+
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
70+
#[serde(deny_unknown_fields, rename_all = "snake_case")]
71+
pub struct LatestHeight {
72+
pub height: u64,
73+
pub timestamp: Timestamp,
74+
}

cosmwasm/ibc-union/lightclient/attested/src/msg.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,8 @@ pub enum QueryMsg {
6767
#[serde(with = "serde_utils::string")]
6868
height: u64,
6969
},
70+
/// Returns the latest height and timestamp attested to for `chain_id`.
71+
LatestHeight { chain_id: String },
7072
#[serde(untagged)]
7173
LightClient(ibc_union_light_client::msg::QueryMsg),
7274
}

cosmwasm/ibc-union/lightclient/attested/src/tests.rs

Lines changed: 91 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ use unionlabs::{
2121

2222
use crate::{
2323
client::{verify_attestation, verify_header},
24-
contract::{execute, migrate, query},
24+
contract::{execute, migrate, query, query::LatestHeight},
2525
errors::Error,
2626
msg::{ExecuteMsg, QueryMsg, RestrictedExecuteMsg},
2727
types::{Attestation, AttestationValue},
@@ -993,3 +993,93 @@ fn attestors_unique_per_chain() {
993993
.collect::<BTreeSet<_>>(),
994994
);
995995
}
996+
997+
#[test]
998+
fn latest_height() {
999+
let (mut deps, env) = setup();
1000+
1001+
reach_quorum(
1002+
&mut deps,
1003+
&env,
1004+
Attestation {
1005+
chain_id: CHAIN_ID.to_owned(),
1006+
height: 1,
1007+
timestamp: Timestamp::from_secs(1),
1008+
key: b"key".into(),
1009+
value: AttestationValue::NonExistence,
1010+
},
1011+
[&*ATTESTOR_1, &*ATTESTOR_2],
1012+
);
1013+
1014+
// no heights attested to for this chain
1015+
assert_query_result(
1016+
deps.as_ref(),
1017+
&env,
1018+
QueryMsg::LatestHeight {
1019+
chain_id: "998".to_owned(),
1020+
},
1021+
&None::<LatestHeight>,
1022+
);
1023+
1024+
assert_query_result(
1025+
deps.as_ref(),
1026+
&env,
1027+
QueryMsg::LatestHeight {
1028+
chain_id: CHAIN_ID.to_owned(),
1029+
},
1030+
&Some(LatestHeight {
1031+
height: 1,
1032+
timestamp: Timestamp::from_secs(1),
1033+
}),
1034+
);
1035+
1036+
reach_quorum(
1037+
&mut deps,
1038+
&env,
1039+
Attestation {
1040+
chain_id: CHAIN_ID.to_owned(),
1041+
height: 2,
1042+
timestamp: Timestamp::from_secs(1),
1043+
key: b"key".into(),
1044+
value: AttestationValue::NonExistence,
1045+
},
1046+
[&*ATTESTOR_1, &*ATTESTOR_2],
1047+
);
1048+
1049+
assert_query_result(
1050+
deps.as_ref(),
1051+
&env,
1052+
QueryMsg::LatestHeight {
1053+
chain_id: CHAIN_ID.to_owned(),
1054+
},
1055+
&Some(LatestHeight {
1056+
height: 2,
1057+
timestamp: Timestamp::from_secs(1),
1058+
}),
1059+
);
1060+
1061+
reach_quorum(
1062+
&mut deps,
1063+
&env,
1064+
Attestation {
1065+
chain_id: CHAIN_ID.to_owned(),
1066+
height: u64::MAX,
1067+
timestamp: Timestamp::from_secs(1),
1068+
key: b"key".into(),
1069+
value: AttestationValue::NonExistence,
1070+
},
1071+
[&*ATTESTOR_1, &*ATTESTOR_2],
1072+
);
1073+
1074+
assert_query_result(
1075+
deps.as_ref(),
1076+
&env,
1077+
QueryMsg::LatestHeight {
1078+
chain_id: CHAIN_ID.to_owned(),
1079+
},
1080+
&Some(LatestHeight {
1081+
height: u64::MAX,
1082+
timestamp: Timestamp::from_secs(1),
1083+
}),
1084+
);
1085+
}

lib/voyager-primitives/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,9 @@ impl ConsensusType {
257257
/// [L2 settlement]: https://github.com/ethereum-optimism/optimism/tree/develop/packages/contracts-bedrock
258258
pub const OPTIMISM: &'static str = "optimism";
259259

260+
/// An attested client.
261+
pub const ATTESTED: &'static str = "attested";
262+
260263
// lots more to come - near, linea, polygon - stay tuned
261264
}
262265

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
[package]
2+
name = "voyager-finality-module-attested-evm"
3+
version = "0.0.0"
4+
5+
authors = { workspace = true }
6+
edition = { workspace = true }
7+
license-file = { workspace = true }
8+
publish = { workspace = true }
9+
repository = { workspace = true }
10+
11+
[lints]
12+
workspace = true
13+
14+
[dependencies]
15+
alloy = { workspace = true, features = ["rpc", "rpc-types", "transports", "transport-http", "transport-ws", "reqwest", "reqwest-rustls-tls", "provider-ws"] }
16+
attested-light-client = { workspace = true }
17+
attested-light-client-types = { workspace = true, features = ["serde"] }
18+
clap = { workspace = true, features = ["derive"] }
19+
cometbft-rpc = { workspace = true }
20+
embed-commit = { workspace = true }
21+
ibc-union-spec = { workspace = true, features = ["serde"] }
22+
jsonrpsee = { workspace = true, features = ["macros", "server", "tracing"] }
23+
prost = { workspace = true }
24+
protos = { workspace = true }
25+
serde = { workspace = true, features = ["derive"] }
26+
serde_json = { workspace = true }
27+
thiserror = { workspace = true }
28+
tokio = { workspace = true }
29+
tracing = { workspace = true }
30+
unionlabs = { workspace = true }
31+
voyager-sdk = { workspace = true }

0 commit comments

Comments
 (0)