From dccc3a78c7b156d21e2d8e90f6b02455a82d08e9 Mon Sep 17 00:00:00 2001 From: Victor Yves Crispim Date: Wed, 26 Jul 2023 16:48:02 -0300 Subject: [PATCH] feat(advance-runner): get off-chain machine hash --- offchain/advance-runner/src/runner.rs | 10 +++ .../advance-runner/src/snapshot/disabled.rs | 9 +++ .../advance-runner/src/snapshot/fs_manager.rs | 81 ++++++++++++++++++- offchain/advance-runner/src/snapshot/mod.rs | 11 ++- offchain/rollups-events/src/common.rs | 2 +- 5 files changed, 110 insertions(+), 3 deletions(-) diff --git a/offchain/advance-runner/src/runner.rs b/offchain/advance-runner/src/runner.rs index 8f97c9c28..a3041e115 100644 --- a/offchain/advance-runner/src/runner.rs +++ b/offchain/advance-runner/src/runner.rs @@ -61,6 +61,9 @@ pub enum RunnerError { got ))] ParentIdMismatchError { expected: String, got: String }, + + #[snafu(display("failed to get hash from snapshot "))] + GetSnapshotHashError { source: SnapError }, } type Result = std::result::Result>; @@ -127,6 +130,13 @@ impl Runner { .context(GetLatestSnapshotSnafu)?; tracing::info!(?snapshot, "got latest snapshot"); + let offchain_hash = self + .snapshot_manager + .get_template_hash(&snapshot) + .await + .context(GetSnapshotHashSnafu)?; + tracing::info!(?offchain_hash, "got snapshot hash"); + let event_id = self .broker .find_previous_finish_epoch(snapshot.epoch) diff --git a/offchain/advance-runner/src/snapshot/disabled.rs b/offchain/advance-runner/src/snapshot/disabled.rs index da1805ac4..8907e53cb 100644 --- a/offchain/advance-runner/src/snapshot/disabled.rs +++ b/offchain/advance-runner/src/snapshot/disabled.rs @@ -11,6 +11,7 @@ // specific language governing permissions and limitations under the License. use super::{Snapshot, SnapshotManager}; +use rollups_events::Hash; #[derive(Debug)] pub struct SnapshotDisabled {} @@ -50,4 +51,12 @@ impl SnapshotManager for SnapshotDisabled { tracing::trace!("snapshots disabled; ignoring"); Ok(()) } + + async fn get_template_hash( + &self, + _: &Snapshot, + ) -> Result { + tracing::trace!("snapshots disabled; returning default"); + Ok(Hash::default()) + } } diff --git a/offchain/advance-runner/src/snapshot/fs_manager.rs b/offchain/advance-runner/src/snapshot/fs_manager.rs index 7dce4c02c..c5a490819 100644 --- a/offchain/advance-runner/src/snapshot/fs_manager.rs +++ b/offchain/advance-runner/src/snapshot/fs_manager.rs @@ -10,14 +10,18 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. +use rollups_events::{Hash, HASH_SIZE}; use snafu::{ensure, OptionExt, ResultExt, Snafu}; use std::collections::HashSet; -use std::fs; +use std::fs::{self, File}; +use std::io::Read; use std::path::{Path, PathBuf}; use super::config::FSManagerConfig; use super::{Snapshot, SnapshotManager}; +const HASH_FILE: &str = "hash"; + #[derive(Debug, Snafu)] pub enum FSSnapshotError { #[snafu(display("failed to follow latest symlink"))] @@ -75,6 +79,18 @@ pub enum FSSnapshotError { path: PathBuf, source: std::io::Error, }, + + #[snafu(display("failed to open hash file for snapshot ({})", path.display()))] + HashNotFoundError { + path: PathBuf, + source: std::io::Error, + }, + + #[snafu(display("failed to read hash file for snapshot ({})", path.display()))] + ReadHashError { + path: PathBuf, + source: std::io::Error, + }, } #[derive(Debug)] @@ -204,6 +220,22 @@ impl SnapshotManager for FSSnapshotManager { Ok(()) } + + //TODO: logs + async fn get_template_hash( + &self, + snapshot: &Snapshot, + ) -> Result { + let path = snapshot.path.with_file_name(HASH_FILE); + let file = File::open(path.clone()) + .context(HashNotFoundSnafu { path: path.clone() })?; + + let mut buffer = [0_u8; HASH_SIZE]; + file.take(HASH_SIZE as u64) + .read(&mut buffer) + .context(ReadHashSnafu { path: path.clone() })?; + Ok(Hash::new(buffer)) + } } fn encode_filename(epoch: u64, processed_input_count: u64) -> String { @@ -534,4 +566,51 @@ mod tests { state.tempdir.path().join("2_2"), ); } + + #[test_log::test(tokio::test)] + async fn test_it_gets_snapshot_hash() { + let state = TestState::setup(); + let path = state.create_snapshot("0_0"); + let hash_path = path.with_file_name(HASH_FILE); + let hash = [ + 160, 170, 75, 88, 113, 141, 144, 31, 252, 78, 159, 6, 79, 114, 6, + 16, 196, 49, 44, 208, 62, 83, 66, 97, 4, 151, 159, 105, 124, 85, + 51, 87, + ]; + fs::write(hash_path, hash).expect("should write hash to file"); + + let snap = Snapshot { + epoch: 0, + processed_input_count: 0, + path, + }; + + assert_eq!( + state + .manager + .get_template_hash(&snap) + .await + .expect("get template hash should work"), + Hash::new(hash) + ); + } + + #[test_log::test(tokio::test)] + async fn test_it_fails_to_get_hash_when_hash_file_does_not_exist() { + let state = TestState::setup(); + let path = state.create_snapshot("0_0"); + let snap = Snapshot { + epoch: 0, + processed_input_count: 0, + path, + }; + + let err = state + .manager + .get_template_hash(&snap) + .await + .expect_err("get template hash should fail"); + + assert!(matches!(err, FSSnapshotError::HashNotFoundError { .. })) + } } diff --git a/offchain/advance-runner/src/snapshot/mod.rs b/offchain/advance-runner/src/snapshot/mod.rs index aef101987..20d924d69 100644 --- a/offchain/advance-runner/src/snapshot/mod.rs +++ b/offchain/advance-runner/src/snapshot/mod.rs @@ -10,14 +10,16 @@ // CONDITIONS OF ANY KIND, either express or implied. See the License for the // specific language governing permissions and limitations under the License. +use rollups_events::Hash; use std::path::PathBuf; pub mod config; pub mod disabled; pub mod fs_manager; +//TODO: improve this description. Create a Metadata struct maybe? /// Cartesi Machine snapshot description -#[derive(Debug, Default, Clone, PartialEq, Eq, PartialOrd, Ord)] +#[derive(Debug, Default, Clone, PartialEq, Eq)] pub struct Snapshot { pub path: PathBuf, pub epoch: u64, @@ -31,6 +33,7 @@ pub trait SnapshotManager { /// Get the most recent snapshot async fn get_latest(&self) -> Result; + //TODO: maybe rename to new_empty_snapshot? /// Get the target storage directory for the snapshot async fn get_storage_directory( &self, @@ -40,4 +43,10 @@ pub trait SnapshotManager { /// Set the most recent snapshot async fn set_latest(&self, snapshot: Snapshot) -> Result<(), Self::Error>; + + /// Get the snapshot's template hash + async fn get_template_hash( + &self, + snapshot: &Snapshot, + ) -> Result; } diff --git a/offchain/rollups-events/src/common.rs b/offchain/rollups-events/src/common.rs index d1f1bc8a3..8d1fdc520 100644 --- a/offchain/rollups-events/src/common.rs +++ b/offchain/rollups-events/src/common.rs @@ -20,7 +20,7 @@ pub const ADDRESS_SIZE: usize = 20; pub const HASH_SIZE: usize = 32; /// A binary array that is converted to a hex string when serialized -#[derive(Clone, Hash, Eq, PartialEq)] +#[derive(Clone, Hash, Eq, PartialEq, PartialOrd, Ord)] pub struct HexArray([u8; N]); impl HexArray {