Squashed: Public Node
This commit is contained in:
parent
858c974440
commit
9bd3f10f41
@ -29,7 +29,7 @@ impl Brain {
|
||||
impl Generator for Brain {
|
||||
fn generate(self) -> Result<KeyPair, Error> {
|
||||
let seed = self.0;
|
||||
let mut secret = seed.bytes().collect::<Vec<u8>>().keccak256();
|
||||
let mut secret = seed.into_bytes().keccak256();
|
||||
|
||||
let mut i = 0;
|
||||
loop {
|
||||
|
@ -171,6 +171,7 @@
|
||||
"geopattern": "1.2.3",
|
||||
"isomorphic-fetch": "2.2.1",
|
||||
"js-sha3": "0.5.5",
|
||||
"keythereum": "0.4.3",
|
||||
"lodash": "4.17.2",
|
||||
"loglevel": "1.4.1",
|
||||
"marked": "0.3.6",
|
||||
@ -207,6 +208,7 @@
|
||||
"redux-thunk": "2.1.0",
|
||||
"rlp": "2.0.0",
|
||||
"scryptsy": "2.0.0",
|
||||
"secp256k1": "3.2.5",
|
||||
"solc": "ngotchac/solc-js",
|
||||
"store": "1.3.20",
|
||||
"sw-toolbox": "^3.6.0",
|
||||
|
@ -23,6 +23,7 @@ import { Db, Eth, Parity, Net, Personal, Shh, Signer, Trace, Web3 } from './rpc'
|
||||
import Subscriptions from './subscriptions';
|
||||
import util from './util';
|
||||
import { isFunction } from './util/types';
|
||||
import { LocalAccountsMiddleware } from './local';
|
||||
|
||||
export default class Api extends EventEmitter {
|
||||
constructor (transport) {
|
||||
@ -45,6 +46,21 @@ export default class Api extends EventEmitter {
|
||||
this._web3 = new Web3(transport);
|
||||
|
||||
this._subscriptions = new Subscriptions(this);
|
||||
|
||||
// Doing a request here in test env would cause an error
|
||||
if (process.env.NODE_ENV !== 'test') {
|
||||
const middleware = this.parity
|
||||
.nodeKind()
|
||||
.then((nodeKind) => {
|
||||
if (nodeKind.availability === 'public') {
|
||||
return new LocalAccountsMiddleware(transport);
|
||||
}
|
||||
|
||||
return null;
|
||||
});
|
||||
|
||||
transport.addMiddleware(middleware);
|
||||
}
|
||||
}
|
||||
|
||||
get db () {
|
||||
|
95
js/src/api/local/accounts/account.js
Normal file
95
js/src/api/local/accounts/account.js
Normal file
@ -0,0 +1,95 @@
|
||||
// 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 { keythereum } from '../ethkey';
|
||||
|
||||
export default class Account {
|
||||
constructor (persist, data) {
|
||||
const {
|
||||
keyObject,
|
||||
meta = {},
|
||||
name = ''
|
||||
} = data;
|
||||
|
||||
this._persist = persist;
|
||||
this._keyObject = keyObject;
|
||||
this._name = name;
|
||||
this._meta = meta;
|
||||
}
|
||||
|
||||
isValidPassword (password) {
|
||||
try {
|
||||
keythereum.recover(Buffer.from(password), this._keyObject);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
get address () {
|
||||
return `0x${this._keyObject.address.toLowerCase()}`;
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
set name (name) {
|
||||
this._name = name;
|
||||
|
||||
this._persist();
|
||||
}
|
||||
|
||||
get meta () {
|
||||
return JSON.stringify(this._meta);
|
||||
}
|
||||
|
||||
set meta (meta) {
|
||||
this._meta = JSON.parse(meta);
|
||||
|
||||
this._persist();
|
||||
}
|
||||
|
||||
get uuid () {
|
||||
return this._keyObject.id;
|
||||
}
|
||||
|
||||
decryptPrivateKey (password) {
|
||||
return keythereum.recover(Buffer.from(password), this._keyObject);
|
||||
}
|
||||
|
||||
static fromPrivateKey (persist, key, password) {
|
||||
const iv = keythereum.crypto.randomBytes(16);
|
||||
const salt = keythereum.crypto.randomBytes(32);
|
||||
|
||||
// Keythereum will fail if `password` is an empty string
|
||||
password = Buffer.from(password);
|
||||
|
||||
const keyObject = keythereum.dump(password, key, salt, iv);
|
||||
|
||||
const account = new Account(persist, { keyObject });
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
toJSON () {
|
||||
return {
|
||||
keyObject: this._keyObject,
|
||||
name: this._name,
|
||||
meta: this._meta
|
||||
};
|
||||
}
|
||||
}
|
120
js/src/api/local/accounts/accounts.js
Normal file
120
js/src/api/local/accounts/accounts.js
Normal file
@ -0,0 +1,120 @@
|
||||
// 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 Account from './account';
|
||||
import localStore from 'store';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
|
||||
const LS_STORE_KEY = '_parity::localAccounts';
|
||||
|
||||
export default class Accounts {
|
||||
constructor (data = localStore.get(LS_STORE_KEY) || {}) {
|
||||
const {
|
||||
last = NULL_ADDRESS,
|
||||
store = []
|
||||
} = data;
|
||||
|
||||
this.persist = debounce(() => {
|
||||
localStore.set(LS_STORE_KEY, this);
|
||||
}, 100);
|
||||
|
||||
this._last = last;
|
||||
this._store = store.map((data) => new Account(this.persist, data));
|
||||
}
|
||||
|
||||
create (secret, password) {
|
||||
const privateKey = Buffer.from(secret.slice(2), 'hex');
|
||||
const account = Account.fromPrivateKey(this.persist, privateKey, password);
|
||||
|
||||
this._store.push(account);
|
||||
this.lastAddress = account.address;
|
||||
|
||||
this.persist();
|
||||
|
||||
return account.address;
|
||||
}
|
||||
|
||||
set lastAddress (value) {
|
||||
this._last = value.toLowerCase();
|
||||
}
|
||||
|
||||
get lastAddress () {
|
||||
return this._last;
|
||||
}
|
||||
|
||||
get (address) {
|
||||
address = address.toLowerCase();
|
||||
|
||||
this.lastAddress = address;
|
||||
|
||||
const account = this._store.find((account) => account.address === address);
|
||||
|
||||
if (!account) {
|
||||
throw new Error(`Account not found: ${address}`);
|
||||
}
|
||||
|
||||
return account;
|
||||
}
|
||||
|
||||
remove (address, password) {
|
||||
address = address.toLowerCase();
|
||||
|
||||
const index = this._store.findIndex((account) => account.address === address);
|
||||
|
||||
if (index === -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const account = this._store[index];
|
||||
|
||||
if (!account.isValidPassword(password)) {
|
||||
console.log('invalid password');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (address === this.lastAddress) {
|
||||
this.lastAddress = NULL_ADDRESS;
|
||||
}
|
||||
|
||||
this._store.splice(index, 1);
|
||||
|
||||
this.persist();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
mapArray (mapper) {
|
||||
return this._store.map(mapper);
|
||||
}
|
||||
|
||||
mapObject (mapper) {
|
||||
const result = {};
|
||||
|
||||
this._store.forEach((account) => {
|
||||
result[account.address] = mapper(account);
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
toJSON () {
|
||||
return {
|
||||
last: this._last,
|
||||
store: this._store
|
||||
};
|
||||
}
|
||||
}
|
21
js/src/api/local/accounts/index.js
Normal file
21
js/src/api/local/accounts/index.js
Normal file
@ -0,0 +1,21 @@
|
||||
// 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 Accounts from './accounts';
|
||||
|
||||
const accounts = new Accounts();
|
||||
|
||||
export default accounts;
|
7778
js/src/api/local/ethkey/dictionary.js
Normal file
7778
js/src/api/local/ethkey/dictionary.js
Normal file
File diff suppressed because it is too large
Load Diff
87
js/src/api/local/ethkey/index.js
Normal file
87
js/src/api/local/ethkey/index.js
Normal file
@ -0,0 +1,87 @@
|
||||
// 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 dictionary from './dictionary';
|
||||
|
||||
// Allow a web worker in the browser, with a fallback for Node.js
|
||||
const hasWebWorkers = typeof Worker !== 'undefined';
|
||||
const KeyWorker = hasWebWorkers ? require('worker-loader!./worker')
|
||||
: require('./worker').KeyWorker;
|
||||
|
||||
// Local accounts should never be used outside of the browser
|
||||
export let keythereum = null;
|
||||
|
||||
if (hasWebWorkers) {
|
||||
require('keythereum/dist/keythereum');
|
||||
|
||||
keythereum = window.keythereum;
|
||||
}
|
||||
|
||||
export function phraseToAddress (phrase) {
|
||||
return phraseToWallet(phrase).then((wallet) => wallet.address);
|
||||
}
|
||||
|
||||
export function phraseToWallet (phrase) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const worker = new KeyWorker();
|
||||
|
||||
worker.postMessage(phrase);
|
||||
worker.onmessage = ({ data }) => {
|
||||
resolve(data);
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
export function randomBytes (length) {
|
||||
if (keythereum) {
|
||||
return keythereum.crypto.randomBytes(length);
|
||||
}
|
||||
|
||||
const buf = Buffer.alloc(length);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
buf[i] = Math.random() * 255;
|
||||
}
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
export function randomNumber (max) {
|
||||
// Use 24 bits to avoid the integer becoming signed via bitshifts
|
||||
const rand = randomBytes(3);
|
||||
|
||||
const integer = (rand[0] << 16) | (rand[1] << 8) | rand[2];
|
||||
|
||||
// floor to integer value via bitor 0
|
||||
return ((integer / 0xFFFFFF) * max) | 0;
|
||||
}
|
||||
|
||||
export function randomWord () {
|
||||
// TODO mh: use better entropy
|
||||
const index = randomNumber(dictionary.length);
|
||||
|
||||
return dictionary[index];
|
||||
}
|
||||
|
||||
export function randomPhrase (length) {
|
||||
const words = [];
|
||||
|
||||
while (length--) {
|
||||
words.push(randomWord());
|
||||
}
|
||||
|
||||
return words.join(' ');
|
||||
}
|
95
js/src/api/local/ethkey/index.spec.js
Normal file
95
js/src/api/local/ethkey/index.spec.js
Normal file
@ -0,0 +1,95 @@
|
||||
// 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 dictionary from './dictionary';
|
||||
import {
|
||||
phraseToAddress,
|
||||
phraseToWallet,
|
||||
randomNumber,
|
||||
randomWord,
|
||||
randomPhrase
|
||||
} from './';
|
||||
|
||||
describe('api/local/ethkey', () => {
|
||||
describe('randomNumber', () => {
|
||||
it('generates numbers in range', () => {
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const number = randomNumber(7777);
|
||||
|
||||
expect(number).to.be.at.least(0);
|
||||
expect(number).to.be.below(7777);
|
||||
expect(number % 1).to.be.equal(0);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('randomWord', () => {
|
||||
it('generates a random word from the dictionary', () => {
|
||||
const word = randomWord();
|
||||
|
||||
expect(dictionary.includes(word)).to.be.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('randomPhrase', () => {
|
||||
it('generates a random phrase from the dictionary', () => {
|
||||
const phrase = randomPhrase(7).split(' ');
|
||||
|
||||
expect(phrase.length).to.be.equal(7);
|
||||
|
||||
phrase.forEach((word) => {
|
||||
expect(dictionary.includes(word)).to.be.equal(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('phraseToAddress', function () {
|
||||
this.timeout(10000);
|
||||
|
||||
it('generates a valid address', () => {
|
||||
const phrase = randomPhrase(12);
|
||||
|
||||
return phraseToAddress(phrase).then((address) => {
|
||||
expect(address.length).to.be.equal(42);
|
||||
expect(address.slice(0, 4)).to.be.equal('0x00');
|
||||
});
|
||||
});
|
||||
|
||||
it('generates valid address for empty phrase', () => {
|
||||
return phraseToAddress('').then((address) => {
|
||||
expect(address).to.be.equal('0x00a329c0648769a73afac7f9381e08fb43dbea72');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('phraseToWallet', function () {
|
||||
this.timeout(10000);
|
||||
|
||||
it('generates a valid wallet object', () => {
|
||||
const phrase = randomPhrase(12);
|
||||
|
||||
return phraseToWallet(phrase).then((wallet) => {
|
||||
expect(wallet.address.length).to.be.equal(42);
|
||||
expect(wallet.secret.length).to.be.equal(66);
|
||||
expect(wallet.public.length).to.be.equal(130);
|
||||
|
||||
expect(wallet.address.slice(0, 4)).to.be.equal('0x00');
|
||||
expect(wallet.secret.slice(0, 2)).to.be.equal('0x');
|
||||
expect(wallet.public.slice(0, 2)).to.be.equal('0x');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
89
js/src/api/local/ethkey/worker.js
Normal file
89
js/src/api/local/ethkey/worker.js
Normal file
@ -0,0 +1,89 @@
|
||||
// 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 { keccak_256 as keccak256 } from 'js-sha3';
|
||||
import secp256k1 from 'secp256k1/js';
|
||||
|
||||
// Stay compatible between environments
|
||||
if (typeof self !== 'object') {
|
||||
const scope = typeof global === 'undefined' ? window : global;
|
||||
|
||||
scope.self = scope;
|
||||
}
|
||||
|
||||
function bytesToHex (bytes) {
|
||||
return '0x' + Array.from(bytes).map(n => ('0' + n.toString(16)).slice(-2)).join('');
|
||||
}
|
||||
|
||||
// Logic ported from /ethkey/src/brain.rs
|
||||
function phraseToWallet (phrase) {
|
||||
let secret = keccak256.array(phrase);
|
||||
|
||||
for (let i = 0; i < 16384; i++) {
|
||||
secret = keccak256.array(secret);
|
||||
}
|
||||
|
||||
while (true) {
|
||||
secret = keccak256.array(secret);
|
||||
|
||||
const secretBuf = Buffer.from(secret);
|
||||
|
||||
if (secp256k1.privateKeyVerify(secretBuf)) {
|
||||
// No compression, slice out last 64 bytes
|
||||
const publicBuf = secp256k1.publicKeyCreate(secretBuf, false).slice(-64);
|
||||
const address = keccak256.array(publicBuf).slice(12);
|
||||
|
||||
if (address[0] !== 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const wallet = {
|
||||
secret: bytesToHex(secretBuf),
|
||||
public: bytesToHex(publicBuf),
|
||||
address: bytesToHex(address)
|
||||
};
|
||||
|
||||
return wallet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.onmessage = function ({ data }) {
|
||||
const wallet = phraseToWallet(data);
|
||||
|
||||
postMessage(wallet);
|
||||
close();
|
||||
};
|
||||
|
||||
// Emulate a web worker in Node.js
|
||||
class KeyWorker {
|
||||
postMessage (data) {
|
||||
// Force async
|
||||
setTimeout(() => {
|
||||
const wallet = phraseToWallet(data);
|
||||
|
||||
this.onmessage({ data: wallet });
|
||||
}, 0);
|
||||
}
|
||||
|
||||
onmessage (event) {
|
||||
// no-op to be overriden
|
||||
}
|
||||
}
|
||||
|
||||
if (exports != null) {
|
||||
exports.KeyWorker = KeyWorker;
|
||||
}
|
17
js/src/api/local/index.js
Normal file
17
js/src/api/local/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 LocalAccountsMiddleware from './middleware';
|
173
js/src/api/local/middleware.js
Normal file
173
js/src/api/local/middleware.js
Normal file
@ -0,0 +1,173 @@
|
||||
// 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 EthereumTx from 'ethereumjs-tx';
|
||||
import accounts from './accounts';
|
||||
import transactions from './transactions';
|
||||
import { Middleware } from '../transport';
|
||||
import { inNumber16 } from '../format/input';
|
||||
import { phraseToWallet, phraseToAddress, randomPhrase } from './ethkey';
|
||||
|
||||
export default class LocalAccountsMiddleware extends Middleware {
|
||||
// Maps transaction requests to transaction hashes.
|
||||
// This allows the locally-signed transactions to emulate the signer.
|
||||
transactionHashes = {};
|
||||
transactions = {};
|
||||
|
||||
// Current transaction id. This doesn't need to be stored, as it's
|
||||
// only relevant for the current the session.
|
||||
transactionId = 1;
|
||||
|
||||
constructor (transport) {
|
||||
super(transport);
|
||||
|
||||
const register = this.register.bind(this);
|
||||
|
||||
register('eth_accounts', () => {
|
||||
return accounts.mapArray((account) => account.address);
|
||||
});
|
||||
|
||||
register('eth_coinbase', () => {
|
||||
return accounts.lastAddress;
|
||||
});
|
||||
|
||||
register('parity_accountsInfo', () => {
|
||||
return accounts.mapObject(({ name }) => {
|
||||
return { name };
|
||||
});
|
||||
});
|
||||
|
||||
register('parity_allAccountsInfo', () => {
|
||||
return accounts.mapObject(({ name, meta, uuid }) => {
|
||||
return { name, meta, uuid };
|
||||
});
|
||||
});
|
||||
|
||||
register('parity_checkRequest', ([id]) => {
|
||||
return transactions.hash(id) || Promise.resolve(null);
|
||||
});
|
||||
|
||||
register('parity_defaultAccount', () => {
|
||||
return accounts.lastAddress;
|
||||
});
|
||||
|
||||
register('parity_generateSecretPhrase', () => {
|
||||
return randomPhrase(12);
|
||||
});
|
||||
|
||||
register('parity_getNewDappsAddresses', () => {
|
||||
return [];
|
||||
});
|
||||
|
||||
register('parity_hardwareAccountsInfo', () => {
|
||||
return {};
|
||||
});
|
||||
|
||||
register('parity_newAccountFromPhrase', ([phrase, password]) => {
|
||||
return phraseToWallet(phrase)
|
||||
.then((wallet) => {
|
||||
return accounts.create(wallet.secret, password);
|
||||
});
|
||||
});
|
||||
|
||||
register('parity_setAccountMeta', ([address, meta]) => {
|
||||
accounts.get(address).meta = meta;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
register('parity_setAccountName', ([address, name]) => {
|
||||
accounts.get(address).name = name;
|
||||
|
||||
return true;
|
||||
});
|
||||
|
||||
register('parity_postTransaction', ([tx]) => {
|
||||
if (!tx.from) {
|
||||
tx.from = accounts.lastAddress;
|
||||
}
|
||||
|
||||
tx.nonce = null;
|
||||
tx.condition = null;
|
||||
|
||||
return transactions.add(tx);
|
||||
});
|
||||
|
||||
register('parity_phraseToAddress', ([phrase]) => {
|
||||
return phraseToAddress(phrase);
|
||||
});
|
||||
|
||||
register('parity_useLocalAccounts', () => {
|
||||
return true;
|
||||
});
|
||||
|
||||
register('parity_listGethAccounts', () => {
|
||||
return [];
|
||||
});
|
||||
|
||||
register('parity_listRecentDapps', () => {
|
||||
return {};
|
||||
});
|
||||
|
||||
register('parity_killAccount', ([address, password]) => {
|
||||
return accounts.remove(address, password);
|
||||
});
|
||||
|
||||
register('signer_confirmRequest', ([id, modify, password]) => {
|
||||
const {
|
||||
gasPrice,
|
||||
gas: gasLimit,
|
||||
from,
|
||||
to,
|
||||
value,
|
||||
data
|
||||
} = Object.assign(transactions.get(id), modify);
|
||||
|
||||
return this
|
||||
.rpcRequest('parity_nextNonce', [from])
|
||||
.then((nonce) => {
|
||||
const tx = new EthereumTx({
|
||||
nonce,
|
||||
to,
|
||||
data,
|
||||
gasLimit: inNumber16(gasLimit),
|
||||
gasPrice: inNumber16(gasPrice),
|
||||
value: inNumber16(value)
|
||||
});
|
||||
const account = accounts.get(from);
|
||||
|
||||
tx.sign(account.decryptPrivateKey(password));
|
||||
|
||||
const serializedTx = `0x${tx.serialize().toString('hex')}`;
|
||||
|
||||
return this.rpcRequest('eth_sendRawTransaction', [serializedTx]);
|
||||
})
|
||||
.then((hash) => {
|
||||
transactions.confirm(id, hash);
|
||||
|
||||
return {};
|
||||
});
|
||||
});
|
||||
|
||||
register('signer_rejectRequest', ([id]) => {
|
||||
return transactions.reject(id);
|
||||
});
|
||||
|
||||
register('signer_requestsToConfirm', () => {
|
||||
return transactions.requestsToConfirm();
|
||||
});
|
||||
}
|
||||
}
|
123
js/src/api/local/transactions.js
Normal file
123
js/src/api/local/transactions.js
Normal file
@ -0,0 +1,123 @@
|
||||
// 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 { toHex } from '../util/format';
|
||||
import { TransportError } from '../transport';
|
||||
|
||||
const AWAITING = Symbol('awaiting');
|
||||
const CONFIRMED = Symbol('confirmed');
|
||||
const REJECTED = Symbol('rejected');
|
||||
|
||||
class Transactions {
|
||||
constructor () {
|
||||
this.reset();
|
||||
}
|
||||
|
||||
// should only really be needed in the constructor and tests
|
||||
reset () {
|
||||
this._id = 1;
|
||||
this._states = {};
|
||||
}
|
||||
|
||||
nextId () {
|
||||
return toHex(this._id++);
|
||||
}
|
||||
|
||||
add (tx) {
|
||||
const id = this.nextId();
|
||||
|
||||
this._states[id] = {
|
||||
status: AWAITING,
|
||||
transaction: tx
|
||||
};
|
||||
|
||||
return id;
|
||||
}
|
||||
|
||||
get (id) {
|
||||
const state = this._states[id];
|
||||
|
||||
if (!state || state.status !== AWAITING) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return state.transaction;
|
||||
}
|
||||
|
||||
hash (id) {
|
||||
const state = this._states[id];
|
||||
|
||||
if (!state) {
|
||||
return null;
|
||||
}
|
||||
|
||||
switch (state.status) {
|
||||
case REJECTED:
|
||||
throw TransportError.requestRejected();
|
||||
case CONFIRMED:
|
||||
return state.hash;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
confirm (id, hash) {
|
||||
const state = this._states[id];
|
||||
|
||||
if (!state || state.status !== AWAITING) {
|
||||
throw new Error('Trying to confirm an invalid transaction');
|
||||
}
|
||||
|
||||
state.hash = hash;
|
||||
state.status = CONFIRMED;
|
||||
}
|
||||
|
||||
reject (id) {
|
||||
const state = this._states[id];
|
||||
|
||||
if (!state) {
|
||||
return false;
|
||||
}
|
||||
|
||||
state.status = REJECTED;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
requestsToConfirm () {
|
||||
const result = [];
|
||||
|
||||
Object.keys(this._states).forEach((id) => {
|
||||
const state = this._states[id];
|
||||
|
||||
if (state.status === AWAITING) {
|
||||
result.push({
|
||||
id,
|
||||
origin: {
|
||||
signer: '0x0'
|
||||
},
|
||||
payload: {
|
||||
sendTransaction: state.transaction
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
export default new Transactions();
|
68
js/src/api/local/transactions.spec.js
Normal file
68
js/src/api/local/transactions.spec.js
Normal file
@ -0,0 +1,68 @@
|
||||
// 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 transactions from './transactions';
|
||||
import { TransportError } from '../transport/error';
|
||||
|
||||
const DUMMY_TX = 'dummy';
|
||||
|
||||
describe('api/local/transactions', () => {
|
||||
beforeEach(() => {
|
||||
transactions.reset();
|
||||
});
|
||||
|
||||
it('can store transactions', () => {
|
||||
const id1 = transactions.add(DUMMY_TX);
|
||||
const id2 = transactions.add(DUMMY_TX);
|
||||
const requests = transactions.requestsToConfirm();
|
||||
|
||||
expect(id1).to.be.equal('0x1');
|
||||
expect(id2).to.be.equal('0x2');
|
||||
expect(requests.length).to.be.equal(2);
|
||||
expect(requests[0].id).to.be.equal(id1);
|
||||
expect(requests[1].id).to.be.equal(id2);
|
||||
expect(requests[0].payload.sendTransaction).to.be.equal(DUMMY_TX);
|
||||
expect(requests[1].payload.sendTransaction).to.be.equal(DUMMY_TX);
|
||||
});
|
||||
|
||||
it('can confirm transactions', () => {
|
||||
const id1 = transactions.add(DUMMY_TX);
|
||||
const id2 = transactions.add(DUMMY_TX);
|
||||
|
||||
const hash1 = '0x1111111111111111111111111111111111111111';
|
||||
const hash2 = '0x2222222222222222222222222222222222222222';
|
||||
|
||||
transactions.confirm(id1, hash1);
|
||||
transactions.confirm(id2, hash2);
|
||||
|
||||
const requests = transactions.requestsToConfirm();
|
||||
|
||||
expect(requests.length).to.be.equal(0);
|
||||
expect(transactions.hash(id1)).to.be.equal(hash1);
|
||||
expect(transactions.hash(id2)).to.be.equal(hash2);
|
||||
});
|
||||
|
||||
it('can reject transactions', () => {
|
||||
const id = transactions.add(DUMMY_TX);
|
||||
|
||||
transactions.reject(id);
|
||||
|
||||
const requests = transactions.requestsToConfirm();
|
||||
|
||||
expect(requests.length).to.be.equal(0);
|
||||
expect(() => transactions.hash(id)).to.throw(TransportError);
|
||||
});
|
||||
});
|
@ -42,6 +42,10 @@ export const ERROR_CODES = {
|
||||
};
|
||||
|
||||
export default class TransportError extends ExtendableError {
|
||||
static requestRejected (method = null) {
|
||||
return new TransportError(method, ERROR_CODES.REQUEST_REJECTED, 'Request has been rejected.');
|
||||
}
|
||||
|
||||
constructor (method, code, message) {
|
||||
const m = `${method}: ${code}: ${message}`;
|
||||
|
||||
|
@ -46,7 +46,7 @@ export default class Http extends JsonRpcBase {
|
||||
};
|
||||
}
|
||||
|
||||
execute (method, ...params) {
|
||||
_execute (method, params) {
|
||||
const request = this._encodeOptions(method, params);
|
||||
|
||||
return fetch(this._url, request)
|
||||
|
@ -16,4 +16,5 @@
|
||||
|
||||
export Http from './http';
|
||||
export Ws from './ws';
|
||||
export TransportError from './error.js';
|
||||
export TransportError from './error';
|
||||
export Middleware from './middleware';
|
||||
|
@ -15,6 +15,7 @@
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import EventEmitter from 'eventemitter3';
|
||||
import { Logging } from '../subscriptions';
|
||||
|
||||
export default class JsonRpcBase extends EventEmitter {
|
||||
constructor () {
|
||||
@ -23,6 +24,7 @@ export default class JsonRpcBase extends EventEmitter {
|
||||
this._id = 1;
|
||||
this._debug = false;
|
||||
this._connected = false;
|
||||
this._middlewareList = Promise.resolve([]);
|
||||
}
|
||||
|
||||
encode (method, params) {
|
||||
@ -36,6 +38,65 @@ export default class JsonRpcBase extends EventEmitter {
|
||||
return json;
|
||||
}
|
||||
|
||||
addMiddleware (middleware) {
|
||||
this._middlewareList = Promise
|
||||
.all([
|
||||
middleware,
|
||||
this._middlewareList
|
||||
])
|
||||
.then(([middleware, middlewareList]) => {
|
||||
// Do nothing if `handlerPromise` resolves to a null-y value.
|
||||
if (middleware == null) {
|
||||
return middlewareList;
|
||||
}
|
||||
|
||||
// don't mutate the original array
|
||||
return middlewareList.concat([middleware]);
|
||||
});
|
||||
}
|
||||
|
||||
_wrapSuccessResult (result) {
|
||||
return {
|
||||
id: this._id,
|
||||
jsonrpc: '2.0',
|
||||
result
|
||||
};
|
||||
}
|
||||
|
||||
_wrapErrorResult (error) {
|
||||
return {
|
||||
id: this._id,
|
||||
jsonrpc: '2.0',
|
||||
error: {
|
||||
code: error.code,
|
||||
message: error.text
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
execute (method, ...params) {
|
||||
return this._middlewareList.then((middlewareList) => {
|
||||
for (const middleware of middlewareList) {
|
||||
const res = middleware.handle(method, params);
|
||||
|
||||
if (res != null) {
|
||||
const result = this._wrapSuccessResult(res);
|
||||
const json = this.encode(method, params);
|
||||
|
||||
Logging.send(method, params, { json, result });
|
||||
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
return this._execute(method, params);
|
||||
});
|
||||
}
|
||||
|
||||
_execute () {
|
||||
throw new Error('Missing implementation of JsonRpcBase#_execute');
|
||||
}
|
||||
|
||||
_setConnected () {
|
||||
if (!this._connected) {
|
||||
this._connected = true;
|
||||
|
42
js/src/api/transport/middleware.js
Normal file
42
js/src/api/transport/middleware.js
Normal file
@ -0,0 +1,42 @@
|
||||
// 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 class Middleware {
|
||||
constructor (transport) {
|
||||
this._transport = transport;
|
||||
this._handlers = {};
|
||||
}
|
||||
|
||||
register (method, handler) {
|
||||
this._handlers[method] = handler;
|
||||
}
|
||||
|
||||
handle (method, params) {
|
||||
const handler = this._handlers[method];
|
||||
|
||||
if (handler != null) {
|
||||
const response = handler(params);
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
rpcRequest (method, params) {
|
||||
return this._transport._execute(method, params);
|
||||
}
|
||||
}
|
58
js/src/api/transport/middleware.spec.js
Normal file
58
js/src/api/transport/middleware.spec.js
Normal file
@ -0,0 +1,58 @@
|
||||
// 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 Middleware from './middleware';
|
||||
import JsonRpcBase from './jsonRpcBase';
|
||||
|
||||
const MOCKED = 'mocked!';
|
||||
|
||||
class MockTransport extends JsonRpcBase {
|
||||
_execute () {
|
||||
return Promise.resolve(MOCKED);
|
||||
}
|
||||
}
|
||||
|
||||
describe('api/transport/Middleware', () => {
|
||||
let middleware;
|
||||
let transport;
|
||||
|
||||
beforeEach(() => {
|
||||
transport = new MockTransport();
|
||||
middleware = new Middleware(transport);
|
||||
|
||||
middleware.register('mock_rpc', ([num]) => num);
|
||||
middleware.register('mock_null', () => null);
|
||||
transport.addMiddleware(middleware);
|
||||
});
|
||||
|
||||
it('Routes requests to middleware', () => {
|
||||
return transport.execute('mock_rpc', 100).then((num) => {
|
||||
expect(num).to.be.equal(100);
|
||||
});
|
||||
});
|
||||
|
||||
it('Passes non-mocked requests through', () => {
|
||||
return transport.execute('not_moced', 200).then((result) => {
|
||||
expect(result).to.be.equal(MOCKED);
|
||||
});
|
||||
});
|
||||
|
||||
it('Passes mocked requests through, if middleware returns null', () => {
|
||||
return transport.execute('mock_null', 300).then((result) => {
|
||||
expect(result).to.be.equal(MOCKED);
|
||||
});
|
||||
});
|
||||
});
|
@ -244,7 +244,7 @@ export default class Ws extends JsonRpcBase {
|
||||
message.timestamp = Date.now();
|
||||
}
|
||||
|
||||
execute (method, ...params) {
|
||||
_execute (method, params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = this.id;
|
||||
const json = this.encode(method, params);
|
||||
|
@ -56,6 +56,11 @@ module.exports = {
|
||||
'babel-loader?cacheDirectory=true'
|
||||
]
|
||||
},
|
||||
{
|
||||
test: /\.js$/,
|
||||
include: /node_modules\/ethereumjs-tx/,
|
||||
use: 'babel-loader'
|
||||
},
|
||||
{
|
||||
test: /\.json$/,
|
||||
use: [ 'json-loader' ]
|
||||
|
@ -4,6 +4,7 @@ mode_timeout = 300
|
||||
mode_alarm = 3600
|
||||
auto_update = "none"
|
||||
release_track = "current"
|
||||
public_node = false
|
||||
no_download = false
|
||||
no_consensus = false
|
||||
|
||||
|
@ -88,6 +88,7 @@ usage! {
|
||||
flag_mode_alarm: u64 = 3600u64, or |c: &Config| otry!(c.parity).mode_alarm.clone(),
|
||||
flag_auto_update: String = "critical", or |c: &Config| otry!(c.parity).auto_update.clone(),
|
||||
flag_release_track: String = "current", or |c: &Config| otry!(c.parity).release_track.clone(),
|
||||
flag_public_node: bool = false, or |c: &Config| otry!(c.parity).public_node.clone(),
|
||||
flag_no_download: bool = false, or |c: &Config| otry!(c.parity).no_download.clone(),
|
||||
flag_no_consensus: bool = false, or |c: &Config| otry!(c.parity).no_consensus.clone(),
|
||||
flag_chain: String = "foundation", or |c: &Config| otry!(c.parity).chain.clone(),
|
||||
@ -365,6 +366,7 @@ struct Operating {
|
||||
mode_alarm: Option<u64>,
|
||||
auto_update: Option<String>,
|
||||
release_track: Option<String>,
|
||||
public_node: Option<bool>,
|
||||
no_download: Option<bool>,
|
||||
no_consensus: Option<bool>,
|
||||
chain: Option<String>,
|
||||
@ -623,6 +625,7 @@ mod tests {
|
||||
flag_mode_alarm: 3600u64,
|
||||
flag_auto_update: "none".into(),
|
||||
flag_release_track: "current".into(),
|
||||
flag_public_node: false,
|
||||
flag_no_download: false,
|
||||
flag_no_consensus: false,
|
||||
flag_chain: "xyz".into(),
|
||||
@ -825,6 +828,7 @@ mod tests {
|
||||
mode_alarm: Some(10u64),
|
||||
auto_update: None,
|
||||
release_track: None,
|
||||
public_node: None,
|
||||
no_download: None,
|
||||
no_consensus: None,
|
||||
chain: Some("./chain.json".into()),
|
||||
|
@ -48,6 +48,9 @@ Operating Options:
|
||||
testing - Testing releases (do not use).
|
||||
current - Whatever track this executable was
|
||||
released on (default: {flag_release_track}).
|
||||
--public-node Start Parity as a public web server. Account storage
|
||||
and transaction signing will be delegated to the UI.
|
||||
(default: {flag_public_node}).
|
||||
--no-download Normally new releases will be downloaded ready for
|
||||
updating. This disables it. Not recommended.
|
||||
(default: {flag_no_download}).
|
||||
|
@ -128,6 +128,7 @@ impl Configuration {
|
||||
Some(true) if pruning == Pruning::Specific(Algorithm::Archive) => writeln!(&mut stderr(), "Warning: Warp Sync is disabled because pruning mode is set to archive").expect("Error writing to stderr"),
|
||||
_ => {},
|
||||
};
|
||||
let public_node = self.args.flag_public_node;
|
||||
let warp_sync = !self.args.flag_no_warp && fat_db != Switch::On && tracing != Switch::On && pruning != Pruning::Specific(Algorithm::Archive);
|
||||
let geth_compatibility = self.args.flag_geth;
|
||||
let ui_address = self.ui_port().map(|port| (self.ui_interface(), port));
|
||||
@ -360,6 +361,7 @@ impl Configuration {
|
||||
wal: wal,
|
||||
vm_type: vm_type,
|
||||
warp_sync: warp_sync,
|
||||
public_node: public_node,
|
||||
geth_compatibility: geth_compatibility,
|
||||
ui_address: ui_address,
|
||||
net_settings: self.network_settings(),
|
||||
@ -709,14 +711,26 @@ impl Configuration {
|
||||
}
|
||||
|
||||
fn rpc_apis(&self) -> String {
|
||||
let mut apis = self.args.flag_rpcapi.clone().unwrap_or(self.args.flag_jsonrpc_apis.clone());
|
||||
let mut apis: Vec<&str> = self.args.flag_rpcapi
|
||||
.as_ref()
|
||||
.unwrap_or(&self.args.flag_jsonrpc_apis)
|
||||
.split(",")
|
||||
.collect();
|
||||
|
||||
if self.args.flag_geth {
|
||||
if !apis.is_empty() {
|
||||
apis.push_str(",");
|
||||
apis.push("personal");
|
||||
}
|
||||
apis.push_str("personal");
|
||||
|
||||
if self.args.flag_public_node {
|
||||
apis.retain(|api| {
|
||||
match *api {
|
||||
"eth" | "net" | "parity" | "rpc" | "web3" => true,
|
||||
_ => false
|
||||
}
|
||||
apis
|
||||
});
|
||||
}
|
||||
|
||||
apis.join(",")
|
||||
}
|
||||
|
||||
fn cors(cors: Option<&String>) -> Option<Vec<String>> {
|
||||
@ -1167,6 +1181,7 @@ mod tests {
|
||||
ipc_conf: Default::default(),
|
||||
net_conf: default_network_config(),
|
||||
network_id: None,
|
||||
public_node: false,
|
||||
warp_sync: true,
|
||||
acc_conf: Default::default(),
|
||||
gas_pricer: Default::default(),
|
||||
|
@ -118,7 +118,7 @@ pub struct Dependencies {
|
||||
pub snapshot: Arc<SnapshotService>,
|
||||
pub sync: Arc<SyncProvider>,
|
||||
pub net: Arc<ManageNetwork>,
|
||||
pub secret_store: Arc<AccountProvider>,
|
||||
pub secret_store: Option<Arc<AccountProvider>>,
|
||||
pub miner: Arc<Miner>,
|
||||
pub external_miner: Arc<ExternalMiner>,
|
||||
pub logger: Arc<RotatingLogger>,
|
||||
|
@ -81,6 +81,7 @@ pub struct RunCmd {
|
||||
pub net_conf: NetworkConfiguration,
|
||||
pub network_id: Option<u64>,
|
||||
pub warp_sync: bool,
|
||||
pub public_node: bool,
|
||||
pub acc_conf: AccountsConfig,
|
||||
pub gas_pricer: GasPricerConfig,
|
||||
pub miner_extras: MinerExtras,
|
||||
@ -407,6 +408,10 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
|
||||
// set up dependencies for rpc servers
|
||||
let rpc_stats = Arc::new(informant::RpcStats::default());
|
||||
let signer_path = cmd.signer_conf.signer_path.clone();
|
||||
let secret_store = match cmd.public_node {
|
||||
true => None,
|
||||
false => Some(account_provider.clone())
|
||||
};
|
||||
let deps_for_rpc_apis = Arc::new(rpc_apis::Dependencies {
|
||||
signer_service: Arc::new(rpc_apis::SignerService::new(move || {
|
||||
signer::generate_new_token(signer_path.clone()).map_err(|e| format!("{:?}", e))
|
||||
@ -415,7 +420,7 @@ pub fn execute(cmd: RunCmd, can_restart: bool, logger: Arc<RotatingLogger>) -> R
|
||||
client: client.clone(),
|
||||
sync: sync_provider.clone(),
|
||||
net: manage_network.clone(),
|
||||
secret_store: account_provider.clone(),
|
||||
secret_store: secret_store,
|
||||
miner: miner.clone(),
|
||||
external_miner: external_miner.clone(),
|
||||
logger: logger.clone(),
|
||||
|
27
rpc/src/v1/helpers/accounts.rs
Normal file
27
rpc/src/v1/helpers/accounts.rs
Normal file
@ -0,0 +1,27 @@
|
||||
// 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/>.
|
||||
|
||||
use std::sync::{Arc, Weak};
|
||||
use ethcore::account_provider::AccountProvider;
|
||||
use jsonrpc_core::Error;
|
||||
use v1::helpers::errors;
|
||||
|
||||
pub fn unwrap_provider(provider: &Option<Weak<AccountProvider>>) -> Result<Arc<AccountProvider>, Error> {
|
||||
match *provider {
|
||||
Some(ref weak) => weak.upgrade().ok_or_else(Error::internal_error),
|
||||
None => Err(errors::public_unsupported(None)),
|
||||
}
|
||||
}
|
@ -65,6 +65,14 @@ pub fn light_unimplemented(details: Option<String>) -> Error {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn public_unsupported(details: Option<String>) -> Error {
|
||||
Error {
|
||||
code: ErrorCode::ServerError(codes::UNSUPPORTED_REQUEST),
|
||||
message: "Method disallowed when running parity as a public node.".into(),
|
||||
data: details.map(Value::String),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn request_not_found() -> Error {
|
||||
Error {
|
||||
code: ErrorCode::ServerError(codes::REQUEST_NOT_FOUND),
|
||||
|
@ -17,6 +17,7 @@
|
||||
#[macro_use]
|
||||
pub mod errors;
|
||||
|
||||
pub mod accounts;
|
||||
pub mod block_import;
|
||||
pub mod dispatch;
|
||||
pub mod fake_sign;
|
||||
|
@ -46,6 +46,7 @@ use jsonrpc_macros::Trailing;
|
||||
use v1::helpers::{errors, limit_logs, fake_sign};
|
||||
use v1::helpers::dispatch::{Dispatcher, FullDispatcher, default_gas_price};
|
||||
use v1::helpers::block_import::is_major_importing;
|
||||
use v1::helpers::accounts::unwrap_provider;
|
||||
use v1::traits::Eth;
|
||||
use v1::types::{
|
||||
RichBlock, Block, BlockTransactions, BlockNumber, Bytes, SyncStatus, SyncInfo,
|
||||
@ -97,7 +98,7 @@ pub struct EthClient<C, SN: ?Sized, S: ?Sized, M, EM> where
|
||||
client: Weak<C>,
|
||||
snapshot: Weak<SN>,
|
||||
sync: Weak<S>,
|
||||
accounts: Weak<AccountProvider>,
|
||||
accounts: Option<Weak<AccountProvider>>,
|
||||
miner: Weak<M>,
|
||||
external_miner: Arc<EM>,
|
||||
seed_compute: Mutex<SeedHashCompute>,
|
||||
@ -116,7 +117,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where
|
||||
client: &Arc<C>,
|
||||
snapshot: &Arc<SN>,
|
||||
sync: &Arc<S>,
|
||||
accounts: &Arc<AccountProvider>,
|
||||
accounts: &Option<Arc<AccountProvider>>,
|
||||
miner: &Arc<M>,
|
||||
em: &Arc<EM>,
|
||||
options: EthClientOptions
|
||||
@ -126,13 +127,19 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where
|
||||
snapshot: Arc::downgrade(snapshot),
|
||||
sync: Arc::downgrade(sync),
|
||||
miner: Arc::downgrade(miner),
|
||||
accounts: Arc::downgrade(accounts),
|
||||
accounts: accounts.as_ref().map(Arc::downgrade),
|
||||
external_miner: em.clone(),
|
||||
seed_compute: Mutex::new(SeedHashCompute::new()),
|
||||
options: options,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to get the `Arc<AccountProvider>`, errors if provider was not
|
||||
/// set, or if upgrading the weak reference failed.
|
||||
fn account_provider(&self) -> Result<Arc<AccountProvider>, Error> {
|
||||
unwrap_provider(&self.accounts)
|
||||
}
|
||||
|
||||
fn block(&self, id: BlockId, include_txs: bool) -> Result<Option<RichBlock>, Error> {
|
||||
let client = take_weak!(self.client);
|
||||
match (client.block(id.clone()), client.block_total_difficulty(id)) {
|
||||
@ -223,7 +230,7 @@ impl<C, SN: ?Sized, S: ?Sized, M, EM> EthClient<C, SN, S, M, EM> where
|
||||
}
|
||||
|
||||
fn dapp_accounts(&self, dapp: DappId) -> Result<Vec<H160>, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
store
|
||||
.note_dapp_used(dapp.clone())
|
||||
.and_then(|_| store.dapp_addresses(dapp))
|
||||
|
@ -40,7 +40,7 @@ use v1::types::{
|
||||
TransactionStats, LocalTransactionStatus,
|
||||
BlockNumber, ConsensusCapability, VersionInfo,
|
||||
OperationsInfo, DappId, ChainStatus,
|
||||
AccountInfo, HwAccountInfo
|
||||
AccountInfo, HwAccountInfo,
|
||||
};
|
||||
|
||||
/// Parity implementation for light client.
|
||||
|
@ -37,6 +37,7 @@ use updater::{Service as UpdateService};
|
||||
use jsonrpc_core::Error;
|
||||
use jsonrpc_macros::Trailing;
|
||||
use v1::helpers::{errors, SigningQueue, SignerService, NetworkSettings};
|
||||
use v1::helpers::accounts::unwrap_provider;
|
||||
use v1::helpers::dispatch::DEFAULT_MAC;
|
||||
use v1::metadata::Metadata;
|
||||
use v1::traits::Parity;
|
||||
@ -46,7 +47,7 @@ use v1::types::{
|
||||
TransactionStats, LocalTransactionStatus,
|
||||
BlockNumber, ConsensusCapability, VersionInfo,
|
||||
OperationsInfo, DappId, ChainStatus,
|
||||
AccountInfo, HwAccountInfo
|
||||
AccountInfo, HwAccountInfo,
|
||||
};
|
||||
|
||||
/// Parity implementation.
|
||||
@ -61,7 +62,7 @@ pub struct ParityClient<C, M, S: ?Sized, U> where
|
||||
sync: Weak<S>,
|
||||
updater: Weak<U>,
|
||||
net: Weak<ManageNetwork>,
|
||||
accounts: Weak<AccountProvider>,
|
||||
accounts: Option<Weak<AccountProvider>>,
|
||||
logger: Arc<RotatingLogger>,
|
||||
settings: Arc<NetworkSettings>,
|
||||
signer: Option<Arc<SignerService>>,
|
||||
@ -82,7 +83,7 @@ impl<C, M, S: ?Sized, U> ParityClient<C, M, S, U> where
|
||||
sync: &Arc<S>,
|
||||
updater: &Arc<U>,
|
||||
net: &Arc<ManageNetwork>,
|
||||
store: &Arc<AccountProvider>,
|
||||
store: &Option<Arc<AccountProvider>>,
|
||||
logger: Arc<RotatingLogger>,
|
||||
settings: Arc<NetworkSettings>,
|
||||
signer: Option<Arc<SignerService>>,
|
||||
@ -95,7 +96,7 @@ impl<C, M, S: ?Sized, U> ParityClient<C, M, S, U> where
|
||||
sync: Arc::downgrade(sync),
|
||||
updater: Arc::downgrade(updater),
|
||||
net: Arc::downgrade(net),
|
||||
accounts: Arc::downgrade(store),
|
||||
accounts: store.as_ref().map(Arc::downgrade),
|
||||
logger: logger,
|
||||
settings: settings,
|
||||
signer: signer,
|
||||
@ -103,6 +104,12 @@ impl<C, M, S: ?Sized, U> ParityClient<C, M, S, U> where
|
||||
dapps_port: dapps_port,
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to get the `Arc<AccountProvider>`, errors if provider was not
|
||||
/// set, or if upgrading the weak reference failed.
|
||||
fn account_provider(&self) -> Result<Arc<AccountProvider>, Error> {
|
||||
unwrap_provider(&self.accounts)
|
||||
}
|
||||
}
|
||||
|
||||
impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
|
||||
@ -116,7 +123,7 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
|
||||
fn accounts_info(&self, dapp: Trailing<DappId>) -> Result<BTreeMap<H160, AccountInfo>, Error> {
|
||||
let dapp = dapp.0;
|
||||
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
let dapp_accounts = store
|
||||
.note_dapp_used(dapp.clone().into())
|
||||
.and_then(|_| store.dapp_addresses(dapp.into()))
|
||||
@ -136,7 +143,7 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
|
||||
}
|
||||
|
||||
fn hardware_accounts_info(&self) -> Result<BTreeMap<H160, HwAccountInfo>, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
let info = store.hardware_accounts_info().map_err(|e| errors::account("Could not fetch account info.", e))?;
|
||||
Ok(info
|
||||
.into_iter()
|
||||
@ -148,7 +155,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> {
|
||||
let dapp_id = meta.dapp_id();
|
||||
future::ok(
|
||||
take_weakf!(self.accounts)
|
||||
try_bf!(self.account_provider())
|
||||
.dapp_default_address(dapp_id.into())
|
||||
.map(Into::into)
|
||||
.ok()
|
||||
@ -376,9 +383,13 @@ impl<C, M, S: ?Sized, U> Parity for ParityClient<C, M, S, U> where
|
||||
fn node_kind(&self) -> Result<::v1::types::NodeKind, Error> {
|
||||
use ::v1::types::{NodeKind, Availability, Capability};
|
||||
|
||||
// TODO [maciej]: public availability flag.
|
||||
let availability = match self.accounts {
|
||||
Some(_) => Availability::Personal,
|
||||
None => Availability::Public
|
||||
};
|
||||
|
||||
Ok(NodeKind {
|
||||
availability: Availability::Personal,
|
||||
availability: availability,
|
||||
capability: Capability::Full,
|
||||
})
|
||||
}
|
||||
|
@ -25,26 +25,33 @@ use ethcore::account_provider::AccountProvider;
|
||||
|
||||
use jsonrpc_core::Error;
|
||||
use v1::helpers::errors;
|
||||
use v1::helpers::accounts::unwrap_provider;
|
||||
use v1::traits::ParityAccounts;
|
||||
use v1::types::{H160 as RpcH160, H256 as RpcH256, DappId, Derive, DeriveHierarchical, DeriveHash};
|
||||
|
||||
/// Account management (personal) rpc implementation.
|
||||
pub struct ParityAccountsClient {
|
||||
accounts: Weak<AccountProvider>,
|
||||
accounts: Option<Weak<AccountProvider>>,
|
||||
}
|
||||
|
||||
impl ParityAccountsClient {
|
||||
/// Creates new PersonalClient
|
||||
pub fn new(store: &Arc<AccountProvider>) -> Self {
|
||||
pub fn new(store: &Option<Arc<AccountProvider>>) -> Self {
|
||||
ParityAccountsClient {
|
||||
accounts: Arc::downgrade(store),
|
||||
accounts: store.as_ref().map(Arc::downgrade),
|
||||
}
|
||||
}
|
||||
|
||||
/// Attempt to get the `Arc<AccountProvider>`, errors if provider was not
|
||||
/// set, or if upgrading the weak reference failed.
|
||||
fn account_provider(&self) -> Result<Arc<AccountProvider>, Error> {
|
||||
unwrap_provider(&self.accounts)
|
||||
}
|
||||
}
|
||||
|
||||
impl ParityAccounts for ParityAccountsClient {
|
||||
fn all_accounts_info(&self) -> Result<BTreeMap<RpcH160, BTreeMap<String, String>>, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
let info = store.accounts_info().map_err(|e| errors::account("Could not fetch account info.", e))?;
|
||||
let other = store.addresses_info();
|
||||
|
||||
@ -66,7 +73,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn new_account_from_phrase(&self, phrase: String, pass: String) -> Result<RpcH160, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
|
||||
let brain = Brain::new(phrase).generate().unwrap();
|
||||
store.insert_account(brain.secret().clone(), &pass)
|
||||
@ -75,7 +82,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn new_account_from_wallet(&self, json: String, pass: String) -> Result<RpcH160, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
|
||||
store.import_presale(json.as_bytes(), &pass)
|
||||
.or_else(|_| store.import_wallet(json.as_bytes(), &pass))
|
||||
@ -84,7 +91,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn new_account_from_secret(&self, secret: RpcH256, pass: String) -> Result<RpcH160, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
|
||||
let secret = Secret::from_slice(&secret.0)
|
||||
.map_err(|e| errors::account("Could not create account.", e))?;
|
||||
@ -96,14 +103,14 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
fn test_password(&self, account: RpcH160, password: String) -> Result<bool, Error> {
|
||||
let account: Address = account.into();
|
||||
|
||||
take_weak!(self.accounts)
|
||||
self.account_provider()?
|
||||
.test_password(&account, &password)
|
||||
.map_err(|e| errors::account("Could not fetch account info.", e))
|
||||
}
|
||||
|
||||
fn change_password(&self, account: RpcH160, password: String, new_password: String) -> Result<bool, Error> {
|
||||
let account: Address = account.into();
|
||||
take_weak!(self.accounts)
|
||||
self.account_provider()?
|
||||
.change_password(&account, password, new_password)
|
||||
.map(|_| true)
|
||||
.map_err(|e| errors::account("Could not fetch account info.", e))
|
||||
@ -111,14 +118,14 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
|
||||
fn kill_account(&self, account: RpcH160, password: String) -> Result<bool, Error> {
|
||||
let account: Address = account.into();
|
||||
take_weak!(self.accounts)
|
||||
self.account_provider()?
|
||||
.kill_account(&account, &password)
|
||||
.map(|_| true)
|
||||
.map_err(|e| errors::account("Could not delete account.", e))
|
||||
}
|
||||
|
||||
fn remove_address(&self, addr: RpcH160) -> Result<bool, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
let addr: Address = addr.into();
|
||||
|
||||
store.remove_address(addr);
|
||||
@ -126,7 +133,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn set_account_name(&self, addr: RpcH160, name: String) -> Result<bool, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
let addr: Address = addr.into();
|
||||
|
||||
store.set_account_name(addr.clone(), name.clone())
|
||||
@ -135,7 +142,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn set_account_meta(&self, addr: RpcH160, meta: String) -> Result<bool, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
let addr: Address = addr.into();
|
||||
|
||||
store.set_account_meta(addr.clone(), meta.clone())
|
||||
@ -144,7 +151,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn set_dapp_addresses(&self, dapp: DappId, addresses: Option<Vec<RpcH160>>) -> Result<bool, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
|
||||
store.set_dapp_addresses(dapp.into(), addresses.map(into_vec))
|
||||
.map_err(|e| errors::account("Couldn't set dapp addresses.", e))
|
||||
@ -152,7 +159,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn dapp_addresses(&self, dapp: DappId) -> Result<Vec<RpcH160>, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
|
||||
store.dapp_addresses(dapp.into())
|
||||
.map_err(|e| errors::account("Couldn't get dapp addresses.", e))
|
||||
@ -160,7 +167,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn set_dapp_default_address(&self, dapp: DappId, address: RpcH160) -> Result<bool, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
|
||||
store.set_dapp_default_address(dapp.into(), address.into())
|
||||
.map_err(|e| errors::account("Couldn't set dapp default address.", e))
|
||||
@ -168,7 +175,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn dapp_default_address(&self, dapp: DappId) -> Result<RpcH160, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
|
||||
store.dapp_default_address(dapp.into())
|
||||
.map_err(|e| errors::account("Couldn't get dapp default address.", e))
|
||||
@ -176,7 +183,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn set_new_dapps_addresses(&self, addresses: Option<Vec<RpcH160>>) -> Result<bool, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
|
||||
store
|
||||
.set_new_dapps_addresses(addresses.map(into_vec))
|
||||
@ -185,7 +192,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn new_dapps_addresses(&self) -> Result<Option<Vec<RpcH160>>, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
|
||||
store.new_dapps_addresses()
|
||||
.map_err(|e| errors::account("Couldn't get dapps addresses.", e))
|
||||
@ -193,7 +200,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn set_new_dapps_default_address(&self, address: RpcH160) -> Result<bool, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
|
||||
store.set_new_dapps_default_address(address.into())
|
||||
.map_err(|e| errors::account("Couldn't set new dapps default address.", e))
|
||||
@ -201,7 +208,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn new_dapps_default_address(&self) -> Result<RpcH160, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
|
||||
store.new_dapps_default_address()
|
||||
.map_err(|e| errors::account("Couldn't get new dapps default address.", e))
|
||||
@ -209,7 +216,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn recent_dapps(&self) -> Result<BTreeMap<DappId, u64>, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
|
||||
store.recent_dapps()
|
||||
.map_err(|e| errors::account("Couldn't get recent dapps.", e))
|
||||
@ -217,7 +224,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn import_geth_accounts(&self, addresses: Vec<RpcH160>) -> Result<Vec<RpcH160>, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
|
||||
store
|
||||
.import_geth_accounts(into_vec(addresses), false)
|
||||
@ -226,66 +233,66 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
}
|
||||
|
||||
fn geth_accounts(&self) -> Result<Vec<RpcH160>, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
|
||||
Ok(into_vec(store.list_geth_accounts(false)))
|
||||
}
|
||||
|
||||
fn create_vault(&self, name: String, password: String) -> Result<bool, Error> {
|
||||
take_weak!(self.accounts)
|
||||
self.account_provider()?
|
||||
.create_vault(&name, &password)
|
||||
.map_err(|e| errors::account("Could not create vault.", e))
|
||||
.map(|_| true)
|
||||
}
|
||||
|
||||
fn open_vault(&self, name: String, password: String) -> Result<bool, Error> {
|
||||
take_weak!(self.accounts)
|
||||
self.account_provider()?
|
||||
.open_vault(&name, &password)
|
||||
.map_err(|e| errors::account("Could not open vault.", e))
|
||||
.map(|_| true)
|
||||
}
|
||||
|
||||
fn close_vault(&self, name: String) -> Result<bool, Error> {
|
||||
take_weak!(self.accounts)
|
||||
self.account_provider()?
|
||||
.close_vault(&name)
|
||||
.map_err(|e| errors::account("Could not close vault.", e))
|
||||
.map(|_| true)
|
||||
}
|
||||
|
||||
fn list_vaults(&self) -> Result<Vec<String>, Error> {
|
||||
take_weak!(self.accounts)
|
||||
self.account_provider()?
|
||||
.list_vaults()
|
||||
.map_err(|e| errors::account("Could not list vaults.", e))
|
||||
}
|
||||
|
||||
fn list_opened_vaults(&self) -> Result<Vec<String>, Error> {
|
||||
take_weak!(self.accounts)
|
||||
self.account_provider()?
|
||||
.list_opened_vaults()
|
||||
.map_err(|e| errors::account("Could not list vaults.", e))
|
||||
}
|
||||
|
||||
fn change_vault_password(&self, name: String, new_password: String) -> Result<bool, Error> {
|
||||
take_weak!(self.accounts)
|
||||
self.account_provider()?
|
||||
.change_vault_password(&name, &new_password)
|
||||
.map_err(|e| errors::account("Could not change vault password.", e))
|
||||
.map(|_| true)
|
||||
}
|
||||
|
||||
fn change_vault(&self, address: RpcH160, new_vault: String) -> Result<bool, Error> {
|
||||
take_weak!(self.accounts)
|
||||
self.account_provider()?
|
||||
.change_vault(address.into(), &new_vault)
|
||||
.map_err(|e| errors::account("Could not change vault.", e))
|
||||
.map(|_| true)
|
||||
}
|
||||
|
||||
fn get_vault_meta(&self, name: String) -> Result<String, Error> {
|
||||
take_weak!(self.accounts)
|
||||
self.account_provider()?
|
||||
.get_vault_meta(&name)
|
||||
.map_err(|e| errors::account("Could not get vault metadata.", e))
|
||||
}
|
||||
|
||||
fn set_vault_meta(&self, name: String, meta: String) -> Result<bool, Error> {
|
||||
take_weak!(self.accounts)
|
||||
self.account_provider()?
|
||||
.set_vault_meta(&name, &meta)
|
||||
.map_err(|e| errors::account("Could not update vault metadata.", e))
|
||||
.map(|_| true)
|
||||
@ -293,7 +300,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
|
||||
fn derive_key_index(&self, addr: RpcH160, password: String, derivation: DeriveHierarchical, save_as_account: bool) -> Result<RpcH160, Error> {
|
||||
let addr: Address = addr.into();
|
||||
take_weak!(self.accounts)
|
||||
self.account_provider()?
|
||||
.derive_account(
|
||||
&addr,
|
||||
Some(password),
|
||||
@ -306,7 +313,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
|
||||
fn derive_key_hash(&self, addr: RpcH160, password: String, derivation: DeriveHash, save_as_account: bool) -> Result<RpcH160, Error> {
|
||||
let addr: Address = addr.into();
|
||||
take_weak!(self.accounts)
|
||||
self.account_provider()?
|
||||
.derive_account(
|
||||
&addr,
|
||||
Some(password),
|
||||
@ -319,7 +326,7 @@ impl ParityAccounts for ParityAccountsClient {
|
||||
|
||||
fn export_account(&self, addr: RpcH160, password: String) -> Result<KeyFile, Error> {
|
||||
let addr = addr.into();
|
||||
take_weak!(self.accounts)
|
||||
self.account_provider()?
|
||||
.export_account(
|
||||
&addr,
|
||||
password,
|
||||
|
@ -26,39 +26,44 @@ use futures::{future, Future, BoxFuture};
|
||||
use jsonrpc_core::Error;
|
||||
use v1::helpers::errors;
|
||||
use v1::helpers::dispatch::{Dispatcher, SignWith};
|
||||
use v1::helpers::accounts::unwrap_provider;
|
||||
use v1::traits::Personal;
|
||||
use v1::types::{H160 as RpcH160, H256 as RpcH256, U128 as RpcU128, TransactionRequest};
|
||||
use v1::metadata::Metadata;
|
||||
|
||||
/// Account management (personal) rpc implementation.
|
||||
pub struct PersonalClient<D: Dispatcher> {
|
||||
accounts: Weak<AccountProvider>,
|
||||
accounts: Option<Weak<AccountProvider>>,
|
||||
dispatcher: D,
|
||||
allow_perm_unlock: bool,
|
||||
}
|
||||
|
||||
impl<D: Dispatcher> PersonalClient<D> {
|
||||
/// Creates new PersonalClient
|
||||
pub fn new(store: &Arc<AccountProvider>, dispatcher: D, allow_perm_unlock: bool) -> Self {
|
||||
pub fn new(store: &Option<Arc<AccountProvider>>, dispatcher: D, allow_perm_unlock: bool) -> Self {
|
||||
PersonalClient {
|
||||
accounts: Arc::downgrade(store),
|
||||
accounts: store.as_ref().map(Arc::downgrade),
|
||||
dispatcher: dispatcher,
|
||||
allow_perm_unlock: allow_perm_unlock,
|
||||
}
|
||||
}
|
||||
|
||||
fn account_provider(&self) -> Result<Arc<AccountProvider>, Error> {
|
||||
unwrap_provider(&self.accounts)
|
||||
}
|
||||
}
|
||||
|
||||
impl<D: Dispatcher + 'static> Personal for PersonalClient<D> {
|
||||
type Metadata = Metadata;
|
||||
|
||||
fn accounts(&self) -> Result<Vec<RpcH160>, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
let accounts = store.accounts().map_err(|e| errors::account("Could not fetch accounts.", e))?;
|
||||
Ok(accounts.into_iter().map(Into::into).collect::<Vec<RpcH160>>())
|
||||
}
|
||||
|
||||
fn new_account(&self, pass: String) -> Result<RpcH160, Error> {
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
|
||||
store.new_account(&pass)
|
||||
.map(Into::into)
|
||||
@ -67,7 +72,7 @@ impl<D: Dispatcher + 'static> Personal for PersonalClient<D> {
|
||||
|
||||
fn unlock_account(&self, account: RpcH160, account_pass: String, duration: Option<RpcU128>) -> Result<bool, Error> {
|
||||
let account: Address = account.into();
|
||||
let store = take_weak!(self.accounts);
|
||||
let store = self.account_provider()?;
|
||||
let duration = match duration {
|
||||
None => None,
|
||||
Some(duration) => {
|
||||
@ -96,7 +101,7 @@ impl<D: Dispatcher + 'static> Personal for PersonalClient<D> {
|
||||
|
||||
fn send_transaction(&self, meta: Metadata, request: TransactionRequest, password: String) -> BoxFuture<RpcH256, Error> {
|
||||
let dispatcher = self.dispatcher.clone();
|
||||
let accounts = take_weakf!(self.accounts);
|
||||
let accounts = try_bf!(self.account_provider());
|
||||
|
||||
let default = match request.from.as_ref() {
|
||||
Some(account) => Ok(account.clone().into()),
|
||||
|
@ -26,13 +26,14 @@ use futures::{future, BoxFuture, Future, IntoFuture};
|
||||
use jsonrpc_core::Error;
|
||||
use v1::helpers::{errors, SignerService, SigningQueue, ConfirmationPayload};
|
||||
use v1::helpers::dispatch::{self, Dispatcher, WithToken};
|
||||
use v1::helpers::accounts::unwrap_provider;
|
||||
use v1::traits::Signer;
|
||||
use v1::types::{TransactionModification, ConfirmationRequest, ConfirmationResponse, ConfirmationResponseWithToken, U256, Bytes};
|
||||
|
||||
/// Transactions confirmation (personal) rpc implementation.
|
||||
pub struct SignerClient<D: Dispatcher> {
|
||||
signer: Weak<SignerService>,
|
||||
accounts: Weak<AccountProvider>,
|
||||
accounts: Option<Weak<AccountProvider>>,
|
||||
dispatcher: D
|
||||
}
|
||||
|
||||
@ -40,17 +41,21 @@ impl<D: Dispatcher + 'static> SignerClient<D> {
|
||||
|
||||
/// Create new instance of signer client.
|
||||
pub fn new(
|
||||
store: &Arc<AccountProvider>,
|
||||
store: &Option<Arc<AccountProvider>>,
|
||||
dispatcher: D,
|
||||
signer: &Arc<SignerService>,
|
||||
) -> Self {
|
||||
SignerClient {
|
||||
signer: Arc::downgrade(signer),
|
||||
accounts: Arc::downgrade(store),
|
||||
accounts: store.as_ref().map(Arc::downgrade),
|
||||
dispatcher: dispatcher,
|
||||
}
|
||||
}
|
||||
|
||||
fn account_provider(&self) -> Result<Arc<AccountProvider>, Error> {
|
||||
unwrap_provider(&self.accounts)
|
||||
}
|
||||
|
||||
fn confirm_internal<F, T>(&self, id: U256, modification: TransactionModification, f: F) -> BoxFuture<WithToken<ConfirmationResponse>, Error> where
|
||||
F: FnOnce(D, Arc<AccountProvider>, ConfirmationPayload) -> T,
|
||||
T: IntoFuture<Item=WithToken<ConfirmationResponse>, Error=Error>,
|
||||
@ -60,7 +65,7 @@ impl<D: Dispatcher + 'static> SignerClient<D> {
|
||||
let dispatcher = self.dispatcher.clone();
|
||||
|
||||
let setup = || {
|
||||
Ok((take_weak!(self.accounts), take_weak!(self.signer)))
|
||||
Ok((self.account_provider()?, take_weak!(self.signer)))
|
||||
};
|
||||
|
||||
let (accounts, signer) = match setup() {
|
||||
|
@ -30,6 +30,7 @@ use v1::helpers::{
|
||||
SIGNING_QUEUE_LIMIT, SigningQueue, ConfirmationPromise, ConfirmationResult, SignerService
|
||||
};
|
||||
use v1::helpers::dispatch::{self, Dispatcher};
|
||||
use v1::helpers::accounts::unwrap_provider;
|
||||
use v1::metadata::Metadata;
|
||||
use v1::traits::{EthSigning, ParitySigning};
|
||||
use v1::types::{
|
||||
@ -55,7 +56,7 @@ enum DispatchResult {
|
||||
/// Implementation of functions that require signing when no trusted signer is used.
|
||||
pub struct SigningQueueClient<D> {
|
||||
signer: Weak<SignerService>,
|
||||
accounts: Weak<AccountProvider>,
|
||||
accounts: Option<Weak<AccountProvider>>,
|
||||
dispatcher: D,
|
||||
pending: Arc<Mutex<TransientHashMap<U256, ConfirmationPromise>>>,
|
||||
}
|
||||
@ -91,17 +92,21 @@ fn collect_garbage(map: &mut TransientHashMap<U256, ConfirmationPromise>) {
|
||||
|
||||
impl<D: Dispatcher + 'static> SigningQueueClient<D> {
|
||||
/// Creates a new signing queue client given shared signing queue.
|
||||
pub fn new(signer: &Arc<SignerService>, dispatcher: D, accounts: &Arc<AccountProvider>) -> Self {
|
||||
pub fn new(signer: &Arc<SignerService>, dispatcher: D, accounts: &Option<Arc<AccountProvider>>) -> Self {
|
||||
SigningQueueClient {
|
||||
signer: Arc::downgrade(signer),
|
||||
accounts: Arc::downgrade(accounts),
|
||||
accounts: accounts.as_ref().map(Arc::downgrade),
|
||||
dispatcher: dispatcher,
|
||||
pending: Arc::new(Mutex::new(TransientHashMap::new(MAX_PENDING_DURATION_SEC))),
|
||||
}
|
||||
}
|
||||
|
||||
fn account_provider(&self) -> Result<Arc<AccountProvider>, Error> {
|
||||
unwrap_provider(&self.accounts)
|
||||
}
|
||||
|
||||
fn dispatch(&self, payload: RpcConfirmationPayload, default_account: DefaultAccount, origin: Origin) -> BoxFuture<DispatchResult, Error> {
|
||||
let accounts = take_weakf!(self.accounts);
|
||||
let accounts = try_bf!(self.account_provider());
|
||||
let default_account = match default_account {
|
||||
DefaultAccount::Provided(acc) => acc,
|
||||
DefaultAccount::ForDapp(dapp) => accounts.dapp_default_address(dapp).ok().unwrap_or_default(),
|
||||
|
@ -24,6 +24,7 @@ use futures::{future, BoxFuture, Future};
|
||||
use jsonrpc_core::Error;
|
||||
use v1::helpers::{errors, DefaultAccount};
|
||||
use v1::helpers::dispatch::{self, Dispatcher};
|
||||
use v1::helpers::accounts::unwrap_provider;
|
||||
use v1::metadata::Metadata;
|
||||
use v1::traits::{EthSigning, ParitySigning};
|
||||
use v1::types::{
|
||||
@ -38,21 +39,25 @@ use v1::types::{
|
||||
|
||||
/// Implementation of functions that require signing when no trusted signer is used.
|
||||
pub struct SigningUnsafeClient<D> {
|
||||
accounts: Weak<AccountProvider>,
|
||||
accounts: Option<Weak<AccountProvider>>,
|
||||
dispatcher: D,
|
||||
}
|
||||
|
||||
impl<D: Dispatcher + 'static> SigningUnsafeClient<D> {
|
||||
/// Creates new SigningUnsafeClient.
|
||||
pub fn new(accounts: &Arc<AccountProvider>, dispatcher: D) -> Self {
|
||||
pub fn new(accounts: &Option<Arc<AccountProvider>>, dispatcher: D) -> Self {
|
||||
SigningUnsafeClient {
|
||||
accounts: Arc::downgrade(accounts),
|
||||
accounts: accounts.as_ref().map(Arc::downgrade),
|
||||
dispatcher: dispatcher,
|
||||
}
|
||||
}
|
||||
|
||||
fn account_provider(&self) -> Result<Arc<AccountProvider>, Error> {
|
||||
unwrap_provider(&self.accounts)
|
||||
}
|
||||
|
||||
fn handle(&self, payload: RpcConfirmationPayload, account: DefaultAccount) -> BoxFuture<RpcConfirmationResponse, Error> {
|
||||
let accounts = take_weakf!(self.accounts);
|
||||
let accounts = try_bf!(self.account_provider());
|
||||
let default = match account {
|
||||
DefaultAccount::Provided(acc) => acc,
|
||||
DefaultAccount::ForDapp(dapp) => accounts.dapp_default_address(dapp).ok().unwrap_or_default(),
|
||||
|
@ -84,15 +84,16 @@ impl EthTester {
|
||||
let client = blockchain_client();
|
||||
let sync = sync_provider();
|
||||
let ap = accounts_provider();
|
||||
let opt_ap = Some(ap.clone());
|
||||
let miner = miner_service();
|
||||
let snapshot = snapshot_service();
|
||||
let hashrates = Arc::new(Mutex::new(HashMap::new()));
|
||||
let external_miner = Arc::new(ExternalMiner::new(hashrates.clone()));
|
||||
let eth = EthClient::new(&client, &snapshot, &sync, &ap, &miner, &external_miner, options).to_delegate();
|
||||
let eth = EthClient::new(&client, &snapshot, &sync, &opt_ap, &miner, &external_miner, options).to_delegate();
|
||||
let filter = EthFilterClient::new(&client, &miner).to_delegate();
|
||||
|
||||
let dispatcher = FullDispatcher::new(Arc::downgrade(&client), Arc::downgrade(&miner));
|
||||
let sign = SigningUnsafeClient::new(&ap, dispatcher).to_delegate();
|
||||
let sign = SigningUnsafeClient::new(&opt_ap, dispatcher).to_delegate();
|
||||
let mut io: IoHandler<Metadata> = IoHandler::default();
|
||||
io.extend_with(eth);
|
||||
io.extend_with(sign);
|
||||
|
@ -72,13 +72,15 @@ impl Dependencies {
|
||||
}
|
||||
|
||||
pub fn client(&self, signer: Option<Arc<SignerService>>) -> TestParityClient {
|
||||
let opt_accounts = Some(self.accounts.clone());
|
||||
|
||||
ParityClient::new(
|
||||
&self.client,
|
||||
&self.miner,
|
||||
&self.sync,
|
||||
&self.updater,
|
||||
&self.network,
|
||||
&self.accounts,
|
||||
&opt_accounts,
|
||||
self.logger.clone(),
|
||||
self.settings.clone(),
|
||||
signer,
|
||||
|
@ -40,7 +40,8 @@ fn accounts_provider_with_vaults_support(temp_path: &str) -> Arc<AccountProvider
|
||||
}
|
||||
|
||||
fn setup_with_accounts_provider(accounts_provider: Arc<AccountProvider>) -> ParityAccountsTester {
|
||||
let parity_accounts = ParityAccountsClient::new(&accounts_provider);
|
||||
let opt_ap = Some(accounts_provider.clone());
|
||||
let parity_accounts = ParityAccountsClient::new(&opt_ap);
|
||||
let mut io = IoHandler::default();
|
||||
io.extend_with(parity_accounts.to_delegate());
|
||||
|
||||
|
@ -51,11 +51,12 @@ fn miner_service() -> Arc<TestMinerService> {
|
||||
|
||||
fn setup() -> PersonalTester {
|
||||
let accounts = accounts_provider();
|
||||
let opt_accounts = Some(accounts.clone());
|
||||
let client = blockchain_client();
|
||||
let miner = miner_service();
|
||||
|
||||
let dispatcher = FullDispatcher::new(Arc::downgrade(&client), Arc::downgrade(&miner));
|
||||
let personal = PersonalClient::new(&accounts, dispatcher, false);
|
||||
let personal = PersonalClient::new(&opt_accounts, dispatcher, false);
|
||||
|
||||
let mut io = IoHandler::default();
|
||||
io.extend_with(personal.to_delegate());
|
||||
|
@ -57,12 +57,13 @@ fn miner_service() -> Arc<TestMinerService> {
|
||||
fn signer_tester() -> SignerTester {
|
||||
let signer = Arc::new(SignerService::new_test(None));
|
||||
let accounts = accounts_provider();
|
||||
let opt_accounts = Some(accounts.clone());
|
||||
let client = blockchain_client();
|
||||
let miner = miner_service();
|
||||
|
||||
let dispatcher = FullDispatcher::new(Arc::downgrade(&client), Arc::downgrade(&miner));
|
||||
let mut io = IoHandler::default();
|
||||
io.extend_with(SignerClient::new(&accounts, dispatcher, &signer).to_delegate());
|
||||
io.extend_with(SignerClient::new(&opt_accounts, dispatcher, &signer).to_delegate());
|
||||
|
||||
SignerTester {
|
||||
signer: signer,
|
||||
|
@ -51,13 +51,14 @@ impl Default for SigningTester {
|
||||
let client = Arc::new(TestBlockChainClient::default());
|
||||
let miner = Arc::new(TestMinerService::default());
|
||||
let accounts = Arc::new(AccountProvider::transient_provider());
|
||||
let opt_accounts = Some(accounts.clone());
|
||||
let mut io = IoHandler::default();
|
||||
|
||||
let dispatcher = FullDispatcher::new(Arc::downgrade(&client), Arc::downgrade(&miner));
|
||||
|
||||
let rpc = SigningQueueClient::new(&signer, dispatcher.clone(), &accounts);
|
||||
let rpc = SigningQueueClient::new(&signer, dispatcher.clone(), &opt_accounts);
|
||||
io.extend_with(EthSigning::to_delegate(rpc));
|
||||
let rpc = SigningQueueClient::new(&signer, dispatcher, &accounts);
|
||||
let rpc = SigningQueueClient::new(&signer, dispatcher, &opt_accounts);
|
||||
io.extend_with(ParitySigning::to_delegate(rpc));
|
||||
|
||||
SigningTester {
|
||||
|
Loading…
Reference in New Issue
Block a user