美文网首页
使用go语言实现简易版的区块链

使用go语言实现简易版的区块链

作者: 吃馍夹菜 | 来源:发表于2022-06-23 12:07 被阅读0次

    使用go语言实现简易版的区块链

    区块链概念

    区块链(Blockchain),是比特币的一个重要概念,它本质上是一个去中心化的数据库,同时作为比特币的底层技术,是一串使用密码学方法相关联产生的数据块,每一个数据块中包含了一批次比特币网络交易的信息,用于验证其信息的有效性(防伪)和生成下一个区块。(百度百科)

    区块里的数据

    // 块数据
    type Block struct {
        Hash        string // 当前块的hash
        BlockNumber int64  // 当前块的高度,是递增的
        PreHash     string // 上个区块的hash
        Timestamp   int64  // 生成当前区块的高度
        Data        string // 区块交易(上链数据) 这里应该是slice,Data里会有很多笔的交易记录
    }
    

    其中hash和preHash是区块 “链”起来的关键。这里hash使用了sha256(所有的参数拼接成字符串)。

    生成hash算法

    hash具有唯一性,是区块的唯一标识。

    // 生成hash
    func generateHash(b Block) string {
        str := fmt.Sprintf("%d,%d,%s,%s", b.BlockNumber, b.Timestamp, b.PreHash, b.Data) // 简单的拼接。实际区块链生成hash比这要复杂的多
        hash := sha256.New()
        hash.Write([]byte(str))
        bytes := hash.Sum(nil)
        return hex.EncodeToString(bytes)
    }
    

    创世区块

    创世块(Genesis Block)作为比特币区块链的第一个区块是设定为不可交易的。与其他包含交易的区块链中不同,创世纪区块不包含任何交易,该区块中的币也不能用于交易中。

    // 创建创世区块
    func genesisBlock() Block {
        b := Block{}
        b.BlockNumber = 0
        b.PreHash = ""
        b.Timestamp = time.Now().Unix()
        b.Data = ""
        b.Hash = generateHash(b)
        return b
    }
    

    生成区块

    使用上个区块的数据,和当前区块的交易数据构建成新的区块数据。
    注意,必须用上一个区块的哈希值来初始化本区块的previousHash,否则区块之间的联系就断了。
    假如我们修改创世块的信息,那么所有的区块的hash值都会发生很大的变化,这就是为什么在区块上作弊是非常困难的一件事情。

    // 创建区块
    func generateBlock(oldBlock Block, data string) Block {
        var b Block
        b.BlockNumber = oldBlock.BlockNumber + 1
        b.PreHash = oldBlock.Hash
        b.Timestamp = time.Now().Unix()
        b.Data = data
        b.Hash = generateHash(b)
        return b
    }
    

    到这里所有关于区块的部分就已经结束了。接下来就是如何把这些区块给串起来,形成链式结构了。

    区块的 “链”

    我们创建的是一个非常简单基础的链,很显然,既然对于每个节点,我们都能计算出一个hash值,并且这个hash值是唯一的。那么们就可以通过块的hash值来定位到这个块的后一个节点是谁,因为我们每个块除了包含交易信息以外,还保存了上一个区块的hash值。
    使用这些块组合在一起就成一个 就形成了 区块链

    // 区块"链"
    type BlockChain struct {
        ChainData []Block
    }
    

    验证新增区块是否合法

    通过上面的介绍,我们知道,块与块之间是通过 hash 来关联的,而块的高度是递增的。以及新增的块的数据,重新计算hash,判断和自身hash是否一致。
    至此我们就能去验证新增的区块是否合法

    
    // 数据区块数据是否合法
    func (bc *BlockChain) verifyBlock(newBlock Block, oldBlock Block) bool {
        // 对比前后高度是否一致
        if newBlock.BlockNumber != oldBlock.BlockNumber+1 {
            return false
        }
        // 对比前后hash是否一致
        if newBlock.PreHash != oldBlock.Hash {
            return false
        }
    
        // 对比生成的hash是否一致
        if newBlock.Hash != generateHash(newBlock) {
            return false
        }
        return true
    }
    
    

    新 “块” 上链

    拿到新的交易数据data,并对数据进行打包,得到newBlock, 并对新旧区块进行验证。

    // 数据上链
    func (bc *BlockChain) uploadChain(data string) bool {
        num := len(bc.ChainData)
        if num == 0 { // 还未产生块
            block := genesisBlock()
            bc.ChainData = append(bc.ChainData, block)
        } else {
            oldBlock := bc.ChainData[len(bc.ChainData)-1]
            var newBlock = generateBlock(oldBlock, data)
            if bc.verifyBlock(newBlock, oldBlock) {
                bc.ChainData = append(bc.ChainData, newBlock)
            } else {
                return false
            }
        }
        return true
    }
    

    打印出整个区块链的信息

    // 输出区块数据记录
    func (bc *BlockChain) getChainData() {
        for _, v := range bc.ChainData {
            fmt.Println("hash:", v.Hash)
            fmt.Println("blockNum:", v.BlockNumber)
            fmt.Println("timestamp:", v.Timestamp)
            fmt.Println("preHash:", v.PreHash)
            fmt.Println("data:", v.Data)
            fmt.Println("-----------------分割线------------------")
        }
    }
    

    测试

    func TestBlockChain(t *testing.T) {
        blockchain := BlockChain{}
        for i := 0; i < 5; i++ {
            b := blockchain.uploadChain("上链数据:" + strconv.Itoa(i))
            if !b {
                fmt.Println("验证区块错误")
                return
            }
            time.Sleep(time.Second * 1)
        }
        // 打印链数据
        blockchain.getChainData()
    }
    

    输出结果:

    === RUN   TestBlockChain
    hash: 266cd15c59789786be91873464938cb955ed24a1b5133742fccee6e964ae1bf2
    blockNum: 0
    timestamp: 1655955949
    preHash: 
    data: genesis block data
    -----------------分割线------------------
    hash: 9d9f8fe10611eaa153f22c2874fe97eef57fe6d581a94c2821e5f53ef252abba
    blockNum: 1
    timestamp: 1655955950
    preHash: 266cd15c59789786be91873464938cb955ed24a1b5133742fccee6e964ae1bf2
    data: 上链数据:1
    -----------------分割线------------------
    hash: fd6355fd343ed65300726002b7a17bc1edaeb904adff9661106158625f09d4df
    blockNum: 2
    timestamp: 1655955951
    preHash: 9d9f8fe10611eaa153f22c2874fe97eef57fe6d581a94c2821e5f53ef252abba
    data: 上链数据:2
    -----------------分割线------------------
    hash: a46b1e05464c191b7dab8d2d00048a926562f474b2bf6fe8ac313e9220c65165
    blockNum: 3
    timestamp: 1655955952
    preHash: fd6355fd343ed65300726002b7a17bc1edaeb904adff9661106158625f09d4df
    data: 上链数据:3
    -----------------分割线------------------
    hash: e782e286cc58e49525592e86c89fab3fbb0ccb160f8c674dc7a25070f77a2370
    blockNum: 4
    timestamp: 1655955953
    preHash: a46b1e05464c191b7dab8d2d00048a926562f474b2bf6fe8ac313e9220c65165
    data: 上链数据:4
    -----------------分割线------------------
    

    相关文章

      网友评论

          本文标题:使用go语言实现简易版的区块链

          本文链接:https://www.haomeiwen.com/subject/kxopvrtx.html