矿池&CPU挖矿

由于对区块链的喜爱,个人最近对矿池以及挖矿做了一点点研究。

首先先说下对于矿池的主要结构,如图


1525254093070.jpg

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 即可。

1525256636299.jpg

然后在查看当前电脑的IP地址,这里可以自行百度,有很多种方法,

然后在挖矿的电脑上执行miner.exe程序,


1525256671691.jpg

在这里输入刚刚查到的ip地址,回车,就可以了。

demo下载地址
demo里面有可执行程序.

你可能感兴趣的:(矿池&CPU挖矿)