package common import ( "crypto/rand" "crypto/sha256" "encoding/hex" "fmt" "io" "math/big" "github.com/33cn/chain33/common/crypto/sha3" "golang.org/x/crypto/ripemd160" ) const ( hashLength = 32 ) type Hash [hashLength]byte func BytesToHash(b []byte) Hash { var h Hash h.SetBytes(b) return h } func StringToHash(s string) Hash { return BytesToHash([]byte(s)) } func BigToHash(b *big.Int) Hash { return BytesToHash(b.Bytes()) } func HexToHash(s string) Hash { b, _ := FromHex(s) return BytesToHash(b) } // Get the string representation of the underlying hash func (h Hash) Str() string { return string(h[:]) } func (h Hash) Bytes() []byte { return h[:] } func (h Hash) Hex() string { return hexEncode(h[:]) } // TerminalString implements log.TerminalStringer, formatting a string for console // output during logging. func (h Hash) TerminalString() string { return fmt.Sprintf("%x…%x", h[:3], h[29:]) } // String implements the stringer interface and is used also by the logger when // doing full logging into a file. func (h Hash) String() string { return h.Hex() } // Format implements fmt.Formatter, forcing the byte slice to be formatted as is, // without going through the stringer interface used for logging. func (h Hash) Format(s fmt.State, c rune) { fmt.Fprintf(s, "%"+string(c), h[:]) } // Sets the hash to the value of b. If b is larger than len(h), 'b' will be cropped (from the left). func (h *Hash) SetBytes(b []byte) { if len(b) > len(h) { b = b[len(b)-hashLength:] } copy(h[hashLength-len(b):], b) } // Set string `s` to h. If s is larger than len(h) s will be cropped (from left) to fit. func (h *Hash) SetString(s string) { h.SetBytes([]byte(s)) } // Sets h to other func (h *Hash) Set(other Hash) { for i, v := range other { h[i] = v } } func EmptyHash(h Hash) bool { return h == Hash{} } func hexEncode(b []byte) string { enc := make([]byte, len(b)*2+2) copy(enc, "0x") hex.Encode(enc[2:], b) return string(enc) } func ToHex(b []byte) string { hex := Bytes2Hex(b) // Prefer output of "0x0" instead of "0x" if len(hex) == 0 { return "" } return "0x" + hex } func HashHex(d []byte) string { var buf [64]byte hex.Encode(buf[:], d) return string(buf[:]) } func FromHex(s string) ([]byte, error) { if len(s) > 1 { if s[0:2] == "0x" || s[0:2] == "0X" { s = s[2:] } if len(s)%2 == 1 { s = "0" + s } return Hex2Bytes(s) } return []byte{}, nil } // Copy bytes // // Returns an exact copy of the provided bytes func CopyBytes(b []byte) (copiedBytes []byte) { if b == nil { return nil } copiedBytes = make([]byte, len(b)) copy(copiedBytes, b) return } func HasHexPrefix(str string) bool { l := len(str) return l >= 2 && str[0:2] == "0x" } func IsHex(str string) bool { l := len(str) return l >= 4 && l%2 == 0 && str[0:2] == "0x" } func Bytes2Hex(d []byte) string { return hex.EncodeToString(d) } func Sha256(b []byte) []byte { data := sha256.Sum256(b) return data[:] } func ShaKeccak256(b []byte) []byte { data := sha3.KeccakSum256(b) return data[:] } func Hex2Bytes(str string) ([]byte, error) { return hex.DecodeString(str) } func sha2Hash(b []byte, out []byte) { s := sha256.New() s.Write(b[:]) tmp := s.Sum(nil) s.Reset() s.Write(tmp) copy(out[:], s.Sum(nil)) } // Returns hash: SHA256( SHA256( data ) ) // Where possible, using ShaHash() should be a bit faster func Sha2Sum(b []byte) (out [32]byte) { sha2Hash(b, out[:]) return } func rimpHash(in []byte, out []byte) { sha := sha256.New() sha.Write(in) rim := ripemd160.New() rim.Write(sha.Sum(nil)[:]) copy(out, rim.Sum(nil)) } // Returns hash: RIMP160( SHA256( data ) ) // Where possible, using RimpHash() should be a bit faster func Rimp160AfterSha256(b []byte) (out [20]byte) { rimpHash(b, out[:]) return } func RandKey() (ret [32]byte) { _, err := io.ReadFull(rand.Reader, ret[:]) if err != nil { panic(err) } return }