import { PaymentChannel } from '../../PaymentChannel' ; import ChannelId from '../../ChannelId' ; import * as BigNumber from 'bignumber.js' ; import EnginePostgres from './EnginePostgres' ; import AbstractChannelsDatabase from '../AbstractChannelsDatabase' ; export default class PostgresChannelsDatabase extends AbstractChannelsDatabase < EnginePostgres > { save ( paymentChannel ) { return this . engine . exec ( ( client ) => client . query ( 'INSERT INTO channel("channelId", kind, sender, receiver, value, spent, state, "tokenContract", "settlementPeriod", "settlingUntil") ' + 'VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)' , [ paymentChannel . channelId , this . kind , paymentChannel . sender , paymentChannel . receiver , paymentChannel . value . toString ( ) , paymentChannel . spent . toString ( ) , paymentChannel . state , paymentChannel . tokenContract , paymentChannel . settlementPeriod , paymentChannel . settlingUntil . toString ( ) ] ) ) ; } firstById ( channelId ) { return this . engine . exec ( ( client ) => client . query ( 'SELECT "channelId", kind, sender, receiver, value, spent, state, "tokenContract", "settlementPeriod", "settlingUntil" FROM channel ' + 'WHERE "channelId" = $1 LIMIT 1' , [ channelId . toString ( ) ] ) ) . then ( ( res ) => this . inflatePaymentChannel ( res . rows [ 0 ] ) ) ; } spend ( channelId , spent ) { return this . engine . exec ( ( client ) => client . query ( 'UPDATE channel SET spent = $2 WHERE "channelId" = $1' , [ channelId . toString ( ) , spent . toString ( ) ] ) ) ; } async deposit ( channelId , value ) { return this . engine . exec ( async ( client ) => { const channel = await this . firstById ( channelId ) ; if ( ! channel ) { throw new Error ( 'Channel not found.' ) ; } const newValue = channel . value . add ( value ) ; return client . query ( 'UPDATE channel SET value = $2 WHERE "channelId" = $1' , [ channelId . toString ( ) , newValue . toString ( ) ] ) ; } ) ; } all ( ) { return this . engine . exec ( ( client ) => client . query ( 'SELECT "channelId", kind, sender, receiver, value, spent, state, "tokenContract", "settlementPeriod", "settlingUntil" FROM channel' ) ) . then ( ( res ) => this . inflatePaymentChannels ( res . rows ) ) ; } allOpen ( ) { return this . engine . exec ( client => client . query ( 'SELECT "channelId", kind, sender, receiver, value, spent, state, "tokenContract", "settlementPeriod", "settlingUntil" FROM channel ' + 'WHERE state = 0' ) ) . then ( ( res ) => this . inflatePaymentChannels ( res . rows ) ) . then ( ( chans ) => this . filterByState ( 0 , chans ) ) ; } async findUsable ( sender , receiver , amount ) { const res = await this . engine . exec ( client => client . query ( 'SELECT "channelId", kind, sender, receiver, value, spent, state, "tokenContract", "settlementPeriod", "settlingUntil" FROM channel ' + 'WHERE sender = $1 AND receiver = $2 AND value >= spent + $3 AND state = 0' , [ sender , receiver , amount . toString ( ) ] ) ) ; const channels = await Promise . all ( res . rows . map ( r => this . inflatePaymentChannel ( r ) ) ) ; const nonEmpty = channels . reduce < Array < PaymentChannel > > ( ( acc , chan ) => { if ( chan ) { acc . push ( chan ) ; } return acc ; } , [ ] ) ; if ( nonEmpty . length ) { return this . filterByState ( 0 , nonEmpty ) [ 0 ] ; } else { return null ; } } findBySenderReceiver ( sender , receiver ) { return this . engine . exec ( ( client ) => client . query ( 'SELECT "channelId", kind, sender, receiver, value, spent, state, "tokenContract", "settlementPeriod", "settlingUntil" FROM channel ' + 'WHERE sender = $1 AND receiver = $2' , [ sender , receiver ] ) ) . then ( ( res ) => this . inflatePaymentChannels ( res . rows ) ) ; } findBySenderReceiverChannelId ( sender , receiver , channelId ) { return this . engine . exec ( ( client ) => client . query ( 'SELECT "channelId", kind, sender, receiver, value, spent, state, "tokenContract", "settlementPeriod", "settlingUntil" FROM channel ' + 'WHERE sender = $1 AND receiver = $2 AND "channelId" = $3 LIMIT 1' , [ sender , receiver , channelId . toString ( ) ] ) ) . then ( ( res ) => this . inflatePaymentChannel ( res . rows [ 0 ] ) ) ; } updateState ( channelId , state ) { return this . engine . exec ( ( client ) => client . query ( 'UPDATE channel SET state = $1 WHERE "channelId" = $2' , [ state , channelId . toString ( ) ] ) ) ; } updateSettlingUntil ( channelId , settlingUntil ) { return this . engine . exec ( ( client ) => client . query ( 'UPDATE channel SET "settlingUntil" = $1 WHERE "channelId" = $2' , [ settlingUntil . toString ( ) , channelId . toString ( ) ] ) ) ; } }