paid-services 3.3.0 → 3.6.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.
Files changed (54) hide show
  1. package/.prettierignore +2 -0
  2. package/CHANGELOG.md +17 -0
  3. package/capacity/accept_capacity_change.js +3 -3
  4. package/capacity/ask_for_change_details.js +34 -1
  5. package/capacity/capacity_change_requests.js +9 -0
  6. package/capacity/change_channel_capacity.js +7 -1
  7. package/capacity/encode_change_request.js +8 -1
  8. package/capacity/fee_for_replacement.js +1 -1
  9. package/capacity/get_capacity_change_requests.js +2 -1
  10. package/capacity/get_capacity_replacement.js +1 -1
  11. package/capacity/initiate_capacity_change.js +5 -5
  12. package/capacity/parse_capacity_change_request.js +8 -1
  13. package/capacity/propose_capacity_change.js +4 -1
  14. package/p2p/service_peer_requests.js +10 -2
  15. package/package.json +10 -9
  16. package/service_types.json +16 -0
  17. package/services/response_for_relay.js +0 -1
  18. package/test/capacity/test_capacity_change_requests.js +1 -0
  19. package/test/capacity/test_encode_change_request.js +5 -0
  20. package/test/capacity/test_parse_capacity_change_request.js +1 -0
  21. package/test/integration/test_accept_capacity_change.js +50 -14
  22. package/test/integration/test_change_channel_capacity.js +7 -1
  23. package/test/integration/test_decrease_channel_capacity.js +16 -7
  24. package/test/integration/test_increase_channel_capacity.js +4 -1
  25. package/test/trades/test_decode_basic_trade.js +40 -0
  26. package/test/trades/test_decode_open_trade.js +50 -0
  27. package/test/trades/test_encode_open_trade.js +35 -0
  28. package/test/trades/test_encode_trade.js +37 -0
  29. package/trades/accept_trade.js +61 -0
  30. package/trades/create_trade.js +149 -37
  31. package/trades/decode_anchored_trade.js +56 -0
  32. package/trades/decode_basic_trade.js +45 -0
  33. package/trades/decode_open_trade.js +69 -0
  34. package/trades/decode_trade.js +48 -66
  35. package/trades/decode_trade_secret.js +89 -0
  36. package/trades/encode_anchored_trade.js +35 -0
  37. package/trades/encode_open_trade.js +89 -0
  38. package/trades/encode_trade.js +48 -33
  39. package/trades/encode_trade_secret.js +50 -0
  40. package/trades/finalize_trade_secret.js +123 -0
  41. package/trades/find_trade.js +339 -0
  42. package/trades/manage_trade.js +61 -39
  43. package/trades/manage_trades.js +209 -2
  44. package/trades/network_from_network_record.js +3 -3
  45. package/trades/network_record_from_network.js +36 -0
  46. package/trades/network_record_from_request.js +2 -1
  47. package/trades/node_as_record.js +98 -0
  48. package/trades/record_as_node.js +113 -0
  49. package/trades/request_trade_by_id.js +95 -0
  50. package/trades/service_anchored_trades.js +181 -0
  51. package/trades/service_open_trade.js +172 -0
  52. package/trades/service_trade_requests.js +255 -0
  53. package/trades/trade_from_invoice.js +51 -0
  54. package/capacity/service_types.json +0 -6
