Skip to content

Commit

Permalink
Added missing stubs to container.
Browse files Browse the repository at this point in the history
Init modules.blocks.verify tests.
  • Loading branch information
vekexasia committed Feb 13, 2018
1 parent dea9512 commit 7fe7338
Show file tree
Hide file tree
Showing 3 changed files with 292 additions and 1 deletion.
236 changes: 236 additions & 0 deletions tests/unit/modules/blocks/verify.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,236 @@
import * as chai from 'chai';
import { expect } from 'chai';
import * as chaiAsPromised from 'chai-as-promised';
import * as sinon from 'sinon';
import { Container } from 'inversify';
import { IBlocksModuleVerify } from '../../../../src/ioc/interfaces/modules';
import { Symbols } from '../../../../src/ioc/symbols';
import { BlocksModuleChain, BlocksModuleVerify } from '../../../../src/modules/blocks/';
import { createContainer } from '../../../utils/containerCreator';
import DbStub from '../../../stubs/helpers/DbStub';
import BlocksModuleStub from '../../../stubs/modules/BlocksModuleStub';
import { BlockLogicStub } from '../../../stubs/logic/BlockLogicStub';
import TransactionLogicStub from '../../../stubs/logic/TransactionLogicStub';
import { DelegatesModuleStub, SlotsStub, TransactionsModuleStub } from '../../../stubs';
import { ForkModuleStub } from '../../../stubs/modules/ForkModuleStub';
import AccountsModuleStub from '../../../stubs/modules/AccountsModuleStub';
import BlockRewardLogicStub from '../../../stubs/logic/BlockRewardLogicStub';
import { BlockLogic, BlockRewardLogic, SignedBlockType } from '../../../../src/logic';
import { createFakeBlock } from '../../../utils/blockCrafter';
import { Ed, Slots } from '../../../../src/helpers';
import { createRandomTransactions } from '../../../utils/txCrafter';

