区块链用的什么语言,区块链用到的编程语言

  

  以下文章来自无情飞雪,作者是无情飞雪。   

  

  Github上有一个Repo,是用Go语言(golang)写的区块链源代码,不到200行代码,准确的说是174行。原作者的名字是用不到200行的go代码编写你自己的区块链!而且作者还为此写了一篇文章。https://medium . com/@ my coral health/code-your-own-in-the-less-200-line-of-go-e 296282 BC ffc   

  

  这篇文章是一个总体思路和实现代码。当然还有很多代码逻辑没有涉及到,我就用不到200行的代码做一个分析,包括原文章没有涉及到的知识点,对Go语言和区块链有更深入的理解。   

  

  的所有源代码都在这里:https://github . com/nosequeeldeebee/区块链-tutorial/blob/master/main.go   

  

  import(' crypto/sha 256 ' ' encoding/hex ' ' encoding/JSON ' ' io ' ' log ' ' net/http ' ' OS ' ' strconv ' ' sync ' ' time ' ' github . com/Dave CGH/go-spew/spew ' ' github . com/gorilla/Mux ' ' github . com/joho/godo tenv))在源代码的开头。就是笔者介绍的一些包,包括标准的和第三方的。sha256、hex等标准包用于sha-256编码,其他包括启动http服务、打印日志、并发控制同步、时间戳等。   

  

  有三个第三方包,其中两个我已经详细介绍过了。我相信你会对它们很熟悉。   

  

  Go-spew是一个变结构的调试工具,可以打印出变结构对应的数据和结构,调试起来非常方便。   

  

  Gorilla/mux是一个web路由服务,它可以帮助我们非常简单地构建web服务。   

  

  不过目前普遍使用的是金酒,也推荐使用金酒https://github.com/gin-gonic/gin.   

  

  Godotenv是一个用于阅读配置文章的库,它允许我们在。env格式,如IP、端口等。从配置文件中。但是,当前配置文件中推荐使用YAML和TOML,对应的第三方库有:   

  

  gopkg.in/yaml.v21https://github.com/BurntSushi/toml   

  

  既然要写区块链,就要有块实体,可以通过golang的struct来实现。   

  

  //在theblockchaintypeblockstruct { indexingtimestampstringbpminthastringprevhassling }块中的BlockeresentSeach“item”包含几个字段:   

  

  即索引块的顺序索引时间戳就是生成块的时间戳BPM。作者说代表心率,每分钟心跳的哈希值就是sha256生成的哈希值。一个块的散列被添加到整个块数据的散列中,从而可以将块连接在一起以形成区块链。有了滑车滑车,区块链很容易处理。   

  

  //BlockchainisaseriesofvalidatedBlocksvarnb   

sp;Blockchain <>Block就是这么简单,一个Block数组就是一个区块链。区块链的构成关键在于Hash和PrevHash,通过他们一个个串联起来,就是一串Block,也就是区块链。因为相互之间通过Hash和PrevHash进行关联,所以整个链很难被篡改,链越长被篡改的成本越大,因为要把整个链全部改掉才能完成篡改的目的,这样的话,其他节点验证这次篡改肯定是不能通过的。

  

既然关键点在于Hash,所以我们要先算出来一个Block的数据的Hash,也就是对Block里的字段进行Hash,计算出一个唯一的Hash值。

  

// SHA256 hasingfunc calculateHash(block Block) string {    record := strconv.Itoa(block.Index) + block.Timestamp + strconv.Itoa(block.BPM) + block.PrevHash    h := sha256.New()    h.Write(<>byte(record))    hashed := h.Sum(nil)    return hex.EncodeToString(hashed)}sha256是golang内置的sha256的散列标准库,可以让我们很容易的生成对应数据的散列值。从源代码看,是把Block的所有字段进行字符串拼接,然后通过sha256进行散列,散列的数据再通过hex.EncodeToString转换为16进制的字符串,这样就得到了我们常见的sha256散列值,类似这样的字符串8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92。

  

Block的散列值被我们计算出来了,Block的Hash和PrevHash这两个字段搞定了,那么我们现在就可以生成一个区块了,因为其他几个字段都是可以自动生成的。

  

// create a new block using previous block's hashfunc 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.Hash = calculateHash(newBlock)    return newBlock}因为区块链是顺序相连的,所以我们在生成一个新的区块的时候,必须知道上一个区块,也就是源代码里的oldBlock。另外一个参数BPM就是我们需要在区块里存储的数据信息了,这里作者演示的例子是心率,我们可以换成其他业务中想要的数据。

  

Index是上一个区块的Index+1,保持顺序;Timestamp通过time.Now()可以得到;Hash通过calculateHash方法计算出来。这样我们就生成了一个新的区块。

  

在这里作者并没有使用POW(工作量证明)这类算法来生成区块,而是没有任何条件的,这里主要是为了模拟区块的生成,演示方便。

  

区块可以生成了,但是生成的区块是否可信,我们还得对他进行校验,不能随便生成一个区块。在比特币(BitCoin)中校验比较复杂,这里作者采用了简单模拟的方式。

  

// make sure block is valid by checking index, and comparing the hash of the previous blockfunc 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}简单的对比Index,Hash是否是正确的,并且重新计算了一遍Hash,防止被篡改。

  

到了这里,关于区块链的代码已经全部完成了,剩下的就是把区块链的生成、查看等包装成一个Web服务,可以通过API、浏览器访问查看。因为作者这里没有实现P2P网络,所以采用的是WEB服务的方式。

  

// create handlersfunc makeMuxRouter() http.Handler {    muxRouter := mux.NewRouter()    muxRouter.HandleFunc("/", handleGetBlockchain).Methods("GET")    muxRouter.HandleFunc("/", handleWriteBlock).Methods("POST")    return muxRouter}通过mux定义了两个Handler,URL都是/,但是对应的Method是不一样的。

  

GET方法通过handleGetBlockchain函数实现,用于获取区块链的信息。

  

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))}Blockchain是一个<>Block,handleGetBlockchain函数的作用是把Blockchain格式化为JSON字符串,然后显示出来。io.WriteString是一个很好用的函数,可以往Writer里写入字符串。更多参考 Go语言实战笔记(十九)| Go Writer 和 Reader

  

