Solidity에서 이더를 보내는 3가지 함수, Call Func의 Re-Entrancy Attact 방지

five1star·2022년 10월 24일
1

Sol

목록 보기
1/1

send

	payable(address).send(uint256 amount);
  • Send 함수는 payable로 선언된 EOA또는 CA에 이더를 전송하는 함수다.
  • 주어진만큼의 wei를 전송하며, 성공여부를 bool로 리턴한다.
  • 성공시 gas 2300gwei를 제공하며 가스비는 변경할 수 없다.
  • 리턴하는 bool값은 전송의 실패이지, 트랜잭션의 실패가 아니다. 즉, false가 반환되어도 지불한 amount는 소비된다.
  • 위와 같은 이유로 반드시 require로 트랜잭션 구동 조건 체크가 필요하다.

transfer

	payable(address).transfer(uint256 amount);
  • Transfer 함수는 payable로 선언된 EOA또는 CA에 이더를 전송하는 함수다.
  • 주어진만큼의 wei를 전송하며, 실패시 트랜잭션이 실패한다.
  • 성공시 gas 2300gwei를 제공하며, 가스 양은 변경할 수 없다.
  • 실패시 트랜잭션이 이루어지지 않기때문에 require를 병행할 필요가 없다.
  • 단, 2019년 이스탄불 하드포크 이후 operation code의 가스비용이 오르며 2300gwei으로 가능한 트랜잭션이 제한적이 되었다.
  • 가스비용 상승을 대비해 call함수의 사용을 권장한다.

Call

	address.call{options}("");
  • call은 송금 결과를 bool값으로 리턴한다.
  • send와 마찬가지의 이유로 require구문과 함께 사용해야한다.
  • call은 가스의 제한량이 없으며 fallback 또는 receive함수의 로직에 따라 가변적으로 변한다.
  • call의 가변적 가스소비는 재진입 공격에 취약하다.

Call 함수 사용에 따른 Re-entrancy Attack과 방지책

  • 재진입 공격이란, 악의를 가진 공격자가 특정 컨트랙트(이하 저장소)에서 이더를 전송받았을 때, fallback혹은 receive를 함수를 통해 다시 이더 전송을 요청하는 재귀를 실행하는것을 의미한다.
  • 가변적 가스소비 가능으로 인해 Re-entrancy Attack이 일어나, 방지를 위해 transfer 함수가 만들어졌으나, 가스 소비량의 제한으로 인해 결과적으로는 Call함수가 다시금 제안되었으며, 이에 따라 Checks-Effects-interactions 패턴 사용이 권장된다.

Checks-Effects-Interaction Pattern

  • 외부 컨트랙트로부터 요청이 있기 전에, 모든 상태 변화가 일어나있어야 한다.
  • Checks-Effects-Interactions는 Re-Entrancy Attack을 방지한 상태로 Call함수를 실행하기위해 디자인 패턴이다.
  • 거래 가능 식별자(이하 Lock)를 통해서 트랜잭션의 실행 가능여부를 체크해 무한정 요청을 방지한다.

bool private reentrancyLock = false;

modifier nonReentrancy() {
    require(!reentrancyLock);
    reentrancyLock = true;
    _;
    reentrancyLock = false;
}
mapping (address => uint) private userBalances;

function withdrawBalance() public nonReentrancy() {
    uint amountToWithdraw = userBalances[msg.sender];
    
    if (!(msg.sender.call{value:amountToWithdraw}())) { throw; } 
    
    // At this point, the caller's code is executed, 
    and can call withdrawBalance again
    userBalances[msg.sender] = 0;
}

위와 같은 코드에서 modifier는 reentrancyLock을 체크하고, 해당 함수가 실행되기 전에 Lock을 true로, 실행 후에 Lock이 해제된다.
이러한 방법에 따라서 재귀를 포함한 다중호출을 방지할 수 있다.

profile
자라나라 코드코드

0개의 댓글