diff --git a/Cargo.lock b/Cargo.lock index 5899e94..d870cdc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -406,7 +406,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -441,7 +441,7 @@ checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -784,7 +784,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", "syn_derive", ] @@ -821,7 +821,7 @@ checksum = "4da9a32f3fed317401fa3c862968128267c3106685286e15d5aaa3d7389c2f60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -931,7 +931,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -1215,7 +1215,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -1239,7 +1239,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.10.0", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -1250,7 +1250,7 @@ checksum = "a668eda54683121533a393014d8692171709ff57a7d61f187b6e782719f8933f" dependencies = [ "darling_core", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -1548,7 +1548,7 @@ checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -1739,7 +1739,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -1841,7 +1841,7 @@ checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -2389,6 +2389,7 @@ checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" dependencies = [ "equivalent", "hashbrown", + "serde", ] [[package]] @@ -2650,6 +2651,27 @@ version = "3.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" +[[package]] +name = "l2l-openapi" +version = "0.0.0" +source = "git+https://github.com/Ash-L2L/l2l-openapi?rev=6e440bb2715ec0d495050e9dbbda3f1590a07385#6e440bb2715ec0d495050e9dbbda3f1590a07385" +dependencies = [ + "jsonrpsee", + "l2l-openapi-macros", + "utoipa", +] + +[[package]] +name = "l2l-openapi-macros" +version = "0.0.0" +source = "git+https://github.com/Ash-L2L/l2l-openapi?rev=6e440bb2715ec0d495050e9dbbda3f1590a07385#6e440bb2715ec0d495050e9dbbda3f1590a07385" +dependencies = [ + "proc-macro2", + "proc_macro_roids", + "quote", + "syn 2.0.66", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -2990,7 +3012,7 @@ dependencies = [ "proc-macro-crate 3.1.0", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -3276,7 +3298,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -3305,7 +3327,7 @@ checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -3454,6 +3476,7 @@ dependencies = [ "proc-macro-error-attr", "proc-macro2", "quote", + "syn 1.0.109", "version_check", ] @@ -3470,13 +3493,24 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.82" +version = "1.0.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ad3d49ab951a01fbaafe34f2ec74122942fe18a3f9814c3268f1bb72042131b" +checksum = "22244ce15aa966053a896d1accb3a6e68469b97c7f33f284b99f0d576879fc23" dependencies = [ "unicode-ident", ] +[[package]] +name = "proc_macro_roids" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0c2a098cd8aaa29f66da27a684ad19f4b7bc886f576abf12f7df4a7391071c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "profiling" version = "1.0.15" @@ -3984,7 +4018,7 @@ checksum = "c5e405930b9796f1c00bee880d03fc7e0bb4b9a11afc776885ffe84320da2865" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -4006,7 +4040,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -4029,7 +4063,7 @@ dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -4304,7 +4338,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -4326,9 +4360,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.61" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c993ed8ccba56ae856363b1845da7266a7cb78e1d146c8a32d54b45a8b831fc9" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -4344,7 +4378,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -4396,27 +4430,27 @@ checksum = "5999e24eaa32083191ba4e425deb75cdf25efefabe5aaccb7446dd0d4122a3f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] name = "thiserror" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "579e9083ca58dd9dcf91a9923bb9054071b9ebbd800b342194c9feb0ee89fc18" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.60" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2470041c06ec3ac1ab38d0356a6119054dedaea53e12fbefc0de730a1c08524" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -4431,7 +4465,7 @@ dependencies = [ [[package]] name = "thunder" -version = "0.8.12" +version = "0.8.13" dependencies = [ "anyhow", "bincode", @@ -4464,11 +4498,12 @@ dependencies = [ "tokio-stream", "tokio-util", "tracing", + "utoipa", ] [[package]] name = "thunder_app" -version = "0.8.12" +version = "0.8.13" dependencies = [ "anyhow", "base64 0.21.7", @@ -4500,11 +4535,12 @@ dependencies = [ "tracing", "tracing-appender", "tracing-subscriber", + "utoipa", ] [[package]] name = "thunder_app_cli" -version = "0.8.12" +version = "0.8.13" dependencies = [ "anyhow", "bip300301", @@ -4514,15 +4550,19 @@ dependencies = [ "thunder", "thunder_app_rpc_api", "tokio", + "utoipa", ] [[package]] name = "thunder_app_rpc_api" -version = "0.8.12" +version = "0.8.13" dependencies = [ "bip300301", "jsonrpsee", + "l2l-openapi", + "serde_json", "thunder", + "utoipa", ] [[package]] @@ -4641,7 +4681,7 @@ checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -4769,7 +4809,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -4917,6 +4957,30 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +[[package]] +name = "utoipa" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23" +dependencies = [ + "indexmap", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "4.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bf0e16c02bc4bf5322ab65f10ab1149bdbcaa782cba66dc7057370a3f8190be" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.66", +] + [[package]] name = "valuable" version = "0.1.0" @@ -4981,7 +5045,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", "wasm-bindgen-shared", ] @@ -5015,7 +5079,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -5797,7 +5861,7 @@ checksum = "15e934569e47891f7d9411f1a451d947a60e000ab3bd24fbb970f000387d1b3b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] @@ -5817,7 +5881,7 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.61", + "syn 2.0.66", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index eb690e1..8c01c35 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -13,7 +13,7 @@ authors = [ "Nikita Chashchinskii " ] edition = "2021" -version = "0.8.12" +version = "0.8.13" [workspace.dependencies.bip300301] git = "https://github.com/Ash-L2L/bip300301.git" diff --git a/app/Cargo.toml b/app/Cargo.toml index 9182022..7b92437 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -40,6 +40,7 @@ tokio-util = { version = "0.7.10", features = ["rt"] } tracing = "0.1.40" tracing-appender = "0.2.3" tracing-subscriber = "0.3.18" +utoipa = "4.2.3" [[bin]] name = "thunder_app" diff --git a/app/rpc_server.rs b/app/rpc_server.rs index dbc862d..c7a40b0 100644 --- a/app/rpc_server.rs +++ b/app/rpc_server.rs @@ -122,6 +122,11 @@ impl RpcServer for RpcServerImpl { }).await.unwrap() } + async fn openapi_schema(&self) -> RpcResult { + let res = ::openapi(); + Ok(res) + } + async fn remove_from_mempool(&self, txid: Txid) -> RpcResult<()> { self.app .node diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 5c49dbc..2949842 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -13,6 +13,7 @@ serde_json = "1.0.113" thunder = { path = "../lib" } thunder_app_rpc_api = { path = "../rpc-api" } tokio = "1.29.1" +utoipa = "4.2.3" [lib] name = "thunder_app_cli_lib" diff --git a/cli/lib.rs b/cli/lib.rs index ba951fd..54e5fdc 100644 --- a/cli/lib.rs +++ b/cli/lib.rs @@ -35,6 +35,9 @@ pub enum Command { #[arg(long)] fee_sats: Option, }, + /// Show OpenAPI schema + #[command(name = "openapi-schema")] + OpenApiSchema, /// Remove a tx from the mempool RemoveFromMempool { txid: Txid }, /// Set the wallet seed from a mnemonic seed phrase @@ -120,6 +123,11 @@ impl Cli { let () = rpc_client.mine(fee_sats).await?; String::default() } + Command::OpenApiSchema => { + let openapi = + ::openapi(); + openapi.to_pretty_json()? + } Command::RemoveFromMempool { txid } => { let () = rpc_client.remove_from_mempool(txid).await?; String::default() diff --git a/lib/Cargo.toml b/lib/Cargo.toml index 63297db..a1de7a7 100644 --- a/lib/Cargo.toml +++ b/lib/Cargo.toml @@ -36,6 +36,7 @@ tokio = { version = "1.29.1", features = ["sync"] } tokio-stream = { version = "0.1.15", features = ["sync"] } tokio-util = { version = "0.7.10", features = ["rt"] } tracing = "0.1.40" +utoipa = "4.2.3" [lib] name = "thunder" diff --git a/lib/types/address.rs b/lib/types/address.rs index 3049e25..5a8cd3c 100644 --- a/lib/types/address.rs +++ b/lib/types/address.rs @@ -78,3 +78,21 @@ impl Serialize for Address { } } } + +impl utoipa::PartialSchema for Address { + fn schema() -> utoipa::openapi::RefOr { + let obj = utoipa::openapi::Object::with_type( + utoipa::openapi::SchemaType::String, + ); + utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(obj)) + } +} + +impl utoipa::ToSchema<'static> for Address { + fn schema() -> ( + &'static str, + utoipa::openapi::RefOr, + ) { + ("Address",
::schema()) + } +} diff --git a/lib/types/hashes.rs b/lib/types/hashes.rs index f86e936..cb39d3e 100644 --- a/lib/types/hashes.rs +++ b/lib/types/hashes.rs @@ -103,6 +103,24 @@ impl std::fmt::Debug for MerkleRoot { } } +impl utoipa::PartialSchema for MerkleRoot { + fn schema() -> utoipa::openapi::RefOr { + let obj = utoipa::openapi::Object::with_type( + utoipa::openapi::SchemaType::String, + ); + utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(obj)) + } +} + +impl utoipa::ToSchema<'static> for MerkleRoot { + fn schema() -> ( + &'static str, + utoipa::openapi::RefOr, + ) { + ("MerkleRoot", ::schema()) + } +} + #[derive( BorshSerialize, Clone, @@ -163,6 +181,24 @@ impl FromStr for Txid { } } +impl utoipa::PartialSchema for Txid { + fn schema() -> utoipa::openapi::RefOr { + let obj = utoipa::openapi::Object::with_type( + utoipa::openapi::SchemaType::String, + ); + utoipa::openapi::RefOr::T(utoipa::openapi::Schema::Object(obj)) + } +} + +impl utoipa::ToSchema<'static> for Txid { + fn schema() -> ( + &'static str, + utoipa::openapi::RefOr, + ) { + ("Txid", ::schema()) + } +} + pub fn hash(data: &T) -> Hash where T: BorshSerialize, diff --git a/lib/types/transaction.rs b/lib/types/transaction.rs index 910347a..cee5b0a 100644 --- a/lib/types/transaction.rs +++ b/lib/types/transaction.rs @@ -6,6 +6,7 @@ use rustreexo::accumulator::{ node_hash::NodeHash, pollard::Pollard, proof::Proof, }; use serde::{Deserialize, Serialize}; +use utoipa::ToSchema; use super::{hash, Address, Hash, MerkleRoot, Txid}; use crate::authorization::Authorization; @@ -34,6 +35,7 @@ where PartialEq, PartialOrd, Serialize, + ToSchema, )] pub enum OutPoint { // Created by transactions. @@ -98,8 +100,16 @@ where } #[derive( - BorshSerialize, Clone, Debug, Deserialize, Eq, PartialEq, Serialize, + BorshSerialize, + Clone, + Debug, + Deserialize, + Eq, + PartialEq, + Serialize, + ToSchema, )] +#[schema(as = OutputContent)] pub enum Content { Value(u64), Withdrawal { @@ -129,11 +139,23 @@ impl GetValue for Content { } } +fn output_content_schema_ref() -> utoipa::openapi::Ref { + utoipa::openapi::Ref::new("OutputContent") +} + #[derive( - BorshSerialize, Clone, Debug, Deserialize, Eq, PartialEq, Serialize, + BorshSerialize, + Clone, + Debug, + Deserialize, + Eq, + PartialEq, + Serialize, + ToSchema, )] pub struct Output { pub address: Address, + #[schema(schema_with = output_content_schema_ref)] pub content: Content, } @@ -145,7 +167,14 @@ impl GetValue for Output { } #[derive( - BorshSerialize, Clone, Debug, Deserialize, Eq, PartialEq, Serialize, + BorshSerialize, + Clone, + Debug, + Deserialize, + Eq, + PartialEq, + Serialize, + ToSchema, )] pub struct PointedOutput { pub outpoint: OutPoint, diff --git a/rpc-api/Cargo.toml b/rpc-api/Cargo.toml index 2d43d95..42b2ac5 100644 --- a/rpc-api/Cargo.toml +++ b/rpc-api/Cargo.toml @@ -8,6 +8,12 @@ version.workspace = true bip300301.workspace = true jsonrpsee = { version = "0.20.0", features = ["macros"] } thunder = { path = "../lib" } +serde_json = "1.0.113" +utoipa = "4.2.3" + +[dependencies.l2l-openapi] +git = "https://github.com/Ash-L2L/l2l-openapi" +rev = "6e440bb2715ec0d495050e9dbbda3f1590a07385" [lib] name = "thunder_app_rpc_api" diff --git a/rpc-api/lib.rs b/rpc-api/lib.rs index fc064a6..72e71e8 100644 --- a/rpc-api/lib.rs +++ b/rpc-api/lib.rs @@ -4,8 +4,76 @@ use std::net::SocketAddr; use bip300301::bitcoin; use jsonrpsee::{core::RpcResult, proc_macros::rpc}; -use thunder::types::{Address, PointedOutput, Txid}; +use l2l_openapi::open_api; +use thunder::types::{ + Address, MerkleRoot, OutPoint, Output, OutputContent, PointedOutput, Txid, +}; +use utoipa::{ + openapi::{RefOr, Schema, SchemaType}, + PartialSchema, ToSchema, +}; + +struct BitcoinAddrSchema; + +impl PartialSchema for BitcoinAddrSchema { + fn schema() -> RefOr { + let obj = utoipa::openapi::Object::with_type(SchemaType::String); + RefOr::T(Schema::Object(obj)) + } +} + +impl ToSchema<'static> for BitcoinAddrSchema { + fn schema() -> (&'static str, RefOr) { + ("bitcoin.Address", ::schema()) + } +} + +struct BitcoinAmountSchema; + +impl PartialSchema for BitcoinAmountSchema { + fn schema() -> RefOr { + let obj = utoipa::openapi::Object::with_type(SchemaType::String); + RefOr::T(Schema::Object(obj)) + } +} + +struct BitcoinOutPointSchema; +impl PartialSchema for BitcoinOutPointSchema { + fn schema() -> RefOr { + let obj = utoipa::openapi::Object::new(); + RefOr::T(Schema::Object(obj)) + } +} + +impl ToSchema<'static> for BitcoinOutPointSchema { + fn schema() -> (&'static str, RefOr) { + ("bitcoin.OutPoint", ::schema()) + } +} + +struct OpenApiSchema; + +impl PartialSchema for OpenApiSchema { + fn schema() -> RefOr { + let obj = utoipa::openapi::Object::new(); + RefOr::T(Schema::Object(obj)) + } +} + +struct SocketAddrSchema; + +impl PartialSchema for SocketAddrSchema { + fn schema() -> RefOr { + let obj = utoipa::openapi::Object::with_type(SchemaType::String); + RefOr::T(Schema::Object(obj)) + } +} + +#[open_api(ref_schemas[ + Address, BitcoinAddrSchema, BitcoinOutPointSchema, MerkleRoot, OutPoint, Output, + OutputContent, Txid +])] #[rpc(client, server)] pub trait Rpc { /// Get balance in sats @@ -13,8 +81,13 @@ pub trait Rpc { async fn balance(&self) -> RpcResult; /// Connect to a peer + #[open_api_method(output_schema(ToSchema))] #[method(name = "connect_peer")] - async fn connect_peer(&self, addr: SocketAddr) -> RpcResult<()>; + async fn connect_peer( + &self, + #[open_api_method_arg(schema(PartialSchema = "SocketAddrSchema"))] + addr: SocketAddr, + ) -> RpcResult<()>; /// Format a deposit address #[method(name = "format_deposit_address")] @@ -48,18 +121,27 @@ pub trait Rpc { async fn list_utxos(&self) -> RpcResult>; /// Attempt to mine a sidechain block + #[open_api_method(output_schema(ToSchema))] #[method(name = "mine")] async fn mine(&self, fee: Option) -> RpcResult<()>; + /// Get OpenRPC schema + #[open_api_method(output_schema(PartialSchema = "OpenApiSchema"))] + #[method(name = "openapi_schema")] + async fn openapi_schema(&self) -> RpcResult; + /// Remove a tx from the mempool + #[open_api_method(output_schema(ToSchema))] #[method(name = "remove_from_mempool")] async fn remove_from_mempool(&self, txid: Txid) -> RpcResult<()>; /// Set the wallet seed from a mnemonic seed phrase + #[open_api_method(output_schema(ToSchema))] #[method(name = "set_seed_from_mnemonic")] async fn set_seed_from_mnemonic(&self, mnemonic: String) -> RpcResult<()>; /// Get total sidechain wealth + #[open_api_method(output_schema(PartialSchema = "BitcoinAmountSchema"))] #[method(name = "sidechain_wealth")] async fn sidechain_wealth(&self) -> RpcResult; @@ -80,7 +162,10 @@ pub trait Rpc { #[method(name = "withdraw")] async fn withdraw( &self, - mainchain_address: bitcoin::Address, + #[open_api_method_arg(schema(PartialSchema = "BitcoinAddrSchema"))] + mainchain_address: bitcoin::Address< + bitcoin::address::NetworkUnchecked, + >, amount_sats: u64, fee_sats: u64, mainchain_fee_sats: u64,