Android アプリからイーサリアムにつないでテストするまでの簡単な手順をまとめてみました。
イーサリアムクライアントとして、web3j を利用しています。
「ややこしい話は抜きだ!」という方は、GitHub に手順後のプロジェクトが置いてあるのでお使いくださいませ。
では、サクサクいきましょう。
プロジェクトの作成
Android Studio を起動して新規プロジェクトを作成します。
アプリ名は StudyWeb3j、プロジェクトフォルダは適宜、あとはデフォルトで。
APIレベルはあまり古くなければ動くと思います。とりあえず 23(Android 6.0) に。
このアプリは画面タップでテストを実行させるだけなので、シンプルな Empty Activity を選択。
web3j の導入
Gradle Scripts → build.gradle(Project: StudyWeb3j) を開き、参照リポジトリに mavenCentral() を追加します。
buildscript {
repositories {
//- 追加 --------
mavenCentral()
//---------------
google()
...
Gradle Scripts → build.gradle(Module: app) を選択し、アプリの依存関係に web3j を追加します(※バージョンは現在のAndroid向けの最新)。
dependencies {
...
androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2'
//- 追加 -------------------------------------------
implementation 'org.web3j:core:4.2.1-android'
//--------------------------------------------------
}
さて、このままだとビルド時に "Static interface methods are only supported starting with Android N" というエラーが発生するので、build.gradle(Module: app) の android.buildTypes ブロック内にて、Javaのバージョンを指定します。
android {
...
buildTypes {
release{
...
}
//- 追加 -------------------------------------------
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
//---------------------------------------------------
}
}
最後に、app -> manifests -> AndroidManifests.xml を開き、アプリがブロックチェーンとやりとりできるように、通信のパーミッションを追加します。
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sample.studyweb3j">
<!-- 追加 -->
<uses-permission android:name="android.permission.INTERNET"/>
<!-- -->
<application
...
ビルドが通れば web3j の準備は万端です。
テストコードの組み込み
下記の3ファイルをソースフォルダ StudyWeb3j/app/src/main/java/com/sample/studyweb3j/ に追加します。
・HelloWorld.java
・TestWeb3.java
・Web3Helper.java
追加後、各クラスがプロジェクトに認識されてるかを確認してください。
追加されたクラスの役割は下記となります。
・HelloWorld … イーサリアムにデプロイされたコントラクトをラップするクラス
・TestWeb3 … テスト処理を担当するクラス
・Web3Helper … イーサリアムアカウントを管理するヘルパークラス
では早速、MainActivity のメンバーに Web3Test のインスタンスを追加しましょう。
public class MainActivity extends AppCompatActivity {
//- 追加 テストクラスのインスタンス ----
private TestWeb3 testWeb3;
//--------------------------------------
つづいて、MainActivity の作成に合わせて Web3Test インスタンスを作成するコードを追加します。
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//- 追加 テストインスタンスの作成 -----------------
testWeb3 = new TestWeb3( this );
//-------------------------------------------------
おっと、初期化に先立って、web3j ライブラリが利用する暗号アルゴリズムを有効にするための処理も呼んでおきます(※これがないと「ECDSAアルゴリズムが無いよ」例外がでます)。
//- 追加 ECDSAアルゴリズムを利用するための設定 ----
Web3Helper.SetupBouncyCastle();
//-------------------------------------------------
最後に、タッチイベントを組み込んで、画面がタップされたらテストが実行されるようにします。
//- 追加 タッチイベント:押下されたらテスト処理を呼び出す -------
@Override
public boolean onTouchEvent(MotionEvent event) {
if( event.getAction() == MotionEvent.ACTION_DOWN ){
// テストの呼び出し
testWeb3.test();
}
return super.onTouchEvent(event);
}
//---------------------------------------------------------------
これでコーディングは終了、お疲れさまでした!
早速ビルド&実行しましょう。
動作確認
アプリが起動したら LogCat を表示し、Android端末の画面をタップしてテストを開始しましょう。
**一回目のログ(タップで開閉します)**
@ TestWeb3: START...
@ [setTarget]
@ target=4(Rinkeby)
@ [setAccount]
@ loadAccount EXCEPTION e=/data/user/0/com.sample.studyweb3j/files/key.json (No such file or directory)
@ CREATE NEW ACCOUNT
@ Write down below json code to import generated account into your wallet apps(e.g. MetaMask)
{"address":"143cffb00a1100b5df461686c13accf46cda0e00","id":"dcf655d2-e38b-4300-ae37-830488bfeea1","version":3,"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"5d53f2ed9122b43de787685a1fabe179"},"ciphertext":"1e4c4a61bb3a03de3e4313f6c9023985f7b16977351a886b8163002f5697969e","kdf":"scrypt","kdfparams":{"dklen":32,"n":4096,"p":6,"r":8,"salt":"d762c0e0e1ae60d091d4a34f6f18d154c536d9fc7befdc478d1c96832637f5a9"},"mac":"fe66a4cc41f119ea30fcb13f503b17a7a512ec7806bef5821b7ff105f3a85335"}}
@ privateKey=f49ebafaa01f39e7895efa75e4ddacffd55b003fb9136dfa931ad760664cf001
@ CURRENT ACCOUNT
@ ethereumAddress=0x143cffb00a1100b5df461686c13accf46cda0e00
@ [checkBalance]
@ balance=0wei
@ [checkSend]
@ EXCEPTION e=java.lang.RuntimeException: Error processing transaction request: insufficient funds for gas * price + value
@ [execCheckHelloWorld]
@ EXCEPTION e=Unable to execute Ethereum request
@ [execDeployHelloWorld]
@ EXCEPTION e=java.lang.RuntimeException: Error processing transaction request: insufficient funds for gas * price + value
@ TestWeb3: FAILED TO INTERACT [HellowWorld] CONTRACT
@ TestWeb3: FINISHED
テストが開始されると、アプリは Rinkeby テストネットへ接続します。
その後、アプリのプライベート領域にアカウント情報(jsonファイル)があるかをチェックし、有効なファイルが無い場合(初回起動時)、アカウントを新規作成します。この時、作成されたアカウント情報は、key.json の名前でアプリのプライベート領域に保存され、次回以降のテストに利用されます。
ログを見ると、新規アカウント 0x143cffb00a1100b5df461686c13accf46cda0e00 が作成されていることがわかります。
また、作成直後のアカウントには残高がないので、送金やコントラクトのデプロイテストが残高不足の例外 "Error processing transaction request: insufficient funds for gas * price + value" により、失敗している様子がうかがえます。
お次は、上記のアカウントへイーサを送金した状況で再テストしてみましょう(※イーサの送金はMetaMask 等で行ってください)。
**二回目のログ(タップで開閉します)**
@ TestWeb3: START...
@ [setTarget]
@ target=4(Rinkeby)
@ [setAccount]
@ FOUND KEY FILE: json={"address":"143cffb00a1100b5df461686c13accf46cda0e00","id":"dcf655d2-e38b-4300-ae37-830488bfeea1","version":3,"crypto":{"cipher":"aes-128-ctr","cipherparams":{"iv":"5d53f2ed9122b43de787685a1fabe179"},"ciphertext":"1e4c4a61bb3a03de3e4313f6c9023985f7b16977351a886b8163002f5697969e","kdf":"scrypt","kdfparams":{"dklen":32,"n":4096,"p":6,"r":8,"salt":"d762c0e0e1ae60d091d4a34f6f18d154c536d9fc7befdc478d1c96832637f5a9"},"mac":"fe66a4cc41f119ea30fcb13f503b17a7a512ec7806bef5821b7ff105f3a85335"}}
@ CURRENT ACCOUNT
@ ethereumAddress=0x143cffb00a1100b5df461686c13accf46cda0e00
@ [checkBalance]
@ balance=200000000000000000wei
@ [checkSend]
@ transaction hash=0x9ad7d8e5b54b7d5de1d0070e126274d3ddfced383611866897b92dcbb432cb08
@ [execCheckHelloWorld]
@ EXCEPTION e=Unable to execute Ethereum request
@ [execDeployHelloWorld]
@ DEPLOYED: contractAddress=0xe82a3e49c9d2408c388897c1a3c623fce083fc07
@ [execInteractHelloWorld]
@ BEFORE: HelloWorld.getWord()=Hello web3j world
@ HelloWorld.setWord( Greeting from web3j at Wed Mar 04 01:22:00 GMT+09:00 2020 )
@ AFTER: HelloWorld.getWorld()=Greeting from web3j at Wed Mar 04 01:22:00 GMT+09:00 2020
@ TestWeb3: FINISHED
今回は残高が十分(0.2 eth)あるので、イーサの送金 や コントラクトのデプロイ等が成功しているのがわかります。
補足
コントラクトのラップクラスの作り方
上でも書きましたが HelloWorld.java はコントラクトをラップするクラスとなります。もととなるソースファイル HelloWorld.sol から出力された .abi と .bin を、web3j コマンドで変換して作成しています。
web3j コマンドによるラップクラスの生成手順等に関しては、GitHub の ReadMe.md をご覧ください。
開発環境
・Windows10
・AndroidStudio 3.1.2
・java 1.8.0_152
検証端末
・Nexus6 android 7.1.1
以上です。
この記事が少しでも、誰かのお役に立ってくれたら嬉しいです。