0 紹介
学習目標: 1. ブロックチェーンとは何かを理解する; 2. ブロックチェーンの基本構造をマスターする; 3. ブロックチェーンの基本モデルを構築する;
- 理論的部分: 1. ブロックチェーンの 7 層アーキテクチャ モデル; 2. ブロックチェーンのチェーン構造;
- 実践的な部分: 1. ブロックを作成する; 2. ブロックの「チェーン」を作成する; 3. ブロックチェーンにアクセスする;
開発環境:GoLand(有料、学生は無料で申請可能、要証明)
ブロックチェーンの理論的基礎については、私が書いた他のブログを参考にしてください この記事では主に実践を紹介します。
- No Go 言語の基礎: 10 分間のクイック スタート ビデオ
オープン ソース コード: https://github.com/hexbo/go-blockchain-demo
お役に立ったら、星を付けて共有してください ~ pr について言及することも歓迎します
1 ブロックチェーンの理論的根拠
データレイヤー
ハッシュ
ハッシュ関数の特徴: 一方向性、確実性、隠蔽性、改ざん防止、衝突防止。
ブロックチェーンの最も基本的な技術はハッシュです. これは go で書かれたハッシュのデモです:
package main
import (
"crypto/sha256"
"encoding/hex"
"log"
)
func calcHash(toBeHashed string) string {
hashInBytes := sha256.Sum256([]byte(toBeHashed))
hashInStr := hex.EncodeToString(hashInBytes[:])
log.Printf("%s %s", toBeHashed, hashInStr)
return hashInStr
}
func main() {
calcHash("test1")
}
Go に慣れていない場合は、上記のコードについて次のような疑問を抱くかもしれません。
-
Go 言語の文字列
string
とバイト配列[]byte
の型変換の問題: [Golang] 文字はどのように保存されますか? utf8 はどのようにエンコードされますか? 文字列の構造は何ですか?golangでの文字列、配列、およびスライスの変換 -
Go 言語での配列とスライスの変換:配列とスライスの相互変換
説明: sha256 は [32]byte を返し、配列型は []byte である hashInBytes[:] によってスライスに変換されます -
Go言語の配列・スライスパラメータの受け渡し問題:【Goの基本】Goでの配列パラメータの受け渡しにはいくつかの方法があり、Go言語の配列(スライス)の値渡しと参照渡し
2 ブロックチェーンモデルを 0 から 1 に実現する
コンポーネント: 1. ブロックとチェーン構造を実現する; 2. シンプルな HTTP サーバーを実現し、読み書きインターフェースを公開する;
注: Go 言語関数名の大文字と小文字: 小文字はメソッドが現在のパッケージでのみ使用でき、プライベート メソッドであることを意味し、大文字はパブリック メソッドを意味します。
ブロック ブロック
package core
import (
"crypto/sha256"
"encoding/hex"
"time"
)
type Block struct {
Index int64 // 区块编号
Timestamp int64 // 区块时间戳
PrevBlockHash string // 上一个区块哈希值
Hash string // 当前区块哈希
Data string // 区块数据
}
func calculateHash(b Block) string {
blockData := string(b.Index) + string(b.Timestamp) + b.PrevBlockHash + b.Data
hashInBytes := sha256.Sum256([]byte(blockData))
return hex.EncodeToString(hashInBytes[:])
}
func GenerateNewBlock(preBlock Block, data string) Block {
newBlock := Block{
}
newBlock.Index = preBlock.Index + 1
newBlock.PrevBlockHash = preBlock.Hash
newBlock.Timestamp = time.Now().Unix()
newBlock.Data = data
newBlock.Hash = calculateHash(newBlock)
return newBlock
}
func GenerateGenesisBlock() Block {
preBlock := Block{
}
preBlock.Index = -1
preBlock.Hash = ""
return GenerateNewBlock(preBlock, "Genesis Block")
}
チェーン構造
コードの説明: 関数の前のパラメーター名は構造体 functionの構文です。これは、GO で構造体にバインドされる C++ クラス メンバー関数に似ています。
package core
import (
"fmt"
"log"
)
type Blockchain struct {
Blocks []*Block
}
func NewBlockchain() *Blockchain {
genesisBlock := GenerateGenesisBlock()
blockchain := Blockchain{
}
blockchain.AppendBlock(&genesisBlock)
return &blockchain
}
func (bc *Blockchain) SendData(data string) {
preBlock := bc.Blocks[len(bc.Blocks)-1]
newBlock := GenerateNewBlock(*preBlock, data)
bc.AppendBlock(&newBlock)
}
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 (bc *Blockchain) Print() {
for _, block := range bc.Blocks {
fmt.Printf("Index: %d\n", block.Index)
fmt.Printf("PrevHash: %s\n", block.PrevBlockHash)
fmt.Printf("CurrHash: %s\n", block.Hash)
fmt.Printf("Data: %s\n", block.Data)
fmt.Printf("Timestamp: %d\n", block.Timestamp)
}
}
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
}
[質問] 次のコード: ここの len 関数は冗長ではありませんか?
Cのstrlenはchar配列を1回トラバースする必要があるので、O(1)ではなくO(n)の複雑さに相当するGoのlen関数がわからない
if isValid(*newBlock, *bc.Blocks[len(bc.Blocks)-1]) {
説明: 上記の go 文字列ストレージに関する質問を参照してください. Go は、文字列ポインタの末尾の '\0' ではなく先頭に文字列長データを格納するため、O(1) クエリ可能である必要があります.
CMD 検証
package main
import "go-blockchain/core"
func main() {
bc := core.NewBlockchain()
bc.SendData("Send 1 BTC to Alice")
bc.SendData("Send 1 EOS to Bob")
bc.Print()
}
HTTP RPC 読み取りおよび書き込み
package main
import (
"encoding/json"
"go-blockchain/core"
"io"
"net/http"
)
var blockchain *core.Blockchain
func run() {
http.HandleFunc("/blockchain/get", blockchainGetHandler)
http.HandleFunc("/blockchain/write", blockchainWriteHandler)
http.ListenAndServe("localhost:8888", 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()
}
実行後、localhost:8888/blockchain/get にアクセスして現在のブロック データを表示し、/write?data="xxxx" にアクセスしてデータを書き込みます。