From 6cedcc57fc9d0194b8192a29dab8d26ed3f780f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20=27birdy=27=20Danjou?= Date: Tue, 20 Feb 2024 00:24:30 +0100 Subject: [PATCH 1/7] feat: add encrypt4 method --- src/sdk/encrypt.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/sdk/encrypt.ts b/src/sdk/encrypt.ts index 3d3b594..39082ed 100644 --- a/src/sdk/encrypt.ts +++ b/src/sdk/encrypt.ts @@ -1,11 +1,24 @@ import { TfheCompactPublicKey, + CompactFheUint4List, CompactFheUint8List, CompactFheUint16List, CompactFheUint32List, CompactFheUint64List, } from 'node-tfhe'; +export const encrypt4 = ( + value: number, + publicKey: TfheCompactPublicKey, +): Uint8Array => { + const uint8Array = new Uint8Array([value]); + const encrypted = CompactFheUint4List.encrypt_with_compact_public_key( + uint8Array, + publicKey, + ); + return encrypted.serialize(); +}; + export const encrypt8 = ( value: number, publicKey: TfheCompactPublicKey, From 09aace2fb4aafc48de1140c6d3973795d1a49982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20=27birdy=27=20Danjou?= Date: Tue, 20 Feb 2024 00:24:57 +0100 Subject: [PATCH 2/7] fix: allow bigint as parameter for encrypt64 --- src/sdk/encrypt.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sdk/encrypt.ts b/src/sdk/encrypt.ts index 39082ed..7018f06 100644 --- a/src/sdk/encrypt.ts +++ b/src/sdk/encrypt.ts @@ -56,7 +56,7 @@ export const encrypt32 = ( }; export const encrypt64 = ( - value: number, + value: number | bigint, publicKey: TfheCompactPublicKey, ): Uint8Array => { const uint64Array = new BigUint64Array([BigInt(value)]); From 3eb50d3062d7b567094d1ff0091967ffa2dff867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20=27birdy=27=20Danjou?= Date: Tue, 20 Feb 2024 00:25:10 +0100 Subject: [PATCH 3/7] 0.4.0-4 --- package-lock.json | 4 ++-- package.json | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/package-lock.json b/package-lock.json index 367958f..60f7f7b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "fhevmjs", - "version": "0.4.0-3", + "version": "0.4.0-4", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "fhevmjs", - "version": "0.4.0-3", + "version": "0.4.0-4", "license": "BSD-3-Clause-Clear", "dependencies": { "bigint-buffer": "^1.1.5", diff --git a/package.json b/package.json index 9757f4c..81cdddc 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fhevmjs", - "version": "0.4.0-3", + "version": "0.4.0-4", "description": "fhEVM SDK for blockchain using TFHE", "main": "lib/node.cjs", "types": "lib/node.d.ts", From 57cf92f682400ba92a3f1bdb460c9e1844126e4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20=27birdy=27=20Danjou?= Date: Tue, 20 Feb 2024 00:42:41 +0100 Subject: [PATCH 4/7] feat: add 64 bits support in CLI --- bin/fhevm.js | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/bin/fhevm.js b/bin/fhevm.js index 570fdd7..c203960 100755 --- a/bin/fhevm.js +++ b/bin/fhevm.js @@ -6,6 +6,8 @@ import { program } from 'commander'; import { toHexString, prependHttps, throwError } from './utils.js'; import { createInstance } from '../lib/node.cjs'; +const allowedBits = [8, 16, 32, 64]; + const FHE_LIB_ADDRESS = '0x000000000000000000000000000000000000005d'; let _instance; @@ -20,23 +22,29 @@ const getInstance = async (provider) => { throwError('Network is unreachable'); } const chainId = +network.chainId.toString(); - const ret = await provider.call({ - to: FHE_LIB_ADDRESS, - // first four bytes of keccak256('fhePubKey(bytes1)') + 1 byte for library - data: '0xd9d47bb001', - }); - const decoded = AbiCoder.defaultAbiCoder().decode(['bytes'], ret); - const publicKey = decoded[0]; - _instance = await createInstance({ chainId, publicKey }); - + try { + const ret = await provider.call({ + to: FHE_LIB_ADDRESS, + // first four bytes of keccak256('fhePubKey(bytes1)') + 1 byte for library + data: '0xd9d47bb001', + }); + const decoded = AbiCoder.defaultAbiCoder().decode(['bytes'], ret); + const publicKey = decoded[0]; + _instance = await createInstance({ chainId, publicKey }); + } catch (e) { + return throwError( + "This network doesn't seem to use fhEVM or use an incompatible version.", + ); + } return _instance; }; program .command('encrypt') - .argument('', 'number of bits (8, 16, 32)') + .argument('', 'number of bits (4, 8, 16, 32, 64)') .argument('', 'integer to encrypt') .action(async (bits, value, options) => { + if (!allowedBits.includes(+bits)) throwError('Invalid number of bits'); const host = prependHttps(options.node); const provider = new JsonRpcProvider(host); const instance = await getInstance(provider); From 43a1b3fffa6ab251f7b130943966b7db09d8776a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20=27birdy=27=20Danjou?= Date: Tue, 20 Feb 2024 00:43:54 +0100 Subject: [PATCH 5/7] feat: add test and export encrypt4 --- src/sdk/encrypt.test.ts | 24 +++++++++++++++++++++++- src/sdk/index.ts | 12 +++++++++++- 2 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/sdk/encrypt.test.ts b/src/sdk/encrypt.test.ts index 8463b87..b1e0feb 100644 --- a/src/sdk/encrypt.test.ts +++ b/src/sdk/encrypt.test.ts @@ -1,8 +1,10 @@ import { + FheUint4, FheUint8, FheUint16, FheUint32, FheUint64, + CompactFheUint4List, CompactFheUint8List, CompactFheUint16List, CompactFheUint32List, @@ -11,7 +13,7 @@ import { TfheClientKey, } from 'node-tfhe'; import { createTfheKeypair } from '../tfhe'; -import { encrypt8, encrypt16, encrypt32, encrypt64 } from './encrypt'; +import { encrypt4, encrypt8, encrypt16, encrypt32, encrypt64 } from './encrypt'; describe('encrypt8', () => { let clientKey: TfheClientKey; @@ -23,6 +25,26 @@ describe('encrypt8', () => { publicKey = keypair.publicKey; }); + it('encrypt/decrypt 0 4bits', async () => { + const buffer = encrypt4(0, publicKey); + const compactList = CompactFheUint4List.deserialize(buffer); + let encryptedList = compactList.expand(); + encryptedList.forEach((v: FheUint4) => { + const decrypted = v.decrypt(clientKey); + expect(decrypted).toBe(0); + }); + }); + + it('encrypt/decrypt 4bits', async () => { + const buffer = encrypt4(7, publicKey); + const compactList = CompactFheUint4List.deserialize(buffer); + let encryptedList = compactList.expand(); + encryptedList.forEach((v: FheUint4) => { + const decrypted = v.decrypt(clientKey); + expect(decrypted).toBe(7); + }); + }); + it('encrypt/decrypt 0 8bits', async () => { const buffer = encrypt8(0, publicKey); const compactList = CompactFheUint8List.deserialize(buffer); diff --git a/src/sdk/index.ts b/src/sdk/index.ts index 1d165ac..7a76d8b 100644 --- a/src/sdk/index.ts +++ b/src/sdk/index.ts @@ -1,6 +1,6 @@ import { TfheCompactPublicKey } from 'node-tfhe'; import sodium from 'libsodium-wrappers'; -import { encrypt8, encrypt16, encrypt32, encrypt64 } from './encrypt'; +import { encrypt4, encrypt8, encrypt16, encrypt32, encrypt64 } from './encrypt'; import { EIP712, GeneratePublicKeyParams, @@ -11,6 +11,7 @@ import { fromHexString, isAddress, toHexString } from '../utils'; import { ContractKeypairs } from './types'; export type FhevmInstance = { + encrypt4: (value: number) => Uint8Array; encrypt8: (value: number) => Uint8Array; encrypt16: (value: number) => Uint8Array; encrypt32: (value: number) => Uint8Array; @@ -107,6 +108,15 @@ export const createInstance = async ( return { // Parameters + encrypt4(value) { + if (value == null) throw new Error('Missing value'); + if (typeof value !== 'number') throw new Error('Value must be a number'); + if (!tfheCompactPublicKey) + throw new Error( + 'Your instance has been created without the public blockchain key', + ); + return encrypt4(value, tfheCompactPublicKey); + }, encrypt8(value) { if (value == null) throw new Error('Missing value'); if (typeof value !== 'number') throw new Error('Value must be a number'); From 56b9ec329745f1d95fff08dae6d027c7ce1d99d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20=27birdy=27=20Danjou?= Date: Tue, 20 Feb 2024 00:44:36 +0100 Subject: [PATCH 6/7] fix: add bigint parameter on encrypt64 --- src/sdk/index.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sdk/index.ts b/src/sdk/index.ts index 7a76d8b..5059b49 100644 --- a/src/sdk/index.ts +++ b/src/sdk/index.ts @@ -15,7 +15,7 @@ export type FhevmInstance = { encrypt8: (value: number) => Uint8Array; encrypt16: (value: number) => Uint8Array; encrypt32: (value: number) => Uint8Array; - encrypt64: (value: number) => Uint8Array; + encrypt64: (value: number | bigint) => Uint8Array; generateToken: ( options: GeneratePublicKeyParams & { force?: boolean; @@ -148,7 +148,8 @@ export const createInstance = async ( encrypt64(value) { if (value == null) throw new Error('Missing value'); - if (typeof value !== 'number') throw new Error('Value must be a number'); + if (typeof value !== 'number' && typeof value !== 'bigint') + throw new Error('Value must be a number or a bigint'); if (!tfheCompactPublicKey) throw new Error( 'Your instance has been created without the public blockchain key', From e5625a72a01be7b20b55dede1e8fa77e9166affe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20=27birdy=27=20Danjou?= Date: Tue, 20 Feb 2024 00:59:50 +0100 Subject: [PATCH 7/7] fix: enhance tests --- src/sdk/encrypt.test.ts | 22 ++++++++++++- src/sdk/index.test.ts | 55 +++++++++++-------------------- src/sdk/index.ts | 73 ----------------------------------------- 3 files changed, 40 insertions(+), 110 deletions(-) diff --git a/src/sdk/encrypt.test.ts b/src/sdk/encrypt.test.ts index b1e0feb..fb5aa22 100644 --- a/src/sdk/encrypt.test.ts +++ b/src/sdk/encrypt.test.ts @@ -105,7 +105,17 @@ describe('encrypt8', () => { }); }); - it('encrypt/decrypt 32bits', async () => { + it('encrypt/decrypt 0 64bits', async () => { + const buffer = encrypt64(0, publicKey); + const compactList = CompactFheUint64List.deserialize(buffer); + let encryptedList = compactList.expand(); + encryptedList.forEach((v: FheUint64) => { + const decrypted = v.decrypt(clientKey); + expect(decrypted.toString()).toBe('0'); + }); + }); + + it('encrypt/decrypt 64bits', async () => { const buffer = encrypt64(3021094839202949, publicKey); const compactList = CompactFheUint64List.deserialize(buffer); let encryptedList = compactList.expand(); @@ -114,4 +124,14 @@ describe('encrypt8', () => { expect(decrypted.toString()).toBe('3021094839202949'); }); }); + + it('encrypt/decrypt 64bits', async () => { + const buffer = encrypt64(BigInt('18446744073709551615'), publicKey); + const compactList = CompactFheUint64List.deserialize(buffer); + let encryptedList = compactList.expand(); + encryptedList.forEach((v: FheUint64) => { + const decrypted = v.decrypt(clientKey); + expect(decrypted.toString()).toBe('18446744073709551615'); + }); + }); }); diff --git a/src/sdk/index.test.ts b/src/sdk/index.test.ts index 17d57bc..30a2526 100644 --- a/src/sdk/index.test.ts +++ b/src/sdk/index.test.ts @@ -3,7 +3,7 @@ import { createInstance } from './index'; import { createTfhePublicKey } from '../tfhe'; import { fromHexString, toHexString, numberToBytes } from '../utils'; -describe('token', () => { +describe('index', () => { let tfhePublicKey: string; beforeAll(async () => { @@ -100,10 +100,13 @@ describe('token', () => { chainId: 1234, publicKey: tfhePublicKey, }); + expect(instance.encrypt64(BigInt(34))).toBeTruthy(); + expect(() => instance.encrypt4(undefined as any)).toThrow('Missing value'); expect(() => instance.encrypt8(undefined as any)).toThrow('Missing value'); expect(() => instance.encrypt16(undefined as any)).toThrow('Missing value'); expect(() => instance.encrypt32(undefined as any)).toThrow('Missing value'); + expect(() => instance.encrypt64(undefined as any)).toThrow('Missing value'); expect(() => instance.encrypt8('wrong value' as any)).toThrow( 'Value must be a number', @@ -114,22 +117,22 @@ describe('token', () => { expect(() => instance.encrypt32('wrong value' as any)).toThrow( 'Value must be a number', ); - }); + expect(() => instance.encrypt64('wrong value' as any)).toThrow( + 'Value must be a number', + ); - it('controls generateToken', async () => { - const instance = await createInstance({ - chainId: 1234, - publicKey: tfhePublicKey, - }); - expect(() => instance.generateToken(undefined as any)).toThrow( - 'Missing contract address', + expect(() => instance.encrypt4(BigInt(34) as any)).toThrow( + 'Value must be a number', ); - expect(() => instance.generateToken({ verifyingContract: '' })).toThrow( - 'Missing contract address', + expect(() => instance.encrypt8(BigInt(34) as any)).toThrow( + 'Value must be a number', + ); + expect(() => instance.encrypt16(BigInt(34) as any)).toThrow( + 'Value must be a number', + ); + expect(() => instance.encrypt32(BigInt(34) as any)).toThrow( + 'Value must be a number', ); - expect(() => - instance.generateToken({ verifyingContract: '0x847473829d' }), - ).toThrow('Invalid contract address'); }); it('controls generatePublicKey', async () => { @@ -148,7 +151,7 @@ describe('token', () => { ).toThrow('Invalid contract address'); }); - it('save generated token', async () => { + it('save generated public key', async () => { const instance = await createInstance({ chainId: 1234, publicKey: tfhePublicKey, @@ -168,26 +171,6 @@ describe('token', () => { 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, - }); - - instance.setTokenSignature(contractAddress, 'signnnn'); - - expect(instance.hasKeypair(contractAddress)).toBeTruthy(); - - const kp = instance.getTokenSignature(contractAddress); - expect(kp!.publicKey).toBe(publicKey); - }); - it("don't export keys without signature", async () => { const instance = await createInstance({ chainId: 1234, @@ -218,7 +201,7 @@ describe('token', () => { verifyingContract: contractAddress, }); - instance.setTokenSignature(contractAddress, 'signnnn'); + instance.setSignature(contractAddress, 'signnnn'); const kp = instance.getPublicKey(contractAddress); expect(kp!.publicKey).toBe(publicKey); diff --git a/src/sdk/index.ts b/src/sdk/index.ts index 5059b49..7afd1fc 100644 --- a/src/sdk/index.ts +++ b/src/sdk/index.ts @@ -16,14 +16,6 @@ export type FhevmInstance = { encrypt16: (value: number) => Uint8Array; encrypt32: (value: number) => Uint8Array; encrypt64: (value: number | bigint) => Uint8Array; - generateToken: ( - options: GeneratePublicKeyParams & { - force?: boolean; - }, - ) => { - publicKey: Uint8Array; - token: EIP712; - }; generatePublicKey: ( options: GeneratePublicKeyParams & { force?: boolean; @@ -32,10 +24,6 @@ export type FhevmInstance = { 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, @@ -157,36 +145,6 @@ export const createInstance = async ( return encrypt64(value, tfheCompactPublicKey); }, - /** - * @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) @@ -212,21 +170,6 @@ export const createInstance = async ( 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 - ) { - contractKeypairs[contractAddress].signature = signature; - } - }, - setSignature(contractAddress: string, signature: string) { if ( contractKeypairs[contractAddress] && @@ -236,22 +179,6 @@ export const createInstance = async ( } }, - /** - * @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 {