財(cái)經(jīng)365訊(編輯 章馨),區(qū)塊鏈?zhǔn)?1世紀(jì)最具革命性的技術(shù)之一,它仍然處于不斷成長的階段,而且還有很多潛力尚未顯現(xiàn)。 本質(zhì)上,區(qū)塊鏈只是一個(gè)分布式數(shù)據(jù)庫而已。 不過,使它獨(dú)一無二的是,區(qū)塊鏈是一個(gè)公開的數(shù)據(jù)庫,而不是一個(gè)私人數(shù)據(jù)庫,也就是說,每個(gè)使用它的人都有一個(gè)完整或部分的副本。 只有經(jīng)過其他“數(shù)據(jù)庫管理員”的同意,才能向數(shù)據(jù)庫中添加新的記錄。 此外,也正是由于區(qū)塊鏈,才使得加密貨幣和智能合約成為現(xiàn)實(shí)。
區(qū)塊
首先從“區(qū)塊”談起。在區(qū)塊鏈中,真正存儲(chǔ)有效信息的是區(qū)塊(block)。而在比特幣中,真正有價(jià)值的信息就是交易(transaction)。實(shí)際上,交易信息是所有加密貨幣的價(jià)值所在。除此以外,區(qū)塊還包含了一些技術(shù)實(shí)現(xiàn)的相關(guān)信息,比如版本,當(dāng)前時(shí)間戳和前一個(gè)區(qū)塊的哈希。
不過,我們要實(shí)現(xiàn)的是一個(gè)簡化版的區(qū)塊鏈,而不是一個(gè)像比特幣技術(shù)規(guī)范所描述那樣成熟完備的區(qū)塊鏈。所以在我們目前的實(shí)現(xiàn)中,區(qū)塊僅包含了部分關(guān)鍵信息,它的數(shù)據(jù)結(jié)構(gòu)如下:
我們這里的 Timestamp,PrevBlockHash, Hash,在比特幣技術(shù)規(guī)范中屬于區(qū)塊頭(block header),區(qū)塊頭是一個(gè)單獨(dú)的數(shù)據(jù)結(jié)構(gòu)。完整的 比特幣的區(qū)塊頭(block header)結(jié)構(gòu) 如下:
下面是比特幣的 golang 實(shí)現(xiàn) btcd 的 BlockHeader 定義:
// BlockHeader defines information about a block and is used in the bitcoin // block (MsgBlock) and headers (MsgHeaders) messages.type BlockHeader struct { // Version of the block. This is not the same as the protocol version.
Version int32 // Hash of the previous block in the block chain.
PrevBlock chainhash.Hash // Merkle tree reference to hash of all transactions for the block.
MerkleRoot chainhash.Hash // Time the block was created. This is, unfortunately, encoded as a // uint32 on the wire and therefore is imited to 2106.
Timestamp time.Time // Difficulty target for the block.
Bits uint32 // Nonce used to generate the block. Nonce uint32}
而我們的 Data, 在比特幣中對應(yīng)的是交易,是另一個(gè)單獨(dú)的數(shù)據(jù)結(jié)構(gòu)。為了簡便起見,目前將這兩個(gè)數(shù)據(jù)結(jié)構(gòu)放在了一起。在真正的比特幣中,區(qū)塊的數(shù)據(jù)結(jié)構(gòu)如下:
在我們的簡化版區(qū)塊中,還有一個(gè) Hash 字段,那么,要如何計(jì)算哈希呢?哈希計(jì)算,是區(qū)塊鏈技術(shù)一個(gè)非常重要的部分。正是由于它,才保證了區(qū)塊鏈的安全。計(jì)算一個(gè)哈希,是在計(jì)算上非常困難的一個(gè)操作。即使在高速電腦上,也要耗費(fèi)很多時(shí)間 (這就是為什么人們會(huì)購買 GPU,F(xiàn)PGA,ASIC 來挖比特幣) 。這是一個(gè)架構(gòu)上有意為之的設(shè)計(jì),它故意使得加入新的區(qū)塊十分困難,繼而保證區(qū)塊一旦被加入以后,就很難再進(jìn)行修改。在接下來的內(nèi)容中,我們將會(huì)討論和實(shí)現(xiàn)這個(gè)機(jī)制。
目前,我們僅取了 Block 結(jié)構(gòu)的部分字段(Timestamp, Data 和 PrevBlockHash),并將它們相互拼接起來,然后在拼接后的結(jié)果上計(jì)算一個(gè) SHA-256,然后就得到了哈希。
Hash = SHA256(PrevBlockHash + Timestamp + Data)
在 SetHash 方法中完成這些操作:
func (b *Block) SetHash() {
timestamp := []byte(strconv.FormatInt(b.Timestamp, 10))
headers := bytes.Join([][]byte{b.PrevBlockHash, b.Data, timestamp}, []byte{})
hash := sha256.Sum256(headers)
b.Hash = hash[:]}
接下來,按照 Golang 的慣例,我們會(huì)實(shí)現(xiàn)一個(gè)用于簡化創(chuàng)建區(qū)塊的函數(shù) NewBlock:
func NewBlock(data string, prevBlockHash []byte) *Block {
block := &Block{time.Now().Unix(), []byte(data), prevBlockHash, []byte{}} block.SetHash()
return block}
區(qū)塊鏈
有了區(qū)塊,下面讓我們來實(shí)現(xiàn)區(qū)塊鏈。本質(zhì)上,區(qū)塊鏈就是一個(gè)有著特定結(jié)構(gòu)的數(shù)據(jù)庫,是一個(gè)有序,每一個(gè)塊都連接到前一個(gè)塊的鏈表。也就是說,區(qū)塊按照插入的順序進(jìn)行存儲(chǔ),每個(gè)塊都與前一個(gè)塊相連。這樣的結(jié)構(gòu),能夠讓我們快速地獲取鏈上的最新塊,并且高效地通過哈希來檢索一個(gè)塊。
在 Golang 中,可以通過一個(gè) array 和 map 來實(shí)現(xiàn)這個(gè)結(jié)構(gòu):array 存儲(chǔ)有序的哈希(Golang 中 array 是有序的),map 存儲(chǔ) hash -> block 對(Golang 中, map 是無序的)。 但是在基本的原型階段,我們只用到了 array,因?yàn)楝F(xiàn)在還不需要通過哈希來獲取塊。
type Blockchain struct { blocks []*Block}
這就是我們的第一個(gè)區(qū)塊鏈!是不是出乎意料地簡單? 就是一個(gè) Block 數(shù)組。
現(xiàn)在,讓我們能夠給它添加一個(gè)區(qū)塊:
func (bc *Blockchain) AddBlock(data string) {
prevBlock := bc.blocks[len(bc.blocks)-1]
newBlock := NewBlock(data, prevBlock.Hash)
bc.blocks = append(bc.blocks, newBlock)}
結(jié)束!不過,就這樣就完成了嗎?
為了加入一個(gè)新的塊,我們必須要有一個(gè)已有的塊,但是,初始狀態(tài)下,我們的鏈?zhǔn)强盏?,一個(gè)塊都沒有!所以,在任何一個(gè)區(qū)塊鏈中,都必須至少有一個(gè)塊。這個(gè)塊,也就是鏈中的第一個(gè)塊,通常叫做創(chuàng)世塊(genesis block). 讓我們實(shí)現(xiàn)一個(gè)方法來創(chuàng)建創(chuàng)世塊:
func NewGenesisBlock() *Block { return NewBlock("Genesis Block", []byte{})}
現(xiàn)在,我們可以實(shí)現(xiàn)一個(gè)函數(shù)來創(chuàng)建有創(chuàng)世塊的區(qū)塊鏈:
func NewBlockchain() *Blockchain {
return &Blockchain{[]*Block{NewGenesisBlock()}}}
檢查一個(gè)我們的區(qū)塊鏈?zhǔn)欠袢缙诠ぷ鳎?/span>
func main() {
bc := NewBlockchain()
bc.AddBlock("Send 1 BTC to Ivan")
bc.AddBlock("Send 2 more BTC to Ivan")
for _, block := range bc.blocks {
fmt.Printf("Prev. hash: %x\n", block.PrevBlockHash)
fmt.Printf("Data: %s\n", block.Data)
fmt.Printf("Hash: %x\n", block.Hash)
fmt.Println()
}}
輸出:
Prev. hash:
Data: Genesis Block
Hash: aff955a50dc6cd2abfe81b8849eab15f99ed1dc333d38487024223b5fe0f1168
Prev. hash: aff955a50dc6cd2abfe81b8849eab15f99ed1dc333d38487024223b5fe0f1168
Data: Send 1 BTC to Ivan
Hash: d75ce22a840abb9b4e8fc3b60767c4ba3f46a0432d3ea15b71aef9fde6a314e1
Prev. hash: d75ce22a840abb9b4e8fc3b60767c4ba3f46a0432d3ea15b71aef9fde6a314e1
Data: Send 2 more BTC to Ivan
Hash: 561237522bb7fcfbccbc6fe0e98bbbde7427ffe01c6fb223f7562288ca2295d1
總結(jié)
我們創(chuàng)建了一個(gè)非常簡單的區(qū)塊鏈原型:它僅僅是一個(gè)數(shù)組構(gòu)成的一系列區(qū)塊,每個(gè)塊都與前一個(gè)塊相關(guān)聯(lián)。真實(shí)的區(qū)塊鏈要比這復(fù)雜得多。在我們的區(qū)塊鏈中,加入新的塊非常簡單,也很快,但是在真實(shí)的區(qū)塊鏈中,加入新的塊需要很多工作:你必須要經(jīng)過十分繁重的計(jì)算(這個(gè)機(jī)制叫做工作量證明),來獲得添加一個(gè)新塊的權(quán)力。并且,區(qū)塊鏈?zhǔn)且粋€(gè)分布式數(shù)據(jù)庫,并且沒有單一決策者。因此,要加入一個(gè)新塊,必須要被網(wǎng)絡(luò)的其他參與者確認(rèn)和同意(這個(gè)機(jī)制叫做共識(shí)(consensus))。還有一點(diǎn),我們的區(qū)塊鏈還沒有任何的交易!
進(jìn)入 src 目錄查看代碼,執(zhí)行 make 即可運(yùn)行:
$ cd src
$ make==> Go build==> Running
Prev hash:
Data: Genesis Block
Hash: 4693b71eee96760de4b0f051083376dcbed2f0711a44294ee5fd42fbeacc9579
Prev hash: 4693b71eee96760de4b0f051083376dcbed2f0711a44294ee5fd42fbeacc9579
Data: Send 1 BTC to Ivan
Hash: 839380a2d0af1dc4686f16ade5423fecdc5f287db9322d9e18adcb4071e7c8ff
Prev hash: 839380a2d0af1dc4686f16ade5423fecdc5f287db9322d9e18adcb4071e7c8ff
Data: Send 2 more BTC to Ivan
Hash: b38052a029bd2b1b9d4bb478af45b4c88605e99bc64e49031ba06d21ad4b0b38。關(guān)于區(qū)塊鏈技術(shù)更多相關(guān)新聞資訊,請關(guān)注財(cái)經(jīng)365外匯頻道!