GO实现BTC-V1-链条

# git代码管理
git clone https://gitee.com/bwcs/beijing_go_term2.git
会下载一个文件夹,名字:`beijing_go_term2`
进入到beijing_go_term2文件夹
执行git fetch origin v1:v1 拿到远程的v1并在本地创建一个v1
切换到v1分支:git checkout v1
git branch 查看当前分支
当远程有提交需要更新本地仓库时,执行:git pull,提示如下:
git branch –set-upstream-to=origin/ v1
执行:把本地的v1与远程的vi关联起来
git branch –set-upstream-to=origin/v1 v1
再次执行:git pull
定义,创建,打印区块
package main
import "fmt"
const genesisInfo = "The Times 03/Jan/2009 Chancellor on brink of second bailout for banks"
type Block struct {
PrevBlockHash []byte //前区块哈希
Hash []byte //当前区块哈希
Data []byte //数据,目前使用字节流,v4开始使用交易代替
}
//创建区块,对Block的每一个字段填充数据即可
func NewBlock(data string, prevBlockHash []byte) *Block {
block := Block{
PrevBlockHash: prevBlockHash,
Hash: []byte{}, //先填充为空,后续会填充数据
Data: []byte(data),
}
return &block
}
func main() {
fmt.Printf("helloworld\n")
block := NewBlock(genesisInfo, []byte{0x0000000000000000})
fmt.Printf("PrevBlockHash : %x\n", block.PrevBlockHash)
fmt.Printf("Hash : %x\n", block.Hash)
fmt.Printf("Data : %s\n", block.Data)
}
实现setHash函数
//为了生成区块哈希,我们实现一个简单的函数,来计算哈希值,没有随机值,没有难度值
func (block *Block) SetHash() {
var data []byte
data = append(data, block.PrevBlockHash...)
data = append(data, block.Data...)
hash /*[32]byte*/ := sha256.Sum256(data)
block.Hash = hash[:]
}
在NewBlock中调用
func NewBlock(data string, prevBlockHash []byte) *Block {
block := Block{
PrevBlockHash: prevBlockHash,
Hash: []byte{}, //先填充为空,后续会填充数据
Data: []byte(data),
}
block.SetHash() //<<<<-----看这里
return &block
}
区块链定义及遍历打印
//创建区块链,使用Block数组模拟
type BlockChain struct {
Blocks []*Block
}
//实现创建区块链的方法
func NewBlockChain() *BlockChain {
//在创建的时候添加一个区块:创世块
genesisBlock := NewBlock(genesisInfo, []byte{0x0000000000000000})
bc := BlockChain{Blocks: []*Block{genesisBlock}}
return &bc
}
func main() {
fmt.Printf("helloworld\n")
//block := NewBlock(genesisInfo, []byte{0x0000000000000000})
bc := NewBlockChain()
for _, block := range bc.Blocks {
fmt.Printf("PrevBlockHash : %x\n", block.PrevBlockHash)
fmt.Printf("Hash : %x\n", block.Hash)
fmt.Printf("Data : %s\n", block.Data)
}
}
添加区块
func (bc *BlockChain) AddBlock(data string) {
//1. 创建一个区块
//bc.Blocks的最后一个区块的Hash值就是当前新区块的PrevBlockHash
lastBlock := bc.Blocks[len(bc.Blocks)-1]
prevHash := lastBlock.Hash
block := NewBlock(data, prevHash)
//2. 添加到bc.Blocks数组中
bc.Blocks = append(bc.Blocks, block)
}
代码重构
添加block.go
添加blockchain.go
更新补充区块字段
type Block struct {
Version uint64 //区块版本号
PrevBlockHash []byte //前区块哈希
MerKleRoot []byte //先填写为空,后续v4的时候使用
TimeStamp uint64 //从1970.1.1至今的秒数
Difficulity uint64 //挖矿的难度值, v2时使用
Nonce uint64 //随机数,挖矿找的就是它!
Data []byte //数据,目前使用字节流,v4开始使用交易代替
Hash []byte //当前区块哈希, 区块中本不存在的字段,为了方便我们添加进来
}
更新NewBlock函数
//创建区块,对Block的每一个字段填充数据即可
func NewBlock(data string, prevBlockHash []byte) *Block {
block := Block{
Version: 00,
PrevBlockHash: prevBlockHash,
MerKleRoot: []byte{},
TimeStamp: uint64(time.Now().Unix()),
Difficulity: 10, //随便写的,v2再调整
Nonce: 10, //同Difficulty
Data: []byte(data),
Hash: []byte{}, //先填充为空,后续会填充数据
}
block.SetHash()
return &block
}
更新SetHash函数
//为了生成区块哈希,我们实现一个简单的函数,来计算哈希值,没有随机值,没有难度值
func (block *Block) SetHash() {
var data []byte
//uintToByte将数字转成[]byte{}, 在utils.go实现
data = append(data, uintToByte(block.Version)...)
data = append(data, block.PrevBlockHash...)
data = append(data, block.MerKleRoot...)
data = append(data, uintToByte(block.TimeStamp)...)
data = append(data, uintToByte(block.Difficulity)...)
data = append(data, block.Data...)
data = append(data, uintToByte(block.Nonce)...)
hash /*[32]byte*/ := sha256.Sum256(data)
block.Hash = hash[:]
}
添加空函数uintToByte
创建新文件`utils.go`,添加内容如下:
package main
//这是一个工具函数文件
func uintToByte(num uint64) []byte {
//TODO
//具体实现暂略,后面再写
return []byte{}
}
编码逻辑实现
package main
import (
"bytes"
"encoding/binary"
"log"
)
//这是一个工具函数文件
func uintToByte(num uint64) []byte {
//使用binary.Write来进行编码
var buffer bytes.Buffer
//编码要进行错误检查,一定要做
err := binary.Write(&buffer, binary.BigEndian, num)
if err != nil {
log.Panic(err)
}
return buffer.Bytes()
}
使用bytes.Join改写函数
//为了生成区块哈希,我们实现一个简单的函数,来计算哈希值,没有随机值,没有难度值
func (block *Block) SetHash() {
//var data []byte
//
////uintToByte将数字转成[]byte{}, 在utils.go实现
//data = append(data, uintToByte(block.Version)...)
//data = append(data, block.PrevBlockHash...)
//data = append(data, block.MerKleRoot...)
//data = append(data, uintToByte(block.TimeStamp)...)
//data = append(data, uintToByte(block.Difficulity)...)
//data = append(data, block.Data...)
//data = append(data, uintToByte(block.Nonce)...)
tmp := [][]byte{
uintToByte(block.Version),
block.PrevBlockHash,
block.MerKleRoot,
uintToByte(block.TimeStamp),
uintToByte(block.Difficulity),
block.Data,
uintToByte(block.Nonce),
}
data := bytes.Join(tmp, []byte{})
hash /*[32]byte*/ := sha256.Sum256(data)
block.Hash = hash[:]
}
demo 二维数组切片连接演示
package main
import (
"strings"
"fmt"
"bytes"
)
func main() {
strsA := []string{"hello", "world", "demo"}
strRes := strings.Join(strsA, "=")
fmt.Printf("strRes : %s\n", strRes)
//func Join(s [][]byte, sep []byte) []byte {
joinRes := bytes.Join([][]byte{[]byte("hello"), []byte("world"), []byte("demo")}, []byte{})
fmt.Printf("joinRes: %s\n", joinRes)
}
打印block字段
for i, block := range bc.Blocks {
fmt.Printf("+++++++++++++++ %d ++++++++++++++\n", i)
fmt.Printf("Version : %d\n", block.Version)
fmt.Printf("PrevBlockHash : %x\n", block.PrevBlockHash)
fmt.Printf("MerKleRoot : %x\n", block.MerKleRoot)
timeFormat := time.Unix(int64(block.TimeStamp), 0).Format("2006-01-02 15:04:05")
fmt.Printf("TimeStamp : %s\n", timeFormat)
fmt.Printf("Difficulity : %d\n", block.Difficulity)
fmt.Printf("Nonce : %d\n", block.Nonce)
fmt.Printf("Hash : %x\n", block.Hash)
fmt.Printf("Data : %s\n", block.Data)
pow := NewProofOfWork(block)
fmt.Printf("IsValid: %v\n", pow.IsValid())
}