LoginSignup
16
21

More than 5 years have passed since last update.

ethereum(private)の環境構築からJSON-RPC実行まで

Posted at

前提

Ethereumのgethを使ったプライベート環境構築記事はほかにもあるので
個人メモ的なもの。JavaでのRPC実行まで。コントラクト実行は確認していない。

環境構築

以下を参考に。
http://book.ethereum-jp.net/first_use/connect_to_private_net.html

ubuntu16LTS ベースとする。

AWSのEC2にubuntuベースでインスタンス作成

microインスタンスのスペックではマイニングができなかった為、m4.largeで構築。(ある程度のCPUとメモリを要求する)

事前準備からインストール

リポジトリを追加して、gethをインストールする。

sudo add-apt-repository -y ppa:ethereum/ethereum
sudo add-apt-repository -y ppa:ethereum/ethereum-dev
sudo apt update
sudo apt upgrade
sudo apt install ethereum
cd /home/ubuntu
mkdir eth_private_net
cd eth_private_net

Genesis.jsonを作る

nano Genesis.json

Genesis.jsonの内容

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

ジェネシスブロック生成

geth --datadir /home/ubuntu/eth_private_net init /home/ubuntu/eth_private_net/Genesis.json
I1116 05:15:12.361001 cmd/geth/main.go:256] successfully wrote genesis block and/or chain rule set: 3b3326d56983eec74bcd3c5757801dcd42e0bf2f169fc0c5d695e28e20f217d7

コンソール起動

geth --identity "sampleNode" --rpc --datadir "/home/ubuntu/eth_private_net" --nodiscover --networkid 10 console 2>> /home/ubuntu/eth_private_net/geth.log

プロンプトが起動する。

アカウント作成

> personal.newAccount()

パスフレーズを指定しなかった場合、入力を2回求められる。

Passphrase:
Repeat passphrase:
"0x02f35a58e4ad954834db125588b9e0c096d41aa7"
> personal.newAccount("パスフレーズ")

でも可能。

送信側、受信側で2つアカウントを作成。

コンソールを一旦停止

> exit

ディレクトリ内のkeystoreとGenesis.json以外削除

Genesis.jsonのalloc部分を、先ほど作成したアカウントに書き換え、balance(残高)を追記

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

再度ジェネシスブロック生成

geth --datadir /home/ubuntu/eth_private_net init /home/ubuntu/eth_private_net/Genesis.json

再度コンソール起動

geth --mine --minerthreads 2 --identity "sampleNode" --rpc --rpcport 8575 --rpcapi "web3,eth,net,personal" --rpccorsdomain "*" --rpcaddr "0.0.0.0" --datadir "/home/ubuntu/eth_private_net" --nodiscover --networkid 10 console 2>> /home/ubuntu/eth_private_net/geth.log

geth起動オプション

コマンド 内容
--mine マイニング実行可能にする
--minerthreads スレッド数 マイニング実行スレッド数
--identity "名前" 名前を付ける
--rpc RPCを有効化
--rpcport ポート番号 RPC受付ポート番号の指定
--rpcapi "機能名" どの機能をRPCで使えるようにするか
--rpccorsdomain "*" クロスドメイン有効化
--rpcaddr "IPアドレス" 指定せずにデフォルトだとローカルアクセスのみ許可されている。外部サーバからRPCアクセスする場合はそのIPを指定。どこからでもRPCアクセス可能にするには"0.0.0.0"を指定。
--datadir "データ保存パス" データをどこに保存するかパスを指定
--nodiscover デフォルトでは自動探索モードでほかのethereumノードを検索しに行く。プライベートブロックチェーンの場合は探しに行かないようにこのオプションを指定。
--networkid 任意の番号 インターネット上のオープンなネットとは異なるネットワークを作る為、任意の数字(0~2以外)、例えば10を指定する。既存ネットワークのidは、0=Olympic, 1=Frontier, 2=Morden
console コンソールを同時に起動する。(attachであとからconsoleの起動も可能)

アドレス確認

