Flutter计算器插件circom_witnesscalc的使用

Flutter计算器插件circom_witnesscalc的使用

本库是circom-witnesscalc的Flutter封装。它用于为零知识证明计算见证文件,并用Rust编写。

平台支持

  • iOS: 兼容任何具有64位架构的iOS设备。
  • macOS: 兼容任何具有arm64位架构的macOS设备。
  • Android: 兼容arm64-v8a和x86_64架构。

安装

pubspec.yaml文件中添加依赖:

flutter pub add circom_witnesscalc

使用

calculateWitness

函数接受输入的JSON字符串和图数据文件字节,并返回见证字节。

import 'package:circom_witnesscalc/circom_witnesscalc.dart';

// ...

final String inputs = await rootBundle.loadString("assets/authV2_inputs.json");
final Uint8List graphData = (await rootBundle.load("assets/authV2.wcd")).buffer.asUint8List();
final proof = await CircomWitnesscalc().calculateWitness(inputs, graphData);

示例应用

查看示例应用示例README以获取一个完整的示例。

示例代码

以下是一个完整的示例代码,展示了如何使用circom_witnesscalc插件生成见证文件和证明。

import 'dart:convert';

import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart';

import 'package:flutter/services.dart';
import 'package:circom_witnesscalc/circom_witnesscalc.dart';
import 'package:flutter_rapidsnark/flutter_rapidsnark.dart';
import 'package:share_plus/share_plus.dart';
import 'dart:io';
import 'package:path_provider/path_provider.dart';
import 'package:path/path.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});

  [@override](/user/override)
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final _circomWitnesscalcPlugin = CircomWitnesscalc();

  String? _inputsPath;
  String? _graphWCDPath;
  String? _zkeyFilePath;

  String? _defaultInputsPath;
  String? _defaultGraphWCDPath;
  String? _defaultZkeyFilePath;

  Uint8List? _witness;
  int _witnessTimestamp = 0;
  int _proofGenerationTimestamp = 0;
  String? _proof;
  String _errorMessage = "";

  [@override](/user/override)
  void initState() {
    super.initState();

    WidgetsBinding.instance.addPostFrameCallback((_) async {
      await copyAssetToDocumentsDirectory('assets/authV2_inputs.json');
      await copyAssetToDocumentsDirectory('assets/authV2.wcd');
      await copyAssetToDocumentsDirectory('assets/authV2.zkey');

      final directory = await getApplicationDocumentsDirectory();
      _defaultInputsPath = '${directory.path}/authV2_inputs.json';
      _defaultGraphWCDPath = '${directory.path}/authV2.wcd';
      _defaultZkeyFilePath = '${directory.path}/authV2.zkey';

      resetData();
    });
  }

  void resetData() {
    setState(() {
      _inputsPath = _defaultInputsPath;
      _graphWCDPath = _defaultGraphWCDPath;
      _zkeyFilePath = _defaultZkeyFilePath;
      resetOutputs();
    });
  }

  void resetOutputs() {
    _witness = null;
    _witnessTimestamp = 0;
    _proofGenerationTimestamp = 0;
    _proof = null;
    _errorMessage = "";
  }

  Future<void> copyAssetToDocumentsDirectory(String asset) async {
    final directory = await getApplicationDocumentsDirectory();
    final fileName = asset.split('/').last;
    final file = File('${directory.path}/$fileName');

    if (!await file.exists()) {
      final byteData = await rootBundle.load(asset);
      await file.writeAsBytes(byteData.buffer.asUint8List(), flush: true);
    }
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    final String witnessResult;
    if (_errorMessage.isNotEmpty) {
      witnessResult = "Error: $_errorMessage";
    } else if (_witness != null) {
      witnessResult = ("Witness generated in $_witnessTimestamp millis");
    } else {
      witnessResult = "Generate witness";
    }

    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin example app'),
        ),
        body: SafeArea(
          child: SingleChildScrollView(
            padding: const EdgeInsets.all(16.0),
            child: Column(
              crossAxisAlignment: CrossAxisAlignment.stretch,
              children: [
                ElevatedButton(
                  onPressed: resetData,
                  child: const Text("Reset all inputs"),
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.black,
                    foregroundColor: Colors.white,
                  ),
                ),
                const SizedBox(height: 16),
                ListTile(
                  title: Text(
                    _defaultInputsPath != _inputsPath
                        ? "Inputs json: ${basename(_inputsPath!)}"
                        : "Default authV2 inputs selected",
                    style: const TextStyle(fontSize: 16),
                  ),
                  trailing: ElevatedButton.icon(
                    onPressed: () async {
                      final result = await FilePicker.platform.pickFiles(
                        type: FileType.custom,
                        allowedExtensions: ["json"],
                      );

                      final file = result?.files.first;
                      if (file != null && file.path != null) {
                        setState(() {
                          _inputsPath = file.path!;
                          resetOutputs();
                        });
                      }
                    },
                    icon: const Icon(Icons.file_open),
                    label: const Text("Select"),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.black,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ),
                const SizedBox(height: 16),
                ListTile(
                  title: Text(
                    _defaultGraphWCDPath != _graphWCDPath
                        ? "Selected .wcd graph file: ${basename(_graphWCDPath!)}"
                        : "Default authV2.wcd graph selected",
                    style: const TextStyle(fontSize: 16),
                  ),
                  trailing: ElevatedButton.icon(
                    onPressed: () async {
                      final result = await FilePicker.platform
                          .pickFiles(type: FileType.any);

                      final filePath = result?.files.first.path;
                      if (filePath != null) {
                        setState(() {
                          _graphWCDPath = filePath;
                          resetOutputs();
                        });
                      }
                    },
                    icon: const Icon(Icons.file_open),
                    label: const Text("Select"),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.black,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ),
                const SizedBox(height: 16),
                ListTile(
                  title: Text(
                    _defaultZkeyFilePath != _zkeyFilePath
                        ? "Selected .zkey File path: ${basename(_zkeyFilePath!)}"
                        : "Default authV2 zkey file selected",
                    style: const TextStyle(fontSize: 16),
                  ),
                  trailing: ElevatedButton.icon(
                    onPressed: () async {
                      final result = await FilePicker.platform
                          .pickFiles(type: FileType.any);

                      final file = result?.files.first;
                      if (file?.path != null) {
                        setState(() {
                          _zkeyFilePath = file!.path!;
                          resetOutputs();
                        });
                      }
                    },
                    icon: const Icon(Icons.file_open),
                    label: const Text("Select"),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.black,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ),
                const SizedBox(height: 16),
                Text(witnessResult, style: const TextStyle(fontSize: 16)),
                const SizedBox(height: 16),
                ElevatedButton.icon(
                  onPressed: onGenerateWitness,
                  icon: const Icon(Icons.play_arrow),
                  label: const Text("Generate witness"),
                  style: ElevatedButton.styleFrom(
                    backgroundColor: Colors.black,
                    foregroundColor: Colors.white,
                  ),
                ),
                if (_witness != null) ...[
                  const SizedBox(height: 16),
                  ElevatedButton(
                    onPressed: onShare,
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.black,
                      foregroundColor: Colors.white,
                    ),
                    child: const Text("Share witness"),
                  ),
                  const SizedBox(height: 16),
                  ElevatedButton.icon(
                    onPressed: onGenerateProof,
                    icon: const Icon(Icons.play_arrow),
                    label: const Text("Generate proof"),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.black,
                      foregroundColor: Colors.white,
                    ),
                  ),
                ],
                if (_proof != null) ...[
                  const SizedBox(height: 16),
                  ElevatedButton(
                    onPressed: onShareProof,
                    child: const Text("Share proof"),
                    style: ElevatedButton.styleFrom(
                      backgroundColor: Colors.black,
                      foregroundColor: Colors.white,
                    ),
                  ),
                  const SizedBox(height: 16),
                  Text("Proof generated in $_proofGenerationTimestamp millis",
                      style: const TextStyle(fontSize: 16)),
                  const SizedBox(height: 16),
                  Text(_proof!, style: const TextStyle(fontSize: 16)),
                ],
              ],
            ),
          ),
        ),
      ),
    );
  }

  // Generate the witness
  Future<void> onGenerateWitness() async {
    final stopwatch = Stopwatch();
    try {
      if (_defaultInputsPath == null || _graphWCDPath == null) {
        return;
      }

      final file = File(_inputsPath!);
      final inputs = await file.readAsString();

      final fileWCD = File(_graphWCDPath!);
      final graphData = await fileWCD.readAsBytes();

      stopwatch.start();
      final witness = await _circomWitnesscalcPlugin.calculateWitness(
        inputs: inputs,
        graphData: graphData,
      );

      setState(() {
        _witness = witness;
        _witnessTimestamp = stopwatch.elapsedMilliseconds;
        _errorMessage = "";
      });
    } on PlatformException catch (e) {
      setState(() {
        _errorMessage = e.message ?? "Unknown error";
      });
    } finally {
      stopwatch.stop();
    }
  }

  // Generate the proof
  Future<void> onGenerateProof() async {
    if (_witness == null) {
      return;
    }

    final stopwatch = Stopwatch();
    try {
      stopwatch.start();
      final zkProof = await Rapidsnark().groth16Prove(
        zkeyPath: _zkeyFilePath!,
        witness: _witness!,
      );
      _proofGenerationTimestamp = stopwatch.elapsedMilliseconds;

      //console log the proof and public signals
      final proofData = {
        'proof': zkProof.proof,
        'pubSignals': zkProof.publicSignals,
      };

      // Convert the map to a JSON string
      final proofJson = jsonEncode(proofData);

      setState(() {
        _proof = proofJson;

        _errorMessage = "";
      });
    } on PlatformException catch (e) {
      setState(() {
        _errorMessage = e.message ?? "Unknown error";
      });
    } finally {
      stopwatch.stop();
    }
  }

  void onShare() {
    if (_witness == null) {
      return;
    }

    Share.shareXFiles(
      [
        XFile.fromData(_witness!, name: "witness.wtns"),
      ],
      text: "Witness generated in $_witnessTimestamp millis",
    );
  }

  void onShareProof() {
    if (_proof == null) {
      return;
    }

    Share.shareXFiles(
      [
        XFile.fromData(Uint8List.fromList(utf8.encode(_proof!)),
            name: "proof.json"),
      ],
      text: "Proof generated in $_proofGenerationTimestamp millis",
    );
  }
}

