『ゼロから創る暗号通貨』
第7章: ここから先のSimpleBitcoin

濵津 誠/hamatz

第7章 ここから先のSimpleBitcoin

この章では、これまでに創ってきたSimpleBitcoinを1つのプラットフォームとして見立てることにより、暗号通貨の範疇を越えてどのようなポテンシャルを持っているかを確認していきます。ポイントとなるのは次のような内容です。

  1. Transactionを拡張することで、どのような可能性の扉が開かれるのか?
  2. 暗号通貨以外のブロックチェーンの活用方法は?
  3. 基盤となるP2Pネットワークが持つ可能性は?

みなさんがSimpleBitcoinをこの先どのように育てていくか、それを考えるためのヒントとなる情報を示せれば幸いです。

[2pt_line]

「今回は、暗号通貨という枠にとどまらず、これまで作ってきたものがどのように利用できるか、さらに先の可能性についてディスカッションしてみよう。このカリキュラムも、いよいよ今回が本当の最後になる」

「うん。実際に作ったから内部の構造は理解できているし、どの辺をいじればどんな拡張ができそうか、僕にも少しは見える気がするよ」

「ほう。どんなことを考えているのか、実に楽しみだ」

「へ、変なプレッシャーをかけないでよ、兄さん!」

7.1 そうはいっても暗号通貨なので

「セキュリティについて考えていたとき、SimpleBitcoinの暗号通貨らしくない要素として、送金理由やコメントの送信を可能にする件について触れたのを覚えているかい?まずはその辺りの可能性から考えてみようか」

「そうだね。ここまで作ってきた範囲でいうと、SimpleBitcoinの送金時にはTransactionInputTransactionOutputの組み合わせしか送っていない。そのため、どの公開鍵アドレスからどの公開鍵アドレスへの送金であるか、という情報以上のことはわからなくなっている」

「もともとのSimpleBitcoinの設計目標として、そのことに問題はないだろうか?」

「もともとの狙いは、他社からの承認を価値としてやり取りする、ということだったよね。面白いツイートに対して『いいね!』をもらえた、といった価値がSimpleBitcoinにとってのコインなのだから、コインが贈られた理由を知ることで得られる満足感のようなものも大切だと思う」

「うむ。一般の暗号通貨と違って匿名性にはあまり重きを置かず、むしろコミュニケーションを活性化させるための潤滑油みたいなものとして暗号通貨を捉えたら面白いだろう、という観点だった」

「でも、いまのSimpleBitcoinの実装だと、そういう満足感は置き去りにされてしまってるんだよね……」

「そうだね。SimpleBitcoinでやり取りしようとしている価値は、どちらかというと、名誉とかそういう類のものだといえる。匿名性とは相容れない価値かもしれない」

「うん。SimpleBitcoinのコインは数えられるポイントだし、お金を出してでも欲しいっていう人が将来は出てくるかもしれないけれど、少なくとも前提は『お金に換算可能な価値ではない』だし、匿名性のためにブロックチェーンを使っているわけでもないんだよなー」

「いい線まできたね。実は、『有限のポイントが不正なくやり取りされているか』を管理するためにブロックチェーンを使っていると捉え直すことで、また別の可能性が開けてくる」

「えっ?それはどういうこと?」

「たとえば、誰かが誰かのツイートやブログの記事に『いいね!』をするとして、はたしてその情報はリアルタイムで届かなければならないものだろうか?」

「そんな必要はないと思うよ。だからこそ、ビットコインとかでは欠点とされる『決済時間の長さ』もSimpleBitcoinでは取り立てて問題になっていなかったし……」

「そのとおり。操作履歴が完全に記録されるデータベースがあって、それが誰か特定のプレイヤーに依存することなくオープンな形で運用されているというのが、ブロックチェーンで実現することの肝だと私は考えている」

「なるほどー。SimpleBitcoinでは、ブロックチェーンのそういう性質を使っていると考えればいいわけか。でも、それが実現できると、何がどう嬉しいの?」

「嬉しさの1つとして、まず考えられるのは、否認防止の機能を実現できることだ」

「否認防止?」

7.1.1 ブロックチェーンで否認防止

「連続したメッセージのやり取りをすべてブロックチェーン上に記録しておくことで、たとえば、『誰かに何かを依頼してその対価を支払うことを約束した』ということの証拠が残せる。依頼されたことを実行した後で、そんな約束しなかったから対価は払わないよ、と否認されてしまうのを防止できるということさ」

「それって、最初に出てきたスマートコントラクトみたいな話?」

「スマートコントラクトのように完全に自動でできるとはいわないが、後でブロックチェーンを確認することで、『約束をしたのに支払いが実施されていない』という裏付けが取れる。たとえば図7.1に当てはめて考えると、最初のステップで署名付きのソースコードを渡すことにすれば、相手の計算リソースを借りるような処理にも適用可能だ。そしてこの場合、ソースコード自身は外部に置いてURLとハッシュ値だけを置いておくようなスタイルにすれば、相当複雑な処理の依頼も可能となるだろう」

