Ethereumに基づくブロックチェーントレーサビリティケース開発

Ethereumトレーサビリティソリューションのデモは、Ethereumネットワークの構築、契約の開発と展開、コードの完全なセットのバックエンドゲートウェイの開発など、ブロックチェーンのオンチェーンおよびトレーサビリティプロセスに簡単に慣れることができます。

より多くのブロックチェーンテクノロジーとアプリケーション分類:

ブロックチェーンアプリケーションブロックチェーン    開発

イーサネットスクエア|ファブリック| BCOS |暗号化|コンセンサスアルゴリズム|ビットコインその他のチェーン

トークンエコノミー従来の財務シナリオ|分散型ファイナンス|偽造防止トレーサビリティ|データ共有|信頼できる預金

 

Ethereumに基づくトレーサビリティソリューションでは、デモを実行してEthereumの特性を体験できます。図に示すように、このプロジェクトのデモの全体的なアーキテクチャ設計は、アプリケーション層(APP)、サービス層(Nodejs-service)、ブロックチェーン層(Ethereum)の3つの層に分かれています。設計のアイデアとソリューションを以下に示します。コンテンツ。アプリケーション層は、サービス層を要求することにより、ブロックチェーンを操作する操作をサービス層に転送し、ブロックチェーンの直接操作を回避します。サービス層では、ユーザー操作をチェックする権限制御、輻輳を回避するためのメッセージキューなど、多くのことを実行できます。 、コア機能は、さまざまなビジネスに応じてAPPからの要求を分離し、さらにスマートコントラクトでさまざまな方法を操作することです。ブロックチェーンネットワークレイヤーは、Ethereumノードを実行し、スマートコントラクトを展開して、サービスレイヤーからのデータを処理します。

 

Ethereumに基づくブロックチェーントレーサビリティケース開発

図0-1トレーサビリティプロジェクトのアーキテクチャ設計

上記の設計フレームワークに基づいて、この記事は3つの部分に分かれて詳しく説明します。

最初の部分であるEthereumネットワークの確立では、Ethereumプライベートチェーンの構築プロセスを紹介します。その後の契約ロジックに関係なく、この章は、Ethereumプライベートチェーンを構築するための一般的なテクノロジーです。

2番目の部分である契約の開発と展開では、契約の設計ロジックとスキームを紹介し、ソースコードを添付します。

3番目の部分であるバックグラウンド開発は、ユーザーアプリとブロックチェーンネットワークの間の媒体としてのnodejsに基づくサービスレイヤー開発であり、ユーザーの要求に応答してブロックチェーンを操作する方法を紹介します。次に、ユーザーがこのAPIサービスのレイヤーを使用してブロックチェーンと対話する方法を紹介します。

Ethereumネットワーク構築の最初の部分

1.gethクライアントをダウンロードします

(1)ダウンロード

ウェブサイトhttps://geth.ethereum.org/downloads/にアクセスして、gethクライアントをダウンロードします。さまざまなオペレーティングシステムに応じて、一致するgethをダウンロードできます。

(2)インストール

バイナリパッケージを直接クリックしてインストールします。

2.gethを使用してアカウントを生成します

(1)gethコマンドを使用します:geth account new

(2)パスワードを入力すると、gethはユーザー入力に従ってキーストアファイルを自動的に生成します(デフォルトのパスはC:\ Users \ Administrator.xxx \ AppData \ Roaming \ Ethereumです)

(3)Ethereumデータを同期するためのディレクトリとして、システム内の任意の場所にデータディレクトリdata0を作成します。

(4)上記のキーストアファイルをdata0フォルダにコピーします

(5)上記の方法でn個のアカウントを事前生成し、各アカウントに対応するパスワードファイル(キャリッジリターンとラインフィード付き)をdata0の同じレベルのディレクトリに配置し、pwdとして保存します。

ヒント:鉱夫のアカウントのキーストアは、次のように「extraData」に存在する必要があります。

"extraData": "0x0000000000000000000000000000000000000000000000000000000000000000e578252579e5f43fe124fe1d8236f0e5250c11970000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"

赤い部分は鉱夫の住所です。

3.ジェネシスブロックを構成します

data0の同じレベルのディレクトリにファウンディングブロック構成ファイルを作成し、上記で生成されたアカウントを構成ファイルのallocフィールドに入力して、初期のエーテル割り当てをカスタマイズします。

