Mina protocol - 体验教程
- 一、零知识证明( Zero Knowledge Proof )
- 1、零知识证明(ZKP)的基本流程
- 工作流程:
- 2、zkApp 的优势:
- 3、zkApp 每个方法的编译过程:
- 二、搭建第一个zkapp
- 先决条件
- 1、下载或者更新 zkApp CLI
- 2、创建一个新项目
- 3、准备项目
- 4、编写 index.ts
- 5.、编写 zkapp 智能合约
- 6、与智能合约交互
- 7、构建并运行智能合约
- 8、运行结果
一、零知识证明( Zero Knowledge Proof )
零知识证明是一种密码学协议,允许证明者在不泄露任何额外信息的情况下,向验证者证明自己知道某个特定的秘密或信息。简单来说,证明者可以证明自己“知道”某件事,而无需透露这件事的具体内容。
零知识证明的重要性在于它能在保护隐私的同时,确保信息的真实性和可靠性。
零知识证明通常生成很麻烦,但是验证很迅速,其主要的运用场景是:
- 隐私保护:验资、投票、交易
- 计算压缩:区块链扩容
1、零知识证明(ZKP)的基本流程
- 编译(Compile)
- 输入:program
- 输出:verification_key(验证密钥,类型为字符串)
- 证明(Prove)
- 输入:program,public_inputs(公共输入),private_inputs(私有输入)
- 输出:proof(证明,类型为字符串)
- 验证(Verify)
- 输入:proof,public_inputs,verification_key
- 输出:布尔值(返回true 或 false 以验证证明的有效性)
工作流程:
Prover Function(证明者函数): 通过私有输入和公共输入生成零知识证明。
Verifier Function(验证者函数): 接收零知识证明与公共输入,并通过验证密钥进行验证,返回 true 或 false。
2、zkApp 的优势:
- zkApp 使用 TypeScript 编写,支持在浏览器中直接运行。
- 可编程隐私(Programmable Privacy):提供灵活的隐私配置,允许开发者根据需求编写隐私功能。
- 链下执行与链上验证(Offchain Execution & Onchain Verification):zkApp 可以在链下执行计算,链上只做验证,从而减少了Gas费用。
- 无需可信设置(No Trusted Setup):zkApp 不依赖于传统的可信设置步骤,简化了系统复杂度。
- 可组合性与 zkRollup 风格的扩展性(Composability and zkRollup-style Scaling):zkApp 通过递归扩展支持可组合性和 Rollup 风格的扩展性,提升了系统的可扩展性。
- 通过 Mina 区块链实现互操作性(Interoperability through Mina’s succinct blockchain):zkApp 通过 Mina 的精简区块链实现了跨链互操作性。
3、zkApp 每个方法的编译过程:
每个 zkApp 的方法实际上被编译成一个程序,该程序具有以下两个要素:
- 输入(Input): 方法的参数(arguments)和链上状态(on-chain values)。
- 输出(Output): 执行的更新列表以及与链上状态相关的前提条件列表。
zkApp 中的智能合约的执行流程可以被描述为:
zkCircuits(State0(public), private inputs) ===> Tx[proof, updates] ===> State1
解释:zkCircuits 处理初始状态(State0),接受公共和私有输入,生成交易证明(Tx proof)和更新列表(updates),最终更新到新的状态(State1)。
方法的参数包含私有输入和智能合约的某些状态。
方法还利用外部世界的状态信息,生成对智能合约状态和外部世界状态的更新。
二、搭建第一个zkapp
- 编写一个基本的智能合约,该合约存储一个作为链上状态的数字。
- 合约逻辑允许仅通过其平方来替换这个数字;例如,3 -> 9 ->81,依此类推。
- 使用zkApp CLI创建一个项目。
- 编写你的智能合约代码。
- 使用模拟的本地 Mina 区块链与智能合约进行交互。
先决条件
- Node.JS v18+
- NPM v10+
- git v2+
1、下载或者更新 zkApp CLI
npm install -g zkapp-cli
2、创建一个新项目
- Zk project 命令能够为你的项目生成用户界面。
- 请选择none选项。
zk project <project-name>
3、准备项目
首先删除新项目附带的默认文件。
rm src/Add.ts
rm src/Add.test.ts
rm src/interact.ts
创建新文件:
zk file src/Square
touch src/main.ts
- zk file命令创建了src/square.ts 和 src/Square.test.ts 测试文件。
4、编写 index.ts
在文本编辑器中打开 src/index.ts 并将其更改为如下所示:
import { Square } from './Square.js';
export { Square };
src/index.ts 文件包含了你希望从智能合约项目外部(例如从用户界面)进行调用的所有导出内容。
5.、编写 zkapp 智能合约
在src/Square.ts文件中编写你的智能合约。
/*
Field:o1js中的原生数字类型。你可以将Field元素视为无符号整数。Field元素是o1js中最基础的类型,所有其他o1js兼容类型都是基于 Field元素构建的。
SmartContract:创建zkApp智能合约的类。
state:在zkApp智能合约中使用的便捷装饰器,用于创建对zkApp账户中链上存储状态的引用。
State:在zkApp智能合约中使用的类,用于在zkApp账户中创建链上存储的状态。
method:在zkApp智能合约中使用的便捷装饰器,用于创建智能合约中的方法(类似函数)。使用此装饰器的方法是用户与智能合约交互的入口点。
*/
import { Field, SmartContract, state, State, method } from 'o1js';
// 名为 Square 的智能合约有一个链上状态元素,名为num,类型为Field,
// zkApp可以拥有最多八个链上状态字段
export class Square extends SmartContract {
@state(Field) num = State<Field>();
init() {
super.init(); // 添加init方法,用于在部署时设置智能合约的初始状态
this.num.set(Field(3)); // 将链上状态num初始化为值 3
}
// 如果用户向update()方法提供的数字(例如 9)是现有链上状态num的平方(例如 3),则将链上存储的num值更新为提供的值(在此情况下为 9)。
// 如果用户提供的数字不满足这些条件,他们将无法生成证明或更新链上状态。
@method async update(square: Field) {
const currentstate = this.num.get();
this.num.requireEquals(currentstate);
square.assertEquals(currentstate.mul(currentstate));
this.num.set(square);
}
}
当用户调用智能合约上的方法时,所有断言必须为真,才能从该智能合约生成零知识证明。Mina网络只有在附加的证明有效时才会接受交易并更新链上状态。
6、与智能合约交互
接下来,编写一个与智能合约交互的脚本。在 src 文件夹下创建 main.ts 文件,以便你能够与智能合约进行交互。
/*
Field:与之前学到的相同的 o1js 无符号整数类型。
Mina:用于模拟本地 Mina 区块链,方便将智能合约部署到其中,并像用户一样与其交互。
PrivateKey:一个包含用于操作私钥的函数的类。
AccountUpdate:生成一个可以更新 zkApp 账户的数据结构的类。
*/
import { Square } from './Square.js';
import { Field, Mina, PrivateKey, AccountUpdate } from 'o1js';
// 初始化本地区块链
const useProof = false; // 设置是否使用证明
const Local = await Mina.LocalBlockchain({ proofsEnabled: useProof });
Mina.setActiveInstance(Local);
// 模拟本地区块链提供了预先资助的账户
const deployerAccount = Local.testAccounts[0];
const deployerKey = deployerAccount.key;
const senderAccount = Local.testAccounts[1];
const senderKey = senderAccount.key;
// ----------------------------------------------------
// 创建公钥/私钥对。公钥是您的地址以及将 zkApp 部署到的位置
const zkAppPrivateKey = PrivateKey.random();
const zkAppAddress = zkAppPrivateKey.toPublicKey();
// 创建一个 Square 实例,并将其部署到 zkAppAddress
const zkAppInstance = new Square(zkAppAddress);
const deployTxn = await Mina.transaction(deployerAccount, async () => {
AccountUpdate.fundNewAccount(deployerAccount);
await zkAppInstance.deploy();
});
await deployTxn.sign([deployerKey, zkAppPrivateKey]).send(); // 发送签名
// 获取部署后 Square 的初始状态
const num0 = zkAppInstance.num.get();
console.log('state after init:', num0.toString());
// ----------------------------------------------------
// 创建了一个新交易,尝试将字段更新为值9。
const txn1 = await Mina.transaction(senderAccount, async () => {
await zkAppInstance.update(Field(9));
});
await txn1.prove();
await txn1.sign([senderKey]).send();
const num1 = zkAppInstance.num.get();
console.log('state after txn1:', num1.toString());
// ----------------------------------------------------
// 尝试一个失败的交易,看一看结果会是怎样
try {
const txn2 = await Mina.transaction(senderAccount, async () => {
await zkAppInstance.update(Field(75));
});
await txn2.prove();
await txn2.sign([senderKey]).send();
} catch (error: any) {
console.log(error.message);
}
const num2 = zkAppInstance.num.get();
console.log('state after txn2:', num2.toString());
// ----------------------------------------------------
const txn3 = await Mina.transaction(senderAccount, async () => {
await zkAppInstance.update(Field(81));
});
await txn3.prove();
await txn3.sign([senderKey]).send();
const num3 = zkAppInstance.num.get();
console.log('state after txn3:', num3.toString());
// ----------------------------------------------------
7、构建并运行智能合约
现在,Square 智能合约已经完成,以下命令将以模拟本地区块链运行您的项目。要将 TypeScript 代码编译成 JavaScript,请使用:
npm run build
要运行 JavaScript 代码:
node build/src/main.js
您可以选择将这些命令合并为一行(推荐):
npm run build && node build/src/main.js