成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專(zhuān)欄INFORMATION COLUMN

用 Go 構(gòu)建一個(gè)區(qū)塊鏈 -- Part 3: 持久化和命令行接口

felix0913 / 2778人閱讀

摘要:引言到目前為止,我們已經(jīng)構(gòu)建了一個(gè)有工作量證明機(jī)制的區(qū)塊鏈。在今天的內(nèi)容中,我們會(huì)將區(qū)塊鏈持久化到一個(gè)數(shù)據(jù)庫(kù)中,然后會(huì)提供一個(gè)簡(jiǎn)單的命令行接口,用來(lái)完成一些與區(qū)塊鏈的交互操作。這同樣也意味著,一個(gè)也就是區(qū)塊鏈的一種標(biāo)識(shí)符。

翻譯的系列文章我已經(jīng)放到了 GitHub 上:blockchain-tutorial,后續(xù)如有更新都會(huì)在 GitHub 上,可能就不在這里同步了。如果想直接運(yùn)行代碼,也可以 clone GitHub 上的教程倉(cāng)庫(kù),進(jìn)入 src 目錄執(zhí)行 make 即可。


引言

到目前為止,我們已經(jīng)構(gòu)建了一個(gè)有工作量證明機(jī)制的區(qū)塊鏈。有了工作量證明,挖礦也就有了著落。雖然目前的實(shí)現(xiàn)離一個(gè)有著完整功能的區(qū)塊鏈越來(lái)越近了,但是它仍然缺少了一些重要的特性。在今天的內(nèi)容中,我們會(huì)將區(qū)塊鏈持久化到一個(gè)數(shù)據(jù)庫(kù)中,然后會(huì)提供一個(gè)簡(jiǎn)單的命令行接口,用來(lái)完成一些與區(qū)塊鏈的交互操作。本質(zhì)上,區(qū)塊鏈?zhǔn)且粋€(gè)分布式數(shù)據(jù)庫(kù),不過(guò),我們暫時(shí)先忽略 “分布式” 這個(gè)部分,僅專(zhuān)注于 “存儲(chǔ)” 這一點(diǎn)。

選擇數(shù)據(jù)庫(kù)

目前,我們的區(qū)塊鏈實(shí)現(xiàn)里面并沒(méi)有用到數(shù)據(jù)庫(kù),而是在每次運(yùn)行程序時(shí),簡(jiǎn)單地將區(qū)塊鏈存儲(chǔ)在內(nèi)存中。那么一旦程序退出,所有的內(nèi)容就都消失了。我們沒(méi)有辦法再次使用這條鏈,也沒(méi)有辦法與其他人共享,所以我們需要把它存儲(chǔ)到磁盤(pán)上。

那么,我們要用哪個(gè)數(shù)據(jù)庫(kù)呢?實(shí)際上,任何一個(gè)數(shù)據(jù)庫(kù)都可以。在 比特幣原始論文 中,并沒(méi)有提到要使用哪一個(gè)具體的數(shù)據(jù)庫(kù),它完全取決于開(kāi)發(fā)者如何選擇。?Bitcoin Core ,最初由中本聰發(fā)布,現(xiàn)在是比特幣的一個(gè)參考實(shí)現(xiàn),它使用的是 ?LevelDB。而我們將要使用的是...

BoltDB

因?yàn)樗?/p>

非常簡(jiǎn)單和簡(jiǎn)約

用 Go 實(shí)現(xiàn)

不需要運(yùn)行一個(gè)服務(wù)器

能夠允許我們構(gòu)造想要的數(shù)據(jù)結(jié)構(gòu)

BoltDB GitHub 上的 README 是這么說(shuō)的:

Bolt 是一個(gè)純鍵值存儲(chǔ)的 Go 數(shù)據(jù)庫(kù),啟發(fā)自 Howard Chu 的 LMDB. 它旨在為那些無(wú)須一個(gè)像 Postgres 和 MySQL 這樣有著完整數(shù)據(jù)庫(kù)服務(wù)器的項(xiàng)目,提供一個(gè)簡(jiǎn)單,快速和可靠的數(shù)據(jù)庫(kù)。

由于 Bolt 意在用于提供一些底層功能,簡(jiǎn)潔便成為其關(guān)鍵所在。它的
API 并不多,并且僅關(guān)注值的獲取和設(shè)置。僅此而已。

