Javaに基づく完全なブロックチェーンシステムを開発する(完全なソースコードを使用)

序文

近年、ブロックチェーンの概念はますます普及しており、特にブロックチェーン技術が国のインフラ建設リストに含まれた後、大手企業はまた、主要なブロックからブロックチェーン技術の研究を行うために人々を募集し始めています求人サイト。チェーンのポジションに関しては、給与は非常に高く、月給は30Kから80Kの範囲です。これはプログラマーにとってもチャンスであり、ブロックチェーンテクノロジーの学習が急務であることを示しています。

このブロックチェーンシステムのコードは非常に簡潔で明確であり、初心者にとっては非常に理解しやすいものであり、ブロックチェーン技術はそれほど高度で複雑ではないことをすべての人に伝えることを目的としています。springbootフレームワークを除いて、システムの残りの部分は基本的に純粋なネイティブ開発であり、P2PネットワークでさえJavaソケットで実装されています。

1.ブロックチェーン技術の理論的基礎

1.基本的な考え方

(1)ブロックチェーン

技術的な観点から、ブロックチェーンは、トランザクション情報を含むブロックが時系列で後ろから前に順番にリンクされるデータ構造です。

アプリケーションレベルから見ると、ブロックチェーンは分散型共有台帳およびデータベースであり、分散化、改ざんなし、完全なトレース、一括保守、オープン性、および透明性の特性を備えています。これらの特性に基づいて、ブロックチェーン技術は独自の信頼システム特性を備えたシステムを開発し、複数の主体間の協調的な信頼と協調行動を実現することができます。

ブロックは、ブロックチェーン内の最小単位であり、主にメタデータを含むブロックヘッダーと、1つ以上のトランザクション情報を格納するブロック本体で構成されます。各ブロックは、現在のブロックのハッシュと値を記録します。前のブロックのハッシュ、2つのハッシュ値を関連付けることにより、すべてのブロックをチェーン構造でつなぎ合わせて、完全なブロックチェーンを形成します。

ブロックチェーンの最初のブロックはジェネシスブロックと呼ばれ、前のブロックに関連付ける必要はありません。BTCネットワークを例にとると、各ブロックには主に次の情報フィールドが含まれています。

  • ブロックサイズ:ブロックデータのサイズ(バイト単位)
  • ブロックヘッダー:ブロックヘッダーは、次のフィールドで構成されます
    。1.ブロックヘッダーのハッシュ値2.親ブロックヘッダーのハッシュ値3.タイムスタンプ:ブロックが生成されたおおよその時間4.マークルルート:マークルブロック内のトランザクションのツリールートのハッシュ5.難易度ターゲット:このブロックの作業証明アルゴリズムの難易度ターゲット6. Nonce:作業証明アルゴリズムに使用されるカウンター
  • トランザクションカウンター:トランザクションの数
  • トランザクション:ブロックに記録されたトランザクション情報

次の図に示すように、ブロックチェーン構造の単純なモデル:


ブロックに設定されたトランザクションは、特定の情報を記録します。BTCネットワークでは、メインレコードはトランザクション情報です。他のブロックチェーンネットワークでは、監査情報、著作権情報、請求書情報などのビジネスロジックに従って対応するビジネスデータを保存できます。 、など。これが、ブロックチェーンが共有元帳としてよく使用される理由です。

たとえば、ブロックチェーンは簿記用のノートと見なすことができます。ブロックは、最初のページから最後のページまでの一定期間のすべての会計情報を記録する紙のページに相当します。ページ番号の順序は完全な元帳です。

(2)ブロックチェーンネットワーク

実際のブロックチェーンシステムは複数のブロックチェーンノードで構成され、各ノードは同じブロックチェーンバックボーンネットワークのセットのコピーを実行し、各ノードはP2Pネットワークを介して相互作用し、最終的に完全なブロックチェーンネットワークシステムを形成します。

P2Pネットワークは、信頼性、分散化、開放性を備えており、各ノードはインタラクティブに動作し、相互に連携します。各ノードは、外部にサービスを提供すると同時に、ネットワーク内の他のノードが提供するサービスも使用します。ブロックチェーンノードが新しいブロックを生成すると、他のノードにブロードキャストします。他のノードがネットワークを介してブロック情報を受信すると、ブロック情報を検証します。特定の数のノードがある場合すべての検証に合格すると、各ノードはブロックを既存のブロックチェーンに更新し、最終的にブロックチェーンネットワーク全体の各ノードの情報を一貫性のあるものにします。これは、ブロックチェーンの分散型で信頼できる機能でもあります。

次の図に示すように、ブロックチェーンネットワークの単純なモデル:

2.ブロックチェーン分類

(1)パブリックチェーン

パブリックブロックチェーン(パブリックブロックチェーン)とは、世界中の個人またはグループがトランザクションを送信でき、トランザクションがブロックチェーンの効果的な確認を取得でき、誰でもブロックチェーンの使用と保守に参加でき、情報はオープントランスペアレントです。パブリックブロックチェーンは最も初期のブロックチェーンです。BTCやイーサリアムなどの仮想デジタル通貨はすべてパブリックブロックチェーンに基づいています。ただし、現在、パブリックチェーンの実際の適用価値はそれほど大きくなく、特に適切な適用シナリオはありません。

(2)アライアンスチェーン

業界ブロックチェーン(コンソーシアムブロックチェーン):複数の事前選択されたノードがグループ内の簿記係として指定され、各ブロックの生成は、事前に選択されたすべてのノード(事前に選択されたノードがコンセンサスプロセスに参加)、その他のアクセスによって共同で決定されますノードトランザクションに参加できますが、権限に制限があり、銀聯組織などの情報が保護されます。現在、アライアンスチェーンは、さまざまなブロックチェーンテクノロジーチームの主な研究対象です。アライアンスチェーンは、ブロックチェーンテクノロジーの特徴のほとんどを備えており、権利管理、データセキュリティ、および監視においてより多くの利点があるため、企業がブロックチェーンを使用します。優先します。技術的な解決策。

市場にはいくつかの主流のコンソーシアムチェーンテクノロジーフレームワークもあり、コンソーシアムチェーンの開発と保守が容易になっています。中国の一部の大手ソフトウェアメーカーは、Ant Financialのブロックチェーンプラットフォーム、TencentのTrustSQLプラットフォーム、NeusoftのSaCa EchoTrustブロックチェーンアプリケーションプラットフォーム、JDのブロックチェーン偽造防止およびトレーサビリティプラットフォームなど、独自のエンタープライズブロックチェーンテクノロジーソリューションも持っています。

(3)プライベートチェーン

プライベートブロックチェーン:簿記にはブロックチェーンの総勘定元帳テクノロジーのみを使用します。これは会社または個人であり、ブロックチェーンに排他的にアクセスできます。ブロックチェーンを元帳データベースとして使用する機能。

3.主要なテクノロジーと機能

(1)コンセンサスメカニズム

コンセンサスメカニズムは、ブロックチェーンシステムの魂であり、ブロックチェーンシステムの信頼システムの基盤と呼ばれます。ブロックチェーンシステムはマルチノード分散型台帳システムです。新しい情報を記録する必要がある場合、どのノードが簿記を担当し、どのノードに簿記報酬が発行され、どのノードが簿記結果の検証を担当し、どのように作成するか各ノードが最終的なコンセンサスに到達し、ネットワーク内のすべてのノードによるアカウンティング結果を同じ順序でコピーして記録することが、コンセンサスメカニズムが実行する必要があることです。

そしてウィキペディアによると:

いわゆる「コンセンサスメカニズム」とは、特別なノードの投票により、トランザクションの検証と確認を非常に短時間で完了することです。トランザクションの場合、関係のない複数のノードがコンセンサスに達することができれば、次のように考えることができます。ネットワーク全体が正しいこと。これについてもコンセンサスを得ることができます。もっと簡単に言えば、中国のWeiboビッグV、米国の仮想通貨プレーヤー、アフリカの学生、ヨーロッパの旅行者はお互いを知りませんが、彼らは皆、あなたが良い人であることに同意します、そして基本的にあなたはあなたは悪くないと結論付けることができます。

現在、より主流のコンセンサスアルゴリズムには、PoW、PoS、DPoS、PBFTなどがあります。実際の使用では、各アルゴリズムには独自の長所と短所があります。さまざまなシナリオに適用される場合、ブロックチェーンプロジェクトはさまざまなコンセンサスメカニズムとアルゴリズムを採用します。

