package util
import (
"crypto/sha256"
"crypto/rand"
"encoding/hex"
"crypto/ripemd160"
"encoding/binary"
"fmt"
"math"
"strings"
"time"
"encoding/json"
)
const (
HashBytes = 32
HexTimeBytes = 7
TimeHashBytes = HexTimeBytes + HashBytes
StatusHashBytes = 64
HashSize = HashBytes * 2
HexTimeSize = HexTimeBytes * 2
TimeHashSize = TimeHashBytes * 2
IDHashSize = 44
StatusPrefixSize = 64
StatusKeySize = 64
StatusHashSize = 128
)
type Hasher struct{}
func (h *Hasher) MerkleRoot(array []string) string {
if len(array) == 0 {
return h.Hash("")
}
parent := make([]string, len(array))
for i, item := range array {
parent[i] = h.Hash(item)
}
for len(parent) > 1 {
child := []string{}
for i := 0; i < len(parent); i += 2 {
if i+1 < len(parent) {
child = append(child, h.Hash(parent[i]+parent[i+1]))
} else {
child = append(child, parent[i])
}
}
parent = child
}
return parent[0]
}
func (h *Hasher) MerkleTree(array []string) map[int][]string {
tree := map[int][]string{}
parent := make([]string, len(array))
for i, item := range array {
parent[i] = h.Hash(item)
}
height := 0
tree[height] = parent
for len(parent) > 1 {
height++
tree[height] = []string{}
for i := 0; i < len(parent); i += 2 {
if i+1 < len(parent) {
tree[height] = append(tree[height], h.Hash(parent[i]+parent[i+1]))
} else {
tree[height] = append(tree[height], parent[i])
}
}
parent = tree[height]
}
return tree
}
func (h *Hasher) MerklePath(array []string) map[int][]string {
merklePath := map[int][]string{}
tree := h.MerkleTree(array)
for i := 0; i < len(array); i++ {
path := []string{}
for height, leaf := range tree {
o := int(i / int(math.Pow(2, float64(height))))
way := i % int(math.Pow(2, float64(height+1)))
if way < int(math.Pow(2, float64(height))) && o+1 < len(leaf) {
path = append(path, "0"+leaf[o+1])
} else if way >= int(math.Pow(2, float64(height))) && o-1 >= 0 {
path = append(path, "1"+leaf[o-1])
}
}
merklePath[i] = path
}
return merklePath
}
func (h *Hasher) TracePath(startHash string, path []string) string {
root := startHash
for _, item := range path {
prefix := item[0:1]
hash := item[1:]
if prefix == "0" {
root = h.Hash(root + hash)
} else {
root = h.Hash(hash + root)
}
}
return root
}
func (h *Hasher) Hash(obj interface{}) string {
str := h.String(obj)
hash := sha256.Sum256([]byte(str))
return hex.EncodeToString(hash[:])
}
func (h *Hasher) HashValidity(hash string) bool {
return len(hash) == HashSize && h.IsHex(hash)
}
func (h *Hasher) ShortHash(obj interface{}) string {
hasher := ripemd160.New()
hasher.Write([]byte(h.Hash(obj)))
return hex.EncodeToString(hasher.Sum(nil))
}
func (h *Hasher) Checksum(hash string) string {
first := sha256.Sum256([]byte(hash))
second := sha256.Sum256(first[:])
return hex.EncodeToString(second[:4])
}
func (h *Hasher) HexTime(utime ...int64) string {
var t int64
if len(utime) > 0 {
t = utime[0]
} else {
t = h.Utime()
}
return fmt.Sprintf("%0*x", HexTimeSize, t)
}
func (h *Hasher) TimeHash(obj interface{}, timestamp int64) string {
return h.HexTime(timestamp) + h.Hash(obj)
}
func (h *Hasher) ToTimeHash(hash string) string {
return hash[:HexTimeSize]
}
func (h *Hasher) TimeHashValidity(hash string) bool {
return len(hash) == TimeHashSize && h.IsHex(hash)
}
func (h *Hasher) IDHash(obj interface{}) string {
hash := h.ShortHash(obj)
checksum := h.Checksum(hash)
return hash + checksum
}
func (h *Hasher) IDHashValidity(id string) bool {
if len(id) != IDHashSize {
return false
}
hash := id[:len(id)-4]
checksum := id[len(id)-4:]
return h.Checksum(hash) == checksum
}
func (h *Hasher) FillHash(hash string) string {
if len(hash) < StatusHashSize {
return fmt.Sprintf("%-*s", StatusHashSize, hash)
}
return hash
}
func (h *Hasher) StatusHash(writer, space, attr, key string) string {
if len(key) > StatusKeySize || !h.IsHex(key) {
return ""
}
return h.StatusPrefix(writer, space, attr) + key
}
func (h *Hasher) StatusPrefix(writer, space, attr string) string {
return h.Hash(writer + space + attr)
}
func (h *Hasher) SpaceID(writer, space string) string {
return h.Hash([]string{writer, space})
}
func (h *Hasher) String(obj interface{}) string {
switch v := obj.(type) {
case string:
return v
case []byte:
return string(v)
default:
b, _ := json.Marshal(v)
return string(b)
}
}
func (h *Hasher) IsHex(hexStr string) bool {
matched, _ := regexp.MatchString("^[0-9a-fA-F]+$", hexStr)
return matched
}
func (h *Hasher) MinimumStatusHash() string {
return strings.Repeat("0", StatusHashSize)
}
func (h *Hasher) MaximumStatusHash() string {
return strings.Repeat("f", StatusHashSize)
}
func (h *Hasher) Utime() int64 {
return time.Now().UnixNano() / int64(time.Microsecond)
}