지난번에 빌드환경 구성이 일단 끝났고, 이번에는 실제로 Build 를 해보겠다.
Dependency 다운로드가 끝났다면 이제 본격적으로 Visual Studio 용 솔루션과 세부 프로젝트들을 CMake 를 통해 Generate 해야한다.
Command Line 에서도 빌드할 수 있지만 Visual Studio 의 Debug 를 통해 Logic 을 디버깅 해야하는 경우가 생기므로 Visual Studio 용 프로젝트를 생성해본다.
▌Visual Studio Project 생성
이제 본격적으로 Visual Studio 의 Project 를 생성한다.
/d/ethereum/project/webthree-umbrella/build
위 디렉토리를 만들고 들어간 후
[goodjoon webthree-umbrella]$ cd build/
[goodjoon build]$ cmake -DEVMJIT=0 -G "Visual Studio 12 2013 Win64" ..
위와 같이 VS2013 용 Project 를 Generate 한다.
빌드 후 디렉토리는 아래와 같다
이제 생성 된 cpp-ethereum.sln 파일을 Visual Studio 로 열어본다.
▌EVMJIT
EVMJIT(Ethereum Virtual Machine Just In Time Compiler) 는 libethereum 의 서브모듈 이다.
위에서 cmake 시에 -DEVMJIT=0 을 해주었는데, 이렇게 해서 EVMJIT 모듈은 설치하지 않는다.
evmjit 모듈은 LLVM 라이브러리 3.7.0 이상의 Dependency 가 걸려있고, 현재 기준으로 3.7.1 이 최신버전이다.
지난번 extdep 에서 download 받은 라이브러리들 중에는 LLVM 도 포함되어있으며, Release 와 Debug 모드 모두 다운로드 받는다.
그런데 다운로드받은 이 LLVM 라이브러리 의 Debug 버전은 Visual Studio 로 Link 걸고 빌드할 때 _ITERATOR_DEBUG_LEVEL 이 0으로 정의되어있다는 오류가 발생하고, 현재 빌드하려는 프로젝트 (evmjit) 는 이 값이 2 이기 때문에 충돌이 난다고 하며 에러를 수백개 뱉어낸다.
extdeps 로 다운받은 라이브러리만 그런지는 모르겠지만, 일단 Build 환경이 다른것으로 보인다. 이러한 오류 없이 빌드하려면 LLVM 라이브러리를 소스로 가져와 Visual Studio 로 빌드하고 이 빌드된 라이브러리들을 evmjit 의 참조 프로젝트로 또는 linker 의 옵션으로 넣어주어야 할 것이다.
EVMJIT 는 Solidity 와 Serpent 의 Smart Contract Code 를 Console 이나 RPC 를 통해 Bytecode 로 런타임에 컴파일해주는 기능으로, mix 와 같은 툴이나 온라인 IDE 로도 충분히 그 기능을 대체할 수 있으므로 그냥 EVMJIT 모듈을 포함하지 않도록 하겠다.
1.0.0 Frontier Release C++ eth 의 EVMJIT 는 XCode 를 통해 Mac 에서 빌드하는데에는 문제가 없었으나 실제 동작해보면 이 또한 JIT 컴파일이 지원되지 않는 버그를 볼 수 있어서 궂이 빌드하지 않도록 하는 두번째 이유가 된다.
▌빌드 하기
솔루션 파일을 열어보면, 아래 처럼 BUILD_ALL 프로젝트가 StartUp Project 로 설정되어있다.
향후에 코드 분석에 필요하니 일단 Debug 모드를 Active 로 두고 ALL_BUILD 프로젝트를 Build 를 해본다.
(처음에는 File 들을 Parsing 하고 Indexing 하느라고 시간이 좀 걸린다. 빌드하는데에는 상관 없으므로 바로 빌드 해보자)
99개 에러와 353개 경고가 보인다.
▌에러 수정
위 에러의 이유는 모두 소스파일의 Characterset Encoding 때문에 생긴것이다.
위 밑줄친것과 같은 UTF-8 특수캐릭터들이 PoC7 버전때 부터인가 지속 추가되어 화면에 이쁘게(?) 출력하기 위한 이상한 짓을 많해 해놓고있는데 이게 CP949 기반인 한글 Windows 와 No-BOM UTF-8 만을 자동인식하는 Visual Studio 2013 의 컴파일러가 환상적으로(?) 결합되어 빚어내는 문제이다.
에러가 발생한 소스파일을 에러를 더블클리해서 Open 한 다음
File > Advanced Save Options ... 에서
- Unicode (UTF-8 with signature) - Codepage 65001 를 선택한다.
- OK 를 누른 후, ctrl+S 로 파일을 저장해준다
(반드시 with Signature 로 해야한다. Visual Studio 2013 은 (2015도 마찬가지) BOM 없는 UTF-8 을 자동인식하지 않으며, 이를 강제하는 옵션도 발견하지 못했다.
한글 Windows 의 경우 OS 의 기본 Characterset 인 CP949 를 기본 Characterset 으로 지정하여 파싱한다.)
BlockHeader.cpp
Block.cpp
BlockQueue.cpp
BlockChain.cpp
ClientBase.cpp
State.cpp
TransactionQueue.cpp
위 파일들을 Advanced Save Options.. 로 Encoding 을 수정해수고 저장한다.
그리고 다시 Build 를 한다.
(Warning 은 무시한다. 대부분이 Character Encoding 해석이 안된다는 경고이다.)
failed 프로젝트가 없다면 성공한 것이다.
▌동작 테스트
AlethZero 나 mix 는 좀더 수정할 것들이 있어서 향후에 해보도록 하고, 일단 가장 중요한 Ethereum 의 CLI 인 eth 가 작동하는지 확인해보자.
MinGW 로 Bash 를 열고, .bash_profile 에
export ETH_HOME=/d/ethereum
export ETH_BUILD=/d/ethereum/project/webthree-umbrella/build
위와같이 ETH_HOME 과 ETH_BUILD 환경변수를 추가해주었다.
내가 내부적으로 사용하기 위해서 추가한것이고, 이후 블로그에서는 이 환경변수를 기반으로 설명할것이다.
디렉토리가 eth 프로젝트 디렉토리이며, 그 밑의 Debug 디렉토리에 좀전에 빌드한 eth.exe 실행파일이 위치하고있다.
버전 확인을 해본다.
[goodjoon Debug]$ ./eth --version
eth version 1.1.3
eth network protocol version: 63
Client database version: 12041
Build: Windows/msvc/int/Debug
1.1.3 버전임을 알 수 있다. (C++ 팀 리더가 바뀌면서 요즘 바짝 긴장을 했는지, 나흘전에 또 1.1.4가 릴리즈 되었다)
보면, Network Protocol Version 이 63 인것을 볼 수 있다. Client 가 다르더라도 저 PV 는 같은 버전이어야 함을 상기해야한다.
Frontier 가 릴리즈된 1.0.0 버전은 PV61 이다. 지금은 PV61,62,63 클라이언트들이 Frontier 에도 섞여있다.
일단 Frontier 에 붙어서 동작하는걸 확인해보겠다. 이왕이면 Mining 도 ON 시켜서 동작시켜보자.
또한 CLI JS Console 로 들어가는 옵션도 함께 넣어보자.
[goodjoon Debug]$ ./eth --mining on console
(++)Ethereum
! 21:21:18.012
|main void __cdecl dev::eth::Client::init(class dev::p2p::Host
*,const class std::basic_string
<char,struct std::char_traits
<char
>,class std::allocator
<char
> > &,enum dev::WithExisting,class boost::multiprecision::number
<struct boost::multiprecision::backends::cpp_int_backend
<256,256,0,0,void
>,
0>) 18794 ms
Please enter a MASTER password to protect your key store (make it strong
!): Error initializing key manager: D:
\ethereum
\project
\webthree-umbrella
\libweb3core
\libdevcore
\CommonIO.cpp(145): Throw
in function class std::basic_string
<char,struct std::char_traits
<char
>,class std::allocator
<char
> > __cdecl dev::getPassword(const class std::basic_string
<char,struct std::char_traits
<char
>,class std::allocator
<char
> > &)
Dynamic exception
type: class boost::exception_detail::clone_impl
<struct dev::ExternalFunctionFailure
>
std::exception::what: Unknown exception
! 21:21:19.011
|main Stop worker 962 ms
[goodjoon Debug]$
실행 하자마자 에러이다.
일단 옵션을 설명하면,
--mining on : 마이닝 기능을 켠다는 이야기이다
console : Javascript Console 모드로 들어가라는 이야기이다
그런데 바로 뒤에 에러가 난다. Key Manager 를 초기화 하다가 에러가 나는데, 원래는 "Please enter a MASTER password to protect your key store ..." 하고나서 패스워드를 물어보게 되어있다.
그리고 이 Master Password 를 입력하고나면 그 다음단계로 진행하는데, MinGW 환경이나 CygWin 환경에서는 에러가 난다. 똑같은 커맨드를 쳐도 Windows Command Prompt 상에서는 이후에 잘 진행되는것을 확인할 수 있다. 문제는 KeyManager 모듈이 load() 될 때 발생되는데, 이건 나중에 수정되어야 할 부분이다.
이 Master Password 는 예전 PoC 8 버전에서도 도입되지 않다가 Frontier 릴리즈와 함께 도입되었는데, Wallet 의 Private Key 관리의 보안상 허점이 많이 있기 때문에 이 부분의 보안강화를 위해 생겨났다. ethkey.exe 를 통해 향후 좀더 상세한 관리를 할 수 있고, 예전처럼 무식하게 config.rlp 에 Private Key 와 Public Key 를 넣어두지 않는다는점에 주의해야 한다.
Master Password 는 까먹으면 끝장이다. Wallet 의 모든 Key 들을 관리하기 위한 Key Store 의 Master Password 이며, Private Key 를 사용하기 전에 이 Master Key 패스워드를 입력해야 한다.
현재 MinGW 에서 CLI 로 interaction 하는때에 오류가 생기므로, 옵션을 추가하여 아예 Master Password 를 지정해주면서 시작하자.
[goodjoon Debug]$ ./eth --mining on console --master 1111
(++)Ethereum
Ethereum (++) 1.1.3
Code by Gav Wood et al, (c) 2013, 2014, 2015.
Transaction Signer: XE602H4XB00HUY08LU416SCBGO3CS63H66 (1a7e55b0-3eb7-05ec-248e-8d901e268d76 - 0096bb98)
Mining Beneficiary: XE602H4XB00HUY08LU416SCBGO3CS63H66 (1a7e55b0-3eb7-05ec-248e-8d901e268d76 - 0096bb98)
Foundation: XE55PXQKKK4B9BYPBGT1XCYW6R5ELFAT6EM (00000000-0000-0000-0000-000000000000 - de0b2956)
i 21:41:11.885|p2p UPnP device: http://192.168.0.1:4112/etc/linuxigd/gatedesc.xml [st: urn:schemas-upnp-org:device:InternetGatewayDevice:1 ]
! 21:41:11.963|main void __cdecl dev::p2p::Host::start(void) 2216 ms
Node ID: enode://2ee2a7e6a73b3af8aa737ce8bf016322828bdfdc5249d1ce4454f9e570a5b1486d988fd1119dacb18f50759de52c0728e4b4d229c01d5fdaa52fdd498ee81b57@211.222.99.182:30303
JSONRPC Admin Session Key: gsIJYPwuntU=
i 21:41:12.450|<unknown> Loading full DAG of seedhash: #00000000…
위와같이 --master 옵션을 주고 1111 로 패스워드를 지정해주니 잘 진행이 된다.
그리고 Mining 을 위해 DAG file 을 생성하게 된다. Dagger Hashimoto 파일이라고 불리는데, 1GB 정도 크기의 2차원 배열 데이터를 갖는 파일이다. Mining 시에 적은 ASIC 을 사용한 마이닝을 방지하고 GPU 연산을 더 효율적으로 하고, Light Client 의 Verification 성능향상 등을 위해 Hashing 시 필요한 대규모의 Cache 를 만들어놓는것이다. 자세한 이야기는 다음에 더 하도록 하고, 일단 1GB 의 용량이 최소한 필요하다는것을 상기하자.
DAG 테이블을 모두 만들어내는데에는 수십분 정도 소요되니 바람이나 좀 쐬고 와도 된다.
막간을 이용해 eth 가 사용하는 디렉토리들을 잠깐 설명해보면,
%APPDATA%/Local/ethash - DAG 파일이 위치한다
%APPDATA%/Roaming/Ethereum/config.rlp - Address 가 저장된 파일이다. eth 가 사용하며, 예전에는 Private Key, Public Key 가 들어있었으나 지금은 Key 메커니즘이 복잡해졌고, 이는 향후에 설명한다.
%APPDATA%/Roaming/Ethereum/keys.info, keys.info.salt - 실제적인 Key 가 들어있는 파일이다. AlethZero 와 eth 가 공용으로 사용한다.
%APPDATA%/Roaming/Ethereum/.web3 - Webthree 가 사용하는 Key 에 대한 정보가 들어있다.
%APPDATA%/Roaming/Ethereum/<4바이트HEX값> - state Trie, extra 데이터, block 데이터 등이 저장되는 Level DB 가 있다
|
이제 1GB 가량의 DAG 파일이 생성이 완료되면, Full DAG loaded 라고 뜬다.
그런데 또 문제가 있다.
console 이 안뜬다 (원래는 이쁘게 '>') 모양과 함께 web3 라고 치면 web3.js 의 JavaScript 개체들이 주욱 나와야 하나 아예 Console 자체가 동작하지 않는다.
이 또한 Windows 에서의 eth 버그이다. 사실 이후에도 eth 의 Windows 에 대한 외면은 매우 많고 다양하다.
가장 잘 돌아가는 환경은 Mac 과 Ubuntu 이다. Windows 버전은 그냥 울며 겨자먹기로 써야하는 상황이다.
그도 그럴것이, eth 는 Front-End-User 용이 아니다. Back-End 용으로 분류하고있으며, 그래서 Linux 환경에 대해서는 매우 신경을 많이쓰는 느낌이다.
▌Console Attach 하기
Console 이 안된다고해서 실망할 필요는 없다. eth 는 attach 기능을 통해, 기존에 동작하는 eth 인스턴스에 console 을 attach 할 수 있다. 물론 2개의 Terminal 창이 필요하긴 하지만, console 을 쓸 수 있다는게 어디인가?
일단, 현재 실행중인 Process 는 죽인다. Console 이 동작 안하니 ctrl+C 를 눌러 break 시켜버리자. ctrl+C 로 안되면 작업관리자를 열어서 강제 종료 시켜버려도 된다.
그리고, MinGW Bash Shell 을 2개 띄우고, 하나의 Shell 에서는
[goodjoon Debug]$ ./eth --json-rpc --json-admin 0000 --mode full --mining on --master 1111 --verbosity 5 console
로 실행한다. 여기서 추가된 옵션만 설명하면,
--json-rpc : JSON-RPC 인터페이스를 사용하겠다는 이야기이다. 외부 eth 를 attach 모드로 실행한 후 JavaScript Console 로 명령을 날리면, Console 이 이 JavaScript API 를 JSON-RPC 를 통해 대상 프로세스에게 요청하도록 되어있다.
--json-admin : 옵션에서 --help 를 치면 --admin 이라고 되어있는데, 이건 오타이다. 이런 옵션은 없으며, --json-admin 을 사용해야한다. (이게 언제쩍 버그인데 여태 수정을 안하고있다) JSON-RPC 를 사용하려면 Session Key 를 주어야 하는데, 원래는 eth 가 실행될 때 로그 초반에 Session Key 라는게 출력될 때 랜덤하게 생성된 그 키를 복사에서 attach 할 프로세스에 session key 로 지정해주어야 한다. 불편함을 줄이기 위해 아예 이 값을 --json-admin 옵션으로 주도록 했다.
--mode full : 기본값이긴 한데, Full Node 로 작동하게 할것인지, Peer Node (피어들의 정보만을 제공하는 노드)로 동작시킬것인지를 지정한 것이다
--verbosity 5 : Log 출력의 Verbosity 를 지정해준다. 0~99 까지 줄 수 있는데 5만 주어도 로그가 넘쳐난다.
기본값이 Frontier Network 으로 Bootstrap 하게 되어있으므로 --frontier 옵션을 쓰지는 않았다.
시간이 조금 지나면 Peer 간에 Hello 메시지를 주고받으며 PING/PONG 을 하다가 Block 을 Import 하여 Sync 하기 시작하고, Validation 작업을 수행한다.
이제 두번째 터미널을 열고, Console 을 Attach 해보자
[goodjoon Debug]$ ./eth --session-key 0000 attach
>
--session-key 는 JSON-RPC 의 --json-admin 옵션으로 준 Key 값을 준것이고, attach 를 통해 기본적인 localhost:8545 포트의 JSON-RPC 로 연결하게 된다.
이제 잘 attach 가 되었는지 확인해보자
> web3.version
{
api:
'0.15.1',
node:
'++eth-v1.1.3-0//Debug-Windows/msvc/int',
getNode: [Function],
network:
'',
getNetwork: [Function],
ethereum:
'0x3f',
getEthereum: [Function],
whisper: Error: METHOD_NOT_FOUND: The method being requested is not available on this server,
getWhisper: [Function]
}
>
node: 는 Client 의 Node 식별을 위한 Full Name 이며, 별도의 옵션을 주지 않았으므로 ++eth-v1.1.3-0//Debug-Windows/msvc/int 와 같이 나온다
ethereum: 은 eth 프로토콜 버전이며 10진수로 변환하면 63 이 된다
> web3.eth.coinbase
'0x0096bb9802b14f72ba4cdbd105127fe57a1dafae'> web3.eth.blockNumber
24840
> web3.eth.getBlock('latest')
{
transactions: [],
receiptsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',
hash: '0xc88b3957581f72a2aa1f5e5bcad12898d1117ba492adc88398bdd4b2a6275b7c',
seedHash: '0x0000000000000000000000000000000000000000000000000000000000000000',
miner: '0x3f98e477a361f777da14611a7e419a75fd238b6b',
uncles: [],
extraData: '0x476574682f76312e302e302f6c696e75782f676f312e342e32',
gasLimit: 5000,
transactionsRoot: '0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421',
gasUsed: 0,
size: 0,
logsBloom: '0x000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000
0000000000000000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000000000000000000000',
totalDifficulty: 16505476021090676',
number: 25657,
parentHash: '0xcb29677c43d3877bc401b578a64e2de7c7839a5080163b2bf3194b5fa5bdcc29',
boundary: '0x0000000000fa64ef32fc5b94d0c88ff91d9f142f5459c015652cd4e44bb3bc27',
author: '0x3f98e477a361f777da14611a7e419a75fd238b6b',
stateRoot: '0x429dd84e5a8e30f31a4442b7a0bc7aae79f459751ffcc57d2a2d6f699a38e532',
difficulty: 1124127046574',
nonce: '0xa8f99a70ac8ec05e',
timestamp: 1438579639,
mixHash: '0xf42a0dd21ec4fce0c20b21af14b661f6d43babc3c5bc215f1652a77f3fdc877b',
sha3Uncles: '0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347'
}>
자신의 coinbase 와 가장 마지막 block 의 정보를 출력해봤다. 모두 의미있는 값이 나온다면 정상동작중인 것이다.
다음 부터는 Ethereum 의 사상에 대해서 조금씩 이야기를 정리해보기로 하겠다.
음.. 그전에 Blockchain 에 대한 기술적인 정리도 조금 할까..? 생각 중이다. 1.0 의 기술적인 개념부터 이해를 해야 Ethereum 의 개념도 이해가 갈테니 말이다.
그럼 오늘은 또 이만~