Big folder refactor

This commit is contained in:
Adria Massanet
2021-01-13 17:03:12 +00:00
committed by rakita
parent 0e5d6944b7
commit c46fe330dc
846 changed files with 255 additions and 39400 deletions

View File

@@ -0,0 +1,37 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use ethcore::client::TestBlockChainClient;
use jsonrpc_core::IoHandler;
use v1::{Debug, DebugClient};
fn io() -> IoHandler {
let client = Arc::new(TestBlockChainClient::new());
let mut io = IoHandler::new();
io.extend_with(DebugClient::new(client).to_delegate());
io
}
#[test]
fn rpc_debug_get_bad_blocks() {
let request = r#"{"jsonrpc": "2.0", "method": "debug_getBadBlocks", "params": [], "id": 1}"#;
let response = "{\"jsonrpc\":\"2.0\",\"result\":[{\"author\":\"0x0000000000000000000000000000000000000000\",\"difficulty\":\"0x0\",\"extraData\":\"0x\",\"gasLimit\":\"0x0\",\"gasUsed\":\"0x0\",\"hash\":\"0x27bfb37e507ce90da141307204b1c6ba24194380613590ac50ca4b1d7198ff65\",\"logsBloom\":\"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\",\"miner\":\"0x0000000000000000000000000000000000000000\",\"number\":\"0x0\",\"parentHash\":\"0x0000000000000000000000000000000000000000000000000000000000000000\",\"reason\":\"Invalid block\",\"receiptsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"rlp\":\"\\\"0x010203\\\"\",\"sealFields\":[],\"sha3Uncles\":\"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347\",\"size\":\"0x3\",\"stateRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"timestamp\":\"0x0\",\"totalDifficulty\":null,\"transactions\":[],\"transactionsRoot\":\"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421\",\"uncles\":[]}],\"id\":1}";
assert_eq!(io().handle_request_sync(request), Some(response.to_owned()));
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,288 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use jsonrpc_core::{
futures::{self, Future, Stream},
MetaIoHandler,
};
use jsonrpc_pubsub::Session;
use std::time::Duration;
use v1::{EthPubSub, EthPubSubClient, Metadata};
use ethcore::client::{
ChainNotify, ChainRoute, ChainRouteType, EachBlockWith, NewBlocks, TestBlockChainClient,
};
use parity_runtime::Runtime;
const DURATION_ZERO: Duration = Duration::from_millis(0);
#[test]
fn should_subscribe_to_new_heads() {
// given
let el = Runtime::with_thread_count(1);
let mut client = TestBlockChainClient::new();
// Insert some blocks
client.add_blocks(3, EachBlockWith::Nothing);
let h3 = client.block_hash_delta_minus(1);
let h2 = client.block_hash_delta_minus(2);
let h1 = client.block_hash_delta_minus(3);
let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor());
let handler = pubsub.handler().upgrade().unwrap();
let pubsub = pubsub.to_delegate();
let mut io = MetaIoHandler::default();
io.extend_with(pubsub);
let mut metadata = Metadata::default();
let (sender, receiver) = futures::sync::mpsc::channel(8);
metadata.session = Some(Arc::new(Session::new(sender)));
// Subscribe
let request =
r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["newHeads"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#;
assert_eq!(
io.handle_request_sync(request, metadata.clone()),
Some(response.to_owned())
);
// Check notifications
handler.new_blocks(NewBlocks::new(
vec![],
vec![],
ChainRoute::new(vec![(h1, ChainRouteType::Enacted)]),
vec![],
vec![],
DURATION_ZERO,
true,
));
let (res, receiver) = receiver.into_future().wait().unwrap();
let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x","gasLimit":"0xf4240","gasUsed":"0x0","hash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x1","parentHash":"0x0cd786a2425d16f152c658316c423e6ce1181e15c3295826d7c9904cba9ce303","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x1c9","stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","timestamp":"0x0","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"},"subscription":"0x416d77337e24399d"}}"#;
assert_eq!(res, Some(response.into()));
// Notify about two blocks
handler.new_blocks(NewBlocks::new(
vec![],
vec![],
ChainRoute::new(vec![
(h2, ChainRouteType::Enacted),
(h3, ChainRouteType::Enacted),
]),
vec![],
vec![],
DURATION_ZERO,
true,
));
// Receive both
let (res, receiver) = receiver.into_future().wait().unwrap();
let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x2","extraData":"0x","gasLimit":"0xf4240","gasUsed":"0x0","hash":"0x44e5ecf454ea99af9d8a8f2ca0daba96964c90de05db7a78f59b84ae9e749706","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x2","parentHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x1c9","stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","timestamp":"0x0","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"},"subscription":"0x416d77337e24399d"}}"#;
assert_eq!(res, Some(response.into()));
let (res, receiver) = receiver.into_future().wait().unwrap();
let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x3","extraData":"0x","gasLimit":"0xf4240","gasUsed":"0x0","hash":"0xdf04a98bb0c6fa8441bd429822f65a46d0cb553f6bcef602b973e65c81497f8e","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","number":"0x3","parentHash":"0x44e5ecf454ea99af9d8a8f2ca0daba96964c90de05db7a78f59b84ae9e749706","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sealFields":[],"sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","size":"0x1c9","stateRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","timestamp":"0x0","transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421"},"subscription":"0x416d77337e24399d"}}"#;
assert_eq!(res, Some(response.into()));
// And unsubscribe
let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(
io.handle_request_sync(request, metadata),
Some(response.to_owned())
);
let (res, _receiver) = receiver.into_future().wait().unwrap();
assert_eq!(res, None);
}
#[test]
fn should_subscribe_to_logs() {
use ethcore::client::BlockInfo;
use types::{
ids::BlockId,
log_entry::{LocalizedLogEntry, LogEntry},
};
// given
let el = Runtime::with_thread_count(1);
let mut client = TestBlockChainClient::new();
// Insert some blocks
client.add_blocks(1, EachBlockWith::Transaction);
let h1 = client.block_hash_delta_minus(1);
let block = client.block(BlockId::Hash(h1)).unwrap();
let tx_hash = block.transactions()[0].hash();
client.set_logs(vec![LocalizedLogEntry {
entry: LogEntry {
address: 5.into(),
topics: vec![1.into(), 2.into(), 0.into(), 0.into()],
data: vec![],
},
block_hash: h1,
block_number: block.header().number(),
transaction_hash: tx_hash,
transaction_index: 0,
log_index: 0,
transaction_log_index: 0,
}]);
let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor());
let handler = pubsub.handler().upgrade().unwrap();
let pubsub = pubsub.to_delegate();
let mut io = MetaIoHandler::default();
io.extend_with(pubsub);
let mut metadata = Metadata::default();
let (sender, receiver) = futures::sync::mpsc::channel(8);
metadata.session = Some(Arc::new(Session::new(sender)));
// Subscribe
let request =
r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["logs", {}], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#;
assert_eq!(
io.handle_request_sync(request, metadata.clone()),
Some(response.to_owned())
);
// Check notifications (enacted)
handler.new_blocks(NewBlocks::new(
vec![],
vec![],
ChainRoute::new(vec![(h1, ChainRouteType::Enacted)]),
vec![],
vec![],
DURATION_ZERO,
false,
));
let (res, receiver) = receiver.into_future().wait().unwrap();
let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"address":"0x0000000000000000000000000000000000000005","blockHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","blockNumber":"0x1","data":"0x","logIndex":"0x0","removed":false,"topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned()
+ &format!("0x{:x}", tx_hash)
+ r#"","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"mined"},"subscription":"0x416d77337e24399d"}}"#;
assert_eq!(res, Some(response.into()));
// Check notifications (retracted)
handler.new_blocks(NewBlocks::new(
vec![],
vec![],
ChainRoute::new(vec![(h1, ChainRouteType::Retracted)]),
vec![],
vec![],
DURATION_ZERO,
false,
));
let (res, receiver) = receiver.into_future().wait().unwrap();
let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":{"address":"0x0000000000000000000000000000000000000005","blockHash":"0x3457d2fa2e3dd33c78ac681cf542e429becf718859053448748383af67e23218","blockNumber":"0x1","data":"0x","logIndex":"0x0","removed":true,"topics":["0x0000000000000000000000000000000000000000000000000000000000000001","0x0000000000000000000000000000000000000000000000000000000000000002","0x0000000000000000000000000000000000000000000000000000000000000000","0x0000000000000000000000000000000000000000000000000000000000000000"],"transactionHash":""#.to_owned()
+ &format!("0x{:x}", tx_hash)
+ r#"","transactionIndex":"0x0","transactionLogIndex":"0x0","type":"removed"},"subscription":"0x416d77337e24399d"}}"#;
assert_eq!(res, Some(response.into()));
// And unsubscribe
let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(
io.handle_request_sync(request, metadata),
Some(response.to_owned())
);
let (res, _receiver) = receiver.into_future().wait().unwrap();
assert_eq!(res, None);
}
#[test]
fn should_subscribe_to_pending_transactions() {
// given
let el = Runtime::with_thread_count(1);
let client = TestBlockChainClient::new();
let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor());
let handler = pubsub.handler().upgrade().unwrap();
let pubsub = pubsub.to_delegate();
let mut io = MetaIoHandler::default();
io.extend_with(pubsub);
let mut metadata = Metadata::default();
let (sender, receiver) = futures::sync::mpsc::channel(8);
metadata.session = Some(Arc::new(Session::new(sender)));
// Fail if params are provided
let request = r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["newPendingTransactions", {}], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Couldn't parse parameters: newPendingTransactions","data":"\"Expected no parameters.\""},"id":1}"#;
assert_eq!(
io.handle_request_sync(request, metadata.clone()),
Some(response.to_owned())
);
// Subscribe
let request = r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["newPendingTransactions"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#;
assert_eq!(
io.handle_request_sync(request, metadata.clone()),
Some(response.to_owned())
);
// Send new transactions
handler.notify_new_transactions(&[5.into(), 7.into()]);
let (res, receiver) = receiver.into_future().wait().unwrap();
let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":"0x0000000000000000000000000000000000000000000000000000000000000005","subscription":"0x416d77337e24399d"}}"#;
assert_eq!(res, Some(response.into()));
let (res, receiver) = receiver.into_future().wait().unwrap();
let response = r#"{"jsonrpc":"2.0","method":"eth_subscription","params":{"result":"0x0000000000000000000000000000000000000000000000000000000000000007","subscription":"0x416d77337e24399d"}}"#;
assert_eq!(res, Some(response.into()));
// And unsubscribe
let request = r#"{"jsonrpc": "2.0", "method": "eth_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(
io.handle_request_sync(request, metadata),
Some(response.to_owned())
);
let (res, _receiver) = receiver.into_future().wait().unwrap();
assert_eq!(res, None);
}
#[test]
fn should_return_unimplemented() {
// given
let el = Runtime::with_thread_count(1);
let client = TestBlockChainClient::new();
let pubsub = EthPubSubClient::new_test(Arc::new(client), el.executor());
let pubsub = pubsub.to_delegate();
let mut io = MetaIoHandler::default();
io.extend_with(pubsub);
let mut metadata = Metadata::default();
let (sender, _receiver) = futures::sync::mpsc::channel(8);
metadata.session = Some(Arc::new(Session::new(sender)));
// Subscribe
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not implemented yet. Please create an issue on Github repo."},"id":1}"#;
let request =
r#"{"jsonrpc": "2.0", "method": "eth_subscribe", "params": ["syncing"], "id": 1}"#;
assert_eq!(
io.handle_request_sync(request, metadata.clone()),
Some(response.to_owned())
);
}

View File

@@ -0,0 +1,41 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use self::ethcore_network::{NetworkContext, ProtocolId};
use std::ops::RangeInclusive;
use sync::ManageNetwork;
extern crate ethcore_network;
pub struct TestManageNetwork;
// TODO: rob, gavin (originally introduced this functions) - proper tests and test state
impl ManageNetwork for TestManageNetwork {
fn accept_unreserved_peers(&self) {}
fn deny_unreserved_peers(&self) {}
fn remove_reserved_peer(&self, _peer: String) -> Result<(), String> {
Ok(())
}
fn add_reserved_peer(&self, _peer: String) -> Result<(), String> {
Ok(())
}
fn start_network(&self) {}
fn stop_network(&self) {}
fn num_peers_range(&self) -> RangeInclusive<u32> {
25..=50
}
fn with_proto_context(&self, _: ProtocolId, _: &mut dyn FnMut(&dyn NetworkContext)) {}
}

View File

@@ -0,0 +1,40 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
//! RPC mocked tests. Most of these test that the RPC server is serializing and forwarding
//! method calls properly.
mod debug;
mod eth;
mod eth_pubsub;
mod manage_network;
mod net;
mod parity;
#[cfg(any(test, feature = "accounts"))]
mod parity_accounts;
mod parity_set;
#[cfg(any(test, feature = "accounts"))]
mod personal;
mod pubsub;
#[cfg(any(test, feature = "accounts"))]
mod secretstore;
mod signer;
#[cfg(any(test, feature = "accounts"))]
mod signing;
#[cfg(any(test, feature = "accounts"))]
mod signing_unsafe;
mod traces;
mod web3;

View File

