Skip to content

Commit

Permalink
[RPC API] - split api into GatewayAPI, ReadApi, FullNodeApi, and Tran…
Browse files Browse the repository at this point in the history
…sactionBuilder (MystenLabs#2093)

* split api into GatewayAPI, ReadApi, FullNodeApi, and TransactionBuilder

* add module support to json rpc doc generation

* revert rpc response sample changes

* remove unnecessary dep and commented out code
  • Loading branch information
patrickkuo authored May 25, 2022
1 parent cf86d29 commit dd08d69
Show file tree
Hide file tree
Showing 14 changed files with 1,056 additions and 732 deletions.
15 changes: 13 additions & 2 deletions crates/generate-json-rpc-spec/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,17 @@ use serde::Serialize;
use serde_json::json;
use std::fs::File;
use std::io::Write;

use sui::config::SUI_WALLET_CONFIG;
use sui::wallet_commands::{WalletCommandResult, WalletCommands, WalletContext};
use sui::wallet_commands::{EXAMPLE_NFT_DESCRIPTION, EXAMPLE_NFT_NAME, EXAMPLE_NFT_URL};
use sui_core::gateway_types::{
GetObjectInfoResponse, SuiObjectRef, TransactionEffectsResponse, TransactionResponse,
};
use sui_gateway::api::RpcGatewayOpenRpc;
use sui_gateway::api::SuiRpcModule;
use sui_gateway::json_rpc::sui_rpc_doc;
use sui_gateway::read_api::{FullNodeApi, ReadApi};
use sui_gateway::rpc_gateway::{GatewayReadApiImpl, RpcGatewayImpl, TransactionBuilderImpl};
use sui_json::SuiJsonValue;
use sui_types::base_types::{ObjectID, SuiAddress};
use sui_types::SUI_FRAMEWORK_ADDRESS;
Expand Down Expand Up @@ -55,7 +59,14 @@ const TRANSACTION_SAMPLE_FILE_PATH: &str = concat!(
#[tokio::main]
async fn main() {
let options = Options::parse();
let open_rpc = RpcGatewayOpenRpc::open_rpc();

let mut open_rpc = sui_rpc_doc();
open_rpc.add_module(TransactionBuilderImpl::rpc_doc_module());
open_rpc.add_module(RpcGatewayImpl::rpc_doc_module());
open_rpc.add_module(GatewayReadApiImpl::rpc_doc_module());
open_rpc.add_module(ReadApi::rpc_doc_module());
open_rpc.add_module(FullNodeApi::rpc_doc_module());

match options.action {
Action::Print => {
let content = serde_json::to_string_pretty(&open_rpc).unwrap();
Expand Down
2 changes: 1 addition & 1 deletion crates/sui-core/src/gateway_state.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ mod gateway_state_tests;

pub type AsyncResult<'a, T, E> = future::BoxFuture<'a, Result<T, E>>;

pub type GatewayClient = Box<dyn GatewayAPI + Sync + Send>;
pub type GatewayClient = Arc<dyn GatewayAPI + Sync + Send>;

pub type GatewayTxSeqNumber = u64;

Expand Down
151 changes: 82 additions & 69 deletions crates/sui-gateway/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

use jsonrpsee::core::RpcResult;
use jsonrpsee_core::server::rpc_module::RpcModule;
use jsonrpsee_proc_macros::rpc;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
Expand All @@ -11,6 +12,7 @@ use sui_core::gateway_state::GatewayTxSeqNumber;
use sui_core::gateway_types::{GetObjectInfoResponse, SuiInputObjectKind, SuiObjectRef};
use sui_core::gateway_types::{TransactionEffectsResponse, TransactionResponse};
use sui_json::SuiJsonValue;
use sui_open_rpc::Module;
use sui_open_rpc_macros::open_rpc;
use sui_types::sui_serde::Base64;
use sui_types::{
Expand All @@ -22,73 +24,9 @@ use sui_types::{
use crate::rpc_gateway::responses::ObjectResponse;
use crate::rpc_gateway::responses::SuiTypeTag;

#[open_rpc(
name = "Sui JSON-RPC",
namespace = "sui",
contact_name = "Mysten Labs",
contact_url = "https://mystenlabs.com",
contact_email = "[email protected]",
license = "Apache-2.0",
license_url = "https://raw.githubusercontent.com/MystenLabs/sui/main/LICENSE",
description = "Sui JSON-RPC API for interaction with the Sui network gateway."
)]
#[open_rpc(namespace = "sui", tag = "Gateway API")]
#[rpc(server, client, namespace = "sui")]
pub trait RpcGateway {
/// Create a transaction to transfer a Sui coin from one address to another.
#[method(name = "transferCoin")]
async fn transfer_coin(
&self,
signer: SuiAddress,
object_id: ObjectID,
gas: Option<ObjectID>,
gas_budget: u64,
recipient: SuiAddress,
) -> RpcResult<TransactionBytes>;

/// Execute a Move call transaction by calling the specified function in the module of a given package.
#[method(name = "moveCall")]
async fn move_call(
&self,
signer: SuiAddress,
package_object_id: ObjectID,
module: String,
function: String,
type_arguments: Vec<SuiTypeTag>,
arguments: Vec<SuiJsonValue>,
gas: Option<ObjectID>,
gas_budget: u64,
) -> RpcResult<TransactionBytes>;

/// Publish Move module.
#[method(name = "publish")]
async fn publish(
&self,
sender: SuiAddress,
compiled_modules: Vec<Base64>,
gas: Option<ObjectID>,
gas_budget: u64,
) -> RpcResult<TransactionBytes>;

#[method(name = "splitCoin")]
async fn split_coin(
&self,
signer: SuiAddress,
coin_object_id: ObjectID,
split_amounts: Vec<u64>,
gas: Option<ObjectID>,
gas_budget: u64,
) -> RpcResult<TransactionBytes>;

#[method(name = "mergeCoins")]
async fn merge_coin(
&self,
signer: SuiAddress,
primary_coin: ObjectID,
coin_to_merge: ObjectID,
gas: Option<ObjectID>,
gas_budget: u64,
) -> RpcResult<TransactionBytes>;

pub trait RpcGatewayApi {
/// Execute the transaction using the transaction data, signature and public key.
#[method(name = "executeTransaction")]
async fn execute_transaction(
Expand All @@ -101,7 +39,11 @@ pub trait RpcGateway {
/// Synchronize client state with validators.
#[method(name = "syncAccountState")]
async fn sync_account_state(&self, address: SuiAddress) -> RpcResult<()>;
}

#[open_rpc(namespace = "sui", tag = "Read API")]
#[rpc(server, client, namespace = "sui")]
pub trait RpcReadApi {
/// Return the list of objects owned by an address.
#[method(name = "getOwnedObjects")]
async fn get_owned_objects(&self, owner: SuiAddress) -> RpcResult<ObjectResponse>;
Expand All @@ -128,6 +70,14 @@ pub trait RpcGateway {
digest: TransactionDigest,
) -> RpcResult<TransactionEffectsResponse>;

/// Return the object information for a specified object
#[method(name = "getObjectInfo")]
async fn get_object_info(&self, object_id: ObjectID) -> RpcResult<GetObjectInfoResponse>;
}

#[open_rpc(namespace = "sui", tag = "Full Node API")]
#[rpc(server, client, namespace = "sui")]
pub trait RpcFullNodeReadApi {
#[method(name = "getTransactionsByInputObject")]
async fn get_transactions_by_input_object(
&self,
Expand All @@ -151,10 +101,65 @@ pub trait RpcGateway {
&self,
addr: SuiAddress,
) -> RpcResult<Vec<(GatewayTxSeqNumber, TransactionDigest)>>;
}

/// Return the object information for a specified object
#[method(name = "getObjectInfo")]
async fn get_object_info(&self, object_id: ObjectID) -> RpcResult<GetObjectInfoResponse>;
#[open_rpc(namespace = "sui", tag = "Transaction Builder API")]
#[rpc(server, client, namespace = "sui")]
pub trait RpcTransactionBuilder {
/// Create a transaction to transfer a Sui coin from one address to another.
#[method(name = "transferCoin")]
async fn transfer_coin(
&self,
signer: SuiAddress,
object_id: ObjectID,
gas: Option<ObjectID>,
gas_budget: u64,
recipient: SuiAddress,
) -> RpcResult<TransactionBytes>;

/// Execute a Move call transaction by calling the specified function in the module of a given package.
#[method(name = "moveCall")]
async fn move_call(
&self,
signer: SuiAddress,
package_object_id: ObjectID,
module: String,
function: String,
type_arguments: Vec<SuiTypeTag>,
arguments: Vec<SuiJsonValue>,
gas: Option<ObjectID>,
gas_budget: u64,
) -> RpcResult<TransactionBytes>;

/// Publish Move module.
#[method(name = "publish")]
async fn publish(
&self,
sender: SuiAddress,
compiled_modules: Vec<Base64>,
gas: Option<ObjectID>,
gas_budget: u64,
) -> RpcResult<TransactionBytes>;

#[method(name = "splitCoin")]
async fn split_coin(
&self,
signer: SuiAddress,
coin_object_id: ObjectID,
split_amounts: Vec<u64>,
gas: Option<ObjectID>,
gas_budget: u64,
) -> RpcResult<TransactionBytes>;

#[method(name = "mergeCoins")]
async fn merge_coin(
&self,
signer: SuiAddress,
primary_coin: ObjectID,
coin_to_merge: ObjectID,
gas: Option<ObjectID>,
gas_budget: u64,
) -> RpcResult<TransactionBytes>;
}

#[serde_as]
Expand Down Expand Up @@ -183,3 +188,11 @@ impl TransactionBytes {
TransactionData::from_signable_bytes(&self.tx_bytes.to_vec()?)
}
}

pub trait SuiRpcModule
where
Self: Sized,
{
fn rpc(self) -> RpcModule<Self>;
fn rpc_doc_module() -> Module;
}
5 changes: 3 additions & 2 deletions crates/sui-gateway/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

use crate::rpc_gateway_client::RpcGatewayClient;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use std::{
collections::BTreeMap,
fmt::{Display, Formatter, Write},
Expand Down Expand Up @@ -65,9 +66,9 @@ impl GatewayType {
let path = config.db_folder_path.clone();
let committee = config.make_committee();
let authority_clients = config.make_authority_clients();
Box::new(GatewayState::new(path, committee, authority_clients)?)
Arc::new(GatewayState::new(path, committee, authority_clients)?)
}
GatewayType::RPC(url) => Box::new(RpcGatewayClient::new(url.clone())?),
GatewayType::RPC(url) => Arc::new(RpcGatewayClient::new(url.clone())?),
})
}
}
Expand Down
39 changes: 26 additions & 13 deletions crates/sui-gateway/src/json_rpc.rs
Original file line number Diff line number Diff line change
@@ -1,22 +1,39 @@
// Copyright (c) 2022, Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

use std::{env, net::SocketAddr, time::Instant};

use anyhow::Result;
use jsonrpsee::{
http_server::{AccessControlBuilder, HttpServerBuilder, HttpServerHandle},
RpcModule,
};
use jsonrpsee_core::{middleware::Middleware, server::rpc_module::Methods};
use jsonrpsee_core::middleware::Middleware;
use prometheus_exporter::prometheus::{
register_histogram_vec, register_int_counter_vec, HistogramVec, IntCounterVec,
};
use serde::Serialize;
use std::{env, net::SocketAddr, time::Instant};
use tracing::info;

use sui_open_rpc::Project;

use crate::api::SuiRpcModule;

pub struct JsonRpcServerBuilder {
module: RpcModule<()>,
server_builder: HttpServerBuilder<JsonRpcMetrics>,
rpc_doc: Project,
}

pub fn sui_rpc_doc() -> Project {
Project::new(
"Sui JSON-RPC",
"Sui JSON-RPC API for interaction with the Sui network gateway.",
"Mysten Labs",
"https://mystenlabs.com",
"[email protected]",
"Apache-2.0",
"https://raw.githubusercontent.com/MystenLabs/sui/main/LICENSE",
)
}

impl JsonRpcServerBuilder {
Expand All @@ -41,23 +58,19 @@ impl JsonRpcServerBuilder {
Ok(Self {
module,
server_builder,
rpc_doc: sui_rpc_doc(),
})
}

pub fn register_methods(&mut self, methods: impl Into<Methods>) -> Result<()> {
self.module.merge(methods).map_err(Into::into)
pub fn register_module<T: SuiRpcModule>(&mut self, module: T) -> Result<()> {
self.rpc_doc.add_module(T::rpc_doc_module());
self.module.merge(module.rpc()).map_err(Into::into)
}

pub fn register_open_rpc<T>(&mut self, spec: T) -> Result<()>
where
T: Clone + Serialize + Send + Sync + 'static,
{
pub async fn start(mut self, listen_address: SocketAddr) -> Result<HttpServerHandle> {
self.module
.register_method("rpc.discover", move |_, _| Ok(spec.clone()))?;
Ok(())
}
.register_method("rpc.discover", move |_, _| Ok(self.rpc_doc.clone()))?;

pub async fn start(self, listen_address: SocketAddr) -> Result<HttpServerHandle> {
let server = self.server_builder.build(listen_address).await?;

let addr = server.local_addr()?;
Expand Down
Loading

0 comments on commit dd08d69

Please sign in to comment.