diff --git a/Cargo.lock b/Cargo.lock index 009dbbc11..6ef5357d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -6941,6 +6941,7 @@ dependencies = [ "lightning-schema", "lightning-types", "lightning-workspace-hack", + "merklize", "schemars", "serde", "serde-big-array", @@ -7302,6 +7303,7 @@ dependencies = [ "lightning-types", "lightning-utils", "lightning-workspace-hack", + "merklize", "once_cell", "rand 0.8.5", "reqwest", @@ -7605,7 +7607,6 @@ dependencies = [ "blake3", "block-buffer 0.9.0", "block-padding 0.3.3", - "borsh", "bytemuck", "byteorder", "bytes", diff --git a/core/application/src/state/query.rs b/core/application/src/state/query.rs index fa5c68754..b7b8754e5 100644 --- a/core/application/src/state/query.rs +++ b/core/application/src/state/query.rs @@ -2,6 +2,7 @@ use std::collections::BTreeSet; use std::path::Path; use std::time::Duration; +use anyhow::Result; use atomo::{ Atomo, AtomoBuilder, @@ -35,7 +36,9 @@ use lightning_interfaces::types::{ Value, }; use lightning_interfaces::SyncQueryRunnerInterface; +use merklize::{StateRootHash, StateTree}; +use crate::env::ApplicationStateTree; use crate::state::ApplicationState; use crate::storage::{AtomoStorage, AtomoStorageBuilder}; @@ -260,4 +263,9 @@ impl SyncQueryRunnerInterface for QueryRunner { self.inner .run(|ctx| self.node_to_uri.get(ctx).get(node_index)) } + + /// Returns the state tree root hash from the application state. + fn get_state_root(&self) -> Result { + self.run(|ctx| ApplicationStateTree::get_state_root(ctx)) + } } diff --git a/core/interfaces/Cargo.toml b/core/interfaces/Cargo.toml index feec4cc51..641867c87 100644 --- a/core/interfaces/Cargo.toml +++ b/core/interfaces/Cargo.toml @@ -33,6 +33,7 @@ hp-fixed.workspace = true ink-quill.workspace = true blake3-tree = { path = "../../lib/blake3-tree" } better-shutdown.workspace = true +merklize.workspace = true # Currently we need the SDK because of the FFI types. fn-sdk = { path = "../../lib/sdk" } diff --git a/core/interfaces/src/application.rs b/core/interfaces/src/application.rs index 3083b7e5e..556979cb9 100644 --- a/core/interfaces/src/application.rs +++ b/core/interfaces/src/application.rs @@ -17,6 +17,7 @@ use lightning_types::{ TxHash, Value, }; +use merklize::StateRootHash; use serde::{Deserialize, Serialize}; use crate::collection::Collection; @@ -187,6 +188,9 @@ pub trait SyncQueryRunnerInterface: Clone + Send + Sync + 'static { /// Returns the node's content registry. fn get_content_registry(&self, node_index: &NodeIndex) -> Option>; + + /// Returns the state root hash from the application state. + fn get_state_root(&self) -> Result; } #[derive(Clone, Debug)] diff --git a/core/rpc/Cargo.toml b/core/rpc/Cargo.toml index ba341e89c..684a4fd32 100644 --- a/core/rpc/Cargo.toml +++ b/core/rpc/Cargo.toml @@ -38,6 +38,7 @@ lightning-interfaces = { path = "../interfaces" } lightning-openrpc = { path = "../rpc-openrpc" } lightning-openrpc-macros = { path = "../rpc-openrpc-macros" } lightning-utils = { path = "../utils" } +merklize.workspace = true alloy-primitives = "0.5.2" resolved-pathbuf = { path = "../../lib/resolved-pathbuf" } diff --git a/core/rpc/src/api/flk.rs b/core/rpc/src/api/flk.rs index 7939ee56c..dc2b53d79 100644 --- a/core/rpc/src/api/flk.rs +++ b/core/rpc/src/api/flk.rs @@ -23,6 +23,7 @@ use lightning_interfaces::types::{ }; use lightning_interfaces::PagingParams; use lightning_openrpc_macros::open_rpc; +use merklize::StateRootHash; #[open_rpc(namespace = "flk", tag = "1.0.0")] #[rpc(client, server, namespace = "flk")] @@ -185,6 +186,9 @@ pub trait FleekApi { #[method(name = "get_sub_dag_index")] async fn get_sub_dag_index(&self) -> RpcResult<(u64, Epoch)>; + #[method(name = "get_state_root")] + async fn get_state_root(&self, epoch: Option) -> RpcResult; + #[method(name = "send_txn")] async fn send_txn(&self, tx: TransactionRequest) -> RpcResult<()>; diff --git a/core/rpc/src/logic/flk_impl.rs b/core/rpc/src/logic/flk_impl.rs index ae7ae5f65..ae0b1777e 100644 --- a/core/rpc/src/logic/flk_impl.rs +++ b/core/rpc/src/logic/flk_impl.rs @@ -30,6 +30,7 @@ use lightning_interfaces::types::{ }; use lightning_interfaces::PagingParams; use lightning_utils::application::QueryRunnerExt; +use merklize::StateRootHash; use crate::api::FleekApiServer; use crate::error::RPCError; @@ -387,6 +388,15 @@ impl FleekApiServer for FleekApi { Ok((sub_dag_index, self.data.query_runner.get_epoch_info().epoch)) } + async fn get_state_root(&self, epoch: Option) -> RpcResult { + Ok(self + .data + .query_runner(epoch) + .await? + .get_state_root() + .map_err(|e| RPCError::custom(e.to_string()))?) + } + async fn send_txn(&self, tx: TransactionRequest) -> RpcResult<()> { Ok(self .data diff --git a/core/rpc/src/tests.rs b/core/rpc/src/tests.rs index aa959da75..f4c6a5292 100644 --- a/core/rpc/src/tests.rs +++ b/core/rpc/src/tests.rs @@ -1143,3 +1143,41 @@ async fn test_rpc_events() -> Result<()> { Ok(()) } + +#[tokio::test] +async fn test_rpc_get_state_root() -> Result<()> { + let temp_dir = tempdir()?; + + // Create keys + let owner_secret_key = AccountOwnerSecretKey::generate(); + let owner_public_key = owner_secret_key.to_pk(); + + // Init application service + let mut genesis = Genesis::default(); + genesis.account.push(GenesisAccount { + public_key: owner_public_key.into(), + flk_balance: 1000u64.into(), + stables_balance: 0, + bandwidth_balance: 0, + }); + + let genesis_path = genesis + .write_to_dir(temp_dir.path().to_path_buf().try_into().unwrap()) + .unwrap(); + + let port = 30024; + let node = init_rpc(&temp_dir, genesis_path, port).await; + + wait_for_server_start(port).await?; + + let client = RpcClient::new_no_auth(&format!("http://127.0.0.1:{port}/rpc/v0"))?; + let root_hash = FleekApiClient::get_state_root(&client, None) + .await? + .to_string(); + + assert_eq!(root_hash.len(), 64); + assert!(root_hash.chars().all(|c| c.is_ascii_hexdigit())); + + node.shutdown().await; + Ok(()) +} diff --git a/etc/workspace-hack/Cargo.toml b/etc/workspace-hack/Cargo.toml index 975822b50..03d5497c1 100644 --- a/etc/workspace-hack/Cargo.toml +++ b/etc/workspace-hack/Cargo.toml @@ -31,7 +31,6 @@ blake2 = { version = "0.10" } blake3 = { version = "1" } block-buffer = { version = "0.9", default-features = false, features = ["block-padding"] } block-padding = { version = "0.3", default-features = false, features = ["std"] } -borsh = { version = "1", features = ["de_strict_order", "derive"] } bytemuck = { version = "1", default-features = false, features = ["extern_crate_alloc"] } byteorder = { version = "1", features = ["i128"] } bytes = { version = "1", features = ["serde"] } @@ -184,7 +183,6 @@ blake2 = { version = "0.10" } blake3 = { version = "1" } block-buffer = { version = "0.9", default-features = false, features = ["block-padding"] } block-padding = { version = "0.3", default-features = false, features = ["std"] } -borsh = { version = "1", features = ["de_strict_order", "derive"] } bytemuck = { version = "1", default-features = false, features = ["extern_crate_alloc"] } byteorder = { version = "1", features = ["i128"] } bytes = { version = "1", features = ["serde"] }