Smart Contract 는 1994년 암호학, 법학에 일가견이 있는 컴퓨터 과학자인 Nick Szabo 에 의해 소개된 개념이다. 신뢰할 수 없는 컴퓨터 인터넷 환경에서 "고도로 발달된" 계약을 준수하도록 하는 프로토콜이다.

혹 처음 "스마트 계약" 이라는 말을 처음 듣는 사람들은, 블록체인에 "계약" 이라 불리울만한 "문서"를 업로드 하고 이를 준수하도록 "약속"하는 것이라는 막연한 상상을 하고는 한다(실제로 있었다). Contract 라는 용어가 그러한 오해를 살 수도 있는게 사실이긴 하다.

말을 약간 바꿔보자. "스마트 계약"은 블록체인에 모든 Node가 접근할 수 있는 "코드" 를 업로드 하고, 이를 "실행" 하도록 하는 "프로토콜" 이라고 한다면 좀더 이해하기 편할 수 있을 것이다.


▌사전 이해 필요항목
일단 이 Smart Contract 에 대하여 기술적으로 이해하기 전에 Ethereum 의 기본적인 아래 사항에 대해 이해하고 있는게 좋다.

  1. EOA/Contract Account
  2. Account / Global State
  3. Create/Message/Transfer Transaction, Call
  4. Transaction Receipt
  5. Gas, GasPrice
  6. Ethereum Virtual Machine
  7. Turing Complete

(위 개념들은 시간나는대로 정리하여 블로그로 올리기로 한다)

▌Bitcoin 의 Contract Code

Contract 는 마치 Assembly 코드를 연상시키는 듯한 Byte Code 형태의 코드로 구성해왔다. OP_CODE 로 불리우는 1바이트 크기의 명령인 Operation Code 들은 Stack 에 쌓이며 실행된다. 

Bitcoin 의 경우, Script 라고 부르고 있으며 기본적인 Balance Transfer Transaction 의 경우 Client 가 Transaction 의 Script 파트에 Pubkey, PubKeyHash, Private Key Signature 를 연산하여 UTXO 를 소비하려는 현재 Transaction 이 정당한지를 검증하는 Script 를 자동으로 추가하도록 되어있다.

좀더 자세히는, UTXO 의 Public Key Hash 가  Transaction 의 INPUT 파트에 있는 Unlocking Script 내의 Public Key 의 HASH160 OPCODE 연산 결과와 같은지를 비교하며 최종 CHECKSIG OPCODE 를 통해 Private Key 의 Signature 를 Public Key 로 검증하여 TRUE 를 리턴하는지를 검사한다.

이러한 Script 파트에는 원하는 OPCODE 들을 구성하여 Transaction 을 만들어낼 수 있는데, 쌍방 혹은 여러 “신뢰되지 않은” Peer 들 간에 특정한 OPCODE Script를 실행시켰을 때 값이 정상이라면 그 Transaction 을 "정상으로 인정한다”라는 “계약적인 조건”을 명시하고 실행 가능하므로, 이러한 개념으로  Contract Code 라고 표현하고 있다.

Bitcoin 의 OPCODE 는 Constants, Flow Control, Stack, String 의 Splice, Bitwise, Arithmetic, Crypto, Locktime, Pseudo-Words 의 카테고리에 해당하는 85개 정도의 명령어를 제공하고있다.

▌Ethereum 의 Smart Contract 차이점 

Bitcoin 이 선보인 이 Contract Script 는 매우 획기적인 방식이며 신뢰할 수 없는 환경에서 신뢰를 기반으로 해야하는 중요한 거래가 가능하도록 하는 핵심적인 요소로서 동작한다. Ethereum 의 Vitalik Buterin 은 어린나이에 이러한 Bitcoin Blockchain 에 대해 이해하고 Contract Code 를 더 확장하면 무한한 확장성을 가진 탈 중앙화 된 컴퓨팅 환경, 더 나아가서는 Global/World Computer 를 실현시킬 수 있다고 믿고 Gavin Wood 박사는 그의 사상을 구현하기위해 함께하며 개발언어에 독립적인 Yellow Paper 를 만든다.

Ethereum 은 Bitcoin 의 Contract 에 비해 매우 고도로 발전된 형태의 Contract Code 라고 말할 수 있으며, 단순한 “검증” 과 “연산” 을 뛰어넘는 ”상태”와 “함수”를 정의하고 “상태변이”와 “데이터 저장”이 가능한  Turing Complete 코드 개발을 가능하도록 하였다.

APPLY(S)=S’
(APPLY: 상태변이 함수, S:현재상태, S’: 변이된 상태)
 
라는 상태변이 함수과계에서,
Bitcoin 의 S와 S' 는 UTXO 의 Balance 만이 가능하지만,
Ethereum 의 S 는 EOA(Externally Owned Account)의 Balance 는 물론, Account의 Nonce, Contract Account 의 Storage 에 저장된 데이터의 값들도 포함된다.

즉, “잔고” 뿐만이 아니라 “데이터” 또한 상태변이 함수로 변이 가능한 상태의 대상이 된다는 것이다.
또한 이러한 상태변이 함수를 Smart Contract 라고 부르며, Smart Contract 는 EVM(Ethereum Virtual Machine) 이 실행시키고, 여기에서 실행되는 이러한 Smart Contract 함수를 정의한 Code 를 EVM Code 또는 Smart Contract Code (줄여서 Contract Code) 라고 부른다.

Loop 지원, Contract Account, (Contract 의) Block 과 Transaction 정보에의 접근, Statefull, Gas 개념 등은 Ethereum Smart Contract 의 큰 특징이다.

Bitcoin 대비 별것 아닌 변화일것이라 생각될 수 있지만 이것은 Blockchain 기술의 적용 가능 분야를 무한히 확장할 수 있는 큰 변화이다. 복잡한 금융 파생상품에서 부터 지능형 머신/IoT 디바이스의 자율적 협업,  슈퍼컴퓨터를 초월하는 강력한 컴퓨팅 까지 가능하게 하는 중요한 열쇠가 바로 이 Smart Contract 라고 할 수 있다.

Ethereum 은 Dapp (Decentralized Application) 이라는 개념을 Smart Contract 를 중심으로 하여 실현하고 있으며, 이는 기존의 중앙 집중식의 시스템의 단점(DoS 취약, 보안 취약, Single Point of Failure, Scalability 한계성, Privacy 침해, 외부 인터페이스 표준화 문제 등)으로 부터 자유로워질 수 있는 탈중앙화된 Appication 아키텍쳐가 가능해지는 기술이라고 할 수 있으며, 전통적인 P2P 로는 풀 수 없었던 “신뢰할 수 없는 환경에서의 신뢰성 있는 서비스 운영”이 가능하게 하는 핵심적인 기술이 아닐까 생각한다.

▌EVM

Ethereum 은 Smart Contract Code 구현을 위해 4가지의 언어를 구현한다. 

Mutan, LLL, Serpent, Solidity 가 현재 Ethereum 의 Smart Contract 를 구현할 수 있는 Code 이다. 이중 Mutan 은 C 언어와 유사한 구조를, LLL 은 Low-Level 의 OPCODE 형태를 가지며, Serpent 는 Python 과 유사하다고 할 수 있고, Solidity 는 JavaScript 와 매우 닮았다.

Mutan 은 Deprecated 되었으며, LLL 은 더이상 Develop 하지 않으나 여전히 지원하고는 있으며 Serpent 와 Solidity 가 주요 언어라고 볼 수 있으며 그 중 Ethereum 은 Solidity 를 Main 언어로 주력하고있다.

EVM(Ethereum Virtual Machine) 은 이러한 상위레벨 언어로 만들어진 코드가 컴파일되어 생성되는 Byte  Code 를 실행하기 위한 Runtime 으로, OPCODE, Stack 외에 Ethereum 에서 추가된 Memory, Storage 를 사용하는 주체이기도 하다.

Smart Contract 실행 (Create, Message Transaction)은 모든 Block 검증을 수행하는 Node 에서 동일하게 일어난다. 또한 Contract 를 실행하는데에는 현재 Homestead 에서도 아래와 같은 Gas 를 소모해야 한다.

Operation Name Gas Cost Remark
step 1 default amount per an execution cycle
stop 0 free
suicide 0 free
sha3 20  
sload 20 get from permanent storage
sstore 100 put into permanent storage
balance 20  
create 100 contract creation
call 20 initiating a read only call
memory 1 every additional word when expanding memory
txdata 5 every byte of data or code for a transaction
transaction 500 base fee transaction
contract creation 53000
changed in homestead from 21000

