GethとDocker Composeの複数コンテナでプライベットネットワークを構築する
docker-composeでGethのノードを複数起動しプライベートネットワークを構築します。
1コンテナ1ノードで、コンテナ同士で接続するイメージです。
送金できるようにするところまでを目指します。
今回は4コンテナを用意します。
version: "3"
services:
app1:
image: ethereum/client-go
entrypoint: /bin/sh
tty: true
volumes:
- ./private_net:/geth
app2:
image: ethereum/client-go
entrypoint: "/bin/sh"
tty: true
volumes:
- ./private_net2:/geth
app3:
image: ethereum/client-go
entrypoint: "/bin/sh"
tty: true
volumes:
- ./private_net3:/geth
app4:
image: ethereum/client-go
entrypoint: "/bin/sh"
tty: true
volumes:
- ./private_net4:/geth
使うイメージはこちら。
ディレクトリ構成もあったほうがいいかな。tree表示。
app1コンテナでGethを起動させる
まずはapp1
のコンテナでGethを起動させます。
docker-compose up
docker-compose exec app1 /bin/sh
ジェネシスブロックの生成
Genesisブロックを作成します。
private_net/genesis.json
を作成します。
{
"config": {
"chainId": 22,
"homesteadBlock": 0,
"eip150Block": 0,
"eip155Block": 0,
"eip158Block": 0,
"byzantiumBlock": 0,
"constantinopleBlock": 0,
"petersburgBlock": 0,
"istanbulBlock": 0,
"berlinBlock": 0,
"londonBlock": 0
},
"alloc": {},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x20000",
"extraData": "",
"gasLimit": "0x2fefd8",
"nonce": "0x00000000000000059",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00"
}
を参考に作成しました。
chainIdは適当に22。
nonceも適当な値にしています。
初期化を実行します。
/geth # geth --datadir /geth/ init /geth/genesis.json
INFO [07-27|12:10:03.031] Maximum peer count ETH=50 LES=0 total=50
INFO [07-27|12:10:03.032] Smartcard socket not found, disabling err="stat /run/pcscd/pcscd.comm: no such file or directory"
INFO [07-27|12:10:03.049] Set global gas cap cap=50,000,000
INFO [07-27|12:10:03.052] Allocated cache and file handles database=/geth/geth/chaindata cache=16.00MiB handles=16
INFO [07-27|12:10:03.131] Opened ancient database database=/geth/geth/chaindata/ancient readonly=false
INFO [07-27|12:10:03.131] Writing custom genesis block
INFO [07-27|12:10:03.131] Persisted trie from memory database nodes=0 size=0.00B time="3.995µs" gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [07-27|12:10:03.147] Successfully wrote genesis state database=chaindata hash=119a56..09b937
INFO [07-27|12:10:03.148] Allocated cache and file handles database=/geth/geth/lightchaindata cache=16.00MiB handles=16
INFO [07-27|12:10:03.226] Opened ancient database database=/geth/geth/lightchaindata/ancient readonly=false
INFO [07-27|12:10:03.226] Writing custom genesis block
INFO [07-27|12:10:03.226] Persisted trie from memory database nodes=0 size=0.00B time="3.262µs" gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [07-27|12:10:03.238] Successfully wrote genesis state database=lightchaindata hash=119a56..09b937
これでジェネシスブロックが生成されました。
Gethを起動します。
/geth # geth --networkid "22" --nodiscover --datadir /geth console 2>> /geth/info.log
Welcome to the Geth JavaScript console!
instance: Geth/v1.10.21-unstable-14b0eeda-20220727/linux-amd64/go1.18.4
at block: 0 (Thu Jan 01 1970 00:00:00 GMT+0000 (UTC))
datadir: /geth
modules: admin:1.0 debug:1.0 engine:1.0 eth:1.0 ethash:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0
To exit, press ctrl-d or type exit
以下で1つ目のブロックが生成されていることを確認できます。
> eth.getBlock(0)
{
baseFeePerGas: 1000000000,
difficulty: 131072,
extraData: "0x",
gasLimit: 3141592,
gasUsed: 0,
hash: "0x119a5666fc2fc8879b3d6da3ac58335c6f78cfd5be0ae85909acfc98ed09b937",
logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
miner: "0x0000000000000000000000000000000000000000",
mixHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
nonce: "0x0000000000000031",
number: 0,
parentHash: "0x0000000000000000000000000000000000000000000000000000000000000000",
receiptsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
size: 512,
stateRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
timestamp: 0,
totalDifficulty: 131072,
transactions: [],
transactionsRoot: "0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421",
uncles: []
}
app2コンテナでGethを起動させてapp1のGethと接続する
ジェネシスブロック生成
残りの3コンテナに対してもジェネシスブロックを生成しておく必要があります。
初期化してないとpeerできないようなので。
また、1つ目と同じgenesis.json
を使う必要があります。
private_net/genesis.json
をprivate_net/2genesis.json
, private_net3/genesis.json
, private_net4/genesis.json
にコピーしておきます。
app2, app3, app4の各コンテナ内でGethの初期化コマンドを実行し、ジェネシスブロックを生成しておきます。
docker-compose exec app2 /bin/sh
/geth # geth --datadir /geth/ init /geth/genesis.json
bootnodeを使ってGethを起動
Geth実行中のapp1コンテナ
で接続情報を取得します。
# app1コンテナ
> admin.nodeInfo
{
enode: "enode://73fede78d501324558c617209c50b8a5bc8621fee7e2649c69d149529307e952e8c1f5d9f19f06214eccc8a51919231ffb6ad329e2059c7e61f99d2ea60048a0@127.0.0.1:30303?discport=0",
enr: "enr:-Jy4QCCMqPvwcXdXU4nYgETymfMy4U7w7X1RuSGs5hV_-oWMEAWOrQQoiKVQ3edJs9WWNRydPd3TDBSqhOTfenOYNkuGAYI_liUog2V0aMfGhL7GGDCAgmlkgnY0gmlwhH8AAAGJc2VjcDI1NmsxoQJz_t541QEyRVjGFyCcULilvIYh_ufiZJxp0UlSkwfpUoRzbmFwwIN0Y3CCdl8",
id: "b898c20aa7c1283c0c16088875226e89697e6ebcb2189d64c5de10c2f5dc86e7",
ip: "127.0.0.1",
listenAddr: "[::]:30303",
name: "Geth/v1.10.21-unstable-14b0eeda-20220727/linux-amd64/go1.18.4",
ports: {
discovery: 0,
listener: 30303
},
protocols: {
eth: {
config: {
berlinBlock: 0,
byzantiumBlock: 0,
chainId: 22,
constantinopleBlock: 0,
eip150Block: 0,
eip150Hash: "0x0000000000000000000000000000000000000000000000000000000000000000",
eip155Block: 0,
eip158Block: 0,
homesteadBlock: 0,
istanbulBlock: 0,
londonBlock: 0,
petersburgBlock: 0
},
difficulty: 131072,
genesis: "0x119a5666fc2fc8879b3d6da3ac58335c6f78cfd5be0ae85909acfc98ed09b937",
head: "0x119a5666fc2fc8879b3d6da3ac58335c6f78cfd5be0ae85909acfc98ed09b937",
network: 22
},
snap: {}
}
}
上記の
enode: "enode://73fede78d501324558c617209c50b8a5bc8621fee7e2649c69d149529307e952e8c1f5d9f19f06214eccc8a51919231ffb6ad329e2059c7e61f99d2ea60048a0@127.0.0.1:30303?discport=0"
を使用します。
ただ、今回はコンテナ同士の接続ということで、
127.0.0.1:30303?discport=0
の部分を
app1:30303
とします。
(30303ポートはGeth起動時のデフォルトポート)
まずはapp2
コンテナでGethを起動させてみます。
/ # geth --networkid "22" --datadir /geth --bootnodes "enode://73fede78d501324558c617209c50b8a5bc8621fee7e2649c69d149529307e952e8c1f5d9f19f06214eccc8a51919231ffb6ad329e2059c7e61f99d2ea60048a0@app1:30303" console 2>> /geth/info.log
app1
のGethとapp2
のGethが接続されているか確認します。
app2
コンテナでadmin.peers
を実行。
# app2コンテナ
> admin.peers
[{
caps: ["eth/66", "eth/67", "snap/1"],
enode: "enode://73fede78d501324558c617209c50b8a5bc8621fee7e2649c69d149529307e952e8c1f5d9f19f06214eccc8a51919231ffb6ad329e2059c7e61f99d2ea60048a0@172.22.0.4:30303",
id: "b898c20aa7c1283c0c16088875226e89697e6ebcb2189d64c5de10c2f5dc86e7",
name: "Geth/v1.10.21-unstable-14b0eeda-20220727/linux-amd64/go1.18.4",
network: {
inbound: false,
localAddress: "172.22.0.3:56428",
remoteAddress: "172.22.0.4:30303",
static: false,
trusted: false
},
protocols: {
eth: {
difficulty: 131072,
head: "0x119a5666fc2fc8879b3d6da3ac58335c6f78cfd5be0ae85909acfc98ed09b937",
version: 67
},
snap: {
version: 1
}
}
}]
enode://73fede78d501324558c617209c50b8a5bc8621fee7e2649c69d149529307e952e8c1f5d9f19f06214eccc8a51919231ffb6ad329e2059c7e61f99d2ea60048a0
を見ると、app1
のadmin.nodeInfo.enode
と一致しています。
また、app1
コンテナでadmin.peers
を実行すると
# app1コンテナ
> admin.peers
[{
caps: ["eth/66", "eth/67", "snap/1"],
enode: "enode://24fc738ee506109b85a4072b3a22b9d41e65be47a4d8d771bc6532cdcde929ff67d5e898a27acb23f43e791602c7301b9e30ff366a780f18e7ffe5539ce73883@172.22.0.3:56428",
id: "597444e34fd9a17d0dca5f23e0e8e6017af3518112cb5d30f05bf112136f7ba9",
name: "Geth/v1.10.21-unstable-14b0eeda-20220727/linux-amd64/go1.18.4",
network: {
inbound: true,
localAddress: "172.22.0.4:30303",
remoteAddress: "172.22.0.3:56428",
static: false,
trusted: false
},
protocols: {
eth: {
difficulty: 131072,
head: "0x119a5666fc2fc8879b3d6da3ac58335c6f78cfd5be0ae85909acfc98ed09b937",
version: 67
},
snap: {
version: 1
}
}
}]
app2
コンテナでadmin.nodeInfo.enode
を実行すると
> admin.nodeInfo.enode
"enode://24fc738ee506109b85a4072b3a22b9d41e65be47a4d8d771bc6532cdcde929ff67d5e898a27acb23f43e791602c7301b9e30ff366a780f18e7ffe5539ce73883@127.0.0.1:30303"
enodeの@以前が一致しています。
app2とapp3を接続する
app1
とapp2
がつながったので、次はapp2
とapp3
をつなげてみます。
app3
でも同様にジェネシスブロックを生成した後、bootnodes。
geth --networkid "22" --datadir /geth --bootnodes "enode://24fc738ee506109b85a4072b3a22b9d41e65be47a4d8d771bc6532cdcde929ff67d5e898a27acb23f43e791602c7301b9e30ff366a780f18e7ffe5539ce73883@app2:30303" console 2>> /geth/info.log
app3
でadmin.peers
を確認してみます。
# app3コンテナ
> admin.peers
[{
caps: ["eth/66", "eth/67", "snap/1"],
enode: "enode://24fc738ee506109b85a4072b3a22b9d41e65be47a4d8d771bc6532cdcde929ff67d5e898a27acb23f43e791602c7301b9e30ff366a780f18e7ffe5539ce73883@172.22.0.3:30303",
id: "597444e34fd9a17d0dca5f23e0e8e6017af3518112cb5d30f05bf112136f7ba9",
name: "Geth/v1.10.21-unstable-14b0eeda-20220727/linux-amd64/go1.18.4",
network: {
inbound: false,
localAddress: "172.22.0.5:50870",
remoteAddress: "172.22.0.3:30303",
static: false,
trusted: false
},
protocols: {
eth: {
difficulty: 131072,
head: "0x119a5666fc2fc8879b3d6da3ac58335c6f78cfd5be0ae85909acfc98ed09b937",
version: 67
},
snap: {
version: 1
}
}
}]
app2
コンテナのenode
が表示されています。
app3
コンテナのenode
を確認しておくと、
> admin.nodeInfo.enode
"enode://b65ffdace6debb20ffad982672b098c1d26144c122f7504c6df805dae1da70a346226a2df0506efa282ec41479316b618150a491be83537ef017d3c35606e95a@127.0.0.1:30303"
です。
app2
コンテナのadmin.peers
を確認してみます。
# app2コンテナ
> admin.peers
[{
caps: ["eth/66", "eth/67", "snap/1"],
enode: "enode://b65ffdace6debb20ffad982672b098c1d26144c122f7504c6df805dae1da70a346226a2df0506efa282ec41479316b618150a491be83537ef017d3c35606e95a@172.22.0.5:50870",
id: "7f4154f1463bb921cfaa6b995bc9a7ca42244a8487e163111713e5eabc6222f6",
name: "Geth/v1.10.21-unstable-14b0eeda-20220727/linux-amd64/go1.18.4",
network: {
inbound: true,
localAddress: "172.22.0.3:30303",
remoteAddress: "172.22.0.5:50870",
static: false,
trusted: false
},
protocols: {
eth: {
difficulty: 131072,
head: "0x119a5666fc2fc8879b3d6da3ac58335c6f78cfd5be0ae85909acfc98ed09b937",
version: 67
},
snap: {
version: 1
}
}
}, {
caps: ["eth/66", "eth/67", "snap/1"],
enode: "enode://73fede78d501324558c617209c50b8a5bc8621fee7e2649c69d149529307e952e8c1f5d9f19f06214eccc8a51919231ffb6ad329e2059c7e61f99d2ea60048a0@172.22.0.4:30303",
id: "b898c20aa7c1283c0c16088875226e89697e6ebcb2189d64c5de10c2f5dc86e7",
name: "Geth/v1.10.21-unstable-14b0eeda-20220727/linux-amd64/go1.18.4",
network: {
inbound: false,
localAddress: "172.22.0.3:56428",
remoteAddress: "172.22.0.4:30303",
static: false,
trusted: false
},
protocols: {
eth: {
difficulty: 131072,
head: "0x119a5666fc2fc8879b3d6da3ac58335c6f78cfd5be0ae85909acfc98ed09b937",
version: 67
},
snap: {
version: 1
}
}
}]
配列の中身が2つに増えて、enodeの@マーク以前がそれぞれapp1
とapp3
のものと一致しているのが確認できます。
app3とapp4を接続する
これまで同様なので割愛します。
これで
app1
- app2
- app3
- app4
と繋がりました。
アカウント作成とマイニング
app1
のアカウントからapp4
のアカウントへ送金してみます。
まずアカウントを作成します。
# app1
> personal.newAccount("hoge")
"0x3b701e006aed28e7b98bb1112438e380b5399760"
# app4
> personal.newAccount("hoge")
"0x709f545704f037f0342e94c04387046bbc434f74"
マイニング用のアカウントも必要なので作成して、マイニングを開始します。
今回はapp2
に作ってみます。
# app2コンテナ
> personal.newAccount("hoge")
"0xc077acc1d8d5f910c5e7d7c339b80004f8221efd"
> miner.start(1)
null
しばらくするとマイニングが開始されて、ブロックの数が増えていきます。
# app2コンテナ
> eth.blockNumber
31
他のコンテナでも同様にブロック数が増えているのが確認できると思います。
送金
それでは送金してみます。
ただ、app1
のアカウントは残高が0なので、app2
のアカウントのeth(マイニング報酬で得られている)をapp1
のアカウントに送金します。
送金時はアカウントのアンロックが必要です。
# app2
> personal.unlockAccount(eth.accounts[0])
Unlock account 0xc077acc1d8d5f910c5e7d7c339b80004f8221efd
Passphrase:
true
app1
のアカウントのアドレスを確認して送金します。
# app2
> eth.sendTransaction({from: eth.accounts[0], to: "0x3b701e006aed28e7b98bb1112438e380b5399760", value: 300000000000000})
"0x868a0f18698a9ed81d3159d290d4e2de1ab4593fdbbee9a05e3ed11db6b27d49"
app1
のアカウントの残高を確認してみます。
# app1
> eth.getBalance(eth.accounts[0])
300000000000000
送金されていました。
それでは、app1
のアカウントからapp4
のアカウントへ送金してみます。
app4
のアカウントのアドレスは"0x4e7489808fa24f2f6297a651d420895d8497069da7370710593230b835d112bd
です。
# app1コンテナ
> personal.unlockAccount(eth.accounts[0])
Unlock account 0x3b701e006aed28e7b98bb1112438e380b5399760
Passphrase:
true
> eth.sendTransaction({from: eth.accounts[0], to: "0x709f545704f037f0342e94c04387046bbc434f74", value: 100000000000000})
"0x4e7489808fa24f2f6297a651d420895d8497069da7370710593230b835d112bd"
> eth.getBalance(eth.accounts[0])
178999999853000
送金後に残高が減っています。
送金にはガス代がかかるのでその分も減っています。
app4
のアカウントを確認します。
# app4コンテナ
> eth.getBalance(eth.accounts[0])
100000000000000
送金できていました。
Discussion