由于对区块链的喜爱,个人最近对矿池以及挖矿做了一点点研究。
首先先说下对于矿池的主要结构,如图
pool
pool为矿池的中心服务器,用来跟链与miner通信的。
运行pool程序的电脑上必须要有一个ETH或者OF的全节点。
他的主要功能包括两个
一:更新工作
func updateWork() {
for true {
currWorkNew, err := callArray("eth_getWork", []interface{}{})
if err == nil {
if currWorkNew.Result == nil {
time.Sleep(time.Millisecond * 200)
continue
}
if currHash == currWorkNew.Result[0].(string) {
//logInfo.Println("新的任务跟当前任务一致")
time.Sleep(time.Millisecond * 200)
continue
}
currWork = currWorkNew
currHash = currWork.Result[0].(string)
} else {
currWork = nil
logError.Println("update work error: ", err)
}
//fmt.Println("Current work", currWork.Result[0])
time.Sleep(time.Millisecond * 200)
}
}
二、提交工作
func submitWork(params []interface{}) (*ResponseBool, error) {
result, err := callBool("eth_submitWork", params)
return result, err
}
miner
miner为矿工方,主要用来挖矿,挖矿算法这里用的是eth公开的算法,我加以个人想法优化的。
首先是更新工作
// 更新区块
func updatePendingBlock() {
var (
//threads int = runtime.NumCPU()
currHashNew int64
//difficulty uint64
)
for true {
time.Sleep(time.Millisecond * 500)
//var pend sync.WaitGroup
currWorkNew, err := callArray("ofbank_getWork", []interface{}{})
//fmt.Println(currWorkNew)
//logInfo.Println(currWorkNew)
if err != nil {
logInfo.Println("new work info", currWorkNew)
logError.Println("get new work error: ", err)
} else {
if currWorkNew.Result == nil {
fmt.Println("获取的任务为空")
continue
}
currHashNew, err = strconv.ParseInt(currWorkNew.Result[2].(string), 0, 64)
if err != nil {
logError.Println("get difficulty error: ", err,currHashNew)
continue
}
if currHash == currHashNew {
continue
}
if mining {
abort <- struct{}{}
}
currHash = currHashNew
currWork = currWorkNew
fmt.Println("开始工作",currHash,currWork.Result[0])
go sealWork(currWorkNew, abort)
}
}
}
这里在获取工作后,执行sealWork,这里有一个终止的通道,用来停止之前的工作,以免做无用功。
func sealWork(currWork *ResponseArray, abort chan struct{}) {
var (
blockDifficulty int64
err error
threads int = runtime.NumCPU()
)
//fmt.Println(currWork)
blockDifficulty, err = strconv.ParseInt(currWork.Result[2].(string), 0, 64)
if err != nil {
logError.Printf("seal work error: %+v", err)
mining = false
return
}
myBlock := &block{
//number: number,
difficulty: big.NewInt(blockDifficulty),
hashNoNonce: common.HexToHash(currWork.Result[0].(string)),
}
var stop = make(chan struct{}, threads)
var l = sync.Mutex{}
var th = 0
mining = true
for i := 0; i < threads; i++ {
th++
//fmt.Println("第",th,"线程")
go func() {
currNonce, md := hasher.Search(myBlock, stop, 0)
l.Lock()
th--
if th < 0 {
th = 0
}
l.Unlock()
myBlock.nonce = currNonce
myBlock.mixDigest = common.BytesToHash(md)
if hasher.Verify(myBlock) {
if mining {
// 挖矿中
l.Lock()
mining = false
l.Unlock()
submitTask := make([]interface{}, 3)
// nonce 必须为偶数位长度
nonceStr := strconv.FormatUint(currNonce, 16)
if len(nonceStr)%2 != 0 {
nonceStr = "0" + nonceStr
}
submitTask[0] = fmt.Sprintf("0x%s", nonceStr)
submitTask[1] = currWork.Result[0].(string)
//submitTask[1] = currWork.Result[0]
submitTask[2] = common.BytesToHash(md).Hex()
fmt.Println("提交工作")
abort <- struct{}{}
submitWork(submitTask)
}
}
}()
}
L:
for {
select {
case <-abort:
l.Lock()
var t = th - 1
mining = false
currWork = nil
currHash = 0
for i := 0; i < t; i ++ {
th--
stop <- struct{}{}
}
l.Unlock()
break L
}
}
}
然后是sealWork方法,这里根据CPU核心数创建线程,以达到最大CPU利用率。
在算到相应的工作后,向pool提交工作,等待下个工作。
使用攻略
简单写下windows系统下的使用攻略:
在有全节点的电脑上执行pool.exe 即可。
然后在查看当前电脑的IP地址,这里可以自行百度,有很多种方法,
然后在挖矿的电脑上执行miner.exe程序,
在这里输入刚刚查到的ip地址,回车,就可以了。
demo下载地址
demo里面有可执行程序.