天创培训:您身旁的信息安全培训专家!
9159金沙游艺场
栏目列表
开班方案
2019年4月CISP培训开班告诉
主讲教师   张教师、王教师等
开课工夫   2019年4月16日-21日
培训方法   实地/面授
讲课天次   培训5天+测验半天
上课工夫   09:00 -- 16:30
课程引见 在线报名
手艺中心您当前位置: > 资本专区 > 手艺中心

如何利用Go言语编写本人的区块链挖矿算法

作者:天创培训  滥觞:px.tcnet.com.cn  更新工夫:2018-03-21  关键词:区块链,挖矿算法

如何利用Go言语编写本人的区块链挖矿算法

 

一、媒介

跟着近期比特币(Bitcoin)以及以太坊(Ethereum)挖矿高潮的鼓起,人们很容易对此浮想联翩。关于刚进入该范畴的新手而言,他们会听到各类故事,好比许多人将GPU堆满堆栈来猖獗挖矿,每个月能挖到代价数百万美圆的加密货泉(cryptocurrency)。那么就是什么是密币挖矿?挖矿的道理是什么?怎么样才气编写本人的挖矿算法?

在本文中,我们会给各人逐个解答这些成绩,然后引见如何编写本人的挖矿算法。我们将这类算法称为Proof of Work(工作量证实)算法,该算法是比特币及以太坊这两种最盛行的加密货泉的根底。无需担心,我们会给各人引见得明明白白。

 

二、什么是密币挖矿

物以稀为贵,对加密货泉来讲也是云云。假如任何人在任何工夫都能够随便消费随便多的比特币,那么比特币作为一种货泉则毫无代价可言(稍等,这不恰是美联储常干的事吗……)。比特币算法每隔10分钟就会向比特币网络中的得胜成员颁布一次比特币,总量约莫在122年内会到达最大值。这类颁布机制能够将通货膨胀率掌握在一定范围内,由于算法并没有在一开始就给出所有密币,而是跟着工夫推移逐渐产出。

在这类算法下,为了获得比特币嘉奖,矿工们需求支出“劳动”,与其他矿工合作。这个历程也称为“挖矿”,由于该历程类似于黄金矿工的采矿历程,为了找到一点黄金,工人们也需求支出工夫及精神终极才气获得胜利果实(有时候也会竹篮打水一场空)。

比特币算法会强迫参与者(或节点)停止挖矿,同时互相合作,确保比特币产出速度不至于太快。

 

三、如何挖矿

假如在Google上搜索“如何挖比特币?”,我们会看到大量成果,纷繁注释发掘比特币需求节点(用户大概主机)来处理庞大的数学困难。固然这类说法从手艺角度来看没有成绩,但简朴称之为“数学”成绩明显有点太过于生硬,挖矿的历程实在十分风趣。我们要理解一些密码学常识以及哈希算法,才气了解挖矿的道理。

密码学简介

单向加密以人眼可读的数据作为输入(如“Hello world”),然后经由过程某个函数天生难以识别的输出。这些函数(大概算法)在性子及复杂度上各不不异。算法越庞大,想逆向阐发就越难。因而,加密算法在庇护数据(如用户密码以及军事代码)方面至关重要。

以十分盛行的SHA-256算法为例,我们能够利用哈希天生网站来生成SHA-256哈希值。好比,我们能够输入“Hello world”,察看对应的哈希成果:

如何利用Go言语编写本人的区块链挖矿算法

我们能够反复计较“Hello world”的哈希值,每次都能获得不异的成果。在编程中,输入不异的状况下,假如每次都获得一样的输出,这个历程称之为“幂等性(idempotency)”。

关于加密算法而言,我们很难经由过程逆向阐发来推导原始输入,但很容易考证输出结果是否准确,这是加密算法的一个根本特性。好比前面谁人例子,我们很容易就能考证“Hello world”的SHA-256哈希值能否准确,但很难经由过程给定的哈希值来推导原始输入为“Hello world”。这也是我们为何将这类加密办法称为单向加密的缘故原由地点。

比特币利用的是两重SHA-256算法(Double SHA-256),也就是说它会将“Hello world”的SHA-256哈希值作为输入,再计较一次SHA-256哈希。在本文中,为了便利起见,我们只计较一次SHA-256哈希。

