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

简介: 一个基于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目录下。

一个基于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目录下。

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

constdifficulty=4

 

typeBlockstruct {

    Indexint

    Timestampstring

    BMPint

    HashCodestring

    PreHashstring

    Diffint

    Nonceint

}

 

varBlockchain []Block

typeMessagestruct {

    BlockPostMessageint

}

varmutex=&sync.Mutex{}

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

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

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

最后定义一个互斥锁。

三、生成区块

funcgenerateBlock(oldBlockBlock, BlockPostMessageint) Block {

    varnewBlockBlock

    newBlock.PreHash=oldBlock.HashCode

   

    newBlock.Index=oldBlock.Index+1

   

    t :=time.Now()

    newBlock.Timestamp=t.String()

   

    newBlock.BlockPostMessage=BlockPostMessage

   

    newBlock.Diff=difficulty

 

    fori :=0; ; i++ {

        newBlock.Nonce++

        hash :=calculateHash(newBlock)

        fmt.Println(hash)

       

        ifisHashValid(hash, newBlock.Diff) {

            fmt.Println("挖矿成功")

            newBlock.HashCode=hash

            returnnewBlock

        }

    }

}

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

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

四、生成哈希值

funccalculateHash(blockBlock) string {

    hashed :=strconv.Itoa(block.Index) +block.Timestamp+

        strconv.Itoa(block.Nonce) +strconv.Itoa(block.BlockPostMessage) +

        block.PreHash

    sha :=sha256.New()

    sha.Write([]byte(hashed))

    hash :=sha.Sum(nil)

    returnhex.EncodeToString(hash)

}

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

五、区块校验

funcisHashValid(hashstring, difficultyint) bool {

    prefix :=strings.Repeat("0", difficulty)

    returnstrings.HasPrefix(hash, prefix)

}

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

六、启动HTTP服务器

funcrun() 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,

    }

    iferr :=s.ListenAndServe(); err!=nil {

        returnerr

    }

    returnnil

}

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

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

七、回调函数的编写

funcmakeMuxRouter() http.Handler {

    muxRouter :=mux.NewRouter()

    muxRouter.HandleFunc("/",

        handGetBlockchain).Methods("GET")

    muxRouter.HandleFunc("/",

        handWriteBlock).Methods("POST")

    returnmuxRouter

}

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

八、处理Get请求

funchandGetBlockchain(whttp.ResponseWriter, r*http.Request) {

    bytes, err :=json.MarshalIndent(Blockchain, "", "\t")

    iferr!=nil {

        http.Error(w, err.Error(), http.StatusInternalServerError)

        return

    }

    io.WriteString(w, string(bytes))

}

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

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

image.png

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

九、处理POST请求

