-
Notifications
You must be signed in to change notification settings - Fork 13
/
gnark.go
173 lines (153 loc) · 5.13 KB
/
gnark.go
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
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package gnark
import (
"bytes"
"fmt"
"github.com/consensys/gnark-crypto/ecc"
"github.com/consensys/gnark-crypto/ecc/bn254/fr"
tedwards "github.com/consensys/gnark-crypto/ecc/twistededwards"
"github.com/consensys/gnark/backend/groth16"
"github.com/consensys/gnark/backend/witness"
"github.com/consensys/gnark/frontend"
"github.com/consensys/gnark/std/algebra/native/twistededwards"
"github.com/consensys/gnark/std/math/uints"
"github.com/hyle-org/hyle/x/zktx"
)
// This is the public interface that a verifiable circuit must implement
type HyleCircuit struct {
Version frontend.Variable `gnark:",public"`
InputLen frontend.Variable `gnark:",public"`
Input []frontend.Variable `gnark:",public"`
OutputLen frontend.Variable `gnark:",public"`
Output []frontend.Variable `gnark:",public"`
OriginLen frontend.Variable `gnark:",public"` // This is encoded as a single ASCII character per byte
Origin [256]uints.U8 `gnark:",public"` // The max capacity is 256 bytes (arbitrarily)
CallerLen frontend.Variable `gnark:",public"` // The 'len' arguments are necessary to parse the witness
Caller [256]uints.U8 `gnark:",public"`
BlockTime frontend.Variable `gnark:",public"`
BlockNb frontend.Variable `gnark:",public"`
TxHash [64]uints.U8 `gnark:",public"`
}
func (c *HyleCircuit) Define(api frontend.API) error { return nil }
// Utility for tests mostly
func ToArray256(d []byte) [256]uints.U8 {
// Pad to 256
if len(d) < 256 {
padded := make([]byte, 256)
copy(padded, d)
d = padded
}
return [256]uints.U8(uints.NewU8Array(d))
}
func ToArray64(d []byte) [64]uints.U8 {
// Pad to 64
if len(d) < 64 {
padded := make([]byte, 64)
copy(padded, d)
d = padded
}
return [64]uints.U8(uints.NewU8Array(d))
}
// Struct type expected for the "proof" argument
type Groth16Proof struct {
Proof []byte `json:"proof"`
VerifyingKey []byte `json:"verifying_key"`
PublicWitness []byte `json:"public_witness"`
}
func (proof *Groth16Proof) ParseProof() (groth16.Proof, groth16.VerifyingKey, witness.Witness, error) {
proofReader := bytes.NewReader(proof.Proof)
g16p := groth16.NewProof(ecc.BN254)
if _, err := g16p.ReadFrom(proofReader); err != nil {
return nil, nil, nil, fmt.Errorf("failed to parse groth16 proof: %s", err)
}
proofReader = bytes.NewReader(proof.VerifyingKey)
vk := groth16.NewVerifyingKey(ecc.BN254)
if _, err := vk.ReadFrom(proofReader); err != nil {
return nil, nil, nil, fmt.Errorf("failed to parse groth16 vk: %w", err)
}
proofReader = bytes.NewReader(proof.PublicWitness)
fid, _ := twistededwards.GetSnarkField(tedwards.BN254) // Note: handle the error if required
witness, err := witness.New(fid)
if err != nil {
return nil, nil, nil, fmt.Errorf("failed to initialize groth16 witness: %w", err)
}
if _, err := witness.ReadFrom(proofReader); err != nil {
return nil, nil, nil, fmt.Errorf("failed to parse groth16 witness: %w", err)
}
return g16p, vk, witness, nil
}
func parseArray(input *fr.Vector, length int) ([]byte, error) {
output := make([]byte, length)
for i := 0; i < length; i++ {
output[i] = uint8((*input)[i].Uint64())
}
// Consume the input
*input = (*input)[length:]
return output, nil
}
func parseSlice(input *fr.Vector) ([]byte, error) {
length := (*input)[0].Uint64()
if length == 0 {
*input = (*input)[1:]
return []byte{}, nil
}
*input = (*input)[1:]
// Sanity check
if length > 0x1000000 || length > uint64(len(*input)) {
return nil, fmt.Errorf("array length exceeds input size")
}
return parseArray(input, int(length))
}
func parseString(input *fr.Vector) (string, error) {
if output, err := parseSlice(input); err != nil {
return "", err
} else {
return string(output), nil
}
}
func parseNumber[T uint8 | uint16 | uint32 | uint64](input *fr.Vector) (T, error) {
if len(*input) < 1 {
return 0, fmt.Errorf("input is empty")
}
val := T((*input)[0].Uint64())
*input = (*input)[1:]
return val, nil
}
func (proof *Groth16Proof) ExtractData(witness witness.Witness) (*zktx.HyleOutput, error) {
// Check payload version identifier
pubWitVector, ok := witness.Vector().(fr.Vector)
if !ok {
return nil, fmt.Errorf("failed to cast witness vector to fr.Vector")
} else if pubWitVector[0] != fr.NewElement(1) {
return nil, fmt.Errorf("invalid version identifier %s, expected 1", pubWitVector[0].Text(10))
}
output := &zktx.HyleOutput{}
// Manually parse the circuit.
var err error
slice := pubWitVector[1:]
if output.InitialState, err = parseSlice(&slice); err != nil {
return nil, err
}
if output.NextState, err = parseSlice(&slice); err != nil {
return nil, err
}
if output.Origin, err = parseString(&slice); err != nil {
return nil, err
}
// Skip remaining bytes
slice = slice[256-len(output.Origin):]
if output.Caller, err = parseString(&slice); err != nil {
return nil, err
}
// Skip remaining bytes
slice = slice[256-len(output.Caller):]
if output.BlockNumber, err = parseNumber[uint64](&slice); err != nil {
return nil, err
}
if output.BlockTime, err = parseNumber[uint64](&slice); err != nil {
return nil, err
}
if output.TxHash, err = parseArray(&slice, 64); err != nil {
return nil, err
}
return output, nil
}