import { Logger } from 'pino' ; import { OpenChannel , unreachable } from '@statechannels/wallet-core' ; import _ from 'lodash' ; import { Transaction } from 'objection' ; import { Store } from '../engine/store' ; import { WalletObjective } from '../models/objective' ; import { EngineResponse } from '../engine/engine-response' ; import { Channel } from '../models/channel' ; import { Nothing , Cranker } from '../objectives/objective-manager' ; import { DirectFunder } from './direct-funder' ; import { LedgerFunder } from './ledger-funder' ; export enum WaitingFor { theirPreFundSetup = 'ChannelOpener.theirPreFundSetup' , theirPostFundState = 'ChannelOpener.theirPostFundSetup' , funding = 'ChannelOpener.funding' } export class ChannelOpener implements Cranker < WalletObjective < OpenChannel > > { constructor ( private store , private logger , private timingMetrics = false ) { } public static create ( store , logger , timingMetrics = false ) { return new ChannelOpener ( store , logger , timingMetrics ) ; } public async crank ( objective , response , tx ) { const channelToLock = objective . data . targetChannelId ; const channel = await this . store . getAndLockChannel ( channelToLock , tx ) ; response . queueChannel ( channel ) ; if ( ! channel . prefundSigned ) { await this . signPrefundSetup ( objective . objectiveId , channel , response , tx ) ; } if ( ! channel . preFundComplete ) return WaitingFor . theirPreFundSetup ; const funded = await this . crankChannelFunder ( objective , channel , response , tx ) ; if ( ! funded ) return WaitingFor . funding ; if ( funded && ! channel . postfundSigned ) { await this . signPostfundSetup ( objective . objectiveId , channel , response , tx ) ; } if ( ! channel . postFundComplete ) return WaitingFor . theirPostFundState ; objective = await this . store . markObjectiveStatus ( objective , 'succeeded' , tx ) ; response . queueSucceededObjective ( objective ) ; return Nothing . ToWaitFor ; } private async crankChannelFunder ( objective , channel , response , tx ) { const strategy = objective . data . fundingStrategy ; switch ( strategy ) { case 'Direct' : return this . directFunder . crank ( objective , channel , response , tx ) ; case 'Fake' : return true ; case 'Ledger' : return this . ledgerFunder . crank ( objective , channel , response , tx ) ; case 'Virtual' : return false ; case 'Unknown' : return false ; default : unreachable ( strategy ) ; } } private get directFunder ( ) : DirectFunder { return DirectFunder . create ( this . store , this . logger , this . timingMetrics ) ; } private get ledgerFunder ( ) : LedgerFunder { return LedgerFunder . create ( this . store , this . logger , this . timingMetrics ) ; } private async signPrefundSetup ( objectiveId , channel , response , tx ) { const prefund = { ... channel . latest , turnNum : 0 } ; const signedState = await this . store . signState ( channel , prefund , tx ) ; response . queueState ( signedState , channel . myIndex , channel . channelId ) ; response . queueChannel ( channel ) ; } private async signPostfundSetup ( objectiveId , channel , response , tx ) { const postfund = { ... channel . latest , turnNum : channel . nParticipants * 2 - 1 } ; const signedState = await this . store . signState ( channel , postfund , tx ) ; response . queueState ( signedState , channel . myIndex , channel . channelId ) ; response . queueChannel ( channel ) ; } }