200行Go代码实现自己的区块链——区块生成与网络通信

在第一篇文章[1]中,我们向大家展示了如何通过精炼的Go代码实现一个简单的区块链。如何计算每个块的 Hash 值,如何验证块数据,如何让块链接起来等等,但是所有这些都是跑在一个节点上的。文章发布后,读者反响热烈,纷纷留言让我快点填坑(网络部分),于是就诞生了这第二篇文章。

这篇文章在之前的基础上,解决多个节点网络内,如何生成块、如何通信、如何广播消息等。

流程

  • 第一个节点创建“创始区块”,同时启动 TCP server并监听一个端口,等待其他节点连接。

Step 1

  • 启动其他节点,并与第一个节点建立TCP连接(这里我们通过不同的终端来模拟其他节点)
  • 创建新的块

Step 2

  • 第一个节点验证新生成块
  • 验证之后广播(链的新状态)给其他节点

Step 3

  • 所有的节点都同步了最新的链的状态

之后你可以重复上面的步骤,使得每个节点都创建TCP server并监听(不同的)端口以便其他节点来连接。通过这样的流程你将建立一个简化的模拟的(本地的)P2P网络,当然你也可以将节点的代码编译后,将二进制程序部署到端。

开始coding吧

设置与导入依赖

参考之前第一篇文章,我们使用相同的计算 hash 的函数、验证块数据的函数等。

设置
在工程的根目录创建一个 .env 文件,并添加配置:

ADDR=9000

通过 go-spew 包将链数据输出到控制台,方便我们阅读:

go get github.com/davecgh/go-spew/spew

通过 godotenv 包来加载配置文件:

go get github.com/joho/godotenv

之后创建 main.go 文件。

导入
接着我们导入所有的依赖:

package main

import (
    "bufio"
    "crypto/sha256"
    "encoding/hex"
    "encoding/json"
    "io"
    "log"
    "net"
    "os"
    "strconv"
    "time"

    "github.com/davecgh/go-spew/spew"
    "github.com/joho/godotenv"
)

回顾
让我们再快速回顾下之前的重点,我们创建一个 Block 结构体,并声明一个Block 类型的 slice,Blockchain

// Block represents each ‘item‘ in the blockchain
type Block struct {
    Index     int
    Timestamp string
    BPM       int
    Hash      string
    PrevHash  string
}

// Blockchain is a series of validated Blocks
var Blockchain []Block

创建块时计算hash值的函数:

// SHA256 hashing
func calculateHash(block Block) string {
    record := string(block.Index) +
            block.Timestamp + string(block.BPM) + block.PrevHash
    h := sha256.New()
    h.Write([]byte(record))
    hashed := h.Sum(nil)
    return hex.EncodeToString(hashed)
}

创建块的函数:

// create a new block using previous block‘s hash
func generateBlock(oldBlock Block, BPM int) (Block, error) {

    var newBlock Block

    t := time.Now()

    newBlock.Index = oldBlock.Index + 1
    newBlock.Timestamp = t.String()
    newBlock.BPM = BPM
    newBlock.PrevHash = oldBlock.Hash
    newBlock.Hash = calculateHash(newBlock)

    return newBlock, nil
}

验证块数据的函数:

// make sure block is valid by checking index,
// and comparing the hash of the previous block
func isBlockValid(newBlock, oldBlock Block) bool {
    if oldBlock.Index+1 != newBlock.Index {
        return false
    }

    if oldBlock.Hash != newBlock.PrevHash {
        return false
    }

    if calculateHash(newBlock) != newBlock.Hash {
        return false
    }

    return true
}

确保各个节点都以最长的链为准:

// make sure the chain we‘re checking is longer than
// the current blockchain
func replaceChain(newBlocks []Block) {
    if len(newBlocks) > len(Blockchain) {
        Blockchain = newBlocks
    }
}

网络通信

接着我们来建立各个节点间的网络,用来传递块、同步链状态等。

我们先来声明一个全局变量 bcServer ,以 channel(译者注:channel 类似其他语言中的 Queue,代码中声明的是一个 Block 数组的 channel)的形式来接受块。

// bcServer handles incoming concurrent Blocks
var bcServer chan []Block

注:Channel 是 Go 语言中很重要的特性之一,它使得我们以流的方式读写数据,特别是用于并发编程。通过这里[2]可以更深入地学习 Channel。

接下来我们声明 main 函数,从 .env 加载配置,也就是端口号,然后实例化 bcServer

 

func main() {
    err := godotenv.Load()
    if err != nil {
        log.Fatal(err)
    }

    bcServer = make(chan []Block)

    // create genesis block
    t := time.Now()
    genesisBlock := Block{0, t.String(), 0, "", ""}
    spew.Dump(genesisBlock)
    Blockchain = append(Blockchain, genesisBlock)
}

接着创建 TCP server 并监听端口:

