在 Rust 中实现零知识多方计算

在本教程中,我们将使用 Rust 和多方计算的概念实现一个简化的类似 zk-rollup 的系统。

该系统涉及三个主要组件:用于提交交易的用户界面、用于批量交易并生成证明的证明器(或操作器)以及检查这些证明的验证器(类似于主网)。

重点是确保这些组件之间的安全和高效通信,处理挑战和响应,以及为用户交互构建简单的终端用户界面(TUI)。

让我们开始吧! 🦀


多方计算(MPC)

多方计算 (MPC) 是密码学的一个子领域,它使多方能够根据其输入联合计算函数,同时保持这些输入的私密性。 MPC 的基本目标是确保在计算过程中,任何一方对另一方输入的了解不会多于从输出中推断出的信息。

核心原则

  • 隐私:各方的输入数据都是保密的,即使对于计算中的其他参与者也是如此。
  • 正确性:计算结果是正确的、可验证的,就像是由受信任的第三方计算的一样。
  • 独立性:任何一方都无法在自己输入的影响之外控制计算结果。


MPC的技术实现涉及几个关键组件和步骤:

  • 安全函数评估 (SFE):要计算的函数以允许安全评估的方式表示,通常使用加密原语。这可以包括将功能表示为逻辑门电路。
  • 秘密共享:输入被分成“份额”,并在各方之间分配。每个份额本身没有意义,但总的来说,这些份额可以重建原始输入。
  • 股份计算:双方签订协议来计算其股份的函数。这通常涉及交换包含从共享中派生的加密或模糊数据的消息。
  • 结果重构:计算结束后,各方将各自的份额合并起来,得到最终的输出。该协议确保此步骤不会泄露有关各个输入的附加信息。


在本教程中,我们将探索简化的类似 zk-rollup 系统的基本概念和实际实现,在概念上与多方计算 (MPC) 相似。尽管我们的重点不是明确地放在 MPC 上,但隐私、正确性和协作计算的基本原则是这两个领域的核心。


实现证明器(操作器)

证明器首先侦听来自打算提交交易的用户的传入连接。这是通过创建绑定到特定端口的 TcpListener 来实现的:

let listener = TcpListener::bind("localhost:7878").expect("Could not bind to port 7878");println!("Operator listening on port 7878");

This code snippet sets up the Prover to listen on port 7878, ready to accept transaction data from users.
此代码片段将 Prover 设置为侦听端口 7878,准备好接受来自用户的交易数据。

收集交易
当用户连接并提交交易时,证明器使用 BufReader 读取此数据,从而有效地管理流的数据:

for stream in listener.incoming() {let stream = stream.expect("Failed to accept incoming connection");println!("User connected");let mut reader = BufReader::new(stream);let mut transaction_data = String::new();reader.read_line(&mut transaction_data).expect("Failed to read from user");// Process transaction data...
}


该循环等待用户连接,读取每个用户发送的交易数据。为了简单起见,这里使用 read_line 方法,假设每个事务或一批事务以换行符结尾。

生成证明

对于每笔交易,证明器都会生成一个哈希值,模拟证明生成过程的一部分。然后将这些哈希值组合起来创建一个“证明”哈希值:

let transaction_hashes: Vec<String> = transactions.iter().map(|tx| {let tx_json = serde_json::to_string(tx).unwrap();let mut hasher = Sha256::new();hasher.update(tx_json.as_bytes());encode(hasher.finalize())
}).collect();let proof = generate_merkle_root(&transaction_hashes);

此代码迭代每个事务,将其序列化为 JSON,计算其 SHA-256 哈希值,并收集这些哈希值。 generate_merkle_root 函数(此处未显示)通常会组合这些哈希值以生成表示证明的单个哈希值。

与验证器沟通

生成证明后,证明器连接到验证器并发送交易哈希值和证明:

​
let transaction_hashes: Vec<String> = transactions.iter().map(|tx| {let tx_json = serde_json::to_string(tx).unwrap();let mut hasher = Sha256::new();hasher.update(tx_json.as_bytes());encode(hasher.finalize())
}).collect();let proof = generate_merkle_root(&transaction_hashes);​


此代码片段建立与验证器的连接(假设正在侦听端口 7879),将交易哈希值和证明打包到 JSON 对象中,将其序列化,然后通过 TCP 流发送。

Handling the Verifier’s Challenge
应对验证器的挑战

最后,证明器侦听来自验证器的质询,以请求的数据进行响应,并关闭连接:

