この記事では、Rubyを使ってシンプルなワーキングブロックチェーンデモを構築する方法を探っていきます。
本ブログは英語版からの翻訳です。オリジナルはこちらからご確認いただけます。一部機械翻訳を使用しております。翻訳の間違いがありましたら、ご指摘いただけると幸いです。
ステージ1:送金
この段階で残高確認と振込を実施します。送金は口座残高に基づいて実行される加算または減算です。
この機能を実装するには、HTTPプロトコルのGETとPOSTが最適です。GETはサーバからデータを取得し、POSTはサーバ上のデータを変更します。
ここでは、UIの表示はHTMLプロトコルを必要としません。RubyのWebフレームワークSinatraを使ってURLや関連するメソッドを整理し、UEを使ってコマンドラインで転送情報を見ることができます。
クライアント側のメソッドとサーバ側のURLは非常にシンプルです。
クライアント: client.rb
def create_user(name) … end
def get_balance(user) … end
def transfer(from, to, amount) ... end
サーバー:haseebcoin.rb
get "/balance" ... end
post "/users" ... end
post "/transfers" ... end
この層に必要な知識:ruby、HTTP GET、POST、Sinatra
ステージ2:ゴシップネットワークの構築
ブロックチェーンには "ゴシッププロトコル "と呼ばれる分散型の構造があります。ここでいう "ゴシップ "とは噂ではなく、分散型ネットワークで拡散される情報のことを指します。
映画の名前が交換されるゴシップネットワークを構築してみましょう。
client.rbは指定されたポートにメッセージを送信します。
def self.gossip(port, state)
...
Faraday.post("#{URL}:#{port}/gossip", state: state).body
...
end
gossip.rbは、送信元ポートと送信先ポートの2つのパラメータを受け取ります。ポート1111や2222など、ソース側の特定のポートを介して情報を交換します。
実際の分散型ネットワークでは、2つのポートは本質的に2つのネットワークノードです。異なるローカルポート間で情報を交換することは、シミュレーションされたネットワークの異なるノード間の通信を表しています。
各ノードで
3 秒ごとに好きな映画の名前を話してください。
every(3.seconds) do
…
gossip_response = Client.gossip(port, JSON.dump(STATE))
update_state(JSON.load(gossip_response))
...
end
8秒ごとにお気に入りの映画名を変更します。
every(8.seconds) do
…
update_state(PORT => [@favorite_movie, @version_number])
...
end
サーバはデータを受信して処理します。
post '/gossip' do
…
update_state(JSON.load(their_state))
…
end
4人のネットワークで
1、最初のノードで gossip.rb 1111 を実行します。最初のノードはポート1111で好きな映画の名前を話します。
2、gossip.rb 2222 1111を実行します。2番目のノードはポート2222で好きなムービー名を最初のノード(ポート1111)に発言します。
3、gossip.rb 3333 2222を実行します。3番目のノードは、ポート3333から2番目のノード(ポート2222)にお気に入りのムービー名を話します。
4、gossip.rb 4444 3333を実行します。4番目のノードは、ポート4444から3番目のノード(ポート3333)にお気に入りのムービー名を話します。
しばらく実行してようやく4つのノードがピアエンドの情報を取得し、データは変化し続けます。これが単純なGossipネットワークです。
ステージ3:データの暗号化と復号化
トップレベルの暗号化アルゴリズムは、ブロックチェーンの基礎となるものです。この層では、ブロックチェーンアカウントを実装するために非対称暗号化技術が使用されています。RSAアルゴリズムは公開鍵、秘密鍵を生成し、非対称暗号化を強制することができます。
def generate_key_pair … end
def sign(plaintext, raw_private_key) ... end
Ruby言語のOpenSSLモジュールのおかげで、非対称暗号化や署名検証を素早く実装することができます。ブロックチェーンでは、公開鍵はアカウント、秘密鍵はパスワードです。鍵のペアはそれぞれ1つのブロックチェーンアカウントになります。
暗号文を復号化する。
def plaintext(ciphertext, raw_public_key) … end
暗号文がメッセージであるかどうかを確認します。
def valid_signature?(message, ciphertext, public_key) … end
この層に必要な知識:非対称暗号化アルゴリズム
ステージ4:データマイニング
この段階では、プルーフ・オブ・ワークが実装され、ブロックチェーン用のブロックが生成されます。これは時間と手間のかかるプロセスです。ハッシュ関数は不可逆的であり、競合はありません。計算プロセスは簡単です。入力に対してハッシュ演算を行うだけで結果が得られる。
入力とは、送金額、送金人の名前、受取人の名前など、送金に関する情報である。ハッシュ演算には様々なアルゴリズムがある。
ここでは、SHA256アルゴリズムを使用します。
def hash(message) … end
同じ情報をハッシュ化すると、毎回違う結果が出てきます。得られた結果が、例えば「0の数桁から始まる」などの特徴を満たすまで演算を続けます。
結果が数桁の0から始まるかどうかを確認します。
def is_valid_nonce?(nonce, message)
hash(message + nonce).start_with?("0" * NUM_ZEROES)
end
上記の条件を満たすための作業を行うことは容易ではない。多くの時間を消費する。このような作業を全てマイニングといいます。
def find_nonce(message)
…
until is_valid_nonce?(nonce, message)
...
end
入力には前回のハッシュ操作の結果が含まれます。したがって、各ハッシュ操作は前のハッシュ操作の影響を受けます。言い換えれば、これはチェーン構造である。これがブロックチェーンと呼ばれる所以です。
ステージ5:最長チェーンルール
この段階では、最初のブロックが初期化され、それに応じてブロックチェーン構造が生成され、ブロックチェーンが形成されます。ブロックチェーンはArray構造に格納されます。保存中に、ブロックは検証を受けなければなりません。
ブロックを初期化します。
def initialize(prev_block, msg)
@msg = msg
@prev_block_hash = prev_block.own_hash if prev_block
mine_block!
end
採掘中に一番やりがいを感じる作業は、nonceを見つけることです。
def mine_block!
@nonce = calc_nonce
@own_hash = hash(full_block(@nonce))
end
完全なブロックはこのように圧縮されます。
def full_block(nonce)
[@msg, @prev_block_hash, nonce].compact.join
end
ブロックチェーンを初期化する: class BlockChain
Arrayを使って保存するだけ!
def initialize(msg)
@blocks = []
@blocks << Block.new(nil, msg)
end
チェーンにブロックを追加する。ブロックチェーン全体が継続的に成長している。
def add_to_chain(msg)
@blocks << Block.new(@blocks.last, msg)
puts @blocks.last
end
ブロックが健全かどうかを厳密に検証する必要があります。
def valid?
@blocks.all? { |block| block.is_a?(Block) } &&
@blocks.all?(&:valid?) &&
@blocks.each_cons(2).all? { |a, b| a.own_hash == b.prev_block_hash }
end
ステージ6. ピースの組み合わせ
最後に、Blockchainは、ネットワーク内のすべてのコンポーネントとの調和のとれたコラボレーションを通じて、その魔法を働かせます。第一段階では、転送はトランザクションクラスであり、情報に署名するために秘密鍵を使用する必要があります。
@signature = PKI.sign(message, priv_key)
最初のブロックを手に入れた鉱夫の報酬は50万枚の銀貨です。
def self.create_genesis_block(pub_key, priv_key)
genesis_txn = Transaction.new(nil, pub_key, 500_000, priv_key)
Block.new(nil, genesis_txn)
end
アカウントに請求された支出が有効かどうかを確認してください。
def all_spends_valid?
compute_balances do |balances, from, to|
return false if balances.values_at(from, to).any? { |bal| bal < 0 }
end
true
end
ネットワークの成長を維持するために未知のノード$PEERSを追加します。
if PEER_PORT.nil?
# You are the progenitor!
$BLOCKCHAIN = BlockChain.new(PUB_KEY, PRIV_KEY)
else
# You're just joining the network.
$PEERS << PEER_PORT
end
ノード間のデータ処理は、ブロックチェーンとPEERを読み込んで更新します。
# @param blockchain
# @param peers
post '/gossip' do
their_blockchain = YAML.load(params['blockchain'])
their_peers = YAML.load(params['peers'])
update_blockchain(their_blockchain)
update_peers(their_peers)
YAML.dump('peers' => $PEERS, 'blockchain' => $BLOCKCHAIN)
end
受信したブロックの処理は、チェーンが長くなっているかどうかに注目しています。
def update_blockchain(their_blockchain)
return if their_blockchain.nil?
return if $BLOCKCHAIN && their_blockchain.length <= $BLOCKCHAIN.length
return unless their_blockchain.valid? $BLOCKCHAIN = their_blockchain
end
新しいものになるまでPEERを更新する。
def update_peers(their_peers)
$PEERS = ($PEERS + their_peers).uniq
end
送金の際には、受取人のpub_keyを取得し、送金人のpub_keyを経由して送金します。
# @param to (port_number)
# @param amount
post '/send_money' do
to = Client.get_pub_key(params['to'])
amount = params['amount'].to_i
$BLOCKCHAIN.add_to_chain(Transaction.new(PUB_KEY, to, amount, PRIV_KEY))
'OK. Block mined!'
end
ブロックチェーンをゴシップネットワークに入れて、すべての機能コンポーネントを組み立てる。これで完成です。ブロックチェーンの作成に成功しました。
このデモについての詳細はGithubで見ることができます: https://github.com/Haseeb-Qureshi/lets-build-a-blockchain
ブロックチェーンやその他の革新的な技術の詳細については、www.alibabacloud.com をご覧ください。
アリババクラウドは日本に2つのデータセンターを有し、世界で60を超えるアベラビリティーゾーンを有するアジア太平洋地域No.1(2019ガートナー)のクラウドインフラ事業者です。
アリババクラウドの詳細は、こちらからご覧ください。
アリババクラウドジャパン公式ページ