(2)地方分権

分散化は、インターネットの開発中に形成された社会関係とコンテンツ生成の形式であり、「集中化」に関連する新しいタイプのネットワークコンテンツ生成プロセスです。多くのノードが分散しているブロックチェーンシステムでは、各ノードは高度な自律性を特徴としています。どのノードも段階的なセンターになる可能性がありますが、必須のセンター制御機能はありません。ノード間の影響は、ネットワークを介して関連付け関係を形成します。これをオープンでフラットな平等主義のシステム現象または構造の分散化と呼びます。

分散型システムは、高いフォールトトレランスと強力な攻撃耐性の特徴を備えています。集中型システムの中央に問題が発生すると、システム全体が崩壊しますが、ブロックチェーンシステムのいずれかのノードに障害が発生しても、ブロックチェーンネットワーク全体に大きな影響はありません。

さらに、仲介者の排除は監督を受け入れないことを意味するのではなく、「地方分権化」は規制当局ではなく中央管理者と仲介者に行きます。監視ノードは、任意のブロックチェーンネットワークに簡単にアクセスできます。また、ブロックチェーンはオープンで透過的な性質を持っているため、規制当局はシステム全体のトランザクションデータをより簡単に監視できます。

(3)スマートコントラクト

技術的な観点から、スマートコントラクトは、ブロックチェーンにデプロイされたプログラムコードの一部であり、プログラムによって設定された条件が満たされると、ブロックチェーンで実行され、対応する結果が得られます。この状況は、WeChatミニプログラムにいくぶん似ています。ブロックチェーンは仮想マシンとスクリプト言語を提供します。ユーザーは、スクリプト言語の構文に従って特定のビジネスロジックを使用してプログラムを開発し、ブロックチェーンに展開します。実行条件が満たされると、Smartコントラクトは、ブロックチェーン仮想マシンによって解釈および実行されます。

典型的なアプリケーションはイーサリアムプラットフォームのスマートコントラクトです。このプラットフォームでは、ユーザーは数行のコードで必要なコントラクトを実現し、人間の監督を必要とせず、改ざんできず、自動的に実行できるコントラクトを実現できます。 、そして家を売買します。仲介者を見つける必要はありません、お金を借りるための公証人を見つける必要はありません...人々は自分のニーズに応じていつでもどこでも契約を開始できます。その実行は人に依存しませんまたは組織であり、すべての信頼は完全にイーサリアムブロックチェーンプラットフォーム自体に基づいています。

(4)不変性

ほとんどの人は不可逆と呼ぶことに慣れていますが、技術的な観点からは、個人的には不可逆と呼ぶ方が適切だと思います。コンピュータシステムであるため、追加、削除、変更、チェックは基本的な機能属性ですが、ブロックチェーンシステムの削除これは、変更操作では少し特殊です。

ブロックチェーンは、各ブロックのハッシュ値によって接続されたチェーン構造であり、ブロックのハッシュ値= SHA256(「現在のブロックコンテンツ+前のブロックのハッシュ値」)、ブロックのコンテンツの任意の1つの変更ハッシュ値が変更され、ハッシュ値が変更されるとサブブロックのハッシュ値も変更され、ブロックチェーン全体が変更されます。

したがって、ブロックチェーン全体のすべてのハッシュ値をジェネシスブロックから最新のブロックに再変更しない限り、ブロックのデータを変更することはほとんど不可能であり、変更後もブロードキャストする必要がありますネットワーク内の他のすべてのノードに、他のすべてのノードに変更を受け入れさせるように指示します。

しかし、現在のコンピューターの計算能力によれば、ブロックチェーンの先頭から末尾までを短時間で変更することは非常に困難であり、変更が完了しても他のノードは変更を受け入れません。 、それ自体がPowerであるため、すべてのノードがコンセンサスに到達するための条件はありません。

4.人気のあるブロックチェーンフレームワークとアプリケーション

(1)パブリックチェーンアプリケーション:BTCネットワーク

Blockchain 1.0製品、ビットコインの場合、中本聡は次のように定義しています。これは、ピアツーピア技術によって完全に実現された電子現金システムであり、オンライン支払いを一方の当事者が直接開始し、もう一方の当事者に支払う必要がありません。真ん中を通過する。あらゆる金融機関。

すべての通貨とは異なり、ビットコインは特定の通貨機関によって発行されたものではなく、特定のアルゴリズムに基づいた多数の計算によって生成されます。ビットコインエコノミーは、P2Pネットワーク全体の多くのノードで構成される分散データベースを使用してすべてを確認および記録しますそして、通貨流通のすべての側面のセキュリティを確保するために暗号化の設計を使用します。その後、信頼の問題を解決するためにビットコインネットワーク技術に基づいたブロックチェーン技術システムを整理し、ビットコインネットワークの原理もブロックチェーン技術の初心者のための古典的な教科書になりました。

(2)パブリックチェーンアプリケーション:イーサリアムネットワーク

ブロックチェーン2.0製品の代表であるイーサリアムは分散型アプリケーション(Dapps)用のオープンソースブロックチェーンプラットフォームです。ほとんどのブロックチェーンテクノロジーの特徴を備えていますが、他のブロックチェーンとは異なり、イーサリアムはプログラム可能であり、開発者はそれを使用してさまざまなアプリケーションを構築できます。分散型イーサリアム仮想マシン(EVM)を提供して、専用の暗号通貨であるイーサリアム(略して「ETH」)を介してピアツーピア契約を処理します。スクリプトコードのみ)。ビットコインネットワークを分散データベースのセットと考えると、イーサリアムはさらに一歩進んで、分散コンピューターと見なすことができます。ブロックチェーンはコンピューターのROMであり、契約はプログラムであり、イーサリアムのマイナーはコンピューティングを担当し、CPUの役割を果たします。


イーサリアムの概念は、「次世代の暗号通貨と分散型アプリケーションプラットフォーム」を意味するビットコインに触発された後、2013年から2014年にプログラマーのVitalikButerinによって最初に提案されました。プラットフォームとしてのイーサリアムは新しいアプリケーションを開発できますが、イーサリアムの動作はBTCネットワークと同じであるため、トークンメカニズムを採用しており、プラットフォームのパフォーマンスが不十分であり、ネットワークの輻輳が頻繁に発生します。プラットフォームが使用されます。ブロックチェーン技術は問題ありませんが、実際の生産で使用することは現実的ではありません。

(3)コンソーシアムチェーン開発フレームワーク:Hyperledger Fabric

Hyperledger Fabricは、Hyperledgerとも呼ばれ、IBMがLinux Foundationに提供した商用分散型台帳であり、エンタープライズアプリケーション向けの世界最大の分散型オープンソースプロジェクトです。他のブロックチェーンテクノロジーと同様に、元帳もあり、スマートコントラクトを使用できます。Fabricのスマートコントラクトは複数のアーキテクチャを持つことができ、Solidityに加えて、Go、Java、Javascriptなどの主流の言語でプログラムすることができます。

これまでのところ、Fabricは、Alibaba、AWS、Azure、Baidu、Google、Huawei、IBM、Oracle、Tencentなどのインターネット大手からサポートを受けています。多くのエンタープライズブロックチェーンプラットフォームは、Oracleなどの基盤となるフレームワークとしてFabricを使用しています。ただし、IBMのブロックチェーンの定義では、ブロックチェーンの分散された不変の要素が強調されているため、コンセンサスメカニズムが弱まり、コンセンサスを達成するためにKafkaとzookeeperの「注文サービス」が使用されます。したがって、一部の業界関係者は、Hyperledgerは「疑似-blockchain」ですが、それでもHyperledgerに対する企業の愛情に抵抗することはできません。現在、Fabric2.0バージョンが正式にリリースされています。

(4)まとめ

現在、パブリックチェーンの実用化にはビジネスシナリオは多くありません。そのほとんどがマイニングやオンラインペットフィーディングをテーマにしたゲームです。デジタル通貨の匿名性により、一部の犯罪者はこの機能を利用しています。マネーロンダリングやダークネット取引などの違法行為にデジタル通貨を使用することは各国のターゲットであり、私の国の政策や規制も厳しく禁止されています。したがって、技術者にとっては、パブリックチェーンを研究の対象として使用することができます。研究、そして当分の間他の側面には多くはありません。多くの実用的な重要性。

