[패캠] 2주차 Geth 실습 — 트랜잭션

우리가 실습하는 실제 환경은 오른쪽 빗금 친 부분이다. 그러니 실제 Ether를 우리가 실습하는 네트워크로 보내지 않길 바란다..-_-;

* 주의 이더에 대한 손실의 책임은 강사에게 없음

geth 옵션 설명

--networkid 네트워크 식별자 // 채굴을 사용할 수 있는 CPU 스레드 수 를 지정함 기본값 1
--mine // 채굴 활성화
--rpc // HTTP-RPC 서버를 활성화 하고, 별도의 콘솔을 연결할 때 필요한 옵션임
--nodiscover // 생성자의 노드를 다른 노드에서 검색할 수 없게 하는 옵션
--maxpeers // 피어를 연결할 최대 허용치
--datadir // chaindata 와 keystore가 쌓이게 할 위치
--console // 노드에 명령어를 전달할 수 있는 자바스크립트 콘솔
--rpcaddr // HTTP RPC Server 호스트 ( default : "localhost" )
--rpcport // HTTP RPC 포트 ( default : 8545" )
--rpcapi // RPC API 모듈 (eth, miner, admin, personal, web3....)
--bootnodes // 부트노드에 연결할 enode 주소를 넣으면 자동으로 해당 Peer가 부트노드와 연결됨
--password // 계정의 password 파일
--etherbase // 이더베이스 설정 마이닝 시 보상을 얻을 수 있는 주소
--unlock // account list 중 unlock을 시킬 계정의 index 주소를 넣음

그 외 설정에 대한 부분은 아래를 통해 확인 가능함

$ geth --help

이제 서버를 띄워보자

$ geth --networkid 8484 --nodiscover --datadir test_data --rpcaddr 0.0.0.0 --rpc --rpcport 8545 --rpccorsdomain "*" --rpcapi="db,eth,net,web3,personal,web3,miner,admin" --miner.threads 1 console 2>> test_data/geth.log
// forground service 를 띄우면서 console 접속함 

background service 로 띄우고 싶다면 아래와 같이 띄우면 됨

$ nohup geth --networkid 8484 --nodiscover --datadir test_data --rpcaddr 0.0.0.0 --rpc --rpcport 8545 --rpccorsdomain "*" --rpcapi="db,eth,net,web3,personal,web3,miner,admin" --miner.threads 1 2>> test_data/geth.log &
Background Service 를 띄우면 위와 같이 나옴

Background Service 를 띄우게 되면 현재 Process ID 는 23271번이 된다. 나중에 이 프로세스를 종료하기 위해선 아래와 같이 종료하면 됨

$ kill -15 23271

프로세스가 돌아가는 것을 확인하기 위해선

$ ps -ef |grep geth

위와 같이 프로세스가 돌아가는 것을 확인 할 수 있고… 서비스 포트에 대해선 아래와 같이 확인할 수 있다. 단, netstat 이 우분투에 깔려있지 않은 관계로 설치부터 하고 실행해보자

$ sudo apt install net-tools
$ netstat -ntlp

위의 상황을 보았을 때 tcp 포트로 30303과 8545가 geth (PID :23271)에서 Listening 하고 있다는 것을 확인 할 수 있다.

$ geth attach http://127.0.0.1:8545

geth 콘솔을 Background Service로 돌렸을 때 접속을 할 수 있는 명령어이다.

우선 송금 트랜잭션을 실습해보았고… geth console 을 통해 API를 실행 해 보자

personal.newAccount('{passphrase}') //유저 Address 생성
miner.start() // 마이닝 시작
miner.stop() // 마이닝 중단

geth.log 를 확인 하기 위해선 터미널을 하나 더 띄우고

$ tail -f ~/ethereum/test_data/geth.log
$ eth.getBalance(eth.accounts[0]) // 잔고 확인

wei 의 단위로 나오게 되는데 이더의 단위로 환산하기 위해서는 web3 모듈을 사용함.

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

기본적인 단위를 환산 하자면 1 eth = 1*10¹⁸ 이다.

HelloWorld Contract Remix를 통해 배포 해보기

  • 배포하기 전에 Virtual Box에서 우린 Geth를 띄우고 있다. 가상환경이기 때문에 어느정도 성능의 제약 조건이 있기 때문에 Geth 만 가상환경에 설치해서 운영하고 외부의 Remix에서 접속 할 수 있도록 포트포워딩 설정을 진행하겠다.