// start TCP and serve TCP server
    server, err := net.Listen("tcp", ":"+os.Getenv("ADDR"))
    if err != nil {
        log.Fatal(err)
    }
    defer server.Close()

需要注意这里的 defer server.Close(),它用来之后关闭链接,可以从这里[3]了解更多 defer 的用法。

for {
        conn, err := server.Accept()
        if err != nil {
            log.Fatal(err)
        }
        go handleConn(conn)
    }

通过这个无限循环,我们可以接受其他节点的 TCP 链接,同时通过 go handleConn(conn) 启动一个新的 go routine(译者注:Rob Pike 不认为go routine 是协程,因此没有译为协程)来处理请求。

接下来是“处理请求”这个重要函数,其他节点可以创建新的块并通过 TCP 连接发送出来。在这里我们依然像第一篇文章一样,以 BPM 来作为示例数据。

  • 客户端通过 stdin 输入 BPM
  • 以 BPM 的值来创建块,这里会用到前面的函数:generateBlockisBlockValid,和 replaceChain
  • 将新的链放在 channel 中,并广播到整个网络
func handleConn(conn net.Conn) {
    io.WriteString(conn, "Enter a new BPM:")

    scanner := bufio.NewScanner(conn)

    // take in BPM from stdin and add it to blockchain after
    // conducting necessary validation
    go func() {
        for scanner.Scan() {
            bpm, err := strconv.Atoi(scanner.Text())
            if err != nil {
                log.Printf("%v not a number: %v", scanner.Text(), err)
                continue
            }
            newBlock, err := generateBlock(
                                Blockchain[len(Blockchain)-1], bpm)
            if err != nil {
                log.Println(err)
                continue
            }
            if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) {
                newBlockchain := append(Blockchain, newBlock)
                replaceChain(newBlockchain)
            }

            bcServer <- Blockchain
            io.WriteString(conn, "\nEnter a new BPM:")
        }
    }()

    defer conn.Close()
}

我们创建一个 scanner,并通过 for scanner.Scan() 来持续接收连接中发来的数据。为了简化,我们把 BPM 数值转化成字符串。bcServer <- Blockchain 是表示我们将新的链写入 channel 中。

通过 TCP 链接将最新的链广播出去时,我们需要:

  • 将数据序列化成 JSON 格式
  • 通过 timer 来定时广播
  • 在控制台中打印出来,方便我们查看链的最新状态
// simulate receiving broadcast
    go func() {
        for {
            time.Sleep(30 * time.Second)
            output, err := json.Marshal(Blockchain)
            if err != nil {
                log.Fatal(err)
            }
            io.WriteString(conn, string(output))
        }
    }()

    for _ = range bcServer {
        spew.Dump(Blockchain)
    }

整个 handleConn 函数差不多就完成了,通过这里[4]可以获得完整的代码。

有意思的地方

现在让我们来启动整个程序,
go run main.go

 

就像我们预期的,首先创建了“创世块”,接着启动了 TCP server 并监听9000端口。

接着我们打开一个新的终端,连接到那个端口。(我们用不同颜色来区分)
nc localhost 9000

接下来我们输入一个BPM值:

接着我们从第一个终端(节点)中能看到(依据输入的BPM)创建了新的块。

我们等待30秒后,可以从其他终端(节点)看到广播过来的最新的链。

http://www.aibbt.com/a/18709.html

下一步

到目前为止,我们为这个例子添加了简单的、本地模拟的网络能力。当然,肯定有读者觉得这不够有说服力。但本质上来说,这就是区块链的网络层。它能接受外部数据并改变内在数据的状态又能将内在数据的最新状态广播出去。

接下来你需要学习的是一些主流的共识算法,比如 PoW (Proof-of-Work) 和 PoS (Proof-of-Stake) 等。当然,我们会继续在后续的文章中将共识算法添加到这个例子中。

原文地址:https://www.cnblogs.com/5rjscn/p/8535330.html

时间: 2024-11-10 09:50:56

200行Go代码实现自己的区块链——区块生成与网络通信的相关文章

只用200行Go代码写一个自己的区块链!

Coral Health · 大约23小时之前 · 220 次点击 · 预计阅读时间 7 分钟 · 不到1分钟之前 开始浏览 区块链是目前最热门的话题,广大读者都听说过比特币,或许还有智能合约,相信大家都非常想了解这一切是如何工作的.这篇文章就是帮助你使用 Go 语言来实现一个简单的区块链,用不到 200 行代码来揭示区块链的原理!高可用架构也会持续推出更多区块链方面文章,欢迎点击上方蓝色『高可用架构』关注. “用不到200行 Go 代码就能实现一个自己的区块链!” 听起来有意思吗?有什么能比开

200行Python代码实现2048