聽(tīng)起來(lái)跟我們的需求完美契合!來(lái)快速過(guò)一下:

Bolt 使用鍵值存儲(chǔ),這意味著它沒(méi)有像 SQL RDBMS (MySQL,PostgreSQL 等等)的表,沒(méi)有行和列。相反,數(shù)據(jù)被存儲(chǔ)為鍵值對(duì)(key-value pair,就像 Golang 的 map)。鍵值對(duì)被存儲(chǔ)在 bucket 中,這是為了將相似的鍵值對(duì)進(jìn)行分組(類(lèi)似 RDBMS 中的表格)。因此,為了獲取一個(gè)值,你需要知道一個(gè) bucket 和一個(gè)鍵(key)。

需要注意的一個(gè)事情是,Bolt 數(shù)據(jù)庫(kù)沒(méi)有數(shù)據(jù)類(lèi)型:鍵和值都是字節(jié)數(shù)組(byte array)。鑒于需要在里面存儲(chǔ) Go 的結(jié)構(gòu)(準(zhǔn)確來(lái)說(shuō),也就是存儲(chǔ)(塊)Block),我們需要對(duì)它們進(jìn)行序列化,也就說(shuō),實(shí)現(xiàn)一個(gè)從 Go struct 轉(zhuǎn)換到一個(gè) byte array 的機(jī)制,同時(shí)還可以從一個(gè) byte array 再轉(zhuǎn)換回 Go struct。雖然我們將會(huì)使用 ?encoding/gob? 來(lái)完成這一目標(biāo),但實(shí)際上也可以選擇使用 JSON, XML, Protocol Buffers 等等。之所以選擇使用 encoding/gob, 是因?yàn)樗芎?jiǎn)單,而且是 Go 標(biāo)準(zhǔn)庫(kù)的一部分。

數(shù)據(jù)庫(kù)結(jié)構(gòu)

在開(kāi)始實(shí)現(xiàn)持久化的邏輯之前,我們首先需要決定到底要如何在數(shù)據(jù)庫(kù)中進(jìn)行存儲(chǔ)。為此,我們可以參考 Bitcoin Core 的做法:

簡(jiǎn)單來(lái)說(shuō),Bitcoin Core 使用兩個(gè) “bucket” 來(lái)存儲(chǔ)數(shù)據(jù):

其中一個(gè) bucket 是 blocks,它存儲(chǔ)了描述一條鏈中所有塊的元數(shù)據(jù)

另一個(gè) bucket 是 chainstate,存儲(chǔ)了一條鏈的狀態(tài),也就是當(dāng)前所有的未花費(fèi)的交易輸出,和一些元數(shù)據(jù)

此外,出于性能的考慮,Bitcoin Core 將每個(gè)區(qū)塊(block)存儲(chǔ)為磁盤(pán)上的不同文件。如此一來(lái),就不需要僅僅為了讀取一個(gè)單一的塊而將所有(或者部分)的塊都加載到內(nèi)存中。但是,為了簡(jiǎn)單起見(jiàn),我們并不會(huì)實(shí)現(xiàn)這一點(diǎn)。

blocks 中,key -> value 為:

key value
b + 32 字節(jié)的 block hash block index record
f + 4 字節(jié)的 file number file information record
l + 4 字節(jié)的 file number the last block file number used
R + 1 字節(jié)的 boolean 是否正在 reindex
F + 1 字節(jié)的 flag name length + flag name string 1 byte boolean: various flags that can be on or off
t + 32 字節(jié)的 transaction hash transaction index record

chainstate,key -> value 為:

key value
c + 32 字節(jié)的 transaction hash unspent transaction output record for that transaction
B 32 字節(jié)的 block hash: the block hash up to which the database represents the unspent transaction outputs

詳情可見(jiàn) 這里:_Data_Storage)。

因?yàn)槟壳斑€沒(méi)有交易,所以我們只需要 blocks bucket。另外,正如上面提到的,我們會(huì)將整個(gè)數(shù)據(jù)庫(kù)存儲(chǔ)為單個(gè)文件,而不是將區(qū)塊存儲(chǔ)在不同的文件中。所以,我們也不會(huì)需要文件編號(hào)(file number)相關(guān)的東西。最終,我們會(huì)用到的鍵值對(duì)有:

32 字節(jié)的 block-hash -> block 結(jié)構(gòu)

l -> 鏈中最后一個(gè)塊的 hash