let mut challenge = String::new();reader.read_line(&mut challenge).expect("Failed to read challenge from verifier");let challenge_index: usize = challenge.trim().parse().expect("Failed to parse challenge");let response = transaction_hashes[challenge_index].clone();verifier_stream.write_all(response.as_bytes()).expect("Failed to respond to challenge");

在此阶段,证明器读取验证器发出的质询(为简单起见,我们可以假设它是请求特定交易哈希的索引),从 transaction_hashes 检索相应的哈希,并将该哈希发送回作为回应。

实施验证器

探讨验证在简化的类似 zk-rollup 的系统中的作用,并辅以相关代码片段来说明实现细节。

启动验证器

验证器首先设置一个 TcpListener 来监听来自证明器的传入连接,指示新一批交易的到达以及相应的证明:

let listener = TcpListener::bind("localhost:7879").expect("Could not bind to port 7879");println!("Mainnet listening on port 7879");

此代码片段建立验证器侦听端口 7879,准备接收来自证明器的数据。


接收证明和交易哈希

连接后,验证使用 BufReader 读取传输的证明和交易摘要,以实现高效的数据处理:

for stream in listener.incoming() {let stream = stream.expect("Failed to accept incoming connection");println!("Operator connected");let mut reader = BufReader::new(stream);let mut proof_data_string = String::new();reader.read_line(&mut proof_data_string).expect("Failed to read from stream");let proof_data: Value = serde_json::from_str(&proof_data_string).expect("Failed to parse proof data");// Further processing...
}


该循环侦听来自证明器的连接并读取传输的 JSON 数据,其中包括交易哈希值和生成的证明。

发起质疑挑战

然后验证向证明发出挑战。这通常涉及请求特定信息来验证证明,例如特定的交易哈希:

let challenge = thread_rng().gen_range(0..transaction_summary.len());write!(stream, "{}\n", challenge).expect("Failed to send challenge to operator");

此代码片段选择一个随机交易哈希索引作为质询,并将该索引发送回证明器,期望证明器以相应的交易哈希进行响应。

验证响应

收到证明工具的响应后,验证工具将提供的交易哈希与其记录进行比较以验证证明:

let mut response = String::new();reader.read_line(&mut response).expect("Failed to read response from operator");let response = response.trim_end();let verified = transaction_summary.get(challenge).map_or(false, |expected_hash| {expected_hash.trim_matches('"') == response
});


在这里,验证读取证明器的响应并修剪任何潜在的换行符。验证涉及将接收到的哈希(响应)与交易摘要中的预期哈希进行比较。如果它们匹配,则该证明被认为是有效的。

记录和最终确定

最后,验证工具记录验证过程的结果,并根据结果采取适当的操作:

if verified {println!("Verification Success: Transactions committed to the blockchain.");
} else {println!("Verification Failed: Invalid proof. Transactions not committed.");
}

此结论代码记录验证是否成功或失败。成功的验证意味着交易是有效的并且可以“提交”到区块链。相反,失败表明证明或批量交易存在问题,从而阻止了它们的承诺。

整合到一起

prover.rs

