作者:ReganYue 来源: 恒生LIGHT云社区

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

一、安装第三方库

go get github.com/davecgh/go-spew/spew

这个库的功能是在命令行格式化输出内容。

go get github.com/gorilla/mux

这个开发包是用来编写Web处理程序的。

go get github.com/joho/godotenv

这个工具包是读取.env后缀名的文件中的数据,如果是Linux环境,.env文件放置在项目的根目录即可,如果是Windows和Mac OS,.env文件需要放在GOPATH/src目录下。

二、定义区块信息、难度系数

const difficulty = 4type Block struct {Index intTimestamp stringBMP intHashCode stringPreHash stringDiff intNonce int
}var Blockchain []Block
type Message struct {BlockPostMessage int
}
var mutex = &sync.Mutex{}

这里我们定义一下挖矿生成区块的难度值,然后定义区块,包含区块高度、时间戳、交易信息、当前的Hash值和上一个的Hash值,还有难度和随机值。

然后定义区块链,用区块数组。

然后我们这里要根据Get请求和Post请求来生成区块,所以定义一个消息结构体用于存储Post信息。

最后定义一个互斥锁。

三、生成区块

func generateBlock(oldBlock Block, BlockPostMessage int) Block {var newBlock BlocknewBlock.PreHash = oldBlock.HashCodenewBlock.Index = oldBlock.Index + 1t := time.Now()newBlock.Timestamp = t.String()newBlock.BlockPostMessage = BlockPostMessagenewBlock.Diff = difficultyfor i := 0; ; i++ {newBlock.Nonce++hash := calculateHash(newBlock)fmt.Println(hash)if isHashValid(hash, newBlock.Diff) {fmt.Println("挖矿成功")newBlock.HashCode = hashreturn newBlock}}
}

每次生成新的区块前,先获取先前区块的Hash值放置在这个区块的上一个区块Hash值,然后获取当前时间,通过String()方法转换成为时间戳后放入区块的Timestamp。然后将Post传递的消息放入区块,将我们固定不变的困难值放入区块。

然后循环挖矿,每次挖矿将随机数加一,然后先不管这个区块能不能成功并入区块链,得先计算它的哈希值才能知道,然后校验哈希值的前导0,如果成功就输出挖矿成功。

四、生成哈希值

func calculateHash(block Block) string {hashed := strconv.Itoa(block.Index) + block.Timestamp +strconv.Itoa(block.Nonce) + strconv.Itoa(block.BlockPostMessage) +block.PreHashsha := sha256.New()sha.Write([]byte(hashed))hash := sha.Sum(nil)return hex.EncodeToString(hash)
}

很简单的逻辑,将区块的数据拼接后用sha256进行加密,得到hash值。

五、区块校验

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 Messagedecoder := 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请求,已经有新的区块在区块链中了。

