我们接着写一个函数,用来计算给定的数据的 SHA256 散列值:
func calculateHash(block Block) string {
record := string(block.Index) + block.Timestamp + string(block.BPM) + block.PrevHash
h := sha256.New()
h.Write([]byte(record))
hashed := h.Sum(nil)
return hex.EncodeToString(hashed)
}
这个 calculateHash 函数接受一个块,通过块中的 Index,Timestamp,BPM,以及 PrevHash 值来计算出 SHA256 散列值。接下来我们就能编写一个生成块的函数:
func generateBlock(oldBlock Block, BPM int) (Block, error) {
var newBlock Block
t := time.Now()
newBlock.Index = oldBlock.Index + 1
newBlock.Timestamp = t.String()
newBlock.BPM = BPM
newBlock.PrevHash = oldBlock.Hash
newBlock.Hash = calculateHash(newBlock)
return newBlock, nil
}
其中,Index 是从给定的前一块的 Index 递增得出,时间戳是直接通过 time.Now() 函数来获得的,Hash 值通过前面的 calculateHash 函数计算得出,PrevHash 则是给定的前一个块的 Hash 值。
校验块
搞定了块的生成,接下来我们需要有函数帮我们判断一个块是否有被篡改。检查 Index 来看这个块是否正确得递增,检查 PrevHash 与前一个块的 Hash 是否一致,再来通过 calculateHash 检查当前块的 Hash 值是否正确。通过这几步我们就能写出一个校验函数:
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 replaceChain(newBlocks []Block) {
if len(newBlocks) > len(Blockchain) {
Blockchain = newBlocks
}
}
到这一步,我们基本就把所有重要的函数完成了。接下来,我们需要一个方便直观的方式来查看我们的链,包括数据及状态。通过浏览器查看 web 页面可能是最合适的方式!
Web 服务
我猜你一定对传统的 web 服务及开发非常熟悉,所以这部分你肯定一看就会。
借助 Gorilla/mux 包,我们先写一个函数来初始化我们的 web 服务:
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
}