use std::net::{TcpListener, TcpStream};
use std::io::{BufRead, BufReader, Write};
use serde::{Serialize, Deserialize};
use serde_json;
use sha2::{Digest, Sha256};
use hex::encode;#[derive(Serialize, Deserialize, Debug)]
struct Transaction {from: String,to: String,amount: u64,
}fn main() {let listener = TcpListener::bind("localhost:7878").expect("Could not bind to port 7878");println!("Operator listening on port 7878");loop {// Accepting transactions from userslet (user_stream, _) = listener.accept().expect("Failed to accept user connection");println!("User connected");let mut user_reader = BufReader::new(user_stream);let mut transactions = Vec::new();let mut line = String::new();// Read transactions from the userwhile user_reader.read_line(&mut line).expect("Failed to read from user") > 0 {if let Ok(tx) = serde_json::from_str::<Transaction>(&line) {transactions.push(tx);}line.clear();}if !transactions.is_empty() {// Process transactions and generate prooflet transaction_hashes = generate_transaction_hashes(&transactions);let proof = generate_merkle_root(&transaction_hashes);// Connect to the verifier and send proof along with transaction hasheslet mut verifier_stream = TcpStream::connect("localhost:7879").expect("Could not connect to verifier");let proof_data = serde_json::json!({"transaction_summary": transaction_hashes,"proof": proof});let serialized = serde_json::to_string(&proof_data).unwrap();verifier_stream.write_all(serialized.as_bytes()).expect("Failed to write to verifier stream");verifier_stream.write_all(b"\n").expect("Failed to write newline to verifier stream");println!("Proof and transaction hashes sent to verifier.");// Listen for a challenge from the verifier and respondlet mut verifier_reader = BufReader::new(verifier_stream);let mut challenge = String::new();verifier_reader.read_line(&mut challenge).expect("Failed to read challenge from verifier");let challenge: usize = challenge.trim().parse().expect("Failed to parse challenge");if challenge < transaction_hashes.len() {println!("Sending response hash: {}", transaction_hashes[challenge]);verifier_reader.get_mut().write_all(transaction_hashes[challenge].as_bytes()).expect("Failed to respond to challenge");verifier_reader.get_mut().write_all(b"\n").expect("Failed to write newline after challenge response");}}}
}fn generate_transaction_hashes(transactions: &[Transaction]) -> Vec<String> {transactions.iter().map(|tx| {let tx_json = serde_json::to_string(tx).unwrap();let mut hasher = Sha256::new();hasher.update(tx_json.as_bytes());encode(hasher.finalize())}).collect()
}fn generate_merkle_root(transaction_hashes: &[String]) -> String {let concatenated_hashes = transaction_hashes.concat();let mut hasher = Sha256::new();hasher.update(concatenated_hashes.as_bytes());encode(hasher.finalize())
}

user_tui.rs

use cursive::views::{Dialog, EditView, LinearLayout, TextView};
use cursive::{Cursive, CursiveExt};
use std::net::TcpStream;
use std::io::Write;
use cursive::traits::Resizable;
use serde_json::json;
use cursive::view::Nameable;
fn main() {let mut siv = Cursive::default();siv.add_layer(Dialog::new().title("Send Transaction").content(LinearLayout::vertical().child(TextView::new("From:")).child(EditView::new().with_name("from").fixed_width(20)).child(TextView::new("To:")).child(EditView::new().with_name("to").fixed_width(20)).child(TextView::new("Amount:")).child(EditView::new().with_name("amount").fixed_width(20)),).button("Send", |s| {let from = s.call_on_name("from", |v: &mut EditView| v.get_content()).unwrap();let to = s.call_on_name("to", |v: &mut EditView| v.get_content()).unwrap();let amount = s.call_on_name("amount", |v: &mut EditView| v.get_content()).unwrap();if let Ok(amount) = amount.parse::<u64>() {send_transaction(&from, &to, amount);s.add_layer(Dialog::info("Transaction sent!"));} else {s.add_layer(Dialog::info("Invalid amount!"));}}).button("Quit", |s| s.quit()),);siv.run();
}fn send_transaction(from: &str, to: &str, amount: u64) {let transaction = json!({"from": from,"to": to,"amount": amount});if let Ok(mut stream) = TcpStream::connect("localhost:7878") {let serialized = serde_json::to_string(&transaction).unwrap() + "\n";if let Err(e) = stream.write_all(serialized.as_bytes()) {eprintln!("Failed to send transaction: {}", e);}} else {eprintln!("Could not connect to prover");}
}

更新后的 Cargo.toml:

[package]
name = "rust-mpc"
version = "0.1.0"
edition = "2021"
authors = ["Luis Soares"]# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html[dependencies]
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
sha2 = "0.10.2"
hex = "0.4.3"
rand = "0.8.5"
cursive = "0.20.0"[[bin]]
name = "prover"
path = "src/prover.rs"[[bin]]
name = "verifier"
path = "src/verifier.rs"[[bin]]
name = "user_tui"
path = "src/user_tui.rs"

现在来测试一下

1.启动验证器

首先启动验证器,它监听来自证明器的证明和交易摘要,并发出验证挑战。

  • 打开终端窗口。
  • 导航到您的项目目录。
  • 使用 Cargo 启动验证器:
cargo run --bin verifier
  • Keep this terminal open to monitor the Verifier’s activity and outputs.
    保持该终端打开以监控验证器的活动和输出。

2.启动证明器

当验证器运行时,启动证明器,它聚合来自用户的交易,生成证明,并与验证器交互以进行验证。

  • 打开一个新的终端窗口。
  • 导航到您的项目目录。
  • 运行证明器:
cargo run --bin prover
  • 监控该终端以观察证明器的操作,包括接收用户的交易以及与验证器的通信。

3. 使用用户界面发送交易

如果您已经实现了用户界面(例如前面讨论的 TUI),请启动它以开始提交交易。如果 TUI 不可用,您可以通过使用 nc (netcat) 等工具手动将 JSON 格式的交易数据发送到 Prover 来模拟用户交易。

  • 打开用户界面的另一个终端窗口。
  • 如果使用 TUI,请使用以下命令启动它:
cargo run --bin user_tui
  • 如果手动发送交易,请使用 nc 连接到证明并输入交易数据:
nc localhost 7878 
{"from":"Alice","to":"Bob","amount":100} 
{"from":"Charlie","to":"Dave","amount":50}
  • 每次交易后按 Enter ,然后使用 Ctrl+D (Linux/Mac) 或 Ctrl+Z ,然后使用 Enter (Windows) 结束传输。

观察互动

  • 在验证器的终端上:观察来自证明器的连接、传入的交易哈希值和证明、发出的质询以及验证过程的结果。
  • 在证明器的终端上:查找来自用户的连接、传入交易、证明生成过程、与验证器的通信、收到的质询以及发送的响应。
  • 在用户界面终端上:如果使用 TUI,请按照提示输入和发送交易。如果手动发送交易,请确保 JSON 数据格式正确。

Github 项目地址:luishsr/rust-mpc: A Rust Multi-party Computation example (github.com)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/826481.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

esp32s3中使用双通道通信解决TCP粘包问题

在使用esp32 idf例程中的tcp_server和tcp_client通信测试时发现&#xff0c; 在tcp_server端&#xff0c;接收到一帧数据之后必须马上回复至少一个字节&#xff0c;才能保证每帧数据不粘包&#xff0c; 如果不回复操作&#xff0c;300ms以内的通信时延会导致tcp严重粘包&…

C++三大特性之一:继承

文章目录 前言一、继承方式二、继承类型继承中构造和析构的顺序继承中的内存分配多继承语法(非重点)继承中同名静态成员的处理继承一般在哪里用到进阶&#xff1a;菱形继承和虚拟继承 总结 前言 C三大特性&#xff1a;继承、多态和封装。继承是面向对象编程的一个核心概念&…

Elastic 网络爬虫:为你的网站添加搜索功能

作者&#xff1a;来自 Elastic Lionel Palacin 为了演示如何使用 Elastic 网络爬虫&#xff0c;我们将以一个具体的网站为例&#xff0c;讲解如何在该网站上添加搜索功能。我们将探讨发现网站的方法&#xff0c;并利用 Elastic 网络爬虫提供的功能&#xff0c;以最佳方式准备待…

HTML、CSS常用的vscode插件 +Css reset 和Normalize.css

个人主页&#xff1a;学习前端的小z 个人专栏&#xff1a;HTML5和CSS3悦读 本专栏旨在分享记录每日学习的前端知识和学习笔记的归纳总结&#xff0c;欢迎大家在评论区交流讨论&#xff01; 文章目录 ✍HTML、CSS常用的vscode插件&#x1f34e;1 HTML 标签同步重命名 – Auto Re…

【Java网络编程】网络编程中的基本概念及实现UDP、TCP客户端服务器程序

目录 一、什么是网络编程&#xff1f; 二、网络编程中的基本概念 1. 客户端和服务器 2. 请求和响应 三、Socket套接字 UDP数据报套接字编程 1. DatagramSocket 2. DatagramPacket 3. UDP回显客户端服务器程序 4. UDP字典客户端服务器程序 TCP流套接字编程 1. Serve…

笔记wife_assistant

一、wifi_spi_init //------------------------------------------------------------------------------------------------------------------- // 函数简介 WiFi 模块初始化 // 参数说明 *wifi_ssid 目标连接的 WiFi 的名称 字符串形式 // 参数说明 *pass…

SpringBoot 3.x + Swagger3 踩坑实录

问题描述 维护的SpringBoot版本是3.0版本&#xff0c;翻教程的时候发现很多SpringBoot2.x版本用的都是springfox&#xff0c;但问题是在SpringBoot3.x版本后&#xff0c;逐渐不支持springfox&#xff0c;强行启动会导致异常&#xff0c;现阶段使用的Springdoc进行替换。 参考…

Java多线程-API

常见API一览 Thread t1 new Thread(() -> {System.out.println("我是线程t1");System.out.println("Hello, World!"); }); t1.start(); // 获取线程名称 getName() // 线程名称默认是Thread-0, Thread-1, ... System.out.println(t1.getName());// 通过…

JVM类加载基本流程及双亲委派模型

1.JVM内存区域划分 一个运行起来的Java进程就是一个JVM虚拟机&#xff0c;这就需要从操作系统中申请一片内存区域。JVM申请到内存之后&#xff0c;会把这个内存划分为几个区域&#xff0c;每个区域都有各自的作用。 一般会把内存划分为四个区域&#xff1a;方法区(也称 "…

【网站项目】党员之家服务系统小程序

&#x1f64a;作者简介&#xff1a;拥有多年开发工作经验&#xff0c;分享技术代码帮助学生学习&#xff0c;独立完成自己的项目或者毕业设计。 代码可以私聊博主获取。&#x1f339;赠送计算机毕业设计600个选题excel文件&#xff0c;帮助大学选题。赠送开题报告模板&#xff…

【数字电路与系统】【北京航空航天大学】实验:时序逻辑设计——三色灯开关(二)、需求分析和系统设计

本次实验&#xff08;一&#xff09;见博客&#xff1a;【数字电路与系统】【北京航空航天大学】实验&#xff1a;时序逻辑设计——三色灯开关&#xff08;一&#xff09;、实验指导书 说明&#xff1a;本次实验的代码使用verilog编写&#xff0c;文章中为阅读方便&#xff0c…

指针的使用以及运算、二级指针、造成野指针的原因以及解决方法、指针和数组相互使用

第七章&#xff0c;指针的学习 目录 前言 一、指针的概念 二、指针的类型 三、野指针 四、指针的运算 五、指针和数组的关系以及使用 六、指针数组 七、二级指针 总结 前言 这章主要学习的是指针方面的知识&#xff0c;这节只是简单了解一下指针&#xff0c;并不会深…

uniapp H5项目 获取接口的二进制流转化成图片url(base64)

如果你使用的是uniapp, 并且你从接口获取下来的数据长这样&#xff1a; 想要把取到的数据展示成图片&#xff0c;那么你可以这样做&#xff1a; // 这是我们的项目封装的请求方法const res await this.$api.getKaptcha({originResponse: true, // 这样写是为了在request那边特…

路由器热备份

HSRP HSRP&#xff08;Hot Standby Routing Protocol&#xff09;热备份路由选择协议 HSRP是思科私有的协议&#xff0c;HSRP起到一个双网关热备份的一个目的&#xff0c;不考虑线路问题针对设备而言&#xff0c;一个设备挂了还有另外一台设备&#xff0c;所以双网关也叫双机…

开发手札:Unity+NetMQ集成使用注意事项

netmq docs netmq算是一个轻量级消息库&#xff0c;最近两天用了一下&#xff0c;分享一下注意事项。 1.根据topic区分通信信道&#xff0c;因为二进制中字母大小写值并不相同&#xff0c;所以topic必须区分大小写。 2.Initial强制DotNet模式&#xff1a;Asy…

stl_set

文章目录 set1.关联式容器2.键值对3. set3.1 set介绍3.2 set的使用3.2.1 pair3.2.2 find3.2.3 lower_bound 3.3 multiset3.3.1 multiset的介绍3.3.2 multiset的使用3.3.3 find3.3.4 equal_range3.3.5 erase set 1.关联式容器 在初阶阶段&#xff0c;我们已经接触过STL中的部分…

嵌入式物联网实战开发笔记-乐鑫ESP32芯片功能对比以及功能选型【doc.yotill.com】

乐鑫ESP32入门到精通项目开发参考百例下载&#xff1a; 链接&#xff1a;https://pan.baidu.com/s/1ATvRnAZvxkev-PJfd3EAPg?pwd4e33 提取码&#xff1a;4e33 2.1 初识 ESP32 ESP32-S3 是一款低功耗的 MCU 系统级芯片 (SoC)&#xff0c;支持 2.4 GHz Wi-Fi 和低功耗蓝牙 (…

设计模式:访问者模式

文章目录 定义应用场景示例代码反例原则间的权衡与冲突设计模式的局限性总结与建议 定义 访问者模式&#xff08;Visitor Pattern&#xff09;是一种将算法与对象结构分离的设计模式。这种模式中&#xff0c;可以在不修改已有程序结构的前提下&#xff0c;通过添加额外的“访问…

强固型国产化工业电脑,在电子看板行业应用,机器视觉在汽车产线行业应用

电子看板行业应用 智能电子看板的核心是通过实现工厂的全面可视化、自动化管理&#xff0c;最终达到提高效率、降低成本及提高产品质量的目标。电子看板硬件主要有两部分组成&#xff1a;微型工业计算机&#xff0c;显示终端&#xff08;平板电视、LCD&#xff09; 方案需求 …

使用C++模板实现工厂模式

工厂模式是一种常用的设计模式&#xff0c;用于创建对象&#xff0c;而不需要指定将要创建的对象的具体类。C模板可以用来实现一个通用的工厂模式&#xff0c;使得工厂能够创建任何类型的对象&#xff0c;只要这些对象遵循了一定的创建接口。以下是使用C模板实现工厂模式的一个…