VM 환경 우분투의 NAT로 설정된 IP를 확인해보자.

$ ifconfig // VirtualBox 가상환경의 IP 10.0.2.15 PC 마다 다르다.

위의 주소를 복사 후 이미지를 잠시 종료한다.

State를 유지한체 종료한다. 혹시나 Power를 끄게 되면 IP 주소가 변경될 수 있기 때문에…

종료 이후 설정 > 네트워크 메뉴로 간다.

고급 설정을 누르면 아래 Port Forwarding 이라는 버튼이 있는데 눌러보자.

호스트 IP는 본인 아이피를 적어주고 GuestIP 에는 우분투에서 복사했던 IP를 적어준다. Port는 HTTP RPC 포트로 설정하면 된다. OK를 누르고 다시 우분투를 실행하자.

그 다음부턴 우분투에서 Geth 를 실행하여 띄운다. 여기서 — rpc 옵션을 줘야한 다는 것을 잊지말자. 우리가 지금 외부에서 가상환경의 우분투를 접속하기 위한 포트포워딩 설정을 한것이다.

이 후엔 VirtualBox가 아닌 외부 Local PC에서 http://remix.ethereum.org/ 로 들어가서 Enviroment 에 Web3 Provider를 선택하고 연결하면 끝이다.

제대로 연결이 된다면 Geth에서 생성한 계정들이 보일 것이고 이 계정들을 통해 실습을 진행하면 된다.!

이제 HelloWorld 의 간단한 컨트랙트를 실행해 볼 예정이다.

HelloWorld.sol

pragma solidity ^0.4.25;
contract HelloWorld {
string private greeting;
constructor() public{
greeting = "Hi! JeongJoo!";
}
function setGreeting(string _greeting) public {
greeting = _greeting;
}
function getGreeting() public view returns (string) {
return greeting;
}
}

정말 간단한 컨트랙트 소스이다.. 모두가 그랬듯이 모든 언어의 시작은 HelloWorld 이다.

해당 소스를 remix.ethereum.org 원격 사이트 툴에서 코딩하고 Deploy 할 수 있다.

맨 좌측 상단에 +버튼은 솔리디티 파일을 만들어준다. 해당 버튼을 누르고 파일이름을 지정하고 컨트랙트 코드 내용을 입력해주면 된다. 현재 2018.11.07 솔리디티의 버전은 0.4.25이다. Experimental의 0.5.0이 나온상태이지만 좀 더 Stable 한 버전으로 실습을 해보겠다.

우선 우측 Run 탭을 잠깐 설명을 하자면 Account는 내 Geth의 주소이다. 만약 0 ether로 되어있다면 약간의 채굴이 필요하다… 채굴을 해서 Ether가 잔고에 있다는 전제하에 Gas Limit을 설정한다. Gas Limit의 경우 해당 컨트랙트에 트랜잭션을 발생시켰을 때 최대로 사용할 수 있는 허용치이다. 만약 컨트랙트를 실행하다가 위의 설정된 값을 넘어가면 Gas 만 잃고 아무것도 변경이 안되니 서비스하고자 하는 Contract 의 Operation을 잘 생각해서 적절하게 넣도록 한다.

HelloWorld 컨트랙트 아래 [Deploy] 버튼을 클릭해보자.

하단 로그에 password or unlock이 되어있다면 Geth Console에서 해당 계정을 personal.unlockAccount 를 통해 해줘야 한다. 이 부분은 생략하겠음

만약, 마이닝 상태가 아니라면 아래와 같은 메시지를 볼 수 있는데.

이 메시지는 현재 블록이 만들어지지 않아 Pending Transaction이 쌓이는 것이다.

pending 된 트랜잭션이다. v,r,s 까지 ECDSA 알고리즘으로 서명이 완료 되었고 처리가 되기를 기다리고 있는 상태이다. input 에는 방금 솔리디티에서 작성한 컴파일 된 바이트 코드가 들어가 있는 것을 확인 할 수가 있다.

마이닝을 하게 된다면 test_data/geth.log 를 확인해보면 아래와 같이 로그를 확인해 볼 수 있다.

