比特币原理及其代码实践1-区块链数据结构


比特币原理及其代码实践1-区块链数据结构

Jeiwan/blockchain_go: A simplified blockchain implementation in Golang (github.com)

https://gitee.com/Oracion/go-blockchain-learning

引言:

区块链本质只是一个分布式数据库而已。

区块链具有去中心化(全节点保留所有区块)匿名(address(公私所生成的)和我本人没了联系)可追溯(UTXO)不可更改(merkleTree)的特点

那么这些特点是如何实现的呢??

如果从原理代码的角度,相信大家可以更好的理解这些特点。

区块链的开山之作,比特币

中本聪在2008年11月1日提出,并于2009年1月3日正式诞生,区块链也因此进入了人们的视野

区块链的层次结构

区块链层次结构

我们将实现最下面的三层

区块链结构

如果要我们自己设计区块链的数据结构,应该如何设计:

  • 指向前一个区块链的hash值
  • 自己的hash值
  • 时间戳
  • 数据(交易信息)

image-20230222104609842

数据结构-区块

//区块
type Block struct {
    Timestamp     int64 //时间戳
    Data          []byte //数据
    PrevBlockHash []byte //前一个区块的hash
    Hash          []byte //自己的hash
}

其中,时间戳、前一个hash、自己的hash就被叫做区块的header

真实的比特币的区块比这个会再复杂一些,会多版本、挖矿难度、merkleTree的hash等字段

这些字段在之后的讲解中也会加上的,现在我们暂时不做那么复杂

区块需要实现的方法

//计算区块自己的hash值
//区块的hash= hash(PrevBlockHash + Data(交易的merkleTree的hash + timestamp )
func (b *Block) SetHash() {
    //伪代码
    b.hash= sha256(PrevBlockHash + Data + timestamp)
}

可以看到,我们使用的hash函数是sha256

于是,我们有了新建区块的方法

//新建一个区块
func NewBlock(data string, prevBlockHash []byte) *Block {
    //伪代码
    block := &Block{now, data, prevBlockHash}
    block.SetHash()
    return block
}

数据结构-区块链

区块连起来,就是区块链

链式结构的实现有两种方式:

  • 数组
  • 指针

我们先使用最简单的数组,之后的实现中会用到指针

//区块链
type Blockchain struct {
    blocks []*Block
}

//添加区块
func (bc *Blockchain) AddBlock(data string) {
    prevBlock := bc.blocks[len(bc.blocks)-1]
    newBlock := NewBlock(data, prevBlock.Hash)
    bc.blocks = append(bc.blocks, newBlock)
}

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

//创建区块链
func NewBlockchain() *Blockchain {
    return &Blockchain{[]*Block{NewGenesisBlock()}}
}

运行

func main() {
    bc := NewBlockchain()

    bc.AddBlock("Send 1 BTC to Ivan")
    bc.AddBlock("Send 2 more BTC to Ivan")

    for _, block := range bc.blocks {
        fmt.Printf("Prev. hash: %x\n", block.PrevBlockHash)
        fmt.Printf("Data: %s\n", block.Data)
        fmt.Printf("Hash: %x\n", block.Hash)
        fmt.Println()
    }
}