基于共识算法和区块链模拟实现超级账本

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常规流程具体分为如下几个步骤:

  1. 交易转发阶段:客户端将交易发送到区块链中的任意节点(包括共识节点与记账节点),其中记账节点在收到交易后会主动转发给与其相连的共识节点;而共识节点在收到客户端的交易后将其广播给其他共识节点,这样所有共识节点的交易池中都会维护一份完整的交易列表;

  2. PrePrepare阶段:主节点按照如下策略进行打包:用户可以根据需求自定义打包的超时时间(batch timeout)与打包的最大区块大小(batch size),主节点在超时时间内收集到了足够多(超过最大区块大小个数)的交易或者超时时间到达后仍未收集到足够多的交易都会触发主节点的打包事件。主节点将交易按照接收的时间顺序打包成块,并进行验证,计算执行结果,最后将定序好的交易信息连同验证结果等写入PrePrepare消息中广播给所有共识节点,开始三阶段处理流程;

  3. Prepare阶段:从节点在收到主节点的PrePrepare消息后,首先进行消息合法性检查,检查当前的视图与区块号等信息,检查通过后向共识节点广播Prepare消息;

  4. Commit阶段:从节点在收到(quorum-1)个Prepare消息以及相应的PrePrepare消息后进行验证,并将验证结果与主节点写入PrePrepare消息中的验证结果进行比对,比对结果一致则广播Commit表明本节点同意主节点的验证结果,否则直接发起ViewChange表明本节点认为主节点存在异常行为,需要切换主节点;

  5. 写入账本:所有共识节点在收到quorum个Commit消息后将执行结果写入本地账本。

Hyperchain通过在共识模块中加入验证机制,可以保证从节点对主节点的每一次排序打包的结果进行校验,尽早地发现主节点的拜占庭行为,提升了系统的稳定性。

检查点

为了防止运行过程中产生过多的消息缓存,共识节点需要定时清理一些无用的消息缓存。

交易池

交易池是共识节点用于缓存交易的场所,交易池的存在一方面限制了客户端发送交易的频率,另一方面也减少了主节点的带宽压力。 

 

4. RBFT视图变更

RBFT视图变更能够解决主节点成为拜占庭节点的问题。在RBFT算法中,参与共识的节点可根据角色分为主节点和从节点。主节点最重要的功能是将收到的交易按照一定策略打包成块,为交易定序,并让所有节点按照此顺序执行。然而,如果主节点发生宕机、系统错误或者被攻占(即成为拜占庭节点),从节点需要及时发现主节点异常并选举产生新的主节点。这将是所有BFT类算法为满足稳定性必须要解决的问题。

视图

在RBFT与PBFT中,都引入了视图(View)概念,即每次更换一个主节点的同时都会切换视图。目前RBFT采用轮换的方式切换主节点,并且view从0开始只增不减。当前的view和总节点数量N决定了主节点id:??????????????????=(????????+1)mod??

可检测到的拜占庭行为

目前RBFT能够检测到的主节点的拜占庭行为主要有2种场景:

  1. 主节点停止工作,不再发送任何消息;
  2. 主节点发送错误的消息。

对于场景一,RBFT由nullRequest机制保证,行为正确的主节点会在没有交易发生时,向所有从节点定时发送nullRequest来维持正常连接。如果从节点在规定时间内没有收到nullRequest,则会触发ViewChange流程选举新的主节点。

对于场景二,从节点会对主节点发出的消息进行验证,如上一节中提到的包含在PrePrepare消息中的验证结果,如果从节点验证不通过的话,会直接发起ViewChange流程选举新的主节点。

此外,RBFT还提供了可配置的ViewChangePeriod选项。用户可以根据需要设置此选项,每写入一定数量区块后进行主动的ViewChange轮换主节点,一来能够缓解主节点作为打包节点的额外压力,二来也使所有参与共识的节点都能承担一定的打包工作,保证了公平性。

 

5. RBFT自主恢复

区块链网络在运行过程中由于网络抖动、突然断电、磁盘故障等原因,可能会导致部分节点的执行速度落后于大多数节点。在这种场景下,节点需要能够做到自动恢复才能继续参与后续的共识流程。为了解决这类数据恢复的问题,RBFT算法提供了一种动态数据自动恢复的机制(recovery),recovery通过主动索取现有共识网络中所有节点的视图、最新区块等信息来更新自身的存储状态,最终同步至整个系统的最新状态。在节点启动、节点重启或者节点落后的时候,节点将会自动进入recovery,同步至整个系统的最新状态。

 

6. RBFT节点增删

在联盟链场景下,由于联盟的扩展或者某些成员的退出,需要联盟链支持成员的动态进出服务,而传统的PBFT算法不支持节点的动态增删。RBFT为了能够更加方便地控制联盟成员的准入和准出,添加了保持集群非停机的情况下动态增删节点的功能。

以上是关于基于共识算法和区块链模拟实现超级账本的主要内容,如果未能解决你的问题,请参考以下文章

区块链:3共识算法 非强一致共识算法PoW机制

Hyperledger Fabric启用CouchDB为状态数据库

区块链|Hyperchain学习

区块链共识算法整理

基于实用拜占庭共识算法(PBFT)的区块链模型的评估

区块链之超级账本Fabric 基于SDK应用开发