apeescape2.com
  • メイン
  • モバイルデザイン
  • ヒントとツール
  • エンジニアリング管理
  • データサイエンスとデータベース
技術

Crystalプログラミング言語での暗号通貨の作成

この投稿は、内部を調査することによってブロックチェーンの重要な側面を理解するための私の試みです。私は読むことから始めました オリジナルのビットコインホワイトペーパー 、しかし、ブロックチェーンを真に理解する唯一の方法は、新しい暗号通貨を最初から構築することだと感じました。

そのため、新しいCrystalプログラミング言語を使用して暗号通貨を作成することにし、それを吹き替えました CrystalCoin 。この記事では、アルゴリズムの選択、ハッシュの難易度、または同様のトピックについては説明しません。代わりに、具体的な例の詳細に焦点を当てます。これにより、ブロックチェーンの長所と制限について、より深く実践的に理解できるようになります。

まだ読んでいない場合は、アルゴとハッシュの背景について、DemirSelmanovicの記事をご覧になることをお勧めします。 ダミーのための暗号通貨:ビットコインとそれ以降 。



Crystalプログラミング言語を選んだ理由

より良いデモンストレーションのために、私は次のような生産的な言語を使用したかった ルビー パフォーマンスを損なうことなく。暗号通貨には多くの時間のかかる計算があります(つまり 鉱業 そして ハッシュ )、そしてそれが、C ++やJavaなどのコンパイル言語が「実際の」暗号通貨を構築するための選択言語である理由です。そうは言っても、開発を楽しくし、読みやすさを向上させるために、よりクリーンな構文の言語を使用したかったのです。とにかくクリスタルの性能は良い傾向があります。

Crystalプログラミング言語の図

それで、なぜ私は使用することに決めました Crystalプログラミング言語 ? Crystalの構文はRubyの構文に大きく影響を受けているため、私にとっては、読みやすく、書きやすいと感じています。特に経験豊富なRuby開発者にとっては、学習曲線が短いという追加の利点があります。

これは、Crystallangチームが公式ウェブサイトに掲載する方法です。

Cのように速く、Rubyのように滑らかです。

ただし、インタープリター言語であるRubyやJavaScriptとは異なり、Crystalはコンパイルされた言語であるため、はるかに高速になり、メモリフットプリントが小さくなります。ボンネットの下で、それは使用します LLVM ネイティブコードにコンパイルするため。

Crystalも静的に型指定されます。つまり、コンパイラーはコンパイル時に型エラーをキャッチするのに役立ちます。

Crystal言語がこの記事の範囲を超えているため、なぜ素晴らしいと思うのかについては説明しませんが、私の楽観的な見方が納得できない場合は、お気軽にチェックしてください。 この記事 Crystalの可能性のより良い概要については。

ポーターの5つの力のスイッチングコスト

注意: この記事は、オブジェクト指向プログラミング(OOP)の基本をすでに理解していることを前提としています。

ブロックチェーン

それで、ブロックチェーンとは何ですか?これは、デジタル指紋(暗号化ハッシュとも呼ばれます)によってリンクおよび保護されたブロックのリスト(チェーン)です。

それを考える最も簡単な方法は、リンクリストのデータ構造として考えることです。そうは言っても、リンクリストは前の要素への参照を持つ必要があるだけです。ブロックには、前のブロックの識別子に応じた識別子が必要です。つまり、後続のすべてのブロックを再計算せずにブロックを置き換えることはできません。

今のところ、ブロックチェーンは、いくつかのデータがチェーンにリンクされた一連のブロックと考えてください。チェーンは前のブロックのハッシュです。

ブロックチェーン全体は、ブロックチェーンと対話する各ノードに存在します。つまり、ネットワーク内の各ノードにコピーされます。単一のサーバーがそれをホストすることはありませんが、すべて ブロックチェーン開発会社 それを使う、それはそれを作る 分散型 。

はい、これは従来の集中型システムと比較して奇妙です。各ノードには、ブロックチェーン全体のコピーがあります(ビットコインブロックチェーンで> 149 Gb 2017年12月 )。

ハッシュとデジタル署名

それで、このハッシュ関数は何ですか?ハッシュを関数と考えてください。これは、テキスト/オブジェクトを与えると一意のフィンガープリントを返します。入力オブジェクトのわずかな変更でさえ、フィンガープリントを劇的に変更します。

さまざまなハッシュアルゴリズムがあり、この記事ではSHA256を使用します。 Bitcoinで使用されるハッシュアルゴリズム。

SHA256を使用する入力が256ビット未満または256ビットよりはるかに大きい場合でも、常に64の16進文字(256ビット)の長さになります。

入力 ハッシュ化された結果
非常に長いテキスト非常に長いテキスト非常に長いテキスト非常に長いテキスト非常に長いテキスト非常に長いテキスト非常に長いテキスト非常に長いテキスト非常に長いテキスト非常に長いテキスト非常に長いテキスト非常に長いテキスト非常に長いテキスト非常に長いテキスト非常に長いテキスト非常に長いテキスト非常に長いテキスト cf49bbb21c8b7c078165919d7e57c145ccb7f398e7b58d9a3729de368d86294a
サルゲッチュ 2e4e500e20f1358224c08c7fb7d3e0e9a5e4ab7a013bfd6774dfa54d7684dd21
サルゲッチュ。 12075307ce09a6859601ce9d451d385053be80238ea127c5df6e6611eed7c6f0

最後の例では、.を追加するだけであることに注意してください。 (ドット)はハッシュに劇的な変化をもたらしました。

