@lightninglabs/wavelength-core 0.1.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/LICENSE +19 -0
- package/README.md +31 -0
- package/dist/activity-options.d.ts +15 -0
- package/dist/activity-options.d.ts.map +1 -0
- package/dist/activity-options.js +7 -0
- package/dist/base-client.d.ts +59 -0
- package/dist/base-client.d.ts.map +1 -0
- package/dist/base-client.js +144 -0
- package/dist/casing.d.ts +4 -0
- package/dist/casing.d.ts.map +1 -0
- package/dist/casing.js +42 -0
- package/dist/client.d.ts +90 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +1 -0
- package/dist/config.d.ts +128 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +142 -0
- package/dist/destination.d.ts +56 -0
- package/dist/destination.d.ts.map +1 -0
- package/dist/destination.js +90 -0
- package/dist/engine/activity.d.ts +25 -0
- package/dist/engine/activity.d.ts.map +1 -0
- package/dist/engine/activity.js +119 -0
- package/dist/engine/constants.d.ts +29 -0
- package/dist/engine/constants.d.ts.map +1 -0
- package/dist/engine/constants.js +29 -0
- package/dist/engine/engine.d.ts +115 -0
- package/dist/engine/engine.d.ts.map +1 -0
- package/dist/engine/engine.js +548 -0
- package/dist/engine/machine.d.ts +50 -0
- package/dist/engine/machine.d.ts.map +1 -0
- package/dist/engine/machine.js +55 -0
- package/dist/engine/optionsAssertions.d.ts +2 -0
- package/dist/engine/optionsAssertions.d.ts.map +1 -0
- package/dist/engine/optionsAssertions.js +20 -0
- package/dist/engine/poller.d.ts +20 -0
- package/dist/engine/poller.d.ts.map +1 -0
- package/dist/engine/poller.js +51 -0
- package/dist/engine/reconcile.d.ts +36 -0
- package/dist/engine/reconcile.d.ts.map +1 -0
- package/dist/engine/reconcile.js +81 -0
- package/dist/engine/snapshot.d.ts +58 -0
- package/dist/engine/snapshot.d.ts.map +1 -0
- package/dist/engine/snapshot.js +10 -0
- package/dist/engine/stabilize.d.ts +8 -0
- package/dist/engine/stabilize.d.ts.map +1 -0
- package/dist/engine/stabilize.js +17 -0
- package/dist/engine/store.d.ts +21 -0
- package/dist/engine/store.d.ts.map +1 -0
- package/dist/engine/store.js +46 -0
- package/dist/errors.d.ts +55 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +80 -0
- package/dist/events.d.ts +62 -0
- package/dist/events.d.ts.map +1 -0
- package/dist/events.js +1 -0
- package/dist/exit.d.ts +93 -0
- package/dist/exit.d.ts.map +1 -0
- package/dist/exit.js +86 -0
- package/dist/facade.d.ts +88 -0
- package/dist/facade.d.ts.map +1 -0
- package/dist/facade.js +150 -0
- package/dist/generated.d.ts +1197 -0
- package/dist/generated.d.ts.map +1 -0
- package/dist/generated.js +206 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +36 -0
- package/dist/passkey.d.ts +46 -0
- package/dist/passkey.d.ts.map +1 -0
- package/dist/passkey.js +16 -0
- package/dist/public-api-assertions.d.ts +2 -0
- package/dist/public-api-assertions.d.ts.map +1 -0
- package/dist/public-api-assertions.js +42 -0
- package/dist/request-assertions.d.ts +2 -0
- package/dist/request-assertions.d.ts.map +1 -0
- package/dist/request-assertions.js +12 -0
- package/dist/requests.d.ts +186 -0
- package/dist/requests.d.ts.map +1 -0
- package/dist/requests.js +5 -0
- package/dist/response-normalization.d.ts +5 -0
- package/dist/response-normalization.d.ts.map +1 -0
- package/dist/response-normalization.js +101 -0
- package/dist/results.d.ts +49 -0
- package/dist/results.d.ts.map +1 -0
- package/dist/results.js +4 -0
- package/dist/state.d.ts +87 -0
- package/dist/state.d.ts.map +1 -0
- package/dist/state.js +87 -0
- package/dist/version.d.ts +18 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +17 -0
- package/package.json +42 -0
package/dist/config.js
ADDED
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { WavelengthError } from "./errors.js";
|
|
2
|
+
/**
|
|
3
|
+
* The daemon log verbosity levels accepted by {@link RuntimeConfig.debugLevel},
|
|
4
|
+
* from most to least verbose. Exported for UIs that render a level picker.
|
|
5
|
+
* debugLevel itself stays a plain string because the daemon also accepts a
|
|
6
|
+
* per-subsystem list such as 'ROND=debug,info'.
|
|
7
|
+
*/
|
|
8
|
+
export const DEBUG_LEVELS = [
|
|
9
|
+
'trace',
|
|
10
|
+
'debug',
|
|
11
|
+
'info',
|
|
12
|
+
'warn',
|
|
13
|
+
'error',
|
|
14
|
+
'critical',
|
|
15
|
+
'off',
|
|
16
|
+
];
|
|
17
|
+
const lwwalletOnly = [
|
|
18
|
+
'walletEsploraUrl',
|
|
19
|
+
'walletPasswordFile',
|
|
20
|
+
'walletPollIntervalSeconds',
|
|
21
|
+
];
|
|
22
|
+
const btcwalletOnly = [
|
|
23
|
+
'walletFeeUrl',
|
|
24
|
+
'walletBlockHeadersSource',
|
|
25
|
+
'walletFilterHeadersSource',
|
|
26
|
+
];
|
|
27
|
+
const numericFields = [
|
|
28
|
+
'walletPollIntervalSeconds',
|
|
29
|
+
'walletRecoveryWindow',
|
|
30
|
+
'maxOperatorFeeSat',
|
|
31
|
+
'signingWorkers',
|
|
32
|
+
'bufferSize',
|
|
33
|
+
];
|
|
34
|
+
function invalidConfig(message) {
|
|
35
|
+
throw new WavelengthError(message, 'invalid_config');
|
|
36
|
+
}
|
|
37
|
+
/** Validates host-owned runtime settings before the typed start dispatches. */
|
|
38
|
+
export function validateRuntimeConfig(config, transport) {
|
|
39
|
+
const walletType = config.walletType ?? 'lwwallet';
|
|
40
|
+
if (walletType !== 'lwwallet' && walletType !== 'btcwallet') {
|
|
41
|
+
invalidConfig(`unsupported walletType: ${String(walletType)}`);
|
|
42
|
+
}
|
|
43
|
+
if (config.network === 'mainnet' && config.allowMainnet !== true) {
|
|
44
|
+
invalidConfig('mainnet requires allowMainnet: true');
|
|
45
|
+
}
|
|
46
|
+
if (walletType === 'lwwallet') {
|
|
47
|
+
for (const field of btcwalletOnly) {
|
|
48
|
+
if (config[field] !== undefined) {
|
|
49
|
+
invalidConfig(`${field} applies only to walletType btcwallet`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
for (const field of lwwalletOnly) {
|
|
55
|
+
if (config[field] !== undefined) {
|
|
56
|
+
invalidConfig(`${field} applies only to walletType lwwallet`);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
for (const field of numericFields) {
|
|
61
|
+
const value = config[field];
|
|
62
|
+
if (value !== undefined &&
|
|
63
|
+
(!Number.isSafeInteger(value) || value < 0)) {
|
|
64
|
+
invalidConfig(`${field} must be a nonnegative safe integer`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
if (config.walletRecoveryWindow !== undefined &&
|
|
68
|
+
config.walletRecoveryWindow > 0xffff_ffff) {
|
|
69
|
+
invalidConfig('walletRecoveryWindow must fit in uint32');
|
|
70
|
+
}
|
|
71
|
+
if (transport === 'rest' && config.arkServerTlsCertPath !== undefined) {
|
|
72
|
+
invalidConfig('arkServerTlsCertPath is unavailable on the web transport');
|
|
73
|
+
}
|
|
74
|
+
if (transport === 'rest' &&
|
|
75
|
+
!config.disableSwaps &&
|
|
76
|
+
config.swapServerTlsCertPath !== undefined) {
|
|
77
|
+
invalidConfig('swapServerTlsCertPath is unavailable on the web transport');
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
// The hosted public deployments per network, mirroring the daemon's own
|
|
81
|
+
// per-network defaults. Record over PresetNetwork so that adding a preset
|
|
82
|
+
// network without its endpoints here is a compile error.
|
|
83
|
+
const NETWORK_ENDPOINTS = {
|
|
84
|
+
signet: {
|
|
85
|
+
ark: {
|
|
86
|
+
rest: 'https://signet.wavelength-rest.lightning.finance',
|
|
87
|
+
grpc: 'signet.wavelength.lightning.finance:443',
|
|
88
|
+
},
|
|
89
|
+
swap: {
|
|
90
|
+
rest: 'https://signet.swapd-rest.lightning.finance',
|
|
91
|
+
grpc: 'swap.signet.wavelength.lightning.finance:443',
|
|
92
|
+
},
|
|
93
|
+
esplora: 'https://mempool-signet.testnet.lightningcluster.com/api',
|
|
94
|
+
},
|
|
95
|
+
testnet: {
|
|
96
|
+
ark: {
|
|
97
|
+
rest: 'https://test.wavelength-rest.lightning.finance',
|
|
98
|
+
grpc: 'test.wavelength.lightning.finance:443',
|
|
99
|
+
},
|
|
100
|
+
swap: {
|
|
101
|
+
rest: 'https://test.swapd-rest.lightning.finance',
|
|
102
|
+
grpc: 'swap.test.wavelength.lightning.finance:443',
|
|
103
|
+
},
|
|
104
|
+
esplora: 'https://mempool-testnet3.testnet.lightningcluster.com/api',
|
|
105
|
+
},
|
|
106
|
+
testnet4: {
|
|
107
|
+
ark: {
|
|
108
|
+
rest: 'https://test4.wavelength-rest.lightning.finance',
|
|
109
|
+
// testnet4's public gRPC NLB is still disabled, so the daemon keeps
|
|
110
|
+
// dialing the raw cluster hostname; a friendly-domain CNAME follows
|
|
111
|
+
// once its certificate work lands.
|
|
112
|
+
grpc: 'lumosd-testnet4.testnet.lightningcluster.com:443',
|
|
113
|
+
},
|
|
114
|
+
swap: {
|
|
115
|
+
rest: 'https://test4.swapd-rest.lightning.finance',
|
|
116
|
+
grpc: 'swapd-testnet4.testnet.lightningcluster.com:443',
|
|
117
|
+
},
|
|
118
|
+
esplora: 'https://mempool-testnet4.testnet.lightningcluster.com/api',
|
|
119
|
+
},
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* Returns the canonical public endpoint preset for a network in one
|
|
123
|
+
* transport's flavor: REST gateway URLs for 'rest' (the web transport),
|
|
124
|
+
* host:port gRPC addresses for 'grpc' (native transports). This is the
|
|
125
|
+
* building block the transport packages' defaultConfig helpers compose over;
|
|
126
|
+
* app code normally calls those instead.
|
|
127
|
+
*
|
|
128
|
+
* Only the preset networks are accepted (see {@link PresetNetwork}); mainnet
|
|
129
|
+
* and regtest have no preset and their {@link RuntimeConfig} is built by hand.
|
|
130
|
+
*
|
|
131
|
+
* @param network - The Bitcoin network to look up.
|
|
132
|
+
* @param transport - The endpoint flavor the caller's transport dials.
|
|
133
|
+
* @returns The preset config fields for that network and transport.
|
|
134
|
+
*/
|
|
135
|
+
export function networkDefaults(network, transport) {
|
|
136
|
+
const endpoints = NETWORK_ENDPOINTS[network];
|
|
137
|
+
return {
|
|
138
|
+
arkServerAddress: endpoints.ark[transport],
|
|
139
|
+
walletEsploraUrl: endpoints.esplora,
|
|
140
|
+
swapServerAddress: endpoints.swap[transport],
|
|
141
|
+
};
|
|
142
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The classification of a pasted send destination. It decides which input
|
|
3
|
+
* fields a send UI should render and nothing more.
|
|
4
|
+
*
|
|
5
|
+
* It deliberately does not name a settlement rail: an `invoice` may still
|
|
6
|
+
* quote as `lightning`, `in_ark`, `credit`, or `mixed`. Read `rail` from the
|
|
7
|
+
* `prepareSend` result for that.
|
|
8
|
+
*/
|
|
9
|
+
export type Destination =
|
|
10
|
+
/** The input is blank. Render no conditional fields. */
|
|
11
|
+
{
|
|
12
|
+
kind: 'empty';
|
|
13
|
+
} | {
|
|
14
|
+
/** The input is a BOLT-11 invoice. */
|
|
15
|
+
kind: 'invoice';
|
|
16
|
+
/** The amount the invoice carries, when it can be read from the HRP. */
|
|
17
|
+
amount: InvoiceAmount;
|
|
18
|
+
}
|
|
19
|
+
/** The input is not an invoice. Treat it as a payable address. */
|
|
20
|
+
| {
|
|
21
|
+
kind: 'address';
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* The amount an invoice carries, when it can be read from the human-readable
|
|
25
|
+
* part.
|
|
26
|
+
*/
|
|
27
|
+
export type InvoiceAmount =
|
|
28
|
+
/** A whole number of satoshis, read from the invoice. */
|
|
29
|
+
{
|
|
30
|
+
status: 'known';
|
|
31
|
+
sat: number;
|
|
32
|
+
}
|
|
33
|
+
/** The invoice carries no amount at all. The payer must supply one. */
|
|
34
|
+
| {
|
|
35
|
+
status: 'amountless';
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* The invoice carries an amount that cannot be shown as a whole number of
|
|
39
|
+
* satoshis: a sub-satoshi figure, or one too large to represent exactly. The
|
|
40
|
+
* invoice is still amount-bearing and the daemon pays it (a sub-satoshi
|
|
41
|
+
* amount is rounded up to the next satoshi), so a UI must not ask the payer
|
|
42
|
+
* for an amount. It simply cannot display one.
|
|
43
|
+
*/
|
|
44
|
+
| {
|
|
45
|
+
status: 'unrepresentable';
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Classifies a pasted destination so a send UI can render only the fields that
|
|
49
|
+
* apply to it. Whitespace is trimmed before matching.
|
|
50
|
+
*
|
|
51
|
+
* Reading the amount from the human-readable part means an amountless invoice
|
|
52
|
+
* is detected without decoding the bech32 payload, so the UI can ask for an
|
|
53
|
+
* amount up front rather than discovering the need after a round trip.
|
|
54
|
+
*/
|
|
55
|
+
export declare function classifyDestination(raw: string): Destination;
|
|
56
|
+
//# sourceMappingURL=destination.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"destination.d.ts","sourceRoot":"","sources":["../src/destination.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW;AACrB,wDAAwD;AACtD;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IACE,sCAAsC;IACtC,IAAI,EAAE,SAAS,CAAC;IAChB,wEAAwE;IACxE,MAAM,EAAE,aAAa,CAAC;CACvB;AACH,kEAAkE;GAChE;IAAE,IAAI,EAAE,SAAS,CAAA;CAAE,CAAC;AAExB;;;GAGG;AACH,MAAM,MAAM,aAAa;AACvB,yDAAyD;AACvD;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE;AAClC,uEAAuE;GACrE;IAAE,MAAM,EAAE,YAAY,CAAA;CAAE;AAC1B;;;;;;GAMG;GACD;IAAE,MAAM,EAAE,iBAAiB,CAAA;CAAE,CAAC;AA6ElC;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAyB5D"}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matches a BOLT-11 human-readable part and captures its optional amount.
|
|
3
|
+
*
|
|
4
|
+
* The network alternation is listed longest-first for legibility, not because
|
|
5
|
+
* it changes the match: JS regex alternation backtracks, so `bc|bcrt` and
|
|
6
|
+
* `bcrt|bc` parse every input identically. What actually makes the parse
|
|
7
|
+
* unambiguous is bech32's data charset, which excludes the digit `1`, so the
|
|
8
|
+
* last `1` in the string before the data part is always the separator and an
|
|
9
|
+
* amount can never contain one. Group 1 is the amount digits and group 2 its
|
|
10
|
+
* multiplier; both are absent for an amountless invoice.
|
|
11
|
+
*/
|
|
12
|
+
const BOLT11_HRP = /^ln(?:bcrt|tbs|bc|tb)(?:(\d+)([munp])?)?1/i;
|
|
13
|
+
const MAX_SAFE = BigInt(Number.MAX_SAFE_INTEGER);
|
|
14
|
+
/** The number of decimal places in one satoshi, as an exponent of one bitcoin. */
|
|
15
|
+
const SATS_EXPONENT = 8;
|
|
16
|
+
/**
|
|
17
|
+
* The BOLT-11 amount multipliers, as the negative power of ten each applies to
|
|
18
|
+
* one bitcoin. An absent multiplier means the digits are already bitcoin. The
|
|
19
|
+
* key type is pinned to the multiplier group of {@link BOLT11_HRP} so a future
|
|
20
|
+
* drift between the two cannot reach `10n ** BigInt(NaN)`, which throws a
|
|
21
|
+
* RangeError during render.
|
|
22
|
+
*/
|
|
23
|
+
const TEN_EXPONENT = {
|
|
24
|
+
m: 3,
|
|
25
|
+
u: 6,
|
|
26
|
+
n: 9,
|
|
27
|
+
p: 12,
|
|
28
|
+
};
|
|
29
|
+
/**
|
|
30
|
+
* Converts a BOLT-11 HRP amount to sats, or returns null when the amount is not
|
|
31
|
+
* a whole number of sats (a nano- or pico-bitcoin figure can be sub-satoshi) or
|
|
32
|
+
* is too large to represent exactly.
|
|
33
|
+
*
|
|
34
|
+
* The arithmetic is exact. Every multiplier is a power of ten, so the conversion
|
|
35
|
+
* is a decimal shift and BigInt performs it without rounding. Doing this in
|
|
36
|
+
* floating point silently loses ordinary amounts: 1000n is exactly 100 sats, but
|
|
37
|
+
* `1000 * 1e-9 * 1e8` evaluates to 100.00000000000001.
|
|
38
|
+
*/
|
|
39
|
+
function satsFromHrp(digits, multiplier) {
|
|
40
|
+
const exponent = SATS_EXPONENT -
|
|
41
|
+
(multiplier
|
|
42
|
+
? TEN_EXPONENT[multiplier.toLowerCase()]
|
|
43
|
+
: 0);
|
|
44
|
+
const value = BigInt(digits);
|
|
45
|
+
// A non-negative exponent scales up exactly. A negative one yields a whole
|
|
46
|
+
// number of sats only when its divisor divides the digits evenly; anything
|
|
47
|
+
// left over is a sub-satoshi amount the wallet cannot send.
|
|
48
|
+
let sats;
|
|
49
|
+
if (exponent >= 0) {
|
|
50
|
+
sats = value * 10n ** BigInt(exponent);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
const divisor = 10n ** BigInt(-exponent);
|
|
54
|
+
if (value % divisor !== 0n) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
sats = value / divisor;
|
|
58
|
+
}
|
|
59
|
+
if (sats <= 0n || sats > MAX_SAFE) {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
return Number(sats);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Classifies a pasted destination so a send UI can render only the fields that
|
|
66
|
+
* apply to it. Whitespace is trimmed before matching.
|
|
67
|
+
*
|
|
68
|
+
* Reading the amount from the human-readable part means an amountless invoice
|
|
69
|
+
* is detected without decoding the bech32 payload, so the UI can ask for an
|
|
70
|
+
* amount up front rather than discovering the need after a round trip.
|
|
71
|
+
*/
|
|
72
|
+
export function classifyDestination(raw) {
|
|
73
|
+
const trimmed = raw.trim();
|
|
74
|
+
if (trimmed === '') {
|
|
75
|
+
return { kind: 'empty' };
|
|
76
|
+
}
|
|
77
|
+
const match = BOLT11_HRP.exec(trimmed);
|
|
78
|
+
if (!match) {
|
|
79
|
+
return { kind: 'address' };
|
|
80
|
+
}
|
|
81
|
+
const [, digits, multiplier] = match;
|
|
82
|
+
if (digits === undefined) {
|
|
83
|
+
return { kind: 'invoice', amount: { status: 'amountless' } };
|
|
84
|
+
}
|
|
85
|
+
const sat = satsFromHrp(digits, multiplier);
|
|
86
|
+
return {
|
|
87
|
+
kind: 'invoice',
|
|
88
|
+
amount: sat === null ? { status: 'unrepresentable' } : { status: 'known', sat },
|
|
89
|
+
};
|
|
90
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { WavelengthClient } from '../client.ts';
|
|
2
|
+
import type { Entry } from '../results.ts';
|
|
3
|
+
/**
|
|
4
|
+
* Owns the daemon activity subscription while the wallet is ready: opening
|
|
5
|
+
* (with an includeExisting replay so changes missed while down are caught),
|
|
6
|
+
* reopening with a capped exponential backoff, debouncing activity events into
|
|
7
|
+
* onActivity, and giving up through onDead after too many consecutive failed
|
|
8
|
+
* opens (the counter includes the initial open, not just reopens).
|
|
9
|
+
*/
|
|
10
|
+
export declare class ActivityStream {
|
|
11
|
+
#private;
|
|
12
|
+
constructor(opts: {
|
|
13
|
+
client: Pick<WavelengthClient, 'startActivity' | 'stopActivity'>;
|
|
14
|
+
onActivity: () => void;
|
|
15
|
+
onReconcile: () => void;
|
|
16
|
+
onDead: (error: Error) => void;
|
|
17
|
+
});
|
|
18
|
+
start(): void;
|
|
19
|
+
stop(): void;
|
|
20
|
+
/** Forwarded 'activity' client events; debounced into one onActivity call. */
|
|
21
|
+
noteActivity(entry: Pick<Entry, 'cursor'>): void;
|
|
22
|
+
/** Forwarded 'activityStream' client events: the stream was lost; reopen. */
|
|
23
|
+
noteStreamLost(): void;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=activity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"activity.d.ts","sourceRoot":"","sources":["../../src/engine/activity.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AACrD,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAQ3C;;;;;;GAMG;AACH,qBAAa,cAAc;;gBAmBb,IAAI,EAAE;QAChB,MAAM,EAAE,IAAI,CAAC,gBAAgB,EAAE,eAAe,GAAG,cAAc,CAAC,CAAC;QACjE,UAAU,EAAE,MAAM,IAAI,CAAC;QACvB,WAAW,EAAE,MAAM,IAAI,CAAC;QACxB,MAAM,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,CAAC;KAChC;IAID,KAAK,IAAI,IAAI;IAYb,IAAI,IAAI,IAAI;IAaZ,8EAA8E;IAC9E,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,IAAI;IAWhD,6EAA6E;IAC7E,cAAc,IAAI,IAAI;CAoEvB"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { ACTIVITY_DEBOUNCE_MS, STREAM_BACKOFF_CAP_MS, STREAM_BACKOFF_MS, STREAM_FAILURE_LIMIT, } from "./constants.js";
|
|
2
|
+
/**
|
|
3
|
+
* Owns the daemon activity subscription while the wallet is ready: opening
|
|
4
|
+
* (with an includeExisting replay so changes missed while down are caught),
|
|
5
|
+
* reopening with a capped exponential backoff, debouncing activity events into
|
|
6
|
+
* onActivity, and giving up through onDead after too many consecutive failed
|
|
7
|
+
* opens (the counter includes the initial open, not just reopens).
|
|
8
|
+
*/
|
|
9
|
+
export class ActivityStream {
|
|
10
|
+
#running = false;
|
|
11
|
+
#cursor = 0;
|
|
12
|
+
#backoff = STREAM_BACKOFF_MS;
|
|
13
|
+
#failures = 0;
|
|
14
|
+
#lifecycleGeneration = 0;
|
|
15
|
+
#retryTimer;
|
|
16
|
+
#debounce;
|
|
17
|
+
// Tracks the lifecycle whose startActivity() call is unsettled. A stream
|
|
18
|
+
// loss within that lifecycle must not double-subscribe, while a stop/start
|
|
19
|
+
// must be able to open even if the stopped lifecycle has not settled yet.
|
|
20
|
+
#openingGeneration;
|
|
21
|
+
#opts;
|
|
22
|
+
constructor(opts) {
|
|
23
|
+
this.#opts = opts;
|
|
24
|
+
}
|
|
25
|
+
start() {
|
|
26
|
+
if (this.#running) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
this.#running = true;
|
|
30
|
+
this.#lifecycleGeneration += 1;
|
|
31
|
+
this.#cursor = 0;
|
|
32
|
+
this.#backoff = STREAM_BACKOFF_MS;
|
|
33
|
+
this.#failures = 0;
|
|
34
|
+
this.#open(this.#lifecycleGeneration);
|
|
35
|
+
}
|
|
36
|
+
stop() {
|
|
37
|
+
if (!this.#running) {
|
|
38
|
+
this.#cursor = 0;
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
this.#running = false;
|
|
42
|
+
this.#lifecycleGeneration += 1;
|
|
43
|
+
this.#cursor = 0;
|
|
44
|
+
clearTimeout(this.#retryTimer);
|
|
45
|
+
clearTimeout(this.#debounce);
|
|
46
|
+
this.#opts.client.stopActivity();
|
|
47
|
+
}
|
|
48
|
+
/** Forwarded 'activity' client events; debounced into one onActivity call. */
|
|
49
|
+
noteActivity(entry) {
|
|
50
|
+
if (!this.#running) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (Number.isSafeInteger(entry.cursor) && entry.cursor > this.#cursor) {
|
|
54
|
+
this.#cursor = entry.cursor;
|
|
55
|
+
}
|
|
56
|
+
clearTimeout(this.#debounce);
|
|
57
|
+
this.#debounce = setTimeout(() => this.#opts.onActivity(), ACTIVITY_DEBOUNCE_MS);
|
|
58
|
+
}
|
|
59
|
+
/** Forwarded 'activityStream' client events: the stream was lost; reopen. */
|
|
60
|
+
noteStreamLost() {
|
|
61
|
+
if (this.#running) {
|
|
62
|
+
this.#opts.onReconcile();
|
|
63
|
+
this.#scheduleRetry(this.#lifecycleGeneration);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
#open(lifecycle) {
|
|
67
|
+
if (this.#openingGeneration === lifecycle) {
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
this.#openingGeneration = lifecycle;
|
|
71
|
+
this.#opts.client.startActivity({
|
|
72
|
+
includeExisting: this.#cursor === 0,
|
|
73
|
+
cursor: this.#cursor,
|
|
74
|
+
}).then(() => {
|
|
75
|
+
if (this.#openingGeneration === lifecycle) {
|
|
76
|
+
this.#openingGeneration = undefined;
|
|
77
|
+
}
|
|
78
|
+
if (!this.#running || lifecycle !== this.#lifecycleGeneration) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
// The initial open replays existing entries. Cursor reopens rely on
|
|
82
|
+
// noteStreamLost's immediate reconciliation before resuming.
|
|
83
|
+
this.#backoff = STREAM_BACKOFF_MS;
|
|
84
|
+
this.#failures = 0;
|
|
85
|
+
}, () => {
|
|
86
|
+
if (this.#openingGeneration === lifecycle) {
|
|
87
|
+
this.#openingGeneration = undefined;
|
|
88
|
+
}
|
|
89
|
+
this.#onReopenFailure(lifecycle);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
#onReopenFailure(lifecycle) {
|
|
93
|
+
if (!this.#running || lifecycle !== this.#lifecycleGeneration) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
this.#failures += 1;
|
|
97
|
+
if (this.#failures >= STREAM_FAILURE_LIMIT) {
|
|
98
|
+
// The stream could not be re-established after repeated attempts;
|
|
99
|
+
// surface it instead of leaving the wallet looking healthy while its
|
|
100
|
+
// balance and history silently stop updating.
|
|
101
|
+
this.#running = false;
|
|
102
|
+
clearTimeout(this.#retryTimer);
|
|
103
|
+
clearTimeout(this.#debounce);
|
|
104
|
+
this.#opts.onDead(new Error('lost the activity stream and could not reconnect'));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
this.#scheduleRetry(lifecycle);
|
|
108
|
+
}
|
|
109
|
+
#scheduleRetry(lifecycle) {
|
|
110
|
+
clearTimeout(this.#retryTimer);
|
|
111
|
+
this.#retryTimer = setTimeout(() => {
|
|
112
|
+
if (!this.#running || lifecycle !== this.#lifecycleGeneration) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
this.#backoff = Math.min(this.#backoff * 2, STREAM_BACKOFF_CAP_MS);
|
|
116
|
+
this.#open(lifecycle);
|
|
117
|
+
}, this.#backoff);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/** Bounds the in-memory log tail the engine keeps. */
|
|
2
|
+
export declare const MAX_LOGS = 200;
|
|
3
|
+
/** Consecutive failed activity-stream opens before the stream is dead. */
|
|
4
|
+
export declare const STREAM_FAILURE_LIMIT = 5;
|
|
5
|
+
/** Initial reopen backoff for a lost activity stream. */
|
|
6
|
+
export declare const STREAM_BACKOFF_MS = 1000;
|
|
7
|
+
/** Safety ceiling on activity-stream reopen backoff. Only approached when reopen attempts hang while losses keep arriving, doubling the backoff each time without incrementing the failure count. */
|
|
8
|
+
export declare const STREAM_BACKOFF_CAP_MS = 30000;
|
|
9
|
+
/** Debounce applied to activity events before the background refresh runs. */
|
|
10
|
+
export declare const ACTIVITY_DEBOUNCE_MS = 250;
|
|
11
|
+
/** Interval of the refresh poll while the wallet is syncing. */
|
|
12
|
+
export declare const SYNC_POLL_MS = 2000;
|
|
13
|
+
/** Consecutive failed sync-poll refreshes before escalating to error. */
|
|
14
|
+
export declare const SYNC_POLL_FAILURE_LIMIT = 5;
|
|
15
|
+
/** Interval of the readiness poll during a background restore. */
|
|
16
|
+
export declare const RESTORE_POLL_MS = 1500;
|
|
17
|
+
/**
|
|
18
|
+
* Follow-up refresh delays used to reconcile a possibly-stale balance after an
|
|
19
|
+
* activity event: the daemon can report an entry settled a beat before
|
|
20
|
+
* balance() reflects the new funds.
|
|
21
|
+
*/
|
|
22
|
+
export declare const SETTLE_RECONCILE_DELAYS_MS: number[];
|
|
23
|
+
/** Consecutive failed background refreshes before escalating to error. */
|
|
24
|
+
export declare const BACKGROUND_REFRESH_FAILURE_LIMIT = 5;
|
|
25
|
+
/** Retries for the post-create/unlock info refetch before escalating. */
|
|
26
|
+
export declare const ADOPT_INFO_RETRIES = 3;
|
|
27
|
+
/** Delay between #adoptInfo retries. */
|
|
28
|
+
export declare const ADOPT_INFO_RETRY_MS = 1000;
|
|
29
|
+
//# sourceMappingURL=constants.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/engine/constants.ts"],"names":[],"mappings":"AAEA,sDAAsD;AACtD,eAAO,MAAM,QAAQ,MAAM,CAAC;AAE5B,0EAA0E;AAC1E,eAAO,MAAM,oBAAoB,IAAI,CAAC;AAEtC,yDAAyD;AACzD,eAAO,MAAM,iBAAiB,OAAO,CAAC;AAEtC,qMAAqM;AACrM,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAE3C,8EAA8E;AAC9E,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,gEAAgE;AAChE,eAAO,MAAM,YAAY,OAAO,CAAC;AAEjC,yEAAyE;AACzE,eAAO,MAAM,uBAAuB,IAAI,CAAC;AAEzC,kEAAkE;AAClE,eAAO,MAAM,eAAe,OAAO,CAAC;AAEpC;;;;GAIG;AACH,eAAO,MAAM,0BAA0B,UAAoB,CAAC;AAE5D,0EAA0E;AAC1E,eAAO,MAAM,gCAAgC,IAAI,CAAC;AAElD,yEAAyE;AACzE,eAAO,MAAM,kBAAkB,IAAI,CAAC;AAEpC,wCAAwC;AACxC,eAAO,MAAM,mBAAmB,OAAO,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Timing and budget constants for the engine's background processes.
|
|
2
|
+
/** Bounds the in-memory log tail the engine keeps. */
|
|
3
|
+
export const MAX_LOGS = 200;
|
|
4
|
+
/** Consecutive failed activity-stream opens before the stream is dead. */
|
|
5
|
+
export const STREAM_FAILURE_LIMIT = 5;
|
|
6
|
+
/** Initial reopen backoff for a lost activity stream. */
|
|
7
|
+
export const STREAM_BACKOFF_MS = 1000;
|
|
8
|
+
/** Safety ceiling on activity-stream reopen backoff. Only approached when reopen attempts hang while losses keep arriving, doubling the backoff each time without incrementing the failure count. */
|
|
9
|
+
export const STREAM_BACKOFF_CAP_MS = 30000;
|
|
10
|
+
/** Debounce applied to activity events before the background refresh runs. */
|
|
11
|
+
export const ACTIVITY_DEBOUNCE_MS = 250;
|
|
12
|
+
/** Interval of the refresh poll while the wallet is syncing. */
|
|
13
|
+
export const SYNC_POLL_MS = 2000;
|
|
14
|
+
/** Consecutive failed sync-poll refreshes before escalating to error. */
|
|
15
|
+
export const SYNC_POLL_FAILURE_LIMIT = 5;
|
|
16
|
+
/** Interval of the readiness poll during a background restore. */
|
|
17
|
+
export const RESTORE_POLL_MS = 1500;
|
|
18
|
+
/**
|
|
19
|
+
* Follow-up refresh delays used to reconcile a possibly-stale balance after an
|
|
20
|
+
* activity event: the daemon can report an entry settled a beat before
|
|
21
|
+
* balance() reflects the new funds.
|
|
22
|
+
*/
|
|
23
|
+
export const SETTLE_RECONCILE_DELAYS_MS = [750, 1500, 3000];
|
|
24
|
+
/** Consecutive failed background refreshes before escalating to error. */
|
|
25
|
+
export const BACKGROUND_REFRESH_FAILURE_LIMIT = 5;
|
|
26
|
+
/** Retries for the post-create/unlock info refetch before escalating. */
|
|
27
|
+
export const ADOPT_INFO_RETRIES = 3;
|
|
28
|
+
/** Delay between #adoptInfo retries. */
|
|
29
|
+
export const ADOPT_INFO_RETRY_MS = 1000;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import type { WavelengthClient } from '../client.ts';
|
|
2
|
+
import type { RuntimeConfig } from '../config.ts';
|
|
3
|
+
import { type ExitBatchEvent, type ExitBatchOptions, type ExitBatchResult } from '../exit.ts';
|
|
4
|
+
import type { CreateWalletRequest, DepositRequest, ExitRequest, ExitStatusRequest, ExitSummaryRequest, GetExitPlanRequest, ListRequest, OpenWalletFromPasskeyRequest, ReceiveRequest, RestoreWalletRequest, SendRequest, SweepWalletRequest, UnlockWalletRequest } from '../requests.ts';
|
|
5
|
+
import type { CreateWalletResult, DepositResult, ExitResult, ExitStatusResult, ExitSummaryResult, GetExitPlanResult, ListResult, OpenWalletFromPasskeyResult, PrepareSendResult, ReceiveResult, SendResult, SweepWalletResult, UnlockWalletResult } from '../results.ts';
|
|
6
|
+
import { type WalletInfo } from '../state.ts';
|
|
7
|
+
import type { WalletSnapshot } from './snapshot.ts';
|
|
8
|
+
/**
|
|
9
|
+
* Options for {@link createWalletEngine}. A discriminated union: the type
|
|
10
|
+
* requires config when autoStart is true, so autoStart cannot be set without
|
|
11
|
+
* a config to start from.
|
|
12
|
+
*/
|
|
13
|
+
export type WalletEngineOptions = {
|
|
14
|
+
/** The transport client the engine drives. */
|
|
15
|
+
client: WavelengthClient;
|
|
16
|
+
/** Default runtime config used by autoStart and by start() with no argument. */
|
|
17
|
+
config: RuntimeConfig;
|
|
18
|
+
/** Start the runtime automatically once it is ready. */
|
|
19
|
+
autoStart: true;
|
|
20
|
+
} | {
|
|
21
|
+
/** The transport client the engine drives. */
|
|
22
|
+
client: WavelengthClient;
|
|
23
|
+
/** Default runtime config used by start() with no argument. */
|
|
24
|
+
config?: RuntimeConfig;
|
|
25
|
+
/** Start the runtime automatically once it is ready. Requires config; omit or set false when config is unset. */
|
|
26
|
+
autoStart?: false;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* Removes a key from every arm of a union type. TypeScript's built-in Omit
|
|
30
|
+
* does not distribute over unions (it flattens to the union of all keys
|
|
31
|
+
* first), which would erase the discriminant on types like
|
|
32
|
+
* {@link WalletEngineOptions}. This distributes the omission per arm instead.
|
|
33
|
+
*/
|
|
34
|
+
export type DistributiveOmit<T, K extends PropertyKey> = T extends unknown ? Omit<T, K> : never;
|
|
35
|
+
/**
|
|
36
|
+
* The headless wallet orchestrator: it owns the lifecycle phase machine, the
|
|
37
|
+
* state snapshot (phase, info, balance, activity, recovery, logs), and the
|
|
38
|
+
* background processes that keep them fresh (activity stream, settle
|
|
39
|
+
* reconcile, syncing poll, restore readiness poll). Framework bindings
|
|
40
|
+
* subscribe via getSnapshot()/subscribe(); vanilla consumers can use it
|
|
41
|
+
* directly. Create one with {@link createWalletEngine} or a transport factory
|
|
42
|
+
* such as createWebWalletEngine.
|
|
43
|
+
*/
|
|
44
|
+
export interface WalletEngine {
|
|
45
|
+
/** The underlying transport client, as an escape hatch. */
|
|
46
|
+
readonly client: WavelengthClient;
|
|
47
|
+
/** The current immutable state snapshot. */
|
|
48
|
+
getSnapshot(): WalletSnapshot;
|
|
49
|
+
/** Subscribes to snapshot changes; returns the unsubscribe function. */
|
|
50
|
+
subscribe(listener: () => void): () => void;
|
|
51
|
+
/**
|
|
52
|
+
* Starts the runtime. Falls back to the engine's configured config when
|
|
53
|
+
* called without an argument; throws if neither exists. A failure moves the
|
|
54
|
+
* phase to 'error' and rejects.
|
|
55
|
+
*/
|
|
56
|
+
start(config?: RuntimeConfig): Promise<WalletInfo>;
|
|
57
|
+
/** Stops the runtime and clears the in-memory snapshot (info, balance, activity, error); persisted wallet data is untouched. A failure moves the phase to 'error'. */
|
|
58
|
+
stop(): Promise<void>;
|
|
59
|
+
/** Re-fetches info, balance, and activity concurrently. */
|
|
60
|
+
refresh(): Promise<void>;
|
|
61
|
+
/** Creates a new wallet, refetches info, and refreshes in the background. */
|
|
62
|
+
createWallet(req: CreateWalletRequest): Promise<CreateWalletResult>;
|
|
63
|
+
/**
|
|
64
|
+
* Restores a wallet from a mnemonic. Resolves as soon as the restored
|
|
65
|
+
* wallet is usable; the optional server-assisted recovery scan continues in
|
|
66
|
+
* the background, observed through snapshot.recovery. Rejects when the
|
|
67
|
+
* restore fails before the wallet came up, when a restore is already in
|
|
68
|
+
* flight, when req.mnemonic is missing or empty, or if the engine has been
|
|
69
|
+
* disposed.
|
|
70
|
+
*/
|
|
71
|
+
restoreWallet(req: RestoreWalletRequest): Promise<WalletInfo>;
|
|
72
|
+
/** Resets snapshot.recovery to idle (e.g. after dismissing a banner). */
|
|
73
|
+
acknowledgeRecovery(): void;
|
|
74
|
+
/** Unlocks an existing wallet, refetches info, and refreshes in the background. */
|
|
75
|
+
unlockWallet(req: UnlockWalletRequest): Promise<UnlockWalletResult>;
|
|
76
|
+
/** Opens the wallet from a passkey PRF output, refetches info, and refreshes in the background. */
|
|
77
|
+
openWalletFromPasskey(req: OpenWalletFromPasskeyRequest): Promise<OpenWalletFromPasskeyResult>;
|
|
78
|
+
/** Requests an on-chain deposit address and refreshes in the background. */
|
|
79
|
+
deposit(req?: DepositRequest): Promise<DepositResult>;
|
|
80
|
+
/** Requests a Lightning receive and refreshes in the background. */
|
|
81
|
+
receive(req: ReceiveRequest): Promise<ReceiveResult>;
|
|
82
|
+
/** Quotes a payment without dispatching it. No refresh: a quote moves no money. */
|
|
83
|
+
prepareSend(req: SendRequest): Promise<PrepareSendResult>;
|
|
84
|
+
/** Dispatches a payment quoted by prepareSend and refreshes in the background. */
|
|
85
|
+
sendPrepared(prepared: PrepareSendResult): Promise<SendResult>;
|
|
86
|
+
/** Sends a payment and refreshes in the background. */
|
|
87
|
+
send(req: SendRequest): Promise<SendResult>;
|
|
88
|
+
/** Starts a single exit (cooperative or unilateral) and refreshes wallet state. */
|
|
89
|
+
exit(req: ExitRequest): Promise<ExitResult>;
|
|
90
|
+
/** Queries the status of an exit. Read-only; does not refresh. */
|
|
91
|
+
exitStatus(req: ExitStatusRequest): Promise<ExitStatusResult>;
|
|
92
|
+
/** Summarizes all in-progress exits. Read-only; does not refresh. */
|
|
93
|
+
exitSummary(req?: ExitSummaryRequest): Promise<ExitSummaryResult>;
|
|
94
|
+
/** Previews unilateral-exit readiness and funding. Read-only; does not refresh. */
|
|
95
|
+
getExitPlan(req: GetExitPlanRequest): Promise<GetExitPlanResult>;
|
|
96
|
+
/** Previews (broadcast:false) or broadcasts (broadcast:true) a backing-wallet sweep. Refreshes only when broadcasting. */
|
|
97
|
+
sweepWallet(req: SweepWalletRequest): Promise<SweepWalletResult>;
|
|
98
|
+
/**
|
|
99
|
+
* Starts a batch of exits, refreshing wallet state after each one starts.
|
|
100
|
+
* Resolves once every exit is started, not completed.
|
|
101
|
+
*/
|
|
102
|
+
exitBatch(opts: ExitBatchOptions & {
|
|
103
|
+
signal?: AbortSignal;
|
|
104
|
+
onEvent?: (event: ExitBatchEvent) => void;
|
|
105
|
+
}): Promise<ExitBatchResult>;
|
|
106
|
+
/** Lists wallet activity, VTXOs, or on-chain outputs. Read-only; does not refresh. */
|
|
107
|
+
list(req: ListRequest): Promise<ListResult>;
|
|
108
|
+
/** Clears the buffered log tail. */
|
|
109
|
+
clearLogs(): void;
|
|
110
|
+
/** Tears down subscriptions, polls, and streams. The engine is done after this. */
|
|
111
|
+
dispose(): void;
|
|
112
|
+
}
|
|
113
|
+
/** Creates a {@link WalletEngine} over any transport client. */
|
|
114
|
+
export declare function createWalletEngine(options: WalletEngineOptions): WalletEngine;
|
|
115
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/engine/engine.ts"],"names":[],"mappings":"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"}
|