add aleo mining code(untested)
This commit is contained in:
parent
c065a36142
commit
6a9ba51a73
|
@ -0,0 +1,524 @@
|
|||
package aleo
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
// 网络常量
|
||||
TcpEndpoint = "127.0.0.1:3030"
|
||||
RpcEndpoint = "http://127.0.0.1:4133"
|
||||
|
||||
// 消息类型
|
||||
MsgHandshake uint16 = 1
|
||||
MsgHandshakeResponse uint16 = 2
|
||||
MsgPuzzleRequest uint16 = 9
|
||||
MsgPuzzleResponse uint16 = 10
|
||||
MsgUnconfirmedSolution uint16 = 11
|
||||
|
||||
// 协议版本
|
||||
ProtocolVersion uint32 = 17
|
||||
|
||||
// 挖矿参数
|
||||
MaxPuzzleInstances = 6 // 最大puzzle实例数
|
||||
)
|
||||
|
||||
// Prover 挖矿节点结构
|
||||
type Prover struct {
|
||||
conn net.Conn
|
||||
minerAddress []byte
|
||||
|
||||
// 最新状态
|
||||
latestEpochHash []byte
|
||||
latestBlockHeader *BlockHeader
|
||||
epochHashMutex sync.RWMutex
|
||||
headerMutex sync.RWMutex
|
||||
|
||||
// puzzle实例计数
|
||||
puzzleInstances atomic.Int32
|
||||
maxPuzzleInstances uint32
|
||||
|
||||
// 停止信号
|
||||
shutdown chan struct{}
|
||||
}
|
||||
|
||||
// BlockHeader 区块头结构
|
||||
type BlockHeader struct {
|
||||
Height uint32
|
||||
PreviousHash [32]byte
|
||||
Timestamp uint64
|
||||
CoinbaseTarget uint64
|
||||
ProofTarget uint64
|
||||
TransactionsRoot [32]byte
|
||||
StateRoot [32]byte
|
||||
}
|
||||
|
||||
// Solution 挖矿解结构
|
||||
type Solution struct {
|
||||
ID [32]byte
|
||||
EpochHash []byte
|
||||
Nonce uint64
|
||||
Address []byte
|
||||
Proof []byte
|
||||
}
|
||||
|
||||
func NewProver(minerAddress []byte) (*Prover, error) {
|
||||
// 计算最大puzzle实例数
|
||||
maxInstances := uint32(runtime.NumCPU() - 2)
|
||||
if maxInstances < 1 {
|
||||
maxInstances = 1
|
||||
}
|
||||
if maxInstances > MaxPuzzleInstances {
|
||||
maxInstances = MaxPuzzleInstances
|
||||
}
|
||||
|
||||
prover := &Prover{
|
||||
minerAddress: minerAddress,
|
||||
maxPuzzleInstances: maxInstances,
|
||||
shutdown: make(chan struct{}),
|
||||
}
|
||||
|
||||
// 连接节点
|
||||
if err := prover.connect(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return prover, nil
|
||||
}
|
||||
|
||||
func (p *Prover) connect() error {
|
||||
conn, err := net.Dial("tcp", TcpEndpoint)
|
||||
if err != nil {
|
||||
return fmt.Errorf("连接失败: %v", err)
|
||||
}
|
||||
p.conn = conn
|
||||
|
||||
// 执行握手
|
||||
if err := p.performHandshake(); err != nil {
|
||||
conn.Close()
|
||||
return fmt.Errorf("握手失败: %v", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// mining/message.go
|
||||
func (p *Prover) sendMessage(msgType uint16, payload []byte) error {
|
||||
header := make([]byte, 6)
|
||||
binary.BigEndian.PutUint16(header[0:2], msgType)
|
||||
binary.BigEndian.PutUint32(header[2:6], ProtocolVersion)
|
||||
|
||||
if _, err := p.conn.Write(header); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if payload != nil {
|
||||
if _, err := p.conn.Write(payload); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Prover) receiveMessage() (uint16, []byte, error) {
|
||||
header := make([]byte, 6)
|
||||
if _, err := io.ReadFull(p.conn, header); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
|
||||
msgType := binary.BigEndian.Uint16(header[0:2])
|
||||
// protocolVersion := binary.BigEndian.Uint32(header[2:6])
|
||||
|
||||
var payload []byte
|
||||
if msgType == MsgPuzzleResponse {
|
||||
// 读取epoch hash
|
||||
epochHash := make([]byte, 32)
|
||||
if _, err := io.ReadFull(p.conn, epochHash); err != nil {
|
||||
return msgType, nil, err
|
||||
}
|
||||
|
||||
// 读取block header
|
||||
lenBuf := make([]byte, 4)
|
||||
if _, err := io.ReadFull(p.conn, lenBuf); err != nil {
|
||||
return msgType, nil, err
|
||||
}
|
||||
headerLen := binary.BigEndian.Uint32(lenBuf)
|
||||
|
||||
blockHeader := make([]byte, headerLen)
|
||||
if _, err := io.ReadFull(p.conn, blockHeader); err != nil {
|
||||
return msgType, nil, err
|
||||
}
|
||||
|
||||
payload = append(epochHash, blockHeader...)
|
||||
}
|
||||
|
||||
return msgType, payload, nil
|
||||
}
|
||||
|
||||
func (p *Prover) getPuzzleTask() error {
|
||||
// 发送请求
|
||||
if err := p.sendMessage(MsgPuzzleRequest, nil); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 接收响应
|
||||
msgType, data, err := p.receiveMessage()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if msgType != MsgPuzzleResponse {
|
||||
return fmt.Errorf("unexpected message type: %d", msgType)
|
||||
}
|
||||
|
||||
// 解析数据
|
||||
epochHash := data[0:32]
|
||||
header, err := parseBlockHeader(data[32:])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 更新状态
|
||||
p.epochHashMutex.Lock()
|
||||
p.latestEpochHash = epochHash
|
||||
p.epochHashMutex.Unlock()
|
||||
|
||||
p.headerMutex.Lock()
|
||||
p.latestBlockHeader = header
|
||||
p.headerMutex.Unlock()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Prover) StartMining() error {
|
||||
// 启动多个puzzle实例
|
||||
for i := uint32(0); i < p.maxPuzzleInstances; i++ {
|
||||
go p.puzzleLoop()
|
||||
}
|
||||
|
||||
// 定期获取新任务
|
||||
go p.taskLoop()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (p *Prover) puzzleLoop() {
|
||||
p.puzzleInstances.Add(1)
|
||||
defer p.puzzleInstances.Add(-1)
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-p.shutdown:
|
||||
return
|
||||
default:
|
||||
// 获取当前状态
|
||||
p.epochHashMutex.RLock()
|
||||
epochHash := p.latestEpochHash
|
||||
p.epochHashMutex.RUnlock()
|
||||
|
||||
p.headerMutex.RLock()
|
||||
header := p.latestBlockHeader
|
||||
p.headerMutex.RUnlock()
|
||||
|
||||
if epochHash != nil && header != nil {
|
||||
// 尝试找到解
|
||||
solution, err := p.findSolution(epochHash, header.ProofTarget)
|
||||
if err == nil {
|
||||
// 提交解
|
||||
if err := p.submitSolution(solution); err != nil {
|
||||
log.Printf("提交解失败: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prover) taskLoop() {
|
||||
ticker := time.NewTicker(time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-p.shutdown:
|
||||
return
|
||||
case <-ticker.C:
|
||||
if err := p.getPuzzleTask(); err != nil {
|
||||
log.Printf("获取任务失败: %v", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Prover) submitSolution(solution *Solution) error {
|
||||
// 序列化solution
|
||||
var buf bytes.Buffer
|
||||
|
||||
buf.Write(solution.ID[:])
|
||||
buf.Write(solution.EpochHash)
|
||||
|
||||
nonceBytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(nonceBytes, solution.Nonce)
|
||||
buf.Write(nonceBytes)
|
||||
|
||||
buf.Write(solution.Address)
|
||||
buf.Write(solution.Proof)
|
||||
|
||||
// 发送消息
|
||||
return p.sendMessage(MsgUnconfirmedSolution, buf.Bytes())
|
||||
}
|
||||
|
||||
func main() {
|
||||
// 创建矿工地址
|
||||
minerAddress := []byte("your_miner_address_here")
|
||||
|
||||
// 创建Prover
|
||||
prover, err := NewProver(minerAddress)
|
||||
if err != nil {
|
||||
log.Fatalf("创建Prover失败: %v", err)
|
||||
}
|
||||
|
||||
// 启动挖矿
|
||||
if err := prover.StartMining(); err != nil {
|
||||
log.Fatalf("启动挖矿失败: %v", err)
|
||||
}
|
||||
|
||||
// 等待中断信号
|
||||
sigChan := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sigChan
|
||||
|
||||
// 优雅关闭
|
||||
close(prover.shutdown)
|
||||
}
|
||||
|
||||
func (p *Prover) findSolution(epochHash []byte, proofTarget uint64) (*Solution, error) {
|
||||
var nonce uint64 = 0
|
||||
maxNonce := ^uint64(0) // 最大uint64值
|
||||
|
||||
// 将proofTarget转换为字节数组用于比较
|
||||
targetBytes := make([]byte, 32)
|
||||
targetBytes[0] = byte(proofTarget)
|
||||
|
||||
for nonce < maxNonce {
|
||||
// 计算proof
|
||||
proof, err := p.computeProof(epochHash, p.minerAddress, nonce)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("计算proof失败: %v", err)
|
||||
}
|
||||
|
||||
// 验证proof是否满足难度要求
|
||||
if p.verifyProof(proof, targetBytes) {
|
||||
// 构造解
|
||||
solution := &Solution{
|
||||
EpochHash: epochHash,
|
||||
Nonce: nonce,
|
||||
Address: p.minerAddress,
|
||||
Proof: proof,
|
||||
}
|
||||
|
||||
// 计算解的ID
|
||||
id, err := p.computeSolutionID(solution)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("计算解ID失败: %v", err)
|
||||
}
|
||||
copy(solution.ID[:], id)
|
||||
|
||||
log.Printf("找到有效解 - Nonce: %d, ID: %x", nonce, id)
|
||||
return solution, nil
|
||||
}
|
||||
|
||||
// 每处理1000个nonce检查一次是否需要退出
|
||||
if nonce%1000 == 0 {
|
||||
select {
|
||||
case <-p.shutdown:
|
||||
return nil, fmt.Errorf("挖矿已停止")
|
||||
default:
|
||||
}
|
||||
}
|
||||
|
||||
nonce++
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("未找到有效解")
|
||||
}
|
||||
|
||||
// computeProof 计算工作量证明
|
||||
func (p *Prover) computeProof(epochHash []byte, address []byte, nonce uint64) ([]byte, error) {
|
||||
hasher := sha256.New()
|
||||
|
||||
// 写入epoch hash
|
||||
if _, err := hasher.Write(epochHash); err != nil {
|
||||
return nil, fmt.Errorf("写入epoch hash失败: %v", err)
|
||||
}
|
||||
|
||||
// 写入地址
|
||||
if _, err := hasher.Write(address); err != nil {
|
||||
return nil, fmt.Errorf("写入地址失败: %v", err)
|
||||
}
|
||||
|
||||
// 写入nonce (小端字节序)
|
||||
nonceBytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(nonceBytes, nonce)
|
||||
if _, err := hasher.Write(nonceBytes); err != nil {
|
||||
return nil, fmt.Errorf("写入nonce失败: %v", err)
|
||||
}
|
||||
|
||||
return hasher.Sum(nil), nil
|
||||
}
|
||||
|
||||
// verifyProof 验证工作量证明是否满足难度要求
|
||||
func (p *Prover) verifyProof(proof []byte, target []byte) bool {
|
||||
// 比较proof和target
|
||||
// proof必须小于target才是有效的
|
||||
for i := 0; i < len(proof) && i < len(target); i++ {
|
||||
if proof[i] < target[i] {
|
||||
return true
|
||||
}
|
||||
if proof[i] > target[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// computeSolutionID 计算解的唯一标识符
|
||||
func (p *Prover) computeSolutionID(solution *Solution) ([]byte, error) {
|
||||
hasher := sha256.New()
|
||||
|
||||
// 写入所有字段
|
||||
if _, err := hasher.Write(solution.EpochHash); err != nil {
|
||||
return nil, fmt.Errorf("写入epoch hash失败: %v", err)
|
||||
}
|
||||
|
||||
if _, err := hasher.Write(solution.Address); err != nil {
|
||||
return nil, fmt.Errorf("写入地址失败: %v", err)
|
||||
}
|
||||
|
||||
nonceBytes := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(nonceBytes, solution.Nonce)
|
||||
if _, err := hasher.Write(nonceBytes); err != nil {
|
||||
return nil, fmt.Errorf("写入nonce失败: %v", err)
|
||||
}
|
||||
|
||||
if _, err := hasher.Write(solution.Proof); err != nil {
|
||||
return nil, fmt.Errorf("写入proof失败: %v", err)
|
||||
}
|
||||
|
||||
return hasher.Sum(nil), nil
|
||||
}
|
||||
|
||||
type PuzzleInstances struct {
|
||||
count uint32
|
||||
}
|
||||
|
||||
// Sub 原子减少实例计数
|
||||
func (p *PuzzleInstances) Sub(delta uint32) {
|
||||
atomic.AddUint32(&p.count, ^(delta - 1))
|
||||
}
|
||||
|
||||
// Add 原子增加实例计数
|
||||
func (p *PuzzleInstances) Add(delta uint32) {
|
||||
atomic.AddUint32(&p.count, delta)
|
||||
}
|
||||
|
||||
// Get 获取当前实例数
|
||||
func (p *PuzzleInstances) Get() uint32 {
|
||||
return atomic.LoadUint32(&p.count)
|
||||
}
|
||||
|
||||
func parseBlockHeader(data []byte) (*BlockHeader, error) {
|
||||
if len(data) < 96 { // 最小区块头长度
|
||||
return nil, fmt.Errorf("区块头数据太短")
|
||||
}
|
||||
|
||||
reader := bytes.NewReader(data)
|
||||
header := &BlockHeader{}
|
||||
|
||||
// 读取高度 (4字节)
|
||||
if err := binary.Read(reader, binary.LittleEndian, &header.Height); err != nil {
|
||||
return nil, fmt.Errorf("读取区块高度失败: %v", err)
|
||||
}
|
||||
|
||||
// 读取前一个区块哈希 (32字节)
|
||||
if _, err := io.ReadFull(reader, header.PreviousHash[:]); err != nil {
|
||||
return nil, fmt.Errorf("读取前一个区块哈希失败: %v", err)
|
||||
}
|
||||
|
||||
// 读取时间戳 (8字节)
|
||||
if err := binary.Read(reader, binary.LittleEndian, &header.Timestamp); err != nil {
|
||||
return nil, fmt.Errorf("读取时间戳失败: %v", err)
|
||||
}
|
||||
|
||||
// 读取coinbase目标值 (8字节)
|
||||
if err := binary.Read(reader, binary.LittleEndian, &header.CoinbaseTarget); err != nil {
|
||||
return nil, fmt.Errorf("读取coinbase目标值失败: %v", err)
|
||||
}
|
||||
|
||||
// 读取proof目标值 (8字节)
|
||||
if err := binary.Read(reader, binary.LittleEndian, &header.ProofTarget); err != nil {
|
||||
return nil, fmt.Errorf("读取proof目标值失败: %v", err)
|
||||
}
|
||||
|
||||
// 读取交易根 (32字节)
|
||||
if _, err := io.ReadFull(reader, header.TransactionsRoot[:]); err != nil {
|
||||
return nil, fmt.Errorf("读取交易根失败: %v", err)
|
||||
}
|
||||
|
||||
// 读取状态根 (32字节)
|
||||
if _, err := io.ReadFull(reader, header.StateRoot[:]); err != nil {
|
||||
return nil, fmt.Errorf("读取状态根失败: %v", err)
|
||||
}
|
||||
|
||||
return header, nil
|
||||
}
|
||||
|
||||
func (p *Prover) performHandshake() error {
|
||||
// 发送握手消息
|
||||
if err := p.sendMessage(MsgHandshake, nil); err != nil {
|
||||
return fmt.Errorf("发送握手消息失败: %v", err)
|
||||
}
|
||||
|
||||
// 设置读取超时
|
||||
if err := p.conn.SetReadDeadline(time.Now().Add(10 * time.Second)); err != nil {
|
||||
return fmt.Errorf("设置读取超时失败: %v", err)
|
||||
}
|
||||
defer p.conn.SetReadDeadline(time.Time{}) // 清除超时
|
||||
|
||||
// 接收握手响应
|
||||
msgType, payload, err := p.receiveMessage()
|
||||
if err != nil {
|
||||
return fmt.Errorf("接收握手响应失败: %v", err)
|
||||
}
|
||||
|
||||
// 验证消息类型
|
||||
if msgType != MsgHandshakeResponse {
|
||||
return fmt.Errorf("收到意外的消息类型: %d, 期望类型: %d", msgType, MsgHandshakeResponse)
|
||||
}
|
||||
|
||||
// 验证协议版本
|
||||
if len(payload) >= 4 {
|
||||
version := binary.BigEndian.Uint32(payload[0:4])
|
||||
if version != ProtocolVersion {
|
||||
return fmt.Errorf("协议版本不匹配: 收到 %d, 期望 %d", version, ProtocolVersion)
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("握手成功 - 协议版本: %d", ProtocolVersion)
|
||||
return nil
|
||||
}
|
|
@ -9,7 +9,6 @@ import (
|
|||
"pool/internal/utility"
|
||||
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
kaspad "github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"github.com/zeromq/goczmq"
|
||||
"go.uber.org/zap"
|
||||
|
@ -58,12 +57,12 @@ type GbtContext struct {
|
|||
|
||||
PushCh *goczmq.Sock
|
||||
|
||||
Started bool
|
||||
Config GbtConfig
|
||||
Client *rpcclient.Client
|
||||
ClientAlph *net.Conn
|
||||
ClientAlphApi *http.HttpClient
|
||||
ClientEnx *kaspad.RPCClient
|
||||
Started bool
|
||||
Config GbtConfig
|
||||
Client *rpcclient.Client
|
||||
ClientAlph *net.Conn
|
||||
ClientAlphApi *http.HttpClient
|
||||
// ClientEnx *kaspad.RPCClient
|
||||
ExitNotifyChan chan bool
|
||||
ExitGbtChan chan bool
|
||||
ExitBlockChan chan bool
|
||||
|
|
|
@ -1,247 +0,0 @@
|
|||
package enx
|
||||
|
||||
import (
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"pool/internal/db"
|
||||
"pool/internal/gbt/coin"
|
||||
"pool/internal/gbt/enx/templatemanager"
|
||||
"pool/internal/msg"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/pow"
|
||||
"github.com/kaspanet/kaspad/util/difficulty"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const GBT_ENX_VERSION string = "enx v1.15.2"
|
||||
|
||||
type EnxAddrConfig struct {
|
||||
Addr string `json:"addr"`
|
||||
}
|
||||
|
||||
type EnxConfig struct {
|
||||
Enx EnxAddrConfig `json:"enx"`
|
||||
}
|
||||
|
||||
type GbtEnxContext struct {
|
||||
Config EnxConfig
|
||||
GbtCtx *coin.GbtContext
|
||||
|
||||
last_time time.Time
|
||||
last_gbt EnxGbtTemplate
|
||||
|
||||
Submits float64
|
||||
|
||||
addressIndex int
|
||||
|
||||
new_block_chan chan int
|
||||
new_block_index int
|
||||
|
||||
last_height uint32
|
||||
}
|
||||
|
||||
var logg *zap.Logger
|
||||
var GbtEnxCtx GbtEnxContext
|
||||
|
||||
// Kaspa GetBlockTemplate 结构体
|
||||
type EnxGbtTemplate struct {
|
||||
Block struct {
|
||||
Header struct {
|
||||
Version int `json:"version"`
|
||||
ParentsByLevel [][]string `json:"parents_by_level"`
|
||||
HashMerkleRoot string `json:"hash_merkle_root"`
|
||||
AcceptedIdMerkleRoot string `json:"accepted_id_merkle_root"`
|
||||
UtxoCommitment string `json:"utxo_commitment"`
|
||||
Timestamp uint64 `json:"timestamp"`
|
||||
Bits uint32 `json:"bits"`
|
||||
Nonce uint64 `json:"nonce"`
|
||||
DaaScore uint64 `json:"daa_score"`
|
||||
BlueWork [3]uint64 `json:"blue_work"` // 可改为 big.Int
|
||||
BlueScore uint64 `json:"blue_score"`
|
||||
PruningPoint string `json:"pruning_point"`
|
||||
} `json:"header"`
|
||||
|
||||
Transactions []struct {
|
||||
Version int `json:"version"`
|
||||
LockTime uint64 `json:"lock_time"`
|
||||
SubnetworkId string `json:"subnetwork_id"`
|
||||
Gas uint64 `json:"gas"`
|
||||
PayLoad string `json:"payload"`
|
||||
Mass uint64 `json:"mass"`
|
||||
|
||||
Inputs []struct{} `json:"inputs"`
|
||||
Outputs []struct{} `json:"outputs"`
|
||||
|
||||
VerboseData interface{} `json:"verbose_data"`
|
||||
} `json:"transactions"`
|
||||
} `json:"block"`
|
||||
|
||||
IsSync bool `json:"is_sync"`
|
||||
}
|
||||
|
||||
type BlockCheckData struct {
|
||||
Height int
|
||||
Nonce string
|
||||
User string
|
||||
Miner string
|
||||
MinerId string
|
||||
Hash string
|
||||
SubIdx int
|
||||
}
|
||||
|
||||
type PushBlkNewMsg struct {
|
||||
Coin string `json:"coin"`
|
||||
Height int `json:"height"`
|
||||
Nonce string `json:"nonce"`
|
||||
}
|
||||
|
||||
type EnxJob struct {
|
||||
JobID uint32
|
||||
Job *externalapi.DomainBlock
|
||||
State *pow.State
|
||||
}
|
||||
|
||||
type EnxContext struct {
|
||||
GbtCtx *coin.GbtContext
|
||||
|
||||
SLock sync.Mutex
|
||||
JobID uint32
|
||||
Jobs []EnxJob
|
||||
|
||||
LastHeight uint64
|
||||
|
||||
Submits float64
|
||||
}
|
||||
|
||||
var enxCtx EnxContext
|
||||
|
||||
func update_block_confirm(gbt *GbtEnxContext) {}
|
||||
|
||||
var hashesTried uint64
|
||||
|
||||
func get_blocktemplate(gbt *GbtEnxContext, mineWhenNotSynced bool) (*externalapi.DomainBlock, *pow.State) {
|
||||
tryCount := 0
|
||||
const sleepTime = 500 * time.Millisecond
|
||||
const sleepTimeWhenNotSynced = 5 * time.Second
|
||||
|
||||
for {
|
||||
tryCount++
|
||||
|
||||
shouldLog := (tryCount-1)%10 == 0
|
||||
template, state, isSynced := templatemanager.Get()
|
||||
if template == nil {
|
||||
if shouldLog {
|
||||
logg.Info("Waiting for the initial template")
|
||||
}
|
||||
time.Sleep(sleepTime)
|
||||
continue
|
||||
}
|
||||
if !isSynced && !mineWhenNotSynced {
|
||||
if shouldLog {
|
||||
logg.Warn("Kaspad is not synced. Skipping current block template")
|
||||
}
|
||||
time.Sleep(sleepTimeWhenNotSynced)
|
||||
continue
|
||||
}
|
||||
|
||||
return template, state
|
||||
}
|
||||
}
|
||||
|
||||
func get_gbt_msg(gbt *GbtEnxContext) []byte {
|
||||
// 这里从节点获取区块模版(block template)
|
||||
dagInfoMsg, err := gbt.GbtCtx.ClientEnx.GetBlockDAGInfo()
|
||||
if err != nil {
|
||||
logg.Info("[gbt]", zap.String("GetBlockDAGInfo ", err.Error()))
|
||||
return nil
|
||||
}
|
||||
height := dagInfoMsg.VirtualDAAScore
|
||||
if err != nil {
|
||||
logg.Info("[gbt]", zap.String("GetBlockCount ", err.Error()))
|
||||
return nil
|
||||
}
|
||||
height = height + 1
|
||||
block, state := getBlockForMining(mineWhenNotSynced)
|
||||
//state.Nonce = nonce
|
||||
state.Nonce = 0
|
||||
atomic.AddUint64(&hashesTried, 1)
|
||||
|
||||
var job msg.EnxStratumJob
|
||||
enxCtx.SLock.Lock()
|
||||
job.Job_id = 0
|
||||
job.Gbt_id = enxCtx.JobID
|
||||
enxCtx.SLock.Unlock()
|
||||
|
||||
job.CurTime = uint64(state.Timestamp)
|
||||
job.Target = hex.EncodeToString(state.Target.Bytes())
|
||||
job.Bits = difficulty.BigToCompact(&(state.Target))
|
||||
job.Nonce = ""
|
||||
job.Extranonce1 = ""
|
||||
job.Extranonce2_size = 6
|
||||
job.Extranonce2 = ""
|
||||
job.Data = hex.EncodeToString(state.GetPrePowHash())
|
||||
job.PrevBlockHash = fmt.Sprint("%s", block.Header.DirectParents())
|
||||
job.Height = block.Header.BlueScore()
|
||||
|
||||
return []byte{}
|
||||
}
|
||||
|
||||
func getBlockForMining(mineWhenNotSynced bool) (*externalapi.DomainBlock, *pow.State) {
|
||||
tryCount := 0
|
||||
|
||||
const sleepTime = 500 * time.Millisecond
|
||||
const sleepTimeWhenNotSynced = 5 * time.Second
|
||||
|
||||
for {
|
||||
tryCount++
|
||||
|
||||
shouldLog := (tryCount-1)%10 == 0
|
||||
template, state, isSynced := templatemanager.Get()
|
||||
if template == nil {
|
||||
if shouldLog {
|
||||
logg.Info("Waiting for the initial template")
|
||||
}
|
||||
time.Sleep(sleepTime)
|
||||
continue
|
||||
}
|
||||
if !isSynced && !mineWhenNotSynced {
|
||||
if shouldLog {
|
||||
logg.Warn("Kaspad is not synced. Skipping current block template")
|
||||
}
|
||||
time.Sleep(sleepTimeWhenNotSynced)
|
||||
continue
|
||||
}
|
||||
|
||||
return template, state
|
||||
}
|
||||
}
|
||||
|
||||
func gbt_notify_running(gbt *GbtEnxContext) {}
|
||||
|
||||
func gbt_running(gbt *GbtEnxContext) {}
|
||||
|
||||
func enxInit(config *EnxConfig) {}
|
||||
|
||||
func Init(GbtCtx *coin.GbtContext, DbCtx *db.DbContext) {}
|
||||
|
||||
func Start() {
|
||||
go gbt_running(&GbtEnxCtx)
|
||||
go gbt_notify_running(&GbtEnxCtx)
|
||||
go submit_block_running(&GbtEnxCtx)
|
||||
}
|
||||
|
||||
func Stop() {
|
||||
defer close(GbtEnxCtx.new_block_chan)
|
||||
}
|
||||
|
||||
func new_block_into_db(block *GbtEnxContext, user string, miner string, minerid string, height int64, nonce string, hash string, subidx int64) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func submit_block_running(block *GbtEnxContext) {}
|
||||
|
||||
func new_job_from_gbt(gbt *GbtEnxContext, rxmsg *msg.GbtMsg) []byte { return []byte{} }
|
|
@ -1,49 +0,0 @@
|
|||
package templatemanager
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"github.com/kaspanet/kaspad/app/appmessage"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/model/externalapi"
|
||||
"github.com/kaspanet/kaspad/domain/consensus/utils/pow"
|
||||
)
|
||||
|
||||
var currentTemplate *externalapi.DomainBlock
|
||||
var currentState *pow.State
|
||||
var prevState *pow.State
|
||||
var isSynced bool
|
||||
var lock = &sync.Mutex{}
|
||||
|
||||
// Get returns the template to work on
|
||||
func Get() (*externalapi.DomainBlock, *pow.State, bool) {
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
// Shallow copy the block so when the user replaces the header it won't affect the template here.
|
||||
if currentTemplate == nil {
|
||||
return nil, nil, false
|
||||
}
|
||||
if prevState != nil {
|
||||
if currentState.Target.Cmp(&(prevState.Target)) == 0 {
|
||||
return nil, nil, false
|
||||
}
|
||||
}
|
||||
block := *currentTemplate
|
||||
state := *currentState
|
||||
prevState = currentState
|
||||
currentTemplate = nil
|
||||
return &block, &state, isSynced
|
||||
}
|
||||
|
||||
// Set sets the current template to work on
|
||||
func Set(template *appmessage.GetBlockTemplateResponseMessage) error {
|
||||
block, err := appmessage.RPCBlockToDomainBlock(template.Block)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
lock.Lock()
|
||||
defer lock.Unlock()
|
||||
currentTemplate = block
|
||||
currentState = pow.NewState(block.Header.ToMutable())
|
||||
isSynced = template.IsSynced
|
||||
return nil
|
||||
}
|
|
@ -24,7 +24,6 @@ import (
|
|||
"pool/internal/gbt/alph/http"
|
||||
"pool/internal/gbt/coin"
|
||||
"pool/internal/gbt/dgb"
|
||||
"pool/internal/gbt/enx"
|
||||
"pool/internal/gbt/grs"
|
||||
"pool/internal/gbt/mona"
|
||||
"pool/internal/gbt/nexa"
|
||||
|
@ -37,7 +36,6 @@ import (
|
|||
"time"
|
||||
|
||||
"github.com/btcsuite/btcd/rpcclient"
|
||||
kaspad "github.com/kaspanet/kaspad/infrastructure/network/rpcclient"
|
||||
"github.com/redis/go-redis/v9"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
@ -74,23 +72,23 @@ func InitClient(gbt *coin.GbtContext) error {
|
|||
gbt.ClientAlphApi = apiClient
|
||||
// blockCount := apiClient.GetBlockCount()
|
||||
return nil
|
||||
case "enx":
|
||||
host := gbt.Config.Rpc.Host
|
||||
port := gbt.Config.Rpc.Port
|
||||
client, err := kaspad.NewRPCClient(host + ":" + port)
|
||||
if err != nil {
|
||||
logg.Info("[gbt]", zap.String("rpcclient new ", err.Error()))
|
||||
return err
|
||||
}
|
||||
gbt.ClientEnx = client
|
||||
dagInfoMsg, err := client.GetBlockDAGInfo()
|
||||
if err != nil {
|
||||
logg.Info("[gbt]", zap.String("GetBlockDAGInfo ", err.Error()))
|
||||
return err
|
||||
}
|
||||
blockCount := dagInfoMsg.VirtualDAAScore
|
||||
logg.Info("[gbt]", zap.Int64("Block count ", int64(blockCount)))
|
||||
return nil
|
||||
// case "enx":
|
||||
// host := gbt.Config.Rpc.Host
|
||||
// port := gbt.Config.Rpc.Port
|
||||
// client, err := kaspad.NewRPCClient(host + ":" + port)
|
||||
// if err != nil {
|
||||
// logg.Info("[gbt]", zap.String("rpcclient new ", err.Error()))
|
||||
// return err
|
||||
// }
|
||||
// gbt.ClientEnx = client
|
||||
// dagInfoMsg, err := client.GetBlockDAGInfo()
|
||||
// if err != nil {
|
||||
// logg.Info("[gbt]", zap.String("GetBlockDAGInfo ", err.Error()))
|
||||
// return err
|
||||
// }
|
||||
// blockCount := dagInfoMsg.VirtualDAAScore
|
||||
// logg.Info("[gbt]", zap.Int64("Block count ", int64(blockCount)))
|
||||
// return nil
|
||||
default:
|
||||
var config rpcclient.ConnConfig
|
||||
if gbt.Config.Rpc.Type == "testnet" {
|
||||
|
@ -183,7 +181,6 @@ var coinobjs = []coinobj{
|
|||
{Coin: "dgbq", Init: dgb.Init, Start: dgb.Start, Stop: dgb.Stop},
|
||||
{Coin: "dgbs", Init: dgb.Init, Start: dgb.Start, Stop: dgb.Stop},
|
||||
{Coin: "alph", Init: alph.Init, Start: alph.Start, Stop: alph.Stop},
|
||||
{Coin: "enx", Init: enx.Init, Start: enx.Start, Stop: enx.Stop},
|
||||
}
|
||||
|
||||
func register_signal(dbctx *db.DbContext) {
|
||||
|
|
|
@ -1,272 +0,0 @@
|
|||
package enx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"pool/internal/msg"
|
||||
"pool/internal/server/coin"
|
||||
"pool/internal/stratum"
|
||||
"pool/internal/utility"
|
||||
|
||||
"github.com/btcsuite/btcd/wire"
|
||||
"go.uber.org/zap"
|
||||
)
|
||||
|
||||
const SERVER_ENX_VERSION string = "enx v1.15.2"
|
||||
|
||||
type ServerEnxContext struct {
|
||||
ServerCtx *coin.ServerContext
|
||||
logg *zap.Logger
|
||||
Job msg.StratumJob
|
||||
}
|
||||
|
||||
var logg *zap.Logger
|
||||
var ServerEnxCtx ServerEnxContext
|
||||
|
||||
// headerHash:本身的pow计算结果,收到后根据nonce和timestamp重新计算该值,确保该值正确
|
||||
// headerHash本身可以通过计算得出难度
|
||||
func handle_submit(miner *coin.MinerObj, id float64, miner_user string, job_id string, headerHash string, ntime string, nonce string) (bool, bool, bool) {
|
||||
var submit_item coin.BlockMsg
|
||||
/*var user_blk_item coin.UserBlockMsg*/
|
||||
var pool_blk_item coin.PoolBlkMsg
|
||||
|
||||
var blk_detail_height int64
|
||||
var blk_detail_hash string
|
||||
var blk_detail_success bool
|
||||
var blk_detail_miner_diff float64
|
||||
var blk_detail_pool_diff float64
|
||||
|
||||
if miner.Authorized != true {
|
||||
miner.ErrOthers = miner.ErrOthers + 1
|
||||
stratum.Handle_exception(miner, id, stratum.MINER_ERR_UNAUTH_WORKER)
|
||||
stratum.Send_reconnect_msg(miner)
|
||||
return false, false, false
|
||||
}
|
||||
var new_found bool = false
|
||||
var ack stratum.Submit_ack
|
||||
ack.ID = id
|
||||
ack.Result = true
|
||||
//logg.Warn("[server]", zap.String("user", miner.User), zap.String("miner", miner.Miner))
|
||||
//logg.Debug("[server]", zap.Float64("id", id), zap.String("job_id", job_id))
|
||||
//logg.Debug("[server]", zap.String("nonce2", nonce2), zap.String("ntime", ntime), zap.String("nonce", nonce))
|
||||
//stratum.UpdateJobs(miner)
|
||||
v, ok := miner.Jobs.Load(job_id)
|
||||
if ok {
|
||||
job := v.(msg.StratumJob)
|
||||
if job.Height < miner.CurHeight-1 {
|
||||
ack.Result = false
|
||||
stratum.Handle_exception(miner, id, stratum.MINER_ERR_STALED_JOB)
|
||||
miner.ErrStaleds = miner.ErrStaleds + 1
|
||||
return false, false, false
|
||||
}
|
||||
|
||||
if miner.LastNonce != nonce {
|
||||
miner.LastNonce = nonce
|
||||
if miner.ZlogInit {
|
||||
miner.Zlog.Info().Msg("height " + fmt.Sprintf("%d", job.Height) + " target " + job.Target + " extra1 " + job.Extranonce1 + " size " + fmt.Sprintf("%d", job.Extranonce2_size) + " " + miner.User + "." + miner.Miner)
|
||||
}
|
||||
}
|
||||
vb := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(vb, uint32(job.Version))
|
||||
vBuffer := bytes.NewBuffer(vb)
|
||||
binary.Read(vBuffer, binary.BigEndian, &(miner.Version))
|
||||
|
||||
job.Nonce = nonce
|
||||
job.Extranonce2 = headerHash
|
||||
|
||||
var calc_hash []byte
|
||||
var header wire.BlockHeader
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// 构造提交至节点的区块
|
||||
func Produce_block_submit(miner *coin.MinerObj, header wire.BlockHeader, job *msg.StratumJob, PowHash string, SubIdx int64) {
|
||||
}
|
||||
|
||||
// server-->miner
|
||||
func enx_parse_miner_notify(miner *coin.MinerObj, msg msg.StratumJob) int {}
|
||||
|
||||
func Init(server *coin.ServerContext) {
|
||||
ServerEnxCtx.ServerCtx = server
|
||||
logg = server.Logg
|
||||
logg.Info("[server]", zap.String("server_mona_version", SERVER_ENX_VERSION))
|
||||
coin.Init_diff_db()
|
||||
}
|
||||
|
||||
func Start() {
|
||||
|
||||
}
|
||||
|
||||
func Stop() {
|
||||
coin.DiffStop()
|
||||
}
|
||||
|
||||
func InitMiner(miner *coin.MinerObj) {}
|
||||
|
||||
func Handle_subscribe_enx(miner *coin.MinerObj, id float64, extranonce1 string) {
|
||||
stratum.Handle_subscribe(miner, id, extranonce1)
|
||||
}
|
||||
|
||||
func HandleMinerSubscribe(miner *coin.MinerObj, id float64, extranonce1 string, msg string) {
|
||||
Handle_subscribe_enx(miner, id, extranonce1)
|
||||
}
|
||||
|
||||
func HandleMinerAuth(miner *coin.MinerObj) {
|
||||
|
||||
}
|
||||
|
||||
func HandleMinerSubmit(miner *coin.MinerObj, id float64, miner_user string, job_id string, nonce2 string, ntime string, nonce string) (bool, bool, bool) {
|
||||
|
||||
accept_ok, submit_ok, handle_ok := handle_submit(miner, id, miner_user, job_id, nonce2, ntime, nonce)
|
||||
return accept_ok, submit_ok, handle_ok
|
||||
}
|
||||
|
||||
func set_difficulty(miner *coin.MinerObj) {}
|
||||
|
||||
func SetDifficulty(miner *coin.MinerObj) {
|
||||
set_difficulty(miner)
|
||||
}
|
||||
|
||||
/**
|
||||
id: int, method: string, params:[string(jobid), string(headerHash, 32 byte serialized header, 8 bytes timestamp)]
|
||||
*/
|
||||
func EnxNotify(miner *coin.MinerObj) {
|
||||
miner.TxLock.Lock()
|
||||
if !((miner.Status == coin.MINER_STATUS_AUTHORIZED) || (miner.Status == coin.MINER_STATUS_RUNNING)) {
|
||||
miner.TxLock.Unlock()
|
||||
return
|
||||
}
|
||||
miner.TxLock.Unlock()
|
||||
if miner.DifficultyNext > -1 {
|
||||
ratio := miner.DifficultyNext / miner.Difficulty
|
||||
if ratio > 1.1 || ratio < 0.9 {
|
||||
miner.Difficulty = miner.DifficultyNext
|
||||
miner.DifficultyNext = -1
|
||||
//Set_difficulty(miner)
|
||||
miner.Server.CoinCtx.SetDifficulty(miner)
|
||||
} else {
|
||||
miner.DifficultyNext = -1
|
||||
}
|
||||
}
|
||||
miner.TxLock.Lock()
|
||||
//log.Println("[server]extra1, id", miner.Job.Extranonce1, miner.Job.Job_id, miner.MinerId)
|
||||
var params [9]interface{}
|
||||
var tlist []string = make([]string, 0)
|
||||
idb := make([]byte, 4)
|
||||
binary.BigEndian.PutUint32(idb, miner.JobId)
|
||||
miner.Job.Job_id = hex.EncodeToString(idb)
|
||||
params[0] = miner.Job.Job_id
|
||||
if len(miner.Job.PrevblockS) > 0 {
|
||||
params[1] = miner.Job.PrevblockBig
|
||||
} else {
|
||||
p_big := utility.Convert_big_endian(miner.Job.Prevblock.CloneBytes())
|
||||
params[1] = hex.EncodeToString(p_big)
|
||||
}
|
||||
params[2] = miner.Job.Coinbase1
|
||||
params[3] = miner.Job.Coinbase2
|
||||
params[4] = tlist
|
||||
|
||||
miner.CurHeight = miner.Job.Height
|
||||
|
||||
if miner.Job.Transactions != nil {
|
||||
if len(*miner.Job.Transactions) > 0 {
|
||||
params[4] = miner.Job.Transactions
|
||||
|
||||
/*miner.Server.Logg.Error("[notify]", zap.String("coinbase1", miner.Job.Coinbase1), zap.String("coinbase2", miner.Job.Coinbase2), zap.Uint32("height", miner.Job.Height))
|
||||
for i := 0; i < len(*miner.Job.Transactions); i++ {
|
||||
miner.Server.Logg.Error("[notify]", zap.String("trans", (*miner.Job.Transactions)[i]))
|
||||
}*/
|
||||
|
||||
}
|
||||
}
|
||||
vb := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(vb, uint32(miner.Job.Version))
|
||||
params[5] = hex.EncodeToString(vb)
|
||||
bb := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(bb, miner.Job.Bits)
|
||||
params[6] = hex.EncodeToString(bb)
|
||||
t := miner.Job.Timestamp.Unix()
|
||||
if t > int64(^uint32(0)) {
|
||||
tb := make([]byte, 8)
|
||||
binary.LittleEndian.PutUint64(tb, uint64(t))
|
||||
params[7] = hex.EncodeToString(tb)
|
||||
} else {
|
||||
tb := make([]byte, 4)
|
||||
binary.LittleEndian.PutUint32(tb, uint32(t))
|
||||
params[7] = hex.EncodeToString(tb)
|
||||
}
|
||||
if miner.Reconnect {
|
||||
params[8] = true
|
||||
miner.Reconnect = false
|
||||
} else {
|
||||
params[8] = miner.Job.IsClean
|
||||
}
|
||||
miner.Job.JobDifficulty = miner.Difficulty
|
||||
|
||||
//miner.Jobs[miner.Job.Job_id] = miner.Job
|
||||
miner.Jobs.LoadOrStore(miner.Job.Job_id, miner.Job)
|
||||
|
||||
/*var entry coin.JobListEntry
|
||||
entry.Job_id = miner.Job.Job_id
|
||||
entry.Ts = time.Now()
|
||||
|
||||
miner.LockForJobs.Lock()
|
||||
miner.JobList.PushFront(entry)
|
||||
var removes string = ""
|
||||
if miner.JobList.Len() > int(coin.LOCAL_JOBS_TOTAL_SIZE) {
|
||||
e := miner.JobList.Back()
|
||||
entry := e.Value.(coin.JobListEntry)
|
||||
removes = entry.Job_id
|
||||
miner.JobList.Remove(e)
|
||||
}
|
||||
miner.LockForJobs.Unlock()
|
||||
if len(removes) > 0 {
|
||||
miner.Jobs.Delete(removes)
|
||||
}*/
|
||||
stratum.AddAndUpdateJob(miner)
|
||||
stratum.UpdateJobs(miner)
|
||||
|
||||
//miner.LastJobId = miner.Job.Job_id
|
||||
miner.JobId++
|
||||
|
||||
var msg stratum.Notify_msg
|
||||
msg.ID = nil
|
||||
msg.Method = "mining.notify"
|
||||
msg.Params = params
|
||||
body, err := json.Marshal(msg)
|
||||
if err != nil {
|
||||
miner.Server.Logg.Error("[server]", zap.String("Marshal", err.Error()))
|
||||
miner.TxLock.Unlock()
|
||||
return
|
||||
}
|
||||
var body_string = string(body) + "\n"
|
||||
err = stratum.Conn_tx(miner.Conn, []byte(body_string))
|
||||
if err != nil {
|
||||
//delete(miner.Server.Miners, miner.MinerId)
|
||||
//miner.Server.Miners.Delete(miner.MinerId)
|
||||
}
|
||||
//miner.Server.Logg.Debug("[server]", zap.String("tx", body_string))
|
||||
miner.TxLock.Unlock()
|
||||
|
||||
if miner.ZlogInit {
|
||||
miner.Zlog.Info().Msg(body_string)
|
||||
}
|
||||
}
|
||||
|
||||
func Notify(miner *coin.MinerObj) {
|
||||
EnxNotify(miner)
|
||||
}
|
||||
|
||||
func HandleJobMsg(server *coin.ServerContext, Msg []byte) {}
|
||||
|
||||
func IsMhsLow(miner *coin.MinerObj) bool {
|
||||
return miner.Mhs5M < 1
|
||||
}
|
||||
|
||||
func GetBlockInterval() int {
|
||||
return 30 // 30秒没获取到任务发送退出信号
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
package enx
|
||||
|
||||
/*
|
||||
#cgo CFLAGS: -I../include/heavyHash
|
||||
#cgo LDFLAGS: -L../lib/heavyHash -lheavyhash
|
||||
#include "heavyhash.h"
|
||||
*/
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
func EnxHash(data []byte) string {
|
||||
// 初始化 CSHA3_256 结构体
|
||||
var sha3 C.CSHA3_256
|
||||
|
||||
// 调用 C 语言的 Write 方法
|
||||
C.CSHA3_256_Write(&sha3, (*C.uchar)(unsafe.Pointer(&data[0])), C.size_t(len(data)))
|
||||
|
||||
// 调用 C 语言的 Finalize 方法来生成哈希
|
||||
var hash [C.OUTPUT_SIZE]C.uchar
|
||||
C.CSHA3_256_Finalize(&sha3, &hash[0])
|
||||
|
||||
// 使用 strings.Builder 来拼接字符串
|
||||
var builder strings.Builder
|
||||
for i := 0; i < C.OUTPUT_SIZE; i++ {
|
||||
// 以十六进制方式格式化每个字节
|
||||
builder.WriteString(fmt.Sprintf("%02x", hash[i]))
|
||||
}
|
||||
|
||||
// 返回最终的哈希值
|
||||
return builder.String()
|
||||
}
|
Loading…
Reference in New Issue