這就是實(shí)現(xiàn)持久化機(jī)制所有需要了解的內(nèi)容了。

序列化

上面提到,在 BoltDB 中,值只能是 []byte 類(lèi)型,但是我們想要存儲(chǔ) Block 結(jié)構(gòu)。所以,我們需要使用 encoding/gob 來(lái)對(duì)這些結(jié)構(gòu)進(jìn)行序列化。

讓我們來(lái)實(shí)現(xiàn) BlockSerialize 方法(為了簡(jiǎn)潔起見(jiàn),此處略去了錯(cuò)誤處理):

func (b *Block) Serialize() []byte {
    var result bytes.Buffer
    encoder := gob.NewEncoder(&result)

    err := encoder.Encode(b)

    return result.Bytes()
}

這個(gè)部分比較直觀:首先,我們定義一個(gè) buffer 存儲(chǔ)序列化之后的數(shù)據(jù)。然后,我們初始化一個(gè) gob encoder 并對(duì) block 進(jìn)行編碼,結(jié)果作為一個(gè)字節(jié)數(shù)組返回。

接下來(lái),我們需要一個(gè)解序列化的函數(shù),它會(huì)接受一個(gè)字節(jié)數(shù)組作為輸入,并返回一個(gè) Block. 它不是一個(gè)方法(method),而是一個(gè)多帶帶的函數(shù)(function):

func DeserializeBlock(d []byte) *Block {
    var block Block

    decoder := gob.NewDecoder(bytes.NewReader(d))
    err := decoder.Decode(&block)

    return &block
}

這就是序列化部分的內(nèi)容了。

持久化

讓我們從 NewBlockchain 函數(shù)開(kāi)始。在之前的實(shí)現(xiàn)中,它會(huì)創(chuàng)建一個(gè)新的
Blockchain 實(shí)例,并向其中加入創(chuàng)世塊。而現(xiàn)在,我們希望它做的事情有:

打開(kāi)一個(gè)數(shù)據(jù)庫(kù)文件

檢查文件里面是否已經(jīng)存儲(chǔ)了一個(gè)區(qū)塊鏈

如果已經(jīng)存儲(chǔ)了一個(gè)區(qū)塊鏈:

創(chuàng)建一個(gè)新的 Blockchain 實(shí)例

設(shè)置 Blockchain 實(shí)例的 tip 為數(shù)據(jù)庫(kù)中存儲(chǔ)的最后一個(gè)塊的哈希

如果沒(méi)有區(qū)塊鏈:

創(chuàng)建創(chuàng)世塊

存儲(chǔ)到數(shù)據(jù)庫(kù)

將創(chuàng)世塊哈希保存為最后一個(gè)塊的哈希

創(chuàng)建一個(gè)新的 Blockchain 實(shí)例,其 tip 指向創(chuàng)世塊(tip 有尾部,尖端的意思,在這里 tip 存儲(chǔ)的是最后一個(gè)塊的哈希)

代碼大概是這樣:

func NewBlockchain() *Blockchain {
    var tip []byte
    db, err := bolt.Open(dbFile, 0600, nil)

    err = db.Update(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte(blocksBucket))

        if b == nil {
            genesis := NewGenesisBlock()
            b, err := tx.CreateBucket([]byte(blocksBucket))
            err = b.Put(genesis.Hash, genesis.Serialize())
            err = b.Put([]byte("l"), genesis.Hash)
            tip = genesis.Hash
        } else {
            tip = b.Get([]byte("l"))
        }

        return nil
    })

    bc := Blockchain{tip, db}

    return &bc
}

來(lái)一段一段地看下代碼:

db, err := bolt.Open(dbFile, 0600, nil)

這是打開(kāi)一個(gè) BoltDB 文件的標(biāo)準(zhǔn)做法。注意,即使不存在這樣的文件,它也不會(huì)返回錯(cuò)誤。

err = db.Update(func(tx *bolt.Tx) error {
...
})

在 BoltDB 中,數(shù)據(jù)庫(kù)操作通過(guò)一個(gè)事務(wù)(transaction)進(jìn)行操作。有兩種類(lèi)型的事務(wù):只讀(read-only)和讀寫(xiě)(read-write)。這里,打開(kāi)的是一個(gè)讀寫(xiě)事務(wù)(db.Update(...)),因?yàn)槲覀兛赡軙?huì)向數(shù)據(jù)庫(kù)中添加創(chuàng)世塊。

