区块链研习社我爱编程

golang实现比特币公链进阶V1:基本数据结构

2018-06-10  本文已影响22人  ccup区块链

golang实现比特币公链进阶:V1

本文为作者加强练习手敲代码,可直接运行到本地,先创建V1文件夹,然后分别创建以下文件即可。
本文借鉴https://github.com/Jeiwan/blockchain_go

字节 字段 说明
4 区块大小 用字节表示的该字段之后的区块大小
80 区块头 组成区块头的几个字段
1-9 交易计数器 该区块包含的交易数量,包含coinbase交易
不定 交易 记录在区块里的交易信息,使用原生的交易信息格式,并且交易在数据流中的位置必须与Merkle树的叶子节点顺序一致
字节 字段 说明
4 版本 区块版本号,表示本区块遵守的验证规则
32 父区块头哈希值 前一区块的哈希值,使用SHA256(SHA256(父区块头))计算
32 Merkle根 该区块中交易的Merkle树根的哈希值,同样采用SHA256(SHA256())计算
4 时间戳 该区块产生的近似时间,精确到秒的UNIX时间戳,必须严格大于前11个区块时间的中值,同时全节点也会拒绝那些超出自己2个小时时间戳的区块
4 难度目标 该区块工作量证明算法的难度目标,已经使用特定算法编码
4 Nonce 为了找到满足难度目标所设定的随机数,为了解决32位随机数在算力飞升的情况下不够用的问题,规定时间戳和coinbase交易信息均可更改,以此扩展nonce的位数

block.go

定义区块block结构体,并且实现创建区块,设置哈希的功能

package main

import (
    "time"
    "crypto/sha256"
    "bytes"
)

type Block struct {
    //区块头
    Version       int64
    PrevBlockHash []byte
    Hash          []byte //为了方便实现,做的临时的简化,正常比特币区块是不包含自己的哈希值
    TimeStamp     int64
    TargetBits    int64
    Nonce         int64
    MerKelRoot    []byte

    Data []byte //区块体,为了简化,临时用,实际应该是交易信息
}

func NewBlock(data string, preBlockHash []byte) *Block {
    //初始化区块各个字段,此处暂时是随便写的
    block := &Block{
        Version:       1,
        PrevBlockHash: preBlockHash,
        //Hash:,后面会计算
        TimeStamp:  time.Now().Unix(),
        TargetBits: 10,
        Nonce:      5,
        MerKelRoot: []byte{},
        Data:       []byte(data),
    }

    block.SetHash()
    return block

}

func (block *Block) SetHash() {

    temp := [][]byte{
        //int 转byte方法
        IntToByte(block.Version),
        block.PrevBlockHash,
        IntToByte(block.TimeStamp),
        block.MerKelRoot,
        IntToByte(block.Nonce),
        block.Data,
    }

    //将区块的各个字段连接成一个切片
    data := bytes.Join(temp, []byte{})

    //对区块进行sha256哈希算法,返回值为数组,不是切片
    hash := sha256.Sum256(data)

    block.Hash = hash[:]//由数组转化为切片

}

//创世区块,即第一个区块,它的前一个区块哈希值为空
func NewGenesisBlock() *Block {
    return NewBlock("Genesis Block", []byte{})
}



utils.go

提取的公共方法,以后期重复使用。

package main

import (
    "bytes"
    "encoding/binary"
    "fmt"
    "os"
)

func IntToByte(num int64)[]byte  {
    var buffer bytes.Buffer
    //二进制写法
    err:=binary.Write(&buffer,binary.BigEndian,num)

    CheckErr(err)

    return buffer.Bytes()
}





func CheckErr(err error)  {
    if err!=nil {
        fmt.Println("IntToByte err occur :",err)
        os.Exit(1)
    }
}



blockchain.go

创建区块链结构体,此处仅以数组作为简易模仿链的操作,并且实现创建区块链和添加区块的操作

package main

import "os"

type BlockChain struct {
    //构造区块链结构,使用数组存储区块
    blocks []*Block
}
//创建区块链实例,并且添加第一个创世块
func NewBlockChain()*BlockChain  {
    return &BlockChain{[]*Block{NewGenesisBlock()}}
}


//添加区块的到链上
func (bc *BlockChain)AddBlock(data string)  {
    //避免越界访问
    if len(bc.blocks)<=0 {
        os.Exit(1)
    }

    //取出链上的最后一个区块,使用其哈希值,构造新区块
    prevBlockHash:=bc.blocks[len(bc.blocks)-1].Hash

    newBlock:=NewBlock(data,prevBlockHash)

    //添加到链数组中
    bc.blocks=append(bc.blocks,newBlock)




}

main.go

简单测试操作区块链,添加区块,读取链数据的功能

package main

import "fmt"

func main() {
    bc := NewBlockChain()
    bc.AddBlock("bob send 1btc to Alice")
    bc.AddBlock("bob send 1btc to Alice")

    for i, block := range bc.blocks {
        fmt.Println("=============block num:", i)
        fmt.Printf("data:%s\n", string(block.Data))
        fmt.Println("Version:", block.Version)
        fmt.Printf("PreHash:%x\n", block.PrevBlockHash)
        fmt.Printf("Hash:%x\n", block.Hash)
        fmt.Printf("Time:%d\n", block.TimeStamp)
        fmt.Printf("Nonce:%d\n", block.Nonce)
    }

}


执行

在终端中,进入V1文件目录下,
1.编译

go build *.go

2.执行,编译后的可执行文件

./block
上一篇下一篇

猜你喜欢

热点阅读