UNPKG

@zkp2p/reclaim-witness-sdk

Version:

<div> <div> <img src="https://raw.githubusercontent.com/reclaimprotocol/.github/main/assets/banners/Attestor-Core.png" /> </div> </div>

620 lines 61.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const tls_1 = require("@reclaimprotocol/tls"); const ethers_1 = require("ethers"); const utils_1 = require("ethers/lib/utils"); const config_1 = require("../../config"); const api_1 = require("../../proto/api"); const utils_2 = require("../../providers/http/utils"); const utils_3 = require("../../utils"); const OK_HTTP_HEADER = 'HTTP/1.1 200'; const dateHeaderRegex = '[dD]ate: ((?:Mon|Tue|Wed|Thu|Fri|Sat|Sun), (?:[0-3][0-9]) (?:Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (?:[0-9]{4}) (?:[01][0-9]|2[0-3])(?::[0-5][0-9]){2} GMT)'; const dateDiff = 1000 * 60 * 10; // allow 10 min difference const HTTP_PROVIDER = { hostPort: getHostPort, writeRedactionMode(params) { return ('writeRedactionMode' in params) ? params.writeRedactionMode : undefined; }, geoLocation(params, secretParams) { return ('geoLocation' in params) ? getGeoLocation(params, secretParams) : undefined; }, additionalClientOptions(params) { let defaultOptions = { applicationLayerProtocols: ['http/1.1'] }; if ('additionalClientOptions' in params) { defaultOptions = { ...defaultOptions, ...params.additionalClientOptions }; } return defaultOptions; }, createRequest(secretParams, params, logger) { var _a, _b; if (!secretParams.cookieStr && !secretParams.authorisationHeader && !secretParams.headers) { throw new Error('auth parameters are not set'); } const pubHeaders = params.headers || {}; const secHeaders = { ...secretParams.headers }; if (secretParams.cookieStr) { secHeaders['Cookie'] = secretParams.cookieStr; } if (secretParams.authorisationHeader) { secHeaders['Authorization'] = secretParams.authorisationHeader; } const hasUserAgent = Object.keys(pubHeaders) .some(k => k.toLowerCase() === 'user-agent') || Object.keys(secHeaders) .some(k => k.toLowerCase() === 'user-agent'); if (!hasUserAgent) { //only set user-agent if not set by provider pubHeaders['User-Agent'] = config_1.RECLAIM_USER_AGENT; } const newParams = substituteParamValues(params, secretParams); params = newParams.newParams; const url = new URL(params.url); const { pathname } = url; const searchParams = params.url.includes('?') ? params.url.split('?')[1] : ''; logger.info({ url: params.url, path: pathname, query: searchParams.toString() }); const body = params.body instanceof Uint8Array ? params.body : (0, tls_1.strToUint8Array)(params.body || ''); const contentLength = body.length; const reqLine = `${params.method} ${pathname}${(searchParams === null || searchParams === void 0 ? void 0 : searchParams.length) ? '?' + searchParams : ''} HTTP/1.1`; const secHeadersList = (0, utils_2.buildHeaders)(secHeaders); logger.info({ requestLine: reqLine }); const httpReqHeaderStr = [ reqLine, `Host: ${getHostHeaderString(url)}`, `Content-Length: ${contentLength}`, 'Connection: close', //no compression 'Accept-Encoding: identity', ...(0, utils_2.buildHeaders)(pubHeaders), ...secHeadersList, '\r\n', ].join('\r\n'); const headerStr = (0, tls_1.strToUint8Array)(httpReqHeaderStr); const data = (0, tls_1.concatenateUint8Arrays)([headerStr, body]); // hide all secret headers const secHeadersStr = secHeadersList.join('\r\n'); const tokenStartIndex = (0, utils_3.findIndexInUint8Array)(data, (0, tls_1.strToUint8Array)(secHeadersStr)); const redactions = [ { fromIndex: tokenStartIndex, toIndex: tokenStartIndex + secHeadersStr.length, } ]; if (((_a = newParams.hiddenBodyParts) === null || _a === void 0 ? void 0 : _a.length) > 0) { for (const hiddenBodyPart of newParams.hiddenBodyParts) { if (hiddenBodyPart.length) { redactions.push({ fromIndex: headerStr.length + hiddenBodyPart.index, toIndex: headerStr.length + hiddenBodyPart.index + hiddenBodyPart.length, }); } } } if (((_b = newParams.hiddenURLParts) === null || _b === void 0 ? void 0 : _b.length) > 0) { for (const hiddenURLPart of newParams.hiddenURLParts) { if (hiddenURLPart.length) { redactions.push({ fromIndex: hiddenURLPart.index, toIndex: hiddenURLPart.index + hiddenURLPart.length, }); } } } redactions.sort((a, b) => a.toIndex - b.toIndex); return { data, redactions: redactions, }; }, getResponseRedactions({ response, params: rawParams, logger, ctx }) { var _a, _b; logger.debug({ response: utils_1.base64.encode(response), params: rawParams }); const res = (0, utils_2.parseHttpResponse)(response); if (!((_a = rawParams.responseRedactions) === null || _a === void 0 ? void 0 : _a.length)) { return []; } if (((res.statusCode / 100) >> 0) !== 2) { logger.error({ response: utils_1.base64.encode(response), params: rawParams }); throw new Error(`Expected status 2xx, got ${res.statusCode} (${res.statusMessage})`); } const newParams = substituteParamValues(rawParams, undefined, true); const params = newParams.newParams; const headerEndIndex = res.statusLineEndIndex; const bodyStartIdx = (_b = res.bodyStartIndex) !== null && _b !== void 0 ? _b : 0; if (bodyStartIdx < 4) { logger.error({ response: (0, utils_3.uint8ArrayToBinaryStr)(response) }); throw new Error('Failed to find response body'); } const reveals = [ { fromIndex: 0, toIndex: headerEndIndex } ]; //reveal double CRLF which separates headers from body if (shouldRevealCrlf(ctx)) { const crlfs = response .slice(res.headerEndIdx, res.headerEndIdx + 4); if (!(0, tls_1.areUint8ArraysEqual)(crlfs, (0, tls_1.strToUint8Array)('\r\n\r\n'))) { logger.error({ response: (0, utils_3.uint8ArrayToBinaryStr)(response) }); throw new Error(`Failed to find header/body separator at index ${res.headerEndIdx}`); } } reveals.push({ fromIndex: res.headerEndIdx, toIndex: res.headerEndIdx + 4 }); //reveal date header if (res.headerIndices['date']) { reveals.push(res.headerIndices['date']); } const body = (0, utils_3.uint8ArrayToBinaryStr)(res.body); const redactions = []; for (const rs of params.responseRedactions || []) { const processor = processRedactionRequest(body, rs, bodyStartIdx, res.chunks); for (const { reveal, redactions: reds } of processor) { reveals.push(reveal); redactions.push(...reds); } } reveals.sort((a, b) => a.toIndex - b.toIndex); if (reveals.length > 1) { let currentIndex = 0; for (const r of reveals) { if (currentIndex < r.fromIndex) { redactions.push({ fromIndex: currentIndex, toIndex: r.fromIndex }); } currentIndex = r.toIndex; } redactions.push({ fromIndex: currentIndex, toIndex: response.length }); } for (const r of reveals) { if (!r.hash) { continue; } redactions.push(r); } redactions.sort((a, b) => a.toIndex - b.toIndex); return redactions; }, assertValidProviderReceipt({ receipt, params: paramsAny, logger, ctx }) { logTranscript(); let extractedParams = {}; const secretParams = ('secretParams' in paramsAny) ? paramsAny.secretParams : undefined; const newParams = substituteParamValues(paramsAny, secretParams, !secretParams); const params = newParams.newParams; extractedParams = { ...extractedParams, ...newParams.extractedValues }; const req = (0, utils_3.getHttpRequestDataFromTranscript)(receipt); if (req.method !== params.method.toLowerCase()) { throw new Error(`Invalid method: ${req.method}`); } const url = new URL(params.url); const { protocol, pathname } = url; if (protocol !== 'https:') { logger.error('params URL: %s', params.url); throw new Error(`Expected protocol: https, found: ${protocol}`); } const searchParams = params.url.includes('?') ? params.url.split('?')[1] : ''; //brackets in URL path turn into %7B and %7D, so replace them back const expectedPath = pathname.replaceAll('%7B', '{').replaceAll('%7D', '}') + ((searchParams === null || searchParams === void 0 ? void 0 : searchParams.length) ? '?' + searchParams : ''); if (!(0, utils_2.matchRedactedStrings)((0, tls_1.strToUint8Array)(expectedPath), (0, tls_1.strToUint8Array)(req.url))) { logger.error('params URL: %s', params.url); throw new Error(`Expected path: ${expectedPath}, found: ${req.url}`); } const expectedHostStr = getHostHeaderString(url); if (req.headers.host !== expectedHostStr) { throw new Error(`Expected host: ${expectedHostStr}, found: ${req.headers.host}`); } const connectionHeader = req.headers['connection']; if (connectionHeader !== 'close') { throw new Error(`Connection header must be "close", got "${connectionHeader}"`); } const serverBlocks = receipt .filter(s => s.sender === 'server') .map((r) => r.message) .filter(b => !b.every(b => b === utils_3.REDACTION_CHAR_CODE)); // filter out fully redacted blocks const response = concatArrays(...serverBlocks); let res; res = (0, utils_3.uint8ArrayToStr)(response); const okRegex = (0, utils_2.makeRegex)('^HTTP\\/1.1 2\\d{2}'); const matchRes = okRegex.exec(res); if (!matchRes) { const statusRegex = (0, utils_2.makeRegex)('^HTTP\\/1.1 (\\d{3})'); const matchRes = statusRegex.exec(res); if (matchRes && matchRes.length > 1) { throw new Error(`Provider returned error ${matchRes[1]}"`); } let lineEnd = res.indexOf('*'); if (lineEnd === -1) { lineEnd = res.indexOf('\n'); } if (lineEnd === -1) { lineEnd = OK_HTTP_HEADER.length; } throw new Error(`Response did not start with \"HTTP/1.1 2XX\" got "${res.slice(0, lineEnd)}"`); } let bodyStart; if (shouldRevealCrlf(ctx)) { bodyStart = res.indexOf('\r\n\r\n', OK_HTTP_HEADER.length) + 4; if (bodyStart < 4) { throw new Error('Response body start not found'); } } else { bodyStart = OK_HTTP_HEADER.length; } //validate server Date header if present const dateHeader = (0, utils_2.makeRegex)(dateHeaderRegex).exec(res); if ((dateHeader === null || dateHeader === void 0 ? void 0 : dateHeader.length) > 1) { const serverDate = new Date(dateHeader[1]); if ((Date.now() - serverDate.getTime()) > dateDiff) { logger.info({ dateHeader: dateHeader[0], current: Date.now() }, 'date header is off'); throw new Error(`Server date is off by "${(Date.now() - serverDate.getTime()) / 1000} s"`); } } const paramBody = params.body instanceof Uint8Array ? params.body : (0, tls_1.strToUint8Array)(params.body || ''); if (paramBody.length > 0 && !(0, utils_2.matchRedactedStrings)(paramBody, req.body)) { throw new Error('request body mismatch'); } //remove asterisks to account for chunks in the middle of revealed strings if (!secretParams) { res = res.slice(bodyStart).replace(/(\*){3,}/g, ''); } for (const { type, value, invert, hash: unsafeHash } of params.responseMatches || []) { const convertedUnsafeHash = Boolean(unsafeHash); const inv = Boolean(invert); // explicitly cast to boolean switch (type) { case 'regex': const regexRes = (0, utils_2.makeRegex)(value).exec(res); const match = regexRes !== null; if (match === inv) { // if both true or both false then fail throw new Error('Invalid receipt.' + ` Regex "${value}" ${invert ? 'matched' : "didn't match"}`); } if (!match) { continue; } const groups = regexRes === null || regexRes === void 0 ? void 0 : regexRes.groups; for (const paramName in groups || []) { if (paramName in extractedParams) { throw new Error(`Duplicate parameter ${paramName}`); } if (convertedUnsafeHash) { extractedParams[paramName] = ethers_1.utils.keccak256((0, tls_1.strToUint8Array)(groups[paramName])); } else { extractedParams[paramName] = groups[paramName]; } } break; case 'contains': const includes = res.includes(value); if (includes === inv) { throw new Error(`Invalid receipt. Response ${invert ? 'contains' : 'does not contain'} "${value}"`); } break; default: throw new Error(`Invalid response match type ${type}`); } } function concatArrays(...bufs) { const totalSize = bufs.reduce((acc, e) => acc + e.length, 0); const merged = new Uint8Array(totalSize); let lenDone = 0; for (const array of bufs) { merged.set(array, lenDone); lenDone += array.length; } return merged; } return { extractedParameters: extractedParams }; function logTranscript() { const clientMsgs = receipt.filter(s => s.sender === 'client').map(m => m.message); const serverMsgs = receipt.filter(s => s.sender === 'server').map(m => m.message); const clientTranscript = utils_1.base64.encode((0, tls_1.concatenateUint8Arrays)(clientMsgs)); const serverTranscript = utils_1.base64.encode((0, tls_1.concatenateUint8Arrays)(serverMsgs)); logger.debug({ request: clientTranscript, response: serverTranscript, params: paramsAny }); } }, }; // revealing CRLF is a breaking change -- and should only be done // if the client's version supports it function shouldRevealCrlf({ version }) { return version >= api_1.AttestorVersion.ATTESTOR_VERSION_2_0_1; } function getHostPort(params, secretParams) { const { host } = new URL(getURL(params, secretParams)); if (!host) { throw new Error('url is incorrect'); } return host; } /** * Obtain the host header string from the URL. * https://stackoverflow.com/a/3364396 */ function getHostHeaderString(url) { const host = url.hostname; const port = url.port; return port && +port !== config_1.DEFAULT_HTTPS_PORT ? `${host}:${port}` : host; } const paramsRegex = /{{([^{}]+)}}/sgi; function* processRedactionRequest(body, rs, bodyStartIdx, resChunks) { let element = body; let elementIdx = 0; let elementLength = -1; if (rs.xPath) { const indexes = (0, utils_2.extractHTMLElementsIndexes)(body, rs.xPath, !!rs.jsonPath); for (const { start, end } of indexes) { element = body.slice(start, end); elementIdx = start; elementLength = end - start; if (rs.jsonPath) { yield* processJsonPath(); } else if (rs.regex) { yield* processRegexp(); } else { yield* addRedaction(); } } } else if (rs.jsonPath) { yield* processJsonPath(); } else if (rs.regex) { yield* processRegexp(); } else { throw new Error('Expected either xPath, jsonPath or regex for redaction'); } function* processJsonPath() { const jsonPathIndexes = (0, utils_2.extractJSONValueIndexes)(element, rs.jsonPath); // eslint-disable-next-line max-depth const eIndex = elementIdx; for (const ji of jsonPathIndexes) { const jStart = ji.start; const jEnd = ji.end; element = body.slice(eIndex + jStart, eIndex + jEnd); elementIdx = eIndex + jStart; elementLength = jEnd - jStart; // eslint-disable-next-line max-depth if (rs.regex) { yield* processRegexp(); } else { yield* addRedaction(); } } } function* processRegexp() { utils_3.logger.debug({ element: utils_1.base64.encode((0, tls_1.strToUint8Array)(element)), body: utils_1.base64.encode((0, tls_1.strToUint8Array)(body)) }); const regexp = (0, utils_2.makeRegex)(rs.regex); const elem = element || body; const match = regexp.exec(elem); // eslint-disable-next-line max-depth if (!(match === null || match === void 0 ? void 0 : match[0])) { throw new Error(`regexp ${rs.regex} does not match found element '${utils_1.base64.encode((0, tls_1.strToUint8Array)(elem))}'`); } elementIdx += match.index; elementLength = regexp.lastIndex - match.index; element = match[0]; if (rs.hash && (!match.groups || Object.keys(match.groups).length > 1)) { throw new Error('Exactly one named capture group is needed per hashed redaction'); } // if there are groups in the regex, // we'll only hash the group values if (!rs.hash || !match.groups) { yield* addRedaction(); return; } const fullStr = match[0]; const grp = Object.values(match.groups)[0]; const grpIdx = fullStr.indexOf(grp); // don't hash the entire regex, we'll hash the group values elementLength = grpIdx; element = fullStr.slice(0, grpIdx); yield* addRedaction(null); elementIdx += grpIdx; element = grp; elementLength = grp.length; const reveal = getReveal(elementIdx, elementLength, rs.hash); const chunkReds = (0, utils_2.getRedactionsForChunkHeaders)(reveal.fromIndex, reveal.toIndex, resChunks); if (chunkReds.length) { throw new Error('Hash redactions cannot be performed if ' + 'the redacted string is split between 2' + ' or more HTTP chunks'); } yield { reveal, redactions: chunkReds }; elementIdx += grp.length; element = fullStr.slice(grpIdx + grp.length); elementLength = element.length; yield* addRedaction(null); } // eslint-disable-next-line unicorn/consistent-function-scoping function* addRedaction(hash = rs.hash, _resChunks = resChunks) { if (elementIdx < 0 || !elementLength) { return; } const reveal = getReveal(elementIdx, elementLength, hash || undefined); yield { reveal, redactions: (0, utils_2.getRedactionsForChunkHeaders)(reveal.fromIndex, reveal.toIndex, _resChunks) }; } function getReveal(startIdx, len, hash) { const from = (0, utils_2.convertResponsePosToAbsolutePos)(startIdx, bodyStartIdx, resChunks); const to = (0, utils_2.convertResponsePosToAbsolutePos)(startIdx + len, bodyStartIdx, resChunks); return { fromIndex: from, toIndex: to, hash }; } } function substituteParamValues(currentParams, secretParams, ignoreMissingParams) { const params = JSON.parse(JSON.stringify(currentParams)); let extractedValues = {}; const hiddenURLParts = []; const urlParams = extractAndReplaceTemplateValues(params.url, ignoreMissingParams); if (urlParams) { params.url = urlParams.newParam; extractedValues = { ...urlParams.extractedValues }; if (urlParams.hiddenParts.length) { const host = getHostHeaderString(new URL(params.url)); const offset = `https://${host}`.length - currentParams.method.length - 1; //space between method and start of the path for (const hiddenURLPart of urlParams.hiddenParts) { hiddenURLParts.push({ index: hiddenURLPart.index - offset, length: hiddenURLPart.length }); } } } let bodyParams; let hiddenBodyParts = []; if (params.body) { const strBody = typeof params.body === 'string' ? params.body : (0, utils_3.uint8ArrayToStr)(params.body); bodyParams = extractAndReplaceTemplateValues(strBody, ignoreMissingParams); if (bodyParams) { params.body = bodyParams.newParam; extractedValues = { ...extractedValues, ...bodyParams.extractedValues }; hiddenBodyParts = bodyParams.hiddenParts; } } const geoParams = extractAndReplaceTemplateValues(params.geoLocation); if (geoParams) { params.geoLocation = geoParams.newParam; extractedValues = { ...extractedValues, ...geoParams.extractedValues }; } if (params.responseRedactions) { for (const r of params.responseRedactions) { if (r.regex) { const regexParams = extractAndReplaceTemplateValues(r.regex); r.regex = regexParams === null || regexParams === void 0 ? void 0 : regexParams.newParam; } if (r.xPath) { const xpathParams = extractAndReplaceTemplateValues(r.xPath); r.xPath = xpathParams === null || xpathParams === void 0 ? void 0 : xpathParams.newParam; } if (r.jsonPath) { const jsonPathParams = extractAndReplaceTemplateValues(r.jsonPath); r.jsonPath = jsonPathParams === null || jsonPathParams === void 0 ? void 0 : jsonPathParams.newParam; } } } if (params.responseMatches) { for (const r of params.responseMatches) { if (r.value !== '') { const matchParam = extractAndReplaceTemplateValues(r.value); r.value = matchParam === null || matchParam === void 0 ? void 0 : matchParam.newParam; extractedValues = { ...extractedValues, ...matchParam === null || matchParam === void 0 ? void 0 : matchParam.extractedValues }; } } } return { newParams: params, extractedValues: extractedValues, hiddenBodyParts: hiddenBodyParts, hiddenURLParts: hiddenURLParts }; function extractAndReplaceTemplateValues(param, ignoreMissingParams) { if (!param) { return null; } //const paramNames: Set<string> = new Set() const extractedValues = {}; const hiddenParts = []; let totalOffset = 0; param = param.replace(paramsRegex, (match, pn, offset) => { if (params.paramValues && pn in params.paramValues) { extractedValues[pn] = params.paramValues[pn]; totalOffset += params.paramValues[pn].length - match.length; return params.paramValues[pn]; } else if (secretParams) { if ((secretParams === null || secretParams === void 0 ? void 0 : secretParams.paramValues) && pn in (secretParams === null || secretParams === void 0 ? void 0 : secretParams.paramValues)) { hiddenParts.push({ index: offset + totalOffset, length: secretParams.paramValues[pn].length, }); totalOffset += secretParams.paramValues[pn].length - match.length; return secretParams.paramValues[pn]; } else { throw new Error(`parameter's "${pn}" value not found in paramValues and secret parameter's paramValues`); } } else { if (!(!!ignoreMissingParams)) { throw new Error(`parameter's "${pn}" value not found in paramValues`); } else { return match; } } }); return { newParam: param, extractedValues: extractedValues, hiddenParts: hiddenParts }; } } function getGeoLocation(v2Params, secretParams) { if (v2Params === null || v2Params === void 0 ? void 0 : v2Params.geoLocation) { const paramNames = new Set(); let geo = v2Params.geoLocation; //extract param names let match = null; while (match = paramsRegex.exec(geo)) { paramNames.add(match[1]); } for (const pn of paramNames) { if (v2Params.paramValues && pn in v2Params.paramValues) { geo = geo === null || geo === void 0 ? void 0 : geo.replaceAll(`{{${pn}}}`, v2Params.paramValues[pn].toString()); } else if ((secretParams === null || secretParams === void 0 ? void 0 : secretParams.paramValues) && pn in secretParams.paramValues) { geo = geo === null || geo === void 0 ? void 0 : geo.replaceAll(`{{${pn}}}`, secretParams.paramValues[pn].toString()); } else { throw new Error(`parameter "${pn}" value not found in templateParams`); } } const geoRegex = /^[A-Za-z]{2}$/sgiu; if (!geoRegex.test(geo)) { throw new Error(`Geolocation ${geo} is invalid`); } return geo; } return undefined; } function getURL(v2Params, secretParams) { let hostPort = v2Params === null || v2Params === void 0 ? void 0 : v2Params.url; const paramNames = new Set(); //extract param names let match = null; while (match = paramsRegex.exec(hostPort)) { paramNames.add(match[1]); } for (const pn of paramNames) { if (v2Params.paramValues && pn in v2Params.paramValues) { hostPort = hostPort === null || hostPort === void 0 ? void 0 : hostPort.replaceAll(`{{${pn}}}`, v2Params.paramValues[pn].toString()); } else if ((secretParams === null || secretParams === void 0 ? void 0 : secretParams.paramValues) && pn in secretParams.paramValues) { hostPort = hostPort === null || hostPort === void 0 ? void 0 : hostPort.replaceAll(`{{${pn}}}`, secretParams.paramValues[pn].toString()); } else { throw new Error(`parameter "${pn}" value not found in templateParams`); } } return hostPort; } exports.default = HTTP_PROVIDER; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvcHJvdmlkZXJzL2h0dHAvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7QUFBQSw4Q0FBeUg7QUFDekgsbUNBQThCO0FBQzlCLDRDQUF5QztBQUN6Qyx1Q0FBbUU7QUFDbkUsdUNBQStDO0FBQy9DLG9EQVFpQztBQUVqQyxxQ0FNa0I7QUFFbEIsTUFBTSxjQUFjLEdBQUcsY0FBYyxDQUFBO0FBQ3JDLE1BQU0sZUFBZSxHQUFHLHdLQUF3SyxDQUFBO0FBQ2hNLE1BQU0sUUFBUSxHQUFHLElBQUksR0FBRyxFQUFFLEdBQUcsRUFBRSxDQUFBLENBQUMsMEJBQTBCO0FBTTFELE1BQU0sYUFBYSxHQUFxQjtJQUN2QyxRQUFRLEVBQUUsV0FBVztJQUNyQixrQkFBa0IsQ0FBQyxNQUFNO1FBQ3hCLE9BQU8sQ0FBQyxvQkFBb0IsSUFBSSxNQUFNLENBQUM7WUFDdEMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxrQkFBa0I7WUFDM0IsQ0FBQyxDQUFDLFNBQVMsQ0FBQTtJQUNiLENBQUM7SUFDRCxXQUFXLENBQUMsTUFBTSxFQUFFLFlBQVk7UUFDL0IsT0FBTyxDQUFDLGFBQWEsSUFBSSxNQUFNLENBQUM7WUFDL0IsQ0FBQyxDQUFDLGNBQWMsQ0FBQyxNQUFNLEVBQUUsWUFBWSxDQUFDO1lBQ3RDLENBQUMsQ0FBQyxTQUFTLENBQUE7SUFDYixDQUFDO0lBQ0QsdUJBQXVCLENBQUMsTUFBTTtRQUM3QixJQUFJLGNBQWMsR0FBeUI7WUFDMUMseUJBQXlCLEVBQUcsQ0FBQyxVQUFVLENBQUM7U0FDeEMsQ0FBQTtRQUNELElBQUcseUJBQXlCLElBQUksTUFBTSxFQUFFLENBQUM7WUFDeEMsY0FBYyxHQUFHO2dCQUNoQixHQUFHLGNBQWM7Z0JBQ2pCLEdBQUcsTUFBTSxDQUFDLHVCQUF1QjthQUNqQyxDQUFBO1FBQ0YsQ0FBQztRQUVELE9BQU8sY0FBYyxDQUFBO0lBQ3RCLENBQUM7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