b := tx.Bucket([]byte(blocksBucket))

if b == nil {
    genesis := NewGenesisBlock()
    b, err := tx.CreateBucket([]byte(blocksBucket))
    err = b.Put(genesis.Hash, genesis.Serialize())
    err = b.Put([]byte("l"), genesis.Hash)
    tip = genesis.Hash
} else {
    tip = b.Get([]byte("l"))
}

這里是函數(shù)的核心。在這里,我們先獲取了存儲(chǔ)區(qū)塊的 bucket:如果存在,就從中讀取 l 鍵;如果不存在,就生成創(chuàng)世塊,創(chuàng)建 bucket,并將區(qū)塊保存到里面,然后更新 l 鍵以存儲(chǔ)鏈中最后一個(gè)塊的哈希。

另外,注意創(chuàng)建 Blockchain 一個(gè)新的方式:

bc := Blockchain{tip, db}

這次,我們不在里面存儲(chǔ)所有的區(qū)塊了,而是僅存儲(chǔ)區(qū)塊鏈的 tip。另外,我們存儲(chǔ)了一個(gè)數(shù)據(jù)庫(kù)連接。因?yàn)槲覀兿胍坏┐蜷_(kāi)它的話,就讓它一直運(yùn)行,直到程序運(yùn)行結(jié)束。因此,Blockchain 的結(jié)構(gòu)現(xiàn)在看起來(lái)是這樣:

type Blockchain struct {
    tip []byte
    db  *bolt.DB
}

接下來(lái)我們想要更新的是 AddBlock 方法:現(xiàn)在向鏈中加入?yún)^(qū)塊,就不是像之前向一個(gè)數(shù)組中加入一個(gè)元素那么簡(jiǎn)單了。從現(xiàn)在開(kāi)始,我們會(huì)將區(qū)塊存儲(chǔ)在數(shù)據(jù)庫(kù)里面:

func (bc *Blockchain) AddBlock(data string) {
    var lastHash []byte

    err := bc.db.View(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte(blocksBucket))
        lastHash = b.Get([]byte("l"))

        return nil
    })

    newBlock := NewBlock(data, lastHash)

    err = bc.db.Update(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte(blocksBucket))
        err := b.Put(newBlock.Hash, newBlock.Serialize())
        err = b.Put([]byte("l"), newBlock.Hash)
        bc.tip = newBlock.Hash

        return nil
    })
}

繼續(xù)來(lái)一段一段分解開(kāi)來(lái):

err := bc.db.View(func(tx *bolt.Tx) error {
    b := tx.Bucket([]byte(blocksBucket))
    lastHash = b.Get([]byte("l"))

    return nil
})

這是 BoltDB 事務(wù)的另一個(gè)類(lèi)型(只讀)。在這里,我們會(huì)從數(shù)據(jù)庫(kù)中獲取最后一個(gè)塊的哈希,然后用它來(lái)挖出一個(gè)新的塊的哈希:

newBlock := NewBlock(data, lastHash)
b := tx.Bucket([]byte(blocksBucket))
err := b.Put(newBlock.Hash, newBlock.Serialize())
err = b.Put([]byte("l"), newBlock.Hash)
bc.tip = newBlock.Hash
檢查區(qū)塊鏈

現(xiàn)在,產(chǎn)生的所有塊都會(huì)被保存到一個(gè)數(shù)據(jù)庫(kù)里面,所以我們可以重新打開(kāi)一個(gè)鏈,然后向里面加入新塊。但是在實(shí)現(xiàn)這一點(diǎn)后,我們失去了之前一個(gè)非常好的特性:我們?cè)僖矡o(wú)法打印區(qū)塊鏈的區(qū)塊了,因?yàn)楝F(xiàn)在不是將區(qū)塊存儲(chǔ)在一個(gè)數(shù)組,而是放到了數(shù)據(jù)庫(kù)里面。讓我們來(lái)解決這個(gè)問(wèn)題!