挖矿

了解加密算法后,我们能够回到密币挖矿这个主题上。比特币需求找到一些办法,让期望得到比特币的参与者能经由过程加密算法来“挖矿”,以便掌握比特币的产出速度。具体说来,比特币会让参与者计较一堆字母和数字的哈希值,直到他们算出的哈希值中“前导0”的位数超越必然长度为止。

好比,回到前面谁人哈希计较网站,输入“886”后我们能够获得前缀为3个0的哈希值。

www.金沙

问题是我们如何才能知道“886”的哈希值开首包罗3个零呢?这才是重点。在撰写本文之前,我们没有办法知道这一点。从理论上讲,我们必需测验考试字母和数字的各类组合,对成果停止测试,直到得到满意要求的成果为止。这里我们事先给出了“886”的哈希成果以便各人了解。

任何人都能很容易考证出“886”能否会天生3位前导zero的哈希成果,如许就能证实我们确实做了大量测试、查抄了大量字母和数字的组合,终极得到了这一成果。因而,假如我们是第一个得到这个成果的人,其他人能够快速考证“886”的正确性,这个工作量的证实历程能够让我博得比特币。这也是为什么人们把比特币的一致性算法称之为Proof-of-Work(工作量证实)算法。

但假如我的命运爆表,第一次测验考试就天生了3个前导zero成果呢?这类工作根本不可能发作,偶尔的节点在初次测验考试时就胜利发掘新块(向各人证实他们确实做了这项事情)的可能性微不足道,相反其他数百万个节点需求支出大量事情才气找到所需的散列。你能够持续测验考试一下,随意输入一些字母和数字的组合,我打赌你得不到3个前导zero的成果。

比特币的约束条件比这个例子愈加庞大(要求获得更多位前导zero!),而且它能静态调解难度,以确保工作量不会太轻大概太重。请记着,比特币算法的目的是每隔10分钟产出一枚比特币。因而,假如太多人参与挖矿,比特币需求加大工作量证实难度,实现难度的静态调解。从我们的角度来看,调解难度等同于增长更多位前导zero。

因而,各人能够大白比特币的一致性算法比纯真“处理数学困难”要有趣很多。

 

四、开端编程

布景常识曾经引见终了,如今我们能够利用Proof-of-Work算法来构建本人的区块链(Blockchain)法式。我挑选利用Go言语来实现,这是一门十分棒的言语。

在持续之前,我倡议你浏览我们之前的一篇博客:《用200行Go代码实现本人的区块链》。这不是硬性要求,但我们会快速擦过以下某些例子,假如你想理解更多细节,能够参考那篇文章。假如你已经洞若观火,能够间接跳到“Proof of Work”章节。

团体架构以下:

如何利用Go言语编写本人的区块链挖矿算法

我们需求一台Go服务器,为了简朴起见,我会将所有代码放在一个main.go文件中。这个文件供给了关于区块链逻辑的所有信息(包罗Proof of Work),也包罗所有REST API对应的处置法式。区块链数据是不成改动的,我们只需求GET以及POST恳求便可。我们需求利用浏览器倡议GET恳求来检察数据,利用Postman来POST新的区块(也能够利用curl)。

导入依靠

起首我们需求导入一些库,请利用go get号令获得以下包:

1、github.com/davecgh/go-spew/spew:协助我们在终端中间接检察构造规整的区块链信息。

2、github.com/gorilla/mux:用来搭建Web服务器的一个库,非常好用。

3、github.com/joho/godotenv:能够从根目录中的.env文件中读取环境变量。

起首我们能够在根目录中创立一个.env文件,用来寄存前面需求的一个环境变量。.env文件只要一行:ADDR=8080

在根目录的main.go中,将依靠包以声明的方法导入:

package main

import (
        "crypto/sha256"
        "encoding/hex"
        "encoding/json"
        "fmt"
        "io"
        "log"
        "net/http"
        "os"
        "strconv"
        "strings"
        "sync"
        "time"

        "github.com/davecgh/go-spew/spew"
        "github.com/gorilla/mux"
        "github.com/joho/godotenv"
)

