Merge pull request #3503 from ethcore/signTransaction-format

Correct format of eth_signTransaction
This commit is contained in:
Gav Wood 2016-11-19 02:58:53 +08:00 committed by GitHub
commit c4f2b71f1b
11 changed files with 85 additions and 21 deletions

View File

@ -28,6 +28,7 @@ use jsonrpc_core::Error;
use v1::helpers::{errors, TransactionRequest, FilledTransactionRequest, ConfirmationPayload}; use v1::helpers::{errors, TransactionRequest, FilledTransactionRequest, ConfirmationPayload};
use v1::types::{ use v1::types::{
H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes, H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes,
RichRawTransaction as RpcRichRawTransaction,
ConfirmationPayload as RpcConfirmationPayload, ConfirmationPayload as RpcConfirmationPayload,
ConfirmationResponse, ConfirmationResponse,
SignRequest as RpcSignRequest, SignRequest as RpcSignRequest,
@ -47,8 +48,7 @@ pub fn execute<C, M>(client: &C, miner: &M, accounts: &AccountProvider, payload:
}, },
ConfirmationPayload::SignTransaction(request) => { ConfirmationPayload::SignTransaction(request) => {
sign_no_dispatch(client, miner, accounts, request, pass) sign_no_dispatch(client, miner, accounts, request, pass)
.map(|tx| rlp::encode(&tx).to_vec()) .map(RpcRichRawTransaction::from)
.map(RpcBytes)
.map(ConfirmationResponse::SignTransaction) .map(ConfirmationResponse::SignTransaction)
}, },
ConfirmationPayload::Signature(address, hash) => { ConfirmationPayload::Signature(address, hash) => {

View File

@ -619,6 +619,10 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
} }
} }
fn submit_transaction(&self, raw: Bytes) -> Result<RpcH256, Error> {
self.send_raw_transaction(raw)
}
fn call(&self, request: CallRequest, num: Trailing<BlockNumber>) -> Result<Bytes, Error> { fn call(&self, request: CallRequest, num: Trailing<BlockNumber>) -> Result<Bytes, Error> {
try!(self.active()); try!(self.active());

View File

@ -34,6 +34,7 @@ use v1::traits::{EthSigning, ParitySigning};
use v1::types::{ use v1::types::{
H160 as RpcH160, H256 as RpcH256, U256 as RpcU256, Bytes as RpcBytes, H520 as RpcH520, H160 as RpcH160, H256 as RpcH256, U256 as RpcU256, Bytes as RpcBytes, H520 as RpcH520,
Either as RpcEither, Either as RpcEither,
RichRawTransaction as RpcRichRawTransaction,
TransactionRequest as RpcTransactionRequest, TransactionRequest as RpcTransactionRequest,
ConfirmationPayload as RpcConfirmationPayload, ConfirmationPayload as RpcConfirmationPayload,
ConfirmationResponse as RpcConfirmationResponse ConfirmationResponse as RpcConfirmationResponse
@ -201,11 +202,11 @@ impl<C: 'static, M: 'static> EthSigning for SigningQueueClient<C, M> where
}); });
} }
fn sign_transaction(&self, ready: Ready<RpcBytes>, request: RpcTransactionRequest) { fn sign_transaction(&self, ready: Ready<RpcRichRawTransaction>, request: RpcTransactionRequest) {
let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::SignTransaction(request))); let res = self.active().and_then(|_| self.dispatch(RpcConfirmationPayload::SignTransaction(request)));
self.handle_dispatch(res, |response| { self.handle_dispatch(res, |response| {
match 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)), Err(e) => ready.ready(Err(e)),
e => ready.ready(Err(errors::internal("Unexpected result.", e))), e => ready.ready(Err(errors::internal("Unexpected result.", e))),
} }

View File

