@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.
Files changed (174) 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 +142 -5
  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 +49 -92
  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 +5 -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/sync-progress.d.ts +6 -0
  75. package/dist/cli/sync-progress.js +91 -0
  76. package/dist/cli/types.d.ts +9 -17
  77. package/dist/cli/types.js +0 -2
  78. package/dist/cli/wallet-format.d.ts +1 -0
  79. package/dist/cli/wallet-format.js +208 -144
  80. package/dist/cli/workflow-hints.d.ts +3 -3
  81. package/dist/cli/workflow-hints.js +11 -8
  82. package/dist/client/default-client.d.ts +3 -1
  83. package/dist/client/default-client.js +45 -2
  84. package/dist/client/factory.js +1 -1
  85. package/dist/client/initialization.js +23 -0
  86. package/dist/client/persistence.js +5 -5
  87. package/dist/client/store-adapter.js +1 -0
  88. package/dist/sqlite/checkpoints.d.ts +1 -0
  89. package/dist/sqlite/checkpoints.js +7 -0
  90. package/dist/sqlite/store.js +14 -1
  91. package/dist/types.d.ts +1 -0
  92. package/dist/wallet/coin-control.d.ts +41 -12
  93. package/dist/wallet/coin-control.js +100 -428
  94. package/dist/wallet/descriptor-normalization.d.ts +1 -3
  95. package/dist/wallet/descriptor-normalization.js +0 -16
  96. package/dist/wallet/lifecycle.d.ts +7 -99
  97. package/dist/wallet/lifecycle.js +513 -968
  98. package/dist/wallet/managed-core-wallet.d.ts +13 -0
  99. package/dist/wallet/managed-core-wallet.js +20 -0
  100. package/dist/wallet/mining/constants.d.ts +5 -12
  101. package/dist/wallet/mining/constants.js +5 -12
  102. package/dist/wallet/mining/control.d.ts +1 -13
  103. package/dist/wallet/mining/control.js +45 -349
  104. package/dist/wallet/mining/index.d.ts +4 -5
  105. package/dist/wallet/mining/index.js +2 -3
  106. package/dist/wallet/mining/runner.d.ts +123 -13
  107. package/dist/wallet/mining/runner.js +899 -511
  108. package/dist/wallet/mining/runtime-artifacts.js +23 -3
  109. package/dist/wallet/mining/sentence-protocol.d.ts +44 -0
  110. package/dist/wallet/mining/sentence-protocol.js +123 -0
  111. package/dist/wallet/mining/sentences.d.ts +4 -8
  112. package/dist/wallet/mining/sentences.js +3 -52
  113. package/dist/wallet/mining/state.d.ts +11 -6
  114. package/dist/wallet/mining/state.js +7 -6
  115. package/dist/wallet/mining/types.d.ts +2 -30
  116. package/dist/wallet/mining/visualizer.d.ts +31 -3
  117. package/dist/wallet/mining/visualizer.js +135 -13
  118. package/dist/wallet/read/context.d.ts +0 -2
  119. package/dist/wallet/read/context.js +119 -140
  120. package/dist/wallet/read/filter.js +2 -11
  121. package/dist/wallet/read/index.d.ts +1 -1
  122. package/dist/wallet/read/project.js +24 -77
  123. package/dist/wallet/read/types.d.ts +10 -25
  124. package/dist/wallet/reset.d.ts +0 -1
  125. package/dist/wallet/reset.js +60 -138
  126. package/dist/wallet/root-resolution.d.ts +1 -5
  127. package/dist/wallet/root-resolution.js +0 -18
  128. package/dist/wallet/runtime.d.ts +0 -6
  129. package/dist/wallet/runtime.js +0 -8
  130. package/dist/wallet/state/client-password-agent.js +208 -0
  131. package/dist/wallet/state/client-password.d.ts +65 -0
  132. package/dist/wallet/state/client-password.js +952 -0
  133. package/dist/wallet/state/crypto.d.ts +1 -20
  134. package/dist/wallet/state/crypto.js +0 -63
  135. package/dist/wallet/state/provider.d.ts +23 -11
  136. package/dist/wallet/state/provider.js +248 -290
  137. package/dist/wallet/state/storage.d.ts +2 -2
  138. package/dist/wallet/state/storage.js +48 -16
  139. package/dist/wallet/tx/anchor.d.ts +3 -28
  140. package/dist/wallet/tx/anchor.js +349 -1250
  141. package/dist/wallet/tx/bitcoin-transfer.d.ts +35 -0
  142. package/dist/wallet/tx/bitcoin-transfer.js +200 -0
  143. package/dist/wallet/tx/cog.d.ts +5 -1
  144. package/dist/wallet/tx/cog.js +149 -185
  145. package/dist/wallet/tx/common.d.ts +61 -8
  146. package/dist/wallet/tx/common.js +266 -146
  147. package/dist/wallet/tx/domain-admin.d.ts +3 -1
  148. package/dist/wallet/tx/domain-admin.js +61 -99
  149. package/dist/wallet/tx/domain-market.d.ts +5 -1
  150. package/dist/wallet/tx/domain-market.js +221 -228
  151. package/dist/wallet/tx/field.d.ts +4 -10
  152. package/dist/wallet/tx/field.js +83 -924
  153. package/dist/wallet/tx/identity-selector.d.ts +9 -3
  154. package/dist/wallet/tx/identity-selector.js +17 -35
  155. package/dist/wallet/tx/index.d.ts +3 -1
  156. package/dist/wallet/tx/index.js +2 -1
  157. package/dist/wallet/tx/register.d.ts +3 -1
  158. package/dist/wallet/tx/register.js +62 -220
  159. package/dist/wallet/tx/reputation.d.ts +3 -1
  160. package/dist/wallet/tx/reputation.js +58 -95
  161. package/dist/wallet/types.d.ts +8 -122
  162. package/package.json +5 -5
  163. package/dist/wallet/archive.d.ts +0 -4
  164. package/dist/wallet/archive.js +0 -41
  165. package/dist/wallet/mining/hook-protocol.d.ts +0 -47
  166. package/dist/wallet/mining/hook-protocol.js +0 -161
  167. package/dist/wallet/mining/hook-runner.js +0 -52
  168. package/dist/wallet/mining/hooks.d.ts +0 -38
  169. package/dist/wallet/mining/hooks.js +0 -520
  170. package/dist/wallet/state/explicit-lock.d.ts +0 -4
  171. package/dist/wallet/state/explicit-lock.js +0 -19
  172. package/dist/wallet/state/session.d.ts +0 -12
  173. package/dist/wallet/state/session.js +0 -23
  174. /package/dist/wallet/{mining/hook-runner.d.ts → state/client-password-agent.d.ts} +0 -0
