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:
73
js/src/3rdparty/etherscan/account.js
vendored
Normal file
73
js/src/3rdparty/etherscan/account.js
vendored
Normal file
@@ -0,0 +1,73 @@
|
||||
// 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 PAGE_SIZE = 25;
|
||||
|
||||
import util from '../../api/util';
|
||||
import { call } from './call';
|
||||
|
||||
function _call (method, params, test) {
|
||||
return call('account', method, params, test);
|
||||
}
|
||||
|
||||
function balance (address, test = false) {
|
||||
return _call('balance', {
|
||||
address: address,
|
||||
tag: 'latest'
|
||||
}, test).then((balance) => {
|
||||
// same format as balancemulti below
|
||||
return {
|
||||
account: address,
|
||||
balance: balance
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function balances (addresses, test = false) {
|
||||
return _call('balancemulti', {
|
||||
address: addresses.join(','),
|
||||
tag: 'latest'
|
||||
}, test);
|
||||
}
|
||||
|
||||
function transactions (address, page, test = false) {
|
||||
// page offset from 0
|
||||
return _call('txlist', {
|
||||
address: address,
|
||||
page: (page || 0) + 1,
|
||||
offset: PAGE_SIZE,
|
||||
sort: 'desc'
|
||||
}, test).then((transactions) => {
|
||||
return transactions.map((tx) => {
|
||||
return {
|
||||
from: util.toChecksumAddress(tx.from),
|
||||
to: util.toChecksumAddress(tx.to),
|
||||
hash: tx.hash,
|
||||
blockNumber: tx.blockNumber,
|
||||
timeStamp: tx.timeStamp,
|
||||
value: tx.value
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const account = {
|
||||
balance: balance,
|
||||
balances: balances,
|
||||
transactions: transactions
|
||||
};
|
||||
|
||||
export { account };
|
||||
69
js/src/3rdparty/etherscan/account.spec.js
vendored
Normal file
69
js/src/3rdparty/etherscan/account.spec.js
vendored
Normal file
@@ -0,0 +1,69 @@
|
||||
// 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 etherscan from './';
|
||||
|
||||
const TESTADDR = '0xbf885e2b55c6bcc84556a3c5f07d3040833c8d00';
|
||||
|
||||
describe.skip('etherscan/account', () => {
|
||||
const checkBalance = function (balance, addr) {
|
||||
expect(balance).to.be.ok;
|
||||
expect(balance.account).to.equal(addr);
|
||||
expect(balance.balance).to.be.ok;
|
||||
};
|
||||
|
||||
it('retrieves an account balance', () => {
|
||||
return etherscan.account
|
||||
.balance(TESTADDR)
|
||||
.then((balance) => {
|
||||
checkBalance(balance, TESTADDR);
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieves multi account balances', () => {
|
||||
const addresses = ['0xde0b295669a9fd93d5f28d9ec85e40f4cb697bae', TESTADDR];
|
||||
|
||||
return etherscan.account
|
||||
.balances(addresses)
|
||||
.then((balances) => {
|
||||
expect(balances).to.be.ok;
|
||||
expect(balances.length).to.equal(2);
|
||||
balances.forEach((balance, idx) => {
|
||||
checkBalance(balance, addresses[idx]);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('transactions', () => {
|
||||
it('retrieves a list of transactions (default)', () => {
|
||||
return etherscan.account
|
||||
.transactions(TESTADDR)
|
||||
.then((transactions) => {
|
||||
expect(transactions).to.be.ok;
|
||||
expect(transactions.length).to.equal(25);
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieves a list of transactions (page 1)', () => {
|
||||
return etherscan.account
|
||||
.transactions(TESTADDR, 1)
|
||||
.then((transactions) => {
|
||||
expect(transactions).to.be.ok;
|
||||
expect(transactions.length).to.equal(25);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
51
js/src/3rdparty/etherscan/call.js
vendored
Normal file
51
js/src/3rdparty/etherscan/call.js
vendored
Normal file
@@ -0,0 +1,51 @@
|
||||
// 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 options = {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
};
|
||||
|
||||
export function call (module, action, _params, test) {
|
||||
const host = test ? 'testnet.etherscan.io' : 'api.etherscan.io';
|
||||
let params = '';
|
||||
|
||||
if (_params) {
|
||||
Object.keys(_params).map((param) => {
|
||||
const value = _params[param];
|
||||
|
||||
params = `${params}&${param}=${value}`;
|
||||
});
|
||||
}
|
||||
|
||||
return fetch(`http://${host}/api?module=${module}&action=${action}${params}`, options)
|
||||
.then((response) => {
|
||||
if (response.status !== 200) {
|
||||
throw { code: response.status, message: response.statusText }; // eslint-disable-line
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.message === 'NOTOK') {
|
||||
throw { code: -1, message: result.result }; // eslint-disable-line
|
||||
}
|
||||
|
||||
return result.result;
|
||||
});
|
||||
}
|
||||
25
js/src/3rdparty/etherscan/index.js
vendored
Normal file
25
js/src/3rdparty/etherscan/index.js
vendored
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 { account } from './account';
|
||||
import { stats } from './stats';
|
||||
|
||||
const etherscan = {
|
||||
account: account,
|
||||
stats: stats
|
||||
};
|
||||
|
||||
export default etherscan;
|
||||
36
js/src/3rdparty/etherscan/stats.js
vendored
Normal file
36
js/src/3rdparty/etherscan/stats.js
vendored
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 { call } from './call';
|
||||
|
||||
function _call (action, test) {
|
||||
return call('stats', action, null, test);
|
||||
}
|
||||
|
||||
function price (test = false) {
|
||||
return _call('ethprice', test);
|
||||
}
|
||||
|
||||
function supply (test = false) {
|
||||
return _call('ethsupply', test);
|
||||
}
|
||||
|
||||
const stats = {
|
||||
price: price,
|
||||
supply: supply
|
||||
};
|
||||
|
||||
export { stats };
|
||||
35
js/src/3rdparty/etherscan/stats.spec.js
vendored
Normal file
35
js/src/3rdparty/etherscan/stats.spec.js
vendored
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 etherscan from './';
|
||||
|
||||
describe.skip('etherscan/stats', () => {
|
||||
it('retrieves the latest price', () => {
|
||||
return etherscan.stats
|
||||
.price()
|
||||
.then((price) => {
|
||||
expect(price).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieves the ether total', () => {
|
||||
return etherscan.stats
|
||||
.supply()
|
||||
.then((supply) => {
|
||||
expect(supply).to.be.ok;
|
||||
});
|
||||
});
|
||||
});
|
||||
71
js/src/3rdparty/shapeshift/helpers.spec.js
vendored
Normal file
71
js/src/3rdparty/shapeshift/helpers.spec.js
vendored
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/>.
|
||||
|
||||
import chai from 'chai';
|
||||
import nock from 'nock';
|
||||
|
||||
global.expect = chai.expect; // eslint-disable-line no-undef
|
||||
|
||||
import 'isomorphic-fetch';
|
||||
import es6Promise from 'es6-promise';
|
||||
es6Promise.polyfill();
|
||||
|
||||
import initShapeshift from './';
|
||||
import initRpc from './rpc';
|
||||
|
||||
const APIKEY = '0x123454321';
|
||||
|
||||
const shapeshift = initShapeshift(APIKEY);
|
||||
const rpc = initRpc(APIKEY);
|
||||
|
||||
function mockget (requests) {
|
||||
let scope = nock(rpc.ENDPOINT);
|
||||
|
||||
requests.forEach((request) => {
|
||||
scope = scope
|
||||
.get(`/${request.path}`)
|
||||
.reply(request.code || 200, () => {
|
||||
return request.reply;
|
||||
});
|
||||
});
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
function mockpost (requests) {
|
||||
let scope = nock(rpc.ENDPOINT);
|
||||
|
||||
requests.forEach((request) => {
|
||||
scope = scope
|
||||
.post(`/${request.path}`)
|
||||
.reply(request.code || 200, (uri, body) => {
|
||||
scope.body = scope.body || {};
|
||||
scope.body[request.path] = body;
|
||||
|
||||
return request.reply;
|
||||
});
|
||||
});
|
||||
|
||||
return scope;
|
||||
}
|
||||
|
||||
export {
|
||||
APIKEY,
|
||||
mockget,
|
||||
mockpost,
|
||||
shapeshift,
|
||||
rpc
|
||||
};
|
||||
22
js/src/3rdparty/shapeshift/index.js
vendored
Normal file
22
js/src/3rdparty/shapeshift/index.js
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
// 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 initRpc from './rpc';
|
||||
import initShapeshift from './shapeshift';
|
||||
|
||||
export default function (apikey) {
|
||||
return initShapeshift(initRpc(apikey));
|
||||
}
|
||||
67
js/src/3rdparty/shapeshift/rpc.js
vendored
Normal file
67
js/src/3rdparty/shapeshift/rpc.js
vendored
Normal file
@@ -0,0 +1,67 @@
|
||||
// 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 ENDPOINT = 'https://cors.shapeshift.io';
|
||||
|
||||
function call (method, options) {
|
||||
return fetch(`${ENDPOINT}/${method}`, options)
|
||||
.then((response) => {
|
||||
if (!response.ok) {
|
||||
throw new Error(response.statusText);
|
||||
}
|
||||
|
||||
return response.json();
|
||||
})
|
||||
.then((result) => {
|
||||
if (result.error) {
|
||||
throw new Error(result.error);
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
export default function (apiKey) {
|
||||
function get (method) {
|
||||
return call(method, {
|
||||
method: 'GET',
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function post (method, data) {
|
||||
const params = Object.assign({}, { apiKey }, data);
|
||||
const body = JSON.stringify(params);
|
||||
|
||||
return call(method, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Accept': 'application/json',
|
||||
'Content-Type': 'application/json',
|
||||
'Content-Length': body.length
|
||||
},
|
||||
body
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
ENDPOINT,
|
||||
get,
|
||||
post
|
||||
};
|
||||
}
|
||||
77
js/src/3rdparty/shapeshift/rpc.spec.js
vendored
Normal file
77
js/src/3rdparty/shapeshift/rpc.spec.js
vendored
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/>.
|
||||
|
||||
import { APIKEY, mockget, mockpost, rpc } from './helpers.spec.js';
|
||||
|
||||
describe('shapeshift/rpc', () => {
|
||||
describe('GET', () => {
|
||||
const REPLY = { test: 'this is some result' };
|
||||
|
||||
let scope;
|
||||
let result;
|
||||
|
||||
beforeEach(() => {
|
||||
scope = mockget([{ path: 'test', reply: REPLY }]);
|
||||
|
||||
return rpc
|
||||
.get('test')
|
||||
.then((_result) => {
|
||||
result = _result;
|
||||
});
|
||||
});
|
||||
|
||||
it('does GET', () => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
});
|
||||
|
||||
it('retrieves the info', () => {
|
||||
expect(result).to.deep.equal(REPLY);
|
||||
});
|
||||
});
|
||||
|
||||
describe('POST', () => {
|
||||
const REPLY = { test: 'this is some result' };
|
||||
|
||||
let scope;
|
||||
let result;
|
||||
|
||||
beforeEach(() => {
|
||||
scope = mockpost([{ path: 'test', reply: REPLY }]);
|
||||
|
||||
return rpc
|
||||
.post('test', { input: 'stuff' })
|
||||
.then((_result) => {
|
||||
result = _result;
|
||||
});
|
||||
});
|
||||
|
||||
it('does POST', () => {
|
||||
expect(scope.isDone()).to.be.true;
|
||||
});
|
||||
|
||||
it('retrieves the info', () => {
|
||||
expect(result).to.deep.equal(REPLY);
|
||||
});
|
||||
|
||||
it('passes the input object', () => {
|
||||
expect(scope.body.test.input).to.equal('stuff');
|
||||
});
|
||||
|
||||
it('passes the apikey specified', () => {
|
||||
expect(scope.body.test.apiKey).to.equal(APIKEY);
|
||||
});
|
||||
});
|
||||
});
|
||||
93
js/src/3rdparty/shapeshift/shapeshift.js
vendored
Normal file
93
js/src/3rdparty/shapeshift/shapeshift.js
vendored
Normal file
@@ -0,0 +1,93 @@
|
||||
// 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 function (rpc) {
|
||||
const subscriptions = [];
|
||||
|
||||
function getCoins () {
|
||||
return rpc.get('getcoins');
|
||||
}
|
||||
|
||||
function getMarketInfo (pair) {
|
||||
return rpc.get(`marketinfo/${pair}`);
|
||||
}
|
||||
|
||||
function getStatus (depositAddress) {
|
||||
return rpc.get(`txStat/${depositAddress}`);
|
||||
}
|
||||
|
||||
function shift (toAddress, returnAddress, pair) {
|
||||
return rpc.post('shift', {
|
||||
withdrawal: toAddress,
|
||||
pair: pair,
|
||||
returnAddress: returnAddress
|
||||
});
|
||||
}
|
||||
|
||||
function subscribe (depositAddress, callback) {
|
||||
const idx = subscriptions.length;
|
||||
|
||||
subscriptions.push({
|
||||
depositAddress,
|
||||
callback,
|
||||
idx
|
||||
});
|
||||
}
|
||||
|
||||
function _getSubscriptionStatus (subscription) {
|
||||
if (!subscription) {
|
||||
return;
|
||||
}
|
||||
|
||||
getStatus(subscription.depositAddress)
|
||||
.then((result) => {
|
||||
switch (result.status) {
|
||||
case 'no_deposits':
|
||||
case 'received':
|
||||
subscription.callback(null, result);
|
||||
return;
|
||||
|
||||
case 'complete':
|
||||
subscription.callback(null, result);
|
||||
subscriptions[subscription.idx] = null;
|
||||
return;
|
||||
|
||||
case 'failed':
|
||||
subscription.callback({
|
||||
message: status.error,
|
||||
fatal: true
|
||||
});
|
||||
subscriptions[subscription.idx] = null;
|
||||
return;
|
||||
}
|
||||
})
|
||||
.catch(subscription.callback);
|
||||
}
|
||||
|
||||
function _pollStatus () {
|
||||
subscriptions.forEach(_getSubscriptionStatus);
|
||||
}
|
||||
|
||||
setInterval(_pollStatus, 2000);
|
||||
|
||||
return {
|
||||
getCoins,
|
||||
getMarketInfo,
|
||||
getStatus,
|
||||
shift,
|
||||
subscribe
|
||||
};
|
||||
}
|
||||
124
js/src/3rdparty/shapeshift/shapeshift.spec.js
vendored
Normal file
124
js/src/3rdparty/shapeshift/shapeshift.spec.js
vendored
Normal file
@@ -0,0 +1,124 @@
|
||||
// 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 { mockget, mockpost, shapeshift } from './helpers.spec.js';
|
||||
|
||||
describe('shapeshift/calls', () => {
|
||||
describe('getCoins', () => {
|
||||
const REPLY = {
|
||||
BTC: {
|
||||
name: 'Bitcoin',
|
||||
symbol: 'BTC',
|
||||
image: 'https://shapeshift.io/images/coins/bitcoin.png',
|
||||
status: 'available'
|
||||
},
|
||||
ETH: {
|
||||
name: 'Ether',
|
||||
symbol: 'ETH',
|
||||
image: 'https://shapeshift.io/images/coins/ether.png',
|
||||
status: 'available'
|
||||
}
|
||||
};
|
||||
|
||||
let scope;
|
||||
|
||||
before(() => {
|
||||
scope = mockget([{ path: 'getcoins', reply: REPLY }]);
|
||||
|
||||
return shapeshift.getCoins();
|
||||
});
|
||||
|
||||
it('makes the call', () => {
|
||||
expect(scope.isDone()).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
describe('getMarketInfo', () => {
|
||||
const REPLY = {
|
||||
pair: 'btc_ltc',
|
||||
rate: 128.17959917,
|
||||
minerFee: 0.003,
|
||||
limit: 0,
|
||||
minimum: 0.00004632
|
||||
};
|
||||
|
||||
let scope;
|
||||
|
||||
before(() => {
|
||||
scope = mockget([{ path: 'marketinfo/btc_ltc', reply: REPLY }]);
|
||||
|
||||
return shapeshift.getMarketInfo('btc_ltc');
|
||||
});
|
||||
|
||||
it('makes the call', () => {
|
||||
expect(scope.isDone()).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
describe('getStatus', () => {
|
||||
const REPLY = {
|
||||
status: '0x123',
|
||||
address: '0x123'
|
||||
};
|
||||
|
||||
let scope;
|
||||
|
||||
before(() => {
|
||||
scope = mockget([{ path: 'txStat/0x123', reply: REPLY }]);
|
||||
|
||||
return shapeshift.getStatus('0x123');
|
||||
});
|
||||
|
||||
it('makes the call', () => {
|
||||
expect(scope.isDone()).to.be.ok;
|
||||
});
|
||||
});
|
||||
|
||||
describe('shift', () => {
|
||||
const REPLY = {
|
||||
deposit: '1BTC',
|
||||
depositType: 'btc',
|
||||
withdrawal: '0x456',
|
||||
withdrawalType: 'eth'
|
||||
};
|
||||
|
||||
let scope;
|
||||
|
||||
before(() => {
|
||||
scope = mockpost([{ path: 'shift', reply: REPLY }]);
|
||||
|
||||
return shapeshift.shift('0x456', '1BTC', 'btc_eth');
|
||||
});
|
||||
|
||||
it('makes the call', () => {
|
||||
expect(scope.isDone()).to.be.ok;
|
||||
});
|
||||
|
||||
describe('body', () => {
|
||||
it('has withdrawal set', () => {
|
||||
expect(scope.body.shift.withdrawal).to.equal('0x456');
|
||||
});
|
||||
|
||||
it('has returnAddress set', () => {
|
||||
expect(scope.body.shift.returnAddress).to.equal('1BTC');
|
||||
});
|
||||
|
||||
it('has pair set', () => {
|
||||
expect(scope.body.shift.pair).to.equal('btc_eth');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
32
js/src/abi/README.md
Normal file
32
js/src/abi/README.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# ethabi-js
|
||||
|
||||
A very early, very POC-type port of [https://github.com/ethcore/ethabi](https://github.com/ethcore/ethabi) to JavaScript
|
||||
|
||||
[](https://travis-ci.org/jacogr/ethabi-js)
|
||||
[](https://coveralls.io/github/jacogr/ethabi-js?branch=master)
|
||||
[](https://david-dm.org/jacogr/ethabi-js)
|
||||
[](https://david-dm.org/jacogr/ethabi-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)
|
||||
|
||||
## installation
|
||||
|
||||
Install the package with `npm install --save ethabi-js` from the [npm registry ethabi-js](https://www.npmjs.com/package/ethabi-js)
|
||||
|
||||
|
||||
## implementation
|
||||
### approach
|
||||
|
||||
- this version tries to stay as close to the original Rust version in intent, function names & purpose
|
||||
- it is a basic port of the Rust version, relying on effectively the same test-suite (expanded where deemed appropriate)
|
||||
- it is meant as a library to be used in other projects, i.e. [ethapi-js](https://www.npmjs.com/package/ethapi-js)
|
||||
|
||||
### differences to original Rust version
|
||||
|
||||
- internally the library operates on string binary representations as opposed to Vector bytes, lengths are therefore 64 bytes as opposed to 32 bytes
|
||||
- function names are adapted from the Rust standard snake_case to the JavaScript standard camelCase
|
||||
- due to the initial library focus, the cli component (as implemented by the original) is not supported nor mplemented
|
||||
20
js/src/abi/abi.js
Normal file
20
js/src/abi/abi.js
Normal file
@@ -0,0 +1,20 @@
|
||||
// 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 Interface from './spec/interface';
|
||||
|
||||
export default class Abi extends Interface {
|
||||
}
|
||||
30
js/src/abi/decoder/bytesTaken.js
Normal file
30
js/src/abi/decoder/bytesTaken.js
Normal file
@@ -0,0 +1,30 @@
|
||||
// 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 BytesTaken {
|
||||
constructor (bytes, newOffset) {
|
||||
this._bytes = bytes;
|
||||
this._newOffset = newOffset;
|
||||
}
|
||||
|
||||
get bytes () {
|
||||
return this._bytes;
|
||||
}
|
||||
|
||||
get newOffset () {
|
||||
return this._newOffset;
|
||||
}
|
||||
}
|
||||
29
js/src/abi/decoder/bytesTaken.spec.js
Normal file
29
js/src/abi/decoder/bytesTaken.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 BytesTaken from './bytesTaken';
|
||||
|
||||
describe('abi/decoder/BytesTaken', () => {
|
||||
describe('constructor', () => {
|
||||
it('sets the bytes of the object', () => {
|
||||
expect((new BytesTaken(1, 2)).bytes).to.equal(1);
|
||||
});
|
||||
|
||||
it('sets the newOffset of the object', () => {
|
||||
expect((new BytesTaken(3, 4)).newOffset).to.equal(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
30
js/src/abi/decoder/decodeResult.js
Normal file
30
js/src/abi/decoder/decodeResult.js
Normal file
@@ -0,0 +1,30 @@
|
||||
// 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 DecodeResult {
|
||||
constructor (token, newOffset) {
|
||||
this._token = token;
|
||||
this._newOffset = newOffset;
|
||||
}
|
||||
|
||||
get token () {
|
||||
return this._token;
|
||||
}
|
||||
|
||||
get newOffset () {
|
||||
return this._newOffset;
|
||||
}
|
||||
}
|
||||
29
js/src/abi/decoder/decodeResult.spec.js
Normal file
29
js/src/abi/decoder/decodeResult.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 DecodeResult from './decodeResult';
|
||||
|
||||
describe('abi/decoder/DecodeResult', () => {
|
||||
describe('constructor', () => {
|
||||
it('sets the token of the object', () => {
|
||||
expect((new DecodeResult('token', 2)).token).to.equal('token');
|
||||
});
|
||||
|
||||
it('sets the newOffset of the object', () => {
|
||||
expect((new DecodeResult('baz', 4)).newOffset).to.equal(4);
|
||||
});
|
||||
});
|
||||
});
|
||||
145
js/src/abi/decoder/decoder.js
Normal file
145
js/src/abi/decoder/decoder.js
Normal file
@@ -0,0 +1,145 @@
|
||||
// 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 utf8 from 'utf8';
|
||||
|
||||
import Token from '../token/token';
|
||||
import BytesTaken from './bytesTaken';
|
||||
import DecodeResult from './decodeResult';
|
||||
import ParamType from '../spec/paramType/paramType';
|
||||
import { sliceData } from '../util/slice';
|
||||
import { asAddress, asBool, asI32, asU32 } from '../util/sliceAs';
|
||||
import { isArray, isInstanceOf } from '../util/types';
|
||||
|
||||
const NULL = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||
|
||||
export default class Decoder {
|
||||
static decode (params, data) {
|
||||
if (!isArray(params)) {
|
||||
throw new Error('Parameters should be array of ParamType');
|
||||
}
|
||||
|
||||
const slices = sliceData(data);
|
||||
let offset = 0;
|
||||
|
||||
return params.map((param) => {
|
||||
const result = Decoder.decodeParam(param, slices, offset);
|
||||
offset = result.newOffset;
|
||||
return result.token;
|
||||
});
|
||||
}
|
||||
|
||||
static peek (slices, position) {
|
||||
if (!slices || !slices[position]) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return slices[position];
|
||||
}
|
||||
|
||||
static takeBytes (slices, position, length) {
|
||||
const slicesLength = Math.floor((length + 31) / 32);
|
||||
let bytesStr = '';
|
||||
|
||||
for (let idx = 0; idx < slicesLength; idx++) {
|
||||
bytesStr = `${bytesStr}${Decoder.peek(slices, position + idx)}`;
|
||||
}
|
||||
|
||||
const bytes = (bytesStr.substr(0, length * 2).match(/.{1,2}/g) || []).map((code) => parseInt(code, 16));
|
||||
|
||||
return new BytesTaken(bytes, position + slicesLength);
|
||||
}
|
||||
|
||||
static decodeParam (param, slices, offset) {
|
||||
if (!isInstanceOf(param, ParamType)) {
|
||||
throw new Error('param should be instanceof ParamType');
|
||||
}
|
||||
|
||||
const tokens = [];
|
||||
let taken;
|
||||
let lengthOffset;
|
||||
let length;
|
||||
let newOffset;
|
||||
|
||||
switch (param.type) {
|
||||
case 'address':
|
||||
return new DecodeResult(new Token(param.type, asAddress(Decoder.peek(slices, offset))), offset + 1);
|
||||
|
||||
case 'bool':
|
||||
return new DecodeResult(new Token(param.type, asBool(Decoder.peek(slices, offset))), offset + 1);
|
||||
|
||||
case 'int':
|
||||
return new DecodeResult(new Token(param.type, asI32(Decoder.peek(slices, offset))), offset + 1);
|
||||
|
||||
case 'uint':
|
||||
return new DecodeResult(new Token(param.type, asU32(Decoder.peek(slices, offset))), offset + 1);
|
||||
|
||||
case 'fixedBytes':
|
||||
taken = Decoder.takeBytes(slices, offset, param.length);
|
||||
|
||||
return new DecodeResult(new Token(param.type, taken.bytes), taken.newOffset);
|
||||
|
||||
case 'bytes':
|
||||
lengthOffset = asU32(Decoder.peek(slices, offset)).div(32).toNumber();
|
||||
length = asU32(Decoder.peek(slices, lengthOffset)).toNumber();
|
||||
taken = Decoder.takeBytes(slices, lengthOffset + 1, length);
|
||||
|
||||
return new DecodeResult(new Token(param.type, taken.bytes), offset + 1);
|
||||
|
||||
case 'string':
|
||||
if (param.indexed) {
|
||||
taken = Decoder.takeBytes(slices, offset, 32);
|
||||
|
||||
return new DecodeResult(new Token('fixedBytes', taken.bytes), offset + 1);
|
||||
}
|
||||
|
||||
lengthOffset = asU32(Decoder.peek(slices, offset)).div(32).toNumber();
|
||||
length = asU32(Decoder.peek(slices, lengthOffset)).toNumber();
|
||||
taken = Decoder.takeBytes(slices, lengthOffset + 1, length);
|
||||
|
||||
const str = taken.bytes.map((code) => String.fromCharCode(code)).join('');
|
||||
|
||||
return new DecodeResult(new Token(param.type, utf8.decode(str)), offset + 1);
|
||||
|
||||
case 'array':
|
||||
lengthOffset = asU32(Decoder.peek(slices, offset)).div(32).toNumber();
|
||||
length = asU32(Decoder.peek(slices, lengthOffset)).toNumber();
|
||||
newOffset = lengthOffset + 1;
|
||||
|
||||
for (let idx = 0; idx < length; idx++) {
|
||||
const result = Decoder.decodeParam(param.subtype, slices, newOffset);
|
||||
newOffset = result.newOffset;
|
||||
tokens.push(result.token);
|
||||
}
|
||||
|
||||
return new DecodeResult(new Token(param.type, tokens), offset + 1);
|
||||
|
||||
case 'fixedArray':
|
||||
newOffset = offset;
|
||||
|
||||
for (let idx = 0; idx < param.length; idx++) {
|
||||
const result = Decoder.decodeParam(param.subtype, slices, newOffset);
|
||||
newOffset = result.newOffset;
|
||||
tokens.push(result.token);
|
||||
}
|
||||
|
||||
return new DecodeResult(new Token(param.type, tokens), newOffset);
|
||||
|
||||
default:
|
||||
throw new Error(`Invalid param type ${param.type} in decodeParam`);
|
||||
}
|
||||
}
|
||||
}
|
||||
310
js/src/abi/decoder/decoder.spec.js
Normal file
310
js/src/abi/decoder/decoder.spec.js
Normal file
@@ -0,0 +1,310 @@
|
||||
// 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 Decoder from './decoder';
|
||||
import ParamType from '../spec/paramType';
|
||||
import Token from '../token';
|
||||
import { padU32 } from '../util/pad';
|
||||
|
||||
describe('abi/decoder/Decoder', () => {
|
||||
const stringToBytes = function (str) {
|
||||
return str.match(/.{1,2}/g).map((code) => parseInt(code, 16));
|
||||
};
|
||||
|
||||
const address1 = '0000000000000000000000001111111111111111111111111111111111111111';
|
||||
const address2 = '0000000000000000000000002222222222222222222222222222222222222222';
|
||||
const address3 = '0000000000000000000000003333333333333333333333333333333333333333';
|
||||
const address4 = '0000000000000000000000004444444444444444444444444444444444444444';
|
||||
const bool1 = '0000000000000000000000000000000000000000000000000000000000000001';
|
||||
const bytes1 = '1234000000000000000000000000000000000000000000000000000000000000';
|
||||
const bytes2 = '1000000000000000000000000000000000000000000000000000000000000000';
|
||||
const bytes3 = '10000000000000000000000000000000000000000000000000000000000002';
|
||||
const bytes4 = '0010000000000000000000000000000000000000000000000000000000000002';
|
||||
const int1 = '0111111111111111111111111111111111111111111111111111111111111111';
|
||||
const intn = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85';
|
||||
const string1 = '6761766f66796f726b0000000000000000000000000000000000000000000000';
|
||||
const tokenAddress1 = new Token('address', `0x${address1.slice(-40)}`);
|
||||
const tokenAddress2 = new Token('address', `0x${address2.slice(-40)}`);
|
||||
const tokenAddress3 = new Token('address', `0x${address3.slice(-40)}`);
|
||||
const tokenAddress4 = new Token('address', `0x${address4.slice(-40)}`);
|
||||
const tokenBool1 = new Token('bool', true);
|
||||
const tokenFixedBytes1 = new Token('fixedBytes', [0x12, 0x34]);
|
||||
const tokenBytes1 = new Token('bytes', [0x12, 0x34]);
|
||||
const tokenBytes2 = new Token('bytes', stringToBytes(bytes2).concat(stringToBytes(bytes2)));
|
||||
const tokenBytes3 = new Token('bytes', stringToBytes(bytes3));
|
||||
const tokenBytes4 = new Token('bytes', stringToBytes(bytes4));
|
||||
const tokenInt1 = new Token('int', new BigNumber(int1, 16));
|
||||
const tokenIntn = new Token('int', new BigNumber(-123));
|
||||
const tokenUint1 = new Token('uint', new BigNumber(int1, 16));
|
||||
const tokenUintn = new Token('uint', new BigNumber(intn, 16));
|
||||
const tokenString1 = new Token('string', 'gavofyork');
|
||||
const slices = [ address1, address2, address3, address4 ];
|
||||
|
||||
describe('peek', () => {
|
||||
it('returns the slice at the correct position', () => {
|
||||
expect(Decoder.peek(slices, 1)).to.equal(slices[1]);
|
||||
});
|
||||
|
||||
it('returns empty on invalid slices', () => {
|
||||
expect(Decoder.peek(null, 4)).to.equal('0000000000000000000000000000000000000000000000000000000000000000');
|
||||
});
|
||||
});
|
||||
|
||||
describe('takeBytes', () => {
|
||||
it('returns a single slice', () => {
|
||||
expect(Decoder.takeBytes(slices, 0, 32).bytes).to.deep.equal(stringToBytes(slices[0]));
|
||||
});
|
||||
|
||||
it('returns a single partial slice', () => {
|
||||
expect(Decoder.takeBytes(slices, 0, 20).bytes).to.deep.equal(stringToBytes(slices[0].substr(0, 40)));
|
||||
});
|
||||
|
||||
it('returns multiple slices', () => {
|
||||
expect(Decoder.takeBytes(slices, 0, 64).bytes).to.deep.equal(stringToBytes(`${slices[0]}${slices[1]}`));
|
||||
});
|
||||
|
||||
it('returns a single offset slice', () => {
|
||||
expect(Decoder.takeBytes(slices, 1, 32).bytes).to.deep.equal(stringToBytes(slices[1]));
|
||||
});
|
||||
|
||||
it('returns multiple offset slices', () => {
|
||||
expect(Decoder.takeBytes(slices, 1, 64).bytes).to.deep.equal(stringToBytes(`${slices[1]}${slices[2]}`));
|
||||
});
|
||||
|
||||
it('returns the requires length from slices', () => {
|
||||
expect(
|
||||
Decoder.takeBytes(slices, 1, 75).bytes
|
||||
).to.deep.equal(stringToBytes(`${slices[1]}${slices[2]}${slices[3]}`.substr(0, 150)));
|
||||
});
|
||||
});
|
||||
|
||||
describe('decodeParam', () => {
|
||||
it('throws an error on non ParamType param', () => {
|
||||
expect(() => Decoder.decodeParam({})).to.throw(/ParamType/);
|
||||
});
|
||||
|
||||
it('throws an error on invalid param type', () => {
|
||||
const pt = new ParamType('address');
|
||||
pt._type = 'noMatch';
|
||||
|
||||
expect(() => Decoder.decodeParam(pt)).to.throw(/noMatch/);
|
||||
});
|
||||
|
||||
it('decodes an address', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('address'), [address1], 0).token
|
||||
).to.deep.equal(tokenAddress1);
|
||||
});
|
||||
|
||||
it('decodes a bool', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('bool'), [bool1], 0).token
|
||||
).to.deep.equal(tokenBool1);
|
||||
});
|
||||
|
||||
it('decodes an int', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('int'), [int1], 0).token
|
||||
).to.deep.equal(tokenInt1);
|
||||
});
|
||||
|
||||
it('decodes a negative int', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('int'), [intn], 0).token
|
||||
).to.deep.equal(tokenIntn);
|
||||
});
|
||||
|
||||
it('decodes an uint', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('uint'), [int1], 0).token
|
||||
).to.deep.equal(tokenUint1);
|
||||
});
|
||||
|
||||
it('decodes an uint (negative as int)', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('uint'), [intn], 0).token
|
||||
).to.deep.equal(tokenUintn);
|
||||
});
|
||||
|
||||
it('decodes fixedBytes', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('fixedBytes', null, 2), [bytes1], 0).token
|
||||
).to.deep.equal(tokenFixedBytes1);
|
||||
});
|
||||
|
||||
it('decodes bytes', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('bytes'), [padU32(0x20), padU32(2), bytes1], 0).token
|
||||
).to.deep.equal(tokenBytes1);
|
||||
});
|
||||
|
||||
it('decodes string', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('string'), [padU32(0x20), padU32(9), string1], 0).token
|
||||
).to.deep.equal(tokenString1);
|
||||
});
|
||||
|
||||
it('decodes string (indexed)', () => {
|
||||
expect(
|
||||
Decoder.decodeParam(new ParamType('string', null, 0, true), [bytes1], 0)
|
||||
).to.deep.equal(Decoder.decodeParam(new ParamType('fixedBytes', null, 32, true), [bytes1], 0));
|
||||
});
|
||||
});
|
||||
|
||||
describe('decode', () => {
|
||||
it('throws an error on invalid params', () => {
|
||||
expect(() => Decoder.decode(null, '123')).to.throw(/array/);
|
||||
});
|
||||
|
||||
describe('address', () => {
|
||||
it('decodes an address', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('address')],
|
||||
`${address1}`
|
||||
)
|
||||
).to.deep.equal([tokenAddress1]);
|
||||
});
|
||||
|
||||
it('decodes 2 addresses', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('address'), new ParamType('address')],
|
||||
`${address1}${address2}`
|
||||
)
|
||||
).to.deep.equal([tokenAddress1, tokenAddress2]);
|
||||
});
|
||||
|
||||
it('decodes a fixedArray of addresses', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('fixedArray', new ParamType('address'), 2)],
|
||||
`${address1}${address2}`
|
||||
)
|
||||
).to.deep.equal([new Token('fixedArray', [tokenAddress1, tokenAddress2])]);
|
||||
});
|
||||
|
||||
it('decodes a dynamic array of addresses', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('array', new ParamType('address'))],
|
||||
`${padU32(0x20)}${padU32(2)}${address1}${address2}`
|
||||
)
|
||||
).to.deep.equal([new Token('array', [tokenAddress1, tokenAddress2])]);
|
||||
});
|
||||
|
||||
it('decodes a dynamic array of fixed arrays', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('array', new ParamType('fixedArray', new ParamType('address'), 2))],
|
||||
`${padU32(0x20)}${padU32(2)}${address1}${address2}${address3}${address4}`
|
||||
)
|
||||
).to.deep.equal([
|
||||
new Token('array', [
|
||||
new Token('fixedArray', [tokenAddress1, tokenAddress2]),
|
||||
new Token('fixedArray', [tokenAddress3, tokenAddress4])
|
||||
])
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('int', () => {
|
||||
it('decodes an int', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('int')],
|
||||
`${int1}`
|
||||
)
|
||||
).to.deep.equal([tokenInt1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('uint', () => {
|
||||
it('decodes an uint', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('uint')],
|
||||
`${int1}`
|
||||
)
|
||||
).to.deep.equal([tokenUint1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('fixedBytes', () => {
|
||||
it('decodes fixedBytes', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('fixedBytes', null, 2)],
|
||||
`${bytes1}`
|
||||
)
|
||||
).to.deep.equal([tokenFixedBytes1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bytes', () => {
|
||||
it('decodes bytes', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('bytes')],
|
||||
`${padU32(0x20)}${padU32(2)}${bytes1}`
|
||||
)
|
||||
).to.deep.equal([tokenBytes1]);
|
||||
});
|
||||
|
||||
it('decodes bytes sequence', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('bytes')],
|
||||
`${padU32(0x20)}${padU32(0x40)}${bytes2}${bytes2}`
|
||||
)
|
||||
).to.deep.equal([tokenBytes2]);
|
||||
});
|
||||
|
||||
it('decodes bytes seuence (2)', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('bytes'), new ParamType('bytes')],
|
||||
`${padU32(0x40)}${padU32(0x80)}${padU32(0x1f)}${bytes3}00${padU32(0x20)}${bytes4}`
|
||||
)
|
||||
).to.deep.equal([tokenBytes3, tokenBytes4]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bool', () => {
|
||||
it('decodes a single bool', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('bool')],
|
||||
bool1
|
||||
)
|
||||
).to.deep.equal([tokenBool1]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('string', () => {
|
||||
it('decodes a string', () => {
|
||||
expect(
|
||||
Decoder.decode(
|
||||
[new ParamType('string')],
|
||||
`${padU32(0x20)}${padU32(9)}${string1}`
|
||||
)
|
||||
).to.deep.equal([tokenString1]);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/abi/decoder/index.js
Normal file
17
js/src/abi/decoder/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 './decoder';
|
||||
72
js/src/abi/encoder/encoder.js
Normal file
72
js/src/abi/encoder/encoder.js
Normal file
@@ -0,0 +1,72 @@
|
||||
// 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 { padAddress, padBool, padBytes, padFixedBytes, padU32, padString } from '../util/pad';
|
||||
import Mediate from './mediate';
|
||||
import Token from '../token/token';
|
||||
import { isArray, isInstanceOf } from '../util/types';
|
||||
|
||||
export default class Encoder {
|
||||
static encode (tokens) {
|
||||
if (!isArray(tokens)) {
|
||||
throw new Error('tokens should be array of Token');
|
||||
}
|
||||
|
||||
const mediates = tokens.map((token) => Encoder.encodeToken(token));
|
||||
const inits = mediates
|
||||
.map((mediate, idx) => mediate.init(Mediate.offsetFor(mediates, idx)))
|
||||
.join('');
|
||||
const closings = mediates
|
||||
.map((mediate, idx) => mediate.closing(Mediate.offsetFor(mediates, idx)))
|
||||
.join('');
|
||||
|
||||
return `${inits}${closings}`;
|
||||
}
|
||||
|
||||
static encodeToken (token) {
|
||||
if (!isInstanceOf(token, Token)) {
|
||||
throw new Error('token should be instanceof Token');
|
||||
}
|
||||
|
||||
switch (token.type) {
|
||||
case 'address':
|
||||
return new Mediate('raw', padAddress(token.value));
|
||||
|
||||
case 'int':
|
||||
case 'uint':
|
||||
return new Mediate('raw', padU32(token.value));
|
||||
|
||||
case 'bool':
|
||||
return new Mediate('raw', padBool(token.value));
|
||||
|
||||
case 'fixedBytes':
|
||||
return new Mediate('raw', padFixedBytes(token.value));
|
||||
|
||||
case 'bytes':
|
||||
return new Mediate('prefixed', padBytes(token.value));
|
||||
|
||||
case 'string':
|
||||
return new Mediate('prefixed', padString(token.value));
|
||||
|
||||
case 'fixedArray':
|
||||
case 'array':
|
||||
return new Mediate(token.type, token.value.map((token) => Encoder.encodeToken(token)));
|
||||
|
||||
default:
|
||||
throw new Error(`Invalid token type ${token.type} in encodeToken`);
|
||||
}
|
||||
}
|
||||
}
|
||||
290
js/src/abi/encoder/encoder.spec.js
Normal file
290
js/src/abi/encoder/encoder.spec.js
Normal file
@@ -0,0 +1,290 @@
|
||||
// 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 Encoder from './encoder';
|
||||
import Token from '../token';
|
||||
import { padAddress, padFixedBytes, padU32 } from '../util/pad';
|
||||
|
||||
describe('abi/encoder/Encoder', () => {
|
||||
describe('encodeToken', () => {
|
||||
it('requires token as Token', () => {
|
||||
expect(() => Encoder.encodeToken()).to.throw(/Token/);
|
||||
});
|
||||
|
||||
it('encodes address tokens in Mediate(raw)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('address', '123'));
|
||||
|
||||
expect(mediate.type).to.equal('raw');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes bool tokens in Mediate(raw)', () => {
|
||||
const mediatet = Encoder.encodeToken(new Token('bool', true));
|
||||
const mediatef = Encoder.encodeToken(new Token('bool', false));
|
||||
|
||||
expect(mediatet.type).to.equal('raw');
|
||||
expect(mediatet.value).to.be.ok;
|
||||
|
||||
expect(mediatef.type).to.equal('raw');
|
||||
expect(mediatef.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes int tokens in Mediate(raw)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('int', '123'));
|
||||
|
||||
expect(mediate.type).to.equal('raw');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes uint tokens in Mediate(raw)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('uint', '123'));
|
||||
|
||||
expect(mediate.type).to.equal('raw');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes fixedBytes tokens in Mediate(raw)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('fixedBytes', '123'));
|
||||
|
||||
expect(mediate.type).to.equal('raw');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes bytes tokens in Mediate(prefixed)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('bytes', '123'));
|
||||
|
||||
expect(mediate.type).to.equal('prefixed');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes string tokens in Mediate(prefixed)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('string', '123'));
|
||||
|
||||
expect(mediate.type).to.equal('prefixed');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes fixedArray tokens in Mediate(fixedArray)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('fixedArray', [new Token('uint', '123')]));
|
||||
|
||||
expect(mediate.type).to.equal('fixedArray');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('encodes array tokens in Mediate(array)', () => {
|
||||
const mediate = Encoder.encodeToken(new Token('array', [new Token('uint', '123')]));
|
||||
|
||||
expect(mediate.type).to.equal('array');
|
||||
expect(mediate.value).to.be.ok;
|
||||
});
|
||||
|
||||
it('throws an Error on invalid tokens', () => {
|
||||
const token = new Token('address');
|
||||
token._type = 'noMatch';
|
||||
|
||||
expect(() => Encoder.encodeToken(token)).to.throw(/noMatch/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('encode', () => {
|
||||
it('requires tokens array', () => {
|
||||
expect(() => Encoder.encode()).to.throw(/array/);
|
||||
});
|
||||
|
||||
describe('addresses', () => {
|
||||
const address1 = '1111111111111111111111111111111111111111';
|
||||
const address2 = '2222222222222222222222222222222222222222';
|
||||
const address3 = '3333333333333333333333333333333333333333';
|
||||
const address4 = '4444444444444444444444444444444444444444';
|
||||
const encAddress1 = padAddress(address1);
|
||||
const encAddress2 = padAddress(address2);
|
||||
const encAddress3 = padAddress(address3);
|
||||
const encAddress4 = padAddress(address4);
|
||||
const tokenAddress1 = new Token('address', address1);
|
||||
const tokenAddress2 = new Token('address', address2);
|
||||
const tokenAddress3 = new Token('address', address3);
|
||||
const tokenAddress4 = new Token('address', address4);
|
||||
|
||||
it('encodes an address', () => {
|
||||
const token = tokenAddress1;
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(encAddress1);
|
||||
});
|
||||
|
||||
it('encodes an array of addresses', () => {
|
||||
const expected = `${padU32(0x20)}${padU32(2)}${encAddress1}${encAddress2}`;
|
||||
const token = new Token('array', [tokenAddress1, tokenAddress2]);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(expected);
|
||||
});
|
||||
|
||||
it('encodes an fixedArray of addresses', () => {
|
||||
const expected = `${encAddress1}${encAddress2}`;
|
||||
const token = new Token('fixedArray', [tokenAddress1, tokenAddress2]);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(expected);
|
||||
});
|
||||
|
||||
it('encodes two addresses', () => {
|
||||
const expected = `${encAddress1}${encAddress2}`;
|
||||
const tokens = [tokenAddress1, tokenAddress2];
|
||||
|
||||
expect(Encoder.encode(tokens)).to.equal(expected);
|
||||
});
|
||||
|
||||
it('encodes fixed array of dynamic array addresses', () => {
|
||||
const tokens1 = new Token('array', [tokenAddress1, tokenAddress2]);
|
||||
const tokens2 = new Token('array', [tokenAddress3, tokenAddress4]);
|
||||
const fixed = new Token('fixedArray', [tokens1, tokens2]);
|
||||
const expected = `${padU32(0x40)}${padU32(0xa0)}${padU32(2)}${encAddress1}${encAddress2}${padU32(2)}${encAddress3}${encAddress4}`;
|
||||
|
||||
expect(Encoder.encode([fixed])).to.equal(expected);
|
||||
});
|
||||
|
||||
it('encodes dynamic array of fixed array addresses', () => {
|
||||
const tokens1 = new Token('fixedArray', [tokenAddress1, tokenAddress2]);
|
||||
const tokens2 = new Token('fixedArray', [tokenAddress3, tokenAddress4]);
|
||||
const dynamic = new Token('array', [tokens1, tokens2]);
|
||||
const expected = `${padU32(0x20)}${padU32(2)}${encAddress1}${encAddress2}${encAddress3}${encAddress4}`;
|
||||
|
||||
expect(Encoder.encode([dynamic])).to.equal(expected);
|
||||
});
|
||||
|
||||
it('encodes dynamic array of dynamic array addresses', () => {
|
||||
const tokens1 = new Token('array', [tokenAddress1]);
|
||||
const tokens2 = new Token('array', [tokenAddress2]);
|
||||
const dynamic = new Token('array', [tokens1, tokens2]);
|
||||
const expected = `${padU32(0x20)}${padU32(2)}${padU32(0x80)}${padU32(0xc0)}${padU32(1)}${encAddress1}${padU32(1)}${encAddress2}`;
|
||||
|
||||
expect(Encoder.encode([dynamic])).to.equal(expected);
|
||||
});
|
||||
|
||||
it('encodes dynamic array of dynamic array addresses (2)', () => {
|
||||
const tokens1 = new Token('array', [tokenAddress1, tokenAddress2]);
|
||||
const tokens2 = new Token('array', [tokenAddress3, tokenAddress4]);
|
||||
const dynamic = new Token('array', [tokens1, tokens2]);
|
||||
const expected = `${padU32(0x20)}${padU32(2)}${padU32(0x80)}${padU32(0xe0)}${padU32(2)}${encAddress1}${encAddress2}${padU32(2)}${encAddress3}${encAddress4}`;
|
||||
|
||||
expect(Encoder.encode([dynamic])).to.equal(expected);
|
||||
});
|
||||
|
||||
it('encodes fixed array of fixed array addresses', () => {
|
||||
const tokens1 = new Token('fixedArray', [tokenAddress1, tokenAddress2]);
|
||||
const tokens2 = new Token('fixedArray', [tokenAddress3, tokenAddress4]);
|
||||
const dynamic = new Token('fixedArray', [tokens1, tokens2]);
|
||||
const expected = `${encAddress1}${encAddress2}${encAddress3}${encAddress4}`;
|
||||
|
||||
expect(Encoder.encode([dynamic])).to.equal(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('bytes', () => {
|
||||
const bytes1 = '0x1234';
|
||||
const bytes2 = '0x10000000000000000000000000000000000000000000000000000000000002';
|
||||
const bytes3 = '0x1000000000000000000000000000000000000000000000000000000000000000';
|
||||
|
||||
it('encodes fixed bytes', () => {
|
||||
const token = new Token('fixedBytes', bytes1);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(padFixedBytes(bytes1));
|
||||
});
|
||||
|
||||
it('encodes bytes', () => {
|
||||
const token = new Token('bytes', bytes1);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(`${padU32(0x20)}${padU32(2)}${padFixedBytes(bytes1)}`);
|
||||
});
|
||||
|
||||
it('encodes bytes (short of boundary)', () => {
|
||||
const token = new Token('bytes', bytes2);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(`${padU32(0x20)}${padU32(0x1f)}${padFixedBytes(bytes2)}`);
|
||||
});
|
||||
|
||||
it('encodes bytes (two blocks)', () => {
|
||||
const input = `${bytes3}${bytes3.slice(-64)}`;
|
||||
const token = new Token('bytes', input);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(`${padU32(0x20)}${padU32(0x40)}${padFixedBytes(input)}`);
|
||||
});
|
||||
|
||||
it('encodes two consecutive bytes', () => {
|
||||
const in1 = '0x10000000000000000000000000000000000000000000000000000000000002';
|
||||
const in2 = '0x0010000000000000000000000000000000000000000000000000000000000002';
|
||||
const tokens = [new Token('bytes', in1), new Token('bytes', in2)];
|
||||
|
||||
expect(Encoder.encode(tokens)).to.equal(`${padU32(0x40)}${padU32(0x80)}${padU32(0x1f)}${padFixedBytes(in1)}${padU32(0x20)}${padFixedBytes(in2)}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('string', () => {
|
||||
it('encodes a string', () => {
|
||||
const string = 'gavofyork';
|
||||
const stringEnc = padFixedBytes('0x6761766f66796f726b');
|
||||
const token = new Token('string', string);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(`${padU32(0x20)}${padU32(string.length.toString(16))}${stringEnc}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('uint', () => {
|
||||
it('encodes a uint', () => {
|
||||
const token = new Token('uint', 4);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(padU32(4));
|
||||
});
|
||||
});
|
||||
|
||||
describe('int', () => {
|
||||
it('encodes a int', () => {
|
||||
const token = new Token('int', 4);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(padU32(4));
|
||||
});
|
||||
});
|
||||
|
||||
describe('bool', () => {
|
||||
it('encodes a bool (true)', () => {
|
||||
const token = new Token('bool', true);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(padU32(1));
|
||||
});
|
||||
|
||||
it('encodes a bool (false)', () => {
|
||||
const token = new Token('bool', false);
|
||||
|
||||
expect(Encoder.encode([token])).to.equal(padU32(0));
|
||||
});
|
||||
});
|
||||
|
||||
describe('comprehensive test', () => {
|
||||
it('encodes a complex sequence', () => {
|
||||
const bytes = '0x131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b';
|
||||
const tokens = [new Token('int', 5), new Token('bytes', bytes), new Token('int', 3), new Token('bytes', bytes)];
|
||||
|
||||
expect(Encoder.encode(tokens)).to.equal(`${padU32(5)}${padU32(0x80)}${padU32(3)}${padU32(0xe0)}${padU32(0x40)}${bytes.substr(2)}${padU32(0x40)}${bytes.substr(2)}`);
|
||||
});
|
||||
|
||||
it('encodes a complex sequence (nested)', () => {
|
||||
const array = [new Token('int', 5), new Token('int', 6), new Token('int', 7)];
|
||||
const tokens = [new Token('int', 1), new Token('string', 'gavofyork'), new Token('int', 2), new Token('int', 3), new Token('int', 4), new Token('array', array)];
|
||||
const stringEnc = padFixedBytes('0x6761766f66796f726b');
|
||||
|
||||
expect(Encoder.encode(tokens)).to.equal(`${padU32(1)}${padU32(0xc0)}${padU32(2)}${padU32(3)}${padU32(4)}${padU32(0x100)}${padU32(9)}${stringEnc}${padU32(3)}${padU32(5)}${padU32(6)}${padU32(7)}`);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/abi/encoder/index.js
Normal file
17
js/src/abi/encoder/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 './encoder';
|
||||
142
js/src/abi/encoder/mediate.js
Normal file
142
js/src/abi/encoder/mediate.js
Normal file
@@ -0,0 +1,142 @@
|
||||
// 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 TYPES = ['raw', 'prefixed', 'fixedArray', 'array'];
|
||||
|
||||
import { padU32 } from '../util/pad';
|
||||
|
||||
export default class Mediate {
|
||||
constructor (type, value) {
|
||||
Mediate.validateType(type);
|
||||
|
||||
this._type = type;
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
initLength () {
|
||||
switch (this._type) {
|
||||
case 'raw':
|
||||
return this._value.length / 2;
|
||||
|
||||
case 'array':
|
||||
case 'prefixed':
|
||||
return 32;
|
||||
|
||||
case 'fixedArray':
|
||||
return this._value
|
||||
.reduce((total, mediate) => {
|
||||
return total + mediate.initLength();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
closingLength () {
|
||||
switch (this._type) {
|
||||
case 'raw':
|
||||
return 0;
|
||||
|
||||
case 'prefixed':
|
||||
return this._value.length / 2;
|
||||
|
||||
case 'array':
|
||||
return this._value
|
||||
.reduce((total, mediate) => {
|
||||
return total + mediate.initLength();
|
||||
}, 32);
|
||||
|
||||
case 'fixedArray':
|
||||
return this._value
|
||||
.reduce((total, mediate) => {
|
||||
return total + mediate.initLength() + mediate.closingLength();
|
||||
}, 0);
|
||||
}
|
||||
}
|
||||
|
||||
init (suffixOffset) {
|
||||
switch (this._type) {
|
||||
case 'raw':
|
||||
return this._value;
|
||||
|
||||
case 'fixedArray':
|
||||
return this._value
|
||||
.map((mediate, idx) => mediate.init(Mediate.offsetFor(this._value, idx)).toString(16))
|
||||
.join('');
|
||||
|
||||
case 'prefixed':
|
||||
case 'array':
|
||||
return padU32(suffixOffset);
|
||||
}
|
||||
}
|
||||
|
||||
closing (offset) {
|
||||
switch (this._type) {
|
||||
case 'raw':
|
||||
return '';
|
||||
|
||||
case 'prefixed':
|
||||
return this._value;
|
||||
|
||||
case 'fixedArray':
|
||||
return this._value
|
||||
.map((mediate, idx) => mediate.closing(Mediate.offsetFor(this._value, idx)).toString(16))
|
||||
.join('');
|
||||
|
||||
case 'array':
|
||||
const prefix = padU32(this._value.length);
|
||||
const inits = this._value
|
||||
.map((mediate, idx) => mediate.init(offset + Mediate.offsetFor(this._value, idx) + 32).toString(16))
|
||||
.join('');
|
||||
const closings = this._value
|
||||
.map((mediate, idx) => mediate.closing(offset + Mediate.offsetFor(this._value, idx)).toString(16))
|
||||
.join('');
|
||||
|
||||
return `${prefix}${inits}${closings}`;
|
||||
}
|
||||
}
|
||||
|
||||
get type () {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
get value () {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
static offsetFor (mediates, position) {
|
||||
if (position < 0 || position >= mediates.length) {
|
||||
throw new Error(`Invalid position ${position} specified for Mediate.offsetFor`);
|
||||
}
|
||||
|
||||
const initLength = mediates
|
||||
.reduce((total, mediate) => {
|
||||
return total + mediate.initLength();
|
||||
}, 0);
|
||||
|
||||
return mediates
|
||||
.slice(0, position)
|
||||
.reduce((total, mediate) => {
|
||||
return total + mediate.closingLength();
|
||||
}, initLength);
|
||||
}
|
||||
|
||||
static validateType (type) {
|
||||
if (TYPES.filter((_type) => type === _type).length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new Error(`Invalid type ${type} received for Mediate.validateType`);
|
||||
}
|
||||
}
|
||||
105
js/src/abi/encoder/mediate.spec.js
Normal file
105
js/src/abi/encoder/mediate.spec.js
Normal file
@@ -0,0 +1,105 @@
|
||||
// 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 Mediate from './mediate';
|
||||
|
||||
describe('abi/encoder/Mediate', () => {
|
||||
const LONG15 = '1234567890abcdef000000000000000000000000000000000000000000000000';
|
||||
const DOUBLE15 = `${LONG15}${LONG15}`;
|
||||
const ARRAY = [new Mediate('raw', DOUBLE15), new Mediate('raw', LONG15)];
|
||||
|
||||
describe('validateType', () => {
|
||||
it('validates raw', () => {
|
||||
expect(Mediate.validateType('raw')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates prefixed', () => {
|
||||
expect(Mediate.validateType('prefixed')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates fixedArray', () => {
|
||||
expect(Mediate.validateType('fixedArray')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates array', () => {
|
||||
expect(Mediate.validateType('array')).to.be.true;
|
||||
});
|
||||
|
||||
it('throws an error on invalid types', () => {
|
||||
expect(() => Mediate.validateType('noMatch')).to.throw(/noMatch/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('offsetFor', () => {
|
||||
it('thows an error when offset < 0', () => {
|
||||
expect(() => Mediate.offsetFor([1], -1)).to.throw(/Invalid position/);
|
||||
});
|
||||
|
||||
it('throws an error when offset >= length', () => {
|
||||
expect(() => Mediate.offsetFor([1], 1)).to.throw(/Invalid position/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('throws an error on invalid types', () => {
|
||||
expect(() => new Mediate('noMatch', '1')).to.throw(/noMatch/);
|
||||
});
|
||||
|
||||
it('sets the type of the object', () => {
|
||||
expect((new Mediate('raw', '1')).type).to.equal('raw');
|
||||
});
|
||||
|
||||
it('sets the value of the object', () => {
|
||||
expect((new Mediate('raw', '1')).value).to.equal('1');
|
||||
});
|
||||
});
|
||||
|
||||
describe('initLength', () => {
|
||||
it('returns correct variable byte length for raw', () => {
|
||||
expect(new Mediate('raw', DOUBLE15).initLength()).to.equal(64);
|
||||
});
|
||||
|
||||
it('returns correct fixed byte length for array', () => {
|
||||
expect(new Mediate('array', [1, 2, 3, 4]).initLength()).to.equal(32);
|
||||
});
|
||||
|
||||
it('returns correct fixed byte length for prefixed', () => {
|
||||
expect(new Mediate('prefixed', 0).initLength()).to.equal(32);
|
||||
});
|
||||
|
||||
it('returns correct variable byte length for fixedArray', () => {
|
||||
expect(new Mediate('fixedArray', ARRAY).initLength()).to.equal(96);
|
||||
});
|
||||
});
|
||||
|
||||
describe('closingLength', () => {
|
||||
it('returns 0 byte length for raw', () => {
|
||||
expect(new Mediate('raw', DOUBLE15).closingLength()).to.equal(0);
|
||||
});
|
||||
|
||||
it('returns prefix + size for prefixed', () => {
|
||||
expect(new Mediate('prefixed', DOUBLE15).closingLength()).to.equal(64);
|
||||
});
|
||||
|
||||
it('returns prefix + size for array', () => {
|
||||
expect(new Mediate('array', ARRAY).closingLength()).to.equal(128);
|
||||
});
|
||||
|
||||
it('returns total length for fixedArray', () => {
|
||||
expect(new Mediate('fixedArray', ARRAY).closingLength()).to.equal(96);
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/abi/index.js
Normal file
17
js/src/abi/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 './abi';
|
||||
36
js/src/abi/spec/constructor.js
Normal file
36
js/src/abi/spec/constructor.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 Encoder from '../encoder/encoder';
|
||||
import Param from './param';
|
||||
|
||||
export default class Constructor {
|
||||
constructor (abi) {
|
||||
this._inputs = Param.toParams(abi.inputs || []);
|
||||
}
|
||||
|
||||
get inputs () {
|
||||
return this._inputs;
|
||||
}
|
||||
|
||||
inputParamTypes () {
|
||||
return this._inputs.map((input) => input.kind);
|
||||
}
|
||||
|
||||
encodeCall (tokens) {
|
||||
return Encoder.encode(tokens);
|
||||
}
|
||||
}
|
||||
52
js/src/abi/spec/constructor.spec.js
Normal file
52
js/src/abi/spec/constructor.spec.js
Normal file
@@ -0,0 +1,52 @@
|
||||
// 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 Constructor from './constructor';
|
||||
import Param from './param';
|
||||
import Token from '../token';
|
||||
|
||||
describe('abi/spec/Constructor', () => {
|
||||
const inputsArr = [{ name: 'boolin', type: 'bool' }, { name: 'stringin', type: 'string' }];
|
||||
const bool = new Param('boolin', 'bool');
|
||||
const string = new Param('stringin', 'string');
|
||||
|
||||
const inputs = [bool, string];
|
||||
const cr = new Constructor({ inputs: inputsArr });
|
||||
|
||||
describe('constructor', () => {
|
||||
it('stores the inputs as received', () => {
|
||||
expect(cr.inputs).to.deep.equal(inputs);
|
||||
});
|
||||
|
||||
it('matches empty inputs with []', () => {
|
||||
expect(new Constructor({}).inputs).to.deep.equal([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inputParamTypes', () => {
|
||||
it('retrieves the input types as received', () => {
|
||||
expect(cr.inputParamTypes()).to.deep.equal([bool.kind, string.kind]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('encodeCall', () => {
|
||||
it('encodes correctly', () => {
|
||||
const result = cr.encodeCall([new Token('bool', true), new Token('string', 'jacogr')]);
|
||||
|
||||
expect(result).to.equal('0000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000066a61636f67720000000000000000000000000000000000000000000000000000');
|
||||
});
|
||||
});
|
||||
});
|
||||
30
js/src/abi/spec/event/decodedLog.js
Normal file
30
js/src/abi/spec/event/decodedLog.js
Normal file
@@ -0,0 +1,30 @@
|
||||
// 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 DecodedLog {
|
||||
constructor (params, address) {
|
||||
this._params = params;
|
||||
this._address = address;
|
||||
}
|
||||
|
||||
get address () {
|
||||
return this._address;
|
||||
}
|
||||
|
||||
get params () {
|
||||
return this._params;
|
||||
}
|
||||
}
|
||||
28
js/src/abi/spec/event/decodedLog.spec.js
Normal file
28
js/src/abi/spec/event/decodedLog.spec.js
Normal file
@@ -0,0 +1,28 @@
|
||||
// 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 DecodedLog from './decodedLog';
|
||||
|
||||
const log = new DecodedLog('someParams', 'someAddress');
|
||||
|
||||
describe('abi/spec/event/DecodedLog', () => {
|
||||
describe('constructor', () => {
|
||||
it('sets internal state', () => {
|
||||
expect(log.params).to.equal('someParams');
|
||||
expect(log.address).to.equal('someAddress');
|
||||
});
|
||||
});
|
||||
});
|
||||
45
js/src/abi/spec/event/decodedLogParam.js
Normal file
45
js/src/abi/spec/event/decodedLogParam.js
Normal file
@@ -0,0 +1,45 @@
|
||||
// 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 ParamType from '../paramType/paramType';
|
||||
import Token from '../../token/token';
|
||||
import { isInstanceOf } from '../../util/types';
|
||||
|
||||
export default class DecodedLogParam {
|
||||
constructor (name, kind, token) {
|
||||
if (!isInstanceOf(kind, ParamType)) {
|
||||
throw new Error('kind not instanceof ParamType');
|
||||
} else if (!isInstanceOf(token, Token)) {
|
||||
throw new Error('token not instanceof Token');
|
||||
}
|
||||
|
||||
this._name = name;
|
||||
this._kind = kind;
|
||||
this._token = token;
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get kind () {
|
||||
return this._kind;
|
||||
}
|
||||
|
||||
get token () {
|
||||
return this._token;
|
||||
}
|
||||
}
|
||||
42
js/src/abi/spec/event/decodedLogParam.spec.js
Normal file
42
js/src/abi/spec/event/decodedLogParam.spec.js
Normal file
@@ -0,0 +1,42 @@
|
||||
// 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 DecodedLogParam from './decodedLogParam';
|
||||
import ParamType from '../paramType';
|
||||
import Token from '../../token';
|
||||
|
||||
describe('abi/spec/event/DecodedLogParam', () => {
|
||||
describe('constructor', () => {
|
||||
const pt = new ParamType('bool');
|
||||
const tk = new Token('bool');
|
||||
|
||||
it('disallows kind not instanceof ParamType', () => {
|
||||
expect(() => new DecodedLogParam('test', 'param')).to.throw(/ParamType/);
|
||||
});
|
||||
|
||||
it('disallows token not instanceof Token', () => {
|
||||
expect(() => new DecodedLogParam('test', pt, 'token')).to.throw(/Token/);
|
||||
});
|
||||
|
||||
it('stores all parameters received', () => {
|
||||
const log = new DecodedLogParam('test', pt, tk);
|
||||
|
||||
expect(log.name).to.equal('test');
|
||||
expect(log.kind).to.equal(pt);
|
||||
expect(log.token).to.equal(tk);
|
||||
});
|
||||
});
|
||||
});
|
||||
113
js/src/abi/spec/event/event.js
Normal file
113
js/src/abi/spec/event/event.js
Normal file
@@ -0,0 +1,113 @@
|
||||
// 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 Decoder from '../../decoder/decoder';
|
||||
import DecodedLog from './decodedLog';
|
||||
import DecodedLogParam from './decodedLogParam';
|
||||
import EventParam from './eventParam';
|
||||
import { asAddress } from '../../util/sliceAs';
|
||||
import { eventSignature } from '../../util/signature';
|
||||
|
||||
export default class Event {
|
||||
constructor (abi) {
|
||||
this._name = abi.name;
|
||||
this._inputs = EventParam.toEventParams(abi.inputs || []);
|
||||
this._anonymous = !!abi.anonymous;
|
||||
|
||||
const { id, signature } = eventSignature(this._name, this.inputParamTypes());
|
||||
this._id = id;
|
||||
this._signature = signature;
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get id () {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get inputs () {
|
||||
return this._inputs;
|
||||
}
|
||||
|
||||
get anonymous () {
|
||||
return this._anonymous;
|
||||
}
|
||||
|
||||
get signature () {
|
||||
return this._signature;
|
||||
}
|
||||
|
||||
inputParamTypes () {
|
||||
return this._inputs.map((input) => input.kind);
|
||||
}
|
||||
|
||||
inputParamNames () {
|
||||
return this._inputs.map((input) => input.name);
|
||||
}
|
||||
|
||||
indexedParams (indexed) {
|
||||
return this._inputs.filter((input) => input.indexed === indexed);
|
||||
}
|
||||
|
||||
decodeLog (topics, data) {
|
||||
const topicParams = this.indexedParams(true);
|
||||
const dataParams = this.indexedParams(false);
|
||||
|
||||
let address;
|
||||
let toSkip;
|
||||
|
||||
if (!this.anonymous) {
|
||||
address = asAddress(topics[0]);
|
||||
toSkip = 1;
|
||||
} else {
|
||||
toSkip = 0;
|
||||
}
|
||||
|
||||
const topicTypes = topicParams.map((param) => param.kind);
|
||||
const flatTopics = topics
|
||||
.filter((topic, idx) => idx >= toSkip)
|
||||
.map((topic) => {
|
||||
return (topic.substr(0, 2) === '0x')
|
||||
? topic.substr(2)
|
||||
: topic;
|
||||
}).join('');
|
||||
const topicTokens = Decoder.decode(topicTypes, flatTopics);
|
||||
|
||||
if (topicTokens.length !== (topics.length - toSkip)) {
|
||||
throw new Error('Invalid topic data');
|
||||
}
|
||||
|
||||
const dataTypes = dataParams.map((param) => param.kind);
|
||||
const dataTokens = Decoder.decode(dataTypes, data);
|
||||
|
||||
const namedTokens = {};
|
||||
|
||||
topicParams.forEach((param, idx) => {
|
||||
namedTokens[param.name] = topicTokens[idx];
|
||||
});
|
||||
dataParams.forEach((param, idx) => {
|
||||
namedTokens[param.name] = dataTokens[idx];
|
||||
});
|
||||
|
||||
const inputParamTypes = this.inputParamTypes();
|
||||
const decodedParams = this.inputParamNames()
|
||||
.map((name, idx) => new DecodedLogParam(name, inputParamTypes[idx], namedTokens[name]));
|
||||
|
||||
return new DecodedLog(decodedParams, address);
|
||||
}
|
||||
}
|
||||
111
js/src/abi/spec/event/event.spec.js
Normal file
111
js/src/abi/spec/event/event.spec.js
Normal file
@@ -0,0 +1,111 @@
|
||||
// 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 Event from './event';
|
||||
import EventParam from './eventParam';
|
||||
import DecodedLogParam from './decodedLogParam';
|
||||
import ParamType from '../paramType';
|
||||
import Token from '../../token';
|
||||
|
||||
describe('abi/spec/event/Event', () => {
|
||||
const inputArr = [{ name: 'a', type: 'bool' }, { name: 'b', type: 'uint', indexed: true }];
|
||||
const inputs = [new EventParam('a', 'bool', false), new EventParam('b', 'uint', true)];
|
||||
const event = new Event({ name: 'test', inputs: inputArr, anonymous: true });
|
||||
|
||||
describe('constructor', () => {
|
||||
it('stores the parameters as received', () => {
|
||||
expect(event.name).to.equal('test');
|
||||
expect(event.inputs).to.deep.equal(inputs);
|
||||
expect(event.anonymous).to.be.true;
|
||||
});
|
||||
|
||||
it('matches empty inputs with []', () => {
|
||||
expect(new Event({ name: 'test' }).inputs).to.deep.equal([]);
|
||||
});
|
||||
|
||||
it('sets the event signature', () => {
|
||||
expect(new Event({ name: 'baz' }).signature)
|
||||
.to.equal('a7916fac4f538170f7cd12c148552e2cba9fcd72329a2dd5b07a6fa906488ddf');
|
||||
});
|
||||
});
|
||||
|
||||
describe('inputParamTypes', () => {
|
||||
it('returns all the types', () => {
|
||||
expect(event.inputParamTypes()).to.deep.equal([new ParamType('bool'), new ParamType('uint', null, 256, true)]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('inputParamNames', () => {
|
||||
it('returns all the names', () => {
|
||||
expect(event.inputParamNames()).to.deep.equal(['a', 'b']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('indexedParams', () => {
|
||||
it('returns all indexed parameters (indexed)', () => {
|
||||
expect(event.indexedParams(true)).to.deep.equal([inputs[1]]);
|
||||
});
|
||||
|
||||
it('returns all indexed parameters (non-indexed)', () => {
|
||||
expect(event.indexedParams(false)).to.deep.equal([inputs[0]]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('decodeLog', () => {
|
||||
it('decodes an event', () => {
|
||||
const event = new Event({
|
||||
name: 'foo',
|
||||
inputs: [
|
||||
{ name: 'a', type: 'int' },
|
||||
{ name: 'b', type: 'int', indexed: true },
|
||||
{ name: 'c', type: 'address' },
|
||||
{ name: 'd', type: 'address', indexed: true }
|
||||
]
|
||||
});
|
||||
const decoded = event.decodeLog([
|
||||
'0000000000000000000000004444444444444444444444444444444444444444',
|
||||
'0000000000000000000000000000000000000000000000000000000000000002',
|
||||
'0000000000000000000000001111111111111111111111111111111111111111' ],
|
||||
'00000000000000000000000000000000000000000000000000000000000000030000000000000000000000002222222222222222222222222222222222222222');
|
||||
|
||||
expect(decoded.address).to.equal('0x4444444444444444444444444444444444444444');
|
||||
expect(decoded.params).to.deep.equal([
|
||||
new DecodedLogParam('a', new ParamType('int', null, 256), new Token('int', new BigNumber(3))),
|
||||
new DecodedLogParam('b', new ParamType('int', null, 256, true), new Token('int', new BigNumber(2))),
|
||||
new DecodedLogParam('c', new ParamType('address'), new Token('address', '0x2222222222222222222222222222222222222222')),
|
||||
new DecodedLogParam('d', new ParamType('address', null, 0, true), new Token('address', '0x1111111111111111111111111111111111111111'))
|
||||
]);
|
||||
});
|
||||
|
||||
it('decodes an anonymous event', () => {
|
||||
const event = new Event({ name: 'foo', inputs: [{ name: 'a', type: 'int' }], anonymous: true });
|
||||
const decoded = event.decodeLog([], '0000000000000000000000000000000000000000000000000000000000000003');
|
||||
|
||||
expect(decoded.address).to.not.be.ok;
|
||||
expect(decoded.params).to.deep.equal([
|
||||
new DecodedLogParam('a', new ParamType('int', null, 256), new Token('int', new BigNumber(3)))
|
||||
]);
|
||||
});
|
||||
|
||||
it('throws on invalid topics', () => {
|
||||
const event = new Event({ name: 'foo', inputs: [{ name: 'a', type: 'int' }], anonymous: true });
|
||||
|
||||
expect(() => event.decodeLog(['0000000000000000000000004444444444444444444444444444444444444444'], '0000000000000000000000000000000000000000000000000000000000000003')).to.throw(/Invalid/);
|
||||
});
|
||||
});
|
||||
});
|
||||
41
js/src/abi/spec/event/eventParam.js
Normal file
41
js/src/abi/spec/event/eventParam.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 { toParamType } from '../paramType/format';
|
||||
|
||||
export default class EventParam {
|
||||
constructor (name, type, indexed = false) {
|
||||
this._name = name;
|
||||
this._indexed = indexed;
|
||||
this._kind = toParamType(type, indexed);
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get kind () {
|
||||
return this._kind;
|
||||
}
|
||||
|
||||
get indexed () {
|
||||
return this._indexed;
|
||||
}
|
||||
|
||||
static toEventParams (params) {
|
||||
return params.map((param) => new EventParam(param.name, param.type, param.indexed));
|
||||
}
|
||||
}
|
||||
43
js/src/abi/spec/event/eventParam.spec.js
Normal file
43
js/src/abi/spec/event/eventParam.spec.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 EventParam from './eventParam';
|
||||
|
||||
describe('abi/spec/event/EventParam', () => {
|
||||
describe('constructor', () => {
|
||||
it('sets the properties', () => {
|
||||
const param = new EventParam('foo', 'uint', true);
|
||||
expect(param.name).to.equal('foo');
|
||||
expect(param.kind.type).to.equal('uint');
|
||||
expect(param.indexed).to.be.true;
|
||||
});
|
||||
|
||||
it('uses defaults for indexed', () => {
|
||||
expect(new EventParam('foo', 'uint').indexed).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('toEventParams', () => {
|
||||
it('maps an array of params', () => {
|
||||
const params = EventParam.toEventParams([{ name: 'foo', type: 'uint' }]);
|
||||
|
||||
expect(params.length).to.equal(1);
|
||||
expect(params[0].indexed).to.be.false;
|
||||
expect(params[0].name).to.equal('foo');
|
||||
expect(params[0].kind.type).to.equal('uint');
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/abi/spec/event/index.js
Normal file
17
js/src/abi/spec/event/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 './event';
|
||||
87
js/src/abi/spec/function.js
Normal file
87
js/src/abi/spec/function.js
Normal file
@@ -0,0 +1,87 @@
|
||||
// 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 Decoder from '../decoder/decoder';
|
||||
import Encoder from '../encoder/encoder';
|
||||
import Param from './param';
|
||||
import { methodSignature } from '../util/signature';
|
||||
|
||||
export default class Func {
|
||||
constructor (abi) {
|
||||
this._abi = abi;
|
||||
this._name = abi.name;
|
||||
this._constant = !!abi.constant;
|
||||
this._payable = abi.payable;
|
||||
this._inputs = Param.toParams(abi.inputs || []);
|
||||
this._outputs = Param.toParams(abi.outputs || []);
|
||||
|
||||
const { id, signature } = methodSignature(this._name, this.inputParamTypes());
|
||||
this._id = id;
|
||||
this._signature = signature;
|
||||
}
|
||||
|
||||
get abi () {
|
||||
return this._abi;
|
||||
}
|
||||
|
||||
get constant () {
|
||||
return this._constant;
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get id () {
|
||||
return this._id;
|
||||
}
|
||||
|
||||
get payable () {
|
||||
return this._payable;
|
||||
}
|
||||
|
||||
get inputs () {
|
||||
return this._inputs;
|
||||
}
|
||||
|
||||
get outputs () {
|
||||
return this._outputs;
|
||||
}
|
||||
|
||||
get signature () {
|
||||
return this._signature;
|
||||
}
|
||||
|
||||
inputParamTypes () {
|
||||
return this._inputs.map((input) => input.kind);
|
||||
}
|
||||
|
||||
outputParamTypes () {
|
||||
return this._outputs.map((output) => output.kind);
|
||||
}
|
||||
|
||||
encodeCall (tokens) {
|
||||
return `${this._signature}${Encoder.encode(tokens)}`;
|
||||
}
|
||||
|
||||
decodeInput (data) {
|
||||
return Decoder.decode(this.inputParamTypes(), data);
|
||||
}
|
||||
|
||||
decodeOutput (data) {
|
||||
return Decoder.decode(this.outputParamTypes(), data);
|
||||
}
|
||||
}
|
||||
89
js/src/abi/spec/function.spec.js
Normal file
89
js/src/abi/spec/function.spec.js
Normal file
@@ -0,0 +1,89 @@
|
||||
// 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 Func from './function';
|
||||
import Param from './param';
|
||||
import Token from '../token';
|
||||
|
||||
describe('abi/spec/Function', () => {
|
||||
const inputsArr = [{ name: 'boolin', type: 'bool' }, { name: 'stringin', type: 'string' }];
|
||||
const outputsArr = [{ name: 'output', type: 'uint' }];
|
||||
|
||||
const uint = new Param('output', 'uint');
|
||||
const bool = new Param('boolin', 'bool');
|
||||
const string = new Param('stringin', 'string');
|
||||
const inputs = [bool, string];
|
||||
const outputs = [uint];
|
||||
|
||||
const func = new Func({
|
||||
name: 'test',
|
||||
inputs: inputsArr,
|
||||
outputs: outputsArr
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('stores the parameters as received', () => {
|
||||
expect(func.name).to.equal('test');
|
||||
expect(func.constant).to.be.false;
|
||||
expect(func.inputs).to.deep.equal(inputs);
|
||||
expect(func.outputs).to.deep.equal(outputs);
|
||||
});
|
||||
|
||||
it('matches empty inputs with []', () => {
|
||||
expect(new Func({ name: 'test', outputs: outputsArr }).inputs).to.deep.equal([]);
|
||||
});
|
||||
|
||||
it('matches empty outputs with []', () => {
|
||||
expect(new Func({ name: 'test', inputs: inputsArr }).outputs).to.deep.equal([]);
|
||||
});
|
||||
|
||||
it('sets the method signature', () => {
|
||||
expect(new Func({ name: 'baz' }).signature).to.equal('a7916fac');
|
||||
});
|
||||
|
||||
it('allows constant functions', () => {
|
||||
expect(new Func({ name: 'baz', constant: true }).constant).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('inputParamTypes', () => {
|
||||
it('retrieves the input types as received', () => {
|
||||
expect(func.inputParamTypes()).to.deep.equal([bool.kind, string.kind]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('outputParamTypes', () => {
|
||||
it('retrieves the output types as received', () => {
|
||||
expect(func.outputParamTypes()).to.deep.equal([uint.kind]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('encodeCall', () => {
|
||||
it('encodes the call correctly', () => {
|
||||
const result = func.encodeCall([new Token('bool', true), new Token('string', 'jacogr')]);
|
||||
|
||||
expect(result).to.equal('023562050000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000066a61636f67720000000000000000000000000000000000000000000000000000');
|
||||
});
|
||||
});
|
||||
|
||||
describe('decodeOutput', () => {
|
||||
it('decodes the result correctly', () => {
|
||||
const result = func.decodeOutput('1111111111111111111111111111111111111111111111111111111111111111');
|
||||
|
||||
expect(result[0].value.toString(16)).to.equal('1111111111111111111111111111111111111111111111111111111111111111');
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/abi/spec/index.js
Normal file
17
js/src/abi/spec/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 './interface';
|
||||
73
js/src/abi/spec/interface.js
Normal file
73
js/src/abi/spec/interface.js
Normal file
@@ -0,0 +1,73 @@
|
||||
// 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 Constructor from './constructor';
|
||||
import Event from './event/event';
|
||||
import Func from './function';
|
||||
import Token from '../token';
|
||||
|
||||
export default class Interface {
|
||||
constructor (abi) {
|
||||
this._interface = Interface.parseABI(abi);
|
||||
}
|
||||
|
||||
get interface () {
|
||||
return this._interface;
|
||||
}
|
||||
|
||||
get constructors () {
|
||||
return this._interface.filter((item) => item instanceof Constructor);
|
||||
}
|
||||
|
||||
get events () {
|
||||
return this._interface.filter((item) => item instanceof Event);
|
||||
}
|
||||
|
||||
get functions () {
|
||||
return this._interface.filter((item) => item instanceof Func);
|
||||
}
|
||||
|
||||
encodeTokens (paramTypes, values) {
|
||||
const createToken = function (paramType, value) {
|
||||
if (paramType.subtype) {
|
||||
return new Token(paramType.type, value.map((entry) => createToken(paramType.subtype, entry)));
|
||||
}
|
||||
|
||||
return new Token(paramType.type, value);
|
||||
};
|
||||
|
||||
return paramTypes.map((paramType, idx) => createToken(paramType, values[idx]));
|
||||
}
|
||||
|
||||
static parseABI (abi) {
|
||||
return abi.map((item) => {
|
||||
switch (item.type) {
|
||||
case 'constructor':
|
||||
return new Constructor(item);
|
||||
|
||||
case 'event':
|
||||
return new Event(item);
|
||||
|
||||
case 'function':
|
||||
case 'fallback':
|
||||
return new Func(item);
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown ABI type ${item.type}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
126
js/src/abi/spec/interface.spec.js
Normal file
126
js/src/abi/spec/interface.spec.js
Normal file
@@ -0,0 +1,126 @@
|
||||
// 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 Interface from './interface';
|
||||
import ParamType from './paramType';
|
||||
import Token from '../token';
|
||||
|
||||
describe('abi/spec/Interface', () => {
|
||||
const construct = {
|
||||
type: 'constructor',
|
||||
inputs: []
|
||||
};
|
||||
const event = {
|
||||
type: 'event',
|
||||
name: 'Event2',
|
||||
anonymous: false,
|
||||
inputs: [{ name: 'a', type: 'uint256', indexed: true }, { name: 'b', type: 'bytes32', indexed: false }]
|
||||
};
|
||||
const func = {
|
||||
type: 'function',
|
||||
name: 'foo',
|
||||
inputs: [{ name: 'a', type: 'uint256' }],
|
||||
outputs: []
|
||||
};
|
||||
|
||||
describe('parseABI', () => {
|
||||
it('throws on invalid types', () => {
|
||||
expect(() => Interface.parseABI([{ type: 'noMatch' }])).to.throw(/noMatch/);
|
||||
});
|
||||
|
||||
it('creates constructors', () => {
|
||||
expect(Interface.parseABI([ construct ])).to.deep.equal([{ _inputs: [] }]);
|
||||
});
|
||||
|
||||
it('creates events', () => {
|
||||
expect(Interface.parseABI([ event ])[0].name).to.equal('Event2');
|
||||
});
|
||||
|
||||
it('creates functions', () => {
|
||||
expect(Interface.parseABI([ func ])[0].name).to.equal('foo');
|
||||
});
|
||||
|
||||
it('parse complex interfaces', () => {
|
||||
expect(Interface.parseABI([ construct, event, func ]).length).to.equal(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
const int = new Interface([ construct, event, func ]);
|
||||
|
||||
it('contains the full interface', () => {
|
||||
expect(int.interface.length).to.equal(3);
|
||||
});
|
||||
|
||||
it('contains the constructors', () => {
|
||||
expect(int.constructors.length).to.equal(1);
|
||||
});
|
||||
|
||||
it('contains the events', () => {
|
||||
expect(int.events.length).to.equal(1);
|
||||
});
|
||||
|
||||
it('contains the functions', () => {
|
||||
expect(int.functions.length).to.equal(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('encodeTokens', () => {
|
||||
const int = new Interface([ construct, event, func ]);
|
||||
|
||||
it('encodes simple types', () => {
|
||||
expect(
|
||||
int.encodeTokens(
|
||||
[new ParamType('bool'), new ParamType('string'), new ParamType('int'), new ParamType('uint')],
|
||||
[true, 'gavofyork', -123, 123]
|
||||
)
|
||||
).to.deep.equal([
|
||||
new Token('bool', true), new Token('string', 'gavofyork'), new Token('int', -123), new Token('uint', 123)
|
||||
]);
|
||||
});
|
||||
|
||||
it('encodes array', () => {
|
||||
expect(
|
||||
int.encodeTokens(
|
||||
[new ParamType('array', new ParamType('bool'))],
|
||||
[[true, false, true]]
|
||||
)
|
||||
).to.deep.equal([
|
||||
new Token('array', [
|
||||
new Token('bool', true), new Token('bool', false), new Token('bool', true)
|
||||
])
|
||||
]);
|
||||
});
|
||||
|
||||
it('encodes simple with array of array', () => {
|
||||
expect(
|
||||
int.encodeTokens(
|
||||
[
|
||||
new ParamType('bool'),
|
||||
new ParamType('fixedArray', new ParamType('array', new ParamType('uint')), 2)
|
||||
],
|
||||
[true, [[0, 1], [2, 3]]]
|
||||
)
|
||||
).to.deep.equal([
|
||||
new Token('bool', true),
|
||||
new Token('fixedArray', [
|
||||
new Token('array', [new Token('uint', 0), new Token('uint', 1)]),
|
||||
new Token('array', [new Token('uint', 2), new Token('uint', 3)])
|
||||
])
|
||||
]);
|
||||
});
|
||||
});
|
||||
});
|
||||
36
js/src/abi/spec/param.js
Normal file
36
js/src/abi/spec/param.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 { toParamType } from './paramType/format';
|
||||
|
||||
export default class Param {
|
||||
constructor (name, type) {
|
||||
this._name = name;
|
||||
this._kind = toParamType(type);
|
||||
}
|
||||
|
||||
get name () {
|
||||
return this._name;
|
||||
}
|
||||
|
||||
get kind () {
|
||||
return this._kind;
|
||||
}
|
||||
|
||||
static toParams (params) {
|
||||
return params.map((param) => new Param(param.name, param.type));
|
||||
}
|
||||
}
|
||||
38
js/src/abi/spec/param.spec.js
Normal file
38
js/src/abi/spec/param.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 Param from './param';
|
||||
|
||||
describe('abi/spec/Param', () => {
|
||||
describe('constructor', () => {
|
||||
const param = new Param('foo', 'uint');
|
||||
|
||||
it('sets the properties', () => {
|
||||
expect(param.name).to.equal('foo');
|
||||
expect(param.kind.type).to.equal('uint');
|
||||
});
|
||||
});
|
||||
|
||||
describe('toParams', () => {
|
||||
it('maps an array of params', () => {
|
||||
const params = Param.toParams([{ name: 'foo', type: 'uint' }]);
|
||||
|
||||
expect(params.length).to.equal(1);
|
||||
expect(params[0].name).to.equal('foo');
|
||||
expect(params[0].kind.type).to.equal('uint');
|
||||
});
|
||||
});
|
||||
});
|
||||
80
js/src/abi/spec/paramType/format.js
Normal file
80
js/src/abi/spec/paramType/format.js
Normal file
@@ -0,0 +1,80 @@
|
||||
// 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 ParamType from './paramType';
|
||||
|
||||
export function toParamType (type, indexed) {
|
||||
if (type[type.length - 1] === ']') {
|
||||
const last = type.lastIndexOf('[');
|
||||
const length = type.substr(last + 1, type.length - last - 2);
|
||||
const subtype = toParamType(type.substr(0, last));
|
||||
|
||||
if (length.length === 0) {
|
||||
return new ParamType('array', subtype, 0, indexed);
|
||||
}
|
||||
|
||||
return new ParamType('fixedArray', subtype, parseInt(length, 10), indexed);
|
||||
}
|
||||
|
||||
switch (type) {
|
||||
case 'address':
|
||||
case 'bool':
|
||||
case 'bytes':
|
||||
case 'string':
|
||||
return new ParamType(type, null, 0, indexed);
|
||||
|
||||
case 'int':
|
||||
case 'uint':
|
||||
return new ParamType(type, null, 256, indexed);
|
||||
|
||||
default:
|
||||
if (type.indexOf('uint') === 0) {
|
||||
return new ParamType('uint', null, parseInt(type.substr(4), 10), indexed);
|
||||
} else if (type.indexOf('int') === 0) {
|
||||
return new ParamType('int', null, parseInt(type.substr(3), 10), indexed);
|
||||
} else if (type.indexOf('bytes') === 0) {
|
||||
return new ParamType('fixedBytes', null, parseInt(type.substr(5), 10), indexed);
|
||||
}
|
||||
|
||||
throw new Error(`Cannot convert ${type} to valid ParamType`);
|
||||
}
|
||||
}
|
||||
|
||||
export function fromParamType (paramType) {
|
||||
switch (paramType.type) {
|
||||
case 'address':
|
||||
case 'bool':
|
||||
case 'bytes':
|
||||
case 'string':
|
||||
return paramType.type;
|
||||
|
||||
case 'int':
|
||||
case 'uint':
|
||||
return `${paramType.type}${paramType.length}`;
|
||||
|
||||
case 'fixedBytes':
|
||||
return `bytes${paramType.length}`;
|
||||
|
||||
case 'fixedArray':
|
||||
return `${fromParamType(paramType.subtype)}[${paramType.length}]`;
|
||||
|
||||
case 'array':
|
||||
return `${fromParamType(paramType.subtype)}[]`;
|
||||
|
||||
default:
|
||||
throw new Error(`Cannot convert from ParamType ${paramType.type}`);
|
||||
}
|
||||
}
|
||||
228
js/src/abi/spec/paramType/format.spec.js
Normal file
228
js/src/abi/spec/paramType/format.spec.js
Normal file
@@ -0,0 +1,228 @@
|
||||
// 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 ParamType from './paramType';
|
||||
import { fromParamType, toParamType } from './format';
|
||||
|
||||
describe('abi/spec/paramType/format', () => {
|
||||
describe('fromParamType', () => {
|
||||
it('errors on invalid types', () => {
|
||||
expect(() => fromParamType({ type: 'noMatch' })).to.throw(/noMatch/);
|
||||
});
|
||||
|
||||
describe('simple types', () => {
|
||||
it('converts address to address', () => {
|
||||
const pt = new ParamType('address');
|
||||
|
||||
expect(fromParamType(pt)).to.equal('address');
|
||||
});
|
||||
|
||||
it('converts bool to bool', () => {
|
||||
const pt = new ParamType('bool');
|
||||
|
||||
expect(fromParamType(pt)).to.equal('bool');
|
||||
});
|
||||
|
||||
it('converts bytes to bytes', () => {
|
||||
const pt = new ParamType('bytes');
|
||||
|
||||
expect(fromParamType(pt)).to.equal('bytes');
|
||||
});
|
||||
|
||||
it('converts string to string', () => {
|
||||
const pt = new ParamType('string');
|
||||
|
||||
expect(fromParamType(pt)).to.equal('string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('length types', () => {
|
||||
it('converts int32 to int32', () => {
|
||||
const pt = new ParamType('int', null, 32);
|
||||
|
||||
expect(fromParamType(pt)).to.equal('int32');
|
||||
});
|
||||
|
||||
it('converts uint64 to int64', () => {
|
||||
const pt = new ParamType('uint', null, 64);
|
||||
|
||||
expect(fromParamType(pt)).to.equal('uint64');
|
||||
});
|
||||
|
||||
it('converts fixedBytes8 to bytes8', () => {
|
||||
const pt = new ParamType('fixedBytes', null, 8);
|
||||
|
||||
expect(fromParamType(pt)).to.equal('bytes8');
|
||||
});
|
||||
});
|
||||
|
||||
describe('arrays', () => {
|
||||
it('converts string[2] to string[2]', () => {
|
||||
const pt = new ParamType('fixedArray', new ParamType('string'), 2);
|
||||
|
||||
expect(fromParamType(pt)).to.equal('string[2]');
|
||||
});
|
||||
|
||||
it('converts bool[] to bool[]', () => {
|
||||
const pt = new ParamType('array', new ParamType('bool'));
|
||||
|
||||
expect(fromParamType(pt)).to.equal('bool[]');
|
||||
});
|
||||
|
||||
it('converts bool[][2] to bool[][2]', () => {
|
||||
const pt = new ParamType('fixedArray', new ParamType('array', new ParamType('bool')), 2);
|
||||
|
||||
expect(fromParamType(pt)).to.equal('bool[][2]');
|
||||
});
|
||||
|
||||
it('converts bool[2][] to bool[2][]', () => {
|
||||
const pt = new ParamType('array', new ParamType('fixedArray', new ParamType('bool'), 2));
|
||||
|
||||
expect(fromParamType(pt)).to.equal('bool[2][]');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('toParamType', () => {
|
||||
it('errors on invalid types', () => {
|
||||
expect(() => toParamType('noMatch')).to.throw(/noMatch/);
|
||||
});
|
||||
|
||||
describe('simple mapping', () => {
|
||||
it('converts address to address', () => {
|
||||
const pt = toParamType('address');
|
||||
|
||||
expect(pt.type).to.equal('address');
|
||||
});
|
||||
|
||||
it('converts bool to bool', () => {
|
||||
const pt = toParamType('bool');
|
||||
|
||||
expect(pt.type).to.equal('bool');
|
||||
});
|
||||
|
||||
it('converts bytes to bytes', () => {
|
||||
const pt = toParamType('bytes');
|
||||
|
||||
expect(pt.type).to.equal('bytes');
|
||||
});
|
||||
|
||||
it('converts string to string', () => {
|
||||
const pt = toParamType('string');
|
||||
|
||||
expect(pt.type).to.equal('string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('number', () => {
|
||||
it('converts int to int256', () => {
|
||||
const pt = toParamType('int');
|
||||
|
||||
expect(pt.type).to.equal('int');
|
||||
expect(pt.length).to.equal(256);
|
||||
});
|
||||
|
||||
it('converts uint to uint256', () => {
|
||||
const pt = toParamType('uint');
|
||||
|
||||
expect(pt.type).to.equal('uint');
|
||||
expect(pt.length).to.equal(256);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sized types', () => {
|
||||
it('converts int32 to int32', () => {
|
||||
const pt = toParamType('int32');
|
||||
|
||||
expect(pt.type).to.equal('int');
|
||||
expect(pt.length).to.equal(32);
|
||||
});
|
||||
|
||||
it('converts uint16 to uint16', () => {
|
||||
const pt = toParamType('uint32');
|
||||
|
||||
expect(pt.type).to.equal('uint');
|
||||
expect(pt.length).to.equal(32);
|
||||
});
|
||||
|
||||
it('converts bytes8 to fixedBytes8', () => {
|
||||
const pt = toParamType('bytes8');
|
||||
|
||||
expect(pt.type).to.equal('fixedBytes');
|
||||
expect(pt.length).to.equal(8);
|
||||
});
|
||||
});
|
||||
|
||||
describe('arrays', () => {
|
||||
describe('fixed arrays', () => {
|
||||
it('creates fixed array', () => {
|
||||
const pt = toParamType('bytes[8]');
|
||||
|
||||
expect(pt.type).to.equal('fixedArray');
|
||||
expect(pt.subtype.type).to.equal('bytes');
|
||||
expect(pt.length).to.equal(8);
|
||||
});
|
||||
|
||||
it('creates fixed arrays of fixed arrays', () => {
|
||||
const pt = toParamType('bytes[45][3]');
|
||||
|
||||
expect(pt.type).to.equal('fixedArray');
|
||||
expect(pt.length).to.equal(3);
|
||||
expect(pt.subtype.type).to.equal('fixedArray');
|
||||
expect(pt.subtype.length).to.equal(45);
|
||||
expect(pt.subtype.subtype.type).to.equal('bytes');
|
||||
});
|
||||
});
|
||||
|
||||
describe('dynamic arrays', () => {
|
||||
it('creates a dynamic array', () => {
|
||||
const pt = toParamType('bytes[]');
|
||||
|
||||
expect(pt.type).to.equal('array');
|
||||
expect(pt.subtype.type).to.equal('bytes');
|
||||
});
|
||||
|
||||
it('creates a dynamic array of dynamic arrays', () => {
|
||||
const pt = toParamType('bool[][]');
|
||||
|
||||
expect(pt.type).to.equal('array');
|
||||
expect(pt.subtype.type).to.equal('array');
|
||||
expect(pt.subtype.subtype.type).to.equal('bool');
|
||||
});
|
||||
});
|
||||
|
||||
describe('mixed arrays', () => {
|
||||
it('creates a fixed dynamic array', () => {
|
||||
const pt = toParamType('bool[][3]');
|
||||
|
||||
expect(pt.type).to.equal('fixedArray');
|
||||
expect(pt.length).to.equal(3);
|
||||
expect(pt.subtype.type).to.equal('array');
|
||||
expect(pt.subtype.subtype.type).to.equal('bool');
|
||||
});
|
||||
|
||||
it('creates a dynamic fixed array', () => {
|
||||
const pt = toParamType('bool[3][]');
|
||||
|
||||
expect(pt.type).to.equal('array');
|
||||
expect(pt.subtype.type).to.equal('fixedArray');
|
||||
expect(pt.subtype.length).to.equal(3);
|
||||
expect(pt.subtype.subtype.type).to.equal('bool');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
17
js/src/abi/spec/paramType/index.js
Normal file
17
js/src/abi/spec/paramType/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 './paramType';
|
||||
52
js/src/abi/spec/paramType/paramType.js
Normal file
52
js/src/abi/spec/paramType/paramType.js
Normal file
@@ -0,0 +1,52 @@
|
||||
// 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 TYPES from './types';
|
||||
|
||||
export default class ParamType {
|
||||
constructor (type, subtype = null, length = 0, indexed = false) {
|
||||
ParamType.validateType(type);
|
||||
|
||||
this._type = type;
|
||||
this._subtype = subtype;
|
||||
this._length = length;
|
||||
this._indexed = indexed;
|
||||
}
|
||||
|
||||
get type () {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
get subtype () {
|
||||
return this._subtype;
|
||||
}
|
||||
|
||||
get length () {
|
||||
return this._length;
|
||||
}
|
||||
|
||||
get indexed () {
|
||||
return this._indexed;
|
||||
}
|
||||
|
||||
static validateType (type) {
|
||||
if (TYPES.filter((_type) => type === _type).length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new Error(`Invalid type ${type} received for ParamType`);
|
||||
}
|
||||
}
|
||||
87
js/src/abi/spec/paramType/paramType.spec.js
Normal file
87
js/src/abi/spec/paramType/paramType.spec.js
Normal file
@@ -0,0 +1,87 @@
|
||||
// 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 ParamType from './paramType';
|
||||
|
||||
describe('abi/spec/paramType/ParamType', () => {
|
||||
describe('validateType', () => {
|
||||
it('validates address', () => {
|
||||
expect(ParamType.validateType('address')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates fixedArray', () => {
|
||||
expect(ParamType.validateType('fixedArray')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates array', () => {
|
||||
expect(ParamType.validateType('array')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates fixedBytes', () => {
|
||||
expect(ParamType.validateType('fixedBytes')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates bytes', () => {
|
||||
expect(ParamType.validateType('bytes')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates bool', () => {
|
||||
expect(ParamType.validateType('bool')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates int', () => {
|
||||
expect(ParamType.validateType('int')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates uint', () => {
|
||||
expect(ParamType.validateType('uint')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates string', () => {
|
||||
expect(ParamType.validateType('string')).to.be.true;
|
||||
});
|
||||
|
||||
it('throws an error on invalid types', () => {
|
||||
expect(() => ParamType.validateType('noMatch')).to.throw(/noMatch/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('throws an error on invalid types', () => {
|
||||
expect(() => new ParamType('noMatch')).to.throw(/noMatch/);
|
||||
});
|
||||
|
||||
it('sets the type of the object', () => {
|
||||
expect((new ParamType('bool', null, 1)).type).to.equal('bool');
|
||||
});
|
||||
|
||||
it('sets the subtype of the object', () => {
|
||||
expect((new ParamType('array', 'bool', 1)).subtype).to.equal('bool');
|
||||
});
|
||||
|
||||
it('sets the length of the object', () => {
|
||||
expect((new ParamType('array', 'bool', 1)).length).to.equal(1);
|
||||
});
|
||||
|
||||
it('sets the index of the object', () => {
|
||||
expect((new ParamType('array', 'bool', 1, true)).indexed).to.be.true;
|
||||
});
|
||||
|
||||
it('sets default values where none supplied', () => {
|
||||
expect(Object.values(new ParamType('string'))).to.deep.equal(['string', null, 0, false]);
|
||||
});
|
||||
});
|
||||
});
|
||||
19
js/src/abi/spec/paramType/types.js
Normal file
19
js/src/abi/spec/paramType/types.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/>.
|
||||
|
||||
const TYPES = ['address', 'bytes', 'int', 'uint', 'bool', 'string', 'array', 'fixedBytes', 'fixedArray'];
|
||||
|
||||
export default TYPES;
|
||||
17
js/src/abi/token/index.js
Normal file
17
js/src/abi/token/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 './token';
|
||||
42
js/src/abi/token/token.js
Normal file
42
js/src/abi/token/token.js
Normal file
@@ -0,0 +1,42 @@
|
||||
// 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 TYPES from '../spec/paramType/types';
|
||||
|
||||
export default class Token {
|
||||
constructor (type, value) {
|
||||
Token.validateType(type);
|
||||
|
||||
this._type = type;
|
||||
this._value = value;
|
||||
}
|
||||
|
||||
get type () {
|
||||
return this._type;
|
||||
}
|
||||
|
||||
get value () {
|
||||
return this._value;
|
||||
}
|
||||
|
||||
static validateType (type) {
|
||||
if (TYPES.filter((_type) => type === _type).length) {
|
||||
return true;
|
||||
}
|
||||
|
||||
throw new Error(`Invalid type ${type} received for Token`);
|
||||
}
|
||||
}
|
||||
75
js/src/abi/token/token.spec.js
Normal file
75
js/src/abi/token/token.spec.js
Normal file
@@ -0,0 +1,75 @@
|
||||
// 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 Token from './token';
|
||||
|
||||
describe('abi/token/token', () => {
|
||||
describe('validateType', () => {
|
||||
it('validates address', () => {
|
||||
expect(Token.validateType('address')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates fixedArray', () => {
|
||||
expect(Token.validateType('fixedArray')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates array', () => {
|
||||
expect(Token.validateType('array')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates fixedBytes', () => {
|
||||
expect(Token.validateType('fixedBytes')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates bytes', () => {
|
||||
expect(Token.validateType('bytes')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates bool', () => {
|
||||
expect(Token.validateType('bool')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates int', () => {
|
||||
expect(Token.validateType('int')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates uint', () => {
|
||||
expect(Token.validateType('uint')).to.be.true;
|
||||
});
|
||||
|
||||
it('validates string', () => {
|
||||
expect(Token.validateType('string')).to.be.true;
|
||||
});
|
||||
|
||||
it('throws an error on invalid types', () => {
|
||||
expect(() => Token.validateType('noMatch')).to.throw(/noMatch/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('constructor', () => {
|
||||
it('throws an error on invalid types', () => {
|
||||
expect(() => new Token('noMatch', '1')).to.throw(/noMatch/);
|
||||
});
|
||||
|
||||
it('sets the type of the object', () => {
|
||||
expect((new Token('bool', '1')).type).to.equal('bool');
|
||||
});
|
||||
|
||||
it('sets the value of the object', () => {
|
||||
expect((new Token('bool', '1')).value).to.equal('1');
|
||||
});
|
||||
});
|
||||
});
|
||||
65
js/src/abi/util/address.js
Normal file
65
js/src/abi/util/address.js
Normal file
@@ -0,0 +1,65 @@
|
||||
// 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 isChecksumValid (_address) {
|
||||
const address = _address.replace('0x', '');
|
||||
const hash = keccak_256(address.toLowerCase(address));
|
||||
|
||||
for (let n = 0; n < 40; n++) {
|
||||
const hashval = parseInt(hash[n], 16);
|
||||
const isLower = address[n].toUpperCase() !== address[n];
|
||||
const isUpper = address[n].toLowerCase() !== address[n];
|
||||
|
||||
if ((hashval > 7 && isLower) || (hashval <= 7 && isUpper)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
export function isAddress (address) {
|
||||
if (address && address.length === 42) {
|
||||
if (!/^(0x)?[0-9a-f]{40}$/i.test(address)) {
|
||||
return false;
|
||||
} else if (/^(0x)?[0-9a-f]{40}$/.test(address) || /^(0x)?[0-9A-F]{40}$/.test(address)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return isChecksumValid(address);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
export function toChecksumAddress (_address) {
|
||||
const address = (_address || '').toLowerCase();
|
||||
|
||||
if (!isAddress(address)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const hash = keccak_256(address.slice(-40));
|
||||
let result = '0x';
|
||||
|
||||
for (let n = 0; n < 40; n++) {
|
||||
result = `${result}${parseInt(hash[n], 16) > 7 ? address[n + 2].toUpperCase() : address[n + 2]}`;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
100
js/src/abi/util/address.spec.js
Normal file
100
js/src/abi/util/address.spec.js
Normal file
@@ -0,0 +1,100 @@
|
||||
// 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 { isChecksumValid, isAddress, toChecksumAddress } from './address';
|
||||
|
||||
describe('abi/util/address', () => {
|
||||
const value = '63Cf90D3f0410092FC0fca41846f596223979195';
|
||||
const address = `0x${value}`;
|
||||
const lowercase = `0x${value.toLowerCase()}`;
|
||||
const uppercase = `0x${value.toUpperCase()}`;
|
||||
const invalid = '0x' + value.split('').map((char) => {
|
||||
if (char >= 'a' && char <= 'f') {
|
||||
return char.toUpperCase();
|
||||
} else if (char >= 'A' && char <= 'F') {
|
||||
return char.toLowerCase();
|
||||
}
|
||||
|
||||
return char;
|
||||
}).join('');
|
||||
const invalidhex = '0x01234567890123456789012345678901234567gh';
|
||||
|
||||
describe('isChecksumValid', () => {
|
||||
it('returns false when fully lowercase', () => {
|
||||
expect(isChecksumValid(lowercase)).to.be.false;
|
||||
});
|
||||
|
||||
it('returns false when fully uppercase', () => {
|
||||
expect(isChecksumValid(uppercase)).to.be.false;
|
||||
});
|
||||
|
||||
it('returns false on a mixed-case address', () => {
|
||||
expect(isChecksumValid(invalid)).to.be.false;
|
||||
});
|
||||
|
||||
it('returns true on a checksummed address', () => {
|
||||
expect(isChecksumValid(address)).to.be.true;
|
||||
});
|
||||
});
|
||||
|
||||
describe('isAddress', () => {
|
||||
it('returns true when fully lowercase', () => {
|
||||
expect(isAddress(lowercase)).to.be.true;
|
||||
});
|
||||
|
||||
it('returns true when fully uppercase', () => {
|
||||
expect(isAddress(uppercase)).to.be.true;
|
||||
});
|
||||
|
||||
it('returns true when checksummed', () => {
|
||||
expect(isAddress(address)).to.be.true;
|
||||
});
|
||||
|
||||
it('returns false when invalid checksum', () => {
|
||||
expect(isAddress(invalid)).to.be.false;
|
||||
});
|
||||
|
||||
it('returns false on valid length, non-hex', () => {
|
||||
expect(isAddress(invalidhex)).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('toChecksumAddress', () => {
|
||||
it('returns empty when no address specified', () => {
|
||||
expect(toChecksumAddress()).to.equal('');
|
||||
});
|
||||
|
||||
it('returns empty on invalid address structure', () => {
|
||||
expect(toChecksumAddress('0xnotaddress')).to.equal('');
|
||||
});
|
||||
|
||||
it('returns formatted address on checksum input', () => {
|
||||
expect(toChecksumAddress(address)).to.equal(address);
|
||||
});
|
||||
|
||||
it('returns formatted address on lowercase input', () => {
|
||||
expect(toChecksumAddress(lowercase)).to.equal(address);
|
||||
});
|
||||
|
||||
it('returns formatted address on uppercase input', () => {
|
||||
expect(toChecksumAddress(uppercase)).to.equal(address);
|
||||
});
|
||||
|
||||
it('returns formatted address on mixed input', () => {
|
||||
expect(toChecksumAddress(invalid)).to.equal(address);
|
||||
});
|
||||
});
|
||||
});
|
||||
75
js/src/abi/util/pad.js
Normal file
75
js/src/abi/util/pad.js
Normal file
@@ -0,0 +1,75 @@
|
||||
// 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 utf8 from 'utf8';
|
||||
|
||||
import { isArray } from './types';
|
||||
|
||||
const ZERO_64 = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||
|
||||
export function padAddress (_input) {
|
||||
const input = _input.substr(0, 2) === '0x' ? _input.substr(2) : _input;
|
||||
|
||||
return `${ZERO_64}${input}`.slice(-64);
|
||||
}
|
||||
|
||||
export function padBool (input) {
|
||||
return `${ZERO_64}${input ? '1' : '0'}`.slice(-64);
|
||||
}
|
||||
|
||||
export function padU32 (input) {
|
||||
let bn = new BigNumber(input);
|
||||
|
||||
if (bn.lessThan(0)) {
|
||||
bn = new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16)
|
||||
.plus(bn).plus(1);
|
||||
}
|
||||
|
||||
return `${ZERO_64}${bn.toString(16)}`.slice(-64);
|
||||
}
|
||||
|
||||
function stringToBytes (input) {
|
||||
if (isArray(input)) {
|
||||
return input;
|
||||
} else if (input.substr(0, 2) === '0x') {
|
||||
return input.substr(2).toLowerCase().match(/.{1,2}/g).map((value) => parseInt(value, 16));
|
||||
} else {
|
||||
return input.split('').map((char) => char.charCodeAt(0));
|
||||
}
|
||||
}
|
||||
|
||||
export function padBytes (_input) {
|
||||
const input = stringToBytes(_input);
|
||||
|
||||
return `${padU32(input.length)}${padFixedBytes(input)}`;
|
||||
}
|
||||
|
||||
export function padFixedBytes (_input) {
|
||||
const input = stringToBytes(_input);
|
||||
const sinput = input.map((code) => `0${code.toString(16)}`.slice(-2)).join('');
|
||||
const max = Math.floor((sinput.length + 63) / 64) * 64;
|
||||
|
||||
return `${sinput}${ZERO_64}`.substr(0, max);
|
||||
}
|
||||
|
||||
export function padString (input) {
|
||||
const array = utf8.encode(input)
|
||||
.split('')
|
||||
.map((char) => char.charCodeAt(0));
|
||||
|
||||
return padBytes(array);
|
||||
}
|
||||
124
js/src/abi/util/pad.spec.js
Normal file
124
js/src/abi/util/pad.spec.js
Normal file
@@ -0,0 +1,124 @@
|
||||
// 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 { padAddress, padBool, padBytes, padFixedBytes, padString, padU32 } from './pad';
|
||||
|
||||
describe('abi/util/pad', () => {
|
||||
const SHORT15 = '1234567890abcdef';
|
||||
const BYTES15 = [0x12, 0x34, 0x56, 0x78, 0x90, 0xab, 0xcd, 0xef];
|
||||
const LONG15 = `${SHORT15}000000000000000000000000000000000000000000000000`;
|
||||
const PAD123 = '0000000000000000000000000000000000000000000000000000000000000123';
|
||||
|
||||
describe('padAddress', () => {
|
||||
it('pads to 64 characters', () => {
|
||||
expect(padAddress('123')).to.equal(PAD123);
|
||||
});
|
||||
|
||||
it('strips leading 0x when passed in', () => {
|
||||
expect(padFixedBytes(`0x${PAD123}`)).to.equal(PAD123);
|
||||
});
|
||||
});
|
||||
|
||||
describe('padBool', () => {
|
||||
const TRUE = '0000000000000000000000000000000000000000000000000000000000000001';
|
||||
const FALSE = '0000000000000000000000000000000000000000000000000000000000000000';
|
||||
|
||||
it('pads true to 64 characters', () => {
|
||||
expect(padBool(true)).to.equal(TRUE);
|
||||
});
|
||||
|
||||
it('pads false to 64 characters', () => {
|
||||
expect(padBool(false)).to.equal(FALSE);
|
||||
});
|
||||
});
|
||||
|
||||
describe('padU32', () => {
|
||||
it('left pads length < 64 bytes to 64 bytes', () => {
|
||||
expect(padU32(1)).to.equal('0000000000000000000000000000000000000000000000000000000000000001');
|
||||
});
|
||||
|
||||
it('pads hex representation', () => {
|
||||
expect(padU32(0x123)).to.equal(PAD123);
|
||||
});
|
||||
|
||||
it('pads decimal representation', () => {
|
||||
expect(padU32(291)).to.equal(PAD123);
|
||||
});
|
||||
|
||||
it('pads string representation', () => {
|
||||
expect(padU32('0x123')).to.equal(PAD123);
|
||||
});
|
||||
|
||||
it('pads BigNumber representation', () => {
|
||||
expect(padU32(new BigNumber(0x123))).to.equal(PAD123);
|
||||
});
|
||||
|
||||
it('converts negative numbers to 2s complement', () => {
|
||||
expect(padU32(-123)).to.equal('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85');
|
||||
});
|
||||
});
|
||||
|
||||
describe('padFixedBytes', () => {
|
||||
it('right pads length < 64 bytes to 64 bytes (string)', () => {
|
||||
expect(padFixedBytes(`0x${SHORT15}`)).to.equal(LONG15);
|
||||
});
|
||||
|
||||
it('right pads length < 64 bytes to 64 bytes (array)', () => {
|
||||
expect(padFixedBytes(BYTES15)).to.equal(LONG15);
|
||||
});
|
||||
|
||||
it('right pads length > 64 bytes (64 byte multiples)', () => {
|
||||
expect(padFixedBytes(`0x${LONG15}${SHORT15}`)).to.equal(`${LONG15}${LONG15}`);
|
||||
});
|
||||
|
||||
it('strips leading 0x when passed in', () => {
|
||||
expect(padFixedBytes(`0x${SHORT15}`)).to.equal(LONG15);
|
||||
});
|
||||
});
|
||||
|
||||
describe('padBytes', () => {
|
||||
it('right pads length < 64, adding the length (string)', () => {
|
||||
const result = padBytes(`0x${SHORT15}`);
|
||||
|
||||
expect(result.length).to.equal(128);
|
||||
expect(result).to.equal(`${padU32(8)}${LONG15}`);
|
||||
});
|
||||
|
||||
it('right pads length < 64, adding the length (array)', () => {
|
||||
const result = padBytes(BYTES15);
|
||||
|
||||
expect(result.length).to.equal(128);
|
||||
expect(result).to.equal(`${padU32(8)}${LONG15}`);
|
||||
});
|
||||
|
||||
it('right pads length > 64, adding the length', () => {
|
||||
const result = padBytes(`0x${LONG15}${SHORT15}`);
|
||||
|
||||
expect(result.length).to.equal(192);
|
||||
expect(result).to.equal(`${padU32(0x28)}${LONG15}${LONG15}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('padString', () => {
|
||||
it('correctly converts & pads strings', () => {
|
||||
const result = padString('gavofyork');
|
||||
|
||||
expect(result.length).to.equal(128);
|
||||
expect(result).to.equal(padBytes('0x6761766f66796f726b'));
|
||||
});
|
||||
});
|
||||
});
|
||||
31
js/src/abi/util/signature.js
Normal file
31
js/src/abi/util/signature.js
Normal file
@@ -0,0 +1,31 @@
|
||||
// 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 { fromParamType } from '../spec/paramType/format';
|
||||
|
||||
export function eventSignature (name, params) {
|
||||
const types = (params || []).map(fromParamType).join(',');
|
||||
const id = `${name || ''}(${types})`;
|
||||
|
||||
return { id, signature: keccak_256(id) };
|
||||
}
|
||||
|
||||
export function methodSignature (name, params) {
|
||||
const { id, signature } = eventSignature(name, params);
|
||||
|
||||
return { id, signature: signature.substr(0, 8) };
|
||||
}
|
||||
68
js/src/abi/util/signature.spec.js
Normal file
68
js/src/abi/util/signature.spec.js
Normal file
@@ -0,0 +1,68 @@
|
||||
// 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 { eventSignature, methodSignature } from './signature';
|
||||
|
||||
describe('abi/util/signature', () => {
|
||||
describe('eventSignature', () => {
|
||||
it('encodes signature baz() correctly', () => {
|
||||
expect(eventSignature('baz', []))
|
||||
.to.deep.equal({ id: 'baz()', signature: 'a7916fac4f538170f7cd12c148552e2cba9fcd72329a2dd5b07a6fa906488ddf' });
|
||||
});
|
||||
|
||||
it('encodes signature baz(uint32) correctly', () => {
|
||||
expect(eventSignature('baz', [{ type: 'uint', length: 32 }]))
|
||||
.to.deep.equal({ id: 'baz(uint32)', signature: '7d68785e8fc871be024b75964bd86d093511d4bc2dc7cf7bea32c48a0efaecb1' });
|
||||
});
|
||||
|
||||
it('encodes signature baz(uint32, bool) correctly', () => {
|
||||
expect(eventSignature('baz', [{ type: 'uint', length: 32 }, { type: 'bool' }]))
|
||||
.to.deep.equal({ id: 'baz(uint32,bool)', signature: 'cdcd77c0992ec5bbfc459984220f8c45084cc24d9b6efed1fae540db8de801d2' });
|
||||
});
|
||||
|
||||
it('encodes no-name signature correctly as ()', () => {
|
||||
expect(eventSignature(undefined, []))
|
||||
.to.deep.equal({ id: '()', signature: '861731d50c3880a2ca1994d5ec287b94b2f4bd832a67d3e41c08177bdd5674fe' });
|
||||
});
|
||||
|
||||
it('encodes no-params signature correctly as ()', () => {
|
||||
expect(eventSignature(undefined, undefined))
|
||||
.to.deep.equal({ id: '()', signature: '861731d50c3880a2ca1994d5ec287b94b2f4bd832a67d3e41c08177bdd5674fe' });
|
||||
});
|
||||
});
|
||||
|
||||
describe('methodSignature', () => {
|
||||
it('encodes signature baz() correctly', () => {
|
||||
expect(methodSignature('baz', [])).to.deep.equal({ id: 'baz()', signature: 'a7916fac' });
|
||||
});
|
||||
|
||||
it('encodes signature baz(uint32) correctly', () => {
|
||||
expect(methodSignature('baz', [{ type: 'uint', length: 32 }])).to.deep.equal({ id: 'baz(uint32)', signature: '7d68785e' });
|
||||
});
|
||||
|
||||
it('encodes signature baz(uint32, bool) correctly', () => {
|
||||
expect(methodSignature('baz', [{ type: 'uint', length: 32 }, { type: 'bool' }])).to.deep.equal({ id: 'baz(uint32,bool)', signature: 'cdcd77c0' });
|
||||
});
|
||||
|
||||
it('encodes no-name signature correctly as ()', () => {
|
||||
expect(methodSignature(undefined, [])).to.deep.equal({ id: '()', signature: '861731d5' });
|
||||
});
|
||||
|
||||
it('encodes no-params signature correctly as ()', () => {
|
||||
expect(methodSignature(undefined, undefined)).to.deep.equal({ id: '()', signature: '861731d5' });
|
||||
});
|
||||
});
|
||||
});
|
||||
35
js/src/abi/util/slice.js
Normal file
35
js/src/abi/util/slice.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 { padAddress } from './pad';
|
||||
|
||||
export function sliceData (_data) {
|
||||
if (!_data || !_data.length) {
|
||||
return [];
|
||||
}
|
||||
|
||||
let data = (_data.substr(0, 2) === '0x') ? _data.substr(2) : _data;
|
||||
|
||||
if (!data.length) {
|
||||
data = padAddress('');
|
||||
}
|
||||
|
||||
if (data.length % 64) {
|
||||
throw new Error(`Invalid data length (not mod 64) passed to sliceData, ${data}, % 64 == ${data.length % 64}`);
|
||||
}
|
||||
|
||||
return data.match(/.{1,64}/g);
|
||||
}
|
||||
48
js/src/abi/util/slice.spec.js
Normal file
48
js/src/abi/util/slice.spec.js
Normal file
@@ -0,0 +1,48 @@
|
||||
// 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 { sliceData } from './slice';
|
||||
|
||||
describe('abi/util/slice', () => {
|
||||
describe('sliceData', () => {
|
||||
const slice1 = '131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b';
|
||||
const slice2 = '2124768576358735263578356373526387638357635873563586353756358763';
|
||||
|
||||
it('throws an error on mod 64 != 0', () => {
|
||||
expect(() => sliceData('123')).to.throw(/sliceData/);
|
||||
});
|
||||
|
||||
it('returns an empty array when length === 0', () => {
|
||||
expect(sliceData('')).to.deep.equal([]);
|
||||
});
|
||||
|
||||
it('returns an array with the slices otherwise', () => {
|
||||
const sliced = sliceData(`${slice1}${slice2}`);
|
||||
|
||||
expect(sliced.length).to.equal(2);
|
||||
expect(sliced[0]).to.equal(slice1);
|
||||
expect(sliced[1]).to.equal(slice2);
|
||||
});
|
||||
|
||||
it('removes leading 0x when passed in', () => {
|
||||
const sliced = sliceData(`0x${slice1}${slice2}`);
|
||||
|
||||
expect(sliced.length).to.equal(2);
|
||||
expect(sliced[0]).to.equal(slice1);
|
||||
expect(sliced[1]).to.equal(slice2);
|
||||
});
|
||||
});
|
||||
});
|
||||
47
js/src/abi/util/sliceAs.js
Normal file
47
js/src/abi/util/sliceAs.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 BigNumber from 'bignumber.js';
|
||||
|
||||
import { toChecksumAddress } from './address';
|
||||
|
||||
export function asU32 (slice) {
|
||||
// TODO: validation
|
||||
|
||||
return new BigNumber(slice, 16);
|
||||
}
|
||||
|
||||
export function asI32 (slice) {
|
||||
if (new BigNumber(slice.substr(0, 1), 16).toString(2)[0] === '1') {
|
||||
return new BigNumber(slice, 16)
|
||||
.minus(new BigNumber('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff', 16))
|
||||
.minus(1);
|
||||
}
|
||||
|
||||
return new BigNumber(slice, 16);
|
||||
}
|
||||
|
||||
export function asAddress (slice) {
|
||||
// TODO: address validation?
|
||||
|
||||
return toChecksumAddress(`0x${slice.slice(-40)}`);
|
||||
}
|
||||
|
||||
export function asBool (slice) {
|
||||
// TODO: everything else should be 0
|
||||
|
||||
return new BigNumber(slice[63]).eq(1);
|
||||
}
|
||||
54
js/src/abi/util/sliceAs.spec.js
Normal file
54
js/src/abi/util/sliceAs.spec.js
Normal file
@@ -0,0 +1,54 @@
|
||||
// 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 { asAddress, asBool, asI32, asU32 } from './sliceAs';
|
||||
|
||||
describe('abi/util/sliceAs', () => {
|
||||
const MAX_INT = 'ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff';
|
||||
|
||||
describe('asAddress', () => {
|
||||
it('correctly returns the last 0x40 characters', () => {
|
||||
const address = '1111111111222222222233333333334444444444';
|
||||
expect(asAddress(`000000000000000000000000${address}`)).to.equal(`0x${address}`);
|
||||
});
|
||||
});
|
||||
|
||||
describe('asBool', () => {
|
||||
it('correctly returns true', () => {
|
||||
expect(asBool('0000000000000000000000000000000000000000000000000000000000000001')).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly returns false', () => {
|
||||
expect(asBool('0000000000000000000000000000000000000000000000000000000000000000')).to.be.false;
|
||||
});
|
||||
});
|
||||
|
||||
describe('asI32', () => {
|
||||
it('correctly decodes positive numbers', () => {
|
||||
expect(asI32('000000000000000000000000000000000000000000000000000000000000007b').toString()).to.equal('123');
|
||||
});
|
||||
|
||||
it('correctly decodes negative numbers', () => {
|
||||
expect(asI32('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85').toString()).to.equal('-123');
|
||||
});
|
||||
});
|
||||
|
||||
describe('asU32', () => {
|
||||
it('returns a maxium U32', () => {
|
||||
expect(asU32(MAX_INT).toString(16)).to.equal(MAX_INT);
|
||||
});
|
||||
});
|
||||
});
|
||||
27
js/src/abi/util/types.js
Normal file
27
js/src/abi/util/types.js
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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 isArray (test) {
|
||||
return Object.prototype.toString.call(test) === '[object Array]';
|
||||
}
|
||||
|
||||
export function isString (test) {
|
||||
return Object.prototype.toString.call(test) === '[object String]';
|
||||
}
|
||||
|
||||
export function isInstanceOf (test, clazz) {
|
||||
return test instanceof clazz;
|
||||
}
|
||||
62
js/src/abi/util/types.spec.js
Normal file
62
js/src/abi/util/types.spec.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 { isArray, isString, isInstanceOf } from './types';
|
||||
import Token from '../token';
|
||||
|
||||
describe('abi/util/types', () => {
|
||||
describe('isArray', () => {
|
||||
it('correctly identifies empty arrays as Array', () => {
|
||||
expect(isArray([])).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly identifies non-empty arrays as Array', () => {
|
||||
expect(isArray([1, 2, 3])).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly identifies strings as non-Array', () => {
|
||||
expect(isArray('not an array')).to.be.false;
|
||||
});
|
||||
|
||||
it('correctly identifies objects as non-Array', () => {
|
||||
expect(isArray({})).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;
|
||||
});
|
||||
});
|
||||
|
||||
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 Token('int', 123), Token)).to.be.true;
|
||||
});
|
||||
|
||||
it('correctly reports false for own', () => {
|
||||
expect(isInstanceOf({ type: 'int' }, Token)).to.be.false;
|
||||
});
|
||||
});
|
||||
});
|
||||
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';
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user