import { BigNumber , ONE , Range } from '@pigi/core-utils' ; import { getOverlappingRange , rangesIntersect } from '@pigi/core-db' ; import { HistoryProof , PluginManager , PredicatePlugin , StateDB , StateManager , StateQuery , StateQueryResult , StateUpdate , Transaction , VerifiedStateUpdate , StateObject , } from '../types' ; import { isValidTransaction , isValidVerifiedStateUpdate , } from '../types/type-guards' ; /**
 * StateManager that validates transactions and wraps and modifies StateDB as necessary.
 *
 * See: http://spec.plasma.group/en/latest/src/05-client-architecture/state-manager.html for more details.
 */ export class DefaultStateManager implements StateManager { public constructor ( private stateDB , private pluginManager ) { } public async executeTransaction ( transaction , inBlock , witness ) { const result = { stateUpdate : undefined , validRanges : [ ] , } ; if ( ! isValidTransaction ( transaction ) ) { throw new Error ( `Cannot execute invalid Transaction: ${ JSON . stringify ( transaction ) }` ) ; } const { start , end } = transaction . range ; const verifiedUpdates = await this . stateDB . getVerifiedStateUpdates ( start , end ) ; for ( const verifiedUpdate of verifiedUpdates ) { this . validateVerifiedStateUpdateForTransaction ( verifiedUpdate , transaction , inBlock ) ; const predicatePlugin = await this . pluginManager . getPlugin ( verifiedUpdate . stateUpdate . stateObject . predicateAddress ) ; const computedState = await predicatePlugin . executeStateTransition ( verifiedUpdate . stateUpdate , transaction , witness ) ; result . validRanges . push ( getOverlappingRange ( transaction . range , verifiedUpdate . range ) ) ; if ( result . stateUpdate === undefined ) { result . stateUpdate = { range : transaction . range , stateObject : computedState , depositAddress : transaction . depositAddress , plasmaBlockNumber : inBlock , } ; } else if ( result . stateUpdate . stateObject !== computedState ) { throw new Error ( `State transition resulted in two different states: ${ JSON . stringify ( result . stateUpdate . stateObject ) } and 
          ${ computedState }. Latter differed from former at range ${ JSON . stringify ( result . validRanges . pop ( ) ) }.` ) ; } } return result ; } public ingestHistoryProof ( historyProof ) { throw Error ( 'DefaultStateManager.ingestHistoryProof is not implemented.' ) ; } public queryState ( query ) { throw Error ( 'DefaultStateManager.queryState is not implemented.' ) ; } param verifiedUpdate param transaction param inBlock throws private validateVerifiedStateUpdateForTransaction ( verifiedUpdate , transaction , inBlock ) { if ( ! isValidVerifiedStateUpdate ( verifiedUpdate ) ) { throw new Error ( `Cannot process transaction for invalid VerifiedStateUpdate: ${ JSON . stringify ( verifiedUpdate ) }` ) ; } if ( ! rangesIntersect ( verifiedUpdate . range , transaction . range ) ) { throw Error ( `VerifiedStateUpdate for range [${ transaction . range . start }, ${ transaction . range . end }) is outside of range: 
        ${ JSON . stringify ( verifiedUpdate . range ) }. VerifiedStateUpdate: ${ JSON . stringify ( verifiedUpdate ) }.` ) ; } if ( ! verifiedUpdate . verifiedBlockNumber . add ( ONE ) . eq ( inBlock ) ) { throw Error ( `VerifiedStateUpdate has block ${ verifiedUpdate . verifiedBlockNumber } and ${ inBlock . sub ( ONE ) } was expected. 
          VerifiedStateUpdate: ${ JSON . stringify ( verifiedUpdate ) }` ) ; } } }