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