Goで200行未満のコードでブロックチェーンを記述する

こんにちは、Habr! 記事「 200行未満のGoで独自のブロックチェーンをコーディングしてください! 」の翻訳を紹介します







画像






このレッスンは、Javascriptでのブロックチェーンの簡単な記述に関するよく適応された投稿です。 Goに移植し、ブラウザでの会話の表示などの追加機能を追加しました。







レッスンの例は、心拍数データに基づいています。 私たちは医療会社です。 楽しみのために、トレーニングコース中に心拍数 (1分あたりの拍数)を計算し、この数を考慮することができます。







世界中のほとんどすべての開発者がブロックチェーンについて聞いたことがありますが、ほとんどの場合、これがどのように機能するのかわかりません。 多くはビットコイン、 スマートコントラクトについて聞いただけです。 この投稿は、200行未満のコードでGoで独自のブロックチェーンを作成できるようにすることで、ブロックチェーンに関する噂を払拭しようとする試みです! このレッスンの最後に、ブロックチェーンにデータを実行してローカルで書き込み、ブラウザで表示できます。







自分でブロックチェーンを作成するよりも、ブロックチェーンについて学ぶ良い方法はありますか?







あなたにできること





できないこと



この投稿をシンプルにするために、仕事の 証明とステークの 証明のより高度な概念は考慮しません。 ネットワークの相互作用がシミュレートされるため、ブロックチェーンを表示し、追加されたブロックを表示できます。 ネットワーキングは将来の投稿のために予約されます。







さあ始めましょう!



設置



Goでコードを作成するため、既に開発した経験があることを前提としています。 インストール後、次のパッケージも使用します。







go get github.com/davecgh/go-spew/spew
      
      





Spewを使用すると、構造とスライスをコンソールに美しく出力できます。







 go get github.com/gorilla/mux
      
      





Gorilla / muxは、リクエストハンドラを記述するための一般的なパッケージです。







 go get github.com/joho/godotenv
      
      





Gotdotenv



使用すると、ディレクトリのルートにある.env



ファイルから読み取ることができるため、コードにhttpポートなどのパラメーターを設定する必要がありません。







ディレクトリのルートに.env



ファイルを作成して、HTTP要求をリッスンするポートを決定します。 ファイルに行を追加するだけです:







 ADDR=8080
      
      





main.go



ファイルを作成します。 実装全体がこのファイルに含まれ、200行未満のコードが含まれます。







輸入品



パッケージのインポートとパッケージ宣言:







 package main import ( "crypto/sha256" "encoding/hex" "encoding/json" "io" "log" "net/http" "os" "time" "github.com/davecgh/go-spew/spew" "github.com/gorilla/mux" "github.com/joho/godotenv" )
      
      





データモデル



ブロックチェーンである各ブロックの構造を定義しましょう。 以下に、これらすべてのフィールドが必要な理由を説明します。







 type Block struct { Index int Timestamp string BPM int Hash string PrevHash string }
      
      





各ブロックには、ブロックチェーンに記録され、各心拍数測定のイベントを表すデータが含まれています。









ブロックチェーンを宣言しましょう。ブロックチェーンは構造のほんの一部です。







 var Blockchain []Block
      
      





では、ブロックとブロックチェーンでハッシュはどのように使用されますか? ハッシュを使用して、ブロックを正しい順序で識別して保存します。 各ブロックのPrevHash



フィールドは前のブロックのHash



フィールドを参照している(つまり、等しい)ため、正しいブロック順序がわかります。







画像



新しいブロックのハッシュ化と作成






なぜハッシュする必要があるのですか? 次の2つの主な理由でハッシュを取得します。









Block



データを受け取り、それらのSHA256ハッシュを作成する関数を作成しましょう。







 func calculateHash(block Block) string { record := string(block.Index) + block.Timestamp + string(block.BPM) + block.PrevHash h := sha256.New() h.Write([]byte(record)) hashed := h.Sum(nil) return hex.EncodeToString(hashed) }
      
      





calculateHash



関数は、 Block



構造のIndex



Timestamp



BPM



PrevHash



を1行に結合しIndex



。これは関数の引数であり、SHA256ハッシュの文字列表現としてすべてを返します。 これで、新しいgenerateBlock



関数を使用して、必要なすべての要素を含む新しいブロックを生成できます。 これを行うには、ハッシュとインデックスを取得できるように前のブロックを転送し、新しいBPM



