@orangecheck/sdk 1.16.1 → 1.16.2

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
@@ -306,11 +306,15 @@ function parseCanonicalMessage(msg) {
306
306
  "ack"
307
307
  ]);
308
308
  const extensions = {};
309
+ const keys = /* @__PURE__ */ new Set();
310
+ const repeated = [];
309
311
  for (const line of lines) {
310
312
  const idx = line.indexOf(":");
311
313
  if (idx === -1) continue;
312
314
  const key = line.slice(0, idx).trim().toLowerCase();
313
315
  const val = line.slice(idx + 1).trim();
316
+ if (keys.has(key)) repeated.push(key);
317
+ keys.add(key);
314
318
  if (!coreKeys.has(key) && val) extensions[key] = val;
315
319
  }
316
320
  const network = extensions["network"] || "mainnet";
@@ -319,6 +323,7 @@ function parseCanonicalMessage(msg) {
319
323
  return {
320
324
  core: { address, identities, issued_at },
321
325
  expires,
326
+ repeated,
322
327
  network,
323
328
  extensions,
324
329
  bond: bond !== void 0 && !isNaN(bond) && bond > 0 ? bond : void 0
@@ -437,6 +442,10 @@ async function verify(input, opts = {}) {
437
442
  const parsed = parseCanonicalMessage(input.msg);
438
443
  network = parsed.network || "mainnet";
439
444
  const attestation_id = await generateAttestationId(input.msg);
445
+ if (parsed.repeated.length > 0) {
446
+ codes.push("decode_error");
447
+ return { ok: false, codes, network, attestation_id };
448
+ }
440
449
  if (parsed.core.address !== input.addr) {
441
450
  codes.push("bad_request");
442
451
  return { ok: false, codes, network, attestation_id };
@@ -694,188 +703,98 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
694
703
  log2.info({ successCount: success.length, failedCount: failed.length }, "Publishing complete");
695
704
  return { success, failed };
696
705
  }
697
- async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
706
+ var PAGE_LIMIT = 500;
707
+ var MAX_PAGES = 4;
708
+ async function querySubject(filter, relays, label) {
698
709
  const events = [];
699
- log2.info({ attestationId, relayCount: relays.length }, "Querying relays for attestation");
700
710
  const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
701
- const queryPromises = relays.map(async (relayUrl) => {
702
- let ws = null;
703
- try {
704
- ws = new WebSocket(relayUrl);
705
- const wsRef = ws;
706
- await new Promise((resolve, reject) => {
707
- const timeout = setTimeout(() => {
708
- wsRef.close();
709
- reject(new Error("Query timeout"));
710
- }, budgetFor(deadlineAt));
711
- const cleanup = () => {
712
- clearTimeout(timeout);
713
- wsRef.close();
714
- };
715
- wsRef.onopen = () => {
716
- const subscriptionId = `ochk_${Date.now()}`;
717
- wsRef.send(
718
- JSON.stringify([
719
- "REQ",
720
- subscriptionId,
721
- {
722
- kinds: [30078],
723
- "#d": [attestationId],
724
- limit: 1
711
+ await Promise.allSettled(
712
+ relays.map(async (relayUrl) => {
713
+ let ws = null;
714
+ try {
715
+ ws = new WebSocket(relayUrl);
716
+ const wsRef = ws;
717
+ await new Promise((resolve, reject) => {
718
+ const timeout = setTimeout(() => {
719
+ wsRef.close();
720
+ reject(new Error("Query timeout"));
721
+ }, budgetFor(deadlineAt));
722
+ const seen = /* @__PURE__ */ new Set();
723
+ let page = 0;
724
+ let inPage = 0;
725
+ let newInPage = 0;
726
+ let oldest = Infinity;
727
+ const ask = () => {
728
+ page++;
729
+ inPage = 0;
730
+ newInPage = 0;
731
+ const until = Number.isFinite(oldest) ? { until: oldest } : {};
732
+ wsRef.send(
733
+ JSON.stringify([
734
+ "REQ",
735
+ `ochk_${label}_${Date.now()}_${page}`,
736
+ { ...filter, ...until, limit: PAGE_LIMIT }
737
+ ])
738
+ );
739
+ };
740
+ wsRef.onopen = ask;
741
+ wsRef.onmessage = (msg) => {
742
+ try {
743
+ const data = JSON.parse(msg.data);
744
+ if (data[0] === "EVENT") {
745
+ const event = data[2];
746
+ const key = event.id ?? JSON.stringify(event);
747
+ inPage++;
748
+ if (!seen.has(key)) {
749
+ seen.add(key);
750
+ newInPage++;
751
+ events.push(event);
752
+ }
753
+ if (typeof event.created_at === "number") {
754
+ oldest = Math.min(oldest, event.created_at);
755
+ }
756
+ } else if (data[0] === "EOSE") {
757
+ wsRef.send(JSON.stringify(["CLOSE", data[1]]));
758
+ if (inPage >= PAGE_LIMIT && newInPage > 0 && page < MAX_PAGES) {
759
+ ask();
760
+ } else {
761
+ clearTimeout(timeout);
762
+ wsRef.close();
763
+ resolve();
764
+ }
725
765
  }
726
- ])
727
- );
728
- };
729
- wsRef.onmessage = (msg) => {
730
- try {
731
- const data = JSON.parse(msg.data);
732
- if (data[0] === "EVENT") {
733
- events.push(data[2]);
734
- } else if (data[0] === "EOSE") {
735
- cleanup();
736
- resolve();
766
+ } catch (err) {
767
+ log2.error({ relay: relayUrl, error: err }, "Failed to parse event");
737
768
  }
738
- } catch (err) {
739
- log2.error({ relay: relayUrl, error: err }, "Failed to parse event");
740
- }
741
- };
742
- wsRef.onerror = (err) => {
743
- log2.error({ relay: relayUrl, error: err }, "WebSocket error during query");
744
- cleanup();
745
- reject(err);
746
- };
747
- wsRef.onclose = () => {
748
- clearTimeout(timeout);
749
- };
750
- });
751
- } catch (err) {
752
- log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
753
- } finally {
754
- if (ws && ws.readyState !== WebSocket.CLOSED) {
755
- ws.close();
769
+ };
770
+ wsRef.onerror = (err) => {
771
+ clearTimeout(timeout);
772
+ log2.error({ relay: relayUrl, error: err }, "WebSocket error during query");
773
+ reject(err);
774
+ };
775
+ });
776
+ } catch (err) {
777
+ log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
778
+ } finally {
779
+ if (ws && ws.readyState !== WebSocket.CLOSED) ws.close();
756
780
  }
757
- }
758
- });
759
- await Promise.allSettled(queryPromises);
760
- log2.info({ eventCount: events.length }, "Query complete");
781
+ })
782
+ );
783
+ log2.info({ label, eventCount: events.length }, "Query complete");
761
784
  return events;
762
785
  }
786
+ async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
787
+ log2.info({ attestationId, relayCount: relays.length }, "Querying relays for attestation");
788
+ return querySubject({ kinds: [30078], "#d": [attestationId] }, relays, "id");
789
+ }
763
790
  async function queryByAddress(address, relays = DEFAULT_RELAYS) {
764
- const events = [];
765
791
  log2.info({ address, relayCount: relays.length }, "Querying relays for address");
766
- const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
767
- const queryPromises = relays.map(async (relayUrl) => {
768
- try {
769
- const ws = new WebSocket(relayUrl);
770
- await new Promise((resolve, reject) => {
771
- const timeout = setTimeout(() => {
772
- ws.close();
773
- reject(new Error("Query timeout"));
774
- }, budgetFor(deadlineAt));
775
- ws.onopen = () => {
776
- const subscriptionId = `ochk_addr_${Date.now()}`;
777
- ws.send(
778
- JSON.stringify([
779
- "REQ",
780
- subscriptionId,
781
- {
782
- kinds: [30078],
783
- // `#t`, NOT `#address`: NIP-12 relays index
784
- // single-letter tags only, so `#address`
785
- // matched nothing on any relay and this
786
- // function returned empty for every real
787
- // attestation. attest.ochk.io's verifier has
788
- // queried `#t` since the same fix landed
789
- // there; this is the back-port.
790
- "#t": [address],
791
- limit: 50
792
- }
793
- ])
794
- );
795
- };
796
- ws.onmessage = (msg) => {
797
- try {
798
- const data = JSON.parse(msg.data);
799
- if (data[0] === "EVENT") {
800
- events.push(data[2]);
801
- } else if (data[0] === "EOSE") {
802
- clearTimeout(timeout);
803
- ws.close();
804
- resolve();
805
- }
806
- } catch (err) {
807
- log2.error({ relay: relayUrl, error: err }, "Failed to parse event");
808
- }
809
- };
810
- ws.onerror = (err) => {
811
- clearTimeout(timeout);
812
- log2.error({ relay: relayUrl, error: err }, "WebSocket error during query");
813
- reject(err);
814
- };
815
- });
816
- } catch (err) {
817
- log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
818
- }
819
- });
820
- await Promise.allSettled(queryPromises);
821
- log2.info({ eventCount: events.length }, "Query complete");
822
- return events;
792
+ return querySubject({ kinds: [30078], "#t": [address] }, relays, "addr");
823
793
  }
824
794
  async function queryByIdentity(protocol, identifier, relays = DEFAULT_RELAYS) {
825
- const events = [];
826
- const identityTag = `${protocol}:${identifier}`;
827
- const identityTags = protocol === "nostr" ? nostrIdentifierForms(identifier).map((f) => `nostr:${f}`) : [identityTag];
795
+ const identityTags = protocol === "nostr" ? nostrIdentifierForms(identifier).map((f) => `nostr:${f}`) : [`${protocol}:${identifier}`];
828
796
  log2.info({ protocol, identifier, relayCount: relays.length }, "Querying relays for identity");
829
- const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
830
- const queryPromises = relays.map(async (relayUrl) => {
831
- try {
832
- const ws = new WebSocket(relayUrl);
833
- await new Promise((resolve, reject) => {
834
- const timeout = setTimeout(() => {
835
- ws.close();
836
- reject(new Error("Query timeout"));
837
- }, budgetFor(deadlineAt));
838
- ws.onopen = () => {
839
- const subscriptionId = `ochk_identity_${Date.now()}`;
840
- ws.send(
841
- JSON.stringify([
842
- "REQ",
843
- subscriptionId,
844
- {
845
- kinds: [30078],
846
- "#i": identityTags,
847
- limit: 10
848
- }
849
- ])
850
- );
851
- };
852
- ws.onmessage = (msg) => {
853
- try {
854
- const data = JSON.parse(msg.data);
855
- if (data[0] === "EVENT") {
856
- events.push(data[2]);
857
- } else if (data[0] === "EOSE") {
858
- clearTimeout(timeout);
859
- ws.close();
860
- resolve();
861
- }
862
- } catch (err) {
863
- log2.error({ relay: relayUrl, error: err }, "Failed to parse event");
864
- }
865
- };
866
- ws.onerror = (err) => {
867
- clearTimeout(timeout);
868
- log2.error({ relay: relayUrl, error: err }, "WebSocket error during query");
869
- reject(err);
870
- };
871
- });
872
- } catch (err) {
873
- log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
874
- }
875
- });
876
- await Promise.allSettled(queryPromises);
877
- log2.info({ eventCount: events.length }, "Query complete");
878
- return events;
797
+ return querySubject({ kinds: [30078], "#i": identityTags }, relays, "identity");
879
798
  }
880
799
  async function queryRecent(relays = DEFAULT_RELAYS, limit = 200) {
881
800
  const events = [];
@@ -958,6 +877,7 @@ function signedEnvelope(envelope) {
958
877
  return null;
959
878
  }
960
879
  if (!parsed.core.address || rfc3339Ms(parsed.core.issued_at) === null) return null;
880
+ if (parsed.repeated.length > 0) return null;
961
881
  const { expires_at: _copy, ...rest } = envelope;
962
882
  return {
963
883
  ...rest,
@@ -1189,7 +1109,6 @@ function extractAttestationIdFromUrl(url) {
1189
1109
  }
1190
1110
 
1191
1111
  // src/check.ts
1192
- var MAX_CANDIDATES = 20;
1193
1112
  async function check(params) {
1194
1113
  const {
1195
1114
  addr,
@@ -1217,7 +1136,7 @@ async function check(params) {
1217
1136
  return { ok: false, sats: 0, days: 0, score: 0, reasons: ["not_found"] };
1218
1137
  }
1219
1138
  let env = envelopes[0];
1220
- for (const candidate of envelopes.slice(0, MAX_CANDIDATES)) {
1139
+ for (const candidate of envelopes) {
1221
1140
  if (await signedBy(candidate)) {
1222
1141
  env = candidate;
1223
1142
  break;