go语言简单实现区块链

这篇具有很好参考价值的文章主要介绍了go语言简单实现区块链。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

        在本文中,我们将使用Go语言来实现一个简单的区块链系统,包括区块生成、交易处理和区块打印、保存区块链等功能。

先决条件

在开始之前,先确保计算机上安装了以下内容:

  1. Go编程语言:您可以从Go官方网站下载并安装Go。
  2. Go开发环境:设置Go开发环境,包括文本编辑器或集成开发环境(IDE)。

区块链基础

        在深入了解代码实现之前,让我们快速了解一些区块链的基本概念。

区块

        区块链由一系列区块组成,每个区块包含一组交易和其他元数据。每个区块都有一个唯一的哈希值,它表示区块的内容。区块还包含前一个区块的哈希值,从而形成一个链条,每个区块都依赖于前一个区块,确保数据的完整性和安全性。

交易

        交易是区块链中的基本操作。交易可以代表各种类型的操作,例如资金转移、资产交换或智能合约执行。交易包含发送者、接收者和交易金额等信息。本文采取简易的交易,不包含任何信息

哈希函数

        哈希函数用于将任意长度的数据转换为固定长度的哈希值。在区块链中,我们使用哈希函数来计算每个区块的哈希值,确保数据未被篡改。常见的哈希函数包括 SHA-256、RIPEMD-160 等。

共识机制

        共识机制确保区块链中的所有节点对交易和区块达成一致。常见的共识机制包括工作量证明(Proof-of-Work)和权益证明(Proof-of-Stake)。在本教程中,我们控制区块生成时间来生成新区快,不涉及共识机制,读者可根据实际进行功能拓展。

代码实现

        让我们开始实现我们的区块链系统。我们将使用 Go 语言来编写代码。

区块结构

        首先,我们定义一个 Block 结构来表示区块。每个区块包含索引、时间戳、交易列表、前一个区块的哈希值和当前区块的哈希值。

// Block 表示区块结构
type Block struct {
        Index     int      // 区块索引
        Timestamp string   // 时间戳
        Txs       []string // 交易
        PrevHash  string   // 前一个区块的哈希值
        Hash      string   // 当前区块的哈希值
}

定义全局变量 

// 先把新区块放入一个map中,最后一起存入redis
var blockMap = make(map[string]string)
var blockchain []Block

         blockMap 是一个用于临时存储新区块的 map,稍后我们将把这些新区块保存到本地文件中。

初始化区块链

        我们需要一个函数来初始化区块链。如果这是第一次运行程序,我们将创建一个创世区块(genesis block)。如果之前已经运行过,我们将从文件中加载现有区块链。

func initBlock() {
        fmt.Println("初始化")
        file, err := os.OpenFile("Block.txt", os.O_RDWR|os.O_CREATE, 0766)
        if err != nil {
                fmt.Println("文件打开失败")
                return
        }
        defer file.Close()

        scanner := bufio.NewScanner(file)
        for scanner.Scan() {
                line := scanner.Text()
                parts := strings.Split(line, ":")
                if len(parts) != 2 {
                        continue
                }
                value := parts[1]
                oldBlock := Block{}
                err := json.Unmarshal([]byte(value), &oldBlock)
                if err != nil {
                        continue
                }
                blockchain = append(blockchain, oldBlock)
        }

        if len(blockchain) == 0 {
                genesisBlock := CreateGenesisBlock()
                blockchain = append(blockchain, genesisBlock)
        }
}

创建创世区块

        创世区块是区块链中的第一个区块。它没有前一个区块的哈希值,因此我们将其设置为空字符串。

