草庐IT

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

爱做梦的小鱼 2023-09-04 原文

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

实验语言: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.Index+1 != newBlock.Index {
      return false
   }
   if oldblock.HashCode != newBlock.PreHash {
      return false
   }
   if calculationHash(newBlock) != newBlock.HashCode {
      return false
   }
   return true
}

​ 论证是否是旧的区块+1,以及哈希值是否对应

POST,挖到一个区块后进行广播

//声明一个post类型的数据类型
type Message struct {
   BPM int
}

func handerwriteblock(writer http.ResponseWriter, request *http.Request) {
   writer.Header().Set("Content-Type", "application/json")
   var message Message
   decoder := json.NewDecoder(request.Body)
   if err := decoder.Decode(&message); err != nil {
      respondWithJson(writer, request, http.StatusNotFound, request.Body)
      return
   }
   defer request.Body.Close()

   //生成区块
   mutex.Lock()
   newblock := generateBlock(Blockchain[len(Blockchain)-1], message.BPM)
   mutex.Unlock()

   //判断是否合法
   if isblockivaild(newblock, Blockchain[len(Blockchain)-1]) {
      Blockchain = append(Blockchain, newblock)
      spew.Dump(Blockchain)
   }
   respondWithJson(writer, request, http.StatusCreated, newblock)
}

     func respondWithJson(writer http.ResponseWriter, request *http.Request, code int, inter interface{}) {
   writer.Header().Set("Content-Type", "application/json")
   //格式化输出JSON
   response, err := json.MarshalIndent(inter, "", "\t")
   if err != nil {
      writer.WriteHeader(http.StatusInternalServerError)
      writer.Write([]byte("HTTP 500:Serve Error"))
      return
   }
   writer.WriteHeader(code)
   writer.Write(response)
}

实验结果

测试主方法

func main() {
   err := godotenv.Load()
   if err != nil {
      log.Fatal(err)
   }
   go func() {
      genesisblock := Block{}
      genesisblock = Block{
         0, time.Now().String(), 0, calculationHash(genesisblock),
         "", differculty, 0}
      mutex.Lock()
      Blockchain = append(Blockchain, genesisblock)
      mutex.Unlock()
      spew.Dump(genesisblock)
   }()
   log.Fatal(run())
}
.env配置文件
ADDR= 8080
Curl测试命令事例
curl -H "Content-Type: application/json" -X POST -d"{\"BPM\":10}"“http://localhost:8080”

curl http://localhost:8080

实验结果

发送指令

播在局域网中

发送被希望记录的交易信息

挖到矿后将其加入了区块链

代码扩展部分

我个人是还想使用bee-go metaMast做一个使用区块链交易界面的,但是还是因为个人的问题,代码在golang的前端部分已经完善,但在remix智能公约部分陷进去,出不来了。所以我简要的附上几张图,工程文件里的view会附上我的html css文件。


首页

查询

智能合约

beego

实验心得

在长达两个月的时间,我从GO语言的小白到可以逐步完善自己的想法,从只知道区块链可以炒股到逐渐对其中的原理有了更深的理解,尤其是在广播时候的代码,因为比特币公开的源代码是用c++编写的,所以用go浮现的时候就有了颇多困难。尤其是开发智能合约到最后还要学Solidity语言。

因为之前一直都在TI杯比赛中接触代码,但是比赛中大多数都是基于嵌入式环境的硬件系统,无论是ARDUINO,STM32还是ESP82266的开发虽然在调试上但是其中的编程语言都是基于C的,GO和css则完全是面向对象的,尤其是Golang的go get,每次下载一个包都是困难重重,无法cloning竟然还和内网有关。

但是面向对象的有趣在于可以大大减少程序员的调试难度,思路也更有逻辑性。暑假里 我一定要彻底完成我自己的以太坊彩票站。

也感谢沈老师和蔺老师在课堂上对于网络安全的讲解,让我认识到安全学科已经成为了新的行业热门,也了解到网络安全对于社会生产的重要性。

说明:

本实验中的代码均已开源在github;本说明文档首发于个人博客

有关基于共识算法和区块链模拟实现超级账本的更多相关文章

  1. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  2. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  3. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  4. 区块链之加解密算法&数字证书 - 2

    目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非

  5. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  6. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  7. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  8. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  9. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  10. ruby - Arrays Sets 和 SortedSets 在 Ruby 中是如何实现的 - 2

    通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复

随机推荐