更多关于Flutter计算器插件circom_witnesscalc的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter计算器插件circom_witnesscalc的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


circom_witnesscalc 是一个用于生成零知识证明(ZKP)的 Flutter 插件,特别是在使用 Circom 电路时。它可以帮助你从电路中计算出 Witness(见证)数据,这是生成 ZKP 的重要步骤。以下是如何在 Flutter 项目中使用 circom_witnesscalc 插件的基本指南。

1. 安装插件

首先,在 pubspec.yaml 文件中添加 circom_witnesscalc 插件依赖:

dependencies:
  circom_witnesscalc: ^0.1.0  # 请根据实际情况使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 导入插件

在你的 Dart 文件中导入插件:

import 'package:circom_witnesscalc/circom_witnesscalc.dart';

3. 加载电路

在使用 circom_witnesscalc 之前,你需要加载一个 Circom 电路。通常,电路会被编译为一个 .wasm 文件:

String circuitWasmPath = 'path/to/your/circuit.wasm';
final witnessCalculator = WitnessCalculator(circuitWasmPath);

4. 计算 Witness

接下来,你可以使用 calculateWitness 方法来计算 Witness。你需要传递一个包含输入的 JSON 字符串:

String inputJson = '''
{
  "a": "123",
  "b": "456"
}
''';

