Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: use nanoseconds instead of seconds in sp1 light client #408

Merged
merged 17 commits into from
Mar 16, 2025
Merged
43 changes: 32 additions & 11 deletions contracts/light-clients/SP1ICS07Tendermint.sol
Original file line number Diff line number Diff line change
@@ -151,19 +151,19 @@ contract SP1ICS07Tendermint is ISP1ICS07TendermintErrors, ISP1ICS07Tendermint, I
/// @param proofHeight The height of the proof.
/// @param path The path of the key-value pair.
/// @param value The value of the key-value pair.
/// @return timestamp The timestamp of the trusted consensus state.
/// @return The timestamp of the trusted consensus state in unix seconds.
function _membership(
bytes calldata proof,
IICS02ClientMsgs.Height calldata proofHeight,
bytes[] calldata path,
bytes memory value
)
private
returns (uint256 timestamp)
returns (uint256)
{
if (proof.length == 0) {
// cached proof
return _getCachedKvPair(proofHeight.revisionHeight, IMembershipMsgs.KVPair(path, value));
return _nanosToSeconds(_getCachedKvPair(proofHeight.revisionHeight, IMembershipMsgs.KVPair(path, value)));
}

IMembershipMsgs.MembershipProof memory membershipProof = abi.decode(proof, (IMembershipMsgs.MembershipProof));
@@ -261,7 +261,7 @@ contract SP1ICS07Tendermint is ISP1ICS07TendermintErrors, ISP1ICS07Tendermint, I
if (output.kvPairs.length > 1) {
_cacheKvPairs(proofHeight.revisionHeight, output.kvPairs, proof.trustedConsensusState.timestamp);
}
return proof.trustedConsensusState.timestamp;
return _getTimestampInSeconds(proof.trustedConsensusState);
}

/// @notice The entrypoint for handling the `SP1MembershipAndUpdateClientProof` proof type.
@@ -360,7 +360,7 @@ contract SP1ICS07Tendermint is ISP1ICS07TendermintErrors, ISP1ICS07Tendermint, I
proofHeight.revisionHeight, output.kvPairs, output.updateClientOutput.newConsensusState.timestamp
);
}
return output.updateClientOutput.newConsensusState.timestamp;
return _getTimestampInSeconds(output.updateClientOutput.newConsensusState);
}

