(수정중)
// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
contract Preservation {
// public library contracts
address public timeZone1Library;
address public timeZone2Library;
address public owner;
uint storedTime;
// Sets the function signature for delegatecall
bytes4 constant setTimeSignature = bytes4(keccak256("setTime(uint256)"));
constructor(address _timeZone1LibraryAddress, address _timeZone2LibraryAddress) public {
timeZone1Library = _timeZone1LibraryAddress;
timeZone2Library = _timeZone2LibraryAddress;
owner = msg.sender;
}
// set the time for timezone 1
function setFirstTime(uint _timeStamp) public {
timeZone1Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));
}
// set the time for timezone 2
function setSecondTime(uint _timeStamp) public {
timeZone2Library.delegatecall(abi.encodePacked(setTimeSignature, _timeStamp));
}
}
// Simple library contract to set the time
contract LibraryContract {
// stores a timestamp
uint storedTime;
function setTime(uint _time) public {
storedTime = _time;
}
}
This contract utilizes a library to store two different times for two different timezones.
The constructor creates two instances of the library for each time to be stored.
The goal of this level is for you to claim ownership of the instance you are given.
Things that might help
Look into Solidity's documentation on the delegatecall low level function, how it works,
how it can be used to delegate operations to on-chain.
libraries, and what implications it has on execution scope.
Understanding what it means for delegatecall to be context-preserving.
Understanding how storage variables are stored and accessed.
Understanding how casting works between different data types.
ownership 가져오기
delegatecall
은 외부 컨트랙트의 함수를 호출할 때 사용된다. 그냥 call
을 사용해도 되지만 delegatecall
의 특징은 context 유지에 있다. 위 사진에 잘 나와있듯이, delegatecall
을 하면 호출된 컨트랙트에서 msg.sender
와 msg.value
는 EOA의 것으로 인식된다. 또한 변수의 값이 변경되면 변경된 값이 Target contract의 storage에 저장되는 것이 아니라 Caller contract의 storage에 저장된다. Target contract에서 함수가 실행되지만 저장은 Caller contract에 되기 때문에 이러한 특징을 이용해서 컨트랙트 업그레이드가 가능하다.
업그레이드가 가능하다는 말이 처음엔 와닿지 않을 수 있다. 스마트컨트랙트는 원래 한 번 디플로이되면 수정할 수 없는 것 아닌가? 물론 맞다. 여기서 업그레이드가 가능하다는 말은 Target contract가 바뀌어도 Caller contract에는 아무 이상 없다는 말이다. 즉, Target contract에 이상이 생기면 이 점을 보완해서 Target contract B를 Caller contract와 연결시키면 된다. 어차피 그 동안의 데이터는 Caller contract에 저장되어 있을 것이고, delegatecall
을 이용하면 문제가 있는 함수의 이름은 그대로 두고 로직만 바꿔서 그대로 호출할 수 있기 때문이다.
이번 문제의 핵심은 delegatecall
을 제대로 이해했는지에 달렸다. 처음에 코드를 봤을 때는 ownership을 변경하는 함수도 없고, 이와 관련된 로직도 발견할 수 없었기 때문에 setFirstTime
이나 setSecondTime
함수가 왜 존재하는지 이해할 수 없었다. 하지만 delegatecall
과 컨트랙트를 업그레이드 할 수 있다는 것을 알고나면 문제에 좀 더 쉽게 접근할 수 있다.