From fd13e61266c00c61ca9b28e709d930e9d89f585f Mon Sep 17 00:00:00 2001 From: Pavel Karpy Date: Mon, 22 Mar 2021 13:56:18 +0300 Subject: [PATCH] [#170] checksum: Drop `Empty` method Signed-off-by: Pavel Karpy --- checksum/checksum.go | 197 +++++++++++++++-------------------- checksum/checksum_test.go | 143 +++++-------------------- checksum/doc.go | 18 ++++ checksum/test/doc.go | 13 +++ checksum/test/generate.go | 4 +- go.mod | 1 + go.sum | Bin 41955 -> 42235 bytes object/fmt.go | 9 +- object/object.go | 24 +++-- object/raw_test.go | 12 +-- storagegroup/storagegroup.go | 15 ++- 11 files changed, 184 insertions(+), 252 deletions(-) create mode 100644 checksum/doc.go create mode 100644 checksum/test/doc.go diff --git a/checksum/checksum.go b/checksum/checksum.go index 19ad500..79324d4 100644 --- a/checksum/checksum.go +++ b/checksum/checksum.go @@ -1,15 +1,23 @@ package checksum import ( - "bytes" "crypto/sha256" "encoding/hex" "fmt" "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/tzhash/tz" ) -// Checksum represents v2-compatible checksum. +// Checksum represents checksum of some digital data. +// +// 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. +// +// Note that direct typecast is not safe and may result in loss of compatibility: +// _ = Checksum(refs.Checksum{}) // not recommended type Checksum refs.Checksum // Type represents the enumeration @@ -23,29 +31,33 @@ const ( // SHA256 is a SHA256 checksum type. SHA256 - // TZ is a Tillich-Zemor checksum type. + // TZ is a Tillich-Zémor checksum type. TZ ) -// NewFromV2 wraps v2 Checksum message to Checksum. +// ReadFromV2 reads Checksum from the refs.Checksum message. // -// Nil refs.Checksum converts to nil. -func NewFromV2(cV2 *refs.Checksum) *Checksum { - return (*Checksum)(cV2) +// See also WriteToV2. +func (c *Checksum) ReadFromV2(m refs.Checksum) { + *c = Checksum(m) } -// New creates and initializes blank Checksum. +// WriteToV2 writes Checksum to the refs.Checksum message. +// The message must not be nil. // -// Defaults: -// - sum: nil; -// - type: Unknown. -func New() *Checksum { - return NewFromV2(new(refs.Checksum)) +// See also ReadFromV2. +func (c Checksum) WriteToV2(m *refs.Checksum) { + *m = (refs.Checksum)(c) } // Type returns checksum type. -func (c *Checksum) Type() Type { - switch (*refs.Checksum)(c).GetType() { +// +// Zero Checksum has Unknown checksum type. +// +// See also SetTillichZemor and SetSHA256. +func (c Checksum) Type() Type { + v2 := (refs.Checksum)(c) + switch v2.GetType() { case refs.SHA256: return SHA256 case refs.TillichZemor: @@ -55,93 +67,77 @@ func (c *Checksum) Type() Type { } } -// Sum returns checksum bytes. -func (c *Checksum) Sum() []byte { - return (*refs.Checksum)(c).GetSum() +// Value returns checksum bytes. Return value +// MUST NOT be mutated. +// +// Zero Checksum has nil sum. +// +// See also SetTillichZemor and SetSHA256. +func (c Checksum) Value() []byte { + v2 := (refs.Checksum)(c) + return v2.GetSum() } // SetSHA256 sets checksum to SHA256 hash. +// +// See also Calculate. func (c *Checksum) SetSHA256(v [sha256.Size]byte) { - checksum := (*refs.Checksum)(c) + v2 := (*refs.Checksum)(c) - checksum.SetType(refs.SHA256) - checksum.SetSum(v[:]) + v2.SetType(refs.SHA256) + v2.SetSum(v[:]) } -// SetTillichZemor sets checksum to Tillich-Zemor hash. -func (c *Checksum) SetTillichZemor(v [64]byte) { - checksum := (*refs.Checksum)(c) - - checksum.SetType(refs.TillichZemor) - checksum.SetSum(v[:]) -} - -// ToV2 converts Checksum to v2 Checksum message. +// Calculate calculates checksum and sets it +// to the passed checksum. Checksum must not be nil. // -// Nil Checksum converts to nil. -func (c *Checksum) ToV2() *refs.Checksum { - return (*refs.Checksum)(c) -} - -func Equal(cs1, cs2 *Checksum) bool { - return cs1.Type() == cs2.Type() && bytes.Equal(cs1.Sum(), cs2.Sum()) -} - -// Marshal marshals Checksum into a protobuf binary form. -func (c *Checksum) Marshal() ([]byte, error) { - return (*refs.Checksum)(c).StableMarshal(nil) -} - -// Unmarshal unmarshals protobuf binary representation of Checksum. -func (c *Checksum) Unmarshal(data []byte) error { - return (*refs.Checksum)(c).Unmarshal(data) -} - -// MarshalJSON encodes Checksum to protobuf JSON format. -func (c *Checksum) MarshalJSON() ([]byte, error) { - return (*refs.Checksum)(c).MarshalJSON() -} - -// UnmarshalJSON decodes Checksum from protobuf JSON format. -func (c *Checksum) UnmarshalJSON(data []byte) error { - return (*refs.Checksum)(c).UnmarshalJSON(data) -} - -func (c *Checksum) String() string { - return hex.EncodeToString((*refs.Checksum)(c).GetSum()) -} - -// Parse parses Checksum from its string representation. -func (c *Checksum) Parse(s string) error { - data, err := hex.DecodeString(s) - if err != nil { - return err - } - - var typ refs.ChecksumType - - switch ln := len(data); ln { +// Does nothing if the passed type is not one of the: +// * SHA256; +// * TZ. +// +// Does not mutate the passed value. +// +// See also SetSHA256, SetTillichZemor. +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: - return fmt.Errorf("unsupported checksum length %d", ln) - case sha256.Size: - typ = refs.SHA256 - case 64: - typ = refs.TillichZemor } - - cV2 := (*refs.Checksum)(c) - cV2.SetType(typ) - cV2.SetSum(data) - - return nil } -// String returns string representation of Type. +// SetTillichZemor sets checksum to Tillich-Zémor hash. // -// String mapping: -// * TZ: TZ; -// * SHA256: SHA256; -// * Unknown, default: CHECKSUM_TYPE_UNSPECIFIED. +// See also Calculate. +func (c *Checksum) SetTillichZemor(v [tz.Size]byte) { + v2 := (*refs.Checksum)(c) + + v2.SetType(refs.TillichZemor) + v2.SetSum(v[:]) +} + +// String implements fmt.Stringer. +// +// String is designed to be human-readable, and its format MAY differ between +// SDK versions. +func (c Checksum) String() string { + v2 := (refs.Checksum)(c) + return fmt.Sprintf("%s:%s", c.Type(), hex.EncodeToString(v2.GetSum())) +} + +// Empty returns true if it is called on +// zero checksum. +func (c Checksum) Empty() bool { + v2 := (refs.Checksum)(c) + return v2.GetSum() == nil +} + +// String implements fmt.Stringer. +// +// String is designed to be human-readable, and its format MAY differ between +// SDK versions. func (m Type) String() string { var m2 refs.ChecksumType @@ -156,26 +152,3 @@ func (m Type) String() string { return m2.String() } - -// FromString parses Type from a string representation. -// It is a reverse action to String(). -// -// Returns true if s was parsed successfully. -func (m *Type) FromString(s string) bool { - var g refs.ChecksumType - - ok := g.FromString(s) - - if ok { - switch g { - default: - *m = Unknown - case refs.TillichZemor: - *m = TZ - case refs.SHA256: - *m = SHA256 - } - } - - return ok -} diff --git a/checksum/checksum_test.go b/checksum/checksum_test.go index ce5515b..f9d22c9 100644 --- a/checksum/checksum_test.go +++ b/checksum/checksum_test.go @@ -6,19 +6,12 @@ import ( "testing" "github.com/nspcc-dev/neofs-api-go/v2/refs" + "github.com/nspcc-dev/tzhash/tz" "github.com/stretchr/testify/require" ) -func randSHA256(t *testing.T) [sha256.Size]byte { - cSHA256 := [sha256.Size]byte{} - _, err := rand.Read(cSHA256[:]) - require.NoError(t, err) - - return cSHA256 -} - func TestChecksum(t *testing.T) { - c := New() + var c Checksum cSHA256 := [sha256.Size]byte{} _, _ = rand.Read(cSHA256[:]) @@ -26,150 +19,62 @@ func TestChecksum(t *testing.T) { c.SetSHA256(cSHA256) require.Equal(t, SHA256, c.Type()) - require.Equal(t, cSHA256[:], c.Sum()) + require.Equal(t, cSHA256[:], c.Value()) - cV2 := c.ToV2() + var cV2 refs.Checksum + c.WriteToV2(&cV2) require.Equal(t, refs.SHA256, cV2.GetType()) require.Equal(t, cSHA256[:], cV2.GetSum()) - cTZ := [64]byte{} + cTZ := [tz.Size]byte{} _, _ = rand.Read(cSHA256[:]) c.SetTillichZemor(cTZ) require.Equal(t, TZ, c.Type()) - require.Equal(t, cTZ[:], c.Sum()) + require.Equal(t, cTZ[:], c.Value()) - cV2 = c.ToV2() + c.WriteToV2(&cV2) require.Equal(t, refs.TillichZemor, cV2.GetType()) require.Equal(t, cTZ[:], cV2.GetSum()) } -func TestEqualChecksums(t *testing.T) { - require.True(t, Equal(nil, nil)) - - csSHA := [sha256.Size]byte{} - _, _ = rand.Read(csSHA[:]) - - cs1 := New() - cs1.SetSHA256(csSHA) - - cs2 := New() - cs2.SetSHA256(csSHA) - - require.True(t, Equal(cs1, cs2)) - - csSHA[0]++ - cs2.SetSHA256(csSHA) - - require.False(t, Equal(cs1, cs2)) -} - -func TestChecksumEncoding(t *testing.T) { - cs := New() - cs.SetSHA256(randSHA256(t)) - - t.Run("binary", func(t *testing.T) { - data, err := cs.Marshal() - require.NoError(t, err) - - c2 := New() - require.NoError(t, c2.Unmarshal(data)) - - require.Equal(t, cs, c2) - }) - - t.Run("json", func(t *testing.T) { - data, err := cs.MarshalJSON() - require.NoError(t, err) - - cs2 := New() - require.NoError(t, cs2.UnmarshalJSON(data)) - - require.Equal(t, cs, cs2) - }) - - t.Run("string", func(t *testing.T) { - cs2 := New() - - require.NoError(t, cs2.Parse(cs.String())) - - require.Equal(t, cs, cs2) - }) -} - -func TestNewChecksumFromV2(t *testing.T) { - t.Run("from nil", func(t *testing.T) { - var x *refs.Checksum - - require.Nil(t, NewFromV2(x)) - }) -} - -func TestChecksum_ToV2(t *testing.T) { - t.Run("nil", func(t *testing.T) { - var x *Checksum - - require.Nil(t, x.ToV2()) - }) -} - func TestNewChecksum(t *testing.T) { t.Run("default values", func(t *testing.T) { - chs := New() + var chs Checksum // check initial values require.Equal(t, Unknown, chs.Type()) - require.Nil(t, chs.Sum()) + require.Nil(t, chs.Value()) // convert to v2 message - chsV2 := chs.ToV2() + var chsV2 refs.Checksum + chs.WriteToV2(&chsV2) require.Equal(t, refs.UnknownChecksum, chsV2.GetType()) require.Nil(t, chsV2.GetSum()) }) } -type enumIface interface { - FromString(string) bool - String() string -} +func TestCalculation(t *testing.T) { + var c Checksum + payload := []byte{0, 1, 2, 3, 4, 5} -type enumStringItem struct { - val enumIface - str string -} + t.Run("SHA256", func(t *testing.T) { + orig := sha256.Sum256(payload) -func testEnumStrings(t *testing.T, e enumIface, items []enumStringItem) { - for _, item := range items { - require.Equal(t, item.str, item.val.String()) + Calculate(&c, SHA256, payload) - s := item.val.String() + require.Equal(t, orig[:], c.Value()) + }) - require.True(t, e.FromString(s), s) + t.Run("TZ", func(t *testing.T) { + orig := tz.Sum(payload) - require.EqualValues(t, item.val, e, item.val) - } + Calculate(&c, TZ, payload) - // incorrect strings - for _, str := range []string{ - "some string", - "undefined", - } { - require.False(t, e.FromString(str)) - } -} - -func TestChecksumType_String(t *testing.T) { - toPtr := func(v Type) *Type { - return &v - } - - testEnumStrings(t, new(Type), []enumStringItem{ - {val: toPtr(TZ), str: "TZ"}, - {val: toPtr(SHA256), str: "SHA256"}, - {val: toPtr(Unknown), str: "CHECKSUM_TYPE_UNSPECIFIED"}, + require.Equal(t, orig[:], c.Value()) }) } diff --git a/checksum/doc.go b/checksum/doc.go new file mode 100644 index 0000000..1acac9f --- /dev/null +++ b/checksum/doc.go @@ -0,0 +1,18 @@ +/* +Package checksum provides primitives to work with checksums. + +Checksum is a basic type of data checksums. +For example, calculating checksums: + // retrieving any payload for hashing + + var sha256Sum Checksum + Calculate(&sha256Sum, SHA256, payload) // sha256Sum contains SHA256 hash of the payload + + var tzSum Checksum + Calculate(&tzSum, TZ, payload) // tzSum contains TZ hash of the payload + +Using package types in an application is recommended to potentially work with +different protocol versions with which these types are compatible. + +*/ +package checksum diff --git a/checksum/test/doc.go b/checksum/test/doc.go new file mode 100644 index 0000000..bb75038 --- /dev/null +++ b/checksum/test/doc.go @@ -0,0 +1,13 @@ +/* +Package checksumtest provides functions for convenient testing of checksum package API. + +Note that importing the package into source files is highly discouraged. + +Random instance generation functions can be useful when testing expects any value, e.g.: + import checksumtest "github.com/nspcc-dev/neofs-sdk-go/checksum/test" + + cs := checksumtest.Checksum() + // test the value + +*/ +package checksumtest diff --git a/checksum/test/generate.go b/checksum/test/generate.go index 1f8fa97..c40bd7a 100644 --- a/checksum/test/generate.go +++ b/checksum/test/generate.go @@ -13,9 +13,9 @@ func Checksum() *checksum.Checksum { rand.Read(cs[:]) - x := checksum.New() + var x checksum.Checksum x.SetSHA256(cs) - return x + return &x } diff --git a/go.mod b/go.mod index 0ea096e..21917ac 100644 --- a/go.mod +++ b/go.mod @@ -11,6 +11,7 @@ require ( github.com/nspcc-dev/hrw v1.0.9 github.com/nspcc-dev/neo-go v0.98.0 github.com/nspcc-dev/neofs-api-go/v2 v2.12.1 + github.com/nspcc-dev/tzhash v1.5.2 github.com/stretchr/testify v1.7.0 go.uber.org/zap v1.18.1 ) diff --git a/go.sum b/go.sum index b2836df7870782afdb2f1a1773502e3d3432192a..85118da67400637f5612a7e2ade58e58ce04d59e 100644 GIT binary patch delta 269 zcmaESoay&TrVaZ%ChvFUv?-~|NG#4!C^OVE)iYAaFtlt>CP-O>QZ(_0Y$ delta 75 zcmex;lIihrrVaZ%HfMVtw9{9}FtiFT^3(Q?uy8CY(GN~D@$kzsi%PCCFmpE#bFI=g fFSjg7i3kbsk22TJF`2B`E