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

sendrawtransaction RPC method implemented #267

Merged
merged 4 commits into from
Dec 9, 2016
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
9 changes: 9 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 7 additions & 3 deletions pbtc/commands/start.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::net::SocketAddr;
use sync::create_sync_connection_factory;
use sync::{create_local_sync_node, create_sync_connection_factory};
use message::Services;
use util::{open_db, init_db, node_table_path};
use {config, p2p, PROTOCOL_VERSION, PROTOCOL_MINIMUM};
Expand Down Expand Up @@ -34,9 +34,13 @@ pub fn start(cfg: config::Config) -> Result<(), String> {
};

let sync_handle = el.handle();
let sync_connection_factory = create_sync_connection_factory(&sync_handle, cfg.magic, db);
let local_sync_node = create_local_sync_node(&sync_handle, cfg.magic, db);
let sync_connection_factory = create_sync_connection_factory(local_sync_node.clone());

let _http_server = try!(rpc::new_http(cfg.rpc_config));
let rpc_deps = rpc::Dependencies {
local_sync_node: local_sync_node,
};
let _rpc_server = try!(rpc::new_http(cfg.rpc_config, rpc_deps));

let p2p = try!(p2p::P2P::new(p2p_cfg, sync_connection_factory, el.handle()).map_err(|x| x.to_string()));
try!(p2p.run().map_err(|_| "Failed to start p2p module"));
Expand Down
18 changes: 12 additions & 6 deletions pbtc/rpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ use std::net::SocketAddr;
use rpc_apis::{self, ApiSet};
use ethcore_rpc::{Server, RpcServer, RpcServerError};
use std::io;
use sync;

pub struct Dependencies {
pub local_sync_node: sync::LocalNodeRef,
}

#[derive(Debug, PartialEq)]
pub struct HttpConfiguration {
Expand All @@ -26,23 +31,24 @@ impl HttpConfiguration {
}
}

