Ui 2 packages (#6169)

* Ensure all internal dapps have package.json

* Update compilation rules

# Conflicts:
#	js/src/redux/providers/balancesActions.js
#	js/src/ui/Form/TypedInput/typedInput.js

* Remove SignerIcon

* Cleanup providers

* Enable request of new token from app

* Queue when no token (yet)

* Add location & token request

* Cleanup send logic

* Request token

* Request comms token

* Remove yarn.lock (not updated)

* Update version to 1.99.99 (publish prepare)

* Move jsonrpc to seperate repo

* Update jsonrpc references

* Update repo info

* Update repo info

* Additional debugging

* Update repo references

* Move ABI to js-abi repo

* Move webWorker to shared

* Fix package reference

* Worker location

* Move js-ui & js-shared components

* Update file references

* Update package repo locations

* Remove debugging info

* Cleanup debug

* Split api into own repo

* Update api local references

* Update app loading

* Update dependencies

* Allow serving of /parity-utils

* Error when EthereumProvider has not been attached

* Use inject.js

* Correct appId retrieval
This commit is contained in:
Jaco Greeff 2017-07-28 10:25:34 +02:00 committed by GitHub
parent 5830767273
commit a1b8fabd99
782 changed files with 530 additions and 64912 deletions

1
js/.gitignore vendored
View File

@ -1,3 +1,4 @@
yarn.lock
node_modules
npm-debug.log
build

14
js/package-lock.json generated
View File

@ -5,17 +5,17 @@
"requires": true,
"dependencies": {
"@parity/abi": {
"version": "file:packages/abi",
"version": "github:paritytech/js-abi#a1021ca5b7550e1e24e4767a22ef3f0d95ead19a",
"requires": {
"bignumber.js": "3.0.1",
"js-sha3": "0.5.5"
}
},
"@parity/api": {
"version": "file:packages/api",
"version": "github:paritytech/js-api#418ee49849a5361ad3c416b224fd9bb6dbabc1d1",
"requires": {
"@parity/abi": "file:packages/abi",
"@parity/jsonrpc": "file:packages/jsonrpc",
"@parity/abi": "github:paritytech/js-abi#a1021ca5b7550e1e24e4767a22ef3f0d95ead19a",
"@parity/jsonrpc": "github:paritytech/js-jsonrpc#ac545573880212a27cf4cc6bb32a37fdab6dc26d",
"bignumber.js": "3.0.1",
"blockies": "0.0.2",
"es6-error": "4.0.0",
@ -73,7 +73,7 @@
}
},
"@parity/jsonrpc": {
"version": "file:packages/jsonrpc"
"version": "github:paritytech/js-jsonrpc#ac545573880212a27cf4cc6bb32a37fdab6dc26d"
},
"@parity/ledger": {
"version": "file:packages/ledger"
@ -102,7 +102,7 @@
}
},
"@parity/shared": {
"version": "file:packages/shared",
"version": "github:paritytech/js-shared#a83c71946d30d5b3bf976860a676add29c48ee24",
"requires": {
"mobx": "2.6.4",
"mobx-react": "4.0.3",
@ -117,7 +117,7 @@
}
},
"@parity/ui": {
"version": "file:packages/ui",
"version": "github:paritytech/js-ui#164c08e1aec4a009dcd559764a746b1cc06c4b22",
"requires": {
"bignumber.js": "3.0.1",
"brace": "0.9.0",

View File

@ -160,17 +160,13 @@
"yargs": "6.6.0"
},
"dependencies": {
"@parity/abi": "file:packages/abi",
"@parity/api": "file:packages/api",
"@parity/dapp-account": "file:packages/dapp-account",
"@parity/dapp-develop": "file:packages/dapp-develop",
"@parity/api": "paritytech/js-api#418ee49849a5361ad3c416b224fd9bb6dbabc1d1",
"@parity/dapps": "file:packages/dapps",
"@parity/etherscan": "file:packages/etherscan",
"@parity/jsonrpc": "file:packages/jsonrpc",
"@parity/ledger": "file:packages/ledger",
"@parity/shapeshift": "file:packages/shapeshift",
"@parity/shared": "file:packages/shared",
"@parity/ui": "file:packages/ui",
"@parity/shared": "paritytech/js-shared#a83c71946d30d5b3bf976860a676add29c48ee24",
"@parity/ui": "paritytech/js-ui#164c08e1aec4a009dcd559764a746b1cc06c4b22",
"@parity/wordlist": "1.0.1",
"es6-error": "4.0.0",
"es6-promise": "4.0.5",

View File

@ -1,32 +0,0 @@
# ethabi-js
A very early, very POC-type port of [https://github.com/paritytech/ethabi](https://github.com/paritytech/ethabi) to JavaScript
[![Build Status](https://travis-ci.org/jacogr/ethabi-js.svg?branch=master)](https://travis-ci.org/jacogr/ethabi-js)
[![Coverage Status](https://coveralls.io/repos/github/jacogr/ethabi-js/badge.svg?branch=master)](https://coveralls.io/github/jacogr/ethabi-js?branch=master)
[![Dependency Status](https://david-dm.org/jacogr/ethabi-js.svg)](https://david-dm.org/jacogr/ethabi-js)
[![devDependency Status](https://david-dm.org/jacogr/ethabi-js/dev-status.svg)](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

View File

@ -1,20 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import Interface from './spec/interface';
export default class Abi extends Interface {
}

View File

@ -1,30 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default class BytesTaken {
constructor (bytes, newOffset) {
this._bytes = bytes;
this._newOffset = newOffset;
}
get bytes () {
return this._bytes;
}
get newOffset () {
return this._newOffset;
}
}

View File

@ -1,29 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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);
});
});
});

View File

@ -1,30 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default class DecodeResult {
constructor (token, newOffset) {
this._token = token;
this._newOffset = newOffset;
}
get token () {
return this._token;
}
get newOffset () {
return this._newOffset;
}
}

View File

@ -1,29 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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);
});
});
});

View File

@ -1,156 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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('');
let decoded;
try {
decoded = utf8.decode(str);
} catch (error) {
decoded = str;
}
return new DecodeResult(new Token(param.type, decoded), 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`);
}
}
}

View File

@ -1,319 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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 string2 = '4665726ee16e64657a0000000000000000000000000000000000000000000000';
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 tokenString2 = new Token('string', 'Fernández');
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 utf8-invalid string', () => {
expect(
Decoder.decodeParam(new ParamType('string'), [padU32(0x20), padU32(9), string2], 0).token
).to.deep.equal(tokenString2);
});
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]);
});
});
});
});

View File

@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './decoder';

View File

@ -1,75 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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, index) => Encoder.encodeToken(token, index));
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, index = 0) {
if (!isInstanceOf(token, Token)) {
throw new Error('token should be instanceof Token');
}
try {
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)));
}
} catch (e) {
throw new Error(`Cannot encode token #${index} [${token.type}: ${token.value}]. ${e.message}`);
}
throw new Error(`Invalid token type ${token.type} in encodeToken`);
}
}

View File

@ -1,291 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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)}`);
});
});
});
});

View File

@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './encoder';

View File

@ -1,142 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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`);
}
}

View File

@ -1,105 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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);
});
});
});

View File

@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './abi';

View File

@ -1,30 +0,0 @@
{
"name": "@parity/abi",
"description": "The Parity ABI library",
"version": "0.0.0",
"main": "index.js",
"author": "Parity Team <admin@parity.io>",
"maintainers": [
"Jaco Greeff",
"Nicolas Gotchac"
],
"contributors": [],
"license": "GPL-3.0",
"repository": {
"type": "git",
"url": "git+https://github.com/paritytech/parity.git"
},
"keywords": [
"Ethereum",
"ABI",
"Parity"
],
"scripts": {
},
"devDependencies": {
},
"dependencies": {
"bignumber.js": "3.0.1",
"js-sha3": "0.5.5"
}
}

View File

@ -1,36 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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);
}
}

View File

@ -1,52 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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');
});
});
});

View File

@ -1,30 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default class DecodedLog {
constructor (params, address) {
this._params = params;
this._address = address;
}
get address () {
return this._address;
}
get params () {
return this._params;
}
}

View File

@ -1,28 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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');
});
});
});

View File

@ -1,45 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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;
}
}

View File

@ -1,42 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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);
});
});
});

View File

@ -1,114 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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._inputs = EventParam.toEventParams(abi.inputs || []);
this._anonymous = !!abi.anonymous;
const { id, name, signature } = eventSignature(abi.name, this.inputParamTypes());
this._id = id;
this._name = name;
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 || idx] = topicTokens[idx];
});
dataParams.forEach((param, idx) => {
namedTokens[param.name || idx] = dataTokens[idx];
});
const inputParamTypes = this.inputParamTypes();
const decodedParams = this.inputParamNames()
.map((name, idx) => new DecodedLogParam(name, inputParamTypes[idx], namedTokens[name || idx]));
return new DecodedLog(decodedParams, address);
}
}

View File

@ -1,111 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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/);
});
});
});

View File

@ -1,41 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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));
}
}

View File

@ -1,44 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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');
});
});
});

View File

@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './event';

View File

@ -1,88 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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._constant = !!abi.constant;
this._payable = abi.payable;
this._inputs = Param.toParams(abi.inputs || []);
this._outputs = Param.toParams(abi.outputs || []);
const { id, name, signature } = methodSignature(abi.name, this.inputParamTypes());
this._id = id;
this._name = name;
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);
}
}

View File

@ -1,101 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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('returns signature correctly if name already contains it', () => {
const func = new Func({
name: 'test(bool,string)',
inputs: inputsArr,
outputs: outputsArr
});
expect(func.name).to.equal('test');
expect(func.id).to.equal('test(bool,string)');
expect(func.signature).to.equal('02356205');
});
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');
});
});
});

View File

@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './interface';

View File

@ -1,77 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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) {
return Interface.encodeTokens(paramTypes, values);
}
static 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}`);
}
});
}
}

View File

@ -1,126 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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)])
])
]);
});
});
});

View File

@ -1,42 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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) => {
if (param instanceof Param) {
return param;
}
return new Param(param.name, param.type);
});
}
}

View File

@ -1,47 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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');
});
it('converts only if needed', () => {
const _params = Param.toParams([{ name: 'foo', type: 'uint' }]);
const params = Param.toParams(_params);
expect(params.length).to.equal(1);
expect(params[0].name).to.equal('foo');
expect(params[0].kind.type).to.equal('uint');
});
});
});

View File

@ -1,80 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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}`);
}
}

View File

@ -1,228 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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');
});
});
});
});
});

View File

@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './paramType';

View File

@ -1,52 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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`);
}
}

View File

@ -1,87 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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]);
});
});
});

View File

@ -1,19 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
const TYPES = ['address', 'bytes', 'int', 'uint', 'bool', 'string', 'array', 'fixedBytes', 'fixedArray'];
export default TYPES;

View File

@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './token';

View File

@ -1,42 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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`);
}
}

View File

@ -1,75 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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');
});
});
});

View File

@ -1,66 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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());
for (let n = 0; n < 40; n++) {
const char = address[n];
const isLower = char !== char.toUpperCase();
const isUpper = char !== char.toLowerCase();
const hashval = parseInt(hash[n], 16);
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;
}

View File

@ -1,100 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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);
});
});
});

View File

@ -1,77 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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') {
const matches = input.substr(2).toLowerCase().match(/.{1,2}/g) || [];
return matches.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);
}

View File

@ -1,124 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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'));
});
});
});

View File

@ -1,49 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { keccak_256 } from 'js-sha3'; // eslint-disable-line camelcase
import { fromParamType } from '../spec/paramType/format';
export function eventSignature (eventName, params) {
const { strName, name } = parseName(eventName);
const types = (params || []).map(fromParamType).join(',');
const id = `${strName}(${types})`;
const signature = strName ? keccak_256(id) : '';
return { id, name, signature };
}
export function methodSignature (methodName, params) {
const { id, name, signature } = eventSignature(methodName, params);
return { id, name, signature: signature.substr(0, 8) };
}
function parseName (name) {
const strName = `${name || ''}`;
const idx = strName.indexOf('(');
if (idx === -1) {
return { strName, name };
}
const trimmedName = strName.slice(0, idx);
return {
strName: trimmedName,
name: trimmedName
};
}

View File

@ -1,111 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { eventSignature, methodSignature } from './signature';
describe('abi/util/signature', () => {
describe('eventSignature', () => {
it('encodes signature baz() correctly', () => {
expect(eventSignature('baz', [])).to.deep.equal({
id: 'baz()',
name: 'baz',
signature: 'a7916fac4f538170f7cd12c148552e2cba9fcd72329a2dd5b07a6fa906488ddf'
});
});
it('encodes signature baz(uint32) correctly', () => {
expect(eventSignature('baz', [{ type: 'uint', length: 32 }])).to.deep.equal({
id: 'baz(uint32)',
name: 'baz',
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)',
name: 'baz',
signature: 'cdcd77c0992ec5bbfc459984220f8c45084cc24d9b6efed1fae540db8de801d2'
});
});
it('encodes no-name signature correctly as ()', () => {
expect(eventSignature(undefined, [])).to.deep.equal({
id: '()',
name: undefined,
signature: ''
});
});
it('encodes no-params signature correctly as ()', () => {
expect(eventSignature(undefined, undefined)).to.deep.equal({
id: '()',
name: undefined,
signature: ''
});
});
});
describe('methodSignature', () => {
it('encodes signature baz() correctly', () => {
expect(methodSignature('baz', [])).to.deep.equal({
id: 'baz()',
name: 'baz',
signature: 'a7916fac'
});
});
it('encodes signature baz(uint32) correctly', () => {
expect(methodSignature('baz', [{ type: 'uint', length: 32 }])).to.deep.equal({
id: 'baz(uint32)',
name: 'baz',
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)',
name: 'baz',
signature: 'cdcd77c0'
});
});
it('encodes signature in name correctly', () => {
expect(methodSignature('baz(uint32,bool)', [{ type: 'uint', length: 32 }, { type: 'bool' }])).to.deep.equal({
id: 'baz(uint32,bool)',
name: 'baz',
signature: 'cdcd77c0'
});
});
it('encodes no-name signature correctly as ()', () => {
expect(methodSignature(undefined, [])).to.deep.equal({
id: '()',
name: undefined,
signature: ''
});
});
it('encodes no-params signature correctly as ()', () => {
expect(methodSignature(undefined, undefined)).to.deep.equal({
id: '()',
name: undefined,
signature: ''
});
});
});
});