200行Python代码实现2048 一.实验说明 1. 环境登录 无需密码自动登录,系统用户名shiyanlou 2. 环境介绍 本实验环境采用带桌面的Ubuntu Linux环境,实验中会用到桌面上的程序: LX终端(LXTerminal): Linux命令行终端,打开后会进入Bash环境,可以使用Linux命令 GVim:非常好用的编辑器,最简单的用法可以参考课程Vim编辑器 3. 环境使用 使用GVim编辑器输入实验所需的代码及文件,使用LX终端(LXTerminal)运行所需命令进行操

用200行Python代码“换脸”

介绍 本文将介绍如何编写一个只有200行的Python脚本,为两张肖像照上人物的“换脸”. 这个过程可分为四步: 检测面部标记. 旋转.缩放和转换第二张图像,使之与第一张图像相适应. 调整第二张图像的色彩平衡,使之与第一个相匹配. 把第二张图像的特性混合在第一张图像中. 完整的源代码可以从这里下载: https://github.com/matthewearl/faceswap/blob/master/faceswap.py 1.使用dlib提取面部标记 该脚本使用dlib的Python绑定来提

[老k说区块链]区块链中的共识(1)— 免信任的共识机制

老k,柏链道捷CTO.清华阿尔山区块链研究中心高级工程师,超过17年的系统软件开发经验,在操作系统.编译器.虚拟机和符号执行方面都有实战经验.主持开发多个开眼项目,目前主要从事区块链底层系统开发工作. 这个系列的文章主要谈一下我对区块链中的共识机制的理解,欢迎跟大家一起交流.探讨. 前言 当今区块链的概念和产业已经遍布神州大地,创业言必区块链,在各种咖啡厅中你都可以听到周围的人谈论区块链,大部分从业者对区块链技术的一个认识是它是一个分布式账本技术,更有些人说区块链是各种计算机技术的组合,如P2P

小白眼里的区块链——区块链和比特币的诞生

前言:作者是个刚接触区块链和比特币的小白,很多人都觉得区块链很难懂,但我经过几天的研究,发现区块链的很多东西都可以通过一些通俗易懂的故事来解释,今天开始,我会把个人一些粗浅的见解用连载故事的形式写出来,希望能对很多像我一样的小白有帮助,也希望各位大佬对我的看法提出指正. 很久很久以前,有个村子,叫做比特村,村子里面有个德高望重的老村长,名叫中本笨,我们姑且叫他老笨,老笨呢因为以前读过几天书,会记账,再加上一贯的口碑不错,所以大家要买卖点啥东西,都到老笨家里去交易,由老笨来定双方物品的交换比例,比

区块链技术--区块链的生成和链接

1.区块的生成 (1)矿工在挖矿前要组建区块,将coinbase交易打包进区块 (2)将交易池中高优先级的交易打包进区块 优先级=交易的额度 * UTXO的深度/交易的size (3) 创建区块的头部[版本号,父区块哈希,Merkle树根,时间戳,难度值,Nonce] (4) 挖矿成功后,将计算出来的随机数nonce填入到区块头部,向邻近节点传播 2.区块的验证链接 (1)相邻节点收到新区块后,会立即验证这个区块 3.Merkle Tree结构 (1)防止数据篡改 (2)快速验证某个交易是否存在

[币严BIZZAN区块链]Java生成ETH钱包助记词、私钥、地址

本文主要介绍在Java工程中如何生成ETH钱包的助记词.私钥.地址. 一.在之前创建的spring boot 项目中的 pom.xml文件中加入需要的依赖 <dependency> <groupId>org.bitcoinj</groupId> <artifactId>bitcoinj-core</artifactId> <version>0.14.7</version> </dependency> <d

用不到 50 行的 Python 代码构建最小的区块链

引用 译者注:随着比特币的不断发展,它的底层技术区块链也逐步走进公众视野,引起大众注意.本文用不到50行的Python代码构建最小的数据区块链,简单介绍了区块链去中心化的结构与其实现原理. 尽管一些人认为区块链是一个等待问题的解决方案,但毫无疑问,这种新技术是计算机的奇迹.但是,区块链到底是什么呢? 区块链 它是比特币或其他加密货币进行交易的数字账本,账本按时间顺序记录并对外公开. 在更一般的术语中,它是一个公共数据库,新数据存储在一个名为块的容器中,并被添加到一个不可变链(后来的区块链)中添加

40多行python代码开发一个区块链。

40多行python代码开发一个区块链?可信吗?我们将通过Python 2动手开发实现一个迷你区块链来帮你真正理解区块链技术的核心原理.python开发区块链的源代码保存在Github. 尽管有人认为区块链目前还是不成熟的解决方案,但它无疑是计算机发展史上的一个奇迹.但是,到底区块链是什么呢? 区块链 区块链是一个公开的数字账本,它按时间顺序记录比特币或其他加密货币发生的交易. 更一般的讲,区块链是一个公共数据库,新的数据将存储在一个被称为"块"的容器中,然后块会被添加到一个不可篡改的