From a8f43a56c845c0e9d356472958c9103a96067aad Mon Sep 17 00:00:00 2001 From: Dimitris Date: Thu, 14 Dec 2023 15:27:22 +0200 Subject: [PATCH 1/3] Add hex package --- pkg/utils/hex/hex.go | 55 +++++++++++++++++++++++++++++++ pkg/utils/hex/hex_test.go | 68 +++++++++++++++++++++++++++++++++++++++ pkg/utils/utils.go | 9 ------ 3 files changed, 123 insertions(+), 9 deletions(-) create mode 100644 pkg/utils/hex/hex.go create mode 100644 pkg/utils/hex/hex_test.go diff --git a/pkg/utils/hex/hex.go b/pkg/utils/hex/hex.go new file mode 100644 index 000000000..818356772 --- /dev/null +++ b/pkg/utils/hex/hex.go @@ -0,0 +1,55 @@ +package hex + +import ( + "encoding/hex" + "errors" + "fmt" + "math/big" + "strings" +) + +// EnsurePrefix adds the prefix (0x) to a given hex string. +func EnsurePrefix(str string) string { + if !strings.HasPrefix(str, "0x") { + str = "0x" + str + } + return str +} + +// ToBig parses the given hex string or panics if it is invalid. +func ToBig(s string) *big.Int { + n, ok := new(big.Int).SetString(s, 16) + if !ok { + panic(fmt.Errorf(`failed to convert "%s" as hex to big.Int`, s)) + } + return n +} + +// RemovePrefix removes the prefix (0x) of a given hex string. +func RemovePrefix(str string) string { + if HasPrefix(str) { + return str[2:] + } + return str +} + +// HasPrefix returns true if the string starts with 0x. +func HasPrefix(str string) bool { + return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X') +} + +// TryParse parses the given hex string to bytes, +// it can return error if the hex string is invalid. +// Follows the semantic of ethereum's FromHex. +func TryParse(s string) (b []byte, err error) { + if !HasPrefix(s) { + err = errors.New("hex string must have 0x prefix") + } else { + s = s[2:] + if len(s)%2 == 1 { + s = "0" + s + } + b, err = hex.DecodeString(s) + } + return +} diff --git a/pkg/utils/hex/hex_test.go b/pkg/utils/hex/hex_test.go new file mode 100644 index 000000000..6e1827559 --- /dev/null +++ b/pkg/utils/hex/hex_test.go @@ -0,0 +1,68 @@ +package hex_test + +import ( + "testing" + + "github.com/smartcontractkit/chainlink-common/pkg/utils/hex" + "github.com/stretchr/testify/assert" +) + +func TestHasPrefix(t *testing.T) { + t.Parallel() + + t.Run("has prefix", func(t *testing.T) { + t.Parallel() + + r := hex.HasPrefix("0xabc") + assert.True(t, r) + }) + + t.Run("doesn't have prefix", func(t *testing.T) { + t.Parallel() + + r := hex.HasPrefix("abc") + assert.False(t, r) + }) + + t.Run("has 0x suffix", func(t *testing.T) { + t.Parallel() + + r := hex.HasPrefix("abc0x") + assert.False(t, r) + }) + +} + +func TestTryParse(t *testing.T) { + t.Parallel() + + t.Run("0x prefix missing", func(t *testing.T) { + t.Parallel() + + _, err := hex.TryParse("abcd") + assert.Error(t, err) + }) + + t.Run("wrong hex characters", func(t *testing.T) { + t.Parallel() + + _, err := hex.TryParse("0xabcdzzz") + assert.Error(t, err) + }) + + t.Run("valid hex string", func(t *testing.T) { + t.Parallel() + + b, err := hex.TryParse("0x1234") + assert.NoError(t, err) + assert.Equal(t, []byte{0x12, 0x34}, b) + }) + + t.Run("prepend odd length with zero", func(t *testing.T) { + t.Parallel() + + b, err := hex.TryParse("0x123") + assert.NoError(t, err) + assert.Equal(t, []byte{0x1, 0x23}, b) + }) +} diff --git a/pkg/utils/utils.go b/pkg/utils/utils.go index 9adff93e7..57ea147c3 100644 --- a/pkg/utils/utils.go +++ b/pkg/utils/utils.go @@ -4,7 +4,6 @@ import ( "context" "math" mrand "math/rand" - "strings" "time" "github.com/smartcontractkit/chainlink-common/pkg/services" @@ -50,11 +49,3 @@ func IsZero[C comparable](val C) bool { var zero C return zero == val } - -// EnsureHexPrefix adds the prefix (0x) to a given hex string. -func EnsureHexPrefix(str string) string { - if !strings.HasPrefix(str, "0x") { - str = "0x" + str - } - return str -} From c6cac32516f8b181340e6c417a8d60e1fe753be1 Mon Sep 17 00:00:00 2001 From: Dimitris Date: Thu, 14 Dec 2023 16:06:34 +0200 Subject: [PATCH 2/3] Improvements --- pkg/utils/hex/hex.go | 14 +++++++------- pkg/utils/hex/hex_test.go | 29 +++++++++++++++++++++++------ 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/pkg/utils/hex/hex.go b/pkg/utils/hex/hex.go index 818356772..abf207c65 100644 --- a/pkg/utils/hex/hex.go +++ b/pkg/utils/hex/hex.go @@ -16,17 +16,17 @@ func EnsurePrefix(str string) string { return str } -// ToBig parses the given hex string or panics if it is invalid. -func ToBig(s string) *big.Int { +// ToBig parses the given hex string and returns error if it is invalid. +func ToBig(s string) (*big.Int, error) { n, ok := new(big.Int).SetString(s, 16) if !ok { - panic(fmt.Errorf(`failed to convert "%s" as hex to big.Int`, s)) + return nil, fmt.Errorf(`failed to convert "%s" as hex to big.Int`, s) } - return n + return n, nil } -// RemovePrefix removes the prefix (0x) of a given hex string. -func RemovePrefix(str string) string { +// TrimPrefix removes the prefix (0x) of a given hex string. +func TrimPrefix(str string) string { if HasPrefix(str) { return str[2:] } @@ -41,7 +41,7 @@ func HasPrefix(str string) bool { // TryParse parses the given hex string to bytes, // it can return error if the hex string is invalid. // Follows the semantic of ethereum's FromHex. -func TryParse(s string) (b []byte, err error) { +func DecodeString(s string) (b []byte, err error) { if !HasPrefix(s) { err = errors.New("hex string must have 0x prefix") } else { diff --git a/pkg/utils/hex/hex_test.go b/pkg/utils/hex/hex_test.go index 6e1827559..f1eb4c89a 100644 --- a/pkg/utils/hex/hex_test.go +++ b/pkg/utils/hex/hex_test.go @@ -7,6 +7,24 @@ import ( "github.com/stretchr/testify/assert" ) +func TestTrimPrefix(t *testing.T) { + t.Parallel() + + t.Run("trims prefix", func(t *testing.T) { + t.Parallel() + + s := hex.TrimPrefix("0xabc") + assert.Equal(t, "abc", s) + }) + + t.Run("returns the same string if it doesn't have prefix", func(t *testing.T) { + t.Parallel() + + s := hex.TrimPrefix("defg") + assert.Equal(t, "defg", s) + }) +} + func TestHasPrefix(t *testing.T) { t.Parallel() @@ -30,30 +48,29 @@ func TestHasPrefix(t *testing.T) { r := hex.HasPrefix("abc0x") assert.False(t, r) }) - } -func TestTryParse(t *testing.T) { +func TestDecodeString(t *testing.T) { t.Parallel() t.Run("0x prefix missing", func(t *testing.T) { t.Parallel() - _, err := hex.TryParse("abcd") + _, err := hex.DecodeString("abcd") assert.Error(t, err) }) t.Run("wrong hex characters", func(t *testing.T) { t.Parallel() - _, err := hex.TryParse("0xabcdzzz") + _, err := hex.DecodeString("0xabcdzzz") assert.Error(t, err) }) t.Run("valid hex string", func(t *testing.T) { t.Parallel() - b, err := hex.TryParse("0x1234") + b, err := hex.DecodeString("0x1234") assert.NoError(t, err) assert.Equal(t, []byte{0x12, 0x34}, b) }) @@ -61,7 +78,7 @@ func TestTryParse(t *testing.T) { t.Run("prepend odd length with zero", func(t *testing.T) { t.Parallel() - b, err := hex.TryParse("0x123") + b, err := hex.DecodeString("0x123") assert.NoError(t, err) assert.Equal(t, []byte{0x1, 0x23}, b) }) From b20725f93888002aa0fb602bbb7a51bb1bcfe0a2 Mon Sep 17 00:00:00 2001 From: Dimitris Date: Thu, 14 Dec 2023 16:43:19 +0200 Subject: [PATCH 3/3] Minor fixes --- pkg/utils/hex/hex.go | 6 +++--- pkg/utils/hex/hex_test.go | 18 ++++++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/pkg/utils/hex/hex.go b/pkg/utils/hex/hex.go index abf207c65..926af75da 100644 --- a/pkg/utils/hex/hex.go +++ b/pkg/utils/hex/hex.go @@ -16,8 +16,8 @@ func EnsurePrefix(str string) string { return str } -// ToBig parses the given hex string and returns error if it is invalid. -func ToBig(s string) (*big.Int, error) { +// ParseBig parses the given hex string and returns error if it is invalid. +func ParseBig(s string) (*big.Int, error) { n, ok := new(big.Int).SetString(s, 16) if !ok { return nil, fmt.Errorf(`failed to convert "%s" as hex to big.Int`, s) @@ -38,7 +38,7 @@ func HasPrefix(str string) bool { return len(str) >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X') } -// TryParse parses the given hex string to bytes, +// DecodeString parses the given hex string to bytes, // it can return error if the hex string is invalid. // Follows the semantic of ethereum's FromHex. func DecodeString(s string) (b []byte, err error) { diff --git a/pkg/utils/hex/hex_test.go b/pkg/utils/hex/hex_test.go index f1eb4c89a..d09cb9b98 100644 --- a/pkg/utils/hex/hex_test.go +++ b/pkg/utils/hex/hex_test.go @@ -7,6 +7,24 @@ import ( "github.com/stretchr/testify/assert" ) +func TestParseBig(t *testing.T) { + t.Parallel() + + t.Run("parses successfully", func(t *testing.T) { + t.Parallel() + + _, err := hex.ParseBig("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F") + assert.NoError(t, err) + }) + + t.Run("returns error", func(t *testing.T) { + t.Parallel() + + _, err := hex.ParseBig("0xabc") + assert.Error(t, err) + }) +} + func TestTrimPrefix(t *testing.T) { t.Parallel()