Help us understand the problem. What is going on with this article?

【Python】10分でイキリ高校生がブロックチェーンを初心者に解説してやる!!!

1. この記事について

1-1. はじめに

 この記事は高校生の私がUdemy:現役シリコンバレーエンジニアが教えるPythonで始めるスクラッチからのブロックチェーン開発入門を受けて感動したので、この記事を読んでいるみなさんにぜひ受講して欲しいという思いで書きました。
 この授業を私は2日で終わらせたので、すでに開発に慣れている方であれば一日で理解し、実装することは十分可能だと思います。
自分が理解するときに役立った記事も適宜紹介していきます。

(いいねしてくれると泣いて喜びます)

1-2. この記事で解説するもの

 この記事では最初にBlockChainについて簡単に説明し、理解してもらいます。
次にBlockChainP2Pネットワークの技術的説明について説明します。
そして最後に、このBlockChainという技術が今後どのようなことに応用できるのかを考察していきます。

Bitcoinを例に解説するので、Bitcoinへの理解にもつながると思います。

必ず、順番に読んでください。途中から読んでも理解は難しいと思います。

1-3. 対象読者

  • BlockChainに興味がある方
  • BlockChainを勉強したけどいまいちわからない方
  • とりあえず記事を読んでいる方
  • 流行りの仮想通貨について詳しく知りたい方

以上の人向けにとにかく1から解説していきます。

1-4.開発環境

Python3.7(Anaconda)

PC: macOS Catalina バージョン10.15(メモリ8GB, Intel Core i5)

開発IDE: PyCharm

2. ブロックチェーンとは

これからブロックチェーンについて説明していきます。

2-1. 暗号通貨とBlockChain

BlockChainの技術的な話は置いといて、まずは暗号通貨を例にBlockChainの立ち位置を説明していきます。
まず、普通の銀行を経由してお金を送金する状況を考えます。以下のAさんからBさんに1000円送る状況を考えてみましょう。
スクリーンショット 2019-10-16 7.08.57.png
 銀行を経由すると、このように銀行がこの送金を承認して送金が完了します。そして、銀行の台帳(データベース)には下のように様々な取引の記録が残されることでしょう。
スクリーンショット 2019-10-16 7.16.34.png

しかし、この銀行を経由した送金では暗黙の了解があります。それは銀行を信用しているということです。
銀行はこの取引を改ざんしないということをみんなが信用しているので、みなさんが銀行経由で送金できるのです。改ざんするというのは、例えば銀行が1000円送るところを500円しか送らないと勝手に書き換えたらこの銀行は信用されなくなり、この銀行で送金する人はいなくなるでしょう。

そして、この場合、銀行が担っていることは主に2つ。

  • この取引が正当かどうか検証する
  • この取引のデータを記録する

ということです。
この取引が正当かどうか検証するというのは、この銀行に登録していないMさんからAさんに10000円送ろうとしても不可能ですよね。また、勝手にAさんの残高を1000000円にしようというのも正当でない取引です。銀行はこのようなことを検証しています。また取引のデータを記録するというのは、先ほどの図のとおりです。

この2つの役割を代替するのがBlockChainです。

そして、ブロックチェーンでは、銀行という中央を設置せずに、参加者全員でその取引が正しいということを検証してあげます。これが大事な部分です。これだけは必ず覚えてください。

では、どのようにして代替するのでしょうか。
技術的な話をしながら説明します。

2-2.P2PとServer/Client

BlockChainはP2Pというネットワークによって支えられています。では、P2Pとはどのようなネットワークなのでしょうか?

2-2.1 Server/Client型ネットワーク

まず、私達が普段使っているネットワークはServer/Clientというネットワークです。Server/Client型ネットワークは以下のようにして取引を行います。

serverclient.png

クライアント側がサーバー側に「データをくれ!」と要求(request)したら、サーバー側はそれに応じてデータを返します。

たとえば、Twitterで考えてみましょう。サーバーはTwitter社が持っており、クライアントは私達のスマホやパソコン等です。Twiiterアプリを開き、一番最初にフォローしてる人のツイートが流れてきます。これは、アプリを開いたときに、クライアント側のスマホがツイッター社のサーバーに向かって、「フォローしてる人のツイートをくれ!」と要求し、それに対しサーバーが「これがデータだよ」とデータを返してタイムラインに流れてくるのです。

