索引:
理論部分
構(gòu)建一個(gè)自己的區(qū)塊鏈
1.創(chuàng)建區(qū)塊
2.創(chuàng)建區(qū)塊鏈
3.http server暴露區(qū)塊鏈接口
代碼傳送門(mén):https://github.com/Bing0514/simpleBlockChain
理論部分
區(qū)塊鏈就是一種特殊的分布式數(shù)據(jù)庫(kù),沒(méi)有中間節(jié)點(diǎn),每個(gè)節(jié)點(diǎn)都是平等的,可以先任何一個(gè)節(jié)點(diǎn)寫(xiě)入數(shù)據(jù),每個(gè)節(jié)點(diǎn)都是平等的因此請(qǐng)求的數(shù)據(jù)會(huì)快速同步到整個(gè)網(wǎng)絡(luò)的所有節(jié)點(diǎn),這個(gè)網(wǎng)絡(luò)沒(méi)有中心,沒(méi)有管理員,每個(gè)節(jié)點(diǎn)都會(huì)寫(xiě)入交易數(shù)據(jù),并且只能增查,不支持改刪。
目前區(qū)塊鏈主要分為三類(lèi):
公有鏈 – 任何人都能讀取、交易等操作,如比特幣、以太坊等
私有鏈 – 一個(gè)公司和組織內(nèi)使用,一般是開(kāi)發(fā)節(jié)點(diǎn)、測(cè)試節(jié)點(diǎn)
聯(lián)盟鏈 – 主要針對(duì)有競(jìng)爭(zhēng)有需要合作的場(chǎng)景,其共識(shí)過(guò)程受到預(yù)選節(jié)點(diǎn)控制的區(qū)塊鏈 如Fabric R3聯(lián)盟(世界各大銀行組成) EEA(以太坊企業(yè)聯(lián)盟) 陽(yáng)光鏈等
解決的問(wèn)題:
價(jià)值傳遞問(wèn)題 – 交易雙方認(rèn)可的有用的物品進(jìn)行交互轉(zhuǎn)移,特點(diǎn)是轉(zhuǎn)移的時(shí)候轉(zhuǎn)讓方必須消失物品,受讓方必須接受到物品,在現(xiàn)實(shí)世界這是很容易的,但是在互聯(lián)網(wǎng)中很難實(shí)現(xiàn),互聯(lián)網(wǎng)中更多的是數(shù)據(jù)的傳遞而非真正的價(jià)值,因此要引入第三方,比如支付寶等等,這樣一來(lái)對(duì)第三方有很強(qiáng)的依賴性。在區(qū)塊鏈中,人們通過(guò)區(qū)塊鏈記賬來(lái)解決互聯(lián)網(wǎng)中一份價(jià)值復(fù)制多份的問(wèn)題
特點(diǎn):
不可篡改:只能增查,不支持改刪
可追溯:公開(kāi)透明(注意,可追溯不代表是數(shù)據(jù)是真實(shí)的)
去中心化
區(qū)塊鏈的六層架構(gòu)
應(yīng)用層 – 封裝了區(qū)塊鏈的各種應(yīng)用場(chǎng)景和案例
合約層 – 封裝各類(lèi)腳本和智能合約,是區(qū)塊鏈可編程的基礎(chǔ)
激勵(lì)層 – 主要應(yīng)用在公有鏈中,激勵(lì)遵守規(guī)則的節(jié)點(diǎn),懲罰不遵守規(guī)則的節(jié)點(diǎn)(在某些區(qū)塊鏈系統(tǒng)中可能不存在)
共識(shí)層 – 網(wǎng)絡(luò)節(jié)點(diǎn)的共識(shí)區(qū)塊算法,決定了記賬方式
網(wǎng)絡(luò)層 – 數(shù)據(jù)傳播、數(shù)據(jù)驗(yàn)證機(jī)制、自動(dòng)組網(wǎng)功能等
數(shù)據(jù)層 – 底層數(shù)據(jù)區(qū)塊鏈?zhǔn)浇Y(jié)構(gòu)個(gè)公私鑰、時(shí)間戳技術(shù)等(最核心最基礎(chǔ))
應(yīng)用層通過(guò)RPC與區(qū)塊鏈其他部分進(jìn)行交互,分開(kāi)部署。
區(qū)塊鏈的鏈?zhǔn)浇Y(jié)構(gòu)
最基本的構(gòu)成單元是區(qū)塊,一個(gè)區(qū)塊由區(qū)塊頭和區(qū)塊體構(gòu)成,區(qū)塊頭中最重要的一個(gè)元素是父區(qū)塊的hash,每個(gè)元素都有父區(qū)塊的hash相當(dāng)于有了一個(gè)父區(qū)塊的指針,把一個(gè)一個(gè)的區(qū)塊連接起來(lái),在鏈?zhǔn)浇Y(jié)構(gòu)中,第一個(gè)區(qū)塊叫做創(chuàng)世區(qū)塊,這個(gè)區(qū)塊只有數(shù)據(jù)沒(méi)有父區(qū)塊hash值。每個(gè)每個(gè)hash值都是通過(guò)hash函數(shù)計(jì)算得來(lái)的。
hash函數(shù)的特點(diǎn)是:
- 確定性:同樣的輸入,無(wú)論計(jì)算多少次都是一定的
- 單向性:反推困難
- 隱秘性:抗暴力破解
- 抗篡改:改變一個(gè)比特,其hash值的變化非常大
- 抗碰撞:hash值重復(fù)可能性非常小
hash函數(shù)的實(shí)現(xiàn):
MD系列
- SHA系列,推薦使用SHA256,SHA3
-
綜上區(qū)塊鏈其實(shí)很簡(jiǎn)單就是這樣的結(jié)構(gòu):
mt845650.0318_waldman_figure9_hiresen-usmsdn.10-2.png
構(gòu)建一個(gè)自己的區(qū)塊鏈
- 實(shí)現(xiàn)鏈?zhǔn)浇Y(jié)構(gòu)
-
實(shí)現(xiàn)一個(gè)http server,對(duì)外暴露讀寫(xiě)接口,從而可以通過(guò)http請(qǐng)求來(lái)讀寫(xiě)鏈上數(shù)據(jù)
目錄結(jié)構(gòu):
Captuqwere.png
步驟:
1.創(chuàng)建區(qū)塊 block.go
package demochain
import (
"crypto/sha256"
"encoding/hex"
"time"
)
type Block struct {
//區(qū)塊頭
Index int64 // 區(qū)塊編號(hào) - 代表區(qū)塊在區(qū)塊鏈中的位置
Timestamp int64 //區(qū)塊時(shí)間戳 - 區(qū)塊創(chuàng)建的時(shí)間
PrevBlockHash string //上一個(gè)區(qū)塊的hash值
Hash string //當(dāng)前區(qū)塊的hash值
// 區(qū)塊體
Data string //區(qū)塊數(shù)據(jù)
}
/**
計(jì)算hash
*/
func calculateHash(b Block) string{
blockData := string(b.Index) + string(b.Timestamp) + b.PrevBlockHash + b.Data
hashInButes := sha256.Sum256([]byte(blockData)) //blockData是一個(gè)字符串,注意這里要轉(zhuǎn)換層字節(jié)切片
return hex.EncodeToString(hashInButes[:])
}
/**
生成新的區(qū)塊 - 數(shù)據(jù)是基于前一個(gè)區(qū)塊的
*/
func GenerateNewBlock(preBlock Block, data string) Block{
newBlock := Block{}
newBlock.Index = preBlock.Index+1
newBlock.PrevBlockHash = preBlock.Hash
newBlock.Timestamp = time.Now().Unix()
newBlock.Hash = calculateHash(newBlock)
return newBlock
}
/**
生成創(chuàng)世區(qū)塊
index是0
hash是一個(gè)空值
*/
func GenerateGenesesBlock() Block{
preBlock := Block{} //這個(gè)父區(qū)塊是不存在的,是為了函數(shù)復(fù)用
preBlock.Index = -1
preBlock.Hash = ""
return GenerateNewBlock(preBlock,"Genesis Block")
}
2.創(chuàng)建區(qū)塊鏈 blockchain.go
package core
import (
"fmt"
"log"
)
type Blockchain struct {
Blocks []*Block
}
/**
新建一個(gè)區(qū)塊鏈
*/
func NewBlockchain() *Blockchain {
genesisBlock := GenerateGenesesBlock()
blockchain := Blockchain{}
blockchain.AppendBlock(&genesisBlock)
return &blockchain
}
/**
寫(xiě)入一條數(shù)據(jù)
*/
func (bc *Blockchain)SendData(data string) {
preBlock := bc.Blocks[len(bc.Blocks) -1 ]
newBlock := GenerateNewBlock(*preBlock,data)
bc.AppendBlock(&newBlock)
}
func (bc *Blockchain) Print(){
for _, block := range bc.Blocks{
fmt.Printf("Index:%d\n", block.Index)
fmt.Printf("Prev.Hash:%s\n", block.PrevBlockHash)
fmt.Printf("Curr.Hash:%s\n", block.Hash)
fmt.Printf("Data:%s\n",block.Data)
fmt.Printf("Timestamp:%d\n",block.Timestamp)
fmt.Println()
}
}
/**
添加新的區(qū)塊
*/
func (bc *Blockchain) AppendBlock(newBlock *Block) {
if len(bc.Blocks) == 0 {
bc.Blocks = append(bc.Blocks,newBlock)
return
}
if(isValid(*newBlock,*bc.Blocks[len(bc.Blocks) -1])){
bc.Blocks = append(bc.Blocks,newBlock)
}else {
log.Fatal("invalid block")
}
}
func isValid(newBlock Block, oldBlock Block) bool{
if(newBlock.Index - 1 != oldBlock.Index){
return false
}
if newBlock.PrevBlockHash != oldBlock.Hash{
return false;
}
if calculateHash(newBlock) != newBlock.Hash{
return false;
}
return true;
}
接下來(lái)創(chuàng)建一個(gè)main來(lái)運(yùn)行
package main
import "demochain/core"
func main() {
bc := core.NewBlockchain()
bc.SendData("Send 1 BTC to Jacky")
bc.SendData("Send 1 EOS to Jack")
bc.Print()
}
運(yùn)行結(jié)果

