プラスプラス開発者ブログ

岩手県盛岡市のシステム開発会社「株式会社プラスプラス」の開発者ブログです。

WindowsでEthereum環境構築でスマートコントラクト、JavaScript実行編

8月1日は何の日?…語呂だと8(ハ)1(イ)ですね!
最高に「ハイ!」って
やつだアアアアアアハハハハハハハハハハーッ

テンション調整を間違えた「軽く熊」です。

以前にスマートコントラクトの記事を書いてから
かなり時間が経ってしまいましたが正当な続編記事です。

ついにスマートコントラクトJavaScriptから呼び出す時がやってきましたッ!


簡単にやり方を説明するとweb3jと呼ばれるクラスを使います。
web3jを使えばJavaScriptからスマートコントラクトを呼び出せます。
と、言う事でEthereum JavaScript APIからweb3jをクローンして、
distフォルダからweb3j.min.jsを取り出しておきます。

metamask をインストールしている場合はブラウザでも動作確認が可能ですが、
今回はEthereum Wallet and MistからMistをダウンロードして使います。

環境構築の時点では最新バージョンは0.10.0でしたが、
最近、Ver 0.11.1にバージョンアップしています。
Ver 0.10.0だとsolidity compilerは 0.4.21までですが、
Ver 0.11.0だとsolidity compilerは 0.4.24に対応しています。

最新バージョンであれば文法仕様変更(コンストラクタ等)も対応していて、
コンパイル出来ますので場合によっては最新版への更新も検討でしょうか。
(バージョン更新する場合には環境の初期化が必要かも知れません。)

今回は以前の記事で作成、登録したコントラクトをそのまま使います。

index.html はこんな感じです。テキスト3つとボタンが2つ。

<!DOCTYPE HTML>
<HTML>
<HEAD>
    <TITLE>テスト</TITLE>
    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=utf-8">
    <meta http-equiv="Cache-Control" content="no-cache">
    <SCRIPT TYPE="text/javascript" SRC="main.js?1"></SCRIPT>
    <SCRIPT TYPE="text/javascript" SRC="web3.min.js?1"></SCRIPT>
</HEAD>
<BODY CLASS="eeffee" onLoad="main_start()">
    <p id="text0">ここにステータスが表示されます。</p>
    <p id="text1">ここに結果が表示されます。</p>
    <p id="text2">ここに結果が表示されます。</p>
    <BR>
    <BUTTON ID="get" CLASS="std" onClick="contract_set()">set</BUTTON>
    <BUTTON ID="set" CLASS="std" onClick="contract_get()">get</BUTTON>
</BODY>
</HTML>



JavaScript は下記のようになりますが、2点書き換える必要があります。

var etherEntryApi;
var etherEntryContract;

// ※この2行を実際にデプロイしたアドレスとABIに書き換える。
var contractAddress = '0x1234567890123456789012345678901234567890';
var contractABI = [ { "constant": false, "inputs": [ { "name": "message", "type": "string" } ], "name": "setMessage", "outputs": [], "payable": false, "stateMutability": "nonpayable", "type": "function" }, { "constant": true, "inputs": [], "name": "Hello", "outputs": [ { "name": "", "type": "string", "value": "Hello World" } ], "payable": false, "stateMutability": "pure", "type": "function" }, { "constant": true, "inputs": [], "name": "getMessage", "outputs": [ { "name": "", "type": "string", "value": "あんどろいど" } ], "payable": false, "stateMutability": "view", "type": "function" }, { "inputs": [], "payable": false, "stateMutability": "nonpayable", "type": "constructor" } ];

function main_start() {
    var p0 = document.getElementById('text0');

    p0.innerHTML = 'web3 start.';
    console.log('web3 start.');

    // Checks Web3 support
    if(typeof web3 !== 'undefined' && typeof Web3 !== 'undefined') {
        // If there's a web3 library loaded, then make your own web3
        web3 = new Web3(web3.currentProvider);
        console.log('connect current.');

    } else if (typeof Web3 !== 'undefined') {
        // If there isn't then set a provider
        web3 = new Web3(new Web3.providers.HttpProvider("http://localhost:8545"));
        console.log('connect local host.');

    } else if(typeof web3 == 'undefined') {
        // If there is neither then this isn't an ethereum browser
        // スマコンを実行できない!
        p0.innerHTML = 'web3 error!! (1)';
        console.log('web3 error!! (1)');
        return;
    }
    console.log('web3 ok. (1)');

    // Check if there are available accounts
    web3.eth.getAccounts(function(e,accounts) { 
        var str = 'account ok.';
        // show the floating baloon
        if (e || !accounts || accounts.length == 0) {
            // アカウントが無いからスマコンを実行できない!
            str = 'web3 no accounts !! (no login ?)';
        }
        console.log(str);
        p0.innerHTML = str;
    });
    console.log('web3 ok. (2)');

    // アドレスが有効か取得してみる
    web3.eth.getCode(contractAddress, function(e, r) { 
        if (! e) {
            // Load the contract
            etherEntryContract = web3.eth.contract(contractABI);
            etherEntryApi = etherEntryContract.at(contractAddress);
        }
    });

    console.log('web3 ok. (3)');

}

