????????在本文中,我們將使用Go語言來實現(xiàn)一個簡單的區(qū)塊鏈系統(tǒng),包括區(qū)塊生成、交易處理和區(qū)塊打印、保存區(qū)塊鏈等功能。
先決條件
在開始之前,先確保計算機上安裝了以下內(nèi)容:
- Go編程語言:您可以從Go官方網(wǎng)站下載并安裝Go。
- Go開發(fā)環(huán)境:設(shè)置Go開發(fā)環(huán)境,包括文本編輯器或集成開發(fā)環(huán)境(IDE)。
區(qū)塊鏈基礎(chǔ)
????????在深入了解代碼實現(xiàn)之前,讓我們快速了解一些區(qū)塊鏈的基本概念。
區(qū)塊
????????區(qū)塊鏈由一系列區(qū)塊組成,每個區(qū)塊包含一組交易和其他元數(shù)據(jù)。每個區(qū)塊都有一個唯一的哈希值,它表示區(qū)塊的內(nèi)容。區(qū)塊還包含前一個區(qū)塊的哈希值,從而形成一個鏈條,每個區(qū)塊都依賴于前一個區(qū)塊,確保數(shù)據(jù)的完整性和安全性。
交易
????????交易是區(qū)塊鏈中的基本操作。交易可以代表各種類型的操作,例如資金轉(zhuǎn)移、資產(chǎn)交換或智能合約執(zhí)行。交易包含發(fā)送者、接收者和交易金額等信息。本文采取簡易的交易,不包含任何信息
哈希函數(shù)
????????哈希函數(shù)用于將任意長度的數(shù)據(jù)轉(zhuǎn)換為固定長度的哈希值。在區(qū)塊鏈中,我們使用哈希函數(shù)來計算每個區(qū)塊的哈希值,確保數(shù)據(jù)未被篡改。常見的哈希函數(shù)包括 SHA-256、RIPEMD-160 等。
共識機制
????????共識機制確保區(qū)塊鏈中的所有節(jié)點對交易和區(qū)塊達(dá)成一致。常見的共識機制包括工作量證明(Proof-of-Work)和權(quán)益證明(Proof-of-Stake)。在本教程中,我們控制區(qū)塊生成時間來生成新區(qū)快,不涉及共識機制,讀者可根據(jù)實際進(jìn)行功能拓展。
代碼實現(xiàn)
????????讓我們開始實現(xiàn)我們的區(qū)塊鏈系統(tǒng)。我們將使用 Go 語言來編寫代碼。
區(qū)塊結(jié)構(gòu)
????????首先,我們定義一個?Block
?結(jié)構(gòu)來表示區(qū)塊。每個區(qū)塊包含索引、時間戳、交易列表、前一個區(qū)塊的哈希值和當(dāng)前區(qū)塊的哈希值。
// Block 表示區(qū)塊結(jié)構(gòu)
type Block struct {
Index int // 區(qū)塊索引
Timestamp string // 時間戳
Txs []string // 交易
PrevHash string // 前一個區(qū)塊的哈希值
Hash string // 當(dāng)前區(qū)塊的哈希值
}
定義全局變量?
// 先把新區(qū)塊放入一個map中,最后一起存入redis
var blockMap = make(map[string]string)
var blockchain []Block
?????????blockMap
?是一個用于臨時存儲新區(qū)塊的 map,稍后我們將把這些新區(qū)塊保存到本地文件中。
初始化區(qū)塊鏈
????????我們需要一個函數(shù)來初始化區(qū)塊鏈。如果這是第一次運行程序,我們將創(chuàng)建一個創(chuàng)世區(qū)塊(genesis block)。如果之前已經(jīng)運行過,我們將從文件中加載現(xiàn)有區(qū)塊鏈。
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)
}
}
創(chuàng)建創(chuàng)世區(qū)塊
????????創(chuàng)世區(qū)塊是區(qū)塊鏈中的第一個區(qū)塊。它沒有前一個區(qū)塊的哈希值,因此我們將其設(shè)置為空字符串。
// 創(chuàng)建創(chuàng)世區(qū)塊
func CreateGenesisBlock() Block {
// 創(chuàng)建創(chuàng)世區(qū)塊
genesisBlock := Block{0, time.Now().String(), []string{}, "", ""}
genesisBlock.Hash = calculateHash(genesisBlock)
// 將新區(qū)塊保存到臨時的map中
blockJSON, _ := json.Marshal(genesisBlock)
blockMap[genesisBlock.Hash] = string(blockJSON)
return genesisBlock
}
????????在上面的代碼中,我們首先初始化創(chuàng)世區(qū)塊的屬性,包括索引(0)、當(dāng)前時間戳、空的交易列表、空的前一個區(qū)塊哈希值和空的當(dāng)前區(qū)塊哈希值。
????????接下來,我們調(diào)用?calculateHash
?函數(shù)來計算創(chuàng)世區(qū)塊的哈希值。這個函數(shù)將使用 SHA-256 哈希函數(shù)對區(qū)塊的內(nèi)容進(jìn)行哈希計算。我們稍后將詳細(xì)討論這個函數(shù)。
????????計算出哈希值后,我們將創(chuàng)世區(qū)塊轉(zhuǎn)換為 JSON 格式并保存到?blockMap
?中。blockMap
?是一個用于臨時存儲新區(qū)塊的 map,稍后我們將把這些新區(qū)塊保存到文件中。
????????最后,我們返回創(chuàng)世區(qū)塊。
生成區(qū)塊
????????現(xiàn)在我們已經(jīng)有了創(chuàng)世區(qū)塊,讓我們來看看如何生成新的區(qū)塊。我們將創(chuàng)建一個名為?generateBlock
?的函數(shù),它接受前一個區(qū)塊和交易列表作為參數(shù)。
// 生成新的區(qū)塊
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
}
????????在這個函數(shù)中,我們首先初始化新區(qū)塊的屬性。索引設(shè)置為前一個區(qū)塊的索引加 1,時間戳設(shè)置為當(dāng)前時間,交易列表設(shè)置為傳入的交易列表,前一個區(qū)塊的哈希值設(shè)置為傳入的前一個區(qū)塊的哈希值。然后,我們再次調(diào)用?calculateHash
?函數(shù)來計算新區(qū)塊的哈希值。
計算哈希值
????????現(xiàn)在讓我們來看看?calculateHash
?函數(shù)是如何計算區(qū)塊的哈希值的。
// 計算區(qū)塊的哈希值
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轉(zhuǎn)換成16進(jìn)制字符串
return hex.EncodeToString(hashed)
}
? ? ? ? ?在這個函數(shù)中,我們首先將區(qū)塊的索引、時間戳、交易列表和前一個區(qū)塊的哈希值連接起來,形成一個字符串。然后,我們使用 SHA-256 哈希函數(shù)來計算這個字符串的哈希值。
? ?sha256.New()
?函數(shù)創(chuàng)建一個新的 SHA-256 哈希對象。h.Write([]byte(record))
?將輸入字符串轉(zhuǎn)換為字節(jié)數(shù)組并寫入哈希對象。h.Sum(nil)
?計算最終的哈希值并返回字節(jié)數(shù)組。最后,我們使用?hex.EncodeToString(hashed)
?將字節(jié)數(shù)組轉(zhuǎn)換為 16 進(jìn)制字符串,這就是區(qū)塊的哈希值。
生成交易
????????在區(qū)塊鏈系統(tǒng)中,交易是基本的操作單元。讓我們創(chuàng)建一個簡單的函數(shù)來生成隨機交易。
// 隨機生成交易
func generateTx() string {
return fmt.Sprintf("Tx%d", rand.Intn(1000))
}
????????在這個函數(shù)中,我們使用?rand.Intn(1000)
?生成一個隨機數(shù),并將其格式化為 "Tx" 開頭的字符串。這個函數(shù)可以根據(jù)需要生成多個隨機交易。
打包交易放入?yún)^(qū)塊
????????讓我們繼續(xù)討論?productBlock
?函數(shù)。這個函數(shù)負(fù)責(zé)打包交易生成區(qū)塊并將其添加到區(qū)塊鏈中。
// 生成區(qū)塊
func productBlock(timeTX int, txPool []string, blockchain []Block) {
for {
// 生成隨機交易并放入交易池
for i := 0; i < 10; i++ {
tx := generateTx()
txPool = append(txPool, tx)
}
// 從交易池中取出交易打包到區(qū)塊中
var blockTxs []string
if len(txPool) > 8 {
blockTxs = txPool[:8]
txPool = txPool[8:]
} else {
blockTxs = txPool
txPool = []string{}
}
// 生成新區(qū)塊并添加到區(qū)塊鏈
newBlock := generateBlock(blockchain[len(blockchain)-1], blockTxs)
blockchain = append(blockchain, newBlock)
// 將新區(qū)塊保存到臨時的map中
blockJSON, _ := json.Marshal(newBlock)
blockMap[newBlock.Hash] = string(blockJSON)
// 等待指定的時間間隔
time.Sleep(time.Duration(timeTX) * time.Second)
}
}
????????在這個函數(shù)中,我們首先使用?generateTx
?函數(shù)生成隨機交易并將其放入交易池?txPool
?中。我們生成 10 個隨機交易,但您可以根據(jù)需要調(diào)整這個數(shù)字。
????????然后,我們從交易池中取出交易打包到區(qū)塊中。我們限制每個區(qū)塊最多包含 8 個交易,因此如果交易池中有超過 8 個交易,我們只取前 8 個,并將剩余的交易留在交易池中供下一個區(qū)塊使用。
????????接下來,我們調(diào)用?generateBlock
?函數(shù)來生成新區(qū)塊。這個函數(shù)接受前一個區(qū)塊和交易列表作為參數(shù),并返回一個新的區(qū)塊。我們將新區(qū)塊添加到區(qū)塊鏈?blockchain
?中。
然后,我們將新區(qū)塊轉(zhuǎn)換為 JSON 格式并保存到?blockMap
?中。blockMap
?是一個用于臨時存儲新區(qū)塊的 map,稍后我們將把這些新區(qū)塊保存到文件中。我們使用?time.Sleep
?函數(shù)等待指定的時間間隔,以模擬區(qū)塊的生成間隔。
????????這個函數(shù)將一直運行,不斷生成新的區(qū)塊并將其添加到區(qū)塊鏈中,可以根據(jù)需要調(diào)整區(qū)塊生成的 時間間隔。
打印區(qū)塊鏈
????????讓我們繼續(xù)討論?printBlock
?和?printBlockchain
?函數(shù)。這些函數(shù)負(fù)責(zé)打印單個區(qū)塊和整個區(qū)塊鏈的信息。
// 打印區(qū)塊
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
?函數(shù)接受一個?Block
?類型的參數(shù),并使用?fmt.Printf
?函數(shù)打印區(qū)塊的各個屬性,包括索引、時間戳、交易列表、前一個區(qū)塊的哈希值和當(dāng)前區(qū)塊的哈希值。每個屬性打印在一行,并以換行符結(jié)束。
// 打印區(qū)塊鏈
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)
}
// 對區(qū)塊按照索引排序
sort.Slice(blocks, func(i, j int) bool {
return blocks[i].Index < blocks[j].Index
})
for _, block := range blocks {
printBlock(block)
}
}
? ?printBlockchain
?函數(shù)負(fù)責(zé)打印整個區(qū)塊鏈的信息。它首先創(chuàng)建一個空的?Block
?切片?blocks
?來存儲從?blockMap
?中解碼的區(qū)塊。blockMap
?是一個用于臨時存儲新區(qū)塊的 map,每個區(qū)塊的哈希值作為鍵,對應(yīng)的 JSON 格式的區(qū)塊作為值。
????????我們使用?json.Unmarshal
?函數(shù)將 JSON 格式的區(qū)塊解碼為?Block
?類型。如果解碼失敗,我們將跳過該區(qū)塊并繼續(xù)處理下一個。
????????然后,我們使用?sort.Slice
?對區(qū)塊按照索引排序。sort.Slice
?函數(shù)接受一個排序函數(shù),在這個函數(shù)中,我們比較兩個區(qū)塊的索引,如果第一個區(qū)塊的索引小于第二個區(qū)塊,則返回 true,表示第一個區(qū)塊應(yīng)該排在前面。
????????最后,我們遍歷排序后的區(qū)塊切片,并調(diào)用?printBlock
?函數(shù)打印每個區(qū)塊的信息。
保存區(qū)塊鏈
????????讓我們繼續(xù)討論?saveBlock
?函數(shù)。這個函數(shù)負(fù)責(zé)將區(qū)塊保存到本地文件中。
// 保存區(qū)塊到本地Block.txt文件下
func saveBlock(blockmap map[string]string) {
//打開文件,沒有則創(chuàng)建
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
}
}
}
????????在這個函數(shù)中,我們首先使用?os.OpenFile
?函數(shù)打開或創(chuàng)建名為 "Block.txt" 的文件。我們使用?os.O_RDWR|os.O_CREATE
?標(biāo)志來指定文件可以讀寫,如果文件不存在則創(chuàng)建。
????????然后,我們使用?defer file.Close()
?確保文件在函數(shù)結(jié)束時關(guān)閉。
????????接下來,我們遍歷?blockMap
?中的所有鍵值對。blockMap
?是一個用于臨時存儲新區(qū)塊的 map,每個區(qū)塊的哈希值作為鍵,對應(yīng)的 JSON 格式的區(qū)塊作為值。
????????對于每個鍵值對,我們打印鍵和值,并使用?file.WriteString
?將它們寫入文件。我們使用?key + ":" + value + "\n"
?來確保每個區(qū)塊以 "鍵:值" 的格式寫入新的一行。
如果寫入文件時發(fā)生錯誤,我們將返回并終止函數(shù)。
????????這個函數(shù)確保新區(qū)塊被保存到本地文件中,以便下次程序運行時可以從文件中加載現(xiàn)有區(qū)塊鏈。
完整代碼
????????以下是本教程中討論的所有代碼的完整版本:
// 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 表示區(qū)塊結(jié)構(gòu)
type Block struct {
Index int // 區(qū)塊索引
Timestamp string // 時間戳
Txs []string // 交易
PrevHash string // 前一個區(qū)塊的哈希值
Hash string // 當(dāng)前區(qū)塊的哈希值
}
// 先把新區(qū)塊放入一個map中,最后一起存入redis
var blockMap = make(map[string]string)
var blockchain []Block
func main() {
initBlock()
// 交易池
txPool := []string{}
loop := true
for {
fmt.Println("------區(qū)塊鏈系統(tǒng)demo------")
fmt.Println("-----1. 添加區(qū)塊-----")
fmt.Println("-----2. 打印區(qū)塊-----")
fmt.Println("-----3. 保存區(qū)塊-----")
fmt.Println("-----4. 退出-----")
fmt.Println("請輸入您的選擇:")
var choice int
fmt.Scanln(&choice)
switch choice {
case 1:
fmt.Println("請輸入生成區(qū)塊的間隔:")
timeTX := 0
fmt.Scanln(&timeTX)
go productBlock(timeTX, txPool, blockchain)
case 2:
fmt.Println("打印區(qū)塊")
printBlockchain(blockMap)
case 3:
fmt.Println("保存區(qū)塊")
saveBlock(blockMap)
case 4:
fmt.Println("退出")
loop = false
}
if loop == false {
break
}
}
}
// 初始化區(qū)塊
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)
}
}
// 打印區(qū)塊
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()
}
// 打印區(qū)塊鏈
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)
}
// 對區(qū)塊按照索引排序
sort.Slice(blocks, func(i, j int) bool {
return blocks[i].Index < blocks[j].Index
})
for _, block := range blocks {
printBlock(block)
}
}
// 保存區(qū)塊到本地Block.txt文件下
func saveBlock(blockmap map[string]string) {
//打開文件,沒有則創(chuàng)建
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
}
}
}
// 創(chuàng)建創(chuàng)世區(qū)塊
func CreateGenesisBlock() Block {
// 創(chuàng)建創(chuàng)世區(qū)塊
genesisBlock := Block{0, time.Now().String(), []string{}, "", ""}
genesisBlock.Hash = calculateHash(genesisBlock)
// 將新區(qū)塊保存到臨時的map中
blockJSON, _ := json.Marshal(genesisBlock)
blockMap[genesisBlock.Hash] = string(blockJSON)
return genesisBlock
}
// 生成區(qū)塊
func productBlock(timeTX int, txPool []string, blockchain []Block) {
for {
// 生成隨機交易并放入交易池
for i := 0; i < 10; i++ {
tx := generateTx()
txPool = append(txPool, tx)
}
// 從交易池中取出交易打包到區(qū)塊中
var blockTxs []string
if len(txPool) > 8 {
blockTxs = txPool[:8]
txPool = txPool[8:]
} else {
blockTxs = txPool
txPool = []string{}
}
// 生成新區(qū)塊并添加到區(qū)塊鏈
newBlock := generateBlock(blockchain[len(blockchain)-1], blockTxs)
blockchain = append(blockchain, newBlock)
// 將新區(qū)塊保存到臨時的map中
blockJSON, _ := json.Marshal(newBlock)
blockMap[newBlock.Hash] = string(blockJSON)
// 等待指定的時間間隔
time.Sleep(time.Duration(timeTX) * time.Second)
}
}
// 計算區(qū)塊的哈希值
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轉(zhuǎn)換成16進(jìn)制字符串
return hex.EncodeToString(hashed)
}
// 生成新的區(qū)塊
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))
}
總結(jié)
????????在本教程中,我們使用 Go 語言實現(xiàn)了一個簡單的區(qū)塊鏈系統(tǒng)。我們討論了區(qū)塊鏈的基本概念,包括區(qū)塊、交易和哈希函數(shù)。我們還實現(xiàn)了生成區(qū)塊、交易和計算哈希值所需的函數(shù)。最后,我們創(chuàng)建了一個主函數(shù)來組合所有內(nèi)容并運行區(qū)塊鏈系統(tǒng)。
????????雖然這個實現(xiàn)是基本的,但它為理解區(qū)塊鏈技術(shù)提供了很好的基礎(chǔ)。您可以在此基礎(chǔ)上繼續(xù)構(gòu)建,添加更多的功能,例如共識機制、智能合約和去中心化網(wǎng)絡(luò)。文章來源:http://www.zghlxwxcb.cn/news/detail-853364.html
????????感謝閱讀,希望這篇教程對您有所幫助!文章來源地址http://www.zghlxwxcb.cn/news/detail-853364.html
到了這里,關(guān)于go語言簡單實現(xiàn)區(qū)塊鏈的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!