Gethのコンパイル環境構築とコンパイル方法

こんにちは。休みの日はDapp開発の勉強をしている 藤田拓弥@ディズニー好きのSE です。

今日はイーサリアムが提供しているクライアントソフトのGethを触ってみようと思い、公式サイトにアクセスしましたが「Retrieving packages from release server...」のダイアログが表示され続け何もできませんでした。インストール用の実行ファイルもダウンロードできませんでした。(2018年9月30日時点)

f:id:takuyafujita:20180930160257j:plain

ブラウザを変えてみても状況が変わらなかったのでダウンロードは諦めて、Gitに公開されているGethのソースコードを自分でコンパイルする方法を試してみました。

試した環境はwindows10です。

Gethのコンパイル方法は以下のサイトを参考にしました。

Git:Geth公式のインストール手順(Windows

github.com

Qiita:gethをwindows10にインストールする

qiita.com

両方参考にしましたが、内容は同じです。

Gethのコンパイル環境構築

Go言語のインストール

公式サイトからwindows用のMSIファイルをダウンロードしてインストールします。

コマンドプロンプトでバージョンが表示されればOKです。

C:\>go version
go version go1.11 windows/amd64

chocolatey(パッケージ管理ソフト)のインストール

コマンドプロンプトまたはWindows PowerShellのどちらかを使用してインストールします。
インストールに必要な要件等は公式サイトを確認してください。

コマンドプロンプトを使用する場合

管理者として以下のコマンドを実行します。

@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"
Windows PowerShellを使用する場合

管理者として以下のコマンドを実行します。

Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))

コマンドプロンプトでバージョンが表示されればOKです。

C:\>choco --version
0.10.11

chocolateyを使用して必要なソフトをインストール

既にインストール済みの場合は対応不要です。インストール時にスクリプトを実行するか?と聞かれるので「y」を選んで進めます。オプション -y をつけると実行確認が表示されないので楽です。

C:\Windows\system32> choco install git
C:\Windows\system32> choco install golang
C:\Windows\system32> choco install mingw

インストール後にsuccessfullyが表示されていればOKです。

環境変数の設定

C:\Users\xxx> set "GOPATH=%USERPROFILE%"
C:\Users\xxx> set "Path=%USERPROFILE%\bin;%Path%"
C:\Users\xxx> setx GOPATH "%GOPATH%"
C:\Users\xxx> setx Path "%Path%"

Gethのコンパイル手順

githubからGethのソースをclone

C:\Users\xxx> mkdir src\github.com\ethereum
C:\Users\xxx> git clone https://github.com/ethereum/go-ethereum src\github.com\ethereum\go-ethereum

goで各種外部パッケージを取得

C:\Users\xxx> cd src\github.com\ethereum\go-ethereum
C:\Users\xxx> go get -u -v golang.org/x/net/context

goでGethの実行ファイルを作成

C:\Users\xxx\src\github.com\ethereum\go-ethereum> go install -v ./cmd/...

Gethが正常にインストールされたか確認

コマンドプロンプトで「geth help」を実行し、Gethの情報(NAME、USAGE、VERSIONなど)が表示されればOKです。

C:\>geth help

NAME:
   geth - the go-ethereum command line interface

   Copyright 2013-2018 The go-ethereum Authors

USAGE:
   geth [options] command [command options] [arguments...]

VERSION:
   1.8.17-unstable

【CryptoZombiesを触ってみた】Lesson5で学んだこと

【CryptoZombiesとは】

Solidityでスマートコントラクトの構築を学習できるサイト
日本語で丁寧に説明してくれるので、プログラミングやったことない人でも学習できる。

cryptozombies.io

【はじめに】

Solidityは未経験ですが、JavaScriptは触ったことがあります。
そんな人が自分用の学習メモとして書いています。

【学んだこと】

ERC20とERC721

ERC(Ethereum Request for Comments)はイーサリアムトークン規格。ERC20はトークンの送金、残高照会、アドレス毎の残高記録など、トークンを発行するのに必要な機能について定められている。ERC20で作成されたトークン同士は互換性があるので連携しやすい(実装が容易になる)。ERC721は同じトークンでも、それぞれがユニークであると仮定され異なる特徴を持たせることができる。ERC721であれば、同じトークン(ゾンビ)でもレベルが異なれば別物として扱うことができる。

