写点什么

一个基于 web 服务器的 PoW 案例(二)

作者:Regan Yue
  • 2021 年 11 月 14 日
  • 本文字数:2576 字

    阅读完需:约 8 分钟

一个基于web服务器的PoW案例(二)

一个基于 web 服务器的 PoW 案例

本文收录于我的专栏:细讲区块链


本专栏会讲述区块链共识算法以及以太坊智能合约、超级账本智能合约、EOS 智能合约相关知识,还会详细的介绍几个实战项目。如果有可能的话,我们还能一起来阅读以太坊的源码。有兴趣的话我们一起来学习区块链技术吧~


五、区块校验

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


这个我们本专栏之前的文章介绍了,在此简单说一下,这里我们就校验一下哈希值前面的零的数量是不是和难度值一致。

六、启动 HTTP 服务器

func run() error {  mux := makeMuxRouter()  httpAddr := os.Getenv("PORT")  log.Println("Listening on ", httpAddr)      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}
复制代码


我们先从.env 文件中获取 PORT 的值。然后监听获取的端口号。http.Server 是设置 http 服务器的参数,其中 Addr 是地址,ReadTimeout、WriteTimeout 分别是读写超时时间,然后是设置请求头的数据大小的最大值,1 << 20 是位运算,算出来就是 1MB。!!!最重要的就是回调函数了,这里需要我们自己编写来处理 Get 和 Post 请求。


然后我们就来监听事件并且根据监听到的事件来服务。

七、回调函数的编写

func makeMuxRouter() http.Handler {  muxRouter := mux.NewRouter()  muxRouter.HandleFunc("/",    handGetBlockchain).Methods("GET")  muxRouter.HandleFunc("/",    handWriteBlock).Methods("POST")  return muxRouter}
复制代码


mux.NewRouter()是用来创建路由,muxRouter.HandleFunc("/",handGetBlockchain).Methods("GET")是根据你访问的目录和请求类型来调用指定的方法。这里是使用 Get 方法访问根目录就调用 handGetBlockchain 方法。同样的,muxRouter.HandleFunc("/",handWriteBlock).Methods("POST")就是使用 Post 请求访问根目录时就调用 handWriteBlock 方法。

八、处理 Get 请求

func handGetBlockchain(w http.ResponseWriter, r *http.Request) {  bytes, err := json.MarshalIndent(Blockchain, "", "\t")  if err != nil {    http.Error(w, err.Error(), http.StatusInternalServerError)    return  }  io.WriteString(w, string(bytes))}
复制代码


我们需要将数据转换为 json 格式,便于与前端进行交互。


同样我们的参数分别是响应和请求。然后处理错误,当出现 500 错误时,也就是 http.StatusInternalServerError,我们将 err.Error()写入 w:



如果没出错,就将 json 数据写入 w。

九、处理 POST 请求

func handWriteBlock(writer http.ResponseWriter, request *http.Request) {  writer.Header().Set("Content-Type", "application/json")  var message Message  decoder := json.NewDecoder(request.Body)  if err := decoder.Decode(&message); err != nil {    responseWithJSON(writer, request, http.StatusNotFound, request.Body)  }
defer request.Body.Close()
mutex.Lock() newBlock := generateBlock(Blockchain[len(Blockchain)-1], message.BPM) mutex.Unlock()
if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) { Blockchain = append(Blockchain, newBlock) spew.Dump(Blockchain) } //返回响应信息 responseWithJSON(writer, request, http.StatusCreated, newBlock)}
复制代码


因为需要服务器响应结果为 json,先设置响应头的"Content-Type"为"application/json"。然后从 request 中读取 JSON 数据,将 JSON 数据转成 Message。如果转换失败,就交给下一步处理异常,如果成功就创建新的区块。


这里使用 defer,说明我们要记得关闭请求哦~


然后添加区块时要记得上锁,可以防止同个时间点多个 POST 请求生成区块。


接下来就要校验生成的区块是否正确,如果正确就加入区块链中。

十、处理异常

func responseWithJSON(writer http.ResponseWriter, request *http.Request,  code int, inter interface{}) {      writer.Header().Set("Content-Type", "application/json")  response, err := json.MarshalIndent(inter, "", "\t")  if err != nil {    writer.WriteHeader(http.StatusInternalServerError)    writer.Write([]byte("HTTP 500:Server Error"))    return  }  writer.WriteHeader(code)  writer.Write(response)}
复制代码


如果将传入的 inter 转换为 json 格式的数据没有出现错误就往响应头写入响应码,并将数据写入。

十一、校验区块是否正确

func isBlockValid(newBlock, oldBlock Block) bool {  if oldBlock.Index+1 != newBlock.Index {    return false  }  if oldBlock.HashCode != newBlock.PreHash {    return false  }  if calculateHash(newBlock) != newBlock.HashCode {    return false  }  return true}
复制代码


这里校验了新区块的 index 是否等于原来最后一个区块的 index 加一,新区块的 PreHash 应该等于之前区块链最后一个区块的 HashCode。然后还需要再一次计算区块的哈希值,进行比对。

十二、主逻辑

然后我们现在用 Go 实现通过 http 请求来完成区块链。


func main() {  err := godotenv.Load()  if err != nil {    log.Fatal(err)  }
go func() { t := time.Now() genessisBlock := Block{} genessisBlock = Block{0, t.String(), 0, calculateHash(genessisBlock), "", difficulty, 0} mutex.Lock() Blockchain = append(Blockchain, genessisBlock) mutex.Unlock() spew.Dump(genessisBlock) }()
//创建http服务器的启动函数 log.Fatal(run())}
复制代码


godotenv.Load()加载一个文件,如果不填写参数,就默认是加载.env 文件。


这个.env 文件我们这里就只需要填写一个端口号。



这里我们先将创世区块加入区块链。然后用 spew.Dump()将其格式化输出到命令行。


最后我们会要用 run 来启动 http 服务器。

十三、运行结果

我们可以使用 curl 来进行 get 和 post 请求。



这是 get 请求,得到区块链。



这是进行 post 请求,新建一个区块加到了区块链。



可以看到再次 get 请求,已经有新的区块在区块链中了。

发布于: 3 小时前阅读数: 5
用户头像

Regan Yue

关注

还未添加个人签名 2020.08.12 加入

对Go、Python、网络安全、区块链感兴趣. · 华为云云享专家 · 掘金资讯创作者

评论

发布
暂无评论
一个基于web服务器的PoW案例(二)