func SerializedCompressedToAddress(key []byte) string {
	publicKey, err := easyecc.NewPublicFromSerializedCompressed(key)
	if err != nil {
		return "**invalid key**"
	}
	return publicKey.Address()
}
SerializedCompressedToAddress is a convenience function which converts
serialized compressed representation of the private key to its address (which is shorter).
If the key is invalid, the return string will contain an error message.
package util
import (
	"context"
	"crypto/sha256"
	"fmt"
	"hash"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
	"strings"
	"syscall"
	"time"
	"github.com/btcsuite/btcutil/base58"
	"github.com/regnull/easyecc"
	"github.com/regnull/ubikom/pb"
	"golang.org/x/crypto/ripemd160"
	"golang.org/x/term"
	"google.golang.org/grpc/codes"
	"google.golang.org/grpc/status"
	"gopkg.in/yaml.v2"
)
const (
	allowedChars      = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-_"
	defaultHomeSubDir = ".ubikom"
	defaultKeyFile    = "key"
)
func NowMs() int64 {
	return time.Now().UnixNano() / 1000000
}
func TimeFromMs(ts int64) time.Time {
	return time.Unix(ts/1000, (ts%1000)*1000000)
}
func Hash256(data []byte) []byte {
	h := sha256.Sum256(data)
	h1 := sha256.Sum256(h[:])
	return h1[:]
}
func calcHash(buf []byte, hasher hash.Hash) []byte {
	hasher.Write(buf)
	return hasher.Sum(nil)
}
func Hash160(buf []byte) []byte {
	return calcHash(calcHash(buf, sha256.New()), ripemd160.New())
}
func ValidateName(name string) bool {
	if len(name) < 3 || len(name) > 64 {
		return false
	}
	for _, c := range name {
		if !strings.ContainsRune(allowedChars, c) {
			return false
		}
	}
	return true
}
func GetDefaultKeyLocation() (string, error) {
	homeDir, err := os.UserHomeDir()
	if err != nil {
		return "", fmt.Errorf("failed to get home directory, %w", err)
	}
	dir := path.Join(homeDir, defaultHomeSubDir)
	keyFile := path.Join(dir, defaultKeyFile)
	return keyFile, nil
}
func GetConfigFileLocation(location string) (string, error) {
	defaultFileName := "ubikom.conf"
	if location != "" {
		s, err := os.Stat(location)
		if err != nil {
			return "", fmt.Errorf("config file doesn't exist: %w", err)
		}
		if s.IsDir() {
			return "", fmt.Errorf("config file location points to a directory")
		}
		return location, nil
	}
	if stat, err := os.Stat(defaultFileName); err == nil && !stat.IsDir() {
		return defaultFileName, nil
	}
	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err == nil {
		configPath := path.Join(dir, defaultFileName)
		if stat, err := os.Stat(configPath); err == nil && !stat.IsDir() {
			return configPath, nil
		}
	}
	if strings.HasSuffix(os.Args[0], "main") || strings.HasSuffix(os.Args[0], "__debug_bin") {
		configPath := path.Join(dir, "..", "..", "config", defaultFileName)
		if stat, err := os.Stat(configPath); err == nil && !stat.IsDir() {
			return configPath, nil
		}
		wd, err := os.Getwd()
		if err == nil {
			configPath = path.Join(wd, "..", "..", "config", defaultFileName)
			if stat, err := os.Stat(configPath); err == nil && !stat.IsDir() {
				return configPath, nil
			}
		}
	}
	return "", fmt.Errorf("config file not found")
}
func FindAndParseConfigFile(configFile string, out interface{}) error {
	configFile, err := GetConfigFileLocation(configFile)
	if err != nil {
		return fmt.Errorf("config file not found: %w", err)
	}
	fmt.Printf("using config file: %s\n", configFile)
	config, err := ioutil.ReadFile(configFile)
	if err != nil {
		return fmt.Errorf("config file not found")
	}
	err = yaml.Unmarshal(config, out)
	if err != nil {
		return fmt.Errorf("failed to parse config file: %w", err)
	}
	return nil
}