首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >教你打造最简比特币—原型币之基本原型

教你打造最简比特币—原型币之基本原型

作者头像
linxinzhe
发布2018-04-10 15:11:54
7670
发布2018-04-10 15:11:54
举报
文章被收录于专栏:林欣哲林欣哲
开发环境:Go语言

本教程是学习Jeiwan的博客后的学习笔记,代码实现也参考它的为主,精简了叙述并在适当位置添加了一些必备的小知识和适当的代码注释,如介绍哈希。

本教程是为了逐步教你设计一款简化的区块链原型币。通过我们不断添加功能,完成一个可交易的原型币。

本节我们设计一个单机版的仅支持保存信息的原型币。

  1. 单机版,仅支持保存信息

区块:

区块链中的最基本单位,一个区块包含区块头和区块体。

区块头

包含这个区块的相关信息。 如:

  1. 区块产生时间
  2. 本区块哈希值(保证本区块不篡改)
  3. 上一个区块哈希值(通过该哈希找到上一个区块,能串起来区块链)

区块体

真正包含数据的是区块体。

对于原型币,为了简便设计,将区块体和区块头合二为一。

请看该区块的代码

type Block struct {
    Timestamp     int64   //区块产生时间时的当前系统时间,从1970开始的毫秒数
    PrevHash []byte   //上一个区块哈希值,指向上一个区块
    Hash          []byte   //本区块哈希值,根据本区块的字节经过哈希算法算的
    Data          []byte  //区块体的信息,自己填写希望保存的信息
}

注意这里都使用byte数组,区块的底层实现其实都是字节byte。

哈希

在我们的区块中,唯一不明确的是,这个区块的哈希要如何计算呢? 先普及一下哈希,哈希是一个单向计算,将原信息通过哈希函数计算出哈希值,即hash=HASH(bytes);得到一个固定字节大小的字符串hash,可以用来代表原来的bytes数据,我们常见的MD5就是一种哈希算法。 哈希具有三大特性。

  1. 无碰撞(Collision-free),可认为hash和bytes一一对应,若bytes被篡改,则hash就变了。
  2. 隐藏信息(Hiding),通过hash字符串不能反推原信息。
  3. 谜题友好(puzzle-friendly),意思是若要从hash字符串反推原信息,只能遍历尝试每一个原信息。 我们在区块链中就是应用了无碰撞的特性,保证了区块不被串改信息。

本节的原型币的哈希函数采用SHA256,公式如下:

Hash = SHA256(PrevBlockHash + Timestamp + Data)

实现代码如下:

func (b *Block) SetHash() {
    timestamp := []byte(strconv.FormatInt(b.Timestamp, 10))  
    //由于Timestamp是int64,需要转化为bytes,再和其他信息拼接,这里暂时没有要求用什么方式转,你可以直接按int64的字符串意义转成byte,也可以按int64的底层byte表示转成byte,这里取前者。只要还原成int64时按照同一个方式即可。
    headers := bytes.Join([][]byte{b.PrevHash, b.Data, timestamp}, []byte{})
    hash := sha256.Sum256(headers)

    b.Hash = hash[:]
}

了解了哈希的计算,我们就知道如何制造一个区块了。

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

区块链

区块链的本质就是串起来的区块,是一种特殊的数据库。通过每一个区块中的上一个区块的哈希值,将区块一个一个的串起来。 为了方便起见,我们单机版的币就用数组,按顺序存储。 代码如下:

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

但是,特殊的是第一个创世区块,需要我们手动制造一下,中本聪也是这么做的。

func NewGenesisBlock() *Block {
    return NewBlock("Genesis Block", []byte{})
}

据此,我们可以创建创世区块链,

func NewBlockchain() *Blockchain {
    return &Blockchain{[]*Block{NewGenesisBlock()}}
}

最后,让我们来运行一下我们的原型币。

func main() {
   bc := datastruct.GenesisBlockchain()

   bc.AddBlock("Send 1 BTC to Lin")
   bc.AddBlock("Send 2 BTC to Lin")

   for _, block := range bc.Blocks {
      fmt.Printf("Prev. hash: %x\n", block.PrevHash)
      fmt.Printf("Data: %s\n", block.Data)
      fmt.Printf("Hash: %x\n", block.Hash)
      fmt.Println()
   }
}

所得结果,你的运行结果和我的都会不一样,因为你的区块产生时间是不一样的:

Prev. hash: 
Data: Genesis Block
Hash: 0a6a60a1def38b806d5e42410468d63d626171f75717a6a99ca7a88c98d69a70

Prev. hash: 0a6a60a1def38b806d5e42410468d63d626171f75717a6a99ca7a88c98d69a70
Data: Send 1 BTC to Lin
Hash: a2daba78c1c635c7b5570cac0eaa699455b940acaefc904f394e199dc2e22dee

Prev. hash: a2daba78c1c635c7b5570cac0eaa699455b940acaefc904f394e199dc2e22dee
Data: Send 2 BTC to Lin
Hash: cf17fc4e346328c957b97ef6e92cf91c7b3cd9a798363afbd84fc7bae9657273

参考:

  1. Building Blockchain in Go. Part 1: Basic Prototype, jiewan

源码

https://github.com/linxinzhe/go-simple-coin/tree/1prototypecoin,点击“阅读原文”即可查看

下一节:

  1. 区块链原型币—工作量证明

全系列:

  1. 区块链原形币--工作量证明
  2. 区块链原型币—工作量证明
  3. 区块链原型币—持久化
  4. 未完待续
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2018-03-09,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 林欣哲 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 区块:
    • 区块头
      • 区块体
      • 哈希
      • 区块链
        • 参考:
          • 源码
            • 下一节:
              • 全系列:
              相关产品与服务
              区块链
              云链聚未来,协同无边界。腾讯云区块链作为中国领先的区块链服务平台和技术提供商,致力于构建技术、数据、价值、产业互联互通的区块链基础设施,引领区块链底层技术及行业应用创新,助力传统产业转型升级,推动实体经济与数字经济深度融合。
              领券
              问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档