반응형

소유 가능한 컨트랙트 Ownable


컨트랙트를 소유 가능하게 하는 방법이 있다.


즉, 컨트랙트를 대상으로 특별한 권리를 가지는 소유자를 생성시킬 수 있다.


OpenZeppelin 솔리디티 라이브러리에 있는

Ownable 컨트랙트는 DApp에서 사용할 수 있는 전한 스마트 컨트랙트의 라이브러리이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
 * @title Ownable
 * @dev The Ownable contract has an owner address, and provides basic authorization control
 * functions, this simplifies the implementation of "user permissions".
 */
contract Ownable {
  address public owner;
  event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);
  /**
   * @dev The Ownable constructor sets the original `owner` of the contract to the sender
   * account.
   */
  function Ownable() public {
    owner = msg.sender;
  }
  /**
   * @dev Throws if called by any account other than the owner.
   */
  modifier onlyOwner() {
    require(msg.sender == owner);
    _;
  }
  /**
   * @dev Allows the current owner to transfer control of the contract to a newOwner.
   * @param newOwner The address to transfer ownership to.
   */
  function transferOwnership(address newOwner) public onlyOwner {
    require(newOwner != address(0));
    OwnershipTransferred(owner, newOwner);
    owner = newOwner;
  }
}
cs




위의 코드에 있는 함수는 다음과 같이 실행된다.


1. 생성자 Ownable()은 컨트랙트와 동일한 이름을 가진,생략할 수 있는 특별한 함수이며 다른 언어와 같이 

이 함수는 컨트랙트가 생성될 때 딱 한 번만 실행된다.


2. 컨트랙트가 생성되면 컨트랙트의 생성자가 owner에 msg.sender(컨트랙트 배포자)를 대입한다.


3. 특정한 함수들에 대해 오직 소유자만 접근 가능하도록 onlyOwner 제어자를 추가한다.


4. 해당 컨트랙트의 소유권을 옮겨준다.


1
2
3
4
5
 modifier onlyOwner() {
 
    require(msg.sender == owner);
    _;
  }
cs


1
2
3
4
5
6
7
contract MyContract is Ownable {
  event LaughManiacally(string laughter);
 
  function likeABoss() external onlyOwner {
    LaughManiacally("Muahahahaha");
  }
}
cs


likeABoss함수를 보면 onlyOwner이 있다. onlyOwner 제어자가 호출되면 require로 owner인지 확인하게 되고 

owner가 맞다면 _;을 통해 likeABoss 함수로 되돌아가고 해당 함수(likeABoss)를 실행하게 된다.


보통은 require를 이용하여 함수 실행전에 체크를 할 수 있지만, onlyOwner의 경우에는, 함수에 이 제어자를 추가하면 오직 컨트랙트의 소유자(자네가 배포했다면 자네겠지)만이 해당 함수를 호출할 수 있다.



** 소유자가 컨트랙트에 특별한 권한을 가지는 일이 종종 필요할 수 있지만 코드를 악용하면 심각한 문제가 될 수 있다.


따라서 이러한 DApp을 피하기 위해서는 개발자가 제공하는 소스 코드를 잘 분석하고 잠재적 위험이있는지 확인하는 습관이 필요하고 개발자는 소비자에게 이러한 위험이 없게 믿고 쓸 수 있도록 해야 DApp 생태계가 밝아진다. 






Payable 제어자 및 컨트랙트에서 ETH 거래


payable 함수는 솔리디티에서 이더리움을 거래할 수 있게 해주는 제어자이다.


단적인 예로 함수를 실행하는 동시에 컨트랙트에 일정 페이를 지불하도록 하는 방식을 만들 수 있다는 것이다.


1
2
3
4
5
6
7
8
contract OnlineStore {
  function buySomething() external payable {
    // 함수 실행에 0.001이더가 보내졌는지 확실히 하기 위해 확인:
    require(msg.value == 0.001 ether);
    // 보내졌다면, 함수를 호출한 자에게 디지털 아이템을 전달하기 위한 내용 구성:
    transferThing(msg.sender);
  }
}
cs


require와 msg.value를 통해 컨트랙트로 ETH가 얼마나 보내진 지 확인이 가능하게 된다.


만약 함수가 payable로 표시되지 않았는데 ETH를 보내려 한다면, 함수에서 자네의 트랜잭션을 거부할 것이다.



특정 컨트랙트로 ETH를 보내게 되면 해당 컨트랙트의 이더리움 지갑에는 이더가 저장될 것이다.


이 컨트랙트에 있는 ETH를 가져오기 위한 방법을 한번 알아보자.


1
2
3
4
5
contract GetPaid is Ownable {
  function withdraw() external onlyOwner {
    owner.transfer(this.balance);
  }
}
cs


onlyOwner 제어자에 의해 소유자가 확인되면 owner.transfer(this.balance)를 통해 컨트랙트 내의 ETH를 인출 할 수 있다.

이때 balance는 ETH의 총합을 의미하고 this는 현재 컨트랙트를 의미한다. 


1
2
uint itemFee = 0.001 ether;
msg.sender.transfer(msg.value - itemFee);
cs



만약 초과분의 ETH를 줬다면 반환기능도 만들 수 있다.







오버플로우


uint256으로 만들어진 변수는 2^256이지만 결국 오버플로우는 존재하게 된다.


단적인 사례를 한번 보자.


https://m.blog.naver.com/715fas/221261325892


이렇게 불안정한 DApp을 만들면 애꿎은 사용자만 피해를 보게 된다.



따라서 OpenZeppelin에서 오버플로를 막아주는 라이브러리 SafeMath를 제공해주고 있으니 참고해보자.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
library SafeMath {
 
  function mul(uint256 a, uint256 b) internal pure returns (uint256) {
    if (a == 0) {
      return 0;
    }
    uint256 c = a * b;
    assert(c / a == b);
    return c;
  }
 
  function div(uint256 a, uint256 b) internal pure returns (uint256) {
    // assert(b > 0); // Solidity automatically throws when dividing by 0
    uint256 c = a / b;
    // assert(a == b * c + a % b); // There is no case in which this doesn't hold
    return c;
  }
 
  function sub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }
 
  function add(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    assert(c >= a);
    return c;
  }
}
cs


따라서 우리는 num이라는 변수에서 num++대신 num = num.add(1);로 변경시켜 오버플로우에 안전해질 수 있다.


반응형