Big folder refactor
This commit is contained in:
37
crates/rpc/src/v1/tests/mocked/debug.rs
Normal file
37
crates/rpc/src/v1/tests/mocked/debug.rs
Normal 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()));
|
||||
}
|
||||
1385
crates/rpc/src/v1/tests/mocked/eth.rs
Normal file
1385
crates/rpc/src/v1/tests/mocked/eth.rs
Normal file
File diff suppressed because it is too large
Load Diff
288
crates/rpc/src/v1/tests/mocked/eth_pubsub.rs
Normal file
288
crates/rpc/src/v1/tests/mocked/eth_pubsub.rs
Normal 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())
|
||||
);
|
||||
}
|
||||
41
crates/rpc/src/v1/tests/mocked/manage_network.rs
Normal file
41
crates/rpc/src/v1/tests/mocked/manage_network.rs
Normal 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)) {}
|
||||
}
|
||||
40
crates/rpc/src/v1/tests/mocked/mod.rs
Normal file
40
crates/rpc/src/v1/tests/mocked/mod.rs
Normal 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;
|
||||
68
crates/rpc/src/v1/tests/mocked/net.rs
Normal file
68
crates/rpc/src/v1/tests/mocked/net.rs
Normal 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()));
|
||||
}
|
||||
571
crates/rpc/src/v1/tests/mocked/parity.rs
Normal file
571
crates/rpc/src/v1/tests/mocked/parity.rs
Normal 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()));
|
||||
}
|
||||
678
crates/rpc/src/v1/tests/mocked/parity_accounts.rs
Normal file
678
crates/rpc/src/v1/tests/mocked/parity_accounts.rs
Normal 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()));
|
||||
}
|
||||
243
crates/rpc/src/v1/tests/mocked/parity_set.rs
Normal file
243
crates/rpc/src/v1/tests/mocked/parity_set.rs
Normal 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");
|
||||
}
|
||||
638
crates/rpc/src/v1/tests/mocked/personal.rs
Normal file
638
crates/rpc/src/v1/tests/mocked/personal.rs
Normal 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);
|
||||
}
|
||||
85
crates/rpc/src/v1/tests/mocked/pubsub.rs
Normal file
85
crates/rpc/src/v1/tests/mocked/pubsub.rs
Normal 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);
|
||||
}
|
||||
205
crates/rpc/src/v1/tests/mocked/secretstore.rs
Normal file
205
crates/rpc/src/v1/tests/mocked/secretstore.rs
Normal 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());
|
||||
}
|
||||
755
crates/rpc/src/v1/tests/mocked/signer.rs
Normal file
755
crates/rpc/src/v1/tests/mocked/signer.rs
Normal 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())
|
||||
);
|
||||
}
|
||||
618
crates/rpc/src/v1/tests/mocked/signing.rs
Normal file
618
crates/rpc/src/v1/tests/mocked/signing.rs
Normal 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()));
|
||||
}
|
||||
292
crates/rpc/src/v1/tests/mocked/signing_unsafe.rs
Normal file
292
crates/rpc/src/v1/tests/mocked/signing_unsafe.rs
Normal 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())
|
||||
);
|
||||
}
|
||||
295
crates/rpc/src/v1/tests/mocked/traces.rs
Normal file
295
crates/rpc/src/v1/tests/mocked/traces.rs
Normal 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())
|
||||
);
|
||||
}
|
||||
59
crates/rpc/src/v1/tests/mocked/web3.rs
Normal file
59
crates/rpc/src/v1/tests/mocked/web3.rs
Normal 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()));
|
||||
}
|
||||
Reference in New Issue
Block a user