区块链学习Day08(Pow案例实现下)

上次回顾
写了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的优缺点

  • 优点
    基于经济学原理,区块链奖励,能吸引人,鼓励更多人参与
    实现了相对的公平

  • 缺点
    需要算力,直接消耗资源,浪费能源
    安全性待考量

猜你喜欢

转载自blog.csdn.net/weixin_44774466/article/details/135187041