Skip to main content

eth zkp groth16 (没运行,不确定)

参考项目

https://github.com/privacy-scaling-explorations/zkp-app-boilerplate/blob/main/package.json

验证器合约

验证器合约(IVerifier)通常是由零知识证明工具链(如snarkjscircom)生成的。这些工具会根据你定义的电路生成相应的验证器合约代码。你需要将这个生成的验证器合约部署到区块链上,然后在你的主合约中引用它。

验证器合约的生成和部署

1. 使用snarkjs生成验证器合约

假设你已经完成了电路的编写和证明的生成,以下是如何生成验证器合约的步骤:

# Compile the circuit
circom circuit.circom --r1cs --wasm --sym --c

# Generate the witness
node circuit_js/generate_witness.js circuit_js/circuit.wasm input.json witness.wtns

# Setup (generate proving and verification keys)
snarkjs groth16 setup circuit.r1cs powersOfTau28_hez_final_10.ptau circuit_final.zkey

# Generate the proof
snarkjs groth16 prove circuit_final.zkey witness.wtns proof.json public.json

# Generate the verifier contract
snarkjs zkey export solidityverifier circuit_final.zkey verifier.sol

这将生成一个名为verifier.sol的 Solidity 文件,其中包含验证器合约的代码。

2. 部署验证器合约

你可以使用 Remix、Truffle 或 Hardhat 等工具将生成的验证器合约部署到以太坊区块链上。以下是使用 Truffle 部署的示例:

truffle-config.js(配置文件)

module.exports = {
networks: {
development: {
host: '127.0.0.1',
port: 8545,
network_id: '*', // Match any network id
},
},
compilers: {
solc: {
version: '0.8.0', // Specify the Solidity version
},
},
};

Verifier.sol(生成的验证器合约)

snarkjs生成的verifier.sol文件放在contracts目录下。

部署脚本migrations/2_deploy_verifier.js

const Verifier = artifacts.require('Verifier');

module.exports = function (deployer) {
deployer.deploy(Verifier);
};

运行部署脚本:

truffle migrate --network development

部署成功后,你会得到验证器合约的地址。

在主合约中引用验证器合约

在你的主合约中,你需要引用已部署的验证器合约地址。以下是如何更新你的主合约:

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.0;

interface IVerifier {
function verifyProof(
uint256[2] memory a,
uint256[2][2] memory b,
uint256[2] memory c,
uint256[7] memory input
) external view returns (bool r);
}

contract ZkApp {
struct Proof {
uint256[2] a;
uint256[2][2] b;
uint256[2] c;
}

address public immutable verifier;
uint256[2][] public pubKeys;
uint256[] public signedMessages;

constructor(address verifier_) {
verifier = verifier_;
}

function registerKey(uint256[2] memory key) public {
require(pubKeys.length < 3, "Multisig for 3");
pubKeys.push(key);
}

function recordSignedMessage(uint256 message, Proof memory proof) public {
require(pubKeys.length == 3, "Key registration is not done yet.");
uint256[7] memory publicSignals;
publicSignals[0] = message;
publicSignals[1] = pubKeys[0][0];
publicSignals[2] = pubKeys[1][0];
publicSignals[3] = pubKeys[2][0];
publicSignals[4] = pubKeys[0][1];
publicSignals[5] = pubKeys[1][1];
publicSignals[6] = pubKeys[2][1];
require(verify(publicSignals, proof), "SNARK verification failed");
signedMessages.push(message);
}

function verify(uint256[7] memory publicSignals, Proof memory proof) public view returns (bool) {
bool result = IVerifier(verifier).verifyProof(proof.a, proof.b, proof.c, publicSignals);
return result;
}

function totalSignedMessages() public view returns (uint256) {
return signedMessages.length;
}
}

在部署主合约时,将验证器合约的地址传递给构造函数:

const ZkApp = artifacts.require('ZkApp');
const Verifier = artifacts.require('Verifier');

module.exports = function (deployer) {
deployer.deploy(Verifier).then(() => {
return deployer.deploy(ZkApp, Verifier.address);
});
};

总结

  • 生成验证器合约:使用snarkjs等工具链生成验证器合约。
  • 部署验证器合约:将生成的验证器合约部署到区块链上。
  • 引用验证器合约:在主合约中引用已部署的验证器合约地址,并使用其验证功能。

通过这种方式,你可以在 Solidity 智能合约中高效地使用 Groth16 零知识证明进行验证。

oh~~ 通过接口调用合约

是的,在 Solidity 中,你可以通过接口(interface)来与其他智能合约进行交互。通过接口,你可以定义其他合约的函数签名,然后使用合约地址来实例化该接口,以便调用这些函数。

在你的例子中,IVerifier是一个接口,用于定义验证器合约的函数签名。你可以使用IVerifier(verifier)来实例化该接口,其中verifier是验证器合约的地址。这样,你就可以调用验证器合约的verifyProof函数。

详细解释

1. 定义接口

首先,你定义了一个接口IVerifier,其中包含验证器合约的函数签名:

interface IVerifier {
function verifyProof(
uint256[2] memory a,
uint256[2][2] memory b,
uint256[2] memory c,
uint256[3] memory input
) external view returns (bool r);
}

2. 使用接口实例化合约

在你的合约中,你可以通过合约地址来实例化这个接口:

bool result = IVerifier(verifier).verifyProof(
proof.a,
proof.b,
proof.c,
publicSignals
);

这里的IVerifier(verifier)就是通过verifier地址来实例化IVerifier接口,这样你就可以调用verifyProof函数。

完整示例

假设你已经部署了验证器合约,并且知道其地址。你可以编写一个主合约来引用这个验证器合约:

// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity >=0.8.0;

interface IVerifier {
function verifyProof(
uint256[2] memory a,
uint256[2][2] memory b,
uint256[2] memory c,
uint256[3] memory input
) external view returns (bool r);
}

contract ZkApp {
struct Proof {
uint256[2] a;
uint256[2][2] b;
uint256[2] c;
}

address public immutable verifier;
uint256[2][] public pubKeys;
uint256[] public signedMessages;

constructor(address verifier_) {
verifier = verifier_;
}

function registerKey(uint256[2] memory key) public {
require(pubKeys.length < 3, "Multisig for 3");
pubKeys.push(key);
}

function recordSignedMessage(uint256 message, Proof memory proof) public {
require(pubKeys.length == 3, "Key registration is not done yet.");
uint256[3] memory publicSignals;
publicSignals[0] = message;
publicSignals[1] = pubKeys[0][0];
publicSignals[2] = pubKeys[1][0];
require(verify(publicSignals, proof), "SNARK verification failed");
signedMessages.push(message);
}

function verify(uint256[3] memory publicSignals, Proof memory proof) public view returns (bool) {
bool result = IVerifier(verifier).verifyProof(proof.a, proof.b, proof.c, publicSignals);
return result;
}

function totalSignedMessages() public view returns (uint256) {
return signedMessages.length;
}
}

部署和使用

  1. 部署验证器合约:首先部署验证器合约,并获取其地址。
  2. 部署主合约:在部署主合约时,将验证器合约的地址传递给构造函数。
const ZkApp = artifacts.require('ZkApp');
const Verifier = artifacts.require('Verifier');

module.exports = function (deployer) {
deployer.deploy(Verifier).then(() => {
return deployer.deploy(ZkApp, Verifier.address);
});
};

通过这种方式,你可以在主合约中引用并调用验证器合约的函数,实现零知识证明的验证。