m2pool_core/internal/utility/utility.go

425 lines
11 KiB
Go
Raw Normal View History

2025-04-10 07:27:24 +00:00
// utility.go
package utility
import (
"encoding/binary"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"strconv"
"bytes"
"log"
"math"
"math/big"
"os"
"os/exec"
"strings"
"time"
"github.com/zeromq/goczmq"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
"gopkg.in/natefinch/lumberjack.v2"
)
const BITCOIND_ZMQ_HASHBLOCK string = "hashblock"
type CoinConfig struct {
Coin string `json:"coin"`
}
type ZmqConfig struct {
Pub string `json:"pub"`
Sub string `json:"sub"`
}
type RedisConfig struct {
Addr string `json:"addr"`
Password string `json:"password"`
DB int `json:"db"`
}
type LogRotateConfig struct {
MaxSize int `json:"maxsize"`
MaxBackups int `json:"maxbackups"`
MaxAge int `json:"maxage"`
Compress bool `json:"compress"`
}
func InitLogg(zaplog *zap.Config, rotate *LogRotateConfig, coinname string, modulename string) (*zap.Logger, *lumberjack.Logger, error) {
os.MkdirAll("logs/"+coinname, os.ModePerm)
logfile := "./logs/" + coinname + "/" + modulename + ".log"
logRotate := &lumberjack.Logger{
Filename: logfile,
MaxSize: rotate.MaxSize,
MaxBackups: rotate.MaxBackups,
MaxAge: rotate.MaxAge,
Compress: rotate.Compress,
}
zaplog.EncoderConfig = zap.NewProductionEncoderConfig()
zaplog.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
opath := []string{"", ""}
opath[0] = zaplog.OutputPaths[0]
opath[1] = logfile
zaplog.OutputPaths = opath
l, err := zaplog.Build(
zap.WrapCore(func(core zapcore.Core) zapcore.Core {
return zapcore.NewCore(
zapcore.NewJSONEncoder(zaplog.EncoderConfig),
zapcore.AddSync(logRotate),
zaplog.Level,
)
}),
)
if err != nil {
log.Fatal("[gbt]", err.Error())
return nil, nil, err
}
return l, logRotate, nil
}
func GetCoin(config_file string) string {
var config CoinConfig
data, err := ioutil.ReadFile(config_file)
if err != nil {
panic(err.Error())
}
if err = json.Unmarshal(data, &config); err != nil {
panic(err.Error())
}
return config.Coin
}
func confirmXPubSubscriptions(pub *goczmq.Channeler, count int) {
for i := 0; i < count; i++ {
select {
case <-pub.RecvChan:
case <-time.After(time.Second * 2):
log.Println("confirmXPubSubscriptions, timeout")
}
}
}
func InitZmqPub(pub_host string) *goczmq.Sock {
pub_ch, err := goczmq.NewXPub(pub_host)
if err != nil {
log.Fatal("[server]", zap.String("NewXPub", "zmq pub create failed!"))
}
//pub_ch.SetMaxmsgsize(1024 * 1024 * 8)
return pub_ch
}
func InitZmqSub(sub_to string, topic string) *goczmq.Sock {
sub_ch, err := goczmq.NewSub(sub_to, topic)
if err != nil {
log.Fatal("[server]", zap.String("NewSub", "zmq sub connect failed!"))
}
//sub_ch.SetMaxmsgsize(1024 * 1024 * 8)
return sub_ch
}
func InitZmqPush(sub_to string) *goczmq.Sock {
push_ch, err := goczmq.NewPush(sub_to)
if err != nil {
log.Fatal("[server]", zap.String("NewPushChanneler", "zmq push connect failed!"))
}
//push_ch.Bind(sub_to)
//push_ch.SetMaxmsgsize(1024 * 1024 * 8)
return push_ch
}
func InitZmqPull(sub_to string) *goczmq.Sock {
pull_ch, err := goczmq.NewPull(sub_to)
if err != nil {
log.Fatal("[server]", zap.String("InitZmqPull", "zmq pull connect failed! "+err.Error()))
}
//pull_ch.SetMaxmsgsize(1024 * 1024 * 8)
return pull_ch
}
func Int32ToString(n uint32) string {
buf := [11]byte{}
pos := len(buf)
i := int64(n)
signed := i < 0
if signed {
i = -i
}
for {
pos--
buf[pos], i = '0'+byte(i%10), i/10
if i == 0 {
if signed {
pos--
buf[pos] = '-'
}
return string(buf[pos:])
}
}
}
func ByteToUint32(bytes []byte) uint32 {
return binary.LittleEndian.Uint32(bytes)
}
func Reverse_string(instr string) string {
var outstr string = ""
for i := 0; i < len(instr)/2; i++ {
outstr = outstr + instr[len(instr)-i*2-2:len(instr)-i*2]
}
return outstr
}
// CompactToBig converts a compact representation of a whole number N to an
// unsigned 32-bit number. The representation is similar to IEEE754 floating
// point numbers.
//
// Like IEEE754 floating point, there are three basic components: the sign,
// the exponent, and the mantissa. They are broken out as follows:
//
// - the most significant 8 bits represent the unsigned base 256 exponent
// - bit 23 (the 24th bit) represents the sign bit
// - the least significant 23 bits represent the mantissa
//
// -------------------------------------------------
// | Exponent | Sign | Mantissa |
// -------------------------------------------------
// | 8 bits [31-24] | 1 bit [23] | 23 bits [22-00] |
// -------------------------------------------------
//
// The formula to calculate N is:
//
// N = (-1^sign) * mantissa * 256^(exponent-3)
//
// This compact form is only used in bitcoin to encode unsigned 256-bit numbers
// which represent difficulty targets, thus there really is not a need for a
// sign bit, but it is implemented here to stay consistent with bitcoind.
func CompactToBig(compact uint32) *big.Int {
// Extract the mantissa, sign bit, and exponent.
mantissa := compact & 0x007fffff
isNegative := compact&0x00800000 != 0
exponent := uint(compact >> 24)
// Since the base for the exponent is 256, the exponent can be treated
// as the number of bytes to represent the full 256-bit number. So,
// treat the exponent as the number of bytes and shift the mantissa
// right or left accordingly. This is equivalent to:
// N = mantissa * 256^(exponent-3)
var bn *big.Int
if exponent <= 3 {
mantissa >>= 8 * (3 - exponent)
bn = big.NewInt(int64(mantissa))
} else {
bn = big.NewInt(int64(mantissa))
bn.Lsh(bn, 8*(exponent-3))
}
// Make it negative if the sign bit is set.
if isNegative {
bn = bn.Neg(bn)
}
return bn
}
const truediffone float64 = 26959535291011309493156476344723991336010898738574164086137773096960.0
const bits192 float64 = 6277101735386680763835789423207666416102355444464034512896.0
const bits128 float64 = 340282366920938463463374607431768211456.0
const bits64 float64 = 18446744073709551616.0
func target2float(target []byte) float64 {
var b64 float64 = float64(binary.LittleEndian.Uint64(target[24:32])) * bits192
b64 += (float64(binary.LittleEndian.Uint64(target[16:24])) * bits128)
b64 += (float64(binary.LittleEndian.Uint64(target[8:16])) * bits64)
b64 += (float64(binary.LittleEndian.Uint64(target[0:8])))
return b64
}
// convert target to difficulty
func Target2Diff(target []byte) float64 {
//var f64 float64 = truediffone
max, _ := new(big.Int).SetString("00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16) // 2 ^ 256 -1
//max, _ := new(big.Int).SetString("00000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)
maxf, _ := new(big.Float).SetString(max.String())
f64, _ := maxf.Float64()
var fcut64 float64 = target2float(target)
//log.Println("diff", f64, fcut64, f64/fcut64)
return f64 / fcut64
}
func DiffToTarget(diff float64 /*, powLimit *big.Int*/) (*big.Int, error) {
if diff <= 0 {
return nil, fmt.Errorf("invalid pool difficulty %v (0 or less than "+
"zero passed)", diff)
}
// Round down in the case of a non-integer diff since we only support
// ints (unless diff < 1 since we don't allow 0)..
if diff <= 1 {
diff = 1
} else {
diff = math.Floor(diff)
}
divisor := new(big.Int).SetInt64(int64(diff))
//max, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)
max, _ := new(big.Int).SetString("00000000FFFF0000000000000000000000000000000000000000000000000000", 16) // BTC -> MAX_TARGET
target := new(big.Int)
//log.Println("target calc", hex.EncodeToString(max.Bytes()), hex.EncodeToString(divisor.Bytes()))
target.Div(max, divisor)
return target, nil
}
func Convert_big_endian(src []byte) []byte {
var dst []byte = make([]byte, 32)
for i := 0; i < 8; i++ {
dst[0+4*i] = src[3+4*i]
dst[1+4*i] = src[2+4*i]
dst[2+4*i] = src[1+4*i]
dst[3+4*i] = src[0+4*i]
}
return dst
}
func ReverseS(s string) (string, error) {
a := strings.Split(s, "")
sRev := ""
if len(a)%2 != 0 {
return "", fmt.Errorf("Incorrect input length")
}
for i := 0; i < len(a); i += 2 {
tmp := []string{a[i], a[i+1], sRev}
sRev = strings.Join(tmp, "")
}
return sRev, nil
}
func Reverse(src []byte) []byte {
dst := make([]byte, len(src))
for i := len(src); i > 0; i-- {
dst[len(src)-i] = src[i-1]
}
return dst
}
func Uint32ToByte(targetu uint32) []byte {
bytes := make([]byte, 4)
binary.LittleEndian.PutUint32(bytes, targetu)
return bytes
}
func Uint32ToByteBig(targetu uint32) []byte {
bytes := make([]byte, 4)
binary.BigEndian.PutUint32(bytes, targetu)
return bytes
}
func ExecShellCmd(s string) (string, error) {
cmd := exec.Command("/bin/bash", "-c", s)
var out bytes.Buffer
cmd.Stdout = &out
err := cmd.Run()
if err != nil {
fmt.Println(err)
}
//fmt.Println(out.String(), s)
return out.String(), err
}
func BytesToHexStr(b []byte) string {
hexString := hex.EncodeToString(b)
return hexString
}
func NormalStrToHexStr(s string) string {
hexStr := fmt.Sprintf("%x", s) // 将字符串转换为16进制字符串
return hexStr
}
func HexStrToBytes(s string) []byte {
// 将16进制字符串转换为[]byte
bytes, err := hex.DecodeString(s)
if err != nil {
log.Fatal(err)
}
return bytes
}
func ChainIndexStr(fromGroup uint32, toGroup uint32) string {
return fmt.Sprintf("%d -> %d", fromGroup, toGroup)
}
func AlphDiff1Target() *big.Int {
// 计算 2^226 - 1
result := new(big.Int).Lsh(big.NewInt(1), 226) // 1 << 226
result.Sub(result, big.NewInt(1)) // 2^226 - 1
return result
}
// fromBuffer: 将字节切片解析为大整数
func fromBuffer(buf []byte) *big.Int {
return new(big.Int).SetBytes(buf)
}
func AlphShareDiff(hash []byte) float64 {
hashBigNum := fromBuffer(hash)
diff1Target := AlphDiff1Target()
temp := new(big.Int).Mul(diff1Target, big.NewInt(1024))
result := new(big.Int).Div(temp, hashBigNum)
finalResult := new(big.Float).SetInt(result)
finalResult.Quo(finalResult, big.NewFloat(1024.0))
diffStr := finalResult.Text('f', 8)
diff, err := strconv.ParseFloat(diffStr, 64)
if err != nil {
log.Fatal(err)
return 0
}
return diff
}
func AlphDiffToTarget(diff float64) (*big.Int, error) {
if diff <= 0 {
return nil, fmt.Errorf("invalid pool difficulty %v (0 or less than "+
"zero passed)", diff)
}
// Round down in the case of a non-integer diff since we only support
// ints (unless diff < 1 since we don't allow 0)..
if diff <= 1 {
diff = 1
} else {
diff = math.Floor(diff)
}
divisor := new(big.Int).SetInt64(int64(diff))
//max, _ := new(big.Int).SetString("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16)
max, _ := new(big.Int).SetString("000000003FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", 16) //
target := new(big.Int)
//log.Println("target calc", hex.EncodeToString(max.Bytes()), hex.EncodeToString(divisor.Bytes()))
target.Div(max, divisor)
return target, nil
}
// 结果等同于上面的AlphDiffToTarget方法
func AlphDiffToTarget2(diff float64) string {
alphTarget1Diff := new(big.Int).Lsh(big.NewInt(1), 226)
alphTarget1Diff.Sub(alphTarget1Diff, big.NewInt(1)) // 难度1的 target
// 直接在原变量上操作,避免不必要的变量创建
alphTarget1Diff.Mul(alphTarget1Diff, big.NewInt(1024))
// 计算 difficulty * 1024 并转换为 big.Int
ceilDifficultyInt := new(big.Int).SetUint64(uint64(math.Ceil(diff * 1024)))
// 计算最终 target
target := new(big.Int).Div(alphTarget1Diff, ceilDifficultyInt)
return target.Text(16)
}