Introduction
区块链是21世纪最具革命性的技术之一,至今仍在发展,很多潜力尚未完全实现。从本质上讲,区块链只是一个分布式数据库。但是它的独特之处在于它不是私人数据库,而是公共数据库,也就是说,使用它的每个人都有完整或部分数据副本。只有在数据库的其他管理员同意的情况下才能添加新记录。此外,正是由于区块链才使加密货币和智能合约成为可能。
在本系列文章中,我们将构建一个基于简单区块链实现的简化加密货币。
Block
让我们从“区块链”的“区块”部分开始。在区块链中,区块是用来存储有价值信息的。实际上,交易信息是所有加密货币的价值所在。除此之外,区块包含一些技术信息,如版本,当前时间戳和前一个块的哈希值。在本文中,我们不打算实现像比特币规范中描述的那种区块,而是将使用它的简化版本,它只包含重要信息。这是它的样子:
type Block struct {
Timestamp int64
Data []byte
PrevBlockHash []byte
Hash []byte
}
Timestamp字段是当前时间戳(创建区块时),Data字段是区块中包含的实际有价值的信息,PrevBlockHash字段存储前一个区块的哈希值,Hash字段是当前区块的哈希值。在比特币规范中Timestamp, PrevBlockHash, 和 Hash是块头,它形成一个单独的数据结构,Data是另一个数据结构。我们为了简单起见将它们混合在了一起。
那么我们如何计算哈希?哈希计算方法是区块链的一个非常重要的特征,正是这个特征使区块链变得安全。问题在于计算哈希是一个计算上很困难的操作,即使在快速计算机上也需要一些时间(这就是人们购买功能强大的GPU来挖掘比特币的原因)。这是一种有意的架构设计,这使得添加新的区块变得困难,从而防止它们在添加后进行修改。我们将在以后的文章中讨论并实现这种机制。
目前,我们仅取了 Block
结构的部分字段(Timestamp
, Data
和 PrevBlockHash
),并将它们相互拼接起来,然后在拼接后的结果上计算一个 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
}
这就是区块!
Blockchain
现在让我们实现一个区块链。从本质上讲,区块链只是一个具有特定结构的数据库:它是一个有序的反向链接列表。这意味着区块以插入顺序存储,并且每个区块都链接到前一个区块。这种结构允许快速获取链中的最新区块并通过其散列(有效地)获得区块。
在 Golang 中,可以通过一个 array 和 map 来实现这个结构:array 存储有序的哈希(Golang 中 array 是有序的),map 存储 hash -> block 对(Golang 中, map 是无序的)。 但是在基本的原型阶段,我们只用到了 array,因为现在还不需要通过哈希来获取区块。
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)
}
结束!不过,就这样就完成了吗?
要添加新的区块,我们需要一个现有的区块,但是我们的区块链中没有区块!因此,在任何区块链中,必须至少有一个区块,这个区块,也就是链中的第一个区块,通常叫做创世区块(genesis block). 让我们实现一个方法来创建创世区块:
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: %xn", block.PrevBlockHash)
fmt.Printf("Data: %sn", block.Data)
fmt.Printf("Hash: %xn", block.Hash)
fmt.Println()
}
}
Output:
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
Conclusion
我们构建了一个非常简单的区块链原型:它只是一个区块数组,每个区块都与前一个区块连接。实际的区块链显然复杂得多。在我们的区块链中添加新区块很容易而且很快,但在实际区块链中添加新区块需要一些工作:在获得添加区块的权限之前必须执行一些繁重的计算(这种机制称为工作量证明)。此外,区块链是一个没有单一决策者的分布式数据库。因此,必须由网络的其他参与者确认和批准新的区块(该机制称为共识)。我们的区块链中还没有交
在以后的文章中,我们将介绍这些功能。
(未经同意,请勿转载)