@seams/wallet-server 0.6.0 → 0.6.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/artifact-manifest.json +1 -1
- package/cloudflare-router-ab/scripts/initialize-local-wallet.mjs +9 -1
- package/cloudflare-router-ab/scripts/start-local-role-workers.mjs +15 -9
- package/cloudflare-router-ab/scripts/start-local-wallet-system.mjs +44 -15
- package/dist/esm/router/cloudflare/d1/auth/d1RouterApiAuthService.js +1 -0
- package/dist/esm/router/cloudflare/d1/registration/d1WalletRegistrationService.js +195 -50
- package/dist/esm/router/cloudflare.js +227 -30
- package/dist/esm/router/domains/walletRegistration/walletRegistrationRoutes.js +166 -29
- package/dist/esm/router/express.js +227 -30
- package/dist/esm/router/framework/routeDefinitions.js +4 -0
- package/dist/esm/router/transport/fetch/routes/walletRegistration.js +59 -3
- package/dist/types/wallet-server/src/core/threeRouteRegistrationContracts.d.ts +31 -2
- package/dist/types/wallet-server/src/router/cloudflare/d1/registration/d1WalletRegistrationService.d.ts +7 -5
- package/dist/types/wallet-server/src/router/domains/walletRegistration/walletRegistrationInputs.d.ts +7 -0
- package/dist/types/wallet-server/src/router/domains/walletRegistration/walletRegistrationRoutes.d.ts +4 -1
- package/dist/types/wallet-server/src/router/framework/authServicePort.d.ts +6 -5
- package/package.json +1 -1
package/artifact-manifest.json
CHANGED
|
@@ -12,6 +12,14 @@ if (!existsSync(binary)) {
|
|
|
12
12
|
}
|
|
13
13
|
// Package managers can normalize executable bits on bundled native assets.
|
|
14
14
|
if ((statSync(binary).mode & 0o111) === 0) chmodSync(binary, 0o755);
|
|
15
|
-
const result = spawnSync(binary, process.argv.slice(2), {
|
|
15
|
+
const result = spawnSync(binary, process.argv.slice(2), {
|
|
16
|
+
stdio: ['inherit', 'pipe', 'inherit'],
|
|
17
|
+
encoding: 'utf8',
|
|
18
|
+
});
|
|
16
19
|
if (result.error) throw result.error;
|
|
20
|
+
if (result.status === 0 && process.stdout.isTTY) {
|
|
21
|
+
console.log('Local role identity ready.');
|
|
22
|
+
} else if (result.stdout) {
|
|
23
|
+
process.stdout.write(result.stdout);
|
|
24
|
+
}
|
|
17
25
|
process.exitCode = result.status ?? 1;
|
|
@@ -22,6 +22,7 @@ const workersReadyPath = path.join(localRoot, '.runtime', 'role-workers.ready');
|
|
|
22
22
|
// Separate local stacks reuse Worker names, so each needs its own discovery registry.
|
|
23
23
|
const workerEnv = {
|
|
24
24
|
...process.env,
|
|
25
|
+
WRANGLER_LOG: process.env.WRANGLER_LOG || 'warn',
|
|
25
26
|
WRANGLER_REGISTRY_PATH: path.join(localRoot, '.local', 'worker-registry'),
|
|
26
27
|
};
|
|
27
28
|
const children = [];
|
|
@@ -46,14 +47,18 @@ async function main() {
|
|
|
46
47
|
startWorkers(runtime);
|
|
47
48
|
await waitForWorkers(runtime);
|
|
48
49
|
writeFileSync(workersReadyPath, 'ready\n');
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
50
|
+
if (process.stdout.isTTY) {
|
|
51
|
+
console.log('Local Wallet role Workers ready.');
|
|
52
|
+
} else {
|
|
53
|
+
console.log(
|
|
54
|
+
JSON.stringify({
|
|
55
|
+
kind: 'wallet_role_workers_ready_v1',
|
|
56
|
+
mpcRouterUrl: runtime.mpcRouterUrl,
|
|
57
|
+
ceremonyPrivateJwkPath,
|
|
58
|
+
workers: runtime.configs.map(describeWorker),
|
|
59
|
+
}),
|
|
60
|
+
);
|
|
61
|
+
}
|
|
57
62
|
await waitUntilStopped();
|
|
58
63
|
}
|
|
59
64
|
|
|
@@ -181,8 +186,9 @@ function applyPrivateD1Migrations(runtime) {
|
|
|
181
186
|
config.configPath,
|
|
182
187
|
],
|
|
183
188
|
repoRoot,
|
|
184
|
-
{ ...
|
|
189
|
+
{ ...workerEnv, CI: 'true' },
|
|
185
190
|
);
|
|
191
|
+
console.log(`Local database ready: ${config.role}`);
|
|
186
192
|
}
|
|
187
193
|
}
|
|
188
194
|
|
|
@@ -9,13 +9,26 @@ import path from 'node:path';
|
|
|
9
9
|
import { fileURLToPath } from 'node:url';
|
|
10
10
|
|
|
11
11
|
import { prepareLocalHostedWalletGatewayConfig } from './prepare-local-runtime-config.mjs';
|
|
12
|
+
import { printLocalServiceTable } from '../../../scripts/print-local-service-table.mjs';
|
|
12
13
|
|
|
13
14
|
const repoRoot = process.cwd();
|
|
14
15
|
const gatewayUrl = process.env.SEAMS_INTENDED_ROUTER_URL || 'http://127.0.0.1:4100';
|
|
15
|
-
const
|
|
16
|
+
const localPortOffset = Number(process.env.SEAMS_LOCAL_PORT_OFFSET || 0);
|
|
17
|
+
const routerUrl = `http://127.0.0.1:${4102 + localPortOffset}`;
|
|
18
|
+
const roleServices = [
|
|
19
|
+
{ name: 'mpc-router', port: 4102 },
|
|
20
|
+
{ name: 'deriver-a', port: 4103 },
|
|
21
|
+
{ name: 'deriver-b', port: 4104 },
|
|
22
|
+
{ name: 'signing-worker', port: 4105 },
|
|
23
|
+
{ name: 'tenant-root-control-plane', port: 4106 },
|
|
24
|
+
];
|
|
16
25
|
const options = parseArguments(process.argv.slice(2));
|
|
17
26
|
const localRoot = path.resolve(
|
|
18
|
-
options.root ||
|
|
27
|
+
options.root ||
|
|
28
|
+
path.join(
|
|
29
|
+
tmpdir(),
|
|
30
|
+
`${path.basename(repoRoot)}-wallet-system-${randomBytes(8).toString('hex')}`,
|
|
31
|
+
),
|
|
19
32
|
);
|
|
20
33
|
const gatewayStateRoot = path.join(localRoot, '.local', 'cloudflare-state', 'wallet-gateway');
|
|
21
34
|
const ceremonyPrivateJwkPath = path.join(
|
|
@@ -36,6 +49,7 @@ const identity = Object.freeze({
|
|
|
36
49
|
// Separate local stacks reuse Worker names, so each needs its own discovery registry.
|
|
37
50
|
const workerEnv = {
|
|
38
51
|
...process.env,
|
|
52
|
+
WRANGLER_LOG: process.env.WRANGLER_LOG || 'warn',
|
|
39
53
|
WRANGLER_REGISTRY_PATH: path.join(localRoot, '.local', 'worker-registry'),
|
|
40
54
|
};
|
|
41
55
|
const children = [];
|
|
@@ -51,10 +65,12 @@ async function main() {
|
|
|
51
65
|
installSignalHandlers();
|
|
52
66
|
mkdirSync(localRoot, { recursive: true });
|
|
53
67
|
rmSync(workersReadyPath, { force: true });
|
|
68
|
+
console.log('Starting local Wallet role Workers...');
|
|
54
69
|
startRoleWorkers();
|
|
55
70
|
await waitForHttp(`${routerUrl}/.well-known/router-ab/keyset`, 120_000, true);
|
|
56
71
|
await waitForFile(workersReadyPath, 120_000);
|
|
57
72
|
await waitForFile(ceremonyPrivateJwkPath, 10_000);
|
|
73
|
+
console.log('Provisioning local tenant root...');
|
|
58
74
|
const tenantRoot = bootstrapTenantRoot();
|
|
59
75
|
const deployment = localDeployment(tenantRoot);
|
|
60
76
|
const runtime = prepareLocalHostedWalletGatewayConfig({
|
|
@@ -67,23 +83,35 @@ async function main() {
|
|
|
67
83
|
deployment,
|
|
68
84
|
});
|
|
69
85
|
applySignerMigrations(runtime);
|
|
86
|
+
console.log('Starting local Wallet Gateway...');
|
|
70
87
|
startGateway(runtime);
|
|
71
88
|
await waitForHttp(`${runtime.gatewayUrl}/readyz`, 120_000, true);
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
);
|
|
89
|
+
const ready = {
|
|
90
|
+
kind: 'wallet_local_system_ready_v1',
|
|
91
|
+
gatewayUrl: runtime.gatewayUrl,
|
|
92
|
+
appOrigin: options.appOrigin,
|
|
93
|
+
walletOrigin: options.walletOrigin,
|
|
94
|
+
projectEnvironmentId: identity.environmentId,
|
|
95
|
+
publishableKey: deployment.credential.publishableKey,
|
|
96
|
+
signingWorkerId: 'local-signing-worker',
|
|
97
|
+
root: localRoot,
|
|
98
|
+
};
|
|
99
|
+
if (process.stdout.isTTY) printServiceTable(ready);
|
|
100
|
+
else console.log(JSON.stringify(ready));
|
|
84
101
|
await waitUntilStopped();
|
|
85
102
|
}
|
|
86
103
|
|
|
104
|
+
function printServiceTable(ready) {
|
|
105
|
+
const services = [];
|
|
106
|
+
for (const role of roleServices) {
|
|
107
|
+
services.push({ name: role.name, url: `http://127.0.0.1:${role.port + localPortOffset}` });
|
|
108
|
+
}
|
|
109
|
+
services.push({ name: 'wallet-gateway', url: ready.gatewayUrl });
|
|
110
|
+
printLocalServiceTable('Local Wallet services ready', services);
|
|
111
|
+
console.log(`Site: ${ready.appOrigin} (start pnpm site in another terminal)`);
|
|
112
|
+
console.log(`Runtime state: ${ready.root}`);
|
|
113
|
+
}
|
|
114
|
+
|
|
87
115
|
function parseArguments(args) {
|
|
88
116
|
const parsed = {
|
|
89
117
|
help: false,
|
|
@@ -254,8 +282,9 @@ function applySignerMigrations(runtime) {
|
|
|
254
282
|
'--config',
|
|
255
283
|
runtime.configPath,
|
|
256
284
|
],
|
|
257
|
-
{ ...
|
|
285
|
+
{ ...workerEnv, CI: 'true' },
|
|
258
286
|
);
|
|
287
|
+
console.log('Local database ready: wallet-gateway');
|
|
259
288
|
}
|
|
260
289
|
|
|
261
290
|
function startGateway(runtime) {
|
|
@@ -1233,6 +1233,7 @@ function createD1WalletRegistrationRouteService(assembly) {
|
|
|
1233
1233
|
readActiveEmailOtpEnrollment: assembly.emailOtpRecoveryService.readActiveEmailOtpEnrollment.bind(assembly.emailOtpRecoveryService),
|
|
1234
1234
|
setupWalletRegistration: assembly.walletRegistrations.setupWalletRegistration.bind(assembly.walletRegistrations),
|
|
1235
1235
|
respondWalletRegistration: assembly.walletRegistrations.respondWalletRegistration.bind(assembly.walletRegistrations),
|
|
1236
|
+
authorizeWalletRegistrationNearAdmission: assembly.walletRegistrations.authorizeWalletRegistrationNearAdmission.bind(assembly.walletRegistrations),
|
|
1236
1237
|
activateWalletRegistration: assembly.walletRegistrations.activateWalletRegistration.bind(assembly.walletRegistrations),
|
|
1237
1238
|
completeWalletRegistrationNearProvisioning: assembly.walletRegistrations.completeWalletRegistrationNearProvisioning.bind(assembly.walletRegistrations),
|
|
1238
1239
|
refreshEd25519YaoWalletSession: assembly.walletRegistrations.refreshEd25519YaoWalletSession.bind(assembly.walletRegistrations),
|
|
@@ -933,6 +933,16 @@ function mergeRouterServerTiming(target, header) {
|
|
|
933
933
|
}
|
|
934
934
|
}
|
|
935
935
|
}
|
|
936
|
+
function recordRegistrationServerTiming(sink, name, startedAtMs) {
|
|
937
|
+
sink.push([name, Math.max(0, Date.now() - startedAtMs)]);
|
|
938
|
+
}
|
|
939
|
+
async function recordRegistrationPromiseTiming(args) {
|
|
940
|
+
try {
|
|
941
|
+
return await args.promise;
|
|
942
|
+
} finally {
|
|
943
|
+
recordRegistrationServerTiming(args.sink, args.name, args.startedAtMs);
|
|
944
|
+
}
|
|
945
|
+
}
|
|
936
946
|
var CloudflareD1WalletRegistrationService = class {
|
|
937
947
|
constructor(input) {
|
|
938
948
|
this.authorizationService = input.authorizationService;
|
|
@@ -1783,8 +1793,99 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
1783
1793
|
return walletRegistrationSetupError("internal", errorMessage(error) || "Failed to set up wallet registration");
|
|
1784
1794
|
}
|
|
1785
1795
|
}
|
|
1786
|
-
async
|
|
1787
|
-
|
|
1796
|
+
async authorizeWalletRegistrationNearAdmission(input) {
|
|
1797
|
+
try {
|
|
1798
|
+
const snapshot = await this.getRegistrationCeremonyIntentStore().getCeremonySnapshot(input.registrationCeremonyId);
|
|
1799
|
+
if (!snapshot) return {
|
|
1800
|
+
ok: false,
|
|
1801
|
+
code: "not_found",
|
|
1802
|
+
message: "registration ceremony not found"
|
|
1803
|
+
};
|
|
1804
|
+
const ceremony = snapshot.ceremony;
|
|
1805
|
+
if (ceremony.signerState.kind !== "signer_set_registration") return {
|
|
1806
|
+
ok: false,
|
|
1807
|
+
code: "invalid_state",
|
|
1808
|
+
message: "signer-set registration state is required"
|
|
1809
|
+
};
|
|
1810
|
+
const branches = registrationSignerBranchesFromPlan(ceremony.signerPlan);
|
|
1811
|
+
if (!findStoredWalletRegistrationEvmFamilyEcdsaBranch(ceremony.signerState) || !branches.nearEd25519) return {
|
|
1812
|
+
ok: false,
|
|
1813
|
+
code: "invalid_state",
|
|
1814
|
+
message: "mixed registration state is required"
|
|
1815
|
+
};
|
|
1816
|
+
const expectedOrigin = normalizeOptionalTrimmedString(ceremony.expectedOrigin) || "";
|
|
1817
|
+
const setupDigestB64u = await computeWalletRegistrationSetupDigestB64u({
|
|
1818
|
+
registrationCeremonyId: ceremony.registrationCeremonyId,
|
|
1819
|
+
intent: ceremony.intent,
|
|
1820
|
+
intentDigestB64u: ceremony.digestB64u,
|
|
1821
|
+
orgId: ceremony.orgId,
|
|
1822
|
+
signingRootId: normalizeOptionalTrimmedString(ceremony.signingRootId) || "",
|
|
1823
|
+
signingRootVersion: normalizeOptionalTrimmedString(ceremony.signingRootVersion) || "",
|
|
1824
|
+
expectedOrigin
|
|
1825
|
+
});
|
|
1826
|
+
const verifiedSetup = await verifySignedWalletRegistrationSetup(input.verifier, input.signedSetup, {
|
|
1827
|
+
registrationCeremonyId: ceremony.registrationCeremonyId,
|
|
1828
|
+
setupDigestB64u,
|
|
1829
|
+
nowMs: Date.now()
|
|
1830
|
+
});
|
|
1831
|
+
if (!verifiedSetup.ok) return {
|
|
1832
|
+
ok: false,
|
|
1833
|
+
code: verifiedSetup.code,
|
|
1834
|
+
message: verifiedSetup.message
|
|
1835
|
+
};
|
|
1836
|
+
const registrationBearerToken = normalizeOptionalTrimmedString(input.signedSetup);
|
|
1837
|
+
if (!registrationBearerToken) return {
|
|
1838
|
+
ok: false,
|
|
1839
|
+
code: "invalid_body",
|
|
1840
|
+
message: "signedSetup is required"
|
|
1841
|
+
};
|
|
1842
|
+
const verifiedAuthority = await this.walletAuthMethods.verifyRegistrationAuthorityForIntent({
|
|
1843
|
+
orgId: ceremony.orgId,
|
|
1844
|
+
authority: input.authority,
|
|
1845
|
+
expectedDigestB64u: ceremony.digestB64u,
|
|
1846
|
+
expectedOrigin,
|
|
1847
|
+
intent: ceremony.intent,
|
|
1848
|
+
verificationOperationId: ceremony.registrationCeremonyId,
|
|
1849
|
+
verificationReceiptExpiresAtMs: ceremony.expiresAtMs,
|
|
1850
|
+
...input.userAgent ? { userAgent: input.userAgent } : {}
|
|
1851
|
+
});
|
|
1852
|
+
if (!verifiedAuthority.ok) return {
|
|
1853
|
+
ok: false,
|
|
1854
|
+
code: verifiedAuthority.code,
|
|
1855
|
+
message: verifiedAuthority.message
|
|
1856
|
+
};
|
|
1857
|
+
if (verifiedAuthority.authority.walletId !== ceremony.intent.walletId) return {
|
|
1858
|
+
ok: false,
|
|
1859
|
+
code: "scope_mismatch",
|
|
1860
|
+
message: "registration authority walletId does not match the ceremony"
|
|
1861
|
+
};
|
|
1862
|
+
const admitted = await this.authorizeRespondEd25519Branch({
|
|
1863
|
+
ceremony,
|
|
1864
|
+
branch: branches.nearEd25519,
|
|
1865
|
+
authority: verifiedAuthority.authority,
|
|
1866
|
+
registrationBearerToken
|
|
1867
|
+
});
|
|
1868
|
+
if (!admitted.ok) return admitted;
|
|
1869
|
+
if (!admitted.deferred) return {
|
|
1870
|
+
ok: false,
|
|
1871
|
+
code: "internal",
|
|
1872
|
+
message: "NEAR admission produced no work"
|
|
1873
|
+
};
|
|
1874
|
+
return {
|
|
1875
|
+
ok: true,
|
|
1876
|
+
registrationCeremonyId: ceremony.registrationCeremonyId,
|
|
1877
|
+
ed25519: admitted.deferred
|
|
1878
|
+
};
|
|
1879
|
+
} catch (error) {
|
|
1880
|
+
return {
|
|
1881
|
+
ok: false,
|
|
1882
|
+
code: "internal",
|
|
1883
|
+
message: errorMessage(error) || "Failed to authorize NEAR registration admission"
|
|
1884
|
+
};
|
|
1885
|
+
}
|
|
1886
|
+
}
|
|
1887
|
+
async respondWalletRegistration(input, traceContext, timingSink) {
|
|
1888
|
+
const serverTiming = timingSink ?? [];
|
|
1788
1889
|
const totalStartedAtMs = Date.now();
|
|
1789
1890
|
const mark = (name, startedAtMs) => {
|
|
1790
1891
|
serverTiming.push([name, Math.max(0, Date.now() - startedAtMs)]);
|
|
@@ -1793,7 +1894,7 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
1793
1894
|
const store = this.getRegistrationCeremonyIntentStore();
|
|
1794
1895
|
const loadStartedAtMs = Date.now();
|
|
1795
1896
|
const snapshot = await store.getCeremonySnapshot(input.registrationCeremonyId);
|
|
1796
|
-
mark("
|
|
1897
|
+
mark("ecdsa_respond_d1_load", loadStartedAtMs);
|
|
1797
1898
|
if (!snapshot) return {
|
|
1798
1899
|
ok: false,
|
|
1799
1900
|
code: "not_found",
|
|
@@ -1952,7 +2053,7 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
1952
2053
|
authority,
|
|
1953
2054
|
registrationBearerToken
|
|
1954
2055
|
})]);
|
|
1955
|
-
mark("
|
|
2056
|
+
mark("ecdsa_respond_router", routerStartedAtMs);
|
|
1956
2057
|
if (!strictResult.ok) {
|
|
1957
2058
|
if (!strictResult.retryable) await store.cancelTerminalCeremony({
|
|
1958
2059
|
registrationCeremonyId: ceremony.registrationCeremonyId,
|
|
@@ -2006,16 +2107,16 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2006
2107
|
const reconciled = await store.getCeremony(input.registrationCeremonyId);
|
|
2007
2108
|
const reconciledBranch = reconciled?.signerState.kind === "signer_set_registration" ? findStoredWalletRegistrationEvmFamilyEcdsaBranch(reconciled.signerState) : null;
|
|
2008
2109
|
if (!reconciled || reconciledBranch?.kind !== "evm_family_ecdsa_pending_activation" || !sameRouterAbEcdsaRegistrationRequest(reconciledBranch.registrationRequest, strictRegistration)) throw error;
|
|
2009
|
-
mark("
|
|
2010
|
-
mark("
|
|
2110
|
+
mark("ecdsa_respond_d1_commit", commitStartedAtMs);
|
|
2111
|
+
mark("ecdsa_respond_total", totalStartedAtMs);
|
|
2011
2112
|
return walletRegistrationRespondResult({
|
|
2012
2113
|
ceremony: reconciled,
|
|
2013
2114
|
strictResult: reconciledBranch.publicResponse,
|
|
2014
2115
|
ed25519: storedRespondEd25519DeferredWork(reconciled.signerState)
|
|
2015
2116
|
});
|
|
2016
2117
|
}
|
|
2017
|
-
mark("
|
|
2018
|
-
mark("
|
|
2118
|
+
mark("ecdsa_respond_d1_commit", commitStartedAtMs);
|
|
2119
|
+
mark("ecdsa_respond_total", totalStartedAtMs);
|
|
2019
2120
|
return walletRegistrationRespondResult({
|
|
2020
2121
|
ceremony: next,
|
|
2021
2122
|
strictResult: strictResult.value.publicResponse,
|
|
@@ -2072,7 +2173,10 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2072
2173
|
});
|
|
2073
2174
|
}
|
|
2074
2175
|
async authorizeNearRegistrationContinuation(input) {
|
|
2075
|
-
|
|
2176
|
+
return (await this.readNearRegistrationContinuationContext(input))?.verified ?? null;
|
|
2177
|
+
}
|
|
2178
|
+
async readNearRegistrationContinuationContext(input, installed) {
|
|
2179
|
+
const installation = installed ?? await this.readCommittedRegistrationInstallation(input.lifecycleId);
|
|
2076
2180
|
if (!installation || !isRegistrationEcdsaReadyReceipt(installation.receipt)) return null;
|
|
2077
2181
|
const nowMs = Date.now();
|
|
2078
2182
|
const issued = await this.authorizationService.readLiveWalletSessionAuthorizationProjectionByCredential({
|
|
@@ -2086,9 +2190,13 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2086
2190
|
const originalSubjects = installation.receipt.committed.session.walletSession.capabilitySubjects;
|
|
2087
2191
|
for (const subject of originalSubjects) if (!issued.session.capabilitySubjects.some(sameRegistrationContinuationSubject.bind(void 0, subject))) return null;
|
|
2088
2192
|
return {
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2193
|
+
verified: {
|
|
2194
|
+
credential: input.credential,
|
|
2195
|
+
admissionRequest: installation.projection.nearEd25519.admissionRequest,
|
|
2196
|
+
expiresAtMs: Math.min(issued.session.expiresAtMs, nowMs + 6e4)
|
|
2197
|
+
},
|
|
2198
|
+
installation,
|
|
2199
|
+
authorization: issued
|
|
2092
2200
|
};
|
|
2093
2201
|
}
|
|
2094
2202
|
async readCommittedRegistrationInstallation(registrationCeremonyId) {
|
|
@@ -2125,10 +2233,10 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2125
2233
|
prepared: record.prepared
|
|
2126
2234
|
};
|
|
2127
2235
|
}
|
|
2128
|
-
async prepareNearProvisioningOperation(registrationCeremonyId, allocated) {
|
|
2129
|
-
const ceremony = await this.getRegistrationCeremonyIntentStore().getCeremony(registrationCeremonyId);
|
|
2236
|
+
async prepareNearProvisioningOperation(registrationCeremonyId, allocated, installed) {
|
|
2237
|
+
const ceremony = installed ? null : await this.getRegistrationCeremonyIntentStore().getCeremony(registrationCeremonyId);
|
|
2130
2238
|
if (!ceremony) {
|
|
2131
|
-
const committed = await this.readCommittedRegistrationInstallation(registrationCeremonyId);
|
|
2239
|
+
const committed = installed ?? await this.readCommittedRegistrationInstallation(registrationCeremonyId);
|
|
2132
2240
|
if (!committed) throw new Error("Registration ceremony is unavailable for identity preparation");
|
|
2133
2241
|
if (allocated && (allocated.walletAuthorityId !== committed.prepared.walletAuthorityId || allocated.deviceId !== committed.prepared.deviceId || allocated.walletAuthMethodId !== committed.prepared.walletAuthMethodId)) throw new Error("registration operation identity does not match its committed installation");
|
|
2134
2242
|
return committed.prepared;
|
|
@@ -2146,9 +2254,16 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2146
2254
|
};
|
|
2147
2255
|
}
|
|
2148
2256
|
async commitDeferredEd25519Signer(args, prepared) {
|
|
2149
|
-
const
|
|
2257
|
+
const committedInstallation = args.authoritySource.kind === "committed_mixed" ? args.authoritySource.installation : null;
|
|
2258
|
+
if (committedInstallation && !isRegistrationEcdsaReadyReceipt(committedInstallation.receipt)) throw new Error("Committed mixed registration receipt is not ECDSA-ready");
|
|
2259
|
+
const ownerProofContext = committedInstallation ? {
|
|
2260
|
+
expectedOrigin: committedInstallation.receipt.expectedOrigin,
|
|
2261
|
+
runtimePolicyScope: registrationPreparedContextRuntimePolicyScope(committedInstallation.projection.preparedContext),
|
|
2262
|
+
expiresAtMs: committedInstallation.receipt.expiresAtMs
|
|
2263
|
+
} : await this.readRegistrationOwnerProofContext(args.input.registrationCeremonyId);
|
|
2150
2264
|
if (!ownerProofContext) throw new Error("Registration owner proof context is unavailable");
|
|
2151
|
-
|
|
2265
|
+
if (!ownerProofContext.runtimePolicyScope) throw new Error("Committed registration is missing its runtime policy scope");
|
|
2266
|
+
const ceremony = committedInstallation ? null : await this.getRegistrationCeremonyIntentStore().getCeremony(args.input.registrationCeremonyId);
|
|
2152
2267
|
let recovery = null;
|
|
2153
2268
|
let registrationAuthority;
|
|
2154
2269
|
let sessionPredecessor;
|
|
@@ -2158,7 +2273,7 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2158
2273
|
registrationAuthority = authority;
|
|
2159
2274
|
sessionPredecessor = { kind: "ed25519_only" };
|
|
2160
2275
|
} else {
|
|
2161
|
-
const committed = await this.readCommittedRegistrationInstallation(args.input.registrationCeremonyId);
|
|
2276
|
+
const committed = committedInstallation ?? await this.readCommittedRegistrationInstallation(args.input.registrationCeremonyId);
|
|
2162
2277
|
if (!committed) throw new Error("Committed registration installation is unavailable");
|
|
2163
2278
|
recovery = registrationFinalizeRecoveryFromCommittedInstallation({
|
|
2164
2279
|
setupClaims: args.setupClaims,
|
|
@@ -2171,14 +2286,7 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2171
2286
|
if (!isRegistrationEcdsaReadyReceipt(activationReceipt)) throw new Error("Committed mixed registration receipt is not ECDSA-ready");
|
|
2172
2287
|
if (activationReceipt.committed.session.tokens.kind !== "evm_family_ecdsa") throw new Error("Committed mixed registration is missing its ECDSA session projection");
|
|
2173
2288
|
if (args.input.authorization.kind === "wallet_session") {
|
|
2174
|
-
const authorization =
|
|
2175
|
-
lifecycleId: args.input.registrationCeremonyId,
|
|
2176
|
-
credential: args.input.authorization.credential
|
|
2177
|
-
}) ? await this.authorizationService.readLiveWalletSessionAuthorizationProjectionByCredential({
|
|
2178
|
-
tenantId: this.authorizationTenantId,
|
|
2179
|
-
token: args.input.authorization.credential,
|
|
2180
|
-
nowMs: Date.now()
|
|
2181
|
-
}) : null;
|
|
2289
|
+
const authorization = args.authoritySource.kind === "committed_mixed" ? args.authoritySource.authorization : null;
|
|
2182
2290
|
if (!authorization) throw new Error("NEAR continuation Wallet Session is no longer active");
|
|
2183
2291
|
sessionPredecessor = {
|
|
2184
2292
|
kind: "resumed_mixed",
|
|
@@ -2190,7 +2298,7 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2190
2298
|
ecdsa: activationReceipt.committed.session.tokens.ecdsa
|
|
2191
2299
|
};
|
|
2192
2300
|
}
|
|
2193
|
-
const effectivePrepared = await this.prepareNearProvisioningOperation(args.input.registrationCeremonyId, prepared);
|
|
2301
|
+
const effectivePrepared = await this.prepareNearProvisioningOperation(args.input.registrationCeremonyId, prepared, committedInstallation ?? void 0);
|
|
2194
2302
|
const committed = await this.executeWalletRegistrationFinalize({
|
|
2195
2303
|
kind: "near_ed25519",
|
|
2196
2304
|
registrationCeremonyId: args.input.registrationCeremonyId,
|
|
@@ -2358,16 +2466,22 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2358
2466
|
* be destroyed because its signer ceremony needs another attempt.
|
|
2359
2467
|
*/
|
|
2360
2468
|
async verifyNearProvisioningAuthority(input) {
|
|
2361
|
-
if (input.authorization.kind === "registration_grant")
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2469
|
+
if (input.authorization.kind === "registration_grant") {
|
|
2470
|
+
const verified = await verifyWalletRegistrationSetupClaims(input.verifier, input.signedSetup, {
|
|
2471
|
+
registrationCeremonyId: input.registrationCeremonyId,
|
|
2472
|
+
nowMs: Date.now()
|
|
2473
|
+
});
|
|
2474
|
+
return verified.ok ? {
|
|
2475
|
+
...verified,
|
|
2476
|
+
source: { kind: "registration_grant" }
|
|
2477
|
+
} : verified;
|
|
2478
|
+
}
|
|
2479
|
+
const installation = await this.readCommittedRegistrationInstallation(input.registrationCeremonyId);
|
|
2480
|
+
const continuation = installation ? await this.readNearRegistrationContinuationContext({
|
|
2366
2481
|
lifecycleId: input.registrationCeremonyId,
|
|
2367
2482
|
credential: input.authorization.credential
|
|
2368
|
-
});
|
|
2369
|
-
|
|
2370
|
-
if (!authorized || !installation || typeof input.signedSetup !== "string") return {
|
|
2483
|
+
}, installation) : null;
|
|
2484
|
+
if (!continuation || typeof input.signedSetup !== "string") return {
|
|
2371
2485
|
ok: false,
|
|
2372
2486
|
code: "invalid_grant",
|
|
2373
2487
|
message: "NEAR continuation authority is unavailable"
|
|
@@ -2381,24 +2495,34 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2381
2495
|
};
|
|
2382
2496
|
registrationFinalizeRecoveryFromCommittedInstallation({
|
|
2383
2497
|
setupClaims: claims,
|
|
2384
|
-
projection: installation.projection
|
|
2498
|
+
projection: continuation.installation.projection
|
|
2385
2499
|
});
|
|
2386
2500
|
return {
|
|
2387
2501
|
ok: true,
|
|
2388
2502
|
claims: {
|
|
2389
2503
|
...claims,
|
|
2390
|
-
expiresAtMs:
|
|
2504
|
+
expiresAtMs: continuation.verified.expiresAtMs
|
|
2505
|
+
},
|
|
2506
|
+
source: {
|
|
2507
|
+
kind: "committed_mixed",
|
|
2508
|
+
installation: continuation.installation,
|
|
2509
|
+
authorization: continuation.authorization
|
|
2391
2510
|
}
|
|
2392
2511
|
};
|
|
2393
2512
|
}
|
|
2394
|
-
async completeWalletRegistrationNearProvisioning(input) {
|
|
2513
|
+
async completeWalletRegistrationNearProvisioning(input, timingSink) {
|
|
2514
|
+
const serverTiming = timingSink ?? [];
|
|
2515
|
+
const totalStartedAtMs = Date.now();
|
|
2395
2516
|
try {
|
|
2517
|
+
const authorityStartedAtMs = Date.now();
|
|
2396
2518
|
const verified = await this.verifyNearProvisioningAuthority(input);
|
|
2519
|
+
recordRegistrationServerTiming(serverTiming, "near_finalize_authority", authorityStartedAtMs);
|
|
2397
2520
|
if (!verified.ok) return {
|
|
2398
2521
|
ok: false,
|
|
2399
2522
|
code: verified.code,
|
|
2400
2523
|
message: verified.message
|
|
2401
2524
|
};
|
|
2525
|
+
const fingerprintStartedAtMs = Date.now();
|
|
2402
2526
|
const requestFingerprint = base64UrlEncode(await sha256BytesUtf8(alphabetizeStringify({
|
|
2403
2527
|
version: "wallet_registration_near_provisioning_digest_v1",
|
|
2404
2528
|
registrationCeremonyId: input.registrationCeremonyId,
|
|
@@ -2407,6 +2531,8 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2407
2531
|
ed25519: input.ed25519,
|
|
2408
2532
|
emailOtpEnrollment: input.emailOtpEnrollment
|
|
2409
2533
|
})));
|
|
2534
|
+
recordRegistrationServerTiming(serverTiming, "near_finalize_fingerprint", fingerprintStartedAtMs);
|
|
2535
|
+
const sideEffectStartedAtMs = Date.now();
|
|
2410
2536
|
const run = await runRouterAbEd25519YaoRegistrationSideEffectV2(this.nearProvisioningSideEffects, {
|
|
2411
2537
|
kind: "prepared_resumable",
|
|
2412
2538
|
operation: "near_provisioning",
|
|
@@ -2414,11 +2540,12 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2414
2540
|
requestFingerprint,
|
|
2415
2541
|
resumeAfterMs: D1_WALLET_REGISTRATION_OPERATION_RESUME_AFTER_MS,
|
|
2416
2542
|
nowMs: Date.now,
|
|
2417
|
-
prepare: async () => await this.prepareNearProvisioningOperation(input.registrationCeremonyId),
|
|
2543
|
+
prepare: async () => await this.prepareNearProvisioningOperation(input.registrationCeremonyId, void 0, verified.source.kind === "committed_mixed" ? verified.source.installation : void 0),
|
|
2418
2544
|
derivePreparedArtifactFingerprint: async (prepared) => base64UrlEncode(await sha256BytesUtf8(alphabetizeStringify(prepared))),
|
|
2419
2545
|
execute: this.commitDeferredEd25519Signer.bind(this, {
|
|
2420
2546
|
input,
|
|
2421
|
-
setupClaims: verified.claims
|
|
2547
|
+
setupClaims: verified.claims,
|
|
2548
|
+
authoritySource: verified.source
|
|
2422
2549
|
}),
|
|
2423
2550
|
projectReceipt: (execution) => projectWalletRegistrationSessionCommitReceiptV2({
|
|
2424
2551
|
operation: "near_provisioning",
|
|
@@ -2428,6 +2555,7 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2428
2555
|
}),
|
|
2429
2556
|
replay: this.replayWalletRegistrationNearProvisioningCommit.bind(this)
|
|
2430
2557
|
});
|
|
2558
|
+
recordRegistrationServerTiming(serverTiming, "near_finalize_side_effect", sideEffectStartedAtMs);
|
|
2431
2559
|
switch (run.kind) {
|
|
2432
2560
|
case "executed":
|
|
2433
2561
|
case "exact_replay": break;
|
|
@@ -2471,18 +2599,30 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2471
2599
|
message: "NEAR provisioning did not establish a Wallet Session",
|
|
2472
2600
|
nearProvisioning: { status: "near_failed_retryable" }
|
|
2473
2601
|
};
|
|
2474
|
-
|
|
2475
|
-
|
|
2476
|
-
|
|
2602
|
+
const cleanup = recordRegistrationPromiseTiming({
|
|
2603
|
+
sink: serverTiming,
|
|
2604
|
+
name: "near_finalize_cleanup",
|
|
2605
|
+
startedAtMs: Date.now(),
|
|
2606
|
+
promise: cleanupFinalizedRegistrationCeremony({
|
|
2607
|
+
store: this.getRegistrationCeremonyIntentStore(),
|
|
2608
|
+
registrationCeremonyId: input.registrationCeremonyId
|
|
2609
|
+
})
|
|
2477
2610
|
});
|
|
2478
2611
|
if (input.authorization.kind === "wallet_session") {
|
|
2479
|
-
const
|
|
2480
|
-
|
|
2481
|
-
|
|
2482
|
-
|
|
2483
|
-
|
|
2612
|
+
const projectionStartedAtMs = Date.now();
|
|
2613
|
+
const [current] = await Promise.all([recordRegistrationPromiseTiming({
|
|
2614
|
+
sink: serverTiming,
|
|
2615
|
+
name: "near_finalize_session_projection",
|
|
2616
|
+
startedAtMs: projectionStartedAtMs,
|
|
2617
|
+
promise: this.authorizationService.readLiveWalletSessionAuthorizationProjectionByCredential({
|
|
2618
|
+
tenantId: this.authorizationTenantId,
|
|
2619
|
+
token: input.authorization.credential,
|
|
2620
|
+
nowMs: Date.now()
|
|
2621
|
+
})
|
|
2622
|
+
}), cleanup]);
|
|
2484
2623
|
const tokens = committed.registrationEstablishedSession.session.tokens;
|
|
2485
2624
|
if (!current || tokens.kind !== "near_ed25519_and_evm_family_ecdsa") throw new Error("NEAR continuation Wallet Session is no longer active");
|
|
2625
|
+
recordRegistrationServerTiming(serverTiming, "near_finalize_total", totalStartedAtMs);
|
|
2486
2626
|
return {
|
|
2487
2627
|
...committed,
|
|
2488
2628
|
registrationEstablishedSession: {
|
|
@@ -2493,11 +2633,14 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2493
2633
|
nearProvisioning: { status: "near_ready" }
|
|
2494
2634
|
};
|
|
2495
2635
|
}
|
|
2636
|
+
await cleanup;
|
|
2637
|
+
recordRegistrationServerTiming(serverTiming, "near_finalize_total", totalStartedAtMs);
|
|
2496
2638
|
return {
|
|
2497
2639
|
...committed,
|
|
2498
2640
|
nearProvisioning: { status: "near_ready" }
|
|
2499
2641
|
};
|
|
2500
2642
|
} catch (error) {
|
|
2643
|
+
recordRegistrationServerTiming(serverTiming, "near_finalize_total", totalStartedAtMs);
|
|
2501
2644
|
return {
|
|
2502
2645
|
ok: false,
|
|
2503
2646
|
code: "internal",
|
|
@@ -2791,7 +2934,7 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2791
2934
|
* before execution; an ambiguous outcome after the custody call stays
|
|
2792
2935
|
* `uncertain` rather than being guessed either way.
|
|
2793
2936
|
*/
|
|
2794
|
-
async activateWalletRegistration(input, traceContext) {
|
|
2937
|
+
async activateWalletRegistration(input, traceContext, serverTiming) {
|
|
2795
2938
|
try {
|
|
2796
2939
|
const verifiedSetup = await verifyWalletRegistrationSetupClaims(input.verifier, input.signedSetup, {
|
|
2797
2940
|
registrationCeremonyId: input.registrationCeremonyId,
|
|
@@ -2830,7 +2973,8 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2830
2973
|
registrationCeremonyId: claims.registrationCeremonyId,
|
|
2831
2974
|
idempotencyKey,
|
|
2832
2975
|
input,
|
|
2833
|
-
traceContext
|
|
2976
|
+
traceContext,
|
|
2977
|
+
serverTiming
|
|
2834
2978
|
}),
|
|
2835
2979
|
projectReceipt: (execution) => projectWalletRegistrationSessionCommitReceiptV2({
|
|
2836
2980
|
operation: "registration_activate",
|
|
@@ -2930,6 +3074,7 @@ var CloudflareD1WalletRegistrationService = class {
|
|
|
2930
3074
|
publicFacts: ecdsa.clientActivation
|
|
2931
3075
|
}
|
|
2932
3076
|
}, context.traceContext, context.idempotencyKey);
|
|
3077
|
+
if (activated.ok && activated.gatewayServerTiming) context.serverTiming?.push(...activated.gatewayServerTiming);
|
|
2933
3078
|
if (!activated.ok) return unissuedRegistrationCommit(throwIfRouterAbEd25519YaoRetryableSideEffectFailureV1(activated));
|
|
2934
3079
|
const keyHandle = activated.ecdsa.bootstrap?.keyHandle;
|
|
2935
3080
|
if (!keyHandle) return unissuedRegistrationCommit({
|