プライベートネットワークでgethのlightモードを使ってみる

# 概要
下記構成としたい。

- ノードA - geth のfullノード
- ノードB - geth のlightノード

fullノードであるノードA でのみマイニングを行う。また、ETHを送金し、そのトランザクションがブロックに取り込まれ、ノードAとノードBで同期されていることを確認してみる。また、gasprice が 0 でも送金できるかを合わせて確認してみる。
ちなみに、lihgtノードからのスマートコントラクトのデプロイは、できないようである。

# 環境
通常はノードAとノードBを別のサーバへインストールするが、今回は同じ１台のサーバへインストールする。

- Ubuntu 14.04
- geth 1.8.21-stable

# 手順
### サーバA 初期化と起動
サーバAの初期化を行う（myGenesis.jsonの中身については今回は触れない）。
--syncmode "full" と明示的に指定することでフルノードとして起動している（デフォルトはfull）。
サーバAはサーバBからLESリクエストというものを受け取ることになるので、 --lightserv 25 と指定することで、全処理の処理時間のうち、LESリクエストを処理する時間を最大で25%割り当てるよう指定する。デフォルトは0%であり、--lightserv を指定した時に限り、lightノードからの処理を受け付けることができる。
--gasprice 0 とすることで、gasprice が 0 のトランザクションもマイニング対象とする。

```
$ mkdir ~/data
$ geth --datadir ~/data init ~/myGenesis.json
$ geth --syncmode "full" --lightserv 25 --datadir ~/data  --networkid 15 --nodiscover --port 30000 --nat=none --gasprice 0
```

# サーバA アカウントの作成とenodeのURLを確認
アカウントを作成した後、サーバAのenodeのURLを確認する。enodeのURLは、サーバBからサーバAへ接続するために、後で必要になる。

```
$ geth attach ipc:data/geth.ipc
> personal.newAccount()
Passphrase:
Repeat passphrase:
"0xd37fe3e5f7c266f0b09fb3f7572f9d2cd5526ed8"
> admin.nodeInfo.enode
"enode://8854537dfa6072be08e000246e0c27d3fb6b4838f62009f38bdb1e91ebe77d5b6e1f9ccae6ccb799b1a1d9c3c2e395fba99890d9f352fbddd103ff6032c4810d@127.0.0.1:30000?discport=0"
>
```

# サーバB 初期化と起動
サーバBの初期化を行う。サーバAと同期するにあたり、サーバAと同じGenesisブロックを作成する必要があるため、サーバAと同じ myGenesis.json で初期化する。
lightノードとしたいので、--syncmode=light とする。
--networkid 15 は、サーバAと同じ15とすることで、サーバAに接続できる。
今回は同一サーバにノード２台であり、サーバAと異なるポートとする必要があるので、--port 30001 とする。
--gasprice 0 はサーバAでは指定しているが、サーバBでは指定する必要は無い（マイニングするのはサーバAだから）。

```
$ mkdir ~/data_light
$ geth --datadir ~/data_light init ~/myGenesis.json
$ geth --syncmode=light --datadir ~/data_light  --nodiscover --networkid 15 --port 30001 --nat=none
```

# サーバB アカウントの作成とサーバAへの接続
アカウントを作成する。
admin.peers と net.peerCount で、まだ接続がないことを確認する。
admin.addPeer で サーバAのenodeのURLを指定することで、サーバAへの接続が始まる。２秒ぐらい待つと接続されるので、admin.peers と net.peerCount で接続されていることを確認する。

```
$ geth attach ipc:data_light/geth.ipc
> personal.newAccount()
Passphrase:
Repeat passphrase:
"0xfb79bee53bae8d63aa5582c54ba2e64ed29578f9"
> admin.peers
[]
> net.peerCount
0
> admin.addPeer("enode://8854537dfa6072be08e000246e0c27d3fb6b4838f62009f38bdb1e91ebe77d5b6e1f9ccae6ccb799b1a1d9c3c2e395fba99890d9f352fbddd103ff6032c4810d@127.0.0.1:30000?discport=0")
> admin.peers
[{
    caps: ["eth/62", "eth/63", "les/1", "les/2"],
    enode: "enode://8854537dfa6072be08e000246e0c27d3fb6b4838f62009f38bdb1e91ebe77d5b6e1f9ccae6ccb799b1a1d9c3c2e395fba99890d9f352fbddd103ff6032c4810d@127.0.0.1:30000?discport=0",
    id: "81b6577aea48d6aad8fe355e3cfcfce561b84c29eccf6bddc9b43318cd17a52e",
    name: "Geth/v1.8.21-stable-9dc5d1a9/linux-amd64/go1.10.4",
    network: {
      inbound: false,
      localAddress: "127.0.0.1:38726",
      remoteAddress: "127.0.0.1:30000",
      static: true,
      trusted: false
    },
    protocols: {
      les: {
        difficulty: 0,
        head: "17d675e5c1b90e217d0a59cc1425a99ca7970d89ebdff2e09f2333a67b050a65",
        version: 2
      }
    }
}]
> net.peerCount
1
>
```

