lightning 5.13.0 → 5.15.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 +17 -0
- package/README.md +2 -2
- package/grpc/protos/chainnotifier.proto +5 -3
- package/grpc/protos/lightning.proto +15 -1
- package/grpc/protos/router.proto +6 -0
- package/grpc/protos/signer.proto +308 -0
- package/index.js +6 -0
- package/lnd_methods/index.js +6 -0
- package/lnd_methods/info/get_route_to_destination.js +10 -0
- package/lnd_methods/macaroon/methods.json +13 -0
- package/lnd_methods/offchain/is_destination_payable.js +4 -0
- package/lnd_methods/offchain/pay.js +4 -0
- package/lnd_methods/offchain/pay_via_payment_details.js +4 -0
- package/lnd_methods/offchain/pay_via_payment_request.js +4 -0
- package/lnd_methods/offchain/probe_for_route.js +4 -0
- package/lnd_methods/offchain/subscribe_to_pay.js +10 -0
- package/lnd_methods/offchain/subscribe_to_pay_via_details.js +4 -0
- package/lnd_methods/offchain/subscribe_to_pay_via_request.js +4 -0
- package/lnd_methods/offchain/subscribe_to_probe_for_route.js +4 -0
- package/lnd_methods/signer/begin_group_signing_session.js +144 -0
- package/lnd_methods/signer/end_group_signing_session.js +102 -0
- package/lnd_methods/signer/index.js +6 -0
- package/lnd_methods/signer/sign_transaction.js +20 -5
- package/lnd_methods/signer/update_group_signing_session.js +110 -0
- package/lnd_responses/index.js +2 -0
- package/lnd_responses/rpc_group_session_as_session.js +51 -0
- package/package.json +6 -6
- package/test/lnd_methods/signer/test_begin_group_signing_session.js +134 -0
- package/test/lnd_methods/signer/test_diffie_hellman_compute_secret.js +3 -1
- package/test/lnd_methods/signer/test_end_group_signing_session.js +93 -0
- package/test/lnd_methods/signer/test_update_group_signing_session.js +108 -0
- package/test/lnd_responses/test_rpc_group_session_as_session.js +79 -0
|
@@ -17,8 +17,11 @@ const isHex = n => !(n.length % 2) && /^[0-9A-F]*$/i.test(n);
|
|
|
17
17
|
|
|
18
18
|
Requires `offchain:write` permission
|
|
19
19
|
|
|
20
|
+
Preferred `confidence` is not supported on LND 0.14.3 and below
|
|
21
|
+
|
|
20
22
|
{
|
|
21
23
|
[cltv_delta]: <Final CLTV Delta Number>
|
|
24
|
+
[confidence]: <Preferred Route Confidence Number Out of One Million Number>
|
|
22
25
|
destination: <Destination Public Key Hex String>
|
|
23
26
|
[features]: [{
|
|
24
27
|
bit: <Feature Bit Number>
|
|
@@ -117,6 +120,7 @@ module.exports = (args, cbk) => {
|
|
|
117
120
|
|
|
118
121
|
const sub = subscribeToProbeForRoute({
|
|
119
122
|
cltv_delta: args.cltv_delta,
|
|
123
|
+
confidence: args.confidence,
|
|
120
124
|
destination: args.destination,
|
|
121
125
|
features: args.features,
|
|
122
126
|
ignore: args.ignore,
|
|
@@ -16,6 +16,7 @@ const {routeHintFromRoute} = require('./../../lnd_requests');
|
|
|
16
16
|
const {safeTokens} = require('./../../bolt00');
|
|
17
17
|
const {states} = require('./payment_states');
|
|
18
18
|
|
|
19
|
+
const asTimePreference = n => n === undefined ? n : ((n * 2) - 1e6) / 1e6;
|
|
19
20
|
const cltvBuf = 3;
|
|
20
21
|
const cltvLimit = (limit, height) => !limit ? undefined : limit - height;
|
|
21
22
|
const cltvLimitErr = /cltv limit \d+ should be greater than \d+/;
|
|
@@ -24,6 +25,7 @@ const defaultMaxPaths = 1;
|
|
|
24
25
|
const defaultTimeoutSeconds = 25;
|
|
25
26
|
const hexToBuf = hex => !hex ? undefined : Buffer.from(hex, 'hex');
|
|
26
27
|
const {isArray} = Array;
|
|
28
|
+
const isConfidence = n => !isNaN(n) && n >= 0 && n <= 1e6;
|
|
27
29
|
const isHex = n => !!n && !(n.length % 2) && /^[0-9A-F]*$/i.test(n);
|
|
28
30
|
const maxTokens = '4294967296';
|
|
29
31
|
const method = 'sendPaymentV2';
|
|
@@ -42,8 +44,11 @@ const unknownServiceErr = 'unknown service verrpc.Versioner';
|
|
|
42
44
|
|
|
43
45
|
`max_path_mtokens` is not supported in LND 0.12.0 or below
|
|
44
46
|
|
|
47
|
+
Preferred `confidence` is not supported on LND 0.14.3 and below
|
|
48
|
+
|
|
45
49
|
{
|
|
46
50
|
[cltv_delta]: <Final CLTV Delta Number>
|
|
51
|
+
[confidence]: <Preferred Route Confidence Number Out of One Million Number>
|
|
47
52
|
[destination]: <Destination Public Key String>
|
|
48
53
|
[features]: [{
|
|
49
54
|
bit: <Feature Bit Number>
|
|
@@ -208,6 +213,10 @@ module.exports = args => {
|
|
|
208
213
|
throw new Error('UnexpectedCltvDeltaWhenSubscribingToPayPaymentRequest');
|
|
209
214
|
}
|
|
210
215
|
|
|
216
|
+
if (args.confidence !== undefined && !isConfidence(args.confidence)) {
|
|
217
|
+
throw new Error('ExpectedConfidencePartsPerMillionForPaymentReq');
|
|
218
|
+
}
|
|
219
|
+
|
|
211
220
|
if (!args.destination && !args.request) {
|
|
212
221
|
throw new Error('ExpectedDestinationWhenPaymentRequestNotSpecified');
|
|
213
222
|
}
|
|
@@ -378,6 +387,7 @@ module.exports = args => {
|
|
|
378
387
|
payment_hash: !args.id ? undefined : hexToBuf(args.id),
|
|
379
388
|
payment_request: !args.request ? undefined : args.request,
|
|
380
389
|
route_hints: !hints.length ? undefined : hints,
|
|
390
|
+
time_pref: asTimePreference(args.confidence),
|
|
381
391
|
timeout_seconds: timeoutSecs || defaultTimeoutSeconds,
|
|
382
392
|
});
|
|
383
393
|
}],
|
|
@@ -17,8 +17,11 @@ const type = 'router';
|
|
|
17
17
|
|
|
18
18
|
`max_path_mtokens` is not supported in LND 0.12.0 or below
|
|
19
19
|
|
|
20
|
+
Preferred `confidence` is not supported on LND 0.14.3 and below
|
|
21
|
+
|
|
20
22
|
{
|
|
21
23
|
[cltv_delta]: <Final CLTV Delta Number>
|
|
24
|
+
[confidence]: <Preferred Route Confidence Number Out of One Million Number>
|
|
22
25
|
destination: <Destination Public Key String>
|
|
23
26
|
[features]: [{
|
|
24
27
|
bit: <Feature Bit Number>
|
|
@@ -191,6 +194,7 @@ module.exports = args => {
|
|
|
191
194
|
|
|
192
195
|
return subscribeToPay({
|
|
193
196
|
cltv_delta: args.cltv_delta || defaultCltvDelta,
|
|
197
|
+
confidence: args.confidence,
|
|
194
198
|
destination: args.destination,
|
|
195
199
|
features: args.features,
|
|
196
200
|
id: args.id || randomId(),
|
|
@@ -10,7 +10,10 @@ const type = 'router';
|
|
|
10
10
|
|
|
11
11
|
`max_path_mtokens` is not supported in LND 0.12.0 or below
|
|
12
12
|
|
|
13
|
+
Preferred `confidence` is not supported on LND 0.14.3 and below
|
|
14
|
+
|
|
13
15
|
{
|
|
16
|
+
[confidence]: <Preferred Route Confidence Number Out of One Million Number>
|
|
14
17
|
[incoming_peer]: <Pay Through Specific Final Hop Public Key Hex String>
|
|
15
18
|
lnd: <Authenticated LND API Object>
|
|
16
19
|
[max_fee]: <Maximum Fee Tokens To Pay Number>
|
|
@@ -167,6 +170,7 @@ module.exports = args => {
|
|
|
167
170
|
}
|
|
168
171
|
|
|
169
172
|
return subscribeToPay({
|
|
173
|
+
confidence: args.confidence,
|
|
170
174
|
incoming_peer: args.incoming_peer,
|
|
171
175
|
lnd: args.lnd,
|
|
172
176
|
max_fee: args.max_fee,
|
|
@@ -20,8 +20,11 @@ const {nextTick} = process;
|
|
|
20
20
|
|
|
21
21
|
Requires `offchain:write` permission
|
|
22
22
|
|
|
23
|
+
Preferred `confidence` is not supported on LND 0.14.3 and below
|
|
24
|
+
|
|
23
25
|
{
|
|
24
26
|
[cltv_delta]: <Final CLTV Delta Number>
|
|
27
|
+
[confidence]: <Preferred Route Confidence Number Out of One Million Number>
|
|
25
28
|
destination: <Destination Public Key Hex String>
|
|
26
29
|
[features]: [{
|
|
27
30
|
bit: <Feature Bit Number>
|
|
@@ -241,6 +244,7 @@ module.exports = args => {
|
|
|
241
244
|
return getRouteToDestination({
|
|
242
245
|
mtokens,
|
|
243
246
|
cltv_delta: args.cltv_delta,
|
|
247
|
+
confidence: args.confidence,
|
|
244
248
|
destination: args.destination,
|
|
245
249
|
features: args.features,
|
|
246
250
|
ignore: ignore.concat(temporaryChannelFailures),
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
const asyncAuto = require('async/auto');
|
|
2
|
+
const {returnResult} = require('asyncjs-util');
|
|
3
|
+
|
|
4
|
+
const getPublicKey = require('./../address/get_public_key');
|
|
5
|
+
const {isLnd} = require('./../../lnd_requests');
|
|
6
|
+
const {rpcGroupSessionAsSession} = require('./../../lnd_responses');
|
|
7
|
+
|
|
8
|
+
const hexAsBuffer = hex => Buffer.from(hex, 'hex');
|
|
9
|
+
const {isArray} = Array;
|
|
10
|
+
const isHash = n => /^[0-9A-F]{64}$/i.test(n);
|
|
11
|
+
const method = 'muSig2CreateSession';
|
|
12
|
+
const type = 'signer';
|
|
13
|
+
const uniq = arr => Array.from(new Set(arr));
|
|
14
|
+
const unsupportedMessage = 'unknown method MuSig2CreateSession for service signrpc.Signer';
|
|
15
|
+
const xOnlyPublicKeyHexLength = 64;
|
|
16
|
+
const xOnlyPublicKey = hexKey => hexKey.slice(2);
|
|
17
|
+
|
|
18
|
+
/** Start a MuSig2 group signing session
|
|
19
|
+
|
|
20
|
+
Requires LND built with `signrpc`, `walletrpc` build tags
|
|
21
|
+
|
|
22
|
+
Requires `address:read`, `signer:generate` permissions
|
|
23
|
+
|
|
24
|
+
This method is not supported in LND 0.14.3 and below
|
|
25
|
+
|
|
26
|
+
{
|
|
27
|
+
lnd: <Authenticated LND API Object>
|
|
28
|
+
[is_key_spend]: <Key Is BIP 86 Key Spend Key Bool>
|
|
29
|
+
key_family: <HD Seed Key Family Number>
|
|
30
|
+
key_index: <Key Index Number>
|
|
31
|
+
public_keys: [<External Public Key Hex String>]
|
|
32
|
+
[root_hash]: <Taproot Script Root Hash Hex String>
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
@returns via cbk or Promise
|
|
36
|
+
{
|
|
37
|
+
external_key: <Final Script or Top Level Public Key Hex String>
|
|
38
|
+
id: <Session Id Hex String>
|
|
39
|
+
[internal_key]: <Internal Top Level Public Key Hex String>
|
|
40
|
+
nonce: <Session Compound Nonces Hex String>
|
|
41
|
+
}
|
|
42
|
+
*/
|
|
43
|
+
module.exports = (args, cbk) => {
|
|
44
|
+
return new Promise((resolve, reject) => {
|
|
45
|
+
return asyncAuto({
|
|
46
|
+
// Check arguments
|
|
47
|
+
validate: cbk => {
|
|
48
|
+
if (!isLnd({method, type, lnd: args.lnd})) {
|
|
49
|
+
return cbk([400, 'ExpectedAuthenticatedLndToStartMuSig2Session']);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (args.key_family === undefined) {
|
|
53
|
+
return cbk([400, 'ExpectedKeyFamilyToStartMuSig2Session']);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
if (args.key_index === undefined) {
|
|
57
|
+
return cbk([400, 'ExpectedKeyIndexToStartMuSig2Session']);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
if (!isArray(args.public_keys)) {
|
|
61
|
+
return cbk([400, 'ExpectedArrayOfPublicKeysForMuSig2SessionStart']);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (!args.public_keys.length) {
|
|
65
|
+
return cbk([400, 'ExpectedOtherPublicKeysForMuSig2SessionStart']);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (!!args.root_hash && !isHash(args.root_hash)) {
|
|
69
|
+
return cbk([400, 'ExpectedHashWhenSpecifyingMuSig2ScriptRootHash']);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return cbk();
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
// Get the local public key to pass it to the session along with others
|
|
76
|
+
getKey: ['validate', ({}, cbk) => {
|
|
77
|
+
return getPublicKey({
|
|
78
|
+
family: args.key_family,
|
|
79
|
+
index: args.key_index,
|
|
80
|
+
lnd: args.lnd,
|
|
81
|
+
},
|
|
82
|
+
cbk);
|
|
83
|
+
}],
|
|
84
|
+
|
|
85
|
+
// Put together the Taproot tweak flags for top level signing
|
|
86
|
+
taprootTweak: ['validate', ({}, cbk) => {
|
|
87
|
+
// Spend should include a top level Taproot script commitment proof
|
|
88
|
+
if (!!args.root_hash) {
|
|
89
|
+
return cbk(null, {script_root: hexAsBuffer(args.root_hash)});
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Spend is absent a Taproot script, but is a top-level Taproot key
|
|
93
|
+
if (!!args.is_key_spend) {
|
|
94
|
+
return cbk(null, {key_spend_only: true});
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return cbk();
|
|
98
|
+
}],
|
|
99
|
+
|
|
100
|
+
// Collect all public keys taking part in the signing session
|
|
101
|
+
publicKeys: ['getKey', ({getKey}, cbk) => {
|
|
102
|
+
// Trim public keys as necessary
|
|
103
|
+
const keys = [getKey.public_key].concat(args.public_keys).map(key => {
|
|
104
|
+
if (key.length === xOnlyPublicKeyHexLength) {
|
|
105
|
+
return key;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return xOnlyPublicKey(key);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
return cbk(null, uniq(keys));
|
|
112
|
+
}],
|
|
113
|
+
|
|
114
|
+
// Create the signing session
|
|
115
|
+
create: [
|
|
116
|
+
'publicKeys',
|
|
117
|
+
'taprootTweak',
|
|
118
|
+
({publicKeys, taprootTweak}, cbk) =>
|
|
119
|
+
{
|
|
120
|
+
return args.lnd[type][method]({
|
|
121
|
+
all_signer_pubkeys: publicKeys.map(hexAsBuffer),
|
|
122
|
+
key_loc: {key_family: args.key_family, key_index: args.key_index},
|
|
123
|
+
taproot_tweak: taprootTweak,
|
|
124
|
+
},
|
|
125
|
+
(err, res) => {
|
|
126
|
+
if (!!err && err.details === unsupportedMessage) {
|
|
127
|
+
return cbk([501, 'MuSig2BeginSigningSessionNotSupported']);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
if (!!err) {
|
|
131
|
+
return cbk([503, 'UnexpectedErrorCreatingMuSig2Session', {err}]);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
try {
|
|
135
|
+
return cbk(null, rpcGroupSessionAsSession(res));
|
|
136
|
+
} catch (err) {
|
|
137
|
+
return cbk([503, err.message]);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
}],
|
|
141
|
+
},
|
|
142
|
+
returnResult({reject, resolve, of: 'create'}, cbk));
|
|
143
|
+
});
|
|
144
|
+
};
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
const asyncAuto = require('async/auto');
|
|
2
|
+
const {returnResult} = require('asyncjs-util');
|
|
3
|
+
|
|
4
|
+
const {isLnd} = require('./../../lnd_requests');
|
|
5
|
+
|
|
6
|
+
const bufferAsHex = buffer => buffer.toString('hex');
|
|
7
|
+
const hexAsBuffer = hex => Buffer.from(hex, 'hex');
|
|
8
|
+
const {isArray} = Array;
|
|
9
|
+
const {isBuffer} = Buffer;
|
|
10
|
+
const method = 'muSig2CombineSig';
|
|
11
|
+
const type = 'signer';
|
|
12
|
+
|
|
13
|
+
/** Complete a MuSig2 signing session
|
|
14
|
+
|
|
15
|
+
Requires LND built with `signrpc` build tag
|
|
16
|
+
|
|
17
|
+
Requires `signer:generate` permission
|
|
18
|
+
|
|
19
|
+
This method is not supported in LND 0.14.3 and below
|
|
20
|
+
|
|
21
|
+
{
|
|
22
|
+
id: <Session Id Hex String>
|
|
23
|
+
lnd: <Authenticated LND API Object>
|
|
24
|
+
[signatures]: [<Combine External Partial Signature Hex String>]
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@returns via cbk or Promise
|
|
28
|
+
{
|
|
29
|
+
[signature]: <Combined Signature Hex String>
|
|
30
|
+
}
|
|
31
|
+
*/
|
|
32
|
+
module.exports = ({id, lnd, signatures}, cbk) => {
|
|
33
|
+
return new Promise((resolve, reject) => {
|
|
34
|
+
return asyncAuto({
|
|
35
|
+
// Check arguments
|
|
36
|
+
validate: cbk => {
|
|
37
|
+
if (!id) {
|
|
38
|
+
return cbk([400, 'ExpectedSessionIdToFinishMuSig2Session']);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!isLnd({lnd, method, type})) {
|
|
42
|
+
return cbk([400, 'ExpectedAuthenticatedLndToFinishMuSig2Session']);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (isArray(signatures) && !signatures.length) {
|
|
46
|
+
return cbk([400, 'ExpectedPartialSignaturesToCombineToEndSession']);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
return cbk();
|
|
50
|
+
},
|
|
51
|
+
|
|
52
|
+
// Close out the signing session
|
|
53
|
+
endSession: ['validate', ({}, cbk) => {
|
|
54
|
+
// Exit early when there are signatures to combine
|
|
55
|
+
if (isArray(signatures)) {
|
|
56
|
+
return cbk();
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return lnd[type].muSig2Cleanup({session_id: hexAsBuffer(id)}, err => {
|
|
60
|
+
if (!!err) {
|
|
61
|
+
return cbk([503, 'UnexpectedErrorCleaningUpMuSig2Session', {err}]);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
return cbk();
|
|
65
|
+
});
|
|
66
|
+
}],
|
|
67
|
+
|
|
68
|
+
// Update the session with signatures
|
|
69
|
+
updateSignatures: ['validate', ({}, cbk) => {
|
|
70
|
+
// Exit early when there are no signatures to update
|
|
71
|
+
if (!isArray(signatures)) {
|
|
72
|
+
return cbk(null, {});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return lnd[type][method]({
|
|
76
|
+
other_partial_signatures: signatures.map(hexAsBuffer),
|
|
77
|
+
session_id: hexAsBuffer(id),
|
|
78
|
+
},
|
|
79
|
+
(err, res) => {
|
|
80
|
+
if (!!err) {
|
|
81
|
+
return cbk([503, 'UnexpectedErrorFinishingMuSig2Session', {err}]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if (!res) {
|
|
85
|
+
return cbk([503, 'ExpectedResponseForMuSig2CombineRequest']);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (res.have_all_signatures === false) {
|
|
89
|
+
return cbk([400, 'ExpectedAllSignaturesProvidedForSession']);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (!isBuffer(res.final_signature)) {
|
|
93
|
+
return cbk([503, 'ExpectedFinalSignatureInCombineResponse']);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return cbk(null, {signature: bufferAsHex(res.final_signature)});
|
|
97
|
+
});
|
|
98
|
+
}],
|
|
99
|
+
},
|
|
100
|
+
returnResult({reject, resolve, of: 'updateSignatures'}, cbk));
|
|
101
|
+
});
|
|
102
|
+
};
|
|
@@ -1,11 +1,17 @@
|
|
|
1
|
+
const beginGroupSigningSession = require('./begin_group_signing_session');
|
|
1
2
|
const diffieHellmanComputeSecret = require('./diffie_hellman_compute_secret');
|
|
3
|
+
const endGroupSigningSession = require('./end_group_signing_session');
|
|
2
4
|
const signBytes = require('./sign_bytes');
|
|
3
5
|
const signTransaction = require('./sign_transaction');
|
|
6
|
+
const updateGroupSigningSession = require('./update_group_signing_session');
|
|
4
7
|
const verifyBytesSignature = require('./verify_bytes_signature');
|
|
5
8
|
|
|
6
9
|
module.exports = {
|
|
10
|
+
beginGroupSigningSession,
|
|
7
11
|
diffieHellmanComputeSecret,
|
|
12
|
+
endGroupSigningSession,
|
|
8
13
|
signBytes,
|
|
9
14
|
signTransaction,
|
|
15
|
+
updateGroupSigningSession,
|
|
10
16
|
verifyBytesSignature,
|
|
11
17
|
};
|
|
@@ -6,6 +6,7 @@ const {isLnd} = require('./../../lnd_requests');
|
|
|
6
6
|
const bufferAsHex = buffer => buffer.toString('hex');
|
|
7
7
|
const hexAsBuffer = hex => Buffer.from(hex, 'hex');
|
|
8
8
|
const {isArray} = Array;
|
|
9
|
+
const isV1 = scriptHex => scriptHex.length === 68 && /5120/.test(scriptHex);
|
|
9
10
|
const method = 'signOutputRaw';
|
|
10
11
|
const notFound = -1;
|
|
11
12
|
const type = 'signer';
|
|
@@ -64,13 +65,27 @@ module.exports = ({inputs, lnd, spending, transaction}, cbk) => {
|
|
|
64
65
|
return cbk();
|
|
65
66
|
},
|
|
66
67
|
|
|
68
|
+
// Derive the previous outputs set
|
|
69
|
+
previousOutputs: ['validate', ({}, cbk) => {
|
|
70
|
+
const outputs = [].concat(inputs).concat(spending || []).map(utxo => ({
|
|
71
|
+
pk_script: hexAsBuffer(utxo.output_script),
|
|
72
|
+
value: utxo.output_tokens,
|
|
73
|
+
}));
|
|
74
|
+
|
|
75
|
+
const v1Spends = outputs.filter(n => isV1(bufferAsHex(n.pk_script)));
|
|
76
|
+
|
|
77
|
+
// Exit early when there is no need to provide prev outs, non-taproot
|
|
78
|
+
if (!v1Spends.length) {
|
|
79
|
+
return cbk();
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return cbk(null, outputs);
|
|
83
|
+
}],
|
|
84
|
+
|
|
67
85
|
// Get signatures
|
|
68
|
-
signTransaction: ['
|
|
86
|
+
signTransaction: ['previousOutputs', ({previousOutputs}, cbk) => {
|
|
69
87
|
return lnd[type][method]({
|
|
70
|
-
prev_outputs:
|
|
71
|
-
pk_script: hexAsBuffer(utxo.output_script),
|
|
72
|
-
value: utxo.output_tokens,
|
|
73
|
-
})),
|
|
88
|
+
prev_outputs: previousOutputs,
|
|
74
89
|
raw_tx_bytes: hexAsBuffer(transaction),
|
|
75
90
|
sign_descs: inputs.map(input => ({
|
|
76
91
|
input_index: input.vin,
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
const asyncAuto = require('async/auto');
|
|
2
|
+
const {returnResult} = require('asyncjs-util');
|
|
3
|
+
|
|
4
|
+
const {isLnd} = require('./../../lnd_requests');
|
|
5
|
+
|
|
6
|
+
const bufferAsHex = buffer => buffer.toString('hex');
|
|
7
|
+
const hexAsBuffer = hex => Buffer.from(hex, 'hex');
|
|
8
|
+
const {isArray} = Array;
|
|
9
|
+
const {isBuffer} = Buffer;
|
|
10
|
+
const isHash = n => !!n && /^[0-9A-F]{64}$/i.test(n);
|
|
11
|
+
const method = 'muSig2RegisterNonces';
|
|
12
|
+
const type = 'signer';
|
|
13
|
+
|
|
14
|
+
/** Update a MuSig2 signing session with nonces and generate a partial sig
|
|
15
|
+
|
|
16
|
+
All remote nonces are expected to be passed
|
|
17
|
+
|
|
18
|
+
Requires LND built with `signrpc` build tag
|
|
19
|
+
|
|
20
|
+
Requires `signer:generate` permission
|
|
21
|
+
|
|
22
|
+
This method is not supported in LND 0.14.3 and below
|
|
23
|
+
|
|
24
|
+
{
|
|
25
|
+
hash: <Hash to Sign Hex String>
|
|
26
|
+
id: <MuSig2 Session Id Hex String>
|
|
27
|
+
lnd: <Authenticated LND API Object>
|
|
28
|
+
nonces: [<Nonce Hex String>]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
@returns via cbk or Promise
|
|
32
|
+
{
|
|
33
|
+
signature: <Partial Signature Hex String>
|
|
34
|
+
}
|
|
35
|
+
*/
|
|
36
|
+
module.exports = ({hash, id, lnd, nonces}, cbk) => {
|
|
37
|
+
return new Promise((resolve, reject) => {
|
|
38
|
+
return asyncAuto({
|
|
39
|
+
// Check arguments
|
|
40
|
+
validate: cbk => {
|
|
41
|
+
if (!isHash(hash)) {
|
|
42
|
+
return cbk([400, 'ExpectedHashToSignToUpdateMuSig2Session']);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!id) {
|
|
46
|
+
return cbk([400, 'ExpectedSessionIdToUpdateMuSig2Session']);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!isLnd({lnd, method, type})) {
|
|
50
|
+
return cbk([400, 'ExpectedAuthenticatedLndToUpdateMuSig2Session']);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!isArray(nonces)) {
|
|
54
|
+
return cbk([400, 'ExpectedArrayOfNoncesToUpdateMuSig2Session']);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return cbk();
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
// Update the session with the nonces
|
|
61
|
+
updateNonces: ['validate', ({}, cbk) => {
|
|
62
|
+
return lnd[type][method]({
|
|
63
|
+
other_signer_public_nonces: nonces.map(hexAsBuffer),
|
|
64
|
+
session_id: hexAsBuffer(id),
|
|
65
|
+
},
|
|
66
|
+
(err, res) => {
|
|
67
|
+
if (!!err) {
|
|
68
|
+
return cbk([503, 'UnexpectedErrorUpdatingMuSig2Session', {err}]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!res) {
|
|
72
|
+
return cbk([503, 'ExpectedResultOfRegisterNoncesRequest']);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
if (res.have_all_nonces !== true) {
|
|
76
|
+
return cbk([400, 'ExpectedAllNoncesForRegisterNoncesRequest']);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
return cbk();
|
|
80
|
+
});
|
|
81
|
+
}],
|
|
82
|
+
|
|
83
|
+
// Partially sign the digest hash
|
|
84
|
+
sign: ['updateNonces', ({}, cbk) => {
|
|
85
|
+
return lnd[type].muSig2Sign({
|
|
86
|
+
message_digest: hexAsBuffer(hash),
|
|
87
|
+
session_id: hexAsBuffer(id),
|
|
88
|
+
},
|
|
89
|
+
(err, res) => {
|
|
90
|
+
if (!!err) {
|
|
91
|
+
return cbk([503, 'UnexpectedErrorSigningMuSig2Session', {err}]);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (!res) {
|
|
95
|
+
return cbk([503, 'ExpectedResultForMuSig2SignRequest']);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (!isBuffer(res.local_partial_signature)) {
|
|
99
|
+
return cbk([503, 'ExpectedPartialSignatureForMuSig2SignRequest']);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return cbk(null, {
|
|
103
|
+
signature: bufferAsHex(res.local_partial_signature),
|
|
104
|
+
});
|
|
105
|
+
});
|
|
106
|
+
}],
|
|
107
|
+
},
|
|
108
|
+
returnResult({reject, resolve, of: 'sign'}, cbk));
|
|
109
|
+
});
|
|
110
|
+
};
|
package/lnd_responses/index.js
CHANGED
|
@@ -26,6 +26,7 @@ const rpcFailedPolicyAsFail = require('./rpc_failed_policy_as_fail');
|
|
|
26
26
|
const rpcFeesAsChannelFees = require('./rpc_fees_as_channel_fees');
|
|
27
27
|
const rpcForwardAsForward = require('./rpc_forward_as_forward');
|
|
28
28
|
const rpcForwardAsForwardRequest = require('./rpc_forward_as_forward_request');
|
|
29
|
+
const rpcGroupSessionAsSession = require('./rpc_group_session_as_session');
|
|
29
30
|
const rpcHopAsHop = require('./rpc_hop_as_hop');
|
|
30
31
|
const rpcInvoiceAsInvoice = require('./rpc_invoice_as_invoice');
|
|
31
32
|
const rpcNetworkAsNetworkInfo = require('./rpc_network_as_network_info');
|
|
@@ -70,6 +71,7 @@ module.exports = {
|
|
|
70
71
|
rpcFeesAsChannelFees,
|
|
71
72
|
rpcForwardAsForward,
|
|
72
73
|
rpcForwardAsForwardRequest,
|
|
74
|
+
rpcGroupSessionAsSession,
|
|
73
75
|
rpcHopAsHop,
|
|
74
76
|
rpcInvoiceAsInvoice,
|
|
75
77
|
rpcNetworkAsNetworkInfo,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
const bufferAsHex = buffer => buffer.toString('hex');
|
|
2
|
+
const {isBuffer} = Buffer;
|
|
3
|
+
|
|
4
|
+
/** Map RPC MuSig2 Session to a MuSig2 Session
|
|
5
|
+
|
|
6
|
+
{
|
|
7
|
+
combined_key: <Combined Public Key Buffer Object>
|
|
8
|
+
local_public_nonces: <Two Concatenated Local Signer Nonces Buffer Object>
|
|
9
|
+
session_id: <Session Id Buffer Object>
|
|
10
|
+
taproot_internal_key: <Taproot Internal Public Key Buffer Object>
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
@throws
|
|
14
|
+
<Error>
|
|
15
|
+
|
|
16
|
+
@returns
|
|
17
|
+
{
|
|
18
|
+
external_key: <Final Script or Top Level Public Key Hex String>
|
|
19
|
+
id: <Session Id Hex String>
|
|
20
|
+
[internal_key]: <Internal Top Level Public Key Hex String>
|
|
21
|
+
nonce: <Session Compound Nonces Hex String>
|
|
22
|
+
}
|
|
23
|
+
*/
|
|
24
|
+
module.exports = args => {
|
|
25
|
+
if (!args) {
|
|
26
|
+
throw new Error('ExpectedResponseForMuSig2SessionRequest');
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!isBuffer(args.combined_key)) {
|
|
30
|
+
throw new Error('ExpectedCombinedPublicKeyInMuSig2SessionResponse');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!isBuffer(args.local_public_nonces)) {
|
|
34
|
+
throw new Error('ExpectedLocalPublicNoncesInMuSig2SessionResponse');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!isBuffer(args.session_id)) {
|
|
38
|
+
throw new Error('ExpectedMuSig2SigningSessionIdInSessionResponse');
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if (!isBuffer(args.taproot_internal_key)) {
|
|
42
|
+
throw new Error('ExpectedTaprootInternalKeyInMuSig2SessionResponse');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
return {
|
|
46
|
+
external_key: bufferAsHex(args.combined_key),
|
|
47
|
+
id: bufferAsHex(args.session_id),
|
|
48
|
+
internal_key: bufferAsHex(args.taproot_internal_key) || undefined,
|
|
49
|
+
nonce: bufferAsHex(args.local_public_nonces),
|
|
50
|
+
};
|
|
51
|
+
};
|
package/package.json
CHANGED
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@grpc/grpc-js": "1.6.7",
|
|
11
|
-
"@grpc/proto-loader": "0.6.
|
|
11
|
+
"@grpc/proto-loader": "0.6.12",
|
|
12
12
|
"@types/express": "4.17.13",
|
|
13
|
-
"@types/node": "17.0.
|
|
13
|
+
"@types/node": "17.0.31",
|
|
14
14
|
"@types/request": "2.48.8",
|
|
15
15
|
"@types/ws": "8.5.3",
|
|
16
16
|
"async": "3.2.3",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"bolt09": "0.2.3",
|
|
23
23
|
"cbor": "8.1.0",
|
|
24
24
|
"ecpair": "2.0.1",
|
|
25
|
-
"express": "4.
|
|
25
|
+
"express": "4.18.1",
|
|
26
26
|
"invoices": "2.0.6",
|
|
27
27
|
"psbt": "2.0.1",
|
|
28
28
|
"tiny-secp256k1": "2.2.1",
|
|
@@ -33,8 +33,8 @@
|
|
|
33
33
|
"@alexbosworth/node-fetch": "2.6.2",
|
|
34
34
|
"@alexbosworth/tap": "15.0.11",
|
|
35
35
|
"tsd": "0.20.0",
|
|
36
|
-
"typescript": "4.6.
|
|
37
|
-
"ws": "8.
|
|
36
|
+
"typescript": "4.6.4",
|
|
37
|
+
"ws": "8.6.0"
|
|
38
38
|
},
|
|
39
39
|
"engines": {
|
|
40
40
|
"node": ">=12.20"
|
|
@@ -59,5 +59,5 @@
|
|
|
59
59
|
"directory": "test/typescript"
|
|
60
60
|
},
|
|
61
61
|
"types": "index.d.ts",
|
|
62
|
-
"version": "5.
|
|
62
|
+
"version": "5.15.0"
|
|
63
63
|
}
|