从比特币看区块链与Golang实战
原创者:文思
一、区块链脉络
1、哈希
哈希以上特点决定了其对区块链支持的天然性。
2、加密
2.1对称加密:
明文—加密方法-->密文
密文—解密方法-->明文
以上密码本一旦暴露,就毫无安全加密可言。
2.2非对称加密
非对称加密简介:
MD5的全称是Message-Digest Algorithm 5(信息-摘要算法),在90年代初由Ronald L. Rivest开发出来,经MD2、MD3和MD4发展而来。
1MD5
简介
MD5是一种散列(Hash)算法,散列算法的用途不是对明文加密,而是通过对信息摘要的比对,防止对原文的篡改。通常对散列算法而言,所谓的“破解”,就是找碰撞。MD5是把一个任意长度的字节串加密成一个固定长度的大整数(通常是16位或32位),加密的过程中要筛选过滤掉一些原文的数据信息,因此想通过对加密的结果进行逆运算来得出原文是不可能的。
2MD5
的应用
关于MD5的应用,例如你在一个论坛注册一个账号密码设为“abc123”。此密码经过MD5运算后,变成“297F1E255D930496EA01037339CD978D”,当你点“提交”按钮提交时,服务器的数据库中不记录你的真正密码,而是记录那个MD5的运算结果。然后,你在此论坛登录,登录时你用的密码是“abc123”,电脑再次进行MD5运算,把“abc123”转为“297F1E255D930496EA01037339CD978D”,然后传送到服务器那边。这时服务器就把你传过来的MD5运算结果与数据库中你注册时的MD5运算结果比较,如果相同则登录成功。也就是说,服务器只是把MD5运算结果作比较。
3MD5的破解
关于MD5的破解。假设攻击者已经得到了“297F1E255D930496EA01037339CD978D”这一串数字,那么你怎么能得出我的密码是“abc123”呢?因为MD5算法是不可逆的,你只能用暴力法(穷举法)来破解,就是列举所有可能的字母和数字的排列组合,然后一一进行MD5运算来验证运算结果是否为“297F1E255D930496EA01037339CD978D”,“abc123”这个密码是6位英文字符和数字混合,这样的排列组合的数量是个天文数字,如一一列举那么很耗时,也许在有生之年看不到。所以只有使用黑客字典才是一种有效可行的方法,黑客字典可以根据一些规则自动生成。例如“abc123”这个密码,就是一种常见的组合,规则是:拼音+拼音+数字,拼音总共大约400个,数字以2位数100个来算,这种规则总共约400*400*100=16,000,000种可能,使用优化的算法,估计用1秒就能破解吧。就算考虑到字母开头大写或全部大写的习惯,也只会花大约10几秒时间。如果是破解你熟悉的某个人的密码,那么你可以根据你对他的了解来缩小词典的范围,以便更快速的破解。这种破解方法在很大程度上依赖于你的运气。
4MD5碰撞
根据密码学的定义,如果内容不同的明文,通过散列算法得出的结果(密码学称为信息摘要)相同,就称为发生了“碰撞”。因为MD5值可以由任意长度的字符计算出来,所以可以把一篇文章或者一个软件的所有字节进行MD5运算得出一个数值,如果这篇文章或软件的数据改动了,那么再计算出的MD5值也会产生变化,这种方法常常用作数字签名校验。因为明文的长度可以大于MD5值的长度,所以可能会有多个明文具有相同的MD5值,如果你找到了两个相同MD5值的明文,那么你就是找到了MD5的“碰撞”。散列算法的碰撞分为两种,强无碰撞和弱无碰撞。还以前面那个密码为例:假如你已这个MD5值,然后找出了一个单词碰巧也能计算出和“abc123”相同的MD5值,那么你就找到了MD5的“弱无碰撞”,其实这就意味着你已经破解了MD5。如果不给你指定的MD5值,让你随便去找任意两个相同MD5值的明文,即找“强无碰撞”,显然要相对容易些了,但对于好的散列算法来说,做到这一点也很不容易了。强无碰撞已经被中国的王小云搞定了,她提出的算法可以在短时间内找到碰撞,现在的电脑大约一两个小时就可以找到一对碰撞。因为只能随意找所以即使找到强无碰撞在实际破解中没有什么真正的用途,所以现在MD5仍然是很安全的
非对称加密:公钥加密、私钥解密
以上非对称加密可以解决加密问题,但无法解决传数据来源的验证问题,因为公钥公开发布,无法解决网络截取数据被劫持替换的问题。(截取网络数据,并伪造数据用公钥加密后传输)
现在就需要数字签名:
3、UTXO
Unspent Transaction Output,未花费的交易输出
比特币的概念没有余额的概念,只有输入与输出,在管理钱包时,记录的是交易记录,而不是余额(为了节省交易空间)
例如:
钱包内的UTXO已接收3笔比特币,1BTC、2BTC、5BTC,有了3笔未交易输出
然后消费4比特币,系统将5BTC的消费转账交易给收款方,系统找零1BTC,收款方收到4BTC
1是交易记录哈希值,2付款钱包地址,3花费,4收款钱包地址
仅保存交易记录,不保存余额,好处:
性能,比特币运行至今全部交易记录只有几百G
避免双重支付,任何一个钱包中的所有交易都写入了整个区块链网络,无法篡改。
4、收、付款方生成交易数据
付款方:
付款方发送上一次的交易数据与收款方公钥一起哈希作为付款方数据一起发送给收款方。
收款方:
5、交易打包成区块
如果有8笔交易,则两两做哈希:
最终的哈希值可以反映出所有的交易信息最终哈希值就叫默克尔树根。当你修改其中一笔交易时,整个块里所有的数据都需要去改,而且修改的操作必须全网中超过51%的节点认可才行,所以从很多方面保证了区块链中的数据不容篡改。
6、比特币网络节点之间的信任
很多笔交易打包成区块后与一个神秘数字放在一起做哈希,得到一个小于固定值的哈希值。
所谓的挖矿就是为了找到这个神秘数字。比特别的总量是2100万个。
7、为什么工作量证明可以信赖
矿工可以理解为具有运算能力的服务器,每台服务器进行神秘数字运算,算出神秘数字的服务器将信息发给周围服务器进行验证,验证通过后进行全网广播,全网广播后写入区款链。将来比特币挖完后,还有转账手续费可以赚取。
8、区块链结构
如上述所说,每两笔相互哈希最终形成默克尔树根,所有的信息都在整个网络。当你修改其中一笔交易时,整个块里所有的数据都需要去改,而且修改的操作必须全网中超过51%的节点认可才行,所以从很多方面保证了区块链中的数据不容篡改。
9、区块链展望
目前发展阶段:
类比的话相当于互联网的BBS时期。
关于区块链目前能够达成的共识:
1.去中心化不是万能的,但是在特定领域确。
2.实能够很好的解决中心化解决不了的问题。
3.所以区块链肯定不是一时热的昙花一现。
4.区块链必将成为未来互联网的基础设施。
区块链的应用领域:
金融、医疗、政府、内容分发版权确认
10、通证经济
https://www.sohu.com/a/226513640_100134484
1.Token不是代币,是通证。token可以理解为能流通的权益证明
2.通证需要具备三要素:权益、加密、流通
3.通证可代表一切权益证明
4.通证与区块链是两件不同的事情,但是彼此之间是最佳拍档
5.通证经济是下一代互联网的数字经济
6.通证、区块链、大数据结合是趋势
但是我们也要认识到通证经济并不能解决一切问题。通证只能保证你的权益不可更改,币不会滥发,参与者能够达成一定程度的共识,但是对很多问题是不能保证的。比如说募集资金使用的监管,项目进度的监管,团队会不会跑路,技术会不会过时等等问题还依然存在。也许是通证经济还只是个雏形,还需要不断的完善发展。
二、Golang实战
(一)认识Go语言
1、Go简介及应用场景
Go语言,我们可以简单的写成Golang,是Google公司创造和主推的。
Go可以做什么?区块链开发、Go服务端开发、Golang分布式/云计算
Go的优势:处理大并发、处理数据、计算能力
Go目前的应用领域:区块链应用、后端服务应用支撑(排序、推荐、搜索、按条件分流等)、云计算/云服务(盛大云计算:CND调度|分发系统|监控系统|短域名服务、京东消息推送云服务及京东分布式文件系统后端全部用go实现)
2、Golang的学习方法
高效;先建立整体框架,然后细节;先know how再know why;多琢磨别人怎么做;总结学习经验;后期注重技术基础及细节原理的认识。
Go语言的吉祥物,金花鼠
3、Go语言小故事
为何要创造Go语言?
发展历程:
2007年原型
2009年开源发布
2015年1.5版本中移除了最后的残余的C代码
2017年2月17日1.8版本
2017年8月24日1.9版本,1.9.2版本
2018年2月16日1.10版本
4、Go语言特点:
(1) Go=C+Python,兼顾运行速度与动态语言的快速开发。继承了C的理念,保留了C的编译执行方式,但弱化了指针。
(2) 引入包的概念,用于组织程序结构,Go语言的每一个文件都要属于一个包,而不能单独存在。
(3) 引入垃圾回收机制。内存自动回收,不需要开发人员管理
(4)天然并发(重要特点):从语言层面支持并发,实现简单;goroutine轻量级线程,可实现大并处理,高效利用多核;基于CPS并发模型实现。
(5) 吸收了管道通信机制,形成了Go语言特有的管道Channel,通过管道可以实现不同goroute之间的通信。
(6) 函数可以返回多个值,且go语句后面不需要写分号(编译器会自动加分号)
(7) 新的创新:比如切片slice、延时执行defer等。
5、Go语言的开发工具
1、visual studio code (VSCode)运行于主流操作系统智商,默认提供Go语言语法高亮,安装Go语言插件后可以智能提示、编译运行。
2、Sublime Text可免费试用默认也支持Go语法,但一定次数后提示是否购买。
3、Vim 是从vi发展出来的,代码补全、编译、错误跳转等方便编程的功能丰富。
4、Emacs 传说中的神器,不仅是一个编辑器,可称为一个集成运行环境。
5、Eclipse
6、LiteIDE 中国人开发专门Go语言开发
7、JetBrains公司的产品,付费,需要安装Go语言
建议学习时使用VSCode或vim。一定阶段后工作使用时用Eclipsse等自动化较强的开发工具。
(二)开发实战
创建一个文件夹gocode,类似java的workspace
package main//归属到main包下
import "fmt"//引入fmt包
func main(){
fmt.Println("hello")//输出hello
}
1、安装开发环境、sdk
https://www.golangtc.com/download下载sdk
然后解压即可:
配置环境变量:
GOROOT 指定sdk的安装路径
Path 添加sdk的/bin目录
GOPATH 工作目录,以后go项目存放的路径
例如:
2、快速入门
go程序的目录结构
d:/goproject
|--src
|--go code
|--project1(项目)
开始hello,world
通过go build命令进行编译源文件:build heelo.go
运行hello.exe文件即可:hello.ext
也可以用go run编译后直接执行:
大家注意,go和java在编译时不同的一点是,go可以指定编译后的文件名称。比如:
执行流程:
先编译再执行:类似java,编译后的exe相当于java的class
使用go run直接编译执行:底层也是先编译再执行。
先编译再执行的好处就是可以把exe拷贝到没有go的安装环境下执行
编译后的exe文件会将依赖的库也编译进来,所以exe会比go文件大很多。
注意事项:
Go应用程序的执行入口是main函数(和java一样)。
Go语言严格区分大小写。
Go方法由一条一条语句构成,语句后不需要加分号。
Go语言一条一条编译,不能把多条语句写在统一行。
Go语言定义的变量或者import引入的包引入后没有被使用,代码不能编译通过(和java不同)。
大括号成对出现,缺一不可。
3、转义字符及注释、规范的编码风格
\t 制表位
\n 换行符
\\ 一个\
\” 一个”
\r 一个回车
1) 行注释://
2)块注释:/**/
编码风格:
1)官方推荐用行注释来注释方法和语句。
2)正确的缩进和空白,可以使用键盘进行缩进,也能使用gofmt命令来进行格式化(gofmt –w main.go该指令将格式化后的内容重新写入到文件)。
3)运算符两边习惯性各加一个空格。比如:2 +3
4)一行尽量不要超过80个字符,如果超长建议使用换行符
5)左侧大括号要写在函数同一行,比如:
func main{
}
4、官方编程指南
点击tour选择中文,可以看中文版
Golang的中文网站https://studygolang.com/
对应api一般都可以在源码下找到
5、变量
Golang的变量是有数据类型的。
1定义变量 2声明变量 3使用变量
var i int
i = 10
变量使用的三种方式:
1指定变量类型,声明后若不赋值,使用默认值
2根据值自行判断变量类型
3省略var,变量名 := 变量值
使用变量及一次性声明多个变量,示例:
packagemain
import "fmt"
func main(){
var i int
fmt.Println(i)
var t = 100
fmt.Println(t)
a := 1000
fmt.Println(a)
//以下是一次性声明多个变量的三种方式
var n1,n2,n3 int
fmt.Println(n1,",",n2,",",n3)
var m1,m2,m3 = 10000,"tom",20000
fmt.Println(m1,",",m2,",",m3)
a1,a2,a3 := 100000,"jack",300000
fmt.Println(a1,",",a2,",",a3)
}
运行输出:
0
100
1000
0, 0 , 0
10000, tom , 20000
100000, jack , 300000
全局变量:在函数外部定义的变量就是全局变量。也可以分开创建声明和使用,也可以一次性声明,示例:
packagemain
import "fmt"
var n1 = 1
var n2 = 2
var n3 = 3
//以下是一次性声明多个全局变量
var(
m1 = 11
m2 = 22
m3 = 33
)
func main(){
fmt.Println(n1,",",n2,",",n3)
fmt.Println(m1,",",m2,",",m3)
}
运行输出:
1, 2 , 3
11, 22 , 33
局部变量有默认的值,全局变量没有默认值。(这点和java相反)
看到全局变量没有初始化赋值报错了。
6、数据类型
6.1基本数据类型
整数类型:int(有符号),int8,int16,int32,int64,uint(无符号),uint8,uint16,uint32,uint64,byte,
rune(等价于int32用于unicode码)
浮点型:float32,float64
字符型:没有专门的字符型,使用byte来保存单个字母字符
布尔型:bool
字符串:string(golang将string归属为基本数据类型,这和java不同)
6.2派生/复杂数据类型
指针,数组,结构体,管道,函数,切片,接口,map
6.3整数类型详解:
int8 有符号,占用1字节,表数范围-128 -127
int16有符号,占用2字节,表数范围-2(15次方) - 2(15次方)-1
int32有符号,占用4字节,表数范围-2(31次方) - 2(31次方)-1
int64有符号,占用8字节,表数范围-2(63次方) - 2(63次方)-1
其具体的大小和系统有关。
6.4变量使用细节
Golang默认的整数类型为int类型,Golang程序中整型变量在使用时遵守“保小不保大”的原则,即:在保证程序正确运行下,尽量使用占用空间小的数据类型。
bit是计算机中最小的存储单位,byte是计算机中最基本的存储单元。1byte = 8 bit
当我们var t =
100使用程序自行判断变量类型时,我们怎么知道变量的类型和字节大小呢,可以用fmt的Printf输出:
packagemain
import "fmt"
import "unsafe"
func main(){
var t =100 //t是什么类型
//fmt.Println()可以用作格式化输出
fmt.Printf("t的类型 %T",t)
fmt.Printf("\n")
fmt.Printf("t占用的字节 %d",unsafe.Sizeof(t))
}
挨个import太麻烦,也不专业,可以一次性集中引入,真正开发中的写法都是import集中引入:
packagemain
import(
"fmt"
"unsafe"
)
func main(){
var t =100 //t是什么类型
//fmt.Println()可以用作格式化输出
fmt.Printf("t的类型 %T",t)
fmt.Printf("\n")
fmt.Printf("t占用的字节 %d",unsafe.Sizeof(t))
}
运行程序显示:
t的类型 int
t占用的字节 8
6.5浮点数
单精度32位占用4字节
双精度64位占用8字节
浮点数在机器中存放形式:浮点数=符号位+指数位+尾数位
浮点数都是有符号的.
64位精度比32位精度大,要保存精度高的数建议选用float64,否则存储过程中容易丢精度。
浮点默认声明为float62类型,也是在开发中推荐使用的。
var num = 1.34e2 //科学计数法表示形式,相当于1.34*10的2次方,也就是1.34*100
var num = .123//相当于0.123,当然不推荐这种写法。
6.6字符类型
Golang中没有专门的字符类型,如果要存储单个字符,一般使用byte来保存。
未完待续......