上次回顾
写了get和post的请求方式,而且get时查询整个区块链的信息。post添加区块来搞的
在这之前,先下载个curl,后面用得到,设置到环境变量里面去
创建个运行.txt文件内容如下
添加区块信息 curl -H "Content-Type: application/json" -X POST -d "{\ "BPM\":10}" http://localhost:9000
查看区块信息 curl http://localhost:9000
package main
import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"github.com/davecgh/go-spew/spew"
"github.com/gorilla/mux"
"github.com/joho/godotenv"
"io"
"log"
"net/http"
"os"
"strconv"
"strings"
"sync"
"time"
)
// 设置难度系数,设置为4
const diffculty = 4
// 定义区块
type Block struct {
//区块高度
Index int
//时间戳
Timestamp string
//data,保存交易信息
BMP int
//当前区块的哈希
HashCode string
//上一个区块的哈希
PreHash string
//前导0 个数Diff
Diff int
//随机值
Noce int
}
// 用数组去维护区块链
var Blockchain []Block
// 声明个锁,以防止并发问题
var mutex = &sync.Mutex{
}
// 生成区块
func generateBlock(oldBlock Block, BMP int) Block {
//声明个新区块
var newBlock Block
newBlock.PreHash = oldBlock.HashCode
newBlock.Timestamp = time.Now().String()
//不能写死了
newBlock.Index = oldBlock.Index + 1
newBlock.BMP = BMP
newBlock.Diff = diffculty
//循环挖矿
for i := 0; ; i++ {
//每挖一次,给Noce加1
newBlock.Noce++
hash := calculateHash(newBlock)
fmt.Println(hash)
//判断前导0
if isHashValid(hash, newBlock.Diff) {
//一致
fmt.Println("挖矿成功")
newBlock.HashCode = hash
//将新的区块返回
return newBlock
}
}
}
// 按照规则生成一个Hash值
func calculateHash(block Block) string {
record := strconv.Itoa(block.Index) + block.Timestamp +
strconv.Itoa(block.Noce) + strconv.Itoa(block.BMP) + block.PreHash
sha := sha256.New()
sha.Write([]byte(record))
hashed := sha.Sum(nil)
return hex.EncodeToString(hashed)
}
// 判断哈希值的前导0个数和难度系数是否一直
func isHashValid(hash string, difficulty int) bool {
prefix := strings.Repeat("0", difficulty)
//判断这个哈希值是否为这个前缀
return strings.HasSuffix(hash, prefix)
}
func main() {
//测试 随便写
//var firstBlock Block
//firstBlock.Diff = 4
//firstBlock.Noce = 0
//firstBlock.PreHash = "0"
//firstBlock.BMP = 1
//firstBlock.Index = 0
//firstBlock.HashCode = "0"
//generateBlock(firstBlock, 1)
//默认加载.env文件,内置名叫就教.env 所以新建的叫.env
err := godotenv.Load()
if err != nil {
log.Fatal(err)
}
go func() {
//创世区块
genesisBlock := Block{
}
genesisBlock = Block{
Index: 0,
Timestamp: time.Now().String(),
BMP: 0,
HashCode: calculateHash(genesisBlock),
PreHash: "",
Diff: diffculty,
}
//将区块添加到区块链
mutex.Lock()
Blockchain = append(Blockchain, genesisBlock)
mutex.Unlock()
//格式化输出到控制台
spew.Dump(genesisBlock)
}()
//作为Http服务器的启动函数
log.Fatal(run())
}
// 将run作为Http启动函数
func run() error {
//声明一个回调函数,处理get还是Post
mux := makeMuxRouter()
httpAddr := os.Getenv("ADDR")
log.Println("Listening on", os.Getenv("ADDR"))
s := &http.Server{
Addr: ":" + httpAddr,
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
//设置个最大相应头 1mb
MaxHeaderBytes: 1 << 20,
}
//监听服务打开
if err := s.ListenAndServe(); err != nil {
return err
}
return nil
}
// 回调函数
func makeMuxRouter() http.Handler {
muxRoter := mux.NewRouter()
//get 读,post写的处理
muxRoter.HandleFunc("/", handGetBlockchain).Methods("GET")
//写的操作post
muxRoter.HandleFunc("/", handWriteBlock).Methods("POSt")
return muxRoter
}
// 处理http的GET请求
// 查看区块链的信息
func handGetBlockchain(w http.ResponseWriter, r *http.Request) {
//转json
bytes, err := json.MarshalIndent(Blockchain, "", "\t")
if err != nil {
//服务器的错误
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
io.WriteString(w, string(bytes))
}
// 声明POST形式发送数据的数据类型
type Message struct {
BMP int
}
// 处理Http的Post请求
func handWriteBlock(writer http.ResponseWriter, requset *http.Request) {
//设置相应结果为json格式
writer.Header().Set("Content-Type", "application/json")
var message Message
//创建json解码器,从request中读取数据
decoder := json.NewDecoder(requset.Body)
if err := decoder.Decode(&message); err != nil {
//没有取成功,写出错误码,返回错误
respondWithJSON(writer, requset, http.StatusNotFound, requset.Body)
return
}
//别忘记释放资源
defer requset.Body.Close()
//生成区块,还得操作锁
mutex.Lock()
//创建新的区块
newBlock := generateBlock(Blockchain[len(Blockchain)-1], message.BMP)
mutex.Unlock()
//判断区块的合法性
if isBlockValic(newBlock, Blockchain[len(Blockchain)-1]) {
//将区块正真的添加到链上
Blockchain = append(Blockchain, newBlock)
//格式化输出
spew.Dump(Blockchain)
}
//返回相应信息
respondWithJSON(writer, requset, http.StatusCreated, newBlock)
}
// 若错误,服务器返回500
func respondWithJSON(writer http.ResponseWriter, requset *http.Request, code int, inter interface{
}) {
//设置相应头
writer.Header().Set("Content-Type", "application/json")
//格式化输出json
response, err := json.MarshalIndent(inter, "", "\t")
//若转换失败。返回500
if err != nil {
writer.WriteHeader(http.StatusInternalServerError)
writer.Write([]byte("HTTP:500:Server Error"))
return
}
//没有错误,返回指定状态码
writer.WriteHeader(code)
//返回指定数据
writer.Write(response)
}
// 判断区块的合法性
func isBlockValic(newBlock, oldBlock Block) bool {
//判断index值是否正确
if oldBlock.Index+1 == newBlock.Index {
return false
}
//判断区块哈希是正确
if oldBlock.HashCode != newBlock.PreHash {
return false
}
//再次计算哈希值进行比对,在之前的文章有提到比特币源码,双重比对
if calculateHash(newBlock) != newBlock.HashCode {
return false
}
//前面都认证通过了
return true
}
运行启动!
结果如下
输入刚刚文本得命令,新建控制台
总结PoW的优缺点
-
优点
基于经济学原理,区块链奖励,能吸引人,鼓励更多人参与
实现了相对的公平 -
缺点
需要算力,直接消耗资源,浪费能源
安全性待考量