ERCについてはこちらの説明が分かりやすいです。
dappsmarket.net

複数継承

Solidityではカンマ区切りで複数の継承ができる。

contract newContract is Contract1, Contract2 {
}

ERC721の実装

ERC721を継承し各関数をオーバーライドして使用する。

import "./erc721.sol";

// ERC721を継承
contract newContract is ERC721 {

  // balanceOf:addressを受け取り、そのaddressのトークン保有量を返す。
  function balanceOf(address _owner) public view returns (uint256 _balance);

  // ownerOf:トークンIDを受け取り、その所有者のaddressを返す。
  function ownerOf(uint256 _tokenId) public view returns (address _owner);

  // transfer:トークン所有者が送り先(address)、送るトークン(トークンID)を指定し所有権を移転する。
  // 処理の最後にTransferイベントを呼び出す必要がある。
  function transfer(address _to, uint256 _tokenId) public;

  // approve:トークン所有者が送り先(address)、送るトークン(トークンID)を指定し、コントラクトのmappingに記録する。
  // transferと異なり、approveを実行しただけで所有権の移転はしない。takeOwnershipを実行した際に移転する。
  // 処理の最後にApprovalイベントを呼び出す必要がある。
  function approve(address _to, uint256 _tokenId) public;

  // takeOwnership:トークンの受取人が呼び出す。approveで記録したmappingから受取人のアドレスを検索し、該当のトークンの所有権を移転する。
  function takeOwnership(uint256 _tokenId) public;

}

オーバーフローとアンダーフロー

オーバーフローとは四則演算の結果が許容範囲を上回ること。

uint8 number = 255;
// uint8の最大値である255を上回るのでオーバーフローが発生し、結果は0となる。
number++;

アンダーフローとは四則演算の結果が許容範囲を下回ること。

uint8 number = 0;
// uint8の最小値である0を下回るのでアンダーフローが発生し、結果は255となる。
number--;

SafeMath

SafeMathはOpenZeppelinのライブラリ。OpenZeppelinについてはこちらの記事でも紹介しています。

takuyafujita.hatenablog.com

SafeMathはadd(加算)、sub(減算)、mul(乗算)、div(除算)の4つの関数を持ち、オーバーフローやアンダーフローの脆弱性に対応している。SafeMathを使用して四則演算を行えばオーバーフローやアンダーフローが発生する演算を行う前にエラーで返してくれる。基本的に四則演算を行う場合はSafeMathを使用した方が安全。

library SafeMath {

  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    if (a == 0) {
      return 0;
    }
    uint256 c = a * b;
    assert(c / a == b);
    return c;
  }

  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return c;
  }

  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    assert(c >= a);
    return c;
  }
}

SafeMathの使い方

// uint256にSafeMathの関数を追加する(使用できるようにする)。
using SafeMath for uint256;

// aがSafeMathの関数の1つ目の引数として渡される。
uint256 a = 5;
uint256 b = a.add(3); // 5 + 3 = 8
uint256 c = a.mul(2); // 5 * 2 = 10

※uint16やuint32の場合、ライブラリ内でuint256に変換されるので脆弱性に対応出来ていない。この場合、uint16やuint32用のSafeMathライブラリを作成する必要がある。

assert

偽(false)の場合、エラーを発生させる。関数呼び出しが失敗した場合、ユーザーにガスの残りを返却しない。

require

真(true)の場合、エラーを発生させる。関数呼び出しが失敗した場合、ユーザーにガスの残りを返却する。

コメント

JavaScriptに似ている

// 1行コメントの場合

/*
  複数行
  コメント
  の場合
*/

// natspec
/// @title contractのタイトル
/// @author contractの作成者
/// @notice contractが何を行うのか説明
contract Math {
  /// @notice 関数が何を行うのか説明
  /// @param x 1つ目の引数の説明
  /// @param y 2つ目の引数の説明
  /// @return z 戻り値の説明
  /// @dev 開発者向けの詳細な説明
  function multiply(uint x, uint y) returns (uint z) {
    z = x * y;
  }
}

【できたもの】

ゾンビが4体に増えた。ゾンビの名前が変更できるようになった。

