200行go语言代码自建一个区块链 体验挖矿乐趣

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/technofiend/article/details/84073073

谈谈区块链:
挖矿的目的:通过挖矿证明算力,防止他人作弊,自己又能获得奖励【给自己加钱】。
挖矿的过程:将网上别的合法且最新的用户交易同步过来,加入到区块,然后加随机数哈希后 与系统给出的值去比较。若符合条件则表示挖矿成功,挖矿成功了还不行,要赶紧告诉别人,我挖成功了,别让其他人抢风头。

挖矿更通俗的表现形式:我这里有编号为 1~1000000000000000个宝箱,其中某些宝箱里面有金子,获得这些金子的方法就是一个个去用钥匙打开,而这个宝箱钥匙的锻造方法就是用 其他人提供的金属来打造。

挖矿需要实时联网吗?
答案:否,只需要把最后的一个区块的哈希放进本区块的PrevHash即可,一般10分钟就更新最后一个区块了,所以还是得10分钟上链同步一下,至于别人提交的交易的账单,可放可不放到区块上,当然交易的账单里面有手续费,若放到区块可以将里面的手续费划到自己账上, 到时候世界上的矿都挖光了,矿场节点就是通过帮别人打包赚点手续费而已,但一般矿场都几乎实时在线同步其他节点收到的交易过来。

区块链以最长链为主链:
若有人捣乱,发一些错误的区块到其他节点上,其他节点不是傻子,肯定不接受,因此无法加入到链上。
除非,有一个算力很强的人想捣乱,他能在1秒钟算出正确的区块,并发布到其他节点,会出现什么情况?这个人由于算力太强了,他在算出区块的时候,不接受任何人的交易记录就生成区块了,那么对于用户来说,用户的交易久久不能进链,自然而然的放弃使用区块链了,而别的矿工看到如此强劲的对手也纷纷离场。

若同时算出区块,并提交到链上的情况:
有两个人同时算出答案,并告知其他小伙伴,我已经算出了答案。他们两个人都告诉了别人。那么他们谁才是胜利者?
这种情况则以下一个人算出的答案来决定上一次的胜利者。
由于下一个人是最新算出的,所以任何人无异议。

关于浪费
挖矿花掉的电力差不多150多个国家电力的总和,要是把挖矿改成算π,那样多少都有点贡献。
呃… 挖矿真的浪费了好多电力,于是乎有人发明一种不需要挖矿的方法,这种方式叫proof of shake,股权证明,大家有兴趣可以去研究一下。

比特币是泡沫吗?
不是,它是人类共同认可的一种虚拟货币,只不过它的价格决定于人民的认可度,而且没人能凭空造出比特币,因为比特币有很强的防伪,若有人伪造,一下子就能瞄出来了,在账本上别人账户上有多少钱都能看到,但是花不了,若要花自己的比特币之前,需要用一把超级安全的防伪造印章去盖一个章,就可以使用授权的钱了。
不妨我们改一种说法,美元是泡沫吗?可它只是一张纸啊,只不过它的价格决定于人民的认可度还有国家的调控,而且国家能凭空造出美元,因为美元由一个美国控制,别人不能通过银行看别人的账户,使用前只需要在银行输入密码取钱出来就可以与人交换货物或者服务了。

关于空气币
有某些黑恶原子份子制作了他们的区块链虽说是真的,但是缺乏人民的认可度,因此不值钱,那么他们通过什么方式赚钱?还是挖矿(挖一大部分币在自己手里先)----然后大肆宣扬什么什么新技术,大家赶紧过来买啊买啊买,哄抬市价,然后将手上的币卖出,只不过开始挖矿易如反掌,后期人多了,挖不了了,就产生了价值,但挖的人不多,币还是容易挖,容易挖就不值钱了,这种就叫做空气币。

package main

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/gorilla/mux"
)

const difficulty = 1 // 真正的比特币每两个星期就更新一次难度,其实是共识更新。

// 区块链的 块结构
type Block struct {
	Index      int
	Timestamp  string
	BPM        int
	Hash       string
	PrevHash   string
	Difficulty int
	Nonce      string
}

// 区块链的链(这里是块的一个数组,真正的是用kv数据库保存的呢!)
var Blockchain []Block

// 心跳值
type Message struct {
	BPM int
}

// 一把锁
var mutex = &sync.Mutex{}

func main() {
	// 创世块,啥都没
	go func() {
		t := time.Now()
		genesisBlock := Block{}
		genesisBlock = Block{
			0,
			t.String(),
			0,
			calculateHash(genesisBlock),
			"",
			difficulty,
			"",
		}

		spew.Dump(genesisBlock) // 显示创世块

		// 将创世块加入链
		mutex.Lock()
		Blockchain = append(Blockchain, genesisBlock)
		mutex.Unlock()
	}()

	// 开启HTTP服务器
	log.Fatal(run())
}