// 创建创世区块
func CreateGenesisBlock() Block {
	// 创建创世区块
	genesisBlock := Block{0, time.Now().String(), []string{}, "", ""}
	genesisBlock.Hash = calculateHash(genesisBlock)
	// 将新区块保存到临时的map中
	blockJSON, _ := json.Marshal(genesisBlock)
	blockMap[genesisBlock.Hash] = string(blockJSON)
	return genesisBlock
}

        在上面的代码中,我们首先初始化创世区块的属性,包括索引(0)、当前时间戳、空的交易列表、空的前一个区块哈希值和空的当前区块哈希值。

        接下来,我们调用 calculateHash 函数来计算创世区块的哈希值。这个函数将使用 SHA-256 哈希函数对区块的内容进行哈希计算。我们稍后将详细讨论这个函数。

        计算出哈希值后,我们将创世区块转换为 JSON 格式并保存到 blockMap 中。blockMap 是一个用于临时存储新区块的 map,稍后我们将把这些新区块保存到文件中。

        最后,我们返回创世区块。

生成区块

        现在我们已经有了创世区块,让我们来看看如何生成新的区块。我们将创建一个名为 generateBlock 的函数,它接受前一个区块和交易列表作为参数。

// 生成新的区块
func generateBlock(oldBlock Block, txs []string) Block {
    var newBlock Block
    newBlock.Index = oldBlock.Index + 1
    newBlock.Timestamp = time.Now().String()
    newBlock.Txs = txs
    newBlock.PrevHash = oldBlock.Hash
    newBlock.Hash = calculateHash(newBlock)
    return newBlock
}

        在这个函数中,我们首先初始化新区块的属性。索引设置为前一个区块的索引加 1,时间戳设置为当前时间,交易列表设置为传入的交易列表,前一个区块的哈希值设置为传入的前一个区块的哈希值。然后,我们再次调用 calculateHash 函数来计算新区块的哈希值。

计算哈希值

        现在让我们来看看 calculateHash 函数是如何计算区块的哈希值的。

// 计算区块的哈希值
func calculateHash(block Block) string {
    // 计算哈希值
    record := strconv.Itoa(block.Index) + block.Timestamp + fmt.Sprint(block.Txs) + block.PrevHash
    h := sha256.New()
    h.Write([]byte(record))
    hashed := h.Sum(nil)
    // 将[]byte转换成16进制字符串
    return hex.EncodeToString(hashed)
}

         在这个函数中,我们首先将区块的索引、时间戳、交易列表和前一个区块的哈希值连接起来,形成一个字符串。然后,我们使用 SHA-256 哈希函数来计算这个字符串的哈希值。

   sha256.New() 函数创建一个新的 SHA-256 哈希对象。h.Write([]byte(record)) 将输入字符串转换为字节数组并写入哈希对象。h.Sum(nil) 计算最终的哈希值并返回字节数组。最后,我们使用 hex.EncodeToString(hashed) 将字节数组转换为 16 进制字符串,这就是区块的哈希值。

生成交易

        在区块链系统中,交易是基本的操作单元。让我们创建一个简单的函数来生成随机交易。

// 随机生成交易
func generateTx() string {
    return fmt.Sprintf("Tx%d", rand.Intn(1000))
}

        在这个函数中,我们使用 rand.Intn(1000) 生成一个随机数,并将其格式化为 "Tx" 开头的字符串。这个函数可以根据需要生成多个随机交易。

打包交易放入区块

        让我们继续讨论 productBlock 函数。这个函数负责打包交易生成区块并将其添加到区块链中。

// 生成区块
func productBlock(timeTX int, txPool []string, blockchain []Block) {
    for {
        // 生成随机交易并放入交易池
        for i := 0; i < 10; i++ {
            tx := generateTx()
            txPool = append(txPool, tx)
        }

        // 从交易池中取出交易打包到区块中
        var blockTxs []string
        if len(txPool) > 8 {
            blockTxs = txPool[:8]
            txPool = txPool[8:]
        } else {
            blockTxs = txPool
            txPool = []string{}
        }

        // 生成新区块并添加到区块链
        newBlock := generateBlock(blockchain[len(blockchain)-1], blockTxs)
        blockchain = append(blockchain, newBlock)

        // 将新区块保存到临时的map中
        blockJSON, _ := json.Marshal(newBlock)
        blockMap[newBlock.Hash] = string(blockJSON)

        // 等待指定的时间间隔
        time.Sleep(time.Duration(timeTX) * time.Second)
    }
}

        在这个函数中,我们首先使用 generateTx 函数生成随机交易并将其放入交易池 txPool 中。我们生成 10 个随机交易,但您可以根据需要调整这个数字。

        然后,我们从交易池中取出交易打包到区块中。我们限制每个区块最多包含 8 个交易,因此如果交易池中有超过 8 个交易,我们只取前 8 个,并将剩余的交易留在交易池中供下一个区块使用。

        接下来,我们调用 generateBlock 函数来生成新区块。这个函数接受前一个区块和交易列表作为参数,并返回一个新的区块。我们将新区块添加到区块链 blockchain 中。