4.初期化

Ethereumデータディレクトリに移動し、gethコマンドを使用します。

geth --datadir data0 init Genesis.json

5.gethコマンドを開始します

(各パラメーターの詳細な説明については、geth --helpを使用してクエリを実行してください)

6.その他

Ethereumノードが正常に起動したら、組み込みAPI(web3)を使用して次の操作を実行します。

マイニングを開始します:miner.start()

マイニングを停止します:miner.stop()

新しいアカウントを生成します:personal.newAccount()

マイニングリワードを表示する:eth.coinbase

アカウント間でトランザクションを転送します。

amount = web3.toWei(5,'ether')
 
personal.unlockAccount(eth.accounts[0])
 
eth.sendTransaction({from:eth.accounts[0],to:eth.accounts[1],value:amount})

契約の開発と展開の第2部

1.ビジネスニーズ

薬物のトレーサビリティを例にとってみましょう。ビジネスシナリオ全体で、ビジネスノードは製薬会社、代理店、小売業者の3つのカテゴリに分類でき、データは薬物情報と流通情報の2つの部分に分類できます。図2-1に示すように、医薬品情報は、製薬会社が医薬品を製造するときに追加され、医薬品流通情報は、流通チャネル上のエージェントおよび小売業者によって追加されます。ブロックチェーンにデータを追加するときは、製品ID +このビジネスノード情報を使用してアップロードします。

 

Ethereumに基づくブロックチェーントレーサビリティケース開発

図2-1薬物追跡可能性ビジネス

2.契約データ構造

契約は、上記のビジネス要件に基づいて、Solidityの組み込み構造とマッピングデータタイプを使用し、ネストを使用して作成されます。薬物の追跡可能性に利用できるデータ構造を取得します。

トレーサビリティ情報の格納構造を図2-2に示します。mapping+ structネスト構造で構成されています。左側はマッピングコレクションです。各マッピング要素はキーと値のペアで、「key」はID値です。対応するIDはこのIDから取得できます。 Structには、「薬物情報」と「トレーサビリティ情報」の2つの部分が含まれています。クエリを実行するときは、ID番号(QRコードでエンコードされたNWRuxXJ75fBWUjBZiなど)を使用して、マッピングで「キー」が「NWRuxXJ75fBWUjBZi」であるStrcutを見つけます。Strcutデータは{dragInfo:999冷薬、Harbin Medicine第6工場、traceInfo:エージェントです。 A =>エージェントB =>小売業者X}

 

Ethereumに基づくブロックチェーントレーサビリティケース開発

図2-2契約保管構造

3.開発準備

(1)堅牢性

Solidityは、JavaScriptと同様のスマートコントラクトの高レベル言語であり、Ethereum Virtual Machine(EVM)で実行されます。これは、Ethereum仮想マシンコードをコンパイルされた方法で生成するように設計されています。Ethereumスマートコントラクトの開発に公式に推奨される言語です。

(2)トリュフ

Truffleは、世界クラスの開発環境、テストフレームワーク、およびEthereumリソース管理チャネルであり、Ethereumでの開発を容易にすることに専念しています。Truffleには次のものがあります。

組み込みのスマートコントラクトのコンパイル、リンク、展開、およびバイナリファイルの管理。

急速な開発中の自動契約テスト。

スクリプト化された、拡張可能な展開およびリリースフレームワーク。

パブリックネットワークまたはプライベートネットワークの数に関係なく展開されるネットワーク環境管理機能

EthPM&NPMが提供するパッケージ管理を使用し、ERC190標準を使用します。

契約と直接通信する直接対話型コンソール(契約を作成した後、コマンドラインで確認できます)。

構成可能なビルドプロセスは、緊密な統合をサポートします。

Truffle環境での外部スクリプトの実行をサポートします。

4.契約コード

注:solidity0.4バージョンはネストされた構造マッピングのトラバースをサポートしていないため、直接表示を容易にするために、トレーサビリティ情報が文字スプライシングの形式で追加されます。

pragma solidity  ^0.4.24;  
 
