Initial new UI source code import (#2607)
* address -> name mappings * expanding, loading all coin details * send use only actual BasicCoin tokens registered (any reg) * sending token & accounts * form styling updates * send form layout in place * coin send working as expected * api subscriptions on multiple addresses * bring in events * simplify * basic events display in-place, functionally complete * basic functionality in-place * fix horrible event address issue * rwork display of events slightly * test TLA availability * table for owner -> tokens * fix signature lookup address * fix signature lookup address * basic overview styling * txhash links * page layout adjustments * background import * adjust colors * no global registration, simplify color selection * updated styling * connection dialog for "busy connecting" * initial token connection - WIP * init token updates take place * basic test for manual token * rework connection display * allow updates of the secure token * first stab at making the build build * update runner tags * fix linting issues * skip tests requiring network (should be e2e, TODO) * re-enable javascript tag/runner * release push does the trick * push to any branch, CI name * javscript-test runner as well * swap dependencies build requires test * revert stages swap * retrieve images associated with tokens * remove js build deps order * null image when hash = 0x0 * 6x64 images (hashes for registries) * don't pass tokens as prop to IdentityIcon * check images against content hash pictures * cleanup signer after connection changes * fix naming typo * display unknownImages for balances (not available as content hash) * unknownImage for transfer dialog * basic githubhint layout * single input for commit/filename * ethcore_hashContent call * lookup hash * registration in place * fixes * events is using a proper table * pass value through as-is * stop wrongly using main app IdentityIcon * NEVER export class instance functions * alignment back to normal * typo in definition * set & get images working (mostly) * show content retrieval info * set exitcode via || * use javascript:latest images * disable npm progress bar * rename phase I * rename phase II * only send build output to GitHub on major branches * also run the build step as part of the test (until comprehensive) * ci-specific build (no webpack progress) * allow for account creation via recovery phrase * display account uuid (where available), closes #2546 * connection dialog now shows up in dapps as well, closes #2538 * token images show up as expected * IdentityName component added and deployed * fix padding tests * adjust tests to map to stricter 0x-prefixed hex * render names via common component for the address -> name * split lint into seperate script (early exit) * test phases changed to lint, test & pack * pack part of test phase * remove files marked for deletion (cleanup) * Signer cleanups, start moving in the direction of the rest * add personal signer methods * basic signer request subscription * don't poll blockNumber when not connected * missing return, creating massive ws queue backlogs * ΞTH -> ETH * fix failing tests * registry uses setAddress to actually set addresses now * bytes mapping operates on lowerCase hex strings * sha3 ids for each application * add dappreg to list of contracts * adjust alignment of queries * show gas estimation log * abi with payable for register function * add key as required * image retrieval from dappreg * use proper Image urls * embed and link apps from Parity, retrieved via /api/apps * filter apps that has been replaced * proxy entry for parity-utils * add basiccoin abi * add support for fallback abi type * capture constructor paramaters * merge master into js * move images to assets/images/ * add font assets * import fonts as part of build * don't inline woff files * Revert "merge master into js" This reverts commit cfcfa81bd26f1b3cbc748d3afa1eb5c670b363fe. * remove unused npm packages * information on gas estimates (like almost everywhere else) * don't pass gas & gasPrice to estimation * display account passwordhint when available * signer subscriptions based on polling & function trapping * pending requests retrieved via jsapi * update signer middleware * remove all web3 instances * remove web3 package * last web3 dependencies removed * no need to toChecksumAddress - api takes care of it * expand description for personal_confirmRequest * Signer conversion from web3 -> parity.js completed * explicit in no return * green circle background * remove generated background * convert /api/* paths to localhost:8080/api/* paths (hard-coded, temporary) * change dapps to load from localhost:8080/ui/* * remove dangling web3 files * update manager test for signer * /api/ping -> / * additional token images * additional token images * add missing styles.css for 8180 error pages * cater for txhash returning null/empty object * adjust output directories * Release merge with origin with ours strategy * additional token images * cater for development server * s/localhost/127.0.0.1/ (cater for origin) * Fix address selection for contract deployment * Adjust z-index for error overlay * better text on unique background pattern * fix signer rejections * Don't allow gavcoin transfer with no balance * fix txhash rendering in signer * remove unnecessary ParityBackground * script to update js-precompiled * Redirect from :8080 to :8180 * Remove extra return * Dapp logo images
This commit is contained in:
145
js/src/api/README.md
Normal file
145
js/src/api/README.md
Normal file
@@ -0,0 +1,145 @@
|
||||
# ethapi-js
|
||||
|
||||
A thin, fast, low-level Promise-based wrapper around the Ethereum APIs.
|
||||
|
||||
[](https://travis-ci.org/jacogr/ethapi-js)
|
||||
[](https://coveralls.io/github/jacogr/ethapi-js?branch=master)
|
||||
[](https://david-dm.org/jacogr/ethapi-js)
|
||||
[](https://david-dm.org/jacogr/ethapi-js#info=devDependencies)
|
||||
|
||||
## contributing
|
||||
|
||||
Clone the repo and install dependencies via `npm install`. Tests can be executed via
|
||||
|
||||
- `npm run testOnce` (100% covered unit tests)
|
||||
- `npm run testE2E` (E2E against a running RPC-enabled testnet Parity/Geth instance, `parity --testnet` and for WebScokets, `geth --testnet --ws --wsorigins '*' --rpc`)
|
||||
- setting the environment `DEBUG=true` will display the RPC POST bodies and responses on E2E tests
|
||||
|
||||
## installation
|
||||
|
||||
Install the package with `npm install --save ethapi-js` from the [npm registry ethapi-js](https://www.npmjs.com/package/ethapi-js)
|
||||
|
||||
## usage
|
||||
|
||||
### initialisation
|
||||
|
||||
```javascript
|
||||
// import the actual EthApi class
|
||||
import EthApi from 'ethapi-js';
|
||||
|
||||
// do the setup
|
||||
const transport = new EthApi.Transport.Http('http://localhost:8545'); // or .Ws('ws://localhost:8546')
|
||||
const ethapi = new EthApi(transport);
|
||||
```
|
||||
|
||||
You will require native Promises and fetch support (latest browsers only), they can be utilised by
|
||||
|
||||
```javascript
|
||||
import 'isomorphic-fetch';
|
||||
|
||||
import es6Promise from 'es6-promise';
|
||||
es6Promise.polyfill();
|
||||
```
|
||||
|
||||
### making calls
|
||||
|
||||
perform a call
|
||||
|
||||
```javascript
|
||||
ethapi.eth
|
||||
.coinbase()
|
||||
.then((coinbase) => {
|
||||
console.log(`The coinbase is ${coinbase}`);
|
||||
});
|
||||
```
|
||||
|
||||
multiple promises
|
||||
|
||||
```javascript
|
||||
Promise
|
||||
.all([
|
||||
ethapi.eth.coinbase(),
|
||||
ethapi.net.listening()
|
||||
])
|
||||
.then(([coinbase, listening]) => {
|
||||
// do stuff here
|
||||
});
|
||||
```
|
||||
|
||||
chaining promises
|
||||
|
||||
```javascript
|
||||
ethapi.eth
|
||||
.newFilter({...})
|
||||
.then((filterId) => ethapi.eth.getFilterChanges(filterId))
|
||||
.then((changes) => {
|
||||
console.log(changes);
|
||||
});
|
||||
```
|
||||
|
||||
### contracts
|
||||
|
||||
attach contract
|
||||
|
||||
```javascript
|
||||
const abi = [{ name: 'callMe', inputs: [{ type: 'bool', ...}, { type: 'string', ...}]}, ...abi...];
|
||||
const contract = new ethapi.newContract(abi);
|
||||
```
|
||||
|
||||
deploy
|
||||
|
||||
```javascript
|
||||
contract
|
||||
.deploy('0xc0de', [params], 'superPassword')
|
||||
.then((address) => {
|
||||
console.log(`the contract was deployed at ${address}`);
|
||||
});
|
||||
```
|
||||
|
||||
attach a contract at address
|
||||
|
||||
```javascript
|
||||
// via the constructor & .at function
|
||||
const contract = api.newContract(abi).at('0xa9280...7347b');
|
||||
// or on an already initialised contract
|
||||
contract.at('0xa9280...7347b');
|
||||
// perform calls here
|
||||
```
|
||||
|
||||
find & call a function
|
||||
|
||||
```javascript
|
||||
contract.named
|
||||
.callMe
|
||||
.call({ gas: 21000 }, [true, 'someString']) // or estimateGas or sendTransaction
|
||||
.then((result) => {
|
||||
console.log(`the result was ${result}`);
|
||||
});
|
||||
```
|
||||
|
||||
parse events from transaction receipt
|
||||
|
||||
```javascript
|
||||
contract
|
||||
.parseTransactionEvents(txReceipt)
|
||||
.then((receipt) => {
|
||||
receipt.logs.forEach((log) => {
|
||||
console.log('log parameters', log.params);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
## apis
|
||||
|
||||
APIs implement the calls as exposed in the [Ethcore JSON Ethereum RPC](https://github.com/ethcore/ethereum-rpc-json/) definitions. Mapping follows the naming conventions of the originals, i.e. `eth_call` becomes `eth.call`, `personal_accounts` becomes `personal.accounts`, etc.
|
||||
|
||||
- [ethapi.db](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#db)
|
||||
- [ethapi.eth](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#eth)
|
||||
- [ethapi.ethcore](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#ethcore)
|
||||
- [ethapi.net](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#net)
|
||||
- [ethapi.personal](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#personal)
|
||||
- [ethapi.shh](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#shh)
|
||||
- [ethapi.trace](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#trace)
|
||||
- [ethapi.web3](https://github.com/ethcore/ethereum-rpc-json/blob/master/interfaces.md#web3)
|
||||
|
||||
As a verification step, all exposed interfaces are tested for existing and pointing to the correct endpoints by using the generated interfaces from the above repo.
|
||||
125
js/src/api/api.js
Normal file
125
js/src/api/api.js
Normal file
@@ -0,0 +1,125 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { Http, Ws } from './transport';
|
||||
import Contract from './contract';
|
||||
|
||||
import { Db, Eth, Ethcore, Net, Personal, Shh, Trace, Web3 } from './rpc';
|
||||
import Subscriptions from './subscriptions';
|
||||
import util from './util';
|
||||
import { isFunction } from './util/types';
|
||||
|
||||
export default class Api {
|
||||
constructor (transport) {
|
||||
if (!transport || !isFunction(transport.execute)) {
|
||||
throw new Error('EthApi needs transport with execute() function defined');
|
||||
}
|
||||
|
||||
this._transport = transport;
|
||||
|
||||
this._db = new Db(transport);
|
||||
this._eth = new Eth(transport);
|
||||
this._ethcore = new Ethcore(transport);
|
||||
this._net = new Net(transport);
|
||||
this._personal = new Personal(transport);
|
||||
this._shh = new Shh(transport);
|
||||
this._trace = new Trace(transport);
|
||||
this._web3 = new Web3(transport);
|
||||
|
||||
this._subscriptions = new Subscriptions(this);
|
||||
}
|
||||
|
||||
get db () {
|
||||
return this._db;
|
||||
}
|
||||
|
||||
get eth () {
|
||||
return this._eth;
|
||||
}
|
||||
|
||||
get ethcore () {
|
||||
return this._ethcore;
|
||||
}
|
||||
|
||||
get net () {
|
||||
return this._net;
|
||||
}
|
||||
|
||||
get personal () {
|
||||
return this._personal;
|
||||
}
|
||||
|
||||
get shh () {
|
||||
return this._shh;
|
||||
}
|
||||
|
||||
get trace () {
|
||||
return this._trace;
|
||||
}
|
||||
|
||||
get transport () {
|
||||
return this._transport;
|
||||
}
|
||||
|
||||
get web3 () {
|
||||
return this._web3;
|
||||
}
|
||||
|
||||
get util () {
|
||||
return util;
|
||||
}
|
||||
|
||||
newContract (abi, address) {
|
||||
return new Contract(this, abi).at(address);
|
||||
}
|
||||
|
||||
subscribe (subscriptionName, callback) {
|
||||
return this._subscriptions.subscribe(subscriptionName, callback);
|
||||
}
|
||||
|
||||
unsubscribe (subscriptionName, subscriptionId) {
|
||||
return this._subscriptions.unsubscribe(subscriptionName, subscriptionId);
|
||||
}
|
||||
|
||||
pollMethod (method, input, validate) {
|
||||
const [_group, endpoint] = method.split('_');
|
||||
const group = `_${_group}`;
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const timeout = () => {
|
||||
this[group][endpoint](input)
|
||||
.then((result) => {
|
||||
if (validate ? validate(result) : result) {
|
||||
resolve(result);
|
||||
} else {
|
||||
setTimeout(timeout, 500);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('pollMethod', error);
|
||||
reject(error);
|
||||
});
|
||||
};
|
||||
|
||||
timeout();
|
||||
});
|
||||
}
|
||||
|
||||
static Transport = {
|
||||
Http: Http,
|
||||
Ws: Ws
|
||||
}
|
||||
}
|
||||
47
js/src/api/api.spec.js
Normal file
47
js/src/api/api.spec.js
Normal file
@@ -0,0 +1,47 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { TEST_HTTP_URL, endpointTest } from '../../test/mockRpc';
|
||||
|
||||
import Api from './api';
|
||||
|
||||
import ethereumRpc from '../jsonrpc/';
|
||||
|
||||
describe('api/Api', () => {
|
||||
describe('constructor', () => {
|
||||
it('requires defined/non-null transport object', () => {
|
||||
expect(() => new Api()).to.throw(/Api needs transport/);
|
||||
expect(() => new Api(null)).to.throw(/Api needs transport/);
|
||||
});
|
||||
|
||||
it('requires an execute function on the transport object', () => {
|
||||
expect(() => new Api({})).to.throw(/Api needs transport/);
|
||||
expect(() => new Api({ execute: true })).to.throw(/Api needs transport/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('interface', () => {
|
||||
const api = new Api(new Api.Transport.Http(TEST_HTTP_URL));
|
||||
|
||||
Object.keys(ethereumRpc).sort().forEach((endpoint) => {
|
||||
describe(endpoint, () => {
|
||||
Object.keys(ethereumRpc[endpoint]).sort().forEach((method) => {
|
||||
endpointTest(api, endpoint, method);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
319
js/src/api/contract/contract.js
Normal file
319
js/src/api/contract/contract.js
Normal file
@@ -0,0 +1,319 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 Abi from '../../abi';
|
||||
import Api from '../api';
|
||||
import { isInstanceOf } from '../util/types';
|
||||
|
||||
let nextSubscriptionId = 0;
|
||||
|
||||
export default class Contract {
|
||||
constructor (api, abi) {
|
||||
if (!isInstanceOf(api, Api)) {
|
||||
throw new Error('API instance needs to be provided to Contract');
|
||||
} else if (!abi) {
|
||||
throw new Error('ABI needs to be provided to Contract instance');
|
||||
}
|
||||
|
||||
this._api = api;
|
||||
this._abi = new Abi(abi);
|
||||
|
||||
this._subscriptions = {};
|
||||
this._constructors = this._abi.constructors.map(this._bindFunction);
|
||||
this._functions = this._abi.functions.map(this._bindFunction);
|
||||
this._events = this._abi.events.map(this._bindEvent);
|
||||
|
||||
this._instance = {};
|
||||
|
||||
this._events.forEach((evt) => {
|
||||
this._instance[evt.name] = evt;
|
||||
});
|
||||
this._functions.forEach((fn) => {
|
||||
this._instance[fn.name] = fn;
|
||||
});
|
||||
|
||||
this._sendSubscriptionChanges();
|
||||
}
|
||||
|
||||
get address () {
|
||||
return this._address;
|
||||
}
|
||||
|
||||
get constructors () {
|
||||
return this._constructors;
|
||||
}
|
||||
|
||||
get events () {
|
||||
return this._events;
|
||||
}
|
||||
|
||||
get functions () {
|
||||
return this._functions;
|
||||
}
|
||||
|
||||
get instance () {
|
||||
this._instance.address = this._address;
|
||||
return this._instance;
|
||||
}
|
||||
|
||||
get api () {
|
||||
return this._api;
|
||||
}
|
||||
|
||||
get abi () {
|
||||
return this._abi;
|
||||
}
|
||||
|
||||
at (address) {
|
||||
this._address = address;
|
||||
return this;
|
||||
}
|
||||
|
||||
deploy (options, values, statecb) {
|
||||
let gas;
|
||||
|
||||
const setState = (state) => {
|
||||
if (!statecb) {
|
||||
return;
|
||||
}
|
||||
|
||||
return statecb(null, state);
|
||||
};
|
||||
|
||||
setState({ state: 'estimateGas' });
|
||||
|
||||
return this._api.eth
|
||||
.estimateGas(this._encodeOptions(this.constructors[0], options, values))
|
||||
.then((_gas) => {
|
||||
gas = _gas.mul(1.2);
|
||||
options.gas = gas.toFixed(0);
|
||||
|
||||
setState({ state: 'postTransaction', gas });
|
||||
return this._api.eth.postTransaction(this._encodeOptions(this.constructors[0], options, values));
|
||||
})
|
||||
.then((requestId) => {
|
||||
setState({ state: 'checkRequest', requestId });
|
||||
return this._pollCheckRequest(requestId);
|
||||
})
|
||||
.then((txhash) => {
|
||||
setState({ state: 'getTransactionReceipt', txhash });
|
||||
return this._pollTransactionReceipt(txhash, gas);
|
||||
})
|
||||
.then((receipt) => {
|
||||
if (receipt.gasUsed.eq(gas)) {
|
||||
throw new Error(`Contract not deployed, gasUsed == ${gas.toFixed(0)}`);
|
||||
}
|
||||
|
||||
setState({ state: 'hasReceipt', receipt });
|
||||
this._address = receipt.contractAddress;
|
||||
return this._address;
|
||||
})
|
||||
.then((address) => {
|
||||
setState({ state: 'getCode' });
|
||||
return this._api.eth.getCode(this._address);
|
||||
})
|
||||
.then((code) => {
|
||||
if (code === '0x') {
|
||||
throw new Error('Contract not deployed, getCode returned 0x');
|
||||
}
|
||||
|
||||
setState({ state: 'completed' });
|
||||
return this._address;
|
||||
});
|
||||
}
|
||||
|
||||
parseEventLogs (logs) {
|
||||
return logs.map((log) => {
|
||||
const signature = log.topics[0].substr(2);
|
||||
const event = this.events.find((evt) => evt.signature === signature);
|
||||
|
||||
if (!event) {
|
||||
throw new Error(`Unable to find event matching signature ${signature}`);
|
||||
}
|
||||
|
||||
const decoded = event.decodeLog(log.topics, log.data);
|
||||
|
||||
log.params = {};
|
||||
log.event = event.name;
|
||||
|
||||
decoded.params.forEach((param) => {
|
||||
log.params[param.name] = param.token.value;
|
||||
});
|
||||
|
||||
return log;
|
||||
});
|
||||
}
|
||||
|
||||
parseTransactionEvents (receipt) {
|
||||
receipt.logs = this.parseEventLogs(receipt.logs);
|
||||
|
||||
return receipt;
|
||||
}
|
||||
|
||||
_pollCheckRequest = (requestId) => {
|
||||
return this._api.pollMethod('eth_checkRequest', requestId);
|
||||
}
|
||||
|
||||
_pollTransactionReceipt = (txhash, gas) => {
|
||||
return this.api.pollMethod('eth_getTransactionReceipt', txhash, (receipt) => {
|
||||
if (!receipt || !receipt.blockNumber || receipt.blockNumber.eq(0)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
_encodeOptions (func, options, values) {
|
||||
const tokens = func ? this._abi.encodeTokens(func.inputParamTypes(), values) : null;
|
||||
const call = tokens ? func.encodeCall(tokens) : null;
|
||||
|
||||
if (options.data && options.data.substr(0, 2) === '0x') {
|
||||
options.data = options.data.substr(2);
|
||||
}
|
||||
options.data = `0x${options.data || ''}${call || ''}`;
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
_addOptionsTo (options = {}) {
|
||||
return Object.assign({
|
||||
to: this._address
|
||||
}, options);
|
||||
}
|
||||
|
||||
_bindFunction = (func) => {
|
||||
func.call = (options, values = []) => {
|
||||
return this._api.eth
|
||||
.call(this._encodeOptions(func, this._addOptionsTo(options), values))
|
||||
.then((encoded) => func.decodeOutput(encoded))
|
||||
.then((tokens) => tokens.map((token) => token.value))
|
||||
.then((returns) => returns.length === 1 ? returns[0] : returns);
|
||||
};
|
||||
|
||||
if (!func.constant) {
|
||||
func.postTransaction = (options, values = []) => {
|
||||
return this._api.eth
|
||||
.postTransaction(this._encodeOptions(func, this._addOptionsTo(options), values));
|
||||
};
|
||||
|
||||
func.estimateGas = (options, values = []) => {
|
||||
return this._api.eth
|
||||
.estimateGas(this._encodeOptions(func, this._addOptionsTo(options), values));
|
||||
};
|
||||
}
|
||||
|
||||
return func;
|
||||
}
|
||||
|
||||
_bindEvent = (event) => {
|
||||
event.subscribe = (options = {}, callback) => {
|
||||
return this._subscribe(event, options, callback);
|
||||
};
|
||||
|
||||
event.unsubscribe = (subscriptionId) => {
|
||||
return this.unsubscribe(subscriptionId);
|
||||
};
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
subscribe (eventName = null, options = {}, callback) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let event = null;
|
||||
|
||||
if (eventName) {
|
||||
event = this._events.find((evt) => evt.name === eventName);
|
||||
|
||||
if (!event) {
|
||||
const events = this._events.map((evt) => evt.name).join(', ');
|
||||
reject(new Error(`${eventName} is not a valid eventName, subscribe using one of ${events} (or null to include all)`));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return this._subscribe(event, options, callback).then(resolve).catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
_subscribe (event = null, _options, callback) {
|
||||
const subscriptionId = nextSubscriptionId++;
|
||||
const options = Object.assign({}, _options, {
|
||||
address: this._address,
|
||||
topics: [event ? event.signature : null]
|
||||
});
|
||||
|
||||
return this._api.eth
|
||||
.newFilter(options)
|
||||
.then((filterId) => {
|
||||
return this._api.eth
|
||||
.getFilterLogs(filterId)
|
||||
.then((logs) => {
|
||||
callback(null, this.parseEventLogs(logs));
|
||||
this._subscriptions[subscriptionId] = {
|
||||
options,
|
||||
callback,
|
||||
filterId
|
||||
};
|
||||
|
||||
return subscriptionId;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
unsubscribe (subscriptionId) {
|
||||
return this._api.eth
|
||||
.uninstallFilter(this._subscriptions[subscriptionId].filterId)
|
||||
.then(() => {
|
||||
delete this._subscriptions[subscriptionId];
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('unsubscribe', error);
|
||||
});
|
||||
}
|
||||
|
||||
_sendSubscriptionChanges = () => {
|
||||
const subscriptions = Object.values(this._subscriptions);
|
||||
const timeout = () => setTimeout(this._sendSubscriptionChanges, 1000);
|
||||
|
||||
Promise
|
||||
.all(
|
||||
subscriptions.map((subscription) => {
|
||||
return this._api.eth.getFilterChanges(subscription.filterId);
|
||||
})
|
||||
)
|
||||
.then((logsArray) => {
|
||||
logsArray.forEach((logs, idx) => {
|
||||
if (!logs || !logs.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
subscriptions[idx].callback(null, this.parseEventLogs(logs));
|
||||
} catch (error) {
|
||||
this.unsubscribe(idx);
|
||||
console.error('_sendSubscriptionChanges', error);
|
||||
}
|
||||
});
|
||||
|
||||
timeout();
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('_sendSubscriptionChanges', error);
|
||||
timeout();
|
||||
});
|
||||
}
|
||||
}
|
||||
539
js/src/api/contract/contract.spec.js
Normal file
539
js/src/api/contract/contract.spec.js
Normal file
@@ -0,0 +1,539 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 BigNumber from 'bignumber.js';
|
||||
import sinon from 'sinon';
|
||||
|
||||
import { TEST_HTTP_URL, mockHttp } from '../../../test/mockRpc';
|
||||
|
||||
import Abi from '../../abi';
|
||||
|
||||
import Api from '../api';
|
||||
import Contract from './contract';
|
||||
import { isInstanceOf, isFunction } from '../util/types';
|
||||
|
||||
const transport = new Api.Transport.Http(TEST_HTTP_URL);
|
||||
const eth = new Api(transport);
|
||||
|
||||
describe('api/contract/Contract', () => {
|
||||
const ADDR = '0x0123456789';
|
||||
const ABI = [
|
||||
{
|
||||
type: 'function', name: 'test',
|
||||
inputs: [{ name: 'boolin', type: 'bool' }, { name: 'stringin', type: 'string' }],
|
||||
outputs: [{ type: 'uint' }]
|
||||
},
|
||||
{
|
||||
type: 'function', name: 'test2',
|
||||
outputs: [{ type: 'uint' }, { type: 'uint' }]
|
||||
},
|
||||
{ type: 'constructor' },
|
||||
{ type: 'event', name: 'baz' },
|
||||
{ type: 'event', name: 'foo' }
|
||||
];
|
||||
const VALUES = [true, 'jacogr'];
|
||||
const ENCODED = '0x023562050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000066a61636f67720000000000000000000000000000000000000000000000000000';
|
||||
const RETURN1 = '0000000000000000000000000000000000000000000000000000000000123456';
|
||||
const RETURN2 = '0000000000000000000000000000000000000000000000000000000000456789';
|
||||
let scope;
|
||||
|
||||
describe('constructor', () => {
|
||||
it('needs an EthAbi instance', () => {
|
||||
expect(() => new Contract()).to.throw(/API instance needs to be provided to Contract/);
|
||||
});
|
||||
|
||||
it('needs an ABI', () => {
|
||||
expect(() => new Contract(eth)).to.throw(/ABI needs to be provided to Contract instance/);
|
||||
});
|
||||
|
||||
describe('internal setup', () => {
|
||||
const contract = new Contract(eth, ABI);
|
||||
|
||||
it('sets EthApi & parsed interface', () => {
|
||||
expect(contract.address).to.not.be.ok;
|
||||
expect(contract.api).to.deep.equal(eth);
|
||||
expect(isInstanceOf(contract.abi, Abi)).to.be.ok;
|
||||
});
|
||||
|
||||
it('attaches functions', () => {
|
||||
expect(contract.functions.length).to.equal(2);
|
||||
expect(contract.functions[0].name).to.equal('test');
|
||||
});
|
||||
|
||||
it('attaches constructors', () => {
|
||||
expect(contract.constructors.length).to.equal(1);
|
||||
});
|
||||
|
||||
it('attaches events', () => {
|
||||
expect(contract.events.length).to.equal(2);
|
||||
expect(contract.events[0].name).to.equal('baz');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('at', () => {
|
||||
it('sets returns the functions, events & sets the address', () => {
|
||||
const contract = new Contract(eth, [
|
||||
{
|
||||
constant: true,
|
||||
inputs: [{
|
||||
name: '_who',
|
||||
type: 'address'
|
||||
}],
|
||||
name: 'balanceOf',
|
||||
outputs: [{
|
||||
name: '',
|
||||
type: 'uint256'
|
||||
}],
|
||||
type: 'function'
|
||||
},
|
||||
{
|
||||
anonymous: false,
|
||||
inputs: [{
|
||||
indexed: false,
|
||||
name: 'amount',
|
||||
type: 'uint256'
|
||||
}],
|
||||
name: 'Drained',
|
||||
type: 'event'
|
||||
}
|
||||
]);
|
||||
contract.at('6789');
|
||||
|
||||
expect(Object.keys(contract.instance)).to.deep.equal(['Drained', 'balanceOf', 'address']);
|
||||
expect(contract.address).to.equal('6789');
|
||||
});
|
||||
});
|
||||
|
||||
describe('parseTransactionEvents', () => {
|
||||
it('checks for unmatched signatures', () => {
|
||||
const contract = new Contract(eth, [{ anonymous: false, name: 'Message', type: 'event' }]);
|
||||
expect(() => contract.parseTransactionEvents({
|
||||
logs: [{
|
||||
data: '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063cf90d3f0410092fc0fca41846f5962239791950000000000000000000000000000000000000000000000000000000056e6c85f0000000000000000000000000000000000000000000000000001000000004fcd00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000d706f7374286d6573736167652900000000000000000000000000000000000000',
|
||||
topics: [
|
||||
'0x954ba6c157daf8a26539574ffa64203c044691aa57251af95f4b48d85ec00dd5',
|
||||
'0x0000000000000000000000000000000000000000000000000001000000004fe0'
|
||||
]
|
||||
}]
|
||||
})).to.throw(/event matching signature/);
|
||||
});
|
||||
|
||||
it('parses a transaction log into the data', () => {
|
||||
const contract = new Contract(eth, [
|
||||
{
|
||||
anonymous: false, name: 'Message', type: 'event',
|
||||
inputs: [
|
||||
{ indexed: true, name: 'postId', type: 'uint256' },
|
||||
{ indexed: false, name: 'parentId', type: 'uint256' },
|
||||
{ indexed: false, name: 'sender', type: 'address' },
|
||||
{ indexed: false, name: 'at', type: 'uint256' },
|
||||
{ indexed: false, name: 'messageId', type: 'uint256' },
|
||||
{ indexed: false, name: 'message', type: 'string' }
|
||||
]
|
||||
}
|
||||
]);
|
||||
const decoded = contract.parseTransactionEvents({
|
||||
blockHash: '0xa9280530a3b47bee2fc80f2862fd56502ae075350571d724d6442ea4c597347b',
|
||||
blockNumber: '0x4fcd',
|
||||
cumulativeGasUsed: '0xb57f',
|
||||
gasUsed: '0xb57f',
|
||||
logs: [{
|
||||
address: '0x22bff18ec62281850546a664bb63a5c06ac5f76c',
|
||||
blockHash: '0xa9280530a3b47bee2fc80f2862fd56502ae075350571d724d6442ea4c597347b',
|
||||
blockNumber: '0x4fcd',
|
||||
data: '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063cf90d3f0410092fc0fca41846f5962239791950000000000000000000000000000000000000000000000000000000056e6c85f0000000000000000000000000000000000000000000000000001000000004fcd00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000d706f7374286d6573736167652900000000000000000000000000000000000000',
|
||||
logIndex: '0x0',
|
||||
topics: [
|
||||
'0x954ba6c157daf8a26539574ffa64203c044691aa57251af95f4b48d85ec00dd5',
|
||||
'0x0000000000000000000000000000000000000000000000000001000000004fe0'
|
||||
],
|
||||
transactionHash: '0xca16f537d761d13e4e80953b754e2b15541f267d6cad9381f750af1bae1e4917',
|
||||
transactionIndex: '0x0'
|
||||
}],
|
||||
to: '0x22bff18ec62281850546a664bb63a5c06ac5f76c',
|
||||
transactionHash: '0xca16f537d761d13e4e80953b754e2b15541f267d6cad9381f750af1bae1e4917',
|
||||
transactionIndex: '0x0'
|
||||
});
|
||||
const log = decoded.logs[0];
|
||||
|
||||
expect(log.event).to.equal('Message');
|
||||
expect(log.address).to.equal('0x22bff18ec62281850546a664bb63a5c06ac5f76c');
|
||||
expect(log.params).to.deep.equal({
|
||||
at: new BigNumber('1457965151'),
|
||||
message: 'post(message)',
|
||||
messageId: new BigNumber('281474976731085'),
|
||||
parentId: new BigNumber(0),
|
||||
postId: new BigNumber('281474976731104'),
|
||||
sender: '0x63Cf90D3f0410092FC0fca41846f596223979195'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('_pollTransactionReceipt', () => {
|
||||
const contract = new Contract(eth, ABI);
|
||||
const ADDRESS = '0xD337e80eEdBdf86eDBba021797d7e4e00Bb78351';
|
||||
const BLOCKNUMBER = '555000';
|
||||
const RECEIPT = { contractAddress: ADDRESS.toLowerCase(), blockNumber: BLOCKNUMBER };
|
||||
const EXPECT = { contractAddress: ADDRESS, blockNumber: new BigNumber(BLOCKNUMBER) };
|
||||
|
||||
let scope;
|
||||
let receipt;
|
||||
|
||||
describe('success', () => {
|
||||
before(() => {
|
||||
scope = mockHttp([
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: null } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: null } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT } }
|
||||
]);
|
||||
|
||||
return contract
|
||||
._pollTransactionReceipt('0x123')
|
||||
.then((_receipt) => {
|
||||
receipt = _receipt;
|
||||
});
|
||||
});
|
||||
|
||||
it('sends multiple getTransactionReceipt calls', () => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
});
|
||||
|
||||
it('passes the txhash through', () => {
|
||||
expect(scope.body.eth_getTransactionReceipt.params[0]).to.equal('0x123');
|
||||
});
|
||||
|
||||
it('receives the final receipt', () => {
|
||||
expect(receipt).to.deep.equal(EXPECT);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error', () => {
|
||||
before(() => {
|
||||
scope = mockHttp([{ method: 'eth_getTransactionReceipt', reply: { error: { code: -1, message: 'failure' } } }]);
|
||||
});
|
||||
|
||||
it('returns the errors', () => {
|
||||
return contract
|
||||
._pollTransactionReceipt('0x123')
|
||||
.catch((error) => {
|
||||
expect(error.message).to.match(/failure/);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('deploy', () => {
|
||||
const contract = new Contract(eth, ABI);
|
||||
const ADDRESS = '0xD337e80eEdBdf86eDBba021797d7e4e00Bb78351';
|
||||
const RECEIPT_PEND = { contractAddress: ADDRESS.toLowerCase(), gasUsed: 50, blockNumber: 0 };
|
||||
const RECEIPT_DONE = { contractAddress: ADDRESS.toLowerCase(), gasUsed: 50, blockNumber: 2500 };
|
||||
const RECEIPT_EXCP = { contractAddress: ADDRESS.toLowerCase(), gasUsed: 1200, blockNumber: 2500 };
|
||||
|
||||
let scope;
|
||||
|
||||
describe('success', () => {
|
||||
before(() => {
|
||||
scope = mockHttp([
|
||||
{ method: 'eth_estimateGas', reply: { result: 1000 } },
|
||||
{ method: 'eth_postTransaction', reply: { result: '0x678' } },
|
||||
{ method: 'eth_checkRequest', reply: { result: null } },
|
||||
{ method: 'eth_checkRequest', reply: { result: '0x890' } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: null } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_PEND } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_DONE } },
|
||||
{ method: 'eth_getCode', reply: { result: '0x456' } }
|
||||
]);
|
||||
|
||||
return contract.deploy({ data: '0x123' }, []);
|
||||
});
|
||||
|
||||
it('calls estimateGas, postTransaction, checkRequest, getTransactionReceipt & getCode in order', () => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
});
|
||||
|
||||
it('passes the options through to postTransaction (incl. gas calculation)', () => {
|
||||
expect(scope.body.eth_postTransaction.params).to.deep.equal([
|
||||
{ data: '0x123', gas: '0x4b0' }
|
||||
]);
|
||||
});
|
||||
|
||||
it('sets the address of the contract', () => {
|
||||
expect(contract.address).to.equal(ADDRESS);
|
||||
});
|
||||
});
|
||||
|
||||
describe('error', () => {
|
||||
it('fails when gasUsed == gas', () => {
|
||||
mockHttp([
|
||||
{ method: 'eth_estimateGas', reply: { result: 1000 } },
|
||||
{ method: 'eth_postTransaction', reply: { result: '0x678' } },
|
||||
{ method: 'eth_checkRequest', reply: { result: '0x789' } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_EXCP } }
|
||||
]);
|
||||
|
||||
return contract
|
||||
.deploy({ data: '0x123' }, [])
|
||||
.catch((error) => {
|
||||
expect(error.message).to.match(/not deployed, gasUsed/);
|
||||
});
|
||||
});
|
||||
|
||||
it('fails when no code was deployed', () => {
|
||||
mockHttp([
|
||||
{ method: 'eth_estimateGas', reply: { result: 1000 } },
|
||||
{ method: 'eth_postTransaction', reply: { result: '0x678' } },
|
||||
{ method: 'eth_checkRequest', reply: { result: '0x789' } },
|
||||
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_DONE } },
|
||||
{ method: 'eth_getCode', reply: { result: '0x' } }
|
||||
]);
|
||||
|
||||
return contract
|
||||
.deploy({ data: '0x123' }, [])
|
||||
.catch((error) => {
|
||||
expect(error.message).to.match(/not deployed, getCode/);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('bindings', () => {
|
||||
let contract;
|
||||
let cons;
|
||||
let func;
|
||||
|
||||
beforeEach(() => {
|
||||
contract = new Contract(eth, ABI);
|
||||
contract.at(ADDR);
|
||||
cons = contract.constructors[0];
|
||||
func = contract.functions.find((fn) => fn.name === 'test');
|
||||
});
|
||||
|
||||
describe('_addOptionsTo', () => {
|
||||
it('works on no object specified', () => {
|
||||
expect(contract._addOptionsTo()).to.deep.equal({ to: ADDR });
|
||||
});
|
||||
|
||||
it('uses the contract address when none specified', () => {
|
||||
expect(contract._addOptionsTo({ from: 'me' })).to.deep.equal({ to: ADDR, from: 'me' });
|
||||
});
|
||||
|
||||
it('overrides the contract address when specified', () => {
|
||||
expect(contract._addOptionsTo({ to: 'you', from: 'me' })).to.deep.equal({ to: 'you', from: 'me' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('attachments', () => {
|
||||
it('attaches .call, .postTransaction & .estimateGas to constructors', () => {
|
||||
expect(isFunction(cons.call)).to.be.true;
|
||||
expect(isFunction(cons.postTransaction)).to.be.true;
|
||||
expect(isFunction(cons.estimateGas)).to.be.true;
|
||||
});
|
||||
|
||||
it('attaches .call, .postTransaction & .estimateGas to functions', () => {
|
||||
expect(isFunction(func.call)).to.be.true;
|
||||
expect(isFunction(func.postTransaction)).to.be.true;
|
||||
expect(isFunction(func.estimateGas)).to.be.true;
|
||||
});
|
||||
|
||||
it('attaches .call only to constant functions', () => {
|
||||
func = (new Contract(eth, [{ type: 'function', name: 'test', constant: true }])).functions[0];
|
||||
|
||||
expect(isFunction(func.call)).to.be.true;
|
||||
expect(isFunction(func.postTransaction)).to.be.false;
|
||||
expect(isFunction(func.estimateGas)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('postTransaction', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_postTransaction', reply: { result: ['hashId'] } }]);
|
||||
});
|
||||
|
||||
it('encodes options and mades an eth_postTransaction call', () => {
|
||||
return func
|
||||
.postTransaction({ someExtras: 'foo' }, VALUES)
|
||||
.then(() => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
expect(scope.body.eth_postTransaction.params[0]).to.deep.equal({
|
||||
someExtras: 'foo',
|
||||
to: ADDR,
|
||||
data: ENCODED
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('estimateGas', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_estimateGas', reply: { result: ['0x123'] } }]);
|
||||
});
|
||||
|
||||
it('encodes options and mades an eth_estimateGas call', () => {
|
||||
return func
|
||||
.estimateGas({ someExtras: 'foo' }, VALUES)
|
||||
.then((amount) => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
expect(amount.toString(16)).to.equal('123');
|
||||
expect(scope.body.eth_estimateGas.params).to.deep.equal([{
|
||||
someExtras: 'foo',
|
||||
to: ADDR,
|
||||
data: ENCODED
|
||||
}]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('call', () => {
|
||||
it('encodes options and mades an eth_call call', () => {
|
||||
scope = mockHttp([{ method: 'eth_call', reply: { result: RETURN1 } }]);
|
||||
|
||||
return func
|
||||
.call({ someExtras: 'foo' }, VALUES)
|
||||
.then((result) => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
expect(scope.body.eth_call.params).to.deep.equal([{
|
||||
someExtras: 'foo',
|
||||
to: ADDR,
|
||||
data: ENCODED
|
||||
}, 'latest']);
|
||||
expect(result.toString(16)).to.equal('123456');
|
||||
});
|
||||
});
|
||||
|
||||
it('encodes options and mades an eth_call call (multiple returns)', () => {
|
||||
scope = mockHttp([{ method: 'eth_call', reply: { result: `${RETURN1}${RETURN2}` } }]);
|
||||
|
||||
return contract.functions[1]
|
||||
.call({}, [])
|
||||
.then((result) => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
expect(result.length).to.equal(2);
|
||||
expect(result[0].toString(16)).to.equal('123456');
|
||||
expect(result[1].toString(16)).to.equal('456789');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('subscribe', () => {
|
||||
const abi = [
|
||||
{
|
||||
anonymous: false, name: 'Message', type: 'event',
|
||||
inputs: [
|
||||
{ indexed: true, name: 'postId', type: 'uint256' },
|
||||
{ indexed: false, name: 'parentId', type: 'uint256' },
|
||||
{ indexed: false, name: 'sender', type: 'address' },
|
||||
{ indexed: false, name: 'at', type: 'uint256' },
|
||||
{ indexed: false, name: 'messageId', type: 'uint256' },
|
||||
{ indexed: false, name: 'message', type: 'string' }
|
||||
]
|
||||
}
|
||||
];
|
||||
const logs = [{
|
||||
address: '0x22bff18ec62281850546a664bb63a5c06ac5f76c',
|
||||
blockHash: '0xa9280530a3b47bee2fc80f2862fd56502ae075350571d724d6442ea4c597347b',
|
||||
blockNumber: '0x4fcd',
|
||||
data: '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063cf90d3f0410092fc0fca41846f5962239791950000000000000000000000000000000000000000000000000000000056e6c85f0000000000000000000000000000000000000000000000000001000000004fcd00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000d706f7374286d6573736167652900000000000000000000000000000000000000',
|
||||
logIndex: '0x0',
|
||||
topics: [
|
||||
'0x954ba6c157daf8a26539574ffa64203c044691aa57251af95f4b48d85ec00dd5',
|
||||
'0x0000000000000000000000000000000000000000000000000001000000004fe0'
|
||||
],
|
||||
transactionHash: '0xca16f537d761d13e4e80953b754e2b15541f267d6cad9381f750af1bae1e4917',
|
||||
transactionIndex: '0x0'
|
||||
}];
|
||||
const parsed = [{
|
||||
address: '0x22bfF18ec62281850546a664bb63a5C06AC5F76C',
|
||||
blockHash: '0xa9280530a3b47bee2fc80f2862fd56502ae075350571d724d6442ea4c597347b',
|
||||
blockNumber: new BigNumber(20429),
|
||||
data: '0x000000000000000000000000000000000000000000000000000000000000000000000000000000000000000063cf90d3f0410092fc0fca41846f5962239791950000000000000000000000000000000000000000000000000000000056e6c85f0000000000000000000000000000000000000000000000000001000000004fcd00000000000000000000000000000000000000000000000000000000000000a0000000000000000000000000000000000000000000000000000000000000000d706f7374286d6573736167652900000000000000000000000000000000000000',
|
||||
event: 'Message',
|
||||
logIndex: new BigNumber(0),
|
||||
params: {
|
||||
at: new BigNumber(1457965151),
|
||||
message: 'post(message)',
|
||||
messageId: new BigNumber(281474976731085),
|
||||
parentId: new BigNumber(0),
|
||||
postId: new BigNumber(281474976731104),
|
||||
sender: '0x63Cf90D3f0410092FC0fca41846f596223979195'
|
||||
},
|
||||
topics: [
|
||||
'0x954ba6c157daf8a26539574ffa64203c044691aa57251af95f4b48d85ec00dd5', '0x0000000000000000000000000000000000000000000000000001000000004fe0'
|
||||
],
|
||||
transactionHash: '0xca16f537d761d13e4e80953b754e2b15541f267d6cad9381f750af1bae1e4917',
|
||||
transactionIndex: new BigNumber(0)
|
||||
}];
|
||||
let contract;
|
||||
|
||||
beforeEach(() => {
|
||||
contract = new Contract(eth, abi);
|
||||
contract.at(ADDR);
|
||||
});
|
||||
|
||||
describe('invalid events', () => {
|
||||
it('fails to subscribe to an invalid names', () => {
|
||||
return contract
|
||||
.subscribe('invalid')
|
||||
.catch((error) => {
|
||||
expect(error.message).to.match(/invalid is not a valid eventName/);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('valid events', () => {
|
||||
let cbb;
|
||||
let cbe;
|
||||
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([
|
||||
{ method: 'eth_newFilter', reply: { result: '0x123' } },
|
||||
{ method: 'eth_getFilterLogs', reply: { result: logs } },
|
||||
{ method: 'eth_newFilter', reply: { result: '0x123' } },
|
||||
{ method: 'eth_getFilterLogs', reply: { result: logs } }
|
||||
]);
|
||||
cbb = sinon.stub();
|
||||
cbe = sinon.stub();
|
||||
|
||||
return contract.subscribe('Message', {}, cbb);
|
||||
});
|
||||
|
||||
it('sets the subscriptionId returned', () => {
|
||||
return contract
|
||||
.subscribe('Message', {}, cbe)
|
||||
.then((subscriptionId) => {
|
||||
expect(subscriptionId).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
it('creates a new filter and retrieves the logs on it', () => {
|
||||
return contract
|
||||
.subscribe('Message', {}, cbe)
|
||||
.then((subscriptionId) => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the logs to the callback', () => {
|
||||
return contract
|
||||
.subscribe('Message', {}, cbe)
|
||||
.then((subscriptionId) => {
|
||||
expect(cbe).to.have.been.calledWith(null, parsed);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/api/contract/index.js
Normal file
17
js/src/api/contract/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './contract';
|
||||
139
js/src/api/format/input.js
Normal file
139
js/src/api/format/input.js
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 BigNumber from 'bignumber.js';
|
||||
|
||||
import { isArray, isHex, isInstanceOf, isString } from '../util/types';
|
||||
|
||||
export function inAddress (address) {
|
||||
// TODO: address validation if we have upper-lower addresses
|
||||
return inHex(address);
|
||||
}
|
||||
|
||||
export function inBlockNumber (blockNumber) {
|
||||
if (isString(blockNumber)) {
|
||||
switch (blockNumber) {
|
||||
case 'earliest':
|
||||
case 'latest':
|
||||
case 'pending':
|
||||
return blockNumber;
|
||||
}
|
||||
}
|
||||
|
||||
return inNumber16(blockNumber);
|
||||
}
|
||||
|
||||
export function inData (data) {
|
||||
if (data && data.length && !isHex(data)) {
|
||||
data = data.split('').map((chr) => {
|
||||
return `0${chr.charCodeAt(0).toString(16)}`.slice(-2);
|
||||
}).join('');
|
||||
}
|
||||
|
||||
return inHex(data);
|
||||
}
|
||||
|
||||
export function inTopics (_topics) {
|
||||
let topics = (_topics || [])
|
||||
.filter((topic) => topic)
|
||||
.map(inHex);
|
||||
|
||||
while (topics.length < 4) {
|
||||
topics.push(null);
|
||||
}
|
||||
|
||||
return topics;
|
||||
}
|
||||
|
||||
export function inFilter (options) {
|
||||
if (options) {
|
||||
Object.keys(options).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'address':
|
||||
if (isArray(options[key])) {
|
||||
options[key] = options[key].map(inAddress);
|
||||
} else {
|
||||
options[key] = inAddress(options[key]);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'fromBlock':
|
||||
case 'toBlock':
|
||||
options[key] = inBlockNumber(options[key]);
|
||||
break;
|
||||
|
||||
case 'limit':
|
||||
options[key] = inNumber10(options[key]);
|
||||
break;
|
||||
|
||||
case 'topics':
|
||||
options[key] = inTopics(options[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
|
||||
export function inHex (str) {
|
||||
if (str && str.substr(0, 2) === '0x') {
|
||||
return str.toLowerCase();
|
||||
}
|
||||
|
||||
return `0x${(str || '').toLowerCase()}`;
|
||||
}
|
||||
|
||||
export function inNumber10 (number) {
|
||||
if (isInstanceOf(number, BigNumber)) {
|
||||
return number.toNumber();
|
||||
}
|
||||
|
||||
return (new BigNumber(number || 0)).toNumber();
|
||||
}
|
||||
|
||||
export function inNumber16 (number) {
|
||||
if (isInstanceOf(number, BigNumber)) {
|
||||
return inHex(number.toString(16));
|
||||
}
|
||||
|
||||
return inHex((new BigNumber(number || 0)).toString(16));
|
||||
}
|
||||
|
||||
export function inOptions (options) {
|
||||
if (options) {
|
||||
Object.keys(options).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'from':
|
||||
case 'to':
|
||||
options[key] = inAddress(options[key]);
|
||||
break;
|
||||
|
||||
case 'gas':
|
||||
case 'gasPrice':
|
||||
case 'value':
|
||||
case 'nonce':
|
||||
options[key] = inNumber16(options[key]);
|
||||
break;
|
||||
|
||||
case 'data':
|
||||
options[key] = inData(options[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return options;
|
||||
}
|
||||
245
js/src/api/format/input.spec.js
Normal file
245
js/src/api/format/input.spec.js
Normal file
@@ -0,0 +1,245 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 BigNumber from 'bignumber.js';
|
||||
|
||||
import { inAddress, inBlockNumber, inData, inFilter, inHex, inNumber10, inNumber16, inOptions } from './input';
|
||||
import { isAddress } from '../../../test/types';
|
||||
|
||||
describe('api/format/input', () => {
|
||||
const address = '0x63cf90d3f0410092fc0fca41846f596223979195';
|
||||
|
||||
describe('inAddress', () => {
|
||||
const address = '63cf90d3f0410092fc0fca41846f596223979195';
|
||||
|
||||
it('adds the leading 0x as required', () => {
|
||||
expect(inAddress(address)).to.equal(`0x${address}`);
|
||||
});
|
||||
|
||||
it('returns verified addresses as-is', () => {
|
||||
expect(inAddress(`0x${address}`)).to.equal(`0x${address}`);
|
||||
});
|
||||
|
||||
it('returns lowercase equivalents', () => {
|
||||
expect(inAddress(address.toUpperCase())).to.equal(`0x${address}`);
|
||||
});
|
||||
|
||||
it('returns 0x on null addresses', () => {
|
||||
expect(inAddress()).to.equal('0x');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inBlockNumber()', () => {
|
||||
it('returns earliest as-is', () => {
|
||||
expect(inBlockNumber('earliest')).to.equal('earliest');
|
||||
});
|
||||
|
||||
it('returns latest as-is', () => {
|
||||
expect(inBlockNumber('latest')).to.equal('latest');
|
||||
});
|
||||
|
||||
it('returns pending as-is', () => {
|
||||
expect(inBlockNumber('pending')).to.equal('pending');
|
||||
});
|
||||
|
||||
it('formats existing BigNumber into hex', () => {
|
||||
expect(inBlockNumber(new BigNumber(0x123456))).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('formats hex strings into hex', () => {
|
||||
expect(inBlockNumber('0x123456')).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('formats numbers into hex', () => {
|
||||
expect(inBlockNumber(0x123456)).to.equal('0x123456');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inData', () => {
|
||||
it('formats to hex', () => {
|
||||
expect(inData('123456')).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('converts a string to a hex representation', () => {
|
||||
expect(inData('jaco')).to.equal('0x6a61636f');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inHex', () => {
|
||||
it('leaves leading 0x as-is', () => {
|
||||
expect(inHex('0x123456')).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('adds a leading 0x', () => {
|
||||
expect(inHex('123456')).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('returns uppercase as lowercase (leading 0x)', () => {
|
||||
expect(inHex('0xABCDEF')).to.equal('0xabcdef');
|
||||
});
|
||||
|
||||
it('returns uppercase as lowercase (no leading 0x)', () => {
|
||||
expect(inHex('ABCDEF')).to.equal('0xabcdef');
|
||||
});
|
||||
|
||||
it('handles empty & null', () => {
|
||||
expect(inHex()).to.equal('0x');
|
||||
expect(inHex('')).to.equal('0x');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inFilter', () => {
|
||||
['address'].forEach((input) => {
|
||||
it(`formats ${input} address as address`, () => {
|
||||
const block = {};
|
||||
block[input] = address;
|
||||
const formatted = inFilter(block)[input];
|
||||
|
||||
expect(isAddress(formatted)).to.be.true;
|
||||
expect(formatted).to.equal(address);
|
||||
});
|
||||
});
|
||||
|
||||
['fromBlock', 'toBlock'].forEach((input) => {
|
||||
it(`formats ${input} number as blockNumber`, () => {
|
||||
const block = {};
|
||||
block[input] = 0x123;
|
||||
const formatted = inFilter(block)[input];
|
||||
|
||||
expect(formatted).to.equal('0x123');
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores and passes through unknown keys', () => {
|
||||
expect(inFilter({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
|
||||
});
|
||||
|
||||
it('formats an filter options object with relevant entries converted', () => {
|
||||
expect(
|
||||
inFilter({
|
||||
address: address,
|
||||
fromBlock: 'latest',
|
||||
toBlock: 0x101,
|
||||
extraData: 'someExtraStuffInHere',
|
||||
limit: 0x32
|
||||
})
|
||||
).to.deep.equal({
|
||||
address: address,
|
||||
fromBlock: 'latest',
|
||||
toBlock: '0x101',
|
||||
extraData: 'someExtraStuffInHere',
|
||||
limit: 50
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('inNumber10()', () => {
|
||||
it('formats existing BigNumber into number', () => {
|
||||
expect(inNumber10(new BigNumber(123))).to.equal(123);
|
||||
});
|
||||
|
||||
it('formats hex strings into decimal', () => {
|
||||
expect(inNumber10('0x0a')).to.equal(10);
|
||||
});
|
||||
|
||||
it('formats numbers into number', () => {
|
||||
expect(inNumber10(123)).to.equal(123);
|
||||
});
|
||||
|
||||
it('formats undefined into 0', () => {
|
||||
expect(inNumber10()).to.equal(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inNumber16()', () => {
|
||||
it('formats existing BigNumber into hex', () => {
|
||||
expect(inNumber16(new BigNumber(0x123456))).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('formats hex strings into hex', () => {
|
||||
expect(inNumber16('0x123456')).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('formats numbers into hex', () => {
|
||||
expect(inNumber16(0x123456)).to.equal('0x123456');
|
||||
});
|
||||
|
||||
it('formats undefined into 0', () => {
|
||||
expect(inNumber16()).to.equal('0x0');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inOptions', () => {
|
||||
['data'].forEach((input) => {
|
||||
it(`converts ${input} to hex data`, () => {
|
||||
const block = {};
|
||||
block[input] = '1234';
|
||||
const formatted = inData(block[input]);
|
||||
|
||||
expect(formatted).to.equal('0x1234');
|
||||
});
|
||||
});
|
||||
|
||||
['from', 'to'].forEach((input) => {
|
||||
it(`formats ${input} address as address`, () => {
|
||||
const block = {};
|
||||
block[input] = address;
|
||||
const formatted = inOptions(block)[input];
|
||||
|
||||
expect(isAddress(formatted)).to.be.true;
|
||||
expect(formatted).to.equal(address);
|
||||
});
|
||||
});
|
||||
|
||||
['gas', 'gasPrice', 'value', 'nonce'].forEach((input) => {
|
||||
it(`formats ${input} number as hexnumber`, () => {
|
||||
const block = {};
|
||||
block[input] = 0x123;
|
||||
const formatted = inOptions(block)[input];
|
||||
|
||||
expect(formatted).to.equal('0x123');
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores and passes through unknown keys', () => {
|
||||
expect(inOptions({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
|
||||
});
|
||||
|
||||
it('formats an options object with relevant entries converted', () => {
|
||||
expect(
|
||||
inOptions({
|
||||
from: address,
|
||||
to: address,
|
||||
gas: new BigNumber('0x100'),
|
||||
gasPrice: 0x101,
|
||||
value: 258,
|
||||
nonce: '0x104',
|
||||
data: '0123456789',
|
||||
extraData: 'someExtraStuffInHere'
|
||||
})
|
||||
).to.deep.equal({
|
||||
from: address,
|
||||
to: address,
|
||||
gas: '0x100',
|
||||
gasPrice: '0x101',
|
||||
value: '0x102',
|
||||
nonce: '0x104',
|
||||
data: '0x0123456789',
|
||||
extraData: 'someExtraStuffInHere'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
165
js/src/api/format/output.js
Normal file
165
js/src/api/format/output.js
Normal file
@@ -0,0 +1,165 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 BigNumber from 'bignumber.js';
|
||||
|
||||
import { toChecksumAddress } from '../../abi/util/address';
|
||||
|
||||
export function outAccountInfo (infos) {
|
||||
const ret = {};
|
||||
|
||||
Object.keys(infos).forEach((address) => {
|
||||
const info = infos[address];
|
||||
|
||||
ret[outAddress(address)] = {
|
||||
name: info.name,
|
||||
uuid: info.uuid,
|
||||
meta: JSON.parse(info.meta)
|
||||
};
|
||||
});
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
export function outAddress (address) {
|
||||
return toChecksumAddress(address);
|
||||
}
|
||||
|
||||
export function outBlock (block) {
|
||||
if (block) {
|
||||
Object.keys(block).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'author':
|
||||
case 'miner':
|
||||
block[key] = outAddress(block[key]);
|
||||
break;
|
||||
|
||||
case 'difficulty':
|
||||
case 'gasLimit':
|
||||
case 'gasUsed':
|
||||
case 'nonce':
|
||||
case 'number':
|
||||
case 'totalDifficulty':
|
||||
block[key] = outNumber(block[key]);
|
||||
break;
|
||||
|
||||
case 'timestamp':
|
||||
block[key] = outDate(block[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return block;
|
||||
}
|
||||
|
||||
export function outDate (date) {
|
||||
return new Date(outNumber(date).toNumber() * 1000);
|
||||
}
|
||||
|
||||
export function outLog (log) {
|
||||
Object.keys(log).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'blockNumber':
|
||||
case 'logIndex':
|
||||
case 'transactionIndex':
|
||||
log[key] = outNumber(log[key]);
|
||||
break;
|
||||
|
||||
case 'address':
|
||||
log[key] = outAddress(log[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
return log;
|
||||
}
|
||||
|
||||
export function outNumber (number) {
|
||||
return new BigNumber(number || 0);
|
||||
}
|
||||
|
||||
export function outPeers (peers) {
|
||||
return {
|
||||
active: outNumber(peers.active),
|
||||
connected: outNumber(peers.connected),
|
||||
max: outNumber(peers.max)
|
||||
};
|
||||
}
|
||||
|
||||
export function outReceipt (receipt) {
|
||||
if (receipt) {
|
||||
Object.keys(receipt).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'blockNumber':
|
||||
case 'cumulativeGasUsed':
|
||||
case 'gasUsed':
|
||||
case 'transactionIndex':
|
||||
receipt[key] = outNumber(receipt[key]);
|
||||
break;
|
||||
|
||||
case 'contractAddress':
|
||||
receipt[key] = outAddress(receipt[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return receipt;
|
||||
}
|
||||
|
||||
export function outSignerRequest (request) {
|
||||
if (request) {
|
||||
Object.keys(request).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'id':
|
||||
request[key] = outNumber(request[key]);
|
||||
break;
|
||||
|
||||
case 'payload':
|
||||
request[key].transaction = outTransaction(request[key].transaction);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
export function outTransaction (tx) {
|
||||
if (tx) {
|
||||
Object.keys(tx).forEach((key) => {
|
||||
switch (key) {
|
||||
case 'blockNumber':
|
||||
case 'gasPrice':
|
||||
case 'gas':
|
||||
case 'nonce':
|
||||
case 'transactionIndex':
|
||||
case 'value':
|
||||
tx[key] = outNumber(tx[key]);
|
||||
break;
|
||||
|
||||
case 'creates':
|
||||
case 'from':
|
||||
case 'to':
|
||||
tx[key] = outAddress(tx[key]);
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return tx;
|
||||
}
|
||||
247
js/src/api/format/output.spec.js
Normal file
247
js/src/api/format/output.spec.js
Normal file
@@ -0,0 +1,247 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 BigNumber from 'bignumber.js';
|
||||
|
||||
import { outBlock, outAccountInfo, outAddress, outDate, outNumber, outPeers, outReceipt, outTransaction } from './output';
|
||||
import { isAddress, isBigNumber, isInstanceOf } from '../../../test/types';
|
||||
|
||||
describe('api/format/output', () => {
|
||||
const address = '0x63cf90d3f0410092fc0fca41846f596223979195';
|
||||
const checksum = '0x63Cf90D3f0410092FC0fca41846f596223979195';
|
||||
|
||||
describe('outAccountInfo', () => {
|
||||
it('returns meta objects parsed', () => {
|
||||
expect(outAccountInfo(
|
||||
{ '0x63cf90d3f0410092fc0fca41846f596223979195': {
|
||||
name: 'name', uuid: 'uuid', meta: '{"name":"456"}' }
|
||||
}
|
||||
)).to.deep.equal({
|
||||
'0x63Cf90D3f0410092FC0fca41846f596223979195': {
|
||||
name: 'name', uuid: 'uuid', meta: { name: '456' }
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outAddress', () => {
|
||||
it('retuns the address as checksummed', () => {
|
||||
expect(outAddress(address)).to.equal(checksum);
|
||||
});
|
||||
|
||||
it('retuns the checksum as checksummed', () => {
|
||||
expect(outAddress(checksum)).to.equal(checksum);
|
||||
});
|
||||
});
|
||||
|
||||
describe('outBlock', () => {
|
||||
['author', 'miner'].forEach((input) => {
|
||||
it(`formats ${input} address as address`, () => {
|
||||
const block = {};
|
||||
block[input] = address;
|
||||
const formatted = outBlock(block)[input];
|
||||
|
||||
expect(isAddress(formatted)).to.be.true;
|
||||
expect(formatted).to.equal(checksum);
|
||||
});
|
||||
});
|
||||
|
||||
['difficulty', 'gasLimit', 'gasUsed', 'number', 'nonce', 'totalDifficulty'].forEach((input) => {
|
||||
it(`formats ${input} number as hexnumber`, () => {
|
||||
const block = {};
|
||||
block[input] = 0x123;
|
||||
const formatted = outBlock(block)[input];
|
||||
|
||||
expect(isInstanceOf(formatted, BigNumber)).to.be.true;
|
||||
expect(formatted.toString(16)).to.equal('123');
|
||||
});
|
||||
});
|
||||
|
||||
['timestamp'].forEach((input) => {
|
||||
it(`formats ${input} number as Date`, () => {
|
||||
const block = {};
|
||||
block[input] = 0x57513668;
|
||||
const formatted = outBlock(block)[input];
|
||||
|
||||
expect(isInstanceOf(formatted, Date)).to.be.true;
|
||||
expect(formatted.getTime()).to.equal(1464940136000);
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores and passes through unknown keys', () => {
|
||||
expect(outBlock({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
|
||||
});
|
||||
|
||||
it('formats a block with all the info converted', () => {
|
||||
expect(
|
||||
outBlock({
|
||||
author: address,
|
||||
miner: address,
|
||||
difficulty: '0x100',
|
||||
gasLimit: '0x101',
|
||||
gasUsed: '0x102',
|
||||
number: '0x103',
|
||||
nonce: '0x104',
|
||||
totalDifficulty: '0x105',
|
||||
timestamp: '0x57513668',
|
||||
extraData: 'someExtraStuffInHere'
|
||||
})
|
||||
).to.deep.equal({
|
||||
author: checksum,
|
||||
miner: checksum,
|
||||
difficulty: new BigNumber('0x100'),
|
||||
gasLimit: new BigNumber('0x101'),
|
||||
gasUsed: new BigNumber('0x102'),
|
||||
number: new BigNumber('0x103'),
|
||||
nonce: new BigNumber('0x104'),
|
||||
totalDifficulty: new BigNumber('0x105'),
|
||||
timestamp: new Date('2016-06-03T07:48:56.000Z'),
|
||||
extraData: 'someExtraStuffInHere'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outDate', () => {
|
||||
it('converts a second date in unix timestamp', () => {
|
||||
expect(outDate(0x57513668)).to.deep.equal(new Date('2016-06-03T07:48:56.000Z'));
|
||||
});
|
||||
});
|
||||
|
||||
describe('outNumber', () => {
|
||||
it('returns a BigNumber equalling the value', () => {
|
||||
const bn = outNumber('0x123456');
|
||||
|
||||
expect(isBigNumber(bn)).to.be.true;
|
||||
expect(bn.eq(0x123456)).to.be.true;
|
||||
});
|
||||
|
||||
it('assumes 0 when ivalid input', () => {
|
||||
expect(outNumber().eq(0)).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('outPeers', () => {
|
||||
it('converts all internal numbers to BigNumbers', () => {
|
||||
expect(outPeers({ active: 789, connected: '456', max: 0x7b })).to.deep.equal({
|
||||
active: new BigNumber(789),
|
||||
connected: new BigNumber(456),
|
||||
max: new BigNumber(123)
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outReceipt', () => {
|
||||
['contractAddress'].forEach((input) => {
|
||||
it(`formats ${input} address as address`, () => {
|
||||
const block = {};
|
||||
block[input] = address;
|
||||
const formatted = outReceipt(block)[input];
|
||||
|
||||
expect(isAddress(formatted)).to.be.true;
|
||||
expect(formatted).to.equal(checksum);
|
||||
});
|
||||
});
|
||||
|
||||
['blockNumber', 'cumulativeGasUsed', 'cumulativeGasUsed', 'gasUsed', 'transactionIndex'].forEach((input) => {
|
||||
it(`formats ${input} number as hexnumber`, () => {
|
||||
const block = {};
|
||||
block[input] = 0x123;
|
||||
const formatted = outReceipt(block)[input];
|
||||
|
||||
expect(isInstanceOf(formatted, BigNumber)).to.be.true;
|
||||
expect(formatted.toString(16)).to.equal('123');
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores and passes through unknown keys', () => {
|
||||
expect(outReceipt({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
|
||||
});
|
||||
|
||||
it('formats a receipt with all the info converted', () => {
|
||||
expect(
|
||||
outReceipt({
|
||||
contractAddress: address,
|
||||
blockNumber: '0x100',
|
||||
cumulativeGasUsed: '0x101',
|
||||
gasUsed: '0x102',
|
||||
transactionIndex: '0x103',
|
||||
extraData: 'someExtraStuffInHere'
|
||||
})
|
||||
).to.deep.equal({
|
||||
contractAddress: checksum,
|
||||
blockNumber: new BigNumber('0x100'),
|
||||
cumulativeGasUsed: new BigNumber('0x101'),
|
||||
gasUsed: new BigNumber('0x102'),
|
||||
transactionIndex: new BigNumber('0x103'),
|
||||
extraData: 'someExtraStuffInHere'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('outTransaction', () => {
|
||||
['from', 'to'].forEach((input) => {
|
||||
it(`formats ${input} address as address`, () => {
|
||||
const block = {};
|
||||
block[input] = address;
|
||||
const formatted = outTransaction(block)[input];
|
||||
|
||||
expect(isAddress(formatted)).to.be.true;
|
||||
expect(formatted).to.equal(checksum);
|
||||
});
|
||||
});
|
||||
|
||||
['blockNumber', 'gasPrice', 'gas', 'nonce', 'transactionIndex', 'value'].forEach((input) => {
|
||||
it(`formats ${input} number as hexnumber`, () => {
|
||||
const block = {};
|
||||
block[input] = 0x123;
|
||||
const formatted = outTransaction(block)[input];
|
||||
|
||||
expect(isInstanceOf(formatted, BigNumber)).to.be.true;
|
||||
expect(formatted.toString(16)).to.equal('123');
|
||||
});
|
||||
});
|
||||
|
||||
it('ignores and passes through unknown keys', () => {
|
||||
expect(outTransaction({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
|
||||
});
|
||||
|
||||
it('formats a transaction with all the info converted', () => {
|
||||
expect(
|
||||
outTransaction({
|
||||
from: address,
|
||||
to: address,
|
||||
blockNumber: '0x100',
|
||||
gasPrice: '0x101',
|
||||
gas: '0x102',
|
||||
nonce: '0x103',
|
||||
transactionIndex: '0x104',
|
||||
value: '0x105',
|
||||
extraData: 'someExtraStuffInHere'
|
||||
})
|
||||
).to.deep.equal({
|
||||
from: checksum,
|
||||
to: checksum,
|
||||
blockNumber: new BigNumber('0x100'),
|
||||
gasPrice: new BigNumber('0x101'),
|
||||
gas: new BigNumber('0x102'),
|
||||
nonce: new BigNumber('0x103'),
|
||||
transactionIndex: new BigNumber('0x104'),
|
||||
value: new BigNumber('0x105'),
|
||||
extraData: 'someExtraStuffInHere'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/api/index.js
Normal file
17
js/src/api/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './api';
|
||||
43
js/src/api/rpc/db/db.js
Normal file
43
js/src/api/rpc/db/db.js
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { inHex } from '../../format/input';
|
||||
|
||||
export default class Db {
|
||||
constructor (transport) {
|
||||
this._transport = transport;
|
||||
}
|
||||
|
||||
getHex (dbName, keyName) {
|
||||
return this._transport
|
||||
.execute('db_getHex', dbName, keyName);
|
||||
}
|
||||
|
||||
getString (dbName, keyName) {
|
||||
return this._transport
|
||||
.execute('db_getString', dbName, keyName);
|
||||
}
|
||||
|
||||
putHex (dbName, keyName, hexData) {
|
||||
return this._transport
|
||||
.execute('db_putHex', dbName, keyName, inHex(hexData));
|
||||
}
|
||||
|
||||
putString (dbName, keyName, stringData) {
|
||||
return this._transport
|
||||
.execute('db_putString', dbName, keyName, stringData);
|
||||
}
|
||||
}
|
||||
38
js/src/api/rpc/db/db.spec.js
Normal file
38
js/src/api/rpc/db/db.spec.js
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { TEST_HTTP_URL, mockHttp } from '../../../../test/mockRpc';
|
||||
|
||||
import Http from '../../transport/http';
|
||||
import Db from './db';
|
||||
|
||||
const instance = new Db(new Http(TEST_HTTP_URL));
|
||||
|
||||
describe('api/rpc/Db', () => {
|
||||
let scope;
|
||||
|
||||
describe('putHex', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'db_putHex', reply: { result: [] } }]);
|
||||
});
|
||||
|
||||
it('formats the inputs correctly', () => {
|
||||
return instance.putHex('db', 'key', '1234').then(() => {
|
||||
expect(scope.body.db_putHex.params).to.deep.equal(['db', 'key', '0x1234']);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/api/rpc/db/index.js
Normal file
17
js/src/api/rpc/db/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './db';
|
||||
170
js/src/api/rpc/eth/eth.e2e.js
Normal file
170
js/src/api/rpc/eth/eth.e2e.js
Normal file
@@ -0,0 +1,170 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { createHttpApi } from '../../../../test/e2e/ethapi';
|
||||
import { isAddress } from '../../../../test/types';
|
||||
|
||||
describe('ethapi.eth', () => {
|
||||
const ethapi = createHttpApi();
|
||||
const address = '0x63cf90d3f0410092fc0fca41846f596223979195';
|
||||
|
||||
let latestBlockNumber;
|
||||
let latestBlockHash;
|
||||
|
||||
describe('accounts', () => {
|
||||
it('returns the available accounts', () => {
|
||||
return ethapi.eth.accounts().then((accounts) => {
|
||||
accounts.forEach((account) => {
|
||||
expect(isAddress(account)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('blockNumber', () => {
|
||||
it('returns the current blockNumber', () => {
|
||||
return ethapi.eth.blockNumber().then((blockNumber) => {
|
||||
latestBlockNumber = blockNumber;
|
||||
expect(blockNumber.gt(0xabcde)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('coinbase', () => {
|
||||
it('returns the coinbase', () => {
|
||||
return ethapi.eth.coinbase().then((coinbase) => {
|
||||
expect(isAddress(coinbase)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('gasPrice', () => {
|
||||
it('returns the current gasPrice', () => {
|
||||
return ethapi.eth.gasPrice().then((gasPrice) => {
|
||||
expect(gasPrice.gt(0)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBalance', () => {
|
||||
it('returns the balance for latest block', () => {
|
||||
return ethapi.eth.getBalance(address).then((balance) => {
|
||||
expect(balance.gt(0)).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the balance for a very early block', () => {
|
||||
const atBlock = '0x65432';
|
||||
const atValue = '18e07120a6e164fee1b';
|
||||
|
||||
return ethapi.eth
|
||||
.getBalance(address, atBlock)
|
||||
.then((balance) => {
|
||||
expect(balance.toString(16)).to.equal(atValue);
|
||||
})
|
||||
.catch((error) => {
|
||||
// Parity doesn't support pruned-before-block balance lookups
|
||||
expect(error.message).to.match(/not supported/);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the balance for a recent/out-of-pruning-range block', () => {
|
||||
return ethapi.eth
|
||||
.getBalance(address, latestBlockNumber.minus(1000))
|
||||
.then((balance) => {
|
||||
expect(balance.gt(0)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBlockByNumber', () => {
|
||||
it('returns the latest block', () => {
|
||||
return ethapi.eth.getBlockByNumber().then((block) => {
|
||||
expect(block).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a block by blockNumber', () => {
|
||||
return ethapi.eth.getBlockByNumber(latestBlockNumber).then((block) => {
|
||||
latestBlockHash = block.hash;
|
||||
expect(block).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a block by blockNumber (full)', () => {
|
||||
return ethapi.eth.getBlockByNumber(latestBlockNumber, true).then((block) => {
|
||||
expect(block).to.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBlockByHash', () => {
|
||||
it('returns the specified block', () => {
|
||||
return ethapi.eth.getBlockByHash(latestBlockHash).then((block) => {
|
||||
expect(block).to.be.ok;
|
||||
expect(block.hash).to.equal(latestBlockHash);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the specified block (full)', () => {
|
||||
return ethapi.eth.getBlockByHash(latestBlockHash, true).then((block) => {
|
||||
expect(block).to.be.ok;
|
||||
expect(block.hash).to.equal(latestBlockHash);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBlockTransactionCountByHash', () => {
|
||||
it('returns the transactions of the specified hash', () => {
|
||||
return ethapi.eth.getBlockTransactionCountByHash(latestBlockHash).then((count) => {
|
||||
expect(count).to.be.ok;
|
||||
expect(count.gte(0)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBlockTransactionCountByNumber', () => {
|
||||
it('returns the transactions of latest', () => {
|
||||
return ethapi.eth.getBlockTransactionCountByNumber().then((count) => {
|
||||
expect(count).to.be.ok;
|
||||
expect(count.gte(0)).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the transactions of a specified number', () => {
|
||||
return ethapi.eth.getBlockTransactionCountByNumber(latestBlockNumber).then((count) => {
|
||||
expect(count).to.be.ok;
|
||||
expect(count.gte(0)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTransactionCount', () => {
|
||||
it('returns the count for an address', () => {
|
||||
return ethapi.eth.getTransactionCount(address).then((count) => {
|
||||
expect(count).to.be.ok;
|
||||
expect(count.gte(0x1000c2)).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the count for an address at specified blockNumber', () => {
|
||||
return ethapi.eth.getTransactionCount(address, latestBlockNumber).then((count) => {
|
||||
expect(count).to.be.ok;
|
||||
expect(count.gte(0x1000c2)).to.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
329
js/src/api/rpc/eth/eth.js
Normal file
329
js/src/api/rpc/eth/eth.js
Normal file
@@ -0,0 +1,329 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { inAddress, inBlockNumber, inData, inFilter, inHex, inNumber16, inOptions } from '../../format/input';
|
||||
import { outAddress, outBlock, outLog, outNumber, outReceipt, outTransaction } from '../../format/output';
|
||||
|
||||
export default class Eth {
|
||||
constructor (transport) {
|
||||
this._transport = transport;
|
||||
}
|
||||
|
||||
accounts () {
|
||||
return this._transport
|
||||
.execute('eth_accounts')
|
||||
.then((accounts) => (accounts || []).map(outAddress));
|
||||
}
|
||||
|
||||
blockNumber () {
|
||||
return this._transport
|
||||
.execute('eth_blockNumber')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
call (options, blockNumber = 'latest') {
|
||||
return this._transport
|
||||
.execute('eth_call', inOptions(options), inBlockNumber(blockNumber));
|
||||
}
|
||||
|
||||
checkRequest (requestId) {
|
||||
return this._transport
|
||||
.execute('eth_checkRequest', inNumber16(requestId));
|
||||
}
|
||||
|
||||
coinbase () {
|
||||
return this._transport
|
||||
.execute('eth_coinbase')
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
compileLLL (code) {
|
||||
return this._transport
|
||||
.execute('eth_compileLLL', inData(code));
|
||||
}
|
||||
|
||||
compileSerpent (code) {
|
||||
return this._transport
|
||||
.execute('eth_compileSerpent', inData(code));
|
||||
}
|
||||
|
||||
compileSolidity (code) {
|
||||
return this._transport
|
||||
.execute('eth_compileSolidity', inData(code));
|
||||
}
|
||||
|
||||
estimateGas (options) {
|
||||
return this._transport
|
||||
.execute('eth_estimateGas', inOptions(options))
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
fetchQueuedTransactions () {
|
||||
return this._transport
|
||||
.execute('eth_fetchQueuedTransactions');
|
||||
}
|
||||
|
||||
flush () {
|
||||
return this._transport
|
||||
.execute('eth_flush');
|
||||
}
|
||||
|
||||
gasPrice () {
|
||||
return this._transport
|
||||
.execute('eth_gasPrice')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
getBalance (address, blockNumber = 'latest') {
|
||||
return this._transport
|
||||
.execute('eth_getBalance', inAddress(address), inBlockNumber(blockNumber))
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
getBlockByHash (hash, full = false) {
|
||||
return this._transport
|
||||
.execute('eth_getBlockByHash', inHex(hash), full)
|
||||
.then(outBlock);
|
||||
}
|
||||
|
||||
getBlockByNumber (blockNumber = 'latest', full = false) {
|
||||
return this._transport
|
||||
.execute('eth_getBlockByNumber', inBlockNumber(blockNumber), full)
|
||||
.then(outBlock);
|
||||
}
|
||||
|
||||
getBlockTransactionCountByHash (hash) {
|
||||
return this._transport
|
||||
.execute('eth_getBlockTransactionCountByHash', inHex(hash))
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
getBlockTransactionCountByNumber (blockNumber = 'latest') {
|
||||
return this._transport
|
||||
.execute('eth_getBlockTransactionCountByNumber', inBlockNumber(blockNumber))
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
getCode (address, blockNumber = 'latest') {
|
||||
return this._transport
|
||||
.execute('eth_getCode', inAddress(address), inBlockNumber(blockNumber));
|
||||
}
|
||||
|
||||
getCompilers () {
|
||||
return this._transport
|
||||
.execute('eth_getCompilers');
|
||||
}
|
||||
|
||||
getFilterChanges (filterId) {
|
||||
return this._transport
|
||||
.execute('eth_getFilterChanges', inNumber16(filterId))
|
||||
.then((logs) => logs.map(outLog));
|
||||
}
|
||||
|
||||
getFilterChangesEx (filterId) {
|
||||
return this._transport
|
||||
.execute('eth_getFilterChangesEx', inNumber16(filterId));
|
||||
}
|
||||
|
||||
getFilterLogs (filterId) {
|
||||
return this._transport
|
||||
.execute('eth_getFilterLogs', inNumber16(filterId))
|
||||
.then((logs) => logs.map(outLog));
|
||||
}
|
||||
|
||||
getFilterLogsEx (filterId) {
|
||||
return this._transport
|
||||
.execute('eth_getFilterLogsEx', inNumber16(filterId));
|
||||
}
|
||||
|
||||
getLogs (options) {
|
||||
return this._transport
|
||||
.execute('eth_getLogs', inFilter(options));
|
||||
}
|
||||
|
||||
getLogsEx (options) {
|
||||
return this._transport
|
||||
.execute('eth_getLogsEx', inFilter(options));
|
||||
}
|
||||
|
||||
getStorageAt (address, index = 0, blockNumber = 'latest') {
|
||||
return this._transport
|
||||
.execute('eth_getStorageAt', inAddress(address), inNumber16(index), inBlockNumber(blockNumber));
|
||||
}
|
||||
|
||||
getTransactionByBlockHashAndIndex (hash, index = 0) {
|
||||
return this._transport
|
||||
.execute('eth_getTransactionByBlockHashAndIndex', inHex(hash), inNumber16(index))
|
||||
.then(outTransaction);
|
||||
}
|
||||
|
||||
getTransactionByBlockNumberAndIndex (blockNumber = 'latest', index = 0) {
|
||||
return this._transport
|
||||
.execute('eth_getTransactionByBlockNumberAndIndex', inBlockNumber(blockNumber), inNumber16(index))
|
||||
.then(outTransaction);
|
||||
}
|
||||
|
||||
getTransactionByHash (hash) {
|
||||
return this._transport
|
||||
.execute('eth_getTransactionByHash', inHex(hash))
|
||||
.then(outTransaction);
|
||||
}
|
||||
|
||||
getTransactionCount (address, blockNumber = 'latest') {
|
||||
return this._transport
|
||||
.execute('eth_getTransactionCount', inAddress(address), inBlockNumber(blockNumber))
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
getTransactionReceipt (txhash) {
|
||||
return this._transport
|
||||
.execute('eth_getTransactionReceipt', inHex(txhash))
|
||||
.then(outReceipt);
|
||||
}
|
||||
|
||||
getUncleByBlockHashAndIndex (hash, index = 0) {
|
||||
return this._transport
|
||||
.execute('eth_getUncleByBlockHashAndIndex', inHex(hash), inNumber16(index));
|
||||
}
|
||||
|
||||
getUncleByBlockNumberAndIndex (blockNumber = 'latest', index = 0) {
|
||||
return this._transport
|
||||
.execute('eth_getUncleByBlockNumberAndIndex', inBlockNumber(blockNumber), inNumber16(index));
|
||||
}
|
||||
|
||||
getUncleCountByBlockHash (hash) {
|
||||
return this._transport
|
||||
.execute('eth_getUncleCountByBlockHash', inHex(hash))
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
getUncleCountByBlockNumber (blockNumber = 'latest') {
|
||||
return this._transport
|
||||
.execute('eth_getUncleCountByBlockNumber', inBlockNumber(blockNumber))
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
getWork () {
|
||||
return this._transport
|
||||
.execute('eth_getWork');
|
||||
}
|
||||
|
||||
hashrate () {
|
||||
return this._transport
|
||||
.execute('eth_hashrate')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
inspectTransaction () {
|
||||
return this._transport
|
||||
.execute('eth_inspectTransaction');
|
||||
}
|
||||
|
||||
mining () {
|
||||
return this._transport
|
||||
.execute('eth_mining');
|
||||
}
|
||||
|
||||
newBlockFilter () {
|
||||
return this._transport
|
||||
.execute('eth_newBlockFilter');
|
||||
}
|
||||
|
||||
newFilter (options) {
|
||||
return this._transport
|
||||
.execute('eth_newFilter', inFilter(options));
|
||||
}
|
||||
|
||||
newFilterEx (options) {
|
||||
return this._transport
|
||||
.execute('eth_newFilterEx', inFilter(options));
|
||||
}
|
||||
|
||||
newPendingTransactionFilter () {
|
||||
return this._transport
|
||||
.execute('eth_newPendingTransactionFilter');
|
||||
}
|
||||
|
||||
notePassword () {
|
||||
return this._transport
|
||||
.execute('eth_notePassword');
|
||||
}
|
||||
|
||||
pendingTransactions () {
|
||||
return this._transport
|
||||
.execute('eth_pendingTransactions');
|
||||
}
|
||||
|
||||
postTransaction (options) {
|
||||
return this._transport
|
||||
.execute('eth_postTransaction', inOptions(options));
|
||||
}
|
||||
|
||||
protocolVersion () {
|
||||
return this._transport
|
||||
.execute('eth_protocolVersion');
|
||||
}
|
||||
|
||||
register () {
|
||||
return this._transport
|
||||
.execute('eth_register');
|
||||
}
|
||||
|
||||
sendRawTransaction (data) {
|
||||
return this._transport
|
||||
.execute('eth_sendRawTransaction', inData(data));
|
||||
}
|
||||
|
||||
sendTransaction (options) {
|
||||
return this._transport
|
||||
.execute('eth_sendTransaction', inOptions(options));
|
||||
}
|
||||
|
||||
sign () {
|
||||
return this._transport
|
||||
.execute('eth_sign');
|
||||
}
|
||||
|
||||
signTransaction () {
|
||||
return this._transport
|
||||
.execute('eth_signTransaction');
|
||||
}
|
||||
|
||||
submitHashrate (hashrate, clientId) {
|
||||
return this._transport
|
||||
.execute('eth_submitHashrate', inNumber16(hashrate), clientId);
|
||||
}
|
||||
|
||||
submitWork (nonce, powHash, mixDigest) {
|
||||
return this._transport
|
||||
.execute('eth_submitWork', inNumber16(nonce), powHash, mixDigest);
|
||||
}
|
||||
|
||||
syncing () {
|
||||
return this._transport
|
||||
.execute('eth_syncing');
|
||||
}
|
||||
|
||||
uninstallFilter (filterId) {
|
||||
return this._transport
|
||||
.execute('eth_uninstallFilter', inHex(filterId));
|
||||
}
|
||||
|
||||
unregister () {
|
||||
return this._transport
|
||||
.execute('eth_unregister');
|
||||
}
|
||||
}
|
||||
474
js/src/api/rpc/eth/eth.spec.js
Normal file
474
js/src/api/rpc/eth/eth.spec.js
Normal file
@@ -0,0 +1,474 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { TEST_HTTP_URL, mockHttp } from '../../../../test/mockRpc';
|
||||
import { isBigNumber } from '../../../../test/types';
|
||||
|
||||
import Http from '../../transport/http';
|
||||
import Eth from './eth';
|
||||
|
||||
const instance = new Eth(new Http(TEST_HTTP_URL));
|
||||
|
||||
describe('rpc/Eth', () => {
|
||||
const address = '0x63Cf90D3f0410092FC0fca41846f596223979195';
|
||||
let scope;
|
||||
|
||||
describe('accounts', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_accounts', reply: { result: [address.toLowerCase()] } }]);
|
||||
});
|
||||
|
||||
it('returns a list of accounts, formatted', () => {
|
||||
return instance.accounts().then((accounts) => {
|
||||
expect(accounts).to.deep.equal([address]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('blockNumber', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_blockNumber', reply: { result: '0x123456' } }]);
|
||||
});
|
||||
|
||||
it('returns the current blockNumber, formatted', () => {
|
||||
return instance.blockNumber().then((blockNumber) => {
|
||||
expect(isBigNumber(blockNumber)).to.be.true;
|
||||
expect(blockNumber.toString(16)).to.equal('123456');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('call', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_call', reply: { result: [] } }]);
|
||||
});
|
||||
|
||||
it('formats the input options & blockNumber', () => {
|
||||
return instance.call({ data: '12345678' }, 'earliest').then(() => {
|
||||
expect(scope.body.eth_call.params).to.deep.equal([{ data: '0x12345678' }, 'earliest']);
|
||||
});
|
||||
});
|
||||
|
||||
it('provides a latest blockNumber when not specified', () => {
|
||||
return instance.call({ data: '12345678' }).then(() => {
|
||||
expect(scope.body.eth_call.params).to.deep.equal([{ data: '0x12345678' }, 'latest']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('coinbase', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_coinbase', reply: { result: address.toLowerCase() } }]);
|
||||
});
|
||||
|
||||
it('returns the coinbase, formatted', () => {
|
||||
return instance.coinbase().then((account) => {
|
||||
expect(account).to.deep.equal(address);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
['LLL', 'Serpent', 'Solidity'].forEach((type) => {
|
||||
const method = `compile${type}`;
|
||||
|
||||
describe(method, () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: `eth_${method}`, reply: { result: '0x123' } }]);
|
||||
});
|
||||
|
||||
it('formats the input as data, returns the output', () => {
|
||||
return instance[method]('0xabcdef').then((result) => {
|
||||
expect(scope.body[`eth_${method}`].params).to.deep.equal(['0xabcdef']);
|
||||
expect(result).to.equal('0x123');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('estimateGas', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_estimateGas', reply: { result: '0x123' } }]);
|
||||
});
|
||||
|
||||
it('converts the options correctly', () => {
|
||||
return instance.estimateGas({ gas: 21000 }).then(() => {
|
||||
expect(scope.body.eth_estimateGas.params).to.deep.equal([{ gas: '0x5208' }]);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the gas used', () => {
|
||||
return instance.estimateGas({}).then((gas) => {
|
||||
expect(isBigNumber(gas)).to.be.true;
|
||||
expect(gas.toString(16)).to.deep.equal('123');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('gasPrice', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_gasPrice', reply: { result: '0x123' } }]);
|
||||
});
|
||||
|
||||
it('returns the fomratted price', () => {
|
||||
return instance.gasPrice().then((price) => {
|
||||
expect(isBigNumber(price)).to.be.true;
|
||||
expect(price.toString(16)).to.deep.equal('123');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBalance', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_getBalance', reply: { result: '0x123' } }]);
|
||||
});
|
||||
|
||||
it('passes in the address (default blockNumber)', () => {
|
||||
return instance.getBalance(address).then(() => {
|
||||
expect(scope.body.eth_getBalance.params).to.deep.equal([address.toLowerCase(), 'latest']);
|
||||
});
|
||||
});
|
||||
|
||||
it('passes in the address & blockNumber', () => {
|
||||
return instance.getBalance(address, 0x456).then(() => {
|
||||
expect(scope.body.eth_getBalance.params).to.deep.equal([address.toLowerCase(), '0x456']);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the balance', () => {
|
||||
return instance.getBalance(address, 0x123).then((balance) => {
|
||||
expect(isBigNumber(balance)).to.be.true;
|
||||
expect(balance.toString(16)).to.deep.equal('123');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBlockByHash', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_getBlockByHash', reply: { result: { miner: address.toLowerCase() } } }]);
|
||||
});
|
||||
|
||||
it('formats the input hash as a hash, default full', () => {
|
||||
return instance.getBlockByHash('1234').then(() => {
|
||||
expect(scope.body.eth_getBlockByHash.params).to.deep.equal(['0x1234', false]);
|
||||
});
|
||||
});
|
||||
|
||||
it('formats the input hash as a hash, full true', () => {
|
||||
return instance.getBlockByHash('1234', true).then(() => {
|
||||
expect(scope.body.eth_getBlockByHash.params).to.deep.equal(['0x1234', true]);
|
||||
});
|
||||
});
|
||||
|
||||
it('formats the output into block', () => {
|
||||
return instance.getBlockByHash('1234').then((block) => {
|
||||
expect(block.miner).to.equal(address);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBlockByNumber', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_getBlockByNumber', reply: { result: { miner: address.toLowerCase() } } }]);
|
||||
});
|
||||
|
||||
it('assumes blockNumber latest & full false', () => {
|
||||
return instance.getBlockByNumber().then(() => {
|
||||
expect(scope.body.eth_getBlockByNumber.params).to.deep.equal(['latest', false]);
|
||||
});
|
||||
});
|
||||
|
||||
it('uses input blockNumber & full false', () => {
|
||||
return instance.getBlockByNumber('0x1234').then(() => {
|
||||
expect(scope.body.eth_getBlockByNumber.params).to.deep.equal(['0x1234', false]);
|
||||
});
|
||||
});
|
||||
|
||||
it('formats the input blockNumber, full true', () => {
|
||||
return instance.getBlockByNumber(0x1234, true).then(() => {
|
||||
expect(scope.body.eth_getBlockByNumber.params).to.deep.equal(['0x1234', true]);
|
||||
});
|
||||
});
|
||||
|
||||
it('formats the output into block', () => {
|
||||
return instance.getBlockByNumber(0x1234).then((block) => {
|
||||
expect(block.miner).to.equal(address);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBlockTransactionCountByHash', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_getBlockTransactionCountByHash', reply: { result: '0x123' } }]);
|
||||
});
|
||||
|
||||
it('formats input hash properly', () => {
|
||||
return instance.getBlockTransactionCountByHash('abcdef').then(() => {
|
||||
expect(scope.body.eth_getBlockTransactionCountByHash.params).to.deep.equal(['0xabcdef']);
|
||||
});
|
||||
});
|
||||
|
||||
it('formats the output number', () => {
|
||||
return instance.getBlockTransactionCountByHash('0x1234').then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
expect(count.toString(16)).to.equal('123');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBlockTransactionCountByNumber', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_getBlockTransactionCountByNumber', reply: { result: '0x123' } }]);
|
||||
});
|
||||
|
||||
it('specified blockNumber latest when none specified', () => {
|
||||
return instance.getBlockTransactionCountByNumber().then(() => {
|
||||
expect(scope.body.eth_getBlockTransactionCountByNumber.params).to.deep.equal(['latest']);
|
||||
});
|
||||
});
|
||||
|
||||
it('formats input blockNumber properly', () => {
|
||||
return instance.getBlockTransactionCountByNumber(0xabcdef).then(() => {
|
||||
expect(scope.body.eth_getBlockTransactionCountByNumber.params).to.deep.equal(['0xabcdef']);
|
||||
});
|
||||
});
|
||||
|
||||
it('formats the output number', () => {
|
||||
return instance.getBlockTransactionCountByNumber('0x1234').then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
expect(count.toString(16)).to.equal('123');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCode', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_getCode', reply: { result: '0x1234567890' } }]);
|
||||
});
|
||||
|
||||
it('passes in the address (default blockNumber)', () => {
|
||||
return instance.getCode(address).then(() => {
|
||||
expect(scope.body.eth_getCode.params).to.deep.equal([address.toLowerCase(), 'latest']);
|
||||
});
|
||||
});
|
||||
|
||||
it('passes in the address & blockNumber', () => {
|
||||
return instance.getCode(address, 0x456).then(() => {
|
||||
expect(scope.body.eth_getCode.params).to.deep.equal([address.toLowerCase(), '0x456']);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the code', () => {
|
||||
return instance.getCode(address, 0x123).then((code) => {
|
||||
expect(code).to.equal('0x1234567890');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getStorageAt', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_getStorageAt', reply: { result: '0x1234567890' } }]);
|
||||
});
|
||||
|
||||
it('passes in the address (default index& blockNumber)', () => {
|
||||
return instance.getStorageAt(address).then(() => {
|
||||
expect(scope.body.eth_getStorageAt.params).to.deep.equal([address.toLowerCase(), '0x0', 'latest']);
|
||||
});
|
||||
});
|
||||
|
||||
it('passes in the address, index & blockNumber', () => {
|
||||
return instance.getStorageAt(address, 15, 0x456).then(() => {
|
||||
expect(scope.body.eth_getStorageAt.params).to.deep.equal([address.toLowerCase(), '0xf', '0x456']);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the storage', () => {
|
||||
return instance.getStorageAt(address, 0x123).then((storage) => {
|
||||
expect(storage).to.equal('0x1234567890');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTransactionByBlockHashAndIndex', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_getTransactionByBlockHashAndIndex', reply: { result: { to: address.toLowerCase() } } }]);
|
||||
});
|
||||
|
||||
it('passes in the hash (default index)', () => {
|
||||
return instance.getTransactionByBlockHashAndIndex('12345').then(() => {
|
||||
expect(scope.body.eth_getTransactionByBlockHashAndIndex.params).to.deep.equal(['0x12345', '0x0']);
|
||||
});
|
||||
});
|
||||
|
||||
it('passes in the hash & specified index', () => {
|
||||
return instance.getTransactionByBlockHashAndIndex('6789', 0x456).then(() => {
|
||||
expect(scope.body.eth_getTransactionByBlockHashAndIndex.params).to.deep.equal(['0x6789', '0x456']);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the formatted transaction', () => {
|
||||
return instance.getTransactionByBlockHashAndIndex('6789', 0x123).then((tx) => {
|
||||
expect(tx).to.deep.equal({ to: address });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTransactionByBlockNumberAndIndex', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_getTransactionByBlockNumberAndIndex', reply: { result: { to: address.toLowerCase() } } }]);
|
||||
});
|
||||
|
||||
it('passes in the default parameters', () => {
|
||||
return instance.getTransactionByBlockNumberAndIndex().then(() => {
|
||||
expect(scope.body.eth_getTransactionByBlockNumberAndIndex.params).to.deep.equal(['latest', '0x0']);
|
||||
});
|
||||
});
|
||||
|
||||
it('passes in the blockNumber & specified index', () => {
|
||||
return instance.getTransactionByBlockNumberAndIndex('0x6789', 0x456).then(() => {
|
||||
expect(scope.body.eth_getTransactionByBlockNumberAndIndex.params).to.deep.equal(['0x6789', '0x456']);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the formatted transaction', () => {
|
||||
return instance.getTransactionByBlockNumberAndIndex('0x6789', 0x123).then((tx) => {
|
||||
expect(tx).to.deep.equal({ to: address });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTransactionByHash', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_getTransactionByHash', reply: { result: { to: address.toLowerCase() } } }]);
|
||||
});
|
||||
|
||||
it('passes in the hash', () => {
|
||||
return instance.getTransactionByHash('12345').then(() => {
|
||||
expect(scope.body.eth_getTransactionByHash.params).to.deep.equal(['0x12345']);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the formatted transaction', () => {
|
||||
return instance.getTransactionByHash('6789').then((tx) => {
|
||||
expect(tx).to.deep.equal({ to: address });
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTransactionCount', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_getTransactionCount', reply: { result: '0x123' } }]);
|
||||
});
|
||||
|
||||
it('passes in the address (default blockNumber)', () => {
|
||||
return instance.getTransactionCount(address).then(() => {
|
||||
expect(scope.body.eth_getTransactionCount.params).to.deep.equal([address.toLowerCase(), 'latest']);
|
||||
});
|
||||
});
|
||||
|
||||
it('passes in the address & blockNumber', () => {
|
||||
return instance.getTransactionCount(address, 0x456).then(() => {
|
||||
expect(scope.body.eth_getTransactionCount.params).to.deep.equal([address.toLowerCase(), '0x456']);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns the count, formatted', () => {
|
||||
return instance.getTransactionCount(address, 0x123).then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
expect(count.toString(16)).to.equal('123');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getUncleByBlockHashAndIndex', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_getUncleByBlockHashAndIndex', reply: { result: [] } }]);
|
||||
});
|
||||
|
||||
it('passes in the hash (default index)', () => {
|
||||
return instance.getUncleByBlockHashAndIndex('12345').then(() => {
|
||||
expect(scope.body.eth_getUncleByBlockHashAndIndex.params).to.deep.equal(['0x12345', '0x0']);
|
||||
});
|
||||
});
|
||||
|
||||
it('passes in the hash & specified index', () => {
|
||||
return instance.getUncleByBlockHashAndIndex('6789', 0x456).then(() => {
|
||||
expect(scope.body.eth_getUncleByBlockHashAndIndex.params).to.deep.equal(['0x6789', '0x456']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getUncleByBlockNumberAndIndex', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_getUncleByBlockNumberAndIndex', reply: { result: [] } }]);
|
||||
});
|
||||
|
||||
it('passes in the default parameters', () => {
|
||||
return instance.getUncleByBlockNumberAndIndex().then(() => {
|
||||
expect(scope.body.eth_getUncleByBlockNumberAndIndex.params).to.deep.equal(['latest', '0x0']);
|
||||
});
|
||||
});
|
||||
|
||||
it('passes in the blockNumber & specified index', () => {
|
||||
return instance.getUncleByBlockNumberAndIndex('0x6789', 0x456).then(() => {
|
||||
expect(scope.body.eth_getUncleByBlockNumberAndIndex.params).to.deep.equal(['0x6789', '0x456']);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getUncleCountByBlockHash', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_getUncleCountByBlockHash', reply: { result: '0x123' } }]);
|
||||
});
|
||||
|
||||
it('passes in the hash', () => {
|
||||
return instance.getUncleCountByBlockHash('12345').then(() => {
|
||||
expect(scope.body.eth_getUncleCountByBlockHash.params).to.deep.equal(['0x12345']);
|
||||
});
|
||||
});
|
||||
|
||||
it('formats the output number', () => {
|
||||
return instance.getUncleCountByBlockHash('0x1234').then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
expect(count.toString(16)).to.equal('123');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getUncleCountByBlockNumber', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_getUncleCountByBlockNumber', reply: { result: '0x123' } }]);
|
||||
});
|
||||
|
||||
it('passes in the default parameters', () => {
|
||||
return instance.getUncleCountByBlockNumber().then(() => {
|
||||
expect(scope.body.eth_getUncleCountByBlockNumber.params).to.deep.equal(['latest']);
|
||||
});
|
||||
});
|
||||
|
||||
it('passes in the blockNumber', () => {
|
||||
return instance.getUncleCountByBlockNumber('0x6789').then(() => {
|
||||
expect(scope.body.eth_getUncleCountByBlockNumber.params).to.deep.equal(['0x6789']);
|
||||
});
|
||||
});
|
||||
|
||||
it('formats the output number', () => {
|
||||
return instance.getUncleCountByBlockNumber('0x1234').then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
expect(count.toString(16)).to.equal('123');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/api/rpc/eth/index.js
Normal file
17
js/src/api/rpc/eth/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './eth';
|
||||
61
js/src/api/rpc/ethcore/ethcore.e2e.js
Normal file
61
js/src/api/rpc/ethcore/ethcore.e2e.js
Normal file
@@ -0,0 +1,61 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { createHttpApi } from '../../../../test/e2e/ethapi';
|
||||
|
||||
describe('ethapi.ethcore', () => {
|
||||
const ethapi = createHttpApi();
|
||||
|
||||
describe('gasFloorTarget', () => {
|
||||
it('returns and translates the target', () => {
|
||||
return ethapi.ethcore.gasFloorTarget().then((value) => {
|
||||
expect(value.gt(0)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('netChain', () => {
|
||||
it('returns and the chain', () => {
|
||||
return ethapi.ethcore.netChain().then((value) => {
|
||||
expect(value).to.equal('morden');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('netPort', () => {
|
||||
it('returns and translates the port', () => {
|
||||
return ethapi.ethcore.netPort().then((value) => {
|
||||
expect(value.gt(0)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('transactionsLimit', () => {
|
||||
it('returns and translates the limit', () => {
|
||||
return ethapi.ethcore.transactionsLimit().then((value) => {
|
||||
expect(value.gt(0)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('rpcSettings', () => {
|
||||
it('returns and translates the settings', () => {
|
||||
return ethapi.ethcore.rpcSettings().then((value) => {
|
||||
expect(value).to.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
168
js/src/api/rpc/ethcore/ethcore.js
Normal file
168
js/src/api/rpc/ethcore/ethcore.js
Normal file
@@ -0,0 +1,168 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { inAddress, inData, inNumber16 } from '../../format/input';
|
||||
import { outAddress, outNumber, outPeers } from '../../format/output';
|
||||
|
||||
export default class Ethcore {
|
||||
constructor (transport) {
|
||||
this._transport = transport;
|
||||
}
|
||||
|
||||
acceptNonReservedPeers () {
|
||||
return this._transport
|
||||
.execute('ethcore_acceptNonReservedPeers');
|
||||
}
|
||||
|
||||
addReservedPeer (encode) {
|
||||
return this._transport
|
||||
.execute('ethcore_addReservedPeer', encode);
|
||||
}
|
||||
|
||||
defaultExtraData () {
|
||||
return this._transport
|
||||
.execute('ethcore_defaultExtraData');
|
||||
}
|
||||
|
||||
devLogs () {
|
||||
return this._transport
|
||||
.execute('ethcore_devLogs');
|
||||
}
|
||||
|
||||
devLogsLevels () {
|
||||
return this._transport
|
||||
.execute('ethcore_devLogsLevels');
|
||||
}
|
||||
|
||||
dropNonReservedPeers () {
|
||||
return this._transport
|
||||
.execute('ethcore_dropNonReservedPeers');
|
||||
}
|
||||
|
||||
extraData () {
|
||||
return this._transport
|
||||
.execute('ethcore_extraData');
|
||||
}
|
||||
|
||||
gasFloorTarget () {
|
||||
return this._transport
|
||||
.execute('ethcore_gasFloorTarget')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
generateSecretPhrase () {
|
||||
return this._transport
|
||||
.execute('ethcore_generateSecretPhrase');
|
||||
}
|
||||
|
||||
hashContent (url) {
|
||||
return this._transport
|
||||
.execute('ethcore_hashContent', url);
|
||||
}
|
||||
|
||||
minGasPrice () {
|
||||
return this._transport
|
||||
.execute('ethcore_minGasPrice')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
netChain () {
|
||||
return this._transport
|
||||
.execute('ethcore_netChain');
|
||||
}
|
||||
|
||||
netPeers () {
|
||||
return this._transport
|
||||
.execute('ethcore_netPeers')
|
||||
.then(outPeers);
|
||||
}
|
||||
|
||||
netMaxPeers () {
|
||||
return this._transport
|
||||
.execute('ethcore_netMaxPeers')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
netPort () {
|
||||
return this._transport
|
||||
.execute('ethcore_netPort')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
nodeName () {
|
||||
return this._transport
|
||||
.execute('ethcore_nodeName');
|
||||
}
|
||||
|
||||
phraseToAddress (phrase) {
|
||||
return this._transport
|
||||
.execute('ethcore_phraseToAddress', phrase)
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
registryAddress () {
|
||||
return this._transport
|
||||
.execute('ethcore_registryAddress')
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
removeReservedPeer (encode) {
|
||||
return this._transport
|
||||
.execute('ethcore_removeReservedPeer', encode);
|
||||
}
|
||||
|
||||
rpcSettings () {
|
||||
return this._transport
|
||||
.execute('ethcore_rpcSettings');
|
||||
}
|
||||
|
||||
setAuthor (address) {
|
||||
return this._transport
|
||||
.execute('ethcore_setAuthor', inAddress(address));
|
||||
}
|
||||
|
||||
setExtraData (data) {
|
||||
return this._transport
|
||||
.execute('ethcore_setExtraData', inData(data));
|
||||
}
|
||||
|
||||
setGasFloorTarget (quantity) {
|
||||
return this._transport
|
||||
.execute('ethcore_setGasFloorTarget', inNumber16(quantity));
|
||||
}
|
||||
|
||||
setMinGasPrice (quantity) {
|
||||
return this._transport
|
||||
.execute('ethcore_setMinGasPrice', inNumber16(quantity));
|
||||
}
|
||||
|
||||
setTransactionsLimit (quantity) {
|
||||
return this._transport
|
||||
.execute('ethcore_setTransactionsLimit', inNumber16(quantity));
|
||||
}
|
||||
|
||||
transactionsLimit () {
|
||||
return this._transport
|
||||
.execute('ethcore_transactionsLimit')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
unsignedTransactionsCount () {
|
||||
return this._transport
|
||||
.execute('ethcore_unsignedTransactionsCount')
|
||||
.then(outNumber);
|
||||
}
|
||||
}
|
||||
92
js/src/api/rpc/ethcore/ethcore.spec.js
Normal file
92
js/src/api/rpc/ethcore/ethcore.spec.js
Normal file
@@ -0,0 +1,92 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { TEST_HTTP_URL, mockHttp } from '../../../../test/mockRpc';
|
||||
import { isBigNumber } from '../../../../test/types';
|
||||
|
||||
import Http from '../../transport/http';
|
||||
import Ethcore from './ethcore';
|
||||
|
||||
const instance = new Ethcore(new Http(TEST_HTTP_URL));
|
||||
|
||||
describe('api/rpc/Ethcore', () => {
|
||||
describe('gasFloorTarget', () => {
|
||||
it('returns the gasfloor, formatted', () => {
|
||||
mockHttp([{ method: 'ethcore_gasFloorTarget', reply: { result: '0x123456' } }]);
|
||||
|
||||
return instance.gasFloorTarget().then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
expect(count.eq(0x123456)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('minGasPrice', () => {
|
||||
it('returns the min gasprice, formatted', () => {
|
||||
mockHttp([{ method: 'ethcore_minGasPrice', reply: { result: '0x123456' } }]);
|
||||
|
||||
return instance.minGasPrice().then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
expect(count.eq(0x123456)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('netMaxPeers', () => {
|
||||
it('returns the max peers, formatted', () => {
|
||||
mockHttp([{ method: 'ethcore_netMaxPeers', reply: { result: 25 } }]);
|
||||
|
||||
return instance.netMaxPeers().then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
expect(count.eq(25)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('newPeers', () => {
|
||||
it('returns the peer structure, formatted', () => {
|
||||
mockHttp([{ method: 'ethcore_netPeers', reply: { result: { active: 123, connected: 456, max: 789 } } }]);
|
||||
|
||||
return instance.netPeers().then((peers) => {
|
||||
expect(peers.active.eq(123)).to.be.true;
|
||||
expect(peers.connected.eq(456)).to.be.true;
|
||||
expect(peers.max.eq(789)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('netPort', () => {
|
||||
it('returns the connected port, formatted', () => {
|
||||
mockHttp([{ method: 'ethcore_netPort', reply: { result: 33030 } }]);
|
||||
|
||||
return instance.netPort().then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
expect(count.eq(33030)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('transactionsLimit', () => {
|
||||
it('returns the tx limit, formatted', () => {
|
||||
mockHttp([{ method: 'ethcore_transactionsLimit', reply: { result: 1024 } }]);
|
||||
|
||||
return instance.transactionsLimit().then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
expect(count.eq(1024)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/api/rpc/ethcore/index.js
Normal file
17
js/src/api/rpc/ethcore/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './ethcore';
|
||||
24
js/src/api/rpc/index.js
Normal file
24
js/src/api/rpc/index.js
Normal file
@@ -0,0 +1,24 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 Db from './db';
|
||||
export Eth from './eth';
|
||||
export Ethcore from './ethcore';
|
||||
export Net from './net';
|
||||
export Personal from './personal';
|
||||
export Shh from './shh';
|
||||
export Trace from './trace';
|
||||
export Web3 from './web3';
|
||||
17
js/src/api/rpc/net/index.js
Normal file
17
js/src/api/rpc/net/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './net';
|
||||
46
js/src/api/rpc/net/net.e2e.js
Normal file
46
js/src/api/rpc/net/net.e2e.js
Normal file
@@ -0,0 +1,46 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { createHttpApi } from '../../../../test/e2e/ethapi';
|
||||
import { isBoolean } from '../../../../test/types';
|
||||
|
||||
describe('ethapi.net', () => {
|
||||
const ethapi = createHttpApi();
|
||||
|
||||
describe('listening', () => {
|
||||
it('returns the listening status', () => {
|
||||
return ethapi.net.listening().then((status) => {
|
||||
expect(isBoolean(status)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('peerCount', () => {
|
||||
it('returns the peer count', () => {
|
||||
return ethapi.net.peerCount().then((count) => {
|
||||
expect(count.gte(0)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('version', () => {
|
||||
it('returns the version', () => {
|
||||
return ethapi.net.version().then((version) => {
|
||||
expect(version).to.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
39
js/src/api/rpc/net/net.js
Normal file
39
js/src/api/rpc/net/net.js
Normal file
@@ -0,0 +1,39 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { outNumber } from '../../format/output';
|
||||
|
||||
export default class Net {
|
||||
constructor (transport) {
|
||||
this._transport = transport;
|
||||
}
|
||||
|
||||
listening () {
|
||||
return this._transport
|
||||
.execute('net_listening');
|
||||
}
|
||||
|
||||
peerCount () {
|
||||
return this._transport
|
||||
.execute('net_peerCount')
|
||||
.then(outNumber);
|
||||
}
|
||||
|
||||
version () {
|
||||
return this._transport
|
||||
.execute('net_version');
|
||||
}
|
||||
}
|
||||
36
js/src/api/rpc/net/net.spec.js
Normal file
36
js/src/api/rpc/net/net.spec.js
Normal file
@@ -0,0 +1,36 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { TEST_HTTP_URL, mockHttp } from '../../../../test/mockRpc';
|
||||
import { isBigNumber } from '../../../../test/types';
|
||||
|
||||
import Http from '../../transport/http';
|
||||
import Net from './net';
|
||||
|
||||
const instance = new Net(new Http(TEST_HTTP_URL));
|
||||
|
||||
describe('api/rpc/Net', () => {
|
||||
describe('peerCount', () => {
|
||||
it('returns the connected peers, formatted', () => {
|
||||
mockHttp([{ method: 'net_peerCount', reply: { result: '0x123456' } }]);
|
||||
|
||||
return instance.peerCount().then((count) => {
|
||||
expect(isBigNumber(count)).to.be.true;
|
||||
expect(count.eq(0x123456)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/api/rpc/personal/index.js
Normal file
17
js/src/api/rpc/personal/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './personal';
|
||||
53
js/src/api/rpc/personal/personal.e2e.js
Normal file
53
js/src/api/rpc/personal/personal.e2e.js
Normal file
@@ -0,0 +1,53 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { createHttpApi } from '../../../../test/e2e/ethapi';
|
||||
import { isAddress, isBoolean } from '../../../../test/types';
|
||||
|
||||
describe.skip('ethapi.personal', () => {
|
||||
const ethapi = createHttpApi();
|
||||
const password = 'P@55word';
|
||||
let address;
|
||||
|
||||
describe('newAccount', () => {
|
||||
it('creates a new account', () => {
|
||||
return ethapi.personal.newAccount(password).then((_address) => {
|
||||
address = _address;
|
||||
expect(isAddress(address)).to.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('listAccounts', () => {
|
||||
it('has the newly-created account', () => {
|
||||
return ethapi.personal.listAccounts(password).then((accounts) => {
|
||||
expect(accounts.filter((_address) => _address === address)).to.deep.equal([address]);
|
||||
accounts.forEach((account) => {
|
||||
expect(isAddress(account)).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('unlockAccount', () => {
|
||||
it('unlocks the newly-created account', () => {
|
||||
return ethapi.personal.unlockAccount(address, password).then((result) => {
|
||||
expect(isBoolean(result)).to.be.true;
|
||||
expect(result).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
112
js/src/api/rpc/personal/personal.js
Normal file
112
js/src/api/rpc/personal/personal.js
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { inAddress, inNumber10, inNumber16, inOptions } from '../../format/input';
|
||||
import { outAccountInfo, outAddress, outSignerRequest } from '../../format/output';
|
||||
|
||||
export default class Personal {
|
||||
constructor (transport) {
|
||||
this._transport = transport;
|
||||
}
|
||||
|
||||
accountsInfo () {
|
||||
return this._transport
|
||||
.execute('personal_accountsInfo')
|
||||
.then(outAccountInfo);
|
||||
}
|
||||
|
||||
confirmRequest (requestId, options, password) {
|
||||
return this._transport
|
||||
.execute('personal_confirmRequest', inNumber16(requestId), options, password);
|
||||
}
|
||||
|
||||
generateAuthorizationToken () {
|
||||
return this._transport
|
||||
.execute('personal_generateAuthorizationToken');
|
||||
}
|
||||
|
||||
listAccounts () {
|
||||
return this._transport
|
||||
.execute('personal_listAccounts')
|
||||
.then((accounts) => (accounts || []).map(outAddress));
|
||||
}
|
||||
|
||||
listGethAccounts () {
|
||||
return this._transport
|
||||
.execute('personal_listGethAccounts')
|
||||
.then((accounts) => (accounts || []).map(outAddress));
|
||||
}
|
||||
|
||||
importGethAccounts (accounts) {
|
||||
return this._transport
|
||||
.execute('personal_importGethAccounts', (accounts || []).map(inAddress))
|
||||
.then((accounts) => (accounts || []).map(outAddress));
|
||||
}
|
||||
|
||||
newAccount (password) {
|
||||
return this._transport
|
||||
.execute('personal_newAccount', password)
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
newAccountFromPhrase (phrase, password) {
|
||||
return this._transport
|
||||
.execute('personal_newAccountFromPhrase', phrase, password)
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
newAccountFromWallet (json, password) {
|
||||
return this._transport
|
||||
.execute('personal_newAccountFromWallet', json, password)
|
||||
.then(outAddress);
|
||||
}
|
||||
|
||||
rejectRequest (requestId) {
|
||||
return this._transport
|
||||
.execute('personal_rejectRequest', inNumber16(requestId));
|
||||
}
|
||||
|
||||
requestsToConfirm () {
|
||||
return this._transport
|
||||
.execute('personal_requestsToConfirm')
|
||||
.then((requests) => (requests || []).map(outSignerRequest));
|
||||
}
|
||||
|
||||
setAccountName (address, name) {
|
||||
return this._transport
|
||||
.execute('personal_setAccountName', inAddress(address), name);
|
||||
}
|
||||
|
||||
setAccountMeta (address, meta) {
|
||||
return this._transport
|
||||
.execute('personal_setAccountMeta', inAddress(address), JSON.stringify(meta));
|
||||
}
|
||||
|
||||
signAndSendTransaction (options, password) {
|
||||
return this._transport
|
||||
.execute('personal_signAndSendTransaction', inOptions(options), password);
|
||||
}
|
||||
|
||||
signerEnabled () {
|
||||
return this._transport
|
||||
.execute('personal_signerEnabled');
|
||||
}
|
||||
|
||||
unlockAccount (account, password, duration = 1) {
|
||||
return this._transport
|
||||
.execute('personal_unlockAccount', inAddress(account), password, inNumber10(duration));
|
||||
}
|
||||
}
|
||||
97
js/src/api/rpc/personal/personal.spec.js
Normal file
97
js/src/api/rpc/personal/personal.spec.js
Normal file
@@ -0,0 +1,97 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { TEST_HTTP_URL, mockHttp } from '../../../../test/mockRpc';
|
||||
|
||||
import Http from '../../transport/http';
|
||||
import Personal from './personal';
|
||||
|
||||
const instance = new Personal(new Http(TEST_HTTP_URL));
|
||||
|
||||
describe('rpc/Personal', () => {
|
||||
const account = '0x63cf90d3f0410092fc0fca41846f596223979195';
|
||||
const checksum = '0x63Cf90D3f0410092FC0fca41846f596223979195';
|
||||
let scope;
|
||||
|
||||
describe('accountsInfo', () => {
|
||||
it('retrieves the available account info', () => {
|
||||
scope = mockHttp([{ method: 'personal_accountsInfo', reply: {
|
||||
result: {
|
||||
'0x63cf90d3f0410092fc0fca41846f596223979195': {
|
||||
name: 'name', uuid: 'uuid', meta: '{"data":"data"}'
|
||||
}
|
||||
}
|
||||
} }]);
|
||||
|
||||
return instance.accountsInfo().then((result) => {
|
||||
expect(result).to.deep.equal({
|
||||
'0x63Cf90D3f0410092FC0fca41846f596223979195': {
|
||||
name: 'name', uuid: 'uuid', meta: {
|
||||
data: 'data'
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('listAccounts', () => {
|
||||
it('retrieves a list of available accounts', () => {
|
||||
scope = mockHttp([{ method: 'personal_listAccounts', reply: { result: [account] } }]);
|
||||
|
||||
return instance.listAccounts().then((result) => {
|
||||
expect(result).to.deep.equal([checksum]);
|
||||
});
|
||||
});
|
||||
|
||||
it('returns an empty list when none available', () => {
|
||||
scope = mockHttp([{ method: 'personal_listAccounts', reply: { result: null } }]);
|
||||
|
||||
return instance.listAccounts().then((result) => {
|
||||
expect(result).to.deep.equal([]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('newAccount', () => {
|
||||
it('passes the password, returning the address', () => {
|
||||
scope = mockHttp([{ method: 'personal_newAccount', reply: { result: account } }]);
|
||||
|
||||
return instance.newAccount('password').then((result) => {
|
||||
expect(scope.body.personal_newAccount.params).to.deep.equal(['password']);
|
||||
expect(result).to.equal(checksum);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('unlockAccount', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'personal_unlockAccount', reply: { result: [] } }]);
|
||||
});
|
||||
|
||||
it('passes account, password & duration', () => {
|
||||
return instance.unlockAccount(account, 'password', 0xf).then(() => {
|
||||
expect(scope.body.personal_unlockAccount.params).to.deep.equal([account, 'password', 15]);
|
||||
});
|
||||
});
|
||||
|
||||
it('provides a default duration when not specified', () => {
|
||||
return instance.unlockAccount(account, 'password').then(() => {
|
||||
expect(scope.body.personal_unlockAccount.params).to.deep.equal([account, 'password', 1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/api/rpc/shh/index.js
Normal file
17
js/src/api/rpc/shh/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './shh';
|
||||
71
js/src/api/rpc/shh/shh.js
Normal file
71
js/src/api/rpc/shh/shh.js
Normal file
@@ -0,0 +1,71 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 Personal {
|
||||
constructor (transport) {
|
||||
this._transport = transport;
|
||||
}
|
||||
|
||||
addToGroup (identity) {
|
||||
return this._transport
|
||||
.execute('shh_addToGroup', identity);
|
||||
}
|
||||
|
||||
getFilterChanges (filterId) {
|
||||
return this._transport
|
||||
.execute('shh_getFilterChanges', filterId);
|
||||
}
|
||||
|
||||
getMessages (filterId) {
|
||||
return this._transport
|
||||
.execute('shh_getMessages', filterId);
|
||||
}
|
||||
|
||||
hasIdentity (identity) {
|
||||
return this._transport
|
||||
.execute('shh_hasIdentity', identity);
|
||||
}
|
||||
|
||||
newFilter (options) {
|
||||
return this._transport
|
||||
.execute('shh_newFilter', options);
|
||||
}
|
||||
|
||||
newGroup () {
|
||||
return this._transport
|
||||
.execute('shh_newGroup');
|
||||
}
|
||||
|
||||
newIdentity () {
|
||||
return this._transport
|
||||
.execute('shh_newIdentity');
|
||||
}
|
||||
|
||||
post (options) {
|
||||
return this._transport
|
||||
.execute('shh_post', options);
|
||||
}
|
||||
|
||||
uninstallFilter (filterId) {
|
||||
return this._transport
|
||||
.execute('shh_uninstallFilter', filterId);
|
||||
}
|
||||
|
||||
version () {
|
||||
return this._transport
|
||||
.execute('shh_version');
|
||||
}
|
||||
}
|
||||
17
js/src/api/rpc/trace/index.js
Normal file
17
js/src/api/rpc/trace/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './trace';
|
||||
35
js/src/api/rpc/trace/trace.e2e.js
Normal file
35
js/src/api/rpc/trace/trace.e2e.js
Normal file
@@ -0,0 +1,35 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { createHttpApi } from '../../../../test/e2e/ethapi';
|
||||
|
||||
describe('ethapi.trace', () => {
|
||||
const ethapi = createHttpApi();
|
||||
|
||||
describe('block', () => {
|
||||
it('returns the latest block', () => {
|
||||
return ethapi.trace.block().then((block) => {
|
||||
expect(block).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
it('returns a specified block', () => {
|
||||
return ethapi.trace.block('0x65432').then((block) => {
|
||||
expect(block).to.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
43
js/src/api/rpc/trace/trace.js
Normal file
43
js/src/api/rpc/trace/trace.js
Normal file
@@ -0,0 +1,43 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { inBlockNumber, inHex, inNumber16 } from '../../format/input';
|
||||
|
||||
export default class Trace {
|
||||
constructor (transport) {
|
||||
this._transport = transport;
|
||||
}
|
||||
|
||||
filter (filterObj) {
|
||||
return this._transport
|
||||
.execute('trace_filter', filterObj);
|
||||
}
|
||||
|
||||
get (txHash, position) {
|
||||
return this._transport
|
||||
.execute('trace_get', inHex(txHash), inNumber16(position));
|
||||
}
|
||||
|
||||
transaction (txHash) {
|
||||
return this._transport
|
||||
.execute('trace_transaction', inHex(txHash));
|
||||
}
|
||||
|
||||
block (blockNumber = 'latest') {
|
||||
return this._transport
|
||||
.execute('trace_block', inBlockNumber(blockNumber));
|
||||
}
|
||||
}
|
||||
44
js/src/api/rpc/trace/trace.spec.js
Normal file
44
js/src/api/rpc/trace/trace.spec.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { TEST_HTTP_URL, mockHttp } from '../../../../test/mockRpc';
|
||||
|
||||
import Http from '../../transport/http';
|
||||
import Trace from './trace';
|
||||
|
||||
const instance = new Trace(new Http(TEST_HTTP_URL));
|
||||
|
||||
describe('api/rpc/Trace', () => {
|
||||
let scope;
|
||||
|
||||
describe('block', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'trace_block', reply: { result: [] } }]);
|
||||
});
|
||||
|
||||
it('assumes latest blockNumber when not specified', () => {
|
||||
return instance.block().then(() => {
|
||||
expect(scope.body.trace_block.params).to.deep.equal(['latest']);
|
||||
});
|
||||
});
|
||||
|
||||
it('passed specified blockNumber', () => {
|
||||
return instance.block(0x123).then(() => {
|
||||
expect(scope.body.trace_block.params).to.deep.equal(['0x123']);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/api/rpc/web3/index.js
Normal file
17
js/src/api/rpc/web3/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './web3';
|
||||
44
js/src/api/rpc/web3/web3.e2e.js
Normal file
44
js/src/api/rpc/web3/web3.e2e.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { createHttpApi } from '../../../../test/e2e/ethapi';
|
||||
import { isHexNumber } from '../../../../test/types';
|
||||
|
||||
describe('ethapi.web3', () => {
|
||||
const ethapi = createHttpApi();
|
||||
|
||||
describe('clientVersion', () => {
|
||||
it('returns the client version', () => {
|
||||
return ethapi.web3.clientVersion().then((version) => {
|
||||
const [client] = version.split('/');
|
||||
|
||||
expect(client === 'Parity' || client === 'Geth').to.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('sha3', () => {
|
||||
it('returns a keccak256 sha', () => {
|
||||
const sha = '0xa7916fac4f538170f7cd12c148552e2cba9fcd72329a2dd5b07a6fa906488ddf';
|
||||
const hexStr = 'baz()'.split('').map((char) => char.charCodeAt(0).toString(16)).join('');
|
||||
|
||||
return ethapi.web3.sha3(`0x${hexStr}`).then((hash) => {
|
||||
expect(isHexNumber(hash)).to.be.true;
|
||||
expect(hash).to.equal(sha);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
33
js/src/api/rpc/web3/web3.js
Normal file
33
js/src/api/rpc/web3/web3.js
Normal file
@@ -0,0 +1,33 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { inHex } from '../../format/input';
|
||||
|
||||
export default class Web3 {
|
||||
constructor (transport) {
|
||||
this._transport = transport;
|
||||
}
|
||||
|
||||
clientVersion () {
|
||||
return this._transport
|
||||
.execute('web3_clientVersion');
|
||||
}
|
||||
|
||||
sha3 (hexStr) {
|
||||
return this._transport
|
||||
.execute('web3_sha3', inHex(hexStr));
|
||||
}
|
||||
}
|
||||
38
js/src/api/rpc/web3/web3.spec.js
Normal file
38
js/src/api/rpc/web3/web3.spec.js
Normal file
@@ -0,0 +1,38 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { TEST_HTTP_URL, mockHttp } from '../../../../test/mockRpc';
|
||||
|
||||
import Http from '../../transport/http';
|
||||
import Web3 from './web3';
|
||||
|
||||
const instance = new Web3(new Http(TEST_HTTP_URL));
|
||||
|
||||
describe('api/rpc/Web3', () => {
|
||||
let scope;
|
||||
|
||||
describe('sha3', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'web3_sha3', reply: { result: [] } }]);
|
||||
});
|
||||
|
||||
it('formats the inputs correctly', () => {
|
||||
return instance.sha3('1234').then(() => {
|
||||
expect(scope.body.web3_sha3.params).to.deep.equal(['0x1234']);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
62
js/src/api/subscriptions/eth.js
Normal file
62
js/src/api/subscriptions/eth.js
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 BigNumber from 'bignumber.js';
|
||||
|
||||
export default class Eth {
|
||||
constructor (updateSubscriptions, api) {
|
||||
this._api = api;
|
||||
this._updateSubscriptions = updateSubscriptions;
|
||||
this._started = false;
|
||||
|
||||
this._lastBlock = new BigNumber(-1);
|
||||
}
|
||||
|
||||
get isStarted () {
|
||||
return this._started;
|
||||
}
|
||||
|
||||
start () {
|
||||
this._started = true;
|
||||
|
||||
return this._blockNumber();
|
||||
}
|
||||
|
||||
_blockNumber = () => {
|
||||
const nextTimeout = (timeout = 1000) => {
|
||||
setTimeout(() => {
|
||||
this._blockNumber();
|
||||
}, timeout);
|
||||
};
|
||||
|
||||
if (!this._api.transport.isConnected) {
|
||||
nextTimeout(500);
|
||||
return;
|
||||
}
|
||||
|
||||
return this._api.eth
|
||||
.blockNumber()
|
||||
.then((blockNumber) => {
|
||||
if (!blockNumber.eq(this._lastBlock)) {
|
||||
this._lastBlock = blockNumber;
|
||||
this._updateSubscriptions('eth_blockNumber', null, blockNumber);
|
||||
}
|
||||
|
||||
nextTimeout();
|
||||
})
|
||||
.catch(nextTimeout);
|
||||
}
|
||||
}
|
||||
101
js/src/api/subscriptions/eth.spec.js
Normal file
101
js/src/api/subscriptions/eth.spec.js
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 BigNumber from 'bignumber.js';
|
||||
import sinon from 'sinon';
|
||||
import 'sinon-as-promised';
|
||||
|
||||
import Eth from './eth';
|
||||
|
||||
const START_BLOCK = 5000;
|
||||
|
||||
function stubApi (blockNumber) {
|
||||
const _calls = {
|
||||
blockNumber: []
|
||||
};
|
||||
|
||||
return {
|
||||
_calls,
|
||||
transport: {
|
||||
isConnected: true
|
||||
},
|
||||
eth: {
|
||||
blockNumber: () => {
|
||||
const stub = sinon.stub().resolves(new BigNumber(blockNumber || START_BLOCK))();
|
||||
_calls.blockNumber.push(stub);
|
||||
return stub;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
describe('api/subscriptions/eth', () => {
|
||||
let api;
|
||||
let eth;
|
||||
let cb;
|
||||
|
||||
beforeEach(() => {
|
||||
api = stubApi();
|
||||
cb = sinon.stub();
|
||||
eth = new Eth(cb, api);
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('starts the instance in a stopped state', () => {
|
||||
expect(eth.isStarted).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('start', () => {
|
||||
describe('blockNumber available', () => {
|
||||
beforeEach(() => {
|
||||
return eth.start();
|
||||
});
|
||||
|
||||
it('sets the started status', () => {
|
||||
expect(eth.isStarted).to.be.true;
|
||||
});
|
||||
|
||||
it('calls eth_blockNumber', () => {
|
||||
expect(api._calls.blockNumber.length).to.be.ok;
|
||||
});
|
||||
|
||||
it('updates subscribers', () => {
|
||||
expect(cb).to.have.been.calledWith('eth_blockNumber', null, new BigNumber(START_BLOCK));
|
||||
});
|
||||
});
|
||||
|
||||
describe('blockNumber not available', () => {
|
||||
beforeEach(() => {
|
||||
api = stubApi(-1);
|
||||
eth = new Eth(cb, api);
|
||||
return eth.start();
|
||||
});
|
||||
|
||||
it('sets the started status', () => {
|
||||
expect(eth.isStarted).to.be.true;
|
||||
});
|
||||
|
||||
it('calls eth_blockNumber', () => {
|
||||
expect(api._calls.blockNumber.length).to.be.ok;
|
||||
});
|
||||
|
||||
it('does not update subscribers', () => {
|
||||
expect(cb).not.to.been.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
19
js/src/api/subscriptions/index.js
Normal file
19
js/src/api/subscriptions/index.js
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 Logging from './logging';
|
||||
|
||||
export default from './manager';
|
||||
44
js/src/api/subscriptions/logging.js
Normal file
44
js/src/api/subscriptions/logging.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright 2015, 2016 Ethcore (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/>.
|
||||
|
||||
let instance = null;
|
||||
|
||||
export default class Logging {
|
||||
constructor (updateSubscriptions) {
|
||||
this._updateSubscriptions = updateSubscriptions;
|
||||
|
||||
instance = this;
|
||||
}
|
||||
|
||||
get isStarted () {
|
||||
return true;
|
||||
}
|
||||
|
||||
start () {
|
||||
}
|
||||
|
||||
static send (method, params, json) {
|
||||
if (!instance) {
|
||||
return;
|
||||
}
|
||||
|
||||
return instance._updateSubscriptions('logging', null, {
|
||||
method,
|
||||
params,
|
||||
json
|
||||
});
|
||||
}
|
||||
}
|
||||
49
js/src/api/subscriptions/logging.spec.js
Normal file
49
js/src/api/subscriptions/logging.spec.js
Normal file
@@ -0,0 +1,49 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 sinon from 'sinon';
|
||||
|
||||
import Logging from './logging';
|
||||
|
||||
describe('api/subscriptions/logging', () => {
|
||||
let cb;
|
||||
let logging;
|
||||
|
||||
beforeEach(() => {
|
||||
cb = sinon.stub();
|
||||
logging = new Logging(cb);
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('starts the instance in a started state', () => {
|
||||
expect(logging.isStarted).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('send', () => {
|
||||
const method = 'method';
|
||||
const params = 'params';
|
||||
const json = 'json';
|
||||
|
||||
beforeEach(() => {
|
||||
Logging.send(method, params, json);
|
||||
});
|
||||
|
||||
it('calls the subscription update', () => {
|
||||
expect(cb).to.have.been.calledWith('logging', null, { method, params, json });
|
||||
});
|
||||
});
|
||||
});
|
||||
134
js/src/api/subscriptions/manager.js
Normal file
134
js/src/api/subscriptions/manager.js
Normal file
@@ -0,0 +1,134 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { isError } from '../util/types';
|
||||
|
||||
import Eth from './eth';
|
||||
import Logging from './logging';
|
||||
import Personal from './personal';
|
||||
import Signer from './signer';
|
||||
|
||||
const events = {
|
||||
'logging': { module: 'logging' },
|
||||
'eth_blockNumber': { module: 'eth' },
|
||||
'personal_accountsInfo': { module: 'personal' },
|
||||
'personal_listAccounts': { module: 'personal' },
|
||||
'personal_requestsToConfirm': { module: 'signer' }
|
||||
};
|
||||
|
||||
let nextSubscriptionId = 0;
|
||||
|
||||
export default class Manager {
|
||||
constructor (api) {
|
||||
this._api = api;
|
||||
|
||||
this.subscriptions = {};
|
||||
this.values = {};
|
||||
|
||||
Object.keys(events).forEach((subscriptionName) => {
|
||||
this.subscriptions[subscriptionName] = {};
|
||||
this.values[subscriptionName] = {
|
||||
error: null,
|
||||
data: null
|
||||
};
|
||||
});
|
||||
|
||||
this._logging = new Logging(this._updateSubscriptions);
|
||||
this._eth = new Eth(this._updateSubscriptions, api);
|
||||
this._personal = new Personal(this._updateSubscriptions, api, this);
|
||||
this._signer = new Signer(this._updateSubscriptions, api, this);
|
||||
}
|
||||
|
||||
_validateType (subscriptionName) {
|
||||
const subscription = events[subscriptionName];
|
||||
|
||||
if (!subscription) {
|
||||
return new Error(`${subscriptionName} is not a valid interface, subscribe using one of ${Object.keys(events).join(', ')}`);
|
||||
}
|
||||
|
||||
return subscription;
|
||||
}
|
||||
|
||||
subscribe (subscriptionName, callback) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const subscription = this._validateType(subscriptionName);
|
||||
|
||||
if (isError(subscription)) {
|
||||
reject(subscription);
|
||||
return;
|
||||
}
|
||||
|
||||
const subscriptionId = nextSubscriptionId++;
|
||||
const { error, data } = this.values[subscriptionName];
|
||||
const engine = this[`_${subscription.module}`];
|
||||
|
||||
this.subscriptions[subscriptionName][subscriptionId] = callback;
|
||||
|
||||
if (!engine.isStarted) {
|
||||
engine.start();
|
||||
} else {
|
||||
this._sendData(subscriptionName, subscriptionId, callback, error, data);
|
||||
}
|
||||
|
||||
resolve(subscriptionId);
|
||||
});
|
||||
}
|
||||
|
||||
unsubscribe (subscriptionName, subscriptionId) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const subscription = this._validateType(subscriptionName);
|
||||
|
||||
if (isError(subscription)) {
|
||||
reject(subscription);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!this.subscriptions[subscriptionName][subscriptionId]) {
|
||||
reject(new Error(`Cannot find subscription ${subscriptionId} for type ${subscriptionName}`));
|
||||
return;
|
||||
}
|
||||
|
||||
delete this.subscriptions[subscriptionName][subscriptionId];
|
||||
resolve();
|
||||
});
|
||||
}
|
||||
|
||||
_sendData (subscriptionName, subscriptionId, callback, error, data) {
|
||||
try {
|
||||
callback(error, data);
|
||||
} catch (error) {
|
||||
console.error(`Unable to update callback for ${subscriptionName}, subscriptionId ${subscriptionId}`, error);
|
||||
this.unsubscribe(subscriptionName, subscriptionId);
|
||||
}
|
||||
}
|
||||
|
||||
_updateSubscriptions = (subscriptionName, error, data) => {
|
||||
if (!this.subscriptions[subscriptionName]) {
|
||||
throw new Error(`Cannot find entry point for subscriptions of type ${subscriptionName}`);
|
||||
}
|
||||
|
||||
this.values[subscriptionName] = { error, data };
|
||||
Object.keys(this.subscriptions[subscriptionName]).forEach((subscriptionId) => {
|
||||
const callback = this.subscriptions[subscriptionName][subscriptionId];
|
||||
|
||||
this._sendData(subscriptionName, subscriptionId, callback, error, data);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export {
|
||||
events
|
||||
};
|
||||
98
js/src/api/subscriptions/manager.spec.js
Normal file
98
js/src/api/subscriptions/manager.spec.js
Normal file
@@ -0,0 +1,98 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 sinon from 'sinon';
|
||||
|
||||
import Manager, { events } from './manager';
|
||||
|
||||
function newStub () {
|
||||
const start = () => manager._updateSubscriptions(manager.__test, null, 'test');
|
||||
const manager = new Manager({
|
||||
transport: {
|
||||
isConnected: true
|
||||
}
|
||||
});
|
||||
|
||||
manager._eth = {
|
||||
isStarted: false,
|
||||
start
|
||||
};
|
||||
|
||||
manager._personal = {
|
||||
isStarted: false,
|
||||
start
|
||||
};
|
||||
|
||||
manager._signer = {
|
||||
isStarted: false,
|
||||
start
|
||||
};
|
||||
|
||||
return manager;
|
||||
}
|
||||
|
||||
describe('api/subscriptions/manager', () => {
|
||||
let manager;
|
||||
|
||||
beforeEach(() => {
|
||||
manager = newStub();
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('sets up the subscription types & defaults', () => {
|
||||
expect(Object.keys(manager.subscriptions)).to.deep.equal(Object.keys(events));
|
||||
expect(Object.keys(manager.values)).to.deep.equal(Object.keys(events));
|
||||
});
|
||||
});
|
||||
|
||||
describe('subscriptions', () => {
|
||||
Object
|
||||
.keys(events)
|
||||
.filter((eventName) => eventName.indexOf('_') !== -1)
|
||||
.forEach((eventName) => {
|
||||
const { module } = events[eventName];
|
||||
let engine;
|
||||
let cb;
|
||||
let subscriptionId;
|
||||
|
||||
describe(eventName, () => {
|
||||
beforeEach(() => {
|
||||
engine = manager[`_${module}`];
|
||||
manager.__test = eventName;
|
||||
cb = sinon.stub();
|
||||
sinon.spy(engine, 'start');
|
||||
return manager
|
||||
.subscribe(eventName, cb)
|
||||
.then((_subscriptionId) => {
|
||||
subscriptionId = _subscriptionId;
|
||||
});
|
||||
});
|
||||
|
||||
it(`puts the ${module} engine in a started state`, () => {
|
||||
expect(engine.start).to.have.been.called;
|
||||
});
|
||||
|
||||
it('returns a subscriptionId', () => {
|
||||
expect(subscriptionId).to.be.ok;
|
||||
});
|
||||
|
||||
it('calls the subscription callback with updated values', () => {
|
||||
expect(cb).to.have.been.calledWith(null, 'test');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
77
js/src/api/subscriptions/personal.js
Normal file
77
js/src/api/subscriptions/personal.js
Normal file
@@ -0,0 +1,77 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 Personal {
|
||||
constructor (updateSubscriptions, api, subscriber) {
|
||||
this._subscriber = subscriber;
|
||||
this._api = api;
|
||||
this._updateSubscriptions = updateSubscriptions;
|
||||
this._started = false;
|
||||
}
|
||||
|
||||
get isStarted () {
|
||||
return this._started;
|
||||
}
|
||||
|
||||
start () {
|
||||
this._started = true;
|
||||
|
||||
return Promise.all([
|
||||
this._listAccounts(),
|
||||
this._accountsInfo(),
|
||||
this._loggingSubscribe()
|
||||
]);
|
||||
}
|
||||
|
||||
_listAccounts = () => {
|
||||
return this._api.personal
|
||||
.listAccounts()
|
||||
.then((accounts) => {
|
||||
this._updateSubscriptions('personal_listAccounts', null, accounts);
|
||||
});
|
||||
}
|
||||
|
||||
_accountsInfo = () => {
|
||||
return this._api.personal
|
||||
.accountsInfo()
|
||||
.then((info) => {
|
||||
this._updateSubscriptions('personal_accountsInfo', null, info);
|
||||
});
|
||||
}
|
||||
|
||||
_loggingSubscribe () {
|
||||
return this._subscriber.subscribe('logging', (error, data) => {
|
||||
if (error || !data) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data.method) {
|
||||
case 'personal_importGethAccounts':
|
||||
case 'personal_newAccount':
|
||||
case 'personal_newAccountFromPhrase':
|
||||
case 'personal_newAccountFromWallet':
|
||||
this._listAccounts();
|
||||
this._accountsInfo();
|
||||
return;
|
||||
|
||||
case 'personal_setAccountName':
|
||||
case 'personal_setAccountMeta':
|
||||
this._accountsInfo();
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
122
js/src/api/subscriptions/personal.spec.js
Normal file
122
js/src/api/subscriptions/personal.spec.js
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 sinon from 'sinon';
|
||||
import 'sinon-as-promised';
|
||||
|
||||
import Personal from './personal';
|
||||
|
||||
const TEST_INFO = {
|
||||
'0xfa64203C044691aA57251aF95f4b48d85eC00Dd5': {
|
||||
name: 'test'
|
||||
}
|
||||
};
|
||||
const TEST_LIST = ['0xfa64203C044691aA57251aF95f4b48d85eC00Dd5'];
|
||||
|
||||
function stubApi (accounts, info) {
|
||||
const _calls = {
|
||||
accountsInfo: [],
|
||||
listAccounts: []
|
||||
};
|
||||
|
||||
return {
|
||||
_calls,
|
||||
personal: {
|
||||
accountsInfo: () => {
|
||||
const stub = sinon.stub().resolves(info || TEST_INFO)();
|
||||
_calls.accountsInfo.push(stub);
|
||||
return stub;
|
||||
},
|
||||
|
||||
listAccounts: () => {
|
||||
const stub = sinon.stub().resolves(accounts || TEST_LIST)();
|
||||
_calls.listAccounts.push(stub);
|
||||
return stub;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function stubLogging () {
|
||||
return {
|
||||
subscribe: sinon.stub()
|
||||
};
|
||||
}
|
||||
|
||||
describe('api/subscriptions/personal', () => {
|
||||
let api;
|
||||
let cb;
|
||||
let logging;
|
||||
let personal;
|
||||
|
||||
beforeEach(() => {
|
||||
api = stubApi();
|
||||
cb = sinon.stub();
|
||||
logging = stubLogging();
|
||||
personal = new Personal(cb, api, logging);
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('starts the instance in a stopped state', () => {
|
||||
expect(personal.isStarted).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('start', () => {
|
||||
describe('info available', () => {
|
||||
beforeEach(() => {
|
||||
return personal.start();
|
||||
});
|
||||
|
||||
it('sets the started status', () => {
|
||||
expect(personal.isStarted).to.be.true;
|
||||
});
|
||||
|
||||
it('calls personal_accountsInfo', () => {
|
||||
expect(api._calls.accountsInfo.length).to.be.ok;
|
||||
});
|
||||
|
||||
it('calls personal_listAccounts', () => {
|
||||
expect(api._calls.listAccounts.length).to.be.ok;
|
||||
});
|
||||
|
||||
it('updates subscribers', () => {
|
||||
expect(cb.firstCall).to.have.been.calledWith('personal_listAccounts', null, TEST_LIST);
|
||||
expect(cb.secondCall).to.have.been.calledWith('personal_accountsInfo', null, TEST_INFO);
|
||||
});
|
||||
});
|
||||
|
||||
describe('info not available', () => {
|
||||
beforeEach(() => {
|
||||
api = stubApi([], {});
|
||||
personal = new Personal(cb, api, logging);
|
||||
return personal.start();
|
||||
});
|
||||
|
||||
it('sets the started status', () => {
|
||||
expect(personal.isStarted).to.be.true;
|
||||
});
|
||||
|
||||
it('calls personal_accountsInfo', () => {
|
||||
expect(api._calls.accountsInfo.length).to.be.ok;
|
||||
});
|
||||
|
||||
it('calls personal_listAccounts', () => {
|
||||
expect(api._calls.listAccounts.length).to.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
76
js/src/api/subscriptions/signer.js
Normal file
76
js/src/api/subscriptions/signer.js
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 Signer {
|
||||
constructor (updateSubscriptions, api, subscriber) {
|
||||
this._subscriber = subscriber;
|
||||
this._api = api;
|
||||
this._updateSubscriptions = updateSubscriptions;
|
||||
this._started = false;
|
||||
}
|
||||
|
||||
get isStarted () {
|
||||
return this._started;
|
||||
}
|
||||
|
||||
start () {
|
||||
this._started = true;
|
||||
|
||||
return Promise.all([
|
||||
this._listRequests(true),
|
||||
this._loggingSubscribe()
|
||||
]);
|
||||
}
|
||||
|
||||
_listRequests = (doTimeout) => {
|
||||
const nextTimeout = (timeout = 1000) => {
|
||||
if (doTimeout) {
|
||||
setTimeout(() => {
|
||||
this._listRequests(true);
|
||||
}, timeout);
|
||||
}
|
||||
};
|
||||
|
||||
if (!this._api.transport.isConnected) {
|
||||
nextTimeout(500);
|
||||
return;
|
||||
}
|
||||
|
||||
return this._api.personal
|
||||
.requestsToConfirm()
|
||||
.then((requests) => {
|
||||
this._updateSubscriptions('personal_requestsToConfirm', null, requests);
|
||||
nextTimeout();
|
||||
})
|
||||
.catch(nextTimeout);
|
||||
}
|
||||
|
||||
_loggingSubscribe () {
|
||||
return this._subscriber.subscribe('logging', (error, data) => {
|
||||
if (error || !data) {
|
||||
return;
|
||||
}
|
||||
|
||||
switch (data.method) {
|
||||
case 'eth_postTransaction':
|
||||
case 'eth_sendTranasction':
|
||||
case 'eth_sendRawTransaction':
|
||||
this._listRequests(false);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
29
js/src/api/transport/http/http.e2e.js
Normal file
29
js/src/api/transport/http/http.e2e.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 Http from './http';
|
||||
|
||||
const http = new Http('http://localhost:8545');
|
||||
|
||||
describe('transport/Http', () => {
|
||||
it('connects and makes a call to web3_clientVersion', () => {
|
||||
return http.execute('web3_clientVersion').then((version) => {
|
||||
const [client] = version.split('/');
|
||||
|
||||
expect(client === 'Geth' || client === 'Parity').to.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
76
js/src/api/transport/http/http.js
Normal file
76
js/src/api/transport/http/http.js
Normal file
@@ -0,0 +1,76 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { Logging } from '../../subscriptions';
|
||||
import JsonRpcBase from '../jsonRpcBase';
|
||||
|
||||
/* global fetch */
|
||||
export default class Http extends JsonRpcBase {
|
||||
constructor (url) {
|
||||
super();
|
||||
|
||||
this._connected = true;
|
||||
this._url = url;
|
||||
}
|
||||
|
||||
_encodeOptions (method, params) {
|
||||
const json = this.encode(method, params);
|
||||
|
||||
this.log(json);
|
||||
|
||||
return {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': json.length
|
||||
},
|
||||
body: json
|
||||
};
|
||||
}
|
||||
|
||||
execute (method, ...params) {
|
||||
const request = this._encodeOptions(method, params);
|
||||
|
||||
return fetch(this._url, request)
|
||||
.catch((error) => {
|
||||
this._connected = false;
|
||||
throw error;
|
||||
})
|
||||
.then((response) => {
|
||||
this._connected = true;
|
||||
|
||||
if (response.status !== 200) {
|
||||
this._connected = false;
|
||||
this.error(JSON.stringify({ status: response.status, statusText: response.statusText }));
|
||||
throw new Error(`${response.status}: ${response.statusText}`);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((response) => {
|
||||
Logging.send(method, params, { request, response });
|
||||
|
||||
if (response.error) {
|
||||
this.error(JSON.stringify(response));
|
||||
throw new Error(`${response.error.code}: ${response.error.message}`);
|
||||
}
|
||||
|
||||
this.log(JSON.stringify(response));
|
||||
return response.result;
|
||||
});
|
||||
}
|
||||
}
|
||||
122
js/src/api/transport/http/http.spec.js
Normal file
122
js/src/api/transport/http/http.spec.js
Normal file
@@ -0,0 +1,122 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { TEST_HTTP_URL, mockHttp } from '../../../../test/mockRpc';
|
||||
import Http from './http';
|
||||
|
||||
const transport = new Http(TEST_HTTP_URL);
|
||||
|
||||
describe('api/transport/Http', () => {
|
||||
describe('instance', () => {
|
||||
it('encodes the options correctly', () => {
|
||||
const opt = transport._encodeOptions('someMethod', ['param']);
|
||||
const enc = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': 65
|
||||
},
|
||||
body: `{"jsonrpc":"2.0","method":"someMethod","params":["param"],"id":${transport._id - 1}}`
|
||||
};
|
||||
|
||||
expect(opt).to.deep.equal(enc);
|
||||
});
|
||||
});
|
||||
|
||||
describe('transport', () => {
|
||||
const RESULT = ['this is some result'];
|
||||
|
||||
let scope;
|
||||
let result;
|
||||
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_call', reply: { result: RESULT } }]);
|
||||
|
||||
return transport
|
||||
.execute('eth_call', 1, 2, 3, 'test')
|
||||
.then((_result) => {
|
||||
result = _result;
|
||||
});
|
||||
});
|
||||
|
||||
it('makes POST', () => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
});
|
||||
|
||||
it('sets jsonrpc', () => {
|
||||
expect(scope.body.eth_call.jsonrpc).to.equal('2.0');
|
||||
});
|
||||
|
||||
it('sets the method', () => {
|
||||
expect(scope.body.eth_call.method).to.equal('eth_call');
|
||||
});
|
||||
|
||||
it('passes the params', () => {
|
||||
expect(scope.body.eth_call.params).to.deep.equal([1, 2, 3, 'test']);
|
||||
});
|
||||
|
||||
it('increments the id', () => {
|
||||
expect(scope.body.eth_call.id).not.to.equal(0);
|
||||
});
|
||||
|
||||
it('passes the actual result back', () => {
|
||||
expect(result).to.deep.equal(RESULT);
|
||||
});
|
||||
});
|
||||
|
||||
describe('HTTP errors', () => {
|
||||
let scope;
|
||||
let error;
|
||||
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_call', reply: {}, code: 500 }]);
|
||||
|
||||
return transport
|
||||
.execute('eth_call')
|
||||
.catch((_error) => {
|
||||
error = _error;
|
||||
});
|
||||
});
|
||||
|
||||
it('returns HTTP errors as throws', () => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
expect(error.message).to.match(/Internal Server Error/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('RPC errors', () => {
|
||||
const ERROR = { code: -1, message: 'ERROR: RPC failure' };
|
||||
|
||||
let scope;
|
||||
let error;
|
||||
|
||||
beforeEach(() => {
|
||||
scope = mockHttp([{ method: 'eth_call', reply: { error: ERROR } }]);
|
||||
|
||||
return transport
|
||||
.execute('eth_call')
|
||||
.catch((_error) => {
|
||||
error = _error;
|
||||
});
|
||||
});
|
||||
|
||||
it('returns RPC errors as throws', () => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
expect(error.message).to.match(/RPC failure/);
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/api/transport/http/index.js
Normal file
17
js/src/api/transport/http/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './http';
|
||||
18
js/src/api/transport/index.js
Normal file
18
js/src/api/transport/index.js
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 Http from './http';
|
||||
export Ws from './ws';
|
||||
62
js/src/api/transport/jsonRpcBase.js
Normal file
62
js/src/api/transport/jsonRpcBase.js
Normal file
@@ -0,0 +1,62 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 JsonRpcBase {
|
||||
constructor () {
|
||||
this._id = 1;
|
||||
this._debug = false;
|
||||
this._connected = false;
|
||||
}
|
||||
|
||||
encode (method, params) {
|
||||
const json = JSON.stringify({
|
||||
jsonrpc: '2.0',
|
||||
method: method,
|
||||
params: params,
|
||||
id: this._id++
|
||||
});
|
||||
|
||||
return json;
|
||||
}
|
||||
|
||||
get id () {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get isDebug () {
|
||||
return this._debug;
|
||||
}
|
||||
|
||||
get isConnected () {
|
||||
return this._connected;
|
||||
}
|
||||
|
||||
setDebug (flag) {
|
||||
this._debug = flag;
|
||||
}
|
||||
|
||||
error (error) {
|
||||
if (this.isDebug) {
|
||||
console.error(error);
|
||||
}
|
||||
}
|
||||
|
||||
log (log) {
|
||||
if (this.isDebug) {
|
||||
console.log(log);
|
||||
}
|
||||
}
|
||||
}
|
||||
88
js/src/api/transport/jsonRpcBase.spec.js
Normal file
88
js/src/api/transport/jsonRpcBase.spec.js
Normal file
@@ -0,0 +1,88 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 sinon from 'sinon';
|
||||
|
||||
import JsonRpcBase from './jsonRpcBase';
|
||||
|
||||
const base = new JsonRpcBase();
|
||||
|
||||
describe('api/transport/JsonRpcBase', () => {
|
||||
describe('encode', () => {
|
||||
it('encodes the body correctly, incrementing id', () => {
|
||||
const id = base.id;
|
||||
const bdy = base.encode('someMethod', ['param1', 'param2']);
|
||||
const enc = `{"jsonrpc":"2.0","method":"someMethod","params":["param1","param2"],"id":${id}}`;
|
||||
|
||||
expect(bdy).to.equal(enc);
|
||||
expect(base.id - id).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setDebug', () => {
|
||||
it('starts with disabled flag', () => {
|
||||
expect(base.isDebug).to.be.false;
|
||||
});
|
||||
|
||||
it('true flag switches on', () => {
|
||||
base.setDebug(true);
|
||||
expect(base.isDebug).to.be.true;
|
||||
});
|
||||
|
||||
it('false flag switches off', () => {
|
||||
base.setDebug(true);
|
||||
expect(base.isDebug).to.be.true;
|
||||
base.setDebug(false);
|
||||
expect(base.isDebug).to.be.false;
|
||||
});
|
||||
|
||||
describe('logging', () => {
|
||||
beforeEach(() => {
|
||||
sinon.spy(console, 'log');
|
||||
sinon.spy(console, 'error');
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
console.log.restore();
|
||||
console.error.restore();
|
||||
});
|
||||
|
||||
it('does not log errors with flag off', () => {
|
||||
base.setDebug(false);
|
||||
base.log('error');
|
||||
expect(console.log).to.not.be.called;
|
||||
});
|
||||
|
||||
it('does not log errors with flag off', () => {
|
||||
base.setDebug(false);
|
||||
base.error('error');
|
||||
expect(console.error).to.not.be.called;
|
||||
});
|
||||
|
||||
it('does log errors with flag on', () => {
|
||||
base.setDebug(true);
|
||||
base.log('error');
|
||||
expect(console.log).to.be.called;
|
||||
});
|
||||
|
||||
it('does log errors with flag on', () => {
|
||||
base.setDebug(true);
|
||||
base.error('error');
|
||||
expect(console.error).to.be.called;
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/api/transport/ws/index.js
Normal file
17
js/src/api/transport/ws/index.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Copyright 2015, 2016 Ethcore (UK) Ltd.
|
||||
// This file is part of Parity.
|
||||
|
||||
// Parity is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
|
||||
// Parity is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
export default from './ws';
|
||||
29
js/src/api/transport/ws/ws.e2e.js
Normal file
29
js/src/api/transport/ws/ws.e2e.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 Ws from './ws';
|
||||
|
||||
const ws = new Ws('ws://localhost:8546/');
|
||||
|
||||
describe('transport/Ws', () => {
|
||||
it('connects and makes a call to web3_clientVersion', () => {
|
||||
return ws.execute('web3_clientVersion').then((version) => {
|
||||
const [client] = version.split('/');
|
||||
|
||||
expect(client === 'Geth' || client === 'Parity').to.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
148
js/src/api/transport/ws/ws.js
Normal file
148
js/src/api/transport/ws/ws.js
Normal file
@@ -0,0 +1,148 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 } from 'js-sha3'; // eslint-disable-line camelcase
|
||||
|
||||
import { Logging } from '../../subscriptions';
|
||||
import JsonRpcBase from '../jsonRpcBase';
|
||||
|
||||
/* global WebSocket */
|
||||
export default class Ws extends JsonRpcBase {
|
||||
constructor (url, token) {
|
||||
super();
|
||||
|
||||
this._url = url;
|
||||
this._token = token;
|
||||
this._messages = {};
|
||||
|
||||
this._connecting = true;
|
||||
this._lastError = null;
|
||||
this._autoConnect = false;
|
||||
|
||||
this._connect();
|
||||
}
|
||||
|
||||
updateToken (token) {
|
||||
this._token = token;
|
||||
this._autoConnect = false;
|
||||
|
||||
this._connect();
|
||||
}
|
||||
|
||||
_connect () {
|
||||
const time = parseInt(new Date().getTime() / 1000, 10);
|
||||
const sha3 = keccak_256(`${this._token}:${time}`);
|
||||
const hash = `${sha3}_${time}`;
|
||||
|
||||
if (this._ws) {
|
||||
this._ws.onerror = null;
|
||||
this._ws.onopen = null;
|
||||
this._ws.onclose = null;
|
||||
this._ws.onmessage = null;
|
||||
this._ws = null;
|
||||
}
|
||||
|
||||
this._connecting = true;
|
||||
this._connected = false;
|
||||
this._lastError = null;
|
||||
|
||||
this._ws = new WebSocket(this._url, hash);
|
||||
this._ws.onerror = this._onError;
|
||||
this._ws.onopen = this._onOpen;
|
||||
this._ws.onclose = this._onClose;
|
||||
this._ws.onmessage = this._onMessage;
|
||||
}
|
||||
|
||||
_onOpen = (event) => {
|
||||
console.log('ws:onOpen', event);
|
||||
this._connected = true;
|
||||
this._connecting = false;
|
||||
this._autoConnect = true;
|
||||
|
||||
Object.keys(this._messages)
|
||||
.filter((id) => this._messages[id].queued)
|
||||
.forEach(this._send);
|
||||
}
|
||||
|
||||
_onClose = (event) => {
|
||||
console.log('ws:onClose', event);
|
||||
this._connected = false;
|
||||
this._connecting = false;
|
||||
|
||||
if (this._autoConnect) {
|
||||
this._connect();
|
||||
}
|
||||
}
|
||||
|
||||
_onError = (event) => {
|
||||
console.error('ws:onError', event);
|
||||
this._lastError = event;
|
||||
}
|
||||
|
||||
_onMessage = (event) => {
|
||||
const result = JSON.parse(event.data);
|
||||
const { method, params, json, resolve, reject } = this._messages[result.id];
|
||||
|
||||
Logging.send(method, params, { json, result });
|
||||
|
||||
if (result.error) {
|
||||
this.error(event.data);
|
||||
|
||||
reject(new Error(`${result.error.code}: ${result.error.message}`));
|
||||
delete this._messages[result.id];
|
||||
return;
|
||||
}
|
||||
|
||||
resolve(result.result);
|
||||
delete this._messages[result.id];
|
||||
}
|
||||
|
||||
_send = (id) => {
|
||||
const message = this._messages[id];
|
||||
|
||||
message.queued = !this._connected;
|
||||
|
||||
if (this._connected) {
|
||||
this._ws.send(message.json);
|
||||
}
|
||||
}
|
||||
|
||||
execute (method, ...params) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const id = this.id;
|
||||
const json = this.encode(method, params);
|
||||
|
||||
this._messages[id] = { id, method, params, json, resolve, reject };
|
||||
this._send(id);
|
||||
});
|
||||
}
|
||||
|
||||
get token () {
|
||||
return this._token;
|
||||
}
|
||||
|
||||
get isAutoConnect () {
|
||||
return this._autoConnect;
|
||||
}
|
||||
|
||||
get isConnecting () {
|
||||
return this._connecting;
|
||||
}
|
||||
|
||||
get lastError () {
|
||||
return this._lastError;
|
||||
}
|
||||
}
|
||||
85
js/src/api/transport/ws/ws.spec.js
Normal file
85
js/src/api/transport/ws/ws.spec.js
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { TEST_WS_URL, mockWs } from '../../../../test/mockRpc';
|
||||
import Ws from './ws';
|
||||
|
||||
describe('api/transport/Ws', () => {
|
||||
let transport;
|
||||
let scope;
|
||||
|
||||
describe('transport', () => {
|
||||
let result;
|
||||
|
||||
beforeEach(() => {
|
||||
scope = mockWs([{ method: 'test_anyCall', reply: 'TestResult' }]);
|
||||
transport = new Ws(TEST_WS_URL);
|
||||
|
||||
return transport
|
||||
.execute('test_anyCall', 1, 2, 3)
|
||||
.then((_result) => {
|
||||
result = _result;
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
scope.stop();
|
||||
});
|
||||
|
||||
it('makes call', () => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
});
|
||||
|
||||
it('sets jsonrpc', () => {
|
||||
expect(scope.body.test_anyCall.jsonrpc).to.equal('2.0');
|
||||
});
|
||||
|
||||
it('sets the method', () => {
|
||||
expect(scope.body.test_anyCall.method).to.equal('test_anyCall');
|
||||
});
|
||||
|
||||
it('passes the params', () => {
|
||||
expect(scope.body.test_anyCall.params).to.deep.equal([1, 2, 3]);
|
||||
});
|
||||
|
||||
it('increments the id', () => {
|
||||
expect(scope.body.test_anyCall.id).not.to.equal(0);
|
||||
});
|
||||
|
||||
it('passes the actual result back', () => {
|
||||
expect(result).to.equal('TestResult');
|
||||
});
|
||||
});
|
||||
|
||||
describe('errors', () => {
|
||||
beforeEach(() => {
|
||||
scope = mockWs([{ method: 'test_anyCall', reply: { error: { code: 1, message: 'TestError' } } }]);
|
||||
transport = new Ws(TEST_WS_URL);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
scope.stop();
|
||||
});
|
||||
|
||||
it('returns RPC errors when encountered', () => {
|
||||
return transport
|
||||
.execute('test_anyCall')
|
||||
.catch((error) => {
|
||||
expect(error).to.match(/TestError/);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
83
js/src/api/util/decode.js
Normal file
83
js/src/api/util/decode.js
Normal file
@@ -0,0 +1,83 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { isHex } from './types';
|
||||
|
||||
import Func from '../../abi/spec/function';
|
||||
import { fromParamType, toParamType } from '../../abi/spec/paramType/format';
|
||||
|
||||
export function decodeCallData (data) {
|
||||
if (!isHex(data)) {
|
||||
throw new Error('Input to decodeCallData should be a hex value');
|
||||
}
|
||||
|
||||
if (data.substr(0, 2) === '0x') {
|
||||
return decodeCallData(data.slice(2));
|
||||
} else if (data.length < 8) {
|
||||
throw new Error('Input to decodeCallData should be method signature + data');
|
||||
}
|
||||
|
||||
const signature = data.substr(0, 8);
|
||||
const paramdata = data.substr(8);
|
||||
|
||||
return {
|
||||
signature: `0x${signature}`,
|
||||
paramdata: `0x${paramdata}`
|
||||
};
|
||||
}
|
||||
|
||||
export function decodeMethodInput (methodAbi, paramdata) {
|
||||
if (!methodAbi) {
|
||||
throw new Error('decodeMethodInput should receive valid method-specific ABI');
|
||||
} else if (!paramdata) {
|
||||
throw new Error('decodeMethodInput should receive valid parameter input data');
|
||||
} else if (!isHex(paramdata)) {
|
||||
throw new Error('Input to decodeMethodInput should be a hex value');
|
||||
} else if (paramdata.substr(0, 2) === '0x') {
|
||||
return decodeMethodInput(methodAbi, paramdata.slice(2));
|
||||
} else if (paramdata.length % 64 !== 0) {
|
||||
throw new Error('Parameter length in decodeMethodInput not a multiple of 64 characters');
|
||||
}
|
||||
|
||||
return new Func(methodAbi).decodeInput(paramdata).map((decoded) => decoded.value);
|
||||
}
|
||||
|
||||
// takes a method in form name(...,types) and returns the inferred abi definition
|
||||
export function methodToAbi (method) {
|
||||
const length = method.length;
|
||||
const typesStart = method.indexOf('(');
|
||||
const typesEnd = method.indexOf(')');
|
||||
|
||||
if (typesStart === -1) {
|
||||
throw new Error(`Missing start ( in call to decodeMethod with ${method}`);
|
||||
} else if (typesEnd === -1) {
|
||||
throw new Error(`Missing end ) in call to decodeMethod with ${method}`);
|
||||
} else if (typesEnd < typesStart) {
|
||||
throw new Error(`End ) is before start ( in call to decodeMethod with ${method}`);
|
||||
} else if (typesEnd !== length - 1) {
|
||||
throw new Error(`Extra characters after end ) in call to decodeMethod with ${method}`);
|
||||
}
|
||||
|
||||
const name = method.substr(0, typesStart);
|
||||
const types = method.substr(typesStart + 1, length - (typesStart + 1) - 1).split(',');
|
||||
const inputs = types.map((_type) => {
|
||||
const type = fromParamType(toParamType(_type));
|
||||
|
||||
return { type };
|
||||
});
|
||||
|
||||
return { type: 'function', name, inputs };
|
||||
}
|
||||
101
js/src/api/util/decode.spec.js
Normal file
101
js/src/api/util/decode.spec.js
Normal file
@@ -0,0 +1,101 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 BigNumber from 'bignumber.js';
|
||||
import { decodeCallData, decodeMethodInput, methodToAbi } from './decode';
|
||||
|
||||
describe('api/util/decode', () => {
|
||||
const METH = '0x70a08231';
|
||||
const ENCO = '0x70a082310000000000000000000000005A5eFF38DA95b0D58b6C616f2699168B480953C9';
|
||||
const DATA = '0x0000000000000000000000005A5eFF38DA95b0D58b6C616f2699168B480953C9';
|
||||
|
||||
describe('decodeCallData', () => {
|
||||
it('throws on non-hex inputs', () => {
|
||||
expect(() => decodeCallData('invalid')).to.throw(/should be a hex value/);
|
||||
});
|
||||
|
||||
it('throws when invalid signature length', () => {
|
||||
expect(() => decodeCallData(METH.slice(-6))).to.throw(/should be method signature/);
|
||||
});
|
||||
|
||||
it('splits valid inputs properly', () => {
|
||||
expect(decodeCallData(ENCO)).to.deep.equal({
|
||||
signature: METH,
|
||||
paramdata: DATA
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('decodeMethodInput', () => {
|
||||
it('expects a valid ABI', () => {
|
||||
expect(() => decodeMethodInput(null, null)).to.throw(/should receive valid method/);
|
||||
});
|
||||
|
||||
it('expects valid parameter data', () => {
|
||||
expect(() => decodeMethodInput({}, null)).to.throw(/should receive valid parameter/);
|
||||
});
|
||||
|
||||
it('expect valid hex parameter data', () => {
|
||||
expect(() => decodeMethodInput({}, 'invalid')).to.throw(/should be a hex value/);
|
||||
});
|
||||
|
||||
it('throws on invalid lengths', () => {
|
||||
expect(() => decodeMethodInput({}, DATA.slice(-32))).to.throw(/not a multiple of/);
|
||||
});
|
||||
|
||||
it('correctly decodes valid inputs', () => {
|
||||
expect(decodeMethodInput({
|
||||
type: 'function',
|
||||
inputs: [
|
||||
{ type: 'uint' }
|
||||
]
|
||||
}, DATA)).to.deep.equal([ new BigNumber('0x5a5eff38da95b0d58b6c616f2699168b480953c9') ]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('methodToAbi', () => {
|
||||
it('throws when no start ( specified', () => {
|
||||
expect(() => methodToAbi('invalid,uint,bool)')).to.throw(/Missing start \(/);
|
||||
});
|
||||
|
||||
it('throws when no end ) specified', () => {
|
||||
expect(() => methodToAbi('invalid(uint,bool')).to.throw(/Missing end \)/);
|
||||
});
|
||||
|
||||
it('throws when end ) is not in the last position', () => {
|
||||
expect(() => methodToAbi('invalid(uint,bool)2')).to.throw(/Extra characters after end \)/);
|
||||
});
|
||||
|
||||
it('throws when start ( is after end )', () => {
|
||||
expect(() => methodToAbi('invalid)uint,bool(')).to.throw(/End \) is before start \(/);
|
||||
});
|
||||
|
||||
it('throws when invalid types are present', () => {
|
||||
expect(() => methodToAbi('method(invalidType,bool,uint)')).to.throw(/Cannot convert invalidType/);
|
||||
});
|
||||
|
||||
it('returns a valid methodabi for a valid method', () => {
|
||||
expect(methodToAbi('valid(uint,bool)')).to.deep.equals({
|
||||
type: 'function',
|
||||
name: 'valid',
|
||||
inputs: [
|
||||
{ type: 'uint256' },
|
||||
{ type: 'bool' }
|
||||
]
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
19
js/src/api/util/format.js
Normal file
19
js/src/api/util/format.js
Normal file
@@ -0,0 +1,19 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 function bytesToHex (bytes) {
|
||||
return '0x' + bytes.map((b) => ('0' + b.toString(16)).slice(-2)).join('');
|
||||
}
|
||||
29
js/src/api/util/format.spec.js
Normal file
29
js/src/api/util/format.spec.js
Normal file
@@ -0,0 +1,29 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { bytesToHex } from './format';
|
||||
|
||||
describe('api/util/format', () => {
|
||||
describe('bytesToHex', () => {
|
||||
it('correctly converts an empty array', () => {
|
||||
expect(bytesToHex([])).to.equal('0x');
|
||||
});
|
||||
|
||||
it('correctly converts a non-empty array', () => {
|
||||
expect(bytesToHex([0, 15, 16])).to.equal('0x000f10');
|
||||
});
|
||||
});
|
||||
});
|
||||
9
js/src/api/util/identity.js
Normal file
9
js/src/api/util/identity.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import blockies from 'blockies';
|
||||
|
||||
export function createIdentityImg (address, scale = 8) {
|
||||
return blockies({
|
||||
seed: (address || '').toLowerCase(),
|
||||
size: 8,
|
||||
scale
|
||||
}).toDataURL();
|
||||
}
|
||||
41
js/src/api/util/index.js
Normal file
41
js/src/api/util/index.js
Normal file
@@ -0,0 +1,41 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { isAddress as isAddressValid, toChecksumAddress } from '../../abi/util/address';
|
||||
import { decodeCallData, decodeMethodInput, methodToAbi } from './decode';
|
||||
import { bytesToHex } from './format';
|
||||
import { fromWei, toWei } from './wei';
|
||||
import { sha3 } from './sha3';
|
||||
import { isArray, isFunction, isHex, isInstanceOf, isString } from './types';
|
||||
import { createIdentityImg } from './identity';
|
||||
|
||||
export default {
|
||||
isAddressValid,
|
||||
isArray,
|
||||
isFunction,
|
||||
isHex,
|
||||
isInstanceOf,
|
||||
isString,
|
||||
bytesToHex,
|
||||
createIdentityImg,
|
||||
decodeCallData,
|
||||
decodeMethodInput,
|
||||
methodToAbi,
|
||||
fromWei,
|
||||
toChecksumAddress,
|
||||
toWei,
|
||||
sha3
|
||||
};
|
||||
21
js/src/api/util/sha3.js
Normal file
21
js/src/api/util/sha3.js
Normal file
@@ -0,0 +1,21 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 } from 'js-sha3'; // eslint-disable-line camelcase
|
||||
|
||||
export function sha3 (value) {
|
||||
return `0x${keccak_256(value)}`;
|
||||
}
|
||||
25
js/src/api/util/sha3.spec.js
Normal file
25
js/src/api/util/sha3.spec.js
Normal file
@@ -0,0 +1,25 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { sha3 } from './sha3';
|
||||
|
||||
describe('api/util/sha3', () => {
|
||||
describe('sha3', () => {
|
||||
it('constructs a correct sha3 value', () => {
|
||||
expect(sha3('jacogr')).to.equal('0x2f4ff4b5a87abbd2edfed699db48a97744e028c7f7ce36444d40d29d792aa4dc');
|
||||
});
|
||||
});
|
||||
});
|
||||
56
js/src/api/util/types.js
Normal file
56
js/src/api/util/types.js
Normal file
@@ -0,0 +1,56 @@
|
||||
// Copyright 2015, 2016 Ethcore (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/>.
|
||||
|
||||
const HEXDIGITS = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
|
||||
|
||||
export function isArray (test) {
|
||||
return Object.prototype.toString.call(test) === '[object Array]';
|
||||
}
|
||||
|
||||
export function isError (test) {
|
||||
return Object.prototype.toString.call(test) === '[object Error]';
|
||||
}
|
||||
|
||||
export function isFunction (test) {
|
||||
return Object.prototype.toString.call(test) === '[object Function]';
|
||||
}
|
||||
|
||||
export function isHex (_test) {
|
||||
if (_test.substr(0, 2) === '0x') {
|
||||
return isHex(_test.slice(2));
|
||||
}
|
||||
|
||||
const test = _test.toLowerCase();
|
||||
let hex = true;
|
||||
|
||||
for (let idx = 0; hex && idx < test.length; idx++) {
|
||||
hex = HEXDIGITS.includes(test[idx]);
|
||||
}
|
||||
|
||||
return hex;
|
||||
}
|
||||
|
||||
export function isObject (test) {
|
||||
return Object.prototype.toString.call(test) === '[object Object]';
|
||||
}
|
||||
|
||||
export function isString (test) {
|
||||
return Object.prototype.toString.call(test) === '[object String]';
|
||||
}
|
||||
|
||||
export function isInstanceOf (test, clazz) {
|
||||
return test instanceof clazz;
|
||||
}
|
||||
112
js/src/api/util/types.spec.js
Normal file
112
js/src/api/util/types.spec.js
Normal file
@@ -0,0 +1,112 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 sinon from 'sinon';
|
||||
|
||||
import { isArray, isError, isFunction, isHex, isInstanceOf, isObject, isString } from './types';
|
||||
import Eth from '../rpc/eth';
|
||||
|
||||
describe('api/util/types', () => {
|
||||
describe('isArray', () => {
|
||||
it('correctly identifies null as false', () => {
|
||||
expect(isArray(null)).to.be.false;
|
||||
});
|
||||
|
||||
it('correctly identifies empty array as true', () => {
|
||||
expect(isArray([])).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly identifies array as true', () => {
|
||||
expect(isArray([1, 2, 3])).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('isError', () => {
|
||||
it('correctly identifies null as false', () => {
|
||||
expect(isError(null)).to.be.false;
|
||||
});
|
||||
|
||||
it('correctly identifies Error as true', () => {
|
||||
expect(isError(new Error('an error'))).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('isFunction', () => {
|
||||
it('correctly identifies null as false', () => {
|
||||
expect(isFunction(null)).to.be.false;
|
||||
});
|
||||
|
||||
it('correctly identifies function as true', () => {
|
||||
expect(isFunction(sinon.stub())).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('isHex', () => {
|
||||
it('correctly identifies hex by leading 0x', () => {
|
||||
expect(isHex('0x123')).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly identifies hex without leading 0x', () => {
|
||||
expect(isHex('123')).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly identifies non-hex values', () => {
|
||||
expect(isHex('123j')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('isInstanceOf', () => {
|
||||
it('correctly identifies build-in instanceof', () => {
|
||||
expect(isInstanceOf(new String('123'), String)).to.be.true; // eslint-disable-line no-new-wrappers
|
||||
});
|
||||
|
||||
it('correctly identifies own instanceof', () => {
|
||||
expect(isInstanceOf(new Eth({}), Eth)).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly reports false for own', () => {
|
||||
expect(isInstanceOf({}, Eth)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('isObject', () => {
|
||||
it('correctly identifies empty object as object', () => {
|
||||
expect(isObject({})).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly identifies non-empty object as object', () => {
|
||||
expect(isObject({ data: '123' })).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly identifies Arrays as non-objects', () => {
|
||||
expect(isObject([1, 2, 3])).to.be.false;
|
||||
});
|
||||
|
||||
it('correctly identifies Strings as non-objects', () => {
|
||||
expect(isObject('123')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('isString', () => {
|
||||
it('correctly identifies empty string as string', () => {
|
||||
expect(isString('')).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly identifies string as string', () => {
|
||||
expect(isString('123')).to.be.true;
|
||||
});
|
||||
});
|
||||
});
|
||||
37
js/src/api/util/wei.js
Normal file
37
js/src/api/util/wei.js
Normal file
@@ -0,0 +1,37 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 BigNumber from 'bignumber.js';
|
||||
|
||||
const UNITS = ['wei', 'ada', 'babbage', 'shannon', 'szabo', 'finney', 'ether', 'kether', 'mether', 'gether', 'tether'];
|
||||
|
||||
export function _getUnitMultiplier (unit) {
|
||||
const position = UNITS.indexOf(unit.toLowerCase());
|
||||
|
||||
if (position === -1) {
|
||||
throw new Error(`Unknown unit ${unit} passed to wei formatter`);
|
||||
}
|
||||
|
||||
return 10 ** (position * 3);
|
||||
}
|
||||
|
||||
export function fromWei (value, unit = 'ether') {
|
||||
return new BigNumber(value).div(_getUnitMultiplier(unit));
|
||||
}
|
||||
|
||||
export function toWei (value, unit = 'ether') {
|
||||
return new BigNumber(value).mul(_getUnitMultiplier(unit));
|
||||
}
|
||||
57
js/src/api/util/wei.spec.js
Normal file
57
js/src/api/util/wei.spec.js
Normal file
@@ -0,0 +1,57 @@
|
||||
// Copyright 2015, 2016 Ethcore (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 { _getUnitMultiplier, fromWei, toWei } from './wei';
|
||||
|
||||
describe('api/util/wei', () => {
|
||||
describe('_getUnitMultiplier', () => {
|
||||
it('returns 10^0 for wei', () => {
|
||||
expect(_getUnitMultiplier('wei')).to.equal(10 ** 0);
|
||||
});
|
||||
|
||||
it('returns 10^15 for finney', () => {
|
||||
expect(_getUnitMultiplier('finney')).to.equal(10 ** 15);
|
||||
});
|
||||
|
||||
it('returns 10^18 for ether', () => {
|
||||
expect(_getUnitMultiplier('ether')).to.equal(10 ** 18);
|
||||
});
|
||||
|
||||
it('throws an error on invalid units', () => {
|
||||
expect(() => _getUnitMultiplier('invalid')).to.throw(/passed to wei formatter/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fromWei', () => {
|
||||
it('formats into ether when nothing specified', () => {
|
||||
expect(fromWei('1230000000000000000').toString()).to.equal('1.23');
|
||||
});
|
||||
|
||||
it('formats into finney when specified', () => {
|
||||
expect(fromWei('1230000000000000000', 'finney').toString()).to.equal('1230');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toWei', () => {
|
||||
it('formats from ether when nothing specified', () => {
|
||||
expect(toWei(1.23).toString()).to.equal('1230000000000000000');
|
||||
});
|
||||
|
||||
it('formats from finney when specified', () => {
|
||||
expect(toWei(1230, 'finney').toString()).to.equal('1230000000000000000');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user