'POST'方法通过handleWriteBlock函数实现,用于模拟区块的生成。

  

func handleWriteBlock(w http.ResponseWriter, r *http.Request) {    w.Header().Set("Content-Type", "application/json")    //使用了一个Mesage结构体,更方便的存储BPM    var msg Message    //接收请求的数据信息,类似{"BPM":60}这样的格式    decoder := json.NewDecoder(r.Body)    if err := decoder.Decode(&msg); err != nil {        respondWithJSON(w, r, http.StatusBadRequest, r.Body)        return    }    defer r.Body.Close()    //控制并发,生成区块链,并且校验    mutex.Lock()    prevBlock := Blockchain    newBlock := generateBlock(prevBlock, msg.BPM)    //校验区块链    if isBlockValid(newBlock, prevBlock) {        Blockchain = append(Blockchain, newBlock)        spew.Dump(Blockchain)    }    mutex.Unlock()    //返回新的区块信息    respondWithJSON(w, r, http.StatusCreated, newBlock)}以上代码我进行了注释,便于理解。主要是通过POST发送一个{"BPM":60}格式的BODY来添加区块,如果格式正确,那么就生成区块进行校验,合格了就加入到区块里;如果格式不对,那么返回错误信息。

  

用于控制并发的锁可以参考Go语言实战笔记(十七)| Go 读写锁

  

这个方法里有个Message结构体,主要是为了便于操作方便。

  

// Message takes incoming JSON payload for writing heart ratetype Message struct {    BPM int}返回的JSON信息,也被抽取成了一个函数respondWithJSON,便于公用。

  

func respondWithJSON(w http.ResponseWriter, r *http.Request, code int, payload interface{}) {    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)}好了,快完成了,以上Web的Handler已经好了,现在我们要启动我们的Web服务了。

  

// web serverfunc run() error {    mux := makeMuxRouter()    //从配置文件里读取监听的端口    httpPort := os.Getenv("PORT")    log.Println("HTTP Server Listening on port :", httpPort)    s := &http.Server{        Addr:           ":" + httpPort,        Handler:        mux,        ReadTimeout:    10 * time.Second,        WriteTimeout:   10 * time.Second,        MaxHeaderBytes: 1 << 20,    }    if err := s.ListenAndServe(); err != nil {        return err    }    return nil}和原生的http.Server基本一样,应该比较好理解。mux其实也是一个Handler,这就是整个Handler处理链。现在我们就差一个main主函数来启动我们整个程序了。

  

//控制并发的锁var mutex = &sync.Mutex{}func main() {    //加载env配置文件    err := godotenv.Load()    if err != nil {        log.Fatal(err)    }    //开启一个goroutine生成一个创世区块    go func() {        t := time.Now()        genesisBlock := Block{}        genesisBlock = Block{0, t.String(), 0, calculateHash(genesisBlock), ""}        spew.Dump(genesisBlock)        mutex.Lock()        Blockchain = append(Blockchain, genesisBlock)        mutex.Unlock()    }()    log.Fatal(run())}整个main函数并不太复杂,主要就是加载env配置文件,开启一个go协程生成一个创世区块并且添加到区块链的第一个位置,然后就是通过run函数启动Web服务。

  

一个区块链都有一个创世区块,也就是第一个区块。有了第一个区块我们才能添加第二个,第三个,第N个区块。创世区块因为是第一个区块,所以它是没有PrevHash的。

  

终于可以运行了,假设我们设置的PORT是8080,现在我们通过go run main.go启动这个简易的区块链程序,就可以看到控制台输出的创世区块信息。然后我们通过浏览器打开http://localhost:8080也可以看到这个区块链的信息,里面只有一个创世区块。

  

如果我们要新增一个区块,通过curl或者postman,向http://localhost:8080 发送body格式为{"BPM":60}的POST的信息即可。然后在通过浏览器访问http://localhost:8080查看区块链信息,验证是否已经添加成功。

  

到这里,整个源代码的分析已经完了,我们看下这个简易的区块链涉及到多少知识:

  

sha256散列字节到16进制转换并发同步锁Web服务配置文件后向式链表结构体JSON……等等,上面的很多知识,我已经在文章中讲解或者通过以前些的文章说明,大家可以看一下,详细了解。

相关文章