import "./strings.sol";  
contract testTraceability_addTime {  
 
 
	using strings for *;  
	string constant optStr = "==>";  
	string constant leftOpt = "(";  
	string constant rightOpt = ")";  
	event SavedProducer(uint256 dragId,string dragInfo,string[] traceInfo);  
 
	struct Drag {  
		string dragInfo;  
		string traceInfo;  
	}  
 
	mapping(string => Drag) allDragsInfo;  
 
	constructor () public{  
	}  
 
	function getDrugInformationWithID(string id) returns(string memory,string memory){  
		return(allDragsInfo[id].dragInfo,allDragsInfo[id].traceInfo);  
	}  
 
	function addDragInfo_init(string ID,string memory dragInfomation,string memory company,string memory time) public {  
		allDragsInfo[ID].dragInfo = dragInfomation;  
		string memory info1 = time.toSlice().concat(rightOpt.toSlice());  
		string memory info2 = leftOpt.toSlice().concat(info1.toSlice());  
		string memory info3 = company.toSlice().concat(info2.toSlice());  
		string memory concatStr = info3.toSlice().concat(optStr.toSlice());  
		allDragsInfo[ID].traceInfo = concatStr;  
	}  
 
 
	function addDragInfo_A(string ID,string memory company,string memory time) public {  
		string memory info1 = time.toSlice().concat(rightOpt.toSlice());  
		string memory info2 = leftOpt.toSlice().concat(info1.toSlice());  
		string memory info3 = company.toSlice().concat(info2.toSlice());  
		string memory concatStr = info3.toSlice().concat(optStr.toSlice());  
		allDragsInfo[ID].traceInfo = allDragsInfo[ID].traceInfo.toSlice().concat(concatStr.toSlice());  
	}  
 
	function addDragInfo_B(string ID,string memory company,string memory time) public {  
		string memory info1 = time.toSlice().concat(rightOpt.toSlice());  
		string memory info2 = leftOpt.toSlice().concat(info1.toSlice());  
		string memory info3 = company.toSlice().concat(info2.toSlice());  
		string memory concatStr = info3.toSlice().concat(optStr.toSlice());  
		allDragsInfo[ID].traceInfo = allDragsInfo[ID].traceInfo.toSlice().concat(concatStr.toSlice());  
	}  
 
	function addDragInfo_C(string ID,string memory company,string memory time) public {  
		string memory info1 = time.toSlice().concat(rightOpt.toSlice());  
		string memory info2 = leftOpt.toSlice().concat(info1.toSlice());  
		string memory info3 = company.toSlice().concat(info2.toSlice());  
		string memory concatStr = info3.toSlice().concat(optStr.toSlice());  
		allDragsInfo[ID].traceInfo = allDragsInfo[ID].traceInfo.toSlice().concat(concatStr.toSlice());  
	}  
}  

5.契約の説明

トレーサビリティデータ構造操作には、addDragInfo_init、addDragInfo_A、addDragInfo_B、addDragInfo_C、getDrugInformationWithID Cなどの操作メソッドがあります。addDragInfo_initは、製薬会社が情報を追加するためのメソッドです。addDragInfo_A、addDragInfo_B、およびaddDragInfo_Cは、ディストリビューションでのノード操作の3つのメソッドです。ユーザーは製品IDを使用して、すべてのトレーサビリティ情報を取得します。

6.契約展開にトリュフを使用する

(1)トリュフを取り付ける

truffleのnpmパッケージをグローバルにインストールします。

npm install tr​​uffle -g

(2)トリュフの初期化

プロジェクトディレクトリに入り、次のコマンドを実行します。

トリュフの初期化

(3)関連する構成ファイルを変更する

図2-3に示す3つのファイルを変更します。

 

Ethereumに基づくブロックチェーントレーサビリティケース開発

図2-3トリュフ操作に関連する構成ファイル

次の3つのファイルを紹介します。

1_initial_migration.js(デフォルトの移行契約)

var Migrations = artifacts.require("./Migrations.sol");  
module.exports = function(deployer) {  
   deployer.deploy(Migrations);  
}; 

2_deploy_contracts.js(デプロイする必要のあるコントラクトを構成します)

var testTraceability = artifacts.require("./testTraceability.sol");  
var testTraceability_addTime = artifacts.require("./testTraceability_addTime.sol");  
var strings = artifacts.require("./strings.sol");  
 