@@ -0,0 +1,69 @@
1
+ const {decodeTlvStream} = require('bolt01');
2
+
3
+ const recordAsNode = require('./record_as_node');
4
+
5
+ const findIdRecord = records => records.find(n => n.type === '5');
6
+ const findNodesRecord = records => records.find(n => n.type === '4');
7
+ const {isArray} = Array;
8
+
9
+ /** Decode open trade records
10
+
11
+ An open ended trade is a pointer to a node or nodes that can create trade
12
+ invoices.
13
+
14
+ 4: <Nodes Records>
15
+ [5]: <Trade Identifier>
16
+
17
+ {
18
+ network: <Network Name String>
19
+ records: [{
20
+ type: <Type Number String>
21
+ value: <Value Hex Encoded String>
22
+ }]
23
+ }
24
+
25
+ @throws
26
+ <Error>
27
+
28
+ @returns
29
+ {
30
+ [id]: <Trade Identifier Hex String>
31
+ network: <Network Name String>
32
+ nodes: [{
33
+ [high_channel]: <High Key Channel Id String>
34
+ [low_channel]: <Low Key Channel Id String>
35
+ [node]: {
36
+ id: <Node Public Key Id Hex String>
37
+ sockets: [<Peer Socket String>]
38
+ }
39
+ }]
40
+ }
41
+ */
42
+ module.exports = ({network, records}) => {
43
+ if (!network) {
44
+ throw new Error('ExpectedNetworkNameToDecodeOpenTrade');
45
+ }
46
+
47
+ if (!isArray(records)) {
48
+ throw new Error('ExpectedArrayOfRecordsToDecodeOpenTrade');
49
+ }
50
+
51
+ const idRecord = findIdRecord(records);
52
+ const nodesRecord = findNodesRecord(records);
53
+
54
+ if (!nodesRecord) {
55
+ throw new Error('ExpectedNodesRecordToDecodeOpenTradeDetails');
56
+ }
57
+
58
+ const nodeRecords = decodeTlvStream({encoded: nodesRecord.value}).records;
59
+
60
+ if (!nodeRecords.length) {
61
+ throw new Error('ExpectedNodeRecordsForOpenTrade');
62
+ }
63
+
64
+ return {
65
+ network,
66
+ id: !!idRecord ? idRecord.value : undefined,
67
+ nodes: nodeRecords.map(({value}) => recordAsNode({encoded: value})),
68
+ };
69
+ };
@@ -1,32 +1,49 @@
1
+ const {decodeBigSize} = require('bolt01');
1
2
  const {decodeTlvStream} = require('bolt01');
2
3
 
4
+ const decodeOpenTrade = require('./decode_open_trade');
5
+ const decodeTradeSecret = require('./decode_trade_secret');
3
6
  const networkFromNetworkRecord = require('./network_from_network_record');
4
- const {requestRecordsAsRequest} = require('./../records');
5
7
 
6
- const findAuth = records => records.find(n => n.type === '1');
7
- const findCipher = records => records.find(n => n.type === '0');
8
- const findDetails = records => records.find(n => n.type === '3');
9
- const findEncrypt = records => records.find(n => n.type === '0');
10
8
  const findNetwork = records => records.find(n => n.type === '1');
11
- const findRequest = records => records.find(n => n.type === '2');
12
- const findVersion = records => records.find(n => n.type === '0');
9
+ const findVer = records => records.find(n => n.type === '0') || {value: '00'};
10
+ const findRequestRecord = records => records.find(n => n.type === '2');
13
11
  const isTrade = trade => trade.toLowerCase().startsWith('626f73ff');
12
+ const isValidVersion = version => BigInt(version) <= BigInt(1);
14
13
  const tradeData = trade => trade.slice('626f73ff'.length);
15
14
 
16
- /** Encode a trade
15
+ /** Decode a trade record
16
+
17
+ [0]: <Version Record>
18
+ [1]: <Network Name Record>
19
+ [2]: <Payment Request Record>
20
+ [3]: <Trade Details Record
21
+ [4]: <Nodes Records>
22
+ [5]: <Trade Identifier>
17
23
 
18
24
  {
19
- trade: <Hex Encoded Trade String>
25
+ trade: <Trade Record Hex String>
20
26
  }
21
27
 
22
- @throws
23
- <Error>
24
-
25
28
  @returns
26
29
  {
27
- auth: <Encrypted Payload Auth Hex String>
28
- payload: <Preimage Encrypted Payload Hex String>
29
- request: <BOLT 11 Payment Request String>
30
+ [connect]: {
31
+ [id]: <Reference Trade Id Hex String>
32
+ network: <Network Name String>
33
+ nodes: [{
34
+ [high_channel]: <High Key Channel Id String>
35
+ [low_channel]: <Low Key Channel Id String>
36
+ [node]: {
37
+ id: <Node Public Key Id Hex String>
38
+ sockets: [<Peer Socket String>]
39
+ }
40
+ }]
41
+ }
42
+ [secret]: {
43
+ auth: <Encrypted Payload Auth Hex String>
44
+ payload: <Preimage Encrypted Payload Hex String>
45
+ request: <BOLT 11 Payment Request String>
46
+ }
30
47
  }
31
48
  */