> eth.accounts[0]
"0x02f35a58e4ad954834db125588b9e0c096d41aa7"

Genesis.jsonで指定したアドレスの残高確認

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

アカウントロック解除

5分で再度ロックがかかるのでunlockしてから送金。
実際は送金側アカウントのみで可。

> personal.unlockAccount("0x02f35a58e4ad954834db125588b9e0c096d41aa7", "パスフレーズ")
true
> personal.unlockAccount("0x5c17f667c6d9f2278c6a960cfa8fd79f11c09e91", "パスフレーズ")
true

送金トランザクション実行

> eth.sendTransaction({from: "0x02f35a58e4ad954834db125588b9e0c096d41aa7", to: "0x5c17f667c6d9f2278c6a960cfa8fd79f11c09e91", value: web3.toWei(5, "ether")})

fromとtoはeth.accounts[0]などの書き方でも可。

マイニング実行

このままではトランザクションは実行されない。
ここで、マイニングを実行する必要がある。

> eth.pendingTransactions

で、ペンディングされたトランザクションを確認できる。

また、

> eth.

の状態で、タブキーを2回押すと、コマンドの一覧が表示される。
これは「eth.」で始まるコマンドに限らず、「personal.」で始まるコマンドなども同様。

マイニング実行

> miner.start()
true

これでしばらく待つとスペックにもよるがマイニングが開始される。

> eth.hashrate

が0でなければ、マイニングされている。
マイニングで入る収入は最初に作ったアカウントに送られる。

> eth.accounts[0]

で表示されるアカウント。
現在のブロック番号を確認するには、以下のコマンド。

> eth.blockNumber
117

マイニングされたら、再度、待機状態のトランザクションを確認

> eth.pendingTransactions
[]

なければ、送金トランザクションは実行済み。
マイニングを停止する。

> miner.stop()

送信側、受信側の残高確認

> eth.getBalance("0x5c17f667c6d9f2278c6a960cfa8fd79f11c09e91")

これも、eth.accounts[0]などの形式での指定でも可。

ここで、送信側からは、実際の送金額+手数料が引かれている。
この送金手数料が、採掘者の収入になる。

あと、通貨の単位が桁数によって4種類(ether、finney、szabo、wei)あるので、どの単位で扱っているかは注意する。

RPC接続確認

RPCで外部から接続可能かどうか、とりあえずcurlで確認。
JSON-RPC 2.0形式でリクエストを投げられれば、どんな方式でもよい。

curl -X POST http://XX.XXX.XXX.XXX:8575/  --data '{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}'

これで、応答があればOK。
AWSの場合はセキュリティグループでRPCのポートを開けておく。

JSON-RPCで使えるAPI

JSON-RPC経由で使えるAPIは以下のページを参考に。
https://github.com/ethereum/wiki/blob/master/JSON-RPC.md

コマンドから実行できるpersonal.XXXX(アカウント作成など)はRPC経由では実行できない。また。マイニングの実行や停止もできない。
その為、すべてのオペレーションをRPC経由で実行するように作りこむことはできない。
クライアントの言語によらず、JSON-RPC2.0でのアクセスを行うための実装をすれば、RPC経由での処理実行と、結果の取得が可能。

Javaでの実装クラス抜粋

実用レベルに作りこんでない。動かしてみたレベル。
(Error時のJSONを作ってない、返してない、パッケージてきとー、命名てきとー、定数外だしー、etc..)

package com.example.service;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.Map;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.example.requestDto.NoArgsRequestDto;
import com.mashape.unirest.http.HttpResponse;
import com.mashape.unirest.http.JsonNode;
import com.mashape.unirest.http.Unirest;
import com.mashape.unirest.http.exceptions.UnirestException;
import com.thetransactioncompany.jsonrpc2.JSONRPC2Request;
import com.thetransactioncompany.jsonrpc2.JSONRPC2Response;
import com.thetransactioncompany.jsonrpc2.client.JSONRPC2Session;
import com.thetransactioncompany.jsonrpc2.client.JSONRPC2SessionException;