「なるほど。依頼された仕事を処理した瞬間に自動的に支払いが実施される、みたいなところまで賢くはないかもしれないけれど、依頼された仕事の詳細とその結果が後で第三者から検証可能な形で残せることで、少なくとも泣き寝入りの可能性は低減できるわけか……」

ブロックチェーン上で仕事を依頼するプロトコルの概要

図7.1: ブロックチェーン上で仕事を依頼するプロトコルの概要

「そう。たとえばドイツの鉄道では、改札機がない代わりに車内で抜き打ち検査があって、乗客が切符を持っていない場合には通常運賃の何十倍もの金額を請求される。こうした罰則によってユーザーに不正をさせないようにできるかもしれない。つまり、SimpleBitcoinにはブロックチェーンとして否認防止の機能があるので、『プロトコルに違反して支払いをしないものはアカウントを排除』といったルールを併用することにより、スマートコントラクトまで複雑な仕組みを組み込まずとも、意外と多くの取引にSimpleBitcoinを使えるかもしれない」

「なるほどー。たしかに、送金に関連しないようなTransactionを積極的に置ける場所っていうのは、ブロックチェーンを実装したSimpleBitcoinが目指すあり方っぽいなー」

「もちろん、ほかの使い道もあるだろう。でも、少なくとも私は、否認防止というのは大きな可能性がある機能だろうと思っている」

「うん。なんだか僕もそんな気がしてきた」

7.1.2 ブロックチェーンは証拠である

「もちろん、ブロックチェーンには否認防止以外の用途もある。ブロックチェーンには、登録されているTransactionが消せないという特性があるだろう?そのため、ある意味では否認防止の真逆の機能というか、自分の発言の証拠を残せるという機能がある。これを活用する道もあるだろう」

「発言の証拠?何が嬉しいの?」

「たとえば、何か新しいアイデアを思いついたとき、それを公開されているブロックチェーン上に記録しておく。そうすれば、誰が最初の発案者であったか、後で時系列を見て判定が可能になる」

「いわゆるパクツイ問題が解決するっていう話?」

「そういうオリジナリティの主張にも使えるだろうし、もっといえば、他人の特許を無効化する理由などとしても使えるだろう」

ブロックチェーンにデータの存在証明を残す

図7.2: ブロックチェーンにデータの存在証明を残す

「誰でも見られてしまう公開のデータベースなので、扱える内容は限られるだろうけど、公開されているからこそ価値があるような状況も考えられるってことか。なるほどー」

「データベースとして見ると、公開情報であることよりも、消せない情報になることのほうが深刻かもしれない。なにしろ、そこに他人の個人情報を書き込もうとするバカな人間もいないとはいえないからな。とはいえ、Transactionに署名を必須とすることで、そういった悪意をもった利用に対する抑止力は設定できるんじゃないかな」

「悪意をもって個人情報なんかを公開した人物の特定にまでは至らなくても、その公開鍵の持ち主をサービスから締め出すといったペナルティを課すことは可能になるもんね。匿名掲示板ほど迂闊には振る舞えないだろうっていうわけか」

7.2 暗号通貨から離れたブロックチェーンの活用例

「すべてのデータを誰もが見られるわけだから、SNSのようなサービスをSimpleBitcoinに載せてしまうことも考えられるだろう。そのまま完全なTwitterの代替にはならないが、そのオープン性を生かした新しいサービスを作れるかもしれない」

「それ、面白そうだよね。鍵アカウントのないTwitterって感じで」

「それだけじゃない。すべてのデータに誰もがアクセスできるということは、API制限も何もない。アプリを開発する誰もが平等にデータを扱える」

「そうか!アプリ開発者からすると、それは魅力的かもしれないよね。TwitterのAPI利用制限でアプリの開発をやめた、なんて話も聞くくらいだし」

「ただ、ブロックチェーンから直接データを取り出しているとリアルタイム性に欠けるし、検索性もよくない。もし実際にその手のサービスをやろうと思ったら、中間にデータアクセスのための層を挟むことにはなるだろう。だが、それでも新しい体験を生み出す可能性はありそうだ」

「オリジナルの発言者が誰なのか証拠が残るし、逆に、言ってもいないことを言ったと責められたときに『消せないブロックチェーン上にそんな発言は存在していない』という証拠を示すことも可能なTwitterかー。たしかに、似てるけれども少し違うサービスを実現できそうな気がするね!」

「そして、SimpleBitcoinでは、『いいね!』に量の概念を持たせることが可能だったよね」

「そうだった!」

「完全なTwitterクローンを作るのは大変だけど、原理的な部分だけなら実際に作ってみて確認できる。Twitter風のサービスを実現する際の肝となる部分がSimpleBitcoinでどのように実現できるかを、ちょっと確認してみようか」

「おお!実際に動くものを作ってみるんだね!」

「まぁ、本当に基礎の基礎の部分だけだけどね」

「やっぱり動くものを作ってるときが一番楽しいよ!」

7.2.1 これから作ろうとするサービスを確認しよう

「まずは、これから作ろうするサービスについて確認しておこう。大きくは次の2つの基本機能を作ることになる」

これから作る2つの機能の概要