然后,我们将新区块转换为 JSON 格式并保存到 blockMap 中。blockMap 是一个用于临时存储新区块的 map,稍后我们将把这些新区块保存到文件中。我们使用 time.Sleep 函数等待指定的时间间隔,以模拟区块的生成间隔。

        这个函数将一直运行,不断生成新的区块并将其添加到区块链中,可以根据需要调整区块生成的 时间间隔。

打印区块链

        让我们继续讨论 printBlock 和 printBlockchain 函数。这些函数负责打印单个区块和整个区块链的信息。

// 打印区块
func printBlock(block Block) {
    fmt.Printf("Index:%d\n", block.Index)
    fmt.Printf("Timestamp:%s\n", block.Timestamp)
    fmt.Printf("Txs:%v\n", block.Txs)
    fmt.Printf("PrevHash:%s\n", block.PrevHash)
    fmt.Printf("Hash:%s\n", block.Hash)
    fmt.Println()
}

    printBlock 函数接受一个 Block 类型的参数,并使用 fmt.Printf 函数打印区块的各个属性,包括索引、时间戳、交易列表、前一个区块的哈希值和当前区块的哈希值。每个属性打印在一行,并以换行符结束。

// 打印区块链
func printBlockchain(blockMap map[string]string) {
    var blocks []Block
    for _, value := range blockMap {
        block := Block{}
        err := json.Unmarshal([]byte(value), &block)
        if err != nil {
            continue
        }
        blocks = append(blocks, block)
    }

    // 对区块按照索引排序
    sort.Slice(blocks, func(i, j int) bool {
        return blocks[i].Index < blocks[j].Index
    })

    for _, block := range blocks {
        printBlock(block)
    }
}

   printBlockchain 函数负责打印整个区块链的信息。它首先创建一个空的 Block 切片 blocks 来存储从 blockMap 中解码的区块。blockMap 是一个用于临时存储新区块的 map,每个区块的哈希值作为键,对应的 JSON 格式的区块作为值。

        我们使用 json.Unmarshal 函数将 JSON 格式的区块解码为 Block 类型。如果解码失败,我们将跳过该区块并继续处理下一个。

        然后,我们使用 sort.Slice 对区块按照索引排序。sort.Slice 函数接受一个排序函数,在这个函数中,我们比较两个区块的索引,如果第一个区块的索引小于第二个区块,则返回 true,表示第一个区块应该排在前面。

        最后,我们遍历排序后的区块切片,并调用 printBlock 函数打印每个区块的信息。

保存区块链

        让我们继续讨论 saveBlock 函数。这个函数负责将区块保存到本地文件中。

// 保存区块到本地Block.txt文件下
func saveBlock(blockmap map[string]string) {
	//打开文件,没有则创建
	file, err := os.OpenFile("Block.txt", os.O_RDWR|os.O_CREATE, 0766)
	if err != nil {
		fmt.Println("文件打开失败")
		return

	}
	defer file.Close()
	for key, value := range blockmap {
		fmt.Println("key:", key, "value:", value)
		//写入文件
		_, err := file.WriteString(key + ":" + value + "\n")
		if err != nil {
			return
		}
	}
}

        在这个函数中,我们首先使用 os.OpenFile 函数打开或创建名为 "Block.txt" 的文件。我们使用 os.O_RDWR|os.O_CREATE 标志来指定文件可以读写,如果文件不存在则创建。

        然后,我们使用 defer file.Close() 确保文件在函数结束时关闭。

        接下来,我们遍历 blockMap 中的所有键值对。blockMap 是一个用于临时存储新区块的 map,每个区块的哈希值作为键,对应的 JSON 格式的区块作为值。

        对于每个键值对,我们打印键和值,并使用 file.WriteString 将它们写入文件。我们使用 key + ":" + value + "\n" 来确保每个区块以 "键:值" 的格式写入新的一行。

