From 5897e01dbdc542a7a12af249c654ab47c0365d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tomasz=20Drwi=C4=99ga?= Date: Fri, 18 Nov 2016 11:03:29 +0100 Subject: [PATCH] Correct format of eth_signTransaction --- rpc/src/v1/helpers/dispatch.rs | 4 ++-- rpc/src/v1/impls/eth.rs | 4 ++++ rpc/src/v1/impls/signing.rs | 5 +++-- rpc/src/v1/impls/signing_unsafe.rs | 5 +++-- rpc/src/v1/tests/mocked/eth.rs | 26 ++++++++++++++++++++++---- rpc/src/v1/tests/mocked/signing.rs | 20 ++++++++++++++++++-- rpc/src/v1/traits/eth.rs | 4 ++++ rpc/src/v1/traits/eth_signing.rs | 8 ++++---- rpc/src/v1/types/confirmations.rs | 6 +++--- rpc/src/v1/types/mod.rs.in | 2 +- rpc/src/v1/types/transaction.rs | 22 +++++++++++++++++++++- 11 files changed, 85 insertions(+), 21 deletions(-) diff --git a/rpc/src/v1/helpers/dispatch.rs b/rpc/src/v1/helpers/dispatch.rs index cbae49cc3..a66bc816d 100644 --- a/rpc/src/v1/helpers/dispatch.rs +++ b/rpc/src/v1/helpers/dispatch.rs @@ -28,6 +28,7 @@ use jsonrpc_core::Error; use v1::helpers::{errors, TransactionRequest, FilledTransactionRequest, ConfirmationPayload}; use v1::types::{ H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes, + RichRawTransaction as RpcRichRawTransaction, ConfirmationPayload as RpcConfirmationPayload, ConfirmationResponse, SignRequest as RpcSignRequest, @@ -47,8 +48,7 @@ pub fn execute(client: &C, miner: &M, accounts: &AccountProvider, payload: }, ConfirmationPayload::SignTransaction(request) => { sign_no_dispatch(client, miner, accounts, request, pass) - .map(|tx| rlp::encode(&tx).to_vec()) - .map(RpcBytes) + .map(RpcRichRawTransaction::from) .map(ConfirmationResponse::SignTransaction) }, ConfirmationPayload::Signature(address, hash) => { diff --git a/rpc/src/v1/impls/eth.rs b/rpc/src/v1/impls/eth.rs index 8207426ba..5f1449e07 100644 --- a/rpc/src/v1/impls/eth.rs +++ b/rpc/src/v1/impls/eth.rs @@ -619,6 +619,10 @@ impl Eth for EthClient where } } + fn submit_transaction(&self, raw: Bytes) -> Result { + self.send_raw_transaction(raw) + } + fn call(&self, request: CallRequest, num: Trailing) -> Result { try!(self.active()); diff --git a/rpc/src/v1/impls/signing.rs b/rpc/src/v1/impls/signing.rs index cf20f1045..262e04dfb 100644 --- a/rpc/src/v1/impls/signing.rs +++ b/rpc/src/v1/impls/signing.rs @@ -34,6 +34,7 @@ use v1::traits::{EthSigning, ParitySigning}; use v1::types::{ H160 as RpcH160, H256 as RpcH256, U256 as RpcU256, Bytes as RpcBytes, H520 as RpcH520, Either as RpcEither, + RichRawTransaction as RpcRichRawTransaction, TransactionRequest as RpcTransactionRequest, ConfirmationPayload as RpcConfirmationPayload, ConfirmationResponse as RpcConfirmationResponse @@ -201,11 +202,11 @@ impl EthSigning for SigningQueueClient where }); } - fn sign_transaction(&self, ready: Ready, request: RpcTransactionRequest) { + fn sign_transaction(&self, ready: Ready, request: RpcTransactionRequest) { let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::SignTransaction(request))); self.handle_dispatch(res, |response| { match response { - Ok(RpcConfirmationResponse::SignTransaction(rlp)) => ready.ready(Ok(rlp)), + Ok(RpcConfirmationResponse::SignTransaction(tx)) => ready.ready(Ok(tx)), Err(e) => ready.ready(Err(e)), e => ready.ready(Err(errors::internal("Unexpected result.", e))), } diff --git a/rpc/src/v1/impls/signing_unsafe.rs b/rpc/src/v1/impls/signing_unsafe.rs index da9aeb901..46ffe6ded 100644 --- a/rpc/src/v1/impls/signing_unsafe.rs +++ b/rpc/src/v1/impls/signing_unsafe.rs @@ -31,6 +31,7 @@ use v1::types::{ U256 as RpcU256, H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes, Either as RpcEither, + RichRawTransaction as RpcRichRawTransaction, TransactionRequest as RpcTransactionRequest, ConfirmationPayload as RpcConfirmationPayload, ConfirmationResponse as RpcConfirmationResponse, @@ -100,9 +101,9 @@ impl EthSigning for SigningUnsafeClient where ready.ready(result); } - fn sign_transaction(&self, ready: Ready, request: RpcTransactionRequest) { + fn sign_transaction(&self, ready: Ready, request: RpcTransactionRequest) { let result = match self.handle(RpcConfirmationPayload::SignTransaction(request)) { - Ok(RpcConfirmationResponse::SignTransaction(rlp)) => Ok(rlp), + Ok(RpcConfirmationResponse::SignTransaction(tx)) => Ok(tx), Err(e) => Err(e), e => Err(errors::internal("Unexpected result", e)), }; diff --git a/rpc/src/v1/tests/mocked/eth.rs b/rpc/src/v1/tests/mocked/eth.rs index 67e77a6db..861bb5234 100644 --- a/rpc/src/v1/tests/mocked/eth.rs +++ b/rpc/src/v1/tests/mocked/eth.rs @@ -18,8 +18,10 @@ use std::str::FromStr; use std::collections::HashMap; use std::sync::Arc; use std::time::{Instant, Duration}; +use rustc_serialize::hex::ToHex; +use time::get_time; use rlp; -use jsonrpc_core::IoHandler; + use util::{Uint, U256, Address, H256, FixedHash, Mutex}; use ethcore::account_provider::AccountProvider; use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionID}; @@ -28,10 +30,10 @@ use ethcore::receipt::LocalizedReceipt; use ethcore::transaction::{Transaction, Action}; use ethcore::miner::{ExternalMiner, MinerService}; use ethsync::SyncState; + +use jsonrpc_core::IoHandler; use v1::{Eth, EthClient, EthClientOptions, EthFilter, EthFilterClient, EthSigning, SigningUnsafeClient}; use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestSnapshotService}; -use rustc_serialize::hex::ToHex; -use time::get_time; fn blockchain_client() -> Arc { let client = TestBlockChainClient::new(); @@ -798,9 +800,25 @@ fn rpc_eth_sign_transaction() { }; let signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap(); let t = t.with_signature(signature, None); + let signature = t.signature(); let rlp = rlp::encode(&t); - let response = r#"{"jsonrpc":"2.0","result":"0x"#.to_owned() + &rlp.to_hex() + r#"","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + + r#""raw":"0x"# + &rlp.to_hex() + r#"","# + + r#""tx":{"# + + r#""blockHash":null,"blockNumber":null,"creates":null,"# + + &format!("\"from\":\"0x{:?}\",", &address) + + r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + + &format!("\"hash\":\"0x{:?}\",", t.hash()) + + r#""input":"0x","nonce":"0x1","# + + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + + &format!("\"r\":\"0x{}\",", signature.r().to_hex()) + + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + + &format!("\"s\":\"0x{}\",", signature.s().to_hex()) + + r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + + &format!("\"v\":{},", signature.v()) + + r#""value":"0x9184e72a""# + + r#"}},"id":1}"#; tester.miner.last_nonces.write().insert(address.clone(), U256::zero()); diff --git a/rpc/src/v1/tests/mocked/signing.rs b/rpc/src/v1/tests/mocked/signing.rs index b43c1e180..7431bc45e 100644 --- a/rpc/src/v1/tests/mocked/signing.rs +++ b/rpc/src/v1/tests/mocked/signing.rs @@ -268,16 +268,32 @@ fn should_add_sign_transaction_to_the_queue() { }; let signature = tester.accounts.sign(address, Some("test".into()), t.hash(None)).unwrap(); let t = t.with_signature(signature, None); + let signature = t.signature(); let rlp = rlp::encode(&t); - let response = r#"{"jsonrpc":"2.0","result":"0x"#.to_owned() + &rlp.to_hex() + r#"","id":1}"#; + let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned() + + r#""raw":"0x"# + &rlp.to_hex() + r#"","# + + r#""tx":{"# + + r#""blockHash":null,"blockNumber":null,"creates":null,"# + + &format!("\"from\":\"0x{:?}\",", &address) + + r#""gas":"0x76c0","gasPrice":"0x9184e72a000","# + + &format!("\"hash\":\"0x{:?}\",", t.hash()) + + r#""input":"0x","nonce":"0x1","# + + &format!("\"publicKey\":\"0x{:?}\",", t.public_key().unwrap()) + + &format!("\"r\":\"0x{}\",", signature.r().to_hex()) + + &format!("\"raw\":\"0x{}\",", rlp.to_hex()) + + &format!("\"s\":\"0x{}\",", signature.s().to_hex()) + + r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"# + + &format!("\"v\":{},", signature.v()) + + r#""value":"0x9184e72a""# + + r#"}},"id":1}"#; // then tester.miner.last_nonces.write().insert(address.clone(), U256::zero()); let async_result = tester.io.handle_request(&request).unwrap(); assert_eq!(tester.signer.requests().len(), 1); // respond - tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SignTransaction(rlp.to_vec().into()))); + tester.signer.request_confirmed(1.into(), Ok(ConfirmationResponse::SignTransaction(t.into()))); assert!(async_result.on_result(move |res| { assert_eq!(res, response.to_owned()); })); diff --git a/rpc/src/v1/traits/eth.rs b/rpc/src/v1/traits/eth.rs index b524c616f..6308be324 100644 --- a/rpc/src/v1/traits/eth.rs +++ b/rpc/src/v1/traits/eth.rs @@ -102,6 +102,10 @@ build_rpc_trait! { #[rpc(name = "eth_sendRawTransaction")] fn send_raw_transaction(&self, Bytes) -> Result; + /// Alias of `eth_sendRawTransaction`. + #[rpc(name = "eth_submitTransaction")] + fn submit_transaction(&self, Bytes) -> Result; + /// Call contract, returning the output data. #[rpc(name = "eth_call")] fn call(&self, CallRequest, Trailing) -> Result; diff --git a/rpc/src/v1/traits/eth_signing.rs b/rpc/src/v1/traits/eth_signing.rs index bd1d1ecdc..09f8c5e03 100644 --- a/rpc/src/v1/traits/eth_signing.rs +++ b/rpc/src/v1/traits/eth_signing.rs @@ -17,7 +17,7 @@ //! Eth rpc interface. use v1::helpers::auto_args::{WrapAsync, Ready}; -use v1::types::{H160, H256, H520, TransactionRequest, Bytes}; +use v1::types::{H160, H256, H520, TransactionRequest, RichRawTransaction}; build_rpc_trait! { /// Signing methods implementation relying on unlocked accounts. @@ -33,9 +33,9 @@ build_rpc_trait! { fn send_transaction(&self, Ready, TransactionRequest); /// Signs transactions without dispatching it to the network. - /// Returns signed transaction RLP representation. - /// It can be later submitted using `eth_sendRawTransaction`. + /// Returns signed transaction RLP representation and the transaction itself. + /// It can be later submitted using `eth_sendRawTransaction/eth_submitTransaction`. #[rpc(async, name = "eth_signTransaction")] - fn sign_transaction(&self, Ready, TransactionRequest); + fn sign_transaction(&self, Ready, TransactionRequest); } } diff --git a/rpc/src/v1/types/confirmations.rs b/rpc/src/v1/types/confirmations.rs index e83989326..2b7813df9 100644 --- a/rpc/src/v1/types/confirmations.rs +++ b/rpc/src/v1/types/confirmations.rs @@ -18,7 +18,7 @@ use std::fmt; use serde::{Serialize, Serializer}; -use v1::types::{U256, TransactionRequest, H160, H256, H520, Bytes}; +use v1::types::{U256, TransactionRequest, RichRawTransaction, H160, H256, H520, Bytes}; use v1::helpers; /// Confirmation waiting in a queue @@ -76,12 +76,12 @@ impl From<(H160, Bytes)> for DecryptRequest { } /// Confirmation response for particular payload -#[derive(Debug, Clone, Eq, PartialEq, Hash)] +#[derive(Debug, Clone, PartialEq)] pub enum ConfirmationResponse { /// Transaction Hash SendTransaction(H256), /// Transaction RLP - SignTransaction(Bytes), + SignTransaction(RichRawTransaction), /// Signature Signature(H520), /// Decrypted data diff --git a/rpc/src/v1/types/mod.rs.in b/rpc/src/v1/types/mod.rs.in index b1e4ae2c9..924f12884 100644 --- a/rpc/src/v1/types/mod.rs.in +++ b/rpc/src/v1/types/mod.rs.in @@ -44,7 +44,7 @@ pub use self::hash::{H64, H160, H256, H512, H520, H2048}; pub use self::index::Index; pub use self::log::Log; pub use self::sync::{SyncStatus, SyncInfo, Peers, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo, PeerEthereumProtocolInfo}; -pub use self::transaction::Transaction; +pub use self::transaction::{Transaction, RichRawTransaction}; pub use self::transaction_request::TransactionRequest; pub use self::receipt::Receipt; pub use self::rpc_settings::RpcSettings; diff --git a/rpc/src/v1/types/transaction.rs b/rpc/src/v1/types/transaction.rs index 0982a5ef9..1f1ef1787 100644 --- a/rpc/src/v1/types/transaction.rs +++ b/rpc/src/v1/types/transaction.rs @@ -19,7 +19,7 @@ use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction}; use v1::types::{Bytes, H160, H256, U256, H512}; /// Transaction -#[derive(Debug, Default, Serialize)] +#[derive(Debug, Default, Clone, PartialEq, Serialize)] pub struct Transaction { /// Hash pub hash: H256, @@ -62,6 +62,26 @@ pub struct Transaction { pub s: H256, } +/// Geth-compatible output for eth_signTransaction method +#[derive(Debug, Default, Clone, PartialEq, Serialize)] +pub struct RichRawTransaction { + /// Raw transaction RLP + pub raw: Bytes, + /// Transaction details + #[serde(rename="tx")] + pub transaction: Transaction +} + +impl From for RichRawTransaction { + fn from(t: SignedTransaction) -> Self { + let tx: Transaction = t.into(); + RichRawTransaction { + raw: tx.raw.clone(), + transaction: tx, + } + } +} + impl From for Transaction { fn from(t: LocalizedTransaction) -> Transaction { let signature = t.signature();