[Blockchain] Docker에 Ethereum Wallet을 설치하기​ #2

in #ethereum2 years ago (edited)

docker+ethereum.jpg


지난번엔 Docker Ubuntu Container에 직접 Ethereum을 설치하는 방법을 알아보았다.

이번에는 Docker Hub에 있는 공식 Go Ethereum Image를 설치하고 Account를 만들어 테스트 해본다.

Go Ethereum Docker Image 다운로드​

$ docker pull ethereum/client-go
Using default tag: latest
latest: Pulling from ethereum/client-go
ff3a5c916c92: Pull complete
bd2ef4cc911f: Pull complete
656176c52e80: Pull complete
089b122c2f1f: Pull complete
Digest: sha256:915b9578a69577ea0789149dec4e0cac2c3621561548a277698ecfc4ec9930ad
Status: Downloaded newer image for ethereum/client-go:latest

$ docker images
REPOSITORY           TAG                 IMAGE ID            CREATED             SIZE
ethereum/client-go   latest              20cd0f9f9136        3 days ago          42.6MB

정상적으로 다운로드가 되었으면 ethereum/client-go 이미지가 설치된 것을 볼 수가 있다.

Genesis Block 생성

최초에 Ethereum Block을 생성할 때에는 Genesis Block을 생성해야한다. Live Network나 Test Network는 이미 Genesis Block이 생성되어있기때문에 필요가 없지만 Private Network에 생성하려면 Genesis Block을 생성해야한다.

Container 내부에 Data를 넣게 되면 Container가 삭제될때에 Data도 함께 삭제된다.
Container가 삭제되더라도 Data는 남을 수 있도록하기 위해서 Container 외부에 Data가 존재해야한다.

앞으로 Container를 Guest라고 하고 Docker를 실행한 PC를 Host라고 하자.

Container 외부인 Host에서 Container내부인 Guest와 공유하기위한 디렉토리를 만든다.

$ mkdir eth_data

eth_data 디렉토리는 앞으로 Guest와 공유할 것이고 Ethereum Data가 저장될 예정이다.
그 안에 genesis.json파일을 생성한다.

# genesis.json

{
  "config": {
        "chainId": 15,
        "homesteadBlock": 0,
        "eip155Block": 0,
        "eip158Block": 0
    },
  "alloc"      : {},
  "coinbase"   : "0x0000000000000000000000000000000000000000",
  "difficulty" : "0x20000",
  "extraData"  : "",
  "gasLimit"   : "0x2fefd8",
  "nonce"      : "0x0000000000000042",
  "mixhash"    : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "parentHash" : "0x0000000000000000000000000000000000000000000000000000000000000000",
  "timestamp"  : "0x00"
}

eth_data/genesis.json 파일을 생성했으면 준비가 끝났다.

이제 Genesis Block를 생성하면서 초기화하자.

$ docker run -v $(pwd)/eth_data:/root/.ethereum ethereum/client-go init /root/.ethereum/genesis.json
INFO [04-16|08:44:52] Maximum peer count                       ETH=25 LES=0 total=25
INFO [04-16|08:44:52] Allocated cache and file handles         database=/root/.ethereum/geth/chaindata cache=16 handles=16
INFO [04-16|08:44:52] Writing custom genesis block
INFO [04-16|08:44:52] Persisted trie from memory database      nodes=0 size=0.00B time=1.681µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [04-16|08:44:52] Successfully wrote genesis state         database=chaindata                      hash=5e1fc7…d790e0
INFO [04-16|08:44:52] Allocated cache and file handles         database=/root/.ethereum/geth/lightchaindata cache=16 handles=16
INFO [04-16|08:44:52] Writing custom genesis block
INFO [04-16|08:44:52] Persisted trie from memory database      nodes=0 size=0.00B time=2.054µs gcnodes=0 gcsize=0.00B gctime=0s livenodes=1 livesize=0.00B
INFO [04-16|08:44:52] Successfully wrote genesis state         database=lightchaindata                      hash=5e1fc7…d790e0

Successfully wrote genesis state ​문구가 보이면 성공적으로 초기화가 된 것이다.

eth_data 에 생성된 파일들을 확인해보자.

$ tree eth_data
eth_data
├── genesis.json
├── geth
│   ├── chaindata
│   │   ├── 000001.log
│   │   ├── CURRENT
│   │   ├── LOCK
│   │   ├── LOG
│   │   └── MANIFEST-000000
│   └── lightchaindata
│       ├── 000001.log
│       ├── CURRENT
│       ├── LOCK
│       ├── LOG
│       └── MANIFEST-000000
└── keystore

4 directories, 11 files

정상적으로 생성된 것을 확인 할 수가 있다.

​이제 Ethereum을 실행해보자. Docker Container를 실행하면 자동으로 Ethereum이 실행된다.