f:id:takuyafujita:20180924194242j:plain

【CryptoZombiesを触ってみた】Lesson4で学んだこと

【CryptoZombiesとは】

Solidityでスマートコントラクトの構築を学習できるサイト
日本語で丁寧に説明してくれるので、プログラミングやったことない人でも学習できる。

cryptozombies.io

【はじめに】

Solidityは未経験ですが、JavaScriptは触ったことがあります。
そんな人が自分用の学習メモとして書いています。

【学んだこと】

payable修飾子

ブロックチェーン上にあるコントラクトを呼び出す際にETHを受け取ることができる。

contract OnlineStore {
  function buySomething() external payable {
    // 関数呼び出し時、0.001ETHが送られてきたか確認する
    require(msg.value == 0.001 ether);
    // 0.001ETHが送られてきた場合に実行する処理
    transferThing(msg.sender);
  }
}
// buySomething関数を呼び出す(web3.js)
// valueに0.001ETHを設定
OnlineStore.buySomething({from: web3.eth.defaultAccount, value: web3.utils.toWei(0.001)})

CryptoZombiesより引用

withdraws関数とtransfer関数

コントラクトに送られたETHはコントラクトのアカウントに貯められる。貯まったETHをコントラクト以外で使用するにはコントラクトから引き出す処理(withdraws関数)が必要になる。transfer関数を使用すると指定したイーサリアムのアドレスに送金できる。

contract GetPaid is Ownable {
  function withdraws() external onlyOwner {
    // transfer関数でownerにETHを送る
    // this.balanceでコントラクトに貯まっているETHの残高を取得する
    owner.transfer(this.balance);
  }
}

CryptoZombiesより引用

乱数

ハッシュ関数のkeccak256で乱数を生成する。ただし、この方法で作成した乱数を使用しランダムな結果にしても脆弱性(※)が含まれる。
※自分のノードに向けてトランザクションを発行し、自分の都合の良い結果になるまで繰り返し行うことができる脆弱性。対策としてはoracle(イーサリアムブロックチェーン外の乱数関数にアクセスする)を使う。

uint once = 0;
uint random = uint(keccak256(once));
once++;
uint random = uint(keccak256(once));

else

JavaScriptと同じ

if (age < 30) {
  // 30歳未満
} else {
  // 30歳以上
}

【できたもの】

ゾンビ同士のバトル機能を追加
バトルに勝つとゾンビのレベルが上がる。更に新たなゾンビを生み出し、自分のゾンビ軍団に加わる。

f:id:takuyafujita:20180917041746j:plain
f:id:takuyafujita:20180917041804j:plain

【CryptoZombiesを触ってみた】Lesson3で学んだこと

【CryptoZombiesとは】

Solidityでスマートコントラクトの構築を学習できるサイト
日本語で丁寧に説明してくれるので、プログラミングやったことない人でも学習できる。

cryptozombies.io

【はじめに】

Solidityは未経験ですが、JavaScriptは触ったことがあります。
そんな人が自分用の学習メモとして書いています。

【学んだこと】

イミュータブル(immutable)

コントラクトをイーサリアム上にデプロイするとイミュータブル(※)になる。改竄できないのでセキュリティが高くなる反面、不具合があっても修正できない。修正する場合は別途、修正版のコントラクトを新規で作成する。

※作成後にその状態を変えることのできないオブジェクト

外部依存関係

外部のコントラクトを呼び出す場合は、コントラクトのアドレスをハードコーディングせず関数にする。関数にしておくと呼び出し先のコントラクトで不具合が発生しても後から変更できる。ハードコーディングしているとイミュータブルなので修正できない。

Ownableコントラクト

外部のコントラクトを呼び出す関数の場合、誰でも呼び出しできる状態(External)だとクラッキングされる。そんな時はOwnableを使い、権限を持った人しか呼び出しできないようにする。OpenZeppelinのOwnableコントラクトを使うのが一般的。

OpenZeppelinのOwnableコントラクト

OpenZeppelinはイーサリアム上で安全なスマートコントラクトを実装するためのフレームワーク。そのうちの1つにOwnableコントラクトがあり、コントラクトの実行権限を限定することができる。

詳細は下記にまとめています。

takuyafujita.hatenablog.com

