こちらに引っ越しました。
主に趣味プロジェクトについて書いていく予定です。
note.com
Cloudnative Days Tokyo 2019で「マイクロサービスにおける最高のDXを目指して」というタイトルで発表しました
cloudnative days tokyo 2019というイベントで登壇してきました。
speakerdeck.com
マイクロサービスにおける最高のDXを目指して、というタイトルで詳細はスライドを見ていただければと思います。
思えば、CFPを出して40分も登壇することが初めての経験だったので普段緊張しない性格ですが割と緊張しました。
普段は大抵、自社のイベントか、ありがたいことに声をかけていただく時に登壇をすることはあるのですが、
今回は社内のエンジニアたちに触発されて2月くらいに応募したのが経緯です。
DroidKaigiに登壇するandroidエンジニアの資料の壁打ちを何度かしました。
droidkaigiの登壇予定者の壁打ちをしたことでマテリアルデザインを分かった気になってる。デザインの裏の哲学まで理解すると一気に面白くなるなー。
— Kenji Suzuki (@kenjiszk) 2019年2月5日
偉そうに資料レビューとかしましたが、ちゃんとCFP出して通って仕事の合間に資料作って作り込んでDroidkaigiで登壇するのって尊敬するわ。そんなんがうちの会社から3人も行くのもすごい。みんな頑張ってー。
— Kenji Suzuki (@kenjiszk) 2019年2月7日
マネージャーという役職についてもう暫くたちますが、
偉そうに資料の壁打ちばかりしてないで自分もチャレンジしていかないとダメかなと思い立ったしだいです。
ということで、ここ半年で自分としても興味があって取り組んできているDXというテーマでCFPを出しました。
ちょっと予想外だったのはCFPが通った直後くらいから会社のプロジェクトが想定以上に忙しくなってしまったこと。
そのためかなりギリギリまで資料や登壇の準備とかをやっていました。
(協力してくれたみなさんありがとうございます)
twitterの反応など見るとある程度は意味のある登壇ができたのかなとホッとしています。
資料ギリギリだったのと緊張してたので、ブースを回れなかったのがちょっと残念でした。
また機会とネタがあればどんどんやっていこうと思います。
(イベント運営の方々、ありがとうございました)
30 days challenge(仮名)というアプリの構想
今年はゴールデンウィークが10日もあるので10daysハッカソンをしている。
今日は4日め。平成最後の日。
なにを作っているか?
一言で言うと、30日で何かを達成したい、習慣づけたいと言う人のためのアプリを作っている。
TEDでも同じようなアイディアを話している人がいるので興味あれば。
www.ted.com
なにができるかと言うと、毎日何かをやった証拠となるものを画像か動画の形式でアップロードしていって30日後に完成というシンプルなもの。
30日間で溜めたアセットをいい感じに変換してSNSなどでシェアできる形にしたいなと思っている。
なんでこれを作りたかったか
最近のSNSなどを見ていると、コンテンツ自体が短時間で大量に消費されていると感じている。
これ自体はいいと思うのだが、コンテンツを作る側も短時間で大量のコンテンツを作ることに傾倒しているのではないかと思う。
そのため全体的にコンテンツがキャッチーさによってしまい質が低下しているように感じた。
30days challengeはそれ自体が、何かを達成したり、習慣化するという点で完結するのだが、その活動結果を短くわかりやすくまとめれれば、短時間だが質の高いコンテンツになるのではないかと思っている。
例えば、誰かが何かに一生懸命取り組んだ30日間は数分で作った動画よりもコンテンツとしては価値が高いのではないかと思う。
チャレンジしたいことリスト
色々と構想はあるが、基本的には自分が使いたいためのアプリを作っているので完成したらやることリストを挙げておく。
腹筋ローラー
まさにこんな感じの成長ストーリー。59日かかってるけどw
www.youtube.com
ギターとかピアノとか
一番相性良さそうなのはこの辺かなと。
まあ特に弾きたい曲はないが。
30日連続で写真撮る
まあ30日と言わず毎日撮るのは変化があって面白いんじゃないかと思う。
家族で毎日写真撮るとかね。
開発
ここからは開発のお話。
構成としては、RailsでAPI書いて、クライアントはFlutterで作ろうかなと思っている。
画像系はS3とかに適当に送ろうかなと。画像や動画の加工はまだ全く未定。
今の所、ざっくりとしたAPIだけ完成。
github.com
今後
実は、30days challengeからさらに発展したビジネスモデルも考えていたりするけどまずは一旦MVPを作ってリリースしてから考える予定。
2019年も3ヶ月終わったので年始に立てたの目標を振り返る
2019年も3ヶ月が終わり。。。
年始に立てた目標は3ヶ月くらいの周期で振り返った方がいいですね、と。
https://scrapbox.io/kenjiszk/2019_Objective
その1
https://scrapbox.io/kenjiszk/2019.12.31までに英語力を一人前にする
英語は、mikanを1~2月と続けていたが最近停滞気味。
単語を覚え続けても全く喋れる雰囲気を感じていたところ。
4月から時間をしっかり作ってレアジョブを再開しようかな。
その2
https://scrapbox.io/kenjiszk/2019年は月に最低一本は渾身のブログを書く
2月に書きすぎた反動で、今月はこのブログによりなんとか体裁が整う。。。
4月からは計画的に。
その3
https://scrapbox.io/kenjiszk/2019年中にAIを利用したアプリケーションを作る
Kaggleを初めて見た。AIだけじゃないけど、広くデータ分析的な。
番外編
ボルダリングにはまっている。
単純に面白いのではまっているのだけど、この辺に英語の勉強とかをちゃんと継続できる秘密がありそうなのでちゃんと考えて見た。
ハマりポイント1
最初の1~3回での成長率が高い気がする。
男性であれば、一番簡単な級はおそらく大抵上れる。そのあと力だけで登れないゾーンが出てくる。
次に行くまでに、人にコツを聞いたりyoutubeを見たりすると前回登れなかったところが意外とサクッといけたりする。
3度目くらいになると筋肉も多少ついてたり筋肉の使い方がわかってきたりしてさらに結構登れるようになる。
ハマりポイント2
前回登れずに終わったところが、今回の初っぱなで登れるようになってる。
ボルダリングの始めたては筋肉を異様に使うので、最後の方はほぼ腕がパンパンになっている。
そんな状態で登れずに悔しい思いで帰るのだが、次回最初にチャレンジすると腕が全回復しているので簡単に登れちゃうことがある。
この感覚が非常にクセになるポイントな気がしている。
「あ〜、これ絶対次回きたら最初にクリアできるわ〜」って思いながら帰るので次回絶対行くのである。
ハマりポイント3
最初の頃は筋肉痛が非常に痛かった。
が、筋肉痛が達成感に変わる気がしている。
逆にあんまり筋肉痛にならなかった時には、いまいち頑張らなかったのかなーという気すらする。
(もしかしたら、さらに上達すると筋肉痛はそんなにこないのかもしれないが)
筋肉痛が引かないとジムに行けないというのも、ソシャゲの体力回復待ち、みたいな感じもする。
ハマりポイント4
一緒に行くひとと、同じくらいのレベルで一緒に成長する人の存在はかなり重要な気がしている。
まあ、これは自明ですかね。
まとめ
ということで、ボルダリングは勝手に継続的にはまってますが、この要素を英語の学習に取り入れていこうかなーというのを4~6月の目標にしようと思います。
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(ネットワーク編 Vol.3)
Rubyでブロックチェーンを実装する
第8回目です。
第1回はウォレットを作成して秘密鍵・公開鍵・ビットコインアドレスを発行してみました。
第2回は単純なトランザクションについて実装してみました。
第3回はトランザクションに対する署名を実装しました。
第4回はトランザクションをブロックに格納し、ブロックチェーンを作りました。
第5回はプルーフオブワークを実装しました。
第6回はブロックチェーンネットワークを作り、ノードを作成しました。
第7回はジェネシシスブロックを作成し、他のノードに伝播させました。
全てのソースコードはgithubに公開しています。
github.com
前提
過去7回の実装が終わっていることを前提とします。
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(ウォレット編) - Work Records
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(トランザクション編) - Work Records
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(署名編) - Work Records
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(ブロック編) - Work Records
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(プルーフオブワーク編) - Work Records
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(ネットワーク編 Vol.1) - Work Records
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(ネットワーク編 Vol.2) - Work Records
前回までで、ジェネシスブロックを作成できているので今回は実際にトランザクションを発行してコインの支払いが完了するところまで実装して行きたいと思います。
支払いのトランザクション
まずは支払いのトランザクションが発行できるAPIを作ります。
http_server.rbに/payのエンドポイントを足して、
server.mount('/pay', PayServlet)
PayServletを作ります。
class PayServlet < WEBrick::HTTPServlet::AbstractServlet def do_POST(req, res) to = req.query['to'] amount = req.query['amount'] # ウォレットをロードしたらpayメソッドを呼びだし、トランザクションインスタンスを作る wallet = Wallet.new wallet.load new_transaction = wallet.pay(to, amount) transactions = Transactions.new # トランザクションが正しいことを確認し、mem poolに格納する if new_transaction.is_valid? transactions.add_to_mem_pool new_transaction end # 自分以外のnodeにトランザクションを送信する dumped_new_transaction = Marshal.dump(new_transaction) Faraday.post "http://" + ENV['NODE1'] + ":8000/transaction", {transaction: dumped_new_transaction} Faraday.post "http://" + ENV['NODE2'] + ":8000/transaction", {transaction: dumped_new_transaction} end end
node1から送られてくるトランザクションデータを受け取れるように/transactionエンドポイントを作ります。
http_server.rbに/transactionを足して
server.mount('/transaction', TransactionServlet)
TransactionServletを追加します。
class TransactionServlet < WEBrick::HTTPServlet::AbstractServlet def do_POST(req, res) transaction = Marshal.load(req.query['transaction']) # 受け取ったトランザクションを自分のmem_poolに追加する transactions = Transactions.new if transaction.is_valid? transactions.add_to_mem_pool transaction end end end
マイニングする
マイニングノードは、mem poolにあるトランザクションを検証してPoWし、自分以外のnodeに伝播させる必要があります。
マイニングを開始するためのエンドポイントを作ります。実際にはノードはある程度溜まったトランザクションを自動的にブロックに追加して行きますが、今回は簡単のためAPIをトリガーに利用します。
server.mount('/start_pow', StartPowServlet)
StartPowServletを書きます。
class StartPowServlet < WEBrick::HTTPServlet::AbstractServlet def do_POST(req, res) transactions = Transactions.new transactions.load_all # mem poolに入っているトランザクションでブロックを作成し、PoWをおこなう if transactions.mem_pool.count > 0 blockchain = Blockchain.new wallet = Wallet.new wallet.load blockchain.create_block(transactions.mem_pool, wallet.address) transactions.delete_mem_pool # PoWが終了したら、他のノードに対してブロックを自分から同期するように命令を出す Faraday.post "http://" + ENV['NODE1'] + ":8000/update_blockchain", {node: ENV['ME']} Faraday.post "http://" + ENV['NODE2'] + ":8000/update_blockchain", {node: ENV['ME']} end end end
最後に、UpdateBlockchainServletを少し書き換えます。
class UpdateBlockchainServlet < WEBrick::HTTPServlet::AbstractServlet def do_POST(req, res) host = req.query['node'] || ENV['NODE1'] api_res = Faraday.get "http://" + host + ":8000/blockchain", {key: "last_hash"} last_block = Marshal.load(api_res.body) last_hash = last_block.hash # ブロック追加時に、PoWが有効かどうかをチェックする db = Database.new blockchain = Blockchain.new unless db.exist_in_local?(last_hash) unless blockchain.is_genesis_block(last_block) pow = ProofOfWork.new(last_block) return unless pow.validate end db.save('last_hash', last_hash) db.save(last_hash, last_block) else return end # ブロック追加時に、PoWが有効かどうかをチェックする prev_block_hash = last_block.prev_block_hash while prev_block_hash != "" api_res = Faraday.get "http://" + ENV['NODE1'] + ":8000/blockchain", {key: prev_block_hash} block = Marshal.load(api_res.body) if db.exist_in_local?(block.hash) break else unless blockchain.is_genesis_block(block) pow = ProofOfWork.new(block) return unless pow.validate end db.save(block.hash, block) prev_block_hash = block.prev_block_hash end end # 全てを同期し終わったら、自分のmem poolはすでに古いので削除する transactions = Transactions.new transactions.delete_mem_pool end end
以上で実装は終了です。
実際にウォレっとの作成からマイニングまで
これで一通りの実装が終わったので、実際にウォレットを作成してから支払いが完了するまでのフローを辿ってみることにします。
まずは、ウォレットを作成します。
$ curl -X POST http://127.0.0.1:8001/wallet 17FkMorsiWrJrzbhhf8cUGKBYt7arR2bzZ $ curl -X POST http://127.0.0.1:8002/wallet 1MRL2YkXSZ2the1snphsH8zgffm53LYZxN $ curl -X POST http://127.0.0.1:8003/wallet 1NA3qUgd3xxY8ea9UTNrZJLHJSfHr6mpTN
次に、ジェネシスブロックを作成します。これはnode1に対して実行します。
$ curl -X POST http://127.0.0.1:8001/genesis_block 20c79c27d2852a20daddfa7297087b4db54edb6b2d591a0dbfeb0b342adfab34
node2,3にジェネシスブロックを同期させます。
$ curl -X POST http://127.0.0.1:8002/update_blockchain $ curl -X POST http://127.0.0.1:8003/update_blockchain
node1からnode2に10コイン支払いを行ってみます。このトランザクションは直ちに他のnodeに伝播します。
$ curl -X POST http://127.0.0.1:8001/pay -d to=1MRL2YkXSZ2the1snphsH8zgffm53LYZxN -d amount=10
node3でPoWして見ましょう。問題なければブロックがマイニングされて他のnodeに同期されるようになるはずです。
$ curl -X POST http://127.0.0.1:8003/start_pow
確認のために、各nodeの残高を表示してみます。
# node1はジェネシスブロックを作って1000コインを持っていたが、node2に支払いをしているので990 $ curl -X GET http://127.0.0.1:8001/wallet 17FkMorsiWrJrzbhhf8cUGKBYt7arR2bzZ 990 # node2はnode1から支払いを受けているので10 $ curl -X GET http://127.0.0.1:8002/wallet 1MRL2YkXSZ2the1snphsH8zgffm53LYZxN 10 # node3はマイニングを行ったので報酬として25 $ curl -X GET http://127.0.0.1:8003/wallet 1NA3qUgd3xxY8ea9UTNrZJLHJSfHr6mpTN 25
ということで、3つのノードでうまく動作していることがわかりました。
まとめ
実際に支払いのトランザクションを発行しました。
そのトランザクションを別のnodeに転送して見ました。
転送されたトランザクションをブロックにしてPoWを行いました。
PoWを行ったブロックを他のnodeに伝播させました。
次回?
今回で一通りの実装は完了したと思います。
もし次回あるなら、複数のnodeでPoWをした場合にネットワークがどのようにコンセンサスをとっていくのかといったあたりを見ていけると良いかと思っています。
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(ネットワーク編 Vol.2)
Rubyでブロックチェーンを実装する
第7回目です。
第1回はウォレットを作成して秘密鍵・公開鍵・ビットコインアドレスを発行してみました。
第2回は単純なトランザクションについて実装してみました。
第3回はトランザクションに対する署名を実装しました。
第4回はトランザクションをブロックに格納し、ブロックチェーンを作りました。
第5回はプルーフオブワークを実装しました。
第6回はブロックチェーンネットワークを作り、ノードを作成しました。
全てのソースコードはgithubに公開しています。
github.com
前提
過去6回の実装が終わっていることを前提とします。
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(ウォレット編) - Work Records
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(トランザクション編) - Work Records
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(署名編) - Work Records
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(ブロック編) - Work Records
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(プルーフオブワーク編) - Work Records
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(ネットワーク編 Vol.1) - Work Records
docker-composeの修正
前回の記事のdocker-composeから少し更新します。versionsを2にすることによってlinksを使用しなくてもお互いのコンテナ同士が疎通できるようになることと、自分以外のnodeをNODE1、NODE2として環境変数で指定できるようにします。
version: '2' services: node1: build: . environment: - REDIS_HOST=redis1 - ME=node1 - NODE1=node2 - NODE2=node3 ports: - "8001:8000" volumes: - .:/app command: ruby http_server.rb redis1: image: redis node2: build: . environment: - REDIS_HOST=redis2 - ME=node2 - NODE1=node1 - NODE2=node3 ports: - "8002:8000" volumes: - .:/app command: ruby http_server.rb redis2: image: redis node3: build: . environment: - REDIS_HOST=redis3 - ME=node3 - NODE1=node1 - NODE2=node2 ports: - "8003:8000" volumes: - .:/app command: ruby http_server.rb redis3: image: redis
ジェネシスブロックの作成とその電波
ジェネシスブロックを作成するAPI
まずは一番最初のブロックであるジェネシスブロックを作成します。どのノードで作ってもいいのですがわかりやすくnode1で作成することにします。
まずは、エンドポイントをhttp_server.rbに追加します。
server.mount('/genesis_block', GenesisBlockServlet)
対応するGenesisBlockServletをservlets.rbに作ります。
/genesis_blockにPOSTされるとまずは、last_hashが存在するかどうかを調べます。これで存在した場合にはすでにブロックは作成済みなのでBad Requestを返して終了します。
last_hashがない場合には、create_genesis_blockに自分のウォレットを渡して最初のブロックを作成します。
require './blockchain.rb' # ~ 省略 ~ class GenesisBlockServlet < WEBrick::HTTPServlet::AbstractServlet def do_POST(req, res) db = Database.new begin last_hash = db.restore("last_hash") res.status = 400 rescue StandardError wallet = Wallet.new wallet.load blockchain = Blockchain.new blockchain.create_genesis_block(wallet) last_hash = db.restore("last_hash") res.body = last_hash res.status = 201 end end end
実際にAPIを叩いてみます。
# ウォレットを作り $ curl -X POST http://127.0.0.1:8001/wallet 19aYwp5VtbUzXbDC6qWSpqUpaE7QEXcZ28 # 最初のブロックを作成します $ curl -X POST http://127.0.0.1:8001/genesis_block 02ebea727cd3bb2f44dbeefbf17fdbae25b3f12b16757e58c84c7ea4832cae3a
Redisを直接みてみると、正しく作成されていることがわかります。
> keys * 1) "last_hash" 2) "wallet" 3) "02ebea727cd3bb2f44dbeefbf17fdbae25b3f12b16757e58c84c7ea4832cae3a"
他のノードがブロックを取得する
ブロックのデータをGETできるAPIを作る
node1で最初のブロックができたので他のnodeからこれを取得できるようにします。
まずは、ブロックをGETできるAPIをhttp_server.rbに作ります。
server.mount('/blockchain', BlockchainServlet)
そして、BlockchainServletをservlet.rbに作ります。
/blockchainのパラメータとしてkeyを渡せるようにしました。keyで指定されたブロックをbodyで返します。ただし、key=last_hashの時だけもう一度last_hashのkeyを元にブロックデータを取得します。
class BlockchainServlet < WEBrick::HTTPServlet::AbstractServlet def do_GET(req, res) db = Database.new if db.exist_in_local?(req.query['key']) if req.query['key'] == "last_hash" hash = db.restore(req.query['key']) res.body = Marshal.dump(db.restore(hash)) else res.body = Marshal.dump(db.restore(req.query['key'])) end else res.status = 404 end end end
ここで、Redisにkeyが存在しているかどうかだけのチェック用のメソッドをdatabase.rbに追加しています。
def exist_in_local?(key) begin restore(key) rescue StandardError return false end true end
node1からブロック情報をとってくる処理
node1からブロック情報をGETできるAPIができたので、他のnodeからそのAPIを叩いてブロック情報を同期する処理を書きます。内部でAPIを叩くのでfaradayを追加します。
gem 'faraday'
まずは、http_server.rbにupdate_blockchainを追加します。これをnode2,3に対して叩くと内部でnode1からブロックの情報を取得するようにします。
server.mount('/update_blockchain', UpdateBlockchainServlet)
UpdateBlockchainServletをservlets.rbに書いていきます。
class UpdateBlockchainServlet < WEBrick::HTTPServlet::AbstractServlet def do_POST(req, res) # node1からlast_hashのブロックを取得する api_res = Faraday.get "http://" + ENV['NODE1'] + ":8000/blockchain", {key: "last_hash"} last_block = Marshal.load(api_res.body) last_hash = last_block.hash db = Database.new # もしすでにそのハッシュ値が自分のRedisに存在するなら処理は終える。 # ないなら保存する unless db.exist_in_local?(last_hash) db.save('last_hash', last_hash) db.save(last_hash, last_block) else return end # last_hashの一個前のハッシュをprev_block_hashから取得し、それも持ってなければ追加していく # prev_block_hashが空(最初のブロック)になるまで処理を続けていく prev_block_hash = last_block.prev_block_hash while prev_block_hash != "" api_res = Faraday.get "http://" + ENV['NODE1'] + ":8000/blockchain", {key: prev_block_hash} block = Marshal.load(api_res.body) if db.exist_in_local?(block.hash) break else db.save(block.hash, block) prev_block_hash = block.prev_block_hash end end end end
実際にnode2に対してapiを叩いてみると、Redisにジェネシスブロックが同期されることがわかります。hashも一致しています。
> keys * 1) "last_hash" 2) "02ebea727cd3bb2f44dbeefbf17fdbae25b3f12b16757e58c84c7ea4832cae3a"
まとめ
ジェネシスブロックを作成する処理を作りました。
他のノードが、node1からブロックを同期してくる処理を書きました。
次回
次回はトランザクションを発行して見たいと思います。
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(ネットワーク編 Vol.1)
Rubyでブロックチェーンを実装する
第6回目です。
第1回はウォレットを作成して秘密鍵・公開鍵・ビットコインアドレスを発行してみました。
第2回は単純なトランザクションについて実装してみました。
第3回はトランザクションに対する署名を実装しました。
第4回はトランザクションをブロックに格納し、ブロックチェーンを作りました。
第5回はプルーフオブワークを実装しました。
全てのソースコードはgithubに公開しています。
github.com
前提
過去5回の実装が終わっていることを前提とします。
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(ウォレット編) - Work Records
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(トランザクション編) - Work Records
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(署名編) - Work Records
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(ブロック編) - Work Records
Rubyでブロックチェーンをできるだけ丁寧に実装して理解する(プルーフオブワーク編) - Work Records
ブロックチェーンのネットワークについて
今までの実装では、ブロックチェーンの各機能の動きに関して必要な部分を実装してきました。
今回はからは少し毛色が異なり、ブロックチェーンのネットワーク自体を実装して見たいと思います。
とはいえ、流石に本格的なP2Pネットワークを作るわけにもいかないので簡単な機能を持つノードを3つ立て、超簡易なブロックチェーンネットワークを構築してみようと思います。
ノードの要件定義
それぞれが独立して動けるフルノードを作成していきます。要件は次のように決めてそのように作っていきます。
システム要件
- ノードはhttpサーバーとして作成し、全ての動作はAPI経由とする
- データの保存はこれまで通りRedisとし、各ノードに一つづつRedisを保持する
- 全てのノードはウォレットを持っている
その他のノード
- その他のノードは、マスターノードからジェネシスブロックを受け取る
トランザクションの検証
ブロックの検証と取り込み
- ブロックが伝播されてきたノードは、その時点で自分のPoW作業を中止し、ブロックの検証に入る
- ブロックが正常であればそのブロックは各ノードのブロックチェーンに保管される
こんな具合になります。詳細はもう少し複雑ではありますが、このような流れで実装して見たいと思います。
APIサーバーを立ち上げる
まず、各ノードはAPIで全てのやり取りを行うためそういった仕組みのサーバーを作ってみようと思います。
一つのPCでの作業が行えるようにするため、docker-composeにより複数環境を作れるようにします。
rubyのhttpサーバー
まずはシンプルなhttpサーバーを作り、ウォレットの作成と取得のAPIを生やしていきます。
http_server.rbという名前で作っていきます。
require 'webrick' require './servlets.rb' server = WEBrick::HTTPServer.new({ :DocumentRoot => './', :BindAddress => '0.0.0.0', :Port => 8000 }) # /wallet にWalletSerlvetをマウントする server.mount('/wallet', WalletServlet) Signal.trap('INT'){server.shutdown} server.start
WalletSerlvetはservlets.rbに書いていきます。
中身は単純に、GETできた場合にはwalletを取得しアドレスを返す、POSTできた場合にはwalletを新規作成します。
require './wallet.rb' class WalletServlet < WEBrick::HTTPServlet::AbstractServlet def do_GET(req, res) wallet = Wallet.new wallet.load if wallet.public_key res.body = wallet.address res.status = 200 else res.status = 404 end end def do_POST(req, res) wallet = Wallet.new wallet.create_key wallet.save res.body = wallet.address res.status = 201 end end
wallet.rbにも少し手を加えます。
今までは、ネットワークという概念がなかったので一つのRedisに3つのウォレットを作成してきましたが、これからは1ノードに1ウォレットとしていきます。勿論複数のウォレットを持つことは可能ですが単純化のためです。
Redis用のkeyは"wallet"とし、loadとsaveメソッドも変更していきます。
class Wallet attr_accessor :private_key, :public_key def initialize # 簡単のために1ウォレっとにするのでkeyをwalletでRedisから取得する @key = 'wallet' @private_key = nil @public_key = nil end # ~ 略 ~ # key=walletでRedisから取得し、もしなければreturnする def load db = Database.new begin saved_wallet = db.restore(@key) rescue StandardError return end @private_key = saved_wallet.private_key @public_key = saved_wallet.public_key self end # ~ 略 ~ # key=walletで保存する def save db = Database.new db.save(@key, self) end
docker-composeによるサーバーの立ち上げ
コードはこれで編集完了ですが、複数のサーバーを立ち上げていくため、docker-composeによる立ち上げを行なっていきます。
以下のようなファイルを追加します。
docke-comose.yml
node1: build: . environment: - REDIS_HOST=redis1 ports: - "8001:8000" volumes: - .:/app links: - redis1 command: ruby http_server.rb redis1: image: redis
DockerfileとGemfile
FROM ruby:2.5.1 WORKDIR /app ADD Gemfile* $WORKDIR/ RUN bundle install
source 'https://rubygems.org' gem 'base58' gem 'ecdsa' gem 'redis'
また、databaseの接続先を環境変数に変更するため、database.rbにも少し修正を加えます。
def initialize @redis = Redis.new(host: ENV['REDIS_HOST'], port: 6379, db: 06) end
これで準備が整いました。
walletのAPIを叩いてみる
サーバーを起動して見ます。
$ docker-compose up -d Creating 06-network_redis1_1 ... done Creating 06-network_node1_1 ... done $ docker-compose ps Name Command State Ports ------------------------------------------------------------------------------------- 06-network_node1_1 ruby http_server.rb Up 0.0.0.0:8001->8000/tcp 06-network_redis1_1 docker-entrypoint.sh redis ... Up 6379/tcp
8001番ポートでリッスンしているのでそこにリクエストを投げて見ます。
まずはPOSTでウォレットを作成します。
$ curl -X POST http://127.0.0.1:8001/wallet 1HiA8wau2VDjQgknNXayqood53rs2o68CP
次にGETしてみます。
$ curl -X GET http://127.0.0.1:8001/wallet 1HiA8wau2VDjQgknNXayqood53rs2o68CP
作成したウォレットが取得できました。
2つめ、3つ目のノードを立ててみる
docker-composeができているのでここに追加するだけです。
node1: build: . environment: - REDIS_HOST=redis1 ports: - "8001:8000" volumes: - .:/app links: - redis1 command: ruby http_server.rb redis1: image: redis node2: build: . environment: - REDIS_HOST=redis2 ports: - "8002:8000" volumes: - .:/app links: - redis2 command: ruby http_server.rb redis2: image: redis node3: build: . environment: - REDIS_HOST=redis3 ports: - "8003:8000" volumes: - .:/app links: - redis3 command: ruby http_server.rb redis3: image: redis
ということでこちらにもリクエストを送ってみます。
$ curl -X POST http://127.0.0.1:8002/wallet 13dbzCGBe2bmv6mp52DFZd5xYYzTy6Bn94 $ curl -X GET http://127.0.0.1:8002/wallet 13dbzCGBe2bmv6mp52DFZd5xYYzTy6Bn94
問題なさそうです。
次回
次回はジェネシスブロックを作成し、他のノードがそのブロックを同期できるようにしていきます。