import { BIG_ENDIAN , BigNumber , MAX_BIG_NUM , ONE , ZERO } from '@pigi/core-utils' ; import { BaseKey , BaseRangeBucket , KeyValueStore , RangeStore , GenericMerkleIntervalTree , } from '@pigi/core-db' ; import { Mutex } from 'async-mutex' ; import { BlockDB , StateUpdate } from '../../types' ; import { deserializeStateUpdate , serializeStateUpdate } from '../serialization' ; const KEYS = { NEXT_BLOCK : Buffer . from ( 'nextblock' ) , BLOCK : new BaseKey ( 'b' , [ 'buffer' ] ) , } ; /**
 * Simple BlockDB implementation.
 */ export class DefaultBlockDB implements BlockDB { private readonly blockMutex : Mutex ; param vars param blocks constructor ( private readonly vars , private readonly blocks ) { this . blockMutex = new Mutex ( ) ; } returns public async getNextBlockNumber ( ) { const buf = await this . vars . get ( KEYS . NEXT_BLOCK ) ; return ! buf ? ONE : new BigNumber ( buf , 'hex' , BIG_ENDIAN ) ; } param stateUpdate returns public async addPendingStateUpdate ( stateUpdate ) { await this . blockMutex . runExclusive ( async ( ) => { const block = await this . getNextBlockStore ( ) ; const start = stateUpdate . range . start ; const end = stateUpdate . range . end ; if ( await block . hasDataInRange ( start , end ) ) { throw new Error ( 'Block already contains a state update over that range.' ) ; } const value = Buffer . from ( serializeStateUpdate ( stateUpdate ) ) ; await block . put ( start , end , value ) ; } ) ; } returns public async getPendingStateUpdates ( ) { const blockNumber = await this . getNextBlockNumber ( ) ; return this . getStateUpdates ( blockNumber ) ; } param blockNumber returns public async getMerkleRoot ( blockNumber ) { const stateUpdates = await this . getStateUpdates ( blockNumber ) ; const leaves = stateUpdates . map ( ( stateUpdate ) => { const encodedStateUpdate = serializeStateUpdate ( stateUpdate ) ; return { start : stateUpdate . range . start , end : stateUpdate . range . end , data : encodedStateUpdate , } ; } ) ; const tree = new GenericMerkleIntervalTree ( leaves ) ; return tree . root ( ) . hash ; } /**
     * Finalizes the next plasma block so that it can be published.
     *
     * Note: The execution of this function is serialized internally,
     * but to be of use, the caller will most likely want to serialize
     * their calls to it as well.
     */ public async finalizeNextBlock ( ) { await this . blockMutex . runExclusive ( async ( ) => { const prevBlockNumber = await this . getNextBlockNumber ( ) ; const nextBlockNumber = prevBlockNumber . add ( ONE ) . toBuffer ( BIG_ENDIAN ) ; await this . vars . put ( KEYS . NEXT_BLOCK , nextBlockNumber ) ; } ) ; } param blockNumber returns private async getBlockStore ( blockNumber ) { const key = KEYS . BLOCK . encode ( [ blockNumber . toBuffer ( BIG_ENDIAN ) ] ) ; const bucket = this . blocks . bucket ( key ) ; return new BaseRangeBucket ( bucket . db , bucket . prefix ) ; } returns private async getNextBlockStore ( ) { const blockNumber = await this . getNextBlockNumber ( ) ; return this . getBlockStore ( blockNumber ) ; } param blockNumber returns private async getStateUpdates ( blockNumber ) { const block = await this . getBlockStore ( blockNumber ) ; const values = await block . get ( ZERO , MAX_BIG_NUM ) ; return values . map ( ( value ) => { return deserializeStateUpdate ( value . value . toString ( ) ) ; } ) ; } }