関数修飾子

functionの代わりにmodifierを使う。

/**
  * @dev Throws if called by any account other than the owner.
  */
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }

下記の場合、transferOwnershipの処理が実行される前に関数修飾子のonlyOwnerが実行される。onlyOwnerの[_;]が実行されると呼び出し元のtransferOwnershipの処理が実行される。関数修飾子はrequireで処理前のチェックを行う使い方が一般的。

/**
  * @dev Allows the current owner to transfer control of the contract to a newOwner.
  * @param _newOwner The address to transfer ownership to.
  */
  function transferOwnership(address _newOwner) public onlyOwner {
    _transferOwnership(_newOwner);
  }

関数修飾子に引数を渡す

関数修飾子には関数同様、引数を渡すことが可能です。

mapping (uint => uint) public age;

// ユーザの年齢が一定の年齢を超えていること
modifier olderThan(uint _age, uint _userId) {
  require (age[_userId] >= _age);
  _;
}

// 基準となる年齢(20)とユーザIDを関数修飾子の引数として渡す
function drinking(uint _userId) public olderThan(20, _userId) {
  // 処理
}

ガス(Gas)

Solidityでは関数を実行する度にガス(Gas)と呼ばれる手数料が必要になる。必要なガスの量は関数のロジックで決まり、複雑なロジックになるほど多くのガスが必要になる。関数を実行する度にお金が必要になるのでコードの最適化(ガス代を抑える)が重要になる。

ガスが必要な理由

分散型のイーサリアムはいくつものノードが関数の実行結果が正しいことを検証しているが、無駄にネットワークに負荷をかける処理が多くなると検証時間も遅くなる。手数料を設定することでネットワークの負荷を軽減している。

ガスを節約する方法(struct構造)

structの中に複数のuintを宣言する場合、小さい単位で宣言すると変数がまとめられてストレージが小さくなりガス代が安くなる。下記の場合、NormalよりMinの方がガス代が安い。更に同じデータ型を隣同士にしておく([uint32 a]と[uint32 b])ことでもストレージを抑えることができ、ガス代も安くなる。
※struct以外でuintを宣言する場合は全て256Bitでストレージを確保するのでガス代が安くなる効果はない。

struct Normal {
  uint a;   // 256Bit
  uint b;   // 256Bit
  uint c;   // 256Bit
}

struct Min {
  uint32 a; // 32Bit
  uint32 b; // 32Bit
  uint c;   // 256Bit
}
ガスを節約する方法(View関数)

View関数はブロックチェーンの変更を行わないので外部から呼び出す場合のみガス代がいらない。

ガスを節約する方法(Memory)

Storageのデータを操作するとブロックチェーンに書き込まれ、全てのノードに取り込まれる必要があるのでガス代が高くなる。一時的に必要なデータであればStorageよりもMemoryを使用してガス代を抑える。Memoryの使い方は以下の通りです。

function getMemory() external pure returns(uint) {
  uint memory value = 10;
  return value;
}

時間

now変数は現在のunixタイムスタンプを返す。

seconds、minutes、hours、days、weeks、years

uintの秒数に変換されて使用できる。

1 minutes : 60
1 hours   : 3600 (60分×60秒)
1 days    : 86400(24時間×60分×60秒)
uint lastUpdated;

// lastUpdatedに5分加算する
function addFiveMin() public view returns (uint) {
  return lastUpdated + 5 minutes;
}

For

JavaScriptと同じ

【できたもの】

レベルが上がるとゾンビの名前を変更できる機能を追加したよ。

f:id:takuyafujita:20180902143837j:plain

【TruffleでDapp開発】公式チュートリアル「ETHEREUM PET SHOP」で基礎を学ぶ

【はじめに】

イーサリアムを購入してからブロックチェーンに興味を持ち、休みの日はDapp開発の勉強をしています。技術情報を調べていると【Truffle】というイーサリアム開発のフレームワークに出会いました。Truffleの公式サイトに「ETHEREUM PET SHOP」というチュートリアルがあり、Dapp開発の基礎を学べるので早速触ってみました。チュートリアルと言ってもインストールでエラーが発生したり、環境構築で四苦八苦しましたので、苦労したところをブログに残しておこうと思います。

