@orangecheck/sdk 1.10.0 → 1.12.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 +36 -0
- package/dist/attestation.d.mts +1 -1
- package/dist/attestation.d.ts +1 -1
- package/dist/canonical.d.mts +1 -1
- package/dist/canonical.d.ts +1 -1
- package/dist/challenge.d.mts +1 -1
- package/dist/challenge.d.ts +1 -1
- package/dist/challenge.js +10 -35
- package/dist/challenge.js.map +1 -1
- package/dist/challenge.mjs +10 -15
- package/dist/challenge.mjs.map +1 -1
- package/dist/identity.d.mts +1 -1
- package/dist/identity.d.ts +1 -1
- package/dist/index.d.mts +105 -3
- package/dist/index.d.ts +105 -3
- package/dist/index.js +269 -112
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +256 -92
- package/dist/index.mjs.map +1 -1
- package/dist/nostr.d.mts +1 -1
- package/dist/nostr.d.ts +1 -1
- package/dist/{types-D9Tb1roj.d.mts → types-BBRMCgMo.d.mts} +1 -0
- package/dist/{types-D9Tb1roj.d.ts → types-BBRMCgMo.d.ts} +1 -0
- package/dist/verify.d.mts +1 -1
- package/dist/verify.d.ts +1 -1
- package/dist/verify.js +85 -96
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +85 -76
- package/dist/verify.mjs.map +1 -1
- package/package.json +2 -4
package/dist/index.mjs
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { bech32 } from '@scure/base';
|
|
2
2
|
import { Buffer } from 'buffer';
|
|
3
|
-
import {
|
|
4
|
-
import * as bitcoinMessage from 'bitcoinjs-message';
|
|
5
|
-
import { schnorr } from '@noble/curves/secp256k1.js';
|
|
3
|
+
import { verifySignature as verifySignature$1, decodeAddress } from '@orangecheck/bip322';
|
|
6
4
|
import { sha256 } from '@noble/hashes/sha2.js';
|
|
5
|
+
import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils.js';
|
|
6
|
+
import { schnorr } from '@noble/curves/secp256k1.js';
|
|
7
7
|
|
|
8
8
|
// src/canonical.ts
|
|
9
9
|
function toLowerHex(bytes) {
|
|
@@ -861,87 +861,79 @@ async function verifySignature({
|
|
|
861
861
|
sig,
|
|
862
862
|
scheme
|
|
863
863
|
}) {
|
|
864
|
-
|
|
865
|
-
|
|
864
|
+
const detectedScheme = scheme || detectSignatureScheme(addr, sig);
|
|
865
|
+
log4.debug(
|
|
866
|
+
{ addr, msgLength: msg.length, sigLength: sig.length, scheme: detectedScheme },
|
|
867
|
+
"Verifying signature"
|
|
868
|
+
);
|
|
869
|
+
if (detectedScheme === "bip322") {
|
|
870
|
+
const sigBase64 = toBase64Maybe(sig);
|
|
866
871
|
log4.debug(
|
|
867
|
-
{
|
|
868
|
-
"
|
|
872
|
+
{ sigBase64: sigBase64.substring(0, 50) + "..." },
|
|
873
|
+
"Signature converted to base64"
|
|
869
874
|
);
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
if (isSegWit && sigBase64.length === 88) {
|
|
890
|
-
log4.warn(
|
|
891
|
-
{ addr, wallet: "possibly Electrum", sigLength: sigBase64.length },
|
|
892
|
-
"BIP-322 verification failed - likely Electrum wallet (does not support BIP-322 for SegWit)"
|
|
893
|
-
);
|
|
894
|
-
} else {
|
|
895
|
-
log4.error(
|
|
896
|
-
{
|
|
897
|
-
addr,
|
|
898
|
-
sigLength: sigBase64.length,
|
|
899
|
-
msgLength: msg.length,
|
|
900
|
-
msgPreview: msg.substring(0, 200)
|
|
901
|
-
},
|
|
902
|
-
"BIP-322 verification failed - signature does not match message"
|
|
903
|
-
);
|
|
904
|
-
}
|
|
905
|
-
}
|
|
906
|
-
return ok ? "sig_ok_bip322" : "sig_invalid";
|
|
907
|
-
}
|
|
908
|
-
if (detectedScheme === "legacy") {
|
|
909
|
-
const sigBase64 = toBase64Maybe(sig);
|
|
910
|
-
if (!addr.startsWith("1") && !addr.startsWith("m") && !addr.startsWith("n")) {
|
|
911
|
-
log4.warn({ addr }, "Legacy signature attempted on non-P2PKH address");
|
|
912
|
-
return "sig_unsupported_script";
|
|
913
|
-
}
|
|
914
|
-
log4.debug(
|
|
915
|
-
{
|
|
916
|
-
addr,
|
|
917
|
-
sigBase64: sigBase64.substring(0, 50) + "...",
|
|
918
|
-
msgPreview: msg.substring(0, 100) + "...",
|
|
919
|
-
msgLength: msg.length
|
|
920
|
-
},
|
|
921
|
-
"Verifying legacy signature"
|
|
922
|
-
);
|
|
923
|
-
const ok = bitcoinMessage.verify(msg, addr, sigBase64);
|
|
924
|
-
log4.info({ addr, ok, sigBase64Length: sigBase64.length }, "Legacy verification result");
|
|
925
|
-
if (!ok) {
|
|
875
|
+
log4.debug(
|
|
876
|
+
{
|
|
877
|
+
msgFull: msg,
|
|
878
|
+
msgLines: msg.split("\n").length,
|
|
879
|
+
msgFirstLine: msg.split("\n")[0],
|
|
880
|
+
msgLastLine: msg.split("\n")[msg.split("\n").length - 1]
|
|
881
|
+
},
|
|
882
|
+
"Message being verified"
|
|
883
|
+
);
|
|
884
|
+
const ok = verifySignature$1(addr, msg, sigBase64);
|
|
885
|
+
log4.info({ addr, ok }, "BIP-322 verification result");
|
|
886
|
+
if (!ok) {
|
|
887
|
+
const isSegWit = addr.startsWith("bc1") || addr.startsWith("tb1");
|
|
888
|
+
if (isSegWit && sigBase64.length === 88) {
|
|
889
|
+
log4.warn(
|
|
890
|
+
{ addr, wallet: "possibly Electrum", sigLength: sigBase64.length },
|
|
891
|
+
"BIP-322 verification failed - likely Electrum wallet (does not support BIP-322 for SegWit)"
|
|
892
|
+
);
|
|
893
|
+
} else {
|
|
926
894
|
log4.error(
|
|
927
895
|
{
|
|
928
896
|
addr,
|
|
929
|
-
|
|
930
|
-
|
|
897
|
+
sigLength: sigBase64.length,
|
|
898
|
+
msgLength: msg.length,
|
|
899
|
+
msgPreview: msg.substring(0, 200)
|
|
931
900
|
},
|
|
932
|
-
"
|
|
901
|
+
"BIP-322 verification failed - signature does not match message"
|
|
933
902
|
);
|
|
934
903
|
}
|
|
935
|
-
return ok ? "sig_ok_legacy" : "sig_invalid";
|
|
936
904
|
}
|
|
937
|
-
return "
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
905
|
+
return ok ? "sig_ok_bip322" : "sig_invalid";
|
|
906
|
+
}
|
|
907
|
+
if (detectedScheme === "legacy") {
|
|
908
|
+
const sigBase64 = toBase64Maybe(sig);
|
|
909
|
+
if (!addr.startsWith("1") && !addr.startsWith("m") && !addr.startsWith("n")) {
|
|
910
|
+
log4.warn({ addr }, "Legacy signature attempted on non-P2PKH address");
|
|
911
|
+
return "sig_unsupported_script";
|
|
912
|
+
}
|
|
913
|
+
log4.debug(
|
|
914
|
+
{
|
|
915
|
+
addr,
|
|
916
|
+
sigBase64: sigBase64.substring(0, 50) + "...",
|
|
917
|
+
msgPreview: msg.substring(0, 100) + "...",
|
|
918
|
+
msgLength: msg.length
|
|
919
|
+
},
|
|
920
|
+
"Verifying legacy signature"
|
|
942
921
|
);
|
|
943
|
-
|
|
922
|
+
const ok = verifySignature$1(addr, msg, sigBase64);
|
|
923
|
+
log4.info({ addr, ok, sigBase64Length: sigBase64.length }, "Legacy verification result");
|
|
924
|
+
if (!ok) {
|
|
925
|
+
log4.error(
|
|
926
|
+
{
|
|
927
|
+
addr,
|
|
928
|
+
msgFirstLine: msg.split("\n")[0],
|
|
929
|
+
sigFormat: sigBase64.substring(0, 10)
|
|
930
|
+
},
|
|
931
|
+
"Legacy signature verification failed - check message format and signature"
|
|
932
|
+
);
|
|
933
|
+
}
|
|
934
|
+
return ok ? "sig_ok_legacy" : "sig_invalid";
|
|
944
935
|
}
|
|
936
|
+
return "invalid_scheme";
|
|
945
937
|
}
|
|
946
938
|
function parseCanonicalMessage(msg) {
|
|
947
939
|
const lines = msg.split("\n").map((l) => l.trim());
|
|
@@ -1033,7 +1025,7 @@ async function getAddressUtxos(address, network, opts) {
|
|
|
1033
1025
|
log4.error({ endpoints, error: lastError?.message, network }, "All Esplora endpoints failed");
|
|
1034
1026
|
throw new Error(`esplora_all_failed: ${lastError?.message || "unknown"}`);
|
|
1035
1027
|
}
|
|
1036
|
-
function computeMetrics(utxos, bondAmount) {
|
|
1028
|
+
function computeMetrics(utxos, bondAmount, coverSats) {
|
|
1037
1029
|
const confirmedUtxos = utxos.filter((u) => u.status.confirmed);
|
|
1038
1030
|
if (confirmedUtxos.length === 0) {
|
|
1039
1031
|
return { sats_bonded: 0, days_unspent: 0, confirmed_balance: 0 };
|
|
@@ -1066,13 +1058,30 @@ function computeMetrics(utxos, bondAmount) {
|
|
|
1066
1058
|
};
|
|
1067
1059
|
}
|
|
1068
1060
|
const sats_bonded = confirmed_balance;
|
|
1061
|
+
if (coverSats !== void 0 && coverSats > 0) {
|
|
1062
|
+
const sorted = [...confirmedUtxos].sort(
|
|
1063
|
+
(a, b) => (a.status.block_height ?? Infinity) - (b.status.block_height ?? Infinity)
|
|
1064
|
+
);
|
|
1065
|
+
let sum = 0;
|
|
1066
|
+
let youngestTime = 0;
|
|
1067
|
+
for (const utxo of sorted) {
|
|
1068
|
+
sum += utxo.value;
|
|
1069
|
+
youngestTime = Math.max(youngestTime, utxo.status.block_time || Date.now() / 1e3);
|
|
1070
|
+
if (sum >= coverSats) break;
|
|
1071
|
+
}
|
|
1072
|
+
const days_unspent2 = Math.max(
|
|
1073
|
+
0,
|
|
1074
|
+
Math.floor((Date.now() / 1e3 - youngestTime) / (24 * 60 * 60))
|
|
1075
|
+
);
|
|
1076
|
+
return { sats_bonded, days_unspent: days_unspent2, confirmed_balance };
|
|
1077
|
+
}
|
|
1069
1078
|
const oldestTime = Math.min(
|
|
1070
1079
|
...confirmedUtxos.map((u) => u.status.block_time || Date.now() / 1e3)
|
|
1071
1080
|
);
|
|
1072
1081
|
const days_unspent = Math.max(0, Math.floor((Date.now() / 1e3 - oldestTime) / (24 * 60 * 60)));
|
|
1073
1082
|
return { sats_bonded, days_unspent, confirmed_balance };
|
|
1074
1083
|
}
|
|
1075
|
-
async function
|
|
1084
|
+
async function verify(input, opts = {}) {
|
|
1076
1085
|
const codes = [];
|
|
1077
1086
|
let network = "mainnet";
|
|
1078
1087
|
try {
|
|
@@ -1129,7 +1138,8 @@ async function verify2(input, opts = {}) {
|
|
|
1129
1138
|
});
|
|
1130
1139
|
const { sats_bonded, days_unspent, confirmed_balance } = computeMetrics(
|
|
1131
1140
|
utxos,
|
|
1132
|
-
parsed.bond
|
|
1141
|
+
parsed.bond,
|
|
1142
|
+
opts.coverSats
|
|
1133
1143
|
);
|
|
1134
1144
|
if (parsed.bond !== void 0 && parsed.bond > 0) {
|
|
1135
1145
|
if (confirmed_balance < parsed.bond) {
|
|
@@ -1227,14 +1237,14 @@ async function check(params) {
|
|
|
1227
1237
|
return { ok: false, sats: 0, days: 0, score: 0, reasons: ["not_found"] };
|
|
1228
1238
|
}
|
|
1229
1239
|
const shared = identity ? await backsOtherIdentity(env, identity, relays) : false;
|
|
1230
|
-
const outcome = await
|
|
1240
|
+
const outcome = await verify(
|
|
1231
1241
|
{
|
|
1232
1242
|
addr: env.address,
|
|
1233
1243
|
msg: env.message,
|
|
1234
1244
|
sig: env.signature,
|
|
1235
1245
|
scheme: env.scheme
|
|
1236
1246
|
},
|
|
1237
|
-
verifyOptions
|
|
1247
|
+
{ ...verifyOptions, coverSats: minSats > 0 ? minSats : void 0 }
|
|
1238
1248
|
);
|
|
1239
1249
|
const sats = outcome.metrics?.sats_bonded ?? 0;
|
|
1240
1250
|
const days = outcome.metrics?.days_unspent ?? 0;
|
|
@@ -1415,21 +1425,17 @@ async function verifyChallenge(opts) {
|
|
|
1415
1425
|
return { ok: false, reason: "purpose_mismatch" };
|
|
1416
1426
|
}
|
|
1417
1427
|
const scheme = opts.scheme ?? "bip322";
|
|
1418
|
-
let sigValid
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
if (!/^[1m][a-km-zA-HJ-NP-Z1-9]{25,34}$/.test(address)) {
|
|
1425
|
-
return { ok: false, reason: "sig_unsupported_scheme" };
|
|
1426
|
-
}
|
|
1427
|
-
sigValid = bitcoinMessage.verify(opts.message, address, opts.signature);
|
|
1428
|
-
} else {
|
|
1428
|
+
let sigValid;
|
|
1429
|
+
if (scheme === "bip322") {
|
|
1430
|
+
const sigBase64 = /^[0-9a-fA-F]+$/.test(opts.signature) && opts.signature.length % 2 === 0 ? Buffer.from(opts.signature, "hex").toString("base64") : opts.signature;
|
|
1431
|
+
sigValid = verifySignature$1(address, opts.message, sigBase64);
|
|
1432
|
+
} else if (scheme === "legacy") {
|
|
1433
|
+
if (!/^[1m][a-km-zA-HJ-NP-Z1-9]{25,34}$/.test(address)) {
|
|
1429
1434
|
return { ok: false, reason: "sig_unsupported_scheme" };
|
|
1430
1435
|
}
|
|
1431
|
-
|
|
1432
|
-
|
|
1436
|
+
sigValid = verifySignature$1(address, opts.message, opts.signature);
|
|
1437
|
+
} else {
|
|
1438
|
+
return { ok: false, reason: "sig_unsupported_scheme" };
|
|
1433
1439
|
}
|
|
1434
1440
|
if (!sigValid) return { ok: false, reason: "sig_invalid" };
|
|
1435
1441
|
return {
|
|
@@ -1442,6 +1448,164 @@ async function verifyChallenge(opts) {
|
|
|
1442
1448
|
...purpose !== void 0 ? { purpose } : {}
|
|
1443
1449
|
};
|
|
1444
1450
|
}
|
|
1451
|
+
var HEADER = "orangecheck-bond";
|
|
1452
|
+
var ACK = "I pledge this coin to back this subject. It stays in my wallet.";
|
|
1453
|
+
var FIELDS = ["address", "outpoint", "subject", "nonce", "issued_at", "expires_at"];
|
|
1454
|
+
var BOND_SATS_FLOORS = [1e3, 1e4, 1e5, 1e6, 1e7];
|
|
1455
|
+
var BOND_DAYS_FLOORS = [7, 30, 90, 365];
|
|
1456
|
+
var BOND_MIN_CONFIRMATIONS = 6;
|
|
1457
|
+
var OUTPOINT_RE = /^[0-9a-f]{64}:(0|[1-9][0-9]{0,9})$/;
|
|
1458
|
+
var NONCE_RE = /^[0-9a-f]{32}$/;
|
|
1459
|
+
var TIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/;
|
|
1460
|
+
var SUBJECT_RE = /^[\x21-\x7e]{1,256}$/;
|
|
1461
|
+
function iso2(d) {
|
|
1462
|
+
return d.toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
1463
|
+
}
|
|
1464
|
+
function randomNonce2() {
|
|
1465
|
+
const b = new Uint8Array(16);
|
|
1466
|
+
globalThis.crypto.getRandomValues(b);
|
|
1467
|
+
return bytesToHex(b);
|
|
1468
|
+
}
|
|
1469
|
+
function wellFormed(f) {
|
|
1470
|
+
return decodeAddress(f.address) !== null && OUTPOINT_RE.test(f.outpoint) && SUBJECT_RE.test(f.subject) && NONCE_RE.test(f.nonce) && TIME_RE.test(f.issued_at) && TIME_RE.test(f.expires_at) && Date.parse(f.expires_at) > Date.parse(f.issued_at);
|
|
1471
|
+
}
|
|
1472
|
+
function buildBondMessage(fields) {
|
|
1473
|
+
if (!wellFormed(fields)) throw new Error("bond fields are malformed");
|
|
1474
|
+
return [HEADER, ...FIELDS.map((k) => `${k}: ${fields[k]}`), `ack: ${ACK}`].join("\n") + "\n";
|
|
1475
|
+
}
|
|
1476
|
+
function issueBondMessage(opts) {
|
|
1477
|
+
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
1478
|
+
const expires = new Date(now.getTime() + (opts.ttlSeconds ?? 600) * 1e3);
|
|
1479
|
+
const nonce = randomNonce2();
|
|
1480
|
+
const message = buildBondMessage({
|
|
1481
|
+
address: opts.address,
|
|
1482
|
+
outpoint: opts.outpoint,
|
|
1483
|
+
subject: opts.subject,
|
|
1484
|
+
nonce,
|
|
1485
|
+
issued_at: iso2(now),
|
|
1486
|
+
expires_at: iso2(expires)
|
|
1487
|
+
});
|
|
1488
|
+
return { message, nonce, expiresAt: expires.getTime() };
|
|
1489
|
+
}
|
|
1490
|
+
function parseBondMessage(message) {
|
|
1491
|
+
if (!message.endsWith("\n") || message.includes("\r")) return null;
|
|
1492
|
+
const lines = message.slice(0, -1).split("\n");
|
|
1493
|
+
if (lines.length !== FIELDS.length + 2 || lines[0] !== HEADER) return null;
|
|
1494
|
+
if (lines[lines.length - 1] !== `ack: ${ACK}`) return null;
|
|
1495
|
+
const out = {};
|
|
1496
|
+
for (const [i, key] of FIELDS.entries()) {
|
|
1497
|
+
const line = lines[i + 1];
|
|
1498
|
+
const prefix = `${key}: `;
|
|
1499
|
+
if (!line.startsWith(prefix)) return null;
|
|
1500
|
+
out[key] = line.slice(prefix.length);
|
|
1501
|
+
}
|
|
1502
|
+
const fields = out;
|
|
1503
|
+
return wellFormed(fields) && buildBondMessage(fields) === message ? fields : null;
|
|
1504
|
+
}
|
|
1505
|
+
function bondId(message) {
|
|
1506
|
+
return bytesToHex(sha256(utf8ToBytes(message)));
|
|
1507
|
+
}
|
|
1508
|
+
function bondNullifier(audience, outpoint) {
|
|
1509
|
+
return bytesToHex(sha256(utf8ToBytes(`orangecheck-bond-nullifier
|
|
1510
|
+
${audience}
|
|
1511
|
+
${outpoint}`)));
|
|
1512
|
+
}
|
|
1513
|
+
function bondFloors(sats, ageBlocks) {
|
|
1514
|
+
const days = Math.floor(ageBlocks / 144);
|
|
1515
|
+
const floor = (ladder, v) => ladder.filter((f) => v >= f).reduce((a, b) => Math.max(a, b), 0);
|
|
1516
|
+
return { sats_floor: floor(BOND_SATS_FLOORS, sats), days_floor: floor(BOND_DAYS_FLOORS, days) };
|
|
1517
|
+
}
|
|
1518
|
+
function verifyBondSignature(opts) {
|
|
1519
|
+
const fields = parseBondMessage(opts.message);
|
|
1520
|
+
if (!fields) return { ok: false, reason: "bond_malformed" };
|
|
1521
|
+
if (opts.subject !== void 0 && fields.subject !== opts.subject) {
|
|
1522
|
+
return { ok: false, reason: "bond_subject_mismatch" };
|
|
1523
|
+
}
|
|
1524
|
+
const now = (opts.now ?? /* @__PURE__ */ new Date()).getTime();
|
|
1525
|
+
const skew = (opts.skewSeconds ?? 60) * 1e3;
|
|
1526
|
+
if (Date.parse(fields.issued_at) - skew > now) return { ok: false, reason: "bond_not_yet_valid" };
|
|
1527
|
+
if (Date.parse(fields.expires_at) + skew < now) return { ok: false, reason: "bond_expired" };
|
|
1528
|
+
if (!verifySignature$1(fields.address, opts.message, opts.signature)) {
|
|
1529
|
+
return { ok: false, reason: "sig_invalid" };
|
|
1530
|
+
}
|
|
1531
|
+
return { ok: true, fields, bond_id: bondId(opts.message) };
|
|
1532
|
+
}
|
|
1533
|
+
async function checkBondCoin(opts) {
|
|
1534
|
+
const decoded = decodeAddress(opts.address);
|
|
1535
|
+
if (!decoded || !OUTPOINT_RE.test(opts.outpoint)) return { ok: false, reason: "bond_malformed" };
|
|
1536
|
+
let coin;
|
|
1537
|
+
let tip;
|
|
1538
|
+
try {
|
|
1539
|
+
[coin, tip] = await Promise.all([opts.source.coin(opts.outpoint), opts.source.tip()]);
|
|
1540
|
+
} catch {
|
|
1541
|
+
return { ok: false, reason: "chain_unavailable" };
|
|
1542
|
+
}
|
|
1543
|
+
if (!coin) return { ok: false, reason: "coin_not_found" };
|
|
1544
|
+
if (coin.scriptPubKey !== bytesToHex(decoded.scriptPubKey)) {
|
|
1545
|
+
return { ok: false, reason: "coin_address_mismatch" };
|
|
1546
|
+
}
|
|
1547
|
+
if (coin.confirmedHeight === null) return { ok: false, reason: "coin_unconfirmed" };
|
|
1548
|
+
if (coin.spent) return { ok: false, reason: "coin_spent" };
|
|
1549
|
+
const confirmations = tip.height - coin.confirmedHeight + 1;
|
|
1550
|
+
if (confirmations < (opts.minConfirmations ?? BOND_MIN_CONFIRMATIONS)) {
|
|
1551
|
+
return { ok: false, reason: "coin_too_recent" };
|
|
1552
|
+
}
|
|
1553
|
+
return { ok: true, sats: coin.sats, confirmed_height: coin.confirmedHeight, tip };
|
|
1554
|
+
}
|
|
1555
|
+
async function verifyBond(opts) {
|
|
1556
|
+
const sig = verifyBondSignature(opts);
|
|
1557
|
+
if (!sig.ok) return sig;
|
|
1558
|
+
const coin = await checkBondCoin({
|
|
1559
|
+
address: sig.fields.address,
|
|
1560
|
+
outpoint: sig.fields.outpoint,
|
|
1561
|
+
source: opts.source,
|
|
1562
|
+
minConfirmations: opts.minConfirmations
|
|
1563
|
+
});
|
|
1564
|
+
if (!coin.ok) return coin;
|
|
1565
|
+
return {
|
|
1566
|
+
ok: true,
|
|
1567
|
+
bond: {
|
|
1568
|
+
bond_id: sig.bond_id,
|
|
1569
|
+
subject: sig.fields.subject,
|
|
1570
|
+
address: sig.fields.address,
|
|
1571
|
+
outpoint: sig.fields.outpoint,
|
|
1572
|
+
sats: coin.sats,
|
|
1573
|
+
confirmed_height: coin.confirmed_height,
|
|
1574
|
+
tip: coin.tip
|
|
1575
|
+
}
|
|
1576
|
+
};
|
|
1577
|
+
}
|
|
1578
|
+
function esploraSource(base = "https://mempool.space/api", opts = {}) {
|
|
1579
|
+
const get = async (path) => {
|
|
1580
|
+
const r = await (opts.fetch ?? fetch)(`${base}${path}`, {
|
|
1581
|
+
signal: AbortSignal.timeout(opts.timeoutMs ?? 5e3)
|
|
1582
|
+
});
|
|
1583
|
+
if (!r.ok && r.status !== 404) throw new Error(`esplora ${path}: HTTP ${r.status}`);
|
|
1584
|
+
return r;
|
|
1585
|
+
};
|
|
1586
|
+
return {
|
|
1587
|
+
async tip() {
|
|
1588
|
+
const [h, hash] = await Promise.all([get("/blocks/tip/height"), get("/blocks/tip/hash")]);
|
|
1589
|
+
return { height: Number(await h.text()), hash: (await hash.text()).trim() };
|
|
1590
|
+
},
|
|
1591
|
+
async coin(outpoint) {
|
|
1592
|
+
const [txid, voutStr] = outpoint.split(":");
|
|
1593
|
+
const vout = Number(voutStr);
|
|
1594
|
+
const txr = await get(`/tx/${txid}`);
|
|
1595
|
+
if (txr.status === 404) return null;
|
|
1596
|
+
const tx = await txr.json();
|
|
1597
|
+
const out = tx.vout[vout];
|
|
1598
|
+
if (!out) return null;
|
|
1599
|
+
const spend = await (await get(`/tx/${txid}/outspend/${vout}`)).json();
|
|
1600
|
+
return {
|
|
1601
|
+
sats: out.value,
|
|
1602
|
+
scriptPubKey: out.scriptpubkey.toLowerCase(),
|
|
1603
|
+
confirmedHeight: tx.status.confirmed ? tx.status.block_height ?? null : null,
|
|
1604
|
+
spent: spend.spent
|
|
1605
|
+
};
|
|
1606
|
+
}
|
|
1607
|
+
};
|
|
1608
|
+
}
|
|
1445
1609
|
|
|
1446
1610
|
// src/utils/rate-limiter.ts
|
|
1447
1611
|
var RateLimitError = class extends Error {
|
|
@@ -2231,6 +2395,6 @@ var STATUS_META = {
|
|
|
2231
2395
|
}
|
|
2232
2396
|
};
|
|
2233
2397
|
|
|
2234
|
-
export { DEFAULT_RELAYS, FANOUT_DEADLINE_MS, STATUS_META, buildCanonicalMessage, check, computeAllScores, computeScore, createAttestation, createAttestationEnvelope, createAttestationEvent, discoverAttestations, extractAttestationIdFromUrl, formatIdentities, formatIdentitiesForDisplay, generateAttestationId, generateGistTemplate, generateTweetText, generateWellKnownContent, getAttestationsForAddress, getAttestationsForIdentity, getDnsVerificationInstructions, getGistCreationUrl, getGitHubVerificationInstructions, getManualVerificationInstructions, getNip07Info, getNostrPublicKey, getNostrVerificationInstructions, getTweetUrl, getTwitterComposeUrl, getTwitterVerificationInstructions, getVerificationInstructions, getVerificationSummary, getVerificationUrl, getWellKnownUrl, isDnsIdentityVerified, isGitHubIdentityVerified, isNip07Available, isNostrIdentityVerified, isValidDomain, isValidTweetUrl, issueChallenge, nostrIdentifierForms, nostrPubkeyToHex, parseAttestationFromEvent, parseIdentities, publishAttestation, publishToRelays, queryByAddress, queryByAttestationId, queryByIdentity, queryRecent, requiresManualVerification, signNostrEvent, supportsAutomaticVerification,
|
|
2398
|
+
export { BOND_DAYS_FLOORS, BOND_MIN_CONFIRMATIONS, BOND_SATS_FLOORS, DEFAULT_RELAYS, FANOUT_DEADLINE_MS, STATUS_META, bondFloors, bondId, bondNullifier, buildBondMessage, buildCanonicalMessage, check, checkBondCoin, computeAllScores, computeScore, createAttestation, createAttestationEnvelope, createAttestationEvent, discoverAttestations, esploraSource, extractAttestationIdFromUrl, formatIdentities, formatIdentitiesForDisplay, generateAttestationId, generateGistTemplate, generateTweetText, generateWellKnownContent, getAttestationsForAddress, getAttestationsForIdentity, getDnsVerificationInstructions, getGistCreationUrl, getGitHubVerificationInstructions, getManualVerificationInstructions, getNip07Info, getNostrPublicKey, getNostrVerificationInstructions, getTweetUrl, getTwitterComposeUrl, getTwitterVerificationInstructions, getVerificationInstructions, getVerificationSummary, getVerificationUrl, getWellKnownUrl, isDnsIdentityVerified, isGitHubIdentityVerified, isNip07Available, isNostrIdentityVerified, isValidDomain, isValidTweetUrl, issueBondMessage, issueChallenge, nostrIdentifierForms, nostrPubkeyToHex, parseAttestationFromEvent, parseBondMessage, parseIdentities, publishAttestation, publishToRelays, queryByAddress, queryByAttestationId, queryByIdentity, queryRecent, requiresManualVerification, signNostrEvent, supportsAutomaticVerification, verify, verifyAllIdentities, verifyAttestationById, verifyBond, verifyBondSignature, verifyChallenge, verifyDnsIdentity, verifyGitHubIdentity, verifyIdentity, verifyNostrIdentity, verifyTwitterIdentity };
|
|
2235
2399
|
//# sourceMappingURL=index.mjs.map
|
|
2236
2400
|
//# sourceMappingURL=index.mjs.map
|