首页 » 搜狗SEO » 区块链seo源码_Go措辞200行写区块链源代码分析

区块链seo源码_Go措辞200行写区块链源代码分析

访客 2024-11-05 0

扫一扫用手机浏览

文章目录 [+]

Github上有一个Repo,是一个利用Go措辞(golang),不到200行代码写的区块链源代码,准确的说是174行。
原作者起了个名字是 Code your own blockchain in less than 200 lines of Go! 而且作者也为此写了一篇文章。
https://medium.com/@mycoralhealth/code-your-own-blockchain-in-less-than-200-lines-of-go-e296282bcffc

这篇文章是一个大概的思路和代码的实现,当然还有很多代码的逻辑没有涉及,以是我就针对这不到200行的代码进行一个剖析,包含原文章里没有涉及到的知识点,对Go措辞,区块链都会有一个更深的认识。

区块链seo源码_Go措辞200行写区块链源代码分析 区块链seo源码_Go措辞200行写区块链源代码分析 搜狗SEO

所有的源代码都在这里:https://github.com/nosequeldeebee/blockchain-tutorial/blob/master/main.go

区块链seo源码_Go措辞200行写区块链源代码分析 区块链seo源码_Go措辞200行写区块链源代码分析 搜狗SEO
(图片来自网络侵删)

import("crypto/sha256""encoding/hex""encoding/json""io""log""net/http""os""strconv""sync""time""github.com/davecgh/go-spew/spew""github.com/gorilla/mux""github.com/joho/godotenv")

在源代码的开头,是作者引入的一些包,有标准的,也有第三方的。
像sha256,hex这些标准包是为了sha-256编码用的,其他还有启动http做事,打印日志的log,并发掌握的sync,韶光戳的time。

第三方包有三个,个中两个我都详细先容过,相信大家不会陌生。

go-spew是一个变量构造体的调试利器,可以打印出变量构造体对应的数据和构造,调试非常方便

gorilla/mux是一个web路由做事,可以很大略的帮我们构建web做事。

不过目前用gin的比较多,也推举利用gin https://github.com/gin-gonic/gin。

godotenv是一个读取配置文章的库,可以让我们读取.env格式的配置文件,比如从配置文件里读取IP、PORT等。
不过目前配置文件还是推举YAML和TOML,对应的第三方库是:

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

既然要写一个区块链,那么肯定的有一个区块的实体,我们通过golang的struct来实现。

//Blockrepresentseach'item'intheblockchaintypeBlockstruct{IndexintTimestampstringBPMintHashstringPrevHashstring}

Block里包含几个字段:

Index 便是Block的顺序索引Timestamp是天生Block的韶光戳BPM,作者说代表心率,每分钟心跳数Hash是通过sha256天生的散列值,对全体Block数据的HashPrevHash 上一个Block的Hash,这样区块才能连在一起构成区块链

有了区块Block了,那么区块链就非常好办了。

//BlockchainisaseriesofvalidatedBlocksvarBlockchain[]Block

便是这么大略,一个Block数组便是一个区块链。
区块链的构成关键在于Hash和PrevHash,通过他们一个个串联起来,便是一串Block,也便是区块链。
由于相互之间通过Hash和PrevHash进行关联,以是全体链很难被修改,链越长被修改的本钱越大,由于要把全体链全部改掉才能完成修改的目的,这样的话,其他节点验证这次修改肯定是不能通过的。

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

//SHA256hasingfunccalculateHash(blockBlock)string{record:=strconv.Itoa(block.Index)+block.Timestamp+strconv.Itoa(block.BPM)+block.PrevHashh:=sha256.New()h.Write([]byte(record))hashed:=h.Sum(nil)returnhex.EncodeToString(hashed)}

sha256是golang内置的sha256的散列标准库,可以让我们很随意马虎的天生对应数据的散列值。
从源代码看,是把Block的所有字段进行字符串拼接,然后通过sha256进行散列,散列的数据再通过hex.EncodeToString转换为16进制的字符串,这样就得到了我们常见的sha256散列值,类似这样的字符串8d969eef6ecad3c29a3a629280e686cf0c3f5d5a86aff3ca12020c923adc6c92。

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

//createanewblockusingpreviousblock'shashfuncgenerateBlock(oldBlockBlock,BPMint)Block{varnewBlockBlockt:=time.Now()newBlock.Index=oldBlock.Index+1newBlock.Timestamp=t.String()newBlock.BPM=BPMnewBlock.PrevHash=oldBlock.HashnewBlock.Hash=calculateHash(newBlock)returnnewBlock}

