工做量證實(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()) }