說(shuō)明(這里前面都提到過(guò)):
第一個(gè)index為0的區(qū)塊是創(chuàng)世區(qū)塊,是沒(méi)有父區(qū)塊的hash的
可以看到第一個(gè)區(qū)塊到第二個(gè)區(qū)塊到第三個(gè)區(qū)塊到之后的區(qū)塊都是通過(guò)之前一個(gè)區(qū)塊的hash來(lái)生成自己的hash的,這就是為什么叫做區(qū)塊鏈的鏈
3.創(chuàng)建http server
這里我們讓這個(gè)區(qū)塊鏈通過(guò)API暴露出來(lái)可以被寫(xiě)入數(shù)據(jù)和讀取數(shù)據(jù)
package main
import (
"demochain/core"
"encoding/json"
"io"
"net/http"
)
var blockchain *core.Blockchain
func run() {
http.HandleFunc("/blockchain/get", blockchainGetHandler)
http.HandleFunc("/blockchain/write",blockchainWriteHandler)
http.ListenAndServe("localhost:8641",nil)
}
func blockchainGetHandler(w http.ResponseWriter, r *http.Request) {
bytes,error := json.Marshal(blockchain)
if error != nil{
http.Error(w, error.Error(),http.StatusInternalServerError)
return
}
io.WriteString(w, string(bytes))
}
func blockchainWriteHandler(w http.ResponseWriter, r *http.Request) {
blockData := r.URL.Query().Get("data")
blockchain.SendData(blockData)
blockchainGetHandler(w,r)
}
func main() {
blockchain = core.NewBlockchain()
run()
}
運(yùn)行這個(gè)main方法,啟動(dòng)一個(gè)http server,首先看到的是創(chuàng)世區(qū)塊,因此這個(gè)時(shí)候還沒(méi)有寫(xiě)入數(shù)據(jù)

然后寫(xiě)入兩個(gè)區(qū)塊數(shù)據(jù)看效果

本文作者熊冰,個(gè)人網(wǎng)站Bing的天涯路,轉(zhuǎn)載請(qǐng)注明出處。