pub fn new_http(conf: HttpConfiguration) -> Result<Option<Server>, String> {
pub fn new_http(conf: HttpConfiguration, deps: Dependencies) -> Result<Option<Server>, String> {
if !conf.enabled {
return Ok(None);
}

let url = format!("{}:{}", conf.interface, conf.port);
let addr = try!(url.parse().map_err(|_| format!("Invalid JSONRPC listen host/port given: {}", url)));
Ok(Some(try!(setup_http_rpc_server(&addr, conf.cors, conf.hosts, conf.apis))))
Ok(Some(try!(setup_http_rpc_server(&addr, conf.cors, conf.hosts, conf.apis, deps))))
}

pub fn setup_http_rpc_server(
url: &SocketAddr,
cors_domains: Option<Vec<String>>,
allowed_hosts: Option<Vec<String>>,
apis: ApiSet
apis: ApiSet,
deps: Dependencies,
) -> Result<Server, String> {
let server = try!(setup_rpc_server(apis));
let server = try!(setup_rpc_server(apis, deps));
// TODO: PanicsHandler
let start_result = server.start_http(url, cors_domains, allowed_hosts);
match start_result {
Expand All @@ -55,7 +61,7 @@ pub fn setup_http_rpc_server(
}
}

fn setup_rpc_server(apis: ApiSet) -> Result<RpcServer, String> {
fn setup_rpc_server(apis: ApiSet, deps: Dependencies) -> Result<RpcServer, String> {
let server = RpcServer::new();
Ok(rpc_apis::setup_rpc(server, apis))
Ok(rpc_apis::setup_rpc(server, apis, deps))
}
5 changes: 3 additions & 2 deletions pbtc/rpc_apis.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use std::str::FromStr;
use std::collections::HashSet;
use rpc::Dependencies;
use ethcore_rpc::Extendable;

#[derive(Debug, PartialEq, Eq, Hash, Copy, Clone)]
Expand Down Expand Up @@ -38,12 +39,12 @@ impl ApiSet {
}
}

pub fn setup_rpc<T: Extendable>(server: T, apis: ApiSet) -> T {
pub fn setup_rpc<T: Extendable>(server: T, apis: ApiSet, deps: Dependencies) -> T {
use ethcore_rpc::v1::*;

for api in apis.list_apis() {
match api {
Api::Raw => server.add_delegate(RawClient::new().to_delegate()),
Api::Raw => server.add_delegate(RawClient::new(RawClientCore::new(deps.local_sync_node.clone())).to_delegate()),
}
}
server
Expand Down
4 changes: 4 additions & 0 deletions primitives/src/hash.rs
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,10 @@ macro_rules! impl_hash {
impl Eq for $name { }

impl $name {
pub fn take(self) -> [u8; $size] {
self.0
}

pub fn reversed(&self) -> Self {
let mut result = self.clone();
result.reverse();
Expand Down
9 changes: 9 additions & 0 deletions rpc/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,18 @@ log = "0.3"
serde = "0.8"
serde_json = "0.8"
rustc-serialize = "0.3"
tokio-core = "0.1.1"
serde_macros = { version = "0.8.0", optional = true }
jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" }
jsonrpc-http-server = { git = "https://github.com/ethcore/jsonrpc.git" }
sync = { path = "../sync" }
serialization = { path = "../serialization" }
chain = { path = "../chain" }
primitives = { path = "../primitives" }
p2p = { path = "../p2p" }
network = { path = "../network" }
db = { path = "../db" }
test-data = { path = "../test-data" }

[build-dependencies]
serde_codegen = { version = "0.8.0", optional = true }
Expand Down
9 changes: 9 additions & 0 deletions rpc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,15 @@ extern crate serde;
extern crate serde_json;
extern crate jsonrpc_core;
extern crate jsonrpc_http_server;
extern crate tokio_core;
extern crate sync;
extern crate chain;
extern crate serialization as ser;
extern crate primitives;
extern crate p2p;
extern crate network;
extern crate db;
extern crate test_data;

pub mod v1;
pub mod rpc_server;
Expand Down
14 changes: 14 additions & 0 deletions rpc/src/v1/helpers/errors.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
///! RPC Error codes and error objects

mod codes {
// NOTE [ToDr] Codes from [-32099, -32000]
pub const EXECUTION_ERROR: i64 = -32015;
}


macro_rules! rpc_unimplemented {
() => (Err(::v1::helpers::errors::unimplemented(None)))
}
Expand All @@ -22,3 +28,11 @@ pub fn invalid_params<T: fmt::Debug>(param: &str, details: T) -> Error {
data: Some(Value::String(format!("{:?}", details))),
}
}

pub fn execution<T: fmt::Debug>(data: T) -> Error {
Error {
code: ErrorCode::ServerError(codes::EXECUTION_ERROR),
message: "Execution error.".into(),
data: Some(Value::String(format!("{:?}", data))),
}
}
2 changes: 1 addition & 1 deletion rpc/src/v1/impls/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
mod raw;

pub use self::raw::RawClient;
pub use self::raw::{RawClient, RawClientCore};
114 changes: 107 additions & 7 deletions rpc/src/v1/impls/raw.rs
Original file line number Diff line number Diff line change
@@ -1,17 +1,117 @@
use v1::traits::Raw;
use v1::types::RawTransaction;
use v1::types::H256;
use v1::helpers::errors::{execution, invalid_params};
use jsonrpc_core::Error;
use chain::Transaction;
use sync;
use ser::{Reader, deserialize};
use primitives::hash::H256 as GlobalH256;

pub struct RawClient;
pub struct RawClient<T: RawClientCoreApi> {
core: T,
}

pub trait RawClientCoreApi: Send + Sync + 'static {
fn accept_transaction(&self, transaction: Transaction) -> Result<GlobalH256, String>;
}

pub struct RawClientCore {
local_sync_node: sync::LocalNodeRef,
}

impl RawClientCore {
pub fn new(local_sync_node: sync::LocalNodeRef) -> Self {
RawClientCore {
local_sync_node: local_sync_node,
}
}
}

impl RawClientCoreApi for RawClientCore {
fn accept_transaction(&self, transaction: Transaction) -> Result<GlobalH256, String> {
self.local_sync_node.accept_transaction(transaction)
}
}

impl<T> RawClient<T> where T: RawClientCoreApi {
pub fn new(core: T) -> Self {
RawClient {
core: core,
}
}
}

impl RawClient {
pub fn new() -> Self {
RawClient { }
impl<T> Raw for RawClient<T> where T: RawClientCoreApi {
fn send_raw_transaction(&self, raw_transaction: RawTransaction) -> Result<H256, Error> {
let raw_transaction_data: Vec<u8> = raw_transaction.into();
let transaction = try!(deserialize(Reader::new(&raw_transaction_data)).map_err(|e| invalid_params("tx", e)));
self.core.accept_transaction(transaction)
.map(|h| h.reversed().into())
.map_err(|e| execution(e))
}
}

impl Raw for RawClient {
fn send_raw_transaction(&self, _tx: RawTransaction) -> Result<(), Error> {
rpc_unimplemented!()
#[cfg(test)]
pub mod tests {
use jsonrpc_core::{IoHandler, GenericIoHandler};
use chain::Transaction;
use primitives::hash::H256 as GlobalH256;
use v1::traits::Raw;
use super::*;

#[derive(Default)]
struct SuccessRawClientCore;
#[derive(Default)]
struct ErrorRawClientCore;

impl RawClientCoreApi for SuccessRawClientCore {
fn accept_transaction(&self, transaction: Transaction) -> Result<GlobalH256, String> {
Ok(transaction.hash())
}
}

impl RawClientCoreApi for ErrorRawClientCore {
fn accept_transaction(&self, _transaction: Transaction) -> Result<GlobalH256, String> {
Err("error".to_owned())
}
}

#[test]
fn sendrawtransaction_accepted() {
let client = RawClient::new(SuccessRawClientCore::default());
let handler = IoHandler::new();
handler.add_delegate(client.to_delegate());

let sample = handler.handle_request_sync(&(r#"
{
"jsonrpc": "2.0",
"method": "sendrawtransaction",
"params": ["00000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a0000000000000000000101000000000000000000000000"],
"id": 1
}"#)
).unwrap();

// direct hash is 0791efccd035c5fe501023ff888106eba5eff533965de4a6e06400f623bcac34
// but client expects reverse hash
assert_eq!(r#"{"jsonrpc":"2.0","result":"34acbc23f60064e0a6e45d9633f5efa5eb068188ff231050fec535d0ccef9107","id":1}"#, &sample);
}

#[test]
fn sendrawtransaction_rejected() {
let client = RawClient::new(ErrorRawClientCore::default());
let handler = IoHandler::new();
handler.add_delegate(client.to_delegate());

let sample = handler.handle_request_sync(&(r#"
{
"jsonrpc": "2.0",
"method": "sendrawtransaction",
"params": ["00000000013ba3edfd7a7b12b27ac72c3e67768f617fc81bc3888a51323a9fb8aa4b1e5e4a0000000000000000000101000000000000000000000000"],
"id": 1
}"#)
).unwrap();

assert_eq!(r#"{"jsonrpc":"2.0","error":{"code":-32015,"message":"Execution error.","data":"\"error\""},"id":1}"#, &sample);
}
}
2 changes: 1 addition & 1 deletion rpc/src/v1/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,4 @@ pub mod traits;
pub mod types;

pub use self::traits::Raw;
pub use self::impls::RawClient;
pub use self::impls::{RawClient, RawClientCore};
3 changes: 2 additions & 1 deletion rpc/src/v1/traits/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ use jsonrpc_core::Error;

use v1::helpers::auto_args::Wrap;
use v1::types::RawTransaction;
use v1::types::H256;

build_rpc_trait! {
/// Partiy-bitcoin raw data interface.
pub trait Raw {
/// Adds transaction to the memory pool && relays it to the peers.
#[rpc(name = "sendrawtransaction")]
fn send_raw_transaction(&self, RawTransaction) -> Result<(), Error>;
fn send_raw_transaction(&self, RawTransaction) -> Result<H256, Error>;
}
}
Loading