美文网首页区块链研习社我爱编程
golang实现比特币公链进阶V1:基本数据结构

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

作者: ccup区块链 | 来源:发表于2018-06-10 13:37 被阅读22次

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

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

    • 区块整体结构
    字节 字段 说明
    4 区块大小 用字节表示的该字段之后的区块大小
    80 区块头 组成区块头的几个字段
    1-9 交易计数器 该区块包含的交易数量,包含coinbase交易
    不定 交易 记录在区块里的交易信息,使用原生的交易信息格式,并且交易在数据流中的位置必须与Merkle树的叶子节点顺序一致
    • 区块头结构(6个字段)
    字节 字段 说明
    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
    

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/tpvfeftx.html