51번째 블록에 트랜잭션이 들어갔고 해당 내용을 아래와 같은 명령어로 확인해보면 transaction이 하나가 처리 되었다는 것을 알 수 있다.

> eth.getBlock('51')

트랜잭션 정보도 확인해보자.

eth.getTransaction('0x0636b9fd99b9bf195b8b653e331ccb6698c91ff7f72138ad4b1be220e4a20934')

컨트랙트가 처리된 것을 알 수 있고 이제 Remix로 가서 컨트랙트 생성 여부를 확인해보면 아래와 같이 Address 주소와 함께 생성된 것이 확인 가능하다.

CA주소 : 0xc4469c796b8908a6c49fd1c30400615d38bdae20

이제 getGreeting을 눌러보겠다.

getGreeting의 메시지는 string 리턴 타입으로 출력되는 것을 볼 수 있고 eth_call 메서드를 내부적으로 활용하는 것을 확인 할 수 있다.

일반적인 Call의 경우에는 Gas 비용이 들지 않는다. 블록체인상의 정보를 get을 하는 경우는 Gas 비용이 들지 않는다는 것을 알아두고 이제 sendTransaction으로 블록체인상의 정보를 변경해보겠다.

실행해보자. 마이닝이 안 되어 있다면 다시 마이닝을 실행하고 트랜잭션 처리가 되면 위에서 말했던 블록에 트랜잭션이 포함되게 된다.

그리고 다시 getGreeting을 실행한다면 아래와 같이 변경했던 결과가 나오게 된다.

이제 Remix-IDE를 통해서 직접 만든 Geth에 트랜잭션을 스마트 컨트랙트를 통해 발생시켜보았다. 이제는 이더리움 콘솔에서 Remix가 해주는 작업을 좀 더 Low한 레벨로 진행하여 보겠다.

HelloWorld Contract 콘솔을 통해 배포 해보기

  • 과거 Geth 1.5 버전에서는 Solidity Compile를 지원 하였으나 1.6버전 이상부터는 지원을 하지 않기 때문에 솔리디티 Remix를 이용해 진행 하겠음

작성된 컨트랙트는 내부에서 자동으로 컴파일 된다. 컴파일 된 정보를 가져오기 위해선 다음과 같이 Complie 탭> Details 버튼을 눌러 확인한다. 현재 Contract 를 작성하는 Solidity를 이해하지 못하더라도 괜찮다~~

WEB3 Deploy 탭의 [ 부터 시작하여 ] 까지 복사한다.

ABI 값을 Console의 변수에다 넣자

> abi = [{"constant":false,"inputs":[{"name":"_greeting","type":"string"}],"name":"setGreeting","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"getGreeting","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"inputs":[],"payable":false,"stateMutability":"nonpayable","type":"constructor"}]

ABI 정보를 abi 변수에 넣게 된다면 아래와 같은 화면이 출력 된다.

ABI란? Application Binary Interface의 약자로 스마트 컨트랙트의 바이트 코드를 일반 프로그램에서 호출하고 실행시킬 수 있는 정보와 인터페이스를 제공해 주는 거라 생각하면 된다. 앞으로 많이 이용될 것이므로 잘 기억해 두자!

ABI 정보를 eth.contract를 통해 만들어 보자

> helloContract = eth.contract(abi)

eth.contract는 용도는 컨트랙트에 정의된 메서드와 이벤트들을 생성한다.

helloContract 인스턴스를 이제 배포하기 위해 하나 더 해야 될은 바이트코드를 가져오는 것이다. 다시 [Details] 버튼으로 가서 [Bytecode]를 눌러 복사하자.

복사된 바이트 코드의 내용은 아래와 같다. 이 중에서 Bold 체로 된 Object 값을 복사하자.