BoltDB 允許對(duì)一個(gè) bucket 里面的所有 key 進(jìn)行迭代,但是所有的 key 都以字節(jié)序進(jìn)行存儲(chǔ),而且我們想要以區(qū)塊能夠進(jìn)入?yún)^(qū)塊鏈中的順序進(jìn)行打印。此外,因?yàn)槲覀儾幌雽⑺械膲K都加載到內(nèi)存中(因?yàn)槲覀兊膮^(qū)塊鏈數(shù)據(jù)庫(kù)可能很大!或者現(xiàn)在可以假裝它可能很大),我們將會(huì)一個(gè)一個(gè)地讀取它們。故而,我們需要一個(gè)區(qū)塊鏈迭代器(BlockchainIterator):

type BlockchainIterator struct {
    currentHash []byte
    db          *bolt.DB
}

每當(dāng)要對(duì)鏈中的塊進(jìn)行迭代時(shí),我們就會(huì)創(chuàng)建一個(gè)迭代器,里面存儲(chǔ)了當(dāng)前迭代的塊哈希(currentHash)和數(shù)據(jù)庫(kù)的連接(db)。通過(guò) db,迭代器邏輯上被附屬到一個(gè)區(qū)塊鏈上(這里的區(qū)塊鏈指的是存儲(chǔ)了一個(gè)數(shù)據(jù)庫(kù)連接的 Blockchain 實(shí)例),并且通過(guò) Blockchain 方法進(jìn)行創(chuàng)建:

func (bc *Blockchain) Iterator() *BlockchainIterator {
    bci := &BlockchainIterator{bc.tip, bc.db}

    return bci
}

注意,迭代器的初始狀態(tài)為鏈中的 tip,因此區(qū)塊將從頭到尾,也就是從最新的到最舊的進(jìn)行獲取。實(shí)際上,選擇一個(gè) tip 就是意味著給一條鏈“投票”。一條鏈可能有多個(gè)分支,最長(zhǎng)的那條鏈會(huì)被認(rèn)為是主分支。在獲得一個(gè) tip (可以是鏈中的任意一個(gè)塊)之后,我們就可以重新構(gòu)造整條鏈,找到它的長(zhǎng)度和需要構(gòu)建它的工作。這同樣也意味著,一個(gè) tip 也就是區(qū)塊鏈的一種標(biāo)識(shí)符。

BlockchainIterator 只會(huì)做一件事情:返回鏈中的下一個(gè)塊。

func (i *BlockchainIterator) Next() *Block {
    var block *Block

    err := i.db.View(func(tx *bolt.Tx) error {
        b := tx.Bucket([]byte(blocksBucket))
        encodedBlock := b.Get(i.currentHash)
        block = DeserializeBlock(encodedBlock)

        return nil
    })

    i.currentHash = block.PrevBlockHash

    return block
}

這就是數(shù)據(jù)庫(kù)部分的內(nèi)容了!

CLI

到目前為止,我們的實(shí)現(xiàn)還沒(méi)有提供一個(gè)與程序交互的接口:目前只是在 main 函數(shù)中簡(jiǎn)單執(zhí)行了 NewBlockchainbc.AddBlock 。是時(shí)候改變了!現(xiàn)在我們想要擁有這些命令:

blockchain_go addblock "Pay 0.031337 for a coffee"
blockchain_go printchain

所有命令行相關(guān)的操作都會(huì)通過(guò) CLI 結(jié)構(gòu)進(jìn)行處理:

type CLI struct {
    bc *Blockchain
}

它的 “入口” 是 Run 函數(shù):

func (cli *CLI) Run() {
    cli.validateArgs()

    addBlockCmd := flag.NewFlagSet("addblock", flag.ExitOnError)
    printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError)

    addBlockData := addBlockCmd.String("data", "", "Block data")

    switch os.Args[1] {
    case "addblock":
        err := addBlockCmd.Parse(os.Args[2:])
    case "printchain":
        err := printChainCmd.Parse(os.Args[2:])
    default:
        cli.printUsage()
        os.Exit(1)
    }

    if addBlockCmd.Parsed() {
        if *addBlockData == "" {
            addBlockCmd.Usage()
            os.Exit(1)
        }
        cli.addBlock(*addBlockData)
    }

    if printChainCmd.Parsed() {
        cli.printChain()
    }
}

我們會(huì)使用標(biāo)準(zhǔn)庫(kù)里面的 flag 包來(lái)解析命令行參數(shù):

addBlockCmd := flag.NewFlagSet("addblock", flag.ExitOnError)
printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError)
addBlockData := addBlockCmd.String("data", "", "Block data")

