Skip to content

Commit 0f6d38b

Browse files
authored
EIPs: add eip-779.md (#5)
1 parent 993d3cb commit 0f6d38b

File tree

1 file changed

+210
-0
lines changed

1 file changed

+210
-0
lines changed

EIPs/eip-779.md

Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
# EIP-779: Hardfork Meta: DAO Fork
2+
3+
4+
## 요약
5+
"`DAO Fork`"는 ***The DAO Hack*** 사건과 관련있는 *hard fork* 입니다.
6+
다른 *hard fork* 들과 달리, `DAO Fork` 는 EVM opcode, 트랜잭션 형식, 블록 구조 등의 `Protocol` 자체에는 수정 사항이 없습니다.
7+
대신에, `DAO Fork`는 계정 목록들("child DAO" contracts)로부터 특정 계정("WithdrawDAO" contract)으로 `ether` 잔액을 전송하는 *불규칙적인 상태 변화* 를 일으키는 *hard fork* 입니다.
8+
9+
## EIP 제안 동기
10+
EIP-779는 The DAO Hack 사건으로 인해 드러난 이더리움의 보안 취약점과 그로 인한 커뮤니티의 신뢰 손상을 해결하기 위해 제안되었습니다.
11+
수백만 달러 상당의 `Ether` 도난은 네트워크 안정성에 대한 우려를 촉발시켜, 커뮤니티는 효과적인 대응책을 강력히 요구했습니다.
12+
EIP-779는 이러한 문제를 해결하고자 하드 포크(*hard fork*)를 통해 도난 자금을 회수하는 등 구체적인 기술적 조치를 제안함으로써, 이더리움 네트워크의 복원력을 강화하는 데 중요한 역할을 했습니다.
13+
14+
## 선정 이유
15+
EVM Security 에 대해 공부할 때 흥미롭게 본 `Re-entrancy Attack`의 대표적인 사례가 바로 ***The DAO Hack*** 사건이었습니다.
16+
해킹 사건 발생 후에 어떤 조치가 어떻게 일어났는지 알아보고 싶어 선정하게 되었습니다.
17+
또, 평소 궁금하던 *hard fork* 적용에 대해서도 살펴보고 싶어 선정하게 되었습니다.
18+
19+
## 본론
20+
21+
`geth` 클라이언트 프로그램에 구현된 `DAO Fork`에 관한 내용은 다음과 같습니다.
22+
23+
**The DAO** 컨트랙트(`0xbb9bc244d798123fde783fcc1c72d3bb8c189413`), **extraBalance** 컨트랙트 (`0x807640a13483f8ac783c557fcdf27be11ea4ac7a`),
24+
모든 **The DAO Creator** 컨트랙트의 *자식* 계정들(`0x4a574510c7014e4ae985403536074abe582adfc8`), 각각의 *자식* 계정들에 대한 *extraBalance* 계정 등이 모두
25+
$L$ 이라는 목록으로 인코딩되어 $1,880,000$ 번째 블록에 기록되었습니다.
26+
27+
계정 목록 $L$ 은 [gist file](https://gist.github.com/gavofyork/af747a034fbee2920f862ed352d32347)에서 확인할 수 있습니다.
28+
29+
$1,920,000$ 번째 블록의 시작 이후에 모든 `Ether` 잔액은 $L$ 로부터 특정 계정 $C$ (`0xbf4ed7b27f1d666546e30d74d50d173d20bca754`) 로 전송됩니다.
30+
중요한 점은 트랜잭션을 통한 송금이 아니라 프로토콜 단에서 강제로 `Ether` 를 옮긴다는 것입니다.
31+
32+
계정 $C$ 는 `WithdrawDAO` 라는 이름의 스마트 컨트랙트가 [구현](https://etherscan.io/address/0xbf4ed7b27f1d666546e30d74d50d173d20bca754#code)된 계정입니다.
33+
34+
```solidity
35+
/**
36+
*Submitted for verification at Etherscan.io on 2016-07-14
37+
*/
38+
39+
contract DAO {
40+
function balanceOf(address addr) returns (uint);
41+
function transferFrom(address from, address to, uint balance) returns (bool);
42+
uint public totalSupply;
43+
}
44+
45+
contract WithdrawDAO {
46+
DAO constant public mainDAO = DAO(0xbb9bc244d798123fde783fcc1c72d3bb8c189413);
47+
address public trustee = 0xda4a4626d3e16e094de3225a751aab7128e96526;
48+
49+
function withdraw(){
50+
uint balance = mainDAO.balanceOf(msg.sender);
51+
52+
if (!mainDAO.transferFrom(msg.sender, this, balance) || !msg.sender.send(balance))
53+
throw;
54+
}
55+
56+
function trusteeWithdraw() {
57+
trustee.send((this.balance + mainDAO.balanceOf(this)) - mainDAO.totalSupply());
58+
}
59+
}
60+
```
61+
62+
이 중 `withdraw()` 함수를 자세히 살펴보겠습니다.
63+
64+
```solidity
65+
function withdraw(){
66+
uint balance = mainDAO.balanceOf(msg.sender);
67+
68+
if (!mainDAO.transferFrom(msg.sender, this, balance) || !msg.sender.send(balance))
69+
throw;
70+
}
71+
```
72+
73+
우선은 `mainDAO` 컨트랙트에 존재하는 토큰의 수를 `balance` 변수에 저장합니다.
74+
이후, if 절에서 ***토큰 이전 시도******이더 전송 시도*** 를 진행합니다.
75+
만일 둘 중 하나라도 정상적으로 이뤄지지 않는다면, `throw`를 통해 예외 처리를 합니다.
76+
77+
---
78+
79+
### Geth
80+
81+
실제 `geth` 코드 내에서는 다음과 같은 코드들을 확인할 수 있었습니다.
82+
83+
#### `params/dao.go`
84+
- `DAO Fork`를 적용하기 위한 여러 *parameter* 값들을 기록한 파일입니다.
85+
- 사고로 흩어진 `ether`를 모으기 위해 작성된 컨트랙트 ***DAORefundContract***,
86+
해커로 인해 `ether`가 비정상적으로 모이게 된 계정들의 목록 ***DrainList*** 등을 명시하고 있습니다.
87+
```go
88+
// DAOForkBlockExtra is the block header extra-data field to set for the DAO fork
89+
// point and a number of consecutive blocks to allow fast/light syncers to correctly
90+
// pick the side they want ("dao-hard-fork").
91+
// EIP-779, DAO hard-fork 지점 이후 및 추가로 연속되는 블록들의 `extra-data` 필드에
92+
// "dao-hard-fork" 를 16진수 형태로 변환하여 기록합니다.
93+
// 이를 통해, 빠른 동기화나 경량 클라이언트 같은 동기화 메커니즘들이 올바른 체인을 선택하도록 돕습니다.
94+
var DAOForkBlockExtra = common.FromHex("0x64616f2d686172642d666f726b")
95+
96+
// DAOForkExtraRange is the number of consecutive blocks from the DAO fork point
97+
// to override the extra-data in to prevent no-fork attacks.
98+
// EIP-779, `no-fork attack` 으로부터 체인을 보호하기 위해 얼마나 많은
99+
// DAO fork 지점 이후 연속되는 블록의 `extra-data`에 덮어쓰기를 할 것인지 명시합니다.
100+
var DAOForkExtraRange = big.NewInt(10)
101+
102+
// DAORefundContract is the address of the refund contract to send DAO balances to.
103+
// EIP-779, 환불(refund)을 위해 사용할 refund contract 의 주소를 명시합니다.
104+
var DAORefundContract = common.HexToAddress("0xbf4ed7b27f1d666546e30d74d50d173d20bca754")
105+
106+
// DAODrainList is the list of accounts whose full balances will be moved into a
107+
// refund contract at the beginning of the dao-fork block.
108+
// EIP-779, 돈을 회수할 계정을 명시합니다.
109+
func DAODrainList() []common.Address {
110+
return []common.Address{
111+
common.HexToAddress("0xd4fe7bc31cedb7bfb8a345f31e668033056b2728"),
112+
common.HexToAddress("0xb3fb0e5aba0e20e5c49d252dfd30e102b171a425"),
113+
common.HexToAddress("0x2c19c7f9ae8b751e37aeb2d93a699722395ae18f"),
114+
common.HexToAddress("0xecd135fa4f61a655311e86238c92adcd779555d2"),
115+
common.HexToAddress("0x1975bd06d486162d5dc297798dfc41edd5d160a7"),
116+
// ... 생략
117+
}
118+
}
119+
120+
```
121+
122+
#### `consensus/misc/dao.go`
123+
- `params/dao.go` 에서의 값들을 바탕으로 `DAO Fork` 를 적용하는 함수가 정의된 파일입니다.
124+
- `DAORefundContract` 의 존재 여부를 검사하고, `DAODrainList` 에 명시된 계정들로부터
125+
`DAORefundContract``Ether` 를 강제로 옮기는 과정을 진행합니다.
126+
```go
127+
// ApplyDAOHardFork modifies the state database according to the DAO hard-fork
128+
// rules, transferring all balances of a set of DAO accounts to a single refund
129+
// contract.
130+
//
131+
// EIP-779, TheDAO hard-fork 에 따라 DB의 상태를 변경하는 함수입니다.
132+
// EIP-779에서도 설명하듯이 DAODrainList 의 계정들로부터 하나의 DAORefundContract 에
133+
// 돈을 전송하게 됩니다.
134+
func ApplyDAOHardFork(statedb *state.StateDB) {
135+
// Retrieve the contract to refund balances into
136+
// EIP-779, 돈을 받을 계정이 존재하지 않는다면 새로 하나 생성합니다.
137+
// 참고로, 계정주소는 "common.HexToAddress("0xbf4ed7b27f1d666546e30d74d50d173d20bca754")" 입니다.
138+
if !statedb.Exist(params.DAORefundContract) {
139+
statedb.CreateAccount(params.DAORefundContract)
140+
}
141+
142+
// Move every DAO account and extra-balance account funds into the refund contract
143+
// 모든 `DAODrainList` 의 계정으로부터 `refund contract`에 돈을 보냅니다.
144+
for _, addr := range params.DAODrainList() {
145+
statedb.AddBalance(params.DAORefundContract, statedb.GetBalance(addr), tracing.BalanceIncreaseDaoContract)
146+
statedb.SetBalance(addr, new(uint256.Int), tracing.BalanceDecreaseDaoAccount)
147+
}
148+
}
149+
```
150+
151+
#### `core/chain_makers.go`
152+
- `chain_makers.go` 파일은 블록체인에 블록을 생성하고 추가하는 기능과 관련된 유틸리티와 도구들을 정의합니다.
153+
- `DAO fork` 와 같은 *hard fork* 를 적용하는 것도 아래 코드와 같이 구현되어 있습니다.
154+
```go
155+
// ...
156+
// EIP-779, DAO fork 를 지원하고 Chain Config에 DAO fork 블록 넘버가 현재 블록 넘버와 동일하다면
157+
// TheDAO hard-fork 를 적용합니다.
158+
if config.DAOForkSupport && config.DAOForkBlock != nil && config.DAOForkBlock.Cmp(b.header.Number) == 0 {
159+
misc.ApplyDAOHardFork(statedb)
160+
}
161+
// ...
162+
```
163+
164+
#### `core/state_processor.go`
165+
- 이더리움의 핵심 기능 중 하나인 상태 전이(state transition) 을 구현합니다.
166+
- `DAO fork`에 관한 코드도 구현되어 있는 것을 확인할 수 있습니다.
167+
```go
168+
// Process processes the state changes according to the Ethereum rules by running
169+
// the transaction messages using the statedb and applying any rewards to both
170+
// the processor (coinbase) and any included uncles.
171+
//
172+
// Process returns the receipts and logs accumulated during the process and
173+
// returns the amount of gas that was used in the process. If any of the
174+
// transactions failed to execute due to insufficient gas it will return an error.
175+
func (p *StateProcessor) Process(block *types.Block, statedb *state.StateDB, cfg vm.Config) (types.Receipts, []*types.Log, uint64, error) {
176+
var (
177+
receipts types.Receipts
178+
usedGas = new(uint64)
179+
header = block.Header()
180+
blockHash = block.Hash()
181+
blockNumber = block.Number()
182+
allLogs []*types.Log
183+
gp = new(GasPool).AddGas(block.GasLimit())
184+
)
185+
186+
// Mutate the block and state according to any hard-fork specs
187+
// EIP-779, DAO fork 를 지원하고 Chain Config에 DAO fork 블록 넘버가 현재 블록 넘버와 동일하다면
188+
// TheDAO hard-fork 를 적용합니다.
189+
if p.config.DAOForkSupport && p.config.DAOForkBlock != nil && p.config.DAOForkBlock.Cmp(block.Number()) == 0 {
190+
misc.ApplyDAOHardFork(statedb)
191+
}
192+
// ...
193+
```
194+
195+
---
196+
197+
**The DAO 해킹 사건**에서는 해커가 탈취한 이더가 28일의 잠금 기간을 갖는 자식 DAO 계정에 입금되었기 때문에, 투자자들이 즉각적으로 자금을 잃는 상황은 발생하지 않았습니다.
198+
이러한 잠금 기간은 이더리움 커뮤니티에 대응할 시간을 제공했고, 결국 `DAO hard fork` 를 통해 탈취된 자금의 대부분을 회수할 수 있었습니다.
199+
그러나 모든 커뮤니티 구성원이 이 *hard fork* 를 지지한 것은 아니었으며, *hard fork* 를 반대하는 일부는 `이더리움 클래식`(`Ethereum Classic`, `ETC`)이라는 새로운 체인을 만들어 `이더리움`의 원래 체인과 분리되었습니다.
200+
`이더리움 클래식`은 *hard fork* 를 반영하지 않고 **The DAO 해킹** 전의 원래 체인을 유지하고 있습니다.
201+
202+
203+
## Reference
204+
https://eips.ethereum.org/EIPS/eip-779
205+
206+
https://medium.com/swlh/the-story-of-the-dao-its-history-and-consequences-71e6a8a551ee
207+
208+
https://ethereum.stackexchange.com/questions/3981/what-is-the-address-and-balance-of-the-daos-extrabalance-account
209+
210+
https://www.gemini.com/cryptopedia/the-dao-hack-makerdao#section-the-response-to-the-dao-hack

0 commit comments

Comments
 (0)