From 08e0da7582b5129c974f0f71fcf5cf836fb326b3 Mon Sep 17 00:00:00 2001 From: Dmitryii Osipov Date: Tue, 5 Nov 2024 10:03:50 +0100 Subject: [PATCH] feat(ethexe/rpc): implement rpc methods (#4299) Co-authored-by: Dmitry Novikov --- Cargo.lock | 3 + core/Cargo.toml | 8 +- core/src/buffer.rs | 1 + core/src/message/common.rs | 3 + core/src/message/context.rs | 1 + core/src/message/mod.rs | 2 + core/src/pages.rs | 1 + core/src/program.rs | 1 + core/src/reservation.rs | 1 + ethexe/common/src/lib.rs | 3 +- ethexe/common/src/mirror.rs | 3 +- ethexe/common/src/router.rs | 3 +- ethexe/common/src/wvara.rs | 3 +- ethexe/rpc/Cargo.toml | 4 +- ethexe/rpc/src/apis/block.rs | 73 +++++++++++++ ethexe/rpc/src/apis/mod.rs | 23 ++++ ethexe/rpc/src/apis/program.rs | 163 +++++++++++++++++++++++++++++ ethexe/rpc/src/common.rs | 36 +++++++ ethexe/rpc/src/errors.rs | 31 ++++++ ethexe/rpc/src/lib.rs | 110 +++---------------- ethexe/runtime/common/Cargo.toml | 10 +- ethexe/runtime/common/src/state.rs | 6 ++ ethexe/signer/src/lib.rs | 5 + gprimitives/src/lib.rs | 14 +++ 24 files changed, 403 insertions(+), 105 deletions(-) create mode 100644 ethexe/rpc/src/apis/block.rs create mode 100644 ethexe/rpc/src/apis/mod.rs create mode 100644 ethexe/rpc/src/apis/program.rs create mode 100644 ethexe/rpc/src/common.rs create mode 100644 ethexe/rpc/src/errors.rs diff --git a/Cargo.lock b/Cargo.lock index 44f413a92f3..46f5ef8ff9e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4779,6 +4779,7 @@ dependencies = [ "ethexe-common", "ethexe-db", "ethexe-processor", + "ethexe-runtime-common", "futures", "gear-core", "gprimitives", @@ -4787,6 +4788,7 @@ dependencies = [ "jsonrpsee 0.24.0", "log", "parity-scale-codec", + "serde", "sp-core", "tokio", "tower 0.4.13", @@ -4821,6 +4823,7 @@ dependencies = [ "gsys", "log", "parity-scale-codec", + "serde", ] [[package]] diff --git a/core/Cargo.toml b/core/Cargo.toml index d38b4e2a262..40ab696369b 100644 --- a/core/Cargo.toml +++ b/core/Cargo.toml @@ -50,4 +50,10 @@ numerated = { workspace = true, features = ["mock"] } [features] default = [] strict = [] -std = ["serde/std", "dep:impl-serde", "wasmparser/std", "gear-core-errors/serde"] +std = [ + "serde/std", + "dep:impl-serde", + "wasmparser/std", + "gear-core-errors/serde", + "gprimitives/serde", +] diff --git a/core/src/buffer.rs b/core/src/buffer.rs index b4488470833..35f3aa013f8 100644 --- a/core/src/buffer.rs +++ b/core/src/buffer.rs @@ -35,6 +35,7 @@ use scale_info::{ /// `E` is overflow error type. /// `N` is max len which a vector can have. #[derive(Clone, Default, Eq, Hash, Ord, PartialEq, PartialOrd, Decode, Encode, TypeInfo)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct LimitedVec(Vec, PhantomData); /// Formatter for [`LimitedVec`] will print to precision of 8 by default, to print the whole data, use `{:+}`. diff --git a/core/src/message/common.rs b/core/src/message/common.rs index 38955c67a79..398b7bbfec7 100644 --- a/core/src/message/common.rs +++ b/core/src/message/common.rs @@ -179,6 +179,7 @@ impl Message { TypeInfo, derive_more::From, )] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub enum MessageDetails { /// Reply details. Reply(ReplyDetails), @@ -228,6 +229,7 @@ impl MessageDetails { #[derive( Clone, Copy, Default, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Decode, Encode, TypeInfo, )] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct ReplyDetails { /// Message id, this message replies on. to: MessageId, @@ -261,6 +263,7 @@ impl ReplyDetails { #[derive( Clone, Copy, Default, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Decode, Encode, TypeInfo, )] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct SignalDetails { /// Message id, which issues signal. to: MessageId, diff --git a/core/src/message/context.rs b/core/src/message/context.rs index 3734a26adc6..32929d7b75f 100644 --- a/core/src/message/context.rs +++ b/core/src/message/context.rs @@ -144,6 +144,7 @@ impl ContextOutcome { /// Store of previous message execution context. #[derive(Clone, Default, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Decode, Encode, TypeInfo)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct ContextStore { outgoing: BTreeMap>, reply: Option, diff --git a/core/src/message/mod.rs b/core/src/message/mod.rs index 52d40f28225..becf47f6a0f 100644 --- a/core/src/message/mod.rs +++ b/core/src/message/mod.rs @@ -60,6 +60,7 @@ const _: () = assert!(MAX_PAYLOAD_SIZE <= u32::MAX as usize); #[derive( Clone, Copy, Default, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Decode, Encode, TypeInfo, )] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct PayloadSizeError; impl From for &str { @@ -113,6 +114,7 @@ pub enum MessageWaitedType { #[derive( Copy, Clone, Default, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Decode, Encode, TypeInfo, )] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub enum DispatchKind { /// Initialization. Init, diff --git a/core/src/pages.rs b/core/src/pages.rs index b69fc58a56e..34d5b70e7bf 100644 --- a/core/src/pages.rs +++ b/core/src/pages.rs @@ -195,6 +195,7 @@ impl PartialOrd> for Page { Default, derive_more::Into, )] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct Page(u32); impl Page { diff --git a/core/src/program.rs b/core/src/program.rs index 4f714854ab2..4812cec8bb6 100644 --- a/core/src/program.rs +++ b/core/src/program.rs @@ -124,6 +124,7 @@ pub enum ProgramState { /// Struct defines infix of memory pages storage. #[derive(Clone, Copy, Debug, Default, Decode, Encode, PartialEq, Eq, TypeInfo)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct MemoryInfix(u32); impl MemoryInfix { diff --git a/core/src/reservation.rs b/core/src/reservation.rs index 9df0e70abe3..c8786cdff2a 100644 --- a/core/src/reservation.rs +++ b/core/src/reservation.rs @@ -40,6 +40,7 @@ use scale_info::{ #[derive( Clone, Copy, Default, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Decode, Encode, TypeInfo, )] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct ReservationNonce(u64); impl From<&InnerNonce> for ReservationNonce { diff --git a/ethexe/common/src/lib.rs b/ethexe/common/src/lib.rs index 94e187d023c..88a57ceaaf1 100644 --- a/ethexe/common/src/lib.rs +++ b/ethexe/common/src/lib.rs @@ -32,6 +32,7 @@ pub use gprimitives; use gprimitives::ActorId; use parity_scale_codec::{Decode, Encode}; +use serde::{Deserialize, Serialize}; #[derive(Clone, Debug, Encode, Decode)] pub enum BlockEvent { @@ -61,7 +62,7 @@ impl From for BlockEvent { } } -#[derive(Clone, Debug, Encode, Decode)] +#[derive(Clone, Debug, Encode, Decode, Serialize, Deserialize)] pub enum BlockRequestEvent { Router(router::RequestEvent), Mirror { diff --git a/ethexe/common/src/mirror.rs b/ethexe/common/src/mirror.rs index 83ab29e55c0..0b1f60f0b0b 100644 --- a/ethexe/common/src/mirror.rs +++ b/ethexe/common/src/mirror.rs @@ -20,6 +20,7 @@ use alloc::vec::Vec; use gear_core::message::ReplyCode; use gprimitives::{ActorId, MessageId, H256}; use parity_scale_codec::{Decode, Encode}; +use serde::{Deserialize, Serialize}; /* Events section */ @@ -104,7 +105,7 @@ impl Event { } } -#[derive(Clone, Debug, Encode, Decode)] +#[derive(Clone, Debug, Encode, Decode, Serialize, Deserialize)] pub enum RequestEvent { ExecutableBalanceTopUpRequested { value: u128, diff --git a/ethexe/common/src/router.rs b/ethexe/common/src/router.rs index 93d6de6c5c0..091036a708e 100644 --- a/ethexe/common/src/router.rs +++ b/ethexe/common/src/router.rs @@ -20,6 +20,7 @@ use alloc::vec::Vec; use gear_core::message::{Message, ReplyDetails}; use gprimitives::{ActorId, CodeId, MessageId, H256}; use parity_scale_codec::{Decode, Encode}; +use serde::{Deserialize, Serialize}; /* Storage related structures */ @@ -142,7 +143,7 @@ impl Event { } } -#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)] +#[derive(Clone, Debug, Encode, Decode, PartialEq, Eq, Serialize, Deserialize)] pub enum RequestEvent { BaseWeightChanged { base_weight: u64, diff --git a/ethexe/common/src/wvara.rs b/ethexe/common/src/wvara.rs index a529e86cb5a..2f6881e78fd 100644 --- a/ethexe/common/src/wvara.rs +++ b/ethexe/common/src/wvara.rs @@ -18,6 +18,7 @@ use gprimitives::{ActorId, U256}; use parity_scale_codec::{Decode, Encode}; +use serde::{Deserialize, Serialize}; /* Events section */ @@ -44,7 +45,7 @@ impl Event { } } -#[derive(Clone, Debug, Encode, Decode)] +#[derive(Clone, Debug, Encode, Decode, Serialize, Deserialize)] pub enum RequestEvent { Transfer { /// Never router, wvara or zero address. diff --git a/ethexe/rpc/Cargo.toml b/ethexe/rpc/Cargo.toml index 94a8625296c..8cf8f0bf0c2 100644 --- a/ethexe/rpc/Cargo.toml +++ b/ethexe/rpc/Cargo.toml @@ -13,7 +13,7 @@ repository.workspace = true tokio = { workspace = true } anyhow.workspace = true futures.workspace = true -gprimitives.workspace = true +gprimitives = { workspace = true, features = ["serde"] } ethexe-db.workspace = true ethexe-processor.workspace = true jsonrpsee = { version = "0.24", features = ["server", "macros"] } @@ -23,5 +23,7 @@ log.workspace = true parity-scale-codec.workspace = true hex.workspace = true ethexe-common.workspace = true +ethexe-runtime-common = { workspace = true, features = ["std"] } sp-core = { workspace = true, features = ["serde"] } gear-core = { workspace = true, features = ["std"] } +serde = { workspace = true, features = ["std"] } diff --git a/ethexe/rpc/src/apis/block.rs b/ethexe/rpc/src/apis/block.rs new file mode 100644 index 00000000000..06f5a6b0453 --- /dev/null +++ b/ethexe/rpc/src/apis/block.rs @@ -0,0 +1,73 @@ +// This file is part of Gear. +// +// Copyright (C) 2024 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::{common::block_header_at_or_latest, errors}; +use ethexe_common::BlockRequestEvent; +use ethexe_db::{BlockHeader, BlockMetaStorage, Database}; +use gprimitives::H256; +use jsonrpsee::{ + core::{async_trait, RpcResult}, + proc_macros::rpc, +}; +use std::collections::VecDeque; + +#[rpc(server)] +pub trait Block { + #[method(name = "block_header")] + async fn block_header(&self, hash: Option) -> RpcResult<(H256, BlockHeader)>; + + #[method(name = "block_commitmentQueue")] + async fn block_commitment_queue(&self, hash: Option) -> RpcResult>; + + #[method(name = "block_events")] + async fn block_events(&self, block_hash: Option) -> RpcResult>; +} + +#[derive(Clone)] +pub struct BlockApi { + db: Database, +} + +impl BlockApi { + pub fn new(db: Database) -> Self { + Self { db } + } +} + +#[async_trait] +impl BlockServer for BlockApi { + async fn block_header(&self, hash: Option) -> RpcResult<(H256, BlockHeader)> { + block_header_at_or_latest(&self.db, hash) + } + + async fn block_commitment_queue(&self, hash: Option) -> RpcResult> { + let block_hash = block_header_at_or_latest(&self.db, hash)?.0; + + self.db + .block_commitment_queue(block_hash) + .ok_or_else(|| errors::db("Block commitment queue wasn't found")) + } + + async fn block_events(&self, hash: Option) -> RpcResult> { + let block_hash = block_header_at_or_latest(&self.db, hash)?.0; + + self.db + .block_events(block_hash) + .ok_or_else(|| errors::db("Block events weren't found")) + } +} diff --git a/ethexe/rpc/src/apis/mod.rs b/ethexe/rpc/src/apis/mod.rs new file mode 100644 index 00000000000..d516efc0d5d --- /dev/null +++ b/ethexe/rpc/src/apis/mod.rs @@ -0,0 +1,23 @@ +// This file is part of Gear. +// +// Copyright (C) 2024 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +mod block; +mod program; + +pub use block::{BlockApi, BlockServer}; +pub use program::{ProgramApi, ProgramServer}; diff --git a/ethexe/rpc/src/apis/program.rs b/ethexe/rpc/src/apis/program.rs new file mode 100644 index 00000000000..19dc1154f49 --- /dev/null +++ b/ethexe/rpc/src/apis/program.rs @@ -0,0 +1,163 @@ +// This file is part of Gear. +// +// Copyright (C) 2024 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::{common::block_header_at_or_latest, errors}; +use ethexe_db::{CodesStorage, Database}; +use ethexe_processor::Processor; +use ethexe_runtime_common::state::{ + Mailbox, MemoryPages, MessageQueue, ProgramState, Storage, Waitlist, +}; +use gear_core::message::ReplyInfo; +use gprimitives::{H160, H256}; +use jsonrpsee::{ + core::{async_trait, RpcResult}, + proc_macros::rpc, +}; +use parity_scale_codec::Encode; +use sp_core::Bytes; + +#[rpc(server)] +pub trait Program { + #[method(name = "program_calculateReplyForHandle")] + async fn calculate_reply_for_handle( + &self, + at: Option, + source: H160, + program_id: H160, + payload: Bytes, + value: u128, + ) -> RpcResult; + + #[method(name = "program_ids")] + async fn ids(&self) -> RpcResult>; + + #[method(name = "program_codeId")] + async fn code_id(&self, program_id: H160) -> RpcResult; + + #[method(name = "program_readState")] + async fn read_state(&self, hash: H256) -> RpcResult; + + #[method(name = "program_readQueue")] + async fn read_queue(&self, hash: H256) -> RpcResult; + + #[method(name = "program_readMailbox")] + async fn read_mailbox(&self, hash: H256) -> RpcResult; + + #[method(name = "program_readPages")] + async fn read_pages(&self, hash: H256) -> RpcResult; + + #[method(name = "program_readWaitlist")] + async fn read_waitlist(&self, hash: H256) -> RpcResult; + + #[method(name = "program_readPageData")] + async fn read_page_data(&self, hash: H256) -> RpcResult; +} + +pub struct ProgramApi { + db: Database, +} + +impl ProgramApi { + pub fn new(db: Database) -> Self { + Self { db } + } +} + +#[async_trait] +impl ProgramServer for ProgramApi { + async fn calculate_reply_for_handle( + &self, + at: Option, + source: H160, + program_id: H160, + payload: Bytes, + value: u128, + ) -> RpcResult { + let block_hash = block_header_at_or_latest(&self.db, at)?.0; + + // TODO (breathx): spawn in a new thread and catch panics. (?) Generally catch runtime panics (?). + // TODO (breathx): optimize here instantiation if matches actual runtime. + let processor = Processor::new(self.db.clone()).map_err(|_| errors::internal())?; + + let mut overlaid_processor = processor.overlaid(); + + overlaid_processor + .execute_for_reply( + block_hash, + source.into(), + program_id.into(), + payload.0, + value, + ) + .map_err(errors::runtime) + } + + async fn ids(&self) -> RpcResult> { + Ok(self + .db + .program_ids() + .into_iter() + .map(|id| id.try_into().unwrap()) + .collect()) + } + + async fn code_id(&self, program_id: H160) -> RpcResult { + self.db + .program_code_id(program_id.into()) + .ok_or_else(|| errors::db("Failed to get code id")) + .map(|code_id| code_id.into()) + } + + async fn read_state(&self, hash: H256) -> RpcResult { + self.db + .read_state(hash) + .ok_or_else(|| errors::db("Failed to read state by hash")) + } + + async fn read_queue(&self, hash: H256) -> RpcResult { + self.db + .read_queue(hash) + .ok_or_else(|| errors::db("Failed to read queue by hash")) + } + + async fn read_mailbox(&self, hash: H256) -> RpcResult { + self.db + .read_mailbox(hash) + .ok_or_else(|| errors::db("Failed to read mailbox by hash")) + } + + async fn read_pages(&self, hash: H256) -> RpcResult { + self.db + .read_pages(hash) + .ok_or_else(|| errors::db("Failed to read pages by hash")) + } + + // TODO: read the whole program state in a single query + async fn read_waitlist(&self, hash: H256) -> RpcResult { + self.db + .read_waitlist(hash) + .ok_or_else(|| errors::db("Failed to read waitlist by hash")) + } + + async fn read_page_data(&self, hash: H256) -> RpcResult { + self.db + .read_page_data(hash) + .map(|buf| buf.encode().into()) + .ok_or_else(|| errors::db("Failed to read page data by hash")) + } +} diff --git a/ethexe/rpc/src/common.rs b/ethexe/rpc/src/common.rs new file mode 100644 index 00000000000..f4bae2e55d9 --- /dev/null +++ b/ethexe/rpc/src/common.rs @@ -0,0 +1,36 @@ +// This file is part of Gear. +// +// Copyright (C) 2024 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use crate::errors; +use ethexe_db::{BlockHeader, BlockMetaStorage, Database}; +use gprimitives::H256; +use jsonrpsee::core::RpcResult; + +pub fn block_header_at_or_latest( + db: &Database, + at: impl Into>, +) -> RpcResult<(H256, BlockHeader)> { + if let Some(hash) = at.into() { + db.block_header(hash) + .map(|header| (hash, header)) + .ok_or_else(|| errors::db("Block header for requested hash wasn't found")) + } else { + db.latest_valid_block() + .ok_or_else(|| errors::db("Latest block header wasn't found")) + } +} diff --git a/ethexe/rpc/src/errors.rs b/ethexe/rpc/src/errors.rs new file mode 100644 index 00000000000..49b4e786166 --- /dev/null +++ b/ethexe/rpc/src/errors.rs @@ -0,0 +1,31 @@ +// This file is part of Gear. +// +// Copyright (C) 2024 Gear Technologies Inc. +// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0 +// +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +use jsonrpsee::types::ErrorObject; + +pub fn db(err: &'static str) -> ErrorObject<'static> { + ErrorObject::owned(8000, "Database error", Some(err)) +} + +pub fn runtime(err: anyhow::Error) -> ErrorObject<'static> { + ErrorObject::owned(8000, "Runtime error", Some(format!("{err}"))) +} + +pub fn internal() -> ErrorObject<'static> { + ErrorObject::owned(8000, "Internal error", None::<&str>) +} diff --git a/ethexe/rpc/src/lib.rs b/ethexe/rpc/src/lib.rs index adf20feee6d..2621f98f41b 100644 --- a/ethexe/rpc/src/lib.rs +++ b/ethexe/rpc/src/lib.rs @@ -17,26 +17,24 @@ // along with this program. If not, see . use anyhow::anyhow; -use ethexe_db::{BlockHeader, BlockMetaStorage, Database}; -use ethexe_processor::Processor; +use apis::{BlockApi, BlockServer, ProgramApi, ProgramServer}; +use ethexe_db::Database; use futures::FutureExt; -use gear_core::message::ReplyInfo; -use gprimitives::{H160, H256}; use jsonrpsee::{ - core::{async_trait, RpcResult}, - proc_macros::rpc, server::{ serve_with_graceful_shutdown, stop_channel, Server, ServerHandle, StopHandle, TowerServiceBuilder, }, - types::ErrorObject, - Methods, + Methods, RpcModule as JsonrpcModule, }; -use sp_core::Bytes; use std::net::SocketAddr; use tokio::net::TcpListener; use tower::Service; +mod apis; +mod common; +mod errors; + #[derive(Clone)] struct PerConnection { methods: Methods, @@ -44,94 +42,6 @@ struct PerConnection { svc_builder: TowerServiceBuilder, } -#[rpc(server)] -pub trait RpcApi { - #[method(name = "blockHeader")] - async fn block_header(&self, hash: Option) -> RpcResult<(H256, BlockHeader)>; - - #[method(name = "calculateReplyForHandle")] - async fn calculate_reply_for_handle( - &self, - at: Option, - source: H160, - program_id: H160, - payload: Bytes, - value: u128, - ) -> RpcResult; -} - -pub struct RpcModule { - db: Database, -} - -impl RpcModule { - pub fn new(db: Database) -> Self { - Self { db } - } - - pub fn block_header_at_or_latest( - &self, - at: impl Into>, - ) -> RpcResult<(H256, BlockHeader)> { - if let Some(hash) = at.into() { - self.db - .block_header(hash) - .map(|header| (hash, header)) - .ok_or_else(|| db_err("Block header for requested hash wasn't found")) - } else { - self.db - .latest_valid_block() - .ok_or_else(|| db_err("Latest block header wasn't found")) - } - } -} - -#[async_trait] -impl RpcApiServer for RpcModule { - async fn block_header(&self, hash: Option) -> RpcResult<(H256, BlockHeader)> { - self.block_header_at_or_latest(hash) - } - - async fn calculate_reply_for_handle( - &self, - at: Option, - source: H160, - program_id: H160, - payload: Bytes, - value: u128, - ) -> RpcResult { - let block_hash = self.block_header_at_or_latest(at)?.0; - - // TODO (breathx): spawn in a new thread and catch panics. (?) Generally catch runtime panics (?). - // TODO (breathx): optimize here instantiation if matches actual runtime. - let processor = Processor::new(self.db.clone()).map_err(|_| internal())?; - - let mut overlaid_processor = processor.overlaid(); - - overlaid_processor - .execute_for_reply( - block_hash, - source.into(), - program_id.into(), - payload.0, - value, - ) - .map_err(runtime_err) - } -} - -fn db_err(err: &'static str) -> ErrorObject<'static> { - ErrorObject::owned(8000, "Database error", Some(err)) -} - -fn runtime_err(err: anyhow::Error) -> ErrorObject<'static> { - ErrorObject::owned(8000, "Runtime error", Some(format!("{err}"))) -} - -fn internal() -> ErrorObject<'static> { - ErrorObject::owned(8000, "Internal error", None::<&str>) -} - pub struct RpcConfig { port: u16, db: Database, @@ -153,7 +63,11 @@ impl RpcService { TcpListener::bind(SocketAddr::from(([127, 0, 0, 1], self.config.port))).await?; let service_builder = Server::builder().to_service_builder(); - let module = RpcApiServer::into_rpc(RpcModule::new(self.config.db)); + let mut module = JsonrpcModule::new(()); + module.merge(ProgramServer::into_rpc(ProgramApi::new( + self.config.db.clone(), + )))?; + module.merge(BlockServer::into_rpc(BlockApi::new(self.config.db.clone())))?; let (stop_handle, server_handle) = stop_channel(); diff --git a/ethexe/runtime/common/Cargo.toml b/ethexe/runtime/common/Cargo.toml index 0e7b00ddb19..aafd1a08a9a 100644 --- a/ethexe/runtime/common/Cargo.toml +++ b/ethexe/runtime/common/Cargo.toml @@ -21,7 +21,15 @@ gear-core-errors.workspace = true anyhow.workspace = true parity-scale-codec.workspace = true log.workspace = true +serde = { workspace = true, features = ["derive"], optional = true } [features] default = ["std"] -std = ["anyhow/std", "core-processor/std", "log/std"] +std = [ + "anyhow/std", + "core-processor/std", + "gear-core/std", + "gprimitives/serde", + "log/std", + "serde/std", +] diff --git a/ethexe/runtime/common/src/state.rs b/ethexe/runtime/common/src/state.rs index 09de4c925df..7f6f5e6c1f2 100644 --- a/ethexe/runtime/common/src/state.rs +++ b/ethexe/runtime/common/src/state.rs @@ -48,6 +48,7 @@ pub use gear_core::program::ProgramState as InitStatus; pub const MAILBOX_VALIDITY: u32 = 54_000; #[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct HashAndLen { pub hash: H256, pub len: NonZero, @@ -64,6 +65,7 @@ impl From for HashAndLen { } #[derive(Clone, Debug, Encode, Decode, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub enum MaybeHash { Hash(HashAndLen), Empty, @@ -102,6 +104,7 @@ impl MaybeHash { } #[derive(Clone, Debug, Decode, Encode, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct ActiveProgram { /// Hash of wasm memory pages allocations, see [`Allocations`]. pub allocations_hash: MaybeHash, @@ -114,6 +117,7 @@ pub struct ActiveProgram { } #[derive(Clone, Debug, Decode, Encode, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub enum Program { Active(ActiveProgram), Exited(ProgramId), @@ -138,6 +142,7 @@ impl Program { /// ethexe program state. #[derive(Clone, Debug, Decode, Encode, PartialEq, Eq)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct ProgramState { /// Active, exited or terminated program state. pub program: Program, @@ -193,6 +198,7 @@ impl ProgramState { } #[derive(Clone, Debug, Encode, Decode)] +#[cfg_attr(feature = "std", derive(serde::Serialize, serde::Deserialize))] pub struct Dispatch { /// Message id. pub id: MessageId, diff --git a/ethexe/signer/src/lib.rs b/ethexe/signer/src/lib.rs index 973b153d668..f9b57187f64 100644 --- a/ethexe/signer/src/lib.rs +++ b/ethexe/signer/src/lib.rs @@ -22,6 +22,7 @@ mod digest; mod signature; pub use digest::{Digest, ToDigest}; +use secp256k1::hashes::hex::{Case, DisplayHex}; pub use sha3; pub use signature::Signature; @@ -249,6 +250,10 @@ impl Signer { let local_public = PublicKey::from_bytes(public_key.serialize()); let key_file = self.key_store.join(local_public.to_hex()); + println!( + "Secret key: {}", + secret_key.secret_bytes().to_hex_string(Case::Lower) + ); fs::write(key_file, secret_key.secret_bytes())?; Ok(local_public) } diff --git a/gprimitives/src/lib.rs b/gprimitives/src/lib.rs index 531d54e2242..4088041a46a 100644 --- a/gprimitives/src/lib.rs +++ b/gprimitives/src/lib.rs @@ -119,6 +119,20 @@ impl From for ActorId { } } +impl TryInto for ActorId { + type Error = &'static str; + + fn try_into(self) -> Result { + if !self.0[..12].iter().all(|i| i.eq(&0)) { + Err("ActorId has non-zero prefix") + } else { + let mut h160 = H160::zero(); + h160.0.copy_from_slice(&self.into_bytes()[12..]); + Ok(h160) + } + } +} + impl fmt::Display for ActorId { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let byte_array = utils::ByteArray(&self.0);