図: これから作る2つの機能の概要

「なるほど。話の流れから1つめの機能はなんとなく想像してたけど、2つめの機能については考えてなかった。ブロックチェーンの更新を定期的に確認する代わりに、P2Pネットワークのときに作った分散型掲示板の仕組み(第2章参照)を使って、登録するデータを同時に全体で共有してしまおうってわけか」

「そのとおり。Mastodonの連合タイムラインのように、フォローしている人など関係なしにP2Pネットワークに接続中の全員のツイートが次々に届くようにすると考えればイメージしやすいかもしれないね」

「要するに、これから作る機能は図7.3に示した2つってことだよね?検索とか、フォローしてる人だけをタイムラインに表示するとか、そういうブロックチェーンが苦手とする部分については『中間層』として切り出してしまい、発言の記録とリアルタイム通知だけを実現するって話」

これから実現しようとしている機能

図7.3: これから実現しようとしている機能

「そのとおりだ。それでは、まず、1つめのブロックチェーンにテキストデータを保存する部分について考えていこう」

「ブロックチェーンに保存する以上は、基本的にはTransactionなんだよね?」

「そうだ。だから考えるべきことはシンプル。Transactionのデータとしてどのような情報要素を持っているべきかを考えればいい」

「うーんと……。Twitterを思い浮かべながら考えると、自由に入力可能な発言者の名前と、実際のシステム上での識別のためのID、それに、本文のテキストくらいかな……」

「どの発言についてのリプライかを示すために、Transactionを識別できる必要があるんじゃないかな?」

「なるほど。その情報をベースに、どの発言へのリプライであるかを順番に辿れるようになっていれば、否認防止の話に出てきたような独自のプロトコルが作れそうだね」

「それじゃあ、どんな構成になるか、ちょっとコードにしてみよう」

「うん。こんな感じでどうだろう?ブロックチェーンに保存すると、それは石板に彫り込んだ文字のように消せないよという意味を込めて、EngravedMessageという呼び方はどうかな?」

Engraved形式のTransactionの構成例

  class EngravedTransaction:
      """
      メッセージをブロックチェーンに刻み込むための拡張Transactionタイプ
      各Transactionには後でSenderの秘密鍵で署名をつける
      """
      def __init__(self, sender, sender_alt_name, message,
                    icon_url=None, reply_to=None, original_reply_to=None):
          self.sender = sender
          self.sender_alt_name = sender_alt_name
          self.icon =  icon_url
          self.message = message
          self.timestamp = time()
          self.reply_to = reply_to
          self.original_reply_to = original_reply_to
          self.content_id = sender + str(self.timestamp)
          self.t_type = 'engraved'

      def to_dict(self):
          d = {
              'sender': self.sender,
              'sender_alt_name': self.sender_alt_name,
              'icon': self.icon,
              'timestamp': self.timestamp,
              'message' : self.message,
              'reply_to': self.reply_to,
              'original_reply_to': self.original_reply_to,
              'id': self.content_id,
              't_type': self.t_type,
          }

          return d

reply_tooriginal_reply_toは何が違うのかな?」

図7.4でいうと、original_reply_toはリプライが開始された起点となるメッセージのIDで、reply_toは直接的にリプライ先となっているメッセージのIDになるよ」

reply_toとoriginal_reply_to

図7.4: reply_toとoriginal_reply_to

「ふむ。関連するメッセージをまとめやすいような工夫を入れてるってことか。そして送信者のIDには公開鍵情報を使うことで、メッセージのIDもタイムスタンプと公開鍵の組み合わせでユニークに定まるようにしたっていうわけか」

「どうだろう?」

「いいと思うよ。私が作ったとしても大して変わらない形になるだろう。もちろん、本当にサービスとして使うとなれば、文字数の上限など、いろいろ考えるべき問題はある」

「あー、文字数か」

「まあ、いまは考えなくていいだろう。とりあえずは、SimpleBitcoinのWalletのメニューバーに機能を追加していく感じで、これらの機能を組み込んでごらん」

「それならKeyManagerも共用できるし、ざっと作るならそれが一番手っ取り早そうだね」

ブロックチェーンにメッセージを記録する機能をwalletに追加する

  def engrave_message(self):
      """
      ブロックチェーンにTwitter風のメッセージを格納する
      """
      def send_e_message():
          new_message = {}
          msg_txt = entry.get().encode('utf-8')

          msg = EngravedTransaction(
              self.km.my_address(),
              'Testman',
              binascii.hexlify(base64.b64encode(msg_txt)).decode('ascii')
          )

          to_be_signed = json.dumps(msg.to_dict(), sort_keys=True)
          signed = self.km.compute_digital_signature(to_be_signed)
          new_tx = json.loads(to_be_signed)
          new_tx['signature'] = signed
          tx_strings = json.dumps(new_tx)
          self.c_core.send_message_to_my_core_node(
                                  MSG_NEW_TRANSACTION, tx_strings)
          f.destroy()

      f = Tk()
      f.title('Engrave New Message')
      label0 = Label(f, text='Any idea?')
      frame1 = ttk.Frame(f)
      label = ttk.Label(frame1, text='Message:')
      entry = ttk.Entry(frame1, width=30)
      button1 = ttk.Button(frame1, text='Engrave this on Blockchain',
                            command=send_e_message)

      label0.grid(row=0,column=0,sticky=(N,E,S,W))
      frame1.grid(row=1,column=0,sticky=(N,E,S,W))
      label.grid(row=2,column=0,sticky=E)
      entry.grid(row=2,column=1,sticky=W)
      button1.grid(row=3,column=1,sticky=W)

「ふむ。テキストを入力してボタンを押すと、Transactionが発行されてサーバーへ送信されるという流れだね」

「うん。送金Transactionを生成する場合と基本的にはそんなに変わらないね」

「次は、このTransactionを受けたサーバー側の動作について考えていこう。現状の実装だと、これを受け取ったときの処理はどうなるかな?」

「送金Transactionかそれ以外かの判定は作ってたけど、それ以外の場合はエラー扱いか無視になるね」

「となると、そこを修正していかなければならないね。気をつけるべき箇所はどこだろう?」

「送信されてきたTransactionの有効性をチェックする処理が必要だよね。それ以外にも、ブロックに格納された後で他のサーバーから通知されたTransactionでもエラーを出さないようにするための処理が必要になると思う。まとめると、大きく分けて確認ポイントは次の2つかな」

2つの確認ポイント

図: 2つの確認ポイント

  1. Wallet、あるいは他のサーバーから送信されてきたTransactionの有効性チェック
  2. 他のサーバーから送られてきた新規ブロックの中に格納されているTransactionの有効性チェック

「それじゃあ、1つめの確認ポイント、送金以外のTransactionの有効性チェックはどういうふうにすればいいと思う?」

「Transactionの種別が増えるたびにチェックの機能を追加していくのも大変だし、実際のところ送信者本人がその情報を作ったことさえ確認できればいいわけだから、署名の有効性だけでいいんじゃないかな」

「つまり、この先どんなTransactionの種別が増えたとしても、共通ルールとして公開鍵を保存する場所はsender、署名を格納する場所はsignatureという約束にしようというわけだね」

「そうそう。コードにすると、RSAUtilにこんな感じの処理を追加するイメージかな」

送金以外のTransactionの署名検証ロジック

  def verify_general_transaction_sig(self, transaction):
      """
      simple_bitcoin 以外のTransactionも署名の形式を統一することで検証を可能にしておく
      """
      print('verify_general_transaction_sig was called')
      sender_pubkey_text = transaction['sender']
      signature = transaction['signature']
      c_transaction = copy.deepcopy(transaction)
      del c_transaction['signature']
      target_txt = json.dumps(c_transaction, sort_keys=True)
      sender_pubkey = RSA.importKey(binascii.unhexlify(sender_pubkey_text))
      result = self.verify_signature(target_txt, signature, sender_pubkey)
      return result

「この検証処理を、Transaction受信時の処理と、新規ブロック受信時の検証処理の2つでどんなふうに使うことになるのか、実際に作ってみようか」

「うん。1つめのTransaction受信時の処理は、ServerCore__handle_message内でやればいいから、簡単だね」

ServerCore__handle_message内の機能追加

  if msg[2] == MSG_NEW_TRANSACTION:
      new_transaction = json.loads(msg[4])
      <中略>
      if current_transactions != None:
          for t in current_transactions:
              if t == new_transaction:
                  print('this is already pooled transaction: ', t)
                  return

          if is_sbc_t is not True:
              print('this is not SimpleBitcoin transaction: ', new_transaction)
              is_verified = self.rsa_util.verify_general_transaction_sig(
                                                        new_transaction)
              if is_verified is not True:
                  print('Transaction Verification Error')
                  return
          else:
      <中略>
          self.tp.set_new_transaction(new_transaction)

          if is_core is not True:
              m_type = MSG_NEW_TRANSACTION
              new_message = self.cm.get_message_text(m_type,
                                              json.dumps(new_transaction))
              self.cm.send_msg_to_all_peer(new_message)
      else:
          if is_sbc_t is not True:
              print('this is not SimpleBitcoin transaction: ', new_transaction)
              is_verified = self.rsa_util.verify_general_transaction_sig(
                                                          new_transaction)
              if is_verified is not True:
                  return
          else:
      <以下略>

「2つめの確認ポイント、ブロック受信時の処理はどうだろう?」

「ブロックの中からTransactionを1つずつ取り出して、検証処理を担当してるServerCorecheck_transactions_in_new_blockの中に入れ込むのがいいと思う」

ServerCorecheck_transactions_in_new_block内の機能追加

  def check_transactions_in_new_block(self, block):
      """
      ブロック内のTrasnactionに不正がないか確認する
      """
      <中略>
          is_sbc_t, t_type = self.um.is_sbc_transaction(t)
          if is_sbc_t:
              if t_type == 'basic':
      <中略>
          else:
              is_verified = self.rsa_util.verify_general_transaction_sig(t)
              if is_verified is not True:
                  return False

      print('ok. this block is acceptable.')
      return True

「うん。これで異物として排除されることなく、無事にブロックチェーンに取り込まれるようになった」

「じゃあ次はブロードキャストだね。これは図7.3にもあるとおり、EngravedTransactionを作るタイミングで一緒に配信用の拡張メッセージを作ってしまうのがよさそうに思う」

「そうだね。ただ、拡張メッセージとして流通するデータは、この先、Transactionを意識するものばかりとは限らないぞ」

「どういうこと?」

「ブロックチェーンに保存せず、単にP2Pネットワークを伝送路として使うようなメッセージについても考慮が必要っていうことだよ」

「そうか。今回はたまたま、ブロックチェーンに保存するためのTransactionを発行すると同時にメッセージをブロードキャストしたけれど、メッセージとして送るデータがTransactionを前提としたデータであるとは限らないものね」

「そのとおり。そして、ブロックチェーンに保存する前提でないデータに対して、Transactionの形式に相当する種別を与えるのも不自然だ」

「たしかに……」

「だから、メッセージを受信したときにどういう情報があれば処理しやすいかを考える必要がある」

「そうかー。言われてみれば、メッセージは送信して終わりじゃないんだった。手元に届いたメッセージを処理するための機能も必要だ」

「メッセージに種別を設定し、それを拡張メッセージの中で分別する、という戦略で作ってみようか」

「うん。それなら、さっきのEngravedTransactionを生成するコードにちょっと追加して、こんなふうにすればいいかな」

wallet_appのengrave_message内の機能追加

  def engrave_message(self):
      """
      ブロックチェーンにTwitter風のメッセージを格納する
      """
      def send_e_message():
          # P2PによるブロードキャストとTransactionのハイブリッド
          new_message = {}
          msg_txt = entry.get().encode('utf-8')

          msg = EngravedTransaction(
              self.km.my_address(),
              'Testman',
              binascii.hexlify(base64.b64encode(msg_txt)).decode('ascii')
          )

          to_be_signed = json.dumps(msg.to_dict(), sort_keys=True)
          signed = self.km.compute_digital_signature(to_be_signed)
          new_tx = json.loads(to_be_signed)
          new_tx['signature'] = signed
          tx_strings = json.dumps(new_tx)
          self.c_core.send_message_to_my_core_node(
                                    MSG_NEW_TRANSACTION, tx_strings)
          new_tx2 = copy.deepcopy(new_tx)
          new_tx2['message_type'] = 'engraved'
          tx_strings2 = json.dumps(new_tx2)
          self.c_core.send_message_to_my_core_node(MSG_ENHANCED, tx_strings2)
          f.destroy()

      <以下略>

「よし。残るはこれを受信したときの動作だ」

「サーバーの場合、メッセージの送信元がCoreノードなら自分の配下のEdgeノードにだけ送信、そうでないならP2Pネットワーク上の他のすべてのCoreノードに対してブロードキャストするだけで、特に表示の機能は必要ないから、追加で開発するべきものは特にないかな」

「そうだね。ServerCore__core_apiを含めて、特に拡張すべきところはなさそうだ」

「じゃあ、あとはWallet側の処理だね。実際、受け取ったメッセージを上位のWalletアプリに渡せるように、ClientCore__client_apiからCallbackしてもらうための関数を登録できる必要があるかな」

ClientCore__client_api内の機能追加

  def __client_api(self, request, message):

      if request == 'pass_message_to_client_application':
          print('Client Core API: pass_message_to_client_application')
          self.my_protocol_message_store.append(message)
          # ここ↓で呼び出す関数をClientCoreの初期化プロセス中に登録できる必要がある
          self.mpmh_callback(message)
      elif request == 'api_type':
          return 'client_core_api'
      else:
          print('not implemented api was used')

「いいだろう。ClientCoreの初期化処理も書き直さないと」

「うん。ClientCore__init__はこうなるべき」

ClientCoreの初期化処理

  class ClientCore:
      def __init__(self, my_port=50082, core_host=None, core_port=None,
                    callback=None, mpmh_callback=None):
          self.client_state = STATE_INIT
          print('Initializing ClientCore...')
          self.my_ip = self.__get_myip()
          print('Server IP address is set to ... ', self.my_ip)
          self.my_port = my_port
          self.my_core_host = core_host
          self.my_core_port = core_port
          self.cm = ConnectionManager4Edge(self.my_ip, self.my_port,
                                    core_host, core_port, self.__handle_message)
          self.bb = BlockBuilder()
          my_genesis_block = self.bb.generate_genesis_block()
          self.bm = BlockchainManager(my_genesis_block.to_dict())
          self.callback = callback
          self.mpmh = MyProtocolMessageHandler()
          self.mpm_store = MessageStore()
          self.mpmh_callback = mpmh_callback

「そうだね。じゃあ、最後に、ここでCallbackされる関数の実体をwallet_appの中に追加してみよう」

「受信したものを表示するだけだから、まぁ簡単だよね。本来ならTwitterみたいに受信したものを時系列に並べて表示したほうがいいんだろうけど、TKinter力が足りないので、今回はここまでかな」

受信メッセージを処理するためにwallet_appへ追加する機能

  def get_message_callback(self, target_message):
      print('get_message_callback called!')

      if target_message['message_type'] == 'engraved':
          sender_name = target_message['sender_alt_name']
          msg_body = base64.b64decode(binascii.unhexlify(
            target_message['message'])).decode('utf-8')
          timestamp = datetime.datetime.fromtimestamp(
            int(target_message['timestamp']))
          messagebox.showwarning(
            'You received a new engraved message!',
            '{} :\n {} \n {}'.format(sender_name, msg_body, timestamp))

「ふむ。良さそうだね。では、動作確認をしてみようか」

「りょーかい!」

7.2.2 実際に動作を確認しよう

「次の2点を確認するね。せっかくのブロードキャスト機能の確認だから、今回もサーバー2つにそれぞれWalletが1つずつぶら下がっている状態でやってみようと思う」

動作確認における2つのポイント

図: 動作確認における2つのポイント

「いいね。図にするとそれぞれ次のような状態だね」

EngravedがTransactionとしてブロックチェーンに格納されるのを確認

図7.5: EngravedがTransactionとしてブロックチェーンに格納されるのを確認

Engraveしたものがメッセージとしてブロードキャストされるのを確認

図7.6: Engraveしたものがメッセージとしてブロードキャストされるのを確認

「うん。さっそく実行してみるね。まずはUI上の確認から」

新規Engraved生成画面

図7.7: 新規Engraved生成画面

同報メッセージが届いた状態

図7.8: 同報メッセージが届いた状態

Wallet上でのブロックチェーン確認画面

図7.9: Wallet上でのブロックチェーン確認画面

「メッセージはbase64エンコードされた状態でブロックチェーン上に保存されるから、ちょっと見にくいけど、ブロックチェーンへの保存もブロードキャストもうまく動いているように見えるね。実際に問題なく動いているか、例によってログを見ながら確認していこうか」

「うん。まずはEngravedを生成したWalletアプリのログから見ていくよ」

  python3 Wallet_App.py 50093 10.1.1.126 50090
  SimpleBitcoin client is now activating ...:
    <中略>
  List from Central. Refresh the core node list...
  latest core node list:  {('10.1.1.126', 50090), ('10.1.1.126', 50082)}
  core node list will be going to overwrite
    <中略>
  ↓※①
  Sending...  {"protocol": "simple_bitcoin_protocol",
  "version": "0.1.0", "msg_type": 7, "my_port": 50093,
  "payload": "{\"icon\": null, \"id\": \"30820122300d0...\",
  \"message\": \"35596937343....\",\"original_reply_to\": null,
  \"reply_to\": null,\"sender\": \"30820122300d06092a864886f70d010101....\",
  \"sender_alt_name\": \"Testman\", \"t_type\": \"engraved\",
  \"timestamp\": 1535106735.470977, \"signature\": \"0d74b909f562b....\"}"}
    <中略>
  ↓※②
  Sending...  {"protocol": "simple_bitcoin_protocol",
  "version": "0.1.0", "msg_type": 11, "my_port": 50093,
   "payload": "{\"icon\": null, \"id\": \"30820122300d06092a8....\",
   \"message\": \"35596937....\",\"original_reply_to\": null, \"reply_to\": null,
   \"sender\": \"30820122300d06092a864886....\",
   \"sender_alt_name\": \"Testman\", \"t_type\": \"engraved\",
   \"timestamp\": 1535106735.470977, \"signature\": \"0d74b909f562bef...\",
   \"message_type\": \"engraved\"}"}
    <中略>
  ↓※③
  ok 2 11 50090 {"icon": null, "id": "30820122300d06092a8", "message":"86f70.."
    <中略>
  ↓※④
  Client Core API: pass_message_to_client_application
  ↓※⑤
  get_message_callback called!
  Connected by ..  ('10.1.1.126', 53271)
  <以下略>
  • ①:まず生成したEngravedがTransactionとしてここで送信される
  • ②:次に、①で生成したものが拡張メッセージとして送信される
  • ③:ここでサーバーからブロードキャストの形で②で送信したメッセージが折り返されてくる
  • ④:ここでClientCoreに③で受信したメッセージが引き渡される
  • ⑤:④によるClientCore__client_apiの処理結果としてwallet_appget_message_callbackが呼び出される

「うん。ちゃんと意図どおりの動作になっているようだね」

「じゃあ次は、Engravedを送信したWalletアプリが接続していたサーバーのログを確認してみよう」

  python3 sample_server2.py 50090 10.1.1.126 50082 test
  Initializing server...
  <中略>
  latest core node list:  {('10.1.1.126', 50090), ('10.1.1.126', 50082)}
  <中略>
  ADD request for Edge node was received!!
  Adding edge:  ('10.1.1.126', 50093)
  <中略>
  ↓※①
  received new_transaction {'icon': None, 'id': '30820122300d06092a86488....',
  'message': '355969373434472b343....','original_reply_to': None,
  'reply_to': None,'sender': '30820122300d06.....','sender_alt_name': 'Testman',
  't_type': 'engraved','timestamp': 1535106735.470977,
  'signature': '0d74b909f562bef56fcc4df91.....'}
  engraved
  Waiting for the connection ...
  <中略>
  ↓※②
  received enhanced message {"icon": null, "id": "30820122300d.....",
  "message": "35596937343447....","original_reply_to": null, "reply_to": null,
  "sender": "30820122300d06092.....",
  "sender_alt_name": "Testman", "t_type": "engraved",
  "timestamp": 1535106735.470977,
  "signature": "0d74b909f562bef56fcc4df91.....","message_type": "engraved"}
  <中略>
  ↓※③
  verify_general_transaction_sig was called
  send_msg_to_all_peer was called!
  message will be sent to ...  ('10.1.1.126', 50082)
  <中略>
  verify_signature was called
  True
  ↓※④
  set_new_transaction is called {'icon': None,
  'id': '30820122300d06.....','message': '355969373434472b3....',
  <中略>
  't_type': 'engraved','timestamp': 1535106735.470977, 'signature': '0d74b....'}
  send_msg_to_all_peer was called!
  message will be sent to ...  ('10.1.1.126', 50082)
  ↓※⑤
  send_msg called {"protocol": "simple_bitcoin_protocol",
  "version": "0.1.0", "msg_type": 7, "my_port": 50090,
  "payload": "{\"icon\": null, \"id\": \"30820122300d0609.....\",
  <中略>
  \"timestamp\": 1535106735.470977, \"signature\": \"0d74b909f562bef5.....\"}"}
  send_msg_to_all_edge was called!
  <中略>
  ↓※⑥
  send_msg called {"protocol": "simple_bitcoin_protocol",
  "version": "0.1.0", "msg_type": 11, "my_port": 50090,
  "payload": "{\"icon\": null, \"id\": \"3082012230.....\",
  <中略>
  \"signature\": \"0d74b909f562b....\",\"message_type\": \"engraved\"}"}
  Connected by ..  ('10.1.1.126', 53267)
  <中略>
  ↓※⑦
  new_block:  {'timestamp': 1535106738.4003851, 'transactions': [
  '{"inputs": [], "outputs": [{"recipient": "30820122300d06...","value": 30}],
  <中略>
  ↓※⑧
  OK, this seems valid block
  get_total_fee_on_block is called
  coinbase_transaction
  engraved
 <中略>
  engraved
  verify_general_transaction_sig was called
  verify_signature was called
  True
  ok. this block is acceptable.
  block_check_result :  True
  BlockchainManager: get_hash was called!
  <中略>
  ↓※⑨
  Current Blockchain is ...  [{'transactions': 'AD9B477B4....',
  'genesis_block': True, 'nonce': '718'}, {'timestamp': 1535106738.4003851,
  'transactions': ['{"inputs": [],
  "outputs": [{"recipient": "30820122300d060....", "value": 30}],
  "timestamp": 1535106738.400219, "t_type": "coinbase_transaction"}',
  '{"icon": null, "id": "30820122300d06092a864....",
  "message": "355969....", "original_reply_to": null, "reply_to": null,
  "sender": "30820122300d06092....", "sender_alt_name": "Testman",
  "t_type": "engraved", "timestamp": 1535106735.470977,
  "signature": "0d74b909f562bef56f...."}'],
  'previous_block': '69b9921b393eb65ecd6d55398....', 'nonce': '11010'}]
  check_edges_connection was called
  <以下略>
  • ①:ここで生成されたEngravedTransactionが届く
  • ②:引き続いて拡張メッセージとして送信されたEngravedが届く
  • ③:ここで受信したTransactionの有効性確認が開始される
  • ④:受信したTransactionは検証OKとなり無事にTransactionPoolへ保存される
  • ⑤:受信したTransactionを他のサーバーと共有する
  • ⑥:拡張メッセージがもう一方のサーバーに転送される
  • ⑦:他のサーバーで生成されたブロックを受信する
  • ⑧:受信したブロックの正当性を検証。確認OKとなる
  • ⑨:更新されたブロックチェーンを確認

「ここもちゃんと意図どおりの動作だよ。先にTransactionを受信してるのに、PoW競争で負けてるのが面白い」

「いいね。次は、接続されているもう一方のサーバーのログを確認してみよう」

  python3 sample_server1.py 50082 test
  Initializing server...
  Server IP address is set to ...  10.1.1.126
   <中略>
   ↓※①
  received enhanced message {"icon": null, "id": "....",
  "message": "....",
  "original_reply_to": null, "reply_to": null,
  "sender": "30820122300d060....", "sender_alt_name": "Testman",
  "t_type": "engraved", "timestamp": 1535106735.470977,
  "signature": "0d74b909", "message_type": "engraved"}
    <中略>
    ↓※②
  received new_transaction {'icon': None, 'id': '.470977', 'message': '...',
  'original_reply_to': None, 'reply_to': None, 'sender': '...',
  'sender_alt_name': 'Testman', 't_type': 'engraved',
  'timestamp': 1535106735.470977, 'signature': '0d74b909f562bef56f....'}
  engraved
  Currently, it seems transaction pool is empty...
  Waiting for the connection ...
    <中略>
  verify_general_transaction_sig was called
    ↓※③
  send_msg called {"protocol": "simple_bitcoin_protocol",
  "version": "0.1.0", "msg_type": 11, "my_port": 50082,
  "payload": "{\"icon\": null,
  <中略>
  \"message_type\": \"engraved\"}"}
  verify_signature was called
  True
    ↓※④
  set_new_transaction is called {'icon': None,
  'id': '30820122300d06092a8648....',
  'message': '355969373434472b343....',
  'original_reply_to': None, 'reply_to': None,
  'sender': '3082012230....',
  'sender_alt_name': 'Testman', 't_type': 'engraved',
  'timestamp': 1535106735.470977, 'signature': '0d74b909f562bef5....'}
    ↓※⑤
  json_block : {
    "previous_block":"69b9921b393eb6....","timestamp": 1535106738.4003851,
    "transactions": ["{\"inputs\": [], \"outputs\":
  <中略>
    \"signature\":\"0d74b909f562bef56fcc4df91584f7....\"
    }"]}
    ↓※⑥
  send_msg called {"protocol": "simple_bitcoin_protocol", "version": "0.1.0",
  "msg_type": 8, "my_port": 50082,
  "payload": "{\"timestamp\": 1535106738.4003851,
  \"transactions\": [\"{\\\"inputs\\\": [],
  \\\"outputs\\\": [{\\\"recipient\\\": \\\"30820122300d06092a864886....\\\",
  <中略>
  \"previous_block\": \"69b9921b393eb65ecd6d55398....\", \"nonce\": \"11010\"}"}
  transaction is now refreshed ...  []
    ↓※⑦
  Current Blockchain is ...  [{'transactions': 'AD9B477B42....',
  'genesis_block': True, 'nonce': '718'}, {'timestamp': 1535106738.4003851,
  'transactions': ['{"inputs": [], "outputs": [{"recipient": "30820122300d....",
   "value": 30}], "timestamp": 1535106738.400219,
   "t_type": "coinbase_transaction"}', '{"icon": null, "id": "30820122300....",
   "message": "355....", "original_reply_to": null, "reply_to": null,
   "sender": "308201....", "sender_alt_name": "Testman",
   "t_type": "engraved", "timestamp": 1535106735.470977,
   "signature": "0d74b909f562bef56fcc4df91584f70e43e1....}'],
   'previous_block': '69b9921....', 'nonce': '11010'}]
  <以下略>
  • ①:ここでもう一方のサーバーから拡張メッセージとして送信されたEngravedが届く
  • ②:ここでもう一方のサーバーを経由して新規Transactionが届く
  • ③:拡張メッセージとして受信したEngravedを接続中のEdgeノードであるWalletに対してブロードキャストする
  • ④:受信したTransactionは検証OKとなり無事にTransactionPoolへ保存される
  • ⑤:ブロックの生成開始
  • ⑥:生成されたブロックを他のサーバーに対してブロードキャストする
  • ⑦:更新されたブロックチェーンを確認

「ここも問題なしだね」

「じゃあ、最後にブロードキャストを受信しただけのWalletのログを確認して終わりにしようか」

  python3 Wallet_App.py 50098 10.1.1.126 50082
  SimpleBitcoin client is now activating ...:
  <中略>
  ↓※①
  ok 2 11 50082 {"icon": null,
  "id": "30820122300d06092a864886....","message": "355969373434472b....",
  "original_reply_to": null, "reply_to": null,"sender": "30820122300d06....",
  "sender_alt_name": "Testman", "t_type": "engraved",
  "timestamp": 1535106735.470977,"signature": "0d74b909f562bef56fc....",
  "message_type": "engraved"}
  my_api:  client_core_api
  <中略>
  ↓※②
  Client Core API: pass_message_to_client_application
  <中略>
  ↓※③
  get_message_callback called!
  Connected by ..  ('10.1.1.126', 53268)
  • ①:ここでサーバーから拡張メッセージとして送信されたEngravedが届く
  • ②:①で受信したメッセージがClientCoreに引き渡される
  • ③:②のClientCore__client_apiの処理結果としてwallet_appget_message_callbackが呼び出される

「UI上も問題なかったけど、内部の動作としてもどうやら問題なさそうだね」

「そのようだね。これで新しい形式のTransactionをブロックチェーンに保存する処理と、P2Pネットワークを併用してメッセージをブロードキャストする機能が実現できたわけだ」

「ログ確認の手間はともかく、作業としては大したことなく機能追加できたよね。暗号通貨って、ベースとして拡張しがいのある、とてもよくできた構造をしてるんだなって思った」

「そう思うだろ?さらに暗号通貨のプラットフォームとしての可能性を実感してもらうために、最後にもう1つだけ機能を追加してみようか」

「そんな簡単にできることがまだあるの?」

  • この書籍は無料で各章の半分まで読めます。
  • クラウドファンディングでのご購入、電子版をご購入のお客様は、全文をお読みいただけます。
  • 閲覧にはご購入されたアカウントでのログイン が必要です。
  • 製本版をご購入の方は別途電子版のご購入 が必要となります。