Skip to content

Commit

Permalink
EIPs: add eip-779.md (#5)
Browse files Browse the repository at this point in the history
  • Loading branch information
c0np4nn4 committed Apr 8, 2024
1 parent 993d3cb commit 0f6d38b
Showing 1 changed file with 210 additions and 0 deletions.
210 changes: 210 additions & 0 deletions EIPs/eip-779.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
# EIP-779: Hardfork Meta: DAO Fork


## 요약
"`DAO Fork`"는 ***The DAO Hack*** 사건과 관련있는 *hard fork* 입니다.
다른 *hard fork* 들과 달리, `DAO Fork` 는 EVM opcode, 트랜잭션 형식, 블록 구조 등의 `Protocol` 자체에는 수정 사항이 없습니다.
대신에, `DAO Fork`는 계정 목록들("child DAO" contracts)로부터 특정 계정("WithdrawDAO" contract)으로 `ether` 잔액을 전송하는 *불규칙적인 상태 변화* 를 일으키는 *hard fork* 입니다.

## EIP 제안 동기
EIP-779는 The DAO Hack 사건으로 인해 드러난 이더리움의 보안 취약점과 그로 인한 커뮤니티의 신뢰 손상을 해결하기 위해 제안되었습니다.
수백만 달러 상당의 `Ether` 도난은 네트워크 안정성에 대한 우려를 촉발시켜, 커뮤니티는 효과적인 대응책을 강력히 요구했습니다.
EIP-779는 이러한 문제를 해결하고자 하드 포크(*hard fork*)를 통해 도난 자금을 회수하는 등 구체적인 기술적 조치를 제안함으로써, 이더리움 네트워크의 복원력을 강화하는 데 중요한 역할을 했습니다.

## 선정 이유
EVM Security 에 대해 공부할 때 흥미롭게 본 `Re-entrancy Attack`의 대표적인 사례가 바로 ***The DAO Hack*** 사건이었습니다.
해킹 사건 발생 후에 어떤 조치가 어떻게 일어났는지 알아보고 싶어 선정하게 되었습니다.
또, 평소 궁금하던 *hard fork* 적용에 대해서도 살펴보고 싶어 선정하게 되었습니다.

## 본론

`geth` 클라이언트 프로그램에 구현된 `DAO Fork`에 관한 내용은 다음과 같습니다.

**The DAO** 컨트랙트(`0xbb9bc244d798123fde783fcc1c72d3bb8c189413`), **extraBalance** 컨트랙트 (`0x807640a13483f8ac783c557fcdf27be11ea4ac7a`),
모든 **The DAO Creator** 컨트랙트의 *자식* 계정들(`0x4a574510c7014e4ae985403536074abe582adfc8`), 각각의 *자식* 계정들에 대한 *extraBalance* 계정 등이 모두
$L$ 이라는 목록으로 인코딩되어 $1,880,000$ 번째 블록에 기록되었습니다.

계정 목록 $L$ 은 [gist file](https://gist.github.com/gavofyork/af747a034fbee2920f862ed352d32347)에서 확인할 수 있습니다.

$1,920,000$ 번째 블록의 시작 이후에 모든 `Ether` 잔액은 $L$ 로부터 특정 계정 $C$ (`0xbf4ed7b27f1d666546e30d74d50d173d20bca754`) 로 전송됩니다.
중요한 점은 트랜잭션을 통한 송금이 아니라 프로토콜 단에서 강제로 `Ether` 를 옮긴다는 것입니다.

계정 $C$ 는 `WithdrawDAO` 라는 이름의 스마트 컨트랙트가 [구현](https://etherscan.io/address/0xbf4ed7b27f1d666546e30d74d50d173d20bca754#code)된 계정입니다.

```solidity
/**
*Submitted for verification at Etherscan.io on 2016-07-14
*/
contract DAO {
function balanceOf(address addr) returns (uint);
function transferFrom(address from, address to, uint balance) returns (bool);
uint public totalSupply;
}
contract WithdrawDAO {
DAO constant public mainDAO = DAO(0xbb9bc244d798123fde783fcc1c72d3bb8c189413);
address public trustee = 0xda4a4626d3e16e094de3225a751aab7128e96526;
function withdraw(){
uint balance = mainDAO.balanceOf(msg.sender);
if (!mainDAO.transferFrom(msg.sender, this, balance) || !msg.sender.send(balance))
throw;
}
function trusteeWithdraw() {
trustee.send((this.balance + mainDAO.balanceOf(this)) - mainDAO.totalSupply());
}
}
```

이 중 `withdraw()` 함수를 자세히 살펴보겠습니다.

```solidity
function withdraw(){
uint balance = mainDAO.balanceOf(msg.sender);
if (!mainDAO.transferFrom(msg.sender, this, balance) || !msg.sender.send(balance))
throw;
}
```

우선은 `mainDAO` 컨트랙트에 존재하는 토큰의 수를 `balance` 변수에 저장합니다.
이후, if 절에서 ***토큰 이전 시도******이더 전송 시도*** 를 진행합니다.
만일 둘 중 하나라도 정상적으로 이뤄지지 않는다면, `throw`를 통해 예외 처리를 합니다.

---

### Geth

실제 `geth` 코드 내에서는 다음과 같은 코드들을 확인할 수 있었습니다.

#### `params/dao.go`
- `DAO Fork`를 적용하기 위한 여러 *parameter* 값들을 기록한 파일입니다.
- 사고로 흩어진 `ether`를 모으기 위해 작성된 컨트랙트 ***DAORefundContract***,
해커로 인해 `ether`가 비정상적으로 모이게 된 계정들의 목록 ***DrainList*** 등을 명시하고 있습니다.
```go
// DAOForkBlockExtra is the block header extra-data field to set for the DAO fork
// point and a number of consecutive blocks to allow fast/light syncers to correctly
// pick the side they want ("dao-hard-fork").
// EIP-779, DAO hard-fork 지점 이후 및 추가로 연속되는 블록들의 `extra-data` 필드에
// "dao-hard-fork" 를 16진수 형태로 변환하여 기록합니다.
// 이를 통해, 빠른 동기화나 경량 클라이언트 같은 동기화 메커니즘들이 올바른 체인을 선택하도록 돕습니다.
var DAOForkBlockExtra = common.FromHex("0x64616f2d686172642d666f726b")

// DAOForkExtraRange is the number of consecutive blocks from the DAO fork point
// to override the extra-data in to prevent no-fork attacks.
// EIP-779, `no-fork attack` 으로부터 체인을 보호하기 위해 얼마나 많은
// DAO fork 지점 이후 연속되는 블록의 `extra-data`에 덮어쓰기를 할 것인지 명시합니다.
var DAOForkExtraRange = big.NewInt(10)

// DAORefundContract is the address of the refund contract to send DAO balances to.
// EIP-779, 환불(refund)을 위해 사용할 refund contract 의 주소를 명시합니다.
var DAORefundContract = common.HexToAddress("0xbf4ed7b27f1d666546e30d74d50d173d20bca754")

// DAODrainList is the list of accounts whose full balances will be moved into a
// refund contract at the beginning of the dao-fork block.
// EIP-779, 돈을 회수할 계정을 명시합니다.
func DAODrainList() []common.Address {
return []common.Address{
common.HexToAddress("0xd4fe7bc31cedb7bfb8a345f31e668033056b2728"),
common.HexToAddress("0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425"),
common.HexToAddress("0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f"),
common.HexToAddress("0xecd135fa4f61a655311e86238c92adcd779555d2"),
common.HexToAddress("0x1975bd06d486162d5dc297798dfc41edd5d160a7"),
// ... 생략
}
}

```

#### `consensus/misc/dao.go`
- `params/dao.go` 에서의 값들을 바탕으로 `DAO Fork` 를 적용하는 함수가 정의된 파일입니다.
- `DAORefundContract` 의 존재 여부를 검사하고, `DAODrainList` 에 명시된 계정들로부터
`DAORefundContract``Ether` 를 강제로 옮기는 과정을 진행합니다.
```go
// ApplyDAOHardFork modifies the state database according to the DAO hard-fork
// rules, transferring all balances of a set of DAO accounts to a single refund
// contract.
//
// EIP-779, TheDAO hard-fork 에 따라 DB의 상태를 변경하는 함수입니다.
// EIP-779에서도 설명하듯이 DAODrainList 의 계정들로부터 하나의 DAORefundContract 에
// 돈을 전송하게 됩니다.
func ApplyDAOHardFork(statedb *state.StateDB) {
// Retrieve the contract to refund balances into
// EIP-779, 돈을 받을 계정이 존재하지 않는다면 새로 하나 생성합니다.
// 참고로, 계정주소는 "common.HexToAddress("0xbf4ed7b27f1d666546e30d74d50d173d20bca754")" 입니다.
if !statedb.Exist(params.DAORefundContract) {
statedb.CreateAccount(params.DAORefundContract)
}

// Move every DAO account and extra-balance account funds into the refund contract
// 모든 `DAODrainList` 의 계정으로부터 `refund contract`에 돈을 보냅니다.
for _, addr := range params.DAODrainList() {
statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), tracing.BalanceIncreaseDaoContract)
statedb.SetBalance(addr, new(uint256.Int), tracing.BalanceDecreaseDaoAccount)
}
}
```

#### `core/chain_makers.go`
- `chain_makers.go` 파일은 블록체인에 블록을 생성하고 추가하는 기능과 관련된 유틸리티와 도구들을 정의합니다.
- `DAO fork` 와 같은 *hard fork* 를 적용하는 것도 아래 코드와 같이 구현되어 있습니다.
```go
// ...
// EIP-779, DAO fork 를 지원하고 Chain Config에 DAO fork 블록 넘버가 현재 블록 넘버와 동일하다면
// TheDAO hard-fork 를 적용합니다.
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 {
misc.ApplyDAOHardFork(statedb)
}
// ...
```

#### `core/state_processor.go`
- 이더리움의 핵심 기능 중 하나인 상태 전이(state transition) 을 구현합니다.
- `DAO fork`에 관한 코드도 구현되어 있는 것을 확인할 수 있습니다.
```go
// Process processes the state changes according to the Ethereum rules by running
// the transaction messages using the statedb and applying any rewards to both
// the processor (coinbase) and any included uncles.
//
// Process returns the receipts and logs accumulated during the process and
// returns the amount of gas that was used in the process. If any of the
// transactions failed to execute due to insufficient gas it will return an error.
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
var (
receipts types.Receipts
usedGas = new(uint64)
header = block.Header()
blockHash = block.Hash()
blockNumber = block.Number()
allLogs []*types.Log
gp = new(GasPool).AddGas(block.GasLimit())
)

// Mutate the block and state according to any hard-fork specs
// EIP-779, DAO fork 를 지원하고 Chain Config에 DAO fork 블록 넘버가 현재 블록 넘버와 동일하다면
// TheDAO hard-fork 를 적용합니다.
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
misc.ApplyDAOHardFork(statedb)
}
// ...
```
---
**The DAO 해킹 사건**에서는 해커가 탈취한 이더가 28일의 잠금 기간을 갖는 자식 DAO 계정에 입금되었기 때문에, 투자자들이 즉각적으로 자금을 잃는 상황은 발생하지 않았습니다.
이러한 잠금 기간은 이더리움 커뮤니티에 대응할 시간을 제공했고, 결국 `DAO hard fork` 를 통해 탈취된 자금의 대부분을 회수할 수 있었습니다.
그러나 모든 커뮤니티 구성원이 이 *hard fork* 를 지지한 것은 아니었으며, *hard fork* 를 반대하는 일부는 `이더리움 클래식`(`Ethereum Classic`, `ETC`)이라는 새로운 체인을 만들어 `이더리움`의 원래 체인과 분리되었습니다.
`이더리움 클래식`은 *hard fork* 를 반영하지 않고 **The DAO 해킹** 전의 원래 체인을 유지하고 있습니다.
## Reference
https://eips.ethereum.org/EIPS/eip-779
https://medium.com/swlh/the-story-of-the-dao-its-history-and-consequences-71e6a8a551ee
https://ethereum.stackexchange.com/questions/3981/what-is-the-address-and-balance-of-the-daos-extrabalance-account
https://www.gemini.com/cryptopedia/the-dao-hack-makerdao#section-the-response-to-the-dao-hack

0 comments on commit 0f6d38b

Please sign in to comment.