天天看點

BlockChain (V1)

// 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")

	}

}
           

繼續閱讀