github.com/consensys/gnark@v0.11.0/backend/witness/witness_test.go (about)

     1  package witness_test
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"reflect"
     8  	"testing"
     9  
    10  	"github.com/consensys/gnark-crypto/ecc"
    11  	"github.com/consensys/gnark-crypto/ecc/bn254/fr"
    12  	"github.com/consensys/gnark/backend/witness"
    13  	"github.com/consensys/gnark/frontend"
    14  	"github.com/consensys/gnark/io"
    15  	"github.com/stretchr/testify/require"
    16  )
    17  
    18  type circuit struct {
    19  	// tagging a variable is optional
    20  	// default uses variable name and secret visibility.
    21  	X frontend.Variable `gnark:",public"`
    22  	Y frontend.Variable `gnark:",public"`
    23  
    24  	E frontend.Variable
    25  }
    26  
    27  func (c *circuit) Define(frontend.API) error {
    28  	return nil
    29  }
    30  
    31  func ExampleWitness() {
    32  	// Witnesses can be created directly by "walking" through an assignment (circuit structure)
    33  	// simple assignment
    34  	assignment := &circuit{
    35  		X: 42,
    36  		Y: 8000,
    37  		E: 1,
    38  	}
    39  
    40  	w, _ := frontend.NewWitness(assignment, ecc.BN254.ScalarField())
    41  
    42  	// Binary [de]serialization
    43  	data, _ := w.MarshalBinary()
    44  
    45  	reconstructed, _ := witness.New(ecc.BN254.ScalarField())
    46  	reconstructed.UnmarshalBinary(data)
    47  
    48  	// For pretty printing, we can do JSON conversions; they are not efficient and don't handle
    49  	// complex circuit structures well.
    50  
    51  	// first get the circuit expected schema
    52  	schema, _ := frontend.NewSchema(assignment)
    53  	ret, _ := reconstructed.ToJSON(schema)
    54  
    55  	var b bytes.Buffer
    56  	json.Indent(&b, ret, "", "\t")
    57  	fmt.Println(b.String())
    58  	// Output:
    59  	// {
    60  	//	"X": 42,
    61  	//	"Y": 8000,
    62  	//	"E": 1
    63  	// }
    64  
    65  }
    66  
    67  func TestMarshalPublic(t *testing.T) {
    68  	assert := require.New(t)
    69  
    70  	var assignment circuit
    71  	assignment.X = new(fr.Element).SetInt64(42)
    72  	assignment.Y = new(fr.Element).SetInt64(8000)
    73  
    74  	roundTripMarshal(assert, assignment, true)
    75  	roundTripMarshalJSON(assert, assignment, true)
    76  }
    77  
    78  func TestMarshal(t *testing.T) {
    79  	assert := require.New(t)
    80  
    81  	var assignment circuit
    82  	assignment.X = new(fr.Element).SetInt64(42)
    83  	assignment.Y = new(fr.Element).SetInt64(8000)
    84  	assignment.E = new(fr.Element).SetInt64(1)
    85  
    86  	roundTripMarshal(assert, assignment, false)
    87  	roundTripMarshalJSON(assert, assignment, false)
    88  }
    89  
    90  func TestPublic(t *testing.T) {
    91  	assert := require.New(t)
    92  
    93  	var assignment circuit
    94  	assignment.X = new(fr.Element).SetInt64(42)
    95  	assignment.Y = new(fr.Element).SetInt64(8000)
    96  	assignment.E = new(fr.Element).SetInt64(1)
    97  
    98  	w, err := frontend.NewWitness(&assignment, ecc.BN254.ScalarField())
    99  	assert.NoError(err)
   100  
   101  	publicW, err := w.Public()
   102  	assert.NoError(err)
   103  
   104  	wt := publicW.Vector().(fr.Vector)
   105  
   106  	assert.Equal(3, len(w.Vector().(fr.Vector)))
   107  	assert.Equal(2, len(wt))
   108  
   109  	assert.Equal("42", wt[0].String())
   110  	assert.Equal("8000", wt[1].String())
   111  }
   112  
   113  func roundTripMarshal(assert *require.Assertions, assignment circuit, publicOnly bool) {
   114  	var opts []frontend.WitnessOption
   115  	if publicOnly {
   116  		opts = append(opts, frontend.PublicOnly())
   117  	}
   118  	w, err := frontend.NewWitness(&assignment, ecc.BN254.ScalarField(), opts...)
   119  	assert.NoError(err)
   120  
   121  	assert.NoError(io.RoundTripCheck(w, func() interface{} {
   122  		rw, err := witness.New(ecc.BN254.ScalarField())
   123  		assert.NoError(err)
   124  		return rw
   125  	}))
   126  }
   127  
   128  func roundTripMarshalJSON(assert *require.Assertions, assignment circuit, publicOnly bool) {
   129  	// build the vector
   130  	var opts []frontend.WitnessOption
   131  	if publicOnly {
   132  		opts = append(opts, frontend.PublicOnly())
   133  	}
   134  	w, err := frontend.NewWitness(&assignment, ecc.BN254.ScalarField(), opts...)
   135  	assert.NoError(err)
   136  
   137  	s, err := frontend.NewSchema(&assignment)
   138  	assert.NoError(err)
   139  
   140  	// serialize the vector to JSON
   141  	data, err := w.ToJSON(s)
   142  	assert.NoError(err)
   143  
   144  	// re-read
   145  	rw, err := witness.New(ecc.BN254.ScalarField())
   146  	assert.NoError(err)
   147  	err = rw.FromJSON(s, data)
   148  	assert.NoError(err)
   149  
   150  	assert.True(reflect.DeepEqual(rw, w), "witness json round trip serialization")
   151  
   152  }
   153  
   154  type initableVariable struct {
   155  	Val []frontend.Variable
   156  }
   157  
   158  func (iv *initableVariable) GnarkInitHook() {
   159  	if iv.Val == nil {
   160  		iv.Val = []frontend.Variable{1, 2} // need to init value as are assigning to witness
   161  	}
   162  }
   163  
   164  type initableCircuit struct {
   165  	X [2]initableVariable
   166  	Y []initableVariable
   167  	Z initableVariable
   168  }
   169  
   170  func (c *initableCircuit) Define(api frontend.API) error {
   171  	panic("not called")
   172  }
   173  
   174  func TestVariableInitHook(t *testing.T) {
   175  	assert := require.New(t)
   176  
   177  	assignment := &initableCircuit{Y: make([]initableVariable, 2)}
   178  	w, err := frontend.NewWitness(assignment, ecc.BN254.ScalarField())
   179  	assert.NoError(err)
   180  	fw, ok := w.Vector().(fr.Vector)
   181  	assert.True(ok)
   182  	assert.Len(fw, 10, "invalid length")
   183  }