Skip to content

Commit

Permalink
fix(gnarkx,contracts): fix and unit test IO (#46)
Browse files Browse the repository at this point in the history
* feat: barebones gnark circuit test

* refactor: cleanup test

* refactor: cleanup

* feat: include input/output in proof.json
  • Loading branch information
ctian1 authored Aug 14, 2023
1 parent df0987a commit ba79cd0
Show file tree
Hide file tree
Showing 16 changed files with 442 additions and 136 deletions.
2 changes: 1 addition & 1 deletion contracts/src/FunctionGateway.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ contract FunctionGateway is IFunctionGateway, FunctionRegistry, TimelockedUpgrad
uint256 _gasLimit,
address _refundAccount
) public payable returns (bytes32) {
bytes32 inputHash = keccak256(_input);
bytes32 inputHash = sha256(_input);
bytes32 contextHash = keccak256(_context);
FunctionRequest memory r = FunctionRequest({
functionId: _functionId,
Expand Down
12 changes: 6 additions & 6 deletions contracts/test/FunctionGateway.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ contract FunctionGatewayTest is Test, IFunctionGatewayEvents, IFunctionGatewayEr

expectedRequest = FunctionRequest({
functionId: FUNCTION_ID,
inputHash: keccak256(REQUEST),
inputHash: sha256(REQUEST),
outputHash: bytes32(0),
contextHash: keccak256(CALLBACK_CONTEXT),
callbackAddress: consumer,
Expand Down Expand Up @@ -105,7 +105,7 @@ contract FunctionGatewayTest is Test, IFunctionGatewayEvents, IFunctionGatewayEr
) = FunctionGateway(gateway).requests(requestId);
assertEq(prevNonce + 1, FunctionGateway(gateway).nonce());
assertEq(FUNCTION_ID, functionId);
assertEq(keccak256(REQUEST), inputHash);
assertEq(sha256(REQUEST), inputHash);
assertEq(bytes32(0), outputHash);
assertEq(keccak256(CALLBACK_CONTEXT), contextHash);
assertEq(address(consumer), callbackAddress);
Expand Down Expand Up @@ -137,7 +137,7 @@ contract FunctionGatewayTest is Test, IFunctionGatewayEvents, IFunctionGatewayEr
) = FunctionGateway(gateway).requests(requestId);
assertEq(prevNonce + 1, FunctionGateway(gateway).nonce());
assertEq(FUNCTION_ID, functionId);
assertEq(keccak256(REQUEST), inputHash);
assertEq(sha256(REQUEST), inputHash);
assertEq(bytes32(0), outputHash);
assertEq(keccak256(CALLBACK_CONTEXT), contextHash);
assertEq(address(consumer), callbackAddress);
Expand Down Expand Up @@ -177,7 +177,7 @@ contract FunctionGatewayTest is Test, IFunctionGatewayEvents, IFunctionGatewayEr
) = FunctionGateway(gateway).requests(requestId);
assertEq(prevNonce + 1, FunctionGateway(gateway).nonce());
assertEq(FUNCTION_ID, functionId);
assertEq(keccak256(REQUEST), inputHash);
assertEq(sha256(REQUEST), inputHash);
assertEq(bytes32(0), outputHash);
assertEq(keccak256(CALLBACK_CONTEXT), contextHash);
assertEq(address(consumer), callbackAddress);
Expand Down Expand Up @@ -213,7 +213,7 @@ contract FunctionGatewayTest is Test, IFunctionGatewayEvents, IFunctionGatewayEr
) = FunctionGateway(gateway).requests(requestId);
assertEq(prevNonce + 1, FunctionGateway(gateway).nonce());
assertEq(FUNCTION_ID, functionId);
assertEq(keccak256(REQUEST), inputHash);
assertEq(sha256(REQUEST), inputHash);
assertEq(REQUEST_OUTPUT_HASH, outputHash);
assertEq(keccak256(CALLBACK_CONTEXT), contextHash);
assertEq(address(consumer), callbackAddress);
Expand Down Expand Up @@ -251,7 +251,7 @@ contract FunctionGatewayTest is Test, IFunctionGatewayEvents, IFunctionGatewayEr
) = FunctionGateway(gateway).requests(requestId);
assertEq(prevNonce + 1, FunctionGateway(gateway).nonce());
assertEq(FUNCTION_ID, functionId);
assertEq(keccak256(REQUEST), inputHash);
assertEq(sha256(REQUEST), inputHash);
assertEq(REQUEST_OUTPUT_HASH, outputHash);
assertEq(keccak256(CALLBACK_CONTEXT), contextHash);
assertEq(address(consumer), callbackAddress);
Expand Down
4 changes: 4 additions & 0 deletions gnarkx/builder/debug.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,7 @@ func (a *API) PrintVarBytes(vars []vars.Byte) {
}
a.api.Println(fvars)
}

func (a *API) PrintU64(u64 vars.U64) {
a.api.Println(u64.Value)
}
11 changes: 11 additions & 0 deletions gnarkx/builder/inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,14 @@ func (r *InputReader) ReadBytes32() [32]vars.Byte {
}
return out
}

