import { should } from '../../setup' ; import { BigNumber , ONE } from '@pigi/core-utils' ; import * as assert from 'assert' ; import { StateUpdate , BlockDB , BlockManager , CommitmentContract , } from '../../../src' ; import { DefaultBlockManager } from '../../../src/app/block-production' ; import { stateUpdatesEqual } from '../../../src/app/utils' ; import { TestUtils } from '../test-utils' ; /*******************
 * Mocks & Helpers *
 *******************/ class DummyCommitmentContract implements CommitmentContract { private throwOnSubmit : boolean = false ; public setThrowOnSubmit ( ) { this . throwOnSubmit = true ; } public async submitBlock ( root ) { if ( this . throwOnSubmit ) { this . throwOnSubmit = false ; throw Error ( 'Simulating error submitting block' ) ; } } } class DummyBlockDB implements BlockDB { private nextBlockNumber : BigNumber = ONE ; private pendingStateUpdates : StateUpdate [ ] = [ ] ; private throwOnFinalize : boolean = false ; public async addPendingStateUpdate ( stateUpdate ) { this . pendingStateUpdates . push ( stateUpdate ) ; } public setThrowOnFinalize ( ) { this . throwOnFinalize = true ; } public async finalizeNextBlock ( ) { if ( this . throwOnFinalize ) { this . throwOnFinalize = false ; throw Error ( 'Simulating error finalizing block' ) ; } this . pendingStateUpdates . length = 0 ; this . nextBlockNumber = this . nextBlockNumber . add ( ONE ) ; } public async getMerkleRoot ( blockNumber ) { return Buffer . from ( 'placeholder' ) ; } public async getNextBlockNumber ( ) { return this . nextBlockNumber ; } public async getPendingStateUpdates ( ) { return this . pendingStateUpdates ; } } const addStateUpdateToBlockManager = async ( blockManager ) : Promise < void > => { const stateUpdate = TestUtils . generateNSequentialStateUpdates ( 1 ) [ 0 ] ; await blockManager . addPendingStateUpdate ( stateUpdate ) ; const returnedUpdates = await blockManager . getPendingStateUpdates ( ) ; assert ( ! ! returnedUpdates && returnedUpdates . length === 1 , `getPendingStateUpdates returned undefined or empty list when expecting a single StateUpdate. returned: ${ JSON . stringify ( returnedUpdates ) }` ) ; } ; /*********
 * TESTS *
 *********/ describe ( 'DefaultBlockManager' , ( ) => { let blockManager ; let blockDB ; let commitmentContract ; beforeEach ( async ( ) => { blockDB = new DummyBlockDB ( ) ; commitmentContract = new DummyCommitmentContract ( ) ; blockManager = new DefaultBlockManager ( blockDB , commitmentContract ) ; } ) ; describe ( 'getNextBlockNumber' , ( ) => { it ( 'is in sync with BlockDB' , async ( ) => { const blockDBNextBlock = await blockDB . getNextBlockNumber ( ) ; const blockManagerNextBlock = await blockManager . getNextBlockNumber ( ) ; assert ( blockManagerNextBlock . eq ( blockDBNextBlock ) , 'BlockDB and BlockManager are out of sync' ) ; } ) ; } ) ; describe ( 'addPendingStateUpdate / getPendingStateUpdates' , ( ) => { it ( 'stores the pending StateUpdate(s)' , async ( ) => { const stateUpdates = TestUtils . generateNSequentialStateUpdates ( 2 ) ; await blockManager . addPendingStateUpdate ( stateUpdates [ 0 ] ) ; let returnedUpdates = await blockManager . getPendingStateUpdates ( ) ; assert ( ! ! returnedUpdates && returnedUpdates . length === 1 , `getPendingStateUpdates returned undefined or empty list when expecting a single StateUpdate. returned: ${ JSON . stringify ( returnedUpdates ) }` ) ; assert ( stateUpdatesEqual ( returnedUpdates [ 0 ] , stateUpdates [ 0 ] ) , 'Added StateUpdate is not the same as returned StateUpdate.' ) ; await blockManager . addPendingStateUpdate ( stateUpdates [ 1 ] ) ; returnedUpdates = await blockManager . getPendingStateUpdates ( ) ; assert ( ! ! returnedUpdates && returnedUpdates . length === 2 , `getPendingStateUpdates returned undefined or empty list when expecting a 2 StateUpdates. returned: ${ JSON . stringify ( returnedUpdates ) }` ) ; assert ( ( stateUpdatesEqual ( returnedUpdates [ 0 ] , stateUpdates [ 0 ] ) || stateUpdatesEqual ( returnedUpdates [ 1 ] , stateUpdates [ 0 ] ) ) && ( stateUpdatesEqual ( returnedUpdates [ 0 ] , stateUpdates [ 1 ] ) || stateUpdatesEqual ( returnedUpdates [ 1 ] , stateUpdates [ 1 ] ) ) , 'Added StateUpdates are not the same as returned StateUpdates.' ) ; } ) ; } ) ; describe ( 'submitNextBlock' , ( ) => { it ( 'increments next block and clears pending StateUpdates' , async ( ) => { const previousNextBlockNumber = await blockManager . getNextBlockNumber ( ) ; await addStateUpdateToBlockManager ( blockManager ) ; await blockManager . submitNextBlock ( ) ; const updatedNextBlockNumber = await blockManager . getNextBlockNumber ( ) ; assert ( updatedNextBlockNumber . eq ( previousNextBlockNumber . add ( ONE ) ) , `Block Number after submitted block is not incremented. Got ${ updatedNextBlockNumber . toString ( ) }, expected ${ previousNextBlockNumber . add ( ONE ) . toString ( ) }` ) ; const returnedUpdates = await blockManager . getPendingStateUpdates ( ) ; assert ( ! ! returnedUpdates && returnedUpdates . length === 0 , `getPendingStateUpdates returned undefined or non-empty list when expecting an empty list. returned: ${ JSON . stringify ( returnedUpdates ) }` ) ; } ) ; it ( 'does not submit block if there are no updates' , async ( ) => { const previousNextBlockNumber = await blockManager . getNextBlockNumber ( ) ; await blockManager . submitNextBlock ( ) ; const updatedNextBlockNumber = await blockManager . getNextBlockNumber ( ) ; assert ( previousNextBlockNumber . eq ( updatedNextBlockNumber ) , 'Block was incremented when submission should not have taken place.' ) ; } ) ; it ( 'throws if block submission fails' , async ( ) => { await addStateUpdateToBlockManager ( blockManager ) ; commitmentContract . setThrowOnSubmit ( ) ; try { await blockManager . submitNextBlock ( ) ; assert ( false , 'This should have thrown.' ) ; } catch ( e ) { } } ) ; it ( 'throws if block finalization fails' , async ( ) => { await addStateUpdateToBlockManager ( blockManager ) ; blockDB . setThrowOnFinalize ( ) ; try { await blockManager . submitNextBlock ( ) ; assert ( false , 'This should have thrown.' ) ; } catch ( e ) { } } ) ; } ) ; } ) ;