@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
@@ -8,7 +8,7 @@ import { resolveWalletRuntimePathsForTesting } from "../runtime.js";
8
8
  import { createDefaultWalletSecretProvider, } from "../state/provider.js";
9
9
  import { serializeRepCommit, serializeRepRevoke, validateDomainName, } from "../cogop/index.js";
10
10
  import { openWalletReadContext } from "../read/index.js";
11
- import { assertFixedInputPrefixMatches, assertFundingInputsAfterFixedPrefix, assertWalletMutationContextReady, buildWalletMutationTransactionWithReserveFallback, formatCogAmount, getDecodedInputScriptPubKeyHex, isAlreadyAcceptedError, isBroadcastUnknownError, outpointKey, pauseMiningForWalletMutation, saveWalletStatePreservingUnlock, unlockTemporaryBuilderLocks, updateMutationRecord, } from "./common.js";
11
+ import { assertFixedInputPrefixMatches, assertFundingInputsAfterFixedPrefix, assertWalletMutationContextReady, buildWalletMutationTransactionWithReserveFallback, createBuiltWalletMutationFeeSummary, createFundingMutationSender, createWalletMutationFeeMetadata, formatCogAmount, getDecodedInputScriptPubKeyHex, isLocalWalletScript, isAlreadyAcceptedError, isBroadcastUnknownError, mergeFixedWalletInputs, outpointKey, pauseMiningForWalletMutation, resolvePendingMutationReuseDecision, resolveWalletMutationFeeSelection, saveWalletStatePreservingUnlock, unlockTemporaryBuilderLocks, updateMutationRecord, } from "./common.js";
12
12
  import { confirmTypedAcknowledgement as confirmSharedTypedAcknowledgement, confirmYesNo as confirmSharedYesNo, } from "./confirm.js";
13
13
  import { getCanonicalIdentitySelector } from "./identity-selector.js";
14
14
  import { findPendingMutationByIntent, upsertPendingMutation } from "./journal.js";
@@ -93,17 +93,6 @@ function describeReputationReview(review) {
93
93
  }
94
94
  return `included (${review.byteLength} bytes)`;
95
95
  }