@@ -0,0 +1,68 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use jsonrpc_core::IoHandler;
use std::sync::Arc;
use v1::{
tests::helpers::{Config, TestSyncProvider},
Net, NetClient,
};
fn sync_provider() -> Arc<TestSyncProvider> {
Arc::new(TestSyncProvider::new(Config {
network_id: 3,
num_peers: 120,
}))
}
#[test]
fn rpc_net_version() {
let sync = sync_provider();
let net = NetClient::new(&sync).to_delegate();
let mut io = IoHandler::new();
io.extend_with(net);
let request = r#"{"jsonrpc": "2.0", "method": "net_version", "params": [], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"3","id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_net_peer_count() {
let sync = sync_provider();
let net = NetClient::new(&sync).to_delegate();
let mut io = IoHandler::new();
io.extend_with(net);
let request = r#"{"jsonrpc": "2.0", "method": "net_peerCount", "params": [], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x78","id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_net_listening() {
let sync = sync_provider();
let net = NetClient::new(&sync).to_delegate();
let mut io = IoHandler::new();
io.extend_with(net);
let request = r#"{"jsonrpc": "2.0", "method": "net_listening", "params": [], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}

View File

@@ -0,0 +1,571 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use ethcore::client::{Executed, TestBlockChainClient, TransactionId};
use ethcore_logger::RotatingLogger;
use ethereum_types::{Address, H256, U256};
use ethstore::ethkey::{Generator, Random};
use miner::pool::local_transactions::Status as LocalTransactionStatus;
use std::sync::Arc;
use sync::ManageNetwork;
use types::{
receipt::{LocalizedReceipt, TransactionOutcome},
transaction::TypedTxId,
};
use super::manage_network::TestManageNetwork;
use jsonrpc_core::IoHandler;
use v1::{
helpers::{external_signer::SignerService, NetworkSettings},
metadata::Metadata,
tests::helpers::{Config, TestMinerService, TestSyncProvider},
Parity, ParityClient,
};
use Host;
pub type TestParityClient = ParityClient<TestBlockChainClient, TestMinerService>;
pub struct Dependencies {
pub miner: Arc<TestMinerService>,
pub client: Arc<TestBlockChainClient>,
pub sync: Arc<TestSyncProvider>,
pub logger: Arc<RotatingLogger>,
pub settings: Arc<NetworkSettings>,
pub network: Arc<dyn ManageNetwork>,
pub ws_address: Option<Host>,
}
impl Dependencies {
pub fn new() -> Self {
Dependencies {
miner: Arc::new(TestMinerService::default()),
client: Arc::new(TestBlockChainClient::default()),
sync: Arc::new(TestSyncProvider::new(Config {
network_id: 3,
num_peers: 120,
})),
logger: Arc::new(RotatingLogger::new("rpc=trace".to_owned())),
settings: Arc::new(NetworkSettings {
name: "mynode".to_owned(),
chain: "testchain".to_owned(),
is_dev_chain: false,
network_port: 30303,
rpc_enabled: true,
rpc_interface: "all".to_owned(),
rpc_port: 8545,
}),
network: Arc::new(TestManageNetwork),
ws_address: Some("127.0.0.1:18546".into()),
}
}
pub fn client(&self, signer: Option<Arc<SignerService>>) -> TestParityClient {
ParityClient::new(
self.client.clone(),
self.miner.clone(),
self.sync.clone(),
self.network.clone(),
self.logger.clone(),
self.settings.clone(),
signer,
self.ws_address.clone(),
None,
)
}
fn default_client(&self) -> IoHandler<Metadata> {
let mut io = IoHandler::default();
io.extend_with(self.client(None).to_delegate());
io
}
fn with_signer(&self, signer: SignerService) -> IoHandler<Metadata> {
let mut io = IoHandler::default();
io.extend_with(self.client(Some(Arc::new(signer))).to_delegate());
io
}
}
#[test]
fn rpc_parity_extra_data() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_extraData", "params": [], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x01020304","id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_default_extra_data() {
use bytes::ToPretty;
use version::version_data;
let deps = Dependencies::new();
let io = deps.default_client();
let request =
r#"{"jsonrpc": "2.0", "method": "parity_defaultExtraData", "params": [], "id": 1}"#;
let response = format!(
r#"{{"jsonrpc":"2.0","result":"0x{}","id":1}}"#,
version_data().to_hex()
);
assert_eq!(io.handle_request_sync(request), Some(response));
}
#[test]
fn rpc_parity_gas_floor_target() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_gasFloorTarget", "params": [], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x3039","id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_min_gas_price() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_minGasPrice", "params": [], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x1312d00","id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_dev_logs() {
let deps = Dependencies::new();
deps.logger.append("a".to_owned());
deps.logger.append("b".to_owned());
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_devLogs", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":["b","a"],"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_dev_logs_levels() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_devLogsLevels", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"rpc=trace","id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_transactions_limit() {
let deps = Dependencies::new();
let io = deps.default_client();
let request =
r#"{"jsonrpc": "2.0", "method": "parity_transactionsLimit", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":1024,"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_net_chain() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_netChain", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"testchain","id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_chain() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_chain", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"foundation","id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_net_peers() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_netPeers", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"active":0,"connected":120,"max":50,"peers":[{"caps":["eth/63","eth/64"],"id":"node1","name":{"ParityClient":{"can_handle_large_requests":true,"compiler":"rustc","identity":"1","name":"Parity-Ethereum","os":"linux","semver":"2.4.0"}},"network":{"localAddress":"127.0.0.1:8888","remoteAddress":"127.0.0.1:7777"},"protocols":{"eth":{"difficulty":"0x28","head":"0000000000000000000000000000000000000000000000000000000000000032","version":63}}},{"caps":["eth/64","eth/65"],"id":null,"name":{"Other":"Open-Ethereum/2/v2.4.0/linux/rustc"},"network":{"localAddress":"127.0.0.1:3333","remoteAddress":"Handshake"},"protocols":{"eth":{"difficulty":null,"head":"000000000000000000000000000000000000000000000000000000000000003c","version":65}}}]},"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_net_port() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_netPort", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":30303,"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_rpc_settings() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_rpcSettings", "params":[], "id": 1}"#;
let response =
r#"{"jsonrpc":"2.0","result":{"enabled":true,"interface":"all","port":8545},"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_node_name() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_nodeName", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"mynode","id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_unsigned_transactions_count() {
let deps = Dependencies::new();
let io = deps.with_signer(SignerService::new_test(true));
let request =
r#"{"jsonrpc": "2.0", "method": "parity_unsignedTransactionsCount", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":0,"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_unsigned_transactions_count_when_signer_disabled() {
let deps = Dependencies::new();
let io = deps.default_client();
let request =
r#"{"jsonrpc": "2.0", "method": "parity_unsignedTransactionsCount", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Trusted Signer is disabled. This API is not available."},"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_pending_transactions() {
let deps = Dependencies::new();
let io = deps.default_client();
let request =
r#"{"jsonrpc": "2.0", "method": "parity_pendingTransactions", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":[],"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_encrypt() {
let deps = Dependencies::new();
let io = deps.default_client();
let key = format!("{:x}", Random.generate().unwrap().public());
let request = r#"{"jsonrpc": "2.0", "method": "parity_encryptMessage", "params":["0x"#
.to_owned()
+ &key
+ r#"", "0x01"], "id": 1}"#;
assert!(
io.handle_request_sync(&request).unwrap().contains("result"),
"Should return success."
);
}
#[test]
fn rpc_parity_ws_address() {
// given
let mut deps = Dependencies::new();
let io1 = deps.default_client();
deps.ws_address = None;
let io2 = deps.default_client();
// when
let request = r#"{"jsonrpc": "2.0", "method": "parity_wsUrl", "params": [], "id": 1}"#;
let response1 = r#"{"jsonrpc":"2.0","result":"127.0.0.1:18546","id":1}"#;
let response2 = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"WebSockets Server is disabled. This API is not available."},"id":1}"#;
// then
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_next_nonce() {
let deps = Dependencies::new();
let address = Address::default();
let io1 = deps.default_client();
let deps = Dependencies::new();
deps.miner.increment_nonce(&address);
deps.miner.increment_nonce(&address);
deps.miner.increment_nonce(&address);
let io2 = deps.default_client();
let request = r#"{
"jsonrpc": "2.0",
"method": "parity_nextNonce",
"params": [""#
.to_owned()
+ &format!("0x{:x}", address)
+ r#""],
"id": 1
}"#;
let response1 = r#"{"jsonrpc":"2.0","result":"0x0","id":1}"#;
let response2 = r#"{"jsonrpc":"2.0","result":"0x3","id":1}"#;
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() {
use types::transaction::{Transaction, TypedTransaction};
let deps = Dependencies::new();
let io = deps.default_client();
let tx = TypedTransaction::Legacy(Transaction {
value: 5.into(),
gas: 3.into(),
gas_price: 2.into(),
action: ::types::transaction::Action::Create,
data: vec![1, 2, 3],
nonce: 0.into(),
})
.fake_sign(3.into());
let tx = Arc::new(::miner::pool::VerifiedTransaction::from_pending_block_transaction(tx));
deps.miner
.local_transactions
.lock()
.insert(10.into(), LocalTransactionStatus::Pending(tx.clone()));
deps.miner
.local_transactions
.lock()
.insert(15.into(), LocalTransactionStatus::Pending(tx.clone()));
let request =
r#"{"jsonrpc": "2.0", "method": "parity_localTransactions", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"0x000000000000000000000000000000000000000000000000000000000000000a":{"status":"pending"},"0x000000000000000000000000000000000000000000000000000000000000000f":{"status":"pending"}},"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_chain_status() {
let deps = Dependencies::new();
let io = deps.default_client();
*deps.client.ancient_block.write() = Some((H256::default(), 5));
*deps.client.first_block.write() = Some((H256::from(U256::from(1234)), 3333));
let request = r#"{"jsonrpc": "2.0", "method": "parity_chainStatus", "params":[], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockGap":["0x6","0xd05"]},"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_node_kind() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{"jsonrpc": "2.0", "method": "parity_nodeKind", "params":[], "id": 1}"#;
let response =
r#"{"jsonrpc":"2.0","result":{"availability":"personal","capability":"full"},"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_call() {
let deps = Dependencies::new();
deps.client.set_execution_result(Ok(Executed {
exception: None,
gas: U256::zero(),
gas_used: U256::from(0xff30),
refunded: U256::from(0x5),
cumulative_gas_used: U256::zero(),
logs: vec![],
contracts_created: vec![],
output: vec![0x12, 0x34, 0xff],
trace: vec![],
vm_trace: None,
state_diff: None,
}));
let io = deps.default_client();
let request = r#"{
"jsonrpc": "2.0",
"method": "parity_call",
"params": [[{
"from": "0xb60e8dd61c5d32be8058bb8eb970870f07233155",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a",
"data": "0xd46e8dd67c5d32be8d46e8dd67c5d32be8058bb8eb970870f072445675058bb8eb970870f072445675"
}],
"latest"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":["0x1234ff"],"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_block_receipts() {
let deps = Dependencies::new();
deps.client.receipts.write().insert(
TransactionId::Hash(1.into()),
LocalizedReceipt {
transaction_hash: 1.into(),
transaction_type: TypedTxId::Legacy,
transaction_index: 0,
block_hash: 3.into(),
block_number: 0,
cumulative_gas_used: 21_000.into(),
gas_used: 21_000.into(),
contract_address: None,
logs: vec![],
log_bloom: 1.into(),
outcome: TransactionOutcome::Unknown,
to: None,
from: 9.into(),
},
);
let io = deps.default_client();
let request = r#"{
"jsonrpc": "2.0",
"method": "parity_getBlockReceipts",
"params": [],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":[{"blockHash":"0x0000000000000000000000000000000000000000000000000000000000000003","blockNumber":"0x0","contractAddress":null,"cumulativeGasUsed":"0x5208","from":"0x0000000000000000000000000000000000000009","gasUsed":"0x5208","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","to":null,"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000001","transactionIndex":"0x0"}],"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_status_ok() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{
"jsonrpc": "2.0",
"method": "parity_nodeStatus",
"params": [],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_status_error_peers() {
let deps = Dependencies::new();
deps.sync.status.write().num_peers = 0;
let io = deps.default_client();
let request = r#"{
"jsonrpc": "2.0",
"method": "parity_nodeStatus",
"params": [],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32066,"message":"Node is not connected to any peers."},"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_status_error_sync() {
let deps = Dependencies::new();
deps.sync.status.write().state = ::sync::SyncState::Blocks;
let io = deps.default_client();
let request = r#"{
"jsonrpc": "2.0",
"method": "parity_nodeStatus",
"params": [],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32001,"message":"Still syncing."},"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_verify_signature() {
let deps = Dependencies::new();
let io = deps.default_client();
let request = r#"{
"jsonrpc": "2.0",
"method": "parity_verifySignature",
"params": [
false,
"0xe552acf4caabe9661893fd48c7b5e68af20bf007193442f8ca05ce836699d75e",
"0x2089e84151c3cdc45255c07557b349f5bf2ed3e68f6098723eaa90a0f8b2b3e5",
"0x5f70e8df7bd0c4417afb5f5a39d82e15d03adeff8796725d8b14889ed1d1aa8a",
"0x1"
],
"id": 0
}"#;
let response = r#"{"jsonrpc":"2.0","result":{"address":"0x9a2a08a1170f51208c2f3cede0d29ada94481eed","isValidForCurrentChain":true,"publicKey":"0xbeec94ea24444906fe247c47841a45220f07e5718d06157fe4502fac326dab617e973e221e45746721330c2db3f63202268686378cc28b9800c1daaf0bbafeb1"},"id":0}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}

View File

@@ -0,0 +1,678 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use accounts::{AccountProvider, AccountProviderSettings};
use ethereum_types::Address;
use ethstore::{accounts_dir::RootDiskDirectory, EthStore};
use tempdir::TempDir;
use jsonrpc_core::IoHandler;
use v1::{ParityAccounts, ParityAccountsClient, ParityAccountsInfo};
struct ParityAccountsTester {
accounts: Arc<AccountProvider>,
io: IoHandler,
}
fn accounts_provider() -> Arc<AccountProvider> {
Arc::new(AccountProvider::transient_provider())
}
fn accounts_provider_with_vaults_support(temp_path: &str) -> Arc<AccountProvider> {
let root_keys_dir = RootDiskDirectory::create(temp_path).unwrap();
let secret_store = EthStore::open(Box::new(root_keys_dir)).unwrap();
Arc::new(AccountProvider::new(
Box::new(secret_store),
AccountProviderSettings::default(),
))
}
fn setup_with_accounts_provider(accounts_provider: Arc<AccountProvider>) -> ParityAccountsTester {
let opt_ap = accounts_provider.clone();
let parity_accounts = ParityAccountsClient::new(&opt_ap);
let parity_accounts2 = ParityAccountsClient::new(&opt_ap);
let mut io = IoHandler::default();
io.extend_with(ParityAccounts::to_delegate(parity_accounts));
io.extend_with(ParityAccountsInfo::to_delegate(parity_accounts2));
let tester = ParityAccountsTester {
accounts: accounts_provider,
io: io,
};
tester
}
fn setup() -> ParityAccountsTester {
setup_with_accounts_provider(accounts_provider())
}
fn setup_with_vaults_support(temp_path: &str) -> ParityAccountsTester {
setup_with_accounts_provider(accounts_provider_with_vaults_support(temp_path))
}
#[test]
fn rpc_parity_accounts_info() {
let tester = setup();
let io = tester.io;
tester.accounts.new_account(&"".into()).unwrap();
let accounts = tester.accounts.accounts().unwrap();
assert_eq!(accounts.len(), 1);
let address = accounts[0];
tester.accounts.set_address_name(1.into(), "XX".into());
tester
.accounts
.set_account_name(address.clone(), "Test".into())
.unwrap();
tester
.accounts
.set_account_meta(address.clone(), "{foo: 69}".into())
.unwrap();
let request = r#"{"jsonrpc": "2.0", "method": "parity_accountsInfo", "params": [], "id": 1}"#;
let response = format!(
"{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{:x}\":{{\"name\":\"Test\"}}}},\"id\":1}}",
address
);
assert_eq!(io.handle_request_sync(request), Some(response));
}
#[test]
fn rpc_parity_default_account() {
let tester = setup();
let io = tester.io;
// Check empty
let address = Address::default();
let request = r#"{"jsonrpc": "2.0", "method": "parity_defaultAccount", "params": [], "id": 1}"#;
let response = format!(
"{{\"jsonrpc\":\"2.0\",\"result\":\"0x{:x}\",\"id\":1}}",
address
);
assert_eq!(io.handle_request_sync(request), Some(response));
// With account
tester.accounts.new_account(&"".into()).unwrap();
let accounts = tester.accounts.accounts().unwrap();
assert_eq!(accounts.len(), 1);
let address = accounts[0];
let request = r#"{"jsonrpc": "2.0", "method": "parity_defaultAccount", "params": [], "id": 1}"#;
let response = format!(
"{{\"jsonrpc\":\"2.0\",\"result\":\"0x{:x}\",\"id\":1}}",
address
);
assert_eq!(io.handle_request_sync(request), Some(response));
}
#[test]
fn should_be_able_to_get_account_info() {
let tester = setup();
tester.accounts.new_account(&"".into()).unwrap();
let accounts = tester.accounts.accounts().unwrap();
assert_eq!(accounts.len(), 1);
let address = accounts[0];
let uuid = tester
.accounts
.accounts_info()
.unwrap()
.get(&address)
.unwrap()
.uuid
.as_ref()
.unwrap()
.clone();
tester
.accounts
.set_account_name(address.clone(), "Test".to_owned())
.unwrap();
tester
.accounts
.set_account_meta(address.clone(), "{foo: 69}".to_owned())
.unwrap();
let request =
r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 1}"#;
let res = tester.io.handle_request_sync(request);
let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{:x}\":{{\"meta\":\"{{foo: 69}}\",\"name\":\"Test\",\"uuid\":\"{}\"}}}},\"id\":1}}", address, uuid);
assert_eq!(res, Some(response));
}
#[test]
fn should_be_able_to_set_name() {
let tester = setup();
tester.accounts.new_account(&"".into()).unwrap();
let accounts = tester.accounts.accounts().unwrap();
assert_eq!(accounts.len(), 1);
let address = accounts[0];
let request = format!(
r#"{{"jsonrpc": "2.0", "method": "parity_setAccountName", "params": ["0x{:x}", "Test"], "id": 1}}"#,
address
);
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
let res = tester.io.handle_request_sync(&request);
assert_eq!(res, Some(response.into()));
let uuid = tester
.accounts
.accounts_info()
.unwrap()
.get(&address)
.unwrap()
.uuid
.as_ref()
.unwrap()
.clone();
let request =
r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 1}"#;
let res = tester.io.handle_request_sync(request);
let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{:x}\":{{\"meta\":\"{{}}\",\"name\":\"Test\",\"uuid\":\"{}\"}}}},\"id\":1}}", address, uuid);
assert_eq!(res, Some(response));
}
#[test]
fn should_be_able_to_set_meta() {
let tester = setup();
tester.accounts.new_account(&"".into()).unwrap();
let accounts = tester.accounts.accounts().unwrap();
assert_eq!(accounts.len(), 1);
let address = accounts[0];
let request = format!(
r#"{{"jsonrpc": "2.0", "method": "parity_setAccountMeta", "params": ["0x{:x}", "{{foo: 69}}"], "id": 1}}"#,
address
);
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
let res = tester.io.handle_request_sync(&request);
assert_eq!(res, Some(response.into()));
let uuid = tester
.accounts
.accounts_info()
.unwrap()
.get(&address)
.unwrap()
.uuid
.as_ref()
.unwrap()
.clone();
let request =
r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 1}"#;
let res = tester.io.handle_request_sync(request);
let response = format!("{{\"jsonrpc\":\"2.0\",\"result\":{{\"0x{:x}\":{{\"meta\":\"{{foo: 69}}\",\"name\":\"\",\"uuid\":\"{}\"}}}},\"id\":1}}", address, uuid);
assert_eq!(res, Some(response));
}
#[test]
fn should_be_able_to_kill_account() {
let tester = setup();
tester.accounts.new_account(&"password".into()).unwrap();
let accounts = tester.accounts.accounts().unwrap();
assert_eq!(accounts.len(), 1);
let address = accounts[0];
let request = format!(
r#"{{"jsonrpc": "2.0", "method": "parity_killAccount", "params": ["0xf00baba2f00baba2f00baba2f00baba2f00baba2"], "id": 1}}"#
);
let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params: invalid length 1, expected a tuple of size 2."},"id":1}"#;
let res = tester.io.handle_request_sync(&request);
assert_eq!(res, Some(response.into()));
let request = format!(
r#"{{"jsonrpc": "2.0", "method": "parity_killAccount", "params": ["0x{:x}", "password"], "id": 1}}"#,
address
);
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
let res = tester.io.handle_request_sync(&request);
assert_eq!(res, Some(response.into()));
let accounts = tester.accounts.accounts().unwrap();
assert_eq!(accounts.len(), 0);
}
#[test]
fn should_be_able_to_remove_address() {
let tester = setup();
// add an address
let request = r#"{"jsonrpc": "2.0", "method": "parity_setAccountName", "params": ["0x000baba1000baba2000baba3000baba4000baba5", "Test"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
let res = tester.io.handle_request_sync(&request);
assert_eq!(res, Some(response.into()));
// verify it exists
let request =
r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 2}"#;
let res = tester.io.handle_request_sync(request);
let response = r#"{"jsonrpc":"2.0","result":{"0x000baba1000baba2000baba3000baba4000baba5":{"meta":"{}","name":"Test"}},"id":2}"#;
assert_eq!(res, Some(response.into()));
// remove the address
let request = r#"{"jsonrpc": "2.0", "method": "parity_removeAddress", "params": ["0x000baba1000baba2000baba3000baba4000baba5"], "id": 3}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":3}"#;
let res = tester.io.handle_request_sync(&request);
assert_eq!(res, Some(response.into()));
// verify empty
let request =
r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params": [], "id": 4}"#;
let res = tester.io.handle_request_sync(request);
let response = r#"{"jsonrpc":"2.0","result":{},"id":4}"#;
assert_eq!(res, Some(response.into()));
}
#[test]
fn rpc_parity_new_vault() {
let tempdir = TempDir::new("").unwrap();
let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap());
let request = r#"{"jsonrpc": "2.0", "method": "parity_newVault", "params":["vault1", "password1"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
assert!(tester.accounts.close_vault("vault1").is_ok());
assert!(tester
.accounts
.open_vault("vault1", &"password1".into())
.is_ok());
}
#[test]
fn rpc_parity_open_vault() {
let tempdir = TempDir::new("").unwrap();
let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap());
assert!(tester
.accounts
.create_vault("vault1", &"password1".into())
.is_ok());
assert!(tester.accounts.close_vault("vault1").is_ok());
let request = r#"{"jsonrpc": "2.0", "method": "parity_openVault", "params":["vault1", "password1"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_parity_close_vault() {
let tempdir = TempDir::new("").unwrap();
let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap());
assert!(tester
.accounts
.create_vault("vault1", &"password1".into())
.is_ok());
let request =
r#"{"jsonrpc": "2.0", "method": "parity_closeVault", "params":["vault1"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_parity_change_vault_password() {
let tempdir = TempDir::new("").unwrap();
let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap());
assert!(tester
.accounts
.create_vault("vault1", &"password1".into())
.is_ok());
let request = r#"{"jsonrpc": "2.0", "method": "parity_changeVaultPassword", "params":["vault1", "password2"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_parity_change_vault() {
let tempdir = TempDir::new("").unwrap();
let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap());
let (address, _) = tester
.accounts
.new_account_and_public(&"root_password".into())
.unwrap();
assert!(tester
.accounts
.create_vault("vault1", &"password1".into())
.is_ok());
let request = format!(
r#"{{"jsonrpc": "2.0", "method": "parity_changeVault", "params":["0x{:x}", "vault1"], "id": 1}}"#,
address
);
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
}
#[test]
fn rpc_parity_vault_adds_vault_field_to_acount_meta() {
let tempdir = TempDir::new("").unwrap();
let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap());
let (address1, _) = tester
.accounts
.new_account_and_public(&"root_password1".into())
.unwrap();
let uuid1 = tester
.accounts
.account_meta(address1.clone())
.unwrap()
.uuid
.unwrap();
assert!(tester
.accounts
.create_vault("vault1", &"password1".into())
.is_ok());
assert!(tester.accounts.change_vault(address1, "vault1").is_ok());
let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params":[], "id": 1}"#;
let response = format!(
r#"{{"jsonrpc":"2.0","result":{{"0x{:x}":{{"meta":"{{\"vault\":\"vault1\"}}","name":"","uuid":"{}"}}}},"id":1}}"#,
address1, uuid1
);
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
// and then
assert!(tester.accounts.change_vault(address1, "").is_ok());
let request = r#"{"jsonrpc": "2.0", "method": "parity_allAccountsInfo", "params":[], "id": 1}"#;
let response = format!(
r#"{{"jsonrpc":"2.0","result":{{"0x{:x}":{{"meta":"{{}}","name":"","uuid":"{}"}}}},"id":1}}"#,
address1, uuid1
);
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_parity_list_vaults() {
let tempdir = TempDir::new("").unwrap();
let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap());
assert!(tester
.accounts
.create_vault("vault1", &"password1".into())
.is_ok());
assert!(tester
.accounts
.create_vault("vault2", &"password2".into())
.is_ok());
let request = r#"{"jsonrpc": "2.0", "method": "parity_listVaults", "params":[], "id": 1}"#;
let response1 = r#"{"jsonrpc":"2.0","result":["vault1","vault2"],"id":1}"#;
let response2 = r#"{"jsonrpc":"2.0","result":["vault2","vault1"],"id":1}"#;
let actual_response = tester.io.handle_request_sync(request);
assert!(
actual_response == Some(response1.to_owned())
|| actual_response == Some(response2.to_owned())
);
}
#[test]
fn rpc_parity_list_opened_vaults() {
let tempdir = TempDir::new("").unwrap();
let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap());
assert!(tester
.accounts
.create_vault("vault1", &"password1".into())
.is_ok());
assert!(tester
.accounts
.create_vault("vault2", &"password2".into())
.is_ok());
assert!(tester
.accounts
.create_vault("vault3", &"password3".into())
.is_ok());
assert!(tester.accounts.close_vault("vault2").is_ok());
let request =
r#"{"jsonrpc": "2.0", "method": "parity_listOpenedVaults", "params":[], "id": 1}"#;
let response1 = r#"{"jsonrpc":"2.0","result":["vault1","vault3"],"id":1}"#;
let response2 = r#"{"jsonrpc":"2.0","result":["vault3","vault1"],"id":1}"#;
let actual_response = tester.io.handle_request_sync(request);
assert!(
actual_response == Some(response1.to_owned())
|| actual_response == Some(response2.to_owned())
);
}
#[test]
fn rpc_parity_get_set_vault_meta() {
let tempdir = TempDir::new("").unwrap();
let tester = setup_with_vaults_support(tempdir.path().to_str().unwrap());
assert!(tester
.accounts
.create_vault("vault1", &"password1".into())
.is_ok());
// when no meta set
let request =
r#"{"jsonrpc": "2.0", "method": "parity_getVaultMeta", "params":["vault1"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"{}","id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
// when meta set
assert!(tester
.accounts
.set_vault_meta("vault1", "vault1_meta")
.is_ok());
let request =
r#"{"jsonrpc": "2.0", "method": "parity_getVaultMeta", "params":["vault1"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"vault1_meta","id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
// change meta
let request = r#"{"jsonrpc": "2.0", "method": "parity_setVaultMeta", "params":["vault1", "updated_vault1_meta"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
// query changed meta
let request =
r#"{"jsonrpc": "2.0", "method": "parity_getVaultMeta", "params":["vault1"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"updated_vault1_meta","id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
// name: parity_deriveAddressHash
// example: {"jsonrpc": "2.0", "method": "parity_deriveAddressHash", "params": ["0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e", "password1", { "type": "soft", "hash": "0x0c0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0c0c" }, true ], "id": 3}
#[test]
fn derive_key_hash() {
let tester = setup();
let hash = tester
.accounts
.insert_account(
"0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a"
.parse()
.unwrap(),
&"password1".into(),
)
.expect("account should be inserted ok");
assert_eq!(
hash,
"c171033d5cbff7175f29dfd3a63dda3d6f8f385e".parse().unwrap()
);
// derive by hash
let request = r#"{"jsonrpc": "2.0", "method": "parity_deriveAddressHash", "params": ["0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e", "password1", { "type": "soft", "hash": "0x0c0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0c0c" }, true ], "id": 3}"#;
let response =
r#"{"jsonrpc":"2.0","result":"0xf28c28fcddf4a9b8f474237278d3647f9c0d1b3c","id":3}"#;
let res = tester.io.handle_request_sync(&request);
assert_eq!(res, Some(response.into()));
}
// name: parity_deriveAddressIndex
// example: {"jsonrpc": "2.0", "method": "parity_deriveAddressIndex", "params": ["0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e", "password1", [{ "type": "soft", "index": 0 }, { "type": "soft", "index": 1 }], false ], "id": 3}
#[test]
fn derive_key_index() {
let tester = setup();
let hash = tester
.accounts
.insert_account(
"0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a"
.parse()
.unwrap(),
&"password1".into(),
)
.expect("account should be inserted ok");
assert_eq!(
hash,
"c171033d5cbff7175f29dfd3a63dda3d6f8f385e".parse().unwrap()
);
// derive by hash
let request = r#"{"jsonrpc": "2.0", "method": "parity_deriveAddressIndex", "params": ["0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e", "password1", [{ "type": "soft", "index": 0 }, { "type": "soft", "index": 1 }], false ], "id": 3}"#;
let response =
r#"{"jsonrpc":"2.0","result":"0xcc548e0bb2efe792a920ae0fbf583b13919f274f","id":3}"#;
let res = tester.io.handle_request_sync(&request);
assert_eq!(res, Some(response.into()));
}
#[test]
fn should_export_account() {
// given
let tester = setup();
let wallet = r#"{"id":"6a186c80-7797-cff2-bc2e-7c1d6a6cc76e","version":3,"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"a1c6ff99070f8032ca1c4e8add006373"},"ciphertext":"df27e3db64aa18d984b6439443f73660643c2d119a6f0fa2fa9a6456fc802d75","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"ddc325335cda5567a1719313e73b4842511f3e4a837c9658eeb78e51ebe8c815"},"mac":"3dc888ae79cbb226ff9c455669f6cf2d79be72120f2298f6cb0d444fddc0aa3d"},"address":"0042e5d2a662eeaca8a7e828c174f98f35d8925b","name":"parity-export-test","meta":"{\"passwordHint\":\"parity-export-test\",\"timestamp\":1490017814987}"}"#;
tester
.accounts
.import_wallet(wallet.as_bytes(), &"parity-export-test".into(), false)
.unwrap();
let accounts = tester.accounts.accounts().unwrap();
assert_eq!(accounts.len(), 1);
// invalid password
let request = r#"{"jsonrpc":"2.0","method":"parity_exportAccount","params":["0x0042e5d2a662eeaca8a7e828c174f98f35d8925b","123"],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32023,"message":"Could not export account.","data":"InvalidPassword"},"id":1}"#;
let res = tester.io.handle_request_sync(&request);
assert_eq!(res, Some(response.into()));
// correct password
let request = r#"{"jsonrpc":"2.0","method":"parity_exportAccount","params":["0x0042e5d2a662eeaca8a7e828c174f98f35d8925b","parity-export-test"],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"address":"0042e5d2a662eeaca8a7e828c174f98f35d8925b","crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"a1c6ff99070f8032ca1c4e8add006373"},"ciphertext":"df27e3db64aa18d984b6439443f73660643c2d119a6f0fa2fa9a6456fc802d75","kdf":"pbkdf2","kdfparams":{"c":10240,"dklen":32,"prf":"hmac-sha256","salt":"ddc325335cda5567a1719313e73b4842511f3e4a837c9658eeb78e51ebe8c815"},"mac":"3dc888ae79cbb226ff9c455669f6cf2d79be72120f2298f6cb0d444fddc0aa3d"},"id":"6a186c80-7797-cff2-bc2e-7c1d6a6cc76e","meta":"{\"passwordHint\":\"parity-export-test\",\"timestamp\":1490017814987}","name":"parity-export-test","version":3},"id":1}"#;
let result = tester.io.handle_request_sync(&request);
println!("Result: {:?}", result);
println!("Response: {:?}", response);
assert_eq!(result, Some(response.into()));
}
#[test]
fn should_import_wallet() {
let tester = setup();
let id = "6a186c80-7797-cff2-bc2e-7c1d6a6cc76e";
let request = r#"{"jsonrpc":"2.0","method":"parity_newAccountFromWallet","params":["{\"id\":\"<ID>\",\"version\":3,\"crypto\":{\"cipher\":\"aes-128-ctr\",\"cipherparams\":{\"iv\":\"478736fb55872c1baf01b27b1998c90b\"},\"ciphertext\":\"fe5a63cc0055d7b0b3b57886f930ad9b63f48950d1348145d95996c41e05f4e0\",\"kdf\":\"pbkdf2\",\"kdfparams\":{\"c\":10240,\"dklen\":32,\"prf\":\"hmac-sha256\",\"salt\":\"658436d6738a19731149a98744e5cf02c8d5aa1f8e80c1a43cc9351c70a984e4\"},\"mac\":\"c7384b26ecf25539d942030230062af9b69de5766cbcc4690bffce1536644631\"},\"address\":\"00bac56a8a27232baa044c03f43bf3648c961735\",\"name\":\"hello world\",\"meta\":\"{}\"}", "himom"],"id":1}"#;
let request = request.replace("<ID>", id);
let response =
r#"{"jsonrpc":"2.0","result":"0x00bac56a8a27232baa044c03f43bf3648c961735","id":1}"#;
let res = tester.io.handle_request_sync(&request).unwrap();
assert_eq!(res, response);
let account_meta = tester
.accounts
.account_meta("0x00bac56a8a27232baa044c03f43bf3648c961735".into())
.unwrap();
let account_uuid: String = account_meta.uuid.unwrap().into();
// the RPC should import the account with a new id
assert!(account_uuid != id);
}
#[test]
fn should_sign_message() {
let tester = setup();
let hash = tester
.accounts
.insert_account(
"0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a0a"
.parse()
.unwrap(),
&"password1".into(),
)
.expect("account should be inserted ok");
assert_eq!(
hash,
"c171033d5cbff7175f29dfd3a63dda3d6f8f385e".parse().unwrap()
);
let request = r#"{"jsonrpc": "2.0", "method": "parity_signMessage", "params": ["0xc171033d5cbff7175f29dfd3a63dda3d6f8f385e", "password1", "0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a"], "id": 3}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x1d9e33a8cf8bfc089a172bca01da462f9e359c6cb1b0f29398bc884e4d18df4f78588aee4fb5cc067ca62d2abab995e0bba29527be6ac98105b0320020a2efaf00","id":3}"#;
let res = tester.io.handle_request_sync(&request);
assert_eq!(res, Some(response.into()));
}

View File

@@ -0,0 +1,243 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use ethereum_types::{Address, U256};
use rustc_hex::FromHex;
use std::{str::FromStr, sync::Arc};
use ethcore::{client::TestBlockChainClient, miner::MinerService};
use sync::ManageNetwork;
use super::manage_network::TestManageNetwork;
use jsonrpc_core::IoHandler;
use v1::{tests::helpers::TestMinerService, ParitySet, ParitySetClient};
use fake_fetch::FakeFetch;
fn miner_service() -> Arc<TestMinerService> {
Arc::new(TestMinerService::default())
}
fn client_service() -> Arc<TestBlockChainClient> {
Arc::new(TestBlockChainClient::default())
}
fn network_service() -> Arc<TestManageNetwork> {
Arc::new(TestManageNetwork)
}
pub type TestParitySetClient =
ParitySetClient<TestBlockChainClient, TestMinerService, FakeFetch<usize>>;
fn parity_set_client(
client: &Arc<TestBlockChainClient>,
miner: &Arc<TestMinerService>,
net: &Arc<TestManageNetwork>,
) -> TestParitySetClient {
ParitySetClient::new(
client,
miner,
&(net.clone() as Arc<dyn ManageNetwork>),
FakeFetch::new(Some(1)),
)
}
#[test]
fn rpc_parity_set_min_gas_price() {
let miner = miner_service();
let client = client_service();
let network = network_service();
let mut io = IoHandler::new();
io.extend_with(parity_set_client(&client, &miner, &network).to_delegate());
let request = r#"{"jsonrpc": "2.0", "method": "parity_setMinGasPrice", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_set_min_gas_price_with_automated_calibration_enabled() {
let miner = miner_service();
*miner.min_gas_price.write() = None;
let client = client_service();
let network = network_service();
let mut io = IoHandler::new();
io.extend_with(parity_set_client(&client, &miner, &network).to_delegate());
let request = r#"{"jsonrpc": "2.0", "method": "parity_setMinGasPrice", "params":["0xdeadbeef"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"Can't update fixed gas price while automatic gas calibration is enabled."},"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_set_gas_floor_target() {
let miner = miner_service();
let client = client_service();
let network = network_service();
let mut io = IoHandler::new();
io.extend_with(parity_set_client(&client, &miner, &network).to_delegate());
let request = r#"{"jsonrpc": "2.0", "method": "parity_setGasFloorTarget", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
assert_eq!(
miner.authoring_params().gas_range_target.0,
U256::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()
);
}
#[test]
fn rpc_parity_set_extra_data() {
let miner = miner_service();
let client = client_service();
let network = network_service();
let mut io = IoHandler::new();
io.extend_with(parity_set_client(&client, &miner, &network).to_delegate());
let request = r#"{"jsonrpc": "2.0", "method": "parity_setExtraData", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
assert_eq!(
miner.authoring_params().extra_data,
"cd1722f3947def4cf144679da39c4c32bdc35681"
.from_hex()
.unwrap()
);
}
#[test]
fn rpc_parity_set_author() {
let miner = miner_service();
let client = client_service();
let network = network_service();
let mut io = IoHandler::new();
io.extend_with(parity_set_client(&client, &miner, &network).to_delegate());
let request = r#"{"jsonrpc": "2.0", "method": "parity_setAuthor", "params":["0xcd1722f3947def4cf144679da39c4c32bdc35681"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
assert_eq!(
miner.authoring_params().author,
Address::from_str("cd1722f3947def4cf144679da39c4c32bdc35681").unwrap()
);
}
#[test]
fn rpc_parity_set_transactions_limit() {
let miner = miner_service();
let client = client_service();
let network = network_service();
let mut io = IoHandler::new();
io.extend_with(parity_set_client(&client, &miner, &network).to_delegate());
let request = r#"{"jsonrpc": "2.0", "method": "parity_setTransactionsLimit", "params":[10240240], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":false,"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_set_hash_content() {
let miner = miner_service();
let client = client_service();
let network = network_service();
let mut io = IoHandler::new();
io.extend_with(parity_set_client(&client, &miner, &network).to_delegate());
let request = r#"{"jsonrpc": "2.0", "method": "parity_hashContent", "params":["https://parity.io/assets/images/ethcore-black-horizontal.png"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x2be00befcf008bc0e7d9cdefc194db9c75352e8632f48498b5a6bfce9f02c88e","id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_remove_transaction() {
use types::transaction::{Action, Transaction, TypedTransaction};
let miner = miner_service();
let client = client_service();
let network = network_service();
let mut io = IoHandler::new();
io.extend_with(parity_set_client(&client, &miner, &network).to_delegate());
let tx = TypedTransaction::Legacy(Transaction {
nonce: 1.into(),
gas_price: 0x9184e72a000u64.into(),
gas: 0x76c0.into(),
action: Action::Call(5.into()),
value: 0x9184e72au64.into(),
data: vec![],
});
let signed = tx.fake_sign(2.into());
let hash = signed.hash();
let request = r#"{"jsonrpc": "2.0", "method": "parity_removeTransaction", "params":[""#
.to_owned()
+ &format!("0x{:x}", hash)
+ r#""], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"blockHash":null,"blockNumber":null,"chainId":null,"condition":null,"creates":null,"from":"0x0000000000000000000000000000000000000002","gas":"0x76c0","gasPrice":"0x9184e72a000","hash":"0x49569012bc8523519642c337fded3f20ba987beab31e14c67223b3d31359956f","input":"0x","nonce":"0x1","publicKey":null,"r":"0x1","raw":"0xe9018609184e72a0008276c0940000000000000000000000000000000000000005849184e72a801f0101","s":"0x1","standardV":"0x4","to":"0x0000000000000000000000000000000000000005","transactionIndex":null,"v":"0x1f","value":"0x9184e72a"},"id":1}"#;
miner.pending_transactions.lock().insert(hash, signed);
assert_eq!(io.handle_request_sync(&request), Some(response.to_owned()));
}
#[test]
fn rpc_parity_set_engine_signer() {
use accounts::AccountProvider;
use bytes::ToPretty;
use v1::{impls::ParitySetAccountsClient, traits::ParitySetAccounts};
let account_provider = Arc::new(AccountProvider::transient_provider());
account_provider
.insert_account(::hash::keccak("cow").into(), &"password".into())
.unwrap();
let miner = miner_service();
let mut io = IoHandler::new();
io.extend_with(ParitySetAccountsClient::new(&account_provider, &miner).to_delegate());
let request = r#"{"jsonrpc": "2.0", "method": "parity_setEngineSigner", "params":["0xcd2a3d9f938e13cd947ec05abc7fe734df8dd826", "password"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
assert_eq!(
miner.authoring_params().author,
Address::from_str("cd2a3d9f938e13cd947ec05abc7fe734df8dd826").unwrap()
);
let signature = miner
.signer
.read()
.as_ref()
.unwrap()
.sign(::hash::keccak("x"))
.unwrap()
.to_vec();
assert_eq!(&format!("{}", signature.pretty()), "6f46069ded2154af6e806706e4f7f6fd310ac45f3c6dccb85f11c0059ee20a09245df0a0008bb84a10882b1298284bc93058e7bc5938ea728e77620061687a6401");
}

View File

@@ -0,0 +1,638 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use std::{str::FromStr, sync::Arc};
use accounts::AccountProvider;
use bytes::ToPretty;
use ethcore::client::TestBlockChainClient;
use ethereum_types::{Address, H520, U256};
use hash::keccak;
use jsonrpc_core::IoHandler;
use parity_runtime::Runtime;
use parking_lot::Mutex;
use types::transaction::{Action, Transaction, TypedTransaction};
use ethkey::Secret;
use serde_json::to_value;
use v1::{
helpers::{
dispatch::{eth_data_hash, FullDispatcher},
eip191, nonce,
},
tests::helpers::TestMinerService,
types::{EIP191Version, PresignedTransaction},
Metadata, Personal, PersonalClient,
};
struct PersonalTester {
_runtime: Runtime,
accounts: Arc<AccountProvider>,
io: IoHandler<Metadata>,
miner: Arc<TestMinerService>,
}
fn blockchain_client() -> Arc<TestBlockChainClient> {
let client = TestBlockChainClient::new();
Arc::new(client)
}
fn accounts_provider() -> Arc<AccountProvider> {
Arc::new(AccountProvider::transient_provider())
}
fn miner_service() -> Arc<TestMinerService> {
Arc::new(TestMinerService::default())
}
fn setup() -> PersonalTester {
setup_with(Config {
allow_experimental_rpcs: true,
})
}
struct Config {
pub allow_experimental_rpcs: bool,
}
fn setup_with(c: Config) -> PersonalTester {
let runtime = Runtime::with_thread_count(1);
let accounts = accounts_provider();
let client = blockchain_client();
let miner = miner_service();
let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor())));
let dispatcher = FullDispatcher::new(client, miner.clone(), reservations, 50);
let personal = PersonalClient::new(&accounts, dispatcher, c.allow_experimental_rpcs);
let mut io = IoHandler::default();
io.extend_with(personal.to_delegate());
let tester = PersonalTester {
_runtime: runtime,
accounts: accounts,
io: io,
miner: miner,
};
tester
}
#[test]
fn accounts() {
let tester = setup();
let address = tester.accounts.new_account(&"".into()).unwrap();
let request = r#"{"jsonrpc": "2.0", "method": "personal_listAccounts", "params": [], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":[""#.to_owned()
+ &format!("0x{:x}", address)
+ r#""],"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn new_account() {
let tester = setup();
let request =
r#"{"jsonrpc": "2.0", "method": "personal_newAccount", "params": ["pass"], "id": 1}"#;
let res = tester.io.handle_request_sync(request);
let accounts = tester.accounts.accounts().unwrap();
assert_eq!(accounts.len(), 1);
let address = accounts[0];
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned()
+ format!("0x{:x}", address).as_ref()
+ r#"","id":1}"#;
assert_eq!(res, Some(response));
}
fn invalid_password_test(method: &str) {
let tester = setup();
let address = tester.accounts.new_account(&"password123".into()).unwrap();
let request = r#"{
"jsonrpc": "2.0",
"method": ""#
.to_owned()
+ method
+ r#"",
"params": [{
"from": ""#
+ format!("0x{:x}", address).as_ref()
+ r#"",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a"
}, "password321"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidPassword)"},"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request.as_ref()),
Some(response.into())
);
}
#[test]
fn sign() {
let tester = setup();
let address = tester.accounts.new_account(&"password123".into()).unwrap();
let data = vec![5u8];
let request = r#"{
"jsonrpc": "2.0",
"method": "personal_sign",
"params": [
""#
.to_owned()
+ format!("0x{}", data.to_hex()).as_ref()
+ r#"",
""# + format!("0x{:x}", address).as_ref()
+ r#"",
"password123"
],
"id": 1
}"#;
let hash = eth_data_hash(data);
let signature = H520(
tester
.accounts
.sign(address, Some("password123".into()), hash)
.unwrap()
.into_electrum(),
);
let signature = format!("{:?}", signature);
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &signature + r#"","id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request.as_ref()),
Some(response)
);
}
#[test]
fn sign_with_invalid_password() {
let tester = setup();
let address = tester.accounts.new_account(&"password123".into()).unwrap();
let request = r#"{
"jsonrpc": "2.0",
"method": "personal_sign",
"params": [
"0x0000000000000000000000000000000000000000000000000000000000000005",
""#
.to_owned()
+ format!("0x{:x}", address).as_ref()
+ r#"",
""
],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidPassword)"},"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request.as_ref()),
Some(response.into())
);
}
#[test]
fn sign_transaction_with_invalid_password() {
invalid_password_test("personal_signTransaction");
}
#[test]
fn sign_and_send_transaction_with_invalid_password() {
invalid_password_test("personal_sendTransaction");
}
#[test]
fn send_transaction() {
sign_and_send_test("personal_sendTransaction");
}
#[test]
fn sign_and_send_transaction() {
sign_and_send_test("personal_signAndSendTransaction");
}
fn sign_and_send_test(method: &str) {
let tester = setup();
let address = tester.accounts.new_account(&"password123".into()).unwrap();
let request = r#"{
"jsonrpc": "2.0",
"method": ""#
.to_owned()
+ method
+ r#"",
"params": [{
"from": ""#
+ format!("0x{:x}", address).as_ref()
+ r#"",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a"
}, "password123"],
"id": 1
}"#;
let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(),
gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0),
action: Action::Call(
Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(),
),
value: U256::from(0x9184e72au64),
data: vec![],
});
tester
.accounts
.unlock_account_temporarily(address, "password123".into())
.unwrap();
let signature = tester
.accounts
.sign(address, None, t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None);
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned()
+ format!("0x{:x}", t.hash()).as_ref()
+ r#"","id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request.as_ref()),
Some(response)
);
tester.miner.increment_nonce(&address);
let t = TypedTransaction::Legacy(Transaction {
nonce: U256::one(),
gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0),
action: Action::Call(
Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(),
),
value: U256::from(0x9184e72au64),
data: vec![],
});
tester
.accounts
.unlock_account_temporarily(address, "password123".into())
.unwrap();
let signature = tester
.accounts
.sign(address, None, t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None);
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned()
+ format!("0x{:x}", t.hash()).as_ref()
+ r#"","id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request.as_ref()),
Some(response)
);
}
#[test]
fn ec_recover() {
let tester = setup();
let address = tester.accounts.new_account(&"password123".into()).unwrap();
let data = vec![5u8];
let hash = eth_data_hash(data.clone());
let signature = H520(
tester
.accounts
.sign(address, Some("password123".into()), hash)
.unwrap()
.into_electrum(),
);
let signature = format!("{:?}", signature);
let request = r#"{
"jsonrpc": "2.0",
"method": "personal_ecRecover",
"params": [
""#
.to_owned()
+ format!("0x{}", data.to_hex()).as_ref()
+ r#"",
""# + &signature
+ r#""
],
"id": 1
}"#;
let address = format!("0x{:x}", address);
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &address + r#"","id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request.as_ref()),
Some(response.into())
);
}
#[test]
fn ec_recover_invalid_signature() {
let tester = setup();
let data = vec![5u8];
let request = r#"{
"jsonrpc": "2.0",
"method": "personal_ecRecover",
"params": [
""#
.to_owned()
+ format!("0x{}", data.to_hex()).as_ref()
+ r#"",
"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"
],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32055,"message":"Encryption error.","data":"InvalidSignature"},"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request.as_ref()),
Some(response.into())
);
}
#[test]
fn should_unlock_account_permanently() {
let tester = setup();
let address = tester.accounts.new_account(&"password123".into()).unwrap();
let request = r#"{
"jsonrpc": "2.0",
"method": "personal_unlockAccount",
"params": [
""#
.to_owned()
+ &format!("0x{:x}", address)
+ r#"",
"password123",
null
],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.into())
);
assert!(
tester
.accounts
.sign(address, None, Default::default())
.is_ok(),
"Should unlock account."
);
}
#[test]
fn sign_eip191_with_validator() {
let tester = setup();
let address = tester.accounts.new_account(&"password123".into()).unwrap();
let request = r#"{
"jsonrpc": "2.0",
"method": "personal_sign191",
"params": [
"0x00",
{
"validator": ""#
.to_owned()
+ &format!("0x{:x}", address)
+ r#"",
"data": ""#
+ &format!("0x{:x}", keccak("hello world"))
+ r#""
},
""# + &format!("0x{:x}", address)
+ r#"",
"password123"
],
"id": 1
}"#;
let with_validator = to_value(PresignedTransaction {
validator: address.into(),
data: keccak("hello world").to_vec().into(),
})
.unwrap();
let result = eip191::hash_message(EIP191Version::PresignedTransaction, with_validator).unwrap();
let result = tester
.accounts
.sign(address, Some("password123".into()), result)
.unwrap()
.into_electrum();
let expected = r#"{"jsonrpc":"2.0","result":""#.to_owned()
+ &format!("0x{}", result.to_hex())
+ r#"","id":1}"#;
let response = tester.io.handle_request_sync(&request).unwrap();
assert_eq!(response, expected)
}
#[test]
fn sign_eip191_structured_data() {
let tester = setup();
let secret: Secret = keccak("cow").into();
let address = tester
.accounts
.insert_account(secret, &"lol".into())
.unwrap();
let request = r#"{
"jsonrpc": "2.0",
"method": "personal_sign191",
"params": [
"0x01",
{
"primaryType": "Mail",
"domain": {
"name": "Ether Mail",
"version": "1",
"chainId": "0x1",
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
},
"message": {
"from": {
"name": "Cow",
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
},
"to": {
"name": "Bob",
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
},
"contents": "Hello, Bob!"
},
"types": {
"EIP712Domain": [
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "chainId", "type": "uint256" },
{ "name": "verifyingContract", "type": "address" }
],
"Person": [
{ "name": "name", "type": "string" },
{ "name": "wallet", "type": "address" }
],
"Mail": [
{ "name": "from", "type": "Person" },
{ "name": "to", "type": "Person" },
{ "name": "contents", "type": "string" }
]
}
},
""#
.to_owned()
+ &format!("0x{:x}", address)
+ r#"",
"lol"
],
"id": 1
}"#;
let expected = r#"{"jsonrpc":"2.0","result":"0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c","id":1}"#;
let response = tester.io.handle_request_sync(&request).unwrap();
assert_eq!(response, expected)
}
#[test]
fn sign_structured_data() {
let tester = setup();
let secret: Secret = keccak("cow").into();
let address = tester
.accounts
.insert_account(secret, &"lol".into())
.unwrap();
let request = r#"{
"jsonrpc": "2.0",
"method": "personal_signTypedData",
"params": [
{
"primaryType": "Mail",
"domain": {
"name": "Ether Mail",
"version": "1",
"chainId": "0x1",
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
},
"message": {
"from": {
"name": "Cow",
"wallet": "0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826"
},
"to": {
"name": "Bob",
"wallet": "0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB"
},
"contents": "Hello, Bob!"
},
"types": {
"EIP712Domain": [
{ "name": "name", "type": "string" },
{ "name": "version", "type": "string" },
{ "name": "chainId", "type": "uint256" },
{ "name": "verifyingContract", "type": "address" }
],
"Person": [
{ "name": "name", "type": "string" },
{ "name": "wallet", "type": "address" }
],
"Mail": [
{ "name": "from", "type": "Person" },
{ "name": "to", "type": "Person" },
{ "name": "contents", "type": "string" }
]
}
},
""#
.to_owned()
+ &format!("0x{:x}", address)
+ r#"",
"lol"
],
"id": 1
}"#;
let expected = r#"{"jsonrpc":"2.0","result":"0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b915621c","id":1}"#;
let response = tester.io.handle_request_sync(&request).unwrap();
assert_eq!(response, expected)
}
#[test]
fn should_disable_experimental_apis() {
// given
let tester = setup_with(Config {
allow_experimental_rpcs: false,
});
// when
let request = r#"{
"jsonrpc": "2.0",
"method": "personal_sign191",
"params": [
"0x01",
{},
"0x1234567891234567891234567891234567891234",
"lol"
],
"id": 1
}"#;
let r1 = tester.io.handle_request_sync(&request).unwrap();
let request = r#"{
"jsonrpc": "2.0",
"method": "personal_signTypedData",
"params": [
{
"types": {},
"message": {},
"domain": {
"name": "",
"version": "1",
"chainId": "0x1",
"verifyingContract": "0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC"
},
"primaryType": ""
},
"0x1234567891234567891234567891234678912344",
"lol"
],
"id": 1
}"#;
let r2 = tester.io.handle_request_sync(&request).unwrap();
// then
let expected = r#"{"jsonrpc":"2.0","error":{"code":-32071,"message":"This method is not part of the official RPC API yet (EIP-191). Run with `--jsonrpc-experimental` to enable it.","data":"See EIP: https://eips.ethereum.org/EIPS/eip-191"},"id":1}"#;
assert_eq!(r1, expected);
let expected = r#"{"jsonrpc":"2.0","error":{"code":-32071,"message":"This method is not part of the official RPC API yet (EIP-712). Run with `--jsonrpc-experimental` to enable it.","data":"See EIP: https://eips.ethereum.org/EIPS/eip-712"},"id":1}"#;
assert_eq!(r2, expected);
}

