使用GO语言实现区块链网络连接功能

原文:《Part 2: Networking — Code your own blockchain in less than 200 lines of Go!

如果你还没有读《200行go代码实现区块链》可以先读一下,以下内容以其为基础。

本文仅是模拟节点广播区块链数据到其他节点,可以在此基础上进行一些修改使其真正地成为节点互联。

流程

第一个终端负责产生创世块,并且作为TCP服务器监听与其他节点建立连接
第一个终端负责产生创世块,并建立TCP服务接受其他节点的连接请求。

  1. 新开一个终端,与第一个终端建立TCP连接,并向块中写入一个数据
  2. 第一个终端记录区块数据,并将区块给其他所有连接进行广播。
  3. 所有已连接的终端同步到区块链。

开始

(原文还有一些关于HTTP 和TCP的区别,这里就不翻译了。)
.env文件内容ADDR=9000
新建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"
)

复习

我们将《200行go代码实现区块链》部分可用代码复制到该文件中。

// 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
    }
}

网络连接

现在进入本文的关键,首先我们定义一个全局的chan

var bcServer chan []Block

接下来我们需要定义main方法
首先我们有如下main方法

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服务、以及处理连接的相关方法。

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

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

最终 main方法如下

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)

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

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

}

到现在为止我们还差一个方法就是handleConn 用于处理TCP连接

func handleConn(conn net.Conn) {
    defer conn.Close()
}

首先,终端连接到服务后,会提示终端输入其心跳数据(PS:本区块链是用于记录自己的心跳数据)
服务器接收到心跳数据后,生成一个新块,并将该块加入到区块链中,最后将新生成的区块链写入chan中。代码如下:

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:")
        }
    }()

广播新生成的区块链(原文代码中并没有使用chan ,而是直接使用Blockchain进行多线程之间的数据共享,这没有体现chan的使用,我将在以后的代码中进行改进)


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)
    }

Ok 至此所有的代码就完成了。可以开始测试了。

测试

我们开启三个终端,第一个终端我们启动我们的服务go run main.go 另外两个终端使用nc命令连接该服务nc localhost 5000 然后我们在第2个终端中输入50 我们可以立即在第一个终端中看到新生成的区块数据,等待30秒后(模拟节点挖矿生成新块需要一定时间)可以在第三个终端看到新的区块链数据

# 英文原文
英文原文的内容比我说的丰富详细多了,建议大伙儿阅读
Part 2_ Networking — Code your own blockchain in less than 200 lines of Go!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值