如果写入文件时发生错误,我们将返回并终止函数。

        这个函数确保新区块被保存到本地文件中,以便下次程序运行时可以从文件中加载现有区块链。

完整代码

        以下是本教程中讨论的所有代码的完整版本:

// Package Block
// -*- coding: utf-8 -*-
// Time    : 2024/4/12 20:13
// Author  : blue
// File    : main.go
// Software: Goland
package main

import (
	"bufio"
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"math/rand"
	"os"
	"sort"
	"strconv"
	"strings"
	"time"
)

// Block 表示区块结构
type Block struct {
	Index     int      // 区块索引
	Timestamp string   // 时间戳
	Txs       []string // 交易
	PrevHash  string   // 前一个区块的哈希值
	Hash      string   // 当前区块的哈希值
}

// 先把新区块放入一个map中,最后一起存入redis
var blockMap = make(map[string]string)
var blockchain []Block

func main() {

	initBlock()
	// 交易池
	txPool := []string{}
	loop := true

	for {
		fmt.Println("------区块链系统demo------")
		fmt.Println("-----1. 添加区块-----")
		fmt.Println("-----2. 打印区块-----")
		fmt.Println("-----3. 保存区块-----")
		fmt.Println("-----4. 退出-----")
		fmt.Println("请输入您的选择:")
		var choice int
		fmt.Scanln(&choice)
		switch choice {
		case 1:
			fmt.Println("请输入生成区块的间隔:")
			timeTX := 0
			fmt.Scanln(&timeTX)
			go productBlock(timeTX, txPool, blockchain)
		case 2:
			fmt.Println("打印区块")
			printBlockchain(blockMap)

		case 3:
			fmt.Println("保存区块")
			saveBlock(blockMap)
		case 4:
			fmt.Println("退出")
			loop = false
		}
		if loop == false {
			break
		}
	}

}

// 初始化区块
func initBlock() {
	fmt.Println("初始化")
	file, err := os.OpenFile("Block.txt", os.O_RDWR|os.O_CREATE, 0766)
	if err != nil {
		fmt.Println("文件打开失败")
		return
	}
	defer file.Close()

	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		line := scanner.Text()
		parts := strings.Split(line, ":")
		if len(parts) != 2 {
			continue
		}
		value := parts[1]
		oldBlock := Block{}
		err := json.Unmarshal([]byte(value), &oldBlock)
		if err != nil {
			continue
		}
		blockchain = append(blockchain, oldBlock)
	}

	if len(blockchain) == 0 {
		genesisBlock := CreateGenesisBlock()
		blockchain = append(blockchain, genesisBlock)
	}
}

// 打印区块
func printBlock(block Block) {
	fmt.Printf("Index:%d\n", block.Index)
	fmt.Printf("Timestamp:%s\n", block.Timestamp)
	fmt.Printf("Txs:%v\n", block.Txs)
	fmt.Printf("PrevHash:%s\n", block.PrevHash)
	fmt.Printf("Hash:%s\n", block.Hash)
	fmt.Println()

}

// 打印区块链
func printBlockchain(blockMap map[string]string) {
	var blocks []Block
	for _, value := range blockMap {
		block := Block{}
		err := json.Unmarshal([]byte(value), &block)
		if err != nil {
			continue
		}
		blocks = append(blocks, block)
	}
	// 对区块按照索引排序
	sort.Slice(blocks, func(i, j int) bool {
		return blocks[i].Index < blocks[j].Index
	})

	for _, block := range blocks {
		printBlock(block)
	}
}

