// block.go
package main
import "time"
import "crypto/sha256"
import "bytes"
type Block struct {
Version int64 //版本資訊
PrevBlockHash []byte //前一個區塊的hash值
Hash []byte //本區塊的hash值,為了友善而做了一些簡化,正常比特币區塊不包含自己的hash值
TimeStamp int64 //時間戳,用于标記産生的時間
TargetBits int64 //難度值
Nonce int64 //随機值
MerKelRoot []byte //默克爾根
Data []byte //區塊體,簡化,正常來說是一個交易
//正常分區塊頭和區塊體,這裡友善編寫,是以寫在了一起
}
func NewBlock(data string, prevBlockHash []byte) *Block { //建立交易資訊
//data 是交易,prev是前一個區塊的hash值
block := &Block{ //初始化區塊各個字段的資料
Version: 1,
PrevBlockHash: prevBlockHash,
//Hash:
TimeStamp: time.Now().Unix(), //時間戳
TargetBits: 10, //沒有涉及pow證明,後期選個合理的
Nonce: 5, //工作量證明的,此處示範
MerKelRoot: []byte{}, //根據交易計算出的
Data: []byte(data)}
//現有的内容做一個hash運算
block.SetHash() //設定各個字段的hash值
return block
}
func (block *Block) SetHash() {
//比特币用的是sha256算法
tmp := [][]byte{
//實作Int類型轉換為byte類型的工具函數
IntToByte(block.Version), //
block.PrevBlockHash,
//hash不用放了
IntToByte(block.TimeStamp), //
block.MerKelRoot,
IntToByte(block.Nonce), //三個類型不對,把int轉換成byte,提供工具包,來解決這個問題。
block.Data}
//join 将區塊的各個字段連接配接成一個切片,使用[]byte{}空切片連接配接,目的是避免污染原區塊的資訊
data := bytes.Join(tmp, []byte{}) //把所有指定的切片用分割符分割
//對區塊進行sha256算法,傳回值為[32]byte數組
hash := sha256.Sum256(data)
block.Hash = hash[:] //由數組轉化為切片
}
//建立比特币的創世塊,即第一個區塊,他的前一個區塊的hash為空
func NewGenesisBlock() *Block {
return NewBlock("Genesis Block!", []byte{})
}
//blockchain.go
package main
import "os"
type BlockChain struct { //構造區塊鍊結構,使用數組來存儲所有區塊
blocks []*Block
}
//建立區塊鍊執行個體,并且添加第一個創世塊
func NewBlockChain() *BlockChain {
//new一個創世塊
return &BlockChain{[]*Block{NewGenesisBlock()}} //填第一個元素
} //到此,完成了區塊鍊的構造
//添加其他的區塊的操作
func (bc *BlockChain) AddBlock(data string) {
//校驗數組的元素個數,避免出現通路越界情況!!!直接操作下标有風險
if len(bc.blocks) <= 0 {
os.Exit(1)
}
//取出最後一個區塊,目的是得到其hash值
lastBlock := bc.blocks[len(bc.blocks)-1] //找到最後一個區塊的下标,因為是數組存儲
prevBlockHash := lastBlock.Hash
//構造新區塊,且添加至整個數組中
block := NewBlock(data, prevBlockHash) //交易資訊和上一個區塊的hash
bc.blocks = append(bc.blocks, block)
}
//utls.go
package main
import "encoding/binary"
import "bytes"
import "fmt"
import "os"
func IntToByte(num int64) []byte {
var buffer bytes.Buffer //用buffer轉換
err := binary.Write(&buffer, binary.BigEndian, num) //三個參數,傳入的buffer,對齊方式,任何類型
if err != nil {
fmt.Println("IntToByte err occur:", err)
os.Exit(1)
}
return buffer.Bytes()
}
/*
func CheckErr(err error){
if err!=nil{
fmt.Println("IntToByte err occur:", err)
os.Exit(1)
}
}
*/
//main.go
package main
import "fmt"
func main() {
bc := NewBlockChain()
bc.AddBlock("班長轉給老師一枚BTC")
bc.AddBlock("班長又轉給老師一枚BTC")
bc.AddBlock("小明轉給老師一枚BTC")
for i, block := range bc.blocks {
fmt.Println("==========block num:", i)
fmt.Println("Data:\t", string(block.Data))
fmt.Println("Version:\t", block.Version)
fmt.Printf("PrevBlockHash:\t %x\n", block.PrevBlockHash)
fmt.Printf("Hash:\t\t %x\n", block.Hash)
fmt.Printf("TimeStamp:\t %d\n", block.TimeStamp)
fmt.Printf("MerKel:\t %x\n", block.MerKelRoot)
fmt.Printf("Nonce:\t %d\n", block.Nonce)
fmt.Println("\n\n")
}
}