paid-services 3.11.4 → 3.12.2
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/CHANGELOG.md +8 -0
- package/balanced/balanced_open_request.js +157 -0
- package/balanced/index.js +3 -0
- package/balanced/service_key_types.json +12 -0
- package/index.js +2 -0
- package/package.json +5 -5
- package/test/balanced/test_balanced_open_request.js +178 -0
- package/trades/network_record_from_network.js +1 -1
- package/trades/service_open_trade.js +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
+
## Version 3.12.2
|
|
4
|
+
|
|
5
|
+
- `serviceOpenTrade`: Fix regtest network support
|
|
6
|
+
|
|
7
|
+
## Version 3.12.1
|
|
8
|
+
|
|
9
|
+
- `balancedOpenRequest`: Add method to derive balanced open proposal details
|
|
10
|
+
|
|
3
11
|
## Version 3.11.4
|
|
4
12
|
|
|
5
13
|
- `changeChannelCapacity`: Add `nodes` to allow moving channel to another node
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
const {parsePaymentRequest} = require('invoices');
|
|
2
|
+
|
|
3
|
+
const {balancedChannelKeyTypes} = require('./service_key_types');
|
|
4
|
+
|
|
5
|
+
const expectedRequestMtokens = '10000';
|
|
6
|
+
const expectedResponseMtokens = '1000';
|
|
7
|
+
const hexAsUtf8 = hex => Buffer.from(hex, 'hex').toString();
|
|
8
|
+
const isHexHashSized = hex => hex.length === 64;
|
|
9
|
+
const isHexNumberSized = hex => hex.length < 14;
|
|
10
|
+
const isOdd = n => n % 2;
|
|
11
|
+
const isPublicKey = n => !!n && /^0[2-3][0-9A-F]{64}$/i.test(n);
|
|
12
|
+
const parseHexNumber = hex => parseInt(hex, 16);
|
|
13
|
+
|
|
14
|
+
/** Derive a balanced open request from a received payment
|
|
15
|
+
|
|
16
|
+
{
|
|
17
|
+
confirmed_at: <Received Request At ISO 8601 Date String>
|
|
18
|
+
is_push: <Received Funds as Push Payment Bool>
|
|
19
|
+
payments: [{
|
|
20
|
+
messages: [{
|
|
21
|
+
type: <Message Record TLV Type String>
|
|
22
|
+
value: <Message Record Value Hex String>
|
|
23
|
+
}]
|
|
24
|
+
}]
|
|
25
|
+
received_mtokens: <Received Millitokens String>
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
@returns
|
|
29
|
+
{
|
|
30
|
+
[proposal]: {
|
|
31
|
+
accept_request: request,
|
|
32
|
+
capacity: parseHexNumber(channelCapacity.value),
|
|
33
|
+
fee_rate: parseHexNumber(fundingFeeRate.value),
|
|
34
|
+
partner_public_key: destination,
|
|
35
|
+
proposed_at: invoice.confirmed_at,
|
|
36
|
+
remote_multisig_key: remoteMultiSigKey.value,
|
|
37
|
+
remote_tx_id: remoteTxId.value,
|
|
38
|
+
remote_tx_vout: parseHexNumber(remoteTxVout.value),
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
*/
|
|
42
|
+
module.exports = args => {
|
|
43
|
+
// Exit early when not receiving a push of the proposal amount
|
|
44
|
+
if (!args.is_push || args.received_mtokens !== expectedRequestMtokens) {
|
|
45
|
+
return {}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const payment = args.payments.find(payment => {
|
|
49
|
+
return !!payment.messages.find(({type}) => {
|
|
50
|
+
return type === balancedChannelKeyTypes.accept_request;
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// Exit early when there is no payment with an accept request
|
|
55
|
+
if (!payment) {
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// The accept request is the reply request for the proposal
|
|
60
|
+
const acceptRequest = payment.messages.find(({type}) => {
|
|
61
|
+
return type === balancedChannelKeyTypes.accept_request;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
const request = hexAsUtf8(acceptRequest.value);
|
|
65
|
+
|
|
66
|
+
// Make sure the accept payment request is a regular one
|
|
67
|
+
try {
|
|
68
|
+
parsePaymentRequest({request});
|
|
69
|
+
} catch (err) {
|
|
70
|
+
return {};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const {destination, mtokens} = parsePaymentRequest({request});
|
|
74
|
+
|
|
75
|
+
// The accept payment request should request the expected amount
|
|
76
|
+
if (mtokens !== expectedResponseMtokens) {
|
|
77
|
+
return {};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Find the requested channel capacity
|
|
81
|
+
const channelCapacity = payment.messages.find(({type}) => {
|
|
82
|
+
return type === balancedChannelKeyTypes.channel_capacity;
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Exit early when there is no channel capacity
|
|
86
|
+
if (!channelCapacity || !isHexNumberSized(channelCapacity.value)) {
|
|
87
|
+
return {};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Exit early when the capacity doesn't make sense for splitting equally
|
|
91
|
+
if (isOdd(parseHexNumber(channelCapacity.value))) {
|
|
92
|
+
return {};
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Find the requested chain fee rate
|
|
96
|
+
const fundingFeeRate = payment.messages.find(({type}) => {
|
|
97
|
+
return type === balancedChannelKeyTypes.funding_tx_fee_rate;
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
// Exit early when there is no fee rate
|
|
101
|
+
if (!fundingFeeRate || !isHexNumberSized(fundingFeeRate.value)) {
|
|
102
|
+
return {};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// There must be a non-zero fee rate
|
|
106
|
+
if (!parseHexNumber(fundingFeeRate.value)) {
|
|
107
|
+
return {};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Find the remote multisig key for the open
|
|
111
|
+
const remoteMultiSigKey = payment.messages.find(({type}) => {
|
|
112
|
+
return type === balancedChannelKeyTypes.multisig_public_key;
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Exit early when there is no remote multisig key
|
|
116
|
+
if (!remoteMultiSigKey) {
|
|
117
|
+
return {};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// The remote multisig key must be a public key
|
|
121
|
+
if (!isPublicKey(remoteMultiSigKey.value)) {
|
|
122
|
+
return {};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Find the remote transaction UTXO transaction id
|
|
126
|
+
const remoteTxId = payment.messages.find(({type}) => {
|
|
127
|
+
return type === balancedChannelKeyTypes.transit_tx_id;
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
// Exit early when there is no tx id
|
|
131
|
+
if (!remoteTxId || !isHexHashSized(remoteTxId.value)) {
|
|
132
|
+
return {};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Find the remote transaction UTXO transaction output index
|
|
136
|
+
const remoteTxVout = payment.messages.find(({type}) => {
|
|
137
|
+
return type === balancedChannelKeyTypes.transit_tx_vout;
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
// Exit early when there is no tx vout
|
|
141
|
+
if (!remoteTxVout || !isHexNumberSized(remoteTxVout.value)) {
|
|
142
|
+
return {};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return {
|
|
146
|
+
proposal: {
|
|
147
|
+
accept_request: request,
|
|
148
|
+
capacity: parseHexNumber(channelCapacity.value),
|
|
149
|
+
fee_rate: parseHexNumber(fundingFeeRate.value),
|
|
150
|
+
partner_public_key: destination,
|
|
151
|
+
proposed_at: args.confirmed_at,
|
|
152
|
+
remote_multisig_key: remoteMultiSigKey.value,
|
|
153
|
+
remote_tx_id: remoteTxId.value,
|
|
154
|
+
remote_tx_vout: parseHexNumber(remoteTxVout.value),
|
|
155
|
+
},
|
|
156
|
+
};
|
|
157
|
+
};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"balancedChannelKeyTypes": {
|
|
3
|
+
"accept_request": "80501",
|
|
4
|
+
"channel_capacity": "80502",
|
|
5
|
+
"funding_signature": "80503",
|
|
6
|
+
"funding_tx_fee_rate": "80504",
|
|
7
|
+
"multisig_public_key": "80505",
|
|
8
|
+
"transit_public_key": "80506",
|
|
9
|
+
"transit_tx_id": "80507",
|
|
10
|
+
"transit_tx_vout": "80508"
|
|
11
|
+
}
|
|
12
|
+
}
|
package/index.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const {balancedOpenRequest} = require('./balanced');
|
|
1
2
|
const {changeChannelCapacity} = require('./capacity');
|
|
2
3
|
const {confirmServiceUse} = require('./client');
|
|
3
4
|
const {createAnchoredTrade} = require('./trades');
|
|
@@ -17,6 +18,7 @@ const {servicePeerRequests} = require('./p2p');
|
|
|
17
18
|
const serviceIds = schema.types;
|
|
18
19
|
|
|
19
20
|
module.exports = {
|
|
21
|
+
balancedOpenRequest,
|
|
20
22
|
changeChannelCapacity,
|
|
21
23
|
confirmServiceUse,
|
|
22
24
|
createAnchoredTrade,
|
package/package.json
CHANGED
|
@@ -13,9 +13,9 @@
|
|
|
13
13
|
"bolt07": "1.8.0",
|
|
14
14
|
"ecpair": "2.0.1",
|
|
15
15
|
"invoices": "2.0.4",
|
|
16
|
-
"ln-service": "53.9.
|
|
17
|
-
"ln-sync": "3.
|
|
18
|
-
"tiny-secp256k1": "2.2.
|
|
16
|
+
"ln-service": "53.9.3",
|
|
17
|
+
"ln-sync": "3.11.0",
|
|
18
|
+
"tiny-secp256k1": "2.2.1"
|
|
19
19
|
},
|
|
20
20
|
"description": "Lightning Paid Services library",
|
|
21
21
|
"devDependencies": {
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"integration-tests": "tap -j 2 --branches=1 --functions=1 --lines=1 --statements=1 -t 180 test/integration/*.js",
|
|
43
|
-
"test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/actions/*.js test/capacity/*.js test/client/*.js test/config/*.js test/p2p/*.js test/records/*.js test/respond/*.js test/server/*.js test/server/*.js test/services/*.js test/trades/*.js"
|
|
43
|
+
"test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/actions/*.js test/balanced/*.js test/capacity/*.js test/client/*.js test/config/*.js test/p2p/*.js test/records/*.js test/respond/*.js test/server/*.js test/server/*.js test/services/*.js test/trades/*.js"
|
|
44
44
|
},
|
|
45
|
-
"version": "3.
|
|
45
|
+
"version": "3.12.2"
|
|
46
46
|
}
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
const {test} = require('@alexbosworth/tap');
|
|
2
|
+
|
|
3
|
+
const method = require('./../../balanced/balanced_open_request');
|
|
4
|
+
|
|
5
|
+
const makeMessages = overrides => {
|
|
6
|
+
const args = {
|
|
7
|
+
'80501': Buffer.from('lntb10n1p3p6msgpp5s3xkaa2gg8q2zgmva8k9ruh3r7falxqdmkadac06wxc8fkqu4x0qdqqcqzpgxqr23ssp5ukm2dcl8wzfztx63758gmdjkna8jycypey880lenh082060fem4s9qyyssqul9nlwtpxqs2qegvpl9amltr4d9d8k9e008gr5ymv4aqkerel7dknusv60gtedgfvl3pq5lzg6c4sk4xf7lmlqtwr97cx047hpj9zqcp3mqur9').toString('hex'),
|
|
8
|
+
'80502': (2e6).toString(16),
|
|
9
|
+
'80504': (255).toString(16),
|
|
10
|
+
'80505': Buffer.alloc(33, 2).toString('hex'),
|
|
11
|
+
'80507': Buffer.alloc(32).toString('hex'),
|
|
12
|
+
'80508': (255).toString(16),
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
Object.keys(overrides).forEach(k => args[k] = overrides[k]);
|
|
16
|
+
|
|
17
|
+
const messages = Object.keys(args)
|
|
18
|
+
.filter(type => args[type] !== undefined)
|
|
19
|
+
.map(type => ({type, value: args[type]}));
|
|
20
|
+
|
|
21
|
+
return messages;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const makeArgs = overrides => {
|
|
25
|
+
const args = {
|
|
26
|
+
confirmed_at: new Date(0).toISOString(),
|
|
27
|
+
is_push: true,
|
|
28
|
+
payments: [{messages: makeMessages({})}],
|
|
29
|
+
received_mtokens: '10000',
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
Object.keys(overrides).forEach(k => args[k] = overrides[k]);
|
|
33
|
+
|
|
34
|
+
return args;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
const tests = [
|
|
38
|
+
{
|
|
39
|
+
args: makeArgs({is_push: false}),
|
|
40
|
+
description: 'A balanced open is a push',
|
|
41
|
+
expected: {},
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
args: makeArgs({received_mtokens: '10'}),
|
|
45
|
+
description: 'A balanced open receives 10 sats',
|
|
46
|
+
expected: {},
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
args: makeArgs({
|
|
50
|
+
payments: [{messages: makeMessages({'80501': undefined})}],
|
|
51
|
+
}),
|
|
52
|
+
description: 'A balanced open has a reply request',
|
|
53
|
+
expected: {},
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
args: makeArgs({
|
|
57
|
+
payments: [{messages: makeMessages({'80501': 'invalid request'})}],
|
|
58
|
+
}),
|
|
59
|
+
description: 'A balanced open has a valid reply request',
|
|
60
|
+
expected: {},
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
args: makeArgs({
|
|
64
|
+
payments: [{
|
|
65
|
+
messages: makeMessages({
|
|
66
|
+
'80501': Buffer.from('lnbc1pvjluezsp5zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zyg3zygspp5qqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqqqsyqcyq5rqwzqfqypqdpl2pkx2ctnv5sxxmmwwd5kgetjypeh2ursdae8g6twvus8g6rfwvs8qun0dfjkxaq9qrsgq357wnc5r2ueh7ck6q93dj32dlqnls087fxdwk8qakdyafkq3yap9us6v52vjjsrvywa6rt52cm9r9zqt8r2t7mlcwspyetp5h2tztugp9lfyql').toString('hex'),
|
|
67
|
+
}),
|
|
68
|
+
}],
|
|
69
|
+
}),
|
|
70
|
+
description: 'A balanced open has a reply request with the correct amount',
|
|
71
|
+
expected: {},
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
args: makeArgs({
|
|
75
|
+
payments: [{messages: makeMessages({'80502': undefined})}],
|
|
76
|
+
}),
|
|
77
|
+
description: 'A balanced open has capacity',
|
|
78
|
+
expected: {},
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
args: makeArgs({
|
|
82
|
+
payments: [{
|
|
83
|
+
messages: makeMessages({'80502': Buffer.alloc(32).toString('hex')}),
|
|
84
|
+
}],
|
|
85
|
+
}),
|
|
86
|
+
description: 'A balanced open has numeric capacity',
|
|
87
|
+
expected: {},
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
args: makeArgs({payments: [{messages: makeMessages({'80502': '01'})}]}),
|
|
91
|
+
description: 'A balanced open has even capacity',
|
|
92
|
+
expected: {},
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
args: makeArgs({
|
|
96
|
+
payments: [{messages: makeMessages({'80504': undefined})}],
|
|
97
|
+
}),
|
|
98
|
+
description: 'A balanced open has a fee rate',
|
|
99
|
+
expected: {},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
args: makeArgs({
|
|
103
|
+
payments: [{
|
|
104
|
+
messages: makeMessages({'80504': Buffer.alloc(32).toString('hex')}),
|
|
105
|
+
}],
|
|
106
|
+
}),
|
|
107
|
+
description: 'A balanced open has a numeric fee rate',
|
|
108
|
+
expected: {},
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
args: makeArgs({payments: [{messages: makeMessages({'80504': '00'})}]}),
|
|
112
|
+
description: 'A balanced open has non zero fee rate',
|
|
113
|
+
expected: {},
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
args: makeArgs({
|
|
117
|
+
payments: [{messages: makeMessages({'80505': undefined})}],
|
|
118
|
+
}),
|
|
119
|
+
description: 'A balanced open has a remote multisig key',
|
|
120
|
+
expected: {},
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
args: makeArgs({
|
|
124
|
+
payments: [{messages: makeMessages({'80505': Buffer.alloc(32)})}],
|
|
125
|
+
}),
|
|
126
|
+
description: 'A balanced open has a remote multisig public key',
|
|
127
|
+
expected: {},
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
args: makeArgs({
|
|
131
|
+
payments: [{messages: makeMessages({'80507': undefined})}],
|
|
132
|
+
}),
|
|
133
|
+
description: 'A balanced open has a tx id',
|
|
134
|
+
expected: {},
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
args: makeArgs({payments: [{messages: makeMessages({'80507': '00'})}]}),
|
|
138
|
+
description: 'A balanced open has a regular sized tx id',
|
|
139
|
+
expected: {},
|
|
140
|
+
},
|
|
141
|
+
{
|
|
142
|
+
args: makeArgs({
|
|
143
|
+
payments: [{messages: makeMessages({'80508': undefined})}],
|
|
144
|
+
}),
|
|
145
|
+
description: 'A balanced open has a tx vout',
|
|
146
|
+
expected: {},
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
args: makeArgs({}),
|
|
150
|
+
description: 'Derive a balanced open request',
|
|
151
|
+
expected: {
|
|
152
|
+
proposal: {
|
|
153
|
+
accept_request: 'lntb10n1p3p6msgpp5s3xkaa2gg8q2zgmva8k9ruh3r7falxqdmkadac06wxc8fkqu4x0qdqqcqzpgxqr23ssp5ukm2dcl8wzfztx63758gmdjkna8jycypey880lenh082060fem4s9qyyssqul9nlwtpxqs2qegvpl9amltr4d9d8k9e008gr5ymv4aqkerel7dknusv60gtedgfvl3pq5lzg6c4sk4xf7lmlqtwr97cx047hpj9zqcp3mqur9',
|
|
154
|
+
capacity: 2000000,
|
|
155
|
+
fee_rate: 255,
|
|
156
|
+
partner_public_key: '020ec0c6a0c4fe5d8a79928ead294c36234a76f6e0dca896c35413612a3fd8dbf8',
|
|
157
|
+
proposed_at: '1970-01-01T00:00:00.000Z',
|
|
158
|
+
remote_multisig_key: '020202020202020202020202020202020202020202020202020202020202020202',
|
|
159
|
+
remote_tx_id: '0000000000000000000000000000000000000000000000000000000000000000',
|
|
160
|
+
remote_tx_vout: 255,
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
},
|
|
164
|
+
];
|
|
165
|
+
|
|
166
|
+
tests.forEach(({args, description, error, expected}) => {
|
|
167
|
+
return test(description, async ({end, strictSame, throws}) => {
|
|
168
|
+
if (!!error) {
|
|
169
|
+
throws(() => method(args), new Error(error), 'Got error');
|
|
170
|
+
} else {
|
|
171
|
+
const res = method(args);
|
|
172
|
+
|
|
173
|
+
strictSame(res, expected, 'Got expected result');
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
return end();
|
|
177
|
+
});
|
|
178
|
+
});
|