この記事ではスマートコントラクトの基本的知識を確認しつつ、Solidityを利用してEthereumのスマートコントラクトを作成、テストしていきます。
ある程度プログラミングなどしたことがある人ならこの条件を達成していると思います。
スマートコントラクトとはなにか
スマートコントラクトとは、ブロックチェーン内に格納されているプログラムのことです。スマートコントラクトによって、ブロックチェーンがデータからコードに拡張されます。これらはパーティ間の契約を表します。契約はコード化され、アクションが発生するとコードが実行されて、応答が提供されます。
コントラクトの記述に使用する言語は Solidity です。 Solidity は “チューリング完全” です。つまり、明確に定義されたコード化された方法で、複雑なコントラクトを記述することができます。
すべての状態遷移はログに記録され、変更不可であるため、コントラクトを運用環境にリリースする前に十分にテストする必要があります。 バグの修正にはコストがかかる可能性があり、システムに重大な障害が発生する可能性もあります。
スマートコントラクトには、次の主要な性質と利点があります。
- 透明性
ブロックチェーンユーザーはスマートコントラクトを読み取り、APIを使用してそれらにアクセスできる。 - 不変性
スマートコントラクトが実行されると、変更不可能なログが作成される。 - 分散
コントラクトの出力は、ネットワーク上の各ノードによって検証及および確認される。コントラクトの状態はパブリックに公開される。場合によっては”プライベート”変数も公開されることがある。
スマートコントラクト開発のためのツール
スマートコントラクトを効果的に開発するのに役立つツールが多くあります。ここでは利用可能な統合開発環境(IDE)、拡張機能、フレームワークをいくつか紹介します。
IDE
- Visual Studio Code
最新の Web およびクラウド アプリケーションのビルドとデバッグ用に再定義および最適化されたコード エディター。 - Remix
Solidity 言語で Ethereum コントラクトを構築し、トランザクションをデバッグするために使用できる、ブラウザー ベースのコンパイラおよび IDE。
拡張機能
- Blockchain Development Kit for Ethereum
Ethereum 台帳にスマート コントラクトを作成、ビルド、およびデプロイする方法を簡略化できる。
フレームワーク
- Truffle Suite
Ethereum コントラクトをパブリックな台帳にデプロイして実際のコストを発生させる前に、Truffle ツール スイートを使用してテストします。 開発者としての作業を容易にするために、ローカルで開発します。 ツール スイートには Truffle、Ganache、および Drizzle が含まれています。 - OpenZeppelin
OpenZeppelin ツールを使用して、分散アプリケーションの作成、デプロイ、運用を行います。 OpenZeppelin には、コントラクト ライブラリと SDK という 2 つの製品が用意されています。
Blockchain Development Kit for Ethereumをインストール
Blockchain Development Kit for EthereumはVisual Studio Codeで利用できる拡張機能です。この機能を利用して、Ethereumブロクチェーンソリューションでスマートコントラクトを作成、ビルド、デプロイします。
拡張機能をインストール
Visual Studio Codeの左側のサイドバーで[拡張機能]を選択します。「blockchain development kit」と検索して選択し、インストールします。
Blockchain開発キットを使用する前に、次のものがインストールされていることを以下の方法で確認してください。
- Python
ターミナルまたはcmdより”python -V
“でバージョン確認 - Node.js
ターミナルで”node -v
”でバージョン確認 - Git
ターミナルで”git --version
”でバージョン確認
プロジェクトの作成
- コンピュータ上に、プロジェクト用の空のディレクトリを追加します。Blockchain開発キットを使用して最初のプロジェクトを作成します。Visual Studio Code からディレクトリを作成するには、[ターミナル] > [新しいターミナル] にアクセスして、「
mkdir newSolidityProject
」と入力します。 この新しいディレクトリの場所をメモしておきます。 この情報は後で必要になります。 - Visual Studio Code で [表示] > [コマンド パレット] にアクセスします。 検索ボックスに「
Blockchain: New Solidity Project
」と入力します。 入力すると、候補の一覧が表示されます。 - Solidity プロジェクトの種類として、[Create basic project]を選択します。
- [エクスプローラー] ペインを使用して、1 で作成したフォルダーを選択します。 ウィンドウの右下隅に、[Creating new project](新しいプロジェクトの作成中) と表示されます。
Solidity プロジェクトが作成されたら、[エクスプローラー] ペインを開いてプロジェクトのファイルを確認します。
プロジェクトには、Solidityコードのひな形が含まれています。次のディレクトリが存在することがわかります。
- contracts:HelloBlockchain.sol および Migrations.sol コントラクトが含まれています。
- migrations:初期移行とデプロイ コントラクトが含まれています。
- test:JavaScript で記述された HelloBlockchain コントラクトのテストが含まれています。
構成ファイルもいくつか存在します。
- package.json:プロジェクトの詳細と依存関係を定義します。
- truffle-config.json:Truffle の依存関係と構成を定義します。
コンパイル
まず、contractフォルダー内のHelloBlockchain.sol
スマートコントラクトから、始めます。
- [エクスプローラー] ペインの contracts フォルダーで、コントラクト名 [HelloBlockchain.sol] を右クリックします。
- [Build Contracts] を選択し、スマート コントラクトをコンパイルします。右下隅のウィンドウに、コントラクトがビルド中であることが示されます。
- [表示] > [出力] を選択して、コンパイルされたコントラクトに関する情報を表示します。 拡張機能からの出力を表示するには、ウィンドウで [Azure Blockchain]を選択することが必要になる場合があります。
コントラクトをデプロイ
コントラクトが正常にコンパイルされたら、ローカルにデプロイできます。この手順では、Azureアカウントへのサインインが必要になります。
- [エクスプローラー] ペインで contracts フォルダーにアクセスし、コントラクト名 [HelloBlockchain.sol] を右クリックします。
- [Deploy Contracts](コントラクトをデプロイする)を選択します。
- 表示されたウィンドウで、[開発] を選択します。 Azure にまだサインインしていない場合は、サインインするように求められます。
出力ウィンドウに、デプロイされた移行とコントラクトに関する情報が表示されます。
ここには、デプロイされたコントラクトに関する重要な情報またはメタデータが表示されます。
- コントラクトのアドレス
- コントラクト作成トランザクションが含まれていたブロックのタイムスタンプ
- コントラクトをデプロイしたアカウントのアドレス
- トランザクションの後のアカウントの残高(Ether)(残高は100ETHから合計コストを差し引いた値です。)
- 使用されたガスの量とガスの価格(”ガス”とは、Ethereumブロックチェーンプラットフォーム上でのトランザクションの実行やコントラクトの実行に必要な料金を指す。
合計コスト = ガス価格*使用するガス)
Truffleをインストール
TruffleはEthereum用の最も一般的な開発フレームです。NPMを使用してインストールすることができます。
インストール
Truffleはnpmコマンドを利用してインストールできます。ターミナルで、以下のコマンドを実行します。
npm install -g truffle
Truffleがインストールされていることを確認するには、次のように入力します。
truffle
Truffleの使用を開始する
- Visual Stadio Codeで[ターミナル]>[新しいターミナル]にアクセスします。
- ターミナルで
truffle compile
を実行します。ソースファイルが正常にコンパイルされるまで待ちます。 - truffle migrateを実行します。移行が完了するまで待ちます。
Truffleを使用してスマートコントラクトをテスト
newSolidityProjectの一部として生成されたテストを実行してみます。
テストではtest/HelloBlockchain.jsにあります。テストファイルを調べて、テストされる内容を理解していきます。
ターミナルに「truffle test
」と入力します。
TruffleがEthereumクライアントに接続できないため、テストは失敗します。
Could not connect to your Ethereum client with the following parameters:
- host > 127.0.0.1
- port > 8545
- network_id > *
Please check that your Ethereum client:
- is running
- is accepting RPC connections (i.e., "--rpc" option is used in geth)
- is accessible over the network
- is properly configured in your Truffle configuration file (truffle-config.js)
この出力は、テストからアクセスできるローカルのEthereumブロックチェーンクライアントが必要であることを示しています。
Ganace CLI
最も一般的なローカルEthereumブロックチェーンはGanacheです。ターミナルから直接対話できるように、CLIバージョンを使用します。
Ganache CLIをプロジェクトにインストールするには、ターミナルで次をのコマンドを実行します。
npm install -g ganache-cli
Ganache CLIがインストールされたら、次を実行します。
ganache-cli
ブロックチェーンには10個のアカウントが生成され、それぞれがテスト用に使用する100Etherを受け取っていることがわかります。各アカウントには、対応する秘密キーもあります。これらすべてのアカウントにはニーモニックがあります。ニーモニックは一意の12単語のフレーズです。これによってウォレットにアクセスし、アカウントからトランザクションを実行することができます。
出力にはブロクチェーンのアドレスも表示されます。このアドレスはブロックチェーンに接続するために使用します。既定では、アドレスは127.0.0.1:8545です。
次に、truffle test
をもう一度実行します。
すべてのテストに成功したことがターミナルに表示されます。
スマートコントラクトを記述する
作成したnewSolkidityProjectに新しいスマートコントラクトを記述します。配達を想定したコントラクトを作成していきます。
出荷コントラクトを作成する
今回作成するスマートコントラクトでは、オンラインマーケットプレイスから購入した商品の状態を追跡します。コントラクトが作成されると、出荷状態がPending
に設定されます。商品が出荷されると、商品の出荷状態がDelivered
に設定され、別のイベントが生成されます。
1. 作成したプロジェクトの contracts ディレクトリで、Shipping.sol という新しいファイルを作成します。
2. 作成したShipping.solに以下の内容をコピペしてください。
pragma solidity >=0.5.12<=0.8.0;
contract Shipping
{
// 列挙型としてリストされている配送の事前定義値
enum ShippingStatus { Pending, Shipped, Delivered }
// 列挙型ShippingStatusを変数statusに保存します
ShippingStatus private status;
// パッケージが到着したときに起動するイベント
event LogNewAlert(string description);
// これにより、コントラクト状態が初期化されます(プログラムの開始時に列挙型が保留に設定されます)
constructor() public {
status = ShippingStatus.Pending;
}
// 出荷済みに変更する機能
function Shipped() public {
status = ShippingStatus.Shipped;
emit LogNewAlert("Your package has been shipped");
}
// 配信済みに変更する機能
function Delivered() public {
status = ShippingStatus.Delivered;
emit LogNewAlert("Your package has arrived");
}
// 出荷状況を取得する機能
function getStatus(ShippingStatus _status) internal pure returns (string memory) {
// 現在のステータスを確認し、正しい名前を返します
if (ShippingStatus.Pending == _status) return "Pending";
if (ShippingStatus.Shipped == _status) return "Shipped";
if (ShippingStatus.Delivered == _status) return "Delivered";
}
// 出荷されたアイテムのステータスを取得します
function Status() public view returns (string memory) {
ShippingStatus _status = status;
return getStatus(_status);
}
}
3. [エクスプローラー] ペインで、コントラクト名を右クリックし、[Build Contracts] を選択してスマート コントラクトをコンパイルします。
移行を追加する
次に、コントラクトをEthereumネットワークにデプロイできるように、移行ファイルを追加します。
- migrations フォルダーの上に3_deploy_contracts.jsというファイルを作成します。
- 次のコードをファイルに追加します。
const Shipping = artifacts.require("Shipping");
module.exports = function (deployer) {
deployer.deploy(Shipping);
};
配置
次に進む前にコントラクトを正常にデプロイできることを確認します。コントラクト名を右クリックして、[Deploy Contracts]を選択します。表示されたウィンドウで[開発]を選択し、デプロイが完了するのを待ちます。
出力ウィンドウから3_deploy_contracts.js のデプロイがあることを確認します。
スマートコントラクトのテスト
出荷コントラクト用の新しいテストをJavaScriptで記述します。代わりにSolidityを使用してテストを記述するお供できるが、JavaScriptの方がよく使用されます。スマートコントラクトをテストするには、Truffle を使用します。
テストの開始
新しいテストファイルを作成します。
- [ターミナル] で「
truffle create test Shipping
」とと入力し実行すると、テストフォルダーにShipping.jsという新しいファイルが作成されます。 - ファイル内のコードを次のコードに置き換えます
const ShippingStatus= artifacts.require("Shipping");
contract('Shipping', () => {
it("should return the status Pending", async ()=> {
// Instance of our deployed contract
const instance = await ShippingStatus.deployed();
// Checking the initial status in our contract
const status = await instance.Status();
// Checking if the status is initially Pending as set in the constructor
assert.equal(status, "Pending");
});
it("should return the status Shipped", async ()=> {
// Instance of our deployed contract
const instance = await ShippingStatus.deployed();
// Calling the Shipped() function
await instance.Shipped();
// Checking the initial status in our contract
const status = await instance.Status();
// Checking if the status is Shipped
assert.equal(status, "Shipped");
});
it("should return the status Delivered", async ()=> {
// Instance of our deployed contract
const instance = await ShippingStatus.deployed();
// Calling the Shipped() function
await instance.Delivered();
// Checking the initial status in our contract
const status = await instance.Status();
// Checking if the status is Delivered
assert.equal(status, "Delivered");
});
});
イベントのテスト
truffle-asserttionsパッケージを使用して、コントラクトによって送信されたイベントをテストします。このパッケージを使用することで、トランザきション中にイベントが生成されたことをアサートできます。
1. 以下のコマンドを実行してライブラリをインストールします。
npm install truffle-assertions
2. テストファイルの2行目に次のコードを追加します。
const truffleAssert = require('truffle-assertions');
3. イベントから想定どおりの説明が返されることを確認するためのテストをファイル内の最後のテストの後に追加します。
contract('Shipping', () => {
・・・
it('should return correct event description', async()=>{
// Instance of our deployed contract
const instance = await ShippingStatus.deployed();
// Calling the Delivered() function
const delivered = await instance.Delivered();
// Check event description is correct
truffleAssert.eventEmitted(delivered, 'LogNewAlert', (event) =>{
return event.description == 'Your package has arrived';
});
});
});
async/await の使用
.deployed()関数ではPromiseが返されます。そのため、関数の前でawait
を使用し、テストコードの前でasync
を使用しています。この設定は、コントラクトがデプロイされた後、Promise が満たされるまでテストを進めることができないことを意味します。
スマートコントラクトのトランザクションはほとんどすべて非同期であるため、このパターンはテストでよく使用されます。トランザクションが非同期なのは、ブロックチェーン大腸に追加する前に検証またはマイニングする必要があるためです。
全体的に、コントラクトに対して100%のテストカバレッジを目指す必要があります。メインのEthereum ネットワーク、または”メインネット”へのデプロイを計画している場合は特にそうです。
テストの実行
ターミナルで次のコマンドを実行します。
truffle test
すべてのテストに成功することを確認します。
まとめ
この記事ではスマートコントラクトの開発に使用できるツールを利用し、スマートコントラクトの記述やテストを行いました。