@cogcoin/client 0.5.15 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. package/README.md +80 -25
  2. package/dist/app-paths.d.ts +5 -6
  3. package/dist/app-paths.js +8 -16
  4. package/dist/art/balance.txt +10 -0
  5. package/dist/art/welcome.txt +16 -0
  6. package/dist/bitcoind/bootstrap/controller.d.ts +1 -0
  7. package/dist/bitcoind/bootstrap/controller.js +53 -1
  8. package/dist/bitcoind/client/follow-block-times.d.ts +1 -0
  9. package/dist/bitcoind/client/follow-block-times.js +1 -1
  10. package/dist/bitcoind/client/internal-types.d.ts +7 -3
  11. package/dist/bitcoind/client/managed-client.d.ts +4 -2
  12. package/dist/bitcoind/client/managed-client.js +14 -0
  13. package/dist/bitcoind/client/sync-engine.js +72 -11
  14. package/dist/bitcoind/hash-order.d.ts +4 -0
  15. package/dist/bitcoind/hash-order.js +13 -0
  16. package/dist/bitcoind/indexer-daemon-main.js +11 -3
  17. package/dist/bitcoind/normalize.js +3 -2
  18. package/dist/bitcoind/processing-start-height.d.ts +5 -0
  19. package/dist/bitcoind/processing-start-height.js +7 -0
  20. package/dist/bitcoind/progress/constants.d.ts +4 -0
  21. package/dist/bitcoind/progress/constants.js +4 -0
  22. package/dist/bitcoind/progress/controller.d.ts +2 -1
  23. package/dist/bitcoind/progress/controller.js +3 -3
  24. package/dist/bitcoind/progress/follow-scene.d.ts +6 -2
  25. package/dist/bitcoind/progress/follow-scene.js +29 -6
  26. package/dist/bitcoind/progress/formatting.d.ts +1 -0
  27. package/dist/bitcoind/progress/formatting.js +6 -0
  28. package/dist/bitcoind/progress/train-scene.js +37 -18
  29. package/dist/bitcoind/progress/tty-renderer.d.ts +6 -1
  30. package/dist/bitcoind/progress/tty-renderer.js +8 -4
  31. package/dist/bitcoind/rpc.d.ts +2 -1
  32. package/dist/bitcoind/rpc.js +3 -0
  33. package/dist/bitcoind/types.d.ts +6 -0
  34. package/dist/bytes.d.ts +1 -0
  35. package/dist/bytes.js +3 -0
  36. package/dist/cli/art.d.ts +2 -0
  37. package/dist/cli/art.js +37 -0
  38. package/dist/cli/commands/client-admin.d.ts +2 -0
  39. package/dist/cli/commands/client-admin.js +91 -0
  40. package/dist/cli/commands/follow.js +0 -2
  41. package/dist/cli/commands/mining-admin.js +6 -47
  42. package/dist/cli/commands/mining-read.js +11 -50
  43. package/dist/cli/commands/mining-runtime.js +38 -3
  44. package/dist/cli/commands/service-runtime.js +0 -2
  45. package/dist/cli/commands/status.js +8 -2
  46. package/dist/cli/commands/sync.js +51 -4
  47. package/dist/cli/commands/wallet-admin.js +142 -136
  48. package/dist/cli/commands/wallet-mutation.js +91 -79
  49. package/dist/cli/commands/wallet-read.js +15 -18
  50. package/dist/cli/context.js +4 -14
  51. package/dist/cli/mining-format.d.ts +0 -1
  52. package/dist/cli/mining-format.js +5 -37
  53. package/dist/cli/mining-json.d.ts +0 -18
  54. package/dist/cli/mining-json.js +0 -35
  55. package/dist/cli/mutation-command-groups.d.ts +1 -2
  56. package/dist/cli/mutation-command-groups.js +0 -5
  57. package/dist/cli/mutation-json.d.ts +24 -145
  58. package/dist/cli/mutation-json.js +30 -136
  59. package/dist/cli/mutation-resolved-json.d.ts +0 -7
  60. package/dist/cli/mutation-resolved-json.js +4 -10
  61. package/dist/cli/mutation-success.d.ts +2 -0
  62. package/dist/cli/mutation-success.js +11 -1
  63. package/dist/cli/mutation-text-format.js +1 -3
  64. package/dist/cli/output.d.ts +1 -1
  65. package/dist/cli/output.js +254 -231
  66. package/dist/cli/parse.d.ts +1 -1
  67. package/dist/cli/parse.js +93 -122
  68. package/dist/cli/preview-json.d.ts +17 -120
  69. package/dist/cli/preview-json.js +14 -97
  70. package/dist/cli/prompt.js +8 -13
  71. package/dist/cli/read-json.d.ts +15 -37
  72. package/dist/cli/read-json.js +44 -140
  73. package/dist/cli/runner.js +10 -13
  74. package/dist/cli/types.d.ts +8 -17
  75. package/dist/cli/types.js +0 -2
  76. package/dist/cli/wallet-format.d.ts +1 -0
  77. package/dist/cli/wallet-format.js +205 -144
  78. package/dist/cli/workflow-hints.d.ts +3 -3
  79. package/dist/cli/workflow-hints.js +11 -8
  80. package/dist/client/default-client.d.ts +3 -1
  81. package/dist/client/default-client.js +45 -2
  82. package/dist/client/factory.js +1 -1
  83. package/dist/client/initialization.js +23 -0
  84. package/dist/client/persistence.js +5 -5
  85. package/dist/client/store-adapter.js +1 -0
  86. package/dist/sqlite/checkpoints.d.ts +1 -0
  87. package/dist/sqlite/checkpoints.js +7 -0
  88. package/dist/sqlite/store.js +14 -1
  89. package/dist/types.d.ts +1 -0
  90. package/dist/wallet/coin-control.d.ts +41 -12
  91. package/dist/wallet/coin-control.js +100 -428
  92. package/dist/wallet/descriptor-normalization.d.ts +1 -3
  93. package/dist/wallet/descriptor-normalization.js +0 -16
  94. package/dist/wallet/lifecycle.d.ts +7 -99
  95. package/dist/wallet/lifecycle.js +513 -968
  96. package/dist/wallet/managed-core-wallet.d.ts +13 -0
  97. package/dist/wallet/managed-core-wallet.js +20 -0
  98. package/dist/wallet/mining/constants.d.ts +5 -12
  99. package/dist/wallet/mining/constants.js +5 -12
  100. package/dist/wallet/mining/control.d.ts +1 -13
  101. package/dist/wallet/mining/control.js +45 -349
  102. package/dist/wallet/mining/index.d.ts +3 -4
  103. package/dist/wallet/mining/index.js +1 -2
  104. package/dist/wallet/mining/runner.d.ts +116 -13
  105. package/dist/wallet/mining/runner.js +885 -501
  106. package/dist/wallet/mining/runtime-artifacts.js +23 -3
  107. package/dist/wallet/mining/sentence-protocol.d.ts +44 -0
  108. package/dist/wallet/mining/sentence-protocol.js +123 -0
  109. package/dist/wallet/mining/sentences.d.ts +4 -8
  110. package/dist/wallet/mining/sentences.js +3 -52
  111. package/dist/wallet/mining/state.d.ts +11 -6
  112. package/dist/wallet/mining/state.js +7 -6
  113. package/dist/wallet/mining/types.d.ts +2 -30
  114. package/dist/wallet/mining/visualizer.d.ts +31 -3
  115. package/dist/wallet/mining/visualizer.js +135 -13
  116. package/dist/wallet/read/context.d.ts +0 -2
  117. package/dist/wallet/read/context.js +119 -140
  118. package/dist/wallet/read/filter.js +2 -11
  119. package/dist/wallet/read/index.d.ts +1 -1
  120. package/dist/wallet/read/project.js +24 -77
  121. package/dist/wallet/read/types.d.ts +10 -25
  122. package/dist/wallet/reset.d.ts +0 -1
  123. package/dist/wallet/reset.js +60 -138
  124. package/dist/wallet/root-resolution.d.ts +1 -5
  125. package/dist/wallet/root-resolution.js +0 -18
  126. package/dist/wallet/runtime.d.ts +0 -6
  127. package/dist/wallet/runtime.js +0 -8
  128. package/dist/wallet/state/client-password-agent.js +208 -0
  129. package/dist/wallet/state/client-password.d.ts +65 -0
  130. package/dist/wallet/state/client-password.js +952 -0
  131. package/dist/wallet/state/crypto.d.ts +1 -20
  132. package/dist/wallet/state/crypto.js +0 -63
  133. package/dist/wallet/state/provider.d.ts +23 -11
  134. package/dist/wallet/state/provider.js +248 -290
  135. package/dist/wallet/state/storage.d.ts +2 -2
  136. package/dist/wallet/state/storage.js +48 -16
  137. package/dist/wallet/tx/anchor.d.ts +3 -28
  138. package/dist/wallet/tx/anchor.js +349 -1250
  139. package/dist/wallet/tx/bitcoin-transfer.d.ts +35 -0
  140. package/dist/wallet/tx/bitcoin-transfer.js +200 -0
  141. package/dist/wallet/tx/cog.d.ts +5 -1
  142. package/dist/wallet/tx/cog.js +149 -185
  143. package/dist/wallet/tx/common.d.ts +61 -8
  144. package/dist/wallet/tx/common.js +266 -146
  145. package/dist/wallet/tx/domain-admin.d.ts +3 -1
  146. package/dist/wallet/tx/domain-admin.js +61 -99
  147. package/dist/wallet/tx/domain-market.d.ts +5 -1
  148. package/dist/wallet/tx/domain-market.js +221 -228
  149. package/dist/wallet/tx/field.d.ts +4 -10
  150. package/dist/wallet/tx/field.js +83 -924
  151. package/dist/wallet/tx/identity-selector.d.ts +9 -3
  152. package/dist/wallet/tx/identity-selector.js +17 -35
  153. package/dist/wallet/tx/index.d.ts +3 -1
  154. package/dist/wallet/tx/index.js +2 -1
  155. package/dist/wallet/tx/register.d.ts +3 -1
  156. package/dist/wallet/tx/register.js +62 -220
  157. package/dist/wallet/tx/reputation.d.ts +3 -1
  158. package/dist/wallet/tx/reputation.js +58 -95
  159. package/dist/wallet/types.d.ts +8 -122
  160. package/package.json +5 -5
  161. package/dist/wallet/archive.d.ts +0 -4
  162. package/dist/wallet/archive.js +0 -41
  163. package/dist/wallet/mining/hook-protocol.d.ts +0 -47
  164. package/dist/wallet/mining/hook-protocol.js +0 -161
  165. package/dist/wallet/mining/hook-runner.js +0 -52
  166. package/dist/wallet/mining/hooks.d.ts +0 -38
  167. package/dist/wallet/mining/hooks.js +0 -520
  168. package/dist/wallet/state/explicit-lock.d.ts +0 -4
  169. package/dist/wallet/state/explicit-lock.js +0 -19
  170. package/dist/wallet/state/session.d.ts +0 -12
  171. package/dist/wallet/state/session.js +0 -23
  172. /package/dist/wallet/{mining/hook-runner.d.ts → state/client-password-agent.d.ts} +0 -0
