Signer provenance (#4477)
* Signer - Tracking Request Provenance * Basic UI * Changing messages * VecDeque::from * Fix dapps tests * Addressing UI grumbles
This commit is contained in:
parent
d925cc05da
commit
5369a129ae
3
Cargo.lock
generated
3
Cargo.lock
generated
@ -25,10 +25,9 @@ dependencies = [
|
|||||||
"ethcore-util 1.6.0",
|
"ethcore-util 1.6.0",
|
||||||
"ethsync 1.6.0",
|
"ethsync 1.6.0",
|
||||||
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"fdlimit 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"hyper 0.9.14 (registry+https://github.com/rust-lang/crates.io-index)",
|
"hyper 0.10.0-a.0 (git+https://github.com/ethcore/hyper)",
|
||||||
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
"isatty 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
"jsonrpc-core 6.0.0 (git+https://github.com/ethcore/jsonrpc.git)",
|
||||||
"lazy_static 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
|
|
||||||
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
"log 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
"num_cpus 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
"number_prefix 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)",
|
||||||
|
@ -16,7 +16,6 @@ number_prefix = "0.2"
|
|||||||
rpassword = "0.2.1"
|
rpassword = "0.2.1"
|
||||||
semver = "0.5"
|
semver = "0.5"
|
||||||
ansi_term = "0.7"
|
ansi_term = "0.7"
|
||||||
lazy_static = "0.2"
|
|
||||||
regex = "0.1"
|
regex = "0.1"
|
||||||
isatty = "0.1"
|
isatty = "0.1"
|
||||||
toml = "0.2"
|
toml = "0.2"
|
||||||
@ -24,7 +23,7 @@ serde = "0.9"
|
|||||||
serde_json = "0.9"
|
serde_json = "0.9"
|
||||||
app_dirs = "1.1.1"
|
app_dirs = "1.1.1"
|
||||||
fdlimit = "0.1"
|
fdlimit = "0.1"
|
||||||
hyper = { version = "0.9", default-features = false }
|
hyper = { default-features = false, git = "https://github.com/ethcore/hyper" }
|
||||||
ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" }
|
ctrlc = { git = "https://github.com/ethcore/rust-ctrlc.git" }
|
||||||
jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" }
|
jsonrpc-core = { git = "https://github.com/ethcore/jsonrpc.git" }
|
||||||
ethsync = { path = "sync" }
|
ethsync = { path = "sync" }
|
||||||
|
@ -77,8 +77,7 @@ impl HttpMetaExtractor<Metadata> for MetadataExtractor {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
Metadata {
|
Metadata {
|
||||||
dapp_id: dapp_id,
|
origin: Origin::Dapps(dapp_id.map(Into::into).unwrap_or_default()),
|
||||||
origin: Origin::Dapps,
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,8 +55,8 @@ fn should_extract_metadata() {
|
|||||||
// given
|
// given
|
||||||
let mut io = MetaIoHandler::default();
|
let mut io = MetaIoHandler::default();
|
||||||
io.add_method_with_meta("rpc_test", |_params, meta: Metadata| {
|
io.add_method_with_meta("rpc_test", |_params, meta: Metadata| {
|
||||||
assert_eq!(meta.dapp_id, Some("https://parity.io/".to_owned()));
|
assert_eq!(meta.origin, Origin::Dapps("https://parity.io/".into()));
|
||||||
assert_eq!(meta.origin, Origin::Dapps);
|
assert_eq!(meta.dapp_id(), "https://parity.io/".into());
|
||||||
future::ok(Value::String("Hello World!".into())).boxed()
|
future::ok(Value::String("Hello World!".into())).boxed()
|
||||||
});
|
});
|
||||||
let server = serve_with_rpc(io);
|
let server = serve_with_rpc(io);
|
||||||
@ -89,8 +89,8 @@ fn should_extract_metadata_from_custom_header() {
|
|||||||
// given
|
// given
|
||||||
let mut io = MetaIoHandler::default();
|
let mut io = MetaIoHandler::default();
|
||||||
io.add_method_with_meta("rpc_test", |_params, meta: Metadata| {
|
io.add_method_with_meta("rpc_test", |_params, meta: Metadata| {
|
||||||
assert_eq!(meta.dapp_id, Some("https://parity.io/".to_owned()));
|
assert_eq!(meta.origin, Origin::Dapps("https://parity.io/".into()));
|
||||||
assert_eq!(meta.origin, Origin::Dapps);
|
assert_eq!(meta.dapp_id(), "https://parity.io/".into());
|
||||||
future::ok(Value::String("Hello World!".into())).boxed()
|
future::ok(Value::String("Hello World!".into())).boxed()
|
||||||
});
|
});
|
||||||
let server = serve_with_rpc(io);
|
let server = serve_with_rpc(io);
|
||||||
|
@ -649,8 +649,8 @@ mod tests {
|
|||||||
use account_provider::AccountProvider;
|
use account_provider::AccountProvider;
|
||||||
use spec::Spec;
|
use spec::Spec;
|
||||||
use engines::{Engine, EngineError, Seal};
|
use engines::{Engine, EngineError, Seal};
|
||||||
use super::*;
|
use super::{Step, View, Height, message_info_rlp, message_full_rlp};
|
||||||
use super::message::*;
|
use super::message::VoteStep;
|
||||||
|
|
||||||
/// Accounts inserted with "0" and "1" are validators. First proposer is "0".
|
/// Accounts inserted with "0" and "1" are validators. First proposer is "0".
|
||||||
fn setup() -> (Spec, Arc<AccountProvider>) {
|
fn setup() -> (Spec, Arc<AccountProvider>) {
|
||||||
|
@ -305,7 +305,7 @@ impl<T> TraceDatabase for TraceDB<T> where T: DatabaseExtras {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace> {
|
fn trace(&self, block_number: BlockNumber, tx_position: usize, trace_position: Vec<usize>) -> Option<LocalizedTrace> {
|
||||||
let trace_position_deq = trace_position.into_iter().collect::<VecDeque<usize>>();
|
let trace_position_deq = VecDeque::from(trace_position);
|
||||||
self.extras.block_hash(block_number)
|
self.extras.block_hash(block_number)
|
||||||
.and_then(|block_hash| self.transactions_traces(&block_hash)
|
.and_then(|block_hash| self.transactions_traces(&block_hash)
|
||||||
.and_then(|traces| traces.into_iter().nth(tx_position))
|
.and_then(|traces| traces.into_iter().nth(tx_position))
|
||||||
|
@ -203,6 +203,13 @@ export function outSignerRequest (request) {
|
|||||||
request[key].signTransaction = outTransaction(request[key].signTransaction);
|
request[key].signTransaction = outTransaction(request[key].signTransaction);
|
||||||
request[key].sendTransaction = outTransaction(request[key].sendTransaction);
|
request[key].sendTransaction = outTransaction(request[key].sendTransaction);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'origin':
|
||||||
|
const type = Object.keys(request[key])[0];
|
||||||
|
const details = request[key][type];
|
||||||
|
|
||||||
|
request[key] = { type, details };
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -28,6 +28,7 @@ export default class Ws extends JsonRpcBase {
|
|||||||
this._url = url;
|
this._url = url;
|
||||||
this._token = token;
|
this._token = token;
|
||||||
this._messages = {};
|
this._messages = {};
|
||||||
|
this._sessionHash = null;
|
||||||
|
|
||||||
this._connecting = false;
|
this._connecting = false;
|
||||||
this._connected = false;
|
this._connected = false;
|
||||||
@ -78,12 +79,14 @@ export default class Ws extends JsonRpcBase {
|
|||||||
this._ws.onmessage = null;
|
this._ws.onmessage = null;
|
||||||
this._ws.close();
|
this._ws.close();
|
||||||
this._ws = null;
|
this._ws = null;
|
||||||
|
this._sessionHash = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
this._connecting = true;
|
this._connecting = true;
|
||||||
this._connected = false;
|
this._connected = false;
|
||||||
this._lastError = null;
|
this._lastError = null;
|
||||||
|
|
||||||
|
this._sessionHash = sha3;
|
||||||
this._ws = new WebSocket(this._url, hash);
|
this._ws = new WebSocket(this._url, hash);
|
||||||
this._ws.onerror = this._onError;
|
this._ws.onerror = this._onError;
|
||||||
this._ws.onopen = this._onOpen;
|
this._ws.onopen = this._onOpen;
|
||||||
@ -255,6 +258,10 @@ export default class Ws extends JsonRpcBase {
|
|||||||
return this._token;
|
return this._token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get sessionHash () {
|
||||||
|
return this._sessionHash;
|
||||||
|
}
|
||||||
|
|
||||||
get isAutoConnect () {
|
get isAutoConnect () {
|
||||||
return this._autoConnect;
|
return this._autoConnect;
|
||||||
}
|
}
|
||||||
|
17
js/src/views/Signer/components/RequestOrigin/index.js
Normal file
17
js/src/views/Signer/components/RequestOrigin/index.js
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
export default from './requestOrigin';
|
@ -0,0 +1,43 @@
|
|||||||
|
/* Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
/* This file is part of Parity.
|
||||||
|
/*
|
||||||
|
/* Parity 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.
|
||||||
|
/*
|
||||||
|
/* Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
.container {
|
||||||
|
text-align: left;
|
||||||
|
margin: 3em .5em;
|
||||||
|
opacity: 0.6;
|
||||||
|
font-size: 0.8em;
|
||||||
|
|
||||||
|
.unknown {
|
||||||
|
color: #e44;
|
||||||
|
}
|
||||||
|
|
||||||
|
.url {
|
||||||
|
max-width: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hash {
|
||||||
|
margin-left: .2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hash, .url {
|
||||||
|
margin-bottom: -.2em;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
}
|
116
js/src/views/Signer/components/RequestOrigin/requestOrigin.js
Normal file
116
js/src/views/Signer/components/RequestOrigin/requestOrigin.js
Normal file
@ -0,0 +1,116 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import React, { Component, PropTypes } from 'react';
|
||||||
|
|
||||||
|
import IdentityIcon from '~/ui/IdentityIcon';
|
||||||
|
|
||||||
|
import styles from './requestOrigin.css';
|
||||||
|
|
||||||
|
export default class RequestOrigin extends Component {
|
||||||
|
static contextTypes = {
|
||||||
|
api: PropTypes.object.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
static propTypes = {
|
||||||
|
origin: PropTypes.shape({
|
||||||
|
type: PropTypes.oneOf(['unknown', 'dapp', 'rpc', 'ipc', 'signer']),
|
||||||
|
details: PropTypes.string.isRequired
|
||||||
|
}).isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
render () {
|
||||||
|
const { origin } = this.props;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={ styles.container }>
|
||||||
|
Requested { this.renderOrigin(origin) }
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderOrigin (origin) {
|
||||||
|
if (origin.type === 'unknown') {
|
||||||
|
return (
|
||||||
|
<span className={ styles.unknown }>via unknown interface</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (origin.type === 'dapp') {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
by a dapp at <span className={ styles.url }>
|
||||||
|
{ origin.details || 'unknown URL' }
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (origin.type === 'rpc') {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
via RPC <span className={ styles.url }>
|
||||||
|
({ origin.details || 'unidentified' })
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (origin.type === 'ipc') {
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
via IPC session
|
||||||
|
<span
|
||||||
|
className={ styles.hash }
|
||||||
|
title={ origin.details }
|
||||||
|
>
|
||||||
|
<IdentityIcon
|
||||||
|
address={ origin.details }
|
||||||
|
tiny
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (origin.type === 'signer') {
|
||||||
|
return this.renderSigner(origin.details);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
renderSigner (session) {
|
||||||
|
if (session.substr(2) === this.context.api.transport.sessionHash) {
|
||||||
|
return (
|
||||||
|
<span title={ session }>via current tab</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<span>
|
||||||
|
via UI session
|
||||||
|
<span
|
||||||
|
className={ styles.hash }
|
||||||
|
title={ `UI Session id: ${session}` }
|
||||||
|
>
|
||||||
|
<IdentityIcon
|
||||||
|
address={ session }
|
||||||
|
tiny
|
||||||
|
/>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,72 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import { shallow } from 'enzyme';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
|
import RequestOrigin from './';
|
||||||
|
|
||||||
|
const context = {
|
||||||
|
context: {
|
||||||
|
api: {
|
||||||
|
transport: {
|
||||||
|
sessionHash: '1234'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('views/Signer/components/RequestOrigin', () => {
|
||||||
|
it('renders unknown', () => {
|
||||||
|
expect(shallow(
|
||||||
|
<RequestOrigin origin={ { type: 'unknown', details: '' } } />,
|
||||||
|
context
|
||||||
|
).text()).to.equal('Requested via unknown interface');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders dapps', () => {
|
||||||
|
expect(shallow(
|
||||||
|
<RequestOrigin origin={ { type: 'dapp', details: 'http://parity.io' } } />,
|
||||||
|
context
|
||||||
|
).text()).to.equal('Requested by a dapp at http://parity.io');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders rpc', () => {
|
||||||
|
expect(shallow(
|
||||||
|
<RequestOrigin origin={ { type: 'rpc', details: '' } } />,
|
||||||
|
context
|
||||||
|
).text()).to.equal('Requested via RPC (unidentified)');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders ipc', () => {
|
||||||
|
expect(shallow(
|
||||||
|
<RequestOrigin origin={ { type: 'ipc', details: '0x1234' } } />,
|
||||||
|
context
|
||||||
|
).text()).to.equal('Requested via IPC session<Connect(IdentityIcon) />');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('renders signer', () => {
|
||||||
|
expect(shallow(
|
||||||
|
<RequestOrigin origin={ { type: 'signer', details: '0x12345' } } />,
|
||||||
|
context
|
||||||
|
).text()).to.equal('Requested via UI session<Connect(IdentityIcon) />');
|
||||||
|
|
||||||
|
expect(shallow(
|
||||||
|
<RequestOrigin origin={ { type: 'signer', details: '0x1234' } } />,
|
||||||
|
context
|
||||||
|
).text()).to.equal('Requested via current tab');
|
||||||
|
});
|
||||||
|
});
|
@ -30,6 +30,7 @@ export default class RequestPending extends Component {
|
|||||||
isTest: PropTypes.bool.isRequired,
|
isTest: PropTypes.bool.isRequired,
|
||||||
onConfirm: PropTypes.func.isRequired,
|
onConfirm: PropTypes.func.isRequired,
|
||||||
onReject: PropTypes.func.isRequired,
|
onReject: PropTypes.func.isRequired,
|
||||||
|
origin: PropTypes.object.isRequired,
|
||||||
payload: PropTypes.oneOfType([
|
payload: PropTypes.oneOfType([
|
||||||
PropTypes.shape({ sendTransaction: PropTypes.object.isRequired }),
|
PropTypes.shape({ sendTransaction: PropTypes.object.isRequired }),
|
||||||
PropTypes.shape({ sign: PropTypes.object.isRequired }),
|
PropTypes.shape({ sign: PropTypes.object.isRequired }),
|
||||||
@ -51,7 +52,7 @@ export default class RequestPending extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { className, date, focus, gasLimit, id, isSending, isTest, onReject, payload, store } = this.props;
|
const { className, date, focus, gasLimit, id, isSending, isTest, onReject, payload, store, origin } = this.props;
|
||||||
|
|
||||||
if (payload.sign) {
|
if (payload.sign) {
|
||||||
const { sign } = payload;
|
const { sign } = payload;
|
||||||
@ -68,6 +69,7 @@ export default class RequestPending extends Component {
|
|||||||
isTest={ isTest }
|
isTest={ isTest }
|
||||||
onConfirm={ this.onConfirm }
|
onConfirm={ this.onConfirm }
|
||||||
onReject={ onReject }
|
onReject={ onReject }
|
||||||
|
origin={ origin }
|
||||||
store={ store }
|
store={ store }
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@ -87,6 +89,7 @@ export default class RequestPending extends Component {
|
|||||||
isTest={ isTest }
|
isTest={ isTest }
|
||||||
onConfirm={ this.onConfirm }
|
onConfirm={ this.onConfirm }
|
||||||
onReject={ onReject }
|
onReject={ onReject }
|
||||||
|
origin={ origin }
|
||||||
store={ store }
|
store={ store }
|
||||||
transaction={ transaction }
|
transaction={ transaction }
|
||||||
/>
|
/>
|
||||||
|
@ -19,6 +19,7 @@ import { observer } from 'mobx-react';
|
|||||||
|
|
||||||
import Account from '../Account';
|
import Account from '../Account';
|
||||||
import TransactionPendingForm from '../TransactionPendingForm';
|
import TransactionPendingForm from '../TransactionPendingForm';
|
||||||
|
import RequestOrigin from '../RequestOrigin';
|
||||||
|
|
||||||
import styles from './signRequest.css';
|
import styles from './signRequest.css';
|
||||||
|
|
||||||
@ -40,9 +41,9 @@ export default class SignRequest extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static propTypes = {
|
static propTypes = {
|
||||||
id: PropTypes.object.isRequired,
|
|
||||||
address: PropTypes.string.isRequired,
|
address: PropTypes.string.isRequired,
|
||||||
data: PropTypes.string.isRequired,
|
data: PropTypes.string.isRequired,
|
||||||
|
id: PropTypes.object.isRequired,
|
||||||
isFinished: PropTypes.bool.isRequired,
|
isFinished: PropTypes.bool.isRequired,
|
||||||
isTest: PropTypes.bool.isRequired,
|
isTest: PropTypes.bool.isRequired,
|
||||||
store: PropTypes.object.isRequired,
|
store: PropTypes.object.isRequired,
|
||||||
@ -52,11 +53,16 @@ export default class SignRequest extends Component {
|
|||||||
isSending: PropTypes.bool,
|
isSending: PropTypes.bool,
|
||||||
onConfirm: PropTypes.func,
|
onConfirm: PropTypes.func,
|
||||||
onReject: PropTypes.func,
|
onReject: PropTypes.func,
|
||||||
|
origin: PropTypes.any,
|
||||||
status: PropTypes.string
|
status: PropTypes.string
|
||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
focus: false
|
focus: false,
|
||||||
|
origin: {
|
||||||
|
type: 'unknown',
|
||||||
|
details: ''
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount () {
|
||||||
@ -92,7 +98,7 @@ export default class SignRequest extends Component {
|
|||||||
|
|
||||||
renderDetails () {
|
renderDetails () {
|
||||||
const { api } = this.context;
|
const { api } = this.context;
|
||||||
const { address, isTest, store, data } = this.props;
|
const { address, isTest, store, data, origin } = this.props;
|
||||||
const { balances, externalLink } = store;
|
const { balances, externalLink } = store;
|
||||||
|
|
||||||
const balance = balances[address];
|
const balance = balances[address];
|
||||||
@ -110,6 +116,7 @@ export default class SignRequest extends Component {
|
|||||||
externalLink={ externalLink }
|
externalLink={ externalLink }
|
||||||
isTest={ isTest }
|
isTest={ isTest }
|
||||||
/>
|
/>
|
||||||
|
<RequestOrigin origin={ origin } />
|
||||||
</div>
|
</div>
|
||||||
<div className={ styles.info } title={ api.util.sha3(data) }>
|
<div className={ styles.info } title={ api.util.sha3(data) }>
|
||||||
<p>A request to sign data using your account:</p>
|
<p>A request to sign data using your account:</p>
|
||||||
|
@ -39,15 +39,17 @@
|
|||||||
width: 40%;
|
width: 40%;
|
||||||
vertical-align: top;
|
vertical-align: top;
|
||||||
|
|
||||||
img {
|
.account {
|
||||||
display: inline-block;
|
img {
|
||||||
width: 50px;
|
display: inline-block;
|
||||||
height: 50px;
|
width: 50px;
|
||||||
margin: 5px;
|
height: 50px;
|
||||||
}
|
margin: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
display: block;
|
display: block;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -22,6 +22,8 @@ import { Button, MethodDecoding } from '~/ui';
|
|||||||
|
|
||||||
import * as tUtil from '../util/transaction';
|
import * as tUtil from '../util/transaction';
|
||||||
import Account from '../Account';
|
import Account from '../Account';
|
||||||
|
import RequestOrigin from '../RequestOrigin';
|
||||||
|
|
||||||
import styles from './transactionMainDetails.css';
|
import styles from './transactionMainDetails.css';
|
||||||
|
|
||||||
export default class TransactionMainDetails extends Component {
|
export default class TransactionMainDetails extends Component {
|
||||||
@ -33,11 +35,19 @@ export default class TransactionMainDetails extends Component {
|
|||||||
gasStore: PropTypes.object,
|
gasStore: PropTypes.object,
|
||||||
id: PropTypes.object.isRequired,
|
id: PropTypes.object.isRequired,
|
||||||
isTest: PropTypes.bool.isRequired,
|
isTest: PropTypes.bool.isRequired,
|
||||||
|
origin: PropTypes.any,
|
||||||
totalValue: PropTypes.object.isRequired,
|
totalValue: PropTypes.object.isRequired,
|
||||||
transaction: PropTypes.object.isRequired,
|
transaction: PropTypes.object.isRequired,
|
||||||
value: PropTypes.object.isRequired
|
value: PropTypes.object.isRequired
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static defaultProps = {
|
||||||
|
origin: {
|
||||||
|
type: 'unknown',
|
||||||
|
details: ''
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
componentWillMount () {
|
componentWillMount () {
|
||||||
const { totalValue, value } = this.props;
|
const { totalValue, value } = this.props;
|
||||||
|
|
||||||
@ -51,7 +61,7 @@ export default class TransactionMainDetails extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { children, externalLink, from, fromBalance, gasStore, isTest, transaction } = this.props;
|
const { children, externalLink, from, fromBalance, gasStore, isTest, transaction, origin } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ styles.transaction }>
|
<div className={ styles.transaction }>
|
||||||
@ -64,6 +74,7 @@ export default class TransactionMainDetails extends Component {
|
|||||||
isTest={ isTest }
|
isTest={ isTest }
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<RequestOrigin origin={ origin } />
|
||||||
</div>
|
</div>
|
||||||
<div className={ styles.method }>
|
<div className={ styles.method }>
|
||||||
<MethodDecoding
|
<MethodDecoding
|
||||||
|
@ -43,6 +43,7 @@ export default class TransactionPending extends Component {
|
|||||||
nonce: PropTypes.number,
|
nonce: PropTypes.number,
|
||||||
onConfirm: PropTypes.func.isRequired,
|
onConfirm: PropTypes.func.isRequired,
|
||||||
onReject: PropTypes.func.isRequired,
|
onReject: PropTypes.func.isRequired,
|
||||||
|
origin: PropTypes.any,
|
||||||
store: PropTypes.object.isRequired,
|
store: PropTypes.object.isRequired,
|
||||||
transaction: PropTypes.shape({
|
transaction: PropTypes.shape({
|
||||||
condition: PropTypes.object,
|
condition: PropTypes.object,
|
||||||
@ -56,7 +57,11 @@ export default class TransactionPending extends Component {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static defaultProps = {
|
static defaultProps = {
|
||||||
focus: false
|
focus: false,
|
||||||
|
origin: {
|
||||||
|
type: 'unknown',
|
||||||
|
details: ''
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
gasStore = new GasPriceEditor.Store(this.context.api, {
|
gasStore = new GasPriceEditor.Store(this.context.api, {
|
||||||
@ -87,7 +92,7 @@ export default class TransactionPending extends Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
renderTransaction () {
|
renderTransaction () {
|
||||||
const { className, focus, id, isSending, isTest, store, transaction } = this.props;
|
const { className, focus, id, isSending, isTest, store, transaction, origin } = this.props;
|
||||||
const { totalValue } = this.state;
|
const { totalValue } = this.state;
|
||||||
const { balances, externalLink } = store;
|
const { balances, externalLink } = store;
|
||||||
const { from, value } = transaction;
|
const { from, value } = transaction;
|
||||||
@ -104,6 +109,7 @@ export default class TransactionPending extends Component {
|
|||||||
gasStore={ this.gasStore }
|
gasStore={ this.gasStore }
|
||||||
id={ id }
|
id={ id }
|
||||||
isTest={ isTest }
|
isTest={ isTest }
|
||||||
|
origin={ origin }
|
||||||
totalValue={ totalValue }
|
totalValue={ totalValue }
|
||||||
transaction={ transaction }
|
transaction={ transaction }
|
||||||
value={ value }
|
value={ value }
|
||||||
|
@ -81,7 +81,7 @@ class Embedded extends Component {
|
|||||||
|
|
||||||
renderPending = (data, index) => {
|
renderPending = (data, index) => {
|
||||||
const { actions, gasLimit, isTest } = this.props;
|
const { actions, gasLimit, isTest } = this.props;
|
||||||
const { date, id, isSending, payload } = data;
|
const { date, id, isSending, payload, origin } = data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RequestPending
|
<RequestPending
|
||||||
@ -95,6 +95,7 @@ class Embedded extends Component {
|
|||||||
key={ id }
|
key={ id }
|
||||||
onConfirm={ actions.startConfirmRequest }
|
onConfirm={ actions.startConfirmRequest }
|
||||||
onReject={ actions.startRejectRequest }
|
onReject={ actions.startRejectRequest }
|
||||||
|
origin={ origin }
|
||||||
payload={ payload }
|
payload={ payload }
|
||||||
store={ this.store }
|
store={ this.store }
|
||||||
/>
|
/>
|
||||||
|
@ -107,7 +107,7 @@ class RequestsPage extends Component {
|
|||||||
|
|
||||||
renderPending = (data, index) => {
|
renderPending = (data, index) => {
|
||||||
const { actions, gasLimit, isTest } = this.props;
|
const { actions, gasLimit, isTest } = this.props;
|
||||||
const { date, id, isSending, payload } = data;
|
const { date, id, isSending, payload, origin } = data;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RequestPending
|
<RequestPending
|
||||||
@ -121,6 +121,7 @@ class RequestsPage extends Component {
|
|||||||
key={ id }
|
key={ id }
|
||||||
onConfirm={ actions.startConfirmRequest }
|
onConfirm={ actions.startConfirmRequest }
|
||||||
onReject={ actions.startRejectRequest }
|
onReject={ actions.startRejectRequest }
|
||||||
|
origin={ origin }
|
||||||
payload={ payload }
|
payload={ payload }
|
||||||
store={ this.store }
|
store={ this.store }
|
||||||
/>
|
/>
|
||||||
|
@ -28,7 +28,7 @@ extern crate ctrlc;
|
|||||||
extern crate docopt;
|
extern crate docopt;
|
||||||
extern crate env_logger;
|
extern crate env_logger;
|
||||||
extern crate fdlimit;
|
extern crate fdlimit;
|
||||||
extern crate hyper; // for price_info.rs
|
extern crate hyper;
|
||||||
extern crate isatty;
|
extern crate isatty;
|
||||||
extern crate jsonrpc_core;
|
extern crate jsonrpc_core;
|
||||||
extern crate num_cpus;
|
extern crate num_cpus;
|
||||||
@ -60,15 +60,14 @@ extern crate parity_reactor;
|
|||||||
extern crate parity_updater as updater;
|
extern crate parity_updater as updater;
|
||||||
extern crate rpc_cli;
|
extern crate rpc_cli;
|
||||||
|
|
||||||
|
#[macro_use]
|
||||||
|
extern crate log as rlog;
|
||||||
|
|
||||||
#[cfg(feature="stratum")]
|
#[cfg(feature="stratum")]
|
||||||
extern crate ethcore_stratum;
|
extern crate ethcore_stratum;
|
||||||
#[cfg(feature = "dapps")]
|
#[cfg(feature = "dapps")]
|
||||||
extern crate ethcore_dapps;
|
extern crate ethcore_dapps;
|
||||||
|
|
||||||
|
|
||||||
#[macro_use]
|
|
||||||
extern crate log as rlog;
|
|
||||||
|
|
||||||
macro_rules! dependency {
|
macro_rules! dependency {
|
||||||
($dep_ty:ident, $url:expr) => {
|
($dep_ty:ident, $url:expr) => {
|
||||||
{
|
{
|
||||||
|
@ -21,9 +21,10 @@ use std::io;
|
|||||||
use io::PanicHandler;
|
use io::PanicHandler;
|
||||||
|
|
||||||
use dir::default_data_path;
|
use dir::default_data_path;
|
||||||
use ethcore_rpc::{self as rpc, RpcServerError, IpcServerError, Metadata};
|
use ethcore_rpc::{self as rpc, RpcServerError, IpcServerError, Metadata, Origin};
|
||||||
use ethcore_rpc::informant::{RpcStats, Middleware};
|
use ethcore_rpc::informant::{RpcStats, Middleware};
|
||||||
use helpers::parity_ipc_path;
|
use helpers::parity_ipc_path;
|
||||||
|
use hyper;
|
||||||
use jsonrpc_core::MetaIoHandler;
|
use jsonrpc_core::MetaIoHandler;
|
||||||
use jsonrpc_core::reactor::{RpcHandler, Remote};
|
use jsonrpc_core::reactor::{RpcHandler, Remote};
|
||||||
use rpc_apis;
|
use rpc_apis;
|
||||||
@ -89,6 +90,18 @@ pub struct Dependencies {
|
|||||||
pub stats: Arc<RpcStats>,
|
pub stats: Arc<RpcStats>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct RpcExtractor;
|
||||||
|
impl rpc::HttpMetaExtractor<Metadata> for RpcExtractor {
|
||||||
|
fn read_metadata(&self, req: &hyper::server::Request<hyper::net::HttpStream>) -> Metadata {
|
||||||
|
let origin = req.headers().get::<hyper::header::Origin>()
|
||||||
|
.map(|origin| format!("{}://{}", origin.scheme, origin.host))
|
||||||
|
.unwrap_or_else(|| "unknown".into());
|
||||||
|
let mut metadata = Metadata::default();
|
||||||
|
metadata.origin = Origin::Rpc(origin);
|
||||||
|
metadata
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn new_http(conf: HttpConfiguration, deps: &Dependencies) -> Result<Option<HttpServer>, String> {
|
pub fn new_http(conf: HttpConfiguration, deps: &Dependencies) -> Result<Option<HttpServer>, String> {
|
||||||
if !conf.enabled {
|
if !conf.enabled {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
@ -113,7 +126,7 @@ pub fn setup_http_rpc_server(
|
|||||||
let apis = setup_apis(apis, dependencies);
|
let apis = setup_apis(apis, dependencies);
|
||||||
let handler = RpcHandler::new(Arc::new(apis), dependencies.remote.clone());
|
let handler = RpcHandler::new(Arc::new(apis), dependencies.remote.clone());
|
||||||
let ph = dependencies.panic_handler.clone();
|
let ph = dependencies.panic_handler.clone();
|
||||||
let start_result = rpc::start_http(url, cors_domains, allowed_hosts, ph, handler);
|
let start_result = rpc::start_http(url, cors_domains, allowed_hosts, ph, handler, RpcExtractor);
|
||||||
match start_result {
|
match start_result {
|
||||||
Err(RpcServerError::IoError(err)) => match err.kind() {
|
Err(RpcServerError::IoError(err)) => match err.kind() {
|
||||||
io::ErrorKind::AddrInUse => Err(format!("RPC address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --jsonrpc-port and --jsonrpc-interface options.", url)),
|
io::ErrorKind::AddrInUse => Err(format!("RPC address {} is already in use, make sure that another instance of an Ethereum client is not running or change the address using the --jsonrpc-port and --jsonrpc-interface options.", url)),
|
||||||
|
@ -23,12 +23,14 @@ pub use ethcore_signer::Server as SignerServer;
|
|||||||
use ansi_term::Colour;
|
use ansi_term::Colour;
|
||||||
use dir::default_data_path;
|
use dir::default_data_path;
|
||||||
use ethcore_rpc::informant::RpcStats;
|
use ethcore_rpc::informant::RpcStats;
|
||||||
|
use ethcore_rpc;
|
||||||
use ethcore_signer as signer;
|
use ethcore_signer as signer;
|
||||||
use helpers::replace_home;
|
use helpers::replace_home;
|
||||||
use io::{ForwardPanic, PanicHandler};
|
use io::{ForwardPanic, PanicHandler};
|
||||||
use jsonrpc_core::reactor::{RpcHandler, Remote};
|
use jsonrpc_core::reactor::{RpcHandler, Remote};
|
||||||
use rpc_apis;
|
use rpc_apis;
|
||||||
use util::path::restrict_permissions_owner;
|
use util::path::restrict_permissions_owner;
|
||||||
|
use util::H256;
|
||||||
|
|
||||||
const CODES_FILENAME: &'static str = "authcodes";
|
const CODES_FILENAME: &'static str = "authcodes";
|
||||||
|
|
||||||
@ -67,6 +69,16 @@ pub struct NewToken {
|
|||||||
pub message: String,
|
pub message: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default, Clone)]
|
||||||
|
pub struct StandardExtractor;
|
||||||
|
impl signer::MetaExtractor<ethcore_rpc::Metadata> for StandardExtractor {
|
||||||
|
fn extract_metadata(&self, session: &H256) -> ethcore_rpc::Metadata {
|
||||||
|
let mut metadata = ethcore_rpc::Metadata::default();
|
||||||
|
metadata.origin = ethcore_rpc::Origin::Signer((*session).into());
|
||||||
|
metadata
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn start(conf: Configuration, deps: Dependencies) -> Result<Option<SignerServer>, String> {
|
pub fn start(conf: Configuration, deps: Dependencies) -> Result<Option<SignerServer>, String> {
|
||||||
if !conf.enabled {
|
if !conf.enabled {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
@ -133,7 +145,7 @@ fn do_start(conf: Configuration, deps: Dependencies) -> Result<SignerServer, Str
|
|||||||
let server = server.stats(deps.rpc_stats.clone());
|
let server = server.stats(deps.rpc_stats.clone());
|
||||||
let apis = rpc_apis::setup_rpc(deps.rpc_stats, deps.apis, rpc_apis::ApiSet::SafeContext);
|
let apis = rpc_apis::setup_rpc(deps.rpc_stats, deps.apis, rpc_apis::ApiSet::SafeContext);
|
||||||
let handler = RpcHandler::new(Arc::new(apis), deps.remote);
|
let handler = RpcHandler::new(Arc::new(apis), deps.remote);
|
||||||
server.start(addr, handler)
|
server.start_with_extractor(addr, handler, StandardExtractor)
|
||||||
};
|
};
|
||||||
|
|
||||||
match start_result {
|
match start_result {
|
||||||
|
@ -65,19 +65,24 @@ use io::PanicHandler;
|
|||||||
use jsonrpc_core::reactor::RpcHandler;
|
use jsonrpc_core::reactor::RpcHandler;
|
||||||
|
|
||||||
pub use ipc::{Server as IpcServer, Error as IpcServerError};
|
pub use ipc::{Server as IpcServer, Error as IpcServerError};
|
||||||
pub use jsonrpc_http_server::{ServerBuilder, Server, RpcServerError};
|
pub use jsonrpc_http_server::{ServerBuilder, Server, RpcServerError, HttpMetaExtractor};
|
||||||
pub mod v1;
|
pub mod v1;
|
||||||
pub use v1::{SigningQueue, SignerService, ConfirmationsQueue, NetworkSettings, Metadata, Origin, informant, dispatch};
|
pub use v1::{SigningQueue, SignerService, ConfirmationsQueue, NetworkSettings, Metadata, Origin, informant, dispatch};
|
||||||
pub use v1::block_import::is_major_importing;
|
pub use v1::block_import::is_major_importing;
|
||||||
|
|
||||||
/// Start http server asynchronously and returns result with `Server` handle on success or an error.
|
/// Start http server asynchronously and returns result with `Server` handle on success or an error.
|
||||||
pub fn start_http<M: jsonrpc_core::Metadata, S: jsonrpc_core::Middleware<M>>(
|
pub fn start_http<M, T, S>(
|
||||||
addr: &SocketAddr,
|
addr: &SocketAddr,
|
||||||
cors_domains: Option<Vec<String>>,
|
cors_domains: Option<Vec<String>>,
|
||||||
allowed_hosts: Option<Vec<String>>,
|
allowed_hosts: Option<Vec<String>>,
|
||||||
panic_handler: Arc<PanicHandler>,
|
panic_handler: Arc<PanicHandler>,
|
||||||
handler: RpcHandler<M, S>,
|
handler: RpcHandler<M, S>,
|
||||||
) -> Result<Server, RpcServerError> {
|
extractor: T,
|
||||||
|
) -> Result<Server, RpcServerError> where
|
||||||
|
M: jsonrpc_core::Metadata,
|
||||||
|
S: jsonrpc_core::Middleware<M>,
|
||||||
|
T: HttpMetaExtractor<M>,
|
||||||
|
{
|
||||||
|
|
||||||
let cors_domains = cors_domains.map(|domains| {
|
let cors_domains = cors_domains.map(|domains| {
|
||||||
domains.into_iter()
|
domains.into_iter()
|
||||||
@ -90,6 +95,7 @@ pub fn start_http<M: jsonrpc_core::Metadata, S: jsonrpc_core::Middleware<M>>(
|
|||||||
});
|
});
|
||||||
|
|
||||||
ServerBuilder::with_rpc_handler(handler)
|
ServerBuilder::with_rpc_handler(handler)
|
||||||
|
.meta_extractor(Arc::new(extractor))
|
||||||
.cors(cors_domains.into())
|
.cors(cors_domains.into())
|
||||||
.allowed_hosts(allowed_hosts.into())
|
.allowed_hosts(allowed_hosts.into())
|
||||||
.panic_handler(move || {
|
.panic_handler(move || {
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
use util::{Address, U256, Bytes};
|
use util::{Address, U256, Bytes};
|
||||||
use v1::types::TransactionCondition;
|
use v1::types::{Origin, TransactionCondition};
|
||||||
|
|
||||||
/// Transaction request coming from RPC
|
/// Transaction request coming from RPC
|
||||||
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Default, Eq, PartialEq, Hash)]
|
||||||
@ -102,6 +102,8 @@ pub struct ConfirmationRequest {
|
|||||||
pub id: U256,
|
pub id: U256,
|
||||||
/// Payload to confirm
|
/// Payload to confirm
|
||||||
pub payload: ConfirmationPayload,
|
pub payload: ConfirmationPayload,
|
||||||
|
/// Request origin
|
||||||
|
pub origin: Origin,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Payload to confirm in Trusted Signer
|
/// Payload to confirm in Trusted Signer
|
||||||
|
@ -22,8 +22,7 @@ use jsonrpc_core;
|
|||||||
use util::{Mutex, RwLock, U256, Address};
|
use util::{Mutex, RwLock, U256, Address};
|
||||||
use ethcore::account_provider::DappId;
|
use ethcore::account_provider::DappId;
|
||||||
use v1::helpers::{ConfirmationRequest, ConfirmationPayload};
|
use v1::helpers::{ConfirmationRequest, ConfirmationPayload};
|
||||||
use v1::metadata::Metadata;
|
use v1::types::{ConfirmationResponse, H160 as RpcH160, Origin, DappId as RpcDappId};
|
||||||
use v1::types::{ConfirmationResponse, H160 as RpcH160};
|
|
||||||
|
|
||||||
/// Result that can be returned from JSON RPC.
|
/// Result that can be returned from JSON RPC.
|
||||||
pub type RpcResult = Result<ConfirmationResponse, jsonrpc_core::Error>;
|
pub type RpcResult = Result<ConfirmationResponse, jsonrpc_core::Error>;
|
||||||
@ -37,9 +36,9 @@ pub enum DefaultAccount {
|
|||||||
ForDapp(DappId),
|
ForDapp(DappId),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<Metadata> for DefaultAccount {
|
impl From<RpcDappId> for DefaultAccount {
|
||||||
fn from(meta: Metadata) -> Self {
|
fn from(dapp_id: RpcDappId) -> Self {
|
||||||
DefaultAccount::ForDapp(meta.dapp_id.unwrap_or_default().into())
|
DefaultAccount::ForDapp(dapp_id.into())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +83,7 @@ const QUEUE_LIMIT: usize = 50;
|
|||||||
pub trait SigningQueue: Send + Sync {
|
pub trait SigningQueue: Send + Sync {
|
||||||
/// Add new request to the queue.
|
/// Add new request to the queue.
|
||||||
/// Returns a `ConfirmationPromise` that can be used to await for resolution of given request.
|
/// Returns a `ConfirmationPromise` that can be used to await for resolution of given request.
|
||||||
fn add_request(&self, request: ConfirmationPayload) -> Result<ConfirmationPromise, QueueAddError>;
|
fn add_request(&self, request: ConfirmationPayload, origin: Origin) -> Result<ConfirmationPromise, QueueAddError>;
|
||||||
|
|
||||||
/// Removes a request from the queue.
|
/// Removes a request from the queue.
|
||||||
/// Notifies possible token holders that request was rejected.
|
/// Notifies possible token holders that request was rejected.
|
||||||
@ -267,7 +266,7 @@ impl Drop for ConfirmationsQueue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl SigningQueue for ConfirmationsQueue {
|
impl SigningQueue for ConfirmationsQueue {
|
||||||
fn add_request(&self, request: ConfirmationPayload) -> Result<ConfirmationPromise, QueueAddError> {
|
fn add_request(&self, request: ConfirmationPayload, origin: Origin) -> Result<ConfirmationPromise, QueueAddError> {
|
||||||
if self.len() > QUEUE_LIMIT {
|
if self.len() > QUEUE_LIMIT {
|
||||||
return Err(QueueAddError::LimitReached);
|
return Err(QueueAddError::LimitReached);
|
||||||
}
|
}
|
||||||
@ -290,6 +289,7 @@ impl SigningQueue for ConfirmationsQueue {
|
|||||||
request: ConfirmationRequest {
|
request: ConfirmationRequest {
|
||||||
id: id,
|
id: id,
|
||||||
payload: request,
|
payload: request,
|
||||||
|
origin: origin,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
queue.get(&id).map(|token| token.as_promise()).expect("Token was just inserted.")
|
queue.get(&id).map(|token| token.as_promise()).expect("Token was just inserted.")
|
||||||
@ -362,7 +362,7 @@ mod test {
|
|||||||
// when
|
// when
|
||||||
let q = queue.clone();
|
let q = queue.clone();
|
||||||
let handle = thread::spawn(move || {
|
let handle = thread::spawn(move || {
|
||||||
let v = q.add_request(request).unwrap();
|
let v = q.add_request(request, Default::default()).unwrap();
|
||||||
let (tx, rx) = mpsc::channel();
|
let (tx, rx) = mpsc::channel();
|
||||||
v.wait_for_result(move |res| {
|
v.wait_for_result(move |res| {
|
||||||
tx.send(res).unwrap();
|
tx.send(res).unwrap();
|
||||||
@ -397,7 +397,7 @@ mod test {
|
|||||||
*v = Some(notification);
|
*v = Some(notification);
|
||||||
}).expect("Should be closed nicely.")
|
}).expect("Should be closed nicely.")
|
||||||
});
|
});
|
||||||
queue.add_request(request).unwrap();
|
queue.add_request(request, Default::default()).unwrap();
|
||||||
queue.finish();
|
queue.finish();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
@ -413,7 +413,7 @@ mod test {
|
|||||||
let request = request();
|
let request = request();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
queue.add_request(request.clone()).unwrap();
|
queue.add_request(request.clone(), Default::default()).unwrap();
|
||||||
let all = queue.requests();
|
let all = queue.requests();
|
||||||
|
|
||||||
// then
|
// then
|
||||||
|
@ -311,7 +311,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn author(&self, meta: Metadata) -> BoxFuture<RpcH160, Error> {
|
fn author(&self, meta: Metadata) -> BoxFuture<RpcH160, Error> {
|
||||||
let dapp = meta.dapp_id.unwrap_or_default();
|
let dapp = meta.dapp_id();
|
||||||
|
|
||||||
let author = move || {
|
let author = move || {
|
||||||
let mut miner = take_weak!(self.miner).author();
|
let mut miner = take_weak!(self.miner).author();
|
||||||
@ -342,7 +342,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> Eth for EthClient<C, SN, S, M, EM> where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn accounts(&self, meta: Metadata) -> BoxFuture<Vec<RpcH160>, Error> {
|
fn accounts(&self, meta: Metadata) -> BoxFuture<Vec<RpcH160>, Error> {
|
||||||
let dapp = meta.dapp_id.unwrap_or_default();
|
let dapp = meta.dapp_id();
|
||||||
|
|
||||||
let accounts = move || {
|
let accounts = move || {
|
||||||
let accounts = self.dapp_accounts(dapp.into())?;
|
let accounts = self.dapp_accounts(dapp.into())?;
|
||||||
|
@ -178,7 +178,7 @@ impl Eth for EthClient {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn accounts(&self, meta: Metadata) -> BoxFuture<Vec<RpcH160>, Error> {
|
fn accounts(&self, meta: Metadata) -> BoxFuture<Vec<RpcH160>, Error> {
|
||||||
let dapp: DappId = meta.dapp_id.unwrap_or_default().into();
|
let dapp: DappId = meta.dapp_id().into();
|
||||||
|
|
||||||
let accounts = self.accounts
|
let accounts = self.accounts
|
||||||
.note_dapp_used(dapp.clone())
|
.note_dapp_used(dapp.clone())
|
||||||
|
@ -145,7 +145,7 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn default_account(&self, meta: Self::Metadata) -> BoxFuture<H160, Error> {
|
fn default_account(&self, meta: Self::Metadata) -> BoxFuture<H160, Error> {
|
||||||
let dapp_id = meta.dapp_id.unwrap_or_default();
|
let dapp_id = meta.dapp_id();
|
||||||
let default_account = move || {
|
let default_account = move || {
|
||||||
Ok(take_weak!(self.accounts)
|
Ok(take_weak!(self.accounts)
|
||||||
.dapps_addresses(dapp_id.into())
|
.dapps_addresses(dapp_id.into())
|
||||||
|
@ -101,7 +101,7 @@ impl<D: Dispatcher + 'static> Personal for PersonalClient<D> {
|
|||||||
let default = match request.from.as_ref() {
|
let default = match request.from.as_ref() {
|
||||||
Some(account) => Ok(account.clone().into()),
|
Some(account) => Ok(account.clone().into()),
|
||||||
None => accounts
|
None => accounts
|
||||||
.default_address(meta.dapp_id.unwrap_or_default().into())
|
.default_address(meta.dapp_id().into())
|
||||||
.map_err(|e| errors::account("Cannot find default account.", e)),
|
.map_err(|e| errors::account("Cannot find default account.", e)),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,7 +38,8 @@ use v1::types::{
|
|||||||
RichRawTransaction as RpcRichRawTransaction,
|
RichRawTransaction as RpcRichRawTransaction,
|
||||||
TransactionRequest as RpcTransactionRequest,
|
TransactionRequest as RpcTransactionRequest,
|
||||||
ConfirmationPayload as RpcConfirmationPayload,
|
ConfirmationPayload as RpcConfirmationPayload,
|
||||||
ConfirmationResponse as RpcConfirmationResponse
|
ConfirmationResponse as RpcConfirmationResponse,
|
||||||
|
Origin,
|
||||||
};
|
};
|
||||||
|
|
||||||
const MAX_PENDING_DURATION: u64 = 60 * 60;
|
const MAX_PENDING_DURATION: u64 = 60 * 60;
|
||||||
@ -81,7 +82,7 @@ impl<D: Dispatcher + 'static> SigningQueueClient<D> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch(&self, payload: RpcConfirmationPayload, default_account: DefaultAccount) -> BoxFuture<DispatchResult, Error> {
|
fn dispatch(&self, payload: RpcConfirmationPayload, default_account: DefaultAccount, origin: Origin) -> BoxFuture<DispatchResult, Error> {
|
||||||
let accounts = take_weakf!(self.accounts);
|
let accounts = take_weakf!(self.accounts);
|
||||||
let default_account = match default_account {
|
let default_account = match default_account {
|
||||||
DefaultAccount::Provided(acc) => acc,
|
DefaultAccount::Provided(acc) => acc,
|
||||||
@ -100,7 +101,7 @@ impl<D: Dispatcher + 'static> SigningQueueClient<D> {
|
|||||||
.boxed()
|
.boxed()
|
||||||
} else {
|
} else {
|
||||||
future::done(
|
future::done(
|
||||||
signer.add_request(payload)
|
signer.add_request(payload, origin)
|
||||||
.map(DispatchResult::Promise)
|
.map(DispatchResult::Promise)
|
||||||
.map_err(|_| errors::request_rejected_limit())
|
.map_err(|_| errors::request_rejected_limit())
|
||||||
).boxed()
|
).boxed()
|
||||||
@ -113,23 +114,26 @@ impl<D: Dispatcher + 'static> SigningQueueClient<D> {
|
|||||||
impl<D: Dispatcher + 'static> ParitySigning for SigningQueueClient<D> {
|
impl<D: Dispatcher + 'static> ParitySigning for SigningQueueClient<D> {
|
||||||
type Metadata = Metadata;
|
type Metadata = Metadata;
|
||||||
|
|
||||||
fn post_sign(&self, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>, Error> {
|
fn post_sign(&self, meta: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>, Error> {
|
||||||
let pending = self.pending.clone();
|
let pending = self.pending.clone();
|
||||||
self.dispatch(RpcConfirmationPayload::Signature((address.clone(), data).into()), DefaultAccount::Provided(address.into()))
|
self.dispatch(
|
||||||
.map(move |result| match result {
|
RpcConfirmationPayload::Signature((address.clone(), data).into()),
|
||||||
DispatchResult::Value(v) => RpcEither::Or(v),
|
DefaultAccount::Provided(address.into()),
|
||||||
DispatchResult::Promise(promise) => {
|
meta.origin
|
||||||
let id = promise.id();
|
).map(move |result| match result {
|
||||||
pending.lock().insert(id, promise);
|
DispatchResult::Value(v) => RpcEither::Or(v),
|
||||||
RpcEither::Either(id.into())
|
DispatchResult::Promise(promise) => {
|
||||||
},
|
let id = promise.id();
|
||||||
})
|
pending.lock().insert(id, promise);
|
||||||
.boxed()
|
RpcEither::Either(id.into())
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>, Error> {
|
fn post_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>, Error> {
|
||||||
let pending = self.pending.clone();
|
let pending = self.pending.clone();
|
||||||
self.dispatch(RpcConfirmationPayload::SendTransaction(request), meta.into())
|
self.dispatch(RpcConfirmationPayload::SendTransaction(request), meta.dapp_id().into(), meta.origin)
|
||||||
.map(move |result| match result {
|
.map(move |result| match result {
|
||||||
DispatchResult::Value(v) => RpcEither::Or(v),
|
DispatchResult::Value(v) => RpcEither::Or(v),
|
||||||
DispatchResult::Promise(promise) => {
|
DispatchResult::Promise(promise) => {
|
||||||
@ -156,8 +160,12 @@ impl<D: Dispatcher + 'static> ParitySigning for SigningQueueClient<D> {
|
|||||||
res
|
res
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decrypt_message(&self, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcBytes, Error> {
|
fn decrypt_message(&self, meta: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcBytes, Error> {
|
||||||
let res = self.dispatch(RpcConfirmationPayload::Decrypt((address.clone(), data).into()), address.into());
|
let res = self.dispatch(
|
||||||
|
RpcConfirmationPayload::Decrypt((address.clone(), data).into()),
|
||||||
|
address.into(),
|
||||||
|
meta.origin,
|
||||||
|
);
|
||||||
|
|
||||||
let (ready, p) = futures::oneshot();
|
let (ready, p) = futures::oneshot();
|
||||||
|
|
||||||
@ -181,8 +189,12 @@ impl<D: Dispatcher + 'static> ParitySigning for SigningQueueClient<D> {
|
|||||||
impl<D: Dispatcher + 'static> EthSigning for SigningQueueClient<D> {
|
impl<D: Dispatcher + 'static> EthSigning for SigningQueueClient<D> {
|
||||||
type Metadata = Metadata;
|
type Metadata = Metadata;
|
||||||
|
|
||||||
fn sign(&self, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcH520, Error> {
|
fn sign(&self, meta: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcH520, Error> {
|
||||||
let res = self.dispatch(RpcConfirmationPayload::Signature((address.clone(), data).into()), address.into());
|
let res = self.dispatch(
|
||||||
|
RpcConfirmationPayload::Signature((address.clone(), data).into()),
|
||||||
|
address.into(),
|
||||||
|
meta.origin,
|
||||||
|
);
|
||||||
|
|
||||||
let (ready, p) = futures::oneshot();
|
let (ready, p) = futures::oneshot();
|
||||||
|
|
||||||
@ -200,7 +212,11 @@ impl<D: Dispatcher + 'static> EthSigning for SigningQueueClient<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn send_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcH256, Error> {
|
fn send_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcH256, Error> {
|
||||||
let res = self.dispatch(RpcConfirmationPayload::SendTransaction(request), meta.into());
|
let res = self.dispatch(
|
||||||
|
RpcConfirmationPayload::SendTransaction(request),
|
||||||
|
meta.dapp_id().into(),
|
||||||
|
meta.origin,
|
||||||
|
);
|
||||||
|
|
||||||
let (ready, p) = futures::oneshot();
|
let (ready, p) = futures::oneshot();
|
||||||
|
|
||||||
@ -218,7 +234,11 @@ impl<D: Dispatcher + 'static> EthSigning for SigningQueueClient<D> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sign_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcRichRawTransaction, Error> {
|
fn sign_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcRichRawTransaction, Error> {
|
||||||
let res = self.dispatch(RpcConfirmationPayload::SignTransaction(request), meta.into());
|
let res = self.dispatch(
|
||||||
|
RpcConfirmationPayload::SignTransaction(request),
|
||||||
|
meta.dapp_id().into(),
|
||||||
|
meta.origin,
|
||||||
|
);
|
||||||
|
|
||||||
let (ready, p) = futures::oneshot();
|
let (ready, p) = futures::oneshot();
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ impl<D: Dispatcher + 'static> EthSigning for SigningUnsafeClient<D>
|
|||||||
{
|
{
|
||||||
type Metadata = Metadata;
|
type Metadata = Metadata;
|
||||||
|
|
||||||
fn sign(&self, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcH520, Error> {
|
fn sign(&self, _: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcH520, Error> {
|
||||||
self.handle(RpcConfirmationPayload::Signature((address.clone(), data).into()), address.into())
|
self.handle(RpcConfirmationPayload::Signature((address.clone(), data).into()), address.into())
|
||||||
.then(|res| match res {
|
.then(|res| match res {
|
||||||
Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature),
|
Ok(RpcConfirmationResponse::Signature(signature)) => Ok(signature),
|
||||||
@ -83,7 +83,7 @@ impl<D: Dispatcher + 'static> EthSigning for SigningUnsafeClient<D>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn send_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcH256, Error> {
|
fn send_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcH256, Error> {
|
||||||
self.handle(RpcConfirmationPayload::SendTransaction(request), meta.into())
|
self.handle(RpcConfirmationPayload::SendTransaction(request), meta.dapp_id().into())
|
||||||
.then(|res| match res {
|
.then(|res| match res {
|
||||||
Ok(RpcConfirmationResponse::SendTransaction(hash)) => Ok(hash),
|
Ok(RpcConfirmationResponse::SendTransaction(hash)) => Ok(hash),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
@ -93,7 +93,7 @@ impl<D: Dispatcher + 'static> EthSigning for SigningUnsafeClient<D>
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn sign_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcRichRawTransaction, Error> {
|
fn sign_transaction(&self, meta: Metadata, request: RpcTransactionRequest) -> BoxFuture<RpcRichRawTransaction, Error> {
|
||||||
self.handle(RpcConfirmationPayload::SignTransaction(request), meta.into())
|
self.handle(RpcConfirmationPayload::SignTransaction(request), meta.dapp_id().into())
|
||||||
.then(|res| match res {
|
.then(|res| match res {
|
||||||
Ok(RpcConfirmationResponse::SignTransaction(tx)) => Ok(tx),
|
Ok(RpcConfirmationResponse::SignTransaction(tx)) => Ok(tx),
|
||||||
Err(e) => Err(e),
|
Err(e) => Err(e),
|
||||||
@ -106,7 +106,7 @@ impl<D: Dispatcher + 'static> EthSigning for SigningUnsafeClient<D>
|
|||||||
impl<D: Dispatcher + 'static> ParitySigning for SigningUnsafeClient<D> {
|
impl<D: Dispatcher + 'static> ParitySigning for SigningUnsafeClient<D> {
|
||||||
type Metadata = Metadata;
|
type Metadata = Metadata;
|
||||||
|
|
||||||
fn decrypt_message(&self, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcBytes, Error> {
|
fn decrypt_message(&self, _: Metadata, address: RpcH160, data: RpcBytes) -> BoxFuture<RpcBytes, Error> {
|
||||||
self.handle(RpcConfirmationPayload::Decrypt((address.clone(), data).into()), address.into())
|
self.handle(RpcConfirmationPayload::Decrypt((address.clone(), data).into()), address.into())
|
||||||
.then(|res| match res {
|
.then(|res| match res {
|
||||||
Ok(RpcConfirmationResponse::Decrypt(data)) => Ok(data),
|
Ok(RpcConfirmationResponse::Decrypt(data)) => Ok(data),
|
||||||
@ -116,7 +116,7 @@ impl<D: Dispatcher + 'static> ParitySigning for SigningUnsafeClient<D> {
|
|||||||
.boxed()
|
.boxed()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn post_sign(&self, _: RpcH160, _: RpcBytes) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>, Error> {
|
fn post_sign(&self, _: Metadata, _: RpcH160, _: RpcBytes) -> BoxFuture<RpcEither<RpcU256, RpcConfirmationResponse>, Error> {
|
||||||
// We don't support this in non-signer mode.
|
// We don't support this in non-signer mode.
|
||||||
future::err(errors::signer_disabled()).boxed()
|
future::err(errors::signer_disabled()).boxed()
|
||||||
}
|
}
|
||||||
|
@ -16,33 +16,22 @@
|
|||||||
|
|
||||||
use jsonrpc_core;
|
use jsonrpc_core;
|
||||||
|
|
||||||
|
use v1::types::{DappId, Origin};
|
||||||
|
|
||||||
/// RPC methods metadata.
|
/// RPC methods metadata.
|
||||||
#[derive(Clone, Default, Debug, PartialEq)]
|
#[derive(Clone, Default, Debug, PartialEq)]
|
||||||
pub struct Metadata {
|
pub struct Metadata {
|
||||||
/// Current dapplication identifier
|
|
||||||
pub dapp_id: Option<String>,
|
|
||||||
/// Request origin
|
/// Request origin
|
||||||
pub origin: Origin,
|
pub origin: Origin,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// RPC request origin
|
impl Metadata {
|
||||||
#[derive(Clone, Debug, PartialEq)]
|
/// Get
|
||||||
pub enum Origin {
|
pub fn dapp_id(&self) -> DappId {
|
||||||
/// RPC server
|
match self.origin {
|
||||||
Rpc,
|
Origin::Dapps(ref dapp_id) => dapp_id.clone(),
|
||||||
/// Dapps server
|
_ => DappId::default(),
|
||||||
Dapps,
|
}
|
||||||
/// IPC server
|
|
||||||
Ipc,
|
|
||||||
/// Signer
|
|
||||||
Signer,
|
|
||||||
/// Unknown
|
|
||||||
Unknown,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for Origin {
|
|
||||||
fn default() -> Self {
|
|
||||||
Origin::Unknown
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,4 +61,5 @@ pub mod types;
|
|||||||
pub use self::traits::{Web3, Eth, EthFilter, EthSigning, Net, Parity, ParityAccounts, ParitySet, ParitySigning, Signer, Personal, Traces, Rpc};
|
pub use self::traits::{Web3, Eth, EthFilter, EthSigning, Net, Parity, ParityAccounts, ParitySet, ParitySigning, Signer, Personal, Traces, Rpc};
|
||||||
pub use self::impls::*;
|
pub use self::impls::*;
|
||||||
pub use self::helpers::{SigningQueue, SignerService, ConfirmationsQueue, NetworkSettings, block_import, informant, dispatch};
|
pub use self::helpers::{SigningQueue, SignerService, ConfirmationsQueue, NetworkSettings, block_import, informant, dispatch};
|
||||||
pub use self::metadata::{Metadata, Origin};
|
pub use self::metadata::Metadata;
|
||||||
|
pub use self::types::Origin;
|
||||||
|
@ -37,6 +37,7 @@ use v1::{Eth, EthClient, EthClientOptions, EthFilter, EthFilterClient, EthSignin
|
|||||||
use v1::helpers::dispatch::FullDispatcher;
|
use v1::helpers::dispatch::FullDispatcher;
|
||||||
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestSnapshotService};
|
use v1::tests::helpers::{TestSyncProvider, Config, TestMinerService, TestSnapshotService};
|
||||||
use v1::metadata::Metadata;
|
use v1::metadata::Metadata;
|
||||||
|
use v1::types::Origin;
|
||||||
|
|
||||||
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
fn blockchain_client() -> Arc<TestBlockChainClient> {
|
||||||
let client = TestBlockChainClient::new();
|
let client = TestBlockChainClient::new();
|
||||||
@ -387,7 +388,7 @@ fn rpc_eth_accounts() {
|
|||||||
let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#;
|
let request = r#"{"jsonrpc": "2.0", "method": "eth_accounts", "params": [], "id": 1}"#;
|
||||||
let response = r#"{"jsonrpc":"2.0","result":["0x000000000000000000000000000000000000000a"],"id":1}"#;
|
let response = r#"{"jsonrpc":"2.0","result":["0x000000000000000000000000000000000000000a"],"id":1}"#;
|
||||||
let mut meta = Metadata::default();
|
let mut meta = Metadata::default();
|
||||||
meta.dapp_id = Some("app1".into());
|
meta.origin = Origin::Dapps("app1".into());
|
||||||
assert_eq!((*tester.io).handle_request_sync(request, meta), Some(response.to_owned()));
|
assert_eq!((*tester.io).handle_request_sync(request, meta), Some(response.to_owned()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,7 +25,7 @@ use rlp::encode;
|
|||||||
|
|
||||||
use serde_json;
|
use serde_json;
|
||||||
use jsonrpc_core::IoHandler;
|
use jsonrpc_core::IoHandler;
|
||||||
use v1::{SignerClient, Signer};
|
use v1::{SignerClient, Signer, Origin};
|
||||||
use v1::metadata::Metadata;
|
use v1::metadata::Metadata;
|
||||||
use v1::tests::helpers::TestMinerService;
|
use v1::tests::helpers::TestMinerService;
|
||||||
use v1::helpers::{SigningQueue, SignerService, FilledTransactionRequest, ConfirmationPayload};
|
use v1::helpers::{SigningQueue, SignerService, FilledTransactionRequest, ConfirmationPayload};
|
||||||
@ -88,15 +88,15 @@ fn should_return_list_of_items_to_confirm() {
|
|||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
condition: None,
|
condition: None,
|
||||||
})).unwrap();
|
}), Origin::Dapps("http://parity.io".into())).unwrap();
|
||||||
tester.signer.add_request(ConfirmationPayload::Signature(1.into(), vec![5].into())).unwrap();
|
tester.signer.add_request(ConfirmationPayload::Signature(1.into(), vec![5].into()), Origin::Unknown).unwrap();
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#;
|
let request = r#"{"jsonrpc":"2.0","method":"signer_requestsToConfirm","params":[],"id":1}"#;
|
||||||
let response = concat!(
|
let response = concat!(
|
||||||
r#"{"jsonrpc":"2.0","result":["#,
|
r#"{"jsonrpc":"2.0","result":["#,
|
||||||
r#"{"id":"0x1","payload":{"sendTransaction":{"condition":null,"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x1"}}},"#,
|
r#"{"id":"0x1","origin":{"dapp":"http://parity.io"},"payload":{"sendTransaction":{"condition":null,"data":"0x","from":"0x0000000000000000000000000000000000000001","gas":"0x989680","gasPrice":"0x2710","nonce":null,"to":"0xd46e8dd67c5d32be8058bb8eb970870f07244567","value":"0x1"}}},"#,
|
||||||
r#"{"id":"0x2","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}}}"#,
|
r#"{"id":"0x2","origin":"unknown","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}}}"#,
|
||||||
r#"],"id":1}"#
|
r#"],"id":1}"#
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -119,7 +119,7 @@ fn should_reject_transaction_from_queue_without_dispatching() {
|
|||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
condition: None,
|
condition: None,
|
||||||
})).unwrap();
|
}), Origin::Unknown).unwrap();
|
||||||
assert_eq!(tester.signer.requests().len(), 1);
|
assert_eq!(tester.signer.requests().len(), 1);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -146,7 +146,7 @@ fn should_not_remove_transaction_if_password_is_invalid() {
|
|||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
condition: None,
|
condition: None,
|
||||||
})).unwrap();
|
}), Origin::Unknown).unwrap();
|
||||||
assert_eq!(tester.signer.requests().len(), 1);
|
assert_eq!(tester.signer.requests().len(), 1);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -162,7 +162,7 @@ fn should_not_remove_transaction_if_password_is_invalid() {
|
|||||||
fn should_not_remove_sign_if_password_is_invalid() {
|
fn should_not_remove_sign_if_password_is_invalid() {
|
||||||
// given
|
// given
|
||||||
let tester = signer_tester();
|
let tester = signer_tester();
|
||||||
tester.signer.add_request(ConfirmationPayload::Signature(0.into(), vec![5].into())).unwrap();
|
tester.signer.add_request(ConfirmationPayload::Signature(0.into(), vec![5].into()), Origin::Unknown).unwrap();
|
||||||
assert_eq!(tester.signer.requests().len(), 1);
|
assert_eq!(tester.signer.requests().len(), 1);
|
||||||
|
|
||||||
// when
|
// when
|
||||||
@ -190,7 +190,7 @@ fn should_confirm_transaction_and_dispatch() {
|
|||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
condition: None,
|
condition: None,
|
||||||
})).unwrap();
|
}), Origin::Unknown).unwrap();
|
||||||
|
|
||||||
let t = Transaction {
|
let t = Transaction {
|
||||||
nonce: U256::zero(),
|
nonce: U256::zero(),
|
||||||
@ -236,7 +236,7 @@ fn should_alter_the_sender_and_nonce() {
|
|||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: Some(10.into()),
|
nonce: Some(10.into()),
|
||||||
condition: None,
|
condition: None,
|
||||||
})).unwrap();
|
}), Origin::Unknown).unwrap();
|
||||||
|
|
||||||
let t = Transaction {
|
let t = Transaction {
|
||||||
nonce: U256::zero(),
|
nonce: U256::zero(),
|
||||||
@ -286,7 +286,7 @@ fn should_confirm_transaction_with_token() {
|
|||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
condition: None,
|
condition: None,
|
||||||
})).unwrap();
|
}), Origin::Unknown).unwrap();
|
||||||
|
|
||||||
let t = Transaction {
|
let t = Transaction {
|
||||||
nonce: U256::zero(),
|
nonce: U256::zero(),
|
||||||
@ -335,7 +335,7 @@ fn should_confirm_transaction_with_rlp() {
|
|||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
condition: None,
|
condition: None,
|
||||||
})).unwrap();
|
}), Origin::Unknown).unwrap();
|
||||||
|
|
||||||
let t = Transaction {
|
let t = Transaction {
|
||||||
nonce: U256::zero(),
|
nonce: U256::zero(),
|
||||||
@ -383,7 +383,7 @@ fn should_return_error_when_sender_does_not_match() {
|
|||||||
data: vec![],
|
data: vec![],
|
||||||
nonce: None,
|
nonce: None,
|
||||||
condition: None,
|
condition: None,
|
||||||
})).unwrap();
|
}), Origin::Unknown).unwrap();
|
||||||
|
|
||||||
let t = Transaction {
|
let t = Transaction {
|
||||||
nonce: U256::zero(),
|
nonce: U256::zero(),
|
||||||
|
@ -27,8 +27,8 @@ build_rpc_trait! {
|
|||||||
type Metadata;
|
type Metadata;
|
||||||
|
|
||||||
/// Signs the hash of data with given address signature.
|
/// Signs the hash of data with given address signature.
|
||||||
#[rpc(async, name = "eth_sign")]
|
#[rpc(meta, name = "eth_sign")]
|
||||||
fn sign(&self, H160, Bytes) -> BoxFuture<H520, Error>;
|
fn sign(&self, Self::Metadata, H160, Bytes) -> BoxFuture<H520, Error>;
|
||||||
|
|
||||||
/// Sends transaction; will block waiting for signer to return the
|
/// Sends transaction; will block waiting for signer to return the
|
||||||
/// transaction hash.
|
/// transaction hash.
|
||||||
|
@ -27,8 +27,8 @@ build_rpc_trait! {
|
|||||||
|
|
||||||
/// Posts sign request asynchronously.
|
/// Posts sign request asynchronously.
|
||||||
/// Will return a confirmation ID for later use with check_transaction.
|
/// Will return a confirmation ID for later use with check_transaction.
|
||||||
#[rpc(async, name = "parity_postSign")]
|
#[rpc(meta, name = "parity_postSign")]
|
||||||
fn post_sign(&self, H160, Bytes) -> BoxFuture<Either<U256, ConfirmationResponse>, Error>;
|
fn post_sign(&self, Self::Metadata, H160, Bytes) -> BoxFuture<Either<U256, ConfirmationResponse>, Error>;
|
||||||
|
|
||||||
/// Posts transaction asynchronously.
|
/// Posts transaction asynchronously.
|
||||||
/// Will return a transaction ID for later use with check_transaction.
|
/// Will return a transaction ID for later use with check_transaction.
|
||||||
@ -42,7 +42,7 @@ build_rpc_trait! {
|
|||||||
|
|
||||||
/// Decrypt some ECIES-encrypted message.
|
/// Decrypt some ECIES-encrypted message.
|
||||||
/// First parameter is the address with which it is encrypted, second is the ciphertext.
|
/// First parameter is the address with which it is encrypted, second is the ciphertext.
|
||||||
#[rpc(async, name = "parity_decryptMessage")]
|
#[rpc(meta, name = "parity_decryptMessage")]
|
||||||
fn decrypt_message(&self, H160, Bytes) -> BoxFuture<Bytes, Error>;
|
fn decrypt_message(&self, Self::Metadata, H160, Bytes) -> BoxFuture<Bytes, Error>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,16 +21,19 @@ use serde::{Serialize, Serializer};
|
|||||||
use util::log::Colour;
|
use util::log::Colour;
|
||||||
use util::bytes::ToPretty;
|
use util::bytes::ToPretty;
|
||||||
|
|
||||||
use v1::types::{U256, TransactionRequest, RichRawTransaction, H160, H256, H520, Bytes, TransactionCondition};
|
use v1::types::{U256, TransactionRequest, RichRawTransaction, H160, H256, H520, Bytes, TransactionCondition, Origin};
|
||||||
use v1::helpers;
|
use v1::helpers;
|
||||||
|
|
||||||
/// Confirmation waiting in a queue
|
/// Confirmation waiting in a queue
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct ConfirmationRequest {
|
pub struct ConfirmationRequest {
|
||||||
/// Id of this confirmation
|
/// Id of this confirmation
|
||||||
pub id: U256,
|
pub id: U256,
|
||||||
/// Payload
|
/// Payload
|
||||||
pub payload: ConfirmationPayload,
|
pub payload: ConfirmationPayload,
|
||||||
|
/// Request origin
|
||||||
|
pub origin: Origin,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<helpers::ConfirmationRequest> for ConfirmationRequest {
|
impl From<helpers::ConfirmationRequest> for ConfirmationRequest {
|
||||||
@ -38,13 +41,14 @@ impl From<helpers::ConfirmationRequest> for ConfirmationRequest {
|
|||||||
ConfirmationRequest {
|
ConfirmationRequest {
|
||||||
id: c.id.into(),
|
id: c.id.into(),
|
||||||
payload: c.payload.into(),
|
payload: c.payload.into(),
|
||||||
|
origin: c.origin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ConfirmationRequest {
|
impl fmt::Display for ConfirmationRequest {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "#{}: {}", self.id, self.payload)
|
write!(f, "#{}: {} coming from {}", self.id, self.payload, self.origin)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -61,6 +65,7 @@ impl fmt::Display for ConfirmationPayload {
|
|||||||
|
|
||||||
/// Sign request
|
/// Sign request
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct SignRequest {
|
pub struct SignRequest {
|
||||||
/// Address
|
/// Address
|
||||||
pub address: H160,
|
pub address: H160,
|
||||||
@ -90,6 +95,7 @@ impl fmt::Display for SignRequest {
|
|||||||
|
|
||||||
/// Decrypt request
|
/// Decrypt request
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub struct DecryptRequest {
|
pub struct DecryptRequest {
|
||||||
/// Address
|
/// Address
|
||||||
pub address: H160,
|
pub address: H160,
|
||||||
@ -153,6 +159,7 @@ pub struct ConfirmationResponseWithToken {
|
|||||||
|
|
||||||
/// Confirmation payload, i.e. the thing to be confirmed
|
/// Confirmation payload, i.e. the thing to be confirmed
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub enum ConfirmationPayload {
|
pub enum ConfirmationPayload {
|
||||||
/// Send Transaction
|
/// Send Transaction
|
||||||
#[serde(rename="sendTransaction")]
|
#[serde(rename="sendTransaction")]
|
||||||
@ -249,11 +256,12 @@ mod tests {
|
|||||||
let request = helpers::ConfirmationRequest {
|
let request = helpers::ConfirmationRequest {
|
||||||
id: 15.into(),
|
id: 15.into(),
|
||||||
payload: helpers::ConfirmationPayload::Signature(1.into(), vec![5].into()),
|
payload: helpers::ConfirmationPayload::Signature(1.into(), vec![5].into()),
|
||||||
|
origin: Origin::Rpc("test service".into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = serde_json::to_string(&ConfirmationRequest::from(request));
|
let res = serde_json::to_string(&ConfirmationRequest::from(request));
|
||||||
let expected = r#"{"id":"0xf","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}}}"#;
|
let expected = r#"{"id":"0xf","payload":{"sign":{"address":"0x0000000000000000000000000000000000000001","data":"0x05"}},"origin":{"rpc":"test service"}}"#;
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(res.unwrap(), expected.to_owned());
|
assert_eq!(res.unwrap(), expected.to_owned());
|
||||||
@ -275,11 +283,12 @@ mod tests {
|
|||||||
nonce: Some(1.into()),
|
nonce: Some(1.into()),
|
||||||
condition: None,
|
condition: None,
|
||||||
}),
|
}),
|
||||||
|
origin: Origin::Signer(5.into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = serde_json::to_string(&ConfirmationRequest::from(request));
|
let res = serde_json::to_string(&ConfirmationRequest::from(request));
|
||||||
let expected = r#"{"id":"0xf","payload":{"sendTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}}}"#;
|
let expected = r#"{"id":"0xf","payload":{"sendTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}},"origin":{"signer":"0x0000000000000000000000000000000000000000000000000000000000000005"}}"#;
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(res.unwrap(), expected.to_owned());
|
assert_eq!(res.unwrap(), expected.to_owned());
|
||||||
@ -301,11 +310,12 @@ mod tests {
|
|||||||
nonce: Some(1.into()),
|
nonce: Some(1.into()),
|
||||||
condition: None,
|
condition: None,
|
||||||
}),
|
}),
|
||||||
|
origin: Origin::Dapps("http://parity.io".into()),
|
||||||
};
|
};
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = serde_json::to_string(&ConfirmationRequest::from(request));
|
let res = serde_json::to_string(&ConfirmationRequest::from(request));
|
||||||
let expected = r#"{"id":"0xf","payload":{"signTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}}}"#;
|
let expected = r#"{"id":"0xf","payload":{"signTransaction":{"from":"0x0000000000000000000000000000000000000000","to":null,"gasPrice":"0x2710","gas":"0x3a98","value":"0x186a0","data":"0x010203","nonce":"0x1","condition":null}},"origin":{"dapp":"http://parity.io"}}"#;
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(res.unwrap(), expected.to_owned());
|
assert_eq!(res.unwrap(), expected.to_owned());
|
||||||
@ -319,11 +329,12 @@ mod tests {
|
|||||||
payload: helpers::ConfirmationPayload::Decrypt(
|
payload: helpers::ConfirmationPayload::Decrypt(
|
||||||
10.into(), vec![1, 2, 3].into(),
|
10.into(), vec![1, 2, 3].into(),
|
||||||
),
|
),
|
||||||
|
origin: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
// when
|
// when
|
||||||
let res = serde_json::to_string(&ConfirmationRequest::from(request));
|
let res = serde_json::to_string(&ConfirmationRequest::from(request));
|
||||||
let expected = r#"{"id":"0xf","payload":{"decrypt":{"address":"0x000000000000000000000000000000000000000a","msg":"0x010203"}}}"#;
|
let expected = r#"{"id":"0xf","payload":{"decrypt":{"address":"0x000000000000000000000000000000000000000a","msg":"0x010203"}},"origin":"unknown"}"#;
|
||||||
|
|
||||||
// then
|
// then
|
||||||
assert_eq!(res.unwrap(), expected.to_owned());
|
assert_eq!(res.unwrap(), expected.to_owned());
|
||||||
|
@ -1,80 +0,0 @@
|
|||||||
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
|
||||||
// This file is part of Parity.
|
|
||||||
|
|
||||||
// Parity 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.
|
|
||||||
|
|
||||||
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
//! Dapp Id type
|
|
||||||
|
|
||||||
use ethcore::account_provider::DappId as EthDappId;
|
|
||||||
|
|
||||||
/// Dapplication Internal Id
|
|
||||||
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
|
|
||||||
pub struct DappId(pub String);
|
|
||||||
|
|
||||||
impl Into<String> for DappId {
|
|
||||||
fn into(self) -> String {
|
|
||||||
self.0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<String> for DappId {
|
|
||||||
fn from(s: String) -> Self {
|
|
||||||
DappId(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<EthDappId> for DappId {
|
|
||||||
fn from(id: EthDappId) -> Self {
|
|
||||||
DappId(id.into())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Into<EthDappId> for DappId {
|
|
||||||
fn into(self) -> EthDappId {
|
|
||||||
Into::<String>::into(self).into()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
|
|
||||||
use serde_json;
|
|
||||||
use super::DappId;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_serialize_dapp_id() {
|
|
||||||
// given
|
|
||||||
let id = DappId("testapp".into());
|
|
||||||
|
|
||||||
// when
|
|
||||||
let res = serde_json::to_string(&id).unwrap();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(res, r#""testapp""#);
|
|
||||||
}
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn should_deserialize_dapp_id() {
|
|
||||||
// given
|
|
||||||
let id = r#""testapp""#;
|
|
||||||
|
|
||||||
// when
|
|
||||||
let res: DappId = serde_json::from_str(id).unwrap();
|
|
||||||
|
|
||||||
// then
|
|
||||||
assert_eq!(res, DappId("testapp".into()));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -36,11 +36,18 @@ macro_rules! impl_hash {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for $name {
|
impl fmt::Debug for $name {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
write!(f, "{}", self.0.to_hex())
|
write!(f, "{}", self.0.to_hex())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for $name {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
let hex = self.0.to_hex();
|
||||||
|
write!(f, "{}..{}", &hex[0..2], &hex[$size-2..$size])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<T> From<T> for $name where $other: From<T> {
|
impl<T> From<T> for $name where $other: From<T> {
|
||||||
fn from(o: T) -> Self {
|
fn from(o: T) -> Self {
|
||||||
$name($other::from(o).0)
|
$name($other::from(o).0)
|
||||||
|
@ -18,29 +18,30 @@
|
|||||||
//! RPC types
|
//! RPC types
|
||||||
|
|
||||||
mod account_info;
|
mod account_info;
|
||||||
mod bytes;
|
|
||||||
mod block;
|
mod block;
|
||||||
mod block_number;
|
mod block_number;
|
||||||
|
mod bytes;
|
||||||
mod call_request;
|
mod call_request;
|
||||||
mod confirmations;
|
mod confirmations;
|
||||||
mod dapp_id;
|
mod consensus_status;
|
||||||
mod filter;
|
mod filter;
|
||||||
mod hash;
|
mod hash;
|
||||||
|
mod histogram;
|
||||||
mod index;
|
mod index;
|
||||||
mod log;
|
mod log;
|
||||||
|
mod provenance;
|
||||||
|
mod receipt;
|
||||||
|
mod rpc_settings;
|
||||||
mod sync;
|
mod sync;
|
||||||
|
mod trace;
|
||||||
|
mod trace_filter;
|
||||||
mod transaction;
|
mod transaction;
|
||||||
mod transaction_request;
|
mod transaction_request;
|
||||||
mod transaction_condition;
|
mod transaction_condition;
|
||||||
mod receipt;
|
|
||||||
mod rpc_settings;
|
|
||||||
mod trace;
|
|
||||||
mod trace_filter;
|
|
||||||
mod uint;
|
mod uint;
|
||||||
mod work;
|
mod work;
|
||||||
mod histogram;
|
|
||||||
mod consensus_status;
|
|
||||||
|
|
||||||
|
pub use self::account_info::{AccountInfo, HwAccountInfo};
|
||||||
pub use self::bytes::Bytes;
|
pub use self::bytes::Bytes;
|
||||||
pub use self::block::{RichBlock, Block, BlockTransactions};
|
pub use self::block::{RichBlock, Block, BlockTransactions};
|
||||||
pub use self::block_number::BlockNumber;
|
pub use self::block_number::BlockNumber;
|
||||||
@ -49,24 +50,24 @@ pub use self::confirmations::{
|
|||||||
ConfirmationPayload, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken,
|
ConfirmationPayload, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken,
|
||||||
TransactionModification, SignRequest, DecryptRequest, Either
|
TransactionModification, SignRequest, DecryptRequest, Either
|
||||||
};
|
};
|
||||||
pub use self::dapp_id::DappId;
|
pub use self::consensus_status::*;
|
||||||
pub use self::filter::{Filter, FilterChanges};
|
pub use self::filter::{Filter, FilterChanges};
|
||||||
pub use self::hash::{H64, H160, H256, H512, H520, H2048};
|
pub use self::hash::{H64, H160, H256, H512, H520, H2048};
|
||||||
|
pub use self::histogram::Histogram;
|
||||||
pub use self::index::Index;
|
pub use self::index::Index;
|
||||||
pub use self::log::Log;
|
pub use self::log::Log;
|
||||||
|
pub use self::provenance::{Origin, DappId};
|
||||||
|
pub use self::receipt::Receipt;
|
||||||
|
pub use self::rpc_settings::RpcSettings;
|
||||||
pub use self::sync::{
|
pub use self::sync::{
|
||||||
SyncStatus, SyncInfo, Peers, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo,
|
SyncStatus, SyncInfo, Peers, PeerInfo, PeerNetworkInfo, PeerProtocolsInfo,
|
||||||
TransactionStats, ChainStatus, EthProtocolInfo, LesProtocolInfo,
|
TransactionStats, ChainStatus, EthProtocolInfo, LesProtocolInfo,
|
||||||
};
|
};
|
||||||
|
pub use self::trace::{LocalizedTrace, TraceResults};
|
||||||
|
pub use self::trace_filter::TraceFilter;
|
||||||
pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionStatus};
|
pub use self::transaction::{Transaction, RichRawTransaction, LocalTransactionStatus};
|
||||||
pub use self::transaction_request::TransactionRequest;
|
pub use self::transaction_request::TransactionRequest;
|
||||||
pub use self::transaction_condition::TransactionCondition;
|
pub use self::transaction_condition::TransactionCondition;
|
||||||
pub use self::receipt::Receipt;
|
|
||||||
pub use self::rpc_settings::RpcSettings;
|
|
||||||
pub use self::trace::{LocalizedTrace, TraceResults};
|
|
||||||
pub use self::trace_filter::TraceFilter;
|
|
||||||
pub use self::uint::{U128, U256};
|
pub use self::uint::{U128, U256};
|
||||||
pub use self::work::Work;
|
pub use self::work::Work;
|
||||||
pub use self::histogram::Histogram;
|
|
||||||
pub use self::consensus_status::*;
|
|
||||||
pub use self::account_info::{AccountInfo, HwAccountInfo};
|
|
||||||
|
154
rpc/src/v1/types/provenance.rs
Normal file
154
rpc/src/v1/types/provenance.rs
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
|
||||||
|
// This file is part of Parity.
|
||||||
|
|
||||||
|
// Parity 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.
|
||||||
|
|
||||||
|
// Parity 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 Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
//! Request Provenance
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use ethcore::account_provider::DappId as EthDappId;
|
||||||
|
use v1::types::H256;
|
||||||
|
|
||||||
|
/// RPC request origin
|
||||||
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
|
pub enum Origin {
|
||||||
|
/// RPC server (includes request origin)
|
||||||
|
#[serde(rename="rpc")]
|
||||||
|
Rpc(String),
|
||||||
|
/// Dapps server (includes DappId)
|
||||||
|
#[serde(rename="dapp")]
|
||||||
|
Dapps(DappId),
|
||||||
|
/// IPC server (includes session hash)
|
||||||
|
#[serde(rename="ipc")]
|
||||||
|
Ipc(H256),
|
||||||
|
/// Signer (includes session hash)
|
||||||
|
#[serde(rename="signer")]
|
||||||
|
Signer(H256),
|
||||||
|
/// Unknown
|
||||||
|
#[serde(rename="unknown")]
|
||||||
|
Unknown,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Origin {
|
||||||
|
fn default() -> Self {
|
||||||
|
Origin::Unknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for Origin {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match *self {
|
||||||
|
Origin::Rpc(ref origin) => write!(f, "RPC (service: {})", origin),
|
||||||
|
Origin::Dapps(ref origin) => write!(f, "Dapp {}", origin),
|
||||||
|
Origin::Ipc(ref session) => write!(f, "IPC (session: {})", session),
|
||||||
|
Origin::Signer(ref session) => write!(f, "UI (session: {})", session),
|
||||||
|
Origin::Unknown => write!(f, "unknown origin"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Dapplication Internal Id
|
||||||
|
#[derive(Debug, Default, Clone, Eq, PartialEq, Hash, Ord, PartialOrd, Serialize, Deserialize)]
|
||||||
|
pub struct DappId(pub String);
|
||||||
|
|
||||||
|
impl fmt::Display for DappId {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<String> for DappId {
|
||||||
|
fn into(self) -> String {
|
||||||
|
self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<String> for DappId {
|
||||||
|
fn from(s: String) -> Self {
|
||||||
|
DappId(s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> From<&'a str> for DappId {
|
||||||
|
fn from(s: &'a str) -> Self {
|
||||||
|
DappId(s.to_owned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<EthDappId> for DappId {
|
||||||
|
fn from(id: EthDappId) -> Self {
|
||||||
|
DappId(id.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<EthDappId> for DappId {
|
||||||
|
fn into(self) -> EthDappId {
|
||||||
|
Into::<String>::into(self).into()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use serde_json;
|
||||||
|
use super::{DappId, Origin};
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_serialize_origin() {
|
||||||
|
// given
|
||||||
|
let o1 = Origin::Rpc("test service".into());
|
||||||
|
let o2 = Origin::Dapps("http://parity.io".into());
|
||||||
|
let o3 = Origin::Ipc(5.into());
|
||||||
|
let o4 = Origin::Signer(10.into());
|
||||||
|
let o5 = Origin::Unknown;
|
||||||
|
|
||||||
|
// when
|
||||||
|
let res1 = serde_json::to_string(&o1).unwrap();
|
||||||
|
let res2 = serde_json::to_string(&o2).unwrap();
|
||||||
|
let res3 = serde_json::to_string(&o3).unwrap();
|
||||||
|
let res4 = serde_json::to_string(&o4).unwrap();
|
||||||
|
let res5 = serde_json::to_string(&o5).unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(res1, r#"{"rpc":"test service"}"#);
|
||||||
|
assert_eq!(res2, r#"{"dapp":"http://parity.io"}"#);
|
||||||
|
assert_eq!(res3, r#"{"ipc":"0x0000000000000000000000000000000000000000000000000000000000000005"}"#);
|
||||||
|
assert_eq!(res4, r#"{"signer":"0x000000000000000000000000000000000000000000000000000000000000000a"}"#);
|
||||||
|
assert_eq!(res5, r#""unknown""#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_serialize_dapp_id() {
|
||||||
|
// given
|
||||||
|
let id = DappId("testapp".into());
|
||||||
|
|
||||||
|
// when
|
||||||
|
let res = serde_json::to_string(&id).unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(res, r#""testapp""#);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn should_deserialize_dapp_id() {
|
||||||
|
// given
|
||||||
|
let id = r#""testapp""#;
|
||||||
|
|
||||||
|
// when
|
||||||
|
let res: DappId = serde_json::from_str(id).unwrap();
|
||||||
|
|
||||||
|
// then
|
||||||
|
assert_eq!(res, DappId("testapp".into()));
|
||||||
|
}
|
||||||
|
}
|
@ -18,6 +18,7 @@ use ethcore;
|
|||||||
|
|
||||||
/// Represents condition on minimum block number or block timestamp.
|
/// Represents condition on minimum block number or block timestamp.
|
||||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
|
||||||
|
#[serde(deny_unknown_fields)]
|
||||||
pub enum TransactionCondition {
|
pub enum TransactionCondition {
|
||||||
/// Valid at this minimum block number.
|
/// Valid at this minimum block number.
|
||||||
#[serde(rename="block")]
|
#[serde(rename="block")]
|
||||||
|
@ -18,6 +18,9 @@ extern crate log;
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
#[macro_use]
|
||||||
|
extern crate matches;
|
||||||
|
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use client::{Rpc, RpcError};
|
use client::{Rpc, RpcError};
|
||||||
|
@ -33,6 +33,8 @@ use rpc::informant::RpcStats;
|
|||||||
|
|
||||||
mod session;
|
mod session;
|
||||||
|
|
||||||
|
pub use self::session::MetaExtractor;
|
||||||
|
|
||||||
/// Signer startup error
|
/// Signer startup error
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub enum ServerError {
|
pub enum ServerError {
|
||||||
@ -51,6 +53,11 @@ impl From<ws::Error> for ServerError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Dummy metadata extractor
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct NoopExtractor;
|
||||||
|
impl<M: Metadata> session::MetaExtractor<M> for NoopExtractor {}
|
||||||
|
|
||||||
/// Builder for `WebSockets` server
|
/// Builder for `WebSockets` server
|
||||||
pub struct ServerBuilder {
|
pub struct ServerBuilder {
|
||||||
queue: Arc<ConfirmationsQueue>,
|
queue: Arc<ConfirmationsQueue>,
|
||||||
@ -86,6 +93,17 @@ impl ServerBuilder {
|
|||||||
/// Starts a new `WebSocket` server in separate thread.
|
/// Starts a new `WebSocket` server in separate thread.
|
||||||
/// Returns a `Server` handle which closes the server when droped.
|
/// Returns a `Server` handle which closes the server when droped.
|
||||||
pub fn start<M: Metadata, S: Middleware<M>>(self, addr: SocketAddr, handler: RpcHandler<M, S>) -> Result<Server, ServerError> {
|
pub fn start<M: Metadata, S: Middleware<M>>(self, addr: SocketAddr, handler: RpcHandler<M, S>) -> Result<Server, ServerError> {
|
||||||
|
self.start_with_extractor(addr, handler, NoopExtractor)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Starts a new `WebSocket` server in separate thread.
|
||||||
|
/// Returns a `Server` handle which closes the server when droped.
|
||||||
|
pub fn start_with_extractor<M: Metadata, S: Middleware<M>, T: session::MetaExtractor<M>>(
|
||||||
|
self,
|
||||||
|
addr: SocketAddr,
|
||||||
|
handler: RpcHandler<M, S>,
|
||||||
|
meta_extractor: T,
|
||||||
|
) -> Result<Server, ServerError> {
|
||||||
Server::start(
|
Server::start(
|
||||||
addr,
|
addr,
|
||||||
handler,
|
handler,
|
||||||
@ -93,8 +111,10 @@ impl ServerBuilder {
|
|||||||
self.authcodes_path,
|
self.authcodes_path,
|
||||||
self.skip_origin_validation,
|
self.skip_origin_validation,
|
||||||
self.stats,
|
self.stats,
|
||||||
|
meta_extractor,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// `WebSockets` server implementation.
|
/// `WebSockets` server implementation.
|
||||||
@ -114,13 +134,14 @@ impl Server {
|
|||||||
|
|
||||||
/// Starts a new `WebSocket` server in separate thread.
|
/// Starts a new `WebSocket` server in separate thread.
|
||||||
/// Returns a `Server` handle which closes the server when droped.
|
/// Returns a `Server` handle which closes the server when droped.
|
||||||
fn start<M: Metadata, S: Middleware<M>>(
|
fn start<M: Metadata, S: Middleware<M>, T: session::MetaExtractor<M>>(
|
||||||
addr: SocketAddr,
|
addr: SocketAddr,
|
||||||
handler: RpcHandler<M, S>,
|
handler: RpcHandler<M, S>,
|
||||||
queue: Arc<ConfirmationsQueue>,
|
queue: Arc<ConfirmationsQueue>,
|
||||||
authcodes_path: PathBuf,
|
authcodes_path: PathBuf,
|
||||||
skip_origin_validation: bool,
|
skip_origin_validation: bool,
|
||||||
stats: Option<Arc<RpcStats>>,
|
stats: Option<Arc<RpcStats>>,
|
||||||
|
meta_extractor: T,
|
||||||
) -> Result<Server, ServerError> {
|
) -> Result<Server, ServerError> {
|
||||||
let config = {
|
let config = {
|
||||||
let mut config = ws::Settings::default();
|
let mut config = ws::Settings::default();
|
||||||
@ -135,7 +156,7 @@ impl Server {
|
|||||||
let origin = format!("{}", addr);
|
let origin = format!("{}", addr);
|
||||||
let port = addr.port();
|
let port = addr.port();
|
||||||
let ws = ws::Builder::new().with_settings(config).build(
|
let ws = ws::Builder::new().with_settings(config).build(
|
||||||
session::Factory::new(handler, origin, port, authcodes_path, skip_origin_validation, stats)
|
session::Factory::new(handler, origin, port, authcodes_path, skip_origin_validation, stats, meta_extractor)
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let panic_handler = PanicHandler::new_in_arc();
|
let panic_handler = PanicHandler::new_in_arc();
|
||||||
|
@ -16,15 +16,16 @@
|
|||||||
|
|
||||||
//! Session handlers factory.
|
//! Session handlers factory.
|
||||||
|
|
||||||
use ws;
|
|
||||||
use authcode_store::AuthCodes;
|
|
||||||
use std::path::{PathBuf, Path};
|
use std::path::{PathBuf, Path};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use authcode_store::AuthCodes;
|
||||||
use jsonrpc_core::{Metadata, Middleware};
|
use jsonrpc_core::{Metadata, Middleware};
|
||||||
use jsonrpc_core::reactor::RpcHandler;
|
use jsonrpc_core::reactor::RpcHandler;
|
||||||
use rpc::informant::RpcStats;
|
use rpc::informant::RpcStats;
|
||||||
use util::{H256, version};
|
use util::{H256, version};
|
||||||
|
use ws;
|
||||||
|
|
||||||
#[cfg(feature = "parity-ui")]
|
#[cfg(feature = "parity-ui")]
|
||||||
mod ui {
|
mod ui {
|
||||||
@ -78,35 +79,39 @@ fn origin_is_allowed(self_origin: &str, header: Option<&[u8]>) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn auth_is_valid(codes_path: &Path, protocols: ws::Result<Vec<&str>>) -> bool {
|
fn auth_token_hash(codes_path: &Path, protocols: ws::Result<Vec<&str>>) -> Option<H256> {
|
||||||
match protocols {
|
match protocols {
|
||||||
Ok(ref protocols) if protocols.len() == 1 => {
|
Ok(ref protocols) if protocols.len() == 1 => {
|
||||||
protocols.iter().any(|protocol| {
|
let protocol = protocols[0];
|
||||||
let mut split = protocol.split('_');
|
let mut split = protocol.split('_');
|
||||||
let auth = split.next().and_then(|v| H256::from_str(v).ok());
|
let auth = split.next().and_then(|v| H256::from_str(v).ok());
|
||||||
let time = split.next().and_then(|v| u64::from_str_radix(v, 10).ok());
|
let time = split.next().and_then(|v| u64::from_str_radix(v, 10).ok());
|
||||||
|
|
||||||
if let (Some(auth), Some(time)) = (auth, time) {
|
if let (Some(auth), Some(time)) = (auth, time) {
|
||||||
// Check if the code is valid
|
// Check if the code is valid
|
||||||
AuthCodes::from_file(codes_path)
|
AuthCodes::from_file(codes_path)
|
||||||
.map(|mut codes| {
|
.ok()
|
||||||
// remove old tokens
|
.and_then(|mut codes| {
|
||||||
codes.clear_garbage();
|
// remove old tokens
|
||||||
|
codes.clear_garbage();
|
||||||
|
|
||||||
let res = codes.is_valid(&auth, time);
|
let res = codes.is_valid(&auth, time);
|
||||||
// make sure to save back authcodes - it might have been modified
|
// make sure to save back authcodes - it might have been modified
|
||||||
if codes.to_file(codes_path).is_err() {
|
if codes.to_file(codes_path).is_err() {
|
||||||
warn!(target: "signer", "Couldn't save authorization codes to file.");
|
warn!(target: "signer", "Couldn't save authorization codes to file.");
|
||||||
}
|
}
|
||||||
res
|
|
||||||
})
|
if res {
|
||||||
.unwrap_or(false)
|
Some(auth)
|
||||||
} else {
|
} else {
|
||||||
false
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_ => false
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -125,7 +130,16 @@ fn add_headers(mut response: ws::Response, mime: &str) -> ws::Response {
|
|||||||
response
|
response
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Session<M: Metadata, S: Middleware<M>> {
|
/// Metadata extractor from session data.
|
||||||
|
pub trait MetaExtractor<M: Metadata>: Send + Clone + 'static {
|
||||||
|
/// Extract metadata for given session
|
||||||
|
fn extract_metadata(&self, _session_id: &H256) -> M {
|
||||||
|
Default::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Session<M: Metadata, S: Middleware<M>, T> {
|
||||||
|
session_id: H256,
|
||||||
out: ws::Sender,
|
out: ws::Sender,
|
||||||
skip_origin_validation: bool,
|
skip_origin_validation: bool,
|
||||||
self_origin: String,
|
self_origin: String,
|
||||||
@ -134,16 +148,16 @@ pub struct Session<M: Metadata, S: Middleware<M>> {
|
|||||||
handler: RpcHandler<M, S>,
|
handler: RpcHandler<M, S>,
|
||||||
file_handler: Arc<ui::Handler>,
|
file_handler: Arc<ui::Handler>,
|
||||||
stats: Option<Arc<RpcStats>>,
|
stats: Option<Arc<RpcStats>>,
|
||||||
|
meta_extractor: T,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: Metadata, S: Middleware<M>> Drop for Session<M, S> {
|
impl<M: Metadata, S: Middleware<M>, T> Drop for Session<M, S, T> {
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.stats.as_ref().map(|stats| stats.close_session());
|
self.stats.as_ref().map(|stats| stats.close_session());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: Metadata, S: Middleware<M>> ws::Handler for Session<M, S> {
|
impl<M: Metadata, S: Middleware<M>, T: MetaExtractor<M>> ws::Handler for Session<M, S, T> {
|
||||||
#[cfg_attr(feature="dev", allow(collapsible_if))]
|
|
||||||
fn on_request(&mut self, req: &ws::Request) -> ws::Result<(ws::Response)> {
|
fn on_request(&mut self, req: &ws::Request) -> ws::Result<(ws::Response)> {
|
||||||
trace!(target: "signer", "Handling request: {:?}", req);
|
trace!(target: "signer", "Handling request: {:?}", req);
|
||||||
|
|
||||||
@ -186,9 +200,15 @@ impl<M: Metadata, S: Middleware<M>> ws::Handler for Session<M, S> {
|
|||||||
// (styles file skips origin validation, so make sure to prevent WS connections on this resource)
|
// (styles file skips origin validation, so make sure to prevent WS connections on this resource)
|
||||||
if req.header("sec-websocket-key").is_some() && !is_styles_file {
|
if req.header("sec-websocket-key").is_some() && !is_styles_file {
|
||||||
// Check authorization
|
// Check authorization
|
||||||
if !auth_is_valid(&self.authcodes_path, req.protocols()) {
|
let auth_token_hash = auth_token_hash(&self.authcodes_path, req.protocols());
|
||||||
info!(target: "signer", "Unauthorized connection to Signer API blocked.");
|
match auth_token_hash {
|
||||||
return Ok(error(ErrorType::Forbidden, "Not Authorized", "Request to this API was not authorized.", None));
|
None => {
|
||||||
|
info!(target: "signer", "Unauthorized connection to Signer API blocked.");
|
||||||
|
return Ok(error(ErrorType::Forbidden, "Not Authorized", "Request to this API was not authorized.", None));
|
||||||
|
},
|
||||||
|
Some(auth) => {
|
||||||
|
self.session_id = auth;
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
let protocols = req.protocols().expect("Existence checked by authorization.");
|
let protocols = req.protocols().expect("Existence checked by authorization.");
|
||||||
@ -214,8 +234,8 @@ impl<M: Metadata, S: Middleware<M>> ws::Handler for Session<M, S> {
|
|||||||
fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
|
fn on_message(&mut self, msg: ws::Message) -> ws::Result<()> {
|
||||||
let req = msg.as_text()?;
|
let req = msg.as_text()?;
|
||||||
let out = self.out.clone();
|
let out = self.out.clone();
|
||||||
// TODO [ToDr] Extract metadata for PubSub/Session
|
// TODO [ToDr] Move to on_connect
|
||||||
let metadata = Default::default();
|
let metadata = self.meta_extractor.extract_metadata(&self.session_id);
|
||||||
|
|
||||||
self.handler.handle_request(req, metadata, move |response| {
|
self.handler.handle_request(req, metadata, move |response| {
|
||||||
if let Some(result) = response {
|
if let Some(result) = response {
|
||||||
@ -229,24 +249,26 @@ impl<M: Metadata, S: Middleware<M>> ws::Handler for Session<M, S> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Factory<M: Metadata, S: Middleware<M>> {
|
pub struct Factory<M: Metadata, S: Middleware<M>, T> {
|
||||||
handler: RpcHandler<M, S>,
|
handler: RpcHandler<M, S>,
|
||||||
skip_origin_validation: bool,
|
skip_origin_validation: bool,
|
||||||
self_origin: String,
|
self_origin: String,
|
||||||
self_port: u16,
|
self_port: u16,
|
||||||
authcodes_path: PathBuf,
|
authcodes_path: PathBuf,
|
||||||
|
meta_extractor: T,
|
||||||
file_handler: Arc<ui::Handler>,
|
file_handler: Arc<ui::Handler>,
|
||||||
stats: Option<Arc<RpcStats>>,
|
stats: Option<Arc<RpcStats>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: Metadata, S: Middleware<M>> Factory<M, S> {
|
impl<M: Metadata, S: Middleware<M>, T> Factory<M, S, T> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
handler: RpcHandler<M, S>,
|
handler: RpcHandler<M, S>,
|
||||||
self_origin: String,
|
self_origin: String,
|
||||||
self_port: u16,
|
self_port: u16,
|
||||||
authcodes_path: PathBuf,
|
authcodes_path: PathBuf,
|
||||||
skip_origin_validation: bool,
|
skip_origin_validation: bool,
|
||||||
stats: Option<Arc<RpcStats>>,
|
stats: Option<Arc<RpcStats>>,
|
||||||
|
meta_extractor: T,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
Factory {
|
Factory {
|
||||||
handler: handler,
|
handler: handler,
|
||||||
@ -254,25 +276,28 @@ impl<M: Metadata, S: Middleware<M>> Factory<M, S> {
|
|||||||
self_origin: self_origin,
|
self_origin: self_origin,
|
||||||
self_port: self_port,
|
self_port: self_port,
|
||||||
authcodes_path: authcodes_path,
|
authcodes_path: authcodes_path,
|
||||||
|
meta_extractor: meta_extractor,
|
||||||
file_handler: Arc::new(ui::Handler::default()),
|
file_handler: Arc::new(ui::Handler::default()),
|
||||||
stats: stats,
|
stats: stats,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<M: Metadata, S: Middleware<M>> ws::Factory for Factory<M, S> {
|
impl<M: Metadata, S: Middleware<M>, T: MetaExtractor<M>> ws::Factory for Factory<M, S, T> {
|
||||||
type Handler = Session<M, S>;
|
type Handler = Session<M, S, T>;
|
||||||
|
|
||||||
fn connection_made(&mut self, sender: ws::Sender) -> Self::Handler {
|
fn connection_made(&mut self, sender: ws::Sender) -> Self::Handler {
|
||||||
self.stats.as_ref().map(|stats| stats.open_session());
|
self.stats.as_ref().map(|stats| stats.open_session());
|
||||||
|
|
||||||
Session {
|
Session {
|
||||||
|
session_id: 0.into(),
|
||||||
out: sender,
|
out: sender,
|
||||||
handler: self.handler.clone(),
|
handler: self.handler.clone(),
|
||||||
skip_origin_validation: self.skip_origin_validation,
|
skip_origin_validation: self.skip_origin_validation,
|
||||||
self_origin: self.self_origin.clone(),
|
self_origin: self.self_origin.clone(),
|
||||||
self_port: self.self_port,
|
self_port: self.self_port,
|
||||||
authcodes_path: self.authcodes_path.clone(),
|
authcodes_path: self.authcodes_path.clone(),
|
||||||
|
meta_extractor: self.meta_extractor.clone(),
|
||||||
file_handler: self.file_handler.clone(),
|
file_handler: self.file_handler.clone(),
|
||||||
stats: self.stats.clone(),
|
stats: self.stats.clone(),
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user