// 保存区块到本地Block.txt文件下
func saveBlock(blockmap map[string]string) {
	//打开文件,没有则创建
	file, err := os.OpenFile("Block.txt", os.O_RDWR|os.O_CREATE, 0766)
	if err != nil {
		fmt.Println("文件打开失败")
		return

	}
	defer file.Close()
	for key, value := range blockmap {
		fmt.Println("key:", key, "value:", value)
		//写入文件
		_, err := file.WriteString(key + ":" + value + "\n")
		if err != nil {
			return
		}
	}
}

// 创建创世区块
func CreateGenesisBlock() Block {
	// 创建创世区块
	genesisBlock := Block{0, time.Now().String(), []string{}, "", ""}
	genesisBlock.Hash = calculateHash(genesisBlock)
	// 将新区块保存到临时的map中
	blockJSON, _ := json.Marshal(genesisBlock)
	blockMap[genesisBlock.Hash] = string(blockJSON)
	return genesisBlock
}

// 生成区块
func productBlock(timeTX int, txPool []string, blockchain []Block) {
	for {
		// 生成随机交易并放入交易池
		for i := 0; i < 10; i++ {
			tx := generateTx()
			txPool = append(txPool, tx)
		}

		// 从交易池中取出交易打包到区块中
		var blockTxs []string
		if len(txPool) > 8 {
			blockTxs = txPool[:8]
			txPool = txPool[8:]
		} else {
			blockTxs = txPool
			txPool = []string{}
		}

		// 生成新区块并添加到区块链
		newBlock := generateBlock(blockchain[len(blockchain)-1], blockTxs)
		blockchain = append(blockchain, newBlock)

		// 将新区块保存到临时的map中
		blockJSON, _ := json.Marshal(newBlock)
		blockMap[newBlock.Hash] = string(blockJSON)

		// 等待指定的时间间隔
		time.Sleep(time.Duration(timeTX) * time.Second)
	}
}

// 计算区块的哈希值
func calculateHash(block Block) string {
	// 计算哈希值
	record := strconv.Itoa(block.Index) + block.Timestamp + fmt.Sprint(block.Txs) + block.PrevHash
	h := sha256.New()
	h.Write([]byte(record))
	hashed := h.Sum(nil)
	//hex.EncodeToString(hashed)将[]byte转换成16进制字符串
	return hex.EncodeToString(hashed)
}

// 生成新的区块
func generateBlock(oldBlock Block, txs []string) Block {
	var newBlock Block
	newBlock.Index = oldBlock.Index + 1
	newBlock.Timestamp = time.Now().String()
	newBlock.Txs = txs
	newBlock.PrevHash = oldBlock.Hash
	newBlock.Hash = calculateHash(newBlock)
	return newBlock
}

// 随机生成交易
func generateTx() string {
	return fmt.Sprintf("Tx%d", rand.Intn(1000))
}

总结

        在本教程中,我们使用 Go 语言实现了一个简单的区块链系统。我们讨论了区块链的基本概念,包括区块、交易和哈希函数。我们还实现了生成区块、交易和计算哈希值所需的函数。最后,我们创建了一个主函数来组合所有内容并运行区块链系统。

        虽然这个实现是基本的,但它为理解区块链技术提供了很好的基础。您可以在此基础上继续构建,添加更多的功能,例如共识机制、智能合约和去中心化网络。

        感谢阅读,希望这篇教程对您有所帮助!文章来源地址https://www.toymoban.com/news/detail-853364.html

