基于Go语言构建区块链:part1

Golang语言和区块链理论学习完毕后,快速入门方法无疑是项目实战。本文将参考https://jeiwan.cc/tags/blockchain/教程,学习如何基于Go语言构建区块链。

编程工具使用GoLand,前文已介绍软件安装经验。软件安装完成后,还需要设置工作路径“GOPATH”。在电脑上新建一个空白目录,然后点击点击Goland菜单按钮:“File”->“Settings”->"GO"->"GOPATH",添加自己的工作目录即可,还可以根据自己的喜好,设置不同的代码呈现颜色、字体、字号等。

2、简化的block实现

block结构如下所示:

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

Timestamp表示Block被创建的时间戳,Data表示block中实际的价值信息,PrevBlockHash表示上一个Block的hash值,Hash表示该block本身的Hash值。
现在,我们首先SetHash方法,将Block中的字段信息组合后,生成该block的SHA-256 Hash值:
// SetHash calculates and sets block hash
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[:]
}
接下来,实现NewBlock方法用于创建一个Block: 
// NewBlock creates and returns Block
func NewBlock(data string, prevBlockHash []byte) *Block {
   block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}}
   block.SetHash()
   return block
}
OK,block实现完成啦! 

3、简化的blockchain实现

本质上,blockchain仅仅是具备某种特殊结构的数据库:有序,反向链接链表。这意味着,block按照插入的顺序存放,同时每个block都保存指向上一个block的链接。这种结构保证可以快速获取最新插入的block同时获取它的hash值。

Go语言中,可以通过slice和map来实现该结构。slice(有序)用于保存有序的hash值,map(无序)用于保存hash->block对。对于我们的blockchain原型,目前不需要根据hash值来获取block,因此仅仅使用slice即可满足需求。blockchain结构如下:

type Blockchain struct {
   blocks []*Block
}

接下来,我们实现AddBlock方法,用于将block添加到blockchain中:
// AddBlock saves provided data as a block in the blockchain
func (bc *Blockchain) AddBlock(data string) {
   prevBlock := bc.blocks[len(bc.blocks)-1]
   newBlock := NewBlock(data, prevBlock.Hash)
   bc.blocks = append(bc.blocks, newBlock)
}
新增一个block的前提是另一个block已经存在,但是一开始blockchain中并没有任何block。因此,在任何blockchain中都必须有一个特殊的block存在,称之为GenesisBlock。下面实现NewGenesisBlock方法用于创建GenesisBlock:
func NewGenesisBlock() *Block {
   return NewBlock("Genesis Block", []byte{})
}
接下来,实现NewBlockchain方法,该方法会创建一个包含Genesis Block的blockchain:
func NewBlockchain() *Blockchain {
   return &Blockchain{[]*Block{NewGenesisBlock()}}
}

4、测试Blockchain是否可以正常工作

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

若直接选择RUN 'go build main.go',会出现如下报错:

# command-line-arguments
.\main.go:8:8: undefined: NewBlockchain

Compilation finished with exit code 2

这是因为“block.go”和“blockchain.go”没有同时编译。选择整个项目“Blockchain”,选择Run 'go build Blockchain'即可成功,结果如下:

Prev. hash: 
Data: Genesis Block
Hash: aa6085b7b44bcc1d878c6a28a3d98a665f8e1522da8093defee6a61e06aeac67

Prev. hash: aa6085b7b44bcc1d878c6a28a3d98a665f8e1522da8093defee6a61e06aeac67
Data: Send 1 BTC to Ivan
Hash: 835c7514ce8b54b83add05c41977bf5f50dfec7791e12e6912feb1d9c286f7b4

Prev. hash: 835c7514ce8b54b83add05c41977bf5f50dfec7791e12e6912feb1d9c286f7b4
Data: Send 2 more BTC to Ivan
Hash: cfbd89e2384d7caabc7ff58ed0a27a5172a18fc381b0b87d990e989d55b98ecd
Process finished with exit code 0