github.com/consensys/gnark@v0.11.0/test/commitments_test.go (about)

     1  package test
     2  
     3  import (
     4  	"fmt"
     5  	"reflect"
     6  	"testing"
     7  
     8  	"github.com/consensys/gnark/backend"
     9  	groth16 "github.com/consensys/gnark/backend/groth16/bn254"
    10  	"github.com/consensys/gnark/backend/witness"
    11  	cs "github.com/consensys/gnark/constraint/bn254"
    12  	"github.com/consensys/gnark/frontend/cs/r1cs"
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"github.com/consensys/gnark-crypto/ecc"
    16  	"github.com/consensys/gnark/frontend"
    17  	"github.com/consensys/gnark/frontend/cs/scs"
    18  	"github.com/stretchr/testify/assert"
    19  )
    20  
    21  type noCommitmentCircuit struct {
    22  	X frontend.Variable
    23  }
    24  
    25  func (c *noCommitmentCircuit) Define(api frontend.API) error {
    26  	api.AssertIsEqual(c.X, 1)
    27  	api.AssertIsEqual(c.X, 1)
    28  	return nil
    29  }
    30  
    31  type commitmentCircuit struct {
    32  	Public []frontend.Variable `gnark:",public"`
    33  	X      []frontend.Variable
    34  }
    35  
    36  func (c *commitmentCircuit) Define(api frontend.API) error {
    37  
    38  	commitment, err := api.(frontend.Committer).Commit(c.X...)
    39  	if err != nil {
    40  		return err
    41  	}
    42  	sum := frontend.Variable(0)
    43  	for i, x := range c.X {
    44  		sum = api.Add(sum, api.Mul(x, i+1))
    45  	}
    46  	for _, p := range c.Public {
    47  		sum = api.Add(sum, p)
    48  	}
    49  	api.AssertIsDifferent(commitment, sum)
    50  	return nil
    51  }
    52  
    53  type committedConstantCircuit struct {
    54  	X frontend.Variable
    55  }
    56  
    57  func (c *committedConstantCircuit) Define(api frontend.API) error {
    58  	commitment, err := api.(frontend.Committer).Commit(1, c.X)
    59  	if err != nil {
    60  		return err
    61  	}
    62  	api.AssertIsDifferent(commitment, c.X)
    63  	return nil
    64  }
    65  
    66  type committedPublicCircuit struct {
    67  	X frontend.Variable `gnark:",public"`
    68  }
    69  
    70  func (c *committedPublicCircuit) Define(api frontend.API) error {
    71  	commitment, err := api.(frontend.Committer).Commit(c.X)
    72  	if err != nil {
    73  		return err
    74  	}
    75  	api.AssertIsDifferent(commitment, c.X)
    76  	return nil
    77  }
    78  
    79  type independentCommitsCircuit struct {
    80  	X []frontend.Variable
    81  }
    82  
    83  func (c *independentCommitsCircuit) Define(api frontend.API) error {
    84  	committer := api.(frontend.Committer)
    85  	for i := range c.X {
    86  		if ch, err := committer.Commit(c.X[i]); err != nil {
    87  			return err
    88  		} else {
    89  			api.AssertIsDifferent(ch, c.X[i])
    90  		}
    91  	}
    92  	return nil
    93  }
    94  
    95  type twoCommitCircuit struct {
    96  	X []frontend.Variable
    97  	Y frontend.Variable
    98  }
    99  
   100  func (c *twoCommitCircuit) Define(api frontend.API) error {
   101  	c0, err := api.(frontend.Committer).Commit(c.X...)
   102  	if err != nil {
   103  		return err
   104  	}
   105  	var c1 frontend.Variable
   106  	if c1, err = api.(frontend.Committer).Commit(c0, c.Y); err != nil {
   107  		return err
   108  	}
   109  	api.AssertIsDifferent(c1, c.Y)
   110  	return nil
   111  }
   112  
   113  type doubleCommitCircuit struct {
   114  	X, Y frontend.Variable
   115  }
   116  
   117  func (c *doubleCommitCircuit) Define(api frontend.API) error {
   118  	var c0, c1 frontend.Variable
   119  	var err error
   120  	if c0, err = api.(frontend.Committer).Commit(c.X); err != nil {
   121  		return err
   122  	}
   123  	if c1, err = api.(frontend.Committer).Commit(c.X, c.Y); err != nil {
   124  		return err
   125  	}
   126  	api.AssertIsDifferent(c0, c1)
   127  	return nil
   128  }
   129  
   130  func TestHollow(t *testing.T) {
   131  
   132  	run := func(c, expected frontend.Circuit) func(t *testing.T) {
   133  		return func(t *testing.T) {
   134  			seen := hollow(c)
   135  			assert.Equal(t, expected, seen)
   136  		}
   137  	}
   138  
   139  	assignments := []frontend.Circuit{
   140  		&committedConstantCircuit{1},
   141  		&commitmentCircuit{X: []frontend.Variable{1}, Public: []frontend.Variable{}},
   142  	}
   143  
   144  	expected := []frontend.Circuit{
   145  		&committedConstantCircuit{nil},
   146  		&commitmentCircuit{X: []frontend.Variable{nil}, Public: []frontend.Variable{}},
   147  	}
   148  
   149  	for i := range assignments {
   150  		t.Run(removePackageName(reflect.TypeOf(assignments[i]).String()), run(assignments[i], expected[i]))
   151  	}
   152  }
   153  
   154  type commitUniquenessCircuit struct {
   155  	X []frontend.Variable
   156  }
   157  
   158  func (c *commitUniquenessCircuit) Define(api frontend.API) error {
   159  	var err error
   160  
   161  	ch := make([]frontend.Variable, len(c.X))
   162  	for i := range c.X {
   163  		if ch[i], err = api.(frontend.Committer).Commit(c.X[i]); err != nil {
   164  			return err
   165  		}
   166  		for j := 0; j < i; j++ {
   167  			api.AssertIsDifferent(ch[i], ch[j])
   168  		}
   169  	}
   170  	return nil
   171  }
   172  
   173  func TestCommitUniquenessZerosScs(t *testing.T) { // TODO @Tabaie Randomize Groth16 commitments for real
   174  
   175  	w, err := frontend.NewWitness(&commitUniquenessCircuit{[]frontend.Variable{0, 0}}, ecc.BN254.ScalarField())
   176  	assert.NoError(t, err)
   177  
   178  	ccs, err := frontend.Compile(ecc.BN254.ScalarField(), scs.NewBuilder, &commitUniquenessCircuit{[]frontend.Variable{nil, nil}})
   179  	assert.NoError(t, err)
   180  
   181  	_, err = ccs.Solve(w)
   182  	assert.NoError(t, err)
   183  }
   184  
   185  var commitmentTestCircuits []frontend.Circuit
   186  
   187  func init() {
   188  	commitmentTestCircuits = []frontend.Circuit{
   189  		&noCommitmentCircuit{1},
   190  		&commitmentCircuit{X: []frontend.Variable{1}, Public: []frontend.Variable{}},                          // single commitment
   191  		&commitmentCircuit{X: []frontend.Variable{1, 2}, Public: []frontend.Variable{}},                       // two commitments
   192  		&commitmentCircuit{X: []frontend.Variable{1, 2, 3, 4, 5}, Public: []frontend.Variable{}},              // five commitments
   193  		&commitmentCircuit{X: []frontend.Variable{0}, Public: []frontend.Variable{1}},                         // single commitment single public
   194  		&commitmentCircuit{X: []frontend.Variable{0, 1, 2, 3, 4}, Public: []frontend.Variable{1, 2, 3, 4, 5}}, // five commitments five public
   195  		&committedConstantCircuit{1},                             // single committed constant
   196  		&committedPublicCircuit{1},                               // single committed public
   197  		&independentCommitsCircuit{X: []frontend.Variable{1, 1}}, // two independent commitments
   198  		&twoCommitCircuit{X: []frontend.Variable{1, 2}, Y: 3},    // two commitments, second depending on first
   199  		&doubleCommitCircuit{X: 1, Y: 2},                         // double committing to the same variable
   200  	}
   201  }
   202  
   203  func TestCommitment(t *testing.T) {
   204  	t.Parallel()
   205  	assert := NewAssert(t)
   206  
   207  	for i, assignment := range commitmentTestCircuits {
   208  		assert.Run(func(assert *Assert) {
   209  			assert.CheckCircuit(hollow(assignment), WithValidAssignment(assignment), WithBackends(backend.GROTH16, backend.PLONK))
   210  		}, fmt.Sprintf("%d-%s", i, removePackageName(reflect.TypeOf(assignment).String())))
   211  	}
   212  }
   213  
   214  func TestCommitmentDummySetup(t *testing.T) {
   215  	t.Parallel()
   216  
   217  	run := func(assignment frontend.Circuit) func(t *testing.T) {
   218  		return func(t *testing.T) {
   219  			// just test the prover
   220  			_cs, err := frontend.Compile(ecc.BN254.ScalarField(), r1cs.NewBuilder, hollow(assignment))
   221  			require.NoError(t, err)
   222  			_r1cs := _cs.(*cs.R1CS)
   223  			var (
   224  				dPk, pk groth16.ProvingKey
   225  				vk      groth16.VerifyingKey
   226  				w       witness.Witness
   227  			)
   228  			require.NoError(t, groth16.Setup(_r1cs, &pk, &vk))
   229  			require.NoError(t, groth16.DummySetup(_r1cs, &dPk))
   230  
   231  			comparePkSizes(t, dPk, pk)
   232  
   233  			w, err = frontend.NewWitness(assignment, ecc.BN254.ScalarField())
   234  			require.NoError(t, err)
   235  			_, err = groth16.Prove(_r1cs, &pk, w)
   236  			require.NoError(t, err)
   237  		}
   238  	}
   239  
   240  	for _, assignment := range commitmentTestCircuits {
   241  		name := removePackageName(reflect.TypeOf(assignment).String())
   242  		if c, ok := assignment.(*commitmentCircuit); ok {
   243  			name += fmt.Sprintf(":%dprivate %dpublic", len(c.X), len(c.Public))
   244  		}
   245  		t.Run(name, run(assignment))
   246  	}
   247  }
   248  
   249  func comparePkSizes(t *testing.T, pk1, pk2 groth16.ProvingKey) {
   250  	// skipping the domain
   251  	require.Equal(t, len(pk1.G1.A), len(pk2.G1.A))
   252  	require.Equal(t, len(pk1.G1.B), len(pk2.G1.B))
   253  	require.Equal(t, len(pk1.G1.Z), len(pk2.G1.Z))
   254  	require.Equal(t, len(pk1.G1.K), len(pk2.G1.K))
   255  
   256  	require.Equal(t, len(pk1.G2.B), len(pk2.G2.B))
   257  
   258  	require.Equal(t, len(pk1.InfinityA), len(pk2.InfinityA))
   259  	require.Equal(t, len(pk1.InfinityB), len(pk2.InfinityB))
   260  	require.Equal(t, pk1.NbInfinityA, pk2.NbInfinityA)
   261  	require.Equal(t, pk1.NbInfinityB, pk2.NbInfinityB)
   262  
   263  	require.Equal(t, len(pk1.CommitmentKeys), len(pk2.CommitmentKeys)) // TODO @Tabaie Compare the commitment keys
   264  }