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.