@orangecheck/sdk 1.16.0 → 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/attestation.d.mts +1 -1
- package/dist/attestation.d.ts +1 -1
- package/dist/attestation.js +86 -170
- package/dist/attestation.js.map +1 -1
- package/dist/attestation.mjs +86 -170
- package/dist/attestation.mjs.map +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/identity.d.mts +1 -1
- package/dist/identity.d.ts +1 -1
- package/dist/identity.js.map +1 -1
- package/dist/identity.mjs.map +1 -1
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +106 -173
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +106 -173
- package/dist/index.mjs.map +1 -1
- package/dist/nostr.d.mts +1 -1
- package/dist/nostr.d.ts +1 -1
- package/dist/nostr.js +86 -170
- package/dist/nostr.js.map +1 -1
- package/dist/nostr.mjs +86 -170
- package/dist/nostr.mjs.map +1 -1
- package/dist/{types-BBRMCgMo.d.mts → types-DsrYs3EB.d.mts} +1 -1
- package/dist/{types-BBRMCgMo.d.ts → types-DsrYs3EB.d.ts} +1 -1
- package/dist/verify.d.mts +2 -1
- package/dist/verify.d.ts +2 -1
- package/dist/verify.js +19 -0
- package/dist/verify.js.map +1 -1
- package/dist/verify.mjs +19 -0
- package/dist/verify.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -215,6 +215,7 @@ function logVerificationError(protocol, identifier, error, context) {
|
|
|
215
215
|
}
|
|
216
216
|
|
|
217
217
|
// src/utils/time.ts
|
|
218
|
+
var ISSUED_AHEAD_MS = 12e4;
|
|
218
219
|
var RFC3339 = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
|
|
219
220
|
function rfc3339Ms(value) {
|
|
220
221
|
if (!value || !RFC3339.test(value)) return null;
|
|
@@ -307,11 +308,15 @@ function parseCanonicalMessage(msg) {
|
|
|
307
308
|
"ack"
|
|
308
309
|
]);
|
|
309
310
|
const extensions = {};
|
|
311
|
+
const keys = /* @__PURE__ */ new Set();
|
|
312
|
+
const repeated = [];
|
|
310
313
|
for (const line of lines) {
|
|
311
314
|
const idx = line.indexOf(":");
|
|
312
315
|
if (idx === -1) continue;
|
|
313
316
|
const key = line.slice(0, idx).trim().toLowerCase();
|
|
314
317
|
const val = line.slice(idx + 1).trim();
|
|
318
|
+
if (keys.has(key)) repeated.push(key);
|
|
319
|
+
keys.add(key);
|
|
315
320
|
if (!coreKeys.has(key) && val) extensions[key] = val;
|
|
316
321
|
}
|
|
317
322
|
const network = extensions["network"] || "mainnet";
|
|
@@ -320,6 +325,7 @@ function parseCanonicalMessage(msg) {
|
|
|
320
325
|
return {
|
|
321
326
|
core: { address, identities, issued_at },
|
|
322
327
|
expires,
|
|
328
|
+
repeated,
|
|
323
329
|
network,
|
|
324
330
|
extensions,
|
|
325
331
|
bond: bond !== void 0 && !isNaN(bond) && bond > 0 ? bond : void 0
|
|
@@ -438,6 +444,10 @@ async function verify(input, opts = {}) {
|
|
|
438
444
|
const parsed = parseCanonicalMessage(input.msg);
|
|
439
445
|
network = parsed.network || "mainnet";
|
|
440
446
|
const attestation_id = await generateAttestationId(input.msg);
|
|
447
|
+
if (parsed.repeated.length > 0) {
|
|
448
|
+
codes.push("decode_error");
|
|
449
|
+
return { ok: false, codes, network, attestation_id };
|
|
450
|
+
}
|
|
441
451
|
if (parsed.core.address !== input.addr) {
|
|
442
452
|
codes.push("bad_request");
|
|
443
453
|
return { ok: false, codes, network, attestation_id };
|
|
@@ -453,6 +463,15 @@ async function verify(input, opts = {}) {
|
|
|
453
463
|
return { ok: false, codes, network, attestation_id };
|
|
454
464
|
}
|
|
455
465
|
}
|
|
466
|
+
const issued = rfc3339Ms(parsed.core.issued_at);
|
|
467
|
+
if (issued === null) {
|
|
468
|
+
codes.push("decode_error");
|
|
469
|
+
return { ok: false, codes, network, attestation_id };
|
|
470
|
+
}
|
|
471
|
+
if (issued > Date.now() + ISSUED_AHEAD_MS) {
|
|
472
|
+
codes.push("not_yet_valid");
|
|
473
|
+
return { ok: false, codes, network, attestation_id };
|
|
474
|
+
}
|
|
456
475
|
if (parsed.expires !== void 0) {
|
|
457
476
|
const exp = rfc3339Ms(parsed.expires);
|
|
458
477
|
if (exp === null || exp < Date.now()) {
|
|
@@ -686,188 +705,98 @@ async function publishToRelays(event, relays = DEFAULT_RELAYS) {
|
|
|
686
705
|
log2.info({ successCount: success.length, failedCount: failed.length }, "Publishing complete");
|
|
687
706
|
return { success, failed };
|
|
688
707
|
}
|
|
689
|
-
|
|
708
|
+
var PAGE_LIMIT = 500;
|
|
709
|
+
var MAX_PAGES = 4;
|
|
710
|
+
async function querySubject(filter, relays, label) {
|
|
690
711
|
const events = [];
|
|
691
|
-
log2.info({ attestationId, relayCount: relays.length }, "Querying relays for attestation");
|
|
692
712
|
const deadlineAt = Date.now() + FANOUT_DEADLINE_MS;
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
713
|
+
await Promise.allSettled(
|
|
714
|
+
relays.map(async (relayUrl) => {
|
|
715
|
+
let ws = null;
|
|
716
|
+
try {
|
|
717
|
+
ws = new WebSocket(relayUrl);
|
|
718
|
+
const wsRef = ws;
|
|
719
|
+
await new Promise((resolve, reject) => {
|
|
720
|
+
const timeout = setTimeout(() => {
|
|
721
|
+
wsRef.close();
|
|
722
|
+
reject(new Error("Query timeout"));
|
|
723
|
+
}, budgetFor(deadlineAt));
|
|
724
|
+
const seen = /* @__PURE__ */ new Set();
|
|
725
|
+
let page = 0;
|
|
726
|
+
let inPage = 0;
|
|
727
|
+
let newInPage = 0;
|
|
728
|
+
let oldest = Infinity;
|
|
729
|
+
const ask = () => {
|
|
730
|
+
page++;
|
|
731
|
+
inPage = 0;
|
|
732
|
+
newInPage = 0;
|
|
733
|
+
const until = Number.isFinite(oldest) ? { until: oldest } : {};
|
|
734
|
+
wsRef.send(
|
|
735
|
+
JSON.stringify([
|
|
736
|
+
"REQ",
|
|
737
|
+
`ochk_${label}_${Date.now()}_${page}`,
|
|
738
|
+
{ ...filter, ...until, limit: PAGE_LIMIT }
|
|
739
|
+
])
|
|
740
|
+
);
|
|
741
|
+
};
|
|
742
|
+
wsRef.onopen = ask;
|
|
743
|
+
wsRef.onmessage = (msg) => {
|
|
744
|
+
try {
|
|
745
|
+
const data = JSON.parse(msg.data);
|
|
746
|
+
if (data[0] === "EVENT") {
|
|
747
|
+
const event = data[2];
|
|
748
|
+
const key = event.id ?? JSON.stringify(event);
|
|
749
|
+
inPage++;
|
|
750
|
+
if (!seen.has(key)) {
|
|
751
|
+
seen.add(key);
|
|
752
|
+
newInPage++;
|
|
753
|
+
events.push(event);
|
|
754
|
+
}
|
|
755
|
+
if (typeof event.created_at === "number") {
|
|
756
|
+
oldest = Math.min(oldest, event.created_at);
|
|
757
|
+
}
|
|
758
|
+
} else if (data[0] === "EOSE") {
|
|
759
|
+
wsRef.send(JSON.stringify(["CLOSE", data[1]]));
|
|
760
|
+
if (inPage >= PAGE_LIMIT && newInPage > 0 && page < MAX_PAGES) {
|
|
761
|
+
ask();
|
|
762
|
+
} else {
|
|
763
|
+
clearTimeout(timeout);
|
|
764
|
+
wsRef.close();
|
|
765
|
+
resolve();
|
|
766
|
+
}
|
|
717
767
|
}
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
};
|
|
721
|
-
wsRef.onmessage = (msg) => {
|
|
722
|
-
try {
|
|
723
|
-
const data = JSON.parse(msg.data);
|
|
724
|
-
if (data[0] === "EVENT") {
|
|
725
|
-
events.push(data[2]);
|
|
726
|
-
} else if (data[0] === "EOSE") {
|
|
727
|
-
cleanup();
|
|
728
|
-
resolve();
|
|
768
|
+
} catch (err) {
|
|
769
|
+
log2.error({ relay: relayUrl, error: err }, "Failed to parse event");
|
|
729
770
|
}
|
|
730
|
-
}
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
};
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
};
|
|
742
|
-
});
|
|
743
|
-
} catch (err) {
|
|
744
|
-
log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
|
|
745
|
-
} finally {
|
|
746
|
-
if (ws && ws.readyState !== WebSocket.CLOSED) {
|
|
747
|
-
ws.close();
|
|
771
|
+
};
|
|
772
|
+
wsRef.onerror = (err) => {
|
|
773
|
+
clearTimeout(timeout);
|
|
774
|
+
log2.error({ relay: relayUrl, error: err }, "WebSocket error during query");
|
|
775
|
+
reject(err);
|
|
776
|
+
};
|
|
777
|
+
});
|
|
778
|
+
} catch (err) {
|
|
779
|
+
log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
|
|
780
|
+
} finally {
|
|
781
|
+
if (ws && ws.readyState !== WebSocket.CLOSED) ws.close();
|
|
748
782
|
}
|
|
749
|
-
}
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
log2.info({ eventCount: events.length }, "Query complete");
|
|
783
|
+
})
|
|
784
|
+
);
|
|
785
|
+
log2.info({ label, eventCount: events.length }, "Query complete");
|
|
753
786
|
return events;
|
|
754
787
|
}
|
|
788
|
+
async function queryByAttestationId(attestationId, relays = DEFAULT_RELAYS) {
|
|
789
|
+
log2.info({ attestationId, relayCount: relays.length }, "Querying relays for attestation");
|
|
790
|
+
return querySubject({ kinds: [30078], "#d": [attestationId] }, relays, "id");
|
|
791
|
+
}
|
|
755
792
|
async function queryByAddress(address, relays = DEFAULT_RELAYS) {
|
|
756
|
-
const events = [];
|
|
757
793
|
log2.info({ address, relayCount: relays.length }, "Querying relays for address");
|
|
758
|
-
|
|
759
|
-
const queryPromises = relays.map(async (relayUrl) => {
|
|
760
|
-
try {
|
|
761
|
-
const ws = new WebSocket(relayUrl);
|
|
762
|
-
await new Promise((resolve, reject) => {
|
|
763
|
-
const timeout = setTimeout(() => {
|
|
764
|
-
ws.close();
|
|
765
|
-
reject(new Error("Query timeout"));
|
|
766
|
-
}, budgetFor(deadlineAt));
|
|
767
|
-
ws.onopen = () => {
|
|
768
|
-
const subscriptionId = `ochk_addr_${Date.now()}`;
|
|
769
|
-
ws.send(
|
|
770
|
-
JSON.stringify([
|
|
771
|
-
"REQ",
|
|
772
|
-
subscriptionId,
|
|
773
|
-
{
|
|
774
|
-
kinds: [30078],
|
|
775
|
-
// `#t`, NOT `#address`: NIP-12 relays index
|
|
776
|
-
// single-letter tags only, so `#address`
|
|
777
|
-
// matched nothing on any relay and this
|
|
778
|
-
// function returned empty for every real
|
|
779
|
-
// attestation. attest.ochk.io's verifier has
|
|
780
|
-
// queried `#t` since the same fix landed
|
|
781
|
-
// there; this is the back-port.
|
|
782
|
-
"#t": [address],
|
|
783
|
-
limit: 50
|
|
784
|
-
}
|
|
785
|
-
])
|
|
786
|
-
);
|
|
787
|
-
};
|
|
788
|
-
ws.onmessage = (msg) => {
|
|
789
|
-
try {
|
|
790
|
-
const data = JSON.parse(msg.data);
|
|
791
|
-
if (data[0] === "EVENT") {
|
|
792
|
-
events.push(data[2]);
|
|
793
|
-
} else if (data[0] === "EOSE") {
|
|
794
|
-
clearTimeout(timeout);
|
|
795
|
-
ws.close();
|
|
796
|
-
resolve();
|
|
797
|
-
}
|
|
798
|
-
} catch (err) {
|
|
799
|
-
log2.error({ relay: relayUrl, error: err }, "Failed to parse event");
|
|
800
|
-
}
|
|
801
|
-
};
|
|
802
|
-
ws.onerror = (err) => {
|
|
803
|
-
clearTimeout(timeout);
|
|
804
|
-
log2.error({ relay: relayUrl, error: err }, "WebSocket error during query");
|
|
805
|
-
reject(err);
|
|
806
|
-
};
|
|
807
|
-
});
|
|
808
|
-
} catch (err) {
|
|
809
|
-
log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
|
|
810
|
-
}
|
|
811
|
-
});
|
|
812
|
-
await Promise.allSettled(queryPromises);
|
|
813
|
-
log2.info({ eventCount: events.length }, "Query complete");
|
|
814
|
-
return events;
|
|
794
|
+
return querySubject({ kinds: [30078], "#t": [address] }, relays, "addr");
|
|
815
795
|
}
|
|
816
796
|
async function queryByIdentity(protocol, identifier, relays = DEFAULT_RELAYS) {
|
|
817
|
-
const
|
|
818
|
-
const identityTag = `${protocol}:${identifier}`;
|
|
819
|
-
const identityTags = protocol === "nostr" ? nostrIdentifierForms(identifier).map((f) => `nostr:${f}`) : [identityTag];
|
|
797
|
+
const identityTags = protocol === "nostr" ? nostrIdentifierForms(identifier).map((f) => `nostr:${f}`) : [`${protocol}:${identifier}`];
|
|
820
798
|
log2.info({ protocol, identifier, relayCount: relays.length }, "Querying relays for identity");
|
|
821
|
-
|
|
822
|
-
const queryPromises = relays.map(async (relayUrl) => {
|
|
823
|
-
try {
|
|
824
|
-
const ws = new WebSocket(relayUrl);
|
|
825
|
-
await new Promise((resolve, reject) => {
|
|
826
|
-
const timeout = setTimeout(() => {
|
|
827
|
-
ws.close();
|
|
828
|
-
reject(new Error("Query timeout"));
|
|
829
|
-
}, budgetFor(deadlineAt));
|
|
830
|
-
ws.onopen = () => {
|
|
831
|
-
const subscriptionId = `ochk_identity_${Date.now()}`;
|
|
832
|
-
ws.send(
|
|
833
|
-
JSON.stringify([
|
|
834
|
-
"REQ",
|
|
835
|
-
subscriptionId,
|
|
836
|
-
{
|
|
837
|
-
kinds: [30078],
|
|
838
|
-
"#i": identityTags,
|
|
839
|
-
limit: 10
|
|
840
|
-
}
|
|
841
|
-
])
|
|
842
|
-
);
|
|
843
|
-
};
|
|
844
|
-
ws.onmessage = (msg) => {
|
|
845
|
-
try {
|
|
846
|
-
const data = JSON.parse(msg.data);
|
|
847
|
-
if (data[0] === "EVENT") {
|
|
848
|
-
events.push(data[2]);
|
|
849
|
-
} else if (data[0] === "EOSE") {
|
|
850
|
-
clearTimeout(timeout);
|
|
851
|
-
ws.close();
|
|
852
|
-
resolve();
|
|
853
|
-
}
|
|
854
|
-
} catch (err) {
|
|
855
|
-
log2.error({ relay: relayUrl, error: err }, "Failed to parse event");
|
|
856
|
-
}
|
|
857
|
-
};
|
|
858
|
-
ws.onerror = (err) => {
|
|
859
|
-
clearTimeout(timeout);
|
|
860
|
-
log2.error({ relay: relayUrl, error: err }, "WebSocket error during query");
|
|
861
|
-
reject(err);
|
|
862
|
-
};
|
|
863
|
-
});
|
|
864
|
-
} catch (err) {
|
|
865
|
-
log2.error({ relay: relayUrl, error: err }, "Failed to query relay");
|
|
866
|
-
}
|
|
867
|
-
});
|
|
868
|
-
await Promise.allSettled(queryPromises);
|
|
869
|
-
log2.info({ eventCount: events.length }, "Query complete");
|
|
870
|
-
return events;
|
|
799
|
+
return querySubject({ kinds: [30078], "#i": identityTags }, relays, "identity");
|
|
871
800
|
}
|
|
872
801
|
async function queryRecent(relays = DEFAULT_RELAYS, limit = 200) {
|
|
873
802
|
const events = [];
|
|
@@ -950,6 +879,7 @@ function signedEnvelope(envelope) {
|
|
|
950
879
|
return null;
|
|
951
880
|
}
|
|
952
881
|
if (!parsed.core.address || rfc3339Ms(parsed.core.issued_at) === null) return null;
|
|
882
|
+
if (parsed.repeated.length > 0) return null;
|
|
953
883
|
const { expires_at: _copy, ...rest } = envelope;
|
|
954
884
|
return {
|
|
955
885
|
...rest,
|
|
@@ -1181,8 +1111,6 @@ function extractAttestationIdFromUrl(url) {
|
|
|
1181
1111
|
}
|
|
1182
1112
|
|
|
1183
1113
|
// src/check.ts
|
|
1184
|
-
var ISSUED_AHEAD_MS = 12e4;
|
|
1185
|
-
var MAX_CANDIDATES = 20;
|
|
1186
1114
|
async function check(params) {
|
|
1187
1115
|
const {
|
|
1188
1116
|
addr,
|
|
@@ -1210,7 +1138,7 @@ async function check(params) {
|
|
|
1210
1138
|
return { ok: false, sats: 0, days: 0, score: 0, reasons: ["not_found"] };
|
|
1211
1139
|
}
|
|
1212
1140
|
let env = envelopes[0];
|
|
1213
|
-
for (const candidate of envelopes
|
|
1141
|
+
for (const candidate of envelopes) {
|
|
1214
1142
|
if (await signedBy(candidate)) {
|
|
1215
1143
|
env = candidate;
|
|
1216
1144
|
break;
|
|
@@ -2361,6 +2289,11 @@ var STATUS_META = {
|
|
|
2361
2289
|
detail: "This signature has passed its expiration date. Generate a new signature with a fresh message.",
|
|
2362
2290
|
severity: "error"
|
|
2363
2291
|
},
|
|
2292
|
+
not_yet_valid: {
|
|
2293
|
+
label: "Not valid yet",
|
|
2294
|
+
detail: "The message is dated ahead of this clock. It counts once that time arrives, or once a clock that is wrong is set right.",
|
|
2295
|
+
severity: "error"
|
|
2296
|
+
},
|
|
2364
2297
|
network_testmode: {
|
|
2365
2298
|
label: "Test network detected",
|
|
2366
2299
|
detail: "This signature uses testnet or signet. Test networks are for development only and cannot be verified in production mode.",
|