블록 timestamp 조작
- 채굴자는 블록 timestamp를 살짝 조작할 수 있다.
- timestmap가 잘못 사용되면 심각한 문제를 초래할 수 있다.
취약점
- 채굴자는 block.timestamp, 이것의 alias(참조)인 now를 조작할 수 있다. 일정 정도의 동기부여가 되면 말이다.
- Roulette 컨트랙트
1 contract Roulette {
2 uint public pastBlockTime; // forces one bet per block
3
4 constructor() public payable {} // initially fund contract
5
6 // fallback function used to make a bet
7 function () public payable {
8 require(msg.value == 10 ether); // must send 10 ether to play
9 require(now != pastBlockTime); // only 1 transaction per block
10 pastBlockTime = now;
11 if(now % 15 == 0) { // winner
12 msg.sender.transfer(this.balance);
13 }
14 }
15 }
- 간단한 복권 게임 컨트랙트이다. 블록당 1개의 트랙잰션은 10 이더를 베팅할 수 있다.
- 이 게임의 가정은 block.timestamp의 마지막 두 자리가 동일한 확률로 나온다는 것이다. (균일 확률)
- 채굴자는 block.timestamp의 15 모듈러값이 0이 되도록 timestamp를 선택할 수 있다. (모듈러 연산은 예를 들면, 시간을 나타낼 때, 13시의 경우 모듈러 12로 하면 1시가 된다)
- 또한 이것은 블록당 한 사람만 베팅할 수 있도록 했기 때문에, 프런트 러닝 문제에 취약하다. (솔루션 탈취!)
예방기법
- 블록 timestamp로 임의수를 발생하게 하면 안된다. 게임의 결정적 요소가 되게 하면 안된다.
- 시간을 사용하고자 한다면 block.number와 평균 블록 시간을 사용하여 시간을 추정하는 것이 좋다.
- 이때, 특정 블록을 선택하여 계산하는 것이 좋다. 왜냐하면 채굴자는 블록 번호를 조작할 수 없기 때문이다.