diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..d40610a --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,29 @@ +name: github pages +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + contents: write + concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + steps: + - uses: actions/checkout@v2 + - name: Setup mdBook + uses: peaceiris/actions-mdbook@v2 + with: + mdbook-version: "latest" + + - run: cd docs && mdbook build + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + if: ${{ github.ref == 'refs/heads/main' }} + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./docs/book + force_orphan: true diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 149d3d2..8d2159f 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -1,9 +1,9 @@ name: CI on: push: - branches: [main, jadex] + branches: [main] pull_request: - branches: [main, jadex] + branches: [main] concurrency: group: ${{ github.head_ref || github.run_id }} @@ -15,24 +15,29 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: dtolnay/rust-toolchain@stable + - name: Install Toolchain + run: | + rustup toolchain install 1.90.0 + rustup component add rust-src --toolchain 1.90.0 + rustup update stable && rustup default stable - uses: taiki-e/install-action@sccache - uses: taiki-e/install-action@nextest - - name: Setup Token - run: | - git config --global credential.helper store - echo "https://x-access-token:${{ secrets.SPACEJAM_TOKEN }}@github.com" > ~/.git-credentials - name: Run Tests - run: cargo nextest run --workspace --no-fail-fast --release + run: cargo nextest run --no-fail-fast --release check: name: Check runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - - uses: arduino/setup-protoc@v3 - - uses: dtolnay/rust-toolchain@stable - - uses: Swatinem/rust-cache@v2 + - name: Install Toolchain + run: | + rustup toolchain install 1.90.0 + rustup component add rust-src --toolchain 1.90.0 + rustup update stable && rustup default stable + rustup component add clippy rustfmt + - uses: crate-ci/typos@v1.24.5 + - uses: taiki-e/install-action@sccache - name: Format run: cargo fmt --check - name: Clippy diff --git a/.github/workflows/typos.yml b/.github/workflows/typos.yml index d2145b5..f46b8b2 100644 --- a/.github/workflows/typos.yml +++ b/.github/workflows/typos.yml @@ -2,9 +2,9 @@ name: Typos on: push: - branches: [main, jadex] + branches: [main] pull_request: - branches: [main, jadex] + branches: [main] env: TERM: xterm-256color diff --git a/Cargo.lock b/Cargo.lock index e90d17e..5577ee8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -135,7 +135,7 @@ checksum = "2fd1289c04a9ea8cb22300a459a72a385d7c73d3259e2ed7dcb2af674838cfa9" [[package]] name = "cjam" -version = "0.0.13-pre.1" +version = "0.0.13" dependencies = [ "anyhow", "clap", @@ -391,9 +391,10 @@ checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" [[package]] name = "jade" -version = "0.0.13-pre.1" +version = "0.0.13" dependencies = [ "anyhow", + "cjam", "jade-derive", "jade-testing", "polkavm-derive", @@ -404,7 +405,7 @@ dependencies = [ [[package]] name = "jade-derive" -version = "0.0.13-pre.1" +version = "0.0.13" dependencies = [ "polkavm-derive-impl", "proc-macro2", @@ -414,7 +415,7 @@ dependencies = [ [[package]] name = "jade-testing" -version = "0.0.13-pre.1" +version = "0.0.13" dependencies = [ "anyhow", "cjam", @@ -521,7 +522,7 @@ checksum = "32a282da65faaf38286cf3be983213fcf1d2e2a58700e808f83f4ea9a4804bc0" [[package]] name = "nauth" -version = "0.0.13-pre.1" +version = "0.0.13" dependencies = [ "cjam", "jade", @@ -776,9 +777,9 @@ dependencies = [ [[package]] name = "serde-jam" -version = "0.0.13-pre.1" +version = "0.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca0e615b76892199b8e52e41664ee964049c15d00fd0d8b7f6451a546426805a" +checksum = "898d2c5713cad74d319eae03319eaaf024b53683be8db823f91ac81e35385897" dependencies = [ "anyhow", "serde", @@ -849,9 +850,9 @@ checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "spacejam-service" -version = "0.0.13-pre.1" +version = "0.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff6e1196c2519547d8534a28f4c1871a6345c79b3f849c0ea3198b620656394a" +checksum = "4537ded3f33d4bd089dfb76a54ad1f824d9324ff7da5310775e6bad7046b88ee" dependencies = [ "anyhow", "blake2", @@ -864,9 +865,9 @@ dependencies = [ [[package]] name = "spacejson" -version = "0.0.13-pre.1" +version = "0.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53492127b4e77736c0471af3e8429861053f9fa2498c283fccbfd40968d21807" +checksum = "428fd09eab6995f5598ae62a9176599a189ccbd76c64d59ff936563efe936c2e" dependencies = [ "anyhow", "hex", @@ -876,9 +877,9 @@ dependencies = [ [[package]] name = "spacejson-derive" -version = "0.0.13-pre.1" +version = "0.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66f22eb5eb367dc54dda0d26c8eea09c8f37cf59658f2198b8d3614199db8547" +checksum = "b335da6a78a99eb68bb3b1df2568b6a24b4addc132fd048406e9f6299a68b21d" dependencies = [ "proc-macro2", "quote", @@ -887,7 +888,7 @@ dependencies = [ [[package]] name = "spacevm-sys" -version = "0.0.13-pre.1" +version = "0.0.13" dependencies = [ "anyhow", "serde-jam", @@ -902,7 +903,7 @@ checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "stoken" -version = "0.0.13-pre.1" +version = "0.0.13" dependencies = [ "cjam", "jade", diff --git a/Cargo.toml b/Cargo.toml index ea3f7e2..b337c51 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,27 +3,27 @@ members = ["crates/*", "crates/jade/derive", "services/*"] resolver = "2" [workspace.package] -version = "0.0.13-pre.1" +version = "0.0.13" edition = "2024" authors = ["clearloop "] license = "GPL-3.0" -homepage = "https://github.com/spacejamapp/spacejam" +homepage = "https://spacejam.app" repository = "https://github.com/spacejamapp/jade" [workspace.dependencies] -cjam = { path = "crates/cli" } -jade = { path = "crates/jade", default-features = false } -jade-derive = { path = "crates/jade/derive" } -spacevm = { path = "crates/sys", package = "spacevm-sys" } -testing = { path = "crates/testing", package = "jade-testing" } +cjam = { path = "crates/cli", version = "0.0.13" } +jade = { path = "crates/jade", default-features = false, version = "0.0.13" } +jade-derive = { path = "crates/jade/derive", version = "0.0.13" } +spacevm = { path = "crates/sys", package = "spacevm-sys", version = "0.0.13" } +testing = { path = "crates/testing", package = "jade-testing", version = "0.0.13" } # services nauth = { path = "services/nauth" } stoken = { path = "services/stoken" } # spacejam dependencies -codec = { package = "serde-jam", version = "0.0.13-pre.1", default-features = false } -service = { package = "spacejam-service", version = "0.0.13-pre.1", default-features = false } +codec = { package = "serde-jam", version = "0.0.13", default-features = false } +service = { package = "spacejam-service", version = "0.0.13", default-features = false } # crates.io anyhow = { version = "1.0.93", default-features = false } @@ -48,3 +48,6 @@ tiny-keccak = { version = "2.0.2", features = ["keccak"] } toml = { version = "0.8.19", features = ["parse"] } tracing = "0.1.41" tracing-subscriber = { version = "0.3.19", features = ["env-filter"] } + +[workspace.metadata.conta] +packages = ["cjam", "spacevm", "testing", "jade-derive", "jade"] diff --git a/README.md b/README.md index f832f5d..83506da 100644 --- a/README.md +++ b/README.md @@ -1,18 +1,24 @@ -> [!NOTE] -> -> This repo is still under heavy development -> -> The local testing simulator requires a binary of `pvms` which will be published soon! +# Jade Framework -# Jade +![main](https://github.com/spacejamapp/jade/actions/workflows/main.yml/badge.svg) +[![crate](https://img.shields.io/crates/v/jade.svg)](https://crates.io/crates/jade) +[![doc](https://img.shields.io/badge/current-docs-brightgreen.svg)](https://docs.rs/jade/) +[![downloads](https://img.shields.io/crates/d/jade.svg)](https://crates.io/crates/jade) +[![telegram](https://img.shields.io/badge/telegram-blue?logo=telegram)](https://t.me/spacejamapp) +[![LICENSE](https://img.shields.io/crates/l/jade.svg)](https://choosealicense.com/licenses/gpl-3.0/) -JAM service libraries. +Jade framework for building JAM services by [SpaceJam](https://spacejam.app). -- Libraries started with `jam-` including `crates/cli` are basically forks of the work done by - the `polkajam` team from crates.io. -- Libraries started with `jade-` are for the `jade` project. -- Libraries started with `spacejam` will be migrated to the `spacejam` main repo once - we got open sourced. +## Quick Start + +``` +cargo install jade +jade new my-service +cd my-service +jade build +``` + +See also our template at [service-template](https://github.com/spacejamapp/service-template). ## License diff --git a/crates/cli/Cargo.toml b/crates/cli/Cargo.toml index ceeb3e2..2d70a7f 100644 --- a/crates/cli/Cargo.toml +++ b/crates/cli/Cargo.toml @@ -2,6 +2,13 @@ name = "cjam" version.workspace = true edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +documentation = "https://docs.rs/cjam" +description = "Jam service builder" +keywords = ["pvm", "spacejam", "jam"] [dependencies] anyhow.workspace = true diff --git a/crates/cli/README.md b/crates/cli/README.md index 77d606c..bd30d3a 100644 --- a/crates/cli/README.md +++ b/crates/cli/README.md @@ -1,4 +1,4 @@ ## cargo-jam Jam service builder based on the fork of [jam-pvm-build](https://docs.rs/jam-pvm-build) -by the `polkajam` team with sort of error handling fixes. +by the `polkajam` team with sort of fixes. diff --git a/crates/cli/src/bin/cargo-jam.rs b/crates/cli/src/bin/cargo-jam.rs deleted file mode 100644 index 8c0e5a4..0000000 --- a/crates/cli/src/bin/cargo-jam.rs +++ /dev/null @@ -1,7 +0,0 @@ -use cjam::cmd::App; -use clap::Parser; - -fn main() { - let app = App::parse(); - app.run().unwrap() -} diff --git a/crates/cli/src/builder.rs b/crates/cli/src/builder.rs index 5a19159..6642b41 100644 --- a/crates/cli/src/builder.rs +++ b/crates/cli/src/builder.rs @@ -18,9 +18,13 @@ use std::{ process::Command, }; +/// Type of blob to build pub enum BlobType { + /// Service blob Service, + /// Authorizer blob Authorizer, + /// CoreVM guest blob CoreVmGuest, } impl Display for BlobType { @@ -34,6 +38,7 @@ impl Display for BlobType { } impl BlobType { + /// Get the dispatch table for the blob type pub fn dispatch_table(&self) -> Vec> { match self { Self::Service => vec![b"jade_refine".into(), b"jade_accumulate".into()], @@ -51,9 +56,13 @@ impl BlobType { } } +/// Type of profile to build pub enum ProfileType { + /// Debug profile Debug, + /// Release profile Release, + /// Other profile Other(&'static str), } impl ProfileType { @@ -252,6 +261,7 @@ pub fn build_pvm_blob( (name, output_file) } +/// Macro to include the PVM binary #[macro_export] macro_rules! pvm_binary { ($name:literal) => { diff --git a/crates/cli/src/cmd/mod.rs b/crates/cli/src/cmd/mod.rs index 5e83bfa..fa152a0 100644 --- a/crates/cli/src/cmd/mod.rs +++ b/crates/cli/src/cmd/mod.rs @@ -1,11 +1,12 @@ //! Command registry -pub use build::Build; use clap::{Parser, command}; +pub use {build::Build, new::New}; mod build; +mod new; -/// jam service command line interfaces +/// Jam service command line interface #[derive(Debug, Parser)] #[command(version, about, long_about = None)] pub struct App { @@ -18,6 +19,7 @@ impl App { pub fn run(&self) -> Result<(), anyhow::Error> { match &self.command { Command::Build(build) => build.run(), + Command::New(new) => new.run(), } } } @@ -26,6 +28,10 @@ impl App { #[derive(Debug, Parser)] #[command(author, version, about, long_about = None)] pub enum Command { + /// Build a JAM PVM blob #[command(about = "Build a JAM PVM blob")] Build(Build), + /// Create a new JAM service + #[command(about = "Create a new JAM service")] + New(New), } diff --git a/crates/cli/src/cmd/new.rs b/crates/cli/src/cmd/new.rs new file mode 100644 index 0000000..d59820d --- /dev/null +++ b/crates/cli/src/cmd/new.rs @@ -0,0 +1,43 @@ +//! `jam new` command + +use clap::Parser; +use std::{fs, path::PathBuf, process::Command}; + +/// CLI utility for creating a new JAM service +#[derive(Parser, Debug, Default)] +pub struct New { + /// Name of the service to create + name: String, + + /// Path to create the service + #[arg(short, long, default_value = ".")] + path: PathBuf, + + /// Do not initialize a git repository + #[arg(short, long, name = "no-git")] + nogit: bool, +} + +impl New { + /// Run the command. + pub fn run(&self) -> anyhow::Result<()> { + let target = self.path.join(self.name.clone()); + let mut git = Command::new("git"); + git.args([ + "clone", + "https://github.com/spacejamapp/service-template.git", + "--depth=1", + ]); + git.arg(target.clone()); + git.status()?; + + // remove git files if not desired + if self.nogit { + fs::remove_file(target.join(".git")).ok(); + fs::remove_file(target.join(".gitignore")).ok(); + fs::remove_file(target.join(".github")).ok(); + } + + Ok(()) + } +} diff --git a/crates/cli/src/lib.rs b/crates/cli/src/lib.rs index 108de64..7108795 100644 --- a/crates/cli/src/lib.rs +++ b/crates/cli/src/lib.rs @@ -1,6 +1,11 @@ -//! Command line interface for JAM services +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] -pub use manifest::{ModuleType, Profile}; +pub use { + clap, + manifest::{ModuleType, Profile}, + util::build, +}; pub mod builder; pub mod cmd; diff --git a/crates/cli/src/manifest.rs b/crates/cli/src/manifest.rs index fe156c9..5e497a7 100644 --- a/crates/cli/src/manifest.rs +++ b/crates/cli/src/manifest.rs @@ -4,6 +4,7 @@ use crate::builder::ProfileType; use jam_program_blob::CrateInfo; use std::{path::Path, process::Command}; +/// Type of module to build #[derive(clap::ValueEnum, Clone, Debug, Default)] #[value(rename_all = "lowercase")] pub enum ModuleType { @@ -22,6 +23,7 @@ pub enum ModuleType { CoreVmGuest, } +/// Type of profile to build #[derive(clap::ValueEnum, Clone, Debug, Default)] #[value(rename_all = "lowercase")] pub enum Profile { diff --git a/crates/cli/src/util.rs b/crates/cli/src/util.rs index dd5e2a6..6043234 100644 --- a/crates/cli/src/util.rs +++ b/crates/cli/src/util.rs @@ -2,16 +2,16 @@ use crate::ModuleType; use anyhow::Result; -use std::{ - env, fs, - path::{Path, PathBuf}, - time::SystemTime, -}; +use std::{env, fs, path::PathBuf}; /// Build the PVM blob /// /// NOTE: this is used for the build script of services -pub fn build(package: &str, module: Option, path: Option) -> Result<()> { +pub fn build(package: &str, module: Option) -> Result<()> { + println!("rerun-if-changed=build.rs"); + println!("rerun-if-changed=src"); + + // avoid shadowing build on riscv target let target = env::var("TARGET")?; if target.contains("polkavm") { return Ok(()); @@ -19,49 +19,21 @@ pub fn build(package: &str, module: Option, path: Option) -> // Build the service let target = etc::find_up("target")?; - let current = path - .map(PathBuf::from) - .unwrap_or_else(|| env::current_dir().expect("Unable to get current directory")); - let binary = target.join("jam").join(format!("{package}.jam")); - let rebuild = if !binary.exists() { - true - } else { - let modified = fs::metadata(&binary)?.modified()?; - check_modified(¤t, modified)? - }; - - if rebuild { - let mut build = crate::cmd::Build::default(); - if let Some(module) = module { - build.module = module; - } - build.target = Some(target); - build.run()?; + let jam = target.join("jam"); + let parget = jam.join(package); + let mut build = crate::cmd::Build::default(); + if let Some(module) = module { + build.module = module; } + build.target = Some(parget.clone()); + build.run()?; // copy service to OUT_DIR + let name = format!("{package}.jam"); + let binary = parget.join("jam").join(&name); let service = PathBuf::from(env::var("OUT_DIR")?).join("service.jam"); println!("Copying service to OUT_DIR: {}", service.display()); fs::copy(&binary, &service)?; + fs::copy(&binary, jam.join(name))?; Ok(()) } - -/// Check if any Rust source files have been modified after the given time -fn check_modified(dir: &Path, since: SystemTime) -> Result { - for entry in fs::read_dir(dir)? { - let entry = entry?; - let path = entry.path(); - let metadata = entry.metadata()?; - if metadata.is_dir() { - if check_modified(&path, since)? { - return Ok(true); - } - } else if path.extension().and_then(|s| s.to_str()) == Some("rs") - && metadata.modified()? > since - { - return Ok(true); - } - } - - Ok(false) -} diff --git a/crates/jade/Cargo.toml b/crates/jade/Cargo.toml index 00929eb..f3bc0ac 100644 --- a/crates/jade/Cargo.toml +++ b/crates/jade/Cargo.toml @@ -6,6 +6,13 @@ authors.workspace = true license.workspace = true homepage.workspace = true repository.workspace = true +documentation = "https://docs.rs/jade" +description = "Jade framework for building JAM services" +keywords = ["pvm", "spacejam", "jam"] + +[[bin]] +name = "jade" +path = "bin/jade.rs" [dependencies] anyhow.workspace = true @@ -14,11 +21,13 @@ jade-derive.workspace = true serde.workspace = true service.workspace = true polkavm-derive.workspace = true -testing = { workspace = true, optional = true } + +[target.'cfg(not(target_arch = "riscv64"))'.dependencies] +cjam.workspace = true +testing.workspace = true [features] default = [] logging = [] -testing = ["dep:testing"] std = ["anyhow/std", "codec/std", "serde/std", "service/std"] tiny = [] diff --git a/crates/jade/README.md b/crates/jade/README.md index 5e3beb9..17945ba 100644 --- a/crates/jade/README.md +++ b/crates/jade/README.md @@ -1 +1,14 @@ -Jade framework for building JAM services. +# Jade + +Jade framework for building JAM services by [SpaceJam](https://spacejam.app). + +```rust +cargo install jade +jade new my-service +cd my-service +jade build +``` + +## License + +GPL-3.0 diff --git a/crates/jade/bin/jade.rs b/crates/jade/bin/jade.rs new file mode 100644 index 0000000..e56076b --- /dev/null +++ b/crates/jade/bin/jade.rs @@ -0,0 +1,11 @@ +//! jade command line interface + +#[cfg(not(target_arch = "riscv64"))] +fn main() { + use cjam::{clap::Parser, cmd::App}; + let app = App::parse(); + app.run().unwrap() +} + +#[cfg(target_arch = "riscv64")] +fn main() {} diff --git a/crates/jade/src/host/general.rs b/crates/jade/src/host/general.rs index 1d4bd75..f692690 100644 --- a/crates/jade/src/host/general.rs +++ b/crates/jade/src/host/general.rs @@ -42,9 +42,7 @@ pub mod storage { ) }; - if len == u64::MAX { - return None; - } else if len == 0 { + if len == u64::MAX || len == 0 { return None; } diff --git a/crates/jade/src/lib.rs b/crates/jade/src/lib.rs index abd3abb..07a9af5 100644 --- a/crates/jade/src/lib.rs +++ b/crates/jade/src/lib.rs @@ -1,4 +1,5 @@ #![doc = include_str!("../README.md")] +#![deny(missing_docs)] #![cfg_attr(not(feature = "std"), no_std)] #[cfg(not(feature = "std"))] @@ -10,14 +11,14 @@ pub mod host; pub mod logging; pub mod prelude; -#[cfg(feature = "testing")] -pub use testing; +#[cfg(not(target_arch = "riscv64"))] +pub use {cjam, testing}; -#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +#[cfg(target_arch = "riscv64")] #[global_allocator] static ALLOCATOR: polkavm_derive::LeakingAllocator = polkavm_derive::LeakingAllocator; -#[cfg(any(target_arch = "riscv32", target_arch = "riscv64"))] +#[cfg(target_arch = "riscv64")] #[panic_handler] fn panic(_info: &core::panic::PanicInfo) -> ! { unsafe { diff --git a/crates/jade/src/logging.rs b/crates/jade/src/logging.rs index 0eb1fea..5fe914c 100644 --- a/crates/jade/src/logging.rs +++ b/crates/jade/src/logging.rs @@ -1,64 +1,65 @@ -//! This module is extracted from `jam-pvm-common` +//! This module is extracted from `jam-pvm-common` with fixes -#[cfg(any(feature = "logging", doc))] -pub mod stuff { +pub use api::*; - /// Log a message with the `error` level. Regular formatting may be used. - #[macro_export] - macro_rules! error { - (target=$target:expr,$($arg:tt)*) => { - $crate::logging::stuff::log_target(0, $target, &$crate::prelude::format!($($arg)*)); - }; - ($($arg:tt)*) => { - $crate::logging::stuff::log(0, &$crate::prelude::format!($($arg)*)); - }; - } +/// Log a message with the `error` level. Regular formatting may be used. +#[macro_export] +macro_rules! error { + (target=$target:expr,$($arg:tt)*) => { + $crate::logging::log_target(0, $target, &$crate::prelude::format!($($arg)*)); + }; + ($($arg:tt)*) => { + $crate::logging::log(0, &$crate::prelude::format!($($arg)*)); + }; +} - /// Log a message with the `warn` level. Regular formatting may be used. - #[macro_export] - macro_rules! warn { - (target=$target:expr,$($arg:tt)*) => { - $crate::logging::stuff::log_target(1, $target, &$crate::prelude::format!($($arg)*)); - }; - ($($arg:tt)*) => { - $crate::logging::stuff::log(1, &$crate::prelude::format!($($arg)*)); - }; - } +/// Log a message with the `warn` level. Regular formatting may be used. +#[macro_export] +macro_rules! warn { + (target=$target:expr,$($arg:tt)*) => { + $crate::logging::log_target(1, $target, &$crate::prelude::format!($($arg)*)); + }; + ($($arg:tt)*) => { + $crate::logging::log(1, &$crate::prelude::format!($($arg)*)); + }; +} - /// Log a message with the `info` level. Regular formatting may be used. - #[macro_export] - macro_rules! info { - (target=$target:expr,$($arg:tt)*) => { - $crate::logging::stuff::log_target(2, $target, &$crate::prelude::format!($($arg)*)); - }; - ($($arg:tt)*) => { - $crate::logging::stuff::log(2, &$crate::prelude::format!($($arg)*)); - }; - } +/// Log a message with the `info` level. Regular formatting may be used. +#[macro_export] +macro_rules! info { + (target=$target:expr,$($arg:tt)*) => { + $crate::logging::log_target(2, $target, &$crate::prelude::format!($($arg)*)); + }; + ($($arg:tt)*) => { + $crate::logging::log(2, &$crate::prelude::format!($($arg)*)); + }; +} - /// Log a message with the `debug` level. Regular formatting may be used. - #[macro_export] - macro_rules! debug { - (target=$target:expr,$($arg:tt)*) => { - $crate::logging::stuff::log_target(3, $target, &$crate::prelude::format!($($arg)*)); - }; - ($($arg:tt)*) => { - $crate::logging::stuff::log(3, &$crate::prelude::format!($($arg)*)); - }; - } +/// Log a message with the `debug` level. Regular formatting may be used. +#[macro_export] +macro_rules! debug { + (target=$target:expr,$($arg:tt)*) => { + $crate::logging::log_target(3, $target, &$crate::prelude::format!($($arg)*)); + }; + ($($arg:tt)*) => { + $crate::logging::log(3, &$crate::prelude::format!($($arg)*)); + }; +} - /// Log a message with the `trace` level. Regular formatting may be used. - #[macro_export] - macro_rules! trace { - (target=$target:expr,$($arg:tt)*) => { - $crate::logging::stuff::log_target(4, $target, &$crate::prelude::format!($($arg)*)); - }; - ($($arg:tt)*) => { - $crate::logging::stuff::log(4, &$crate::prelude::format!($($arg)*)); - }; - } +/// Log a message with the `trace` level. Regular formatting may be used. +#[macro_export] +macro_rules! trace { + (target=$target:expr,$($arg:tt)*) => { + $crate::logging::log_target(4, $target, &$crate::prelude::format!($($arg)*)); + }; + ($($arg:tt)*) => { + $crate::logging::log(4, &$crate::prelude::format!($($arg)*)); + }; +} - // CAUTION: Not public API. DO NOT USE. +#[cfg(any(feature = "logging", doc))] +mod api { + /// CAUTION: Not public API. DO NOT USE. pub fn log_target(level: u64, target: &str, msg: &str) { let t = target.as_bytes(); let m = msg.as_bytes(); @@ -73,7 +74,7 @@ pub mod stuff { } } - // CAUTION: Not public API. DO NOT USE. + /// CAUTION: Not public API. DO NOT USE. pub fn log(level: u64, msg: &str) { let m = msg.as_bytes(); unsafe { @@ -83,44 +84,14 @@ pub mod stuff { } #[cfg(not(any(feature = "logging", doc)))] -pub mod stuff { - /// Log a message with the `error` level. Regular formatting may be used.mod stuff { - #[macro_export] - macro_rules! error { - ($($arg:tt)*) => { - { let _ = ($( $arg, )*); } - }; - } - - /// Log a message with the `warn` level. Regular formatting may be used. - #[macro_export] - macro_rules! warn { - ($($arg:tt)*) => { - { let _ = ($( $arg, )*); } - }; - } - - /// Log a message with the `info` level. Regular formatting may be used. - #[macro_export] - macro_rules! info { - ($($arg:tt)*) => { - { let _ = ($( $arg, )*); } - }; - } - - /// Log a message with the `debug` level. Regular formatting may be used. - #[macro_export] - macro_rules! debug { - ($($arg:tt)*) => { - { let _ = ($( $arg, )*); } - }; - } +mod api { + /// CAUTION: Not public API. DO NOT USE. + pub fn log_target(level: u64, target: &str, msg: &str) { + let _ = (level, target, msg); + } - /// Log a message with the `trace` level. Regular formatting may be used. - #[macro_export] - macro_rules! trace { - ($($arg:tt)*) => { - { let _ = ($( $arg, )*); } - }; - } + /// CAUTION: Not public API. DO NOT USE. + pub fn log(level: u64, msg: &str) { + let _ = (level, msg); + } } diff --git a/crates/sys/Cargo.toml b/crates/sys/Cargo.toml index cf8e551..fe6d378 100644 --- a/crates/sys/Cargo.toml +++ b/crates/sys/Cargo.toml @@ -8,7 +8,7 @@ repository.workspace = true description = "SpaceVM system interfaces" documentation = "https://docs.rs/spacevm-sys" categories = ["embedded", "api-bindings"] -keywords = ["pvm", "spacejam", "vm"] +keywords = ["pvm", "spacejam", "vm", "jam"] include = ["lib", "src", "build.rs", "Cargo.toml", "README.md"] [dependencies] diff --git a/crates/sys/src/lib.rs b/crates/sys/src/lib.rs index fd7d7a2..b32bc1f 100644 --- a/crates/sys/src/lib.rs +++ b/crates/sys/src/lib.rs @@ -1,4 +1,5 @@ -//! SpaceVM system interface +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] use crate::abi::Buffer; pub use abi::init_logger; @@ -62,7 +63,7 @@ mod abi { impl Buffer { /// Get the buffer as a byte slice - pub fn to_vec(&self) -> Vec { + pub fn to_vec(self) -> Vec { let result = unsafe { core::slice::from_raw_parts(self.ptr, self.len).to_vec() }; unsafe { let layout = std::alloc::Layout::from_size_align(self.len, 1).unwrap(); diff --git a/crates/testing/Cargo.toml b/crates/testing/Cargo.toml index c5a8bb2..98f669d 100644 --- a/crates/testing/Cargo.toml +++ b/crates/testing/Cargo.toml @@ -2,6 +2,13 @@ name = "jade-testing" version.workspace = true edition.workspace = true +authors.workspace = true +license.workspace = true +homepage.workspace = true +repository.workspace = true +documentation = "https://docs.rs/jade-testing" +description = "Testing library for the JAM services" +keywords = ["pvm", "spacejam", "jam"] [dependencies] anyhow.workspace = true diff --git a/crates/testing/README.md b/crates/testing/README.md index e69de29..7b82420 100644 --- a/crates/testing/README.md +++ b/crates/testing/README.md @@ -0,0 +1,7 @@ +# Jade Testing + +Testing library for the JAM services. + +## License + +GPL-3.0 diff --git a/crates/testing/src/account.rs b/crates/testing/src/account.rs index 09afb4b..4fdee83 100644 --- a/crates/testing/src/account.rs +++ b/crates/testing/src/account.rs @@ -40,7 +40,7 @@ impl Jam { let vkey = key.to_vec(); let key = key::storage(service, &codec::encode(&vkey).ok()?); let encoded = account.storage.get(key.as_ref())?; - codec::decode(&mut &encoded[..]).ok() + codec::decode(&encoded[..]).ok() } /// Set the code of the service account diff --git a/crates/testing/src/exec.rs b/crates/testing/src/exec.rs index 87d7bbc..ff57f8b 100644 --- a/crates/testing/src/exec.rs +++ b/crates/testing/src/exec.rs @@ -53,7 +53,7 @@ impl ExecutionInfo { let vkey = key.to_vec(); let key = key::storage(service, &vkey); let encoded = account.storage.get(key.as_ref())?; - codec::decode(&mut &encoded[..]).ok() + codec::decode(encoded).ok() } } @@ -78,7 +78,7 @@ impl Jam { spacevm::authorize(AuthorizeArgs { package: work.clone(), - core_idx: core_idx, + core_idx, accounts: self.chain.accounts.clone(), timeslot: self.chain.best.slot, }) diff --git a/crates/testing/src/lib.rs b/crates/testing/src/lib.rs index b91bf08..f21400e 100644 --- a/crates/testing/src/lib.rs +++ b/crates/testing/src/lib.rs @@ -1,4 +1,5 @@ -//! Testing library for the PVM +#![doc = include_str!("../README.md")] +#![deny(missing_docs)] pub use service::service::ServiceAccount as Account; use service::service::WorkItem; diff --git a/crates/testing/src/util.rs b/crates/testing/src/util.rs index 04ce0c0..a7565ba 100644 --- a/crates/testing/src/util.rs +++ b/crates/testing/src/util.rs @@ -26,8 +26,8 @@ pub fn load_service(package: &str) -> Result> { } /// Build the service -pub fn build_service(package: &str, module: Option, path: Option) { - cjam::util::build(package, module, path).expect("Failed to build service"); +pub fn build_service(package: &str, module: Option) { + cjam::util::build(package, module).expect("Failed to build service"); } /// Load the current service diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 0000000..7585238 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1 @@ +book diff --git a/docs/book.toml b/docs/book.toml new file mode 100644 index 0000000..1873ab3 --- /dev/null +++ b/docs/book.toml @@ -0,0 +1,5 @@ +[book] +authors = ["clearloop"] +language = "en" +src = "src" +title = "Jade Handbook" diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md new file mode 100644 index 0000000..797759b --- /dev/null +++ b/docs/src/SUMMARY.md @@ -0,0 +1,9 @@ +# Jade Handbook + +[Jade Framework](./jade.md) + +# Service + +- [Structure](./service/structure.md) +- [Build](./service/build.md) +- [Testing](./service/testing.md) diff --git a/docs/src/jade.md b/docs/src/jade.md new file mode 100644 index 0000000..70d1f99 --- /dev/null +++ b/docs/src/jade.md @@ -0,0 +1,19 @@ +# Jade Framework + +[Jade][jade] is a development framework for building polkadot JAM services developed by [SpaceJam](https://spacejam.app). + +### Installation + +```bash +cargo install jade +``` + +### Quick Start + +```bash +jade new my-service +cd my-service +cargo nextest run +``` + +[jade]: https://github.com/spacejamapp/jade diff --git a/docs/src/quickstart.md b/docs/src/quickstart.md new file mode 100644 index 0000000..98b6b5b --- /dev/null +++ b/docs/src/quickstart.md @@ -0,0 +1 @@ +# quickstart diff --git a/docs/src/service/build.md b/docs/src/service/build.md new file mode 100644 index 0000000..8588fd5 --- /dev/null +++ b/docs/src/service/build.md @@ -0,0 +1,34 @@ +# Build Service + +There are two ways to build a service: using jade command line tool or using rust build script. + +## Using jade command line tool + +```bash +cargo install jade +cd my-service +jade build +``` + +## Using rust build script + +```toml +# my-service/Cargo.toml +# +# ... +# +[build-dependencies] +cjam = "*" +``` + +```rust +// my-service/build.rs + +fn main() { + cjam::util::build( + env!("CARGO_PKG_NAME"), + // or Some(cjam::ModuleType::Authorizer) + Some(cjam::ModuleType::Service), + ).ok(); +} +``` diff --git a/docs/src/service/structure.md b/docs/src/service/structure.md new file mode 100644 index 0000000..3fb3f7c --- /dev/null +++ b/docs/src/service/structure.md @@ -0,0 +1,52 @@ +# Service Structure + +Jade currently supports two types of services: [authorizer service](#authorizer-service) and [general service](#general-service), there is also `corevm service` supported in our toolkit but it has not been +official integrated in JAM implementations yet. + +- `[no_std]`: we need to avoid standard rust library on building `riscv64` services + for making our compiled binaries compatible with the JAM virtual machines. +- `#[jade::is_authorized]`: the entrypoint of the `is_authorized` logic. +- `#[jade::refine]`: the entrypoint of the `refine` logic. +- `#[jade::accumulate]`: the entrypoint of the `accumulate` logic. + +> Note that authorizer service and general service are different service types, you cannot +> declare both `#[jade::is_authorized]` and `#[jade::refine]` (or `#[jade::accumulate]`) +> in the same service. + +### [Authorizer Service][nauth] + +```rust +#![cfg_attr(target_arch = "riscv64", no_std)] +use jade::prelude::{AuthTrace, CoreIndex}; + +#[jade::is_authorized] +fn is_authorized(_core_index: CoreIndex) -> AuthTrace { + Default::default() +} +``` + +### [General Service][stoken] + +```rust +#![cfg_attr(target_arch = "riscv64", no_std)] +use jade::prelude::{AuthTrace, CoreIndex}; + +#[jade::refine] +fn refine( + core: u16, + index: u16, + id: u32, + payload: Vec, + package_hash: OpaqueHash, +) -> Vec { + // ... refine logic here +} + +#[jade::accumulate] +fn accumulate(now: u32, id: u32, results: Vec) -> Option { + // ... accumulate logic here +} +``` + +[nauth]: https://github.com/spacejamapp/jade/blob/main/services/nauth/src/lib.rs +[stoken]: https://github.com/spacejamapp/jade/blob/main/services/stoken/src/lib.rs diff --git a/docs/src/service/testing.md b/docs/src/service/testing.md new file mode 100644 index 0000000..97d662c --- /dev/null +++ b/docs/src/service/testing.md @@ -0,0 +1,36 @@ +# Service Testing + +Jade provides a testing module for testing your service. + +```rust +//! stoken/tests/main.rs +use jade::testing::Jam; +use stoken::{Holders, Instruction, SERVICE}; + +const AUTHORIZER_ID: u32 = 500; +const SERVICE_ID: u32 = 501; +const ALICE: u32 = 0; + +#[test] +fn test_mint() { + // initialize the logger + jade::testing::util::init_logger(); + + // Set up JAM with authorization using the null authorizer service + let mut jam = Jam::default().with_auth(AUTHORIZER_ID, nauth::SERVICE.to_vec()); + jam.add_service(SERVICE_ID, SERVICE.to_vec()); + + // 1. send a mint instruction + let amount = 100; + let instr = vec![Instruction::Mint { to: ALICE, amount }]; + let info = jam + .execute(SERVICE_ID, codec::encode(&instr).unwrap()) + .expect("failed to execute work item"); + + // 2. check the balance + let holders: Holders = info + .get_storage(SERVICE_ID, Holders::key()) + .expect("failed to get holders"); + assert_eq!(holders.balance(ALICE), amount); +} +``` diff --git a/services/nauth/Cargo.toml b/services/nauth/Cargo.toml index 25b58c2..e565886 100644 --- a/services/nauth/Cargo.toml +++ b/services/nauth/Cargo.toml @@ -12,10 +12,7 @@ description = "A JAM authorizer which always authorizes" tiny = [] [dependencies] -jade = { workspace = true, features = ["logging"] } - -[dev-dependencies] -jade = { workspace = true, features = ["testing"] } +jade = { workspace = true } [build-dependencies] cjam.workspace = true diff --git a/services/nauth/build.rs b/services/nauth/build.rs index 2cb3181..a04e6bf 100644 --- a/services/nauth/build.rs +++ b/services/nauth/build.rs @@ -1,10 +1,5 @@ //! Build the service fn main() { - cjam::util::build( - env!("CARGO_PKG_NAME"), - Some(cjam::ModuleType::Authorizer), - None, - ) - .expect("Failed to build service"); + cjam::build(env!("CARGO_PKG_NAME"), Some(cjam::ModuleType::Authorizer)).ok(); } diff --git a/services/stoken/Cargo.toml b/services/stoken/Cargo.toml index 229d933..cebbd24 100644 --- a/services/stoken/Cargo.toml +++ b/services/stoken/Cargo.toml @@ -7,19 +7,18 @@ license.workspace = true homepage.workspace = true repository.workspace = true -[features] -default = [] -tiny = [] -std = ["codec/std"] - [dependencies] codec.workspace = true -serde = { workspace = true, features = ["alloc"] } jade = { workspace = true, features = ["logging"] } +serde.workspace = true [dev-dependencies] nauth.workspace = true -jade = { workspace = true, features = ["testing"] } [build-dependencies] cjam.workspace = true + +[features] +default = [] +tiny = ["jade/tiny"] +std = ["codec/std"] diff --git a/services/stoken/build.rs b/services/stoken/build.rs index f3c07a2..96d47c5 100644 --- a/services/stoken/build.rs +++ b/services/stoken/build.rs @@ -1,10 +1,5 @@ //! Build the service fn main() { - let _ = cjam::util::build( - env!("CARGO_PKG_NAME"), - Some(cjam::ModuleType::Service), - None, - ) - .ok(); + cjam::build(env!("CARGO_PKG_NAME"), Some(cjam::ModuleType::Service)).ok(); } diff --git a/services/stoken/src/service.rs b/services/stoken/src/service.rs index d1753f6..0d1072c 100644 --- a/services/stoken/src/service.rs +++ b/services/stoken/src/service.rs @@ -16,7 +16,7 @@ fn refine( _package_hash: OpaqueHash, ) -> Vec { info!("entering refine logic ..."); - let Ok(instructions) = codec::decode::>(&mut payload.as_slice()) else { + let Ok(instructions) = codec::decode::>(payload.as_slice()) else { error!( target = "simple-token-service", "failed to decode instructions" @@ -26,9 +26,8 @@ fn refine( info!( target = "simple-token-service", - "instructions: {:?}", instructions + "decoded payload as instructions: {:?}", instructions ); - info!("payload: {:?}", payload); payload } @@ -43,7 +42,7 @@ fn accumulate(_now: u32, _id: u32, results: Vec) -> Option None } }) { - let instructions = codec::decode::>(&mut &raw_instructions[..]).unwrap(); + let instructions = codec::decode::>(&raw_instructions).unwrap(); jade::info!("instructions: {:?}", instructions); for inst in instructions { match inst {