{  "linkReferences": {},  "object": "608060405234801561001057600080fd5b5060408051808201909152600d8082527f486921204a656f6e674a6f6f210000000000000000000000000000000000000060209092019182526100559160009161005b565b506100f6565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061009c57805160ff19168380011785556100c9565b828001600101855582156100c9579182015b828111156100c95782518255916020019190600101906100ae565b506100d59291506100d9565b5090565b6100f391905b808211156100d557600081556001016100df565b90565b6102a7806101056000396000f30060806040526004361061004b5763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663a41368628114610050578063fe50cc72146100ab575b600080fd5b34801561005c57600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526100a99436949293602493928401919081908401838280828437509497506101359650505050505050565b005b3480156100b757600080fd5b506100c061014c565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100fa5781810151838201526020016100e2565b50505050905090810190601f1680156101275780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80516101489060009060208401906101e3565b5050565b60008054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156101d85780601f106101ad576101008083540402835291602001916101d8565b820191906000526020600020905b8154815290600101906020018083116101bb57829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061022457805160ff1916838001178555610251565b82800160010185558215610251579182015b82811115610251578251825591602001919060010190610236565b5061025d929150610261565b5090565b6101e091905b8082111561025d57600081556001016102675600a165627a7a723058208d8da878c4c7582510046c77811ee034e06887c9e95c5a79c1bc37ebcf3fc46e0029",  "opcodes": "PUSH1 0x80 PUSH1 0x40 MSTORE CALLVALUE DUP1 ISZERO PUSH2 0x10 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 DUP1 MLOAD DUP1 DUP3 ADD SWAP1 SWAP2 MSTORE PUSH1 0xD DUP1 DUP3 MSTORE PUSH32 0x486921204A656F6E674A6F6F2100000000000000000000000000000000000000 PUSH1 0x20 SWAP1 SWAP3 ADD SWAP2 DUP3 MSTORE PUSH2 0x55 SWAP2 PUSH1 0x0 SWAP2 PUSH2 0x5B JUMP JUMPDEST POP PUSH2 0xF6 JUMP JUMPDEST DUP3 DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 PUSH1 0x1F ADD PUSH1 0x20 SWAP1 DIV DUP2 ADD SWAP3 DUP3 PUSH1 0x1F LT PUSH2 0x9C JUMPI DUP1 MLOAD PUSH1 0xFF NOT AND DUP4 DUP1 ADD OR DUP6 SSTORE PUSH2 0xC9 JUMP JUMPDEST DUP3 DUP1 ADD PUSH1 0x1 ADD DUP6 SSTORE DUP3 ISZERO PUSH2 0xC9 JUMPI SWAP2 DUP3 ADD JUMPDEST DUP3 DUP2 GT ISZERO PUSH2 0xC9 JUMPI DUP3 MLOAD DUP3 SSTORE SWAP2 PUSH1 0x20 ADD SWAP2 SWAP1 PUSH1 0x1 ADD SWAP1 PUSH2 0xAE JUMP JUMPDEST POP PUSH2 0xD5 SWAP3 SWAP2 POP PUSH2 0xD9 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST PUSH2 0xF3 SWAP2 SWAP1 JUMPDEST DUP1 DUP3 GT ISZERO PUSH2 0xD5 JUMPI PUSH1 0x0 DUP2 SSTORE PUSH1 0x1 ADD PUSH2 0xDF JUMP JUMPDEST SWAP1 JUMP JUMPDEST PUSH2 0x2A7 DUP1 PUSH2 0x105 PUSH1 0x0 CODECOPY PUSH1 0x0 RETURN STOP PUSH1 0x80 PUSH1 0x40 MSTORE PUSH1 0x4 CALLDATASIZE LT PUSH2 0x4B JUMPI PUSH4 0xFFFFFFFF PUSH29 0x100000000000000000000000000000000000000000000000000000000 PUSH1 0x0 CALLDATALOAD DIV AND PUSH4 0xA4136862 DUP2 EQ PUSH2 0x50 JUMPI DUP1 PUSH4 0xFE50CC72 EQ PUSH2 0xAB JUMPI JUMPDEST PUSH1 0x0 DUP1 REVERT JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0x5C JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 PUSH1 0x4 DUP1 CALLDATALOAD DUP1 DUP3 ADD CALLDATALOAD PUSH1 0x1F DUP2 ADD DUP5 SWAP1 DIV DUP5 MUL DUP6 ADD DUP5 ADD SWAP1 SWAP6 MSTORE DUP5 DUP5 MSTORE PUSH2 0xA9 SWAP5 CALLDATASIZE SWAP5 SWAP3 SWAP4 PUSH1 0x24 SWAP4 SWAP3 DUP5 ADD SWAP2 SWAP1 DUP2 SWAP1 DUP5 ADD DUP4 DUP3 DUP1 DUP3 DUP5 CALLDATACOPY POP SWAP5 SWAP8 POP PUSH2 0x135 SWAP7 POP POP POP POP POP POP POP JUMP JUMPDEST STOP JUMPDEST CALLVALUE DUP1 ISZERO PUSH2 0xB7 JUMPI PUSH1 0x0 DUP1 REVERT JUMPDEST POP PUSH2 0xC0 PUSH2 0x14C JUMP JUMPDEST PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 DUP1 DUP3 MSTORE DUP4 MLOAD DUP2 DUP4 ADD MSTORE DUP4 MLOAD SWAP2 SWAP3 DUP4 SWAP3 SWAP1 DUP4 ADD SWAP2 DUP6 ADD SWAP1 DUP1 DUP4 DUP4 PUSH1 0x0 JUMPDEST DUP4 DUP2 LT ISZERO PUSH2 0xFA JUMPI DUP2 DUP2 ADD MLOAD DUP4 DUP3 ADD MSTORE PUSH1 0x20 ADD PUSH2 0xE2 JUMP JUMPDEST POP POP POP POP SWAP1 POP SWAP1 DUP2 ADD SWAP1 PUSH1 0x1F AND DUP1 ISZERO PUSH2 0x127 JUMPI DUP1 DUP3 SUB DUP1 MLOAD PUSH1 0x1 DUP4 PUSH1 0x20 SUB PUSH2 0x100 EXP SUB NOT AND DUP2 MSTORE PUSH1 0x20 ADD SWAP2 POP JUMPDEST POP SWAP3 POP POP POP PUSH1 0x40 MLOAD DUP1 SWAP2 SUB SWAP1 RETURN JUMPDEST DUP1 MLOAD PUSH2 0x148 SWAP1 PUSH1 0x0 SWAP1 PUSH1 0x20 DUP5 ADD SWAP1 PUSH2 0x1E3 JUMP JUMPDEST POP POP JUMP JUMPDEST PUSH1 0x0 DUP1 SLOAD PUSH1 0x40 DUP1 MLOAD PUSH1 0x20 PUSH1 0x1F PUSH1 0x2 PUSH1 0x0 NOT PUSH2 0x100 PUSH1 0x1 DUP9 AND ISZERO MUL ADD SWAP1 SWAP6 AND SWAP5 SWAP1 SWAP5 DIV SWAP4 DUP5 ADD DUP2 SWAP1 DIV DUP2 MUL DUP3 ADD DUP2 ADD SWAP1 SWAP3 MSTORE DUP3 DUP2 MSTORE PUSH1 0x60 SWAP4 SWAP1 SWAP3 SWAP1 SWAP2 DUP4 ADD DUP3 DUP3 DUP1 ISZERO PUSH2 0x1D8 JUMPI DUP1 PUSH1 0x1F LT PUSH2 0x1AD JUMPI PUSH2 0x100 DUP1 DUP4 SLOAD DIV MUL DUP4 MSTORE SWAP2 PUSH1 0x20 ADD SWAP2 PUSH2 0x1D8 JUMP JUMPDEST DUP3 ADD SWAP2 SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 JUMPDEST DUP2 SLOAD DUP2 MSTORE SWAP1 PUSH1 0x1 ADD SWAP1 PUSH1 0x20 ADD DUP1 DUP4 GT PUSH2 0x1BB JUMPI DUP3 SWAP1 SUB PUSH1 0x1F AND DUP3 ADD SWAP2 JUMPDEST POP POP POP POP POP SWAP1 POP JUMPDEST SWAP1 JUMP JUMPDEST DUP3 DUP1 SLOAD PUSH1 0x1 DUP2 PUSH1 0x1 AND ISZERO PUSH2 0x100 MUL SUB AND PUSH1 0x2 SWAP1 DIV SWAP1 PUSH1 0x0 MSTORE PUSH1 0x20 PUSH1 0x0 KECCAK256 SWAP1 PUSH1 0x1F ADD PUSH1 0x20 SWAP1 DIV DUP2 ADD SWAP3 DUP3 PUSH1 0x1F LT PUSH2 0x224 JUMPI DUP1 MLOAD PUSH1 0xFF NOT AND DUP4 DUP1 ADD OR DUP6 SSTORE PUSH2 0x251 JUMP JUMPDEST DUP3 DUP1 ADD PUSH1 0x1 ADD DUP6 SSTORE DUP3 ISZERO PUSH2 0x251 JUMPI SWAP2 DUP3 ADD JUMPDEST DUP3 DUP2 GT ISZERO PUSH2 0x251 JUMPI DUP3 MLOAD DUP3 SSTORE SWAP2 PUSH1 0x20 ADD SWAP2 SWAP1 PUSH1 0x1 ADD SWAP1 PUSH2 0x236 JUMP JUMPDEST POP PUSH2 0x25D SWAP3 SWAP2 POP PUSH2 0x261 JUMP JUMPDEST POP SWAP1 JUMP JUMPDEST PUSH2 0x1E0 SWAP2 SWAP1 JUMPDEST DUP1 DUP3 GT ISZERO PUSH2 0x25D JUMPI PUSH1 0x0 DUP2 SSTORE PUSH1 0x1 ADD PUSH2 0x267 JUMP STOP LOG1 PUSH6 0x627A7A723058 KECCAK256 DUP14 DUP14 0xa8 PUSH25 0xC4C7582510046C77811EE034E06887C9E95C5A79C1BC37EBCF 0x3f 0xc4 PUSH15 0x2900000000000000000000000000 ",  "sourceMap": "26:301:0:-;;;82:63;8:9:-1;5:2;;;30:1;27;20:12;5:2;-1:-1;112:26:0;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;112:26:0;:::i;:::-;;26:301;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;26:301:0;;;-1:-1:-1;26:301:0;:::i;:::-;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;" }