funchandWriteBlock(writerhttp.ResponseWriter, request*http.Request) {

    writer.Header().Set("Content-Type", "application/json")

    varmessageMessage

    decoder :=json.NewDecoder(request.Body)

    iferr :=decoder.Decode(&message); err!=nil {

        responseWithJSON(writer, request, http.StatusNotFound, request.Body)

    }

 

    deferrequest.Body.Close()

 

    mutex.Lock()

    newBlock :=generateBlock(Blockchain[len(Blockchain)-1], message.BPM)

    mutex.Unlock()

 

    ifisBlockValid(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文件我们这里就只需要填写一个端口号。

image.png

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

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

十三、运行结果

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

image.png

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

image.png

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

image.png

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


目录
相关文章
|
1天前
|
SQL 数据挖掘 数据库
服务器数据恢复—意外断电导致XenServer虚拟机不可用的数据恢复案例
服务器数据恢复环境: 一台服务器中有一组由4块STAT硬盘通过RAID卡组建的RAID10阵列,上层是XenServer虚拟化平台,虚拟机安装Windows Server操作系统,作为Web服务器使用。 服务器故障: 因机房异常断电导致服务器中一台VPS(Xen Server虚拟机)不可用,虚拟磁盘文件丢失。
服务器数据恢复—意外断电导致XenServer虚拟机不可用的数据恢复案例
|
9天前
|
网络协议
keepalived对后端服务器的监测方式实战案例
关于使用keepalived进行后端服务器TCP监测的实战案例,包括配置文件的编辑和keepalived服务的重启,以确保配置生效。
23 1
keepalived对后端服务器的监测方式实战案例
|
2天前
|
JavaScript 前端开发
【前端web入门第一天】03 综合案例 个人简介与vue简介
该网页采用“从上到下,先整体再局部”的制作思路,逐步分析并编写代码实现个人简介页面。内容涵盖尤雨溪的背景、学习经历及主要成就,同时介绍其开发的Vue.js框架特点。代码结构清晰,注重细节处理,如使用快捷键提高效率,预留超链接位置等,确保最终效果符合预期。
|
10天前
|
存储 运维 小程序
服务器数据恢复—双循环RAID5阵列数据恢复案例
服务器存储数据恢复环境: 一台存储中有一组由7块硬盘组建的RAID5阵列,存储中还有另外3块盘是raid中掉线的硬盘(硬盘掉线了,管理员只是添加一块的新的硬盘做rebuild,并没有将掉线的硬盘拔掉)。整个RAID5阵列的存储空间划分了一个LUN。 服务器存储故障: 硬盘出现故障导致存储中阵列瘫痪。 和管理员沟通,据管理员说是磁盘阵列中某些硬盘出现故障导致存储不可用,初步判断RAID中有硬盘掉线了。
|
11天前
|
API C# 开发框架
WPF与Web服务集成大揭秘:手把手教你调用RESTful API,客户端与服务器端优劣对比全解析!
【8月更文挑战第31天】在现代软件开发中,WPF 和 Web 服务各具特色。WPF 以其出色的界面展示能力受到欢迎,而 Web 服务则凭借跨平台和易维护性在互联网应用中占有一席之地。本文探讨了 WPF 如何通过 HttpClient 类调用 RESTful API,并展示了基于 ASP.NET Core 的 Web 服务如何实现同样的功能。通过对比分析,揭示了两者各自的优缺点:WPF 客户端直接处理数据,减轻服务器负担,但需处理网络异常;Web 服务则能利用服务器端功能如缓存和权限验证,但可能增加服务器负载。希望本文能帮助开发者根据具体需求选择合适的技术方案。
41 0
|
11天前
|
Rust 安全 开发者
惊爆!Xamarin 携手机器学习,开启智能应用新纪元,个性化体验与跨平台优势完美融合大揭秘!
【8月更文挑战第31天】随着互联网的发展,Web应用对性能和安全性要求不断提高。Rust凭借卓越的性能、内存安全及丰富生态,成为构建高性能Web服务器的理想选择。本文通过一个简单示例,展示如何使用Rust和Actix-web框架搭建基本Web服务器,从创建项目到运行服务器全程指导,帮助读者领略Rust在Web后端开发中的强大能力。通过实践,读者可以体验到Rust在性能和安全性方面的优势,以及其在Web开发领域的巨大潜力。
22 0
|
11天前
|
前端开发 大数据 数据库
🔥大数据洪流下的决战:JSF 表格组件如何做到毫秒级响应?揭秘背后的性能魔法!💪
【8月更文挑战第31天】在 Web 应用中,表格组件常用于展示和操作数据,但在大数据量下性能会成瓶颈。本文介绍在 JavaServer Faces(JSF)中优化表格组件的方法,包括数据处理、分页及懒加载等技术。通过后端分页或懒加载按需加载数据,减少不必要的数据加载和优化数据库查询,并利用缓存机制减少数据库访问次数,从而提高表格组件的响应速度和整体性能。掌握这些最佳实践对开发高性能 JSF 应用至关重要。
25 0
|
11天前
|
Java 数据库 API
JSF与JPA的史诗级联盟:如何编织数据持久化的华丽织锦,重塑Web应用的荣耀
【8月更文挑战第31天】JavaServer Faces (JSF) 和 Java Persistence API (JPA) 分别是构建Java Web应用的用户界面组件框架和持久化标准。结合使用JSF与JPA,能够打造强大的数据驱动Web应用。首先,通过定义实体类(如`User`)和配置`persistence.xml`来设置JPA环境。然后,在JSF中利用Managed Bean(如`UserBean`)管理业务逻辑,通过`EntityManager`执行数据持久化操作。
21 0
|
11天前
|
UED 存储 自然语言处理
【语言无界·体验无疆】解锁Vaadin应用全球化秘籍:从代码到文化,让你的应用畅游世界每一个角落!
【8月更文挑战第31天】《国际化与本地化实战:构建多语言支持的Vaadin应用》详细介绍了如何使用Vaadin框架实现应用的国际化和本地化,提升用户体验和市场竞争力。文章涵盖资源文件的创建与管理、消息绑定与动态加载、日期和数字格式化及文化敏感性处理等方面,通过具体示例代码和最佳实践,帮助开发者构建适应不同语言和地区设置的Vaadin应用。通过这些步骤,您的应用将更加灵活,满足全球用户需求。
25 0
|
11天前
|
API 数据库 UED
全面解析构建高性能API的秘诀:运用Entity Framework Core与异步编程提升Web应用响应速度及并发处理能力的详细指南与实践案例
【8月更文挑战第31天】本文详细介绍了如何利用 Entity Framework Core (EF Core)的异步编程特性构建高性能 API。通过创建基于 EF Core 的 .NET Core Web API 项目,配置数据库上下文,并定义领域模型,文章展示了如何使用异步方法进行数据查询、加载相关实体及事务处理。具体代码示例涵盖了 GET、POST、PUT 和 DELETE 操作,全面展示了 EF Core 异步编程的优势,有助于提升 API 的响应速度和处理能力。
21 0