Gas 는 Gas * GasPrice 만큼의 Wei 비용으로 지불되며, 이는 Turing Complete Machine 인 이더리움이 무한루프와 같은 Attack 으로 부터 자유롭기 위함과 Contract 실행과 Storage 사용에 대한 자원비용 보상을 지불하는 목적이다.

Gas 가 생각보다 많이 나올 수 있는데, Code 실행과  sstore 등 뿐만 아니라, Transaction 의 size 에도 비용이 비례하여 증가하게 되기 때문이며, 최소 Gas Price 보다 크지 않으면 아예 Transaction 이 발생하지 못하는 비극이 발생한다. 결론적으로 돈이 있어야 한다.

▌Storage, Memory, Stack

모든 Contract Account 는 Storage 라고 불리는 Persistent 저장소를 갖고있다. Key-Value 맵 구조로 32바이트 키를 32바이트 값으로 맵핑하도록 되어있다. Contract 는 자기 자신 이외의 Contract 의 Storage 를 읽거나 쓸 수 없다.

Memory 는 Contract 가 Message Call 이 있을 때 마다 최신의 Instance 를 얻을 수 있는 공간으로, Byte 레벨로 읽고 쓸 수 있으나 32바이트 단위 Chunk로 저장된다. 즉, 1 이라는 값을 저장하면 32바이트 (256비트) 공간에 저장된다.

EVM 은 총 1024개의 Instruction Set (OPCODE) 를 담을 수 있는 Stack 을 갖으며, 256비트의 word (값) 을 갖는다. 

이러한 특성으로 Message Call 시에도 몇가지 제약사항들이 있는데, 그중 하나는 32바이트를 넘는 문자열을 하나의 파라미터로 보낼 수는 없다는 것이다. 

▌Message Call

Contract 는 다른 Contract 를 Call 하거나 EOA 로 Ether 를 보낼 수 있으며, 이때 Message Call 을 사용하게된다. 일반 이체 Transaction 과 매우 유사하지만 호출할 Contract 의 주소, Method 의 주소, 함수  파라미터(옵션)를 넣는것이 다르다.

Sub Contract Call 이 실행되어야 하는 경우, 연쇄적인 Message Call 이 Contract 에 의해 일어나게 되며, 이러한 연속된 Contract 실행을 위한 충분한 Gas 를 포함시켜야만 나중에 Contract 실행이 끝까지 될 수 있다. Call 은 최대 1024 Depth 까지 호출될 수 있다.

Message Call 의 결과는 Block 내의 Transaction Receipt 를 참고하여 조회할 수 있다.

Contract 실행 전, EVM 은 Transaction Sender 의 잔고가 충분한지 검토하고 실행하며, 실행 중 Gas 가 모두 소진되었으나 실행이 완료되지 않은 경우에는 그때까지의 Gas 는 지출되고 모든 State 는 Contract 실행 이전 단계로 돌아간다.

▌Delegate Call, Callcode, 라이브러리

Message Call 중에, Delegatecall 이라는 방식이 있는데, 이건 Contract 가 실행될 때 Contract 를 실행시킨 Contract 의 Context 를 사용한다는 것이다. 그리고, Message Sender 와 Value 가 변경되지 않고 Caller Contract 의 것을 그대로 사용한다.

이러한 특성을 활용하여 Library 형태의 Contract 를 만들 수 있다.

▌Create Contract Transaction

Contract 를 Byte Code 로 컴파일 하여 Blockchain 에 Upload 하여야 Contract 를 실행할 수 있다. Contract Create 는 EOA 가 Create 하는것이 일반적이나, Contract 가 다른 Contract 를 Create 할 수도 있다. 

▌Self Destruct

Contract 를 삭제할 수 있는 유일한 방법은 Contract Address 에서 (자신이) SELFDESTRUCT 명령을 수행하는 것이다. 이때 Contract Account 의 Ether 잔고는 지정된 Account 로 이체되고 Contract 의 모든 State 는 소멸된다.

위 기본적인 개념들 말고도 설명해야 하는 것들이 많다. 앞으로 조금씩 써보도록 하고, 예제도 종종 올려보도록 하겠다. 


반응형
블로그 이미지

Good Joon

IT Professionalist Since 1999

,
Genesis 정보까지 모두 준비되었다. 이제 Node 를 실행하면 된다.
그런데 옵션으로 주어야 할게 쫌 있다. 좀 자질구레한것들이지만 Shell Script 로 만들어놓으면 편하기 때문에 MinGW 의 bash shell script 로 만들어놓는다.

▌실행 Script

내가 사용중인 Shell Script 는 다음과 같다. Wallet 의 Master Password 는 1111 이고 JSON-RPC 를 사용하기 위한 Session-Key 로는 0000 을 지정해주었다. 

그리고 연결되는 Node 간의 시간차는 +- 13분 이내 이어야 한다. 시간을 잘 맞추고 시작하자.

[Linux]
./eth --config $ETH_HOME/res/config.json --master 1111 --json-rpc
--json-admin 0000 --admin-via-http --no-bootstrap --listen 30301
--remote "192.168.0.122" --port 30301 --db-path $ETH_HOME/db_goodjoon 
--mining on --address "0047d27a61e384403d875239cbc462896044213e" 
--verbosity 99 --json-rpc-port 8545 --ipc --upnp off  $@

[Windows]
.\eth.exe --config d:\ethereum\res\config.json --master 1111 
--json-rpc --json-rpc-port 8545 --json-admin 0000 --admin-via-http 
--listen 30301 --remote "192.168.0.60" --port 30301 
--db-path d:\ethereum\db_goodjoon --verbosity 9 
--admin-via-http -o full  --upnp off  %*

--admin-via-http 옵션은 1.2.0 에서 새로생긴 옵션인데, JSON-RPC 를 통해 admin 기능을 실행할 수 있게 허용하겠단 이야기이다. 
--no-bootstrap 으로, Peer Server 에 연결하지 않도록 하였으며
--remote 옵션으로 직접 연결할 Node 를 지정해주었다.
--db-path 로 기본 DB PATH 를 사용하지 않고 db_goodjoon 을 사용하도록 설정하였다. 
console 옵션은 어차피 1.2.1 버전의 C++ eth 는 동작하지 않는다. 나중에 process 를 하나 더 띄워서 attach 해서 사용할것이기 때문에 console 옵션은 주지 않았다.

뒤에서 언급하겠지만, 1.2.1 버전 오면서 Windows 의 버그와 Default 값의 동작이 더 심각해졌다. coinbase address 도 기본으로 안넣어주고, JSONRPC 포트도 지정해주지 않으면 -1 값이다. 그러나 아직도 --help 에는 8545 기본값으로 나온다. Geth 의 jsonrpcapi 옵션에서 볼수있었던 JSON RPC admin 기능을 위해 --admin-via-http 옵션도 생겨나고 했는데 그러면서 좀더 꼬이고있기도 하다. (새로 팀장 오고나서 팀을 "REBOOT" 하겠다고 했는데, 좀 잘 되었으면 좋겠다. Go 팀을 좀 봐라 쫌..)

▌실행 확인
(++)Ethereum 이 출력되고 이후 뭔가 좌르륵~ 나오고 있다면 일단 동작하는 것이다. 

Ubuntu에서 실행중인 모습
korean44@ubuntu-svr:~/project/blockchain/webthree-umbrella/build/webthree/eth$ ./ethGoodJoon.sh 
(++)Ethereum
...  00:36:07.628|eth  Reading /home/korean44/.web3/keys/152d7983-ac83-b2f9-159c-18ab7106fd83.json
⧎ ℹ  00:36:07.649|eth  Id: ##e808dba2…...  00:36:07.684|eth  Opened blockchain DB. Latest: #5df28093… (rebuild not needed)...  00:36:07.700|eth  Opened state DB.
⧫ ◎  00:36:07.702|eth  startedWorking()
cpp-ethereum 1.2.1
  By cpp-ethereum contributors, (c) 2013-2016.
  See the README for contributors and credits.
Transaction Signer: XE50000000000000000000000000000000 (00000000-0000-0000-0000-000000000000 - 00000000)
Mining Beneficiary: XE8916H0DGVW3SIMF6X9AWCB8J6ZV5PWE6 (152d7983-ac83-b2f9-159c-18ab7106fd83 - 0047d27a)
Foundation: XE55PXQKKK4B9BYPBGT1XCYW6R5ELFAT6EM (00000000-0000-0000-0000-000000000000 - de0b2956)
  ℹ  00:36:13.611|p2p  UPnP device: http://192.168.0.1:3274/etc/linuxigd/gatedesc.xml [st: urn:schemas-upnp-org:device:InternetGatewayDevice:1 ]