그리고 앞에 0x 를 추가해줘야한다. 이더리움은 기본적으로 16진수 앞에 Prefix로 0x 를 붙여준다.

> contract = helloContract.new(abi, {from:eth.accounts[0], data:'0x608060405234801561001057600080fd5b5060408051808201909152600d8082527f486921204a656f6e674a6f6f210000000000000000000000000000000000000060209092019182526100559160009161005b565b506100f6565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061009c57805160ff19168380011785556100c9565b828001600101855582156100c9579182015b828111156100c95782518255916020019190600101906100ae565b506100d59291506100d9565b5090565b6100f391905b808211156100d557600081556001016100df565b90565b6102a7806101056000396000f30060806040526004361061004b5763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663a41368628114610050578063fe50cc72146100ab575b600080fd5b34801561005c57600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526100a99436949293602493928401919081908401838280828437509497506101359650505050505050565b005b3480156100b757600080fd5b506100c061014c565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100fa5781810151838201526020016100e2565b50505050905090810190601f1680156101275780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80516101489060009060208401906101e3565b5050565b60008054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156101d85780601f106101ad576101008083540402835291602001916101d8565b820191906000526020600020905b8154815290600101906020018083116101bb57829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061022457805160ff1916838001178555610251565b82800160010185558215610251579182015b82811115610251578251825591602001919060010190610236565b5061025d929150610261565b5090565b6101e091905b8082111561025d57600081556001016102675600a165627a7a723058208d8da878c4c7582510046c77811ee034e06887c9e95c5a79c1bc37ebcf3fc46e0029', gas:3000000})

