@cogcoin/client 0.5.15 → 1.0.1
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/README.md +80 -25
- package/dist/app-paths.d.ts +5 -6
- package/dist/app-paths.js +8 -16
- package/dist/art/balance.txt +10 -0
- package/dist/art/welcome.txt +16 -0
- package/dist/bitcoind/bootstrap/controller.d.ts +1 -0
- package/dist/bitcoind/bootstrap/controller.js +53 -1
- package/dist/bitcoind/client/follow-block-times.d.ts +1 -0
- package/dist/bitcoind/client/follow-block-times.js +1 -1
- package/dist/bitcoind/client/internal-types.d.ts +7 -3
- package/dist/bitcoind/client/managed-client.d.ts +4 -2
- package/dist/bitcoind/client/managed-client.js +14 -0
- package/dist/bitcoind/client/sync-engine.js +72 -11
- package/dist/bitcoind/hash-order.d.ts +4 -0
- package/dist/bitcoind/hash-order.js +13 -0
- package/dist/bitcoind/indexer-daemon-main.js +11 -3
- package/dist/bitcoind/normalize.js +3 -2
- package/dist/bitcoind/processing-start-height.d.ts +5 -0
- package/dist/bitcoind/processing-start-height.js +7 -0
- package/dist/bitcoind/progress/constants.d.ts +4 -0
- package/dist/bitcoind/progress/constants.js +4 -0
- package/dist/bitcoind/progress/controller.d.ts +2 -1
- package/dist/bitcoind/progress/controller.js +3 -3
- package/dist/bitcoind/progress/follow-scene.d.ts +6 -2
- package/dist/bitcoind/progress/follow-scene.js +29 -6
- package/dist/bitcoind/progress/formatting.d.ts +1 -0
- package/dist/bitcoind/progress/formatting.js +6 -0
- package/dist/bitcoind/progress/train-scene.js +37 -18
- package/dist/bitcoind/progress/tty-renderer.d.ts +6 -1
- package/dist/bitcoind/progress/tty-renderer.js +8 -4
- package/dist/bitcoind/rpc.d.ts +2 -1
- package/dist/bitcoind/rpc.js +3 -0
- package/dist/bitcoind/types.d.ts +6 -0
- package/dist/bytes.d.ts +1 -0
- package/dist/bytes.js +3 -0
- package/dist/cli/art.d.ts +2 -0
- package/dist/cli/art.js +37 -0
- package/dist/cli/commands/client-admin.d.ts +2 -0
- package/dist/cli/commands/client-admin.js +91 -0
- package/dist/cli/commands/follow.js +0 -2
- package/dist/cli/commands/mining-admin.js +6 -47
- package/dist/cli/commands/mining-read.js +11 -50
- package/dist/cli/commands/mining-runtime.js +142 -5
- package/dist/cli/commands/service-runtime.js +0 -2
- package/dist/cli/commands/status.js +8 -2
- package/dist/cli/commands/sync.js +49 -92
- package/dist/cli/commands/wallet-admin.js +142 -136
- package/dist/cli/commands/wallet-mutation.js +91 -79
- package/dist/cli/commands/wallet-read.js +15 -18
- package/dist/cli/context.js +5 -14
- package/dist/cli/mining-format.d.ts +0 -1
- package/dist/cli/mining-format.js +5 -37
- package/dist/cli/mining-json.d.ts +0 -18
- package/dist/cli/mining-json.js +0 -35
- package/dist/cli/mutation-command-groups.d.ts +1 -2
- package/dist/cli/mutation-command-groups.js +0 -5
- package/dist/cli/mutation-json.d.ts +24 -145
- package/dist/cli/mutation-json.js +30 -136
- package/dist/cli/mutation-resolved-json.d.ts +0 -7
- package/dist/cli/mutation-resolved-json.js +4 -10
- package/dist/cli/mutation-success.d.ts +2 -0
- package/dist/cli/mutation-success.js +11 -1
- package/dist/cli/mutation-text-format.js +1 -3
- package/dist/cli/output.d.ts +1 -1
- package/dist/cli/output.js +254 -231
- package/dist/cli/parse.d.ts +1 -1
- package/dist/cli/parse.js +93 -122
- package/dist/cli/preview-json.d.ts +17 -120
- package/dist/cli/preview-json.js +14 -97
- package/dist/cli/prompt.js +8 -13
- package/dist/cli/read-json.d.ts +15 -37
- package/dist/cli/read-json.js +44 -140
- package/dist/cli/runner.js +10 -13
- package/dist/cli/sync-progress.d.ts +6 -0
- package/dist/cli/sync-progress.js +91 -0
- package/dist/cli/types.d.ts +9 -17
- package/dist/cli/types.js +0 -2
- package/dist/cli/wallet-format.d.ts +1 -0
- package/dist/cli/wallet-format.js +208 -144
- package/dist/cli/workflow-hints.d.ts +3 -3
- package/dist/cli/workflow-hints.js +11 -8
- package/dist/client/default-client.d.ts +3 -1
- package/dist/client/default-client.js +45 -2
- package/dist/client/factory.js +1 -1
- package/dist/client/initialization.js +23 -0
- package/dist/client/persistence.js +5 -5
- package/dist/client/store-adapter.js +1 -0
- package/dist/sqlite/checkpoints.d.ts +1 -0
- package/dist/sqlite/checkpoints.js +7 -0
- package/dist/sqlite/store.js +14 -1
- package/dist/types.d.ts +1 -0
- package/dist/wallet/coin-control.d.ts +41 -12
- package/dist/wallet/coin-control.js +100 -428
- package/dist/wallet/descriptor-normalization.d.ts +1 -3
- package/dist/wallet/descriptor-normalization.js +0 -16
- package/dist/wallet/lifecycle.d.ts +7 -99
- package/dist/wallet/lifecycle.js +513 -968
- package/dist/wallet/managed-core-wallet.d.ts +13 -0
- package/dist/wallet/managed-core-wallet.js +20 -0
- package/dist/wallet/mining/constants.d.ts +5 -12
- package/dist/wallet/mining/constants.js +5 -12
- package/dist/wallet/mining/control.d.ts +1 -13
- package/dist/wallet/mining/control.js +45 -349
- package/dist/wallet/mining/index.d.ts +4 -5
- package/dist/wallet/mining/index.js +2 -3
- package/dist/wallet/mining/runner.d.ts +123 -13
- package/dist/wallet/mining/runner.js +899 -511
- package/dist/wallet/mining/runtime-artifacts.js +23 -3
- package/dist/wallet/mining/sentence-protocol.d.ts +44 -0
- package/dist/wallet/mining/sentence-protocol.js +123 -0
- package/dist/wallet/mining/sentences.d.ts +4 -8
- package/dist/wallet/mining/sentences.js +3 -52
- package/dist/wallet/mining/state.d.ts +11 -6
- package/dist/wallet/mining/state.js +7 -6
- package/dist/wallet/mining/types.d.ts +2 -30
- package/dist/wallet/mining/visualizer.d.ts +31 -3
- package/dist/wallet/mining/visualizer.js +135 -13
- package/dist/wallet/read/context.d.ts +0 -2
- package/dist/wallet/read/context.js +119 -140
- package/dist/wallet/read/filter.js +2 -11
- package/dist/wallet/read/index.d.ts +1 -1
- package/dist/wallet/read/project.js +24 -77
- package/dist/wallet/read/types.d.ts +10 -25
- package/dist/wallet/reset.d.ts +0 -1
- package/dist/wallet/reset.js +60 -138
- package/dist/wallet/root-resolution.d.ts +1 -5
- package/dist/wallet/root-resolution.js +0 -18
- package/dist/wallet/runtime.d.ts +0 -6
- package/dist/wallet/runtime.js +0 -8
- package/dist/wallet/state/client-password-agent.js +208 -0
- package/dist/wallet/state/client-password.d.ts +65 -0
- package/dist/wallet/state/client-password.js +952 -0
- package/dist/wallet/state/crypto.d.ts +1 -20
- package/dist/wallet/state/crypto.js +0 -63
- package/dist/wallet/state/provider.d.ts +23 -11
- package/dist/wallet/state/provider.js +248 -290
- package/dist/wallet/state/storage.d.ts +2 -2
- package/dist/wallet/state/storage.js +48 -16
- package/dist/wallet/tx/anchor.d.ts +3 -28
- package/dist/wallet/tx/anchor.js +349 -1250
- package/dist/wallet/tx/bitcoin-transfer.d.ts +35 -0
- package/dist/wallet/tx/bitcoin-transfer.js +200 -0
- package/dist/wallet/tx/cog.d.ts +5 -1
- package/dist/wallet/tx/cog.js +149 -185
- package/dist/wallet/tx/common.d.ts +61 -8
- package/dist/wallet/tx/common.js +266 -146
- package/dist/wallet/tx/domain-admin.d.ts +3 -1
- package/dist/wallet/tx/domain-admin.js +61 -99
- package/dist/wallet/tx/domain-market.d.ts +5 -1
- package/dist/wallet/tx/domain-market.js +221 -228
- package/dist/wallet/tx/field.d.ts +4 -10
- package/dist/wallet/tx/field.js +83 -924
- package/dist/wallet/tx/identity-selector.d.ts +9 -3
- package/dist/wallet/tx/identity-selector.js +17 -35
- package/dist/wallet/tx/index.d.ts +3 -1
- package/dist/wallet/tx/index.js +2 -1
- package/dist/wallet/tx/register.d.ts +3 -1
- package/dist/wallet/tx/register.js +62 -220
- package/dist/wallet/tx/reputation.d.ts +3 -1
- package/dist/wallet/tx/reputation.js +58 -95
- package/dist/wallet/types.d.ts +8 -122
- package/package.json +5 -5
- package/dist/wallet/archive.d.ts +0 -4
- package/dist/wallet/archive.js +0 -41
- package/dist/wallet/mining/hook-protocol.d.ts +0 -47
- package/dist/wallet/mining/hook-protocol.js +0 -161
- package/dist/wallet/mining/hook-runner.js +0 -52
- package/dist/wallet/mining/hooks.d.ts +0 -38
- package/dist/wallet/mining/hooks.js +0 -520
- package/dist/wallet/state/explicit-lock.d.ts +0 -4
- package/dist/wallet/state/explicit-lock.js +0 -19
- package/dist/wallet/state/session.d.ts +0 -12
- package/dist/wallet/state/session.js +0 -23
- /package/dist/wallet/{mining/hook-runner.d.ts → state/client-password-agent.d.ts} +0 -0
package/dist/wallet/lifecycle.js
CHANGED
|
@@ -7,25 +7,21 @@ import { attachOrStartManagedBitcoindService, createManagedWalletReplica, probeM
|
|
|
7
7
|
import { resolveManagedServicePaths } from "../bitcoind/service-paths.js";
|
|
8
8
|
import { createRpcClient } from "../bitcoind/node.js";
|
|
9
9
|
import { openSqliteStore } from "../sqlite/index.js";
|
|
10
|
-
import {
|
|
11
|
-
import { DEFAULT_PROACTIVE_RESERVE_SATS, normalizeWalletStateRecord, persistWalletCoinControlStateIfNeeded, } from "./coin-control.js";
|
|
10
|
+
import { normalizeWalletStateRecord, persistWalletCoinControlStateIfNeeded, } from "./coin-control.js";
|
|
12
11
|
import { normalizeWalletDescriptorState, persistNormalizedWalletDescriptorStateIfNeeded, persistWalletStateUpdate, resolveNormalizedWalletDescriptorState, stripDescriptorChecksum, } from "./descriptor-normalization.js";
|
|
13
|
-
import { acquireFileLock, clearOrphanedFileLock } from "./fs/lock.js";
|
|
12
|
+
import { acquireFileLock, clearOrphanedFileLock, readLockMetadata } from "./fs/lock.js";
|
|
14
13
|
import { createInternalCoreWalletPassphrase, createMnemonicConfirmationChallenge, deriveWalletMaterialFromMnemonic, generateWalletMaterial, isEnglishMnemonicWord, validateEnglishMnemonic, } from "./material.js";
|
|
15
14
|
import { deriveWalletRuntimePathsForSeed, resolveWalletRuntimePathsForTesting, } from "./runtime.js";
|
|
16
|
-
import {
|
|
15
|
+
import { readMiningGenerationActivity } from "./mining/coordination.js";
|
|
17
16
|
import { loadClientConfig } from "./mining/config.js";
|
|
18
|
-
import { inspectMiningHookState } from "./mining/hooks.js";
|
|
19
17
|
import { loadMiningRuntimeStatus, saveMiningRuntimeStatus } from "./mining/runtime-artifacts.js";
|
|
20
18
|
import { normalizeMiningStateRecord } from "./mining/state.js";
|
|
21
19
|
import { renderWalletMnemonicRevealArt } from "./mnemonic-art.js";
|
|
22
|
-
import { clearWalletExplicitLock, loadWalletExplicitLock, saveWalletExplicitLock, } from "./state/explicit-lock.js";
|
|
23
20
|
import { clearWalletPendingInitializationState, loadWalletPendingInitializationStateOrNull, saveWalletPendingInitializationState, } from "./state/pending-init.js";
|
|
24
|
-
import { clearUnlockSession, loadUnlockSession, saveUnlockSession } from "./state/session.js";
|
|
25
21
|
import { addImportedWalletSeedRecord, assertValidImportedWalletSeedName, ensureMainWalletSeedIndexRecord, findWalletSeedRecord, loadWalletSeedIndex, removeWalletSeedRecord, } from "./state/seed-index.js";
|
|
26
|
-
import { createDefaultWalletSecretProvider, createWalletPendingInitSecretReference, createWalletRootId, createWalletSecretReference, } from "./state/provider.js";
|
|
22
|
+
import { createDefaultWalletSecretProvider, ensureClientPasswordConfigured, createWalletPendingInitSecretReference, createWalletRootId, createWalletSecretReference, withInteractiveWalletSecretProvider, } from "./state/provider.js";
|
|
23
|
+
import { clearLegacyWalletLockArtifacts, withUnlockedManagedCoreWallet, } from "./managed-core-wallet.js";
|
|
27
24
|
import { extractWalletRootIdHintFromWalletStateEnvelope, loadRawWalletStateEnvelope, loadWalletState, saveWalletState, } from "./state/storage.js";
|
|
28
|
-
export const DEFAULT_UNLOCK_DURATION_MS = 15 * 60 * 1000;
|
|
29
25
|
export { previewResetWallet, resetWallet, } from "./reset.js";
|
|
30
26
|
function sanitizeWalletName(walletRootId) {
|
|
31
27
|
return `cogcoin-${walletRootId}`.replace(/[^a-zA-Z0-9._-]+/g, "-").slice(0, 63);
|
|
@@ -99,16 +95,12 @@ async function loadOrCreatePendingInitializationMaterial(options) {
|
|
|
99
95
|
}
|
|
100
96
|
function createInitialWalletState(options) {
|
|
101
97
|
return {
|
|
102
|
-
schemaVersion:
|
|
98
|
+
schemaVersion: 5,
|
|
103
99
|
stateRevision: 1,
|
|
104
100
|
lastWrittenAtUnixMs: options.nowUnixMs,
|
|
105
101
|
walletRootId: options.walletRootId,
|
|
106
102
|
network: "mainnet",
|
|
107
|
-
|
|
108
|
-
proactiveReserveSats: DEFAULT_PROACTIVE_RESERVE_SATS,
|
|
109
|
-
proactiveReserveOutpoints: [],
|
|
110
|
-
nextDedicatedIndex: 1,
|
|
111
|
-
fundingIndex: 0,
|
|
103
|
+
localScriptPubKeyHexes: [options.material.funding.scriptPubKeyHex],
|
|
112
104
|
mnemonic: {
|
|
113
105
|
phrase: options.material.mnemonic.phrase,
|
|
114
106
|
language: options.material.mnemonic.language,
|
|
@@ -135,21 +127,12 @@ function createInitialWalletState(options) {
|
|
|
135
127
|
walletName: sanitizeWalletName(options.walletRootId),
|
|
136
128
|
internalPassphrase: options.internalCoreWalletPassphrase,
|
|
137
129
|
descriptorChecksum: null,
|
|
138
|
-
|
|
139
|
-
|
|
130
|
+
walletAddress: null,
|
|
131
|
+
walletScriptPubKeyHex: null,
|
|
140
132
|
proofStatus: "not-proven",
|
|
141
133
|
lastImportedAtUnixMs: null,
|
|
142
134
|
lastVerifiedAtUnixMs: null,
|
|
143
135
|
},
|
|
144
|
-
identities: [
|
|
145
|
-
{
|
|
146
|
-
index: 0,
|
|
147
|
-
scriptPubKeyHex: options.material.funding.scriptPubKeyHex,
|
|
148
|
-
address: options.material.funding.address,
|
|
149
|
-
status: "funding",
|
|
150
|
-
assignedDomainNames: [],
|
|
151
|
-
},
|
|
152
|
-
],
|
|
153
136
|
domains: [],
|
|
154
137
|
miningState: {
|
|
155
138
|
runMode: "stopped",
|
|
@@ -172,7 +155,7 @@ function createInitialWalletState(options) {
|
|
|
172
155
|
currentBlockTargetHeight: null,
|
|
173
156
|
currentReferencedBlockHashDisplay: null,
|
|
174
157
|
currentIntentFingerprintHex: null,
|
|
175
|
-
|
|
158
|
+
livePublishInMempool: null,
|
|
176
159
|
currentPublishDecision: null,
|
|
177
160
|
replacementCount: 0,
|
|
178
161
|
currentBlockFeeSpentSats: "0",
|
|
@@ -180,44 +163,11 @@ function createInitialWalletState(options) {
|
|
|
180
163
|
lifetimeFeeSpentSats: "0",
|
|
181
164
|
sharedMiningConflictOutpoint: null,
|
|
182
165
|
},
|
|
183
|
-
hookClientState: {
|
|
184
|
-
mining: {
|
|
185
|
-
mode: "builtin",
|
|
186
|
-
validationState: "never",
|
|
187
|
-
lastValidationAtUnixMs: null,
|
|
188
|
-
lastValidationError: null,
|
|
189
|
-
validatedLaunchFingerprint: null,
|
|
190
|
-
validatedFullFingerprint: null,
|
|
191
|
-
fullTrustWarningAcknowledgedAtUnixMs: null,
|
|
192
|
-
consecutiveFailureCount: 0,
|
|
193
|
-
cooldownUntilUnixMs: null,
|
|
194
|
-
},
|
|
195
|
-
},
|
|
196
|
-
proactiveFamilies: [],
|
|
197
166
|
pendingMutations: [],
|
|
198
167
|
};
|
|
199
168
|
}
|
|
200
|
-
function
|
|
201
|
-
return {
|
|
202
|
-
schemaVersion: 1,
|
|
203
|
-
walletRootId: state.walletRootId,
|
|
204
|
-
sessionId: randomBytes(16).toString("hex"),
|
|
205
|
-
createdAtUnixMs: nowUnixMs,
|
|
206
|
-
unlockUntilUnixMs,
|
|
207
|
-
sourceStateRevision: state.stateRevision,
|
|
208
|
-
wrappedSessionKeyMaterial: secretKeyId,
|
|
209
|
-
};
|
|
210
|
-
}
|
|
211
|
-
function createWalletExplicitLock(walletRootId, nowUnixMs) {
|
|
212
|
-
return {
|
|
213
|
-
schemaVersion: 1,
|
|
214
|
-
walletRootId,
|
|
215
|
-
lockedAtUnixMs: nowUnixMs,
|
|
216
|
-
};
|
|
217
|
-
}
|
|
218
|
-
async function normalizeUnlockedWalletStateIfNeeded(options) {
|
|
169
|
+
async function normalizeLoadedWalletStateIfNeeded(options) {
|
|
219
170
|
let state = options.state;
|
|
220
|
-
let session = options.session;
|
|
221
171
|
let source = options.source;
|
|
222
172
|
if (options.dataDir !== undefined) {
|
|
223
173
|
const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
|
|
@@ -233,14 +183,12 @@ async function normalizeUnlockedWalletStateIfNeeded(options) {
|
|
|
233
183
|
provider: options.provider,
|
|
234
184
|
secretReference: createWalletSecretReference(state.walletRootId),
|
|
235
185
|
},
|
|
236
|
-
session,
|
|
237
186
|
paths: options.paths,
|
|
238
187
|
nowUnixMs: options.nowUnixMs,
|
|
239
188
|
replacePrimary: options.source === "backup",
|
|
240
189
|
rpc: (options.rpcFactory ?? createRpcClient)(node.rpc),
|
|
241
190
|
});
|
|
242
191
|
state = normalized.state;
|
|
243
|
-
session = normalized.session ?? session;
|
|
244
192
|
source = normalized.changed ? "primary" : options.source;
|
|
245
193
|
const coinControl = await persistWalletCoinControlStateIfNeeded({
|
|
246
194
|
state,
|
|
@@ -248,14 +196,12 @@ async function normalizeUnlockedWalletStateIfNeeded(options) {
|
|
|
248
196
|
provider: options.provider,
|
|
249
197
|
secretReference: createWalletSecretReference(state.walletRootId),
|
|
250
198
|
},
|
|
251
|
-
session,
|
|
252
199
|
paths: options.paths,
|
|
253
200
|
nowUnixMs: options.nowUnixMs,
|
|
254
201
|
replacePrimary: source === "backup",
|
|
255
202
|
rpc: createRpcClient(node.rpc),
|
|
256
203
|
});
|
|
257
204
|
state = coinControl.state;
|
|
258
|
-
session = coinControl.session ?? session;
|
|
259
205
|
source = coinControl.changed ? "primary" : source;
|
|
260
206
|
}
|
|
261
207
|
finally {
|
|
@@ -263,7 +209,6 @@ async function normalizeUnlockedWalletStateIfNeeded(options) {
|
|
|
263
209
|
}
|
|
264
210
|
}
|
|
265
211
|
return {
|
|
266
|
-
session,
|
|
267
212
|
state: normalizeWalletStateRecord({
|
|
268
213
|
...state,
|
|
269
214
|
miningState: normalizeMiningStateRecord(state.miningState),
|
|
@@ -271,106 +216,6 @@ async function normalizeUnlockedWalletStateIfNeeded(options) {
|
|
|
271
216
|
source,
|
|
272
217
|
};
|
|
273
218
|
}
|
|
274
|
-
function createPortableWalletArchivePayload(state, exportedAtUnixMs) {
|
|
275
|
-
return {
|
|
276
|
-
schemaVersion: 1,
|
|
277
|
-
exportedAtUnixMs,
|
|
278
|
-
walletRootId: state.walletRootId,
|
|
279
|
-
network: state.network,
|
|
280
|
-
anchorValueSats: state.anchorValueSats,
|
|
281
|
-
proactiveReserveSats: state.proactiveReserveSats,
|
|
282
|
-
proactiveReserveOutpoints: state.proactiveReserveOutpoints,
|
|
283
|
-
nextDedicatedIndex: state.nextDedicatedIndex,
|
|
284
|
-
fundingIndex: state.fundingIndex,
|
|
285
|
-
mnemonic: {
|
|
286
|
-
phrase: state.mnemonic.phrase,
|
|
287
|
-
language: state.mnemonic.language,
|
|
288
|
-
},
|
|
289
|
-
expected: {
|
|
290
|
-
masterFingerprintHex: state.keys.masterFingerprintHex,
|
|
291
|
-
accountPath: state.keys.accountPath,
|
|
292
|
-
accountXpub: state.keys.accountXpub,
|
|
293
|
-
publicExternalDescriptor: stripDescriptorChecksum(state.descriptor.publicExternal),
|
|
294
|
-
descriptorChecksum: state.descriptor.checksum,
|
|
295
|
-
rangeEnd: state.descriptor.rangeEnd,
|
|
296
|
-
safetyMargin: state.descriptor.safetyMargin,
|
|
297
|
-
fundingAddress0: state.funding.address,
|
|
298
|
-
fundingScriptPubKeyHex0: state.funding.scriptPubKeyHex,
|
|
299
|
-
walletBirthTime: state.walletBirthTime,
|
|
300
|
-
},
|
|
301
|
-
identities: state.identities,
|
|
302
|
-
domains: state.domains,
|
|
303
|
-
miningState: normalizeMiningStateRecord(state.miningState),
|
|
304
|
-
hookClientState: state.hookClientState,
|
|
305
|
-
proactiveFamilies: state.proactiveFamilies.filter((family) => family.status === "confirmed" || family.status === "canceled"),
|
|
306
|
-
};
|
|
307
|
-
}
|
|
308
|
-
function createWalletStateFromPortableArchive(options) {
|
|
309
|
-
const material = deriveWalletMaterialFromMnemonic(options.payload.mnemonic.phrase);
|
|
310
|
-
if (material.keys.masterFingerprintHex !== options.payload.expected.masterFingerprintHex
|
|
311
|
-
|| material.keys.accountPath !== options.payload.expected.accountPath
|
|
312
|
-
|| material.keys.accountXpub !== options.payload.expected.accountXpub
|
|
313
|
-
|| stripDescriptorChecksum(material.descriptor.publicExternal) !== stripDescriptorChecksum(options.payload.expected.publicExternalDescriptor)
|
|
314
|
-
|| material.funding.address !== options.payload.expected.fundingAddress0
|
|
315
|
-
|| material.funding.scriptPubKeyHex !== options.payload.expected.fundingScriptPubKeyHex0) {
|
|
316
|
-
throw new Error("wallet_import_material_mismatch");
|
|
317
|
-
}
|
|
318
|
-
const baseState = createInitialWalletState({
|
|
319
|
-
walletRootId: options.payload.walletRootId,
|
|
320
|
-
nowUnixMs: options.nowUnixMs,
|
|
321
|
-
material,
|
|
322
|
-
internalCoreWalletPassphrase: options.internalCoreWalletPassphrase,
|
|
323
|
-
});
|
|
324
|
-
return {
|
|
325
|
-
...baseState,
|
|
326
|
-
walletRootId: options.payload.walletRootId,
|
|
327
|
-
network: options.payload.network,
|
|
328
|
-
anchorValueSats: options.payload.anchorValueSats,
|
|
329
|
-
proactiveReserveSats: options.payload.proactiveReserveSats,
|
|
330
|
-
proactiveReserveOutpoints: options.payload.proactiveReserveOutpoints,
|
|
331
|
-
nextDedicatedIndex: options.payload.nextDedicatedIndex,
|
|
332
|
-
fundingIndex: options.payload.fundingIndex,
|
|
333
|
-
walletBirthTime: options.payload.expected.walletBirthTime,
|
|
334
|
-
descriptor: {
|
|
335
|
-
...baseState.descriptor,
|
|
336
|
-
checksum: options.payload.expected.descriptorChecksum,
|
|
337
|
-
rangeEnd: options.payload.expected.rangeEnd,
|
|
338
|
-
safetyMargin: options.payload.expected.safetyMargin,
|
|
339
|
-
},
|
|
340
|
-
identities: options.payload.identities,
|
|
341
|
-
domains: options.payload.domains,
|
|
342
|
-
miningState: normalizeMiningStateRecord(options.payload.miningState),
|
|
343
|
-
hookClientState: options.payload.hookClientState,
|
|
344
|
-
proactiveFamilies: options.payload.proactiveFamilies,
|
|
345
|
-
pendingMutations: [],
|
|
346
|
-
};
|
|
347
|
-
}
|
|
348
|
-
function isExportBlockedByLocalState(state) {
|
|
349
|
-
if (state.miningState.state === "repair-required"
|
|
350
|
-
|| state.miningState.currentPublishState === "broadcasting"
|
|
351
|
-
|| state.miningState.currentPublishState === "broadcast-unknown"
|
|
352
|
-
|| state.miningState.currentPublishState === "in-mempool") {
|
|
353
|
-
return "wallet_export_requires_quiescent_local_state";
|
|
354
|
-
}
|
|
355
|
-
if (state.proactiveFamilies.some((family) => family.status === "draft"
|
|
356
|
-
|| family.status === "broadcasting"
|
|
357
|
-
|| family.status === "broadcast-unknown"
|
|
358
|
-
|| family.status === "live"
|
|
359
|
-
|| family.status === "repair-required")) {
|
|
360
|
-
return "wallet_export_requires_quiescent_local_state";
|
|
361
|
-
}
|
|
362
|
-
if (state.domains.some((domain) => domain.localAnchorIntent === "repair-required")) {
|
|
363
|
-
return "wallet_export_requires_quiescent_local_state";
|
|
364
|
-
}
|
|
365
|
-
if ((state.pendingMutations ?? []).some((mutation) => mutation.status === "draft"
|
|
366
|
-
|| mutation.status === "broadcasting"
|
|
367
|
-
|| mutation.status === "broadcast-unknown"
|
|
368
|
-
|| mutation.status === "live"
|
|
369
|
-
|| mutation.status === "repair-required")) {
|
|
370
|
-
return "wallet_export_requires_quiescent_local_state";
|
|
371
|
-
}
|
|
372
|
-
return null;
|
|
373
|
-
}
|
|
374
219
|
async function promptRequiredValue(prompter, message) {
|
|
375
220
|
const value = (await prompter.prompt(message)).trim();
|
|
376
221
|
if (value === "") {
|
|
@@ -384,14 +229,6 @@ async function promptHiddenValue(prompter, message) {
|
|
|
384
229
|
: await prompter.prompt(message);
|
|
385
230
|
return value.trim();
|
|
386
231
|
}
|
|
387
|
-
async function promptForArchivePassphrase(prompter, promptPrefix) {
|
|
388
|
-
const first = await promptRequiredValue(prompter, `${promptPrefix} passphrase: `);
|
|
389
|
-
const second = await promptRequiredValue(prompter, `Confirm ${promptPrefix.toLowerCase()} passphrase: `);
|
|
390
|
-
if (first !== second) {
|
|
391
|
-
throw new Error("wallet_archive_passphrase_mismatch");
|
|
392
|
-
}
|
|
393
|
-
return first;
|
|
394
|
-
}
|
|
395
232
|
async function promptForRestoreMnemonic(prompter) {
|
|
396
233
|
const words = [];
|
|
397
234
|
for (let index = 0; index < 24; index += 1) {
|
|
@@ -419,38 +256,12 @@ async function confirmRestoreReplacement(prompter) {
|
|
|
419
256
|
throw new Error("wallet_restore_replace_confirmation_required");
|
|
420
257
|
}
|
|
421
258
|
}
|
|
422
|
-
async function confirmOverwriteIfNeeded(prompter, path) {
|
|
423
|
-
if (!await pathExists(path)) {
|
|
424
|
-
return;
|
|
425
|
-
}
|
|
426
|
-
const answer = (await prompter.prompt(`Archive ${path} already exists. Overwrite it? Type yes to continue: `)).trim().toLowerCase();
|
|
427
|
-
if (answer !== "yes") {
|
|
428
|
-
throw new Error("wallet_export_overwrite_declined");
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
259
|
async function confirmYesNo(prompter, message) {
|
|
432
260
|
const answer = (await prompter.prompt(message)).trim().toLowerCase();
|
|
433
261
|
if (answer !== "yes") {
|
|
434
262
|
throw new Error("wallet_delete_confirmation_required");
|
|
435
263
|
}
|
|
436
264
|
}
|
|
437
|
-
async function readManagedSnapshotTip(options) {
|
|
438
|
-
const daemon = await attachOrStartIndexerDaemon({
|
|
439
|
-
dataDir: options.dataDir,
|
|
440
|
-
databasePath: options.databasePath,
|
|
441
|
-
walletRootId: options.walletRootId,
|
|
442
|
-
});
|
|
443
|
-
try {
|
|
444
|
-
const lease = await readSnapshotWithRetry(daemon, options.walletRootId);
|
|
445
|
-
return {
|
|
446
|
-
nodeBestHeight: lease.status.coreBestHeight,
|
|
447
|
-
snapshotHeight: lease.payload.tip?.height ?? null,
|
|
448
|
-
};
|
|
449
|
-
}
|
|
450
|
-
finally {
|
|
451
|
-
await daemon.close().catch(() => undefined);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
265
|
async function recreateManagedCoreWalletReplica(state, provider, paths, dataDir, nowUnixMs, options = {}) {
|
|
455
266
|
const walletName = sanitizeWalletName(state.walletRootId);
|
|
456
267
|
const walletDir = join(dataDir, "wallets", walletName);
|
|
@@ -739,14 +550,14 @@ function applyRepairStoppedMiningState(state) {
|
|
|
739
550
|
miningState: {
|
|
740
551
|
...miningState,
|
|
741
552
|
runMode: "stopped",
|
|
742
|
-
state: miningState.
|
|
553
|
+
state: miningState.livePublishInMempool
|
|
743
554
|
? miningState.state === "paused-stale"
|
|
744
555
|
? "paused-stale"
|
|
745
556
|
: "paused"
|
|
746
557
|
: miningState.state === "repair-required"
|
|
747
558
|
? "repair-required"
|
|
748
559
|
: "idle",
|
|
749
|
-
pauseReason: miningState.
|
|
560
|
+
pauseReason: miningState.livePublishInMempool
|
|
750
561
|
? miningState.state === "paused-stale"
|
|
751
562
|
? "stale-block-context"
|
|
752
563
|
: "wallet-repair"
|
|
@@ -766,11 +577,121 @@ function createStoppedBackgroundRuntimeSnapshot(snapshot, nowUnixMs) {
|
|
|
766
577
|
backgroundWorkerHeartbeatAtUnixMs: null,
|
|
767
578
|
backgroundWorkerHealth: null,
|
|
768
579
|
currentPhase: "idle",
|
|
769
|
-
note: snapshot.
|
|
580
|
+
note: snapshot.livePublishInMempool
|
|
770
581
|
? "Background mining stopped for wallet repair. The last mining transaction may still confirm from mempool."
|
|
771
582
|
: "Background mining stopped for wallet repair.",
|
|
772
583
|
};
|
|
773
584
|
}
|
|
585
|
+
function resolveMiningGenerationRequestPath(paths) {
|
|
586
|
+
return join(paths.miningRoot, "generation-request.json");
|
|
587
|
+
}
|
|
588
|
+
function resolveMiningGenerationActivityPath(paths) {
|
|
589
|
+
return join(paths.miningRoot, "generation-activity.json");
|
|
590
|
+
}
|
|
591
|
+
function normalizeRepairMiningPid(value) {
|
|
592
|
+
return typeof value === "number" && Number.isInteger(value) && value > 0
|
|
593
|
+
? value
|
|
594
|
+
: null;
|
|
595
|
+
}
|
|
596
|
+
function createRepairStoppedMiningNote(livePublishInMempool) {
|
|
597
|
+
return livePublishInMempool
|
|
598
|
+
? "Background mining stopped for wallet repair. The last mining transaction may still confirm from mempool."
|
|
599
|
+
: "Background mining stopped for wallet repair.";
|
|
600
|
+
}
|
|
601
|
+
function createStoppedMiningRuntimeSnapshotForRepair(options) {
|
|
602
|
+
const stoppedMiningState = normalizeMiningStateRecord(applyRepairStoppedMiningState(options.state).miningState);
|
|
603
|
+
const note = createRepairStoppedMiningNote(stoppedMiningState.livePublishInMempool);
|
|
604
|
+
if (options.snapshot !== null) {
|
|
605
|
+
return {
|
|
606
|
+
...createStoppedBackgroundRuntimeSnapshot(options.snapshot, options.nowUnixMs),
|
|
607
|
+
miningState: stoppedMiningState.state,
|
|
608
|
+
currentPublishState: stoppedMiningState.currentPublishState,
|
|
609
|
+
targetBlockHeight: stoppedMiningState.currentBlockTargetHeight,
|
|
610
|
+
referencedBlockHashDisplay: stoppedMiningState.currentReferencedBlockHashDisplay,
|
|
611
|
+
currentDomainId: stoppedMiningState.currentDomainId,
|
|
612
|
+
currentDomainName: stoppedMiningState.currentDomain,
|
|
613
|
+
currentSentenceDisplay: stoppedMiningState.currentSentence,
|
|
614
|
+
currentTxid: stoppedMiningState.currentTxid,
|
|
615
|
+
currentWtxid: stoppedMiningState.currentWtxid,
|
|
616
|
+
livePublishInMempool: stoppedMiningState.livePublishInMempool,
|
|
617
|
+
currentFeeRateSatVb: stoppedMiningState.currentFeeRateSatVb,
|
|
618
|
+
currentAbsoluteFeeSats: stoppedMiningState.currentAbsoluteFeeSats,
|
|
619
|
+
currentBlockFeeSpentSats: stoppedMiningState.currentBlockFeeSpentSats,
|
|
620
|
+
sessionFeeSpentSats: stoppedMiningState.sessionFeeSpentSats,
|
|
621
|
+
lifetimeFeeSpentSats: stoppedMiningState.lifetimeFeeSpentSats,
|
|
622
|
+
currentPublishDecision: stoppedMiningState.currentPublishDecision,
|
|
623
|
+
pauseReason: stoppedMiningState.pauseReason,
|
|
624
|
+
note,
|
|
625
|
+
};
|
|
626
|
+
}
|
|
627
|
+
return {
|
|
628
|
+
schemaVersion: 1,
|
|
629
|
+
walletRootId: options.state.walletRootId,
|
|
630
|
+
workerApiVersion: null,
|
|
631
|
+
workerBinaryVersion: null,
|
|
632
|
+
workerBuildId: null,
|
|
633
|
+
updatedAtUnixMs: options.nowUnixMs,
|
|
634
|
+
runMode: "stopped",
|
|
635
|
+
backgroundWorkerPid: null,
|
|
636
|
+
backgroundWorkerRunId: null,
|
|
637
|
+
backgroundWorkerHeartbeatAtUnixMs: null,
|
|
638
|
+
backgroundWorkerHealth: null,
|
|
639
|
+
indexerDaemonState: null,
|
|
640
|
+
indexerDaemonInstanceId: null,
|
|
641
|
+
indexerSnapshotSeq: null,
|
|
642
|
+
indexerSnapshotOpenedAtUnixMs: null,
|
|
643
|
+
indexerTruthSource: undefined,
|
|
644
|
+
indexerHeartbeatAtUnixMs: null,
|
|
645
|
+
coreBestHeight: null,
|
|
646
|
+
coreBestHash: null,
|
|
647
|
+
indexerTipHeight: null,
|
|
648
|
+
indexerTipHash: null,
|
|
649
|
+
indexerReorgDepth: null,
|
|
650
|
+
indexerTipAligned: null,
|
|
651
|
+
corePublishState: null,
|
|
652
|
+
providerState: null,
|
|
653
|
+
lastSuspendDetectedAtUnixMs: null,
|
|
654
|
+
reconnectSettledUntilUnixMs: null,
|
|
655
|
+
tipSettledUntilUnixMs: null,
|
|
656
|
+
miningState: stoppedMiningState.state,
|
|
657
|
+
currentPhase: "idle",
|
|
658
|
+
currentPublishState: stoppedMiningState.currentPublishState,
|
|
659
|
+
targetBlockHeight: stoppedMiningState.currentBlockTargetHeight,
|
|
660
|
+
referencedBlockHashDisplay: stoppedMiningState.currentReferencedBlockHashDisplay,
|
|
661
|
+
currentDomainId: stoppedMiningState.currentDomainId,
|
|
662
|
+
currentDomainName: stoppedMiningState.currentDomain,
|
|
663
|
+
currentSentenceDisplay: stoppedMiningState.currentSentence,
|
|
664
|
+
currentCanonicalBlend: null,
|
|
665
|
+
currentTxid: stoppedMiningState.currentTxid,
|
|
666
|
+
currentWtxid: stoppedMiningState.currentWtxid,
|
|
667
|
+
livePublishInMempool: stoppedMiningState.livePublishInMempool,
|
|
668
|
+
currentFeeRateSatVb: stoppedMiningState.currentFeeRateSatVb,
|
|
669
|
+
currentAbsoluteFeeSats: stoppedMiningState.currentAbsoluteFeeSats,
|
|
670
|
+
currentBlockFeeSpentSats: stoppedMiningState.currentBlockFeeSpentSats,
|
|
671
|
+
sessionFeeSpentSats: stoppedMiningState.sessionFeeSpentSats,
|
|
672
|
+
lifetimeFeeSpentSats: stoppedMiningState.lifetimeFeeSpentSats,
|
|
673
|
+
sameDomainCompetitorSuppressed: null,
|
|
674
|
+
higherRankedCompetitorDomainCount: null,
|
|
675
|
+
dedupedCompetitorDomainCount: null,
|
|
676
|
+
competitivenessGateIndeterminate: null,
|
|
677
|
+
mempoolSequenceCacheStatus: null,
|
|
678
|
+
currentPublishDecision: stoppedMiningState.currentPublishDecision,
|
|
679
|
+
lastMempoolSequence: null,
|
|
680
|
+
lastCompetitivenessGateAtUnixMs: null,
|
|
681
|
+
pauseReason: stoppedMiningState.pauseReason,
|
|
682
|
+
providerConfigured: false,
|
|
683
|
+
providerKind: null,
|
|
684
|
+
bitcoindHealth: "unavailable",
|
|
685
|
+
bitcoindServiceState: null,
|
|
686
|
+
bitcoindReplicaStatus: null,
|
|
687
|
+
nodeHealth: "unavailable",
|
|
688
|
+
indexerHealth: "unavailable",
|
|
689
|
+
tipsAligned: null,
|
|
690
|
+
lastEventAtUnixMs: null,
|
|
691
|
+
lastError: null,
|
|
692
|
+
note,
|
|
693
|
+
};
|
|
694
|
+
}
|
|
774
695
|
async function persistRepairState(options) {
|
|
775
696
|
return await persistWalletStateUpdate({
|
|
776
697
|
state: options.state,
|
|
@@ -783,41 +704,57 @@ async function persistRepairState(options) {
|
|
|
783
704
|
replacePrimary: options.replacePrimary,
|
|
784
705
|
});
|
|
785
706
|
}
|
|
786
|
-
async function
|
|
787
|
-
const
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
707
|
+
async function cleanupMiningForRepair(options) {
|
|
708
|
+
const controlLockMetadata = await readLockMetadata(options.paths.miningControlLockPath).catch(() => null);
|
|
709
|
+
const generationActivity = await readMiningGenerationActivity(options.paths).catch(() => null);
|
|
710
|
+
const controlLockPid = normalizeRepairMiningPid(controlLockMetadata?.processId);
|
|
711
|
+
const backgroundWorkerPid = normalizeRepairMiningPid(options.snapshot?.backgroundWorkerPid);
|
|
712
|
+
const generationOwnerPid = normalizeRepairMiningPid(generationActivity?.generationOwnerPid);
|
|
713
|
+
const discoveredPids = new Set();
|
|
714
|
+
let backgroundWorkerAlive = false;
|
|
715
|
+
let foregroundWorkerAlive = false;
|
|
716
|
+
const pidSources = [
|
|
717
|
+
{ pid: backgroundWorkerPid, source: "background" },
|
|
718
|
+
{ pid: controlLockPid, source: "foreground" },
|
|
719
|
+
{ pid: generationOwnerPid, source: "foreground" },
|
|
720
|
+
];
|
|
721
|
+
for (const source of pidSources) {
|
|
722
|
+
if (source.pid === null || source.pid === process.pid || !await isProcessAlive(source.pid)) {
|
|
723
|
+
continue;
|
|
791
724
|
}
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
}
|
|
725
|
+
discoveredPids.add(source.pid);
|
|
726
|
+
if (source.source === "background") {
|
|
727
|
+
backgroundWorkerAlive = true;
|
|
796
728
|
}
|
|
797
|
-
|
|
729
|
+
else {
|
|
730
|
+
foregroundWorkerAlive = true;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
for (const pid of discoveredPids) {
|
|
734
|
+
await stopRecordedManagedProcess(pid, "mining_process_stop_timeout");
|
|
798
735
|
}
|
|
799
|
-
await
|
|
736
|
+
await rm(options.paths.miningControlLockPath, { force: true }).catch(() => undefined);
|
|
737
|
+
await rm(resolveMiningGenerationRequestPath(options.paths), { force: true }).catch(() => undefined);
|
|
738
|
+
await rm(resolveMiningGenerationActivityPath(options.paths), { force: true }).catch(() => undefined);
|
|
739
|
+
await saveMiningRuntimeStatus(options.paths.miningStatusPath, createStoppedMiningRuntimeSnapshotForRepair({
|
|
740
|
+
state: options.state,
|
|
741
|
+
snapshot: options.snapshot,
|
|
742
|
+
nowUnixMs: options.nowUnixMs,
|
|
743
|
+
}));
|
|
744
|
+
return {
|
|
745
|
+
preRepairRunMode: backgroundWorkerAlive
|
|
746
|
+
? "background"
|
|
747
|
+
: foregroundWorkerAlive
|
|
748
|
+
? "foreground"
|
|
749
|
+
: "stopped",
|
|
750
|
+
};
|
|
800
751
|
}
|
|
801
752
|
async function canResumeBackgroundMiningAfterRepair(options) {
|
|
802
|
-
if (options.
|
|
803
|
-
|| options.unlockUntilUnixMs <= options.nowUnixMs
|
|
804
|
-
|| options.bitcoindPostRepairHealth !== "ready"
|
|
753
|
+
if (options.bitcoindPostRepairHealth !== "ready"
|
|
805
754
|
|| options.indexerPostRepairHealth !== "synced"
|
|
806
755
|
|| normalizeMiningStateRecord(options.repairedState.miningState).state === "repair-required") {
|
|
807
756
|
return false;
|
|
808
757
|
}
|
|
809
|
-
const hookMode = options.repairedState.hookClientState.mining?.mode ?? "builtin";
|
|
810
|
-
if (hookMode === "custom") {
|
|
811
|
-
const inspection = await inspectMiningHookState({
|
|
812
|
-
hookRootPath: options.paths.hooksMiningDir,
|
|
813
|
-
entrypointPath: options.paths.hooksMiningEntrypointPath,
|
|
814
|
-
packagePath: options.paths.hooksMiningPackageJsonPath,
|
|
815
|
-
localState: options.repairedState.hookClientState.mining ?? null,
|
|
816
|
-
verify: false,
|
|
817
|
-
nowUnixMs: options.nowUnixMs,
|
|
818
|
-
});
|
|
819
|
-
return inspection.operatorValidationState === "current" && !inspection.cooldownActive;
|
|
820
|
-
}
|
|
821
758
|
try {
|
|
822
759
|
const config = await loadClientConfig({
|
|
823
760
|
path: options.paths.clientConfigPath,
|
|
@@ -829,29 +766,6 @@ async function canResumeBackgroundMiningAfterRepair(options) {
|
|
|
829
766
|
return false;
|
|
830
767
|
}
|
|
831
768
|
}
|
|
832
|
-
export function parseUnlockDurationToMs(raw) {
|
|
833
|
-
if (raw == null || raw.trim() === "") {
|
|
834
|
-
return DEFAULT_UNLOCK_DURATION_MS;
|
|
835
|
-
}
|
|
836
|
-
const match = /^([1-9][0-9]*)([smhd])$/i.exec(raw.trim());
|
|
837
|
-
if (match == null) {
|
|
838
|
-
throw new Error("wallet_unlock_duration_invalid");
|
|
839
|
-
}
|
|
840
|
-
const value = Number.parseInt(match[1], 10);
|
|
841
|
-
const unit = match[2].toLowerCase();
|
|
842
|
-
const multiplier = unit === "s"
|
|
843
|
-
? 1_000
|
|
844
|
-
: unit === "m"
|
|
845
|
-
? 60_000
|
|
846
|
-
: unit === "h"
|
|
847
|
-
? 3_600_000
|
|
848
|
-
: 86_400_000;
|
|
849
|
-
const duration = value * multiplier;
|
|
850
|
-
if (!Number.isFinite(duration) || duration <= 0) {
|
|
851
|
-
throw new Error("wallet_unlock_duration_invalid");
|
|
852
|
-
}
|
|
853
|
-
return duration;
|
|
854
|
-
}
|
|
855
769
|
async function ensureWalletNotInitialized(paths, provider) {
|
|
856
770
|
if (await pathExists(paths.walletStatePath) || await pathExists(paths.walletStateBackupPath)) {
|
|
857
771
|
await clearPendingInitialization(paths, provider);
|
|
@@ -860,8 +774,7 @@ async function ensureWalletNotInitialized(paths, provider) {
|
|
|
860
774
|
}
|
|
861
775
|
function isWalletSecretAccessError(error) {
|
|
862
776
|
const message = error instanceof Error ? error.message : String(error);
|
|
863
|
-
return message
|
|
864
|
-
|| message.startsWith("wallet_secret_missing_")
|
|
777
|
+
return message.startsWith("wallet_secret_missing_")
|
|
865
778
|
|| message.startsWith("wallet_secret_provider_");
|
|
866
779
|
}
|
|
867
780
|
function writeMnemonicReveal(prompter, phrase, introLines) {
|
|
@@ -898,22 +811,23 @@ async function importDescriptorIntoManagedCoreWallet(state, provider, paths, dat
|
|
|
898
811
|
});
|
|
899
812
|
const normalizedDescriptors = await resolveNormalizedWalletDescriptorState(state, rpc);
|
|
900
813
|
const walletName = sanitizeWalletName(state.walletRootId);
|
|
901
|
-
await
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
814
|
+
await withUnlockedManagedCoreWallet({
|
|
815
|
+
rpc,
|
|
816
|
+
walletName,
|
|
817
|
+
internalPassphrase: state.managedCoreWallet.internalPassphrase,
|
|
818
|
+
run: async () => {
|
|
819
|
+
const importResults = await rpc.importDescriptors(walletName, [{
|
|
820
|
+
desc: normalizedDescriptors.privateExternal,
|
|
821
|
+
timestamp: state.walletBirthTime,
|
|
822
|
+
active: false,
|
|
823
|
+
internal: false,
|
|
824
|
+
range: [0, state.descriptor.rangeEnd],
|
|
825
|
+
}]);
|
|
826
|
+
if (!importResults.every((result) => result.success)) {
|
|
827
|
+
throw new Error(`wallet_descriptor_import_failed_${JSON.stringify(importResults)}`);
|
|
828
|
+
}
|
|
829
|
+
},
|
|
830
|
+
});
|
|
917
831
|
const derivedFunding = await rpc.deriveAddresses(normalizedDescriptors.publicExternal, [0, 0]);
|
|
918
832
|
if (derivedFunding[0] !== state.funding.address) {
|
|
919
833
|
throw new Error("wallet_funding_address_verification_failed");
|
|
@@ -950,8 +864,8 @@ async function importDescriptorIntoManagedCoreWallet(state, provider, paths, dat
|
|
|
950
864
|
...state.managedCoreWallet,
|
|
951
865
|
walletName,
|
|
952
866
|
descriptorChecksum: normalizedDescriptors.checksum,
|
|
953
|
-
|
|
954
|
-
|
|
867
|
+
walletAddress: verifiedReplica.fundingAddress0 ?? null,
|
|
868
|
+
walletScriptPubKeyHex: verifiedReplica.fundingScriptPubKeyHex0 ?? null,
|
|
955
869
|
proofStatus: "ready",
|
|
956
870
|
lastImportedAtUnixMs: nowUnixMs,
|
|
957
871
|
lastVerifiedAtUnixMs: nowUnixMs,
|
|
@@ -991,8 +905,8 @@ export async function verifyManagedCoreWalletReplica(state, dataDir, dependencie
|
|
|
991
905
|
created: false,
|
|
992
906
|
proofStatus: "missing",
|
|
993
907
|
descriptorChecksum: state.managedCoreWallet.descriptorChecksum,
|
|
994
|
-
fundingAddress0: state.managedCoreWallet.
|
|
995
|
-
fundingScriptPubKeyHex0: state.managedCoreWallet.
|
|
908
|
+
fundingAddress0: state.managedCoreWallet.walletAddress,
|
|
909
|
+
fundingScriptPubKeyHex0: state.managedCoreWallet.walletScriptPubKeyHex,
|
|
996
910
|
message: "Expected descriptor is missing from the managed Core wallet.",
|
|
997
911
|
};
|
|
998
912
|
}
|
|
@@ -1036,133 +950,40 @@ export async function verifyManagedCoreWalletReplica(state, dataDir, dependencie
|
|
|
1036
950
|
created: false,
|
|
1037
951
|
proofStatus: "not-proven",
|
|
1038
952
|
descriptorChecksum: state.managedCoreWallet.descriptorChecksum,
|
|
1039
|
-
fundingAddress0: state.managedCoreWallet.
|
|
1040
|
-
fundingScriptPubKeyHex0: state.managedCoreWallet.
|
|
953
|
+
fundingAddress0: state.managedCoreWallet.walletAddress,
|
|
954
|
+
fundingScriptPubKeyHex0: state.managedCoreWallet.walletScriptPubKeyHex,
|
|
1041
955
|
message: error instanceof Error ? error.message : String(error),
|
|
1042
956
|
};
|
|
1043
957
|
}
|
|
1044
958
|
}
|
|
1045
|
-
|
|
959
|
+
async function loadWalletStateForAccess(options = {}) {
|
|
1046
960
|
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
1047
961
|
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1048
962
|
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
}
|
|
1057
|
-
const loaded = await loadWalletState({
|
|
1058
|
-
primaryPath: paths.walletStatePath,
|
|
1059
|
-
backupPath: paths.walletStateBackupPath,
|
|
1060
|
-
}, {
|
|
1061
|
-
provider,
|
|
1062
|
-
});
|
|
1063
|
-
if (loaded.state.walletRootId !== session.walletRootId
|
|
1064
|
-
|| loaded.state.stateRevision !== session.sourceStateRevision) {
|
|
1065
|
-
await clearUnlockSession(paths.walletUnlockSessionPath);
|
|
1066
|
-
return null;
|
|
1067
|
-
}
|
|
1068
|
-
return await normalizeUnlockedWalletStateIfNeeded({
|
|
1069
|
-
provider,
|
|
1070
|
-
session,
|
|
1071
|
-
state: loaded.state,
|
|
1072
|
-
source: loaded.source,
|
|
1073
|
-
nowUnixMs,
|
|
1074
|
-
paths,
|
|
1075
|
-
dataDir: options.dataDir,
|
|
1076
|
-
attachService: options.attachService,
|
|
1077
|
-
rpcFactory: options.rpcFactory,
|
|
1078
|
-
});
|
|
1079
|
-
}
|
|
1080
|
-
catch {
|
|
1081
|
-
return null;
|
|
1082
|
-
}
|
|
1083
|
-
}
|
|
1084
|
-
export async function loadOrAutoUnlockWalletState(options = {}) {
|
|
1085
|
-
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
1086
|
-
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1087
|
-
const unlockDurationMs = options.unlockDurationMs ?? DEFAULT_UNLOCK_DURATION_MS;
|
|
1088
|
-
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1089
|
-
const loadExisting = () => loadUnlockedWalletState({
|
|
963
|
+
const loaded = await loadWalletState({
|
|
964
|
+
primaryPath: paths.walletStatePath,
|
|
965
|
+
backupPath: paths.walletStateBackupPath,
|
|
966
|
+
}, {
|
|
967
|
+
provider,
|
|
968
|
+
});
|
|
969
|
+
return normalizeLoadedWalletStateIfNeeded({
|
|
1090
970
|
provider,
|
|
971
|
+
state: loaded.state,
|
|
972
|
+
source: loaded.source,
|
|
1091
973
|
nowUnixMs,
|
|
1092
974
|
paths,
|
|
1093
975
|
dataDir: options.dataDir,
|
|
1094
976
|
attachService: options.attachService,
|
|
1095
977
|
rpcFactory: options.rpcFactory,
|
|
1096
978
|
});
|
|
1097
|
-
const existing = await loadExisting();
|
|
1098
|
-
if (existing !== null) {
|
|
1099
|
-
return existing;
|
|
1100
|
-
}
|
|
1101
|
-
const loadAndMaybeAutoUnlock = async () => {
|
|
1102
|
-
const reloaded = await loadExisting();
|
|
1103
|
-
if (reloaded !== null) {
|
|
1104
|
-
return reloaded;
|
|
1105
|
-
}
|
|
1106
|
-
let loaded;
|
|
1107
|
-
try {
|
|
1108
|
-
loaded = await loadWalletState({
|
|
1109
|
-
primaryPath: paths.walletStatePath,
|
|
1110
|
-
backupPath: paths.walletStateBackupPath,
|
|
1111
|
-
}, {
|
|
1112
|
-
provider,
|
|
1113
|
-
});
|
|
1114
|
-
}
|
|
1115
|
-
catch {
|
|
1116
|
-
return null;
|
|
1117
|
-
}
|
|
1118
|
-
const explicitLock = await loadWalletExplicitLock(paths.walletExplicitLockPath);
|
|
1119
|
-
if (explicitLock !== null) {
|
|
1120
|
-
if (explicitLock.walletRootId === loaded.state.walletRootId) {
|
|
1121
|
-
await clearUnlockSession(paths.walletUnlockSessionPath);
|
|
1122
|
-
return null;
|
|
1123
|
-
}
|
|
1124
|
-
await clearWalletExplicitLock(paths.walletExplicitLockPath);
|
|
1125
|
-
}
|
|
1126
|
-
const secretReference = createWalletSecretReference(loaded.state.walletRootId);
|
|
1127
|
-
const unlockUntilUnixMs = nowUnixMs + unlockDurationMs;
|
|
1128
|
-
const session = createUnlockSession(loaded.state, unlockUntilUnixMs, secretReference.keyId, nowUnixMs);
|
|
1129
|
-
await saveUnlockSession(paths.walletUnlockSessionPath, session, {
|
|
1130
|
-
provider,
|
|
1131
|
-
secretReference,
|
|
1132
|
-
});
|
|
1133
|
-
return await normalizeUnlockedWalletStateIfNeeded({
|
|
1134
|
-
provider,
|
|
1135
|
-
session,
|
|
1136
|
-
state: loaded.state,
|
|
1137
|
-
source: loaded.source,
|
|
1138
|
-
nowUnixMs,
|
|
1139
|
-
paths,
|
|
1140
|
-
dataDir: options.dataDir,
|
|
1141
|
-
attachService: options.attachService,
|
|
1142
|
-
rpcFactory: options.rpcFactory,
|
|
1143
|
-
});
|
|
1144
|
-
};
|
|
1145
|
-
if (options.controlLockHeld) {
|
|
1146
|
-
return await loadAndMaybeAutoUnlock();
|
|
1147
|
-
}
|
|
1148
|
-
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
1149
|
-
purpose: "wallet-auto-unlock",
|
|
1150
|
-
walletRootId: null,
|
|
1151
|
-
});
|
|
1152
|
-
try {
|
|
1153
|
-
return await loadAndMaybeAutoUnlock();
|
|
1154
|
-
}
|
|
1155
|
-
finally {
|
|
1156
|
-
await controlLock.release();
|
|
1157
|
-
}
|
|
1158
979
|
}
|
|
1159
980
|
export async function initializeWallet(options) {
|
|
1160
981
|
if (!options.prompter.isInteractive) {
|
|
1161
982
|
throw new Error("wallet_init_requires_tty");
|
|
1162
983
|
}
|
|
1163
984
|
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
985
|
+
const interactiveProvider = withInteractiveWalletSecretProvider(provider, options.prompter);
|
|
1164
986
|
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1165
|
-
const unlockDurationMs = options.unlockDurationMs ?? DEFAULT_UNLOCK_DURATION_MS;
|
|
1166
987
|
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1167
988
|
if (paths.selectedSeedName !== "main") {
|
|
1168
989
|
throw new Error("wallet_init_seed_not_supported");
|
|
@@ -1172,9 +993,28 @@ export async function initializeWallet(options) {
|
|
|
1172
993
|
walletRootId: null,
|
|
1173
994
|
});
|
|
1174
995
|
try {
|
|
1175
|
-
await
|
|
996
|
+
const passwordAction = await ensureClientPasswordConfigured(provider, options.prompter);
|
|
997
|
+
const hasWalletState = await pathExists(paths.walletStatePath) || await pathExists(paths.walletStateBackupPath);
|
|
998
|
+
if (hasWalletState) {
|
|
999
|
+
await clearPendingInitialization(paths, interactiveProvider);
|
|
1000
|
+
const loaded = await loadWalletStateForAccess({
|
|
1001
|
+
provider: interactiveProvider,
|
|
1002
|
+
nowUnixMs,
|
|
1003
|
+
paths,
|
|
1004
|
+
dataDir: options.dataDir,
|
|
1005
|
+
attachService: options.attachService,
|
|
1006
|
+
rpcFactory: options.rpcFactory,
|
|
1007
|
+
});
|
|
1008
|
+
return {
|
|
1009
|
+
passwordAction,
|
|
1010
|
+
walletAction: "already-initialized",
|
|
1011
|
+
walletRootId: loaded.state.walletRootId,
|
|
1012
|
+
fundingAddress: loaded.state.funding.address,
|
|
1013
|
+
state: loaded.state,
|
|
1014
|
+
};
|
|
1015
|
+
}
|
|
1176
1016
|
const material = await loadOrCreatePendingInitializationMaterial({
|
|
1177
|
-
provider,
|
|
1017
|
+
provider: interactiveProvider,
|
|
1178
1018
|
paths,
|
|
1179
1019
|
nowUnixMs,
|
|
1180
1020
|
});
|
|
@@ -1200,7 +1040,7 @@ export async function initializeWallet(options) {
|
|
|
1200
1040
|
const internalCoreWalletPassphrase = createInternalCoreWalletPassphrase();
|
|
1201
1041
|
const secretReference = createWalletSecretReference(walletRootId);
|
|
1202
1042
|
const secret = randomBytes(32);
|
|
1203
|
-
await
|
|
1043
|
+
await interactiveProvider.storeSecret(secretReference.keyId, secret);
|
|
1204
1044
|
const initialState = createInitialWalletState({
|
|
1205
1045
|
walletRootId,
|
|
1206
1046
|
nowUnixMs,
|
|
@@ -1215,27 +1055,23 @@ export async function initializeWallet(options) {
|
|
|
1215
1055
|
primaryPath: paths.walletStatePath,
|
|
1216
1056
|
backupPath: paths.walletStateBackupPath,
|
|
1217
1057
|
}, initialState, {
|
|
1218
|
-
provider,
|
|
1058
|
+
provider: interactiveProvider,
|
|
1219
1059
|
secretReference,
|
|
1220
1060
|
});
|
|
1221
|
-
return importDescriptorIntoManagedCoreWallet(initialState,
|
|
1222
|
-
});
|
|
1223
|
-
const unlockUntilUnixMs = nowUnixMs + unlockDurationMs;
|
|
1224
|
-
await clearWalletExplicitLock(paths.walletExplicitLockPath);
|
|
1225
|
-
await saveUnlockSession(paths.walletUnlockSessionPath, createUnlockSession(verifiedState, unlockUntilUnixMs, secretReference.keyId, nowUnixMs), {
|
|
1226
|
-
provider,
|
|
1227
|
-
secretReference,
|
|
1061
|
+
return importDescriptorIntoManagedCoreWallet(initialState, interactiveProvider, paths, options.dataDir, nowUnixMs, options.attachService, options.rpcFactory);
|
|
1228
1062
|
});
|
|
1229
|
-
await
|
|
1063
|
+
await clearLegacyWalletLockArtifacts(paths.walletRuntimeRoot);
|
|
1064
|
+
await clearPendingInitialization(paths, interactiveProvider);
|
|
1230
1065
|
await ensureMainWalletSeedIndexRecord({
|
|
1231
1066
|
paths: resolveMainWalletPaths(paths),
|
|
1232
1067
|
walletRootId,
|
|
1233
1068
|
nowUnixMs,
|
|
1234
1069
|
});
|
|
1235
1070
|
return {
|
|
1071
|
+
passwordAction,
|
|
1072
|
+
walletAction: "initialized",
|
|
1236
1073
|
walletRootId,
|
|
1237
1074
|
fundingAddress: verifiedState.funding.address,
|
|
1238
|
-
unlockUntilUnixMs,
|
|
1239
1075
|
state: verifiedState,
|
|
1240
1076
|
};
|
|
1241
1077
|
}
|
|
@@ -1248,6 +1084,7 @@ export async function showWalletMnemonic(options) {
|
|
|
1248
1084
|
throw new Error("wallet_show_mnemonic_requires_tty");
|
|
1249
1085
|
}
|
|
1250
1086
|
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
1087
|
+
const interactiveProvider = withInteractiveWalletSecretProvider(provider, options.prompter);
|
|
1251
1088
|
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1252
1089
|
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1253
1090
|
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
@@ -1262,32 +1099,19 @@ export async function showWalletMnemonic(options) {
|
|
|
1262
1099
|
if (!hasPrimaryStateFile && !hasBackupStateFile) {
|
|
1263
1100
|
throw new Error("wallet_uninitialized");
|
|
1264
1101
|
}
|
|
1265
|
-
const
|
|
1266
|
-
provider,
|
|
1102
|
+
const loaded = await loadWalletStateForAccess({
|
|
1103
|
+
provider: interactiveProvider,
|
|
1267
1104
|
nowUnixMs,
|
|
1268
1105
|
paths,
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
try {
|
|
1273
|
-
await loadWalletState({
|
|
1274
|
-
primaryPath: paths.walletStatePath,
|
|
1275
|
-
backupPath: paths.walletStateBackupPath,
|
|
1276
|
-
}, {
|
|
1277
|
-
provider,
|
|
1278
|
-
});
|
|
1279
|
-
}
|
|
1280
|
-
catch (error) {
|
|
1281
|
-
if (isWalletSecretAccessError(error)) {
|
|
1282
|
-
throw new Error("wallet_locked");
|
|
1283
|
-
}
|
|
1284
|
-
throw new Error("local-state-corrupt");
|
|
1106
|
+
}).catch((error) => {
|
|
1107
|
+
if (isWalletSecretAccessError(error)) {
|
|
1108
|
+
throw new Error("wallet_secret_provider_unavailable");
|
|
1285
1109
|
}
|
|
1286
|
-
throw new Error("
|
|
1287
|
-
}
|
|
1110
|
+
throw new Error("local-state-corrupt");
|
|
1111
|
+
});
|
|
1288
1112
|
await confirmTypedAcknowledgement(options.prompter, "show mnemonic", "Type \"show mnemonic\" to continue: ", "wallet_show_mnemonic_typed_ack_required");
|
|
1289
1113
|
let mnemonicRevealed = false;
|
|
1290
|
-
writeMnemonicReveal(options.prompter,
|
|
1114
|
+
writeMnemonicReveal(options.prompter, loaded.state.mnemonic.phrase, [
|
|
1291
1115
|
"Cogcoin Wallet Recovery Phrase",
|
|
1292
1116
|
"This 24-word recovery phrase controls the wallet.",
|
|
1293
1117
|
"",
|
|
@@ -1308,239 +1132,13 @@ export async function showWalletMnemonic(options) {
|
|
|
1308
1132
|
await controlLock.release();
|
|
1309
1133
|
}
|
|
1310
1134
|
}
|
|
1311
|
-
export async function unlockWallet(options = {}) {
|
|
1312
|
-
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
1313
|
-
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1314
|
-
const unlockDurationMs = options.unlockDurationMs ?? DEFAULT_UNLOCK_DURATION_MS;
|
|
1315
|
-
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1316
|
-
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
1317
|
-
purpose: "wallet-unlock",
|
|
1318
|
-
walletRootId: null,
|
|
1319
|
-
});
|
|
1320
|
-
try {
|
|
1321
|
-
const loaded = await loadWalletState({
|
|
1322
|
-
primaryPath: paths.walletStatePath,
|
|
1323
|
-
backupPath: paths.walletStateBackupPath,
|
|
1324
|
-
}, {
|
|
1325
|
-
provider,
|
|
1326
|
-
});
|
|
1327
|
-
const secretReference = createWalletSecretReference(loaded.state.walletRootId);
|
|
1328
|
-
const unlockUntilUnixMs = nowUnixMs + unlockDurationMs;
|
|
1329
|
-
await clearWalletExplicitLock(paths.walletExplicitLockPath);
|
|
1330
|
-
await saveUnlockSession(paths.walletUnlockSessionPath, createUnlockSession(loaded.state, unlockUntilUnixMs, secretReference.keyId, nowUnixMs), {
|
|
1331
|
-
provider,
|
|
1332
|
-
secretReference,
|
|
1333
|
-
});
|
|
1334
|
-
return {
|
|
1335
|
-
unlockUntilUnixMs,
|
|
1336
|
-
state: loaded.state,
|
|
1337
|
-
source: loaded.source,
|
|
1338
|
-
};
|
|
1339
|
-
}
|
|
1340
|
-
finally {
|
|
1341
|
-
await controlLock.release();
|
|
1342
|
-
}
|
|
1343
|
-
}
|
|
1344
|
-
export async function lockWallet(options) {
|
|
1345
|
-
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
1346
|
-
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1347
|
-
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1348
|
-
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
1349
|
-
purpose: "wallet-lock",
|
|
1350
|
-
walletRootId: null,
|
|
1351
|
-
});
|
|
1352
|
-
try {
|
|
1353
|
-
let walletRootId = null;
|
|
1354
|
-
let coreLocked = false;
|
|
1355
|
-
try {
|
|
1356
|
-
const loaded = await loadWalletState({
|
|
1357
|
-
primaryPath: paths.walletStatePath,
|
|
1358
|
-
backupPath: paths.walletStateBackupPath,
|
|
1359
|
-
}, {
|
|
1360
|
-
provider,
|
|
1361
|
-
});
|
|
1362
|
-
walletRootId = loaded.state.walletRootId;
|
|
1363
|
-
try {
|
|
1364
|
-
const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
|
|
1365
|
-
dataDir: options.dataDir,
|
|
1366
|
-
chain: "main",
|
|
1367
|
-
startHeight: 0,
|
|
1368
|
-
walletRootId,
|
|
1369
|
-
});
|
|
1370
|
-
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
1371
|
-
await rpc.walletLock(loaded.state.managedCoreWallet.walletName).catch(() => undefined);
|
|
1372
|
-
coreLocked = true;
|
|
1373
|
-
}
|
|
1374
|
-
catch {
|
|
1375
|
-
coreLocked = false;
|
|
1376
|
-
}
|
|
1377
|
-
}
|
|
1378
|
-
catch {
|
|
1379
|
-
walletRootId = null;
|
|
1380
|
-
}
|
|
1381
|
-
await clearUnlockSession(paths.walletUnlockSessionPath);
|
|
1382
|
-
if (walletRootId !== null) {
|
|
1383
|
-
await saveWalletExplicitLock(paths.walletExplicitLockPath, createWalletExplicitLock(walletRootId, nowUnixMs));
|
|
1384
|
-
}
|
|
1385
|
-
return {
|
|
1386
|
-
walletRootId,
|
|
1387
|
-
coreLocked,
|
|
1388
|
-
};
|
|
1389
|
-
}
|
|
1390
|
-
finally {
|
|
1391
|
-
await controlLock.release();
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
export async function exportWallet(options) {
|
|
1395
|
-
if (!options.prompter.isInteractive) {
|
|
1396
|
-
throw new Error("wallet_export_requires_tty");
|
|
1397
|
-
}
|
|
1398
|
-
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
1399
|
-
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1400
|
-
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1401
|
-
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
1402
|
-
purpose: "wallet-export",
|
|
1403
|
-
walletRootId: null,
|
|
1404
|
-
});
|
|
1405
|
-
try {
|
|
1406
|
-
const unlocked = await loadOrAutoUnlockWalletState({
|
|
1407
|
-
provider,
|
|
1408
|
-
nowUnixMs,
|
|
1409
|
-
paths,
|
|
1410
|
-
controlLockHeld: true,
|
|
1411
|
-
});
|
|
1412
|
-
if (unlocked === null) {
|
|
1413
|
-
throw new Error("wallet_locked");
|
|
1414
|
-
}
|
|
1415
|
-
const blockedReason = isExportBlockedByLocalState(unlocked.state);
|
|
1416
|
-
if (blockedReason !== null) {
|
|
1417
|
-
throw new Error(blockedReason);
|
|
1418
|
-
}
|
|
1419
|
-
const replica = await verifyManagedCoreWalletReplica(unlocked.state, options.dataDir, {
|
|
1420
|
-
attachService: options.attachService,
|
|
1421
|
-
rpcFactory: options.rpcFactory,
|
|
1422
|
-
});
|
|
1423
|
-
if (replica.proofStatus !== "ready") {
|
|
1424
|
-
throw new Error("wallet_export_core_replica_not_ready");
|
|
1425
|
-
}
|
|
1426
|
-
const tips = await (options.readSnapshotTip ?? readManagedSnapshotTip)({
|
|
1427
|
-
dataDir: options.dataDir,
|
|
1428
|
-
databasePath: options.databasePath,
|
|
1429
|
-
walletRootId: unlocked.state.walletRootId,
|
|
1430
|
-
});
|
|
1431
|
-
if (tips.snapshotHeight === null || tips.nodeBestHeight === null || tips.snapshotHeight !== tips.nodeBestHeight) {
|
|
1432
|
-
throw new Error("wallet_export_tip_mismatch");
|
|
1433
|
-
}
|
|
1434
|
-
await confirmOverwriteIfNeeded(options.prompter, options.archivePath);
|
|
1435
|
-
const passphrase = await promptForArchivePassphrase(options.prompter, "Archive");
|
|
1436
|
-
await writePortableWalletArchive(options.archivePath, createPortableWalletArchivePayload(unlocked.state, nowUnixMs), passphrase);
|
|
1437
|
-
return {
|
|
1438
|
-
archivePath: options.archivePath,
|
|
1439
|
-
walletRootId: unlocked.state.walletRootId,
|
|
1440
|
-
};
|
|
1441
|
-
}
|
|
1442
|
-
finally {
|
|
1443
|
-
await controlLock.release();
|
|
1444
|
-
}
|
|
1445
|
-
}
|
|
1446
|
-
export async function importWallet(options) {
|
|
1447
|
-
if (!options.prompter.isInteractive) {
|
|
1448
|
-
throw new Error("wallet_import_requires_tty");
|
|
1449
|
-
}
|
|
1450
|
-
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
1451
|
-
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1452
|
-
const unlockDurationMs = options.unlockDurationMs ?? DEFAULT_UNLOCK_DURATION_MS;
|
|
1453
|
-
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1454
|
-
if (paths.selectedSeedName !== "main") {
|
|
1455
|
-
throw new Error("wallet_import_seed_not_supported");
|
|
1456
|
-
}
|
|
1457
|
-
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
1458
|
-
purpose: "wallet-import",
|
|
1459
|
-
walletRootId: null,
|
|
1460
|
-
});
|
|
1461
|
-
try {
|
|
1462
|
-
const archivePassphrase = await promptRequiredValue(options.prompter, "Archive passphrase: ");
|
|
1463
|
-
const payload = await readPortableWalletArchive(options.archivePath, archivePassphrase);
|
|
1464
|
-
const replacementStateExists = await pathExists(paths.walletStatePath) || await pathExists(paths.walletStateBackupPath);
|
|
1465
|
-
const importedWalletDir = join(options.dataDir, "wallets", sanitizeWalletName(payload.walletRootId));
|
|
1466
|
-
const replacementCoreWalletExists = await pathExists(importedWalletDir);
|
|
1467
|
-
await clearPendingInitialization(paths, provider);
|
|
1468
|
-
if (replacementStateExists || replacementCoreWalletExists) {
|
|
1469
|
-
await confirmTypedAcknowledgement(options.prompter, "IMPORT", "Type IMPORT to replace the existing local wallet state and managed Core wallet replica: ");
|
|
1470
|
-
}
|
|
1471
|
-
let previousWalletRootId = null;
|
|
1472
|
-
try {
|
|
1473
|
-
const loaded = await loadWalletState({
|
|
1474
|
-
primaryPath: paths.walletStatePath,
|
|
1475
|
-
backupPath: paths.walletStateBackupPath,
|
|
1476
|
-
}, {
|
|
1477
|
-
provider,
|
|
1478
|
-
});
|
|
1479
|
-
previousWalletRootId = loaded.state.walletRootId;
|
|
1480
|
-
}
|
|
1481
|
-
catch {
|
|
1482
|
-
previousWalletRootId = null;
|
|
1483
|
-
}
|
|
1484
|
-
const secretReference = createWalletSecretReference(payload.walletRootId);
|
|
1485
|
-
const replacementSecret = randomBytes(32);
|
|
1486
|
-
await provider.storeSecret(secretReference.keyId, replacementSecret);
|
|
1487
|
-
const initialState = createWalletStateFromPortableArchive({
|
|
1488
|
-
payload,
|
|
1489
|
-
nowUnixMs,
|
|
1490
|
-
internalCoreWalletPassphrase: createInternalCoreWalletPassphrase(),
|
|
1491
|
-
});
|
|
1492
|
-
await clearUnlockSession(paths.walletUnlockSessionPath);
|
|
1493
|
-
await clearWalletExplicitLock(paths.walletExplicitLockPath);
|
|
1494
|
-
await saveWalletState({
|
|
1495
|
-
primaryPath: paths.walletStatePath,
|
|
1496
|
-
backupPath: paths.walletStateBackupPath,
|
|
1497
|
-
}, initialState, {
|
|
1498
|
-
provider,
|
|
1499
|
-
secretReference,
|
|
1500
|
-
});
|
|
1501
|
-
const importedState = await recreateManagedCoreWalletReplica(initialState, provider, paths, options.dataDir, nowUnixMs, {
|
|
1502
|
-
attachService: options.attachService,
|
|
1503
|
-
rpcFactory: options.rpcFactory,
|
|
1504
|
-
});
|
|
1505
|
-
const unlockUntilUnixMs = nowUnixMs + unlockDurationMs;
|
|
1506
|
-
await clearWalletExplicitLock(paths.walletExplicitLockPath);
|
|
1507
|
-
await saveUnlockSession(paths.walletUnlockSessionPath, createUnlockSession(importedState, unlockUntilUnixMs, secretReference.keyId, nowUnixMs), {
|
|
1508
|
-
provider,
|
|
1509
|
-
secretReference,
|
|
1510
|
-
});
|
|
1511
|
-
await clearPendingInitialization(paths, provider);
|
|
1512
|
-
if (previousWalletRootId !== null && previousWalletRootId !== payload.walletRootId) {
|
|
1513
|
-
await provider.deleteSecret(createWalletSecretReference(previousWalletRootId).keyId).catch(() => undefined);
|
|
1514
|
-
}
|
|
1515
|
-
await (options.attachIndexerDaemon ?? attachOrStartIndexerDaemon)({
|
|
1516
|
-
dataDir: options.dataDir,
|
|
1517
|
-
databasePath: options.databasePath,
|
|
1518
|
-
walletRootId: importedState.walletRootId,
|
|
1519
|
-
}).then((daemon) => daemon.close());
|
|
1520
|
-
await ensureMainWalletSeedIndexRecord({
|
|
1521
|
-
paths: resolveMainWalletPaths(paths),
|
|
1522
|
-
walletRootId: importedState.walletRootId,
|
|
1523
|
-
nowUnixMs,
|
|
1524
|
-
});
|
|
1525
|
-
return {
|
|
1526
|
-
archivePath: options.archivePath,
|
|
1527
|
-
walletRootId: importedState.walletRootId,
|
|
1528
|
-
fundingAddress: importedState.funding.address,
|
|
1529
|
-
unlockUntilUnixMs,
|
|
1530
|
-
state: importedState,
|
|
1531
|
-
};
|
|
1532
|
-
}
|
|
1533
|
-
finally {
|
|
1534
|
-
await controlLock.release();
|
|
1535
|
-
}
|
|
1536
|
-
}
|
|
1537
1135
|
export async function restoreWalletFromMnemonic(options) {
|
|
1538
1136
|
if (!options.prompter.isInteractive) {
|
|
1539
1137
|
throw new Error("wallet_restore_requires_tty");
|
|
1540
1138
|
}
|
|
1541
1139
|
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
1140
|
+
const interactiveProvider = withInteractiveWalletSecretProvider(provider, options.prompter);
|
|
1542
1141
|
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1543
|
-
const unlockDurationMs = options.unlockDurationMs ?? DEFAULT_UNLOCK_DURATION_MS;
|
|
1544
1142
|
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1545
1143
|
const seedName = assertValidImportedWalletSeedName(paths.selectedSeedName);
|
|
1546
1144
|
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
@@ -1556,7 +1154,8 @@ export async function restoreWalletFromMnemonic(options) {
|
|
|
1556
1154
|
if (findWalletSeedRecord(seedIndex, seedName) !== null) {
|
|
1557
1155
|
throw new Error("wallet_seed_name_exists");
|
|
1558
1156
|
}
|
|
1559
|
-
await
|
|
1157
|
+
const passwordAction = await ensureClientPasswordConfigured(provider, options.prompter);
|
|
1158
|
+
await ensureWalletNotInitialized(paths, interactiveProvider);
|
|
1560
1159
|
let promptPhaseStarted = false;
|
|
1561
1160
|
let mnemonicPhrase;
|
|
1562
1161
|
try {
|
|
@@ -1568,39 +1167,33 @@ export async function restoreWalletFromMnemonic(options) {
|
|
|
1568
1167
|
await options.prompter.clearSensitiveDisplay?.("restore-mnemonic-entry");
|
|
1569
1168
|
}
|
|
1570
1169
|
}
|
|
1571
|
-
await clearPendingInitialization(paths,
|
|
1170
|
+
await clearPendingInitialization(paths, interactiveProvider);
|
|
1572
1171
|
const material = deriveWalletMaterialFromMnemonic(mnemonicPhrase);
|
|
1573
1172
|
const walletRootId = createWalletRootId();
|
|
1574
1173
|
const internalCoreWalletPassphrase = createInternalCoreWalletPassphrase();
|
|
1575
1174
|
const secretReference = createWalletSecretReference(walletRootId);
|
|
1576
1175
|
const secret = randomBytes(32);
|
|
1577
|
-
await
|
|
1176
|
+
await interactiveProvider.storeSecret(secretReference.keyId, secret);
|
|
1578
1177
|
const initialState = createInitialWalletState({
|
|
1579
1178
|
walletRootId,
|
|
1580
1179
|
nowUnixMs,
|
|
1581
1180
|
material,
|
|
1582
1181
|
internalCoreWalletPassphrase,
|
|
1583
1182
|
});
|
|
1584
|
-
await
|
|
1585
|
-
await clearWalletExplicitLock(paths.walletExplicitLockPath);
|
|
1183
|
+
await clearLegacyWalletLockArtifacts(paths.walletRuntimeRoot);
|
|
1586
1184
|
await saveWalletState({
|
|
1587
1185
|
primaryPath: paths.walletStatePath,
|
|
1588
1186
|
backupPath: paths.walletStateBackupPath,
|
|
1589
1187
|
}, initialState, {
|
|
1590
|
-
provider,
|
|
1188
|
+
provider: interactiveProvider,
|
|
1591
1189
|
secretReference,
|
|
1592
1190
|
});
|
|
1593
|
-
const restoredState = await recreateManagedCoreWalletReplica(initialState,
|
|
1191
|
+
const restoredState = await recreateManagedCoreWalletReplica(initialState, interactiveProvider, paths, options.dataDir, nowUnixMs, {
|
|
1594
1192
|
attachService: options.attachService,
|
|
1595
1193
|
rpcFactory: options.rpcFactory,
|
|
1596
1194
|
});
|
|
1597
|
-
|
|
1598
|
-
await
|
|
1599
|
-
await saveUnlockSession(paths.walletUnlockSessionPath, createUnlockSession(restoredState, unlockUntilUnixMs, secretReference.keyId, nowUnixMs), {
|
|
1600
|
-
provider,
|
|
1601
|
-
secretReference,
|
|
1602
|
-
});
|
|
1603
|
-
await clearPendingInitialization(paths, provider);
|
|
1195
|
+
await clearLegacyWalletLockArtifacts(paths.walletRuntimeRoot);
|
|
1196
|
+
await clearPendingInitialization(paths, interactiveProvider);
|
|
1604
1197
|
await addImportedWalletSeedRecord({
|
|
1605
1198
|
paths: mainPaths,
|
|
1606
1199
|
seedName,
|
|
@@ -1608,10 +1201,10 @@ export async function restoreWalletFromMnemonic(options) {
|
|
|
1608
1201
|
nowUnixMs,
|
|
1609
1202
|
});
|
|
1610
1203
|
return {
|
|
1204
|
+
passwordAction,
|
|
1611
1205
|
seedName,
|
|
1612
1206
|
walletRootId,
|
|
1613
1207
|
fundingAddress: restoredState.funding.address,
|
|
1614
|
-
unlockUntilUnixMs,
|
|
1615
1208
|
state: restoredState,
|
|
1616
1209
|
warnings: [],
|
|
1617
1210
|
};
|
|
@@ -1670,8 +1263,7 @@ export async function deleteImportedWalletSeed(options) {
|
|
|
1670
1263
|
await rpc.unloadWallet(walletName, false).catch(() => undefined);
|
|
1671
1264
|
}
|
|
1672
1265
|
}
|
|
1673
|
-
await
|
|
1674
|
-
await clearWalletExplicitLock(paths.walletExplicitLockPath).catch(() => undefined);
|
|
1266
|
+
await clearLegacyWalletLockArtifacts(paths.walletRuntimeRoot).catch(() => undefined);
|
|
1675
1267
|
await clearPendingInitialization(paths, provider).catch(() => undefined);
|
|
1676
1268
|
await provider.deleteSecret(createWalletSecretReference(seedRecord.walletRootId).keyId).catch(() => undefined);
|
|
1677
1269
|
await rm(paths.walletStateRoot, { recursive: true, force: true }).catch(() => undefined);
|
|
@@ -1703,7 +1295,6 @@ export async function repairWallet(options) {
|
|
|
1703
1295
|
const attachManagedBitcoind = options.attachService ?? attachOrStartManagedBitcoindService;
|
|
1704
1296
|
const probeManagedIndexerDaemon = options.probeIndexerDaemon ?? probeIndexerDaemon;
|
|
1705
1297
|
const attachManagedIndexerDaemon = options.attachIndexerDaemon ?? attachOrStartIndexerDaemon;
|
|
1706
|
-
const requestMiningPreemptionForRepair = options.requestMiningPreemption ?? requestMiningGenerationPreemption;
|
|
1707
1298
|
await clearOrphanedRepairLocks([
|
|
1708
1299
|
paths.walletControlLockPath,
|
|
1709
1300
|
paths.miningControlLockPath,
|
|
@@ -1713,7 +1304,6 @@ export async function repairWallet(options) {
|
|
|
1713
1304
|
walletRootId: null,
|
|
1714
1305
|
});
|
|
1715
1306
|
try {
|
|
1716
|
-
let miningPreemption = null;
|
|
1717
1307
|
let loaded;
|
|
1718
1308
|
try {
|
|
1719
1309
|
loaded = await loadWalletState({
|
|
@@ -1723,7 +1313,7 @@ export async function repairWallet(options) {
|
|
|
1723
1313
|
provider,
|
|
1724
1314
|
});
|
|
1725
1315
|
}
|
|
1726
|
-
catch {
|
|
1316
|
+
catch (error) {
|
|
1727
1317
|
throw new Error("local-state-corrupt");
|
|
1728
1318
|
}
|
|
1729
1319
|
const recoveredFromBackup = loaded.source === "backup";
|
|
@@ -1736,25 +1326,15 @@ export async function repairWallet(options) {
|
|
|
1736
1326
|
servicePaths.indexerDaemonLockPath,
|
|
1737
1327
|
]);
|
|
1738
1328
|
const preRepairMiningRuntime = await loadMiningRuntimeStatus(paths.miningStatusPath).catch(() => null);
|
|
1739
|
-
const
|
|
1740
|
-
&& preRepairMiningRuntime.backgroundWorkerPid !== null
|
|
1741
|
-
&& await isProcessAlive(preRepairMiningRuntime.backgroundWorkerPid);
|
|
1742
|
-
const preRepairUnlockedState = await loadUnlockedWalletState({
|
|
1743
|
-
provider,
|
|
1744
|
-
nowUnixMs,
|
|
1329
|
+
const miningCleanup = await cleanupMiningForRepair({
|
|
1745
1330
|
paths,
|
|
1331
|
+
state: repairedState,
|
|
1332
|
+
snapshot: preRepairMiningRuntime,
|
|
1333
|
+
nowUnixMs,
|
|
1746
1334
|
});
|
|
1747
|
-
const miningPreRepairRunMode =
|
|
1748
|
-
? "background"
|
|
1749
|
-
: preRepairMiningRuntime?.runMode === "foreground"
|
|
1750
|
-
? "foreground"
|
|
1751
|
-
: "stopped";
|
|
1335
|
+
const miningPreRepairRunMode = miningCleanup.preRepairRunMode;
|
|
1752
1336
|
const miningWasResumable = miningPreRepairRunMode === "background"
|
|
1753
|
-
&& preRepairUnlockedState !== null
|
|
1754
1337
|
&& normalizeMiningStateRecord(repairedState.miningState).state !== "repair-required";
|
|
1755
|
-
const savedUnlockUntilUnixMs = miningWasResumable
|
|
1756
|
-
? preRepairUnlockedState?.session.unlockUntilUnixMs ?? null
|
|
1757
|
-
: null;
|
|
1758
1338
|
let initialBitcoindProbe = {
|
|
1759
1339
|
compatibility: "unreachable",
|
|
1760
1340
|
status: null,
|
|
@@ -1770,340 +1350,305 @@ export async function repairWallet(options) {
|
|
|
1770
1350
|
: "none";
|
|
1771
1351
|
let miningPostRepairRunMode = "stopped";
|
|
1772
1352
|
let miningResumeError = null;
|
|
1353
|
+
if (miningPreRepairRunMode !== "stopped" || preRepairMiningRuntime?.runMode !== "stopped") {
|
|
1354
|
+
repairedState = applyRepairStoppedMiningState(repairedState);
|
|
1355
|
+
repairStateNeedsPersist = true;
|
|
1356
|
+
}
|
|
1357
|
+
if (!(options.assumeYes ?? false)) {
|
|
1358
|
+
await ensureIndexerDatabaseHealthy({
|
|
1359
|
+
databasePath: options.databasePath,
|
|
1360
|
+
dataDir: options.dataDir,
|
|
1361
|
+
walletRootId: repairedState.walletRootId,
|
|
1362
|
+
resetIfNeeded: false,
|
|
1363
|
+
});
|
|
1364
|
+
}
|
|
1365
|
+
const bitcoindLock = await acquireFileLock(servicePaths.bitcoindLockPath, {
|
|
1366
|
+
purpose: "managed-bitcoind-repair",
|
|
1367
|
+
walletRootId: repairedState.walletRootId,
|
|
1368
|
+
dataDir: options.dataDir,
|
|
1369
|
+
});
|
|
1370
|
+
let resetIndexerDatabase = false;
|
|
1371
|
+
let bitcoindHandle = null;
|
|
1372
|
+
let bitcoindPostRepairHealth = "unavailable";
|
|
1773
1373
|
try {
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1374
|
+
initialBitcoindProbe = await probeManagedBitcoind({
|
|
1375
|
+
dataDir: options.dataDir,
|
|
1376
|
+
chain: "main",
|
|
1377
|
+
startHeight: 0,
|
|
1378
|
+
walletRootId: repairedState.walletRootId,
|
|
1777
1379
|
});
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1380
|
+
bitcoindCompatibilityIssue = mapBitcoindCompatibilityToRepairIssue(initialBitcoindProbe.compatibility);
|
|
1381
|
+
if (initialBitcoindProbe.compatibility === "service-version-mismatch"
|
|
1382
|
+
|| initialBitcoindProbe.compatibility === "wallet-root-mismatch"
|
|
1383
|
+
|| initialBitcoindProbe.compatibility === "runtime-mismatch") {
|
|
1384
|
+
const processId = initialBitcoindProbe.status?.processId ?? null;
|
|
1385
|
+
if (processId === null) {
|
|
1386
|
+
throw new Error("managed_bitcoind_process_id_unavailable");
|
|
1387
|
+
}
|
|
1782
1388
|
try {
|
|
1783
|
-
|
|
1784
|
-
paths,
|
|
1785
|
-
snapshot: preRepairMiningRuntime,
|
|
1786
|
-
nowUnixMs,
|
|
1787
|
-
});
|
|
1389
|
+
process.kill(processId, "SIGTERM");
|
|
1788
1390
|
}
|
|
1789
|
-
|
|
1790
|
-
|
|
1391
|
+
catch (error) {
|
|
1392
|
+
if (!(error instanceof Error) || !("code" in error) || error.code !== "ESRCH") {
|
|
1393
|
+
throw error;
|
|
1394
|
+
}
|
|
1791
1395
|
}
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
if (!(options.assumeYes ?? false)) {
|
|
1796
|
-
await ensureIndexerDatabaseHealthy({
|
|
1797
|
-
databasePath: options.databasePath,
|
|
1798
|
-
dataDir: options.dataDir,
|
|
1799
|
-
walletRootId: repairedState.walletRootId,
|
|
1800
|
-
resetIfNeeded: false,
|
|
1801
|
-
});
|
|
1396
|
+
await waitForProcessExit(processId, 15_000, "managed_bitcoind_stop_timeout");
|
|
1397
|
+
await clearManagedBitcoindArtifacts(servicePaths);
|
|
1398
|
+
bitcoindServiceAction = "stopped-incompatible-service";
|
|
1802
1399
|
}
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
let bitcoindHandle = null;
|
|
1810
|
-
let bitcoindPostRepairHealth = "unavailable";
|
|
1811
|
-
try {
|
|
1812
|
-
initialBitcoindProbe = await probeManagedBitcoind({
|
|
1813
|
-
dataDir: options.dataDir,
|
|
1814
|
-
chain: "main",
|
|
1815
|
-
startHeight: 0,
|
|
1816
|
-
walletRootId: repairedState.walletRootId,
|
|
1817
|
-
});
|
|
1818
|
-
bitcoindCompatibilityIssue = mapBitcoindCompatibilityToRepairIssue(initialBitcoindProbe.compatibility);
|
|
1819
|
-
if (initialBitcoindProbe.compatibility === "service-version-mismatch"
|
|
1820
|
-
|| initialBitcoindProbe.compatibility === "wallet-root-mismatch"
|
|
1821
|
-
|| initialBitcoindProbe.compatibility === "runtime-mismatch") {
|
|
1822
|
-
const processId = initialBitcoindProbe.status?.processId ?? null;
|
|
1823
|
-
if (processId === null) {
|
|
1824
|
-
throw new Error("managed_bitcoind_process_id_unavailable");
|
|
1825
|
-
}
|
|
1826
|
-
try {
|
|
1827
|
-
process.kill(processId, "SIGTERM");
|
|
1828
|
-
}
|
|
1829
|
-
catch (error) {
|
|
1830
|
-
if (!(error instanceof Error) || !("code" in error) || error.code !== "ESRCH") {
|
|
1831
|
-
throw error;
|
|
1832
|
-
}
|
|
1833
|
-
}
|
|
1834
|
-
await waitForProcessExit(processId, 15_000, "managed_bitcoind_stop_timeout");
|
|
1400
|
+
else if (initialBitcoindProbe.compatibility === "unreachable") {
|
|
1401
|
+
const hasStaleArtifacts = await pathExists(servicePaths.bitcoindStatusPath)
|
|
1402
|
+
|| await pathExists(servicePaths.bitcoindPidPath)
|
|
1403
|
+
|| await pathExists(servicePaths.bitcoindReadyPath)
|
|
1404
|
+
|| await pathExists(servicePaths.bitcoindWalletStatusPath);
|
|
1405
|
+
if (hasStaleArtifacts) {
|
|
1835
1406
|
await clearManagedBitcoindArtifacts(servicePaths);
|
|
1836
|
-
bitcoindServiceAction = "
|
|
1407
|
+
bitcoindServiceAction = "cleared-stale-artifacts";
|
|
1837
1408
|
}
|
|
1838
|
-
else if (initialBitcoindProbe.compatibility === "unreachable") {
|
|
1839
|
-
const hasStaleArtifacts = await pathExists(servicePaths.bitcoindStatusPath)
|
|
1840
|
-
|| await pathExists(servicePaths.bitcoindPidPath)
|
|
1841
|
-
|| await pathExists(servicePaths.bitcoindReadyPath)
|
|
1842
|
-
|| await pathExists(servicePaths.bitcoindWalletStatusPath);
|
|
1843
|
-
if (hasStaleArtifacts) {
|
|
1844
|
-
await clearManagedBitcoindArtifacts(servicePaths);
|
|
1845
|
-
bitcoindServiceAction = "cleared-stale-artifacts";
|
|
1846
|
-
}
|
|
1847
|
-
}
|
|
1848
|
-
else if (initialBitcoindProbe.compatibility === "protocol-error") {
|
|
1849
|
-
throw new Error(initialBitcoindProbe.error ?? "managed_bitcoind_protocol_error");
|
|
1850
|
-
}
|
|
1851
|
-
}
|
|
1852
|
-
finally {
|
|
1853
|
-
await bitcoindLock.release();
|
|
1854
1409
|
}
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
chain: "main",
|
|
1858
|
-
startHeight: 0,
|
|
1859
|
-
walletRootId: repairedState.walletRootId,
|
|
1860
|
-
});
|
|
1861
|
-
const bitcoindRpc = (options.rpcFactory ?? createRpcClient)(bitcoindHandle.rpc);
|
|
1862
|
-
const normalizedDescriptorState = await normalizeWalletDescriptorState(repairedState, bitcoindRpc);
|
|
1863
|
-
if (normalizedDescriptorState.changed) {
|
|
1864
|
-
repairedState = normalizedDescriptorState.state;
|
|
1865
|
-
repairStateNeedsPersist = true;
|
|
1410
|
+
else if (initialBitcoindProbe.compatibility === "protocol-error") {
|
|
1411
|
+
throw new Error(initialBitcoindProbe.error ?? "managed_bitcoind_protocol_error");
|
|
1866
1412
|
}
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1413
|
+
}
|
|
1414
|
+
finally {
|
|
1415
|
+
await bitcoindLock.release();
|
|
1416
|
+
}
|
|
1417
|
+
bitcoindHandle = await attachManagedBitcoind({
|
|
1418
|
+
dataDir: options.dataDir,
|
|
1419
|
+
chain: "main",
|
|
1420
|
+
startHeight: 0,
|
|
1421
|
+
walletRootId: repairedState.walletRootId,
|
|
1422
|
+
});
|
|
1423
|
+
const bitcoindRpc = (options.rpcFactory ?? createRpcClient)(bitcoindHandle.rpc);
|
|
1424
|
+
const normalizedDescriptorState = await normalizeWalletDescriptorState(repairedState, bitcoindRpc);
|
|
1425
|
+
if (normalizedDescriptorState.changed) {
|
|
1426
|
+
repairedState = normalizedDescriptorState.state;
|
|
1427
|
+
repairStateNeedsPersist = true;
|
|
1428
|
+
}
|
|
1429
|
+
const reconciledCoinControl = await persistWalletCoinControlStateIfNeeded({
|
|
1430
|
+
state: repairedState,
|
|
1431
|
+
access: {
|
|
1432
|
+
provider,
|
|
1433
|
+
secretReference,
|
|
1434
|
+
},
|
|
1435
|
+
paths,
|
|
1436
|
+
nowUnixMs,
|
|
1437
|
+
replacePrimary: recoveredFromBackup && !repairStateNeedsPersist,
|
|
1438
|
+
rpc: (options.rpcFactory ?? createRpcClient)(bitcoindHandle.rpc),
|
|
1439
|
+
});
|
|
1440
|
+
repairedState = reconciledCoinControl.state;
|
|
1441
|
+
if (reconciledCoinControl.changed) {
|
|
1442
|
+
repairStateNeedsPersist = false;
|
|
1443
|
+
}
|
|
1444
|
+
let replica = await verifyManagedCoreWalletReplica(repairedState, options.dataDir, {
|
|
1445
|
+
nodeHandle: bitcoindHandle,
|
|
1446
|
+
attachService: options.attachService,
|
|
1447
|
+
rpcFactory: options.rpcFactory,
|
|
1448
|
+
});
|
|
1449
|
+
let recreatedManagedCoreWallet = false;
|
|
1450
|
+
if (replica.proofStatus !== "ready") {
|
|
1451
|
+
repairedState = await recreateManagedCoreWalletReplica(repairedState, provider, paths, options.dataDir, nowUnixMs, {
|
|
1452
|
+
attachService: options.attachService,
|
|
1453
|
+
rpcFactory: options.rpcFactory,
|
|
1877
1454
|
});
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
let replica = await verifyManagedCoreWalletReplica(repairedState, options.dataDir, {
|
|
1455
|
+
recreatedManagedCoreWallet = true;
|
|
1456
|
+
managedCoreReplicaAction = "recreated";
|
|
1457
|
+
repairStateNeedsPersist = false;
|
|
1458
|
+
replica = await verifyManagedCoreWalletReplica(repairedState, options.dataDir, {
|
|
1883
1459
|
nodeHandle: bitcoindHandle,
|
|
1884
1460
|
attachService: options.attachService,
|
|
1885
1461
|
rpcFactory: options.rpcFactory,
|
|
1886
1462
|
});
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
});
|
|
1909
|
-
if (bitcoindServiceAction === "none" && initialBitcoindProbe.compatibility === "unreachable") {
|
|
1910
|
-
bitcoindServiceAction = "restarted-compatible-service";
|
|
1911
|
-
}
|
|
1912
|
-
let initialIndexerDaemonInstanceId = null;
|
|
1913
|
-
let preAttachIndexerDaemonInstanceId = null;
|
|
1914
|
-
const indexerLock = await acquireFileLock(servicePaths.indexerDaemonLockPath, {
|
|
1915
|
-
purpose: "indexer-daemon-repair",
|
|
1916
|
-
walletRootId: repairedState.walletRootId,
|
|
1463
|
+
}
|
|
1464
|
+
const finalBitcoindStatus = await bitcoindHandle.refreshServiceStatus?.() ?? null;
|
|
1465
|
+
const chainInfo = await bitcoindRpc.getBlockchainInfo();
|
|
1466
|
+
bitcoindPostRepairHealth = mapBitcoindRepairHealth({
|
|
1467
|
+
serviceState: finalBitcoindStatus?.state ?? null,
|
|
1468
|
+
catchingUp: chainInfo.blocks < chainInfo.headers,
|
|
1469
|
+
replica,
|
|
1470
|
+
});
|
|
1471
|
+
if (bitcoindServiceAction === "none" && initialBitcoindProbe.compatibility === "unreachable") {
|
|
1472
|
+
bitcoindServiceAction = "restarted-compatible-service";
|
|
1473
|
+
}
|
|
1474
|
+
let initialIndexerDaemonInstanceId = null;
|
|
1475
|
+
let preAttachIndexerDaemonInstanceId = null;
|
|
1476
|
+
const indexerLock = await acquireFileLock(servicePaths.indexerDaemonLockPath, {
|
|
1477
|
+
purpose: "indexer-daemon-repair",
|
|
1478
|
+
walletRootId: repairedState.walletRootId,
|
|
1479
|
+
dataDir: options.dataDir,
|
|
1480
|
+
databasePath: options.databasePath,
|
|
1481
|
+
});
|
|
1482
|
+
try {
|
|
1483
|
+
const initialProbe = await probeManagedIndexerDaemon({
|
|
1917
1484
|
dataDir: options.dataDir,
|
|
1918
|
-
|
|
1485
|
+
walletRootId: repairedState.walletRootId,
|
|
1919
1486
|
});
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1487
|
+
indexerCompatibilityIssue = mapIndexerCompatibilityToRepairIssue(initialProbe.compatibility);
|
|
1488
|
+
initialIndexerDaemonInstanceId = initialProbe.status?.daemonInstanceId ?? null;
|
|
1489
|
+
if (initialProbe.compatibility === "compatible") {
|
|
1490
|
+
await initialProbe.client?.close().catch(() => undefined);
|
|
1491
|
+
}
|
|
1492
|
+
else if (initialProbe.compatibility === "service-version-mismatch"
|
|
1493
|
+
|| initialProbe.compatibility === "wallet-root-mismatch"
|
|
1494
|
+
|| initialProbe.compatibility === "schema-mismatch") {
|
|
1495
|
+
const processId = initialProbe.status?.processId ?? null;
|
|
1496
|
+
if (processId === null) {
|
|
1497
|
+
throw new Error("indexer_daemon_process_id_unavailable");
|
|
1929
1498
|
}
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|| initialProbe.compatibility === "schema-mismatch") {
|
|
1933
|
-
const processId = initialProbe.status?.processId ?? null;
|
|
1934
|
-
if (processId === null) {
|
|
1935
|
-
throw new Error("indexer_daemon_process_id_unavailable");
|
|
1936
|
-
}
|
|
1937
|
-
try {
|
|
1938
|
-
process.kill(processId, "SIGTERM");
|
|
1939
|
-
}
|
|
1940
|
-
catch (error) {
|
|
1941
|
-
if (!(error instanceof Error) || !("code" in error) || error.code !== "ESRCH") {
|
|
1942
|
-
throw error;
|
|
1943
|
-
}
|
|
1944
|
-
}
|
|
1945
|
-
await waitForProcessExit(processId);
|
|
1946
|
-
await clearIndexerDaemonArtifacts(servicePaths);
|
|
1947
|
-
indexerDaemonAction = "stopped-incompatible-daemon";
|
|
1499
|
+
try {
|
|
1500
|
+
process.kill(processId, "SIGTERM");
|
|
1948
1501
|
}
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
if (hasStaleArtifacts) {
|
|
1953
|
-
await clearIndexerDaemonArtifacts(servicePaths);
|
|
1954
|
-
indexerDaemonAction = "cleared-stale-artifacts";
|
|
1502
|
+
catch (error) {
|
|
1503
|
+
if (!(error instanceof Error) || !("code" in error) || error.code !== "ESRCH") {
|
|
1504
|
+
throw error;
|
|
1955
1505
|
}
|
|
1956
1506
|
}
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
resetIndexerDatabase = await ensureIndexerDatabaseHealthy({
|
|
1961
|
-
databasePath: options.databasePath,
|
|
1962
|
-
dataDir: options.dataDir,
|
|
1963
|
-
walletRootId: repairedState.walletRootId,
|
|
1964
|
-
resetIfNeeded: options.assumeYes ?? false,
|
|
1965
|
-
});
|
|
1507
|
+
await waitForProcessExit(processId);
|
|
1508
|
+
await clearIndexerDaemonArtifacts(servicePaths);
|
|
1509
|
+
indexerDaemonAction = "stopped-incompatible-daemon";
|
|
1966
1510
|
}
|
|
1967
|
-
|
|
1968
|
-
await
|
|
1511
|
+
else if (initialProbe.compatibility === "unreachable") {
|
|
1512
|
+
const hasStaleArtifacts = await pathExists(servicePaths.indexerDaemonSocketPath)
|
|
1513
|
+
|| await pathExists(servicePaths.indexerDaemonStatusPath);
|
|
1514
|
+
if (hasStaleArtifacts) {
|
|
1515
|
+
await clearIndexerDaemonArtifacts(servicePaths);
|
|
1516
|
+
indexerDaemonAction = "cleared-stale-artifacts";
|
|
1517
|
+
}
|
|
1969
1518
|
}
|
|
1970
|
-
|
|
1971
|
-
|
|
1972
|
-
state: repairedState,
|
|
1973
|
-
provider,
|
|
1974
|
-
paths,
|
|
1975
|
-
nowUnixMs,
|
|
1976
|
-
replacePrimary: true,
|
|
1977
|
-
});
|
|
1978
|
-
repairStateNeedsPersist = false;
|
|
1519
|
+
else {
|
|
1520
|
+
throw new Error(initialProbe.error ?? "indexer_daemon_protocol_error");
|
|
1979
1521
|
}
|
|
1980
|
-
|
|
1981
|
-
|
|
1982
|
-
state: repairedState,
|
|
1983
|
-
provider,
|
|
1984
|
-
paths,
|
|
1985
|
-
nowUnixMs,
|
|
1986
|
-
});
|
|
1987
|
-
repairStateNeedsPersist = false;
|
|
1988
|
-
}
|
|
1989
|
-
const preAttachProbe = await probeManagedIndexerDaemon({
|
|
1522
|
+
resetIndexerDatabase = await ensureIndexerDatabaseHealthy({
|
|
1523
|
+
databasePath: options.databasePath,
|
|
1990
1524
|
dataDir: options.dataDir,
|
|
1991
1525
|
walletRootId: repairedState.walletRootId,
|
|
1526
|
+
resetIfNeeded: options.assumeYes ?? false,
|
|
1992
1527
|
});
|
|
1993
|
-
|
|
1994
|
-
|
|
1995
|
-
|
|
1996
|
-
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
1528
|
+
}
|
|
1529
|
+
finally {
|
|
1530
|
+
await indexerLock.release();
|
|
1531
|
+
}
|
|
1532
|
+
if (recoveredFromBackup) {
|
|
1533
|
+
repairedState = await persistRepairState({
|
|
1534
|
+
state: repairedState,
|
|
1535
|
+
provider,
|
|
1536
|
+
paths,
|
|
1537
|
+
nowUnixMs,
|
|
1538
|
+
replacePrimary: true,
|
|
1539
|
+
});
|
|
1540
|
+
repairStateNeedsPersist = false;
|
|
1541
|
+
}
|
|
1542
|
+
else if (repairStateNeedsPersist) {
|
|
1543
|
+
repairedState = await persistRepairState({
|
|
1544
|
+
state: repairedState,
|
|
1545
|
+
provider,
|
|
1546
|
+
paths,
|
|
1547
|
+
nowUnixMs,
|
|
1548
|
+
});
|
|
1549
|
+
repairStateNeedsPersist = false;
|
|
1550
|
+
}
|
|
1551
|
+
const preAttachProbe = await probeManagedIndexerDaemon({
|
|
1552
|
+
dataDir: options.dataDir,
|
|
1553
|
+
walletRootId: repairedState.walletRootId,
|
|
1554
|
+
});
|
|
1555
|
+
if (preAttachProbe.compatibility === "compatible") {
|
|
1556
|
+
preAttachIndexerDaemonInstanceId = preAttachProbe.status?.daemonInstanceId ?? null;
|
|
1557
|
+
await preAttachProbe.client?.close().catch(() => undefined);
|
|
1558
|
+
}
|
|
1559
|
+
else if (preAttachProbe.compatibility !== "unreachable") {
|
|
1560
|
+
throw new Error(preAttachProbe.error ?? "indexer_daemon_protocol_error");
|
|
1561
|
+
}
|
|
1562
|
+
const daemon = await attachManagedIndexerDaemon({
|
|
1563
|
+
dataDir: options.dataDir,
|
|
1564
|
+
databasePath: options.databasePath,
|
|
1565
|
+
walletRootId: repairedState.walletRootId,
|
|
1566
|
+
});
|
|
1567
|
+
try {
|
|
1568
|
+
const { health: indexerPostRepairHealth, daemonInstanceId: postRepairDaemonInstanceId, } = await verifyIndexerPostRepairHealth({
|
|
1569
|
+
daemon,
|
|
1570
|
+
probeIndexerDaemon: probeManagedIndexerDaemon,
|
|
2001
1571
|
dataDir: options.dataDir,
|
|
2002
|
-
databasePath: options.databasePath,
|
|
2003
1572
|
walletRootId: repairedState.walletRootId,
|
|
1573
|
+
nowUnixMs,
|
|
2004
1574
|
});
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
1575
|
+
const restartedIndexerDaemon = indexerDaemonAction !== "none" || preAttachProbe.compatibility === "unreachable";
|
|
1576
|
+
if (restartedIndexerDaemon
|
|
1577
|
+
&& initialIndexerDaemonInstanceId !== null
|
|
1578
|
+
&& postRepairDaemonInstanceId === initialIndexerDaemonInstanceId) {
|
|
1579
|
+
throw new Error("indexer_daemon_repair_identity_not_rotated");
|
|
1580
|
+
}
|
|
1581
|
+
if (!restartedIndexerDaemon
|
|
1582
|
+
&& preAttachProbe.compatibility === "compatible"
|
|
1583
|
+
&& preAttachIndexerDaemonInstanceId !== null
|
|
1584
|
+
&& postRepairDaemonInstanceId !== preAttachIndexerDaemonInstanceId) {
|
|
1585
|
+
throw new Error("indexer_daemon_repair_identity_changed");
|
|
1586
|
+
}
|
|
1587
|
+
if (indexerDaemonAction === "none" && preAttachProbe.compatibility === "unreachable") {
|
|
1588
|
+
indexerDaemonAction = "restarted-compatible-daemon";
|
|
1589
|
+
}
|
|
1590
|
+
if (miningWasResumable) {
|
|
1591
|
+
const postRepairResumeReady = await canResumeBackgroundMiningAfterRepair({
|
|
1592
|
+
provider,
|
|
1593
|
+
paths,
|
|
1594
|
+
repairedState,
|
|
1595
|
+
bitcoindPostRepairHealth,
|
|
1596
|
+
indexerPostRepairHealth,
|
|
2012
1597
|
});
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
&& initialIndexerDaemonInstanceId !== null
|
|
2016
|
-
&& postRepairDaemonInstanceId === initialIndexerDaemonInstanceId) {
|
|
2017
|
-
throw new Error("indexer_daemon_repair_identity_not_rotated");
|
|
2018
|
-
}
|
|
2019
|
-
if (!restartedIndexerDaemon
|
|
2020
|
-
&& preAttachProbe.compatibility === "compatible"
|
|
2021
|
-
&& preAttachIndexerDaemonInstanceId !== null
|
|
2022
|
-
&& postRepairDaemonInstanceId !== preAttachIndexerDaemonInstanceId) {
|
|
2023
|
-
throw new Error("indexer_daemon_repair_identity_changed");
|
|
2024
|
-
}
|
|
2025
|
-
if (indexerDaemonAction === "none" && preAttachProbe.compatibility === "unreachable") {
|
|
2026
|
-
indexerDaemonAction = "restarted-compatible-daemon";
|
|
1598
|
+
if (!postRepairResumeReady) {
|
|
1599
|
+
miningResumeAction = "skipped-post-repair-blocked";
|
|
2027
1600
|
}
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
bitcoindPostRepairHealth,
|
|
2036
|
-
indexerPostRepairHealth,
|
|
2037
|
-
unlockUntilUnixMs: savedUnlockUntilUnixMs,
|
|
2038
|
-
});
|
|
2039
|
-
if (!postRepairResumeReady) {
|
|
2040
|
-
miningResumeAction = "skipped-post-repair-blocked";
|
|
2041
|
-
}
|
|
2042
|
-
else if (savedUnlockUntilUnixMs === null || savedUnlockUntilUnixMs <= nowUnixMs) {
|
|
2043
|
-
miningResumeAction = "skipped-post-repair-blocked";
|
|
2044
|
-
}
|
|
2045
|
-
else {
|
|
2046
|
-
await saveUnlockSession(paths.walletUnlockSessionPath, createUnlockSession(repairedState, savedUnlockUntilUnixMs, secretReference.keyId, nowUnixMs), {
|
|
1601
|
+
else {
|
|
1602
|
+
try {
|
|
1603
|
+
const startBackgroundMining = options.startBackgroundMining
|
|
1604
|
+
?? (await import("./mining/runner.js")).startBackgroundMining;
|
|
1605
|
+
const resumed = await startBackgroundMining({
|
|
1606
|
+
dataDir: options.dataDir,
|
|
1607
|
+
databasePath: options.databasePath,
|
|
2047
1608
|
provider,
|
|
2048
|
-
|
|
1609
|
+
paths,
|
|
1610
|
+
prompter: createSilentNonInteractivePrompter(),
|
|
2049
1611
|
});
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
?? (await import("./mining/runner.js")).startBackgroundMining;
|
|
2054
|
-
const resumed = await startBackgroundMining({
|
|
2055
|
-
dataDir: options.dataDir,
|
|
2056
|
-
databasePath: options.databasePath,
|
|
2057
|
-
provider,
|
|
2058
|
-
paths,
|
|
2059
|
-
prompter: createSilentNonInteractivePrompter(),
|
|
2060
|
-
});
|
|
2061
|
-
if (resumed.snapshot?.runMode === "background") {
|
|
2062
|
-
miningResumeAction = "resumed-background";
|
|
2063
|
-
miningPostRepairRunMode = "background";
|
|
2064
|
-
}
|
|
2065
|
-
else {
|
|
2066
|
-
miningResumeAction = "resume-failed";
|
|
2067
|
-
miningResumeError = "Background mining did not report a background runtime after repair.";
|
|
2068
|
-
}
|
|
1612
|
+
if (resumed.snapshot?.runMode === "background") {
|
|
1613
|
+
miningResumeAction = "resumed-background";
|
|
1614
|
+
miningPostRepairRunMode = "background";
|
|
2069
1615
|
}
|
|
2070
|
-
|
|
1616
|
+
else {
|
|
2071
1617
|
miningResumeAction = "resume-failed";
|
|
2072
|
-
miningResumeError =
|
|
1618
|
+
miningResumeError = "Background mining did not report a background runtime after repair.";
|
|
2073
1619
|
}
|
|
2074
1620
|
}
|
|
1621
|
+
catch (error) {
|
|
1622
|
+
miningResumeAction = "resume-failed";
|
|
1623
|
+
miningResumeError = error instanceof Error ? error.message : String(error);
|
|
1624
|
+
}
|
|
2075
1625
|
}
|
|
2076
|
-
if (!keepUnlockSession) {
|
|
2077
|
-
await clearUnlockSession(paths.walletUnlockSessionPath);
|
|
2078
|
-
}
|
|
2079
|
-
return {
|
|
2080
|
-
walletRootId: repairedState.walletRootId,
|
|
2081
|
-
recoveredFromBackup,
|
|
2082
|
-
recreatedManagedCoreWallet,
|
|
2083
|
-
resetIndexerDatabase,
|
|
2084
|
-
bitcoindServiceAction,
|
|
2085
|
-
bitcoindCompatibilityIssue,
|
|
2086
|
-
managedCoreReplicaAction,
|
|
2087
|
-
bitcoindPostRepairHealth,
|
|
2088
|
-
indexerDaemonAction,
|
|
2089
|
-
indexerCompatibilityIssue,
|
|
2090
|
-
indexerPostRepairHealth,
|
|
2091
|
-
miningPreRepairRunMode,
|
|
2092
|
-
miningResumeAction,
|
|
2093
|
-
miningPostRepairRunMode,
|
|
2094
|
-
miningResumeError,
|
|
2095
|
-
note: resetIndexerDatabase
|
|
2096
|
-
? "Indexer artifacts were reset and may still be catching up."
|
|
2097
|
-
: null,
|
|
2098
|
-
};
|
|
2099
|
-
}
|
|
2100
|
-
finally {
|
|
2101
|
-
await daemon.close().catch(() => undefined);
|
|
2102
|
-
await bitcoindHandle?.stop?.().catch(() => undefined);
|
|
2103
1626
|
}
|
|
1627
|
+
await clearLegacyWalletLockArtifacts(paths.walletRuntimeRoot);
|
|
1628
|
+
return {
|
|
1629
|
+
walletRootId: repairedState.walletRootId,
|
|
1630
|
+
recoveredFromBackup,
|
|
1631
|
+
recreatedManagedCoreWallet,
|
|
1632
|
+
resetIndexerDatabase,
|
|
1633
|
+
bitcoindServiceAction,
|
|
1634
|
+
bitcoindCompatibilityIssue,
|
|
1635
|
+
managedCoreReplicaAction,
|
|
1636
|
+
bitcoindPostRepairHealth,
|
|
1637
|
+
indexerDaemonAction,
|
|
1638
|
+
indexerCompatibilityIssue,
|
|
1639
|
+
indexerPostRepairHealth,
|
|
1640
|
+
miningPreRepairRunMode,
|
|
1641
|
+
miningResumeAction,
|
|
1642
|
+
miningPostRepairRunMode,
|
|
1643
|
+
miningResumeError,
|
|
1644
|
+
note: resetIndexerDatabase
|
|
1645
|
+
? "Indexer artifacts were reset and may still be catching up."
|
|
1646
|
+
: null,
|
|
1647
|
+
};
|
|
2104
1648
|
}
|
|
2105
1649
|
finally {
|
|
2106
|
-
await
|
|
1650
|
+
await daemon.close().catch(() => undefined);
|
|
1651
|
+
await bitcoindHandle?.stop?.().catch(() => undefined);
|
|
2107
1652
|
}
|
|
2108
1653
|
}
|
|
2109
1654
|
finally {
|