bolt07 1.7.4 → 1.8.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 CHANGED
@@ -1,5 +1,10 @@
1
1
  # Versions
2
2
 
3
+ ## 1.8.2
4
+
5
+ - `decodeSocket`: Decode a connection socket from hex data
6
+ - `encodeSocket`: Encode a connection socket to hex data
7
+
3
8
  ## 1.7.4
4
9
 
5
10
  - `hopsFromChannels`: Derive policy hops from a list of channels
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
@@ -7,11 +7,11 @@
7
7
  "url": "https://github.com/alexbosworth/bolt07/issues"
8
8
  },
9
9
  "dependencies": {
10
- "bn.js": "5.2.0"
10
+ "bn.js": "5.2.1"
11
11
  },
12
12
  "description": "Utilities for working with bolt07 data formats",
13
13
  "devDependencies": {
14
- "@alexbosworth/tap": "15.0.10"
14
+ "@alexbosworth/tap": "15.0.11"
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.4"
34
+ "version": "1.8.2"
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
+ });
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