この場合、Twitter社のサーバーがダウンしてしまったらどうなるでしょうか?アプリを開いたときに、クライアント側が要求しても、それに応じたデータを返すことができなくなってしまいます。つまり、私達はTwitterとしてのサービスを受けられなくなってしまいます。

次に、Twitter社のサーバーの処理性能がとても低かったらどうでしょうか?アプリを開いたときに、クライアント側が要求しても、サーバー側がデータを探すのにとてつもなく時間がかかってしまいます。すると、私達はTwitterを利用するのに時間がかかったり、時にはサーバーがダウンしてしまい、ストレスを感じてしまいます。

まとめると以下のことがServer/Client型ネットワークの欠点です。

  • サーバーがダウンしてしまうとネットワークを維持できず、サービスを利用できない
  • サーバーの処理性能が低いと、負荷がかかるサービスは提供できない

これでは、いつサーバーがダウンするかわからない状況なので、信頼性が下がります。

この2つの欠点をなくしたのがP2Pネットワークです。

2-2.2 P2P型ネットワーク

P2P.png

P2P型ネットワークは上の図のようになりたっています。もっと詳しく説明していきましょう。

まず、P2Pネットワークでは、先程のようなサーバーやクライアントという役割がありません。逆にいうと、ネットワークに参加しているすべてのコンピュータがサーバーでもありクライアントでもあります。

次に、コンピュータはそれぞれデータを持っていて、そのデータを全員で管理しあっています。なので、この中の一つのコンピュータがダウンしても、ネットワークの運用・維持が可能になっています。そのため、Server/Client型ネットワークよりも信頼性が高くなります。

さらに、ネットワークに参加しているすべてのコンピュータがサーバーとしての役割を果たしているため、ネットワークに参加しているコンピュータが多ければ多いほど、ネットワーク全体としての処理性能が高くなります。

よって、以下のことがP2P型ネットワークの利点であり、それらはServer/Client型ネットワークの欠点を克服します。

  • 1台のコンピュータがダウンしても、ネットワークの運用・維持が可能
  • ネットワーク全体としての処理性能が非常に高い

3.詳しい説明

ここでは、BlockChainの詳しい説明をしていきます。

ブロックチェーン.png

ブロックチェーンは上の図のようにしてデータを格納していきます、と言われてもよくわかりませんね。詳しく説明します。わかりやすくBitcoinを例にして説明していきます。

まず、ブロックとはどのようなものなのでしょうか?

ブロックとは、取引データの証明書です。たとえば、AさんがBさんに1Bitcoin送金した、などです。しかし、前にも説明した通り、送金するためにはその取引データが正しいということを参加者全員でその取引が正しいということを検証してあげなければなりません。その検証の証明書がブロックということです。しかし、この証明書は簡単には改ざんできません。改ざんできてしまうとどうなるかというと、大昔にAさんがBさんに1Bitcoin送金したけど、5年後にAさんはお金に困ってしまったのでその送金をなかったことにした、などができてしまいます。そんなことできたら大変ですよね(大変どころではない)。そのため、必ず改ざんできないようにこの証明書(ブロック)が保存されます。その仕組みを説明しましょう。

はじめに、ブロックのなかには4つの情報が入っています。前のハッシュ値タイムスタンプナンストランザクション、の4つです。それぞれ個別に説明します。

まず、タイムスタンプです。タイムスタンプとはその取引が正しいということを検証した時間です。これにより、いつ(when)この取引が正しいと承認されたかが保証されます。

次にトランザクションです。トランザクションには実際の取引のデータのことです。2-1における銀行の吹き出しのやつです。これか取引データですね。

次に、前のハッシュ値です。ここからもっと丁寧に開発します。ここで、実際のブロックがどのようなものなのか見てみましょう。(これはchainではなく1つのブロックです。ブロックがつながったものがchainです。)

ブロックの中身.png

