github.com/status-im/status-go@v1.1.0/eth-node/types/address.go (about) 1 package types 2 3 import ( 4 "database/sql/driver" 5 "encoding/hex" 6 "encoding/json" 7 "fmt" 8 "math/big" 9 "reflect" 10 "strings" 11 12 "golang.org/x/crypto/sha3" 13 ) 14 15 /////////// Address 16 17 // AddressLength is the expected length of the address 18 const AddressLength = 20 19 20 var ( 21 addressT = reflect.TypeOf(Address{}) 22 ) 23 24 // Address represents the 20 byte address of an Ethereum account. 25 type Address [AddressLength]byte 26 27 // BytesToAddress returns Address with value b. 28 // If b is larger than len(h), b will be cropped from the left. 29 func BytesToAddress(b []byte) Address { 30 var a Address 31 a.SetBytes(b) 32 return a 33 } 34 35 // BigToAddress returns Address with byte values of b. 36 // If b is larger than len(h), b will be cropped from the left. 37 func BigToAddress(b *big.Int) Address { return BytesToAddress(b.Bytes()) } 38 39 // HexToAddress returns Address with byte values of s. 40 // If s is larger than len(h), s will be cropped from the left. 41 func HexToAddress(s string) Address { return BytesToAddress(FromHex(s)) } 42 43 // IsHexAddress verifies whether a string can represent a valid hex-encoded 44 // Ethereum address or not. 45 func IsHexAddress(s string) bool { 46 if has0xPrefix(s) { 47 s = s[2:] 48 } 49 return len(s) == 2*AddressLength && isHex(s) 50 } 51 52 // Bytes gets the string representation of the underlying address. 53 func (a Address) Bytes() []byte { return a[:] } 54 55 // Hash converts an address to a hash by left-padding it with zeros. 56 func (a Address) Hash() Hash { return BytesToHash(a[:]) } 57 58 // Hex returns an EIP55-compliant hex string representation of the address. 59 func (a Address) Hex() string { 60 unchecksummed := hex.EncodeToString(a[:]) 61 sha := sha3.NewLegacyKeccak256() 62 _, _ = sha.Write([]byte(unchecksummed)) 63 hash := sha.Sum(nil) 64 65 result := []byte(unchecksummed) 66 for i := 0; i < len(result); i++ { 67 hashByte := hash[i/2] 68 if i%2 == 0 { 69 hashByte = hashByte >> 4 70 } else { 71 hashByte &= 0xf 72 } 73 if result[i] > '9' && hashByte > 7 { 74 result[i] -= 32 75 } 76 } 77 return "0x" + string(result) 78 } 79 80 // String implements fmt.Stringer. 81 func (a Address) String() string { 82 return a.Hex() 83 } 84 85 // Format implements fmt.Formatter, forcing the byte slice to be formatted as is, 86 // without going through the stringer interface used for logging. 87 func (a Address) Format(s fmt.State, c rune) { 88 fmt.Fprintf(s, "%"+string(c), a[:]) 89 } 90 91 // SetBytes sets the address to the value of b. 92 // If b is larger than len(a) it will panic. 93 func (a *Address) SetBytes(b []byte) { 94 if len(b) > len(a) { 95 b = b[len(b)-AddressLength:] 96 } 97 copy(a[AddressLength-len(b):], b) 98 } 99 100 // MarshalText returns the hex representation of a. 101 func (a Address) MarshalText() ([]byte, error) { 102 return HexBytes(a[:]).MarshalText() 103 } 104 105 // UnmarshalText parses a hash in hex syntax. 106 func (a *Address) UnmarshalText(input []byte) error { 107 return UnmarshalFixedText("Address", input, a[:]) 108 } 109 110 // UnmarshalJSON parses a hash in hex syntax. 111 func (a *Address) UnmarshalJSON(input []byte) error { 112 return UnmarshalFixedJSON(addressT, input, a[:]) 113 } 114 115 // Scan implements Scanner for database/sql. 116 func (a *Address) Scan(src interface{}) error { 117 srcB, ok := src.([]byte) 118 if !ok { 119 return fmt.Errorf("can't scan %T into Address", src) 120 } 121 if len(srcB) != AddressLength { 122 return fmt.Errorf("can't scan []byte of len %d into Address, want %d", len(srcB), AddressLength) 123 } 124 copy(a[:], srcB) 125 return nil 126 } 127 128 // Value implements valuer for database/sql. 129 func (a Address) Value() (driver.Value, error) { 130 return a[:], nil 131 } 132 133 // ImplementsGraphQLType returns true if Hash implements the specified GraphQL type. 134 func (a Address) ImplementsGraphQLType(name string) bool { return name == "Address" } 135 136 // UnmarshalGraphQL unmarshals the provided GraphQL query data. 137 func (a *Address) UnmarshalGraphQL(input interface{}) error { 138 var err error 139 switch input := input.(type) { 140 case string: 141 err = a.UnmarshalText([]byte(input)) 142 default: 143 err = fmt.Errorf("Unexpected type for Address: %v", input) 144 } 145 return err 146 } 147 148 // UnprefixedAddress allows marshaling an Address without 0x prefix. 149 type UnprefixedAddress Address 150 151 // UnmarshalText decodes the address from hex. The 0x prefix is optional. 152 func (a *UnprefixedAddress) UnmarshalText(input []byte) error { 153 return UnmarshalFixedUnprefixedText("UnprefixedAddress", input, a[:]) 154 } 155 156 // MarshalText encodes the address as hex. 157 func (a UnprefixedAddress) MarshalText() ([]byte, error) { 158 return []byte(hex.EncodeToString(a[:])), nil 159 } 160 161 // MixedcaseAddress retains the original string, which may or may not be 162 // correctly checksummed 163 type MixedcaseAddress struct { 164 addr Address 165 original string 166 } 167 168 // NewMixedcaseAddress constructor (mainly for testing) 169 func NewMixedcaseAddress(addr Address) MixedcaseAddress { 170 return MixedcaseAddress{addr: addr, original: addr.Hex()} 171 } 172 173 // NewMixedcaseAddressFromString is mainly meant for unit-testing 174 func NewMixedcaseAddressFromString(hexaddr string) (*MixedcaseAddress, error) { 175 if !IsHexAddress(hexaddr) { 176 return nil, fmt.Errorf("Invalid address") 177 } 178 a := FromHex(hexaddr) 179 return &MixedcaseAddress{addr: BytesToAddress(a), original: hexaddr}, nil 180 } 181 182 // UnmarshalJSON parses MixedcaseAddress 183 func (ma *MixedcaseAddress) UnmarshalJSON(input []byte) error { 184 if err := UnmarshalFixedJSON(addressT, input, ma.addr[:]); err != nil { 185 return err 186 } 187 return json.Unmarshal(input, &ma.original) 188 } 189 190 // MarshalJSON marshals the original value 191 func (ma *MixedcaseAddress) MarshalJSON() ([]byte, error) { 192 if strings.HasPrefix(ma.original, "0x") || strings.HasPrefix(ma.original, "0X") { 193 return json.Marshal(fmt.Sprintf("0x%s", ma.original[2:])) 194 } 195 return json.Marshal(fmt.Sprintf("0x%s", ma.original)) 196 } 197 198 // Address returns the address 199 func (ma *MixedcaseAddress) Address() Address { 200 return ma.addr 201 } 202 203 // String implements fmt.Stringer 204 func (ma *MixedcaseAddress) String() string { 205 if ma.ValidChecksum() { 206 return fmt.Sprintf("%s [chksum ok]", ma.original) 207 } 208 return fmt.Sprintf("%s [chksum INVALID]", ma.original) 209 } 210 211 // ValidChecksum returns true if the address has valid checksum 212 func (ma *MixedcaseAddress) ValidChecksum() bool { 213 return ma.original == ma.addr.Hex() 214 } 215 216 // Original returns the mixed-case input string 217 func (ma *MixedcaseAddress) Original() string { 218 return ma.original 219 }