@cogcoin/client 0.5.14 → 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.
- package/README.md +80 -25
- package/dist/app-paths.d.ts +5 -6
- package/dist/app-paths.js +8 -16
- package/dist/art/balance.txt +10 -0
- package/dist/art/welcome.txt +16 -0
- package/dist/bitcoind/bootstrap/controller.d.ts +1 -0
- package/dist/bitcoind/bootstrap/controller.js +53 -1
- package/dist/bitcoind/client/follow-block-times.d.ts +1 -0
- package/dist/bitcoind/client/follow-block-times.js +1 -1
- package/dist/bitcoind/client/internal-types.d.ts +7 -3
- package/dist/bitcoind/client/managed-client.d.ts +4 -2
- package/dist/bitcoind/client/managed-client.js +14 -0
- package/dist/bitcoind/client/sync-engine.js +72 -11
- package/dist/bitcoind/hash-order.d.ts +4 -0
- package/dist/bitcoind/hash-order.js +13 -0
- package/dist/bitcoind/indexer-daemon-main.js +11 -3
- package/dist/bitcoind/normalize.js +3 -2
- package/dist/bitcoind/processing-start-height.d.ts +5 -0
- package/dist/bitcoind/processing-start-height.js +7 -0
- package/dist/bitcoind/progress/constants.d.ts +4 -0
- package/dist/bitcoind/progress/constants.js +4 -0
- package/dist/bitcoind/progress/controller.d.ts +2 -1
- package/dist/bitcoind/progress/controller.js +3 -3
- package/dist/bitcoind/progress/follow-scene.d.ts +6 -2
- package/dist/bitcoind/progress/follow-scene.js +29 -6
- package/dist/bitcoind/progress/formatting.d.ts +1 -0
- package/dist/bitcoind/progress/formatting.js +6 -0
- package/dist/bitcoind/progress/train-scene.js +37 -18
- package/dist/bitcoind/progress/tty-renderer.d.ts +6 -1
- package/dist/bitcoind/progress/tty-renderer.js +8 -4
- package/dist/bitcoind/rpc.d.ts +2 -1
- package/dist/bitcoind/rpc.js +3 -0
- package/dist/bitcoind/types.d.ts +16 -0
- package/dist/bytes.d.ts +1 -0
- package/dist/bytes.js +3 -0
- package/dist/cli/art.d.ts +2 -0
- package/dist/cli/art.js +37 -0
- package/dist/cli/commands/client-admin.d.ts +2 -0
- package/dist/cli/commands/client-admin.js +91 -0
- package/dist/cli/commands/follow.js +0 -2
- package/dist/cli/commands/mining-admin.js +6 -47
- package/dist/cli/commands/mining-read.js +11 -50
- package/dist/cli/commands/mining-runtime.js +38 -3
- package/dist/cli/commands/service-runtime.js +0 -2
- package/dist/cli/commands/status.js +8 -2
- package/dist/cli/commands/sync.js +51 -4
- package/dist/cli/commands/wallet-admin.js +142 -136
- package/dist/cli/commands/wallet-mutation.js +91 -79
- package/dist/cli/commands/wallet-read.js +15 -18
- package/dist/cli/context.js +4 -14
- package/dist/cli/mining-format.d.ts +0 -1
- package/dist/cli/mining-format.js +5 -37
- package/dist/cli/mining-json.d.ts +0 -18
- package/dist/cli/mining-json.js +0 -35
- package/dist/cli/mutation-command-groups.d.ts +1 -2
- package/dist/cli/mutation-command-groups.js +0 -5
- package/dist/cli/mutation-json.d.ts +24 -145
- package/dist/cli/mutation-json.js +30 -136
- package/dist/cli/mutation-resolved-json.d.ts +0 -7
- package/dist/cli/mutation-resolved-json.js +4 -10
- package/dist/cli/mutation-success.d.ts +2 -0
- package/dist/cli/mutation-success.js +11 -1
- package/dist/cli/mutation-text-format.js +1 -3
- package/dist/cli/output.d.ts +1 -1
- package/dist/cli/output.js +254 -231
- package/dist/cli/parse.d.ts +1 -1
- package/dist/cli/parse.js +93 -122
- package/dist/cli/preview-json.d.ts +17 -120
- package/dist/cli/preview-json.js +14 -97
- package/dist/cli/prompt.js +8 -13
- package/dist/cli/read-json.d.ts +15 -37
- package/dist/cli/read-json.js +44 -140
- package/dist/cli/runner.js +10 -13
- package/dist/cli/types.d.ts +8 -17
- package/dist/cli/types.js +0 -2
- package/dist/cli/wallet-format.d.ts +1 -0
- package/dist/cli/wallet-format.js +205 -144
- package/dist/cli/workflow-hints.d.ts +3 -3
- package/dist/cli/workflow-hints.js +11 -8
- package/dist/client/default-client.d.ts +3 -1
- package/dist/client/default-client.js +45 -2
- package/dist/client/factory.js +1 -1
- package/dist/client/initialization.js +23 -0
- package/dist/client/persistence.js +5 -5
- package/dist/client/store-adapter.js +1 -0
- package/dist/sqlite/checkpoints.d.ts +1 -0
- package/dist/sqlite/checkpoints.js +7 -0
- package/dist/sqlite/store.js +14 -1
- package/dist/types.d.ts +1 -0
- package/dist/wallet/coin-control.d.ts +41 -11
- package/dist/wallet/coin-control.js +100 -357
- package/dist/wallet/descriptor-normalization.d.ts +1 -3
- package/dist/wallet/descriptor-normalization.js +0 -16
- package/dist/wallet/lifecycle.d.ts +7 -99
- package/dist/wallet/lifecycle.js +513 -968
- package/dist/wallet/managed-core-wallet.d.ts +13 -0
- package/dist/wallet/managed-core-wallet.js +20 -0
- package/dist/wallet/mining/constants.d.ts +5 -12
- package/dist/wallet/mining/constants.js +5 -12
- package/dist/wallet/mining/control.d.ts +1 -13
- package/dist/wallet/mining/control.js +45 -349
- package/dist/wallet/mining/index.d.ts +3 -4
- package/dist/wallet/mining/index.js +1 -2
- package/dist/wallet/mining/runner.d.ts +179 -6
- package/dist/wallet/mining/runner.js +891 -501
- package/dist/wallet/mining/runtime-artifacts.js +23 -3
- package/dist/wallet/mining/sentence-protocol.d.ts +44 -0
- package/dist/wallet/mining/sentence-protocol.js +123 -0
- package/dist/wallet/mining/sentences.d.ts +4 -8
- package/dist/wallet/mining/sentences.js +3 -52
- package/dist/wallet/mining/state.d.ts +11 -6
- package/dist/wallet/mining/state.js +7 -6
- package/dist/wallet/mining/types.d.ts +2 -30
- package/dist/wallet/mining/visualizer.d.ts +31 -3
- package/dist/wallet/mining/visualizer.js +135 -13
- package/dist/wallet/read/context.d.ts +0 -2
- package/dist/wallet/read/context.js +119 -140
- package/dist/wallet/read/filter.js +2 -11
- package/dist/wallet/read/index.d.ts +1 -1
- package/dist/wallet/read/project.js +24 -77
- package/dist/wallet/read/types.d.ts +10 -25
- package/dist/wallet/reset.d.ts +0 -1
- package/dist/wallet/reset.js +60 -138
- package/dist/wallet/root-resolution.d.ts +1 -5
- package/dist/wallet/root-resolution.js +0 -18
- package/dist/wallet/runtime.d.ts +0 -6
- package/dist/wallet/runtime.js +0 -8
- package/dist/wallet/state/client-password-agent.js +208 -0
- package/dist/wallet/state/client-password.d.ts +65 -0
- package/dist/wallet/state/client-password.js +952 -0
- package/dist/wallet/state/crypto.d.ts +1 -20
- package/dist/wallet/state/crypto.js +0 -63
- package/dist/wallet/state/provider.d.ts +23 -11
- package/dist/wallet/state/provider.js +248 -290
- package/dist/wallet/state/storage.d.ts +2 -2
- package/dist/wallet/state/storage.js +48 -16
- package/dist/wallet/tx/anchor.d.ts +3 -28
- package/dist/wallet/tx/anchor.js +349 -1240
- package/dist/wallet/tx/bitcoin-transfer.d.ts +35 -0
- package/dist/wallet/tx/bitcoin-transfer.js +200 -0
- package/dist/wallet/tx/cog.d.ts +5 -1
- package/dist/wallet/tx/cog.js +149 -185
- package/dist/wallet/tx/common.d.ts +74 -10
- package/dist/wallet/tx/common.js +315 -138
- package/dist/wallet/tx/domain-admin.d.ts +3 -1
- package/dist/wallet/tx/domain-admin.js +61 -99
- package/dist/wallet/tx/domain-market.d.ts +5 -1
- package/dist/wallet/tx/domain-market.js +221 -228
- package/dist/wallet/tx/field.d.ts +4 -10
- package/dist/wallet/tx/field.js +84 -914
- package/dist/wallet/tx/identity-selector.d.ts +9 -3
- package/dist/wallet/tx/identity-selector.js +17 -35
- package/dist/wallet/tx/index.d.ts +3 -1
- package/dist/wallet/tx/index.js +2 -1
- package/dist/wallet/tx/register.d.ts +3 -1
- package/dist/wallet/tx/register.js +62 -220
- package/dist/wallet/tx/reputation.d.ts +3 -1
- package/dist/wallet/tx/reputation.js +58 -95
- package/dist/wallet/types.d.ts +8 -122
- package/package.json +5 -5
- package/dist/wallet/archive.d.ts +0 -4
- package/dist/wallet/archive.js +0 -41
- package/dist/wallet/mining/hook-protocol.d.ts +0 -47
- package/dist/wallet/mining/hook-protocol.js +0 -161
- package/dist/wallet/mining/hook-runner.js +0 -52
- package/dist/wallet/mining/hooks.d.ts +0 -38
- package/dist/wallet/mining/hooks.js +0 -520
- package/dist/wallet/state/explicit-lock.d.ts +0 -4
- package/dist/wallet/state/explicit-lock.js +0 -19
- package/dist/wallet/state/session.d.ts +0 -12
- package/dist/wallet/state/session.js +0 -23
- /package/dist/wallet/{mining/hook-runner.d.ts → state/client-password-agent.d.ts} +0 -0
package/dist/wallet/tx/field.js
CHANGED
|
@@ -10,9 +10,8 @@ import { createDefaultWalletSecretProvider, } from "../state/provider.js";
|
|
|
10
10
|
import { FIELD_FORMAT_BYTES, serializeDataUpdate, serializeFieldReg, } from "../cogop/index.js";
|
|
11
11
|
import { validateFieldName } from "../cogop/validate-name.js";
|
|
12
12
|
import { findDomainField, openWalletReadContext, } from "../read/index.js";
|
|
13
|
-
import {
|
|
13
|
+
import { assertWalletMutationContextReady, buildWalletMutationTransactionWithReserveFallback, createBuiltWalletMutationFeeSummary, createWalletMutationFeeMetadata, isAlreadyAcceptedError, isBroadcastUnknownError, mergeFixedWalletInputs, outpointKey, pauseMiningForWalletMutation, resolvePendingMutationReuseDecision, resolveWalletMutationFeeSelection, saveWalletStatePreservingUnlock, unlockTemporaryBuilderLocks, updateMutationRecord, } from "./common.js";
|
|
14
14
|
import { confirmTypedAcknowledgement as confirmSharedTypedAcknowledgement, confirmYesNo as confirmSharedYesNo, } from "./confirm.js";
|
|
15
|
-
import { getCanonicalIdentitySelector } from "./identity-selector.js";
|
|
16
15
|
import { findPendingMutationByIntent, upsertPendingMutation } from "./journal.js";
|
|
17
16
|
function createResolvedFieldSenderSummary(sender, selector) {
|
|
18
17
|
return {
|
|
@@ -30,18 +29,6 @@ function createResolvedFieldValueSummary(format, value) {
|
|
|
30
29
|
}
|
|
31
30
|
function createResolvedFieldSummary(options) {
|
|
32
31
|
if (options.kind === "field-create") {
|
|
33
|
-
if (options.family) {
|
|
34
|
-
return {
|
|
35
|
-
sender: createResolvedFieldSenderSummary(options.sender, options.senderSelector),
|
|
36
|
-
path: "field-reg-plus-data-update-family",
|
|
37
|
-
value: options.value,
|
|
38
|
-
effect: {
|
|
39
|
-
kind: "create-and-initialize-field",
|
|
40
|
-
tx1BurnCogtoshi: "100",
|
|
41
|
-
tx2AdditionalBurnCogtoshi: "1",
|
|
42
|
-
},
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
32
|
return {
|
|
46
33
|
sender: createResolvedFieldSenderSummary(options.sender, options.senderSelector),
|
|
47
34
|
path: "standalone-field-reg",
|
|
@@ -83,8 +70,6 @@ function describeFieldEffect(effect) {
|
|
|
83
70
|
switch (effect.kind) {
|
|
84
71
|
case "create-empty-field":
|
|
85
72
|
return `burn ${effect.burnCogtoshi} cogtoshi to create an empty field`;
|
|
86
|
-
case "create-and-initialize-field":
|
|
87
|
-
return `burn ${effect.tx1BurnCogtoshi} cogtoshi in Tx1 and ${effect.tx2AdditionalBurnCogtoshi} additional cogtoshi in Tx2`;
|
|
88
73
|
case "write-field-value":
|
|
89
74
|
return `burn ${effect.burnCogtoshi} cogtoshi to write the field value`;
|
|
90
75
|
case "clear-field-value":
|
|
@@ -150,61 +135,12 @@ function isActiveMutationStatus(status) {
|
|
|
150
135
|
|| status === "live"
|
|
151
136
|
|| status === "repair-required";
|
|
152
137
|
}
|
|
153
|
-
function isActiveFamilyStatus(status) {
|
|
154
|
-
return status === "draft"
|
|
155
|
-
|| status === "broadcasting"
|
|
156
|
-
|| status === "broadcast-unknown"
|
|
157
|
-
|| status === "live"
|
|
158
|
-
|| status === "repair-required";
|
|
159
|
-
}
|
|
160
|
-
function createFamilyTransactionRecord() {
|
|
161
|
-
return {
|
|
162
|
-
status: "draft",
|
|
163
|
-
attemptedTxid: null,
|
|
164
|
-
attemptedWtxid: null,
|
|
165
|
-
temporaryBuilderLockedOutpoints: [],
|
|
166
|
-
rawHex: null,
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
function upsertProactiveFamily(state, family) {
|
|
170
|
-
const families = state.proactiveFamilies.slice();
|
|
171
|
-
const existingIndex = families.findIndex((entry) => entry.familyId === family.familyId);
|
|
172
|
-
if (existingIndex >= 0) {
|
|
173
|
-
families[existingIndex] = family;
|
|
174
|
-
}
|
|
175
|
-
else {
|
|
176
|
-
families.push(family);
|
|
177
|
-
}
|
|
178
|
-
return {
|
|
179
|
-
...state,
|
|
180
|
-
proactiveFamilies: families,
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
function findFieldFamilyByIntent(state, intentFingerprintHex) {
|
|
184
|
-
return state.proactiveFamilies.find((family) => family.type === "field" && family.intentFingerprintHex === intentFingerprintHex) ?? null;
|
|
185
|
-
}
|
|
186
|
-
function findActiveFieldFamilyByDomain(state, domainName) {
|
|
187
|
-
return state.proactiveFamilies.find((family) => family.type === "field"
|
|
188
|
-
&& family.domainName === domainName
|
|
189
|
-
&& isActiveFamilyStatus(family.status)) ?? null;
|
|
190
|
-
}
|
|
191
138
|
function findActiveFieldCreateMutationByDomain(state, domainName, intentFingerprintHex) {
|
|
192
139
|
return (state.pendingMutations ?? []).find((mutation) => mutation.kind === "field-create"
|
|
193
140
|
&& mutation.domainName === domainName
|
|
194
141
|
&& mutation.intentFingerprintHex !== intentFingerprintHex
|
|
195
142
|
&& isActiveMutationStatus(mutation.status)) ?? null;
|
|
196
143
|
}
|
|
197
|
-
function resolveAnchorOutpointForSender(state, sender, errorPrefix) {
|
|
198
|
-
const anchoredDomain = state.domains.find((domain) => domain.currentOwnerLocalIndex === sender.index
|
|
199
|
-
&& domain.canonicalChainStatus === "anchored") ?? null;
|
|
200
|
-
if (anchoredDomain?.currentCanonicalAnchorOutpoint === null || anchoredDomain === null) {
|
|
201
|
-
throw new Error(`${errorPrefix}_anchor_outpoint_unavailable`);
|
|
202
|
-
}
|
|
203
|
-
return {
|
|
204
|
-
txid: anchoredDomain.currentCanonicalAnchorOutpoint.txid,
|
|
205
|
-
vout: anchoredDomain.currentCanonicalAnchorOutpoint.vout,
|
|
206
|
-
};
|
|
207
|
-
}
|
|
208
144
|
function resolveAnchoredFieldOperation(context, domainName, errorPrefix) {
|
|
209
145
|
assertWalletMutationContextReady(context, errorPrefix);
|
|
210
146
|
const chainDomain = lookupDomain(context.snapshot.state, domainName);
|
|
@@ -215,24 +151,19 @@ function resolveAnchoredFieldOperation(context, domainName, errorPrefix) {
|
|
|
215
151
|
throw new Error(`${errorPrefix}_domain_not_anchored`);
|
|
216
152
|
}
|
|
217
153
|
const ownerHex = Buffer.from(chainDomain.ownerScriptPubKey).toString("hex");
|
|
218
|
-
const
|
|
219
|
-
if (
|
|
154
|
+
const state = context.localState.state;
|
|
155
|
+
if (ownerHex !== state.funding.scriptPubKeyHex || state.funding.address.trim() === "") {
|
|
220
156
|
throw new Error(`${errorPrefix}_owner_not_locally_controlled`);
|
|
221
157
|
}
|
|
222
|
-
if (ownerIdentity.readOnly) {
|
|
223
|
-
throw new Error(`${errorPrefix}_owner_read_only`);
|
|
224
|
-
}
|
|
225
158
|
return {
|
|
226
159
|
readContext: context,
|
|
227
|
-
state
|
|
228
|
-
unlockUntilUnixMs: context.localState.unlockUntilUnixMs,
|
|
160
|
+
state,
|
|
229
161
|
sender: {
|
|
230
|
-
localIndex:
|
|
231
|
-
scriptPubKeyHex:
|
|
232
|
-
address:
|
|
162
|
+
localIndex: 0,
|
|
163
|
+
scriptPubKeyHex: state.funding.scriptPubKeyHex,
|
|
164
|
+
address: state.funding.address,
|
|
233
165
|
},
|
|
234
|
-
senderSelector:
|
|
235
|
-
anchorOutpoint: resolveAnchorOutpointForSender(context.localState.state, ownerIdentity, errorPrefix),
|
|
166
|
+
senderSelector: state.funding.address,
|
|
236
167
|
chainDomain,
|
|
237
168
|
};
|
|
238
169
|
}
|
|
@@ -241,89 +172,34 @@ function buildAnchoredFieldPlan(options) {
|
|
|
241
172
|
&& entry.confirmations >= 1
|
|
242
173
|
&& entry.spendable !== false
|
|
243
174
|
&& entry.safe !== false);
|
|
244
|
-
const anchorUtxo = options.allUtxos.find((entry) => entry.txid === options.anchorOutpoint.txid
|
|
245
|
-
&& entry.vout === options.anchorOutpoint.vout
|
|
246
|
-
&& entry.scriptPubKey === options.sender.scriptPubKeyHex
|
|
247
|
-
&& entry.confirmations >= 1
|
|
248
|
-
&& entry.spendable !== false
|
|
249
|
-
&& entry.safe !== false);
|
|
250
|
-
if (anchorUtxo === undefined) {
|
|
251
|
-
throw new Error(`${options.errorPrefix}_anchor_utxo_missing`);
|
|
252
|
-
}
|
|
253
175
|
return {
|
|
254
176
|
sender: options.sender,
|
|
255
177
|
changeAddress: options.state.funding.address,
|
|
256
|
-
fixedInputs: [
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
outputs: [
|
|
260
|
-
{ data: Buffer.from(options.opReturnData).toString("hex") },
|
|
261
|
-
{ [options.sender.address]: satsToBtcNumber(BigInt(options.state.anchorValueSats)) },
|
|
262
|
-
],
|
|
263
|
-
changePosition: 2,
|
|
178
|
+
fixedInputs: [],
|
|
179
|
+
outputs: [{ data: Buffer.from(options.opReturnData).toString("hex") }],
|
|
180
|
+
changePosition: 1,
|
|
264
181
|
expectedOpReturnScriptHex: encodeOpReturnScript(options.opReturnData),
|
|
265
|
-
expectedAnchorScriptHex: options.sender.scriptPubKeyHex,
|
|
266
|
-
expectedAnchorValueSats: BigInt(options.state.anchorValueSats),
|
|
267
182
|
allowedFundingScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
|
|
268
183
|
eligibleFundingOutpointKeys: new Set(fundingUtxos.map((entry) => outpointKey({ txid: entry.txid, vout: entry.vout }))),
|
|
269
184
|
errorPrefix: options.errorPrefix,
|
|
270
185
|
};
|
|
271
186
|
}
|
|
272
|
-
function buildFieldFamilyTx2Plan(options) {
|
|
273
|
-
const fundingUtxos = options.allUtxos.filter((entry) => entry.scriptPubKey === options.state.funding.scriptPubKeyHex
|
|
274
|
-
&& entry.confirmations >= 1
|
|
275
|
-
&& entry.spendable !== false
|
|
276
|
-
&& entry.safe !== false);
|
|
277
|
-
return {
|
|
278
|
-
sender: options.sender,
|
|
279
|
-
changeAddress: options.state.funding.address,
|
|
280
|
-
fixedInputs: [{ txid: options.tx1Txid, vout: 1 }],
|
|
281
|
-
outputs: [
|
|
282
|
-
{ data: Buffer.from(options.opReturnData).toString("hex") },
|
|
283
|
-
{ [options.sender.address]: satsToBtcNumber(BigInt(options.state.anchorValueSats)) },
|
|
284
|
-
],
|
|
285
|
-
changePosition: 2,
|
|
286
|
-
expectedOpReturnScriptHex: encodeOpReturnScript(options.opReturnData),
|
|
287
|
-
expectedAnchorScriptHex: options.sender.scriptPubKeyHex,
|
|
288
|
-
expectedAnchorValueSats: BigInt(options.state.anchorValueSats),
|
|
289
|
-
allowedFundingScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
|
|
290
|
-
eligibleFundingOutpointKeys: new Set(fundingUtxos.map((entry) => outpointKey({ txid: entry.txid, vout: entry.vout }))),
|
|
291
|
-
errorPrefix: "wallet_field_create_tx2",
|
|
292
|
-
};
|
|
293
|
-
}
|
|
294
187
|
function validateFieldDraft(decoded, funded, plan) {
|
|
295
188
|
const inputs = decoded.tx.vin;
|
|
296
189
|
const outputs = decoded.tx.vout;
|
|
297
190
|
if (inputs.length === 0) {
|
|
298
191
|
throw new Error(`${plan.errorPrefix}_missing_sender_input`);
|
|
299
192
|
}
|
|
300
|
-
assertFixedInputPrefixMatches(inputs, plan.fixedInputs, `${plan.errorPrefix}_sender_input_mismatch`);
|
|
301
|
-
if (inputs[0]?.prevout?.scriptPubKey?.hex !== plan.sender.scriptPubKeyHex) {
|
|
302
|
-
throw new Error(`${plan.errorPrefix}_sender_input_mismatch`);
|
|
303
|
-
}
|
|
304
|
-
assertFundingInputsAfterFixedPrefix({
|
|
305
|
-
inputs,
|
|
306
|
-
fixedInputs: plan.fixedInputs,
|
|
307
|
-
allowedFundingScriptPubKeyHex: plan.allowedFundingScriptPubKeyHex,
|
|
308
|
-
eligibleFundingOutpointKeys: plan.eligibleFundingOutpointKeys,
|
|
309
|
-
errorCode: `${plan.errorPrefix}_unexpected_funding_input`,
|
|
310
|
-
});
|
|
311
193
|
if (outputs[0]?.scriptPubKey?.hex !== plan.expectedOpReturnScriptHex) {
|
|
312
194
|
throw new Error(`${plan.errorPrefix}_opreturn_mismatch`);
|
|
313
195
|
}
|
|
314
|
-
if (outputs[1]?.scriptPubKey?.hex !== plan.expectedAnchorScriptHex) {
|
|
315
|
-
throw new Error(`${plan.errorPrefix}_anchor_output_mismatch`);
|
|
316
|
-
}
|
|
317
|
-
if (valueToSats(outputs[1]?.value ?? 0) !== plan.expectedAnchorValueSats) {
|
|
318
|
-
throw new Error(`${plan.errorPrefix}_anchor_value_mismatch`);
|
|
319
|
-
}
|
|
320
196
|
if (funded.changepos === -1) {
|
|
321
|
-
if (outputs.length !==
|
|
197
|
+
if (outputs.length !== 1) {
|
|
322
198
|
throw new Error(`${plan.errorPrefix}_unexpected_output_count`);
|
|
323
199
|
}
|
|
324
200
|
return;
|
|
325
201
|
}
|
|
326
|
-
if (funded.changepos !== plan.changePosition || outputs.length !==
|
|
202
|
+
if (funded.changepos !== plan.changePosition || outputs.length !== 2) {
|
|
327
203
|
throw new Error(`${plan.errorPrefix}_change_position_mismatch`);
|
|
328
204
|
}
|
|
329
205
|
if (outputs[funded.changepos]?.scriptPubKey?.hex !== plan.allowedFundingScriptPubKeyHex) {
|
|
@@ -339,7 +215,8 @@ async function buildFieldTransaction(options) {
|
|
|
339
215
|
validateFundedDraft: validateFieldDraft,
|
|
340
216
|
finalizeErrorCode: `${options.plan.errorPrefix}_finalize_failed`,
|
|
341
217
|
mempoolRejectPrefix: `${options.plan.errorPrefix}_mempool_rejected`,
|
|
342
|
-
|
|
218
|
+
feeRate: options.feeRateSatVb,
|
|
219
|
+
availableFundingMinConf: options.availableFundingMinConf,
|
|
343
220
|
});
|
|
344
221
|
}
|
|
345
222
|
async function saveUpdatedState(options) {
|
|
@@ -351,7 +228,6 @@ async function saveUpdatedState(options) {
|
|
|
351
228
|
await saveWalletStatePreservingUnlock({
|
|
352
229
|
state: nextState,
|
|
353
230
|
provider: options.provider,
|
|
354
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
355
231
|
nowUnixMs: options.nowUnixMs,
|
|
356
232
|
paths: options.paths,
|
|
357
233
|
});
|
|
@@ -374,6 +250,7 @@ function createStandaloneFieldMutation(options) {
|
|
|
374
250
|
lastUpdatedAtUnixMs: options.nowUnixMs,
|
|
375
251
|
attemptedTxid: null,
|
|
376
252
|
attemptedWtxid: null,
|
|
253
|
+
...createWalletMutationFeeMetadata(options.feeSelection),
|
|
377
254
|
temporaryBuilderLockedOutpoints: [],
|
|
378
255
|
};
|
|
379
256
|
}
|
|
@@ -395,61 +272,10 @@ function createStandaloneFieldMutation(options) {
|
|
|
395
272
|
lastUpdatedAtUnixMs: options.nowUnixMs,
|
|
396
273
|
attemptedTxid: null,
|
|
397
274
|
attemptedWtxid: null,
|
|
275
|
+
...createWalletMutationFeeMetadata(options.feeSelection),
|
|
398
276
|
temporaryBuilderLockedOutpoints: [],
|
|
399
277
|
};
|
|
400
278
|
}
|
|
401
|
-
function createFieldFamilyRecord(options) {
|
|
402
|
-
if (options.existing !== null && options.existing !== undefined) {
|
|
403
|
-
return {
|
|
404
|
-
...options.existing,
|
|
405
|
-
type: "field",
|
|
406
|
-
status: "draft",
|
|
407
|
-
domainName: options.domainName,
|
|
408
|
-
domainId: options.domainId,
|
|
409
|
-
sourceSenderLocalIndex: options.sender.localIndex,
|
|
410
|
-
sourceSenderScriptPubKeyHex: options.sender.scriptPubKeyHex,
|
|
411
|
-
fieldName: options.fieldName,
|
|
412
|
-
expectedFieldId: options.expectedFieldId,
|
|
413
|
-
fieldPermanent: options.permanence,
|
|
414
|
-
fieldFormat: options.format,
|
|
415
|
-
fieldValueHex: options.valueHex,
|
|
416
|
-
currentStep: "tx1",
|
|
417
|
-
lastUpdatedAtUnixMs: options.nowUnixMs,
|
|
418
|
-
tx1: createFamilyTransactionRecord(),
|
|
419
|
-
tx2: createFamilyTransactionRecord(),
|
|
420
|
-
};
|
|
421
|
-
}
|
|
422
|
-
return {
|
|
423
|
-
familyId: randomBytes(12).toString("hex"),
|
|
424
|
-
type: "field",
|
|
425
|
-
status: "draft",
|
|
426
|
-
intentFingerprintHex: options.intentFingerprintHex,
|
|
427
|
-
createdAtUnixMs: options.nowUnixMs,
|
|
428
|
-
lastUpdatedAtUnixMs: options.nowUnixMs,
|
|
429
|
-
domainName: options.domainName,
|
|
430
|
-
domainId: options.domainId,
|
|
431
|
-
sourceSenderLocalIndex: options.sender.localIndex,
|
|
432
|
-
sourceSenderScriptPubKeyHex: options.sender.scriptPubKeyHex,
|
|
433
|
-
fieldName: options.fieldName,
|
|
434
|
-
expectedFieldId: options.expectedFieldId,
|
|
435
|
-
fieldPermanent: options.permanence,
|
|
436
|
-
fieldFormat: options.format,
|
|
437
|
-
fieldValueHex: options.valueHex,
|
|
438
|
-
currentStep: "tx1",
|
|
439
|
-
tx1: createFamilyTransactionRecord(),
|
|
440
|
-
tx2: createFamilyTransactionRecord(),
|
|
441
|
-
};
|
|
442
|
-
}
|
|
443
|
-
function updateFieldFamilyState(options) {
|
|
444
|
-
return upsertProactiveFamily(options.state, {
|
|
445
|
-
...options.family,
|
|
446
|
-
status: options.status,
|
|
447
|
-
currentStep: options.currentStep,
|
|
448
|
-
lastUpdatedAtUnixMs: options.nowUnixMs,
|
|
449
|
-
tx1: options.tx1 ?? options.family.tx1 ?? createFamilyTransactionRecord(),
|
|
450
|
-
tx2: options.tx2 ?? options.family.tx2 ?? createFamilyTransactionRecord(),
|
|
451
|
-
});
|
|
452
|
-
}
|
|
453
279
|
function getObservedFieldState(context, domainName, fieldName) {
|
|
454
280
|
if (context.snapshot === null) {
|
|
455
281
|
return null;
|
|
@@ -532,7 +358,6 @@ async function reconcilePendingFieldMutation(options) {
|
|
|
532
358
|
nextState = await saveUpdatedState({
|
|
533
359
|
state: nextState,
|
|
534
360
|
provider: options.provider,
|
|
535
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
536
361
|
nowUnixMs: options.nowUnixMs,
|
|
537
362
|
paths: options.paths,
|
|
538
363
|
});
|
|
@@ -547,7 +372,6 @@ async function reconcilePendingFieldMutation(options) {
|
|
|
547
372
|
nextState = await saveUpdatedState({
|
|
548
373
|
state: nextState,
|
|
549
374
|
provider: options.provider,
|
|
550
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
551
375
|
nowUnixMs: options.nowUnixMs,
|
|
552
376
|
paths: options.paths,
|
|
553
377
|
});
|
|
@@ -565,7 +389,6 @@ async function reconcilePendingFieldMutation(options) {
|
|
|
565
389
|
nextState = await saveUpdatedState({
|
|
566
390
|
state: nextState,
|
|
567
391
|
provider: options.provider,
|
|
568
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
569
392
|
nowUnixMs: options.nowUnixMs,
|
|
570
393
|
paths: options.paths,
|
|
571
394
|
});
|
|
@@ -582,7 +405,6 @@ async function reconcilePendingFieldMutation(options) {
|
|
|
582
405
|
nextState = await saveUpdatedState({
|
|
583
406
|
state: nextState,
|
|
584
407
|
provider: options.provider,
|
|
585
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
586
408
|
nowUnixMs: options.nowUnixMs,
|
|
587
409
|
paths: options.paths,
|
|
588
410
|
});
|
|
@@ -594,180 +416,6 @@ async function reconcilePendingFieldMutation(options) {
|
|
|
594
416
|
resolution: "continue",
|
|
595
417
|
};
|
|
596
418
|
}
|
|
597
|
-
async function reconcileFieldFamily(options) {
|
|
598
|
-
const domainName = options.family.domainName ?? "";
|
|
599
|
-
const fieldName = options.family.fieldName ?? "";
|
|
600
|
-
const observed = getObservedFieldState(options.context, domainName, fieldName);
|
|
601
|
-
if (observed !== null) {
|
|
602
|
-
if (observed.fieldId === options.family.expectedFieldId
|
|
603
|
-
&& observed.permanent === options.family.fieldPermanent
|
|
604
|
-
&& observed.hasValue
|
|
605
|
-
&& observed.format === options.family.fieldFormat
|
|
606
|
-
&& observed.rawValueHex === options.family.fieldValueHex) {
|
|
607
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
|
|
608
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
|
|
609
|
-
let nextState = updateFieldFamilyState({
|
|
610
|
-
state: options.state,
|
|
611
|
-
family: options.family,
|
|
612
|
-
status: "confirmed",
|
|
613
|
-
currentStep: "tx2",
|
|
614
|
-
nowUnixMs: options.nowUnixMs,
|
|
615
|
-
tx1: options.family.tx1 == null
|
|
616
|
-
? undefined
|
|
617
|
-
: { ...options.family.tx1, status: "confirmed", temporaryBuilderLockedOutpoints: [] },
|
|
618
|
-
tx2: options.family.tx2 == null
|
|
619
|
-
? undefined
|
|
620
|
-
: { ...options.family.tx2, status: "confirmed", temporaryBuilderLockedOutpoints: [] },
|
|
621
|
-
});
|
|
622
|
-
nextState = await saveUpdatedState({
|
|
623
|
-
state: nextState,
|
|
624
|
-
provider: options.provider,
|
|
625
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
626
|
-
nowUnixMs: options.nowUnixMs,
|
|
627
|
-
paths: options.paths,
|
|
628
|
-
});
|
|
629
|
-
return {
|
|
630
|
-
state: nextState,
|
|
631
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
|
|
632
|
-
...options.family,
|
|
633
|
-
status: "confirmed",
|
|
634
|
-
},
|
|
635
|
-
resolution: "confirmed",
|
|
636
|
-
};
|
|
637
|
-
}
|
|
638
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
|
|
639
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
|
|
640
|
-
let nextState = updateFieldFamilyState({
|
|
641
|
-
state: options.state,
|
|
642
|
-
family: options.family,
|
|
643
|
-
status: "repair-required",
|
|
644
|
-
currentStep: "tx2",
|
|
645
|
-
nowUnixMs: options.nowUnixMs,
|
|
646
|
-
tx1: options.family.tx1 == null
|
|
647
|
-
? undefined
|
|
648
|
-
: { ...options.family.tx1, temporaryBuilderLockedOutpoints: [] },
|
|
649
|
-
tx2: options.family.tx2 == null
|
|
650
|
-
? undefined
|
|
651
|
-
: { ...options.family.tx2, temporaryBuilderLockedOutpoints: [] },
|
|
652
|
-
});
|
|
653
|
-
nextState = await saveUpdatedState({
|
|
654
|
-
state: nextState,
|
|
655
|
-
provider: options.provider,
|
|
656
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
657
|
-
nowUnixMs: options.nowUnixMs,
|
|
658
|
-
paths: options.paths,
|
|
659
|
-
});
|
|
660
|
-
return {
|
|
661
|
-
state: nextState,
|
|
662
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
|
|
663
|
-
...options.family,
|
|
664
|
-
status: "repair-required",
|
|
665
|
-
},
|
|
666
|
-
resolution: "repair-required",
|
|
667
|
-
};
|
|
668
|
-
}
|
|
669
|
-
const tx2Known = options.family.tx2?.attemptedTxid == null
|
|
670
|
-
? false
|
|
671
|
-
: await options.rpc.getRawTransaction(options.family.tx2.attemptedTxid, true).then(() => true).catch(() => false);
|
|
672
|
-
if (tx2Known) {
|
|
673
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
|
|
674
|
-
let nextState = updateFieldFamilyState({
|
|
675
|
-
state: options.state,
|
|
676
|
-
family: options.family,
|
|
677
|
-
status: "live",
|
|
678
|
-
currentStep: "tx2",
|
|
679
|
-
nowUnixMs: options.nowUnixMs,
|
|
680
|
-
tx2: options.family.tx2 == null
|
|
681
|
-
? undefined
|
|
682
|
-
: { ...options.family.tx2, status: "live", temporaryBuilderLockedOutpoints: [] },
|
|
683
|
-
});
|
|
684
|
-
nextState = await saveUpdatedState({
|
|
685
|
-
state: nextState,
|
|
686
|
-
provider: options.provider,
|
|
687
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
688
|
-
nowUnixMs: options.nowUnixMs,
|
|
689
|
-
paths: options.paths,
|
|
690
|
-
});
|
|
691
|
-
return {
|
|
692
|
-
state: nextState,
|
|
693
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
|
|
694
|
-
...options.family,
|
|
695
|
-
status: "live",
|
|
696
|
-
},
|
|
697
|
-
resolution: "live",
|
|
698
|
-
};
|
|
699
|
-
}
|
|
700
|
-
const tx1Known = options.family.tx1?.attemptedTxid == null
|
|
701
|
-
? false
|
|
702
|
-
: await options.rpc.getRawTransaction(options.family.tx1.attemptedTxid, true).then(() => true).catch(() => false);
|
|
703
|
-
if (tx1Known) {
|
|
704
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
|
|
705
|
-
let nextState = updateFieldFamilyState({
|
|
706
|
-
state: options.state,
|
|
707
|
-
family: options.family,
|
|
708
|
-
status: "live",
|
|
709
|
-
currentStep: "tx1",
|
|
710
|
-
nowUnixMs: options.nowUnixMs,
|
|
711
|
-
tx1: options.family.tx1 == null
|
|
712
|
-
? undefined
|
|
713
|
-
: { ...options.family.tx1, status: "live", temporaryBuilderLockedOutpoints: [] },
|
|
714
|
-
});
|
|
715
|
-
nextState = await saveUpdatedState({
|
|
716
|
-
state: nextState,
|
|
717
|
-
provider: options.provider,
|
|
718
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
719
|
-
nowUnixMs: options.nowUnixMs,
|
|
720
|
-
paths: options.paths,
|
|
721
|
-
});
|
|
722
|
-
return {
|
|
723
|
-
state: nextState,
|
|
724
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
|
|
725
|
-
...options.family,
|
|
726
|
-
status: "live",
|
|
727
|
-
},
|
|
728
|
-
resolution: "ready-for-tx2",
|
|
729
|
-
};
|
|
730
|
-
}
|
|
731
|
-
if (options.family.status === "broadcast-unknown"
|
|
732
|
-
|| options.family.status === "draft"
|
|
733
|
-
|| options.family.status === "broadcasting") {
|
|
734
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
|
|
735
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
|
|
736
|
-
let nextState = updateFieldFamilyState({
|
|
737
|
-
state: options.state,
|
|
738
|
-
family: options.family,
|
|
739
|
-
status: "canceled",
|
|
740
|
-
currentStep: options.family.currentStep,
|
|
741
|
-
nowUnixMs: options.nowUnixMs,
|
|
742
|
-
tx1: options.family.tx1 == null
|
|
743
|
-
? undefined
|
|
744
|
-
: { ...options.family.tx1, status: options.family.tx1.attemptedTxid === null ? "canceled" : options.family.tx1.status, temporaryBuilderLockedOutpoints: [] },
|
|
745
|
-
tx2: options.family.tx2 == null
|
|
746
|
-
? undefined
|
|
747
|
-
: { ...options.family.tx2, status: options.family.tx2.attemptedTxid === null ? "canceled" : options.family.tx2.status, temporaryBuilderLockedOutpoints: [] },
|
|
748
|
-
});
|
|
749
|
-
nextState = await saveUpdatedState({
|
|
750
|
-
state: nextState,
|
|
751
|
-
provider: options.provider,
|
|
752
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
753
|
-
nowUnixMs: options.nowUnixMs,
|
|
754
|
-
paths: options.paths,
|
|
755
|
-
});
|
|
756
|
-
return {
|
|
757
|
-
state: nextState,
|
|
758
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
|
|
759
|
-
...options.family,
|
|
760
|
-
status: "canceled",
|
|
761
|
-
},
|
|
762
|
-
resolution: "not-seen",
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
return {
|
|
766
|
-
state: options.state,
|
|
767
|
-
family: options.family,
|
|
768
|
-
resolution: "continue",
|
|
769
|
-
};
|
|
770
|
-
}
|
|
771
419
|
async function confirmYesNo(prompter, message, errorCode, options) {
|
|
772
420
|
await confirmSharedYesNo(prompter, message, {
|
|
773
421
|
assumeYes: options.assumeYes,
|
|
@@ -789,39 +437,10 @@ async function confirmFieldCreate(prompter, options) {
|
|
|
789
437
|
const fieldRef = `${options.domainName}:${options.fieldName}`;
|
|
790
438
|
prompter.writeLine(`Creating field "${fieldRef}" as ${options.permanent ? "permanent" : "mutable"}.`);
|
|
791
439
|
prompter.writeLine(`Resolved sender: ${options.sender.selector} (${options.sender.address})`);
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
await confirmYesNo(prompter, "The field will be created empty and the burn is not reversible.", "wallet_field_create_confirmation_rejected", {
|
|
797
|
-
assumeYes: options.assumeYes,
|
|
798
|
-
requiresTtyErrorCode: "wallet_field_create_requires_tty",
|
|
799
|
-
});
|
|
800
|
-
return;
|
|
801
|
-
}
|
|
802
|
-
prompter.writeLine("Path: field-reg-plus-data-update-family");
|
|
803
|
-
prompter.writeLine(`Effect: ${describeFieldEffect({
|
|
804
|
-
kind: "create-and-initialize-field",
|
|
805
|
-
tx1BurnCogtoshi: "100",
|
|
806
|
-
tx2AdditionalBurnCogtoshi: "1",
|
|
807
|
-
})}.`);
|
|
808
|
-
prompter.writeLine(`Value: format ${options.value.format}, ${options.value.value.length} bytes`);
|
|
809
|
-
prompter.writeLine(`Initial value format: ${options.value.formatLabel}`);
|
|
810
|
-
prompter.writeLine(`Initial value bytes: ${options.value.value.length}`);
|
|
811
|
-
prompter.writeLine("Warning: non-clear field values are public in the mempool and on-chain.");
|
|
812
|
-
prompter.writeLine("This uses the same-block FIELD_REG -> DATA_UPDATE family.");
|
|
813
|
-
prompter.writeLine("Tx1 burns 0.00000100 COG. Tx2 may burn an additional 0.00000001 COG.");
|
|
814
|
-
prompter.writeLine("Tx1 may confirm even if Tx2 later fails, is canceled, or needs repair.");
|
|
815
|
-
if (options.permanent) {
|
|
816
|
-
prompter.writeLine("This is the first non-clear value write to a permanent field.");
|
|
817
|
-
await confirmTyped(prompter, fieldRef, `Type ${fieldRef} to continue: `, "wallet_field_create_confirmation_rejected", {
|
|
818
|
-
assumeYes: options.assumeYes,
|
|
819
|
-
requiresTtyErrorCode: "wallet_field_create_requires_tty",
|
|
820
|
-
typedAckRequiredErrorCode: "wallet_field_create_typed_ack_required",
|
|
821
|
-
});
|
|
822
|
-
return;
|
|
823
|
-
}
|
|
824
|
-
await confirmYesNo(prompter, "This creates and initializes the field in the same block family.", "wallet_field_create_confirmation_rejected", {
|
|
440
|
+
prompter.writeLine("Path: standalone-field-reg");
|
|
441
|
+
prompter.writeLine(`Effect: ${describeFieldEffect({ kind: "create-empty-field", burnCogtoshi: "100" })}.`);
|
|
442
|
+
prompter.writeLine("This publishes a standalone FIELD_REG and burns 0.00000100 COG.");
|
|
443
|
+
await confirmYesNo(prompter, "The field will be created empty and the burn is not reversible.", "wallet_field_create_confirmation_rejected", {
|
|
825
444
|
assumeYes: options.assumeYes,
|
|
826
445
|
requiresTtyErrorCode: "wallet_field_create_requires_tty",
|
|
827
446
|
});
|
|
@@ -971,7 +590,6 @@ async function sendStandaloneMutation(options) {
|
|
|
971
590
|
nextState = await saveUpdatedState({
|
|
972
591
|
state: nextState,
|
|
973
592
|
provider: options.provider,
|
|
974
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
975
593
|
nowUnixMs: options.nowUnixMs,
|
|
976
594
|
paths: options.paths,
|
|
977
595
|
});
|
|
@@ -994,7 +612,6 @@ async function sendStandaloneMutation(options) {
|
|
|
994
612
|
nextState = await saveUpdatedState({
|
|
995
613
|
state: nextState,
|
|
996
614
|
provider: options.provider,
|
|
997
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
998
615
|
nowUnixMs: options.nowUnixMs,
|
|
999
616
|
paths: options.paths,
|
|
1000
617
|
});
|
|
@@ -1010,7 +627,6 @@ async function sendStandaloneMutation(options) {
|
|
|
1010
627
|
nextState = await saveUpdatedState({
|
|
1011
628
|
state: nextState,
|
|
1012
629
|
provider: options.provider,
|
|
1013
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1014
630
|
nowUnixMs: options.nowUnixMs,
|
|
1015
631
|
paths: options.paths,
|
|
1016
632
|
});
|
|
@@ -1027,228 +643,11 @@ async function sendStandaloneMutation(options) {
|
|
|
1027
643
|
nextState = await saveUpdatedState({
|
|
1028
644
|
state: nextState,
|
|
1029
645
|
provider: options.provider,
|
|
1030
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1031
646
|
nowUnixMs: options.nowUnixMs,
|
|
1032
647
|
paths: options.paths,
|
|
1033
648
|
});
|
|
1034
649
|
return { state: nextState, mutation: live };
|
|
1035
650
|
}
|
|
1036
|
-
async function sendFamilyTx1(options) {
|
|
1037
|
-
let nextState = updateFieldFamilyState({
|
|
1038
|
-
state: options.state,
|
|
1039
|
-
family: options.family,
|
|
1040
|
-
status: "broadcasting",
|
|
1041
|
-
currentStep: "tx1",
|
|
1042
|
-
nowUnixMs: options.nowUnixMs,
|
|
1043
|
-
tx1: {
|
|
1044
|
-
status: "broadcasting",
|
|
1045
|
-
attemptedTxid: options.built.txid,
|
|
1046
|
-
attemptedWtxid: options.built.wtxid,
|
|
1047
|
-
temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
|
|
1048
|
-
rawHex: options.built.rawHex,
|
|
1049
|
-
},
|
|
1050
|
-
});
|
|
1051
|
-
nextState = await saveUpdatedState({
|
|
1052
|
-
state: nextState,
|
|
1053
|
-
provider: options.provider,
|
|
1054
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1055
|
-
nowUnixMs: options.nowUnixMs,
|
|
1056
|
-
paths: options.paths,
|
|
1057
|
-
});
|
|
1058
|
-
if (options.snapshotHeight !== null && options.snapshotHeight !== (await options.rpc.getBlockchainInfo()).blocks) {
|
|
1059
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
|
|
1060
|
-
throw new Error("wallet_field_create_tx1_tip_mismatch");
|
|
1061
|
-
}
|
|
1062
|
-
try {
|
|
1063
|
-
await options.rpc.sendRawTransaction(options.built.rawHex);
|
|
1064
|
-
}
|
|
1065
|
-
catch (error) {
|
|
1066
|
-
if (!isAlreadyAcceptedError(error)) {
|
|
1067
|
-
if (isBroadcastUnknownError(error)) {
|
|
1068
|
-
nextState = updateFieldFamilyState({
|
|
1069
|
-
state: nextState,
|
|
1070
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
|
|
1071
|
-
status: "broadcast-unknown",
|
|
1072
|
-
currentStep: "tx1",
|
|
1073
|
-
nowUnixMs: options.nowUnixMs,
|
|
1074
|
-
tx1: {
|
|
1075
|
-
status: "broadcast-unknown",
|
|
1076
|
-
attemptedTxid: options.built.txid,
|
|
1077
|
-
attemptedWtxid: options.built.wtxid,
|
|
1078
|
-
temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
|
|
1079
|
-
rawHex: options.built.rawHex,
|
|
1080
|
-
},
|
|
1081
|
-
});
|
|
1082
|
-
nextState = await saveUpdatedState({
|
|
1083
|
-
state: nextState,
|
|
1084
|
-
provider: options.provider,
|
|
1085
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1086
|
-
nowUnixMs: options.nowUnixMs,
|
|
1087
|
-
paths: options.paths,
|
|
1088
|
-
});
|
|
1089
|
-
throw new Error("wallet_field_create_tx1_broadcast_unknown");
|
|
1090
|
-
}
|
|
1091
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
|
|
1092
|
-
nextState = updateFieldFamilyState({
|
|
1093
|
-
state: nextState,
|
|
1094
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
|
|
1095
|
-
status: "canceled",
|
|
1096
|
-
currentStep: "tx1",
|
|
1097
|
-
nowUnixMs: options.nowUnixMs,
|
|
1098
|
-
tx1: {
|
|
1099
|
-
status: "canceled",
|
|
1100
|
-
attemptedTxid: options.built.txid,
|
|
1101
|
-
attemptedWtxid: options.built.wtxid,
|
|
1102
|
-
temporaryBuilderLockedOutpoints: [],
|
|
1103
|
-
rawHex: options.built.rawHex,
|
|
1104
|
-
},
|
|
1105
|
-
});
|
|
1106
|
-
nextState = await saveUpdatedState({
|
|
1107
|
-
state: nextState,
|
|
1108
|
-
provider: options.provider,
|
|
1109
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1110
|
-
nowUnixMs: options.nowUnixMs,
|
|
1111
|
-
paths: options.paths,
|
|
1112
|
-
});
|
|
1113
|
-
throw error;
|
|
1114
|
-
}
|
|
1115
|
-
}
|
|
1116
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
|
|
1117
|
-
nextState = updateFieldFamilyState({
|
|
1118
|
-
state: nextState,
|
|
1119
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
|
|
1120
|
-
status: "live",
|
|
1121
|
-
currentStep: "tx1",
|
|
1122
|
-
nowUnixMs: options.nowUnixMs,
|
|
1123
|
-
tx1: {
|
|
1124
|
-
status: "live",
|
|
1125
|
-
attemptedTxid: options.built.txid,
|
|
1126
|
-
attemptedWtxid: options.built.wtxid,
|
|
1127
|
-
temporaryBuilderLockedOutpoints: [],
|
|
1128
|
-
rawHex: options.built.rawHex,
|
|
1129
|
-
},
|
|
1130
|
-
});
|
|
1131
|
-
nextState = await saveUpdatedState({
|
|
1132
|
-
state: nextState,
|
|
1133
|
-
provider: options.provider,
|
|
1134
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1135
|
-
nowUnixMs: options.nowUnixMs,
|
|
1136
|
-
paths: options.paths,
|
|
1137
|
-
});
|
|
1138
|
-
return {
|
|
1139
|
-
state: nextState,
|
|
1140
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
|
|
1141
|
-
...options.family,
|
|
1142
|
-
status: "live",
|
|
1143
|
-
},
|
|
1144
|
-
};
|
|
1145
|
-
}
|
|
1146
|
-
async function sendFamilyTx2(options) {
|
|
1147
|
-
let nextState = updateFieldFamilyState({
|
|
1148
|
-
state: options.state,
|
|
1149
|
-
family: options.family,
|
|
1150
|
-
status: "broadcasting",
|
|
1151
|
-
currentStep: "tx2",
|
|
1152
|
-
nowUnixMs: options.nowUnixMs,
|
|
1153
|
-
tx2: {
|
|
1154
|
-
status: "broadcasting",
|
|
1155
|
-
attemptedTxid: options.built.txid,
|
|
1156
|
-
attemptedWtxid: options.built.wtxid,
|
|
1157
|
-
temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
|
|
1158
|
-
rawHex: options.built.rawHex,
|
|
1159
|
-
},
|
|
1160
|
-
});
|
|
1161
|
-
nextState = await saveUpdatedState({
|
|
1162
|
-
state: nextState,
|
|
1163
|
-
provider: options.provider,
|
|
1164
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1165
|
-
nowUnixMs: options.nowUnixMs,
|
|
1166
|
-
paths: options.paths,
|
|
1167
|
-
});
|
|
1168
|
-
try {
|
|
1169
|
-
await options.rpc.sendRawTransaction(options.built.rawHex);
|
|
1170
|
-
}
|
|
1171
|
-
catch (error) {
|
|
1172
|
-
if (!isAlreadyAcceptedError(error)) {
|
|
1173
|
-
if (isBroadcastUnknownError(error)) {
|
|
1174
|
-
nextState = updateFieldFamilyState({
|
|
1175
|
-
state: nextState,
|
|
1176
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
|
|
1177
|
-
status: "broadcast-unknown",
|
|
1178
|
-
currentStep: "tx2",
|
|
1179
|
-
nowUnixMs: options.nowUnixMs,
|
|
1180
|
-
tx2: {
|
|
1181
|
-
status: "broadcast-unknown",
|
|
1182
|
-
attemptedTxid: options.built.txid,
|
|
1183
|
-
attemptedWtxid: options.built.wtxid,
|
|
1184
|
-
temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
|
|
1185
|
-
rawHex: options.built.rawHex,
|
|
1186
|
-
},
|
|
1187
|
-
});
|
|
1188
|
-
nextState = await saveUpdatedState({
|
|
1189
|
-
state: nextState,
|
|
1190
|
-
provider: options.provider,
|
|
1191
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1192
|
-
nowUnixMs: options.nowUnixMs,
|
|
1193
|
-
paths: options.paths,
|
|
1194
|
-
});
|
|
1195
|
-
throw new Error("wallet_field_create_tx2_broadcast_unknown");
|
|
1196
|
-
}
|
|
1197
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
|
|
1198
|
-
nextState = updateFieldFamilyState({
|
|
1199
|
-
state: nextState,
|
|
1200
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
|
|
1201
|
-
status: "live",
|
|
1202
|
-
currentStep: "tx1",
|
|
1203
|
-
nowUnixMs: options.nowUnixMs,
|
|
1204
|
-
tx2: {
|
|
1205
|
-
status: "canceled",
|
|
1206
|
-
attemptedTxid: options.built.txid,
|
|
1207
|
-
attemptedWtxid: options.built.wtxid,
|
|
1208
|
-
temporaryBuilderLockedOutpoints: [],
|
|
1209
|
-
rawHex: options.built.rawHex,
|
|
1210
|
-
},
|
|
1211
|
-
});
|
|
1212
|
-
nextState = await saveUpdatedState({
|
|
1213
|
-
state: nextState,
|
|
1214
|
-
provider: options.provider,
|
|
1215
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1216
|
-
nowUnixMs: options.nowUnixMs,
|
|
1217
|
-
paths: options.paths,
|
|
1218
|
-
});
|
|
1219
|
-
throw error;
|
|
1220
|
-
}
|
|
1221
|
-
}
|
|
1222
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
|
|
1223
|
-
nextState = updateFieldFamilyState({
|
|
1224
|
-
state: nextState,
|
|
1225
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
|
|
1226
|
-
status: "live",
|
|
1227
|
-
currentStep: "tx2",
|
|
1228
|
-
nowUnixMs: options.nowUnixMs,
|
|
1229
|
-
tx2: {
|
|
1230
|
-
status: "live",
|
|
1231
|
-
attemptedTxid: options.built.txid,
|
|
1232
|
-
attemptedWtxid: options.built.wtxid,
|
|
1233
|
-
temporaryBuilderLockedOutpoints: [],
|
|
1234
|
-
rawHex: options.built.rawHex,
|
|
1235
|
-
},
|
|
1236
|
-
});
|
|
1237
|
-
nextState = await saveUpdatedState({
|
|
1238
|
-
state: nextState,
|
|
1239
|
-
provider: options.provider,
|
|
1240
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1241
|
-
nowUnixMs: options.nowUnixMs,
|
|
1242
|
-
paths: options.paths,
|
|
1243
|
-
});
|
|
1244
|
-
return {
|
|
1245
|
-
state: nextState,
|
|
1246
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
|
|
1247
|
-
...options.family,
|
|
1248
|
-
status: "live",
|
|
1249
|
-
},
|
|
1250
|
-
};
|
|
1251
|
-
}
|
|
1252
651
|
async function submitStandaloneFieldMutation(options) {
|
|
1253
652
|
if (!options.prompter.isInteractive && options.assumeYes !== true) {
|
|
1254
653
|
throw new Error(`${options.errorPrefix}_requires_tty`);
|
|
@@ -1283,82 +682,91 @@ async function submitStandaloneFieldMutation(options) {
|
|
|
1283
682
|
normalizedDomainName,
|
|
1284
683
|
normalizedFieldName,
|
|
1285
684
|
]);
|
|
685
|
+
const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
|
|
686
|
+
dataDir: options.dataDir,
|
|
687
|
+
chain: "main",
|
|
688
|
+
startHeight: 0,
|
|
689
|
+
walletRootId: operation.state.walletRootId,
|
|
690
|
+
});
|
|
691
|
+
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
692
|
+
const walletName = operation.state.managedCoreWallet.walletName;
|
|
693
|
+
const feeSelection = await resolveWalletMutationFeeSelection({
|
|
694
|
+
rpc,
|
|
695
|
+
feeRateSatVb: options.feeRateSatVb ?? null,
|
|
696
|
+
});
|
|
1286
697
|
const existingMutation = findPendingMutationByIntent(operation.state, intentFingerprintHex);
|
|
698
|
+
let workingState = operation.state;
|
|
699
|
+
let replacementFixedInputs = null;
|
|
1287
700
|
if (existingMutation !== null) {
|
|
1288
|
-
const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
|
|
1289
|
-
dataDir: options.dataDir,
|
|
1290
|
-
chain: "main",
|
|
1291
|
-
startHeight: 0,
|
|
1292
|
-
walletRootId: operation.state.walletRootId,
|
|
1293
|
-
});
|
|
1294
|
-
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
1295
|
-
const walletName = operation.state.managedCoreWallet.walletName;
|
|
1296
701
|
const reconciled = await reconcilePendingFieldMutation({
|
|
1297
702
|
state: operation.state,
|
|
1298
703
|
mutation: existingMutation,
|
|
1299
704
|
provider,
|
|
1300
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1301
705
|
nowUnixMs,
|
|
1302
706
|
paths,
|
|
1303
707
|
rpc,
|
|
1304
708
|
walletName,
|
|
1305
709
|
context: readContext,
|
|
1306
710
|
});
|
|
711
|
+
workingState = reconciled.state;
|
|
1307
712
|
if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
format: reconciled.mutation.fieldFormat ?? existingObservedField?.format ?? null,
|
|
1317
|
-
status: reconciled.resolution,
|
|
1318
|
-
reusedExisting: true,
|
|
1319
|
-
resolved: createResolvedFieldSummary({
|
|
1320
|
-
sender: operation.sender,
|
|
1321
|
-
senderSelector: operation.senderSelector,
|
|
713
|
+
const reuse = await resolvePendingMutationReuseDecision({
|
|
714
|
+
rpc,
|
|
715
|
+
walletName,
|
|
716
|
+
mutation: reconciled.mutation,
|
|
717
|
+
nextFeeSelection: feeSelection,
|
|
718
|
+
});
|
|
719
|
+
if (reuse.reuseExisting) {
|
|
720
|
+
return {
|
|
1322
721
|
kind: options.kind,
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
722
|
+
domainName: normalizedDomainName,
|
|
723
|
+
fieldName: normalizedFieldName,
|
|
724
|
+
fieldId: reconciled.mutation.fieldId ?? existingObservedField?.fieldId ?? null,
|
|
725
|
+
txid: reconciled.mutation.attemptedTxid ?? "unknown",
|
|
726
|
+
permanent: reconciled.mutation.fieldPermanent ?? existingObservedField?.permanent ?? null,
|
|
727
|
+
format: reconciled.mutation.fieldFormat ?? existingObservedField?.format ?? null,
|
|
728
|
+
status: reconciled.resolution,
|
|
729
|
+
reusedExisting: true,
|
|
730
|
+
resolved: createResolvedFieldSummary({
|
|
731
|
+
sender: operation.sender,
|
|
732
|
+
senderSelector: operation.senderSelector,
|
|
733
|
+
kind: options.kind,
|
|
734
|
+
value: createResolvedFieldValueFromStoredData(options.kind, reconciled.mutation.fieldFormat ?? existingObservedField?.format ?? null, reconciled.mutation.fieldValueHex),
|
|
735
|
+
}),
|
|
736
|
+
fees: reuse.fees,
|
|
737
|
+
};
|
|
738
|
+
}
|
|
739
|
+
replacementFixedInputs = reuse.replacementFixedInputs;
|
|
1327
740
|
}
|
|
1328
741
|
if (reconciled.resolution === "repair-required") {
|
|
1329
742
|
throw new Error(`${options.errorPrefix}_repair_required`);
|
|
1330
743
|
}
|
|
1331
744
|
}
|
|
1332
745
|
await options.confirm(operation);
|
|
1333
|
-
const planned = await options.createMutation(operation, existingMutation);
|
|
1334
|
-
let nextState = upsertPendingMutation(
|
|
746
|
+
const planned = await options.createMutation(operation, existingMutation, feeSelection);
|
|
747
|
+
let nextState = upsertPendingMutation(workingState, planned.mutation);
|
|
1335
748
|
nextState = await saveUpdatedState({
|
|
1336
749
|
state: nextState,
|
|
1337
750
|
provider,
|
|
1338
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1339
751
|
nowUnixMs,
|
|
1340
752
|
paths,
|
|
1341
753
|
});
|
|
1342
|
-
const
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
754
|
+
const fieldPlan = buildAnchoredFieldPlan({
|
|
755
|
+
state: nextState,
|
|
756
|
+
allUtxos: await rpc.listUnspent(walletName, 1),
|
|
757
|
+
sender: operation.sender,
|
|
758
|
+
opReturnData: planned.opReturnData,
|
|
759
|
+
errorPrefix: options.errorPrefix,
|
|
1347
760
|
});
|
|
1348
|
-
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
1349
|
-
const walletName = operation.state.managedCoreWallet.walletName;
|
|
1350
761
|
const built = await buildFieldTransaction({
|
|
1351
762
|
rpc,
|
|
1352
763
|
walletName,
|
|
1353
764
|
state: nextState,
|
|
1354
|
-
plan:
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
opReturnData: planned.opReturnData,
|
|
1360
|
-
errorPrefix: options.errorPrefix,
|
|
1361
|
-
}),
|
|
765
|
+
plan: {
|
|
766
|
+
...fieldPlan,
|
|
767
|
+
fixedInputs: mergeFixedWalletInputs(fieldPlan.fixedInputs, replacementFixedInputs),
|
|
768
|
+
},
|
|
769
|
+
feeRateSatVb: feeSelection.feeRateSatVb,
|
|
1362
770
|
});
|
|
1363
771
|
const final = await sendStandaloneMutation({
|
|
1364
772
|
rpc,
|
|
@@ -1368,7 +776,6 @@ async function submitStandaloneFieldMutation(options) {
|
|
|
1368
776
|
mutation: nextState.pendingMutations.find((mutation) => mutation.intentFingerprintHex === planned.mutation.intentFingerprintHex),
|
|
1369
777
|
state: nextState,
|
|
1370
778
|
provider,
|
|
1371
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1372
779
|
nowUnixMs,
|
|
1373
780
|
paths,
|
|
1374
781
|
errorPrefix: options.errorPrefix,
|
|
@@ -1379,7 +786,6 @@ async function submitStandaloneFieldMutation(options) {
|
|
|
1379
786
|
fieldName: normalizedFieldName,
|
|
1380
787
|
fieldId: final.mutation.fieldId ?? existingObservedField?.fieldId ?? null,
|
|
1381
788
|
txid: final.mutation.attemptedTxid ?? built.txid,
|
|
1382
|
-
family: false,
|
|
1383
789
|
permanent: final.mutation.fieldPermanent ?? existingObservedField?.permanent ?? null,
|
|
1384
790
|
format: final.mutation.fieldFormat ?? existingObservedField?.format ?? null,
|
|
1385
791
|
status: "live",
|
|
@@ -1388,237 +794,11 @@ async function submitStandaloneFieldMutation(options) {
|
|
|
1388
794
|
sender: operation.sender,
|
|
1389
795
|
senderSelector: operation.senderSelector,
|
|
1390
796
|
kind: options.kind,
|
|
1391
|
-
family: false,
|
|
1392
797
|
value: createResolvedFieldValueFromStoredData(options.kind, planned.mutation.fieldFormat ?? existingObservedField?.format ?? null, planned.mutation.fieldValueHex),
|
|
1393
798
|
}),
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
await readContext.close();
|
|
1398
|
-
await miningPreemption.release();
|
|
1399
|
-
}
|
|
1400
|
-
}
|
|
1401
|
-
finally {
|
|
1402
|
-
await controlLock.release();
|
|
1403
|
-
}
|
|
1404
|
-
}
|
|
1405
|
-
async function submitFieldCreateFamily(options) {
|
|
1406
|
-
if (!options.prompter.isInteractive && options.assumeYes !== true) {
|
|
1407
|
-
throw new Error("wallet_field_create_requires_tty");
|
|
1408
|
-
}
|
|
1409
|
-
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
1410
|
-
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1411
|
-
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1412
|
-
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
1413
|
-
purpose: "wallet_field_create",
|
|
1414
|
-
walletRootId: null,
|
|
1415
|
-
});
|
|
1416
|
-
try {
|
|
1417
|
-
const miningPreemption = await pauseMiningForWalletMutation({
|
|
1418
|
-
paths,
|
|
1419
|
-
reason: "wallet_field_create",
|
|
1420
|
-
});
|
|
1421
|
-
const readContext = await (options.openReadContext ?? openWalletReadContext)({
|
|
1422
|
-
dataDir: options.dataDir,
|
|
1423
|
-
databasePath: options.databasePath,
|
|
1424
|
-
secretProvider: provider,
|
|
1425
|
-
walletControlLockHeld: true,
|
|
1426
|
-
paths,
|
|
1427
|
-
});
|
|
1428
|
-
try {
|
|
1429
|
-
const normalizedDomainName = normalizeDomainName(options.domainName);
|
|
1430
|
-
const normalizedFieldName = normalizeFieldName(options.fieldName);
|
|
1431
|
-
const operation = resolveAnchoredFieldOperation(readContext, normalizedDomainName, "wallet_field_create");
|
|
1432
|
-
const existingField = getObservedFieldState(readContext, normalizedDomainName, normalizedFieldName);
|
|
1433
|
-
if (existingField !== null) {
|
|
1434
|
-
throw new Error("wallet_field_create_field_exists");
|
|
1435
|
-
}
|
|
1436
|
-
if (operation.chainDomain.nextFieldId === 0xffff_ffff) {
|
|
1437
|
-
throw new Error("wallet_field_create_field_id_exhausted");
|
|
1438
|
-
}
|
|
1439
|
-
if (hex(operation.chainDomain.delegate) !== null) {
|
|
1440
|
-
throw new Error("wallet_field_create_delegate_blocks_same_block_family");
|
|
1441
|
-
}
|
|
1442
|
-
const senderBalance = getBalance(operation.readContext.snapshot.state, Buffer.from(operation.sender.scriptPubKeyHex, "hex"));
|
|
1443
|
-
if (senderBalance < 101n) {
|
|
1444
|
-
throw new Error("wallet_field_create_insufficient_cog");
|
|
1445
|
-
}
|
|
1446
|
-
const intentFingerprintHex = createIntentFingerprint([
|
|
1447
|
-
"field-create",
|
|
1448
|
-
operation.state.walletRootId,
|
|
1449
|
-
normalizedDomainName,
|
|
1450
|
-
normalizedFieldName,
|
|
1451
|
-
options.permanent ? 1 : 0,
|
|
1452
|
-
options.value.format,
|
|
1453
|
-
options.value.valueHex,
|
|
1454
|
-
]);
|
|
1455
|
-
const existingFamily = findFieldFamilyByIntent(operation.state, intentFingerprintHex);
|
|
1456
|
-
const conflictingFamily = findActiveFieldFamilyByDomain(operation.state, normalizedDomainName);
|
|
1457
|
-
if (conflictingFamily !== null && conflictingFamily.intentFingerprintHex !== intentFingerprintHex) {
|
|
1458
|
-
throw new Error("wallet_field_create_family_already_active");
|
|
1459
|
-
}
|
|
1460
|
-
const conflictingCreate = findActiveFieldCreateMutationByDomain(operation.state, normalizedDomainName, intentFingerprintHex);
|
|
1461
|
-
if (conflictingCreate !== null) {
|
|
1462
|
-
throw new Error("wallet_field_create_registration_already_pending");
|
|
1463
|
-
}
|
|
1464
|
-
const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
|
|
1465
|
-
dataDir: options.dataDir,
|
|
1466
|
-
chain: "main",
|
|
1467
|
-
startHeight: 0,
|
|
1468
|
-
walletRootId: operation.state.walletRootId,
|
|
1469
|
-
});
|
|
1470
|
-
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
1471
|
-
const walletName = operation.state.managedCoreWallet.walletName;
|
|
1472
|
-
let workingState = operation.state;
|
|
1473
|
-
let resumedFamily = null;
|
|
1474
|
-
if (existingFamily !== null) {
|
|
1475
|
-
const reconciled = await reconcileFieldFamily({
|
|
1476
|
-
state: workingState,
|
|
1477
|
-
family: existingFamily,
|
|
1478
|
-
provider,
|
|
1479
|
-
nowUnixMs,
|
|
1480
|
-
paths,
|
|
1481
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1482
|
-
rpc,
|
|
1483
|
-
walletName,
|
|
1484
|
-
context: readContext,
|
|
1485
|
-
});
|
|
1486
|
-
workingState = reconciled.state;
|
|
1487
|
-
if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
|
|
1488
|
-
return {
|
|
1489
|
-
kind: "field-create",
|
|
1490
|
-
domainName: normalizedDomainName,
|
|
1491
|
-
fieldName: normalizedFieldName,
|
|
1492
|
-
fieldId: reconciled.family.expectedFieldId ?? null,
|
|
1493
|
-
txid: reconciled.family.tx2?.attemptedTxid ?? reconciled.family.tx1?.attemptedTxid ?? "unknown",
|
|
1494
|
-
tx1Txid: reconciled.family.tx1?.attemptedTxid ?? null,
|
|
1495
|
-
tx2Txid: reconciled.family.tx2?.attemptedTxid ?? null,
|
|
1496
|
-
family: true,
|
|
1497
|
-
permanent: reconciled.family.fieldPermanent ?? null,
|
|
1498
|
-
format: reconciled.family.fieldFormat ?? null,
|
|
1499
|
-
status: reconciled.resolution,
|
|
1500
|
-
reusedExisting: true,
|
|
1501
|
-
resolved: createResolvedFieldSummary({
|
|
1502
|
-
sender: operation.sender,
|
|
1503
|
-
senderSelector: operation.senderSelector,
|
|
1504
|
-
kind: "field-create",
|
|
1505
|
-
family: true,
|
|
1506
|
-
value: createResolvedFieldValueFromStoredData("field-create", reconciled.family.fieldFormat ?? null, reconciled.family.fieldValueHex),
|
|
1507
|
-
}),
|
|
1508
|
-
};
|
|
1509
|
-
}
|
|
1510
|
-
if (reconciled.resolution === "repair-required") {
|
|
1511
|
-
throw new Error("wallet_field_create_repair_required");
|
|
1512
|
-
}
|
|
1513
|
-
if (reconciled.resolution === "ready-for-tx2") {
|
|
1514
|
-
resumedFamily = reconciled.family;
|
|
1515
|
-
}
|
|
1516
|
-
}
|
|
1517
|
-
if (resumedFamily === null) {
|
|
1518
|
-
await confirmFieldCreate(options.prompter, {
|
|
1519
|
-
domainName: normalizedDomainName,
|
|
1520
|
-
fieldName: normalizedFieldName,
|
|
1521
|
-
permanent: options.permanent,
|
|
1522
|
-
value: options.value,
|
|
1523
|
-
sender: createResolvedFieldSenderSummary(operation.sender, operation.senderSelector),
|
|
1524
|
-
assumeYes: options.assumeYes,
|
|
1525
|
-
});
|
|
1526
|
-
let nextState = upsertProactiveFamily(workingState, createFieldFamilyRecord({
|
|
1527
|
-
domainName: normalizedDomainName,
|
|
1528
|
-
domainId: operation.chainDomain.domainId,
|
|
1529
|
-
fieldName: normalizedFieldName,
|
|
1530
|
-
expectedFieldId: operation.chainDomain.nextFieldId,
|
|
1531
|
-
sender: operation.sender,
|
|
1532
|
-
permanence: options.permanent,
|
|
1533
|
-
format: options.value.format,
|
|
1534
|
-
valueHex: options.value.valueHex,
|
|
1535
|
-
intentFingerprintHex,
|
|
1536
|
-
nowUnixMs,
|
|
1537
|
-
existing: existingFamily,
|
|
1538
|
-
}));
|
|
1539
|
-
nextState = await saveUpdatedState({
|
|
1540
|
-
state: nextState,
|
|
1541
|
-
provider,
|
|
1542
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1543
|
-
nowUnixMs,
|
|
1544
|
-
paths,
|
|
1545
|
-
});
|
|
1546
|
-
const family = findFieldFamilyByIntent(nextState, intentFingerprintHex);
|
|
1547
|
-
const tx1 = await buildFieldTransaction({
|
|
1548
|
-
rpc,
|
|
1549
|
-
walletName,
|
|
1550
|
-
state: nextState,
|
|
1551
|
-
plan: buildAnchoredFieldPlan({
|
|
1552
|
-
state: nextState,
|
|
1553
|
-
allUtxos: await rpc.listUnspent(walletName, 1),
|
|
1554
|
-
sender: operation.sender,
|
|
1555
|
-
anchorOutpoint: operation.anchorOutpoint,
|
|
1556
|
-
opReturnData: serializeFieldReg(operation.chainDomain.domainId, options.permanent, normalizedFieldName).opReturnData,
|
|
1557
|
-
errorPrefix: "wallet_field_create_tx1",
|
|
1558
|
-
}),
|
|
1559
|
-
});
|
|
1560
|
-
const afterTx1 = await sendFamilyTx1({
|
|
1561
|
-
rpc,
|
|
1562
|
-
walletName,
|
|
1563
|
-
snapshotHeight: readContext.snapshot?.tip?.height ?? null,
|
|
1564
|
-
built: tx1,
|
|
1565
|
-
family,
|
|
1566
|
-
state: nextState,
|
|
1567
|
-
provider,
|
|
1568
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1569
|
-
nowUnixMs,
|
|
1570
|
-
paths,
|
|
1571
|
-
});
|
|
1572
|
-
workingState = afterTx1.state;
|
|
1573
|
-
resumedFamily = afterTx1.family;
|
|
1574
|
-
}
|
|
1575
|
-
const tx1Txid = resumedFamily.tx1?.attemptedTxid;
|
|
1576
|
-
if (tx1Txid == null) {
|
|
1577
|
-
throw new Error("wallet_field_create_tx1_missing");
|
|
1578
|
-
}
|
|
1579
|
-
await rpc.getRawTransaction(tx1Txid, true);
|
|
1580
|
-
const tx2 = await buildFieldTransaction({
|
|
1581
|
-
rpc,
|
|
1582
|
-
walletName,
|
|
1583
|
-
state: workingState,
|
|
1584
|
-
plan: buildFieldFamilyTx2Plan({
|
|
1585
|
-
state: workingState,
|
|
1586
|
-
allUtxos: await rpc.listUnspent(walletName, 1),
|
|
1587
|
-
sender: operation.sender,
|
|
1588
|
-
tx1Txid,
|
|
1589
|
-
opReturnData: serializeDataUpdate(operation.chainDomain.domainId, resumedFamily.expectedFieldId ?? operation.chainDomain.nextFieldId, options.value.format, options.value.value).opReturnData,
|
|
1590
|
-
}),
|
|
1591
|
-
});
|
|
1592
|
-
const final = await sendFamilyTx2({
|
|
1593
|
-
rpc,
|
|
1594
|
-
walletName,
|
|
1595
|
-
built: tx2,
|
|
1596
|
-
family: resumedFamily,
|
|
1597
|
-
state: workingState,
|
|
1598
|
-
provider,
|
|
1599
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1600
|
-
nowUnixMs,
|
|
1601
|
-
paths,
|
|
1602
|
-
});
|
|
1603
|
-
return {
|
|
1604
|
-
kind: "field-create",
|
|
1605
|
-
domainName: normalizedDomainName,
|
|
1606
|
-
fieldName: normalizedFieldName,
|
|
1607
|
-
fieldId: final.family.expectedFieldId ?? null,
|
|
1608
|
-
txid: final.family.tx2?.attemptedTxid ?? tx2.txid,
|
|
1609
|
-
tx1Txid,
|
|
1610
|
-
tx2Txid: final.family.tx2?.attemptedTxid ?? tx2.txid,
|
|
1611
|
-
family: true,
|
|
1612
|
-
permanent: final.family.fieldPermanent ?? null,
|
|
1613
|
-
format: final.family.fieldFormat ?? null,
|
|
1614
|
-
status: "live",
|
|
1615
|
-
reusedExisting: existingFamily !== null,
|
|
1616
|
-
resolved: createResolvedFieldSummary({
|
|
1617
|
-
sender: operation.sender,
|
|
1618
|
-
senderSelector: operation.senderSelector,
|
|
1619
|
-
kind: "field-create",
|
|
1620
|
-
family: true,
|
|
1621
|
-
value: createResolvedFieldValueSummary(options.value.format, options.value.value),
|
|
799
|
+
fees: createBuiltWalletMutationFeeSummary({
|
|
800
|
+
selection: feeSelection,
|
|
801
|
+
built,
|
|
1622
802
|
}),
|
|
1623
803
|
};
|
|
1624
804
|
}
|
|
@@ -1632,15 +812,7 @@ async function submitFieldCreateFamily(options) {
|
|
|
1632
812
|
}
|
|
1633
813
|
}
|
|
1634
814
|
export async function createField(options) {
|
|
1635
|
-
const normalizedSource = options.source == null ? null : await loadFieldValue(options.source);
|
|
1636
815
|
const permanent = options.permanent ?? false;
|
|
1637
|
-
if (normalizedSource !== null) {
|
|
1638
|
-
return submitFieldCreateFamily({
|
|
1639
|
-
...options,
|
|
1640
|
-
permanent,
|
|
1641
|
-
value: normalizedSource,
|
|
1642
|
-
});
|
|
1643
|
-
}
|
|
1644
816
|
return submitStandaloneFieldMutation({
|
|
1645
817
|
kind: "field-create",
|
|
1646
818
|
errorPrefix: "wallet_field_create",
|
|
@@ -1656,7 +828,7 @@ export async function createField(options) {
|
|
|
1656
828
|
openReadContext: options.openReadContext,
|
|
1657
829
|
attachService: options.attachService,
|
|
1658
830
|
rpcFactory: options.rpcFactory,
|
|
1659
|
-
async createMutation(operation, existing) {
|
|
831
|
+
async createMutation(operation, existing, feeSelection) {
|
|
1660
832
|
const existingField = getObservedFieldState(operation.readContext, normalizeDomainName(options.domainName), normalizeFieldName(options.fieldName));
|
|
1661
833
|
if (existingField !== null) {
|
|
1662
834
|
throw new Error("wallet_field_create_field_exists");
|
|
@@ -1677,10 +849,6 @@ export async function createField(options) {
|
|
|
1677
849
|
normalizedFieldName,
|
|
1678
850
|
permanent ? 1 : 0,
|
|
1679
851
|
]);
|
|
1680
|
-
const conflictFamily = findActiveFieldFamilyByDomain(operation.state, normalizedDomainName);
|
|
1681
|
-
if (conflictFamily !== null && conflictFamily.intentFingerprintHex !== intentFingerprintHex) {
|
|
1682
|
-
throw new Error("wallet_field_create_family_already_active");
|
|
1683
|
-
}
|
|
1684
852
|
const conflictCreate = findActiveFieldCreateMutationByDomain(operation.state, normalizedDomainName, intentFingerprintHex);
|
|
1685
853
|
if (conflictCreate !== null) {
|
|
1686
854
|
throw new Error("wallet_field_create_registration_already_pending");
|
|
@@ -1694,6 +862,7 @@ export async function createField(options) {
|
|
|
1694
862
|
sender: operation.sender,
|
|
1695
863
|
intentFingerprintHex,
|
|
1696
864
|
nowUnixMs: options.nowUnixMs ?? Date.now(),
|
|
865
|
+
feeSelection,
|
|
1697
866
|
existing,
|
|
1698
867
|
fieldId: operation.chainDomain.nextFieldId,
|
|
1699
868
|
fieldPermanent: permanent,
|
|
@@ -1705,7 +874,6 @@ export async function createField(options) {
|
|
|
1705
874
|
domainName: normalizeDomainName(options.domainName),
|
|
1706
875
|
fieldName: normalizeFieldName(options.fieldName),
|
|
1707
876
|
permanent,
|
|
1708
|
-
value: null,
|
|
1709
877
|
sender: createResolvedFieldSenderSummary(operation.sender, operation.senderSelector),
|
|
1710
878
|
assumeYes: options.assumeYes,
|
|
1711
879
|
});
|
|
@@ -1729,7 +897,7 @@ export async function setField(options) {
|
|
|
1729
897
|
openReadContext: options.openReadContext,
|
|
1730
898
|
attachService: options.attachService,
|
|
1731
899
|
rpcFactory: options.rpcFactory,
|
|
1732
|
-
async createMutation(operation, existing) {
|
|
900
|
+
async createMutation(operation, existing, feeSelection) {
|
|
1733
901
|
const normalizedDomainName = normalizeDomainName(options.domainName);
|
|
1734
902
|
const normalizedFieldName = normalizeFieldName(options.fieldName);
|
|
1735
903
|
const observedField = getObservedFieldState(operation.readContext, normalizedDomainName, normalizedFieldName);
|
|
@@ -1760,6 +928,7 @@ export async function setField(options) {
|
|
|
1760
928
|
sender: operation.sender,
|
|
1761
929
|
intentFingerprintHex,
|
|
1762
930
|
nowUnixMs: options.nowUnixMs ?? Date.now(),
|
|
931
|
+
feeSelection,
|
|
1763
932
|
existing,
|
|
1764
933
|
fieldId: observedField.fieldId,
|
|
1765
934
|
fieldPermanent: observedField.permanent,
|
|
@@ -1803,7 +972,7 @@ export async function clearField(options) {
|
|
|
1803
972
|
openReadContext: options.openReadContext,
|
|
1804
973
|
attachService: options.attachService,
|
|
1805
974
|
rpcFactory: options.rpcFactory,
|
|
1806
|
-
async createMutation(operation, existing) {
|
|
975
|
+
async createMutation(operation, existing, feeSelection) {
|
|
1807
976
|
const normalizedDomainName = normalizeDomainName(options.domainName);
|
|
1808
977
|
const normalizedFieldName = normalizeFieldName(options.fieldName);
|
|
1809
978
|
const observedField = getObservedFieldState(operation.readContext, normalizedDomainName, normalizedFieldName);
|
|
@@ -1828,6 +997,7 @@ export async function clearField(options) {
|
|
|
1828
997
|
sender: operation.sender,
|
|
1829
998
|
intentFingerprintHex,
|
|
1830
999
|
nowUnixMs: options.nowUnixMs ?? Date.now(),
|
|
1000
|
+
feeSelection,
|
|
1831
1001
|
existing,
|
|
1832
1002
|
fieldId: observedField.fieldId,
|
|
1833
1003
|
fieldPermanent: observedField.permanent,
|