区块链产生
- 密码朋克:通过匿名性来保护隐私安全
- 不可篡改文件记录:加时间戳到文件内容中,证实数据真实存在
- 数字现金
发展历史
- 2009年,比特币诞生1.0
- 2014年,以太坊诞生2.0
- 2018年中期,EOS主网上线3.0
- Fabric
区块链场景概念
- 数字货币:可以与现金流进行等价交换,拥有现有现金流的属性和功能
- ICO:众筹
- 电子钱包:在电商购物中的支付工具
什么是区块链
- 区块链是一种集成了点对点传输协议,现代密码学,共识算法,分布数据存储的新型应用模型
区块链特点
- 可追溯
- 不可篡改
- 去中心化
- 完整备份
- 历史记录
- 交易广播
区块链加密货币特点
- 独立性:所有货币都是独立存在的
- 唯一性:地址、交易都具有不重复的唯一性
- 匿名性:账户信息和个人信息没有关联,整个交易过程全程加密
- 不可伪造
区块链核心技术
- 点对点传输协议:在网络中的数据流通方式
- 现代密码学:在区块链中的应用:公私钥签名,哈希算法
- 共识算法:数据一致性
- 分布式数据存储:实现去中心化的重要技术依据
区块链核心概念
区块链
- 本质:一个分布式账本,通过共识算法来决定谁能抢到当前的记账权。区块链以区块为单位,以区块产生的时间顺序去进行连接。
区块
- 概念:区块链的基本组成单位
- 区块头
- 时间戳
- 当前区块哈希
- 父区块哈希
- 随机数
- Merkle树
- 区块号码
- 区块体:交易数据
分布式数据库:区块链中的区块数据都会存储在每一个节点中,所有的节点组成一个分布式数据库。
节点
- 可以理解称为一个运行区块软件的计算机
- 分类
- 全节点:保存了完整的区块链的副本,安全性极高,效率不高
- 轻节点:不保存所有的区块,需要依赖全节点进行验证,效率更高,安全不如全节点
- 挖矿节点:带有挖矿功能的全节点,专门处理交易验证
挖矿
- 对交易进行验证处理(记账),区块就是通过挖矿产生的
- 穷举随机数算法,生成哈希,与目标哈希进行比较,成功则说明挖矿成功
分叉
升级分叉
矿工遵从不同的机制(规则)导致分叉
硬分叉:如果区块链共识规则改变之后,不允许前向兼容,旧节点没有办法认可新节点产生的区块
软分叉:如果区块链共识规则改变之后,允许前向兼容,旧节点可以兼容新节点产生的区块
挖矿分叉
现象:两个或者多个矿工,同时完成了工作量证明,就会产生两个新的区块,形成分叉
解决方案:不同矿工跟随了不同的区块,但是不同链算力会有区别,矿工的数量一样,链的增长速度就不会相同,最终会出现一条链更长,这条就会变成主链。
交易
- 概念:一笔资产在参与者之间的转移
- 内容
- 金额
- 发送者
- 接受者
- 交易ID(HASH)
双花
概念:复用数字货币的数字特性,可以完成两次或者多次支付
传统的虚拟货币之所以可以避免双花是因为有可依赖的第三方机构提供保证
区块链中需要达成只通过分布式节点之间的相互校验与共识来避免双花,同时完成价值转移
UTXO(unspent transaction output)交易模式
- 是比特币独有的交易模式,比特币交易过程中的基本单位,主要就是为了避免双花
哈希
- 将任意的原始数据(交易记录)通过指定哈希函数,编码为特定长度的字符串
- 在区块链中的使用:生成地址,交易验证
- 特点
- 不可逆
- 随机性
- 时间正相关:输入的源数据越长,哈希的处理时间就越长
加密算法
- 对称加密:加密与解密都是用相同的密钥
- 非对称加密
- 采用公钥和私钥进行加密
- 无法用公钥反推私钥
数字签名
Merkle树
- Merkle树可以是二叉树,也可以是多叉树,它具有树的所有特点
- 在区块链中的作用:快速校验、归纳交易完整性
- 在区块链中,Merkle树可以极大的提高查询效率,区块头只需要保存一个Merkle根的hash
- Merkle支持SPV
P2P
- 通过对等网络来分配工作任务的分布式应用架构
- 由于在P2P中,所有网络节点的地位是对等的,不存在任何一个中心化节点,也不存在所谓的层级结构,所以每个节点都需要承担验证区块数据等功能
区块链的分类
- 公有链:真正意义上的去中心化分布式区块链,任何一个节点都可以随时加入/退出网络中
- 私有链:部分中心化的区块链,具有一定的分布式特点,但是有一个中心节点,可以指定参与者
- 联盟链:部分去中心化的区块链,拥有权限控制的功能
- 代表:Fabric
区块链架构特点
- 去中心化:基于分布式系统,整个网络总没有中心机构存在
- 可靠数据库:分布式存储,参与系统的节点越多,数据库的安全性就越高
- 开源可编程:区块链提供了灵活的脚本系统甚至于完善的开发平台,支持用户创建更加高级的应用
- 集体维护:区块链中的数据由整个系统中所有具有记账功能的节点进行维护
- 安全可信:通过现代密码学实现
- 准匿名性:采用与身份信息无关的hash作为身份地址与交易ID
应用分析
比特币
架构
前端
钱包:保存用户私钥,管理用户余额,提供比特币交易
钱包分类
决定性钱包:所有的私钥都由一个私钥种子通过单向哈希算法生成
普通决定性钱包:由私钥种子一次性生成所有私钥
层级决定性钱包:由私钥种子生成父私钥,父私钥生成种子私钥
非决定性钱包:直接保存私钥,私钥直接放在DB
展示方式的分类
桌面钱包
1.厚钱包:下载整条区块链,可以完整交易,安全性高,验证成本高
2.薄钱包:不会下载整条区块链,采用部分存储+节点请求验证的方式
3.离线钱包:USB设备,纸钱包,可以有效防范网络攻击
HTTP/JSON RPC API
1.比特币提供的接口,可以使外部通过该接口访问或者控制比特币
命令行接口
1.通过命令行的方式实现类似钱包的功能
浏览器
1.访问区块链的区块数据等信息
节点后台:负责参与比特币网络的通信,区块链的维护,验证,交易
比特币地址
1.基本概念:由哈希生成
2.生成过程:
1.随机数生成私钥
2.采用Secp256k1椭圆加密算法生成公钥
3.生成地址
1.以公钥作为输入,进行SHA256,再进行RIPEMD160,最后通过base58编码生成比特币地址
比特币区块校验:确保确实完成了工作量证明
1.校验内容:格式、难度、时间戳、大小、交易
比特币交易
1.交易结构
1.输入(input)
2.输出(output)
3.交易ID(HASH)
2.说明
1.每一笔交易的输入来自于前面交易的输出
3.UTXO数据库:专门用来存储当前比特币中未被花费的输出
以太坊
- 一个用于开发去中心化DAPP的分布式平台
- 智能合约:一个拥有自我校验与自我执行的协议
- 以太坊提供了非常方便的应用开发平台,对底层做了完善的封装,让开发者只需要关注与上层应用
- 共识:POW(ETHASH)
EOS
一个用于开发去中心化DAPP的分布式平台
TPS有了极大的提高,能够达到百万级的TPS处理量
以太坊本身是一条公链,其中每个DAPP会消耗整条链上的资源
EOS本身不再是一条单纯的公链,它是一个区块链的基础架构,开发者可以自由再EOS上创建公链,链与
链之间不会影响彼此的资源使用,因此,不会出现单个应用占用的资源太多使得整个网络拥堵
共识算法:DPOS
EOS上的智能合约调用不需要手续费
Fabric(联盟链)
- 联盟链代表,与公链最大的区别在于不发行虚拟货币。
- 目标:实现一个通过权限管理区块链的底层基础框架。
比特币交易原理
传统的web交易
账户
余额
参与者
货币基本概念
- 比特币系统中的交易没有余额的概念,它使用的是UTXO交易模型,在传统交易过程中所说的交易余额实际上指的是一个比特币钱包地址的UTXO集合
交易组成
- 在比特币中,交易主要由输入,输出,ID,交易时间组成
UTXO交易模型
- 比特币专有的交易模型
- 在比特币中,交易实际上就是不但查找指定钱包地址的UTXO集合,然后进行修改的过程
- UTXO是比特币交易中最基本的单元,是不可拆分的
交易过程
coinbase:挖矿奖励的比特币,没有发送者,由系统提供,所以不包含input
普通转账:正常的转账交易,有发送者参与,所以包含input
密码学回顾
在区块链中的用途
- 地址生成
- 交易签名
base64编码
- 概念:用一种由64个字符来表示二进制数据的方法
- 作用:通常用于在HTTP中传递比较长的信息的处理方法
- 编码原理
- 将给定的字符串进行整合,由3个8位转换为4个6位,在6位前面补0,对于要编码的数据(由字符串转换为二进制数据)如果不是3的倍数,最后余数只会是1或者2,Base64在末尾用\x00补足,而在编码展示的末尾,添加1个或者两个“=”,等号的个数代表补足的字节数,在解码的时候,自动将等号去掉。
1
2
3
4
5
6
7
8
9
10
11
12package main
import (
"encoding/base64"
"fmt"
)
func main() {
s := "elssm"
encode := base64.StdEncoding.EncodeToString([]byte(s))
fmt.Println(encode)
}base58编码
为什么需要base58编码
- 在base64的基础上去掉了6个字符(0,O,大写I,小写l,+,/)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67package main
import (
"bytes"
"fmt"
"math/big"
)
var b58Alphabet = []byte(""+"123456789"+"abcdefghijkmnopqrstuvwxyz"+"ABCDEFGHJKLMNPQRSTUVWXYZ")
//编码函数
func Base58Encode(input []byte) []byte {
var result []byte
//byte字节数组转换为big.int
x := big.NewInt(0).SetBytes(input)
//求余的基本长度
base := big.NewInt(int64(len(b58Alphabet)))
//求余数和商
//判断条件,除掉的最终结果是否为0
zero := big.NewInt(0)
//设置余数,代表base58基数表的索引位置
mod := &big.Int{}
for x.Cmp(zero) != 0 {
x.DivMod(x,base,mod)
result = append(result,b58Alphabet[mod.Int64()])
}
Reverse(result)
result = append([]byte{b58Alphabet[0]},result...)
return result
}
func Reverse(data []byte) {
for i,j :=0,len(data)-1;i<j;i,j = i+1,j-1 {
data[i],data[j] = data[j],data[i]
}
}
//解码函数
func Base58Decode(input []byte) []byte {
fmt.Println(input)
result := big.NewInt(0)
zeroBytes := 1
//去掉前缀
data := input[zeroBytes:]
fmt.Println(data)
for _,b := range data {
//查找input中指定数字/字符在基数表中出现的索引(mod)
charIndex := bytes.IndexByte(b58Alphabet,b)
//余数*58
result.Mul(result,big.NewInt(58))
//乘积结果+mod(索引)
result.Add(result,big.NewInt(int64(charIndex)))
}
fmt.Println(result)
//转换为byte字节数组
decoded := result.Bytes()
fmt.Println(decoded)
return decoded
}
func main() {
result := Base58Encode([]byte("elssm"))
fmt.Printf("result : %s\n",result)
decodeResult := Base58Decode([]byte("1crFth1D"))
fmt.Printf("decode Result : %s\n",decodeResult)
}
Go语言区块链简单实现
创建一个Block.go
文件,定义一个最基本的区块结构,这个结构包含了区块最基本的一些东西。
1 | type Block struct { |
接着实现一个建立区块的函数,这个函数就是对区块结构进行一些初始化。对于区块的hash我们单独实现一个方法。
1 | func NewBlock(height int64,prevBlockHash []byte,data []byte) *Block { |
区块哈希实现,因为在区块结构中区块高度和时间戳都是int
型,因此我们要将int
转为[]byte
,转换可以通过go语言中的encoding/binary包实现。
1 | func IntToHex(data int64) []byte { |
1 | func (b *Block) SetHash() { |
实现一个生成创世区块的函数,在函数内部调用生成区块的函数实现。
1 | func CreateGenesisBlock(data []byte) *Block { |
创建一个BlockChain.go
文件,首先定义区块链的基本结构,因为区块链他是一种链式的,所有我们用切片这种数据结构来实现。
1 | type BlockChain struct { |
初始化区块链
1 | func CreateBlockChainWithGenesisBlock() *BlockChain { |
添加区块到区块链中
1 | func (bc *BlockChain) AddBlock(height int64,prevBlockHash []byte,data []byte) { |
创建一个main.go
文件,进行一个简单的测试。首先创建一个创世区块,并添加两条新链,最后通过hash来判断是否上链成功。结果如下图,除了第一个创世区块没有prevhash值以外,后面的区块的prevhash值都是前一个区块的currenthash值。
1 | func main() { |