@orangecheck/sdk 1.12.0 → 1.14.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/dist/index.mjs CHANGED
@@ -1,6 +1,7 @@
1
1
  import { bech32 } from '@scure/base';
2
2
  import { Buffer } from 'buffer';
3
3
  import { verifySignature as verifySignature$1, decodeAddress } from '@orangecheck/bip322';
4
+ import { hmac } from '@noble/hashes/hmac.js';
4
5
  import { sha256 } from '@noble/hashes/sha2.js';
5
6
  import { bytesToHex, utf8ToBytes } from '@noble/hashes/utils.js';
6
7
  import { schnorr } from '@noble/curves/secp256k1.js';
@@ -217,7 +218,9 @@ var ATTESTATION_EVENT_KIND = 30078;
217
218
  var DEFAULT_RELAYS = [
218
219
  "wss://relay.damus.io",
219
220
  "wss://nos.lol",
220
- "wss://relay.snort.social",
221
+ // relay.snort.social sat here until 2026-10-21. It now runs in memory: it
222
+ // answered OK true to family events and served none of them back.
223
+ "wss://nostr.oxtr.dev",
221
224
  "wss://relay.ochk.io"
222
225
  ];
223
226
  var FANOUT_DEADLINE_MS = 4e3;
@@ -1449,11 +1452,12 @@ async function verifyChallenge(opts) {
1449
1452
  };
1450
1453
  }
1451
1454
  var HEADER = "orangecheck-bond";
1452
- var ACK = "I pledge this coin to back this subject. It stays in my wallet.";
1455
+ var ACK = "I back this subject with this coin. It stays in my wallet.";
1453
1456
  var FIELDS = ["address", "outpoint", "subject", "nonce", "issued_at", "expires_at"];
1454
1457
  var BOND_SATS_FLOORS = [1e3, 1e4, 1e5, 1e6, 1e7];
1455
1458
  var BOND_DAYS_FLOORS = [7, 30, 90, 365];
1456
1459
  var BOND_MIN_CONFIRMATIONS = 6;
1460
+ var BOND_MAX_WINDOW_SECONDS = 3600;
1457
1461
  var OUTPOINT_RE = /^[0-9a-f]{64}:(0|[1-9][0-9]{0,9})$/;
1458
1462
  var NONCE_RE = /^[0-9a-f]{32}$/;
1459
1463
  var TIME_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/;
@@ -1461,13 +1465,17 @@ var SUBJECT_RE = /^[\x21-\x7e]{1,256}$/;
1461
1465
  function iso2(d) {
1462
1466
  return d.toISOString().replace(/\.\d{3}Z$/, "Z");
1463
1467
  }
1468
+ function instant(s) {
1469
+ const t = Date.parse(s);
1470
+ return TIME_RE.test(s) && Number.isFinite(t) && iso2(new Date(t)) === s;
1471
+ }
1464
1472
  function randomNonce2() {
1465
1473
  const b = new Uint8Array(16);
1466
1474
  globalThis.crypto.getRandomValues(b);
1467
1475
  return bytesToHex(b);
1468
1476
  }
1469
1477
  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);
1478
+ return decodeAddress(f.address) !== null && OUTPOINT_RE.test(f.outpoint) && SUBJECT_RE.test(f.subject) && NONCE_RE.test(f.nonce) && instant(f.issued_at) && instant(f.expires_at) && Date.parse(f.expires_at) > Date.parse(f.issued_at);
1471
1479
  }
1472
1480
  function buildBondMessage(fields) {
1473
1481
  if (!wellFormed(fields)) throw new Error("bond fields are malformed");
@@ -1505,10 +1513,12 @@ function parseBondMessage(message) {
1505
1513
  function bondId(message) {
1506
1514
  return bytesToHex(sha256(utf8ToBytes(message)));
1507
1515
  }
1508
- function bondNullifier(audience, outpoint) {
1509
- return bytesToHex(sha256(utf8ToBytes(`orangecheck-bond-nullifier
1516
+ function bondNullifier(key, audience, outpoint) {
1517
+ if (key.length < 32) throw new Error("bond nullifier key must be at least 32 bytes");
1518
+ const message = utf8ToBytes(`orangecheck-bond-nullifier
1510
1519
  ${audience}
1511
- ${outpoint}`)));
1520
+ ${outpoint}`);
1521
+ return bytesToHex(hmac(sha256, key, message));
1512
1522
  }
1513
1523
  function bondFloors(sats, ageBlocks) {
1514
1524
  const days = Math.floor(ageBlocks / 144);
@@ -1521,6 +1531,10 @@ function verifyBondSignature(opts) {
1521
1531
  if (opts.subject !== void 0 && fields.subject !== opts.subject) {
1522
1532
  return { ok: false, reason: "bond_subject_mismatch" };
1523
1533
  }
1534
+ const window2 = Date.parse(fields.expires_at) - Date.parse(fields.issued_at);
1535
+ if (window2 > (opts.maxWindowSeconds ?? BOND_MAX_WINDOW_SECONDS) * 1e3) {
1536
+ return { ok: false, reason: "bond_window_too_long" };
1537
+ }
1524
1538
  const now = (opts.now ?? /* @__PURE__ */ new Date()).getTime();
1525
1539
  const skew = (opts.skewSeconds ?? 60) * 1e3;
1526
1540
  if (Date.parse(fields.issued_at) - skew > now) return { ok: false, reason: "bond_not_yet_valid" };
@@ -1576,22 +1590,28 @@ async function verifyBond(opts) {
1576
1590
  };
1577
1591
  }
1578
1592
  function esploraSource(base = "https://mempool.space/api", opts = {}) {
1579
- const get = async (path) => {
1593
+ const get = async (path, missingOk = false) => {
1580
1594
  const r = await (opts.fetch ?? fetch)(`${base}${path}`, {
1581
1595
  signal: AbortSignal.timeout(opts.timeoutMs ?? 5e3)
1582
1596
  });
1583
- if (!r.ok && r.status !== 404) throw new Error(`esplora ${path}: HTTP ${r.status}`);
1597
+ if (!r.ok && !(missingOk && r.status === 404)) {
1598
+ throw new Error(`esplora ${path}: HTTP ${r.status}`);
1599
+ }
1584
1600
  return r;
1585
1601
  };
1586
1602
  return {
1587
1603
  async tip() {
1588
1604
  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() };
1605
+ const tip = { height: Number(await h.text()), hash: (await hash.text()).trim() };
1606
+ if (!Number.isSafeInteger(tip.height) || !/^[0-9a-f]{64}$/.test(tip.hash)) {
1607
+ throw new Error("esplora tip: malformed");
1608
+ }
1609
+ return tip;
1590
1610
  },
1591
1611
  async coin(outpoint) {
1592
1612
  const [txid, voutStr] = outpoint.split(":");
1593
1613
  const vout = Number(voutStr);
1594
- const txr = await get(`/tx/${txid}`);
1614
+ const txr = await get(`/tx/${txid}`, true);
1595
1615
  if (txr.status === 404) return null;
1596
1616
  const tx = await txr.json();
1597
1617
  const out = tx.vout[vout];
@@ -2395,6 +2415,6 @@ var STATUS_META = {
2395
2415
  }
2396
2416
  };
2397
2417
 
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 };
2418
+ export { BOND_DAYS_FLOORS, BOND_MAX_WINDOW_SECONDS, 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 };
2399
2419
  //# sourceMappingURL=index.mjs.map
2400
2420
  //# sourceMappingURL=index.mjs.map