본문 바로가기
IT Series

Smart Contract Best Practices Revisited: Block Number vs. Timestamp 재검토된 스마트 계약 모범 사례: 블록 번호 대 타임스탬프

by 개발이 체질인 나그네 2022. 5. 18.
반응형

Last November, Spankchain, a blockchain for the adult entertainment industry, they informed investors that they had extrapolated the end date two days ahead. The culprit? With the assumptions that block confirmations typically run 15 seconds, they extrapolated the estimated block.number with auction_complete

 

지난 11월 성인 연예계 블록체인 스팽크체인은 투자자들에게 종료일을 이틀 앞당겼다고 알렸다. 그 범인? 블록 확인이 일반적으로 15초 동안 실행된다는 가정 하에, 그들은 경매_완료를 사용하여 예상 block.number를 외삽했습니다.

modifier auction_complete {
 require(auctionEndBlock <= block.number || currentAuctionState == AuctionState.success || currentAuctionState == AuctionState.cancel)
 _;}
 
Unfortunately, this assumption turned out to be incorrect, as the average block time turned out to be 13.5–14s, leading to a huge discrepancy in the end date. This incident helps to highlight confusion with block number and timestamps in the EVM. In this article, we are going to explore the block number and timestamp on the EVM, how they are used to ascribe event outcomes, and how we move forward in evaluating different uses.
불행히도 평균 블록 시간이 13.5-14초로 밝혀져 종료 날짜에 큰 불일치가 발생했기 때문에 이 가정은 잘못된 것으로 판명되었습니다. 이 사건은 EVM의 블록 번호 및 타임스탬프와의 혼동을 강조하는 데 도움이 됩니다. 이 기사에서는 EVM의 블록 번호와 타임스탬프, 이벤트 결과를 설명하는 데 사용되는 방법, 다양한 사용을 평가하는 방법을 살펴보겠습니다.
 

What is a timestamp exactly and how does it come about? Let’s say there’s a simple contract that stores the timestamp of the block in which it was mined. It would look something like this

타임스탬프란 정확히 무엇이며 어떻게 생성됩니까? 채굴된 블록의 타임스탬프를 저장하는 간단한 계약이 있다고 가정해 보겠습니다. 다음과 같이 보일 것입니다.
 
pragma solidity 0.4.11
contract getTime {
 uint myTime = now;
}

 

Compiling the contract and looking into the EVM instructions, one finds

 

계약을 컴파일하고 EVM 지침을 살펴보면 다음을 찾을 수 있습니다.

 

At the 5th instruction, TIMESTAMP is called and subsequently stored in the variable myTime. Where does the timestamp come from? Defined in the yellow paper as a “scalar value equal to the reasonable output of Unix's time()” , popular ethereum clients, geth and parity, have slightly different definitions block header validity. Both clients require that the node's internal NTP protocol does not drift from pool.ntp.org by more than 10 seconds(geth, parity ). When it comes to validating a block, however, it's a different story. Geth and parity both check if the timestamp is greater than the parent's timestamp, but geth will outright reject any block that has its current time set ahead of the node’s current time. Given the protocol, even if a malicious miner tries to “game” a timestamp, there exists not only the possibility Geth would reject the node, other miners also would be more incentivized to accept an earlier, more accurately timestamped block. This is the most sought-after outcome, otherwise, the miners would lose their rewards from the orphaned block.

 

5번째 명령어에서 TIMESTAMP가 호출되고 이어서 myTime 변수에 저장됩니다. 타임스탬프는 어디에서 가져오나요? 옐로우 페이퍼에서 "Unix의 time()의 합리적인 출력과 동일한 스칼라 값"으로 정의된 인기 있는 이더리움 클라이언트인 geth 및 parity는 블록 헤더 유효성에 대해 약간 다른 정의를 가지고 있습니다. 두 클라이언트 모두 노드의 내부 NTP 프로토콜이 pool.ntp.org에서 10초 이상 이동하지 않아야 합니다(geth, parity). 그러나 블록의 유효성을 검사하는 경우에는 이야기가 다릅니다. Geth와 parity는 모두 타임스탬프가 부모의 타임스탬프보다 큰지 확인하지만, geth는 현재 시간이 노드의 현재 시간보다 먼저 설정된 모든 블록을 완전히 거부합니다. 프로토콜을 감안할 때 악의적인 광부가 타임스탬프를 "게임"하려고 해도 Geth가 노드를 거부할 가능성이 있을 뿐만 아니라 다른 광부도 더 일찍, 더 정확하게 타임스탬프가 지정된 블록을 수락하도록 더 유인됩니다. 이것은 가장 많이 찾는 결과입니다. 그렇지 않으면 광부가 고아 블록에서 보상을 잃게 됩니다.

 

