@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.
- 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 +6 -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 -12
- package/dist/wallet/coin-control.js +100 -428
- 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 +116 -13
- package/dist/wallet/mining/runner.js +885 -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 -1250
- 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 +61 -8
- package/dist/wallet/tx/common.js +266 -146
- 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 +83 -924
- 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,98 +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
|
-
const tx1FundingChangeInputs = findSpendableFundingInputsFromTransaction({
|
|
278
|
-
allUtxos: options.allUtxos,
|
|
279
|
-
txid: options.tx1Txid,
|
|
280
|
-
fundingScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
|
|
281
|
-
minConf: 0,
|
|
282
|
-
});
|
|
283
|
-
return {
|
|
284
|
-
sender: options.sender,
|
|
285
|
-
changeAddress: options.state.funding.address,
|
|
286
|
-
fixedInputs: [
|
|
287
|
-
{ txid: options.tx1Txid, vout: 1 },
|
|
288
|
-
...tx1FundingChangeInputs,
|
|
289
|
-
],
|
|
290
|
-
outputs: [
|
|
291
|
-
{ data: Buffer.from(options.opReturnData).toString("hex") },
|
|
292
|
-
{ [options.sender.address]: satsToBtcNumber(BigInt(options.state.anchorValueSats)) },
|
|
293
|
-
],
|
|
294
|
-
changePosition: 2,
|
|
295
|
-
expectedOpReturnScriptHex: encodeOpReturnScript(options.opReturnData),
|
|
296
|
-
expectedAnchorScriptHex: options.sender.scriptPubKeyHex,
|
|
297
|
-
expectedAnchorValueSats: BigInt(options.state.anchorValueSats),
|
|
298
|
-
allowedFundingScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
|
|
299
|
-
eligibleFundingOutpointKeys: new Set(fundingUtxos.map((entry) => outpointKey({ txid: entry.txid, vout: entry.vout }))),
|
|
300
|
-
errorPrefix: "wallet_field_create_tx2",
|
|
301
|
-
};
|
|
302
|
-
}
|
|
303
187
|
function validateFieldDraft(decoded, funded, plan) {
|
|
304
188
|
const inputs = decoded.tx.vin;
|
|
305
189
|
const outputs = decoded.tx.vout;
|
|
306
190
|
if (inputs.length === 0) {
|
|
307
191
|
throw new Error(`${plan.errorPrefix}_missing_sender_input`);
|
|
308
192
|
}
|
|
309
|
-
assertFixedInputPrefixMatches(inputs, plan.fixedInputs, `${plan.errorPrefix}_sender_input_mismatch`);
|
|
310
|
-
if (getDecodedInputScriptPubKeyHex(decoded, 0) !== plan.sender.scriptPubKeyHex) {
|
|
311
|
-
throw new Error(`${plan.errorPrefix}_sender_input_mismatch`);
|
|
312
|
-
}
|
|
313
|
-
assertFundingInputsAfterFixedPrefix({
|
|
314
|
-
decoded,
|
|
315
|
-
fixedInputs: plan.fixedInputs,
|
|
316
|
-
allowedFundingScriptPubKeyHex: plan.allowedFundingScriptPubKeyHex,
|
|
317
|
-
eligibleFundingOutpointKeys: plan.eligibleFundingOutpointKeys,
|
|
318
|
-
errorCode: `${plan.errorPrefix}_unexpected_funding_input`,
|
|
319
|
-
});
|
|
320
193
|
if (outputs[0]?.scriptPubKey?.hex !== plan.expectedOpReturnScriptHex) {
|
|
321
194
|
throw new Error(`${plan.errorPrefix}_opreturn_mismatch`);
|
|
322
195
|
}
|
|
323
|
-
if (outputs[1]?.scriptPubKey?.hex !== plan.expectedAnchorScriptHex) {
|
|
324
|
-
throw new Error(`${plan.errorPrefix}_anchor_output_mismatch`);
|
|
325
|
-
}
|
|
326
|
-
if (valueToSats(outputs[1]?.value ?? 0) !== plan.expectedAnchorValueSats) {
|
|
327
|
-
throw new Error(`${plan.errorPrefix}_anchor_value_mismatch`);
|
|
328
|
-
}
|
|
329
196
|
if (funded.changepos === -1) {
|
|
330
|
-
if (outputs.length !==
|
|
197
|
+
if (outputs.length !== 1) {
|
|
331
198
|
throw new Error(`${plan.errorPrefix}_unexpected_output_count`);
|
|
332
199
|
}
|
|
333
200
|
return;
|
|
334
201
|
}
|
|
335
|
-
if (funded.changepos !== plan.changePosition || outputs.length !==
|
|
202
|
+
if (funded.changepos !== plan.changePosition || outputs.length !== 2) {
|
|
336
203
|
throw new Error(`${plan.errorPrefix}_change_position_mismatch`);
|
|
337
204
|
}
|
|
338
205
|
if (outputs[funded.changepos]?.scriptPubKey?.hex !== plan.allowedFundingScriptPubKeyHex) {
|
|
@@ -348,8 +215,8 @@ async function buildFieldTransaction(options) {
|
|
|
348
215
|
validateFundedDraft: validateFieldDraft,
|
|
349
216
|
finalizeErrorCode: `${options.plan.errorPrefix}_finalize_failed`,
|
|
350
217
|
mempoolRejectPrefix: `${options.plan.errorPrefix}_mempool_rejected`,
|
|
218
|
+
feeRate: options.feeRateSatVb,
|
|
351
219
|
availableFundingMinConf: options.availableFundingMinConf,
|
|
352
|
-
reserveCandidates: options.state.proactiveReserveOutpoints,
|
|
353
220
|
});
|
|
354
221
|
}
|
|
355
222
|
async function saveUpdatedState(options) {
|
|
@@ -361,7 +228,6 @@ async function saveUpdatedState(options) {
|
|
|
361
228
|
await saveWalletStatePreservingUnlock({
|
|
362
229
|
state: nextState,
|
|
363
230
|
provider: options.provider,
|
|
364
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
365
231
|
nowUnixMs: options.nowUnixMs,
|
|
366
232
|
paths: options.paths,
|
|
367
233
|
});
|
|
@@ -384,6 +250,7 @@ function createStandaloneFieldMutation(options) {
|
|
|
384
250
|
lastUpdatedAtUnixMs: options.nowUnixMs,
|
|
385
251
|
attemptedTxid: null,
|
|
386
252
|
attemptedWtxid: null,
|
|
253
|
+
...createWalletMutationFeeMetadata(options.feeSelection),
|
|
387
254
|
temporaryBuilderLockedOutpoints: [],
|
|
388
255
|
};
|
|
389
256
|
}
|
|
@@ -405,61 +272,10 @@ function createStandaloneFieldMutation(options) {
|
|
|
405
272
|
lastUpdatedAtUnixMs: options.nowUnixMs,
|
|
406
273
|
attemptedTxid: null,
|
|
407
274
|
attemptedWtxid: null,
|
|
275
|
+
...createWalletMutationFeeMetadata(options.feeSelection),
|
|
408
276
|
temporaryBuilderLockedOutpoints: [],
|
|
409
277
|
};
|
|
410
278
|
}
|
|
411
|
-
function createFieldFamilyRecord(options) {
|
|
412
|
-
if (options.existing !== null && options.existing !== undefined) {
|
|
413
|
-
return {
|
|
414
|
-
...options.existing,
|
|
415
|
-
type: "field",
|
|
416
|
-
status: "draft",
|
|
417
|
-
domainName: options.domainName,
|
|
418
|
-
domainId: options.domainId,
|
|
419
|
-
sourceSenderLocalIndex: options.sender.localIndex,
|
|
420
|
-
sourceSenderScriptPubKeyHex: options.sender.scriptPubKeyHex,
|
|
421
|
-
fieldName: options.fieldName,
|
|
422
|
-
expectedFieldId: options.expectedFieldId,
|
|
423
|
-
fieldPermanent: options.permanence,
|
|
424
|
-
fieldFormat: options.format,
|
|
425
|
-
fieldValueHex: options.valueHex,
|
|
426
|
-
currentStep: "tx1",
|
|
427
|
-
lastUpdatedAtUnixMs: options.nowUnixMs,
|
|
428
|
-
tx1: createFamilyTransactionRecord(),
|
|
429
|
-
tx2: createFamilyTransactionRecord(),
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
return {
|
|
433
|
-
familyId: randomBytes(12).toString("hex"),
|
|
434
|
-
type: "field",
|
|
435
|
-
status: "draft",
|
|
436
|
-
intentFingerprintHex: options.intentFingerprintHex,
|
|
437
|
-
createdAtUnixMs: options.nowUnixMs,
|
|
438
|
-
lastUpdatedAtUnixMs: options.nowUnixMs,
|
|
439
|
-
domainName: options.domainName,
|
|
440
|
-
domainId: options.domainId,
|
|
441
|
-
sourceSenderLocalIndex: options.sender.localIndex,
|
|
442
|
-
sourceSenderScriptPubKeyHex: options.sender.scriptPubKeyHex,
|
|
443
|
-
fieldName: options.fieldName,
|
|
444
|
-
expectedFieldId: options.expectedFieldId,
|
|
445
|
-
fieldPermanent: options.permanence,
|
|
446
|
-
fieldFormat: options.format,
|
|
447
|
-
fieldValueHex: options.valueHex,
|
|
448
|
-
currentStep: "tx1",
|
|
449
|
-
tx1: createFamilyTransactionRecord(),
|
|
450
|
-
tx2: createFamilyTransactionRecord(),
|
|
451
|
-
};
|
|
452
|
-
}
|
|
453
|
-
function updateFieldFamilyState(options) {
|
|
454
|
-
return upsertProactiveFamily(options.state, {
|
|
455
|
-
...options.family,
|
|
456
|
-
status: options.status,
|
|
457
|
-
currentStep: options.currentStep,
|
|
458
|
-
lastUpdatedAtUnixMs: options.nowUnixMs,
|
|
459
|
-
tx1: options.tx1 ?? options.family.tx1 ?? createFamilyTransactionRecord(),
|
|
460
|
-
tx2: options.tx2 ?? options.family.tx2 ?? createFamilyTransactionRecord(),
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
279
|
function getObservedFieldState(context, domainName, fieldName) {
|
|
464
280
|
if (context.snapshot === null) {
|
|
465
281
|
return null;
|
|
@@ -542,7 +358,6 @@ async function reconcilePendingFieldMutation(options) {
|
|
|
542
358
|
nextState = await saveUpdatedState({
|
|
543
359
|
state: nextState,
|
|
544
360
|
provider: options.provider,
|
|
545
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
546
361
|
nowUnixMs: options.nowUnixMs,
|
|
547
362
|
paths: options.paths,
|
|
548
363
|
});
|
|
@@ -557,7 +372,6 @@ async function reconcilePendingFieldMutation(options) {
|
|
|
557
372
|
nextState = await saveUpdatedState({
|
|
558
373
|
state: nextState,
|
|
559
374
|
provider: options.provider,
|
|
560
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
561
375
|
nowUnixMs: options.nowUnixMs,
|
|
562
376
|
paths: options.paths,
|
|
563
377
|
});
|
|
@@ -575,7 +389,6 @@ async function reconcilePendingFieldMutation(options) {
|
|
|
575
389
|
nextState = await saveUpdatedState({
|
|
576
390
|
state: nextState,
|
|
577
391
|
provider: options.provider,
|
|
578
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
579
392
|
nowUnixMs: options.nowUnixMs,
|
|
580
393
|
paths: options.paths,
|
|
581
394
|
});
|
|
@@ -592,7 +405,6 @@ async function reconcilePendingFieldMutation(options) {
|
|
|
592
405
|
nextState = await saveUpdatedState({
|
|
593
406
|
state: nextState,
|
|
594
407
|
provider: options.provider,
|
|
595
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
596
408
|
nowUnixMs: options.nowUnixMs,
|
|
597
409
|
paths: options.paths,
|
|
598
410
|
});
|
|
@@ -604,180 +416,6 @@ async function reconcilePendingFieldMutation(options) {
|
|
|
604
416
|
resolution: "continue",
|
|
605
417
|
};
|
|
606
418
|
}
|
|
607
|
-
async function reconcileFieldFamily(options) {
|
|
608
|
-
const domainName = options.family.domainName ?? "";
|
|
609
|
-
const fieldName = options.family.fieldName ?? "";
|
|
610
|
-
const observed = getObservedFieldState(options.context, domainName, fieldName);
|
|
611
|
-
if (observed !== null) {
|
|
612
|
-
if (observed.fieldId === options.family.expectedFieldId
|
|
613
|
-
&& observed.permanent === options.family.fieldPermanent
|
|
614
|
-
&& observed.hasValue
|
|
615
|
-
&& observed.format === options.family.fieldFormat
|
|
616
|
-
&& observed.rawValueHex === options.family.fieldValueHex) {
|
|
617
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
|
|
618
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
|
|
619
|
-
let nextState = updateFieldFamilyState({
|
|
620
|
-
state: options.state,
|
|
621
|
-
family: options.family,
|
|
622
|
-
status: "confirmed",
|
|
623
|
-
currentStep: "tx2",
|
|
624
|
-
nowUnixMs: options.nowUnixMs,
|
|
625
|
-
tx1: options.family.tx1 == null
|
|
626
|
-
? undefined
|
|
627
|
-
: { ...options.family.tx1, status: "confirmed", temporaryBuilderLockedOutpoints: [] },
|
|
628
|
-
tx2: options.family.tx2 == null
|
|
629
|
-
? undefined
|
|
630
|
-
: { ...options.family.tx2, status: "confirmed", temporaryBuilderLockedOutpoints: [] },
|
|
631
|
-
});
|
|
632
|
-
nextState = await saveUpdatedState({
|
|
633
|
-
state: nextState,
|
|
634
|
-
provider: options.provider,
|
|
635
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
636
|
-
nowUnixMs: options.nowUnixMs,
|
|
637
|
-
paths: options.paths,
|
|
638
|
-
});
|
|
639
|
-
return {
|
|
640
|
-
state: nextState,
|
|
641
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
|
|
642
|
-
...options.family,
|
|
643
|
-
status: "confirmed",
|
|
644
|
-
},
|
|
645
|
-
resolution: "confirmed",
|
|
646
|
-
};
|
|
647
|
-
}
|
|
648
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
|
|
649
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
|
|
650
|
-
let nextState = updateFieldFamilyState({
|
|
651
|
-
state: options.state,
|
|
652
|
-
family: options.family,
|
|
653
|
-
status: "repair-required",
|
|
654
|
-
currentStep: "tx2",
|
|
655
|
-
nowUnixMs: options.nowUnixMs,
|
|
656
|
-
tx1: options.family.tx1 == null
|
|
657
|
-
? undefined
|
|
658
|
-
: { ...options.family.tx1, temporaryBuilderLockedOutpoints: [] },
|
|
659
|
-
tx2: options.family.tx2 == null
|
|
660
|
-
? undefined
|
|
661
|
-
: { ...options.family.tx2, temporaryBuilderLockedOutpoints: [] },
|
|
662
|
-
});
|
|
663
|
-
nextState = await saveUpdatedState({
|
|
664
|
-
state: nextState,
|
|
665
|
-
provider: options.provider,
|
|
666
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
667
|
-
nowUnixMs: options.nowUnixMs,
|
|
668
|
-
paths: options.paths,
|
|
669
|
-
});
|
|
670
|
-
return {
|
|
671
|
-
state: nextState,
|
|
672
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
|
|
673
|
-
...options.family,
|
|
674
|
-
status: "repair-required",
|
|
675
|
-
},
|
|
676
|
-
resolution: "repair-required",
|
|
677
|
-
};
|
|
678
|
-
}
|
|
679
|
-
const tx2Known = options.family.tx2?.attemptedTxid == null
|
|
680
|
-
? false
|
|
681
|
-
: await options.rpc.getRawTransaction(options.family.tx2.attemptedTxid, true).then(() => true).catch(() => false);
|
|
682
|
-
if (tx2Known) {
|
|
683
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
|
|
684
|
-
let nextState = updateFieldFamilyState({
|
|
685
|
-
state: options.state,
|
|
686
|
-
family: options.family,
|
|
687
|
-
status: "live",
|
|
688
|
-
currentStep: "tx2",
|
|
689
|
-
nowUnixMs: options.nowUnixMs,
|
|
690
|
-
tx2: options.family.tx2 == null
|
|
691
|
-
? undefined
|
|
692
|
-
: { ...options.family.tx2, status: "live", temporaryBuilderLockedOutpoints: [] },
|
|
693
|
-
});
|
|
694
|
-
nextState = await saveUpdatedState({
|
|
695
|
-
state: nextState,
|
|
696
|
-
provider: options.provider,
|
|
697
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
698
|
-
nowUnixMs: options.nowUnixMs,
|
|
699
|
-
paths: options.paths,
|
|
700
|
-
});
|
|
701
|
-
return {
|
|
702
|
-
state: nextState,
|
|
703
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
|
|
704
|
-
...options.family,
|
|
705
|
-
status: "live",
|
|
706
|
-
},
|
|
707
|
-
resolution: "live",
|
|
708
|
-
};
|
|
709
|
-
}
|
|
710
|
-
const tx1Known = options.family.tx1?.attemptedTxid == null
|
|
711
|
-
? false
|
|
712
|
-
: await options.rpc.getRawTransaction(options.family.tx1.attemptedTxid, true).then(() => true).catch(() => false);
|
|
713
|
-
if (tx1Known) {
|
|
714
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
|
|
715
|
-
let nextState = updateFieldFamilyState({
|
|
716
|
-
state: options.state,
|
|
717
|
-
family: options.family,
|
|
718
|
-
status: "live",
|
|
719
|
-
currentStep: "tx1",
|
|
720
|
-
nowUnixMs: options.nowUnixMs,
|
|
721
|
-
tx1: options.family.tx1 == null
|
|
722
|
-
? undefined
|
|
723
|
-
: { ...options.family.tx1, status: "live", temporaryBuilderLockedOutpoints: [] },
|
|
724
|
-
});
|
|
725
|
-
nextState = await saveUpdatedState({
|
|
726
|
-
state: nextState,
|
|
727
|
-
provider: options.provider,
|
|
728
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
729
|
-
nowUnixMs: options.nowUnixMs,
|
|
730
|
-
paths: options.paths,
|
|
731
|
-
});
|
|
732
|
-
return {
|
|
733
|
-
state: nextState,
|
|
734
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
|
|
735
|
-
...options.family,
|
|
736
|
-
status: "live",
|
|
737
|
-
},
|
|
738
|
-
resolution: "ready-for-tx2",
|
|
739
|
-
};
|
|
740
|
-
}
|
|
741
|
-
if (options.family.status === "broadcast-unknown"
|
|
742
|
-
|| options.family.status === "draft"
|
|
743
|
-
|| options.family.status === "broadcasting") {
|
|
744
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx1?.temporaryBuilderLockedOutpoints ?? []);
|
|
745
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.family.tx2?.temporaryBuilderLockedOutpoints ?? []);
|
|
746
|
-
let nextState = updateFieldFamilyState({
|
|
747
|
-
state: options.state,
|
|
748
|
-
family: options.family,
|
|
749
|
-
status: "canceled",
|
|
750
|
-
currentStep: options.family.currentStep,
|
|
751
|
-
nowUnixMs: options.nowUnixMs,
|
|
752
|
-
tx1: options.family.tx1 == null
|
|
753
|
-
? undefined
|
|
754
|
-
: { ...options.family.tx1, status: options.family.tx1.attemptedTxid === null ? "canceled" : options.family.tx1.status, temporaryBuilderLockedOutpoints: [] },
|
|
755
|
-
tx2: options.family.tx2 == null
|
|
756
|
-
? undefined
|
|
757
|
-
: { ...options.family.tx2, status: options.family.tx2.attemptedTxid === null ? "canceled" : options.family.tx2.status, temporaryBuilderLockedOutpoints: [] },
|
|
758
|
-
});
|
|
759
|
-
nextState = await saveUpdatedState({
|
|
760
|
-
state: nextState,
|
|
761
|
-
provider: options.provider,
|
|
762
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
763
|
-
nowUnixMs: options.nowUnixMs,
|
|
764
|
-
paths: options.paths,
|
|
765
|
-
});
|
|
766
|
-
return {
|
|
767
|
-
state: nextState,
|
|
768
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
|
|
769
|
-
...options.family,
|
|
770
|
-
status: "canceled",
|
|
771
|
-
},
|
|
772
|
-
resolution: "not-seen",
|
|
773
|
-
};
|
|
774
|
-
}
|
|
775
|
-
return {
|
|
776
|
-
state: options.state,
|
|
777
|
-
family: options.family,
|
|
778
|
-
resolution: "continue",
|
|
779
|
-
};
|
|
780
|
-
}
|
|
781
419
|
async function confirmYesNo(prompter, message, errorCode, options) {
|
|
782
420
|
await confirmSharedYesNo(prompter, message, {
|
|
783
421
|
assumeYes: options.assumeYes,
|
|
@@ -799,39 +437,10 @@ async function confirmFieldCreate(prompter, options) {
|
|
|
799
437
|
const fieldRef = `${options.domainName}:${options.fieldName}`;
|
|
800
438
|
prompter.writeLine(`Creating field "${fieldRef}" as ${options.permanent ? "permanent" : "mutable"}.`);
|
|
801
439
|
prompter.writeLine(`Resolved sender: ${options.sender.selector} (${options.sender.address})`);
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
await confirmYesNo(prompter, "The field will be created empty and the burn is not reversible.", "wallet_field_create_confirmation_rejected", {
|
|
807
|
-
assumeYes: options.assumeYes,
|
|
808
|
-
requiresTtyErrorCode: "wallet_field_create_requires_tty",
|
|
809
|
-
});
|
|
810
|
-
return;
|
|
811
|
-
}
|
|
812
|
-
prompter.writeLine("Path: field-reg-plus-data-update-family");
|
|
813
|
-
prompter.writeLine(`Effect: ${describeFieldEffect({
|
|
814
|
-
kind: "create-and-initialize-field",
|
|
815
|
-
tx1BurnCogtoshi: "100",
|
|
816
|
-
tx2AdditionalBurnCogtoshi: "1",
|
|
817
|
-
})}.`);
|
|
818
|
-
prompter.writeLine(`Value: format ${options.value.format}, ${options.value.value.length} bytes`);
|
|
819
|
-
prompter.writeLine(`Initial value format: ${options.value.formatLabel}`);
|
|
820
|
-
prompter.writeLine(`Initial value bytes: ${options.value.value.length}`);
|
|
821
|
-
prompter.writeLine("Warning: non-clear field values are public in the mempool and on-chain.");
|
|
822
|
-
prompter.writeLine("This uses the same-block FIELD_REG -> DATA_UPDATE family.");
|
|
823
|
-
prompter.writeLine("Tx1 burns 0.00000100 COG. Tx2 may burn an additional 0.00000001 COG.");
|
|
824
|
-
prompter.writeLine("Tx1 may confirm even if Tx2 later fails, is canceled, or needs repair.");
|
|
825
|
-
if (options.permanent) {
|
|
826
|
-
prompter.writeLine("This is the first non-clear value write to a permanent field.");
|
|
827
|
-
await confirmTyped(prompter, fieldRef, `Type ${fieldRef} to continue: `, "wallet_field_create_confirmation_rejected", {
|
|
828
|
-
assumeYes: options.assumeYes,
|
|
829
|
-
requiresTtyErrorCode: "wallet_field_create_requires_tty",
|
|
830
|
-
typedAckRequiredErrorCode: "wallet_field_create_typed_ack_required",
|
|
831
|
-
});
|
|
832
|
-
return;
|
|
833
|
-
}
|
|
834
|
-
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", {
|
|
835
444
|
assumeYes: options.assumeYes,
|
|
836
445
|
requiresTtyErrorCode: "wallet_field_create_requires_tty",
|
|
837
446
|
});
|
|
@@ -981,7 +590,6 @@ async function sendStandaloneMutation(options) {
|
|
|
981
590
|
nextState = await saveUpdatedState({
|
|
982
591
|
state: nextState,
|
|
983
592
|
provider: options.provider,
|
|
984
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
985
593
|
nowUnixMs: options.nowUnixMs,
|
|
986
594
|
paths: options.paths,
|
|
987
595
|
});
|
|
@@ -1004,7 +612,6 @@ async function sendStandaloneMutation(options) {
|
|
|
1004
612
|
nextState = await saveUpdatedState({
|
|
1005
613
|
state: nextState,
|
|
1006
614
|
provider: options.provider,
|
|
1007
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1008
615
|
nowUnixMs: options.nowUnixMs,
|
|
1009
616
|
paths: options.paths,
|
|
1010
617
|
});
|
|
@@ -1020,7 +627,6 @@ async function sendStandaloneMutation(options) {
|
|
|
1020
627
|
nextState = await saveUpdatedState({
|
|
1021
628
|
state: nextState,
|
|
1022
629
|
provider: options.provider,
|
|
1023
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1024
630
|
nowUnixMs: options.nowUnixMs,
|
|
1025
631
|
paths: options.paths,
|
|
1026
632
|
});
|
|
@@ -1037,228 +643,11 @@ async function sendStandaloneMutation(options) {
|
|
|
1037
643
|
nextState = await saveUpdatedState({
|
|
1038
644
|
state: nextState,
|
|
1039
645
|
provider: options.provider,
|
|
1040
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1041
646
|
nowUnixMs: options.nowUnixMs,
|
|
1042
647
|
paths: options.paths,
|
|
1043
648
|
});
|
|
1044
649
|
return { state: nextState, mutation: live };
|
|
1045
650
|
}
|
|
1046
|
-
async function sendFamilyTx1(options) {
|
|
1047
|
-
let nextState = updateFieldFamilyState({
|
|
1048
|
-
state: options.state,
|
|
1049
|
-
family: options.family,
|
|
1050
|
-
status: "broadcasting",
|
|
1051
|
-
currentStep: "tx1",
|
|
1052
|
-
nowUnixMs: options.nowUnixMs,
|
|
1053
|
-
tx1: {
|
|
1054
|
-
status: "broadcasting",
|
|
1055
|
-
attemptedTxid: options.built.txid,
|
|
1056
|
-
attemptedWtxid: options.built.wtxid,
|
|
1057
|
-
temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
|
|
1058
|
-
rawHex: options.built.rawHex,
|
|
1059
|
-
},
|
|
1060
|
-
});
|
|
1061
|
-
nextState = await saveUpdatedState({
|
|
1062
|
-
state: nextState,
|
|
1063
|
-
provider: options.provider,
|
|
1064
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1065
|
-
nowUnixMs: options.nowUnixMs,
|
|
1066
|
-
paths: options.paths,
|
|
1067
|
-
});
|
|
1068
|
-
if (options.snapshotHeight !== null && options.snapshotHeight !== (await options.rpc.getBlockchainInfo()).blocks) {
|
|
1069
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
|
|
1070
|
-
throw new Error("wallet_field_create_tx1_tip_mismatch");
|
|
1071
|
-
}
|
|
1072
|
-
try {
|
|
1073
|
-
await options.rpc.sendRawTransaction(options.built.rawHex);
|
|
1074
|
-
}
|
|
1075
|
-
catch (error) {
|
|
1076
|
-
if (!isAlreadyAcceptedError(error)) {
|
|
1077
|
-
if (isBroadcastUnknownError(error)) {
|
|
1078
|
-
nextState = updateFieldFamilyState({
|
|
1079
|
-
state: nextState,
|
|
1080
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
|
|
1081
|
-
status: "broadcast-unknown",
|
|
1082
|
-
currentStep: "tx1",
|
|
1083
|
-
nowUnixMs: options.nowUnixMs,
|
|
1084
|
-
tx1: {
|
|
1085
|
-
status: "broadcast-unknown",
|
|
1086
|
-
attemptedTxid: options.built.txid,
|
|
1087
|
-
attemptedWtxid: options.built.wtxid,
|
|
1088
|
-
temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
|
|
1089
|
-
rawHex: options.built.rawHex,
|
|
1090
|
-
},
|
|
1091
|
-
});
|
|
1092
|
-
nextState = await saveUpdatedState({
|
|
1093
|
-
state: nextState,
|
|
1094
|
-
provider: options.provider,
|
|
1095
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1096
|
-
nowUnixMs: options.nowUnixMs,
|
|
1097
|
-
paths: options.paths,
|
|
1098
|
-
});
|
|
1099
|
-
throw new Error("wallet_field_create_tx1_broadcast_unknown");
|
|
1100
|
-
}
|
|
1101
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
|
|
1102
|
-
nextState = updateFieldFamilyState({
|
|
1103
|
-
state: nextState,
|
|
1104
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
|
|
1105
|
-
status: "canceled",
|
|
1106
|
-
currentStep: "tx1",
|
|
1107
|
-
nowUnixMs: options.nowUnixMs,
|
|
1108
|
-
tx1: {
|
|
1109
|
-
status: "canceled",
|
|
1110
|
-
attemptedTxid: options.built.txid,
|
|
1111
|
-
attemptedWtxid: options.built.wtxid,
|
|
1112
|
-
temporaryBuilderLockedOutpoints: [],
|
|
1113
|
-
rawHex: options.built.rawHex,
|
|
1114
|
-
},
|
|
1115
|
-
});
|
|
1116
|
-
nextState = await saveUpdatedState({
|
|
1117
|
-
state: nextState,
|
|
1118
|
-
provider: options.provider,
|
|
1119
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1120
|
-
nowUnixMs: options.nowUnixMs,
|
|
1121
|
-
paths: options.paths,
|
|
1122
|
-
});
|
|
1123
|
-
throw error;
|
|
1124
|
-
}
|
|
1125
|
-
}
|
|
1126
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
|
|
1127
|
-
nextState = updateFieldFamilyState({
|
|
1128
|
-
state: nextState,
|
|
1129
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
|
|
1130
|
-
status: "live",
|
|
1131
|
-
currentStep: "tx1",
|
|
1132
|
-
nowUnixMs: options.nowUnixMs,
|
|
1133
|
-
tx1: {
|
|
1134
|
-
status: "live",
|
|
1135
|
-
attemptedTxid: options.built.txid,
|
|
1136
|
-
attemptedWtxid: options.built.wtxid,
|
|
1137
|
-
temporaryBuilderLockedOutpoints: [],
|
|
1138
|
-
rawHex: options.built.rawHex,
|
|
1139
|
-
},
|
|
1140
|
-
});
|
|
1141
|
-
nextState = await saveUpdatedState({
|
|
1142
|
-
state: nextState,
|
|
1143
|
-
provider: options.provider,
|
|
1144
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1145
|
-
nowUnixMs: options.nowUnixMs,
|
|
1146
|
-
paths: options.paths,
|
|
1147
|
-
});
|
|
1148
|
-
return {
|
|
1149
|
-
state: nextState,
|
|
1150
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
|
|
1151
|
-
...options.family,
|
|
1152
|
-
status: "live",
|
|
1153
|
-
},
|
|
1154
|
-
};
|
|
1155
|
-
}
|
|
1156
|
-
async function sendFamilyTx2(options) {
|
|
1157
|
-
let nextState = updateFieldFamilyState({
|
|
1158
|
-
state: options.state,
|
|
1159
|
-
family: options.family,
|
|
1160
|
-
status: "broadcasting",
|
|
1161
|
-
currentStep: "tx2",
|
|
1162
|
-
nowUnixMs: options.nowUnixMs,
|
|
1163
|
-
tx2: {
|
|
1164
|
-
status: "broadcasting",
|
|
1165
|
-
attemptedTxid: options.built.txid,
|
|
1166
|
-
attemptedWtxid: options.built.wtxid,
|
|
1167
|
-
temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
|
|
1168
|
-
rawHex: options.built.rawHex,
|
|
1169
|
-
},
|
|
1170
|
-
});
|
|
1171
|
-
nextState = await saveUpdatedState({
|
|
1172
|
-
state: nextState,
|
|
1173
|
-
provider: options.provider,
|
|
1174
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1175
|
-
nowUnixMs: options.nowUnixMs,
|
|
1176
|
-
paths: options.paths,
|
|
1177
|
-
});
|
|
1178
|
-
try {
|
|
1179
|
-
await options.rpc.sendRawTransaction(options.built.rawHex);
|
|
1180
|
-
}
|
|
1181
|
-
catch (error) {
|
|
1182
|
-
if (!isAlreadyAcceptedError(error)) {
|
|
1183
|
-
if (isBroadcastUnknownError(error)) {
|
|
1184
|
-
nextState = updateFieldFamilyState({
|
|
1185
|
-
state: nextState,
|
|
1186
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
|
|
1187
|
-
status: "broadcast-unknown",
|
|
1188
|
-
currentStep: "tx2",
|
|
1189
|
-
nowUnixMs: options.nowUnixMs,
|
|
1190
|
-
tx2: {
|
|
1191
|
-
status: "broadcast-unknown",
|
|
1192
|
-
attemptedTxid: options.built.txid,
|
|
1193
|
-
attemptedWtxid: options.built.wtxid,
|
|
1194
|
-
temporaryBuilderLockedOutpoints: options.built.temporaryBuilderLockedOutpoints,
|
|
1195
|
-
rawHex: options.built.rawHex,
|
|
1196
|
-
},
|
|
1197
|
-
});
|
|
1198
|
-
nextState = await saveUpdatedState({
|
|
1199
|
-
state: nextState,
|
|
1200
|
-
provider: options.provider,
|
|
1201
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1202
|
-
nowUnixMs: options.nowUnixMs,
|
|
1203
|
-
paths: options.paths,
|
|
1204
|
-
});
|
|
1205
|
-
throw new Error("wallet_field_create_tx2_broadcast_unknown");
|
|
1206
|
-
}
|
|
1207
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
|
|
1208
|
-
nextState = updateFieldFamilyState({
|
|
1209
|
-
state: nextState,
|
|
1210
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
|
|
1211
|
-
status: "live",
|
|
1212
|
-
currentStep: "tx1",
|
|
1213
|
-
nowUnixMs: options.nowUnixMs,
|
|
1214
|
-
tx2: {
|
|
1215
|
-
status: "canceled",
|
|
1216
|
-
attemptedTxid: options.built.txid,
|
|
1217
|
-
attemptedWtxid: options.built.wtxid,
|
|
1218
|
-
temporaryBuilderLockedOutpoints: [],
|
|
1219
|
-
rawHex: options.built.rawHex,
|
|
1220
|
-
},
|
|
1221
|
-
});
|
|
1222
|
-
nextState = await saveUpdatedState({
|
|
1223
|
-
state: nextState,
|
|
1224
|
-
provider: options.provider,
|
|
1225
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1226
|
-
nowUnixMs: options.nowUnixMs,
|
|
1227
|
-
paths: options.paths,
|
|
1228
|
-
});
|
|
1229
|
-
throw error;
|
|
1230
|
-
}
|
|
1231
|
-
}
|
|
1232
|
-
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.built.temporaryBuilderLockedOutpoints);
|
|
1233
|
-
nextState = updateFieldFamilyState({
|
|
1234
|
-
state: nextState,
|
|
1235
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? options.family,
|
|
1236
|
-
status: "live",
|
|
1237
|
-
currentStep: "tx2",
|
|
1238
|
-
nowUnixMs: options.nowUnixMs,
|
|
1239
|
-
tx2: {
|
|
1240
|
-
status: "live",
|
|
1241
|
-
attemptedTxid: options.built.txid,
|
|
1242
|
-
attemptedWtxid: options.built.wtxid,
|
|
1243
|
-
temporaryBuilderLockedOutpoints: [],
|
|
1244
|
-
rawHex: options.built.rawHex,
|
|
1245
|
-
},
|
|
1246
|
-
});
|
|
1247
|
-
nextState = await saveUpdatedState({
|
|
1248
|
-
state: nextState,
|
|
1249
|
-
provider: options.provider,
|
|
1250
|
-
unlockUntilUnixMs: options.unlockUntilUnixMs,
|
|
1251
|
-
nowUnixMs: options.nowUnixMs,
|
|
1252
|
-
paths: options.paths,
|
|
1253
|
-
});
|
|
1254
|
-
return {
|
|
1255
|
-
state: nextState,
|
|
1256
|
-
family: findFieldFamilyByIntent(nextState, options.family.intentFingerprintHex) ?? {
|
|
1257
|
-
...options.family,
|
|
1258
|
-
status: "live",
|
|
1259
|
-
},
|
|
1260
|
-
};
|
|
1261
|
-
}
|
|
1262
651
|
async function submitStandaloneFieldMutation(options) {
|
|
1263
652
|
if (!options.prompter.isInteractive && options.assumeYes !== true) {
|
|
1264
653
|
throw new Error(`${options.errorPrefix}_requires_tty`);
|
|
@@ -1293,82 +682,91 @@ async function submitStandaloneFieldMutation(options) {
|
|
|
1293
682
|
normalizedDomainName,
|
|
1294
683
|
normalizedFieldName,
|
|
1295
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
|
+
});
|
|
1296
697
|
const existingMutation = findPendingMutationByIntent(operation.state, intentFingerprintHex);
|
|
698
|
+
let workingState = operation.state;
|
|
699
|
+
let replacementFixedInputs = null;
|
|
1297
700
|
if (existingMutation !== null) {
|
|
1298
|
-
const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
|
|
1299
|
-
dataDir: options.dataDir,
|
|
1300
|
-
chain: "main",
|
|
1301
|
-
startHeight: 0,
|
|
1302
|
-
walletRootId: operation.state.walletRootId,
|
|
1303
|
-
});
|
|
1304
|
-
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
1305
|
-
const walletName = operation.state.managedCoreWallet.walletName;
|
|
1306
701
|
const reconciled = await reconcilePendingFieldMutation({
|
|
1307
702
|
state: operation.state,
|
|
1308
703
|
mutation: existingMutation,
|
|
1309
704
|
provider,
|
|
1310
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1311
705
|
nowUnixMs,
|
|
1312
706
|
paths,
|
|
1313
707
|
rpc,
|
|
1314
708
|
walletName,
|
|
1315
709
|
context: readContext,
|
|
1316
710
|
});
|
|
711
|
+
workingState = reconciled.state;
|
|
1317
712
|
if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
format: reconciled.mutation.fieldFormat ?? existingObservedField?.format ?? null,
|
|
1327
|
-
status: reconciled.resolution,
|
|
1328
|
-
reusedExisting: true,
|
|
1329
|
-
resolved: createResolvedFieldSummary({
|
|
1330
|
-
sender: operation.sender,
|
|
1331
|
-
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 {
|
|
1332
721
|
kind: options.kind,
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
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;
|
|
1337
740
|
}
|
|
1338
741
|
if (reconciled.resolution === "repair-required") {
|
|
1339
742
|
throw new Error(`${options.errorPrefix}_repair_required`);
|
|
1340
743
|
}
|
|
1341
744
|
}
|
|
1342
745
|
await options.confirm(operation);
|
|
1343
|
-
const planned = await options.createMutation(operation, existingMutation);
|
|
1344
|
-
let nextState = upsertPendingMutation(
|
|
746
|
+
const planned = await options.createMutation(operation, existingMutation, feeSelection);
|
|
747
|
+
let nextState = upsertPendingMutation(workingState, planned.mutation);
|
|
1345
748
|
nextState = await saveUpdatedState({
|
|
1346
749
|
state: nextState,
|
|
1347
750
|
provider,
|
|
1348
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1349
751
|
nowUnixMs,
|
|
1350
752
|
paths,
|
|
1351
753
|
});
|
|
1352
|
-
const
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
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,
|
|
1357
760
|
});
|
|
1358
|
-
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
1359
|
-
const walletName = operation.state.managedCoreWallet.walletName;
|
|
1360
761
|
const built = await buildFieldTransaction({
|
|
1361
762
|
rpc,
|
|
1362
763
|
walletName,
|
|
1363
764
|
state: nextState,
|
|
1364
|
-
plan:
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
opReturnData: planned.opReturnData,
|
|
1370
|
-
errorPrefix: options.errorPrefix,
|
|
1371
|
-
}),
|
|
765
|
+
plan: {
|
|
766
|
+
...fieldPlan,
|
|
767
|
+
fixedInputs: mergeFixedWalletInputs(fieldPlan.fixedInputs, replacementFixedInputs),
|
|
768
|
+
},
|
|
769
|
+
feeRateSatVb: feeSelection.feeRateSatVb,
|
|
1372
770
|
});
|
|
1373
771
|
const final = await sendStandaloneMutation({
|
|
1374
772
|
rpc,
|
|
@@ -1378,7 +776,6 @@ async function submitStandaloneFieldMutation(options) {
|
|
|
1378
776
|
mutation: nextState.pendingMutations.find((mutation) => mutation.intentFingerprintHex === planned.mutation.intentFingerprintHex),
|
|
1379
777
|
state: nextState,
|
|
1380
778
|
provider,
|
|
1381
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1382
779
|
nowUnixMs,
|
|
1383
780
|
paths,
|
|
1384
781
|
errorPrefix: options.errorPrefix,
|
|
@@ -1389,7 +786,6 @@ async function submitStandaloneFieldMutation(options) {
|
|
|
1389
786
|
fieldName: normalizedFieldName,
|
|
1390
787
|
fieldId: final.mutation.fieldId ?? existingObservedField?.fieldId ?? null,
|
|
1391
788
|
txid: final.mutation.attemptedTxid ?? built.txid,
|
|
1392
|
-
family: false,
|
|
1393
789
|
permanent: final.mutation.fieldPermanent ?? existingObservedField?.permanent ?? null,
|
|
1394
790
|
format: final.mutation.fieldFormat ?? existingObservedField?.format ?? null,
|
|
1395
791
|
status: "live",
|
|
@@ -1398,238 +794,11 @@ async function submitStandaloneFieldMutation(options) {
|
|
|
1398
794
|
sender: operation.sender,
|
|
1399
795
|
senderSelector: operation.senderSelector,
|
|
1400
796
|
kind: options.kind,
|
|
1401
|
-
family: false,
|
|
1402
797
|
value: createResolvedFieldValueFromStoredData(options.kind, planned.mutation.fieldFormat ?? existingObservedField?.format ?? null, planned.mutation.fieldValueHex),
|
|
1403
798
|
}),
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
await readContext.close();
|
|
1408
|
-
await miningPreemption.release();
|
|
1409
|
-
}
|
|
1410
|
-
}
|
|
1411
|
-
finally {
|
|
1412
|
-
await controlLock.release();
|
|
1413
|
-
}
|
|
1414
|
-
}
|
|
1415
|
-
async function submitFieldCreateFamily(options) {
|
|
1416
|
-
if (!options.prompter.isInteractive && options.assumeYes !== true) {
|
|
1417
|
-
throw new Error("wallet_field_create_requires_tty");
|
|
1418
|
-
}
|
|
1419
|
-
const provider = options.provider ?? createDefaultWalletSecretProvider();
|
|
1420
|
-
const nowUnixMs = options.nowUnixMs ?? Date.now();
|
|
1421
|
-
const paths = options.paths ?? resolveWalletRuntimePathsForTesting();
|
|
1422
|
-
const controlLock = await acquireFileLock(paths.walletControlLockPath, {
|
|
1423
|
-
purpose: "wallet_field_create",
|
|
1424
|
-
walletRootId: null,
|
|
1425
|
-
});
|
|
1426
|
-
try {
|
|
1427
|
-
const miningPreemption = await pauseMiningForWalletMutation({
|
|
1428
|
-
paths,
|
|
1429
|
-
reason: "wallet_field_create",
|
|
1430
|
-
});
|
|
1431
|
-
const readContext = await (options.openReadContext ?? openWalletReadContext)({
|
|
1432
|
-
dataDir: options.dataDir,
|
|
1433
|
-
databasePath: options.databasePath,
|
|
1434
|
-
secretProvider: provider,
|
|
1435
|
-
walletControlLockHeld: true,
|
|
1436
|
-
paths,
|
|
1437
|
-
});
|
|
1438
|
-
try {
|
|
1439
|
-
const normalizedDomainName = normalizeDomainName(options.domainName);
|
|
1440
|
-
const normalizedFieldName = normalizeFieldName(options.fieldName);
|
|
1441
|
-
const operation = resolveAnchoredFieldOperation(readContext, normalizedDomainName, "wallet_field_create");
|
|
1442
|
-
const existingField = getObservedFieldState(readContext, normalizedDomainName, normalizedFieldName);
|
|
1443
|
-
if (existingField !== null) {
|
|
1444
|
-
throw new Error("wallet_field_create_field_exists");
|
|
1445
|
-
}
|
|
1446
|
-
if (operation.chainDomain.nextFieldId === 0xffff_ffff) {
|
|
1447
|
-
throw new Error("wallet_field_create_field_id_exhausted");
|
|
1448
|
-
}
|
|
1449
|
-
if (hex(operation.chainDomain.delegate) !== null) {
|
|
1450
|
-
throw new Error("wallet_field_create_delegate_blocks_same_block_family");
|
|
1451
|
-
}
|
|
1452
|
-
const senderBalance = getBalance(operation.readContext.snapshot.state, Buffer.from(operation.sender.scriptPubKeyHex, "hex"));
|
|
1453
|
-
if (senderBalance < 101n) {
|
|
1454
|
-
throw new Error("wallet_field_create_insufficient_cog");
|
|
1455
|
-
}
|
|
1456
|
-
const intentFingerprintHex = createIntentFingerprint([
|
|
1457
|
-
"field-create",
|
|
1458
|
-
operation.state.walletRootId,
|
|
1459
|
-
normalizedDomainName,
|
|
1460
|
-
normalizedFieldName,
|
|
1461
|
-
options.permanent ? 1 : 0,
|
|
1462
|
-
options.value.format,
|
|
1463
|
-
options.value.valueHex,
|
|
1464
|
-
]);
|
|
1465
|
-
const existingFamily = findFieldFamilyByIntent(operation.state, intentFingerprintHex);
|
|
1466
|
-
const conflictingFamily = findActiveFieldFamilyByDomain(operation.state, normalizedDomainName);
|
|
1467
|
-
if (conflictingFamily !== null && conflictingFamily.intentFingerprintHex !== intentFingerprintHex) {
|
|
1468
|
-
throw new Error("wallet_field_create_family_already_active");
|
|
1469
|
-
}
|
|
1470
|
-
const conflictingCreate = findActiveFieldCreateMutationByDomain(operation.state, normalizedDomainName, intentFingerprintHex);
|
|
1471
|
-
if (conflictingCreate !== null) {
|
|
1472
|
-
throw new Error("wallet_field_create_registration_already_pending");
|
|
1473
|
-
}
|
|
1474
|
-
const node = await (options.attachService ?? attachOrStartManagedBitcoindService)({
|
|
1475
|
-
dataDir: options.dataDir,
|
|
1476
|
-
chain: "main",
|
|
1477
|
-
startHeight: 0,
|
|
1478
|
-
walletRootId: operation.state.walletRootId,
|
|
1479
|
-
});
|
|
1480
|
-
const rpc = (options.rpcFactory ?? createRpcClient)(node.rpc);
|
|
1481
|
-
const walletName = operation.state.managedCoreWallet.walletName;
|
|
1482
|
-
let workingState = operation.state;
|
|
1483
|
-
let resumedFamily = null;
|
|
1484
|
-
if (existingFamily !== null) {
|
|
1485
|
-
const reconciled = await reconcileFieldFamily({
|
|
1486
|
-
state: workingState,
|
|
1487
|
-
family: existingFamily,
|
|
1488
|
-
provider,
|
|
1489
|
-
nowUnixMs,
|
|
1490
|
-
paths,
|
|
1491
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1492
|
-
rpc,
|
|
1493
|
-
walletName,
|
|
1494
|
-
context: readContext,
|
|
1495
|
-
});
|
|
1496
|
-
workingState = reconciled.state;
|
|
1497
|
-
if (reconciled.resolution === "confirmed" || reconciled.resolution === "live") {
|
|
1498
|
-
return {
|
|
1499
|
-
kind: "field-create",
|
|
1500
|
-
domainName: normalizedDomainName,
|
|
1501
|
-
fieldName: normalizedFieldName,
|
|
1502
|
-
fieldId: reconciled.family.expectedFieldId ?? null,
|
|
1503
|
-
txid: reconciled.family.tx2?.attemptedTxid ?? reconciled.family.tx1?.attemptedTxid ?? "unknown",
|
|
1504
|
-
tx1Txid: reconciled.family.tx1?.attemptedTxid ?? null,
|
|
1505
|
-
tx2Txid: reconciled.family.tx2?.attemptedTxid ?? null,
|
|
1506
|
-
family: true,
|
|
1507
|
-
permanent: reconciled.family.fieldPermanent ?? null,
|
|
1508
|
-
format: reconciled.family.fieldFormat ?? null,
|
|
1509
|
-
status: reconciled.resolution,
|
|
1510
|
-
reusedExisting: true,
|
|
1511
|
-
resolved: createResolvedFieldSummary({
|
|
1512
|
-
sender: operation.sender,
|
|
1513
|
-
senderSelector: operation.senderSelector,
|
|
1514
|
-
kind: "field-create",
|
|
1515
|
-
family: true,
|
|
1516
|
-
value: createResolvedFieldValueFromStoredData("field-create", reconciled.family.fieldFormat ?? null, reconciled.family.fieldValueHex),
|
|
1517
|
-
}),
|
|
1518
|
-
};
|
|
1519
|
-
}
|
|
1520
|
-
if (reconciled.resolution === "repair-required") {
|
|
1521
|
-
throw new Error("wallet_field_create_repair_required");
|
|
1522
|
-
}
|
|
1523
|
-
if (reconciled.resolution === "ready-for-tx2") {
|
|
1524
|
-
resumedFamily = reconciled.family;
|
|
1525
|
-
}
|
|
1526
|
-
}
|
|
1527
|
-
if (resumedFamily === null) {
|
|
1528
|
-
await confirmFieldCreate(options.prompter, {
|
|
1529
|
-
domainName: normalizedDomainName,
|
|
1530
|
-
fieldName: normalizedFieldName,
|
|
1531
|
-
permanent: options.permanent,
|
|
1532
|
-
value: options.value,
|
|
1533
|
-
sender: createResolvedFieldSenderSummary(operation.sender, operation.senderSelector),
|
|
1534
|
-
assumeYes: options.assumeYes,
|
|
1535
|
-
});
|
|
1536
|
-
let nextState = upsertProactiveFamily(workingState, createFieldFamilyRecord({
|
|
1537
|
-
domainName: normalizedDomainName,
|
|
1538
|
-
domainId: operation.chainDomain.domainId,
|
|
1539
|
-
fieldName: normalizedFieldName,
|
|
1540
|
-
expectedFieldId: operation.chainDomain.nextFieldId,
|
|
1541
|
-
sender: operation.sender,
|
|
1542
|
-
permanence: options.permanent,
|
|
1543
|
-
format: options.value.format,
|
|
1544
|
-
valueHex: options.value.valueHex,
|
|
1545
|
-
intentFingerprintHex,
|
|
1546
|
-
nowUnixMs,
|
|
1547
|
-
existing: existingFamily,
|
|
1548
|
-
}));
|
|
1549
|
-
nextState = await saveUpdatedState({
|
|
1550
|
-
state: nextState,
|
|
1551
|
-
provider,
|
|
1552
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1553
|
-
nowUnixMs,
|
|
1554
|
-
paths,
|
|
1555
|
-
});
|
|
1556
|
-
const family = findFieldFamilyByIntent(nextState, intentFingerprintHex);
|
|
1557
|
-
const tx1 = await buildFieldTransaction({
|
|
1558
|
-
rpc,
|
|
1559
|
-
walletName,
|
|
1560
|
-
state: nextState,
|
|
1561
|
-
plan: buildAnchoredFieldPlan({
|
|
1562
|
-
state: nextState,
|
|
1563
|
-
allUtxos: await rpc.listUnspent(walletName, 1),
|
|
1564
|
-
sender: operation.sender,
|
|
1565
|
-
anchorOutpoint: operation.anchorOutpoint,
|
|
1566
|
-
opReturnData: serializeFieldReg(operation.chainDomain.domainId, options.permanent, normalizedFieldName).opReturnData,
|
|
1567
|
-
errorPrefix: "wallet_field_create_tx1",
|
|
1568
|
-
}),
|
|
1569
|
-
});
|
|
1570
|
-
const afterTx1 = await sendFamilyTx1({
|
|
1571
|
-
rpc,
|
|
1572
|
-
walletName,
|
|
1573
|
-
snapshotHeight: readContext.snapshot?.tip?.height ?? null,
|
|
1574
|
-
built: tx1,
|
|
1575
|
-
family,
|
|
1576
|
-
state: nextState,
|
|
1577
|
-
provider,
|
|
1578
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1579
|
-
nowUnixMs,
|
|
1580
|
-
paths,
|
|
1581
|
-
});
|
|
1582
|
-
workingState = afterTx1.state;
|
|
1583
|
-
resumedFamily = afterTx1.family;
|
|
1584
|
-
}
|
|
1585
|
-
const tx1Txid = resumedFamily.tx1?.attemptedTxid;
|
|
1586
|
-
if (tx1Txid == null) {
|
|
1587
|
-
throw new Error("wallet_field_create_tx1_missing");
|
|
1588
|
-
}
|
|
1589
|
-
await rpc.getRawTransaction(tx1Txid, true);
|
|
1590
|
-
const tx2 = await buildFieldTransaction({
|
|
1591
|
-
rpc,
|
|
1592
|
-
walletName,
|
|
1593
|
-
state: workingState,
|
|
1594
|
-
availableFundingMinConf: 0,
|
|
1595
|
-
plan: buildFieldFamilyTx2Plan({
|
|
1596
|
-
state: workingState,
|
|
1597
|
-
allUtxos: await rpc.listUnspent(walletName, 0),
|
|
1598
|
-
sender: operation.sender,
|
|
1599
|
-
tx1Txid,
|
|
1600
|
-
opReturnData: serializeDataUpdate(operation.chainDomain.domainId, resumedFamily.expectedFieldId ?? operation.chainDomain.nextFieldId, options.value.format, options.value.value).opReturnData,
|
|
1601
|
-
}),
|
|
1602
|
-
});
|
|
1603
|
-
const final = await sendFamilyTx2({
|
|
1604
|
-
rpc,
|
|
1605
|
-
walletName,
|
|
1606
|
-
built: tx2,
|
|
1607
|
-
family: resumedFamily,
|
|
1608
|
-
state: workingState,
|
|
1609
|
-
provider,
|
|
1610
|
-
unlockUntilUnixMs: operation.unlockUntilUnixMs,
|
|
1611
|
-
nowUnixMs,
|
|
1612
|
-
paths,
|
|
1613
|
-
});
|
|
1614
|
-
return {
|
|
1615
|
-
kind: "field-create",
|
|
1616
|
-
domainName: normalizedDomainName,
|
|
1617
|
-
fieldName: normalizedFieldName,
|
|
1618
|
-
fieldId: final.family.expectedFieldId ?? null,
|
|
1619
|
-
txid: final.family.tx2?.attemptedTxid ?? tx2.txid,
|
|
1620
|
-
tx1Txid,
|
|
1621
|
-
tx2Txid: final.family.tx2?.attemptedTxid ?? tx2.txid,
|
|
1622
|
-
family: true,
|
|
1623
|
-
permanent: final.family.fieldPermanent ?? null,
|
|
1624
|
-
format: final.family.fieldFormat ?? null,
|
|
1625
|
-
status: "live",
|
|
1626
|
-
reusedExisting: existingFamily !== null,
|
|
1627
|
-
resolved: createResolvedFieldSummary({
|
|
1628
|
-
sender: operation.sender,
|
|
1629
|
-
senderSelector: operation.senderSelector,
|
|
1630
|
-
kind: "field-create",
|
|
1631
|
-
family: true,
|
|
1632
|
-
value: createResolvedFieldValueSummary(options.value.format, options.value.value),
|
|
799
|
+
fees: createBuiltWalletMutationFeeSummary({
|
|
800
|
+
selection: feeSelection,
|
|
801
|
+
built,
|
|
1633
802
|
}),
|
|
1634
803
|
};
|
|
1635
804
|
}
|
|
@@ -1643,15 +812,7 @@ async function submitFieldCreateFamily(options) {
|
|
|
1643
812
|
}
|
|
1644
813
|
}
|
|
1645
814
|
export async function createField(options) {
|
|
1646
|
-
const normalizedSource = options.source == null ? null : await loadFieldValue(options.source);
|
|
1647
815
|
const permanent = options.permanent ?? false;
|
|
1648
|
-
if (normalizedSource !== null) {
|
|
1649
|
-
return submitFieldCreateFamily({
|
|
1650
|
-
...options,
|
|
1651
|
-
permanent,
|
|
1652
|
-
value: normalizedSource,
|
|
1653
|
-
});
|
|
1654
|
-
}
|
|
1655
816
|
return submitStandaloneFieldMutation({
|
|
1656
817
|
kind: "field-create",
|
|
1657
818
|
errorPrefix: "wallet_field_create",
|
|
@@ -1667,7 +828,7 @@ export async function createField(options) {
|
|
|
1667
828
|
openReadContext: options.openReadContext,
|
|
1668
829
|
attachService: options.attachService,
|
|
1669
830
|
rpcFactory: options.rpcFactory,
|
|
1670
|
-
async createMutation(operation, existing) {
|
|
831
|
+
async createMutation(operation, existing, feeSelection) {
|
|
1671
832
|
const existingField = getObservedFieldState(operation.readContext, normalizeDomainName(options.domainName), normalizeFieldName(options.fieldName));
|
|
1672
833
|
if (existingField !== null) {
|
|
1673
834
|
throw new Error("wallet_field_create_field_exists");
|
|
@@ -1688,10 +849,6 @@ export async function createField(options) {
|
|
|
1688
849
|
normalizedFieldName,
|
|
1689
850
|
permanent ? 1 : 0,
|
|
1690
851
|
]);
|
|
1691
|
-
const conflictFamily = findActiveFieldFamilyByDomain(operation.state, normalizedDomainName);
|
|
1692
|
-
if (conflictFamily !== null && conflictFamily.intentFingerprintHex !== intentFingerprintHex) {
|
|
1693
|
-
throw new Error("wallet_field_create_family_already_active");
|
|
1694
|
-
}
|
|
1695
852
|
const conflictCreate = findActiveFieldCreateMutationByDomain(operation.state, normalizedDomainName, intentFingerprintHex);
|
|
1696
853
|
if (conflictCreate !== null) {
|
|
1697
854
|
throw new Error("wallet_field_create_registration_already_pending");
|
|
@@ -1705,6 +862,7 @@ export async function createField(options) {
|
|
|
1705
862
|
sender: operation.sender,
|
|
1706
863
|
intentFingerprintHex,
|
|
1707
864
|
nowUnixMs: options.nowUnixMs ?? Date.now(),
|
|
865
|
+
feeSelection,
|
|
1708
866
|
existing,
|
|
1709
867
|
fieldId: operation.chainDomain.nextFieldId,
|
|
1710
868
|
fieldPermanent: permanent,
|
|
@@ -1716,7 +874,6 @@ export async function createField(options) {
|
|
|
1716
874
|
domainName: normalizeDomainName(options.domainName),
|
|
1717
875
|
fieldName: normalizeFieldName(options.fieldName),
|
|
1718
876
|
permanent,
|
|
1719
|
-
value: null,
|
|
1720
877
|
sender: createResolvedFieldSenderSummary(operation.sender, operation.senderSelector),
|
|
1721
878
|
assumeYes: options.assumeYes,
|
|
1722
879
|
});
|
|
@@ -1740,7 +897,7 @@ export async function setField(options) {
|
|
|
1740
897
|
openReadContext: options.openReadContext,
|
|
1741
898
|
attachService: options.attachService,
|
|
1742
899
|
rpcFactory: options.rpcFactory,
|
|
1743
|
-
async createMutation(operation, existing) {
|
|
900
|
+
async createMutation(operation, existing, feeSelection) {
|
|
1744
901
|
const normalizedDomainName = normalizeDomainName(options.domainName);
|
|
1745
902
|
const normalizedFieldName = normalizeFieldName(options.fieldName);
|
|
1746
903
|
const observedField = getObservedFieldState(operation.readContext, normalizedDomainName, normalizedFieldName);
|
|
@@ -1771,6 +928,7 @@ export async function setField(options) {
|
|
|
1771
928
|
sender: operation.sender,
|
|
1772
929
|
intentFingerprintHex,
|
|
1773
930
|
nowUnixMs: options.nowUnixMs ?? Date.now(),
|
|
931
|
+
feeSelection,
|
|
1774
932
|
existing,
|
|
1775
933
|
fieldId: observedField.fieldId,
|
|
1776
934
|
fieldPermanent: observedField.permanent,
|
|
@@ -1814,7 +972,7 @@ export async function clearField(options) {
|
|
|
1814
972
|
openReadContext: options.openReadContext,
|
|
1815
973
|
attachService: options.attachService,
|
|
1816
974
|
rpcFactory: options.rpcFactory,
|
|
1817
|
-
async createMutation(operation, existing) {
|
|
975
|
+
async createMutation(operation, existing, feeSelection) {
|
|
1818
976
|
const normalizedDomainName = normalizeDomainName(options.domainName);
|
|
1819
977
|
const normalizedFieldName = normalizeFieldName(options.fieldName);
|
|
1820
978
|
const observedField = getObservedFieldState(operation.readContext, normalizedDomainName, normalizedFieldName);
|
|
@@ -1839,6 +997,7 @@ export async function clearField(options) {
|
|
|
1839
997
|
sender: operation.sender,
|
|
1840
998
|
intentFingerprintHex,
|
|
1841
999
|
nowUnixMs: options.nowUnixMs ?? Date.now(),
|
|
1000
|
+
feeSelection,
|
|
1842
1001
|
existing,
|
|
1843
1002
|
fieldId: observedField.fieldId,
|
|
1844
1003
|
fieldPermanent: observedField.permanent,
|