したがって、ブロックチェーンでは、次のブロックにリンクされるハッシュを生成するハッシュアルゴリズムにブロックデータを渡すことによってチェーンが構築され、以降、前のブロックのハッシュにリンクされた一連のブロックが形成されます。

クリスタルで暗号通貨を構築する

それでは、Crystalプロジェクトの作成を開始して、SHA256をビルドしましょう。暗号化。

あなたが持っていると仮定して Crystalプログラミング言語がインストールされています 、CrystalCoinのスケルトンを作成しましょうCrystalの組み込みプロジェクトツールを使用したコードベースcrystal init app [name]:

% crystal init app crystal_coin create crystal_coin/.gitignore create crystal_coin/.editorconfig create crystal_coin/LICENSE create crystal_coin/README.md create crystal_coin/.travis.yml create crystal_coin/shard.yml create crystal_coin/src/crystal_coin.cr create crystal_coin/src/crystal_coin/version.cr create crystal_coin/spec/spec_helper.cr create crystal_coin/spec/crystal_coin_spec.cr Initialized empty Git repository in /Users/eki/code/crystal_coin/.git/

このコマンドは、すでに初期化されたgitリポジトリ、ライセンス、およびreadmeファイルを使用して、プロジェクトの基本構造を作成します。また、テスト用のスタブとshard.ymlが付属しています。プロジェクトを記述し、依存関係を管理するためのファイル。シャードとも呼ばれます。

opensslを追加しましょうシャード、ビルドに必要SHA256アルゴリズム:

# shard.yml dependencies: openssl: github: datanoise/openssl.cr

それが入ったら、ターミナルに戻ってcrystal depsを実行します。これを行うと、プルダウンされますopensslそして私たちが利用するためのその依存関係。

これで、必要なライブラリがコードにインストールされました。まず、Blockを定義します。クラスを作成してから、ハッシュ関数を作成します。

# src/crystal_coin/block.cr require 'openssl' module CrystalCoin class Block def initialize(data : String) @data = data end def hash hash = OpenSSL::Digest.new('SHA256') hash.update(@data) hash.hexdigest end end end puts CrystalCoin::Block.new('Hello, Cryptos!').hash

これで、crystal run crystal src/crystal_coin/block.crを実行してアプリケーションをテストできます。ターミナルから。

crystal_coin [master●] % crystal src/crystal_coin/block.cr 33eedea60b0662c66c289ceba71863a864cf84b00e10002ca1069bf58f9362d5

ブロックチェーンの設計

各ブロックはtimestampで保存されますおよび、オプションで、index。 CrystalCoinでは、両方を保存します。ブロックチェーン全体の整合性を確保するために、各ブロックには自己識別機能があります ハッシュ 。ビットコインと同様に、各ブロックのハッシュは、ブロックの暗号化ハッシュ(index、timestamp、data、および前のブロックのハッシュprevious_hash)になります。今のところ、データは何でもかまいません。

module CrystalCoin class Block property current_hash : String def initialize(index = 0, data = 'data', previous_hash = 'hash') @data = data @index = index @timestamp = Time.now @previous_hash = previous_hash @current_hash = hash_block end private def hash_block hash = OpenSSL::Digest.new('SHA256') hash.update('#{@index}#{@timestamp}#{@data}#{@previous_hash}') hash.hexdigest end end end puts CrystalCoin::Block.new(data: 'Same Data').current_hash

Crystal langでは、Rubyのattr_accessor、attr_getterを置き換えますおよびattr_setter新しいキーワードを持つメソッド:

Rubyキーワード クリスタルキーワード
attr_accessor プロパティ
attr_reader ゲッター
attr_writer セッター

Crystalで気付いたかもしれないもう一つのことは、コードを通じて特定の型についてコンパイラーにヒントを与えたいということです。 Crystalは型を推測しますが、あいまいな場合はいつでも、型を明示的に宣言することもできます。そのため、Stringを追加しましたcurrent_hashのタイプ。

それでは実行しましょうblock.cr 2回、timestampが異なるため、同じデータが異なるハッシュを生成することに注意してください。

crystal_coin [master●] % crystal src/crystal_coin/block.cr 361d0df74e28d37b71f6c5f579ee182dd3d41f73f174dc88c9f2536172d3bb66 crystal_coin [master●] % crystal src/crystal_coin/block.cr b1fafd81ba13fc21598fb083d9429d1b8a7e9a7120dbdacc7e461791b96b9bf3

これでブロック構造ができましたが、ブロックチェーンを作成しています。実際のチェーンを形成するには、ブロックの追加を開始する必要があります。前に述べたように、各ブロックには前のブロックからの情報が必要です。しかし、ブロックチェーンの最初のブロックはどのようにしてそこに到達するのでしょうか?さて、最初のブロック、またはgenesisブロックは、特別なブロック(先行ブロックのないブロック)です。多くの場合、手動で追加するか、独自のロジックを使用して追加できます。

ジェネシスブロックを返す新しい関数を作成します。このブロックはindex=0であり、previous_hashに任意のデータ値と任意の値があります。パラメータ。

メソッドをビルドまたはクラス化しましょうBlock.firstジェネシスブロックを生成します:

module CrystalCoin class Block ... def self.first(data='Genesis Block') Block.new(data: data, previous_hash: '0') end ... end end

そして、p CrystalCoin::Block.firstを使用してテストしてみましょう。

#

これで、 創世記 ブロックには、ブロックチェーンで後続のブロックを生成する関数が必要です。

この関数は、チェーン内の前のブロックをパラメーターとして受け取り、生成されるブロックのデータを作成し、適切なデータを含む新しいブロックを返します。新しいブロックが前のブロックからの情報をハッシュする場合、ブロックチェーンの整合性は新しいブロックごとに増加します。

