parent
97c259858c
commit
5baed0c158
@ -471,6 +471,10 @@ usage! {
|
|||||||
"--jsonrpc-no-keep-alive",
|
"--jsonrpc-no-keep-alive",
|
||||||
"Disable HTTP/1.1 keep alive header. Disabling keep alive will prevent re-using the same TCP connection to fire multiple requests, recommended when using one request per connection.",
|
"Disable HTTP/1.1 keep alive header. Disabling keep alive will prevent re-using the same TCP connection to fire multiple requests, recommended when using one request per connection.",
|
||||||
|
|
||||||
|
FLAG flag_jsonrpc_experimental: (bool) = false, or |c: &Config| c.rpc.as_ref()?.experimental_rpcs.clone(),
|
||||||
|
"--jsonrpc-experimental",
|
||||||
|
"Enable experimental RPCs. Enable to have access to methods from unfinalised EIPs in all namespaces",
|
||||||
|
|
||||||
ARG arg_jsonrpc_port: (u16) = 8545u16, or |c: &Config| c.rpc.as_ref()?.port.clone(),
|
ARG arg_jsonrpc_port: (u16) = 8545u16, or |c: &Config| c.rpc.as_ref()?.port.clone(),
|
||||||
"--jsonrpc-port=[PORT]",
|
"--jsonrpc-port=[PORT]",
|
||||||
"Specify the port portion of the HTTP JSON-RPC API server.",
|
"Specify the port portion of the HTTP JSON-RPC API server.",
|
||||||
@ -1224,6 +1228,7 @@ struct Rpc {
|
|||||||
processing_threads: Option<usize>,
|
processing_threads: Option<usize>,
|
||||||
max_payload: Option<usize>,
|
max_payload: Option<usize>,
|
||||||
keep_alive: Option<bool>,
|
keep_alive: Option<bool>,
|
||||||
|
experimental_rpcs: Option<bool>,
|
||||||
poll_lifetime: Option<u32>,
|
poll_lifetime: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1682,6 +1687,7 @@ mod tests {
|
|||||||
// RPC
|
// RPC
|
||||||
flag_no_jsonrpc: false,
|
flag_no_jsonrpc: false,
|
||||||
flag_jsonrpc_no_keep_alive: false,
|
flag_jsonrpc_no_keep_alive: false,
|
||||||
|
flag_jsonrpc_experimental: false,
|
||||||
arg_jsonrpc_port: 8545u16,
|
arg_jsonrpc_port: 8545u16,
|
||||||
arg_jsonrpc_interface: "local".into(),
|
arg_jsonrpc_interface: "local".into(),
|
||||||
arg_jsonrpc_cors: "null".into(),
|
arg_jsonrpc_cors: "null".into(),
|
||||||
@ -1965,6 +1971,7 @@ mod tests {
|
|||||||
processing_threads: None,
|
processing_threads: None,
|
||||||
max_payload: None,
|
max_payload: None,
|
||||||
keep_alive: None,
|
keep_alive: None,
|
||||||
|
experimental_rpcs: None,
|
||||||
poll_lifetime: None,
|
poll_lifetime: None,
|
||||||
}),
|
}),
|
||||||
ipc: Some(Ipc {
|
ipc: Some(Ipc {
|
||||||
|
@ -138,6 +138,7 @@ impl Configuration {
|
|||||||
let compaction = self.args.arg_db_compaction.parse()?;
|
let compaction = self.args.arg_db_compaction.parse()?;
|
||||||
let warp_sync = !self.args.flag_no_warp;
|
let warp_sync = !self.args.flag_no_warp;
|
||||||
let geth_compatibility = self.args.flag_geth;
|
let geth_compatibility = self.args.flag_geth;
|
||||||
|
let experimental_rpcs = self.args.flag_jsonrpc_experimental;
|
||||||
let ipfs_conf = self.ipfs_config();
|
let ipfs_conf = self.ipfs_config();
|
||||||
let secretstore_conf = self.secretstore_config()?;
|
let secretstore_conf = self.secretstore_config()?;
|
||||||
let format = self.format()?;
|
let format = self.format()?;
|
||||||
@ -377,6 +378,7 @@ impl Configuration {
|
|||||||
warp_sync: warp_sync,
|
warp_sync: warp_sync,
|
||||||
warp_barrier: self.args.arg_warp_barrier,
|
warp_barrier: self.args.arg_warp_barrier,
|
||||||
geth_compatibility: geth_compatibility,
|
geth_compatibility: geth_compatibility,
|
||||||
|
experimental_rpcs,
|
||||||
net_settings: self.network_settings()?,
|
net_settings: self.network_settings()?,
|
||||||
ipfs_conf: ipfs_conf,
|
ipfs_conf: ipfs_conf,
|
||||||
secretstore_conf: secretstore_conf,
|
secretstore_conf: secretstore_conf,
|
||||||
@ -1424,6 +1426,7 @@ mod tests {
|
|||||||
compaction: Default::default(),
|
compaction: Default::default(),
|
||||||
vm_type: Default::default(),
|
vm_type: Default::default(),
|
||||||
geth_compatibility: false,
|
geth_compatibility: false,
|
||||||
|
experimental_rpcs: false,
|
||||||
net_settings: Default::default(),
|
net_settings: Default::default(),
|
||||||
ipfs_conf: Default::default(),
|
ipfs_conf: Default::default(),
|
||||||
secretstore_conf: Default::default(),
|
secretstore_conf: Default::default(),
|
||||||
|
@ -228,6 +228,7 @@ pub struct FullDependencies {
|
|||||||
pub net_service: Arc<ManageNetwork>,
|
pub net_service: Arc<ManageNetwork>,
|
||||||
pub updater: Arc<Updater>,
|
pub updater: Arc<Updater>,
|
||||||
pub geth_compatibility: bool,
|
pub geth_compatibility: bool,
|
||||||
|
pub experimental_rpcs: bool,
|
||||||
pub ws_address: Option<Host>,
|
pub ws_address: Option<Host>,
|
||||||
pub fetch: FetchClient,
|
pub fetch: FetchClient,
|
||||||
pub executor: Executor,
|
pub executor: Executor,
|
||||||
@ -316,7 +317,7 @@ impl FullDependencies {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
Api::Personal => {
|
Api::Personal => {
|
||||||
handler.extend_with(PersonalClient::new(&self.secret_store, dispatcher.clone(), self.geth_compatibility).to_delegate());
|
handler.extend_with(PersonalClient::new(&self.secret_store, dispatcher.clone(), self.geth_compatibility, self.experimental_rpcs).to_delegate());
|
||||||
},
|
},
|
||||||
Api::Signer => {
|
Api::Signer => {
|
||||||
handler.extend_with(SignerClient::new(&self.secret_store, dispatcher.clone(), &self.signer_service, self.executor.clone()).to_delegate());
|
handler.extend_with(SignerClient::new(&self.secret_store, dispatcher.clone(), &self.signer_service, self.executor.clone()).to_delegate());
|
||||||
@ -438,6 +439,7 @@ pub struct LightDependencies<T> {
|
|||||||
pub ws_address: Option<Host>,
|
pub ws_address: Option<Host>,
|
||||||
pub fetch: FetchClient,
|
pub fetch: FetchClient,
|
||||||
pub geth_compatibility: bool,
|
pub geth_compatibility: bool,
|
||||||
|
pub experimental_rpcs: bool,
|
||||||
pub executor: Executor,
|
pub executor: Executor,
|
||||||
pub whisper_rpc: Option<::whisper::RpcFactory>,
|
pub whisper_rpc: Option<::whisper::RpcFactory>,
|
||||||
pub private_tx_service: Option<Arc<PrivateTransactionManager>>,
|
pub private_tx_service: Option<Arc<PrivateTransactionManager>>,
|
||||||
@ -531,7 +533,7 @@ impl<C: LightChainClient + 'static> LightDependencies<C> {
|
|||||||
handler.extend_with(EthPubSub::to_delegate(client));
|
handler.extend_with(EthPubSub::to_delegate(client));
|
||||||
},
|
},
|
||||||
Api::Personal => {
|
Api::Personal => {
|
||||||
handler.extend_with(PersonalClient::new(&self.secret_store, dispatcher.clone(), self.geth_compatibility).to_delegate());
|
handler.extend_with(PersonalClient::new(&self.secret_store, dispatcher.clone(), self.geth_compatibility, self.experimental_rpcs).to_delegate());
|
||||||
},
|
},
|
||||||
Api::Signer => {
|
Api::Signer => {
|
||||||
handler.extend_with(SignerClient::new(&self.secret_store, dispatcher.clone(), &self.signer_service, self.executor.clone()).to_delegate());
|
handler.extend_with(SignerClient::new(&self.secret_store, dispatcher.clone(), &self.signer_service, self.executor.clone()).to_delegate());
|
||||||
|
@ -115,6 +115,7 @@ pub struct RunCmd {
|
|||||||
pub compaction: DatabaseCompactionProfile,
|
pub compaction: DatabaseCompactionProfile,
|
||||||
pub vm_type: VMType,
|
pub vm_type: VMType,
|
||||||
pub geth_compatibility: bool,
|
pub geth_compatibility: bool,
|
||||||
|
pub experimental_rpcs: bool,
|
||||||
pub net_settings: NetworkSettings,
|
pub net_settings: NetworkSettings,
|
||||||
pub ipfs_conf: ipfs::Configuration,
|
pub ipfs_conf: ipfs::Configuration,
|
||||||
pub secretstore_conf: secretstore::Configuration,
|
pub secretstore_conf: secretstore::Configuration,
|
||||||
@ -312,6 +313,7 @@ fn execute_light_impl(cmd: RunCmd, logger: Arc<RotatingLogger>) -> Result<Runnin
|
|||||||
ws_address: cmd.ws_conf.address(),
|
ws_address: cmd.ws_conf.address(),
|
||||||
fetch: fetch,
|
fetch: fetch,
|
||||||
geth_compatibility: cmd.geth_compatibility,
|
geth_compatibility: cmd.geth_compatibility,
|
||||||
|
experimental_rpcs: cmd.experimental_rpcs,
|
||||||
executor: runtime.executor(),
|
executor: runtime.executor(),
|
||||||
whisper_rpc: whisper_factory,
|
whisper_rpc: whisper_factory,
|
||||||
private_tx_service: None, //TODO: add this to client.
|
private_tx_service: None, //TODO: add this to client.
|
||||||
@ -712,6 +714,7 @@ fn execute_impl<Cr, Rr>(cmd: RunCmd, logger: Arc<RotatingLogger>, on_client_rq:
|
|||||||
net_service: manage_network.clone(),
|
net_service: manage_network.clone(),
|
||||||
updater: updater.clone(),
|
updater: updater.clone(),
|
||||||
geth_compatibility: cmd.geth_compatibility,
|
geth_compatibility: cmd.geth_compatibility,
|
||||||
|
experimental_rpcs: cmd.experimental_rpcs,
|
||||||
ws_address: cmd.ws_conf.address(),
|
ws_address: cmd.ws_conf.address(),
|
||||||
fetch: fetch.clone(),
|
fetch: fetch.clone(),
|
||||||
executor: runtime.executor(),
|
executor: runtime.executor(),
|
||||||
|
@ -54,6 +54,7 @@ mod codes {
|
|||||||
pub const NO_LIGHT_PEERS: i64 = -32065;
|
pub const NO_LIGHT_PEERS: i64 = -32065;
|
||||||
pub const NO_PEERS: i64 = -32066;
|
pub const NO_PEERS: i64 = -32066;
|
||||||
pub const DEPRECATED: i64 = -32070;
|
pub const DEPRECATED: i64 = -32070;
|
||||||
|
pub const EXPERIMENTAL_RPC: i64 = -32071;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unimplemented(details: Option<String>) -> Error {
|
pub fn unimplemented(details: Option<String>) -> Error {
|
||||||
@ -513,3 +514,15 @@ pub fn status_error(has_peers: bool) -> Error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns a descriptive error in case experimental RPCs are not enabled.
|
||||||
|
pub fn require_experimental(allow_experimental_rpcs: bool, eip: &str) -> Result<(), Error> {
|
||||||
|
if allow_experimental_rpcs {
|
||||||
|
Ok(())
|
||||||
|
} else {
|
||||||
|
Err(Error {
|
||||||
|
code: ErrorCode::ServerError(codes::EXPERIMENTAL_RPC),
|
||||||
|
message: format!("This method is not part of the official RPC API yet (EIP-{}). Run with `--jsonrpc-experimental` to enable it.", eip),
|
||||||
|
data: Some(Value::String(format!("See EIP: https://eips.ethereum.org/EIPS/eip-{}", eip))),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -47,15 +47,22 @@ pub struct PersonalClient<D: Dispatcher> {
|
|||||||
accounts: Arc<AccountProvider>,
|
accounts: Arc<AccountProvider>,
|
||||||
dispatcher: D,
|
dispatcher: D,
|
||||||
allow_perm_unlock: bool,
|
allow_perm_unlock: bool,
|
||||||
|
allow_experimental_rpcs: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<D: Dispatcher> PersonalClient<D> {
|
impl<D: Dispatcher> PersonalClient<D> {
|
||||||
/// Creates new PersonalClient
|
/// Creates new PersonalClient
|
||||||
pub fn new(accounts: &Arc<AccountProvider>, dispatcher: D, allow_perm_unlock: bool) -> Self {
|
pub fn new(
|
||||||
|
accounts: &Arc<AccountProvider>,
|
||||||
|
dispatcher: D,
|
||||||
|
allow_perm_unlock: bool,
|
||||||
|
allow_experimental_rpcs: bool,
|
||||||
|
) -> Self {
|
||||||
PersonalClient {
|
PersonalClient {
|
||||||
accounts: accounts.clone(),
|
accounts: accounts.clone(),
|
||||||
dispatcher,
|
dispatcher,
|
||||||
allow_perm_unlock,
|
allow_perm_unlock,
|
||||||
|
allow_experimental_rpcs,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -154,6 +161,8 @@ impl<D: Dispatcher + 'static> Personal for PersonalClient<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sign_191(&self, version: EIP191Version, data: Value, account: RpcH160, password: String) -> BoxFuture<RpcH520> {
|
fn sign_191(&self, version: EIP191Version, data: Value, account: RpcH160, password: String) -> BoxFuture<RpcH520> {
|
||||||
|
try_bf!(errors::require_experimental(self.allow_experimental_rpcs, "191"));
|
||||||
|
|
||||||
let data = try_bf!(eip191::hash_message(version, data));
|
let data = try_bf!(eip191::hash_message(version, data));
|
||||||
let dispatcher = self.dispatcher.clone();
|
let dispatcher = self.dispatcher.clone();
|
||||||
let accounts = self.accounts.clone();
|
let accounts = self.accounts.clone();
|
||||||
@ -174,6 +183,8 @@ impl<D: Dispatcher + 'static> Personal for PersonalClient<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sign_typed_data(&self, typed_data: EIP712, account: RpcH160, password: String) -> BoxFuture<RpcH520> {
|
fn sign_typed_data(&self, typed_data: EIP712, account: RpcH160, password: String) -> BoxFuture<RpcH520> {
|
||||||
|
try_bf!(errors::require_experimental(self.allow_experimental_rpcs, "712"));
|
||||||
|
|
||||||
let data = match hash_structured_data(typed_data) {
|
let data = match hash_structured_data(typed_data) {
|
||||||
Ok(d) => d,
|
Ok(d) => d,
|
||||||
Err(err) => return Box::new(future::err(errors::invalid_call_data(err.kind()))),
|
Err(err) => return Box::new(future::err(errors::invalid_call_data(err.kind()))),
|
||||||
|
@ -57,6 +57,16 @@ fn miner_service() -> Arc<TestMinerService> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn setup() -> PersonalTester {
|
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 runtime = Runtime::with_thread_count(1);
|
||||||
let accounts = accounts_provider();
|
let accounts = accounts_provider();
|
||||||
let client = blockchain_client();
|
let client = blockchain_client();
|
||||||
@ -64,7 +74,7 @@ fn setup() -> PersonalTester {
|
|||||||
let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor())));
|
let reservations = Arc::new(Mutex::new(nonce::Reservations::new(runtime.executor())));
|
||||||
|
|
||||||
let dispatcher = FullDispatcher::new(client, miner.clone(), reservations, 50);
|
let dispatcher = FullDispatcher::new(client, miner.clone(), reservations, 50);
|
||||||
let personal = PersonalClient::new(&accounts, dispatcher, false);
|
let personal = PersonalClient::new(&accounts, dispatcher, false, c.allow_experimental_rpcs);
|
||||||
|
|
||||||
let mut io = IoHandler::default();
|
let mut io = IoHandler::default();
|
||||||
io.extend_with(personal.to_delegate());
|
io.extend_with(personal.to_delegate());
|
||||||
@ -418,3 +428,109 @@ fn sign_eip191_structured_data() {
|
|||||||
let response = tester.io.handle_request_sync(&request).unwrap();
|
let response = tester.io.handle_request_sync(&request).unwrap();
|
||||||
assert_eq!(response, expected)
|
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);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user