chai.use(chaiAsPromised);
describe('modules/blocks/verify', () => {
let inst: IBlocksModuleVerify;
let instReal: BlocksModuleVerify;
let container: Container;
beforeEach(() => {
container = createContainer();
container.rebind(Symbols.modules.blocksSubModules.verify).to(BlocksModuleVerify);

inst = instReal = container.get(Symbols.modules.blocksSubModules.verify);
});

let blocksModule: BlocksModuleStub;
let blocksChain: BlocksModuleChain;
let delegatesModule: DelegatesModuleStub;
let forkModule: ForkModuleStub;
let txModule: TransactionsModuleStub;
let accountsModule: AccountsModuleStub;

let dbStub: DbStub;

let slots: SlotsStub;

let blockLogic: BlockLogicStub;
let blockRewardLogic: BlockRewardLogicStub;
let txLogic: TransactionLogicStub;
beforeEach(() => {
blocksModule = container.get(Symbols.modules.blocks);
blocksChain = container.get(Symbols.modules.blocksSubModules.chain);
delegatesModule = container.get(Symbols.modules.delegates);
forkModule = container.get(Symbols.modules.fork);
txModule = container.get(Symbols.modules.transactions);
accountsModule = container.get(Symbols.modules.accounts);

dbStub = container.get(Symbols.generic.db);

slots = container.get(Symbols.helpers.slots);

blockLogic = container.get(Symbols.logic.block);
blockRewardLogic = container.get(Symbols.logic.blockReward);
txLogic = container.get(Symbols.logic.transaction);
});

describe('onNewBlock', () => {
it('should add blockid to last known block ids [private] (till constants.blockSlotWindow)', async () => {
const constants = container.get<any>(Symbols.helpers.constants);
for (let i = 0; i < constants.blockSlotWindow * 2; i++) {
await instReal.onNewBlock({ id: `${i}` } as any);
}
// all first contants.blockSlotWindow indexes will be removed
expect(instReal['lastNBlockIds']).to.be.deep.eq(new Array(constants.blockSlotWindow)
.fill(null)
.map((a, idx) => `${constants.blockSlotWindow + idx }`));
});
});

describe('onBlockchainReady', () => {
it('should initialize [private].lastNBlockIds with the last blockSlotWindow block ids from db', async () => {
dbStub.enqueueResponse('query', Promise.resolve([{ id: '1' }, { id: '2' }, { id: '3' }]));
await instReal.onBlockchainReady();
expect(instReal['lastNBlockIds']).to.be.deep.eq(['1', '2', '3']);
});
});

describe('cleanup', () => {
it('should return Promise', () => {
expect(instReal.cleanup()).to.be.a.instanceOf(Promise);
});
});

describe('verifyReceipt & verifyBlock', () => {
let block: SignedBlockType;
beforeEach(() => {
const constants = container.get<any>(Symbols.helpers.constants);
block = createFakeBlock({
timestamp : 101 * constants.blockTime,
previousBlock: { id: '1', height: 100 } as any
});
blocksModule.lastBlock = { id: '1', height: 100 } as any;
container.rebind(Symbols.helpers.slots).to(Slots);
container.rebind(Symbols.logic.block).to(BlockLogic);
container.rebind(Symbols.logic.blockReward).to(BlockRewardLogic);
container.rebind(Symbols.helpers.ed).toConstantValue(new Ed());
inst = instReal = container.get(Symbols.modules.blocksSubModules.verify);

// Suppress custom implementations for verifyReceipt & verifyBlock
sinon.stub(inst as any, 'verifyBlockSlotWindow').returns([]);
sinon.stub(inst as any, 'verifyBlockAgainstLastIds').returns([]);
sinon.stub(inst as any, 'verifyForkOne').returns([]);
sinon.stub(inst as any, 'verifyBlockSlot').returns([]);
});
['verifyReceipt', 'verifyBlock'].forEach((what) => {
describe(what, () => {
it('should pass for valid block', async () => {
const res = await inst[what](block);
expect(res.verified).is.true;
expect(res.errors).is.empty;
})
it('error if signature is invalid', async () => {
block.blockSignature = new Array(64).fill(null).map(() => 'aa').join('');
const res = await inst[what](block);
expect(res.errors).to.be.deep.eq(['Failed to verify block signature']);
expect(res.verified).is.false;
});
it('error if previousBlock is not set', async () => {
block.previousBlock = null;
const res = await inst[what](block);
expect(res.errors).to.contain('Invalid previous block');
expect(res.verified).is.false;
});
it('error if version is > 0', async () => {
block.version = 1;
const res = await inst[what](block);
expect(res.errors).to.contain('Invalid block version');
expect(res.verified).is.false;
});
describe('payload check', () => {
it('error if payload length is greater than max allowed (1MB)', async () => {
block.payloadLength = 1024 * 1024 + 1;

const res = await inst[what](block);
expect(res.errors).to.contain('Payload length is too long');
});
it('error if transactions.length is != than block.numberOftransactions', async () => {
block.numberOfTransactions = block.numberOfTransactions+1;

const res = await inst[what](block);
expect(res.errors).to.contain('Included transactions do not match block transactions count');
});
it('error if transactions.length exceeds maxTxsPerBlock', async () => {
block.transactions = new Array(100).fill(null);

const res = await inst[what](block);
expect(res.errors).to.contain('Number of transactions exceeds maximum per block');
});
it('error if duplicate transaction', async () => {
txLogic.stubs.getBytes.returns(Buffer.alloc(10));
const txs = createRandomTransactions({send: 10});
block = createFakeBlock({
previousBlock: { id: '1', height: 100 } as any,
timestamp : block.timestamp,
transactions : txs.concat([txs[0]]),
});
const res = await inst[what](block);
expect(res.errors).to.contain(`Encountered duplicate transaction: ${txs[0].id}`);
});
it('error if tx.getBytes returns error', async () => {
txLogic.stubs.getBytes.returns(Buffer.alloc(10));
txLogic.stubs.getBytes.onCall(1).throws(new Error('meow'));
const txs = createRandomTransactions({send: 10});
block = createFakeBlock({
previousBlock: { id: '1', height: 100 } as any,
timestamp : block.timestamp,
transactions : txs,
});
const res = await inst[what](block);
expect(res.errors).to.contain(`Error: meow`);
});
it('error if computed payload hex is diff from advertised block.payloadHash', async () => {
txLogic.stubs.getBytes.returns(Buffer.alloc(10));
const txs = createRandomTransactions({send: 10});
block = createFakeBlock({
previousBlock: { id: '1', height: 100 } as any,
timestamp : block.timestamp,
transactions : txs,
});
const res = await inst[what](block);
expect(res.errors).to.contain('Invalid payload hash');
});
it('should return error computed totalAmount differs block.totalAmount', async () => {
txLogic.stubs.getBytes.returns(Buffer.alloc(10));
const txs = createRandomTransactions({send: 10});
block = createFakeBlock({
previousBlock: { id: '1', height: 100 } as any,
timestamp : block.timestamp,
transactions : txs,
});
block.totalAmount = block.totalAmount + 1;
const res = await inst[what](block);
expect(res.errors).to.contain('Invalid total amount');
});
it('should return error if computed totalFee differs block.totalFee', async () => {
txLogic.stubs.getBytes.returns(Buffer.alloc(10));
const txs = createRandomTransactions({send: 10});
block = createFakeBlock({
previousBlock: { id: '1', height: 100 } as any,
timestamp : block.timestamp,
transactions : txs,
});
block.totalFee = block.totalFee + 1;
const res = await inst[what](block);
expect(res.errors).to.contain('Invalid total fee');
});
});
});
});
});
describe('verifyReceipt', () => {
it('error if blockslot is in the past of more than blockSlotWindow');
it('error if blockslot is in the future');
it('error if block is already known between lastBlocks');
});

describe('verifyBlock', () => {
it('error if block is in fork 1');
it('error if slotNumber is in the future');
it('error if slotNumber is before lastBlock slot');
});

describe('processBlock', () => {

});

});
50 changes: 50 additions & 0 deletions tests/utils/blockCrafter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import * as crypto from 'crypto';
import * as uuid from 'uuid';
import { Ed, IKeypair } from '../../src/helpers';
import { IBlockLogic } from '../../src/ioc/interfaces/logic';
import { Symbols } from '../../src/ioc/symbols';
import {
BlockLogic,
BlockRewardLogic,
SignedAndChainedBlockType,
SignedBlockType,
TransactionLogic
} from '../../src/logic';
import { IBaseTransaction, SendTransaction, VoteTransaction } from '../../src/logic/transactions';
import { createContainer } from './containerCreator';

