balanceofsatoshis 12.8.2 → 12.8.5
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 +5 -1
- package/README.md +1 -1
- package/bos +20 -0
- package/package.json +3 -3
- package/telegram/start_telegram_bot.js +2 -2
- package/triggers/create_connectivity_trigger.js +59 -0
- package/triggers/create_follow_node_trigger.js +59 -0
- package/triggers/decode_connectivity_params.js +45 -0
- package/triggers/decode_follow_params.js +45 -0
- package/triggers/decode_trigger.js +81 -0
- package/triggers/encode_connectivity_params.js +29 -0
- package/triggers/encode_follow_params.js +29 -0
- package/triggers/encode_trigger.js +73 -0
- package/triggers/get_triggers.js +93 -0
- package/triggers/index.js +3 -0
- package/triggers/manage_triggers.js +243 -0
- package/triggers/subscribe_to_triggers.js +232 -0
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -499,7 +499,7 @@ On Umbrel this would be:
|
|
|
499
499
|
## --network="host"
|
|
500
500
|
## --add-host=umbrel.local:192.168.1.23
|
|
501
501
|
## -v $HOME/umbrel/lnd:/home/node/.lnd:ro
|
|
502
|
-
docker run -it --rm --network=
|
|
502
|
+
docker run -it --rm --network=umbrel_main_network --add-host=umbrel.local:192.168.1.23 -v $HOME/.bos:/home/node/.bos -v $HOME/umbrel/lnd:/home/node/.lnd:ro alexbosworth/balanceofsatoshis report
|
|
503
503
|
```
|
|
504
504
|
|
|
505
505
|
Note: For [umbrel-os](https://github.com/getumbrel/umbrel-os) users, when
|
package/bos
CHANGED
|
@@ -42,6 +42,7 @@ const services = importLazy('./services');
|
|
|
42
42
|
const {swapTypes} = commandConstants;
|
|
43
43
|
const swaps = importLazy('./swaps');
|
|
44
44
|
const telegram = importLazy('./telegram');
|
|
45
|
+
const triggers = importLazy('./triggers');
|
|
45
46
|
const wallets = importLazy('./wallets');
|
|
46
47
|
const {version} = importLazy('./package');
|
|
47
48
|
|
|
@@ -1817,6 +1818,25 @@ prog
|
|
|
1817
1818
|
});
|
|
1818
1819
|
})
|
|
1819
1820
|
|
|
1821
|
+
// Manage triggers
|
|
1822
|
+
.command('triggers', 'Manage event triggers')
|
|
1823
|
+
.option('--node <name>', 'Node to manage triggers on')
|
|
1824
|
+
.visible(false)
|
|
1825
|
+
.action((args, options, logger) => {
|
|
1826
|
+
return new Promise(async (resolve, reject) => {
|
|
1827
|
+
try {
|
|
1828
|
+
return triggers.manageTriggers({
|
|
1829
|
+
logger,
|
|
1830
|
+
ask: (n, cbk) => inquirer.prompt([n]).then(res => cbk(res)),
|
|
1831
|
+
lnd: (await lndForNode(logger, options.node)).lnd,
|
|
1832
|
+
},
|
|
1833
|
+
responses.returnObject({exit, logger, reject, resolve}));
|
|
1834
|
+
} catch (err) {
|
|
1835
|
+
return logger.error({err}) && reject();
|
|
1836
|
+
}
|
|
1837
|
+
});
|
|
1838
|
+
})
|
|
1839
|
+
|
|
1820
1840
|
// Unlock wallet
|
|
1821
1841
|
.command('unlock', 'Unlock wallet if locked')
|
|
1822
1842
|
.help('Check if the wallet is locked, if so use a password file to unlock')
|
package/package.json
CHANGED
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"ini": "3.0.0",
|
|
37
37
|
"inquirer": "8.2.4",
|
|
38
38
|
"ln-accounting": "5.0.6",
|
|
39
|
-
"ln-service": "53.
|
|
39
|
+
"ln-service": "53.17.0",
|
|
40
40
|
"ln-sync": "3.12.0",
|
|
41
41
|
"ln-telegram": "3.21.5",
|
|
42
42
|
"moment": "2.29.3",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@alexbosworth/tap": "15.0.11",
|
|
57
57
|
"invoices": "2.0.6",
|
|
58
|
-
"ln-docker-daemons": "2.2.
|
|
58
|
+
"ln-docker-daemons": "2.2.11",
|
|
59
59
|
"mock-lnd": "1.4.1",
|
|
60
60
|
"tiny-secp256k1": "2.2.1"
|
|
61
61
|
},
|
|
@@ -84,5 +84,5 @@
|
|
|
84
84
|
"postpublish": "docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v7 -t alexbosworth/balanceofsatoshis --push .",
|
|
85
85
|
"test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/arrays/*.js test/balances/*.js test/chain/*.js test/display/*.js test/encryption/*.js test/lnd/*.js test/network/*.js test/nodes/*.js test/peers/*.js test/responses/*.js test/routing/*.js test/services/*.js test/swaps/*.js test/tags/*.js test/telegram/*.js test/wallets/*.js"
|
|
86
86
|
},
|
|
87
|
-
"version": "12.8.
|
|
87
|
+
"version": "12.8.5"
|
|
88
88
|
}
|
|
@@ -211,7 +211,7 @@ module.exports = (args, cbk) => {
|
|
|
211
211
|
reply: n => ctx.reply(n, markdown),
|
|
212
212
|
request: args.request,
|
|
213
213
|
},
|
|
214
|
-
err => !!err ? logger.error({err}) : null);
|
|
214
|
+
err => !!err ? args.logger.error({err}) : null);
|
|
215
215
|
});
|
|
216
216
|
|
|
217
217
|
// Handle command to get the connect id
|
|
@@ -335,7 +335,7 @@ module.exports = (args, cbk) => {
|
|
|
335
335
|
});
|
|
336
336
|
});
|
|
337
337
|
} catch (err) {
|
|
338
|
-
args.logger.error(err);
|
|
338
|
+
args.logger.error({err});
|
|
339
339
|
}
|
|
340
340
|
});
|
|
341
341
|
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const asyncAuto = require('async/auto');
|
|
2
|
+
const {createInvoice} = require('ln-service');
|
|
3
|
+
const {returnResult} = require('asyncjs-util');
|
|
4
|
+
|
|
5
|
+
const encodeTrigger = require('./encode_trigger');
|
|
6
|
+
|
|
7
|
+
const daysAsMs = days => Number(days) * 1000 * 60 * 60 * 24;
|
|
8
|
+
const defaultTriggerDays = 365;
|
|
9
|
+
const futureDate = ms => new Date(Date.now() + ms).toISOString();
|
|
10
|
+
|
|
11
|
+
/** Create a connectivity with peer trigger
|
|
12
|
+
|
|
13
|
+
{
|
|
14
|
+
id: <Node Id Public Key Hex String>
|
|
15
|
+
lnd: <Authenticated LND API Object>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@returns via cbk or Promise
|
|
19
|
+
*/
|
|
20
|
+
module.exports = ({id, lnd}, cbk) => {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
return asyncAuto({
|
|
23
|
+
// Check arguments
|
|
24
|
+
validate: cbk => {
|
|
25
|
+
if (!id) {
|
|
26
|
+
return cbk([400, 'ExpectedNodeIdToCreateConnectivityTrigger']);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!lnd) {
|
|
30
|
+
return cbk([400, 'ExpectedLndToCreateConnectivityTrigger']);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return cbk();
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// Encode the trigger
|
|
37
|
+
description: ['validate', ({}, cbk) => {
|
|
38
|
+
try {
|
|
39
|
+
const {encoded} = encodeTrigger({connectivity: {id}});
|
|
40
|
+
|
|
41
|
+
return cbk(null, encoded);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
return cbk([400, err.message]);
|
|
44
|
+
}
|
|
45
|
+
}],
|
|
46
|
+
|
|
47
|
+
// Add the trigger invoice
|
|
48
|
+
create: ['description', ({description}, cbk) => {
|
|
49
|
+
return createInvoice({
|
|
50
|
+
description,
|
|
51
|
+
lnd,
|
|
52
|
+
expires_at: futureDate(daysAsMs(defaultTriggerDays)),
|
|
53
|
+
},
|
|
54
|
+
cbk);
|
|
55
|
+
}],
|
|
56
|
+
},
|
|
57
|
+
returnResult({reject, resolve}, cbk));
|
|
58
|
+
});
|
|
59
|
+
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const asyncAuto = require('async/auto');
|
|
2
|
+
const {createInvoice} = require('ln-service');
|
|
3
|
+
const {returnResult} = require('asyncjs-util');
|
|
4
|
+
|
|
5
|
+
const encodeTrigger = require('./encode_trigger');
|
|
6
|
+
|
|
7
|
+
const daysAsMs = days => Number(days) * 1000 * 60 * 60 * 24;
|
|
8
|
+
const defaultTriggerDays = 365;
|
|
9
|
+
const futureDate = ms => new Date(Date.now() + ms).toISOString();
|
|
10
|
+
|
|
11
|
+
/** Create a follow node trigger
|
|
12
|
+
|
|
13
|
+
{
|
|
14
|
+
id: <Node Id Public Key Hex String>
|
|
15
|
+
lnd: <Authenticated LND API Object>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@returns via cbk or Promise
|
|
19
|
+
*/
|
|
20
|
+
module.exports = ({id, lnd}, cbk) => {
|
|
21
|
+
return new Promise((resolve, reject) => {
|
|
22
|
+
return asyncAuto({
|
|
23
|
+
// Check arguments
|
|
24
|
+
validate: cbk => {
|
|
25
|
+
if (!id) {
|
|
26
|
+
return cbk([400, 'ExpectedNodeIdToFollowToCreateFollowNodeTrigger']);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!lnd) {
|
|
30
|
+
return cbk([400, 'ExpectedLndToCreateFollowNodeTrigger']);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return cbk();
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// Encode the trigger
|
|
37
|
+
description: ['validate', ({}, cbk) => {
|
|
38
|
+
try {
|
|
39
|
+
const {encoded} = encodeTrigger({follow: {id}});
|
|
40
|
+
|
|
41
|
+
return cbk(null, encoded);
|
|
42
|
+
} catch (err) {
|
|
43
|
+
return cbk([400, err.message]);
|
|
44
|
+
}
|
|
45
|
+
}],
|
|
46
|
+
|
|
47
|
+
// Add the trigger invoice
|
|
48
|
+
create: ['description', ({description}, cbk) => {
|
|
49
|
+
return createInvoice({
|
|
50
|
+
description,
|
|
51
|
+
lnd,
|
|
52
|
+
expires_at: futureDate(daysAsMs(defaultTriggerDays)),
|
|
53
|
+
},
|
|
54
|
+
cbk);
|
|
55
|
+
}],
|
|
56
|
+
},
|
|
57
|
+
returnResult({reject, resolve}, cbk));
|
|
58
|
+
});
|
|
59
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const {decodeTlvStream} = require('bolt01');
|
|
2
|
+
|
|
3
|
+
const findRecord = (records, type) => records.find(n => n.type === type);
|
|
4
|
+
const isPublicKey = n => !!n && /^0[2-3][0-9A-F]{64}$/i.test(n);
|
|
5
|
+
const typeId = '1';
|
|
6
|
+
const typeVersion = '0';
|
|
7
|
+
|
|
8
|
+
/** Decode connectivity trigger parameters
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
parameters: <Encoded Parameters Hex String>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@throws
|
|
15
|
+
<Error>
|
|
16
|
+
|
|
17
|
+
@returns
|
|
18
|
+
{
|
|
19
|
+
id: <Node Id Hex String>
|
|
20
|
+
}
|
|
21
|
+
*/
|
|
22
|
+
module.exports = ({parameters}) => {
|
|
23
|
+
if (!parameters) {
|
|
24
|
+
throw new Error('ExpectedEncodedParametersToDecodeConnectivityParameters');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const {records} = decodeTlvStream({encoded: parameters});
|
|
28
|
+
|
|
29
|
+
// Check the parameters version
|
|
30
|
+
if (!!findRecord(records, typeVersion)) {
|
|
31
|
+
throw new Error('UnexpectedVersionForEncodedConnectivityTrigger');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const idRecord = findRecord(records, typeId);
|
|
35
|
+
|
|
36
|
+
if (!idRecord) {
|
|
37
|
+
throw new Error('ExpectedNodePublicKeyForEncodedConnectivityTrigger');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!isPublicKey(idRecord.value)) {
|
|
41
|
+
throw new Error('ExpectedValidNodePublicKeyForEncodedConnectivityTrigger');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {id: idRecord.value};
|
|
45
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const {decodeTlvStream} = require('bolt01');
|
|
2
|
+
|
|
3
|
+
const findRecord = (records, type) => records.find(n => n.type === type);
|
|
4
|
+
const isPublicKey = n => !!n && /^0[2-3][0-9A-F]{64}$/i.test(n);
|
|
5
|
+
const typeId = '1';
|
|
6
|
+
const typeVersion = '0';
|
|
7
|
+
|
|
8
|
+
/** Decode follow trigger parameters
|
|
9
|
+
|
|
10
|
+
{
|
|
11
|
+
parameters: <Encoded Parameters Hex String>
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
@throws
|
|
15
|
+
<Error>
|
|
16
|
+
|
|
17
|
+
@returns
|
|
18
|
+
{
|
|
19
|
+
id: <Node Id Hex String>
|
|
20
|
+
}
|
|
21
|
+
*/
|
|
22
|
+
module.exports = ({parameters}) => {
|
|
23
|
+
if (!parameters) {
|
|
24
|
+
throw new Error('ExpectedEncodedParametersToDecodeFollowParameters');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const {records} = decodeTlvStream({encoded: parameters});
|
|
28
|
+
|
|
29
|
+
// Check the parameters version
|
|
30
|
+
if (!!findRecord(records, typeVersion)) {
|
|
31
|
+
throw new Error('UnexpectedVersionForEncodedTrigger');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const idRecord = findRecord(records, typeId);
|
|
35
|
+
|
|
36
|
+
if (!idRecord) {
|
|
37
|
+
throw new Error('ExpectedNodePublicKeyForEncodedTrigger');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!isPublicKey(idRecord.value)) {
|
|
41
|
+
throw new Error('ExpectedValidNodePublicKeyForEncodedTrigger');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return {id: idRecord.value};
|
|
45
|
+
};
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
const {decodeTlvStream} = require('bolt01');
|
|
2
|
+
|
|
3
|
+
const decodeConnectivityParams = require('./decode_connectivity_params');
|
|
4
|
+
const decodeFollowParams = require('./decode_follow_params');
|
|
5
|
+
|
|
6
|
+
const base64AsHex = base64 => Buffer.from(base64, 'base64').toString('hex');
|
|
7
|
+
const defaultMethodRecord = {value: '00'};
|
|
8
|
+
const defaultVersionRecord = {value: '00'};
|
|
9
|
+
const findRecord = (records, type) => records.find(n => n.type === type);
|
|
10
|
+
const knownVersions = ['00', '01'];
|
|
11
|
+
const methodConnectivity = '01';
|
|
12
|
+
const methodFollow = '00';
|
|
13
|
+
const triggerPrefix = 'bos-trigger:';
|
|
14
|
+
const typeMethod = '1';
|
|
15
|
+
const typeParams = '2';
|
|
16
|
+
const typeVersion = '0';
|
|
17
|
+
|
|
18
|
+
/** Decode an encoded trigger
|
|
19
|
+
|
|
20
|
+
{
|
|
21
|
+
encoded: <Encoded Trigger String>
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
@throws <Error>
|
|
25
|
+
|
|
26
|
+
@returns
|
|
27
|
+
{
|
|
28
|
+
[connectivity]: {
|
|
29
|
+
id: <Node Id Hex String>
|
|
30
|
+
}
|
|
31
|
+
[follow]: {
|
|
32
|
+
id: <Node Id Hex String>
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
*/
|
|
36
|
+
module.exports = ({encoded}) => {
|
|
37
|
+
if (!encoded) {
|
|
38
|
+
throw new Error('ExpectedEncodedTriggerToDecode');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!encoded.startsWith(triggerPrefix)) {
|
|
42
|
+
throw new Error('ExpectedTriggerPrefixForEncodedPrefix');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const data = base64AsHex(encoded.slice(triggerPrefix.length));
|
|
46
|
+
|
|
47
|
+
const {records} = decodeTlvStream({encoded: data});
|
|
48
|
+
|
|
49
|
+
const version = findRecord(records, typeVersion) || defaultVersionRecord;
|
|
50
|
+
|
|
51
|
+
// Check the trigger version
|
|
52
|
+
if (!knownVersions.includes(version.value)) {
|
|
53
|
+
throw new Error('UnexpectedVersionForEncodedTrigger');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const methodRecord = findRecord(records, typeMethod) || defaultMethodRecord;
|
|
57
|
+
|
|
58
|
+
// Trigger parameters are encoded into a stream record
|
|
59
|
+
const parametersRecord = findRecord(records, typeParams);
|
|
60
|
+
|
|
61
|
+
if (!parametersRecord) {
|
|
62
|
+
throw new Error('ExpectedParametersForTrigger');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const parameters = parametersRecord.value;
|
|
66
|
+
|
|
67
|
+
switch (methodRecord.value) {
|
|
68
|
+
case methodConnectivity:
|
|
69
|
+
const connectivity = decodeConnectivityParams({parameters});
|
|
70
|
+
|
|
71
|
+
return {connectivity};
|
|
72
|
+
|
|
73
|
+
case methodFollow:
|
|
74
|
+
const follow = decodeFollowParams({parameters});
|
|
75
|
+
|
|
76
|
+
return {follow};
|
|
77
|
+
|
|
78
|
+
default:
|
|
79
|
+
throw new Error('UnrecognizedMethodTypeForTrigger');
|
|
80
|
+
}
|
|
81
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const {encodeTlvStream} = require('bolt01');
|
|
2
|
+
|
|
3
|
+
const isPublicKey = n => !!n && /^0[2-3][0-9A-F]{64}$/i.test(n);
|
|
4
|
+
const typeNodeId = '1';
|
|
5
|
+
|
|
6
|
+
/** Encode the connectivity with node params
|
|
7
|
+
|
|
8
|
+
[0]: <Version>
|
|
9
|
+
1: <Node Id>
|
|
10
|
+
|
|
11
|
+
{
|
|
12
|
+
id: <Node Identity Public Key Hex String>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@throws
|
|
16
|
+
<Error>
|
|
17
|
+
|
|
18
|
+
@returns
|
|
19
|
+
{
|
|
20
|
+
encoded: <Trigger Parameters Hex String>
|
|
21
|
+
}
|
|
22
|
+
*/
|
|
23
|
+
module.exports = ({id}) => {
|
|
24
|
+
if (!isPublicKey(id)) {
|
|
25
|
+
throw new Error('ExpectedPublicKeyToEncodeConnectivityParams');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return encodeTlvStream({records: [{type: typeNodeId, value: id}]});
|
|
29
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
const {encodeTlvStream} = require('bolt01');
|
|
2
|
+
|
|
3
|
+
const isPublicKey = n => !!n && /^0[2-3][0-9A-F]{64}$/i.test(n);
|
|
4
|
+
const typeNodeId = '1';
|
|
5
|
+
|
|
6
|
+
/** Encode the follow node params
|
|
7
|
+
|
|
8
|
+
[0]: <Version>
|
|
9
|
+
1: <Node Id>
|
|
10
|
+
|
|
11
|
+
{
|
|
12
|
+
id: <Node Identity Public Key Hex String>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
@throws
|
|
16
|
+
<Error>
|
|
17
|
+
|
|
18
|
+
@returns
|
|
19
|
+
{
|
|
20
|
+
encoded: <Trigger Parameters Hex String>
|
|
21
|
+
}
|
|
22
|
+
*/
|
|
23
|
+
module.exports = ({id}) => {
|
|
24
|
+
if (!isPublicKey(id)) {
|
|
25
|
+
throw new Error('ExpectedPublicKeyToEncodeFollowParams');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return encodeTlvStream({records: [{type: typeNodeId, value: id}]});
|
|
29
|
+
};
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
const {encodeTlvStream} = require('bolt01');
|
|
2
|
+
|
|
3
|
+
const encodeConnectivityParams = require('./encode_connectivity_params');
|
|
4
|
+
const encodeFollowParams = require('./encode_follow_params');
|
|
5
|
+
|
|
6
|
+
const hexAsBase64 = hex => Buffer.from(hex, 'hex').toString('base64');
|
|
7
|
+
const methodConnectivity = '01';
|
|
8
|
+
const triggerPrefix = 'bos-trigger:';
|
|
9
|
+
const typeTriggerMethod = '1';
|
|
10
|
+
const typeTriggerParameters = '2';
|
|
11
|
+
const typeVersion = '0';
|
|
12
|
+
const version = '01';
|
|
13
|
+
|
|
14
|
+
/** Encode a trigger
|
|
15
|
+
|
|
16
|
+
[0]: <Version>
|
|
17
|
+
[1]: <Method>
|
|
18
|
+
[2]: <Parameters>
|
|
19
|
+
|
|
20
|
+
{
|
|
21
|
+
[connectivity]: {
|
|
22
|
+
id: <Node Id Hex String>
|
|
23
|
+
}
|
|
24
|
+
[follow]: {
|
|
25
|
+
id: <Node Id Hex String>
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@throws
|
|
30
|
+
<Error>
|
|
31
|
+
|
|
32
|
+
@returns
|
|
33
|
+
{
|
|
34
|
+
encoded: <Encoded Trigger String>
|
|
35
|
+
}
|
|
36
|
+
*/
|
|
37
|
+
module.exports = ({connectivity, follow}) => {
|
|
38
|
+
if (!connectivity && !follow) {
|
|
39
|
+
throw new Error('ExpectedConnectivityOrFollowDetailsToEncodeTrigger');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (!!connectivity) {
|
|
43
|
+
// Encode the trigger parameters for a connectivity trigger
|
|
44
|
+
const {encoded} = encodeTlvStream({
|
|
45
|
+
records: [
|
|
46
|
+
{
|
|
47
|
+
type: typeTriggerMethod,
|
|
48
|
+
value: methodConnectivity,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: typeTriggerParameters,
|
|
52
|
+
value: encodeConnectivityParams({id: connectivity.id}).encoded,
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
type: typeVersion,
|
|
56
|
+
value: version,
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
return {encoded: `${triggerPrefix}${hexAsBase64(encoded)}`};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Encode the trigger parameters for a follow trigger
|
|
65
|
+
const {encoded} = encodeTlvStream({
|
|
66
|
+
records: [{
|
|
67
|
+
type: typeTriggerParameters,
|
|
68
|
+
value: encodeFollowParams({id: follow.id}).encoded,
|
|
69
|
+
}],
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return {encoded: `${triggerPrefix}${hexAsBase64(encoded)}`};
|
|
73
|
+
};
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
const asyncAuto = require('async/auto');
|
|
2
|
+
const asyncUntil = require('async/until');
|
|
3
|
+
const {getInvoices} = require('ln-service');
|
|
4
|
+
const {returnResult} = require('asyncjs-util');
|
|
5
|
+
|
|
6
|
+
const decodeTrigger = require('./decode_trigger');
|
|
7
|
+
|
|
8
|
+
const defaultInvoicesLimit = 100;
|
|
9
|
+
|
|
10
|
+
/** Get registered triggers
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
lnd: <Authenticated LND API Object>
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
@returns via cbk or Promise
|
|
17
|
+
{
|
|
18
|
+
triggers: [{
|
|
19
|
+
[connectivity]: {
|
|
20
|
+
id: <Node Identity Public Key Hex String>
|
|
21
|
+
}
|
|
22
|
+
[follow]: {
|
|
23
|
+
id: <Node Identity Public Key Hex String>
|
|
24
|
+
}
|
|
25
|
+
id: <Trigger Id Hex String>
|
|
26
|
+
}]
|
|
27
|
+
}
|
|
28
|
+
*/
|
|
29
|
+
module.exports = ({lnd}, cbk) => {
|
|
30
|
+
return new Promise((resolve, reject) => {
|
|
31
|
+
return asyncAuto({
|
|
32
|
+
// Check arguments
|
|
33
|
+
validate: cbk => {
|
|
34
|
+
if (!lnd) {
|
|
35
|
+
return cbk([400, 'ExpectedAuthenticatedLndToGetTriggers']);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return cbk();
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
// Get the past triggers
|
|
42
|
+
getTriggers: ['validate', ({}, cbk) => {
|
|
43
|
+
let token;
|
|
44
|
+
const triggers = [];
|
|
45
|
+
|
|
46
|
+
// Register past trigger invoices
|
|
47
|
+
return asyncUntil(
|
|
48
|
+
cbk => cbk(null, token === false),
|
|
49
|
+
cbk => {
|
|
50
|
+
return getInvoices({
|
|
51
|
+
lnd,
|
|
52
|
+
token,
|
|
53
|
+
is_unconfirmed: true,
|
|
54
|
+
limit: !token ? defaultInvoicesLimit : undefined,
|
|
55
|
+
},
|
|
56
|
+
(err, res) => {
|
|
57
|
+
if (!!err) {
|
|
58
|
+
return cbk(err);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
token = res.next || false;
|
|
62
|
+
|
|
63
|
+
res.invoices.forEach(({description, id}) => {
|
|
64
|
+
try {
|
|
65
|
+
const trigger = decodeTrigger({encoded: description});
|
|
66
|
+
|
|
67
|
+
return triggers.push({
|
|
68
|
+
id,
|
|
69
|
+
connectivity: trigger.connectivity,
|
|
70
|
+
follow: trigger.follow,
|
|
71
|
+
});
|
|
72
|
+
} catch (err) {
|
|
73
|
+
// Ignore invoices that are not triggers
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
return cbk();
|
|
79
|
+
});
|
|
80
|
+
},
|
|
81
|
+
err => {
|
|
82
|
+
if (!!err) {
|
|
83
|
+
return cbk(err);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return cbk(null, triggers);
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
}],
|
|
90
|
+
},
|
|
91
|
+
returnResult({reject, resolve, of: 'getTriggers'}, cbk));
|
|
92
|
+
});
|
|
93
|
+
};
|
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
const asyncAuto = require('async/auto');
|
|
2
|
+
const {cancelHodlInvoice} = require('ln-service');
|
|
3
|
+
const {returnResult} = require('asyncjs-util');
|
|
4
|
+
|
|
5
|
+
const createConnectivityTrigger = require('./create_connectivity_trigger');
|
|
6
|
+
const createFollowNodeTrigger = require('./create_follow_node_trigger');
|
|
7
|
+
const getTriggers = require('./get_triggers');
|
|
8
|
+
const subscribeToTriggers = require('./subscribe_to_triggers');
|
|
9
|
+
|
|
10
|
+
const actionAddConnectivityTrigger = 'action-add-connectivity-trigger';
|
|
11
|
+
const actionAddFollowTrigger = 'action-add-follow-trigger';
|
|
12
|
+
const actionDeleteTrigger = 'action-delete-trigger';
|
|
13
|
+
const actionListTriggers = 'action-list-triggers';
|
|
14
|
+
const actionSubscribeToTriggers = 'action-subscribe-to-triggers';
|
|
15
|
+
const isPublicKey = n => !!n && /^0[2-3][0-9A-F]{64}$/i.test(n);
|
|
16
|
+
|
|
17
|
+
/** Manage trigger actions
|
|
18
|
+
|
|
19
|
+
{
|
|
20
|
+
ask: <Ask Function>
|
|
21
|
+
lnd: <Authenticated LND API Object>
|
|
22
|
+
logger: <Winston Logger Object>
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@returns via cbk or Promise
|
|
26
|
+
*/
|
|
27
|
+
module.exports = ({ask, lnd, logger}, cbk) => {
|
|
28
|
+
return new Promise((resolve, reject) => {
|
|
29
|
+
return asyncAuto({
|
|
30
|
+
// Check arguments
|
|
31
|
+
validate: cbk => {
|
|
32
|
+
if (!ask) {
|
|
33
|
+
return cbk([400, 'ExpectedAskFunctionToManageTriggers']);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!lnd) {
|
|
37
|
+
return cbk([400, 'ExpectedAuthenticatedLndToManageTriggers']);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!logger) {
|
|
41
|
+
return cbk([400, 'ExpectedWinstonLoggerToManageTriggers']);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return cbk();
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
// Select trigger action
|
|
48
|
+
selectAction: ['validate', ({}, cbk) => {
|
|
49
|
+
return ask({
|
|
50
|
+
choices: [
|
|
51
|
+
{
|
|
52
|
+
name: 'Add Node Connectivity Trigger',
|
|
53
|
+
value: actionAddConnectivityTrigger,
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
name: 'Add Follow Node Trigger',
|
|
57
|
+
value: actionAddFollowTrigger,
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'View Triggers',
|
|
61
|
+
value: actionListTriggers,
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
name: 'Subscribe to Triggers',
|
|
65
|
+
value: actionSubscribeToTriggers,
|
|
66
|
+
},
|
|
67
|
+
],
|
|
68
|
+
message: 'Trigger action?',
|
|
69
|
+
name: 'action',
|
|
70
|
+
type: 'list',
|
|
71
|
+
},
|
|
72
|
+
({action}) => cbk(null, action));
|
|
73
|
+
}],
|
|
74
|
+
|
|
75
|
+
// Ask for details about a new connectivity trigger
|
|
76
|
+
askForConnectivityTrigger: ['selectAction', ({selectAction}, cbk) => {
|
|
77
|
+
// Exit early when not adding a trigger
|
|
78
|
+
if (selectAction !== actionAddConnectivityTrigger) {
|
|
79
|
+
return cbk();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return ask({
|
|
83
|
+
message: 'Node public key to watch connectivity with?',
|
|
84
|
+
name: 'id',
|
|
85
|
+
type: 'input',
|
|
86
|
+
validate: input => {
|
|
87
|
+
if (!input) {
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (!isPublicKey(input)) {
|
|
92
|
+
return 'Enter a node identity public key';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return true;
|
|
96
|
+
},
|
|
97
|
+
},
|
|
98
|
+
({id}) => cbk(null, id));
|
|
99
|
+
}],
|
|
100
|
+
|
|
101
|
+
// Ask for details about a new follow trigger
|
|
102
|
+
askForFollowTrigger: ['selectAction', ({selectAction}, cbk) => {
|
|
103
|
+
// Exit early when not adding a trigger
|
|
104
|
+
if (selectAction !== actionAddFollowTrigger) {
|
|
105
|
+
return cbk();
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return ask({
|
|
109
|
+
message: 'Node public key to follow?',
|
|
110
|
+
name: 'id',
|
|
111
|
+
type: 'input',
|
|
112
|
+
validate: input => {
|
|
113
|
+
if (!input) {
|
|
114
|
+
return false;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (!isPublicKey(input)) {
|
|
118
|
+
return 'Enter a node identity public key to follow';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return true;
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
({id}) => cbk(null, id));
|
|
125
|
+
}],
|
|
126
|
+
|
|
127
|
+
// Get the list of triggers
|
|
128
|
+
getTriggers: ['selectAction', ({selectAction}, cbk) => {
|
|
129
|
+
// Exit early when not listing triggers
|
|
130
|
+
if (selectAction !== actionListTriggers) {
|
|
131
|
+
return cbk();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
logger.info({finding_triggers: true});
|
|
135
|
+
|
|
136
|
+
return getTriggers({lnd}, cbk);
|
|
137
|
+
}],
|
|
138
|
+
|
|
139
|
+
// Subscribe to triggers
|
|
140
|
+
subscribeToTriggers: ['selectAction', ({selectAction}, cbk) => {
|
|
141
|
+
// Exit early when not subscribing
|
|
142
|
+
if (selectAction !== actionSubscribeToTriggers) {
|
|
143
|
+
return cbk();
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const sub = subscribeToTriggers({lnds: [lnd]});
|
|
147
|
+
|
|
148
|
+
sub.on('channel_opened', opened => logger.info({opened}));
|
|
149
|
+
sub.on('peer_connected', connected => logger.info({connected}));
|
|
150
|
+
sub.on('peer_disconnected', disconnect => logger.info({disconnect}));
|
|
151
|
+
sub.on('error', err => cbk(err));
|
|
152
|
+
|
|
153
|
+
return logger.info({listening_for_trigger_events: true});
|
|
154
|
+
}],
|
|
155
|
+
|
|
156
|
+
// Create a new connectivity trigger
|
|
157
|
+
createConnectivityTrigger: [
|
|
158
|
+
'askForConnectivityTrigger',
|
|
159
|
+
({askForConnectivityTrigger}, cbk) =>
|
|
160
|
+
{
|
|
161
|
+
if (!askForConnectivityTrigger) {
|
|
162
|
+
return cbk();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return createConnectivityTrigger({
|
|
166
|
+
lnd,
|
|
167
|
+
id: askForConnectivityTrigger,
|
|
168
|
+
},
|
|
169
|
+
cbk);
|
|
170
|
+
}],
|
|
171
|
+
|
|
172
|
+
// Create a new follow trigger
|
|
173
|
+
createFollowTrigger: [
|
|
174
|
+
'askForFollowTrigger',
|
|
175
|
+
({askForFollowTrigger}, cbk) =>
|
|
176
|
+
{
|
|
177
|
+
if (!askForFollowTrigger) {
|
|
178
|
+
return cbk();
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
return createFollowNodeTrigger({lnd, id: askForFollowTrigger}, cbk);
|
|
182
|
+
}],
|
|
183
|
+
|
|
184
|
+
// Select a trigger from the list
|
|
185
|
+
selectTrigger: ['getTriggers', ({getTriggers}, cbk) => {
|
|
186
|
+
if (!getTriggers) {
|
|
187
|
+
return cbk();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (!getTriggers.length) {
|
|
191
|
+
return cbk([404, 'NoTriggersFound']);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return ask({
|
|
195
|
+
choices: getTriggers.map(({connectivity, follow, id}) => {
|
|
196
|
+
if (!!connectivity) {
|
|
197
|
+
return {
|
|
198
|
+
name: `Connectivity with ${connectivity.id}`,
|
|
199
|
+
value: id,
|
|
200
|
+
};
|
|
201
|
+
} else {
|
|
202
|
+
return {
|
|
203
|
+
name: `Following ${follow.id}`,
|
|
204
|
+
value: id,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
}),
|
|
208
|
+
message: 'Triggers:',
|
|
209
|
+
name: 'view',
|
|
210
|
+
type: 'list',
|
|
211
|
+
},
|
|
212
|
+
({view}) => cbk(null, view));
|
|
213
|
+
}],
|
|
214
|
+
|
|
215
|
+
// Trigger actions
|
|
216
|
+
triggerAction: ['selectTrigger', ({selectTrigger}, cbk) => {
|
|
217
|
+
// Exit early when no trigger is selected to take actions against
|
|
218
|
+
if (!selectTrigger) {
|
|
219
|
+
return cbk();
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
return ask({
|
|
223
|
+
choices: [{name: 'Delete Trigger', value: actionDeleteTrigger}],
|
|
224
|
+
message: 'Action?',
|
|
225
|
+
name: 'modify',
|
|
226
|
+
type: 'list',
|
|
227
|
+
},
|
|
228
|
+
({modify}) => cbk(null, selectTrigger));
|
|
229
|
+
}],
|
|
230
|
+
|
|
231
|
+
// Delete a trigger
|
|
232
|
+
deleteTrigger: ['triggerAction', ({triggerAction}, cbk) => {
|
|
233
|
+
// Exit early when not deleting a triger
|
|
234
|
+
if (!triggerAction) {
|
|
235
|
+
return cbk();
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
return cancelHodlInvoice({lnd, id: triggerAction}, cbk);
|
|
239
|
+
}],
|
|
240
|
+
},
|
|
241
|
+
returnResult({reject, resolve}, cbk));
|
|
242
|
+
});
|
|
243
|
+
};
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
const EventEmitter = require('events');
|
|
2
|
+
|
|
3
|
+
const asyncUntil = require('async/until');
|
|
4
|
+
const {decodeChanId} = require('bolt07');
|
|
5
|
+
const {getHeight} = require('ln-service');
|
|
6
|
+
const {getInvoices} = require('ln-service');
|
|
7
|
+
const {subscribeToGraph} = require('ln-service');
|
|
8
|
+
const {subscribeToInvoice} = require('ln-service');
|
|
9
|
+
const {subscribeToInvoices} = require('ln-service');
|
|
10
|
+
const {subscribeToPeers} = require('ln-service');
|
|
11
|
+
|
|
12
|
+
const decodeTrigger = require('./decode_trigger');
|
|
13
|
+
|
|
14
|
+
const defaultInvoicesLimit = 100;
|
|
15
|
+
const {keys} = Object;
|
|
16
|
+
|
|
17
|
+
/** Subscribe to trigger events
|
|
18
|
+
|
|
19
|
+
{
|
|
20
|
+
lnds: <Authenticated LND API Object>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@event 'channel_opened'
|
|
24
|
+
{
|
|
25
|
+
[capacity]: <Channel Token Capacity Number>
|
|
26
|
+
id: <Standard Format Channel Id String>
|
|
27
|
+
public_keys: [<Announcing Public Key>, <Target Public Key String>]
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@event 'peer_connected'
|
|
31
|
+
{
|
|
32
|
+
public_key: <Node Identity Public Key Hex String>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@event 'peer_disconnected'
|
|
36
|
+
{
|
|
37
|
+
public_key: <Node Identity Public Key Hex String>
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
@returns
|
|
41
|
+
<Event Emitter Object>
|
|
42
|
+
*/
|
|
43
|
+
module.exports = ({lnds}) => {
|
|
44
|
+
const channels = new Set();
|
|
45
|
+
const emitter = new EventEmitter();
|
|
46
|
+
const subs = [];
|
|
47
|
+
const triggers = {};
|
|
48
|
+
|
|
49
|
+
// Stop subscription when listeners are removed
|
|
50
|
+
emitter.on('removeListener', () => {
|
|
51
|
+
if (!!emitter.listenerCount('channel_opened')) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return cbk([400, 'RemovedAllListeners']);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Clean up when there is an error
|
|
59
|
+
const errored = err => {
|
|
60
|
+
subs.forEach(n => n.removeAllListeners());
|
|
61
|
+
|
|
62
|
+
if (!emitter.listenerCount('error')) {
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return emitter.emit('error', err);
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// Register trigger if present
|
|
70
|
+
const register = ({description, id}, lnd) => {
|
|
71
|
+
try {
|
|
72
|
+
decodeTrigger({encoded: description});
|
|
73
|
+
} catch (err) {
|
|
74
|
+
// Exit early when the invoice is not a trigger invoice
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
triggers[id] = decodeTrigger({encoded: description});
|
|
79
|
+
|
|
80
|
+
// Listen for the trigger invoice to be canceled to stop it
|
|
81
|
+
const sub = subscribeToInvoice({id, lnd});
|
|
82
|
+
|
|
83
|
+
subs.push(sub);
|
|
84
|
+
|
|
85
|
+
// Listen for an error on the invoice
|
|
86
|
+
sub.on('error', err => errored(err));
|
|
87
|
+
|
|
88
|
+
// Listen for the trigger to get canceled
|
|
89
|
+
sub.on('invoice_updated', invoice => {
|
|
90
|
+
if (!invoice.is_canceled) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
delete triggers[invoice.id];
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
return;
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
lnds.forEach(lnd => {
|
|
101
|
+
const graphSub = subscribeToGraph({lnd});
|
|
102
|
+
const invoicesSub = subscribeToInvoices({lnd});
|
|
103
|
+
const peersSub = subscribeToPeers({lnd});
|
|
104
|
+
let startHeight;
|
|
105
|
+
let token;
|
|
106
|
+
|
|
107
|
+
subs.push(graphSub);
|
|
108
|
+
subs.push(invoicesSub);
|
|
109
|
+
subs.push(peersSub);
|
|
110
|
+
|
|
111
|
+
getHeight({lnd}, (err, res) => {
|
|
112
|
+
if (!!err) {
|
|
113
|
+
return errored(err);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return startHeight = res.current_block_height;
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Listen for errors on the invoices subscription
|
|
120
|
+
invoicesSub.on('error', err => errored(err));
|
|
121
|
+
|
|
122
|
+
// Listen for new trigger invoices
|
|
123
|
+
invoicesSub.on('invoice_updated', updated => register(updated, lnd));
|
|
124
|
+
|
|
125
|
+
// Listen for errors on the graph subscription
|
|
126
|
+
graphSub.on('error', err => errored(err));
|
|
127
|
+
|
|
128
|
+
// Listen for updates to a channel that may match a trigger
|
|
129
|
+
graphSub.on('channel_updated', (update, cbk) => {
|
|
130
|
+
// Exit early when start height is not known yet
|
|
131
|
+
if (!startHeight) {
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Exit early when the channel exists in the set
|
|
136
|
+
if (channels.has(update.id)) {
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
// See if the channel matches a relevant trigger
|
|
141
|
+
const follows = keys(triggers)
|
|
142
|
+
.filter(id => !!triggers[id].follow)
|
|
143
|
+
.filter(id => update.public_keys.includes(triggers[id].follow.id));
|
|
144
|
+
|
|
145
|
+
// Exit early when this channel doesn't match any follow trigger
|
|
146
|
+
if (!follows.length) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const height = decodeChanId({channel: update.id}).block_height;
|
|
151
|
+
|
|
152
|
+
// Exit early when the channel id is less than the start height
|
|
153
|
+
if (height < startHeight) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Mark new channel as announced
|
|
158
|
+
channels.add(update.id);
|
|
159
|
+
|
|
160
|
+
// This is a new channel that confirmed after the start height
|
|
161
|
+
return emitter.emit('channel_opened', {
|
|
162
|
+
capacity: update.capacity,
|
|
163
|
+
id: update.id,
|
|
164
|
+
public_keys: update.public_keys,
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Listen for errors on the peers subscription
|
|
169
|
+
peersSub.on('error', err => errored(err));
|
|
170
|
+
|
|
171
|
+
// Listen for connected peers subscription
|
|
172
|
+
peersSub.on('connected', (update, cbk) => {
|
|
173
|
+
const id = update.public_key;
|
|
174
|
+
|
|
175
|
+
// See if the peer matches a relevant trigger
|
|
176
|
+
const follows = keys(triggers)
|
|
177
|
+
.filter(id => !!triggers[id].connectivity)
|
|
178
|
+
.filter(id => update.public_key === triggers[id].connectivity.id);
|
|
179
|
+
|
|
180
|
+
// Exit early when this peer doesn't match any connectivity trigger
|
|
181
|
+
if (!follows.length) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
return emitter.emit('peer_connected', update);
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
// Listen for disconnected peers subscription
|
|
189
|
+
peersSub.on('disconnected', (update, cbk) => {
|
|
190
|
+
const id = update.public_key;
|
|
191
|
+
|
|
192
|
+
// See if the peer matches a relevant trigger
|
|
193
|
+
const follows = keys(triggers)
|
|
194
|
+
.filter(id => !!triggers[id].connectivity)
|
|
195
|
+
.filter(id => update.public_key === triggers[id].connectivity.id);
|
|
196
|
+
|
|
197
|
+
// Exit early when this peer doesn't match any connectivity trigger
|
|
198
|
+
if (!follows.length) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return emitter.emit('peer_disconnected', update);
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// Register past trigger invoices
|
|
206
|
+
asyncUntil(
|
|
207
|
+
cbk => cbk(null, token === false),
|
|
208
|
+
cbk => {
|
|
209
|
+
return getInvoices({
|
|
210
|
+
lnd,
|
|
211
|
+
token,
|
|
212
|
+
is_unconfirmed: true,
|
|
213
|
+
limit: !token ? defaultInvoicesLimit : undefined,
|
|
214
|
+
},
|
|
215
|
+
(err, res) => {
|
|
216
|
+
if (!!err) {
|
|
217
|
+
return cbk(err);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
token = res.next || false;
|
|
221
|
+
|
|
222
|
+
res.invoices.forEach(invoice => register(invoice, lnd));
|
|
223
|
+
|
|
224
|
+
return cbk();
|
|
225
|
+
});
|
|
226
|
+
},
|
|
227
|
+
err => !!err ? errored(err) : null,
|
|
228
|
+
);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
return emitter;
|
|
232
|
+
};
|