Merge branch 'master' into auth-bft
This commit is contained in:
@@ -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<C, M>(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) => {
|
||||
|
||||
@@ -22,7 +22,7 @@ macro_rules! rpc_unimplemented {
|
||||
|
||||
use std::fmt;
|
||||
use rlp::DecoderError;
|
||||
use ethcore::error::{Error as EthcoreError, CallError};
|
||||
use ethcore::error::{Error as EthcoreError, CallError, TransactionError};
|
||||
use ethcore::account_provider::{Error as AccountError};
|
||||
use fetch::FetchError;
|
||||
use jsonrpc_core::{Error, ErrorCode, Value};
|
||||
@@ -227,40 +227,44 @@ pub fn from_password_error(error: AccountError) -> Error {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_transaction_error(error: EthcoreError) -> Error {
|
||||
pub fn transaction_message(error: TransactionError) -> String {
|
||||
use ethcore::error::TransactionError::*;
|
||||
|
||||
match error {
|
||||
AlreadyImported => "Transaction with the same hash was already imported.".into(),
|
||||
Old => "Transaction nonce is too low. Try incrementing the nonce.".into(),
|
||||
TooCheapToReplace => {
|
||||
"Transaction gas price is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce.".into()
|
||||
},
|
||||
LimitReached => {
|
||||
"There are too many transactions in the queue. Your transaction was dropped due to limit. Try increasing the fee.".into()
|
||||
},
|
||||
InsufficientGas { minimal, got } => {
|
||||
format!("Transaction gas is too low. There is not enough gas to cover minimal cost of the transaction (minimal: {}, got: {}). Try increasing supplied gas.", minimal, got)
|
||||
},
|
||||
InsufficientGasPrice { minimal, got } => {
|
||||
format!("Transaction gas price is too low. It does not satisfy your node's minimal gas price (minimal: {}, got: {}). Try increasing the gas price.", minimal, got)
|
||||
},
|
||||
InsufficientBalance { balance, cost } => {
|
||||
format!("Insufficient funds. Account you try to send transaction from does not have enough funds. Required {} and got: {}.", cost, balance)
|
||||
},
|
||||
GasLimitExceeded { limit, got } => {
|
||||
format!("Transaction cost exceeds current gas limit. Limit: {}, got: {}. Try decreasing supplied gas.", limit, got)
|
||||
},
|
||||
InvalidNetworkId => "Invalid network id.".into(),
|
||||
InvalidGasLimit(_) => "Supplied gas is beyond limit.".into(),
|
||||
SenderBanned => "Sender is banned in local queue.".into(),
|
||||
RecipientBanned => "Recipient is banned in local queue.".into(),
|
||||
CodeBanned => "Code is banned in local queue.".into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_transaction_error(error: EthcoreError) -> Error {
|
||||
|
||||
if let EthcoreError::Transaction(e) = error {
|
||||
let msg = match e {
|
||||
AlreadyImported => "Transaction with the same hash was already imported.".into(),
|
||||
Old => "Transaction nonce is too low. Try incrementing the nonce.".into(),
|
||||
TooCheapToReplace => {
|
||||
"Transaction gas price is too low. There is another transaction with same nonce in the queue. Try increasing the gas price or incrementing the nonce.".into()
|
||||
},
|
||||
LimitReached => {
|
||||
"There are too many transactions in the queue. Your transaction was dropped due to limit. Try increasing the fee.".into()
|
||||
},
|
||||
InsufficientGas { minimal, got } => {
|
||||
format!("Transaction gas is too low. There is not enough gas to cover minimal cost of the transaction (minimal: {}, got: {}). Try increasing supplied gas.", minimal, got)
|
||||
},
|
||||
InsufficientGasPrice { minimal, got } => {
|
||||
format!("Transaction gas price is too low. It does not satisfy your node's minimal gas price (minimal: {}, got: {}). Try increasing the gas price.", minimal, got)
|
||||
},
|
||||
InsufficientBalance { balance, cost } => {
|
||||
format!("Insufficient funds. Account you try to send transaction from does not have enough funds. Required {} and got: {}.", cost, balance)
|
||||
},
|
||||
GasLimitExceeded { limit, got } => {
|
||||
format!("Transaction cost exceeds current gas limit. Limit: {}, got: {}. Try decreasing supplied gas.", limit, got)
|
||||
},
|
||||
InvalidGasLimit(_) => "Supplied gas is beyond limit.".into(),
|
||||
SenderBanned => "Sender is banned in local queue.".into(),
|
||||
RecipientBanned => "Recipient is banned in local queue.".into(),
|
||||
CodeBanned => "Code is banned in local queue.".into(),
|
||||
e => format!("{}", e).into(),
|
||||
};
|
||||
Error {
|
||||
code: ErrorCode::ServerError(codes::TRANSACTION_ERROR),
|
||||
message: msg,
|
||||
message: transaction_message(e),
|
||||
data: None,
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -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> {
|
||||
try!(self.active());
|
||||
|
||||
|
||||
@@ -34,7 +34,11 @@ use ethcore::account_provider::AccountProvider;
|
||||
|
||||
use jsonrpc_core::Error;
|
||||
use v1::traits::Parity;
|
||||
use v1::types::{Bytes, U256, H160, H256, H512, Peers, Transaction, RpcSettings, Histogram};
|
||||
use v1::types::{
|
||||
Bytes, U256, H160, H256, H512,
|
||||
Peers, Transaction, RpcSettings, Histogram,
|
||||
TransactionStats, LocalTransactionStatus,
|
||||
};
|
||||
use v1::helpers::{errors, SigningQueue, SignerService, NetworkSettings};
|
||||
use v1::helpers::dispatch::DEFAULT_MAC;
|
||||
|
||||
@@ -259,6 +263,27 @@ impl<C, M, S: ?Sized> Parity for ParityClient<C, M, S> where
|
||||
Ok(take_weak!(self.miner).all_transactions().into_iter().map(Into::into).collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
fn pending_transactions_stats(&self) -> Result<BTreeMap<H256, TransactionStats>, Error> {
|
||||
try!(self.active());
|
||||
|
||||
let stats = take_weak!(self.sync).transactions_stats();
|
||||
Ok(stats.into_iter()
|
||||
.map(|(hash, stats)| (hash.into(), stats.into()))
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
|
||||
fn local_transactions(&self) -> Result<BTreeMap<H256, LocalTransactionStatus>, Error> {
|
||||
try!(self.active());
|
||||
|
||||
let transactions = take_weak!(self.miner).local_transactions();
|
||||
Ok(transactions
|
||||
.into_iter()
|
||||
.map(|(hash, status)| (hash.into(), status.into()))
|
||||
.collect()
|
||||
)
|
||||
}
|
||||
|
||||
fn signer_port(&self) -> Result<u16, Error> {
|
||||
try!(self.active());
|
||||
|
||||
|
||||
@@ -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<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)));
|
||||
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))),
|
||||
}
|
||||
|
||||
@@ -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<C: 'static, M: 'static> EthSigning for SigningUnsafeClient<C, M> where
|
||||
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)) {
|
||||
Ok(RpcConfirmationResponse::SignTransaction(rlp)) => Ok(rlp),
|
||||
Ok(RpcConfirmationResponse::SignTransaction(tx)) => Ok(tx),
|
||||
Err(e) => Err(e),
|
||||
e => Err(errors::internal("Unexpected result", e)),
|
||||
};
|
||||
|
||||
@@ -24,7 +24,7 @@ use ethcore::block::{ClosedBlock, IsBlock};
|
||||
use ethcore::header::BlockNumber;
|
||||
use ethcore::transaction::SignedTransaction;
|
||||
use ethcore::receipt::{Receipt, RichReceipt};
|
||||
use ethcore::miner::{MinerService, MinerStatus, TransactionImportResult};
|
||||
use ethcore::miner::{MinerService, MinerStatus, TransactionImportResult, LocalTransactionStatus};
|
||||
|
||||
/// Test miner service.
|
||||
pub struct TestMinerService {
|
||||
@@ -34,6 +34,8 @@ pub struct TestMinerService {
|
||||
pub latest_closed_block: Mutex<Option<ClosedBlock>>,
|
||||
/// Pre-existed pending transactions
|
||||
pub pending_transactions: Mutex<HashMap<H256, SignedTransaction>>,
|
||||
/// Pre-existed local transactions
|
||||
pub local_transactions: Mutex<BTreeMap<H256, LocalTransactionStatus>>,
|
||||
/// Pre-existed pending receipts
|
||||
pub pending_receipts: Mutex<BTreeMap<H256, Receipt>>,
|
||||
/// Last nonces.
|
||||
@@ -53,6 +55,7 @@ impl Default for TestMinerService {
|
||||
imported_transactions: Mutex::new(Vec::new()),
|
||||
latest_closed_block: Mutex::new(None),
|
||||
pending_transactions: Mutex::new(HashMap::new()),
|
||||
local_transactions: Mutex::new(BTreeMap::new()),
|
||||
pending_receipts: Mutex::new(BTreeMap::new()),
|
||||
last_nonces: RwLock::new(HashMap::new()),
|
||||
min_gas_price: RwLock::new(U256::from(20_000_000)),
|
||||
@@ -195,6 +198,10 @@ impl MinerService for TestMinerService {
|
||||
self.pending_transactions.lock().values().cloned().collect()
|
||||
}
|
||||
|
||||
fn local_transactions(&self) -> BTreeMap<H256, LocalTransactionStatus> {
|
||||
self.local_transactions.lock().iter().map(|(hash, stats)| (*hash, stats.clone())).collect()
|
||||
}
|
||||
|
||||
fn pending_transactions(&self, _best_block: BlockNumber) -> Vec<SignedTransaction> {
|
||||
self.pending_transactions.lock().values().cloned().collect()
|
||||
}
|
||||
|
||||
@@ -16,8 +16,9 @@
|
||||
|
||||
//! Test implementation of SyncProvider.
|
||||
|
||||
use util::{RwLock};
|
||||
use ethsync::{SyncProvider, SyncStatus, SyncState, PeerInfo};
|
||||
use std::collections::BTreeMap;
|
||||
use util::{H256, RwLock};
|
||||
use ethsync::{SyncProvider, SyncStatus, SyncState, PeerInfo, TransactionStats};
|
||||
|
||||
/// TestSyncProvider config.
|
||||
pub struct Config {
|
||||
@@ -74,7 +75,7 @@ impl SyncProvider for TestSyncProvider {
|
||||
PeerInfo {
|
||||
id: Some("node1".to_owned()),
|
||||
client_version: "Parity/1".to_owned(),
|
||||
capabilities: vec!["eth/62".to_owned(), "eth/63".to_owned()],
|
||||
capabilities: vec!["eth/62".to_owned(), "eth/63".to_owned()],
|
||||
remote_address: "127.0.0.1:7777".to_owned(),
|
||||
local_address: "127.0.0.1:8888".to_owned(),
|
||||
eth_version: 62,
|
||||
@@ -84,7 +85,7 @@ impl SyncProvider for TestSyncProvider {
|
||||
PeerInfo {
|
||||
id: None,
|
||||
client_version: "Parity/2".to_owned(),
|
||||
capabilities: vec!["eth/63".to_owned(), "eth/64".to_owned()],
|
||||
capabilities: vec!["eth/63".to_owned(), "eth/64".to_owned()],
|
||||
remote_address: "Handshake".to_owned(),
|
||||
local_address: "127.0.0.1:3333".to_owned(),
|
||||
eth_version: 64,
|
||||
@@ -97,5 +98,22 @@ impl SyncProvider for TestSyncProvider {
|
||||
fn enode(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
fn transactions_stats(&self) -> BTreeMap<H256, TransactionStats> {
|
||||
map![
|
||||
1.into() => TransactionStats {
|
||||
first_seen: 10,
|
||||
propagated_to: map![
|
||||
128.into() => 16
|
||||
]
|
||||
},
|
||||
5.into() => TransactionStats {
|
||||
first_seen: 16,
|
||||
propagated_to: map![
|
||||
16.into() => 1
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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<TestBlockChainClient> {
|
||||
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());
|
||||
|
||||
|
||||
@@ -18,8 +18,9 @@ use std::sync::Arc;
|
||||
use util::log::RotatingLogger;
|
||||
use util::Address;
|
||||
use ethsync::ManageNetwork;
|
||||
use ethcore::client::{TestBlockChainClient};
|
||||
use ethcore::account_provider::AccountProvider;
|
||||
use ethcore::client::{TestBlockChainClient};
|
||||
use ethcore::miner::LocalTransactionStatus;
|
||||
use ethstore::ethkey::{Generator, Random};
|
||||
|
||||
use jsonrpc_core::IoHandler;
|
||||
@@ -355,3 +356,28 @@ fn rpc_parity_next_nonce() {
|
||||
assert_eq!(io1.handle_request_sync(&request), Some(response1.to_owned()));
|
||||
assert_eq!(io2.handle_request_sync(&request), Some(response2.to_owned()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rpc_parity_transactions_stats() {
|
||||
let deps = Dependencies::new();
|
||||
let io = deps.default_client();
|
||||
|
||||
let request = r#"{"jsonrpc": "2.0", "method": "parity_pendingTransactionsStats", "params":[], "id": 1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":{"0x0000000000000000000000000000000000000000000000000000000000000001":{"firstSeen":10,"propagatedTo":{"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000080":16}},"0x0000000000000000000000000000000000000000000000000000000000000005":{"firstSeen":16,"propagatedTo":{"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010":1}}},"id":1}"#;
|
||||
|
||||
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn rpc_parity_local_transactions() {
|
||||
let deps = Dependencies::new();
|
||||
let io = deps.default_client();
|
||||
deps.miner.local_transactions.lock().insert(10.into(), LocalTransactionStatus::Pending);
|
||||
deps.miner.local_transactions.lock().insert(15.into(), LocalTransactionStatus::Future);
|
||||
|
||||
let request = r#"{"jsonrpc": "2.0", "method": "parity_localTransactions", "params":[], "id": 1}"#;
|
||||
let response = r#"{"jsonrpc":"2.0","result":{"0x000000000000000000000000000000000000000000000000000000000000000a":{"status":"pending"},"0x000000000000000000000000000000000000000000000000000000000000000f":{"status":"future"}},"id":1}"#;
|
||||
|
||||
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
}));
|
||||
|
||||
@@ -102,6 +102,10 @@ build_rpc_trait! {
|
||||
#[rpc(name = "eth_sendRawTransaction")]
|
||||
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.
|
||||
#[rpc(name = "eth_call")]
|
||||
fn call(&self, CallRequest, Trailing<BlockNumber>) -> Result<Bytes, Error>;
|
||||
|
||||
@@ -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<H256>, 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<Bytes>, TransactionRequest);
|
||||
fn sign_transaction(&self, Ready<RichRawTransaction>, TransactionRequest);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,11 @@ use jsonrpc_core::Error;
|
||||
|
||||
use std::collections::BTreeMap;
|
||||
use v1::helpers::auto_args::Wrap;
|
||||
use v1::types::{H160, H256, H512, U256, Bytes, Peers, Transaction, RpcSettings, Histogram};
|
||||
use v1::types::{
|
||||
H160, H256, H512, U256, Bytes,
|
||||
Peers, Transaction, RpcSettings, Histogram,
|
||||
TransactionStats, LocalTransactionStatus,
|
||||
};
|
||||
|
||||
build_rpc_trait! {
|
||||
/// Parity-specific rpc interface.
|
||||
@@ -115,6 +119,14 @@ build_rpc_trait! {
|
||||
#[rpc(name = "parity_pendingTransactions")]
|
||||
fn pending_transactions(&self) -> Result<Vec<Transaction>, Error>;
|
||||
|
||||
/// Returns propagation statistics on transactions pending in the queue.
|
||||
#[rpc(name = "parity_pendingTransactionsStats")]
|
||||
fn pending_transactions_stats(&self) -> Result<BTreeMap<H256, TransactionStats>, Error>;
|
||||
|
||||
/// Returns a list of current and past local transactions with status details.
|
||||
#[rpc(name = "parity_localTransactions")]
|
||||
fn local_transactions(&self) -> Result<BTreeMap<H256, LocalTransactionStatus>, Error>;
|
||||
|
||||
/// Returns current Trusted Signer port or an error if signer is disabled.
|
||||
#[rpc(name = "parity_signerPort")]
|
||||
fn signer_port(&self) -> Result<u16, Error>;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -43,8 +43,8 @@ pub use self::filter::{Filter, FilterChanges};
|
||||
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::sync::{SyncStatus, SyncInfo, Peers, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo, PeerEthereumProtocolInfo, TransactionStats};
|
||||
pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionStatus};
|
||||
pub use self::transaction_request::TransactionRequest;
|
||||
pub use self::receipt::Receipt;
|
||||
pub use self::rpc_settings::RpcSettings;
|
||||
|
||||
@@ -14,9 +14,10 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use ethsync::PeerInfo as SyncPeerInfo;
|
||||
use std::collections::BTreeMap;
|
||||
use ethsync::{PeerInfo as SyncPeerInfo, TransactionStats as SyncTransactionStats};
|
||||
use serde::{Serialize, Serializer};
|
||||
use v1::types::U256;
|
||||
use v1::types::{U256, H512};
|
||||
|
||||
/// Sync info
|
||||
#[derive(Default, Debug, Serialize, PartialEq)]
|
||||
@@ -117,8 +118,19 @@ impl Serialize for SyncStatus {
|
||||
}
|
||||
}
|
||||
|
||||
/// Propagation statistics for pending transaction.
|
||||
#[derive(Default, Debug, Serialize)]
|
||||
pub struct TransactionStats {
|
||||
/// Block no this transaction was first seen.
|
||||
#[serde(rename="firstSeen")]
|
||||
pub first_seen: u64,
|
||||
/// Peers this transaction was propagated to with count.
|
||||
#[serde(rename="propagatedTo")]
|
||||
pub propagated_to: BTreeMap<H512, usize>,
|
||||
}
|
||||
|
||||
impl From<SyncPeerInfo> for PeerInfo {
|
||||
fn from(p: SyncPeerInfo) -> PeerInfo {
|
||||
fn from(p: SyncPeerInfo) -> Self {
|
||||
PeerInfo {
|
||||
id: p.id,
|
||||
name: p.client_version,
|
||||
@@ -138,10 +150,23 @@ impl From<SyncPeerInfo> for PeerInfo {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<SyncTransactionStats> for TransactionStats {
|
||||
fn from(s: SyncTransactionStats) -> Self {
|
||||
TransactionStats {
|
||||
first_seen: s.first_seen,
|
||||
propagated_to: s.propagated_to
|
||||
.into_iter()
|
||||
.map(|(id, count)| (id.into(), count))
|
||||
.collect()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use serde_json;
|
||||
use super::{SyncInfo, SyncStatus, Peers};
|
||||
use std::collections::BTreeMap;
|
||||
use super::{SyncInfo, SyncStatus, Peers, TransactionStats};
|
||||
|
||||
#[test]
|
||||
fn test_serialize_sync_info() {
|
||||
@@ -176,4 +201,17 @@ mod tests {
|
||||
let serialized = serde_json::to_string(&t).unwrap();
|
||||
assert_eq!(serialized, r#"{"startingBlock":"0x0","currentBlock":"0x0","highestBlock":"0x0","warpChunksAmount":null,"warpChunksProcessed":null,"blockGap":["0x1","0x5"]}"#)
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_serialize_transaction_stats() {
|
||||
let stats = TransactionStats {
|
||||
first_seen: 100,
|
||||
propagated_to: map![
|
||||
10.into() => 50
|
||||
]
|
||||
};
|
||||
|
||||
let serialized = serde_json::to_string(&stats).unwrap();
|
||||
assert_eq!(serialized, r#"{"firstSeen":100,"propagatedTo":{"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000a":50}}"#)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,12 +14,15 @@
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
use serde::{Serialize, Serializer};
|
||||
use ethcore::miner;
|
||||
use ethcore::contract_address;
|
||||
use ethcore::transaction::{LocalizedTransaction, Action, SignedTransaction};
|
||||
use v1::helpers::errors;
|
||||
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 +65,93 @@ pub struct Transaction {
|
||||
pub s: H256,
|
||||
}
|
||||
|
||||
/// Local Transaction Status
|
||||
#[derive(Debug)]
|
||||
pub enum LocalTransactionStatus {
|
||||
/// Transaction is pending
|
||||
Pending,
|
||||
/// Transaction is in future part of the queue
|
||||
Future,
|
||||
/// Transaction is already mined.
|
||||
Mined(Transaction),
|
||||
/// Transaction was dropped because of limit.
|
||||
Dropped(Transaction),
|
||||
/// Transaction was replaced by transaction with higher gas price.
|
||||
Replaced(Transaction, U256, H256),
|
||||
/// Transaction never got into the queue.
|
||||
Rejected(Transaction, String),
|
||||
/// Transaction is invalid.
|
||||
Invalid(Transaction),
|
||||
}
|
||||
|
||||
impl Serialize for LocalTransactionStatus {
|
||||
fn serialize<S>(&self, serializer: &mut S) -> Result<(), S::Error>
|
||||
where S: Serializer
|
||||
{
|
||||
use self::LocalTransactionStatus::*;
|
||||
|
||||
let elems = match *self {
|
||||
Pending | Future => 1,
|
||||
Mined(..) | Dropped(..) | Invalid(..) => 2,
|
||||
Rejected(..) => 3,
|
||||
Replaced(..) => 4,
|
||||
};
|
||||
|
||||
let status = "status";
|
||||
let transaction = "transaction";
|
||||
|
||||
let mut state = try!(serializer.serialize_struct("LocalTransactionStatus", elems));
|
||||
match *self {
|
||||
Pending => try!(serializer.serialize_struct_elt(&mut state, status, "pending")),
|
||||
Future => try!(serializer.serialize_struct_elt(&mut state, status, "future")),
|
||||
Mined(ref tx) => {
|
||||
try!(serializer.serialize_struct_elt(&mut state, status, "mined"));
|
||||
try!(serializer.serialize_struct_elt(&mut state, transaction, tx));
|
||||
},
|
||||
Dropped(ref tx) => {
|
||||
try!(serializer.serialize_struct_elt(&mut state, status, "dropped"));
|
||||
try!(serializer.serialize_struct_elt(&mut state, transaction, tx));
|
||||
},
|
||||
Invalid(ref tx) => {
|
||||
try!(serializer.serialize_struct_elt(&mut state, status, "invalid"));
|
||||
try!(serializer.serialize_struct_elt(&mut state, transaction, tx));
|
||||
},
|
||||
Rejected(ref tx, ref reason) => {
|
||||
try!(serializer.serialize_struct_elt(&mut state, status, "rejected"));
|
||||
try!(serializer.serialize_struct_elt(&mut state, transaction, tx));
|
||||
try!(serializer.serialize_struct_elt(&mut state, "error", reason));
|
||||
},
|
||||
Replaced(ref tx, ref gas_price, ref hash) => {
|
||||
try!(serializer.serialize_struct_elt(&mut state, status, "replaced"));
|
||||
try!(serializer.serialize_struct_elt(&mut state, transaction, tx));
|
||||
try!(serializer.serialize_struct_elt(&mut state, "hash", hash));
|
||||
try!(serializer.serialize_struct_elt(&mut state, "gasPrice", gas_price));
|
||||
},
|
||||
}
|
||||
serializer.serialize_struct_end(state)
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 {
|
||||
fn from(t: LocalizedTransaction) -> Transaction {
|
||||
let signature = t.signature();
|
||||
@@ -124,9 +214,24 @@ impl From<SignedTransaction> for Transaction {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<miner::LocalTransactionStatus> for LocalTransactionStatus {
|
||||
fn from(s: miner::LocalTransactionStatus) -> Self {
|
||||
use ethcore::miner::LocalTransactionStatus::*;
|
||||
match s {
|
||||
Pending => LocalTransactionStatus::Pending,
|
||||
Future => LocalTransactionStatus::Future,
|
||||
Mined(tx) => LocalTransactionStatus::Mined(tx.into()),
|
||||
Dropped(tx) => LocalTransactionStatus::Dropped(tx.into()),
|
||||
Rejected(tx, err) => LocalTransactionStatus::Rejected(tx.into(), errors::transaction_message(err)),
|
||||
Replaced(tx, gas_price, hash) => LocalTransactionStatus::Replaced(tx.into(), gas_price.into(), hash.into()),
|
||||
Invalid(tx) => LocalTransactionStatus::Invalid(tx.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::Transaction;
|
||||
use super::{Transaction, LocalTransactionStatus};
|
||||
use serde_json;
|
||||
|
||||
#[test]
|
||||
@@ -135,5 +240,50 @@ mod tests {
|
||||
let serialized = serde_json::to_string(&t).unwrap();
|
||||
assert_eq!(serialized, r#"{"hash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0","blockHash":null,"blockNumber":null,"transactionIndex":null,"from":"0x0000000000000000000000000000000000000000","to":null,"value":"0x0","gasPrice":"0x0","gas":"0x0","input":"0x","creates":null,"raw":"0x","publicKey":null,"v":0,"r":"0x0000000000000000000000000000000000000000000000000000000000000000","s":"0x0000000000000000000000000000000000000000000000000000000000000000"}"#);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_local_transaction_status_serialize() {
|
||||
let tx_ser = serde_json::to_string(&Transaction::default()).unwrap();
|
||||
let status1 = LocalTransactionStatus::Pending;
|
||||
let status2 = LocalTransactionStatus::Future;
|
||||
let status3 = LocalTransactionStatus::Mined(Transaction::default());
|
||||
let status4 = LocalTransactionStatus::Dropped(Transaction::default());
|
||||
let status5 = LocalTransactionStatus::Invalid(Transaction::default());
|
||||
let status6 = LocalTransactionStatus::Rejected(Transaction::default(), "Just because".into());
|
||||
let status7 = LocalTransactionStatus::Replaced(Transaction::default(), 5.into(), 10.into());
|
||||
|
||||
assert_eq!(
|
||||
serde_json::to_string(&status1).unwrap(),
|
||||
r#"{"status":"pending"}"#
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&status2).unwrap(),
|
||||
r#"{"status":"future"}"#
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&status3).unwrap(),
|
||||
r#"{"status":"mined","transaction":"#.to_owned() + &format!("{}", tx_ser) + r#"}"#
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&status4).unwrap(),
|
||||
r#"{"status":"dropped","transaction":"#.to_owned() + &format!("{}", tx_ser) + r#"}"#
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&status5).unwrap(),
|
||||
r#"{"status":"invalid","transaction":"#.to_owned() + &format!("{}", tx_ser) + r#"}"#
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&status6).unwrap(),
|
||||
r#"{"status":"rejected","transaction":"#.to_owned() +
|
||||
&format!("{}", tx_ser) +
|
||||
r#","error":"Just because"}"#
|
||||
);
|
||||
assert_eq!(
|
||||
serde_json::to_string(&status7).unwrap(),
|
||||
r#"{"status":"replaced","transaction":"#.to_owned() +
|
||||
&format!("{}", tx_ser) +
|
||||
r#","hash":"0x000000000000000000000000000000000000000000000000000000000000000a","gasPrice":"0x5"}"#
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user