@Service
public class GetAccountsService {

    private static final Logger LOGGER = LoggerFactory
            .getLogger(GetAccountsService.class.getName());
    private static final String HOST = "http://XX.XXX.XXX.XXX:8575/";

    /**
     * jsonrpc2-client版
     * http://software.dzhuvinov.com/json-rpc-2.0-client.html
     * @return
     */
    public String getAccountsJ() {
        URL serverURL = null;
        JSONRPC2Session mySession = null;
        try {
            serverURL = new URL(HOST);
            mySession = new JSONRPC2Session(serverURL);
            mySession.getOptions().setRequestContentType("application/json+rpc");
        } catch (MalformedURLException e) {
            LOGGER.warn(e.getMessage());
            return e.getMessage();
        }

        String method = "eth_accounts";
        String id = "1";
        Map<String, Object> params = null;
        JSONRPC2Request req = new JSONRPC2Request(method, params, id);
        JSONRPC2Response res = null;
        try {
            res = mySession.send(req);
        } catch (JSONRPC2SessionException e) {
            LOGGER.warn(e.getMessage());
            return e.getMessage();
        }
        if (res.indicatesSuccess()){
            return res.getResult().toString();
        }else{
            return res.getError().getMessage();
        }
    }

    /**
     * Unirest版
     * http://unirest.io/java.html
     * @return
     */
    public String getAccounts() {
        HttpResponse<JsonNode> jsonResponse = null;
        NoArgsRequestDto requestDto = new NoArgsRequestDto();
        requestDto.setId(1);
        requestDto.setJsonrpc("2.0");
        requestDto.setMethod("eth_accounts");
        try {
            jsonResponse = Unirest.post(HOST)
                    .header("accept", "application/json")
                    .header("Content-Type", "application/json")
                    .body(requestDto).asJson();
            LOGGER.info(jsonResponse.getBody().toString());
        } catch (UnirestException e) {
            LOGGER.warn(e.getMessage());
            return e.getMessage();
        }
        String result = jsonResponse.getBody().toString();
        return result;
    }
}
package com.example.service;

import java.io.IOException;

import org.springframework.stereotype.Service;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.mashape.unirest.http.ObjectMapper;
import com.mashape.unirest.http.Unirest;

@Service
public class UnirestInitService {
    static  {
        Unirest.setObjectMapper(new ObjectMapper() {
            private com.fasterxml.jackson.databind.ObjectMapper jacksonObjectMapper
                        = new com.fasterxml.jackson.databind.ObjectMapper();

            public <T> T readValue(String value, Class<T> valueType) {
                try {
                    return jacksonObjectMapper.readValue(value, valueType);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

            public String writeValue(Object value) {
                try {
                    return jacksonObjectMapper.writeValueAsString(value);
                } catch (JsonProcessingException e) {
                    throw new RuntimeException(e);
                }
            }
        });
    }
}
package com.example.requestDto;

import java.io.Serializable;

public class BaseRequestDto implements Serializable {

    private String jsonrpc;

    private String method;

    private String[] params;

    private int id;

    /**
     * @return method
     */
    public String getMethod() {
        return method;
    }

    /**
     * @param method セットする method
     */
    public void setMethod(String method) {
        this.method = method;
    }

    /**
     * @return params
     */
    public String[] getParams() {
        return params;
    }

    /**
     * @param params セットする params
     */
    public void setParams(String[] params) {
        this.params = params;
    }

    /**
     * @return id
     */
    public int getId() {
        return id;
    }

    /**
     * @param id セットする id
     */
    public void setId(int id) {
        this.id = id;
    }

    /**
     * @return jsonrpc
     */
    public String getJsonrpc() {
        return jsonrpc;
    }

    /**
     * @param jsonrpc セットする jsonrpc
     */
    public void setJsonrpc(String jsonrpc) {
        this.jsonrpc = jsonrpc;
    }

}
package com.example.requestDto;


public class NoArgsRequestDto extends BaseRequestDto {

}
16
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
16
21