教你打造最简比特币—原型币之基本原型
开发环境:Go语言
本教程是学习Jeiwan的博客后的学习笔记,代码实现也参考它的为主,精简了叙述并在适当位置添加了一些必备的小知识和适当的代码注释,如介绍哈希。
本教程是为了逐步教你设计一款简化的区块链原型币。通过我们不断添加功能,完成一个可交易的原型币。
本节我们设计一个单机版的仅支持保存信息的原型币。
- 单机版,仅支持保存信息
区块:
区块链中的最基本单位,一个区块包含区块头和区块体。
区块头
包含这个区块的相关信息。 如:
- 区块产生时间
- 本区块哈希值(保证本区块不篡改)
- 上一个区块哈希值(通过该哈希找到上一个区块,能串起来区块链)
区块体
真正包含数据的是区块体。
对于原型币,为了简便设计,将区块体和区块头合二为一。
请看该区块的代码
type Block struct {
Timestamp int64 //区块产生时间时的当前系统时间,从1970开始的毫秒数
PrevHash []byte //上一个区块哈希值,指向上一个区块
Hash []byte //本区块哈希值,根据本区块的字节经过哈希算法算的
Data []byte //区块体的信息,自己填写希望保存的信息
}
注意这里都使用byte数组,区块的底层实现其实都是字节byte。
哈希
在我们的区块中,唯一不明确的是,这个区块的哈希要如何计算呢? 先普及一下哈希,哈希是一个单向计算,将原信息通过哈希函数计算出哈希值,即hash=HASH(bytes);得到一个固定字节大小的字符串hash,可以用来代表原来的bytes数据,我们常见的MD5就是一种哈希算法。 哈希具有三大特性。
- 无碰撞(Collision-free),可认为hash和bytes一一对应,若bytes被篡改,则hash就变了。
- 隐藏信息(Hiding),通过hash字符串不能反推原信息。
- 谜题友好(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: %xn", block.PrevHash)
fmt.Printf("Data: %sn", block.Data)
fmt.Printf("Hash: %xn", 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
参考:
- Building Blockchain in Go. Part 1: Basic Prototype, jiewan
源码
https://github.com/linxinzhe/go-simple-coin/tree/1prototypecoin,点击“阅读原文”即可查看
下一节:
- 区块链原型币—工作量证明
全系列:
- 区块链原形币--工作量证明
- 区块链原型币—工作量证明
- 区块链原型币—持久化
- 未完待续
- Druid数据库连接池 实现数据库账号密码加密
- 【maven学习】划分模块
- 缓存策略优化
- Quartz框架应用(1)
- 6个编写优质干净代码的技巧
- 【Jfinal源码】第一章 com.jfinal.core.JFinalFilter(1)
- 【ehcache】 timeToLiveSeconds 和 timeToLiveSeconds 的区别
- 升级 CentOS7 、Redis 3.2.x 的问题
- AngularJS 中使用Swiper制作滚动图不能滑动
- JAVA服务端配置允许跨域请求
- CentOS mysql配置主从复制
- Quartz依赖数据库表
- Spring Security Oauth2.0 实现短信验证码登录
- 【Spring Cloud】Redis缓存接入监控、运维平台CacheCloud
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 解决Laravel blade模板转义html标签的问题
- laravel 配置路由 api和web定义的路由的区别详解
- Flutter 实现网易云音乐字幕的代码
- Yii框架通过请求组件处理get,post请求的方法分析
- PHP实现单文件、多个单文件、多文件上传函数的封装示例
- Android自定义控件单位尺寸实现代码
- Android中socket通信的简单实现
- Thinkphp5框架使用validate实现验证功能的方法
- Android通过Java sdk的方式接入OpenCv的方法
- php+js实现的无刷新下载文件功能示例
- Android如何获取视频首帧图片
- PHP单文件上传原理及上传函数的封装操作示例
- php中错误处理操作实例分析
- Android 百度地图定位实现仿钉钉签到打卡功能的完整代码
- Thinkphp5框架实现图片、音频和视频文件的上传功能详解