View File

@ -1,31 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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('');
}
return data.match(/.{1,64}/g);
}

View File

@ -1,44 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { sliceData } from './slice';
describe('abi/util/slice', () => {
describe('sliceData', () => {
const slice1 = '131a3afc00d1b1e3461b955e53fc866dcf303b3eb9f4c16f89e388930f48134b';
const slice2 = '2124768576358735263578356373526387638357635873563586353756358763';
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);
});
});
});

View File

@ -1,47 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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);
}

View File

@ -1,55 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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);
});
});
});

View File

@ -1,27 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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;
}

View File

@ -1,62 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
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;
});
});
});

View File

@ -1,83 +0,0 @@
# @parity/parity.js
Parity.js is a thin, fast, Promise-based wrapper around the Ethereum APIs.
[https://github.com/paritytech/parity/tree/master/js/src/api](https://github.com/paritytech/parity/tree/master/js/src/api)
## installation
Install the package with `npm install --save @parity/parity.js`
## usage
### initialisation
```javascript
// import the actual Api class
import { Api } from '@parity/parity.js';
// do the setup
const provider = new Api.Provider.Http('http://localhost:8545');
const api = new Api(provider);
```
### making calls
perform a call
```javascript
api.eth
.coinbase()
.then((coinbase) => {
console.log(`The coinbase is ${coinbase}`);
});
```
multiple promises
```javascript
Promise
.all([
api.eth.coinbase(),
api.net.listening()
])
.then(([coinbase, listening]) => {
// do stuff here
});
```
chaining promises
```javascript
api.eth
.newFilter({...})
.then((filterId) => api.eth.getFilterChanges(filterId))
.then((changes) => {
console.log(changes);
});
```
### contracts
attach contract
```javascript
const abi = [{ name: 'callMe', inputs: [{ type: 'bool', ...}, { type: 'string', ...}]}, ...abi...];
const address = '0x123456...9abc';
const contract = new api.newContract(abi, address);
```
find & call a function
```javascript
contract.instance
.callMe
.call({ gas: 21000 }, [true, 'someString']) // or estimateGas or postTransaction
.then((result) => {
console.log(`the result was ${result}`);
});
```
## apis
APIs implement the calls as exposed in the [Ethcore JSON Ethereum RPC](https://github.com/paritytech/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.

View File

@ -1,206 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import EventEmitter from 'eventemitter3';
import Contract from './contract';
import { PromiseProvider, Http as HttpProvider, PostMessage as PostMessageProvider, Ws as WsProvider, WsSecure as WsSecureProvider } from './provider';
import { Http as HttpTransport, Ws as WsTransport, WsSecure as WsSecureTransport } from './transport';
import { Db, Eth, Parity, Net, Personal, Shell, Shh, Signer, Trace, Web3 } from './rpc';
import Subscriptions from './subscriptions';
import Pubsub from './pubsub';
import util from './util';
import { isFunction } from './util/types';
import LocalAccountsMiddleware from '~/packages/api/local';
export default class Api extends EventEmitter {
constructor (provider, allowSubscriptions = true) {
super();
if (!provider || !isFunction(provider.send)) {
console.log(provider);
console.warn(new Error('deprecated: Api needs provider with send() function, old-style Transport found instead'));
}
// does use new provider interface (not promiseProvider)
if (provider && isFunction(provider.subscribe)) {
this._pubsub = new Pubsub(provider);
}
this._provider = new PromiseProvider(provider);
this._db = new Db(this._provider);
this._eth = new Eth(this._provider);
this._net = new Net(this._provider);
this._parity = new Parity(this._provider);
this._personal = new Personal(this._provider);
this._shell = new Shell(this._provider);
this._shh = new Shh(this._provider);
this._signer = new Signer(this._provider);
this._trace = new Trace(this._provider);
this._web3 = new Web3(this._provider);
if (allowSubscriptions) {
this._subscriptions = new Subscriptions(this);
}
// Doing a request here in test env would cause an error
if (LocalAccountsMiddleware && process.env.NODE_ENV !== 'test') {
const middleware = this.parity
.nodeKind()
.then((nodeKind) => {
if (nodeKind.availability === 'public') {
return LocalAccountsMiddleware;
}
return null;
})
.catch(() => null);
provider.addMiddleware(middleware);
}
}
get isConnected () {
const isConnected = this.provider.isConnected;
return isConnected || typeof isConnected === 'undefined';
}
get pubsub () {
if (!this._pubsub) {
throw Error('Pubsub is only available with a subscribing-supported transport injected!');
}
return this._pubsub;
}
get db () {
return this._db;
}
get eth () {
return this._eth;
}
get parity () {
return this._parity;
}
get net () {
return this._net;
}
get personal () {
return this._personal;
}
get provider () {
return this._provider.provider;
}
get shell () {
return this._shell;
}
get shh () {
return this._shh;
}
get signer () {
return this._signer;
}
get trace () {
return this._trace;
}
get transport () {
return this.provider;
}
get web3 () {
return this._web3;
}
get util () {
return util;
}
newContract (abi, address) {
return new Contract(this, abi).at(address);
}
subscribe (subscriptionName, callback) {
if (!this._subscriptions) {
return Promise.resolve(1);
}
return this._subscriptions.subscribe(subscriptionName, callback);
}
unsubscribe (subscriptionId) {
if (!this._subscriptions) {
return Promise.resolve(true);
}
return this._subscriptions.unsubscribe(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) => {
// Don't print if the request is rejected: that's ok
if (error.type !== 'REQUEST_REJECTED') {
console.error('pollMethod', error);
}
reject(error);
});
};
timeout();
});
}
static util = util
static Provider = {
Http: HttpProvider,
PostMessage: PostMessageProvider,
Ws: WsProvider,
WsSecure: WsSecureProvider
}
// NOTE: kept for backwards compatibility
static Transport = {
Http: HttpTransport,
Ws: WsTransport,
WsSecure: WsSecureTransport
}
}

View File

@ -1,47 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import ethereumRpc from '@parity/jsonrpc';
import { TEST_HTTP_URL, endpointTest } from '../../test/mockRpc';
import util from './util';
import Api from './api';
describe('api/Api', () => {
describe('interface', () => {
const api = new Api(new Api.Provider.Http(TEST_HTTP_URL, -1));
const ignored = [
'eth_subscribe', 'eth_unsubscribe',
'parity_subscribe', 'parity_unsubscribe',
'signer_subscribePending', 'signer_unsubscribePending'
];
Object.keys(ethereumRpc).sort().forEach((endpoint) => {
describe(endpoint, () => {
Object.keys(ethereumRpc[endpoint]).sort()
.filter(method => ignored.indexOf(method) !== -1)
.forEach((method) => {
endpointTest(api, endpoint, method);
});
});
});
});
it('exposes util as static property', () => {
expect(Api.util).to.equal(util);
});
});

View File