View File

@@ -0,0 +1,85 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use std::sync::{atomic, Arc};
use jsonrpc_core::{
self as core,
futures::{self, Future, Stream},
MetaIoHandler,
};
use jsonrpc_pubsub::Session;
use parity_runtime::Runtime;
use v1::{Metadata, PubSub, PubSubClient};
fn rpc() -> MetaIoHandler<Metadata, core::NoopMiddleware> {
let mut io = MetaIoHandler::default();
let called = atomic::AtomicBool::new(false);
io.add_method("hello", move |_| {
if !called.load(atomic::Ordering::SeqCst) {
called.store(true, atomic::Ordering::SeqCst);
Ok(core::Value::String("hello".into()))
} else {
Ok(core::Value::String("world".into()))
}
});
io
}
#[test]
fn should_subscribe_to_a_method() {
// given
let el = Runtime::with_thread_count(1);
let rpc = rpc();
let pubsub = PubSubClient::new_test(rpc, el.executor()).to_delegate();
let mut io = MetaIoHandler::default();
io.extend_with(pubsub);
let mut metadata = Metadata::default();
let (sender, receiver) = futures::sync::mpsc::channel(8);
metadata.session = Some(Arc::new(Session::new(sender)));
// Subscribe
let request =
r#"{"jsonrpc": "2.0", "method": "parity_subscribe", "params": ["hello", []], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x416d77337e24399d","id":1}"#;
assert_eq!(
io.handle_request_sync(request, metadata.clone()),
Some(response.to_owned())
);
// Check notifications
let (res, receiver) = receiver.into_future().wait().unwrap();
let response = r#"{"jsonrpc":"2.0","method":"parity_subscription","params":{"result":"hello","subscription":"0x416d77337e24399d"}}"#;
assert_eq!(res, Some(response.into()));
let (res, receiver) = receiver.into_future().wait().unwrap();
let response = r#"{"jsonrpc":"2.0","method":"parity_subscription","params":{"result":"world","subscription":"0x416d77337e24399d"}}"#;
assert_eq!(res, Some(response.into()));
// And unsubscribe
let request = r#"{"jsonrpc": "2.0", "method": "parity_unsubscribe", "params": ["0x416d77337e24399d"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
assert_eq!(
io.handle_request_sync(request, metadata),
Some(response.to_owned())
);
let (res, _receiver) = receiver.into_future().wait().unwrap();
assert_eq!(res, None);
}

