From 091a279ff2af02d41218b256bef527609ffd9ed7 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 5 Jul 2024 09:27:00 +0400 Subject: [PATCH 1/8] checksum: Drop redundant example test It teaches what a regular user should be freed from. Signed-off-by: Leonard Lyubich --- checksum/example_test.go | 35 ----------------------------------- 1 file changed, 35 deletions(-) diff --git a/checksum/example_test.go b/checksum/example_test.go index 5a18a484..f520ff93 100644 --- a/checksum/example_test.go +++ b/checksum/example_test.go @@ -1,14 +1,5 @@ package checksum -import ( - "bytes" - "crypto/sha256" - "fmt" - "math/rand" - - "github.com/nspcc-dev/neofs-api-go/v2/refs" -) - func ExampleCalculate() { payload := []byte{0, 1, 2, 3, 4, 5, 6} var checksum Checksum @@ -19,29 +10,3 @@ func ExampleCalculate() { // checksum contains TZ hash of the payload Calculate(&checksum, TZ, payload) } - -// Instances can be also used to process NeoFS API V2 protocol messages with [https://github.com/nspcc-dev/neofs-api] package. -func ExampleChecksum_marshalling() { - var ( - csRaw [sha256.Size]byte - csV2 refs.Checksum - cs Checksum - ) - - //nolint:staticcheck - rand.Read(csRaw[:]) - cs.SetSHA256(csRaw) - - // On the client side. - - cs.WriteToV2(&csV2) - - fmt.Println(bytes.Equal(cs.Value(), csV2.GetSum())) - // Example output: true - - // *send message* - - // On the server side. - - _ = cs.ReadFromV2(csV2) -} From d35587d6b65236f94997ea673b1c69ca380eec23 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 5 Jul 2024 10:17:09 +0400 Subject: [PATCH 2/8] checksum: Provide constructors for various types/scenarios `Checksum` type instances are very lightweight, so setters do not provide much benefit while forcing user to explicitly declare a new var. It was also no convenient way to create an instance from stdlib `hash.Hash`. From now `Checksum` can be instantiated with less code using set of new constructors. Old functions are marked as deprecated. Refs #483. Signed-off-by: Leonard Lyubich --- checksum/checksum.go | 82 +++++++++++++++++++++++++++++---------- checksum/checksum_test.go | 45 ++++++++++++++------- checksum/example_test.go | 54 ++++++++++++++++++++++---- checksum/test/generate.go | 7 +--- object/fmt.go | 5 +-- object/search_test.go | 10 +---- object/slicer/slicer.go | 8 +--- 7 files changed, 146 insertions(+), 65 deletions(-) diff --git a/checksum/checksum.go b/checksum/checksum.go index 3f55c1c7..0c5d182c 100644 --- a/checksum/checksum.go +++ b/checksum/checksum.go @@ -5,6 +5,7 @@ import ( "encoding/hex" "errors" "fmt" + "hash" "github.com/nspcc-dev/neofs-api-go/v2/refs" "github.com/nspcc-dev/tzhash/tz" @@ -15,7 +16,7 @@ import ( // Checksum is mutually compatible with github.com/nspcc-dev/neofs-api-go/v2/refs.Checksum // message. See ReadFromV2 / WriteToV2 methods. // -// Instances can be created using built-in var declaration. +// Instances must be created using one of the constructors. // // Note that direct typecast is not safe and may result in loss of compatibility: // @@ -37,6 +38,56 @@ const ( TZ ) +func typeToProto(t Type) refs.ChecksumType { + switch t { + default: + return refs.ChecksumType(t) + case SHA256: + return refs.SHA256 + case TZ: + return refs.TillichZemor + } +} + +// New constructs new Checksum instance. It is the caller's responsibility to +// ensure that the hash matches the type. +func New(typ Type, hsh []byte) Checksum { + var res refs.Checksum + res.SetType(typeToProto(typ)) + res.SetSum(hsh) + return Checksum(res) +} + +// NewSHA256 constructs new Checksum from SHA-256 hash. +func NewSHA256(h [sha256.Size]byte) Checksum { + return New(SHA256, h[:]) +} + +// NewTillichZemor constructs new Checksum from Tillich-Zémor homomorphic hash. +func NewTillichZemor(h [tz.Size]byte) Checksum { + return New(TZ, h[:]) +} + +// NewFromHash constructs new Checksum of specified type from accumulated +// hash.Hash. It is the caller's responsibility to ensure that the hash matches +// the type. +func NewFromHash(t Type, h hash.Hash) Checksum { + return New(t, h.Sum(nil)) +} + +// NewFromData calculates Checksum of given type for specified data. The typ +// must be an enum value declared in current package. +func NewFromData(typ Type, data []byte) (Checksum, error) { + switch typ { + default: + return Checksum{}, fmt.Errorf("unsupported checksum type %d", typ) + case SHA256: + return NewSHA256(sha256.Sum256(data)), nil + case TZ: + return NewTillichZemor(tz.Sum(data)), nil + } +} + // ReadFromV2 reads Checksum from the refs.Checksum message. Checks if the // message conforms to NeoFS API V2 protocol. // @@ -69,7 +120,7 @@ func (c Checksum) WriteToV2(m *refs.Checksum) { // // Zero Checksum has Unknown checksum type. // -// See also SetTillichZemor and SetSHA256. +// See also [NewTillichZemor], [NewSHA256]. func (c Checksum) Type() Type { v2 := (refs.Checksum)(c) switch v2.GetType() { @@ -90,7 +141,7 @@ func (c Checksum) Type() Type { // The value returned shares memory with the structure itself, so changing it can lead to data corruption. // Make a copy if you need to change it. // -// See also SetTillichZemor and SetSHA256. +// See also [NewTillichZemor], [NewSHA256]. func (c Checksum) Value() []byte { v2 := (refs.Checksum)(c) return v2.GetSum() @@ -99,12 +150,8 @@ func (c Checksum) Value() []byte { // SetSHA256 sets checksum to SHA256 hash. // // See also Calculate. -func (c *Checksum) SetSHA256(v [sha256.Size]byte) { - v2 := (*refs.Checksum)(c) - - v2.SetType(refs.SHA256) - v2.SetSum(v[:]) -} +// Deprecated: use [NewSHA256] instead. +func (c *Checksum) SetSHA256(v [sha256.Size]byte) { *c = NewSHA256(v) } // Calculate calculates checksum and sets it // to the passed checksum. Checksum must not be nil. @@ -116,25 +163,18 @@ func (c *Checksum) SetSHA256(v [sha256.Size]byte) { // Does not mutate the passed value. // // See also SetSHA256, SetTillichZemor. +// Deprecated: use [NewFromData] instead. func Calculate(c *Checksum, t Type, v []byte) { - switch t { - case SHA256: - c.SetSHA256(sha256.Sum256(v)) - case TZ: - c.SetTillichZemor(tz.Sum(v)) - default: + if cs, err := NewFromData(t, v); err == nil { + *c = cs } } // SetTillichZemor sets checksum to Tillich-Zémor hash. // // See also Calculate. -func (c *Checksum) SetTillichZemor(v [tz.Size]byte) { - v2 := (*refs.Checksum)(c) - - v2.SetType(refs.TillichZemor) - v2.SetSum(v[:]) -} +// Deprecated: use [NewTillichZemor] instead. +func (c *Checksum) SetTillichZemor(v [tz.Size]byte) { *c = NewTillichZemor(v) } // String implements fmt.Stringer. // diff --git a/checksum/checksum_test.go b/checksum/checksum_test.go index f9d22c95..ed75c69c 100644 --- a/checksum/checksum_test.go +++ b/checksum/checksum_test.go @@ -11,12 +11,12 @@ import ( ) func TestChecksum(t *testing.T) { - var c Checksum + data := make([]byte, 32) + //nolint:staticcheck + rand.Read(data) - cSHA256 := [sha256.Size]byte{} - _, _ = rand.Read(cSHA256[:]) - - c.SetSHA256(cSHA256) + cSHA256 := sha256.Sum256(data) + c := NewSHA256(cSHA256) require.Equal(t, SHA256, c.Type()) require.Equal(t, cSHA256[:], c.Value()) @@ -27,10 +27,8 @@ func TestChecksum(t *testing.T) { require.Equal(t, refs.SHA256, cV2.GetType()) require.Equal(t, cSHA256[:], cV2.GetSum()) - cTZ := [tz.Size]byte{} - _, _ = rand.Read(cSHA256[:]) - - c.SetTillichZemor(cTZ) + cTZ := tz.Sum(data) + c = NewTillichZemor(cTZ) require.Equal(t, TZ, c.Type()) require.Equal(t, cTZ[:], c.Value()) @@ -58,23 +56,44 @@ func TestNewChecksum(t *testing.T) { }) } -func TestCalculation(t *testing.T) { - var c Checksum +func TestNewFromData(t *testing.T) { + _, err := NewFromData(0, nil) + require.EqualError(t, err, "unsupported checksum type 0") + var cs Checksum + Calculate(&cs, 0, nil) + require.Zero(t, cs) + _, err = NewFromData(TZ+1, nil) + require.EqualError(t, err, "unsupported checksum type 3") + Calculate(&cs, TZ+1, nil) + require.Zero(t, cs) + payload := []byte{0, 1, 2, 3, 4, 5} t.Run("SHA256", func(t *testing.T) { orig := sha256.Sum256(payload) - Calculate(&c, SHA256, payload) + c, err := NewFromData(SHA256, payload) + require.NoError(t, err) + require.Equal(t, orig[:], c.Value()) + require.Equal(t, SHA256, c.Type()) + c = Checksum{} + Calculate(&c, SHA256, payload) require.Equal(t, orig[:], c.Value()) + require.Equal(t, SHA256, c.Type()) }) t.Run("TZ", func(t *testing.T) { orig := tz.Sum(payload) - Calculate(&c, TZ, payload) + c, err := NewFromData(TZ, payload) + require.NoError(t, err) + require.Equal(t, orig[:], c.Value()) + require.Equal(t, TZ, c.Type()) + c = Checksum{} + Calculate(&c, TZ, payload) require.Equal(t, orig[:], c.Value()) + require.Equal(t, TZ, c.Type()) }) } diff --git a/checksum/example_test.go b/checksum/example_test.go index f520ff93..afa1b269 100644 --- a/checksum/example_test.go +++ b/checksum/example_test.go @@ -1,12 +1,52 @@ package checksum -func ExampleCalculate() { - payload := []byte{0, 1, 2, 3, 4, 5, 6} - var checksum Checksum +import ( + "crypto/sha256" + "fmt" + "hash" - // checksum contains SHA256 hash of the payload - Calculate(&checksum, SHA256, payload) + "github.com/nspcc-dev/tzhash/tz" +) - // checksum contains TZ hash of the payload - Calculate(&checksum, TZ, payload) +func ExampleNewSHA256() { + data := []byte("Hello, world!") + c := NewSHA256(sha256.Sum256(data)) + fmt.Println(c) +} + +func ExampleNewTillichZemor() { + data := []byte("Hello, world!") + c := NewTillichZemor(tz.Sum(data)) + fmt.Println(c) +} + +func ExampleNewFromHash() { + data := []byte("Hello, world!") + for _, tc := range []struct { + typ Type + newHash func() hash.Hash + }{ + {SHA256, sha256.New}, + {TZ, tz.New}, + } { + h := tc.newHash() + h.Write(data) + cs := NewFromHash(tc.typ, h) + fmt.Println(cs) + } +} + +func ExampleNewFromData() { + data := []byte("Hello, world!") + for _, typ := range []Type{ + SHA256, + TZ, + } { + cs, err := NewFromData(typ, data) + if err != nil { + fmt.Printf("calculation for %v failed: %v\n", typ, err) + return + } + fmt.Println(cs) + } } diff --git a/checksum/test/generate.go b/checksum/test/generate.go index 7a6f9067..13296196 100644 --- a/checksum/test/generate.go +++ b/checksum/test/generate.go @@ -12,10 +12,5 @@ func Checksum() checksum.Checksum { var cs [sha256.Size]byte //nolint:staticcheck rand.Read(cs[:]) - - var x checksum.Checksum - - x.SetSHA256(cs) - - return x + return checksum.NewSHA256(cs) } diff --git a/object/fmt.go b/object/fmt.go index cccfe8b2..10c37c32 100644 --- a/object/fmt.go +++ b/object/fmt.go @@ -25,10 +25,7 @@ var ( // CalculatePayloadChecksum calculates and returns checksum of // object payload bytes. func CalculatePayloadChecksum(payload []byte) checksum.Checksum { - var res checksum.Checksum - checksum.Calculate(&res, checksum.SHA256, payload) - - return res + return checksum.NewSHA256(sha256.Sum256(payload)) } // CalculateAndSetPayloadChecksum calculates checksum of current diff --git a/object/search_test.go b/object/search_test.go index 39ea2183..4c6168c6 100644 --- a/object/search_test.go +++ b/object/search_test.go @@ -340,10 +340,7 @@ func TestSearchFilters_AddPayloadHashFilter(t *testing.T) { func ExampleSearchFilters_AddPayloadHashFilter() { hash, _ := hex.DecodeString("66842cfea090b1d906b52400fae49d86df078c0670f2bdd059ba289ebe24a498") - - var cs checksum.Checksum - cs.SetSHA256([sha256.Size]byte(hash)) - + cs := checksum.NewSHA256([sha256.Size]byte(hash)) fmt.Println(hex.EncodeToString(cs.Value())) // Output: 66842cfea090b1d906b52400fae49d86df078c0670f2bdd059ba289ebe24a498 } @@ -373,10 +370,7 @@ func TestSearchFilters_AddHomomorphicHashFilter(t *testing.T) { func ExampleSearchFilters_AddHomomorphicHashFilter() { hash, _ := hex.DecodeString("7e302ebb3937e810feb501965580c746048db99cebd095c3ce27022407408bf904dde8d9aa8085d2cf7202345341cc947fa9d722c6b6699760d307f653815d0c") - - var cs checksum.Checksum - cs.SetTillichZemor([tz.Size]byte(hash)) - + cs := checksum.NewTillichZemor([tz.Size]byte(hash)) fmt.Println(hex.EncodeToString(cs.Value())) // Output: 7e302ebb3937e810feb501965580c746048db99cebd095c3ce27022407408bf904dde8d9aa8085d2cf7202345341cc947fa9d722c6b6699760d307f653815d0c } diff --git a/object/slicer/slicer.go b/object/slicer/slicer.go index 44d54742..e7e9c583 100644 --- a/object/slicer/slicer.go +++ b/object/slicer/slicer.go @@ -598,14 +598,10 @@ func (x *PayloadWriter) _writeChild(ctx context.Context, meta dynamicObjectMetad } func flushObjectMetadata(signer neofscrypto.Signer, meta dynamicObjectMetadata, header *object.Object) (oid.ID, error) { - var cs checksum.Checksum - - cs.SetSHA256([sha256.Size]byte(meta.checksum.Sum(nil))) - header.SetPayloadChecksum(cs) + header.SetPayloadChecksum(checksum.NewFromHash(checksum.SHA256, meta.checksum)) if meta.homomorphicChecksum != nil { - cs.SetTillichZemor([tz.Size]byte(meta.homomorphicChecksum.Sum(nil))) - header.SetPayloadHomomorphicHash(cs) + header.SetPayloadHomomorphicHash(checksum.NewFromHash(checksum.TZ, meta.homomorphicChecksum)) } header.SetPayloadSize(meta.length) From 4b78f65c1159c3bd67a165dcd6ab342f5b23102d Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 5 Jul 2024 10:23:20 +0400 Subject: [PATCH 3/8] checksum/test: Add more randomness to randomizer Vary not only the value but also the type. Signed-off-by: Leonard Lyubich --- checksum/test/generate.go | 15 +++++++++++---- checksum/test/generate_test.go | 21 +++++++++++++++++++++ 2 files changed, 32 insertions(+), 4 deletions(-) create mode 100644 checksum/test/generate_test.go diff --git a/checksum/test/generate.go b/checksum/test/generate.go index 13296196..55dd02c1 100644 --- a/checksum/test/generate.go +++ b/checksum/test/generate.go @@ -1,16 +1,23 @@ package checksumtest import ( - "crypto/sha256" + "fmt" "math/rand" "github.com/nspcc-dev/neofs-sdk-go/checksum" ) +var allSupportedTypes = []checksum.Type{checksum.SHA256, checksum.TZ} + // Checksum returns random checksum.Checksum. func Checksum() checksum.Checksum { - var cs [sha256.Size]byte + data := make([]byte, 32) //nolint:staticcheck - rand.Read(cs[:]) - return checksum.NewSHA256(cs) + rand.Read(data) + i := rand.Int() % len(allSupportedTypes) + res, err := checksum.NewFromData(allSupportedTypes[i], data) + if err != nil { + panic(fmt.Errorf("unexpected NewFromData error: %w", err)) + } + return res } diff --git a/checksum/test/generate_test.go b/checksum/test/generate_test.go new file mode 100644 index 00000000..de6dc215 --- /dev/null +++ b/checksum/test/generate_test.go @@ -0,0 +1,21 @@ +package checksumtest_test + +import ( + "testing" + + "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-sdk-go/checksum" + checksumtest "github.com/nspcc-dev/neofs-sdk-go/checksum/test" + "github.com/stretchr/testify/require" +) + +func TestChecksum(t *testing.T) { + cs := checksumtest.Checksum() + require.NotEqual(t, cs, checksumtest.Checksum()) + + var m refs.Checksum + cs.WriteToV2(&m) + var cs2 checksum.Checksum + require.NoError(t, cs2.ReadFromV2(m)) + require.Equal(t, cs, cs2) +} From 71381ffc7f7499f5a8775534fa55b30cf6abcff3 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 5 Jul 2024 10:53:40 +0400 Subject: [PATCH 4/8] checksum: Do not lose type value during conversion Previously, `Checksum.Type` method returned any unsupported value as zero. This led to a kind of value loss and the impossibility of forward compatibility - although at the type level it is possible. From now method returns underlying numeric value for unknown enum values. The stringer now also does not fallback. `Unknown` constant is no longer needed and deprecated. Signed-off-by: Leonard Lyubich --- checksum/checksum.go | 18 ++++-------------- checksum/checksum_test.go | 2 +- 2 files changed, 5 insertions(+), 15 deletions(-) diff --git a/checksum/checksum.go b/checksum/checksum.go index 0c5d182c..bfe3e772 100644 --- a/checksum/checksum.go +++ b/checksum/checksum.go @@ -29,6 +29,7 @@ type Type uint8 const ( // Unknown is an undefined checksum type. + // Deprecated: use 0 instead. Unknown Type = iota // SHA256 is a SHA256 checksum type. @@ -123,13 +124,13 @@ func (c Checksum) WriteToV2(m *refs.Checksum) { // See also [NewTillichZemor], [NewSHA256]. func (c Checksum) Type() Type { v2 := (refs.Checksum)(c) - switch v2.GetType() { + switch typ := v2.GetType(); typ { case refs.SHA256: return SHA256 case refs.TillichZemor: return TZ default: - return Unknown + return Type(typ) } } @@ -190,16 +191,5 @@ func (c Checksum) String() string { // String is designed to be human-readable, and its format MAY differ between // SDK versions. func (m Type) String() string { - var m2 refs.ChecksumType - - switch m { - default: - m2 = refs.UnknownChecksum - case TZ: - m2 = refs.TillichZemor - case SHA256: - m2 = refs.SHA256 - } - - return m2.String() + return typeToProto(m).String() } diff --git a/checksum/checksum_test.go b/checksum/checksum_test.go index ed75c69c..7d5268a3 100644 --- a/checksum/checksum_test.go +++ b/checksum/checksum_test.go @@ -44,7 +44,7 @@ func TestNewChecksum(t *testing.T) { var chs Checksum // check initial values - require.Equal(t, Unknown, chs.Type()) + require.Zero(t, chs.Type()) require.Nil(t, chs.Value()) // convert to v2 message From e829c90bb999cc0d4dddfe0f676c4689d6f796bc Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 5 Jul 2024 10:59:04 +0400 Subject: [PATCH 5/8] checksum: Increase forward compatibility for new checksum types Previously, protocol conversion method `Checksum.ReadFromV2` failed on any unsupported enum value. This didn't make much sense since type getter is provided, so callers can decide for themselves. From now method ignores checksum type. Signed-off-by: Leonard Lyubich --- checksum/checksum.go | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/checksum/checksum.go b/checksum/checksum.go index bfe3e772..467edf38 100644 --- a/checksum/checksum.go +++ b/checksum/checksum.go @@ -25,7 +25,7 @@ type Checksum refs.Checksum // Type represents the enumeration // of checksum types. -type Type uint8 +type Type uint32 const ( // Unknown is an undefined checksum type. @@ -98,12 +98,6 @@ func (c *Checksum) ReadFromV2(m refs.Checksum) error { return errors.New("missing value") } - switch m.GetType() { - default: - return fmt.Errorf("unsupported type %v", m.GetType()) - case refs.SHA256, refs.TillichZemor: - } - *c = Checksum(m) return nil From 590660ca5bf6d770ab46ee5291e7f1815107a6da Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 5 Jul 2024 11:45:34 +0400 Subject: [PATCH 6/8] checksum: Increase test coverage Signed-off-by: Leonard Lyubich --- checksum/checksum_test.go | 257 +++++++++++++++++++++++++++++--------- 1 file changed, 197 insertions(+), 60 deletions(-) diff --git a/checksum/checksum_test.go b/checksum/checksum_test.go index 7d5268a3..dbacc79e 100644 --- a/checksum/checksum_test.go +++ b/checksum/checksum_test.go @@ -1,99 +1,236 @@ -package checksum +package checksum_test import ( - "crypto/rand" "crypto/sha256" + "hash" + "hash/adler32" + "math/rand" "testing" "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/neofs-sdk-go/checksum" "github.com/nspcc-dev/tzhash/tz" "github.com/stretchr/testify/require" ) -func TestChecksum(t *testing.T) { - data := make([]byte, 32) - //nolint:staticcheck - rand.Read(data) - - cSHA256 := sha256.Sum256(data) - c := NewSHA256(cSHA256) - - require.Equal(t, SHA256, c.Type()) - require.Equal(t, cSHA256[:], c.Value()) +func TestType_String(t *testing.T) { + for _, tc := range []struct { + typ checksum.Type + exp string + }{ + {0, "CHECKSUM_TYPE_UNSPECIFIED"}, + {checksum.SHA256, "SHA256"}, + {checksum.TZ, "TZ"}, + {3, "3"}, + } { + require.Equal(t, tc.exp, tc.typ.String()) + } +} - var cV2 refs.Checksum - c.WriteToV2(&cV2) +func TestChecksum_String(t *testing.T) { + data := []byte("Hello, world!") + for _, tc := range []struct { + cs checksum.Checksum + exp string + }{ + {checksum.New(0, data), "CHECKSUM_TYPE_UNSPECIFIED:48656c6c6f2c20776f726c6421"}, + {checksum.New(127, data), "127:48656c6c6f2c20776f726c6421"}, + {checksum.NewSHA256(sha256.Sum256(data)), "SHA256:315f5bdb76d078c43b8ac0064e4a0164612b1fce77c869345bfc94c75894edd3"}, + {checksum.NewTillichZemor(tz.Sum(data)), "TZ:0000014249f10795c0240eddca8a6ebf000001c9c4dc98b017fd92ad62979c8c0000008d94cd98a457b983e937838dcd000000dbc8689e75c7dd8925ad0df727"}, + } { + require.Equal(t, tc.exp, tc.cs.String()) + } +} - require.Equal(t, refs.SHA256, cV2.GetType()) - require.Equal(t, cSHA256[:], cV2.GetSum()) +func TestChecksumDecodingFailures(t *testing.T) { + t.Run("api", func(t *testing.T) { + for _, tc := range []struct { + name, err string + corrupt func(*refs.Checksum) + }{ + {name: "value/nil", err: "missing value", corrupt: func(cs *refs.Checksum) { cs.SetSum(nil) }}, + {name: "value/empty", err: "missing value", corrupt: func(cs *refs.Checksum) { cs.SetSum([]byte{}) }}, + } { + t.Run(tc.name, func(t *testing.T) { + var src, dst checksum.Checksum + var m refs.Checksum + + src.WriteToV2(&m) + tc.corrupt(&m) + require.ErrorContains(t, dst.ReadFromV2(m), tc.err) + }) + } + }) +} - cTZ := tz.Sum(data) - c = NewTillichZemor(cTZ) +func TestNew(t *testing.T) { + typ := checksum.Type(rand.Uint32()) + val := make([]byte, 128) + //nolint:staticcheck + rand.Read(val) + cs := checksum.New(typ, val) + require.Equal(t, typ, cs.Type()) + require.Equal(t, val, cs.Value()) - require.Equal(t, TZ, c.Type()) - require.Equal(t, cTZ[:], c.Value()) + otherTyp := checksum.Type(rand.Uint32()) + otherVal := make([]byte, 128) + //nolint:staticcheck + rand.Read(otherVal) + cs = checksum.New(otherTyp, otherVal) + require.Equal(t, otherTyp, cs.Type()) + require.Equal(t, otherVal, cs.Value()) + + t.Run("encoding", func(t *testing.T) { + t.Run("api", func(t *testing.T) { + src := checksum.New(typ, val) + var dst checksum.Checksum + var m refs.Checksum + + src.WriteToV2(&m) + switch actual := m.GetType(); typ { + default: + require.EqualValues(t, typ, actual) + case checksum.TZ: + require.Equal(t, refs.TillichZemor, actual) + case checksum.SHA256: + require.Equal(t, refs.SHA256, actual) + } + require.Equal(t, val, m.GetSum()) + require.NoError(t, dst.ReadFromV2(m)) + require.Equal(t, typ, dst.Type()) + require.Equal(t, val, dst.Value()) + }) + }) +} - c.WriteToV2(&cV2) +func testTypeConstructor[T [sha256.Size]byte | [tz.Size]byte]( + t *testing.T, + typ checksum.Type, + typAPI refs.ChecksumType, + cons func(T) checksum.Checksum, +) { + // array generics do not support T[:] op, so randomize through conversion + b := make([]byte, 128) // more than any popular hash + //nolint:staticcheck + rand.Read(b) + val := T(b) + cs := cons(val) + require.Equal(t, typ, cs.Type()) + require.Len(t, cs.Value(), len(val)) + require.Equal(t, val, T(cs.Value())) - require.Equal(t, refs.TillichZemor, cV2.GetType()) - require.Equal(t, cTZ[:], cV2.GetSum()) + //nolint:staticcheck + rand.Read(b) + otherVal := T(b) + cs = cons(otherVal) + require.Equal(t, typ, cs.Type()) + require.Len(t, cs.Value(), len(otherVal)) + require.Equal(t, otherVal, T(cs.Value())) + + t.Run("encoding", func(t *testing.T) { + t.Run("api", func(t *testing.T) { + src := cons(val) + var dst checksum.Checksum + var m refs.Checksum + + src.WriteToV2(&m) + require.Equal(t, typAPI, m.GetType()) + require.Len(t, m.GetSum(), len(val)) + require.Equal(t, val, T(m.GetSum())) + require.NoError(t, dst.ReadFromV2(m)) + require.Equal(t, typ, dst.Type()) + require.Len(t, dst.Value(), len(val)) + require.Equal(t, val, T(dst.Value())) + }) + }) } -func TestNewChecksum(t *testing.T) { - t.Run("default values", func(t *testing.T) { - var chs Checksum - - // check initial values - require.Zero(t, chs.Type()) - require.Nil(t, chs.Value()) +func TestNewSHA256(t *testing.T) { + testTypeConstructor(t, checksum.SHA256, refs.SHA256, checksum.NewSHA256) +} - // convert to v2 message - var chsV2 refs.Checksum - chs.WriteToV2(&chsV2) +func TestNewTZ(t *testing.T) { + testTypeConstructor(t, checksum.TZ, refs.TillichZemor, checksum.NewTillichZemor) +} - require.Equal(t, refs.UnknownChecksum, chsV2.GetType()) - require.Nil(t, chsV2.GetSum()) +func TestNewFromHash(t *testing.T) { + var h hash.Hash = adler32.New() // any non-standard hash just for example + h.Write([]byte("Hello, world!")) + hb := []byte{32, 94, 4, 138} + + typ := checksum.Type(rand.Uint32()) + cs := checksum.NewFromHash(typ, h) + require.Equal(t, typ, cs.Type()) + require.Equal(t, hb, cs.Value()) + + t.Run("encoding", func(t *testing.T) { + t.Run("api", func(t *testing.T) { + src := checksum.NewFromHash(typ, h) + var dst checksum.Checksum + var m refs.Checksum + + src.WriteToV2(&m) + switch actual := m.GetType(); typ { + default: + require.EqualValues(t, typ, actual) + case checksum.TZ: + require.Equal(t, refs.TillichZemor, actual) + case checksum.SHA256: + require.Equal(t, refs.SHA256, actual) + } + require.Equal(t, hb, m.GetSum()) + require.NoError(t, dst.ReadFromV2(m)) + require.Equal(t, typ, dst.Type()) + require.Equal(t, hb, dst.Value()) + }) }) } func TestNewFromData(t *testing.T) { - _, err := NewFromData(0, nil) + _, err := checksum.NewFromData(0, nil) require.EqualError(t, err, "unsupported checksum type 0") - var cs Checksum - Calculate(&cs, 0, nil) + var cs checksum.Checksum + checksum.Calculate(&cs, 0, nil) require.Zero(t, cs) - _, err = NewFromData(TZ+1, nil) + _, err = checksum.NewFromData(checksum.TZ+1, nil) require.EqualError(t, err, "unsupported checksum type 3") - Calculate(&cs, TZ+1, nil) + checksum.Calculate(&cs, checksum.TZ+1, nil) require.Zero(t, cs) - payload := []byte{0, 1, 2, 3, 4, 5} + payload := []byte("Hello, world!") + hSHA256 := [sha256.Size]byte{49, 95, 91, 219, 118, 208, 120, 196, 59, 138, 192, 6, 78, 74, 1, 100, 97, 43, 31, 206, 119, 200, 105, 52, 91, 252, 148, 199, 88, 148, 237, 211} + hTZ := [tz.Size]byte{0, 0, 1, 66, 73, 241, 7, 149, 192, 36, 14, 221, 202, 138, 110, 191, 0, 0, 1, 201, 196, 220, 152, 176, 23, 253, 146, 173, 98, 151, 156, 140, + 0, 0, 0, 141, 148, 205, 152, 164, 87, 185, 131, 233, 55, 131, 141, 205, 0, 0, 0, 219, 200, 104, 158, 117, 199, 221, 137, 37, 173, 13, 247, 39} t.Run("SHA256", func(t *testing.T) { - orig := sha256.Sum256(payload) - - c, err := NewFromData(SHA256, payload) + c, err := checksum.NewFromData(checksum.SHA256, payload) require.NoError(t, err) - require.Equal(t, orig[:], c.Value()) - require.Equal(t, SHA256, c.Type()) + require.Equal(t, hSHA256[:], c.Value()) + require.Equal(t, checksum.SHA256, c.Type()) - c = Checksum{} - Calculate(&c, SHA256, payload) - require.Equal(t, orig[:], c.Value()) - require.Equal(t, SHA256, c.Type()) + c = checksum.Checksum{} + checksum.Calculate(&c, checksum.SHA256, payload) + require.Equal(t, hSHA256[:], c.Value()) + require.Equal(t, checksum.SHA256, c.Type()) }) - t.Run("TZ", func(t *testing.T) { - orig := tz.Sum(payload) - - c, err := NewFromData(TZ, payload) + t.Run("TillichZemor", func(t *testing.T) { + c, err := checksum.NewFromData(checksum.TZ, payload) require.NoError(t, err) - require.Equal(t, orig[:], c.Value()) - require.Equal(t, TZ, c.Type()) + require.Equal(t, hTZ[:], c.Value()) + require.Equal(t, checksum.TZ, c.Type()) - c = Checksum{} - Calculate(&c, TZ, payload) - require.Equal(t, orig[:], c.Value()) - require.Equal(t, TZ, c.Type()) + c = checksum.Checksum{} + checksum.Calculate(&c, checksum.TZ, payload) + require.Equal(t, hTZ[:], c.Value()) + require.Equal(t, checksum.TZ, c.Type()) }) } + +func TestChecksum_SetSHA256(t *testing.T) { + testTypeConstructor(t, checksum.SHA256, refs.SHA256, func(b [sha256.Size]byte) (c checksum.Checksum) { c.SetSHA256(b); return }) +} + +func TestChecksum_SetTillichZemor(t *testing.T) { + testTypeConstructor(t, checksum.TZ, refs.TillichZemor, func(b [tz.Size]byte) (c checksum.Checksum) { c.SetTillichZemor(b); return }) +} From 15269a66d967583c1ed535745df737237b0594eb Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 9 Jul 2024 10:57:46 +0400 Subject: [PATCH 7/8] checksum: Make `Type` enum definition more compact Signed-off-by: Leonard Lyubich --- checksum/checksum.go | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/checksum/checksum.go b/checksum/checksum.go index 467edf38..10a1fcc4 100644 --- a/checksum/checksum.go +++ b/checksum/checksum.go @@ -28,15 +28,9 @@ type Checksum refs.Checksum type Type uint32 const ( - // Unknown is an undefined checksum type. - // Deprecated: use 0 instead. - Unknown Type = iota - - // SHA256 is a SHA256 checksum type. - SHA256 - - // TZ is a Tillich-Zémor checksum type. - TZ + Unknown Type = iota // Deprecated: use 0 instead. + SHA256 // SHA-256 hash + TZ // Tillich-Zémor homomorphic hash ) func typeToProto(t Type) refs.ChecksumType { From 4a5fd58dc0b8554a833d8216fc9c7fba75d160e2 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 9 Jul 2024 11:04:41 +0400 Subject: [PATCH 8/8] =?UTF-8?q?checksum:=20Provide=20more=20clear=20name?= =?UTF-8?q?=20to=20Tillich-Z=C3=A9mor=20hash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unlike SHA-256, this hash function is not so widely known. Signed-off-by: Leonard Lyubich --- checksum/checksum.go | 20 ++++++++++++-------- checksum/checksum_test.go | 22 +++++++++++----------- checksum/example_test.go | 4 ++-- checksum/test/generate.go | 2 +- object/slicer/slicer.go | 2 +- 5 files changed, 27 insertions(+), 23 deletions(-) diff --git a/checksum/checksum.go b/checksum/checksum.go index 10a1fcc4..cbc32ce2 100644 --- a/checksum/checksum.go +++ b/checksum/checksum.go @@ -28,18 +28,22 @@ type Checksum refs.Checksum type Type uint32 const ( - Unknown Type = iota // Deprecated: use 0 instead. - SHA256 // SHA-256 hash - TZ // Tillich-Zémor homomorphic hash + Unknown Type = iota // Deprecated: use 0 instead. + SHA256 // SHA-256 hash + TillichZemor // Tillich-Zémor homomorphic hash ) +// TZ is a type for Tillich-Zémor homomorphic hash. +// Deprecated: use TillichZemor instead. +const TZ = TillichZemor + func typeToProto(t Type) refs.ChecksumType { switch t { default: return refs.ChecksumType(t) case SHA256: return refs.SHA256 - case TZ: + case TillichZemor: return refs.TillichZemor } } @@ -60,7 +64,7 @@ func NewSHA256(h [sha256.Size]byte) Checksum { // NewTillichZemor constructs new Checksum from Tillich-Zémor homomorphic hash. func NewTillichZemor(h [tz.Size]byte) Checksum { - return New(TZ, h[:]) + return New(TillichZemor, h[:]) } // NewFromHash constructs new Checksum of specified type from accumulated @@ -78,7 +82,7 @@ func NewFromData(typ Type, data []byte) (Checksum, error) { return Checksum{}, fmt.Errorf("unsupported checksum type %d", typ) case SHA256: return NewSHA256(sha256.Sum256(data)), nil - case TZ: + case TillichZemor: return NewTillichZemor(tz.Sum(data)), nil } } @@ -116,7 +120,7 @@ func (c Checksum) Type() Type { case refs.SHA256: return SHA256 case refs.TillichZemor: - return TZ + return TillichZemor default: return Type(typ) } @@ -147,7 +151,7 @@ func (c *Checksum) SetSHA256(v [sha256.Size]byte) { *c = NewSHA256(v) } // // Does nothing if the passed type is not one of the: // - SHA256; -// - TZ. +// - TillichZemor. // // Does not mutate the passed value. // diff --git a/checksum/checksum_test.go b/checksum/checksum_test.go index dbacc79e..830952cc 100644 --- a/checksum/checksum_test.go +++ b/checksum/checksum_test.go @@ -20,7 +20,7 @@ func TestType_String(t *testing.T) { }{ {0, "CHECKSUM_TYPE_UNSPECIFIED"}, {checksum.SHA256, "SHA256"}, - {checksum.TZ, "TZ"}, + {checksum.TillichZemor, "TZ"}, {3, "3"}, } { require.Equal(t, tc.exp, tc.typ.String()) @@ -90,7 +90,7 @@ func TestNew(t *testing.T) { switch actual := m.GetType(); typ { default: require.EqualValues(t, typ, actual) - case checksum.TZ: + case checksum.TillichZemor: require.Equal(t, refs.TillichZemor, actual) case checksum.SHA256: require.Equal(t, refs.SHA256, actual) @@ -150,7 +150,7 @@ func TestNewSHA256(t *testing.T) { } func TestNewTZ(t *testing.T) { - testTypeConstructor(t, checksum.TZ, refs.TillichZemor, checksum.NewTillichZemor) + testTypeConstructor(t, checksum.TillichZemor, refs.TillichZemor, checksum.NewTillichZemor) } func TestNewFromHash(t *testing.T) { @@ -173,7 +173,7 @@ func TestNewFromHash(t *testing.T) { switch actual := m.GetType(); typ { default: require.EqualValues(t, typ, actual) - case checksum.TZ: + case checksum.TillichZemor: require.Equal(t, refs.TillichZemor, actual) case checksum.SHA256: require.Equal(t, refs.SHA256, actual) @@ -192,9 +192,9 @@ func TestNewFromData(t *testing.T) { var cs checksum.Checksum checksum.Calculate(&cs, 0, nil) require.Zero(t, cs) - _, err = checksum.NewFromData(checksum.TZ+1, nil) + _, err = checksum.NewFromData(checksum.TillichZemor+1, nil) require.EqualError(t, err, "unsupported checksum type 3") - checksum.Calculate(&cs, checksum.TZ+1, nil) + checksum.Calculate(&cs, checksum.TillichZemor+1, nil) require.Zero(t, cs) payload := []byte("Hello, world!") @@ -215,15 +215,15 @@ func TestNewFromData(t *testing.T) { }) t.Run("TillichZemor", func(t *testing.T) { - c, err := checksum.NewFromData(checksum.TZ, payload) + c, err := checksum.NewFromData(checksum.TillichZemor, payload) require.NoError(t, err) require.Equal(t, hTZ[:], c.Value()) - require.Equal(t, checksum.TZ, c.Type()) + require.Equal(t, checksum.TillichZemor, c.Type()) c = checksum.Checksum{} - checksum.Calculate(&c, checksum.TZ, payload) + checksum.Calculate(&c, checksum.TillichZemor, payload) require.Equal(t, hTZ[:], c.Value()) - require.Equal(t, checksum.TZ, c.Type()) + require.Equal(t, checksum.TillichZemor, c.Type()) }) } @@ -232,5 +232,5 @@ func TestChecksum_SetSHA256(t *testing.T) { } func TestChecksum_SetTillichZemor(t *testing.T) { - testTypeConstructor(t, checksum.TZ, refs.TillichZemor, func(b [tz.Size]byte) (c checksum.Checksum) { c.SetTillichZemor(b); return }) + testTypeConstructor(t, checksum.TillichZemor, refs.TillichZemor, func(b [tz.Size]byte) (c checksum.Checksum) { c.SetTillichZemor(b); return }) } diff --git a/checksum/example_test.go b/checksum/example_test.go index afa1b269..8b221154 100644 --- a/checksum/example_test.go +++ b/checksum/example_test.go @@ -27,7 +27,7 @@ func ExampleNewFromHash() { newHash func() hash.Hash }{ {SHA256, sha256.New}, - {TZ, tz.New}, + {TillichZemor, tz.New}, } { h := tc.newHash() h.Write(data) @@ -40,7 +40,7 @@ func ExampleNewFromData() { data := []byte("Hello, world!") for _, typ := range []Type{ SHA256, - TZ, + TillichZemor, } { cs, err := NewFromData(typ, data) if err != nil { diff --git a/checksum/test/generate.go b/checksum/test/generate.go index 55dd02c1..f93e130b 100644 --- a/checksum/test/generate.go +++ b/checksum/test/generate.go @@ -7,7 +7,7 @@ import ( "github.com/nspcc-dev/neofs-sdk-go/checksum" ) -var allSupportedTypes = []checksum.Type{checksum.SHA256, checksum.TZ} +var allSupportedTypes = []checksum.Type{checksum.SHA256, checksum.TillichZemor} // Checksum returns random checksum.Checksum. func Checksum() checksum.Checksum { diff --git a/object/slicer/slicer.go b/object/slicer/slicer.go index e7e9c583..a54894ba 100644 --- a/object/slicer/slicer.go +++ b/object/slicer/slicer.go @@ -601,7 +601,7 @@ func flushObjectMetadata(signer neofscrypto.Signer, meta dynamicObjectMetadata, header.SetPayloadChecksum(checksum.NewFromHash(checksum.SHA256, meta.checksum)) if meta.homomorphicChecksum != nil { - header.SetPayloadHomomorphicHash(checksum.NewFromHash(checksum.TZ, meta.homomorphicChecksum)) + header.SetPayloadHomomorphicHash(checksum.NewFromHash(checksum.TillichZemor, meta.homomorphicChecksum)) } header.SetPayloadSize(meta.length)