假如你看过我们前面那篇文章,那么对上面这张图该当不会感应生疏。我们利用散列算法来考证区块链中区块的正确性,详细办法是将某个区块中的previous hash与前一个区块的hash停止比照,确保二者相称。如许就能保护区块链的完整性,而且歹意成员无法窜改区块链的汗青信息。

8455.com

此中,BPM指的是脉搏速率,大概每分钟的跳动次数。我们将利用你的心率来作为区块中的一部分数据。你能够将两根手指放在伎俩内侧,计较一分钟内感受到多少次脉动,记着这个数字便可。

基本概念

如今,在main.go的声明语句前面增加数据模型以及前面需求用到的其他变量。

const difficulty = 1

type Block struct {
        Index      int
        Timestamp  string
        BPM        int
        Hash       string
        PrevHash   string
        Difficulty int
        Nonce      string
}

var Blockchain []Block

type Message struct {
        BPM int
}

var mutex = &sync.Mutex{}

difficulty是一个常量,用来界说哈希值中需求的前导零位数。前导零位数越多,我们越难找到准确的哈希值。我们先从1位前导zero开端。

Block是每一个区块的数据模型。不要担忧Nonce字段,前面我们会引见。

Blockchain由多个Block构成,代表完好的区块链。

我们会在REST API中,利用POST恳求提交Message以天生新的Block

我们声明了一个互斥量mutex,前面我们会利用该变量制止呈现数据合作,确保多个区块不会同一时间天生。

Web服务器

让我们快速搭一个Web服务器。起首创立一个run函数,main函数随后会挪用这个函数来运转服务器。我们在makeMuxRouter()中声明了响应的恳求处置函数。请留意,我们需求经由过程GET恳求获得区块链,经由过程POST恳求增加新的区块。区块链无法变动,因而我们不需要实现编纂或删除功用。

func run() error {
        mux := makeMuxRouter()
        httpAddr := os.Getenv("ADDR")
        log.Println("Listening on ", os.Getenv("ADDR"))
        s := &http.Server{
                Addr:           ":" + httpAddr,
                Handler:        mux,
                ReadTimeout:    10 * time.Second,
                WriteTimeout:   10 * time.Second,
                MaxHeaderBytes: 1 << 20,
        }

        if err := s.ListenAndServe(); err != nil {
                return err
        }

        return nil
}

func makeMuxRouter() http.Handler {
        muxRouter := mux.NewRouter()
        muxRouter.HandleFunc("/", handleGetBlockchain).Methods("GET")
        muxRouter.HandleFunc("/", handleWriteBlock).Methods("POST")
        return muxRouter
}

httpAddr := os.Getenv("ADDR")这行语句会从我们前面创立的.env文件中提取:8080这个信息。我们能够经由过程浏览器会见http://localhost:8080/这个地址来访问我们构建的使用。

如今我们需求编写GET处置函数,在浏览器中显现区块链信息。我们还需求增加一个respondwithJSON函数,一旦API挪用历程中呈现毛病就能以JSON格局返回错误信息。

func handleGetBlockchain(w http.ResponseWriter, r *http.Request) {
        bytes, err := json.MarshalIndent(Blockchain, "", "  ")
        if err != nil {
                http.Error(w, err.Error(), http.StatusInternalServerError)
                return
        }
        io.WriteString(w, string(bytes))
}

func respondWithJSON(w http.ResponseWriter, r *http.Request, code int, payload interface{}) {
        w.Header().Set("Content-Type", "application/json")
        response, err := json.MarshalIndent(payload, "", "  ")
        if err != nil {
                w.WriteHeader(http.StatusInternalServerError)
                w.Write([]byte("HTTP 500: Internal Server Error"))
                return
        }
        w.WriteHeader(code)
        w.Write(response)
}


如今编写POST处置函数,这个函数能够实现新区块的增加历程。我们利用Postman来倡议POST恳求,向http://localhost:8080发送JSON数据(如{“BPM”:60}),此中包罗前面你记载下的谁人脉搏次数。

func handleWriteBlock(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Content-Type", "application/json")
        var m Message

        decoder := json.NewDecoder(r.Body)
        if err := decoder.Decode(&m); err != nil {
                respondWithJSON(w, r, http.StatusBadRequest, r.Body)
                return
        }   
        defer r.Body.Close()

        //ensure atomicity when creating new block
        mutex.Lock()
        newBlock := generateBlock(Blockchain[len(Blockchain)-1], m.BPM)
        mutex.Unlock()

        if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) {
                Blockchain = append(Blockchain, newBlock)
                spew.Dump(Blockchain)
        }   

        respondWithJSON(w, r, http.StatusCreated, newBlock)

}