/// @notice Validates the MembershipOutput public values.
@@ -428,16 +428,19 @@ contract SP1ICS07Tendermint is ISP1ICS07TendermintErrors, ISP1ICS07Tendermint, I
/// @notice Validates the client state and time.
/// @dev This function does not check the equality of the latest height and isFrozen.
/// @param publicClientState The public client state.
/// @param time The time.
/// @param time The time in unix nanoseconds.
function _validateClientStateAndTime(
IICS07TendermintMsgs.ClientState memory publicClientState,
uint64 time
uint128 time
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not uint256? Seems like we are using that elsewhere here

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

rust returns nanos as u128. I think this is a nice reminder to us that this field is a nanos. Making us less likely to confuse.

)
private
view
{
require(time <= block.timestamp, ProofIsInTheFuture(block.timestamp, time));
require(block.timestamp - time <= ALLOWED_SP1_CLOCK_DRIFT, ProofIsTooOld(block.timestamp, time));
require(_nanosToSeconds(time) <= block.timestamp, ProofIsInTheFuture(block.timestamp, _nanosToSeconds(time)));
require(
block.timestamp - _nanosToSeconds(time) <= ALLOWED_SP1_CLOCK_DRIFT,
ProofIsTooOld(block.timestamp, _nanosToSeconds(time))
);

// Check client state equality
// NOTE: We do not check the equality of latest height and isFrozen, this is because:
@@ -508,7 +511,7 @@ contract SP1ICS07Tendermint is ISP1ICS07TendermintErrors, ISP1ICS07Tendermint, I
/// @notice Caches the key-value pairs to the transient storage with the timestamp.
/// @param proofHeight The height of the proof.
/// @param kvPairs The key-value pairs.
/// @param timestamp The timestamp of the trusted consensus state.
/// @param timestamp The timestamp of the trusted consensus state in unix nanoseconds.
/// @dev WARNING: Transient store is not reverted even if a message within a transaction reverts.
/// @dev WARNING: This function must be called after all proof and validation checks.
function _cacheKvPairs(uint64 proofHeight, IMembershipMsgs.KVPair[] memory kvPairs, uint256 timestamp) private {
@@ -521,7 +524,7 @@ contract SP1ICS07Tendermint is ISP1ICS07TendermintErrors, ISP1ICS07Tendermint, I
/// @notice Gets the timestamp of the cached key-value pair from the transient storage.
/// @param proofHeight The height of the proof.
/// @param kvPair The key-value pair.
/// @return The timestamp of the cached key-value pair.
/// @return The timestamp of the cached key-value pair in unix nanoseconds.
function _getCachedKvPair(
uint64 proofHeight,
IMembershipMsgs.KVPair memory kvPair
@@ -536,6 +539,24 @@ contract SP1ICS07Tendermint is ISP1ICS07TendermintErrors, ISP1ICS07Tendermint, I
return timestamp;
}

/// @notice Returns the timestamp of the trusted consensus state in unix seconds.
/// @param consensusState The consensus state.
/// @return The timestamp of the trusted consensus state in unix seconds.
function _getTimestampInSeconds(IICS07TendermintMsgs.ConsensusState memory consensusState)
private
pure
returns (uint256)
{
return _nanosToSeconds(consensusState.timestamp);
}

/// @notice Converts nanoseconds to seconds.
/// @param nanos The nanoseconds.
/// @return The seconds.
function _nanosToSeconds(uint256 nanos) private pure returns (uint256) {
return nanos / 1e9;
}

/// @notice Modifier to check if the client is not frozen.
modifier notFrozen() {
require(!clientState.isFrozen, FrozenClientState());
4 changes: 2 additions & 2 deletions contracts/light-clients/msgs/IICS07TendermintMsgs.sol
Original file line number Diff line number Diff line change
@@ -36,11 +36,11 @@ interface IICS07TendermintMsgs {

/// @notice Defines the Tendermint light client's consensus state at some height.
/// @param timestamp timestamp that corresponds to the counterparty block height
/// in which the ConsensusState was generated.
/// in which the ConsensusState was generated. (in unix nanoseconds)
/// @param root commitment root (i.e app hash)
/// @param nextValidatorsHash next validators hash
struct ConsensusState {
uint64 timestamp;
uint128 timestamp;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto on uint256

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not gonna write on the others, but any reason we mix?

bytes32 root;
bytes32 nextValidatorsHash;
}
4 changes: 2 additions & 2 deletions contracts/light-clients/msgs/IMisbehaviourMsgs.sol
Original file line number Diff line number Diff line change
@@ -17,14 +17,14 @@ interface IMisbehaviourMsgs {

/// @notice The public value output for the sp1 misbehaviour program.
/// @param clientState The client state that was used to verify the misbehaviour.
/// @param time The time which the misbehaviour was verified in seconds.
/// @param time The time which the misbehaviour was verified in unix nanoseconds.
/// @param trustedHeight1 The trusted height of header 1
/// @param trustedHeight2 The trusted height of header 2
/// @param trustedConsensusState1 The trusted consensus state of header 1
/// @param trustedConsensusState2 The trusted consensus state of header 2
struct MisbehaviourOutput {
IICS07TendermintMsgs.ClientState clientState;
uint64 time;
uint128 time;
IICS02ClientMsgs.Height trustedHeight1;
IICS02ClientMsgs.Height trustedHeight2;
IICS07TendermintMsgs.ConsensusState trustedConsensusState1;
4 changes: 2 additions & 2 deletions contracts/light-clients/msgs/IUpdateClientMsgs.sol
Original file line number Diff line number Diff line change
@@ -19,14 +19,14 @@ interface IUpdateClientMsgs {
/// @param clientState The client state that was used to verify the header.
/// @param trustedConsensusState The trusted consensus state.
/// @param newConsensusState The new consensus state with the verified header.
/// @param time The time which the header was verified in seconds.
/// @param time The time which the header was verified in unix nanoseconds.
/// @param trustedHeight The trusted height.
/// @param newHeight The new height.
struct UpdateClientOutput {
IICS07TendermintMsgs.ClientState clientState;
IICS07TendermintMsgs.ConsensusState trustedConsensusState;
IICS07TendermintMsgs.ConsensusState newConsensusState;
uint64 time;
uint128 time;
IICS02ClientMsgs.Height trustedHeight;
IICS02ClientMsgs.Height newHeight;
}
8 changes: 3 additions & 5 deletions packages/relayer-lib/src/tx_builder/cosmos_to_cosmos.rs
Original file line number Diff line number Diff line change
@@ -78,24 +78,22 @@ impl TxBuilderService<CosmosSdk, CosmosSdk> for TxBuilder {
revision_height,
};

let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_secs();
let now_since_unix = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?;

let mut timeout_msgs = cosmos::target_events_to_timeout_msgs(
target_events,
&target_client_id,
&target_height,
&self.signer_address,
now,
now_since_unix.as_secs(),
);

let (mut recv_msgs, mut ack_msgs) = cosmos::src_events_to_recv_and_ack_msgs(
src_events,
&target_client_id,
&target_height,
&self.signer_address,
now,
now_since_unix.as_secs(),
);

cosmos::inject_tendermint_proofs(
10 changes: 4 additions & 6 deletions packages/relayer-lib/src/tx_builder/cosmos_to_eth.rs
Original file line number Diff line number Diff line change
@@ -91,9 +91,7 @@ where
dest_events: Vec<EurekaEventWithHeight>,
target_client_id: String,
) -> Result<Vec<u8>> {
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_secs();
let now_since_unix = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?;

let latest_light_block = self.tm_client.get_light_block(None).await?;
let revision_height = latest_light_block.height().value();
@@ -108,14 +106,14 @@ where
dest_events,
&target_client_id,
&latest_height,
now,
now_since_unix.as_secs(),
);

let recv_and_ack_msgs = eth_eureka::src_events_to_recv_and_ack_msgs(
src_events,
&target_client_id,
&latest_height,
now,
now_since_unix.as_secs(),
);

let mut all_msgs = timeout_msgs
@@ -136,7 +134,7 @@ where
&self.tm_client,
latest_light_block,
client_state,
now,
now_since_unix.as_nanos(),
)
.await?;

16 changes: 6 additions & 10 deletions packages/relayer-lib/src/tx_builder/eth_to_cosmos.rs
Original file line number Diff line number Diff line change
@@ -254,24 +254,22 @@ where
revision_height: minimum_block_number,
};

let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_secs();
let now_since_unix = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?;

let mut timeout_msgs = cosmos::target_events_to_timeout_msgs(
dest_events,
&target_client_id,
&target_height,
&self.signer_address,
now,
now_since_unix.as_secs(),
);

let (mut recv_msgs, mut ack_msgs) = cosmos::src_events_to_recv_and_ack_msgs(
src_events,
&target_client_id,
&target_height,
&self.signer_address,
now,
now_since_unix.as_secs(),
);

let ethereum_client_state = self.ethereum_client_state(target_client_id.clone()).await?;
@@ -547,24 +545,22 @@ where
revision_height: target_block_number,
};

let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_secs();
let now_since_unix = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?;

let mut timeout_msgs = cosmos::target_events_to_timeout_msgs(
dest_events,
&target_client_id,
&target_height,
&self.signer_address,
now,
now_since_unix.as_secs(),
);

let (mut recv_msgs, mut ack_msgs) = cosmos::src_events_to_recv_and_ack_msgs(
src_events,
&target_client_id,
&target_height,
&self.signer_address,
now,
now_since_unix.as_secs(),
);

tracing::debug!("Timeout messages: #{}", timeout_msgs.len());
2 changes: 1 addition & 1 deletion packages/relayer-lib/src/utils/eth_eureka.rs
Original file line number Diff line number Diff line change
@@ -109,7 +109,7 @@ pub async fn inject_sp1_proof<C: SP1ProverComponents>(
tm_client: &HttpClient,
target_light_block: LightBlock,
client_state: ClientState,
now: u64,
now: u128,
) -> Result<()> {
let target_height = target_light_block.height().value();

11 changes: 7 additions & 4 deletions packages/solidity/src/msgs.rs
Original file line number Diff line number Diff line change
@@ -59,7 +59,9 @@ impl From<ICS07TendermintConsensusState> for IICS07TendermintMsgs::ConsensusStat
.unwrap();
Self {
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
timestamp: ics07_tendermint_consensus_state.timestamp.unix_timestamp() as u64,
timestamp: ics07_tendermint_consensus_state
.timestamp
.unix_timestamp_nanos() as u128,
root: root.into(),
nextValidatorsHash: next_validators_hash.into(),
}
@@ -69,9 +71,10 @@ impl From<ICS07TendermintConsensusState> for IICS07TendermintMsgs::ConsensusStat
#[allow(clippy::fallible_impl_from)]
impl From<IICS07TendermintMsgs::ConsensusState> for ICS07TendermintConsensusState {
fn from(consensus_state: IICS07TendermintMsgs::ConsensusState) -> Self {
let time =
OffsetDateTime::from_unix_timestamp(consensus_state.timestamp.try_into().unwrap())
.unwrap();
let time = OffsetDateTime::from_unix_timestamp_nanos(
consensus_state.timestamp.try_into().unwrap(),
)
.unwrap();
let seconds = time.unix_timestamp();
let nanos = time.nanosecond();
Self {
6 changes: 3 additions & 3 deletions packages/sp1-ics07-tendermint-prover/src/prover.rs
Original file line number Diff line number Diff line change
@@ -164,7 +164,7 @@ where
client_state: &SolClientState,
trusted_consensus_state: &SolConsensusState,
proposed_header: &Header,
time: u64,
time: u128,
) -> SP1ProofWithPublicValues {
// Encode the inputs into our program.
let encoded_1 = client_state.abi_encode();
@@ -229,7 +229,7 @@ where
client_state: &SolClientState,
trusted_consensus_state: &SolConsensusState,
proposed_header: &Header,
time: u64,
time: u128,
kv_proofs: Vec<(KVPair, MerkleProof)>,
) -> SP1ProofWithPublicValues {
assert!(!kv_proofs.is_empty(), "No key-value pairs to prove");
@@ -271,7 +271,7 @@ where
misbehaviour: &Misbehaviour,
trusted_consensus_state_1: &SolConsensusState,
trusted_consensus_state_2: &SolConsensusState,
time: u64,
time: u128,
) -> SP1ProofWithPublicValues {
let encoded_1 = client_state.abi_encode();
let encoded_2 = misbehaviour.encode_to_vec();
7 changes: 2 additions & 5 deletions programs/operator/src/runners/fixtures/misbehaviour.rs
Original file line number Diff line number Diff line change
@@ -102,16 +102,13 @@ pub async fn run(args: MisbehaviourCmd) -> anyhow::Result<()> {
let verify_misbehaviour_prover =
SP1ICS07TendermintProver::<MisbehaviourProgram, _>::new(args.proof_type, &sp1_prover);

let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_secs();

let now_since_unix = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?;
let proof_data = verify_misbehaviour_prover.generate_proof(
&trusted_client_state_2,
&misbehaviour,
&trusted_consensus_state_1,
&trusted_consensus_state_2,
now,
now_since_unix.as_nanos(),
);

let submit_msg = MsgSubmitMisbehaviour {
6 changes: 2 additions & 4 deletions programs/operator/src/runners/fixtures/uc_and_mem.rs
Original file line number Diff line number Diff line change
@@ -57,9 +57,6 @@ pub async fn run(args: UpdateClientAndMembershipCmd) -> anyhow::Result<()> {
SolConsensusState::abi_decode(&genesis.trusted_consensus_state, false)?.into();

let proposed_header = target_light_block.into_header(&trusted_light_block);
let now = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)?
.as_secs();

let kv_proofs: Vec<(_, _)> =
futures::future::try_join_all(args.membership.key_paths.into_iter().map(|path| async {
@@ -83,12 +80,13 @@ pub async fn run(args: UpdateClientAndMembershipCmd) -> anyhow::Result<()> {
.await?;

let kv_len = kv_proofs.len();
let now_since_unix = std::time::SystemTime::now().duration_since(std::time::UNIX_EPOCH)?;
// Generate a header update proof for the specified blocks.
let proof_data = uc_mem_prover.generate_proof(
&trusted_client_state,
&trusted_consensus_state.into(),
&proposed_header,
now,
now_since_unix.as_nanos(),
kv_proofs,
);

Loading