@ -31,6 +31,7 @@ use v1::types::{
U256 as RpcU256, U256 as RpcU256,
H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes, H160 as RpcH160, H256 as RpcH256, H520 as RpcH520, Bytes as RpcBytes,
Either as RpcEither, Either as RpcEither,
RichRawTransaction as RpcRichRawTransaction,
TransactionRequest as RpcTransactionRequest, TransactionRequest as RpcTransactionRequest,
ConfirmationPayload as RpcConfirmationPayload, ConfirmationPayload as RpcConfirmationPayload,
ConfirmationResponse as RpcConfirmationResponse, ConfirmationResponse as RpcConfirmationResponse,
@ -100,9 +101,9 @@ impl<C: 'static, M: 'static> EthSigning for SigningUnsafeClient<C, M> where
ready.ready(result); ready.ready(result);
} }
fn sign_transaction(&self, ready: Ready<RpcBytes>, request: RpcTransactionRequest) { fn sign_transaction(&self, ready: Ready<RpcRichRawTransaction>, request: RpcTransactionRequest) {
let result = match self.handle(RpcConfirmationPayload::SignTransaction(request)) { let result = match self.handle(RpcConfirmationPayload::SignTransaction(request)) {
Ok(RpcConfirmationResponse::SignTransaction(rlp)) => Ok(rlp), Ok(RpcConfirmationResponse::SignTransaction(tx)) => Ok(tx),
Err(e) => Err(e), Err(e) => Err(e),
e => Err(errors::internal("Unexpected result", e)), e => Err(errors::internal("Unexpected result", e)),
}; };

View File

@ -18,8 +18,10 @@ use std::str::FromStr;
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::Arc; use std::sync::Arc;
use std::time::{Instant, Duration}; use std::time::{Instant, Duration};
use rustc_serialize::hex::ToHex;
use time::get_time;
use rlp; use rlp;
use jsonrpc_core::IoHandler;
use util::{Uint, U256, Address, H256, FixedHash, Mutex}; use util::{Uint, U256, Address, H256, FixedHash, Mutex};
use ethcore::account_provider::AccountProvider; use ethcore::account_provider::AccountProvider;
use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionID}; use ethcore::client::{TestBlockChainClient, EachBlockWith, Executed, TransactionID};
@ -28,10 +30,10 @@ use ethcore::receipt::LocalizedReceipt;
use ethcore::transaction::{Transaction, Action}; use ethcore::transaction::{Transaction, Action};
use ethcore::miner::{ExternalMiner, MinerService}; use ethcore::miner::{ExternalMiner, MinerService};
use ethsync::SyncState; use ethsync::SyncState;
use jsonrpc_core::IoHandler;
use v1::{Eth, EthClient, EthClientOptions, EthFilter, EthFilterClient, EthSigning, SigningUnsafeClient}; use v1::{Eth, EthClient, EthClientOptions, EthFilter, EthFilterClient, EthSigning, SigningUnsafeClient};
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestSnapshotService}; use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestSnapshotService};
use rustc_serialize::hex::ToHex;
use time::get_time;
fn blockchain_client() -> Arc<TestBlockChainClient> { fn blockchain_client() -> Arc<TestBlockChainClient> {
let client = TestBlockChainClient::new(); 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 signature = tester.accounts_provider.sign(address, None, t.hash(None)).unwrap();
let t = t.with_signature(signature, None); let t = t.with_signature(signature, None);
let signature = t.signature();
let rlp = rlp::encode(&t); 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()); tester.miner.last_nonces.write().insert(address.clone(), U256::zero());

View File

@ -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 signature = tester.accounts.sign(address, Some("test".into()), t.hash(None)).unwrap();
let t = t.with_signature(signature, None); let t = t.with_signature(signature, None);
let signature = t.signature();
let rlp = rlp::encode(&t); 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 // then
tester.miner.last_nonces.write().insert(address.clone(), U256::zero()); tester.miner.last_nonces.write().insert(address.clone(), U256::zero());
let async_result = tester.io.handle_request(&request).unwrap(); let async_result = tester.io.handle_request(&request).unwrap();
assert_eq!(tester.signer.requests().len(), 1); assert_eq!(tester.signer.requests().len(), 1);
// respond // 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!(async_result.on_result(move |res| {
assert_eq!(res, response.to_owned()); assert_eq!(res, response.to_owned());
})); }));

View File

