Skip to content

Commit 20c127d

Browse files
committed
feat(rofl-appd): initial implementation without sapphire
1 parent 394da33 commit 20c127d

6 files changed

Lines changed: 194 additions & 1 deletion

File tree

Cargo.lock

Lines changed: 39 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rofl-appd/Cargo.toml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ rocket = { git = "https://github.com/rwf2/Rocket", rev = "28891e8072136f4641a33f
1818
] }
1919
serde = { version = "1.0", features = ["derive"] }
2020
serde_with = { version = "3.12.0", features = ["hex"] }
21+
serde_json = "1.0"
2122
hex = "0.4"
2223
sp800-185 = "0.2.0"
2324
thiserror = "1.0"
@@ -30,6 +31,7 @@ tokio = { version = "1.38", features = [
3031
] }
3132
tokio-retry = "0.3.0"
3233
zeroize = "1.7"
34+
reqwest = { version = "0.12", features = ["json"] }
3335

3436
[dev-dependencies]
3537
rustc-hex = "2.0.1"
@@ -39,3 +41,8 @@ serde_json = "1.0"
3941
default = ["tx"]
4042
# Add routes for transaction submission.
4143
tx = []
44+
mock = []
45+
46+
[[bin]]
47+
name = "rofl-appd-mock"
48+
path = "src/main.rs"

rofl-appd/src/lib.rs

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@ mod routes;
44
pub mod services;
55
pub(crate) mod state;
66

7+
use oasis_runtime_sdk::crypto::signature::{secp256k1, Signer};
78
use std::sync::Arc;
89

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

11-
use rofl_app_core::{App, Environment};
12+
use crate::state::LocalEnv;
13+
use rofl_app_core::{App, AppId, Environment};
1214

1315
/// API server configuration.
1416
#[derive(Clone)]
@@ -57,3 +59,44 @@ where
5759

5860
Ok(())
5961
}
62+
63+
/// Start the REST API server in mock mode
64+
pub async fn start_local(
65+
cfg: Config<'_>,
66+
rpc_url: Option<String>,
67+
seed: &[u8],
68+
) -> Result<(), rocket::Error> {
69+
let signer = secp256k1::MemorySigner::new_from_seed(seed).unwrap();
70+
71+
// For mock mode, just use a simple stub that implements Env
72+
let env: Arc<dyn state::Env> =
73+
Arc::new(LocalEnv::new(AppId::default(), Arc::new(signer), rpc_url));
74+
75+
// Server configuration.
76+
let rocket_cfg = Figment::from(rocket::config::Config::default())
77+
.select("default")
78+
.merge(("address", cfg.address))
79+
.merge(("reuse", true));
80+
81+
let server = rocket::custom(rocket_cfg)
82+
.manage(cfg.kms)
83+
.manage(env)
84+
.mount("/rofl/v1/app", routes![routes::app::id,])
85+
.mount("/rofl/v1/keys", routes![routes::keys::generate,]);
86+
87+
let server = server.manage(cfg.metadata).mount(
88+
"/rofl/v1/metadata",
89+
routes![routes::metadata::set, routes::metadata::get],
90+
);
91+
92+
let server = server.mount("/rofl/v1/query", routes![routes::query::query]);
93+
94+
#[cfg(feature = "tx")]
95+
let server = server
96+
.manage(routes::tx::Config::default())
97+
.mount("/rofl/v1/tx", routes![routes::tx::sign_and_submit]);
98+
99+
server.launch().await?;
100+
101+
Ok(())
102+
}

rofl-appd/src/main.rs

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
use rofl_appd::{
2+
services::{kms::MockKmsService, metadata::InMemoryMetadataService},
3+
Config,
4+
};
5+
use std::{env, sync::Arc};
6+
7+
#[tokio::main]
8+
async fn main() -> Result<(), rocket::Error> {
9+
let socket = env::args()
10+
.nth(1)
11+
.unwrap_or_else(|| "unix:/run/rofl-appd.sock".to_string());
12+
let seed = env::args()
13+
.nth(2)
14+
.unwrap_or_else(|| "24b41929dc5bc3ec792f8792c7b7c32f".to_string());
15+
16+
let config = Config {
17+
address: &socket,
18+
kms: Arc::new(MockKmsService),
19+
metadata: Arc::new(InMemoryMetadataService::default()),
20+
};
21+
22+
println!("Starting minimal rofl-appd () at {}", socket);
23+
24+
rofl_appd::start_local(config, None, seed.as_bytes()).await
25+
}

rofl-appd/src/services/metadata.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,3 +104,29 @@ impl<A: App> MetadataService for OasisMetadataService<A> {
104104
Ok(map.clone())
105105
}
106106
}
107+
108+
pub struct InMemoryMetadataService {
109+
metadata: RwLock<BTreeMap<String, String>>,
110+
}
111+
112+
impl Default for InMemoryMetadataService {
113+
fn default() -> Self {
114+
Self {
115+
metadata: RwLock::new(BTreeMap::new()),
116+
}
117+
}
118+
}
119+
120+
#[async_trait]
121+
impl MetadataService for InMemoryMetadataService {
122+
async fn set(&self, metadata: BTreeMap<String, String>) -> Result<(), Error> {
123+
let mut map = self.metadata.write().await;
124+
*map = metadata;
125+
Ok(())
126+
}
127+
128+
async fn get(&self) -> Result<BTreeMap<String, String>, Error> {
129+
let map = self.metadata.read().await;
130+
Ok(map.clone())
131+
}
132+
}

rofl-appd/src/state.rs

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use anyhow::Ok;
12
use oasis_runtime_sdk::{crypto::signature::Signer, types::transaction};
23
use rofl_app_core::{client::SubmitTxOpts, prelude::*};
34

@@ -61,3 +62,55 @@ impl<A: App> Env for EnvImpl<A> {
6162
Ok(cbor::to_vec(result))
6263
}
6364
}
65+
66+
pub(crate) struct LocalEnv {
67+
app_id: AppId,
68+
signer: Arc<dyn Signer>,
69+
rpc_url: Option<String>,
70+
}
71+
72+
impl LocalEnv {
73+
pub fn new(app_id: AppId, signer: Arc<dyn Signer>, rpc_url: Option<String>) -> Self {
74+
Self {
75+
app_id,
76+
signer,
77+
rpc_url,
78+
}
79+
}
80+
}
81+
82+
#[async_trait]
83+
impl Env for LocalEnv {
84+
fn app_id(&self) -> AppId {
85+
self.app_id.clone()
86+
}
87+
88+
fn signer(&self) -> Arc<dyn Signer> {
89+
self.signer.clone()
90+
}
91+
92+
async fn sign_and_submit_tx(
93+
&self,
94+
_signer: Arc<dyn Signer>,
95+
_tx: transaction::Transaction,
96+
_opts: SubmitTxOpts,
97+
) -> Result<transaction::CallResult> {
98+
if let Some(rpc_url) = &self.rpc_url {
99+
// Not implemented yet
100+
Ok(transaction::CallResult::default())
101+
} else {
102+
// Mock mode: return default
103+
Ok(transaction::CallResult::default())
104+
}
105+
}
106+
107+
async fn query(&self, method: &str, args: Vec<u8>) -> Result<Vec<u8>> {
108+
if let Some(rpc_url) = &self.rpc_url {
109+
// Not implemented yet
110+
Ok(vec![])
111+
} else {
112+
// Mock mode: return empty
113+
Ok(vec![])
114+
}
115+
}
116+
}

0 commit comments

Comments
 (0)