Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 5 additions & 3 deletions std/compress/lzss/snark.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import (
// it is on the caller to ensure that the dictionary is correct; in particular it must consist of bytes. Decompress does not check this.
// it is recommended to pack the dictionary using compress.Pack and take a MiMC checksum of it.
// d will consist of bytes
// It returns the length of d as a frontend.Variable
// It returns the length of d as a frontend.Variable; if the decompressed stream doesn't fit in d, dLength will be "-1"
func Decompress(api frontend.API, c []frontend.Variable, cLength frontend.Variable, d, dict []frontend.Variable, level lzss.Level) (dLength frontend.Variable, err error) {

// size-related "constants"
Expand Down Expand Up @@ -62,7 +62,7 @@ func Decompress(api frontend.API, c []frontend.Variable, cLength frontend.Variab
copyLen := frontend.Variable(0) // remaining length of the current copy
copyLen01 := frontend.Variable(1)
eof := frontend.Variable(0)
dLength = 0
dLength = -1 // if the following loop ends before hitting eof, we will get the "error" value -1 for dLength

for outI := range d {

Expand Down Expand Up @@ -114,7 +114,9 @@ func Decompress(api frontend.API, c []frontend.Variable, cLength frontend.Variab

eofNow := rangeChecker.IsLessThan(byteNbWords, api.Sub(cLength, inI)) // less than a byte left; meaning we are at the end of the input

dLength = api.Add(dLength, api.Mul(api.Sub(eofNow, eof), outI+1)) // if eof, don't advance dLength
// if eof, don't advance dLength
// if eof was JUST hit, dLength += outI + 2; so dLength = -1 + outI + 2 = outI + 1 which is the current output length
dLength = api.Add(dLength, api.Mul(api.Sub(eofNow, eof), outI+2))
eof = eofNow

}
Expand Down
41 changes: 41 additions & 0 deletions std/compress/lzss/snark_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,31 @@ func Test3c2943withHeader(t *testing.T) {
test.NewAssert(t).CheckCircuit(circuit, test.WithValidAssignment(assignment), test.WithBackends(backend.PLONK), test.WithCurves(ecc.BLS12_377))
}

func TestOutBufTooShort(t *testing.T) {
const truncationAmount = 3
d := []byte{1, 2, 3, 4, 5, 6, 7, 8, 9}
compressor, err := lzss.NewCompressor(nil, lzss.BestCompression)
require.NoError(t, err)
c, err := compressor.Compress(d)
require.NoError(t, err)

circuit := decompressionLengthTestCircuit{
C: make([]frontend.Variable, len(c)+inputExtraBytes),
D: make([]frontend.Variable, len(d)-truncationAmount), // not enough room

}

assignment := decompressionLengthTestCircuit{
C: test_vector_utils.ToVariableSlice(append(c, make([]byte, inputExtraBytes)...)),
CLength: len(c),
D: test_vector_utils.ToVariableSlice(d[:len(d)-truncationAmount]),
ExpectedDLength: -1,
}

RegisterHints()
test.NewAssert(t).CheckCircuit(&circuit, test.WithValidAssignment(&assignment), test.WithCurves(ecc.BLS12_377))
}

// Fuzz test the decompression
func Fuzz(f *testing.F) { // TODO This is always skipped
f.Fuzz(func(t *testing.T, input, dict []byte) {
Expand Down Expand Up @@ -179,3 +204,19 @@ func getDictionary() []byte {
}
return d
}

type decompressionLengthTestCircuit struct {
C, D []frontend.Variable
CLength frontend.Variable
ExpectedDLength frontend.Variable
}

func (c *decompressionLengthTestCircuit) Define(api frontend.API) error {
dict := test_vector_utils.ToVariableSlice(lzss.AugmentDict(nil))
if dLength, err := Decompress(api, c.C, c.CLength, c.D, dict, lzss.BestCompression); err != nil {
return err
} else {
api.AssertIsEqual(dLength, c.ExpectedDLength)
return nil
}
}