首先,我們創(chuàng)建兩個(gè)子命令: addblockprintchain, 然后給 addblock 添加 -data 標(biāo)志。printchain 沒(méi)有任何標(biāo)志。

switch os.Args[1] {
case "addblock":
    err := addBlockCmd.Parse(os.Args[2:])
case "printchain":
    err := printChainCmd.Parse(os.Args[2:])
default:
    cli.printUsage()
    os.Exit(1)
}

然后,我們檢查用戶提供的命令,解析相關(guān)的 flag 子命令:

if addBlockCmd.Parsed() {
    if *addBlockData == "" {
        addBlockCmd.Usage()
        os.Exit(1)
    }
    cli.addBlock(*addBlockData)
}

if printChainCmd.Parsed() {
    cli.printChain()
}

接著檢查解析是哪一個(gè)子命令,并調(diào)用相關(guān)函數(shù):

func (cli *CLI) addBlock(data string) {
    cli.bc.AddBlock(data)
    fmt.Println("Success!")
}

func (cli *CLI) printChain() {
    bci := cli.bc.Iterator()

    for {
        block := bci.Next()

        fmt.Printf("Prev. hash: %x
", block.PrevBlockHash)
        fmt.Printf("Data: %s
", block.Data)
        fmt.Printf("Hash: %x
", block.Hash)
        pow := NewProofOfWork(block)
        fmt.Printf("PoW: %s
", strconv.FormatBool(pow.Validate()))
        fmt.Println()

        if len(block.PrevBlockHash) == 0 {
            break
        }
    }
}

這部分內(nèi)容跟之前的很像,唯一的區(qū)別是我們現(xiàn)在使用的是 BlockchainIterator 對(duì)區(qū)塊鏈中的區(qū)塊進(jìn)行迭代:

記得不要忘了對(duì) main 函數(shù)作出相應(yīng)的修改:

func main() {
    bc := NewBlockchain()
    defer bc.db.Close()

    cli := CLI{bc}
    cli.Run()
}

注意,無(wú)論提供什么命令行參數(shù),都會(huì)創(chuàng)建一個(gè)新的鏈。

這就是今天的所有內(nèi)容了! 來(lái)看一下是不是如期工作:

$ blockchain_go printchain
No existing blockchain found. Creating a new one...
Mining the block containing "Genesis Block"
000000edc4a82659cebf087adee1ea353bd57fcd59927662cd5ff1c4f618109b

Prev. hash:
Data: Genesis Block
Hash: 000000edc4a82659cebf087adee1ea353bd57fcd59927662cd5ff1c4f618109b
PoW: true

$ blockchain_go addblock -data "Send 1 BTC to Ivan"
Mining the block containing "Send 1 BTC to Ivan"
000000d7b0c76e1001cdc1fc866b95a481d23f3027d86901eaeb77ae6d002b13

Success!

$ blockchain_go addblock -data "Pay 0.31337 BTC for a coffee"
Mining the block containing "Pay 0.31337 BTC for a coffee"
000000aa0748da7367dec6b9de5027f4fae0963df89ff39d8f20fd7299307148

Success!

$ blockchain_go printchain
Prev. hash: 000000d7b0c76e1001cdc1fc866b95a481d23f3027d86901eaeb77ae6d002b13
Data: Pay 0.31337 BTC for a coffee
Hash: 000000aa0748da7367dec6b9de5027f4fae0963df89ff39d8f20fd7299307148
PoW: true

Prev. hash: 000000edc4a82659cebf087adee1ea353bd57fcd59927662cd5ff1c4f618109b
Data: Send 1 BTC to Ivan
Hash: 000000d7b0c76e1001cdc1fc866b95a481d23f3027d86901eaeb77ae6d002b13
PoW: true

Prev. hash:
Data: Genesis Block
Hash: 000000edc4a82659cebf087adee1ea353bd57fcd59927662cd5ff1c4f618109b
PoW: true

總結(jié)

在下篇文章中,我們將會(huì)實(shí)現(xiàn)地址,錢(qián)包,(可能實(shí)現(xiàn))交易。盡請(qǐng)收聽(tīng)!

鏈接

Full source codes

Bitcoin Core Data Storage:_Data_Storage)

boltdb

encoding/gob

flag

本文源碼:part_3

原文:Building Blockchain in Go. Part 3: Persistence and CLI

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://m.hztianpu.com/yun/23942.html

相關(guān)文章

  • Go 構(gòu)建一個(gè)區(qū)塊 -- Part 6: 交易(2)

    摘要:到目前為止,我們幾乎已經(jīng)實(shí)現(xiàn)了一個(gè)區(qū)塊鏈數(shù)據(jù)庫(kù)的所有元素。使用根據(jù)在區(qū)塊鏈中找到一筆交易。是一個(gè)比特幣輕節(jié)點(diǎn),它不需要下載整個(gè)區(qū)塊鏈,也不需要驗(yàn)證區(qū)塊和交易。到目前為止,我們只是將一個(gè)塊里面的每筆交易哈希連接了起來(lái),將在上面應(yīng)用了算法。 翻譯的系列文章我已經(jīng)放到了 GitHub 上:blockchain-tutorial,后續(xù)如有更新都會(huì)在 GitHub 上,可能就不在這里同步了。如果...

    spacewander 評(píng)論0 收藏0
  • 基于Java語(yǔ)言構(gòu)建區(qū)塊(三)—— 久化 & 命令

    摘要:我們?cè)撨x擇哪一款數(shù)據(jù)庫(kù)呢事實(shí)上,在比特幣白皮書(shū)中并沒(méi)有明確指定使用哪一種的數(shù)據(jù)庫(kù),因此這個(gè)由開(kāi)發(fā)人員自己決定。詳見(jiàn)精通比特幣第二版第章節(jié)交易的輸入與輸出此外,每個(gè)區(qū)塊數(shù)據(jù)都是以單獨(dú)的文件形式存儲(chǔ)在磁盤(pán)上。資料源代碼精通比特幣第二版 showImg(https://segmentfault.com/img/remote/1460000013923488?w=1200&h=627); 最...

    asoren 評(píng)論0 收藏0
  • 基于Java語(yǔ)言構(gòu)建區(qū)塊(三)—— 久化 & 命令

    摘要:我們?cè)撨x擇哪一款數(shù)據(jù)庫(kù)呢事實(shí)上,在比特幣白皮書(shū)中并沒(méi)有明確指定使用哪一種的數(shù)據(jù)庫(kù),因此這個(gè)由開(kāi)發(fā)人員自己決定。詳見(jiàn)精通比特幣第二版第章節(jié)交易的輸入與輸出此外,每個(gè)區(qū)塊數(shù)據(jù)都是以單獨(dú)的文件形式存儲(chǔ)在磁盤(pán)上。資料源代碼精通比特幣第二版 showImg(https://segmentfault.com/img/remote/1460000013923488?w=1200&h=627); 最...

    李世贊 評(píng)論0 收藏0
  • Go 構(gòu)建一個(gè)區(qū)塊 -- Part 2: 工作量證明

    摘要:哈希函數(shù)被廣泛用于檢測(cè)數(shù)據(jù)的一致性。在區(qū)塊鏈中,哈希被用于保證一個(gè)塊的一致性。比特幣使用,一個(gè)最初用來(lái)防止垃圾郵件的工作量證明算法。下面是與前面例子哈希的形式化比較第一個(gè)哈?;谟?jì)算比目標(biāo)要大,因此它并不是一個(gè)有效的工作量證明。 翻譯的系列文章我已經(jīng)放到了 GitHub 上:blockchain-tutorial,后續(xù)如有更新都會(huì)在 GitHub 上,可能就不在這里同步了。如果想直接運(yùn)...

    suxier 評(píng)論0 收藏0
  • Go 構(gòu)建一個(gè)區(qū)塊 -- Part 7: 網(wǎng)絡(luò)

    摘要:盡管我們不會(huì)實(shí)現(xiàn)一個(gè)真實(shí)的網(wǎng)絡(luò),但是我們會(huì)實(shí)現(xiàn)一個(gè)真是,也是比特幣最常見(jiàn)最重要的用戶場(chǎng)景。不過(guò),這并不是處于禮貌用于找到一個(gè)更長(zhǎng)的區(qū)塊鏈。意為給我看一下你有什么區(qū)塊在比特幣中,這會(huì)更加復(fù)雜。 翻譯的系列文章我已經(jīng)放到了 GitHub 上:blockchain-tutorial,后續(xù)如有更新都會(huì)在 GitHub 上,可能就不在這里同步了。如果想直接運(yùn)行代碼,也可以 clone GitHu...

    lingdududu 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

閱讀需要支付1元查看
<