aboutsummaryrefslogtreecommitdiffstats
path: root/src/ark/proto.js
diff options
context:
space:
mode:
authorAdam Soltys <[email protected]>2026-07-04 19:29:08 -0700
committerAdam Soltys <[email protected]>2026-07-04 19:29:08 -0700
commit1d9cac6ca92187718eafbd567562875d25fa772b (patch)
treec5ffaa9205d0236fda69ca798d73c33dd9c49a54 /src/ark/proto.js
parentcc2c05d2a1b7a9601eb3cb0df998483099e590cc (diff)
downloadcoinosv3-1d9cac6ca92187718eafbd567562875d25fa772b.tar.gz
coinosv3-1d9cac6ca92187718eafbd567562875d25fa772b.zip
Ark: recover from stale vtxo state
Match captaind's actual 'not spendable' error so a crashed or foreign spend fails the action instead of retrying forever, and reconcile locally-spendable vtxos against the server (GetVtxoStatus + ownership attestation) when an ark address lands in the send form. boardFee now treats absent proto3 fee fields as zero (Second's signet boards free). Co-Authored-By: Claude Fable 5 <[email protected]>
Diffstat (limited to 'src/ark/proto.js')
-rw-r--r--src/ark/proto.js21
1 files changed, 20 insertions, 1 deletions
diff --git a/src/ark/proto.js b/src/ark/proto.js
index 728686b..1cbc0ef 100644
--- a/src/ark/proto.js
+++ b/src/ark/proto.js
@@ -363,7 +363,8 @@ export function decodeVtxo(bytes) {
export async function getArkInfo(ark) {
const data = await grpcCall(ark, 'bark_server.ArkService/GetArkInfo', new Uint8Array(0));
- const info = {};
+ // Fee schedules a server never sends (or sends all-zero) mean "free".
+ const info = { boardFees: {}, refreshFees: { baseFeeSat: 0, ppmExpiryTable: [] } };
for (const { field, value } of pbFields(data)) {
if (field === 1) info.network = td.decode(value);
if (field === 2) info.serverPubkey = hex.encode(value);
@@ -446,6 +447,24 @@ export function decodeMailboxMessage(value) {
return msg;
}
+// VtxoSpendState values from GetVtxoStatus (bark_server.proto).
+export const VTXO_STATE_SPENDABLE = 1;
+export const VTXO_STATE_SPENT = 2;
+
+const VTXO_STATUS_PREFIX = te.encode('Ark VTXO status query challenge '); // 32 bytes
+
+// Ask the server for a vtxo's authoritative spend state. The attestation is a
+// BIP340 signature with the vtxo's user key, so only the owner can query.
+export async function getVtxoStatus(ark, vtxoIdRaw /* 36B outpoint */, vtxoPrivkey) {
+ const attestation = schnorr.sign(sha256(concatBytes(VTXO_STATUS_PREFIX, vtxoIdRaw)), vtxoPrivkey);
+ const w = pbWriter();
+ w.bytesField(1, vtxoIdRaw);
+ w.bytesField(2, attestation);
+ const data = await grpcCall(ark, 'bark_server.ArkService/GetVtxoStatus', w.finish());
+ for (const { field, value } of pbFields(data)) if (field === 1) return Number(value);
+ return 0; // proto3 omits a zero enum: UNSPECIFIED
+}
+
// Build an authenticated MailboxRequest (shared by read + subscribe).
export function mailboxRequestBytes(mailbox, checkpoint = 0) {
const expiry = Math.floor(Date.now() / 1000) + 60;