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構造のIndexTimestampBPMPrevHashを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つの単純なルートで十分です。 localhostGETリクエストを送信した場合、チェーンを確認します。 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翻訳の作者は、翻訳の指摘された誤りと不正確さに感謝します。



Source: https://habr.com/ru/post/J347930/


All Articles