위의 내용은 이더리움 네트웍상에 컨트랙트를 배포하는 과정이다. from 누가 배포할 것인지 그리고 gas는 실제 저 배포에 들어가는 수수료를 책정하는 것이다. 이제 배포해보자. 배포를 하게 되면 mining이 되어있지 않을 경우 Pending Transaction으로 등록이 된다. 아래 Tx ID는 트랜잭션의 ID라고 보면 된다.

Tx ID Hash = 0xc6e2b9c2c881e33a6b82046f4d7f682d049d8c895b5a33e898ecb3af47685710

CA 주소 = 0xD9f87482f17C3E0995Df4C6E84Fac46354a73895

geth 로그확인하면 TXID와 CA 주소가 생성된 것을 확인 할 수 있음

Pending Transaction 확인 하기

> eth.pendingTransactions
[{
blockHash: null,
blockNumber: null,
from: "0xc470eb7552ffb0bbf48c8e81f2991aee3b3f99f4",
gas: 3000000,
gasPrice: 1000000000,
hash: "0xc6e2b9c2c881e33a6b82046f4d7f682d049d8c895b5a33e898ecb3af47685710",
input: "0x608060405234801561001057600080fd5b5060408051808201909152600d8082527f486921204a656f6e674a6f6f210000000000000000000000000000000000000060209092019182526100559160009161005b565b506100f6565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061009c57805160ff19168380011785556100c9565b828001600101855582156100c9579182015b828111156100c95782518255916020019190600101906100ae565b506100d59291506100d9565b5090565b6100f391905b808211156100d557600081556001016100df565b90565b6102a7806101056000396000f30060806040526004361061004b5763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663a41368628114610050578063fe50cc72146100ab575b600080fd5b34801561005c57600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526100a99436949293602493928401919081908401838280828437509497506101359650505050505050565b005b3480156100b757600080fd5b506100c061014c565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100fa5781810151838201526020016100e2565b50505050905090810190601f1680156101275780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80516101489060009060208401906101e3565b5050565b60008054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156101d85780601f106101ad576101008083540402835291602001916101d8565b820191906000526020600020905b8154815290600101906020018083116101bb57829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061022457805160ff1916838001178555610251565b82800160010185558215610251579182015b82811115610251578251825591602001919060010190610236565b5061025d929150610261565b5090565b6101e091905b8082111561025d57600081556001016102675600a165627a7a723058208d8da878c4c7582510046c77811ee034e06887c9e95c5a79c1bc37ebcf3fc46e0029",
nonce: 9,
r: "0xe62144b1247ab09df560c24d0b8541fc68662b5385c97119ce271b0f631a73f1",
s: "0x4772212cc572543f23c6ed8899b1cd3841951e5d68cdf4be4d3df0e748e97a23",
to: null,
transactionIndex: 0,
v: "0x426b",
value: 0
}]