请留意代码中mutex的lock以及unlock操纵。在写入新的区块之前,我们需求锁定互斥量,否则屡次写入就会形成数据合作。仔细的读者会注意到generateBlock函数,这是处置Proof of Work的枢纽函数,稍后我们会引见。

根本的区块链函数

在引见Proof of Work之前,先收拾整顿下根本的区块链函数。我们增加了一个isBlockValid函数,确保我们的索引能准确递增,而且当前区块的PrevHash与前一个区块的Hash相婚配。

我们也增加了一个calculateHash函数,用来天生哈希值,以计较Hash以及PrevHash。我们将IndexTimestampBPMPrevHash以及Nonce(稍后我们会引见这个字段)串在一起,计算出一个SHA-256哈希。

func isBlockValid(newBlock, oldBlock Block) bool {
        if oldBlock.Index+1 != newBlock.Index {
                return false
        }

        if oldBlock.Hash != newBlock.PrevHash {
                return false
        }

        if calculateHash(newBlock) != newBlock.Hash {
                return false
        }

        return true
}

func calculateHash(block Block) string {
        record := strconv.Itoa(block.Index) + block.Timestamp + strconv.Itoa(block.BPM) + block.PrevHash + block.Nonce
        h := sha256.New()
        h.Write([]byte(record))
        hashed := h.Sum(nil)
        return hex.EncodeToString(hashed)
}

 

五、Proof of Work

如今来引见挖矿算法,也就是Proof of Work。在新的Block参加blockchain之前,我们需求确保Proof of Work使命曾经完成。我们先以一个简朴的函数开端,该函数能够查抄Proof of Work历程中天生的哈希能否满意我们设置的约束条件。

我们的约束条件以下:

1、Proof of Work天生的哈希必需具有特定位数的前导zero。

2、前导zero的位数由法式刚开始界说的difficulty常量来决议(这里这个值为1).

3、我们能够增长难度值来提高Proof of Work的难度。

起首机关一个函数:isHashValid

func isHashValid(hash string, difficulty int) bool {
        prefix := strings.Repeat("0", difficulty)
        return strings.HasPrefix(hash, prefix)
}

Go言语在strings包中供给了Repeat以及HasPrefix函数,利用起来十分便利。我们界说了一个prefix变量,用来暗示前导零位数,然后查抄哈希值的前导零位数能否满意要求,满意则返回True,不满足则返回False

接下来创立generateBlock函数。

func generateBlock(oldBlock Block, BPM int) Block {
        var newBlock Block

        t := time.Now()

        newBlock.Index = oldBlock.Index + 1
        newBlock.Timestamp = t.String()
        newBlock.BPM = BPM
        newBlock.PrevHash = oldBlock.Hash
        newBlock.Difficulty = difficulty

        for i := 0; ; i++ {
                hex := fmt.Sprintf("%x", i)
                newBlock.Nonce = hex
                if !isHashValid(calculateHash(newBlock), newBlock.Difficulty) {
                        fmt.Println(calculateHash(newBlock), " do more work!")
                        time.Sleep(time.Second)
                        continue
                } else {
                        fmt.Println(calculateHash(newBlock), " work done!")
                        newBlock.Hash = calculateHash(newBlock)
                        break
                }

        }
        return newBlock
}

我们创立了一个newBlock变量,将前一个区块的哈希值保留到PrevHash字段中,确保区块链满意连续性要求。其他字段的寄义该当比力较着:

1、Index不竭增长;

2、Timestamp是当前工夫的字符串表现形式;

3、BPM是前面记载下的心率;

4、Difficulty间接摘抄自最开首界说的谁人常量。这个例子中我们不会利用这个字段,但假如我们想进一步考证,确保难度值与哈希成果分歧(也就是说哈希成果的前导零位数为N,那么Difficulty的值该当也为N,不然区块链就会遭到毁坏),那么这个字段就十分有效。

这个函数中的for轮回非常重要,我们具体引见一下:

