forked from madztheo/noir-react-native-starter
-
Notifications
You must be signed in to change notification settings - Fork 0
/
NoirModule.swift
157 lines (129 loc) · 5.22 KB
/
NoirModule.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
//
// NoirModule.swift
// NoirReactNative
//
// Created by Theo Madzou on 21/02/2024.
//
import Foundation
import React
import Swoir
import SwoirCore
import Swoirenberg
// c.f. https://stackoverflow.com/questions/26501276/converting-hex-string-to-nsdata-in-swift
extension String {
/// Create `Data` from hexadecimal string representation
///
/// This creates a `Data` object from hex string. Note, if the string has any spaces or non-hex characters (e.g. starts with '<' and with a '>'), those are ignored and only hex characters are processed.
///
/// - returns: Data represented by this hexadecimal string.
var hexadecimal: Data? {
var data = Data(capacity: count / 2)
let regex = try! NSRegularExpression(pattern: "[0-9a-f]{1,2}", options: .caseInsensitive)
regex.enumerateMatches(in: self, range: NSRange(startIndex..., in: self)) { match, _, _ in
let byteString = (self as NSString).substring(with: match!.range)
let num = UInt8(byteString, radix: 16)!
data.append(num)
}
guard data.count > 0 else { return nil }
return data
}
}
// c.f. https://stackoverflow.com/questions/39075043/how-to-convert-data-to-hex-string-in-swift
extension Data {
struct HexEncodingOptions: OptionSet {
let rawValue: Int
static let upperCase = HexEncodingOptions(rawValue: 1 << 0)
}
func hexEncodedString(options: HexEncodingOptions = []) -> String {
let format = options.contains(.upperCase) ? "%02hhX" : "%02hhx"
return self.map { String(format: format, $0) }.joined()
}
}
enum CircuitError: Error {
case unableToInitiateCircuit
case undefinedCircuit
}
@objc(NoirModule)
class NoirModule: NSObject {
var swoir = Swoir(backend: Swoirenberg.self)
var circuits: [String: Circuit] = [:]
func loadCircuit(circuitData: Data) throws -> String {
do {
let circuit = try swoir.createCircuit(manifest: circuitData)
let id = circuit.manifest.hash.description
circuits[id] = circuit
return id
} catch {
print("Error", error)
throw CircuitError.unableToInitiateCircuit
}
}
func getLocalSrsPath() -> String? {
// The srs file is assumed to be named "srs.local" and located in the ios folder
// and added to the app bundle (c.f. readme for more info)
let path = Bundle.main.path(forResource: "srs.local", ofType: nil)
return path
}
@objc(setupCircuit:resolve:reject:)
func setupCircuit(_ circuitData: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
do {
let circuitId = try loadCircuit(circuitData: circuitData.data(using: .utf8)!)
let circuit = circuits[circuitId]
// Try to get the local srs if any
// If no local srs file is found, it will be fetched directly
// online from Aztec servers
let localSrs = getLocalSrsPath()
try circuit?.setupSrs(srs_path: localSrs)
resolve(["circuitId": circuitId])
} catch {
print("Error", error)
reject("CIRCUIT_SETUP_ERROR", "Error setting up the circuit", error)
}
}
@objc(prove:circuitId:proofType:resolve:reject:)
func prove(_ inputs: [String: Any], circuitId: String, proofType: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
do {
let circuit = circuits[circuitId]
if circuit == nil {
throw CircuitError.undefinedCircuit
}
let start = DispatchTime.now()
let proof = try circuit?.prove(inputs, proof_type: proofType ?? "plonk")
let end = DispatchTime.now()
let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds
let timeInterval = Double(nanoTime) / 1_000_000
print("Proof generation time: \(timeInterval) ms")
let hexProof = proof?.proof.hexEncodedString()
let hexVkey = proof?.vkey.hexEncodedString()
resolve(["proof": hexProof, "vkey": hexVkey])
} catch {
print("Error", error)
reject("PROOF_GENERATION_ERROR", "Error generating the proof", error)
}
}
@objc(verify:vkey:circuitId:proofType:resolve:reject:)
func verify(_ proof: String, vkey: String, circuitId: String, proofType: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
do {
let circuit = circuits[circuitId]
if circuit == nil {
throw CircuitError.undefinedCircuit
}
let wholeProof = Proof(proof: proof.hexadecimal!, vkey: vkey.hexadecimal!)
let verified = try circuit!.verify(wholeProof, proof_type: proofType ?? "plonk")
resolve(["verified": verified])
} catch {
print("Error", error)
reject("PROOF_VERIFICATION_ERROR", "Error verifying the proof", error)
}
}
@objc(clearCircuit:resolve:reject:)
func clearCircuit(_ circuitId: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
circuits.removeValue(forKey: circuitId)
resolve(["success": true])
}
@objc(clearAllCircuits:reject:)
func clearAllCircuits(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) {
circuits.removeAll()
resolve(["success": true])
}
}