module.exports = function(deployer) {  
	deployer.deploy(strings,  {from:"0xe578252579e5f43fe124fe1d8236f0e5250c1197"});// 部署合约,使用旷工地址(保证gas费充足)  
	deployer.link(strings, testTraceability);   // 库链接  
	deployer.deploy(testTraceability,  {from:"0xe578252579e5f43fe124fe1d8236f0e5250c1197"});// 部署合约  
	deployer.deploy(testTraceability_addTime,  {from:"0xe578252579e5f43fe124fe1d8236f0e5250c1197"});// 部署合约  
};  

3_truffle.js(ターゲットEthereumに関連する情報を構成します)

/*  
  *  NB:  since  truffle-hdwallet-provider  0.0.5  you  must  wrap  HDWallet  providers  in  a    
  *  function  when  declaring  them.  Failure  to  do  so  will  cause  commands  to  hang.  ex:  
  *  ```  
  *  mainnet:  {  
  *          provider:  function()  {    
  *              return  new  HDWalletProvider(mnemonic,  'https://mainnet.infura.io/<infura-key>')    
  *          },  
  *          network_id:  '1',  
  *          gas:  4500000,  
  *          gasPrice:  10000000000,  
  *      },  
  */    
//  Allows  us  to  use  ES6  in  our  migrations  and  tests.   
 
require('babel-register')({  
	ignore: /node_modules\/(?!zeppelin-solidity)/  
});  
 
require('babel-polyfill');  
module.exports = {  
   //See <http://truffleframework.com/docs/advanced/configuration>  
   //to customize your Truffle configuration!  
    networks: {  
        development: {  
            host: "127.0.0.1",//合约部署的以太坊节点ip  
            port: 7545,       //合约部署的以太坊节点端口  
            network_id: "*",  // 以太坊网络id  
            gas: 8000000      // 合约部署gas上限  
         }  
     },  
     mocha: {  
         useColors: true  
     },  
     solc: {  
         optimizer: {  
             enabled: true,  
             runs: 200  
         }  
     }  
 };  

(4)契約をまとめる

プロジェクトディレクトリで実行します。

トリュフコンパイル

truffle compile --all(すべて再コンパイル)

(5)移行契約

プロジェクトディレクトリで実行

トリュフの移行

trufflemigrate --reset(すべて移行スクリプトを再実行します)

背景開発の第3部

1.背景開発手順

このブロックチェーンネットワークはEthereumプライベートチェーンに基づいているため、データのセキュリティと信頼性を確保するために、ユーザーはブロックチェーンを直接操作できず、ミドルウェアサービスを使用する必要があります。そのため、チェーン上のデータを制御し、権限管理、メッセージキュー、ブロックデータの一時ストレージなど、その他の必要なサービスを実行できるノードミドルウェアが開発されています。

2.需要分析

第2部から、3つのタイプのビジネスノード(プロデューサー、セラー、リテーラー)はそれぞれデータをブロックチェーンにアップロードする必要があり、ブロックチェーンを直接操作するのではなく、中間サービスに基づいている必要があることがわかります。そのため、図3-1に示すバックグラウンドアーキテクチャが採用されています。つまり、B側とC側のすべてのユーザーは、ブロックチェーンを間接的に操作またはアクセスするためのミドルウェアノードサービスに基づいています。したがって、各ユーザーのさまざまな機能に応じて、さまざまなAPIサービスが開かれ、各ユーザーはブロックチェーン上でさまざまなトランザクションを実行します。図に示すように、簡単に言えば、製薬会社、エージェントA、エージェントB、および小売業者がデータをブロックチェーンに書き込み、ユーザーがブロックチェーンからデータを読み取ります。

 

Ethereumに基づくブロックチェーントレーサビリティケース開発

図3-1ノードの背景アーキテクチャ図

3.開発準備

(1)Nodejs

JavaScriptは、オープンソースコミュニティ全体で最も広く使用されています。現在、JavaScript言語に対応するプロジェクトのほとんどはNode.jsプラットフォームに基づいているため、Nodejsは当然のことであり、最も人気のある開発プラットフォームの1つです。Nodejsは、構築中のChrome V8でのJavaScriptインスタント実行プラットフォームであり、時間駆動型のノンブロッキングI / Oモデルを使用します。軽量で効率的であり、株式、資金、暗号通貨などのデータ集約型のリアルタイムアプリケーションに非常に適しています。待つ。特に、Nodejsのパッケージ管理ツールnpmは、世界で最も活発なコミュニティになっています。開発者が選択できるサードパーティのパッケージはたくさんあり、ブロックチェーン関連のコンポーネントのほとんどは、npmパッケージの形式で開発および適用されています。 、したがって、Nodejsはブロックチェーン開発のための強力なツールです。