$ docker run -it -p 30303:30303 -p 8545:8545 -v $(pwd)/eth_data:/root/.ethereum \
--name ethereum_container ethereum/client-go --networkid 4649 \
--nodiscover --maxpeers 0 --rpc --rpcaddr "0.0.0.0" --rpccorsdomain "*" \
--rpcapi "admin,db,eth,debug,miner,net,shh,txpool,personal,web3" console

Ethereum Network는 30303 Port를 사용하기때문에 Geust와 Host의​ 30303 Port를 연결하고 초기화한 eth_data 디렉토리를 공유한다.

추가적인 parameter는 ​geth parameter이므로 https://github.com/ethereum/go-ethereum/wiki/Command-Line-Options에서 내용을 확인​할 수가 있다.

Welcome to the Geth JavaScript console!

instance: Geth/v1.8.4-unstable-60516c83/linux-amd64/go1.10.1
 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

>

Ethereum Console 프롬프트가 뜨고 정상적으로 실행이 되었다.
하지만 exit 명령을 입력하고 Ethereum Console이 종료되면 Container도 함께 종료된다.

> exit
INFO [04-16|09:05:00] HTTP endpoint closed                     url=http://0.0.0.0:8545
INFO [04-16|09:05:00] IPC endpoint closed                      endpoint=/root/.ethereum/geth.ipc
INFO [04-16|09:05:00] Blockchain manager stopped
INFO [04-16|09:05:00] Stopping Ethereum protocol
INFO [04-16|09:05:00] Ethereum protocol stopped
INFO [04-16|09:05:00] Transaction pool stopped
INFO [04-16|09:05:00] Database closed

$

Guest가 종료되고 Host로 빠져나오게 된다.

Ethereum Console을 다시 실행하려면 start 명령으로 실행하면 된다.

$ docker start -ai ethereum_container

다시 Ethereum Console 프롬프트가 나온다. 그리고 기존의 Container의 내용은 그대로 복원된다. Container가 stop 상태이기 때문에 다시 start 를 하면 Parameter와 그 안의 내용은 변함없이 지속된다.​

우리는 Container를 실행할 때에 eth_data 디렉토리를 Guest와 Host가 공유하기때문에 설사 Container가 삭제되더라도 그 안의 내용은 그대로 복원된다.

Account 생성

Ethereum Console에서 Account를 생성하고 Mining하여 Balance가 늘어나는지 확인한다.

> eth.accounts
[]

현재 만들어진 Account는 아무 것도 없다.

personal.newAccount(패스워드) 명령으로 Account를 생성할 수가 있다.

> personal.newAccount('mypass')
"0xf6a2d2cf4bd9bc24b83fe8f30da59f01e5eb1d18"
> personal.newAccount('mypass')
"0xfb8e9e865f63c325dbb4caad052a7330693925c3"
> personal.newAccount('mypass')
"0xed208035f0fb054430c20e016f3c8a8cbcf559c6"
> eth.accounts
["0xf6a2d2cf4bd9bc24b83fe8f30da59f01e5eb1d18", "0xfb8e9e865f63c325dbb4caad052a7330693925c3", "0xed208035f0fb054430c20e016f3c8a8cbcf559c6"]
>

3개의 Account가 생성되었다.

Mining (채굴)

coinbase는 Mining을 할때 보상이 들어오는 계정이다.

> eth.accounts[0]
"0xf6a2d2cf4bd9bc24b83fe8f30da59f01e5eb1d18"
> eth.coinbase
"0xf6a2d2cf4bd9bc24b83fe8f30da59f01e5eb1d18"
>

coinbase가 첫번째 Account (eth.accounts[0])와 동일한 것을 확인할 수가 있다.

Mining을 시작해보자

> eth.getBalance(eth.accounts[0])
0
> eth.blockNumber
0
> eth.mining
false
> miner.start(1)
.
.
.
> eth.mining
true

Mining이 시작되었다. 최초에 시작하면 DAG가 생성될 때까지 기다려야한다.​

...
INFO [04-16|09:26:52] Generating DAG in progress               epoch=1 percentage=56 elapsed=2m0.271s
INFO [04-16|09:26:54] Generating DAG in progress               epoch=1 percentage=57 elapsed=2m2.471s
INFO [04-16|09:26:57] Generating DAG in progress               epoch=1 percentage=58 elapsed=2m4.634s
...

percentage가 100이 되고 잠시 기다리면 Block이 생성되기 시작한다.

> eth.blockNumber
21
> miner.stop()
true
> eth.mining
false
> eth.getBalance(eth.accounts[0])
105000000000000000000
>

새로운 Block이 생성되었고 eth.accounts[0]의 잔고에 값이 생긴 것을 알 수가 있다.