心拍数値を送信する必要があります。







 func generateBlock(oldBlock Block, BPM int) (Block, error) { var newBlock Block t := time.Now() newBlock.Index = oldBlock.Index + 1 newBlock.Timestamp = t.String() newBlock.BPM = BPM newBlock.PrevHash = oldBlock.Hash newBlock.Hash = calculateHash(newBlock) return newBlock, nil }
      
      





現在の時刻はtime.Now()



によってブロックに自動的に書き込まれることに注意してください。 また、 calculateHash



関数が呼び出されたことにも注意してください。 前のブロックのハッシュ値がPrevHash



フィールドにコピーされます。 Index



は、前のブロックの値から1だけ増加します。







ブロックチェック



ここで、前のブロックの有効性をチェックする関数を作成する必要があります。 これを行うには、 Index



をチェックして、期待どおりに成長することを確認します。 また、 PrevHash



前のブロックのHash



PrevHash



一致することを確認します。 最後に、現在のブロックのハッシュを再計算して、正しいことを確認します。 これらすべてのアクションを実行し、ブール値を返すisBlockValid



関数を書きましょう。 すべてのチェックが正しくパスした場合、関数はtrue



を返しtrue









 func isBlockValid(newBlock, oldBlock Block) bool { if oldBlock.Index+1 != newBlock.Index { return false } if oldBlock.Hash != newBlock.PrevHash { return false } if calculateHash(newBlock) != newBlock.Hash { return false } return true }
      
      





ブロックチェーンエコシステムの2つのノードがチェーンにブロックを追加し、両方がそれらを取得したときに問題が発生した場合はどうなりますか。 どのソースを適切なソースとして選択しますか? 最も長いチェーンを選択します。 これは古典的なブロックチェーンの問題です。







したがって、受け入れる新しいチェーンが現在のチェーンよりも長いことを確認しましょう。 もしそうなら、新しいブロックを持つ新しいチェーンでチェーンを書き換えることができます。







画像

チェーンスライスの長さを比較するだけです。







 func replaceChain(newBlocks []Block) { if len(newBlocks) > len(Blockchain) { Blockchain = newBlocks } }
      
      





成功したら、背中を軽く叩くことができます! ブロックチェーンの機能フレームワークについて説明しました。







今、私たちはブロックチェーンを表示して、理想的にはブラウザで書き込み、友人に自慢できる便利な方法が必要です!







Webサーバー



Webサーバーがどのように機能するかを既に理解しており、Goの使用経験があることを前提としています。







以前にダウンロードしたGorrila/mux



パッケージを使用します。 run



関数を作成してサーバーを起動し、後で呼び出します。







 func run() error { mux := makeMuxRouter() httpAddr := os.Getenv("ADDR") log.Println("Listening on ", os.Getenv("ADDR")) s := &http.Server{ Addr: ":" + httpAddr, Handler: mux, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, } if err := s.ListenAndServe(); err != nil { return err } return nil }
      
      





ポートは、以前に作成した.env



ファイルから構成されていることに注意してください。 log.Printlnメソッドを呼び出して、サーバーの起動に関する情報をコンソールに表示します。 サーバーをセットアップしてListenAndServe



を呼び出しListenAndServe



。 Goの一般的なプラクティス。







次に、ハンドラーを定義するmakeMuxRouter



関数を作成する必要があります。 ブラウザでブロックチェーンを表示および記録するには、2つの単純なルートで十分です。 localhost



GET



リクエストを送信した場合、チェーンを確認します。 POST



リクエストを送信すると、データを書き込むことができます。







 func makeMuxRouter() http.Handler { muxRouter := mux.NewRouter() muxRouter.HandleFunc("/", handleGetBlockchain).Methods("GET") muxRouter.HandleFunc("/", handleWriteBlock).Methods("POST") return muxRouter }
      
      





GET



リクエストハンドラー:







 func handleGetBlockchain(w http.ResponseWriter, r *http.Request) { bytes, err := json.MarshalIndent(Blockchain, "", " ") if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } io.WriteString(w, string(bytes)) }
      
      





JSON形式でブロックチェーンを説明します。これは、 localhost:8080



任意のブラウザーで表示できます。 .env



ファイルでポートを指定できます。







POST



リクエストはもう少し複雑で、新しいMessage



メッセージ構造が必要です。







 type Message struct { BPM int }
      
      





