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