diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml new file mode 100644 index 0000000..399db8e --- /dev/null +++ b/.github/workflows/publish.yml @@ -0,0 +1,18 @@ +on: + release: + types: [created] + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: 18.x + - run: npm ci + - run: npm test + - run: npm run build + - uses: JS-DevTools/npm-publish@0f451a94170d1699fd50710966d48fb26194d939 + with: + token: ${{ secrets.NPM_TOKEN }} diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..d6ec135 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,22 @@ +name: Node.js CI + +on: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [18.x] + steps: + - uses: actions/checkout@v3 + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + - run: npm ci + - run: npm test + - run: npm run build + - uses: ArtiomTr/jest-coverage-report-action@v2 diff --git a/README.md b/README.md index 1806d65..b1e746d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# fhEVM SDK +# fhevmjs This is a library to interact with a blockchain using fhEVM. diff --git a/jest.config.js b/jest.config.js index a0f256d..4fb72c4 100644 --- a/jest.config.js +++ b/jest.config.js @@ -22,4 +22,11 @@ module.exports = { coverageReporters: ['lcov', 'text-summary', 'json'], transformIgnorePatterns: ['/node_modules/'], coveragePathIgnorePatterns: [], + coverageThreshold: { + global: { + branches: 80, + functions: 80, + lines: 80, + }, + }, }; diff --git a/package.json b/package.json index 8cfa141..ad8ddc9 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fhevmjs", - "version": "0.0.2", + "version": "0.0.1", "description": "fhEVM SDK for blockchain using TFHE", "main": "lib/node.js", "browser": "lib/web.js", diff --git a/src/sdk/encrypt.ts b/src/sdk/encrypt.ts index f5f6847..5110282 100644 --- a/src/sdk/encrypt.ts +++ b/src/sdk/encrypt.ts @@ -13,7 +13,6 @@ export const encrypt16 = (value: number, publicKey: TfheCompactPublicKey): Uint8 }; export const encrypt32 = (value: number, publicKey: TfheCompactPublicKey): Uint8Array => { - // const encrypted = FheUint32.encrypt_with_compact_public_key(value, publicKey); const uint32Array = new Uint32Array([value]); const encrypted = CompactFheUint32List.encrypt_with_compact_public_key(uint32Array, publicKey); return encrypted.serialize(); diff --git a/src/sdk/index.test.ts b/src/sdk/index.test.ts index 5829afb..36c84eb 100644 --- a/src/sdk/index.test.ts +++ b/src/sdk/index.test.ts @@ -30,7 +30,7 @@ describe('token', () => { const contractAddress = '0x1c786b8ca49D932AFaDCEc00827352B503edf16c'; - const instance = await createInstance({ + const instance = createInstance({ chainId: 1234, publicKey: TFHEkeypair.publicKey, keypairs: { @@ -56,14 +56,28 @@ describe('token', () => { publicKey: TFHEkeypair.publicKey, }); - expect(() => instance.encrypt8(undefined as any)).toThrow(); - expect(() => instance.encrypt16(undefined as any)).toThrow(); - expect(() => instance.encrypt32(undefined as any)).toThrow(); + 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.encrypt8('wrong value' as any)).toThrow('Value must be a number'); + expect(() => instance.encrypt16('wrong value' as any)).toThrow('Value must be a number'); + expect(() => instance.encrypt32('wrong value' as any)).toThrow('Value must be a number'); + }); + + it('controls generateToken', async () => { + const TFHEkeypair = createTFHEKey(); + const instance = createInstance({ + chainId: 1234, + publicKey: TFHEkeypair.publicKey, + }); + await expect(instance.generateToken(undefined as any)).rejects.toThrow('Missing contract address'); + await expect(instance.generateToken({ verifyingContract: '' })).rejects.toThrow('Missing contract address'); }); - it('save generated tokens', async () => { + it('save generated token', async () => { const TFHEkeypair = createTFHEKey(); - const instance = await createInstance({ + const instance = createInstance({ chainId: 1234, publicKey: TFHEkeypair.publicKey, }); @@ -72,20 +86,47 @@ describe('token', () => { const { token, publicKey } = await 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 TFHEkeypair = createTFHEKey(); + const instance = createInstance({ + chainId: 1234, + publicKey: TFHEkeypair.publicKey, + }); + + const contractAddress = '0x1c786b8ca49D932AFaDCEc00827352B503edf16c'; + + const { token, publicKey } = await instance.generateToken({ verifyingContract: contractAddress }); const keypairs = instance.serializeKeypairs(); expect(keypairs[contractAddress]).toBeUndefined(); const keypair = instance.getTokenSignature(contractAddress); expect(keypair).toBeNull(); + expect(instance.hasKeypair(contractAddress)).toBeFalsy(); + }); + + it('decrypts data', async () => { + const TFHEkeypair = createTFHEKey(); + const instance = createInstance({ + chainId: 1234, + publicKey: TFHEkeypair.publicKey, + }); + + const contractAddress = '0x1c786b8ca49D932AFaDCEc00827352B503edf16c'; + + const { token, publicKey } = await instance.generateToken({ verifyingContract: contractAddress }); instance.setTokenSignature(contractAddress, 'signnnn'); + const kp = instance.getTokenSignature(contractAddress); expect(kp!.publicKey).toBe(publicKey); - const keypairs2 = instance.serializeKeypairs(); - expect(keypairs2[contractAddress].publicKey).toBe(toHexString(publicKey)); - const keypair2 = instance.getTokenSignature(contractAddress); - expect(keypair2?.publicKey).toBe(publicKey); - const value = 8238290348; const ciphertext = sodium.crypto_box_seal(numberToBytes(value), publicKey, 'hex'); const cleartext = await instance.decrypt(contractAddress, ciphertext); diff --git a/src/sdk/index.ts b/src/sdk/index.ts index 41f94ec..4931b60 100644 --- a/src/sdk/index.ts +++ b/src/sdk/index.ts @@ -60,22 +60,24 @@ export const createInstance = (params: FhevmInstanceParams): FhevmInstance => { // Parameters encrypt8(value) { if (!value) throw new Error('Missing value'); + if (typeof value !== 'number') throw new Error('Value must be a number'); return encrypt8(value, publicKey); }, encrypt16(value) { if (!value) throw new Error('Missing value'); + if (typeof value !== 'number') throw new Error('Value must be a number'); return encrypt16(value, publicKey); }, encrypt32(value) { if (!value) throw new Error('Missing value'); - + if (typeof value !== 'number') throw new Error('Value must be a number'); return encrypt32(value, publicKey); }, // Reencryption async generateToken(options) { - if (!options.verifyingContract) throw new Error('Missing contract address'); + 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]) { diff --git a/src/web.ts b/src/web.ts index a646136..a70232d 100644 --- a/src/web.ts +++ b/src/web.ts @@ -1,3 +1,2 @@ export * from './sdk'; -export * from './tfhe'; export * from './init';