[区块链\理解BTCD源码]GO语言实现一个区块链原型

  • 时间:
  • 浏览:1
  • 来源:大发快3_快3是真的吗_大发快3是真的吗

摘要

本文构建了有有一个 多使用工作量证明机制(POW)的类BTC的区块链。将区块链持久化到有有一个 多Bolt数据库中,然时要提供有有一个 多简单的命令行接口,用来完成不要 不要 与区块链的交互操作。这篇文章目的是希望帮助亲戚亲戚亲戚朋友理解BTC源码的架构,不要 不要 不要 不要 主要专注于的实现原理及存储上,暂时忽略了 “分布式” 你這個每段。严格来说还还还可以有无有有一个 多删改意义上的区块链系统。

开发环境

语言:GO;

数据库:BoltDB;

IDE: Goland或不要 不要 工具还还还可以;

系统:不限,本文使用windows。

BoltDB数据库

实际上,确定任何有有一个 多数据库还还还可以,本文先用的是BoltDB。在比特币白皮书中,并如此 提到要使用哪有有一个 多具体的数据库,它删改取决于开发者如保确定。现在是比特币的有有一个 多参考实现,Bitcoin core使用的是是LevelDB。

BoltDB安装及使用还还还可以参考《BoltDB简单使用教程》

BoltDB有如下优点:

  1. 非常简单和简约
  2. 用 Go 实现
  3. 不时要运行有有一个 多服务器
  4. 还还还可以允许亲戚亲戚亲戚朋友构造让你的数据底部形态

将会 Bolt 意在用于提供不要 不要 底层功能,简洁便成为其关键所在。它的API 并不要 ,不要 不要 不要 不要 我仅关注值的获取和设置。仅此而已。

Bolt 使用键值存储,数据被存储为键值对(key-value pair,就像 Golang 的 map)。键值对被存储在 bucket 中,这是为了将类事的键值对进行分组(类事 RDBMS 中的表格)。不要 不要 不要 不要 我,为了获取有有一个 多值,你时要知道有有一个 多 bucket 和有有一个 多键(key)。

注意:Bolt 数据库如此 数据类型:键和值时要字节数组(byte array)。鉴于时要在里边存储 Go 的底部形态(准确来说,要是要 不要 不要 不要 我存储(块)Block),亲戚亲戚亲戚朋友时要对它们进行序列化,要是要 不要 不要 不要 我,实现有有一个 多从 Go struct 转换到有有一个 多 byte array 的机制,同时还还还可以从有有一个 多 byte array 再转换回 Go struct。我虽然亲戚亲戚亲戚朋友将会使用 encoding/gob 来完成你這個目标,但实际上也还还还可以确定使用 JSON, XML, Protocol Buffers 等等。不言而喻确定使用 encoding/gob, 是将会它很简单,不要 不要 不要 不要 我是 Go 标准库的一每段。

区块链原型的函数架构

系统实现

1.区块文件block.go

该每段主要包括:

对区块底部形态的定义;创建区块的最好的法子NewBlock();区块的序列化Serialize()与反序列化Deserialize()函数;以及创世区块的生成NewGenesisBlock()

//定义有有一个

多区块的底部形态Block
type Block struct {
    //版本号
    Version int64
    //父区块头哈希值
    PreBlockHash []byte
    //当前区块的Hash值, 为了繁杂代码
    Hash []byte
    //Merkle根
    MerkleRoot []byte
    //时间戳
    TimeStamp int64
    //难度值
    Bits int64
    //随机值
    Nonce int64

    //交易信息
    Data []byte
}

//提供有有一个

多创建区块的最好的法子
func NewBlock(data string, preBlockHash []byte) *Block {
    var block Block
    block = Block{
        Version:      1,
        PreBlockHash: preBlockHash,
        //Hash TODO
        MerkleRoot: []byte{},
        TimeStamp:  time.Now().Unix(),
        Bits:       targetBits,
        Nonce:      0,
        Data:       []byte(data)}
    //block.SetHash()
    pow := NewProofOfWork(&block)
    nonce, hash := pow.Run()
    block.Nonce = nonce
    block.Hash = hash

    return &block
}

// 将 Block 序列化为有有一个

多字节数组
func (block *Block) Serialize() []byte {
    var buffer bytes.Buffer
    encoder := gob.NewEncoder(&buffer)

    err := encoder.Encode(block)
    CheckErr("Serialize", err)

    return buffer.Bytes()
}

// 将字节数组反序列化为有有一个

多 Block
func Deserialize(data []byte) *Block {

    if len(data) == 0 {
        return nil
    }

    var block Block

    decoder := gob.NewDecoder(bytes.NewReader(data))
    err := decoder.Decode(&block)
    CheckErr("Deserialize", err)

    return &block
}


//创世块
func NewGenesisBlock() *Block {
    return NewBlock("Genesis Block", []byte{})
}

2.区块链blockChain.go

该每段内容主要包括:

  • 定义有有一个 多区块链底部形态BlockChain底部形态体

  • 提供有有一个 多创建BlockChain的最好的法子NewBlockChain()

