go实现简易的区块链(1)

引言

本文及后续文章以通俗的方式讲解区块链技术,用go实现一个简单的区块链。

什么是区块链?

区块链是一种安全共享的去中心化的数据账本。 本质上,区块链只是一个分布式数据库而已。不过使他独一无二的事,区块链是一个公开的数据库,而不是一个私有的数据库,也就是说,每个使用它的人都有完整或部分的副本。 数据被细分为多个共享区块,并以加密哈希形式的唯一标识符链接在一起。 此外,也正是由于区块链,才使得加密货币和智能合约成为现实。

区块

首先我们先来了解什么叫做区块,区块就是真正存储有效信息的就是区块(block)。而在比特币中,真正存在价值的是transaction。实际上,交易信息是所有加密货币的价值所在。除此以外,区块还包含了一些技术实现的相关信息,比如版本,当前时间戳和前一个区块的哈希。

我们实现一个简易的区块链,里面只包含了关键信息,而不是像比特币那种完整的生态链。

// Block 区块结构体
type Block struct {
	Timestamp     int64  // 当前时间戳,也就是区块创建的时间
	Data          []byte // 前一个块的哈希,即父哈希
	PrevBlockHash []byte // 当前块的哈希
	Hash          []byte // 区块存储的实际有效信息,也就是交易
}

这里的Timestamp,PrevBlockHash,Hash,在比特币技术规范中属于区块头,区块头是一个单独的数据结构。下面是golang实现btcd的区块头定义:

// BlockHeader defines information about a block and is used in the bitcoin
// block (MsgBlock) and headers (MsgHeaders) messages.
type BlockHeader struct {
    // Version of the block.  This is not the same as the protocol version.
    Version int32

    // Hash of the previous block in the block chain.
    PrevBlock chainhash.Hash

    // Merkle tree reference to hash of all transactions for the block.
    MerkleRoot chainhash.Hash

    // Time the block was created.  This is, unfortunately, encoded as a
    // uint32 on the wire and therefore is limited to 2106.
    Timestamp time.Time

    // Difficulty target for the block.
    Bits uint32

    // Nonce used to generate the block.
    Nonce uint32
}

而我们简易版的Data,在比特币中对应的就是transaction,是另一个单独的数据结构。为了方便起见,把两个数据结构放在了一起。

在我们的简化版中,还有一个Hash字段,那么,要如何计算哈希呢?哈希计算是区块链中非常重要的部分,因为有了他才能保证区块的安全。为什么现在我们所说的挖矿非常困难了呢,那是因为计算一个哈希,是在计算上非常困难的一个操作。即使放在好一点的电脑上,也要耗费很多时间(这就是为什么人们会goumai一些GPU,FPGA,ASIC来挖比特币)。这是一个架构上有意为之的设计,它故意使得加入新的区块十分困难,继而保证区块一旦被加入以后,就很难再进行修改。在接下来的内容中,我们将会讨论和实现这个机制。

目前,我们仅取了 Block 结构的部分字段(Timestamp, DataPrevBlockHash),并将它们相互拼接起来,然后在拼接后的结果上计算一个 SHA-256,然后就得到了哈希.

Hash = SHA256(PrevBlockHash + Timestamp + Data)

下面是在golang中实现哈希和创建一个简易的区块。

// 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[:]
}

// NewBlock 创建简单的区块函数
func NewBlock(data string, prevBlockHash []byte) *Block {
	block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}}
	block.setHash()
	return block
}

区块链

有了区块,下一步就是实现区块链了。区块链其实就是把之前的区块按照插入的顺序进行存储,每一个块都与前一个块相连,其实就是一个特定的数据结构的数据库。这样的数据结构可以让我们快速获取链上的最新块,并且高效的通过哈希来检索一个块。

接下来我们来简单的实现一个区块链,结构非常简单,就是一个Block数组,组成的区块链。

type Blockchain struct {
	blocks []*Block
}

现在可以添加一个区块:

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

不过不仅仅是这样的。为了加入一个新的块,我们必须要有一个已有的块,一开始我们的链是空的,所以就像链表一样需要一个头块。毕竟至少需要一个块才能组成区块链,这个块也就是链中的第一个块,通常叫做创世块。下面实现一个方法来创建创世块。

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

现在实现一个函数来创建有创世块的区块链:

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

测试区块函数

func main() {
	bc := NewBlockchain()
	bc.AddBlock("Send 1 ETH to Alex")
	bc.AddBlock("Send 2 ETH to Alex")
	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()
	}
}

// console result 
Prev. hash: 
Data: Genesis Block
Hash: 1d4e96771da1e76bd946296039709860259a12e8031411f3c2644142dafc309f

Prev. hash: 1d4e96771da1e76bd946296039709860259a12e8031411f3c2644142dafc309f
Data: Send 1 BTC to Ivan
Hash: e1f60a364931dffa531aec919b4ca29513405d4d1f441b5c8e85f49b8c6d0aaf

Prev. hash: e1f60a364931dffa531aec919b4ca29513405d4d1f441b5c8e85f49b8c6d0aaf
Data: Send 2 more BTC to Ivan
Hash: 0c3062803bc312bd36dc39503ed02db040724e06bb4e257fd988d2432d72c405

可以看出我们已经完成了基础的区块链原型,建立了一个链,增加了区块。但是在真实的区块链中,加入一个新的块需要很多工作,需要经过十分繁重的计算,从而获得添加一个新块的权利。下一章我们将讲述工作量证明,为什么一个区块链添加区块需要繁琐的计算?

  • 3
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值