2017年11月19日日曜日

【Android Studio 3.0】既存アプリをAndroid Studio 3.0に対応させる。

  • 公開日:2017年11月18日

記事概要

kotlinとJavaの混じったアプリを2.3.x系でビルドすると、制約がひどく、ビルドも遅いのでAndroid Studio 3.0に移行しました.
結構色々なエラーと修正が発生したので、今後移行する人の役に立つように作業メモを記載して残しておきます。

環境

  • Android Studio 2.3.1 → Android Studio 3.0

Android Studio 3.0に対応

ダイアログで表示されるgradleのversionにアップデートします。ダイアログでOKを押下します。
自動的にbuild.gradleと/gradle/wrapper/gradle-wrapper.propertiesが上書きされます。

{project_folder}/build.gradle

     dependencies {
-        classpath 'com.android.tools.build:gradle:2.3.3'
+        classpath 'com.android.tools.build:gradle:3.0.0'
         classpath 'com.google.gms:google-services:3.0.0'
     }

{project_folder}/gradle/wrapper/gradle-wrapper.properties

-#Fri Jul 28 22:16:36 JST 2017
+#Sat Nov 18 19:40:49 JST 2017
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-3.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.1-all.zip

jackOptionsを無効化

gradleの変更だけだと、おそらくビルドに失敗します。jackOptionsが有効だと以下のエラーが出力されます。

Event Log

Warning:The Jack toolchain is deprecated and will not run. To enable support for Java 8 language features built into the plugin, remove 'jackOptions { ... }' from your build.gradle file, and add

android.compileOptions.sourceCompatibility 1.8
android.compileOptions.targetCompatibility 1.8

Future versions of the plugin will not support usage of 'jackOptions' in build.gradle.
To learn more, go to https://d.android.com/r/tools/java-8-support-message.html

エラーの内容は、jackOptionsはもうすぐサポート終わるから使わないでってことです。
なので、以下のように変更します。

{project_folder}/app/build.gradle

    defaultConfig {
         // something...
         vectorDrawables.useSupportLibrary = true
-        jackOptions {
-            enabled true
-        }
         multiDexEnabled true
         // something...
    }

jackOptionsを削除するだけです。sourceCompatibilityとtargetCompatibilityは残しておきます。

flavor dimensionsの設定

Android Studio 3.0からは、単一のflavor dimensionを使用する場合でも、すべてのflavorが名前付きflavor dimensionに属している必要があります。でないと、以下のようなエラーが発生します。

Event Log

All flavors must now belong to a named flavor dimension. Learn more at https://d.android.com/r/tools/flavorDimensions-missing-error-message.html

つまり、dimensionを定義してね。ってことです。正直、ダメな仕様だと思います。大規模なプロジェクトには便利な機能ですが、必須である理由がわかりません。大抵のアプリは小さいアプリなはずです。いずれ元に戻ると思いたいですね。
ビルド周りは大きく変わっているので、面倒でもサンプルプロジェクトを作成して仕様を確認することをお勧めします。ドキュメントを読むだけだと、ピンと来ないと思います。

私の環境は分ける必要がないので、以下のように名前を一つ追加しただけです。

{project_folder}/app/build.gradle

+    flavorDimensions 'normal'
     productFlavors {
         dev {
+            dimension 'normal'
         }
         prod {
+            dimension 'normal'
         }
     }

kotlinのバージョンアップ

jackOptionsが不要になったので、kotlinのバージョン縛りがなくなりました。なので、このままだと以下のエラーが発生します。

Event Log

Unable to find method 'com.android.build.gradle.internal.variant.BaseVariantData.getOutputs()Ljava/util/List;'. Possible causes for this unexpected error include:
Gradle's dependency cache may be corrupt (this sometimes occurs after a network connection timeout.) Re-download dependencies and sync project (requires network)
The state of a Gradle build process (daemon) may be corrupt. Stopping all Gradle daemons may solve this problem. Stop Gradle build processes (requires restart)
Your project may be using a third-party plugin which is not compatible with the other plugins in the project or the version of Gradle requested by the project.
In the case of corrupt Gradle processes, you can also try closing the IDE and then killing all Java processes.

エラーにkotlinの文字が出ないのは良くないですね。いずれは改善して欲しいですね。
kotlinのアップデートはgradleの記載を変更するだけです。

