1. 문제

모든 자금을 훔쳐라

- 신뢰할 수 없는(외부의) 컨트렉트는 예상치 못한 위치에서 코드가 실행될 수 있다
- fallback method 이해하라
- throw/revert 전파(bubbling) 현상을 이용해라
- smart contract를 공격하기 위해 또 다른 contrat를 이용하는 것도 방법이다 
pragma solidity ^0.6.12;

import "openzeppelin-contracts-06/math/SafeMath.sol";

contract Reentrance {
    using SafeMath for uint256;

    mapping(address => uint256) public balances;

    function donate(address _to) public payable {
        balances[_to] = balances[_to].add(msg.value);
    }

    function balanceOf(address _who) public view returns (uint256 balance) {
        return balances[_who];
    }

    function withdraw(uint256 _amount) public {
        if (balances[msg.sender] >= _amount) {
            (bool result,) = msg.sender.call{value: _amount}("");
            if (result) {
                _amount;
            }
            balances[msg.sender] -= _amount;
        }
    }

    receive() external payable {}
}

2. Re-entrancy

Re-Entrancy(재진입)란, 외부 컨트랙트로 이더를 보낼 때 그 컨트랙트가 다시 나의 함수를 “되돌아와서 재실행”하는 것입니다.

보통 스마트 컨트랙트는 외부 호출 시 상대방이 신뢰된 코드라고 가정합니다.

하지만 공격자는 fallback() 또는 receive() 함수를 이용해, 외부 호출 도중 의도적으로 내 컨트랙트의 함수를 다시 호출하도록 설계할 수 있습니다.

3. 코드 분석

function withdraw(uint256 _amount) public {
        if (balances[msg.sender] >= _amount) {
            (bool result,) = msg.sender.call{value: _amount}("");
            if (result) {
                _amount;
            }
            balances[msg.sender] -= _amount;
        }
    }

withdraw() 내부를 살펴보면 msg.sender.call 부분이 존재하고 하단 로직에서 balance를 감소시키는 로직이 존재한다

여기서 msg.sender.call 까지만 동작하고 balance를 감소시키지 않은채 withdraw함수를 지속적으로 호출하면 어떻게 될까? → msg.sender.call 이 호출되고 이때 동작하는 receive or fallback 함수에서 withdraw를 계속해서 호출한다면?

돈이 복사가 되는 현상이 발생한다!!!@~!~!@!@#!#!@#!#

나도 이 세계에서는 조만장자?!??!!?!? ㄷㄷ