import { simpleEthAllocation , BN , serializeState , serializeAllocation , serializeOutcome , } from '@statechannels/wallet-core' ; import Objection from 'objection' ; import { Channel } from '../../../models/channel' ; import { defaultTestEngineConfig , Engine } from '../..' ; import { seedBobsSigningWallet } from '../../../db/seeds/1_signing_wallet_seeds' ; import { stateWithHashSignedBy } from '../fixtures/states' ; import { bob , alice } from '../fixtures/signing-wallets' ; import { bob as bobP } from '../fixtures/participants' ; import { channel } from '../../../models/__test__/fixtures/channel' ; import { defaultTestWalletConfig } from '../../../config' ; import { DBAdmin } from '../../../db-admin/db-admin' ; import { getChannelResultFor , getSignedStateFor } from '../../../__test__/test-helpers' ; import { ObjectiveModel } from '../../../models/objective' ; import { createLogger } from '../../../logger' ; let w ; beforeEach ( async ( ) => { const logger = createLogger ( defaultTestWalletConfig ( ) ) ; w = await Engine . create ( defaultTestEngineConfig ( ) , logger ) ; await DBAdmin . truncateDataBaseFromKnex ( w . knex ) ; await seedBobsSigningWallet ( w . knex ) ; } ) ; afterEach ( async ( ) => { await w . destroy ( ) ; } ) ; describe ( 'directly funded app' , ( ) => { it ( 'signs multiple prefund setups when joining multiple channels' , async ( ) => { const appData = '0x0f00' ; const preFS = { turnNum : 0 , appData } ; const state1 = { ... preFS , channelNonce : 1 } ; const state2 = { ... preFS , channelNonce : 2 } ; const c1 = channel ( { signingAddress : bob ( ) . address , channelNonce : 1 , vars : [ stateWithHashSignedBy ( [ alice ( ) ] ) ( state1 ) ] , } ) ; await Channel . query ( w . knex ) . insert ( c1 ) ; const c2 = channel ( { signingAddress : bob ( ) . address , channelNonce : 2 , vars : [ stateWithHashSignedBy ( [ alice ( ) ] ) ( state2 ) ] , } ) ; await Channel . query ( w . knex ) . insert ( c2 ) ; const channelIds = [ c1 , c2 ] . map ( c => c . channelId ) ; await ObjectiveModel . insert ( { type : 'OpenChannel' , participants : c1 . participants , data : { targetChannelId : c1 . channelId , fundingStrategy : 'Direct' , role : 'app' , } , } , w . knex ) ; await ObjectiveModel . insert ( { type : 'OpenChannel' , participants : c2 . participants , data : { targetChannelId : c2 . channelId , fundingStrategy : 'Direct' , role : 'app' , } , } , w . knex ) ; const { outbox , channelResults } = await w . joinChannels ( channelIds ) ; expect ( getChannelResultFor ( c1 . channelId , channelResults ) ) . toMatchObject ( { channelId : c1 . channelId , turnNum : 0 , } ) ; expect ( getChannelResultFor ( c2 . channelId , channelResults ) ) . toMatchObject ( { channelId : c2 . channelId , turnNum : 0 , } ) ; expect ( getSignedStateFor ( c1 . channelId , outbox ) ) . toMatchObject ( { ... state1 , turnNum : 0 } ) ; expect ( getSignedStateFor ( c2 . channelId , outbox ) ) . toMatchObject ( { ... state2 , turnNum : 0 } ) ; await Promise . all ( channelIds . map ( async ( c ) => { const updated = await Channel . forId ( c , w . knex ) ; expect ( updated . protocolState ) . toMatchObject ( { latest : { turnNum : 0 } , supported : { turnNum : 0 } , } ) ; } ) ) ; } ) ; it ( 'signs the prefund setup ' , async ( ) => { const appData = '0x0f00' ; const preFS0 = { turnNum : 0 , appData } ; const preFS1 = { turnNum : 0 , appData } ; const c = channel ( { signingAddress : bob ( ) . address , vars : [ stateWithHashSignedBy ( [ alice ( ) ] ) ( preFS0 ) ] , } ) ; await Channel . query ( w . knex ) . insert ( c ) ; const { channelId } = c ; const current = await Channel . forId ( channelId , w . knex ) ; expect ( current . protocolState ) . toMatchObject ( { latest : preFS0 , supported : undefined } ) ; await ObjectiveModel . insert ( { type : 'OpenChannel' , participants : current . participants , data : { targetChannelId : current . channelId , fundingStrategy : 'Direct' , role : 'app' , } , } , w . knex ) ; await expect ( w . joinChannel ( { channelId } ) ) . resolves . toMatchObject ( { outbox : [ { params : { recipient : 'alice' , sender : 'bob' , data : { signedStates : [ preFS1 ] } } } ] , channelResult : { channelId , turnNum : 0 , appData , status : 'opening' } , } ) ; const updated = await Channel . forId ( channelId , w . knex ) ; expect ( updated . protocolState ) . toMatchObject ( { latest : preFS1 , supported : preFS1 } ) ; } ) ; it ( 'signs the prefund setup and makes a deposit, when I am first to deposit in a directly funded app' , async ( ) => { const outcome = simpleEthAllocation ( [ { destination : bobP ( ) . destination , amount : BN . from ( 5 ) } ] ) ; const preFS0 = { turnNum : 0 , outcome } ; const preFS1 = { turnNum : 0 , outcome } ; const c = channel ( { signingAddress : bob ( ) . address , vars : [ stateWithHashSignedBy ( [ alice ( ) ] ) ( preFS0 ) ] , } ) ; await Channel . query ( w . knex ) . insert ( c ) ; const channelId = c . channelId ; const current = await Channel . forId ( channelId , w . knex ) ; expect ( current . latest ) . toMatchObject ( preFS0 ) ; await ObjectiveModel . insert ( { type : 'OpenChannel' , participants : current . participants , data : { targetChannelId : current . channelId , fundingStrategy : 'Direct' , role : 'app' , } , } , w . knex ) ; await expect ( w . joinChannel ( { channelId } ) ) . resolves . toMatchObject ( { outbox : [ { method : 'MessageQueued' , params : { recipient : 'alice' , sender : 'bob' , data : { signedStates : [ { ... preFS1 , outcome : serializeOutcome ( preFS1 . outcome ) } ] } , } , } , ] , channelResult : { channelId , turnNum : 0 , status : 'opening' } , } ) ; const updated = await Channel . forId ( channelId , w . knex ) ; expect ( updated . protocolState ) . toMatchObject ( { latest : preFS1 , supported : preFS1 , chainServiceRequests : [ { request : 'fund' , attempts : 1 , channelId , } , ] , } ) ; } ) ; } ) ; describe ( 'ledger funded app scenarios' , ( ) => { let ledger ; let app ; beforeEach ( async ( ) => { const someNonConflictingChannelNonce = 23364518 ; ledger = await Channel . query ( w . knex ) . insert ( channel ( { signingAddress : bob ( ) . address , channelNonce : someNonConflictingChannelNonce , appDefinition : '0x0000000000000000000000000000000000000000' , vars : [ stateWithHashSignedBy ( [ alice ( ) , bob ( ) ] ) ( { appDefinition : '0x0000000000000000000000000000000000000000' , channelNonce : someNonConflictingChannelNonce , turnNum : 4 , outcome : simpleEthAllocation ( [ { destination : bobP ( ) . destination , amount : BN . from ( 5 ) } ] ) , } ) , ] , } ) ) ; await Channel . setLedger ( ledger . channelId , w . knex ) ; app = channel ( { fundingStrategy : 'Ledger' , fundingLedgerChannelId : ledger . channelId , } ) ; } ) ; const putTestChannelInsideEngine = async ( args ) => { const channel = await Channel . query ( w . knex ) . insert ( args ) ; await ObjectiveModel . insert ( { type : 'OpenChannel' , participants : channel . participants , data : { targetChannelId : channel . channelId , fundingStrategy : 'Ledger' , fundingLedgerChannelId : ledger . channelId , role : 'app' , } , } , w . knex ) ; return channel ; } ; it ( 'countersigns a prefund setup and automatically proposes a ledger update' , async ( ) => { const outcome = simpleEthAllocation ( [ { destination : bobP ( ) . destination , amount : BN . from ( 5 ) } ] ) ; const preFS0 = { turnNum : 0 , outcome } ; const preFS1 = { turnNum : 0 , outcome } ; const { channelId } = await putTestChannelInsideEngine ( { ... app , signingAddress : bob ( ) . address , vars : [ stateWithHashSignedBy ( [ alice ( ) ] ) ( preFS0 ) ] , } ) ; const signedPreFS1 = stateWithHashSignedBy ( [ alice ( ) , bob ( ) ] ) ( preFS1 ) ; await expect ( w . joinChannel ( { channelId } ) ) . resolves . toMatchObject ( { outbox : [ { method : 'MessageQueued' , params : { recipient : 'alice' , sender : 'bob' , data : { signedStates : [ serializeState ( stateWithHashSignedBy ( [ bob ( ) ] ) ( signedPreFS1 ) ) ] , } , } , } , ] , channelResult : { channelId , turnNum : 0 , allocations : serializeAllocation ( outcome ) , status : 'opening' , } , } ) ; const { protocolState } = await Channel . forId ( channelId , w . knex ) ; expect ( protocolState ) . toMatchObject ( { latest : signedPreFS1 , supported : signedPreFS1 , } ) ; } ) ; } ) ;