⧎ ℹ  00:36:13.684|p2p  Punched through NAT and mapped local port 30301 onto external port 15725 .
⧎ ℹ  00:36:13.684|p2p  External addr: 211.222.99.134
⧎ ℹ  00:36:13.686|p2p  p2p.started id: ##e808dba2…
 ⚡   00:36:13.695|eth  void dev::p2p::Host::start() 2091 ms
Node ID: enode://e808dba2f0d7eb464656e01be81317386af18b8a0c554c7f9fd78fcd0f1a008a584891aed201018444af50ede46faf6884750baa1f06eca9e9c706d827b931a3@211.222.99.134:15725
JSONRPC Admin Session Key: 0000
⧫ ℹ  00:36:13.738|eth  Mining Beneficiary: @0047d27a
⧫ ◎  00:36:13.739|eth  Rejigging seal engine......  00:36:13.741|eth  Generating seal on #ab31b73b… # 1
  ℹ  00:36:13.743|miner0  Loading full DAG of seedhash: #00000000…
DAG  00:36:19.606|miner0  Generating DAG file. Progress: 0 %
⧫ ◎  00:36:22.718|eth  Since 2016-03-04 15:36:07.686Z (15): 15ticks
DAG  00:36:25.842|miner0  Generating DAG file. Progress: 1 %
DAG  00:36:32.097|miner0  Generating DAG file. Progress: 2 %



아마 Windows 에서는 아예 Console 에서 JavaScript Console 기능이 동작하지 않을 것이다. 버그이다. 이건 Frontier Release 이전부터 계속되어오는 문제이다.
수정하고싶은 생각도 없는듯 하고 암튼 Windows 는 C++ Ethereum의 대상이 아닌게 날이갈수록 확신이 든다.

Miner는 DAG 파일을 만드는데에 상당한 시간이 소요된다. 처음 1회만 실행되므로 참고 기다린다.


▌Console Attach

그래도 JavaScript Console 을 띄워서 보는게 가장 빠른 방법이므로 한번 해보도록 한다.

Linux 에서는 
$ eth --session-key 0000 attach
만으로도 현재 실행중인 eth 에 attach 를 한다. Linux 에서는 console 이 잘 동작하므로 처음 실행할 때 마지막에 console 이라고 치면 되긴 하지만, 로그가 수없이 지나가므로 JavaScript API 를 실행시켜도 결과가 로그에 파묻힌다. 그래서 난 그냥 터미널 하나 더 띄워서 attach 시킨다.

Windows 는 1.2.1 버전 오더니 더 심각한 버그들이 발생한다.
$ eth --session-key 0000 attach --url "http://localhost:8545"
이렇게 url 까지 적어줘야 한다. default 가 먹히질 않는다.

암튼 Console 이 attach 되고나면 ">" 가 보인다. JavaScript Console 이다.

1.2.0 까지만 해도 안그랬던것 같은데, JavaScript API 를 Geth 와 I/F 를 맞추고있는 과정이라서 그런지 web3 만 치면 전체 object 들이 나와야 하지만 에러가 난다. 이건 Linux 나 Windows 나 모두 마찬가지이다.

