使用go語言實(shí)現(xiàn)簡易版的區(qū)塊鏈

使用go語言實(shí)現(xiàn)簡易版的區(qū)塊鏈

區(qū)塊鏈概念

區(qū)塊鏈(Blockchain),是比特幣的一個重要概念,它本質(zhì)上是一個去中心化的數(shù)據(jù)庫,同時作為比特幣的底層技術(shù),是一串使用密碼學(xué)方法相關(guān)聯(lián)產(chǎn)生的數(shù)據(jù)塊,每一個數(shù)據(jù)塊中包含了一批次比特幣網(wǎng)絡(luò)交易的信息,用于驗(yàn)證其信息的有效性(防偽)和生成下一個區(qū)塊。(百度百科)

區(qū)塊里的數(shù)據(jù)

// 塊數(shù)據(jù)
type Block struct {
    Hash        string // 當(dāng)前塊的hash
    BlockNumber int64  // 當(dāng)前塊的高度,是遞增的
    PreHash     string // 上個區(qū)塊的hash
    Timestamp   int64  // 生成當(dāng)前區(qū)塊的高度
    Data        string // 區(qū)塊交易(上鏈數(shù)據(jù)) 這里應(yīng)該是slice,Data里會有很多筆的交易記錄
}

其中hash和preHash是區(qū)塊 “鏈”起來的關(guān)鍵。這里hash使用了sha256(所有的參數(shù)拼接成字符串)。

生成hash算法

hash具有唯一性,是區(qū)塊的唯一標(biāo)識。

// 生成hash
func generateHash(b Block) string {
    str := fmt.Sprintf("%d,%d,%s,%s", b.BlockNumber, b.Timestamp, b.PreHash, b.Data) // 簡單的拼接。實(shí)際區(qū)塊鏈生成hash比這要復(fù)雜的多
    hash := sha256.New()
    hash.Write([]byte(str))
    bytes := hash.Sum(nil)
    return hex.EncodeToString(bytes)
}

創(chuàng)世區(qū)塊

創(chuàng)世塊(Genesis Block)作為比特幣區(qū)塊鏈的第一個區(qū)塊是設(shè)定為不可交易的。與其他包含交易的區(qū)塊鏈中不同,創(chuàng)世紀(jì)區(qū)塊不包含任何交易,該區(qū)塊中的幣也不能用于交易中。

// 創(chuàng)建創(chuàng)世區(qū)塊
func genesisBlock() Block {
    b := Block{}
    b.BlockNumber = 0
    b.PreHash = ""
    b.Timestamp = time.Now().Unix()
    b.Data = ""
    b.Hash = generateHash(b)
    return b
}

生成區(qū)塊

使用上個區(qū)塊的數(shù)據(jù),和當(dāng)前區(qū)塊的交易數(shù)據(jù)構(gòu)建成新的區(qū)塊數(shù)據(jù)。
注意,必須用上一個區(qū)塊的哈希值來初始化本區(qū)塊的previousHash,否則區(qū)塊之間的聯(lián)系就斷了。
假如我們修改創(chuàng)世塊的信息,那么所有的區(qū)塊的hash值都會發(fā)生很大的變化,這就是為什么在區(qū)塊上作弊是非常困難的一件事情。

// 創(chuàng)建區(qū)塊
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
}

到這里所有關(guān)于區(qū)塊的部分就已經(jīng)結(jié)束了。接下來就是如何把這些區(qū)塊給串起來,形成鏈?zhǔn)浇Y(jié)構(gòu)了。

區(qū)塊的 “鏈”

我們創(chuàng)建的是一個非常簡單基礎(chǔ)的鏈,很顯然,既然對于每個節(jié)點(diǎn),我們都能計算出一個hash值,并且這個hash值是唯一的。那么們就可以通過塊的hash值來定位到這個塊的后一個節(jié)點(diǎn)是誰,因?yàn)槲覀兠總€塊除了包含交易信息以外,還保存了上一個區(qū)塊的hash值。
使用這些塊組合在一起就成一個 就形成了 區(qū)塊鏈。

// 區(qū)塊"鏈"
type BlockChain struct {
    ChainData []Block
}

驗(yàn)證新增區(qū)塊是否合法

通過上面的介紹,我們知道,塊與塊之間是通過 hash 來關(guān)聯(lián)的,而塊的高度是遞增的。以及新增的塊的數(shù)據(jù),重新計算hash,判斷和自身hash是否一致。
至此我們就能去驗(yàn)證新增的區(qū)塊是否合法


// 數(shù)據(jù)區(qū)塊數(shù)據(jù)是否合法
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
}

新 “塊” 上鏈

拿到新的交易數(shù)據(jù)data,并對數(shù)據(jù)進(jìn)行打包,得到newBlock, 并對新舊區(qū)塊進(jìn)行驗(yàn)證。

// 數(shù)據(jù)上鏈
func (bc *BlockChain) uploadChain(data string) bool {
    num := len(bc.ChainData)
    if num == 0 { // 還未產(chǎn)生塊
        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
}

打印出整個區(qū)塊鏈的信息

// 輸出區(qū)塊數(shù)據(jù)記錄
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("上鏈數(shù)據(jù):" + strconv.Itoa(i))
        if !b {
            fmt.Println("驗(yàn)證區(qū)塊錯誤")
            return
        }
        time.Sleep(time.Second * 1)
    }
    // 打印鏈數(shù)據(jù)
    blockchain.getChainData()
}

輸出結(jié)果:

=== RUN   TestBlockChain
hash: 266cd15c59789786be91873464938cb955ed24a1b5133742fccee6e964ae1bf2
blockNum: 0
timestamp: 1655955949
preHash: 
data: genesis block data
-----------------分割線------------------
hash: 9d9f8fe10611eaa153f22c2874fe97eef57fe6d581a94c2821e5f53ef252abba
blockNum: 1
timestamp: 1655955950
preHash: 266cd15c59789786be91873464938cb955ed24a1b5133742fccee6e964ae1bf2
data: 上鏈數(shù)據(jù):1
-----------------分割線------------------
hash: fd6355fd343ed65300726002b7a17bc1edaeb904adff9661106158625f09d4df
blockNum: 2
timestamp: 1655955951
preHash: 9d9f8fe10611eaa153f22c2874fe97eef57fe6d581a94c2821e5f53ef252abba
data: 上鏈數(shù)據(jù):2
-----------------分割線------------------
hash: a46b1e05464c191b7dab8d2d00048a926562f474b2bf6fe8ac313e9220c65165
blockNum: 3
timestamp: 1655955952
preHash: fd6355fd343ed65300726002b7a17bc1edaeb904adff9661106158625f09d4df
data: 上鏈數(shù)據(jù):3
-----------------分割線------------------
hash: e782e286cc58e49525592e86c89fab3fbb0ccb160f8c674dc7a25070f77a2370
blockNum: 4
timestamp: 1655955953
preHash: a46b1e05464c191b7dab8d2d00048a926562f474b2bf6fe8ac313e9220c65165
data: 上鏈數(shù)據(jù):4
-----------------分割線------------------
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容