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  }