현재 Pending 되어있고 to 와 blockNumber, blockHash가 null 인것을 볼 수 있다.

이제 채굴을 start 해보자

> miner.start()

로그를 확인해보면 본인 PC의 경우 55번째 블록에 해당 트랜잭션이 추가된 것을 볼 수 있다.

> eth.getBlock(55)

eth.getBlock은 블록에 대한 정보를 볼 수 있는 부분인데 transactions를 보면 아까 Pending 되었던 우리의 스마트 컨트랙트 객체가 처리가 된것을 볼 수 잇을 것이다. 해당 트랜잭션을 복사하여 트랜잭션 내용을 확인해보자.

0xc6e2b9c2c881e33a6b82046f4d7f682d049d8c895b5a33e898ecb3af47685710

> eth.getTransaction('0xc6e2b9c2c881e33a6b82046f4d7f682d049d8c895b5a33e898ecb3af47685710')

트랜잭션 내용

{
blockHash: "0x541d828cc481bd77b01b4855e4cad1d95075707e2c9ea4f251835784d67a3230",
blockNumber: 55,
from: "0xc470eb7552ffb0bbf48c8e81f2991aee3b3f99f4",
gas: 3000000,
gasPrice: 1000000000,
hash: "0xc6e2b9c2c881e33a6b82046f4d7f682d049d8c895b5a33e898ecb3af47685710",
input: "0x608060405234801561001057600080fd5b5060408051808201909152600d8082527f486921204a656f6e674a6f6f210000000000000000000000000000000000000060209092019182526100559160009161005b565b506100f6565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061009c57805160ff19168380011785556100c9565b828001600101855582156100c9579182015b828111156100c95782518255916020019190600101906100ae565b506100d59291506100d9565b5090565b6100f391905b808211156100d557600081556001016100df565b90565b6102a7806101056000396000f30060806040526004361061004b5763ffffffff7c0100000000000000000000000000000000000000000000000000000000600035041663a41368628114610050578063fe50cc72146100ab575b600080fd5b34801561005c57600080fd5b506040805160206004803580820135601f81018490048402850184019095528484526100a99436949293602493928401919081908401838280828437509497506101359650505050505050565b005b3480156100b757600080fd5b506100c061014c565b6040805160208082528351818301528351919283929083019185019080838360005b838110156100fa5781810151838201526020016100e2565b50505050905090810190601f1680156101275780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b80516101489060009060208401906101e3565b5050565b60008054604080516020601f60026000196101006001881615020190951694909404938401819004810282018101909252828152606093909290918301828280156101d85780601f106101ad576101008083540402835291602001916101d8565b820191906000526020600020905b8154815290600101906020018083116101bb57829003601f168201915b505050505090505b90565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061022457805160ff1916838001178555610251565b82800160010185558215610251579182015b82811115610251578251825591602001919060010190610236565b5061025d929150610261565b5090565b6101e091905b8082111561025d57600081556001016102675600a165627a7a723058208d8da878c4c7582510046c77811ee034e06887c9e95c5a79c1bc37ebcf3fc46e0029",
nonce: 9,
r: "0xe62144b1247ab09df560c24d0b8541fc68662b5385c97119ce271b0f631a73f1",
s: "0x4772212cc572543f23c6ed8899b1cd3841951e5d68cdf4be4d3df0e748e97a23",
to: null,
transactionIndex: 0,
v: "0x426b",
value: 0
}

