diff --git a/substrate/client/serai/src/lib.rs b/substrate/client/serai/src/lib.rs index 9c925e67..944ce523 100644 --- a/substrate/client/serai/src/lib.rs +++ b/substrate/client/serai/src/lib.rs @@ -14,18 +14,16 @@ use borsh::BorshDeserialize; pub use serai_abi as abi; use abi::{ primitives::{BlockHash, network_id::ExternalNetworkId}, - Block, Event, + Transaction, Block, Event, }; use async_lock::RwLock; -/// RPC client functionality for the coins module. -pub mod coins; -use coins::*; +mod coins; +pub use coins::Coins; -/// RPC client functionality for the validator sets module. -pub mod validator_sets; -use validator_sets::*; +mod validator_sets; +pub use validator_sets::ValidatorSets; /// An error from the RPC. #[derive(Debug, Error)] @@ -164,6 +162,16 @@ impl Serai { Self::block_internal(self.call("blockchain/block", &format!(r#"{{ "block": {block} }}"#))).await } + /// Publish a transaction onto the Serai blockchain. + pub async fn publish_transaction(&self, transaction: &Transaction) -> Result<(), RpcError> { + self + .call( + "blockchain/publish_transaction", + &format!(r#"{{ "transaction": {} }}"#, hex::encode(borsh::to_vec(transaction).unwrap())), + ) + .await + } + /// Scope this RPC client to the state as of a specific block. /// /// This will yield an error if the block chosen isn't finalized. This ensures, given an honest diff --git a/substrate/node/src/rpc/blockchain.rs b/substrate/node/src/rpc/blockchain.rs index e0cdb868..c7bf3612 100644 --- a/substrate/node/src/rpc/blockchain.rs +++ b/substrate/node/src/rpc/blockchain.rs @@ -1,4 +1,5 @@ -use std::{sync::Arc, ops::Deref, collections::HashSet}; +use core::{ops::Deref, future::Future}; +use std::{sync::Arc, collections::HashSet}; use rand_core::{RngCore, OsRng}; @@ -8,8 +9,9 @@ use sp_consensus::BlockStatus; use sp_block_builder::BlockBuilder; use sp_api::ProvideRuntimeApi; use sc_client_api::BlockBackend; +use sc_transaction_pool_api::TransactionPool; -use serai_abi::{primitives::prelude::*, SubstrateBlock as Block}; +use serai_abi::{primitives::prelude::*, Transaction, SubstrateBlock as Block}; use serai_runtime::SeraiApi; @@ -27,6 +29,7 @@ pub(crate) fn module< + ProvideRuntimeApi>, >( client: Arc, + pool: Arc>, ) -> Result, Box> { let mut module = RpcModule::new(client); @@ -80,5 +83,36 @@ pub(crate) fn module< ) })?; + module.register_async_method("blockchain/publish_transaction", move |params, client, _ext| { + let pool = pool.clone(); + async move { + #[derive(sp_core::serde::Deserialize)] + #[serde(crate = "sp_core::serde")] + struct TransactionRequest { + transaction: String, + }; + let Ok(transaction) = params.parse::() else { + return Err(Error::InvalidRequest(r#"missing `string` "transaction" field"#)); + }; + let Ok(transaction) = hex::decode(transaction.transaction) else { + Err(Error::InvalidRequest(r#"transaction was not hex-encoded"#))? + }; + let Ok(transaction) = + ::deserialize_reader(&mut transaction.as_slice()) + else { + Err(Error::InvalidRequest(r#"transaction could not be deserialized"#))? + }; + pool + .submit_one( + client.info().best_hash, + sc_transaction_pool_api::TransactionSource::External, + transaction, + ) + .await + .map_err(|e| Error::InvalidTransaction(format!("{e}")))?; + Ok(()) + } + })?; + Ok(module) } diff --git a/substrate/node/src/rpc/mod.rs b/substrate/node/src/rpc/mod.rs index dd7dd5b7..cb8c94e3 100644 --- a/substrate/node/src/rpc/mod.rs +++ b/substrate/node/src/rpc/mod.rs @@ -36,14 +36,14 @@ pub fn create_full< + HeaderBackend + HeaderMetadata + BlockBackend, - P: 'static + TransactionPool, + P: 'static + TransactionPool, >( deps: FullDeps, ) -> Result, Box> { let FullDeps { id, client, pool, authority_discovery } = deps; let mut root = RpcModule::new(()); - root.merge(blockchain::module(client.clone())?)?; + root.merge(blockchain::module(client.clone(), pool)?)?; root.merge(validator_sets::module(client.clone()))?; if let Some(authority_discovery) = authority_discovery { root.merge(p2p_validators::module(id, client, authority_discovery)?)?; diff --git a/substrate/node/src/rpc/utils.rs b/substrate/node/src/rpc/utils.rs index 998ff588..ba16f886 100644 --- a/substrate/node/src/rpc/utils.rs +++ b/substrate/node/src/rpc/utils.rs @@ -7,6 +7,7 @@ pub(super) enum Error { Internal(&'static str), InvalidRequest(&'static str), InvalidStateReference, + InvalidTransaction(String), } impl From for jsonrpsee::types::error::ErrorObjectOwned { @@ -19,10 +20,15 @@ impl From for jsonrpsee::types::error::ErrorObjectOwned { jsonrpsee::types::error::ErrorObjectOwned::owned(-2, str, Option::<()>::None) } Error::InvalidStateReference => jsonrpsee::types::error::ErrorObjectOwned::owned( - -4, + -3, "the block used as the reference was not locally held", Option::<()>::None, ), + Error::InvalidTransaction(str) => jsonrpsee::types::error::ErrorObjectOwned::owned( + -4, + format!("transaction was not accepted to the mempool: {str}"), + Option::<()>::None, + ), } } }