【参考にしたブログ】

Dapp開発初心者かつ英語も得意ではないので、こじりょー(@kojiryoinvestor)さんのブログを参考に進めました。大変分かり易かったです。ありがとうございます。

Dapps作成手順をイチから学べる!Truffleの「イーサリアム・ペットショップ」

【Truffleのインストール】

nodeとnpmが使える環境で以下のコマンドを実行します。
nodeはv6以上をインストールしておかないと、後々困るので注意してください。私は見落としていて時間を無駄にしてしまいました。。。

npm install -g truffle

【バージョン確認】

以下のコマンドを実行し、Truffleのバージョンが表示すれば正常にインストールされています。

truffle version

正しくインストールされている場合、TruffleとSolidityのバージョンが表示されます。

Truffle v4.1.13 (core: 4.1.13)
Solidity v0.4.24 (solc-js)

私の場合、ここでSyntaxErrorが発生しました。

SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode

解決方法はこちらにまとめています。

【Truffleのプロジェクト作成】

Truffleのプロジェクトを格納するディレクトリを作成し、そのディレクトリに移動します。

mkdir pet_shop
cd pet_shop

下記のコマンドで「ETHEREUM PET SHOP」のプロジェクト一式がダウンロードできます。

truffle unbox pet-shop

成功すると下記の様に「Unbox successful. Sweet!」が表示されます。

Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!

Commands:

  Compile:        truffle compile
  Migrate:        truffle migrate
  Test contracts: truffle test
  Run dev server: npm run dev

私の場合、「Downloading...」で止まり、先に進んでいる様子がありませんでした。
解決方法はこちらにまとめています。

【スマートコントラクトの作成】

contractsのディレクトリ配下に「Adoption.sol」を新規作成します。ファイルの中身は次の様に記載します。

pragma solidity ^0.4.17;

contract Adoption {

  address[16] public adopters;

  // Adopting a pet
  function adopt(uint petId) public returns (uint) {
    require(petId >= 0 && petId <= 15);

    adopters[petId] = msg.sender;

    return petId;
  }

  // Retrieving the adopters
  function getAdopters() public view returns (address[16]) {
    return adopters;
  }

}

コンパイル

truffle compile

build/contractsのディレクトリ配下に「Adoption.json」、「Migrations.json」が作成されていればコンパイルが正常に完了しています。

Windowsの場合、コマンドプロンプトコンパイルすると、以下の様にtruffle.jsのエラーになります。公式にも記載さている通り、私はWindows PowerShellコンパイルしました。

truffle.jsのエラー

エラー:'module'は宣言されていません。
コード:800A1391
ソース:MicrosoftJScript 実行時エラー

マイグレーション

作成したスマートコントラクトをデプロイするためにマイグレーションファイルが必要です。migrationsのディレクトリ配下に「2_deploy_contracts.js」を新規作成します。ファイルの中身は次の様に記載します。

var Adoption = artifacts.require("Adoption");

module.exports = function(deploy) {
  deploy.deploy(Adoption);
}

【Ganacheを起動】

開発用のブロックチェーン(プライベートネットワーク)が必要なので、ローカル環境にイーサリアムブロックチェーンを構築してくれるツール「Ganache」を使用します。Ganacheをダウンロードして起動します。

Ganacheはこちらからダウンロードできます。

起動するとテスト用のアドレスが割り振られます。

f:id:takuyafujita:20180819113840j:plain

【デプロイ】

Ganacheを起動した状態で以下のコマンドを実行します。

truffle migrate

次の様な出力が表示されます。

Using network 'development'.

Running migration: 1_initial_migration.js
  Deploying Migrations...
  ... 0x1d88cf62c4d2c04cd115f578496740c76198299db54e706f751f21b63425b37c
  Migrations: 0x15637172d8cf0057da00a7b55e8bf6843c44163a
Saving successful migration to network...
  ... 0x4a6862964a8c66850373e62d3cd8b364667d437f81f6b00c0b7f24a93c24679e
Saving artifacts...
Running migration: 2_deploy_contracts.js
  Deploying Adoption...
  ... 0x207859a3b740dc07200a2f0068adf25390044725ed4846d0b94efbd69f366104
  Adoption: 0xf979539e570d3d49bd2dc97741ab1df96339f132