function contract_set() {
    // 指定したアカウントで書き込みメソッドを実行する。
    web3.eth.getAccounts(function(e, accounts) {
        etherEntryApi.setMessage('test test !!', {from: accounts[0]}, function(err, res) {
            console.log('setMessage() ok.', err, res);
            var p0 = document.getElementById('text0');
            p0.innerHTML = 'setMessage() ok.'
        });
    });
}

function contract_get() {
    // 固定文字列を返すメソッドを実行する。
    etherEntryApi.Hello(function(err, res) {
        console.log('Hello !', err, res);
        var p1 = document.getElementById('text1');
        if (! err) {
           p1.innerHTML = res;
        } else {
           p1.innerHTML = e;
        }
    });

    // 可変文字列を返すメソッドを実行する。
    etherEntryApi.getMessage(function(err, res) {
        console.log('Hello !', err, res);
        var p2 = document.getElementById('text2');
        if (! err) {
           p2.innerHTML = res;
        } else {
           p2.innerHTML = e;
        }
    });
}


グローバル変数として定義されているcontractAddresscontractABI
実際にデプロイしたコントラクトのアドレスとABIを記述する必要があります。
(まったく同じコントラクトを使っているならABIは同一ですが。念の為。)

アドレスとABIの文字列Ethereum Walletから取得します。
Ethereum Walletで登録済みのMyNewContractを開きます。

f:id:plusplus-calc_kuma:20180801171229p:plain
main.jsに文字列をペーストして書き換えます。

ソースが準備できたところで次は環境です。
今までの環境構築で作成したプライベートネットが動作している前提で始めます。

Mist を起動してURLに「file://d:\test\main.html」を指定します。

f:id:plusplus-calc_kuma:20180801171249p:plain
あれれ?…ファイルが無いとエラーになってしまいました。

何度か試してみましたかMistはローカル参照だと動作しないようです。
そうなると実際にhttpアクセスできるようにする必要があるので
httpサーバーの作成ソフトウェア、Apacheが必要になります。

下記を参考にWindows用バイナリ(httpd-2.4.33-win64-VC15.zip)を手に入れます。
Apache2.4.33のダウンロード及びインストール
公式でWindows用バイナリを用意していないってどういう事だYO!


さらにApachelocalhostが動作する状態に設定します。
初心者でもできる!Apacheをインストールする方法

初期設定だとインストールしたApacheフォルダの中にあるhtdocsフォルダ
サーバーとしてアクセスした時に参照されるので
htdocsフォルダに作成した3つのファイルを配置します。

index.html
main.js
web3.min.js

これでMistに「http://localhost/main.html」を指定して正常に表示されるようになります。

f:id:plusplus-calc_kuma:20180801171302p:plain
右上のアイコンをクリックしてアカウントのログインを行います。
忘れずにプライベートネットのマイニングを開始しておいてください。これでついにスマートコントラクトが実行可能になりました。

getをクリックすると「Hello World」が表示されますが、setをクリックすると
書き込み動作になるのでアカウントの承諾が必要になります。

f:id:plusplus-calc_kuma:20180801171315p:plain
アカウントのパスワードを入力して暫く待ちます。
「setMessage() ok.」が表示されたら再度getをクリックで画面を更新します。

f:id:plusplus-calc_kuma:20180801171325p:plain
文字列が書き変わっているのが確認できます。

Ethereum Walletで登録したコントラクトを参照すると値が変わっていますし、
Ethereum Walletのコントラクトで文字列を書き換えると
Mistでgetをクリックして値が変わります。


今回はこれで終了です。
慣れないJavaScriptでヒドイコードだったかも知れませんが、
いろいろ書き直して
「ブログ閲覧者が『スマコンの世界に……』入門してくるとは……!!」
驚く物を作って貰えたらと思います。


シリーズ記事
WindowsでEthereum環境構築はじめました。その1
WindowsでEthereum環境構築はじめました。その2
WindowsでEthereum環境構築はじめました。その3
WindowsでEthereum環境構築にAndroidくわえました。
WindowsでEthereum環境構築でスマートコントラクトはじめました。
イーサリアムのスマートコントラクトで何ができる?
Androidでmetamask不要のウォレットアプリをビルドする
WindowsでEthereum環境構築でスマートコントラクト、JavaScript実行編 ← この記事だよ!