// 网页服务器
func run() error {
	mux := makeMuxRouter()
	httpPort := "8080"
	log.Println("HTTP Server Listening on port :", httpPort)
	s := &http.Server{
		Addr:           ":" + httpPort,
		Handler:        mux,
		ReadTimeout:    10 * time.Second,
		WriteTimeout:   10 * time.Second,
		MaxHeaderBytes: 1 << 20,
	}

	if err := s.ListenAndServe(); err != nil {
		return err
	}

	return nil
}

// 创建路由
func makeMuxRouter() http.Handler {
	muxRouter := mux.NewRouter()
	muxRouter.HandleFunc("/", handleGetBlockchain).Methods("GET")
	muxRouter.HandleFunc("/", handleWriteBlock).Methods("POST")
	return muxRouter
}

// http 方法,得到当前所有区块
func handleGetBlockchain(w http.ResponseWriter, r *http.Request) {
	bytes, err := json.MarshalIndent(Blockchain, "", "  ")
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}
	io.WriteString(w, string(bytes))
}

// 上传心跳值,并且在挖出矿后将此心跳值加入区块。
func handleWriteBlock(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "application/json")
	var m Message

	decoder := json.NewDecoder(r.Body)
	if err := decoder.Decode(&m); err != nil {
		respondWithJSON(w, r, http.StatusBadRequest, r.Body)
		return
	}
	defer r.Body.Close()

	//ensure atomicity when creating new block
	mutex.Lock()
	newBlock := generateBlock(Blockchain[len(Blockchain)-1], m.BPM)
	mutex.Unlock()

	if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) {
		Blockchain = append(Blockchain, newBlock)
		spew.Dump(Blockchain)
	}

	respondWithJSON(w, r, http.StatusCreated, newBlock)
}

// 显示出json数据
func respondWithJSON(w http.ResponseWriter, r *http.Request, code int, payload interface{}) {
	w.Header().Set("Content-Type", "application/json")
	response, err := json.MarshalIndent(payload, "", "  ")
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		w.Write([]byte("HTTP 500: Internal Server Error"))
		return
	}
	w.WriteHeader(code)
	w.Write(response)
}

// 验证区块是否正确
func isBlockValid(newBlock, oldBlock Block) bool {
	if oldBlock.Index+1 != newBlock.Index {
		return false
	}

	if oldBlock.Hash != newBlock.PrevHash {
		return false
	}

	if calculateHash(newBlock) != newBlock.Hash {
		return false
	}

	return true
}

// 计算区块哈希
func calculateHash(block Block) string {
	record := strconv.Itoa(block.Index) + block.Timestamp + strconv.Itoa(block.BPM) + block.PrevHash + block.Nonce
	h := sha256.New()
	h.Write([]byte(record))
	hashed := h.Sum(nil)
	return hex.EncodeToString(hashed)
}

// 挖矿
func generateBlock(oldBlock Block, BPM int) Block {
	var newBlock Block

	t := time.Now()

	newBlock.Index = oldBlock.Index + 1
	newBlock.Timestamp = t.String()
	newBlock.BPM = BPM
	newBlock.PrevHash = oldBlock.Hash
	newBlock.Difficulty = difficulty

	for i := 0; ; i++ {
		hex := fmt.Sprintf("%x", i)
		newBlock.Nonce = hex
		if !isHashValid(calculateHash(newBlock), newBlock.Difficulty) {
			fmt.Println(calculateHash(newBlock), " do more work!")
			time.Sleep(time.Second) // 此处延时为了模拟真实
			continue
		} else {
			fmt.Println(calculateHash(newBlock), " work done!")
			newBlock.Hash = calculateHash(newBlock)
			break
		}

	}
	return newBlock
}

// 区块验证
func isHashValid(hash string, difficulty int) bool {
	prefix := strings.Repeat("0", difficulty)
	return strings.HasPrefix(hash, prefix)
}

以上代码的原理:
该区块链的区块用于存储的【人类心跳】,好处是别人想存必须先通过挖矿。
实际的区块链将【人类心跳】换成 交易记录。
本区块链由于是单节点,因此没有 币的地址。
只阐明挖矿原理。

代码详细解析:
https://medium.com/@mycoralhealth/code-your-own-blockchain-mining-algorithm-in-go-82c6a71aba1f

请在启动服务后,安装postman,向http://127.0.0.1:8080, 提交原生数据{“BPM”: 50}

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/technofiend/article/details/84073073