diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 39b701b54..4491a2cbb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -137,6 +137,8 @@ jobs: working-directory: ${{ github.workspace }}/barge run: | bash -x start_ocean.sh --with-typesense 2>&1 > start-node.log & + env: + NODE_VERSION: pr-1207 - name: Install deps & build run: npm ci && npm run build:metadata diff --git a/package-lock.json b/package-lock.json index 8a5c8fd3b..08e1ff0ae 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,6 +19,7 @@ "cross-fetch": "^4.0.0", "crypto-js": "^4.1.1", "decimal.js": "^10.4.1", + "eciesjs": "^0.4.5", "ethers": "^6.15.0", "form-data": "^2.3.3", "jsonwebtoken": "^9.0.2" @@ -3800,12 +3801,20 @@ "dev": true, "license": "ISC" }, + "node_modules/@noble/ciphers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.3.0.tgz", + "integrity": "sha512-ldbrnOjmNRwFdXcTM6uXDcxpMIFrbzAWNnpBPp4oTJTFF0XByGD6vf45WrehZGXRQTRVV+Zm8YP+EgEf+e4cWA==", + "license": "MIT", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, "node_modules/@noble/curves": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", "license": "MIT", - "peer": true, "dependencies": { "@noble/hashes": "1.4.0" }, @@ -3818,7 +3827,6 @@ "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 16" }, @@ -9068,6 +9076,20 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/eciesjs": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.5.tgz", + "integrity": "sha512-2zSRIygO48LpdS95Rwt9ryIkJNO37IdbkjRsnYyAn7gx7e4WPBNimnk6jGNdx2QQYr/VJRPnSVdwQpO5bycYZw==", + "license": "MIT", + "dependencies": { + "@noble/ciphers": "^0.3.0", + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.2" + }, + "engines": { + "node": ">=16.0.0" + } + }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", @@ -24564,11 +24586,15 @@ "integrity": "sha512-/kSXhY692qiV1MXu6EeOZvg5nECLclxNXcKCxJ3cXQgYuRymRHpdx/t7JXfsK+JLjwA1e1c1/SBrlQYpusC29Q==", "dev": true }, + "@noble/ciphers": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@noble/ciphers/-/ciphers-0.3.0.tgz", + "integrity": "sha512-ldbrnOjmNRwFdXcTM6uXDcxpMIFrbzAWNnpBPp4oTJTFF0XByGD6vf45WrehZGXRQTRVV+Zm8YP+EgEf+e4cWA==" + }, "@noble/curves": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.4.2.tgz", "integrity": "sha512-TavHr8qycMChk8UwMld0ZDRvatedkzWfH8IiaeGCfymOP5i0hSCozz9vHOL0nkwk7HRMlFnAiKpS2jrUmSybcw==", - "peer": true, "requires": { "@noble/hashes": "1.4.0" }, @@ -24576,8 +24602,7 @@ "@noble/hashes": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.4.0.tgz", - "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==", - "peer": true + "integrity": "sha512-V1JJ1WTRUqHHrOSh597hURcMqVKVGL/ea3kv0gSnEdsEZ0/+VyPghM1lMNGc00z7CIQorSvbKpuJkxvuHbvdbg==" } } }, @@ -28268,6 +28293,16 @@ "safe-buffer": "^5.0.1" } }, + "eciesjs": { + "version": "0.4.5", + "resolved": "https://registry.npmjs.org/eciesjs/-/eciesjs-0.4.5.tgz", + "integrity": "sha512-2zSRIygO48LpdS95Rwt9ryIkJNO37IdbkjRsnYyAn7gx7e4WPBNimnk6jGNdx2QQYr/VJRPnSVdwQpO5bycYZw==", + "requires": { + "@noble/ciphers": "^0.3.0", + "@noble/curves": "^1.2.0", + "@noble/hashes": "^1.3.2" + } + }, "ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", diff --git a/package.json b/package.json index 79292a2d0..ade3c14a1 100644 --- a/package.json +++ b/package.json @@ -66,6 +66,7 @@ "cross-fetch": "^4.0.0", "crypto-js": "^4.1.1", "decimal.js": "^10.4.1", + "eciesjs": "^0.4.5", "ethers": "^6.15.0", "form-data": "^2.3.3", "jsonwebtoken": "^9.0.2" diff --git a/src/@types/Compute.ts b/src/@types/Compute.ts index 25fe2ae07..fd1b2c5b2 100644 --- a/src/@types/Compute.ts +++ b/src/@types/Compute.ts @@ -199,3 +199,9 @@ export interface ValidationResponse { isValid: boolean message: string } + +export interface dockerRegistryAuth { + username?: string + password?: string + auth?: string +} diff --git a/src/@types/Provider.ts b/src/@types/Provider.ts index 3fbb99635..7ee1e385b 100644 --- a/src/@types/Provider.ts +++ b/src/@types/Provider.ts @@ -45,3 +45,43 @@ export interface ServiceEndpoint { export interface UserCustomParameters { [key: string]: any } + +export const PROTOCOL_COMMANDS = { + DOWNLOAD: 'download', + ENCRYPT: 'encrypt', + ENCRYPT_FILE: 'encryptFile', + DECRYPT_DDO: 'decryptDDO', + GET_DDO: 'getDDO', + QUERY: 'query', + NONCE: 'nonce', + STATUS: 'status', + DETAILED_STATUS: 'detailedStatus', + FIND_DDO: 'findDDO', + GET_FEES: 'getFees', + FILE_INFO: 'fileInfo', + VALIDATE_DDO: 'validateDDO', + COMPUTE_GET_ENVIRONMENTS: 'getComputeEnvironments', + COMPUTE_START: 'startCompute', + FREE_COMPUTE_START: 'freeStartCompute', + COMPUTE_STOP: 'stopCompute', + COMPUTE_GET_STATUS: 'getComputeStatus', + COMPUTE_GET_STREAMABLE_LOGS: 'getComputeStreamableLogs', + COMPUTE_GET_RESULT: 'getComputeResult', + COMPUTE_INITIALIZE: 'initializeCompute', + STOP_NODE: 'stopNode', + REINDEX_TX: 'reindexTx', + REINDEX_CHAIN: 'reindexChain', + HANDLE_INDEXING_THREAD: 'handleIndexingThread', + COLLECT_FEES: 'collectFees', + POLICY_SERVER_PASSTHROUGH: 'PolicyServerPassthrough', + GET_P2P_PEER: 'getP2PPeer', + GET_P2P_PEERS: 'getP2PPeers', + GET_P2P_NETWORK_STATS: 'getP2PNetworkStats', + FIND_PEER: 'findPeer', + CREATE_AUTH_TOKEN: 'createAuthToken', + INVALIDATE_AUTH_TOKEN: 'invalidateAuthToken', + FETCH_CONFIG: 'fetchConfig', + PUSH_CONFIG: 'pushConfig', + GET_LOGS: 'getLogs', + JOBS: 'jobs' +} diff --git a/src/services/Aquarius.ts b/src/services/Aquarius.ts index 0b00cc38b..573f9d66e 100644 --- a/src/services/Aquarius.ts +++ b/src/services/Aquarius.ts @@ -4,6 +4,7 @@ import { sleep } from '../utils/General.js' import { Signer } from 'ethers' import { signRequest } from '../utils/SignatureUtils.js' import { Asset, DDO, DDOManager, ValidateMetadata } from '@oceanprotocol/ddo-js' +import { PROTOCOL_COMMANDS } from '../@types/Provider.js' export interface SearchQuery { from?: number @@ -25,6 +26,12 @@ export class Aquarius { this.aquariusURL = aquariusURL } + // temp, untll we merge aquarius & provider + private getAuthorization(signerOrAuthToken: Signer | string): string | undefined { + const isAuthToken = typeof signerOrAuthToken === 'string' + return isAuthToken ? signerOrAuthToken : undefined + } + /** Resolves a DID * @param {string} did DID of the asset. * @param {AbortSignal} signal abort signal @@ -136,18 +143,19 @@ export class Aquarius { nonce = '0' } const nextNonce = (Number(nonce) + 1).toString() // have to increase the previous - // same signed message as usual (did + nonce) - // the node will only validate (add his signature if there fields are present and are valid) - // let signatureMessage = publisherAddress - const signatureMessage = publisherAddress + nextNonce - const signature = await signRequest(signer, signatureMessage) + const message = String( + String(await signer.getAddress()) + + String(nextNonce) + + String(PROTOCOL_COMMANDS.VALIDATE_DDO) + ) + const signature = await signRequest(signer, message) const data = { ddo, publisherAddress, nonce: nextNonce, signature } const response = await fetch(ddoValidateRoute, { method: 'POST', body: JSON.stringify(data), headers: { - 'Content-Type': 'application/octet-stream', - Authorization: authorization + 'Content-Type': 'application/json', + Authorization: this.getAuthorization(signer) }, signal }) @@ -155,8 +163,6 @@ export class Aquarius { if (response.status !== 200) { throw new Error('Metadata validation failed') } - console.log('Ddo successfully validated') - return { valid: true, hash: jsonResponse.hash, diff --git a/src/services/Provider.ts b/src/services/Provider.ts index 35e662cef..0c5c8f95b 100644 --- a/src/services/Provider.ts +++ b/src/services/Provider.ts @@ -1,5 +1,5 @@ import fetch from 'cross-fetch' -import { ethers, JsonRpcSigner, Signer } from 'ethers' +import { Signer } from 'ethers' import { LoggerInstance } from '../utils/Logger.js' import { Arweave, @@ -19,9 +19,13 @@ import { ComputePayment, ComputeJobMetadata, PolicyServerInitializeCommand, - PolicyServerPassthroughCommand + PolicyServerPassthroughCommand, + dockerRegistryAuth } from '../@types' +import { PROTOCOL_COMMANDS } from '../@types/Provider.js' import { decodeJwt } from '../utils/Jwt.js' +import { eciesencrypt } from '../utils/eciesencrypt.js' +import { signRequest } from '../utils/SignatureUtils.js' export class Provider { private async getConsumerAddress(signerOrAuthToken: Signer | string): Promise { @@ -33,10 +37,15 @@ export class Provider { private async getSignature( signerOrAuthToken: Signer | string, - message: string + nonce: string, + command: string ): Promise { const isAuthToken = typeof signerOrAuthToken === 'string' - return isAuthToken ? null : await this.signProviderRequest(signerOrAuthToken, message) + if (isAuthToken) return null + const message = String( + String(await signerOrAuthToken.getAddress()) + String(nonce) + String(command) + ) + return signRequest(signerOrAuthToken, message) } private getAuthorization(signerOrAuthToken: Signer | string): string | undefined { @@ -59,6 +68,15 @@ export class Provider { } } + /** + * Returns the node public key + * @return {string} The node public key + */ + private async getNodePublicKey(providerUri: string): Promise { + const providerEndpoints = await this.getEndpoints(providerUri) + return providerEndpoints.nodePublicKey + } + /** * This function returns the endpoint URL for a given service name. * @param {ServiceEndpoint[]} servicesEndpoints - The array of service endpoints @@ -139,36 +157,6 @@ export class Provider { } } - /** - * Sign a provider request with a signer. - * @param {Signer} signer - The signer to use. - * @param {string} message - The message to sign. - * @returns {Promise} A promise that resolves with the signature. - */ - public async signProviderRequest(signer: Signer, message: string): Promise { - // const isMetaMask = web3 && web3.currentProvider && (web3.currentProvider as any).isMetaMask - // if (isMetaMask) return await web3.eth.personal.sign(consumerMessage, accountId, password) - // await web3.eth.sign(consumerMessage, await signer.getAddress()) - // const consumerMessage = ethers.keccak256(toUtf8Bytes(message)) - // const messageHashBytes = ethers.getBytes(consumerMessage) - const consumerMessage = ethers.solidityPackedKeccak256( - ['bytes'], - [ethers.hexlify(ethers.toUtf8Bytes(message))] - ) - const messageHashBytes = ethers.toBeArray(consumerMessage) - try { - return await signer.signMessage(messageHashBytes) - } catch (error) { - // LoggerInstance.error('Sign provider message error: ', error) - // Check if the user is using barge chain - const network = await signer.provider.getNetwork() - const chainId = Number(network.chainId) - if (chainId === 8996) { - return await (signer as JsonRpcSigner)._legacySignMessage(messageHashBytes) - } - } - } - /** * Encrypt data using the Provider's own symmetric key * @param {string} data data in json format that needs to be sent , it can either be a DDO or a File array @@ -200,10 +188,11 @@ export class Provider { serviceEndpoints )) + 1 ).toString() - - // same signed message as for start compute (consumer address + did[0] + nonce) - const signatureMessage = String(nonce) - const signature = await this.getSignature(signerOrAuthToken, signatureMessage) + const signature = await this.getSignature( + signerOrAuthToken, + nonce, + PROTOCOL_COMMANDS.ENCRYPT + ) let path = (this.getEndpointURL(serviceEndpoints, 'encrypt') @@ -539,6 +528,7 @@ export class Provider { * @param {ComputeResourceRequest[]} resources The resources to start compute job with. * @param {number} chainId The chain used to do payments * @param {any} policyServer Policy server data. + * @param {dockerRegistryAuth} dockerRegistryAuth Docker registry authentication data. * @param {AbortSignal} signal abort signal * @return {Promise} ProviderComputeInitialize data */ @@ -553,6 +543,7 @@ export class Provider { resources: ComputeResourceRequest[], chainId: number, policyServer?: any, + dockerRegistryAuth?: dockerRegistryAuth, signal?: AbortSignal ): Promise { const providerEndpoints = await this.getEndpoints(providerUri) @@ -579,10 +570,14 @@ export class Provider { ).toString() // same signed message as for start compute (consumer address + did[0] + nonce) - let signatureMessage = consumerAddress - signatureMessage += assets[0]?.documentId - signatureMessage += nonce - const signature = await this.getSignature(signerOrAuthToken, signatureMessage) + let signature + const isAuthToken = typeof signerOrAuthToken === 'string' + if (!isAuthToken) { + let signatureMessage = consumerAddress + signatureMessage += assets[0]?.documentId + signatureMessage += nonce + signature = await signRequest(signerOrAuthToken, signatureMessage) + } const providerData: Record = { datasets: assets, @@ -597,6 +592,15 @@ export class Provider { consumerAddress, signature } + if (dockerRegistryAuth) { + const nodeKey = await this.getNodePublicKey(providerUri) + if (nodeKey) { + providerData.dockerRegistryAuth = eciesencrypt( + nodeKey, + JSON.stringify(dockerRegistryAuth) + ) + } + } if (policyServer) providerData.policyServer = policyServer @@ -679,7 +683,11 @@ export class Provider { )) + 1 ).toString() - const signature = await this.getSignature(signerOrAuthToken, did + nonce) + const signature = await this.getSignature( + signerOrAuthToken, + nonce, + PROTOCOL_COMMANDS.DOWNLOAD + ) let consumeUrl = downloadUrl consumeUrl += `?fileIndex=${fileIndex}` consumeUrl += `&documentId=${did}` @@ -742,7 +750,7 @@ export class Provider { let signatureMessage = consumerAddress signatureMessage += dataset.documentId signatureMessage += nonce - const signature = await this.signProviderRequest(consumer, signatureMessage) + const signature = await signRequest(consumer, signatureMessage) const payload = Object() payload.consumerAddress = consumerAddress payload.signature = signature @@ -845,8 +853,11 @@ export class Provider { serviceEndpoints )) + 1 ).toString() - const signatureMessage = String(consumerAddress + datasets[0]?.documentId + nonce) - const signature = await this.getSignature(signerOrAuthToken, signatureMessage) + const signature = await this.getSignature( + signerOrAuthToken, + nonce, + PROTOCOL_COMMANDS.COMPUTE_START + ) const payload = Object() payload.consumerAddress = consumerAddress payload.signature = signature @@ -963,9 +974,11 @@ export class Provider { )) + 1 ).toString() - const signatureMessage = nonce // datasets[0].documentId - console.log('signatureMessage: ', signatureMessage) - const signature = await this.getSignature(signerOrAuthToken, signatureMessage) + const signature = await this.getSignature( + signerOrAuthToken, + nonce, + PROTOCOL_COMMANDS.FREE_COMPUTE_START + ) const payload = Object() payload.consumerAddress = consumerAddress payload.signature = signature @@ -1063,8 +1076,11 @@ export class Provider { let url = `?consumerAddress=${consumerAddress}&jobId=${jobId}` // Is signer, add signature and nonce if (!isAuthToken) { - const signatureMessage = `${consumerAddress}${jobId}${nonce}` - const signature = await this.getSignature(signerOrAuthToken, signatureMessage) + const signature = await this.getSignature( + signerOrAuthToken, + nonce, + PROTOCOL_COMMANDS.COMPUTE_GET_STREAMABLE_LOGS + ) url += `&signature=${signature}` url += `&nonce=${nonce}` } @@ -1158,14 +1174,11 @@ export class Provider { )) + 1 ).toString() - const signatureMessage = consumerAddress + (jobId || '') - // On current provider impl (and nodes) we DO NOT check this signature - // On nodes we are signing again just the Nonce to send the request to Operator Service - // on current provider we sign: {owner}{job_id}{nonce}" OR {owner}{nonce} if no jobId - // On provider service STOP route, we just check signature owner + jobId OR just owner if no jobId - // signatureMessage += (agreementId && `${this.noZeroX(agreementId)}`) || '' - // signatureMessage += nonce - const signature = await this.getSignature(signerOrAuthToken, signatureMessage) + const signature = await this.getSignature( + signerOrAuthToken, + nonce, + PROTOCOL_COMMANDS.COMPUTE_STOP + ) const queryParams = new URLSearchParams() queryParams.set('consumerAddress', consumerAddress) queryParams.set('nonce', nonce) @@ -1307,11 +1320,11 @@ export class Provider { serviceEndpoints )) + 1 ).toString() - let signatureMessage = consumerAddress - signatureMessage += jobId - signatureMessage += index.toString() - signatureMessage += nonce - const signature = await this.getSignature(signerOrAuthToken, signatureMessage) + const signature = await this.getSignature( + signerOrAuthToken, + nonce, + PROTOCOL_COMMANDS.COMPUTE_GET_RESULT + ) if (!computeResultUrl) return null let resultUrl = computeResultUrl resultUrl += `?consumerAddress=${consumerAddress}` @@ -1324,85 +1337,6 @@ export class Provider { return resultUrl } - /** Deletes a compute job. - * @param {string} did asset did - * @param {SignerOrAuthToken} signerOrAuthToken signer or auth token - * @param {string} jobId the compute job ID - * @param {string} providerUri The URI of the provider we want to query - * @param {AbortSignal} signal abort signal - * @return {Promise} - */ - public async computeDelete( - did: string, - signerOrAuthToken: Signer | string, - jobId: string, - providerUri: string, - signal?: AbortSignal - ): Promise { - const providerEndpoints = await this.getEndpoints(providerUri) - const serviceEndpoints = await this.getServiceEndpoints( - providerUri, - providerEndpoints - ) - const computeDeleteUrl = this.getEndpointURL(serviceEndpoints, 'computeDelete') - ? this.getEndpointURL(serviceEndpoints, 'computeDelete').urlPath - : null - - const consumerAddress = await this.getConsumerAddress(signerOrAuthToken) - const nonce = ( - (await this.getNonce( - providerUri, - consumerAddress, - signal, - providerEndpoints, - serviceEndpoints - )) + 1 - ).toString() - - let signatureMessage = consumerAddress - signatureMessage += jobId || '' - signatureMessage += (did && `${this.noZeroX(did)}`) || '' - signatureMessage += nonce - const signature = await this.getSignature(signerOrAuthToken, signatureMessage) - const payload = Object() - payload.documentId = did // this.noZeroX(did) #https://github.com/oceanprotocol/ocean.js/issues/1892 - payload.consumerAddress = consumerAddress - payload.jobId = jobId - if (signature) payload.signature = signature - - if (!computeDeleteUrl) return null - let response - try { - response = await fetch(computeDeleteUrl, { - method: 'DELETE', - body: JSON.stringify(payload), - headers: { - 'Content-Type': 'application/json', - Authorization: this.getAuthorization(signerOrAuthToken) - }, - signal - }) - } catch (e) { - LoggerInstance.error('Delete compute job failed:') - LoggerInstance.error(e) - LoggerInstance.error('Payload was:', payload) - throw new Error('HTTP request failed calling Provider') - } - if (response?.ok) { - const params = await response.json() - return params - } - const resolvedResponse = await response.json() - LoggerInstance.error( - 'Delete compute job failed:', - response.status, - response.statusText, - resolvedResponse - ) - LoggerInstance.error('Payload was:', payload) - throw new Error(JSON.stringify(resolvedResponse)) - } - /** Generates an auth token * @param {Signer} consumer consumer Signer wallet object * @param {string} providerUri The URI of the provider we want to query @@ -1431,8 +1365,11 @@ export class Provider { )) + 1 ).toString() - const signatureMessage = consumerAddress + nonce - const signature = await this.signProviderRequest(consumer, signatureMessage) + const signature = await this.getSignature( + consumer, + nonce, + PROTOCOL_COMMANDS.CREATE_AUTH_TOKEN + ) try { const response = await fetch(url, { @@ -1492,7 +1429,7 @@ export class Provider { ).toString() const signatureMessage = consumerAddress + nonce - const signature = await this.signProviderRequest(consumer, signatureMessage) + const signature = await signRequest(consumer, signatureMessage) try { const response = await fetch(url, { @@ -1695,10 +1632,18 @@ export class Provider { ) return null } + const consumerAddress = await signer.getAddress() + const nonce = ( + (await this.getNonce( + providerUri, + consumerAddress, + signal, + providerEndpoints, + serviceEndpoints + )) + 1 + ).toString() - const expiryTimestamp = Date.now() + 5 * 60 * 1000 - const signature = await signer.signMessage(expiryTimestamp.toString()) - + const signature = await this.getSignature(signer, nonce, PROTOCOL_COMMANDS.GET_LOGS) let url = logsUrl + `?startTime=${startTime}&endTime=${endTime}` if (maxLogs) url += `&maxLogs=${maxLogs}` if (moduleName) url += `&moduleName=${moduleName}` @@ -1711,7 +1656,8 @@ export class Provider { method: 'POST', body: JSON.stringify({ signature, - expiryTimestamp + nonce, + address: consumerAddress }), headers: { 'Content-Type': 'application/json' }, signal diff --git a/src/utils/SignatureUtils.ts b/src/utils/SignatureUtils.ts index e84bae241..790755bfb 100644 --- a/src/utils/SignatureUtils.ts +++ b/src/utils/SignatureUtils.ts @@ -1,4 +1,4 @@ -import { JsonRpcSigner, Signer, getBytes, keccak256, toUtf8Bytes } from 'ethers' +import { JsonRpcSigner, Signer, getBytes, ethers } from 'ethers' import { LoggerInstance } from './Logger' /** @@ -24,16 +24,17 @@ export async function signHash(signer: Signer, message: string) { } export async function signRequest(signer: Signer, message: string): Promise { - const consumerMessage = keccak256(toUtf8Bytes(message)) - const messageHashBytes = getBytes(consumerMessage) - if (!signer.provider) { - throw new Error('Provider is required but not available') - } - const { chainId } = await signer.provider.getNetwork() + const consumerMessage = ethers.solidityPackedKeccak256( + ['bytes'], + [ethers.hexlify(ethers.toUtf8Bytes(message))] + ) + const messageHashBytes = ethers.toBeArray(consumerMessage) try { return await signer.signMessage(messageHashBytes) } catch (error) { - LoggerInstance.error('Sign message error: ', error) + // LoggerInstance.error('Sign message error: ', error) + const network = await signer.provider.getNetwork() + const chainId = Number(network.chainId) if (Number(chainId) === 8996) { console.log('Signing message with _legacySignMessage') return await (signer as JsonRpcSigner)._legacySignMessage(messageHashBytes) diff --git a/src/utils/eciesencrypt.ts b/src/utils/eciesencrypt.ts new file mode 100644 index 000000000..b0b797521 --- /dev/null +++ b/src/utils/eciesencrypt.ts @@ -0,0 +1,18 @@ +import { encrypt, PublicKey } from 'eciesjs' + +/** + * @dev eciesencrypt + * Encrypt content using ECIES and return the encrypted content as a hex string + * + * @param publicKey public key string '0x...' + * @param content content to encrypt + */ +export function eciesencrypt(publicKey: string, content: string) { + // Encrypt using ECIES + const key = PublicKey.fromHex(publicKey) + const encrypted = encrypt(key.toHex(), Buffer.from(content)) + + // Convert to hex string + const encryptedHex = Buffer.from(encrypted).toString('hex') + return encryptedHex +} diff --git a/test/integration/Auth.test.ts b/test/integration/Auth.test.ts index a267a4516..0d3c9a97d 100644 --- a/test/integration/Auth.test.ts +++ b/test/integration/Auth.test.ts @@ -1,6 +1,6 @@ import { Signer } from 'ethers' import { getTestConfig, provider } from '../config' -import { ProviderInstance } from '../../src' +import { ProviderInstance, sleep } from '../../src' import { expect } from 'chai' describe('Auth token tests', async () => { @@ -18,11 +18,13 @@ describe('Auth token tests', async () => { }) it('should generate auth token', async () => { + await sleep(100) const token = await ProviderInstance.generateAuthToken(account, providerUrl) expect(token).to.be.a('string') }) it('should invalidate auth token', async () => { + await sleep(100) const token = await ProviderInstance.generateAuthToken(account, providerUrl) expect(token).to.be.a('string')