Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.0.12: add support for generic handlers #121

Merged
merged 3 commits into from
Mar 25, 2023
Merged
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
4 changes: 2 additions & 2 deletions tests/e2e/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,9 @@ homepage = "https://avax.network"
[dependencies]

[dev-dependencies]
avalanche-installer = "0.0.50"
avalanche-installer = "0.0.55"
avalanche-network-runner-sdk = "0.3.0" # https://crates.io/crates/avalanche-network-runner-sdk
avalanche-types = { version = "0.0.319", features = ["jsonrpc_client", "subnet"] } # https://crates.io/crates/avalanche-types
avalanche-types = { version = "0.0.323", features = ["jsonrpc_client", "subnet"] } # https://crates.io/crates/avalanche-types
env_logger = "0.10.0"
log = "0.4.17"
random-manager = "0.0.5"
Expand Down
2 changes: 1 addition & 1 deletion tests/e2e/src/tests/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use std::{
use avalanche_network_runner_sdk::{BlockchainSpec, Client, GlobalConfig, StartRequest};
use avalanche_types::{ids, jsonrpc::client::info as avalanche_sdk_info, subnet};

const AVALANCHEGO_VERSION: &str = "v1.9.11";
const AVALANCHEGO_VERSION: &str = "v1.9.15";

#[tokio::test]
async fn e2e() {
Expand Down
3 changes: 2 additions & 1 deletion timestampvm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ repository = "https://github.com/ava-labs/timestampvm-rs"
readme = "../README.md"

[dependencies]
avalanche-types = { version = "0.0.319", features = ["subnet", "codec_base64"] } # https://crates.io/crates/avalanche-types
avalanche-types = { version = "0.0.323", features = ["subnet", "codec_base64"] } # https://crates.io/crates/avalanche-types
base64 = { version = "0.21.0" }
bytes = "1.4.0"
chrono = "0.4.23"
clap = { version = "4.1.8", features = ["cargo", "derive"] } # https://github.com/clap-rs/clap/releases
derivative = "2.2.0"
Expand Down
53 changes: 47 additions & 6 deletions timestampvm/src/api/chain_handlers.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
//! Implements chain/VM specific handlers.
//! To be served via `[HOST]/ext/bc/[CHAIN ID]/rpc`.

use std::str::FromStr;
use std::{io, marker::PhantomData, str::FromStr};

use crate::{block::Block, vm::Vm};
use avalanche_types::ids;
use jsonrpc_core::{BoxFuture, Error, ErrorCode, Result};
use avalanche_types::{ids, proto::http::Element, subnet::rpc::http::handle::Handle};
use bytes::Bytes;
use jsonrpc_core::{BoxFuture, Error, ErrorCode, IoHandler, Result};
use jsonrpc_derive::rpc;
use serde::{Deserialize, Serialize};

use super::de_request;

/// Defines RPCs specific to the chain.
#[rpc]
pub trait Rpc {
Expand Down Expand Up @@ -60,17 +63,18 @@ pub struct GetBlockResponse {
}

/// Implements API services for the chain-specific handlers.
pub struct Service<A> {
#[derive(Clone)]
pub struct ChainService<A> {
pub vm: Vm<A>,
}

impl<A> Service<A> {
impl<A> ChainService<A> {
pub fn new(vm: Vm<A>) -> Self {
Self { vm }
}
}

impl<A> Rpc for Service<A>
impl<A> Rpc for ChainService<A>
where
A: Send + Sync + Clone + 'static,
{
Expand Down Expand Up @@ -140,6 +144,43 @@ where
}
}

#[derive(Clone, Debug)]
pub struct ChainHandler<T> {
pub handler: IoHandler,
_marker: PhantomData<T>,
}

impl<T: Rpc> ChainHandler<T> {
pub fn new(service: T) -> Self {
let mut handler = jsonrpc_core::IoHandler::new();
handler.extend_with(Rpc::to_delegate(service));
Self {
handler,
_marker: PhantomData,
}
}
}

#[tonic::async_trait]
impl<T> Handle for ChainHandler<T>
where
T: Rpc + Send + Sync + Clone + 'static,
{
async fn request(
&self,
req: &Bytes,
_headers: &[Element],
) -> std::io::Result<(Bytes, Vec<Element>)> {
match self.handler.handle_request(&de_request(req)?).await {
Some(resp) => Ok((Bytes::from(resp), Vec::new())),
None => Err(io::Error::new(
io::ErrorKind::Other,
"failed to handle request",
)),
}
}
}

fn create_jsonrpc_error(e: std::io::Error) -> Error {
let mut error = Error::new(ErrorCode::InternalError);
error.message = format!("{}", e);
Expand Down
20 changes: 20 additions & 0 deletions timestampvm/src/api/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,29 @@
pub mod chain_handlers;
pub mod static_handlers;

use std::io;

use bytes::Bytes;
use jsonrpc_core::MethodCall;
use serde::{Deserialize, Serialize};

#[derive(Deserialize, Serialize, Debug, Clone)]
pub struct PingResponse {
pub success: bool,
}

/// Deserializes JSON-RPC method call.
pub fn de_request(req: &Bytes) -> io::Result<String> {
let method_call: MethodCall = serde_json::from_slice(req).map_err(|e| {
io::Error::new(
io::ErrorKind::Other,
format!("failed to deserialize request: {e}"),
)
})?;
serde_json::to_string(&method_call).map_err(|e| {
io::Error::new(
io::ErrorKind::Other,
format!("failed to serialize request: {e}"),
)
})
}
54 changes: 42 additions & 12 deletions timestampvm/src/api/static_handlers.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
//! Implements static handlers specific to this VM.
//! To be served via `[HOST]/ext/vm/[VM ID]/static`.

use crate::vm::Vm;
use jsonrpc_core::{BoxFuture, Result};
use std::io;

use avalanche_types::{proto::http::Element, subnet::rpc::http::handle::Handle};
use bytes::Bytes;
use jsonrpc_core::{BoxFuture, IoHandler, Result};
use jsonrpc_derive::rpc;

use super::de_request;

/// Defines static handler RPCs for this VM.
#[rpc]
pub trait Rpc {
Expand All @@ -13,22 +18,47 @@ pub trait Rpc {
}

/// Implements API services for the static handlers.
pub struct Service<A> {
pub vm: Vm<A>,
}
#[derive(Default)]
pub struct StaticService {}

impl<A> Service<A> {
pub fn new(vm: Vm<A>) -> Self {
Self { vm }
impl StaticService {
pub fn new() -> Self {
Self {}
}
}

impl<A> Rpc for Service<A>
where
A: Send + Sync + Clone + 'static,
{
impl Rpc for StaticService {
fn ping(&self) -> BoxFuture<Result<crate::api::PingResponse>> {
log::debug!("ping called");
Box::pin(async move { Ok(crate::api::PingResponse { success: true }) })
}
}
#[derive(Clone)]
pub struct StaticHandler {
pub handler: IoHandler,
}

impl StaticHandler {
pub fn new(service: StaticService) -> Self {
let mut handler = jsonrpc_core::IoHandler::new();
handler.extend_with(Rpc::to_delegate(service));
Self { handler }
}
}

#[tonic::async_trait]
impl Handle for StaticHandler {
async fn request(
&self,
req: &Bytes,
_headers: &[Element],
) -> std::io::Result<(Bytes, Vec<Element>)> {
match self.handler.handle_request(&de_request(req)?).await {
Some(resp) => Ok((Bytes::from(resp), Vec::new())),
None => Err(io::Error::new(
io::ErrorKind::Other,
"failed to handle request",
)),
}
}
}
53 changes: 34 additions & 19 deletions timestampvm/src/vm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ use std::{
sync::Arc,
};

use crate::{api, block::Block, genesis::Genesis, state};
use crate::{
api::{
chain_handlers::{ChainHandler, ChainService},
static_handlers::{StaticHandler, StaticService},
},
block::Block,
genesis::Genesis,
state,
};
use avalanche_types::{
choices, ids,
subnet::{
Expand All @@ -19,6 +27,7 @@ use avalanche_types::{
engine::common::{
appsender::AppSender,
engine::{AppHandler, CrossChainAppHandler, NetworkAppHandler},
http_handler::{HttpHandler, LockOptions},
vm::{CommonVm, Connector},
},
},
Expand Down Expand Up @@ -205,6 +214,8 @@ where
{
type DatabaseManager = DatabaseManager;
type AppSender = A;
type ChainHandler = ChainHandler<ChainService<A>>;
type StaticHandler = StaticHandler;

async fn initialize(
&mut self,
Expand Down Expand Up @@ -285,32 +296,36 @@ where
/// Creates static handlers.
async fn create_static_handlers(
&mut self,
) -> io::Result<HashMap<String, snow::engine::common::http_handler::HttpHandler>> {
let svc = api::static_handlers::Service::new(self.clone());
let mut handler = jsonrpc_core::IoHandler::new();
handler.extend_with(api::static_handlers::Rpc::to_delegate(svc));

let http_handler = snow::engine::common::http_handler::HttpHandler::new_from_u8(0, handler)
.map_err(|_| Error::from(ErrorKind::InvalidData))?;

) -> io::Result<HashMap<String, HttpHandler<Self::StaticHandler>>> {
let handler = StaticHandler::new(StaticService::new());
let mut handlers = HashMap::new();
handlers.insert("/static".to_string(), http_handler);
handlers.insert(
"/static".to_string(),
HttpHandler {
lock_option: LockOptions::WriteLock,
handler,
server_addr: None,
},
);

Ok(handlers)
}

/// Creates VM-specific handlers.
async fn create_handlers(
&mut self,
) -> io::Result<HashMap<String, snow::engine::common::http_handler::HttpHandler>> {
let svc = api::chain_handlers::Service::new(self.clone());
let mut handler = jsonrpc_core::IoHandler::new();
handler.extend_with(api::chain_handlers::Rpc::to_delegate(svc));

let http_handler = snow::engine::common::http_handler::HttpHandler::new_from_u8(0, handler)
.map_err(|_| Error::from(ErrorKind::InvalidData))?;

) -> io::Result<HashMap<String, HttpHandler<Self::ChainHandler>>> {
let handler = ChainHandler::new(ChainService::new(self.clone()));
let mut handlers = HashMap::new();
handlers.insert("/rpc".to_string(), http_handler);
handlers.insert(
"/rpc".to_string(),
HttpHandler {
lock_option: LockOptions::WriteLock,
handler,
server_addr: None,
},
);

Ok(handlers)
}
}
Expand Down