View File

@@ -0,0 +1,205 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use accounts::AccountProvider;
use crypto::DEFAULT_MAC;
use ethereum_types::H256;
use ethkey::{verify_public, KeyPair, Signature};
use jsonrpc_core::{IoHandler, Success};
use serde_json;
use v1::{
helpers::secretstore::ordered_servers_keccak, metadata::Metadata,
traits::secretstore::SecretStore, types::EncryptedDocumentKey, SecretStoreClient,
};
struct Dependencies {
pub accounts: Arc<AccountProvider>,
}
impl Dependencies {
pub fn new() -> Self {
Dependencies {
accounts: Arc::new(AccountProvider::transient_provider()),
}
}
pub fn client(&self) -> SecretStoreClient {
SecretStoreClient::new(&self.accounts)
}
fn default_client(&self) -> IoHandler<Metadata> {
let mut io = IoHandler::default();
io.extend_with(self.client().to_delegate());
io
}
}
#[test]
fn rpc_secretstore_encrypt_and_decrypt() {
let deps = Dependencies::new();
let io = deps.default_client();
// insert new account
let secret = "c1f1cfe279a5c350d13795bce162941967340c8a228e6ba175489afc564a5bef"
.parse()
.unwrap();
deps.accounts
.insert_account(secret, &"password".into())
.unwrap();
// execute encryption request
let encryption_request = r#"{"jsonrpc": "2.0", "method": "secretstore_encrypt", "params":[
"0x5c2f3b4ec0c2234f8358697edc8b82a62e3ac995", "password",
"0x0440262acc06f1e13cb11b34e792cdf698673a16bb812163cb52689ac34c94ae47047b58f58d8b596d21ac7b03a55896132d07a7dc028b2dad88f6c5a90623fa5b30ff4b1ba385a98c970432d13417cf6d7facd62f86faaef15ca993735890da0cb3e417e2740fc72de7501eef083a12dd5a9ebe513b592b1740848576a936a1eb88fc553fc624b1cae41a0a4e074e34e2aaae686709f08d70e505c5acba12ef96017e89be675a2adb07c72c4e95814fbf",
"0xdeadbeef"
], "id": 1}"#;
let encryption_response = io.handle_request_sync(encryption_request).unwrap();
let encryption_response: Success = serde_json::from_str(&encryption_response).unwrap();
// execute decryption request
let decryption_request_left = r#"{"jsonrpc": "2.0", "method": "secretstore_decrypt", "params":[
"0x5c2f3b4ec0c2234f8358697edc8b82a62e3ac995", "password",
"0x0440262acc06f1e13cb11b34e792cdf698673a16bb812163cb52689ac34c94ae47047b58f58d8b596d21ac7b03a55896132d07a7dc028b2dad88f6c5a90623fa5b30ff4b1ba385a98c970432d13417cf6d7facd62f86faaef15ca993735890da0cb3e417e2740fc72de7501eef083a12dd5a9ebe513b592b1740848576a936a1eb88fc553fc624b1cae41a0a4e074e34e2aaae686709f08d70e505c5acba12ef96017e89be675a2adb07c72c4e95814fbf",""#;
let decryption_request_mid = encryption_response.result.as_str().unwrap();
let decryption_request_right = r#""
], "id": 2}"#;
let decryption_request =
decryption_request_left.to_owned() + decryption_request_mid + decryption_request_right;
let decryption_response = io.handle_request_sync(&decryption_request).unwrap();
assert_eq!(
decryption_response,
r#"{"jsonrpc":"2.0","result":"0xdeadbeef","id":2}"#
);
}
#[test]
fn rpc_secretstore_shadow_decrypt() {
let deps = Dependencies::new();
let io = deps.default_client();
// insert new account
let secret = "82758356bf46b42710d3946a8efa612b7bf5e125e4d49f28facf1139db4a46f4"
.parse()
.unwrap();
deps.accounts
.insert_account(secret, &"password".into())
.unwrap();
// execute decryption request
let decryption_request = r#"{"jsonrpc": "2.0", "method": "secretstore_shadowDecrypt", "params":[
"0x00dfE63B22312ab4329aD0d28CaD8Af987A01932", "password",
"0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91",
"0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3",
["0x049ce50bbadb6352574f2c59742f78df83333975cbd5cbb151c6e8628749a33dc1fa93bb6dffae5994e3eb98ae859ed55ee82937538e6adb054d780d1e89ff140f121529eeadb1161562af9d3342db0008919ca280a064305e5a4e518e93279de7a9396fe5136a9658e337e8e276221248c381c5384cd1ad28e5921f46ff058d5fbcf8a388fc881d0dd29421c218d51761"],
"0x2ddec1f96229efa2916988d8b2a82a47ef36f71c"
], "id": 1}"#;
let decryption_response = io.handle_request_sync(&decryption_request).unwrap();
assert_eq!(
decryption_response,
r#"{"jsonrpc":"2.0","result":"0xdeadbeef","id":1}"#
);
}
#[test]
fn rpc_secretstore_servers_set_hash() {
let deps = Dependencies::new();
let io = deps.default_client();
// execute hashing request
let hashing_request = r#"{"jsonrpc": "2.0", "method": "secretstore_serversSetHash", "params":[
["0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91",
"0x07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3"]
], "id": 1}"#;
let hashing_response = io.handle_request_sync(&hashing_request).unwrap();
let hashing_response = hashing_response.replace(r#"{"jsonrpc":"2.0","result":"0x"#, "");
let hashing_response = hashing_response.replace(r#"","id":1}"#, "");
let hash: H256 = hashing_response.parse().unwrap();
let servers_set_keccak = ordered_servers_keccak(vec![
"843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91".parse().unwrap(),
"07230e34ebfe41337d3ed53b186b3861751f2401ee74b988bba55694e2a6f60c757677e194be2e53c3523cc8548694e636e6acb35c4e8fdc5e29d28679b9b2f3".parse().unwrap()
].into_iter().collect());
assert_eq!(hash, servers_set_keccak);
}
#[test]
fn rpc_secretstore_sign_raw_hash() {
let deps = Dependencies::new();
let io = deps.default_client();
// insert new account
let secret = "82758356bf46b42710d3946a8efa612b7bf5e125e4d49f28facf1139db4a46f4"
.parse()
.unwrap();
let key_pair = KeyPair::from_secret(secret).unwrap();
deps.accounts
.insert_account(key_pair.secret().clone(), &"password".into())
.unwrap();
// execute signing request
let signing_request = r#"{"jsonrpc": "2.0", "method": "secretstore_signRawHash", "params":[
"0x00dfE63B22312ab4329aD0d28CaD8Af987A01932", "password", "0x0000000000000000000000000000000000000000000000000000000000000001"
], "id": 1}"#;
let signing_response = io.handle_request_sync(&signing_request).unwrap();
let signing_response = signing_response.replace(r#"{"jsonrpc":"2.0","result":"0x"#, "");
let signing_response = signing_response.replace(r#"","id":1}"#, "");
let signature: Signature = signing_response.parse().unwrap();
let hash = "0000000000000000000000000000000000000000000000000000000000000001"
.parse()
.unwrap();
assert!(verify_public(key_pair.public(), &signature, &hash).unwrap());
}
#[test]
fn rpc_secretstore_generate_document_key() {
let deps = Dependencies::new();
let io = deps.default_client();
// insert new account
let secret = "82758356bf46b42710d3946a8efa612b7bf5e125e4d49f28facf1139db4a46f4"
.parse()
.unwrap();
let key_pair = KeyPair::from_secret(secret).unwrap();
deps.accounts
.insert_account(key_pair.secret().clone(), &"password".into())
.unwrap();
// execute generation request
let generation_request = r#"{"jsonrpc": "2.0", "method": "secretstore_generateDocumentKey", "params":[
"0x00dfE63B22312ab4329aD0d28CaD8Af987A01932", "password",
"0x843645726384530ffb0c52f175278143b5a93959af7864460f5a4fec9afd1450cfb8aef63dec90657f43f55b13e0a73c7524d4e9a13c051b4e5f1e53f39ecd91"
], "id": 1}"#;
let generation_response = io.handle_request_sync(&generation_request).unwrap();
let generation_response = generation_response.replace(r#"{"jsonrpc":"2.0","result":"#, "");
let generation_response = generation_response.replace(r#","id":1}"#, "");
let generation_response: EncryptedDocumentKey =
serde_json::from_str(&generation_response).unwrap();
// the only thing we can check is that 'encrypted_key' can be decrypted by passed account
assert!(deps
.accounts
.decrypt(
"00dfE63B22312ab4329aD0d28CaD8Af987A01932".parse().unwrap(),
Some("password".into()),
&DEFAULT_MAC,
&generation_response.encrypted_key.0
)
.is_ok());
}

View File

@@ -0,0 +1,755 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use bytes::ToPretty;
use ethereum_types::{Address, H520, U256};
use std::{str::FromStr, sync::Arc};
use accounts::AccountProvider;
use ethcore::client::TestBlockChainClient;
use parity_runtime::Runtime;
use parking_lot::Mutex;
use types::transaction::{Action, SignedTransaction, Transaction, TypedTransaction};
use jsonrpc_core::IoHandler;
use serde_json;
use v1::{
helpers::{
dispatch::{self, eth_data_hash, FullDispatcher},
external_signer::{SignerService, SigningQueue},
nonce, ConfirmationPayload, FilledTransactionRequest,
},
metadata::Metadata,
tests::helpers::TestMinerService,
types::Bytes as RpcBytes,
Origin, Signer, SignerClient,
};
struct SignerTester {
_runtime: Runtime,
signer: Arc<SignerService>,
accounts: Arc<AccountProvider>,
io: IoHandler<Metadata>,
miner: Arc<TestMinerService>,
}
fn blockchain_client() -> Arc<TestBlockChainClient> {
let client = TestBlockChainClient::new();
Arc::new(client)
}
fn accounts_provider() -> Arc<AccountProvider> {
Arc::new(AccountProvider::transient_provider())
}
fn miner_service() -> Arc<TestMinerService> {
Arc::new(TestMinerService::default())
}
fn signer_tester() -> SignerTester {
let runtime = Runtime::with_thread_count(1);
let signer = Arc::new(SignerService::new_test(false));
let accounts = accounts_provider();
let account_signer = Arc::new(dispatch::Signer::new(accounts.clone()));
let client = blockchain_client();
let miner = miner_service();
let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor())));
let dispatcher = FullDispatcher::new(client, miner.clone(), reservations, 50);
let mut io = IoHandler::default();
io.extend_with(
SignerClient::new(account_signer, dispatcher, &signer, runtime.executor()).to_delegate(),
);
SignerTester {
_runtime: runtime,
signer: signer,
accounts: accounts,
io: io,
miner: miner,
}
}
#[test]
fn should_return_list_of_items_to_confirm() {
// given
let tester = signer_tester();
let _send_future = tester
.signer
.add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: Address::from(1),
used_default_from: false,
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
gas_price: U256::from(10_000),
gas: U256::from(10_000_000),
value: U256::from(1),
data: vec![],
nonce: None,
condition: None,
access_list: None,
}),
Origin::Unknown,
)
.unwrap();
let _sign_future = tester
.signer
.add_request(
ConfirmationPayload::EthSignMessage(1.into(), vec![5].into()),
Origin::Unknown,
)
.unwrap();
// when
let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#;
let response = concat!(
r#"{"jsonrpc":"2.0","result":["#,
r#"{"id":"0x1","origin":"unknown","payload":{"sendTransaction":{"condition":null,"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x1"}}},"#,
r#"{"id":"0x2","origin":"unknown","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}}}"#,
r#"],"id":1}"#
);
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
}
#[test]
fn should_reject_transaction_from_queue_without_dispatching() {
// given
let tester = signer_tester();
let _confirmation_future = tester
.signer
.add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: Address::from(1),
used_default_from: false,
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
gas_price: U256::from(10_000),
gas: U256::from(10_000_000),
value: U256::from(1),
data: vec![],
nonce: None,
condition: None,
access_list: None,
}),
Origin::Unknown,
)
.unwrap();
assert_eq!(tester.signer.requests().len(), 1);
// when
let request = r#"{"jsonrpc":"2.0","method":"signer_rejectRequest","params":["0x1"],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":true,"id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
assert_eq!(tester.signer.requests().len(), 0);
assert_eq!(tester.miner.imported_transactions.lock().len(), 0);
}
#[test]
fn should_not_remove_transaction_if_password_is_invalid() {
// given
let tester = signer_tester();
let _confirmation_future = tester
.signer
.add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: Address::from(1),
used_default_from: false,
to: Some(Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap()),
gas_price: U256::from(10_000),
gas: U256::from(10_000_000),
value: U256::from(1),
data: vec![],
nonce: None,
condition: None,
access_list: None,
}),
Origin::Unknown,
)
.unwrap();
assert_eq!(tester.signer.requests().len(), 1);
// when
let request =
r#"{"jsonrpc":"2.0","method":"signer_confirmRequest","params":["0x1",{},"xxx"],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidAccount)"},"id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
assert_eq!(tester.signer.requests().len(), 1);
}
#[test]
fn should_not_remove_sign_if_password_is_invalid() {
// given
let tester = signer_tester();
let _confirmation_future = tester
.signer
.add_request(
ConfirmationPayload::EthSignMessage(0.into(), vec![5].into()),
Origin::Unknown,
)
.unwrap();
assert_eq!(tester.signer.requests().len(), 1);
// when
let request =
r#"{"jsonrpc":"2.0","method":"signer_confirmRequest","params":["0x1",{},"xxx"],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32021,"message":"Account password is invalid or account does not exist.","data":"SStore(InvalidAccount)"},"id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
assert_eq!(tester.signer.requests().len(), 1);
}
#[test]
fn should_confirm_transaction_and_dispatch() {
//// given
let tester = signer_tester();
let address = tester.accounts.new_account(&"test".into()).unwrap();
let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
let _confirmation_future = tester
.signer
.add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: address,
used_default_from: false,
to: Some(recipient),
gas_price: U256::from(10_000),
gas: U256::from(10_000_000),
value: U256::from(1),
data: vec![],
nonce: None,
condition: None,
access_list: None,
}),
Origin::Unknown,
)
.unwrap();
let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(),
gas_price: U256::from(0x1000),
gas: U256::from(0x50505),
action: Action::Call(recipient),
value: U256::from(0x1),
data: vec![],
});
tester
.accounts
.unlock_account_temporarily(address, "test".into())
.unwrap();
let signature = tester
.accounts
.sign(address, None, t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None);
assert_eq!(tester.signer.requests().len(), 1);
// when
let request = r#"{
"jsonrpc":"2.0",
"method":"signer_confirmRequest",
"params":["0x1", {"gasPrice":"0x1000","gas":"0x50505"}, "test"],
"id":1
}"#;
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned()
+ format!("0x{:x}", t.hash()).as_ref()
+ r#"","id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
assert_eq!(tester.signer.requests().len(), 0);
assert_eq!(tester.miner.imported_transactions.lock().len(), 1);
}
#[test]
fn should_alter_the_sender_and_nonce() {
//// given
let tester = signer_tester();
let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
let _confirmation_future = tester
.signer
.add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: 0.into(),
used_default_from: false,
to: Some(recipient),
gas_price: U256::from(10_000),
gas: U256::from(10_000_000),
value: U256::from(1),
data: vec![],
nonce: Some(10.into()),
condition: None,
access_list: None,
}),
Origin::Unknown,
)
.unwrap();
let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(),
gas_price: U256::from(0x1000),
gas: U256::from(0x50505),
action: Action::Call(recipient),
value: U256::from(0x1),
data: vec![],
});
let address = tester.accounts.new_account(&"test".into()).unwrap();
let signature = tester
.accounts
.sign(address, Some("test".into()), t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None);
assert_eq!(tester.signer.requests().len(), 1);
// when
let request = r#"{
"jsonrpc":"2.0",
"method":"signer_confirmRequest",
"params":["0x1", {"sender":""#
.to_owned()
+ &format!("0x{:x}", address)
+ r#"","gasPrice":"0x1000","gas":"0x50505"}, "test"],
"id":1
}"#;
let response =
r#"{"jsonrpc":"2.0","result":""#.to_owned() + &format!("0x{:x}", t.hash()) + r#"","id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
assert_eq!(tester.signer.requests().len(), 0);
assert_eq!(tester.miner.imported_transactions.lock().len(), 1);
}
#[test]
fn should_confirm_transaction_with_token() {
// given
let tester = signer_tester();
let address = tester.accounts.new_account(&"test".into()).unwrap();
let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
let _confirmation_future = tester
.signer
.add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: address,
used_default_from: false,
to: Some(recipient),
gas_price: U256::from(10_000),
gas: U256::from(10_000_000),
value: U256::from(1),
data: vec![],
nonce: None,
condition: None,
access_list: None,
}),
Origin::Unknown,
)
.unwrap();
let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(),
gas_price: U256::from(0x1000),
gas: U256::from(10_000_000),
action: Action::Call(recipient),
value: U256::from(0x1),
data: vec![],
});
let (signature, token) = tester
.accounts
.sign_with_token(address, "test".into(), t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None);
assert_eq!(tester.signer.requests().len(), 1);
// when
let request = r#"{
"jsonrpc":"2.0",
"method":"signer_confirmRequestWithToken",
"params":["0x1", {"gasPrice":"0x1000"}, ""#
.to_owned()
+ token.as_str()
+ r#""],
"id":1
}"#;
let response = r#"{"jsonrpc":"2.0","result":{"result":""#.to_owned()
+ format!("0x{:x}", t.hash()).as_ref()
+ r#"","token":""#;
// then
let result = tester.io.handle_request_sync(&request).unwrap();
assert!(
result.starts_with(&response),
"Should return correct result. Expected: {:?}, Got: {:?}",
response,
result
);
assert_eq!(tester.signer.requests().len(), 0);
assert_eq!(tester.miner.imported_transactions.lock().len(), 1);
}
#[test]
fn should_confirm_transaction_with_rlp() {
// given
let tester = signer_tester();
let address = tester.accounts.new_account(&"test".into()).unwrap();
let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
let _confirmation_future = tester
.signer
.add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: address,
used_default_from: false,
to: Some(recipient),
gas_price: U256::from(10_000),
gas: U256::from(10_000_000),
value: U256::from(1),
data: vec![],
nonce: None,
condition: None,
access_list: None,
}),
Origin::Unknown,
)
.unwrap();
let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(),
gas_price: U256::from(0x1000),
gas: U256::from(10_000_000),
action: Action::Call(recipient),
value: U256::from(0x1),
data: vec![],
});
let signature = tester
.accounts
.sign(address, Some("test".into()), t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None);
let rlp = t.encode();
assert_eq!(tester.signer.requests().len(), 1);
// when
let request = r#"{
"jsonrpc":"2.0",
"method":"signer_confirmRequestRaw",
"params":["0x1", "0x"#
.to_owned()
+ &rlp.to_hex()
+ r#""],
"id":1
}"#;
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned()
+ format!("0x{:x}", t.hash()).as_ref()
+ r#"","id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
assert_eq!(tester.signer.requests().len(), 0);
assert_eq!(tester.miner.imported_transactions.lock().len(), 1);
}
#[test]
fn should_return_error_when_sender_does_not_match() {
// given
let tester = signer_tester();
let address = tester.accounts.new_account(&"test".into()).unwrap();
let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
let _confirmation_future = tester
.signer
.add_request(
ConfirmationPayload::SendTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: Address::default(),
used_default_from: false,
to: Some(recipient),
gas_price: U256::from(10_000),
gas: U256::from(10_000_000),
value: U256::from(1),
data: vec![],
nonce: None,
condition: None,
access_list: None,
}),
Origin::Unknown,
)
.unwrap();
let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(),
gas_price: U256::from(0x1000),
gas: U256::from(10_000_000),
action: Action::Call(recipient),
value: U256::from(0x1),
data: vec![],
});
tester
.accounts
.unlock_account_temporarily(address, "test".into())
.unwrap();
let signature = tester
.accounts
.sign(address, None, t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None);
let rlp = t.encode();
assert_eq!(tester.signer.requests().len(), 1);
// when
let request = r#"{
"jsonrpc":"2.0",
"method":"signer_confirmRequestRaw",
"params":["0x1", "0x"#
.to_owned()
+ &rlp.to_hex()
+ r#""],
"id":1
}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Couldn't parse parameters: Sent transaction does not match the request.","data":"[\"from\"]"},"id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
assert_eq!(tester.signer.requests().len(), 1);
}
#[test]
fn should_confirm_sign_transaction_with_rlp() {
// given
let tester = signer_tester();
let address = tester.accounts.new_account(&"test".into()).unwrap();
let recipient = Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap();
let _confirmation_future = tester
.signer
.add_request(
ConfirmationPayload::SignTransaction(FilledTransactionRequest {
tx_type: Default::default(),
from: address,
used_default_from: false,
to: Some(recipient),
gas_price: U256::from(10_000),
gas: U256::from(10_000_000),
value: U256::from(1),
data: vec![],
nonce: None,
condition: None,
access_list: None,
}),
Origin::Unknown,
)
.unwrap();
assert_eq!(tester.signer.requests().len(), 1);
let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(),
gas_price: U256::from(0x1000),
gas: U256::from(10_000_000),
action: Action::Call(recipient),
value: U256::from(0x1),
data: vec![],
});
let signature = tester
.accounts
.sign(address, Some("test".into()), t.signature_hash(None))
.unwrap();
let t = SignedTransaction::new(t.with_signature(signature.clone(), None)).unwrap();
let rlp = t.encode();
// when
let request = r#"{
"jsonrpc":"2.0",
"method":"signer_confirmRequestRaw",
"params":["0x1", "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,"#
+ &format!(
"\"chainId\":{},",
t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))
)
+ r#""condition":null,"creates":null,"#
+ &format!("\"from\":\"0x{:x}\",", &address)
+ r#""gas":"0x989680","gasPrice":"0x1000","#
+ &format!("\"hash\":\"0x{:x}\",", t.hash())
+ r#""input":"0x","#
+ r#""nonce":"0x0","#
+ &format!("\"publicKey\":\"0x{:x}\",", t.public_key().unwrap())
+ &format!("\"r\":\"0x{:x}\",", U256::from(signature.r()))
+ &format!("\"raw\":\"0x{}\",", rlp.to_hex())
+ &format!("\"s\":\"0x{:x}\",", U256::from(signature.s()))
+ &format!("\"standardV\":\"0x{:x}\",", U256::from(t.standard_v()))
+ r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"#
+ &format!("\"v\":\"0x{:x}\",", U256::from(t.original_v()))
+ r#""value":"0x1""#
+ r#"}},"id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
assert_eq!(tester.signer.requests().len(), 0);
assert_eq!(tester.miner.imported_transactions.lock().len(), 0);
}
#[test]
fn should_confirm_data_sign_with_signature() {
// given
let tester = signer_tester();
let address = tester.accounts.new_account(&"test".into()).unwrap();
let _confirmation_future = tester
.signer
.add_request(
ConfirmationPayload::EthSignMessage(address, vec![1, 2, 3, 4].into()),
Origin::Unknown,
)
.unwrap();
assert_eq!(tester.signer.requests().len(), 1);
let data_hash = eth_data_hash(vec![1, 2, 3, 4].into());
let signature = H520(
tester
.accounts
.sign(address, Some("test".into()), data_hash)
.unwrap()
.into_electrum(),
);
let signature = format!("{:?}", signature);
// when
let request = r#"{
"jsonrpc":"2.0",
"method":"signer_confirmRequestRaw",
"params":["0x1", ""#
.to_owned()
+ &signature
+ r#""],
"id":1
}"#;
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned() + &signature + r#"","id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
assert_eq!(tester.signer.requests().len(), 0);
assert_eq!(tester.miner.imported_transactions.lock().len(), 0);
}
#[test]
fn should_confirm_decrypt_with_phrase() {
// given
let tester = signer_tester();
let address = tester.accounts.new_account(&"test".into()).unwrap();
let _confirmation_future = tester
.signer
.add_request(
ConfirmationPayload::Decrypt(address, vec![1, 2, 3, 4].into()),
Origin::Unknown,
)
.unwrap();
assert_eq!(tester.signer.requests().len(), 1);
let decrypted = serde_json::to_string(&RpcBytes::new(b"phrase".to_vec())).unwrap();
// when
let request = r#"{
"jsonrpc":"2.0",
"method":"signer_confirmRequestRaw",
"params":["0x1", "#
.to_owned()
+ &decrypted
+ r#"],
"id":1
}"#;
let response = r#"{"jsonrpc":"2.0","result":"#.to_owned() + &decrypted + r#","id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
assert_eq!(tester.signer.requests().len(), 0);
assert_eq!(tester.miner.imported_transactions.lock().len(), 0);
}
#[test]
fn should_generate_new_token() {
// given
let tester = signer_tester();
// when
let request = r#"{
"jsonrpc":"2.0",
"method":"signer_generateAuthorizationToken",
"params":[],
"id":1
}"#;
let response = r#"{"jsonrpc":"2.0","result":"new_token","id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
}

