zoukankan      html  css  js  c++  java
  • 【区块链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/

  • 相关阅读:
    解决 Mac launchpad 启动台 Gitter 图标无法删除的问题
    React 与 React-Native 使用同一个 meteor 后台
    解决 React-Native mac 运行报错 error Failed to build iOS project. We ran "xcodebuild" command but it exited with error code 65. To debug build logs further, consider building your app with Xcode.app, by ope
    一行命令更新所有 npm 依赖包
    swift学习笔记
    IOS语言总结
    focusSNS学习笔记
    别小看锤子,老罗真的很认真
    windowsphone开发页面跳转到另一个dll中的页面
    【令人振奋】【转】微软潘正磊谈DevOps、Visual Studio 2013新功能、.NET未来
  • 原文地址:https://www.cnblogs.com/leejack/p/8259723.html
Copyright © 2011-2022 走看看