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()) }