現在、ほとんどのブロックチェーン企業の研究の方向性は、主にアライアンスチェーンとプライベートチェーンの企業を対象としており、国レベルでもブロックチェーン技術の開発、特にブロックチェーンの基盤となるコア技術の研究開発を強力にサポートしています。ブロックチェーンテクノロジーの使用を提唱します。コアテクノロジーの独立したイノベーションの重要なブレークスルーとして、チェーンは攻撃の主な方向性を明確にし、投資を増やし、多くの主要なコアテクノロジーの征服に焦点を当て、ブロックチェーンテクノロジーの開発を加速しました。産業革新。ただし、市場に出回っている主流のブロックチェーンプラットフォームのほとんどは、依然として外国企業によって支配されています。国内のブロックチェーンの基盤となるコアテクノロジーの開発には、技術者の二重の努力が必要です。

2.ブロックチェーンテクノロジーのJava実装

1.ブロックチェーンテクノロジーアーキテクチャ


現在、主流のブロックチェーン技術アーキテクチャは主に5つの層に分かれています。データ層は最下位の技術であり、主にデータストレージ、アカウント情報、トランザクション情報などのモジュールを実現します。データストレージは主にマークルツリーに基づいています。ブロックチェーンの構造が実現され、アカウントとトランザクションは、ブロックチェーン内のデータのセキュリティを確保するために、デジタル署名、ハッシュ機能、非対称暗号化テクノロジーなどのさまざまな暗号化アルゴリズムとテクノロジーに基づいています。

ネットワーク層は主に、ポイントツーポイントテクノロジーとも呼ばれるネットワークノードの接続と通信を実現します。各ブロックチェーンノードは、ネットワークを介して通信します。コンセンサスレイヤーは、コンセンサスアルゴリズムを使用して、ネットワーク内の各ノードがネットワーク全体のすべてのブロックデータの信頼性と正確性について合意に達することを可能にし、ビザンチン攻撃、51攻撃、およびその他のブロックチェーンコンセンサスアルゴリズム攻撃を防ぎます。

インセンティブ層は、主にパブリックチェーンのカテゴリーであるブロックチェーントークンの発行・配布メカニズムを実現するためのものであり、分析は行いません。アプリケーション層は通常、ブロックチェーンシステムをプラットフォームと見なし、いくつかの分散型アプリケーションまたはスマートコントラクトをプラットフォームに実装し、プラットフォームはこれらのアプリケーションを実行するための仮想マシンを提供します。

次に、Java言語をベースにした小さなブロックチェーンシステムを開発し、データレイヤー、ネットワークレイヤー、コンセンサスレイヤーの一部の機能を実現し、単純なコードを使用して概念を直感的に抽象化し、上記のブロックチェーンテクノロジーの理解を深めます。理論的理解。

2.Javaベースのブロックチェーン開発プラクティス

(1)開発環境

開発ツール

VSCode

開発言語

Java

JDKバージョン

JDK1.8またはOpenJDK11

開発フレームワーク

SpringBoot2.2.1

エンジニアリング管理

Maven3.6

テストツール

郵便配達員

(2)ブロックチェーンの基本モデルの構築

ブロックは、ブロックチェーンシステムの最小単位です。最初のステップは、最も単純なブロック構造を実装し、主に次のフィールドを含む新しいBlock.javaクラスを作成することです
。Block.java

/**
 * 区块结构
 * 
 * @author Jared Jia
 *
 */
public class Block implements Serializable {


  private static final long serialVersionUID = 1L;
  /**
   * 区块索引号(区块高度)
   */
  private int index;
  /**
   * 当前区块的hash值,区块唯一标识
   */
  private String hash;
  /**
   * 前一个区块的hash值
   */
  private String previousHash;
  /**
   * 生成区块的时间戳
   */
  private long timestamp;
  /**
   * 工作量证明,计算正确hash值的次数
   */
  private int nonce;
  /**
   * 当前区块存储的业务数据集合(例如转账交易信息、票据信息、合同信息等)
   */
  private List<Transaction> transactions;
  
  /*** 省略get set方法****/
  }

ブロックチェーンは、ブロックがブロックハッシュの順序で直列に接続されているデータ構造です。ハッシュ値は、ハッシュアルゴリズムを使用してブロックに対して2回目のハッシュ計算を実行することによって取得されるデジタル抽象情報です(不明な場合ハッシュ関数を使用すると、最初にBaiduでSHAアルゴリズムを理解できます)。これは、ブロックの情報セキュリティとブロックチェーン全体の有効性を保証するために使用されます。したがって、2番目のステップでは、SHA256アルゴリズムを使用し、Javaを介して実装するブロックのハッシュ値を計算するための新しいメソッドCryptoUtil.javaを追加しました

/**
 * 密码学工具类
 * 
 * @author Jared Jia
 *
 */
public class CryptoUtil {


  /**
   * SHA256散列函数
   * @param str
   * @return
   */
  public static String SHA256(String str) {
    MessageDigest messageDigest;
    String encodeStr = "";
    try {
      messageDigest = MessageDigest.getInstance("SHA-256");
      messageDigest.update(str.getBytes("UTF-8"));
      encodeStr = byte2Hex(messageDigest.digest());
    } catch (Exception e) {
      System.out.println("getSHA256 is error" + e.getMessage());
    }
    return encodeStr;
  }
  
  private static String byte2Hex(byte[] bytes) {
    StringBuilder builder = new StringBuilder();
    String temp;
    for (int i = 0; i < bytes.length; i++) {
      temp = Integer.toHexString(bytes[i] & 0xFF);
      if (temp.length() == 1) {
        builder.append("0");
      }
      builder.append(temp);
    }
    return builder.toString();
  }
}

3番目のステップは、チェーン構造オブジェクトを作成し、ブロックオブジェクトを順番に保存し、整然としたブロックチェーンリストを作成することです。スレッドセーフの問題を考慮して、CopyOnWriteArrayListを使用して実装します。テストの便宜上、ブロックチェーンを配置します。構造はローカルキャッシュに保存されます。実際のブロックチェーンネットワークは、最終的に永続層の機能を実現し、ブロックチェーンデータをデータベースに保存します。たとえば、BTCコアネットワークはKVデータベースLevelDB:
BlockCache.javaを使用します。

public class BlockCache {


  /**
   * 当前节点的区块链结构
   */
  private List<Block> blockChain = new CopyOnWriteArrayList<Block>();
  
  public List<Block> getBlockChain() {
    return blockChain;
  }


  public void setBlockChain(List<Block> blockChain) {
    this.blockChain = blockChain;
  }
 }

4番目のステップでは、ブロックチェーン構造を作成した後、ブロックチェーンにブロックを追加する新しいメソッドを追加する必要があります。同時に、ブロックを追加するたびに、新しいブロックの有効性を確認する必要があります。ハッシュ値が正しい新しいブロックの前のブロックのハッシュ属性の値が前のブロックのハッシュ値と等しいかどうか。

さらに、ブロックチェーンにジェネシスブロックが必要です。これは、ハードコーディングによって直接実装します:
BlockService.java

/**
 * 区块链核心服务
 * 
 * @author Jared Jia
 *
 */
@Service
public class BlockService {


  @Autowired
  BlockCache blockCache;
  
  /**
   * 创建创世区块
   * @return
   */
  public String createGenesisBlock() {
    Block genesisBlock = new Block();
    //设置创世区块高度为1
    genesisBlock.setIndex(1);
    genesisBlock.setTimestamp(System.currentTimeMillis());
    genesisBlock.setNonce(1);
    //封装业务数据
    List<Transaction> tsaList = new ArrayList<Transaction>();
    Transaction tsa = new Transaction();
    tsa.setId("1");
    tsa.setBusinessInfo("这是创世区块");
    tsaList.add(tsa);
    Transaction tsa2 = new Transaction();
    tsa2.setId("2");
    tsa2.setBusinessInfo("区块链高度为:1");
    tsaList.add(tsa2);    
    genesisBlock.setTransactions(tsaList);
    //设置创世区块的hash值
    genesisBlock.setHash(calculateHash("",tsaList,1));
    //添加到已打包保存的业务数据集合中
    blockCache.getPackedTransactions().addAll(tsaList);
    //添加到区块链中
    blockCache.getBlockChain().add(genesisBlock);
    return JSON.toJSONString(genesisBlock);
  }
  
