Assessment reports>Brevis>Discussion>Unallocated inputs slots not constrained

Unallocated inputs slots not constrained

The host circuit holds a slice of circuit variables Input.InputCommitments for commitments to input data (receipt, storage, or transaction data). This is instantiated in with the defaultCircuitInput function of sdk/circuit_input.go with a fixed length NumMaxDataPoints and default value 0 for all entries. A number of entries corresponding to the allocated number of receipts, storage, and transactions is assigned to with BrevisApp.assignInputCommitment in sdk/app.go.

These entries corresponding to allocated receipts, storage, and transactions are constrained to be the commitments of the corresponding input data used in the circuit. The entire list of input commitments is used as leaves of a Merkle tree whose root hash is exposed as a public input to the circuit.

Except their involvement in the Merkle root-hash calculation, the Input.InputCommitments entries beyond the allocated ones are not constrained, however. This does not appear to be a problem in the current circuits.

The lack of constraints means that it is possible to assign arbitrary commitment hashes there. Like this, it may be possible to add additional input data entries beyond the maximum the circuit declared. These entries still need to be legitimate, however, because they take part in the root hash that is ultimately checked by the Brevis backend circuits.

Additionally, application circuits using the sdk as intended should not access these extra data commitments, and there will be no corresponding unpacked data for these entries in the circuit.

For defense in depth, we recommend to nevertheless consider constraining these unused data commitments to be zero, which would take only a small number of constraints.

Alternatively, it may be possible to use constants for these entries instead of circuit variables, in which case no additional constraints would be needed, and witnesses could be saved as well. The way to do this should be to change the defaultCircuitInput function in sdk/circuit_input.go to instantiate the inputCommits slice with the total allocated length rather than the number of leaves for the eventual Merkle tree:

func defaultCircuitInput(maxReceipts, maxStorage, maxTxs int) CircuitInput {
+   numTotalAllocated := maxReceipts + maxStorage + maxTxs
- 	var inputCommits = make([]frontend.Variable, NumMaxDataPoints)
+ 	var inputCommits = make([]frontend.Variable, numTotalAllocated)
- 	for i := 0; i < NumMaxDataPoints; i++ {
+ 	for i := 0; i < numTotalAllocated; i++ {
		inputCommits[i] = 0
	}
	return CircuitInput{
		DataInput:            defaultDataInput(maxReceipts, maxStorage, maxTxs),
		InputCommitmentsRoot: 0,
		InputCommitments:     inputCommits,
		TogglesCommitment:    0,
		OutputCommitment:     OutputCommitment{0, 0},
	}
}

This will require additional corresponding changes in other parts of the code. In particular, in HostCircuit.Define, the line

inputCommitmentRoot, err := calMerkelRoot(gapi, c.Input.InputCommitments)

would need to be modified. The second argument to calMerkelRoot would now need to consist not of c.Input.InputCommitments but of c.Input.InputCommitments padded with the constant 0 up to length NumMaxDataPoints. There may also be other spots in the codebase requiring modifications.

Zellic © 2025Back to top ↑