ブロックチェーン書き込みハンドラーのコード。







 func handleWriteBlock(w http.ResponseWriter, r *http.Request) { var m Message decoder := json.NewDecoder(r.Body) if err := decoder.Decode(&m); err != nil { respondWithJSON(w, r, http.StatusBadRequest, r.Body) return } defer r.Body.Close() newBlock, err := generateBlock(Blockchain[len(Blockchain)-1], m.BPM) if err != nil { respondWithJSON(w, r, http.StatusInternalServerError, m) return } if isBlockValid(newBlock, Blockchain[len(Blockchain)-1]) { newBlockchain := append(Blockchain, newBlock) replaceChain(newBlockchain) spew.Dump(Blockchain) } respondWithJSON(w, r, http.StatusCreated, newBlock) }
      
      





別のメッセージ構造を使用した理由は、 POST



要求の本文がJSON



あり、それを使用して新しいブロックを書き込むためです。 これにより、次のフォームのPOST



リクエストを送信でき、ハンドラーが残りのブロックを埋めてくれます。







 {"BPM":50}
      
      





50



は心拍数の例です。 心拍数の値を使用できます。







リクエストボディをvar m Message



構造にデコードした後、新しいブロックを作成し、前のサイドと新しいパルス値を前にgenerateBlock



したgenerateBlock



関数に渡します。 isBlockValid



関数を使用して、新しいブロックが正しいことを確認するために簡単なチェックを行いましょう。







注:









POST



リクエストが成功または失敗したときに通知を受け取りたい。 小さなラッパーを使用して結果を取得します。 Goではエラーが無視されることはありません。







 func respondWithJSON(w http.ResponseWriter, r *http.Request, code int, payload interface{}) { response, err := json.MarshalIndent(payload, "", " ") if err != nil { w.WriteHeader(http.StatusInternalServerError) w.Write([]byte("HTTP 500: Internal Server Error")) return } w.WriteHeader(code) w.Write(response) }
      
      





ほぼ完了!



すべての開発を1つの関数main



結合しましょう。







 func main() { err := godotenv.Load() if err != nil { log.Fatal(err) } go func() { t := time.Now() genesisBlock := Block{0, t.String(), 0, "", ""} spew.Dump(genesisBlock) Blockchain = append(Blockchain, genesisBlock) }() log.Fatal(run()) }
      
      





ここで何が起こっていますか?









すべて準備完了です!



githubですべてのコードを取得できます

コードを確認しましょう。

ターミナルでgo run main.go



アプリケーションを起動します

ターミナルで、Webサーバーが動作していることを確認し、初期化された最初のブロックの出力を取得します。







画像

localhost:8080にアクセスします。 予想どおり、最初のブロックが表示されます。







画像

次に、 POST



リクエストを送信してブロックを追加しましょう。 Postmanを使用して、異なるBPM



値を持ついくつかの新しいブロックを追加します。







curlコマンド(翻訳者から):







 curl -X POST http://localhost:8080/ -H 'content-type: application/json' -d '{"BPM":50}'
      
      





画像

ブラウザでページを更新します。 これで、チェーン内の新しいブロックを確認できます。 PrevHash



新しいブロックには古いブロックのHash



に対応するPrevHash



が含まれています!







画像

将来的には



おめでとうございます! 正しいハッシュとブロック検証を使用してブロックチェーンを作成しました。 これで、プルーフオブワーク、プルーフオブステーク、スマートコントラクト、Dapps、サイドチェーンなど、より複雑なブロックチェーンの問題を学習できます。







このレッスンでは、Proof of Workを使用して追加される新しいブロックなどのトピックは扱いません。 これは別のレッスンになりますが、Proof of Workメカニズムがない多くのブロックチェーンがあります。 現在、Webサーバーでブロックチェーンデータを記録および表示することにより、すべてがモデル化されています。 このチュートリアルにはP2Pコンポーネントはありません。







Proof of Workメカニズムを追加してネットワークで作業する場合は、 Telegramチャットで報告するか、 Twitterでフォローしてください! これらは私達に連絡する最良の方法です。 レッスンに対する新しいフィードバックと新しい提案をお待ちしています。 私たちはあなたから聞いてうれしいです!







Coral Healthの詳細と医学研究でブロックチェーンを使用する方法については、当社のWebサイトをご覧ください。







PS翻訳の作者は、翻訳の指摘された誤りと不正確さに感謝します。








All Articles