├── appendix └── genesis.json ├── chap2 └── ch2_cmd.txt ├── chap3 ├── ch3_01_HelloWorld_comment.sol ├── ch3_02_HelloWorldOrg.sol ├── ch3_03_HelloWorld.sol ├── ch3_04_DataTypeSample.sol ├── ch3_05_IntSample.sol ├── ch3_06_AddressSample.sol ├── ch3_07_ArraySample.sol ├── ch3_08_StructSample.sol ├── ch3_09_MappingSample.sol ├── ch3_10_EtherUnitSample.sol ├── ch3_11_TimeUnitSample.sol ├── ch3_12_Inheritance.sol ├── ch3_13_Inheritance2.sol ├── ch3_14_SelfDestructSample.sol └── ch3_cmd.txt ├── chap4 ├── ch4_01_OreOreCoin.sol ├── ch4_02_OreOreCoin.sol ├── ch4_03_OreOreCoin.sol ├── ch4_04_OreOreCoin.sol ├── ch4_05_OreOreCoin.sol └── ch4_06_OreOreCoin.sol ├── chap5 ├── ch5_01_KeyValueStore.sol ├── ch5_02_TransactionLogNG.sol ├── ch5_03_TransactionLogOK.sol ├── ch5_04_PersonCertification.sol └── ch5_04_cmd.txt └── chap6 ├── .DS_Store ├── 6.2.2.sol ├── 6.3.1.sol ├── 6.3.2.sol ├── 6.3.3.sol ├── 6.4.2.sol ├── 6.4.3.sol └── 6.5.2.sol /appendix/genesis.json: -------------------------------------------------------------------------------- 1 | { 2 | "config": {}, 3 | "nonce": "0x0000000000000042", 4 | "timestamp": "0x0", 5 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 6 | "gasLimit": "0x8000000", 7 | "difficulty": "0x4000", 8 | "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", 9 | "alloc": {} 10 | } 11 | -------------------------------------------------------------------------------- /chap2/ch2_cmd.txt: -------------------------------------------------------------------------------- 1 | // 명령줄에서 필수 라이브러리, 프로그램 등을 설치 2 | sudo apt-get install -y build-essential libgmp3-dev golang git tree 3 | 4 | // Geth 설치(1.5.5) 5 | cd 6 | git clone https://github.com/ethereum/go-ethereum.git 7 | cd go-ethereum/ 8 | git checkout refs/tags/v1.5.5 9 | make geth 10 | ./build/bin/geth version 11 | sudo cp build/bin/geth /usr/local/bin/ 12 | which geth 13 | 14 | // 데이터 디렉토리 준비 15 | mkdir ~/data_testnet 16 | 17 | // Genesis 파일 18 | cd data_testnet/ 19 | vi genesis.json 20 | { 21 | "nonce": "0x0000000000000042", 22 | "timestamp": "0x0", 23 | "parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000", 24 | "extraData": "0x0", 25 | "gasLimit": "0x8000000", 26 | "difficulty": "0x4000", 27 | "mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000", 28 | "coinbase": "0x3333333333333333333333333333333333333333", 29 | "alloc": {} 30 | } 31 | 32 | // 초기화 (디렉토리는 적절하게 변경해야 한다. 여기서는 eth라는 사용자의 홈 디렉토리에서 작업을 수행한다) 33 | geth --datadir /home/eth/data_testnet init /home/eth/data_testnet/genesis.json 34 | cd 35 | tree data_testnet/ 36 | 37 | // Geth 실행 38 | geth --networkid 4649 --nodiscover --maxpeers 0 --datadir /home/eth/data_testnet console 2>> /home/eth/data_testnet/geth.log 39 | 40 | 41 | // 계정 생성 42 | personal.newAccount("pass0") 43 | personal.newAccount("pass1") 44 | 45 | // 계정 확인 46 | eth.accounts 47 | eth.accounts[0] 48 | eth.accounts[1] 49 | 50 | // Geth 콘솔 종료 51 | exit 52 | 53 | // geth 명령으로 계정 생성 54 | geth --datadir /home/eth/data_testnet account new 55 | 56 | // geth 명령으로 계정 확인 57 | geth --datadir /home/eth/data_testnet account list 58 | 59 | 60 | // Geth 실행 61 | geth --networkid 4649 --nodiscover --maxpeers 0 --datadir /home/eth/data_testnet console 2>> /home/eth/data_testnet/geth.log 62 | 63 | // 계정 확인 64 | eth.accounts 65 | 66 | // Etherbase 확인 67 | eth.coinbase 68 | 69 | // Etherbase 변경 70 | miner.setEtherbase(eth.accounts[1]) 71 | 72 | // Etherbase 확인 및 반환 73 | eth.coinbase 74 | miner.setEtherbase(eth.accounts[0]) 75 | eth.coinbase 76 | 77 | // 계정 잔고 확인 78 | eth.getBalance(eth.accounts[0]) 79 | eth.getBalance(eth.accounts[1]) 80 | eth.getBalance(eth.accounts[2]) 81 | 82 | // 블록 번호 확인 83 | eth.blockNumber 84 | 85 | // 채굴 개시 86 | miner.start(1) 87 | 88 | // 채굴 확인 관련 명령어 89 | eth.mining 90 | eth.hashrate 91 | eth.blockNumber 92 | 93 | // 채굴 정지 94 | miner.stop() 95 | 96 | // Etherbase 잔고 확인 97 | eth.getBalance(eth.coinbase) 98 | 99 | // 계정 잔고 확인 100 | eth.getBalance(eth.accounts[0]) 101 | eth.getBalance(eth.accounts[1]) 102 | eth.getBalance(eth.accounts[2]) 103 | 104 | web3.fromWei(eth.getBalance(eth.accounts[0]), "ether") 105 | web3.fromWei(eth.getBalance(eth.accounts[1]), "ether") 106 | web3.fromWei(eth.getBalance(eth.accounts[2]), "ether") 107 | 108 | // 송금 109 | eth.sendTransaction({from: eth.accounts[0], to: eth.accounts[1], value: web3.toWei(10, "ether")}) 110 | 111 | // 잠금 해제 112 | personal.unlockAccount(eth.accounts[0]) 113 | personal.unlockAccount(eth.accounts[0], "pass0") 114 | personal.unlockAccount(eth.accounts[0], "pass0", 0) 115 | 116 | // 처리 대기 중인 트랜잭션 확인 117 | eth.pendingTransactions 118 | 119 | // 잔고 확인 120 | web3.fromWei(eth.getBalance(eth.accounts[1]), "ether") 121 | 122 | // 송금 123 | eth.sendTransaction({from: eth.accounts[1], to: eth.accounts[2], value: web3.toWei(5, "ether")}) 124 | 125 | // 잔고 확인 126 | web3.fromWei(eth.getBalance(eth.accounts[2]), "ether") 127 | web3.fromWei(eth.getBalance(eth.accounts[1]), "ether") 128 | web3.fromWei(eth.getBalance(eth.accounts[0]), "ether") 129 | 130 | 131 | // 백그라운드로 Geth 실행 132 | nohup geth --networkid 4649 --nodiscover --maxpeers 0 --datadir /home/eth/data_testnet --mine --minerthreads 1 --rpc 2>> /home/eth/data_testnet/geth.log & 133 | 134 | // Geth 콘솔에 접속 135 | geth attach rpc:http://localhost:8545 136 | 137 | // 채굴 확인 138 | eth.mining 139 | 140 | // 콘솔 종료 141 | exit 142 | 143 | // 백그라운드로 Geth 실행(JSON-RPC) 144 | nohup geth --networkid 4649 --nodiscover --maxpeers 0 --datadir /home/eth/data_testnet --mine --minerthreads 1 --rpc --rpcaddr "0.0.0.0" --rpcport 8545 --rpccorsdomain "*" --rpcapi "admin,db,eth,debug,miner,net,shh,txpool,personal,web3" 2>> /home/eth/data_testnet/geth.log & 145 | 146 | // curl로 계정 생성 147 | curl -X POST --data '{"jsonrpc":"2.0","method":"personal_newAccount","params":["pass3"],"id":10}' localhost:8545 148 | 149 | // curl로 계정 확인 150 | curl -X POST --data '{"jsonrpc":"2.0","method":"personal_listAccounts","params":[],"id":10}' localhost:8545 151 | 152 | // curl로 채굴 확인 153 | $ curl -X POST --data '{"jsonrpc":"2.0","method":"eth_mining","params":[],"id":10}' localhost:8545 154 | 155 | // curl로 해시 속도 확인 156 | $ curl -X POST --data '{"jsonrpc":"2.0","method":"eth_hashrate","params":[],"id":10}' localhost:8545 157 | 158 | // curl로 블록 번호 확인 159 | $ curl -X POST --data '{"jsonrpc":"2.0","method":"eth_blockNumber","params":[],"id":10}' localhost:8545 160 | 161 | // curl로 잔고 확인 (계정은 적절하게 변경한다) 162 | curl -X POST --data '{"jsonrpc":"2.0","method":"eth_getBalance","params":["0xd4b066d813731a946fb883037f318c2d9444fcfe" , "latest"],"id":10}' localhost:8545 163 | 164 | // curl로 잠금 해제 (계정은 적절하게 변경한다) 165 | curl -X POST --data '{"jsonrpc":"2.0","method":"personal_unlockAccount","params":["0xd4b066d813731a946fb883037f318c2d9444fcfe", "pass2", 300],"id":10}' localhost:8545 166 | 167 | // curl로 송금 (계정은 적절하게 변경한다) 168 | curl -X POST --data '{"jsonrpc":"2.0","method":"eth_sendTransaction","params":[{"from":"0xd4b066d813731a946fb883037f318c2d9444fcfe","value":"0x6F05B59D3B20000","to":"0x3293ba9409e0881d23b494c8922318793971aac2"}],"id":10}' localhost:8545 169 | 170 | 171 | // Geth 실행시 계정 잠금 해제 172 | geth --networkid 4649 --nodiscover --maxpeers 0 --datadir /home/eth/data_testnet --mine --minerthreads 1 --rpc --rpcaddr "0.0.0.0" --rpcport 8545 --rpccorsdomain "*" --rpcapi "admin,db,eth,debug,miner,net,shh,txpool,personal,web3" --unlock 0 --verbosity 6 console 2>> /home/eth/data_testnet/geth.log 173 | 174 | 175 | // 패스워드 파일 준비 176 | echo pass0 >> /home/eth/data_testnet/passwd 177 | echo pass1 >> /home/eth/data_testnet/passwd 178 | cat /home/eth/data_testnet/passwd 179 | pass0 180 | pass1 181 | 182 | // Geth 실행시 계정 잠금 해제 (패스워드 파일 사용) 183 | geth --networkid 4649 --nodiscover --maxpeers 0 --datadir /home/eth/data_testnet --mine --minerthreads 1 --rpc --rpcaddr "0.0.0.0" --rpcport 8545 --rpccorsdomain "*" --rpcapi "admin,db,eth,debug,miner,net,shh,txpool,personal,web3" --unlock 0,1 --password /home/eth/data_testnet/passwd --verbosity 6 console 2>> /home/eth/data_testnet/geth.log 184 | 185 | // 백그라운드로 Geth를 실행할 때 계정 잠금 해제 (패스워드 파일 사용) 186 | nohup geth --networkid 4649 --nodiscover --maxpeers 0 --datadir /home/eth/data_testnet --mine --minerthreads 1 --rpc --rpcaddr "0.0.0.0" --rpcport 8545 --rpccorsdomain "*" --rpcapi "admin,db,eth,debug,miner,net,shh,txpool,personal,web3" --unlock 0,1 --password /home/eth/data_testnet/passwd --verbosity 6 2>> /home/eth/data_testnet/geth.log & 187 | 188 | // Geth 콘솔에 접속 189 | geth attach rpc:http://localhost:8545 190 | 191 | -------------------------------------------------------------------------------- /chap3/ch3_01_HelloWorld_comment.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; // (1) 버전 프라그마 2 | 3 | // (2) 계약 선언 4 | contract HelloWorld { 5 | // (3) 상태 변수 선언 6 | string public greeting; 7 | // (4) 생성자 8 | function HelloWorld(string _greeting) { 9 | greeting = _greeting; 10 | } 11 | // (5) 메서드 선언 12 | function setGreeting(string _greeting) { 13 | greeting = _greeting; 14 | } 15 | function say() constant returns (string) { 16 | return greeting; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /chap3/ch3_02_HelloWorldOrg.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | contract HelloWorld { 3 | string public greeting; 4 | function HelloWorld(string _greeting) { 5 | greeting = _greeting; 6 | } 7 | function setGreeting(string _greeting) { 8 | greeting = _greeting; 9 | } 10 | function say() constant returns (string) { 11 | return greeting; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /chap3/ch3_03_HelloWorld.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8;contract HelloWorld { string public greeting; function HelloWorld(string _greeting) { greeting = _greeting; } function setGreeting(string _greeting) { greeting = _greeting; } function say() constant returns (string) { return greeting; }} -------------------------------------------------------------------------------- /chap3/ch3_04_DataTypeSample.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract DataTypeSample { 4 | function getValueType() constant returns (uint) { 5 | uint a; // uint형 변수 a를 선언. 이 시점에서 a는 0으로 초기화된다. 6 | a = 1; // a의 값이 1이 된다. 7 | uint b = a; // 변수 a에 a의 값 1이 대입 8 | b = 2; // b의 값이 2가 된다. 9 | return a; // a의 값인 1이 반환 10 | } 11 | 12 | function getReferenceType() constant return (uint[2]) { 13 | uint[2] a; // uint 형식을 가진 배열 변수 a를 선언 14 | a[0] = 1; // 배열의 첫 번째 요소의 값에 1을 대입. 15 | a[1] = 2; // 배열의 두 번째 요소의 값에 2를 대입. 16 | uint[2] b = a; // uint 형식을 가진 배열 변수 b를 선언하고 a를 b에 대입. a는 데이터 영역 주소이기 때문에 b는 a와 동일한 데이터 영역을 참조함 17 | b[0] = 10; // b와 a는 같은 데이터 영역을 참조하기 때문에 a[0]도 10이 된다. 18 | b[1] = 20; // 마찬가지로 a[1]도 20이 된다 19 | return a; // 10, 20이 반환된다. 20 | } 21 | } -------------------------------------------------------------------------------- /chap3/ch3_05_IntSample.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract IntSample { 4 | function division() constant returns (uint) { 5 | uint a = 3; 6 | uint b = 2; 7 | uint c = a / b * 10 // a / b 의 결과는 1이다. 8 | return c; // 10이 반환된다. 9 | } 10 | function divisionLiterals() constant returns (uint) { 11 | uint c = 3 / 2 * 10; // 상수이기 때문에 a / b의 나머지를 버리지 않는다. 즉 1.5가 된다. 12 | return c; // 15가 반환된다. 13 | } 14 | function divisionByZero() constant returns (uint) { 15 | uint a = 3; 16 | uint c = a / 0; // 컴파일은 되지만 실행 시 예외가 발생한다. 17 | return c; // uint c = 3 / 0으로 하면 컴파일도 진행되지 않는다. 18 | } 19 | function shift() constant returns (uint[2]) { 20 | uint[2] a; 21 | a[0] = 16 << 2; // 16 * 2 ** 2 = 64 22 | a[1] = 16 >> 2; // 16 / 2 ** 2 = 4 23 | return a; // 64, 4가 반환된다. 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /chap3/ch3_06_AddressSample.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract AddressSample { 4 | // 이름 없는 함수(송금되면 실행된다) payable을 지정해 Ether를 받는 것이 가능 5 | function () payable {} 6 | function getBalance(address _target) constant returns (uint) { 7 | if (_target == address(0)) { // _target이 0인 경우 계약 자신의 주소를 할당 8 | _target = this; 9 | } 10 | return _target.balance; // 잔고 반환 11 | } 12 | // 이후, 송금 메서드를 실행하기 전 이 계약에 대해 송금해둬야 한다 13 | // 인수로 지정된 주소에 transfer를 사용해 송금 14 | function send(address _to, uint _amount) { 15 | if (!_to.send(_amount)) { // send를 사용할 경우 반환값을 체크해야 한다 16 | throw; 17 | } 18 | } 19 | // 인수로 지정된 주소에 call을 사용해 송금 20 | function call(address _to, uint _amount) { 21 | if (!_to.call.value(_amount).gas(1000000)()) { // call도 반환값을 체크해야 한다 22 | throw; // 23 | } 24 | } 25 | // 인출 패턴(transfer) 26 | function withdraw() { 27 | address to = msg.sender; // 메서드 실행자를 받는 사람으로 한다 28 | to.transfer(this.balance); // 전액 송금한다 29 | } 30 | // 인출 패턴(call) 31 | function withdraw2() { 32 | address to = msg.sender; // 메서드 실행자를 받는 사람으로 한다 33 | if (!to.call.value(this.balance).gas(1000000)()) { // 전액 송금한다 34 | throw; 35 | } 36 | } 37 | } 38 | 39 | -------------------------------------------------------------------------------- /chap3/ch3_07_ArraySample.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract ArraySample { 4 | uint[5] public fArray = [uint(10), 20, 30, 40, 50]; // 고정 길이 배열의 선언 및 초기화 5 | uint[] public dArray; // 가변 길이 배열 선언 6 | function getFixedArray() constant returns (uint[5]) { 7 | uint[5] storage a = fArray; // 길이가 5인 고정 배열을 선언 8 | // 메서드 안에서는 이 형식으로 초기화할 수 없다. 9 | // uint[5] b = [uint(1), 2, 3, 4, 5] 10 | for (uint i = 0; i < a.length; i++) { // 초기화 11 | a[i] = i + 1; 12 | } 13 | return a; // [1, 2, 3, 4, 5]를 반환 14 | } 15 | function getFixedArray2() constant returns (uint[5]) { 16 | uint[5] storage b = fArray; // 상태 변수로 초기화 17 | return b; // [10, 20, 30, 40, 50] 을 반환 18 | } 19 | function pushFixedArray(uint x) constant returns (uint) { 20 | // 다음은 컴파일 오류가 발생한다 21 | // fArray.push(x); 22 | return fArray.length; 23 | } 24 | function pushDArray(uint x) returns (uint) { 25 | return dArray.push(x); // 인수로 받은 요소를 추가하고 변경 후의 배열 길이를 반환 26 | } 27 | function getDArrayLength() returns (uint) { 28 | return dArray.length; // 가변 길이 배열의 현재 크기를 반환 29 | } 30 | function initDArray(uint len) { 31 | dArray.length = len; // 가변 길이 배열의 크기를 변경 32 | for (uint i = 0; i < len; i++) { //초기화 33 | dArray[i] = i + 1; 34 | } 35 | } 36 | function getDArray() constant returns (uint[]) { 37 | return dArray; // 가변 길이 배열도 반환 38 | } 39 | function delDArray() returns (uint) { 40 | delete dArray; // 가변 길이 배열 삭제 41 | return dArray.length; // 0을 반환 42 | } 43 | function delFArray() returns (uint) { 44 | delete fArray; // 고정 길이 배열 삭제. 각 요소는 0이 된다 45 | return fArray.length; // 길이는 변하지 않기 때문에 5를 반환 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /chap3/ch3_08_StructSample.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract StructSample { 4 | struct User { // 구조체 선언 (C 언어와 동일) 5 | address addr; 6 | string name; 7 | } 8 | User[] public userList; // 구조체의 배열도 선언할 수 있다 9 | function addUser(string _name) returns (uint) { // 사용자 추가 10 | uint id = userList.push(User({ // 배열의 가장 마지막에 추가한다 11 | addr: msg.sender, 12 | name: _name 13 | })); 14 | return (id - 1); 15 | } 16 | function addUser2(string _name) returns (uint) { // 사용자 추가 17 | userList.length += 1; // 배열의 길이를 1만큼 증가시킨다 18 | uint id = userList.length - 1; 19 | userList[id].addr = msg.sender; 20 | userList[id].name = _name; 21 | return id; 22 | } 23 | function editUser(uint _id, string _name) { 24 | if (userList.length <= _id || // id가 배열의 길이 이상 25 | userList[_id].addr != msg.sender) // 주소가 등록된 것과 다르다 26 | { 27 | throw; // 예외 처리 28 | } 29 | userList[_id].name = _name; 30 | } 31 | 32 | // 구조체는 직접 반환하지 않기 때문에 다음 메서드는 컴파일 오류가 발생한다 33 | // function getUser(uint _id) constant returns (User) { 34 | // return userList[_id]; 35 | // } 36 | // 아래 메서드는 문제 없음 37 | function getUser(uint _id) constant returns (address, string) { 38 | return (userList[_id].addr, userList[_id].name); 39 | } 40 | } 41 | 42 | -------------------------------------------------------------------------------- /chap3/ch3_09_MappingSample.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract MappingSample { 4 | struct User { 5 | string name; 6 | uint age; 7 | } 8 | mapping(address=>User) public userList; // value를 구조체(User)로 설정 9 | 10 | function setUser(string _name, uint _age) { 11 | userList[msg.sender].name = _name; // key를 지정해 접근한다 12 | userList[msg.sender].age = _age; 13 | } 14 | 15 | function getUser() returns (string, uint) { 16 | User u = userList[msg.sender]; 17 | return (u.name, u.age); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /chap3/ch3_10_EtherUnitSample.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract EtherUnitSample { 4 | function () payable {} // Ether를 받는 메서드 5 | 6 | // getEther 실행 전에 이 계약에 1 ether를 송금해야 한다 7 | function getEther() constant returns (uint _wei, uint _szabo, uint _finney, uint _ether) { 8 | uint amount = this.balance; // 1000000000000000000 9 | _wei = amount / 1 wei; // 1000000000000000000 10 | _szabo = _wei / 1 szabo; // 1000000 11 | _finney = _wei / 1 finney; // 1000 12 | _ether = _wei / 1 ether; // 1 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /chap3/ch3_11_TimeUnitSample.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract TimeUnitSample { 4 | uint public startTime; // 시작 시간 5 | // 시작 6 | function start() { 7 | startTime = now; // now는 block.timestamp의 별칭(Alias) 8 | } 9 | // 시작 시간으로부터 지정한 '분'만큼 경과했는지 확인(bool 형태로 반환) 10 | function minutesAfter(uint min) constant returns (bool) { 11 | if (startTime == 0) return false; // 시작 전에는 false를 반환 12 | return ((now - startTime) / 1 minutes >= min); 13 | } 14 | // 경과한 '초'를 반환 15 | function getSeconds() constant returns (uint) { 16 | if (startTime == 0) return 0; // 시작 전에는 0을 반환 17 | return (now - startTime); 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /chap3/ch3_12_Inheritance.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract A { 4 | uint public a; 5 | function setA(uint _a) { 6 | a = _a; 7 | } 8 | function getData() constant returns (uint) { 9 | return a; // a를 그대로 반환 10 | } 11 | } 12 | 13 | contract B is A { // B는 A의 하위 계약 14 | function getData() constant returns (uint) { 15 | return a * 10; // a * 10을 반환 16 | } 17 | } 18 | 19 | contract C { 20 | A[] internal c; // 데이터 형식을 계약 A 형식의 가변 길이 배열로 설정해 c로 선언 21 | function makeContract() returns(uint, uint) { 22 | c.length = 2; // c의 길이를 2로 설정 23 | A a = new A(); // 계약 A를 a로 생성 24 | a.setA(1); // 1을 할당 25 | c[0] = a; // 배열의 첫 번째 요소에 a를 대입 26 | B b = new B(); // 계약 B를 b로 생성 27 | b.setA(1); // 마찬가지로 1을 할당 28 | c[1] = b; // 배열의 두 번째 요소에 b를 대입 29 | return (c[0].getData(), c[1].getData()); // 계약 A와 B의 반환값을 출력 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /chap3/ch3_13_Inheritance2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract A { 4 | uint public num = 10; // 10으로 고정한다(public이기 때문에 외부에서 참조 가능). 5 | function getNum() constant returns (uint) { 6 | return num; 7 | } 8 | } 9 | 10 | contract B { 11 | A a = new A(); 12 | address public addr; 13 | function setA(A _a) { // 별도로 생성한 A의 주소를 설정한다. 14 | addr = _a; // 주소에 저장 15 | } 16 | // 상태 변수num의 값을 직접 취득 17 | function aNum() constant returns (uint) { 18 | return a.num(); // 10; 19 | } 20 | // 메서드로부터 num의 값을 취득 21 | function aGetNum() constant returns (uint) { 22 | return a.getNum(); // 10 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /chap3/ch3_14_SelfDestructSample.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract SelfDestructSample { 4 | address public owner = msg.sender; // 계약을 배포한 주소를 소유자로 한다 5 | //송금을 받는다(close() 뒤에 호출하면 송금도 할 수 없게 된다) 6 | function () payable { } 7 | // 계약을 파기하는 메서드 8 | function close() { 9 | if (owner != msg.sender) throw; // 보내는 사람이 소유자가 아닌 경우는 예외 처리 10 | selfdestruct(owner); // 계약을 파기한다 11 | } 12 | // 계약 잔고를 반환하는 메서드 13 | function Balance() constant returns (uint) { // close() 뒤에 호출하면 오류 발생 14 | return this.balance; 15 | } 16 | } 17 | 18 | -------------------------------------------------------------------------------- /chap3/ch3_cmd.txt: -------------------------------------------------------------------------------- 1 | // 컴파일러(solc) 설치 2 | sudo add-apt-repository ppa:ethereum/ethereum 3 | sudo apt-get update 4 | sudo apt-get install solc 5 | solc --version 6 | which solc 7 | 8 | // Geth 실행 (디렉토리는 적절하게 변경한다) 9 | nohup geth --networkid 4649 --nodiscover --maxpeers 0 --datadir /home/eth/data_testnet --mine --minerthreads 1 --rpc --rpcaddr "0.0.0.0" --rpcport 8545 --rpccorsdomain "*" --rpcapi "admin,db,eth,debug,miner,net,shh,txpool,personal,web3" --unlock 0,1 --password /home/eth/data_testnet/passwd --verbosity 6 2>> /home/eth/data_testnet/geth.log & 10 | 11 | // Geth 콘솔에 접속 12 | geth attach rpc:http://localhost:8545 13 | 14 | // Geth에 solc 경로 지정 15 | admin.setSolc("/usr/bin/solc") 16 | 17 | // 확인 18 | eth.getCompilers() 19 | 20 | -------------------------------------------------------------------------------- /chap4/ch4_01_OreOreCoin.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract OreOreCoin { 4 | // (1) 상태 변수 선언 5 | string public name; // 토큰 이름 6 | string public symbol; // 토큰 단위 7 | uint8 public decimals; // 소수점 이하 자릿수 8 | uint256 public totalSupply; // 토큰 총량 9 | mapping (address => uint256) public balanceOf; // 각 주소의 잔고 10 | 11 | // (2) 이벤트 알림 12 | event Transfer(address indexed from, address indexed to, uint256 value); 13 | 14 | // (3) 생성자 15 | function OreOreCoin(uint256 _supply, string _name, string _symbol, uint8 _decimals) { 16 | balanceOf[msg.sender] = _supply; 17 | name = _name; 18 | symbol = _symbol; 19 | decimals = _decimals; 20 | totalSupply = _supply; 21 | } 22 | 23 | // (4) 송금 24 | function transfer(address _to, uint256 _value) { 25 | // (5) 부정 송금 확인 26 | if (balanceOf[msg.sender] < _value) throw; 27 | if (balanceOf[_to] + _value < balanceOf[_to]) throw; 28 | // (6) 송금하는 주소와 송금받는 주소의 잔고 갱신 29 | balanceOf[msg.sender] -= _value; 30 | balanceOf[_to] += _value; 31 | // (7) 이벤트 알림 32 | Transfer(msg.sender, _to, _value); 33 | } 34 | } 35 | 36 | -------------------------------------------------------------------------------- /chap4/ch4_02_OreOreCoin.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | // 블랙리스트 기능을 추가한 가상 화폐 4 | contract OreOreCoin { 5 | // (1) 상태 변수 선언 6 | string public name; // 토큰 이름 7 | string public symbol; // 토큰 단위 8 | uint8 public decimals; // 소수점 이하 자릿수 9 | uint256 public totalSupply; // 토큰 총량 10 | mapping (address => uint256) public balanceOf; // 각 주소의 잔고 11 | mapping (address => int8) public blackList; // 블랙리스트 12 | address public owner; // 소유자 주소 13 | 14 | // (2) 수식자 15 | modifier onlyOwner() { if (msg.sender != owner) throw; _; } 16 | 17 | // (3) 이벤트 알림 18 | event Transfer(address indexed from, address indexed to, uint256 value); 19 | event Blacklisted(address indexed target); 20 | event DeleteFromBlacklist(address indexed target); 21 | event RejectedPaymentToBlacklistedAddr(address indexed from, address indexed to, uint256 value); 22 | event RejectedPaymentFromBlacklistedAddr(address indexed from, address indexed to, uint256 value); 23 | 24 | // (4) 생성자 25 | function OreOreCoin(uint256 _supply, string _name, string _symbol, uint8 _decimals) { 26 | balanceOf[msg.sender] = _supply; 27 | name = _name; 28 | symbol = _symbol; 29 | decimals = _decimals; 30 | totalSupply = _supply; 31 | owner = msg.sender; // 소유자 주소 설정 32 | } 33 | 34 | // (5) 주소를 블랙리스트에 등록 35 | function blacklisting(address _addr) onlyOwner { 36 | blackList[_addr] = 1; 37 | Blacklisted(_addr); 38 | } 39 | 40 | // (6) 주소를 블랙리스트에서 제거 41 | function deleteFromBlacklist(address _addr) onlyOwner { 42 | blackList[_addr] = -1; 43 | DeleteFromBlacklist(_addr); 44 | } 45 | 46 | // (7) 송금 47 | function transfer(address _to, uint256 _value) { 48 | // 부정 송금 확인 49 | if (balanceOf[msg.sender] < _value) throw; 50 | if (balanceOf[_to] + _value < balanceOf[_to]) throw; 51 | // 블랙리스트에 존재하는 주소는 입출금 불가 52 | if (blackList[msg.sender] > 0) { 53 | RejectedPaymentFromBlacklistedAddr(msg.sender, _to, _value); 54 | } else if (blackList[_to] > 0) { 55 | RejectedPaymentToBlacklistedAddr(msg.sender, _to, _value); 56 | } else { 57 | balanceOf[msg.sender] -= _value; 58 | balanceOf[_to] += _value; 59 | Transfer(msg.sender, _to, _value); 60 | } 61 | } 62 | } 63 | 64 | -------------------------------------------------------------------------------- /chap4/ch4_03_OreOreCoin.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | // 캐시백 기능이 추가된 가상 화폐 4 | contract OreOreCoin { 5 | // (1) 상태 변수 선언 6 | string public name; // 토큰 이름 7 | string public symbol; // 토큰 단위 8 | uint8 public decimals; // 소수점 이하 자릿수 9 | uint256 public totalSupply; // 토큰 총량 10 | mapping (address => uint256) public balanceOf; // 각 주소의 잔고 11 | mapping (address => int8) public blackList; // 블랙리스트 12 | mapping (address => int8) public cashbackRate; // 각 주소의 캐시백 비율 13 | address public owner; // 소유자 주소 14 | 15 | // 수식자 16 | modifier onlyOwner() { if (msg.sender != owner) throw; _; } 17 | 18 | // (2) 이벤트 알림 19 | event Transfer(address indexed from, address indexed to, uint256 value); 20 | event Blacklisted(address indexed target); 21 | event DeleteFromBlacklist(address indexed target); 22 | event RejectedPaymentToBlacklistedAddr(address indexed from, address indexed to, uint256 value); 23 | event RejectedPaymentFromBlacklistedAddr(address indexed from, address indexed to, uint256 value); 24 | event SetCashback(address indexed addr, int8 rate); 25 | event Cashback(address indexed from, address indexed to, uint256 value); 26 | 27 | // 생성자 28 | function OreOreCoin(uint256 _supply, string _name, string _symbol, uint8 _decimals) { 29 | balanceOf[msg.sender] = _supply; 30 | name = _name; 31 | symbol = _symbol; 32 | decimals = _decimals; 33 | totalSupply = _supply; 34 | owner = msg.sender; 35 | } 36 | 37 | // 주소를 블랙리스트에 등록 38 | function blacklisting(address _addr) onlyOwner { 39 | blackList[_addr] = 1; 40 | Blacklisted(_addr); 41 | } 42 | 43 | // 주소를 블랙리스트에서 제거 44 | function deleteFromBlacklist(address _addr) onlyOwner { 45 | blackList[_addr] = -1; 46 | DeleteFromBlacklist(_addr); 47 | } 48 | 49 | // (3) 캐시백 비율 설정 50 | function setCashbackRate(int8 _rate) { 51 | if (_rate < 1) { 52 | _rate = -1; 53 | } else if (_rate > 100) { 54 | _rate = 100; 55 | } 56 | cashbackRate[msg.sender] = _rate; 57 | if (_rate < 1) { 58 | _rate = 0; 59 | } 60 | SetCashback(msg.sender, _rate); 61 | } 62 | 63 | // 송금 64 | function transfer(address _to, uint256 _value) { 65 | // 부정 송금 확인 66 | if (balanceOf[msg.sender] < _value) throw; 67 | if (balanceOf[_to] + _value < balanceOf[_to]) throw; 68 | 69 | // 블랙리스트에 존재하는 주소는 입출금 불가 70 | if (blackList[msg.sender] > 0) { 71 | RejectedPaymentFromBlacklistedAddr(msg.sender, _to, _value); 72 | } else if (blackList[_to] > 0) { 73 | RejectedPaymentToBlacklistedAddr(msg.sender, _to, _value); 74 | } else { 75 | // (4) 캐시백 금액 계산(각 대상의 캐시백 비율을 사용) 76 | uint256 cashback = 0; 77 | if(cashbackRate[_to] > 0) cashback = _value / 100 * uint256(cashbackRate[_to]); 78 | 79 | balanceOf[msg.sender] -= (_value - cashback); 80 | balanceOf[_to] += (_value - cashback); 81 | 82 | Transfer(msg.sender, _to, _value); 83 | Cashback(_to, msg.sender, cashback); 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /chap4/ch4_04_OreOreCoin.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | // 소유자 관리용 계약 4 | contract Owned { 5 | // 상태 변수 6 | address public owner; // 소유자 주소 7 | 8 | // 소유자 변경 시 이벤트 9 | event TransferOwnership(address oldaddr, address newaddr); 10 | 11 | // 소유자 한정 메서드용 수식자 12 | modifier onlyOwner() { if (msg.sender != owner) throw; _; } 13 | 14 | // 생성자 15 | function Owned() { 16 | owner = msg.sender; // 처음에 계약을 생성한 주소를 소유자로 한다 17 | } 18 | 19 | // (1) 소유자 변경 20 | function transferOwnership(address _new) onlyOwner { 21 | address oldaddr = owner; 22 | owner = _new; 23 | TransferOwnership(oldaddr, owner); 24 | } 25 | } 26 | 27 | // (2) 회원 관리용 계약 28 | contract Members is Owned { 29 | // (3) 상태 변수 선언 30 | address public coin; // 토큰(가상 화폐) 주소 31 | MemberStatus[] public status; // 회원 등급 배열 32 | mapping(address => History) public tradingHistory; // 회원별 거래 이력 33 | 34 | // (4) 회원 등급용 구조체 35 | struct MemberStatus { 36 | string name; // 등급명 37 | uint256 times; // 최저 거래 회수 38 | uint256 sum; // 최저 거래 금액 39 | int8 rate; // 캐시백 비율 40 | } 41 | // 거래 이력용 구조체 42 | struct History { 43 | uint256 times; // 거래 회수 44 | uint256 sum; // 거래 금액 45 | uint256 statusIndex; // 등급 인덱스 46 | } 47 | 48 | // (5) 토큰 한정 메서드용 수식자 49 | modifier onlyCoin() { if (msg.sender == coin) _; } 50 | 51 | // (6) 토큰 주소 설정 52 | function setCoin(address _addr) onlyOwner { 53 | coin = _addr; 54 | } 55 | 56 | // (7) 회원 등급 추가 57 | function pushStatus(string _name, uint256 _times, uint256 _sum, int8 _rate) onlyOwner { 58 | status.push(MemberStatus({ 59 | name: _name, 60 | times: _times, 61 | sum: _sum, 62 | rate: _rate 63 | })); 64 | } 65 | 66 | // (8) 회원 등급 내용 변경 67 | function editStatus(uint256 _index, string _name, uint256 _times, uint256 _sum, int8 _rate) onlyOwner { 68 | if (_index < status.length) { 69 | status[_index].name = _name; 70 | status[_index].times = _times; 71 | status[_index].sum = _sum; 72 | status[_index].rate = _rate; 73 | } 74 | } 75 | 76 | // (9) 거래 내역 갱신 77 | function updateHistory(address _member, uint256 _value) onlyCoin { 78 | tradingHistory[_member].times += 1; 79 | tradingHistory[_member].sum += _value; 80 | // 새로운 회원 등급 결정(거래마다 실행) 81 | uint256 index; 82 | int8 tmprate; 83 | for (uint i = 0; i < status.length; i++) { 84 | // 최저 거래 횟수, 최저 거래 금액 충족 시 가장 캐시백 비율이 좋은 등급으로 설정 85 | if (tradingHistory[_member].times >= status[i].times && 86 | tradingHistory[_member].sum >= status[i].sum && 87 | tmprate < status[i].rate) { 88 | index = i; 89 | } 90 | } 91 | tradingHistory[_member].statusIndex = index; 92 | } 93 | 94 | // (10) 캐시백 비율 획득(회원의 등급에 해당하는 비율 확인) 95 | function getCashbackRate(address _member) constant returns (int8 rate) { 96 | rate = status[tradingHistory[_member].statusIndex].rate; 97 | } 98 | } 99 | 100 | // (11) 회원 관리 기능이 구현된 가상 화폐 101 | contract OreOreCoin is Owned{ 102 | // 상태 변수 선언 103 | string public name; // 토큰 이름 104 | string public symbol; // 토큰 단위 105 | uint8 public decimals; // 소수점 이하 자릿수 106 | uint256 public totalSupply; // 토큰 총량 107 | mapping (address => uint256) public balanceOf; // 각 주소의 잔고 108 | mapping (address => int8) public blackList; // 블랙리스트 109 | mapping (address => Members) public members; // 각 주소의 회원 정보 110 | 111 | // 이벤트 알림 112 | event Transfer(address indexed from, address indexed to, uint256 value); 113 | event Blacklisted(address indexed target); 114 | event DeleteFromBlacklist(address indexed target); 115 | event RejectedPaymentToBlacklistedAddr(address indexed from, address indexed to, uint256 value); 116 | event RejectedPaymentFromBlacklistedAddr(address indexed from, address indexed to, uint256 value); 117 | event Cashback(address indexed from, address indexed to, uint256 value); 118 | 119 | // 생성자 120 | function OreOreCoin(uint256 _supply, string _name, string _symbol, uint8 _decimals) { 121 | balanceOf[msg.sender] = _supply; 122 | name = _name; 123 | symbol = _symbol; 124 | decimals = _decimals; 125 | totalSupply = _supply; 126 | } 127 | 128 | // 주소를 블랙리스트에 등록 129 | function blacklisting(address _addr) onlyOwner { 130 | blackList[_addr] = 1; 131 | Blacklisted(_addr); 132 | } 133 | 134 | // 주소를 블랙리스트에서 해제 135 | function deleteFromBlacklist(address _addr) onlyOwner { 136 | blackList[_addr] = -1; 137 | DeleteFromBlacklist(_addr); 138 | } 139 | 140 | // 회원 관리 계약 설정 141 | function setMembers(Members _members) { 142 | members[msg.sender] = Members(_members); 143 | } 144 | 145 | // 송금 146 | function transfer(address _to, uint256 _value) { 147 | // 부정 송금 확인 148 | if (balanceOf[msg.sender] < _value) throw; 149 | if (balanceOf[_to] + _value < balanceOf[_to]) throw; 150 | 151 | // 블랙리스트에 존재하는 계정은 입출금 불가 152 | if (blackList[msg.sender] > 0) { 153 | RejectedPaymentFromBlacklistedAddr(msg.sender, _to, _value); 154 | } else if (blackList[_to] > 0) { 155 | RejectedPaymentToBlacklistedAddr(msg.sender, _to, _value); 156 | } else { 157 | // (12) 캐시백 금액을 계산(각 대상의 비율을 사용) 158 | uint256 cashback = 0; 159 | if(members[_to] > address(0)) { 160 | cashback = _value / 100 * uint256(members[_to].getCashbackRate(msg.sender)); 161 | members[_to].updateHistory(msg.sender, _value); 162 | } 163 | 164 | balanceOf[msg.sender] -= (_value - cashback); 165 | balanceOf[_to] += (_value - cashback); 166 | 167 | Transfer(msg.sender, _to, _value); 168 | Cashback(_to, msg.sender, cashback); 169 | } 170 | } 171 | } 172 | 173 | 174 | -------------------------------------------------------------------------------- /chap4/ch4_05_OreOreCoin.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | // 소유자 관리용 계약 4 | contract Owned { 5 | // 상태 변수 6 | address public owner; // 소유자 주소 7 | 8 | // 소유자 변경 시 이벤트 9 | event TransferOwnership(address oldaddr, address newaddr); 10 | 11 | // 소유자 한정 메서드용 수식자 12 | modifier onlyOwner() { if (msg.sender != owner) throw; _; } 13 | 14 | // 생성자 15 | function Owned() { 16 | owner = msg.sender; // 처음에 계약을 생성한 주소를 소유자로 한다 17 | } 18 | 19 | // (1) 소유자 변경 20 | function transferOwnership(address _new) onlyOwner { 21 | address oldaddr = owner; 22 | owner = _new; 23 | TransferOwnership(oldaddr, owner); 24 | } 25 | } 26 | 27 | // (2) 회원 관리용 계약 28 | contract Members is Owned { 29 | // (3) 상태 변수 선언 30 | address public coin; // 토큰(가상 화폐) 주소 31 | MemberStatus[] public status; // 회원 등급 배열 32 | mapping(address => History) public tradingHistory; // 회원별 거래 이력 33 | 34 | // (4) 회원 등급용 구조체 35 | struct MemberStatus { 36 | string name; // 등급명 37 | uint256 times; // 최저 거래 회수 38 | uint256 sum; // 최저 거래 금액 39 | int8 rate; // 캐시백 비율 40 | } 41 | // 거래 이력용 구조체 42 | struct History { 43 | uint256 times; // 거래 회수 44 | uint256 sum; // 거래 금액 45 | uint256 statusIndex; // 등급 인덱스 46 | } 47 | 48 | // (5) 토큰 한정 메서드용 수식자 49 | modifier onlyCoin() { if (msg.sender == coin) _; } 50 | 51 | // (6) 토큰 주소 설정 52 | function setCoin(address _addr) onlyOwner { 53 | coin = _addr; 54 | } 55 | 56 | // (7) 회원 등급 추가 57 | function pushStatus(string _name, uint256 _times, uint256 _sum, int8 _rate) onlyOwner { 58 | status.push(MemberStatus({ 59 | name: _name, 60 | times: _times, 61 | sum: _sum, 62 | rate: _rate 63 | })); 64 | } 65 | 66 | // (8) 회원 등급 내용 변경 67 | function editStatus(uint256 _index, string _name, uint256 _times, uint256 _sum, int8 _rate) onlyOwner { 68 | if (_index < status.length) { 69 | status[_index].name = _name; 70 | status[_index].times = _times; 71 | status[_index].sum = _sum; 72 | status[_index].rate = _rate; 73 | } 74 | } 75 | 76 | // (9) 거래 내역 갱신 77 | function updateHistory(address _member, uint256 _value) onlyCoin { 78 | tradingHistory[_member].times += 1; 79 | tradingHistory[_member].sum += _value; 80 | // 새로운 회원 등급 결정(거래마다 실행) 81 | uint256 index; 82 | int8 tmprate; 83 | for (uint i = 0; i < status.length; i++) { 84 | // 최저 거래 횟수, 최저 거래 금액 충족 시 가장 캐시백 비율이 좋은 등급으로 설정 85 | if (tradingHistory[_member].times >= status[i].times && 86 | tradingHistory[_member].sum >= status[i].sum && 87 | tmprate < status[i].rate) { 88 | index = i; 89 | } 90 | } 91 | tradingHistory[_member].statusIndex = index; 92 | } 93 | 94 | // (10) 캐시백 비율 획득(회원의 등급에 해당하는 비율 확인) 95 | function getCashbackRate(address _member) constant returns (int8 rate) { 96 | rate = status[tradingHistory[_member].statusIndex].rate; 97 | } 98 | } 99 | 100 | // (11) 회원 관리 기능이 구현된 가상 화폐 101 | contract OreOreCoin is Owned{ 102 | // 상태 변수 선언 103 | string public name; // 토큰 이름 104 | string public symbol; // 토큰 단위 105 | uint8 public decimals; // 소수점 이하 자릿수 106 | uint256 public totalSupply; // 토큰 총량 107 | mapping (address => uint256) public balanceOf; // 각 주소의 잔고 108 | mapping (address => int8) public blackList; // 블랙리스트 109 | mapping (address => Members) public members; // 각 주소의 회원 정보 110 | 111 | // 이벤트 알림 112 | event Transfer(address indexed from, address indexed to, uint256 value); 113 | event Blacklisted(address indexed target); 114 | event DeleteFromBlacklist(address indexed target); 115 | event RejectedPaymentToBlacklistedAddr(address indexed from, address indexed to, uint256 value); 116 | event RejectedPaymentFromBlacklistedAddr(address indexed from, address indexed to, uint256 value); 117 | event Cashback(address indexed from, address indexed to, uint256 value); 118 | 119 | // 생성자 120 | function OreOreCoin(uint256 _supply, string _name, string _symbol, uint8 _decimals) { 121 | balanceOf[msg.sender] = _supply; 122 | name = _name; 123 | symbol = _symbol; 124 | decimals = _decimals; 125 | totalSupply = _supply; 126 | } 127 | 128 | // 주소를 블랙리스트에 등록 129 | function blacklisting(address _addr) onlyOwner { 130 | blackList[_addr] = 1; 131 | Blacklisted(_addr); 132 | } 133 | 134 | // 주소를 블랙리스트에서 해제 135 | function deleteFromBlacklist(address _addr) onlyOwner { 136 | blackList[_addr] = -1; 137 | DeleteFromBlacklist(_addr); 138 | } 139 | 140 | // 회원 관리 계약 설정 141 | function setMembers(Members _members) { 142 | members[msg.sender] = Members(_members); 143 | } 144 | 145 | // 송금 146 | function transfer(address _to, uint256 _value) { 147 | // 부정 송금 확인 148 | if (balanceOf[msg.sender] < _value) throw; 149 | if (balanceOf[_to] + _value < balanceOf[_to]) throw; 150 | 151 | // 블랙리스트에 존재하는 계정은 입출금 불가 152 | if (blackList[msg.sender] > 0) { 153 | RejectedPaymentFromBlacklistedAddr(msg.sender, _to, _value); 154 | } else if (blackList[_to] > 0) { 155 | RejectedPaymentToBlacklistedAddr(msg.sender, _to, _value); 156 | } else { 157 | // (12) 캐시백 금액을 계산(각 대상의 비율을 사용) 158 | uint256 cashback = 0; 159 | if(members[_to] > address(0)) { 160 | cashback = _value / 100 * uint256(members[_to].getCashbackRate(msg.sender)); 161 | members[_to].updateHistory(msg.sender, _value); 162 | } 163 | 164 | balanceOf[msg.sender] -= (_value - cashback); 165 | balanceOf[_to] += (_value - cashback); 166 | 167 | Transfer(msg.sender, _to, _value); 168 | Cashback(_to, msg.sender, cashback); 169 | } 170 | } 171 | } 172 | 173 | // (1) 크라우드 세일 174 | contract Crowdsale is Owned { 175 | // (2) 상태 변수 176 | uint256 public fundingGoal; // 목표 금액 177 | uint256 public deadline; // 기한 178 | uint256 public price; // 토큰 기본 가격 179 | uint256 public transferableToken; // 전송 가능 토큰 180 | uint256 public soldToken; // 판매된 토큰 181 | uint256 public startTime; // 개시 시간 182 | OreOreCoin public tokenReward; // 지불에 사용할 토큰 183 | bool public fundingGoalReached; // 목표 도달 플래그 184 | bool public isOpened; // 크라우드 세일 개시 플래그 185 | mapping (address => Property) public fundersProperty; // 자금 제공자의 자산 정보 186 | 187 | // (3) 자산정보 구조체 188 | struct Property { 189 | uint256 paymentEther; // 지불한 Ether 190 | uint256 reservedToken; // 받은 토큰 191 | bool withdrawed; // 인출 플래그 192 | } 193 | 194 | // (4) 이벤트 알림 195 | event CrowdsaleStart(uint fundingGoal, uint deadline, uint transferableToken, address beneficiary); 196 | event ReservedToken(address backer, uint amount, uint token); 197 | event CheckGoalReached(address beneficiary, uint fundingGoal, uint amountRaised, bool reached, uint raisedToken); 198 | event WithdrawalToken(address addr, uint amount, bool result); 199 | event WithdrawalEther(address addr, uint amount, bool result); 200 | 201 | // (5) 수식자 202 | modifier afterDeadline() { if (now >= deadline) _; } 203 | 204 | // (6) 생성자 205 | function Crowdsale ( 206 | uint _fundingGoalInEthers, 207 | uint _transferableToken, 208 | uint _amountOfTokenPerEther, 209 | OreOreCoin _addressOfTokenUsedAsReward 210 | ) { 211 | fundingGoal = _fundingGoalInEthers * 1 ether; 212 | price = 1 ether / _amountOfTokenPerEther; 213 | transferableToken = _transferableToken; 214 | tokenReward = OreOreCoin(_addressOfTokenUsedAsReward); 215 | } 216 | 217 | // (7) 이름 없는 함수(Ether 받기) 218 | function () payable { 219 | // 개시 전 또는 기간이 지난 경우 예외 처리 220 | if (!isOpened || now >= deadline) throw; 221 | 222 | // 받은 Ether와 판매 예정 토큰 223 | uint amount = msg.value; 224 | uint token = amount / price * (100 + currentSwapRate()) / 100; 225 | // 판매 예정 토큰의 확인(예정 수를 초과하는 경우는 예외 처리) 226 | if (token == 0 || soldToken + token > transferableToken) throw; 227 | // 자산 제공자의 자산 정보 변경 228 | fundersProperty[msg.sender].paymentEther += amount; 229 | fundersProperty[msg.sender].reservedToken += token; 230 | soldToken += token; 231 | ReservedToken(msg.sender, amount, token); 232 | } 233 | 234 | // (8) 개시(토큰이 예정한 수 이상 있다면 개시) 235 | function start(uint _durationInMinutes) onlyOwner { 236 | if (fundingGoal == 0 || price == 0 || transferableToken == 0 || 237 | tokenReward == address(0) || _durationInMinutes == 0 || startTime != 0) 238 | { 239 | throw; 240 | } 241 | if (tokenReward.balanceOf(this) >= transferableToken) { 242 | startTime = now; 243 | deadline = now + _durationInMinutes * 1 minutes; 244 | isOpened = true; 245 | CrowdsaleStart(fundingGoal, deadline, transferableToken, owner); 246 | } 247 | } 248 | 249 | // (9) 교환 비율(개시 시작부터 시간이 적게 경과할수록 더 많은 보상) 250 | function currentSwapRate() constant returns(uint) { 251 | if (startTime + 3 minutes > now) { 252 | return 100; 253 | } else if (startTime + 5 minutes > now) { 254 | return 50; 255 | } else if (startTime + 10 minutes > now) { 256 | return 20; 257 | } else { 258 | return 0; 259 | } 260 | } 261 | 262 | // (10) 남은 시간(분 단위)과 목표와의 차이(eth 단위), 토큰 확인용 메서드 263 | function getRemainingTimeEthToken() constant returns(uint min, uint shortage, uint remainToken) { 264 | if (now < deadline) { 265 | min = (deadline - now) / (1 minutes); 266 | } 267 | shortage = (fundingGoal - this.balance) / (1 ether); 268 | remainToken = transferableToken - soldToken; 269 | } 270 | 271 | // (11) 목표 도달 확인(기한 후 실시 가능) 272 | function checkGoalReached() afterDeadline { 273 | if (isOpened) { 274 | // 모인 Ether와 목표 Ether 비교 275 | if (this.balance >= fundingGoal) { 276 | fundingGoalReached = true; 277 | } 278 | isOpened = false; 279 | CheckGoalReached(owner, fundingGoal, this.balance, fundingGoalReached, soldToken); 280 | } 281 | } 282 | 283 | // (12) 소유자용 인출 메서드(판매 종료 후 실시 가능) 284 | function withdrawalOwner() onlyOwner { 285 | if (isOpened) throw; 286 | 287 | // 목표 달성: Ether와 남은 토큰. 목표 미달: 토큰 288 | if (fundingGoalReached) { 289 | // Ether 290 | uint amount = this.balance; 291 | if (amount > 0) { 292 | bool ok = msg.sender.call.value(amount)(); 293 | WithdrawalEther(msg.sender, amount, ok); 294 | } 295 | // 남은 토큰 296 | uint val = transferableToken - soldToken; 297 | if (val > 0) { 298 | tokenReward.transfer(msg.sender, transferableToken - soldToken); 299 | WithdrawalToken(msg.sender, val, true); 300 | } 301 | } else { 302 | // 토큰 303 | uint val2 = tokenReward.balanceOf(this); 304 | tokenReward.transfer(msg.sender, val2); 305 | WithdrawalToken(msg.sender, val2, true); 306 | } 307 | } 308 | 309 | // (13) 자금 제공자용 인출 메서드(세일 종료 후 실시 가능) 310 | function withdrawal() { 311 | if (isOpened) return; 312 | // 이미 인출된 경우 예외 처리 313 | if (fundersProperty[msg.sender].withdrawed) throw; 314 | // 목표 달성: 토큰, 목표 미달 : Ether 315 | if (fundingGoalReached) { 316 | if (fundersProperty[msg.sender].reservedToken > 0) { 317 | tokenReward.transfer(msg.sender, fundersProperty[msg.sender].reservedToken); 318 | fundersProperty[msg.sender].withdrawed = true; 319 | WithdrawalToken( 320 | msg.sender, 321 | fundersProperty[msg.sender].reservedToken, 322 | fundersProperty[msg.sender].withdrawed 323 | ); 324 | } 325 | } else { 326 | if (fundersProperty[msg.sender].paymentEther > 0) { 327 | if (msg.sender.call.value(fundersProperty[msg.sender].paymentEther)()) { 328 | fundersProperty[msg.sender].withdrawed = true; 329 | } 330 | WithdrawalEther( 331 | msg.sender, 332 | fundersProperty[msg.sender].paymentEther, 333 | fundersProperty[msg.sender].withdrawed 334 | ); 335 | } 336 | } 337 | } 338 | } 339 | -------------------------------------------------------------------------------- /chap4/ch4_06_OreOreCoin.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | // 소유자 관리용 계약 4 | contract Owned { 5 | // 상태 변수 6 | address public owner; // 소유자 주소 7 | 8 | // 소유자 변경 시 이벤트 9 | event TransferOwnership(address oldaddr, address newaddr); 10 | 11 | // 소유자 한정 메서드용 수식자 12 | modifier onlyOwner() { if (msg.sender != owner) throw; _; } 13 | 14 | // 생성자 15 | function Owned() { 16 | owner = msg.sender; // 처음에 계약을 생성한 주소를 소유자로 한다 17 | } 18 | 19 | // (1) 소유자 변경 20 | function transferOwnership(address _new) onlyOwner { 21 | address oldaddr = owner; 22 | owner = _new; 23 | TransferOwnership(oldaddr, owner); 24 | } 25 | } 26 | 27 | // (2) 회원 관리용 계약 28 | contract Members is Owned { 29 | // (3) 상태 변수 선언 30 | address public coin; // 토큰(가상 화폐) 주소 31 | MemberStatus[] public status; // 회원 등급 배열 32 | mapping(address => History) public tradingHistory; // 회원별 거래 이력 33 | 34 | // (4) 회원 등급용 구조체 35 | struct MemberStatus { 36 | string name; // 등급명 37 | uint256 times; // 최저 거래 회수 38 | uint256 sum; // 최저 거래 금액 39 | int8 rate; // 캐시백 비율 40 | } 41 | // 거래 이력용 구조체 42 | struct History { 43 | uint256 times; // 거래 회수 44 | uint256 sum; // 거래 금액 45 | uint256 statusIndex; // 등급 인덱스 46 | } 47 | 48 | // (5) 토큰 한정 메서드용 수식자 49 | modifier onlyCoin() { if (msg.sender == coin) _; } 50 | 51 | // (6) 토큰 주소 설정 52 | function setCoin(address _addr) onlyOwner { 53 | coin = _addr; 54 | } 55 | 56 | // (7) 회원 등급 추가 57 | function pushStatus(string _name, uint256 _times, uint256 _sum, int8 _rate) onlyOwner { 58 | status.push(MemberStatus({ 59 | name: _name, 60 | times: _times, 61 | sum: _sum, 62 | rate: _rate 63 | })); 64 | } 65 | 66 | // (8) 회원 등급 내용 변경 67 | function editStatus(uint256 _index, string _name, uint256 _times, uint256 _sum, int8 _rate) onlyOwner { 68 | if (_index < status.length) { 69 | status[_index].name = _name; 70 | status[_index].times = _times; 71 | status[_index].sum = _sum; 72 | status[_index].rate = _rate; 73 | } 74 | } 75 | 76 | // (9) 거래 내역 갱신 77 | function updateHistory(address _member, uint256 _value) onlyCoin { 78 | tradingHistory[_member].times += 1; 79 | tradingHistory[_member].sum += _value; 80 | // 새로운 회원 등급 결정(거래마다 실행) 81 | uint256 index; 82 | int8 tmprate; 83 | for (uint i = 0; i < status.length; i++) { 84 | // 최저 거래 횟수, 최저 거래 금액 충족 시 가장 캐시백 비율이 좋은 등급으로 설정 85 | if (tradingHistory[_member].times >= status[i].times && 86 | tradingHistory[_member].sum >= status[i].sum && 87 | tmprate < status[i].rate) { 88 | index = i; 89 | } 90 | } 91 | tradingHistory[_member].statusIndex = index; 92 | } 93 | 94 | // (10) 캐시백 비율 획득(회원의 등급에 해당하는 비율 확인) 95 | function getCashbackRate(address _member) constant returns (int8 rate) { 96 | rate = status[tradingHistory[_member].statusIndex].rate; 97 | } 98 | } 99 | 100 | // (11) 회원 관리 기능이 구현된 가상 화폐 101 | contract OreOreCoin is Owned{ 102 | // 상태 변수 선언 103 | string public name; // 토큰 이름 104 | string public symbol; // 토큰 단위 105 | uint8 public decimals; // 소수점 이하 자릿수 106 | uint256 public totalSupply; // 토큰 총량 107 | mapping (address => uint256) public balanceOf; // 각 주소의 잔고 108 | mapping (address => int8) public blackList; // 블랙리스트 109 | mapping (address => Members) public members; // 각 주소의 회원 정보 110 | 111 | // 이벤트 알림 112 | event Transfer(address indexed from, address indexed to, uint256 value); 113 | event Blacklisted(address indexed target); 114 | event DeleteFromBlacklist(address indexed target); 115 | event RejectedPaymentToBlacklistedAddr(address indexed from, address indexed to, uint256 value); 116 | event RejectedPaymentFromBlacklistedAddr(address indexed from, address indexed to, uint256 value); 117 | event Cashback(address indexed from, address indexed to, uint256 value); 118 | 119 | // 생성자 120 | function OreOreCoin(uint256 _supply, string _name, string _symbol, uint8 _decimals) { 121 | balanceOf[msg.sender] = _supply; 122 | name = _name; 123 | symbol = _symbol; 124 | decimals = _decimals; 125 | totalSupply = _supply; 126 | } 127 | 128 | // 주소를 블랙리스트에 등록 129 | function blacklisting(address _addr) onlyOwner { 130 | blackList[_addr] = 1; 131 | Blacklisted(_addr); 132 | } 133 | 134 | // 주소를 블랙리스트에서 해제 135 | function deleteFromBlacklist(address _addr) onlyOwner { 136 | blackList[_addr] = -1; 137 | DeleteFromBlacklist(_addr); 138 | } 139 | 140 | // 회원 관리 계약 설정 141 | function setMembers(Members _members) { 142 | members[msg.sender] = Members(_members); 143 | } 144 | 145 | // 송금 146 | function transfer(address _to, uint256 _value) { 147 | // 부정 송금 확인 148 | if (balanceOf[msg.sender] < _value) throw; 149 | if (balanceOf[_to] + _value < balanceOf[_to]) throw; 150 | 151 | // 블랙리스트에 존재하는 계정은 입출금 불가 152 | if (blackList[msg.sender] > 0) { 153 | RejectedPaymentFromBlacklistedAddr(msg.sender, _to, _value); 154 | } else if (blackList[_to] > 0) { 155 | RejectedPaymentToBlacklistedAddr(msg.sender, _to, _value); 156 | } else { 157 | // (12) 캐시백 금액을 계산(각 대상의 비율을 사용) 158 | uint256 cashback = 0; 159 | if(members[_to] > address(0)) { 160 | cashback = _value / 100 * uint256(members[_to].getCashbackRate(msg.sender)); 161 | members[_to].updateHistory(msg.sender, _value); 162 | } 163 | 164 | balanceOf[msg.sender] -= (_value - cashback); 165 | balanceOf[_to] += (_value - cashback); 166 | 167 | Transfer(msg.sender, _to, _value); 168 | Cashback(_to, msg.sender, cashback); 169 | } 170 | } 171 | } 172 | 173 | // (1) 에스크로 174 | contract Escrow is Owned { 175 | // (2) 상태 변수 176 | OreOreCoin public token; // 토큰 177 | uint256 public salesVolume; // 판매량 178 | uint256 public sellingPrice; // 판매 가격 179 | uint256 public deadline; // 기한 180 | bool public isOpened; // 에스크로 개시 플래그 181 | 182 | // (3) 이벤트 알림 183 | event EscrowStart(uint salesVolume, uint sellingPrice, uint deadline, address beneficiary); 184 | event ConfirmedPayment(address addr, uint amount); 185 | 186 | // (4) 생성자 187 | function Escrow (OreOreCoin _token, uint256 _salesVolume, uint256 _priceInEther) { 188 | token = OreOreCoin(_token); 189 | salesVolume = _salesVolume; 190 | sellingPrice = _priceInEther * 1 ether; 191 | } 192 | 193 | // (5) 이름 없는 함수(Ether 수령) 194 | function () payable { 195 | // 개시 전 또는 기한이 끝난 경우에는 예외 처리 196 | if (!isOpened || now >= deadline) throw; 197 | 198 | // 판매 가격 미만인 경우 예외 처리 199 | uint amount = msg.value; 200 | if (amount < sellingPrice) throw; 201 | 202 | // 보내는 사람에게 토큰을 전달하고 에스크로 개시 플래그를 false로 설정 203 | token.transfer(msg.sender, salesVolume); 204 | isOpened = false; 205 | ConfirmedPayment(msg.sender, amount); 206 | } 207 | 208 | // (6) 개시(토큰이 예정 수 이상이라면 개시) 209 | function start(uint256 _durationInMinutes) onlyOwner { 210 | if (token == address(0) || salesVolume == 0 || sellingPrice == 0 || deadline != 0) throw; 211 | if (token.balanceOf(this) >= salesVolume){ 212 | deadline = now + _durationInMinutes * 1 minutes; 213 | isOpened = true; 214 | EscrowStart(salesVolume, sellingPrice, deadline, owner); 215 | } 216 | } 217 | 218 | // (7) 남은 시간 확인용 메서드(분 단위) 219 | function getRemainingTime() constant returns(uint min) { 220 | if(now < deadline) { 221 | min = (deadline - now) / (1 minutes); 222 | } 223 | } 224 | 225 | // (8) 종료 226 | function close() onlyOwner { 227 | // 토큰을 소유자에게 전송 228 | token.transfer(owner, token.balanceOf(this)); 229 | // 계약을 파기(해당 계약이 보유하고 있는 Ether는 소유자에게 전송 230 | selfdestruct(owner); 231 | } 232 | } 233 | 234 | -------------------------------------------------------------------------------- /chap5/ch5_01_KeyValueStore.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | contract KeyValueStore { 3 | uint256 keyIndex; 4 | struct values { 5 | string value1; 6 | string value2; 7 | } 8 | mapping (uint256 => values) Obj; 9 | function setValue(string _value1, string _value2) constant returns (uint256) { 10 | Obj[keyIndex].value1 = _value1; 11 | Obj[keyIndex].value2 = _value2; 12 | keyIndex++; 13 | return keyIndex; 14 | } 15 | function getValue1(uint _key) constant returns (string) { 16 | return Obj[_key].value1; 17 | } 18 | function getValue2(uint _key) constant returns (string) { 19 | return Obj[_key].value2; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /chap5/ch5_02_TransactionLogNG.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | // (1) 거래 로그 계약 선언 4 | contract TransactionLogNG { 5 | // (2) 저장소 정의 6 | mapping (bytes32 => mapping (bytes32 => string)) public tranlog; 7 | 8 | // (3) 거래 내용 등록 9 | function setTransaction(bytes32 user_id, bytes32 project_id, string tran_data) public { 10 | // (4) 등록 11 | tranlog[user_id][project_id] = tran_data; 12 | } 13 | 14 | // (5) 사용자, 프로젝트별 거래 내용을 가져온다 15 | function getTransaction(bytes32 user_id, bytes32 project_id) public constant returns (string tran_data) { 16 | return tranlog[user_id][project_id]; 17 | } 18 | } 19 | 20 | -------------------------------------------------------------------------------- /chap5/ch5_03_TransactionLogOK.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | contract TransactionLogOK { 3 | mapping (bytes32 => mapping (bytes32 => string)) public tranlog; 4 | function setTransaction(bytes32 user_id, bytes32 project_id, string tran_data) public { 5 | if(bytes(tranlog[user_id][project_id]).length != 0) { 6 | throw; 7 | } 8 | tranlog[user_id][project_id] = tran_data; 9 | } 10 | function getTransaction(bytes32 user_id, bytes32 project_id) public constant returns (string tran_data) { 11 | return tranlog[user_id][project_id]; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /chap5/ch5_04_PersonCertification.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | // 본인 확인 계약 4 | contract PersonCertification { 5 | 6 | // 계약 관리자 주소 7 | address admin; 8 | 9 | // (1) 열람 허가 정보 10 | struct AppDetail { 11 | bool allowReference; 12 | uint256 approveBlockNo; 13 | uint256 refLimitBlockNo; 14 | address applicant; 15 | } 16 | 17 | // (2) 본인 확인 정보(홍길동) 18 | struct PersonDetail { 19 | string name; 20 | string birth; 21 | address[] orglist; 22 | } 23 | 24 | // (3) 인증 기관 정보(학교, 회사 등) 25 | struct OrganizationDetail { 26 | string name; 27 | } 28 | 29 | // (4) 해당 키의 열람 허가 정보 30 | mapping(address => AppDetail) appDetail; 31 | 32 | // (5) 해당 키의 본인 확인 정보 33 | mapping(address => PersonDetail) personDetail; 34 | 35 | // (6) 해당 키의 조직 정보 36 | mapping(address => OrganizationDetail) public orgDetail; 37 | 38 | // (7) 생성자 39 | function PersonCertification() { 40 | admin = msg.sender; 41 | } 42 | 43 | // --------------------------------------------------------------------- 44 | // 데이터 등록 기관(set) 45 | // --------------------------------------------------------------------- 46 | // (8) 본인 정보를 등록 47 | function setPerson(string _name, string _birth) { 48 | personDetail[msg.sender].name = _name; 49 | personDetail[msg.sender].birth = _birth; 50 | } 51 | 52 | // (9) 조직 정보를 등록 53 | function setOrganization(string _name) { 54 | orgDetail[msg.sender].name = _name; 55 | } 56 | 57 | // (10) 조직이 개인의 소속을 증명 58 | function setBelong(address _person) { 59 | personDetail[_person].orglist.push(msg.sender); 60 | } 61 | 62 | // (11) 본인 확인 정보 참조를 허가 63 | function setApprove(address _applicant, uint256 _span) { 64 | appDetail[msg.sender].allowReference = true; 65 | appDetail[msg.sender].approveBlockNo = block.number; 66 | appDetail[msg.sender].refLimitBlockNo = block.number + _span; 67 | appDetail[msg.sender].applicant = _applicant; 68 | } 69 | 70 | // --------------------------------------------------------------------- 71 | // 데이터 취득 함수(get) 72 | // --------------------------------------------------------------------- 73 | // (12) 본인 확인 정보를 참조 74 | function getPerson(address _person) public constant returns( 75 | bool _allowReference, 76 | uint256 _approveBlockNo, 77 | uint256 _refLimitBlockNo, 78 | address _applicant, 79 | string _name, 80 | string _birth, 81 | address[] _orglist) { 82 | // (12-1) 열람을 허가할 정보 83 | _allowReference = appDetail[_person].allowReference; 84 | _approveBlockNo = appDetail[_person].approveBlockNo; 85 | _refLimitBlockNo = appDetail[_person].refLimitBlockNo; 86 | _applicant = appDetail[_person].applicant; 87 | // (12-2) 열람을 제한할 정보 88 | if (((msg.sender == _applicant) 89 | && (_allowReference == true) 90 | && (block.number < _refLimitBlockNo)) 91 | || (msg.sender == admin) 92 | || (msg.sender == _person)) { 93 | _name = personDetail[_person].name; 94 | _birth = personDetail[_person].birth; 95 | _orglist = personDetail[_person].orglist; 96 | } 97 | } 98 | } 99 | 100 | -------------------------------------------------------------------------------- /chap5/ch5_04_cmd.txt: -------------------------------------------------------------------------------- 1 | 계약 관리자 - eth.account[1] 2 | "0xf73dfb27c3236cddef7e59162826b77b3815194a" 3 | 인증 기관(대학) - eth.account[2] 4 | "0xdad1e62247ddba270ceede3de858366d0dc5430d" 5 | 인증 기관(기업) - eth.account[3] 6 | "0x50b9b8b7e6362b734cb6cda675b4cfc202633c0f" 7 | 열람자(채용 담당자) - eth.account[4] 8 | "0x446369222c208e74cd5ab7f76d205f12cb782414" 9 | 정보 공개자(조현수) - eth.account[5] 10 | "0xbc9d277020e91f46ff919175a6b077bca5d5636c" 11 | 12 | 시나리오 1. 계약 등록 13 | > eth.coinbase 14 | "0xf73dfb27c3236cddef7e59162826b77b3815194a" (계약 관리자) 15 | contractObj 변수 선언 16 | 17 | 시나리오 2. 인증 조직 정보 등록 18 | - 인증 기관(대학) 등록 19 | > eth.coinbase 20 | "0xdad1e62247ddba270ceede3de858366d0dc5430d" (인증 기관(대학)) 21 | > contractObj.SetOrganization.sendTransaction("가천대학교", eth.account[2]) 22 | > contractObj.orgDetail.call("0xdad1e62247ddba270ceede3de858366d0dc5430d", {from:eth.accounts[2]}) 23 | "연세대학교" 24 | > contractObj.setBelong.sendTransaction("0xbc9d277020e91f46ff919175a6b077bca5d5636c", {from:eth.accounts[2]}) 25 | - 인증 기관(기업) 등록 26 | > eth.coinbase 27 | "0x50b9b8b7e6362b734cb6cda675b4cfc202633c0f" (인증 기관(기업)) 28 | > contractObj.SetOrganization.sendTransaction("현대카드", eth.account[3]) 29 | > contractObj.orgDetail.call("0x50b9b8b7e6362b734cb6cda675b4cfc202633c0f", {from:eth.accounts[2]}) 30 | "현대카드" 31 | > contractObj.setBelong.sendTransaction("0xbc9d277020e91f46ff919175a6b077bca5d5636c", {from:eth.accounts[2]}) 32 | 33 | 시나리오 3. 본인 정보 등록 34 | - 본인 정보 35 | > eth.coinbase 36 | "0xbc9d277020e91f46ff919175a6b077bca5d5636c" (정보 공개자(조현수)) 37 | > contractObj.setPerson.sendTransaction("조현수", "19861002", {from:eth.accounts[5]}) 38 | > contractObj.getPerson.call("0xbc9d277020e91f46ff919175a6b077bca5d5636c", {from:eth.accounts[5]}) 39 | [false, 0, 0, "0x000000000000000000", "조현수", "19861002", ["0xdad1e62247ddba270ceede3de858366d0dc5430d", "0x50b9b8b7e6362b734cb6cda675b4cfc202633c0f"]] 40 | - 열람자(채용담당자)에게 권한 설정 41 | > contractObj.setApprove.sendTransaction("0x446369222c208e74cd5ab7f76d205f12cb782414", 20, {from:eth.accounts[5]}) 42 | > contractObj.getPerson.call("0xbc9d277020e91f46ff919175a6b077bca5d5636c", {from:eth.accounts[5]}) 43 | [true, 1050, 1070, "0x446369222c208e74cd5ab7f76d205f12cb782414", "조현수", "19861002", ["0xdad1e62247ddba270ceede3de858366d0dc5430d", "0x50b9b8b7e6362b734cb6cda675b4cfc202633c0f"]] 44 | 45 | 시나리오 4. 본인 확인 정보 열람 46 | > eth.coinbase 47 | "0x446369222c208e74cd5ab7f76d205f12cb782414" (열람자(채용 담당자)) 48 | > eth.blockNumber 49 | 1060 50 | > contractObj.getPerson.call("0xbc9d277020e91f46ff919175a6b077bca5d5636c", {from:eth.accounts[4]}) 51 | [true, 1050, 1070, "0x446369222c208e74cd5ab7f76d205f12cb782414", "조현수", "19861002", ["0xdad1e62247ddba270ceede3de858366d0dc5430d", "0x50b9b8b7e6362b734cb6cda675b4cfc202633c0f"]] 52 | > eth.blockNumber 53 | 1080 54 | > contractObj.getPerson.call("0xbc9d277020e91f46ff919175a6b077bca5d5636c", {from:eth.accounts[4]}) 55 | [true, 1050, 1070, "0x446369222c208e74cd5ab7f76d205f12cb782414", "", "", []] 56 | -------------------------------------------------------------------------------- /chap6/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/wikibook/blockchain-solidity/809a57ea92f7f4cbbf8c23cda784f88dc9a8aaae/chap6/.DS_Store -------------------------------------------------------------------------------- /chap6/6.2.2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract RandomNumber { 4 | function get(uint max) constant returns (uint, uint) { 5 | // (1) 가장 마지막 블록이 생성된 시각을 정수 값으로 반환 6 | uint block_timestamp = block.timestamp; 7 | 8 | // (2) 그 값을 max로 나눈 나머지를 계산 9 | // max = 6인 경우 나머지는 0~5의 정수이므로 +1를 해 1~6의 정수로 만든다 10 | uint mod = block_timestamp % max + 1; 11 | 12 | return (block_timestamp, mod); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /chap6/6.3.1.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract BlockHashTest { 4 | function getBlockHash(uint _blockNumber) constant returns (bytes32 blockhash, uint blockhashToNumber){ 5 | bytes32 _blockhash = block.blockhash(_blockNumber); 6 | uint _blockhashToNumber = uint(_blockhash); 7 | return (_blockhash, _blockhashToNumber); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /chap6/6.3.2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract RandomNumber { 4 | address owner; 5 | // (1) 1~numberMax 의 난수 값을 생성하도록 설정하는 변수 6 | uint numberMax; 7 | 8 | // (2) 예약 객체 9 | struct draw { 10 | uint blockNumber; 11 | uint drawnNumber; 12 | } 13 | 14 | // (3) 예약 객체 배열 15 | struct draws { 16 | uint numDraws; 17 | mapping (uint => draw) draws; 18 | } 19 | 20 | // (4) 사용자(address)별로 예약 배열을 관리 21 | mapping (address => draws) requests; 22 | 23 | // (5) 이벤트(용도에 대해서는 이후 설명) 24 | event ReturnNextIndex(uint _index); 25 | event ReturnDraw(int _status, bytes32 _blockhash, uint _drawnNumber); 26 | 27 | // (6) 생성자 28 | function RandomNumber(uint _max) { 29 | owner = msg.sender; 30 | numberMax = _max; 31 | } 32 | 33 | // (7) 난수 생성 예약을 추가 34 | function request() returns (uint) { 35 | // (8) 현재 예약 갯수 취득 36 | uint _nextIndex = requests[msg.sender].numDraws; 37 | // (9) 마지막 블록의 블록 번호를 기록 38 | requests[msg.sender].draws[_nextIndex].blockNumber = block.number; 39 | // (10) 예약 갯수 카운트 증가 40 | requests[msg.sender].numDraws = _nextIndex + 1; 41 | // (11) 예약 번호 반환 42 | ReturnNextIndex(_nextIndex); 43 | return _nextIndex; 44 | } 45 | 46 | // (12) 예약된 난수 생성 결과 획득 시도 47 | function get(uint _index) returns (int status, bytes32 blockhash, uint drawnNumber){ 48 | // (13) 존재하지 않는 예약 번호인 경우 49 | if(_index >= requests[msg.sender].numDraws){ 50 | ReturnDraw(-2, 0, 0); 51 | return (-2, 0, 0); 52 | // (14) 예약 번호가 존재하는 경우 53 | }else{ 54 | // (15) 예약시 기록한 block.number의 다음 블록 번호를 계산 55 | uint _nextBlockNumber = requests[msg.sender].draws[_index]. blockNumber + 1; 56 | // (16) 아직 다음 블록이 생성되지 않은 경우 57 | if (_nextBlockNumber >= block.number) { 58 | ReturnDraw(-1, 0, 0); 59 | return (-1, 0, 0); 60 | // (17) 다음 블록이 생성됐기 때문에 난수 계산 61 | }else{ 62 | // (18) 블록 해시 값을 획득 63 | bytes32 _blockhash = block.blockhash(_nextBlockNumber); 64 | // (19) 블록 해시 값에서 난수 값을 계산 65 | uint _drawnNumber = uint(_blockhash) % numberMax + 1; 66 | // (20) 계산된 난수 값을 저장 67 | requests[msg.sender].draws[_index].drawnNumber = _drawnNumber; 68 | // (21) 결과를 반환 69 | ReturnDraw(0, _blockhash, _drawnNumber); 70 | return (0, _blockhash, _drawnNumber); 71 | } 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /chap6/6.3.3.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract RandomNumber { 4 | address owner; 5 | uint numberMax; 6 | 7 | struct draw { 8 | // (1) 예약할 때 마지막 블록 번호만 유지 9 | uint blockNumber; 10 | } 11 | 12 | struct draws { 13 | uint numDraws; 14 | mapping (uint => draw) draws; 15 | } 16 | 17 | mapping (address => draws) requests; 18 | 19 | // (2) request()의 반환 값 참조용 이벤트에 정의 20 | event ReturnNextIndex(uint _index); 21 | 22 | function RandomNumber(uint _max) { 23 | owner = msg.sender; 24 | numberMax = _max; 25 | } 26 | 27 | function request() returns (uint) { 28 | uint _nextIndex = requests[msg.sender].numDraws; 29 | requests[msg.sender].draws[_nextIndex].blockNumber = block.number; 30 | requests[msg.sender].numDraws = _nextIndex + 1; 31 | ReturnNextIndex(_nextIndex); 32 | return _nextIndex; 33 | } 34 | 35 | // (3) 난수 값 계산 결과를 저장하지 않게끔 변경하고 constant 함수로 변경 36 | function get(uint _index) constant returns (int status, bytes32 blockhash, uint drawnNumber){ 37 | if(_index >= requests[msg.sender].numDraws){ 38 | return (-2, 0, 0); 39 | }else{ 40 | uint _nextBlockNumber = requests[msg.sender].draws[_index]. blockNumber + 1; 41 | if (_nextBlockNumber >= block.number) { 42 | return (-1, 0, 0); 43 | }else{ 44 | // (4) 매번 블록 번호로부터 블록 해시를 참조해 반환 45 | bytes32 _blockhash = block.blockhash(_nextBlockNumber); 46 | uint _drawnNumber = uint(_blockhash) % numberMax + 1; 47 | return (0, _blockhash, _drawnNumber); 48 | } 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /chap6/6.4.2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | 3 | contract RandomNumber { 4 | address owner; 5 | uint numberMax; 6 | 7 | struct draw { 8 | uint blockNumber; 9 | } 10 | 11 | struct draws { 12 | uint numDraws; 13 | mapping (uint => draw) draws; 14 | } 15 | 16 | mapping (address => draws) requests; 17 | 18 | event ReturnNextIndex(uint _index); 19 | 20 | function RandomNumber(uint _max) { 21 | owner = msg.sender; 22 | numberMax = _max; 23 | } 24 | 25 | function request() returns (uint) { 26 | uint _nextIndex = requests[msg.sender].numDraws; 27 | requests[msg.sender].draws[_nextIndex].blockNumber = block.number; 28 | requests[msg.sender].numDraws = _nextIndex + 1; 29 | ReturnNextIndex(_nextIndex); 30 | return _nextIndex; 31 | } 32 | 33 | function get(uint _index) constant returns (int status, bytes32 blockhash, bytes32 seed, uint drawnNumber){ 34 | if(_index >= requests[msg.sender].numDraws){ 35 | return (-2, 0, 0, 0); 36 | }else{ 37 | uint _nextBlockNumber = requests[msg.sender].draws[_index]. blockNumber + 1; 38 | if (_nextBlockNumber >= block.number) { 39 | return (-1, 0, 0, 0); 40 | }else{ 41 | bytes32 _blockhash = block.blockhash(_nextBlockNumber); 42 | bytes32 _seed = sha256(_blockhash, msg.sender, _index); 43 | uint _drawnNumber = uint(_seed) % numberMax + 1; 44 | return (0, _blockhash, _seed, _drawnNumber); 45 | } 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /chap6/6.4.3.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8;contract RandomNumber { address owner; uint numberMax; struct draw { uint blockNumber; } struct draws { uint numDraws; mapping (uint => draw) draws; } mapping (address => draws) requests; event ReturnNextIndex(uint _index); event ReturnDebug(bytes32 _seed, uint _drawnNumber); function RandomNumber(uint _max) { owner = msg.sender; numberMax = _max; } function request(uint _dummy) returns (uint) { uint _nextIndex = requests[msg.sender].numDraws; requests[msg.sender].draws[_nextIndex].blockNumber = block.number; requests[msg.sender].numDraws = _nextIndex + 1; ReturnNextIndex(_nextIndex); return _nextIndex; } function getNum() constant returns(uint num){ return requests[msg.sender].numDraws; } function get(uint _index) constant returns (int status, bytes32 blockhash, bytes32 seed, uint drawnNumber){ if(_index >= requests[msg.sender].numDraws) { return (-2, 0, 0, 0); }else{ uint _nextBlockNumber = requests[msg.sender]. draws[_index].blockNumber + 1; if (_nextBlockNumber >= block.number) { return (-1, 0, 0, 0); }else{ bytes32 _blockhash = block.blockhash(_nextBlockNumber); bytes32 _seed = sha256(_blockhash, msg.sender, _index); uint _drawnNumber = uint(_seed) % numberMax + 1; return (0, _blockhash, _seed, _drawnNumber); } } } } 2 | 3 | 4 | 5 | 6 | 7 | pragma solidity ^0.4.8;contract RandomNumber {address owner;uint numberMax;struct draw {uint blockNumber;}struct draws {uint numDraws;mapping (uint => draw) draws;}mapping (address => draws) requests;event ReturnNextIndex(uint _index);function RandomNumber(uint _max) {owner = msg.sender;numberMax = _max;}function request() returns (uint) {uint _nextIndex = requests[msg.sender].numDraws;requests[msg.sender].draws[_nextIndex].blockNumber = block.number;requests[msg.sender].numDraws = _nextIndex + 1;ReturnNextIndex(_nextIndex);return _nextIndex;}function get(uint _index) constant returns (int status, bytes32 blockhash, bytes32 seed, uint drawnNumber){if(_index >= requests[msg.sender].numDraws){return (-2, 0, 0, 0);}else{uint _nextBlockNumber = requests[msg.sender].draws[_index]. blockNumber + 1;if (_nextBlockNumber >= block.number) {return (-1, 0, 0, 0);}else{bytes32 _blockhash = block.blockhash(_nextBlockNumber);bytes32 _seed = sha256(_blockhash, msg.sender, _index);uint _drawnNumber = uint(_seed) % numberMax + 1;return (0, _blockhash, _seed, _drawnNumber);}}}} 8 | -------------------------------------------------------------------------------- /chap6/6.5.2.sol: -------------------------------------------------------------------------------- 1 | pragma solidity ^0.4.8; 2 | import "github.com/oraclize/ethereum-api/oraclizeAPI.sol"; 3 | 4 | contract RandomNumberOraclized is usingOraclize{ 5 | uint public randomNumber; 6 | bytes32 public request_id; 7 | 8 | function RandomNumberOraclized() { 9 | // (1) Oraclize Address Resolver를 읽어온다 10 | //