Dian_BlockChainPractice
根据区块链中存储的信息的不同,区块链所应用的场所也不尽相同。(比如比特币中所应用的区块中就是存储的用户交易信息)
例如:建立食物链。
分为四种类型:公有区块链、私有区块链、联盟区块链、许可区块链。
主要研究共有区块链技术,另外三种只是部分不同,大体一致。
由于Go语言支持语言层次上的并发操作,所以Go语言非常适合分布式系统(可以简单理解为具有很多个服务器的一个集成系统)
- 包
包是一系列源代码的集合。每个源文件都要求属于且仅属于一个包。
按照道理说,每个Go语言应用程序都有一个main包,main包中能有一个main方法。但是本次实习为了方便,将所有的.go文件都放到同一个main包中,然后编译运行时,只需要分开单独编译运行即可。
- GOPATH模式
GOPATH模式最大的问题就是版本管理,GOPATH模式根本就没有版本的概念。导入包时,无法确保导入最新的版本,因为所有的版本都是一样的命名。
- GO vender 模式
每个Go项目都有一个独立的vender文件夹,可以在文件夹中存放一些包(自己写的或者导入的外部包)。每个项目的vender之间互相不影响。
问题:如果有很多的Go项目都使用了同一个包,那么就会导致在很多地方都需要导入同一个包,这无疑是对磁盘空间的一种浪费。除此之外,也无法对第三方包进行集中的管理。
- GO mod模式
通过一下命令来打开GO mod模式(将环境变量GO111MODULE修改为on)
$ go env -w GO111MODULE="on"
比较复杂,主要是通过go.mod和go.sum两个文件夹来实现。(以后再深入了解)
- PoW算法简述:
PoW,全称为Proof of Work(工作量证明)。由于哈希算法的单向性,计算机只能通过枚举的方式不断尝试。只有运用巨大的算力率先得到满足难度值答案的用户,才算完成了工作量证明,从而有权利进行打包交易,并活得手续费。
- 实现PoW算法
- PoS算法简介:
权益证明,也就是说谁的权益(持币数乘以持币时间)更大,谁就更有可能称为矿工,进行打包。
- 实现原理:
PoS实现的重点是如何用数据表示出权益值,暂时先忽略时间因素,只考虑持币数。设定一个切片,切片长度等于总币数,切片的值为该比特币的用户地址。(即该切片表示了每一个比特币的拥有者是谁)然后再数组内产生随机数,实现简易版本的PoS算法。
预期完成任务:
1)学习使用GO MOD模式(记得仔细阅读一下官方教程)
2)实现PoS简易算法(写道一个新的go文件中,但是记得先搞清楚go语言多文件的一些规则)
3)开始动手进阶任务
源代码文件放在project01_demo\main\PoS.go中。
关键是如何利用矿工的权益值,通过随机数选出一个幸运矿工。
下面的函数解决了这个问题。
将所有用户的权益比例计算出来后,然后依次存到[0, 1] 之间。再利用随机数产生一个0到1之间的小数,看该小数落到哪一块,哪一位用户就有权力打包。
// ReturnIndex 利用随机数返回一个幸运矿工的序号
func ReturnIndex(users []User) int64 {
for _, user := range users {
user.ComputeWeight() //为每一个用户计算权益值
}
//随机数算法返回幸运的打包者
//计算总权重
var totalWeight int64
for i := 1; i <= userNum; i++ {
totalWeight += users[i].weight
}
var portion []float64 = make([]float64, userNum) //记录每个用户的比例(portion)
for i := 1; i <= userNum; i++ {
// 将每个用户所占的比例计算出来,然后再加上之前的用户的比例(最终所有用户的比例会占满[0, 1])
portion[i] = float64(users[i].weight)/float64(totalWeight) + portion[i-1]
}
rand.Seed(time.Now().Unix())
var x = rand.Float64()
for i := 1; i <= userNum; i++ {
if x > portion[i-1] && x < portion[i] {
return int64(i)
}
}
return 0
}
- Go语言http包工作原理
-
创建 Listen Socket, 监听指定的端口,等待客户端请求到来。
-
Listen Socket 接受客户端的请求,得到 Client Socket, 接下来通过 Client Socket 与客户端通信。
-
处理客户端的请求,首先从 Client Socket 读取 HTTP 请求的协议头,如果是 POST 方法,还可能要读取客户端提交的数据,然后交给相应的 handler 处理请求,handler 处理完毕准备好客户端需要的数据,通过 Client Socket 写给客户端。
- 理解处理GET请求的代码片段
大致理解了组长给的代码的大致含义(看了一晚上文档,眼睛快没了~~~)
知道了怎么设置handler,怎么设置env,怎么发起http请求等。
预期完成:
1)通过阅读mux,godotenv,spew三个包的官方文档,了解三个包中一些函数的基本用法
2)熟练掌握上面三个包的用法和功能
3)运用三个包和net/http包中的函数完成验收任务
该包主要是提供一个方便操作的路由复用器,这样就不用使用http包里面的ServerMux。(因为ServerMux无法提供一些复杂的路由功能)
godotnev主要是读取.env文件,设置环境变量,然后获取端口号的作用。
spew代替fmt进行更牛皮的格式化输出
- 理解io包的用法,和基本函数。尝试再GET请求实现的基础上完成POST请求
由于不知道为什么一直无法解析(因为请求的格式问题),所以用两个相似的进行比对,最终得出结论。(下图)
可以看出图中的斜线,以及两边的飘点,还有10没有双引号,综上所述,便可得出正确的格式输入!
咦,为啥DATA可以读取,但是Bmp的值却一直为零了。从晚上七点到九点,这个问题让我想不明白。我以为是因为只能读取字符串,然后想把bmp弄成字符串,然后再转换成数字,可惜还是失败了。最后尝试了多种方法仍无果后,我无意间看到了BPM这个有点小奇怪。再仔细一看,我人傻了。输入的请求是BPM,结构体字段内的值为BMP,这当然无法读取了。难受,一个小拼写错误浪费了我两个多钟头!非常得离谱!
最终完成了POST请求。(测试时难度值设为1,设定为10的话,PoW算法需要一点时间算出来)
预期完成任务:
1)理解PBFT和Raft算法
2)读懂案例代码并补全
Raft算法通过选出一个leader来简化日志副本的管理。Raft能为在计算机集群之间部署有限状态机提供一种通用方法,并确保集群内的任意节点在某种状态转换上保持一致。
正常情况下,只会有一个服务器是Leader,并且所有的外部请求都会被导到Leader处。Leader会每隔一段时间发送消息(心跳),如果Follower超时(timeout)未收到消息,那么集群进入选举状态。
- Raft算法的三个子问题
1)领导选举
主要变量为计算机集群任期(用term表示),每个服务器的任期编号(用term couter表示)。
状态转移图如下:
赢得超半数票的候选人状态转移至Leader状态,然后开始在新的任期工作。
2)记录复写
记录复写的任务主要由Leader完成,用于保证每个服务器的记录统一,同时合理运行指令。
3)安全性
- 选举安全性:每个任期最多只能选出一个领袖。
- 领袖附加性:领袖只会把新指令附加(英语:append)在记录尾端,不会改写或删除已有指令。
- 记录符合性:如果某个指令在两个记录中的任期和指令序号一样,则保证序号较小的指令也完全一样。
- 领袖完整性:如果某个指令在某个任期中存储成功,则保证存在于领袖该任期之后的记录中。
- 状态机安全性:如果某服务器在其状态机上运行了某个指令,其他服务器保证不会在同个状态(也就是the same index of the state machine)上运行不同的指令。
PBFT算法的提出是用于解决拜占庭将军问题。也就是在网络节点中存在一定数量的“叛徒”结点。
主要分为四个步骤:
- 客户端发送请求给主节点
- 主节点广播请求给其它节点,节点执行 pbft 算法的三阶段共识流程。
- 节点处理完三阶段流程后,返回消息给客户端。
- 客户端收到来自 f+1 个节点的相同消息后,代表共识已经正确完成。
也就是服务端创建一个满足rpc规则的方法,然后开启监听对应的端口号。
客户端通过拨号对应的端口,然后输入相应的参数进行调用对应的方法,获得输出。
先实现三个分布式结点的选举。不同结点之间采用rpc来进行通信。
- 两种RPC通信
- Ticker结构使用
package main
import (
"fmt"
"time"
)
func main() {
ticker := time.NewTicker(time.Second) // 每隔1s进行一次打印
for {
//每隔time.Second时间,就会向ticker.C通道传递一个值。(可以用来模拟结点超时开启候选模式)
<-ticker.C
fmt.Println("这是ticker的打印")
}
}
由于复现Raft算法难度有点大,今天没有能够完成复现任务。明天继续尝试复现,争取完成拓展任务。
预期完成任务:
1)提升运用Go语言特性(高并发)的能力,做到能够熟练运用Go的协程
2)继续复现Raft算法
代码推进过程有点困难,在写代码的时候,发现很多关于Raft算法的细节地方理解得不是很清楚。按照道理来说,如果算法了解得很清楚的情况下,代码实现应该不算很困难的一件事情。
所以继续回去阅读Raft算法的一些具体细节。
- 算法实现过程:
三个结点的共识模块同时被初始化为Follower状态,然后在150~300ms的随机超时时间内,如果该结点还是为Follower且没有收到其他节点成为Leader的消息,则该结点变为Candidate状态,并向其他结点发送VoteRequest通信。
在另外一个文件Listen.go中,应该让三个结点同时开启监听8080号端口,方便收到通信请求。同时,在Listen.go文件中,还应该设计好调用的结构的方法。例如Rect的Area方法和Perimeter方法。(示例文件中提供)
在网上查阅了一些资料之后,花了大半天的时间,最终实现了简单的Raft算法,但是可惜无法用cmd展示。程序可以产生.exe文件,但是只能自己运行,并且没有任何输出,没有多余的时间进行展示部分的优化。