  /**
   * 创建新区块
   * @param nonce
   * @param previousHash
   * @param hash
   * @param blockTxs
   * @return
   */
  public Block createNewBlock(int nonce, String previousHash, String hash, List<Transaction> blockTxs) {
    Block block = new Block();
    block.setIndex(blockCache.getBlockChain().size() + 1);
    //时间戳
    block.setTimestamp(System.currentTimeMillis());
    block.setTransactions(blockTxs);
    //工作量证明,计算正确hash值的次数
    block.setNonce(nonce);
    //上一区块的哈希
    block.setPreviousHash(previousHash);
    //当前区块的哈希
    block.setHash(hash);
    if (addBlock(block)) {
      return block;
    }
    return null;
  }


  /**
   * 添加新区块到当前节点的区块链中
   * 
   * @param newBlock
   */
  public boolean addBlock(Block newBlock) {
    //先对新区块的合法性进行校验
    if (isValidNewBlock(newBlock, blockCache.getLatestBlock())) {
      blockCache.getBlockChain().add(newBlock);
      // 新区块的业务数据需要加入到已打包的业务数据集合里去
      blockCache.getPackedTransactions().addAll(newBlock.getTransactions());
      return true;
    }
    return false;
  }
  
  /**
   * 验证新区块是否有效
   * 
   * @param newBlock
   * @param previousBlock
   * @return
   */
  public boolean isValidNewBlock(Block newBlock, Block previousBlock) {
    if (!previousBlock.getHash().equals(newBlock.getPreviousHash())) {
      System.out.println("新区块的前一个区块hash验证不通过");
      return false;
    } else {
      // 验证新区块hash值的正确性
      String hash = calculateHash(newBlock.getPreviousHash(), newBlock.getTransactions(), newBlock.getNonce());
      if (!hash.equals(newBlock.getHash())) {
        System.out.println("新区块的hash无效: " + hash + " " + newBlock.getHash());
        return false;
      }
      if (!isValidHash(newBlock.getHash())) {
        return false;
      }
    }


    return true;
  }
 }

上記のキーコードを実装した後、基本ブロックモデルとブロックチェーンモデルを含む非常に単純なブロックチェーンモデルを構築し、新しいブロックを生成してブロックチェーンに追加できるようにしました。次にテストします。

5番目のステップでは、 BlockController.javaを呼び出すControllerクラスを記述します。

@Controller
public class BlockController {


  @Resource
  BlockService blockService;
  
  @Autowired
  BlockCache blockCache;
  
  /**
   * 查看当前节点区块链数据
   * @return
   */
  @GetMapping("/scan")
  @ResponseBody
  public String scanBlock() {
    return JSON.toJSONString(blockCache.getBlockChain());
  }
  
  /**
   * 创建创世区块
   * @return
   */
  @GetMapping("/create")
  @ResponseBody
  public String createFirstBlock() {
    blockService.createGenesisBlock();
    return JSON.toJSONString(blockCache.getBlockChain());
  }
 }

6番目のステップであるシステムテスト
まず、システムを起動した後、最初にブロックチェーンのデータを確認します。現在のシステムのブロックチェーンが空であることがわかります。


次に、ジェネシスブロックを作成するメソッドを呼び出し、戻り結果を確認します。

生成されたジェネシスブロックをローカルブロックチェーンに追加し、それをJSON文字列に変換して返すと、現在のブロックチェーンにブロックオブジェクトが格納されていることがわかります。これまでのところ、単純なブロックチェーンを実装しています。実際のブロックチェーンシステムモデルははるかに複雑であり、対応するフィールドはさまざまなビジネスシナリオに応じて拡張する必要がありますが、基本的な機能は同じです。

(3)コンセンサスメカニズムの実施

前の章では、単純なブロックチェーン構造を実装し、新しいブロックを生成して追加することができましたが、ここで問題が発生します。実際のブロックチェーンシステムは、マルチノード、分散型、分散型ネットワークであり、各ノードはネットワークを介して相互作用し、同じブロックチェーンデータ全体をリアルタイムで同期して、生成したブロックを他のノードで認識し、他のすべてのノードに同期的に追加するにはどうすればよいですか?現時点では、すべてのネットワークノード参加者がコンセンサス、受け入れ、新しいブロックの保存、いわゆる「コンセンサスメカニズム」。

理論的根拠で述べたように、多くのコンセンサスメカニズムがあり、それぞれに長所と短所があります。次に、Javaコードを使用して、最もよく知っているメカニズムの1つであるプルーフオブワークをシミュレートして実装します。 。プルーフオブワークロード、POWメカニズムに基づいて構築されたブロックチェーンネットワークでは、ノードはランダムハッシュの値を計算することによってアカウンティング権を競い、正しい値を取得してブロックを生成する機能は、ノードの特定の計算能力です。パフォーマンスと計算のプロセスは、一般的に鮮やかに「マイニング」と呼ばれます。

簡単に言えば、ブロックチェーンシステムは一連の計算ルールまたは一連の計算問題を設定します。新しいブロックが生成される前に、各ノードはこの問題の計算に投資されます。どのノードが最初に結果を計算し、検証された後他のノードによって認識されると、このノードは新しいブロックの簿記権を取得し、システムの対応する報酬を受け取り、コンセンサスは終了します。

PoWコンセンサスメカニズムの典型的なアプリケーションはBTCネットワークです。BTCネットワークでは、コンセンサス計算の目的は、特定の要件を満たすブロックハッシュ(ハッシュ値)を見つけることです。このブロックハッシュ値は、作業結果の証明です。計算作業の目的は、この証明値を見つけることです。前の章では、テスト中にこのハッシュ値を確認しました。

[
    {
        "hash": "25931395e736653212f0258824df4222ae739ec2d5897310258b0857d4d3870c",
        "index": 1,
        "nonce": 1,
        "timestamp": 1580970554734,
        "transactions": [
            {
                "businessInfo": "这是创世区块",
                "id": "1"
            }
        ]
    }
]

BTCネットワークPoWで使用されるハッシュキャッシュアルゴリズムには、次の一般的なロジックがあります。

  1. いくつかの公に知られているデータを取得します(BTCネットワークでは、ブロックヘッダーを指します)。
  2. カウンターナンスを追加します。初期値は0に設定されます。
  3. データとナンスの連結文字列のハッシュ値を計算します。
  4. 前のステップのハッシュ値が特定の条件を満たしているかどうかを確認し、満たされている場合は計算を停止し、満たされていない場合はナンスに1を加算して、この特定の条件が満たされるまでステップ3と4を繰り返します。

次に、Javaコードを使用してこのアルゴリズムを実装し、ハッシュ値の最初の4桁がすべて0であるかのように満たされる特定の条件を設定すると、計算が成功します(実際のブロックチェーンネットワークの特定の条件はより要求が厳しくなります) 、計算の計算能力が必要です。また、高いですし、システムは、ブロック生成の速度を確保するために、計算の難しさを満たす特定の条件を動的に調整します。

最初のステップでは、新しいコンセンサスメカニズムサービスクラスを作成し、「マイニング」メソッドを追加します。計算が成功した後、アカウンティング権を取得し、ブロックを追加するメソッドを呼び出し、ブロックをブロックチェーンに追加します
。PowService.java

/**
 * 共识机制
 * 采用POW即工作量证明实现共识
 * @author Administrator
 *
 */
@Service
public class PowService {


  @Autowired
  BlockCache blockCache;
  
  @Autowired
  BlockService blockService;
  
  /**
   * 通过“挖矿”进行工作量证明,实现节点间的共识
   * 
   * @return
   */
  public Block mine(){
    
    // 封装业务数据集合,记录区块产生的节点信息,临时硬编码实现
    List<Transaction> tsaList = new ArrayList<Transaction>();
    Transaction tsa1 = new Transaction();
    tsa1.setId("1");
    tsa1.setBusinessInfo("这是IP为:"+CommonUtil.getLocalIp()+",端口号为:"+blockCache.getP2pport()+"的节点挖矿生成的区块");
    tsaList.add(tsa1);
    Transaction tsa2 = new Transaction();
    tsa2.setId("2");
    tsa2.setBusinessInfo("区块链高度为:"+(blockCache.getLatestBlock().getIndex()+1));
    tsaList.add(tsa2);
    
    // 定义每次哈希函数的结果 
    String newBlockHash = "";
    int nonce = 0;
    long start = System.currentTimeMillis();
    System.out.println("开始挖矿");
    while (true) {
      // 计算新区块hash值
      newBlockHash = blockService.calculateHash(blockCache.getLatestBlock().getHash(), tsaList, nonce);
      // 校验hash值
      if (blockService.isValidHash(newBlockHash)) {
        System.out.println("挖矿完成,正确的hash值:" + newBlockHash);
        System.out.println("挖矿耗费时间:" + (System.currentTimeMillis() - start) + "ms");
        break;
      }
      System.out.println("第"+(nonce+1)+"次尝试计算的hash值:" + newBlockHash);
      nonce++;
    }
    // 创建新的区块
    Block block = blockService.createNewBlock(nonce, blockCache.getLatestBlock().getHash(), newBlockHash, tsaList);
    return block;
  }
  