使用:Nodejs環境のインストール

(2)エクスプレス

Expressは、シンプルで柔軟なnode.js Webアプリケーションフレームワークであり、さまざまなWebアプリケーションの作成に役立つ一連の強力な機能と豊富なHTTPツールを提供します。Expressを使用すると、フル機能のWebサイトをすばやく構築でき、さまざまなHTTPユーティリティとミドルウェアをサポートし、強力なAPIをすばやく簡単に作成できます。

使用:npmパッケージをインポートします。

(3)web3 API

Ethereumネットワークはノードで構成され、各ノードにはブロックチェーンのコピーが含まれています。スマートコントラクトでメソッドを呼び出す必要がある場合は、ノードの1つからメソッドを見つけて伝える必要があります。Ethereumノードは、JSON-RPCと呼ばれる言語しか認識できません。これは、あまり読みやすくありません。Web3.jsは、これらのあいまいなクエリステートメントを非表示にすることができ、理解しやすいJavaScriptインターフェイスと対話するだけで済みます。

web3.jsは、Ethereumが提供するJavascriptライブラリです。EthereumのJSON RPC APIをカプセル化し、ネットワークステータスの表示、ローカルアカウントの表示、トランザクションとゾーンの表示など、ブロックチェーンと対話する一連のJavascriptオブジェクトと関数を提供します。ブロック、トランザクションの送信、スマートコントラクトのコンパイル/展開、スマートコントラクトの呼び出しなど。これらの中で最も重要なのは、スマートコントラクトと対話するAPIです。したがって、Ethereumに基づいてプロジェクトを開発するには、web3jsの一般的なAPIに精通している必要があります。

使用:npmパッケージをインポートします。

4.ノードコード

nodejs + express + web3jsを使用して単純なノードサーバーを開発し、APIインターフェイスを外部に公開します。ブロックチェーンを操作するためのHttp要求を介した外部呼び出しAPIサービス。

const web3 = require('web3');  
const ethRPC = "ws://localhost:8546";  
const serverPort = 10999;  
const provider = new web3.providers.WebsocketProvider(ethRPC);  
const web3Client = new web3(provider);  
const coinbase = '0xe578252579e5f43fe124fe1d8236f0e5250c1197';  
const contract = require("truffle-contract");  
const express = require('express');  
const app = express();  
const cors = require('cors');  
 
app.use(cors());  
   
var Resp = (function(yj) {  
 
    yj = function() {  
       this.dragId = '';  
       this.dragName = '';  
       this.trace = '';  
    };  
 
    yj.prototype = {  
 
        toJson: function() {  
           return JSON.stringify(this);  
        }  
   };  
        return yj;  
 
 }(Resp || function(){}));  
 
 
function eraseErrorOfContract(arr) {  
 
    for (let contrc of arr) {  
           if (typeof contrc.currentProvider.sendAsync !== "function") {  
           contrc.currentProvider.sendAsync = function () {  
                return contrc.currentProvider.send.apply(  
                    contrc.currentProvider, arguments  
               );  
           }  
       }  
   }  
}  
 
   
const traceabilityJSON = require('../build/contracts/testTraceability_addTime');  
const traceability = contract(traceabilityJSON);  
 