Manipulability(조작성)

Consider this alarm contract, based off of PonziGovermental contract

 

PonziGovermental 계약을 기반으로 하는 이 경보 계약을 고려하십시오.
 
tick = block.timestamp; // in constructor
function defuseAlarm() returns(bool){
 if(block.timestamp > tick+TWELVE_HOURS) {
 tick = block.timestamp;
 // … alarm remains alive
 return false;
 } else {
 // stop alarm …
 return true;
 }}

 

 

In this contract, the tick is initialized in the constructor. As long the caller of defuseAlarm sends a transaction within 12 hours, the alarm gets defused. However, assume that the only person who calls defuseAlarm does it with 1 second before the alarm sounds. Without an interfering miner, the alarm would be defused. But because timestamp is manipulable, the miner could set a timestamp 2 seconds in the future and prevent the alarm from getting defused (another transaction is needed to stop the alarm)

 

이 계약에서 틱은 생성자에서 초기화됩니다. defuseAlarm 호출자가 12시간 이내에 트랜잭션을 보내는 한 경보가 해제됩니다. 단, defuseAlarm을 호출한 사람만 알람이 울리기 1초 전에 호출한다고 가정합니다. 방해하는 광부가 없으면 경보가 해제됩니다. 그러나 타임 스탬프는 조작 가능하기 때문에 광부는 미래에 타임 스탬프를 2초로 설정하고 알람이 해제되는 것을 방지할 수 있습니다(알람을 중지하려면 다른 트랜잭션이 필요함)

 

Block Number

Block numbers, on the other hand, come about from a bit different context. Compiling a contract to get the block number one finds

 

반면에 블록 번호는 약간 다른 컨텍스트에서 발생합니다. 하나가 찾은 블록 번호를 얻기 위해 계약 컴파일

05 NUMBER
06 PUSH1 00
08 SSTORE

 

When NUMBER is called, it returns the block number of the parent increased by one. Block numbers persist over all of the nodes in the system, and the restriction is held for all Ethereum clients trying to sync. The block number is a variable where there can exist tamper-proof temporal settlements with Dapps. In this context, it seems as if block number is a much better substitute for time-dependent computation. This is true, as it is a way more reliable source of truth than the timestamp, but it lacks the readability we desire when implementing it. You wouldn’t set your alarm to block 19284832, but rather to a UTC standard date.

 

NUMBER를 호출하면 부모의 블록 번호가 1 증가하여 반환됩니다. 블록 번호는 시스템의 모든 노드에서 지속되며 동기화를 시도하는 모든 Ethereum 클라이언트에 대해 제한이 유지됩니다. 블록 번호는 Dapp과의 변조 방지 임시 정산이 존재할 수 있는 변수입니다. 이러한 맥락에서 블록 번호는 시간 종속 계산을 훨씬 더 잘 대체하는 것처럼 보입니다. 이것은 타임스탬프보다 훨씬 더 신뢰할 수 있는 진실 소스이지만 구현할 때 원하는 가독성이 부족하기 때문에 사실입니다. 19284832를 차단하도록 알람을 설정하지 않고 UTC 표준 날짜로 설정합니다.

 

Uses of Each

So what considerations should one undertake evaluating block.number and block.timestamp? Block.number is guaranteed by the protocol to increase by one compared to the previous block, while block.timestamp can be minimally gamed by a miner. As mentioned in the discussion of how geth and parity resolve timestamp, a general rule of thumb is your contract can tolerate a 30-second timestamp variation and maintain integrity, then it is safe to use a timestamp.

