import { SignedState , State , SubmitChallenge } from '@statechannels/wallet-core' ; import { Transaction } from 'objection' ; import { Logger } from 'pino' ; import { ChainServiceRequest } from '../models/chain-service-request' ; import { Channel } from '../models/channel' ; import { WalletObjective } from '../models/objective' ; import { Cranker , Nothing } from '../objectives/objective-manager' ; import { Store } from '../engine/store' ; import { EngineResponse } from '../engine/engine-response' ; export const enum WaitingFor { existingChainServiceRequest = 'ChallengeSubmitter.existingChainServiceRequest' , existingChallenge = 'ChallengeSubmitter.existingChallenge' } export class ChallengeSubmitter implements Cranker < WalletObjective < SubmitChallenge > > { constructor ( private store , private logger , private timingMetrics = false ) { } public static create ( store , logger , timingMetrics = false ) { return new ChallengeSubmitter ( store , logger , timingMetrics ) ; } public async crank ( objective , response , tx ) { const { targetChannelId : channelToLock } = objective . data ; const channel = await this . store . getAndLockChannel ( channelToLock , tx ) ; const status = channel . adjudicatorStatus ? channel . adjudicatorStatus . toResult ( ) . channelMode : 'Open' ; if ( status !== 'Open' ) { this . logger . warn ( 'There is an existing challenge or the channel is finalized on chain' ) ; return WaitingFor . existingChallenge ; } if ( ! channel . signingWallet ) { throw new Error ( `No signing wallet fetched for channel ${ channelToLock }` ) ; } const existingRequest = await ChainServiceRequest . query ( tx ) . where ( { channelId : channelToLock , request : 'challenge' } ) . first ( ) ; if ( existingRequest ) { this . logger . warn ( 'There is already an existing request' , existingRequest ) ; return WaitingFor . existingChainServiceRequest ; } await ChainServiceRequest . insertOrUpdate ( channelToLock , 'challenge' , tx ) ; if ( channel . initialSupport . length === 0 ) { this . logger . error ( { channelId : channel . channelId } , 'There is no initial support stored for the channel' ) ; await this . store . markObjectiveStatus ( objective , 'failed' , tx ) ; return Nothing . ToWaitFor ; } response . queueChainRequest ( [ { type : 'Challenge' , challengeStates : channel . initialSupport , privateKey : channel . signingWallet . privateKey , } , ] ) ; objective = await this . store . markObjectiveStatus ( objective , 'succeeded' , tx ) ; response . queueChannel ( channel ) ; response . queueSucceededObjective ( objective ) ; return Nothing . ToWaitFor ; } /**
     * A simple method that only produces a signature for a given state.
     * It does not store the state or validate it in any way.
     */ private async signState ( channel , state , tx ) { const signingWallet = channel . signingWallet || ( await channel . $relatedQuery ( 'signingWallet' , tx ) . first ( ) ) ; if ( ! signingWallet ) { throw new Error ( 'No signing wallets' ) ; } return { ... state , signatures : [ signingWallet . signState ( state ) ] } ; } }