  /**
   * 验证hash值是否满足系统条件
   * 暂定前4位是0则满足条件
   * @param hash
   * @return
   */
  public boolean isValidHash(String hash) {
    //System.out.println("难度系数:"+blockCache.getDifficulty());
    return hash.startsWith("0000");
  }
}

2番目のステップは、コンセンサスメカニズムサービスをテストするためのControllerクラスメソッドを作成することです:
BlockController.java

/**
   * 工作量证明PoW
   * 挖矿生成新的区块 
   */
  @GetMapping("/mine")
  @ResponseBody
  public String createNewBlock() {
    powService.mine();
    return JSON.toJSONString(blockCache.getBlockChain());
  }

3番目のステップは、システムを起動してテストすることです。
最初に
http:// localhost:8080 / createメソッドを実行して、作成ブロックを生成します。次に、プルーフオブワーク計算のためにhttp:// localhost:8080 / mineメソッドを
呼び出し、新しいブロックを生成して、ローカルブロックチェーンに追加します。

システムのバックグラウンド計算プロセスを見てみましょう。この計算では、条件を満たすハッシュ値を計算するのに合計1048ミリ秒かかり、合計4850回計算されます。

これまで、単純なプルーフオブワークメカニズムを実装し、それを現在のブロックチェーンシステムノードで実行し、正しい結果の計算を完了して、新しいブロックを生成しました。

次に、複数のノードの同時運用を実現するP2Pネットワークを開発します。ノードがマイニングを完了すると、P2Pネットワークを介して他のノードにブロードキャストします。他のノードが検証に合格すると、新しく生成されたブロックがに追加されます。それ自体がブロックチェーン上にあり、それによってブロックチェーンネットワーク全体のすべてのノードのデータの一貫性が保証されます。

(4)P2Pネットワーク開発

以前、基本的なブロックチェーンシステムを実装し、PoWワークロードプルーフコンセンサスメカニズムを実装しました。これは、マイニングを通じて正しい結果を計算し、ブロックチェーンに追加する新しいブロックを生成しますが、これらは、単一ノードの操作について、実際のブロックチェーンは、複数のノードが同時に実行される分散ネットワークシステムです。すべてのノードが同時にアカウンティング権限を計算して取得し、完全なブロックチェーンを共同で維持します。

次に、JavaのWebSocketに基づくピアツーピアネットワークを実装して、複数のノード間の相互通信を実現します。この章では、次の機能を実装します。

  - 创建一个基于java的p2p网络
  - 运行多个节点,且多个节点通过p2p网络自动同步区块信息
  - 一个节点挖矿生成新的区块后,自动广播给其他所有节点
  - 每个节点在接收到其他节点发送的区块内容后,进行验证,验证通过添加到本地区块链上
  - 在自我节点查看整个区块链内容

この章の機能を開発およびテストするには、2台のコンピューターまたは仮想マシン、あるいは複数のノードでテストを実行するのに便利なDockerクラスター環境を準備することをお勧めします。コンピューターが1台しかない場合は、各ノードで実行されているポート番号を異なるものに設定できます。

最初のステップは開発のアイデアを整理することです。
現在、単一ノードのブロック生成を実現しており、各ノードのメッセージ同期を実現するだけで済みます。

  • まず、p2pネットワークのサーバー側とクライアント側はjavaコードを介して実装され、各ノードはサーバー側とクライアント側の両方になります。
  • 次に、ノードが起動すると、ブロックチェーンネットワーク上で有効なノードを探し、ソケット接続を確立します(BTCネットワークはビットコインノードのIPアドレスリストを提供する「DNS」シード方式を使用して有効なBTCノードを取得できます)。ノードリストをapplication.ymlファイルに直接設定します。
  • 次に、接続されているノードから最新のブロック情報を取得します。現在のノードが初めて実行される場合は、ブロックチェーン情報全体を取得して、ローカルで置き換えます。
  • その後、各ノードは同時にマイニングと計算を行います。どちらのノードが最初に計算を完了した場合でも、生成された新しいブロックを他のすべてのノードにブロードキャストします(実際のブロックチェーンネットワークは常に計算しています。テストの便宜のために、手動でノードをトリガーしてマイニングします。 。ブロックを生成してから、ネットワーク全体の他のすべてのノードにブロードキャストします)。
  • 最後に、ノードはブロードキャストコンテンツを受信すると、受信した新しいブロックを検証し、検証に合格した後、ローカルブロックチェーンに追加します。検証の主な条件は、新しいブロックの高さがローカルブロックチェーンよりも高くなければならないことです。 。

2番目のステップは、最初にP2Pネットワークサーバー側を実装し
、新しいP2PServerクラスを作成し、サーバー側を初期化するメソッドP2PServer.javaを追加すること
です。

/**
 * p2p服务端
 * 
 * @author Jared Jia
 *
 */
@Component
public class P2PServer {


  @Autowired
  P2PService p2pService;


  public void initP2PServer(int port) {
    WebSocketServer socketServer = new WebSocketServer(new InetSocketAddress(port)) {


      /**
       * 连接建立后触发
       */
      @Override
      public void onOpen(WebSocket webSocket, ClientHandshake clientHandshake) {
        p2pService.getSockets().add(webSocket);
      }


      /**
       * 连接关闭后触发
       */
      @Override
      public void onClose(WebSocket webSocket, int i, String s, boolean b) {
        p2pService.getSockets().remove(webSocket);
        System.out.println("connection closed to address:" + webSocket.getRemoteSocketAddress());
      }


      /**
       * 接收到客户端消息时触发
       */
      @Override
      public void onMessage(WebSocket webSocket, String msg) {
        //作为服务端,业务逻辑处理
        p2pService.handleMessage(webSocket, msg, p2pService.getSockets());
      }


      /**
       * 发生错误时触发
       */
      @Override
      public void onError(WebSocket webSocket, Exception e) {
        p2pService.getSockets().remove(webSocket);
        System.out.println("connection failed to address:" + webSocket.getRemoteSocketAddress());
      }


      @Override
      public void onStart() {


      }


    };
    socketServer.start();
    System.out.println("listening websocket p2p port on: " + port);
  }
}

3番目のステップは、P2Pネットワーククライアント側の
P2PClient.javaを実装することです。

/**
 * p2p客户端
 * 
 * @author Jared Jia
 *
 */
@Component
public class P2PClient {
  
  @Autowired
  P2PService p2pService;


  public void connectToPeer(String addr) {
    try {
      final WebSocketClient socketClient = new WebSocketClient(new URI(addr)) {
        @Override
        public void onOpen(ServerHandshake serverHandshake) {
          //客户端发送请求,查询最新区块
          p2pService.write(this, p2pService.queryLatestBlockMsg());
          p2pService.getSockets().add(this);
        }


        /**
         * 接收到消息时触发
         * @param msg
         */
        @Override
        public void onMessage(String msg) {
          p2pService.handleMessage(this, msg, p2pService.getSockets());
        }


        @Override
        public void onClose(int i, String msg, boolean b) {
          p2pService.getSockets().remove(this);
          System.out.println("connection closed");
        }


        @Override
        public void onError(Exception e) {
          p2pService.getSockets().remove(this);
          System.out.println("connection failed");
        }
      };
      socketClient.connect();
    } catch (URISyntaxException e) {
      System.out.println("p2p connect is error:" + e.getMessage());
    }
  }
}

