Skip to content

Commit

Permalink
some minor improvements on code readability + integration tests over …
Browse files Browse the repository at this point in the history
…ledger implementation exceptions.
  • Loading branch information
vekexasia committed Mar 13, 2018
1 parent c778814 commit c046ab1
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 25 deletions.
26 changes: 13 additions & 13 deletions src/library.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ export class DposLedger {
public async getPubKey(account: LedgerAccount): Promise<{publicKey: string, address: string}> {
const pathBuf = account.derivePath();
const resp = await this.exchange([
'04',
0x04,
(pathBuf.length / 4),
pathBuf,
]);
Expand Down Expand Up @@ -85,7 +85,7 @@ export class DposLedger {
* ```
*/
public signTX(account: LedgerAccount, buff: Buffer, hasRequesterPKey: boolean = false) {
return this.sign('05', account, buff, hasRequesterPKey);
return this.sign(0x05, account, buff, hasRequesterPKey);
}

/**
Expand All @@ -107,7 +107,7 @@ export class DposLedger {
*/
public async signMSG(account: LedgerAccount, what: string | Buffer) {
const buffer: Buffer = typeof(what) === 'string' ? new Buffer(what, 'utf8') : what;
const signature = await this.sign('06', account, buffer);
const signature = await this.sign(0x06, account, buffer);
return Buffer.concat([signature, buffer]);
}

Expand All @@ -124,7 +124,7 @@ export class DposLedger {
* ```
*/
public async version(): Promise<string> {
const [res] = await this.exchange('09');
const [res] = await this.exchange(0x09);
return res.toString('utf8');
}

Expand All @@ -133,8 +133,8 @@ export class DposLedger {
* @returns {Promise<void>}
*/
public async ping(): Promise<void> {
const [res] = await this.exchange('08');
if (res.toString('utf8') !== 'PONG') {
const [res] = await this.exchange(0x08);
if (res.toString('ascii') !== 'PONG') {
throw new Error('Didnt receive PONG');
}
}
Expand All @@ -144,21 +144,21 @@ export class DposLedger {
* @param {string | Buffer} hexData
* @returns {Promise<Buffer[]>} Raw response buffers.
*/
public async exchange(hexData: string | Buffer | Array<(string | Buffer | number)>): Promise<Buffer[]> {
public async exchange(hexData: string | Buffer | number | Array<(string | Buffer | number)>): Promise<Buffer[]> {
let inputBuffer: Buffer;
if (Array.isArray(hexData)) {
inputBuffer = Buffer.concat(hexData.map((item) => {
if (typeof(item) === 'string') {
return new Buffer(item, 'hex');
} else if (typeof(item) === 'number') {
const b = new Buffer(1);
b.writeUInt8(item, 0);
return b;
return Buffer.alloc(1).fill(item);
}
return item;
}));
} else if (typeof(hexData) === 'string') {
inputBuffer = new Buffer(hexData, 'hex');
} else if (typeof(hexData) === 'number') {
inputBuffer = Buffer.alloc(1).fill(hexData);
} else {
inputBuffer = hexData;
}
Expand Down Expand Up @@ -203,14 +203,14 @@ export class DposLedger {

/**
* Raw sign protocol utility. It will handle signature of both msg and txs.
* @param {string} signType type of signature. 05 for txs, 06 for messages.
* @param {number} signType type of signature. 0x05 for txs, 0x06 for messages.
* @param {LedgerAccount} account account
* @param {Buffer} buff buffer to sign
* @param {boolean} hasRequesterPKey if it has a requesterpublickey (used only in tx signing mode)
* @returns {Promise<Buffer>} the signature
*/
private async sign(
signType: string,
signType: number,
account: LedgerAccount,
buff: Buffer,
hasRequesterPKey: boolean = false): Promise<Buffer> {
Expand All @@ -225,7 +225,7 @@ export class DposLedger {
pathBuf,
// headers
buffLength,
hasRequesterPKey ? '01' : '00',
hasRequesterPKey ? 0x01 : 0x00,
// data
buff,
]);
Expand Down
55 changes: 55 additions & 0 deletions tests/integration/library.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import * as sodium from 'libsodium-wrappers';
import * as chaiAsPromised from 'chai-as-promised';
import * as chai from 'chai';
import { expect } from 'chai';
import {
BaseTx,
Expand All @@ -17,6 +19,8 @@ import TransportNodeHid from '@ledgerhq/hw-transport-node-hid';
import { isBrowser, isNode } from 'browser-or-node';
import { ITransport } from '../../src/ledger';

chai.use(chaiAsPromised);

describe('Integration tests', function () {
this.timeout(150222200);
let dl: DposLedger;
Expand Down Expand Up @@ -408,4 +412,55 @@ describe('Integration tests', function () {
expect(await dl.version()).to.be.eq('1.0.0');
});

describe('comm_errors', () => {
// try to send 10KB+1 Bytes
it('should fail if we exceed 10KB', () => {
const buffer = Buffer.alloc(10*1024 + 1).fill('a');
return expect(dl.signMSG(account, buffer)).to.rejectedWith('6a84');
});
it('should fail if we exceed data size during comm', async () => {
const startBuff = Buffer.alloc(2);
startBuff.writeUInt16BE(10, 0); // 10 Bytes
await transport.send(0xe0, 89, 0, 0, startBuff);

// first command 9bytes + 1bytes
await transport.send(0xe0, 90, 0, 0, Buffer.alloc(9));
await transport.send(0xe0, 90, 0, 0, Buffer.alloc(1));

// here it should fail
return expect(transport.send(0xe0, 90, 0, 0, Buffer.alloc(1)))
.to.be.rejectedWith('6700'); // Incorrect length
});
it('should fail if we start comm without having started', async () => {
// first command 9bytes + 1bytes
return expect(transport.send(0xe0, 90, 0, 0, Buffer.alloc(9)))
.to.be.rejectedWith('9802'); // 'CODE_NOT_INITIALIZED'

// NOTE: this expects that there are no pending open comm from other tests.
// If this fails suddenly, then it probably means that c implementation has a BUG

});
it('should fail if we close comm without having sent anything', async () => {
return expect(transport.send(0xe0, 91, 0, 0))
.to.be.rejectedWith('6a80'); // Invalid data received
});
it('should fail if we try to sign an unknown tx', async () => {
const tx = new SendTx()
.set('amount', 0)
.set('timestamp', 10)
.set('fee', 100)
.set('recipientId', '123456781230L')
.set('senderPublicKey', pubKey);

tx.type = 11; // unknwon type.

return expect(dl.signTX(account, tx.getBytes())).to.be.rejectedWith('6a80'); // INCORRECT_DATA
});

it('should throw if unknown command', () => {
return expect(dl.exchange(0x11)).to.be.rejectedWith('6a80'); // INCORRECT_DATA
});

});

});
25 changes: 13 additions & 12 deletions tests/unit/library.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ describe('library', () => {
await instance.getPubKey(account);
expect(instanceExchangeStub.calledOnce).is.true;
expect(instanceExchangeStub.firstCall.args[0]).to.be.deep.eq([
'04',
0x04,
account.derivePath().length / 4,
account.derivePath()
]);
Expand Down Expand Up @@ -196,17 +196,17 @@ describe('library', () => {
it('should call instance.exchange with signType 05', async () => {
await instance.signTX(account, Buffer.alloc(2));
expect(instanceExchangeStub.calledOnce).is.true;
expect(instanceExchangeStub.firstCall.args[0][0]).to.be.deep.eq('05');
expect(instanceExchangeStub.firstCall.args[0][0]).to.be.deep.eq(0x05);
});
it('should default hasRequesterPKey to false', async () => {
await instance.signTX(account, Buffer.alloc(2));
expect(instanceExchangeStub.calledOnce).is.true;
expect(instanceExchangeStub.firstCall.args[0][4]).to.be.deep.eq('00');
expect(instanceExchangeStub.firstCall.args[0][4]).to.be.deep.eq(0x0);
});
it('should allow hasRequesterPKey to true', async () => {
await instance.signTX(account, Buffer.alloc(2), true);
expect(instanceExchangeStub.calledOnce).is.true;
expect(instanceExchangeStub.firstCall.args[0][4]).to.be.deep.eq('01');
expect(instanceExchangeStub.firstCall.args[0][4]).to.be.deep.eq(0x01);
});
it('should propagate correct data derived from inputbuffer and account', async () => {
const buff = Buffer.alloc(2);
Expand All @@ -215,11 +215,11 @@ describe('library', () => {
const lengthBuff = Buffer.alloc(2);
lengthBuff.writeUInt16BE(2, 0);
expect(instanceExchangeStub.firstCall.args[0]).to.be.deep.eq([
'05', // sign type
0x05, // sign type
account.derivePath().length / 4,
account.derivePath(),
lengthBuff, // buffer length
'00',
0x00,
buff
]);
});
Expand All @@ -243,12 +243,12 @@ describe('library', () => {
it('should call instance.exchange with signType 06', async () => {
await instance.signMSG(account, Buffer.alloc(2));
expect(instanceExchangeStub.calledOnce).is.true;
expect(instanceExchangeStub.firstCall.args[0][0]).to.be.deep.eq('06');
expect(instanceExchangeStub.firstCall.args[0][0]).to.be.deep.eq(0x06);
});
it('should default hasRequesterPKey to false', async () => {
await instance.signMSG(account, Buffer.alloc(2));
expect(instanceExchangeStub.calledOnce).is.true;
expect(instanceExchangeStub.firstCall.args[0][4]).to.be.deep.eq('00');
expect(instanceExchangeStub.firstCall.args[0][4]).to.be.deep.eq(0x0);
});
it('should propagate correct data derived from inputbuffer and account', async () => {
const buff = Buffer.alloc(2);
Expand All @@ -257,11 +257,11 @@ describe('library', () => {
const lengthBuff = Buffer.alloc(2);
lengthBuff.writeUInt16BE(2, 0);
expect(instanceExchangeStub.firstCall.args[0]).to.be.deep.eq([
'06', // sign type
0x06, // sign type
account.derivePath().length / 4,
account.derivePath(),
lengthBuff, // buffer length
'00',
0x0,
buff,
]);
});
Expand All @@ -272,6 +272,7 @@ describe('library', () => {
.eq(new Buffer('vekexasia rules', 'utf8'));
});
});

describe('ping', () => {
let instanceExchangeStub: SinonStub;
beforeEach(() => {
Expand All @@ -281,7 +282,7 @@ describe('library', () => {
it('should send 08 with exchange', async () => {
await instance.ping();
expect(instanceExchangeStub.calledOnce).is.true;
expect(instanceExchangeStub.firstCall.args[0]).is.eq('08');
expect(instanceExchangeStub.firstCall.args[0]).is.eq(0x08);
});
it('should throw if exchange did not respond with PONG', () => {
instanceExchangeStub.resolves('POOOONG');
Expand All @@ -297,7 +298,7 @@ describe('library', () => {
it('should send 09 with exchange', async () => {
await instance.version();
expect(instanceExchangeStub.calledOnce).is.true;
expect(instanceExchangeStub.firstCall.args[0]).is.eq('09');
expect(instanceExchangeStub.firstCall.args[0]).is.eq(0x09);
});
it('should respond utf8 representation of exchange output', async () => {
expect(await instance.version()).to.be.eq('1.0.0');
Expand Down

0 comments on commit c046ab1

Please sign in to comment.