ln-service 59.3.6 → 59.4.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,5 +1,15 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
+
## 59.4.0
|
|
4
|
+
|
|
5
|
+
- `sendMessage`: Add method to send a network onion message
|
|
6
|
+
- `subscribeToMessages`: Add method to listen to network onion messages
|
|
7
|
+
|
|
8
|
+
## 59.3.7
|
|
9
|
+
|
|
10
|
+
- Add support for LND 0.21.3
|
|
11
|
+
- Add support for LND 0.20.4
|
|
12
|
+
|
|
3
13
|
## 59.3.6
|
|
4
14
|
|
|
5
15
|
- `parsePaymentRequest`: Add `signet` support
|
package/README.md
CHANGED
|
@@ -9,8 +9,8 @@ through npm.
|
|
|
9
9
|
|
|
10
10
|
Supported LND versions:
|
|
11
11
|
|
|
12
|
-
- v0.21.0-beta to v0.21.
|
|
13
|
-
- v0.20.0-beta to v0.20.
|
|
12
|
+
- v0.21.0-beta to v0.21.3-beta
|
|
13
|
+
- v0.20.0-beta to v0.20.4-beta
|
|
14
14
|
|
|
15
15
|
For typescript-ready methods, check out https://github.com/alexbosworth/lightning#readme
|
|
16
16
|
|
|
@@ -257,6 +257,7 @@ for `unlocker` methods.
|
|
|
257
257
|
- [restrictMacaroon](#restrictmacaroon) - Add limitations to a macaroon
|
|
258
258
|
- [revokeAccess](#revokeaccess) - Revoke all access macaroons given to an id
|
|
259
259
|
- [routeFromChannels](#routefromchannels) - Convert channel series to a route
|
|
260
|
+
- [sendMessage](#sendmessage) - Send a network onion message
|
|
260
261
|
- [sendMessageToPeer](#sendmessagetopeer) - Send a custom message to a peer
|
|
261
262
|
- [sendToChainAddress](#sendtochainaddress) - Send on-chain to an address
|
|
262
263
|
- [sendToChainAddresses](#sendtochainaddresses) - Send on-chain to addresses
|
|
@@ -281,6 +282,7 @@ for `unlocker` methods.
|
|
|
281
282
|
- [subscribeToGraph](#subscribetograph) - Subscribe to network graph updates
|
|
282
283
|
- [subscribeToInvoice](#subscribetoinvoice) - Subscribe to invoice updates
|
|
283
284
|
- [subscribeToInvoices](#subscribetoinvoices) - Subscribe to all invoices
|
|
285
|
+
- [subscribeToMessages](#subscribetomessages) - Subscribe to network messages
|
|
284
286
|
- [subscribeToOpenRequests](#subscribetoopenrequests) - Approve open requests
|
|
285
287
|
- [subscribeToPastPayment](#subscribetopastpayment) - Subscribe to a payment
|
|
286
288
|
- [subscribeToPastPayments](#subscribetopastpayments) - Subscribe to all sent
|
|
@@ -5140,6 +5142,53 @@ const res = routeFromChannels({channels, destination, height, mtokens});
|
|
|
5140
5142
|
const {route} = res;
|
|
5141
5143
|
```
|
|
5142
5144
|
|
|
5145
|
+
### sendMessage
|
|
5146
|
+
|
|
5147
|
+
Send a generic message over the network
|
|
5148
|
+
|
|
5149
|
+
The receiver of the message needs to first publish an inbound path to them,
|
|
5150
|
+
this is supplied via `inbound`. To send, add a series of relaying node ids
|
|
5151
|
+
that support message passing, to a landmark node provided by the receiver.
|
|
5152
|
+
|
|
5153
|
+
This method is not supported in LND 0.20.4 and below
|
|
5154
|
+
|
|
5155
|
+
{
|
|
5156
|
+
inbound: [{
|
|
5157
|
+
encrypted_data: <Encrypted Data Hex String>
|
|
5158
|
+
relay_key: <Blinded Relaying Public Key Into Destination Hex String>
|
|
5159
|
+
}]
|
|
5160
|
+
key: <Message Path Key Hex String>
|
|
5161
|
+
lnd: <Authenticated LND API Object>
|
|
5162
|
+
[message]: {
|
|
5163
|
+
type: <Message Payload Record Type Number String>
|
|
5164
|
+
value: <Message Payload Hex String>
|
|
5165
|
+
}
|
|
5166
|
+
outbound: [<Relaying Node Public Key Out of Source Hex String>]
|
|
5167
|
+
[reply]: [<Reply Path Relaying Node Public Key Back To Sender Hex String>]
|
|
5168
|
+
}
|
|
5169
|
+
|
|
5170
|
+
@returns via cbk or Promise
|
|
5171
|
+
{
|
|
5172
|
+
[reply]: <Reply Identifier Hex String>
|
|
5173
|
+
}
|
|
5174
|
+
|
|
5175
|
+
Example:
|
|
5176
|
+
|
|
5177
|
+
```node
|
|
5178
|
+
const {sendMessage} = require('ln-service');
|
|
5179
|
+
|
|
5180
|
+
const {reply} = await sendMessage({
|
|
5181
|
+
lnd,
|
|
5182
|
+
inbound: blindedPath.path,
|
|
5183
|
+
key: blindedPath.key,
|
|
5184
|
+
outbound: [peer.public_key, blindedPath.introduction_node],
|
|
5185
|
+
message: {type, value},
|
|
5186
|
+
reply: [peer.public_key, own.public_key],
|
|
5187
|
+
});
|
|
5188
|
+
|
|
5189
|
+
// Wait for message reply on `reply` id
|
|
5190
|
+
```
|
|
5191
|
+
|
|
5143
5192
|
### sendMessageToPeer
|
|
5144
5193
|
|
|
5145
5194
|
Send a custom message to a connected peer
|
|
@@ -6165,6 +6214,67 @@ const sub = subscribeToInvoices({lnd});
|
|
|
6165
6214
|
const [lastUpdatedInvoice] = await once(sub, 'invoice_updated');
|
|
6166
6215
|
```
|
|
6167
6216
|
|
|
6217
|
+
### subscribeToMessages
|
|
6218
|
+
|
|
6219
|
+
Subscribe to received onion messages
|
|
6220
|
+
|
|
6221
|
+
Requires `offchain:read` permission
|
|
6222
|
+
|
|
6223
|
+
This method is not supported in LND 0.20.4 and below
|
|
6224
|
+
|
|
6225
|
+
{
|
|
6226
|
+
lnd: <Authenticated LND API Object>
|
|
6227
|
+
}
|
|
6228
|
+
|
|
6229
|
+
@returns
|
|
6230
|
+
<EventEmitter Object>
|
|
6231
|
+
|
|
6232
|
+
@event 'message_received'
|
|
6233
|
+
{
|
|
6234
|
+
encrypted: <Encrypted Data Hex String>
|
|
6235
|
+
key: <Path Key Hex String>
|
|
6236
|
+
[message]: {
|
|
6237
|
+
type: <Message Payload Record Type Number String>
|
|
6238
|
+
value: <Message Payload Hex String>
|
|
6239
|
+
}
|
|
6240
|
+
onion: <Onion Packet Hex String>
|
|
6241
|
+
[reply]: {
|
|
6242
|
+
inbound: [{
|
|
6243
|
+
encrypted_data: <Encrypted Data Hex String>
|
|
6244
|
+
relay_key: <Blinded Relay Key Hex String>
|
|
6245
|
+
}]
|
|
6246
|
+
[introduction_edge]: <Introduction Node Edge Format Channel Id String>
|
|
6247
|
+
[introduction_node]: <Introduction Node Public Key Hex String>
|
|
6248
|
+
key: <Path Key Hex String>
|
|
6249
|
+
}
|
|
6250
|
+
via: <Message Received Via Peer with Id Public Key Hex String>
|
|
6251
|
+
}
|
|
6252
|
+
|
|
6253
|
+
```node
|
|
6254
|
+
const {decryptBlindedPath} = require('bolt04');
|
|
6255
|
+
const {diffieHellmanComputeSecret} = require('ln-service');
|
|
6256
|
+
const {subscribeToMessages} = require('ln-service');
|
|
6257
|
+
|
|
6258
|
+
const sub = subscribeToMessages({lnd});
|
|
6259
|
+
|
|
6260
|
+
sub.on('message_received', async ({custom, encrypted, key}) => {
|
|
6261
|
+
// The message id is encrypted
|
|
6262
|
+
const {secret} = await diffieHellmanComputeSecret({
|
|
6263
|
+
lnd,
|
|
6264
|
+
partner_public_key: key,
|
|
6265
|
+
});
|
|
6266
|
+
|
|
6267
|
+
const decrypted = decryptBlindedPath({encrypted, key, secret});
|
|
6268
|
+
|
|
6269
|
+
// The received id should be equal to the one given out
|
|
6270
|
+
if (decrypted.id !== blindedPath.id) {
|
|
6271
|
+
return;
|
|
6272
|
+
}
|
|
6273
|
+
|
|
6274
|
+
// Message to path received
|
|
6275
|
+
});
|
|
6276
|
+
```
|
|
6277
|
+
|
|
6168
6278
|
### subscribeToOpenRequests
|
|
6169
6279
|
|
|
6170
6280
|
Subscribe to inbound channel open requests
|
package/index.js
CHANGED
|
@@ -112,6 +112,7 @@ const {requestChainFeeIncrease} = require('lightning');
|
|
|
112
112
|
const {restrictMacaroon} = require('./macaroons');
|
|
113
113
|
const {revokeAccess} = require('lightning');
|
|
114
114
|
const {routeFromChannels} = require('bolt07');
|
|
115
|
+
const {sendMessage} = require('lightning');
|
|
115
116
|
const {sendMessageToPeer} = require('lightning');
|
|
116
117
|
const {sendToChainAddress} = require('lightning');
|
|
117
118
|
const {sendToChainAddresses} = require('lightning');
|
|
@@ -134,6 +135,7 @@ const {subscribeToForwards} = require('lightning');
|
|
|
134
135
|
const {subscribeToGraph} = require('lightning');
|
|
135
136
|
const {subscribeToInvoice} = require('lightning');
|
|
136
137
|
const {subscribeToInvoices} = require('lightning');
|
|
138
|
+
const {subscribeToMessages} = require('lightning');
|
|
137
139
|
const {subscribeToOpenRequests} = require('lightning');
|
|
138
140
|
const {subscribeToPastPayment} = require('lightning');
|
|
139
141
|
const {subscribeToPastPayments} = require('lightning');
|
|
@@ -279,6 +281,7 @@ module.exports = {
|
|
|
279
281
|
restrictMacaroon,
|
|
280
282
|
revokeAccess,
|
|
281
283
|
routeFromChannels,
|
|
284
|
+
sendMessage,
|
|
282
285
|
sendMessageToPeer,
|
|
283
286
|
sendToChainAddress,
|
|
284
287
|
sendToChainAddresses,
|
|
@@ -301,6 +304,7 @@ module.exports = {
|
|
|
301
304
|
subscribeToGraph,
|
|
302
305
|
subscribeToInvoice,
|
|
303
306
|
subscribeToInvoices,
|
|
307
|
+
subscribeToMessages,
|
|
304
308
|
subscribeToOpenRequests,
|
|
305
309
|
subscribeToPastPayment,
|
|
306
310
|
subscribeToPastPayments,
|
package/package.json
CHANGED
|
@@ -14,25 +14,23 @@
|
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"bolt07": "2.0.0",
|
|
16
16
|
"invoices": "6.1.4",
|
|
17
|
-
"lightning": "12.
|
|
17
|
+
"lightning": "12.3.0",
|
|
18
18
|
"macaroon": "3.0.4"
|
|
19
19
|
},
|
|
20
20
|
"description": "Interaction helper for your Lightning Network daemon",
|
|
21
21
|
"devDependencies": {
|
|
22
|
-
"@alexbosworth/blockchain": "4.
|
|
22
|
+
"@alexbosworth/blockchain": "4.8.0",
|
|
23
23
|
"async": "3.2.6",
|
|
24
24
|
"asyncjs-util": "1.2.12",
|
|
25
25
|
"bip32": "5.0.1",
|
|
26
26
|
"bip66": "2.0.0",
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"ln-docker-daemons": "8.0.4",
|
|
27
|
+
"bolt04": "1.0.0",
|
|
28
|
+
"ecpair": "3.0.2",
|
|
29
|
+
"ln-docker-daemons": "8.0.5",
|
|
31
30
|
"p2tr": "3.0.0",
|
|
32
31
|
"portfinder": "1.0.38",
|
|
33
32
|
"psbt": "6.0.3",
|
|
34
|
-
"tiny-secp256k1": "2.2.4"
|
|
35
|
-
"varuint-bitcoin": "2.0.0"
|
|
33
|
+
"tiny-secp256k1": "2.2.4"
|
|
36
34
|
},
|
|
37
35
|
"engines": {
|
|
38
36
|
"node": ">=22"
|
|
@@ -52,14 +50,16 @@
|
|
|
52
50
|
"url": "git+https://github.com/alexbosworth/ln-service.git"
|
|
53
51
|
},
|
|
54
52
|
"scripts": {
|
|
53
|
+
"integration-test-0.21.3": "DOCKER_LND_VERSION=v0.21.3-beta npm run test",
|
|
55
54
|
"integration-test-0.21.2": "DOCKER_LND_VERSION=v0.21.2-beta npm run test",
|
|
56
55
|
"integration-test-0.21.1": "DOCKER_LND_VERSION=v0.21.1-beta npm run test",
|
|
57
56
|
"integration-test-0.21.0": "DOCKER_LND_VERSION=v0.21.0-beta npm run test",
|
|
57
|
+
"integration-test-0.20.4": "DOCKER_LND_VERSION=v0.20.4-beta npm run test",
|
|
58
58
|
"integration-test-0.20.3": "DOCKER_LND_VERSION=v0.20.3-beta npm run test",
|
|
59
59
|
"integration-test-0.20.2": "DOCKER_LND_VERSION=v0.20.2-beta npm run test",
|
|
60
60
|
"integration-test-0.20.1": "DOCKER_LND_VERSION=v0.20.1-beta npm run test",
|
|
61
61
|
"integration-test-0.20.0": "DOCKER_LND_VERSION=v0.20.0-beta npm run test",
|
|
62
62
|
"test": "echo $DOCKER_LND_VERSION && node test/runner"
|
|
63
63
|
},
|
|
64
|
-
"version": "59.
|
|
64
|
+
"version": "59.4.0"
|
|
65
65
|
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
const {equal} = require('node:assert').strict;
|
|
2
|
+
const test = require('node:test');
|
|
3
|
+
|
|
4
|
+
const asyncRetry = require('async/retry');
|
|
5
|
+
const {blindedPathFromHops} = require('bolt04');
|
|
6
|
+
const {decryptBlindedPath} = require('bolt04');
|
|
7
|
+
const {setupChannel} = require('ln-docker-daemons');
|
|
8
|
+
const {spawnLightningCluster} = require('ln-docker-daemons');
|
|
9
|
+
|
|
10
|
+
const {diffieHellmanComputeSecret} = require('./../../');
|
|
11
|
+
const {sendMessage} = require('./../../');
|
|
12
|
+
const {subscribeToMessages} = require('./../../');
|
|
13
|
+
|
|
14
|
+
const capacity = 1e6;
|
|
15
|
+
const interval = 10;
|
|
16
|
+
const size = 5;
|
|
17
|
+
const times = 1000;
|
|
18
|
+
const type = '805805';
|
|
19
|
+
const value = Buffer.from('hello world').toString('hex');
|
|
20
|
+
|
|
21
|
+
// Subscribing to messages should surface message events
|
|
22
|
+
test('Subscribe to messages', async t => {
|
|
23
|
+
const {kill, nodes} = await spawnLightningCluster({size});
|
|
24
|
+
const messages = {};
|
|
25
|
+
|
|
26
|
+
const [control, alice, bob, charlie, dave] = nodes;
|
|
27
|
+
|
|
28
|
+
const {generate, lnd} = control;
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
// Dave creates a blinded path into their node, given out of band to CTRL
|
|
32
|
+
const blindedPath = (() => {
|
|
33
|
+
const hops = [bob.id, charlie.id, dave.id];
|
|
34
|
+
|
|
35
|
+
const {id, key, path} = blindedPathFromHops({hops});
|
|
36
|
+
|
|
37
|
+
return {id, key, path, introduction_node: bob.id};
|
|
38
|
+
})();
|
|
39
|
+
|
|
40
|
+
// Control listens for a reply to their message
|
|
41
|
+
const subReply = subscribeToMessages({lnd});
|
|
42
|
+
|
|
43
|
+
subReply.on('message_received', async ({encrypted, key, message})=> {
|
|
44
|
+
// The message id is encrypted
|
|
45
|
+
const {secret} = await diffieHellmanComputeSecret({
|
|
46
|
+
lnd,
|
|
47
|
+
partner_public_key: key,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
const decrypted = decryptBlindedPath({encrypted, key, secret});
|
|
51
|
+
|
|
52
|
+
messages.reply = {received_value: message.value, reply_id: decrypted.id};
|
|
53
|
+
|
|
54
|
+
return;
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
// Dave listens for a message to be received on their blinded path
|
|
58
|
+
const sub = subscribeToMessages({lnd: dave.lnd});
|
|
59
|
+
|
|
60
|
+
sub.on('message_received', async ({encrypted, key, message, reply}) => {
|
|
61
|
+
const {secret} = await diffieHellmanComputeSecret({
|
|
62
|
+
lnd: dave.lnd,
|
|
63
|
+
partner_public_key: key,
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
const decrypted = decryptBlindedPath({key, encrypted, secret});
|
|
67
|
+
|
|
68
|
+
// The received id should be equal to the one given out
|
|
69
|
+
if (decrypted.id !== blindedPath.id) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
await sendMessage({
|
|
74
|
+
lnd: dave.lnd,
|
|
75
|
+
inbound: reply.inbound.map(hop => ({
|
|
76
|
+
encrypted_data: hop.encrypted_data,
|
|
77
|
+
relay_key: hop.relay_key,
|
|
78
|
+
})),
|
|
79
|
+
key: reply.key,
|
|
80
|
+
message: {type, value: message.value + message.value},
|
|
81
|
+
outbound: [charlie.id, bob.id, reply.introduction_node],
|
|
82
|
+
});
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
// Make channels between all nodes so they will relay messages
|
|
86
|
+
{
|
|
87
|
+
await asyncRetry({interval, times}, async () => {
|
|
88
|
+
await generate({});
|
|
89
|
+
|
|
90
|
+
await setupChannel({
|
|
91
|
+
capacity,
|
|
92
|
+
generate,
|
|
93
|
+
lnd,
|
|
94
|
+
to: alice,
|
|
95
|
+
});
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
// Make sure that send message is supported by LND
|
|
99
|
+
try {
|
|
100
|
+
await sendMessage({
|
|
101
|
+
lnd,
|
|
102
|
+
inbound: blindedPath.path,
|
|
103
|
+
key: blindedPath.key,
|
|
104
|
+
message: {type, value},
|
|
105
|
+
outbound: [alice.id, blindedPath.introduction_node],
|
|
106
|
+
reply: [alice.id, control.id],
|
|
107
|
+
});
|
|
108
|
+
} catch (err) {
|
|
109
|
+
await kill({});
|
|
110
|
+
|
|
111
|
+
const [code, message] = err;
|
|
112
|
+
|
|
113
|
+
equal(code, 501, 'Method not supported');
|
|
114
|
+
equal(message, 'SendOnionMessageMethodUnsupported', 'Unknown method');
|
|
115
|
+
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
await asyncRetry({interval, times}, async () => {
|
|
120
|
+
await alice.generate({});
|
|
121
|
+
|
|
122
|
+
await setupChannel({
|
|
123
|
+
capacity,
|
|
124
|
+
generate: alice.generate,
|
|
125
|
+
lnd: alice.lnd,
|
|
126
|
+
to: bob,
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
await asyncRetry({interval, times}, async () => {
|
|
131
|
+
await bob.generate({});
|
|
132
|
+
|
|
133
|
+
await setupChannel({
|
|
134
|
+
capacity,
|
|
135
|
+
generate: bob.generate,
|
|
136
|
+
lnd: bob.lnd,
|
|
137
|
+
to: charlie,
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
await asyncRetry({interval, times}, async () => {
|
|
142
|
+
await charlie.generate({});
|
|
143
|
+
|
|
144
|
+
await setupChannel({
|
|
145
|
+
capacity,
|
|
146
|
+
generate: charlie.generate,
|
|
147
|
+
lnd: charlie.lnd,
|
|
148
|
+
to: dave,
|
|
149
|
+
});
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const {reply} = await sendMessage({
|
|
154
|
+
lnd,
|
|
155
|
+
inbound: blindedPath.path,
|
|
156
|
+
key: blindedPath.key,
|
|
157
|
+
message: {type, value},
|
|
158
|
+
outbound: [alice.id, blindedPath.introduction_node],
|
|
159
|
+
reply: [alice.id, control.id],
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Wait for the message send and response
|
|
163
|
+
await asyncRetry({interval, times}, async () => {
|
|
164
|
+
if (!messages.reply) {
|
|
165
|
+
throw new Error('ExpectedMessageReplyReceived');
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return;
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
equal(messages.reply.reply_id, reply, 'Got correct message reply id');
|
|
172
|
+
equal(messages.reply.received_value, value + value, 'Got message reply');
|
|
173
|
+
|
|
174
|
+
await kill({});
|
|
175
|
+
} catch (err) {
|
|
176
|
+
await kill({});
|
|
177
|
+
|
|
178
|
+
throw err;
|
|
179
|
+
}
|
|
180
|
+
});
|
|
@@ -3,7 +3,7 @@ const {equal} = require('node:assert').strict;
|
|
|
3
3
|
const test = require('node:test');
|
|
4
4
|
|
|
5
5
|
const bip32 = require('bip32');
|
|
6
|
-
const
|
|
6
|
+
const {decodeBase58} = require('@alexbosworth/blockchain');
|
|
7
7
|
const ecc = require('tiny-secp256k1')
|
|
8
8
|
const {spawnLightningCluster} = require('ln-docker-daemons');
|
|
9
9
|
|
|
@@ -60,10 +60,12 @@ test(`Get master public keys`, async () => {
|
|
|
60
60
|
const masterIdentityKey = keys.find(n => n.named === identityKeyName);
|
|
61
61
|
|
|
62
62
|
// Convert the key from base58 into its raw form
|
|
63
|
-
const
|
|
63
|
+
const {payload} = decodeBase58({
|
|
64
|
+
encoded: masterIdentityKey.extended_public_key,
|
|
65
|
+
});
|
|
64
66
|
|
|
65
|
-
const chainCode =
|
|
66
|
-
const publicKey =
|
|
67
|
+
const chainCode = chainCodeFromMasterPublicKey(payload);
|
|
68
|
+
const publicKey = publicKeyFromMasterPublicKey(payload);
|
|
67
69
|
|
|
68
70
|
// Make a bip32 object to derive from
|
|
69
71
|
const masterKey = fromPublicKey(publicKey, chainCode);
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
const BN = require('bn.js');
|
|
2
|
-
|
|
3
|
-
const {encode} = require('varuint-bitcoin');
|
|
4
|
-
|
|
5
|
-
const decBase = 10;
|
|
6
|
-
const {isBuffer} = Buffer;
|
|
7
|
-
|
|
8
|
-
/** Convert an array of script buffer elements to a fully formed script
|
|
9
|
-
|
|
10
|
-
@param
|
|
11
|
-
[<Script Element Buffer>, <Script OP_CODE Decimal Number>]
|
|
12
|
-
|
|
13
|
-
@throws
|
|
14
|
-
<Error> when a script element length exceeds maximum
|
|
15
|
-
|
|
16
|
-
@returns
|
|
17
|
-
<Script Hex>
|
|
18
|
-
*/
|
|
19
|
-
module.exports = scriptElements => {
|
|
20
|
-
// Convert numbers to buffers and hex data to pushdata
|
|
21
|
-
const fullScript = scriptElements
|
|
22
|
-
.map(element => {
|
|
23
|
-
if (isBuffer(element)) {
|
|
24
|
-
return Buffer.concat([encode(element.length), element]);
|
|
25
|
-
} else {
|
|
26
|
-
return new BN(element, decBase).toArrayLike(Buffer);
|
|
27
|
-
}
|
|
28
|
-
})
|
|
29
|
-
.reduce((element, script) => Buffer.concat([element, script]));
|
|
30
|
-
|
|
31
|
-
return fullScript.toString('hex');
|
|
32
|
-
};
|