aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorFabio Berger <me@fabioberger.com>2017-05-26 23:12:22 +0800
committerFabio Berger <me@fabioberger.com>2017-05-26 23:12:22 +0800
commit555bac19cb90a83d5a0025e53341f76ff39c4373 (patch)
tree45db0a8151ef22ad69d9d42b8fc10807d1b613ac
parentf4bf9fc423c4bb7f2027cdd5700de56f8f4bff96 (diff)
downloaddexon-sol-tools-555bac19cb90a83d5a0025e53341f76ff39c4373.tar
dexon-sol-tools-555bac19cb90a83d5a0025e53341f76ff39c4373.tar.gz
dexon-sol-tools-555bac19cb90a83d5a0025e53341f76ff39c4373.tar.bz2
dexon-sol-tools-555bac19cb90a83d5a0025e53341f76ff39c4373.tar.lz
dexon-sol-tools-555bac19cb90a83d5a0025e53341f76ff39c4373.tar.xz
dexon-sol-tools-555bac19cb90a83d5a0025e53341f76ff39c4373.tar.zst
dexon-sol-tools-555bac19cb90a83d5a0025e53341f76ff39c4373.zip
Setup blockchain snapshotting before/after every test, implemented unit tests for exchangeWrapper.isValidSignature
-rw-r--r--package.json5
-rw-r--r--src/ts/globals.d.ts5
-rw-r--r--test/contract_wrapper_test.ts99
-rw-r--r--test/utils/blockchain_clean.ts19
-rw-r--r--test/utils/constants.ts5
-rw-r--r--test/utils/rpc.ts48
-rw-r--r--test/utils/web3_factory.ts23
7 files changed, 204 insertions, 0 deletions
diff --git a/package.json b/package.json
index 0adb44f8b..48a26fd48 100644
--- a/package.json
+++ b/package.json
@@ -36,6 +36,7 @@
"devDependencies": {
"@types/bignumber.js": "^4.0.2",
"@types/chai": "^3.5.2",
+ "@types/chai-as-promised": "0.0.30",
"@types/jsonschema": "^1.1.1",
"@types/lodash": "^4.14.64",
"@types/mocha": "^2.2.41",
@@ -43,6 +44,7 @@
"awesome-typescript-loader": "^3.1.3",
"bignumber.js": "^4.0.2",
"chai": "^3.5.0",
+ "chai-as-promised": "^6.0.0",
"chai-bignumber": "^2.0.0",
"copyfiles": "^1.2.0",
"json-loader": "^0.5.4",
@@ -50,12 +52,15 @@
"npm-run-all": "^4.0.2",
"nyc": "^10.3.2",
"opn-cli": "^3.1.0",
+ "request": "^2.81.0",
+ "request-promise-native": "^1.0.4",
"shx": "^0.2.2",
"source-map-support": "^0.4.15",
"tslint": "^5.3.2",
"tslint-config-0xproject": "^0.0.2",
"typedoc": "^0.7.1",
"typescript": "^2.3.3",
+ "web3-provider-engine": "^12.1.0",
"web3-typescript-typings": "0.0.8",
"webpack": "^2.6.0"
},
diff --git a/src/ts/globals.d.ts b/src/ts/globals.d.ts
index 30225d0d8..dee957f2f 100644
--- a/src/ts/globals.d.ts
+++ b/src/ts/globals.d.ts
@@ -1,5 +1,8 @@
declare module 'chai-bignumber';
declare module 'bn.js';
+declare module 'request-promise-native';
+declare module 'web3-provider-engine';
+declare module 'web3-provider-engine/subproviders/rpc';
declare interface Schema {
id: string;
@@ -12,6 +15,7 @@ declare interface Schema {
declare namespace Chai {
interface Assertion {
bignumber: Assertion;
+ eventually: Assertion;
}
}
/* tslint:enable */
@@ -30,6 +34,7 @@ declare module 'ethereumjs-util' {
const ecrecover: (msgHashBuff: Buffer, v: number, r: Buffer, s: Buffer) => string;
const pubToAddress: (pubKey: string) => Buffer;
const isValidAddress: (address: string) => boolean;
+ const bufferToInt: (buffer: Buffer) => number;
}
// truffle-contract declarations
diff --git a/test/contract_wrapper_test.ts b/test/contract_wrapper_test.ts
new file mode 100644
index 000000000..65eceb827
--- /dev/null
+++ b/test/contract_wrapper_test.ts
@@ -0,0 +1,99 @@
+import 'mocha';
+import * as chai from 'chai';
+import chaiAsPromised = require('chai-as-promised');
+import {web3Factory} from './utils/web3_factory';
+import * as Web3 from 'web3';
+import {ExchangeWrapper} from '../src/ts/contract_wrappers/exchange_wrapper';
+import {BlockchainClean} from './utils/blockchain_clean';
+import {Web3Wrapper} from './../src/ts/web3_wrapper';
+
+const expect = chai.expect;
+chai.use(chaiAsPromised);
+const blockchainClean = new BlockchainClean();
+
+describe('ExchangeWrapper', () => {
+ let web3Wrapper: Web3Wrapper;
+ let exchangeWrapper: ExchangeWrapper;
+ before(async () => {
+ const web3 = web3Factory.create();
+ web3Wrapper = new Web3Wrapper(web3);
+ exchangeWrapper = new ExchangeWrapper(web3Wrapper);
+ });
+ beforeEach(async () => {
+ await blockchainClean.setupAsync();
+ });
+ afterEach(async () => {
+ await blockchainClean.restoreAsync();
+ });
+ describe('#isValidSignatureAsync', () => {
+ // The Exchange smart contract `isValidSignature` method only validates orderHashes and assumes
+ // the length of the data is exactly 32 bytes. Thus for these tests, we use data of this size.
+ const dataHex = '0x6927e990021d23b1eb7b8789f6a6feaf98fe104bb0cf8259421b79f9a34222b0';
+ const signature = {
+ v: 27,
+ r: '0x61a3ed31b43c8780e905a260a35faefcc527be7516aa11c0256729b5b351bc33',
+ s: '0x40349190569279751135161d22529dc25add4f6069af05be04cacbda2ace2254',
+ };
+ const address = '0x5409ed021d9299bf6814279a6a1411a7e866a631';
+ describe('should throw if passed a malformed signature', () => {
+ it('malformed v', async () => {
+ const malformedSignature = {
+ v: 34,
+ r: signature.r,
+ s: signature.s,
+ };
+ expect(exchangeWrapper.isValidSignatureAsync(dataHex, malformedSignature, address))
+ .to.be.rejected;
+ });
+ it('r lacks 0x prefix', () => {
+ const malformedR = signature.r.replace('0x', '');
+ const malformedSignature = {
+ v: signature.v,
+ r: malformedR,
+ s: signature.s,
+ };
+ expect(exchangeWrapper.isValidSignatureAsync(dataHex, malformedSignature, address))
+ .to.be.rejected;
+ });
+ it('r is too short', () => {
+ const malformedR = signature.r.substr(10);
+ const malformedSignature = {
+ v: signature.v,
+ r: malformedR,
+ s: signature.s.replace('0', 'z'),
+ };
+ expect(exchangeWrapper.isValidSignatureAsync(dataHex, malformedSignature, address))
+ .to.be.rejected;
+ });
+ it('s is not hex', () => {
+ const malformedS = signature.s.replace('0', 'z');
+ const malformedSignature = {
+ v: signature.v,
+ r: signature.r,
+ s: malformedS,
+ };
+ expect(exchangeWrapper.isValidSignatureAsync(dataHex, malformedSignature, address))
+ .to.be.rejected;
+ });
+ });
+ it('should return false if the data doesn\'t pertain to the signature & address', async () => {
+ const isValid = await exchangeWrapper.isValidSignatureAsync('0x0', signature, address);
+ expect(isValid).to.be.false;
+ });
+ it('should return false if the address doesn\'t pertain to the signature & dataHex', async () => {
+ const validUnrelatedAddress = '0x8b0292B11a196601eD2ce54B665CaFEca0347D42';
+ const isValid = await exchangeWrapper.isValidSignatureAsync(dataHex, signature, validUnrelatedAddress);
+ expect(isValid).to.be.false;
+ });
+ it('should return false if the signature doesn\'t pertain to the dataHex & address', async () => {
+ const wrongSignature = Object.assign({}, signature, {v: 28});
+ const isValid = await exchangeWrapper.isValidSignatureAsync(dataHex, wrongSignature, address);
+ expect(isValid).to.be.false;
+ });
+ it('should return true if the signature does pertain to the dataHex & address', async () => {
+ const isValid = await exchangeWrapper.isValidSignatureAsync(dataHex, signature, address);
+ console.log('isValid', isValid);
+ expect(isValid).to.be.true;
+ });
+ });
+});
diff --git a/test/utils/blockchain_clean.ts b/test/utils/blockchain_clean.ts
new file mode 100644
index 000000000..6468dbec7
--- /dev/null
+++ b/test/utils/blockchain_clean.ts
@@ -0,0 +1,19 @@
+import {RPC} from './rpc';
+
+export class BlockchainClean {
+ private rpc: RPC;
+ private snapshotId: number;
+ constructor() {
+ this.rpc = new RPC();
+ }
+ // TODO: Check if running on TestRPC or on actual node, if actual node, re-deploy contracts instead
+ public async setupAsync() {
+ this.snapshotId = await this.rpc.takeSnapshotAsync();
+ }
+ public async restoreAsync() {
+ const didRevert = await this.rpc.revertSnapshotAsync(this.snapshotId);
+ if (!didRevert) {
+ throw new Error(`Snapshot with id #${this.snapshotId} failed to revert`);
+ }
+ }
+};
diff --git a/test/utils/constants.ts b/test/utils/constants.ts
new file mode 100644
index 000000000..aa1967f88
--- /dev/null
+++ b/test/utils/constants.ts
@@ -0,0 +1,5 @@
+export const constants = {
+ NULL_ADDRESS: '0x0000000000000000000000000000000000000000',
+ RPC_HOST: 'localhost',
+ RPC_PORT: 8545,
+};
diff --git a/test/utils/rpc.ts b/test/utils/rpc.ts
new file mode 100644
index 000000000..b689a938c
--- /dev/null
+++ b/test/utils/rpc.ts
@@ -0,0 +1,48 @@
+import * as ethUtil from 'ethereumjs-util';
+import * as request from 'request-promise-native';
+import {constants} from './constants';
+
+export class RPC {
+ private host: string;
+ private port: number;
+ private id: number;
+ constructor() {
+ this.host = constants.RPC_HOST;
+ this.port = constants.RPC_PORT;
+ this.id = 0;
+ }
+ public async takeSnapshotAsync(): Promise<number> {
+ const method = 'evm_snapshot';
+ const params: any[] = [];
+ const payload = this.toPayload(method, params);
+ const snapshotIdHex = await this.sendAsync(payload);
+ const snapshotId = ethUtil.bufferToInt(ethUtil.toBuffer(snapshotIdHex));
+ return snapshotId;
+ }
+ public async revertSnapshotAsync(snapshotId: number): Promise<boolean> {
+ const method = 'evm_revert';
+ const params = [snapshotId];
+ const payload = this.toPayload(method, params);
+ const didRevert = await this.sendAsync(payload);
+ return didRevert;
+ }
+ private toPayload(method: string, params: any[] = []) {
+ const payload = JSON.stringify({
+ id: this.id,
+ method,
+ params,
+ });
+ this.id += 1;
+ return payload;
+ }
+ private async sendAsync(payload: string) {
+ const opts = {
+ method: 'POST',
+ uri: `http://${this.host}:${this.port}`,
+ body: payload,
+ };
+ const bodyString = await request(opts);
+ const body = JSON.parse(bodyString);
+ return body.result;
+ }
+}
diff --git a/test/utils/web3_factory.ts b/test/utils/web3_factory.ts
new file mode 100644
index 000000000..493fbc2df
--- /dev/null
+++ b/test/utils/web3_factory.ts
@@ -0,0 +1,23 @@
+// HACK: web3 injects XMLHttpRequest into the global scope and ProviderEngine checks XMLHttpRequest
+// to know whether it is running in a browser or node environment. We need it to be undefined since
+// we are not running in a browser env.
+// Filed issue: https://github.com/ethereum/web3.js/issues/844
+(global as any).XMLHttpRequest = undefined;
+import ProviderEngine = require('web3-provider-engine');
+import RpcSubprovider = require('web3-provider-engine/subproviders/rpc');
+import * as Web3 from 'web3';
+import {constants} from './constants';
+
+export const web3Factory = {
+ create(): Web3 {
+ const provider = new ProviderEngine();
+ const rpcUrl = `http://${constants.RPC_HOST}:${constants.RPC_PORT}`;
+ provider.addProvider(new RpcSubprovider({
+ rpcUrl,
+ }));
+ provider.start();
+ const web3 = new Web3();
+ web3.setProvider(provider);
+ return web3;
+ },
+};