const fakeContainer = createContainer();
fakeContainer.rebind(Symbols.helpers.ed).toConstantValue(new Ed());
fakeContainer.rebind(Symbols.logic.blockReward).to(BlockRewardLogic).inSingletonScope();
fakeContainer.rebind(Symbols.logic.block).to(BlockLogic).inSingletonScope();
fakeContainer.rebind(Symbols.logic.transaction).to(TransactionLogic).inSingletonScope();
fakeContainer.bind(Symbols.logic.transactions.send).to(SendTransaction).inSingletonScope();
fakeContainer.bind(Symbols.logic.transactions.vote).to(VoteTransaction).inSingletonScope();

const txLogic: TransactionLogic = fakeContainer.get(Symbols.logic.transaction);
txLogic.attachAssetType(fakeContainer.get(Symbols.logic.transactions.send));
txLogic.attachAssetType(fakeContainer.get(Symbols.logic.transactions.vote));
/**
* Creates a fake "but valid" block
*/
export const createFakeBlock = (cfg: {
timestamp?: number,
keypair?: IKeypair,
transactions?: Array<IBaseTransaction<any>>,
previousBlock?: SignedAndChainedBlockType
} = {}): SignedBlockType => {
const blockLogic: IBlockLogic = fakeContainer.get(Symbols.logic.block);
const ed: Ed = fakeContainer.get(Symbols.helpers.ed);
const keypair = cfg.keypair || ed.makeKeypair(crypto
.createHash('sha256').update(uuid.v4(), 'utf8')
.digest());
const timestamp = cfg.timestamp || 0;
const transactions = cfg.transactions || [];
const previousBlock: any = cfg.previousBlock || { id: '1', height: 1 };
return blockLogic.create({
keypair,
previousBlock,
timestamp,
transactions,
});
}
7 changes: 6 additions & 1 deletion tests/utils/containerCreator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { constants } from '../../src/helpers';
import { Symbols } from '../../src/ioc/symbols';
import {
BlocksSubmoduleChainStub, BlocksSubmoduleVerifyStub,
BusStub, DelegatesModuleStub,
BusStub, DelegatesModuleStub, ExceptionsManagerStub,
LoggerStub,
PeersLogicStub,
SystemModuleStub,
Expand All @@ -26,6 +26,8 @@ import { AppStateStub } from '../stubs/logic/AppStateStub';
import RoundsLogicStub from '../stubs/logic/RoundsLogicStub';
import { ForkModuleStub } from '../stubs/modules/ForkModuleStub';
import { BlocksSubmoduleProcessStub } from '../stubs/modules/blocks/BlocksSubmoduleProcessStub';
import BlockRewardLogicStub from '../stubs/logic/BlockRewardLogicStub';
import AccountLogicStub from '../stubs/logic/AccountLogicStub';

export const createContainer = (): Container => {
const container = new Container();
Expand All @@ -38,6 +40,7 @@ export const createContainer = (): Container => {
container.bind(Symbols.helpers.constants).toConstantValue({...{}, ...constants});
container.bind(Symbols.helpers.bus).to(BusStub).inSingletonScope();
container.bind(Symbols.helpers.ed).to(EdStub).inSingletonScope();
container.bind(Symbols.helpers.exceptionsManager).to(ExceptionsManagerStub).inSingletonScope();
container.bind(Symbols.helpers.logger).to(LoggerStub).inSingletonScope();
container.bind(Symbols.helpers.sequence).to(SequenceStub).inSingletonScope().whenTargetTagged(
Symbols.helpers.sequence,
Expand All @@ -54,8 +57,10 @@ export const createContainer = (): Container => {
container.bind(Symbols.helpers.slots).to(SlotsStub).inSingletonScope();

// LOGIC
container.bind(Symbols.logic.account).to(AccountLogicStub).inSingletonScope();
container.bind(Symbols.logic.appState).to(AppStateStub).inSingletonScope();
container.bind(Symbols.logic.block).to(BlockLogicStub).inSingletonScope();
container.bind(Symbols.logic.blockReward).to(BlockRewardLogicStub).inSingletonScope();
container.bind(Symbols.logic.peers).to(PeersLogicStub).inSingletonScope();
container.bind(Symbols.logic.transaction).to(TransactionLogicStub).inSingletonScope();
container.bind(Symbols.logic.rounds).to(RoundsLogicStub).inSingletonScope();
Expand Down

0 comments on commit 7fe7338

Please sign in to comment.