bolt07 1.7.1 → 1.8.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/CHANGELOG.md CHANGED
@@ -1,6 +1,11 @@
1
1
  # Versions
2
2
 
3
- ## 1.7.1
3
+ ## 1.8.0
4
+
5
+ - `decodeSocket`: Decode a connection socket from hex data
6
+ - `encodeSocket`: Encode a connection socket to hex data
7
+
8
+ ## 1.7.4
4
9
 
5
10
  - `hopsFromChannels`: Derive policy hops from a list of channels
6
11
 
package/README.md CHANGED
@@ -3,8 +3,6 @@
3
3
  Utilities for working with Lightning Network [BOLT 07](https://github.com/lightningnetwork/lightning-rfc/blob/master/07-routing-gossip.md)
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/bolt07.svg)](https://badge.fury.io/js/bolt07)
6
- [![Coverage Status](https://coveralls.io/repos/github/alexbosworth/bolt07/badge.svg?branch=master)](https://coveralls.io/github/alexbosworth/bolt07?branch=master)
7
- [![Build Status](https://travis-ci.org/alexbosworth/bolt07.svg?branch=master)](https://travis-ci.org/alexbosworth/bolt07)
8
6
 
9
7
  ## Methods
10
8
 
@@ -0,0 +1,43 @@
1
+ const bufferAsHex = buffer => buffer.toString('hex');
2
+ const byte = 8;
3
+ const max = 255;
4
+ const read = n => 'abcdefghijklmnopqrstuvwxyz234567'.indexOf(n);
5
+ const size = len => (len * 5 / 8) | 0;
6
+ const word = 5;
7
+
8
+ /** Decode base32 encoded value
9
+
10
+ {
11
+ base32: <Base32 String>
12
+ }
13
+
14
+ @returns
15
+ {
16
+ data: <Hex Encoded Value String>
17
+ }
18
+ */
19
+ module.exports = ({base32}) => {
20
+ const b32 = base32.toLowerCase();
21
+ let bits = 0;
22
+ let index = 0;
23
+ const len = base32.length;
24
+ let value = 0;
25
+
26
+ const output = Buffer.alloc(size(len));
27
+
28
+ for (let i = 0; i < len; i++) {
29
+ value = (value << word) | read(b32[i]);
30
+
31
+ bits += word;
32
+
33
+ if (bits < byte) {
34
+ continue;
35
+ }
36
+
37
+ output[index++] = (value >>> (bits - byte)) & max;
38
+
39
+ bits -= byte;
40
+ }
41
+
42
+ return {data: bufferAsHex(output)};
43
+ };
@@ -0,0 +1,64 @@
1
+ const encodeBase32 = require('./encode_base32');
2
+
3
+ const hexAsIpV4 = hex => [...Buffer.from(hex.slice(0, 8), 'hex')].join('.');
4
+ const hexAsIpV6 = data => data.slice(0, 32).match(/.{1,4}/g).join(':');
5
+ const hexAsTorV3 = data => encodeBase32({data: data.slice(0, 70)}).base32;
6
+ const ip4HexLength = 12;
7
+ const ip6HexLength = 36;
8
+ const portFromData = data => Buffer.from(data.slice(-4), 'hex').readUInt16BE();
9
+ const portHexLength = 4;
10
+ const tor3HexLength = 74;
11
+
12
+ /** Decode an encoded socket
13
+
14
+ {
15
+ [ip4]: <Hex Encoded IpV4 Socket String>
16
+ [ip6]: <Hex Encoded IpV6 Socket String>
17
+ [tor3]: <Hex Encoded TorV3 Socket String>
18
+ }
19
+
20
+ @returns
21
+ {
22
+ [socket]: <Connection Socket String>
23
+ }
24
+ */
25
+ module.exports = ({ip4, ip6, tor3}) => {
26
+ const data = ip4 || ip6 || tor3;
27
+
28
+ if (!data) {
29
+ throw new Error('ExectedSocketDataToDecodeSocket');
30
+ }
31
+
32
+ const [, other] = [ip4, ip6, tor3].filter(n => !!n);
33
+
34
+ if (!!other) {
35
+ throw new Error('ExpectedOnlyOneSocketTypeToDecode');
36
+ }
37
+
38
+ if (data.length < portHexLength) {
39
+ throw new Error('ExpectedSocketDataWithPortToDecodeSocket');
40
+ }
41
+
42
+ // Read the port number as a UInt16 off of the end
43
+ const port = portFromData(data);
44
+
45
+ if (!!ip4 && ip4.length !== ip4HexLength) {
46
+ throw new Error('UnexpectedLengthForIpV4SocketData');
47
+ }
48
+
49
+ if (!!ip6 && ip6.length !== ip6HexLength) {
50
+ throw new Error('UnexpectedLengthForIpV6SocketData');
51
+ }
52
+
53
+ if (!!tor3 && tor3.length !== tor3HexLength) {
54
+ throw new Error('UnexpectedLengthForTorV3SocketData');
55
+ }
56
+
57
+ if (!!ip4) {
58
+ return {socket: `${hexAsIpV4(ip4)}:${port}`};
59
+ } else if (!!ip6) {
60
+ return {socket: `${hexAsIpV6(ip6)}:${port}`};
61
+ } else {
62
+ return {socket: `${hexAsTorV3(data)}.onion:${port}`};
63
+ }
64
+ };
@@ -0,0 +1,40 @@
1
+ const alphabet = 'abcdefghijklmnopqrstuvwxyz234567';
2
+ const byte = 8;
3
+ const hexAsBuffer = hex => Buffer.from(hex, 'hex');
4
+ const lastIndex = 31;
5
+ const word = 5;
6
+
7
+ /** Encode data as base32
8
+
9
+ {
10
+ data: <Data To Encode as Base32 Format Hex String>
11
+ }
12
+
13
+ @returns
14
+ {
15
+ base32: <Base32 Encoded String>
16
+ }
17
+ */
18
+ module.exports = ({data}) => {
19
+ let bits = 0;
20
+ const encoded = hexAsBuffer(data);
21
+ let base32 = '';
22
+ let value = 0;
23
+
24
+ for (let i = 0; i < encoded.length; i++) {
25
+ bits += byte;
26
+ value = (value << byte) | encoded[i];
27
+
28
+ while (bits >= word) {
29
+ base32 += alphabet[(value >>> (bits - word)) & lastIndex];
30
+
31
+ bits -= word;
32
+ }
33
+ }
34
+
35
+ if (bits > 0) {
36
+ base32 += alphabet[(value << (word - bits)) & lastIndex];
37
+ }
38
+
39
+ return {base32};
40
+ };
@@ -0,0 +1,60 @@
1
+ const decodeBase32 = require('./decode_base32');
2
+
3
+ const bufferAsHex = buffer => buffer.toString('hex');
4
+ const ipv4Match = /^((25[0-5]|(2[0-4]|1\d|[1-9]|)\d)(\.(?!$)|$)){4}$/;
5
+ const ipv6Match = /^[a-fA-F0-9:]+$/;
6
+ const portBuffer = () => Buffer.alloc(2);
7
+ const torV3Data = hostname => hostname.slice(0, 56);
8
+ const torV3Match = /[a-z2-7]{56}.onion/i;
9
+
10
+ /** Hex-encode a socket
11
+
12
+ {
13
+ socket: <Host:Port String>
14
+ }
15
+
16
+ @returns
17
+ {
18
+ [ip4]: <IPv4 Socket Hex Encoded String>
19
+ [ip6]: <IPv6 Socket Hex Encoded String>
20
+ [tor3]: <Tor V3 Socket Hex Encoded String>
21
+ }
22
+ */
23
+ module.exports = args => {
24
+ if (!args) {
25
+ throw new Error('ExpectedArgumentsToEncodeSocket');
26
+ }
27
+
28
+ if (!args.socket) {
29
+ throw new Error('ExpectedSocketToEncode');
30
+ }
31
+
32
+ const [port, ...host] = args.socket.split(':').reverse();
33
+
34
+ const hostname = host.reverse().join(':');
35
+
36
+ // The port will be written to a UInt16
37
+ const rawPort = portBuffer();
38
+
39
+ rawPort.writeUInt16BE(Number(port));
40
+
41
+ const encodedPort = bufferAsHex(rawPort);
42
+
43
+ if (ipv4Match.test(hostname)) {
44
+ const parts = Buffer.from(hostname.split('.').map(n => parseInt(n)));
45
+
46
+ return {ip4: `${parts.toString('hex')}${encodedPort}`};
47
+ }
48
+
49
+ if (ipv6Match.test(hostname)) {
50
+ return {ip6: `${hostname.split(':').join('')}${encodedPort}`};
51
+ }
52
+
53
+ if (torV3Match.test(hostname)) {
54
+ const {data} = decodeBase32({base32: torV3Data(hostname)});
55
+
56
+ return {tor3: `${data}${encodedPort}`};
57
+ }
58
+
59
+ return {};
60
+ };
@@ -0,0 +1,4 @@
1
+ const decodeSocket = require('./decode_socket');
2
+ const encodeSocket = require('./encode_socket');
3
+
4
+ module.exports = {decodeSocket, encodeSocket};
package/index.js CHANGED
@@ -1,7 +1,9 @@
1
1
  const {chanFormat} = require('./ids');
2
2
  const {chanNumber} = require('./ids');
3
3
  const {decodeChanId} = require('./ids');
4
+ const {decodeSocket} = require('./addresses');
4
5
  const {encodeChanId} = require('./ids');
6
+ const {encodeSocket} = require('./addresses');
5
7
  const {hopsFromChannels} = require('./routing');
6
8
  const {rawChanId} = require('./ids');
7
9
  const {routeFromChannels} = require('./routing');
@@ -11,7 +13,9 @@ module.exports = {
11
13
  chanFormat,
12
14
  chanNumber,
13
15
  decodeChanId,
16
+ decodeSocket,
14
17
  encodeChanId,
18
+ encodeSocket,
15
19
  hopsFromChannels,
16
20
  rawChanId,
17
21
  routeFromChannels,
package/package.json CHANGED
@@ -11,7 +11,7 @@
11
11
  },
12
12
  "description": "Utilities for working with bolt07 data formats",
13
13
  "devDependencies": {
14
- "tap": "14.11.0"
14
+ "@alexbosworth/tap": "15.0.10"
15
15
  },
16
16
  "keywords": [
17
17
  "bolt",
@@ -29,7 +29,7 @@
29
29
  "url": "https://github.com/alexbosworth/bolt07.git"
30
30
  },
31
31
  "scripts": {
32
- "test": "tap test/ids/*.js test/routing/*.js"
32
+ "test": "tap --branches=1 --functions=1 --lines=1 --statements=1 test/addresses/*.js test/ids/*.js test/routing/*.js"
33
33
  },
34
- "version": "1.7.1"
34
+ "version": "1.8.0"
35
35
  }
@@ -0,0 +1,67 @@
1
+ const {test} = require('@alexbosworth/tap');
2
+
3
+ const {decodeSocket} = require('./../../');
4
+
5
+ const tests = [
6
+ {
7
+ args: {},
8
+ description: 'A socket is expected',
9
+ error: 'ExectedSocketDataToDecodeSocket',
10
+ },
11
+ {
12
+ args: {ip4: true, ip6: true},
13
+ description: 'A single socket is expected',
14
+ error: 'ExpectedOnlyOneSocketTypeToDecode',
15
+ },
16
+ {
17
+ args: {ip4: '00'},
18
+ description: 'A socket requires sufficient bytes',
19
+ error: 'ExpectedSocketDataWithPortToDecodeSocket',
20
+ },
21
+ {
22
+ args: {ip4: '000000'},
23
+ description: 'Ip4 requires sufficient bytes',
24
+ error: 'UnexpectedLengthForIpV4SocketData',
25
+ },
26
+ {
27
+ args: {ip6: '000000'},
28
+ description: 'Ip6 requires sufficient bytes',
29
+ error: 'UnexpectedLengthForIpV6SocketData',
30
+ },
31
+ {
32
+ args: {tor3: '000000'},
33
+ description: 'Tor3 requires sufficient bytes',
34
+ error: 'UnexpectedLengthForTorV3SocketData',
35
+ },
36
+ {
37
+ args: {ip4: Buffer.alloc(6).toString('hex')},
38
+ description: 'Decode ip version 4 socket',
39
+ expected: {socket: '0.0.0.0:0'},
40
+ },
41
+ {
42
+ args: {ip6: Buffer.alloc(18).toString('hex')},
43
+ description: 'Decode ip version 6 socket',
44
+ expected: {socket: '0000:0000:0000:0000:0000:0000:0000:0000:0'},
45
+ },
46
+ {
47
+ args: {tor3: Buffer.alloc(37).toString('hex')},
48
+ description: 'Decode tor v3 onion socket',
49
+ expected: {
50
+ socket: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion:0',
51
+ },
52
+ },
53
+ ];
54
+
55
+ tests.forEach(({args, description, error, expected}) => {
56
+ return test(description, ({end, strictSame, throws}) => {
57
+ if (!!error) {
58
+ throws(() => decodeSocket(args), new Error(error), 'Got expected error');
59
+ } else {
60
+ const res = decodeSocket(args);
61
+
62
+ strictSame(res, expected, 'Got expected result');
63
+ }
64
+
65
+ return end();
66
+ });
67
+ });
@@ -0,0 +1,54 @@
1
+ const {test} = require('@alexbosworth/tap');
2
+
3
+ const {encodeSocket} = require('./../../');
4
+
5
+ const tests = [
6
+ {
7
+ args: undefined,
8
+ description: 'Arguments are required',
9
+ error: 'ExpectedArgumentsToEncodeSocket',
10
+ },
11
+ {
12
+ args: {},
13
+ description: 'A socket is required',
14
+ error: 'ExpectedSocketToEncode',
15
+ },
16
+ {
17
+ args: {socket: 'socket'},
18
+ description: 'Unknown socket type is not encoded',
19
+ expected: {},
20
+ },
21
+ {
22
+ args: {socket: '1.2.3.4:56789'},
23
+ description: 'Encode ip version 4 socket',
24
+ expected: {ip4: '01020304ddd5'},
25
+ },
26
+ {
27
+ args: {socket: '0000:1111:2222:3333:4444:5555:6666:12345'},
28
+ description: 'Encode ip version 6 socket',
29
+ expected: {ip6: '00001111222233334444555566663039'},
30
+ },
31
+ {
32
+ args: {
33
+ socket: 'aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa.onion:0',
34
+ },
35
+ description: 'Encode tor v3 socket',
36
+ expected: {
37
+ tor3: '00000000000000000000000000000000000000000000000000000000000000000000000000',
38
+ },
39
+ },
40
+ ];
41
+
42
+ tests.forEach(({args, description, error, expected}) => {
43
+ return test(description, ({end, strictSame, throws}) => {
44
+ if (!!error) {
45
+ throws(() => encodeSocket(args), new Error(error), 'Got expected error');
46
+ } else {
47
+ const res = encodeSocket(args);
48
+
49
+ strictSame(res, expected, 'Got expected result');
50
+ }
51
+
52
+ return end();
53
+ });
54
+ });
@@ -1,4 +1,4 @@
1
- const {test} = require('tap');
1
+ const {test} = require('@alexbosworth/tap');
2
2
 
3
3
  const {chanFormat} = require('./../../');
4
4
 
@@ -31,7 +31,7 @@ const tests = [
31
31
  ];
32
32
 
33
33
  tests.forEach(({args, description, error, expected}) => {
34
- return test(description, ({end, equals, throws}) => {
34
+ return test(description, ({end, equal, throws}) => {
35
35
  if (!!error) {
36
36
  throws(() => chanFormat(args), new Error(error), 'Got expected error');
37
37
 
@@ -40,7 +40,7 @@ tests.forEach(({args, description, error, expected}) => {
40
40
 
41
41
  const {channel} = chanFormat(args);
42
42
 
43
- equals(channel, expected.channel, 'Channel formatted returned');
43
+ equal(channel, expected.channel, 'Channel formatted returned');
44
44
 
45
45
  return end();
46
46
  });
@@ -1,4 +1,4 @@
1
- const {test} = require('tap');
1
+ const {test} = require('@alexbosworth/tap');
2
2
 
3
3
  const {chanNumber} = require('./../../');
4
4
 
@@ -31,7 +31,7 @@ const tests = [
31
31
  ];
32
32
 
33
33
  tests.forEach(({args, description, error, expected}) => {
34
- return test(description, ({end, equals, throws}) => {
34
+ return test(description, ({end, equal, throws}) => {
35
35
  if (!!error) {
36
36
  throws(() => chanNumber(args), new Error(error), 'Got expected error');
37
37
 
@@ -40,7 +40,7 @@ tests.forEach(({args, description, error, expected}) => {
40
40
 
41
41
  const {number} = chanNumber(args);
42
42
 
43
- equals(number, expected.number, 'Channel id number returned');
43
+ equal(number, expected.number, 'Channel id number returned');
44
44
 
45
45
  return end();
46
46
  });
@@ -1,4 +1,4 @@
1
- const {test} = require('tap');
1
+ const {test} = require('@alexbosworth/tap');
2
2
 
3
3
  const componentsFromBuffer = require('./../../ids/components_from_buffer');
4
4
 
@@ -16,7 +16,7 @@ const tests = [
16
16
  ];
17
17
 
18
18
  tests.forEach(({args, description, error, expected}) => {
19
- return test(description, ({end, equals, throws}) => {
19
+ return test(description, ({end, equal, throws}) => {
20
20
  if (!!error) {
21
21
  throws(() => componentsFromBuffer(args), new Error(error), 'Got error');
22
22
 
@@ -1,4 +1,4 @@
1
- const {test} = require('tap');
1
+ const {test} = require('@alexbosworth/tap');
2
2
 
3
3
  const {decodeChanId} = require('./../../');
4
4
 
@@ -1,4 +1,4 @@
1
- const {test} = require('tap');
1
+ const {test} = require('@alexbosworth/tap');
2
2
 
3
3
  const {encodeChanId} = require('./../../');
4
4
 
@@ -1,4 +1,4 @@
1
- const {test} = require('tap');
1
+ const {test} = require('@alexbosworth/tap');
2
2
 
3
3
  const {rawChanId} = require('./../../');
4
4
 
@@ -31,7 +31,7 @@ const tests = [
31
31
  ];
32
32
 
33
33
  tests.forEach(({args, description, error, expected}) => {
34
- return test(description, ({end, equals, throws}) => {
34
+ return test(description, ({end, equal, throws}) => {
35
35
  if (!!error) {
36
36
  throws(() => rawChanId(args), new Error(error), 'Got expected error');
37
37
 
@@ -40,7 +40,7 @@ tests.forEach(({args, description, error, expected}) => {
40
40
 
41
41
  const {id} = rawChanId(args);
42
42
 
43
- equals(id, expected.id, 'Raw channel id returned');
43
+ equal(id, expected.id, 'Raw channel id returned');
44
44
 
45
45
  return end();
46
46
  });
@@ -1,4 +1,4 @@
1
- const {test} = require('tap');
1
+ const {test} = require('@alexbosworth/tap');
2
2
 
3
3
  const betaChannels = require('./../fixtures/graph_beta').channels;
4
4
  const charlieChannels = require('./../fixtures/graph_charlie').channels;
@@ -274,13 +274,13 @@ const tests = [
274
274
  ];
275
275
 
276
276
  tests.forEach(({args, description, error, expected}) => {
277
- return test(description, ({deepIs, end, throws}) => {
277
+ return test(description, ({end, strictSame, throws}) => {
278
278
  if (!!error) {
279
279
  throws(() => hopsFromChannels(args), new Error(error), 'Got error');
280
280
  } else {
281
281
  const {hops} = hopsFromChannels(args);
282
282
 
283
- deepIs(hops, expected, 'Hops returned as expected');
283
+ strictSame(hops, expected, 'Hops returned as expected');
284
284
  }
285
285
 
286
286
  return end();
@@ -1,4 +1,4 @@
1
- const {test} = require('tap');
1
+ const {test} = require('@alexbosworth/tap');
2
2
 
3
3
  const policyFee = require('./../../routing/policy_fee');
4
4
 
@@ -1,4 +1,4 @@
1
- const {test} = require('tap');
1
+ const {test} = require('@alexbosworth/tap');
2
2
 
3
3
  const {routeFromChannels} = require('./../../');
4
4
 
@@ -938,7 +938,7 @@ const tests = [
938
938
  ];
939
939
 
940
940
  tests.forEach(({args, description, error, expected}) => {
941
- return test(description, ({deepIs, end, throws}) => {
941
+ return test(description, ({end, strictSame, throws}) => {
942
942
  if (!!error) {
943
943
  throws(() => routeFromChannels(args), new Error(error), 'Got error');
944
944
  } else {
@@ -947,7 +947,7 @@ tests.forEach(({args, description, error, expected}) => {
947
947
  delete route.payment;
948
948
  delete route.total_mtokens;
949
949
 
950
- deepIs(route, expected.route, 'Route is constructed as expected');
950
+ strictSame(route, expected.route, 'Route is constructed as expected');
951
951
  }
952
952
 
953
953
  return end();
@@ -1,4 +1,4 @@
1
- const {test} = require('tap');
1
+ const {test} = require('@alexbosworth/tap');
2
2
 
3
3
  const {routeFromHops} = require('./../../');
4
4
 
@@ -340,7 +340,7 @@ const tests = [
340
340
  ];
341
341
 
342
342
  tests.forEach(({args, description, error, expected}) => {
343
- return test(description, ({deepIs, end, throws}) => {
343
+ return test(description, ({end, strictSame, throws}) => {
344
344
  if (!!error) {
345
345
  throws(() => routeFromHops(args), new Error(error), 'Got expected err');
346
346
  } else {
@@ -349,7 +349,7 @@ tests.forEach(({args, description, error, expected}) => {
349
349
  delete route.payment;
350
350
  delete route.total_mtokens;
351
351
 
352
- deepIs(route, expected, 'Route is constructed as expected');
352
+ strictSame(route, expected, 'Route is constructed as expected');
353
353
  }
354
354
 
355
355
  return end();
package/.travis.yml DELETED
@@ -1,15 +0,0 @@
1
- language: node_js
2
-
3
- node_js:
4
- - 12
5
- - 10
6
-
7
- os:
8
- - linux
9
-
10
- cache:
11
- directories:
12
- - $HOME/.npm
13
-
14
- notifications:
15
- email: false