亲戚亲戚亲戚朋友希望NewBlockchain实现的功能有

  1. 打开有有一个 多数据库文件
  2. 检查文件里边有无将会存储了有有一个 多区块链
  3. 将会将会存储了有有一个 多区块链:
    1. 创建有有一个 多新的 Blockchain 实例
    2. 设置 Blockchain 实例的 tip 为数据库中存储的最后有有一个 多块的哈希
  4. 将会如此 区块链:
    1. 创建创世块
    2. 存储到数据库
    3. 将创世块哈希保存为最后有有一个 多块的哈希
    4. 创建有有一个 多新的 Blockchain 实例,初始时 tail 指向创世块( tail存储的是最后有有一个 多块的哈希值)
  • 提供有有一个 多加带区块的最好的法子AddBlock(data string)
  • 迭代器对区块进行遍历。
const dbFile = "blockchain.db"
const blocksBucket = "bucket"
const lastHashKey = "key"

//定义有有一个

多区块链底部形态BlockChain
type BlockChain struct {
    //blocks []*Block
    //数据库的操作句柄
    db *bolt.DB
    //tail尾巴,表示最后有有一个

多区块的哈希值
    //在链的末端将会出現短暂分叉的清况

,不要

不要

不要

不要

确定tail我我虽然要是要

不要

不要

不要

我确定了哪条链
    tail []byte
}

//提供有有一个

多创建BlockChain的最好的法子
func NewBlockChain() *BlockChain {
    // 打开有有一个

多 BoltDB 文件
    //func Open(path string, mode os.FileMode, options *Options) (*DB, error)
    db, err := bolt.Open(dbFile, 03000, nil)
    //utils中的校验函数,校验错误
    CheckErr("NewBlockChain1", err)

    var lastHash []byte

    err = db.Update(func(tx *bolt.Tx) error {
        bucket := tx.Bucket([]byte(blocksBucket))

        // 将会数据库中不存在bucket,要去创建创世区块,将数据填写到数据库的bucket中
        if bucket == nil {
            fmt.Println("No existing blockchain found. Creating a new one...")
            genesis := NewGenesisBlock()

            bucket, err := tx.CreateBucket([]byte(blocksBucket))
            CheckErr("NewBlockChain2", err)

            err = bucket.Put(genesis.Hash, genesis.Serialize())
            CheckErr("NewBlockChain3", err)

            err = bucket.Put([]byte(lastHashKey), genesis.Hash)
            CheckErr("NewBlockChain4", err)
            lastHash = genesis.Hash
        } else {
            //直接读取最后区块的哈希值
            lastHash = bucket.Get([]byte(lastHashKey))
        }

        return nil
    })

    CheckErr("db.Update", err)

    return &BlockChain{db, lastHash}
}

//提供有有一个

多加带区块的最好的法子
func (bc *BlockChain) AddBlock(data string) {
    var preBlockHash []byte

    err := bc.db.View(func(tx *bolt.Tx) error {
        bucket := tx.Bucket([]byte(blocksBucket))
        if bucket == nil {
            os.Exit(1)
        }

        preBlockHash = bucket.Get([]byte(lastHashKey))
        return nil
    })
    CheckErr("AddBlock-View", err)

    block := NewBlock(data, preBlockHash)
    err = bc.db.Update(func(tx *bolt.Tx) error {
        bucket := tx.Bucket([]byte(blocksBucket))
        if bucket == nil {
            os.Exit(1)
        }

        err = bucket.Put(block.Hash, block.Serialize())
        CheckErr("AddBlock1", err)

        err = bucket.Put([]byte(lastHashKey), block.Hash)
        CheckErr("AddBlock2", err)
        bc.tail = block.Hash
        return nil
    })
    CheckErr("AddBlock-Update", err)
}

//迭代器,不要

不要

不要

不要

我有有一个

多对象,它里边富含了有有一个

多游标,时不时向前/后移动,完成整个容器的遍历
type BlockChainIterator struct {
    currentHash []byte
    db          *bolt.DB
}

//创建迭代器,同时初始化为指向最后有有一个

多区块
func (bc *BlockChain) NewIterator() *BlockChainIterator {
    return &BlockChainIterator{bc.tail, bc.db}
}

// 返回链中的下有有一个

多块
func (it *BlockChainIterator) Next() (block *Block) {

    err := it.db.View(func(tx *bolt.Tx) error {
        bucket := tx.Bucket([]byte(blocksBucket))
        if bucket == nil {
            return nil
        }
        data := bucket.Get(it.currentHash)
        block = Deserialize(data)
        it.currentHash = block.PreBlockHash
        return nil
    })
    CheckErr("Next", err)
    return
}

3.工作量证明机制POW.go

该每段主要包括:

创建POW的最好的法子NewProofOfWork(block *Block)

计算哈希值的最好的法子 Run() (int64, []byte)

//定义有有一个

多工作量证明的底部形态ProofOfWork
type ProofOfWork struct {
    block *Block
    //目标值
    target *big.Int
}

//难度值常量
const targetBits = 20

