@cogcoin/client 1.1.7 → 1.1.9
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 +1 -1
- package/dist/bitcoind/service.js +1 -1
- package/dist/cli/commands/mining-read.js +1 -1
- package/dist/cli/commands/wallet-mutation/anchor.d.ts +2 -0
- package/dist/cli/commands/wallet-mutation/anchor.js +33 -0
- package/dist/cli/commands/wallet-mutation/bitcoin-transfer.d.ts +2 -0
- package/dist/cli/commands/wallet-mutation/bitcoin-transfer.js +32 -0
- package/dist/cli/commands/wallet-mutation/cog.d.ts +2 -0
- package/dist/cli/commands/wallet-mutation/cog.js +131 -0
- package/dist/cli/commands/wallet-mutation/context.d.ts +3 -0
- package/dist/cli/commands/wallet-mutation/context.js +18 -0
- package/dist/cli/commands/wallet-mutation/domain-admin.d.ts +2 -0
- package/dist/cli/commands/wallet-mutation/domain-admin.js +173 -0
- package/dist/cli/commands/wallet-mutation/domain-market.d.ts +2 -0
- package/dist/cli/commands/wallet-mutation/domain-market.js +107 -0
- package/dist/cli/commands/wallet-mutation/field.d.ts +2 -0
- package/dist/cli/commands/wallet-mutation/field.js +125 -0
- package/dist/cli/commands/wallet-mutation/register.d.ts +2 -0
- package/dist/cli/commands/wallet-mutation/register.js +38 -0
- package/dist/cli/commands/wallet-mutation/registry.d.ts +3 -0
- package/dist/cli/commands/wallet-mutation/registry.js +39 -0
- package/dist/cli/commands/wallet-mutation/reputation.d.ts +2 -0
- package/dist/cli/commands/wallet-mutation/reputation.js +57 -0
- package/dist/cli/commands/wallet-mutation/types.d.ts +32 -0
- package/dist/cli/commands/wallet-mutation/types.js +1 -0
- package/dist/cli/commands/wallet-mutation.js +13 -765
- package/dist/cli/commands/wallet-read.js +4 -4
- package/dist/cli/mutation-success.d.ts +0 -2
- package/dist/cli/output/classify.d.ts +7 -0
- package/dist/cli/output/classify.js +94 -0
- package/dist/cli/output/render.d.ts +2 -0
- package/dist/cli/output/render.js +13 -0
- package/dist/cli/output/rules/cli-surface.d.ts +2 -0
- package/dist/cli/output/rules/cli-surface.js +110 -0
- package/dist/cli/output/rules/generic.d.ts +2 -0
- package/dist/cli/output/rules/generic.js +13 -0
- package/dist/cli/output/rules/index.d.ts +2 -0
- package/dist/cli/output/rules/index.js +24 -0
- package/dist/cli/output/rules/mining-update.d.ts +2 -0
- package/dist/cli/output/rules/mining-update.js +68 -0
- package/dist/cli/output/rules/services.d.ts +2 -0
- package/dist/cli/output/rules/services.js +110 -0
- package/dist/cli/output/rules/wallet-admin.d.ts +2 -0
- package/dist/cli/output/rules/wallet-admin.js +224 -0
- package/dist/cli/output/rules/wallet-mutations.d.ts +2 -0
- package/dist/cli/output/rules/wallet-mutations.js +274 -0
- package/dist/cli/output/types.d.ts +16 -0
- package/dist/cli/output/types.js +1 -0
- package/dist/cli/output.d.ts +2 -168
- package/dist/cli/output.js +6 -989
- package/dist/cli/pagination.d.ts +15 -0
- package/dist/cli/pagination.js +16 -0
- package/dist/cli/recommendations.d.ts +4 -0
- package/dist/cli/recommendations.js +108 -0
- package/dist/cli/wallet-format/availability.d.ts +5 -0
- package/dist/cli/wallet-format/availability.js +96 -0
- package/dist/cli/wallet-format/balance.d.ts +2 -0
- package/dist/cli/wallet-format/balance.js +162 -0
- package/dist/cli/wallet-format/domains.d.ts +8 -0
- package/dist/cli/wallet-format/domains.js +84 -0
- package/dist/cli/wallet-format/fields.d.ts +6 -0
- package/dist/cli/wallet-format/fields.js +61 -0
- package/dist/cli/wallet-format/identity.d.ts +5 -0
- package/dist/cli/wallet-format/identity.js +19 -0
- package/dist/cli/wallet-format/locks.d.ts +7 -0
- package/dist/cli/wallet-format/locks.js +52 -0
- package/dist/cli/wallet-format/overview.d.ts +2 -0
- package/dist/cli/wallet-format/overview.js +122 -0
- package/dist/cli/wallet-format/pending.d.ts +13 -0
- package/dist/cli/wallet-format/pending.js +101 -0
- package/dist/cli/wallet-format/shared.d.ts +7 -0
- package/dist/cli/wallet-format/shared.js +31 -0
- package/dist/cli/wallet-format/status.d.ts +3 -0
- package/dist/cli/wallet-format/status.js +27 -0
- package/dist/cli/wallet-format.d.ts +8 -30
- package/dist/cli/wallet-format.js +8 -830
- package/dist/cli/wallet-read-helpers.d.ts +6 -0
- package/dist/cli/wallet-read-helpers.js +17 -0
- package/dist/wallet/mining/candidate.d.ts +1 -1
- package/dist/wallet/mining/candidate.js +3 -3
- package/dist/wallet/mining/constants.d.ts +2 -2
- package/dist/wallet/mining/constants.js +2 -2
- package/dist/wallet/mining/engine-state.js +10 -0
- package/dist/wallet/mining/sentence-protocol.d.ts +2 -2
- package/dist/wallet/mining/sentences.js +8 -8
- package/dist/wallet/mining/visualizer-sync.js +79 -15
- package/dist/wallet/read/context.js +1 -1
- package/dist/wallet/reset/artifacts.d.ts +16 -0
- package/dist/wallet/reset/artifacts.js +141 -0
- package/dist/wallet/reset/execution.d.ts +38 -0
- package/dist/wallet/reset/execution.js +458 -0
- package/dist/wallet/reset/preflight.d.ts +7 -0
- package/dist/wallet/reset/preflight.js +116 -0
- package/dist/wallet/reset/preview.d.ts +2 -0
- package/dist/wallet/reset/preview.js +50 -0
- package/dist/wallet/reset/process-cleanup.d.ts +12 -0
- package/dist/wallet/reset/process-cleanup.js +179 -0
- package/dist/wallet/reset/types.d.ts +189 -0
- package/dist/wallet/reset/types.js +1 -0
- package/dist/wallet/reset.d.ts +4 -119
- package/dist/wallet/reset.js +4 -882
- package/dist/wallet/tx/anchor/confirm.d.ts +15 -0
- package/dist/wallet/tx/anchor/confirm.js +60 -0
- package/dist/wallet/tx/anchor/draft.d.ts +39 -0
- package/dist/wallet/tx/anchor/draft.js +167 -0
- package/dist/wallet/tx/anchor/index.d.ts +5 -0
- package/dist/wallet/tx/anchor/index.js +148 -0
- package/dist/wallet/tx/anchor/intent.d.ts +61 -0
- package/dist/wallet/tx/anchor/intent.js +101 -0
- package/dist/wallet/tx/anchor/plan.d.ts +3 -0
- package/dist/wallet/tx/anchor/plan.js +18 -0
- package/dist/wallet/tx/anchor/result.d.ts +25 -0
- package/dist/wallet/tx/anchor/result.js +20 -0
- package/dist/wallet/tx/anchor.d.ts +1 -39
- package/dist/wallet/tx/anchor.js +1 -494
- package/dist/wallet/tx/bitcoin-transfer/confirm.d.ts +7 -0
- package/dist/wallet/tx/bitcoin-transfer/confirm.js +11 -0
- package/dist/wallet/tx/bitcoin-transfer/index.d.ts +5 -0
- package/dist/wallet/tx/bitcoin-transfer/index.js +112 -0
- package/dist/wallet/tx/bitcoin-transfer/intent.d.ts +52 -0
- package/dist/wallet/tx/bitcoin-transfer/intent.js +74 -0
- package/dist/wallet/tx/bitcoin-transfer/plan.d.ts +5 -0
- package/dist/wallet/tx/bitcoin-transfer/plan.js +21 -0
- package/dist/wallet/tx/bitcoin-transfer/result.d.ts +19 -0
- package/dist/wallet/tx/bitcoin-transfer/result.js +16 -0
- package/dist/wallet/tx/bitcoin-transfer.d.ts +1 -35
- package/dist/wallet/tx/bitcoin-transfer.js +1 -200
- package/dist/wallet/tx/cog/confirm.d.ts +13 -0
- package/dist/wallet/tx/cog/confirm.js +59 -0
- package/dist/wallet/tx/cog/draft.d.ts +20 -0
- package/dist/wallet/tx/cog/draft.js +114 -0
- package/dist/wallet/tx/cog/index.d.ts +6 -0
- package/dist/wallet/tx/cog/index.js +117 -0
- package/dist/wallet/tx/cog/intent.d.ts +30 -0
- package/dist/wallet/tx/cog/intent.js +169 -0
- package/dist/wallet/tx/cog/plan.d.ts +19 -0
- package/dist/wallet/tx/cog/plan.js +65 -0
- package/dist/wallet/tx/cog/result.d.ts +27 -0
- package/dist/wallet/tx/cog/result.js +28 -0
- package/dist/wallet/tx/cog/types.d.ts +186 -0
- package/dist/wallet/tx/cog/types.js +2 -0
- package/dist/wallet/tx/cog/variants/claim.d.ts +3 -0
- package/dist/wallet/tx/cog/variants/claim.js +92 -0
- package/dist/wallet/tx/cog/variants/lock.d.ts +2 -0
- package/dist/wallet/tx/cog/variants/lock.js +102 -0
- package/dist/wallet/tx/cog/variants/send.d.ts +2 -0
- package/dist/wallet/tx/cog/variants/send.js +77 -0
- package/dist/wallet/tx/cog.d.ts +1 -96
- package/dist/wallet/tx/cog.js +1 -824
- package/dist/wallet/tx/common.d.ts +14 -199
- package/dist/wallet/tx/common.js +10 -493
- package/dist/wallet/tx/domain-admin/confirm.d.ts +17 -0
- package/dist/wallet/tx/domain-admin/confirm.js +58 -0
- package/dist/wallet/tx/domain-admin/draft.d.ts +20 -0
- package/dist/wallet/tx/domain-admin/draft.js +161 -0
- package/dist/wallet/tx/domain-admin/index.d.ts +9 -0
- package/dist/wallet/tx/domain-admin/index.js +150 -0
- package/dist/wallet/tx/domain-admin/intent.d.ts +12 -0
- package/dist/wallet/tx/domain-admin/intent.js +61 -0
- package/dist/wallet/tx/domain-admin/plan.d.ts +19 -0
- package/dist/wallet/tx/domain-admin/plan.js +64 -0
- package/dist/wallet/tx/domain-admin/result.d.ts +19 -0
- package/dist/wallet/tx/domain-admin/result.js +33 -0
- package/dist/wallet/tx/domain-admin/types.d.ts +162 -0
- package/dist/wallet/tx/domain-admin/types.js +1 -0
- package/dist/wallet/tx/domain-admin/variants/canonical.d.ts +2 -0
- package/dist/wallet/tx/domain-admin/variants/canonical.js +22 -0
- package/dist/wallet/tx/domain-admin/variants/delegate.d.ts +3 -0
- package/dist/wallet/tx/domain-admin/variants/delegate.js +60 -0
- package/dist/wallet/tx/domain-admin/variants/endpoint.d.ts +3 -0
- package/dist/wallet/tx/domain-admin/variants/endpoint.js +102 -0
- package/dist/wallet/tx/domain-admin/variants/miner.d.ts +3 -0
- package/dist/wallet/tx/domain-admin/variants/miner.js +59 -0
- package/dist/wallet/tx/domain-admin.d.ts +1 -107
- package/dist/wallet/tx/domain-admin.js +1 -729
- package/dist/wallet/tx/domain-market/confirm.d.ts +6 -0
- package/dist/wallet/tx/domain-market/confirm.js +52 -0
- package/dist/wallet/tx/domain-market/draft.d.ts +43 -0
- package/dist/wallet/tx/domain-market/draft.js +286 -0
- package/dist/wallet/tx/domain-market/index.d.ts +6 -0
- package/dist/wallet/tx/domain-market/index.js +145 -0
- package/dist/wallet/tx/domain-market/intent.d.ts +15 -0
- package/dist/wallet/tx/domain-market/intent.js +131 -0
- package/dist/wallet/tx/domain-market/plan.d.ts +31 -0
- package/dist/wallet/tx/domain-market/plan.js +98 -0
- package/dist/wallet/tx/domain-market/result.d.ts +45 -0
- package/dist/wallet/tx/domain-market/result.js +88 -0
- package/dist/wallet/tx/domain-market/types.d.ts +221 -0
- package/dist/wallet/tx/domain-market/types.js +1 -0
- package/dist/wallet/tx/domain-market/variants/buy.d.ts +2 -0
- package/dist/wallet/tx/domain-market/variants/buy.js +103 -0
- package/dist/wallet/tx/domain-market/variants/sell.d.ts +2 -0
- package/dist/wallet/tx/domain-market/variants/sell.js +91 -0
- package/dist/wallet/tx/domain-market/variants/transfer.d.ts +2 -0
- package/dist/wallet/tx/domain-market/variants/transfer.js +105 -0
- package/dist/wallet/tx/domain-market.d.ts +1 -116
- package/dist/wallet/tx/domain-market.js +1 -1078
- package/dist/wallet/tx/draft-build.d.ts +60 -0
- package/dist/wallet/tx/draft-build.js +127 -0
- package/dist/wallet/tx/executor.d.ts +6 -40
- package/dist/wallet/tx/executor.js +6 -100
- package/dist/wallet/tx/fee.d.ts +30 -0
- package/dist/wallet/tx/fee.js +98 -0
- package/dist/wallet/tx/field/confirm.d.ts +11 -0
- package/dist/wallet/tx/field/confirm.js +19 -0
- package/dist/wallet/tx/field/draft.d.ts +23 -0
- package/dist/wallet/tx/field/draft.js +202 -0
- package/dist/wallet/tx/field/index.d.ts +5 -0
- package/dist/wallet/tx/field/index.js +140 -0
- package/dist/wallet/tx/field/intent.d.ts +5 -0
- package/dist/wallet/tx/field/intent.js +50 -0
- package/dist/wallet/tx/field/plan.d.ts +20 -0
- package/dist/wallet/tx/field/plan.js +65 -0
- package/dist/wallet/tx/field/result.d.ts +29 -0
- package/dist/wallet/tx/field/result.js +103 -0
- package/dist/wallet/tx/field/types.d.ts +163 -0
- package/dist/wallet/tx/field/types.js +1 -0
- package/dist/wallet/tx/field/variants/clear.d.ts +2 -0
- package/dist/wallet/tx/field/variants/clear.js +60 -0
- package/dist/wallet/tx/field/variants/create.d.ts +2 -0
- package/dist/wallet/tx/field/variants/create.js +67 -0
- package/dist/wallet/tx/field/variants/set.d.ts +2 -0
- package/dist/wallet/tx/field/variants/set.js +195 -0
- package/dist/wallet/tx/field.d.ts +1 -95
- package/dist/wallet/tx/field.js +1 -920
- package/dist/wallet/tx/mining-preemption.d.ts +6 -0
- package/dist/wallet/tx/mining-preemption.js +7 -0
- package/dist/wallet/tx/primitives.d.ts +13 -0
- package/dist/wallet/tx/primitives.js +42 -0
- package/dist/wallet/tx/psbt-assert.d.ts +14 -0
- package/dist/wallet/tx/psbt-assert.js +39 -0
- package/dist/wallet/tx/publish.d.ts +37 -0
- package/dist/wallet/tx/publish.js +88 -0
- package/dist/wallet/tx/readiness.d.ts +7 -0
- package/dist/wallet/tx/readiness.js +61 -0
- package/dist/wallet/tx/reconcile.d.ts +24 -0
- package/dist/wallet/tx/reconcile.js +72 -0
- package/dist/wallet/tx/register/confirm.d.ts +6 -0
- package/dist/wallet/tx/register/confirm.js +66 -0
- package/dist/wallet/tx/register/draft.d.ts +42 -0
- package/dist/wallet/tx/register/draft.js +181 -0
- package/dist/wallet/tx/register/index.d.ts +6 -0
- package/dist/wallet/tx/register/index.js +158 -0
- package/dist/wallet/tx/register/intent.d.ts +74 -0
- package/dist/wallet/tx/register/intent.js +119 -0
- package/dist/wallet/tx/register/plan.d.ts +43 -0
- package/dist/wallet/tx/register/plan.js +168 -0
- package/dist/wallet/tx/register/result.d.ts +78 -0
- package/dist/wallet/tx/register/result.js +41 -0
- package/dist/wallet/tx/register.d.ts +1 -70
- package/dist/wallet/tx/register.js +1 -681
- package/dist/wallet/tx/reputation/confirm.d.ts +11 -0
- package/dist/wallet/tx/reputation/confirm.js +51 -0
- package/dist/wallet/tx/reputation/draft.d.ts +20 -0
- package/dist/wallet/tx/reputation/draft.js +130 -0
- package/dist/wallet/tx/reputation/index.d.ts +4 -0
- package/dist/wallet/tx/reputation/index.js +162 -0
- package/dist/wallet/tx/reputation/intent.d.ts +36 -0
- package/dist/wallet/tx/reputation/intent.js +157 -0
- package/dist/wallet/tx/reputation/plan.d.ts +19 -0
- package/dist/wallet/tx/reputation/plan.js +64 -0
- package/dist/wallet/tx/reputation/result.d.ts +21 -0
- package/dist/wallet/tx/reputation/result.js +31 -0
- package/dist/wallet/tx/reputation/types.d.ts +130 -0
- package/dist/wallet/tx/reputation/types.js +1 -0
- package/dist/wallet/tx/reputation.d.ts +1 -74
- package/dist/wallet/tx/reputation.js +1 -556
- package/dist/wallet/tx/signing.d.ts +18 -0
- package/dist/wallet/tx/signing.js +31 -0
- package/dist/wallet/tx/state-persist.d.ts +27 -0
- package/dist/wallet/tx/state-persist.js +54 -0
- package/dist/wallet/tx/types.d.ts +44 -0
- package/dist/wallet/tx/types.js +1 -0
- package/package.json +1 -1
- package/dist/cli/mining-json.d.ts +0 -20
- package/dist/cli/mining-json.js +0 -46
- package/dist/cli/mutation-json.d.ts +0 -325
- package/dist/cli/mutation-json.js +0 -269
- package/dist/cli/mutation-resolved-json.d.ts +0 -117
- package/dist/cli/mutation-resolved-json.js +0 -123
- package/dist/cli/preview-json.d.ts +0 -319
- package/dist/cli/preview-json.js +0 -254
- package/dist/cli/read-json.d.ts +0 -190
- package/dist/cli/read-json.js +0 -627
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { WalletPrompter } from "../../lifecycle.js";
|
|
2
|
+
export interface AnchorFoundingMessage {
|
|
3
|
+
text: string | null;
|
|
4
|
+
payloadHex: string | null;
|
|
5
|
+
}
|
|
6
|
+
export declare function resolveFoundingMessage(options: {
|
|
7
|
+
foundingMessageText: string | null | undefined;
|
|
8
|
+
promptForFoundingMessageWhenMissing?: boolean;
|
|
9
|
+
prompter: WalletPrompter;
|
|
10
|
+
}): Promise<AnchorFoundingMessage>;
|
|
11
|
+
export declare function confirmDirectAnchor(prompter: WalletPrompter, options: {
|
|
12
|
+
domainName: string;
|
|
13
|
+
walletAddress: string;
|
|
14
|
+
foundingMessageText: string | null;
|
|
15
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { encodeSentence } from "@cogcoin/scoring";
|
|
2
|
+
function encodeFoundingMessage(foundingMessageText) {
|
|
3
|
+
const trimmed = foundingMessageText?.trim() ?? "";
|
|
4
|
+
if (trimmed === "") {
|
|
5
|
+
return Promise.resolve({
|
|
6
|
+
text: null,
|
|
7
|
+
payloadHex: null,
|
|
8
|
+
});
|
|
9
|
+
}
|
|
10
|
+
return encodeSentence(trimmed)
|
|
11
|
+
.then((payload) => ({
|
|
12
|
+
text: trimmed,
|
|
13
|
+
payloadHex: Buffer.from(payload).toString("hex"),
|
|
14
|
+
}))
|
|
15
|
+
.catch((error) => {
|
|
16
|
+
throw new Error(error instanceof Error ? `wallet_anchor_invalid_message_${error.message}` : "wallet_anchor_invalid_message");
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
function extractAnchorInvalidMessageReason(error) {
|
|
20
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
21
|
+
if (message === "wallet_anchor_invalid_message") {
|
|
22
|
+
return null;
|
|
23
|
+
}
|
|
24
|
+
if (!message.startsWith("wallet_anchor_invalid_message_")) {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
const reason = message.slice("wallet_anchor_invalid_message_".length).trim();
|
|
28
|
+
return reason === "" ? null : reason;
|
|
29
|
+
}
|
|
30
|
+
export async function resolveFoundingMessage(options) {
|
|
31
|
+
if (!options.promptForFoundingMessageWhenMissing || options.foundingMessageText != null) {
|
|
32
|
+
return encodeFoundingMessage(options.foundingMessageText ?? null);
|
|
33
|
+
}
|
|
34
|
+
for (;;) {
|
|
35
|
+
const answer = await options.prompter.prompt("Founding message (optional, press Enter to skip): ");
|
|
36
|
+
try {
|
|
37
|
+
return await encodeFoundingMessage(answer);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
const reason = extractAnchorInvalidMessageReason(error);
|
|
41
|
+
options.prompter.writeLine("Founding message cannot be encoded in canonical Coglex.");
|
|
42
|
+
if (reason !== null) {
|
|
43
|
+
options.prompter.writeLine(`Reason: ${reason}`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
export async function confirmDirectAnchor(prompter, options) {
|
|
49
|
+
prompter.writeLine(`You are anchoring "${options.domainName}".`);
|
|
50
|
+
prompter.writeLine(`Wallet address: ${options.walletAddress}`);
|
|
51
|
+
prompter.writeLine("Anchoring publishes a standalone DOMAIN_ANCHOR from the local wallet address.");
|
|
52
|
+
if (options.foundingMessageText !== null) {
|
|
53
|
+
prompter.writeLine("The founding message bytes will be public in mempool and on-chain.");
|
|
54
|
+
prompter.writeLine(`Founding message: ${options.foundingMessageText}`);
|
|
55
|
+
}
|
|
56
|
+
const answer = (await prompter.prompt("Type the domain name to continue: ")).trim();
|
|
57
|
+
if (answer !== options.domainName) {
|
|
58
|
+
throw new Error("wallet_anchor_confirmation_rejected");
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { WalletRuntimePaths } from "../../runtime.js";
|
|
2
|
+
import type { WalletSecretProvider } from "../../state/provider.js";
|
|
3
|
+
import type { PendingMutationRecord, WalletStateV1 } from "../../types.js";
|
|
4
|
+
import type { WalletReadContext } from "../../read/index.js";
|
|
5
|
+
import { type WalletMutationFeeSelection } from "../common.js";
|
|
6
|
+
import type { AnchorMutationOperation, WalletAnchorRpcClient } from "./intent.js";
|
|
7
|
+
export declare function createDraftAnchorMutation(options: {
|
|
8
|
+
state: WalletStateV1;
|
|
9
|
+
domainName: string;
|
|
10
|
+
intentFingerprintHex: string;
|
|
11
|
+
nowUnixMs: number;
|
|
12
|
+
feeSelection: WalletMutationFeeSelection;
|
|
13
|
+
existing?: PendingMutationRecord | null;
|
|
14
|
+
}): PendingMutationRecord;
|
|
15
|
+
export declare function upsertAnchoredDomainRecord(options: {
|
|
16
|
+
state: WalletStateV1;
|
|
17
|
+
domainName: string;
|
|
18
|
+
domainId: number;
|
|
19
|
+
foundingMessageText: string | null;
|
|
20
|
+
}): WalletStateV1;
|
|
21
|
+
export declare function anchorConfirmedOnSnapshot(options: {
|
|
22
|
+
snapshot: NonNullable<WalletReadContext["snapshot"]>;
|
|
23
|
+
state: WalletStateV1;
|
|
24
|
+
domainName: string;
|
|
25
|
+
}): boolean;
|
|
26
|
+
export declare function reconcilePendingAnchorMutation(options: {
|
|
27
|
+
operation: AnchorMutationOperation;
|
|
28
|
+
mutation: PendingMutationRecord;
|
|
29
|
+
provider: WalletSecretProvider;
|
|
30
|
+
nowUnixMs: number;
|
|
31
|
+
paths: WalletRuntimePaths;
|
|
32
|
+
rpc: WalletAnchorRpcClient;
|
|
33
|
+
walletName: string;
|
|
34
|
+
context: WalletReadContext;
|
|
35
|
+
}): Promise<{
|
|
36
|
+
state: WalletStateV1;
|
|
37
|
+
mutation: PendingMutationRecord;
|
|
38
|
+
resolution: "confirmed" | "live" | "repair-required" | "not-seen" | "continue";
|
|
39
|
+
}>;
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { randomBytes } from "node:crypto";
|
|
2
|
+
import { lookupDomain } from "@cogcoin/indexer/queries";
|
|
3
|
+
import { createWalletMutationFeeMetadata, unlockTemporaryBuilderLocks, updateMutationRecord, } from "../common.js";
|
|
4
|
+
import { persistWalletMutationState } from "../executor.js";
|
|
5
|
+
import { upsertPendingMutation } from "../journal.js";
|
|
6
|
+
export function createDraftAnchorMutation(options) {
|
|
7
|
+
const existing = options.existing ?? null;
|
|
8
|
+
if (existing !== null) {
|
|
9
|
+
return {
|
|
10
|
+
...existing,
|
|
11
|
+
kind: "anchor",
|
|
12
|
+
domainName: options.domainName,
|
|
13
|
+
parentDomainName: null,
|
|
14
|
+
senderScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
|
|
15
|
+
senderLocalIndex: 0,
|
|
16
|
+
intentFingerprintHex: options.intentFingerprintHex,
|
|
17
|
+
status: "draft",
|
|
18
|
+
lastUpdatedAtUnixMs: options.nowUnixMs,
|
|
19
|
+
attemptedTxid: null,
|
|
20
|
+
attemptedWtxid: null,
|
|
21
|
+
...createWalletMutationFeeMetadata(options.feeSelection),
|
|
22
|
+
temporaryBuilderLockedOutpoints: [],
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
return {
|
|
26
|
+
mutationId: randomBytes(12).toString("hex"),
|
|
27
|
+
kind: "anchor",
|
|
28
|
+
domainName: options.domainName,
|
|
29
|
+
parentDomainName: null,
|
|
30
|
+
senderScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
|
|
31
|
+
senderLocalIndex: 0,
|
|
32
|
+
intentFingerprintHex: options.intentFingerprintHex,
|
|
33
|
+
status: "draft",
|
|
34
|
+
createdAtUnixMs: options.nowUnixMs,
|
|
35
|
+
lastUpdatedAtUnixMs: options.nowUnixMs,
|
|
36
|
+
attemptedTxid: null,
|
|
37
|
+
attemptedWtxid: null,
|
|
38
|
+
...createWalletMutationFeeMetadata(options.feeSelection),
|
|
39
|
+
temporaryBuilderLockedOutpoints: [],
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
export function upsertAnchoredDomainRecord(options) {
|
|
43
|
+
const domains = options.state.domains.slice();
|
|
44
|
+
const existingIndex = domains.findIndex((entry) => entry.name === options.domainName);
|
|
45
|
+
const current = existingIndex >= 0 ? domains[existingIndex] : null;
|
|
46
|
+
const nextRecord = {
|
|
47
|
+
name: options.domainName,
|
|
48
|
+
domainId: options.domainId,
|
|
49
|
+
currentOwnerScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
|
|
50
|
+
canonicalChainStatus: "anchored",
|
|
51
|
+
foundingMessageText: options.foundingMessageText ?? current?.foundingMessageText ?? null,
|
|
52
|
+
birthTime: current?.birthTime ?? options.state.lastWrittenAtUnixMs,
|
|
53
|
+
};
|
|
54
|
+
if (existingIndex >= 0) {
|
|
55
|
+
domains[existingIndex] = nextRecord;
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
domains.push(nextRecord);
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
...options.state,
|
|
62
|
+
domains,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
export function anchorConfirmedOnSnapshot(options) {
|
|
66
|
+
const chainDomain = lookupDomain(options.snapshot.state, options.domainName);
|
|
67
|
+
if (chainDomain === null || !chainDomain.anchored) {
|
|
68
|
+
return false;
|
|
69
|
+
}
|
|
70
|
+
const ownerHex = Buffer.from(chainDomain.ownerScriptPubKey).toString("hex");
|
|
71
|
+
return ownerHex === options.state.funding.scriptPubKeyHex
|
|
72
|
+
|| (options.state.localScriptPubKeyHexes ?? []).includes(ownerHex);
|
|
73
|
+
}
|
|
74
|
+
async function saveState(options) {
|
|
75
|
+
return persistWalletMutationState(options);
|
|
76
|
+
}
|
|
77
|
+
export async function reconcilePendingAnchorMutation(options) {
|
|
78
|
+
if (options.mutation.status === "repair-required") {
|
|
79
|
+
return {
|
|
80
|
+
state: options.operation.state,
|
|
81
|
+
mutation: options.mutation,
|
|
82
|
+
resolution: "repair-required",
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (options.context.snapshot !== null && anchorConfirmedOnSnapshot({
|
|
86
|
+
snapshot: options.context.snapshot,
|
|
87
|
+
state: options.operation.state,
|
|
88
|
+
domainName: options.mutation.domainName,
|
|
89
|
+
})) {
|
|
90
|
+
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.mutation.temporaryBuilderLockedOutpoints);
|
|
91
|
+
const confirmedMutation = updateMutationRecord(options.mutation, "confirmed", options.nowUnixMs, {
|
|
92
|
+
temporaryBuilderLockedOutpoints: [],
|
|
93
|
+
});
|
|
94
|
+
const chainDomain = lookupDomain(options.context.snapshot.state, options.mutation.domainName);
|
|
95
|
+
const nextState = upsertAnchoredDomainRecord({
|
|
96
|
+
state: upsertPendingMutation(options.operation.state, confirmedMutation),
|
|
97
|
+
domainName: options.mutation.domainName,
|
|
98
|
+
domainId: chainDomain?.domainId ?? 0,
|
|
99
|
+
foundingMessageText: options.operation.message.text,
|
|
100
|
+
});
|
|
101
|
+
return {
|
|
102
|
+
state: await saveState({
|
|
103
|
+
state: nextState,
|
|
104
|
+
provider: options.provider,
|
|
105
|
+
nowUnixMs: options.nowUnixMs,
|
|
106
|
+
paths: options.paths,
|
|
107
|
+
}),
|
|
108
|
+
mutation: confirmedMutation,
|
|
109
|
+
resolution: "confirmed",
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
if (options.mutation.attemptedTxid !== null) {
|
|
113
|
+
const mempool = await options.rpc.getRawMempool().catch(() => []);
|
|
114
|
+
if (mempool.includes(options.mutation.attemptedTxid)) {
|
|
115
|
+
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.mutation.temporaryBuilderLockedOutpoints);
|
|
116
|
+
const liveMutation = updateMutationRecord(options.mutation, "live", options.nowUnixMs, {
|
|
117
|
+
temporaryBuilderLockedOutpoints: [],
|
|
118
|
+
});
|
|
119
|
+
const domainId = (options.context.snapshot === null
|
|
120
|
+
? null
|
|
121
|
+
: lookupDomain(options.context.snapshot.state, options.mutation.domainName)?.domainId)
|
|
122
|
+
?? options.operation.state.domains.find((domain) => domain.name === options.mutation.domainName)?.domainId
|
|
123
|
+
?? 0;
|
|
124
|
+
const nextState = upsertAnchoredDomainRecord({
|
|
125
|
+
state: upsertPendingMutation(options.operation.state, liveMutation),
|
|
126
|
+
domainName: options.mutation.domainName,
|
|
127
|
+
domainId,
|
|
128
|
+
foundingMessageText: options.operation.message.text,
|
|
129
|
+
});
|
|
130
|
+
return {
|
|
131
|
+
state: await saveState({
|
|
132
|
+
state: nextState,
|
|
133
|
+
provider: options.provider,
|
|
134
|
+
nowUnixMs: options.nowUnixMs,
|
|
135
|
+
paths: options.paths,
|
|
136
|
+
}),
|
|
137
|
+
mutation: liveMutation,
|
|
138
|
+
resolution: "live",
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
if (options.mutation.status === "broadcast-unknown"
|
|
143
|
+
|| options.mutation.status === "live"
|
|
144
|
+
|| options.mutation.status === "draft"
|
|
145
|
+
|| options.mutation.status === "broadcasting") {
|
|
146
|
+
await unlockTemporaryBuilderLocks(options.rpc, options.walletName, options.mutation.temporaryBuilderLockedOutpoints);
|
|
147
|
+
const canceledMutation = updateMutationRecord(options.mutation, "canceled", options.nowUnixMs, {
|
|
148
|
+
temporaryBuilderLockedOutpoints: [],
|
|
149
|
+
});
|
|
150
|
+
const nextState = upsertPendingMutation(options.operation.state, canceledMutation);
|
|
151
|
+
return {
|
|
152
|
+
state: await saveState({
|
|
153
|
+
state: nextState,
|
|
154
|
+
provider: options.provider,
|
|
155
|
+
nowUnixMs: options.nowUnixMs,
|
|
156
|
+
paths: options.paths,
|
|
157
|
+
}),
|
|
158
|
+
mutation: canceledMutation,
|
|
159
|
+
resolution: "not-seen",
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
state: options.operation.state,
|
|
164
|
+
mutation: options.mutation,
|
|
165
|
+
resolution: "continue",
|
|
166
|
+
};
|
|
167
|
+
}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { type AnchorDomainOptions } from "./intent.js";
|
|
2
|
+
import { type AnchorDomainResult } from "./result.js";
|
|
3
|
+
export type { AnchorDomainOptions } from "./intent.js";
|
|
4
|
+
export type { AnchorDomainResult } from "./result.js";
|
|
5
|
+
export declare function anchorDomain(options: AnchorDomainOptions): Promise<AnchorDomainResult>;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import { buildWalletMutationTransactionWithReserveFallback, mergeFixedWalletInputs, updateMutationRecord, } from "../common.js";
|
|
2
|
+
import { executeWalletMutationOperation, publishWalletMutation, resolveExistingWalletMutation, } from "../executor.js";
|
|
3
|
+
import { upsertPendingMutation } from "../journal.js";
|
|
4
|
+
import { confirmDirectAnchor, resolveFoundingMessage } from "./confirm.js";
|
|
5
|
+
import { anchorConfirmedOnSnapshot, createDraftAnchorMutation, reconcilePendingAnchorMutation, upsertAnchoredDomainRecord, } from "./draft.js";
|
|
6
|
+
import { buildDirectAnchorPlan, createAnchorOperationFingerprint, normalizeAnchorDomainName, resolveAnchorOperation, } from "./intent.js";
|
|
7
|
+
import { validateDirectAnchorDraft } from "./plan.js";
|
|
8
|
+
import { createAnchorResult, createAnchorReuseResult, } from "./result.js";
|
|
9
|
+
export async function anchorDomain(options) {
|
|
10
|
+
if (!options.prompter.isInteractive) {
|
|
11
|
+
throw new Error("wallet_anchor_requires_tty");
|
|
12
|
+
}
|
|
13
|
+
const normalizedDomainName = normalizeAnchorDomainName(options.domainName);
|
|
14
|
+
const execution = await executeWalletMutationOperation({
|
|
15
|
+
...options,
|
|
16
|
+
controlLockPurpose: "wallet-anchor",
|
|
17
|
+
preemptionReason: "wallet-anchor",
|
|
18
|
+
async resolveOperation(readContext) {
|
|
19
|
+
const message = await resolveFoundingMessage({
|
|
20
|
+
foundingMessageText: options.foundingMessageText,
|
|
21
|
+
promptForFoundingMessageWhenMissing: options.promptForFoundingMessageWhenMissing,
|
|
22
|
+
prompter: options.prompter,
|
|
23
|
+
});
|
|
24
|
+
return resolveAnchorOperation({
|
|
25
|
+
readContext,
|
|
26
|
+
normalizedDomainName,
|
|
27
|
+
message,
|
|
28
|
+
});
|
|
29
|
+
},
|
|
30
|
+
createIntentFingerprint(operation) {
|
|
31
|
+
return createAnchorOperationFingerprint(operation);
|
|
32
|
+
},
|
|
33
|
+
async resolveExistingMutation({ operation, existingMutation, execution }) {
|
|
34
|
+
if (existingMutation === null) {
|
|
35
|
+
return { state: operation.state, replacementFixedInputs: null, result: null };
|
|
36
|
+
}
|
|
37
|
+
return resolveExistingWalletMutation({
|
|
38
|
+
existingMutation,
|
|
39
|
+
execution,
|
|
40
|
+
repairRequiredErrorCode: "wallet_anchor_repair_required",
|
|
41
|
+
reconcileExistingMutation: (mutation) => reconcilePendingAnchorMutation({
|
|
42
|
+
operation,
|
|
43
|
+
mutation,
|
|
44
|
+
provider: execution.provider,
|
|
45
|
+
nowUnixMs: execution.nowUnixMs,
|
|
46
|
+
paths: execution.paths,
|
|
47
|
+
rpc: execution.rpc,
|
|
48
|
+
walletName: execution.walletName,
|
|
49
|
+
context: execution.readContext,
|
|
50
|
+
}),
|
|
51
|
+
createReuseResult: ({ mutation, resolution, fees }) => createAnchorReuseResult({
|
|
52
|
+
operation,
|
|
53
|
+
mutation,
|
|
54
|
+
resolution,
|
|
55
|
+
fees,
|
|
56
|
+
}),
|
|
57
|
+
});
|
|
58
|
+
},
|
|
59
|
+
confirm({ operation }) {
|
|
60
|
+
return confirmDirectAnchor(options.prompter, {
|
|
61
|
+
domainName: operation.normalizedDomainName,
|
|
62
|
+
walletAddress: operation.state.funding.address,
|
|
63
|
+
foundingMessageText: operation.message.text,
|
|
64
|
+
});
|
|
65
|
+
},
|
|
66
|
+
createDraftMutation({ operation, existingMutation, execution, intentFingerprintHex }) {
|
|
67
|
+
return {
|
|
68
|
+
mutation: createDraftAnchorMutation({
|
|
69
|
+
state: operation.state,
|
|
70
|
+
domainName: operation.normalizedDomainName,
|
|
71
|
+
intentFingerprintHex,
|
|
72
|
+
nowUnixMs: execution.nowUnixMs,
|
|
73
|
+
feeSelection: execution.feeSelection,
|
|
74
|
+
existing: existingMutation ?? null,
|
|
75
|
+
}),
|
|
76
|
+
prepared: null,
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
async build({ operation, state, execution, replacementFixedInputs }) {
|
|
80
|
+
const directAnchorPlan = buildDirectAnchorPlan({
|
|
81
|
+
state,
|
|
82
|
+
allUtxos: await execution.rpc.listUnspent(execution.walletName, 1),
|
|
83
|
+
domainId: operation.chainDomain.domainId,
|
|
84
|
+
foundingMessagePayloadHex: operation.message.payloadHex,
|
|
85
|
+
});
|
|
86
|
+
return buildWalletMutationTransactionWithReserveFallback({
|
|
87
|
+
rpc: execution.rpc,
|
|
88
|
+
walletName: execution.walletName,
|
|
89
|
+
state,
|
|
90
|
+
plan: {
|
|
91
|
+
...directAnchorPlan,
|
|
92
|
+
fixedInputs: mergeFixedWalletInputs(directAnchorPlan.fixedInputs, replacementFixedInputs),
|
|
93
|
+
},
|
|
94
|
+
validateFundedDraft: validateDirectAnchorDraft,
|
|
95
|
+
finalizeErrorCode: "wallet_anchor_finalize_failed",
|
|
96
|
+
mempoolRejectPrefix: "wallet_anchor_mempool_rejected",
|
|
97
|
+
feeRate: execution.feeSelection.feeRateSatVb,
|
|
98
|
+
});
|
|
99
|
+
},
|
|
100
|
+
publish({ operation, state, execution, built, mutation }) {
|
|
101
|
+
return publishWalletMutation({
|
|
102
|
+
rpc: execution.rpc,
|
|
103
|
+
walletName: execution.walletName,
|
|
104
|
+
snapshotHeight: execution.readContext.snapshot?.tip?.height ?? null,
|
|
105
|
+
built,
|
|
106
|
+
mutation,
|
|
107
|
+
state,
|
|
108
|
+
provider: execution.provider,
|
|
109
|
+
nowUnixMs: execution.nowUnixMs,
|
|
110
|
+
paths: execution.paths,
|
|
111
|
+
errorPrefix: "wallet_anchor",
|
|
112
|
+
async afterAccepted({ state: acceptedState, broadcastingMutation, built, nowUnixMs }) {
|
|
113
|
+
const finalStatus = anchorConfirmedOnSnapshot({
|
|
114
|
+
snapshot: execution.readContext.snapshot,
|
|
115
|
+
state: acceptedState,
|
|
116
|
+
domainName: operation.normalizedDomainName,
|
|
117
|
+
}) ? "confirmed" : "live";
|
|
118
|
+
const finalMutation = updateMutationRecord(broadcastingMutation, finalStatus, nowUnixMs, {
|
|
119
|
+
attemptedTxid: built.txid,
|
|
120
|
+
attemptedWtxid: built.wtxid,
|
|
121
|
+
temporaryBuilderLockedOutpoints: [],
|
|
122
|
+
});
|
|
123
|
+
return {
|
|
124
|
+
state: upsertAnchoredDomainRecord({
|
|
125
|
+
state: upsertPendingMutation(acceptedState, finalMutation),
|
|
126
|
+
domainName: operation.normalizedDomainName,
|
|
127
|
+
domainId: operation.chainDomain.domainId,
|
|
128
|
+
foundingMessageText: operation.message.text,
|
|
129
|
+
}),
|
|
130
|
+
mutation: finalMutation,
|
|
131
|
+
status: finalStatus,
|
|
132
|
+
};
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
},
|
|
136
|
+
createResult({ operation, mutation, built, status, reusedExisting, fees }) {
|
|
137
|
+
return createAnchorResult({
|
|
138
|
+
operation,
|
|
139
|
+
mutation,
|
|
140
|
+
builtTxid: built?.txid ?? null,
|
|
141
|
+
status: status,
|
|
142
|
+
reusedExisting,
|
|
143
|
+
fees,
|
|
144
|
+
});
|
|
145
|
+
},
|
|
146
|
+
});
|
|
147
|
+
return execution.result;
|
|
148
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { lookupDomain } from "@cogcoin/indexer/queries";
|
|
2
|
+
import { attachOrStartManagedBitcoindService } from "../../../bitcoind/service.js";
|
|
3
|
+
import { createRpcClient } from "../../../bitcoind/node.js";
|
|
4
|
+
import type { RpcListUnspentEntry } from "../../../bitcoind/types.js";
|
|
5
|
+
import type { WalletPrompter } from "../../lifecycle.js";
|
|
6
|
+
import { openWalletReadContext, type WalletReadContext } from "../../read/index.js";
|
|
7
|
+
import type { WalletRuntimePaths } from "../../runtime.js";
|
|
8
|
+
import type { WalletSecretProvider } from "../../state/provider.js";
|
|
9
|
+
import type { WalletStateV1 } from "../../types.js";
|
|
10
|
+
import { type FixedWalletInput, type WalletMutationRpcClient } from "../common.js";
|
|
11
|
+
import type { AnchorFoundingMessage } from "./confirm.js";
|
|
12
|
+
export interface WalletAnchorRpcClient extends WalletMutationRpcClient {
|
|
13
|
+
getBlockchainInfo(): Promise<{
|
|
14
|
+
blocks: number;
|
|
15
|
+
}>;
|
|
16
|
+
sendRawTransaction(hex: string): Promise<string>;
|
|
17
|
+
getRawMempool(): Promise<string[]>;
|
|
18
|
+
}
|
|
19
|
+
export interface AnchorDomainOptions {
|
|
20
|
+
domainName: string;
|
|
21
|
+
foundingMessageText?: string | null;
|
|
22
|
+
promptForFoundingMessageWhenMissing?: boolean;
|
|
23
|
+
feeRateSatVb?: number | null;
|
|
24
|
+
dataDir: string;
|
|
25
|
+
databasePath: string;
|
|
26
|
+
provider?: WalletSecretProvider;
|
|
27
|
+
prompter: WalletPrompter;
|
|
28
|
+
nowUnixMs?: number;
|
|
29
|
+
paths?: WalletRuntimePaths;
|
|
30
|
+
openReadContext?: typeof openWalletReadContext;
|
|
31
|
+
attachService?: typeof attachOrStartManagedBitcoindService;
|
|
32
|
+
rpcFactory?: (config: Parameters<typeof createRpcClient>[0]) => WalletAnchorRpcClient;
|
|
33
|
+
}
|
|
34
|
+
export interface AnchorMutationOperation {
|
|
35
|
+
state: WalletStateV1;
|
|
36
|
+
normalizedDomainName: string;
|
|
37
|
+
chainDomain: NonNullable<ReturnType<typeof lookupDomain>>;
|
|
38
|
+
message: AnchorFoundingMessage;
|
|
39
|
+
}
|
|
40
|
+
export interface DirectAnchorPlan {
|
|
41
|
+
fixedInputs: FixedWalletInput[];
|
|
42
|
+
outputs: unknown[];
|
|
43
|
+
changeAddress: string;
|
|
44
|
+
changePosition: number;
|
|
45
|
+
expectedOpReturnScriptHex: string;
|
|
46
|
+
allowedFundingScriptPubKeyHex: string;
|
|
47
|
+
eligibleFundingOutpointKeys: Set<string>;
|
|
48
|
+
}
|
|
49
|
+
export declare function normalizeAnchorDomainName(domainName: string): string;
|
|
50
|
+
export declare function resolveAnchorOperation(options: {
|
|
51
|
+
readContext: WalletReadContext;
|
|
52
|
+
normalizedDomainName: string;
|
|
53
|
+
message: AnchorFoundingMessage;
|
|
54
|
+
}): AnchorMutationOperation;
|
|
55
|
+
export declare function createAnchorOperationFingerprint(operation: AnchorMutationOperation): string;
|
|
56
|
+
export declare function buildDirectAnchorPlan(options: {
|
|
57
|
+
state: WalletStateV1;
|
|
58
|
+
allUtxos: RpcListUnspentEntry[];
|
|
59
|
+
domainId: number;
|
|
60
|
+
foundingMessagePayloadHex: string | null;
|
|
61
|
+
}): DirectAnchorPlan;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { lookupDomain } from "@cogcoin/indexer/queries";
|
|
3
|
+
import { attachOrStartManagedBitcoindService } from "../../../bitcoind/service.js";
|
|
4
|
+
import { createRpcClient } from "../../../bitcoind/node.js";
|
|
5
|
+
import { openWalletReadContext } from "../../read/index.js";
|
|
6
|
+
import { serializeDomainAnchor, validateDomainName, } from "../../cogop/index.js";
|
|
7
|
+
import { assertWalletMutationContextReady, outpointKey, } from "../common.js";
|
|
8
|
+
function normalizeDomainName(domainName) {
|
|
9
|
+
const normalized = domainName.trim().toLowerCase();
|
|
10
|
+
if (normalized.length === 0) {
|
|
11
|
+
throw new Error("wallet_anchor_missing_domain");
|
|
12
|
+
}
|
|
13
|
+
validateDomainName(normalized);
|
|
14
|
+
return normalized;
|
|
15
|
+
}
|
|
16
|
+
function createIntentFingerprint(parts) {
|
|
17
|
+
return createHash("sha256")
|
|
18
|
+
.update(parts.map((part) => String(part)).join("\n"))
|
|
19
|
+
.digest("hex");
|
|
20
|
+
}
|
|
21
|
+
function encodeOpReturnScript(payload) {
|
|
22
|
+
if (payload.length <= 75) {
|
|
23
|
+
return Buffer.concat([
|
|
24
|
+
Buffer.from([0x6a, payload.length]),
|
|
25
|
+
Buffer.from(payload),
|
|
26
|
+
]).toString("hex");
|
|
27
|
+
}
|
|
28
|
+
return Buffer.concat([
|
|
29
|
+
Buffer.from([0x6a, 0x4c, payload.length]),
|
|
30
|
+
Buffer.from(payload),
|
|
31
|
+
]).toString("hex");
|
|
32
|
+
}
|
|
33
|
+
function sortUtxos(entries) {
|
|
34
|
+
return entries
|
|
35
|
+
.slice()
|
|
36
|
+
.sort((left, right) => right.amount - left.amount
|
|
37
|
+
|| left.txid.localeCompare(right.txid)
|
|
38
|
+
|| left.vout - right.vout);
|
|
39
|
+
}
|
|
40
|
+
function isSpendableFundingUtxo(entry, fundingScriptPubKeyHex) {
|
|
41
|
+
return entry.scriptPubKey === fundingScriptPubKeyHex
|
|
42
|
+
&& entry.confirmations >= 1
|
|
43
|
+
&& entry.spendable !== false
|
|
44
|
+
&& entry.safe !== false;
|
|
45
|
+
}
|
|
46
|
+
export function normalizeAnchorDomainName(domainName) {
|
|
47
|
+
return normalizeDomainName(domainName);
|
|
48
|
+
}
|
|
49
|
+
export function resolveAnchorOperation(options) {
|
|
50
|
+
assertWalletMutationContextReady(options.readContext, "wallet_anchor");
|
|
51
|
+
const state = options.readContext.localState.state;
|
|
52
|
+
const chainDomain = lookupDomain(options.readContext.snapshot.state, options.normalizedDomainName);
|
|
53
|
+
if (chainDomain === null) {
|
|
54
|
+
throw new Error("wallet_anchor_domain_not_found");
|
|
55
|
+
}
|
|
56
|
+
if (chainDomain.anchored) {
|
|
57
|
+
throw new Error("wallet_anchor_domain_already_anchored");
|
|
58
|
+
}
|
|
59
|
+
const ownerHex = Buffer.from(chainDomain.ownerScriptPubKey).toString("hex");
|
|
60
|
+
const localScriptHexes = new Set([
|
|
61
|
+
state.funding.scriptPubKeyHex,
|
|
62
|
+
...(state.localScriptPubKeyHexes ?? []),
|
|
63
|
+
]);
|
|
64
|
+
if (!localScriptHexes.has(ownerHex)) {
|
|
65
|
+
throw new Error("wallet_anchor_owner_not_locally_controlled");
|
|
66
|
+
}
|
|
67
|
+
if (state.funding.address.trim() === "") {
|
|
68
|
+
throw new Error("wallet_anchor_owner_identity_not_supported");
|
|
69
|
+
}
|
|
70
|
+
return {
|
|
71
|
+
state,
|
|
72
|
+
normalizedDomainName: options.normalizedDomainName,
|
|
73
|
+
chainDomain,
|
|
74
|
+
message: options.message,
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export function createAnchorOperationFingerprint(operation) {
|
|
78
|
+
return createIntentFingerprint([
|
|
79
|
+
"anchor",
|
|
80
|
+
operation.state.walletRootId,
|
|
81
|
+
operation.normalizedDomainName,
|
|
82
|
+
operation.state.funding.scriptPubKeyHex,
|
|
83
|
+
operation.message.payloadHex ?? "",
|
|
84
|
+
]);
|
|
85
|
+
}
|
|
86
|
+
export function buildDirectAnchorPlan(options) {
|
|
87
|
+
const fundingUtxos = sortUtxos(options.allUtxos.filter((entry) => isSpendableFundingUtxo(entry, options.state.funding.scriptPubKeyHex)));
|
|
88
|
+
const foundingPayload = options.foundingMessagePayloadHex === null
|
|
89
|
+
? undefined
|
|
90
|
+
: Buffer.from(options.foundingMessagePayloadHex, "hex");
|
|
91
|
+
const opReturnData = serializeDomainAnchor(options.domainId, foundingPayload).opReturnData;
|
|
92
|
+
return {
|
|
93
|
+
fixedInputs: [],
|
|
94
|
+
outputs: [{ data: Buffer.from(opReturnData).toString("hex") }],
|
|
95
|
+
changeAddress: options.state.funding.address,
|
|
96
|
+
changePosition: 1,
|
|
97
|
+
expectedOpReturnScriptHex: encodeOpReturnScript(opReturnData),
|
|
98
|
+
allowedFundingScriptPubKeyHex: options.state.funding.scriptPubKeyHex,
|
|
99
|
+
eligibleFundingOutpointKeys: new Set(fundingUtxos.map((entry) => outpointKey({ txid: entry.txid, vout: entry.vout }))),
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { BuiltWalletMutationTransaction } from "../common.js";
|
|
2
|
+
import type { DirectAnchorPlan } from "./intent.js";
|
|
3
|
+
export declare function validateDirectAnchorDraft(decoded: BuiltWalletMutationTransaction["decoded"], funded: BuiltWalletMutationTransaction["funded"], plan: DirectAnchorPlan): void;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function validateDirectAnchorDraft(decoded, funded, plan) {
|
|
2
|
+
const outputs = decoded.tx.vout;
|
|
3
|
+
if (outputs[0]?.scriptPubKey?.hex !== plan.expectedOpReturnScriptHex) {
|
|
4
|
+
throw new Error("wallet_anchor_opreturn_mismatch");
|
|
5
|
+
}
|
|
6
|
+
if (funded.changepos === -1) {
|
|
7
|
+
if (outputs.length !== 1) {
|
|
8
|
+
throw new Error("wallet_anchor_unexpected_output_count");
|
|
9
|
+
}
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (funded.changepos !== plan.changePosition || outputs.length !== 2) {
|
|
13
|
+
throw new Error("wallet_anchor_change_position_mismatch");
|
|
14
|
+
}
|
|
15
|
+
if (outputs[funded.changepos]?.scriptPubKey?.hex !== plan.allowedFundingScriptPubKeyHex) {
|
|
16
|
+
throw new Error("wallet_anchor_change_output_mismatch");
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { PendingMutationRecord } from "../../types.js";
|
|
2
|
+
import type { WalletMutationFeeSummary } from "../common.js";
|
|
3
|
+
import type { AnchorMutationOperation } from "./intent.js";
|
|
4
|
+
export interface AnchorDomainResult {
|
|
5
|
+
domainName: string;
|
|
6
|
+
txid: string;
|
|
7
|
+
status: "live" | "confirmed";
|
|
8
|
+
reusedExisting: boolean;
|
|
9
|
+
foundingMessageText?: string | null;
|
|
10
|
+
fees: WalletMutationFeeSummary;
|
|
11
|
+
}
|
|
12
|
+
export declare function createAnchorReuseResult(options: {
|
|
13
|
+
operation: AnchorMutationOperation;
|
|
14
|
+
mutation: PendingMutationRecord;
|
|
15
|
+
resolution: "confirmed" | "live";
|
|
16
|
+
fees: WalletMutationFeeSummary;
|
|
17
|
+
}): AnchorDomainResult;
|
|
18
|
+
export declare function createAnchorResult(options: {
|
|
19
|
+
operation: AnchorMutationOperation;
|
|
20
|
+
mutation: PendingMutationRecord;
|
|
21
|
+
builtTxid: string | null;
|
|
22
|
+
status: "live" | "confirmed";
|
|
23
|
+
reusedExisting: boolean;
|
|
24
|
+
fees: WalletMutationFeeSummary;
|
|
25
|
+
}): AnchorDomainResult;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export function createAnchorReuseResult(options) {
|
|
2
|
+
return {
|
|
3
|
+
domainName: options.operation.normalizedDomainName,
|
|
4
|
+
txid: options.mutation.attemptedTxid ?? "unknown",
|
|
5
|
+
status: options.resolution,
|
|
6
|
+
reusedExisting: true,
|
|
7
|
+
foundingMessageText: options.operation.message.text,
|
|
8
|
+
fees: options.fees,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function createAnchorResult(options) {
|
|
12
|
+
return {
|
|
13
|
+
domainName: options.operation.normalizedDomainName,
|
|
14
|
+
txid: options.mutation.attemptedTxid ?? options.builtTxid ?? "unknown",
|
|
15
|
+
status: options.status,
|
|
16
|
+
reusedExisting: options.reusedExisting,
|
|
17
|
+
foundingMessageText: options.operation.message.text,
|
|
18
|
+
fees: options.fees,
|
|
19
|
+
};
|
|
20
|
+
}
|