4番目のステップは、P2Pネットワーク同期
のメッセージモデルを定義することです。同期のメッセージモデルを次の4つのカテゴリに定義します
。BlockConstant.java

  // 查询最新的区块
  public final static int QUERY_LATEST_BLOCK = 1;


  // 返回最新的区块
  public final static int RESPONSE_LATEST_BLOCK = 2;


  // 查询整个区块链
  public final static int QUERY_BLOCKCHAIN = 3;


  // 返回整个区块链
  public final static int RESPONSE_BLOCKCHAIN = 4;

ノード間で渡されるメッセージモデルを定義します:
Message.java

/**
 * p2p通讯消息
 *
 * @author Jared Jia
 * 
 */
public class Message implements Serializable {
  
    private static final long serialVersionUID = 1L;
    /**
     * 消息类型
     */
    private int type;
    /**
     * 消息内容
     */
    private String data;
    
    /****set get方法省略****/
    
  }

5番目のステップは、他のノードにブロードキャストする方法を実装することです

新しいp2pネットワークサービスクラスを作成して、メッセージを送信したり、現在のノードから他のノードに送信された要求を処理したりします。
P2PService.java

/**
   * 全网广播消息
   * @param message
   */
  public void broatcast(String message) {
    List<WebSocket> socketsList = this.getSockets();
    if (CollectionUtils.isEmpty(socketsList)) {
      return;
    }
    System.out.println("======全网广播消息开始:");
    for (WebSocket socket : socketsList) {
      this.write(socket, message);
    }
    System.out.println("======全网广播消息结束");
  }

6番目のステップ、メッセージ処理ルートを開発する

5番目のステップでは、メッセージの送信が実現され、このステップはメッセージの受信を実現します。
まず、サーバーとクライアントが共有するメッセージルーティングは、対応する処理ユニットにメッセージを配信するように設計されています。
P2PService.java

/**
   * 客户端和服务端共用的消息处理方法
   * @param webSocket
   * @param msg
   * @param sockets
   */
  public void handleMessage(WebSocket webSocket, String msg, List<WebSocket> sockets) {
    try {
      Message message = JSON.parseObject(msg, Message.class);
      System.out.println("接收到IP地址为:" +webSocket.getRemoteSocketAddress().getAddress().toString()
          +",端口号为:"+ webSocket.getRemoteSocketAddress().getPort() + "的p2p消息:"
              + JSON.toJSONString(message));
      switch (message.getType()) {
      //客户端请求查询最新的区块:1
      case BlockConstant.QUERY_LATEST_BLOCK:
        write(webSocket, responseLatestBlockMsg());//服务端调用方法返回最新区块:2
        break;
      //接收到服务端返回的最新区块:2
      case BlockConstant.RESPONSE_LATEST_BLOCK:
        handleBlockResponse(message.getData(), sockets);
        break;
      //客户端请求查询整个区块链:3
      case BlockConstant.QUERY_BLOCKCHAIN:
        write(webSocket, responseBlockChainMsg());//服务端调用方法返回最新区块:4
        break;
      //直接接收到其他节点发送的整条区块链信息:4
      case BlockConstant.RESPONSE_BLOCKCHAIN:
        handleBlockChainResponse(message.getData(), sockets);
        break;
      }
    } catch (Exception e) {
      System.out.println("处理IP地址为:" +webSocket.getRemoteSocketAddress().getAddress().toString()
        +",端口号为:"+ webSocket.getRemoteSocketAddress().getPort() + "的p2p消息错误:" 
        + e.getMessage());
    }
  }

7番目のステップは、メッセージ処理ユニットを開発する

メッセージルーティングが設定されたら、他のノードから送信されたブロックまたはブロックチェーン情報を処理するために別の処理ユニットを作成します。一般的な原則は、最初に他のノードから送信されたブロックまたはブロックチェーンの有効性を確認し、次にそれらの高さを判断することです。現在のノードのブロックチェーンよりも高い場合は、ローカルブロックチェーンを置き換えるか、ローカルブロックチェーンに新しいブロックを追加します。
P2PService.java

/**
   * 处理其它节点发送过来的区块信息
   * @param blockData
   * @param sockets
   */
  public synchronized void handleBlockResponse(String blockData, List<WebSocket> sockets) {
    //反序列化得到其它节点的最新区块信息
    Block latestBlockReceived = JSON.parseObject(blockData, Block.class);
    //当前节点的最新区块
    Block latestBlock = blockCache.getLatestBlock();
    
    if (latestBlockReceived != null) {
      if(latestBlock != null) {
        //如果接收到的区块高度比本地区块高度大的多
        if(latestBlockReceived.getIndex() > latestBlock.getIndex() + 1) {
          broatcast(queryBlockChainMsg());
          System.out.println("重新查询所有节点上的整条区块链");
        }else if (latestBlockReceived.getIndex() > latestBlock.getIndex() && 
            latestBlock.getHash().equals(latestBlockReceived.getPreviousHash())) {
          if (blockService.addBlock(latestBlockReceived)) {
            broatcast(responseLatestBlockMsg());
          }
          System.out.println("将新接收到的区块加入到本地的区块链");
        }
      }else if(latestBlock == null) {
        broatcast(queryBlockChainMsg());
        System.out.println("重新查询所有节点上的整条区块链");
      }
    }
  }
  
  /**
   * 处理其它节点发送过来的区块链信息
   * @param blockData
   * @param sockets
   */
  public synchronized void handleBlockChainResponse(String blockData, List<WebSocket> sockets) {
    //反序列化得到其它节点的整条区块链信息
    List<Block> receiveBlockchain = JSON.parseArray(blockData, Block.class);
    if(!CollectionUtils.isEmpty(receiveBlockchain) && blockService.isValidChain(receiveBlockchain)) {
      //根据区块索引先对区块进行排序
      Collections.sort(receiveBlockchain, new Comparator<Block>() {
        public int compare(Block block1, Block block2) {
          return block1.getIndex() - block2.getIndex();
        }
      });
      
      //其它节点的最新区块
      Block latestBlockReceived = receiveBlockchain.get(receiveBlockchain.size() - 1);
      //当前节点的最新区块
      Block latestBlock = blockCache.getLatestBlock();
      
      if(latestBlock == null) {
        //替换本地的区块链
        blockService.replaceChain(receiveBlockchain);
      }else {
        //其它节点区块链如果比当前节点的长,则处理当前节点的区块链
        if (latestBlockReceived.getIndex() > latestBlock.getIndex()) {
          if (latestBlock.getHash().equals(latestBlockReceived.getPreviousHash())) {
            if (blockService.addBlock(latestBlockReceived)) {
              broatcast(responseLatestBlockMsg());
            }
            System.out.println("将新接收到的区块加入到本地的区块链");
          } else {
            // 用长链替换本地的短链
            blockService.replaceChain(receiveBlockchain);
          }
        }
      }
    }
  }

3.完全なシステム操作とテスト

最初のステップは、テスト用の実行可能jarパッケージをパッケージ化して生成することです
。2台のマシン(仮想マシンまたはDockerクラスター)を準備し、2つのノードを同時に実行します。ノード情報は次のとおりです。

mvn package -Dmaven.test.skip = trueコマンドを使用してプロジェクトをパッケージ化し、直接実行できるjarパッケージを生成します。パッケージ化する前にプロジェクトを構成します。構成情報は次のとおりです。

ノードNode1パッケージ:

ノードNode2パッケージ:

別々にパッケージ化された後、2つのノードの実行可能jarパッケージが次のように生成されます。

2つのjarパッケージを対応するIPのWindowsマシンに配置し、コマンドラインモードを開き、jarが配置されているフォルダーに入り、それぞれ次のコマンドを実行して2つのノードを実行します:
java-jardce-blockchain-node1。 jar
java -jar dce-blockchain-node2.jar

次の図に示すように、ノード2を起動すると、バックグラウンドログが表示され、ノード1が接続されています。

2番目のステップは、2つのノードをテストすることです

まず、2つのノードが起動した後、postmanを使用して
それぞれhttp://192.168.0.104:8080/scanリクエストとhttp://192.168.0.112:8090/scanリクエストを実行すると、2つのノードのブロックチェーンコンテンツがわかります。 nullです。

次に、ノード1でそれぞれhttp://192.168.0.104:8080/createリクエストとhttp://192.168.0.104:8080/mineリクエストを実行し
て、ジェネシスブロックを生成し、マイニングを通じて2番目のブロックを生成します。実行後、ブロックチェーンを確認します。ノード1の情報は次のとおりです。http://192.168.0.104 :8080 /scanresultを
実行します。