Let's reconstruct the auction_complete modifier with a timestamp dependence

 


그렇다면 block.number와 block.timestamp를 평가하려면 어떤 고려 사항을 취해야 할까요? Block.number는 이전 블록에 비해 1 증가하도록 프로토콜에 의해 보장되는 반면, block.timestamp는 채굴자가 최소한으로 게임을 할 수 있습니다. geth 및 parity가 타임스탬프를 해결하는 방법에 대한 논의에서 언급했듯이 일반적인 경험 법칙은 계약이 30초 타임스탬프 변동을 허용하고 무결성을 유지할 수 있다는 것이므로 타임스탬프를 사용하는 것이 안전합니다.

타임스탬프 종속성을 사용하여 경매_완료 수정자를 재구성해 보겠습니다.

 

modifier auction_complete {
 require(auctionEndTime <= now || 
 	currentAuctionState == AuctionState.success 
        || currentAuctionState == AuctionState.cancel);
 _;}

 

 

When based off of timestamp, the auction is resolved much more accurately, off by a matter of minutes rather than days. With factors like the difficulty bomb and hard/soft fork upgrades to the network, accurately extrapolating block.number to a time is dangerous and can lead to serious discrepancies in expectations. This is essentially the tradeoff being made when weighing the two options.

If miner manipulation of the timestamp comes at a cost of integrity to your smart contract, then block.number can be a recourse if used in the right context. Let's propose we have a contract Reward, that will decide on a winner of a payout after 200 blocks. It would use a modifier such as this:

 

타임스탬프를 기반으로 하면 경매가 며칠이 아닌 몇 분 만에 훨씬 더 정확하게 해결됩니다. 난이도 폭탄 및 네트워크에 대한 하드/소프트 포크 업그레이드와 같은 요소로 인해 block.number를 시간으로 정확하게 외삽하는 것은 위험하고 기대치에 심각한 불일치를 초래할 수 있습니다. 이것은 본질적으로 두 가지 옵션을 평가할 때 발생하는 절충안입니다.

타임 스탬프의 광부 조작으로 인해 스마트 계약의 무결성이 희생되는 경우 올바른 컨텍스트에서 사용되는 경우 block.number가 소구될 수 있습니다. 200 블록 후에 지불금의 승자를 결정하는 계약 보상이 있다고 제안합시다. 다음과 같은 수정자를 사용합니다.

 

modifier lottery_complete {
 require(block.number>=startingBlock+200);
 _;}

 

Even with the best-informed on block confirmation times, there exists a vulnerability of uncertainty on when the contract would execute on the modifier. This would be a situation where it’s more important that 200 blocks have passed, rather finding a winner every hour or so.

 

블록 확인 시간에 대해 가장 잘 알고 있더라도 계약이 수정자에 대해 실행될 시기에 대한 불확실성의 취약성이 존재합니다. 매 시간마다 승자를 찾는 것보다 200블록을 넘기는 것이 더 중요한 상황이 될 것입니다.

 

 

Conclusion

To recap, block.number and block.timestamp hold different utilities in constructing robust and secure smart contracts. Block numbers cannot be manipulated by miners however it can lead to larger than expected time differences; timestamps are much simpler to base temporal logic off of.

Do you love this stuff?

I’m part of the team at ConsenSys Diligence. If you have a knack for diving deeply into Solidity and the EVM, and an interest in smart contract security, we’re looking for people to join our team (Apply here).

요약하자면, block.number 및 block.timestamp는 강력하고 안전한 스마트 계약을 구성하는 데 서로 다른 유틸리티를 보유하고 있습니다. 블록 번호는 채굴자가 조작할 수 없지만 예상보다 큰 시간 차이가 발생할 수 있습니다. 타임스탬프는 시간적 논리를 기반으로 하는 것이 훨씬 간단합니다.

이 물건을 좋아합니까?

저는 ConsenSys Diligence 팀의 일원입니다. 솔리디티와 EVM에 대해 깊이 파고드는 재주가 있고 스마트 계약 보안에 관심이 있다면 우리 팀에 합류할 사람들을 찾고 있습니다(여기에서 지원).

 

반응형

댓글