由于区块链是顺序相连的,以是我们在天生一个新的区块的时候,必须知道上一个区块,也便是源代码里的oldBlock。
其余一个参数BPM便是我们须要在区块里存储的数据信息了,这里作者演示的例子是心率,我们可以换成其他业务中想要的数据。

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

在这里作者并没有利用POW(事情量证明)这类算法来天生区块,而是没有任何条件的,这里紧张是为了仿照区块的天生,演示方便。

区块可以天生了,但是天生的区块是否可信,我们还得对他进行校验,不能随便天生一个区块。
在比特币(BitCoin)中校验比较繁芜,这里作者采取了大略仿照的办法。

//makesureblockisvalidbycheckingindex,andcomparingthehashofthepreviousblockfuncisBlockValid(newBlock,oldBlockBlock)bool{ifoldBlock.Index+1!=newBlock.Index{returnfalse}ifoldBlock.Hash!=newBlock.PrevHash{returnfalse}ifcalculateHash(newBlock)!=newBlock.Hash{returnfalse}returntrue}

大略的比拟Index,Hash是否是精确的,并且重新打算了一遍Hash,防止被修改。

到了这里,关于区块链的代码已经全部完成了,剩下的便是把区块链的天生、查看等包装成一个Web做事,可以通过API、浏览器访问查看。
由于作者这里没有实现P2P网络,以是采取的是WEB做事的办法。

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

通过mux定义了两个Handler,URL都是/,但是对应的Method是不一样的。

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

funchandleGetBlockchain(whttp.ResponseWriter,rhttp.Request){bytes,err:=json.MarshalIndent(Blockchain,"","")iferr!=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函数实现,用于仿照区块的天生。

funchandleWriteBlock(whttp.ResponseWriter,rhttp.Request){w.Header().Set("Content-Type","application/json")//利用了一个Mesage构造体,更方便的存储BPMvarmsgMessage//吸收要求的数据信息,类似{"BPM":60}这样的格式decoder:=json.NewDecoder(r.Body)iferr:=decoder.Decode(&msg);err!=nil{respondWithJSON(w,r,http.StatusBadRequest,r.Body)return}deferr.Body.Close()//掌握并发,天生区块链,并且校验mutex.Lock()prevBlock:=Blockchain[len(Blockchain)-1]newBlock:=generateBlock(prevBlock,msg.BPM)//校验区块链ifisBlockValid(newBlock,prevBlock){Blockchain=append(Blockchain,newBlock)spew.Dump(Blockchain)}mutex.Unlock()//返回新的区块信息respondWithJSON(w,r,http.StatusCreated,newBlock)}

以上代码我进行了注释,便于理解。
紧张是通过POST发送一个{"BPM":60}格式的BODY来添加区块,如果格式精确,那么就天生区块进行校验,合格了就加入到区块里;如果格式不对,那么返回缺点信息。

用于掌握并发的锁可以参考Go措辞实战条记(十七)| Go 读写锁

这个方法里有个Message构造体,紧张是为了便于操作方便。

//MessagetakesincomingJSONpayloadforwritingheartratetypeMessagestruct{BPMint}

返回的JSON信息,也被抽取成了一个函数respondWithJSON,便于公用。

funcrespondWithJSON(whttp.ResponseWriter,rhttp.Request,codeint,payloadinterface{}){response,err:=json.MarshalIndent(payload,"","")iferr!=nil{w.WriteHeader(http.StatusInternalServerError)w.Write([]byte("HTTP500:InternalServerError"))return}w.WriteHeader(code)w.Write(response)}

好了,快完成了,以上Web的Handler已经好了,现在我们要启动我们的Web做事了。

//webserverfuncrun()error{mux:=makeMuxRouter()//从配置文件里读取监听的端口httpPort:=os.Getenv("PORT")log.Println("HTTPServerListeningonport:",httpPort)s:=&http.Server{Addr:":"+httpPort,Handler:mux,ReadTimeout:10time.Second,WriteTimeout:10time.Second,MaxHeaderBytes:1<<20,}iferr:=s.ListenAndServe();err!=nil{returnerr}returnnil}

和原生的http.Server基本一样,该当比较好理解。
mux实在也是一个Handler,这便是全体Handler处理链。
现在我们就差一个main主函数来启动我们全体程序了。

//掌握并发的锁varmutex=&sync.Mutex{}funcmain(){//加载env配置文件err:=godotenv.Load()iferr!=nil{log.Fatal(err)}//开启一个goroutine天生一个创世区块gofunc(){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……

等等,上面的很多知识,我已经在文章中讲解或者通过以前些的文章解释,大家可以看一下,详细理解。

标签:

相关文章