Saving successful migration to network...
  ... 0xf3325f15f22d04ed92ca9b06bf802c9df13f5a182532980c508e8dd6d15e8be7
Saving artifacts...

Ganacheを見るとスマートコントラクトをデプロイする際のコストとして、ETHが消費されていることが分かります。

f:id:takuyafujita:20180819114005j:plain

これでプライベートネットワークにスマートコントラクトがデプロイされました。

【スマートコントラクトのテスト】

TruffleはJavaScriptやSolidityでテストコードが作成できます。チュートリアルでは、Solidityで書いています。testのディレクトリ配下に「TestAdoption.sol」を新規作成します。ファイルの中身は次の様に記載します。

pragma solidity ^0.4.17;

import "truffle/Assert.sol";
import "truffle/DeployedAddresses.sol";
import "../contracts/Adoption.sol";

contract TestAdoption {
  Adoption adoption = Adoption(DeployedAddresses.Adoption());

  // Testing the adopt() function
  function testUserCanAdoptPet() public {
    uint returnedId = adoption.adopt(8);

    uint expected = 8;

    Assert.equal(returnedId, expected, "Adoption of pet ID 8 should be recorded.");
  }

  // Testing retrieval of a single pet's owner
  function testGetAdopterAddressByPetId() public {
    // Expected owner is this contract
    address expected = this;

    address adopter = adoption.adopters(8);

    Assert.equal(adopter, expected, "Owner of pet ID 8 should be recorded.");
  }

  // Testing retrieval of all pet owners
  function testGetAdopterAddressByPetIdInArray() public {
    // Expected owner is this contract
    address expected = this;

    // Store adopters in memory rather than contract's storage
    address[16] memory adopters = adoption.getAdopters();

    Assert.equal(adopters[8], expected, "Owner of pet ID 8 should be recorded.");
  }
}

以下のコマンドでテストを実行します。

truffle test

次の様なテスト結果が出力されます。

Using network 'development'.

Compiling .\contracts\Adoption.sol...
Compiling .\test\TestAdoption.sol...
Compiling truffle/Assert.sol...
Compiling truffle/DeployedAddresses.sol...

  TestAdoption
    √ testUserCanAdoptPet (55ms)
    √ testGetAdopterAddressByPetId (51ms)
    √ testGetAdopterAddressByPetIdInArray (73ms)

  3 passing (802ms)

すべてのテストが想定通りの結果になりましたので、テストは完了です。

ユーザインタフェースの作成】

バックエンド側の実装が完了しましたので、続いてフロント側を実装していきます。src/jsのディレクトリ配下にある「app.js」を開きます。必要な関数は全て準備されていますが中身がコメントになっています。コメント部分をチュートリアルに記載されているコードに置き換えていきます。置き換えるのは、initWeb3、initContract、markAdopted、handleAdoptの4箇所です。

以下、完成した「app.js」のコードになります。