重要な結果は、連続するすべてのブロックのハッシュを変更せずにブロックを変更できないことです。これは、以下の例で示されています。ブロック44のデータがLOOPから変更された場合EASTにするには、連続するブロックのすべてのハッシュを変更する必要があります。これは、ブロックのハッシュがprevious_hashの値に依存するためです。 (とりわけ)。

クリスタル暗号通貨ハッシュ図

図と地面のゲシュタルト法則

これを行わなかった場合、外部の関係者がデータを変更し、チェーンをまったく新しいものに置き換える方が簡単です。このハッシュチェーンは暗号化の証拠として機能し、ブロックがブロックチェーンに追加されると、それを置き換えたり削除したりできないようにするのに役立ちます。クラスメソッドを作成しましょうBlock.next:

module CrystalCoin class Block ... def self.next(previous_node, data = 'Transaction Data') Block.new( data: 'Transaction data number (#{previous_node.index + 1})', index: previous_node.index + 1, previous_hash: previous_hash.hash ) end ... end end

一緒に試すために、簡単なブロックチェーンを作成します。リストの最初の要素はジェネシスブロックです。そしてもちろん、後続のブロックを追加する必要があります。 CrystalCoinを示すために、5つの新しいブロックを作成します。

blockchain = [ CrystalCoin::Block.first ] previous_block = blockchain[0] 5.times do new_block = CrystalCoin::Block.next(previous_block: previous_block) blockchain << new_block previous_block = new_block end p blockchain [#, #, #, #, #, #

プルーフオブワーク

プルーフオブワークアルゴリズム(PoW)は、新しいブロックを作成する方法、または 採掘 ブロックチェーン上。 PoWの目標は、問題を解決する数を見つけることです。番号を見つけるのは難しいですが、ネットワーク上の誰もが計算で簡単に確認できる必要があります。これは、プルーフオブワークの背後にある中心的なアイデアです。

すべてが明確であることを確認するために、例を使ってデモンストレーションしましょう。ある整数xに別のyを掛けたハッシュは、00で始まる必要があると想定します。そう:

hash(x * y) = 00ac23dc...

そして、この単純化された例では、x=5を修正しましょう。これをCrystalに実装します。

x = 5 y = 0 while hash((x*y).to_s)[0..1] != '00' y += 1 end puts 'The solution is y = #{y}' puts 'Hash(#{x}*#{y}) = #{hash((x*y).to_s)}'

コードを実行してみましょう:

crystal_coin [master●●] % time crystal src/crystal_coin/pow.cr The solution is y = 530 Hash(5*530) = 00150bc11aeeaa3cdbdc1e27085b0f6c584c27e05f255e303898dcd12426f110 crystal src/crystal_coin/pow.cr 1.53s user 0.23s system 160% cpu 1.092 total

ご覧のとおり、この番号y=530見つけるのは困難でしたが(ブルートフォース)、ハッシュ関数を使用して簡単に確認できました。

なぜこのPoWアルゴリズムを気にするのですか?ブロックごとに1つのハッシュを作成するだけではどうでしょうか。ハッシュは 有効 。この場合、ハッシュの最初の2文字が00であれば、ハッシュは有効です。ハッシュが00......で始まる場合、それは有効であると見なされます。これは、 困難 。難易度が高いほど、有効なハッシュを取得するのに時間がかかります。

ただし、ハッシュが最初に有効でない場合は、使用するデータに何か変更を加える必要があります。同じデータを何度も使用すると、同じハッシュが何度も取得され、ハッシュが有効になることはありません。 nonceと呼ばれるものを使用しますハッシュ内(前の例ではy)。これは、ハッシュが無効になるたびにインクリメントする単純な数値です。データ(日付、メッセージ、前のハッシュ、インデックス)と1のナンスを取得します。これらで取得したハッシュが有効でない場合は、2のノンスで試行します。有効なハッシュを取得するまでノンスをインクリメントします。 。

ビットコインでは、プルーフオブワークアルゴリズムは ハッシュキャッシュ 。ブロッククラスにプルーフオブワークを追加して始めましょう 鉱業 ナンスを見つけるために。ハードコードされたものを使用します 困難 2つの先行ゼロ「00」の:

それをサポートするために、Blockクラスを再設計しましょう。私たちのCrystalCoinブロックには次の属性が含まれます。

1) index: indicates the index of the block ex: 0,1 2) timestamp: timestamp in epoch, number of seconds since 1 Jan 1970 3) data: the actual data that needs to be stored on the blockchain. 4) previous_hash: the hash of the previous block, this is the chain/link between the blocks 5) nonce: this is the number that is to be mined/found. 6) current_hash: The hash value of the current block, this is generated by combining all the above attributes and passing it to a hashing algorithm

画像の代替テキスト

ハッシュを実行してnonceを見つけるための別のモジュールを作成しますそのため、コードをクリーンでモジュール式に保ちます。私はそれをproof_of_work.crと呼びます:

require 'openssl' module CrystalCoin module ProofOfWork private def proof_of_work(difficulty = '00') nonce = 0 loop do hash = calc_hash_with_nonce(nonce) if hash[0..1] == difficulty return nonce else nonce += 1 end end end private def calc_hash_with_nonce(nonce = 0) sha = OpenSSL::Digest.new('SHA256') sha.update('#{nonce}#{@index}#{@timestamp}#{@data}#{@previous_hash}') sha.hexdigest end end end

私たちのBlockクラスは次のようになります。

