安装
要安装 EZKL 引擎, 只需使用您最喜欢的软件包管理器 :
# npm
npm install @ezkljs/engine
# yarn
yarn add @ezkljs/engine
# pnpm
pnpm add @ezkljs/engine
在浏览器/nodejs上下文中
- 遗传: 从给定输入生成一个证人.
- 椭圆形: 从随机种子中生成一个 El-Gamal 键帕.
- 椭圆: 使用 ElGamal 公钥和随机性加密任意消息.
- 椭圆: 使用 ElGamal 密钥解密密码文本
- 证明: 生成一个证明
- 校验: 验证给定的证据
- 马来多哈什: 使用 Poseidon 散列函数的给定消息 hash
以及一些帮助器功能,这些功能使得与引擎的工作更加容易:
- vecU64 向 Felt 移动: 将 4u64s 转换为字段元素( hex string)
- vecU64 输入: 将直接代表字段元素的 4u64s 转换为整数
- vecU64 移动: 将直接代表一个字段元素的 4 u64s 转换为( 从固定点缩放调整) 浮动点
- 浮点图VecU64: 将浮动点元素转换为代表固定点字段元素的 4u64s
- 缓冲器ToVecOfVecU64: 将4u64s的向量转换为缓冲
为了了解更多如何使用这些帮助方法, 看看我们为他们写的测试 这里
引擎方法在后端使用web组装支持在浏览器/JS上下文中运行生锈码. 从 WASM 绑定中传递和返回的非原始数据类型必须采用序列(缓冲)格式. 这些序列化和去序列化方法使得JS对象更容易被转换成一种可以被引擎方法接受的格式.
节点对 Web 目标
引擎有两个目标: nodejs 财务报告和审定财务报表 web编辑 nodejs 目标用于节点应用程序(即节点服务器侧数据处理)和 web 目标用于浏览器应用程序(即反应前端)。 从守规矩的角度看,唯一的
两者的区别在于:网络捆绑通过网络工人实例支持多条读取,而节点则不支持.
这个readme将专注于记录网页目标. 如果您有兴趣使用节点目标,请参见单位测试 我们为 他们。 。 。 .
如果您只想跳入查看一个显示如何使用所有核心引擎绑定的示例应用程序 此应用程序的代码 这里
十字起源隔离.
要在所有浏览器中使用“ 共享箭头缓冲器” 特性, 您需要确保全局性 crossOriginIsolated 设置为 true否则, ezkljs 引擎绑定可能行不通, 因为WebAssembly内存由主线程和网络工人共享 。 跟随 本指南 通过google来确保您的网络应用程序是交叉源代码隔离的. 在使用下个js构建的应用示例中,我们通过在下个应用中添加以下内容,实现了跨源隔离. 相会. js 文件 :
const nextConfig = {
async headers() {
return [
{
source: '/(.*)',
headers: [
{
key: 'Cross-Origin-Embedder-Policy',
value: 'require-corp',
},
{
key: 'Cross-Origin-Opener-Policy',
value: 'same-origin',
},
],
},
]
},
}
引擎调试
为了让引擎重回错误 我们建议调用 init_panic_hook 在调用其他任何引擎方法之前使用.
这将确保引擎出错以可读格式登录到控制台上,否则,如果出现出错,会以可读格式登录到控制台上 RuntimeError: unreachable
如果您还想要从引擎中打印调试数据, 您可以调用 init_logger 方法。 这将打印出所有调试数据到控制台 .
证明一个新的网络集合实例.
如果您正在使用 Web 捆绑, 您首先需要初始化一个 Web 组装模块, 然后再使用引擎曝光的任何方法 。 要导入默认导出 @ezkljs/engine/web/ezkl.js 然后唤作. 如果您想要超越默认的 WASM 内存大小, 您可以通过 Web Assembly 。 记忆对象作为第二个参数. 我们强烈建议这样做,如果您想要您的应用程序与移动浏览器兼容的话,因为默认内存分配对于iOS浏览器来说太大了. 从我们的实验中,我们发现iOS浏览器所能处理的最大内存分配是4096 mb(默认为65536 mb).
import init, { init_panic_hook, init_logger } from '@ezkljs/engine/web/ezkl.js'
export default function Home() {
useEffect(() => {
async function run() {
// Initialize the WASM module. Here we are overiding the default memory allocation with the recommend allocation for the best performance across all mobile browsers.
await init(
undefined,
new WebAssembly.Memory({ initial: 20, maximum: 4096, shared: true }),
)
// Initialize the panic hook and logger
init_panic_hook()
init_logger()
}
run()
})
}
生成证人
要从给定输入中生成一个证人,您可以使用 genWitness 方法。 你可以把目击证人看作是输入输出对,通过对输入进行量化,通过被量化的模型运行,并进行任意散开或加密生成的输入对. 这个目击文件包含了证明诸如"我在这个数据上运行了我的神经网络,它产生了这个输出"等陈述所需的输入和输出对. 链接到示例应用程序中在此使用的方法 : 手柄GenWitness 按钮
import { genWitness, deserialize } from '@ezkljs/engine/web'
// This is the code for the button that triggers the
// witness generation that we used in the example app.
export async function handleGenWitnessButton<T extends FileMapping>(
files: T,
): Promise<Uint8ArrayResult> {
const result = await convertFilesToFilesSer(files)
const start = performance.now() // Start the timer
let output = genWitness(result['compiled_model'], result['input'])
let witness = deserialize(output)
console.log(JSON.stringify(witness, null, 2))
const end = performance.now() // End the timer
return {
output: output,
executionTime: end - start,
}
}
输出 :
{
"inputs": [
[
[
"14385415396251402209",
"2429374486035521128",
"12558163205804149944",
"2583518171365219058"
],
[
"6425625360762666998",
"7924344314350639699",
"14762033076929465436",
"2023505479389396574"
],
[
"1949230679015292902",
"16913946402569752895",
"5177146667339417225",
"1571765431670520771"
]
]
],
"outputs": [
[
[
"415066004289224689",
"11886516471525959549",
"3696305541684646538",
"3035258219084094862"
],
[
"956231351009279921",
"10951436676983309100",
"2250248050743556928",
"1228298028208591648"
],
[0, 0, 0, 0],
[0, 0, 0, 0]
]
],
"processed_inputs": null,
"processed_params": null,
"processed_outputs": null,
"max_lookup_inputs": 22
}
平均变量
您可以从随机种子中生成一个随机的 ElGamal 键盘, 用于加密和解密 elgamalGenRandom 方法.
import { elgamalGenRandom, deserialize } from '@ezkljs/engine/web'
// Function to generate a 256 bit seed in the browser
// using a cryptographically secure source of randomness.
// DO NOT USE MATH.RANDOM AS ITS NOT A SECURE SOURCE OF RANDOMNESS
function generate256BitSeed(): Uint8ClampedArray {
const uuid = self.crypto.randomUUID()
const buffer = new TextEncoder().encode(uuid)
let seed = self.crypto.getRandomValues(buffer)
seed = seed.slice(0, 32)
return new Uint8ClampedArray(seed.buffer)
}
// Function to convert the return buffer elgamalGenRandom into a JSON object
function uint8ArrayToJsonObject(uint8Array) {
let string = new TextDecoder().decode(uint8Array)
let jsonObject = JSONBig.parse(string)
return jsonObject
}
const buffer = elgamalGenRandom(generate256BitSeed())
// We can take a look at the serialized contents of the
// buffer returned by elgamalGenRandom by converting
// the Uint8Array to a JSON object
let elgamalVariables = deserialize(buffer)
console.log(JSON.stringify(elgamalVariables, null, 2))
输出 :
{
"r": [
"3454421873345507054",
"16808310348879090885",
"8288793628600014913",
"1828575853528138165"
],
"sk": [
"8833745371820985590",
"8074024378078747364",
"11905923764904461178",
"851912620759564510"
],
"pk": [
[
"10721601427614006364",
"165460855961439422",
"2494139285445986520",
"993922445972607230"
],
[
"11418900806032782576",
"15880182954156597531",
"16112055391177898581",
"485390167428225782"
]
],
"aux_generator": [
[
"13503889609873471352",
"8660427972151475473",
"16373159693836788928",
"3475191407935738629"
],
[
"9899281824843853575",
"11168630486339379471",
"550474616581266472",
"3277527638473654200"
]
],
"window_size": 4
}
同步加密
使用上一个步骤的公钥(pk)和随机性(r),您可以使用 elgamalEncrypt 方法.
在例应用中,我们用这个 埃尔加马尔- 齐普FileDownload 中 下载方法
r, sk和pk 字段的 elgamalVariables 作为 JSON 文件.
一旦您通过点击页面上方的"Generate"按钮下载了您的Elgamal变量, 您可以在下载文件夹中解开“ elgamal var” 文件 .
在此之后,您需要创建包含您想要加密的信件的文本文件 .
由于精液加密是ZKP操作,我们需要将信件转换为
以 u254 表示的序列字段元素。 我们把现场元素序列化
通过将每个u254字段元素分解为4个u64.
用于参考, 请检查示例信件文件 这里
import { elgamalEncrypt, deserialize } from '@ezkljs/engine/web'
// This is the code for the button that triggers the
// generation of the elgamal encyrption ciphertext.
export async function handleGenElgamalEncryptionButton<T extends FileMapping>(
files: T,
): Promise<Uint8ArrayResult> {
const result = await convertFilesToFilesSer(files)
const start = performance.now() // Start the timer
let output = elgamalEncrypt(result['pk'], result['message'], result['r'])
const end = performance.now() // End the timer
let cipherText = deserialize(output)
console.log(JSON.stringify(cipherText, null, 2))
return {
output: output,
executionTime: end - start,
}
}
输出 :
[
[
[
"10209188590651781239",
"1606867371818388001",
"2009969314029440862",
"68539226482053667"
],
[
"11542493346099842483",
"15506753538305362096",
"11085744708984070406",
"1449434265777497591"
],
[
"14888253900846995994",
"17596893823803099477",
"7958618303962015576",
"30430041561409514"
]
],
[
[
"10873062863189913544",
"1639997932972316199",
"7128784801669346971",
"2001604198948701760"
],
[
"10873062863189913545",
"1639997932972316199",
"7128784801669346971",
"2001604198948701760"
]
]
]
椭圆解密
使用前一步的密钥(sk)和密码文本,可以使用 elgamalDecrypt 方法.
示例应用中使用的方法 : 手柄 GenElgal 解析按钮
import { elgamalDecrypt, deserialize } from '@ezkljs/engine/web'
// This is the code for the button that triggers the
// generation of the elgamal decryption of the ciphertext.
export async function handleGenElgamalDecryptionButton<T extends FileMapping>(
files: T,
): Promise<Uint8ArrayResult> {
const result = await convertFilesToFilesSer(files)
const start = performance.now() // Start the timer
let output = elgamalDecrypt(result['cipher'], result['sk'])
const end = performance.now() // End the timer
let message = deserialize(output)
console.log(JSON.stringify(message, null, 2))
return {
output: output,
executionTime: end - start,
}
}
输出 :
[
[0, 0, 0, 0],
[1, 0, 0, 0]
]
而如你所见,输出与我们加密的信息.txt中的原始消息相符:-)
证明
一旦你拥有所有必要的文物来证明(Witness、Proving Key、编译的Onnx模型和SRS),就可以使用 prove 方法.
实例应用中的方法使用 处理GenProof按钮
import { prove, deserialize } from '@ezkljs/engine/web'
// This is the code for the button that triggers the
// generation of a proof.
export async function handleGenProofButton<T extends FileMapping>(
files: T,
): Promise<Uint8ArrayResult> {
const result = await convertFilesToFilesSer(files)
const start = performance.now() // Start the timer
let output = prove(
result['data'],
result['pk'],
result['model'],
result['srs'],
)
const end = performance.now() // End the timer
let proof = deserialize(output)
console.log(JSON.stringify(proof.proof, null, 2))
console.log(JSON.stringify(proof.instances, null, 2))
return {
output: output,
executionTime: end - start,
}
}
输出 :
"061557cfc629ce604da894831cd029d0444acf4f7e384d980e1ba7a5204f71a42a4c51e0a713df141416fd197367b56cfb3a56b1a5c7bd137f25046aa051665612f99bb5511db4b3d582f052ec82ed49356370e792ac669a8e940d3568285bbc10c78295369f844ada048e5efa897a227f05f68cc62706b145f88e3f943e559021dcd03884e21668db5cd925ce6807d6549e851709a7a3a8ac7b9666d1b2c0100d8a2682aa56d96a2ce8d0ae671e0c35c9c43e380308d246e7613122beca4f0828cdf8c9eebb0d9ee5a82a99a16c83ee5af9830a532a37c20e9f007d3f0312c200bd527998d6bbf40fab9e56bebd691ccc8131800a9b52851aea5019d06b736c1c44165d3c1992de958365a24f151606a4a50ffd121765d5283f7f809fa684432504487d8c48f12aeaf9caf30133f9ab1ec820e18db051b2ea31f2a10174514c2fe7a9f069c9913c3ee92a5deb9ab2fe9960d9a538ca00c3e0fb2cc457b899702aa74659de954943d3a5b589f82502693997178fa55a4f8677cd908e8ddcd9801cb371b84319454d1e8e1028f61bd355ae90689f6d3793444cb343cfe4ea637f2171d720686e0de1415d608c66ea9780c1abc0c884520e81a79ff8c11f5c339c2fb76fc9740cadb6eacee21f5c4adda72dce152d513c6562f3a7d23acfa83eff124945177df5433f7929730caae3622748a1ca86a0f73d838fcdb35f2a5686f02cba638cb1e5dff403519bf4813c60782f79cf08f2e583c490670e126a21213704fde2b3a4f9a8c418b3278bfe2f55ff47c6aa2015ded6752829b3cee375941315c98e5e31d0f4ec9b894ec51fee3941c438c974c51c41316de87b09be96f5f00fcfd4a5f556069e5798f49de750328088864927d4e4fe981550e5db1b981cff01ec4b2d03f63d3b4af7dd228f44c0b9b0ad4b72caf9bf7d4cd8c5b0ca51a9cd13fcda2a4cac59a8dd387ee6f503bbfd463ee379fb89aaa0cd6cffcbd95bb7ac1a1fa808ce43262d64c2fe355fdeae74ed366d27840dda8d0fe55ba6ffcef20e1be8475b82348ec92e6025c2496e11448d8b33377a360fca6d2918898a686c982db58e3bd42491992af5b4aea77e6991ae1f039f87bd3ed19b9ec74423956a3708d42d087723886ddd1c5486ed16610a123250c3c5e6014689d2767b4b4e22ef1e3af64dee3c29529c19be5081fdcb0a40171965ad5985dd942a836617f9dbbe012f27810b056ecc44636fadbc71943de4a20ffd3dc97238b0dbca16be8c10180d116ba8cc95eeb91d10d5e19f59303e984a845437a2c850021b2aa3c2141b3e020d1469095d76fb8298a870761fd2fcc2fc8057bf030f62ac3144a5770c60b0000000000000000000000000000000000000000000000000000000000000000025865fabaaa498f6b2042243bea2276cc7cdcf25e091f96a43eddaefd2a6a935013a3354557867640b7e902a005357c86c983aaee40bccb10eb8d6db6415915e1b477e066537576f70026752c4a27da71d491787297c18ba10f2a42db41b13df2d4096ced97df14e8c89a27bbc0e26b828dad7f5005e98e201992d3849611f520a0ed6030f402c363e526bf3607cced57998af94af4b6800ba87a685edbf742b202130ece182d26daefcfbe7412ff629407701d9adeff18446edaefd9abf6c98000000000000000000000000000000000000000000000000000000000000000000bda3a6a626469e211146d2f95c86d27f2a5b9d6d68f0a9670cee1243955c0f0ae946af8d44a1c2d448c9e024d2bbe96e5d2fea5d7915ca8eb481149bfadef20ecfee1f2b7d4a2ab4df06e00486bc1bd1c617f4d59a63fc2ec237594364f7510230555057f2f18e41e3d497f29bc9a30e2cbb17896f3e7f47ae0c010264b74501a6e3c81f4a937b66fc4baa9aa1cd062cdffdfecfef96f25b52f904c8b9d5b00fdd517551727930dd77dd45abee71f7aa50966d6e97aa4ba24831bacdd6354a18930a821ae32be9c13e66e4cb2a82c4d9e9ffd500c812c9a9a731a288d9c2361888a4e1b1a43df2fe0eca25a20025b1fccef7a4da7f808e59d050b652551fa100eeac3b40d1280d81aba6a3bfb433421427713de0d5d0b0a3639f1c7a4be64b2949b981ef4878eece723328d64def0ad4df93a2774d6a882ff0b6cd039239d627305afa05611ce6705dedef6c8cb221a1f251df9b3f1a04bb66ddc64a3feac51c01e60a44f75a12585a003ae3ddfdefa7568d8b30a31e2e5e8252aa9f34e476253ae4d74edd37762da29a7287184a2b7052d3b133721407aa36b5c9996e68c70b754ea01d61e33600675eb551a78f9fbb5b2abaf9a6b4c06b6cb2046b1eba8b2691a9fb51778a8bb703a618ff4406336431f38dd6927b254bc13428883ae6520e7139fcf91c3ec9c1e2cffbd588759f55308bdde05292d97988056e23a5814c1725005ba922bcadd7efbe9144f55628330b6d6d7e75d634b917e69b648ef2fc267660bd34097a148cbce8d2e9bfb96c57c8de474bd0e8de959f76f7eea00cfe05de16ea880ec32cf78d401a6210bec843dbdcc3b55df477887fce3b921ec76a019098130b9a6bcde1ea663d118e28e6b1878a5826679483ab408ed1a128561d0b62d261638e6f75f70b0406102cf15270c719be349cadec69638972ffc3dfed2afa94ed6db88dd30fc3c370115a1b317c6994f1afb1b74e1bf123b176f5665e2f419728174208207da99c5a6fbf8eaadecfbbd7d028866dcc737b8bff64b8c40bb3ca88b64c55a6b6ecafc5336c130eafc53d44a40e26a3b0bd8270bfa32c2e"
[
[
[
0,
0,
0,
0
],
[
0,
0,
0,
0
],
[
0,
0,
0,
0
],
[
0,
0,
0,
0
]
]
]
校验
当证据生成时,您可以使用 verify 方法。 除了证明外,您还需要通过文件内容
校验密钥、电路设置和 srs 文件。 如果所有文物都正确,验证方法就会回归真实.
示例应用中所使用的方法 : 控件验证按钮
import { verify } from '@ezkljs/engine/web'
// This is the code for the button that triggers the
// verification of a proof.
export async function handleVerifyButton<T extends FileMapping>(
files: T,
): Promise<VerifyResult> {
const result = await convertFilesToFilesSer(files)
const start = performance.now() // Start the timer
let output = verify(
result['proof'],
result['vk'],
result['circuitSettings'],
result['srs'],
)
const end = performance.now() // End the timer
console.log(output)
return {
output: output,
executionTime: end - start,
}
}
输出 :
true
散开
我们还可以使用引擎来使用波塞冬散列函数来散列给定消息. 就像精灵加密和解密一样 我们需要把信息转换成 序列化的字段元素.
import { hash, deserialize } from '@ezkljs/engine/web'
// This is the code for the button that triggers the
// posiedon hashing of a message.
export async function handleGenHashButton(message: File): Promise<Uint8Array> {
const message_hash = await readUploadedFileAsBuffer(message)
let output = poseidonHash(message_hash)
let hash = deserialize(output)
console.log(JSON.stringify(hash, null, 2))
return output
}
如果您使用来自 elgamal 加密实例的同名信件. txt 文件, 您应该获得以下输出 :
[
[
[
"11803729404268464273",
"978666863701269549",
"3979600028898666103",
"2559194768255192844"
]
]
]