# サーバA サーバBからの接続の確認
サーバBから接続されていることをサーバA上で確認する。

```
> admin.peers
[{
    caps: ["les/1", "les/2"],
    enode: "enode://1daeef7fbb80905f6c815f135465acea173059716011a510a83f0cc26b9a4a38ea0db35b562d36bf5cb0937d63a85bd48bdbe3431da054013aa994405c1d047b@127.0.0.1:38726",
    id: "09bef018bbadf5ede19a47e8a1efd1088effa6441ed9c7e0ecf792ef02d65fce",
    name: "Geth/v1.8.21-stable-9dc5d1a9/linux-amd64/go1.10.4",
    network: {
      inbound: true,
      localAddress: "127.0.0.1:30000",
      remoteAddress: "127.0.0.1:38726",
      static: false,
      trusted: false
    },
    protocols: {
      les: {
        difficulty: 0,
        head: "17d675e5c1b90e217d0a59cc1425a99ca7970d89ebdff2e09f2333a67b050a65",
        version: 2
      }
    }
}]
> net.peerCount
1
```

# サーバA 送金
ここまでで、既に下記EOAは作成済みである。
- サーバA eth.accounts[0] "0xd37fe3e5f7c266f0b09fb3f7572f9d2cd5526ed8"
- サーバB eth.accounts[0] "0xfb79bee53bae8d63aa5582c54ba2e64ed29578f9"

マイニングを開始している。eth.blockNumber で実際にマイニングが開始されたことを確認する。サーバAの eth.accounts[0] から サーバBの eth.accounts[0] へ送金するトランザクションを送る。ログを眺めているとブロックが作成されていることに気づくだろう。その後、本当に送金されたのかどうか、送金先の残高を確認する（送金前は残高0）。ここまでは、サーバA上でのトランザクションの確認である。サーバBに同期されているか、次で確認したい。

```
> eth.blockNumber
0
> miner.start()
null
(しばらく待つ)
> eth.blockNumber
14
> personal.unlockAccount(eth.accounts[0], "password0", 60)
true
> eth.sendTransaction({from: eth.accounts[0], to: "0xfb79bee53bae8d63aa5582c54ba2e64ed29578f9", value: 1000000})
"0xaa2d5d72f9ecebfda0530bcfa790dd0e3b6633115c7fbedbe881df7220db63b6"
> eth.getBalance("0xfb79bee53bae8d63aa5582c54ba2e64ed29578f9")
1000000
```

# サーバB 残高の確認
サーバB上で送金先の残高の確認してみる。確かに同期されている。

```
> eth.getBalance(eth.accounts[0])
1000000
```

# サーバB 送金
サーバB上でもう１つアカウントを作成し、eth.accounts[0] から eth.accounts[1] へ送金してみる。

```
> personal.newAccount()
Passphrase:
Repeat passphrase:
"0x006abf1e1245475b2b479a04fa5a9079ad52f269"

> personal.unlockAccount(eth.accounts[0], "password0", 60)
true
> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: 100, gasPrice: 0})
"0x65c6cc9a80e655a642f2ed0deb17e4cbd9ebad6436a4dc5647edda39d94f562d"
// ブロックが作られるまで数秒待つ
> eth.getBalance(eth.accounts[1])
100
```

# おまけ
eth.sendTransaction の gas"P"rice と gas"p"rice とで型が違うようだ。
eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: 100, gasprice: 0}) とすると Error: invalid argument 0: json: cannot unmarshal non-string into Go struct field SendTxArgs.gasPrice of type *hexutil.Big という具合でエラーになってしまう。
gasprice: "0x0" または gasPrice: 0 とすれば、エラーにはならない。

# 参考
[How to run a “server” for light clients?](https://ethereum.stackexchange.com/questions/11014/how-to-run-a-server-for-light-clients)
[Can't run truffle migrate with --light --testnet](https://github.com/ethereum/go-ethereum/issues/15366)