Skip to content

Commit 15f2c03

Browse files
author
Robert Kiel
committed
✨keep track which party initiated the closing request
1 parent d396f72 commit 15f2c03

File tree

3 files changed

+97
-89
lines changed

3 files changed

+97
-89
lines changed

src/paymentChannels/closeChannels.js

Lines changed: 50 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ const pull = require('pull-stream')
44
const lp = require('pull-length-prefixed')
55
const paramap = require('pull-paramap')
66

7-
87
const { waterfall } = require('neo-async')
98
const { pubKeyToPeerId, pubKeyToEthereumAddress, log } = require('../utils')
109
const BN = require('bn.js')
@@ -19,59 +18,66 @@ module.exports = (self) => (cb) => pull(
1918
const channelId = data.key.slice(17)
2019
const { tx, restoreTx, index } = data.value
2120

22-
self.contract.methods.channels(channelId).call({
23-
from: pubKeyToEthereumAddress(self.node.peerInfo.id.pubKey.marshal())
24-
}, 'latest', (err, channel) => {
25-
// check whether the channel exists
26-
if (parseInt(channel.state) == 0) {
27-
log(self.node.peerInfo.id, `Found orphaned payment channel ${channelId.toString('hex')} inside database. Was the node shut down inappropriately?`)
28-
return self.deleteChannel(channelId, cb)
29-
}
3021

31-
if (index.compare(tx.index) === 1) { // index > tx.index ?
32-
waterfall([
33-
(cb) => pubKeyToPeerId(restoreTx.counterparty, cb),
34-
(peerId, cb) => self.node.peerRouting.findPeer(peerId, cb),
35-
(peerInfo, cb) => self.node.dialProtocol(peerInfo, c.PROTOCOL_SETTLE_CHANNEL, cb),
36-
], (err, conn) => {
37-
if (err)
38-
throw err
22+
waterfall([
23+
(cb) => self.contract.methods.channels(channelId).call({
24+
from: pubKeyToEthereumAddress(self.node.peerInfo.id.pubKey.marshal())
25+
}, 'latest', cb),
26+
(channel, cb) => {
27+
if (parseInt(channel.state) == 0) {
28+
log(self.node.peerInfo.id, `Found orphaned payment channel ${channelId.toString('hex')} inside database. Was the node shut down inappropriately?`)
29+
return self.deleteChannel(channelId, cb)
30+
}
31+
32+
if (index.compare(tx.index) === 1) { // index > tx.index ?
33+
// Ask counterparty to settle payment channel because
34+
// last payment went to that party which means that we
35+
// have only one signature of the last transaction.
36+
waterfall([
37+
(cb) => pubKeyToPeerId(restoreTx.counterparty, cb),
38+
(peerId, cb) => self.node.peerRouting.findPeer(peerId, cb),
39+
(peerInfo, cb) => self.node.dialProtocol(peerInfo, c.PROTOCOL_SETTLE_CHANNEL, cb),
40+
(conn, cb) => {
41+
const now = Date.now()
3942

40-
const now = Date.now()
43+
// TODO: Implement proper transaction handling
44+
const timeout = setTimeout(self.requestClose, SETTLEMENT_TIMEOUT, channelId, true)
4145

42-
// TODO: Implement proper transaction handling
43-
const timeout = setTimeout(self.requestClose, SETTLEMENT_TIMEOUT, channelId, true)
46+
self.contract.once('ClosedChannel', {
47+
topics: [`0x${channelId.toString('hex')}`]
48+
}, (err) => {
49+
if (err)
50+
throw err
4451

45-
self.contract.once('ClosedChannel', {
46-
topics: [`0x${channelId.toString('hex')}`]
47-
}, (err) => {
48-
if (err)
49-
throw err
52+
if (Date.now() - now < SETTLEMENT_TIMEOUT) {
53+
// Prevent node from settling channel itself with a probably
54+
// outdated transaction
55+
clearTimeout(timeout)
56+
}
57+
})
5058

51-
if (Date.now() - now < SETTLEMENT_TIMEOUT) {
52-
// Prevent node from settling channel itself with a probably
53-
// outdated transaction
54-
clearTimeout(timeout)
59+
pull(
60+
pull.once(channelId),
61+
lp.encode(),
62+
conn
63+
)
5564
}
65+
], (err) => {
66+
if(err)
67+
console.log(err)
5668
})
69+
} else {
70+
self.requestClose(channelId)
71+
}
5772

58-
pull(
59-
pull.once(channelId),
60-
lp.encode(),
61-
conn
62-
)
73+
self.once(`closed ${channelId.toString('base64')}`, (receivedMoney) => {
74+
// Callback just when the channel is settled, i.e. the closing listener
75+
// emits the 'closed <channelId>' event.
76+
77+
cb(null, receivedMoney)
6378
})
64-
} else {
65-
self.requestClose(channelId)
6679
}
67-
68-
self.on(`closed ${channelId.toString('base64')}`, (receivedMoney) => {
69-
// Callback just when the channel is settled, i.e. the closing listener
70-
// emits the 'closed <channelId>' event.
71-
72-
cb(null, receivedMoney)
73-
})
74-
})
80+
], cb)
7581
}),
7682
pull.collect((err, values) => {
7783
if (err)

src/paymentChannels/index.js

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ const closeChannels = require('./closeChannels')
2222
const registerHandlers = require('./handlers')
2323

2424
const HASH_LENGTH = 32
25+
const CHANNEL_ID_BYTES = HASH_LENGTH
2526

2627
class PaymentChannel extends EventEmitter {
2728
constructor(options) {
@@ -38,6 +39,8 @@ class PaymentChannel extends EventEmitter {
3839
this.transfer = transfer(this)
3940
this.requestClose = requestClose(this)
4041
this.closeChannels = closeChannels(this)
42+
43+
this.closingRequests = new Set()
4144
}
4245

4346
/**
@@ -101,27 +104,35 @@ class PaymentChannel extends EventEmitter {
101104
}
102105
}
103106

104-
setChannel(newRecord, channelId, cb) {
105-
if (typeof channelId === 'function') {
107+
setChannel(newRecord, options, cb) {
108+
if (typeof options === 'function') {
109+
cb = options
110+
options = {}
111+
}
112+
113+
if (!options.channelId || !Buffer.isBuffer(options.channelId)) {
106114
if (!newRecord.restoreTx)
107115
return cb(Error('Unable to compute channelId.'))
108116

109-
cb = channelId
110-
channelId = newRecord.tx.getChannelId(this.node.peerInfo.id)
117+
options.channelId = newRecord.tx.getChannelId(this.node.peerInfo.id)
111118
}
112119

113-
if (!channelId || !Buffer.isBuffer(channelId) || channelId.length !== 32)
120+
if (!options.channelId || !Buffer.isBuffer(options.channelId) || options.channelId.length !== CHANNEL_ID_BYTES)
114121
return cb(Error('Unable to determine channelId.'))
115122

116-
const key = this.getKey(channelId)
123+
const key = this.getKey(options.channelId)
117124

118-
this.getChannel(channelId, (err, record = {}) => {
119-
if (err)
120-
return cb(err)
125+
this.node.db.get(key, (err, record) => {
126+
if (err && !err.notFound)
127+
return cb()
121128

122-
Object.assign(record, newRecord)
129+
if (err && err.notFound) {
130+
record = {}
131+
} else {
132+
record = this.fromBuffer(record)
133+
}
123134

124-
this.node.db.put(key, this.toBuffer(record), cb)
135+
this.node.db.put(key, this.toBuffer(Object.assign(record, newRecord)), options, cb)
125136
})
126137
}
127138

@@ -152,13 +163,9 @@ class PaymentChannel extends EventEmitter {
152163
const key = this.getKey(channelId)
153164

154165
this.node.db.get(key, (err, record) => {
155-
if (err) {
156-
if (err.notFound)
157-
return cb()
166+
if (err)
167+
return cb(err.notFound ? null : err)
158168

159-
return cb(err)
160-
}
161-
162169
cb(null, this.fromBuffer(record))
163170
})
164171
}

src/paymentChannels/requestClose.js

Lines changed: 23 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
22

33
const { isPartyA, pubKeyToEthereumAddress, log, bufferToNumber } = require('../utils')
4+
const { waterfall } = require('neo-async')
45
const BN = require('bn.js')
56

67
module.exports = (self) => (channelId, useRestoreTx = false, cb = () => { }) => {
@@ -9,40 +10,35 @@ module.exports = (self) => (channelId, useRestoreTx = false, cb = () => { }) =>
910
useRestoreTx = false
1011
}
1112

12-
self.getChannel(channelId, (err, record) => {
13-
if (err)
14-
throw err
13+
waterfall([
14+
(cb) => self.getChannel(channelId, cb),
15+
(record, cb) => {
16+
if (typeof record === 'function')
17+
return cb(null, new BN('0'))
1518

16-
if (!record)
17-
cb(null, new BN('0'))
19+
const lastTx = useRestoreTx ? record.restoreTx : record.tx
1820

19-
let lastTx
20-
if (useRestoreTx) {
21-
lastTx = record.restoreTx
22-
} else {
23-
lastTx = record.tx
24-
}
25-
26-
log(self.node.peerInfo.id, `Trying to close payment channel \x1b[33m${channelId.toString('hex')}\x1b[0m. Nonce is ${self.nonce}`)
21+
log(self.node.peerInfo.id, `Trying to close payment channel \x1b[33m${channelId.toString('hex')}\x1b[0m. Nonce is ${self.nonce}`)
2722

28-
const lastValue = new BN(lastTx.value)
23+
const lastValue = new BN(lastTx.value)
2924

30-
self.contractCall(self.contract.methods.closeChannel(
31-
lastTx.index,
32-
lastTx.nonce,
33-
lastValue.toString(),
34-
lastTx.signature.slice(0, 32),
35-
lastTx.signature.slice(32, 64),
36-
bufferToNumber(lastTx.recovery) + 27
37-
), (err, receipt) => {
38-
if (err)
39-
throw err
25+
self.contractCall(self.contract.methods.closeChannel(
26+
lastTx.index,
27+
lastTx.nonce,
28+
lastValue.toString(),
29+
lastTx.signature.slice(0, 32),
30+
lastTx.signature.slice(32, 64),
31+
bufferToNumber(lastTx.recovery) + 27
32+
), cb)
33+
},
34+
(receipt, cb) => {
35+
self.closingRequests.add(channelId.toString('base64'))
4036

4137
let receivedMoney
4238

4339
const initialValue = new BN(record.restoreTx.value)
4440
if (isPartyA(
45-
pubKeyToEthereumAddress(self.node.peerInfo.id.pubKey.marshal()),
41+
pubKeyToEthereumAddress(self.node.peerInfo.id.pubKey.marshal()),
4642
pubKeyToEthereumAddress(record.restoreTx.counterparty))) {
4743
receivedMoney = lastValue.isub(initialValue)
4844
} else {
@@ -52,7 +48,6 @@ module.exports = (self) => (channelId, useRestoreTx = false, cb = () => { }) =>
5248
log(self.node.peerInfo.id, `Settled channel \x1b[33m${channelId.toString('hex')}\x1b[0m with txHash \x1b[32m${receipt.transactionHash}\x1b[0m. Nonce is now \x1b[31m${self.nonce}\x1b[0m`)
5349

5450
cb(null, receivedMoney)
55-
})
56-
})
57-
51+
}
52+
], cb)
5853
}

0 commit comments

Comments
 (0)