// SPDX-License-Identifier: MIT
pragma solidity ^0.6.0;
interface Building {
function isLastFloor(uint) external returns (bool);
}
contract Elevator {
bool public top;
uint public floor;
function goTo(uint _floor) public {
Building building = Building(msg.sender);
if (! building.isLastFloor(_floor)) {
floor = _floor;
top = building.isLastFloor(floor);
}
}
}
This elevator won't let you reach the top of your building. Right?
Things that might help:
Sometimes solidity is not good at keeping promises.
This Elevator expects to be used from a Building.
꼭대기 층에 가면 될 것 같지만 어디까지가 끝인지 모른다. Top
에 true 값을 주면 되지 않을까?
한 동안 골머리를 앓다가 답을 봐도 이해가 안 됐었다. 먼저 인터페이스에 대한 이해가 필요하다. 인터페이스를 설명할 때 자주 드는 예는 자동차의 브레이크다. 운전할 때 운전자는 브레이크가 어떻게 설계됐는지 알 필요가 없이 그냥 밟기만 하면 된다. 운전자가 알아야 할 것은 브레이크를 밟으면 자동차가 멈춘다는 것이다.
이와 같이 컨트랙트에 있는 함수를 다른 컨트랙트에서 사용하고자 할 때 인터페이스를 사용할 수 있는데, 예를 들어 컨트랙트A에 있는 함수A'를 컨트랙트B에서 사용하고자 할 때 사용자는 함수A'가 어떤 메커니즘으로 설계돼있는지 알 필요가 없다. 그저 인터페이스에서 호출하면 함수A'가 동작한다는 것만 알면 된다. 이 때 컨트랙트A의 주소가 필요하다.
Building building = Building(msg.sender);
인터페이스를 사용할 때 보통 컨트랙트의 주소를 넣는다. 그런데 여기에는 msg.sender
를 인자로 받는다. 인터페이스를 사용할 때 컨트랙트의 주소가 반드시 필요한 게 아닌걸까. 해답과 관련된 의문은 아니지만 왜 이렇게 설계했는지, 어떻게 동작하는지 궁금했다. stack overflow에 질문해봤지만 아직까지 명쾌하게 이해되지 않았다. 이해되는대로 글로 남길 예정이다.
의문은 아직 풀리지 않았지만 문제에 대한 해답은 인터페이스에 선언된 상태에 있었다. view
또는 pure
로 선언되지 않은 인터페이스는 상태를 변환시킬 수 있다. 핵심은 isLastFloor()
함수가 두 번째 호출될 때 boolean이 바뀌도록 하는 것이다. 이렇게 되면 처음 false였던 상태로 goTo
함수가 실행되고 if문이 실행된다. floor 값을 전달한 뒤 다시 한 번 isLastFloor()
함수가 호출되면 top
값에는 true가 할당되면서 문제가 풀린다.
여기서 얻을 수 있는 교훈은 interface로 선언된 함수의 값을 고정시키고 싶을 때 반드시 view
또는 pure
를 사용해야 한다는 것이다.