32
49
  module.exports = ({trade}) => {
@@ -45,64 +62,29 @@ module.exports = ({trade}) => {
45
62
  }
46
63
 
47
64
  // Decode the overall packet
48
- const tradeRecords = decodeTlvStream({encoded: tradeData(trade)}).records;
65
+ const {records} = decodeTlvStream({encoded: tradeData(trade)});
49
66
 
50
67
  // Get the trade version
51
- const version = findVersion(tradeRecords);
52
-
53
- if (!!version) {
54
- throw new Error('UnexpectedVersionOfTradeData');
55
- }
56
-
57
- // Get the network record
58
- const networkRecord = findNetwork(tradeRecords) || {};
59
-
60
- // Get the payment request record
61
- const requestRecord = findRequest(tradeRecords);
62
-
63
- if (!requestRecord) {
64
- throw new Error('ExpectedRequestRecordInTradeData');
65
- }
66
-
67
- const {request} = requestRecordsAsRequest({
68
- network: networkFromNetworkRecord({value: networkRecord.value}),
69
- encoded: requestRecord.value,
70
- });
68
+ const versionRecord = findVer(records);
71
69
 
72
- // Get the trade details
73
- const detailsRecord = findDetails(tradeRecords);
70
+ // Decode the version number
71
+ const version = decodeBigSize({encoded: versionRecord.value}).decoded;
74
72
 
75
- if (!detailsRecord) {
76
- throw new Error('ExpctedDetailsRecordToDecodeTrade');
73
+ // Too-high versions cannot be parsed
74
+ if (!isValidVersion(version)) {
75
+ throw new Error('ExpectedKnownVersionToDecodeTrade');
77
76
  }
78
77
 
79
- // Trade details
80
- const details = decodeTlvStream({encoded: detailsRecord.value}).records;
81
-
82
- // Encode the encrypted data
83
- const encryptedRecord = findEncrypt(details);
84
-
85
- if (!encryptedRecord) {
86
- throw new Error('ExpectedEncryptedRecordToDecodeTrade');
87
- }
88
-
89
- const encrypted = decodeTlvStream({encoded: encryptedRecord.value}).records;
90
-
91
- const encryptedDataRecord = findCipher(encrypted);
92
-
93
- if (!encryptedDataRecord) {
94
- throw new Error('ExpectedEncryptedDataRecordToDecodeTrade');
95
- }
78
+ // Get the network record
79
+ const networkRecord = findNetwork(records) || {};
96
80
 
97
- const authDataRecord = findAuth(encrypted);
81
+ // Decode the network record
82
+ const {network} = networkFromNetworkRecord({value: networkRecord.value});
98
83
 
99
- if (!authDataRecord) {
100
- throw new Error('ExpectedAuthDataRecordToDecodeTrade');
84
+ // A trade is either a trade secret to someone or an open trade offer
85
+ if (!!findRequestRecord(records)) {
86
+ return {secret: decodeTradeSecret({network, records})};
87
+ } else {
88
+ return {connect: decodeOpenTrade({network, records})};
101
89
  }
102
-
103
- return {
104
- request,
105
- auth: authDataRecord.value,
106
- payload: encryptedDataRecord.value,
107
- };
108
90
  };
@@ -0,0 +1,89 @@
1
+ const {decodeTlvStream} = require('bolt01');
2
+
3
+ const {requestRecordsAsRequest} = require('./../records');
4
+
5
+ const findAuth = records => records.find(n => n.type === '1');
6
+ const findCipher = records => records.find(n => n.type === '0');
7
+ const findDetails = records => records.find(n => n.type === '3');
8
+ const findEncrypt = records => records.find(n => n.type === '0');
9
+ const findRequest = records => records.find(n => n.type === '2');
10
+ const {isArray} = Array;
11
+
12
+ /** Decode a trade secret
13
+
14
+ 2: <Payment Request Record>
15
+ 3: <Trade Details Record
16
+
17
+ {
18
+ network: <Network Name String>
19
+ records: [{
20
+ type: <Type Number String>
21
+ value: <Hex Encoded Value String>
22
+ }]
23
+ }
24
+
25
+ @throws
26
+ <Error>
27
+
28
+ @returns
29
+ {
30
+ auth: <Encrypted Payload Auth Hex String>
31
+ payload: <Preimage Encrypted Payload Hex String>
32
+ request: <BOLT 11 Payment Request String>
33
+ }
34
+ */
35
+ module.exports = ({network, records}) => {
36
+ if (!network) {
37
+ throw new Error('ExpectedNetworkNameToDecodeTradeSecret');
38
+ }
39
+
40
+ if (!isArray(records)) {
41
+ throw new Error('ExpectedArrayOfRecordsToDecodeTradeSecret');
42
+ }
43
+
44
+ // Get the payment request record
45
+ const requestRecord = findRequest(records);
46
+
47
+ if (!requestRecord) {
48
+ throw new Error('ExpectedRequestRecordInTradeData');
49
+ }
50
+
51
+ const encoded = requestRecord.value;
52
+
53
+ // Get the trade details
54
+ const detailsRecord = findDetails(records);
55
+
56
+ if (!detailsRecord) {
57
+ throw new Error('ExpctedDetailsRecordToDecodeTrade');
58
+ }
59
+
60
+ // Trade details
61
+ const details = decodeTlvStream({encoded: detailsRecord.value}).records;
62
+
63
+ // Encode the encrypted data
64
+ const encryptedRecord = findEncrypt(details);
65
+
66
+ if (!encryptedRecord) {
67
+ throw new Error('ExpectedEncryptedRecordToDecodeTrade');
68
+ }
69
+
70
+ const encrypted = decodeTlvStream({encoded: encryptedRecord.value}).records;
71
+
72
+ const encryptedDataRecord = findCipher(encrypted);
73
+
74
+ if (!encryptedDataRecord) {
75
+ throw new Error('ExpectedEncryptedDataRecordToDecodeTrade');
76
+ }
77
+
78
+ const authDataRecord = findAuth(encrypted);
79
+
80
+ if (!authDataRecord) {
81
+ throw new Error('ExpectedAuthDataRecordToDecodeTrade');
82
+ }
83
+
84
+ return {
85
+ request: requestRecordsAsRequest({encoded, network}).request,
86
+ auth: authDataRecord.value,
87
+ payload: encryptedDataRecord.value,
88
+ };
89
+ };
@@ -0,0 +1,35 @@
1
+ const {encodeTlvStream} = require('bolt01');
2
+
3
+ const anchorPrefix = 'anchor-trade-secret:';
4
+ const descriptionRecord = value => !!value ? ({value, type: '1'}) : undefined;
5
+ const secretRecord = value => ({value, type: '0'});
6
+ const utf8AsHex = utf8 => Buffer.from(utf8, 'utf8').toString('hex');
7
+ const withPrefix = (pre, v) => pre + Buffer.from(v, 'hex').toString('base64');
8
+
9
+ /** Encode a trade into an anchor blob
10
+
11
+ This will look like anchored-trade-secret:<base64-encoded-data>
12
+
13
+ The data encodes a TLV:
14
+ 0: <Trade Secret>
15
+ [1]: <Trade Description>
16
+
17
+ {
18
+ [description]: <Open Trade Description String>
19
+ secret: <Open Trade Secret String>
20
+ }
21
+
22
+ @returns
23
+ {
24
+ encoded: <Encoded Anchor Trade Data Blob String>
25
+ }
26
+ */
27
+ module.exports = ({description, secret}) => {
28
+ const records = [secretRecord(secret), descriptionRecord(description)]
29
+ .filter(n => !!n)
30
+ .map(({type, value}) => ({type, value: utf8AsHex(value)}));
31
+
32
+ const {encoded} = encodeTlvStream({records});
33
+
34
+ return {encoded: withPrefix(anchorPrefix, encoded)};
35
+ };
@@ -0,0 +1,89 @@
1
+ const {encodeTlvStream} = require('bolt01');
2
+
3
+ const networkRecordFromNetwork = require('./network_record_from_network');
4
+ const nodeAsRecord = require('./node_as_record');
5
+
6
+ const asTrade = hex => `626f73ff${hex}`;
7
+ const idType = '5';
8
+ const {isArray} = Array;
9
+ const networkRecordType = '1';
10
+ const nodeType = '4';
11
+ const versionRecord = '01';
12
+ const versionType = '0';
13
+
14
+ /** Encode records for an open ended trade
15
+
16
+ An open ended trade is a pointer to a node or nodes that can create trade
17
+ invoices. At the minimum it has a pointer to a node.
18
+
19
+ 0: version (v1)
20
+ [1]: network name
21
+ 4: nodes records
22
+ [5]: trade identifier
23
+
24
+ {
25
+ [id]: <Trade Identifier Hex String>
26
+ network: <Network Name String>
27
+ nodes: [{
28
+ channels: [{
29
+ id: <Standard Format Channel Id String>
30
+ partner_public_key: <Node Public Key Hex String>
31
+ }]
32
+ id: <Node Public Key Id Hex String>
33
+ sockets: [<Peer Socket String>]
34
+ }]
35
+ }
36
+
37
+ @throws
38
+ <Error>
39
+
40
+ @returns
41
+ {
42
+ trade: <Encoded Open Ended Trade Hex String>
43
+ }
44
+ */
45
+ module.exports = ({id, network, nodes}) => {
46
+ if (!network) {
47
+ throw new Error('ExpectedNetworkNameToEncodeOpenTrade');
48
+ }
49
+
50
+ if (!isArray(nodes)) {
51
+ throw new Error('ExpectedArrayOfNodesToEncodeOpenTrade');
52
+ }
53
+
54
+ if (!nodes.length) {
55
+ throw new Error('ExpectedNodeToReferToInOpenTrade');
56
+ }
57
+
58
+ // Add trade version record
59
+ const records = [{type: versionType, value: versionRecord}];
60
+
61
+ // Add a network name record if required
62
+ if (!!networkRecordFromNetwork({network}).value) {
63
+ records.push({
64
+ type: networkRecordType,
65
+ value: networkRecordFromNetwork({network}).value,
66
+ });
67
+ }
68
+
69
+ // A trade id refers to a specific trade
70
+ if (!!id) {
71
+ records.push({type: idType, value: id});
72
+ }
73
+
74
+ // Map the nodes to node records
75
+ const nodeRecords = nodes.map(({channels, id, sockets}, type) => {
76
+ return {
77
+ type: type.toString(),
78
+ value: nodeAsRecord({channels, id, sockets}).encoded,
79
+ };
80
+ });
81
+
82
+ // Add the node records to the open trade
83
+ records.push({
84
+ type: nodeType,
85
+ value: encodeTlvStream({records: nodeRecords}).encoded,
86
+ });
87
+
88
+ return {trade: asTrade(encodeTlvStream({records}).encoded)};
89
+ };
@@ -1,50 +1,65 @@
1
- const {encodeTlvStream} = require('bolt01');
1
+ const encodeOpenTrade = require('./encode_open_trade');
2
+ const encodeTradeSecret = require('./encode_trade_secret');
2
3
 
3
- const networkRecordFromRequest = require('./network_record_from_request');
4
- const {requestAsRequestRecords} = require('./../records');
4
+ /** Encode v1 trade records
5
5
 
6
- const asTrade = hex => `626f73ff${hex}`;
6
+ A v1 trade is either a qualified trade with a request, or an open ended trade
7
+ with connection details.
7
8
 
8
- /** Encode a trade
9
+ 0: version (v1)
10
+ [1]: network
11
+ [2]: payment request
12
+ [3]: trade details
13
+ [4]: nodes records
14
+ [5]: trade identifier
9
15
 
10
16
  {
11
- auth: <Encrypted Payload Auth Hex String>
12
- payload: <Preimage Encrypted Payload Hex String>
13
- request: <BOLT 11 Payment Request String>
17
+ [connect]: {
18
+ [id]: <Reference Trade Id Hex String>
19
+ network: <Network Name String>
20
+ nodes: [{
21
+ channels: [<Standard Format Channel Id String>]
22
+ id: <Node Public Key Id Hex String>
23
+ [sockets]: [<Peer Socket String>]
24
+ }]
25
+ }
26
+ [secret]: {
27
+ auth: <Encrypted Payload Auth Hex String>
28
+ payload: <Preimage Encrypted Payload Hex String>
29
+ request: <BOLT 11 Payment Request String>
30
+ }
14
31
  }
15
32
 
33
+ @throws
34
+ <Error>
35
+
16
36
  @returns
17
37
  {
18
38
  trade: <Hex Encoded Trade String>
19
39
  }
20
40
  */
21
- module.exports = ({auth, payload, request}, cbk) => {
22
- // Encode the trade record
23
- const tradeRecords = [{
24
- type: '2',
25
- value: requestAsRequestRecords({request}).encoded,
26
- }];
27
-
28
- // Add a network name record if required
29
- if (!!networkRecordFromRequest({request}).value) {
30
- tradeRecords.push({
31
- type: '1',
32
- value: networkRecordFromRequest({request}).value,
33
- });
41
+ module.exports = ({connect, secret}) => {
42
+ if (!connect && !secret) {
43
+ throw new Error('ExpectedEitherConnectDetailsOrTradeSecret');
34
44
  }
35
45
 
36
- // Encode the encrypted data
37
- const encryptionRecords = encodeTlvStream({
38
- records: [{type: '0', value: payload}, {type: '1', value: auth}],
39
- });
40
-
41
- // Encode the details of the trade
42
- const detailsRecords = encodeTlvStream({
43
- records: [{type: '0', value: encryptionRecords.encoded}],
44
- });
46
+ if (!!connect && !!secret) {
47
+ throw new Error('ExpectedEitherConnectDetailsOrTradeSecretNotBoth');
48
+ }
45
49
 
46
- // Add the trade details to the trade
47
- tradeRecords.push({type: '3', value: detailsRecords.encoded});
50
+ // Exit early when this is an open ended trade
51
+ if (!!connect) {
52
+ return encodeOpenTrade({
53
+ id: connect.id,
54
+ network: connect.network,
55
+ nodes: connect.nodes,
56
+ });
57
+ }
48
58
 
49
- return {trade: asTrade(encodeTlvStream({records: tradeRecords}).encoded)};
59
+ // Encode the trade secret
60
+ return encodeTradeSecret({
61
+ auth: secret.auth,
62
+ payload: secret.payload,
63
+ request: secret.request,
64
+ });
50
65
  };
@@ -0,0 +1,50 @@
1
+ const {encodeTlvStream} = require('bolt01');
2
+
3
+ const networkRecordFromRequest = require('./network_record_from_request');
4
+ const {requestAsRequestRecords} = require('./../records');
5
+
6
+ const asTrade = hex => `626f73ff${hex}`;
7
+
8
+ /** Encode a trade
9
+
10
+ {
11
+ auth: <Encrypted Payload Auth Hex String>
12
+ payload: <Preimage Encrypted Payload Hex String>
13
+ request: <BOLT 11 Payment Request String>
14
+ }
15
+
16
+ @returns
17
+ {
18
+ trade: <Hex Encoded Trade String>
19
+ }
20
+ */
21
+ module.exports = ({auth, payload, request}, cbk) => {
22
+ // Encode the trade record
23
+ const tradeRecords = [{
24
+ type: '2',
25
+ value: requestAsRequestRecords({request}).encoded,
26
+ }];
27
+
28
+ // Add a network name record if required
29
+ if (!!networkRecordFromRequest({request}).value) {
30
+ tradeRecords.push({
31
+ type: '1',
32
+ value: networkRecordFromRequest({request}).value,
33
+ });
34
+ }
35
+
36
+ // Encode the encrypted data
37
+ const encryptionRecords = encodeTlvStream({
38
+ records: [{type: '0', value: payload}, {type: '1', value: auth}],
39
+ });
40
+
41
+ // Encode the details of the trade
42
+ const detailsRecords = encodeTlvStream({
43
+ records: [{type: '0', value: encryptionRecords.encoded}],
44
+ });
45
+
46
+ // Add the trade details to the trade
47
+ tradeRecords.push({type: '3', value: detailsRecords.encoded});
48
+
49
+ return {trade: asTrade(encodeTlvStream({records: tradeRecords}).encoded)};
50
+ };