• 【区块链Go语言实现】Part 1:区块链基本原型


     

    0x00 介绍

    区块链(Blockchain)是21世纪最具革命性的技术之一,目前它仍处于逐渐成熟阶段,且其发展潜力尚未被完全意识到。从本质上讲,区块链只是一种记录的分布式数据库。但它之所以独特,是因为它并不是一个私有的数据库,而是一个公共数据库,也就是说,每个使用它的人都有一份完整或部分的数据副本。并且,只有在数据库的其他持有者同意的情况下,才可以向区块链中添加新的记录。此外,正是区块链使得加密货币和智能合约成为可能。

    在本系列文章中,我们将基于区块链构建一种简单的加密货币。

    0x01 区块

    首先,我们从“区块链”中的“区块”介绍开始。在区块链中,区块是存储有价值信息的块。例如,比特币区块用于存储交易,这是任何一种加密货币的本质。除此之外,区块还包含一些技术信息,比如它的版本、当前时间戳和前一区块的散列值(哈希值)。

    在本文中,我们不打算实现区块链或比特币规范中描述的那种区块,而是使用它的简化版本,即我们将要实现的区块结构中只包含重要的信息。下面代码中为我们的区块结构:

    type Block struct {
        Timestamp     int64
        Data          []byte
        PrevBlockHash []byte
        Hash          []byte
    }

    Timestamp(时间戳)是区块创建时刻的时间戳,Data是包含在区块中的实际有价值的信息,PrevBlockHas存储前一区块的散列值,而Hash是当前区块的散列值。在比特币规范中,Timestamp、PrevBlockHash和Hash是区块头,它们组成了一个单独的数据结构,而交易(在我们的例子中是Data)也是一个单独的数据结构。为了简单起见,我们此处将它们混合在一起。

    那么如何计算散列值呢?散列值的计算方式是区块链的一个十分重要的特性,正是该特性使得区块链变得安全。计算一个区块的散列值是一种计算上十分困难的操作,即使在快速计算机上也需要一些时间(这就是人们会购买强大的GPU来挖比特币的原因)。这是一种有意的架构设计,它使得添加新的区块变得很困难,因此可以防止对已有区块的篡改。我们将在以后的文章中讨论并实现这种机制。

    现在,我们仅仅使用区块字段,连接它们,并在连接的组合上计算一个SHA-256散列值。下面,我们使用SetHash方法来实现这一操作:

    func (b *Block) SetHash() {
        timestamp := []byte(strconv.FormatInt(b.Timestamp, 10))
        headers := bytes.Join([][]byte{b.PrevBlockHash, b.Data, timestamp}, []byte{})
        hash := sha256.Sum256(headers)
    
        b.Hash = hash[:]
    }

    接下来,依照Golang惯例,我们将实现一个函数来简化区块的创建:

    func NewBlock(data string, prevBlockHash []byte) *Block {
        block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}}
        block.SetHash()
        return block
    }

    这样,我们就准备好了区块的工作!

    0x02 区块链

    现在,我们来实现一个区块链。实质上,区块链只是一个包含某些特定结构的数据库:它是一种有序的反向链接列表。这意味着区块以插入顺序进行存储,并每个区块会链接到前一区块。这个结构能够保证快速获得一个区块链中最新的区块,并能够通过区块散列值高效地获取到该区块。

    在Golang中,可以使用一个数组和一个map来实现这种结构:数组用于保存有序的散列(在Go中数组是有序的),而map用于保存散列->区块对(map是无序的)。然而,对于我们的区块链原型来说,我们仅仅使用一个数组,因为现在我们不需要通过区块的散列来获得对应的区块。

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

    这样就完了么?No!

    为了向区块链中添加一个新区块,我们需要确保已经存在了一个区块,但是目前我们的区块链中并未存在任何区块。所以,在任何区块链中,都必须存在至少一个区块,区块链中的首个区块称为创世区块。下面,我们实现一个方法来创建创世区块:

    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
    ", block.PrevBlockHash)
            fmt.Printf("Data: %s
    ", block.Data)
            fmt.Printf("Hash: %x
    ", block.Hash)
            fmt.Println()
        }
    }

    输出结果如下:

    Prev. hash:
    Data: Genesis Block
    Hash: aff955a50dc6cd2abfe81b8849eab15f99ed1dc333d38487024223b5fe0f1168
    
    Prev. hash: aff955a50dc6cd2abfe81b8849eab15f99ed1dc333d38487024223b5fe0f1168
    Data: Send 1 BTC to Ivan
    Hash: d75ce22a840abb9b4e8fc3b60767c4ba3f46a0432d3ea15b71aef9fde6a314e1
    
    Prev. hash: d75ce22a840abb9b4e8fc3b60767c4ba3f46a0432d3ea15b71aef9fde6a314e1
    Data: Send 2 more BTC to Ivan
    Hash: 561237522bb7fcfbccbc6fe0e98bbbde7427ffe01c6fb223f7562288ca2295d1

    通过上面的结果,我们可以确定该区块链可以正常工作。

    0x03 结论

    本文中,我们创建了一个非常简单的区块链原型:它仅仅是一个区块数组,其中每个区块都有一个链接到前一区块,不过实际的区块链要比这复杂得多。在我们的区块链中,添加新的区块简单快速,但在实际的区块链中添加新区块则需要做一些工作:在获取添加区块的权限之前,区块添加者(或者说节点)必须执行一些繁重的计算(这种机制称为工作证明,Proof-of-Work,POW)。此外,区块链是一种分布式数据库,所以并没有一个唯一的决策者。因此,一个新的区块必须由网络中的其他参与者确认和批准(这种机制称为共识机制)。最后,目前我们的区块链中还没有交易!在后续的文章中我们将逐步讨论这些功能和特点。

    下一篇《【区块链Go语言实现】Part 2:工作量证明机制POW》中我们将介绍区块链中的工作量证明机制。

    英文链接:https://jeiwan.cc/posts/building-blockchain-in-go-part-1/

  • 相关阅读:
    常见常用的CSS
    Js删除数组函数
    使用CSS让多出来的字变为省略号
    CSS缩写的样式
    mac下安装nginx
    ubuntu下程序员常用命令大全
    vue.js实现瀑布流之vue-waterfall-easy
    vue.js常见的报错信息及其解决方法的记录
    laravel5.4生成验证码
    java算法之超级丑数
  • 原文地址:https://www.cnblogs.com/leejack/p/8259723.html
Copyright © 2020-2023  润新知