본문 바로가기

:D/block chain

[Solidity] 컨트랙트의 불변성

 

 

* 이더리움에 컨트랙트를 배포하고 나면, 컨트랙트는 변하지 않는다. (Immutable)

   -> 컨트랙트를 수정하거나 업데이트 할 수 없음

       컨트랙트로 배포한 최초의 코드는 항상, 블록체인에 영구적으로 존재함.

       이 부분이 솔리디티에 있어서 보안이 굉장히 큰 이슈인 이유

       컨트랙트 코드에 결점이 있다고 하더라도, 배포 이후 그것을 고칠 수 있는 방법이 전혀 없음.

       함수를 호출할 때 마다 코드에 쓰여진 그대로 함수가 실행될 것.

       그 누구도 배포 이후에 함수를 수정하거나 예상치 못한 결과를 발생시키지 못함

 

 

* 외부 의존성

- 특정 컨트랙트의 주소를 DApp에 직접 붙여넣은 경우,

붙여넣은 특정 컨트랙트에 버그가 있었고, 누군가가 파괴해버렸다면?

그럴 일은 잘 없겠지만, 만약 그런 일이 발생한다면 DApp은 완전히 쓸모가 없어짐.

 -> 직접 주소를 써넣었기 때문에 어떤 것도 받아올 수 없게 되고 컨트랙트를 수정할수도 없음.

 

이런 이유로 대부분의 경우 DApp의 중요한 일부를 수정할 수 있도록 하는 함수를 만들어놓는 것이 합리적

예를 들자면 컨트랙트 주소를 직접 써넣는 대신 언젠가 컨트랙트에 문제가 생기면 주소를 바꿀 수 있도록 해주는 함수를 생성해놓을 수 있음.

 

 

 

소유 가능한 컨트랙트

external 속성은 누구든지 함수를 호출하고 코드를 실행할 수 있기 때문에 보안 이슈가 있음.

컨트랙트에서 주소를 바꿀 수 있게끔 하고 싶지만 그렇다고 모든 사람이 주소를 업데이트 할 수 있기를 원하지는 않음.

이런 경우에 대처하기 위해 주로 쓰는 하나의 방법은 컨트랙트를 소유 가능 하게 만드는 것.

  ->  컨트랙트를 대상으로 특별한 권리를 가지는 소유자가 있음을 의미

 

 

 


 

 

 

OpenZeppelin의 Ownable 컨트랙트

 

 

 

/**
 * @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;
  }
}

 

 

  • 생성자(Constructor): function Ownable()는 생성자. 컨트랙트와 동일한 이름을 가진, 생략할 수 있는 특별한 함수.
    이 함수는 컨트랙트가 생성될 때 딱 한 번만 실행.
  • 함수 제어자(Function Modifier): modifier onlyOwner(): 제어자는 다른 함수들에 대한 접근을 제어하기 위해 사용되는 일종의 유사 함수. 보통 함수 실행 전의 요구사항 충족 여부를 확인하는 데에 사용. onlyOwner의 경우에는 접근을 제한해서 오직 컨트랙트의 소유자만 해당 함수를 실행할 수 있도록 하기 위해 사용될 수 있음.

 

 Ownable 컨트랙트 역할

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

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

3. 새로운 소유자에게 해당 컨트랙트의 소유권을 옮길 수 있도록 한다.

 

onlyOwner는 컨트랙트에서 흔히 쓰는 것 중 하나라 대부분의 솔리디티 DApp 들은 Ownable 컨트랙트를 복사/붙여넣기 하면서 시작함. 그리고 첫 컨트랙트는 이 컨트랙트를 상속해서 만든다.

 

 

 

 


 

 

onlyOwner 함수 제어자

 

 

 

함수제어자는 함수처럼 보이지만, function 키워드 대신 modifier 키워드를 사용하고 함수를 호출하듯이 직접 호출할 수 없음. 대신 함수 정의부 끝에 해당 함수의 작동 방식을 바꾸도록 제어자의 이름을 붙일 수 있음.

 

 

/**
 * @dev Throws if called by any account other than the owner.
 */
modifier onlyOwner() {
  require(msg.sender == owner);
  _;
}

 

 

 

contract MyContract is Ownable {
  event LaughManiacally(string laughter);

  // `onlyOwner`의 사용 방법
  function likeABoss() external onlyOwner {
    LaughManiacally("Muahahahaha");
  }
}

 

 

 

likeABoss 함수의 onlyOwner 제어자 부분보면 likeABoss 함수를 호출하면, onlyOwner의 코드가 먼저 실행

그리고 onlyOwner의 _; 부분을 likeABoss 함수로 되돌아가 해당 코드를 실행

제어자를 사용할 수 있는 다양한 방법이 있지만, 가장 일반적으로 쓰는 예시 중 하나는 함수 실행 전에 require 체크를 넣는 것

onlyOwner의 경우에는, 함수에 이 제어자를 추가하면 오직 컨트랙트의 소유자(배포한 사람)만이 해당 함수를 호출할 수 있음

참고: 이렇게 소유자가 컨트랙트에 특별한 권한을 갖도록 하는 것은 자주 필요하지만, 이게 악용될 수도 있음.
예를 들어, 소유자가 다른 사람의 것을 뺏어올 수 있도록 하는 백도어 함수를 추가할 수도 있음

이더리움에서 돌아가는 DApp이라고 해서 그것만으로 분산화되어 있다고 할 수는 없음. 반드시 전체 소스 코드를 읽어보고, 잠재적으로 걱정할 만한, 소유자에 의한 특별한 제어가 불가능한 상태인지 확인하기. 개발자로서는 잠재적인 버그를 수정하고 DApp을 안정적으로 유지하도록 하는 것과, 사용자들이 그들의 데이터를 믿고 저장할 수 있는 소유자가 없는 플랫폼을 만드는 것 사이에서 균형을 잘 잡는 것이 중요함

' :D > block chain' 카테고리의 다른 글

UNISWAP 1 : 유니스왑이란?  (0) 2022.03.14
[Solidity] 가스(Gas)  (0) 2022.02.16
[Solidity] Storage vs Memory  (0) 2022.02.15
[Solidity] 상속  (0) 2022.02.15
[Solidity] Require  (0) 2022.02.15