github.com/dominant-strategies/go-quai@v0.28.2/common/external_address.go (about) 1 package common 2 3 import ( 4 "bytes" 5 "database/sql/driver" 6 "encoding/hex" 7 "fmt" 8 "reflect" 9 10 "github.com/dominant-strategies/go-quai/common/hexutil" 11 "golang.org/x/crypto/sha3" 12 ) 13 14 type ExternalAddress [AddressLength]byte 15 16 // Bytes gets the string representation of the underlying address. 17 func (a ExternalAddress) Bytes() []byte { return a[:] } 18 19 // Hash converts an address to a hash by left-padding it with zeros. 20 func (a ExternalAddress) Hash() Hash { return BytesToHash(a[:]) } 21 22 // Hex returns a hex string representation of the address. 23 func (a ExternalAddress) Hex() string { 24 return string(a.checksumHex()) 25 } 26 27 // String implements fmt.Stringer. 28 func (a ExternalAddress) String() string { 29 return a.Hex() 30 } 31 32 func (a *ExternalAddress) checksumHex() []byte { 33 buf := a.hex() 34 35 // compute checksum 36 sha := sha3.NewLegacyKeccak256() 37 sha.Write(buf[2:]) 38 hash := sha.Sum(nil) 39 for i := 2; i < len(buf); i++ { 40 hashByte := hash[(i-2)/2] 41 if i%2 == 0 { 42 hashByte = hashByte >> 4 43 } else { 44 hashByte &= 0xf 45 } 46 if buf[i] > '9' && hashByte > 7 { 47 buf[i] -= 32 48 } 49 } 50 return buf[:] 51 } 52 53 func (a ExternalAddress) hex() []byte { 54 var buf [len(a)*2 + 2]byte 55 copy(buf[:2], "0x") 56 hex.Encode(buf[2:], a[:]) 57 return buf[:] 58 } 59 60 // Format implements fmt.Formatter. 61 // Address supports the %v, %s, %v, %x, %X and %d format verbs. 62 func (a ExternalAddress) Format(s fmt.State, c rune) { 63 switch c { 64 case 'v', 's': 65 s.Write(a.checksumHex()) 66 case 'q': 67 q := []byte{'"'} 68 s.Write(q) 69 s.Write(a.checksumHex()) 70 s.Write(q) 71 case 'x', 'X': 72 // %x disables the checksum. 73 hex := a.hex() 74 if !s.Flag('#') { 75 hex = hex[2:] 76 } 77 if c == 'X' { 78 hex = bytes.ToUpper(hex) 79 } 80 s.Write(hex) 81 case 'd': 82 fmt.Fprint(s, ([len(a)]byte)(a)) 83 default: 84 fmt.Fprintf(s, "%%!%c(address=%x)", c, a) 85 } 86 } 87 88 // SetBytes sets the address to the value of b. 89 // If b is larger than len(a), b will be cropped from the left. 90 func (a *ExternalAddress) setBytes(b []byte) { 91 if len(b) > len(a) { 92 b = b[len(b)-AddressLength:] 93 } 94 copy(a[AddressLength-len(b):], b) 95 } 96 97 // MarshalText returns the hex representation of a. 98 func (a ExternalAddress) MarshalText() ([]byte, error) { 99 return hexutil.Bytes(a[:]).MarshalText() 100 } 101 102 // UnmarshalText parses a hash in hex syntax. 103 func (a *ExternalAddress) UnmarshalText(input []byte) error { 104 return hexutil.UnmarshalFixedText("Address", input, a[:]) 105 } 106 107 // UnmarshalJSON parses a hash in hex syntax. 108 func (a *ExternalAddress) UnmarshalJSON(input []byte) error { 109 return hexutil.UnmarshalFixedJSON(reflect.TypeOf(ExternalAddress{}), input, a[:]) 110 } 111 112 // Scan implements Scanner for database/sql. 113 func (a *ExternalAddress) Scan(src interface{}) error { 114 srcB, ok := src.([]byte) 115 if !ok { 116 return fmt.Errorf("can't scan %T into Address", src) 117 } 118 if len(srcB) != AddressLength { 119 return fmt.Errorf("can't scan []byte of len %d into Address, want %d", len(srcB), AddressLength) 120 } 121 copy(a[:], srcB) 122 return nil 123 } 124 125 // Value implements valuer for database/sql. 126 func (a ExternalAddress) Value() (driver.Value, error) { 127 return a[:], nil 128 } 129 130 // Location looks up the chain location which contains this address 131 func (a ExternalAddress) Location() *Location { 132 R, Z, D := 0, 0, HierarchyDepth 133 if NodeLocation.HasRegion() { 134 R = NodeLocation.Region() 135 } 136 if NodeLocation.HasZone() { 137 Z = NodeLocation.Zone() 138 } 139 140 // Search zone->region->prime address spaces in-slice first, and then search 141 // zone->region out-of-slice address spaces next. This minimizes expected 142 // search time under the following assumptions: 143 // * a node is more likely to encounter a TX from its slice than from another 144 // * we expect `>= Z` `zone` TXs for every `region` TX 145 // * we expect `>= R` `region` TXs for every `prime` TX 146 // * (and by extension) we expect `>= R*Z` `zone` TXs for every `prime` TX 147 primeChecked := false 148 for r := 0; r < NumRegionsInPrime; r++ { 149 for z := 0; z < NumZonesInRegion; z++ { 150 l := Location{byte((r + R) % D), byte((z + Z) % D)} 151 if l.ContainsAddress(Address{&a}) { 152 return &l 153 } 154 } 155 l := Location{byte((r + R) % D)} 156 if l.ContainsAddress(Address{&a}) { 157 return &l 158 } 159 // Check prime on first pass through slice, but not again 160 if !primeChecked { 161 primeChecked = true 162 l := Location{} 163 if l.ContainsAddress(Address{&a}) { 164 return &l 165 } 166 } 167 } 168 return nil 169 }