Course 3 : 고급 솔리디티 개념

학미새🐥·2022년 8월 28일
0

1. 컨트랙트의 불변성

  • 솔리디티 컨트랙트는 배포 후 불변하다. (immutable)
  • 이러한 불변성으로 인해 나중에 중요하게 수정해야할 부분까지 고치지 못하는 것을 방지하고자, 중요한 일부를 수정할 수 있도록 하는 함수도 만드는 것이 좋다.

외부의존성

  • 예를들어, 접근할 다른 컨트랙트의 주소를 직접 코드에 적어놓으면, 그 컨트랙트에 문제가 생길 시 우리 컨트랙트도 영영 무용지물이 되어버린다.

2. 소유 가능한 컨트랙트

  • 컨트랙트를 소유하는 권리

OpenZepplin의 Ownable 컨트랙트

  • 오픈제플린은 솔리디티의 라이브러리

생성자(Constructor)

  • 컨트랙트와 동일한 이름을 가진 함수 (생략 가능)
  • 컨트랙트가 생성될 때 딱 한번 실행됨

함수 제어자(FUnction Modifier)

  • 다른 함수들에 대한 접근을 제어하기 위한 유사 함수
  • 함수 실행 전 요구사항 충족 여부 확인용으로 사용

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 제어자를 컨트랙트에서 자주 사용하기 때문에
대부분의 DApp은 제플린의 Ownable 컨트랙트를 복붙하고 상속받아서 사용한다고 함!

3. onlyOwner 함수 제어자

  • A컨트랙트 is B컨트랙트
  • B컨트랙트 is C컨트랙트
    일 경우,
  • A컨트랙트가 C컨트랙트도 상속받게 된다.

함수 제어자

  • 함수처럼 보이지만 function 키워드가 아닌 modifier 키워드를 사용함
  • 함수처럼 직접 호출할 수 없음
  • 함수 정의부 끝에 제어자를 붙이는 방식으로 사용 가능
    <onlyOwner 제어자 생김새>
modifier onlyOwner() {
  require(msg.sender == owner);
  _;
}

대부분의 제어자는 이렇게 require문이 먼저 나오고, 그 후 _;가 나온다.
이 제어자가 붙은 함수가 호출될 경우,
1. 함수 내부보다 제어자가 먼저 실행되어 require문이 실행되고,
2. 그 후 제어자의 _;에 도달하면 이 때 함수의 내부로 돌아가 그 내용을 실행한다.
-> 따라서 함수 내부를 실행하기 전에 확인하고자 하는 조건을 체크하는 용도로 사용되는 것!

🔊 _;함수 내부로 돌아간다!는 의미라는 것 기억하기

4. 가스

  • 솔리디티와 다른 프로그래밍 언어와의 차이점

GAS = 이더리움 DApp이 사용하는 연료

  • 사용자가 Dapp의 함수를 실행할 때마다 ETH화폐를 지불해야함
  • 함수에 사용되는 각 연산이 소모하는 자원의 양이 곧 가스 비용.
  • 따라서 함수 로직이 복잡할 수록 가스비가 비싸다.

가스를 아끼기 위한 구조체 압축

  • 구조체 내부에서 uint를 사용하는 경우!
  • 구조체 안에서는 가능한 한 작은 크기의 정수 타입을 쓰자 (ex- uint8, uint16, uint32...)
  • 또, 동일한 데이터 타입끼리 서로 옆에있도록 선언하면 저장공간을 최소화할 수 있음
    ex) uint32 a; uint c; uint32 b; (X)
    ex) uint c; uint32 a; uint32 b; (O)

5. 시간 단위

  • 솔리디티가 제공하는 시간 단위계 (Time units)

  • now 변수 : 현재의 유닉스 타임스탬프값 (1970/1/1부터 지금까지 지난 초)

  • seconds, minutes, hours, days, weeks, years : 해당 단위의 초 단위 길이로 변환됨
    ex)

      // `lastUpdated`를 `now`로 설정
      function updateTimestamp() public {
        lastUpdated = now;
      }
    
      // 마지막으로 `updateTimestamp`가 호출된 뒤 5분이 지났으면 `true`를, 5분이 아직 지나지 않았으면 `false`를 반환
      function fiveMinutesHavePassed() public view returns (bool) {
        return (now >= (lastUpdated + 5 minutes));
      }

