[크립토좀비] 레슨3. 고급 솔리디티 (1) onlyOwner modifier

가영·2021년 4월 8일
0

솔리디티

목록 보기
3/7
post-thumbnail

컨트랙트의 불변성

DApp만이 가지는 특징: Immutable

👉🏻 컨트랙트로 배포한 최초의 코드는 항상 블록체인에 영구적으로 존재하게 된다.

👉🏻 이게 바로 솔리디티에서 보안이 큰 이슈인 이유이다 ❗

만약 컨트랙트 코드에 결점이 있더라도, 그것을 이후에 고칠 수 있는 방법이 전혀 없다. 우리가 만약 그런 컨트랙트의 개발자라면 결점을 보완한 새로운 스마트 컨트랙트 주소를 사용자들에게 일일히 전하는 수밖에 없다.


외부 의존성

레슨 2에서 우리는 크립토키티의 컨트랙트의 주소를 직접 써넣었었다.

address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;

근데 이런 식으로 하면, 만약 크립터키니 컨트랙트가 새롭게 수정돼야할 때, 크립토키티에 의존하고 있는 지금 이 컨트랙트도 다 새로 만들어야한다.

그렇게 되지 않게 하기 위해, 밑에처럼 코드를 수정해야한다.

KittyInterface kittyContract; // KittyInterface 변수 생성
function setKittyContractAddress(address _address) external {
    kittyContract = KittyInterface(_address);
  }

크립토키티 컨트랙트의 주소를 변경할 수 있는 함수를 만들어주었고 덕분에 우리의 좀비 컨트랙트는 외부 의존성을 극복했따👻

근데 이 코드에는 문제가 있다.

function setKittyContractAddress(address _address) external;

setKittyContractAddress 함수가 external로 선언돼있어서 모든 사용자가 이 함수를 사용해서 크립토키티 컨트랙트의 주소를 바꿀 수도 있을 것이기 때문이다.

이제 우리는 setKittyContractAddress 함수를, 좀비 컨트랙트를 만든(소유한) 사용자만이 사용할 수 있게 해줄 제어자(modifier)가 필요해진것이다💥

이런 경우에 대처하기 위해 최근에 주로 쓰는 방법 중 하나는 컨트랙트를 Ownable 하게 만드는 것이다! 그게 뭐냐구?


OpenZepplin의 Ownable 컨트랙트

OpenZepplin은 DApp에서 사용할 수 있는 검증받은 스마트 컨트랙트 라이브러리다. 우리는 OpenZepplin이 제공하는 Ownable 컨트랙트를 이용해서 setKittyContractAddress 함수에 제어자를 추가해줄 것이다.

우선 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;
  }
}

Ownable 컨트랙트는 기본적으로 다음과 같은 것들을 하네:

  • 컨트랙트가 생성되면 컨트랙트의 생성자가 ownermsg.sender(컨트랙트를 배포한 사람)를 대입한다. 🥺❓

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

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

이런 식으로 자주 이용한다고 ... 💫💨


onlyOwner modifier 사용하기

onlyOwner의 경우에는, 함수에 이 제어자를 추가하면 오직 컨트랙트의 소유자(자네가 배포했다면 자네겠지)만이 해당 함수를 호출할 수 있네.

우리가 어쩌다 onlyOwner 컨트랙트까지 공부하게 된건지 다시 돌아보면,

크립토키티 컨트랙트의 주소를 바꾸는 함수인 setCryptoKittyContractAddress 에 새로운 제어자를 추가해서 컨트랙트 소유자가 아니면 함수를 호출할 수 없도록 하기 위해서였다!

OnlyOwner 컨트랙트의 코드를 다시 보면,

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

onlyOwner()modifier로 정의 돼있고, 우리는 이 제어자를 사용해서 다음과 같이 함수를 선언해줄 수 있다.

function setCryptoKittyContractAddress(address _newAddress) external onlyOwner {
  ...
}

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

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

0개의 댓글