基于共识算法和区块链模拟实现超级账本
Posted 爱做梦的小鱼
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于共识算法和区块链模拟实现超级账本相关的知识,希望对你有一定的参考价值。
基于共识算法和区块链模拟实现超级账本
实验语言:GO
实验环境:GoLand 2022.1 ; go 1.13.4.widows-amd64.msi ;curl-7.83.1
实验中使用的开源包:
http://github.com/davecgh/go-spew/spew;http://github.com/gorilla/mux;http://github.com/joho/godotenv;
实验中使用的工具包:
"crypto/sha256"“encoding/hex”“encoding/json”“fmt”“io”“log”“net/http”“os”“strconv”“strings”“sync”"time”
实验中的网络端口:http://localhost:8080
目录:
文章目录
- 基于共识算法和区块链模拟实现超级账本
实验简述
1.基于pow算法挖矿并实现区块链的基本功能
2.基于LevleDB实现数据的KV存储
实验背景
P2P
对等网络,即对等计算机网络,是一种在对等者之间分配任务和工作负载的分布式应用架构,网络的参与者共享他们所拥有的一部分硬件资源(处理能力、存储能力、网络连接能力、打印机等),这些共享资源通过网络提供服务和内容,能被其它对等节点直接访问而无需经过中间实体。在此网络中的参与者既是资源、服务和内容的提供者,又是资源、服务和内容的获取者。
比特币
与大多数货币不同,比特币不依靠特定货币机构发行,它依据特定算法,通过大量的计算产生,比特币经济使用整个P2P网络中众多节点构成的分布式数据库来确认并记录所有的交易行为,并使用密码学的设计来确保货币流通各个环节的安全性。P2P的去中心化特性与算法本身可以确保无法通过大量制造比特币来人为操控币值。基于密码学的设计可以使比特币只能被真实的拥有者转移或支付。
区块链
区块链,就是一个又一个区块组成的链条。每一个区块中保存了一定的信息,它们按照各自产生的时间顺序连接成链条。这个链条被保存在所有的服务器中,只要整个系统中有一台服务器可以工作,整条区块链就是安全的。这些服务器在区块链系统中被称为节点,它们为整个区块链系统提供存储空间和算力支持。如果要修改区块链中的信息,必须征得半数以上节点的同意并修改所有节点中的信息,而这些节点通常掌握在不同的主体手中。
实验第一部分-基于pow算法挖矿并实现区块链的基本功能
实验原理
区块有两种,一个是普通区块,一个就是创世区块。创世区块就是一项区块链项目中的第一个区块。
实验内容
配置环境
声明变量
//区块结构体
type Block struct
PreHash string
Hashcode string
TimeStamp string
Diff int
Value string
Index int
Nonce int
//区块链链表
type lian_my struct
NextNode *lian_my
Data *BLOCK.Block
这一步用于根据比特币定义我们的哈希值以及新的链表
方法
通过自带的数据包生成每一个区块的哈希值
func GenerationHashValue(block Block) string
var hashdata = strconv.Itoa(block.Index) + strconv.Itoa(block.Nonce) + strconv.Itoa(block.Diff) + block.TimeStamp
var hashmy = sha256.New()
hashmy.Write([]byte(hashdata))
hashed := hashmy.Sum(nil)
return hex.EncodeToString(hashed)
生成之后的区块
func GenerateNextBlock(data string, oldBlock Block) Block
var newBlock Block
newBlock.TimeStamp = time.Now().String()
newBlock.Diff = 3
newBlock.Index = oldBlock.Nonce + 1
newBlock.Value = data
newBlock.PreHash = oldBlock.Hashcode
newBlock.Nonce = 0
newBlock.Hashcode = pow(newBlock.Diff, &newBlock)
return newBlock
初始区块声明
func GenerateFirstBlock(data string) Block
var chuangshi Block
chuangshi.PreHash = "0"
chuangshi.TimeStamp = time.Now().String()
chuangshi.Diff = 3
chuangshi.Value = data
chuangshi.Index = 1
chuangshi.Nonce = 0
chuangshi.Hashcode = GenerationHashValue(chuangshi)
return chuangshi
pow算法
//pow工作量证明
func pow(diff int, block *Block) string
for
hashmy := GenerationHashValue(*block)
if strings.HasPrefix(hashmy, strings.Repeat("0", diff))
return hashmy
fmt.Println("挖到了一个区块")
else
block.Nonce++
函数传入指针,这个相当于是在不断的挖矿,for是一个死循环,用block的信息生成一个哈希值,判断这一次的哈希值是否前面的0满足diff,这里可以直接使用hasprefix()函数来进行判断,相当的方便。
区块链记录
//创建头节点,保存创世区块
func CreaterHeader(data *BLOCK.Block) *lian_my
headerNode := new(lian_my)
headerNode.NextNode = nil
headerNode.Data = data
return headerNode
//添加下一个结点
func Addnode(data *BLOCK.Block, node *lian_my) *lian_my
var newNode *lian_my = new(lian_my)
newNode.NextNode = nil
newNode.Data = data
node.NextNode = newNode
return newNode
//展示当前所有节点
func ShowNodes(node *lian_my)
n := node
for
if n.NextNode == nil
fmt.Println(n.Data)
break
else
fmt.Println(n.Data)
n = n.NextNode
调用方法运行结果
初始化第一个区块
进行挖矿
广播基本的区块链
实验 第二部分-模拟实现基于LevelDB的KV存储
实验原理
leveldb是谷歌两位工程师使用C++实现的k-v存储系统,我们这里希望使用go进行复现, 属于按照key和value存储,用户也是可以重写排序函数,包含了最基本的数据操作接口,put,get,delay.同时多次操作可以认为是一次原子操作,可以用于支持事务。
实验代码里的方法及对象
结构体初始化
DB结构体
type DB struct
path string
data map[string][]byte
这里path(字符串类型)用于存储连接的地址,data(byte类型)用于存储kv的键值
迭代器接口和结构体
type Iterator interface
Next() bool
Key() []byte
Value() []byte
type myIterator struct
data []Pair
index int
length int
NEXT判断是否下一个有值, key和value用于遍历键值
键值的结构体
type Pair struct
Key []byte
Value []byte
初始化结构体
func NewDefaultIterator(data map[string][]byte) *myIterator
my := new(myIterator)
my.index = -1
my.length = len(data)
for k, v := range data
p := Pair[]byte(k),
v,
my.data = append(self.data, p)
return my
初始化迭代器的默认值并进行遍历
迭代器基本功能的实现
func (self *myIterator) Next() bool
if self.index < self.length-1
self.index++
return true
return false
func (self *myIterator) Key() []byte
if self.index == -1 || self.index >= self.length
panic(fmt.Errorf("INDEXOUTOFBOUNDERROR"))
return self.data[self.index].Key
func (self *myIterator) Value() []byte
if self.index >= self.length
panic(fmt.Errorf("INDEXOUTOFBOUNDERROR"))
return self.data[self.index].Value
实现下一个是否存在,以及返回value 和key
模拟功能实现
模拟连接
func New(path string) (*DB, error)
my := DB
path: path,
data: make(map[string][]byte),
return &my, nil
模拟put,get
func (my *DB) Put(key []byte, value []byte) error
my.data[string(key)] = value
return nil
func (my *DB) Get(key []byte) ([]byte, error)
if v, ok := my.data[string(key)]; ok
return v, nil//如果有则说明可以直接使用
else //如果返回为空,则说明没有
return nil,
fmt.Errorf("Not Found")
模拟 Del
func (self *DB) Del(key [](byte)) error
if _, ok := self.data[string(key)]; ok
delete(self.data, string(key))
return nil
else
return fmt.Errorf("not Found")
KB存储测试
迭代器测试
测试方法:
import (
"fmt"
"testing"
)
//迭代器测试
func TestNewDefaultIterator(t *testing.T)
data := make(map[string][]byte)
data["K1"] = []byte("V1")
data["K2"] = []byte("V2")
data["K3"] = []byte("V3")
data["K4"] = []byte("V4")
my := NewDefaultIterator(data)
if my.length != 3
t.Fatal()
for iter.Next()
fmt.Printf("%s : %s\\n", my.Key(), string(my.Value()))
测试结果
DB测试
测试方法
func Test_leveldb(t *testing.T)
db, err := New("")
check(err)
err = db.Put([]byte("k1"), []byte("v1"))
check(err)
err = db.Put([]byte("k4"), []byte("v4"))
check(err)
err = db.Put([]byte("k2"), []byte("v2"))
check(err)
err = db.Put([]byte("k1"), []byte("v1"))
check(err)
err = db.Put([]byte("k8"), []byte("v8"))
check(err)
v, err := db.Get([]byte("k8"))
fmt.Printf("%s\\n\\n", v)
if !bytes.Equal(v, []byte("v8"))
t.Fatal()
v, err = db.Get([]byte("k1"))
if !bytes.Equal(v, []byte("v1"))
t.Fatal()
//err = db.Del([]byte("k1"))
//check(err)
iter := db.Iterator()
for iter.Next()
fmt.Printf("%s - %s \\n", string(iter.Key()), string(iter.Value()))
func check(err error)
if err != nil
panic(err)
测试结果
PUT和GET测试
DEL测试
GET不存在元素时测试
实验第3部分-局域网广播和有效区块链
实验内容
1.可以在网络端POST指令访问,添加新的比特币交易区块。
2.可以在网络端GET指令访问,查看所有已经添加的交易信息。
实验原理
1.实验中使用的开源包:
http://github.com/davecgh/go-spew/spew;
http://github.com/gorilla/mux;
http://github.com/joho/godotenv;
开源包功能:编写web程序的软件包,在控制台格式化输出结果,配置编写.env文件
实验方法
声明区块,区块链等基本(和前两部分一样)
注:基本代码和前两部分相同,但在下面处存在区别
var Blockchain []Block
var mutex = &sync.Mutex
上锁,并且有数组进行存储,同时在有效判别函数中,会抓取所有节点,寻找最长的链,来进行写入。但是在本次试验中因为我在本地并没有其他人的加入,所以我就用简单的python语言大概简述。
def resolve(self) ->bool:
fujin=self.nodes
new_chain = None
max_length = len(self.chain)
for node in fujin:
get new_length
if new_legth>max_length. and ishashvaild(self)
max_length=length
new_chain=chain
if new_chain:
self.chain = new_chain
return true
return false
互联网区块链方法
HTTP启动
//http启动
func run() error
mux := makeMuxRouter()
httpAddr := os.Getenv("ADDR")
log.Println("listening on", os.Getenv("ADDR"))
s := &http.Server
Addr: ":" + httpAddr,
Handler: mux,
ReadTimeout: 10 * time.Second,
WriteTimeout: 10 * time.Second,
MaxHeaderBytes: 1 << 20,
if err := s.ListenAndServe(); err != nil
return err
return nil
对区块链数据的操作的初始化
func makeMuxRouter() http.Handler
muxRouter := mux.NewRouter()
muxRouter.HandleFunc("/", handgetblockhain).Methods("GET")
muxRouter.HandleFunc("/", handerwriteblock).Methods("POST")
return muxRouter
查看互联网请求中的内容
func handgetblockhain(w http.ResponseWriter, r *http.Request)
bytes, err := json.MarshalIndent(Blockchain, "", "\\t")
if err != nil
http.Error(w, err.Error(), http.StatusInternalServerError)
return
io.WriteString(w, string(bytes))
验证当前区块是否加入
func isblockivaild(newBlock, oldblock Block) bool
if oldblock区块链|Hyperchain学习
共识
1. 概述
共识机制是保证区块链中所有共识节点(即验证节点:validating peer,VP)按照相同顺序执行交易、写入账本的基础,记账节点(即非验证节点:non-validating peer,NVP)只需要从其所连接的共识节点中同步账本信息,因此无需参与共识。
Hyperchain平台支持可插拔的共识机制,可以针对区块链的不同应用场景提供不同的共识算法,当前版本已经实现了PBFT算法的改良算法:高鲁棒拜占庭容错算法RBFT(Robust Byzantine Fault Tolerance),其算法构思来源于多篇论文(尤其是Aardvark),后续将陆续支持RAFT等共识算法。
2. RBFT
拜占庭容错:至少3f+1个节点才能容忍f个拜占庭错误
3. RBFT流程
RBFT常规流程具体分为如下几个步骤:
-
交易转发阶段:客户端将交易发送到区块链中的任意节点(包括共识节点与记账节点),其中记账节点在收到交易后会主动转发给与其相连的共识节点;而共识节点在收到客户端的交易后将其广播给其他共识节点,这样所有共识节点的交易池中都会维护一份完整的交易列表;
-
PrePrepare阶段:主节点按照如下策略进行打包:用户可以根据需求自定义打包的超时时间(batch timeout)与打包的最大区块大小(batch size),主节点在超时时间内收集到了足够多(超过最大区块大小个数)的交易或者超时时间到达后仍未收集到足够多的交易都会触发主节点的打包事件。主节点将交易按照接收的时间顺序打包成块,并进行验证,计算执行结果,最后将定序好的交易信息连同验证结果等写入PrePrepare消息中广播给所有共识节点,开始三阶段处理流程;
-
Prepare阶段:从节点在收到主节点的PrePrepare消息后,首先进行消息合法性检查,检查当前的视图与区块号等信息,检查通过后向共识节点广播Prepare消息;
-
Commit阶段:从节点在收到(quorum-1)个Prepare消息以及相应的PrePrepare消息后进行验证,并将验证结果与主节点写入PrePrepare消息中的验证结果进行比对,比对结果一致则广播Commit表明本节点同意主节点的验证结果,否则直接发起ViewChange表明本节点认为主节点存在异常行为,需要切换主节点;
-
写入账本:所有共识节点在收到quorum个Commit消息后将执行结果写入本地账本。
Hyperchain通过在共识模块中加入验证机制,可以保证从节点对主节点的每一次排序打包的结果进行校验,尽早地发现主节点的拜占庭行为,提升了系统的稳定性。
检查点
为了防止运行过程中产生过多的消息缓存,共识节点需要定时清理一些无用的消息缓存。
交易池
交易池是共识节点用于缓存交易的场所,交易池的存在一方面限制了客户端发送交易的频率,另一方面也减少了主节点的带宽压力。
4. RBFT视图变更
RBFT视图变更能够解决主节点成为拜占庭节点的问题。在RBFT算法中,参与共识的节点可根据角色分为主节点和从节点。主节点最重要的功能是将收到的交易按照一定策略打包成块,为交易定序,并让所有节点按照此顺序执行。然而,如果主节点发生宕机、系统错误或者被攻占(即成为拜占庭节点),从节点需要及时发现主节点异常并选举产生新的主节点。这将是所有BFT类算法为满足稳定性必须要解决的问题。
视图
在RBFT与PBFT中,都引入了视图(View)概念,即每次更换一个主节点的同时都会切换视图。目前RBFT采用轮换的方式切换主节点,并且view从0开始只增不减。当前的view和总节点数量N决定了主节点id:??????????????????=(????????+1)mod??
可检测到的拜占庭行为
目前RBFT能够检测到的主节点的拜占庭行为主要有2种场景:
- 主节点停止工作,不再发送任何消息;
- 主节点发送错误的消息。
对于场景一,RBFT由nullRequest机制保证,行为正确的主节点会在没有交易发生时,向所有从节点定时发送nullRequest来维持正常连接。如果从节点在规定时间内没有收到nullRequest,则会触发ViewChange流程选举新的主节点。
对于场景二,从节点会对主节点发出的消息进行验证,如上一节中提到的包含在PrePrepare消息中的验证结果,如果从节点验证不通过的话,会直接发起ViewChange流程选举新的主节点。
此外,RBFT还提供了可配置的ViewChangePeriod选项。用户可以根据需要设置此选项,每写入一定数量区块后进行主动的ViewChange轮换主节点,一来能够缓解主节点作为打包节点的额外压力,二来也使所有参与共识的节点都能承担一定的打包工作,保证了公平性。
5. RBFT自主恢复
区块链网络在运行过程中由于网络抖动、突然断电、磁盘故障等原因,可能会导致部分节点的执行速度落后于大多数节点。在这种场景下,节点需要能够做到自动恢复才能继续参与后续的共识流程。为了解决这类数据恢复的问题,RBFT算法提供了一种动态数据自动恢复的机制(recovery),recovery通过主动索取现有共识网络中所有节点的视图、最新区块等信息来更新自身的存储状态,最终同步至整个系统的最新状态。在节点启动、节点重启或者节点落后的时候,节点将会自动进入recovery,同步至整个系统的最新状态。
6. RBFT节点增删
在联盟链场景下,由于联盟的扩展或者某些成员的退出,需要联盟链支持成员的动态进出服务,而传统的PBFT算法不支持节点的动态增删。RBFT为了能够更加方便地控制联盟成员的准入和准出,添加了保持集群非停机的情况下动态增删节点的功能。
以上是关于基于共识算法和区块链模拟实现超级账本的主要内容,如果未能解决你的问题,请参考以下文章