솔리디티에서 'delegatecall'은 무엇인가요?
_____Q1. `delegatecall`이란 무엇인가요?
A1. `delegatecall`은 Solidity의 저수준 호출(low-level call) 중 하나로, 호출 대상(라이브러리나 다른 컨트랙트)의 코드를 호출자의 컨텍스트(스토리지, 주소, 잔액)에서 실행하도록 합니다. 즉, 호출된 컨트랙트의 함수 로직을 가져와 호출자 컨트랙트의 상태를 변경할 수 있게 해 줍니다.
Q2. 일반 `call`이나 `callcode`와 차이점은 무엇인가요?
A2.
1. `call`: 호출 대상 컨트랙트의 상태(스토리지, 이더 잔액)를 사용하면서 대상 컨트랙트의 코드와 상태가 함께 동작.
2. `callcode`(이전 버전): 호출 대상의 코드를 호출자 스토리지로 실행했으나, `msg.sender`와 `msg.value`가 잘못 전달되는 문제가 있어 삭제됨.
3. `delegatecall`: `callcode`를 대체하며, 호출 대상 코드를 호출자 컨텍스트에서 실행. `msg.sender`와 `msg.value`를 그대로 유지.
Q3. 언제 `delegatecall`을 사용하나요?
A3. 대표적으로 업그레이더블(Upgradable) 프록시 패턴에서 사용됩니다. 로직은 라이브러리나 구현(implementation) 컨트랙트에 두고, 프록시 컨트랙트가 `delegatecall`로 해당 로직을 호출해 필요에 따라 구현 주소를 교체할 수 있게 합니다.
Q4. 예시 코드는 어떻게 되나요?
A4.
```solidity
pragma solidity ^0.8.0;
contract Logic {
uint public x;
function setX(uint _x) external {
x = _x;
}
}
contract Proxy {
address public impl; // Logic 컨트랙트 주소
impl = _impl;
}
fallback() external payable {
// 모든 호출을 impl로 delegatecall
(bool ok, ) = impl.delegatecall(msg.data);
require(ok, "delegatecall 실패");
}
}
```
이 예제에서 `Proxy`에 호출된 함수는 모두 `Logic` 컨트랙트의 로직으로 실행되지만, 상태 변수 `x`는 `Proxy`의 스토리지가 변경됩니다.
Q5. `delegatecall` 사용 시 주의사항은 무엇인가요?
A5.
- 스토리지 슬롯 충돌(Storage Collision): 호출자와 피호출자(라이브러리)의 상태 변수 레이아웃이 일치해야 안전.
- `msg.sender`·`msg.value` 유지: 호출자 컨텍스트에서 실행되므로, 권한 체크에 활용할 때 의도치 않은 차단이 발생할 수 있음.
- 재진입 공격(Reentrancy) 주의: 일반 호출과 마찬가지로 외부로 `delegatecall` 시 재진입 가능성 검토.
Q6. `delegatecall`의 가스 비용(Gas Cost)은 어떤가요?
A6. `delegatecall`은 내부적으로 코드파편을 복사하지 않고, 호출자 컨텍스트에서 실행하므로 `call`보다 약간 저렴할 수 있지만 여전히 외부 호출이므로 추가 오버헤드가 있습니다. 최근 EIP(예: EIP-1884)로 인해 가스 비용이 조정될 수 있으니, 실제 배포 전 테스트넷에서 측정하는 것이 좋습니다.
Q7. 보안 취약점은 어떤 것이 있나요?
A7.
- 잘못된 구현 주소(Impl) 설정: `impl`을 임의로 변경하거나 통제할 수 없게 해야 핵심 로직이 교체되는 것을 방지.
- 스토리지 레이아웃 불일치: 호출자와 라이브러리의 상태 변수 순서·타입이 달라질 경우, 예기치 않은 슬롯이 덮어써져 치명적 버그 초래.
- 접근 제어 누락: 프록시의 관리 기능(예: 구현 주소 변경) 메서드에만 관리자 권한 체크를 해야 합니다.
Q8. 대안이 있나요?
A8. 솔리디티 0.6.x 이상에서 제공하는 `delegatecall` 기반 프로그래밍을 쉽게 지원하는 오픈소스 프레임워크(예: OpenZeppelin의 Proxy 라이브러리)를 활용하면 보안·레이아웃 검증·관리 기능이 사전에 구현되어 있어 안전성을 높일 수 있습니다.
`delegatecall`은 다른 계약의 함수를 호출할 때, 호출하는 계약의 컨텍스트에서 실행되도록 하는 메커니즘입니다.
이는 주로 프로토콜의 업그레이드 가능성을 높이고, 코드 재사용성을 증대시키기 위해 사용됩니다.
기본 개념 `delegatecall`은 두 가지 주요 계약 간의 상호작용을 가능하게 합니다: 1. 호출하는 계약 (Caller Contract) : `delegatecall`을 사용하여 다른 계약의 함수를 호출하는 계약입니다.
2. 호출되는 계약 (Target Contract) : `delegatecall`을 통해 호출되는 계약입니다.
`delegatecall`을 사용하면 호출되는 계약의 코드가 호출하는 계약의 상태를 수정할 수 있습니다.
즉, 호출되는 계약의 함수가 호출하는 계약의 저장소에 접근하고, 그 상태를 변경할 수 있습니다.
이는 `call`과의 주요 차이점입니다.
`call`은 호출되는 계약의 상태를 변경하지만, `delegatecall`은 호출하는 계약의 상태를 변경합니다.
사용 예시 `delegatecall`은 주로 다음과 같은 상황에서 사용됩니다: - 업그레이드 가능한 계약 : 스마트 계약의 로직을 변경해야 할 경우, 기존 계약을 수정하는 대신 새로운 계약을 배포하고, 기존 계약이 새로운 계약의 함수를 `delegatecall`을 통해 호출하도록 할 수 있습니다.
이를 통해 기존 계약의 상태를 유지하면서 로직을 변경할 수 있습니다.
- 모듈화 : 여러 계약에서 공통적으로 사용되는 기능을 별도의 계약으로 분리하고, 이를 `delegatecall`을 통해 호출함으로써 코드의 중복을 줄이고 유지보수를 용이하게 할 수 있습니다.
예제 코드 다음은 `delegatecall`의 간단한 예제입니다: ```solidity pragma solidity ^0.8.0; contract Library { uint public value; function setValue(uint _value) public { value = _value; } } contract Caller { uint public value; function setLibraryValue(address _library, uint _value) public { (bool success, ) = _library.delegatecall(abi.encodeWithSignature("setValue(uint25
6)", _value)); require(success, "Delegatecall failed"); } } ``` 위의 예제에서 `Caller` 계약은 `Library` 계약의 `setValue` 함수를 `delegatecall`을 통해 호출합니다.
이때 `Library` 계약의 `setValue` 함수는 `Caller` 계약의 상태를 변경합니다.
주의사항 `delegatecall`을 사용할 때는 몇 가지 주의해야 할 점이 있습니다: 1. 상태 저장소 : `delegatecall`은 호출하는 계약의 상태 저장소를 사용하므로, 호출되는 계약의 상태 변수와 호출하는 계약의 상태 변수가 일치해야 합니다.
그렇지 않으면 예기치 않은 동작이 발생할 수 있습니다.
2. 보안 : `delegatecall`은 외부 계약의 코드를 실행하므로, 호출되는 계약이 악의적이거나 버그가 있는 경우 호출하는 계약의 상태가 손상될 수 있습니다.
따라서 신뢰할 수 있는 계약만을 대상으로 `delegatecall`을 사용해야 합니다.
3. 가스 비용 : `delegatecall`은 호출되는 계약의 코드가 실행되므로, 가스 비용이 발생합니다.
복잡한 로직을 가진 계약을 호출할 경우 가스 비용이 높아질 수 있습니다.
결론 `delegatecall`은 Solidity에서 매우 유용한 기능으로, 스마트 계약의 업그레이드 가능성과 코드 재사용성을 높이는 데 기여합니다.
그러나 사용 시 주의가 필요하며, 보안과 상태 관리에 대한 충분한 이해가 요구됩니다.
이를 통해 개발자는 더 안전하고 효율적인 스마트 계약을 작성할 수 있습니다.
작성자:
최지은 [비회원]
| 작성일자: 1년 전
2024-11-22 19:32:10
조회수: 170 | 댓글: 0 | 좋아요: 0 | 싫어요: 0
조회수: 170 | 댓글: 0 | 좋아요: 0 | 싫어요: 0
내용이 부정확하다면 싫어요를 클릭해주세요.