-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #4 from Cleverse/feature/package-address
add Ethereum Address utilitues package
- Loading branch information
Showing
9 changed files
with
549 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
[![GoDoc](https://godoc.org/github.com/Cleverse/go-utilities/address?status.svg)](http://godoc.org/github.com/Cleverse/go-utilities/address) | ||
[![Report card](https://goreportcard.com/badge/github.com/Cleverse/go-utilities/address)](https://goreportcard.com/report/github.com/Cleverse/go-utilities/address) | ||
|
||
# address | ||
|
||
[go-ethereum](https://github.com/ethereum/go-ethereum) address utilities package. | ||
|
||
## Installation | ||
|
||
```shell | ||
go get github.com/Cleverse/go-utilities/address | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,138 @@ | ||
package address | ||
|
||
import ( | ||
"crypto/rand" | ||
"encoding/hex" | ||
"strings" | ||
|
||
"github.com/Cleverse/go-utilities/utils" | ||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/ethereum/go-ethereum/crypto" | ||
) | ||
|
||
const ( | ||
// AddressLength is the expected length of the address | ||
AddressLength = common.AddressLength | ||
) | ||
|
||
var ( | ||
// Ether is the default address of the Ethereum's native currency. | ||
Ether = common.HexToAddress("0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE") | ||
|
||
// Zero is zero value of common.Address. | ||
Zero = common.Address{} | ||
|
||
// DeadWalletAddress is the defailt address of the dead wallet. | ||
DeadWalletAddress = common.HexToAddress("0x000000000000000000000000000000000000dead") | ||
|
||
// DeadAddresseses is the list of dead addresses. | ||
DeadAddresseses = map[common.Address]interface{}{ | ||
Zero: nil, // 0x0000000000000000000000000000000000000000 | ||
DeadWalletAddress: nil, // 0x000000000000000000000000000000000000dead | ||
common.HexToAddress("0x0000000000000000000000000000000000000001"): nil, // Safemoon, GALA, AWC, NOW | ||
common.HexToAddress("0xdEAD000000000000000042069420694206942069"): nil, // SHIB, BOHR, HEGIC, etc in Ethereum | ||
common.HexToAddress("0xdead000000000000000000000000000000000000"): nil, | ||
} | ||
|
||
// RandomGenerator is the address random generator function and used by address.Random() function. | ||
// | ||
// Default address random generator is cryptographically secure random number generator. | ||
RandomGenerator func() (addr common.Address) = RandomFromBytes | ||
) | ||
|
||
// FromHex safely converts a hex string to a common.Address. | ||
func FromHex(address string) (addr common.Address) { | ||
address = strings.TrimSpace(address) | ||
if len(address) == 0 { | ||
return Zero | ||
} | ||
return common.HexToAddress(address) | ||
} | ||
|
||
// FromString safely converts a string to a common.Address. | ||
func FromString(address string) (addr common.Address) { | ||
return FromHex(address) | ||
} | ||
|
||
// FromHexes safely converts hex string slice to a common.Address slice. | ||
func FromHexes(src []string) (dst []common.Address) { | ||
dst = make([]common.Address, 0, len(src)) | ||
for _, addr := range src { | ||
dst = append(dst, FromHex(addr)) | ||
} | ||
return dst | ||
} | ||
|
||
// FromStrings alias of FromHexes. safely converts string slice to a common.Address slice. | ||
func FromStrings(src []string) (dst []common.Address) { | ||
return FromHexes(src) | ||
} | ||
|
||
// ToLower converts an address to a lower-case string withouth checksum. | ||
func ToLower(a common.Address) string { | ||
buf := [common.AddressLength*2 + 2]byte{} | ||
copy(buf[:2], []byte("0x")) | ||
_ = hex.Encode(buf[2:], a[:]) | ||
return string(buf[:]) | ||
} | ||
|
||
// ToString alias of ToLower. converts an address to a lower-case string withouth checksum. | ||
func ToString(a common.Address) string { | ||
return ToLower(a) | ||
} | ||
|
||
// ToLowers converts an addresses to a lower-case strings withouth checksum. | ||
func ToLowers(src []common.Address) []string { | ||
dst := make([]string, 0, len(src)) | ||
for _, addr := range src { | ||
dst = append(dst, ToLower(addr)) | ||
} | ||
return dst | ||
} | ||
|
||
// ToStrings alias of ToLowers. converts an addresses to a lower-case strings withouth checksum. | ||
func ToStrings(src []common.Address) []string { | ||
return ToLowers(src) | ||
} | ||
|
||
// IsZero returns `true` if the address is zero value. | ||
func IsZero(a common.Address) bool { | ||
return a == Zero | ||
} | ||
|
||
// IsEmpty returns `true` if the address is empty (alias of IsZero) | ||
func IsEmpty(a common.Address) bool { | ||
return a == Zero | ||
} | ||
|
||
// IsDead returns `true` if the address is dead address. | ||
func IsDead(a common.Address) bool { | ||
_, ok := DeadAddresseses[a] | ||
return ok | ||
} | ||
|
||
// IsZero returns `true` if the address is can't be used. (zero value or dead address) | ||
func IsValid(a common.Address) bool { | ||
return !IsZero(a) && !IsDead(a) | ||
} | ||
|
||
// Random returns a random common.Address. can be changed address random generator by RandomGenerator variable. | ||
// | ||
// Default address random generator is use cryptographically secure random number generator. | ||
func Random() (addr common.Address) { | ||
if RandomGenerator == nil { | ||
RandomGenerator = RandomFromBytes | ||
} | ||
return RandomGenerator() | ||
} | ||
|
||
// RandomFromPrivateKey returns a random address from a random private key | ||
func RandomFromPrivateKey() common.Address { | ||
return crypto.PubkeyToAddress(utils.Must(crypto.GenerateKey()).PublicKey) | ||
} | ||
|
||
// RandomFromBytes returns a random address from a random byte slice (via crypto/rand) | ||
func RandomFromBytes() (addr common.Address) { | ||
_, _ = rand.Read(addr[:]) | ||
return addr | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,187 @@ | ||
package address | ||
|
||
import ( | ||
"crypto/rand" | ||
"encoding/hex" | ||
"fmt" | ||
"strings" | ||
"testing" | ||
|
||
"github.com/ethereum/go-ethereum/common" | ||
"github.com/stretchr/testify/assert" | ||
) | ||
|
||
func BenchmarkRandomAddress(b *testing.B) { | ||
b.Run("rand/privatekey", func(b *testing.B) { | ||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
_ = RandomFromPrivateKey() | ||
b.SetBytes(AddressLength) | ||
} | ||
}) | ||
b.Run("crypto/rand", func(b *testing.B) { | ||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
buf := make([]byte, AddressLength) | ||
_, _ = rand.Read(buf) | ||
_ = common.HexToAddress(hex.EncodeToString(buf)) | ||
b.SetBytes(AddressLength) | ||
} | ||
}) | ||
b.Run("crypto/rand /w optimize", func(b *testing.B) { | ||
b.ResetTimer() | ||
for i := 0; i < b.N; i++ { | ||
_ = RandomFromBytes() | ||
b.SetBytes(AddressLength) | ||
} | ||
}) | ||
} | ||
|
||
func BenchmarkToLower(b *testing.B) { | ||
address := common.HexToAddress("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") | ||
var sizeAddressString int64 = 42 | ||
toStrings := map[string]func(common.Address) string{ | ||
"Strings.ToLower": func(a common.Address) string { | ||
return strings.ToLower(a.String()) | ||
}, | ||
"Old": func(a common.Address) string { | ||
hexEncoding := hex.EncodeToString(a.Bytes()) | ||
return fmt.Sprintf("0x%s", hexEncoding) | ||
}, | ||
"StringBuilder_1": func(a common.Address) string { | ||
var s strings.Builder | ||
s.WriteString("0x") | ||
s.WriteString(hex.EncodeToString(a.Bytes())) | ||
return s.String() | ||
}, | ||
"StringBuilder_2": func(a common.Address) string { | ||
var s strings.Builder | ||
b := make([]byte, 40) | ||
_ = hex.Encode(b, a.Bytes()) // Avoid conversion between []byte and string to reduce memory allocation. | ||
s.WriteString("0x") | ||
s.Write(b) | ||
return s.String() | ||
}, | ||
"StringBuilder_3": func(a common.Address) string { | ||
var s strings.Builder | ||
b := make([]byte, 40) | ||
_ = hex.Encode(b, a.Bytes()) // Avoid conversion between []byte and string to reduce memory allocation. | ||
s.Write([]byte{48, 120}) // use Write instead of WriteString | ||
s.Write(b) | ||
return s.String() | ||
}, | ||
"Now": ToLower, | ||
} | ||
ordered := []string{"Strings.ToLower", "Old", "StringBuilder_1", "StringBuilder_2", "StringBuilder_3", "Now"} | ||
for _, name := range ordered { | ||
toString := toStrings[name] | ||
b.Run(name, func(b *testing.B) { | ||
for i := 0; i < b.N; i++ { | ||
_ = toString(address) | ||
b.SetBytes(sizeAddressString) | ||
} | ||
}) | ||
} | ||
/* | ||
goos: darwin | ||
goarch: arm64 | ||
BenchmarkToLower/Strings.ToLower-8 1529884 808.3 ns/op 51.96 MB/s 1104 B/op 7 allocs/op | ||
BenchmarkToLower/Old-8 10714300 104.1 ns/op 403.51 MB/s 160 B/op 4 allocs/op | ||
BenchmarkToLower/StringBuilder_1-8 14235339 88.43 ns/op 474.93 MB/s 152 B/op 4 allocs/op | ||
BenchmarkToLower/StringBuilder_2-8 24213360 49.09 ns/op 855.51 MB/s 56 B/op 2 allocs/op | ||
BenchmarkToLower/StringBuilder_3-8 23675196 49.30 ns/op 851.92 MB/s 56 B/op 2 allocs/op | ||
BenchmarkToLower/Now-8 39285494 30.81 ns/op 1363.39 MB/s 48 B/op 1 allocs/op | ||
*/ | ||
} | ||
|
||
func BenchmarkToString(b *testing.B) { | ||
address := common.HexToAddress("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF") | ||
var sizeAddressString int64 = 42 | ||
|
||
toStrings := map[string]func(common.Address) string{ | ||
"Address.String()": func(a common.Address) string { | ||
return a.String() | ||
}, | ||
"Old": func(a common.Address) string { | ||
hexEncoding := hex.EncodeToString(a.Bytes()) | ||
return fmt.Sprintf("0x%s", hexEncoding) | ||
}, | ||
"StringBuilder": func(a common.Address) string { | ||
var s strings.Builder | ||
s.WriteString("0x") | ||
s.WriteString(hex.EncodeToString(a.Bytes())) | ||
return s.String() | ||
}, | ||
"Now": ToString, | ||
} | ||
|
||
for name, toString := range toStrings { | ||
b.Run(name, func(b *testing.B) { | ||
for i := 0; i < b.N; i++ { | ||
_ = toString(address) | ||
b.SetBytes(sizeAddressString) | ||
} | ||
}) | ||
} | ||
/* | ||
goos: darwin | ||
goarch: arm64 | ||
BenchmarkToString/Address.String()-8 1906382 632.1 ns/op 66.44 MB/s 1056 B/op 6 allocs/op | ||
BenchmarkToString/Old-8 10145156 103.6 ns/op 405.57 MB/s 160 B/op 4 allocs/op | ||
BenchmarkToString/StringBuilder-8 14861654 81.47 ns/op 515.50 MB/s 152 B/op 4 allocs/op | ||
BenchmarkToString/Now-8 39218355 30.88 ns/op 1359.94 MB/s 48 B/op 1 allocs/op | ||
*/ | ||
} | ||
|
||
func TestToLower(t *testing.T) { | ||
address := "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" | ||
expected := strings.ToLower(address) | ||
actual := ToLower(common.HexToAddress(expected)) | ||
assert.Equal(t, expected, actual) | ||
} | ||
|
||
func TestToLowers(t *testing.T) { | ||
addresses := []common.Address{ | ||
common.HexToAddress("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), | ||
common.HexToAddress("0xABCDEFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), | ||
common.HexToAddress("0xdEAD000000000000000042069420694206942069"), | ||
} | ||
|
||
actuals := ToLowers(addresses) | ||
for i, actual := range actuals { | ||
assert.Equal(t, strings.ToLower(addresses[i].String()), actual) | ||
} | ||
} | ||
|
||
func TestToString(t *testing.T) { | ||
expected := "0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF" | ||
actual := ToString(common.HexToAddress(expected)) | ||
assert.Equal(t, strings.ToLower(expected), actual) | ||
} | ||
|
||
func TestToStrings(t *testing.T) { | ||
addresses := []common.Address{ | ||
common.HexToAddress("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), | ||
common.HexToAddress("0xABCDEFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), | ||
common.HexToAddress("0xdEAD000000000000000042069420694206942069"), | ||
} | ||
|
||
actuals := ToStrings(addresses) | ||
for i, actual := range actuals { | ||
assert.Equal(t, strings.ToLower(addresses[i].String()), actual) | ||
} | ||
} | ||
|
||
func TestFromStrings(t *testing.T) { | ||
expected := []common.Address{ | ||
common.HexToAddress("0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"), | ||
common.HexToAddress("0xABCDEFAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), | ||
common.HexToAddress("0xdEAD000000000000000042069420694206942069"), | ||
} | ||
expectedString := ToStrings(expected) | ||
|
||
actuals := FromStrings(expectedString) | ||
for i, actual := range actuals { | ||
assert.Equal(t, expected[i], actual) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
module github.com/Cleverse/go-utilities/address | ||
|
||
go 1.21 | ||
|
||
require ( | ||
github.com/Cleverse/go-utilities/utils v0.0.0-20231023121154-63cf4ad4e8a2 | ||
github.com/ethereum/go-ethereum v1.13.4 | ||
github.com/stretchr/testify v1.8.4 | ||
) | ||
|
||
require ( | ||
github.com/Cleverse/go-utilities/errors v0.0.0-20231019072721-442842e3dc09 // indirect | ||
github.com/btcsuite/btcd/btcec/v2 v2.3.2 // indirect | ||
github.com/davecgh/go-spew v1.1.1 // indirect | ||
github.com/decred/dcrd/dcrec/secp256k1/v4 v4.2.0 // indirect | ||
github.com/holiman/uint256 v1.2.3 // indirect | ||
github.com/pmezard/go-difflib v1.0.0 // indirect | ||
golang.org/x/crypto v0.14.0 // indirect | ||
golang.org/x/sys v0.13.0 // indirect | ||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect | ||
gopkg.in/yaml.v3 v3.0.1 // indirect | ||
) |
Oops, something went wrong.