//创建POW的最好的法子
func NewProofOfWork(block *Block) *ProofOfWork {

    //000000000000000... 01
    target := big.NewInt(1)
    //0x30000000000000...00
    target.Lsh(target, uint(256-targetBits))

    pow := ProofOfWork{block: block, target: target}
    return &pow
}

//给Run()准备数据
func (pow *ProofOfWork) PrepareData(nonce int64) []byte {
    block := pow.block
    tmp := [][]byte{
        /*
            时要将block中的不类事型都转化为byte,以便进行连接
        */
        IntToByte(block.Version),
        block.PreBlockHash,
        block.MerkleRoot,
        IntToByte(block.TimeStamp),
        IntToByte(nonce),
        IntToByte(targetBits),
        block.Data}
    //func Join(s [][]byte, sep []byte) []byte
    data := bytes.Join(tmp, []byte{})
    return data
}

//计算哈希值的最好的法子
func (pow *ProofOfWork) Run() (int64, []byte) {
    /*伪代码
    for nonce {
        hash := sha256(block数据 + nonce)
        if 转换(Hash)< pow.target{
            找到了
        }else{
        nonce++
        }
    }
    return nonce,hash{:}
    */
    //1.拼装数据
    //2.哈希值转成big.Int类型
    var hash [32]byte
    var nonce int64 = 0
    var hashInt big.Int

    fmt.Println("Begin Minding...")
    fmt.Printf("target hash :    %x\n", pow.target.Bytes())

    for nonce < math.MaxInt64 {
        data := pow.PrepareData(nonce)
        hash = sha256.Sum256(data)

        hashInt.SetBytes(hash[:])
        // Cmp compares x and y and returns:
        //
        //   -1 if x <  y
        //    0 if x == y
        //   +1 if x >  y
        //
        //func (x *Int) Cmp(y *Int) (r int) {
        if hashInt.Cmp(pow.target) == -1 {
            fmt.Printf("found hash  :%x,nonce :%d\n,", hash, nonce)
            break
        } else {
            //fmt.Printf("not found nonce,current nonce :%d,hash : %x\n", nonce, hash)
            nonce++
        }
    }
    return nonce, hash[:]
}

//校验函数
func (pow *ProofOfWork) IsValid() bool {
    var hashInt big.Int

    data := pow.PrepareData(pow.block.Nonce)
    hash := sha256.Sum256(data)
    hashInt.SetBytes(hash[:])

    return hashInt.Cmp(pow.target) == -1
}

4.命令函交互CLI.go

注意这每段时要使用标准库里边的 flag 包来解析命令行参数;

首先,创建有有一个 多子命令: addblock 和 printchain, 不要 不要 不要 不要 我给 addblock 加带 --data 标志。printchain 如此 标志;

不要 不要 不要 不要 我,检查用户输入的命令并解析相关的 flag 子命令;

最后检查解析是哪有有一个 多子命令,并调用相关函数执行。

具体如下:

//将会是多行的,不要

不要

不要

不要

用反引号`···`包一下,还还还可以实现多行字符串的拼接,不时要转义!
//命令行提示
const usage = `
Usage:
  addBlock -data BLOCK_DATA    "add a block to the blockchain"
  printChain                   "print all the blocks of the blockchain"
`
const AddBlockCmdString = "addBlock"
const PrintChainCmdString = "printChain"

//输出提示函数
func (cli *CLI) printUsage() {
    fmt.Println("Invalid input!")
    fmt.Println(usage)
    os.Exit(1)
}

//参数检查函数
func (cli *CLI) validateArgs() {
    if len(os.Args) < 2 {
        fmt.Println("invalid input!")
        cli.printUsage()
    }
}

func (cli *CLI) Run() {
    cli.validateArgs()

    addBlockCmd := flag.NewFlagSet(AddBlockCmdString, flag.ExitOnError)
    printChainCmd := flag.NewFlagSet(PrintChainCmdString, flag.ExitOnError)
    //func (f *FlagSet) String(name string, value string, usage string) *string
    addBlocCmdPara := addBlockCmd.String("data", "", "Block data")

    switch os.Args[1] {
    case AddBlockCmdString:
        //加带动作
        err := addBlockCmd.Parse(os.Args[2:])
        CheckErr("Run()1", err)
        if addBlockCmd.Parsed() {
            if *addBlocCmdPara == "" {
                fmt.Println("addBlock data not should be empty!")
                cli.printUsage()
            }
            cli.AddBlock(*addBlocCmdPara)
        }
    case PrintChainCmdString:
        //打印输出
        err := printChainCmd.Parse(os.Args[2:])
        CheckErr("Run()2", err)
        if printChainCmd.Parsed() {
            cli.PrintChain()
        }
    default:
        //命令不符合规定,输出提示信息
        cli.printUsage()
    }
}

区块链操作演示效果:

首先 go build 编译程序运行;输入不带--data参数的错误命令,查看提示。

输入交易信息,查看pow运算:

打印区块链已有区块信息:

Reference:

最还会感谢Ivan Kuznetsov在GitHub社区的贡献!