[
    {
        "hash": "5303d2990c139992bdb5a22aa1dac4f2719755304e45bac03ca4a1f1688c909e",
        "index": 1,
        "nonce": 1,
        "timestamp": 1581064647736,
        "transactions": [
            {
                "businessInfo": "这是创世区块",
                "id": "1"
            },
            {
                "businessInfo": "区块链高度为:1",
                "id": "2"
            }
        ]
    },
    {
        "hash": "0000de5eea0c20c2e7d06220bc023886e88dd8784eaa2fd2d1d6c5e581061d85",
        "index": 2,
        "nonce": 4850,
        "previousHash": "5303d2990c139992bdb5a22aa1dac4f2719755304e45bac03ca4a1f1688c909e",
        "timestamp": 1581064655139,
        "transactions": [
            {
                "businessInfo": "这是IP为:192.168.0.104,端口号为:7001的节点挖矿生成的区块",
                "id": "1"
            },
            {
                "businessInfo": "区块链高度为:2",
                "id": "2"
            }
        ]
    }
]

最後に、ノード2がノード1によって生成されたブロックチェーン情報のネットワーク同期を完了したかどうかを確認しましょう。Postmanは
http://192.168.0.112:8090/scanリクエストを実行して、返された結果を表示します。

結果から、ブロックチェーンネットワークノード2がノード1から送信されたブロックチェーン情報を受信し、システムログは次のようになっていることがわかります。

逆に、ノード2で別のマイニング操作を実行すると、ノード1で、ノード2の新しくマイニングされたブロック情報が受信され、ノード1のブロックチェーンに追加されたことがわかります。

これまで、完全な小さなブロックチェーンネットワークを実現し、各ノード間の通信を実現し、複数のノードが共同で同じブロックチェーン情報を維持しています。

結論:
ブロックチェーンシステムは非常に大きく、さまざまなテクノロジーが含まれています。私が示したコードは、主にブロックチェーン基盤のいくつかの概念を解釈します。興味のある学生は、これに基づいて開発を続け、たとえば、永続性レイヤー、メッセージの暗号化と復号化、システムを実現できます。アカウントモデル、オラクル、サイドチェーンテクノロジー、スマートコントラクトおよびその他のブロックチェーンシステム機能。


すべてのブロックチェーン技術者へ:
現在、Hyperledger Fabricなどの市場で人気のあるエンタープライズレベルのブロックチェーンフレームワークは外国人によって支配されており、わが国のいくつかの大規模な工場に加えて、他の多くのブロックチェーン企業は基本的にすべて他の人が二次包装をしていると、彼らの会社がブロックチェーンのコア技術を習得し、企業にサービスを提供していると主張しますが、これは悪い現象です。私たちが使用している開発言語とフレームワークのどれだけが実際に国内で生産されているかを考えることができます。ZTEとHuaweiが他のコアテクノロジーによってブロックされていたという事実を考えてみましょう。やるべきこと、そして私たちはそれらを取り除く必要があります。本当の「カーブの追い越し」を達成するために、衝動的になり、落ち着いて、基礎となるコアテクノロジーを研究してください!

リンクリストの高頻度の面接の質問(反転、マージ、交差、除算、ループの長さなどを含む)
1.1質問の説明

単一リンクリストを逆にします。

例:

  • 入力:1-> 2-> 3-> 4-> 5-> NULL
  • 出力:5-> 4-> 3-> 2-> 1-> NULL

詳細:リンクリストを繰り返しまたは再帰的に逆にすることができます。この問題を2つの方法で解決できますか?

1.2アルゴリズムの実装

1.2.1アルゴリズムのアイデア

ダブルポインタ:一方のポインタは元のリンクリストの現在のノードの前のノードを事前に指し、もう一方のポインタは次に現在のノードの次のノードを一時的に格納します。

リンクリストの各ノードを順番にトラバースします。ノードをトラバースするたびに、前のノード(反転)がポイントされ、主に4つのステップに分けられます。

  1. 最初に次のノードをバックアップします(ポインターの移動時に次のノードが見つからない場合):next = head.Next;
  2. 次のノードの次は前のノードを指します:next.Next = pre;
  3. 前のノードを現在のノードに1つ戻します。pre=head;
  4. ポストノードは、ある位置を次のノードに戻します。head= next、このステップに注意してください(無視するのは簡単です)。

1.2.2コードの実装

type LikedList struct{
    Val int
    Next *LikedList
}

// 头插法,倒置单链表
func reverseLikedList(head *LikedList)*LikedList{
    if(head == nil || head.Next == nil){
        return head
    }
 var pre, next *LikedList 
 // head是当前节点,pre是当前节点的前一个几点,next是当前节点的后一个节点
    for head != nil {  // 当前节点不为空
        next = head.Next    // 备份head.Next(指向当前节点的下一个节点),防止移动节点时找不到后置节点
  head.Next = pre     // 更新head.Next,后一个节点指向前一个(翻转)
  pre = head          // 前一个节点后移
  head = next   // 当前节点后移
 }
    return pre              // 注意:当pre指向原链表的最后一个节点时,head已经指向最后一个节点的Next即空节点,所以这里应返回pre
}

2.リンクリストのm番目からn番目のノードを逆にします

トピックソース:

https://leetcode-cn.com/problems/reverse-linked-list-ii

2.1トピックの説明

リンクリストを位置mからnに逆にします。反転を完了するには、1回のスキャンを使用してください。

説明:

1≤m≤n≤リンクリストの長さ。

例:

  • 入力:1-> 2-> 3-> 4-> 5-> NULL、m = 2、n = 4
  • 出力:1-> 4-> 3-> 2-> 5-> NULL

2.2アルゴリズムの実装

2.2.1アルゴリズムのアイデア

記録する重要な位置は、m番目のノードとその先行ノード、n番目のノードとその後続ノードの4つです。

  1. リンクリスト全体をトラバースし、リンクリストのヘッドポインタをm番目のノードであるm-1ノードだけ後ろに移動し、その前のノードをpreとして記録します。
  2. m番目のノードから開始して、changLen = n-m + 1ノードを反転します。つまり、m番目からn番目のノードを順番に反転し、反転したヘッドノードreversedListHeadとテールノードreversedListTailを記録します。
  3. リバーステールノードreversedListTailのNextフィールドをn番目のノードの後続ノードヘッドに接続し、preノードのNextフィールドをreversedListHeadに接続します。

2.2.2コードの実装

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func reverseBetween(head *ListNode, m int, n int) *ListNode {
    changeLen := n - m + 1                      // 计算需要逆置的节点个数
    var pre *ListNode = nil                     // 初始化开始逆置的节点的前驱
    var result *ListNode = head                 // 最终转换后的链表的头结点,非特殊情况即为head
    move := m - 1                               // head向后移动m-1个位置指向第m个节点
    for(head != nil && move > 0){               // 将head后移m-1个位置,即
        pre = head                              // for循环后pre指向第m个节点的前驱节点
        head = head.Next                        // for循环后head指向第m个节点
        move--
    }

    var reversedListTail *ListNode = head       // 此时reversedListTail指向的是第m个节点,该节点即是链表片段反转后的尾节点
    var reversedListHead *ListNode = nil        // 记录链表片段反转后的头结点
    for(head != nil && changeLen > 0){          // 逆置changeLen个节点
        next := head.Next                       // 暂存当前节点的下一个节点
        head.Next = reversedListHead            // 当前节点的Next指针域指向新开辟的反转后的头结点
        reversedListHead = head                 // 反转后的链表的头结点后移一个位置
        head = next                             // 当前节点后移一个节点
        changeLen--                             // 每完成一个节点逆置,changLen--
    }

    reversedListTail.Next = head                // 连接逆置后的链表尾与逆置段的后一个节点,翻转后的尾节点指向第n个节点的下一个节点
    if(pre != nil){                             // 如果pre不为空,说明不是从第1个节点开始逆置的,即m > 1 
        pre.Next = reversedListHead             // 将逆置链表开始的节点前驱与逆置后的头结点连接起来
    }else{                                      // 如果pre为空,说明m=1,即从第1个节点开始逆置,结果即为逆置后的头结点
        result = reversedListHead
    }
    return result
}

3.2つのリンクリスト間の共通部分を見つけます

3.1トピックの説明

トピックソース:

