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