Skip to content

Commit

Permalink
feat(state channels): add round method (#763)
Browse files Browse the repository at this point in the history
  • Loading branch information
mpowaga authored and nduchak committed Nov 6, 2019
1 parent 18ec371 commit d31a51e
Show file tree
Hide file tree
Showing 3 changed files with 83 additions and 5 deletions.
30 changes: 29 additions & 1 deletion es/channel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,13 @@

import AsyncInit from '../utils/async-init'
import { snakeToPascal } from '../utils/string'
import { buildTx } from '../tx/builder'
import { buildTx, unpackTx } from '../tx/builder'
import { TX_TYPE } from '../tx/builder/schema'
import * as handlers from './handlers'
import {
eventEmitters,
status as channelStatus,
state as channelState,
initialize,
enqueueAction,
send,
Expand Down Expand Up @@ -100,6 +101,32 @@ async function state () {
return snakeToPascalObjKeys(await call(this, 'channels.get.offchain_state', {}))
}

/**
* Get current round
*
* If round cannot be determined (for example when channel has not been opened)
* it will return `null`.
*
* @return {Number}
*/
function round () {
const state = channelState.get(this)
if (!state) {
return null
}
const { txType, tx } = unpackTx(channelState.get(this)).tx.encodedTx
switch (txType) {
case TX_TYPE.channelCreate:
return 1
case TX_TYPE.channelOffChain:
case TX_TYPE.channelWithdraw:
case TX_TYPE.channelDeposit:
return parseInt(tx.round, 10)
default:
return null
}
}

/**
* Get channel id
*
Expand Down Expand Up @@ -754,6 +781,7 @@ const Channel = AsyncInit.compose({
off,
status,
state,
round,
id,
update,
poi,
Expand Down
3 changes: 3 additions & 0 deletions es/channel/internal.js
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,9 @@ async function initialize (channel, channelOptions) {
changeStatus(channel, 'connected')
if (params.reconnectTx) {
enterState(channel, { handler: channelOpen })
setTimeout(async () =>
changeState(channel, (await call(channel, 'channels.get.offchain_state', {})).signed_tx)
, 0)
}
ping(channel)
},
Expand Down
55 changes: 51 additions & 4 deletions test/integration/channel.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ function waitForChannel (channel) {
)
}

describe('Channel', function () {
describe.only('Channel', function () {
configure(this)
this.timeout(120000)

Expand All @@ -52,6 +52,7 @@ describe('Channel', function () {
let initiatorCh
let responderCh
let responderShouldRejectUpdate
let existingChannelRound
let existingChannelId
let offchainTx
let contractAddress
Expand Down Expand Up @@ -115,6 +116,8 @@ describe('Channel', function () {
sign: responderSign
})
await Promise.all([waitForChannel(initiatorCh), waitForChannel(responderCh)])
initiatorCh.round().should.equal(1)
responderCh.round().should.equal(1)
sinon.assert.calledOnce(initiatorSign)
sinon.assert.calledWithExactly(initiatorSign, sinon.match('initiator_sign'), sinon.match.string)
sinon.assert.calledOnce(responderSign)
Expand All @@ -139,6 +142,7 @@ describe('Channel', function () {

it('can post update and accept', async () => {
responderShouldRejectUpdate = false
const roundBefore = initiatorCh.round()
const sign = sinon.spy(initiator.signTransaction.bind(initiator))
const amount = 1
const result = await initiatorCh.update(
Expand All @@ -147,6 +151,7 @@ describe('Channel', function () {
amount,
sign
)
initiatorCh.round().should.equal(roundBefore + 1)
result.accepted.should.equal(true)
result.signedTx.should.be.a('string')
sinon.assert.notCalled(initiatorSign)
Expand Down Expand Up @@ -195,13 +200,15 @@ describe('Channel', function () {
responderShouldRejectUpdate = true
const sign = sinon.spy(initiator.signTransaction.bind(initiator))
const amount = 1
const roundBefore = initiatorCh.round()
const result = await initiatorCh.update(
await responder.address(),
await initiator.address(),
amount,
sign
)
result.accepted.should.equal(false)
initiatorCh.round().should.equal(roundBefore)
sinon.assert.notCalled(initiatorSign)
sinon.assert.calledOnce(responderSign)
sinon.assert.calledWithExactly(
Expand Down Expand Up @@ -395,12 +402,14 @@ describe('Channel', function () {
const onOwnWithdrawLocked = sinon.spy()
const onWithdrawLocked = sinon.spy()
responderShouldRejectUpdate = false
const roundBefore = initiatorCh.round()
const result = await initiatorCh.withdraw(
amount,
sign,
{ onOnChainTx, onOwnWithdrawLocked, onWithdrawLocked }
)
result.should.eql({ accepted: true, signedTx: (await initiatorCh.state()).signedTx })
initiatorCh.round().should.equal(roundBefore + 1)
sinon.assert.called(onOnChainTx)
sinon.assert.calledWithExactly(onOnChainTx, sinon.match.string)
sinon.assert.calledOnce(onOwnWithdrawLocked)
Expand Down Expand Up @@ -447,11 +456,13 @@ describe('Channel', function () {
const onOwnWithdrawLocked = sinon.spy()
const onWithdrawLocked = sinon.spy()
responderShouldRejectUpdate = true
const roundBefore = initiatorCh.round()
const result = await initiatorCh.withdraw(
amount,
sign,
{ onOnChainTx, onOwnWithdrawLocked, onWithdrawLocked }
)
initiatorCh.round().should.equal(roundBefore)
result.should.eql({ ...result, accepted: false })
sinon.assert.notCalled(onOnChainTx)
sinon.assert.notCalled(onOwnWithdrawLocked)
Expand Down Expand Up @@ -520,12 +531,14 @@ describe('Channel', function () {
const onOwnDepositLocked = sinon.spy()
const onDepositLocked = sinon.spy()
responderShouldRejectUpdate = false
const roundBefore = initiatorCh.round()
const result = await initiatorCh.deposit(
amount,
sign,
{ onOnChainTx, onOwnDepositLocked, onDepositLocked }
)
result.should.eql({ accepted: true, signedTx: (await initiatorCh.state()).signedTx })
initiatorCh.round().should.equal(roundBefore + 1)
sinon.assert.called(onOnChainTx)
sinon.assert.calledWithExactly(onOnChainTx, sinon.match.string)
sinon.assert.calledOnce(onOwnDepositLocked)
Expand Down Expand Up @@ -572,11 +585,13 @@ describe('Channel', function () {
const onOwnDepositLocked = sinon.spy()
const onDepositLocked = sinon.spy()
responderShouldRejectUpdate = true
const roundBefore = initiatorCh.round()
const result = await initiatorCh.deposit(
amount,
sign,
{ onOnChainTx, onOwnDepositLocked, onDepositLocked }
)
initiatorCh.round().should.equal(roundBefore)
result.should.eql({ ...result, accepted: false })
sinon.assert.notCalled(onOnChainTx)
sinon.assert.notCalled(onOwnDepositLocked)
Expand Down Expand Up @@ -663,6 +678,14 @@ describe('Channel', function () {
sign: responderSign
})
await Promise.all([waitForChannel(initiatorCh), waitForChannel(responderCh)])
const { accepted } = await initiatorCh.update(
await initiator.address(),
await responder.address(),
100,
tx => initiator.signTransaction(tx)
)
accepted.should.be.true
existingChannelRound = initiatorCh.round()
const result = await initiatorCh.leave()
result.channelId.should.be.a('string')
result.signedTx.should.be.a('string')
Expand All @@ -688,6 +711,7 @@ describe('Channel', function () {
sign: responderSign
})
await Promise.all([waitForChannel(initiatorCh), waitForChannel(responderCh)])
initiatorCh.round().should.equal(existingChannelRound)
sinon.assert.notCalled(initiatorSign)
sinon.assert.notCalled(responderSign)
})
Expand Down Expand Up @@ -832,6 +856,7 @@ describe('Channel', function () {
await Promise.all([waitForChannel(initiatorCh), waitForChannel(responderCh)])
const code = await initiator.compileContractAPI(identityContract, { backend: 'aevm' })
const callData = await initiator.contractEncodeCallDataAPI(identityContract, 'init', [], { backend: 'aevm' })
const roundBefore = initiatorCh.round()
const result = await initiatorCh.createContract({
code,
callData,
Expand All @@ -840,6 +865,7 @@ describe('Channel', function () {
abiVersion: 1
}, async (tx) => initiator.signTransaction(tx))
result.should.eql({ accepted: true, address: result.address, signedTx: (await initiatorCh.state()).signedTx })
initiatorCh.round().should.equal(roundBefore + 1)
contractAddress = result.address
contractEncodeCall = (method, args) => initiator.contractEncodeCallDataAPI(identityContract, method, args, { backend: 'aevm' })
})
Expand All @@ -848,13 +874,15 @@ describe('Channel', function () {
responderShouldRejectUpdate = true
const code = await initiator.compileContractAPI(identityContract, { backend: 'aevm' })
const callData = await initiator.contractEncodeCallDataAPI(identityContract, 'init', [], { backend: 'aevm' })
const roundBefore = initiatorCh.round()
const result = await initiatorCh.createContract({
code,
callData,
deposit: BigNumber('10e18'),
vmVersion: 4,
abiVersion: 1
}, async (tx) => initiator.signTransaction(tx))
initiatorCh.round().should.equal(roundBefore)
result.should.eql({ ...result, accepted: false })
})

Expand Down Expand Up @@ -891,24 +919,28 @@ describe('Channel', function () {
})

it('can call a contract and accept', async () => {
const roundBefore = initiatorCh.round()
const result = await initiatorCh.callContract({
amount: 0,
callData: await contractEncodeCall('main', ['42']),
contract: contractAddress,
abiVersion: 1
}, async (tx) => initiator.signTransaction(tx))
result.should.eql({ accepted: true, signedTx: (await initiatorCh.state()).signedTx })
callerNonce = Number(unpackTx((await initiatorCh.state()).signedTx).tx.encodedTx.tx.round)
initiatorCh.round().should.equal(roundBefore + 1)
callerNonce = initiatorCh.round()
})

it('can call a contract and reject', async () => {
responderShouldRejectUpdate = true
const roundBefore = initiatorCh.round()
const result = await initiatorCh.callContract({
amount: 0,
callData: await contractEncodeCall('main', ['42']),
contract: contractAddress,
abiVersion: 1
}, async (tx) => initiator.signTransaction(tx))
initiatorCh.round().should.equal(roundBefore)
result.should.eql({ ...result, accepted: false })
})

Expand Down Expand Up @@ -1032,8 +1064,15 @@ describe('Channel', function () {
sign: responderSign
})
await Promise.all([waitForChannel(initiatorCh), waitForChannel(responderCh)])
const result = await initiatorCh.update(
await initiator.address(),
await responder.address(),
100,
tx => initiator.signTransaction(tx)
)
result.accepted.should.be.true
const channelId = await initiatorCh.id()
const round = Number(unpackTx((await initiatorCh.state()).signedTx).tx.encodedTx.tx.nonce)
const round = initiatorCh.round()
initiatorCh.disconnect()
const ch = await Channel.reconnect({
...sharedParams,
Expand All @@ -1046,7 +1085,15 @@ describe('Channel', function () {
role: 'initiator',
pubkey: await initiator.address()
})
await waitForChannel(ch)
await new Promise((resolve) => {
const checkRound = () => {
ch.round().should.equal(round)
// TODO: enable line below
// ch.off('stateChanged', checkRound)
resolve()
}
ch.on('stateChanged', checkRound)
})
ch.state().should.eventually.be.fulfilled
})

Expand Down

0 comments on commit d31a51e

Please sign in to comment.