{project_folder}/app/build.gradle

 buildscript {
     ext {
-        kotlin_version = '1.1.1'
+        kotlin_version = '1.1.51'
     }

buildToolのバージョンアップ

buildToolも更新が必要です。buildToolのバージョンが古いと以下の警告メッセージが表示されます。

Event Log

Error:The specified Android SDK Build Tools version (26.0.1) is ignored, as it is below the minimum supported version (26.0.2) for Android Gradle Plugin 3.0.0.
Android SDK Build Tools 26.0.2 will be used.
To suppress this warning, remove "buildToolsVersion '26.0.1'" from your build.gradle file, as each version of the Android Gradle Plugin now has a default version of the build tools.

gradleを更新します。

{project_folder}/app/build.gradle

 android {
-    compileSdkVersion 26
+    compileSdkVersion 27
     buildToolsVersion rootProject.buildToolsVersion

{project_folder}/build.gradle

 allprojects {
     repositories {
+        google()
         mavenCentral()
-        maven { url 'https://maven.google.com' }
     }
 }

 ext {
-    buildToolsVersion = "26.0.1"
-    supportLibVersion = "26.1.0"
+    buildToolsVersion = "27.0.1"
+    supportLibVersion = "27.0.1"
     multidexVersion = "1.0.0"
-    constraintLayoutVersion = "1.0.2"
+    constraintLayoutVersion = "1.1.0-beta3"
     rxandroidVersion = "2.0.1"
     rxjavaVersion = "2.1.3"

maven { url 'https://maven.google.com' } を google() に書き換えできるようになりました。

また、Android Studio 3.0だと、constraintLayoutが安定してIDEで表示されるようになりました。ConstraintLayoutを使うとviewの入れ子を少なくできて、描写が高速になるので、積極的に利用していきましょう。beta版が嫌な人は1.0.2でも良いと思います。サポートライブラリArchitecture Componentsの更新も忘れずに。

コード修正

最後にkotlinのアップデートとsdk27のアップデートのコード対応を行います。
私の環境で発生したエラーと修正方法を記載しておきます。

Error: style attribute '@android:attr/windowEnterAnimation' not found.

@が不要になりました。

{project_folder}/app/src/main/res/values/styles.xml

     <style name="Animations.GrowFromBottom">
-        <item name="@android:windowEnterAnimation">@anim/grow_from_bottom</item>
-        <item name="@android:windowExitAnimation">@anim/shrink_from_top</item>
+        <item name="android:windowEnterAnimation">@anim/grow_from_bottom</item>
+        <item name="android:windowExitAnimation">@anim/shrink_from_top</item>
     </style>
 
     <style name="Animations.GrowFromTop">
-        <item name="@android:windowEnterAnimation">@anim/grow_from_top</item>
-        <item name="@android:windowExitAnimation">@anim/shrink_from_bottom</item>
+        <item name="android:windowEnterAnimation">@anim/grow_from_top</item>
+        <item name="android:windowExitAnimation">@anim/shrink_from_bottom</item>
     </style>
 
     <style name="Animations.PopDownMenu">
-        <item name="@android:windowEnterAnimation">@anim/grow_from_topleft_to_bottomright</item>
-        <item name="@android:windowExitAnimation">@anim/shrink_from_bottomright_to_topleft</item>
+        <item name="android:windowEnterAnimation">@anim/grow_from_topleft_to_bottomright</item>
+        <item name="android:windowExitAnimation">@anim/shrink_from_bottomright_to_topleft</item>
     </style>

Android3.0からは、デフォルトでAAPT2が有効になったことの影響です。

Error: Smart cast to 'Bundle' is impossible, because 'arguments' is a mutable property that could have been changed by this time

getArguments()のSmart castができなくなりました。コードを見ると、sdk27(サポートライブラリ)では以下のように実装されています。

android.support.v4.app.Fragment

    /**
     * Return the arguments supplied when the fragment was instantiated,
     * if any.
     */
    @Nullable
    final public Bundle getArguments() {
        return mArguments;
    }

sdk26(26.1.0)では以下のような実装でした。

android.support.v4.app.Fragment

    /**
     * Return the arguments supplied when the fragment was instantiated,
     * if any.
     */
    final public Bundle getArguments() {
        return mArguments;
    }

@Nullableが追加されています。ブロックの引数を指定する必要があります。

{project_folder}/MainActivity.java

-        arguments?.let {
+        arguments?.let { it ->


というかこれは既存コードに潜在的なバグが含まれていたっぽいです。。。letのブロック内の変数はなるべくitに置き換えるか、名称を変更するべきですね。

Error: Class 'XYZPagerAdapter' is not abstract and does not implement abstract base class member public abstract fun isViewFromObject(@NonNull p0: View, @NonNull p1: Any): Boolean defined in android.support.v4.view.PagerAdapter

PagerAdapterにも変更が入ったようです。コードを見ると、sdk27(サポートライブラリ)では以下のように実装されています。

android.support.v4.app.Fragment

    public abstract boolean isViewFromObject(@NonNull View view, @NonNull Object object);

sdk26(26.1.0)では以下のような実装でした。

android.support.v4.app.Fragment

    public abstract boolean isViewFromObject(View view, Object object);

これも同じように@Nullableが追加されています。?を削除します。

{project_folder}/MainActivity.java

-        override fun isViewFromObject(view: View?, `object`: Any?): Boolean {
+        override fun isViewFromObject(view: View, obj: Any): Boolean {

isViewFromObjectの引数って@NonNullだったんですね。チェックしてコード書いてた時期があったような。

Error: Fragment

Fragmentも変更されてますね。これもsdk27で@Nullableのアノテーションが追加されたのではないでしょうか。

android.support.v4.app.Fragment

    @Nullable
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        return null;
    }

sdk26(26.1.0)では以下のような実装でした。

android.support.v4.app.Fragment

    @Nullable
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
            @Nullable Bundle savedInstanceState) {
        return null;
    }

LayoutInflaterが@NonNullになっています。これは良い修正ですね。以下のように修正します。

{project_folder}/MainActivity.java

-    override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
+    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,

-        val view = inflater!!.inflate(R.layout.fragment_floating_action, container, false)
+        val view = inflater.inflate(R.layout.fragment_floating_action, container, false)

Error: Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type FragmentActivity?

FragmentActivityも変更されてますね。これも@Nullableのアノテーションが追加されたのではないでしょうか。

android.support.v4.app.Fragment

    final public FragmentActivity getActivity() {
        return mHost == null ? null : (FragmentActivity) mHost.getActivity();
    }

    public Context getContext() {
        return mHost == null ? null : mHost.getContext();
    }

sdk26(26.1.0)では以下のような実装でした。

android.support.v4.app.Fragment

    @Nullable
    final public FragmentActivity getActivity() {
        return mHost == null ? null : (FragmentActivity) mHost.getActivity();
    }

    /**
     * Return the {@link Context} this fragment is currently associated with.
     */
    @Nullable
    public Context getContext() {
        return mHost == null ? null : mHost.getContext();
    }

Safe-call演算子を使って書き換えます。

色々

-            activity.supportFragmentManager.popBackStack()
+            activity?.supportFragmentManager?.popBackStack()

-            Toast.makeText(context, context.getString(R.string.sample), Toast.LENGTH_SHORT).show()
+            Toast.makeText(context, context?.getString(R.string.sample), Toast.LENGTH_SHORT).show()

gradle修正

gradleを変更します。

compileをimplementationに変更します。

implementationにするとビルドが早くなります。
なぜなら、implementationは、コンパイルする必要がある依存ライブラリだけをコンパイルします。

{project_folder}/build.gradle

-    compile 'com.android.support:multidex:' + rootProject.multidexVersion
-    compile 'com.android.support:appcompat-v7:' + rootProject.supportLibVersion
-    compile 'com.android.support:design:' + rootProject.supportLibVersion
-    compile 'com.android.support:support-v4:' + rootProject.supportLibVersion
-    compile 'com.android.support.constraint:constraint-layout:' + rootProject.constraintLayoutVersion
-    compile 'com.android.support:recyclerview-v7:' + rootProject.supportLibVersion
-    compile 'com.android.support:cardview-v7:' + rootProject.supportLibVersion
+    implementation 'com.android.support:multidex:' + rootProject.multidexVersion
+    implementation 'com.android.support:appcompat-v7:' + rootProject.supportLibVersion
+    implementation 'com.android.support:design:' + rootProject.supportLibVersion
+    implementation 'com.android.support:support-v4:' + rootProject.supportLibVersion
+    implementation 'com.android.support.constraint:constraint-layout:' + rootProject.constraintLayoutVersion
+    implementation 'com.android.support:recyclerview-v7:' + rootProject.supportLibVersion
+    implementation 'com.android.support:cardview-v7:' + rootProject.supportLibVersion

以上で終了です。お疲れさまでした。
コンパイルが随分と高速化されました。

結論

kotlinのNull Safety機能は強力ですね。小さいアプリだとAndroid Architecture Componentsは不要ですね。導入すると無駄にコードも増えますし。

kotlinの導入とconstraintLayoutの導入は本当にお勧めです。8.1のリリースも近いので、そろそろ既存アプリのandroid studio3.0の対応は終了させておきたいですね。

PICK UP

運営サイト


この記事がお役にたちましたらシェアをお願いします

このエントリーをはてなブックマークに追加

2017年11月13日月曜日

【書評】Kotlinイン・アクション

  • 公開日:2017年11月13日
  • 最終更新日:2017年11月29日
  • 学習期間:2017年11月06日 - 途中
  • 学習期間:705minites[途中]

初めに

本格的にkotlinを利用するようになってきたので、詳細な仕様を把握するために本書を購入しました。
この記事は感想とまとめを兼ねています。

本の評価には色々な視点がありますが、私はjavaのサーバーサイド、android, kotlinの開発経験があるので、その視点で本書を評価しています。

また、まとめの量が多くなってきたので、更新しながら感想を書いていきます。つまり、途中の殴り書きです。。。
良書だし、ちゃんと最後にまとめます。

紙 or 電子書籍

頻繁に持ち運ぶなら電子書籍。職場や家置いとくなら紙で良いと思います。私はkindleで買いました。kindleだと容量を70Mくらい使うので、空容量に注意しましょう。

本書の対象者

Javaの実装経験と、簡単なkotlinの実装経験がある人が読む本です。javaやkotlinの実装経験がない人が読む本ではありません。

kotlinの実装経験がない場合は、「Kotlinスタートブック」から読みましょう。

章別感想とまとめ

後々開発や復習で見返すようためのメモ書き。気になった事項の調査等。結構無駄に深く読み込んでいる箇所もあるので、そこは無視してください。

1章

kotlinは実用主義。アイデアを探求する言語ではない。
多重配列が使いにくいのはそれでかな?
もしくは、 Effective Javaの項目25「 配列よりリストを好む 」が適用されているか。。。

2章

if文(p23 - p24)

ifは式なので、値を返す

ブロック本体と式本体で記述可能

{project_folder}/inaction/If.kt

fun max (a : Int = 0, b : Int = 0) : Int {
  return if a > b : a ? b
}

{}で囲むことを「ブロック本体を持つ」という。
return文が必要。

式本体だけで書く

{project_folder}/inaction/If.kt

fun max (a : Int = 0, b : Int = 0) : Int = if a > b : a ? b

直接返す場合は「式本体を持つ」という。

式本体の関数の場合は、戻り値も省略可能

{project_folder}/inaction/If.kt

fun max (a : Int = 0, b : Int = 0) = if a > b : a ? b

変数

なるべくval(イミュータブル(不変))で宣言するべき。

クラス

valはイミュータブル(不変)なのでgetterのみ

varはミュータブル(可変)なのでgetterとsetterの両方作成

プロパティを呼び出すとき、実際はgetterを利用している。

greteな仕様だ。

カスタムアクセサ

使ったことない。p33

enum

私は、Android開発ではenumは使わないで、Support Annotationを使う派です。でも、Android Architecture Componentsでは普通に使われていますね。Lifecycleのpublic enum Stateとか。。。proguardで難読化すれば問題はないのですが。。。

Androidの神と呼ばれているJakeWhartonさんはenum使うべき派だそうです。参考
理由は、我々は組み込みエンジニアではなく、Webエンジニアである。との理由だそうです。AndroidエンジニアはWebエンジニアではなく、組み込みエンジニアであるじゃないのかな???よくわかりません。。。

when

whenは式なので値を返す。当然、ブロックを使うこともできます。
javaと異なり、オブジェクトも利用可能。本書では、setOfでSetオブジェクトを作成して比較している。

setOfはkotlinで以下のように実装されている。

{project_folder}/inaction/If.java

public fun <T> setOf(vararg elements: T): Set<T> = if (elements.size > 0) elements.toSet() else emptySet()

Setオブジェクトを返していますね。toSetはkotlin.collectionsパッケージで実装されています。

{project_folder}/inaction/If.java

/**
 * Returns a [Set] of all elements.
 *
 * The returned set preserves the element iteration order of the original array.
 */
public fun <T> Array<out T>.toSet(): Set<T> {
  return when (size) {
  0 -> emptySet()
  1 -> setOf(this[0])
  else -> toCollection(LinkedHashSet<T>(mapCapacity(size)))
  }
}

ここでもwhenを使ってますね。out Tのout 修飾子を利用しています。分散アノテーションともいいます。つまりクラスC は T の プロデューサ であり、 T の コンシューマ ではない、と考えます。

スマートキャスト

kotlinの地雷ですね、これ。ネットにもあまり良い説明が載ってない。どうしても好きになれない仕様。

isでチェックとキャストを両方やる。

valで宣言されている必要がある。

3章

トップレベル関数(p64 - 66)

説明がわかりにくいです。要は、javaのようにクラス宣言をしなくてもメソッドを作成できるし、呼び出せるってことですね。

まずはjavaで乗数を計算するクラスと実行クラスを実装してみます。

{project_folder}/inaction/Top.java

package inaction;

public class Top {
    public static Double topFunction() {
        double  a = 2.0;
        return Math.pow(a, 2.0);
    }
}

{project_folder}/Jikko.java

import inaction.Top;

public class Jikko {
    public static void main(String []args) {
        System.out.println(Top.topFunction());
    }
}

実行結果は[4]です。両方ともclassを宣言して、メソッドにstatic宣言をしています。
kotlinの場合は、classとstatic宣言が不要になります。

{project_folder}/Jikko.kt

import inaction.topFunction

fun main(args : Array) {
    System.out.println(topFunction())
}

{project_folder}/inaction/Top.java

package inaction

fun topFunction() : Int {
    val a = 2.0
    return Math.pow(a, 2.0).toInt()
}

結果は同じように4になります。javaクラスにコンパイルされる時に勝手にクラスが作成されて、メソッドがstaticになります。
さらに、ファイルクラスの名前も変更できます。

{project_folder}/inaction/Top.java

@file:JvmName("AnotherTop")
package inaction

fun topFunction() : Int {
    val a = 2.0
    return Math.pow(a, 2.0).toInt()
}


@file:JvmNameアノテーションでファイル名を変更できます。

呼び出してみます。

{project_folder}/inaction/Top.java

import inaction.AnotherTop;

public class Jikko {
    public static void main(String []args) {
        System.out.println(AnotherTop.topFunction());
    }
}

別名AnotherTopで呼び出せています。

拡張関数

これはkotlinを使っている人はみんな知っていますね。

拡張関数はオーバーライドは無理なことを覚えておきましょう。静的関数だから、当たり前ですね。

可変長引数

使うケースは、まずない気がしますが、忘備録的に記載しておきます。

{project_folder}/inaction/Kahen.kt

fun main(args : Array<String>) {
    accept("test1",  "test2", "test3")
}

fun accept(vararg msg: String) {
    System.out.println(listOf(*msg))
}

可変長引数は、スプレッド演算子「*」を使います。

to

mapOfで利用するtoは関数です。中置呼び出しと呼びます。toはPairクラスを返します。ScalaのTupleと同じ機能です。

{project_folder}/inaction/To.kt

    val map = mapOf(1 to "one", 2 to "two")
    for ((key, value) in map.iterator().withIndex()) {
        System.out.println("key:${key}, valu:${value}")
    }

key:0, valu:1=one
key:1, valu:2=two

トリプルクヴォート

あまり使い道がなさそう

ローカル関数

関数の中で関数の宣言をすることです。javaScriptのような記載が可能です。無駄な宣言を減らせます。

4章

kotlinのクラスやメソッドはデフォルトでfinal。継承可能なクラスはopenをつける必要がある。

{project_folder}/inaction/test.kt

class Button {
    fun disable() {}
    open fun amimate() {}
    fun click() {}
}

// finalエラーが出る。
class TextButton : Button() {

}

openを追加します。

{project_folder}/inaction/test.kt

open class Button {
    fun disable() {}
    open fun amimate() {}
    fun click() {}
}

class TextButton : Button() {
    override fun amimate() {
        super.amimate()
    }

    // これはfinalエラー
    override fun click() {}
    
}

デフォルトがfinalの利点は、スマートキャストが可能になることです。

ただし、abstractクラスのメソッドは継承前提なので、openは不要。

sealed class

暗黙的にopenのついているクラス。クラスの継承を制限するために使います。インタフェースにはsealedがつけられません。子クラスにdataクラスを持つこともできます。
whenでelse文で使うとelseが必要なくなります。まだあまり使い道が理解できてません。

クラス移譲 byキーワード

説明がわかりにくいです。記載してあるコードも動かないし。移譲に関しては、公式サイトを読みましょう。クラス移譲は、インターフェースの実装を他のクラスに委譲できます。
デザインパターンのデリゲートパターンですね。

{project_folder}/inaction/test.kt

interface Base {
    fun onClick()
}

class BaseImpl() : Base {
    override fun onClick() {
        System.out.println("clicked!!")
    }
}

class Application(base: Base) : Base by base

fun main(args: Array) {
    val b = Application(BaseImpl())
    Application(b).onClick()
}

オブジェクト宣言

シングルトンを作成する

5章

コレクション操作

mapは集約

filterはフィルタリング

遅延コレクション操作

一時オブジェクトを生成しないコレクションチェーンです。

mapは集約

SAM

関数が一つのjava interfaceはラムダで代用できる。

匿名objectでも代用できるが、呼び出しごとにインスタンスを生成する。

インスタンスを何度も生成しないで利用する場合は、以下のように実装する(p168)

{project_folder}/inaction/test.kt

vae listener = OnClickListener { view - >
  // something

}

ラムダ内でthisは使えない。なぜならコードブロックだから。

with

最初の引数を、二番目の引数のラムダレシーバに変換する。

{project_folder}/inaction/test.kt

fun alphabet = with(StirngBuilder()) {
  // something
toString
}

上記のような書き方も可能。。。

ラムダの実行結果を返す。
レシーバ(引数)を返したい場合は、applyを使う

apply

レシーバ(引数)を返したい場合に使う。TextViewで複数の値を設定する場合に利用する。(p172)

6章

エルビス演算子

監獄ロックだろ。知ってるよ。

null値の代わりにデフォルトを返す演算子です。

{project_folder}/inaction/test.kt

fun alphabet = with(aaa :Stvring?) {
  val msg: String? = aaa ?: "hello"
}

as?

null値の代わりにデフォルトを返す演算子です。

let

型のチェック

{project_folder}/inaction/test.kt

if (aaa != null) {
  mesoddo(aaa)
}

{project_folder}/inaction/test.kt

aaa?.let { mesoddo(it)}

関連記事

この記事がお役にたちましたらシェアをお願いします

このエントリーをはてなブックマークに追加

2017年11月8日水曜日

【ethereum】ethereumのインストールと動作の確認

  • 公開日:2017年10月26日
  • 実施日:2017年10月26日 - 2017年11月08日

記事概要

仮想通貨の仕組みを理解するために、ethereumをインストールして色々と試してみたので、その作業記録です。
この記事を使って開発環境を構築しました。
現在利用できない箇所や足りない部分を記事で補完しています。

環境

  • Ubuntu 16.04.3 LTS
  • ethereum Geth/v1.7.2

ethereumのインストール

デフォルトのdockerを使う場合は、curlをインストールします。

terminal

apt-get install curl

ethereum公式のWikiに記載されている通りにethereumをインストールします。

terminal

apt-get install software-properties-common
add-apt-repository -y ppa:ethereum/ethereum
apt-get update
apt-get install ethereum

gethコマンドでinstallが成功されたことを確認します。

terminal

geth help

NAME:
   geth - the go-ethereum command line interface

   Copyright 2013-2017 The go-ethereum Authors

USAGE:
   geth [options] command [command options] [arguments...]
   
VERSION:
   1.7.2-stable-1db4ecdc

// something

プライベート・ネットに接続

開発で使うので、プライベート・ネットで動作させます。まずは、ディレクトリを作成します。

terminal

cd /home
mkdir -p test_u/eth_private_net

myGenesis.jsonを作成します。Genesisは、初期データのことです。つまり、myGenesis.jsonは初期データファイルです。

terminal

apt-get install vi

cd /home/test_u/eth_private_net

vi myGenesis.json

{
  "nonce": "0x0000000000000042",
  "timestamp": "0x0",
  "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "extraData": "0x",
  "gasLimit": "0x8000000",
  "difficulty": "0x4000",
  "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
  "coinbase": "0x3333333333333333333333333333333333333333",
  "alloc": {}
}

// https://qiita.com/oggata/items/eea4d5e37f38785f6079

データ内容はまだ触れないでおきます。gasLimitやcoinbaseの意味は、最初はわからないと思います。

myGenesis.jsonファイルで初期化します。

terminal

geth --datadir /home/test_u/eth_private_net2 init /home/test_u/eth_private_net2/myGenesis.json

指定オプションの意味はhelpで確認しましょう。

terminal

--datadir "/root/.ethereum"  Data directory for the databases and keystore
init        Bootstrap and initialize a new genesis block

Genesis Block = 一番初めのブロックのことです。

つまり、上記は、一番初めのブロックを初期化していることを示しています。実際に実行します。

terminal

geth --datadir /home/test_u/eth_private_net2 init /home/test_u/eth_private_net2/myGenesis.json
WARN [10-25|23:04:56] No etherbase set and no accounts found as default 
INFO [10-25|23:04:56] Allocated cache and file handles         database=/home/test_u/eth_private_net/geth/chaindata cache=16 handles=16
Fatal: Failed to write genesis block: genesis has no chain configuration

chaindataディレクトリが新しく作成されて、その中にgenesisブロックのブロックチェーン情報が保存されていることを確認します。

terminal

ls -l geth/chaindata
total 12
-rw-r--r-- 1 root root   0 Oct 25 23:04 000001.log
-rw-r--r-- 1 root root  16 Oct 25 23:04 CURRENT
-rw-r--r-- 1 root root   0 Oct 25 23:04 LOCK
-rw-r--r-- 1 root root 361 Oct 25 23:04 LOG
-rw-r--r-- 1 root root  54 Oct 25 23:04 MANIFEST-000000


初期化に成功しました。

gethの起動

terminal

geth --dev --networkid "10" --datadir "/home/test_u/eth_private_net2" console 2>> /home/test_u/eth_private_net2/geth_err.log

テストネットで起動するときは、--networkid を付加します。--networkidオプションで任意の正の整数のIDを指定することで、ライブ・ネットとは異なるネットワークを立ち上げることが可能です。

また、--dev を付加して開発用途のプライベートチェーン設定で起動しましょう。--devをつけることで素早く採掘ができます。

terminal

Welcome to the Geth JavaScript console!

instance: Geth/v1.7.2-stable-1db4ecdc/linux-amd64/go1.9
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

> 

起動に成功しました。

データの確認

まずは、対話型で動きと仕様を理解していきます。

eth.getBlock(0)で登録データを確認できます。

terminal

> eth.getBlock(0)
{
  difficulty: 17179869184,
  extraData: "0x11bbe8db4e347b4e8c937c1c8370e4b5ed33adb3db69cbdb7a38e1e50b1b82fa",
  gasLimit: 5000,
  gasUsed: 0,
  hash: "0xd4e56740f876aef8c010b86a40d5f56745a118d0906a34e69aec8c0db1cb8fa3",
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  miner: "0x0000000000000000000000000000000000000000",
  mixHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
  nonce: "0x0000000000000042",
  number: 0,
  parentHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
  receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 540,
  stateRoot: "0xd7f8974fb5ac78d9ac099b9ad5018bedc2ce0a72dad1827a1709da30580f0544",
  timestamp: 0,
  totalDifficulty: 17179869184,
  transactions: [],
  transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  uncles: []
}

eth.getBlock(0)は初期データです。

exitコマンドで退出できます。

terminal

> exit
root@04a2878d5ace:/home/test_u/eth_private_net#

採掘

次は採掘してみましょう。ビットコインでいう、マイニングです。

ビットコインは平均10分ごとにブロックを生成しますが、イーサリアムは15秒です。

まず、ノードに登録されたアカウント(EOA)を表示します。

terminal

> eth.accounts
[]

最初は上記のように空です。

EOA(Externally Owned Account)を作成します。
personal.newAccount("passwd")コマンドを使います。

terminal

> personal.newAccount("passwd")
"0xe1a9e49ee272e6ab971b5f6160d8157ff9fe412b"

passwdは、本番は実際のパスワードを入力してください。これでEOA(Externally Owned Account)を作成できます。
実行すると、作成されたEOAの20バイトのアドレスが表示されます。

頭に「0x」が付加されているので16進数のハッシュですね。

もう一つアカウントを作成します。

terminal

> personal.newAccount("passwd2")
"0x0f8669608765e4df02f2a348d11954696ae5698d"

ノードに登録されたアカウント(EOA)を表示します。

terminal

> eth.accounts
["0xe1a9e49ee272e6ab971b5f6160d8157ff9fe412b", "0x0f8669608765e4df02f2a348d11954696ae5698d"]

ノードに登録されたアカウント(EOA)が表示されました。

etherbase

etherbaseとは、各ノードで採掘を行う際にその報酬を紐づけるEOAのアドレスを示します。

terminal

> eth.coinbase
"0xe1a9e49ee272e6ab971b5f6160d8157ff9fe412b"

etherの採掘

作成したEOAのアドレスがetherbaseとしてセットされていれば、etherの採掘が可能です。 etherの採掘はminer.start(thread_num)コマンドで開始します。

terminal

> miner.start()
null

nullが表示されていますが、採掘は実行されています。これは少しわかりにくい仕様ですね。以前はtrueが返される仕様だったのですが、なぜ変更したのでしょうか?

採掘が実行されてるかを確認するには、eth.hashrateコマンドで、現在の採掘処理のハッシュ・レートを確認します。

terminal

> eth.hashrate
2096

> eth.hashrate
66704

ハッシュ・レートがゼロよりも大きければ、採掘処理が行われていると考えます。続ければ続けるほど数値はどんどん上がっていきます。

採掘は時間がかかります。しかし、この記事ではdev環境で動かしているので、次々と採掘されます。

また、ログで確認する方法もあります。ログ出力フォルダの指定は起動時に行っておきましょう。dockerの場合は、terminalでps接続で確認できます。

terminal

docker exec -it {name} /bin/sh

採掘状況の確認

ブロックは次々と採掘されていきます。ブロックチェーンに何番目のブロックまで連なっているのか(=ブロック高)を確認するには、eth.blockNumberコマンドを用います。

terminal

> eth.blockNumber
65
> eth.blockNumber
68
> eth.blockNumber
81
> eth.blockNumber
117

> eth.blockNumber
123

必要な数のブロックを採掘したら、採掘を停止します。

terminal

> miner.stop()
true

採掘したブロックの情報を確認します。

123のブロックを採掘したので、123番目のブロックを確認します。

terminal

> eth.getBlock(123)
{
  difficulty: 139055,
  extraData: "0xd583010702846765746885676f312e39856c696e7578",
  gasLimit: 4712388,
  gasUsed: 0,
  hash: "0x8409ba210cf0a933321c9fc9b1d54dd4b39d98833fa412417ab88c6336603be0",
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  miner: "0x7a05d8030738af4ffe794d8b3d3b86ee28e11318",
  mixHash: "0xb090804669ad4b7922f63f4ed91c05f1dabb29ff609695bd6018de2002a01f05",
  nonce: "0x584cd0329f0dd51d",
  number: 123,
  parentHash: "0x07599d3aeb1dd4f9fb322326e68f89e5bc2cb6a260a82a504ee74929a2f86fab",
  receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 533,
  stateRoot: "0x4eab6ed2c31249285c576d0d79bf629bb811750f31e671f71ff0937a2da29aef",
  timestamp: 1509576985,
  totalDifficulty: 16739304,
  transactions: [],
  transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
  uncles: []
}

まだ採掘されていない124番目のブロックを確認してみます。

terminal

> eth.getBlock(124)
null

報酬の確認

採掘した報酬を確認します。

terminal

> eth.getBalance(eth.accounts[0])
369000000000000000000

eth.getBalance(address)は「wei」の単位で出力します。単位をethに変換してみましょう。

terminal

> web3.fromWei(eth.getBalance(eth.accounts[0]),"ether")
369

送金

送金の仕組みを理解します。
各アカウントの残高を確認します。

terminal

> eth.getBalance(eth.accounts[0])
369000000000000000000
> eth.getBalance(eth.accounts[1])
0

eth.accounts[0]からeth.accounts[1]に2ether送金してみます。
eth.sendTransactionコマンドを用います

送金の際は、採掘処理をバックグラウンドで実行しておく必要があります。また、ロックの解除も必要です。

terminal

// 採掘
miner.start()

// ロック解除 パスワードが必要
personal.unlockAccount(eth.accounts[0])

// 送金の実行。 パスワードは不要
eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(4, "ether")})

// 確認
eth.getBalance(eth.accounts[1])

4000000000000000000

今度は逆にaccounts[1]からaccounts[0]に送金します。

terminal

// 採掘
> miner.start()
null

// ロック解除 パスワードが必要
> personal.unlockAccount(eth.accounts[1])
Passphrase: 
true

// 送金の実行。 パスワードは不要
> eth.sendTransaction({from: eth.accounts[1], to: eth.accounts[0], value: web3.toWei(2, "ether")})
"0xa0f782ee4245e886f9d79cf682006ac479857b8f43bba3d5b3df022f18cecc30"

// 確認
> eth.getBalance(eth.accounts[1])
2000000000000000000

トランザクションを調べます。sendTransaction時に発行されたハッシュを使います。

terminal

> eth.getTransaction("0xa0f782ee4245e886f9d79cf682006ac479857b8f43bba3d5b3df022f18cecc30")
{
  blockHash: "0x2756e0ac42054df3f792b6861f16a9b19392c66b149d6a54dee56eee3ca5eeda",
  blockNumber: 315,
  from: "0xd286d2666015e7a36d5826bd49f83af4ea922f52",
  gas: 90000,
  gasPrice: 0,
  hash: "0xa0f782ee4245e886f9d79cf682006ac479857b8f43bba3d5b3df022f18cecc30",
  input: "0x",
  nonce: 0,
  r: "0xbba0222bb19a7ad54cb5fd0f9512f256285caeaa98ddb396ed1b256b0e8dab54",
  s: "0x2d5329ce25e69ad94709f302c972071cab2a5363bdffacdb19fdb11986ddda71",
  to: "0x7a05d8030738af4ffe794d8b3d3b86ee28e11318",
  transactionIndex: 0,
  v: "0xa95",
  value: 2000000000000000000
}

トランザクションが表示されました。gasPriceが0なので、トランザクションを生成した際に採掘者に対して支払うトランザクション手数料は無料になっていますね。

Contract

やってみたけど、情報が古くてうまくいかないので、penddingします。今はSolcが使えなくなっているので、情報を集める時は注意してください。

まとめ

実際に手を動かすと、ethereumの基本が理解できました。
とはいえ、騒がれているほどの凄さは感じなかったので、これからも少しづつブロックチェーンの学習を進めていきたいと思います。
railsやスマホのように化けて役立ってくれるかもしれないので。。。

以上。

PICK UP オススメ書籍

運営サイト


参考記事

この記事がお役にたちましたらシェアをお願いします

このエントリーをはてなブックマークに追加
Related Posts Plugin for WordPress, Blogger...