@ -1,561 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import Abi from '@parity/abi';
let nextSubscriptionId = 0;
export default class Contract {
constructor (api, abi) {
if (!api) {
throw new Error('API instance needs to be provided to Contract');
}
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._instance[evt.signature] = evt;
});
this._functions.forEach((fn) => {
this._instance[fn.name] = fn;
this._instance[fn.signature] = fn;
});
this._subscribedToPendings = false;
this._pendingsSubscriptionId = null;
this._subscribedToBlock = false;
this._blockSubscriptionId = null;
if (api && api.patch && api.patch.contract) {
api.patch.contract(this);
}
}
get address () {
return this._address;
}
get constructors () {
return this._constructors;
}
get events () {
return this._events;
}
get functions () {
return this._functions;
}
get receipt () {
return this._receipt;
}
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;
}
deployEstimateGas (options, values) {
const _options = this._encodeOptions(this.constructors[0], options, values);
return this._api.eth
.estimateGas(_options)
.then((gasEst) => {
return [gasEst, gasEst.mul(1.2)];
});
}
deploy (options, values, statecb = () => {}, skipGasEstimate = false) {
let gasEstPromise;
if (skipGasEstimate) {
gasEstPromise = Promise.resolve(null);
} else {
statecb(null, { state: 'estimateGas' });
gasEstPromise = this.deployEstimateGas(options, values)
.then(([gasEst, gas]) => gas);
}
return gasEstPromise
.then((_gas) => {
if (_gas) {
options.gas = _gas.toFixed(0);
}
const gas = _gas || options.gas;
statecb(null, { state: 'postTransaction', gas });
const encodedOptions = this._encodeOptions(this.constructors[0], options, values);
return this._api.parity
.postTransaction(encodedOptions)
.then((requestId) => {
statecb(null, { state: 'checkRequest', requestId });
return this._pollCheckRequest(requestId);
})
.then((txhash) => {
statecb(null, { 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)}`);
}
statecb(null, { state: 'hasReceipt', receipt });
this._receipt = receipt;
this._address = receipt.contractAddress;
return this._address;
})
.then((address) => {
statecb(null, { state: 'getCode' });
return this._api.eth.getCode(this._address);
})
.then((code) => {
if (code === '0x') {
throw new Error('Contract not deployed, getCode returned 0x');
}
statecb(null, { 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) {
console.warn(`Unable to find event matching signature ${signature}`);
return null;
}
try {
const decoded = event.decodeLog(log.topics, log.data);
log.params = {};
log.event = event.name;
decoded.params.forEach((param, index) => {
const { type, value } = param.token;
const key = param.name || index;
log.params[key] = { type, value };
});
return log;
} catch (error) {
console.warn('Error decoding log', log);
console.warn(error);
return null;
}
})
.filter((log) => log);
}
parseTransactionEvents (receipt) {
receipt.logs = this.parseEventLogs(receipt.logs);
return receipt;
}
_pollCheckRequest = (requestId) => {
return this._api.pollMethod('parity_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;
});
}
getCallData = (func, options, values) => {
let data = options.data;
const tokens = func ? Abi.encodeTokens(func.inputParamTypes(), values) : null;
const call = tokens ? func.encodeCall(tokens) : null;
if (data && data.substr(0, 2) === '0x') {
data = data.substr(2);
}
return `0x${data || ''}${call || ''}`;
}
_encodeOptions (func, options, values) {
const data = this.getCallData(func, options, values);
return {
...options,
data
};
}
_addOptionsTo (options = {}) {
return {
to: this._address,
...options
};
}
_bindFunction = (func) => {
func.contract = this;
func.call = (_options = {}, values = []) => {
const rawTokens = !!_options.rawTokens;
const options = {
..._options
};
delete options.rawTokens;
let callParams;
try {
callParams = this._encodeOptions(func, this._addOptionsTo(options), values);
} catch (error) {
return Promise.reject(error);
}
return this._api.eth
.call(callParams)
.then((encoded) => func.decodeOutput(encoded))
.then((tokens) => {
if (rawTokens) {
return tokens;
}
return tokens.map((token) => token.value);
})
.then((returns) => returns.length === 1 ? returns[0] : returns)
.catch((error) => {
console.warn(`${func.name}.call`, values, error);
throw error;
});
};
if (!func.constant) {
func.postTransaction = (options, values = []) => {
let _options;
try {
_options = this._encodeOptions(func, this._addOptionsTo(options), values);
} catch (error) {
return Promise.reject(error);
}
return this._api.parity
.postTransaction(_options)
.catch((error) => {
console.warn(`${func.name}.postTransaction`, values, error);
throw error;
});
};
func.estimateGas = (options, values = []) => {
const _options = this._encodeOptions(func, this._addOptionsTo(options), values);
return this._api.eth
.estimateGas(_options)
.catch((error) => {
console.warn(`${func.name}.estimateGas`, values, error);
throw error;
});
};
}
return func;
}
_bindEvent = (event) => {
event.subscribe = (options = {}, callback, autoRemove) => {
return this._subscribe(event, options, callback, autoRemove);
};
event.unsubscribe = (subscriptionId) => {
return this.unsubscribe(subscriptionId);
};
event.getAllLogs = (options = {}) => {
return this.getAllLogs(event);
};
return event;
}
getAllLogs (event, _options) {
// Options as first parameter
if (!_options && event && event.topics) {
return this.getAllLogs(null, event);
}
const options = this._getFilterOptions(event, _options);
options.fromBlock = 0;
options.toBlock = 'latest';
return this._api.eth
.getLogs(options)
.then((logs) => this.parseEventLogs(logs));
}
_findEvent (eventName = null) {
const event = eventName
? this._events.find((evt) => evt.name === eventName)
: null;
if (eventName && !event) {
const events = this._events.map((evt) => evt.name).join(', ');
throw new Error(`${eventName} is not a valid eventName, subscribe using one of ${events} (or null to include all)`);
}
return event;
}
_getFilterOptions (event = null, _options = {}) {
const optionTopics = _options.topics || [];
const signature = event && event.signature || null;
// If event provided, remove the potential event signature
// as the first element of the topics
const topics = signature
? [ signature ].concat(optionTopics.filter((t, idx) => idx > 0 || t !== signature))
: optionTopics;
const options = Object.assign({}, _options, {
address: this._address,
topics
});
return options;
}
_createEthFilter (event = null, _options) {
const options = this._getFilterOptions(event, _options);
return this._api.eth.newFilter(options);
}
subscribe (eventName = null, options = {}, callback, autoRemove) {
try {
const event = this._findEvent(eventName);
return this._subscribe(event, options, callback, autoRemove);
} catch (e) {
return Promise.reject(e);
}
}
_sendData (subscriptionId, error, logs) {
const { autoRemove, callback } = this._subscriptions[subscriptionId];
let result = true;
try {
result = callback(error, logs);
} catch (error) {
console.warn('_sendData', subscriptionId, error);
}
if (autoRemove && result && typeof result === 'boolean') {
this.unsubscribe(subscriptionId);
}
}
_subscribe (event = null, _options, callback, autoRemove = false) {
const subscriptionId = nextSubscriptionId++;
const { skipInitFetch } = _options;
delete _options['skipInitFetch'];
return this
._createEthFilter(event, _options)
.then((filterId) => {
this._subscriptions[subscriptionId] = {
options: _options,
autoRemove,
callback,
filterId,
id: subscriptionId
};
if (skipInitFetch) {
this._subscribeToChanges();
return subscriptionId;
}
return this._api.eth
.getFilterLogs(filterId)
.then((logs) => {
this._sendData(subscriptionId, null, this.parseEventLogs(logs));
this._subscribeToChanges();
return subscriptionId;
});
})
.catch((error) => {
console.warn('subscribe', event, _options, error);
throw error;
});
}
unsubscribe (subscriptionId) {
return this._api.eth
.uninstallFilter(this._subscriptions[subscriptionId].filterId)
.catch((error) => {
console.error('unsubscribe', error);
})
.then(() => {
delete this._subscriptions[subscriptionId];
this._unsubscribeFromChanges();
});
}
_subscribeToChanges = () => {
const subscriptions = Object.values(this._subscriptions);
const pendingSubscriptions = subscriptions
.filter((s) => s.options.toBlock && s.options.toBlock === 'pending');
const otherSubscriptions = subscriptions
.filter((s) => !(s.options.toBlock && s.options.toBlock === 'pending'));
if (pendingSubscriptions.length > 0 && !this._subscribedToPendings) {
this._subscribedToPendings = true;
this._subscribeToPendings();
}
if (otherSubscriptions.length > 0 && !this._subscribedToBlock) {
this._subscribedToBlock = true;
this._subscribeToBlock();
}
}
_unsubscribeFromChanges = () => {
const subscriptions = Object.values(this._subscriptions);
const pendingSubscriptions = subscriptions
.filter((s) => s.options.toBlock && s.options.toBlock === 'pending');
const otherSubscriptions = subscriptions
.filter((s) => !(s.options.toBlock && s.options.toBlock === 'pending'));
if (pendingSubscriptions.length === 0 && this._subscribedToPendings) {
this._subscribedToPendings = false;
clearTimeout(this._pendingsSubscriptionId);
}
if (otherSubscriptions.length === 0 && this._subscribedToBlock) {
this._subscribedToBlock = false;
this._api.unsubscribe(this._blockSubscriptionId);
}
}
_subscribeToBlock = () => {
this._api
.subscribe('eth_blockNumber', (error) => {
if (error) {
console.error('::_subscribeToBlock', error, error && error.stack);
}
const subscriptions = Object.values(this._subscriptions)
.filter((s) => !(s.options.toBlock && s.options.toBlock === 'pending'));
this._sendSubscriptionChanges(subscriptions);
})
.then((blockSubId) => {
this._blockSubscriptionId = blockSubId;
})
.catch((e) => {
console.error('::_subscribeToBlock', e, e && e.stack);
});
}
_subscribeToPendings = () => {
const subscriptions = Object.values(this._subscriptions)
.filter((s) => s.options.toBlock && s.options.toBlock === 'pending');
const timeout = () => setTimeout(() => this._subscribeToPendings(), 1000);
this._sendSubscriptionChanges(subscriptions)
.then(() => {
this._pendingsSubscriptionId = timeout();
});
}
_sendSubscriptionChanges = (subscriptions) => {
return Promise
.all(
subscriptions.map((subscription) => {
return this._api.eth.getFilterChanges(subscription.filterId);
})
)
.then((logsArray) => {
logsArray.forEach((logs, index) => {
if (!logs || !logs.length) {
return;
}
try {
this._sendData(subscriptions[index].id, null, this.parseEventLogs(logs));
} catch (error) {
console.error('_sendSubscriptionChanges', error);
}
});
})
.catch((error) => {
console.error('_sendSubscriptionChanges', error);
});
}
}

View File

@ -1,598 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import BigNumber from 'bignumber.js';
import sinon from 'sinon';
import Abi from '@parity/abi';
import { TEST_HTTP_URL, mockHttp } from '../../../test/mockRpc';
import { sha3 } from '../util/sha3';
import Api from '../api';
import Contract from './contract';
import { isInstanceOf, isFunction } from '../util/types';
const provider = new Api.Provider.Http(TEST_HTTP_URL, -1);
const eth = new Api(provider);
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',
inputs: [{ name: 'boolin', type: 'bool' }, { name: 'stringin', type: 'string' }]
},
{ type: 'event', name: 'baz' },
{ type: 'event', name: 'foo' }
];
const ABI_NO_PARAMS = [
{
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 CALLDATA = `
0000000000000000000000000000000000000000000000000000000000000001
0000000000000000000000000000000000000000000000000000000000000040
0000000000000000000000000000000000000000000000000000000000000006
6a61636f67720000000000000000000000000000000000000000000000000000
`.replace(/\s/g, '');
const SIGNATURE = '02356205';
const ENCODED = `0x${SIGNATURE}${CALLDATA}`;
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',
/^(?:0x)(.+)$/.exec(sha3('Drained(uint256)'))[1],
'balanceOf',
/^(?:0x)(.+)$/.exec(sha3('balanceOf(address)'))[1].substr(0, 8),
'address'
]);
expect(contract.address).to.equal('6789');
});
});
describe('parseTransactionEvents', () => {
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: { type: 'uint', value: new BigNumber('1457965151') },
message: { type: 'string', value: 'post(message)' },
messageId: { type: 'uint', value: new BigNumber('281474976731085') },
parentId: { type: 'uint', value: new BigNumber(0) },
postId: { type: 'uint', value: new BigNumber('281474976731104') },
sender: { type: 'address', value: '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 without parameters', () => {
const contract = new Contract(eth, ABI_NO_PARAMS);
const CODE = '0x123';
const ADDRESS = '0xD337e80eEdBdf86eDBba021797d7e4e00Bb78351';
const RECEIPT_DONE = { contractAddress: ADDRESS.toLowerCase(), gasUsed: 50, blockNumber: 2500 };
let scope;
describe('success', () => {
before(() => {
scope = mockHttp([
{ method: 'eth_estimateGas', reply: { result: 1000 } },
{ method: 'parity_postTransaction', reply: { result: '0x678' } },
{ method: 'parity_checkRequest', reply: { result: '0x890' } },
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_DONE } },
{ method: 'eth_getCode', reply: { result: CODE } }
]);
return contract.deploy({ data: CODE }, []);
});
it('passes the options through to postTransaction (incl. gas calculation)', () => {
expect(scope.body.parity_postTransaction.params[0].data).to.equal(CODE);
});
});
});
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: 'parity_postTransaction', reply: { result: '0x678' } },
{ method: 'parity_checkRequest', reply: { result: null } },
{ method: 'parity_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' }, VALUES);
});
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.parity_postTransaction.params).to.deep.equal([
{ data: `0x123${CALLDATA}`, 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: 'parity_postTransaction', reply: { result: '0x678' } },
{ method: 'parity_checkRequest', reply: { result: '0x789' } },
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_EXCP } }
]);
return contract
.deploy({ data: '0x123' }, VALUES)
.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: 'parity_postTransaction', reply: { result: '0x678' } },
{ method: 'parity_checkRequest', reply: { result: '0x789' } },
{ method: 'eth_getTransactionReceipt', reply: { result: RECEIPT_DONE } },
{ method: 'eth_getCode', reply: { result: '0x' } }
]);
return contract
.deploy({ data: '0x123' }, VALUES)
.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: 'parity_postTransaction', reply: { result: ['hashId'] } }]);
});
it('encodes options and mades an parity_postTransaction call', () => {
return func
.postTransaction({ someExtras: 'foo' }, VALUES)
.then(() => {
expect(scope.isDone()).to.be.true;
expect(scope.body.parity_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: { type: 'uint', value: new BigNumber(1457965151) },
message: { type: 'string', value: 'post(message)' },
messageId: { type: 'uint', value: new BigNumber(281474976731085) },
parentId: { type: 'uint', value: new BigNumber(0) },
postId: { type: 'uint', value: new BigNumber(281474976731104) },
sender: { type: 'address', value: '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_getFilterChanges', 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', { toBlock: 'pending' }, cbb);
});
it('sets the subscriptionId returned', () => {
return contract
.subscribe('Message', { toBlock: 'pending' }, cbe)
.then((subscriptionId) => {
expect(subscriptionId).to.equal(1);
});
});
it('creates a new filter and retrieves the logs on it', () => {
return contract
.subscribe('Message', { toBlock: 'pending' }, cbe)
.then((subscriptionId) => {
expect(scope.isDone()).to.be.true;
});
});
it('returns the logs to the callback', () => {
return contract
.subscribe('Message', { toBlock: 'pending' }, cbe)
.then((subscriptionId) => {
expect(cbe).to.have.been.calledWith(null, parsed);
});
});
});
});
});

View File

@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './contract';

View File

@ -1,242 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import BigNumber from 'bignumber.js';
import { isArray, isHex, isInstanceOf, isString } from '../util/types';
import { padLeft, toHex } from '../util/format';
export function inAddress (address) {
// TODO: address validation if we have upper-lower addresses
return inHex(address);
}
export function inAddresses (addresses) {
return (addresses || []).map(inAddress);
}
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 inHash (hash) {
return inHex(hash);
}
export function inTopics (_topics) {
let topics = (_topics || [])
.filter((topic) => topic === null || topic)
.map((topic) => {
if (topic === null) {
return null;
}
if (Array.isArray(topic)) {
return inTopics(topic);
}
return padLeft(topic, 32);
});
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) {
return toHex(str);
}
export function inNumber10 (number) {
if (isInstanceOf(number, BigNumber)) {
return number.toNumber();
}
return (new BigNumber(number || 0)).toNumber();
}
export function inNumber16 (number) {
const bn = isInstanceOf(number, BigNumber)
? number
: (new BigNumber(number || 0));
if (!bn.isInteger()) {
throw new Error(`[format/input::inNumber16] the given number is not an integer: ${bn.toFormat()}`);
}
return inHex(bn.toString(16));
}
export function inOptionsCondition (condition) {
if (condition) {
if (condition.block) {
condition.block = condition.block ? inNumber10(condition.block) : null;
} else if (condition.time) {
condition.time = inNumber10(Math.floor(condition.time.getTime() / 1000));
}
}
return condition;
}
export function inOptions (_options = {}) {
const options = { ..._options };
Object.keys(options).forEach((key) => {
switch (key) {
case 'to':
// Don't encode the `to` option if it's empty
// (eg. contract deployments)
if (options[key]) {
options.to = inAddress(options[key]);
}
break;
case 'from':
options[key] = inAddress(options[key]);
break;
case 'condition':
options[key] = inOptionsCondition(options[key]);
break;
case 'gas':
case 'gasPrice':
options[key] = inNumber16((new BigNumber(options[key])).round());
break;
case 'value':
case 'nonce':
options[key] = inNumber16(options[key]);
break;
case 'data':
options[key] = inData(options[key]);
break;
}
});
return options;
}
export function inTraceFilter (filterObject) {
if (filterObject) {
Object.keys(filterObject).forEach((key) => {
switch (key) {
case 'fromAddress':
case 'toAddress':
filterObject[key] = [].concat(filterObject[key])
.map(address => inAddress(address));
break;
case 'toBlock':
case 'fromBlock':
filterObject[key] = inBlockNumber(filterObject[key]);
break;
}
});
}
return filterObject;
}
export function inTraceType (whatTrace) {
if (isString(whatTrace)) {
return [whatTrace];
}
return whatTrace;
}
function inDeriveType (derive) {
return derive && derive.type === 'hard' ? 'hard' : 'soft';
}
export function inDeriveHash (derive) {
const hash = derive && derive.hash ? derive.hash : derive;
const type = inDeriveType(derive);
return {
hash: inHex(hash),
type
};
}
export function inDeriveIndex (derive) {
if (!derive) {
return [];
}
if (!isArray(derive)) {
derive = [derive];
}
return derive.map(item => {
const index = inNumber10(item && item.index ? item.index : item);
return {
index,
type: inDeriveType(item)
};
});
}

View File

@ -1,341 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import BigNumber from 'bignumber.js';
import {
inAddress, inBlockNumber, inData, inFilter, inHex,
inNumber10, inNumber16, inOptions, inTraceType,
inDeriveHash, inDeriveIndex
} 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);
});
});
it('does not encode an empty `to` value', () => {
const options = { to: '' };
const formatted = inOptions(options);
expect(formatted.to).to.equal('');
});
['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('passes condition as null when specified as such', () => {
expect(inOptions({ condition: null })).to.deep.equal({ condition: null });
});
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'
});
});
});
describe('inTraceType', () => {
it('returns array of types as is', () => {
const types = ['vmTrace', 'trace', 'stateDiff'];
expect(inTraceType(types)).to.deep.equal(types);
});
it('formats single string type into array', () => {
const type = 'vmTrace';
expect(inTraceType(type)).to.deep.equal([type]);
});
});
describe('inDeriveHash', () => {
it('returns derive hash', () => {
expect(inDeriveHash(1)).to.deep.equal({
hash: '0x1',
type: 'soft'
});
expect(inDeriveHash(null)).to.deep.equal({
hash: '0x',
type: 'soft'
});
expect(inDeriveHash({
hash: 5
})).to.deep.equal({
hash: '0x5',
type: 'soft'
});
expect(inDeriveHash({
hash: 5,
type: 'hard'
})).to.deep.equal({
hash: '0x5',
type: 'hard'
});
});
});
describe('inDeriveIndex', () => {
it('returns derive hash', () => {
expect(inDeriveIndex(null)).to.deep.equal([]);
expect(inDeriveIndex([])).to.deep.equal([]);
expect(inDeriveIndex([1])).to.deep.equal([{
index: 1,
type: 'soft'
}]);
expect(inDeriveIndex({
index: 1
})).to.deep.equal([{
index: 1,
type: 'soft'
}]);
expect(inDeriveIndex([{
index: 1,
type: 'hard'
}, 5])).to.deep.equal([
{
index: 1,
type: 'hard'
},
{
index: 5,
type: 'soft'
}
]);
});
});
});

View File

@ -1,415 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import BigNumber from 'bignumber.js';
import { toChecksumAddress } from '@parity/abi/util/address';
import { isString } from '../util/types';
export function outAccountInfo (infos) {
return Object
.keys(infos)
.reduce((ret, _address) => {
const info = infos[_address];
const address = outAddress(_address);
ret[address] = {
name: info.name
};
if (info.meta) {
ret[address].uuid = info.uuid;
ret[address].meta = JSON.parse(info.meta);
}
return ret;
}, {});
}
export function outAddress (address) {
return toChecksumAddress(address);
}
export function outAddresses (addresses) {
return (addresses || []).map(outAddress);
}
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 outChainStatus (status) {
if (status) {
Object.keys(status).forEach((key) => {
switch (key) {
case 'blockGap':
status[key] = status[key]
? status[key].map(outNumber)
: status[key];
break;
}
});
}
return status;
}
export function outDate (date) {
if (typeof date.toISOString === 'function') {
return date;
}
try {
if (typeof date === 'string' && (new Date(date)).toISOString() === date) {
return new Date(date);
}
} catch (error) {}
return new Date(outNumber(date).toNumber() * 1000);
}
export function outHistogram (histogram) {
if (histogram) {
Object.keys(histogram).forEach((key) => {
switch (key) {
case 'bucketBounds':
case 'counts':
histogram[key] = histogram[key].map(outNumber);
break;
}
});
}
return histogram;
}
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 outHwAccountInfo (infos) {
return Object
.keys(infos)
.reduce((ret, _address) => {
const address = outAddress(_address);
ret[address] = infos[_address];
return ret;
}, {});
}
export function outNodeKind (info) {
return info;
}
export function outNumber (number) {
return new BigNumber(number || 0);
}
export function outPeer (peer) {
const protocols = Object.keys(peer.protocols)
.reduce((obj, key) => {
if (peer.protocols[key]) {
obj[key] = {
...peer.protocols[key],
difficulty: outNumber(peer.protocols[key].difficulty)
};
}
return obj;
}, {});
return {
...peer,
protocols
};
}
export function outPeers (peers) {
return {
active: outNumber(peers.active),
connected: outNumber(peers.connected),
max: outNumber(peers.max),
peers: peers.peers.map((peer) => outPeer(peer))
};
}
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 outRecentDapps (recentDapps) {
if (recentDapps) {
Object.keys(recentDapps).forEach((url) => {
recentDapps[url] = outDate(recentDapps[url]);
});
}
return recentDapps;
}
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].decrypt = outSigningPayload(request[key].decrypt);
request[key].sign = outSigningPayload(request[key].sign);
request[key].signTransaction = outTransaction(request[key].signTransaction);
request[key].sendTransaction = outTransaction(request[key].sendTransaction);
break;
case 'origin':
const type = Object.keys(request[key])[0];
const details = request[key][type];
request[key] = { type, details };
break;
}
});
}
return request;
}
export function outSyncing (syncing) {
if (syncing && syncing !== 'false') {
Object.keys(syncing).forEach((key) => {
switch (key) {
case 'currentBlock':
case 'highestBlock':
case 'startingBlock':
case 'warpChunksAmount':
case 'warpChunksProcessed':
syncing[key] = outNumber(syncing[key]);
break;
case 'blockGap':
syncing[key] = syncing[key] ? syncing[key].map(outNumber) : syncing[key];
break;
}
});
}
return syncing;
}
export function outTransactionCondition (condition) {
if (condition) {
if (condition.block) {
condition.block = outNumber(condition.block);
} else if (condition.time) {
condition.time = outDate(condition.time);
}
}
return condition;
}
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 'condition':
tx[key] = outTransactionCondition(tx[key]);
break;
case 'creates':
case 'from':
case 'to':
tx[key] = outAddress(tx[key]);
break;
}
});
}
return tx;
}
export function outSigningPayload (payload) {
if (payload) {
Object.keys(payload).forEach((key) => {
switch (key) {
case 'address':
payload[key] = outAddress(payload[key]);
break;
}
});
}
return payload;
}
export function outTrace (trace) {
if (trace) {
if (trace.action) {
Object.keys(trace.action).forEach(key => {
switch (key) {
case 'gas':
case 'value':
case 'balance':
trace.action[key] = outNumber(trace.action[key]);
break;
case 'from':
case 'to':
case 'address':
case 'refundAddress':
trace.action[key] = outAddress(trace.action[key]);
break;
}
});
}
if (trace.result) {
Object.keys(trace.result).forEach(key => {
switch (key) {
case 'gasUsed':
trace.result[key] = outNumber(trace.result[key]);
break;
case 'address':
trace.action[key] = outAddress(trace.action[key]);
break;
}
});
}
if (trace.traceAddress) {
trace.traceAddress.forEach((address, index) => {
trace.traceAddress[index] = outNumber(address);
});
}
Object.keys(trace).forEach((key) => {
switch (key) {
case 'subtraces':
case 'transactionPosition':
case 'blockNumber':
trace[key] = outNumber(trace[key]);
break;
}
});
}
return trace;
}
export function outTraces (traces) {
if (traces) {
return traces.map(outTrace);
}
return traces;
}
export function outTraceReplay (trace) {
if (trace) {
Object.keys(trace).forEach((key) => {
switch (key) {
case 'trace':
trace[key] = outTraces(trace[key]);
break;
}
});
}
return trace;
}
export function outVaultMeta (meta) {
if (isString(meta)) {
try {
const obj = JSON.parse(meta);
return obj;
} catch (error) {
return {};
}
}
return meta || {};
}

View File

@ -1,502 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import BigNumber from 'bignumber.js';
import { outBlock, outAccountInfo, outAddress, outChainStatus, outDate, outHistogram, outHwAccountInfo, outNodeKind, outNumber, outPeer, outPeers, outReceipt, outRecentDapps, outSyncing, outTransaction, outTrace, outVaultMeta } 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' }
}
});
});
it('returns objects without meta & uuid as required', () => {
expect(outAccountInfo(
{ '0x63cf90d3f0410092fc0fca41846f596223979195': { name: 'name' } }
)).to.deep.equal({
'0x63Cf90D3f0410092FC0fca41846f596223979195': { name: 'name' }
});
});
});
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('outChainStatus', () => {
it('formats blockGap values', () => {
const status = {
blockGap: [0x1234, '0x5678']
};
expect(outChainStatus(status)).to.deep.equal({
blockGap: [new BigNumber(0x1234), new BigNumber(0x5678)]
});
});
it('handles null blockGap values', () => {
const status = {
blockGap: null
};
expect(outChainStatus(status)).to.deep.equal(status);
});
});
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('outHistogram', () => {
['bucketBounds', 'counts'].forEach((type) => {
it(`formats ${type} as number arrays`, () => {
expect(
outHistogram({ [type]: [0x123, 0x456, 0x789] })
).to.deep.equal({
[type]: [new BigNumber(0x123), new BigNumber(0x456), new BigNumber(0x789)]
});
});
});
});
describe('outHwAccountInfo', () => {
it('returns objects with formatted addresses', () => {
expect(outHwAccountInfo(
{ '0x63cf90d3f0410092fc0fca41846f596223979195': { manufacturer: 'mfg', name: 'type' } }
)).to.deep.equal({
'0x63Cf90D3f0410092FC0fca41846f596223979195': { manufacturer: 'mfg', name: 'type' }
});
});
});
describe('outNodeKind', () => {
it('formats the input as received', () => {
const kind = { availability: 'personal', capability: 'full' };
expect(outNodeKind(kind)).to.deep.equal(kind);
});
});
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('outPeer', () => {
it('converts all internal numbers to BigNumbers', () => {
expect(outPeer({
caps: ['par/1'],
id: '0x01',
name: 'Parity',
network: {
localAddress: '10.0.0.1',
remoteAddress: '10.0.0.1'
},
protocols: {
par: {
difficulty: '0x0f',
head: '0x02',
version: 63
}
}
})).to.deep.equal({
caps: ['par/1'],
id: '0x01',
name: 'Parity',
network: {
localAddress: '10.0.0.1',
remoteAddress: '10.0.0.1'
},
protocols: {
par: {
difficulty: new BigNumber(15),
head: '0x02',
version: 63
}
}
});
});
it('does not output null protocols', () => {
expect(outPeer({
caps: ['par/1'],
id: '0x01',
name: 'Parity',
network: {
localAddress: '10.0.0.1',
remoteAddress: '10.0.0.1'
},
protocols: {
les: null
}
})).to.deep.equal({
caps: ['par/1'],
id: '0x01',
name: 'Parity',
network: {
localAddress: '10.0.0.1',
remoteAddress: '10.0.0.1'
},
protocols: {}
});
});
});
describe('outPeers', () => {
it('converts all internal numbers to BigNumbers', () => {
expect(outPeers({
active: 789,
connected: '456',
max: 0x7b,
peers: [
{
caps: ['par/1'],
id: '0x01',
name: 'Parity',
network: {
localAddress: '10.0.0.1',
remoteAddress: '10.0.0.1'
},
protocols: {
par: {
difficulty: '0x0f',
head: '0x02',
version: 63
},
les: null
}
}
]
})).to.deep.equal({
active: new BigNumber(789),
connected: new BigNumber(456),
max: new BigNumber(123),
peers: [
{
caps: ['par/1'],
id: '0x01',
name: 'Parity',
network: {
localAddress: '10.0.0.1',
remoteAddress: '10.0.0.1'
},
protocols: {
par: {
difficulty: new BigNumber(15),
head: '0x02',
version: 63
}
}
}
]
});
});
});
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('outRecentDapps', () => {
it('formats the URLs with timestamps', () => {
expect(outRecentDapps({ testing: 0x57513668 })).to.deep.equal({
testing: new Date('2016-06-03T07:48:56.000Z')
});
});
});
describe('outSyncing', () => {
['currentBlock', 'highestBlock', 'startingBlock', 'warpChunksAmount', 'warpChunksProcessed'].forEach((input) => {
it(`formats ${input} numbers as a number`, () => {
expect(outSyncing({ [input]: '0x123' })).to.deep.equal({
[input]: new BigNumber('0x123')
});
});
});
it('formats blockGap properly', () => {
expect(outSyncing({ blockGap: [0x123, 0x456] })).to.deep.equal({
blockGap: [new BigNumber(0x123), new BigNumber(0x456)]
});
});
});
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('passes condition as null when null', () => {
expect(outTransaction({ condition: null })).to.deep.equal({ condition: null });
});
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'
});
});
});
describe('outTrace', () => {
it('ignores and passes through unknown keys', () => {
expect(outTrace({ someRandom: 'someRandom' })).to.deep.equal({ someRandom: 'someRandom' });
});
it('formats a trace with all the info converted', () => {
const formatted = outTrace({
type: 'call',
action: {
from: address,
to: address,
value: '0x06',
gas: '0x07',
input: '0x1234',
callType: 'call'
},
result: {
gasUsed: '0x08',
output: '0x5678'
},
traceAddress: [ '0x2' ],
subtraces: 3,
transactionPosition: '0xb',
transactionHash: '0x000000000000000000000000000000000000000000000000000000000000000c',
blockNumber: '0x0d',
blockHash: '0x000000000000000000000000000000000000000000000000000000000000000e'
});
expect(isBigNumber(formatted.action.gas)).to.be.true;
expect(formatted.action.gas.toNumber()).to.equal(7);
expect(isBigNumber(formatted.action.value)).to.be.true;
expect(formatted.action.value.toNumber()).to.equal(6);
expect(formatted.action.from).to.equal(checksum);
expect(formatted.action.to).to.equal(checksum);
expect(isBigNumber(formatted.blockNumber)).to.be.true;
expect(formatted.blockNumber.toNumber()).to.equal(13);
expect(isBigNumber(formatted.transactionPosition)).to.be.true;
expect(formatted.transactionPosition.toNumber()).to.equal(11);
});
});
describe('outVaultMeta', () => {
it('returns an exmpt object on null', () => {
expect(outVaultMeta(null)).to.deep.equal({});
});
it('returns the original value if not string', () => {
expect(outVaultMeta({ test: 123 })).to.deep.equal({ test: 123 });
});
it('returns an object from JSON string', () => {
expect(outVaultMeta('{"test":123}')).to.deep.equal({ test: 123 });
});
it('returns an empty object on invalid JSON', () => {
expect(outVaultMeta('{"test"}')).to.deep.equal({});
});
});
});

View File

@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './api';

View File

@ -1,34 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import es6Promise from 'es6-promise';
es6Promise.polyfill();
const isNode = typeof global !== 'undefined' && typeof global !== 'undefined';
const isBrowser = typeof self !== 'undefined' && typeof self.window !== 'undefined';
if (isBrowser) {
require('whatwg-fetch');
}
if (isNode) {
global.fetch = require('node-fetch');
}
import Abi from '../abi';
import Api from './';
export { Api, Abi };

View File

@ -1,112 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { createKeyObject, decryptPrivateKey } from '../ethkey';
export default class Account {
constructor (persist, data = {}) {
const {
keyObject = null,
meta = {},
name = ''
} = data;
this._persist = persist;
this._keyObject = keyObject;
this._name = name;
this._meta = meta;
}
isValidPassword (password) {
return decryptPrivateKey(this._keyObject, password)
.then((privateKey) => {
if (!privateKey) {
return false;
}
return true;
});
}
export () {
const exported = Object.assign({}, this._keyObject);
exported.meta = JSON.stringify(this._meta);
exported.name = this._name;
return exported;
}
get address () {
return `0x${this._keyObject.address.toLowerCase()}`;
}
get name () {
return this._name;
}
set name (name) {
this._name = name;
this._persist();
}
get meta () {
return JSON.stringify(this._meta);
}
set meta (meta) {
this._meta = JSON.parse(meta);
this._persist();
}
get uuid () {
if (!this._keyObject) {
return null;
}
return this._keyObject.id;
}
decryptPrivateKey (password) {
return decryptPrivateKey(this._keyObject, password);
}
changePassword (key, password) {
return createKeyObject(key, password).then((keyObject) => {
this._keyObject = keyObject;
this._persist();
});
}
static fromPrivateKey (persist, key, password) {
return createKeyObject(key, password).then((keyObject) => {
const account = new Account(persist, { keyObject });
return account;
});
}
toJSON () {
return {
keyObject: this._keyObject,
name: this._name,
meta: this._meta
};
}
}

View File

@ -1,223 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import Account from './account';
import localStore from 'store';
import { debounce } from 'lodash';
import { decryptPrivateKey } from '../ethkey';
const NULL_ADDRESS = '0x0000000000000000000000000000000000000000';
const LS_STORE_KEY = '_parity::localAccounts';
export default class Accounts {
persist = debounce(() => {
this._lastState = JSON.stringify(this);
localStore.set(LS_STORE_KEY, this);
}, 100);
constructor (data = localStore.get(LS_STORE_KEY) || {}) {
this._lastState = JSON.stringify(data);
window.addEventListener('storage', ({ key, newValue }) => {
if (key !== LS_STORE_KEY) {
return;
}
if (newValue !== this._lastState) {
console.log('Data changed in a second tab, syncing state');
this.restore(JSON.parse(newValue));
}
});
this.restore(data);
}
restore (data) {
const {
last = NULL_ADDRESS,
dappsDefault = NULL_ADDRESS,
store = {}
} = data;
this._last = last;
this._dappsDefaultAddress = dappsDefault;
this._store = {};
if (Array.isArray(store)) {
// Recover older version that stored accounts as an array
store.forEach((data) => {
const account = new Account(this.persist, data);
this._store[account.address] = account;
});
} else {
Object.keys(store).forEach((key) => {
this._store[key] = new Account(this.persist, store[key]);
});
}
}
_addAccount = (account) => {
const { address } = account;
if (address in this._store && this._store[address].uuid) {
throw new Error(`Account ${address} already exists!`);
}
this._store[address] = account;
this.lastAddress = address;
this.persist();
return account.address;
}
create (secret, password) {
const privateKey = Buffer.from(secret.slice(2), 'hex');
return Account
.fromPrivateKey(this.persist, privateKey, password)
.then(this._addAccount);
}
restoreFromWallet (wallet, password) {
return decryptPrivateKey(wallet, password)
.then((privateKey) => {
if (!privateKey) {
throw new Error('Invalid password');
}
return Account.fromPrivateKey(this.persist, privateKey, password);
})
.then(this._addAccount);
}
set lastAddress (value) {
this._last = value.toLowerCase();
}
get lastAddress () {
return this._last;
}
get dappsDefaultAddress () {
if (this._dappsDefaultAddress === NULL_ADDRESS) {
return this._last;
}
if (this._dappsDefaultAddress in this._store) {
return this._dappsDefaultAddress;
}
return NULL_ADDRESS;
}
set dappsDefaultAddress (value) {
this._dappsDefaultAddress = value.toLowerCase();
this.persist();
}
get (address) {
address = address.toLowerCase();
const account = this._store[address];
if (!account) {
throw new Error(`Account not found: ${address}`);
}
this.lastAddress = address;
return account;
}
getLazyCreate (address) {
address = address.toLowerCase();
this.lastAddress = address;
if (!(address in this._store)) {
this._store[address] = new Account(this.persist);
}
return this._store[address];
}
remove (address, password) {
address = address.toLowerCase();
const account = this.get(address);
if (!account) {
return false;
}
return account
.isValidPassword(password)
.then((isValid) => {
if (!isValid) {
return false;
}
if (address === this.lastAddress) {
this.lastAddress = NULL_ADDRESS;
}
this.removeUnsafe(address);
return true;
});
}
removeUnsafe (address) {
address = address.toLowerCase();
delete this._store[address];
this.persist();
}
allAddresses () {
return Object.keys(this._store);
}
accountAddresses () {
return Object
.keys(this._store)
.filter((address) => this._store[address].uuid);
}
map (mapper) {
const result = {};
Object.keys(this._store).forEach((key) => {
result[key] = mapper(this._store[key]);
});
return result;
}
toJSON () {
return {
last: this._last,
dappsDefault: this._dappsDefaultAddress,
store: this._store
};
}
}

View File

@ -1,21 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import Accounts from './accounts';
const accounts = new Accounts();
export default accounts;

View File

@ -1,147 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
/* global WebAssembly */
import wasmBuffer from './ethkey.wasm.js';
const NOOP = () => {};
// WASM memory setup
const WASM_PAGE_SIZE = 65536;
const STATIC_BASE = 1024;
const STATICTOP = STATIC_BASE + WASM_PAGE_SIZE * 2;
const STACK_BASE = align(STATICTOP + 16);
const STACKTOP = STACK_BASE;
const TOTAL_STACK = 5 * 1024 * 1024;
const TOTAL_MEMORY = 16777216;
const STACK_MAX = STACK_BASE + TOTAL_STACK;
const DYNAMIC_BASE = STACK_MAX + 64;
const DYNAMICTOP_PTR = STACK_MAX;
function mockWebAssembly () {
function throwWasmError () {
throw new Error('Missing WebAssembly support');
}
// Simple mock replacement
return {
Memory: class { buffer = new ArrayBuffer(2048) },
Table: class {},
Module: class {},
Instance: class {
exports = {
'_input_ptr': () => 0,
'_secret_ptr': () => 0,
'_public_ptr': () => 0,
'_address_ptr': () => 0,
'_ecpointg': NOOP,
'_brain': throwWasmError,
'_verify_secret': throwWasmError
}
}
};
}
const { Memory, Table, Module, Instance } = typeof WebAssembly !== 'undefined' ? WebAssembly : mockWebAssembly();
const wasmMemory = new Memory({
initial: TOTAL_MEMORY / WASM_PAGE_SIZE,
maximum: TOTAL_MEMORY / WASM_PAGE_SIZE
});
const wasmTable = new Table({
initial: 8,
maximum: 8,
element: 'anyfunc'
});
// TypedArray views into the memory
const wasmMemoryU8 = new Uint8Array(wasmMemory.buffer);
const wasmMemoryU32 = new Uint32Array(wasmMemory.buffer);
// Keep DYNAMIC_BASE in memory
wasmMemoryU32[DYNAMICTOP_PTR >> 2] = align(DYNAMIC_BASE);
function align (mem) {
const ALIGN_SIZE = 16;
return (Math.ceil(mem / ALIGN_SIZE) * ALIGN_SIZE) | 0;
}
export function slice (ptr, len) {
return wasmMemoryU8.subarray(ptr, ptr + len);
}
// Required by emscripten
function abort (what) {
throw new Error(what || 'WASM abort');
}
// Required by emscripten
function abortOnCannotGrowMemory () {
abort(`Cannot enlarge memory arrays.`);
}
// Required by emscripten
function enlargeMemory () {
abortOnCannotGrowMemory();
}
// Required by emscripten
function getTotalMemory () {
return TOTAL_MEMORY;
}
// Required by emscripten - used to perform memcpy on large data
function memcpy (dest, src, len) {
wasmMemoryU8.set(wasmMemoryU8.subarray(src, src + len), dest);
return dest;
}
// Synchronously compile WASM from the buffer
const module = new Module(wasmBuffer);
// Instantiated WASM module
const instance = new Instance(module, {
global: {},
env: {
DYNAMICTOP_PTR,
STACKTOP,
STACK_MAX,
abort,
enlargeMemory,
getTotalMemory,
abortOnCannotGrowMemory,
___lock: NOOP,
___syscall6: () => 0,
___setErrNo: (no) => no,
_abort: abort,
___syscall140: () => 0,
_emscripten_memcpy_big: memcpy,
___syscall54: () => 0,
___unlock: NOOP,
_llvm_trap: abort,
___syscall146: () => 0,
'memory': wasmMemory,
'table': wasmTable,
tableBase: 0,
memoryBase: STATIC_BASE
}
});
export const extern = instance.exports;

File diff suppressed because one or more lines are too long

View File

@ -1,47 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import workerPool from './workerPool';
export function createKeyObject (key, password) {
return workerPool.action('createKeyObject', { key, password })
.then((obj) => JSON.parse(obj));
}
export function decryptPrivateKey (keyObject, password) {
return workerPool
.action('decryptPrivateKey', { keyObject, password })
.then((privateKey) => {
if (privateKey) {
return Buffer.from(privateKey);
}
return null;
});
}
export function phraseToAddress (phrase) {
return phraseToWallet(phrase)
.then((wallet) => wallet.address);
}
export function phraseToWallet (phrase) {
return workerPool.action('phraseToWallet', phrase);
}
export function verifySecret (secret) {
return workerPool.action('verifySecret', secret);
}

View File

@ -1,58 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { randomPhrase } from '@parity/wordlist';
import { phraseToAddress, phraseToWallet } from './';
// TODO: Skipping until Node.js 8.0 comes out and we can test WebAssembly
describe.skip('api/local/ethkey', () => {
describe('phraseToAddress', function () {
this.timeout(30000);
it('generates a valid address', () => {
const phrase = randomPhrase(12);
return phraseToAddress(phrase).then((address) => {
expect(address.length).to.be.equal(42);
expect(address.slice(0, 4)).to.be.equal('0x00');
});
});
it('generates valid address for empty phrase', () => {
return phraseToAddress('').then((address) => {
expect(address).to.be.equal('0x00a329c0648769a73afac7f9381e08fb43dbea72');
});
});
});
describe('phraseToWallet', function () {
this.timeout(30000);
it('generates a valid wallet object', () => {
const phrase = randomPhrase(12);
return phraseToWallet(phrase).then((wallet) => {
expect(wallet.address.length).to.be.equal(42);
expect(wallet.secret.length).to.be.equal(66);
expect(wallet.public.length).to.be.equal(130);
expect(wallet.address.slice(0, 4)).to.be.equal('0x00');
expect(wallet.secret.slice(0, 2)).to.be.equal('0x');
expect(wallet.public.slice(0, 2)).to.be.equal('0x');
});
});
});
});

View File

@ -1,138 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { bytesToHex } from '../../util/format';
import { extern, slice } from './ethkey.js';
const isWorker = typeof self !== 'undefined';
// Stay compatible between environments
if (!isWorker) {
const scope = typeof global === 'undefined' ? window : global;
scope.self = scope;
}
// keythereum should never be used outside of the browser
let keythereum = require('keythereum');
if (isWorker) {
keythereum = self.keythereum;
}
function route ({ action, payload }) {
if (action in actions) {
return actions[action](payload);
}
return null;
}
const input = slice(extern._input_ptr(), 1024);
const secret = slice(extern._secret_ptr(), 32);
const publicKey = slice(extern._public_ptr(), 64);
const address = slice(extern._address_ptr(), 20);
extern._ecpointg();
const actions = {
phraseToWallet (phrase) {
const phraseUtf8 = Buffer.from(phrase, 'utf8');
if (phraseUtf8.length > input.length) {
throw new Error('Phrase is too long!');
}
input.set(phraseUtf8);
extern._brain(phraseUtf8.length);
const wallet = {
secret: bytesToHex(secret),
public: bytesToHex(publicKey),
address: bytesToHex(address)
};
return wallet;
},
verifySecret (key) {
const keyBuf = Buffer.from(key.slice(2), 'hex');
secret.set(keyBuf);
return extern._verify_secret();
},
createKeyObject ({ key, password }) {
key = Buffer.from(key);
password = Buffer.from(password);
const iv = keythereum.crypto.randomBytes(16);
const salt = keythereum.crypto.randomBytes(32);
const keyObject = keythereum.dump(password, key, salt, iv);
return JSON.stringify(keyObject);
},
decryptPrivateKey ({ keyObject, password }) {
password = Buffer.from(password);
try {
const key = keythereum.recover(password, keyObject);
// Convert to array to safely send from the worker
return Array.from(key);
} catch (e) {
return null;
}
}
};
self.onmessage = function ({ data }) {
try {
const result = route(data);
postMessage([null, result]);
} catch (err) {
console.error(err);
postMessage([err.toString(), null]);
}
};
// Emulate a web worker in Node.js
class KeyWorker {
postMessage (data) {
// Force async
setTimeout(() => {
try {
const result = route(data);
this.onmessage({ data: [null, result] });
} catch (err) {
this.onmessage({ data: [err, null] });
}
}, 0);
}
onmessage (event) {
// no-op to be overriden
}
}
if (exports != null) {
exports.KeyWorker = KeyWorker;
}

View File

@ -1,105 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
// Allow a web worker in the browser, with a fallback for Node.js
const hasWebWorkers = typeof Worker !== 'undefined';
const KeyWorker = hasWebWorkers ? require('worker-loader!./worker')
: require('./worker').KeyWorker;
class WorkerContainer {
busy = false;
_worker = new KeyWorker();
action (action, payload) {
if (this.busy) {
throw new Error('Cannot issue an action on a busy worker!');
}
this.busy = true;
return new Promise((resolve, reject) => {
this._worker.postMessage({ action, payload });
this._worker.onmessage = ({ data }) => {
const [err, result] = data;
this.busy = false;
if (err) {
// `err` ought to be a String
reject(new Error(err));
} else {
resolve(result);
}
};
});
}
}
class WorkerPool {
pool = [
new WorkerContainer(),
new WorkerContainer()
];
queue = [];
_getContainer () {
return this.pool.find((container) => !container.busy);
}
action (action, payload) {
let container = this.pool.find((container) => !container.busy);
let promise;
// const start = Date.now();
if (container) {
promise = container.action(action, payload);
} else {
promise = new Promise((resolve, reject) => {
this.queue.push([action, payload, resolve]);
});
}
return promise
.catch((err) => {
this.processQueue();
throw err;
})
.then((result) => {
this.processQueue();
// console.log('Work done in ', Date.now() - start);
return result;
});
}
processQueue () {
let container = this._getContainer();
while (container && this.queue.length > 0) {
const [action, payload, resolve] = this.queue.shift();
resolve(container.action(action, payload));
container = this._getContainer();
}
}
}
export default new WorkerPool();

View File

@ -1,17 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default null;

View File

@ -1,284 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import EthereumTx from 'ethereumjs-tx';
import accounts from './accounts';
import transactions from './transactions';
import { Middleware } from '../transport';
import { inNumber16 } from '../format/input';
import { phraseToWallet, phraseToAddress, verifySecret } from './ethkey';
import { randomPhrase } from '@parity/wordlist';
export default class LocalAccountsMiddleware extends Middleware {
constructor (transport) {
super(transport);
const register = this.register.bind(this);
register('eth_accounts', () => {
return accounts.accountAddresses();
});
register('eth_coinbase', () => {
return accounts.lastAddress;
});
register('parity_accountsInfo', () => {
return accounts.map(({ name }) => {
return { name };
});
});
register('parity_allAccountsInfo', () => {
return accounts.map(({ name, meta, uuid }) => {
return { name, meta, uuid };
});
});
register('parity_changePassword', ([address, oldPassword, newPassword]) => {
const account = accounts.get(address);
return account
.decryptPrivateKey(oldPassword)
.then((privateKey) => {
if (!privateKey) {
return false;
}
account.changePassword(privateKey, newPassword);
return true;
});
});
register('parity_checkRequest', ([id]) => {
return transactions.hash(id) || Promise.resolve(null);
});
register('parity_dappsList', () => {
return [];
});
register('parity_defaultAccount', () => {
return accounts.dappsDefaultAddress;
});
register('parity_exportAccount', ([address, password]) => {
const account = accounts.get(address);
if (!password) {
password = '';
}
return account.isValidPassword(password)
.then((isValid) => {
if (!isValid) {
throw new Error('Invalid password');
}
return account.export();
});
});
register('parity_generateSecretPhrase', () => {
return randomPhrase(12);
});
register('parity_getNewDappsAddresses', () => {
return accounts.accountAddresses();
});
register('parity_getNewDappsDefaultAddress', () => {
return accounts.dappsDefaultAddress;
});
register('parity_hardwareAccountsInfo', () => {
return {};
});
register('parity_newAccountFromPhrase', ([phrase, password]) => {
return phraseToWallet(phrase)
.then((wallet) => {
return accounts.create(wallet.secret, password);
});
});
register('parity_newAccountFromSecret', ([secret, password]) => {
return verifySecret(secret)
.then((isValid) => {
if (!isValid) {
throw new Error('Invalid secret key');
}
return accounts.create(secret, password);
});
});
register('parity_newAccountFromWallet', ([json, password]) => {
if (!password) {
password = '';
}
return accounts.restoreFromWallet(JSON.parse(json), password);
});
register('parity_setAccountMeta', ([address, meta]) => {
accounts.getLazyCreate(address).meta = meta;
return true;
});
register('parity_setAccountName', ([address, name]) => {
accounts.getLazyCreate(address).name = name;
return true;
});
register('parity_setNewDappsDefaultAddress', ([address]) => {
accounts.dappsDefaultAddress = address;
return true;
});
register('parity_postTransaction', ([tx]) => {
if (!tx.from) {
tx.from = accounts.lastAddress;
}
tx.nonce = null;
tx.condition = null;
return transactions.add(tx);
});
register('parity_phraseToAddress', ([phrase]) => {
return phraseToAddress(phrase);
});
register('parity_useLocalAccounts', () => {
return true;
});
register('parity_listGethAccounts', () => {
return [];
});
register('parity_listOpenedVaults', () => {
return [];
});
register('parity_listRecentDapps', () => {
return {};
});
register('parity_listVaults', () => {
return [];
});
register('parity_wsUrl', () => {
// This is a hack, will be replaced by a `hostname` setting on the node itself
return `${window.location.hostname}:8546`;
});
register('parity_dappsUrl', () => {
// This is a hack, will be replaced by a `hostname` setting on the node itself
return `${window.location.hostname}:8545`;
});
register('parity_hashContent', () => {
throw new Error('Functionality unavailable on a public wallet.');
});
register('parity_killAccount', ([address, password]) => {
return accounts.remove(address, password);
});
register('parity_testPassword', ([address, password]) => {
const account = accounts.get(address);
return account.isValidPassword(password);
});
register('parity_upgradeReady', () => {
return false;
});
register('signer_confirmRequest', ([id, modify, password]) => {
const {
gasPrice,
gas: gasLimit,
from,
to,
value,
data
} = Object.assign(transactions.get(id), modify);
transactions.lock(id);
const account = accounts.get(from);
return Promise.all([
this.rpcRequest('parity_nextNonce', [from]),
account.decryptPrivateKey(password)
])
.catch((err) => {
transactions.unlock(id);
// transaction got unlocked, can propagate rejection further
throw err;
})
.then(([nonce, privateKey]) => {
if (!privateKey) {
transactions.unlock(id);
throw new Error('Invalid password');
}
const tx = new EthereumTx({
nonce,
to,
data,
gasLimit: inNumber16(gasLimit),
gasPrice: inNumber16(gasPrice),
value: inNumber16(value)
});
tx.sign(privateKey);
const serializedTx = `0x${tx.serialize().toString('hex')}`;
return this.rpcRequest('eth_sendRawTransaction', [serializedTx]);
})
.then((hash) => {
transactions.confirm(id, hash);
return {};
});
});
register('signer_generateAuthorizationToken', () => {
return '';
});
register('signer_rejectRequest', ([id]) => {
return transactions.reject(id);
});
register('signer_requestsToConfirm', () => {
return transactions.requestsToConfirm();
});
}
}

View File

@ -1,157 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import LocalAccountsMiddleware from './localAccountsMiddleware';
import JsonRpcBase from '../transport/jsonRpcBase';
const RPC_RESPONSE = Symbol('RPC response');
const ADDRESS = '0x00a329c0648769a73afac7f9381e08fb43dbea72';
const SECRET = '0x4d5db4107d237df6a3d58ee5f70ae63d73d7658d4026f2eefd2f204c81682cb7';
const PASSWORD = 'password';
const FOO_PHRASE = 'foobar';
const FOO_PASSWORD = 'foopass';
const FOO_ADDRESS = '0x007ef7ac1058e5955e366ab9d6b6c4ebcc937e7e';
class MockedTransport extends JsonRpcBase {
_execute (method, params) {
return RPC_RESPONSE;
}
}
// Skip till all CI runs on Node 8+
describe.skip('api/local/LocalAccountsMiddleware', function () {
this.timeout(30000);
let transport;
beforeEach(() => {
transport = new MockedTransport();
transport.addMiddleware(LocalAccountsMiddleware);
// Same as `parity_newAccountFromPhrase` with empty phrase
return transport
.execute('parity_newAccountFromSecret', [SECRET, PASSWORD])
.catch((_err) => {
// Ignore the error - all instances of LocalAccountsMiddleware
// share account storage
});
});
it('registers all necessary methods', () => {
return Promise
.all([
'eth_accounts',
'eth_coinbase',
'parity_accountsInfo',
'parity_allAccountsInfo',
'parity_changePassword',
'parity_checkRequest',
'parity_defaultAccount',
'parity_generateSecretPhrase',
'parity_getNewDappsAddresses',
'parity_hardwareAccountsInfo',
'parity_newAccountFromPhrase',
'parity_newAccountFromSecret',
'parity_setAccountMeta',
'parity_setAccountName',
'parity_postTransaction',
'parity_phraseToAddress',
'parity_useLocalAccounts',
'parity_listGethAccounts',
'parity_listOpenedVaults',
'parity_listRecentDapps',
'parity_listVaults',
'parity_killAccount',
'parity_testPassword',
'signer_confirmRequest',
'signer_rejectRequest',
'signer_requestsToConfirm'
].map((method) => {
return transport
.execute(method)
.then((result) => {
expect(result).not.to.be.equal(RPC_RESPONSE);
})
// Some errors are expected here since we are calling methods
// without parameters.
.catch((_) => {});
}));
});
it('allows non-registered methods through', () => {
return transport
.execute('eth_getBalance', ['0x407d73d8a49eeb85d32cf465507dd71d507100c1'])
.then((result) => {
expect(result).to.be.equal(RPC_RESPONSE);
});
});
it('can handle `eth_accounts`', () => {
return transport
.execute('eth_accounts')
.then((accounts) => {
expect(accounts.length).to.be.equal(1);
expect(accounts[0]).to.be.equal(ADDRESS);
});
});
it('can handle `parity_defaultAccount`', () => {
return transport
.execute('parity_defaultAccount')
.then((address) => {
expect(address).to.be.equal(ADDRESS);
});
});
it('can handle `parity_phraseToAddress`', () => {
return transport
.execute('parity_phraseToAddress', [''])
.then((address) => {
expect(address).to.be.equal(ADDRESS);
return transport.execute('parity_phraseToAddress', [FOO_PHRASE]);
})
.then((address) => {
expect(address).to.be.equal(FOO_ADDRESS);
});
});
it('can create and kill an account', () => {
return transport
.execute('parity_newAccountFromPhrase', [FOO_PHRASE, FOO_PASSWORD])
.then((address) => {
expect(address).to.be.equal(FOO_ADDRESS);
return transport.execute('eth_accounts');
})
.then((accounts) => {
expect(accounts.length).to.be.equal(2);
expect(accounts.includes(FOO_ADDRESS)).to.be.true;
return transport.execute('parity_killAccount', [FOO_ADDRESS, FOO_PASSWORD]);
})
.then((result) => {
expect(result).to.be.true;
return transport.execute('eth_accounts');
})
.then((accounts) => {
expect(accounts.length).to.be.equal(1);
expect(accounts.includes(FOO_ADDRESS)).to.be.false;
});
});
});

View File

@ -1,147 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { toHex } from '../util/format';
import { TransportError } from '../transport';
const AWAITING = Symbol('awaiting');
const LOCKED = Symbol('locked');
const CONFIRMED = Symbol('confirmed');
const REJECTED = Symbol('rejected');
class Transactions {
constructor () {
this.reset();
}
// should only really be needed in the constructor and tests
reset () {
this._id = 1;
this._states = {};
}
nextId () {
return toHex(this._id++);
}
add (tx) {
const id = this.nextId();
this._states[id] = {
status: AWAITING,
transaction: tx
};
return id;
}
get (id) {
const state = this._states[id];
if (!state || state.status !== AWAITING) {
return null;
}
return state.transaction;
}
lock (id) {
const state = this._states[id];
if (!state || state.status !== AWAITING) {
throw new Error('Trying to lock an invalid transaction');
}
state.status = LOCKED;
}
unlock (id) {
const state = this._states[id];
if (!state || state.status !== LOCKED) {
throw new Error('Trying to unlock an invalid transaction');
}
state.status = AWAITING;
}
hash (id) {
const state = this._states[id];
if (!state) {
return null;
}
switch (state.status) {
case REJECTED:
throw TransportError.requestRejected();
case CONFIRMED:
return state.hash;
default:
return null;
}
}
confirm (id, hash) {
const state = this._states[id];
const status = state ? state.status : null;
switch (status) {
case AWAITING: break;
case LOCKED: break;
default: throw new Error('Trying to confirm an invalid transaction');
}
state.hash = hash;
state.status = CONFIRMED;
}
reject (id) {
const state = this._states[id];
if (!state) {
return false;
}
state.status = REJECTED;
return true;
}
requestsToConfirm () {
const result = [];
Object.keys(this._states).forEach((id) => {
const state = this._states[id];
if (state.status === AWAITING) {
result.push({
id,
origin: {
signer: '0x0'
},
payload: {
sendTransaction: state.transaction
}
});
}
});
return result;
}
}
export default new Transactions();

View File

@ -1,85 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import transactions from './transactions';
import { TransportError } from '../transport/error';
const DUMMY_TX = 'dummy';
describe('api/local/transactions', () => {
beforeEach(() => {
transactions.reset();
});
it('can store transactions', () => {
const id1 = transactions.add(DUMMY_TX);
const id2 = transactions.add(DUMMY_TX);
const requests = transactions.requestsToConfirm();
expect(id1).to.be.equal('0x1');
expect(id2).to.be.equal('0x2');
expect(requests.length).to.be.equal(2);
expect(requests[0].id).to.be.equal(id1);
expect(requests[1].id).to.be.equal(id2);
expect(requests[0].payload.sendTransaction).to.be.equal(DUMMY_TX);
expect(requests[1].payload.sendTransaction).to.be.equal(DUMMY_TX);
});
it('can confirm transactions', () => {
const id1 = transactions.add(DUMMY_TX);
const id2 = transactions.add(DUMMY_TX);
const hash1 = '0x1111111111111111111111111111111111111111';
const hash2 = '0x2222222222222222222222222222222222222222';
transactions.confirm(id1, hash1);
transactions.confirm(id2, hash2);
const requests = transactions.requestsToConfirm();
expect(requests.length).to.be.equal(0);
expect(transactions.hash(id1)).to.be.equal(hash1);
expect(transactions.hash(id2)).to.be.equal(hash2);
});
it('can reject transactions', () => {
const id = transactions.add(DUMMY_TX);
transactions.reject(id);
const requests = transactions.requestsToConfirm();
expect(requests.length).to.be.equal(0);
expect(() => transactions.hash(id)).to.throw(TransportError);
});
it('can lock and confirm transactions', () => {
const id = transactions.add(DUMMY_TX);
const hash = '0x1111111111111111111111111111111111111111';
transactions.lock(id);
const requests = transactions.requestsToConfirm();
expect(requests.length).to.be.equal(0);
expect(transactions.get(id)).to.be.null;
expect(transactions.hash(id)).to.be.null;
transactions.confirm(id, hash);
expect(transactions.hash(id)).to.be.equal(hash);
});
});

View File

@ -1,41 +0,0 @@
{
"name": "@parity/api",
"description": "The Parity Promise-based API library for interfacing with Ethereum over RPC",
"version": "0.0.0",
"main": "index.js",
"author": "Parity Team <admin@parity.io>",
"maintainers": [
"Jaco Greeff",
"Nicolas Gotchac"
],
"contributors": [],
"license": "GPL-3.0",
"repository": {
"type": "git",
"url": "git+https://github.com/paritytech/parity.git"
},
"keywords": [
"Ethereum",
"API",
"RPC",
"Parity",
"Promise"
],
"scripts": {
},
"devDependencies": {
},
"dependencies": {
"@parity/abi": "file:../abi",
"@parity/jsonrpc": "file:../jsonrpc",
"bignumber.js": "3.0.1",
"blockies": "0.0.2",
"es6-error": "4.0.0",
"ethereumjs-tx": "1.2.5",
"eventemitter3": "2.0.2",
"js-sha3": "0.5.5",
"keythereum": "0.4.6",
"node-fetch": "1.6.3",
"secp256k1": "3.2.5"
}
}

View File

@ -1,26 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { Http as Transport } from '../transport';
export default class Http extends Transport {
send = (method, params, callback) => {
this
._execute(method, params)
.then((result) => callback(null, result))
.catch((error) => callback(error));
}
}

View File

@ -1,28 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import Ws from './ws';
export Http from './http';
export PostMessage from './postMessage';
export PromiseProvider from './promise';
const WsSecure = Ws;
export {
Ws,
WsSecure
};

View File

@ -1,102 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default class PostMessage {
id = 0;
_messages = {};
constructor (token, destination) {
this._token = token;
this._destination = destination;
window.addEventListener('message', this.receiveMessage, false);
}
addMiddleware () {
}
_send (data) {
this._destination.postMessage(data, '*');
}
send = (method, params, callback) => {
const id = ++this.id;
this._messages[id] = { callback };
this._send({
id,
from: this._token,
method,
params,
token: this._token
});
}
subscribe = (api, callback, params) => {
// console.log('paritySubscribe', JSON.stringify(params), api, callback);
return new Promise((resolve, reject) => {
const id = ++this.id;
this._messages[id] = { callback, resolve, reject, subscription: true, initial: true };
this._send({
id,
from: this._token,
api,
params,
token: this._token
});
});
}
unsubscribe = (subId) => {
return new Promise((resolve, reject) => {
const id = ++this.id;
this._messages[id] = { callback: (e, v) => e ? reject(e) : resolve(v) };
this._send({
id,
from: this._token,
subId,
token: this._token
});
});
}
unsubscribeAll () {
return this.unsubscribe('*');
}
receiveMessage = ({ data: { id, error, from, token, result }, origin, source }) => {
if (from !== 'shell' || token !== this._token) {
return;
}
if (error) {
console.error(from, error);
}
if (this._messages[id].subscription) {
// console.log('subscription', result, 'initial?', this._messages[id].initial);
this._messages[id].initial
? this._messages[id].resolve(result)
: this._messages[id].callback(error && new Error(error), result);
this._messages[id].initial = false;
} else {
this._messages[id].callback(error && new Error(error), result);
this._messages[id] = null;
}
}
}

View File

@ -1,38 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default class PromiseProvider {
constructor (provider) {
this.provider = provider;
}
send = (method, ...params) => {
if (!this.provider.send) {
// old-style transport interface for backwards compatibility
return this.provider.execute(method, params);
}
return new Promise((resolve, reject) => {
this.provider.send(method, params, (error, result) => {
if (error) {
reject(error);
} else {
resolve(result);
}
});
});
}
}

View File

@ -1,26 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import { Ws as Transport } from '../transport';
export default class Ws extends Transport {
send = (method, params, callback) => {
this
._execute(method, params)
.then((result) => callback(null, result))
.catch((error) => callback(error));
}
}

View File

@ -1,227 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import PubsubBase from '../pubsubBase';
import { inAddress, inBlockNumber, inHex, inNumber16, inOptions, inFilter } from '../../format/input';
import { outAddress, outBlock, outNumber, outTransaction, outSyncing, outReceipt, outLog } from '../../format/output';
export default class Eth extends PubsubBase {
constructor (provider) {
super(provider);
this._api = 'parity';
}
newHeads (callback) {
return this.addListener('eth', 'newHeads', callback);
}
logs (callback) {
throw Error('not supported yet');
}
// eth API
protocolVersion (callback) {
return this.addListener(this._api, 'eth_protocolVersion', callback);
}
syncing (callback) {
return this.addListener(this._api, 'eth_syncing', (error, data) => {
error
? callback(error)
: callback(null, outSyncing(data));
});
}
hashrate (callback) {
return this.addListener(this._api, 'eth_hashrate', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
});
}
coinbase (callback) {
return this.addListener(this._api, 'eth_coinbase', (error, data) => {
error
? callback(error)
: callback(null, outAddress(data));
});
}
mining (callback) {
return this.addListener(this._api, 'eth_mining', callback);
}
gasPrice (callback) {
return this.addListener(this._api, 'eth_gasPrice', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
});
}
accounts (callback) {
return this.addListener(this._api, 'eth_accounts', (error, accounts) => {
error
? callback(error)
: callback(null, (accounts || []).map(outAddress));
});
}
blockNumber (callback) {
return this.addListener(this._api, 'eth_blockNumber', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
});
}
getBalance (callback, address, blockNumber = 'latest') {
return this.addListener(this._api, 'eth_getBalance', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
}, [inAddress(address), inBlockNumber(blockNumber)]);
}
getStorageAt (callback, address, index = 0, blockNumber = 'latest') {
return this.addListener(this._api, 'eth_getStorageAt', callback, [inAddress(address), inNumber16(index), inBlockNumber(blockNumber)]);
}
getBlockByHash (callback, hash, full = false) {
return this.addListener(this._api, 'eth_getBlockByHash', (error, data) => {
error
? callback(error)
: callback(null, outBlock(data));
}, [inHex(hash), full]);
}
getBlockByNumber (callback, blockNumber = 'latest', full = false) {
return this.addListener(this._api, 'eth_getBlockByNumber', (error, data) => {
error
? callback(error)
: callback(null, outBlock(data));
}, [inBlockNumber(blockNumber), full]);
}
getTransactionCount (callback, address, blockNumber = 'latest') {
return this.addListener(this._api, 'eth_getTransactionCount', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
}, [inAddress(address), inBlockNumber(blockNumber)]);
}
getBlockTransactionCountByHash (callback, hash) {
return this.addListener(this._api, 'eth_getBlockTransactionCountByHash', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
}, [inHex(hash)]);
}
getBlockTransactionCountByNumber (callback, blockNumber = 'latest') {
return this.addListener(this._api, 'eth_getBlockTransactionCountByNumber', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
}, [inBlockNumber(blockNumber)]);
}
getUncleCountByBlockHash (callback, hash) {
return this.addListener(this._api, 'eth_getUncleCountByBlockHash', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
}, [inHex(hash)]);
}
getUncleCountByBlockNumber (callback, blockNumber = 'latest') {
return this.addListener(this._api, 'eth_getUncleCountByBlockNumber', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
}, [inBlockNumber(blockNumber)]);
}
getCode (callback, address, blockNumber = 'latest') {
return this.addListener(this._api, 'eth_getCode', callback, [inAddress(address), inBlockNumber(blockNumber)]);
}
call (callback, options, blockNumber = 'latest') {
return this.addListener(this._api, 'eth_call', callback, [inOptions(options), inBlockNumber(blockNumber)]);
}
estimateGas (callback, options) {
return this.addListener(this._api, 'eth_estimateGas', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
}, [inOptions(options)]);
}
getTransactionByHash (callback, hash) {
return this.addListener(this._api, 'eth_getTransactionByHash', (error, data) => {
error
? callback(error)
: callback(null, outTransaction(data));
}, [inHex(hash)]);
}
getTransactionByBlockHashAndIndex (callback, hash, index = 0) {
return this.addListener(this._api, 'eth_getTransactionByBlockHashAndIndex', (error, data) => {
error
? callback(error)
: callback(null, outTransaction(data));
}, [inHex(hash), inNumber16(index)]);
}
getTransactionByBlockNumberAndIndex (callback, blockNumber = 'latest', index = 0) {
return this.addListener(this._api, 'eth_getTransactionByBlockNumberAndIndex', (error, data) => {
error
? callback(error)
: callback(null, outTransaction(data));
}, [inBlockNumber(blockNumber), inNumber16(index)]);
}
getTransactionReceipt (callback, txhash) {
return this.addListener(this._api, 'eth_getTransactionReceipt', (error, data) => {
error
? callback(error)
: callback(null, outReceipt(data));
}, [inHex(txhash)]);
}
getUncleByBlockHashAndIndex (callback, hash, index = 0) {
return this.addListener(this._api, 'eth_getUncleByBlockHashAndIndex', callback, [inHex(hash), inNumber16(index)]);
}
getUncleByBlockNumberAndIndex (callback, blockNumber = 'latest', index = 0) {
return this.addListener(this._api, 'eth_getUncleByBlockNumberAndIndex', callback, [inBlockNumber(blockNumber), inNumber16(index)]);
}
getLogs (callback, options) {
return this.addListener(this._api, 'eth_getLogs', (error, logs) => {
error
? callback(error)
: callback(null, (logs) => logs.map(outLog));
}, [inFilter(options)]);
}
getWork (callback) {
return this.addListener(this._api, 'eth_getWork', callback);
}
}

View File

@ -1,16 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './eth';

View File

@ -1,16 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './pubsub';

View File

@ -1,16 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './net';

View File

@ -1,42 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import PubsubBase from '../pubsubBase';
import { outNumber } from '../../format/output';
export default class Net extends PubsubBase {
constructor (provider) {
super(provider);
this._api = 'parity';
}
// net API
version (callback) {
return this.addListener(this._api, 'net_version', callback);
}
peerCount (callback) {
return this.addListener(this._api, 'net_peerCount', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
});
}
listening (callback) {
return this.addListener(this._api, 'net_listening', callback);
}
}

View File

@ -1,16 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default from './parity';

View File

@ -1,355 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import PubsubBase from '../pubsubBase';
import { inAddress, inBlockNumber, inData, inHex, inDeriveHash, inDeriveIndex } from '../../format/input';
import { outAccountInfo, outAddress, outBlock, outChainStatus, outHistogram, outHwAccountInfo, outNodeKind, outNumber, outPeers, outTransaction, outAddresses, outRecentDapps, outVaultMeta } from '../../format/output';
export default class Parity extends PubsubBase {
constructor (provider) {
super(provider);
this._api = 'parity';
}
// parity API
accountsInfo (callback) {
return this.addListener(this._api, 'parity_accountsInfo', (error, data) => {
error
? callback(error)
: callback(null, outAccountInfo(data));
});
}
hardwareAccountsInfo (callback) {
return this.addListener(this._api, 'parity_hardwareAccountsInfo', (error, data) => {
error
? callback(error)
: callback(null, outHwAccountInfo(data));
});
}
defaultAccount (callback) {
return this.addListener(this._api, 'parity_defaultAccount', (error, data) => {
error
? callback(error)
: callback(null, outAddress(data));
});
}
transactionsLimit (callback) {
return this.addListener(this._api, 'parity_transactionsLimit', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
});
}
extraData (callback) {
return this.addListener(this._api, 'parity_extraData', callback);
}
gasFloorTarget (callback) {
return this.addListener(this._api, 'parity_gasFloorTarget', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
});
}
gasCeilTarget (callback) {
return this.addListener(this._api, 'parity_gasCeilTarget', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
});
}
minGasPrice (callback) {
return this.addListener(this._api, 'parity_minGasPrice', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
});
}
devLogs (callback) {
return this.addListener(this._api, 'parity_devLogs', callback);
}
devLogsLevels (callback) {
return this.addListener(this._api, 'parity_devLogsLevels', callback);
}
netChain (callback) {
return this.addListener(this._api, 'parity_netChain', callback);
}
netPeers (callback) {
return this.addListener(this._api, 'parity_netPeers', (error, data) => {
error
? callback(error)
: callback(null, outPeers(data));
});
}
netPort (callback) {
return this.addListener(this._api, 'parity_netPort', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
});
}
rpcSettings (callback) {
return this.addListener(this._api, 'parity_rpcSettings', callback);
}
nodeName (callback) {
return this.addListener(this._api, 'parity_nodeName', callback);
}
defaultExtraData (callback) {
return this.addListener(this._api, 'parity_defaultExtraData', callback);
}
gasPriceHistogram (callback) {
return this.addListener(this._api, 'parity_gasPriceHistogram', (error, data) => {
error
? callback(error)
: callback(null, outHistogram(data));
});
}
unsignedTransactionsCount (callback) {
return this.addListener(this._api, 'parity_unsignedTransactionsCount', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
});
}
registryAddress (callback) {
return this.addListener(this._api, 'parity_registryAddress', (error, data) => {
error
? callback(error)
: callback(null, outAddress(data));
});
}
listAccounts (callback, count, offset = null, blockNumber = 'latest') {
return this.addListener(this._api, 'parity_listAccounts', (error, data) => {
error
? callback(error)
: callback(null, (data) => (data || []).map(outAddress));
}, [count, inAddress(offset), inBlockNumber(blockNumber)]);
}
listStorageKeys (callback, address, count, hash = null, blockNumber = 'latest') {
return this.addListener(this._api, 'parity_listStorageKeys', callback, [inAddress(address), count, inHex(hash), inBlockNumber(blockNumber)]);
}
pendingTransactions (callback) {
return this.addListener(this._api, 'parity_pendingTransactions', (error, data) => {
error
? callback(error)
: callback(null, outTransaction(data));
});
}
futureTransactions (callback) {
return this.addListener(this._api, 'parity_futureTransactions', (error, data) => {
error
? callback(error)
: callback(null, outTransaction(data));
});
}
pendingTransactionsStats (callback) {
return this.addListener(this._api, 'parity_pendingTransactionsStats', callback);
}
localTransactions (callback) {
return this.addListener(this._api, 'parity_localTransactions', (error, transactions) => {
error
? callback(error)
: callback(null, transactions => {
Object.values(transactions)
.filter(tx => tx.transaction)
.map(tx => {
tx.transaction = outTransaction(tx.transaction);
});
return transactions;
});
});
}
dappsUrl (callback) {
return this.addListener(this._api, 'parity_dappsUrl', callback);
}
wsUrl (callback) {
return this.addListener(this._api, 'parity_wsUrl', callback);
}
nextNonce (callback, account) {
return this.addListener(this._api, 'parity_nextNonce', (error, data) => {
error
? callback(error)
: callback(null, outNumber(data));
}, [inAddress(account)]);
}
mode (callback) {
return this.addListener(this._api, 'parity_mode', callback);
}
chain (callback) {
return this.addListener(this._api, 'parity_chain', callback);
}
enode (callback) {
return this.addListener(this._api, 'parity_enode', callback);
}
consensusCapability (callback) {
return this.addListener(this._api, 'parity_consensusCapability', callback);
}
versionInfo (callback) {
return this.addListener(this._api, 'parity_versionInfo', callback);
}
releasesInfo (callback) {
return this.addListener(this._api, 'parity_releasesInfo', callback);
}
chainStatus (callback) {
return this.addListener(this._api, 'parity_chainStatus', (error, data) => {
error
? callback(error)
: callback(null, outChainStatus(data));
});
}
nodeKind (callback) {
return this.addListener(this._api, 'parity_nodeKind', (error, data) => {
error
? callback(error)
: callback(null, outNodeKind(data));
});
}
getBlockHeaderByNumber (callback, blockNumber = 'latest') {
return this.addListener(this._api, 'parity_getBlockHeaderByNumber', (error, data) => {
error
? callback(error)
: callback(null, outBlock(data));
}, [inBlockNumber(blockNumber)]);
}
cidV0 (callback, data) {
return this.addListener(this._api, 'parity_cidV0', callback, [inData(data)]);
}
// parity accounts API (only secure API or configured to be exposed)
allAccountsInfo (callback) {
return this._addListener(this._api, 'parity_allAccountsInfo', (error, data) => {
error
? callback(error)
: callback(null, outAccountInfo(data));
});
}
getDappAddresses (callback, dappId) {
return this._addListener(this._api, 'parity_getDappAddresses', (error, data) => {
error
? callback(error)
: callback(null, outAddresses(data));
}, [dappId]);
}
getDappDefaultAddress (callback, dappId) {
return this._addListener(this._api, 'parity_getDappDefaultAddress', (error, data) => {
error
? callback(error)
: callback(null, outAddress(data));
}, [dappId]);
}
getNewDappsAddresses (callback) {
return this._addListener(this._api, 'parity_getDappDefaultAddress', (error, addresses) => {
error
? callback(error)
: callback(null, addresses ? addresses.map(outAddress) : null);
});
}
getNewDappsDefaultAddress (callback) {
return this._addListener(this._api, 'parity_getNewDappsDefaultAddress', (error, data) => {
error
? callback(error)
: callback(null, outAddress(data));
});
}
listRecentDapps (callback) {
return this._addListener(this._api, 'parity_listRecentDapps', (error, data) => {
error
? callback(error)
: callback(null, outRecentDapps(data));
});
}
listGethAccounts (callback) {
return this._addListener(this._api, 'parity_listGethAccounts', (error, data) => {
error
? callback(error)
: callback(null, outAddresses(data));
});
}
listVaults (callback) {
return this._addListener(this._api, 'parity_listVaults', callback);
}
listOpenedVaults (callback) {
return this._addListener(this._api, 'parity_listOpenedVaults', callback);
}
getVaultMeta (callback, vaultName) {
return this._addListener(this._api, 'parity_getVaultMeta', (error, data) => {
error
? callback(error)
: callback(null, outVaultMeta(data));
}, [vaultName]);
}
deriveAddressHash (callback, address, password, hash, shouldSave) {
return this._addListener(this._api, 'parity_deriveAddressHash', (error, data) => {
error
? callback(error)
: callback(null, outAddress(data));
}, [inAddress(address), password, inDeriveHash(hash), !!shouldSave]);
}
deriveAddressIndex (callback, address, password, index, shouldSave) {
return this._addListener(this._api, 'parity_deriveAddressIndex', (error, data) => {
error
? callback(error)
: callback(null, outAddress(data));
}, [inAddress(address), password, inDeriveIndex(index), !!shouldSave]);
}
}

View File

@ -1,50 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import Eth from './eth';
import Parity from './parity';
import Net from './net';
import { isFunction } from '../util/types';
export default class Pubsub {
constructor (provider) {
if (!provider || !isFunction(provider.subscribe)) {
throw new Error('Pubsub API needs transport with subscribe() function defined. (WebSocket)');
}
this._eth = new Eth(provider);
this._net = new Net(provider);
this._parity = new Parity(provider);
}
get net () {
return this._net;
}
get eth () {
return this._eth;
}
get parity () {
return this._parity;
}
unsubscribe (subscriptionIds) {
// subscriptions are namespace independent. Thus we can simply removeListener from any.
return this._parity.removeListener(subscriptionIds);
}
}

View File

@ -1,613 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
import BigNumber from 'bignumber.js';
import { TEST_WS_URL, mockWs } from '../../../test/mockRpc';
import { isBigNumber } from '../../../test/types';
import { WsSecure } from '../provider';
import Pubsub from './pubsub';
describe('api/pubsub/Pubsub', () => {
let scope;
let instance;
const address = '0x63Cf90D3f0410092FC0fca41846f596223979195';
describe('accountsInfo', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: {
'0x63cf90d3f0410092fc0fca41846f596223979195': {
name: 'name', uuid: 'uuid', meta: '{"data":"data"}'
}
},
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('retrieves the available account info', (done) => {
instance.parity.accountsInfo((error, result) => {
expect(error).to.be.null;
expect(result).to.deep.equal({
'0x63Cf90D3f0410092FC0fca41846f596223979195': {
name: 'name', uuid: 'uuid', meta: {
data: 'data'
}
}
});
done();
});
});
});
describe('Unsubscribe', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2 },
{ method: 'parity_unsubscribe', reply: true }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('Promise gets resolved on success.', (done) => {
instance.parity.accountsInfo().then(s => {
instance.parity.unsubscribe(s).then(b => {
expect(b).to.be.true;
});
});
done();
});
});
describe('chainStatus', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: {
'blockGap': [0x123, 0x456]
},
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('retrieves the chain status', (done) => {
instance.parity.chainStatus((error, result) => {
expect(error).to.be.null;
expect(result).to.deep.equal({
'blockGap': [new BigNumber(0x123), new BigNumber(0x456)]
});
done();
});
});
});
describe('gasFloorTarget', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: '0x123456',
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('returns the gasfloor, formatted', (done) => {
instance.parity.gasFloorTarget((error, result) => {
expect(error).to.be.null;
expect(isBigNumber(result)).to.be.true;
expect(result.eq(0x123456)).to.be.true;
done();
});
});
});
describe('transactionsLimit', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: 1024,
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('returns the tx limit, formatted', (done) => {
instance.parity.transactionsLimit((error, result) => {
expect(error).to.be.null;
expect(isBigNumber(result)).to.be.true;
expect(result.eq(1024)).to.be.true;
done();
});
});
});
describe('minGasPrice', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: '0x123456',
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('returns the min gasprice, formatted', (done) => {
instance.parity.minGasPrice((error, result) => {
expect(error).to.be.null;
expect(isBigNumber(result)).to.be.true;
expect(result.eq(0x123456)).to.be.true;
done();
});
});
});
describe('netPeers', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: { active: 123, connected: 456, max: 789, peers: [] },
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('returns the peer structure, formatted', (done) => {
instance.parity.netPeers((error, peers) => {
expect(error).to.be.null;
expect(peers.active.eq(123)).to.be.true;
expect(peers.connected.eq(456)).to.be.true;
expect(peers.max.eq(789)).to.be.true;
done();
});
});
});
describe('netPort', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: 33030,
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('returns the connected port, formatted', (done) => {
instance.parity.netPort((error, count) => {
expect(error).to.be.null;
expect(isBigNumber(count)).to.be.true;
expect(count.eq(33030)).to.be.true;
done();
});
});
});
// Eth API
describe('accounts', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: [address.toLowerCase()],
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('returns a list of accounts, formatted', (done) => {
instance.eth.accounts((error, accounts) => {
expect(error).to.be.null;
expect(accounts).to.deep.equal([address]);
done();
});
});
});
describe('newHeads', () => {
beforeEach(() => {
scope = mockWs([{ method: 'eth_subscribe', reply: 2, subscription: {
method: 'eth_subscription',
params: {
result: '0x123456',
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('returns newHeads for eth_subscribe', (done) => {
instance.eth.newHeads((error, blockNumber) => {
expect(error).to.be.null;
expect(blockNumber).to.equal('0x123456');
done();
});
});
});
describe('blockNumber', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: '0x123456',
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('returns the current blockNumber, formatted', (done) => {
instance.eth.blockNumber((error, blockNumber) => {
expect(error).to.be.null;
expect(isBigNumber(blockNumber)).to.be.true;
expect(blockNumber.toString(16)).to.equal('123456');
done();
});
});
});
describe('call', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: [],
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('formats the input options & blockNumber', (done) => {
instance.eth.call((error) => {
expect(error).to.be.null;
expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_call', [{ data: '0x12345678' }, 'earliest']]);
done();
}, { data: '12345678' }, 'earliest');
});
it('provides a latest blockNumber when not specified', (done) => {
instance.eth.call((error) => {
expect(error).to.be.null;
expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_call', [{ data: '0x12345678' }, 'latest']]);
done();
}, { data: '12345678' });
});
});
describe('coinbase', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: address.toLowerCase(),
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('returns the coinbase, formatted', (done) => {
instance.eth.coinbase((error, account) => {
expect(error).to.be.null;
expect(account).to.deep.equal(address);
done();
});
});
});
describe('estimateGas', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: '0x123',
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('converts the options correctly', (done) => {
instance.eth.estimateGas((error) => {
expect(error).to.be.null;
expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_estimateGas', [{ gas: '0x5208' }]]);
done();
}, { gas: 21000 });
});
it('returns the gas used, formatted', (done) => {
instance.eth.estimateGas((error, gas) => {
expect(error).to.be.null;
expect(isBigNumber(gas)).to.be.true;
expect(gas.toString(16)).to.deep.equal('123');
done();
});
});
});
describe('gasPrice', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: '0x123',
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('returns the gas price, formatted', (done) => {
instance.eth.gasPrice((error, price) => {
expect(error).to.be.null;
expect(isBigNumber(price)).to.be.true;
expect(price.toString(16)).to.deep.equal('123');
done();
});
});
});
describe('getBalance', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: '0x123',
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('passes in the address (default blockNumber)', (done) => {
instance.eth.getBalance((error) => {
expect(error).to.be.null;
expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getBalance', [address.toLowerCase(), 'latest']]);
done();
}, address);
});
it('passes in the address & blockNumber', (done) => {
instance.eth.getBalance((error) => {
expect(error).to.be.null;
expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getBalance', [address.toLowerCase(), '0x456']]);
done();
}, address, 0x456);
});
it('returns the balance', (done) => {
instance.eth.getBalance((error, balance) => {
expect(error).to.be.null;
expect(isBigNumber(balance)).to.be.true;
expect(balance.toString(16)).to.deep.equal('123');
done();
}, address);
});
});
describe('getBlockByHash', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: { miner: address.toLowerCase() },
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('formats the input hash as a hash, default full', (done) => {
instance.eth.getBlockByHash((error) => {
expect(error).to.be.null;
expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getBlockByHash', ['0x1234', false]]);
done();
}, '1234');
});
it('formats the input hash as a hash, full true', (done) => {
instance.eth.getBlockByHash((error) => {
expect(error).to.be.null;
expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getBlockByHash', ['0x1234', true]]);
done();
}, '1234', true);
});
it('formats the output into block', (done) => {
instance.eth.getBlockByHash((error, block) => {
expect(error).to.be.null;
expect(block.miner).to.equal(address);
done();
}, '1234');
});
});
describe('getBlockByNumber', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: { miner: address.toLowerCase() },
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('assumes blockNumber latest & full false', (done) => {
instance.eth.getBlockByNumber((error) => {
expect(error).to.be.null;
expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getBlockByNumber', ['latest', false]]);
done();
});
});
it('uses input blockNumber & full false', (done) => {
instance.eth.getBlockByNumber((error) => {
expect(error).to.be.null;
expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getBlockByNumber', ['0x1234', false]]);
done();
}, '0x1234');
});
it('formats the input blockNumber, full true', (done) => {
instance.eth.getBlockByNumber((error) => {
expect(error).to.be.null;
expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getBlockByNumber', ['0x1234', true]]);
done();
}, 0x1234, true);
});
it('formats the output into block', (done) => {
instance.eth.getBlockByNumber((error, block) => {
expect(error).to.be.null;
expect(block.miner).to.equal(address);
done();
}, 0x1234);
});
});
describe('getTransactionCount', () => {
beforeEach(() => {
scope = mockWs([{ method: 'parity_subscribe', reply: 2, subscription: {
method: 'parity_subscription',
params: {
result: '0x123',
subscription: 2
}
} }]);
instance = new Pubsub(new WsSecure(TEST_WS_URL));
});
afterEach(() => {
scope.stop();
});
it('passes in the address (default blockNumber)', (done) => {
instance.eth.getTransactionCount((error) => {
expect(error).to.be.null;
expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getTransactionCount', [address.toLowerCase(), 'latest']]);
done();
}, address);
});
it('passes in the address & blockNumber', (done) => {
instance.eth.getTransactionCount((error) => {
expect(error).to.be.null;
expect(scope.body.parity_subscribe.params).to.deep.equal(['eth_getTransactionCount', [address.toLowerCase(), '0x456']]);
done();
}, address, 0x456);
});
it('returns the count, formatted', (done) => {
instance.eth.getTransactionCount((error, count) => {
expect(error).to.be.null;
expect(isBigNumber(count)).to.be.true;
expect(count.toString(16)).to.equal('123');
done();
}, address, 0x456);
});
});
});

View File

@ -1,37 +0,0 @@
// Copyright 2015-2017 Parity Technologies (UK) Ltd.
// This file is part of Parity.
// Parity is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// Parity is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with Parity. If not, see <http://www.gnu.org/licenses/>.
export default class PubsubBase {
// Provider for websocket pubsub transport
constructor (provider) {
this._provider = provider;
}
addListener (module, eventName, handler, eventParams) {
return eventParams
? this._provider.subscribe(module, handler, [eventName, eventParams])
: this._provider.subscribe(module, handler, [eventName, []]);
// this._transport.subscribe(module, handler, eventName); After Patch from tomac is merged to master! => eth_subscribe does not support empty array as params
}
removeListener (subscriptionIds) {
return this._provider.unsubscribe(subscriptionIds);
}
unsubscribe (subscriptionIds) {
return this.removeListener(subscriptionIds);
}
}

Some files were not shown because too many files have changed in this diff Show More