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

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

區(qū)塊鏈概念

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

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

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

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

生成hash算法

hash具有唯一性撤逢,是區(qū)塊的唯一標(biāo)識(shí)膛锭。

// 生成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ū)塊鏈的第一個(gè)區(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ū)塊

使用上個(gè)區(qū)塊的數(shù)據(jù)奢入,和當(dāng)前區(qū)塊的交易數(shù)據(jù)構(gòu)建成新的區(qū)塊數(shù)據(jù)筝闹。
注意,必須用上一個(gè)區(qū)塊的哈希值來初始化本區(qū)塊的previousHash腥光,否則區(qū)塊之間的聯(lián)系就斷了关顷。
假如我們修改創(chuàng)世塊的信息,那么所有的區(qū)塊的hash值都會(huì)發(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)建的是一個(gè)非常簡單基礎(chǔ)的鏈,很顯然界睁,既然對(duì)于每個(gè)節(jié)點(diǎn)觉增,我們都能計(jì)算出一個(gè)hash值,并且這個(gè)hash值是唯一的翻斟。那么們就可以通過塊的hash值來定位到這個(gè)塊的后一個(gè)節(jié)點(diǎn)是誰逾礁,因?yàn)槲覀兠總€(gè)塊除了包含交易信息以外,還保存了上一個(gè)區(qū)塊的hash值访惜。
使用這些塊組合在一起就成一個(gè) 就形成了 區(qū)塊鏈嘹履。

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

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

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


// 數(shù)據(jù)區(qū)塊數(shù)據(jù)是否合法
func (bc *BlockChain) verifyBlock(newBlock Block, oldBlock Block) bool {
    // 對(duì)比前后高度是否一致
    if newBlock.BlockNumber != oldBlock.BlockNumber+1 {
        return false
    }
    // 對(duì)比前后hash是否一致
    if newBlock.PreHash != oldBlock.Hash {
        return false
    }

    // 對(duì)比生成的hash是否一致
    if newBlock.Hash != generateHash(newBlock) {
        return false
    }
    return true
}

新 “塊” 上鏈

拿到新的交易數(shù)據(jù)data墙杯,并對(duì)數(shù)據(jù)進(jìn)行打包配并,得到newBlock, 并對(duì)新舊區(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
}

打印出整個(gè)區(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("-----------------分割線------------------")
    }
}

測(cè)試

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ū)塊錯(cuò)誤")
            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)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末高镐,一起剝皮案震驚了整個(gè)濱河市溉旋,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌嫉髓,老刑警劉巖观腊,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件邑闲,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡梧油,警方通過查閱死者的電腦和手機(jī)监憎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來婶溯,“玉大人,你說我怎么就攤上這事偷霉∑” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵类少,是天一觀的道長叙身。 經(jīng)常有香客問我,道長硫狞,這世上最難降的妖魔是什么信轿? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮残吩,結(jié)果婚禮上财忽,老公的妹妹穿的比我還像新娘。我一直安慰自己泣侮,他們只是感情好即彪,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著活尊,像睡著了一般隶校。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上蛹锰,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天深胳,我揣著相機(jī)與錄音,去河邊找鬼铜犬。 笑死舞终,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的癣猾。 我是一名探鬼主播权埠,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼煎谍!你這毒婦竟也來了攘蔽?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤呐粘,失蹤者是張志新(化名)和其女友劉穎满俗,沒想到半個(gè)月后转捕,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡唆垃,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年五芝,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片辕万。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡枢步,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出渐尿,到底是詐尸還是另有隱情醉途,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布砖茸,位于F島的核電站隘擎,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏凉夯。R本人自食惡果不足惜货葬,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望劲够。 院中可真熱鬧震桶,春花似錦、人聲如沸征绎。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽炒瘸。三九已至淤堵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間顷扩,已是汗流浹背拐邪。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留隘截,地道東北人扎阶。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓,卻偏偏與公主長得像婶芭,于是被迫代替她去往敵國和親东臀。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容