diff --git a/README.md b/README.md index 8545f6f4..3fe88566 100644 --- a/README.md +++ b/README.md @@ -176,6 +176,15 @@ Ensure the faucet command was succesful.
+#### Run tests for network1 network + +```bash +# codegen for network1 network +TARGET_NETWORK=Network1 npx ts-node codegen/main.ts && npm run prettier +# run tests for network1 network, assumes network1 rpc already running locally +npx hardhat test --network localNetwork1 +``` + ### Adding new operators Operators can be defined as data inside `codegen/common.ts` file and code automatically generates solidity overloads. diff --git a/codegen/common.ts b/codegen/common.ts index 629c5833..1f22affc 100644 --- a/codegen/common.ts +++ b/codegen/common.ts @@ -1,5 +1,10 @@ import { strict as assert } from 'node:assert'; +export enum Network { + Evmos, + Network1, +} + export type Operator = { name: string; // express left scalar operation as different operation with arguments swapped @@ -18,9 +23,8 @@ export type Operator = { unarySolidityOperator?: string; }; -export type Precompile = { - name: string; - code: number; +export type CodegenContext = { + libFheAddress: string; }; export enum OperatorArguments { @@ -35,37 +39,6 @@ export enum ReturnType { export const SUPPORTED_BITS: number[] = [8, 16, 32]; -export const ALL_PRECOMPILES: Precompile[] = [ - { name: 'Add', code: 65 }, - { name: 'Verify', code: 66 }, - { name: 'Reencrypt', code: 67 }, - { name: 'FhePubKey', code: 68 }, - { name: 'LessThanOrEqual', code: 70 }, - { name: 'Subtract', code: 71 }, - { name: 'Multiply', code: 72 }, - { name: 'LessThan', code: 73 }, - { name: 'Rand', code: 74 }, - { name: 'OptimisticRequire', code: 75 }, - { name: 'Cast', code: 76 }, - { name: 'TrivialEncrypt', code: 77 }, - { name: 'BitwiseAnd', code: 78 }, - { name: 'BitwiseOr', code: 79 }, - { name: 'BitwiseXor', code: 80 }, - { name: 'Equal', code: 81 }, - { name: 'GreaterThanOrEqual', code: 82 }, - { name: 'GreaterThan', code: 83 }, - { name: 'ShiftLeft', code: 84 }, - { name: 'ShiftRight', code: 85 }, - { name: 'NotEqual', code: 86 }, - { name: 'Min', code: 87 }, - { name: 'Max', code: 88 }, - { name: 'Negate', code: 89 }, - { name: 'Not', code: 90 }, - { name: 'Decrypt', code: 91 }, - { name: 'Divide', code: 92 }, - { name: 'Rem', code: 94 }, -]; - export const ALL_OPERATORS: Operator[] = [ { name: 'add', @@ -263,3 +236,16 @@ export function checks(operators: Operator[]): Operator[] { return operators; } + +export function networkCodegenContext(network: Network): CodegenContext { + switch (network) { + case Network.Evmos: + return { + libFheAddress: '0x000000000000000000000000000000000000005d', + }; + case Network.Network1: + return { + libFheAddress: '0x010000000000000000000000000000000000005D', + }; + } +} diff --git a/codegen/main.ts b/codegen/main.ts index 5d944201..4c688547 100644 --- a/codegen/main.ts +++ b/codegen/main.ts @@ -1,21 +1,29 @@ import { mkdirSync, writeFileSync } from 'fs'; -import { ALL_OPERATORS, ALL_PRECOMPILES, SUPPORTED_BITS, checks } from './common'; +import { ALL_OPERATORS, Network, SUPPORTED_BITS, checks, networkCodegenContext } from './common'; import * as t from './templates'; import * as testgen from './testgen'; function generateAllFiles() { const operators = checks(ALL_OPERATORS); - const [tfheSolSource, overloads] = t.tfheSol(operators, SUPPORTED_BITS); + const network = Network[(process.env.TARGET_NETWORK as keyof typeof Network) || 'Evmos']; + const context = networkCodegenContext(network); + const [tfheSolSource, overloads] = t.tfheSol(context, operators, SUPPORTED_BITS); const ovShards = testgen.splitOverloadsToShards(overloads); - writeFileSync('lib/Impl.sol', t.implSol(operators)); + writeFileSync('lib/Impl.sol', t.implSol(context, operators)); writeFileSync('lib/TFHE.sol', tfheSolSource); mkdirSync('examples/tests', { recursive: true }); ovShards.forEach((os) => { writeFileSync(`examples/tests/TFHETestSuite${os.shardNumber}.sol`, testgen.generateSmartContract(os)); }); writeFileSync('test/tfheOperations/tfheOperations.ts', testgen.generateTestCode(ovShards)); + writeFileSync( + 'test/generated.ts', + ` + export const FHE_LIB_ADDRESS = '${context.libFheAddress}'; + `, + ); } generateAllFiles(); diff --git a/codegen/templates.ts b/codegen/templates.ts index 317cf57d..3563e4f8 100644 --- a/codegen/templates.ts +++ b/codegen/templates.ts @@ -1,6 +1,6 @@ import { assert } from 'console'; -import { Operator, OperatorArguments, Precompile, ReturnType } from './common'; +import { CodegenContext, Operator, OperatorArguments, ReturnType } from './common'; import { ArgumentType, OverloadSignature } from './testgen'; export function commonSolLib(): string { @@ -42,7 +42,7 @@ function binaryOperatorImpl(op: Operator, isScalar: boolean, isEncrypted: boolea ); } -export function implSol(operators: Operator[]): string { +export function implSol(ctx: CodegenContext, operators: Operator[]): string { const res: string[] = []; const fheLibInterface = generateImplFhevmLibInterface(operators); @@ -54,7 +54,7 @@ pragma solidity 0.8.19; ${fheLibInterface} -address constant EXT_TFHE_LIBRARY = address(93); +address constant EXT_TFHE_LIBRARY = address(${ctx.libFheAddress}); library Impl { // 32 bytes for the 'byte' type header + 48 bytes for the NaCl anonymous @@ -77,7 +77,7 @@ library Impl { } }); - res.push(implCustomMethods()); + res.push(implCustomMethods(ctx)); res.push('}\n'); @@ -136,7 +136,11 @@ function fheLibCustomInterfaceFunctions(): string { `; } -export function tfheSol(operators: Operator[], supportedBits: number[]): [string, OverloadSignature[]] { +export function tfheSol( + ctx: CodegenContext, + operators: Operator[], + supportedBits: number[], +): [string, OverloadSignature[]] { const signatures: OverloadSignature[] = []; const res: string[] = []; @@ -182,7 +186,7 @@ library TFHE { supportedBits.forEach((bits) => res.push(tfheUnaryOperators(bits, operators, signatures))); supportedBits.forEach((bits) => res.push(tfheCustomUnaryOperators(bits, signatures))); - res.push(tfheCustomMethods()); + res.push(tfheCustomMethods(ctx)); res.push('}\n'); @@ -523,26 +527,6 @@ function tfheCustomUnaryOperators(bits: number, signatures: OverloadSignature[]) `; } -export function precompiles(precompiles: Precompile[]): string { - const res: string[] = []; - - res.push(`// SPDX-License-Identifier: BSD-3-Clause-Clear - -pragma solidity 0.8.19; - -library Precompiles { -`); - - precompiles.forEach((op) => { - res.push(` uint256 public constant ${op.name} = ${op.code}; -`); - }); - - res.push('}\n'); - - return res.join(''); -} - function unaryOperatorImpl(op: Operator): string { let fname = operatorFheLibFunction(op); return ` @@ -552,7 +536,7 @@ function unaryOperatorImpl(op: Operator): string { `; } -function tfheCustomMethods(): string { +function tfheCustomMethods(ctx: CodegenContext): string { return ` // Optimistically require that 'b' is true. // @@ -625,7 +609,7 @@ function tfheCustomMethods(): string { `; } -function implCustomMethods(): string { +function implCustomMethods(ctx: CodegenContext): string { return ` // If 'control's value is 'true', the result has the same value as 'ifTrue'. // If 'control's value is 'false', the result has the same value as 'ifFalse'. diff --git a/docs/client/examples.md b/docs/client/examples.md index 9cfc50db..f5cecae6 100644 --- a/docs/client/examples.md +++ b/docs/client/examples.md @@ -26,9 +26,14 @@ const getInstance = async () => { const chainId = +network.chainId.toString(); // Need to be a number // Get blockchain public key - const publicKey = await provider.call({ - to: "0x0000000000000000000000000000000000000044", + const ret = await provider.call({ + // fhe lib address, may need to be changed depending on network + to: "0x000000000000000000000000000000000000005d", + // first four bytes of keccak256('fhePubKey(bytes1)') + 1 byte for library + data: "0xd9d47bb001", }); + const decoded = ethers.AbiCoder.defaultAbiCoder().decode(["bytes"], ret); + const publicKey = decoded[0]; // Create instance _instance = createInstance({ chainId, publicKey }); @@ -74,9 +79,14 @@ const getInstance = async () => { const chainId = +network.chainId.toString(); // Get blockchain public key - const publicKey = await provider.call({ - to: "0x0000000000000000000000000000000000000044", + const ret = await provider.call({ + // fhe lib address, may need to be changed depending on network + to: "0x000000000000000000000000000000000000005d", + // first four bytes of keccak256('fhePubKey(bytes1)') + 1 byte for library + data: "0xd9d47bb001", }); + const decoded = ethers.AbiCoder.defaultAbiCoder().decode(["bytes"], ret); + const publicKey = decoded[0]; // Create instance _instance = createInstance({ chainId, publicKey }); diff --git a/docs/client/examples/getbalance.md b/docs/client/examples/getbalance.md index c331a488..3cf6f2c0 100644 --- a/docs/client/examples/getbalance.md +++ b/docs/client/examples/getbalance.md @@ -23,9 +23,14 @@ const getInstance = async () => { const chainId = +network.chainId.toString(); // Get blockchain public key - const publicKey = await provider.call({ - to: "0x0000000000000000000000000000000000000044", + const ret = await provider.call({ + // fhe lib address, may need to be changed depending on network + to: "0x000000000000000000000000000000000000005d", + // first four bytes of keccak256('fhePubKey(bytes1)') + 1 byte for library + data: "0xd9d47bb001", }); + const decoded = ethers.AbiCoder.defaultAbiCoder().decode(["bytes"], ret); + const publicKey = decoded[0]; // Create instance _instance = createInstance({ chainId, publicKey }); diff --git a/docs/client/examples/transfererc20.md b/docs/client/examples/transfererc20.md index 3ac55aee..b01baf07 100644 --- a/docs/client/examples/transfererc20.md +++ b/docs/client/examples/transfererc20.md @@ -22,9 +22,14 @@ const getInstance = async () => { const chainId = +network.chainId.toString(); // Need to be a number // Get blockchain public key - const publicKey = await provider.call({ - to: "0x0000000000000000000000000000000000000044", + const ret = await provider.call({ + // fhe lib address, may need to be changed depending on network + to: "0x000000000000000000000000000000000000005d", + // first four bytes of keccak256('fhePubKey(bytes1)') + 1 byte for library + data: "0xd9d47bb001", }); + const decoded = ethers.AbiCoder.defaultAbiCoder().decode(["bytes"], ret); + const publicKey = decoded[0]; // Create instance _instance = createInstance({ chainId, publicKey }); diff --git a/docs/client/getting_started/browser.md b/docs/client/getting_started/browser.md index fb30c7cb..a13e4069 100644 --- a/docs/client/getting_started/browser.md +++ b/docs/client/getting_started/browser.md @@ -10,10 +10,14 @@ const createFhevmInstance = async () => { const provider = new BrowserProvider(window.ethereum); const network = await provider.getNetwork(); const chainId = +network.chainId.toString(); - const publicKey = await provider.call({ - from: null, - to: "0x0000000000000000000000000000000000000044", + const ret = await provider.call({ + // fhe lib address, may need to be changed depending on network + to: "0x000000000000000000000000000000000000005d", + // first four bytes of keccak256('fhePubKey(bytes1)') + 1 byte for library + data: "0xd9d47bb001", }); + const decoded = ethers.AbiCoder.defaultAbiCoder().decode(["bytes"], ret); + const publicKey = decoded[0]; return createInstance({ chainId, publicKey }); }; diff --git a/docs/client/instance.md b/docs/client/instance.md index 78a9cbe6..4c72cc8a 100644 --- a/docs/client/instance.md +++ b/docs/client/instance.md @@ -39,10 +39,14 @@ const initInstance = async () => { const chainId = parseInt(chainIdHex, 16); // Get blockchain public key - const publicKey = await window.ethereum.request({ - method: "eth_call", - params: [{ from: null, to: "0x0000000000000000000000000000000000000044" }], + const ret = await provider.call({ + // fhe lib address, may need to be changed depending on network + to: "0x000000000000000000000000000000000000005d", + // first four bytes of keccak256('fhePubKey(bytes1)') + 1 byte for library + data: "0xd9d47bb001", }); + const decoded = ethers.AbiCoder.defaultAbiCoder().decode(["bytes"], ret); + const publicKey = decoded[0]; // Create instance return createInstance({ chainId, publicKey, keypairs }); diff --git a/hardhat.config.ts b/hardhat.config.ts index 0e844033..b3d4de63 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -22,6 +22,7 @@ if (!mnemonic) { const chainIds = { zama: 8009, local: 9000, + localNetwork1: 9000, }; function getChainConfig(chain: keyof typeof chainIds): NetworkUserConfig { @@ -30,6 +31,9 @@ function getChainConfig(chain: keyof typeof chainIds): NetworkUserConfig { case 'local': jsonRpcUrl = 'http://localhost:8545'; break; + case 'localNetwork1': + jsonRpcUrl = 'http://127.0.0.1:9650/ext/bc/fhevm/rpc'; + break; case 'zama': jsonRpcUrl = 'https://devnet.zama.ai'; break; @@ -63,6 +67,7 @@ const config: HardhatUserConfig = { zama: getChainConfig('zama'), localDev: getChainConfig('local'), local: getChainConfig('local'), + localNetwork1: getChainConfig('localNetwork1'), }, paths: { artifacts: './artifacts', diff --git a/lib/Impl.sol b/lib/Impl.sol index b65a8033..9ac2d19f 100644 --- a/lib/Impl.sol +++ b/lib/Impl.sol @@ -58,7 +58,7 @@ interface FhevmLib { function fheRand(bytes1 inp) external view returns (uint256 result); } -address constant EXT_TFHE_LIBRARY = address(93); +address constant EXT_TFHE_LIBRARY = address(0x000000000000000000000000000000000000005d); library Impl { // 32 bytes for the 'byte' type header + 48 bytes for the NaCl anonymous diff --git a/test/generated.ts b/test/generated.ts new file mode 100644 index 00000000..b56991b4 --- /dev/null +++ b/test/generated.ts @@ -0,0 +1 @@ +export const FHE_LIB_ADDRESS = '0x000000000000000000000000000000000000005d'; diff --git a/test/instance.ts b/test/instance.ts index 77a8c1f1..a2670835 100644 --- a/test/instance.ts +++ b/test/instance.ts @@ -2,6 +2,7 @@ import { Signer } from 'ethers'; import fhevmjs, { FhevmInstance } from 'fhevmjs'; import { ethers as hethers } from 'hardhat'; +import { FHE_LIB_ADDRESS } from './generated'; import type { Signers } from './signers'; import { FhevmInstances } from './types'; @@ -21,9 +22,13 @@ export const createInstances = async ( chainId = +network.chainId.toString(); // Need to be a number // Get blockchain public key - publicKey = await provider.call({ - to: '0x0000000000000000000000000000000000000044', + const ret = await provider.call({ + to: FHE_LIB_ADDRESS, + // first four bytes of keccak256('fhePubKey(bytes1)') + 1 byte for library + data: '0xd9d47bb001', }); + const decoded = ethers.AbiCoder.defaultAbiCoder().decode(['bytes'], ret); + publicKey = decoded[0]; } // Create instance