https://leetcode-cn.com/problems/intersection-of-two-linked-lists

2つの単一リンクリストが交差する開始ノードを見つけるプログラムを作成します。

次の2つのリンクリストなど。

交差点はノードc1から始まります。

例1:

  • 入力:intersectVal = 8、listA = [4,1,8,4,5]、listB = [5,0,1,8,4,5]、skipA = 2、skipB = 3
  • 出力:値=8のノードの参照

入力説明:交差ノードの値は8です(2つのリストが交差する場合は0にできないことに注意してください)。それぞれのヘッダーから数えると、リンクリストAは[4,1,8,4,5]であり、リンクリストBは[5,0,1,8,4,5]です。Aでは、交差するノードの前に2つのノードがあり、Bでは、交差するノードの前に3つのノードがあります。

推奨事項:250の面接質問の要約

例2:

  • 入力:intersectVal = 2、listA = [0,9,1,2,4]、listB = [3,2,4]、skipA = 3、skipB = 1
  • 出力:値=2のノードの参照

入力説明:交差ノードの値は2です(2つのリストが交差する場合は0にできないことに注意してください)。それぞれのヘッダーから始めて、リストAは[0,9,1,2,4]であり、リストBは[3,2,4]です。Aでは、交差ノードの前に3つのノードがあり、Bでは、交差ノードの前に1つのノードがあります。

例3:

  • 入力:intersectVal = 0、listA = [2,6,4]、listB = [1,5]、skipA = 3、skipB = 2
  • 出力:null

入力の説明:それぞれのヘッダーから始めて、リンクリストAは[2,6,4]であり、リンクリストBは[1,5]です。2つのリンクリストは交差しないため、intersectValは0である必要がありますが、skipAとskipBは任意の値にすることができます。説明:2つのリンクリストが交差していないため、nullが返されます。

知らせ:

  • 2つのリンクリストが交差しない場合はnullを返します。
  • 結果を返した後も、2つのリンクリストは元の構造を維持する必要があります。
  • リンクリスト構造全体にループはないと想定できます。
  • プログラムはO(n)時間計算量を満たそうとし、O(1)メモリのみを使用します。

3.2アルゴリズムの実装

3.2.1アルゴリズムのアイデア

  1. 2つのリンクリストListNode1とListNode2をそれぞれトラバースし、それらの長さlen1とlen2を計算します。
  2. 長いリンクリストのヘッドポインタは、abs(len1-len2)ノードによって戻されます。
  3. リンクリストが最後までトラバースされておらず、2つのリンクリストが同じノードを指している場合、そのノードは2つのリンクリストの交点です。同じノードを指していない場合、2つのヘッドのポインタはノードは後方に移動し続けます。トラバーサル後にノードが見つからない場合は、交差がないことを意味し、nilを返します。

3.2.2コードの実装

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func getIntersectionNode(headA, headB *ListNode) *ListNode {
    var lenA, lenB int                                      // 分别记录链表headA和链表headB的长度    
    lenA = getListLength(headA)
    lenB = getListLength(headB)

    if lenA > lenB {
        headA = forwardDeltaLenNode(lenA, lenB, headA)      // headA链表的头结点指向移动多出的节点个位置后
    }else{
        headB = forwardDeltaLenNode(lenB, lenA, headB)      // 如果链表headB长,移动其头结点到相应位置
    }
    for headA != nil && headB != nil {      // 没有到达链表末尾
        if(headA == headB){                 // 当前两个链表的节点相同,即指向同一个节点,说明找到的交点;否则,两链表同时后移
            return headA
        }
        headA = headA.Next                  
        headB = headB.Next
    }
    return nil                              // 遍历到链表末尾还没有找到同一个节点,说明两链表没有交点
}

// 逐个节点遍历,获取链表长度
func getListLength(head *ListNode)int{
    var lengthList int 
    for head != nil {
        lengthList++
        head = head.Next
    }
    return lengthList
}

// 较长链表移动 长链表长度-短链表长度 个节点后对应的头指针
// 参数longLen和shortLen分别表示长链表和短链表的长度,longList对应长链表
func forwardDeltaLenNode(longLen, shortLen int, longList *ListNode)*ListNode{
    deltaLen := longLen - shortLen
    for longList != nil && deltaLen != 0{
        longList = longList.Next
        deltaLen--
    }
    return longList             // 如果longList为nil或者deltaLen=0直接返回此时的头结点longList
}

4.リンクリストにリングがあるかどうかを判断します。リングがある場合は、リングが配置されている開始ノードとリングの長さを指定します。

4.1リンクリストにサイクルがあるかどうかを判断する

4.2リング付きリンクリストの開始ノードとリングの長さを指定します

5.リンクリストを特定の値xで囲まれた2つの部分に分割します

リンクリストは次のように分割されます。リンクリストの前部はx未満、後部はx以上、元のノードの相対的な順序は変更されません。例:1-> 3-7-> 2-> 5-> 3-> 6-9-2を2つの部分に分割し、5を境界として:1-> 3-> 2-> 3-> 2-> 7-> 5-> 6-> 9

5.1問題の説明

リンクリストと特定の値xが与えられた場合、x未満のすべてのノードがx以上のノードの前に来るようにリンクリストを分割します。

両方のパーティションで、各ノードの初期相対位置を維持する必要があります。

例:

  • 入力:ヘッド= 1-> 4-> 3-> 2-> 5-> 2、x = 3
  • 出力:1-> 2-> 2-> 4-> 3-> 5

リンク:

https://leetcode-cn.com/problems/partition-list

5.2アルゴリズムの実装

5.2.1アルゴリズムのアイデア

リンクリストは、前面と背面の2つの部分に分かれています。リンクリストを順番にトラバースし、preHeadが指すリンクリストの前半にxより小さいノードを挿入し、以上のノードを接続できます。 xはpostHeadに対応する別のリンクリストになります。preHeadのテールノードのNextフィールドはpostHeadノードの次のノードを指し、postHeadのテールノードのNextフィールドは空に設定されます。 2つのリンクリストが実現されます。

preHeadとpostHeadの2つのリンクリストを接続するには、2つのリンクリストのテールをそれぞれ知る必要があります。ここでは、prePtrポインターとpostPtrポインターを使用して、preHeadとpostHeadをそれぞれトラバースし、2つのテールノードを認識します。リンクリスト。

  • 前のリンクリスト:preHead-> 1-> 2-> 2
  • 後者のリンクリスト:postHead-> 4-> 3-> 5

5.2.2コードの実装

/**
 * Definition for singly-linked list.
 * type ListNode struct {
 *     Val int
 *     Next *ListNode
 * }
 */
func partition(head *ListNode, x int) *ListNode {
    //var preHead, postHead *ListNode     // 前后两部分链表的头结点,不能这么声明,这样声明的话两者都为&ListNode{0, nil}其中Next域为nil,当运行到prePtr.Next = head时会报panic: runtime error:invalid memory address or nil pointer deference错误
    preHead := &ListNode{0, nil}        // 生成一个preHead链表的头结点,该节点固定一直不对其移动,改头节点的Val为多少都不影响结果,因为使用的是头结点的Next节点
    postHead := &ListNode{0, nil}       // 生成一个postHead链表的头结点,该节点固定不动
    prePtr := preHead                   // prePtr指针对preHead链表进行遍历,注意:开始时其指向preHead
    postPtr := postHead
    for head != nil {                   // 遍历原链表
        if(head.Val < x ){              // 小于x的节点尾插到preHead链表后面
            prePtr.Next = head          // prePtr的Next域指向当前节点,即将当前节点从preHead链表尾部prePtr依次插入
            prePtr = head               // prePtr指针后移一位,指向当前节点
        }else{                          // 大于等于x的节点放在postHead链表的后面
            postPtr.Next = head         // 将当前节点插入到postHead链表尾节点postPtr后面
            postPtr = head              // postHead链表的尾节点后移一位
        }
        head = head.Next                // 当前指针后移一位
    }
    // 对head链表遍历结束分为preHead和postHead两个子链表
    prePtr.Next = postHead.Next         // 注意:这里是将preHead链表的尾节点prePtr的Next域指向postHead链表头结点的下一个节点
    postPtr.Next = nil                  // postPtr的尾节点的Next域指向空
    return preHead.Next                 // 返回preHead链表的Next之后的链表

おすすめ

転載: blog.csdn.net/m0_61926454/article/details/124413808