배포된 컨트랙트 객체 확인하기

> contract
{
abi: [{
constant: false,
inputs: [{...}],
name: "setGreeting",
outputs: [],
payable: false,
stateMutability: "nonpayable",
type: "function"
}, {
constant: true,
inputs: [],
name: "getGreeting",
outputs: [{...}],
payable: false,
stateMutability: "view",
type: "function"
}, {
inputs: [],
payable: false,
stateMutability: "nonpayable",
type: "constructor"
}],
address: "0xd9f87482f17c3e0995df4c6e84fac46354a73895",
transactionHash: "0xc6e2b9c2c881e33a6b82046f4d7f682d049d8c895b5a33e898ecb3af47685710",
allEvents: function(),
getGreeting: function(),
setGreeting: function()
}

이제 getGreeting을 실행해보도록 하자.

> contract.getGreeting.call()

제대로 나오는 것을 확인할 수 있다.

이제 setGreeting 을 통해 sendTransaction을 날려보겠다.

먼저 sendTransaction의 경우 Method와 Arguement를 정의를 해줘야하는데 인코딩이 되어 들어간다. bytes4(keccak256(“setGreeting(string)”) 의 바이트 값은 a4136892 이고 앞에 0x를 붙이면 아래 Bold 체로 붙인 부분이 만들어지고 뒷부분부터는 “Hi!! FastCampus!!!”의 스트링 값 뒤쪽에 Padding 하여 rlp encoding을 시킨 후 데이터를 직렬화해서 보내는 과정이다.

0xa41368620000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001248692121204661737443616d7075732121210000000000000000000000000000

우선 rlp 인코딩된 위의 값을 복사하여 아래 data 에 추가한 후 sendTransaction을 날려보겠다.

> eth.sendTransaction({from:eth.accounts[0],to:'0xd9f87482f17c3e0995df4c6e84fac46354a73895',data:'0xa41368620000000000000000000000000000000000000000000000000000000000000020000000000000000000000000000000000000000000000000000000000000001248692121204661737443616d7075732121210000000000000000000000000000'})

TxID : 0x318ecc7ae86524088d9e0b1fc284b4376fffe03ccd04983ce1cb55643887b4a3

트랜잭션이 날라갔다. 이제 마이닝을 해본 다음에 getGreeting 을 call하여 데이터를 확인해보자.

위와 같이 변경된 것을 볼 수있다.

결론

자 정리해보면 이런 것이다. 처음에는 Remix 툴을 통해 해본 것이고 두 번째는 Geth console에서 실제 Contract를 만드는 과정을 좀 더 Low한 레벨로 해본 것이다. 이렇게 해야만 구조적으로 트랜잭션이 어떻게 처리가 되는지 과정들을 알 수 있기 때문에 진행해 본 실습이였다.

그 다음 Geth를 통해 해야할 것은 Node 2개를 붙여보는 것이다. 노드 2개를 붙이고 동일한 실험을 해보도록 하겠다!

  • 저는 블록체인 개발사 (주)34일에서 블록체인 엔지니어로 일하고 있습니다.
  • 880만 팔로워 전세계 1위 한류 미디어 케이스타라이브(KStarLive)와 함께 만든 한류 플랫폼에서 사용되는 케이스타코인(KStarCoin) 프로젝트를 진행 중입니다. 팬 커뮤니티 활동을 하면서 코인을 얻을 수 있으며, 한류 콘텐츠 구매, 공연 예매, 한국 관광 상품 구매, 기부 및 팬클럽 활동 등에 사용 될 계획입니다.