@reclaimprotocol/attestor-core 5.0.1-beta.21 → 5.0.1-beta.23

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.
Files changed (133) hide show
  1. package/browser/resources/attestor-browser.min.mjs +9 -9
  2. package/lib/avs/abis/avsDirectoryABI.js +340 -0
  3. package/lib/avs/abis/delegationABI.js +1 -0
  4. package/lib/avs/abis/registryABI.js +725 -0
  5. package/lib/avs/client/create-claim-on-avs.js +140 -0
  6. package/lib/avs/config.js +20 -0
  7. package/lib/avs/contracts/factories/ReclaimServiceManager__factory.js +1166 -0
  8. package/lib/avs/contracts/factories/index.js +4 -0
  9. package/lib/avs/contracts/index.js +2 -0
  10. package/lib/avs/utils/contracts.js +33 -0
  11. package/lib/avs/utils/register.js +79 -0
  12. package/lib/avs/utils/tasks.js +41 -0
  13. package/lib/client/create-claim.js +432 -0
  14. package/lib/client/index.js +3 -0
  15. package/lib/client/tunnels/make-rpc-tcp-tunnel.js +51 -0
  16. package/lib/client/tunnels/make-rpc-tls-tunnel.js +131 -0
  17. package/lib/client/utils/attestor-pool.js +25 -0
  18. package/lib/client/utils/client-socket.js +97 -0
  19. package/lib/client/utils/message-handler.js +87 -0
  20. package/lib/config/index.js +44 -0
  21. package/lib/external-rpc/benchmark.js +69 -0
  22. package/lib/external-rpc/event-bus.js +14 -0
  23. package/lib/external-rpc/handle-incoming-msg.js +232 -0
  24. package/lib/external-rpc/index.js +3 -10399
  25. package/lib/external-rpc/jsc-polyfills/1.js +82 -0
  26. package/lib/external-rpc/jsc-polyfills/2.js +20 -0
  27. package/lib/external-rpc/jsc-polyfills/event.js +14 -0
  28. package/lib/external-rpc/jsc-polyfills/index.js +2 -0
  29. package/lib/external-rpc/jsc-polyfills/ws.js +81 -0
  30. package/lib/external-rpc/setup-browser.js +33 -0
  31. package/lib/external-rpc/setup-jsc.js +22 -0
  32. package/lib/external-rpc/types.d.ts +0 -1
  33. package/lib/external-rpc/utils.js +100 -0
  34. package/lib/external-rpc/zk.js +63 -0
  35. package/lib/index.js +9 -8326
  36. package/lib/mechain/abis/governanceABI.js +458 -0
  37. package/lib/mechain/abis/taskABI.js +509 -0
  38. package/lib/mechain/client/create-claim-on-mechain.js +28 -0
  39. package/lib/mechain/client/index.js +1 -0
  40. package/lib/mechain/constants/index.js +3 -0
  41. package/lib/mechain/index.js +2 -0
  42. package/lib/mechain/types/index.js +1 -0
  43. package/lib/proto/api.js +4363 -0
  44. package/lib/proto/tee-bundle.js +1316 -0
  45. package/lib/providers/http/index.js +653 -0
  46. package/lib/providers/http/patch-parse5-tree.js +32 -0
  47. package/lib/providers/http/utils.js +324 -0
  48. package/lib/providers/index.js +4 -0
  49. package/lib/server/create-server.js +103 -0
  50. package/lib/server/handlers/claimTeeBundle.js +252 -0
  51. package/lib/server/handlers/claimTunnel.js +73 -0
  52. package/lib/server/handlers/completeClaimOnChain.js +24 -0
  53. package/lib/server/handlers/createClaimOnChain.js +26 -0
  54. package/lib/server/handlers/createTaskOnMechain.js +47 -0
  55. package/lib/server/handlers/createTunnel.js +93 -0
  56. package/lib/server/handlers/disconnectTunnel.js +5 -0
  57. package/lib/server/handlers/fetchCertificateBytes.js +41 -0
  58. package/lib/server/handlers/index.js +22 -0
  59. package/lib/server/handlers/init.js +32 -0
  60. package/lib/server/handlers/toprf.js +16 -0
  61. package/lib/server/index.js +4 -0
  62. package/lib/server/socket.js +109 -0
  63. package/lib/server/tunnels/make-tcp-tunnel.js +177 -0
  64. package/lib/server/utils/apm.js +36 -0
  65. package/lib/server/utils/assert-valid-claim-request.js +325 -0
  66. package/lib/server/utils/config-env.js +4 -0
  67. package/lib/server/utils/dns.js +18 -0
  68. package/lib/server/utils/gcp-attestation.js +289 -0
  69. package/lib/server/utils/generics.d.ts +1 -1
  70. package/lib/server/utils/generics.js +51 -0
  71. package/lib/server/utils/iso.js +256 -0
  72. package/lib/server/utils/keep-alive.js +38 -0
  73. package/lib/server/utils/nitro-attestation.js +324 -0
  74. package/lib/server/utils/oprf-raw.js +54 -0
  75. package/lib/server/utils/process-handshake.js +215 -0
  76. package/lib/server/utils/proxy-session.js +6 -0
  77. package/lib/server/utils/tee-oprf-mpc-verification.js +90 -0
  78. package/lib/server/utils/tee-oprf-verification.js +174 -0
  79. package/lib/server/utils/tee-transcript-reconstruction.js +187 -0
  80. package/lib/server/utils/tee-verification.js +421 -0
  81. package/lib/server/utils/validation.js +38 -0
  82. package/lib/types/bgp.js +1 -0
  83. package/lib/types/claims.js +1 -0
  84. package/lib/types/client.js +1 -0
  85. package/lib/types/general.js +1 -0
  86. package/lib/types/handlers.js +1 -0
  87. package/lib/types/index.js +10 -0
  88. package/lib/types/providers.d.ts +3 -2
  89. package/lib/types/providers.gen.js +10 -0
  90. package/lib/types/providers.js +1 -0
  91. package/lib/types/rpc.js +1 -0
  92. package/lib/types/signatures.d.ts +1 -2
  93. package/lib/types/signatures.js +1 -0
  94. package/lib/types/tunnel.js +1 -0
  95. package/lib/types/zk.js +1 -0
  96. package/lib/utils/auth.js +59 -0
  97. package/lib/utils/b64-json.js +17 -0
  98. package/lib/utils/bgp-listener.js +119 -0
  99. package/lib/utils/claims.js +98 -0
  100. package/lib/utils/env.js +15 -0
  101. package/lib/utils/error.js +50 -0
  102. package/lib/utils/generics.js +317 -0
  103. package/lib/utils/http-parser.js +246 -0
  104. package/lib/utils/index.js +13 -0
  105. package/lib/utils/logger.js +91 -0
  106. package/lib/utils/prepare-packets.js +71 -0
  107. package/lib/utils/redactions.js +177 -0
  108. package/lib/utils/retries.js +24 -0
  109. package/lib/utils/signatures/eth.js +32 -0
  110. package/lib/utils/signatures/index.js +7 -0
  111. package/lib/utils/socket-base.js +92 -0
  112. package/lib/utils/tls.js +58 -0
  113. package/lib/utils/ws.js +22 -0
  114. package/lib/utils/zk.js +585 -0
  115. package/package.json +5 -3
  116. package/lib/scripts/check-avs-registration.d.ts +0 -1
  117. package/lib/scripts/fallbacks/crypto.d.ts +0 -1
  118. package/lib/scripts/fallbacks/empty.d.ts +0 -3
  119. package/lib/scripts/fallbacks/re2.d.ts +0 -1
  120. package/lib/scripts/fallbacks/snarkjs.d.ts +0 -1
  121. package/lib/scripts/fallbacks/stwo.d.ts +0 -6
  122. package/lib/scripts/generate-provider-types.d.ts +0 -5
  123. package/lib/scripts/generate-receipt.d.ts +0 -9
  124. package/lib/scripts/jsc-cli-rpc.d.ts +0 -1
  125. package/lib/scripts/register-avs-operator.d.ts +0 -1
  126. package/lib/scripts/start-server.d.ts +0 -1
  127. package/lib/scripts/update-avs-metadata.d.ts +0 -1
  128. package/lib/scripts/utils.d.ts +0 -1
  129. package/lib/scripts/whitelist-operator.d.ts +0 -1
  130. /package/lib/{scripts/build-browser.d.ts → avs/contracts/ReclaimServiceManager.js} +0 -0
  131. /package/lib/{scripts/build-jsc.d.ts → avs/contracts/common.js} +0 -0
  132. /package/lib/{scripts/build-lib.d.ts → avs/types/index.js} +0 -0
  133. /package/lib/{scripts/generate-toprf-keys.d.ts → external-rpc/types.js} +0 -0
