Squashed: Public Node

This commit is contained in:
maciejhirsz
2017-03-29 17:07:58 +02:00
parent 858c974440
commit 9bd3f10f41
45 changed files with 9060 additions and 89 deletions

View File

@@ -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 () {

View 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
};
}
}

View 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
};
}
}

View 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;

File diff suppressed because it is too large Load Diff

View 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(' ');
}

View 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');
});
});
});
});

View 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
View 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';

View 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();
});
}
}

View 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();

View 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);
});
});

View File

@@ -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}`;

View File

@@ -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)

View File

@@ -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';

View File

@@ -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;

View 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);
}
}

View 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);
});
});
});

View File

@@ -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);