96
- function resolveAnchorOutpointForSender(state, sender, errorPrefix) {
97
- const anchoredDomain = state.domains.find((domain) => domain.currentOwnerLocalIndex === sender.index
98
- && domain.canonicalChainStatus === "anchored") ?? null;
99
- if (anchoredDomain?.currentCanonicalAnchorOutpoint === null || anchoredDomain === null) {
100
- throw new Error(`${errorPrefix}_anchor_outpoint_unavailable`);
101
- }
102
- return {
103
- txid: anchoredDomain.currentCanonicalAnchorOutpoint.txid,
104
- vout: anchoredDomain.currentCanonicalAnchorOutpoint.vout,
105
- };
106
- }
107
96
  function resolveReputationOperation(context, sourceDomainName, targetDomainName, errorPrefix) {
108
97
  assertWalletMutationContextReady(context, errorPrefix);
109
98
  const sourceDomain = lookupDomain(context.snapshot.state, sourceDomainName);
@@ -121,24 +110,14 @@ function resolveReputationOperation(context, sourceDomainName, targetDomainName,
121
110
  throw new Error(`${errorPrefix}_target_domain_not_anchored`);
122
111
  }
123
112
  const ownerHex = Buffer.from(sourceDomain.ownerScriptPubKey).toString("hex");
124
- const ownerIdentity = context.model.identities.find((identity) => identity.scriptPubKeyHex === ownerHex) ?? null;
125
- if (ownerIdentity === null || ownerIdentity.address === null) {
113
+ if (ownerHex !== context.localState.state.funding.scriptPubKeyHex || context.model.walletAddress == null) {
126
114
  throw new Error(`${errorPrefix}_source_owner_not_locally_controlled`);
127
115
  }
128
- if (ownerIdentity.readOnly) {
129
- throw new Error(`${errorPrefix}_source_owner_read_only`);
130
- }
131
116
  return {
132
117
  readContext: context,
133
118
  state: context.localState.state,
134
- unlockUntilUnixMs: context.localState.unlockUntilUnixMs,
135
- sender: {
136
- localIndex: ownerIdentity.index,
137
- scriptPubKeyHex: ownerIdentity.scriptPubKeyHex,
138
- address: ownerIdentity.address,
139
- },
140
- senderSelector: getCanonicalIdentitySelector(ownerIdentity),
141
- anchorOutpoint: resolveAnchorOutpointForSender(context.localState.state, ownerIdentity, errorPrefix),
119
+ sender: createFundingMutationSender(context.localState.state),
120
+ senderSelector: context.model.walletAddress,
142
121
  sourceDomain,
143
122
  targetDomain,
144
123
  availableBalanceCogtoshi: getBalance(context.snapshot.state, sourceDomain.ownerScriptPubKey),
@@ -150,29 +129,13 @@ function buildPlanForReputationOperation(options) {
150
129
  && entry.confirmations >= 1
151
130
  && entry.spendable !== false
152
131
  && entry.safe !== false);
153
- const anchorUtxo = options.allUtxos.find((entry) => entry.txid === options.anchorOutpoint.txid
154
- && entry.vout === options.anchorOutpoint.vout
155
- && entry.scriptPubKey === options.sender.scriptPubKeyHex
156
- && entry.confirmations >= 1
157
- && entry.spendable !== false
158
- && entry.safe !== false);
159
- if (anchorUtxo === undefined) {
160
- throw new Error(`${options.errorPrefix}_anchor_utxo_missing`);
161
- }
162
132
  return {
163
133
  sender: options.sender,
164
134
  changeAddress: options.state.funding.address,
165
- fixedInputs: [
166
- { txid: anchorUtxo.txid, vout: anchorUtxo.vout },
167
- ],
168
- outputs: [
169
- { data: Buffer.from(options.opReturnData).toString("hex") },
170
- { [options.sender.address]: satsToBtcNumber(BigInt(options.state.anchorValueSats)) },
171
- ],
172
- changePosition: 2,
135
+ fixedInputs: [],
136
+ outputs: [{ data: Buffer.from(options.opReturnData).toString("hex") }],
137
+ changePosition: 1,
173
138
  expectedOpReturnScriptHex: encodeOpReturnScript(options.opReturnData),
174
- expectedAnchorScriptHex: options.sender.scriptPubKeyHex,
175
- expectedAnchorValueSats: BigInt(options.state.anchorValueSats),
176
139
  allowedFundingScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
177
140
  eligibleFundingOutpointKeys: new Set(fundingUtxos.map((entry) => outpointKey({ txid: entry.txid, vout: entry.vout }))),
178
141
  errorPrefix: options.errorPrefix,
@@ -184,33 +147,16 @@ function validateFundedDraft(decoded, funded, plan) {
184
147
  if (inputs.length === 0) {
185
148
  throw new Error(`${plan.errorPrefix}_missing_sender_input`);
186
149
  }
187
- assertFixedInputPrefixMatches(inputs, plan.fixedInputs, `${plan.errorPrefix}_sender_input_mismatch`);
188
- if (getDecodedInputScriptPubKeyHex(decoded, 0) !== plan.sender.scriptPubKeyHex) {
189
- throw new Error(`${plan.errorPrefix}_sender_input_mismatch`);
190
- }
191
- assertFundingInputsAfterFixedPrefix({
192
- decoded,
193
- fixedInputs: plan.fixedInputs,
194
- allowedFundingScriptPubKeyHex: plan.allowedFundingScriptPubKeyHex,
195
- eligibleFundingOutpointKeys: plan.eligibleFundingOutpointKeys,
196
- errorCode: `${plan.errorPrefix}_unexpected_funding_input`,
197
- });
198
150
  if (outputs[0]?.scriptPubKey?.hex !== plan.expectedOpReturnScriptHex) {
199
151
  throw new Error(`${plan.errorPrefix}_opreturn_mismatch`);
200
152
  }
201
- if (outputs[1]?.scriptPubKey?.hex !== plan.expectedAnchorScriptHex) {
202
- throw new Error(`${plan.errorPrefix}_anchor_output_mismatch`);
203
- }
204
- if (valueToSats(outputs[1]?.value ?? 0) !== plan.expectedAnchorValueSats) {
205
- throw new Error(`${plan.errorPrefix}_anchor_value_mismatch`);
206
- }
207
153
  if (funded.changepos === -1) {
208
- if (outputs.length !== 2) {
154
+ if (outputs.length !== 1) {
209
155
  throw new Error(`${plan.errorPrefix}_unexpected_output_count`);
210
156
  }
211
157
  return;
212
158
  }
213
- if (funded.changepos !== plan.changePosition || outputs.length !== 3) {
159
+ if (funded.changepos !== plan.changePosition || outputs.length !== 2) {
214
160
  throw new Error(`${plan.errorPrefix}_change_position_mismatch`);
215
161
  }
216
162
  if (outputs[funded.changepos]?.scriptPubKey?.hex !== plan.allowedFundingScriptPubKeyHex) {
@@ -226,7 +172,7 @@ async function buildTransaction(options) {
226
172
  validateFundedDraft,
227
173
  finalizeErrorCode: `${options.plan.errorPrefix}_finalize_failed`,
228
174
  mempoolRejectPrefix: `${options.plan.errorPrefix}_mempool_rejected`,
229
- reserveCandidates: options.state.proactiveReserveOutpoints,
175
+ feeRate: options.feeRateSatVb,
230
176
  });
231
177
  }
232
178
  function createDraftMutation(options) {
@@ -244,6 +190,7 @@ function createDraftMutation(options) {
244
190
  lastUpdatedAtUnixMs: options.nowUnixMs,
245
191
  attemptedTxid: null,
246
192
  attemptedWtxid: null,
193
+ ...createWalletMutationFeeMetadata(options.feeSelection),
247
194
  temporaryBuilderLockedOutpoints: [],
248
195
  };
249
196
  }
@@ -263,6 +210,7 @@ function createDraftMutation(options) {
263
210
  lastUpdatedAtUnixMs: options.nowUnixMs,
264
211
  attemptedTxid: null,
265
212
  attemptedWtxid: null,
213
+ ...createWalletMutationFeeMetadata(options.feeSelection),
266
214
  temporaryBuilderLockedOutpoints: [],
267
215
  };
268
216
  }
@@ -275,7 +223,6 @@ async function saveUpdatedMutationState(options) {
275
223
  await saveWalletStatePreservingUnlock({
276
224
  state: nextState,
277
225
  provider: options.provider,
278
- unlockUntilUnixMs: options.unlockUntilUnixMs,
279
226
  nowUnixMs: options.nowUnixMs,
280
227
  paths: options.paths,
281
228
  });
@@ -322,7 +269,6 @@ async function reconcilePendingReputationMutation(options) {
322
269
  nextState = await saveUpdatedMutationState({
323
270
  state: nextState,
324
271
  provider: options.provider,
325
- unlockUntilUnixMs: options.unlockUntilUnixMs,
326
272
  nowUnixMs: options.nowUnixMs,
327
273
  paths: options.paths,
328
274
  });
@@ -341,7 +287,6 @@ async function reconcilePendingReputationMutation(options) {
341
287
  nextState = await saveUpdatedMutationState({
342
288
  state: nextState,
343
289
  provider: options.provider,
344
- unlockUntilUnixMs: options.unlockUntilUnixMs,
345
290
  nowUnixMs: options.nowUnixMs,
346
291
  paths: options.paths,
347
292
  });
@@ -358,7 +303,6 @@ async function reconcilePendingReputationMutation(options) {
358
303
  nextState = await saveUpdatedMutationState({
359
304
  state: nextState,
360
305
  provider: options.provider,
361
- unlockUntilUnixMs: options.unlockUntilUnixMs,
362
306
  nowUnixMs: options.nowUnixMs,
363
307
  paths: options.paths,
364
308
  });
@@ -448,7 +392,6 @@ async function sendBuiltTransaction(options) {
448
392
  nextState = await saveUpdatedMutationState({
449
393
  state: nextState,
450
394
  provider: options.provider,
451
- unlockUntilUnixMs: options.unlockUntilUnixMs,
452
395
  nowUnixMs: options.nowUnixMs,
453
396
  paths: options.paths,
454
397
  });
@@ -471,7 +414,6 @@ async function sendBuiltTransaction(options) {
471
414
  nextState = await saveUpdatedMutationState({
472
415
  state: nextState,
473
416
  provider: options.provider,
474
- unlockUntilUnixMs: options.unlockUntilUnixMs,
475
417
  nowUnixMs: options.nowUnixMs,
476
418
  paths: options.paths,
477
419
  });
@@ -487,7 +429,6 @@ async function sendBuiltTransaction(options) {
487
429
  nextState = await saveUpdatedMutationState({
488
430
  state: nextState,
489
431
  provider: options.provider,
490
- unlockUntilUnixMs: options.unlockUntilUnixMs,
491
432
  nowUnixMs: options.nowUnixMs,
492
433
  paths: options.paths,
493
434
  });
@@ -504,7 +445,6 @@ async function sendBuiltTransaction(options) {
504
445
  nextState = await saveUpdatedMutationState({
505
446
  state: nextState,
506
447
  provider: options.provider,
507
- unlockUntilUnixMs: options.unlockUntilUnixMs,
508
448
  nowUnixMs: options.nowUnixMs,
509
449
  paths: options.paths,
510
450
  });
@@ -577,31 +517,47 @@ async function submitReputationMutation(options) {
577
517
  });
578
518
  const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
579
519
  const walletName = operation.state.managedCoreWallet.walletName;
520
+ const feeSelection = await resolveWalletMutationFeeSelection({
521
+ rpc,
522
+ feeRateSatVb: options.feeRateSatVb ?? null,
523
+ });
580
524
  const existingMutation = findPendingMutationByIntent(operation.state, intentFingerprintHex);
525
+ let workingState = operation.state;
526
+ let replacementFixedInputs = null;
581
527
  if (existingMutation !== null) {
582
528
  const reconciled = await reconcilePendingReputationMutation({
583
529
  state: operation.state,
584
530
  mutation: existingMutation,
585
531
  provider,
586
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
587
532
  nowUnixMs,
588
533
  paths,
589
534
  rpc,
590
535
  walletName,
591
536
  context: readContext,
592
537
  });
538
+ workingState = reconciled.state;
593
539
  if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
594
- return {
595
- kind: options.kind === "rep-give" ? "give" : "revoke",
596
- sourceDomainName: normalizedSourceDomainName,
597
- targetDomainName: normalizedTargetDomainName,
598
- amountCogtoshi: options.amountCogtoshi,
599
- txid: reconciled.mutation.attemptedTxid ?? "unknown",
600
- status: reconciled.resolution,
601
- reusedExisting: true,
602
- reviewIncluded: review.payloadHex !== null,
603
- resolved,
604
- };
540
+ const reuse = await resolvePendingMutationReuseDecision({
541
+ rpc,
542
+ walletName,
543
+ mutation: reconciled.mutation,
544
+ nextFeeSelection: feeSelection,
545
+ });
546
+ if (reuse.reuseExisting) {
547
+ return {
548
+ kind: options.kind === "rep-give" ? "give" : "revoke",
549
+ sourceDomainName: normalizedSourceDomainName,
550
+ targetDomainName: normalizedTargetDomainName,
551
+ amountCogtoshi: options.amountCogtoshi,
552
+ txid: reconciled.mutation.attemptedTxid ?? "unknown",
553
+ status: reconciled.resolution,
554
+ reusedExisting: true,
555
+ reviewIncluded: review.payloadHex !== null,
556
+ resolved,
557
+ fees: reuse.fees,
558
+ };
559
+ }
560
+ replacementFixedInputs = reuse.replacementFixedInputs;
605
561
  }
606
562
  if (reconciled.resolution === "repair-required") {
607
563
  throw new Error(`${options.errorPrefix}_repair_required`);
@@ -616,7 +572,7 @@ async function submitReputationMutation(options) {
616
572
  resolved,
617
573
  assumeYes: options.assumeYes,
618
574
  });
619
- let nextState = upsertPendingMutation(operation.state, createDraftMutation({
575
+ let nextState = upsertPendingMutation(workingState, createDraftMutation({
620
576
  kind: options.kind,
621
577
  sourceDomainName: normalizedSourceDomainName,
622
578
  targetDomainName: normalizedTargetDomainName,
@@ -625,30 +581,34 @@ async function submitReputationMutation(options) {
625
581
  intentFingerprintHex,
626
582
  nowUnixMs,
627
583
  reviewPayloadHex: review.payloadHex,
584
+ feeSelection,
628
585
  existing: existingMutation,
629
586
  }));
630
587
  nextState = await saveUpdatedMutationState({
631
588
  state: nextState,
632
589
  provider,
633
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
634
590
  nowUnixMs,
635
591
  paths,
636
592
  });
637
593
  const opReturnData = options.kind === "rep-give"
638
594
  ? serializeRepCommit(operation.sourceDomain.domainId, operation.targetDomain.domainId, options.amountCogtoshi, review.payload).opReturnData
639
595
  : serializeRepRevoke(operation.sourceDomain.domainId, operation.targetDomain.domainId, options.amountCogtoshi, review.payload).opReturnData;
596
+ const reputationPlan = buildPlanForReputationOperation({
597
+ state: nextState,
598
+ allUtxos: await rpc.listUnspent(walletName, 1),
599
+ sender: operation.sender,
600
+ opReturnData,
601
+ errorPrefix: options.errorPrefix,
602
+ });
640
603
  const built = await buildTransaction({
641
604
  rpc,
642
605
  walletName,
643
606
  state: nextState,
644
- plan: buildPlanForReputationOperation({
645
- state: nextState,
646
- allUtxos: await rpc.listUnspent(walletName, 1),
647
- sender: operation.sender,
648
- anchorOutpoint: operation.anchorOutpoint,
649
- opReturnData,
650
- errorPrefix: options.errorPrefix,
651
- }),
607
+ plan: {
608
+ ...reputationPlan,
609
+ fixedInputs: mergeFixedWalletInputs(reputationPlan.fixedInputs, replacementFixedInputs),
610
+ },
611
+ feeRateSatVb: feeSelection.feeRateSatVb,
652
612
  });
653
613
  const final = await sendBuiltTransaction({
654
614
  rpc,
@@ -658,7 +618,6 @@ async function submitReputationMutation(options) {
658
618
  mutation: nextState.pendingMutations.find((mutation) => mutation.intentFingerprintHex === intentFingerprintHex),
659
619
  state: nextState,
660
620
  provider,
661
- unlockUntilUnixMs: operation.unlockUntilUnixMs,
662
621
  nowUnixMs,
663
622
  paths,
664
623
  errorPrefix: options.errorPrefix,
@@ -673,6 +632,10 @@ async function submitReputationMutation(options) {
673
632
  reusedExisting: false,
674
633
  reviewIncluded: review.payloadHex !== null,
675
634
  resolved,
635
+ fees: createBuiltWalletMutationFeeSummary({
636
+ selection: feeSelection,
637
+ built,
638
+ }),
676
639
  };
677
640
  }
678
641
  finally {
@@ -8,7 +8,7 @@ export interface OutpointRecord {
8
8
  export type PendingMutationStatus = "draft" | "broadcasting" | "broadcast-unknown" | "live" | "confirmed" | "canceled" | "repair-required";
9
9
  export interface PendingMutationRecord {
10
10
  mutationId: string;
11
- kind: "register" | "transfer" | "sell" | "buy" | "rep-give" | "rep-revoke" | "send" | "lock" | "claim" | "field-create" | "field-set" | "field-clear" | "endpoint" | "delegate" | "miner" | "canonical";
11
+ kind: "anchor" | "register" | "transfer" | "sell" | "buy" | "rep-give" | "rep-revoke" | "send" | "lock" | "claim" | "field-create" | "field-set" | "field-clear" | "endpoint" | "delegate" | "miner" | "canonical";
12
12
  registerKind?: "root" | "subdomain";
13
13
  domainName: string;
14
14
  parentDomainName: string | null;
@@ -35,6 +35,8 @@ export interface PendingMutationRecord {
35
35
  lastUpdatedAtUnixMs: number;
36
36
  attemptedTxid: string | null;
37
37
  attemptedWtxid: string | null;
38
+ selectedFeeRateSatVb?: number | null;
39
+ feeSelectionSource?: "custom-satvb" | "estimated-next-block-plus-one" | "fallback-default" | null;
38
40
  temporaryBuilderLockedOutpoints: OutpointRecord[];
39
41
  }
40
42
  export interface LocalIdentityRecord {
@@ -47,30 +49,11 @@ export interface LocalIdentityRecord {
47
49
  export interface DomainRecord {
48
50
  name: string;
49
51
  domainId: number | null;
50
- dedicatedIndex: number | null;
51
52
  currentOwnerScriptPubKeyHex: ScriptPubKeyHex | null;
52
- currentOwnerLocalIndex: number | null;
53
53
  canonicalChainStatus: "unknown" | "registered-unanchored" | "anchored";
54
- localAnchorIntent: "none" | "reserved" | "tx1-live" | "tx2-live" | "repair-required";
55
- currentCanonicalAnchorOutpoint: {
56
- txid: string;
57
- vout: number;
58
- valueSats: number;
59
- } | null;
60
54
  foundingMessageText: string | null;
61
55
  birthTime: number | null;
62
56
  }
63
- export interface HookClientStateRecord {
64
- mode: "builtin" | "custom" | "disabled";
65
- validationState: "unknown" | "validated" | "stale" | "failed" | "never" | "current";
66
- lastValidationAtUnixMs: number | null;
67
- lastValidationError: string | null;
68
- validatedLaunchFingerprint: string | null;
69
- validatedFullFingerprint: string | null;
70
- fullTrustWarningAcknowledgedAtUnixMs: number | null;
71
- consecutiveFailureCount: number;
72
- cooldownUntilUnixMs: number | null;
73
- }
74
57
  export interface MiningStateRecord {
75
58
  runMode: "stopped" | "foreground" | "background";
76
59
  state: "idle" | "live" | "paused" | "paused-stale" | "repair-required";
@@ -92,7 +75,7 @@ export interface MiningStateRecord {
92
75
  currentBlockTargetHeight: number | null;
93
76
  currentReferencedBlockHashDisplay: string | null;
94
77
  currentIntentFingerprintHex: string | null;
95
- liveMiningFamilyInMempool: boolean | null;
78
+ livePublishInMempool: boolean | null;
96
79
  currentPublishDecision: string | null;
97
80
  replacementCount: number;
98
81
  currentBlockFeeSpentSats: string;
@@ -103,49 +86,13 @@ export interface MiningStateRecord {
103
86
  vout: number;
104
87
  } | null;
105
88
  }
106
- export interface ProactiveFamilyTransactionRecord {
107
- status: "draft" | "broadcasting" | "broadcast-unknown" | "live" | "confirmed" | "canceled" | "repair-required";
108
- attemptedTxid: string | null;
109
- attemptedWtxid: string | null;
110
- temporaryBuilderLockedOutpoints: OutpointRecord[];
111
- rawHex: string | null;
112
- }
113
- export interface ProactiveFamilyStateRecord {
114
- familyId: string;
115
- type: "anchor" | "field" | string;
116
- status: "draft" | "broadcasting" | "broadcast-unknown" | "live" | "confirmed" | "canceled" | "repair-required";
117
- intentFingerprintHex: string;
118
- createdAtUnixMs: number;
119
- lastUpdatedAtUnixMs?: number;
120
- domainName?: string | null;
121
- domainId?: number | null;
122
- sourceSenderLocalIndex?: number | null;
123
- sourceSenderScriptPubKeyHex?: ScriptPubKeyHex | null;
124
- reservedDedicatedIndex?: number | null;
125
- reservedScriptPubKeyHex?: ScriptPubKeyHex | null;
126
- foundingMessageText?: string | null;
127
- foundingMessagePayloadHex?: string | null;
128
- listingCancelCommitted?: boolean;
129
- fieldName?: string | null;
130
- expectedFieldId?: number | null;
131
- fieldPermanent?: boolean | null;
132
- fieldFormat?: number | null;
133
- fieldValueHex?: string | null;
134
- currentStep?: "reserved" | "tx1" | "tx2" | null;
135
- tx1?: ProactiveFamilyTransactionRecord | null;
136
- tx2?: ProactiveFamilyTransactionRecord | null;
137
- }
138
89
  export interface WalletStateV1 {
139
- schemaVersion: 1;
90
+ schemaVersion: 5;
140
91
  stateRevision: number;
141
92
  lastWrittenAtUnixMs: number;
142
93
  walletRootId: string;
143
94
  network: WalletNetwork;
144
- anchorValueSats: number;
145
- proactiveReserveSats: number;
146
- proactiveReserveOutpoints: OutpointRecord[];
147
- nextDedicatedIndex: number;
148
- fundingIndex: 0;
95
+ localScriptPubKeyHexes?: ScriptPubKeyHex[];
149
96
  mnemonic: {
150
97
  phrase: string;
151
98
  language: WalletMnemonicLanguage;
@@ -172,69 +119,22 @@ export interface WalletStateV1 {
172
119
  walletName: string;
173
120
  internalPassphrase: string;
174
121
  descriptorChecksum: string | null;
175
- fundingAddress0: string | null;
176
- fundingScriptPubKeyHex0: ScriptPubKeyHex | null;
122
+ walletAddress?: string | null;
123
+ walletScriptPubKeyHex?: ScriptPubKeyHex | null;
177
124
  proofStatus: "not-proven" | "ready" | "missing" | "mismatch";
178
125
  lastImportedAtUnixMs: number | null;
179
126
  lastVerifiedAtUnixMs: number | null;
180
127
  };
181
- identities: LocalIdentityRecord[];
182
128
  domains: DomainRecord[];
183
129
  miningState: MiningStateRecord;
184
- hookClientState: {
185
- mining: HookClientStateRecord;
186
- };
187
- proactiveFamilies: ProactiveFamilyStateRecord[];
188
130
  pendingMutations?: PendingMutationRecord[];
189
131
  }
190
- export interface PortableWalletArchivePayloadV1 {
191
- schemaVersion: 1;
192
- exportedAtUnixMs: number;
193
- walletRootId: string;
194
- network: WalletNetwork;
195
- anchorValueSats: number;
196
- proactiveReserveSats: number;
197
- proactiveReserveOutpoints: OutpointRecord[];
198
- nextDedicatedIndex: number;
199
- fundingIndex: 0;
200
- mnemonic: {
201
- phrase: string;
202
- language: WalletMnemonicLanguage;
203
- };
204
- expected: {
205
- masterFingerprintHex: string;
206
- accountPath: string;
207
- accountXpub: string;
208
- publicExternalDescriptor: string;
209
- descriptorChecksum: string | null;
210
- rangeEnd: number;
211
- safetyMargin: number;
212
- fundingAddress0: string;
213
- fundingScriptPubKeyHex0: ScriptPubKeyHex;
214
- walletBirthTime: number;
215
- };
216
- identities: LocalIdentityRecord[];
217
- domains: DomainRecord[];
218
- miningState: MiningStateRecord;
219
- hookClientState: {
220
- mining: HookClientStateRecord;
221
- };
222
- proactiveFamilies: ProactiveFamilyStateRecord[];
223
- }
224
- export interface Argon2EnvelopeParams {
225
- name: "argon2id";
226
- memoryKib: number;
227
- iterations: number;
228
- parallelism: number;
229
- salt: string;
230
- }
231
132
  export interface EncryptedEnvelopeV1 {
232
133
  format: string;
233
134
  version: 1;
234
135
  cipher: "aes-256-gcm";
235
136
  wrappedBy: string;
236
137
  walletRootIdHint?: string | null;
237
- argon2id?: Argon2EnvelopeParams | null;
238
138
  secretProvider?: {
239
139
  kind: string;
240
140
  keyId: string;
@@ -243,20 +143,6 @@ export interface EncryptedEnvelopeV1 {
243
143
  tag: string;
244
144
  ciphertext: string;
245
145
  }
246
- export interface UnlockSessionStateV1 {
247
- schemaVersion: 1;
248
- walletRootId: string;
249
- sessionId: string;
250
- createdAtUnixMs: number;
251
- unlockUntilUnixMs: number;
252
- sourceStateRevision: number;
253
- wrappedSessionKeyMaterial: string;
254
- }
255
- export interface WalletExplicitLockStateV1 {
256
- schemaVersion: 1;
257
- walletRootId: string;
258
- lockedAtUnixMs: number;
259
- }
260
146
  export interface WalletPendingInitializationStateV1 {
261
147
  schemaVersion: 1;
262
148
  createdAtUnixMs: number;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cogcoin/client",
3
- "version": "0.5.15",
3
+ "version": "1.0.0",
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",
@@ -56,21 +56,21 @@
56
56
  "dist"
57
57
  ],
58
58
  "scripts": {
59
- "build": "rm -rf dist && node ./node_modules/typescript/bin/tsc -p tsconfig.json && node -e \"import('node:fs/promises').then(async (fs) => { await fs.mkdir('dist/art', { recursive: true }); await Promise.all([fs.copyFile('src/writing_quotes.json', 'dist/writing_quotes.json'), fs.copyFile('src/art/banner.txt', 'dist/art/banner.txt'), fs.copyFile('src/art/scroll.txt', 'dist/art/scroll.txt'), fs.copyFile('src/art/train-smoke.txt', 'dist/art/train-smoke.txt'), fs.copyFile('src/art/train.txt', 'dist/art/train.txt'), fs.copyFile('src/art/train-car.txt', 'dist/art/train-car.txt'), fs.copyFile('src/art/wallet.txt', 'dist/art/wallet.txt')]); })\"",
59
+ "build": "rm -rf dist && node ./node_modules/typescript/bin/tsc -p tsconfig.json && node ./scripts/copy-static-assets.mjs build",
60
60
  "generate:default-snapshot-chunk-manifest": "node scripts/generate-default-snapshot-chunk-manifest.mjs",
61
61
  "verify:default-snapshot-chunk-manifest": "node scripts/generate-default-snapshot-chunk-manifest.mjs --check",
62
- "test": "rm -rf .test-dist && node ./node_modules/typescript/bin/tsc -p tsconfig.test.json && node -e \"import('node:fs/promises').then(async (fs) => { await fs.mkdir('.test-dist/src/art', { recursive: true }); await Promise.all([fs.copyFile('src/writing_quotes.json', '.test-dist/src/writing_quotes.json'), fs.copyFile('src/art/banner.txt', '.test-dist/src/art/banner.txt'), fs.copyFile('src/art/scroll.txt', '.test-dist/src/art/scroll.txt'), fs.copyFile('src/art/train-smoke.txt', '.test-dist/src/art/train-smoke.txt'), fs.copyFile('src/art/train.txt', '.test-dist/src/art/train.txt'), fs.copyFile('src/art/train-car.txt', '.test-dist/src/art/train-car.txt'), fs.copyFile('src/art/wallet.txt', '.test-dist/src/art/wallet.txt')]); })\" && node --test .test-dist/test/*.test.js"
62
+ "test": "rm -rf .test-dist && node ./node_modules/typescript/bin/tsc -p tsconfig.test.json && node ./scripts/copy-static-assets.mjs test && node --test .test-dist/test/*.test.js"
63
63
  },
64
64
  "dependencies": {
65
65
  "@cogcoin/bitcoin": "30.2.0",
66
66
  "@cogcoin/genesis": "1.0.0",
67
- "@cogcoin/indexer": "1.0.0",
67
+ "@cogcoin/indexer": "1.0.1",
68
68
  "@cogcoin/scoring": "1.0.0",
69
+ "@noble/hashes": "2.0.1",
69
70
  "@scure/base": "^2.0.0",
70
71
  "@scure/bip32": "^2.0.1",
71
72
  "@scure/bip39": "^2.0.1",
72
73
  "better-sqlite3": "12.8.0",
73
- "hash-wasm": "^4.12.0",
74
74
  "zeromq": "6.5.0"
75
75
  },
76
76
  "devDependencies": {
@@ -1,4 +0,0 @@
1
- import type { PortableWalletArchivePayloadV1 } from "./types.js";
2
- export declare const PORTABLE_WALLET_ARCHIVE_FORMAT = "cogcoin-portable-wallet-archive";
3
- export declare function writePortableWalletArchive(path: string, payload: PortableWalletArchivePayloadV1, passphrase: Uint8Array | string): Promise<void>;
4
- export declare function readPortableWalletArchive(path: string, passphrase: Uint8Array | string): Promise<PortableWalletArchivePayloadV1>;
@@ -1,41 +0,0 @@
1
- import { readFile } from "node:fs/promises";
2
- import { writeJsonFileAtomic } from "./fs/atomic.js";
3
- import { normalizePortableWalletArchivePayload } from "./coin-control.js";
4
- import { decryptJsonWithPassphrase, encryptJsonWithPassphrase, } from "./state/crypto.js";
5
- export const PORTABLE_WALLET_ARCHIVE_FORMAT = "cogcoin-portable-wallet-archive";
6
- function assertPortableWalletArchivePayload(payload) {
7
- const normalized = normalizePortableWalletArchivePayload(payload);
8
- if (normalized.schemaVersion !== 1
9
- || normalized.walletRootId.trim() === ""
10
- || normalized.mnemonic.phrase.trim() === ""
11
- || normalized.expected.accountPath.trim() === ""
12
- || normalized.expected.publicExternalDescriptor.trim() === ""
13
- || normalized.expected.fundingAddress0.trim() === ""
14
- || normalized.expected.fundingScriptPubKeyHex0.trim() === "") {
15
- throw new Error("wallet_archive_payload_invalid");
16
- }
17
- return normalized;
18
- }
19
- export async function writePortableWalletArchive(path, payload, passphrase) {
20
- const envelope = await encryptJsonWithPassphrase(assertPortableWalletArchivePayload(payload), passphrase, {
21
- format: PORTABLE_WALLET_ARCHIVE_FORMAT,
22
- });
23
- await writeJsonFileAtomic(path, envelope, { mode: 0o600 });
24
- }
25
- export async function readPortableWalletArchive(path, passphrase) {
26
- let raw;
27
- try {
28
- raw = await readFile(path, "utf8");
29
- }
30
- catch (error) {
31
- if (error instanceof Error && "code" in error && error.code === "ENOENT") {
32
- throw new Error("wallet_import_archive_not_found");
33
- }
34
- throw error;
35
- }
36
- const envelope = JSON.parse(raw);
37
- if (envelope.format !== PORTABLE_WALLET_ARCHIVE_FORMAT || envelope.version !== 1) {
38
- throw new Error("wallet_archive_format_invalid");
39
- }
40
- return assertPortableWalletArchivePayload(await decryptJsonWithPassphrase(envelope, passphrase));
41
- }