diff --git a/package-lock.json b/package-lock.json index e7347d0..bf27ca6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "fhevmjs", - "version": "0.2.2", + "version": "0.3.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "fhevmjs", - "version": "0.2.2", + "version": "0.3.0", "license": "BSD-3-Clause-Clear", "dependencies": { "commander": "^11.0.0", @@ -14,9 +14,9 @@ "ethers": "^6.6.4", "libsodium": "^0.7.11", "libsodium-wrappers": "^0.7.11", - "node-tfhe": "^0.3.1", + "node-tfhe": "^0.4.1", "sha3": "^2.1.4", - "tfhe": "^0.3.1" + "tfhe": "^0.4.1" }, "bin": { "fhevm": "bin/fhevm.js" @@ -4519,9 +4519,9 @@ "dev": true }, "node_modules/node-tfhe": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/node-tfhe/-/node-tfhe-0.3.1.tgz", - "integrity": "sha512-re5tBgPMwoSWl6b/pL13MWKnsBUv9nldAXoWkokG1aiUm9P0aOjJoz0J9+zOwV/dB0i9xN/TI//mnqF3uWAOIg==" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/node-tfhe/-/node-tfhe-0.4.1.tgz", + "integrity": "sha512-GysZYDRnvL8UyywaK/+/Rvqvpdk5pIRR8MaCqEJmaG3rGo/8OXwPzb6WJaAyYNZz2VmimCGBxznkuNVRviFHMQ==" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -5467,9 +5467,9 @@ } }, "node_modules/tfhe": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/tfhe/-/tfhe-0.3.1.tgz", - "integrity": "sha512-PqnsUVChETviwd5ebfJoj9yueUxIKPaSsfSGVmgvpUsUqAlianAx+4UH9hEmjDxgshSRDgm1Fp0/rRgCx2BYog==" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/tfhe/-/tfhe-0.4.1.tgz", + "integrity": "sha512-Ok8CrrryBjtgCbPliDG3YuAFX0oJLS4y9pCajF6TSORyKYIDVtfOKbVntG2gZFOeUukUTHSI5FVofqhX5GkPTw==" }, "node_modules/tmpl": { "version": "1.0.5", @@ -9462,9 +9462,9 @@ "dev": true }, "node-tfhe": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/node-tfhe/-/node-tfhe-0.3.1.tgz", - "integrity": "sha512-re5tBgPMwoSWl6b/pL13MWKnsBUv9nldAXoWkokG1aiUm9P0aOjJoz0J9+zOwV/dB0i9xN/TI//mnqF3uWAOIg==" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/node-tfhe/-/node-tfhe-0.4.1.tgz", + "integrity": "sha512-GysZYDRnvL8UyywaK/+/Rvqvpdk5pIRR8MaCqEJmaG3rGo/8OXwPzb6WJaAyYNZz2VmimCGBxznkuNVRviFHMQ==" }, "normalize-path": { "version": "3.0.0", @@ -10142,9 +10142,9 @@ } }, "tfhe": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/tfhe/-/tfhe-0.3.1.tgz", - "integrity": "sha512-PqnsUVChETviwd5ebfJoj9yueUxIKPaSsfSGVmgvpUsUqAlianAx+4UH9hEmjDxgshSRDgm1Fp0/rRgCx2BYog==" + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/tfhe/-/tfhe-0.4.1.tgz", + "integrity": "sha512-Ok8CrrryBjtgCbPliDG3YuAFX0oJLS4y9pCajF6TSORyKYIDVtfOKbVntG2gZFOeUukUTHSI5FVofqhX5GkPTw==" }, "tmpl": { "version": "1.0.5", diff --git a/package.json b/package.json index 5b90c24..396b083 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fhevmjs", - "version": "0.2.2", + "version": "0.3.0", "description": "fhEVM SDK for blockchain using TFHE", "main": "lib/node.cjs", "types": "lib/node.d.ts", @@ -44,9 +44,9 @@ "ethers": "^6.6.4", "libsodium": "^0.7.11", "libsodium-wrappers": "^0.7.11", - "node-tfhe": "^0.3.1", + "node-tfhe": "^0.4.1", "sha3": "^2.1.4", - "tfhe": "^0.3.1" + "tfhe": "^0.4.1" }, "devDependencies": { "@rollup/plugin-alias": "^5.0.0", diff --git a/src/sdk/index.test.ts b/src/sdk/index.test.ts index a435617..ece0909 100644 --- a/src/sdk/index.test.ts +++ b/src/sdk/index.test.ts @@ -19,10 +19,10 @@ describe('token', () => { expect(instance.encrypt8).toBeDefined(); expect(instance.encrypt16).toBeDefined(); expect(instance.encrypt32).toBeDefined(); - expect(instance.generateToken).toBeDefined(); + expect(instance.generatePublicKey).toBeDefined(); expect(instance.decrypt).toBeDefined(); expect(instance.serializeKeypairs).toBeDefined(); - expect(instance.getTokenSignature).toBeDefined(); + expect(instance.getPublicKey).toBeDefined(); expect(instance.hasKeypair).toBeDefined(); }); @@ -104,6 +104,22 @@ describe('token', () => { ).toThrow('Invalid contract address'); }); + it('controls generatePublicKey', async () => { + const instance = await createInstance({ + chainId: 1234, + publicKey: tfhePublicKey, + }); + expect(() => instance.generatePublicKey(undefined as any)).toThrow( + 'Missing contract address', + ); + expect(() => instance.generatePublicKey({ verifyingContract: '' })).toThrow( + 'Missing contract address', + ); + expect(() => + instance.generatePublicKey({ verifyingContract: '0x847473829d' }), + ).toThrow('Invalid contract address'); + }); + it('save generated token', async () => { const instance = await createInstance({ chainId: 1234, @@ -112,6 +128,26 @@ describe('token', () => { const contractAddress = '0x1c786b8ca49D932AFaDCEc00827352B503edf16c'; + const { eip712, publicKey } = instance.generatePublicKey({ + verifyingContract: contractAddress, + }); + + instance.setSignature(contractAddress, 'signnnn'); + + expect(instance.hasKeypair(contractAddress)).toBeTruthy(); + + const kp = instance.getPublicKey(contractAddress); + expect(kp!.publicKey).toBe(publicKey); + }); + + it('save generated token (deprecated)', async () => { + const instance = await createInstance({ + chainId: 1234, + publicKey: tfhePublicKey, + }); + + const contractAddress = '0x1c786b8ca49D932AFaDCEc00827352B503edf16c'; + const { token, publicKey } = instance.generateToken({ verifyingContract: contractAddress, }); @@ -132,12 +168,12 @@ describe('token', () => { const contractAddress = '0x1c786b8ca49D932AFaDCEc00827352B503edf16c'; - const { token, publicKey } = instance.generateToken({ + const { eip712, publicKey } = instance.generatePublicKey({ verifyingContract: contractAddress, }); const keypairs = instance.serializeKeypairs(); expect(keypairs[contractAddress]).toBeUndefined(); - const keypair = instance.getTokenSignature(contractAddress); + const keypair = instance.getPublicKey(contractAddress); expect(keypair).toBeNull(); expect(instance.hasKeypair(contractAddress)).toBeFalsy(); }); @@ -150,13 +186,13 @@ describe('token', () => { const contractAddress = '0x1c786b8ca49D932AFaDCEc00827352B503edf16c'; - const { token, publicKey } = instance.generateToken({ + const { eip712, publicKey } = instance.generatePublicKey({ verifyingContract: contractAddress, }); instance.setTokenSignature(contractAddress, 'signnnn'); - const kp = instance.getTokenSignature(contractAddress); + const kp = instance.getPublicKey(contractAddress); expect(kp!.publicKey).toBe(publicKey); const value = 89290; diff --git a/src/sdk/index.ts b/src/sdk/index.ts index 7966555..aabf6ef 100644 --- a/src/sdk/index.ts +++ b/src/sdk/index.ts @@ -1,7 +1,11 @@ import { TfheCompactPublicKey } from 'node-tfhe'; import sodium from 'libsodium-wrappers'; import { encrypt8, encrypt16, encrypt32 } from './encrypt'; -import { EIP712, generateToken } from './token'; +import { + EIP712, + GeneratePublicKeyParams, + generatePublicKey, +} from './publicKey'; import { decrypt } from './decrypt'; import { fromHexString, isAddress, toHexString } from '../utils'; import { ContractKeypairs } from './types'; @@ -10,19 +14,30 @@ export type FhevmInstance = { encrypt8: (value: number) => Uint8Array; encrypt16: (value: number) => Uint8Array; encrypt32: (value: number) => Uint8Array; - generateToken: (options: { - verifyingContract: string; - name?: string; - version?: string; - force?: boolean; - }) => { + generateToken: ( + options: GeneratePublicKeyParams & { + force?: boolean; + }, + ) => { publicKey: Uint8Array; token: EIP712; }; + generatePublicKey: ( + options: GeneratePublicKeyParams & { + force?: boolean; + }, + ) => { + publicKey: Uint8Array; + eip712: EIP712; + }; setTokenSignature: (contractAddress: string, signature: string) => void; getTokenSignature: ( contractAddress: string, ) => { publicKey: Uint8Array; signature: string } | null; + setSignature: (contractAddress: string, signature: string) => void; + getPublicKey: ( + contractAddress: string, + ) => { publicKey: Uint8Array; signature: string } | null; hasKeypair: (contractAddress: string) => boolean; decrypt: (contractAddress: string, ciphertext: string) => number; serializeKeypairs: () => ExportedContractKeypairs; @@ -105,8 +120,38 @@ export const createInstance = async ( return encrypt32(value, tfheCompactPublicKey); }, - // Reencryption + /** + * @deprecated Since version 0.3.0. Will be deleted in version 0.4.0. Use generatePublicKey instead. + */ generateToken(options) { + console.warn( + 'generateToken is deprecated. Use generatePublicKey instead', + ); + if (!options || !options.verifyingContract) + throw new Error('Missing contract address'); + if (!isAddress(options.verifyingContract)) + throw new Error('Invalid contract address'); + let kp; + if (!options.force && contractKeypairs[options.verifyingContract]) { + kp = contractKeypairs[options.verifyingContract]; + } + const { eip712, keypair } = generatePublicKey({ + verifyingContract: options.verifyingContract, + name: options.name, + version: options.version, + chainId, + keypair: kp, + }); + contractKeypairs[options.verifyingContract] = { + privateKey: keypair.privateKey, + publicKey: keypair.publicKey, + signature: null, + }; + return { token: eip712, publicKey: keypair.publicKey }; + }, + + // Reencryption + generatePublicKey(options) { if (!options || !options.verifyingContract) throw new Error('Missing contract address'); if (!isAddress(options.verifyingContract)) @@ -115,7 +160,7 @@ export const createInstance = async ( if (!options.force && contractKeypairs[options.verifyingContract]) { kp = contractKeypairs[options.verifyingContract]; } - const { token, keypair } = generateToken({ + const { eip712, keypair } = generatePublicKey({ verifyingContract: options.verifyingContract, name: options.name, version: options.version, @@ -127,10 +172,16 @@ export const createInstance = async ( publicKey: keypair.publicKey, signature: null, }; - return { token, publicKey: keypair.publicKey }; + return { eip712, publicKey: keypair.publicKey }; }, + /** + * @deprecated Since version 0.3.0. Will be deleted in version 0.4.0. Use generatePublicKey instead. + */ setTokenSignature(contractAddress: string, signature: string) { + console.warn( + 'setTokenSignature is deprecated. Use generatePublicKey instead', + ); if ( contractKeypairs[contractAddress] && contractKeypairs[contractAddress].privateKey @@ -139,7 +190,32 @@ export const createInstance = async ( } }, + setSignature(contractAddress: string, signature: string) { + if ( + contractKeypairs[contractAddress] && + contractKeypairs[contractAddress].privateKey + ) { + contractKeypairs[contractAddress].signature = signature; + } + }, + + /** + * @deprecated Since version 0.3.0. Will be deleted in version 0.4.0. Use generatePublicKey instead. + */ getTokenSignature(contractAddress: string): TokenSignature | null { + console.warn( + 'setTokenSignature is deprecated. Use generatePublicKey instead', + ); + if (hasKeypair(contractAddress)) { + return { + publicKey: contractKeypairs[contractAddress].publicKey, + signature: contractKeypairs[contractAddress].signature!, + }; + } + return null; + }, + + getPublicKey(contractAddress: string): TokenSignature | null { if (hasKeypair(contractAddress)) { return { publicKey: contractKeypairs[contractAddress].publicKey, diff --git a/src/sdk/publicKey.test.ts b/src/sdk/publicKey.test.ts new file mode 100644 index 0000000..42599cf --- /dev/null +++ b/src/sdk/publicKey.test.ts @@ -0,0 +1,45 @@ +import sodium from 'libsodium-wrappers'; +import { toHexString } from '../utils'; +import { generatePublicKey } from './publicKey'; + +describe('token', () => { + beforeAll(async () => { + await sodium.ready; + }); + it('creates a valid EIP712 object', async () => { + const { eip712, keypair } = generatePublicKey({ + verifyingContract: '0xccc', + chainId: 1234, + name: 'hello', + version: '3', + }); + expect(eip712.domain.chainId).toBe(1234); + expect(eip712.domain.name).toBe('hello'); + expect(eip712.domain.version).toBe('3'); + expect(eip712.message.publicKey).toBe( + `0x${toHexString(keypair.publicKey)}`, + ); + + expect(eip712.primaryType).toBe('Reencrypt'); + expect(eip712.types.Reencrypt.length).toBe(1); + expect(eip712.types.Reencrypt[0].name).toBe('publicKey'); + expect(eip712.types.Reencrypt[0].type).toBe('bytes32'); + }); + + it('creates a valid EIP712 object with default values', async () => { + const { eip712, keypair } = generatePublicKey({ + verifyingContract: '0xccc', + }); + expect(eip712.domain.chainId).toBe(9000); + expect(eip712.domain.name).toBe('Reencryption'); + expect(eip712.domain.version).toBe('1'); + expect(eip712.message.publicKey).toBe( + `0x${toHexString(keypair.publicKey)}`, + ); + + expect(eip712.primaryType).toBe('Reencrypt'); + expect(eip712.types.Reencrypt.length).toBe(1); + expect(eip712.types.Reencrypt[0].name).toBe('publicKey'); + expect(eip712.types.Reencrypt[0].type).toBe('bytes32'); + }); +}); diff --git a/src/sdk/token.ts b/src/sdk/publicKey.ts similarity index 90% rename from src/sdk/token.ts rename to src/sdk/publicKey.ts index c30809e..d11b2af 100644 --- a/src/sdk/token.ts +++ b/src/sdk/publicKey.ts @@ -20,7 +20,7 @@ export type EIP712 = { }; }; -export type GenerateTokenParams = { +export type GeneratePublicKeyParams = { name?: string; version?: string; chainId?: number; @@ -30,10 +30,12 @@ export type GenerateTokenParams = { export type FhevmToken = { keypair: ContractKeypair; - token: EIP712; + eip712: EIP712; }; -export const generateToken = (params: GenerateTokenParams): FhevmToken => { +export const generatePublicKey = ( + params: GeneratePublicKeyParams, +): FhevmToken => { const keypair = params.keypair || sodium.crypto_box_keypair(); const msgParams: EIP712 = { types: { @@ -54,7 +56,7 @@ export const generateToken = (params: GenerateTokenParams): FhevmToken => { primaryType: 'Reencrypt', domain: { // Give a user-friendly name to the specific contract you're signing for. - name: params.name || 'Authorization token', + name: params.name || 'Reencryption', // This identifies the latest version. version: params.version || '1', // This defines the network, in this case, Mainnet. @@ -71,6 +73,6 @@ export const generateToken = (params: GenerateTokenParams): FhevmToken => { publicKey: keypair.publicKey, privateKey: keypair.privateKey, }, - token: msgParams, + eip712: msgParams, }; }; diff --git a/src/sdk/token.test.ts b/src/sdk/token.test.ts deleted file mode 100644 index b6d1115..0000000 --- a/src/sdk/token.test.ts +++ /dev/null @@ -1,41 +0,0 @@ -import sodium from 'libsodium-wrappers'; -import { toHexString } from '../utils'; -import { generateToken } from './token'; - -describe('token', () => { - beforeAll(async () => { - await sodium.ready; - }); - it('creates a valid EIP712 object', async () => { - const { token, keypair } = generateToken({ - verifyingContract: '0xccc', - chainId: 1234, - name: 'hello', - version: '3', - }); - expect(token.domain.chainId).toBe(1234); - expect(token.domain.name).toBe('hello'); - expect(token.domain.version).toBe('3'); - expect(token.message.publicKey).toBe(`0x${toHexString(keypair.publicKey)}`); - - expect(token.primaryType).toBe('Reencrypt'); - expect(token.types.Reencrypt.length).toBe(1); - expect(token.types.Reencrypt[0].name).toBe('publicKey'); - expect(token.types.Reencrypt[0].type).toBe('bytes32'); - }); - - it('creates a valid EIP712 object with default values', async () => { - const { token, keypair } = generateToken({ - verifyingContract: '0xccc', - }); - expect(token.domain.chainId).toBe(9000); - expect(token.domain.name).toBe('Authorization token'); - expect(token.domain.version).toBe('1'); - expect(token.message.publicKey).toBe(`0x${toHexString(keypair.publicKey)}`); - - expect(token.primaryType).toBe('Reencrypt'); - expect(token.types.Reencrypt.length).toBe(1); - expect(token.types.Reencrypt[0].name).toBe('publicKey'); - expect(token.types.Reencrypt[0].type).toBe('bytes32'); - }); -});