@ -102,6 +102,10 @@ build_rpc_trait! {
#[rpc(name = "eth_sendRawTransaction")] #[rpc(name = "eth_sendRawTransaction")]
fn send_raw_transaction(&self, Bytes) -> Result<H256, Error>; fn send_raw_transaction(&self, Bytes) -> Result<H256, Error>;
/// Alias of `eth_sendRawTransaction`.
#[rpc(name = "eth_submitTransaction")]
fn submit_transaction(&self, Bytes) -> Result<H256, Error>;
/// Call contract, returning the output data. /// Call contract, returning the output data.
#[rpc(name = "eth_call")] #[rpc(name = "eth_call")]
fn call(&self, CallRequest, Trailing<BlockNumber>) -> Result<Bytes, Error>; fn call(&self, CallRequest, Trailing<BlockNumber>) -> Result<Bytes, Error>;

View File

@ -17,7 +17,7 @@
//! Eth rpc interface. //! Eth rpc interface.
use v1::helpers::auto_args::{WrapAsync, Ready}; 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! { build_rpc_trait! {
/// Signing methods implementation relying on unlocked accounts. /// Signing methods implementation relying on unlocked accounts.
@ -33,9 +33,9 @@ build_rpc_trait! {
fn send_transaction(&self, Ready<H256>, TransactionRequest); fn send_transaction(&self, Ready<H256>, TransactionRequest);
/// Signs transactions without dispatching it to the network. /// Signs transactions without dispatching it to the network.
/// Returns signed transaction RLP representation. /// Returns signed transaction RLP representation and the transaction itself.
/// It can be later submitted using `eth_sendRawTransaction`. /// It can be later submitted using `eth_sendRawTransaction/eth_submitTransaction`.
#[rpc(async, name = "eth_signTransaction")] #[rpc(async, name = "eth_signTransaction")]
fn sign_transaction(&self, Ready<Bytes>, TransactionRequest); fn sign_transaction(&self, Ready<RichRawTransaction>, TransactionRequest);
} }
} }

View File

@ -18,7 +18,7 @@
use std::fmt; use std::fmt;
use serde::{Serialize, Serializer}; 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; use v1::helpers;
/// Confirmation waiting in a queue /// Confirmation waiting in a queue
@ -76,12 +76,12 @@ impl From<(H160, Bytes)> for DecryptRequest {
} }
/// Confirmation response for particular payload /// Confirmation response for particular payload
#[derive(Debug, Clone, Eq, PartialEq, Hash)] #[derive(Debug, Clone, PartialEq)]
pub enum ConfirmationResponse { pub enum ConfirmationResponse {
/// Transaction Hash /// Transaction Hash
SendTransaction(H256), SendTransaction(H256),
/// Transaction RLP /// Transaction RLP
SignTransaction(Bytes), SignTransaction(RichRawTransaction),
/// Signature /// Signature
Signature(H520), Signature(H520),
/// Decrypted data /// Decrypted data

View File

@ -44,7 +44,7 @@ pub use self::hash::{H64, H160, H256, H512, H520, H2048};
pub use self::index::Index; pub use self::index::Index;
pub use self::log::Log; pub use self::log::Log;
pub use self::sync::{SyncStatus, SyncInfo, Peers, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo, PeerEthereumProtocolInfo}; 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::transaction_request::TransactionRequest;
pub use self::receipt::Receipt; pub use self::receipt::Receipt;
pub use self::rpc_settings::RpcSettings; pub use self::rpc_settings::RpcSettings;

View File

@ -19,7 +19,7 @@ use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction};
use v1::types::{Bytes, H160, H256, U256, H512}; use v1::types::{Bytes, H160, H256, U256, H512};
/// Transaction /// Transaction
#[derive(Debug, Default, Serialize)] #[derive(Debug, Default, Clone, PartialEq, Serialize)]
pub struct Transaction { pub struct Transaction {
/// Hash /// Hash
pub hash: H256, pub hash: H256,
@ -62,6 +62,26 @@ pub struct Transaction {
pub s: H256, 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<SignedTransaction> for RichRawTransaction {
fn from(t: SignedTransaction) -> Self {
let tx: Transaction = t.into();
RichRawTransaction {
raw: tx.raw.clone(),
transaction: tx,
}
}
}
impl From<LocalizedTransaction> for Transaction { impl From<LocalizedTransaction> for Transaction {
fn from(t: LocalizedTransaction) -> Transaction { fn from(t: LocalizedTransaction) -> Transaction {
let signature = t.signature(); let signature = t.signature();