Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 39 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 7 additions & 0 deletions rofl-appd/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ rocket = { git = "https://github.com/rwf2/Rocket", rev = "28891e8072136f4641a33f
] }
serde = { version = "1.0", features = ["derive"] }
serde_with = { version = "3.12.0", features = ["hex"] }
serde_json = "1.0"
hex = "0.4"
sp800-185 = "0.2.0"
thiserror = "1.0"
Expand All @@ -30,6 +31,7 @@ tokio = { version = "1.38", features = [
] }
tokio-retry = "0.3.0"
zeroize = "1.7"
reqwest = { version = "0.12", features = ["json"] }

[dev-dependencies]
rustc-hex = "2.0.1"
Expand All @@ -39,3 +41,8 @@ serde_json = "1.0"
default = ["tx"]
# Add routes for transaction submission.
tx = []
mock = []

[[bin]]
name = "rofl-appd-mock"
path = "src/main.rs"
45 changes: 44 additions & 1 deletion rofl-appd/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ mod routes;
pub mod services;
pub(crate) mod state;

use oasis_runtime_sdk::crypto::signature::{secp256k1, Signer};
use std::sync::Arc;

use rocket::{figment::Figment, routes};

use rofl_app_core::{App, Environment};
use crate::state::LocalEnv;
use rofl_app_core::{App, AppId, Environment};

/// API server configuration.
#[derive(Clone)]
Expand Down Expand Up @@ -57,3 +59,44 @@ where

Ok(())
}

/// Start the REST API server in mock mode
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Do you think we should add another /rofl-appd-mock package that wraps /rofl-appd so it's clear what code is used in production ROFLs and what is there for the Localnet testing?

pub async fn start_local(
cfg: Config<'_>,
rpc_url: Option<String>,
seed: &[u8],
) -> Result<(), rocket::Error> {
let signer = secp256k1::MemorySigner::new_from_seed(seed).unwrap();

// For mock mode, just use a simple stub that implements Env
let env: Arc<dyn state::Env> =
Arc::new(LocalEnv::new(AppId::default(), Arc::new(signer), rpc_url));

// Server configuration.
let rocket_cfg = Figment::from(rocket::config::Config::default())
.select("default")
.merge(("address", cfg.address))
.merge(("reuse", true));

let server = rocket::custom(rocket_cfg)
.manage(cfg.kms)
.manage(env)
.mount("/rofl/v1/app", routes![routes::app::id,])
.mount("/rofl/v1/keys", routes![routes::keys::generate,]);

let server = server.manage(cfg.metadata).mount(
"/rofl/v1/metadata",
routes![routes::metadata::set, routes::metadata::get],
);

let server = server.mount("/rofl/v1/query", routes![routes::query::query]);

#[cfg(feature = "tx")]
let server = server
.manage(routes::tx::Config::default())
.mount("/rofl/v1/tx", routes![routes::tx::sign_and_submit]);

server.launch().await?;

Ok(())
}
25 changes: 25 additions & 0 deletions rofl-appd/src/main.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use rofl_appd::{
services::{kms::MockKmsService, metadata::InMemoryMetadataService},
Config,
};
use std::{env, sync::Arc};

#[tokio::main]
async fn main() -> Result<(), rocket::Error> {
let socket = env::args()
.nth(1)
.unwrap_or_else(|| "unix:/run/rofl-appd.sock".to_string());
let seed = env::args()
.nth(2)
.unwrap_or_else(|| "24b41929dc5bc3ec792f8792c7b7c32f".to_string());
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Did you extract this seed from the existing mock SGX version of sapphire-localnet running a ROFL app?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

It is just a random seed for now, but it could be replaced.


let config = Config {
address: &socket,
kms: Arc::new(MockKmsService),
metadata: Arc::new(InMemoryMetadataService::default()),
};

println!("Starting minimal rofl-appd () at {}", socket);

rofl_appd::start_local(config, None, seed.as_bytes()).await
}
26 changes: 26 additions & 0 deletions rofl-appd/src/services/metadata.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,29 @@ impl<A: App> MetadataService for OasisMetadataService<A> {
Ok(map.clone())
}
}

pub struct InMemoryMetadataService {
metadata: RwLock<BTreeMap<String, String>>,
}

impl Default for InMemoryMetadataService {
fn default() -> Self {
Self {
metadata: RwLock::new(BTreeMap::new()),
}
}
}

#[async_trait]
impl MetadataService for InMemoryMetadataService {
async fn set(&self, metadata: BTreeMap<String, String>) -> Result<(), Error> {
let mut map = self.metadata.write().await;
*map = metadata;
Ok(())
}

async fn get(&self) -> Result<BTreeMap<String, String>, Error> {
let map = self.metadata.read().await;
Ok(map.clone())
}
}
53 changes: 53 additions & 0 deletions rofl-appd/src/state.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use anyhow::Ok;
use oasis_runtime_sdk::{crypto::signature::Signer, types::transaction};
use rofl_app_core::{client::SubmitTxOpts, prelude::*};

Expand Down Expand Up @@ -61,3 +62,55 @@ impl<A: App> Env for EnvImpl<A> {
Ok(cbor::to_vec(result))
}
}

pub(crate) struct LocalEnv {
app_id: AppId,
signer: Arc<dyn Signer>,
rpc_url: Option<String>,
}

impl LocalEnv {
pub fn new(app_id: AppId, signer: Arc<dyn Signer>, rpc_url: Option<String>) -> Self {
Self {
app_id,
signer,
rpc_url,
}
}
}

#[async_trait]
impl Env for LocalEnv {
fn app_id(&self) -> AppId {
self.app_id.clone()
}

fn signer(&self) -> Arc<dyn Signer> {
self.signer.clone()
}

async fn sign_and_submit_tx(
&self,
_signer: Arc<dyn Signer>,
_tx: transaction::Transaction,
_opts: SubmitTxOpts,
) -> Result<transaction::CallResult> {
if let Some(rpc_url) = &self.rpc_url {
// Not implemented yet
Ok(transaction::CallResult::default())
} else {
// Mock mode: return default
Ok(transaction::CallResult::default())
}
}

async fn query(&self, method: &str, args: Vec<u8>) -> Result<Vec<u8>> {
if let Some(rpc_url) = &self.rpc_url {
// Not implemented yet
Ok(vec![])
} else {
// Mock mode: return empty
Ok(vec![])
}
}
}
Loading