View File

@@ -0,0 +1,618 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use std::{str::FromStr, sync::Arc, thread, time::Duration};
use jsonrpc_core::{futures::Future, IoHandler, Success};
use v1::{
helpers::{
dispatch,
external_signer::{SignerService, SigningQueue},
nonce, FullDispatcher,
},
impls::SigningQueueClient,
metadata::Metadata,
tests::{helpers::TestMinerService, mocked::parity},
traits::{EthSigning, Parity, ParitySigning},
types::{ConfirmationResponse, RichRawTransaction},
};
use accounts::AccountProvider;
use bytes::ToPretty;
use ethcore::client::TestBlockChainClient;
use ethereum_types::{Address, U256};
use ethkey::Secret;
use ethstore::ethkey::{Generator, Random};
use parity_runtime::{Executor, Runtime};
use parking_lot::Mutex;
use serde_json;
use types::transaction::{Action, SignedTransaction, Transaction, TypedTransaction};
struct SigningTester {
pub runtime: Runtime,
pub signer: Arc<SignerService>,
pub client: Arc<TestBlockChainClient>,
pub miner: Arc<TestMinerService>,
pub accounts: Arc<AccountProvider>,
pub io: IoHandler<Metadata>,
}
impl Default for SigningTester {
fn default() -> Self {
let runtime = Runtime::with_thread_count(1);
let signer = Arc::new(SignerService::new_test(false));
let client = Arc::new(TestBlockChainClient::default());
let miner = Arc::new(TestMinerService::default());
let accounts = Arc::new(AccountProvider::transient_provider());
let account_signer = Arc::new(dispatch::Signer::new(accounts.clone())) as _;
let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor())));
let mut io = IoHandler::default();
let dispatcher = FullDispatcher::new(client.clone(), miner.clone(), reservations, 50);
let executor = Executor::new_thread_per_future();
let rpc = SigningQueueClient::new(
&signer,
dispatcher.clone(),
executor.clone(),
&account_signer,
);
io.extend_with(EthSigning::to_delegate(rpc));
let rpc = SigningQueueClient::new(&signer, dispatcher, executor, &account_signer);
io.extend_with(ParitySigning::to_delegate(rpc));
SigningTester {
runtime,
signer: signer,
client: client,
miner: miner,
accounts: accounts,
io: io,
}
}
}
fn eth_signing() -> SigningTester {
SigningTester::default()
}
#[test]
fn rpc_eth_sign() {
use rustc_hex::FromHex;
let tester = eth_signing();
let account = tester
.accounts
.insert_account(Secret::from([69u8; 32]), &"abcd".into())
.unwrap();
tester
.accounts
.unlock_account_permanently(account, "abcd".into())
.unwrap();
let _message = "0cc175b9c0f1b6a831c399e26977266192eb5ffee6ae2fec3ad71c777531578f"
.from_hex()
.unwrap();
let req = r#"{
"jsonrpc": "2.0",
"method": "eth_sign",
"params": [
""#
.to_owned()
+ &format!("0x{:x}", account)
+ r#"",
"0x0cc175b9c0f1b6a831c399e26977266192eb5ffee6ae2fec3ad71c777531578f"
],
"id": 1
}"#;
let res = r#"{"jsonrpc":"2.0","result":"0xa2870db1d0c26ef93c7b72d2a0830fa6b841e0593f7186bc6c7cc317af8cf3a42fda03bd589a49949aa05db83300cdb553116274518dbe9d90c65d0213f4af491b","id":1}"#;
assert_eq!(tester.io.handle_request_sync(&req), Some(res.into()));
}
#[test]
fn should_add_sign_to_queue() {
// given
let tester = eth_signing();
let address = Address::random();
assert_eq!(tester.signer.requests().len(), 0);
// when
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_sign",
"params": [
""#
.to_owned()
+ format!("0x{:x}", address).as_ref()
+ r#"",
"0x0000000000000000000000000000000000000000000000000000000000000005"
],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","id":1}"#;
// then
let promise = tester.io.handle_request(&request);
// the future must be polled at least once before request is queued.
let signer = tester.signer.clone();
::std::thread::spawn(move || loop {
if signer.requests().len() == 1 {
// respond
let sender = signer.take(&1.into()).unwrap();
signer.request_confirmed(sender, Ok(ConfirmationResponse::Signature(0.into())));
break;
}
::std::thread::sleep(Duration::from_millis(100))
});
let res = promise.wait().unwrap();
assert_eq!(res, Some(response.to_owned()));
}
#[test]
fn should_post_sign_to_queue() {
// given
let tester = eth_signing();
let address = Address::random();
assert_eq!(tester.signer.requests().len(), 0);
// when
let request = r#"{
"jsonrpc": "2.0",
"method": "parity_postSign",
"params": [
""#
.to_owned()
+ format!("0x{:x}", address).as_ref()
+ r#"",
"0x0000000000000000000000000000000000000000000000000000000000000005"
],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x1","id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
assert_eq!(tester.signer.requests().len(), 1);
}
#[test]
fn should_check_status_of_request() {
// given
let tester = eth_signing();
let address = Address::random();
let request = r#"{
"jsonrpc": "2.0",
"method": "parity_postSign",
"params": [
""#
.to_owned()
+ format!("0x{:x}", address).as_ref()
+ r#"",
"0x0000000000000000000000000000000000000000000000000000000000000005"
],
"id": 1
}"#;
tester.io.handle_request_sync(&request).expect("Sent");
// when
let request = r#"{
"jsonrpc": "2.0",
"method": "parity_checkRequest",
"params": ["0x1"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
}
#[test]
fn should_check_status_of_request_when_its_resolved() {
// given
let tester = eth_signing();
let address = Address::random();
let request = r#"{
"jsonrpc": "2.0",
"method": "parity_postSign",
"params": [
""#
.to_owned()
+ format!("0x{:x}", address).as_ref()
+ r#"",
"0x0000000000000000000000000000000000000000000000000000000000000005"
],
"id": 1
}"#;
tester.io.handle_request_sync(&request).expect("Sent");
let sender = tester.signer.take(&1.into()).unwrap();
tester
.signer
.request_confirmed(sender, Ok(ConfirmationResponse::Signature(1.into())));
// This is not ideal, but we need to give futures some time to be executed, and they need to run in a separate thread
thread::sleep(Duration::from_millis(20));
// when
let request = r#"{
"jsonrpc": "2.0",
"method": "parity_checkRequest",
"params": ["0x1"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001","id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
}
#[test]
fn should_sign_if_account_is_unlocked() {
// given
let tester = eth_signing();
let data = vec![5u8];
let acc = tester
.accounts
.insert_account(Secret::from([69u8; 32]), &"test".into())
.unwrap();
tester
.accounts
.unlock_account_permanently(acc, "test".into())
.unwrap();
// when
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_sign",
"params": [
""#
.to_owned()
+ format!("0x{:x}", acc).as_ref()
+ r#"",
""# + format!("0x{}", data.to_hex()).as_ref()
+ r#""
],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":"0xdb53b32e56cf3e9735377b7664d6de5a03e125b1bf8ec55715d253668b4238503b4ac931fe6af90add73e72a585e952665376b2b9afc5b6b239b7df74c734e121b","id":1}"#;
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
assert_eq!(tester.signer.requests().len(), 0);
}
#[test]
fn should_add_transaction_to_queue() {
// given
let tester = eth_signing();
let address = Address::random();
assert_eq!(tester.signer.requests().len(), 0);
// when
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_sendTransaction",
"params": [{
"from": ""#
.to_owned()
+ format!("0x{:x}", address).as_ref()
+ r#"",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a"
}],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x0000000000000000000000000000000000000000000000000000000000000000","id":1}"#;
// then
let promise = tester.io.handle_request(&request);
// the future must be polled at least once before request is queued.
let signer = tester.signer.clone();
::std::thread::spawn(move || loop {
if signer.requests().len() == 1 {
// respond
let sender = signer.take(&1.into()).unwrap();
signer.request_confirmed(sender, Ok(ConfirmationResponse::SendTransaction(0.into())));
break;
}
::std::thread::sleep(Duration::from_millis(100))
});
let res = promise.wait().unwrap();
assert_eq!(res, Some(response.to_owned()));
}
#[test]
fn should_add_sign_transaction_to_the_queue() {
// given
let tester = eth_signing();
let address = tester.accounts.new_account(&"test".into()).unwrap();
assert_eq!(tester.signer.requests().len(), 0);
// when
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_signTransaction",
"params": [{
"from": ""#
.to_owned()
+ format!("0x{:x}", address).as_ref()
+ r#"",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a"
}],
"id": 1
}"#;
let t = TypedTransaction::Legacy(Transaction {
nonce: U256::one(),
gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0),
action: Action::Call(
Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(),
),
value: U256::from(0x9184e72au64),
data: vec![],
});
let signature = tester
.accounts
.sign(address, Some("test".into()), t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None);
let t = SignedTransaction::new(t).unwrap();
let signature = t.signature();
let rlp = t.encode();
let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned()
+ r#""raw":"0x"#
+ &rlp.to_hex()
+ r#"","#
+ r#""tx":{"#
+ r#""blockHash":null,"blockNumber":null,"#
+ &format!(
"\"chainId\":{},",
t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))
)
+ r#""condition":null,"creates":null,"#
+ &format!("\"from\":\"0x{:x}\",", &address)
+ r#""gas":"0x76c0","gasPrice":"0x9184e72a000","#
+ &format!("\"hash\":\"0x{:x}\",", t.hash())
+ r#""input":"0x","#
+ r#""nonce":"0x1","#
+ &format!("\"publicKey\":\"0x{:x}\",", t.public_key().unwrap())
+ &format!("\"r\":\"0x{:x}\",", U256::from(signature.r()))
+ &format!("\"raw\":\"0x{}\",", rlp.to_hex())
+ &format!("\"s\":\"0x{:x}\",", U256::from(signature.s()))
+ &format!("\"standardV\":\"0x{:x}\",", U256::from(t.standard_v()))
+ r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"#
+ &format!("\"v\":\"0x{:x}\",", U256::from(t.original_v()))
+ r#""value":"0x9184e72a""#
+ r#"}},"id":1}"#;
// then
tester.miner.increment_nonce(&address);
let promise = tester.io.handle_request(&request);
// the future must be polled at least once before request is queued.
let signer = tester.signer.clone();
::std::thread::spawn(move || loop {
if signer.requests().len() == 1 {
// respond
let sender = signer.take(&1.into()).unwrap();
signer.request_confirmed(
sender,
Ok(ConfirmationResponse::SignTransaction(
RichRawTransaction::from_signed(t.into()),
)),
);
break;
}
::std::thread::sleep(Duration::from_millis(100))
});
let res = promise.wait().unwrap();
assert_eq!(res, Some(response.to_owned()));
}
#[test]
fn should_dispatch_transaction_if_account_is_unlock() {
// given
let tester = eth_signing();
let acc = tester.accounts.new_account(&"test".into()).unwrap();
tester
.accounts
.unlock_account_permanently(acc, "test".into())
.unwrap();
let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(),
gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0),
action: Action::Call(
Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(),
),
value: U256::from(0x9184e72au64),
data: vec![],
});
let signature = tester
.accounts
.sign(acc, None, t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None);
// when
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_sendTransaction",
"params": [{
"from": ""#
.to_owned()
+ format!("0x{:x}", acc).as_ref()
+ r#"",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a"
}],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned()
+ format!("0x{:x}", t.hash()).as_ref()
+ r#"","id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.to_owned())
);
}
#[test]
fn should_decrypt_message_if_account_is_unlocked() {
// given
let mut tester = eth_signing();
let parity = parity::Dependencies::new();
tester.io.extend_with(parity.client(None).to_delegate());
let (address, public) = tester
.accounts
.new_account_and_public(&"test".into())
.unwrap();
tester
.accounts
.unlock_account_permanently(address, "test".into())
.unwrap();
// First encrypt message
let request = format!(
"{}0x{:x}{}",
r#"{"jsonrpc": "2.0", "method": "parity_encryptMessage", "params":[""#,
public,
r#"", "0x01020304"], "id": 1}"#
);
let encrypted: Success =
serde_json::from_str(&tester.io.handle_request_sync(&request).unwrap()).unwrap();
// then call decrypt
let request = format!(
"{}{:x}{}{}{}",
r#"{"jsonrpc": "2.0", "method": "parity_decryptMessage", "params":["0x"#,
address,
r#"","#,
encrypted.result,
r#"], "id": 1}"#
);
println!("Request: {:?}", request);
let response = r#"{"jsonrpc":"2.0","result":"0x01020304","id":1}"#;
// then
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.into())
);
}
#[test]
fn should_add_decryption_to_the_queue() {
// given
let tester = eth_signing();
let acc = Random.generate().unwrap();
assert_eq!(tester.signer.requests().len(), 0);
// when
let request = r#"{
"jsonrpc": "2.0",
"method": "parity_decryptMessage",
"params": ["0x"#
.to_owned()
+ &format!("{:x}", acc.address())
+ r#"",
"0x012345"],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x0102","id":1}"#;
// then
let promise = tester.io.handle_request(&request);
// the future must be polled at least once before request is queued.
let signer = tester.signer.clone();
::std::thread::spawn(move || loop {
if signer.requests().len() == 1 {
// respond
let sender = signer.take(&1.into()).unwrap();
signer.request_confirmed(
sender,
Ok(ConfirmationResponse::Decrypt(vec![0x1, 0x2].into())),
);
break;
}
::std::thread::sleep(Duration::from_millis(10))
});
// check response: will deadlock if unsuccessful.
let res = promise.wait().unwrap();
assert_eq!(res, Some(response.to_owned()));
}
#[test]
fn should_compose_transaction() {
// given
let tester = eth_signing();
let acc = Random.generate().unwrap();
assert_eq!(tester.signer.requests().len(), 0);
let from = format!("{:x}", acc.address());
// when
let request = r#"{
"jsonrpc": "2.0",
"method": "parity_composeTransaction",
"params": [{"from":"0x"#
.to_owned()
+ &from
+ r#"","value":"0x5"}],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","result":{"condition":null,"data":"0x","from":"0x"#
.to_owned()
+ &from
+ r#"","gas":"0x5208","gasPrice":"0x4a817c800","nonce":"0x0","to":null,"value":"0x5"},"id":1}"#;
// then
let res = tester.io.handle_request(&request).wait().unwrap();
assert_eq!(res, Some(response.to_owned()));
}

View File

@@ -0,0 +1,292 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use std::{str::FromStr, sync::Arc};
use accounts::AccountProvider;
use ethcore::client::TestBlockChainClient;
use ethereum_types::{Address, U256};
use parity_runtime::Runtime;
use parking_lot::Mutex;
use types::transaction::{Action, Transaction, TypedTransaction};
use jsonrpc_core::IoHandler;
use v1::{
helpers::{
dispatch::{self, FullDispatcher},
nonce,
},
metadata::Metadata,
tests::helpers::TestMinerService,
EthClientOptions, EthSigning, SigningUnsafeClient,
};
fn blockchain_client() -> Arc<TestBlockChainClient> {
let client = TestBlockChainClient::new();
Arc::new(client)
}
fn accounts_provider() -> Arc<AccountProvider> {
Arc::new(AccountProvider::transient_provider())
}
fn miner_service() -> Arc<TestMinerService> {
Arc::new(TestMinerService::default())
}
struct EthTester {
pub runtime: Runtime,
pub client: Arc<TestBlockChainClient>,
pub accounts_provider: Arc<AccountProvider>,
pub miner: Arc<TestMinerService>,
pub io: IoHandler<Metadata>,
}
impl Default for EthTester {
fn default() -> Self {
Self::new_with_options(Default::default())
}
}
impl EthTester {
pub fn new_with_options(options: EthClientOptions) -> Self {
let runtime = Runtime::with_thread_count(1);
let client = blockchain_client();
let accounts_provider = accounts_provider();
let ap = Arc::new(dispatch::Signer::new(accounts_provider.clone())) as _;
let miner = miner_service();
let gas_price_percentile = options.gas_price_percentile;
let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor())));
let dispatcher = FullDispatcher::new(
client.clone(),
miner.clone(),
reservations,
gas_price_percentile,
);
let sign = SigningUnsafeClient::new(&ap, dispatcher).to_delegate();
let mut io: IoHandler<Metadata> = IoHandler::default();
io.extend_with(sign);
EthTester {
runtime,
client,
miner,
io,
accounts_provider,
}
}
}
#[test]
fn rpc_eth_send_transaction() {
let tester = EthTester::default();
let address = tester.accounts_provider.new_account(&"".into()).unwrap();
tester
.accounts_provider
.unlock_account_permanently(address, "".into())
.unwrap();
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_sendTransaction",
"params": [{
"from": ""#
.to_owned()
+ format!("0x{:x}", address).as_ref()
+ r#"",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a"
}],
"id": 1
}"#;
let t = TypedTransaction::Legacy(Transaction {
nonce: U256::zero(),
gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0),
action: Action::Call(
Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(),
),
value: U256::from(0x9184e72au64),
data: vec![],
});
let signature = tester
.accounts_provider
.sign(address, None, t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None);
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned()
+ format!("0x{:x}", t.hash()).as_ref()
+ r#"","id":1}"#;
assert_eq!(tester.io.handle_request_sync(&request), Some(response));
tester.miner.increment_nonce(&address);
let t = TypedTransaction::Legacy(Transaction {
nonce: U256::one(),
gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0),
action: Action::Call(
Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(),
),
value: U256::from(0x9184e72au64),
data: vec![],
});
let signature = tester
.accounts_provider
.sign(address, None, t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None);
let response = r#"{"jsonrpc":"2.0","result":""#.to_owned()
+ format!("0x{:x}", t.hash()).as_ref()
+ r#"","id":1}"#;
assert_eq!(tester.io.handle_request_sync(&request), Some(response));
}
#[test]
fn rpc_eth_sign_transaction() {
use rustc_hex::ToHex;
let tester = EthTester::default();
let address = tester.accounts_provider.new_account(&"".into()).unwrap();
tester
.accounts_provider
.unlock_account_permanently(address, "".into())
.unwrap();
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_signTransaction",
"params": [{
"from": ""#
.to_owned()
+ format!("0x{:x}", address).as_ref()
+ r#"",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a"
}],
"id": 1
}"#;
let t = TypedTransaction::Legacy(Transaction {
nonce: U256::one(),
gas_price: U256::from(0x9184e72a000u64),
gas: U256::from(0x76c0),
action: Action::Call(
Address::from_str("d46e8dd67c5d32be8058bb8eb970870f07244567").unwrap(),
),
value: U256::from(0x9184e72au64),
data: vec![],
});
let signature = tester
.accounts_provider
.sign(address, None, t.signature_hash(None))
.unwrap();
let t = t.with_signature(signature, None);
let signature = t.signature();
let rlp = t.encode();
let response = r#"{"jsonrpc":"2.0","result":{"#.to_owned()
+ r#""raw":"0x"#
+ &rlp.to_hex()
+ r#"","#
+ r#""tx":{"#
+ r#""blockHash":null,"blockNumber":null,"#
+ &format!(
"\"chainId\":{},",
t.chain_id().map_or("null".to_owned(), |n| format!("{}", n))
)
+ r#""condition":null,"creates":null,"#
+ &format!("\"from\":\"0x{:x}\",", &address)
+ r#""gas":"0x76c0","gasPrice":"0x9184e72a000","#
+ &format!("\"hash\":\"0x{:x}\",", t.hash())
+ r#""input":"0x","#
+ r#""nonce":"0x1","#
+ &format!("\"publicKey\":\"0x{:x}\",", t.recover_public().unwrap())
+ &format!("\"r\":\"0x{:x}\",", U256::from(signature.r()))
+ &format!("\"raw\":\"0x{}\",", rlp.to_hex())
+ &format!("\"s\":\"0x{:x}\",", U256::from(signature.s()))
+ &format!("\"standardV\":\"0x{:x}\",", U256::from(t.standard_v()))
+ r#""to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","transactionIndex":null,"#
+ &format!("\"v\":\"0x{:x}\",", U256::from(t.original_v()))
+ r#""value":"0x9184e72a""#
+ r#"}},"id":1}"#;
tester.miner.increment_nonce(&address);
assert_eq!(tester.io.handle_request_sync(&request), Some(response));
}
#[test]
fn rpc_eth_send_transaction_with_bad_to() {
let tester = EthTester::default();
let address = tester.accounts_provider.new_account(&"".into()).unwrap();
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_sendTransaction",
"params": [{
"from": ""#
.to_owned()
+ format!("0x{:x}", address).as_ref()
+ r#"",
"to": "",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a"
}],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32602,"message":"Invalid params: prefix is missing."},"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.into())
);
}
#[test]
fn rpc_eth_send_transaction_error() {
let tester = EthTester::default();
let address = tester.accounts_provider.new_account(&"".into()).unwrap();
let request = r#"{
"jsonrpc": "2.0",
"method": "eth_sendTransaction",
"params": [{
"from": ""#
.to_owned()
+ format!("0x{:x}", address).as_ref()
+ r#"",
"to": "0xd46e8dd67c5d32be8058bb8eb970870f07244567",
"gas": "0x76c0",
"gasPrice": "0x9184e72a000",
"value": "0x9184e72a"
}],
"id": 1
}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32020,"message":"Your account is locked. Unlock the account via CLI, personal_unlockAccount or use Trusted Signer.","data":"NotUnlocked"},"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(&request),
Some(response.into())
);
}