App = {
  web3Provider: null,
  contracts: {},

  init: function() {
    // Load pets.
    $.getJSON('../pets.json', function(data) {
      var petsRow = $('#petsRow');
      var petTemplate = $('#petTemplate');

      for (i = 0; i < data.length; i ++) {
        petTemplate.find('.panel-title').text(data[i].name);
        petTemplate.find('img').attr('src', data[i].picture);
        petTemplate.find('.pet-breed').text(data[i].breed);
        petTemplate.find('.pet-age').text(data[i].age);
        petTemplate.find('.pet-location').text(data[i].location);
        petTemplate.find('.btn-adopt').attr('data-id', data[i].id);

        petsRow.append(petTemplate.html());
      }
    });

    return App.initWeb3();
  },

  initWeb3: function() {
    // Is there an injected web3 instance?
    if (typeof web3 !== 'undefined') {
      App.web3Provider = web3.currentProvider;
    } else {
      // If no injected web3 instance is detected, fall back to Ganache
      App.web3Provider = new Web3.providers.HttpProvider('http://localhost:7545');
    }
    web3 = new Web3(App.web3Provider);

    return App.initContract();
  },

  initContract: function() {
    $.getJSON('Adoption.json', function(data) {
      // Get the necessary contract artifact file and instantiate it with truffle-contract
      var AdoptionArtifact = data;
      App.contracts.Adoption = TruffleContract(AdoptionArtifact);

      // Set the provider for our contract
      App.contracts.Adoption.setProvider(App.web3Provider);

      // Use our contract to retrieve and mark the adopted pets
      return App.markAdopted();
    });

    return App.bindEvents();
  },

  bindEvents: function() {
    $(document).on('click', '.btn-adopt', App.handleAdopt);
  },

  markAdopted: function(adopters, account) {
    var adoptionInstance;

    App.contracts.Adoption.deployed().then(function(instance) {
      adoptionInstance = instance;

      return adoptionInstance.getAdopters.call();
    }).then(function(adopters) {
      for (i = 0; i < adopters.length; i++) {
        if (adopters[i] !== '0x0000000000000000000000000000000000000000') {
          $('.panel-pet').eq(i).find('button').text('Success').attr('disabled', true);
        }
      }
    }).catch(function(err) {
      console.log(err.message);
    });
  },

  handleAdopt: function(event) {
    event.preventDefault();

    var petId = parseInt($(event.target).data('id'));

    var adoptionInstance;

    web3.eth.getAccounts(function(error, accounts) {
      if (error) {
        console.log(error);
      }

      var account = accounts[0];

      App.contracts.Adoption.deployed().then(function(instance) {
        adoptionInstance = instance;

        // Execute adopt as a transaction by sending account
        return adoptionInstance.adopt(petId, {from: account});
      }).then(function(result) {
        return App.markAdopted();
      }).catch(function(err) {
        console.log(err.message);
      });
    });
  }

};

$(function() {
  $(window).load(function() {
    App.init();
  });
});

【MetaMaskのインストールと設定】

ETHEREUM PET SHOPの実装は全て完了しましたが、取引する為にはMetaMaskが必要になります。

チュートリアルではGanacheに表示されるMnemonicを使用してMetaMaskのアカウントを作成していますが、メインネットでこのアカウントにETHを送信すると全て失います。

f:id:takuyafujita:20180819114029j:plain

私はMetaMaskのアカウントを別で作成していましたので、こじりょーさんのブログ(MetaMaskをプライベートネットに接続しようの箇所)を参考にして、Ganacheに表示されているアドレスをMetaMaskに取り込みました。この辺りは言葉の意味を理解できていないとセルフゴックス(操作ミスで仮想通貨を失う)の可能性があります。誰も責任を取ってくれませんので自己責任でお願いします。私の拙い文章では誤解される可能性がありますので詳細を省きます。

【ETHEREUM PET SHOPを表示】

以下のコマンドを実行すると、ETHEREUM PET SHOPがブラウザに表示されます。チュートリアルなので、lite-server(ローカルのWEBサーバ)の起動、Webサイトはsrcディレクトリ配下のファイルを使用、スマートコントラクトはbuild/contractsディレクトリ配下のファイルを使用するように設定済みでした。

npm run dev

遂にETHEREUM PET SHOPを表示することができました!

f:id:takuyafujita:20180819114044j:plain

早速、ペットを購入してみます。Adoptボタンをクリック!

f:id:takuyafujita:20180819114342j:plain

MetaMaskでETHを支払うと・・・

f:id:takuyafujita:20180819114350j:plain

Successに変わりました。正常に購入できました!

【まとめ】

環境構築に時間を費やしてしまいましたが、その後は止まることなく進めることができました。このチュートリアルを行うことで、TruffleやGanacheを使用してプライベートネットワークにデプロイする方法とプロジェクトの構成(フロント、バックエンド、デプロイに必要な設定など)が理解できました。新しい技術を勉強するのは楽しいですね。もっと時間が欲しい。

【TruffleでDapp開発】「truffle unbox pet-shop」を実行すると「Downloading...」で止まる

【はじめに】

イーサリアムを購入してからブロックチェーンに興味を持ち、休みの日はDapp開発の勉強をしています。技術情報を調べていると【Truffle】というイーサリアム開発のフレームワークに出会いました。早速触ってみましたが、インストールでエラーが発生したり環境構築で四苦八苦しています。

【ETHEREUM PET SHOP】

先回、何とかTruffleのインストールが完了したので、公式サイトにあるチュートリアルETHEREUM PET SHOP」をダウンロードしてみました。

