paid-services 3.4.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.prettierignore +2 -0
- package/CHANGELOG.md +16 -0
- package/capacity/accept_capacity_change.js +2 -0
- package/capacity/ask_for_change_details.js +41 -9
- package/capacity/ask_for_decrease.js +167 -49
- package/capacity/capacity_change_requests.js +9 -0
- package/capacity/change_channel_capacity.js +7 -1
- package/capacity/encode_change_request.js +8 -1
- package/capacity/fee_for_replacement.js +3 -7
- package/capacity/get_capacity_change_requests.js +1 -0
- package/capacity/get_capacity_replacement.js +56 -7
- package/capacity/initiate_capacity_change.js +1 -2
- package/capacity/parse_capacity_change_request.js +8 -1
- package/capacity/propose_capacity_change.js +208 -92
- package/capacity/propose_channel_open.js +54 -0
- package/package.json +9 -9
- package/services/response_for_relay.js +0 -1
- package/test/capacity/test_capacity_change_requests.js +1 -0
- package/test/capacity/test_encode_change_request.js +5 -0
- package/test/capacity/test_parse_capacity_change_request.js +1 -0
- package/test/integration/test_accept_capacity_change.js +3 -4
- package/test/integration/test_change_channel_capacity.js +8 -1
- package/test/integration/test_decrease_channel_capacity.js +23 -8
- package/test/integration/test_increase_channel_capacity.js +4 -1
- package/test/integration/test_split_channel_capacity.js +241 -0
- package/trades/accept_trade.js +12 -3
- package/trades/create_trade.js +82 -50
- package/trades/decode_anchored_trade.js +56 -0
- package/trades/encode_anchored_trade.js +35 -0
- package/trades/finalize_trade_secret.js +7 -0
- package/trades/find_trade.js +28 -3
- package/trades/manage_trade.js +15 -4
- package/trades/manage_trades.js +209 -2
- package/trades/request_trade_by_id.js +7 -1
- package/trades/service_anchored_trades.js +181 -0
- package/trades/service_open_trade.js +172 -0
- package/trades/service_trade_requests.js +106 -35
- package/trades/trade_from_invoice.js +51 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
const EventEmitter = require('events');
|
|
2
2
|
const {randomBytes} = require('crypto');
|
|
3
3
|
|
|
4
|
+
const {getInvoice} = require('ln-service');
|
|
4
5
|
const {subscribeToInvoice} = require('ln-service');
|
|
5
6
|
|
|
6
7
|
const acceptTrade = require('./accept_trade');
|
|
@@ -11,24 +12,37 @@ const {serviceTypeRequestTrades} = require('./../service_types');
|
|
|
11
12
|
const {serviceTypeReceiveTrades} = require('./../service_types');
|
|
12
13
|
|
|
13
14
|
const descRecord = description => ({type: '2', value: description});
|
|
15
|
+
const events = ['details', 'end', 'failure', 'settled', 'trade'];
|
|
14
16
|
const findIdRecord = records => records.find(n => n.type === '0');
|
|
15
17
|
const findRequestIdRecord = records => records.find(n => n.type === '1');
|
|
18
|
+
const hashHexLength = 64;
|
|
16
19
|
const idBackType = '0';
|
|
17
20
|
const idRecord = id => ({type: '1', value: id});
|
|
18
|
-
const
|
|
21
|
+
const msRemainingToTime = time => time - Date.now();
|
|
19
22
|
const postOpenTradeTimeoutMs = 1000 * 30;
|
|
23
|
+
const sumOf = arr => arr.reduce((sum, n) => sum + n, 0);
|
|
20
24
|
const tradeSecretType = '1';
|
|
21
25
|
const utf8AsHex = utf8 => Buffer.from(utf8).toString('hex');
|
|
22
26
|
|
|
23
27
|
/** Service requests for an open trade
|
|
24
28
|
|
|
29
|
+
The maximum expiration date is three weeks
|
|
30
|
+
|
|
25
31
|
{
|
|
26
32
|
description: <Trade Description String>
|
|
33
|
+
expires_at: <Trade Expires At ISO 8601 Date String>
|
|
34
|
+
id: <Trade Id Hex String>
|
|
27
35
|
lnd: <Authenticated LND API Object>
|
|
28
36
|
secret: <Secret Payload String>
|
|
29
37
|
tokens: <Tokens Number>
|
|
30
38
|
}
|
|
31
39
|
|
|
40
|
+
// Return details
|
|
41
|
+
@event 'details'
|
|
42
|
+
{
|
|
43
|
+
to: <Public Key Id Hex String>
|
|
44
|
+
}
|
|
45
|
+
|
|
32
46
|
// Service ended
|
|
33
47
|
@event 'end'
|
|
34
48
|
|
|
@@ -36,12 +50,6 @@ const utf8AsHex = utf8 => Buffer.from(utf8).toString('hex');
|
|
|
36
50
|
@event 'failure'
|
|
37
51
|
[<Failure Code Number>, <Failure Code String>]
|
|
38
52
|
|
|
39
|
-
// Return details
|
|
40
|
-
@event 'details'
|
|
41
|
-
{
|
|
42
|
-
to: <Public Key Id Hex String>
|
|
43
|
-
}
|
|
44
|
-
|
|
45
53
|
// Received payment
|
|
46
54
|
@event 'settled'
|
|
47
55
|
{
|
|
@@ -57,18 +65,66 @@ const utf8AsHex = utf8 => Buffer.from(utf8).toString('hex');
|
|
|
57
65
|
@returns
|
|
58
66
|
<Service Event Emitter Object>
|
|
59
67
|
*/
|
|
60
|
-
module.exports =
|
|
68
|
+
module.exports = args => {
|
|
69
|
+
if (!args.description) {
|
|
70
|
+
throw new Error('ExpectedTradeDescriptionToServiceTradeRequests');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
if (!args.id) {
|
|
74
|
+
throw new Error('ExpectedTradeIdToServiceTradeRequests');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (args.id.length !== hashHexLength) {
|
|
78
|
+
throw new Error('ExpectedShorterRequestIdValueToServiceTradeRequests');
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (!args.lnd) {
|
|
82
|
+
throw new Error('ExpectedAuthenticatedLndToServiceTradeRequests');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (!args.secret) {
|
|
86
|
+
throw new Error('ExpectedSecretToEncryptToServiceTradeRequests');
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (!args.tokens) {
|
|
90
|
+
throw new Error('ExpectedTokensPriceForTradeToServiceTradeRequests');
|
|
91
|
+
}
|
|
92
|
+
|
|
61
93
|
const emitter = new EventEmitter();
|
|
62
94
|
|
|
63
95
|
const holds = [];
|
|
64
|
-
const
|
|
65
|
-
|
|
96
|
+
const service = servicePeerRequests({lnd: args.lnd});
|
|
97
|
+
|
|
98
|
+
// Stop the service when there are no more listeners
|
|
99
|
+
emitter.on('removeListener', () => {
|
|
100
|
+
// Exit early when there are still listeners on an event
|
|
101
|
+
if (!!sumOf(events.map(n => emitter.listenerCount(n)))) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return service.stop({});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
const sub = subscribeToInvoice({id: args.id, lnd: args.lnd});
|
|
109
|
+
|
|
110
|
+
sub.on('invoice_updated', invoice => {
|
|
111
|
+
if (!invoice.is_canceled) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
holds.forEach(n => n.sub.removeAllListeners());
|
|
116
|
+
|
|
117
|
+
return service.stop({});
|
|
118
|
+
});
|
|
66
119
|
|
|
67
120
|
// Notify when the service ends
|
|
68
121
|
service.end(() => emitter.emit('end', {}));
|
|
69
122
|
|
|
70
123
|
// Basic trade description
|
|
71
|
-
const basicTradeRecords = [
|
|
124
|
+
const basicTradeRecords = [
|
|
125
|
+
idRecord(args.id),
|
|
126
|
+
descRecord(utf8AsHex(args.description)),
|
|
127
|
+
];
|
|
72
128
|
|
|
73
129
|
// Wait for a request for the open trade
|
|
74
130
|
service.request({type: serviceTypeRequestTrades}, (req, res) => {
|
|
@@ -76,7 +132,7 @@ module.exports = ({description, lnd, secret, tokens}) => {
|
|
|
76
132
|
const requestTradeId = findIdRecord(req.records);
|
|
77
133
|
|
|
78
134
|
// Exit early when the trade requested is unrelated
|
|
79
|
-
if (!!requestTradeId && requestTradeId.value !== id) {
|
|
135
|
+
if (!!requestTradeId && requestTradeId.value !== args.id) {
|
|
80
136
|
return;
|
|
81
137
|
}
|
|
82
138
|
|
|
@@ -87,6 +143,13 @@ module.exports = ({description, lnd, secret, tokens}) => {
|
|
|
87
143
|
return emitter.emit('failure', [400, 'ExpectedRequestTradeOrRequestId']);
|
|
88
144
|
}
|
|
89
145
|
|
|
146
|
+
// Exit early when the trade is expired
|
|
147
|
+
if (args.expires_at < new Date().toISOString()) {
|
|
148
|
+
service.stop({});
|
|
149
|
+
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
90
153
|
// Exit early and ping peer with trade when this is an open ended query
|
|
91
154
|
if (!requestTradeId) {
|
|
92
155
|
emitter.emit('details', {to: req.from});
|
|
@@ -95,25 +158,31 @@ module.exports = ({description, lnd, secret, tokens}) => {
|
|
|
95
158
|
|
|
96
159
|
const records = [].concat(basicTradeRecords).concat(reqIdRecord);
|
|
97
160
|
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
records,
|
|
102
|
-
timeout: postOpenTradeTimeoutMs,
|
|
103
|
-
to: req.from,
|
|
104
|
-
type: serviceTypeReceiveTrades,
|
|
105
|
-
},
|
|
106
|
-
err => {
|
|
107
|
-
if (!!err) {
|
|
108
|
-
failure([503, 'FailedToDeliverTradeInfo']);
|
|
109
|
-
|
|
110
|
-
return emitter.emit(
|
|
111
|
-
'failure',
|
|
112
|
-
[503, 'FailedToDeliverTradeInfo', {err}]
|
|
113
|
-
);
|
|
161
|
+
return getInvoice({id: args.id, lnd: args.lnd}, (err, res) => {
|
|
162
|
+
if (!!err || !!res.is_canceled) {
|
|
163
|
+
return;
|
|
114
164
|
}
|
|
115
165
|
|
|
116
|
-
|
|
166
|
+
// Ping the node with trade details
|
|
167
|
+
return makePeerRequest({
|
|
168
|
+
records,
|
|
169
|
+
lnd: args.lnd,
|
|
170
|
+
timeout: postOpenTradeTimeoutMs,
|
|
171
|
+
to: req.from,
|
|
172
|
+
type: serviceTypeReceiveTrades,
|
|
173
|
+
},
|
|
174
|
+
err => {
|
|
175
|
+
if (!!err) {
|
|
176
|
+
failure([503, 'FailedToDeliverTradeInfo']);
|
|
177
|
+
|
|
178
|
+
return emitter.emit(
|
|
179
|
+
'failure',
|
|
180
|
+
[503, 'FailedToDeliverTradeInfo', {err}]
|
|
181
|
+
);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return success({});
|
|
185
|
+
});
|
|
117
186
|
});
|
|
118
187
|
}
|
|
119
188
|
|
|
@@ -122,12 +191,13 @@ module.exports = ({description, lnd, secret, tokens}) => {
|
|
|
122
191
|
|
|
123
192
|
// Create a trade secret for the requesting peer and return that
|
|
124
193
|
return finalizeTradeSecret({
|
|
125
|
-
description,
|
|
126
|
-
|
|
127
|
-
secret,
|
|
128
|
-
tokens,
|
|
194
|
+
description: args.description,
|
|
195
|
+
expires_at: args.expires_at,
|
|
129
196
|
is_hold: true,
|
|
197
|
+
lnd: args.lnd,
|
|
198
|
+
secret: args.secret,
|
|
130
199
|
to: req.from,
|
|
200
|
+
tokens: args.tokens,
|
|
131
201
|
},
|
|
132
202
|
(err, res) => {
|
|
133
203
|
if (!!err) {
|
|
@@ -140,7 +210,7 @@ module.exports = ({description, lnd, secret, tokens}) => {
|
|
|
140
210
|
const record = {type: tradeSecretType, value: res.trade};
|
|
141
211
|
|
|
142
212
|
// Add hold invoice to set of held invoices, only one should settle
|
|
143
|
-
const sub = subscribeToInvoice({
|
|
213
|
+
const sub = subscribeToInvoice({id: res.id, lnd: args.lnd});
|
|
144
214
|
|
|
145
215
|
// Add sub to list of holds
|
|
146
216
|
holds.push({sub, id: res.id});
|
|
@@ -160,8 +230,9 @@ module.exports = ({description, lnd, secret, tokens}) => {
|
|
|
160
230
|
|
|
161
231
|
// Settle the held invoice with the preimage
|
|
162
232
|
return acceptTrade({
|
|
163
|
-
lnd,
|
|
164
233
|
cancel: holds.filter(n => n.id !== updated.id).map(n => n.id),
|
|
234
|
+
id: args.id,
|
|
235
|
+
lnd: args.lnd,
|
|
165
236
|
secret: res.secret,
|
|
166
237
|
},
|
|
167
238
|
err => {
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const decodeAnchoredTrade = require('./decode_anchored_trade');
|
|
2
|
+
|
|
3
|
+
/** Derive open trade details from an invoice
|
|
4
|
+
|
|
5
|
+
{
|
|
6
|
+
description: <Invoice Description String>
|
|
7
|
+
id: <Invoice Id Hex String>
|
|
8
|
+
expires_at: <Invoice Expiration ISO 8601 Date String>
|
|
9
|
+
[is_canceled]: <Invoice is Canceled Bool>
|
|
10
|
+
is_confirmed: <Invoice is Confirmed Bool>
|
|
11
|
+
[is_push]: <Invoice is Push Payment Bool>
|
|
12
|
+
tokens: <Invoiced Amount Number>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@returns
|
|
16
|
+
{
|
|
17
|
+
[trade]: {
|
|
18
|
+
description: <Trade Description String>
|
|
19
|
+
expires_at: <Trade Expires at ISO 8601 Date String>
|
|
20
|
+
id: <Trade Id Hex String>
|
|
21
|
+
secret: <Secret Payload String>
|
|
22
|
+
tokens: <Tokens Number>
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
*/
|
|
26
|
+
module.exports = args => {
|
|
27
|
+
if (!args.description || !!args.is_canceled || !!args.is_confirmed) {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!!args.is_push || !args.tokens) {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const {trade} = decodeAnchoredTrade({encoded: args.description});
|
|
36
|
+
|
|
37
|
+
// Exit early when the invoice doesn't match an anchored trade
|
|
38
|
+
if (!trade) {
|
|
39
|
+
return {};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
trade: {
|
|
44
|
+
description: trade.description,
|
|
45
|
+
expires_at: args.expires_at,
|
|
46
|
+
id: args.id,
|
|
47
|
+
secret: trade.secret,
|
|
48
|
+
tokens: args.tokens,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
};
|