잔고를 좀 더 알아보기 쉽기 위해서는 다음과 같이 실행해보자

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

105 Ether가 생긴 것을 알 수가 있다.​

Ether 송금

Ether 송금을 위해서는 보내는 Account가 UnLock상태여야한다.

personal.unlockAccount(계정, 패스워드, 유효기간)

위의 명령으로 UnLock을 할 수가 있다.

> personal.unlockAccount(eth.accounts[0], 'mypass', 0)
true

마지막 parameter에 0을 입력하면 geth 프로세스가 종료될 때까지 UnLock을 유지한다. 편의상 0​을 입력하자.

eth.sendTransaction({from: 보내는 계정, to: 받는 계정, value: 보내는 값})

송금을 위해서는 eth.sendTransaction을 실행한다.

첫번째 계정(eth.accounts[0])이 Mining을 통해서 얻는 Ether를 두번째 계정(eth.accounts[1])으로 송금해보자.

> eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value:web3.toWei(1, "ether")})
"0x56bc8cbcacecf62cc15e44d90e8650d0f530133c25e61f0c4d4ef09c7777575e"

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

정상적으로 송금이 완료되어 Transaction ID가 생성되었지만 아직 eth.account[1]에는 이더가 도착하지 않았다.

> eth.pendingTransactions
[{
    blockHash: null,
    blockNumber: null,
    from: "0xf6a2d2cf4bd9bc24b83fe8f30da59f01e5eb1d18",
    gas: 90000,
    gasPrice: 18000000000,
    hash: "0x56bc8cbcacecf62cc15e44d90e8650d0f530133c25e61f0c4d4ef09c7777575e",
    input: "0x",
    nonce: 1,
    r: "0x17f87f709f5899ab369458cfe647443da11b39e2f49ba06ebca6ba4570ccf367",
    s: "0x6828fcd200603c49c6fc388d165900855af5276575929dae789f984f9812174",
    to: "0xfb8e9e865f63c325dbb4caad052a7330693925c3",
    transactionIndex: 0,
    v: "0x273c",
    value: 1000000000000000000
}]

새롭게 생성된 Transaction ID는 아직 pending 상태인 것을 확인할 수가 있다.​

송금을 위해서는 Mining을 통해서 Block을 생성해야한다.

> miner.start(1)
null
> eth.pendingTransactions
[]
> miner.stop()
true
>

Mining을 시작하고 pendingTransactions이 없어진 것을 확인 할 수가 있다.

이제 eth.accounts[1]계정에 잔고가 생겼는지 확인해보자

> eth.getBalance(eth.accounts[1])
1000000000000000000

eth.accounts[1]의 잔고가 생긴 것을 알 수가 있다.

송금시 생긴 Transaction ID로 더 자세한 내용을 살펴보자

> eth.getBalance(eth.accounts[1])
2000000000000000000
> eth.getTransaction('0x56bc8cbcacecf62cc15e44d90e8650d0f530133c25e61f0c4d4ef09c7777575e')
{
  blockHash: "0x61b8627f9dfc126b8a5e237fafbacdc29c930422820ec0fa2bc6e244c783082f",
  blockNumber: 33,
  from: "0xf6a2d2cf4bd9bc24b83fe8f30da59f01e5eb1d18",
  gas: 90000,
  gasPrice: 18000000000,
  hash: "0x56bc8cbcacecf62cc15e44d90e8650d0f530133c25e61f0c4d4ef09c7777575e",
  input: "0x",
  nonce: 1,
  r: "0x17f87f709f5899ab369458cfe647443da11b39e2f49ba06ebca6ba4570ccf367",
  s: "0x6828fcd200603c49c6fc388d165900855af5276575929dae789f984f9812174",
  to: "0xfb8e9e865f63c325dbb4caad052a7330693925c3",
  transactionIndex: 0,
  v: "0x273c",
  value: 1000000000000000000
}

Pending때에 Transaction 내용중 null이였던 blockNumber, blockHash의 값이 새롭게 만들어 진 것을 볼 수가 있다.

eth.accounts[0]의 ether 잔고를 확인해보면 소수점 자리가 없는 것을 확인할 수가 있다.
송금시 송금한 계정에서 수수료가 출금되어야 하는데 소수점자리가 없다는 것은 수수료가 출금되지 않았다는 의미이다.

왜 송금시 수수료가 출금되지 않은 것일까?

송금시 gas라는 수수료는 출금되고 Miner(채굴자)에게 돌아가게 된다.
eth.accounts[0] 계정이 송금자이자 Miner이기 때문에 수수료가 출금되지 않은 것 처럼 보인다.
하지만 수수료는 출금되었고 해당 수수료는 본인이 채굴을 통해서 다시 갖게 된 것이다.​