工做量证实(PoW,Proof of Work)。
经过计算一个数值(nonce
),使得拼揍上交易数据后内容的 Hash 值知足规定的上限。在节点成功找到知足的Hash值以后,会立刻对全网进行广播打包区块,网络的节点收到广播打包区块,会马上对其进行验证。git
若是验证经过,则代表已经有节点成功解迷,本身就再也不竞争当前区块打包,而是选择接受这个区块,记录到本身的帐本中,而后进行下一个区块的竞争猜谜。 网络中只有最快解谜的区块,才会添加的帐本中,其余的节点进行复制,这样就保证了整个帐本的惟一性。github
假如节点有任何的做弊行为,都会致使网络的节点验证不经过,直接丢弃其打包的区块,这个区块就没法记录到总帐本中,做弊的节点耗费的成本就白费了,所以在巨大的挖矿成本下,也使得矿工自觉自愿的遵照比特币系统的共识协议,也就确保了整个系统的安全。
参考黎跃春的翻译web
代码出处算法
ADDR=8080
package main import ( "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "io" "log" "net/http" "os" "strconv" "strings" "sync" "time" "github.com/davecgh/go-spew/spew" "github.com/gorilla/mux" "github.com/joho/godotenv" ) const difficulty = 1 type Block struct { Index int Timestamp string BPM int Hash string PrevHash string Difficulty int Nonce string } var Blockchain []Block type Message struct { BPM int } var mutex = &sync.Mutex{} 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 { //复制 difficulty 个0,并返回新字符串,当 difficulty 为2 ,则 prefix 为 00 prefix := strings.Repeat("0", difficulty) // HasPrefix判断字符串 hash 是否包含前缀 prefix return strings.HasPrefix(hash, prefix) } 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 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 run() error { 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, 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 } 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) } 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 main() { err := godotenv.Load() if err != nil { log.Fatal(err) } 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() }() log.Fatal(run()) }