詳しい中身は置いといて、水色の部分がナンス、黄緑色が前のハッシュ値、紫がタイムスタンプ、ピンクがトランザクションです。これは一つのブロックなのでこのブロックの前にもこのようなブロックがあります(もちろん各数字などは違います)。この前のブロック全体をハッシュ化したものが前のブロックのハッシュ値となります。前のブロックのハッシュ値を次のブロックの情報としていれる理由は、改ざんされないためです。この前のブロックを改ざんしたとしたら、次のブロックに入っている"前のブロックのハッシュ値"の情報が変わるため、このブロックをハッシュ化したもののハッシュ値が変わります。(次の図を参照)

ブロック改ざん.png

つまり、一つのブロックを改ざんすると、それ以降のブロックすべてが変わるということです。それにより、ひとつのブロックだけを書き換える(改ざんする)ということはできなくなっています。

最後にナンスについて説明します。ナンスとは、問題の答えです。では、問題とはなんでしょうか。前に、ブロックチェーンでは、みんなで新しくブロックを追加するときに、その取引が正しいか検証していると説明しました。bitcoinでは、その検証方法をProof of Work(PoW)といいます。ナンスは、PoWの答えになります。あくまでも、Proof of Workはbitcoinにおける検証方法で、他にも検証方法はいくつかあります。例えば、Rippleという通貨では、Proof of Consensusであったり、NEMコインではProof of Importanceが用いられています。(詳しくは"コンセンサスアルゴリズム 一覧"で検索)

このProof of Workでこのブロックが正しいと検証するときにナンスと言うものを利用します。まず、取引が行われた時点では(ブロックを追加しようとする段階では)、まだナンスはわかっていません。わかっているのは、トランザクション(取引の内容)と前のブロックのハッシュ値です。そのため、わかっている2つの情報(トランザクションと前のブロックのハッシュ値)とまだわからない変数のようなナンスを使って導いた計算結果が、ある決められた値になるようなナンスを見つけます。(方程式のような感覚です。transaction +prev_hash+nonce=Ans(Ansはなにか決められた答え)となるようなnonceを探す感じです。厳密には違いますが、感覚的には同じで、この計算方法には逆演算がないためひたすらナンスの値を変えて、偶然一致したらその値が最終的な正しいナンスとなります。)ナンスの値を変えて、正しいナンスを見つける作業をProof of Workです。

マイニングというのは、Bitcoinにおける新規通貨を発行することで、一番最初にProof of Workを成功させた採掘者(マイナー)がその発行したコインをもらえます。

4.BlockChainの実装

まず、実装の流れを説明します。

スクリーンショット 2019-12-11 23.44.45.png

今回は、上の図の①②を説明します。ブロックチェーンサーバーのAPI化とブロックチェーンネットワークの構築はUdemyの動画をご覧くださいUdemyの動画はこちら

ブロックチェーンの作成とウォレットの作成も、ここでは全体の20%分ほどしか説明してないので、是非動画をご覧ください。

まず、設計図となるブロックチェーンクラスを作成します。次にブロックチェーンを成り立たせる、PoWとnonceとmining(これはBitcoinのみ)の実装をします。ここまでが①です。

次に、②でウォレットを作成します。ウォレットとは、仮想通貨の財布です。それだけです。ここでは、個人の財布を識別するブロックチェーンアドレスの生成をします。そして、仮想通貨を取引するためのトランザクションの署名と証明について学びます。

4-1 ブロックチェーンの実装

4-1.1 ブロックチェーンクラスの作成

以下がブロックチェーンの大まかな設計図になります。

これからたくさん出てくる、sender_blockchain_addressrecipient_blockchain_adressは、送り主と受け取り主のことです。それぞれを識別している変数です。

今までの解説を読んでいただけた方なら特に難しいこともないので、コメントと読んでいただければ理解できると思います。

