@cogcoin/client 0.5.6 → 0.5.8

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 (71) hide show
  1. package/README.md +11 -2
  2. package/dist/bitcoind/bootstrap/getblock-archive.d.ts +39 -0
  3. package/dist/bitcoind/bootstrap/getblock-archive.js +548 -0
  4. package/dist/bitcoind/bootstrap.d.ts +1 -0
  5. package/dist/bitcoind/bootstrap.js +1 -0
  6. package/dist/bitcoind/client/factory.js +92 -30
  7. package/dist/bitcoind/client/managed-client.d.ts +1 -1
  8. package/dist/bitcoind/client/managed-client.js +22 -2
  9. package/dist/bitcoind/client/sync-engine.js +7 -0
  10. package/dist/bitcoind/errors.js +18 -0
  11. package/dist/bitcoind/indexer-daemon-main.js +78 -0
  12. package/dist/bitcoind/indexer-daemon.d.ts +3 -1
  13. package/dist/bitcoind/indexer-daemon.js +13 -6
  14. package/dist/bitcoind/node.js +2 -0
  15. package/dist/bitcoind/progress/constants.d.ts +1 -0
  16. package/dist/bitcoind/progress/constants.js +1 -0
  17. package/dist/bitcoind/progress/controller.d.ts +22 -0
  18. package/dist/bitcoind/progress/controller.js +48 -23
  19. package/dist/bitcoind/progress/formatting.js +25 -0
  20. package/dist/bitcoind/progress/render-policy.d.ts +35 -0
  21. package/dist/bitcoind/progress/render-policy.js +81 -0
  22. package/dist/bitcoind/service-paths.js +2 -6
  23. package/dist/bitcoind/service.d.ts +5 -1
  24. package/dist/bitcoind/service.js +92 -54
  25. package/dist/bitcoind/testing.d.ts +2 -1
  26. package/dist/bitcoind/testing.js +2 -1
  27. package/dist/bitcoind/types.d.ts +35 -1
  28. package/dist/cli/commands/follow.js +2 -0
  29. package/dist/cli/commands/getblock-archive-restart.d.ts +5 -0
  30. package/dist/cli/commands/getblock-archive-restart.js +15 -0
  31. package/dist/cli/commands/mining-admin.js +4 -0
  32. package/dist/cli/commands/mining-read.js +8 -5
  33. package/dist/cli/commands/mining-runtime.js +4 -0
  34. package/dist/cli/commands/status.js +2 -0
  35. package/dist/cli/commands/sync.js +2 -0
  36. package/dist/cli/commands/wallet-admin.js +29 -3
  37. package/dist/cli/commands/wallet-mutation.js +57 -4
  38. package/dist/cli/commands/wallet-read.js +2 -0
  39. package/dist/cli/context.js +5 -3
  40. package/dist/cli/mutation-command-groups.d.ts +2 -1
  41. package/dist/cli/mutation-command-groups.js +5 -0
  42. package/dist/cli/mutation-json.d.ts +18 -2
  43. package/dist/cli/mutation-json.js +47 -0
  44. package/dist/cli/mutation-success.d.ts +1 -0
  45. package/dist/cli/mutation-success.js +2 -2
  46. package/dist/cli/output.js +97 -1
  47. package/dist/cli/parse.d.ts +1 -1
  48. package/dist/cli/parse.js +127 -3
  49. package/dist/cli/preview-json.d.ts +10 -1
  50. package/dist/cli/preview-json.js +30 -0
  51. package/dist/cli/prompt.js +1 -1
  52. package/dist/cli/runner.js +20 -0
  53. package/dist/cli/signals.js +1 -1
  54. package/dist/cli/types.d.ts +11 -4
  55. package/dist/cli/wallet-format.js +6 -0
  56. package/dist/wallet/lifecycle.d.ts +18 -1
  57. package/dist/wallet/lifecycle.js +170 -81
  58. package/dist/wallet/mining/visualizer.d.ts +11 -6
  59. package/dist/wallet/mining/visualizer.js +32 -15
  60. package/dist/wallet/reset.js +39 -27
  61. package/dist/wallet/runtime.d.ts +12 -1
  62. package/dist/wallet/runtime.js +53 -11
  63. package/dist/wallet/state/provider.d.ts +1 -0
  64. package/dist/wallet/state/provider.js +119 -3
  65. package/dist/wallet/state/seed-index.d.ts +43 -0
  66. package/dist/wallet/state/seed-index.js +151 -0
  67. package/dist/wallet/tx/anchor.d.ts +22 -0
  68. package/dist/wallet/tx/anchor.js +201 -8
  69. package/dist/wallet/tx/index.d.ts +1 -1
  70. package/dist/wallet/tx/index.js +1 -1
  71. package/package.json +1 -1