traceability.setProvider(provider);  
var traceabilityInst;  
eraseErrorOfContract([traceability]);  
 
 
traceability.deployed({  
    from: coinbase,  
    gas: 600000  
}).then(instance => {  
    traceabilityInst = instance;  
    // traceabilityInst.addDragInfo_init('NWRuxXJ75fBWUjBZiDIyR16PdFLIOG/odIos/Z8ggxNqrvQyPMcnXjAVB0JAmHYu4LGxyOaA4FIlOJgzGrPvcIndf0EbYZ80l',"999pai","hayao",{from:coinbase,gas:600000}).then(result => {  
 
    //     console.log(result);  
    //     return traceabilityInst.addDragInfo_A('NWRuxXJ75fBWUjBZiDIyR16PdFLIOG/odIos/Z8ggxNqrvQyPMcnXjAVB0JAmHYu4LGxyOaA4FIlOJgzGrPvcIndf0EbYZ80l',"经销商1",{from:coinbase,gas:600000})  
 
    // }).then(result2 => {  
    //     console.log(result2);  
    //     return traceabilityInst.getDrugInformationWithID.call('NWRuxXJ75fBWUjBZiDIyR16PdFLIOG/odIos/Z8ggxNqrvQyPMcnXjAVB0JAmHYu4LGxyOaA4FIlOJgzGrPvcIndf0EbYZ80l',{from:coinbase,gas:600000});  
 
    // }).then(result3 =>{  
    //     console.log(result3);  
    // }).catch(err => {  
    //     console.log(err)  
    // });  
 
});  
 
   
app.get('/addDragInfo_produce',function (req,res) {  // app => router  
 
    let currentTime = new Date().toLocaleString();  
    traceabilityInst.addDragInfo_init(req.query.id,req.query.dragInfo,req.query.company,currentTime,{from:coinbase,gas:600000}).then(result => {  
        console.log(result);  
        res.end("您(生产商)已将药品信息上链!交易hash:"+result.tx);  
    }).catch(err => {  
        console.log(err);  
        res.end(JSON.stringify(err))  
    });  
});  
 
   
 
app.get('/addDragInfo_A',function (req,res) {  // app => router  
 
    let currentTime = new Date().toLocaleString();  
    traceabilityInst.addDragInfo_A(req.query.id,req.query.company,currentTime,{from:coinbase,gas:600000}).then(result => {  
        res.end("您(销售商A)已将信息上链!交易hash:"+result.tx)  
    }).catch(err => {  
        res.end(JSON.stringify(err))  
    });  
 
});  
 
   
app.get('/addDragInfo_B',function (req,res) {  // app => router  
 
    let currentTime = new Date().toLocaleString();  
    traceabilityInst.addDragInfo_B(req.query.id,req.query.company,currentTime,{from:coinbase,gas:600000}).then(result => {  
        res.end("您(销售商B)已将信息上链!交易hash:"+result.tx)  
    }).catch(err => {  
        res.end(JSON.stringify(err))  
    });  
});  
 
   
 
app.get('/addDragInfo_C',function (req,res) {  // app => router  
    let currentTime = new Date().toLocaleString();  
    traceabilityInst.addDragInfo_C(req.query.id,req.query.company,currentTime,{from:coinbase,gas:600000}).then(result => {  
        res.end("您(销售商C)已将信息上链!交易hash:"+result.tx)  
    }).catch(err => {  
        res.end(JSON.stringify(err))  
    });  
});  
 
 
app.get('/getDrugInformationWithID',function (req,res) {  // app => router  
 
    let ret = new Resp();  
    traceabilityInst.getDrugInformationWithID.call(req.query.id,{from:coinbase,gas:600000}).then(result => {  
 
        ret.dragId = req.query.id;  
        ret.dragName = result[0] ;  
        ret.trace = result[1];  
        res.setHeader('Content-Type', 'text/plain;charset=utf-8');  
        res.end("药品ID:"+ret.dragId+'\n'+"药品名称:"+ret.dragName+'\n'+"溯源信息:"+ret.trace);  
 
    }).catch(err => {  
 
        res.end(JSON.stringify(err))  
 
    });  
});  
 
 
var server = app.listen(serverPort, function () {  
 
    var host = server.address().address;  
    var port = server.address().port;  
    console.log( 'Express started on http://'+ host +':' + port  );  
 
}); 

5.ノードサービスの説明

需要分析によると、製薬会社、代理店、小売業者はブロックチェーンにデータを書き込み、ユーザーはブロックチェーンからデータを読み取ります。したがって、以下に示すように、さまざまなユーザーがhttpを介してさまざまなAPIにアクセスし、それぞれのビジネス目標を達成できます。

メーカーデータのアップロード操作:http:// localhost:8546 / addDragInfo_produce

エージェントAのデータチェーン操作:http:// localhost:8546 / addDragInfo_A

エージェントBデータのオンチェーン操作:http:// localhost:8546 / addDragInfo_B

ユーザークエリのトレーサビリティ情報操作:http:// localhost:8546 // getDrugInformationWithID


元のリンク:Ethereumに基づくブロックチェーントレーサビリティケースの開発

おすすめ

転載: blog.csdn.net/JonasErosonAtsea/article/details/109236358