【区块链】一个基于web服务器的PoW案例相关推荐

  1. 一个基于Web服务器的PoW区块链案例

    一个基于web服务器的PoW案例 一.安装第三方库 go get github.com/davecgh/go-spew/spew 这个库的功能是在命令行格式化输出内容. go get github.c ...

  2. ETH:Windows搭建ETH(区块链技术)利用Web端和小程序端两种方式调用ETH上的SC智能合约

    ETH:Windows搭建ETH(区块链技术)利用Web端和小程序端两种方式调用ETH上的SC智能合约 目录 1.Geth安装.配置文件.与ETH节点交互 1.1.下载并安装好geth客户端 1.2. ...

  3. 搭建区块链浏览器——基于hyperledger fabric 1.0,MySQL容器

    搭建区块链浏览器--基于hyperledger fabric 1.0,MySQL容器 区块链 hyperledger fabric 浏览器 MySQL docker  Contents 环境要求 分支 ...

  4. AIBlockChain:“知名博主独家讲授”人工智能创新应用竞赛【精选实战作品】之《基于计算机视觉、自然语言处理和区块链技术的乘客智能报警系统》案例的界面简介、功能介绍分享之总篇

    AI&BlockChain:"知名博主独家讲授"人工智能创新应用竞赛[精选实战作品]之<基于计算机视觉.自然语言处理和区块链技术的乘客智能报警系统>案例的界面简 ...

  5. AIBlockChain:“知名博主独家讲授”人工智能创新应用竞赛【精选实战作品】之《基于计算机视觉、自然语言处理和区块链技术的乘客智能报警系统》案例的界面简介、功能介绍分享之区块链技术

    AI&BlockChain:"知名博主独家讲授"人工智能创新应用竞赛[精选实战作品]之<基于计算机视觉.自然语言处理和区块链技术的乘客智能报警系统>案例的界面简 ...

  6. [翻译]深入理解Tornado——一个异步web服务器

    本人的第一次翻译,转载请注明出处:http://www.cnblogs.com/yiwenshengmei/archive/2011/06/08/understanding_tornado.html ...

  7. 从源码开始编译一个带有WEB服务器功能的小型LINUX(下)

    上接:从源码开始编译一个带有WEB服务器功能的小型LINUX(上) 七.为新构建的ToyLinux启用虚拟控制台 这个可以通过宿主机来实现,也可以直接启动刚构建成功的小Linux进行配置.我们这里采用 ...

  8. AIBlockChain:“知名博主独家讲授”人工智能创新应用竞赛【精选实战作品】之《基于计算机视觉、自然语言处理和区块链技术的乘客智能报警系统》案例的界面简介、功能介绍分享之计算机视觉技术

    AI&BlockChain:"知名博主独家讲授"人工智能创新应用竞赛[精选实战作品]之<基于计算机视觉.自然语言处理和区块链技术的乘客智能报警系统>案例的界面简 ...

  9. JAVA化妆品销售网,区块链技术基于SSM的化妆品销售网站、基于JavaWeb的化妆品在线商城源码...

    第8179篇区块链技术文章区块链技术基于SSM的化妆品销售网站.基于JavaWeb的化妆品在线商城源码 需求分析 基于SSM技术设计实现一个化妆品销售网站, 支持商家在线售卖化妆品, 整个网站设计采用 ...

  10. 夸克服务器过载或暂停维修,夸克区块链存储中心化服务器

    原标题:夸克区块链存储中心化服务器 现在互联网面对的都是海量的数据.海量的用户.我们为了提高的读取.写入能力,一般都优胜分布式的方式来存储数据,比如分布式缓存.我们有海量的数据需要缓存,所以一个缓存机 ...

最新文章

  1. POJ 2749 Building roads 2-sat+二分答案
  2. 查看TCP进程各状态连接数 IP封掉 的两个脚本
  3. response.setcharacterencoding 报错是缺哪个包_出街,你缺的是这一款包包...
  4. 【Linux】一步一步学Linux——mii-tool命令(154)
  5. java 的toString()函数
  6. oracle数据库安装提示M,Python第13课:oracle数据库的安装
  7. mysql分组和where条件查询,mysql中where和having条件查询的区别
  8. halcon学习-网格校正
  9. STM32矩阵键盘的实现原理
  10. 如何给linux添加新硬盘(转)
  11. 开关电容共模反馈学习
  12. 小程序源码:喝酒娱乐小游戏助力神器-多玩法安装简单
  13. OpenCV开发笔记(四十九):红胖子8分钟带你深入了解轮廓识别(图文并茂+浅显易懂+程序源码)
  14. 游戏地图与场景设计常用名词
  15. EasyRTMP Android安卓手机直播推流摄像头偏暗的问题解决
  16. PHP签名不合法或拉黑,拒绝做微商被屏蔽拉黑,方法我有!-微商教程
  17. 我天!中国科技原来有桎梏!道翰天琼认知智能机器人API平台接口为您揭秘-1。
  18. 2017年8月21日 星期一
  19. 认识DPDK的UIO驱动(一)
  20. 日本最省女孩15年买下3套房的启示

热门文章

  1. 在局域网中安装和设置防病毒服务器(10)(转)
  2. ssm+jsp计算机毕业设计阳泉市旅游网站的开发与设计i0tjd(程序+lw+源码+远程部署)
  3. C\C++ Thread 分析线程detach()
  4. 保安日记:番外篇之软件测试(一)
  5. kubernetes管理节点(master)计算节点(node)设置可调度与不可调度、驱逐节点上的pod
  6. echarts 设置仪表盘数字的位置_汽车仪表盘维修多少钱
  7. 读书笔记--文字符号
  8. OpenCV图像处理(二十--大结局)---OpencCV VS Matplotlib显示图像
  9. Linux-Level1-day11: 大型程序编译makefile;复合类型之结构体(核心);联合体union
  10. android无法连接到相机,Android应用开发之无法连接相机怎么解决?