@@ -0,0 +1,4 @@
1
+ /* Autogenerated file. Do not edit manually. */
2
+ /* tslint:disable */
3
+ /* eslint-disable */
4
+ export { ReclaimServiceManager__factory } from "./ReclaimServiceManager__factory.js";
@@ -0,0 +1,2 @@
1
+ export * as factories from "./factories/index.js";
2
+ export { ReclaimServiceManager__factory } from "./factories/ReclaimServiceManager__factory.js";
@@ -0,0 +1,33 @@
1
+ import { Contract, JsonRpcProvider, Wallet } from 'ethers';
2
+ import { avsDirectoryABI } from "../abis/avsDirectoryABI.js";
3
+ import { delegationABI } from "../abis/delegationABI.js";
4
+ import { registryABI } from "../abis/registryABI.js";
5
+ import { CHAIN_CONFIGS, PRIVATE_KEY, SELECTED_CHAIN_ID } from "../config.js";
6
+ import { ReclaimServiceManager__factory } from "../contracts/index.js";
7
+ const configs = {};
8
+ /**
9
+ * get the contracts for the given chain ID
10
+ */
11
+ export function getContracts(chainId = SELECTED_CHAIN_ID) {
12
+ const config = CHAIN_CONFIGS[chainId];
13
+ if (!config) {
14
+ throw new Error(`No config found for chain ID: ${chainId}`);
15
+ }
16
+ configs[chainId] ||= initialiseContracts(config);
17
+ return configs[chainId];
18
+ }
19
+ export function initialiseContracts({ rpcUrl, stakeRegistryAddress, avsDirectoryAddress, contractAddress, delegationManagerAddress, }, privateKey = PRIVATE_KEY) {
20
+ const provider = new JsonRpcProvider(rpcUrl);
21
+ const wallet = privateKey
22
+ ? new Wallet(privateKey, provider)
23
+ : undefined;
24
+ return {
25
+ provider,
26
+ wallet,
27
+ delegationManager: new Contract(delegationManagerAddress, delegationABI, wallet || provider),
28
+ // eslint-disable-next-line camelcase
29
+ contract: ReclaimServiceManager__factory.connect(contractAddress, wallet || provider),
30
+ registryContract: new Contract(stakeRegistryAddress, registryABI, wallet || provider),
31
+ avsDirectory: new Contract(avsDirectoryAddress, avsDirectoryABI, wallet || provider),
32
+ };
33
+ }
@@ -0,0 +1,79 @@
1
+ import { hexlify, randomBytes, SigningKey } from 'ethers';
2
+ import { RECLAIM_PUBLIC_URL, SELECTED_CHAIN_ID } from "../config.js";
3
+ import { getContracts } from "./contracts.js";
4
+ import { logger as LOGGER } from "../../utils/index.js";
5
+ /**
6
+ * Registers the operator on the chain, if required.
7
+ * If already registered -- will just pass through
8
+ */
9
+ export async function registerOperator({ logger = LOGGER, chainId = SELECTED_CHAIN_ID, wallet = getContracts(chainId).wallet, reclaimRpcUrl = RECLAIM_PUBLIC_URL } = {}) {
10
+ const contracts = getContracts(chainId);
11
+ const delegationManager = contracts.delegationManager
12
+ .connect(wallet);
13
+ const avsDirectory = contracts.avsDirectory
14
+ .connect(wallet);
15
+ const contract = contracts.contract
16
+ .connect(wallet);
17
+ const registryContract = contracts.registryContract
18
+ .connect(wallet);
19
+ const addr = wallet.address;
20
+ try {
21
+ const tx1 = await delegationManager
22
+ .registerAsOperator({
23
+ earningsReceiver: addr,
24
+ delegationApprover: '0x0000000000000000000000000000000000000000',
25
+ stakerOptOutWindowBlocks: 0
26
+ }, '');
27
+ await tx1.wait();
28
+ logger.info('operator registered on DM successfully');
29
+ }
30
+ catch (err) {
31
+ if (!err.message.includes('operator has already registered')) {
32
+ throw err;
33
+ }
34
+ logger.info('Operator already registered on EL');
35
+ }
36
+ const salt = hexlify(randomBytes(32));
37
+ // Example expiry, 1 hour from now
38
+ const expiry = Math.floor(Date.now() / 1000) + 3600;
39
+ // Define the output structure
40
+ const operatorSignature = {
41
+ expiry: expiry,
42
+ salt: salt,
43
+ signature: ''
44
+ };
45
+ // Calculate the digest hash using the avsDirectory's method
46
+ const contractAddress = await contract.getAddress();
47
+ const digestHash = await avsDirectory
48
+ .calculateOperatorAVSRegistrationDigestHash(addr, contractAddress, salt, expiry);
49
+ // Sign the digest hash with the operator's private key
50
+ const signingKey = new SigningKey(wallet.privateKey);
51
+ const signature = signingKey.sign(digestHash);
52
+ // Encode the signature in the required format
53
+ operatorSignature.signature = signature.serialized;
54
+ logger.info('operator signature generated successfully');
55
+ if (!(await registryContract.operatorRegistered(addr))) {
56
+ const tx2 = await registryContract
57
+ .registerOperatorWithSignature(addr, operatorSignature);
58
+ await tx2.wait();
59
+ logger.info('operator registered on AVS successfully');
60
+ }
61
+ else {
62
+ logger.info('Operator already registered on AVS');
63
+ }
64
+ const existingMetadata = await contract.getMetadataForOperator(addr)
65
+ .catch(err => {
66
+ if (err.message.includes('Operator not found')) {
67
+ return undefined;
68
+ }
69
+ throw err;
70
+ });
71
+ const metadata = { addr, url: reclaimRpcUrl };
72
+ if (existingMetadata?.addr === metadata.addr
73
+ && existingMetadata?.url === metadata.url) {
74
+ logger.info('operator metadata already up to date');
75
+ return;
76
+ }
77
+ await contract.updateOperatorMetadata(metadata);
78
+ logger.info({ metadata }, 'operator metadata updated successfully');
79
+ }
@@ -0,0 +1,41 @@
1
+ import { EventLog, getBytes } from 'ethers';
2
+ import { getContracts } from "./contracts.js";
3
+ export async function createNewClaimRequestOnChain({ request, payer, chainId, ...rest }) {
4
+ const contracts = getContracts(chainId);
5
+ const contract = contracts.contract.connect(payer);
6
+ const ownerAddress = typeof rest.owner === 'string'
7
+ ? rest.owner
8
+ : rest.owner.address;
9
+ const fullRequest = {
10
+ ...request,
11
+ owner: ownerAddress
12
+ };
13
+ const signature = await getSignature();
14
+ const task = await contract.createNewTask(fullRequest, signature || '0x00');
15
+ const rslt = await task.wait();
16
+ const logs = rslt?.logs ?? [];
17
+ const eventLogs = logs.filter((log) => log instanceof EventLog);
18
+ // check task created event was emitted
19
+ const ev = eventLogs[0];
20
+ const arg = ev?.args;
21
+ return { task: arg, tx: rslt };
22
+ function getSignature() {
23
+ if (ownerAddress.toLowerCase() === payer.address.toLowerCase()) {
24
+ return;
25
+ }
26
+ if ('requestSignature' in rest) {
27
+ return rest.requestSignature;
28
+ }
29
+ if (typeof rest.owner !== 'object') {
30
+ throw new Error('Owner wallet must be provided or'
31
+ + ' requestSignature must be provided');
32
+ }
33
+ return signClaimRequest(fullRequest, rest.owner, chainId);
34
+ }
35
+ }
36
+ export async function signClaimRequest(request, owner, chainId) {
37
+ const contract = getContracts(chainId).contract;
38
+ const encoded = await contract.encodeClaimRequest(request);
39
+ const strSig = await owner.signMessage(getBytes(encoded));
40
+ return getBytes(strSig);
41
+ }
@@ -0,0 +1,432 @@
1
+ import { asciiToUint8Array } from '@reclaimprotocol/tls';
2
+ import { makeRpcTlsTunnel } from "./tunnels/make-rpc-tls-tunnel.js";
3
+ import { getAttestorClientFromPool } from "./utils/attestor-pool.js";
4
+ import { DEFAULT_HTTPS_PORT, PROVIDER_CTX, TOPRF_DOMAIN_SEPARATOR } from "../config/index.js";
5
+ import { ClaimTunnelRequest } from "../proto/api.js";
6
+ import { providers } from "../providers/index.js";
7
+ import { AttestorError, binaryHashToStr, canonicalStringify, generateTunnelId, getBlocksToReveal, getEngineProto, getProviderValue, isApplicationData, logger as LOGGER, makeDefaultOPRFOperator, makeHttpResponseParser, preparePacketsForReveal, redactSlices, uint8ArrayToStr, unixTimestampSeconds } from "../utils/index.js";
8
+ import { executeWithRetries } from "../utils/retries.js";
9
+ import { SIGNATURES } from "../utils/signatures/index.js";
10
+ import { getDefaultTlsOptions } from "../utils/tls.js";
11
+ /**
12
+ * Create a claim on the attestor
13
+ */
14
+ export function createClaimOnAttestor({ logger: _logger, maxRetries = 3, ...opts }) {
15
+ const logger = _logger
16
+ // if the client has already been initialised
17
+ // and no logger is provided, use the client's logger
18
+ // otherwise default to the global logger
19
+ || ('logger' in opts.client ? opts.client.logger : LOGGER);
20
+ return executeWithRetries(attempt => (_createClaimOnAttestor({
21
+ ...opts,
22
+ logger: attempt ? logger.child({ attempt }) : logger
23
+ })), { maxRetries, logger, shouldRetry });
24
+ }
25
+ function shouldRetry(err) {
26
+ if (err instanceof TypeError) {
27
+ return false;
28
+ }
29
+ // possibly a network error, or the server
30
+ // closed the connection before we received the full data
31
+ if (err?.message?.includes('stream ended before')) {
32
+ return true;
33
+ }
34
+ return err instanceof AttestorError
35
+ && err.code !== 'ERROR_INVALID_CLAIM'
36
+ && err.code !== 'ERROR_BAD_REQUEST'
37
+ && err.code !== 'ERROR_AUTHENTICATION_FAILED'
38
+ && err.code !== 'ERROR_TOPRF_OUT_OF_BOUNDS';
39
+ }
40
+ async function _createClaimOnAttestor({ name, params, secretParams, context, onStep, ownerPrivateKey, client: clientInit, logger = LOGGER, timestampS, updateProviderParams, updateParametersFromOprfData = true, ...zkOpts }) {
41
+ const provider = providers[name];
42
+ const hostPort = getProviderValue(params, provider.hostPort, secretParams);
43
+ const geoLocation = getProviderValue(params, provider.geoLocation, secretParams);
44
+ const proxySessionId = getProviderValue(params, provider.proxySessionId, secretParams);
45
+ const providerTlsOpts = getProviderValue(params, provider.additionalClientOptions);
46
+ const tlsOpts = {
47
+ ...getDefaultTlsOptions(),
48
+ fetchCertificateBytes: fetchCertificateBytesFromAttestor,
49
+ ...providerTlsOpts
50
+ };
51
+ const { zkEngine = 'snarkjs' } = zkOpts;
52
+ let redactionMode = getProviderValue(params, provider.writeRedactionMode);
53
+ const [host, port] = hostPort.split(':');
54
+ const resParser = makeHttpResponseParser();
55
+ let client;
56
+ let lastMsgRevealed = false;
57
+ const revealMap = new Map();
58
+ onStep?.({ name: 'connecting' });
59
+ let endedHttpRequest;
60
+ const createTunnelReq = {
61
+ host,
62
+ port: port ? +port : DEFAULT_HTTPS_PORT,
63
+ geoLocation,
64
+ proxySessionId,
65
+ id: generateTunnelId()
66
+ };
67
+ logger = logger.child({ tunnelId: createTunnelReq.id });
68
+ const authRequest = 'authRequest' in clientInit
69
+ ? (typeof clientInit.authRequest === 'function'
70
+ ? await clientInit.authRequest()
71
+ : clientInit.authRequest)
72
+ : undefined;
73
+ const tunnel = await makeRpcTlsTunnel({
74
+ tlsOpts,
75
+ connect: (connectMsgs) => {
76
+ let created = false;
77
+ if ('metadata' in clientInit) {
78
+ client = clientInit;
79
+ }
80
+ else {
81
+ client = getAttestorClientFromPool(clientInit.url, () => {
82
+ created = true;
83
+ return {
84
+ authRequest: authRequest,
85
+ initMessages: connectMsgs,
86
+ logger
87
+ };
88
+ });
89
+ }
90
+ if (!created) {
91
+ client
92
+ .waitForInit()
93
+ .then(() => client.sendMessage(...connectMsgs))
94
+ .catch(err => {
95
+ logger.error({ err }, 'error in sending init msgs');
96
+ });
97
+ }
98
+ return client;
99
+ },
100
+ logger,
101
+ request: createTunnelReq,
102
+ onMessage(data) {
103
+ logger.debug({ bytes: data.length }, 'recv data from server');
104
+ resParser.onChunk(data);
105
+ if (resParser.res.complete) {
106
+ logger?.debug('got complete HTTP response from server');
107
+ // wait a little bit to make sure the client has
108
+ // finished writing the response
109
+ setTimeout(() => {
110
+ endedHttpRequest?.();
111
+ }, 100);
112
+ }
113
+ },
114
+ onClose(err) {
115
+ const level = err ? 'error' : 'debug';
116
+ logger?.[level]({ err }, 'tls session ended');
117
+ endedHttpRequest?.(err);
118
+ try {
119
+ resParser.streamEnded();
120
+ }
121
+ catch { }
122
+ },
123
+ });
124
+ const { version: tlsVersion, cipherSuite } = tunnel.tls.getMetadata();
125
+ if (tlsVersion === 'TLS1_2' && redactionMode !== 'zk') {
126
+ redactionMode = 'zk';
127
+ logger.info('TLS1.2 detected, defaulting to zk redaction mode');
128
+ }
129
+ const { redactions, data: requestStr } = provider.createRequest(
130
+ // @ts-ignore
131
+ secretParams, params, logger);
132
+ const requestData = typeof requestStr === 'string'
133
+ ? asciiToUint8Array(requestStr)
134
+ : requestStr;
135
+ logger.debug({ redactions: redactions.length }, 'generated request');
136
+ const waitForAllData = new Promise((resolve, reject) => {
137
+ endedHttpRequest = err => (err ? reject(err) : resolve());
138
+ });
139
+ onStep?.({ name: 'sending-request-data' });
140
+ try {
141
+ if (redactionMode === 'zk') {
142
+ await writeRedactedZk();
143
+ }
144
+ else {
145
+ await writeRedactedWithKeyUpdate();
146
+ }
147
+ logger.info('wrote request to server');
148
+ }
149
+ catch (err) {
150
+ // wait for complete stream end when the session is closed
151
+ // mid-write, as this means the server could not process
152
+ // our request due to some error. Hope the stream end
153
+ // error will be more descriptive
154
+ logger.error({ err }, 'session errored during write, waiting for stream end');
155
+ }
156
+ onStep?.({ name: 'waiting-for-response' });
157
+ await waitForAllData;
158
+ await tunnel.close();
159
+ logger.info('session closed, processing response');
160
+ // update the response selections
161
+ if (updateProviderParams) {
162
+ const { params: updatedParms, secretParams: updatedSecretParms } = await updateProviderParams(tunnel.transcript, tlsVersion ?? 'TLS1_2');
163
+ params = { ...params, ...updatedParms };
164
+ secretParams = { ...secretParams, ...updatedSecretParms };
165
+ }
166
+ const signatureAlg = SIGNATURES[client.metadata.signatureType];
167
+ let serverIV;
168
+ let clientIV;
169
+ const [serverBlock] = getLastBlocks('server', 1);
170
+ if (serverBlock?.message.type === 'ciphertext') {
171
+ serverIV = serverBlock.message.fixedIv;
172
+ }
173
+ const [clientBlock] = getLastBlocks('client', 1);
174
+ if (clientBlock?.message.type === 'ciphertext') {
175
+ clientIV = clientBlock.message.fixedIv;
176
+ }
177
+ const transcript = await generateTranscript();
178
+ // now that we have the full transcript, we need
179
+ // to generate the ZK proofs & send them to the attestor
180
+ // to verify & sign our claim
181
+ const claimTunnelReq = ClaimTunnelRequest.create({
182
+ request: createTunnelReq,
183
+ data: {
184
+ provider: name,
185
+ parameters: canonicalStringify(params),
186
+ context: canonicalStringify(context),
187
+ timestampS: timestampS ?? unixTimestampSeconds(),
188
+ owner: getAddress(),
189
+ },
190
+ transcript: transcript,
191
+ zkEngine: getEngineProto(zkEngine),
192
+ fixedServerIV: serverIV,
193
+ fixedClientIV: clientIV,
194
+ });
195
+ onStep?.({ name: 'waiting-for-verification' });
196
+ const claimTunnelBytes = ClaimTunnelRequest
197
+ .encode(claimTunnelReq).finish();
198
+ const requestSignature = await signatureAlg
199
+ .sign(claimTunnelBytes, ownerPrivateKey);
200
+ claimTunnelReq.signatures = { requestSignature };
201
+ const result = await client.rpc('claimTunnel', claimTunnelReq);
202
+ logger.info({ success: !!result.claim }, 'recv claim response');
203
+ return result;
204
+ async function fetchCertificateBytesFromAttestor(url) {
205
+ if (!client) {
206
+ throw new Error('attestor client not initialized');
207
+ }
208
+ const result = await client.rpc('fetchCertificateBytes', { url });
209
+ return result.bytes;
210
+ }
211
+ async function writeRedactedWithKeyUpdate() {
212
+ let currentIndex = 0;
213
+ for (const section of redactions) {
214
+ const block = requestData
215
+ .slice(currentIndex, section.fromIndex);
216
+ if (block.length) {
217
+ await writeWithReveal(block, true);
218
+ }
219
+ const redacted = requestData
220
+ .slice(section.fromIndex, section.toIndex);
221
+ await writeWithReveal(redacted, false);
222
+ currentIndex = section.toIndex;
223
+ }
224
+ // write if redactions were there
225
+ const lastBlockStart = redactions?.[redactions.length - 1]
226
+ ?.toIndex || 0;
227
+ const block = requestData.slice(lastBlockStart);
228
+ if (block.length) {
229
+ await writeWithReveal(block, true);
230
+ }
231
+ }
232
+ async function writeRedactedZk() {
233
+ let blocksWritten = tunnel.transcript.length;
234
+ await tunnel.tls.write(requestData);
235
+ blocksWritten = tunnel.transcript.length - blocksWritten;
236
+ setRevealOfLastSentBlocks({
237
+ type: 'zk',
238
+ redactedPlaintext: redactSlices(requestData, redactions)
239
+ }, blocksWritten);
240
+ }
241
+ /**
242
+ * Write data to the tunnel, with the option to mark the packet
243
+ * as revealable to the attestor or not
244
+ */
245
+ async function writeWithReveal(data, reveal) {
246
+ // if the reveal state has changed, update the traffic keys
247
+ // to not accidentally reveal a packet not meant to be revealed
248
+ // and vice versa
249
+ if (reveal !== lastMsgRevealed) {
250
+ await tunnel.tls.updateTrafficKeys();
251
+ }
252
+ let blocksWritten = tunnel.transcript.length;
253
+ await tunnel.write(data);
254
+ blocksWritten = tunnel.transcript.length - blocksWritten;
255
+ // now we mark the packet to be revealed to the attestor
256
+ setRevealOfLastSentBlocks(reveal ? { type: 'complete' } : undefined, blocksWritten);
257
+ lastMsgRevealed = reveal;
258
+ }
259
+ function setRevealOfLastSentBlocks(reveal, nBlocks = 1) {
260
+ const lastBlocks = getLastBlocks('client', nBlocks);
261
+ if (!lastBlocks.length) {
262
+ return;
263
+ }
264
+ for (const block of lastBlocks) {
265
+ setRevealOfMessage(block.message, reveal);
266
+ }
267
+ }
268
+ function getLastBlocks(sender, nBlocks) {
269
+ // set the correct index for the server blocks
270
+ const lastBlocks = [];
271
+ for (let i = tunnel.transcript.length - 1; i >= 0; i--) {
272
+ const block = tunnel.transcript[i];
273
+ if (block.sender === sender) {
274
+ lastBlocks.push(block);
275
+ if (lastBlocks.length === nBlocks) {
276
+ break;
277
+ }
278
+ }
279
+ }
280
+ return lastBlocks;
281
+ }
282
+ /**
283
+ * Generate transcript with reveal data for the attestor to verify
284
+ */
285
+ async function generateTranscript() {
286
+ await addServerSideReveals();
287
+ const startMs = Date.now();
288
+ const revealedMessages = await preparePacketsForReveal(tunnel.transcript, revealMap, {
289
+ logger,
290
+ cipherSuite: cipherSuite,
291
+ onZkProgress(done, total) {
292
+ const timeSinceStartMs = Date.now() - startMs;
293
+ const timePerBlockMs = timeSinceStartMs / done;
294
+ const timeLeftMs = timePerBlockMs * (total - done);
295
+ onStep?.({
296
+ name: 'generating-zk-proofs',
297
+ proofsDone: done,
298
+ proofsTotal: total,
299
+ approxTimeLeftS: Math.round(timeLeftMs / 1000),
300
+ });
301
+ },
302
+ ...zkOpts,
303
+ });
304
+ return revealedMessages;
305
+ }
306
+ /**
307
+ * Add reveals for server side blocks, using
308
+ * the provider's redaction function if available.
309
+ * Otherwise, opts to reveal all server side blocks.
310
+ */
311
+ async function addServerSideReveals() {
312
+ const allPackets = tunnel.transcript;
313
+ let serverPacketsToReveal = 'all';
314
+ const packets = [];
315
+ const serverBlocks = [];
316
+ for (const b of allPackets) {
317
+ if (b.message.type !== 'ciphertext'
318
+ || !isApplicationData(b.message, tlsVersion)) {
319
+ continue;
320
+ }
321
+ const plaintext = tlsVersion === 'TLS1_3'
322
+ ? b.message.plaintext.slice(0, -1)
323
+ : b.message.plaintext;
324
+ packets.push({
325
+ message: plaintext,
326
+ sender: b.sender
327
+ });
328
+ if (b.sender === 'server') {
329
+ serverBlocks.push({
330
+ plaintext: plaintext,
331
+ message: b.message
332
+ });
333
+ }
334
+ }
335
+ if (provider.getResponseRedactions) {
336
+ serverPacketsToReveal = await getBlocksToReveal(serverBlocks, total => provider.getResponseRedactions({
337
+ response: total,
338
+ params,
339
+ logger,
340
+ ctx: PROVIDER_CTX
341
+ }), performOprf);
342
+ }
343
+ const revealedPackets = packets
344
+ .filter(p => p.sender === 'client');
345
+ if (serverPacketsToReveal === 'all') {
346
+ // reveal all server side blocks
347
+ for (const { message, sender } of allPackets) {
348
+ if (sender === 'server') {
349
+ setRevealOfMessage(message, { type: 'complete' });
350
+ }
351
+ }
352
+ revealedPackets.push(...packets.filter(p => p.sender === 'server'));
353
+ }
354
+ else {
355
+ for (const { block, redactedPlaintext, overshotToprfFromPrevBlock, toprfs, oprfRawMarkers } of serverPacketsToReveal) {
356
+ setRevealOfMessage(block.message, {
357
+ type: 'zk',
358
+ redactedPlaintext,
359
+ toprfs,
360
+ oprfRawMarkers,
361
+ overshotToprfFromPrevBlock
362
+ });
363
+ revealedPackets.push({ sender: 'server', message: redactedPlaintext });
364
+ if (updateParametersFromOprfData && toprfs) {
365
+ let strParams = canonicalStringify(params);
366
+ for (const toprf of toprfs) {
367
+ const ogText = uint8ArrayToStr(toprf.plaintext);
368
+ const hashedText = binaryHashToStr(toprf.nullifier, toprf.dataLocation.length);
369
+ strParams = strParams.replaceAll(ogText, hashedText);
370
+ }
371
+ params = JSON.parse(strParams);
372
+ }
373
+ }
374
+ }
375
+ await provider.assertValidProviderReceipt({
376
+ receipt: revealedPackets,
377
+ params: {
378
+ ...params,
379
+ // provide secret params for proper
380
+ // request body validation
381
+ secretParams,
382
+ },
383
+ logger,
384
+ ctx: PROVIDER_CTX
385
+ });
386
+ // reveal all handshake blocks
387
+ // so the attestor can verify there was no
388
+ // hanky-panky
389
+ for (const p of allPackets) {
390
+ if (p.message.type !== 'ciphertext') {
391
+ continue;
392
+ }
393
+ // break the moment we hit the first
394
+ // application data packet
395
+ if (isApplicationData(p.message, tlsVersion)) {
396
+ break;
397
+ }
398
+ setRevealOfMessage(p.message, { type: 'complete' });
399
+ }
400
+ }
401
+ async function performOprf(plaintext) {
402
+ logger.info({ length: plaintext.length }, 'generating OPRF...');
403
+ const oprfOperator = zkOpts.oprfOperators?.['chacha20']
404
+ || makeDefaultOPRFOperator('chacha20', zkEngine, logger);
405
+ const reqData = await oprfOperator.generateOPRFRequestData(plaintext, TOPRF_DOMAIN_SEPARATOR, logger);
406
+ const res = await client.rpc('toprf', {
407
+ maskedData: reqData.maskedData,
408
+ engine: getEngineProto(zkEngine)
409
+ });
410
+ const nullifier = await oprfOperator.finaliseOPRF(client.initResponse.toprfPublicKey, reqData, [res]);
411
+ const data = {
412
+ nullifier,
413
+ responses: [res],
414
+ mask: reqData.mask,
415
+ dataLocation: undefined,
416
+ plaintext
417
+ };
418
+ return data;
419
+ }
420
+ function setRevealOfMessage(message, reveal) {
421
+ if (reveal) {
422
+ revealMap.set(message, reveal);
423
+ return;
424
+ }
425
+ revealMap.delete(message);
426
+ }
427
+ function getAddress() {
428
+ const { getAddress, getPublicKey } = signatureAlg;
429
+ const pubKey = getPublicKey(ownerPrivateKey);
430
+ return getAddress(pubKey);
431
+ }
432
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./create-claim.js";
2
+ export * from "./utils/attestor-pool.js";
3
+ export * from "./utils/client-socket.js";
@@ -0,0 +1,51 @@
1
+ import { AttestorError } from "../../utils/index.js";
2
+ /**
3
+ * Makes a tunnel communication wrapper for a TCP tunnel.
4
+ *
5
+ * It listens for messages and disconnect events from the server,
6
+ * and appropriately calls the `onMessage` and `onClose` callbacks.
7
+ */
8
+ export const makeRpcTcpTunnel = ({ tunnelId, client, onClose, onMessage, }) => {
9
+ let closed = false;
10
+ client.addEventListener('tunnel-message', onMessageListener);
11
+ client.addEventListener('tunnel-disconnect-event', onDisconnectListener);
12
+ client.addEventListener('connection-terminated', onConnectionTerminatedListener);
13
+ return {
14
+ async write(message) {
15
+ await client.sendMessage({ tunnelMessage: { tunnelId, message } });
16
+ },
17
+ async close(err) {
18
+ if (closed) {
19
+ return;
20
+ }
21
+ onErrorRecv(err);
22
+ await client.rpc('disconnectTunnel', { id: tunnelId });
23
+ }
24
+ };
25
+ function onMessageListener({ data }) {
26
+ if (data.tunnelId !== tunnelId) {
27
+ return;
28
+ }
29
+ onMessage?.(data.message);
30
+ }
31
+ function onDisconnectListener({ data }) {
32
+ if (data.tunnelId !== tunnelId) {
33
+ return;
34
+ }
35
+ onErrorRecv(data.error?.code
36
+ ? AttestorError.fromProto(data.error)
37
+ : undefined);
38
+ }
39
+ function onConnectionTerminatedListener({ data }) {
40
+ onErrorRecv(data);
41
+ }
42
+ function onErrorRecv(err) {
43
+ client.logger?.debug({ tunnelId, err }, 'TCP tunnel closed');
44
+ client.removeEventListener('tunnel-message', onMessageListener);
45
+ client.removeEventListener('tunnel-disconnect-event', onDisconnectListener);
46
+ client.removeEventListener('connection-terminated', onConnectionTerminatedListener);
47
+ onClose?.(err);
48
+ onClose = undefined;
49
+ closed = true;
50
+ }
51
+ };