@@ -1,438 +1,126 @@
1
- import { saveUnlockSession } from "./state/session.js";
2
1
  import { persistWalletStateUpdate } from "./descriptor-normalization.js";
3
- import { miningFamilyMayStillExist } from "./mining/state.js";
4
- export const DEFAULT_PROACTIVE_RESERVE_SATS = 1_000;
5
- function btcNumberToSats(value) {
6
- return BigInt(Math.round(value * 100_000_000));
7
- }
2
+ import { normalizeMiningStateRecord } from "./mining/state.js";
3
+ export const DEFAULT_PROACTIVE_RESERVE_SATS = 0;
8
4
  export function outpointKey(outpoint) {
9
5
  return `${outpoint.txid}:${outpoint.vout}`;
10
6
  }
11
- function normalizeOutpointRecordList(outpoints) {
12
- const normalized = [];
7
+ function uniqueStrings(values) {
8
+ return values.filter((value, index, entries) => typeof value === "string" && value.length > 0 && entries.indexOf(value) === index);
9
+ }
10
+ function uniqueOutpoints(values) {
13
11
  const seen = new Set();
14
- for (const outpoint of outpoints ?? []) {
15
- if (outpoint == null
16
- || typeof outpoint.txid !== "string"
17
- || outpoint.txid.length === 0
18
- || typeof outpoint.vout !== "number"
19
- || !Number.isInteger(outpoint.vout)
20
- || outpoint.vout < 0) {
12
+ const normalized = [];
13
+ for (const value of values ?? []) {
14
+ if (value == null
15
+ || typeof value.txid !== "string"
16
+ || value.txid.length === 0
17
+ || typeof value.vout !== "number"
18
+ || !Number.isInteger(value.vout)
19
+ || value.vout < 0) {
21
20
  continue;
22
21
  }
23
- const key = outpointKey(outpoint);
22
+ const key = outpointKey(value);
24
23
  if (seen.has(key)) {
25
24
  continue;
26
25
  }
27
26
  seen.add(key);
28
- normalized.push({ txid: outpoint.txid, vout: outpoint.vout });
27
+ normalized.push({ txid: value.txid, vout: value.vout });
29
28
  }
30
29
  return normalized;
31
30
  }
32
- function normalizeReserveSats(raw) {
33
- if (typeof raw !== "number" || !Number.isFinite(raw)) {
34
- return DEFAULT_PROACTIVE_RESERVE_SATS;
35
- }
36
- const normalized = Math.max(0, Math.trunc(raw));
37
- if (normalized === 0) {
38
- return 0;
39
- }
40
- return DEFAULT_PROACTIVE_RESERVE_SATS;
41
- }
42
- function sameOutpointList(left, right) {
43
- if (left.length !== (right?.length ?? 0)) {
44
- return false;
45
- }
46
- return left.every((outpoint, index) => outpoint.txid === right?.[index]?.txid && outpoint.vout === right?.[index]?.vout);
47
- }
48
- export function normalizeWalletStateRecord(state) {
49
- const rawProactiveReserveSats = state.proactiveReserveSats;
50
- const proactiveReserveSats = normalizeReserveSats(rawProactiveReserveSats);
51
- const reserveValueChanged = proactiveReserveSats !== rawProactiveReserveSats;
52
- const proactiveReserveOutpoints = normalizeOutpointRecordList(proactiveReserveSats <= 0 || reserveValueChanged
53
- ? []
54
- : state.proactiveReserveOutpoints);
55
- const pendingMutations = state.pendingMutations ?? [];
56
- if (proactiveReserveSats === state.proactiveReserveSats
57
- && sameOutpointList(proactiveReserveOutpoints, state.proactiveReserveOutpoints)
58
- && pendingMutations === state.pendingMutations) {
59
- return state;
60
- }
61
- return {
62
- ...state,
63
- proactiveReserveSats,
64
- proactiveReserveOutpoints,
65
- pendingMutations,
66
- };
67
- }
68
- export function normalizePortableWalletArchivePayload(payload) {
69
- const rawProactiveReserveSats = payload.proactiveReserveSats;
70
- const proactiveReserveSats = normalizeReserveSats(rawProactiveReserveSats);
71
- const reserveValueChanged = proactiveReserveSats !== rawProactiveReserveSats;
72
- const proactiveReserveOutpoints = normalizeOutpointRecordList(proactiveReserveSats <= 0 || reserveValueChanged
73
- ? []
74
- : payload.proactiveReserveOutpoints);
75
- if (proactiveReserveSats === payload.proactiveReserveSats
76
- && sameOutpointList(proactiveReserveOutpoints, payload.proactiveReserveOutpoints)) {
77
- return payload;
78
- }
79
- return {
80
- ...payload,
81
- proactiveReserveSats,
82
- proactiveReserveOutpoints,
83
- };
84
- }
85
- function isSpendableUtxo(entry) {
86
- return entry.spendable !== false && entry.safe !== false;
87
- }
88
- function isConfirmedFundingUtxo(state, entry) {
89
- return entry.scriptPubKey === state.funding.scriptPubKeyHex
90
- && entry.confirmations >= 1
91
- && isSpendableUtxo(entry);
92
- }
93
- function sortFundingEntriesForReserve(entries) {
94
- return entries.slice().sort((left, right) => {
95
- const amount = btcNumberToSats(right.amount) - btcNumberToSats(left.amount);
96
- if (amount !== 0n) {
97
- return amount > 0n ? 1 : -1;
98
- }
99
- const txid = left.txid.localeCompare(right.txid);
100
- if (txid !== 0) {
101
- return txid;
102
- }
103
- return left.vout - right.vout;
104
- });
105
- }
106
- function isActiveTrackedTransaction(record) {
107
- if (record == null) {
108
- return false;
109
- }
110
- return record.status === "broadcasting"
111
- || record.status === "broadcast-unknown"
112
- || record.status === "live";
113
- }
114
- function isActiveTrackedStatus(status) {
115
- return status === "broadcasting"
116
- || status === "broadcast-unknown"
117
- || status === "live";
118
- }
119
- function deriveLiveProvisionalOutpointKeys(state) {
120
- const keys = new Set();
121
- for (const family of state.proactiveFamilies) {
122
- if ((family.type !== "anchor" && family.type !== "field") || family.tx1?.attemptedTxid == null) {
123
- continue;
124
- }
125
- if (isActiveTrackedStatus(family.status)) {
126
- keys.add(outpointKey({ txid: family.tx1.attemptedTxid, vout: 1 }));
127
- }
128
- }
129
- return keys;
130
- }
131
- function deriveAuxiliaryDedicatedOutpoints(state, spendableUtxos) {
132
- const canonicalAnchorKeys = new Set(state.domains
133
- .map((domain) => domain.currentCanonicalAnchorOutpoint)
134
- .filter((outpoint) => outpoint !== null)
135
- .map((outpoint) => outpointKey(outpoint)));
136
- const dedicatedScriptSet = new Set(state.identities
137
- .filter((identity) => identity.status === "dedicated")
138
- .map((identity) => identity.scriptPubKeyHex));
139
- const liveProvisionalKeys = deriveLiveProvisionalOutpointKeys(state);
140
- const auxiliary = [];
141
- const seen = new Set();
142
- for (const entry of spendableUtxos) {
143
- if (!isSpendableUtxo(entry) || !dedicatedScriptSet.has(entry.scriptPubKey)) {
144
- continue;
145
- }
146
- const outpoint = { txid: entry.txid, vout: entry.vout };
147
- const key = outpointKey(outpoint);
148
- if (canonicalAnchorKeys.has(key) || liveProvisionalKeys.has(key) || seen.has(key)) {
149
- continue;
150
- }
151
- seen.add(key);
152
- auxiliary.push(outpoint);
153
- }
154
- return auxiliary;
155
- }
156
- export function computeDesignatedProactiveReserveOutpoints(state, spendableUtxos) {
157
- const normalizedState = normalizeWalletStateRecord(state);
158
- if (normalizedState.proactiveReserveSats <= 0) {
159
- return [];
160
- }
161
- const conflictKey = normalizedState.miningState.sharedMiningConflictOutpoint === null
162
- ? null
163
- : outpointKey(normalizedState.miningState.sharedMiningConflictOutpoint);
164
- const eligible = sortFundingEntriesForReserve(spendableUtxos.filter((entry) => isConfirmedFundingUtxo(normalizedState, entry)
165
- && outpointKey({ txid: entry.txid, vout: entry.vout }) !== conflictKey));
166
- const selected = [];
167
- let total = 0n;
168
- const target = BigInt(normalizedState.proactiveReserveSats);
169
- for (const entry of eligible) {
170
- selected.push({ txid: entry.txid, vout: entry.vout });
171
- total += btcNumberToSats(entry.amount);
172
- if (total >= target) {
173
- break;
174
- }
175
- }
176
- if (total < target) {
177
- return [];
178
- }
179
- return selected;
31
+ function uniqueLocalScriptPubKeyHexes(state) {
32
+ return uniqueStrings([
33
+ state.funding?.scriptPubKeyHex ?? "",
34
+ ...(state.localScriptPubKeyHexes ?? []),
35
+ ...((state.identities ?? []).map((identity) => identity?.scriptPubKeyHex ?? "")),
36
+ ]);
180
37
  }
181
- function syncStateWithComputedReserve(state, spendableUtxos) {
182
- const normalizedState = normalizeWalletStateRecord(state);
183
- const proactiveReserveOutpoints = computeDesignatedProactiveReserveOutpoints(normalizedState, spendableUtxos);
184
- const sameLength = proactiveReserveOutpoints.length === normalizedState.proactiveReserveOutpoints.length;
185
- const sameKeys = sameLength && proactiveReserveOutpoints.every((outpoint, index) => outpointKey(outpoint) === outpointKey(normalizedState.proactiveReserveOutpoints[index]));
186
- if (sameKeys && normalizedState === state) {
187
- return {
188
- state,
189
- changed: false,
190
- };
191
- }
192
- if (sameKeys) {
193
- return {
194
- state: normalizedState,
195
- changed: true,
196
- };
197
- }
38
+ function normalizeDomains(rawDomains) {
39
+ return (rawDomains ?? [])
40
+ .filter((domain) => typeof domain?.name === "string" && domain.name.trim().length > 0)
41
+ .map((domain) => ({
42
+ name: domain.name.trim().toLowerCase(),
43
+ domainId: domain.domainId ?? null,
44
+ currentOwnerScriptPubKeyHex: domain.currentOwnerScriptPubKeyHex ?? null,
45
+ canonicalChainStatus: domain.canonicalChainStatus ?? "unknown",
46
+ foundingMessageText: domain.foundingMessageText ?? null,
47
+ birthTime: domain.birthTime ?? null,
48
+ }))
49
+ .sort((left, right) => left.name.localeCompare(right.name));
50
+ }
51
+ export function normalizeWalletStateRecord(rawState) {
52
+ const fundingAddress = rawState.funding?.address
53
+ ?? rawState.managedCoreWallet?.walletAddress
54
+ ?? rawState.managedCoreWallet?.fundingAddress0
55
+ ?? "";
56
+ const fundingScriptPubKeyHex = rawState.funding?.scriptPubKeyHex
57
+ ?? rawState.managedCoreWallet?.walletScriptPubKeyHex
58
+ ?? rawState.managedCoreWallet?.fundingScriptPubKeyHex0
59
+ ?? "";
60
+ const localScriptPubKeyHexes = uniqueLocalScriptPubKeyHexes(rawState);
61
+ const pendingMutations = (rawState.pendingMutations ?? [])
62
+ .filter((mutation) => mutation.status === "confirmed" || mutation.status === "canceled")
63
+ .map((mutation) => ({
64
+ ...mutation,
65
+ senderLocalIndex: mutation.senderScriptPubKeyHex === fundingScriptPubKeyHex ? 0 : null,
66
+ senderScriptPubKeyHex: mutation.senderScriptPubKeyHex === ""
67
+ ? fundingScriptPubKeyHex
68
+ : mutation.senderScriptPubKeyHex,
69
+ temporaryBuilderLockedOutpoints: [],
70
+ }));
198
71
  return {
199
- state: {
200
- ...normalizedState,
201
- proactiveReserveOutpoints,
72
+ schemaVersion: 5,
73
+ stateRevision: rawState.stateRevision ?? 1,
74
+ lastWrittenAtUnixMs: rawState.lastWrittenAtUnixMs ?? 0,
75
+ walletRootId: rawState.walletRootId ?? "",
76
+ network: rawState.network ?? "mainnet",
77
+ localScriptPubKeyHexes,
78
+ mnemonic: {
79
+ phrase: rawState.mnemonic?.phrase ?? "",
80
+ language: rawState.mnemonic?.language ?? "english",
202
81
  },
203
- changed: true,
204
- };
205
- }
206
- function collectInactiveTemporaryBuilderLockCleanup(state) {
207
- const normalizedState = normalizeWalletStateRecord(state);
208
- const stale = new Map();
209
- let familiesChanged = false;
210
- let mutationsChanged = false;
211
- const proactiveFamilies = normalizedState.proactiveFamilies.map((family) => {
212
- let nextFamily = family;
213
- for (const key of ["tx1", "tx2"]) {
214
- const record = nextFamily[key];
215
- if (record == null || isActiveTrackedTransaction(record) || record.temporaryBuilderLockedOutpoints.length === 0) {
216
- continue;
217
- }
218
- for (const outpoint of record.temporaryBuilderLockedOutpoints) {
219
- stale.set(outpointKey(outpoint), outpoint);
220
- }
221
- nextFamily = {
222
- ...nextFamily,
223
- [key]: {
224
- ...record,
225
- temporaryBuilderLockedOutpoints: [],
226
- },
227
- };
228
- familiesChanged = true;
229
- }
230
- return nextFamily;
231
- });
232
- const pendingMutations = (normalizedState.pendingMutations ?? []).map((mutation) => {
233
- if (isActiveTrackedStatus(mutation.status) || mutation.temporaryBuilderLockedOutpoints.length === 0) {
234
- return mutation;
235
- }
236
- for (const outpoint of mutation.temporaryBuilderLockedOutpoints) {
237
- stale.set(outpointKey(outpoint), outpoint);
238
- }
239
- mutationsChanged = true;
240
- return {
241
- ...mutation,
242
- temporaryBuilderLockedOutpoints: [],
243
- };
244
- });
245
- if (!familiesChanged && !mutationsChanged && normalizedState === state) {
246
- return {
247
- state,
248
- staleOutpoints: [],
249
- changed: false,
250
- };
251
- }
252
- return {
253
- state: familiesChanged || mutationsChanged
254
- ? {
255
- ...normalizedState,
256
- proactiveFamilies,
257
- pendingMutations,
258
- }
259
- : normalizedState,
260
- staleOutpoints: [...stale.values()],
261
- changed: familiesChanged || mutationsChanged || normalizedState !== state,
262
- };
263
- }
264
- function collectPersistentPolicyLockedOutpoints(state, spendableUtxos) {
265
- const outpoints = [];
266
- const seen = new Set();
267
- const pushUnique = (outpoint) => {
268
- if (outpoint === null) {
269
- return;
270
- }
271
- const key = outpointKey(outpoint);
272
- if (seen.has(key)) {
273
- return;
274
- }
275
- seen.add(key);
276
- outpoints.push(outpoint);
277
- };
278
- for (const domain of state.domains) {
279
- pushUnique(domain.currentCanonicalAnchorOutpoint);
280
- }
281
- for (const outpoint of deriveAuxiliaryDedicatedOutpoints(state, spendableUtxos)) {
282
- pushUnique(outpoint);
283
- }
284
- for (const outpoint of state.proactiveReserveOutpoints) {
285
- pushUnique(outpoint);
286
- }
287
- if (!miningFamilyMayStillExist(state.miningState)) {
288
- pushUnique(state.miningState.sharedMiningConflictOutpoint);
289
- }
290
- return outpoints;
291
- }
292
- function collectManagedScriptPubKeyHexes(state) {
293
- const scripts = new Set();
294
- const add = (scriptPubKeyHex) => {
295
- if (typeof scriptPubKeyHex === "string" && scriptPubKeyHex.length > 0) {
296
- scripts.add(scriptPubKeyHex);
297
- }
82
+ keys: {
83
+ masterFingerprintHex: rawState.keys?.masterFingerprintHex ?? "",
84
+ accountPath: rawState.keys?.accountPath ?? "",
85
+ accountXprv: rawState.keys?.accountXprv ?? "",
86
+ accountXpub: rawState.keys?.accountXpub ?? "",
87
+ },
88
+ descriptor: {
89
+ privateExternal: rawState.descriptor?.privateExternal ?? "",
90
+ publicExternal: rawState.descriptor?.publicExternal ?? "",
91
+ checksum: rawState.descriptor?.checksum ?? null,
92
+ rangeEnd: rawState.descriptor?.rangeEnd ?? 0,
93
+ safetyMargin: rawState.descriptor?.safetyMargin ?? 0,
94
+ },
95
+ funding: {
96
+ address: fundingAddress,
97
+ scriptPubKeyHex: fundingScriptPubKeyHex,
98
+ },
99
+ walletBirthTime: rawState.walletBirthTime ?? 0,
100
+ managedCoreWallet: {
101
+ walletName: rawState.managedCoreWallet?.walletName ?? "",
102
+ internalPassphrase: rawState.managedCoreWallet?.internalPassphrase ?? "",
103
+ descriptorChecksum: rawState.managedCoreWallet?.descriptorChecksum ?? null,
104
+ walletAddress: rawState.managedCoreWallet?.walletAddress ?? fundingAddress,
105
+ walletScriptPubKeyHex: rawState.managedCoreWallet?.walletScriptPubKeyHex ?? fundingScriptPubKeyHex,
106
+ proofStatus: rawState.managedCoreWallet?.proofStatus ?? "not-proven",
107
+ lastImportedAtUnixMs: rawState.managedCoreWallet?.lastImportedAtUnixMs ?? null,
108
+ lastVerifiedAtUnixMs: rawState.managedCoreWallet?.lastVerifiedAtUnixMs ?? null,
109
+ },
110
+ domains: normalizeDomains(rawState.domains),
111
+ miningState: normalizeMiningStateRecord(rawState.miningState),
112
+ pendingMutations,
298
113
  };
299
- add(state.funding.scriptPubKeyHex);
300
- for (const identity of state.identities) {
301
- add(identity.scriptPubKeyHex);
302
- }
303
- for (const domain of state.domains) {
304
- add(domain.currentOwnerScriptPubKeyHex);
305
- }
306
- for (const family of state.proactiveFamilies) {
307
- add(family.sourceSenderScriptPubKeyHex);
308
- add(family.reservedScriptPubKeyHex);
309
- }
310
- add(state.miningState.currentSenderScriptPubKeyHex);
311
- return scripts;
312
114
  }
313
- function findWalletTransactionOutputScriptPubKeyHex(transaction, vout) {
314
- const decodedScriptPubKeyHex = transaction?.decoded?.vout.find((output) => output.n === vout)?.scriptPubKey?.hex;
315
- return typeof decodedScriptPubKeyHex === "string" && decodedScriptPubKeyHex.length > 0
316
- ? decodedScriptPubKeyHex
317
- : null;
318
- }
319
- async function collectManagedInspectionUnlocks(options) {
320
- if (options.rpc.getTransaction === undefined) {
321
- return [];
322
- }
323
- const managedScripts = collectManagedScriptPubKeyHexes(options.state);
324
- if (managedScripts.size === 0 || options.lockedOutpoints.length === 0) {
325
- return [];
326
- }
327
- const transactionCache = new Map();
328
- const inspectionUnlocks = [];
329
- const loadTransaction = (txid) => {
330
- let cached = transactionCache.get(txid);
331
- if (cached === undefined) {
332
- cached = options.rpc.getTransaction?.(options.walletName, txid).catch(() => null) ?? Promise.resolve(null);
333
- transactionCache.set(txid, cached);
334
- }
335
- return cached;
336
- };
337
- for (const outpoint of options.lockedOutpoints) {
338
- const key = outpointKey(outpoint);
339
- if (options.fixedInputKeys.has(key) || options.temporarilyUnlockedKeys.has(key)) {
340
- continue;
341
- }
342
- const transaction = await loadTransaction(outpoint.txid);
343
- const scriptPubKeyHex = findWalletTransactionOutputScriptPubKeyHex(transaction, outpoint.vout);
344
- if (scriptPubKeyHex !== null && managedScripts.has(scriptPubKeyHex)) {
345
- inspectionUnlocks.push({ txid: outpoint.txid, vout: outpoint.vout });
346
- }
347
- }
348
- return inspectionUnlocks;
115
+ export function computeDesignatedProactiveReserveOutpoints(_state, _spendableUtxos) {
116
+ return [];
349
117
  }
350
118
  export async function reconcilePersistentPolicyLocks(options) {
351
- const rawReserveOutpoints = normalizeOutpointRecordList(options.state.proactiveReserveOutpoints);
352
- let state = normalizeWalletStateRecord(options.state);
353
- let changed = state !== options.state;
354
- const fixedInputKeys = new Set((options.fixedInputs ?? []).map((outpoint) => outpointKey(outpoint)));
355
- const temporarilyUnlockedKeys = new Set((options.temporarilyUnlockedOutpoints ?? []).map((outpoint) => outpointKey(outpoint)));
356
- if (options.cleanupInactiveTemporaryBuilderLocks === true) {
357
- const cleaned = collectInactiveTemporaryBuilderLockCleanup(state);
358
- state = cleaned.state;
359
- changed ||= cleaned.changed;
360
- if (cleaned.staleOutpoints.length > 0) {
361
- await options.rpc.lockUnspent(options.walletName, true, cleaned.staleOutpoints).catch(() => undefined);
362
- }
363
- }
364
- const lockedBeforeReserveInspection = await options.rpc.listLockUnspent(options.walletName).catch(() => []);
365
- const lockedBeforeReserveInspectionKeys = new Set(lockedBeforeReserveInspection.map((outpoint) => outpointKey(outpoint)));
366
- const reserveInspectionUnlocks = rawReserveOutpoints.filter((outpoint) => {
367
- const key = outpointKey(outpoint);
368
- return lockedBeforeReserveInspectionKeys.has(key) && !fixedInputKeys.has(key);
369
- });
370
- const managedInspectionUnlocks = await collectManagedInspectionUnlocks({
371
- rpc: options.rpc,
372
- walletName: options.walletName,
373
- state,
374
- lockedOutpoints: lockedBeforeReserveInspection,
375
- fixedInputKeys,
376
- temporarilyUnlockedKeys,
377
- });
378
- const inspectionUnlockMap = new Map();
379
- for (const outpoint of [...reserveInspectionUnlocks, ...managedInspectionUnlocks]) {
380
- inspectionUnlockMap.set(outpointKey(outpoint), outpoint);
381
- }
382
- const inspectionUnlocks = [...inspectionUnlockMap.values()];
383
- if (inspectionUnlocks.length > 0) {
384
- await options.rpc.lockUnspent(options.walletName, true, inspectionUnlocks).catch(() => undefined);
385
- }
386
- const spendableUtxos = inspectionUnlocks.length > 0 || options.spendableUtxos === undefined
387
- ? await options.rpc.listUnspent(options.walletName, 0).catch(() => [])
388
- : options.spendableUtxos;
389
- const previouslyProtectedUniverse = collectPersistentPolicyLockedOutpoints(state, spendableUtxos);
390
- const reserveSynced = syncStateWithComputedReserve(state, spendableUtxos);
391
- state = reserveSynced.state;
392
- changed ||= reserveSynced.changed;
393
- const protectedUniverse = collectPersistentPolicyLockedOutpoints(state, spendableUtxos);
394
- if (protectedUniverse.length === 0 && previouslyProtectedUniverse.length === 0) {
395
- return {
396
- state,
397
- changed,
398
- spendableUtxos,
399
- };
400
- }
401
- const protectedUniverseKeys = new Set(protectedUniverse.map((outpoint) => outpointKey(outpoint)));
402
- const previouslyProtectedUniverseKeys = new Set(previouslyProtectedUniverse.map((outpoint) => outpointKey(outpoint)));
403
- const managedProtectedKeys = new Set([
404
- ...protectedUniverseKeys,
405
- ...previouslyProtectedUniverseKeys,
406
- ]);
407
- const locked = await options.rpc.listLockUnspent(options.walletName).catch(() => []);
408
- const spendableKeys = new Set(spendableUtxos.map((entry) => outpointKey(entry)));
409
- const lockedKeys = new Set(locked.map((outpoint) => outpointKey(outpoint)));
410
- const expectedLocked = protectedUniverse.filter((outpoint) => {
411
- const key = outpointKey(outpoint);
412
- return (spendableKeys.has(key) || lockedKeys.has(key))
413
- && !fixedInputKeys.has(key)
414
- && !temporarilyUnlockedKeys.has(key);
415
- });
416
- const expectedLockedKeys = new Set(expectedLocked.map((outpoint) => outpointKey(outpoint)));
417
- const lockedManaged = locked.filter((outpoint) => managedProtectedKeys.has(outpointKey(outpoint)));
418
- const staleLocked = lockedManaged.filter((outpoint) => !expectedLockedKeys.has(outpointKey(outpoint)));
419
- const missingLocked = protectedUniverse.filter((outpoint) => {
420
- const key = outpointKey(outpoint);
421
- return spendableKeys.has(key)
422
- && !fixedInputKeys.has(key)
423
- && !temporarilyUnlockedKeys.has(key)
424
- && !lockedKeys.has(key);
425
- });
426
- if (staleLocked.length > 0) {
427
- await options.rpc.lockUnspent(options.walletName, true, staleLocked).catch(() => undefined);
428
- }
429
- if (missingLocked.length > 0) {
430
- await options.rpc.lockUnspent(options.walletName, false, missingLocked).catch(() => undefined);
431
- }
119
+ const state = normalizeWalletStateRecord(options.state);
432
120
  return {
433
121
  state,
434
- changed,
435
- spendableUtxos,
122
+ changed: state !== options.state,
123
+ spendableUtxos: options.spendableUtxos ?? await options.rpc.listUnspent(options.walletName, 0).catch(() => []),
436
124
  };
437
125
  }
438
126
  export async function persistWalletCoinControlStateIfNeeded(options) {
@@ -440,12 +128,10 @@ export async function persistWalletCoinControlStateIfNeeded(options) {
440
128
  rpc: options.rpc,
441
129
  walletName: options.state.managedCoreWallet.walletName,
442
130
  state: options.state,
443
- cleanupInactiveTemporaryBuilderLocks: options.cleanupInactiveTemporaryBuilderLocks ?? true,
444
131
  });
445
132
  if (!reconciled.changed) {
446
133
  return {
447
134
  changed: false,
448
- session: options.session ?? null,
449
135
  state: reconciled.state,
450
136
  };
451
137
  }
@@ -456,22 +142,8 @@ export async function persistWalletCoinControlStateIfNeeded(options) {
456
142
  nowUnixMs: options.nowUnixMs,
457
143
  replacePrimary: options.replacePrimary,
458
144
  });
459
- if (options.session == null) {
460
- return {
461
- changed: true,
462
- session: null,
463
- state: nextState,
464
- };
465
- }
466
- const nextSession = {
467
- ...options.session,
468
- walletRootId: nextState.walletRootId,
469
- sourceStateRevision: nextState.stateRevision,
470
- };
471
- await saveUnlockSession(options.paths.walletUnlockSessionPath, nextSession, options.access);
472
145
  return {
473
146
  changed: true,
474
- session: nextSession,
475
147
  state: nextState,
476
148
  };
477
149
  }
@@ -1,6 +1,6 @@
1
1
  import { type WalletStateSaveAccess } from "./state/storage.js";
2
2
  import type { WalletRuntimePaths } from "./runtime.js";
3
- import type { UnlockSessionStateV1, WalletStateV1 } from "./types.js";
3
+ import type { WalletStateV1 } from "./types.js";
4
4
  export interface WalletDescriptorInfoRpc {
5
5
  getDescriptorInfo(descriptor: string): Promise<{
6
6
  descriptor: string;
@@ -30,13 +30,11 @@ export declare function persistWalletStateUpdate(options: {
30
30
  export declare function persistNormalizedWalletDescriptorStateIfNeeded(options: {
31
31
  state: WalletStateV1;
32
32
  access: WalletStateSaveAccess;
33
- session?: UnlockSessionStateV1 | null;
34
33
  paths: WalletRuntimePaths;
35
34
  nowUnixMs: number;
36
35
  replacePrimary?: boolean;
37
36
  rpc: WalletDescriptorInfoRpc;
38
37
  }): Promise<{
39
38
  changed: boolean;
40
- session: UnlockSessionStateV1 | null;
41
39
  state: WalletStateV1;
42
40
  }>;
@@ -1,6 +1,5 @@
1
1
  import { rm } from "node:fs/promises";
2
2
  import { deriveWalletMaterialFromMnemonic } from "./material.js";
3
- import { saveUnlockSession } from "./state/session.js";
4
3
  import { saveWalletState } from "./state/storage.js";
5
4
  export function stripDescriptorChecksum(descriptor) {
6
5
  return descriptor.replace(/#[A-Za-z0-9]+$/, "");
@@ -76,7 +75,6 @@ export async function persistNormalizedWalletDescriptorStateIfNeeded(options) {
76
75
  if (!normalized.changed) {
77
76
  return {
78
77
  changed: false,
79
- session: options.session ?? null,
80
78
  state: options.state,
81
79
  };
82
80
  }
@@ -87,22 +85,8 @@ export async function persistNormalizedWalletDescriptorStateIfNeeded(options) {
87
85
  nowUnixMs: options.nowUnixMs,
88
86
  replacePrimary: options.replacePrimary,
89
87
  });
90
- if (options.session === undefined || options.session === null) {
91
- return {
92
- changed: true,
93
- session: options.session ?? null,
94
- state: nextState,
95
- };
96
- }
97
- const nextSession = {
98
- ...options.session,
99
- walletRootId: nextState.walletRootId,
100
- sourceStateRevision: nextState.stateRevision,
101
- };
102
- await saveUnlockSession(options.paths.walletUnlockSessionPath, nextSession, options.access);
103
88
  return {
104
89
  changed: true,
105
- session: nextSession,
106
90
  state: nextState,
107
91
  };
108
92
  }