到了这里,关于go语言简单实现区块链的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【Golang】VsCode下开发Go语言的环境配置(超详细图文详解)

    📓推荐网站(不断完善中):个人博客 📌个人主页:个人主页 👉相关专栏:CSDN专栏、个人专栏 🏝立志赚钱,干活想躺,瞎分享的摸鱼工程师一枚 ​ 话说在前,Go语言的编码方式是 UTF-8 ,理论上你直接使用文本进行编辑也是可以的,当然为了提升我们的开发效率我们还是需

    2024年02月07日
    浏览(85)
  • GO语言-区块链离线钱包开发之如何存储私钥

    # 如何存储私钥 在确保私钥安全的情况下,为了更好的体验,我们需要让钱包把私钥存储起来。给用户更好的体验感。Geth是将私钥通过加密技术转换为json格式的文件,这个文件虽然是明文的,但是解析它的时候需要密码,否则将无法解密。 在Geth中,使用`personal.newAccount(\\\"p

    2024年02月16日
    浏览(53)
  • 【go语言开发】redis简单使用

    本文主要介绍redis安装和使用。首先安装redis依赖库,这里是v8版本;然后连接redis,完成基本配置;最后测试封装的工具类 欢迎大家访问个人博客网址:https://www.maogeshuo.com,博主努力更新中… 参考文件: Yaml文件配置,Config使用 Log日志封装 常用工具类封装 命令行安装redis

    2024年03月12日
    浏览(60)
  • GO语言实现区块链POW共识算法- -区块定义与数据串行化

    持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第9天,点击查看活动详情 区块链分布式系统,共识算法系统是它的灵魂,pow也就是工作量证明,证明你做过一定量的工作。(按劳分配,拼算力) 在我们实现pow之前,需要对区块链的基本架子先搭起来(相当

    2024年02月08日
    浏览(40)
  • 【Go语言开发】简单了解一下搜索引擎并用go写一个demo

    这篇文章我们一起来了解一下搜索引擎的原理,以及用go写一个小demo来体验一下搜索引擎。 搜索引擎一般简化为三个步骤 爬虫:爬取数据源,用做搜索数据支持。 索引:根据爬虫爬取到的数据进行索引的建立。 排序:对搜索的结果进行排序。 然后我们再对几个专业名词做

    2024年02月16日
    浏览(44)
  • go语言从0基础到安全项目开发实战

    搭建环境比较简单 到以下链接下 Go下载 - Go语言中文网 - Golang中文社区 下载windows版本64位zip包 https://studygolang.com/dl/golang/go1.20.7.windows-amd64.zip 不配置的话就只能在bin目录下才能运行go命令 创建test.go文件 然后代码如下 编译运行  两种方式编译运行代码 1.先 go build test.go编译成

    2024年02月13日
    浏览(47)
  • 【区块链技术开发语言】在ubuntu18 系统环境下命令操作配置以太坊go-ethereum环境

    项目简介: 以太坊是一个基于区块链技术的分布式平台,用于构建去中心化应用程序(DApps)。go-ethereum 是以太坊官方开发团队维护的 Go 语言实现的以太坊客户端,也被称为 Geth。它提供了一个完整的以太坊节点,用于参与以太坊网络,执行智能合约,进行交易等。 前提条件

    2024年02月21日
    浏览(45)
  • Go 语言实现冒泡排序算法的简单示例

    以下是使用 Go 语言实现冒泡排序算法的简单示例: 在这个例子中, bubbleSort 函数接收一个整数切片,对切片中的元素进行冒泡排序。在 main 函数中,我们定义了一个示例数组,调用 bubbleSort 函数对其进行排序,并输出结果。 注意,冒泡排序算法的时间复杂度为 O(n^2),因此对

    2024年01月23日
    浏览(46)
  • 【设计模式】使用 go 语言实现简单工厂模式

    最近在看《大话设计模式》,这本书通过对话形式讲解设计模式的使用场景,有兴趣的可以去看一下。 第一篇讲的是 简单工厂模式 ,要求输入两个数和运算符号,得到运行结果。 这个需求不难,难就难在类要怎么设计,才能达到可复用、维护性强、可拓展和灵活性高。 运

    2024年02月05日
    浏览(48)
  • Go 语言实现归并排序算法的简单示例(附上源码)

    以下是使用 Go 语言实现归并排序算法的简单示例: 在这个例子中, mergeSort 函数接收一个整数切片,使用递归的方式进行归并排序。 merge 函数用于合并两个已排序的切片。在 main 函数中,我们定义了一个示例数组,调用 mergeSort 函数对其进行排序,并输出结果。 归并排序算

    2024年01月21日
    浏览(45)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包