paid-services 3.1.2 → 3.2.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 +5 -0
- package/index.js +4 -0
- package/p2p/encode_peer_request.js +59 -0
- package/p2p/encode_peer_response.js +45 -0
- package/p2p/encode_response_code.js +49 -0
- package/p2p/index.js +4 -0
- package/p2p/make_peer_request.js +151 -0
- package/p2p/parse_request_message.js +103 -0
- package/p2p/parse_response_code.js +60 -0
- package/p2p/return_failure_response.js +43 -0
- package/p2p/return_success_response.js +47 -0
- package/p2p/service_peer_requests.js +100 -0
- package/package.json +3 -2
- package/test/integration/test_service_peer_requests.js +59 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
+
## Version 3.2.0
|
|
4
|
+
|
|
5
|
+
- `makePeerRequest`: Add method to make peer messaging requests
|
|
6
|
+
- `servicePeerRequests`: Add method to handle peer messaging requests
|
|
7
|
+
|
|
3
8
|
## Version 3.1.2
|
|
4
9
|
|
|
5
10
|
- `manageTrades`: Add method to create and interact with secret trades
|
package/index.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
const {confirmServiceUse} = require('./client');
|
|
2
2
|
const {getServiceSchema} = require('./client');
|
|
3
3
|
const {getServicesList} = require('./client');
|
|
4
|
+
const {makePeerRequest} = require('./p2p');
|
|
4
5
|
const {makeServiceRequest} = require('./client');
|
|
5
6
|
const {manageTrades} = require('./trades');
|
|
6
7
|
const {schema} = require('./services');
|
|
7
8
|
const {servicePaidRequests} = require('./server');
|
|
9
|
+
const {servicePeerRequests} = require('./p2p');
|
|
8
10
|
|
|
9
11
|
const serviceIds = schema.types;
|
|
10
12
|
|
|
@@ -12,8 +14,10 @@ module.exports = {
|
|
|
12
14
|
confirmServiceUse,
|
|
13
15
|
getServiceSchema,
|
|
14
16
|
getServicesList,
|
|
17
|
+
makePeerRequest,
|
|
15
18
|
makeServiceRequest,
|
|
16
19
|
manageTrades,
|
|
17
20
|
serviceIds,
|
|
18
21
|
servicePaidRequests,
|
|
22
|
+
servicePeerRequests,
|
|
19
23
|
};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const {encodeBigSize} = require('bolt01');
|
|
2
|
+
const {encodeTlvStream} = require('bolt01');
|
|
3
|
+
|
|
4
|
+
const bigSize = number => encodeBigSize({number}).encoded;
|
|
5
|
+
const code = null;
|
|
6
|
+
const encodeMessage = message => `626f73ff${message}`;
|
|
7
|
+
const headers = null;
|
|
8
|
+
const {isArray} = Array;
|
|
9
|
+
const recordsAsTlvStream = records => encodeTlvStream({records}).encoded;
|
|
10
|
+
const version = null;
|
|
11
|
+
|
|
12
|
+
/** Encode a peer request
|
|
13
|
+
|
|
14
|
+
{
|
|
15
|
+
id: <Request Id Hex String>
|
|
16
|
+
[records]: [{
|
|
17
|
+
type: <Type Number String>
|
|
18
|
+
value: <Value Hex String>
|
|
19
|
+
}]
|
|
20
|
+
type: <Type Number String>
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
@throws
|
|
24
|
+
<Error>
|
|
25
|
+
|
|
26
|
+
@returns
|
|
27
|
+
{
|
|
28
|
+
message: <Hex Encoded Mesage String>
|
|
29
|
+
}
|
|
30
|
+
*/
|
|
31
|
+
module.exports = ({id, records, type}) => {
|
|
32
|
+
if (!id) {
|
|
33
|
+
throw new Error('ExpectedRequestIdHexStringToEncodePeerRequest');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!!records && !isArray(records)) {
|
|
37
|
+
throw new Error('ExpectedRecordsArrayToEncodePeerRequest');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (!type) {
|
|
41
|
+
throw new Error('ExpectedRequestTypeToEncodePeerRequest');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Encode the request records
|
|
45
|
+
const response = [version, id, code, bigSize(type), headers, records]
|
|
46
|
+
.map((value, type) => ({type, value}))
|
|
47
|
+
.filter(n => !!n.value)
|
|
48
|
+
.map(({type, value}) => {
|
|
49
|
+
if (!isArray(value)) {
|
|
50
|
+
return {type, value};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return {type, value: recordsAsTlvStream(value)};
|
|
54
|
+
})
|
|
55
|
+
.map(({value, type}) => ({value, type: type.toString()}));
|
|
56
|
+
|
|
57
|
+
// Return the encoded request message
|
|
58
|
+
return {message: encodeMessage(recordsAsTlvStream(response))};
|
|
59
|
+
};
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
const {encodeBigSize} = require('bolt01');
|
|
2
|
+
const {encodeTlvStream} = require('bolt01');
|
|
3
|
+
|
|
4
|
+
const encodeMessage = message => `626f73ff${message}`;
|
|
5
|
+
const encodeResponseCode = require('./encode_response_code');
|
|
6
|
+
|
|
7
|
+
const headers = null;
|
|
8
|
+
const params = null;
|
|
9
|
+
const present = n => !!n;
|
|
10
|
+
const version = null;
|
|
11
|
+
|
|
12
|
+
/** Encode a response to a peer
|
|
13
|
+
|
|
14
|
+
{
|
|
15
|
+
[failure]: [
|
|
16
|
+
<Failure Code Number>
|
|
17
|
+
<Failure Message String>
|
|
18
|
+
]
|
|
19
|
+
id: <Request Id Hex String>
|
|
20
|
+
[records]: [{
|
|
21
|
+
type: <Type Number String>
|
|
22
|
+
value: <Value Hex Encoded String>
|
|
23
|
+
}]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@throws
|
|
27
|
+
<Error>
|
|
28
|
+
|
|
29
|
+
@returns
|
|
30
|
+
{
|
|
31
|
+
message: <Encoded Hex String>
|
|
32
|
+
}
|
|
33
|
+
*/
|
|
34
|
+
module.exports = ({failure, id, records}) => {
|
|
35
|
+
const code = encodeResponseCode({failure}).encoded;
|
|
36
|
+
const {encoded} = !!records ? encodeTlvStream({records}) : {};
|
|
37
|
+
|
|
38
|
+
const response = [version, id, code, params, headers, encoded]
|
|
39
|
+
.map((value, type) => ({type, value}))
|
|
40
|
+
.filter(n => !!n.value);
|
|
41
|
+
|
|
42
|
+
const message = encodeMessage(encodeTlvStream({records: response}).encoded);
|
|
43
|
+
|
|
44
|
+
return {message};
|
|
45
|
+
};
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const {encodeBigSize} = require('bolt01');
|
|
2
|
+
const {encodeTlvStream} = require('bolt01');
|
|
3
|
+
|
|
4
|
+
const codeForSuccess = 200;
|
|
5
|
+
const {isArray} = Array;
|
|
6
|
+
const string = n => n.toString();
|
|
7
|
+
const success = n => [{type: '0', value: encodeBigSize({number: n}).encoded}];
|
|
8
|
+
const utf8AsHex = utf8 => Buffer.from(utf8, 'utf8').toString('hex');
|
|
9
|
+
|
|
10
|
+
/** Encode a response code
|
|
11
|
+
|
|
12
|
+
{
|
|
13
|
+
[failure]: [
|
|
14
|
+
<Failure Code Number>
|
|
15
|
+
<Failure Message String>
|
|
16
|
+
]
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
@throws
|
|
20
|
+
<Error>
|
|
21
|
+
|
|
22
|
+
@returns
|
|
23
|
+
{
|
|
24
|
+
encoded: <Encoded Hex String>
|
|
25
|
+
}
|
|
26
|
+
*/
|
|
27
|
+
module.exports = ({failure}) => {
|
|
28
|
+
if (!failure) {
|
|
29
|
+
return encodeTlvStream({records: success(codeForSuccess)});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (!isArray(failure)) {
|
|
33
|
+
throw new Error('ExpectedFailureArrayToEncodeResponseCode');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const [code, message] = failure;
|
|
37
|
+
|
|
38
|
+
if (!code) {
|
|
39
|
+
throw new Error('ExpectedErrorCodeToEncodeResponseCode');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const number = code.toString();
|
|
43
|
+
|
|
44
|
+
const elements = [encodeBigSize({number}).encoded, utf8AsHex(message)];
|
|
45
|
+
|
|
46
|
+
const records = elements.map((value, type) => ({value, type: string(type)}));
|
|
47
|
+
|
|
48
|
+
return encodeTlvStream({records});
|
|
49
|
+
};
|
package/p2p/index.js
ADDED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
const {randomBytes} = require('crypto');
|
|
2
|
+
|
|
3
|
+
const asyncAuto = require('async/auto');
|
|
4
|
+
const {returnResult} = require('asyncjs-util');
|
|
5
|
+
const {sendMessageToPeer} = require('ln-service');
|
|
6
|
+
const {subscribeToPeerMessages} = require('ln-service');
|
|
7
|
+
|
|
8
|
+
const encodePeerRequest = require('./encode_peer_request');
|
|
9
|
+
const parseRequestFailure = (msg, cbk) => { cbk([503, msg]); return {}; };
|
|
10
|
+
const parseRequestMessage = require('./parse_request_message');
|
|
11
|
+
|
|
12
|
+
const makeId = () => randomBytes(32).toString('hex');
|
|
13
|
+
|
|
14
|
+
/** Make a peer request
|
|
15
|
+
|
|
16
|
+
{
|
|
17
|
+
lnd: <Authenticated LND API Object>
|
|
18
|
+
[records]: [{
|
|
19
|
+
type: <Type Number String>
|
|
20
|
+
value: <Value Hex Encoded String>
|
|
21
|
+
}]
|
|
22
|
+
[timeout]: <Peer Response Timeout Milliseconds>
|
|
23
|
+
to: <Node Id Public Key Hex String>
|
|
24
|
+
type: <Request Type Number String>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@throws error via cbk or Promise
|
|
28
|
+
[408, PeerRequestTimeout]
|
|
29
|
+
|
|
30
|
+
@returns via cbk or Promise
|
|
31
|
+
{
|
|
32
|
+
[records]: [{
|
|
33
|
+
type: <Type Number String>
|
|
34
|
+
value: <Value Hex Encoded String>
|
|
35
|
+
}]
|
|
36
|
+
}
|
|
37
|
+
*/
|
|
38
|
+
module.exports = ({lnd, records, timeout, to, type}, cbk) => {
|
|
39
|
+
return new Promise((resolve, reject) => {
|
|
40
|
+
return asyncAuto({
|
|
41
|
+
// Check arguments
|
|
42
|
+
validate: cbk => {
|
|
43
|
+
if (!lnd) {
|
|
44
|
+
return cbk([400, 'ExpectedLndToMakePeerRequest']);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (!to) {
|
|
48
|
+
return cbk([400, 'ExpectedPublicKeyToMakePeerRequestTo']);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (!type) {
|
|
52
|
+
return cbk([400, 'ExpectedTypeToMakePeerRequest']);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return cbk();
|
|
56
|
+
},
|
|
57
|
+
|
|
58
|
+
// Generate an id for the request
|
|
59
|
+
id: ['validate', ({}, cbk) => cbk(null, makeId())],
|
|
60
|
+
|
|
61
|
+
// Encode the request message
|
|
62
|
+
message: ['id', ({id}, cbk) => {
|
|
63
|
+
try {
|
|
64
|
+
// Encode the peer request elements into an encoded request
|
|
65
|
+
const {message} = encodePeerRequest({id, records, type});
|
|
66
|
+
|
|
67
|
+
return cbk(null, message);
|
|
68
|
+
} catch (err) {
|
|
69
|
+
return cbk([400, err.message]);
|
|
70
|
+
}
|
|
71
|
+
}],
|
|
72
|
+
|
|
73
|
+
// Make the request to the peer
|
|
74
|
+
request: ['id', 'message', ({id, message}, cbk) => {
|
|
75
|
+
// Listen to incoming peer messages
|
|
76
|
+
const sub = subscribeToPeerMessages({lnd});
|
|
77
|
+
|
|
78
|
+
// Stop waiting for a response if things have gone on too long
|
|
79
|
+
const timer = (() => {
|
|
80
|
+
return !timeout ? null : setTimeout(() => {
|
|
81
|
+
sub.removeAllListeners();
|
|
82
|
+
|
|
83
|
+
return cbk([408, 'PeerRequestTimeout']);
|
|
84
|
+
},
|
|
85
|
+
timeout);
|
|
86
|
+
})();
|
|
87
|
+
|
|
88
|
+
// An error on the subscription
|
|
89
|
+
sub.on('error', err => {
|
|
90
|
+
// Stop listening
|
|
91
|
+
sub.removeAllListeners();
|
|
92
|
+
|
|
93
|
+
// Stop the timeout timer
|
|
94
|
+
clearTimeout(timer);
|
|
95
|
+
|
|
96
|
+
return cbk([503, 'PeerRequestResponseListenerFailed', {err}]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// Wait for a message on the request id
|
|
100
|
+
sub.on('message_received', received => {
|
|
101
|
+
// Exit early on messages from other nodes
|
|
102
|
+
if (received.public_key !== to) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const {response} = (() => {
|
|
107
|
+
try {
|
|
108
|
+
return parseRequestMessage(received);
|
|
109
|
+
} catch (err) {
|
|
110
|
+
return parseRequestFailure(err.message, cbk);
|
|
111
|
+
}
|
|
112
|
+
})();
|
|
113
|
+
|
|
114
|
+
// Exit early when this message is not a reponse to the request
|
|
115
|
+
if (!response || response.id !== id) {
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Stop the timeout timer
|
|
120
|
+
clearTimeout(timer);
|
|
121
|
+
|
|
122
|
+
// Remove listener for response
|
|
123
|
+
sub.removeAllListeners();
|
|
124
|
+
|
|
125
|
+
// Exit early when the server responds with a failure code
|
|
126
|
+
if (!!response.failure) {
|
|
127
|
+
return cbk(response.failure);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
return cbk(null, {records: response.records});
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// Send the peer request
|
|
134
|
+
return sendMessageToPeer({lnd, message, public_key: to}, err => {
|
|
135
|
+
if (!!err) {
|
|
136
|
+
// Stop timeout when there was an error sending
|
|
137
|
+
clearTimeout(timer);
|
|
138
|
+
|
|
139
|
+
// Stop listening for a response when there was an error
|
|
140
|
+
sub.removeAllListeners();
|
|
141
|
+
|
|
142
|
+
return cbk(err);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
return;
|
|
146
|
+
});
|
|
147
|
+
}],
|
|
148
|
+
},
|
|
149
|
+
returnResult({reject, resolve, of: 'request'}, cbk));
|
|
150
|
+
});
|
|
151
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
const {decodeBigSize} = require('bolt01');
|
|
2
|
+
const {decodeTlvStream} = require('bolt01');
|
|
3
|
+
|
|
4
|
+
const parseResponseCode = require('./parse_response_code');
|
|
5
|
+
|
|
6
|
+
const defaultRecords = {value: ''};
|
|
7
|
+
const findRequestRecords = records => records.find(n => n.type === '5');
|
|
8
|
+
const findResponseCodeRecord = records => records.find(n => n.type === '2');
|
|
9
|
+
const findRequestIdRecord = records => records.find(n => n.type === '1');
|
|
10
|
+
const findTypeRecord = records => records.find(n => n.type === '3');
|
|
11
|
+
const findVersionRecord = records => records.find(n => n.type === '0');
|
|
12
|
+
const idHexLength = 32 * 2;
|
|
13
|
+
const isRequest = message => message.toLowerCase().startsWith('626f73ff');
|
|
14
|
+
const requestData = message => message.slice('626f73ff'.length);
|
|
15
|
+
const requestType = 32768;
|
|
16
|
+
const res = records => records.map(({type, value}) => ({type, value}));
|
|
17
|
+
|
|
18
|
+
/** Parse a peer request format message
|
|
19
|
+
|
|
20
|
+
Messages can either be requests or responses
|
|
21
|
+
|
|
22
|
+
{
|
|
23
|
+
message: <Encoded Request Message Hex String>
|
|
24
|
+
type: <Type Number String>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@throws
|
|
28
|
+
<Error>
|
|
29
|
+
|
|
30
|
+
@returns
|
|
31
|
+
{
|
|
32
|
+
[response]: {
|
|
33
|
+
[failure]: [
|
|
34
|
+
<Failure Code Number>
|
|
35
|
+
<Failure Code String>
|
|
36
|
+
]
|
|
37
|
+
id: <Request Id Hex String>
|
|
38
|
+
records: [{
|
|
39
|
+
type: <Type Number String>
|
|
40
|
+
value: <Value Hex Encoded String>
|
|
41
|
+
}]
|
|
42
|
+
}
|
|
43
|
+
[request]: {
|
|
44
|
+
id: <Request Id Hex String>
|
|
45
|
+
records: [{
|
|
46
|
+
type: <Type Number String>
|
|
47
|
+
value: <Value Hex Encoded String>
|
|
48
|
+
}]
|
|
49
|
+
type: <Type Number String>
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
*/
|
|
53
|
+
module.exports = ({message, type}) => {
|
|
54
|
+
// Exit early when the message type or message content is not request type
|
|
55
|
+
if (type !== requestType || !isRequest(message)) {
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const {records} = decodeTlvStream({encoded: requestData(message)});
|
|
60
|
+
|
|
61
|
+
const version = findVersionRecord(records);
|
|
62
|
+
|
|
63
|
+
if (!!version) {
|
|
64
|
+
throw new Error('UnexpectedVersionNumberOfRequestMessage');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const idRecord = findRequestIdRecord(records);
|
|
68
|
+
|
|
69
|
+
if (!idRecord || idRecord.value.length !== idHexLength) {
|
|
70
|
+
throw new Error('ExpectedRequestIdInRequestMessage');
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const recordsRecord = findRequestRecords(records) || defaultRecords;
|
|
74
|
+
|
|
75
|
+
const responseCodeRecord = findResponseCodeRecord(records);
|
|
76
|
+
const typeRecord = findTypeRecord(records);
|
|
77
|
+
|
|
78
|
+
// Requests have request types and responses have response codes
|
|
79
|
+
if (!typeRecord && !responseCodeRecord) {
|
|
80
|
+
throw new Error('ExpectedEitherRequestParametersOrResponseCode');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Exit early when this is a response to a request
|
|
84
|
+
if (!!responseCodeRecord) {
|
|
85
|
+
const {failure} = parseResponseCode({encoded: responseCodeRecord.value});
|
|
86
|
+
|
|
87
|
+
return {
|
|
88
|
+
response: {
|
|
89
|
+
failure,
|
|
90
|
+
id: idRecord.value,
|
|
91
|
+
records: res(decodeTlvStream({encoded: recordsRecord.value}).records),
|
|
92
|
+
},
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return {
|
|
97
|
+
request: {
|
|
98
|
+
id: idRecord.value,
|
|
99
|
+
records: res(decodeTlvStream({encoded: recordsRecord.value}).records),
|
|
100
|
+
type: decodeBigSize({encoded: typeRecord.value}).decoded,
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
};
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const {decodeBigSize} = require('bolt01');
|
|
2
|
+
const {decodeTlvStream} = require('bolt01');
|
|
3
|
+
|
|
4
|
+
const defaultRecord = {value: ''};
|
|
5
|
+
const findCodeRecord = records => records.find(n => n.type === '0');
|
|
6
|
+
const findMessageRecord = records => records.find(n => n.type === '1');
|
|
7
|
+
const hexAsUtf8 = hex => Buffer.from(hex, 'hex').toString('utf8');
|
|
8
|
+
const isFailure = n => n > BigInt(400);
|
|
9
|
+
const isTooBig = n => n > BigInt(Number.MAX_SAFE_INTEGER);
|
|
10
|
+
const isTooSmall = n => n < BigInt(100);
|
|
11
|
+
|
|
12
|
+
/** Parse a response code TLV stream
|
|
13
|
+
|
|
14
|
+
{
|
|
15
|
+
encoded: <Encoded Response Code Value Hex String>
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@throws
|
|
19
|
+
<Error>
|
|
20
|
+
|
|
21
|
+
@returns
|
|
22
|
+
{
|
|
23
|
+
[failure]: [
|
|
24
|
+
<Failure Code Number>
|
|
25
|
+
<Failure Code String>
|
|
26
|
+
]
|
|
27
|
+
}
|
|
28
|
+
*/
|
|
29
|
+
module.exports = ({encoded}) => {
|
|
30
|
+
if (!encoded) {
|
|
31
|
+
throw new Error('ExpectedResponseCodeValueToParseResponseCode');
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const {records} = decodeTlvStream({encoded});
|
|
35
|
+
|
|
36
|
+
const codeRecord = findCodeRecord(records);
|
|
37
|
+
|
|
38
|
+
if (!codeRecord) {
|
|
39
|
+
throw new Error('ExpectedCodeRecordToParseResponseCode');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const code = BigInt(decodeBigSize({encoded: codeRecord.value}).decoded);
|
|
43
|
+
|
|
44
|
+
if (isTooBig(code)) {
|
|
45
|
+
throw new Error('UnexpectedlyLargeResponseCodeInResponse');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (isTooSmall(code)) {
|
|
49
|
+
throw new Error('UnexpectedlySmallResponseCodeInResponse');
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Exit early when the response code is not a failure
|
|
53
|
+
if (!isFailure(code)) {
|
|
54
|
+
return {};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const messageRecord = findMessageRecord(records) || defaultRecord;
|
|
58
|
+
|
|
59
|
+
return {failure: [Number(code), hexAsUtf8(messageRecord.value)]};
|
|
60
|
+
};
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
const {sendMessageToPeer} = require('ln-service');
|
|
2
|
+
|
|
3
|
+
const encodePeerResponse = require('./encode_peer_response');
|
|
4
|
+
|
|
5
|
+
const defaultServerError = [500, 'InternalServiceError'];
|
|
6
|
+
|
|
7
|
+
/** Return a failure response handler
|
|
8
|
+
|
|
9
|
+
{
|
|
10
|
+
from: <Requst From Node Id Public Key Hex String>
|
|
11
|
+
id: <Request Id Hex String>
|
|
12
|
+
lnd: <Authenticated LND API Object>
|
|
13
|
+
service: {
|
|
14
|
+
error: <Report Error Function>
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@returns
|
|
19
|
+
<Handle Failure Response Function>
|
|
20
|
+
*/
|
|
21
|
+
module.exports = ({from, id, lnd, service}) => {
|
|
22
|
+
return failure => {
|
|
23
|
+
// Encode the message to send to the peer
|
|
24
|
+
const {message} = (() => {
|
|
25
|
+
try {
|
|
26
|
+
return encodePeerResponse({id, failure});
|
|
27
|
+
} catch (err) {
|
|
28
|
+
service.error([503, err.message]);
|
|
29
|
+
|
|
30
|
+
return encodePeerResponse({id, failure: defaultServerError});
|
|
31
|
+
}
|
|
32
|
+
})();
|
|
33
|
+
|
|
34
|
+
// Tell the peer about the failure
|
|
35
|
+
return sendMessageToPeer({lnd, message, public_key: from}, err => {
|
|
36
|
+
if (!!err) {
|
|
37
|
+
return service.error([503, 'ErrorSendingFailureMessage', {err}]);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return;
|
|
41
|
+
});
|
|
42
|
+
};
|
|
43
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
const {sendMessageToPeer} = require('ln-service');
|
|
2
|
+
|
|
3
|
+
const encodePeerResponse = require('./encode_peer_response');
|
|
4
|
+
|
|
5
|
+
const defaultServerError = [500, 'InternalServiceError'];
|
|
6
|
+
|
|
7
|
+
/** Return handler for success response
|
|
8
|
+
|
|
9
|
+
{
|
|
10
|
+
from: <Requst From Node Id Public Key Hex String>
|
|
11
|
+
id: <Request Id Hex String>
|
|
12
|
+
lnd: <Authenticated LND API Object>
|
|
13
|
+
service: {
|
|
14
|
+
error: <Report Error Function>
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
@returns
|
|
19
|
+
<Handle Success Response Function>
|
|
20
|
+
*/
|
|
21
|
+
module.exports = ({from, id, lnd, service}) => {
|
|
22
|
+
return args => {
|
|
23
|
+
if (!args) {
|
|
24
|
+
throw new Error('ExpectedSuccessArgumentsToReturnSuccessPeerResponse');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Encode the message to send to the peer
|
|
28
|
+
const {message} = (() => {
|
|
29
|
+
try {
|
|
30
|
+
return encodePeerResponse({id, records: args.records});
|
|
31
|
+
} catch (err) {
|
|
32
|
+
service.error([503, err.message]);
|
|
33
|
+
|
|
34
|
+
return encodePeerResponse({id, failure: defaultServerError});
|
|
35
|
+
}
|
|
36
|
+
})();
|
|
37
|
+
|
|
38
|
+
// Tell the peer about the success
|
|
39
|
+
return sendMessageToPeer({lnd, message, public_key: from}, err => {
|
|
40
|
+
if (!!err) {
|
|
41
|
+
return service.error([503, 'ErrorSendingSuccessMessage', {err}]);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return;
|
|
45
|
+
});
|
|
46
|
+
};
|
|
47
|
+
};
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
const {sendMessageToPeer} = require('ln-service');
|
|
2
|
+
const {subscribeToPeerMessages} = require('ln-service');
|
|
3
|
+
|
|
4
|
+
const defaultServerError = [500, 'InternalServiceError'];
|
|
5
|
+
const encodePeerResponse = require('./encode_peer_response');
|
|
6
|
+
const parseRequestFailure = (report, n) => { report([503, n]); return {}; };
|
|
7
|
+
const parseRequestMessage = require('./parse_request_message');
|
|
8
|
+
const returnFailureResponse = require('./return_failure_response');
|
|
9
|
+
const returnSuccessResponse = require('./return_success_response');
|
|
10
|
+
|
|
11
|
+
/** Get a p2p message listener
|
|
12
|
+
|
|
13
|
+
{
|
|
14
|
+
lnd: <Authenticated LND API Object>
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
@returns
|
|
18
|
+
{
|
|
19
|
+
error: <Add an Error Listener Function>
|
|
20
|
+
request: <Add A Request Listener For Type Function>
|
|
21
|
+
stop: <Stop Listening Function> ({}) => {};
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
request:
|
|
25
|
+
{
|
|
26
|
+
type: <Type Number String>
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
@returns via cbk
|
|
30
|
+
{
|
|
31
|
+
from: <Request From Node Id Hex Encoded Public Key String>
|
|
32
|
+
[records]: [{
|
|
33
|
+
type: <Type Number String>
|
|
34
|
+
value: <Value Hex String>
|
|
35
|
+
}]
|
|
36
|
+
}
|
|
37
|
+
{
|
|
38
|
+
failure: <Return Failure Function>
|
|
39
|
+
success: <Return Success Function>
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
failure:
|
|
43
|
+
[
|
|
44
|
+
<Return Error Code Number>
|
|
45
|
+
<Return Error Message String>
|
|
46
|
+
]
|
|
47
|
+
|
|
48
|
+
success:
|
|
49
|
+
{
|
|
50
|
+
[records]: [{
|
|
51
|
+
type: <Type Number String>
|
|
52
|
+
value: <Value Hex String>
|
|
53
|
+
}]
|
|
54
|
+
}
|
|
55
|
+
*/
|
|
56
|
+
module.exports = ({lnd}) => {
|
|
57
|
+
const listeners = {};
|
|
58
|
+
const service = {error: () => {}};
|
|
59
|
+
const sub = subscribeToPeerMessages({lnd});
|
|
60
|
+
|
|
61
|
+
sub.on('message_received', received => {
|
|
62
|
+
const from = received.public_key;
|
|
63
|
+
const {message} = received;
|
|
64
|
+
const {type} = received;
|
|
65
|
+
|
|
66
|
+
// Parse out the p2p request message details
|
|
67
|
+
const {request} = (() => {
|
|
68
|
+
try {
|
|
69
|
+
return parseRequestMessage({message, type});
|
|
70
|
+
} catch (err) {
|
|
71
|
+
return parseRequestFailure(service.error, err.message);
|
|
72
|
+
}
|
|
73
|
+
})();
|
|
74
|
+
|
|
75
|
+
// Exit early when the received message isn't actually a p2p request
|
|
76
|
+
if (!request) {
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Exit early when nothing is listening for this type
|
|
81
|
+
if (!listeners[request.type]) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const {id} = request;
|
|
86
|
+
const req = {from, records: request.records};
|
|
87
|
+
|
|
88
|
+
const failure = returnFailureResponse({from, id, lnd, service});
|
|
89
|
+
const success = returnSuccessResponse({from, id, lnd, service});
|
|
90
|
+
|
|
91
|
+
// Call the listener to tell it about the request
|
|
92
|
+
return listeners[request.type](req, {failure, success});
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
error: cbk => service[error] = cbk,
|
|
97
|
+
request: ({type}, cbk) => listeners[type] = cbk,
|
|
98
|
+
stop: ({}) => sub.removeAllListeners(),
|
|
99
|
+
};
|
|
100
|
+
};
|
package/package.json
CHANGED
|
@@ -11,11 +11,12 @@
|
|
|
11
11
|
"asyncjs-util": "1.2.7",
|
|
12
12
|
"bolt01": "1.2.3",
|
|
13
13
|
"invoices": "2.0.2",
|
|
14
|
-
"ln-service": "
|
|
14
|
+
"ln-service": "53.1.2"
|
|
15
15
|
},
|
|
16
16
|
"description": "Lightning Paid Services library",
|
|
17
17
|
"devDependencies": {
|
|
18
18
|
"@alexbosworth/tap": "15.0.10",
|
|
19
|
+
"ln-docker-daemons": "2.1.0",
|
|
19
20
|
"mock-lnd": "1.4.1",
|
|
20
21
|
"secp256k1": "4.0.2"
|
|
21
22
|
},
|
|
@@ -36,5 +37,5 @@
|
|
|
36
37
|
"scripts": {
|
|
37
38
|
"test": "tap --branches=1 --functions=1 --lines=1 --statements=1 -t 60 test/actions/*.js test/client/*.js test/config/*.js test/records/*.js test/respond/*.js test/server/*.js test/server/*.js test/services/*.js"
|
|
38
39
|
},
|
|
39
|
-
"version": "3.
|
|
40
|
+
"version": "3.2.0"
|
|
40
41
|
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
const {addPeer} = require('ln-service');
|
|
2
|
+
const {spawnLightningCluster} = require('ln-docker-daemons');
|
|
3
|
+
const {test} = require('@alexbosworth/tap');
|
|
4
|
+
|
|
5
|
+
const {makePeerRequest} = require('./../../');
|
|
6
|
+
const {servicePeerRequests} = require('./../../');
|
|
7
|
+
|
|
8
|
+
const failure = [402, 'PurchaseRequired'];
|
|
9
|
+
const failureType = '1';
|
|
10
|
+
const records = [{type: '1', value: '01'}];
|
|
11
|
+
const size = 2;
|
|
12
|
+
const type = '0';
|
|
13
|
+
|
|
14
|
+
// Adding a listener for peer requests should allow responding to peer requests
|
|
15
|
+
test(`Listen for peer requests`, async ({end, equal, strictSame}) => {
|
|
16
|
+
const {kill, nodes} = await spawnLightningCluster({size});
|
|
17
|
+
|
|
18
|
+
const [{id, lnd}, target] = nodes;
|
|
19
|
+
|
|
20
|
+
await addPeer({lnd, public_key: target.id, socket: target.socket});
|
|
21
|
+
|
|
22
|
+
// Start the server and respond to requests
|
|
23
|
+
const listener = servicePeerRequests({lnd});
|
|
24
|
+
|
|
25
|
+
listener.request({type}, (req, res) => res.success({records}));
|
|
26
|
+
listener.request({type: failureType}, (req, res) => res.failure(failure));
|
|
27
|
+
|
|
28
|
+
// Make a request to the server and get a success response
|
|
29
|
+
try {
|
|
30
|
+
const got = await makePeerRequest({
|
|
31
|
+
type,
|
|
32
|
+
lnd: target.lnd,
|
|
33
|
+
timeout: 1000,
|
|
34
|
+
to: id,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
strictSame(records, got.records, 'Got response records');
|
|
38
|
+
} catch (err) {
|
|
39
|
+
equal(err, null, 'Expected no error making peer request');
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Make a request to the server and get a failure response
|
|
43
|
+
try {
|
|
44
|
+
const got = await makePeerRequest({
|
|
45
|
+
type: failureType,
|
|
46
|
+
lnd: target.lnd,
|
|
47
|
+
timeout: 1000,
|
|
48
|
+
to: id,
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
strictSame(got, null, 'Expected failure response for failure type');
|
|
52
|
+
} catch (err) {
|
|
53
|
+
strictSame(err, failure, 'Got failure response for failure type');
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await kill({});
|
|
57
|
+
|
|
58
|
+
return end();
|
|
59
|
+
});
|