require './proof_of_work' module CrystalCoin class Block include ProofOfWork property current_hash : String property index : Int32 property nonce : Int32 property previous_hash : String def initialize(index = 0, data = 'data', previous_hash = 'hash') @data = data @index = index @timestamp = Time.now @previous_hash = previous_hash @nonce = proof_of_work @current_hash = calc_hash_with_nonce(@nonce) end def self.first(data = 'Genesis Block') Block.new(data: data, previous_hash: '0') end def self.next(previous_block, data = 'Transaction Data') Block.new( data: 'Transaction data number (#{previous_block.index + 1})', index: previous_block.index + 1, previous_hash: previous_block.current_hash ) end end end

一般的なCrystalコードとCrystal言語の例について注意すべき点はほとんどありません。 Crystalでは、メソッドはデフォルトでパブリックです。 Crystalでは、各プライベートメソッドの前にprivateキーワードを付ける必要があります。これは、Rubyからの混乱を招く可能性があります。

Crystalの整数型にはInt8、Int16、Int32、Int64、UInt8、UInt16、| _ + _があることに気づいたかもしれません。 |、またはUInt32 RubyのUInt64と比較して。 Fixnumおよびtrue falseの値ですクラスの値ではなくクラスBoolまたはTrueClass Rubyで。

Crystalには、コア言語機能としてオプションの名前付きメソッド引数があり、引数を処理するための特別なコードを記述する必要はありません。これは非常に優れています。チェックアウトFalseClass次に、Block#initialize(index = 0, data = 'data', previous_hash = 'hash')のようなもので呼び出します。

CrystalとRubyプログラミング言語の違いの詳細なリストについては、チェックアウトしてください。 Rubyistsのためのクリスタル 。

それでは、以下を使用して5つのトランザクションを作成してみましょう。

Block.new(data: data, previous_hash: '0') blockchain = [ CrystalCoin::Block.first ] puts blockchain.inspect previous_block = blockchain[0] 5.times do |i| new_block = CrystalCoin::Block.next(previous_block: previous_block) blockchain << new_block previous_block = new_block puts new_block.inspect end