View File

@@ -0,0 +1,295 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use std::sync::Arc;
use ethcore::{
client::TestBlockChainClient,
executed::{CallError, Executed},
trace::{
trace::{Action, Call, Res},
LocalizedTrace,
},
};
use vm::CallType;
use jsonrpc_core::IoHandler;
use v1::{tests::helpers::TestMinerService, Metadata, Traces, TracesClient};
struct Tester {
client: Arc<TestBlockChainClient>,
_miner: Arc<TestMinerService>,
io: IoHandler<Metadata>,
}
fn io() -> Tester {
let client = Arc::new(TestBlockChainClient::new());
*client.traces.write() = Some(vec![LocalizedTrace {
action: Action::Call(Call {
from: 0xf.into(),
to: 0x10.into(),
value: 0x1.into(),
gas: 0x100.into(),
input: vec![1, 2, 3],
call_type: CallType::Call,
}),
result: Res::None,
subtraces: 0,
trace_address: vec![0],
transaction_number: Some(0),
transaction_hash: Some(5.into()),
block_number: 10,
block_hash: 10.into(),
}]);
*client.execution_result.write() = Some(Ok(Executed {
exception: None,
gas: 20_000.into(),
gas_used: 10_000.into(),
refunded: 0.into(),
cumulative_gas_used: 10_000.into(),
logs: vec![],
contracts_created: vec![],
output: vec![1, 2, 3],
trace: vec![],
vm_trace: None,
state_diff: None,
}));
let miner = Arc::new(TestMinerService::default());
let traces = TracesClient::new(&client);
let mut io = IoHandler::default();
io.extend_with(traces.to_delegate());
Tester {
client: client,
_miner: miner,
io: io,
}
}
#[test]
fn rpc_trace_filter() {
let tester = io();
let request = r#"{"jsonrpc":"2.0","method":"trace_filter","params": [{}],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":[{"action":{"callType":"call","from":"0x000000000000000000000000000000000000000f","gas":"0x100","input":"0x010203","to":"0x0000000000000000000000000000000000000010","value":"0x1"},"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000a","blockNumber":10,"result":null,"subtraces":0,"traceAddress":[0],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionPosition":0,"type":"call"}],"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_trace_filter_missing_trace() {
let tester = io();
*tester.client.traces.write() = None;
let request = r#"{"jsonrpc":"2.0","method":"trace_filter","params": [{}],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_trace_block() {
let tester = io();
let request = r#"{"jsonrpc":"2.0","method":"trace_block","params": ["0x10"],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":[{"action":{"callType":"call","from":"0x000000000000000000000000000000000000000f","gas":"0x100","input":"0x010203","to":"0x0000000000000000000000000000000000000010","value":"0x1"},"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000a","blockNumber":10,"result":null,"subtraces":0,"traceAddress":[0],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionPosition":0,"type":"call"}],"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_trace_block_missing_traces() {
let tester = io();
*tester.client.traces.write() = None;
let request = r#"{"jsonrpc":"2.0","method":"trace_block","params": ["0x10"],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_trace_transaction() {
let tester = io();
let request = r#"{"jsonrpc":"2.0","method":"trace_transaction","params":["0x0000000000000000000000000000000000000000000000000000000000000005"],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":[{"action":{"callType":"call","from":"0x000000000000000000000000000000000000000f","gas":"0x100","input":"0x010203","to":"0x0000000000000000000000000000000000000010","value":"0x1"},"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000a","blockNumber":10,"result":null,"subtraces":0,"traceAddress":[0],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionPosition":0,"type":"call"}],"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_trace_transaction_missing_trace() {
let tester = io();
*tester.client.traces.write() = None;
let request = r#"{"jsonrpc":"2.0","method":"trace_transaction","params":["0x0000000000000000000000000000000000000000000000000000000000000005"],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_trace_get() {
let tester = io();
let request = r#"{"jsonrpc":"2.0","method":"trace_get","params":["0x0000000000000000000000000000000000000000000000000000000000000005", ["0","0","0"]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"action":{"callType":"call","from":"0x000000000000000000000000000000000000000f","gas":"0x100","input":"0x010203","to":"0x0000000000000000000000000000000000000010","value":"0x1"},"blockHash":"0x000000000000000000000000000000000000000000000000000000000000000a","blockNumber":10,"result":null,"subtraces":0,"traceAddress":[0],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000005","transactionPosition":0,"type":"call"},"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_trace_get_missing_trace() {
let tester = io();
*tester.client.traces.write() = None;
let request = r#"{"jsonrpc":"2.0","method":"trace_get","params":["0x0000000000000000000000000000000000000000000000000000000000000005", ["0","0","0"]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":null,"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_trace_call() {
let tester = io();
let request = r#"{"jsonrpc":"2.0","method":"trace_call","params":[{}, ["stateDiff", "vmTrace", "trace"]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"output":"0x010203","stateDiff":null,"trace":[],"vmTrace":null},"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_trace_multi_call() {
let tester = io();
let request = r#"{"jsonrpc":"2.0","method":"trace_callMany","params":[[[{}, ["stateDiff", "vmTrace", "trace"]]]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":[{"output":"0x010203","stateDiff":null,"trace":[],"vmTrace":null}],"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_trace_call_state_pruned() {
let tester = io();
*tester.client.execution_result.write() = Some(Err(CallError::StatePruned));
let request = r#"{"jsonrpc":"2.0","method":"trace_call","params":[{}, ["stateDiff", "vmTrace", "trace"]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive."},"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_trace_raw_transaction() {
let tester = io();
let request = r#"{"jsonrpc":"2.0","method":"trace_rawTransaction","params":["0xf869018609184e72a0008276c094d46e8dd67c5d32be8058bb8eb970870f07244567849184e72a801ba0617f39c1a107b63302449c476d96a6cb17a5842fc98ff0c5bcf4d5c4d8166b95a009fdb6097c6196b9bbafc3a59f02f38d91baeef23d0c60a8e4f23c7714cea3a9", ["stateDiff", "vmTrace", "trace"]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"output":"0x010203","stateDiff":null,"trace":[],"vmTrace":null},"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_trace_raw_transaction_state_pruned() {
let tester = io();
*tester.client.execution_result.write() = Some(Err(CallError::StatePruned));
let request = r#"{"jsonrpc":"2.0","method":"trace_rawTransaction","params":["0xf869018609184e72a0008276c094d46e8dd67c5d32be8058bb8eb970870f07244567849184e72a801ba0617f39c1a107b63302449c476d96a6cb17a5842fc98ff0c5bcf4d5c4d8166b95a009fdb6097c6196b9bbafc3a59f02f38d91baeef23d0c60a8e4f23c7714cea3a9", ["stateDiff", "vmTrace", "trace"]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive."},"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_trace_replay_transaction() {
let tester = io();
let request = r#"{"jsonrpc":"2.0","method":"trace_replayTransaction","params":["0x0000000000000000000000000000000000000000000000000000000000000005", ["trace", "stateDiff", "vmTrace"]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":{"output":"0x010203","stateDiff":null,"trace":[],"vmTrace":null},"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_trace_replay_transaction_state_pruned() {
let tester = io();
*tester.client.execution_result.write() = Some(Err(CallError::StatePruned));
let request = r#"{"jsonrpc":"2.0","method":"trace_replayTransaction","params":["0x0000000000000000000000000000000000000000000000000000000000000005", ["trace", "stateDiff", "vmTrace"]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","error":{"code":-32000,"message":"This request is not supported because your node is running with state pruning. Run with --pruning=archive."},"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}
#[test]
fn rpc_trace_replay_block_transactions() {
let tester = io();
let request = r#"{"jsonrpc":"2.0","method":"trace_replayBlockTransactions","params":["0x10", ["trace", "stateDiff", "vmTrace"]],"id":1}"#;
let response = r#"{"jsonrpc":"2.0","result":[{"output":"0x010203","stateDiff":null,"trace":[],"transactionHash":"0x0000000000000000000000000000000000000000000000000000000000000005","vmTrace":null}],"id":1}"#;
assert_eq!(
tester.io.handle_request_sync(request),
Some(response.to_owned())
);
}

View File

@@ -0,0 +1,59 @@
// Copyright 2015-2020 Parity Technologies (UK) Ltd.
// This file is part of OpenEthereum.
// OpenEthereum is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// OpenEthereum is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with OpenEthereum. If not, see <http://www.gnu.org/licenses/>.
use jsonrpc_core::IoHandler;
use v1::{Web3, Web3Client};
use version::version;
#[test]
fn rpc_web3_version() {
let web3 = Web3Client::default().to_delegate();
let mut io = IoHandler::new();
io.extend_with(web3);
let v = version().to_owned().replacen("/", "//", 1);
let request = r#"{"jsonrpc": "2.0", "method": "web3_clientVersion", "params": [], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"VER","id":1}"#
.to_owned()
.replace("VER", v.as_ref());
assert_eq!(io.handle_request_sync(request), Some(response));
}
#[test]
fn rpc_web3_sha3() {
let web3 = Web3Client::default().to_delegate();
let mut io = IoHandler::new();
io.extend_with(web3);
let request = r#"{"jsonrpc": "2.0", "method": "web3_sha3", "params": ["0x00"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"0xbc36789e7a1e281436464229828f817d6612f7b477d66591ff96a9e064bcc98a","id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}
#[test]
fn rpc_web3_sha3_wiki() {
let web3 = Web3Client::default().to_delegate();
let mut io = IoHandler::new();
io.extend_with(web3);
let request = r#"{"jsonrpc": "2.0", "method": "web3_sha3", "params": ["0x68656c6c6f20776f726c64"], "id": 1}"#;
let response = r#"{"jsonrpc":"2.0","result":"0x47173285a8d7341e5e972fc677286384f802f8ef42a5ec5f03bbfa254cb01fad","id":1}"#;
assert_eq!(io.handle_request_sync(request), Some(response.to_owned()));
}