【ダウンロード】

Truffleのインストールが完了していると、下記のコマンドで「ETHEREUM PET SHOP」のプロジェクト一式がダウンロードできます。

truffle unbox pet-shop

成功すると下記の様に「Unbox successful. Sweet!」が表示されます。

Downloading...
Unpacking...
Setting up...
Unbox successful. Sweet!

Commands:

  Compile:        truffle compile
  Migrate:        truffle migrate
  Test contracts: truffle test
  Run dev server: npm run dev

私の場合は「Downloading...」で止まり、先に進んでいる様子がありません。。。

【原因】

セキュリティソフトが原因でした。Webのトラフィックを監視する機能を無効にして再度、コマンドを実行すると「Unbox successful. Sweet!」が表示されました。気付いたら簡単な事ですが、なかなか気付けず2~3時間悩んでしまいました。まだまだ先が長い。

【TruffleでDapp開発】Truffleのコマンド「truffle version」でSyntaxError

【はじめに】

イーサリアムを購入してからブロックチェーンに興味を持ち、休みの日はDapp開発の勉強をしています。技術情報を調べていると【Truffle】というイーサリアム開発のフレームワークに出会いました。早速触ってみましたが、インストール時点でエラーが発生しましたので原因と対応方法をまとめておきます。

【Truffleのインストール】

nodeとnpmが使える環境で以下のコマンドを実行します。

npm install -g truffle

【バージョン確認】

以下のコマンドを実行し、Truffleのバージョンが表示すれば正常にインストールされています。

truffle version

正しくインストールされている場合、TruffleとSolidityのバージョンが表示されます。

Truffle v4.1.13 (core: 4.1.13)
Solidity v0.4.24 (solc-js)

私の場合、ここでSyntaxErrorが発生しました。

SyntaxError: Block-scoped declarations (let, const, function, class) not yet supported outside strict mode

【原因と対応方法】

原因と対応方法については、この記事が参考になります。

trueman-developer.blogspot.com

以下のいずれかの方法で対応可能であることが分かりましたので、nodeのバージョンをあげることにしました。Truffleのインストールなのでnodeのバージョンアップしか選択肢がありません。

  1. "use strict"(厳格モード)を使用する
  2. 実行時に起動オプション(--use_strict)をつける
  3. nodeのバージョンをあげる(6.x 以上)

【nodeのバージョンアップ】

上げる前のバージョン:v5.10.1
上げた後のバージョン:v10.8.0

nodeのバージョンは以下のコマンドで確認できます。

node -v

今回はnodeのバージョン管理ツール「nodist」を使いました。以下の記事が参考になります。

webbibouroku.com

手順1

github.com

上記サイトからインストーラーをダウンロードして、nodistのインストールを行います。インストール後は端末を再起動します。

手順2

再起動後、以下のコマンドでnodistのバージョンを確認します。

nodist -v

nodistのバージョンが表示すればインストール完了です。

0.8.8

手順3

nodeのバージョンを指定してインストールを行います。以下のコマンドでインストールできるバージョンが確認できます。

nodist dist

手順4

バージョンを指定してインストールを行います。今回はv10.8.0にしました。

nodist + 10.8.0

手順5

使用するnodeのバージョンに切り替えを行います。nodistはnodeのバージョン管理ツールなので簡単に切り替えができます。

nodist 10.8.0

手順6

正しく切り替わっているか確認します。以下のコマンドでnodeのバージョンを確認します。

node -v

変わっていない。。。

v5.10.1

nodist経由以外でnodeをインストールしている場合、アンインストールする必要がありました。ということで、v5.10.1のnodeをアンインストールします。windowsの設定>アプリと機能からnodeをアンインストールしました。

再度、nodeのバージョンを確認します。

node -v

バージョンがv10.8.0になりました!

v10.8.0

【Truffleのバージョン確認】

Truffleのバージョンを確認します。

truffle version

おぉ、SyntaxErrorではなくTruffleとSolidityのバージョンが表示されました。nodeのバージョンが古すぎたのが原因でしたね。これでTruffleのインストールは完了です。

Truffle v4.1.13 (core: 4.1.13)
Solidity v0.4.24 (solc-js)