違いを見ます?これで、すべてのハッシュは[#] # # # # # で始まります。それがプルーフオブワークの魔法です。 00を使用するProofOfWorkが見つかりました証明は、一致する難易度のハッシュ、つまり2つの先行ゼロnonceです。

作成した最初のブロックで、一致するラッキーナンバーが見つかるまで17ナンスを試しました。

ブロック ループ/ハッシュ計算の数
#0 17
#1 24
#2 61
#3 149
#4 570
#5 475

それでは、4つの先行ゼロ(00)の難易度を試してみましょう。

ブロック ループ/ハッシュ計算の数
#1 26 762
#2 68 419
#3 23 416
#4 15 353

最初のブロックでは、一致するラッキーナンバーが見つかるまで、26762ノンス(難易度「00」の17ノンスと比較)を試しました。

APIとしてのブロックチェーン

ここまでは順調ですね。シンプルなブロックチェーンを作成しましたが、比較的簡単に作成できました。しかし、ここでの問題はdifficulty='0000'です。単一のマシンでのみ実行できます(分散/分散化されていません)。

これから、CrystalCoinのJSONデータの使用を開始します。データはトランザクションになるため、各ブロックのデータフィールドはトランザクションのリストになります。

各トランザクションは、CrystalCoinの詳細を示すJSONオブジェクトになりますコインのsenderコインの、そしてreceiver転送されているCrystalCoinの:

amount

{ 'from': '71238uqirbfh894-random-public-key-a-alkjdflakjfewn204ij', 'to': '93j4ivnqiopvh43-random-public-key-b-qjrgvnoeirbnferinfo', 'amount': 3 } へのいくつかの変更新しいBlockをサポートするクラスフォーマット(以前はtransactionと呼ばれていました)。したがって、混乱を避けて一貫性を維持するために、dataという用語を使用します。 transactionを参照するこれからサンプルアプリケーションに投稿します。

新しい単純なクラスを紹介しますdata:

Transaction

トランザクションはブロックにパックされるため、ブロックには1つまたは複数のトランザクションを含めることができます。トランザクションを含むブロックは頻繁に生成され、ブロックチェーンに追加されます。

ブロックチェーンは、ブロックのコレクションであると想定されています。すべてのブロックをCrystalリストに保存できるため、新しいクラスmodule CrystalCoin class Block class Transaction property from : String property to : String property amount : Int32 def initialize(@from, @to, @amount) end end end end を導入します。

Blockchain Blockchainがありますおよびchain配列。 uncommitted_transactionsブロックチェーン内のすべてのマイニングされたブロックが含まれ、chainブロックチェーンに追加されていない(まだマイニングされていない)すべてのトランザクションが含まれます。 uncommitted_transactionsを初期化したら、Blockchainを使用してジェネシスブロックを作成します。そしてそれをBlock.firstに追加します配列、そして空のchainを追加しますアレイ。

uncommitted_transactionsを作成しますBlockchain#add_transactionにトランザクションを追加するメソッドアレイ。

新しいuncommitted_transactionsを作成しましょうクラス:

Blockchain

require './block' require './transaction' module CrystalCoin class Blockchain getter chain getter uncommitted_transactions def initialize @chain = [ Block.first ] @uncommitted_transactions = [] of Block::Transaction end def add_transaction(transaction) @uncommitted_transactions << transaction end end end でクラスBlockを使い始めますtransactionsの代わりに:

data

トランザクションがどのようになるかがわかったので、module CrystalCoin class Block include ProofOfWork def initialize(index = 0, transactions = [] of Transaction, previous_hash = 'hash') @transactions = transactions ... end .... def self.next(previous_block, transactions = [] of Transaction) Block.new( transactions: transactions, index: previous_block.index + 1, previous_hash: previous_block.current_hash ) end end end と呼ばれるブロックチェーンネットワーク内のコンピューターの1つにトランザクションを追加する方法が必要です。そのために、単純なHTTPサーバーを作成します。

4つのエンドポイントを作成します。

  • [POST] node:ブロックへの新しいトランザクションを作成します
  • [GET] /transactions/new:サーバーに新しいブロックをマイニングするように指示します。
  • [GET] /mine:/chainで完全なブロックチェーンを返しますフォーマット。
  • [GET] JSON:保留中のトランザクションを返します(/pending)。

使用します 成熟 Webフレームワーク。これは、エンドポイントをCrystal関数に簡単にマッピングできるようにするマイクロフレームワークです。ケマルは影響を強く受けています シナトラ Rubyistsのために、非常によく似た方法で動作します。あなたが探しているなら Ruby on Rails 同等の次にチェックアウト アンバー 。

サーバーは、ブロックチェーンネットワーク内で単一のノードを形成します。最初にuncommitted_transactionsを追加しましょうKemalへとしてファイルし、依存関係をインストールします。

shard.yml

それでは、HTTPサーバーのスケルトンを構築しましょう。

dependencies: kemal: github: kemalcr/kemal

そしてサーバーを実行します:

# src/server.cr require 'kemal' require './crystal_coin' # Generate a globally unique address for this node node_identifier = UUID.random.to_s # Create our Blockchain blockchain = Blockchain.new get '/chain' do 'Send the blockchain as json objects' end get '/mine' do 'We'll mine a new Block' end get '/pending' do 'Send pending transactions as json objects' end post '/transactions/new' do 'We'll add a new transaction' end Kemal.run

サーバーが正常に機能していることを確認しましょう。

crystal_coin [master●●] % crystal run src/server.cr [development] Kemal is ready to lead at http://0.0.0.0:3000

ここまでは順調ですね。これで、各エンドポイントの実装に進むことができます。 % curl http://0.0.0.0:3000/chain Send the blockchain as json objects% を実装することから始めましょうおよび/transactions/newエンドポイント:

pending

簡単な実装。 get '/pending' do { transactions: blockchain.uncommitted_transactions }.to_json end post '/transactions/new' do |env| transaction = CrystalCoin::Block::Transaction.new( from: env.params.json['from'].as(String), to: env.params.json['to'].as(String), amount: env.params.json['amount'].as(Int64) ) blockchain.add_transaction(transaction) 'Transaction #{transaction} has been added to the node' end を作成するだけですオブジェクトを作成し、トランザクションをCrystalCoin::Block::Transactionに追加しますuncommitted_transactionsを使用した配列。

c法人とsの違い

現時点では、トランザクションは最初はBlockchain#add_transactionのプールに保存されます。未確認のトランザクションをブロックに入れ、プルーフオブワーク(PoW)を計算するプロセスは、 鉱業 ブロックの。一度uncommitted_transactions制約を満たすことがわかったので、ブロックがマイニングされ、新しいブロックがブロックチェーンに配置されたと言えます。

nonceでは、前に作成した単純なプルーフオブワークアルゴリズムを使用します。新しいブロックを作成するには、マイナーのコンピューターは次のことを行う必要があります。

  • CrystalCoinの最後のブロックを見つけます。
  • 保留中のトランザクションを検索します(chain)。
  • uncommitted_transactionsを使用して新しいブロックを作成します。
  • マイニングされたブロックをBlock.nextに追加しますアレイ。
  • クリーンアップchainアレイ。

したがって、uncommitted_transactionsを実装するにはエンドポイントでは、最初に上記の手順を/mineに実装しましょう。

Blockchain#mine

まず、マイニングする保留中のトランザクションがあることを確認します。次に、module CrystalCoin class Blockchain include Consensus BLOCK_SIZE = 25 ... def mine raise 'No transactions to be mined' if @uncommitted_transactions.empty? new_block = Block.next( previous_block: @chain.last, transactions: @uncommitted_transactions.shift(BLOCK_SIZE) ) @chain << new_block end end end を使用して最後のブロックを取得し、最初の@chain.lastを取得します。マイニングされるトランザクション(25を使用して最初の25 Array#shift(BLOCK_SIZE)の配列を返し、インデックス0から始まる要素を削除します)。

それでは、uncommitted_transactionsを実装しましょう終点:

/mine

そしてget '/mine' do blockchain.mine 'Block with index=#{blockchain.chain.last.index} is mined.' end のために終点:

/chain

ブロックチェーンとの相互作用

get '/chain' do { chain: blockchain.chain }.to_json end を使用しますネットワークを介してAPIとやり取りします。

まず、サーバーを起動しましょう。

cURL

次に、crystal_coin [master] % crystal run src/server.cr [development] Kemal is ready to lead at http://0.0.0.0:3000 を作成して、2つの新しいトランザクションを作成しましょう。 POSTへのリクエストトランザクション構造を含む本文:

http://localhost:3000/transactions/new

次に、保留中のトランザクション(つまり、まだブロックに追加されていないトランザクション)を一覧表示します。

crystal_coin [master●] % curl -X POST http://0.0.0.0:3000/transactions/new -H 'Content-Type: application/json' -d '{'from': 'eki', 'to':'iron_man', 'amount': 1000}' Transaction # has been added to the node% crystal_coin [master●] % curl -X POST http://0.0.0.0:3000/transactions/new -H 'Content-Type: application/json' -d '{'from': 'eki', 'to':'hulk', 'amount': 700}' Transaction # has been added to the node%

ご覧のとおり、前に作成した2つのトランザクションがcrystal_coin [master●] % curl http://0.0.0.0:3000/pendings { 'transactions':[ { 'from':'ekis', 'to':'huslks', 'amount':7090 }, { 'from':'ekis', 'to':'huslks', 'amount':70900 } ] } に追加されています。

さあ、 私の uncommitted_transactionsを作成することによる2つのトランザクションGETへのリクエスト:

http://0.0.0.0:3000/mine

最初のブロックを正常にマイニングし、それをcrystal_coin [master●] % curl http://0.0.0.0:3000/mine Block with index=1 is mined. に追加したようです。 chainを再確認しましょうマイニングされたブロックが含まれている場合:

chain

コンセンサスと地方分権

これはカッコいい。トランザクションを受け入れ、新しいブロックをマイニングできる基本的なブロックチェーンを手に入れました。ただし、これまでに実装したコードは1台のコンピューターで実行することを目的としていますが、ブロックチェーンの要点は分散化する必要があるということです。しかし、それらが分散化されている場合、それらがすべて同じチェーンを反映するようにするにはどうすればよいでしょうか。

これがcrystal_coin [master●] % curl http://0.0.0.0:3000/chain { 'chain': [ { 'index': 0, 'current_hash': '00d469d383005b4303cfa7321c02478ce76182564af5d16e1a10d87e31e2cb30', 'nonce': 363, 'previous_hash': '0', 'transactions': [ ], 'timestamp': '2018-05-23T01:59:52+0300' }, { 'index': 1, 'current_hash': '003c05da32d3672670ba1e25ecb067b5bc407e1d5f8733b5e33d1039de1a9bf1', 'nonce': 320, 'previous_hash': '00d469d383005b4303cfa7321c02478ce76182564af5d16e1a10d87e31e2cb30', 'transactions': [ { 'from': 'ekis', 'to': 'huslks', 'amount': 7090 }, { 'from': 'ekis', 'to': 'huslks', 'amount': 70900 } ], 'timestamp': '2018-05-23T02:02:38+0300' } ] } の問題です。

ネットワークに複数のノードが必要な場合は、コンセンサスアルゴリズムを実装する必要があります。

新しいノードの登録

コンセンサスアルゴリズムを実装するには、ネットワーク上の隣接ノードについてノードに通知する方法が必要です。ネットワーク上の各ノードは、ネットワーク上の他のノードのレジストリを保持する必要があります。したがって、より多くのエンドポイントが必要になります。

  • [POST] Consensus:URLの形式で新しいノードのリストを受け入れます。
  • [GET] /nodes/register:競合を解決するコンセンサスアルゴリズムを実装し、ノードに正しいチェーンがあることを確認します。

ブロックチェーンのコンストラクターを変更し、ノードを登録するためのメソッドを提供する必要があります。

/nodes/resolve

--- a/src/crystal_coin/blockchain.cr +++ b/src/crystal_coin/blockchain.cr @@ -7,10 +7,12 @@ module CrystalCoin getter chain getter uncommitted_transactions + getter nodes def initialize @chain = [ Block.first ] @uncommitted_transactions = [] of Block::Transaction + @nodes = Set(String).new [] of String end def add_transaction(transaction) を使用したことに注意してくださいSetのデータ構造ノードのリストを保持するために入力します。これは、新しいノードの追加がべき等であり、特定のノードを何度追加しても、それが1回だけ表示されることを保証する安価な方法です。

それでは、新しいモジュールをStringに追加しましょう。最初のメソッドを実装しますConsensus:

register_node(address)

require 'uri' module CrystalCoin module Consensus def register_node(address : String) uri = URI.parse(address) node_address = '#{uri.scheme}:://#{uri.host}' node_address = '#{node_address}:#{uri.port}' unless uri.port.nil? @nodes.add(node_address) rescue raise 'Invalid URL' end end end 関数は、ノードのURLを解析し、フォーマットします。

そしてここでregister_nodeを作成しましょう終点:

/nodes/register

この実装では、複数のノードで問題が発生する可能性があります。いくつかのノードのチェーンのコピーは異なる場合があります。その場合、システム全体の整合性を維持するために、チェーンのいくつかのバージョンに同意する必要があります。コンセンサスを得る必要があります。

これを解決するために、最も長い有効なチェーンが使用されるチェーンであるというルールを作成します。このアルゴリズムを使用して、ネットワーク内のノード間でコンセンサスに到達します。このアプローチの背後にある理由は、最長のチェーンが、実行された作業の最大量の適切な見積もりであるためです。

画像の代替テキスト

post '/nodes/register' do |env| nodes = env.params.json['nodes'].as(Array) raise 'Empty array' if nodes.empty? nodes.each do |node| blockchain.register_node(node.to_s) end 'New nodes have been added: #{blockchain.nodes}' end

module CrystalCoin module Consensus ... def resolve updated = false @nodes.each do |node| node_chain = parse_chain(node) return unless node_chain.size > @chain.size return unless valid_chain?(node_chain) @chain = node_chain updated = true rescue IO::Timeout puts 'Timeout!' end updated end ... end end に注意してくださいは、すべての隣接ノードをループし、それらのチェーンをダウンロードして、resolveを使用して検証するメソッドです。方法。長さが私たちのチェーンよりも長い有効なチェーンが見つかった場合、私たちは私たちのチェーンを置き換えます。

それでは、valid_chain?を実装しましょうおよびparse_chain()プライベートメソッド:

valid_chain?()

module CrystalCoin module Consensus ... private def parse_chain(node : String) node_url = URI.parse('#{node}/chain') node_chain = HTTP::Client.get(node_url) node_chain = JSON.parse(node_chain.body)['chain'].to_json Array(CrystalCoin::Block).from_json(node_chain) end private def valid_chain?(node_chain) previous_hash = '0' node_chain.each do |block| current_block_hash = block.current_hash block.recalculate_hash return false if current_block_hash != block.current_hash return false if previous_hash != block.previous_hash return false if current_block_hash[0..1] != '00' previous_hash = block.current_hash end return true end end end の場合私達:

  • parse_chain()を発行しますGETを使用したHTTPリクエスト〜HTTP::Client.get終点。
  • /chainを解析します/chainを使用したJSON応答。
  • JSON.parseの配列を抽出しますCrystalCoin::Blockを使用して返されたJSONBLOBからのオブジェクト。

CrystalでJSONを解析する方法は複数あります。推奨される方法は、Crystalの非常に便利なArray(CrystalCoin::Block).from_json(node_chain)を使用することです。以下を提供する機能:

  • JSON.mapping(key_name: Type)を実行して、JSON文字列からそのクラスのインスタンスを作成する方法。
  • Class.from_jsonを実行して、そのクラスのインスタンスをJSON文字列にシリアル化する方法。
  • そのクラスで定義されたキーのゲッターとセッター。

私たちの場合、instance.to_jsonを定義する必要がありましたJSON.mappingでオブジェクト、および削除しましたCrystalCoin::Block次のようなクラスでの使用法:

property

ここで、module CrystalCoin class Block JSON.mapping( index: Int32, current_hash: String, nonce: Int32, previous_hash: String, transactions: Array(Transaction), timestamp: Time ) ... end end について、すべてのブロックを反復処理し、それぞれについて次のことを行います。

  • Blockchain#valid_chain?を使用してブロックのハッシュを再計算しますブロックのハッシュが正しいことを確認します。
Block#recalculate_hash
  • 以前のハッシュと正しくリンクされている各ブロックを確認します。
  • ブロックのハッシュがゼロの数(この場合はmodule CrystalCoin class Block ... def recalculate_hash @nonce = proof_of_work @current_hash = calc_hash_with_nonce(@nonce) end end end )に対して有効であることを確認してください。

そして最後にdifficultyを実装します終点:

00

終わった!あなたは見つけることができます 最終コード GitHubで。

プロジェクトの構造は次のようになります。

デザイン思考が重要な理由
/nodes/resolve

試してみましょう

  • 別のマシンを入手して、ネットワーク上で別のノードを実行します。または、同じマシン上の異なるポートを使用してプロセスをスピンアップします。私の場合、マシン上に2つのノードを作成し、別のポートに2つのノードを作成しました:get '/nodes/resolve' do if blockchain.resolve 'Successfully updated the chain' else 'Current chain is up-to-date' end end およびcrystal_coin [master●] % tree src/ src/ ├── crystal_coin │ ├── block.cr │ ├── blockchain.cr │ ├── consensus.cr │ ├── proof_of_work.cr │ ├── transaction.cr │ └── version.cr ├── crystal_coin.cr └── server.cr 。
  • 以下を使用して、2番目のノードアドレスを最初のノードに登録します。
http://localhost:3000
  • 2番目のノードにトランザクションを追加しましょう。
http://localhost:3001
  • トランザクションを2番目のノードのブロックにマイニングしましょう。
crystal_coin [master●●] % curl -X POST http://0.0.0.0:3000/nodes/register -H 'Content-Type: application/json' -d '{'nodes': ['http://0.0.0.0:3001']}' New nodes have been added: Set{'http://0.0.0.0:3001'}%
  • この時点で、最初のノードには1つのブロック(ジェネシスブロック)のみがあり、2番目のノードには2つのノード(ジェネシスとマイニングされたブロック)があります。
crystal_coin [master●●] % curl -X POST http://0.0.0.0:3001/transactions/new -H 'Content-Type: application/json' -d '{'from': 'eqbal', 'to':'spiderman', 'amount': 100}' Transaction # has been added to the node% crystal_coin [master●●] % curl http://0.0.0.0:3001/mine Block with index=1 is mined.%
  • 私たちの目標は、最初のノードのチェーンを更新して、新しく生成されたブロックを2番目のノードに含めることです。それでは、最初のノードを解決しましょう。
crystal_coin [master●●] % curl http://0.0.0.0:3000/chain {'chain':[{'index':0,'current_hash':'00fe9b1014901e3a00f6d8adc6e9d9c1df03344dda84adaeddc8a1c2287fb062','nonce':157,'previous_hash':'0','transactions':[],'timestamp':'2018-05-24T00:21:45+0300'}]}%

最初のノードのチェーンが更新されたかどうかを確認しましょう。

crystal_coin [master●●] % curl http://0.0.0.0:3001/chain {'chain':[{'index':0,'current_hash':'007635d82950bc4b994a91f8b0b20afb73a3939e660097c9ea8416ad614faf8e','nonce':147,'previous_hash':'0','transactions':[],'timestamp':'2018-05-24T00:21:38+0300'},{'index':1,'current_hash':'00441a4d9a4dfbab0b07acd4c7639e53686944953fa3a6c64d2333a008627f7d','nonce':92,'previous_hash':'007635d82950bc4b994a91f8b0b20afb73a3939e660097c9ea8416ad614faf8e','transactions':[{'from':'eqbal','to':'spiderman','amount':100}],'timestamp':'2018-05-24T00:23:57+0300'}]}%

画像の代替テキスト

やったー!私たちのCrystal言語の例は魅力のように機能します。この長いチュートリアルが非常に明確であることがわかったと思いますが、駄洒落はご容赦ください。

まとめ

このCrystal言語チュートリアルでは、パブリックブロックチェーンの基本について説明しました。従うと、ブロックチェーンを最初から実装し、ユーザーがブロックチェーン上の情報を共有できるようにする簡単なアプリケーションを構築しました。

この時点で、かなりのサイズのブロックチェーンを作成しました。さて、crystal_coin [master●●] % curl http://0.0.0.0:3000/nodes/resolve Successfully updated the chain% 複数のマシンで起動してネットワークを作成でき、実際のcrystal_coin [master●●] % curl http://0.0.0.0:3000/chain {'chain':[{'index':0,'current_hash':'007635d82950bc4b994a91f8b0b20afb73a3939e660097c9ea8416ad614faf8e','nonce':147,'previous_hash':'0','transactions':[],'timestamp':'2018-05-24T00:21:38+0300'},{'index':1,'current_hash':'00441a4d9a4dfbab0b07acd4c7639e53686944953fa3a6c64d2333a008627f7d','nonce':92,'previous_hash':'007635d82950bc4b994a91f8b0b20afb73a3939e660097c9ea8416ad614faf8e','transactions':[{'from':'eqbal','to':'spiderman','amount':100}],'timestamp':'2018-05-24T00:23:57+0300'}]}% 採掘することができます。

これがあなたに何か新しいものを作るきっかけになったと思います。少なくともCrystalプログラミングを詳しく見てみましょう。

注意: このチュートリアルのコードは、実際に使用する準備ができていません。これは一般的なガイドとしてのみ参照してください。

基本を理解する

Crystalプログラミング言語の用途は何ですか?

Crystalは一般的な言語です。 Crystalを使用してRubyでできることは何でもでき、パフォーマンスが向上し、メモリ使用量が少なくなります。 Crystalのセールスポイントの1つは、Cで1行も記述せずに、既存のCライブラリにバインドできるため、Cライブラリとのインターフェイスが簡単なことです。

なぜCrystalプログラミング言語を学ぶ必要があるのですか?

Crystalは、人間が簡単に理解でき、高速プログラムにコンパイルできるプログラミング言語です。これは静的に型付けされ、コンパイルされた言語であり、Rubyと同じくらい読みやすい構文を持ちながら、c / c ++に近いパフォーマンスを実現します。

RubyとCrystalの違いは何ですか?

CrystalにはRubyに似た構文がありますが、Rubyの実装ではなく、異なる言語です。コンパイルされた静的に型付けされた言語であるため、Rubyと比較すると言語にはいくつかの違いがあります。 Rubyを使用しているため、Crystalの学習曲線は低くなっています。

フレームワークはどうですか?

Railsの完全性が気に入った場合は、Amberフレームワークに慣れることができます。シナトラのシンプルさと簡単なカスタマイズがあなたのものであるなら、あなたはケマルでそのシンプルさを見つけるでしょう。

Crystalは有望なプログラミング言語ですか?

これは、今日最も有望なプログラミング言語の1つです。読みやすさと使いやすさで愛されているRuby / Pythonのような構文的に甘い解釈/動的言語と、C / C ++および低レベルシステム言語の生の馬力との間の障壁を打ち破ったようです。

あなた自身のプライベートエクイティファンドを調達するための考慮事項

財務プロセス

あなた自身のプライベートエクイティファンドを調達するための考慮事項
成長する成長:このオープンソースコードを使用して独自のコホート分析を実行します

成長する成長:このオープンソースコードを使用して独自のコホート分析を実行します

データサイエンスとデータベース

人気の投稿
別の次元、新しい習得–アイソメトリックイラストレーションチュートリアル
別の次元、新しい習得–アイソメトリックイラストレーションチュートリアル
TensorFlow入門:機械学習チュートリアル
TensorFlow入門:機械学習チュートリアル
ゲシュタルトの設計原則を探る
ゲシュタルトの設計原則を探る
Webおよび印刷デザインの書体スタイ​​ル
Webおよび印刷デザインの書体スタイ​​ル
退屈なアイコンをオリジナルの傑作にすばやく変換する方法
退屈なアイコンをオリジナルの傑作にすばやく変換する方法
 
42億ドルは合理的ですか?インスタカートの評価を評価する方法
42億ドルは合理的ですか?インスタカートの評価を評価する方法
コウノトリ、パート4:ステートメントの実装とまとめ
コウノトリ、パート4:ステートメントの実装とまとめ
第一印象–UXのオンボーディングガイド
第一印象–UXのオンボーディングガイド
Android DDMS:究極のAndroidコンソールのガイド
Android DDMS:究極のAndroidコンソールのガイド
H-1Bビザ:ホンジュラスからシリコンバレーへのiOS開発者の旅
H-1Bビザ:ホンジュラスからシリコンバレーへのiOS開発者の旅
人気の投稿
  • テレグラムボットの作り方
  • C ++に含める
  • c ++の高度なチュートリアル
  • 不和ボットc ++の作り方
  • 需要の価格弾力性が2を測定する場合、これは消費者が
  • arm vs arm64 vs x86
  • ペン先ファイルとは
カテゴリー
収益性と効率性 投資家と資金調達 プロセスとツール Webフロントエンド バックエンド アジャイル Uxデザイン ツールとチュートリアル 技術 人とチーム

© 2021 | 全著作権所有

apeescape2.com