그래서, web3.eth 객체를 살펴보면, 아래처럼 출력이 될 것이다.
> web3.eth
{
  _requestManager: {
    provider: {
      send: [Function],
      sendAsync: [Function]
    },
    polls: {
    },
    timeout: null,
    send: [Function],
    sendAsync: [Function],
    sendBatch: [Function],
    setProvider: [Function],
    startPolling: [Function],
    stopPolling: [Function],
    reset: [Function],
    poll: [Function]
  },
  getBalance: [Function],
  getStorageAt: [Function],
  getCode: [Function],
  getBlock: [Function],
  getUncle: [Function],
  getCompilers: [Function],
  getBlockTransactionCount: [Function],
  getBlockUncleCount: [Function],
  getTransaction: [Function],
  getTransactionFromBlock: [Function],
  getTransactionReceipt: [Function],
  getTransactionCount: [Function],
  call: [Function],
  estimateGas: [Function],
  sendRawTransaction: [Function],
  sendTransaction: [Function],
  sign: [Function],
  compile: {
    solidity: [Function],
    lll: [Function],
    serpent: [Function]
  },
  submitWork: [Function],
  getWork: [Function],
  coinbase: '0x0047d27a61e384403d875239cbc462896044213e',
  getCoinbase: [Function],
  mining: false,
  getMining: [Function],
  hashrate: 0,
  getHashrate: [Function],
  syncing: false,
  getSyncing: [Function],
  gasPrice: 50000000000',
  getGasPrice: [Function],
  accounts: ['
0x0047d27a61e384403d875239cbc462896044213e'],
  getAccounts: [Function],
  blockNumber: 0,
  getBlockNumber: [Function],
  iban: [Function],
  sendIBANTransaction: [Function],
  defaultBlock: '
latest',
  defaultAccount: undefined,
  contract: [Function],
  filter: [Function],
  namereg: [Function],
  icapNamereg: [Function],
  isSyncing: [Function]
}

▌Block Sync 확인
현재 Difficulty 도 낮춰놓고, 블록 생성 간격도 13분에서 1분으로 맞추도록 조정하였다. 


▌Windows 버전 => Linux / Mac OS X 으로 갈아타자
여태 Windows 버전을 Build 하고 소스 수정하고 해왔다. 그런데 Homestead 겨냥중인 1.2.1 버전 릴리즈 되면서 더이상 Windows 를 안쓰고싶게 만든다.
Frontier (1.0.1) 까지는 그나마 참고 쓸 수 있었으나 1.1.3 버전 부터였나 점점 되야하는 기능들이 Windows 에서 안돌더니 이제 Geth 와 I/F 맞춘다는 명목등을 이유로 작업을 하고있으면서 Windows 는 안그래도 뒷전인데 더 뒷전이 되어버렸다.

Windows 에서 못써먹겠다는 이유는 대략 이러하다.
1.0.1 => Contract 를 Create 하고 배포한 후 JSONRPC 건 JavaScript 건 call 을 2회 하면 그 다음부터는 무조건 0을 리턴한다. 재기동만이 정상화 방법이다. 그래서 Windows 에서는 Mining 시키고 Mac 이나 Linux 에 Contract call 을 해왔다.

1.1.3 => Windows 버전이 다른 Peer 와 Connect 할때 RLPx Handshake 시에 괜히 auth fail 이 난다. 그리고 이 시점 부터 다른 Platform 의 Miner 가 멈추고 아예 동작을 안한다. Windows 를 먼저 띄워놓고 다른 Platform 의 Peer 가 connect 을 하면 괜챦다. 

1.2.0/1.2.1 => Windows 가 붙으면 auth fail 은 또 계속 난다. Miner 죽는 문제는 해결한듯 하다. 그런데 Geth 와 CLI 나 JS Console 이 아직 맞춰지지도 않았고 API 가 동작하지 않는것들도 있다. 

그리고, DAG 도 버그가 있다. 어떤때는 hashrate 가 3,4 H/s 가 나온다. 계속 재실행 하다보면 언젠다 다시 정상 hashrate 가 나온다. 문제다.

위 이유로, 이후 진행은 Linux 로만 한다. Mac OS X 도 잘 지원되는 편이나 블로그 쓰는데 맥북까지 켜서 2대에서 끄적거리기가 싫다 ^^;; 그냥 VirtualBox 로 Linux 2대 켜놓고, shared folder 하나 만들어서 소스는 한군데에서 수정하고 빌드한 후 양쪽 VBox Guest (Ubuntu 15.10) 에서 실행시켜 테스트 하는 방향으로 가겠다. 뭐 필요하면 3대 4대 하거나 Instance 를 더 늘리던가 하겠다.





반응형
블로그 이미지

Good Joon

IT Professionalist Since 1999

,
Ubuntu 를 15.10 으로 업그레이드한 후 Shared Folder 를 설정했는데 먹히질 않는다.
이전 VirtualBox 의 Guest Addition 이 오류가 생긴듯 하다. 그래서 다시 Guest Addition 을 설치해보도록 한다.

1. VirtualBox 에서 GuestAddition CD 를 삽입한 후
$ sudo mount /dev/cdrom /media/cdrom

2. Guest Addition 을 빌드하기 위한 필요 패키지들을 받는다.
※ 설치 전에 apt-get update 는 필수이다
$ sudo apt-get install -y dkms build-essential linux-headers-generic linux-headers-$(uname -r)

3. VBoxLinuxAdditions.run 을 실행한다
$ sudo /media/cdrom/VBoxLinuxAdditions.run

4. Reboot 한다

5. 공유폴더를 Mount 해본다 
$ sudo mount -t vboxsf <공유이름> <마운트할폴더>

잘 되면 그냥 쓰면된다. 그런데 No Such Device 라고 나올 수 있다. 이럴때는

6. vboxadd 설정을 다시 해본다
$ cd /opt/VBoxGuestAdditions-*/init 
$ sudo ./vboxadd setup

7. 다시한번 리부팅 하고 마운트 해보면 잘 될것이다



반응형
블로그 이미지

Good Joon

IT Professionalist Since 1999

,
Account 까지 만들었으므로 이제 Account 의 address 에 이더가 충만한 나만의 genesis 를 만들어서 간단한 Transfer Transaction 을 실행해보겠다.

참고로, 그간 golang 강좌 초반부를 쓰느라 신경을 좀 못쓴 동안에 1.2.0 버전이 바로 어제 릴리즈 되었다 (2016.02.29 에).
git pull 하고 submodule 을 update 한 후 1.2.0 버전으로 소스를 업데이트 하고서 다시 빌드를 하였으며, 이후 설명은 eth 1.2.0 기준으로 해 가도록 하겠다.

1.1.4 버전으로 Private Network 을 구성하고 나서 좀 당황스러운 점들이 있었다. 일단, Node 간에 RLPx handshake 가 auth 를 검증하는 동안 실패하는 상황이 발생하며 Handshake가 실패하면 아예 Miner 가 Mining 을 멈추는 상황으로 가는 버그이다. 이게 수정되었는지 궁굼한데 Release Note 를 보니 Mining 쪽 버그 픽스가 되었다고 하는데 1.2.0 에서 확인해봐야 하겠다.

그리고, Geth 의 Attach 가 eth 에 가능하게 되었다. geth 에는 --session-key 옵션이 없는데 이게 어떻게 동작할라나 모르겠다. 암튼 된다고 하니 이 두녀석들을 갖고 또 테스트 해봐야 하겠다. eth 1.2.0 과 geth 1.3.4 가 거의 동시에 릴리즈 되었으니, geth 설명할 때에도 최신버전 (아예 1.3.4의 코드네임이 Homestead 이다)으로 해야 하겠다.

[goodjoon Debug]$  ./eth --version
eth version 1.2.0
eth network protocol version: 63
Client database version: 12041
Build: Windows/msvc/int/Debug
[goodjoon Debug]$

일단은 Private Network 상에서 PoW 로 Consensus 를 하는, Public 형태와 동일한 Ethereum Local Network 을 구성 해보도록 한다.

▌config.json (genesis.json) 만들기

genesis.json 이 Frontier Release 버전인 eth 1.0.0 과 달라졌다. Geth 와도 호환이 안되는 JSON 포맷으로 바뀌었다 (config.json). 그나마 좀 Simple 한 포맷이었는데, 좀더 복잡하게 바뀌었다.
create-genesis.py 스크립트로 만들어져 나오는 json 은 동작하지 않으므로, 수동으로 작업해보도록 하겠다.

1.2.0 부터는 genesis.json 으로 부르지 않고, config.json 으로 부른다. 옵션도 --config 로 바뀌었다. --genesis 옵션도 코드 내에서는 아직 살아있지만 --help 로 볼때는 나오지 않는다.

우선 내 Account 중에 억만장자를 만들고 싶은 Account 의 Address 를 결정해야 한다.
eth 가 잘 빌드되었다면

$WEBTHREE/build/libethereum/ethkey/Debug
밑에 ethkey.exe 파일이 있다

[goodjoon Debug]$  ethkey.exe list --master 1111
1a7e55b0-3eb7-05ec-248e-8d901e268d76 0096bb98… XE602H4XB00HUY08LU416SCBGO3CS63H66  Default key
6ecc980b-2f99-013d-167e-0ea9caffde4e 007386ab… XE561WBEXUY46M6G05SDKF5P9C334V3JT6  myname
31ae0923-14da-a1a9-85ba-ab9563c5da4b 005bfaf9… XE241IE5KGWVRVYD6B5H8ZHMXNMP57Q4KM  joooooon
[goodjoon Debug]$
위 처럼 Wallet 의 Key 들이 보인다면 이중에서 사용할 Key 를 선택한다. 난 "Default key" 로 자동생성되었던 Account 를 선택한다.

Genesis 에는 Account 의 Balance 를 지정해주려면 Address 가 필요한데, 위 ethkey list 명령으로 출력된 결과에는 address 가 FULL 로 표시되지 않는다. ethkey 를 다시 사용하여 Full Address 를 표시해본다.
[goodjoon Debug]$  ethkey.exe inspect "Default key" --master 1111
Default key (0096bb98…)
  ICAP: XE602H4XB00HUY08LU416SCBGO3CS63H66
  Raw hex: 0096bb9802b14f72ba4cdbd105127fe57a1dafae
[goodjoon Debug]$

옵션에 계속 --master 1111 을 넣는 이유는, 현재 MinGW/MSYS 를 사용하고있는데 eth 가 이 환경에서는 key store 의 master 패스워드 입력하라는 Prompt 가 나올 때 Exception 이 발생하므로 master 패스워드를 바로 지정해주어야 한다. (Windows Command Prompt 환경에서는 문제가 없다)

위 Raw hex : 부분이 사용할 Account 의 address 이다.

이렇게 Linux 나 Mac 버전 또는 다른 Windows 에서도 Wallet 을 만들고 (keys.info, keys.salt), Key 를 생성한다 (Wallet (keys.info) 에 key 추가, .web3/keys 디렉토리에 추가된 key 의 정보(json) 추가).
Account 의 Address 를 모두 파악했다면, 이제 Genesis 정보를 만들어준다

{
    "sealEngine""Ethash",
    "params": {
        "accountStartNonce""0x00",
        "maximumExtraDataSize""0xFF",
        "tieBreakingGas"false,
        "minGasLimit""0x1388",
        "gasLimitBoundDivisor""0x0400",
        "minimumDifficulty""0x020000",
        "difficultyBoundDivisor""0x0800",
        "durationLimit""0x0d",
        "blockReward""0x4563918244F40000",
        "registrar" : "",
        "networkID" : "0xA1"
    },
    "genesis": {
        "nonce""0x0000000000000000",
        "difficulty""0x020000",
        "mixHash""0x0000000000000000000000000000000000000000000000000000000000000000",
        "author""0x0000000000000000000000000000000000000000",
        "timestamp""0x00",
        "parentHash""0x0000000000000000000000000000000000000000000000000000000000000000",
        "extraData""0x",
        "gasLimit""0x1388"
    },
    "accounts": {
        "0096bb9802b14f72ba4cdbd105127fe57a1dafae": { "wei""10000000000000000000000000000000000"},
        "0047d27a61e384403d875239cbc462896044213e": { "wei""20000000000000000000000000000000000"}
    }
}

위 처럼 genesis 정보를 만들어 준다. 가장 아래의 "accounts" 내의 정보가 주소와 Balance 이다.

아래는 "파악된" json 파라미터 정보들이다. Ethereum 의 가장 큰 문제가 바로 "체계 없는 문서화" 라고 생각한다. 어떤것은 Github Wiki 에 있고 어떤건 Gitbook 에, 또 어떤건 readthedocs 에.. 또 어떤건 각 Repository 의 wiki 에.. 또 어떤건 Repository 내의 프로젝트에 있는 .md 파일에.. 뭐 난리다.. 그래서 더 접근하기가 쉽지 않다.

위 config.json 의 경우도 제대로 된 문서 하나를 찾지 못했다. 개념적인 부분과 소스코드를 분석했던 기억을 더듬어 다시 써보니 혹시 틀린 부분이 있다면 그저 그러려니 하자.

[SealEngine]
Block 생성을 위한 Consensus 메커니즘을 어떤걸 사용할것인지를 지정한다.
현재 SealEngineBase Class 를 상속받는 Class 들에는 Ethash, BasicAuthority, NoProof 가 있다. 

Ethash 는 DAG 나 General Hashing 을 통해 PoW 를 하는 엔진이고, BasicAuthority 는 PoA 를 위해 실험적으로 Ethereum 에서 만들어놓은 Consensus 메커니즘으로, 현재 flu core 를 통해 사용할 수도 있으며, web3 에 통합되어있다. NoProof 는 Consensus 를 위한 증명작업을 하지 않는 Sealer 이다.

[Params]
"AccountStartNonce" : Account 의 최초 시작 Nonce 값을 지정한다. 기본적으로는 당연히 0 부터 시작하면 된다.
"maximumExtraDataSize" : 블럭의 Extra Data 의 최대 크기를 지정한다. 
"tieBreakingGas" : 
"minGasLimit" : 블럭의 최소 Gas 제한량이다. Block 의 GasLimit 값은 이 minGasLimit 보다 커야한다.
"gasLimitBoundDivisor" : 현재 블럭의 GasLimit 값은 parent block 의 gaslimit 대비 +- (parent block 의 GasLimit / gasLimitBoundDivisor)이내에 있어야 한다.
"minimumDifficulty" : 블럭의 최소 Difficulty 이다. 아무리 시간이 오래걸려도 PoW 는 이 Difficulty 이상을 유지해야 한다.
"difficultyBoundDivisor" : Frontier 까지 유효하며, 이전 Block 생성시간 대비 현재 Block 생성 시간이 durationLimit 보다 작으면 parent 의 difficulty 에 parent 의 difficulty / difficultyBoundDivisor 값을 더하고, 크면 같은값을 빼서 블럭 생성 시간을 조정한다
"blockReward" : Miner 의 Codebase 에 챙겨줄 reward wei 이다
"registrar" : Olympic 부터 Frontier, Homestead 등에서 기본적으로 존재하는 registrar Smart Contract 의 Address 이다. Name Register 서비스를 해주는 Contract 로 보면 된다.
"networkID" : 피어간의 통신 시 논리적으로 네트웍을 구분짓는 ID 이다. 피어간에 통신을 위해서는 이 networkID 값이 같아야 한다. Olympic 은 0, Frontier 는 1, Morden Testnet 은 2를 쓴다.

[Genesis]
"nonce" : 블럭의 nonce 값
"difficulty" : Block 의 Difficulty
"mixhash" : Block 의 hash 값
"author" : Block Author 정보, 필요하면 넣고..
"timestamp" : 블럭 생성 시각
"parentHash" : Genesis 이므로 0
"extraData" : 넣고싶은 Extra Data
"gasLimit" : Block 의 Gas Limit

[Accounts]
"<Account Address>": {"wei":<Balance>} 와 같은 형식은 UCA/UOA Account 의 Balance 를 적는 형식이고,
"0000000000000000000000000000000000000001": { "wei": "1", "precompiled": { "name": "ecrecover", "linear": { "base": 3000, "word": 0 } } } 와 같은 형식은 Precompiled Contract 를 표시하는 형식이다. Precompiled Contract 사용에 대해서는 향후에 알아보자.


이렇게 하면 일단 Private Network 에서 동작하는 Ethereum 을 위한 기본적인 Genesis 정보를 정의할 수 있다. 원래는 Genesis 만 지정하였는데, C++ Ethereum 은 좀더 유연성을 부여하기 위해 
  1. sealEngine
  2. options
  3. params
  4. genesis
  5. accounts
  6. network
로 구성된 config 정보를 입력할 수 있도록 하였다. 일단 목적은 private blockchain 을 염두에 둔것 같다.

다음은 이 Genesis 정보 기반으로 두 노드간에 실제적으로 통신을 해보자 (졸려졸려..)

반응형
블로그 이미지

Good Joon

IT Professionalist Since 1999

,
조건, 반복, 분기문에 대해 알아본다. Java 나 C/C++ 대비 다른 부분만 설명하도록 하겠다. 일반적인 부분은 느낌대로 쓰면 된다. 뭔가 잘못되면 문법 오류가 바로 뜰테니 몇번 만나면 익숙해진다.

▌if

if <조건> { 처럼 사용, 중괄호 “{“ 가 반드시 inline 으로 와야한다. 중괄호를 생략해도 에러난다.
조건문에는 괄호 () 가 오지 않는다.
if err != nil {
   utils.Fatalf("Unlock error: %v"err)
}

if 의 block 이 끝나는 중괄호와 같은 라인에 else { 가 와야 한다
if script := ctx.GlobalString(utils.ExecFlag.Name)script != "" {
   repl.batch(script)
else {
   repl.welcome()
   repl.interactive()
}

} else if <조건> { 과 같이 else if 문도 inline 이어야 한다
if entry := GetHeader(dbheader.Hash())entry == nil {
   t.Fatalf("Stored header not found")
else if entry.Hash() != header.Hash() {
   t.Fatalf("Retrieved header mismatch: have %v, want %v"entryheader)
}

if <조건문> 내에서 함수 실행. 세미콜론 ; 으로 구분하여 if 구문 안에서 하고자 하는 작업을 선행할 수 있다. 
if berr := ioutil.ReadFile("./hello.txt")err == nil {
fmt.Printf("%s"b)
}

위에서 ioutil 패키지는 []byte, error 를 동시리턴하도록 되어있다. 나중에 다룰 내용이지만 이 동시리턴 된 값중 error 값을 조사하여 error 가 있으면 if block 을 실행하는 예제이다.

위에서 if 조건문에서 사용한 b, err 변수는 if 문 바깥에서는 사용할 수 없다


▌for

Go 에는 do…while 이나 while 이 없고 for 반복문만 제공됨
if 문 처럼 for <조건문> 에서도 괄호 () 를 사용하지 않으며 { 는 inline 으로 붙어야 함

for 문에 <조건문> 을 주지 않으면 무한루프를 돈다
func main() {
   for {
      fmt.Println("무한 루프")
   }
}


▌루프 Label 과 break, continue

Go 에서는 break 와 continue 시에 Loop Label 을 지원함
Label 은 for 문 바로 윗줄에 쓰여야 함
func main() {
   OutterLoop:
   for i := 0i < 10i++ {
      for j := 0j < 10j++ {
         if i == && j == {
            break OutterLoop
         } else if j == {
            continue OutterLoop
         }

         fmt.Println(ij)
      }
   }
}
결과
0 0
0 1
0 2
1 0
1 1
1 2
2 0
2 1

▌switch

Go 의 switch 문에서는 switch / case 만 쓰면 되며, 매 case 마다 break 를 사용할 필요가 없다.
조건에 만족하지 않으면 default: 라벨로 간다
case 에 “문자열” 을 쓸 수 있다.
func main() {
   str := "굿준"
   switch str {
   case "굿준":
      fmt.Println(str"'s Blog")
   default:
      fmt.Println("DEFAULT")
   }
}

그러나 여전히 break 문을 case 를 빠져나오기 위해 사용할 수 있다. (if 문 안에서..)

case 안에서 fallthrough 키워드를 사용하면 java 나 C/C++ 에서 break 문이 없는것과 같이 다음 case 로 넘어가는 효과를 낼 수 있다
func main() {
   str := "굿준"
   switch str {
   case "굿준":
      fmt.Print(str)
      fallthrough // 다음 case 로 흘러간다
   case "블로그":
      fmt.Println("'s 블로그")
   default:
      fmt.Println("DEFAULT")
   }
}

결과
굿준's 블로그

case 안에서 여러 조건을 함께 처리할 수 있다. case 의 값을 콤마 , 로 분리하면 된다.
func main() {
   str := "굿준"
   switch str {
   case "굿준""개발자":
      fmt.Print(str)
      fallthrough // 다음 case 로 흘러간다
   case "블로그""트위터""페이스북":
      fmt.Println("'s 블로그")
   default:
      fmt.Println("DEFAULT")
   }
}

switch 안에서 조건식도 사용 가능하다. 조건문 안에서 함수를 호출하고 case 로 분기하는 예제이다.
이때 조건문 안에서의 함수호출 다음에는 반드시 세미콜론 ; 이 와야한다.
func main() {
   val1 := 100

   rand.Seed(time.Now().UnixNano())
   switch swVal := val1 + rand.Intn(10)// 조건문 안에서의 함수호출은 ; 필수!
   case swVal > 100 && swVal < 200:
      fmt.Println("100 보다 크고 200보다 작음")
   case swVal >= 200 :
      fmt.Println("200 보다 크거나 같음")
      fallthrough
   default:
      fmt.Println("많이 큼")
   }
}

그리고, 함수가 호출된 경우에는 case 문의 값은 <조건식> 만 가능하다.

반응형

'Software Development > Go (golang)' 카테고리의 다른 글

3.3 Go - Package  (4) 2016.03.01
3.2 Go - 기본 문법 및 Type  (488) 2016.02.29
3.1 Go - Hello World  (5) 2016.02.29
2. Go - 개발환경 구성  (524) 2016.02.28
1.3 Go 언어란? - 다른 언어와 비교  (1159) 2016.02.28
블로그 이미지

Good Joon

IT Professionalist Since 1999

,
Java 와 같이 import 키워드로 Package 를 사용한다

하나의 패키지를 import 할때는 아래와 같이 import “<패키지명>” 을 사용한다
import "fmt"

여러 패키지를 import 할 때에는 import 를 여러번 쓸 수도 있지만, import ( ) 를 사용하면 편하다
import (
   "fmt"
   "io/ioutil"
   "os"
   "path/filepath"
   "runtime"
   "strconv"
   "strings"
   "time"

   "github.com/codegangsta/cli"
)

import 한 패키지를 패키지 이름 없이 전역으로 사용하려면 
import "fmt"

func main() {
   Println(runtime.NumCPU())
}
처럼 해주면 되나, 추천하지 않음

위 전역으로 사용하는 예제를 봐서 감이 오겠지만, import 키워드 다음에는 alias name 이 올 수 있다.
import out "fmt"

func main() {
   out.Println(runtime.NumCPU())
}

위 처럼 out 을 “fmt” 의 Alias Name 으로 지정하여 사용할 수 있다.
이전에도 잠깐 이야기 했지만, _ 를 alias name 으로 사용하면 사용하지 않은 Package 에러가 나지 않는다

반응형

'Software Development > Go (golang)' 카테고리의 다른 글

3.4 Go - 제어문  (504) 2016.03.01
3.2 Go - 기본 문법 및 Type  (488) 2016.02.29
3.1 Go - Hello World  (5) 2016.02.29
2. Go - 개발환경 구성  (524) 2016.02.28
1.3 Go 언어란? - 다른 언어와 비교  (1159) 2016.02.28
블로그 이미지

Good Joon

IT Professionalist Since 1999

,
기본 문법 요소들에 대해 알아보겠다. 이전에도 이야기했듯이 Go 는 코드 포맷에 대해 상당히 신경을 쓴다. 

1. 중괄호 { }, 소괄호 (), 세미콜론 ; 의 사용이 약간 다르다 (아래 참조)
2. Statement 들여쓰기 공백(space)은 탭으로 대체된다
3. 모든 코드는 UTF-8 기반 Unicode 이다.


▌세미콜론 ;

Go 는 ; 이 옵션이다. 있어도 되고 없어도 된다. 행여 쓰더라도 gofmt 를 돌리면 ; 은 삭제해버린다.
그러나 코드 작성 중에는 

fmt.Println("백 ");fmt.Println("입니다")
처럼 쓸 수 있다. 그러나 gofmt 를 돌리면 
fmt.Println("백 ")
fmt.Println("입니다")
이렇게 세미콜론을 없애고 두줄로 바뀐다.

▌중괄호 {}

중괄호는 반드시 inline 으로 시작하여야 한다. 다음줄에서 시작하면 오류다. 또한 if 문의 경우 else 문은 반드시 닫는 중괄호 } 뒤에 inline 으로 와야 한다.

package main

import "fmt"

func main(){
       some := 100
       if (some == 100) {
              fmt.Println("백 ");fmt.Println("입니다")
       } else {
              fmt.Println("뭥미")
       }

       for i:=i<10 i++ {
              fmt.Println("==> "i)
       }
}

▌소괄호 ()

소괄호는 함수가 아닌 경우 사용하지 않는다. 사용해도 무방하지만 gofmt 를 돌리면 이 역시 날아간다
위 코드를 gofmt 를 적용한 결과는

package main

import "fmt"

func main() {
       some := 100
       if some == 100 {
              fmt.Println("백 ")
              fmt.Println("입니다")
       } else {
              fmt.Println("뭥미")
       }

       for i := 0i < 10i++ {
              fmt.Println("==> "i)
       }
}

어찌 보면 이게 더 코드 가독성을 떨어뜨리는것 같기도 하지만, 함수 호출이 눈에 좀더 들어오기는 한다.

▌변수 선언

변수 선언은 아래 방법이 있다

var <변수> = <값> 를 사용한 자동 타입 결정 선언
var <변수> <타입> 을 사용한 명시적 타입 변수 선언
var <변수> [타입] [= <값>] 을 사용한 명시적 타입의 변수 선언 및 할당
<변수> := <값> 을 사용한 자동 변수 선언 및 값에 의한 타입 결정 후 할당

여러 변수 선언 방법은 
var <변수>[,<변수>,…] = <값> [,<값>,…]
var <변수>[,<변수>,…] <타입>
var (
     <위 여러 변수 선언 방법에서 var 키워드를 제외한 부분과 동일>
     [, <변수선언>]
     […]
)

/**
변수 선언 예제
*/
package main

import "fmt"

func main() {
       // 변수 선언 예제
       var val1 = "1"
       var val2 int
       var val3 int 3
       val4 := 0x04

       // 여러 변수 동시 선언
       var val5val6 = "5"6
       var val7val8 int
       var val9val10 int 910
       val11val12 := 11"12"

       var (
              val13val14 int 1314
              val15val16 = "15"16
       )
}

그런데 위 코드를 빌드하면 에러가 발생한다

1. “fmt” 를 import 하였으나 사용하지 않았음
2. 모든 변수를 선언하였으나 사용하지 않았음

다른 언어에서는 그냥 Warning 으로 지나칠것을 Go 에서는 에러로 간주한다. 이때에는 _ 를 사용하여 이러한 에러를 내지 않도록 할 수 있다.

import 문
import "fmt"

사용하지 않은 변수 에러 회피
var val1 = "1"
_ = val1

위 처럼 _ 를 사용하여 에러를 회피 가능하나 좋은 방법은 아니다.

▌숫자형 데이터 타입

uint8       the set of all unsigned  8-bit integers (to 255)
uint16      the set of all unsigned 16-bit integers (to 65535)
uint32      the set of all unsigned 32-bit integers (to 4294967295)
uint64      the set of all unsigned 64-bit integers (to 18446744073709551615)

int8        the set of all signed  8-bit integers (-128 to 127)
int16       the set of all signed 16-bit integers (-32768 to 32767)
int32       the set of all signed 32-bit integers (-2147483648 to 2147483647)
int64       the set of all signed 64-bit integers (-9223372036854775808 to 9223372036854775807)

float32     the set of all IEEE-754 32-bit floating-point numbers
float64     the set of all IEEE-754 64-bit floating-point numbers

complex64   the set of all complex numbers with float32 real and imaginary parts
complex128  the set of all complex numbers with float64 real and imaginary parts

byte        alias for uint8
rune        alias for int32


32bit 플랫폼은 int, uint = int32, uint32
64bit 플랫폼은 int, uint = int64, uint64 이다
float 타입은 플랫폼에 상관없이 32 나 64 를 직접 지정해줘야 한다

생소한 타입은 float64, complex64, complex128, rune 정도이다.
float64 : 64bit 의 float 이다
complex64 : float32 의 실수 + 32비트 허수부로 구성된 복소수
complex128 : float64 의 실수 + 64비트 허수부로 구성된 복소수


▌정수

정수는 크게 다를게 없으므로 Pass

▌실수

다른 언어와 마찬가지로 부동 소수점 방식으로 저장됨. 
지수는 e, E 로 표기하며 + 또는 - 이후에 소수점 위치를 나타냄

var val1 float64 0.1
var val2 float64 .2
var val3 float64 3.3123e3
var val4 float64 4.4123e+5
var val5 float64 5.5123e-5

val3 => 3312.3
val4 => 441230
val5 => 0.00005123

실수 연산의 오차율
실수 연산 시 Go 언어의 실수 오차범위는 1e-14 임. 값 비교 시 실수 값의 실수부 차를 제외한 값의 차이가 1e-14 이내이면 같은 실수값으로 보아야 하며, 비교시에는 math 패키지의 abs() 를 통해 비교해야 부호에 의한 오판 없음

▌복소수 (Complex)

실수부 + 허수부 로 표현되는 복소수. 고정소수점 과 부동소수점 으로 표현함.
복소수를 변수에 저장할때에는 실수부 또는 허수부 생략 가능함.

일단, 복소수의 수학적 개념을 보면

머리가 아플수도 있지만 허수 계산이 필요한 경우 이 complex 타입을 사용한다.


complex 타입을 위한 함수
real() : complex 에서 실수부 가져옴
imag() : complex 에서 허수부 가져옴

표현 방법:
var <변수> complex32 = <실수부> + <허수부>i 또는 complex(<실수부>, <허수부>) 로 표현

var val0 complex128 1.123e-4 2.234e+3i // 실수와 허수 모두 입력 
var val1 complex128 2.234e+3i
var val2 complex128 2.234e+3i // 허수부만 입력
var val3 complex128 complex(1.123e-42.234e+3i// complex() 로 입력

var rVal1 float64 real(val1) // 실수부
var iVal1 float64 imag(val1) // 허수부

위 처럼 표현 가능함

▌rune

UTF-8 character 를 저장하기 위한 타입. 값은 char 처럼 ‘ ‘ 로 묶어줌.

var val1 rune '굿'
var val2 = '준'
var val3 rune '\uAD7F'
val4 := '\uC900'

fmt.Println(val1" "val2)
fmt.Println(val3" "val4)
fmt.Printf("%c%c"val3val4)

실행결과
44415   51456
44415   51456
굿준

▌형변환

Go 는 암시적(자동) 형변환을 제공하지 않는다. 명시적인 형변환은 <타입>(<값>) 형태로 하면 된다.

아래 코드는 오류가 발생한다.
package main

import "fmt"

func main() {
   var val1 int64 1000000
   var val2 float32 1.234e-3

   var res = val1 + val2

   fmt.Println(res)
}

위 코드를
var res = float32(val1) + val2
처럼 수정해야 에러가 나지 않는다.

정수와 소수의 최소/최대 크기는 math 패키지의 정의로 확인할 수 있다
// int
fmt.Println(math.MinInt16)
fmt.Println(math.MaxInt16)
fmt.Println(math.MinInt32)
fmt.Println(math.MaxInt32)
fmt.Println(math.MinInt64)
fmt.Println(math.MaxInt64)

// float
fmt.Println(math.MaxFloat32)
fmt.Println(math.MaxFloat64)

결과는
-32768
32767
-2147483648
2147483647
-9223372036854775808
9223372036854775807
3.4028234663852886e+38
1.7976931348623157e+308

이외에, 변수의 Size 를 알아보려면 unsafe 패키지의 Sizeof() 를 사용하면 된다.

▌문자열

string 타입을 사용한다. + 연산자 사용 가능, == 연산자로 문자열 비교
len() 으로 전체 size 가져옴. utf8 패키지로 utf8 문자열의 카운팅 등

package main

import (
   "fmt"
   "unicode/utf8"
)

func main() {
   var str1 string "goodjoon.tistory.com "
   var str2 string `굿준 블로그`
   var str3 string `
어서 오세요
환영합니다` // 여러줄을 사용

   fmt.Println(str1" - "str2str3)
   fmt.Println("len str1 : "len(str1)) // len() 을 사용하면 문자열 전체 바이트 수 가져옴
   fmt.Println("len str2 : "len(str2))
   // utf8 패키지 사용하여 utf8 문자열의 문자 수 카운트
   fmt.Println("RuneCountInString(str1) : "utf8.RuneCountInString(str1))
   fmt.Println("RuneCountInString(str2) : "utf8.RuneCountInString(str2))

   fmt.Println(" + : "str1 + " - " + str2) // + 연산자
   fmt.Println(" == "str2 == "굿준 블로그"" / "str1 == str2) // Go 는 == 로 문자열 비교함
   fmt.Println("str1[1]"str1[1]) // 배열로 문자열 []byte 를 접근함. 수정은 불가함 read-only
   fmt.Println("str2[1]"str2[1])
   for i:=i<len(str2) i++ {
      fmt.Printf("%x|",str2[i])
   }
}

결과
/usr/local/go/bin/go run /Users/korean44/src/go_workspace/src/Grammar1/string_type.go
goodjoon.tistory.com   -  굿준 블로그 
어서 오세요
환영합니다
len str1 :  21
len str2 :  16
RuneCountInString(str1) :  21
RuneCountInString(str2) :  6
 + :  goodjoon.tistory.com  - 굿준 블로그
 ==  true  /  false
str1[1] 111
str2[1] 181
ea|b5|bf|ec|a4|80|20|eb|b8|94|eb|a1|9c|ea|b7|b8|


▌상수 및 Enumeration

C/C++ 처럼 const 로 상수 만듦. 
const 로 선언하는 문자열은 var 키워드를 붙이지 않음.
iota 키워드를 사용하면 const 선언 내에서 1씩 증가하는 수가 생성됨
const 변수는 선언하고 사용하지 않아도 에러 나지 않음

package main

import "fmt"

const CONST_NUM 100
const CONST_STR string "굿준"
const (
   RED int 0
   GREEN int 1
   BLUE int 2
)
const (
   ZERO iota       // 0 부터 시작하며 하나씩 증가
   ONE
   TWO
   THREE
   FOUR
)
const (
   HANA 0
   DUL
   SET
)

func main() {
   fmt.Println(CONST_STR)
   fmt.Println(GREEN)
   fmt.Println(THREE)
   fmt.Println(DUL)
}



▌연산자

나머지는 다른 언어들과 동일하며, 몇가지 연산자만 추가설명

&^ : AND 연산 값을 NOT 연산 함 

func main() {
   var val1 byte 0x70 // 0111 0000
   var val2 byte 0x11 // 0001 0001
   var val3,val4 = 0x700x11

   fmt.Println("==== Bit Operator ====")
   fmt.Printf("%08b\n"val1 ^ val2) // XOR 연산
   fmt.Printf("%08b\n"^val1)
   // &^ : 좌항과 XOR 연산 값을 다시 좌항과 AND 연산 함 (좌항 값의 Bit Clear)
   fmt.Printf("%08b\n"val1 &^ val2)
   fmt.Printf("%08b\n"(val1 ^ val2) & val1) // &^ 과 같음
   val3 &^= val4
   fmt.Printf("%08b\n"val3)

   // Pointer 관련 연산자
   var val5 = 10
   var ref1 = &val5

   fmt.Println("==== Pointer ====")
   fmt.Printf("%d\n",*ref1)
   fmt.Printf("val5's address : %x / ref1's value : %x"&val5ref1)
   
}

결과
==== Bit Operator ====
01100001
10001111
01100000
01100000
01100000
==== Pointer ====
10
val5's address : c82000a2a8 / ref1's value : c82000a2a8



반응형

'Software Development > Go (golang)' 카테고리의 다른 글

3.4 Go - 제어문  (504) 2016.03.01
3.3 Go - Package  (4) 2016.03.01
3.1 Go - Hello World  (5) 2016.02.29
2. Go - 개발환경 구성  (524) 2016.02.28
1.3 Go 언어란? - 다른 언어와 비교  (1159) 2016.02.28
블로그 이미지

Good Joon

IT Professionalist Since 1999

,

GO 언어 익히기

Go 언어는 Strong Type 의 언어이며 문법이 매우 간결한 편이다. 또한 gofmt 와 같은 유틸리티가 기본적으로 내장되어 있어 코드를 일관성 있는 포맷으로 유지시키도록 되어있다.

LiteIDE 의 경우, 심지어 작성중인 코드를 ctrl+S 로 저장함과 동시에 gofmt 가 동작하여 코드를 일관성있는 포맷으로 변경시켜준다. 아키텍트였던 필자에게는 Java 개발 시 PMD 와 Eclipse 의 Formatter 기능을 합쳐놓은듯한 기분이 든다. 

이 강좌는 적어도 한두개 이상의 프로그래밍 언어를 할 줄 아는 사람을 대상으로 하기 때문에 가급적 연산자나 타입의 “의미”에 대해서는 길게 언급하지 않으며 Java, C/C++, JavaScript 와 비교해볼 때 새롭거나 다른 개념이 적용된 부분에 대해 주로 이야기 하도록 하겠다.


Hello World

Fortran, COBOL, Shell Script 까지 포함하여 이 Hello World 를 찍어볼 언어가 몇가지나 되는지 기억도 안날정도이다. 못해도 20여개 이상의 언어로 Hello World 를 찍어본듯하다.
이번에도 또 찍어보자. Hello World 를 찍고 Go 언어에 대해 설명을 들어가보도록 하겠다.

일반 모든 진행은 LiteIDE 를 사용하여 해 나아가므로 화면캡춰를 자주 사용하더라도 양해 부탁한다.

LiteIDE 를 실행한 후 제일 먼저 해주어야 할 것은 바로 GOPATH 를 LiteIDE 에 추가해주는 것이다.
(Windows 버전은 아직 확인을 안해보았지만 Mac OS X 버전의 경우, .app 이 사용하는 환경변수와 Terminal 에서 사용하는 Shell 의 환경변수가 공용하지 않기 때문에 Mac 에서는 GOPATH 를 수동으로 입력해주어야 한다)


메뉴에서 “Manage GOPATH…” 를 누르면 다이얼로그가 뜨는데, 하단부의 “custom Directories” 부분에 원하는 GOPATH 를 추가해준다

GOPATH 가 추가된 모습이다.

File>New… 메뉴를 누르면 New Project or File 다이얼로그가 보인다.
GOPATH: 리스트에서 위에서 추가한 GOPATH 를 “더블클릭” 한다. 그러면 하단의 Location: 부분의 경로가 업데이트 된다.

Template: 에서 Go Source File 을 선택하고, Name 에 HelloWorld 를 입력한다.
Location: 을 $GOPATH/src/HelloWorld 로 입력하고 OK 를 누른다


HelloWorld.go 파일이 자동으로 생성되었지만 좌측 상단의 “1:Folders” 뷰에 아무것도 보이지 않는다.
나중에 설명하겠지만 일단 “Folders” 로 선택되어있는 콤보박스를 눌러 “File System” 으로 다시 선택한다. 그러면 HelloWorld.go 가 보일것이다.


아래는 기본으로 생성된 HelloWorld.go 파일의 코드이다.
// HelloWorld
package main

import (
       "fmt"
)

func main() {
       fmt.Println("Hello World!")
}

package 는 현재 go 파일이 속하는 패키지 이름을 적는다. main 패키지는 특수한 패키지로, 프로그램의 Entry Point 가 되는 main() 함수를 갖는 Entry 패키지임을 지정하는 것이다.

import "fmt" 는 fmt 패키지를 import 한다는 의미이다.
    fmt 패키지는 Formatted I/O 패키지로, C 의 printf 나 println 과 유사한 함수들을 포함한다.

func 함수 선언자이다

CMD+B 를 눌러서 Build 를 해보면, File System Window 에 파일이 하나 추가된것을 볼 수 있다

소스파일이 컴파일되고 실행파일이 빌드되었다. Mac OS X 기준으로 2.1MB 용량의 파일이 생겼다. 
Go 는 VM 자체를 개별 실행파일에 포함하도록 되어있어서 기본적인 Footprint 사이즈가 이정도 나오게 된다.

CMD+R 로 실행을 시켜본다.

하단의 Output Window 에 Hello World! 가 출력됨을 볼 수 있다.


반응형

'Software Development > Go (golang)' 카테고리의 다른 글

3.3 Go - Package  (4) 2016.03.01
3.2 Go - 기본 문법 및 Type  (488) 2016.02.29
2. Go - 개발환경 구성  (524) 2016.02.28
1.3 Go 언어란? - 다른 언어와 비교  (1159) 2016.02.28
1.2 Go 언어란? - 주요 특징  (845) 2016.02.28
블로그 이미지

Good Joon

IT Professionalist Since 1999

,
Go 개발환경 설치는 어렵지 않다. 

우선 Go SDK 를 다운로드 받고, IDE 만 준비되면 된다.

Go 설치
공식 홈페이지인 golang.org 사이트에 가서 자신의 OS 에 맞는 GO SDK 를 다운로드 하고 설치한다.


설치가 완료되면 Terminal (Windows 는 Command Prompt)에서 go version 으로 설치가 잘 되었는지 확인한다.

버전이 보이면 잘 설치된것이다.


▌Lite IDE 설치
이번에는 LiteIDE 를 설치해본다. Source 로 빌드할 수도 있지만 소스를 수정할 일이 없으므로 바이너리를 다운로드 받아 실행해본다.
1. X28 버전 다운로드
2. /usr/local 밑에 압축 풀기
3. LiteIDE.app 실행


▌Workspace 디렉토리 구조
Go 언어는 언어 자체적인 패키지 관리를 한다고 앞서 이야기 하였다. 이런 관계로 Go 는 특정한 구조의 디렉토리와 이러한 디렉토리의 위치를 알기 위한 환경변수 설정이 필요하다.

원하는 Workspace 디렉토리를 결정하고 만든다. 나는 /Users/korean44/src/go_workspace 를 Workspace 로 결정했다.

Workspace 디렉토리는 GOPATH 라는 이름의 환경변수로 정의되어 있어야 go 언어가 이후 개발할 프로젝트에서 import 를 통해 지정한 패키지가 없을 때 GOPATH 에서 찾거나 없는 패키지를 다운로드 받아 $GOPATH/pkg 디렉토리에 저장한다

GOPATH 디렉토리의 구조는 아래와 같다.

우리가 myproject 를 만든다면 위 처럼 GOPATH(Workspace)/src 밑에 프로젝트를 만들면 된다.
GOPATH 를 Workspace 로 볼 수도 있지만, 단일 Project 를 GOPATH 로 잡을 수도 있다. 그러면 해당 프로젝트의 구조가 저렇게 bin, pkg, src 처럼 정형화 되어 깔끔해질 수도 있다.

▌환경변수

위에서 GOPATH 환경변수가 필요하다는 것을 알았으므로 일단 .profile 이나 windows 의 경우 환경변수를 편집하여 GOPATH 변수를 추가해준다.

go 와 관련된 환경변수는 go env 를 실행하면 출력된다
macbook-joon:go_workspace korean44$ go env
GOARCH="amd64"
GOBIN=""
GOEXE=""
GOHOSTARCH="amd64"
GOHOSTOS="darwin"
GOOS="darwin"
GOPATH="/Users/korean44/src/go_workspace"
GORACE=""
GOROOT="/usr/local/go"
GOTOOLDIR="/usr/local/go/pkg/tool/darwin_amd64"
GO15VENDOREXPERIMENT="1"
CC="clang"
GOGCCFLAGS="-fPIC -m64 -pthread -fno-caret-diagnostics 
            -Qunused-arguments -fmessage-length=0 -fno-common"

CXX="clang++"
CGO_ENABLED="1"
macbook-joon:go_workspace korean44$ 

이중에
$GOROOT : go 가 설치된 경로
$GOPATH : 위에서 설명한 GOPATH. Workspace 로 이해가 편하도록 설명하였으나 사실은 Dependency 관리를 위해 참조할 pkg, src 디렉토리와 go install 시에 실행파일을 위치시킬 bin 디렉토리를 포함하는 지정 구조의 디렉토리 루트가 필요해서 지정하는 것이다.

반응형
블로그 이미지

Good Joon

IT Professionalist Since 1999

,
Go 로 응용프로그램을 개발하기 위해서는 일단 Go SDK 를 설치하여야 한다.
그후 개발자라면 고민하게 될 것은 바로 IDE 이다. 

Go SDK 와 텍스트 편집기만 있으면 개발 가능하다고 Golang 사이트에서는 이야기 하지만 사실 어떤 언어이건 텍스트 에디터만 있으면 개발되지 않는 언어가 어디있겠는가? 하지만 디버깅을 하고 파라미터 정보를 보여주고 Definition 이나 Reference 를 보여주는 등의 기본적인 개발지원 기능이 있는 IDE 가 없다면 어떠한 개발자라도 왠만한 이유 아니면 그 언어로 개발하려고 하지 않을것이다.

Go 언어는 아직 나온지 얼마 안된 언어인 탓에 Go 언어를 지원하는 제대로된 IDE 는 아직 그리 많지 않은편이다 (Java 나 C/C++ 의 IDE 들은 정말이지 아직 Go 의 IDE 들에게는 넘사벽이다)

Cloud 에디터, 텍스트 편집기의 Syntex Highlight 플러그인을  제외한 Go 언어를 위한 대표적인 설치형 IDE 는 아래와 같다.

Eclipse - GoClipse : Eclipse 의 플러그인
GoWorks - Netbeans 의 플러그인이나 Netbeans 기반의 Standalone 형태로 사용
IntelliJ IDEA - IntelliJ IDEA 용 플러그인
LiteIDE - Go 언어 전용의 IDE 로, Go 언어 릴리즈 초기부터 함께한 IDE
Visual Studio - Visual Studio 용 플러그인
그외에 상용 IDE 로는, KomodoIDE, Zeus 등이 있음

위 IDE 들 중 빠르고 깔끔한 LiteIDE (크로스플랫폼지원)를 Go 개발자들이 가장 많이 사용중이므로 본 강좌에서도 이 IDE 를 기준으로 하도록 한다.

Lite IDE 중에서도 Mac 버전을 기준으로 하겠으나, 어차피 Qt 기반의 UI 이므로 UI 구성은 Windows 나 Mac 이나 Linux 나 유사하여 다른 플랫폼에서도 큰 문제없이 이해가 가능할것이다.
(사실 Windows 에서 주로 작업하지만 당분간 노트북으로 글을 써야하는 상황에 노트북이 맥북 밖에 없다)

반응형

'Software Development > Go (golang)' 카테고리의 다른 글

3.2 Go - 기본 문법 및 Type  (488) 2016.02.29
3.1 Go - Hello World  (5) 2016.02.29
1.3 Go 언어란? - 다른 언어와 비교  (1159) 2016.02.28
1.2 Go 언어란? - 주요 특징  (845) 2016.02.28
1.1 Go 언어란? - History  (518) 2016.02.27
블로그 이미지

Good Joon

IT Professionalist Since 1999

,