From 0346337615c923ad48d2a5a980eb23ecb30f5752 Mon Sep 17 00:00:00 2001 From: freesig Date: Fri, 9 Jan 2026 13:19:29 +1100 Subject: [PATCH] feat(node): add image ID storage and retrieval Store RISC0 state and recursive image IDs in node storage at startup. Expose via get_state_image_id() and get_recursive_image_id() on Node. Supports both Db (SQLite) and Memory storage backends. --- .../2026-01-09-image-id-storage-design.md | 99 ++++ docs/plans/2026-01-09-image-id-storage.md | 479 ++++++++++++++++++ node/sql/create/image_ids.sql | 5 + node/sql/insert/image_ids.sql | 4 + node/sql/query/recursive_image_id.sql | 6 + node/sql/query/state_image_id.sql | 6 + node/src/db.rs | 69 +++ node/src/lib.rs | 19 + node/src/memory.rs | 19 + node/src/storage.rs | 24 + 10 files changed, 730 insertions(+) create mode 100644 docs/plans/2026-01-09-image-id-storage-design.md create mode 100644 docs/plans/2026-01-09-image-id-storage.md create mode 100644 node/sql/create/image_ids.sql create mode 100644 node/sql/insert/image_ids.sql create mode 100644 node/sql/query/recursive_image_id.sql create mode 100644 node/sql/query/state_image_id.sql diff --git a/docs/plans/2026-01-09-image-id-storage-design.md b/docs/plans/2026-01-09-image-id-storage-design.md new file mode 100644 index 0000000..1b2d8e4 --- /dev/null +++ b/docs/plans/2026-01-09-image-id-storage-design.md @@ -0,0 +1,99 @@ +# Image ID Storage Design + +Store RISC0 image IDs (state and recursive) in node storage and expose them via Node methods. + +## Background + +Currently, image IDs are computed on-demand from ELF code during recursive proof generation but not persisted. This design adds persistent storage and retrieval methods. + +## Requirements + +- Store state and recursive image IDs as `[u8; 32]` +- Compute and store at node startup when ELF codes are provided +- Support both Db (SQLite) and Memory storage backends +- Expose via two methods: `get_state_image_id()` and `get_recursive_image_id()` + +## Design + +### Database Schema + +New file: `node/sql/create/image_ids.sql` + +```sql +CREATE TABLE IF NOT EXISTS image_ids ( + id INTEGER PRIMARY KEY, + state_image_id BLOB NOT NULL, + recursive_image_id BLOB NOT NULL +) +``` + +Single row table holding both image IDs. + +### Storage Layer + +**`node/src/db.rs`** - Add methods: + +```rust +pub async fn set_image_ids(&self, state: [u8; 32], recursive: [u8; 32]) +pub async fn get_state_image_id(&self) -> Option<[u8; 32]> +pub async fn get_recursive_image_id(&self) -> Option<[u8; 32]> +``` + +**`node/src/memory.rs`** - Add fields to `MemoryInner`: + +```rust +state_image_id: Option<[u8; 32]>, +recursive_image_id: Option<[u8; 32]>, +``` + +With matching setter and getter methods on `Memory`. + +**`node/src/storage.rs`** - Add delegation on `Storage` enum: + +```rust +pub async fn set_image_ids(&self, state: [u8; 32], recursive: [u8; 32]) +pub async fn get_state_image_id(&self) -> Option<[u8; 32]> +pub async fn get_recursive_image_id(&self) -> Option<[u8; 32]> +``` + +### Node API + +**`node/src/lib.rs`** - Add methods on `Node`: + +```rust +pub async fn get_state_image_id(&self) -> Option<[u8; 32]> { + self.storage.get_state_image_id().await +} + +pub async fn get_recursive_image_id(&self) -> Option<[u8; 32]> { + self.storage.get_recursive_image_id().await +} +``` + +### Initialization + +In the ZK publisher flow (`run_zk_publisher_stream`), at startup when `ElfCode` is available: + +```rust +let state_image_id: [u8; 32] = risc0_zkvm::compute_image_id(&elf_code.state_elf_code)?.into(); +let recursive_image_id: [u8; 32] = risc0_zkvm::compute_image_id(&elf_code.recursive_elf_code)?.into(); +storage.set_image_ids(state_image_id, recursive_image_id).await; +``` + +This runs once at startup before the main processing loop. + +## Files Changed + +| File | Change | +|------|--------| +| `node/sql/create/image_ids.sql` | New table schema | +| `node/src/db.rs` | Add set/get methods for image IDs | +| `node/src/memory.rs` | Add fields and methods to MemoryInner/Memory | +| `node/src/storage.rs` | Add delegation methods on Storage enum | +| `node/src/lib.rs` | Add Node methods and initialization logic | + +## Not In Scope + +- HTTP endpoints for image IDs (can be added later if needed) +- Storing full ELF code bytes +- Changes to proof storage or retrieval diff --git a/docs/plans/2026-01-09-image-id-storage.md b/docs/plans/2026-01-09-image-id-storage.md new file mode 100644 index 0000000..b87dce0 --- /dev/null +++ b/docs/plans/2026-01-09-image-id-storage.md @@ -0,0 +1,479 @@ +# Image ID Storage Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task. + +**Goal:** Store RISC0 image IDs in node storage and expose them via Node methods. + +**Architecture:** Add image ID fields to both Memory and Db storage backends, with delegation through Storage enum to Node public API. Compute and store at ZK publisher startup. + +**Tech Stack:** Rust, SQLite (rusqlite), async/tokio + +--- + +## Verification Checklist + +After all tasks complete, verify: + +- [ ] `cargo build -p void-app-node` compiles without errors +- [ ] `cargo build -p void-app-node --features cuda` compiles without errors +- [ ] All design doc requirements implemented: + - [ ] `node/sql/create/image_ids.sql` exists with correct schema + - [ ] `Db` has `set_image_ids`, `get_state_image_id`, `get_recursive_image_id` methods + - [ ] `Memory` has `set_image_ids`, `get_state_image_id`, `get_recursive_image_id` methods + - [ ] `Storage` enum delegates all three methods + - [ ] `Node` exposes `get_state_image_id` and `get_recursive_image_id` + - [ ] ZK publisher startup computes and stores image IDs + +--- + +### Task 1: Create SQL Schema for Image IDs + +**Files:** +- Create: `node/sql/create/image_ids.sql` + +**Step 1: Create the SQL file** + +Create `node/sql/create/image_ids.sql`: + +```sql +CREATE TABLE IF NOT EXISTS image_ids ( + id INTEGER PRIMARY KEY, + state_image_id BLOB NOT NULL, + recursive_image_id BLOB NOT NULL +) +``` + +**Step 2: Verify file exists** + +Run: `cat node/sql/create/image_ids.sql` + +--- + +### Task 2: Create SQL Insert Statement for Image IDs + +**Files:** +- Create: `node/sql/insert/image_ids.sql` + +**Step 1: Create the SQL file** + +Create `node/sql/insert/image_ids.sql`: + +```sql +INSERT + OR REPLACE INTO image_ids (id, state_image_id, recursive_image_id) +VALUES + (1, ?, ?); +``` + +**Step 2: Verify file exists** + +Run: `cat node/sql/insert/image_ids.sql` + +--- + +### Task 3: Create SQL Query Statements for Image IDs + +**Files:** +- Create: `node/sql/query/state_image_id.sql` +- Create: `node/sql/query/recursive_image_id.sql` + +**Step 1: Create state_image_id query** + +Create `node/sql/query/state_image_id.sql`: + +```sql +SELECT + state_image_id +FROM + image_ids +WHERE + id = 1; +``` + +**Step 2: Create recursive_image_id query** + +Create `node/sql/query/recursive_image_id.sql`: + +```sql +SELECT + recursive_image_id +FROM + image_ids +WHERE + id = 1; +``` + +**Step 3: Verify files exist** + +Run: `ls node/sql/query/` + +--- + +### Task 4: Register SQL Constants in db.rs + +**Files:** +- Modify: `node/src/db.rs:214-229` + +**Step 1: Add image_ids to create module** + +In `node/src/db.rs`, add to the `create` module (around line 214-217): + +```rust +pub mod create { + decl_const_sql_str!(CURRENT_PROOF, "create/current_proof.sql"); + decl_const_sql_str!(LATEST_HEADER, "create/latest_header.sql"); + decl_const_sql_str!(IMAGE_IDS, "create/image_ids.sql"); +} +``` + +**Step 2: Add image_ids to insert module** + +Add to the `insert` module (around line 219-223): + +```rust +pub mod insert { + decl_const_sql_str!(CURRENT_PROOF, "insert/current_proof.sql"); + decl_const_sql_str!(CURRENT_PROOF_LIMIT, "insert/current_proof_limit.sql"); + decl_const_sql_str!(LATEST_HEADER, "insert/latest_header.sql"); + decl_const_sql_str!(IMAGE_IDS, "insert/image_ids.sql"); +} +``` + +**Step 3: Add image_ids to query module** + +Add to the `query` module (around line 225-229): + +```rust +pub mod query { + decl_const_sql_str!(CURRENT_PROOF, "query/current_proof.sql"); + decl_const_sql_str!(GET_PROOF, "query/get_proof.sql"); + decl_const_sql_str!(LATEST_HEADER, "query/latest_header.sql"); + decl_const_sql_str!(STATE_IMAGE_ID, "query/state_image_id.sql"); + decl_const_sql_str!(RECURSIVE_IMAGE_ID, "query/recursive_image_id.sql"); +} +``` + +**Step 4: Add table creation to create_tables function** + +Modify `create_tables` function (around line 207-212): + +```rust +pub fn create_tables(tx: &rusqlite::Transaction) -> anyhow::Result<()> { + tx.execute(create::CURRENT_PROOF, [])?; + tx.execute(create::LATEST_HEADER, [])?; + tx.execute(create::IMAGE_IDS, [])?; + tx.execute(insert::CURRENT_PROOF_LIMIT, [])?; + Ok(()) +} +``` + +**Step 5: Verify compilation** + +Run: `cargo build -p void-app-node 2>&1 | head -20` + +--- + +### Task 5: Add Image ID Methods to Db + +**Files:** +- Modify: `node/src/db.rs:94-137` + +**Step 1: Add set_image_ids method to Db** + +Add after `get_current_block_height_and_hash` method (after line 136): + +```rust + pub async fn set_image_ids(&self, state: [u8; 32], recursive: [u8; 32]) -> anyhow::Result<()> + where + DbData: Clone + Send + 'static, + { + self.apply(move |tx| { + tx.tx.execute( + insert::IMAGE_IDS, + rusqlite::params![state.as_slice(), recursive.as_slice()], + )?; + Ok(()) + }) + .await + } + + pub async fn get_state_image_id(&self) -> anyhow::Result> + where + DbData: Clone + Send + 'static, + { + self.apply(|tx| { + let mut stmt = tx.tx.prepare(query::STATE_IMAGE_ID)?; + let image_id = stmt + .query_row(rusqlite::params![], |row| { + let data: Vec = row.get(0)?; + Ok(data) + }) + .optional()?; + match image_id { + Some(data) => { + let arr: [u8; 32] = data.try_into().map_err(|_| { + anyhow::anyhow!("state_image_id is not 32 bytes") + })?; + Ok(Some(arr)) + } + None => Ok(None), + } + }) + .await + } + + pub async fn get_recursive_image_id(&self) -> anyhow::Result> + where + DbData: Clone + Send + 'static, + { + self.apply(|tx| { + let mut stmt = tx.tx.prepare(query::RECURSIVE_IMAGE_ID)?; + let image_id = stmt + .query_row(rusqlite::params![], |row| { + let data: Vec = row.get(0)?; + Ok(data) + }) + .optional()?; + match image_id { + Some(data) => { + let arr: [u8; 32] = data.try_into().map_err(|_| { + anyhow::anyhow!("recursive_image_id is not 32 bytes") + })?; + Ok(Some(arr)) + } + None => Ok(None), + } + }) + .await + } +``` + +**Step 2: Verify compilation** + +Run: `cargo build -p void-app-node 2>&1 | head -20` + +--- + +### Task 6: Add Image ID Fields and Methods to Memory + +**Files:** +- Modify: `node/src/memory.rs:16-21` and add methods after line 84 + +**Step 1: Add fields to MemoryInner** + +Modify `MemoryInner` struct (around line 16-21): + +```rust +struct MemoryInner { + app_state: App, + api_state: Api, + proofs: KeyValueBuffer<10, u64, AppProof>, + latest_header: LatestHeaderMem, + state_image_id: Option<[u8; 32]>, + recursive_image_id: Option<[u8; 32]>, +} +``` + +**Step 2: Initialize fields in Memory::new** + +Modify `Memory::new` (around line 46-55): + +```rust + pub fn new(app_state: App, api_state: Api) -> Self { + Self { + inner: Lock::new(MemoryInner { + app_state, + api_state, + proofs: KeyValueBuffer::new(), + latest_header: LatestHeaderMem::default(), + state_image_id: None, + recursive_image_id: None, + }), + } + } +``` + +**Step 3: Add image ID methods to Memory** + +Add after `get_current_block_height_and_hash` method (after line 84): + +```rust + pub fn set_image_ids(&self, state: [u8; 32], recursive: [u8; 32]) { + self.inner.access(|inner| { + inner.state_image_id = Some(state); + inner.recursive_image_id = Some(recursive); + }); + } + + pub fn get_state_image_id(&self) -> Option<[u8; 32]> { + self.inner.access(|inner| inner.state_image_id) + } + + pub fn get_recursive_image_id(&self) -> Option<[u8; 32]> { + self.inner.access(|inner| inner.recursive_image_id) + } +``` + +**Step 4: Verify compilation** + +Run: `cargo build -p void-app-node 2>&1 | head -20` + +--- + +### Task 7: Add Image ID Delegation to Storage Enum + +**Files:** +- Modify: `node/src/storage.rs:151-163` + +**Step 1: Add image ID methods to Storage** + +Add a new impl block after the existing `get_current_block_height_and_hash` impl block (after line 163): + +```rust +impl Storage +where + DbData: DbDataConstraints, +{ + pub async fn set_image_ids(&self, state: [u8; 32], recursive: [u8; 32]) -> anyhow::Result<()> { + match self { + Storage::Db(db) => db.set_image_ids(state, recursive).await, + Storage::Memory(mem) => { + mem.set_image_ids(state, recursive); + Ok(()) + } + } + } + + pub async fn get_state_image_id(&self) -> anyhow::Result> { + match self { + Storage::Db(db) => db.get_state_image_id().await, + Storage::Memory(mem) => Ok(mem.get_state_image_id()), + } + } + + pub async fn get_recursive_image_id(&self) -> anyhow::Result> { + match self { + Storage::Db(db) => db.get_recursive_image_id().await, + Storage::Memory(mem) => Ok(mem.get_recursive_image_id()), + } + } +} +``` + +**Step 2: Verify compilation** + +Run: `cargo build -p void-app-node 2>&1 | head -20` + +--- + +### Task 8: Add Image ID Methods to Node + +**Files:** +- Modify: `node/src/lib.rs:300-313` + +**Step 1: Add image ID methods to Node** + +Add a new impl block after the existing `get_latest_proof` impl block (after line 313): + +```rust +impl Node +where + DbData: DbDataConstraints, +{ + pub async fn get_state_image_id(&self) -> anyhow::Result> { + self.storage.get_state_image_id().await + } + + pub async fn get_recursive_image_id(&self) -> anyhow::Result> { + self.storage.get_recursive_image_id().await + } +} +``` + +**Step 2: Verify compilation** + +Run: `cargo build -p void-app-node 2>&1 | head -20` + +--- + +### Task 9: Add Image ID Initialization in ZK Publisher + +**Files:** +- Modify: `node/src/lib.rs:996-1011` + +**Step 1: Compute and store image IDs at startup** + +Modify `run_zk_publisher_stream` function. After the `ElfCode` destructuring (line 996-1000) and before `get_current_block_height_and_hash` call, add: + +```rust + let ElfCode { + state_elf_code, + recursive_elf_code, + } = elf_code; + + // Compute and store image IDs at startup + let state_image_id: [u8; 32] = risc0_zkvm::compute_image_id(&state_elf_code)?.into(); + let recursive_image_id: [u8; 32] = risc0_zkvm::compute_image_id(&recursive_elf_code)?.into(); + node.storage.set_image_ids(state_image_id, recursive_image_id).await?; + + let recursive_elf_code = Arc::new(recursive_elf_code); +``` + +Note: The existing `let recursive_elf_code = Arc::new(recursive_elf_code);` line should come after the image ID computation since we need to borrow `recursive_elf_code` first. + +**Step 2: Verify compilation** + +Run: `cargo build -p void-app-node 2>&1 | head -20` + +--- + +### Task 10: Final Verification + +**Step 1: Full build check** + +Run: `cargo build -p void-app-node` +Expected: Build succeeds with no errors + +**Step 2: Build with cuda feature** + +Run: `cargo build -p void-app-node --features cuda` +Expected: Build succeeds with no errors + +**Step 3: Run clippy** + +Run: `cargo clippy -p void-app-node -- -D warnings 2>&1 | head -30` +Expected: No warnings + +**Step 4: Review design doc checklist** + +Verify all items from design doc are implemented: +- `node/sql/create/image_ids.sql` - created in Task 1 +- `Db` methods - added in Task 5 +- `Memory` methods - added in Task 6 +- `Storage` delegation - added in Task 7 +- `Node` methods - added in Task 8 +- ZK publisher initialization - added in Task 9 + +--- + +## Commit + +After all tasks pass, commit with: + +```bash +git add node/sql/create/image_ids.sql \ + node/sql/insert/image_ids.sql \ + node/sql/query/state_image_id.sql \ + node/sql/query/recursive_image_id.sql \ + node/src/db.rs \ + node/src/memory.rs \ + node/src/storage.rs \ + node/src/lib.rs + +git commit -m "feat(node): add image ID storage and retrieval + +Store RISC0 state and recursive image IDs in node storage at startup. +Expose via get_state_image_id() and get_recursive_image_id() on Node. + +Supports both Db (SQLite) and Memory storage backends." +``` diff --git a/node/sql/create/image_ids.sql b/node/sql/create/image_ids.sql new file mode 100644 index 0000000..c783700 --- /dev/null +++ b/node/sql/create/image_ids.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS image_ids ( + id INTEGER PRIMARY KEY, + state_image_id BLOB NOT NULL, + recursive_image_id BLOB NOT NULL +) diff --git a/node/sql/insert/image_ids.sql b/node/sql/insert/image_ids.sql new file mode 100644 index 0000000..386eeb2 --- /dev/null +++ b/node/sql/insert/image_ids.sql @@ -0,0 +1,4 @@ +INSERT + OR REPLACE INTO image_ids (id, state_image_id, recursive_image_id) +VALUES + (1, ?, ?); diff --git a/node/sql/query/recursive_image_id.sql b/node/sql/query/recursive_image_id.sql new file mode 100644 index 0000000..aaf3efa --- /dev/null +++ b/node/sql/query/recursive_image_id.sql @@ -0,0 +1,6 @@ +SELECT + recursive_image_id +FROM + image_ids +WHERE + id = 1; diff --git a/node/sql/query/state_image_id.sql b/node/sql/query/state_image_id.sql new file mode 100644 index 0000000..8fce410 --- /dev/null +++ b/node/sql/query/state_image_id.sql @@ -0,0 +1,6 @@ +SELECT + state_image_id +FROM + image_ids +WHERE + id = 1; diff --git a/node/src/db.rs b/node/src/db.rs index ceb838d..0c5dc5d 100644 --- a/node/src/db.rs +++ b/node/src/db.rs @@ -134,6 +134,70 @@ where self.apply(|tx| tx.get_current_block_height_and_hash()) .await } + + pub async fn set_image_ids(&self, state: [u8; 32], recursive: [u8; 32]) -> anyhow::Result<()> + where + DbData: Clone + Send + 'static, + { + self.apply(move |tx| { + tx.tx.execute( + insert::IMAGE_IDS, + rusqlite::params![state.as_slice(), recursive.as_slice()], + )?; + Ok(()) + }) + .await + } + + pub async fn get_state_image_id(&self) -> anyhow::Result> + where + DbData: Clone + Send + 'static, + { + self.apply(|tx| { + let mut stmt = tx.tx.prepare(query::STATE_IMAGE_ID)?; + let image_id = stmt + .query_row(rusqlite::params![], |row| { + let data: Vec = row.get(0)?; + Ok(data) + }) + .optional()?; + match image_id { + Some(data) => { + let arr: [u8; 32] = data.try_into().map_err(|_| { + anyhow::anyhow!("state_image_id is not 32 bytes") + })?; + Ok(Some(arr)) + } + None => Ok(None), + } + }) + .await + } + + pub async fn get_recursive_image_id(&self) -> anyhow::Result> + where + DbData: Clone + Send + 'static, + { + self.apply(|tx| { + let mut stmt = tx.tx.prepare(query::RECURSIVE_IMAGE_ID)?; + let image_id = stmt + .query_row(rusqlite::params![], |row| { + let data: Vec = row.get(0)?; + Ok(data) + }) + .optional()?; + match image_id { + Some(data) => { + let arr: [u8; 32] = data.try_into().map_err(|_| { + anyhow::anyhow!("recursive_image_id is not 32 bytes") + })?; + Ok(Some(arr)) + } + None => Ok(None), + } + }) + .await + } } impl Tx<'_, DbData> { @@ -207,6 +271,7 @@ impl UpdateLatestBlock for Tx<'_, DbData> { pub fn create_tables(tx: &rusqlite::Transaction) -> anyhow::Result<()> { tx.execute(create::CURRENT_PROOF, [])?; tx.execute(create::LATEST_HEADER, [])?; + tx.execute(create::IMAGE_IDS, [])?; tx.execute(insert::CURRENT_PROOF_LIMIT, [])?; Ok(()) } @@ -214,16 +279,20 @@ pub fn create_tables(tx: &rusqlite::Transaction) -> anyhow::Result<()> { pub mod create { decl_const_sql_str!(CURRENT_PROOF, "create/current_proof.sql"); decl_const_sql_str!(LATEST_HEADER, "create/latest_header.sql"); + decl_const_sql_str!(IMAGE_IDS, "create/image_ids.sql"); } pub mod insert { decl_const_sql_str!(CURRENT_PROOF, "insert/current_proof.sql"); decl_const_sql_str!(CURRENT_PROOF_LIMIT, "insert/current_proof_limit.sql"); decl_const_sql_str!(LATEST_HEADER, "insert/latest_header.sql"); + decl_const_sql_str!(IMAGE_IDS, "insert/image_ids.sql"); } pub mod query { decl_const_sql_str!(CURRENT_PROOF, "query/current_proof.sql"); decl_const_sql_str!(GET_PROOF, "query/get_proof.sql"); decl_const_sql_str!(LATEST_HEADER, "query/latest_header.sql"); + decl_const_sql_str!(STATE_IMAGE_ID, "query/state_image_id.sql"); + decl_const_sql_str!(RECURSIVE_IMAGE_ID, "query/recursive_image_id.sql"); } diff --git a/node/src/lib.rs b/node/src/lib.rs index 683f1df..64d4288 100644 --- a/node/src/lib.rs +++ b/node/src/lib.rs @@ -323,6 +323,19 @@ where } } +impl Node +where + DbData: DbDataConstraints, +{ + pub async fn get_state_image_id(&self) -> anyhow::Result> { + self.storage.get_state_image_id().await + } + + pub async fn get_recursive_image_id(&self) -> anyhow::Result> { + self.storage.get_recursive_image_id().await + } +} + pub enum DataType { Memory(App, Api), Db(DbData), @@ -997,6 +1010,12 @@ where state_elf_code, recursive_elf_code, } = elf_code; + + // Compute and store image IDs at startup + let state_image_id: [u8; 32] = risc0_zkvm::compute_image_id(&state_elf_code)?.into(); + let recursive_image_id: [u8; 32] = risc0_zkvm::compute_image_id(&recursive_elf_code)?.into(); + node.storage.set_image_ids(state_image_id, recursive_image_id).await?; + let recursive_elf_code = Arc::new(recursive_elf_code); let (last_parent_height, last_parent_hash) = node diff --git a/node/src/memory.rs b/node/src/memory.rs index 50089ed..c245b19 100644 --- a/node/src/memory.rs +++ b/node/src/memory.rs @@ -18,6 +18,8 @@ struct MemoryInner { api_state: Api, proofs: KeyValueBuffer<10, u64, AppProof>, latest_header: LatestHeaderMem, + state_image_id: Option<[u8; 32]>, + recursive_image_id: Option<[u8; 32]>, } #[derive(Default)] @@ -50,6 +52,8 @@ impl Memory { api_state, proofs: KeyValueBuffer::new(), latest_header: LatestHeaderMem::default(), + state_image_id: None, + recursive_image_id: None, }), } } @@ -83,6 +87,21 @@ impl Memory { self.inner.access(|inner| inner.latest_header.get()) } + pub fn set_image_ids(&self, state: [u8; 32], recursive: [u8; 32]) { + self.inner.access(|inner| { + inner.state_image_id = Some(state); + inner.recursive_image_id = Some(recursive); + }); + } + + pub fn get_state_image_id(&self) -> Option<[u8; 32]> { + self.inner.access(|inner| inner.state_image_id) + } + + pub fn get_recursive_image_id(&self) -> Option<[u8; 32]> { + self.inner.access(|inner| inner.recursive_image_id) + } + pub fn apply(&self, f: F) -> R where F: FnOnce(Apply<'_, App, Api>) -> R, diff --git a/node/src/storage.rs b/node/src/storage.rs index 3cbdc2d..c794935 100644 --- a/node/src/storage.rs +++ b/node/src/storage.rs @@ -160,6 +160,30 @@ where Storage::Memory(mem) => Ok(mem.get_current_block_height_and_hash()), } } + + pub async fn set_image_ids(&self, state: [u8; 32], recursive: [u8; 32]) -> anyhow::Result<()> { + match self { + Storage::Db(db) => db.set_image_ids(state, recursive).await, + Storage::Memory(mem) => { + mem.set_image_ids(state, recursive); + Ok(()) + } + } + } + + pub async fn get_state_image_id(&self) -> anyhow::Result> { + match self { + Storage::Db(db) => db.get_state_image_id().await, + Storage::Memory(mem) => Ok(mem.get_state_image_id()), + } + } + + pub async fn get_recursive_image_id(&self) -> anyhow::Result> { + match self { + Storage::Db(db) => db.get_recursive_image_id().await, + Storage::Memory(mem) => Ok(mem.get_recursive_image_id()), + } + } } impl Clone for Storage {