import hashlib
import json
import time
class BlockChain(object):

    def __init__(self, blockchain_address=None):
        self.transaction_pool = []  #まだ認証されていないトランザクション
        self.chain = [] #認証されたトランザクションを保存するブロックチェーン
        self.create_block(0, self.hash({})) 
        self.blockchain_address = blockchain_address

    #ブロックを作るメソッド
    def create_block(self, nonce, previous_hash):        
        """
        新しいブロックの作り方
        1.未完了の取引リスト(transaction_pool)からブロックの情報を手に入れる
        2.ブロックをブロックチェーンに追加する
        3.未完了の取引を空にする
        """
        block = {
            'timestamp': time.time(), #取引の時間
            'transactions': self.transaction_pool, #取引内容(未承認の取引のリストから持ってくる)
            'nonce': nonce, #ナンス
            'previous_hash': previous_hash #前のブロックのハッシュ値
        }
        self.chain.append(block) #ブロックチェーンに追加する
        self.transaction_pool = [] #未承認の取引リストを空にする
        return block

    #ブロックのハッシュ値を求めるメソッド
    def hash(self, block):          
        return hashlib.sha256(json.dumps(block).encode()).hexdigest() #ハッシュ化したものを16進数で返す

    #新しい取引をするメソッド
    #ここで新しい取引をして、その取引がcreate_blockでブロックチェーンに追加される
    def add_transaction(self, sender_blockchain_address,
                        recipient_blockchain_address, value,
                        sender_public_key=None):
        #取引の内容を作る
        transaction = {
            'sender_blockchain_address': sender_blockchain_address, #誰が送ったか
            'recipient_blockchain_address': recipient_blockchain_address, #誰が受け取ったか
            'value': float(value) #値(金額)はいくつか
        }
        #未承認の取引リストに追加する
        self.transaction_pool.append(transaction)
        return True

4-1.2 PoW,nonce,miningの実装

前にも説明した通り、PoW(Proof of Work)はトランザクションと前のブロックのハッシュ値とナンスを用いて、その3つから導き出されるとある文字列が、決められた値になるまでナンスを変え続けることをします。

#決められた値になっているかどうか検証するメソッド
def valid_proof(self, transactions, previous_hash, nonce,):
        guess_block = {
            'transactions': transactions,
            'nonce': nonce,
            'previous_hash': previous_hash
        }
        guess_hash = self.hash(guess_block)
        #前から3文字が000だったらtrueを返し、違ったらfalseを返す
        return guess_hash[:3] == '0'*3

#決められた値になるまでナンスの値を変え続けるメソッド
def proof_of_work(self):
        transactions = self.transaction_pool.copy()
        previous_hash = self.hash(self.chain[-1])
        nonce = 0
        #決められた値になるまでナンスの値を変え続ける
        while self.valid_proof(transactions, previous_hash, nonce) is False:
            nonce += 1
        return nonce

まず、valid_proofメソッドでは先程の説明のトランザクションと前のブロックのハッシュ値とナンスを用いて、その3つから導き出されるとある文字列が、決められた値になるかどうかを検証しています。guess_blockはその仮のブロックで、guess_hashがそのハッシュ値です。guess_hashが3つから導き出される文字列です。もちろん、ハッシュ化しているので逆演算がないです。

guess_hash[:3]というのは、guess_hash文字列の前から3文字ということです。そのため、今回はguess_hashの前から3文字が000となるまでナンスの値を変え続けるということです。

proof_of_workメソッドでは、valid_proofメソッドの返り値がtrueとなるまでナンスの値を変えています。ナンスは0から始まって、答えとなるまでナンスの値が+1されています。valid_proofメソッドとproof_of_workメソッドは2つで一つのようなもんですね。

次にminigについて説明します。前にも説明した通り、マイニングはあくまでもビットコインにおける新規通貨発行をする行為のことです。その新規通貨を発行する条件が、PoWを成功させた人です。

#マイニングの報酬でもらえる通貨は送り主(sender)がいないためあらかじめ決めておく
 MINING_SENDER =  'THIS IS MINING SENDER' 
#マイニングの報酬を決めておく
 MINING_REWAES = 1
#マイニングのメソッド
 def mining(self):
        #マイニングで得られた報酬も一応取引なので未承認の取引リストに追加する
        self.add_transaction(
            sender_blockchain_address=MINING_SENDER,
            recipient_blockchain_address=self.blockchain_address,
            value=MINING_REWARD)
        nonce = self.proof_of_work()
        previous_hash = self.hash(self.chain[-1]) #一つ前のブロックをハッシュ化
        #ナンスと前のブロックのハッシュ値を用いてブロックを作る
        self.create_block(nonce, previous_hash) 
        return True

