@thurinlabs/identity-kit 1.4.0 → 2.1.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 +44 -384
- package/dist/{chunk-ZAQ2EPF5.js → chunk-SGAMXZDL.js} +2484 -1343
- package/dist/{chunk-M2OJDB7I.cjs → chunk-T4VPFPFP.cjs} +2489 -1348
- package/dist/core.cjs +46 -3
- package/dist/core.d.cts +1593 -902
- package/dist/core.d.ts +1593 -902
- package/dist/core.js +67 -24
- package/dist/index.cjs +27 -459
- package/dist/index.d.cts +1 -83
- package/dist/index.d.ts +1 -83
- package/dist/index.js +67 -499
- package/package.json +6 -22
- package/dist/chunk-M2OJDB7I.cjs.map +0 -1
- package/dist/chunk-ZAQ2EPF5.js.map +0 -1
- package/dist/core.cjs.map +0 -1
- package/dist/core.js.map +0 -1
- package/dist/embed.global.js +0 -32213
- package/dist/embed.global.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.css +0 -185
- package/dist/index.css.map +0 -1
- package/dist/index.js.map +0 -1
|
@@ -1,237 +1,1190 @@
|
|
|
1
|
-
// src/core/
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
1
|
+
// src/core/fingerprint.ts
|
|
2
|
+
import { keccak256 } from "viem";
|
|
3
|
+
var FINGERPRINT_HEX = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
4
|
+
function normalizeFingerprint(input) {
|
|
5
|
+
const hex = input.replace(/^0x/i, "").replace(/\s+/g, "").toLowerCase();
|
|
6
|
+
return FINGERPRINT_HEX.test(hex) ? hex : null;
|
|
7
|
+
}
|
|
8
|
+
function sameFingerprint(a, b) {
|
|
9
|
+
const x = a ? normalizeFingerprint(a) : null;
|
|
10
|
+
return x !== null && x === (b ? normalizeFingerprint(b) : null);
|
|
11
|
+
}
|
|
12
|
+
function fingerprintToBytes(fingerprint) {
|
|
13
|
+
const hex = normalizeFingerprint(fingerprint);
|
|
14
|
+
if (!hex) throw new Error(`Invalid PGP fingerprint: ${fingerprint}`);
|
|
15
|
+
return `0x${hex}`;
|
|
16
|
+
}
|
|
17
|
+
function bytesToFingerprint(bytes) {
|
|
18
|
+
return bytes.replace(/^0x/i, "").toLowerCase();
|
|
19
|
+
}
|
|
20
|
+
function fingerprintHash(fingerprint) {
|
|
21
|
+
return keccak256(fingerprintToBytes(fingerprint));
|
|
22
|
+
}
|
|
23
|
+
function keyIdOf(fingerprint) {
|
|
24
|
+
const hex = normalizeFingerprint(fingerprint);
|
|
25
|
+
if (!hex) throw new Error(`Invalid PGP fingerprint: ${fingerprint}`);
|
|
26
|
+
return `0x${keyIdHex(hex)}`;
|
|
27
|
+
}
|
|
28
|
+
function keyIdHex(normalizedFingerprint) {
|
|
29
|
+
return normalizedFingerprint.length === 64 ? normalizedFingerprint.slice(0, 16) : normalizedFingerprint.slice(-16);
|
|
30
|
+
}
|
|
31
|
+
function keyIdToBytes(keyId) {
|
|
32
|
+
const hex = keyId.replace(/^0x/i, "").replace(/\s+/g, "").toLowerCase();
|
|
33
|
+
return /^[0-9a-f]{16}$/.test(hex) ? `0x${hex}` : null;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// src/core/proofs.ts
|
|
37
|
+
var get = (url, init = {}) => fetch(url, { referrerPolicy: "no-referrer", ...init });
|
|
38
|
+
var PROVIDERS = [
|
|
29
39
|
{
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
{
|
|
35
|
-
"name": "",
|
|
36
|
-
"type": "string",
|
|
37
|
-
"internalType": "string"
|
|
38
|
-
}
|
|
39
|
-
],
|
|
40
|
-
"stateMutability": "view"
|
|
40
|
+
provider: "github",
|
|
41
|
+
label: "GitHub",
|
|
42
|
+
pattern: /^https:\/\/gist\.github\.com\/([^/]+)\/([a-f0-9]+)$/i,
|
|
43
|
+
parse: (m) => ({ user: m[1], gistId: m[2] })
|
|
41
44
|
},
|
|
42
45
|
{
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
"
|
|
46
|
-
"
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
"type": "string",
|
|
50
|
-
"internalType": "string"
|
|
51
|
-
}
|
|
52
|
-
],
|
|
53
|
-
"stateMutability": "view"
|
|
46
|
+
// A repository's description: the form an organisation can use, since gists belong to user
|
|
47
|
+
// accounts only. Same shape as the Codeberg proof.
|
|
48
|
+
provider: "github",
|
|
49
|
+
label: "GitHub",
|
|
50
|
+
pattern: /^https:\/\/github\.com\/([A-Za-z0-9-]+)\/([A-Za-z0-9._-]+)$/i,
|
|
51
|
+
parse: (m) => ({ user: m[1], repo: m[2] })
|
|
54
52
|
},
|
|
55
53
|
{
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
"name": "",
|
|
62
|
-
"type": "uint256",
|
|
63
|
-
"internalType": "uint256"
|
|
64
|
-
}
|
|
65
|
-
],
|
|
66
|
-
"stateMutability": "view"
|
|
54
|
+
provider: "dns",
|
|
55
|
+
label: "DNS",
|
|
56
|
+
// A hostname only; underscores allowed for names like _thurin.example.com.
|
|
57
|
+
pattern: /^dns:([a-z0-9._-]+)\?type=TXT$/i,
|
|
58
|
+
parse: (m) => ({ domain: m[1] })
|
|
67
59
|
},
|
|
68
60
|
{
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
{
|
|
74
|
-
"name": "",
|
|
75
|
-
"type": "uint256",
|
|
76
|
-
"internalType": "uint256"
|
|
77
|
-
}
|
|
78
|
-
],
|
|
79
|
-
"stateMutability": "view"
|
|
61
|
+
provider: "farcaster",
|
|
62
|
+
label: "Farcaster",
|
|
63
|
+
pattern: /^https:\/\/farcaster\.xyz\/([^/]+)\/(0x[a-f0-9]+)$/i,
|
|
64
|
+
parse: (m) => ({ user: m[1], castHash: m[2] })
|
|
80
65
|
},
|
|
81
66
|
{
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
{
|
|
87
|
-
"name": "",
|
|
88
|
-
"type": "uint256",
|
|
89
|
-
"internalType": "uint256"
|
|
90
|
-
}
|
|
91
|
-
],
|
|
92
|
-
"stateMutability": "view"
|
|
67
|
+
provider: "codeberg",
|
|
68
|
+
label: "Codeberg",
|
|
69
|
+
pattern: /^https:\/\/codeberg\.org\/([^/]+)\/([^/]+)$/i,
|
|
70
|
+
parse: (m) => ({ user: m[1], repo: m[2] })
|
|
93
71
|
},
|
|
94
72
|
{
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
"
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
73
|
+
provider: "mastodon",
|
|
74
|
+
label: "Mastodon",
|
|
75
|
+
// A bare hostname (optional port), so "mastodon.social@evil.com" can't send the lookup elsewhere.
|
|
76
|
+
pattern: /^https:\/\/([a-z0-9.-]+(?::\d+)?)\/@([^/@?#\s]+)$/i,
|
|
77
|
+
parse: (m) => ({ instance: m[1], user: m[2] })
|
|
78
|
+
}
|
|
79
|
+
];
|
|
80
|
+
function identifyProof(notation) {
|
|
81
|
+
if (notation.name !== "proof@thurin.id") return null;
|
|
82
|
+
for (const { provider, label, pattern, parse } of PROVIDERS) {
|
|
83
|
+
const m = notation.value.match(pattern);
|
|
84
|
+
if (m) {
|
|
85
|
+
return { provider, label, url: notation.value, ...parse(m) };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
return { provider: "unknown", label: "Unknown", url: notation.value };
|
|
89
|
+
}
|
|
90
|
+
function displayUrl(proof) {
|
|
91
|
+
if (proof.provider === "dns") return proof.domain;
|
|
92
|
+
if (proof.provider === "github") return proof.user;
|
|
93
|
+
if (proof.provider === "farcaster") return `@${proof.user}`;
|
|
94
|
+
if (proof.provider === "codeberg") return proof.user;
|
|
95
|
+
if (proof.provider === "mastodon") return `@${proof.user}@${proof.instance}`;
|
|
96
|
+
return proof.url;
|
|
97
|
+
}
|
|
98
|
+
function proofHref(proof) {
|
|
99
|
+
if (proof.provider === "dns") return `https://${proof.domain}`;
|
|
100
|
+
if (proof.provider === "github") return `https://github.com/${proof.user}`;
|
|
101
|
+
if (proof.provider === "farcaster") return `https://farcaster.xyz/${proof.user}`;
|
|
102
|
+
if (proof.provider === "codeberg") return `https://codeberg.org/${proof.user}`;
|
|
103
|
+
if (proof.provider === "mastodon") return `https://${proof.instance}/@${proof.user}`;
|
|
104
|
+
if (proof.url.startsWith("http")) return proof.url;
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
function proofSecondaryHref(proof) {
|
|
108
|
+
if (proof.provider === "github") return proof.url;
|
|
109
|
+
if (proof.provider === "farcaster") return proof.url;
|
|
110
|
+
if (proof.provider === "codeberg") return proof.url;
|
|
111
|
+
if (proof.provider === "mastodon") return proof.url;
|
|
112
|
+
return null;
|
|
113
|
+
}
|
|
114
|
+
var FPR_TOKEN = "OPENPGP4FPR:";
|
|
115
|
+
function containsFingerprint(text, fingerprint) {
|
|
116
|
+
return text.toUpperCase().includes(FPR_TOKEN + fingerprint.toUpperCase());
|
|
117
|
+
}
|
|
118
|
+
function containsFingerprintUrl(text, fingerprint) {
|
|
119
|
+
return text.toUpperCase().includes(fingerprint.toUpperCase());
|
|
120
|
+
}
|
|
121
|
+
async function verifyGitHub(proof, fingerprint) {
|
|
122
|
+
if (proof.repo) return verifyGitHubRepo(proof, fingerprint);
|
|
123
|
+
try {
|
|
124
|
+
const resp = await get(`https://api.github.com/gists/${proof.gistId}`);
|
|
125
|
+
if (!resp.ok) return { verified: false, reason: `GitHub API returned ${resp.status}` };
|
|
126
|
+
const data = await resp.json();
|
|
127
|
+
if (data.owner?.login?.toLowerCase() !== proof.user.toLowerCase()) {
|
|
128
|
+
return { verified: false, reason: "Gist owner does not match claimed user" };
|
|
129
|
+
}
|
|
130
|
+
for (const file of Object.values(data.files || {})) {
|
|
131
|
+
if (file.content && containsFingerprint(file.content, fingerprint)) {
|
|
132
|
+
return { verified: true };
|
|
103
133
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
134
|
+
}
|
|
135
|
+
return { verified: false, reason: "Fingerprint token not found in gist" };
|
|
136
|
+
} catch (err) {
|
|
137
|
+
return { verified: false, reason: `GitHub fetch failed: ${err.message}` };
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
async function verifyGitHubRepo(proof, fingerprint) {
|
|
141
|
+
try {
|
|
142
|
+
const resp = await get(
|
|
143
|
+
`https://api.github.com/repos/${encodeURIComponent(proof.user)}/${encodeURIComponent(proof.repo)}`
|
|
144
|
+
);
|
|
145
|
+
if (!resp.ok) return { verified: false, reason: `GitHub API returned ${resp.status}` };
|
|
146
|
+
const data = await resp.json();
|
|
147
|
+
if (data.owner?.login?.toLowerCase() !== proof.user.toLowerCase()) {
|
|
148
|
+
return { verified: false, reason: "Repository owner does not match claimed account" };
|
|
149
|
+
}
|
|
150
|
+
if (data.description && containsFingerprint(data.description, fingerprint)) {
|
|
151
|
+
return { verified: true };
|
|
152
|
+
}
|
|
153
|
+
return { verified: false, reason: "Fingerprint token not found in repository description" };
|
|
154
|
+
} catch (err) {
|
|
155
|
+
return { verified: false, reason: `GitHub fetch failed: ${err.message}` };
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
async function verifyDNS(proof, fingerprint) {
|
|
159
|
+
try {
|
|
160
|
+
const resp = await get(
|
|
161
|
+
`https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(proof.domain)}&type=TXT`,
|
|
162
|
+
{ headers: { Accept: "application/dns-json" } }
|
|
163
|
+
);
|
|
164
|
+
if (!resp.ok) return { verified: false, reason: `DNS query returned ${resp.status}` };
|
|
165
|
+
const data = await resp.json();
|
|
166
|
+
for (const answer of data.Answer || []) {
|
|
167
|
+
if (answer.type !== 16) continue;
|
|
168
|
+
if (answer.data && containsFingerprint(answer.data, fingerprint)) {
|
|
169
|
+
return { verified: true };
|
|
116
170
|
}
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
{
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
"name": "",
|
|
153
|
-
"type": "bytes32",
|
|
154
|
-
"internalType": "bytes32"
|
|
155
|
-
}
|
|
156
|
-
],
|
|
157
|
-
"stateMutability": "view"
|
|
158
|
-
},
|
|
159
|
-
{
|
|
160
|
-
"type": "function",
|
|
161
|
-
"name": "VERSION",
|
|
162
|
-
"inputs": [],
|
|
163
|
-
"outputs": [
|
|
164
|
-
{
|
|
165
|
-
"name": "",
|
|
166
|
-
"type": "uint8",
|
|
167
|
-
"internalType": "uint8"
|
|
171
|
+
}
|
|
172
|
+
return { verified: false, reason: "Fingerprint token not found in TXT records" };
|
|
173
|
+
} catch (err) {
|
|
174
|
+
return { verified: false, reason: `DNS fetch failed: ${err.message}` };
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
var FARCASTER_HUB = "https://haatz.quilibrium.com";
|
|
178
|
+
var NEYNAR_HUB = "https://hub-api.neynar.com";
|
|
179
|
+
function farcasterSource(opts) {
|
|
180
|
+
if (opts.farcasterHub) return { base: opts.farcasterHub.replace(/\/+$/, ""), headers: {} };
|
|
181
|
+
if (opts.neynarApiKey) return { base: NEYNAR_HUB, headers: { "x-api-key": opts.neynarApiKey } };
|
|
182
|
+
return { base: FARCASTER_HUB, headers: {} };
|
|
183
|
+
}
|
|
184
|
+
async function verifyFarcaster(proof, fingerprint, opts = {}) {
|
|
185
|
+
const { base, headers } = farcasterSource(opts);
|
|
186
|
+
const host = base.replace(/^https?:\/\//, "");
|
|
187
|
+
try {
|
|
188
|
+
const nameResp = await get(`${base}/v1/userNameProofByName?name=${encodeURIComponent(proof.user)}`, { headers });
|
|
189
|
+
if (nameResp.status === 404) return { verified: false, reason: `Could not resolve Farcaster user "${proof.user}"` };
|
|
190
|
+
if (!nameResp.ok) return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) returned ${nameResp.status}` };
|
|
191
|
+
const fid = (await nameResp.json()).fid;
|
|
192
|
+
if (!fid) return { verified: false, reason: `Could not resolve Farcaster user "${proof.user}"` };
|
|
193
|
+
let pageToken = "";
|
|
194
|
+
for (let page = 0; page < 5; page++) {
|
|
195
|
+
const url = `${base}/v1/castsByFid?fid=${fid}&pageSize=100&reverse=true${pageToken ? `&pageToken=${pageToken}` : ""}`;
|
|
196
|
+
const resp = await get(url, { headers });
|
|
197
|
+
if (!resp.ok) return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) returned ${resp.status}` };
|
|
198
|
+
const data = await resp.json();
|
|
199
|
+
for (const msg of data.messages || []) {
|
|
200
|
+
if (msg.hash && msg.hash.startsWith(proof.castHash)) {
|
|
201
|
+
const text = msg.data?.castAddBody?.text || "";
|
|
202
|
+
if (containsFingerprint(text, fingerprint)) {
|
|
203
|
+
return { verified: true };
|
|
204
|
+
}
|
|
205
|
+
}
|
|
168
206
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
207
|
+
if (!data.nextPageToken) break;
|
|
208
|
+
pageToken = data.nextPageToken;
|
|
209
|
+
}
|
|
210
|
+
return { verified: false, reason: "Cast not found" };
|
|
211
|
+
} catch (err) {
|
|
212
|
+
return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) didn't answer (${err.message})` };
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
async function verifyCodeberg(proof, fingerprint) {
|
|
216
|
+
try {
|
|
217
|
+
const resp = await get(
|
|
218
|
+
`https://codeberg.org/api/v1/repos/${encodeURIComponent(proof.user)}/${encodeURIComponent(proof.repo)}`
|
|
219
|
+
);
|
|
220
|
+
if (!resp.ok) return { verified: false, reason: `Codeberg API returned ${resp.status}` };
|
|
221
|
+
const data = await resp.json();
|
|
222
|
+
if (data.owner?.login?.toLowerCase() !== proof.user.toLowerCase()) {
|
|
223
|
+
return { verified: false, reason: "Repository owner does not match claimed account" };
|
|
224
|
+
}
|
|
225
|
+
if (data.description && containsFingerprint(data.description, fingerprint)) {
|
|
226
|
+
return { verified: true };
|
|
227
|
+
}
|
|
228
|
+
return { verified: false, reason: "Fingerprint token not found in repo description" };
|
|
229
|
+
} catch (err) {
|
|
230
|
+
return { verified: false, reason: `Codeberg fetch failed: ${err.message}` };
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async function verifyMastodon(proof, fingerprint) {
|
|
234
|
+
try {
|
|
235
|
+
if (!proof.instance || !/^[a-z0-9.-]+(?::\d+)?$/i.test(proof.instance)) {
|
|
236
|
+
return { verified: false, reason: "Invalid Mastodon instance host" };
|
|
237
|
+
}
|
|
238
|
+
const resp = await get(
|
|
239
|
+
`https://${proof.instance}/api/v1/accounts/lookup?acct=${encodeURIComponent(proof.user)}`
|
|
240
|
+
);
|
|
241
|
+
if (!resp.ok) return { verified: false, reason: `Mastodon API returned ${resp.status}` };
|
|
242
|
+
const data = await resp.json();
|
|
243
|
+
const keyId = keyIdHex(fingerprint.replace(/^0x/i, "").replace(/\s+/g, "").toLowerCase());
|
|
244
|
+
for (const field of data.fields || []) {
|
|
245
|
+
const text = field.value.replace(/<[^>]*>/g, "");
|
|
246
|
+
if (containsFingerprintUrl(text, fingerprint) || containsFingerprintUrl(text, keyId)) {
|
|
247
|
+
return { verified: true };
|
|
180
248
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
"internalType": "address[]"
|
|
249
|
+
}
|
|
250
|
+
if (data.note) {
|
|
251
|
+
const text = data.note.replace(/<[^>]*>/g, "");
|
|
252
|
+
if (containsFingerprintUrl(text, fingerprint) || containsFingerprintUrl(text, keyId)) {
|
|
253
|
+
return { verified: true };
|
|
187
254
|
}
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
255
|
+
}
|
|
256
|
+
return { verified: false, reason: "Fingerprint not found in profile fields or bio" };
|
|
257
|
+
} catch (err) {
|
|
258
|
+
return { verified: false, reason: `Mastodon fetch failed: ${err.message}` };
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
var verifiers = {
|
|
262
|
+
github: verifyGitHub,
|
|
263
|
+
dns: verifyDNS,
|
|
264
|
+
farcaster: verifyFarcaster,
|
|
265
|
+
codeberg: verifyCodeberg,
|
|
266
|
+
mastodon: verifyMastodon
|
|
267
|
+
};
|
|
268
|
+
async function verifyProof(proof, fingerprint, opts) {
|
|
269
|
+
const fn = verifiers[proof.provider];
|
|
270
|
+
if (!fn) return { verified: false, reason: "Unknown provider" };
|
|
271
|
+
return fn(proof, fingerprint, typeof opts === "string" ? { neynarApiKey: opts } : opts ?? {});
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// src/core/pgp.ts
|
|
275
|
+
function pgpConfig(openpgp) {
|
|
276
|
+
const rejectCurves = new Set([...openpgp.config.rejectCurves ?? []].filter((c) => c !== "secp256k1"));
|
|
277
|
+
return { ...openpgp.config, rejectCurves };
|
|
278
|
+
}
|
|
279
|
+
function newestSignature(sigs = []) {
|
|
280
|
+
return [...sigs].sort((x, y) => (y.created?.getTime?.() ?? 0) - (x.created?.getTime?.() ?? 0))[0];
|
|
281
|
+
}
|
|
282
|
+
function algorithmName(keyOrSubkey) {
|
|
283
|
+
try {
|
|
284
|
+
const info = keyOrSubkey.getAlgorithmInfo();
|
|
285
|
+
const curve = (info.curve || "").toLowerCase();
|
|
286
|
+
if (curve.startsWith("ed25519") || info.algorithm === "ed25519" || info.algorithm === "eddsaLegacy") return "Ed25519";
|
|
287
|
+
if (curve.startsWith("curve25519") || info.algorithm === "x25519") return "Cv25519";
|
|
288
|
+
if (curve.startsWith("ed448")) return "Ed448";
|
|
289
|
+
if (curve.startsWith("curve448") || info.algorithm === "x448") return "X448";
|
|
290
|
+
if (curve.startsWith("nistp") || curve.startsWith("p")) return `NIST P-${curve.replace(/\D/g, "")}`;
|
|
291
|
+
if (curve.startsWith("secp")) return curve;
|
|
292
|
+
if (curve.startsWith("brainpool")) return curve;
|
|
293
|
+
if (info.algorithm.startsWith("rsa")) return info.bits ? `RSA ${info.bits}` : "RSA";
|
|
294
|
+
if (info.algorithm.startsWith("dsa")) return info.bits ? `DSA ${info.bits}` : "DSA";
|
|
295
|
+
if (info.algorithm.startsWith("elgamal")) return info.bits ? `ElGamal ${info.bits}` : "ElGamal";
|
|
296
|
+
return info.curve || info.algorithm;
|
|
297
|
+
} catch {
|
|
298
|
+
return String(keyOrSubkey.keyPacket?.algorithm ?? "unknown");
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
var TEXT_START = 45;
|
|
302
|
+
function hexToBytes(hex) {
|
|
303
|
+
const h = hex.slice(2);
|
|
304
|
+
const out = new Uint8Array(h.length / 2);
|
|
305
|
+
for (let i = 0; i < out.length; i++) out[i] = parseInt(h.substr(i * 2, 2), 16);
|
|
306
|
+
return out;
|
|
307
|
+
}
|
|
308
|
+
function normalizeInput(input) {
|
|
309
|
+
if (typeof input === "string") {
|
|
310
|
+
const t = input.trim();
|
|
311
|
+
if (/^0x([0-9a-fA-F]{2})*$/.test(t)) return normalizeInput(hexToBytes(t));
|
|
312
|
+
return input;
|
|
313
|
+
}
|
|
314
|
+
if (input.length > 0 && input[0] === TEXT_START) return new TextDecoder().decode(input);
|
|
315
|
+
return input;
|
|
316
|
+
}
|
|
317
|
+
async function readKeyAny(openpgp, input) {
|
|
318
|
+
const v = normalizeInput(input);
|
|
319
|
+
return typeof v === "string" ? openpgp.readKey({ armoredKey: v }) : openpgp.readKey({ binaryKey: v });
|
|
320
|
+
}
|
|
321
|
+
async function readSignatureAny(openpgp, input) {
|
|
322
|
+
const v = normalizeInput(input);
|
|
323
|
+
if (typeof v === "string" && v.includes("-----BEGIN PGP SIGNED MESSAGE-----")) {
|
|
324
|
+
const message = await openpgp.readCleartextMessage({ cleartextMessage: v });
|
|
325
|
+
return { packets: message.signature.packets, text: message.getText(), signature: message.signature };
|
|
326
|
+
}
|
|
327
|
+
const signature = typeof v === "string" ? await openpgp.readSignature({ armoredSignature: v }) : await openpgp.readSignature({ binarySignature: v });
|
|
328
|
+
return { packets: signature.packets, text: null, signature };
|
|
329
|
+
}
|
|
330
|
+
function statementText(address) {
|
|
331
|
+
return `I control the Ethereum address: ${address.toLowerCase()}`;
|
|
332
|
+
}
|
|
333
|
+
async function parsePgpKey(armoredKey) {
|
|
334
|
+
try {
|
|
335
|
+
const openpgp = await import("openpgp");
|
|
336
|
+
const config = pgpConfig(openpgp);
|
|
337
|
+
const key = await readKeyAny(openpgp, armoredKey);
|
|
338
|
+
const fingerprint = key.getFingerprint().toUpperCase();
|
|
339
|
+
const algorithm = algorithmName(key);
|
|
340
|
+
const created = key.keyPacket.created?.toISOString() ?? null;
|
|
341
|
+
const expiration = await key.getExpirationTime();
|
|
342
|
+
const expires = expiration && expiration !== Infinity ? new Date(expiration).toISOString() : null;
|
|
343
|
+
const userIDs = [];
|
|
344
|
+
const notations = [];
|
|
345
|
+
const seen = /* @__PURE__ */ new Set();
|
|
346
|
+
for (const user of key.users) {
|
|
347
|
+
const certs = user.selfCertifications;
|
|
348
|
+
if (!certs || certs.length === 0) continue;
|
|
349
|
+
let userVerified = false;
|
|
350
|
+
for (const cert of certs) {
|
|
351
|
+
try {
|
|
352
|
+
await cert.verify(
|
|
353
|
+
key.keyPacket,
|
|
354
|
+
cert.signatureType,
|
|
355
|
+
{ userID: user.userID, key: key.keyPacket },
|
|
356
|
+
void 0,
|
|
357
|
+
// date: now
|
|
358
|
+
void 0,
|
|
359
|
+
// detached
|
|
360
|
+
config
|
|
361
|
+
);
|
|
362
|
+
} catch {
|
|
363
|
+
continue;
|
|
364
|
+
}
|
|
365
|
+
userVerified = true;
|
|
366
|
+
if (cert.rawNotations) {
|
|
367
|
+
for (const n of cert.rawNotations) {
|
|
368
|
+
const name = typeof n.name === "string" ? n.name : new TextDecoder().decode(n.name);
|
|
369
|
+
const value = n.value instanceof Uint8Array ? new TextDecoder().decode(n.value) : typeof n.value === "string" ? n.value : null;
|
|
370
|
+
if (value) {
|
|
371
|
+
if (!seen.has(value)) {
|
|
372
|
+
seen.add(value);
|
|
373
|
+
notations.push({ name, value });
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
}
|
|
199
378
|
}
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
"name": "",
|
|
204
|
-
"type": "uint256",
|
|
205
|
-
"internalType": "uint256"
|
|
379
|
+
if (userVerified) {
|
|
380
|
+
const uid = user.userID?.userID;
|
|
381
|
+
if (uid) userIDs.push(uid);
|
|
206
382
|
}
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
383
|
+
}
|
|
384
|
+
const now = /* @__PURE__ */ new Date();
|
|
385
|
+
const subkeys = await Promise.all(key.subkeys.map(async (sk) => {
|
|
386
|
+
const flags = newestSignature(sk.bindingSignatures)?.keyFlags?.[0] ?? 0;
|
|
387
|
+
const usage = [];
|
|
388
|
+
if (flags & 2) usage.push("sign");
|
|
389
|
+
if (flags & 12) usage.push("encrypt");
|
|
390
|
+
if (flags & 32) usage.push("auth");
|
|
391
|
+
const exp = await sk.getExpirationTime(now, config).catch(() => null);
|
|
392
|
+
const valid = await sk.verify(now, config).then(() => true, () => false);
|
|
393
|
+
return {
|
|
394
|
+
algorithm: algorithmName(sk),
|
|
395
|
+
created: sk.keyPacket.created?.toISOString() ?? null,
|
|
396
|
+
fingerprint: sk.getFingerprint().toUpperCase(),
|
|
397
|
+
usage,
|
|
398
|
+
expires: exp && exp !== Infinity ? new Date(exp).toISOString() : null,
|
|
399
|
+
valid
|
|
400
|
+
};
|
|
401
|
+
}));
|
|
402
|
+
const canEncrypt = await key.getEncryptionKey(void 0, now, void 0, config).then(() => true, () => false);
|
|
403
|
+
return { fingerprint, userIDs, algorithm, created, expires, notations, subkeys, canEncrypt };
|
|
404
|
+
} catch {
|
|
405
|
+
return null;
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
async function verifySignedText(openpgp, publicKey, packets, text) {
|
|
409
|
+
const { LiteralDataPacket } = openpgp;
|
|
410
|
+
const config = pgpConfig(openpgp);
|
|
411
|
+
const now = /* @__PURE__ */ new Date();
|
|
412
|
+
for (const sig of packets) {
|
|
413
|
+
if (!publicKey.getKeys(sig.issuerKeyID).length) continue;
|
|
414
|
+
const signingKey = await publicKey.getSigningKey(sig.issuerKeyID, now, void 0, config);
|
|
415
|
+
const literal = new LiteralDataPacket();
|
|
416
|
+
literal.setText(text.replace(/\r?\n/g, "\r\n"));
|
|
417
|
+
await sig.verify(signingKey.keyPacket, sig.signatureType, literal, now, true, config);
|
|
418
|
+
return true;
|
|
419
|
+
}
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
async function verifyClearsigned({ armoredKey, clearsigned }) {
|
|
423
|
+
try {
|
|
424
|
+
const openpgp = await import("openpgp");
|
|
425
|
+
const publicKey = await readKeyAny(openpgp, armoredKey);
|
|
426
|
+
const message = await openpgp.readCleartextMessage({ cleartextMessage: clearsigned });
|
|
427
|
+
const text = message.text ?? message.getText();
|
|
428
|
+
if (await verifySignedText(openpgp, publicKey, message.signature.packets, text)) return { verified: true, text: message.getText() };
|
|
429
|
+
return { verified: false, reason: "Message is not signed by this key", text: message.getText() };
|
|
430
|
+
} catch (err) {
|
|
431
|
+
return { verified: false, reason: err instanceof Error ? err.message : "Verification failed", text: "" };
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
async function verifyStatementSignature({ key, signature, address }) {
|
|
435
|
+
const text = statementText(address);
|
|
436
|
+
try {
|
|
437
|
+
const openpgp = await import("openpgp");
|
|
438
|
+
const publicKey = await readKeyAny(openpgp, key);
|
|
439
|
+
const { packets } = await readSignatureAny(openpgp, signature);
|
|
440
|
+
if (await verifySignedText(openpgp, publicKey, packets, text)) return { verified: true, text };
|
|
441
|
+
return { verified: false, reason: "Message is not signed by this key", text };
|
|
442
|
+
} catch (err) {
|
|
443
|
+
return { verified: false, reason: err instanceof Error ? err.message : "Verification failed", text };
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
var REVOCATION_REASON = {
|
|
447
|
+
1: "replaced by a newer key",
|
|
448
|
+
2: "compromised",
|
|
449
|
+
3: "no longer used",
|
|
450
|
+
32: "a user ID is no longer valid"
|
|
451
|
+
};
|
|
452
|
+
var iso = (d) => d instanceof Date ? d.toISOString() : typeof d === "number" && Number.isFinite(d) ? new Date(d).toISOString() : null;
|
|
453
|
+
function revocationOf(sigs) {
|
|
454
|
+
const sig = [...sigs ?? []].sort((a, b) => (b.created?.getTime?.() ?? 0) - (a.created?.getTime?.() ?? 0))[0];
|
|
455
|
+
const flag = sig?.reasonForRevocationFlag ?? null;
|
|
456
|
+
return {
|
|
457
|
+
at: iso(sig?.created),
|
|
458
|
+
compromised: flag === 2,
|
|
459
|
+
revocationReason: sig?.reasonForRevocationString || (flag !== null ? REVOCATION_REASON[flag] ?? null : null)
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
async function keyLife(key, issuer, config) {
|
|
463
|
+
const now = /* @__PURE__ */ new Date();
|
|
464
|
+
const signer = issuer && !key.getKeyID().equals(issuer) ? key.subkeys.find((s) => s.getKeyID().equals(issuer)) ?? null : key;
|
|
465
|
+
const signingKey = signer ? signer.getFingerprint().toUpperCase() : null;
|
|
466
|
+
const safe = async (f, fallback) => {
|
|
467
|
+
try {
|
|
468
|
+
return await f();
|
|
469
|
+
} catch {
|
|
470
|
+
return fallback;
|
|
471
|
+
}
|
|
472
|
+
};
|
|
473
|
+
const keyRevoked = await safe(() => key.isRevoked(void 0, void 0, now, config), false);
|
|
474
|
+
const keyExp = await safe(() => key.getExpirationTime(void 0, config), null);
|
|
475
|
+
const isSub = signer && signer !== key;
|
|
476
|
+
const subRevoked = isSub ? await safe(() => signer.isRevoked(null, key.keyPacket, now, config), false) : false;
|
|
477
|
+
const subExp = isSub ? await safe(() => signer.getExpirationTime(null, config), null) : null;
|
|
478
|
+
const finite = (d) => d instanceof Date && Number.isFinite(d.getTime()) ? d : null;
|
|
479
|
+
const kExp = finite(keyExp), sExp = finite(subExp);
|
|
480
|
+
let problem = null;
|
|
481
|
+
if (keyRevoked) {
|
|
482
|
+
const r = revocationOf(key.revocationSignatures);
|
|
483
|
+
problem = { kind: r.compromised ? "compromised" : "revoked", at: r.at, revocationReason: r.revocationReason };
|
|
484
|
+
} else if (kExp && kExp <= now) {
|
|
485
|
+
problem = { kind: "expired", at: kExp.toISOString() };
|
|
486
|
+
} else if (subRevoked) {
|
|
487
|
+
const r = revocationOf(signer.revocationSignatures);
|
|
488
|
+
problem = { kind: r.compromised ? "compromised" : "signing-key-revoked", at: r.at, revocationReason: r.revocationReason };
|
|
489
|
+
} else if (sExp && sExp <= now) {
|
|
490
|
+
problem = { kind: "signing-key-expired", at: sExp.toISOString() };
|
|
491
|
+
}
|
|
492
|
+
const earliest = [kExp, sExp].filter(Boolean).sort((a, b) => a - b)[0];
|
|
493
|
+
return { problem, signingKey, expiresAt: earliest ? earliest.toISOString() : null, algorithm: algorithmName(signer ?? key) };
|
|
494
|
+
}
|
|
495
|
+
async function verifyAttestation({
|
|
496
|
+
pgpPublicKey,
|
|
497
|
+
pgpSignature,
|
|
498
|
+
fingerprint,
|
|
499
|
+
ethAddress
|
|
500
|
+
}) {
|
|
501
|
+
try {
|
|
502
|
+
if (!pgpPublicKey || !pgpSignature) {
|
|
503
|
+
return { verified: false, reason: "Missing PGP data", kind: "bad-signature" };
|
|
504
|
+
}
|
|
505
|
+
const openpgp = await import("openpgp");
|
|
506
|
+
const publicKey = await readKeyAny(openpgp, pgpPublicKey);
|
|
507
|
+
const keyFingerprint = publicKey.getFingerprint().toUpperCase();
|
|
508
|
+
if (keyFingerprint !== fingerprint.toUpperCase()) {
|
|
509
|
+
return { verified: false, reason: "Key fingerprint mismatch", kind: "bad-signature" };
|
|
510
|
+
}
|
|
511
|
+
let sig;
|
|
512
|
+
try {
|
|
513
|
+
sig = await readSignatureAny(openpgp, pgpSignature);
|
|
514
|
+
} catch {
|
|
515
|
+
return { verified: false, reason: "Unreadable signature", kind: "bad-signature" };
|
|
516
|
+
}
|
|
517
|
+
const life = await keyLife(publicKey, sig.packets[0]?.issuerKeyID ?? null, pgpConfig(openpgp));
|
|
518
|
+
const v = sig.text !== null ? await verifyClearsigned({ armoredKey: publicKey.armor(), clearsigned: normalizeInput(pgpSignature) }) : await verifyStatementSignature({ key: publicKey.armor(), signature: pgpSignature, address: ethAddress });
|
|
519
|
+
if (!v.verified) {
|
|
520
|
+
if (life.problem) return { verified: false, reason: v.reason, signingKey: life.signingKey, ...life.problem };
|
|
521
|
+
if (/too weak|reject|unsupported|not supported|unknown (curve|algorithm)/i.test(v.reason || "")) {
|
|
522
|
+
return { verified: false, reason: v.reason, kind: "unsupported", algorithm: life.algorithm, signingKey: life.signingKey };
|
|
523
|
+
}
|
|
524
|
+
return { verified: false, reason: v.reason, kind: "bad-signature", signingKey: life.signingKey };
|
|
525
|
+
}
|
|
526
|
+
if (v.text.trim().toLowerCase() !== statementText(ethAddress).toLowerCase()) {
|
|
527
|
+
return { verified: false, reason: "Signed text is not the statement for this address", kind: "bad-signature", signingKey: life.signingKey };
|
|
528
|
+
}
|
|
529
|
+
return { verified: true, kind: "verified", signingKey: life.signingKey, expiresAt: life.expiresAt };
|
|
530
|
+
} catch {
|
|
531
|
+
return { verified: false, reason: "Signature verification failed", kind: "bad-signature" };
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
var EMAIL_UID = /@/;
|
|
535
|
+
var EMAIL_IN_TEXT = /(?:^|[^\w/.+-])([\w.+-]+@[\w-]+(?:\.[\w-]+)+)/;
|
|
536
|
+
function notationEmails(sig) {
|
|
537
|
+
const out = [];
|
|
538
|
+
for (const n of sig?.rawNotations ?? []) {
|
|
539
|
+
const text = typeof n.value === "string" ? n.value : new TextDecoder().decode(n.value);
|
|
540
|
+
const m = text.match(EMAIL_IN_TEXT);
|
|
541
|
+
if (m) out.push(m[1]);
|
|
542
|
+
}
|
|
543
|
+
return out;
|
|
544
|
+
}
|
|
545
|
+
async function stripEmailUserIDs(armoredKey) {
|
|
546
|
+
try {
|
|
547
|
+
const openpgp = await import("openpgp");
|
|
548
|
+
const key = await readKeyAny(openpgp, armoredKey);
|
|
549
|
+
const kept = [];
|
|
550
|
+
const removed = [];
|
|
551
|
+
const users = key.users.filter((u) => {
|
|
552
|
+
const uid = u.userID?.userID;
|
|
553
|
+
if (!uid) return false;
|
|
554
|
+
if (EMAIL_UID.test(uid)) {
|
|
555
|
+
removed.push(uid);
|
|
556
|
+
return false;
|
|
557
|
+
}
|
|
558
|
+
kept.push(uid);
|
|
559
|
+
return true;
|
|
560
|
+
});
|
|
561
|
+
if (users.length === 0) return null;
|
|
562
|
+
key.users = users;
|
|
563
|
+
return { armored: key.armor(), binary: key.write(), kept, removed };
|
|
564
|
+
} catch {
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
async function hasEmailUserID(armoredKey) {
|
|
569
|
+
try {
|
|
570
|
+
const openpgp = await import("openpgp");
|
|
571
|
+
const key = await readKeyAny(openpgp, armoredKey);
|
|
572
|
+
return key.users.some((u) => EMAIL_UID.test(u.userID?.userID ?? ""));
|
|
573
|
+
} catch {
|
|
574
|
+
return false;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
async function leanKey(input, { includeEmail = false } = {}) {
|
|
578
|
+
try {
|
|
579
|
+
const openpgp = await import("openpgp");
|
|
580
|
+
const key = await readKeyAny(openpgp, input);
|
|
581
|
+
const kept = [];
|
|
582
|
+
const removed = [];
|
|
583
|
+
const newest = (sigs = []) => sigs.length ? [[...sigs].sort((x, y) => (y.created?.getTime?.() ?? 0) - (x.created?.getTime?.() ?? 0))[0]] : [];
|
|
584
|
+
for (const u of key.users) {
|
|
585
|
+
u.selfCertifications = u.revocationSignatures?.length ? [] : newest(u.selfCertifications);
|
|
586
|
+
u.otherCertifications = [];
|
|
587
|
+
}
|
|
588
|
+
const users = key.users.filter((u) => {
|
|
589
|
+
const uid = u.userID?.userID;
|
|
590
|
+
if (!uid) return false;
|
|
591
|
+
if (!includeEmail && (EMAIL_UID.test(uid) || u.selfCertifications.some((c) => notationEmails(c).length))) {
|
|
592
|
+
removed.push(uid);
|
|
593
|
+
return false;
|
|
594
|
+
}
|
|
595
|
+
kept.push(uid);
|
|
596
|
+
return true;
|
|
597
|
+
});
|
|
598
|
+
if (users.length === 0) return null;
|
|
599
|
+
key.users = users;
|
|
600
|
+
key.directSignatures = newest(key.directSignatures);
|
|
601
|
+
const keyNotationEmails = includeEmail ? [] : key.directSignatures.flatMap(notationEmails);
|
|
602
|
+
for (const sk of key.subkeys) sk.bindingSignatures = newest(sk.bindingSignatures);
|
|
603
|
+
return { binary: key.write(), kept, removed, keyNotationEmails };
|
|
604
|
+
} catch {
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
async function claimSignature({ signature, key, address }) {
|
|
609
|
+
try {
|
|
610
|
+
const openpgp = await import("openpgp");
|
|
611
|
+
const sig = await readSignatureAny(openpgp, signature);
|
|
612
|
+
if (sig.text === null) {
|
|
613
|
+
const v = normalizeInput(signature);
|
|
614
|
+
const raw = typeof v === "string" ? (await openpgp.unarmor(v)).data : v;
|
|
615
|
+
return { signature: raw, messageVersion: 1 };
|
|
616
|
+
}
|
|
617
|
+
const packet = sig.signature.write();
|
|
618
|
+
if ((await verifyStatementSignature({ key, signature: packet, address })).verified) return { signature: packet, messageVersion: 1 };
|
|
619
|
+
return { signature: normalizeInput(signature), messageVersion: 0 };
|
|
620
|
+
} catch {
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
async function signatureEmail(signature) {
|
|
625
|
+
try {
|
|
626
|
+
const openpgp = await import("openpgp");
|
|
627
|
+
const { packets } = await readSignatureAny(openpgp, signature);
|
|
628
|
+
for (const p of packets) {
|
|
629
|
+
const uid = p.signersUserID ?? null;
|
|
630
|
+
if (uid && uid.includes("@")) return uid;
|
|
631
|
+
}
|
|
632
|
+
return null;
|
|
633
|
+
} catch {
|
|
634
|
+
return null;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
async function payloadText(input, kind) {
|
|
638
|
+
try {
|
|
639
|
+
const v = normalizeInput(input);
|
|
640
|
+
if (typeof v === "string") return v;
|
|
641
|
+
const openpgp = await import("openpgp");
|
|
642
|
+
if (kind === "key") return (await openpgp.readKey({ binaryKey: v })).armor();
|
|
643
|
+
return (await openpgp.readSignature({ binarySignature: v })).armor();
|
|
644
|
+
} catch {
|
|
645
|
+
return null;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
var AUTH_FLAG = 32;
|
|
649
|
+
function sshString(bytes) {
|
|
650
|
+
const out = new Uint8Array(4 + bytes.length);
|
|
651
|
+
new DataView(out.buffer).setUint32(0, bytes.length);
|
|
652
|
+
out.set(bytes, 4);
|
|
653
|
+
return out;
|
|
654
|
+
}
|
|
655
|
+
function sshMpint(bytes) {
|
|
656
|
+
let i = 0;
|
|
657
|
+
while (i < bytes.length - 1 && bytes[i] === 0) i++;
|
|
658
|
+
const b = bytes.subarray(i);
|
|
659
|
+
return sshString(b[0] & 128 ? new Uint8Array([0, ...b]) : b);
|
|
660
|
+
}
|
|
661
|
+
function sshBlob(p) {
|
|
662
|
+
const text = (s) => new TextEncoder().encode(s);
|
|
663
|
+
const join = (...parts) => {
|
|
664
|
+
const out = new Uint8Array(parts.reduce((n, x) => n + x.length, 0));
|
|
665
|
+
let o = 0;
|
|
666
|
+
for (const x of parts) {
|
|
667
|
+
out.set(x, o);
|
|
668
|
+
o += x.length;
|
|
669
|
+
}
|
|
670
|
+
return out;
|
|
671
|
+
};
|
|
672
|
+
const { publicParams: pp } = p;
|
|
673
|
+
switch (p.algorithm) {
|
|
674
|
+
case 1:
|
|
675
|
+
case 3:
|
|
676
|
+
return { type: "ssh-rsa", blob: join(sshString(text("ssh-rsa")), sshMpint(pp.e), sshMpint(pp.n)) };
|
|
677
|
+
case 22:
|
|
678
|
+
if (pp.oid?.getName?.() !== "ed25519Legacy" || pp.Q?.length !== 33) return null;
|
|
679
|
+
return { type: "ssh-ed25519", blob: join(sshString(text("ssh-ed25519")), sshString(pp.Q.subarray(1))) };
|
|
680
|
+
case 27:
|
|
681
|
+
return { type: "ssh-ed25519", blob: join(sshString(text("ssh-ed25519")), sshString(pp.A)) };
|
|
682
|
+
case 19: {
|
|
683
|
+
const curve = { nistP256: "nistp256", nistP384: "nistp384", nistP521: "nistp521" }[pp.oid?.getName?.()];
|
|
684
|
+
if (!curve) return null;
|
|
685
|
+
const type = `ecdsa-sha2-${curve}`;
|
|
686
|
+
return { type, blob: join(sshString(text(type)), sshString(text(curve)), sshString(pp.Q)) };
|
|
687
|
+
}
|
|
688
|
+
default:
|
|
689
|
+
return null;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
function base64(bytes) {
|
|
693
|
+
let s = "";
|
|
694
|
+
for (const b of bytes) s += String.fromCharCode(b);
|
|
695
|
+
return btoa(s);
|
|
696
|
+
}
|
|
697
|
+
async function sshKeys(input) {
|
|
698
|
+
try {
|
|
699
|
+
const openpgp = await import("openpgp");
|
|
700
|
+
const config = pgpConfig(openpgp);
|
|
701
|
+
const key = await readKeyAny(openpgp, input);
|
|
702
|
+
const now = /* @__PURE__ */ new Date();
|
|
703
|
+
try {
|
|
704
|
+
await key.verifyPrimaryKey(now, void 0, config);
|
|
705
|
+
} catch {
|
|
706
|
+
return [];
|
|
707
|
+
}
|
|
708
|
+
const candidates = [];
|
|
709
|
+
const direct = newestSignature(key.directSignatures);
|
|
710
|
+
const primarySig = direct?.keyFlags ? direct : (await key.getPrimaryUser(now, void 0, config)).selfCertification;
|
|
711
|
+
candidates.push({ packet: key.keyPacket, flags: primarySig?.keyFlags?.[0] ?? 0 });
|
|
712
|
+
for (const sk of key.subkeys) {
|
|
713
|
+
try {
|
|
714
|
+
await sk.verify(now, config);
|
|
715
|
+
} catch {
|
|
716
|
+
continue;
|
|
717
|
+
}
|
|
718
|
+
candidates.push({ packet: sk.keyPacket, flags: newestSignature(sk.bindingSignatures)?.keyFlags?.[0] ?? 0 });
|
|
719
|
+
}
|
|
720
|
+
const out = [];
|
|
721
|
+
for (const { packet, flags } of candidates) {
|
|
722
|
+
if (!(flags & AUTH_FLAG)) continue;
|
|
723
|
+
const ssh = sshBlob(packet);
|
|
724
|
+
if (!ssh) continue;
|
|
725
|
+
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", ssh.blob));
|
|
726
|
+
out.push({
|
|
727
|
+
line: `${ssh.type} ${base64(ssh.blob)}`,
|
|
728
|
+
sha256: `SHA256:${base64(digest).replace(/=+$/, "")}`,
|
|
729
|
+
fingerprint: packet.getFingerprint().toUpperCase()
|
|
730
|
+
});
|
|
731
|
+
}
|
|
732
|
+
return out;
|
|
733
|
+
} catch {
|
|
734
|
+
return [];
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
|
|
738
|
+
// src/core/authorization.ts
|
|
739
|
+
import { hashTypedData, keccak256 as keccak2562, stringToHex, recoverTypedDataAddress, isErc6492Signature } from "viem";
|
|
740
|
+
var EIP712_NAME = "Thurin PGPRegistry";
|
|
741
|
+
var EIP712_VERSION = "3";
|
|
742
|
+
function registryDomain(chainId, verifyingContract) {
|
|
743
|
+
return { name: EIP712_NAME, version: EIP712_VERSION, chainId, verifyingContract };
|
|
744
|
+
}
|
|
745
|
+
var AUTHORIZATION_TYPES = {
|
|
746
|
+
Attest: [
|
|
747
|
+
{ name: "owner", type: "address" },
|
|
748
|
+
{ name: "fingerprint", type: "bytes" },
|
|
749
|
+
{ name: "signature", type: "bytes" },
|
|
750
|
+
{ name: "key", type: "bytes" },
|
|
751
|
+
{ name: "nonce", type: "uint256" },
|
|
752
|
+
{ name: "deadline", type: "uint256" }
|
|
753
|
+
],
|
|
754
|
+
Reattest: [
|
|
755
|
+
{ name: "owner", type: "address" },
|
|
756
|
+
{ name: "revokeIndex", type: "uint256" },
|
|
757
|
+
{ name: "fingerprint", type: "bytes" },
|
|
758
|
+
{ name: "signature", type: "bytes" },
|
|
759
|
+
{ name: "key", type: "bytes" },
|
|
760
|
+
{ name: "keepRecords", type: "bool" },
|
|
761
|
+
{ name: "nonce", type: "uint256" },
|
|
762
|
+
{ name: "deadline", type: "uint256" }
|
|
763
|
+
],
|
|
764
|
+
UpdateKey: [
|
|
765
|
+
{ name: "owner", type: "address" },
|
|
766
|
+
{ name: "index", type: "uint256" },
|
|
767
|
+
{ name: "key", type: "bytes" },
|
|
768
|
+
{ name: "nonce", type: "uint256" },
|
|
769
|
+
{ name: "deadline", type: "uint256" }
|
|
770
|
+
],
|
|
771
|
+
Revoke: [
|
|
772
|
+
{ name: "owner", type: "address" },
|
|
773
|
+
{ name: "index", type: "uint256" },
|
|
774
|
+
{ name: "reason", type: "string" },
|
|
775
|
+
{ name: "nonce", type: "uint256" },
|
|
776
|
+
{ name: "deadline", type: "uint256" }
|
|
777
|
+
],
|
|
778
|
+
SetRecord: [
|
|
779
|
+
{ name: "owner", type: "address" },
|
|
780
|
+
{ name: "index", type: "uint256" },
|
|
781
|
+
{ name: "kind", type: "string" },
|
|
782
|
+
{ name: "value", type: "string" },
|
|
783
|
+
{ name: "nonce", type: "uint256" },
|
|
784
|
+
{ name: "deadline", type: "uint256" }
|
|
785
|
+
],
|
|
786
|
+
MarkCompromised: [
|
|
787
|
+
{ name: "owner", type: "address" },
|
|
788
|
+
{ name: "index", type: "uint256" },
|
|
789
|
+
{ name: "nonce", type: "uint256" },
|
|
790
|
+
{ name: "deadline", type: "uint256" }
|
|
791
|
+
]
|
|
792
|
+
};
|
|
793
|
+
var REVOKE_REASONS = ["", "compromised", "retired", "superseded", "other"];
|
|
794
|
+
var OWNER_REVOKE_REASONS = ["", "compromised", "retired", "other"];
|
|
795
|
+
function payloadBytes(v) {
|
|
796
|
+
return /^0x([0-9a-fA-F]{2})*$/.test(v) ? v : stringToHex(v);
|
|
797
|
+
}
|
|
798
|
+
function attestTypedData(chainId, registry, a) {
|
|
799
|
+
return {
|
|
800
|
+
domain: registryDomain(chainId, registry),
|
|
801
|
+
types: { Attest: AUTHORIZATION_TYPES.Attest },
|
|
802
|
+
primaryType: "Attest",
|
|
803
|
+
message: {
|
|
804
|
+
owner: a.owner,
|
|
805
|
+
fingerprint: fingerprintToBytes(a.fingerprint),
|
|
806
|
+
signature: payloadBytes(a.signature),
|
|
807
|
+
key: payloadBytes(a.key),
|
|
808
|
+
nonce: a.nonce,
|
|
809
|
+
deadline: a.deadline
|
|
810
|
+
}
|
|
811
|
+
};
|
|
812
|
+
}
|
|
813
|
+
function reattestTypedData(chainId, registry, a) {
|
|
814
|
+
return {
|
|
815
|
+
domain: registryDomain(chainId, registry),
|
|
816
|
+
types: { Reattest: AUTHORIZATION_TYPES.Reattest },
|
|
817
|
+
primaryType: "Reattest",
|
|
818
|
+
message: {
|
|
819
|
+
owner: a.owner,
|
|
820
|
+
revokeIndex: a.revokeIndex,
|
|
821
|
+
fingerprint: fingerprintToBytes(a.fingerprint),
|
|
822
|
+
signature: payloadBytes(a.signature),
|
|
823
|
+
key: payloadBytes(a.key),
|
|
824
|
+
keepRecords: a.keepRecords,
|
|
825
|
+
nonce: a.nonce,
|
|
826
|
+
deadline: a.deadline
|
|
827
|
+
}
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
function updateKeyTypedData(chainId, registry, a) {
|
|
831
|
+
return {
|
|
832
|
+
domain: registryDomain(chainId, registry),
|
|
833
|
+
types: { UpdateKey: AUTHORIZATION_TYPES.UpdateKey },
|
|
834
|
+
primaryType: "UpdateKey",
|
|
835
|
+
message: { owner: a.owner, index: a.index, key: payloadBytes(a.key), nonce: a.nonce, deadline: a.deadline }
|
|
836
|
+
};
|
|
837
|
+
}
|
|
838
|
+
function revokeTypedData(chainId, registry, a) {
|
|
839
|
+
return {
|
|
840
|
+
domain: registryDomain(chainId, registry),
|
|
841
|
+
types: { Revoke: AUTHORIZATION_TYPES.Revoke },
|
|
842
|
+
primaryType: "Revoke",
|
|
843
|
+
message: { owner: a.owner, index: a.index, reason: a.reason ?? "", nonce: a.nonce, deadline: a.deadline }
|
|
844
|
+
};
|
|
845
|
+
}
|
|
846
|
+
function setRecordTypedData(chainId, registry, a) {
|
|
847
|
+
return {
|
|
848
|
+
domain: registryDomain(chainId, registry),
|
|
849
|
+
types: { SetRecord: AUTHORIZATION_TYPES.SetRecord },
|
|
850
|
+
primaryType: "SetRecord",
|
|
851
|
+
message: { owner: a.owner, index: a.index, kind: a.kind, value: a.value, nonce: a.nonce, deadline: a.deadline }
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
function markCompromisedTypedData(chainId, registry, a) {
|
|
855
|
+
return {
|
|
856
|
+
domain: registryDomain(chainId, registry),
|
|
857
|
+
types: { MarkCompromised: AUTHORIZATION_TYPES.MarkCompromised },
|
|
858
|
+
primaryType: "MarkCompromised",
|
|
859
|
+
message: { owner: a.owner, index: a.index, nonce: a.nonce, deadline: a.deadline }
|
|
860
|
+
};
|
|
861
|
+
}
|
|
862
|
+
function authorizationDigest(typedData) {
|
|
863
|
+
return hashTypedData(typedData);
|
|
864
|
+
}
|
|
865
|
+
var ERC1271_MAGIC = "0x1626ba7e";
|
|
866
|
+
var ERC1271_ABI = [{
|
|
867
|
+
type: "function",
|
|
868
|
+
name: "isValidSignature",
|
|
869
|
+
stateMutability: "view",
|
|
870
|
+
inputs: [{ name: "hash", type: "bytes32" }, { name: "signature", type: "bytes" }],
|
|
871
|
+
outputs: [{ name: "", type: "bytes4" }]
|
|
872
|
+
}];
|
|
873
|
+
async function permissionSigned(client, typedData, signature, owner) {
|
|
874
|
+
const code = await client.getCode({ address: owner }).catch(() => void 0);
|
|
875
|
+
const deployed = !!code && code !== "0x";
|
|
876
|
+
if (isErc6492Signature(signature)) {
|
|
877
|
+
return { ok: false, reason: deployed ? "This signature was made before the account was on-chain. Sign again, now that it is" : "This smart account isn't on-chain yet, so the registry can't check its signature. Send any transaction from it first (that creates it), then sign again" };
|
|
878
|
+
}
|
|
879
|
+
const signer = await recoverTypedDataAddress({ ...typedData, signature }).catch(() => void 0);
|
|
880
|
+
if (signer && signer.toLowerCase() === owner.toLowerCase()) return { ok: true };
|
|
881
|
+
if (deployed) {
|
|
882
|
+
const answer = await client.readContract({
|
|
883
|
+
address: owner,
|
|
884
|
+
abi: ERC1271_ABI,
|
|
885
|
+
functionName: "isValidSignature",
|
|
886
|
+
args: [authorizationDigest(typedData), signature]
|
|
887
|
+
}).catch(() => void 0);
|
|
888
|
+
if (answer === ERC1271_MAGIC) return { ok: true };
|
|
889
|
+
return { ok: false, reason: `The account ${owner} doesn't accept this signature as its own` };
|
|
890
|
+
}
|
|
891
|
+
return { ok: false, reason: signer ? `The signature is from ${signer}, not ${owner}` : "The signature can't be read", signer };
|
|
892
|
+
}
|
|
893
|
+
function recordKind(name) {
|
|
894
|
+
return keccak2562(stringToHex(name));
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
// src/core/contract.ts
|
|
898
|
+
import { mainnet, sepolia, foundry } from "viem/chains";
|
|
899
|
+
|
|
900
|
+
// src/core/abi.ts
|
|
901
|
+
var REGISTRY_ABI = [
|
|
902
|
+
{
|
|
903
|
+
"type": "function",
|
|
904
|
+
"name": "ATTEST_TYPEHASH",
|
|
905
|
+
"inputs": [],
|
|
906
|
+
"outputs": [
|
|
907
|
+
{
|
|
908
|
+
"name": "",
|
|
216
909
|
"type": "bytes32",
|
|
217
910
|
"internalType": "bytes32"
|
|
218
|
-
}
|
|
911
|
+
}
|
|
912
|
+
],
|
|
913
|
+
"stateMutability": "view"
|
|
914
|
+
},
|
|
915
|
+
{
|
|
916
|
+
"type": "function",
|
|
917
|
+
"name": "DOMAIN_SEPARATOR",
|
|
918
|
+
"inputs": [],
|
|
919
|
+
"outputs": [
|
|
920
|
+
{
|
|
921
|
+
"name": "",
|
|
922
|
+
"type": "bytes32",
|
|
923
|
+
"internalType": "bytes32"
|
|
924
|
+
}
|
|
925
|
+
],
|
|
926
|
+
"stateMutability": "view"
|
|
927
|
+
},
|
|
928
|
+
{
|
|
929
|
+
"type": "function",
|
|
930
|
+
"name": "EIP712_NAME",
|
|
931
|
+
"inputs": [],
|
|
932
|
+
"outputs": [
|
|
933
|
+
{
|
|
934
|
+
"name": "",
|
|
935
|
+
"type": "string",
|
|
936
|
+
"internalType": "string"
|
|
937
|
+
}
|
|
938
|
+
],
|
|
939
|
+
"stateMutability": "view"
|
|
940
|
+
},
|
|
941
|
+
{
|
|
942
|
+
"type": "function",
|
|
943
|
+
"name": "EIP712_VERSION",
|
|
944
|
+
"inputs": [],
|
|
945
|
+
"outputs": [
|
|
946
|
+
{
|
|
947
|
+
"name": "",
|
|
948
|
+
"type": "string",
|
|
949
|
+
"internalType": "string"
|
|
950
|
+
}
|
|
951
|
+
],
|
|
952
|
+
"stateMutability": "view"
|
|
953
|
+
},
|
|
954
|
+
{
|
|
955
|
+
"type": "function",
|
|
956
|
+
"name": "EPOCH",
|
|
957
|
+
"inputs": [],
|
|
958
|
+
"outputs": [
|
|
959
|
+
{
|
|
960
|
+
"name": "",
|
|
961
|
+
"type": "uint256",
|
|
962
|
+
"internalType": "uint256"
|
|
963
|
+
}
|
|
964
|
+
],
|
|
965
|
+
"stateMutability": "view"
|
|
966
|
+
},
|
|
967
|
+
{
|
|
968
|
+
"type": "function",
|
|
969
|
+
"name": "MARK_COMPROMISED_TYPEHASH",
|
|
970
|
+
"inputs": [],
|
|
971
|
+
"outputs": [
|
|
972
|
+
{
|
|
973
|
+
"name": "",
|
|
974
|
+
"type": "bytes32",
|
|
975
|
+
"internalType": "bytes32"
|
|
976
|
+
}
|
|
977
|
+
],
|
|
978
|
+
"stateMutability": "view"
|
|
979
|
+
},
|
|
980
|
+
{
|
|
981
|
+
"type": "function",
|
|
982
|
+
"name": "MAX_CLAIMS_PER_OWNER",
|
|
983
|
+
"inputs": [],
|
|
984
|
+
"outputs": [
|
|
219
985
|
{
|
|
220
|
-
"name": "
|
|
986
|
+
"name": "",
|
|
987
|
+
"type": "uint256",
|
|
988
|
+
"internalType": "uint256"
|
|
989
|
+
}
|
|
990
|
+
],
|
|
991
|
+
"stateMutability": "view"
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
"type": "function",
|
|
995
|
+
"name": "MAX_KEY_BYTES",
|
|
996
|
+
"inputs": [],
|
|
997
|
+
"outputs": [
|
|
998
|
+
{
|
|
999
|
+
"name": "",
|
|
1000
|
+
"type": "uint256",
|
|
1001
|
+
"internalType": "uint256"
|
|
1002
|
+
}
|
|
1003
|
+
],
|
|
1004
|
+
"stateMutability": "view"
|
|
1005
|
+
},
|
|
1006
|
+
{
|
|
1007
|
+
"type": "function",
|
|
1008
|
+
"name": "MAX_KIND_BYTES",
|
|
1009
|
+
"inputs": [],
|
|
1010
|
+
"outputs": [
|
|
1011
|
+
{
|
|
1012
|
+
"name": "",
|
|
1013
|
+
"type": "uint256",
|
|
1014
|
+
"internalType": "uint256"
|
|
1015
|
+
}
|
|
1016
|
+
],
|
|
1017
|
+
"stateMutability": "view"
|
|
1018
|
+
},
|
|
1019
|
+
{
|
|
1020
|
+
"type": "function",
|
|
1021
|
+
"name": "MAX_PAYLOAD_BYTES",
|
|
1022
|
+
"inputs": [],
|
|
1023
|
+
"outputs": [
|
|
1024
|
+
{
|
|
1025
|
+
"name": "",
|
|
1026
|
+
"type": "uint256",
|
|
1027
|
+
"internalType": "uint256"
|
|
1028
|
+
}
|
|
1029
|
+
],
|
|
1030
|
+
"stateMutability": "view"
|
|
1031
|
+
},
|
|
1032
|
+
{
|
|
1033
|
+
"type": "function",
|
|
1034
|
+
"name": "MAX_RECORD_BYTES",
|
|
1035
|
+
"inputs": [],
|
|
1036
|
+
"outputs": [
|
|
1037
|
+
{
|
|
1038
|
+
"name": "",
|
|
1039
|
+
"type": "uint256",
|
|
1040
|
+
"internalType": "uint256"
|
|
1041
|
+
}
|
|
1042
|
+
],
|
|
1043
|
+
"stateMutability": "view"
|
|
1044
|
+
},
|
|
1045
|
+
{
|
|
1046
|
+
"type": "function",
|
|
1047
|
+
"name": "MAX_SIGNATURE_BYTES",
|
|
1048
|
+
"inputs": [],
|
|
1049
|
+
"outputs": [
|
|
1050
|
+
{
|
|
1051
|
+
"name": "",
|
|
221
1052
|
"type": "uint256",
|
|
222
1053
|
"internalType": "uint256"
|
|
1054
|
+
}
|
|
1055
|
+
],
|
|
1056
|
+
"stateMutability": "view"
|
|
1057
|
+
},
|
|
1058
|
+
{
|
|
1059
|
+
"type": "function",
|
|
1060
|
+
"name": "MESSAGE_CLEARSIGNED",
|
|
1061
|
+
"inputs": [],
|
|
1062
|
+
"outputs": [
|
|
1063
|
+
{
|
|
1064
|
+
"name": "",
|
|
1065
|
+
"type": "uint8",
|
|
1066
|
+
"internalType": "uint8"
|
|
1067
|
+
}
|
|
1068
|
+
],
|
|
1069
|
+
"stateMutability": "view"
|
|
1070
|
+
},
|
|
1071
|
+
{
|
|
1072
|
+
"type": "function",
|
|
1073
|
+
"name": "MESSAGE_DETACHED",
|
|
1074
|
+
"inputs": [],
|
|
1075
|
+
"outputs": [
|
|
1076
|
+
{
|
|
1077
|
+
"name": "",
|
|
1078
|
+
"type": "uint8",
|
|
1079
|
+
"internalType": "uint8"
|
|
1080
|
+
}
|
|
1081
|
+
],
|
|
1082
|
+
"stateMutability": "view"
|
|
1083
|
+
},
|
|
1084
|
+
{
|
|
1085
|
+
"type": "function",
|
|
1086
|
+
"name": "REATTEST_TYPEHASH",
|
|
1087
|
+
"inputs": [],
|
|
1088
|
+
"outputs": [
|
|
1089
|
+
{
|
|
1090
|
+
"name": "",
|
|
1091
|
+
"type": "bytes32",
|
|
1092
|
+
"internalType": "bytes32"
|
|
1093
|
+
}
|
|
1094
|
+
],
|
|
1095
|
+
"stateMutability": "view"
|
|
1096
|
+
},
|
|
1097
|
+
{
|
|
1098
|
+
"type": "function",
|
|
1099
|
+
"name": "REVOKE_TYPEHASH",
|
|
1100
|
+
"inputs": [],
|
|
1101
|
+
"outputs": [
|
|
1102
|
+
{
|
|
1103
|
+
"name": "",
|
|
1104
|
+
"type": "bytes32",
|
|
1105
|
+
"internalType": "bytes32"
|
|
1106
|
+
}
|
|
1107
|
+
],
|
|
1108
|
+
"stateMutability": "view"
|
|
1109
|
+
},
|
|
1110
|
+
{
|
|
1111
|
+
"type": "function",
|
|
1112
|
+
"name": "SET_RECORD_TYPEHASH",
|
|
1113
|
+
"inputs": [],
|
|
1114
|
+
"outputs": [
|
|
1115
|
+
{
|
|
1116
|
+
"name": "",
|
|
1117
|
+
"type": "bytes32",
|
|
1118
|
+
"internalType": "bytes32"
|
|
1119
|
+
}
|
|
1120
|
+
],
|
|
1121
|
+
"stateMutability": "view"
|
|
1122
|
+
},
|
|
1123
|
+
{
|
|
1124
|
+
"type": "function",
|
|
1125
|
+
"name": "UPDATE_KEY_TYPEHASH",
|
|
1126
|
+
"inputs": [],
|
|
1127
|
+
"outputs": [
|
|
1128
|
+
{
|
|
1129
|
+
"name": "",
|
|
1130
|
+
"type": "bytes32",
|
|
1131
|
+
"internalType": "bytes32"
|
|
1132
|
+
}
|
|
1133
|
+
],
|
|
1134
|
+
"stateMutability": "view"
|
|
1135
|
+
},
|
|
1136
|
+
{
|
|
1137
|
+
"type": "function",
|
|
1138
|
+
"name": "VERSION",
|
|
1139
|
+
"inputs": [],
|
|
1140
|
+
"outputs": [
|
|
1141
|
+
{
|
|
1142
|
+
"name": "",
|
|
1143
|
+
"type": "uint8",
|
|
1144
|
+
"internalType": "uint8"
|
|
1145
|
+
}
|
|
1146
|
+
],
|
|
1147
|
+
"stateMutability": "view"
|
|
1148
|
+
},
|
|
1149
|
+
{
|
|
1150
|
+
"type": "function",
|
|
1151
|
+
"name": "armorToBytes",
|
|
1152
|
+
"inputs": [
|
|
1153
|
+
{
|
|
1154
|
+
"name": "armored",
|
|
1155
|
+
"type": "string",
|
|
1156
|
+
"internalType": "string"
|
|
1157
|
+
}
|
|
1158
|
+
],
|
|
1159
|
+
"outputs": [
|
|
1160
|
+
{
|
|
1161
|
+
"name": "",
|
|
1162
|
+
"type": "bytes",
|
|
1163
|
+
"internalType": "bytes"
|
|
1164
|
+
}
|
|
1165
|
+
],
|
|
1166
|
+
"stateMutability": "pure"
|
|
1167
|
+
},
|
|
1168
|
+
{
|
|
1169
|
+
"type": "function",
|
|
1170
|
+
"name": "armoredKey",
|
|
1171
|
+
"inputs": [
|
|
1172
|
+
{
|
|
1173
|
+
"name": "owner",
|
|
1174
|
+
"type": "address",
|
|
1175
|
+
"internalType": "address"
|
|
223
1176
|
},
|
|
224
1177
|
{
|
|
225
|
-
"name": "
|
|
1178
|
+
"name": "index",
|
|
226
1179
|
"type": "uint256",
|
|
227
1180
|
"internalType": "uint256"
|
|
228
1181
|
}
|
|
229
1182
|
],
|
|
230
1183
|
"outputs": [
|
|
231
1184
|
{
|
|
232
|
-
"name": "
|
|
233
|
-
"type": "
|
|
234
|
-
"internalType": "
|
|
1185
|
+
"name": "",
|
|
1186
|
+
"type": "string",
|
|
1187
|
+
"internalType": "string"
|
|
235
1188
|
}
|
|
236
1189
|
],
|
|
237
1190
|
"stateMutability": "view"
|
|
@@ -246,12 +1199,12 @@ var REGISTRY_ABI = [
|
|
|
246
1199
|
"internalType": "bytes"
|
|
247
1200
|
},
|
|
248
1201
|
{
|
|
249
|
-
"name": "
|
|
1202
|
+
"name": "signature",
|
|
250
1203
|
"type": "bytes",
|
|
251
1204
|
"internalType": "bytes"
|
|
252
1205
|
},
|
|
253
1206
|
{
|
|
254
|
-
"name": "
|
|
1207
|
+
"name": "key",
|
|
255
1208
|
"type": "bytes",
|
|
256
1209
|
"internalType": "bytes"
|
|
257
1210
|
}
|
|
@@ -280,38 +1233,126 @@ var REGISTRY_ABI = [
|
|
|
280
1233
|
"internalType": "bytes"
|
|
281
1234
|
},
|
|
282
1235
|
{
|
|
283
|
-
"name": "
|
|
1236
|
+
"name": "signature",
|
|
1237
|
+
"type": "bytes",
|
|
1238
|
+
"internalType": "bytes"
|
|
1239
|
+
},
|
|
1240
|
+
{
|
|
1241
|
+
"name": "key",
|
|
1242
|
+
"type": "bytes",
|
|
1243
|
+
"internalType": "bytes"
|
|
1244
|
+
},
|
|
1245
|
+
{
|
|
1246
|
+
"name": "deadline",
|
|
1247
|
+
"type": "uint256",
|
|
1248
|
+
"internalType": "uint256"
|
|
1249
|
+
},
|
|
1250
|
+
{
|
|
1251
|
+
"name": "permission",
|
|
284
1252
|
"type": "bytes",
|
|
285
1253
|
"internalType": "bytes"
|
|
1254
|
+
}
|
|
1255
|
+
],
|
|
1256
|
+
"outputs": [
|
|
1257
|
+
{
|
|
1258
|
+
"name": "index",
|
|
1259
|
+
"type": "uint256",
|
|
1260
|
+
"internalType": "uint256"
|
|
1261
|
+
}
|
|
1262
|
+
],
|
|
1263
|
+
"stateMutability": "nonpayable"
|
|
1264
|
+
},
|
|
1265
|
+
{
|
|
1266
|
+
"type": "function",
|
|
1267
|
+
"name": "cancelAuthorization",
|
|
1268
|
+
"inputs": [],
|
|
1269
|
+
"outputs": [],
|
|
1270
|
+
"stateMutability": "nonpayable"
|
|
1271
|
+
},
|
|
1272
|
+
{
|
|
1273
|
+
"type": "function",
|
|
1274
|
+
"name": "claim",
|
|
1275
|
+
"inputs": [
|
|
1276
|
+
{
|
|
1277
|
+
"name": "owner",
|
|
1278
|
+
"type": "address",
|
|
1279
|
+
"internalType": "address"
|
|
1280
|
+
},
|
|
1281
|
+
{
|
|
1282
|
+
"name": "index",
|
|
1283
|
+
"type": "uint256",
|
|
1284
|
+
"internalType": "uint256"
|
|
1285
|
+
}
|
|
1286
|
+
],
|
|
1287
|
+
"outputs": [
|
|
1288
|
+
{
|
|
1289
|
+
"name": "details",
|
|
1290
|
+
"type": "tuple",
|
|
1291
|
+
"internalType": "struct PGPRegistry.ClaimView",
|
|
1292
|
+
"components": [
|
|
1293
|
+
{
|
|
1294
|
+
"name": "index",
|
|
1295
|
+
"type": "uint256",
|
|
1296
|
+
"internalType": "uint256"
|
|
1297
|
+
},
|
|
1298
|
+
{
|
|
1299
|
+
"name": "fingerprint",
|
|
1300
|
+
"type": "bytes",
|
|
1301
|
+
"internalType": "bytes"
|
|
1302
|
+
},
|
|
1303
|
+
{
|
|
1304
|
+
"name": "createdAt",
|
|
1305
|
+
"type": "uint64",
|
|
1306
|
+
"internalType": "uint64"
|
|
1307
|
+
},
|
|
1308
|
+
{
|
|
1309
|
+
"name": "revokedAt",
|
|
1310
|
+
"type": "uint64",
|
|
1311
|
+
"internalType": "uint64"
|
|
1312
|
+
},
|
|
1313
|
+
{
|
|
1314
|
+
"name": "state",
|
|
1315
|
+
"type": "string",
|
|
1316
|
+
"internalType": "string"
|
|
1317
|
+
},
|
|
1318
|
+
{
|
|
1319
|
+
"name": "replacedBy",
|
|
1320
|
+
"type": "uint256",
|
|
1321
|
+
"internalType": "uint256"
|
|
1322
|
+
},
|
|
1323
|
+
{
|
|
1324
|
+
"name": "revokeReason",
|
|
1325
|
+
"type": "string",
|
|
1326
|
+
"internalType": "string"
|
|
1327
|
+
},
|
|
1328
|
+
{
|
|
1329
|
+
"name": "messageVersion",
|
|
1330
|
+
"type": "uint8",
|
|
1331
|
+
"internalType": "uint8"
|
|
1332
|
+
}
|
|
1333
|
+
]
|
|
286
1334
|
},
|
|
287
1335
|
{
|
|
288
|
-
"name": "
|
|
289
|
-
"type": "
|
|
290
|
-
"internalType": "
|
|
1336
|
+
"name": "key",
|
|
1337
|
+
"type": "string",
|
|
1338
|
+
"internalType": "string"
|
|
291
1339
|
},
|
|
292
1340
|
{
|
|
293
|
-
"name": "
|
|
294
|
-
"type": "
|
|
295
|
-
"internalType": "
|
|
1341
|
+
"name": "statement",
|
|
1342
|
+
"type": "string",
|
|
1343
|
+
"internalType": "string"
|
|
296
1344
|
},
|
|
297
1345
|
{
|
|
298
|
-
"name": "
|
|
299
|
-
"type": "
|
|
300
|
-
"internalType": "
|
|
301
|
-
}
|
|
302
|
-
],
|
|
303
|
-
"outputs": [
|
|
304
|
-
{
|
|
305
|
-
"name": "index",
|
|
306
|
-
"type": "uint256",
|
|
307
|
-
"internalType": "uint256"
|
|
1346
|
+
"name": "recordKinds",
|
|
1347
|
+
"type": "string[]",
|
|
1348
|
+
"internalType": "string[]"
|
|
308
1349
|
}
|
|
309
1350
|
],
|
|
310
|
-
"stateMutability": "
|
|
1351
|
+
"stateMutability": "view"
|
|
311
1352
|
},
|
|
312
1353
|
{
|
|
313
1354
|
"type": "function",
|
|
314
|
-
"name": "
|
|
1355
|
+
"name": "claimCount",
|
|
315
1356
|
"inputs": [
|
|
316
1357
|
{
|
|
317
1358
|
"name": "owner",
|
|
@@ -330,7 +1371,7 @@ var REGISTRY_ABI = [
|
|
|
330
1371
|
},
|
|
331
1372
|
{
|
|
332
1373
|
"type": "function",
|
|
333
|
-
"name": "
|
|
1374
|
+
"name": "claimsOf",
|
|
334
1375
|
"inputs": [
|
|
335
1376
|
{
|
|
336
1377
|
"name": "owner",
|
|
@@ -340,10 +1381,15 @@ var REGISTRY_ABI = [
|
|
|
340
1381
|
],
|
|
341
1382
|
"outputs": [
|
|
342
1383
|
{
|
|
343
|
-
"name": "",
|
|
1384
|
+
"name": "out",
|
|
344
1385
|
"type": "tuple[]",
|
|
345
|
-
"internalType": "struct PGPRegistry.
|
|
1386
|
+
"internalType": "struct PGPRegistry.ClaimView[]",
|
|
346
1387
|
"components": [
|
|
1388
|
+
{
|
|
1389
|
+
"name": "index",
|
|
1390
|
+
"type": "uint256",
|
|
1391
|
+
"internalType": "uint256"
|
|
1392
|
+
},
|
|
347
1393
|
{
|
|
348
1394
|
"name": "fingerprint",
|
|
349
1395
|
"type": "bytes",
|
|
@@ -360,19 +1406,24 @@ var REGISTRY_ABI = [
|
|
|
360
1406
|
"internalType": "uint64"
|
|
361
1407
|
},
|
|
362
1408
|
{
|
|
363
|
-
"name": "
|
|
364
|
-
"type": "
|
|
365
|
-
"internalType": "
|
|
1409
|
+
"name": "state",
|
|
1410
|
+
"type": "string",
|
|
1411
|
+
"internalType": "string"
|
|
1412
|
+
},
|
|
1413
|
+
{
|
|
1414
|
+
"name": "replacedBy",
|
|
1415
|
+
"type": "uint256",
|
|
1416
|
+
"internalType": "uint256"
|
|
366
1417
|
},
|
|
367
1418
|
{
|
|
368
|
-
"name": "
|
|
369
|
-
"type": "
|
|
370
|
-
"internalType": "
|
|
1419
|
+
"name": "revokeReason",
|
|
1420
|
+
"type": "string",
|
|
1421
|
+
"internalType": "string"
|
|
371
1422
|
},
|
|
372
1423
|
{
|
|
373
|
-
"name": "
|
|
374
|
-
"type": "
|
|
375
|
-
"internalType": "
|
|
1424
|
+
"name": "messageVersion",
|
|
1425
|
+
"type": "uint8",
|
|
1426
|
+
"internalType": "uint8"
|
|
376
1427
|
}
|
|
377
1428
|
]
|
|
378
1429
|
}
|
|
@@ -381,7 +1432,7 @@ var REGISTRY_ABI = [
|
|
|
381
1432
|
},
|
|
382
1433
|
{
|
|
383
1434
|
"type": "function",
|
|
384
|
-
"name": "
|
|
1435
|
+
"name": "claimsOfRange",
|
|
385
1436
|
"inputs": [
|
|
386
1437
|
{
|
|
387
1438
|
"name": "owner",
|
|
@@ -403,8 +1454,13 @@ var REGISTRY_ABI = [
|
|
|
403
1454
|
{
|
|
404
1455
|
"name": "out",
|
|
405
1456
|
"type": "tuple[]",
|
|
406
|
-
"internalType": "struct PGPRegistry.
|
|
1457
|
+
"internalType": "struct PGPRegistry.ClaimView[]",
|
|
407
1458
|
"components": [
|
|
1459
|
+
{
|
|
1460
|
+
"name": "index",
|
|
1461
|
+
"type": "uint256",
|
|
1462
|
+
"internalType": "uint256"
|
|
1463
|
+
},
|
|
408
1464
|
{
|
|
409
1465
|
"name": "fingerprint",
|
|
410
1466
|
"type": "bytes",
|
|
@@ -421,19 +1477,24 @@ var REGISTRY_ABI = [
|
|
|
421
1477
|
"internalType": "uint64"
|
|
422
1478
|
},
|
|
423
1479
|
{
|
|
424
|
-
"name": "
|
|
425
|
-
"type": "
|
|
426
|
-
"internalType": "
|
|
1480
|
+
"name": "state",
|
|
1481
|
+
"type": "string",
|
|
1482
|
+
"internalType": "string"
|
|
1483
|
+
},
|
|
1484
|
+
{
|
|
1485
|
+
"name": "replacedBy",
|
|
1486
|
+
"type": "uint256",
|
|
1487
|
+
"internalType": "uint256"
|
|
427
1488
|
},
|
|
428
1489
|
{
|
|
429
|
-
"name": "
|
|
430
|
-
"type": "
|
|
431
|
-
"internalType": "
|
|
1490
|
+
"name": "revokeReason",
|
|
1491
|
+
"type": "string",
|
|
1492
|
+
"internalType": "string"
|
|
432
1493
|
},
|
|
433
1494
|
{
|
|
434
|
-
"name": "
|
|
435
|
-
"type": "
|
|
436
|
-
"internalType": "
|
|
1495
|
+
"name": "messageVersion",
|
|
1496
|
+
"type": "uint8",
|
|
1497
|
+
"internalType": "uint8"
|
|
437
1498
|
}
|
|
438
1499
|
]
|
|
439
1500
|
}
|
|
@@ -442,10 +1503,27 @@ var REGISTRY_ABI = [
|
|
|
442
1503
|
},
|
|
443
1504
|
{
|
|
444
1505
|
"type": "function",
|
|
445
|
-
"name": "
|
|
446
|
-
"inputs": [
|
|
447
|
-
|
|
448
|
-
|
|
1506
|
+
"name": "clearsigned",
|
|
1507
|
+
"inputs": [
|
|
1508
|
+
{
|
|
1509
|
+
"name": "owner",
|
|
1510
|
+
"type": "address",
|
|
1511
|
+
"internalType": "address"
|
|
1512
|
+
},
|
|
1513
|
+
{
|
|
1514
|
+
"name": "index",
|
|
1515
|
+
"type": "uint256",
|
|
1516
|
+
"internalType": "uint256"
|
|
1517
|
+
}
|
|
1518
|
+
],
|
|
1519
|
+
"outputs": [
|
|
1520
|
+
{
|
|
1521
|
+
"name": "",
|
|
1522
|
+
"type": "string",
|
|
1523
|
+
"internalType": "string"
|
|
1524
|
+
}
|
|
1525
|
+
],
|
|
1526
|
+
"stateMutability": "view"
|
|
449
1527
|
},
|
|
450
1528
|
{
|
|
451
1529
|
"type": "function",
|
|
@@ -469,10 +1547,15 @@ var REGISTRY_ABI = [
|
|
|
469
1547
|
"internalType": "uint256"
|
|
470
1548
|
},
|
|
471
1549
|
{
|
|
472
|
-
"name": "
|
|
1550
|
+
"name": "details",
|
|
473
1551
|
"type": "tuple",
|
|
474
|
-
"internalType": "struct PGPRegistry.
|
|
1552
|
+
"internalType": "struct PGPRegistry.ClaimView",
|
|
475
1553
|
"components": [
|
|
1554
|
+
{
|
|
1555
|
+
"name": "index",
|
|
1556
|
+
"type": "uint256",
|
|
1557
|
+
"internalType": "uint256"
|
|
1558
|
+
},
|
|
476
1559
|
{
|
|
477
1560
|
"name": "fingerprint",
|
|
478
1561
|
"type": "bytes",
|
|
@@ -489,25 +1572,73 @@ var REGISTRY_ABI = [
|
|
|
489
1572
|
"internalType": "uint64"
|
|
490
1573
|
},
|
|
491
1574
|
{
|
|
492
|
-
"name": "
|
|
493
|
-
"type": "
|
|
494
|
-
"internalType": "
|
|
1575
|
+
"name": "state",
|
|
1576
|
+
"type": "string",
|
|
1577
|
+
"internalType": "string"
|
|
1578
|
+
},
|
|
1579
|
+
{
|
|
1580
|
+
"name": "replacedBy",
|
|
1581
|
+
"type": "uint256",
|
|
1582
|
+
"internalType": "uint256"
|
|
495
1583
|
},
|
|
496
1584
|
{
|
|
497
|
-
"name": "
|
|
498
|
-
"type": "
|
|
499
|
-
"internalType": "
|
|
1585
|
+
"name": "revokeReason",
|
|
1586
|
+
"type": "string",
|
|
1587
|
+
"internalType": "string"
|
|
500
1588
|
},
|
|
501
1589
|
{
|
|
502
|
-
"name": "
|
|
503
|
-
"type": "
|
|
504
|
-
"internalType": "
|
|
1590
|
+
"name": "messageVersion",
|
|
1591
|
+
"type": "uint8",
|
|
1592
|
+
"internalType": "uint8"
|
|
505
1593
|
}
|
|
506
1594
|
]
|
|
507
1595
|
}
|
|
508
1596
|
],
|
|
509
1597
|
"stateMutability": "view"
|
|
510
1598
|
},
|
|
1599
|
+
{
|
|
1600
|
+
"type": "function",
|
|
1601
|
+
"name": "eip712Domain",
|
|
1602
|
+
"inputs": [],
|
|
1603
|
+
"outputs": [
|
|
1604
|
+
{
|
|
1605
|
+
"name": "fields",
|
|
1606
|
+
"type": "bytes1",
|
|
1607
|
+
"internalType": "bytes1"
|
|
1608
|
+
},
|
|
1609
|
+
{
|
|
1610
|
+
"name": "name",
|
|
1611
|
+
"type": "string",
|
|
1612
|
+
"internalType": "string"
|
|
1613
|
+
},
|
|
1614
|
+
{
|
|
1615
|
+
"name": "version",
|
|
1616
|
+
"type": "string",
|
|
1617
|
+
"internalType": "string"
|
|
1618
|
+
},
|
|
1619
|
+
{
|
|
1620
|
+
"name": "chainId",
|
|
1621
|
+
"type": "uint256",
|
|
1622
|
+
"internalType": "uint256"
|
|
1623
|
+
},
|
|
1624
|
+
{
|
|
1625
|
+
"name": "verifyingContract",
|
|
1626
|
+
"type": "address",
|
|
1627
|
+
"internalType": "address"
|
|
1628
|
+
},
|
|
1629
|
+
{
|
|
1630
|
+
"name": "salt",
|
|
1631
|
+
"type": "bytes32",
|
|
1632
|
+
"internalType": "bytes32"
|
|
1633
|
+
},
|
|
1634
|
+
{
|
|
1635
|
+
"name": "extensions",
|
|
1636
|
+
"type": "uint256[]",
|
|
1637
|
+
"internalType": "uint256[]"
|
|
1638
|
+
}
|
|
1639
|
+
],
|
|
1640
|
+
"stateMutability": "view"
|
|
1641
|
+
},
|
|
511
1642
|
{
|
|
512
1643
|
"type": "function",
|
|
513
1644
|
"name": "fingerprintsForKeyId",
|
|
@@ -525,16 +1656,159 @@ var REGISTRY_ABI = [
|
|
|
525
1656
|
"internalType": "bytes[]"
|
|
526
1657
|
}
|
|
527
1658
|
],
|
|
528
|
-
"stateMutability": "view"
|
|
1659
|
+
"stateMutability": "view"
|
|
1660
|
+
},
|
|
1661
|
+
{
|
|
1662
|
+
"type": "function",
|
|
1663
|
+
"name": "fingerprintsForKeyIdCount",
|
|
1664
|
+
"inputs": [
|
|
1665
|
+
{
|
|
1666
|
+
"name": "keyId",
|
|
1667
|
+
"type": "bytes8",
|
|
1668
|
+
"internalType": "bytes8"
|
|
1669
|
+
}
|
|
1670
|
+
],
|
|
1671
|
+
"outputs": [
|
|
1672
|
+
{
|
|
1673
|
+
"name": "",
|
|
1674
|
+
"type": "uint256",
|
|
1675
|
+
"internalType": "uint256"
|
|
1676
|
+
}
|
|
1677
|
+
],
|
|
1678
|
+
"stateMutability": "view"
|
|
1679
|
+
},
|
|
1680
|
+
{
|
|
1681
|
+
"type": "function",
|
|
1682
|
+
"name": "fingerprintsForKeyIdRange",
|
|
1683
|
+
"inputs": [
|
|
1684
|
+
{
|
|
1685
|
+
"name": "keyId",
|
|
1686
|
+
"type": "bytes8",
|
|
1687
|
+
"internalType": "bytes8"
|
|
1688
|
+
},
|
|
1689
|
+
{
|
|
1690
|
+
"name": "start",
|
|
1691
|
+
"type": "uint256",
|
|
1692
|
+
"internalType": "uint256"
|
|
1693
|
+
},
|
|
1694
|
+
{
|
|
1695
|
+
"name": "count",
|
|
1696
|
+
"type": "uint256",
|
|
1697
|
+
"internalType": "uint256"
|
|
1698
|
+
}
|
|
1699
|
+
],
|
|
1700
|
+
"outputs": [
|
|
1701
|
+
{
|
|
1702
|
+
"name": "out",
|
|
1703
|
+
"type": "bytes[]",
|
|
1704
|
+
"internalType": "bytes[]"
|
|
1705
|
+
}
|
|
1706
|
+
],
|
|
1707
|
+
"stateMutability": "view"
|
|
1708
|
+
},
|
|
1709
|
+
{
|
|
1710
|
+
"type": "function",
|
|
1711
|
+
"name": "keyBytes",
|
|
1712
|
+
"inputs": [
|
|
1713
|
+
{
|
|
1714
|
+
"name": "owner",
|
|
1715
|
+
"type": "address",
|
|
1716
|
+
"internalType": "address"
|
|
1717
|
+
},
|
|
1718
|
+
{
|
|
1719
|
+
"name": "index",
|
|
1720
|
+
"type": "uint256",
|
|
1721
|
+
"internalType": "uint256"
|
|
1722
|
+
}
|
|
1723
|
+
],
|
|
1724
|
+
"outputs": [
|
|
1725
|
+
{
|
|
1726
|
+
"name": "key",
|
|
1727
|
+
"type": "bytes",
|
|
1728
|
+
"internalType": "bytes"
|
|
1729
|
+
}
|
|
1730
|
+
],
|
|
1731
|
+
"stateMutability": "view"
|
|
1732
|
+
},
|
|
1733
|
+
{
|
|
1734
|
+
"type": "function",
|
|
1735
|
+
"name": "keyStatus",
|
|
1736
|
+
"inputs": [
|
|
1737
|
+
{
|
|
1738
|
+
"name": "owner",
|
|
1739
|
+
"type": "address",
|
|
1740
|
+
"internalType": "address"
|
|
1741
|
+
},
|
|
1742
|
+
{
|
|
1743
|
+
"name": "fingerprint",
|
|
1744
|
+
"type": "bytes",
|
|
1745
|
+
"internalType": "bytes"
|
|
1746
|
+
}
|
|
1747
|
+
],
|
|
1748
|
+
"outputs": [
|
|
1749
|
+
{
|
|
1750
|
+
"name": "",
|
|
1751
|
+
"type": "string",
|
|
1752
|
+
"internalType": "string"
|
|
1753
|
+
}
|
|
1754
|
+
],
|
|
1755
|
+
"stateMutability": "view"
|
|
1756
|
+
},
|
|
1757
|
+
{
|
|
1758
|
+
"type": "function",
|
|
1759
|
+
"name": "markCompromisedFor",
|
|
1760
|
+
"inputs": [
|
|
1761
|
+
{
|
|
1762
|
+
"name": "owner",
|
|
1763
|
+
"type": "address",
|
|
1764
|
+
"internalType": "address"
|
|
1765
|
+
},
|
|
1766
|
+
{
|
|
1767
|
+
"name": "index",
|
|
1768
|
+
"type": "uint256",
|
|
1769
|
+
"internalType": "uint256"
|
|
1770
|
+
},
|
|
1771
|
+
{
|
|
1772
|
+
"name": "deadline",
|
|
1773
|
+
"type": "uint256",
|
|
1774
|
+
"internalType": "uint256"
|
|
1775
|
+
},
|
|
1776
|
+
{
|
|
1777
|
+
"name": "permission",
|
|
1778
|
+
"type": "bytes",
|
|
1779
|
+
"internalType": "bytes"
|
|
1780
|
+
}
|
|
1781
|
+
],
|
|
1782
|
+
"outputs": [],
|
|
1783
|
+
"stateMutability": "nonpayable"
|
|
1784
|
+
},
|
|
1785
|
+
{
|
|
1786
|
+
"type": "function",
|
|
1787
|
+
"name": "multicall",
|
|
1788
|
+
"inputs": [
|
|
1789
|
+
{
|
|
1790
|
+
"name": "calls",
|
|
1791
|
+
"type": "bytes[]",
|
|
1792
|
+
"internalType": "bytes[]"
|
|
1793
|
+
}
|
|
1794
|
+
],
|
|
1795
|
+
"outputs": [
|
|
1796
|
+
{
|
|
1797
|
+
"name": "results",
|
|
1798
|
+
"type": "bytes[]",
|
|
1799
|
+
"internalType": "bytes[]"
|
|
1800
|
+
}
|
|
1801
|
+
],
|
|
1802
|
+
"stateMutability": "nonpayable"
|
|
529
1803
|
},
|
|
530
1804
|
{
|
|
531
1805
|
"type": "function",
|
|
532
|
-
"name": "
|
|
1806
|
+
"name": "nonces",
|
|
533
1807
|
"inputs": [
|
|
534
1808
|
{
|
|
535
|
-
"name": "
|
|
536
|
-
"type": "
|
|
537
|
-
"internalType": "
|
|
1809
|
+
"name": "owner",
|
|
1810
|
+
"type": "address",
|
|
1811
|
+
"internalType": "address"
|
|
538
1812
|
}
|
|
539
1813
|
],
|
|
540
1814
|
"outputs": [
|
|
@@ -548,133 +1822,67 @@ var REGISTRY_ABI = [
|
|
|
548
1822
|
},
|
|
549
1823
|
{
|
|
550
1824
|
"type": "function",
|
|
551
|
-
"name": "
|
|
1825
|
+
"name": "ownersOf",
|
|
552
1826
|
"inputs": [
|
|
553
1827
|
{
|
|
554
|
-
"name": "
|
|
555
|
-
"type": "
|
|
556
|
-
"internalType": "
|
|
557
|
-
},
|
|
558
|
-
{
|
|
559
|
-
"name": "start",
|
|
560
|
-
"type": "uint256",
|
|
561
|
-
"internalType": "uint256"
|
|
562
|
-
},
|
|
563
|
-
{
|
|
564
|
-
"name": "count",
|
|
565
|
-
"type": "uint256",
|
|
566
|
-
"internalType": "uint256"
|
|
1828
|
+
"name": "fingerprint",
|
|
1829
|
+
"type": "bytes",
|
|
1830
|
+
"internalType": "bytes"
|
|
567
1831
|
}
|
|
568
1832
|
],
|
|
569
1833
|
"outputs": [
|
|
570
1834
|
{
|
|
571
|
-
"name": "
|
|
572
|
-
"type": "
|
|
573
|
-
"internalType": "
|
|
1835
|
+
"name": "",
|
|
1836
|
+
"type": "address[]",
|
|
1837
|
+
"internalType": "address[]"
|
|
574
1838
|
}
|
|
575
1839
|
],
|
|
576
1840
|
"stateMutability": "view"
|
|
577
1841
|
},
|
|
578
1842
|
{
|
|
579
1843
|
"type": "function",
|
|
580
|
-
"name": "
|
|
1844
|
+
"name": "ownersOfCount",
|
|
581
1845
|
"inputs": [
|
|
582
1846
|
{
|
|
583
|
-
"name": "
|
|
584
|
-
"type": "
|
|
585
|
-
"internalType": "
|
|
586
|
-
},
|
|
587
|
-
{
|
|
588
|
-
"name": "index",
|
|
589
|
-
"type": "uint256",
|
|
590
|
-
"internalType": "uint256"
|
|
1847
|
+
"name": "fingerprint",
|
|
1848
|
+
"type": "bytes",
|
|
1849
|
+
"internalType": "bytes"
|
|
591
1850
|
}
|
|
592
1851
|
],
|
|
593
1852
|
"outputs": [
|
|
594
1853
|
{
|
|
595
1854
|
"name": "",
|
|
596
|
-
"type": "
|
|
597
|
-
"internalType": "
|
|
598
|
-
"components": [
|
|
599
|
-
{
|
|
600
|
-
"name": "fingerprint",
|
|
601
|
-
"type": "bytes",
|
|
602
|
-
"internalType": "bytes"
|
|
603
|
-
},
|
|
604
|
-
{
|
|
605
|
-
"name": "createdAt",
|
|
606
|
-
"type": "uint64",
|
|
607
|
-
"internalType": "uint64"
|
|
608
|
-
},
|
|
609
|
-
{
|
|
610
|
-
"name": "revokedAt",
|
|
611
|
-
"type": "uint64",
|
|
612
|
-
"internalType": "uint64"
|
|
613
|
-
},
|
|
614
|
-
{
|
|
615
|
-
"name": "messageVersion",
|
|
616
|
-
"type": "uint8",
|
|
617
|
-
"internalType": "uint8"
|
|
618
|
-
},
|
|
619
|
-
{
|
|
620
|
-
"name": "keyPtr",
|
|
621
|
-
"type": "address",
|
|
622
|
-
"internalType": "address"
|
|
623
|
-
},
|
|
624
|
-
{
|
|
625
|
-
"name": "sigPtr",
|
|
626
|
-
"type": "address",
|
|
627
|
-
"internalType": "address"
|
|
628
|
-
}
|
|
629
|
-
]
|
|
1855
|
+
"type": "uint256",
|
|
1856
|
+
"internalType": "uint256"
|
|
630
1857
|
}
|
|
631
1858
|
],
|
|
632
1859
|
"stateMutability": "view"
|
|
633
1860
|
},
|
|
634
1861
|
{
|
|
635
1862
|
"type": "function",
|
|
636
|
-
"name": "
|
|
1863
|
+
"name": "ownersOfRange",
|
|
637
1864
|
"inputs": [
|
|
638
1865
|
{
|
|
639
|
-
"name": "
|
|
640
|
-
"type": "
|
|
641
|
-
"internalType": "
|
|
1866
|
+
"name": "fingerprint",
|
|
1867
|
+
"type": "bytes",
|
|
1868
|
+
"internalType": "bytes"
|
|
642
1869
|
},
|
|
643
1870
|
{
|
|
644
|
-
"name": "
|
|
1871
|
+
"name": "start",
|
|
645
1872
|
"type": "uint256",
|
|
646
1873
|
"internalType": "uint256"
|
|
647
|
-
}
|
|
648
|
-
],
|
|
649
|
-
"outputs": [
|
|
650
|
-
{
|
|
651
|
-
"name": "pgpSignature",
|
|
652
|
-
"type": "bytes",
|
|
653
|
-
"internalType": "bytes"
|
|
654
1874
|
},
|
|
655
1875
|
{
|
|
656
|
-
"name": "
|
|
657
|
-
"type": "
|
|
658
|
-
"internalType": "
|
|
659
|
-
}
|
|
660
|
-
],
|
|
661
|
-
"stateMutability": "view"
|
|
662
|
-
},
|
|
663
|
-
{
|
|
664
|
-
"type": "function",
|
|
665
|
-
"name": "nonces",
|
|
666
|
-
"inputs": [
|
|
667
|
-
{
|
|
668
|
-
"name": "",
|
|
669
|
-
"type": "address",
|
|
670
|
-
"internalType": "address"
|
|
1876
|
+
"name": "count",
|
|
1877
|
+
"type": "uint256",
|
|
1878
|
+
"internalType": "uint256"
|
|
671
1879
|
}
|
|
672
1880
|
],
|
|
673
1881
|
"outputs": [
|
|
674
1882
|
{
|
|
675
|
-
"name": "",
|
|
676
|
-
"type": "
|
|
677
|
-
"internalType": "
|
|
1883
|
+
"name": "out",
|
|
1884
|
+
"type": "address[]",
|
|
1885
|
+
"internalType": "address[]"
|
|
678
1886
|
}
|
|
679
1887
|
],
|
|
680
1888
|
"stateMutability": "view"
|
|
@@ -694,14 +1902,19 @@ var REGISTRY_ABI = [
|
|
|
694
1902
|
"internalType": "bytes"
|
|
695
1903
|
},
|
|
696
1904
|
{
|
|
697
|
-
"name": "
|
|
1905
|
+
"name": "signature",
|
|
698
1906
|
"type": "bytes",
|
|
699
1907
|
"internalType": "bytes"
|
|
700
1908
|
},
|
|
701
1909
|
{
|
|
702
|
-
"name": "
|
|
1910
|
+
"name": "key",
|
|
703
1911
|
"type": "bytes",
|
|
704
1912
|
"internalType": "bytes"
|
|
1913
|
+
},
|
|
1914
|
+
{
|
|
1915
|
+
"name": "keepRecords",
|
|
1916
|
+
"type": "bool",
|
|
1917
|
+
"internalType": "bool"
|
|
705
1918
|
}
|
|
706
1919
|
],
|
|
707
1920
|
"outputs": [
|
|
@@ -733,22 +1946,27 @@ var REGISTRY_ABI = [
|
|
|
733
1946
|
"internalType": "bytes"
|
|
734
1947
|
},
|
|
735
1948
|
{
|
|
736
|
-
"name": "
|
|
1949
|
+
"name": "signature",
|
|
737
1950
|
"type": "bytes",
|
|
738
1951
|
"internalType": "bytes"
|
|
739
1952
|
},
|
|
740
1953
|
{
|
|
741
|
-
"name": "
|
|
1954
|
+
"name": "key",
|
|
742
1955
|
"type": "bytes",
|
|
743
1956
|
"internalType": "bytes"
|
|
744
1957
|
},
|
|
1958
|
+
{
|
|
1959
|
+
"name": "keepRecords",
|
|
1960
|
+
"type": "bool",
|
|
1961
|
+
"internalType": "bool"
|
|
1962
|
+
},
|
|
745
1963
|
{
|
|
746
1964
|
"name": "deadline",
|
|
747
1965
|
"type": "uint256",
|
|
748
1966
|
"internalType": "uint256"
|
|
749
1967
|
},
|
|
750
1968
|
{
|
|
751
|
-
"name": "
|
|
1969
|
+
"name": "permission",
|
|
752
1970
|
"type": "bytes",
|
|
753
1971
|
"internalType": "bytes"
|
|
754
1972
|
}
|
|
@@ -764,7 +1982,7 @@ var REGISTRY_ABI = [
|
|
|
764
1982
|
},
|
|
765
1983
|
{
|
|
766
1984
|
"type": "function",
|
|
767
|
-
"name": "
|
|
1985
|
+
"name": "recordText",
|
|
768
1986
|
"inputs": [
|
|
769
1987
|
{
|
|
770
1988
|
"name": "owner",
|
|
@@ -778,15 +1996,44 @@ var REGISTRY_ABI = [
|
|
|
778
1996
|
},
|
|
779
1997
|
{
|
|
780
1998
|
"name": "kind",
|
|
781
|
-
"type": "
|
|
782
|
-
"internalType": "
|
|
1999
|
+
"type": "string",
|
|
2000
|
+
"internalType": "string"
|
|
783
2001
|
}
|
|
784
2002
|
],
|
|
785
2003
|
"outputs": [
|
|
786
2004
|
{
|
|
787
2005
|
"name": "",
|
|
788
|
-
"type": "
|
|
789
|
-
"internalType": "
|
|
2006
|
+
"type": "string",
|
|
2007
|
+
"internalType": "string"
|
|
2008
|
+
}
|
|
2009
|
+
],
|
|
2010
|
+
"stateMutability": "view"
|
|
2011
|
+
},
|
|
2012
|
+
{
|
|
2013
|
+
"type": "function",
|
|
2014
|
+
"name": "recordsOf",
|
|
2015
|
+
"inputs": [
|
|
2016
|
+
{
|
|
2017
|
+
"name": "owner",
|
|
2018
|
+
"type": "address",
|
|
2019
|
+
"internalType": "address"
|
|
2020
|
+
},
|
|
2021
|
+
{
|
|
2022
|
+
"name": "index",
|
|
2023
|
+
"type": "uint256",
|
|
2024
|
+
"internalType": "uint256"
|
|
2025
|
+
}
|
|
2026
|
+
],
|
|
2027
|
+
"outputs": [
|
|
2028
|
+
{
|
|
2029
|
+
"name": "kinds",
|
|
2030
|
+
"type": "string[]",
|
|
2031
|
+
"internalType": "string[]"
|
|
2032
|
+
},
|
|
2033
|
+
{
|
|
2034
|
+
"name": "values",
|
|
2035
|
+
"type": "string[]",
|
|
2036
|
+
"internalType": "string[]"
|
|
790
2037
|
}
|
|
791
2038
|
],
|
|
792
2039
|
"stateMutability": "view"
|
|
@@ -799,6 +2046,11 @@ var REGISTRY_ABI = [
|
|
|
799
2046
|
"name": "index",
|
|
800
2047
|
"type": "uint256",
|
|
801
2048
|
"internalType": "uint256"
|
|
2049
|
+
},
|
|
2050
|
+
{
|
|
2051
|
+
"name": "reason",
|
|
2052
|
+
"type": "string",
|
|
2053
|
+
"internalType": "string"
|
|
802
2054
|
}
|
|
803
2055
|
],
|
|
804
2056
|
"outputs": [],
|
|
@@ -818,13 +2070,18 @@ var REGISTRY_ABI = [
|
|
|
818
2070
|
"type": "uint256",
|
|
819
2071
|
"internalType": "uint256"
|
|
820
2072
|
},
|
|
2073
|
+
{
|
|
2074
|
+
"name": "reason",
|
|
2075
|
+
"type": "string",
|
|
2076
|
+
"internalType": "string"
|
|
2077
|
+
},
|
|
821
2078
|
{
|
|
822
2079
|
"name": "deadline",
|
|
823
2080
|
"type": "uint256",
|
|
824
2081
|
"internalType": "uint256"
|
|
825
2082
|
},
|
|
826
2083
|
{
|
|
827
|
-
"name": "
|
|
2084
|
+
"name": "permission",
|
|
828
2085
|
"type": "bytes",
|
|
829
2086
|
"internalType": "bytes"
|
|
830
2087
|
}
|
|
@@ -843,13 +2100,13 @@ var REGISTRY_ABI = [
|
|
|
843
2100
|
},
|
|
844
2101
|
{
|
|
845
2102
|
"name": "kind",
|
|
846
|
-
"type": "
|
|
847
|
-
"internalType": "
|
|
2103
|
+
"type": "string",
|
|
2104
|
+
"internalType": "string"
|
|
848
2105
|
},
|
|
849
2106
|
{
|
|
850
2107
|
"name": "value",
|
|
851
|
-
"type": "
|
|
852
|
-
"internalType": "
|
|
2108
|
+
"type": "string",
|
|
2109
|
+
"internalType": "string"
|
|
853
2110
|
}
|
|
854
2111
|
],
|
|
855
2112
|
"outputs": [],
|
|
@@ -871,13 +2128,13 @@ var REGISTRY_ABI = [
|
|
|
871
2128
|
},
|
|
872
2129
|
{
|
|
873
2130
|
"name": "kind",
|
|
874
|
-
"type": "
|
|
875
|
-
"internalType": "
|
|
2131
|
+
"type": "string",
|
|
2132
|
+
"internalType": "string"
|
|
876
2133
|
},
|
|
877
2134
|
{
|
|
878
2135
|
"name": "value",
|
|
879
|
-
"type": "
|
|
880
|
-
"internalType": "
|
|
2136
|
+
"type": "string",
|
|
2137
|
+
"internalType": "string"
|
|
881
2138
|
},
|
|
882
2139
|
{
|
|
883
2140
|
"name": "deadline",
|
|
@@ -885,7 +2142,7 @@ var REGISTRY_ABI = [
|
|
|
885
2142
|
"internalType": "uint256"
|
|
886
2143
|
},
|
|
887
2144
|
{
|
|
888
|
-
"name": "
|
|
2145
|
+
"name": "permission",
|
|
889
2146
|
"type": "bytes",
|
|
890
2147
|
"internalType": "bytes"
|
|
891
2148
|
}
|
|
@@ -893,6 +2150,83 @@ var REGISTRY_ABI = [
|
|
|
893
2150
|
"outputs": [],
|
|
894
2151
|
"stateMutability": "nonpayable"
|
|
895
2152
|
},
|
|
2153
|
+
{
|
|
2154
|
+
"type": "function",
|
|
2155
|
+
"name": "signatureBytes",
|
|
2156
|
+
"inputs": [
|
|
2157
|
+
{
|
|
2158
|
+
"name": "owner",
|
|
2159
|
+
"type": "address",
|
|
2160
|
+
"internalType": "address"
|
|
2161
|
+
},
|
|
2162
|
+
{
|
|
2163
|
+
"name": "index",
|
|
2164
|
+
"type": "uint256",
|
|
2165
|
+
"internalType": "uint256"
|
|
2166
|
+
}
|
|
2167
|
+
],
|
|
2168
|
+
"outputs": [
|
|
2169
|
+
{
|
|
2170
|
+
"name": "signature",
|
|
2171
|
+
"type": "bytes",
|
|
2172
|
+
"internalType": "bytes"
|
|
2173
|
+
}
|
|
2174
|
+
],
|
|
2175
|
+
"stateMutability": "view"
|
|
2176
|
+
},
|
|
2177
|
+
{
|
|
2178
|
+
"type": "function",
|
|
2179
|
+
"name": "statementFor",
|
|
2180
|
+
"inputs": [
|
|
2181
|
+
{
|
|
2182
|
+
"name": "owner",
|
|
2183
|
+
"type": "address",
|
|
2184
|
+
"internalType": "address"
|
|
2185
|
+
}
|
|
2186
|
+
],
|
|
2187
|
+
"outputs": [
|
|
2188
|
+
{
|
|
2189
|
+
"name": "",
|
|
2190
|
+
"type": "string",
|
|
2191
|
+
"internalType": "string"
|
|
2192
|
+
}
|
|
2193
|
+
],
|
|
2194
|
+
"stateMutability": "pure"
|
|
2195
|
+
},
|
|
2196
|
+
{
|
|
2197
|
+
"type": "function",
|
|
2198
|
+
"name": "summary",
|
|
2199
|
+
"inputs": [
|
|
2200
|
+
{
|
|
2201
|
+
"name": "owner",
|
|
2202
|
+
"type": "address",
|
|
2203
|
+
"internalType": "address"
|
|
2204
|
+
}
|
|
2205
|
+
],
|
|
2206
|
+
"outputs": [
|
|
2207
|
+
{
|
|
2208
|
+
"name": "total",
|
|
2209
|
+
"type": "uint256",
|
|
2210
|
+
"internalType": "uint256"
|
|
2211
|
+
},
|
|
2212
|
+
{
|
|
2213
|
+
"name": "active",
|
|
2214
|
+
"type": "uint256",
|
|
2215
|
+
"internalType": "uint256"
|
|
2216
|
+
},
|
|
2217
|
+
{
|
|
2218
|
+
"name": "hasCurrent",
|
|
2219
|
+
"type": "bool",
|
|
2220
|
+
"internalType": "bool"
|
|
2221
|
+
},
|
|
2222
|
+
{
|
|
2223
|
+
"name": "currentIndex",
|
|
2224
|
+
"type": "uint256",
|
|
2225
|
+
"internalType": "uint256"
|
|
2226
|
+
}
|
|
2227
|
+
],
|
|
2228
|
+
"stateMutability": "view"
|
|
2229
|
+
},
|
|
896
2230
|
{
|
|
897
2231
|
"type": "function",
|
|
898
2232
|
"name": "updateKey",
|
|
@@ -903,7 +2237,7 @@ var REGISTRY_ABI = [
|
|
|
903
2237
|
"internalType": "uint256"
|
|
904
2238
|
},
|
|
905
2239
|
{
|
|
906
|
-
"name": "
|
|
2240
|
+
"name": "key",
|
|
907
2241
|
"type": "bytes",
|
|
908
2242
|
"internalType": "bytes"
|
|
909
2243
|
}
|
|
@@ -926,7 +2260,7 @@ var REGISTRY_ABI = [
|
|
|
926
2260
|
"internalType": "uint256"
|
|
927
2261
|
},
|
|
928
2262
|
{
|
|
929
|
-
"name": "
|
|
2263
|
+
"name": "key",
|
|
930
2264
|
"type": "bytes",
|
|
931
2265
|
"internalType": "bytes"
|
|
932
2266
|
},
|
|
@@ -936,7 +2270,7 @@ var REGISTRY_ABI = [
|
|
|
936
2270
|
"internalType": "uint256"
|
|
937
2271
|
},
|
|
938
2272
|
{
|
|
939
|
-
"name": "
|
|
2273
|
+
"name": "permission",
|
|
940
2274
|
"type": "bytes",
|
|
941
2275
|
"internalType": "bytes"
|
|
942
2276
|
}
|
|
@@ -966,29 +2300,23 @@ var REGISTRY_ABI = [
|
|
|
966
2300
|
"indexed": true,
|
|
967
2301
|
"internalType": "uint256"
|
|
968
2302
|
},
|
|
969
|
-
{
|
|
970
|
-
"name": "fingerprint",
|
|
971
|
-
"type": "bytes",
|
|
972
|
-
"indexed": false,
|
|
973
|
-
"internalType": "bytes"
|
|
974
|
-
},
|
|
975
|
-
{
|
|
976
|
-
"name": "messageVersion",
|
|
977
|
-
"type": "uint8",
|
|
2303
|
+
{
|
|
2304
|
+
"name": "fingerprint",
|
|
2305
|
+
"type": "bytes",
|
|
978
2306
|
"indexed": false,
|
|
979
|
-
"internalType": "
|
|
2307
|
+
"internalType": "bytes"
|
|
980
2308
|
},
|
|
981
2309
|
{
|
|
982
|
-
"name": "
|
|
2310
|
+
"name": "payload",
|
|
983
2311
|
"type": "address",
|
|
984
2312
|
"indexed": false,
|
|
985
2313
|
"internalType": "address"
|
|
986
2314
|
},
|
|
987
2315
|
{
|
|
988
|
-
"name": "
|
|
989
|
-
"type": "
|
|
2316
|
+
"name": "messageVersion",
|
|
2317
|
+
"type": "uint8",
|
|
990
2318
|
"indexed": false,
|
|
991
|
-
"internalType": "
|
|
2319
|
+
"internalType": "uint8"
|
|
992
2320
|
},
|
|
993
2321
|
{
|
|
994
2322
|
"name": "submitter",
|
|
@@ -1022,13 +2350,13 @@ var REGISTRY_ABI = [
|
|
|
1022
2350
|
"internalType": "uint256"
|
|
1023
2351
|
},
|
|
1024
2352
|
{
|
|
1025
|
-
"name": "
|
|
2353
|
+
"name": "oldPayload",
|
|
1026
2354
|
"type": "address",
|
|
1027
2355
|
"indexed": false,
|
|
1028
2356
|
"internalType": "address"
|
|
1029
2357
|
},
|
|
1030
2358
|
{
|
|
1031
|
-
"name": "
|
|
2359
|
+
"name": "newPayload",
|
|
1032
2360
|
"type": "address",
|
|
1033
2361
|
"indexed": false,
|
|
1034
2362
|
"internalType": "address"
|
|
@@ -1078,11 +2406,23 @@ var REGISTRY_ABI = [
|
|
|
1078
2406
|
"internalType": "uint256"
|
|
1079
2407
|
},
|
|
1080
2408
|
{
|
|
1081
|
-
"name": "
|
|
2409
|
+
"name": "kindHash",
|
|
1082
2410
|
"type": "bytes32",
|
|
1083
2411
|
"indexed": true,
|
|
1084
2412
|
"internalType": "bytes32"
|
|
1085
2413
|
},
|
|
2414
|
+
{
|
|
2415
|
+
"name": "kind",
|
|
2416
|
+
"type": "string",
|
|
2417
|
+
"indexed": false,
|
|
2418
|
+
"internalType": "string"
|
|
2419
|
+
},
|
|
2420
|
+
{
|
|
2421
|
+
"name": "value",
|
|
2422
|
+
"type": "string",
|
|
2423
|
+
"indexed": false,
|
|
2424
|
+
"internalType": "string"
|
|
2425
|
+
},
|
|
1086
2426
|
{
|
|
1087
2427
|
"name": "submitter",
|
|
1088
2428
|
"type": "address",
|
|
@@ -1092,6 +2432,31 @@ var REGISTRY_ABI = [
|
|
|
1092
2432
|
],
|
|
1093
2433
|
"anonymous": false
|
|
1094
2434
|
},
|
|
2435
|
+
{
|
|
2436
|
+
"type": "event",
|
|
2437
|
+
"name": "RecordsMoved",
|
|
2438
|
+
"inputs": [
|
|
2439
|
+
{
|
|
2440
|
+
"name": "owner",
|
|
2441
|
+
"type": "address",
|
|
2442
|
+
"indexed": true,
|
|
2443
|
+
"internalType": "address"
|
|
2444
|
+
},
|
|
2445
|
+
{
|
|
2446
|
+
"name": "fromIndex",
|
|
2447
|
+
"type": "uint256",
|
|
2448
|
+
"indexed": true,
|
|
2449
|
+
"internalType": "uint256"
|
|
2450
|
+
},
|
|
2451
|
+
{
|
|
2452
|
+
"name": "toIndex",
|
|
2453
|
+
"type": "uint256",
|
|
2454
|
+
"indexed": true,
|
|
2455
|
+
"internalType": "uint256"
|
|
2456
|
+
}
|
|
2457
|
+
],
|
|
2458
|
+
"anonymous": false
|
|
2459
|
+
},
|
|
1095
2460
|
{
|
|
1096
2461
|
"type": "event",
|
|
1097
2462
|
"name": "Revoked",
|
|
@@ -1114,6 +2479,18 @@ var REGISTRY_ABI = [
|
|
|
1114
2479
|
"indexed": true,
|
|
1115
2480
|
"internalType": "uint256"
|
|
1116
2481
|
},
|
|
2482
|
+
{
|
|
2483
|
+
"name": "reason",
|
|
2484
|
+
"type": "string",
|
|
2485
|
+
"indexed": false,
|
|
2486
|
+
"internalType": "string"
|
|
2487
|
+
},
|
|
2488
|
+
{
|
|
2489
|
+
"name": "replacedBy",
|
|
2490
|
+
"type": "uint256",
|
|
2491
|
+
"indexed": false,
|
|
2492
|
+
"internalType": "uint256"
|
|
2493
|
+
},
|
|
1117
2494
|
{
|
|
1118
2495
|
"name": "submitter",
|
|
1119
2496
|
"type": "address",
|
|
@@ -1126,893 +2503,629 @@ var REGISTRY_ABI = [
|
|
|
1126
2503
|
{
|
|
1127
2504
|
"type": "error",
|
|
1128
2505
|
"name": "AlreadyRevoked",
|
|
1129
|
-
"inputs": [
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
{
|
|
1137
|
-
"type": "error",
|
|
1138
|
-
"name": "DeploymentFailed",
|
|
1139
|
-
"inputs": []
|
|
1140
|
-
},
|
|
1141
|
-
{
|
|
1142
|
-
"type": "error",
|
|
1143
|
-
"name": "DuplicateActiveFingerprint",
|
|
1144
|
-
"inputs": []
|
|
1145
|
-
},
|
|
1146
|
-
{
|
|
1147
|
-
"type": "error",
|
|
1148
|
-
"name": "EmptyPublicKey",
|
|
1149
|
-
"inputs": []
|
|
1150
|
-
},
|
|
1151
|
-
{
|
|
1152
|
-
"type": "error",
|
|
1153
|
-
"name": "EmptySignature",
|
|
1154
|
-
"inputs": []
|
|
1155
|
-
},
|
|
1156
|
-
{
|
|
1157
|
-
"type": "error",
|
|
1158
|
-
"name": "IndexOutOfBounds",
|
|
1159
|
-
"inputs": []
|
|
1160
|
-
},
|
|
1161
|
-
{
|
|
1162
|
-
"type": "error",
|
|
1163
|
-
"name": "InvalidAuthorization",
|
|
1164
|
-
"inputs": []
|
|
2506
|
+
"inputs": [
|
|
2507
|
+
{
|
|
2508
|
+
"name": "index",
|
|
2509
|
+
"type": "uint256",
|
|
2510
|
+
"internalType": "uint256"
|
|
2511
|
+
}
|
|
2512
|
+
]
|
|
1165
2513
|
},
|
|
1166
2514
|
{
|
|
1167
2515
|
"type": "error",
|
|
1168
|
-
"name": "
|
|
2516
|
+
"name": "BadArmorChecksum",
|
|
1169
2517
|
"inputs": []
|
|
1170
2518
|
},
|
|
1171
2519
|
{
|
|
1172
2520
|
"type": "error",
|
|
1173
|
-
"name": "
|
|
2521
|
+
"name": "BadBase64",
|
|
1174
2522
|
"inputs": []
|
|
1175
2523
|
},
|
|
1176
2524
|
{
|
|
1177
2525
|
"type": "error",
|
|
1178
|
-
"name": "
|
|
1179
|
-
"inputs": [
|
|
2526
|
+
"name": "ClaimActive",
|
|
2527
|
+
"inputs": [
|
|
2528
|
+
{
|
|
2529
|
+
"name": "index",
|
|
2530
|
+
"type": "uint256",
|
|
2531
|
+
"internalType": "uint256"
|
|
2532
|
+
}
|
|
2533
|
+
]
|
|
1180
2534
|
},
|
|
1181
2535
|
{
|
|
1182
2536
|
"type": "error",
|
|
1183
|
-
"name": "
|
|
2537
|
+
"name": "DeploymentFailed",
|
|
1184
2538
|
"inputs": []
|
|
1185
2539
|
},
|
|
1186
2540
|
{
|
|
1187
2541
|
"type": "error",
|
|
1188
|
-
"name": "
|
|
1189
|
-
"inputs": [
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
import { mainnet, sepolia, foundry } from "viem/chains";
|
|
1195
|
-
var REGISTRY_ADDRESS = "0x9302E02e2869e129aC8516fE5eFFd51EA3082c09";
|
|
1196
|
-
var NETWORKS = {
|
|
1197
|
-
mainnet: {
|
|
1198
|
-
chainId: 1,
|
|
1199
|
-
address: REGISTRY_ADDRESS,
|
|
1200
|
-
deployBlock: 25962908n,
|
|
1201
|
-
// 2026-09-12, tx 0xda4c3a19cae4e71ffb02559a048403f664d5a7967c19d42158c39600ec63bad8
|
|
1202
|
-
explorerUrl: "https://etherscan.io",
|
|
1203
|
-
defaultRpcUrl: "https://ethereum.publicnode.com"
|
|
1204
|
-
// same service as ethereum-rpc.publicnode.com; this hostname resolves on more networks
|
|
1205
|
-
},
|
|
1206
|
-
sepolia: {
|
|
1207
|
-
chainId: 11155111,
|
|
1208
|
-
address: REGISTRY_ADDRESS,
|
|
1209
|
-
deployBlock: 11683667n,
|
|
1210
|
-
// 2026-09-11, tx 0x3e40e8f5b9fbf82a4b8943561bcc9b964b2987de10640ac8ff3a1b4ef0725322
|
|
1211
|
-
explorerUrl: "https://sepolia.etherscan.io",
|
|
1212
|
-
defaultRpcUrl: "https://ethereum-sepolia.publicnode.com"
|
|
1213
|
-
},
|
|
1214
|
-
/** A local anvil instance (`anvil`, chain id 31337). Deploy with pgp-registry's Deploy script. */
|
|
1215
|
-
local: {
|
|
1216
|
-
chainId: 31337,
|
|
1217
|
-
address: REGISTRY_ADDRESS,
|
|
1218
|
-
deployBlock: 0n,
|
|
1219
|
-
explorerUrl: "",
|
|
1220
|
-
defaultRpcUrl: "http://127.0.0.1:8545"
|
|
1221
|
-
}
|
|
1222
|
-
};
|
|
1223
|
-
function isNetworkName(value) {
|
|
1224
|
-
return value === "mainnet" || value === "sepolia" || value === "local";
|
|
1225
|
-
}
|
|
1226
|
-
function getRegistry(network = "mainnet", addressOverride) {
|
|
1227
|
-
const r = NETWORKS[network];
|
|
1228
|
-
if (!r) throw new Error(`Unknown network: ${network}`);
|
|
1229
|
-
if (addressOverride && /^0x[0-9a-fA-F]{40}$/.test(addressOverride)) {
|
|
1230
|
-
return { ...r, address: addressOverride };
|
|
1231
|
-
}
|
|
1232
|
-
return r;
|
|
1233
|
-
}
|
|
1234
|
-
function chainFor(network = "mainnet") {
|
|
1235
|
-
if (network === "sepolia") return sepolia;
|
|
1236
|
-
if (network === "local") return foundry;
|
|
1237
|
-
return mainnet;
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
// src/core/identityError.ts
|
|
1241
|
-
function needsRpcProbe(s) {
|
|
1242
|
-
return s.ensEmpty || s.ensFailed || s.claimsFailed;
|
|
1243
|
-
}
|
|
1244
|
-
function identityErrorKind(s) {
|
|
1245
|
-
if (!needsRpcProbe(s) || s.rpcAnswered === void 0) return null;
|
|
1246
|
-
if (!s.rpcAnswered) return "rpc";
|
|
1247
|
-
if (s.ensEmpty && !s.ensFailed) return "not-found";
|
|
1248
|
-
return "read";
|
|
1249
|
-
}
|
|
1250
|
-
var IDENTITY_ERROR_TEXT = {
|
|
1251
|
-
rpc: "Couldn't reach the RPC, so nothing here is known yet.",
|
|
1252
|
-
"not-found": "No address for this name.",
|
|
1253
|
-
read: "Couldn't read the registry."
|
|
1254
|
-
};
|
|
1255
|
-
|
|
1256
|
-
// src/core/fingerprint.ts
|
|
1257
|
-
import { keccak256 } from "viem";
|
|
1258
|
-
var FINGERPRINT_HEX = /^(?:[0-9a-f]{40}|[0-9a-f]{64})$/;
|
|
1259
|
-
function normalizeFingerprint(input) {
|
|
1260
|
-
const hex = input.replace(/^0x/i, "").replace(/\s+/g, "").toLowerCase();
|
|
1261
|
-
return FINGERPRINT_HEX.test(hex) ? hex : null;
|
|
1262
|
-
}
|
|
1263
|
-
function fingerprintToBytes(fingerprint) {
|
|
1264
|
-
const hex = normalizeFingerprint(fingerprint);
|
|
1265
|
-
if (!hex) throw new Error(`Invalid PGP fingerprint: ${fingerprint}`);
|
|
1266
|
-
return `0x${hex}`;
|
|
1267
|
-
}
|
|
1268
|
-
function bytesToFingerprint(bytes) {
|
|
1269
|
-
return bytes.replace(/^0x/i, "").toLowerCase();
|
|
1270
|
-
}
|
|
1271
|
-
function fingerprintHash(fingerprint) {
|
|
1272
|
-
return keccak256(fingerprintToBytes(fingerprint));
|
|
1273
|
-
}
|
|
1274
|
-
function keyIdOf(fingerprint) {
|
|
1275
|
-
const hex = normalizeFingerprint(fingerprint);
|
|
1276
|
-
if (!hex) throw new Error(`Invalid PGP fingerprint: ${fingerprint}`);
|
|
1277
|
-
return `0x${keyIdHex(hex)}`;
|
|
1278
|
-
}
|
|
1279
|
-
function keyIdHex(normalizedFingerprint) {
|
|
1280
|
-
return normalizedFingerprint.length === 64 ? normalizedFingerprint.slice(0, 16) : normalizedFingerprint.slice(-16);
|
|
1281
|
-
}
|
|
1282
|
-
function keyIdToBytes(keyId) {
|
|
1283
|
-
const hex = keyId.replace(/^0x/i, "").replace(/\s+/g, "").toLowerCase();
|
|
1284
|
-
return /^[0-9a-f]{16}$/.test(hex) ? `0x${hex}` : null;
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
// src/core/pgp.ts
|
|
1288
|
-
function pgpConfig(openpgp) {
|
|
1289
|
-
const rejectCurves = new Set([...openpgp.config.rejectCurves ?? []].filter((c) => c !== "secp256k1"));
|
|
1290
|
-
return { ...openpgp.config, rejectCurves };
|
|
1291
|
-
}
|
|
1292
|
-
function algorithmName(keyOrSubkey) {
|
|
1293
|
-
try {
|
|
1294
|
-
const info = keyOrSubkey.getAlgorithmInfo();
|
|
1295
|
-
const curve = (info.curve || "").toLowerCase();
|
|
1296
|
-
if (curve.startsWith("ed25519") || info.algorithm === "ed25519" || info.algorithm === "eddsaLegacy") return "Ed25519";
|
|
1297
|
-
if (curve.startsWith("curve25519") || info.algorithm === "x25519") return "Cv25519";
|
|
1298
|
-
if (curve.startsWith("ed448")) return "Ed448";
|
|
1299
|
-
if (curve.startsWith("curve448") || info.algorithm === "x448") return "X448";
|
|
1300
|
-
if (curve.startsWith("nistp") || curve.startsWith("p")) return `NIST P-${curve.replace(/\D/g, "")}`;
|
|
1301
|
-
if (curve.startsWith("secp")) return curve;
|
|
1302
|
-
if (curve.startsWith("brainpool")) return curve;
|
|
1303
|
-
if (info.algorithm.startsWith("rsa")) return info.bits ? `RSA ${info.bits}` : "RSA";
|
|
1304
|
-
if (info.algorithm.startsWith("dsa")) return info.bits ? `DSA ${info.bits}` : "DSA";
|
|
1305
|
-
if (info.algorithm.startsWith("elgamal")) return info.bits ? `ElGamal ${info.bits}` : "ElGamal";
|
|
1306
|
-
return info.curve || info.algorithm;
|
|
1307
|
-
} catch {
|
|
1308
|
-
return String(keyOrSubkey.keyPacket?.algorithm ?? "unknown");
|
|
1309
|
-
}
|
|
1310
|
-
}
|
|
1311
|
-
async function parsePgpKey(armoredKey) {
|
|
1312
|
-
try {
|
|
1313
|
-
const openpgp = await import("openpgp");
|
|
1314
|
-
const { readKey } = openpgp;
|
|
1315
|
-
const config = pgpConfig(openpgp);
|
|
1316
|
-
const key = await readKey({ armoredKey });
|
|
1317
|
-
const fingerprint = key.getFingerprint().toUpperCase();
|
|
1318
|
-
const algorithm = algorithmName(key);
|
|
1319
|
-
const created = key.keyPacket.created?.toISOString() ?? null;
|
|
1320
|
-
const expiration = await key.getExpirationTime();
|
|
1321
|
-
const expires = expiration && expiration !== Infinity ? new Date(expiration).toISOString() : null;
|
|
1322
|
-
const userIDs = [];
|
|
1323
|
-
const notations = [];
|
|
1324
|
-
const seen = /* @__PURE__ */ new Set();
|
|
1325
|
-
for (const user of key.users) {
|
|
1326
|
-
const certs = user.selfCertifications;
|
|
1327
|
-
if (!certs || certs.length === 0) continue;
|
|
1328
|
-
let userVerified = false;
|
|
1329
|
-
for (const cert of certs) {
|
|
1330
|
-
try {
|
|
1331
|
-
await cert.verify(
|
|
1332
|
-
key.keyPacket,
|
|
1333
|
-
cert.signatureType,
|
|
1334
|
-
{ userID: user.userID, key: key.keyPacket },
|
|
1335
|
-
void 0,
|
|
1336
|
-
// date: now
|
|
1337
|
-
void 0,
|
|
1338
|
-
// detached
|
|
1339
|
-
config
|
|
1340
|
-
);
|
|
1341
|
-
} catch {
|
|
1342
|
-
continue;
|
|
1343
|
-
}
|
|
1344
|
-
userVerified = true;
|
|
1345
|
-
if (cert.rawNotations) {
|
|
1346
|
-
for (const n of cert.rawNotations) {
|
|
1347
|
-
const name = typeof n.name === "string" ? n.name : new TextDecoder().decode(n.name);
|
|
1348
|
-
const value = n.value instanceof Uint8Array ? new TextDecoder().decode(n.value) : typeof n.value === "string" ? n.value : null;
|
|
1349
|
-
if (value) {
|
|
1350
|
-
if (!seen.has(value)) {
|
|
1351
|
-
seen.add(value);
|
|
1352
|
-
notations.push({ name, value });
|
|
1353
|
-
}
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
}
|
|
2542
|
+
"name": "DuplicateActiveFingerprint",
|
|
2543
|
+
"inputs": [
|
|
2544
|
+
{
|
|
2545
|
+
"name": "fingerprint",
|
|
2546
|
+
"type": "bytes",
|
|
2547
|
+
"internalType": "bytes"
|
|
1357
2548
|
}
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
2549
|
+
]
|
|
2550
|
+
},
|
|
2551
|
+
{
|
|
2552
|
+
"type": "error",
|
|
2553
|
+
"name": "EmptyKey",
|
|
2554
|
+
"inputs": []
|
|
2555
|
+
},
|
|
2556
|
+
{
|
|
2557
|
+
"type": "error",
|
|
2558
|
+
"name": "EmptySignature",
|
|
2559
|
+
"inputs": []
|
|
2560
|
+
},
|
|
2561
|
+
{
|
|
2562
|
+
"type": "error",
|
|
2563
|
+
"name": "IndexOutOfBounds",
|
|
2564
|
+
"inputs": [
|
|
2565
|
+
{
|
|
2566
|
+
"name": "index",
|
|
2567
|
+
"type": "uint256",
|
|
2568
|
+
"internalType": "uint256"
|
|
2569
|
+
},
|
|
2570
|
+
{
|
|
2571
|
+
"name": "count",
|
|
2572
|
+
"type": "uint256",
|
|
2573
|
+
"internalType": "uint256"
|
|
1361
2574
|
}
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
}
|
|
1373
|
-
async function verifyClearsigned({ armoredKey, clearsigned }) {
|
|
1374
|
-
try {
|
|
1375
|
-
const openpgp = await import("openpgp");
|
|
1376
|
-
const { readKey, readCleartextMessage, LiteralDataPacket } = openpgp;
|
|
1377
|
-
const config = pgpConfig(openpgp);
|
|
1378
|
-
const publicKey = await readKey({ armoredKey });
|
|
1379
|
-
const message = await readCleartextMessage({ cleartextMessage: clearsigned });
|
|
1380
|
-
const msg = message;
|
|
1381
|
-
const now = /* @__PURE__ */ new Date();
|
|
1382
|
-
for (const sig of msg.signature.packets) {
|
|
1383
|
-
if (!publicKey.getKeys(sig.issuerKeyID).length) continue;
|
|
1384
|
-
const signingKey = await publicKey.getSigningKey(sig.issuerKeyID, now, void 0, config);
|
|
1385
|
-
const literal = new LiteralDataPacket();
|
|
1386
|
-
literal.setText(msg.text ?? message.getText().replace(/\r?\n/g, "\r\n"));
|
|
1387
|
-
await sig.verify(signingKey.keyPacket, sig.signatureType, literal, now, true, config);
|
|
1388
|
-
return { verified: true, text: message.getText() };
|
|
1389
|
-
}
|
|
1390
|
-
return { verified: false, reason: "Message is not signed by this key", text: message.getText() };
|
|
1391
|
-
} catch (err) {
|
|
1392
|
-
return { verified: false, reason: err instanceof Error ? err.message : "Verification failed", text: "" };
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1395
|
-
var REVOCATION_REASON = {
|
|
1396
|
-
// 0 = "no reason specified", gpg's default: say nothing rather than "(reason: no reason given)"
|
|
1397
|
-
1: "replaced by a newer key",
|
|
1398
|
-
2: "compromised",
|
|
1399
|
-
3: "no longer used",
|
|
1400
|
-
32: "a user ID is no longer valid"
|
|
1401
|
-
};
|
|
1402
|
-
var iso = (d) => d instanceof Date ? d.toISOString() : typeof d === "number" && Number.isFinite(d) ? new Date(d).toISOString() : null;
|
|
1403
|
-
function revocationOf(sigs) {
|
|
1404
|
-
const sig = [...sigs ?? []].sort((a, b) => (b.created?.getTime?.() ?? 0) - (a.created?.getTime?.() ?? 0))[0];
|
|
1405
|
-
const flag = sig?.reasonForRevocationFlag ?? null;
|
|
1406
|
-
return {
|
|
1407
|
-
at: iso(sig?.created),
|
|
1408
|
-
compromised: flag === 2,
|
|
1409
|
-
revocationReason: sig?.reasonForRevocationString || (flag !== null ? REVOCATION_REASON[flag] ?? null : null)
|
|
1410
|
-
};
|
|
1411
|
-
}
|
|
1412
|
-
async function keyLife(openpgp, key, clearsigned, config) {
|
|
1413
|
-
const now = /* @__PURE__ */ new Date();
|
|
1414
|
-
let issuer = null;
|
|
1415
|
-
try {
|
|
1416
|
-
issuer = (await openpgp.readCleartextMessage({ cleartextMessage: clearsigned })).signature.packets[0]?.issuerKeyID ?? null;
|
|
1417
|
-
} catch {
|
|
1418
|
-
}
|
|
1419
|
-
const signer = issuer && !key.getKeyID().equals(issuer) ? key.subkeys.find((s) => s.getKeyID().equals(issuer)) ?? null : key;
|
|
1420
|
-
const signingKey = signer ? signer.getFingerprint().toUpperCase() : null;
|
|
1421
|
-
const safe = async (f, fallback) => {
|
|
1422
|
-
try {
|
|
1423
|
-
return await f();
|
|
1424
|
-
} catch {
|
|
1425
|
-
return fallback;
|
|
1426
|
-
}
|
|
1427
|
-
};
|
|
1428
|
-
const keyRevoked = await safe(() => key.isRevoked(void 0, void 0, now, config), false);
|
|
1429
|
-
const keyExp = await safe(() => key.getExpirationTime(void 0, config), null);
|
|
1430
|
-
const isSub = signer && signer !== key;
|
|
1431
|
-
const subRevoked = isSub ? await safe(() => signer.isRevoked(null, key.keyPacket, now, config), false) : false;
|
|
1432
|
-
const subExp = isSub ? await safe(() => signer.getExpirationTime(null, config), null) : null;
|
|
1433
|
-
const finite = (d) => d instanceof Date && Number.isFinite(d.getTime()) ? d : null;
|
|
1434
|
-
const kExp = finite(keyExp), sExp = finite(subExp);
|
|
1435
|
-
let problem = null;
|
|
1436
|
-
if (keyRevoked) {
|
|
1437
|
-
const r = revocationOf(key.revocationSignatures);
|
|
1438
|
-
problem = { kind: r.compromised ? "compromised" : "revoked", at: r.at, revocationReason: r.revocationReason };
|
|
1439
|
-
} else if (kExp && kExp <= now) {
|
|
1440
|
-
problem = { kind: "expired", at: kExp.toISOString() };
|
|
1441
|
-
} else if (subRevoked) {
|
|
1442
|
-
const r = revocationOf(signer.revocationSignatures);
|
|
1443
|
-
problem = { kind: r.compromised ? "compromised" : "signing-key-revoked", at: r.at, revocationReason: r.revocationReason };
|
|
1444
|
-
} else if (sExp && sExp <= now) {
|
|
1445
|
-
problem = { kind: "signing-key-expired", at: sExp.toISOString() };
|
|
1446
|
-
}
|
|
1447
|
-
const earliest = [kExp, sExp].filter(Boolean).sort((a, b) => a - b)[0];
|
|
1448
|
-
return { problem, signingKey, expiresAt: earliest ? earliest.toISOString() : null, algorithm: algorithmName(signer ?? key) };
|
|
1449
|
-
}
|
|
1450
|
-
async function verifyAttestation({
|
|
1451
|
-
pgpPublicKey,
|
|
1452
|
-
pgpSignature,
|
|
1453
|
-
fingerprint,
|
|
1454
|
-
ethAddress
|
|
1455
|
-
}) {
|
|
1456
|
-
try {
|
|
1457
|
-
if (!pgpPublicKey || !pgpSignature) {
|
|
1458
|
-
return { verified: false, reason: "Missing PGP data", kind: "bad-signature" };
|
|
1459
|
-
}
|
|
1460
|
-
const openpgp = await import("openpgp");
|
|
1461
|
-
const { readKey } = openpgp;
|
|
1462
|
-
const publicKey = await readKey({ armoredKey: pgpPublicKey });
|
|
1463
|
-
const keyFingerprint = publicKey.getFingerprint().toUpperCase();
|
|
1464
|
-
if (keyFingerprint !== fingerprint.toUpperCase()) {
|
|
1465
|
-
return { verified: false, reason: "Key fingerprint mismatch", kind: "bad-signature" };
|
|
1466
|
-
}
|
|
1467
|
-
const life = await keyLife(openpgp, publicKey, pgpSignature, pgpConfig(openpgp));
|
|
1468
|
-
const v = await verifyClearsigned({ armoredKey: pgpPublicKey, clearsigned: pgpSignature });
|
|
1469
|
-
if (!v.verified) {
|
|
1470
|
-
if (life.problem) return { verified: false, reason: v.reason, signingKey: life.signingKey, ...life.problem };
|
|
1471
|
-
if (/too weak|reject|unsupported|not supported|unknown (curve|algorithm)/i.test(v.reason || "")) {
|
|
1472
|
-
return { verified: false, reason: v.reason, kind: "unsupported", algorithm: life.algorithm, signingKey: life.signingKey };
|
|
2575
|
+
]
|
|
2576
|
+
},
|
|
2577
|
+
{
|
|
2578
|
+
"type": "error",
|
|
2579
|
+
"name": "InvalidFingerprint",
|
|
2580
|
+
"inputs": [
|
|
2581
|
+
{
|
|
2582
|
+
"name": "fingerprint",
|
|
2583
|
+
"type": "bytes",
|
|
2584
|
+
"internalType": "bytes"
|
|
1473
2585
|
}
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
}
|
|
1485
|
-
var EMAIL_UID = /@/;
|
|
1486
|
-
async function stripEmailUserIDs(armoredKey) {
|
|
1487
|
-
try {
|
|
1488
|
-
const { readKey } = await import("openpgp");
|
|
1489
|
-
const key = await readKey({ armoredKey });
|
|
1490
|
-
const kept = [];
|
|
1491
|
-
const removed = [];
|
|
1492
|
-
const users = key.users.filter((u) => {
|
|
1493
|
-
const uid = u.userID?.userID;
|
|
1494
|
-
if (!uid) return false;
|
|
1495
|
-
if (EMAIL_UID.test(uid)) {
|
|
1496
|
-
removed.push(uid);
|
|
1497
|
-
return false;
|
|
2586
|
+
]
|
|
2587
|
+
},
|
|
2588
|
+
{
|
|
2589
|
+
"type": "error",
|
|
2590
|
+
"name": "InvalidFingerprintLength",
|
|
2591
|
+
"inputs": [
|
|
2592
|
+
{
|
|
2593
|
+
"name": "length",
|
|
2594
|
+
"type": "uint256",
|
|
2595
|
+
"internalType": "uint256"
|
|
1498
2596
|
}
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
}
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
2597
|
+
]
|
|
2598
|
+
},
|
|
2599
|
+
{
|
|
2600
|
+
"type": "error",
|
|
2601
|
+
"name": "InvalidKindName",
|
|
2602
|
+
"inputs": [
|
|
2603
|
+
{
|
|
2604
|
+
"name": "kind",
|
|
2605
|
+
"type": "string",
|
|
2606
|
+
"internalType": "string"
|
|
2607
|
+
}
|
|
2608
|
+
]
|
|
2609
|
+
},
|
|
2610
|
+
{
|
|
2611
|
+
"type": "error",
|
|
2612
|
+
"name": "InvalidPermission",
|
|
2613
|
+
"inputs": []
|
|
2614
|
+
},
|
|
2615
|
+
{
|
|
2616
|
+
"type": "error",
|
|
2617
|
+
"name": "InvalidPointer",
|
|
2618
|
+
"inputs": []
|
|
2619
|
+
},
|
|
2620
|
+
{
|
|
2621
|
+
"type": "error",
|
|
2622
|
+
"name": "KeyCompromised",
|
|
2623
|
+
"inputs": [
|
|
2624
|
+
{
|
|
2625
|
+
"name": "fingerprint",
|
|
2626
|
+
"type": "bytes",
|
|
2627
|
+
"internalType": "bytes"
|
|
2628
|
+
}
|
|
2629
|
+
]
|
|
2630
|
+
},
|
|
2631
|
+
{
|
|
2632
|
+
"type": "error",
|
|
2633
|
+
"name": "KeyStillActive",
|
|
2634
|
+
"inputs": [
|
|
2635
|
+
{
|
|
2636
|
+
"name": "fingerprint",
|
|
2637
|
+
"type": "bytes",
|
|
2638
|
+
"internalType": "bytes"
|
|
2639
|
+
}
|
|
2640
|
+
]
|
|
2641
|
+
},
|
|
2642
|
+
{
|
|
2643
|
+
"type": "error",
|
|
2644
|
+
"name": "KeyTooLarge",
|
|
2645
|
+
"inputs": [
|
|
2646
|
+
{
|
|
2647
|
+
"name": "size",
|
|
2648
|
+
"type": "uint256",
|
|
2649
|
+
"internalType": "uint256"
|
|
2650
|
+
},
|
|
2651
|
+
{
|
|
2652
|
+
"name": "max",
|
|
2653
|
+
"type": "uint256",
|
|
2654
|
+
"internalType": "uint256"
|
|
2655
|
+
}
|
|
2656
|
+
]
|
|
2657
|
+
},
|
|
2658
|
+
{
|
|
2659
|
+
"type": "error",
|
|
2660
|
+
"name": "NotAKey",
|
|
2661
|
+
"inputs": [
|
|
2662
|
+
{
|
|
2663
|
+
"name": "firstByte",
|
|
2664
|
+
"type": "bytes1",
|
|
2665
|
+
"internalType": "bytes1"
|
|
2666
|
+
}
|
|
2667
|
+
]
|
|
2668
|
+
},
|
|
2669
|
+
{
|
|
2670
|
+
"type": "error",
|
|
2671
|
+
"name": "NotASignature",
|
|
2672
|
+
"inputs": [
|
|
2673
|
+
{
|
|
2674
|
+
"name": "firstByte",
|
|
2675
|
+
"type": "bytes1",
|
|
2676
|
+
"internalType": "bytes1"
|
|
2677
|
+
}
|
|
2678
|
+
]
|
|
2679
|
+
},
|
|
2680
|
+
{
|
|
2681
|
+
"type": "error",
|
|
2682
|
+
"name": "NotArmored",
|
|
2683
|
+
"inputs": []
|
|
2684
|
+
},
|
|
2685
|
+
{
|
|
2686
|
+
"type": "error",
|
|
2687
|
+
"name": "PayloadTooLarge",
|
|
2688
|
+
"inputs": [
|
|
2689
|
+
{
|
|
2690
|
+
"name": "size",
|
|
2691
|
+
"type": "uint256",
|
|
2692
|
+
"internalType": "uint256"
|
|
2693
|
+
},
|
|
2694
|
+
{
|
|
2695
|
+
"name": "max",
|
|
2696
|
+
"type": "uint256",
|
|
2697
|
+
"internalType": "uint256"
|
|
2698
|
+
}
|
|
2699
|
+
]
|
|
2700
|
+
},
|
|
2701
|
+
{
|
|
2702
|
+
"type": "error",
|
|
2703
|
+
"name": "PermissionExpired",
|
|
2704
|
+
"inputs": [
|
|
2705
|
+
{
|
|
2706
|
+
"name": "deadline",
|
|
2707
|
+
"type": "uint256",
|
|
2708
|
+
"internalType": "uint256"
|
|
2709
|
+
}
|
|
2710
|
+
]
|
|
2711
|
+
},
|
|
1552
2712
|
{
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
2713
|
+
"type": "error",
|
|
2714
|
+
"name": "RecordTooLarge",
|
|
2715
|
+
"inputs": [
|
|
2716
|
+
{
|
|
2717
|
+
"name": "size",
|
|
2718
|
+
"type": "uint256",
|
|
2719
|
+
"internalType": "uint256"
|
|
2720
|
+
},
|
|
2721
|
+
{
|
|
2722
|
+
"name": "max",
|
|
2723
|
+
"type": "uint256",
|
|
2724
|
+
"internalType": "uint256"
|
|
2725
|
+
}
|
|
2726
|
+
]
|
|
1557
2727
|
},
|
|
1558
2728
|
{
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
2729
|
+
"type": "error",
|
|
2730
|
+
"name": "SignatureTooLarge",
|
|
2731
|
+
"inputs": [
|
|
2732
|
+
{
|
|
2733
|
+
"name": "size",
|
|
2734
|
+
"type": "uint256",
|
|
2735
|
+
"internalType": "uint256"
|
|
2736
|
+
},
|
|
2737
|
+
{
|
|
2738
|
+
"name": "max",
|
|
2739
|
+
"type": "uint256",
|
|
2740
|
+
"internalType": "uint256"
|
|
2741
|
+
}
|
|
2742
|
+
]
|
|
1565
2743
|
},
|
|
1566
2744
|
{
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
// records like _thurin.example.com); rejects slashes, userinfo, and spaces.
|
|
1571
|
-
pattern: /^dns:([a-z0-9._-]+)\?type=TXT$/i,
|
|
1572
|
-
parse: (m) => ({ domain: m[1] })
|
|
2745
|
+
"type": "error",
|
|
2746
|
+
"name": "SupersededIsSetByReattest",
|
|
2747
|
+
"inputs": []
|
|
1573
2748
|
},
|
|
1574
2749
|
{
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
parse: (m) => ({ user: m[1], castHash: m[2] })
|
|
2750
|
+
"type": "error",
|
|
2751
|
+
"name": "TooManyClaims",
|
|
2752
|
+
"inputs": []
|
|
1579
2753
|
},
|
|
1580
2754
|
{
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
2755
|
+
"type": "error",
|
|
2756
|
+
"name": "UnknownRevokeReason",
|
|
2757
|
+
"inputs": [
|
|
2758
|
+
{
|
|
2759
|
+
"name": "reason",
|
|
2760
|
+
"type": "string",
|
|
2761
|
+
"internalType": "string"
|
|
2762
|
+
}
|
|
2763
|
+
]
|
|
1585
2764
|
},
|
|
1586
2765
|
{
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
// like "mastodon.social@evil.com" can't redirect the lookup elsewhere.
|
|
1591
|
-
pattern: /^https:\/\/([a-z0-9.-]+(?::\d+)?)\/@([^/@?#\s]+)$/i,
|
|
1592
|
-
parse: (m) => ({ instance: m[1], user: m[2] })
|
|
2766
|
+
"type": "error",
|
|
2767
|
+
"name": "UnsupportedHeader",
|
|
2768
|
+
"inputs": []
|
|
1593
2769
|
}
|
|
1594
2770
|
];
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
if (proof.url.startsWith("http")) return proof.url;
|
|
1620
|
-
return null;
|
|
1621
|
-
}
|
|
1622
|
-
function proofSecondaryHref(proof) {
|
|
1623
|
-
if (proof.provider === "github") return proof.url;
|
|
1624
|
-
if (proof.provider === "farcaster") return proof.url;
|
|
1625
|
-
if (proof.provider === "codeberg") return proof.url;
|
|
1626
|
-
if (proof.provider === "mastodon") return proof.url;
|
|
1627
|
-
return null;
|
|
1628
|
-
}
|
|
1629
|
-
var FPR_TOKEN = "OPENPGP4FPR:";
|
|
1630
|
-
function containsFingerprint(text, fingerprint) {
|
|
1631
|
-
return text.toUpperCase().includes(FPR_TOKEN + fingerprint.toUpperCase());
|
|
1632
|
-
}
|
|
1633
|
-
function containsFingerprintUrl(text, fingerprint) {
|
|
1634
|
-
return text.toUpperCase().includes(fingerprint.toUpperCase());
|
|
1635
|
-
}
|
|
1636
|
-
async function verifyGitHub(proof, fingerprint) {
|
|
1637
|
-
if (proof.repo) return verifyGitHubRepo(proof, fingerprint);
|
|
1638
|
-
try {
|
|
1639
|
-
const resp = await fetch(`https://api.github.com/gists/${proof.gistId}`);
|
|
1640
|
-
if (!resp.ok) return { verified: false, reason: `GitHub API returned ${resp.status}` };
|
|
1641
|
-
const data = await resp.json();
|
|
1642
|
-
if (data.owner?.login?.toLowerCase() !== proof.user.toLowerCase()) {
|
|
1643
|
-
return { verified: false, reason: "Gist owner does not match claimed user" };
|
|
1644
|
-
}
|
|
1645
|
-
for (const file of Object.values(data.files || {})) {
|
|
1646
|
-
if (file.content && containsFingerprint(file.content, fingerprint)) {
|
|
1647
|
-
return { verified: true };
|
|
1648
|
-
}
|
|
1649
|
-
}
|
|
1650
|
-
return { verified: false, reason: "Fingerprint token not found in gist" };
|
|
1651
|
-
} catch (err) {
|
|
1652
|
-
return { verified: false, reason: `GitHub fetch failed: ${err.message}` };
|
|
1653
|
-
}
|
|
1654
|
-
}
|
|
1655
|
-
async function verifyGitHubRepo(proof, fingerprint) {
|
|
1656
|
-
try {
|
|
1657
|
-
const resp = await fetch(
|
|
1658
|
-
`https://api.github.com/repos/${encodeURIComponent(proof.user)}/${encodeURIComponent(proof.repo)}`
|
|
1659
|
-
);
|
|
1660
|
-
if (!resp.ok) return { verified: false, reason: `GitHub API returned ${resp.status}` };
|
|
1661
|
-
const data = await resp.json();
|
|
1662
|
-
if (data.owner?.login?.toLowerCase() !== proof.user.toLowerCase()) {
|
|
1663
|
-
return { verified: false, reason: "Repository owner does not match claimed account" };
|
|
1664
|
-
}
|
|
1665
|
-
if (data.description && containsFingerprint(data.description, fingerprint)) {
|
|
1666
|
-
return { verified: true };
|
|
1667
|
-
}
|
|
1668
|
-
return { verified: false, reason: "Fingerprint token not found in repository description" };
|
|
1669
|
-
} catch (err) {
|
|
1670
|
-
return { verified: false, reason: `GitHub fetch failed: ${err.message}` };
|
|
1671
|
-
}
|
|
1672
|
-
}
|
|
1673
|
-
async function verifyDNS(proof, fingerprint) {
|
|
1674
|
-
try {
|
|
1675
|
-
const resp = await fetch(
|
|
1676
|
-
`https://cloudflare-dns.com/dns-query?name=${encodeURIComponent(proof.domain)}&type=TXT`,
|
|
1677
|
-
{ headers: { Accept: "application/dns-json" } }
|
|
1678
|
-
);
|
|
1679
|
-
if (!resp.ok) return { verified: false, reason: `DNS query returned ${resp.status}` };
|
|
1680
|
-
const data = await resp.json();
|
|
1681
|
-
for (const answer of data.Answer || []) {
|
|
1682
|
-
if (answer.type !== 16) continue;
|
|
1683
|
-
if (answer.data && containsFingerprint(answer.data, fingerprint)) {
|
|
1684
|
-
return { verified: true };
|
|
1685
|
-
}
|
|
1686
|
-
}
|
|
1687
|
-
return { verified: false, reason: "Fingerprint token not found in TXT records" };
|
|
1688
|
-
} catch (err) {
|
|
1689
|
-
return { verified: false, reason: `DNS fetch failed: ${err.message}` };
|
|
1690
|
-
}
|
|
1691
|
-
}
|
|
1692
|
-
var FARCASTER_HUB = "https://haatz.quilibrium.com";
|
|
1693
|
-
var NEYNAR_HUB = "https://hub-api.neynar.com";
|
|
1694
|
-
function farcasterSource(opts) {
|
|
1695
|
-
if (opts.farcasterHub) return { base: opts.farcasterHub.replace(/\/+$/, ""), headers: {} };
|
|
1696
|
-
if (opts.neynarApiKey) return { base: NEYNAR_HUB, headers: { "x-api-key": opts.neynarApiKey } };
|
|
1697
|
-
return { base: FARCASTER_HUB, headers: {} };
|
|
1698
|
-
}
|
|
1699
|
-
async function verifyFarcaster(proof, fingerprint, opts = {}) {
|
|
1700
|
-
const { base, headers } = farcasterSource(opts);
|
|
1701
|
-
const host = base.replace(/^https?:\/\//, "");
|
|
1702
|
-
try {
|
|
1703
|
-
const nameResp = await fetch(`${base}/v1/userNameProofByName?name=${encodeURIComponent(proof.user)}`, { headers });
|
|
1704
|
-
if (nameResp.status === 404) return { verified: false, reason: `Could not resolve Farcaster user "${proof.user}"` };
|
|
1705
|
-
if (!nameResp.ok) return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) returned ${nameResp.status}` };
|
|
1706
|
-
const fid = (await nameResp.json()).fid;
|
|
1707
|
-
if (!fid) return { verified: false, reason: `Could not resolve Farcaster user "${proof.user}"` };
|
|
1708
|
-
let pageToken = "";
|
|
1709
|
-
for (let page = 0; page < 5; page++) {
|
|
1710
|
-
const url = `${base}/v1/castsByFid?fid=${fid}&pageSize=100&reverse=true${pageToken ? `&pageToken=${pageToken}` : ""}`;
|
|
1711
|
-
const resp = await fetch(url, { headers });
|
|
1712
|
-
if (!resp.ok) return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) returned ${resp.status}` };
|
|
1713
|
-
const data = await resp.json();
|
|
1714
|
-
for (const msg of data.messages || []) {
|
|
1715
|
-
if (msg.hash && msg.hash.startsWith(proof.castHash)) {
|
|
1716
|
-
const text = msg.data?.castAddBody?.text || "";
|
|
1717
|
-
if (containsFingerprint(text, fingerprint)) {
|
|
1718
|
-
return { verified: true };
|
|
1719
|
-
}
|
|
1720
|
-
}
|
|
1721
|
-
}
|
|
1722
|
-
if (!data.nextPageToken) break;
|
|
1723
|
-
pageToken = data.nextPageToken;
|
|
1724
|
-
}
|
|
1725
|
-
return { verified: false, reason: "Cast not found" };
|
|
1726
|
-
} catch (err) {
|
|
1727
|
-
return { verified: false, reason: `Couldn't check: the Farcaster node (${host}) didn't answer (${err.message})` };
|
|
1728
|
-
}
|
|
1729
|
-
}
|
|
1730
|
-
async function verifyCodeberg(proof, fingerprint) {
|
|
1731
|
-
try {
|
|
1732
|
-
const resp = await fetch(
|
|
1733
|
-
`https://codeberg.org/api/v1/repos/${encodeURIComponent(proof.user)}/${encodeURIComponent(proof.repo)}`
|
|
1734
|
-
);
|
|
1735
|
-
if (!resp.ok) return { verified: false, reason: `Codeberg API returned ${resp.status}` };
|
|
1736
|
-
const data = await resp.json();
|
|
1737
|
-
if (data.description && containsFingerprint(data.description, fingerprint)) {
|
|
1738
|
-
return { verified: true };
|
|
1739
|
-
}
|
|
1740
|
-
return { verified: false, reason: "Fingerprint token not found in repo description" };
|
|
1741
|
-
} catch (err) {
|
|
1742
|
-
return { verified: false, reason: `Codeberg fetch failed: ${err.message}` };
|
|
1743
|
-
}
|
|
1744
|
-
}
|
|
1745
|
-
async function verifyMastodon(proof, fingerprint) {
|
|
1746
|
-
try {
|
|
1747
|
-
if (!proof.instance || !/^[a-z0-9.-]+(?::\d+)?$/i.test(proof.instance)) {
|
|
1748
|
-
return { verified: false, reason: "Invalid Mastodon instance host" };
|
|
1749
|
-
}
|
|
1750
|
-
const resp = await fetch(
|
|
1751
|
-
`https://${proof.instance}/api/v1/accounts/lookup?acct=${encodeURIComponent(proof.user)}`
|
|
1752
|
-
);
|
|
1753
|
-
if (!resp.ok) return { verified: false, reason: `Mastodon API returned ${resp.status}` };
|
|
1754
|
-
const data = await resp.json();
|
|
1755
|
-
const keyId = keyIdHex(fingerprint.replace(/^0x/i, "").replace(/\s+/g, "").toLowerCase());
|
|
1756
|
-
for (const field of data.fields || []) {
|
|
1757
|
-
const text = field.value.replace(/<[^>]*>/g, "");
|
|
1758
|
-
if (containsFingerprintUrl(text, fingerprint) || containsFingerprintUrl(text, keyId)) {
|
|
1759
|
-
return { verified: true };
|
|
1760
|
-
}
|
|
1761
|
-
}
|
|
1762
|
-
if (data.note) {
|
|
1763
|
-
const text = data.note.replace(/<[^>]*>/g, "");
|
|
1764
|
-
if (containsFingerprintUrl(text, fingerprint) || containsFingerprintUrl(text, keyId)) {
|
|
1765
|
-
return { verified: true };
|
|
1766
|
-
}
|
|
1767
|
-
}
|
|
1768
|
-
return { verified: false, reason: "Fingerprint not found in profile fields or bio" };
|
|
1769
|
-
} catch (err) {
|
|
1770
|
-
return { verified: false, reason: `Mastodon fetch failed: ${err.message}` };
|
|
1771
|
-
}
|
|
1772
|
-
}
|
|
1773
|
-
var verifiers = {
|
|
1774
|
-
github: verifyGitHub,
|
|
1775
|
-
dns: verifyDNS,
|
|
1776
|
-
farcaster: verifyFarcaster,
|
|
1777
|
-
codeberg: verifyCodeberg,
|
|
1778
|
-
mastodon: verifyMastodon
|
|
2771
|
+
|
|
2772
|
+
// src/core/contract.ts
|
|
2773
|
+
var REGISTRY_ADDRESS = "0xFa6956c11163517249f8A67F5560a4406B519451";
|
|
2774
|
+
var NETWORKS = {
|
|
2775
|
+
mainnet: {
|
|
2776
|
+
chainId: 1,
|
|
2777
|
+
address: REGISTRY_ADDRESS,
|
|
2778
|
+
explorerUrl: "https://etherscan.io",
|
|
2779
|
+
defaultRpcUrl: "https://ethereum.publicnode.com"
|
|
2780
|
+
// resolves on more networks than ethereum-rpc.publicnode.com
|
|
2781
|
+
},
|
|
2782
|
+
sepolia: {
|
|
2783
|
+
chainId: 11155111,
|
|
2784
|
+
address: REGISTRY_ADDRESS,
|
|
2785
|
+
explorerUrl: "https://sepolia.etherscan.io",
|
|
2786
|
+
defaultRpcUrl: "https://ethereum-sepolia.publicnode.com"
|
|
2787
|
+
},
|
|
2788
|
+
/** A local anvil instance (`anvil`, chain id 31337). Deploy with pgp-registry's Deploy script. */
|
|
2789
|
+
local: {
|
|
2790
|
+
chainId: 31337,
|
|
2791
|
+
address: REGISTRY_ADDRESS,
|
|
2792
|
+
explorerUrl: "",
|
|
2793
|
+
defaultRpcUrl: "http://127.0.0.1:8545"
|
|
2794
|
+
}
|
|
1779
2795
|
};
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
if (!fn) return { verified: false, reason: "Unknown provider" };
|
|
1783
|
-
return fn(proof, fingerprint, typeof opts === "string" ? { neynarApiKey: opts } : opts ?? {});
|
|
1784
|
-
}
|
|
1785
|
-
|
|
1786
|
-
// src/core/avatar.ts
|
|
1787
|
-
var IPFS_GATEWAYS = ["https://ipfs.filebase.io/ipfs/", "https://gateway.pinata.cloud/ipfs/"];
|
|
1788
|
-
var IPFS_GATEWAY = IPFS_GATEWAYS[0];
|
|
1789
|
-
var ARWEAVE_GATEWAY = "https://arweave.net/";
|
|
1790
|
-
function avatarUrl(uri) {
|
|
1791
|
-
if (typeof uri !== "string") return null;
|
|
1792
|
-
const u = uri.trim();
|
|
1793
|
-
if (/^data:image\/(png|jpe?g|gif|webp|avif|svg\+xml)[;,]/i.test(u)) return u;
|
|
1794
|
-
let m = u.match(/^ipfs:\/\/(?:ipfs\/)?([a-zA-Z0-9]{46,}(?:\/[^?#\s]*)?)$/);
|
|
1795
|
-
if (m) return IPFS_GATEWAY + m[1];
|
|
1796
|
-
m = u.match(/^ar:\/\/([a-zA-Z0-9_-]{43}(?:\/[^?#\s]*)?)$/);
|
|
1797
|
-
if (m) return ARWEAVE_GATEWAY + m[1];
|
|
1798
|
-
if (/^https:\/\/euc\.li\/[^?#\s]+$/.test(u)) return u;
|
|
1799
|
-
m = u.match(/^https:\/\/[^/]+\/ipfs\/([a-zA-Z0-9]{46,}(?:\/[^?#\s]*)?)$/);
|
|
1800
|
-
if (m) return IPFS_GATEWAY + m[1];
|
|
1801
|
-
return null;
|
|
2796
|
+
function isNetworkName(value) {
|
|
2797
|
+
return value === "mainnet" || value === "sepolia" || value === "local";
|
|
1802
2798
|
}
|
|
1803
|
-
function
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
2799
|
+
function getRegistry(network = "mainnet", addressOverride) {
|
|
2800
|
+
const r = NETWORKS[network];
|
|
2801
|
+
if (!r) throw new Error(`Unknown network: ${network}`);
|
|
2802
|
+
if (addressOverride && /^0x[0-9a-fA-F]{40}$/.test(addressOverride)) {
|
|
2803
|
+
return { ...r, address: addressOverride };
|
|
2804
|
+
}
|
|
2805
|
+
return r;
|
|
1807
2806
|
}
|
|
1808
|
-
function
|
|
1809
|
-
|
|
1810
|
-
|
|
2807
|
+
function chainFor(network = "mainnet") {
|
|
2808
|
+
if (network === "sepolia") return sepolia;
|
|
2809
|
+
if (network === "local") return foundry;
|
|
2810
|
+
return mainnet;
|
|
1811
2811
|
}
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
const
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
2812
|
+
|
|
2813
|
+
// src/core/claims.ts
|
|
2814
|
+
var CLAIM_LIMIT = 50;
|
|
2815
|
+
async function readClaims(client, owner, { registry = REGISTRY_ADDRESS, limit = CLAIM_LIMIT } = {}) {
|
|
2816
|
+
const read = (functionName, args) => client.readContract({ address: registry, abi: REGISTRY_ABI, functionName, args });
|
|
2817
|
+
const rows = await read("claimsOf", [owner]);
|
|
2818
|
+
const first = Math.max(0, rows.length - limit);
|
|
2819
|
+
return Promise.all(rows.map(async (row, index) => {
|
|
2820
|
+
const revokedAt = Number(row.revokedAt);
|
|
2821
|
+
const claim = {
|
|
2822
|
+
index,
|
|
2823
|
+
fingerprint: bytesToFingerprint(row.fingerprint),
|
|
2824
|
+
createdAt: Number(row.createdAt),
|
|
2825
|
+
revoked: revokedAt !== 0,
|
|
2826
|
+
revokedAt: revokedAt || null,
|
|
2827
|
+
state: row.state,
|
|
2828
|
+
replacedBy: row.state === "replaced" ? Number(row.replacedBy) : null,
|
|
2829
|
+
revokeReason: row.revokeReason,
|
|
2830
|
+
messageVersion: Number(row.messageVersion),
|
|
2831
|
+
pgpPublicKey: null,
|
|
2832
|
+
pgpSignature: null,
|
|
2833
|
+
verification: null
|
|
2834
|
+
};
|
|
2835
|
+
if (index < first) return claim;
|
|
2836
|
+
const bytes = (fn) => read(fn, [owner, BigInt(index)]).catch(() => null);
|
|
2837
|
+
const [keyHex, sigHex] = await Promise.all([bytes("keyBytes"), bytes("signatureBytes")]);
|
|
2838
|
+
claim.pgpPublicKey = keyHex ? await payloadText(keyHex, "key") : null;
|
|
2839
|
+
claim.pgpSignature = sigHex ? await payloadText(sigHex, "signature") : null;
|
|
2840
|
+
claim.verification = await verifyAttestation({
|
|
2841
|
+
pgpPublicKey: claim.pgpPublicKey,
|
|
2842
|
+
pgpSignature: claim.pgpSignature,
|
|
2843
|
+
fingerprint: claim.fingerprint,
|
|
2844
|
+
ethAddress: owner
|
|
2845
|
+
});
|
|
2846
|
+
return claim;
|
|
2847
|
+
}));
|
|
2848
|
+
}
|
|
2849
|
+
function keyStanding(claims) {
|
|
2850
|
+
const active = claims.filter((c) => !c.revoked);
|
|
2851
|
+
const verified = active.filter((c) => c.verification?.verified);
|
|
2852
|
+
if (verified.length) return { kind: "verified", claim: verified[verified.length - 1] };
|
|
2853
|
+
if (active.length) return { kind: "not-counted", claim: active[active.length - 1] };
|
|
2854
|
+
if (claims.length) return { kind: "inactive", claim: claims[claims.length - 1] };
|
|
2855
|
+
return { kind: "none", claim: null };
|
|
2856
|
+
}
|
|
2857
|
+
async function findOwners(client, key, { registry = REGISTRY_ADDRESS } = {}) {
|
|
2858
|
+
const read = (functionName, args) => client.readContract({ address: registry, abi: REGISTRY_ABI, functionName, args });
|
|
2859
|
+
let fingerprints;
|
|
2860
|
+
if ("keyId" in key) {
|
|
2861
|
+
const keyId = keyIdToBytes(key.keyId);
|
|
2862
|
+
if (!keyId) return [];
|
|
2863
|
+
fingerprints = (await read("fingerprintsForKeyId", [keyId])).map(bytesToFingerprint);
|
|
2864
|
+
} else {
|
|
2865
|
+
const fp = normalizeFingerprint(key.fingerprint);
|
|
2866
|
+
if (!fp) return [];
|
|
2867
|
+
fingerprints = [fp];
|
|
2868
|
+
}
|
|
2869
|
+
const found = [];
|
|
2870
|
+
for (const fingerprint of fingerprints) {
|
|
2871
|
+
for (const owner of await read("ownersOf", [fingerprintToBytes(fingerprint)])) {
|
|
2872
|
+
if (!found.some((f) => f.owner.toLowerCase() === owner.toLowerCase() && f.fingerprint === fingerprint)) found.push({ owner, fingerprint });
|
|
1833
2873
|
}
|
|
1834
|
-
if (!meta) return null;
|
|
1835
2874
|
}
|
|
1836
|
-
return
|
|
2875
|
+
return found;
|
|
1837
2876
|
}
|
|
1838
2877
|
|
|
1839
|
-
// src/core/
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
2878
|
+
// src/core/claimStatus.ts
|
|
2879
|
+
var CLAIM_CHECK_LABEL = {
|
|
2880
|
+
verified: "pgp verified",
|
|
2881
|
+
expired: "key expired",
|
|
2882
|
+
"signing-key-expired": "signing key expired",
|
|
2883
|
+
revoked: "key revoked",
|
|
2884
|
+
compromised: "key compromised",
|
|
2885
|
+
"signing-key-revoked": "signing key revoked",
|
|
2886
|
+
unsupported: "not supported",
|
|
2887
|
+
"bad-signature": "doesn't verify"
|
|
2888
|
+
};
|
|
2889
|
+
function formatClaimDate(isoOrSeconds) {
|
|
2890
|
+
if (isoOrSeconds === null || isoOrSeconds === void 0) return "an unknown date";
|
|
2891
|
+
const d = typeof isoOrSeconds === "number" ? new Date(isoOrSeconds * 1e3) : new Date(isoOrSeconds);
|
|
2892
|
+
if (Number.isNaN(d.getTime())) return "an unknown date";
|
|
2893
|
+
return d.toLocaleDateString("en-US", { month: "short", day: "numeric", year: "numeric", timeZone: "UTC" });
|
|
1851
2894
|
}
|
|
1852
|
-
function
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
return
|
|
2895
|
+
function shortFingerprint(fpr) {
|
|
2896
|
+
if (!fpr) return "unknown";
|
|
2897
|
+
const f = fpr.toUpperCase();
|
|
2898
|
+
return `${f.slice(0, 4)} ${f.slice(4, 8)} \u2026`;
|
|
1856
2899
|
}
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
];
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
2900
|
+
function claimCheckText(v, formatDate = formatClaimDate) {
|
|
2901
|
+
const kind = v.kind ?? (v.verified ? "verified" : "bad-signature");
|
|
2902
|
+
const at = formatDate(v.at);
|
|
2903
|
+
const t = (sentence, fix = null) => ({ kind, label: CLAIM_CHECK_LABEL[kind], sentence, fix });
|
|
2904
|
+
switch (kind) {
|
|
2905
|
+
case "verified":
|
|
2906
|
+
return t("The key signed a line naming this address, and the signature checks out.");
|
|
2907
|
+
case "expired":
|
|
2908
|
+
return t(
|
|
2909
|
+
`The key on this claim expired on ${at}, so the claim no longer counts.`,
|
|
2910
|
+
"Extend the key, then Update key. No new signature needed."
|
|
2911
|
+
);
|
|
2912
|
+
case "signing-key-expired":
|
|
2913
|
+
return t(
|
|
2914
|
+
`The subkey that signed this claim (${shortFingerprint(v.signingKey)}) expired on ${at}.`,
|
|
2915
|
+
"Extend that subkey, then Update key."
|
|
2916
|
+
);
|
|
2917
|
+
case "revoked":
|
|
2918
|
+
return t(
|
|
2919
|
+
`Its owner revoked this PGP key on ${at}${v.revocationReason ? ` (reason: ${v.revocationReason})` : ""}. The claim no longer counts.`,
|
|
2920
|
+
"Revoke this claim and attest your new key."
|
|
2921
|
+
);
|
|
2922
|
+
case "compromised":
|
|
2923
|
+
return t(
|
|
2924
|
+
`Its owner revoked this PGP key on ${at} as compromised. Don't trust anything it signed after that date.`,
|
|
2925
|
+
"Revoke this claim and attest a new key."
|
|
2926
|
+
);
|
|
2927
|
+
case "signing-key-revoked":
|
|
2928
|
+
return t(
|
|
2929
|
+
`The subkey that signed this claim was revoked on ${at}.`,
|
|
2930
|
+
"Replace the claim with a signature from a current key."
|
|
2931
|
+
);
|
|
2932
|
+
case "unsupported":
|
|
2933
|
+
return t(`This key uses an algorithm Thurin doesn't check${v.algorithm ? ` (${v.algorithm})` : ""}. The claim is on-chain, but no lookup will vouch for it.`);
|
|
2934
|
+
case "bad-signature":
|
|
2935
|
+
default:
|
|
2936
|
+
return t("The signature stored with this claim doesn't match its key, so nothing from this claim is shown.");
|
|
2937
|
+
}
|
|
1869
2938
|
}
|
|
1870
|
-
|
|
1871
|
-
const
|
|
1872
|
-
|
|
2939
|
+
function keyProblemText(v, formatDate = formatClaimDate) {
|
|
2940
|
+
const at = formatDate(v.at);
|
|
2941
|
+
const t = (kind, sentence, fix) => ({ kind, label: CLAIM_CHECK_LABEL[kind], sentence, fix });
|
|
2942
|
+
switch (v.kind) {
|
|
2943
|
+
case "expired":
|
|
2944
|
+
return t("expired", `This key expired on ${at}, so a claim with it wouldn't count.`, "Extend it (gpg --quick-set-expire), then export it again.");
|
|
2945
|
+
case "signing-key-expired":
|
|
2946
|
+
return t("signing-key-expired", `The subkey that signed (${shortFingerprint(v.signingKey)}) expired on ${at}.`, "Extend that subkey, or sign with a current one.");
|
|
2947
|
+
case "revoked":
|
|
2948
|
+
return t("revoked", `This key was revoked on ${at}${v.revocationReason ? ` (reason: ${v.revocationReason})` : ""}.`, "Use a current key.");
|
|
2949
|
+
case "compromised":
|
|
2950
|
+
return t("compromised", `This key was revoked as compromised on ${at}.`, "Use a new key.");
|
|
2951
|
+
case "signing-key-revoked":
|
|
2952
|
+
return t("signing-key-revoked", `The subkey that signed was revoked on ${at}.`, "Sign with a current subkey.");
|
|
2953
|
+
case "unsupported":
|
|
2954
|
+
return t("unsupported", `This key uses an algorithm Thurin doesn't check${v.algorithm ? ` (${v.algorithm})` : ""}, so no lookup would vouch for the claim.`, "Use an Ed25519 or RSA key.");
|
|
2955
|
+
default:
|
|
2956
|
+
return null;
|
|
2957
|
+
}
|
|
1873
2958
|
}
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
function registryDomain(chainId, verifyingContract) {
|
|
1880
|
-
return { name: EIP712_NAME, version: EIP712_VERSION, chainId, verifyingContract };
|
|
2959
|
+
function expiresSoon(v, now = Date.now(), withinDays = 30) {
|
|
2960
|
+
if (!v?.verified || !v.expiresAt) return null;
|
|
2961
|
+
const ms = new Date(v.expiresAt).getTime() - now;
|
|
2962
|
+
if (!Number.isFinite(ms) || ms < 0 || ms > withinDays * 864e5) return null;
|
|
2963
|
+
return { days: Math.floor(ms / 864e5), at: v.expiresAt };
|
|
1881
2964
|
}
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
{ name: "fingerprint", type: "bytes" },
|
|
1886
|
-
{ name: "pgpSignature", type: "bytes" },
|
|
1887
|
-
{ name: "pgpPublicKey", type: "bytes" },
|
|
1888
|
-
{ name: "nonce", type: "uint256" },
|
|
1889
|
-
{ name: "deadline", type: "uint256" }
|
|
1890
|
-
],
|
|
1891
|
-
Reattest: [
|
|
1892
|
-
{ name: "owner", type: "address" },
|
|
1893
|
-
{ name: "revokeIndex", type: "uint256" },
|
|
1894
|
-
{ name: "fingerprint", type: "bytes" },
|
|
1895
|
-
{ name: "pgpSignature", type: "bytes" },
|
|
1896
|
-
{ name: "pgpPublicKey", type: "bytes" },
|
|
1897
|
-
{ name: "nonce", type: "uint256" },
|
|
1898
|
-
{ name: "deadline", type: "uint256" }
|
|
1899
|
-
],
|
|
1900
|
-
UpdateKey: [
|
|
1901
|
-
{ name: "owner", type: "address" },
|
|
1902
|
-
{ name: "index", type: "uint256" },
|
|
1903
|
-
{ name: "pgpPublicKey", type: "bytes" },
|
|
1904
|
-
{ name: "nonce", type: "uint256" },
|
|
1905
|
-
{ name: "deadline", type: "uint256" }
|
|
1906
|
-
],
|
|
1907
|
-
Revoke: [
|
|
1908
|
-
{ name: "owner", type: "address" },
|
|
1909
|
-
{ name: "index", type: "uint256" },
|
|
1910
|
-
{ name: "nonce", type: "uint256" },
|
|
1911
|
-
{ name: "deadline", type: "uint256" }
|
|
1912
|
-
],
|
|
1913
|
-
SetRecord: [
|
|
1914
|
-
{ name: "owner", type: "address" },
|
|
1915
|
-
{ name: "index", type: "uint256" },
|
|
1916
|
-
{ name: "kind", type: "bytes32" },
|
|
1917
|
-
{ name: "value", type: "bytes" },
|
|
1918
|
-
{ name: "nonce", type: "uint256" },
|
|
1919
|
-
{ name: "deadline", type: "uint256" }
|
|
1920
|
-
]
|
|
1921
|
-
};
|
|
1922
|
-
function attestTypedData(chainId, registry, a) {
|
|
1923
|
-
return {
|
|
1924
|
-
domain: registryDomain(chainId, registry),
|
|
1925
|
-
types: { Attest: AUTHORIZATION_TYPES.Attest },
|
|
1926
|
-
primaryType: "Attest",
|
|
1927
|
-
message: {
|
|
1928
|
-
owner: a.owner,
|
|
1929
|
-
fingerprint: fingerprintToBytes(a.fingerprint),
|
|
1930
|
-
pgpSignature: stringToHex(a.pgpSignature),
|
|
1931
|
-
pgpPublicKey: stringToHex(a.pgpPublicKey),
|
|
1932
|
-
nonce: a.nonce,
|
|
1933
|
-
deadline: a.deadline
|
|
1934
|
-
}
|
|
1935
|
-
};
|
|
2965
|
+
function expiresSoonText(soon, formatDate = formatClaimDate) {
|
|
2966
|
+
const when = soon.days === 0 ? "today" : soon.days === 1 ? "in 1 day" : `in ${soon.days} days`;
|
|
2967
|
+
return `Key expires ${when} (${formatDate(soon.at)}).`;
|
|
1936
2968
|
}
|
|
1937
|
-
function
|
|
1938
|
-
const
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
2969
|
+
function claimFates(claims) {
|
|
2970
|
+
const out = /* @__PURE__ */ new Map();
|
|
2971
|
+
for (const c of claims) {
|
|
2972
|
+
if (c.state === "active" || !c.revokedAt) out.set(c.index, { state: "active" });
|
|
2973
|
+
else if (c.state === "replaced" && c.replacedBy !== null) out.set(c.index, { state: "replaced", at: c.revokedAt, by: c.replacedBy, ...c.revokeReason === "compromised" ? { compromised: true } : {} });
|
|
2974
|
+
else out.set(c.index, { state: "revoked", at: c.revokedAt, ...c.revokeReason ? { reason: c.revokeReason } : {} });
|
|
2975
|
+
}
|
|
2976
|
+
return out;
|
|
1945
2977
|
}
|
|
1946
|
-
function
|
|
1947
|
-
return {
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
2978
|
+
function claimFateText(fate, formatDate = (s) => formatClaimDate(s)) {
|
|
2979
|
+
if (fate.state === "revoked") return `Revoked by its owner on ${formatDate(fate.at)}${fate.reason ? ` (${fate.reason})` : ""}.`;
|
|
2980
|
+
if (fate.state === "replaced") return `Replaced by claim #${fate.by} on ${formatDate(fate.at)}.${fate.compromised ? " Its key was marked compromised." : ""}`;
|
|
2981
|
+
return null;
|
|
2982
|
+
}
|
|
2983
|
+
|
|
2984
|
+
// src/core/encrypt.ts
|
|
2985
|
+
async function encryptionKeyFor(claims, { now = Date.now(), recentDays = 7 } = {}) {
|
|
2986
|
+
const standing = keyStanding(claims);
|
|
2987
|
+
if (standing.kind === "none" || standing.kind === "inactive") return { ok: false, reason: "no-claim", claim: standing.claim };
|
|
2988
|
+
if (standing.kind === "not-counted" || !standing.claim.pgpPublicKey) return { ok: false, reason: "unverified", claim: standing.claim };
|
|
2989
|
+
const claim = standing.claim;
|
|
2990
|
+
try {
|
|
2991
|
+
const openpgp = await import("openpgp");
|
|
2992
|
+
const config = pgpConfig(openpgp);
|
|
2993
|
+
const key = await readKeyAny(openpgp, claim.pgpPublicKey);
|
|
2994
|
+
const date = new Date(now);
|
|
2995
|
+
const enc = await key.getEncryptionKey(void 0, date, void 0, config).catch(() => null);
|
|
2996
|
+
if (!enc) {
|
|
2997
|
+
let lastExpiry = null;
|
|
2998
|
+
for (const sk of key.subkeys) {
|
|
2999
|
+
const flags = newestSignature(sk.bindingSignatures)?.keyFlags?.[0] ?? 0;
|
|
3000
|
+
if (!(flags & 12) || await sk.isRevoked(void 0, void 0, date, config).catch(() => false)) continue;
|
|
3001
|
+
const exp2 = await sk.getExpirationTime(date, config).catch(() => null);
|
|
3002
|
+
if (typeof exp2 === "number" || exp2 instanceof Date) {
|
|
3003
|
+
const t = new Date(exp2).getTime();
|
|
3004
|
+
if (t <= now && (lastExpiry === null || t > lastExpiry)) lastExpiry = t;
|
|
3005
|
+
}
|
|
3006
|
+
}
|
|
3007
|
+
return lastExpiry !== null ? { ok: false, reason: "encryption-subkey-expired", claim, expired: new Date(lastExpiry).toISOString() } : { ok: false, reason: "no-encryption-subkey", claim };
|
|
3008
|
+
}
|
|
3009
|
+
const exp = await enc.getExpirationTime(date, config).catch(() => null);
|
|
3010
|
+
return {
|
|
3011
|
+
ok: true,
|
|
3012
|
+
claim,
|
|
3013
|
+
key: claim.pgpPublicKey,
|
|
3014
|
+
subkey: {
|
|
3015
|
+
fingerprint: enc.getFingerprint().toUpperCase(),
|
|
3016
|
+
algorithm: algorithmName(enc),
|
|
3017
|
+
expires: exp && exp !== Infinity ? new Date(exp).toISOString() : null
|
|
3018
|
+
},
|
|
3019
|
+
addedAt: claim.createdAt,
|
|
3020
|
+
changedRecently: now / 1e3 - claim.createdAt < recentDays * 86400
|
|
3021
|
+
};
|
|
3022
|
+
} catch {
|
|
3023
|
+
return { ok: false, reason: "unverified", claim };
|
|
3024
|
+
}
|
|
1953
3025
|
}
|
|
1954
|
-
function
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
1959
|
-
|
|
1960
|
-
|
|
3026
|
+
function encryptRefusalText(r, formatDate = formatClaimDate) {
|
|
3027
|
+
switch (r.reason) {
|
|
3028
|
+
case "no-claim":
|
|
3029
|
+
return "No claim counts for this identity, so there is no key to encrypt to.";
|
|
3030
|
+
case "unverified":
|
|
3031
|
+
return "Its newest claim doesn't verify, so its key can't be trusted with a message.";
|
|
3032
|
+
case "no-encryption-subkey":
|
|
3033
|
+
return "Its key has no encryption subkey, so it can sign but not receive.";
|
|
3034
|
+
case "encryption-subkey-expired":
|
|
3035
|
+
return `Its encryption subkey expired on ${formatDate(r.expired)}.`;
|
|
3036
|
+
}
|
|
1961
3037
|
}
|
|
1962
|
-
function
|
|
1963
|
-
return
|
|
1964
|
-
|
|
1965
|
-
|
|
1966
|
-
|
|
1967
|
-
message: { owner: a.owner, index: a.index, kind: a.kind, value: a.value, nonce: a.nonce, deadline: a.deadline }
|
|
1968
|
-
};
|
|
3038
|
+
function keyChangedText(k, now = Date.now()) {
|
|
3039
|
+
if (!k.changedRecently) return null;
|
|
3040
|
+
const days = Math.floor((now / 1e3 - k.addedAt) / 86400);
|
|
3041
|
+
const when = days <= 0 ? "today" : days === 1 ? "yesterday" : `${days} days ago`;
|
|
3042
|
+
return `This key was added ${when}. If you were expecting a different one, check with them another way.`;
|
|
1969
3043
|
}
|
|
1970
|
-
function
|
|
1971
|
-
|
|
3044
|
+
async function encryptTo(key, data, { filename, armor = typeof data === "string", hideRecipient = true, date = /* @__PURE__ */ new Date() } = {}) {
|
|
3045
|
+
const openpgp = await import("openpgp");
|
|
3046
|
+
const config = pgpConfig(openpgp);
|
|
3047
|
+
const encryptionKeys = await readKeyAny(openpgp, key);
|
|
3048
|
+
const message = typeof data === "string" ? await openpgp.createMessage({ text: data, date }) : await openpgp.createMessage({ binary: data, filename, format: "binary", date });
|
|
3049
|
+
const opts = { message, encryptionKeys, config, wildcard: hideRecipient, date };
|
|
3050
|
+
return armor ? await openpgp.encrypt({ ...opts, format: "armored" }) : await openpgp.encrypt({ ...opts, format: "binary" });
|
|
1972
3051
|
}
|
|
1973
|
-
|
|
1974
|
-
|
|
3052
|
+
|
|
3053
|
+
// src/core/contractErrors.ts
|
|
3054
|
+
var CONTRACT_ERROR_TEXT = {
|
|
3055
|
+
AlreadyRevoked: "That claim has already ended, or is already marked compromised.",
|
|
3056
|
+
BadArmorChecksum: "The armor's checksum doesn't match its data: the paste was changed or cut short.",
|
|
3057
|
+
BadBase64: "The armored data isn't valid base64.",
|
|
3058
|
+
ClaimActive: 'That claim is still active: revoke it with the reason "compromised" instead.',
|
|
3059
|
+
DeploymentFailed: "The key and signature couldn't be stored: together they're over the contract's size limit.",
|
|
3060
|
+
DuplicateActiveFingerprint: "This address already has an active claim for this key: update it or replace it instead.",
|
|
3061
|
+
EmptyKey: "No key was given.",
|
|
3062
|
+
EmptySignature: "No signature was given.",
|
|
3063
|
+
IndexOutOfBounds: "This address has no claim at that index.",
|
|
3064
|
+
InvalidFingerprint: "That 32-byte fingerprint ends in 12 zero bytes, which no real v6 key has.",
|
|
3065
|
+
InvalidFingerprintLength: "The fingerprint isn't 20 bytes (v4 key) or 32 bytes (v6 key).",
|
|
3066
|
+
InvalidKindName: "The record name is empty, longer than 31 bytes, or uses something other than a-z, 0-9, '-', and '.'.",
|
|
3067
|
+
InvalidPermission: "The permission isn't the owner's signature over exactly this change and their current nonce: it was already used, cancelled, or superseded by a newer one.",
|
|
3068
|
+
InvalidPointer: "There is no stored data at that address.",
|
|
3069
|
+
KeyCompromised: "This address marked the key compromised: it can never claim it again.",
|
|
3070
|
+
KeyStillActive: 'This address still has an active claim for the key: revoke that claim as "compromised" instead.',
|
|
3071
|
+
KeyTooLarge: "The key is over 16,384 bytes: export it without photos or old signatures.",
|
|
3072
|
+
NotAKey: "The key doesn't start with an OpenPGP public-key packet: give gpg's export of the public key.",
|
|
3073
|
+
NotASignature: "The signature is neither an OpenPGP signature nor a clearsigned message.",
|
|
3074
|
+
NotArmored: "The paste has no -----BEGIN PGP and -----END PGP lines.",
|
|
3075
|
+
PayloadTooLarge: "The signature and key together are over 24,000 bytes.",
|
|
3076
|
+
PermissionExpired: "The permission is past its deadline: ask the owner for a new one.",
|
|
3077
|
+
RecordTooLarge: "The record value is over 1,024 bytes.",
|
|
3078
|
+
SignatureTooLarge: "The signature is over 8,192 bytes.",
|
|
3079
|
+
SupersededIsSetByReattest: '"superseded" is set only by replacing a claim: pick another reason.',
|
|
3080
|
+
TooManyClaims: "This address has reached the limit of 65,535 claims.",
|
|
3081
|
+
UnknownRevokeReason: 'The revoke reason must be empty, "compromised", "retired", or "other".',
|
|
3082
|
+
UnsupportedHeader: "Armor header lines (like Comment:) can't be told from the data, as when line breaks were lost: remove them."
|
|
3083
|
+
};
|
|
3084
|
+
function contractErrorName(err) {
|
|
3085
|
+
for (let e = err, depth = 0; e && depth < 8; e = e.cause, depth++) {
|
|
3086
|
+
const name = e.data?.errorName ?? e.errorName;
|
|
3087
|
+
if (typeof name === "string" && name) return name;
|
|
3088
|
+
}
|
|
3089
|
+
return null;
|
|
3090
|
+
}
|
|
3091
|
+
function contractErrorText(err) {
|
|
3092
|
+
const name = contractErrorName(err);
|
|
3093
|
+
return name ? CONTRACT_ERROR_TEXT[name] ?? `The registry refused it (${name}).` : null;
|
|
1975
3094
|
}
|
|
1976
3095
|
|
|
1977
3096
|
// src/core/records.ts
|
|
1978
|
-
import { hexToString, stringToHex as stringToHex2 } from "viem";
|
|
1979
3097
|
var MAX_RECORD_BYTES = 1024;
|
|
3098
|
+
var MAX_KIND_BYTES = 31;
|
|
1980
3099
|
function kindName(input) {
|
|
1981
3100
|
return input.includes(".") ? input : `thurin.${input}`;
|
|
1982
3101
|
}
|
|
3102
|
+
function checkKindName(input) {
|
|
3103
|
+
if (!/^[a-z0-9.-]+$/.test(input)) throw new Error(`Record name "${input}": use a-z, 0-9, "-" and "." only`);
|
|
3104
|
+
const name = kindName(input);
|
|
3105
|
+
if (name.length > MAX_KIND_BYTES) throw new Error(`Record name "${name}" is ${name.length} bytes; the registry allows ${MAX_KIND_BYTES}`);
|
|
3106
|
+
return name;
|
|
3107
|
+
}
|
|
3108
|
+
function checkRecordValue(value) {
|
|
3109
|
+
const bytes = new TextEncoder().encode(value).length;
|
|
3110
|
+
if (bytes > MAX_RECORD_BYTES) throw new Error(`Record is ${bytes} bytes; the registry accepts up to ${MAX_RECORD_BYTES}`);
|
|
3111
|
+
return value;
|
|
3112
|
+
}
|
|
1983
3113
|
var KNOWN_KINDS = [
|
|
1984
3114
|
"thurin.railgun",
|
|
1985
3115
|
"thurin.security",
|
|
1986
3116
|
"thurin.successor",
|
|
1987
3117
|
"thurin.affiliation",
|
|
1988
3118
|
"thurin.canary",
|
|
1989
|
-
"thurin.
|
|
1990
|
-
"thurin.disclosure",
|
|
1991
|
-
"thurin.pointer"
|
|
1992
|
-
];
|
|
1993
|
-
var IDENTITY_KINDS = [
|
|
1994
|
-
"thurin.railgun",
|
|
1995
|
-
"thurin.security",
|
|
1996
|
-
"thurin.successor",
|
|
1997
|
-
"thurin.affiliation",
|
|
1998
|
-
"thurin.canary",
|
|
3119
|
+
"thurin.releases",
|
|
1999
3120
|
"thurin.private",
|
|
2000
3121
|
"thurin.disclosure"
|
|
2001
3122
|
];
|
|
2002
|
-
function
|
|
2003
|
-
const bytes = new TextEncoder().encode(value).length;
|
|
2004
|
-
if (bytes > MAX_RECORD_BYTES) throw new Error(`Record is ${bytes} bytes; the registry accepts up to ${MAX_RECORD_BYTES}`);
|
|
2005
|
-
return stringToHex2(value);
|
|
2006
|
-
}
|
|
2007
|
-
function decodeRecord(hex) {
|
|
2008
|
-
return hex === "0x" ? "" : hexToString(hex);
|
|
2009
|
-
}
|
|
2010
|
-
function parsePointer(text) {
|
|
3123
|
+
function parseReleases(text) {
|
|
2011
3124
|
const p = JSON.parse(text);
|
|
2012
|
-
if (p?.v !== 1 || !Array.isArray(p.releases)) throw new Error("Not a v1 thurin.
|
|
3125
|
+
if (p?.v !== 1 || !Array.isArray(p.releases)) throw new Error("Not a v1 thurin.releases record");
|
|
2013
3126
|
return p;
|
|
2014
3127
|
}
|
|
2015
|
-
function
|
|
3128
|
+
function addRelease(existing, entry) {
|
|
2016
3129
|
if (!/^[0-9a-f]{64}$/i.test(entry.sha256)) throw new Error("sha256 must be 64 hex characters");
|
|
2017
3130
|
const releases = [entry, ...(existing?.releases ?? []).filter((r) => r.name !== entry.name)];
|
|
2018
3131
|
const dropped = [];
|
|
@@ -2020,7 +3133,7 @@ function addPointer(existing, entry) {
|
|
|
2020
3133
|
while (new TextEncoder().encode(JSON.stringify(record)).length > MAX_RECORD_BYTES && releases.length > 1) dropped.push(releases.pop());
|
|
2021
3134
|
return { record, dropped };
|
|
2022
3135
|
}
|
|
2023
|
-
function
|
|
3136
|
+
function renderReleases(p) {
|
|
2024
3137
|
return p.releases.map((r) => `${r.name.padEnd(22)} ${r.date} sha256 ${r.sha256}${r.url ? ` ${r.url}` : ""}`).join("\n");
|
|
2025
3138
|
}
|
|
2026
3139
|
var RAILGUN_ADDRESS = /^0zk1[qpzry9x8gf2tvdw0s3jn54khce6mua7l]{60,200}$/;
|
|
@@ -2080,186 +3193,214 @@ async function parseRecord(kind, text, opts = {}) {
|
|
|
2080
3193
|
}
|
|
2081
3194
|
return { ...base, valid: true, data: { type: "encrypted", recipients } };
|
|
2082
3195
|
}
|
|
2083
|
-
case "thurin.
|
|
3196
|
+
case "thurin.releases": {
|
|
2084
3197
|
try {
|
|
2085
|
-
return { ...base, valid: true, data: { type: "
|
|
3198
|
+
return { ...base, valid: true, data: { type: "releases", releases: parseReleases(t).releases } };
|
|
2086
3199
|
} catch {
|
|
2087
|
-
return invalid(
|
|
3200
|
+
return invalid('Not a v1 release list: expected {"v":1,"releases":[\u2026]}');
|
|
2088
3201
|
}
|
|
2089
3202
|
}
|
|
2090
3203
|
default:
|
|
2091
3204
|
return { ...base, valid: true, data: { type: "text" } };
|
|
2092
3205
|
}
|
|
2093
3206
|
}
|
|
2094
|
-
|
|
3207
|
+
function pickRecords(names, values, kinds = KNOWN_KINDS) {
|
|
3208
|
+
const all = names.map((kind, i) => ({ kind, text: values[i] ?? "" })).filter((r) => r.text);
|
|
3209
|
+
if (!kinds) return all;
|
|
3210
|
+
return kinds.flatMap((k) => all.filter((r) => r.kind === k));
|
|
3211
|
+
}
|
|
3212
|
+
function pageRecords(names, values) {
|
|
3213
|
+
const all = pickRecords(names, values, null);
|
|
3214
|
+
const ours = KNOWN_KINDS.flatMap((k) => all.filter((r) => r.kind === k));
|
|
3215
|
+
return [...ours, ...all.filter((r) => !KNOWN_KINDS.includes(r.kind))];
|
|
3216
|
+
}
|
|
3217
|
+
async function fetchRecords(client, registry, abi, owner, index, kinds = KNOWN_KINDS, opts = {}) {
|
|
3218
|
+
const [names, values] = await client.readContract({ address: registry, abi, functionName: "recordsOf", args: [owner, BigInt(index)] });
|
|
2095
3219
|
const out = [];
|
|
2096
|
-
for (const
|
|
2097
|
-
const hex = await client.readContract({ address: registry, abi, functionName: "record", args: [owner, BigInt(index), recordKind(kind)] });
|
|
2098
|
-
const text = decodeRecord(hex);
|
|
2099
|
-
if (text) out.push(await parseRecord(kind, text, opts));
|
|
2100
|
-
}
|
|
3220
|
+
for (const r of pickRecords(names, values, kinds)) out.push(await parseRecord(r.kind, r.text, opts));
|
|
2101
3221
|
return out;
|
|
2102
3222
|
}
|
|
2103
3223
|
|
|
2104
|
-
// src/core/
|
|
2105
|
-
var
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
};
|
|
2115
|
-
|
|
2116
|
-
if (
|
|
2117
|
-
|
|
2118
|
-
if (
|
|
2119
|
-
return
|
|
3224
|
+
// src/core/avatar.ts
|
|
3225
|
+
var IPFS_GATEWAYS = ["https://ipfs.filebase.io/ipfs/", "https://gateway.pinata.cloud/ipfs/"];
|
|
3226
|
+
var IPFS_GATEWAY = IPFS_GATEWAYS[0];
|
|
3227
|
+
var ARWEAVE_GATEWAY = "https://arweave.net/";
|
|
3228
|
+
function avatarUrl(uri) {
|
|
3229
|
+
if (typeof uri !== "string") return null;
|
|
3230
|
+
const u = uri.trim();
|
|
3231
|
+
if (/^data:image\/(png|jpe?g|gif|webp|avif|svg\+xml)[;,]/i.test(u)) return u;
|
|
3232
|
+
let m = u.match(/^ipfs:\/\/(?:ipfs\/)?([a-zA-Z0-9]{46,}(?:\/[^?#\s]*)?)$/);
|
|
3233
|
+
if (m) return IPFS_GATEWAY + m[1];
|
|
3234
|
+
m = u.match(/^ar:\/\/([a-zA-Z0-9_-]{43}(?:\/[^?#\s]*)?)$/);
|
|
3235
|
+
if (m) return ARWEAVE_GATEWAY + m[1];
|
|
3236
|
+
if (/^https:\/\/euc\.li\/[^?#\s]+$/.test(u)) return u;
|
|
3237
|
+
m = u.match(/^https:\/\/[^/]+\/ipfs\/([a-zA-Z0-9]{46,}(?:\/[^?#\s]*)?)$/);
|
|
3238
|
+
if (m) return IPFS_GATEWAY + m[1];
|
|
3239
|
+
return null;
|
|
2120
3240
|
}
|
|
2121
|
-
function
|
|
2122
|
-
if (!
|
|
2123
|
-
const
|
|
2124
|
-
return
|
|
3241
|
+
function avatarFallbacks(url) {
|
|
3242
|
+
if (!url || !url.startsWith(IPFS_GATEWAYS[0])) return [];
|
|
3243
|
+
const path = url.slice(IPFS_GATEWAYS[0].length);
|
|
3244
|
+
return IPFS_GATEWAYS.slice(1).map((g) => g + path);
|
|
2125
3245
|
}
|
|
2126
|
-
function
|
|
2127
|
-
const
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2143
|
-
|
|
2144
|
-
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
|
|
2148
|
-
|
|
2149
|
-
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
case "signing-key-revoked":
|
|
2154
|
-
return t(
|
|
2155
|
-
`The subkey that signed this claim was revoked on ${at}.`,
|
|
2156
|
-
"Replace the claim with a signature from a current key."
|
|
2157
|
-
);
|
|
2158
|
-
case "unsupported":
|
|
2159
|
-
return t(`This key uses an algorithm Thurin doesn't check${v.algorithm ? ` (${v.algorithm})` : ""}. The claim is on-chain, but no lookup will vouch for it.`);
|
|
2160
|
-
case "bad-signature":
|
|
2161
|
-
default:
|
|
2162
|
-
return t("The signature stored with this claim doesn't match its key, so nothing from this claim is shown.");
|
|
3246
|
+
function parseNftAvatar(raw) {
|
|
3247
|
+
const m = typeof raw === "string" && raw.trim().match(/^eip155:(\d+)\/(erc721|erc1155):(0x[0-9a-fA-F]{40})\/(\d+)$/i);
|
|
3248
|
+
return m ? { chainId: Number(m[1]), standard: m[2].toLowerCase(), contract: m[3], tokenId: BigInt(m[4]) } : null;
|
|
3249
|
+
}
|
|
3250
|
+
var NFT_AVATAR_ABI = [
|
|
3251
|
+
{ type: "function", name: "tokenURI", stateMutability: "view", inputs: [{ type: "uint256" }], outputs: [{ type: "string" }] },
|
|
3252
|
+
{ type: "function", name: "uri", stateMutability: "view", inputs: [{ type: "uint256" }], outputs: [{ type: "string" }] }
|
|
3253
|
+
];
|
|
3254
|
+
async function nftAvatarImage(tokenUri, tokenId, fetchFn = fetch) {
|
|
3255
|
+
const uri = tokenUri.replace("{id}", tokenId.toString(16).padStart(64, "0"));
|
|
3256
|
+
let meta;
|
|
3257
|
+
const inline = uri.match(/^data:application\/json(;base64)?,([\s\S]*)$/);
|
|
3258
|
+
if (inline) meta = JSON.parse(inline[1] ? atob(inline[2]) : decodeURIComponent(inline[2]));
|
|
3259
|
+
else {
|
|
3260
|
+
const url = uri.startsWith("data:") ? null : avatarUrl(uri);
|
|
3261
|
+
if (!url) return null;
|
|
3262
|
+
for (const candidate of [url, ...avatarFallbacks(url)]) {
|
|
3263
|
+
try {
|
|
3264
|
+
const res = await fetchFn(candidate);
|
|
3265
|
+
if (res.ok) {
|
|
3266
|
+
meta = await res.json();
|
|
3267
|
+
break;
|
|
3268
|
+
}
|
|
3269
|
+
} catch {
|
|
3270
|
+
}
|
|
3271
|
+
}
|
|
3272
|
+
if (!meta) return null;
|
|
2163
3273
|
}
|
|
3274
|
+
return avatarUrl(meta?.image ?? meta?.image_url);
|
|
2164
3275
|
}
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2168
|
-
|
|
2169
|
-
|
|
3276
|
+
|
|
3277
|
+
// src/core/ensHint.ts
|
|
3278
|
+
import { namehash, normalize } from "viem/ens";
|
|
3279
|
+
var ENS_HINT_KEY = "id.thurin";
|
|
3280
|
+
function ensHintFor(record, verifiedFingerprint) {
|
|
3281
|
+
const raw = record?.trim() ?? "";
|
|
3282
|
+
const expected = verifiedFingerprint ? normalizeFingerprint(verifiedFingerprint) : null;
|
|
3283
|
+
if (!raw) return { key: ENS_HINT_KEY, state: "unset", record: null, fingerprint: null, expected };
|
|
3284
|
+
const fingerprint = normalizeFingerprint(raw);
|
|
3285
|
+
if (!fingerprint) return { key: ENS_HINT_KEY, state: "mismatch", record: raw, fingerprint: null, expected, reason: "The record is not a PGP fingerprint" };
|
|
3286
|
+
if (!expected) return { key: ENS_HINT_KEY, state: "mismatch", record: raw, fingerprint, expected, reason: "The address has no verified claim" };
|
|
3287
|
+
if (fingerprint === expected) return { key: ENS_HINT_KEY, state: "match", record: raw, fingerprint, expected };
|
|
3288
|
+
return { key: ENS_HINT_KEY, state: "mismatch", record: raw, fingerprint, expected, reason: "The record names a key this address has not claimed" };
|
|
2170
3289
|
}
|
|
2171
|
-
function
|
|
2172
|
-
const
|
|
2173
|
-
|
|
3290
|
+
function ensHintValue(fingerprint) {
|
|
3291
|
+
const fp = normalizeFingerprint(fingerprint);
|
|
3292
|
+
if (!fp) throw new Error(`Not a fingerprint: ${fingerprint}`);
|
|
3293
|
+
return fp.toUpperCase();
|
|
2174
3294
|
}
|
|
2175
|
-
|
|
2176
|
-
|
|
2177
|
-
|
|
2178
|
-
|
|
2179
|
-
|
|
2180
|
-
|
|
2181
|
-
|
|
2182
|
-
|
|
2183
|
-
|
|
2184
|
-
|
|
2185
|
-
|
|
3295
|
+
var ENS_TEXT_RESOLVER_ABI = [
|
|
3296
|
+
{ type: "function", name: "text", stateMutability: "view", inputs: [{ name: "node", type: "bytes32" }, { name: "key", type: "string" }], outputs: [{ name: "", type: "string" }] },
|
|
3297
|
+
{ type: "function", name: "setText", stateMutability: "nonpayable", inputs: [{ name: "node", type: "bytes32" }, { name: "key", type: "string" }, { name: "value", type: "string" }], outputs: [] }
|
|
3298
|
+
];
|
|
3299
|
+
function ensHintWrite(name, fingerprint) {
|
|
3300
|
+
const normalized = normalize(name);
|
|
3301
|
+
return {
|
|
3302
|
+
abi: ENS_TEXT_RESOLVER_ABI,
|
|
3303
|
+
functionName: "setText",
|
|
3304
|
+
args: [namehash(normalized), ENS_HINT_KEY, ensHintValue(fingerprint)],
|
|
3305
|
+
name: normalized
|
|
3306
|
+
};
|
|
2186
3307
|
}
|
|
2187
|
-
function
|
|
2188
|
-
|
|
2189
|
-
|
|
2190
|
-
return null;
|
|
3308
|
+
async function fetchEnsHint(client, name, verifiedFingerprint) {
|
|
3309
|
+
const record = await client.getEnsText({ name: normalize(name), key: ENS_HINT_KEY });
|
|
3310
|
+
return ensHintFor(record, verifiedFingerprint);
|
|
2191
3311
|
}
|
|
2192
3312
|
|
|
2193
3313
|
export {
|
|
2194
|
-
REGISTRY_ABI,
|
|
2195
|
-
REGISTRY_ADDRESS,
|
|
2196
|
-
NETWORKS,
|
|
2197
|
-
isNetworkName,
|
|
2198
|
-
getRegistry,
|
|
2199
|
-
chainFor,
|
|
2200
|
-
needsRpcProbe,
|
|
2201
|
-
identityErrorKind,
|
|
2202
|
-
IDENTITY_ERROR_TEXT,
|
|
2203
3314
|
normalizeFingerprint,
|
|
3315
|
+
sameFingerprint,
|
|
2204
3316
|
fingerprintToBytes,
|
|
2205
3317
|
bytesToFingerprint,
|
|
2206
3318
|
fingerprintHash,
|
|
2207
3319
|
keyIdOf,
|
|
2208
3320
|
keyIdToBytes,
|
|
2209
|
-
parsePgpKey,
|
|
2210
|
-
verifyClearsigned,
|
|
2211
|
-
verifyAttestation,
|
|
2212
|
-
stripEmailUserIDs,
|
|
2213
|
-
hasEmailUserID,
|
|
2214
|
-
fetchEFPGraph,
|
|
2215
3321
|
identifyProof,
|
|
2216
3322
|
displayUrl,
|
|
2217
3323
|
proofHref,
|
|
2218
3324
|
proofSecondaryHref,
|
|
2219
3325
|
FARCASTER_HUB,
|
|
2220
3326
|
verifyProof,
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
ensHintWrite,
|
|
2234
|
-
fetchEnsHint,
|
|
3327
|
+
statementText,
|
|
3328
|
+
parsePgpKey,
|
|
3329
|
+
verifyClearsigned,
|
|
3330
|
+
verifyStatementSignature,
|
|
3331
|
+
verifyAttestation,
|
|
3332
|
+
stripEmailUserIDs,
|
|
3333
|
+
hasEmailUserID,
|
|
3334
|
+
leanKey,
|
|
3335
|
+
claimSignature,
|
|
3336
|
+
signatureEmail,
|
|
3337
|
+
payloadText,
|
|
3338
|
+
sshKeys,
|
|
2235
3339
|
EIP712_NAME,
|
|
2236
3340
|
EIP712_VERSION,
|
|
2237
3341
|
registryDomain,
|
|
2238
3342
|
AUTHORIZATION_TYPES,
|
|
3343
|
+
REVOKE_REASONS,
|
|
3344
|
+
OWNER_REVOKE_REASONS,
|
|
2239
3345
|
attestTypedData,
|
|
2240
3346
|
reattestTypedData,
|
|
2241
3347
|
updateKeyTypedData,
|
|
2242
3348
|
revokeTypedData,
|
|
2243
3349
|
setRecordTypedData,
|
|
3350
|
+
markCompromisedTypedData,
|
|
2244
3351
|
authorizationDigest,
|
|
3352
|
+
permissionSigned,
|
|
2245
3353
|
recordKind,
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
fetchRecords,
|
|
3354
|
+
REGISTRY_ABI,
|
|
3355
|
+
REGISTRY_ADDRESS,
|
|
3356
|
+
NETWORKS,
|
|
3357
|
+
isNetworkName,
|
|
3358
|
+
getRegistry,
|
|
3359
|
+
chainFor,
|
|
3360
|
+
CLAIM_LIMIT,
|
|
3361
|
+
readClaims,
|
|
3362
|
+
keyStanding,
|
|
3363
|
+
findOwners,
|
|
2257
3364
|
CLAIM_CHECK_LABEL,
|
|
2258
3365
|
formatClaimDate,
|
|
2259
3366
|
claimCheckText,
|
|
3367
|
+
keyProblemText,
|
|
2260
3368
|
expiresSoon,
|
|
2261
3369
|
expiresSoonText,
|
|
2262
3370
|
claimFates,
|
|
2263
|
-
claimFateText
|
|
3371
|
+
claimFateText,
|
|
3372
|
+
encryptionKeyFor,
|
|
3373
|
+
encryptRefusalText,
|
|
3374
|
+
keyChangedText,
|
|
3375
|
+
encryptTo,
|
|
3376
|
+
CONTRACT_ERROR_TEXT,
|
|
3377
|
+
contractErrorName,
|
|
3378
|
+
contractErrorText,
|
|
3379
|
+
MAX_RECORD_BYTES,
|
|
3380
|
+
MAX_KIND_BYTES,
|
|
3381
|
+
kindName,
|
|
3382
|
+
checkKindName,
|
|
3383
|
+
checkRecordValue,
|
|
3384
|
+
KNOWN_KINDS,
|
|
3385
|
+
parseReleases,
|
|
3386
|
+
addRelease,
|
|
3387
|
+
renderReleases,
|
|
3388
|
+
parseRecord,
|
|
3389
|
+
pickRecords,
|
|
3390
|
+
pageRecords,
|
|
3391
|
+
fetchRecords,
|
|
3392
|
+
IPFS_GATEWAYS,
|
|
3393
|
+
IPFS_GATEWAY,
|
|
3394
|
+
ARWEAVE_GATEWAY,
|
|
3395
|
+
avatarUrl,
|
|
3396
|
+
avatarFallbacks,
|
|
3397
|
+
parseNftAvatar,
|
|
3398
|
+
NFT_AVATAR_ABI,
|
|
3399
|
+
nftAvatarImage,
|
|
3400
|
+
ENS_HINT_KEY,
|
|
3401
|
+
ensHintFor,
|
|
3402
|
+
ensHintValue,
|
|
3403
|
+
ENS_TEXT_RESOLVER_ABI,
|
|
3404
|
+
ensHintWrite,
|
|
3405
|
+
fetchEnsHint
|
|
2264
3406
|
};
|
|
2265
|
-
//# sourceMappingURL=chunk-ZAQ2EPF5.js.map
|