@@ -21,55 +21,20 @@ function tryDecodeUtf8(value) {
21
21
  }
22
22
  export function createWalletReadModel(walletState, snapshot) {
23
23
  const snapshotState = snapshot?.state ?? null;
24
- const localIdentityByScript = new Map();
25
- const identities = walletState.identities
26
- .slice()
27
- .sort((left, right) => left.index - right.index)
28
- .map((identity) => {
29
- const scriptBytes = scriptHexToBytes(identity.scriptPubKeyHex);
30
- const ownedDomains = snapshotState === null
31
- ? []
32
- : listDomainsByOwner(snapshotState, scriptBytes).sort((left, right) => left.name.localeCompare(right.name));
33
- const anchoredOwnedDomains = ownedDomains.filter((domain) => domain.anchored);
34
- const canonicalDomainId = snapshotState === null ? null : resolveCanonical(snapshotState, scriptBytes);
35
- const canonicalDomainName = canonicalDomainId === null || snapshotState === null
36
- ? null
37
- : (snapshotState ? lookupDomainById(snapshotState, canonicalDomainId)?.name ?? null : null);
38
- const readOnly = identity.status === "read-only" || anchoredOwnedDomains.length > 1;
39
- const observedCogBalance = snapshotState === null ? null : getBalance(snapshotState, scriptBytes);
40
- const view = {
41
- index: identity.index,
42
- scriptPubKeyHex: identity.scriptPubKeyHex,
43
- address: identity.address,
44
- selectors: [
45
- `id:${identity.index}`,
46
- ...ownedDomains.map((domain) => `domain:${domain.name}`),
47
- ...(identity.address === null ? [] : [identity.address]),
48
- `spk:${identity.scriptPubKeyHex}`,
49
- ],
50
- assignedDomainNames: identity.assignedDomainNames.slice().sort((left, right) => left.localeCompare(right)),
51
- localStatus: identity.status,
52
- effectiveStatus: readOnly ? "read-only" : identity.status,
53
- canonicalDomainId,
54
- canonicalDomainName,
55
- ownedDomainNames: ownedDomains.map((domain) => domain.name),
56
- anchoredOwnedDomainNames: anchoredOwnedDomains.map((domain) => domain.name),
57
- observedCogBalance,
58
- readOnly,
59
- };
60
- localIdentityByScript.set(identity.scriptPubKeyHex, view);
61
- return view;
62
- });
24
+ const localScriptHexes = new Set([
25
+ walletState.funding.scriptPubKeyHex,
26
+ ...(walletState.localScriptPubKeyHexes ?? []),
27
+ ]);
28
+ const fundingScriptBytes = scriptHexToBytes(walletState.funding.scriptPubKeyHex);
29
+ const ownedDomains = snapshotState === null
30
+ ? []
31
+ : listDomainsByOwner(snapshotState, fundingScriptBytes).sort((left, right) => left.name.localeCompare(right.name));
63
32
  const domainNames = new Set();
64
33
  for (const domain of walletState.domains) {
65
34
  domainNames.add(domain.name);
66
35
  }
67
- if (snapshotState !== null) {
68
- for (const identity of identities) {
69
- for (const name of identity.ownedDomainNames) {
70
- domainNames.add(name);
71
- }
72
- }
36
+ for (const name of ownedDomains.map((domain) => domain.name)) {
37
+ domainNames.add(name);
73
38
  }
74
39
  const domains = [...domainNames]
75
40
  .sort((left, right) => left.localeCompare(right))
@@ -77,20 +42,14 @@ export function createWalletReadModel(walletState, snapshot) {
77
42
  const localRecord = walletState.domains.find((domain) => domain.name === name) ?? null;
78
43
  const chainRecord = snapshotState === null ? null : lookupDomain(snapshotState, name);
79
44
  const ownerScriptPubKeyHex = chainRecord ? bytesToHex(chainRecord.ownerScriptPubKey) : localRecord?.currentOwnerScriptPubKeyHex ?? null;
80
- const ownerIdentity = ownerScriptPubKeyHex === null ? null : localIdentityByScript.get(ownerScriptPubKeyHex) ?? null;
45
+ const ownerIsLocal = ownerScriptPubKeyHex !== null && localScriptHexes.has(ownerScriptPubKeyHex);
81
46
  const fields = chainRecord && snapshotState ? listFields(snapshotState, chainRecord.domainId) : null;
82
47
  const listing = chainRecord && snapshotState ? getListing(snapshotState, chainRecord.domainId) : null;
83
48
  const activeLocks = chainRecord && snapshotState ? listActiveLocksByDomain(snapshotState, chainRecord.domainId) : null;
84
49
  const reputation = chainRecord && snapshotState ? getReputation(snapshotState, chainRecord.domainId) : null;
85
- const readOnly = ownerIdentity?.readOnly ?? (localRecord?.currentOwnerLocalIndex !== null && localRecord?.currentOwnerLocalIndex !== undefined
86
- ? identities.find((identity) => identity.index === localRecord.currentOwnerLocalIndex)?.readOnly ?? false
87
- : false);
88
50
  let localRelationship = "external";
89
- if (ownerIdentity !== null) {
90
- localRelationship = readOnly ? "read-only" : "owned";
91
- }
92
- else if (localRecord !== null) {
93
- localRelationship = "tracked";
51
+ if (ownerIsLocal) {
52
+ localRelationship = "local";
94
53
  }
95
54
  else if (ownerScriptPubKeyHex === null) {
96
55
  localRelationship = "unknown";
@@ -100,15 +59,13 @@ export function createWalletReadModel(walletState, snapshot) {
100
59
  domainId: chainRecord?.domainId ?? localRecord?.domainId ?? null,
101
60
  anchored: chainRecord?.anchored ?? (localRecord?.canonicalChainStatus === "anchored" ? true : localRecord?.canonicalChainStatus === "registered-unanchored" ? false : null),
102
61
  ownerScriptPubKeyHex,
103
- ownerLocalIndex: ownerIdentity?.index ?? localRecord?.currentOwnerLocalIndex ?? null,
104
- ownerAddress: ownerIdentity?.address ?? null,
62
+ ownerAddress: ownerScriptPubKeyHex === walletState.funding.scriptPubKeyHex ? walletState.funding.address : null,
105
63
  localTracked: localRecord !== null,
106
64
  localRecord,
107
65
  chainFound: chainRecord !== null,
108
66
  chainStatus: chainRecord === null
109
67
  ? localRecord?.canonicalChainStatus ?? "unknown"
110
68
  : chainRecord.anchored ? "anchored" : "registered-unanchored",
111
- localAnchorIntent: localRecord?.localAnchorIntent ?? null,
112
69
  foundingMessageText: chainRecord?.foundingMessage ?? localRecord?.foundingMessageText ?? null,
113
70
  endpointText: tryDecodeUtf8(chainRecord?.endpoint),
114
71
  delegateScriptPubKeyHex: bytesToHex(chainRecord?.delegate),
@@ -120,31 +77,21 @@ export function createWalletReadModel(walletState, snapshot) {
120
77
  supportedStakeCogtoshi: reputation?.supportedStake ?? null,
121
78
  totalSupportedCogtoshi: reputation?.totalSupported ?? null,
122
79
  totalRevokedCogtoshi: reputation?.totalRevoked ?? null,
123
- readOnly,
80
+ readOnly: false,
124
81
  localRelationship,
125
82
  };
126
83
  });
127
84
  return {
128
85
  walletRootId: walletState.walletRootId,
129
- fundingIdentity: identities.find((identity) => identity.index === walletState.fundingIndex) ?? null,
130
- identities,
86
+ walletAddress: walletState.funding.address,
87
+ walletScriptPubKeyHex: walletState.funding.scriptPubKeyHex,
131
88
  domains,
132
- readOnlyIdentityCount: identities.filter((identity) => identity.readOnly).length,
133
89
  };
134
90
  }
135
- function lookupDomainById(state, domainId) {
136
- for (const record of state.consensus.domainsById.values()) {
137
- if (record.domainId === domainId) {
138
- return { name: record.name };
139
- }
140
- }
141
- return null;
142
- }
143
91
  export function listWalletLocks(context) {
144
92
  if (context.snapshot === null || context.model === null) {
145
93
  return null;
146
94
  }
147
- const localScriptToIndex = new Map(context.model.identities.map((identity) => [identity.scriptPubKeyHex, identity.index]));
148
95
  const localDomainIds = new Set(context.model.domains
149
96
  .map((domain) => domain.domainId)
150
97
  .filter((domainId) => domainId !== null));
@@ -155,30 +102,32 @@ export function listWalletLocks(context) {
155
102
  const locks = [...context.snapshot.state.consensus.locks.values()]
156
103
  .filter((lock) => {
157
104
  const lockerHex = bytesToHex(lock.lockerScriptPubKey);
158
- return (lockerHex !== null && localScriptToIndex.has(lockerHex)) || localDomainIds.has(lock.recipientDomainId);
105
+ return (lockerHex !== null && lockerHex === context.model.walletScriptPubKeyHex) || localDomainIds.has(lock.recipientDomainId);
159
106
  })
160
107
  .sort((left, right) => left.timeoutHeight - right.timeoutHeight || left.lockId - right.lockId);
161
108
  return locks.map((lock) => {
162
109
  const lockerScriptPubKeyHex = bytesToHex(lock.lockerScriptPubKey);
163
- const lockerLocalIndex = lockerScriptPubKeyHex === null ? null : localScriptToIndex.get(lockerScriptPubKeyHex) ?? null;
164
110
  const recipientDomain = context.snapshot.state.consensus.domainsById.get(lock.recipientDomainId) ?? null;
165
111
  const recipientOwnerHex = recipientDomain === null ? null : bytesToHex(recipientDomain.ownerScriptPubKey);
166
112
  const claimableNow = currentHeight !== null
167
113
  && currentHeight < lock.timeoutHeight
168
114
  && recipientOwnerHex !== null
169
- && localScriptToIndex.has(recipientOwnerHex);
115
+ && recipientOwnerHex === context.model.walletScriptPubKeyHex;
170
116
  return {
171
117
  lockId: lock.lockId,
172
118
  status: "active",
173
119
  amountCogtoshi: lock.amount,
174
120
  timeoutHeight: lock.timeoutHeight,
175
121
  lockerScriptPubKeyHex: lockerScriptPubKeyHex ?? "",
176
- lockerLocalIndex,
122
+ lockerLocal: lockerScriptPubKeyHex === context.model.walletScriptPubKeyHex,
123
+ lockerLocalIndex: lockerScriptPubKeyHex === context.model.walletScriptPubKeyHex ? 0 : null,
177
124
  recipientDomainId: lock.recipientDomainId,
178
125
  recipientDomainName: domainsById.get(lock.recipientDomainId) ?? null,
179
126
  recipientLocal: localDomainIds.has(lock.recipientDomainId),
180
127
  claimableNow,
181
- reclaimableNow: currentHeight !== null && currentHeight >= lock.timeoutHeight && lockerLocalIndex !== null,
128
+ reclaimableNow: currentHeight !== null
129
+ && currentHeight >= lock.timeoutHeight
130
+ && lockerScriptPubKeyHex === context.model.walletScriptPubKeyHex,
182
131
  };
183
132
  });
184
133
  }
@@ -201,13 +150,11 @@ export function findWalletDomain(context, name) {
201
150
  domainId: chainDomain.domainId,
202
151
  anchored: chainDomain.anchored,
203
152
  ownerScriptPubKeyHex: bytesToHex(chainDomain.ownerScriptPubKey),
204
- ownerLocalIndex: null,
205
153
  ownerAddress: null,
206
154
  localTracked: false,
207
155
  localRecord: null,
208
156
  chainFound: true,
209
157
  chainStatus: chainDomain.anchored ? "anchored" : "registered-unanchored",
210
- localAnchorIntent: null,
211
158
  foundingMessageText: chainDomain.foundingMessage,
212
159
  endpointText: tryDecodeUtf8(chainDomain.endpoint),
213
160
  delegateScriptPubKeyHex: bytesToHex(chainDomain.delegate),
@@ -2,18 +2,19 @@ import type { IndexerState } from "@cogcoin/indexer/types";
2
2
  import type { ManagedBitcoindHealth, ManagedBitcoindObservedStatus, ManagedCoreWalletReplicaStatus, ManagedIndexerDaemonObservedStatus, ManagedIndexerTruthSource } from "../../bitcoind/types.js";
3
3
  import type { ClientTip } from "../../types.js";
4
4
  import type { MiningControlPlaneView } from "../mining/index.js";
5
- import type { DomainRecord as LocalDomainRecord, LocalIdentityRecord, WalletStateV1 } from "../types.js";
6
- export type WalletStateAvailability = "uninitialized" | "locked" | "ready" | "local-state-corrupt";
5
+ import type { DomainRecord as LocalDomainRecord, WalletStateV1 } from "../types.js";
6
+ import type { ClientPasswordReadiness } from "../state/client-password.js";
7
+ export type WalletStateAvailability = "uninitialized" | "ready" | "local-state-corrupt";
7
8
  export type WalletServiceHealth = "synced" | "catching-up" | "reorging" | "starting" | "stale-heartbeat" | "failed" | "schema-mismatch" | "service-version-mismatch" | "wallet-root-mismatch" | "unavailable";
8
9
  export interface WalletLocalStateStatus {
9
10
  availability: WalletStateAvailability;
11
+ clientPasswordReadiness: ClientPasswordReadiness;
12
+ unlockRequired: boolean;
10
13
  walletRootId: string | null;
11
14
  state: WalletStateV1 | null;
12
15
  source: "primary" | "backup" | null;
13
- unlockUntilUnixMs: number | null;
14
16
  hasPrimaryStateFile: boolean;
15
17
  hasBackupStateFile: boolean;
16
- hasUnlockSessionFile: boolean;
17
18
  message: string | null;
18
19
  }
19
20
  export interface WalletNodeStatus {
@@ -52,33 +53,16 @@ export interface WalletSnapshotView {
52
53
  snapshotSeq?: string | null;
53
54
  openedAtUnixMs?: number | null;
54
55
  }
55
- export interface WalletIdentityView {
56
- index: number;
57
- scriptPubKeyHex: string;
58
- address: string | null;
59
- selectors: string[];
60
- assignedDomainNames: string[];
61
- localStatus: LocalIdentityRecord["status"];
62
- effectiveStatus: LocalIdentityRecord["status"];
63
- canonicalDomainId: number | null;
64
- canonicalDomainName: string | null;
65
- ownedDomainNames: string[];
66
- anchoredOwnedDomainNames: string[];
67
- observedCogBalance: bigint | null;
68
- readOnly: boolean;
69
- }
70
56
  export interface WalletDomainView {
71
57
  name: string;
72
58
  domainId: number | null;
73
59
  anchored: boolean | null;
74
60
  ownerScriptPubKeyHex: string | null;
75
- ownerLocalIndex: number | null;
76
61
  ownerAddress: string | null;
77
62
  localTracked: boolean;
78
63
  localRecord: LocalDomainRecord | null;
79
64
  chainFound: boolean;
80
65
  chainStatus: LocalDomainRecord["canonicalChainStatus"];
81
- localAnchorIntent: LocalDomainRecord["localAnchorIntent"] | null;
82
66
  foundingMessageText: string | null;
83
67
  endpointText: string | null;
84
68
  delegateScriptPubKeyHex: string | null;
@@ -91,14 +75,13 @@ export interface WalletDomainView {
91
75
  totalSupportedCogtoshi: bigint | null;
92
76
  totalRevokedCogtoshi: bigint | null;
93
77
  readOnly: boolean;
94
- localRelationship: "owned" | "read-only" | "tracked" | "external" | "unknown";
78
+ localRelationship: "local" | "external" | "unknown";
95
79
  }
96
80
  export interface WalletReadModel {
97
81
  walletRootId: string;
98
- fundingIdentity: WalletIdentityView | null;
99
- identities: WalletIdentityView[];
82
+ walletAddress: string | null;
83
+ walletScriptPubKeyHex: string;
100
84
  domains: WalletDomainView[];
101
- readOnlyIdentityCount: number;
102
85
  }
103
86
  export interface WalletReadContext {
104
87
  dataDir: string;
@@ -111,6 +94,7 @@ export interface WalletReadContext {
111
94
  indexer: WalletIndexerStatus;
112
95
  snapshot: WalletSnapshotView | null;
113
96
  model: WalletReadModel | null;
97
+ fundingSpendableSats: bigint | null;
114
98
  mining?: MiningControlPlaneView;
115
99
  close(): Promise<void>;
116
100
  }
@@ -120,6 +104,7 @@ export interface WalletLockView {
120
104
  amountCogtoshi: bigint;
121
105
  timeoutHeight: number;
122
106
  lockerScriptPubKeyHex: string;
107
+ lockerLocal?: boolean;
123
108
  lockerLocalIndex: number | null;
124
109
  recipientDomainId: number;
125
110
  recipientDomainName: string | null;
@@ -40,7 +40,6 @@ export interface WalletResetPreview {
40
40
  defaultAction: "retain-mnemonic";
41
41
  acceptedInputs: ["", "skip", "delete wallet"];
42
42
  entropyRetainingResetAvailable: boolean;
43
- requiresPassphrase: boolean;
44
43
  envelopeSource: "primary" | "backup" | null;
45
44
  };
46
45
  bootstrapSnapshot: {
@@ -6,12 +6,13 @@ import { createRpcClient } from "../bitcoind/node.js";
6
6
  import { resolveBootstrapPathsForTesting } from "../bitcoind/bootstrap/paths.js";
7
7
  import { validateSnapshotFileForTesting } from "../bitcoind/bootstrap/snapshot-file.js";
8
8
  import { attachOrStartManagedBitcoindService, createManagedWalletReplica, } from "../bitcoind/service.js";
9
+ import { resolveLegacyHooksRootPath } from "../app-paths.js";
9
10
  import { resolveNormalizedWalletDescriptorState } from "./descriptor-normalization.js";
10
11
  import { acquireFileLock } from "./fs/lock.js";
11
12
  import { createInternalCoreWalletPassphrase, deriveWalletIdentityMaterial, deriveWalletMaterialFromMnemonic, } from "./material.js";
12
13
  import { loadMiningRuntimeStatus } from "./mining/runtime-artifacts.js";
14
+ import { clearLegacyWalletLockArtifacts, withUnlockedManagedCoreWallet } from "./managed-core-wallet.js";
13
15
  import { resolveWalletRuntimePathsForTesting } from "./runtime.js";
14
- import { loadWalletExplicitLock } from "./state/explicit-lock.js";
15
16
  import { createDefaultWalletSecretProvider, createWalletRootId, createWalletSecretReference, } from "./state/provider.js";
16
17
  import { loadWalletSeedIndex } from "./state/seed-index.js";
17
18
  import { extractWalletRootIdHintFromWalletStateEnvelope, loadRawWalletStateEnvelope, loadWalletState, saveWalletState, } from "./state/storage.js";
@@ -19,6 +20,9 @@ import { confirmTypedAcknowledgement } from "./tx/confirm.js";
19
20
  function sanitizeWalletName(walletRootId) {
20
21
  return `cogcoin-${walletRootId}`.replace(/[^a-zA-Z0-9._-]+/g, "-").slice(0, 63);
21
22
  }
23
+ function providerUsesExternalSecretStore(provider) {
24
+ return provider.kind === "macos-keychain";
25
+ }
22
26
  function isPathWithin(root, target) {
23
27
  const rel = relative(root, target);
24
28
  return rel === "" || (!rel.startsWith("..") && rel !== ".");
@@ -147,50 +151,17 @@ async function restoreStagedArtifacts(artifacts) {
147
151
  await moveFile(artifact.stagedPath, artifact.restorePath);
148
152
  }
149
153
  }
150
- function collectMnemonicDerivedIdentityIndices(state) {
151
- const indices = new Set();
152
- indices.add(state.fundingIndex);
153
- for (const identity of state.identities) {
154
- if (identity.status === "funding" || identity.status === "dedicated") {
155
- indices.add(identity.index);
156
- }
157
- }
158
- return [...indices].sort((left, right) => left - right);
159
- }
160
154
  function createEntropyRetainedWalletState(previousState, nowUnixMs) {
161
155
  const material = deriveWalletMaterialFromMnemonic(previousState.mnemonic.phrase);
162
156
  const walletRootId = createWalletRootId();
163
- const preservedIndices = collectMnemonicDerivedIdentityIndices(previousState);
164
- const identities = preservedIndices.map((index) => {
165
- if (index === previousState.fundingIndex) {
166
- return {
167
- index,
168
- scriptPubKeyHex: material.funding.scriptPubKeyHex,
169
- address: material.funding.address,
170
- status: "funding",
171
- assignedDomainNames: [],
172
- };
173
- }
174
- const identityMaterial = deriveWalletIdentityMaterial(material.keys.accountXprv, index);
175
- return {
176
- index,
177
- scriptPubKeyHex: identityMaterial.scriptPubKeyHex,
178
- address: identityMaterial.address,
179
- status: "dedicated",
180
- assignedDomainNames: [],
181
- };
182
- });
157
+ void deriveWalletIdentityMaterial;
183
158
  return {
184
- schemaVersion: 1,
159
+ schemaVersion: 5,
185
160
  stateRevision: 1,
186
161
  lastWrittenAtUnixMs: nowUnixMs,
187
162
  walletRootId,
188
163
  network: previousState.network,
189
- anchorValueSats: previousState.anchorValueSats,
190
- proactiveReserveSats: previousState.proactiveReserveSats,
191
- proactiveReserveOutpoints: previousState.proactiveReserveOutpoints,
192
- nextDedicatedIndex: previousState.nextDedicatedIndex,
193
- fundingIndex: previousState.fundingIndex,
164
+ localScriptPubKeyHexes: [material.funding.scriptPubKeyHex],
194
165
  mnemonic: {
195
166
  phrase: previousState.mnemonic.phrase,
196
167
  language: previousState.mnemonic.language,
@@ -217,13 +188,12 @@ function createEntropyRetainedWalletState(previousState, nowUnixMs) {
217
188
  walletName: sanitizeWalletName(walletRootId),
218
189
  internalPassphrase: createInternalCoreWalletPassphrase(),
219
190
  descriptorChecksum: null,
220
- fundingAddress0: null,
221
- fundingScriptPubKeyHex0: null,
191
+ walletAddress: null,
192
+ walletScriptPubKeyHex: null,
222
193
  proofStatus: "not-proven",
223
194
  lastImportedAtUnixMs: null,
224
195
  lastVerifiedAtUnixMs: null,
225
196
  },
226
- identities,
227
197
  domains: [],
228
198
  miningState: {
229
199
  runMode: "stopped",
@@ -246,7 +216,7 @@ function createEntropyRetainedWalletState(previousState, nowUnixMs) {
246
216
  currentBlockTargetHeight: null,
247
217
  currentReferencedBlockHashDisplay: null,
248
218
  currentIntentFingerprintHex: null,
249
- liveMiningFamilyInMempool: null,
219
+ livePublishInMempool: null,
250
220
  currentPublishDecision: null,
251
221
  replacementCount: 0,
252
222
  currentBlockFeeSpentSats: "0",
@@ -254,20 +224,6 @@ function createEntropyRetainedWalletState(previousState, nowUnixMs) {
254
224
  lifetimeFeeSpentSats: "0",
255
225
  sharedMiningConflictOutpoint: null,
256
226
  },
257
- hookClientState: {
258
- mining: {
259
- mode: "builtin",
260
- validationState: "never",
261
- lastValidationAtUnixMs: null,
262
- lastValidationError: null,
263
- validatedLaunchFingerprint: null,
264
- validatedFullFingerprint: null,
265
- fullTrustWarningAcknowledgedAtUnixMs: null,
266
- consecutiveFailureCount: 0,
267
- cooldownUntilUnixMs: null,
268
- },
269
- },
270
- proactiveFamilies: [],
271
227
  pendingMutations: [],
272
228
  };
273
229
  }
@@ -285,22 +241,23 @@ async function recreateManagedCoreWalletReplicaForReset(options) {
285
241
  });
286
242
  const normalizedDescriptors = await resolveNormalizedWalletDescriptorState(options.state, rpc);
287
243
  const walletName = sanitizeWalletName(options.state.walletRootId);
288
- await rpc.walletPassphrase(walletName, options.state.managedCoreWallet.internalPassphrase, 10);
289
- try {
290
- const importResults = await rpc.importDescriptors(walletName, [{
291
- desc: normalizedDescriptors.privateExternal,
292
- timestamp: options.state.walletBirthTime,
293
- active: false,
294
- internal: false,
295
- range: [0, options.state.descriptor.rangeEnd],
296
- }]);
297
- if (!importResults.every((result) => result.success)) {
298
- throw new Error(`wallet_descriptor_import_failed_${JSON.stringify(importResults)}`);
299
- }
300
- }
301
- finally {
302
- await rpc.walletLock(walletName).catch(() => undefined);
303
- }
244
+ await withUnlockedManagedCoreWallet({
245
+ rpc,
246
+ walletName,
247
+ internalPassphrase: options.state.managedCoreWallet.internalPassphrase,
248
+ run: async () => {
249
+ const importResults = await rpc.importDescriptors(walletName, [{
250
+ desc: normalizedDescriptors.privateExternal,
251
+ timestamp: options.state.walletBirthTime,
252
+ active: false,
253
+ internal: false,
254
+ range: [0, options.state.descriptor.rangeEnd],
255
+ }]);
256
+ if (!importResults.every((result) => result.success)) {
257
+ throw new Error(`wallet_descriptor_import_failed_${JSON.stringify(importResults)}`);
258
+ }
259
+ },
260
+ });
304
261
  const derivedFunding = await rpc.deriveAddresses(normalizedDescriptors.publicExternal, [0, 0]);
305
262
  if (derivedFunding[0] !== options.state.funding.address) {
306
263
  throw new Error("wallet_funding_address_verification_failed");
@@ -324,8 +281,8 @@ async function recreateManagedCoreWalletReplicaForReset(options) {
324
281
  ...options.state.managedCoreWallet,
325
282
  walletName,
326
283
  descriptorChecksum: normalizedDescriptors.checksum,
327
- fundingAddress0: options.state.funding.address,
328
- fundingScriptPubKeyHex0: options.state.funding.scriptPubKeyHex,
284
+ walletAddress: options.state.funding.address,
285
+ walletScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
329
286
  proofStatus: "ready",
330
287
  lastImportedAtUnixMs: options.nowUnixMs,
331
288
  lastVerifiedAtUnixMs: options.nowUnixMs,
@@ -337,12 +294,6 @@ async function recreateManagedCoreWalletReplicaForReset(options) {
337
294
  }, nextState, options.access);
338
295
  return nextState;
339
296
  }
340
- async function promptHiddenOrVisible(prompter, message) {
341
- if (typeof prompter.promptHidden === "function") {
342
- return await prompter.promptHidden(message);
343
- }
344
- return await prompter.prompt(message);
345
- }
346
297
  async function loadWalletForEntropyReset(options) {
347
298
  if (options.wallet.rawEnvelope === null) {
348
299
  throw new Error("reset_wallet_entropy_reset_unavailable");
@@ -366,28 +317,7 @@ async function loadWalletForEntropyReset(options) {
366
317
  throw new Error("reset_wallet_entropy_reset_unavailable");
367
318
  }
368
319
  }
369
- if (options.wallet.mode !== "passphrase-wrapped") {
370
- throw new Error("reset_wallet_entropy_reset_unavailable");
371
- }
372
- const passphrase = (await promptHiddenOrVisible(options.prompter, "Wallet-state passphrase: ")).trim();
373
- if (passphrase === "") {
374
- throw new Error("reset_wallet_passphrase_required");
375
- }
376
- try {
377
- return {
378
- loaded: await loadWalletState({
379
- primaryPath: options.paths.walletStatePath,
380
- backupPath: options.paths.walletStateBackupPath,
381
- }, passphrase),
382
- access: {
383
- kind: "passphrase",
384
- passphrase,
385
- },
386
- };
387
- }
388
- catch {
389
- throw new Error("reset_wallet_access_failed");
390
- }
320
+ throw new Error("reset_wallet_entropy_reset_unavailable");
391
321
  }
392
322
  async function collectTrackedManagedProcesses(paths) {
393
323
  const trackedProcesses = [];
@@ -478,7 +408,10 @@ function resolveBitcoindPreservingRemovedRoots(paths) {
478
408
  paths.stateRoot,
479
409
  paths.runtimeRoot,
480
410
  configRoot,
481
- paths.hooksRoot,
411
+ resolveLegacyHooksRootPath({
412
+ dataRoot: paths.dataRoot,
413
+ clientConfigPath: paths.clientConfigPath,
414
+ }),
482
415
  ]);
483
416
  }
484
417
  function resolveRemovedRoots(paths, options = {
@@ -497,7 +430,6 @@ async function preflightReset(options) {
497
430
  primaryPath: options.paths.walletStatePath,
498
431
  backupPath: options.paths.walletStateBackupPath,
499
432
  });
500
- const explicitLock = await loadWalletExplicitLock(options.paths.walletExplicitLockPath).catch(() => null);
501
433
  const snapshotPaths = resolveBootstrapPathsForTesting(options.dataDir, DEFAULT_SNAPSHOT_METADATA);
502
434
  const validateSnapshot = options.validateSnapshotFile
503
435
  ?? ((path) => validateSnapshotFileForTesting(path, DEFAULT_SNAPSHOT_METADATA));
@@ -537,11 +469,10 @@ async function preflightReset(options) {
537
469
  ? (hasWalletState ? "unknown" : "unknown")
538
470
  : rawEnvelope.envelope.secretProvider != null
539
471
  ? "provider-backed"
540
- : "passphrase-wrapped",
472
+ : "unsupported-legacy",
541
473
  envelopeSource: rawEnvelope?.source ?? null,
542
474
  secretProviderKeyId,
543
475
  importedSeedSecretProviderKeyIds,
544
- explicitLock,
545
476
  rawEnvelope,
546
477
  },
547
478
  snapshot: {
@@ -634,7 +565,6 @@ async function resolveResetExecutionDecision(options) {
634
565
  wallet: options.preflight.wallet,
635
566
  paths: options.paths,
636
567
  provider: options.provider,
637
- prompter: options.prompter,
638
568
  });
639
569
  }
640
570
  }
@@ -707,8 +637,7 @@ export async function previewResetWallet(options) {
707
637
  ? {
708
638
  defaultAction: "retain-mnemonic",
709
639
  acceptedInputs: ["", "skip", "delete wallet"],
710
- entropyRetainingResetAvailable: preflight.wallet.mode !== "unknown",
711
- requiresPassphrase: preflight.wallet.mode === "passphrase-wrapped",
640
+ entropyRetainingResetAvailable: preflight.wallet.mode === "provider-backed",
712
641
  envelopeSource: preflight.wallet.envelopeSource,
713
642
  }
714
643
  : null,
@@ -729,8 +658,9 @@ export async function previewResetWallet(options) {
729
658
  : null,
730
659
  },
731
660
  trackedProcessKinds: preflight.trackedProcessKinds,
732
- willDeleteOsSecrets: preflight.wallet.secretProviderKeyId !== null
733
- || preflight.wallet.importedSeedSecretProviderKeyIds.length > 0,
661
+ willDeleteOsSecrets: providerUsesExternalSecretStore(provider)
662
+ && (preflight.wallet.secretProviderKeyId !== null
663
+ || preflight.wallet.importedSeedSecretProviderKeyIds.length > 0),
734
664
  removedPaths,
735
665
  };
736
666
  }
@@ -782,7 +712,6 @@ export async function resetWallet(options) {
782
712
  const failedSecretRefs = [];
783
713
  const preservedSecretRefs = [];
784
714
  let walletOldRootId = extractWalletRootIdHintFromWalletStateEnvelope(preflight.wallet.rawEnvelope?.envelope ?? null)
785
- ?? preflight.wallet.explicitLock?.walletRootId
786
715
  ?? null;
787
716
  let walletNewRootId = null;
788
717
  try {
@@ -790,16 +719,12 @@ export async function resetWallet(options) {
790
719
  if (walletAction === "kept-unchanged" || walletAction === "retain-mnemonic") {
791
720
  const stagedPrimary = await stageArtifact(paths.walletStatePath, stagingRoot, "wallet/wallet-state.enc");
792
721
  const stagedBackup = await stageArtifact(paths.walletStateBackupPath, stagingRoot, "wallet/wallet-state.enc.bak");
793
- const stagedExplicitLock = await stageArtifact(paths.walletExplicitLockPath, stagingRoot, "wallet/wallet-explicit-lock.json");
794
722
  if (stagedPrimary !== null) {
795
723
  stagedWalletArtifacts.push(stagedPrimary);
796
724
  }
797
725
  if (stagedBackup !== null) {
798
726
  stagedWalletArtifacts.push(stagedBackup);
799
727
  }
800
- if (walletAction === "kept-unchanged" && stagedExplicitLock !== null) {
801
- stagedWalletArtifacts.push(stagedExplicitLock);
802
- }
803
728
  }
804
729
  if (snapshotResultStatus === "preserved" && isDeletedByRemovalPlan(removedPaths, preflight.snapshot.path)) {
805
730
  const stagedSnapshot = await stageArtifact(preflight.snapshot.path, stagingRoot, "snapshot/utxo-910000.dat");
@@ -823,28 +748,18 @@ export async function resetWallet(options) {
823
748
  let nextState = createEntropyRetainedWalletState(decision.loadedWalletForEntropyReset.loaded.state, nowUnixMs);
824
749
  walletOldRootId = decision.loadedWalletForEntropyReset.loaded.state.walletRootId;
825
750
  walletNewRootId = nextState.walletRootId;
826
- let nextAccess;
827
- if (decision.loadedWalletForEntropyReset.access.kind === "provider") {
828
- const secretReference = createWalletSecretReference(nextState.walletRootId);
829
- newProviderKeyId = secretReference.keyId;
830
- await provider.storeSecret(secretReference.keyId, randomBytes(32));
831
- nextAccess = {
832
- provider,
833
- secretReference,
834
- };
835
- await saveWalletState({
836
- primaryPath: paths.walletStatePath,
837
- backupPath: paths.walletStateBackupPath,
838
- }, nextState, nextAccess);
839
- preservedSecretRefs.push(secretReference.keyId);
840
- }
841
- else {
842
- nextAccess = decision.loadedWalletForEntropyReset.access.passphrase;
843
- await saveWalletState({
844
- primaryPath: paths.walletStatePath,
845
- backupPath: paths.walletStateBackupPath,
846
- }, nextState, nextAccess);
847
- }
751
+ const secretReference = createWalletSecretReference(nextState.walletRootId);
752
+ newProviderKeyId = secretReference.keyId;
753
+ await provider.storeSecret(secretReference.keyId, randomBytes(32));
754
+ const nextAccess = {
755
+ provider,
756
+ secretReference,
757
+ };
758
+ await saveWalletState({
759
+ primaryPath: paths.walletStatePath,
760
+ backupPath: paths.walletStateBackupPath,
761
+ }, nextState, nextAccess);
762
+ preservedSecretRefs.push(secretReference.keyId);
848
763
  nextState = await recreateManagedCoreWalletReplicaForReset({
849
764
  state: nextState,
850
765
  access: nextAccess,
@@ -892,12 +807,19 @@ export async function resetWallet(options) {
892
807
  else if (deletedSecretRefs.length > 0) {
893
808
  secretCleanupStatus = "deleted";
894
809
  }
895
- else if (preflight.wallet.secretProviderKeyId === null
810
+ else if (providerUsesExternalSecretStore(provider)
811
+ && preflight.wallet.secretProviderKeyId === null
896
812
  && preflight.wallet.importedSeedSecretProviderKeyIds.length === 0
897
813
  && preflight.wallet.present
898
814
  && preflight.wallet.rawEnvelope === null) {
899
815
  secretCleanupStatus = "unknown";
900
816
  }
817
+ else if (preflight.wallet.secretProviderKeyId === null
818
+ && preflight.wallet.importedSeedSecretProviderKeyIds.length === 0
819
+ && preflight.wallet.present
820
+ && preflight.wallet.rawEnvelope === null) {
821
+ secretCleanupStatus = "not-found";
822
+ }
901
823
  else if (deletedSecretRefs.length === 0) {
902
824
  secretCleanupStatus = "not-found";
903
825
  }