上のプログラムでわかるように、PoWをしたアドレスにMINING_REWAES分を送金しているだけです。このとき、送金元がMINING_SENDERとなっているのは、マイニングでの新規通貨発行では送金元がいないため、このように定めてあげています。

あとは、その取引をブロックとして保存してあげているだけです。

4-2. ウォレットの作成

ウォレットは、特にブロックチェーンとは関係なく、仮想通貨における財布の役割を担うだけのものなのでトランザクションの署名と証明についてだけ説明します。

ここで、もう少し詳しくブロックチェーンアドレスについて説明します。まず、ブロックチェーンに関わってる人全員に、公開鍵秘密鍵というものがあります。公開鍵と秘密鍵は暗号化のため仕組みです(詳しくはggってください)。この公開鍵と秘密鍵で個人を識別しているので、仮想通貨の取引も誰から誰に送ったという識別をこの公開鍵と秘密鍵の文字列で保存すればいいです。しかし、公開鍵と秘密鍵の文字列は非常に長い文字列で、この長い文字列をすべての取引ごとに保存していては、データベースとしてのブロックチェーンの容量が非常に圧迫されてしまいます。なので、もっと短い文字列で個人を識別できるアドレスのようなものがあったらいいですよね。それがブロックチェーンアドレスです。このブロックチェーンアドレスは公開鍵と秘密鍵から生成されます。仕組みは複雑で、僕も理解していない(ほぼコピペ状態)ので、ブロックチェーンの生成方法を知りたい方はUdemyの講座を受講してください。

4-2.1 トランザクションの署名と証明

トランザクションの署名と証明とは、ウォレットからトランザクションをP2Pを構成しているコンピュータに送信する際に、本当にその人から送信されたものなのかをしょめい証明するものです。どういうことかというと、「AさんからBさんに100円送る」という情報をAさんから送られてきたなら正しいのですが、Cさんから送られてきたのでは間違っていますよね。それを証明するものです。
その証明には、Aさんの公開鍵トランザクションSignatureをセットで送ります。Signatureとは、秘密鍵とトランザクションから生成される文字列です。
Sinatureを生成して、公開鍵とトランザクションとSignatureを送信することを署名といいます。
そして、公開鍵とトランザクションとSignatureを受信して正しいものなのかどうか検証することを証明といいます。

class Transaction(object):

    def __init__(self, sender_private_key, sender_public_key,
                 sender_blockchain_address, recipient_blockchain_address,
                 value):
        self.sender_private_key = sender_private_key
        self.sender_public_key = sender_public_key
        self.sender_blockchain_address = sender_blockchain_address
        self.recipient_blockchain_address = recipient_blockchain_address
        self.value = value

    def generate_signature(self):
        sha256 = hashlib.sha256() #sha256を使う
        #トランザクションを作る
        transaction ={ 
            'sender_blockchain_address': self.sender_blockchain_address,
            'recipient_blockchain_address': self.recipient_blockchain_address,
            'value': float(self.value)
        }
        sha256.update(str(transaction).encode('utf-8'))
        message = sha256.digest()
        private_key = SigningKey.from_string(
            bytes().fromhex(self.sender_private_key), curve=NIST256p)
        private_key_sign = private_key.sign(message)
        signature= private_key_sign.hex()
        return signature

↑署名のプログラム

def verify_transaction_signature(
            self, sender_public_key, signature, transaction):
        sha256 = hashlib.sha256()
        sha256.update(str(transaction).encode('utf-8'))
        message = sha256.digest()
        signature_bytes = bytes().fromhex(signature)
        verifying_key = VerifyingKey.from_string(
            bytes().fromhex(sender_public_key), curve=NIST256p)
        verified_key = verifying_key.verify(signature_bytes, message)
        return verified_key

↑証明のプログラム

5.これからのBlockChainの立ち位置

ブロックチェーンを学んだあとだと、なんでこの技術が必要なのかわかると思います。

5-1. ブロックチェーンのメリット・デメリット

ここまで記事を読んでくれた方ならわかると思いますが、もう一度ブロックチェーンのメリット・デメリットを確認します。

もっと詳しく書いてある記事があったので読んでみるといいと思います。記事はこちら

