『ゼロから創る暗号通貨』
第7章: ここから先のSimpleBitcoin
濵津 誠/hamatz
この章では、これまでに創ってきたSimpleBitcoinを1つのプラットフォームとして見立てることにより、暗号通貨の範疇を越えてどのようなポテンシャルを持っているかを確認していきます。ポイントとなるのは次のような内容です。
みなさんがSimpleBitcoinをこの先どのように育てていくか、それを考えるためのヒントとなる情報を示せれば幸いです。
「今回は、暗号通貨という枠にとどまらず、これまで作ってきたものがどのように利用できるか、さらに先の可能性についてディスカッションしてみよう。このカリキュラムも、いよいよ今回が本当の最後になる」
「うん。実際に作ったから内部の構造は理解できているし、どの辺をいじればどんな拡張ができそうか、僕にも少しは見える気がするよ」
「ほう。どんなことを考えているのか、実に楽しみだ」
「へ、変なプレッシャーをかけないでよ、兄さん!」
「セキュリティについて考えていたとき、SimpleBitcoinの暗号通貨らしくない要素として、送金理由やコメントの送信を可能にする件について触れたのを覚えているかい?まずはその辺りの可能性から考えてみようか」
「そうだね。ここまで作ってきた範囲でいうと、SimpleBitcoinの送金時にはTransactionInput
とTransactionOutput
の組み合わせしか送っていない。そのため、どの公開鍵アドレスからどの公開鍵アドレスへの送金であるか、という情報以上のことはわからなくなっている」
「もともとのSimpleBitcoinの設計目標として、そのことに問題はないだろうか?」
「もともとの狙いは、他社からの承認を価値としてやり取りする、ということだったよね。面白いツイートに対して『いいね!』をもらえた、といった価値がSimpleBitcoinにとってのコインなのだから、コインが贈られた理由を知ることで得られる満足感のようなものも大切だと思う」
「うむ。一般の暗号通貨と違って匿名性にはあまり重きを置かず、むしろコミュニケーションを活性化させるための潤滑油みたいなものとして暗号通貨を捉えたら面白いだろう、という観点だった」
「でも、いまのSimpleBitcoinの実装だと、そういう満足感は置き去りにされてしまってるんだよね……」
「そうだね。SimpleBitcoinでやり取りしようとしている価値は、どちらかというと、名誉とかそういう類のものだといえる。匿名性とは相容れない価値かもしれない」
「うん。SimpleBitcoinのコインは数えられるポイントだし、お金を出してでも欲しいっていう人が将来は出てくるかもしれないけれど、少なくとも前提は『お金に換算可能な価値ではない』だし、匿名性のためにブロックチェーンを使っているわけでもないんだよなー」
「いい線まできたね。実は、『有限のポイントが不正なくやり取りされているか』を管理するためにブロックチェーンを使っていると捉え直すことで、また別の可能性が開けてくる」
「えっ?それはどういうこと?」
「たとえば、誰かが誰かのツイートやブログの記事に『いいね!』をするとして、はたしてその情報はリアルタイムで届かなければならないものだろうか?」
「そんな必要はないと思うよ。だからこそ、ビットコインとかでは欠点とされる『決済時間の長さ』もSimpleBitcoinでは取り立てて問題になっていなかったし……」
「そのとおり。操作履歴が完全に記録されるデータベースがあって、それが誰か特定のプレイヤーに依存することなくオープンな形で運用されているというのが、ブロックチェーンで実現することの肝だと私は考えている」
「なるほどー。SimpleBitcoinでは、ブロックチェーンのそういう性質を使っていると考えればいいわけか。でも、それが実現できると、何がどう嬉しいの?」
「嬉しさの1つとして、まず考えられるのは、否認防止の機能を実現できることだ」
「否認防止?」
「連続したメッセージのやり取りをすべてブロックチェーン上に記録しておくことで、たとえば、『誰かに何かを依頼してその対価を支払うことを約束した』ということの証拠が残せる。依頼されたことを実行した後で、そんな約束しなかったから対価は払わないよ、と否認されてしまうのを防止できるということさ」
「それって、最初に出てきたスマートコントラクトみたいな話?」
「スマートコントラクトのように完全に自動でできるとはいわないが、後でブロックチェーンを確認することで、『約束をしたのに支払いが実施されていない』という裏付けが取れる。たとえば図7.1に当てはめて考えると、最初のステップで署名付きのソースコードを渡すことにすれば、相手の計算リソースを借りるような処理にも適用可能だ。そしてこの場合、ソースコード自身は外部に置いてURLとハッシュ値だけを置いておくようなスタイルにすれば、相当複雑な処理の依頼も可能となるだろう」
「なるほど。依頼された仕事を処理した瞬間に自動的に支払いが実施される、みたいなところまで賢くはないかもしれないけれど、依頼された仕事の詳細とその結果が後で第三者から検証可能な形で残せることで、少なくとも泣き寝入りの可能性は低減できるわけか……」
「そう。たとえばドイツの鉄道では、改札機がない代わりに車内で抜き打ち検査があって、乗客が切符を持っていない場合には通常運賃の何十倍もの金額を請求される。こうした罰則によってユーザーに不正をさせないようにできるかもしれない。つまり、SimpleBitcoinにはブロックチェーンとして否認防止の機能があるので、『プロトコルに違反して支払いをしないものはアカウントを排除』といったルールを併用することにより、スマートコントラクトまで複雑な仕組みを組み込まずとも、意外と多くの取引にSimpleBitcoinを使えるかもしれない」
「なるほどー。たしかに、送金に関連しないようなTransactionを積極的に置ける場所っていうのは、ブロックチェーンを実装したSimpleBitcoinが目指すあり方っぽいなー」
「もちろん、ほかの使い道もあるだろう。でも、少なくとも私は、否認防止というのは大きな可能性がある機能だろうと思っている」
「うん。なんだか僕もそんな気がしてきた」
「もちろん、ブロックチェーンには否認防止以外の用途もある。ブロックチェーンには、登録されているTransactionが消せないという特性があるだろう?そのため、ある意味では否認防止の真逆の機能というか、自分の発言の証拠を残せるという機能がある。これを活用する道もあるだろう」
「発言の証拠?何が嬉しいの?」
「たとえば、何か新しいアイデアを思いついたとき、それを公開されているブロックチェーン上に記録しておく。そうすれば、誰が最初の発案者であったか、後で時系列を見て判定が可能になる」
「いわゆるパクツイ問題が解決するっていう話?」
「そういうオリジナリティの主張にも使えるだろうし、もっといえば、他人の特許を無効化する理由などとしても使えるだろう」
「誰でも見られてしまう公開のデータベースなので、扱える内容は限られるだろうけど、公開されているからこそ価値があるような状況も考えられるってことか。なるほどー」
「データベースとして見ると、公開情報であることよりも、消せない情報になることのほうが深刻かもしれない。なにしろ、そこに他人の個人情報を書き込もうとするバカな人間もいないとはいえないからな。とはいえ、Transactionに署名を必須とすることで、そういった悪意をもった利用に対する抑止力は設定できるんじゃないかな」
「悪意をもって個人情報なんかを公開した人物の特定にまでは至らなくても、その公開鍵の持ち主をサービスから締め出すといったペナルティを課すことは可能になるもんね。匿名掲示板ほど迂闊には振る舞えないだろうっていうわけか」
「すべてのデータを誰もが見られるわけだから、SNSのようなサービスをSimpleBitcoinに載せてしまうことも考えられるだろう。そのまま完全なTwitterの代替にはならないが、そのオープン性を生かした新しいサービスを作れるかもしれない」
「それ、面白そうだよね。鍵アカウントのないTwitterって感じで」
「それだけじゃない。すべてのデータに誰もがアクセスできるということは、API制限も何もない。アプリを開発する誰もが平等にデータを扱える」
「そうか!アプリ開発者からすると、それは魅力的かもしれないよね。TwitterのAPI利用制限でアプリの開発をやめた、なんて話も聞くくらいだし」
「ただ、ブロックチェーンから直接データを取り出しているとリアルタイム性に欠けるし、検索性もよくない。もし実際にその手のサービスをやろうと思ったら、中間にデータアクセスのための層を挟むことにはなるだろう。だが、それでも新しい体験を生み出す可能性はありそうだ」
「オリジナルの発言者が誰なのか証拠が残るし、逆に、言ってもいないことを言ったと責められたときに『消せないブロックチェーン上にそんな発言は存在していない』という証拠を示すことも可能なTwitterかー。たしかに、似てるけれども少し違うサービスを実現できそうな気がするね!」
「そして、SimpleBitcoinでは、『いいね!』に量の概念を持たせることが可能だったよね」
「そうだった!」
「完全なTwitterクローンを作るのは大変だけど、原理的な部分だけなら実際に作ってみて確認できる。Twitter風のサービスを実現する際の肝となる部分がSimpleBitcoinでどのように実現できるかを、ちょっと確認してみようか」
「おお!実際に動くものを作ってみるんだね!」
「まぁ、本当に基礎の基礎の部分だけだけどね」
「やっぱり動くものを作ってるときが一番楽しいよ!」
「まずは、これから作ろうするサービスについて確認しておこう。大きくは次の2つの基本機能を作ることになる」
「なるほど。話の流れから1つめの機能はなんとなく想像してたけど、2つめの機能については考えてなかった。ブロックチェーンの更新を定期的に確認する代わりに、P2Pネットワークのときに作った分散型掲示板の仕組み(第2章参照)を使って、登録するデータを同時に全体で共有してしまおうってわけか」
「そのとおり。Mastodonの連合タイムラインのように、フォローしている人など関係なしにP2Pネットワークに接続中の全員のツイートが次々に届くようにすると考えればイメージしやすいかもしれないね」
「要するに、これから作る機能は図7.3に示した2つってことだよね?検索とか、フォローしてる人だけをタイムラインに表示するとか、そういうブロックチェーンが苦手とする部分については『中間層』として切り出してしまい、発言の記録とリアルタイム通知だけを実現するって話」
「そのとおりだ。それでは、まず、1つめのブロックチェーンにテキストデータを保存する部分について考えていこう」
「ブロックチェーンに保存する以上は、基本的にはTransactionなんだよね?」
「そうだ。だから考えるべきことはシンプル。Transactionのデータとしてどのような情報要素を持っているべきかを考えればいい」
「うーんと……。Twitterを思い浮かべながら考えると、自由に入力可能な発言者の名前と、実際のシステム上での識別のためのID、それに、本文のテキストくらいかな……」
「どの発言についてのリプライかを示すために、Transactionを識別できる必要があるんじゃないかな?」
「なるほど。その情報をベースに、どの発言へのリプライであるかを順番に辿れるようになっていれば、否認防止の話に出てきたような独自のプロトコルが作れそうだね」
「それじゃあ、どんな構成になるか、ちょっとコードにしてみよう」
「うん。こんな感じでどうだろう?ブロックチェーンに保存すると、それは石板に彫り込んだ文字のように消せないよという意味を込めて、EngravedMessageという呼び方はどうかな?」
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_to
とoriginal_reply_to
は何が違うのかな?」
「図7.4でいうと、original_reply_to
はリプライが開始された起点となるメッセージのIDで、reply_to
は直接的にリプライ先となっているメッセージのIDになるよ」
「ふむ。関連するメッセージをまとめやすいような工夫を入れてるってことか。そして送信者のIDには公開鍵情報を使うことで、メッセージのIDもタイムスタンプと公開鍵の組み合わせでユニークに定まるようにしたっていうわけか」
「どうだろう?」
「いいと思うよ。私が作ったとしても大して変わらない形になるだろう。もちろん、本当にサービスとして使うとなれば、文字数の上限など、いろいろ考えるべき問題はある」
「あー、文字数か」
「まあ、いまは考えなくていいだろう。とりあえずは、SimpleBitcoinのWalletのメニューバーに機能を追加していく感じで、これらの機能を組み込んでごらん」
「それならKeyManager
も共用できるし、ざっと作るならそれが一番手っ取り早そうだね」
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つかな」
「それじゃあ、1つめの確認ポイント、送金以外のTransactionの有効性チェックはどういうふうにすればいいと思う?」
「Transactionの種別が増えるたびにチェックの機能を追加していくのも大変だし、実際のところ送信者本人がその情報を作ったことさえ確認できればいいわけだから、署名の有効性だけでいいんじゃないかな」
「つまり、この先どんなTransactionの種別が増えたとしても、共通ルールとして公開鍵を保存する場所はsender
、署名を格納する場所はsignature
という約束にしようというわけだね」
「そうそう。コードにすると、RSAUtil
にこんな感じの処理を追加するイメージかな」
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
内でやればいいから、簡単だね」
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つずつ取り出して、検証処理を担当してるServerCore
のcheck_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を生成するコードにちょっと追加して、こんなふうにすればいいかな」
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してもらうための関数を登録できる必要があるかな」
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__
はこうなるべき」
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力が足りないので、今回はここまでかな」
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))
「ふむ。良さそうだね。では、動作確認をしてみようか」
「りょーかい!」
「次の2点を確認するね。せっかくのブロードキャスト機能の確認だから、今回もサーバー2つにそれぞれWalletが1つずつぶら下がっている状態でやってみようと思う」
「いいね。図にするとそれぞれ次のような状態だね」
「うん。さっそく実行してみるね。まずはUI上の確認から」
「メッセージは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) <以下略>
ClientCore
に③で受信したメッセージが引き渡されるClientCore
の__client_api
の処理結果としてwallet_app
のget_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
が届くTransactionPool
へ保存される「ここもちゃんと意図どおりの動作だよ。先に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'}] <以下略>
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)
ClientCore
に引き渡されるClientCore
の__client_api
の処理結果としてwallet_app
のget_message_callback
が呼び出される「UI上も問題なかったけど、内部の動作としてもどうやら問題なさそうだね」
「そのようだね。これで新しい形式のTransactionをブロックチェーンに保存する処理と、P2Pネットワークを併用してメッセージをブロードキャストする機能が実現できたわけだ」
「ログ確認の手間はともかく、作業としては大したことなく機能追加できたよね。暗号通貨って、ベースとして拡張しがいのある、とてもよくできた構造をしてるんだなって思った」
「そう思うだろ?さらに暗号通貨のプラットフォームとしての可能性を実感してもらうために、最後にもう1つだけ機能を追加してみようか」
「そんな簡単にできることがまだあるの?」