6. 좀비 재사용 대기 시간

구조체를 인수로 전달하기

  • private, internal 함수에 구조체의 포인터를 인수로 전달할 수 있음
    ex) Zombie라는 구조체를 전달할 경우
    function _doStuff(Zombie storage _zombie) internal {
      // _zombie로 할 수 있는 것들을 처리
    }

7. Public 함수 & 보안

  • publicexternal함수는 제어자가 없는 이상 모든 사용자가 함수를 호출할 수 있다

특정 함수만이 해당 함수를 호출할 수 있게 하려면?

  1. 해당 함수를 internal로 정의한다
  2. 특정 함수에서 해당 함수를 호출한다

8. 함수 제어자의 또 다른 특징

인수를 가지는 함수 제어자

  • 함수 제어자는 인수를 받을 수 있다
    ex)
modifier olderThan(uint _age, uint _userId) {
  require (age[_userId] >= _age);
  _;
}

이렇게 제어자가 인수를 가질 수 있고,
사용할 땐, 제어자가 붙는 함수가 받는 인수를 전달받아 대입할 수 있다.

ex)

function driveCar(uint _userId) public olderThan(16, _userId) {
  // 필요한 함수 내용들
}

9. 좀비 제어자

(새로 배운 내용 없음)

10. 'view'함수를 사용해 가스 절약하기

  • view함수 : 블록체인에서 데이터를 읽기만 하면 되는 함수

🔊 view함수는 가스를 소모하지 않는다!!!

  • 블록체인 상의 어떠한 데이터도 수정하지 않기 때문에.
  • 즉, 블록체인 상에 어떠한 트랜잭션도 만들지 않는다.
  • 단, view 함수는 외부에서 호출될 경우만 무료임
    • 동일 컨트랙트 내의 다른 함수가 내부적으로 view함수를 호출하면 가스비 발생함.

11. Storage 는 비싸다

  • storage는 솔리디티의 비싼 연산 : 블록체인에 영구적으로 기록하는 것이기 때문
  • 따라서 필요한 경우가 아니면 storage의 사용을 최소화하자.
  • 다른 언어와는 달리, 데이터 집합의 개별 요소에 모두 접근하는 것이 external view함수라면, storage를 사용하는 것 보다 저렴한 방식이다.

메모리에 배열 선언하기

  • storage 없이 함수 내 배열을 만들기 위해 memory를 사용함.

  • 함수가 끝날 때까지만 존재!

  • storage로 영구 저장된 배열을 매번 업데이트하는 것보다 훨씬 나음

  • 메모리에 배열 선언하는 법

uint[] memory values = new uint[](3);
//길이 3짜리 배열을 메모리에 생성한다는 뜻
  • 메모리 배열은 반드시! 길이 인수와 함께 생성해야함!
    🔊 storage배열과 달리 array.push()로 크기가 조절되지 않음!

12. For 반복문

  • 만약 한 배열에서 다른 배열로 하나의 요소를 옮길 때 필요한 로직
    1. 전달할 요소를 새 배열에 넣는다
    2. 기존 배열에서 해당 요소를 지운다
    3. 지워진 자리를 메우기 위해 뒤 요소를 한칸씩 당긴다.
    4. 배열 길이를 1 줄인다.
      -> 연산 양 많음. 가스비 소모 큼!!

for 반복문 사용하기

  • for문은 js 반복문 문법과 동일하다
  • veiw 함수 내부에서 for반복문을 사용해서 배열 내의 모든 요소에 무료로 접근할 수 있다. (가스비 필요X)
profile
뭐든 다해보려는 공대생입니다

0개의 댓글