1、获得i的十六进制情势,将该值赋给Nonce。我们需求一种办法来将静态变革的一个值参加哈希成果中,如许假如我们没有获得幻想的哈希值,就能够经由过程calculateHash函数持续天生新的值。我们在calculateHash计较历程中增加的静态值就称为“Nonce”。

2、在轮回中,我们的i和Nonce值从0开端递增,判定天生的哈希成果中前导零位数能否与difficulty相称。假如不相等,我们进入新的迭代,增长Nonce,再次计较。

3、我们加了1秒的休眠操纵,模仿处理Proof of Work所需的工夫。

4、持续轮回,直到计较成果中包罗特定位数的前导zero为止,此时我们胜利完成了Proof of Work使命。只要在这种情况下,我们的Block才气经由过程handleWriteBlock处置函数增加到blockchain中。

如今我们曾经完成了所有函数,因而让我们完成main函数吧:

func main() {
        err := godotenv.Load()
        if err != nil {
                log.Fatal(err)
        }   

        go func() {
                t := time.Now()
                genesisBlock := Block{}
                genesisBlock = Block{0, t.String(), 0, calculateHash(genesisBlock), "", difficulty, ""} 
                spew.Dump(genesisBlock)

                mutex.Lock()
                Blockchain = append(Blockchain, genesisBlock)
                mutex.Unlock()
        }() 
        log.Fatal(run())

}

利用godotenv.Load()语句我们能够完成环境变量(:8080端口)的加载,以便经由过程浏览器停止会见。

区块链得有一个出发点,以是我们利用一个go例程来创立创世区块。

然后利用前面机关的run()函数启动Web服务器。

 

六、大功告成

各人能够会见Github获得完整版代码。

来跑一下看看结果。

起首利用go run main.go号令启动法式。

然后在浏览器中会见http://localhost:8080

区块链中曾经有一个创世区块。如今翻开Postman,经由过程POST恳求向服务器发送包罗BPM字段(心率数据)的JSON数据。

澳门太阳赌城集团2007

发送完恳求后,我们能够在终端中察看操纵成果。能够看到,我们的计较时机利用不竭递增的Nonce值来创立新的哈希,直到计较出来的哈希满意前导零位数要求为止。

如何利用Go言语编写本人的区块链挖矿算法

完成Proof of Work使命后,我们能够看到一则提醒动静:work done!。我们能够查验这个哈希值,发明它确实满意difficulty所设置的前导零位数要求。这意味着从理论上来说,我们利用BPM = 60参数所天生的新区块如今该当曾经增加到区块链中。

革新浏览器看一下成果:

如何利用Go言语编写本人的区块链挖矿算法

大功告成!我们的第二个区块曾经增加到创世区块前面。也就是说,我们胜利经由过程POST恳求发送了区块,这个操纵触发了挖矿历程,当且仅当Proof of Work得以处理时,新的区块才气增加到区块链中。

 

七、下一步思索

非常好,前面学到的常识非常重要。Proof of Work是比特币、以太坊以及其他区块链平台的根底。我们前面的阐发意义不凡,固然这个例子利用的难度值其实不大,但实践情况中Proof of Work区块链的事情道理就是把难度提高到较高水平罢了。

如今你已经深化理解了区块链手艺的枢纽组成部分,前面的路需求你自己去走,我们倡议你能够持续进修以下常识:

1、进修区块链网络的事情道理。

2、进修如何故分布式方法存储大型文件并与区块链交互。

假如你还想持续深化进修,能够思索理解一下Proof of Stake(权益证实)。虽然今朝大多数区块链利用Proof of Work作为一致性算法,但公家愈来愈存眷Proof of Stake。人们遍及以为,将来以太坊将从Proof of Work迁徙至Proof of Stake。



0

推荐浏览

 |  关于天创 |  课程体系 |  最新动态 |  联络我们 |  网站舆图 |  二维码
版权所有:江苏天创科技有限公司 苏ICP备16028135号-2
姑苏总部地址:江苏省苏州市十梓街327号 电话:0512-65129087 传真:0512-65157410 邮编:215000
南京分公司地址:南京珠江路88号新世界中心A座 电话:025-84533276 传真:025-84533286 邮编:210000
澳门金莎
澳门太阳赌城集团2007
8455.com