// ReadUint64 reads a uint64 in big-endian from the input stream.
func (r *InputReader) ReadUint64() vars.U64 {
out := vars.NewU64()
for i := 0; i < 8; i++ {
out = r.api.MulU64(out, vars.U64{Value: vars.Variable{Value: 256}})
byte := r.readByte()
out = r.api.AddU64(out, vars.U64{Value: byte.Value})
}
return out
}
6 changes: 6 additions & 0 deletions gnarkx/builder/outputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,12 @@ func NewOutputWriter(api API) *OutputWriter {
// Writes a single u64 to the output stream.
func (w *OutputWriter) WriteU64(i1 vars.U64) {
bytes := w.api.ToBytes32FromU64LE(i1)
for i := 0; i < 8; i++ {
w.bytes = append(w.bytes, bytes[8-i-1])
}
}

func (w *OutputWriter) WriteBytes32(bytes [32]vars.Byte) {
for i := 0; i < 32; i++ {
w.bytes = append(w.bytes, bytes[i])
}
Expand Down
24 changes: 24 additions & 0 deletions gnarkx/builder/u64.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,30 @@ func (a *API) AddU64(in ...vars.U64) vars.U64 {
return vars.U64{Value: reduced}
}

func (a *API) MulU64(in ...vars.U64) vars.U64 {
// Find the maximum number of bits needed to represent the product of all inputs.
nbTerms := len(in)
nbMaxBits := 64 * nbTerms

// Compute the product of all inputs over the field.
acc := vars.ONE
for i := 0; i < len(in); i++ {
acc = a.Mul(acc, in[i].Value)
}

// Convert the sum to binary with the calculated number of maximum bits.
bits := a.ToBinaryLE(acc, int(nbMaxBits))

// Compute acc % 2^64.
reduced := vars.ZERO
power := vars.ONE
for i := 0; i < 64; i++ {
reduced = a.Add(reduced, a.Mul(bits[i].Value, power))
power = a.Mul(power, vars.TWO)
}
return vars.U64{Value: reduced}
}

// Converts a U64 to a Bytes32 in little-endian format. In particular, the u64 is decomposed into
// bytes b1, ..., b8 such that 256^0 * b1 + ... + 256^7 * b8 is the native value. The bytes32
// returned is in the form [b1, ..., b8, 0, ..., 0].
Expand Down
2 changes: 1 addition & 1 deletion gnarkx/hash/sha256/sha256.go
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ func Hash(api builder.API, in []vars.Byte) [32]vars.Byte {
// Computes sha256(in) && ((1 << nbBits) - 1).
func HashAndTruncate(api builder.API, in []vars.Byte, nbBits int) vars.Variable {
// Compute the untruncated hash.
hash := Hash(api, in)
hash := vars.ReverseBytes32(Hash(api, in))

// Accumulate with byte digits until we get to the last relevant byte.
acc := vars.ZERO
Expand Down
136 changes: 136 additions & 0 deletions gnarkx/succinct/build.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
package succinct

import (
"fmt"
"os"

"github.com/consensys/gnark-crypto/ecc"
"github.com/consensys/gnark/backend/groth16"
"github.com/consensys/gnark/constraint"
)

type CircuitBuild struct {
pk groth16.ProvingKey
vk groth16.VerifyingKey
r1cs constraint.ConstraintSystem
}

// Export exports the R1CS, proving key, and verifying key to files.
func (build *CircuitBuild) Export() {
// Make build directory.
err := os.MkdirAll("build", 0755)
if err != nil {
fmt.Printf("Failed to create directory: %v\n", err)
return
}

// Write R1CS.
r1csFile, err := os.Create("build/r1cs.bin")
if err != nil {
fmt.Println("Failed to create file:", err)
return
}
defer r1csFile.Close()

_, err = build.r1cs.WriteTo(r1csFile)
if err != nil {
fmt.Println("Failed to write data:", err)
return
}

// Create the proving key file.
pkFile, err := os.Create("build/pkey.bin")
if err != nil {
fmt.Println("Failed to create file:", err)
return
}
defer pkFile.Close()

// Write proving key.
_, err = build.pk.WriteTo(pkFile)
if err != nil {
fmt.Println("Failed to write data:", err)
return
}

// Write verification key.
vkFile, err := os.Create("build/vkey.bin")
if err != nil {
fmt.Println("Failed to create file:", err)
return
}
defer vkFile.Close()

_, err = build.vk.WriteTo(vkFile)
if err != nil {
fmt.Println("Failed to write data:", err)
return
}

// Write verifier smart contract into a file.
verifierFile, err := os.Create("build/FunctionVerifier.sol")
if err != nil {
fmt.Println("Failed to create file:", err)
return
}
defer verifierFile.Close()

svk := &SuccinctVerifyingKey{VerifyingKey: build.vk}
err = svk.ExportIFunctionVerifierSolidity(verifierFile)
if err != nil {
fmt.Println("Failed to export solidity verifier:", err)
return
}

}

// ImportCircuitBuild imports the R1CS, proving key, and verifying key from files.
func ImportCircuitBuild() (*CircuitBuild, error) {
r1cs := groth16.NewCS(ecc.BN254)

// Read the proving key file.
pkFile, err := os.Open("build/pkey.bin")
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
defer pkFile.Close()

// Deserialize the proving key.
pk := groth16.NewProvingKey(ecc.BN254)
_, err = pk.ReadFrom(pkFile)
if err != nil {
return nil, fmt.Errorf("failed to read data: %w", err)
}

vkFile, err := os.Open("build/vkey.bin")
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
defer vkFile.Close()

// Deserialize the verifying key.
vk := groth16.NewVerifyingKey(ecc.BN254)
_, err = vk.ReadFrom(vkFile)
if err != nil {
return nil, fmt.Errorf("failed to read data: %w", err)
}

// Read the R1CS file.
r1csFile, err := os.Open("build/r1cs.bin")
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
defer r1csFile.Close()

// Deserialize the R1CS.
_, err = r1cs.ReadFrom(r1csFile)
if err != nil {
return nil, fmt.Errorf("failed to read data: %w", err)
}

return &CircuitBuild{
pk: pk,
vk: vk,
r1cs: r1cs,
}, nil
}
Loading

0 comments on commit ba79cd0

Please sign in to comment.