メリットはまず、非中央集権ということです。自分がコンピュータをブロックチェーンのネットワークに参加することによって、一箇所にデータや権力が集中することがなくなります。Facebookがすべてのデータを持っているのは、権力が集中していることと同じです。そのデータをみんなで管理し合うことによって権力が分散します。

次に信頼できるネットワークが構築されることです。多数のコンピュータでネットワークを構築しているので、1台のコンピュータでネットワークを構築するより維持率も性能も高くなりますね。

そして、改ざんできないということです。それはブロックに前のブロックをハッシュ化した文字列を含んでいるからです。

デメリットはまず、利用者側の消費電力が大きいということです。PoWはひたすら計算し続けるので、消費電力が大きくなってしまいます。そのため、Bitcoinでのマイニングでは、マイニングにかかる消費電力の電気代が、マイニングでもらえる報酬に見合わないとういことで新規に参加する人はあまりいないようです。

また、これらによりなにかを複製することをできないようにすることもできます。コンピュータによって何でも複製できる時代になったからこそ、複製できない技術に価値が生まれてくると思います。

5-2. ブロックチェーンの事業例

個人的におもしろいとおもった事業を紹介しておきます。

1.Bitcoin
まあみんな知ってる仮想通貨ですね。特に説明はしません。
Bitcoin.org

2.bPassport
Bitflyerが開発中の個人情報を管理するやつです。最近は、会社間での個人情報の無断売買が行われたり問題になっているので、個人がどの会社に個人情報を提供するかをしっかりと選択できるのは大切ですね。
https://crypto.watch.impress.co.jp/docs/event/1210425.html

3.Verisart
veriartはアート作品の画像データをブロックチェーン上に保存することで、偽物と本物を見分けることに役立ちます。
アートとテクノロジーが結びつくのは非常に面白いなぁと思いました。
https://verisart.com

etc
まとまってるリンクを見つけたので紹介しておきます。
https://innovation.mufg.jp/detail/id=322

6.最後に

参考文献には、この記事とは直接関係ないものの自分が勉強するにあたってとても役立った本や記事を紹介します。

6-1.謝辞

この記事を書くきっかけを作ってくれた、Udemy:現役シリコンバレーエンジニアが教えるPythonで始めるスクラッチからのブロックチェーン開発入門酒井潤講師に非常に感謝しています。ぜひ受講してください(n回目)。また、ここまで読んでいただいた読者のみなさま、本当にありがとうございます。記事中に自分の理解不足による誤植や、間違いがありましたらぜひコメントで教えて下さい。

モチベ維持のためにいいねしてくれると泣いて喜びます!!

6-2.参考文献

以下の文献は非常に役に立ったので紹介します。

Peaks:ゼロから創る暗号通貨
 対話形式でブロックチェーン、公開鍵暗号方式、P2Pネットワークについて深くまで説明し、SimpleBitCoinという暗号通貨を1から実装していきます。最終的にはその暗号通貨の問題点を考え、応用先についても考察していきます。Pythonでの開発に慣れている方向けです。

Udemy:現役シリコンバレーエンジニアが教えるPythonで始めるスクラッチからのブロックチェーン開発入門
 基本的なPythonの文法を理解している人を対象に、簡単な暗号通貨を実装します。BlockChainについて理解するならこの講座が一番はやいと思います。

講座内容
ブロックチェーンの仕組みを理解することができます。
Pythonを使ったブロックチェーンの基本となる開発を学べます
ブロックチェーンのトランザクションの認証を理解することできます。
ブロックチェーンを繋げるハッシュの仕組みができます。
プロックチェーンのマイニングでのナンスを導き出すコンセンサスアルゴリズムが理解できます。
ブロックチェーンの分散された合意形成のコンセンサスの仕組みが理解できます。
仮想通貨の送受信の仕組みが理解できます。

NewsPicks:ブロックチェーン入門
 ブロックチェーンの現状と今後について説明しています。技術的に難しい話はせずに、社会的な影響について話しているので読みやすいです。

徹底理解ブロックチェーン ゼロから着実にわかる次世代技術の原則
 コードを用いずに、多彩な比喩を用いてブロックチェーンの説明をしています。非エンジニアがブロックチェーンを理解したい方向けです。問題点や応用先についても深く考察しています。

sa2shun
大学が決まったらしいです、 おめでとう、僕。
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした