솔리디티(Solidity) 프로그래밍


Solidity is a contract-oriented, high-level language for implementing smart contracts.

개발환경 설정

Mist 브라우저

이더리움 지갑 프로그램

$ Ethereum-Wallet-win64-0-10-0>"Ethereum Wallet.exe" --rpc http://192.168.0.207:8545

Ethereum Wallet

Note: Insecure RPC connection

pragma solidity ^0.4.18;

contract MyFirstContract {
    
    string message = "hello";
    string goodbye = "잘가";

    function sayHello() public view returns(string) {
        return message;
    }
    
    function changeHello(string _message) public {
        message = _message;
    }
    
    function sayGoodbye() public view returns(string) {
        return goodbye;
    }
    
    function changeGoodbye(string _goodbye) public {
        goodbye = _goodbye;
    }
}

생성된 컨트랙트 확인

> eth.blockNumber
979
> eth.getBlock(814)
{
  difficulty: 190874,
  extraData: "0xd783010802846765746887676f312e392e34856c696e7578",
  gasLimit: 60592358,
  gasUsed: 366695,
  hash: "0xa78e91b855732bc3877fde86cbb0e736a1798ff52dd55965a8eface4a8a1f9c8",
  logsBloom: "0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
  miner: "0xbfa12bc27bd8a01a77543a6094b8882f85a2dd58",
  mixHash: "0xcda7642fe2e756abba6e6588e1704a8d9566bafcfa0d256f37b56b8998a064bb",
  nonce: "0x2fd6feeabbb5dbae",
  number: 814,
  parentHash: "0x60c69c2ae4d79de3eb055c3f2e1b0a6e835d3245e86e9ea615dfd30aae627bc7",
  receiptsRoot: "0x887d7f75d593561f074b2be61cee4a2b8d4ee97791e1b097560437cb8b44d0e7",
  sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
  size: 1926,
  stateRoot: "0xb4bea3e5cea0fd39af2a94859e44194b737a8f793ba393f8296c7f152ae7f1cf",
  timestamp: 1528090922,
  totalDifficulty: 129787198,
  transactions: ["0x010cc761723c9bec0797941b0d742e2601fc6bec4779b4c13493e46d506190a4"],
  transactionsRoot: "0xeb7c53d52f6bc58c2387a91dcf6c4186428e33a5e8db0e26bd75ade8cffaef7d",
  uncles: []
}

Remix IDE 개발환경

로컬 버전

browser-solidity-gh-pages/index.html

온라인 버전

http://remix.ethereum.org

  • Environment : Web3 Provider
    • Web3 Provider Endpoint -> http://192.168.0.207:8545
  • Account 에 계정 연결을 확인

  • Compile
    • Check “Auto compile”
  • Settings
    • Solidity version: 0.4.18+commit.9cf6e910
  • Run
    • Account 선택
    • Value: 1 ether
    • Deploy
      • 콘솔에서 personal.unlockAccount(eth.accounts[0]) 수행

NodeJS

$ nodejs --version
v4.2.6
$ npm --version
3.5.2

MetaMast

  • 로그인 패스워드: 1q2w3e4r
  • Custom RPC
    • New RPC URL: http://192.168.0.207:8545

Solc

$ sudo apt-get install solc
$ solc --version
solc, the solidity compiler commandline interface
Version: 0.4.24+commit.e67f0147.Linux.g++
$ which solc
/usr/bin/solc

Private 이더리움 네트워크 구축

Geth 초기화

$ mkdir smartcontract_testnet
$ cd smartcontract_testnet/
$ cp ../data_testnet/genesis.json .

$ geth --datadir /home/user01/smartcontract_testnet/ init /home/user01/smartcontract_testnet/genesis.json 

Geth 실행

$ nohup geth --networkid 4649 --nodiscover --datadir /home/user01/smartcontract_testnet --rpc --rpcaddr "0.0.0.0" --rpcport 8545 --rpccorsdomain "*" --rpcapi "admin,db,eth,debug,miner,net,shh,txpool,personal,web3" --verbosity 6 2>>/home/user01/smartcontract_testnet/geth.log & 

$ ps -eaf | grep geth
user01    1787  1650  0 10:39 pts/0    00:00:00 geth --networkid 4649 --nodiscover --datadir /home/user01/smartcontract_testnet --rpc --rpcaddr 0.0.0.0 --rpcport 8545 --rpccorsdomain * --rpcapi admin,db,eth,debug,miner,net,shh,txpool,personal,web3 --verbosity 6

$ kill $(ps aux | grep geth | grep rpc | awk '{print $2}')

Geth Console 연결

$ geth attach http://localhost:8545
Welcome to the Geth JavaScript console!

instance: Geth/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

> 

계정 생성

Welcome to the Geth JavaScript console!

instance: Geth/PrivateNetwork/v1.8.2-stable-b8b9f7f4/linux-amd64/go1.9.4
 modules: admin:1.0 debug:1.0 eth:1.0 miner:1.0 net:1.0 personal:1.0 rpc:1.0 txpool:1.0 web3:1.0

> personal.newAccount("pass0")
"0xbfa12bc27bd8a01a77543a6094b8882f85a2dd58"
> personal.newAccount("pass1")
"0x7e0fc0342326a8645e1913771d42917c3a65c119"
> personal.newAccount("pass2")
"0x1cc0072199d86ba38b5fabf2235899d50cc0a29d"
> personal.newAccount("pass3")
"0x92ff3867275ab97c29785e88a5c7946ae9cfaa63"

채굴

> miner.start(1)
null

> eth.getBalance(eth.coinbase)
115000000000000000000
> web3.fromWei(eth.getBalance(eth.coinbase))
185
> miner.stop()
true
> eth.blockNumber
37

송금

> personal.unlockAccount(eth.accounts[0])
Unlock account 0xbfa12bc27bd8a01a77543a6094b8882f85a2dd58
Passphrase: 
true
> eth.sendTransaction({from:eth.accounts[0], to:eth.accounts[1], value:web3.toWei(50, "ether")})
"0xbdb29bdf578ebcd1caa6f9b18aec1bf874ebed918e26f69b4f2b10dee2c2e33a"
> eth.sendTransaction({from:eth.accounts[0], to:eth.accounts[2], value:web3.toWei(50, "ether")})
"0x7fb3141756d954a55d40f196c8ced6221a169e30ae11db8f2bae78feab47f09f"
> eth.sendTransaction({from:eth.accounts[0], to:eth.accounts[3], value:web3.toWei(50, "ether")})
"0x46076f2fb46c9b6f221ea149066da79256aa50346b96142a3fd28b8e64a86254"

# 채굴되지 않은 트랜잭션 리스트
> eth.pendingTransactions
> eth.blockNumber
> eth.getBlock(43)

Peer 연결

$ sudo ifconfig enp0s8 192.168.0.207 netmask 255.255.255.0 up
[sudo] password for user01: 
$ sudo route add default gw 192.168.0.1
$ ping 192.168.0.107
$ ifconfig -a
enp0s8    Link encap:Ethernet  HWaddr 08:00:27:05:44:dc  
          inet addr:192.168.0.207  Bcast:192.168.0.255  Mask:255.255.255.0
          inet6 addr: fe80::a00:27ff:fe05:44dc/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:710 errors:0 dropped:446 overruns:0 frame:0
          TX packets:37 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000 
          RX bytes:47898 (47.8 KB)  TX bytes:2713 (2.7 KB)
          Interrupt:16 Base address:0xd240 
$ sudo mkdir -p /etc/sysconfig/network-scripts
$ sudo vi /etc/sysconfig/network-scripts/ifcfg-enp0s8
DEVICE=enp0s8
BOOTPROTO=static
BROADCAST=192.168.0.255
IPADDR=192.168.0.207
NETMASK=255.255.255.0
NETWORK=192.168.0.0
ONBOOT=yes

$ sudo vi /etc/sysconfig/network
NETWORKING=yes
HOSTNAME=ubuntu
GATEWAY=192.168.0.1
> admin.nodeInfo.enode
"enode://33dbede907252cd274d95244b12a26157c716ea3c4131307e748191175a93afd0e500500829a30c30d5456f05c2f36e41cdaa09522d9916d13bc25e9b56bcdbd@[::]:30303?discport=0"
> admin.addPeer("enode://b128200b26a55a5aa2b605905b1dfa9be8426cf6d8f73c1a1c0ea89e4d4771a98feead21cc9830b772c559d42b91ee2455fad4402738571cad61f17c93b787ae@192.168.0.202:30303?discport=0")
> net.peerCount
0
> admin.peers
[]

솔리디티 문법

기본 자료형

함수

Web3

web3.js - Ethereum JavaScript API

  • https://www.mobilefish.com/download/ethereum/web3api.html
  • https://www.mobilefish.com/download/ethereum/DemoDapp.html

Express 설치

$ sudo npm install express-generator -g
[sudo] password for user01: 
/usr/local/bin/express -> /usr/local/lib/node_modules/express-generator/bin/express-cli.js
/usr/local/lib
└─┬ express-generator@4.16.0 
  ├── commander@2.13.0 
  ├── ejs@2.5.7 
  ├─┬ minimatch@3.0.4 
  │ └─┬ brace-expansion@1.1.11 
  │   ├── balanced-match@1.0.0 
  │   └── concat-map@0.0.1 
  ├─┬ mkdirp@0.5.1 
  │ └── minimist@0.0.8 
  └── sorted-object@2.0.1 

$ sudo ln -s /usr/bin/nodejs /usr/bin/node
$ express -h

Express 페이지 생성

$ express --view=pug web3
$ cd web3 && sudo npm install
$ DEBUG=web3 npm start

사이트 접속: http://192.168.0.207:3000/

  • web3 demo
    • /home/user01/web3/public
      • js, monitor.html 업로드
    • http://192.168.0.207:3000/monitor.html

Account

account.html

Wallet

wallet.html

Vote

vote.html

var vc = web3.eth.contract(
  ABI
).at("contract address");

VoteContract.sol

> personal.unlockAccount(eth.accounts[0])
> miner.start()

스마트컨트랙트 개발 보안

Circuit Breaker 패턴

pragma solidity ^0.4.18;

contract CircuitBreakerDemo {
    
    bool public stopped;
    
    address public owner;
    
    bytes16 public message;
    
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }
    
    modifier isStopped() {
        require(!stopped);
        _;
    }
    
    function CircuitBreakerDemo() public {
        owner = msg.sender;
        stopped = false;
    }
    
    function toggleCircuit(bool _stopped) public onlyOwner {
        stopped = _stopped;
    }
    
    function message(bytes16 _message) public isStopped {
        message = _message;
    }
}
> var cb = eth.contract(abi).at('cb-address')
> cb
> cb.stopped()
false
> personal.unlockAccount(eth.accounts[0])
Unlock account 0xbfa12bc27bd8a01a77543a6094b8882f85a2dd58
Passphrase: 
true
> cb.setMessage.sendTransaction("aaa", {from:eth.accounts[0], gas:500000})
"0x045569ec1f489e3d309b5a92556624e6e3694aa586e983268a6fb9c47ee34117"
> web3.toUtf8(cb.message())
"aaa"

> cb.toggleCircuit.sendTransaction(true, {from:eth.accounts[0], gas:500000})
"0xdc67f05731cbea80fc7b4f27cbe5f20b53d4098335b58258bbe56444875971aa"
> cb.setMessage.sendTransaction("ccc", {from:eth.accounts[0], gas:500000})
"0xce09f57acc6739c4520ab68b2167cd22d647edbf01a9d0450efda92ca27d12bf"
> web3.toUtf8(cb.message())
"bbb"

Mortal 패턴

  • 더이상 필요하지 않게 된 계약을 그때그때 소멸시키는 패턴
  • 소멸시킬 계약이 이더를 보유하고 있다면 이 계약으로부터 이더를 회수해야 함.
  • 소멸과 동시에 이더를 계약의 소유자에게 송금
pragma solidity ^0.4.18;

contract Owned {
    
    address public owner;
    
    bytes16 public message;
    
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }
    
    function owned() internal {
        owner = msg.sender;
    }
    
    function changeOwner(address _newOwner) public onlyOwner {
        owner = _newOwner;
    }
}

contract Mortal is Owned {
    
    function kill() public onlyOwner {
        selfdestruct(owner);
    }
}

contract MortalSample is Mortal {
    
    string public somestate;
    
    //fallback function
    function() public payable {
        
    }
    
    function MortalSample() public {
        owned();
        somestate = "initial";
    }
}

Access Restriction 패턴

pragma solidity ^0.4.18;

contract Owned {
    
    address public owner;
    
    bytes16 public message;
    
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }
    
    function owned() internal {
        owner = msg.sender;
    }
    
    function changeOwner(address _newOwner) public onlyOwner {
        owner = _newOwner;
    }
}

contract AccessRestrictionDemo is Owned {
    
    string public somestate;
    
    function AccessRestrictionDemo() public {
        owned();
        somestate = "initial";
    }
    
    function updateSomeState(string _newState) public onlyOwner {
        somestate = _newState;
    }
}

Withdraw 패턴

pragma solidity ^0.4.18;

contract Auction {
    
    address public highhestBidder;
    
    uint public highhestBid;

    function Auction() public payable {
        highhestBidder = msg.sender;
        highhestBid = 0;
    }
    
    function bid() public payable {
        require(msg.value > highhestBid);
        
        uint refundAmount = highhestBid;
        
        address currentHighhestBidder = highhestBidder;
        
        highhestBid = msg.value;
        highhestBidder = msg.sender;
        
        if (currentHighhestBidder.send(refundAmount)) {
            revert();    
        }
    }
}
> var au = eth.contract([
  ]).at('0x9c98e7dd51bfdca025e2ce488696e0a87ceb4c6b')
var db=eth.contract([
  ]).at('0xb434963fbc8cfed2d622f8d05a792fa612d51f06')
> au
> db
> au.highhestBid()
0
> au.highhestBidder()
"0xbfa12bc27bd8a01a77543a6094b8882f85a2dd58"
> web3.fromWei(eth.getBalance(eth.accounts[1]), "ether")
200.989432884
> web3.fromWei(eth.getBalance(eth.accounts[2]), "ether")
295.985790836
> web3.fromWei(eth.getBalance(eth.accounts[3]), "ether")
199.995910112
> personal.unlockAccount(eth.accounts[1])
Unlock account 0x7e0fc0342326a8645e1913771d42917c3a65c119
Passphrase: 
true
> au.bid.sendTransaction({from:eth.accounts[1], gas:500000, value:web3.toWei(1,"ether")})
"0x4875bde8824982f618d7daa5e4d5e29516785acb752c35064c36133670515601"
> web3.fromWei(au.highhestBid(), "ether")
> db.bid.sendTransaction(au.address, {from:eth.accounts[1], gas:50000, value:web3.toWei(5, "ether)})

참조






© 2017. by yeopoong.github.io

Powered by yeopoong