final witness = await witnessCalculator.calculateWitness(inputJson);
print('Witness: $witness');

5. 处理 Witness 数据

calculateWitness 方法返回的 witness 是一个包含 Witness 数据的数组。你可以根据需要进行进一步处理,比如将其用于生成零知识证明。

6. 错误处理

在使用过程中,可能会遇到一些错误,比如无效的输入或电路加载失败。确保添加适当的错误处理:

try {
  final witness = await witnessCalculator.calculateWitness(inputJson);
  print('Witness: $witness');
} catch (e) {
  print('Error calculating witness: $e');
}

7. 示例代码

以下是一个完整的示例代码:

import 'package:circom_witnesscalc/circom_witnesscalc.dart';
import 'package:flutter/material.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  [@override](/user/override)
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(title: Text('Circom Witness Calculator Example')),
        body: Center(
          child: ElevatedButton(
            onPressed: () async {
              try {
                String circuitWasmPath = 'path/to/your/circuit.wasm';
                final witnessCalculator = WitnessCalculator(circuitWasmPath);

                String inputJson = '''
                {
                  "a": "123",
                  "b": "456"
                }
                ''';

                final witness = await witnessCalculator.calculateWitness(inputJson);
                print('Witness: $witness');
              } catch (e) {
                print('Error calculating witness: $e');
              }
            },
            child: Text('Calculate Witness'),
          ),
        ),
      ),
    );
  }
}
回到顶部