@@ -10,6 +10,7 @@ import { createDefaultWalletSecretProvider, } from "../state/provider.js";
10
10
  import { serializeDomainAnchor, serializeDomainTransfer, validateDomainName, } from "../cogop/index.js";
11
11
  import { openWalletReadContext } from "../read/index.js";
12
12
  import { assertWalletMutationContextReady, buildWalletMutationTransaction, isAlreadyAcceptedError, isBroadcastUnknownError, pauseMiningForWalletMutation, saveWalletStatePreservingUnlock, unlockTemporaryBuilderLocks, } from "./common.js";
13
+ import { confirmYesNo } from "./confirm.js";
13
14
  const ACTIVE_FAMILY_STATUSES = new Set([
14
15
  "draft",
15
16
  "broadcasting",
@@ -117,21 +118,62 @@ function findActiveAnchorFamilyByDomain(state, domainName) {
117
118
  function findAnchorFamilyById(state, familyId) {
118
119
  return state.proactiveFamilies.find((family) => family.familyId === familyId) ?? null;
119
120
  }
120
- function selectNextDedicatedIdentityTarget(state) {
121
+ function collectActivelyReservedDedicatedIndices(state) {
121
122
  const reservedIndices = new Set();
122
123
  for (const domain of state.domains) {
123
- if (domain.dedicatedIndex !== null) {
124
+ if (domain.dedicatedIndex !== null && domain.localAnchorIntent !== "none") {
124
125
  reservedIndices.add(domain.dedicatedIndex);
125
126
  }
126
127
  }
127
128
  for (const family of state.proactiveFamilies) {
128
- if (family.type === "anchor" && family.reservedDedicatedIndex !== null && family.reservedDedicatedIndex !== undefined) {
129
+ if (family.type === "anchor"
130
+ && ACTIVE_FAMILY_STATUSES.has(family.status)
131
+ && family.reservedDedicatedIndex !== null
132
+ && family.reservedDedicatedIndex !== undefined) {
129
133
  reservedIndices.add(family.reservedDedicatedIndex);
130
134
  }
131
135
  }
136
+ return reservedIndices;
137
+ }
138
+ function selectReusableDedicatedIdentityTarget(state) {
139
+ const reservedIndices = collectActivelyReservedDedicatedIndices(state);
140
+ const reusableIdentity = state.identities
141
+ .filter((identity) => identity.status === "dedicated"
142
+ && identity.address !== null
143
+ && identity.assignedDomainNames.length === 0
144
+ && !reservedIndices.has(identity.index))
145
+ .sort((left, right) => left.index - right.index)[0];
146
+ if (reusableIdentity == null) {
147
+ return null;
148
+ }
149
+ const material = deriveWalletIdentityMaterial(state.keys.accountXprv, reusableIdentity.index);
150
+ const reusableAddress = reusableIdentity.address;
151
+ if (reusableAddress === null) {
152
+ return null;
153
+ }
154
+ return {
155
+ ...material,
156
+ localIndex: reusableIdentity.index,
157
+ address: reusableAddress,
158
+ scriptPubKeyHex: reusableIdentity.scriptPubKeyHex,
159
+ };
160
+ }
161
+ function selectFreshDedicatedIdentityTarget(state) {
162
+ const unavailableIndices = new Set();
163
+ for (const identity of state.identities) {
164
+ unavailableIndices.add(identity.index);
165
+ }
166
+ for (const domain of state.domains) {
167
+ if (domain.dedicatedIndex !== null) {
168
+ unavailableIndices.add(domain.dedicatedIndex);
169
+ }
170
+ }
171
+ for (const index of collectActivelyReservedDedicatedIndices(state)) {
172
+ unavailableIndices.add(index);
173
+ }
132
174
  const startIndex = Math.max(1, state.nextDedicatedIndex);
133
175
  for (let index = startIndex; index <= state.descriptor.rangeEnd; index += 1) {
134
- if (reservedIndices.has(index)) {
176
+ if (unavailableIndices.has(index)) {
135
177
  continue;
136
178
  }
137
179
  const material = deriveWalletIdentityMaterial(state.keys.accountXprv, index);
@@ -142,6 +184,20 @@ function selectNextDedicatedIdentityTarget(state) {
142
184
  }
143
185
  throw new Error("wallet_anchor_no_fresh_dedicated_index");
144
186
  }
187
+ function selectNextDedicatedIdentityTarget(state) {
188
+ return selectReusableDedicatedIdentityTarget(state) ?? selectFreshDedicatedIdentityTarget(state);
189
+ }
190
+ function deriveAnchorTargetIdentityForIndex(state, localIndex) {
191
+ const existingIdentity = state.identities.find((identity) => identity.index === localIndex
192
+ && identity.address !== null) ?? null;
193
+ const material = deriveWalletIdentityMaterial(state.keys.accountXprv, localIndex);
194
+ return {
195
+ ...material,
196
+ localIndex,
197
+ address: existingIdentity?.address ?? material.address,
198
+ scriptPubKeyHex: existingIdentity?.scriptPubKeyHex ?? material.scriptPubKeyHex,
199
+ };
200
+ }
145
201
  function encodeFoundingMessage(foundingMessageText) {
146
202
  const trimmed = foundingMessageText?.trim() ?? "";
147
203
  if (trimmed === "") {
@@ -190,6 +246,17 @@ async function confirmAnchor(prompter, operation) {
190
246
  throw new Error("wallet_anchor_confirmation_rejected");
191
247
  }
192
248
  }
249
+ async function confirmAnchorClear(prompter, domainName, dedicatedIndex, assumeYes = false) {
250
+ const releaseLine = dedicatedIndex === null
251
+ ? "This will cancel the local pending anchor reservation."
252
+ : `This will cancel the local pending anchor reservation and release dedicated index ${dedicatedIndex} for reuse.`;
253
+ await confirmYesNo(prompter, releaseLine, {
254
+ assumeYes,
255
+ errorCode: "wallet_anchor_clear_confirmation_rejected",
256
+ requiresTtyErrorCode: "wallet_anchor_clear_requires_tty",
257
+ prompt: `Clear pending anchor for "${domainName}"? [y/N]: `,
258
+ });
259
+ }
193
260
  function resolveAnchorOperation(context, domainName, foundingMessageText, foundingMessagePayloadHex) {
194
261
  assertWalletMutationContextReady(context, "wallet_anchor");
195
262
  const chainDomain = lookupDomain(context.snapshot.state, domainName);
@@ -231,6 +298,41 @@ function resolveAnchorOperation(context, domainName, foundingMessageText, foundi
231
298
  hadListing: getListing(context.snapshot.state, chainDomain.domainId) !== null,
232
299
  };
233
300
  }
301
+ function releaseClearedAnchorReservationState(options) {
302
+ const family = findAnchorFamilyById(options.state, options.familyId);
303
+ const domains = options.state.domains.map((domain) => {
304
+ if (domain.name !== options.domainName) {
305
+ return domain;
306
+ }
307
+ return {
308
+ ...domain,
309
+ dedicatedIndex: null,
310
+ localAnchorIntent: "none",
311
+ };
312
+ });
313
+ const nextState = {
314
+ ...options.state,
315
+ domains,
316
+ };
317
+ if (family === null) {
318
+ return nextState;
319
+ }
320
+ return upsertProactiveFamily(nextState, {
321
+ ...family,
322
+ status: "canceled",
323
+ lastUpdatedAtUnixMs: options.nowUnixMs,
324
+ tx1: family.tx1 == null ? family.tx1 : {
325
+ ...family.tx1,
326
+ status: "canceled",
327
+ temporaryBuilderLockedOutpoints: [],
328
+ },
329
+ tx2: family.tx2 == null ? family.tx2 : {
330
+ ...family.tx2,
331
+ status: "canceled",
332
+ temporaryBuilderLockedOutpoints: [],
333
+ },
334
+ });
335
+ }
234
336
  function createFamilyTransactionRecord() {
235
337
  return {
236
338
  status: "draft",
@@ -1020,10 +1122,7 @@ export async function anchorDomain(options) {
1020
1122
  let workingState = operation.state;
1021
1123
  if (existingFamily !== null) {
1022
1124
  const existingReservedIndex = existingFamily.reservedDedicatedIndex ?? operation.targetIdentity.localIndex;
1023
- const existingTargetIdentity = {
1024
- ...deriveWalletIdentityMaterial(operation.state.keys.accountXprv, existingReservedIndex),
1025
- localIndex: existingReservedIndex,
1026
- };
1125
+ const existingTargetIdentity = deriveAnchorTargetIdentityForIndex(operation.state, existingReservedIndex);
1027
1126
  const reconciled = await reconcileAnchorFamily({
1028
1127
  state: operation.state,
1029
1128
  family: existingFamily,
@@ -1209,3 +1308,97 @@ export async function anchorDomain(options) {
1209
1308
  await controlLock.release();
1210
1309
  }
1211
1310
  }
1311
+ export async function clearPendingAnchor(options) {
1312
+ const provider = options.provider ?? createDefaultWalletSecretProvider();
1313
+ const nowUnixMs = options.nowUnixMs ?? Date.now();
1314
+ const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
1315
+ const controlLock = await acquireFileLock(paths.walletControlLockPath, {
1316
+ purpose: "wallet-anchor-clear",
1317
+ walletRootId: null,
1318
+ });
1319
+ const normalizedDomainName = normalizeDomainName(options.domainName);
1320
+ try {
1321
+ const miningPreemption = await pauseMiningForWalletMutation({
1322
+ paths,
1323
+ reason: "wallet-anchor-clear",
1324
+ });
1325
+ const readContext = await (options.openReadContext ?? openWalletReadContext)({
1326
+ dataDir: options.dataDir,
1327
+ databasePath: options.databasePath,
1328
+ secretProvider: provider,
1329
+ walletControlLockHeld: true,
1330
+ paths,
1331
+ });
1332
+ try {
1333
+ assertWalletMutationContextReady(readContext, "wallet_anchor_clear");
1334
+ const family = findActiveAnchorFamilyByDomain(readContext.localState.state, normalizedDomainName);
1335
+ const domain = readContext.localState.state.domains.find((entry) => entry.name === normalizedDomainName) ?? null;
1336
+ if (domain === null && family === null) {
1337
+ throw new Error("wallet_anchor_clear_domain_not_found");
1338
+ }
1339
+ if (family === null) {
1340
+ if (domain === null) {
1341
+ throw new Error("wallet_anchor_clear_domain_not_found");
1342
+ }
1343
+ if (domain.localAnchorIntent !== "none") {
1344
+ throw new Error("wallet_anchor_clear_inconsistent_state");
1345
+ }
1346
+ return {
1347
+ domainName: normalizedDomainName,
1348
+ cleared: false,
1349
+ previousFamilyStatus: null,
1350
+ previousFamilyStep: null,
1351
+ releasedDedicatedIndex: null,
1352
+ };
1353
+ }
1354
+ if (family.type !== "anchor") {
1355
+ throw new Error("wallet_anchor_clear_inconsistent_state");
1356
+ }
1357
+ if (family.status !== "draft" || family.currentStep !== "reserved") {
1358
+ throw new Error(`wallet_anchor_clear_not_clearable_${family.status}`);
1359
+ }
1360
+ const reservedDedicatedIndex = family.reservedDedicatedIndex ?? null;
1361
+ if (reservedDedicatedIndex === null
1362
+ || family.tx1?.attemptedTxid !== null
1363
+ || family.tx2?.attemptedTxid !== null
1364
+ || (domain !== null
1365
+ && (domain.localAnchorIntent !== "reserved"
1366
+ || domain.dedicatedIndex === null
1367
+ || domain.dedicatedIndex !== reservedDedicatedIndex))) {
1368
+ throw new Error("wallet_anchor_clear_inconsistent_state");
1369
+ }
1370
+ await confirmAnchorClear(options.prompter, normalizedDomainName, reservedDedicatedIndex, options.assumeYes ?? false);
1371
+ const releasedState = releaseClearedAnchorReservationState({
1372
+ state: readContext.localState.state,
1373
+ familyId: family.familyId,
1374
+ domainName: normalizedDomainName,
1375
+ nowUnixMs,
1376
+ });
1377
+ await saveWalletStatePreservingUnlock({
1378
+ state: {
1379
+ ...releasedState,
1380
+ stateRevision: releasedState.stateRevision + 1,
1381
+ lastWrittenAtUnixMs: nowUnixMs,
1382
+ },
1383
+ provider,
1384
+ unlockUntilUnixMs: readContext.localState.unlockUntilUnixMs,
1385
+ nowUnixMs,
1386
+ paths,
1387
+ });
1388
+ return {
1389
+ domainName: normalizedDomainName,
1390
+ cleared: true,
1391
+ previousFamilyStatus: family.status,
1392
+ previousFamilyStep: family.currentStep ?? null,
1393
+ releasedDedicatedIndex: reservedDedicatedIndex,
1394
+ };
1395
+ }
1396
+ finally {
1397
+ await readContext.close();
1398
+ await miningPreemption.release();
1399
+ }
1400
+ }
1401
+ finally {
1402
+ await controlLock.release();
1403
+ }
1404
+ }
@@ -1,7 +1,7 @@
1
1
  export { extractOpReturnPayloadFromScriptHex, registerDomain, type RegisterDomainResult, } from "./register.js";
2
2
  export { buyDomain, parseCogAmountToCogtoshi, sellDomain, transferDomain, type DomainMarketMutationResult, } from "./domain-market.js";
3
3
  export { claimCogLock, lockCogToDomain, reclaimCogLock, sendCog, type CogMutationResult, } from "./cog.js";
4
- export { anchorDomain, type AnchorDomainResult, } from "./anchor.js";
4
+ export { anchorDomain, clearPendingAnchor, type AnchorDomainResult, type ClearPendingAnchorResult, } from "./anchor.js";
5
5
  export { clearDomainDelegate, clearDomainEndpoint, clearDomainMiner, setDomainCanonical, setDomainDelegate, setDomainEndpoint, setDomainMiner, type DomainAdminMutationResult, } from "./domain-admin.js";
6
6
  export { clearField, createField, setField, type ClearFieldOptions, type CreateFieldOptions, type FieldMutationResult, type FieldValueInputSource, type SetFieldOptions, } from "./field.js";
7
7
  export { giveReputation, revokeReputation, type GiveReputationOptions, type ReputationMutationResult, type RevokeReputationOptions, } from "./reputation.js";
@@ -1,7 +1,7 @@
1
1
  export { extractOpReturnPayloadFromScriptHex, registerDomain, } from "./register.js";
2
2
  export { buyDomain, parseCogAmountToCogtoshi, sellDomain, transferDomain, } from "./domain-market.js";
3
3
  export { claimCogLock, lockCogToDomain, reclaimCogLock, sendCog, } from "./cog.js";
4
- export { anchorDomain, } from "./anchor.js";
4
+ export { anchorDomain, clearPendingAnchor, } from "./anchor.js";
5
5
  export { clearDomainDelegate, clearDomainEndpoint, clearDomainMiner, setDomainCanonical, setDomainDelegate, setDomainEndpoint, setDomainMiner, } from "./domain-admin.js";
6
6
  export { clearField, createField, setField, } from "./field.js";
7
7
  export { giveReputation, revokeReputation, } from "./reputation.js";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cogcoin/client",
3
- "version": "0.5.6",
3
+ "version": "0.5.8",
4
4
  "description": "Store-backed Cogcoin client with wallet flows, SQLite persistence, and managed Bitcoin Core integration.",
5
5
  "license": "MIT",
6
6
  "type": "module",