Solidity로 개발된 스마트 컨트랙트는 기본적으로 불변성을 가지기 때문에 배포된 후에는 코드 자체를 수정할 수 없습니다. 하지만, 다양한 디자인 패턴과 프록시(Proxy) 구조를 사용하여 스마트 컨트랙트를 수정 가능하도록 만들 수 있습니다. 이를 업그레이드 가능한 스마트 컨트랙트라고 합니다.
스마트 컨트랙트를 업그레이드 가능하게 만드는 방법
1. 프록시 패턴 (Proxy Pattern)
프록시 패턴은 업그레이드 가능한 스마트 컨트랙트 구조를 구현하는 가장 일반적인 방법입니다. 프록시 컨트랙트를 통해 호출을 실제 로직을 포함하는 로직 컨트랙트로 위임(delegate)합니다.
(1) Transparent Proxy Pattern
- 구조:
- Proxy Contract: 사용자가 상호작용하는 컨트랙트.
delegatecall을 통해 로직 컨트랙트로 호출을 위임. - Logic Contract: 실제 비즈니스 로직을 포함한 컨트랙트.
- Admin: 로직 컨트랙트를 교체할 수 있는 관리 권한 보유자.
- Proxy Contract: 사용자가 상호작용하는 컨트랙트.
- 특징:
- 프록시와 로직 분리.
- 호출자는 프록시를 통해 상호작용하지만, 내부적으로는 로직 컨트랙트를 호출.
contract Proxy {
address public implementation; // Logic Contract Address
address public admin;
constructor(address _implementation) {
admin = msg.sender;
implementation = _implementation;
}
fallback() external payable {
require(msg.sender != admin, "Admin cannot call fallback");
address impl = implementation;
require(impl != address(0), "Implementation not set");
assembly {
calldatacopy(0, 0, calldatasize())
let result := delegatecall(gas(), impl, 0, calldatasize(), 0, 0)
returndatacopy(0, 0, returndatasize())
switch result
case 0 { revert(0, returndatasize()) }
default { return(0, returndatasize()) }
}
}
function upgrade(address newImplementation) external {
require(msg.sender == admin, "Not authorized");
implementation = newImplementation;
}
}
(2) 장점
- 로직 컨트랙트를 교체하여 기능을 업그레이드 가능.
- 사용자 주소는 변경되지 않음 (프록시 주소 고정).
(3) 단점
- 초기 개발 복잡성 증가.
- 관리자가 로직 컨트랙트를 교체할 수 있어 신뢰 문제가 발생할 수 있음.
2. UUPS (Universal Upgradeable Proxy Standard)
UUPS는 업그레이드 가능한 컨트랙트를 위한 간소화된 표준입니다. 이 구조는 프록시 컨트랙트가 간단하고 관리가 쉬운 것이 특징입니다.
- 구조:
- 프록시 컨트랙트와 로직 컨트랙트는 동일한 저장소를 사용.
- 업그레이드는 로직 컨트랙트 내에서 수행.
- 코드 예제:
contract LogicContract {
address public admin;
function upgrade(address newImplementation) external {
require(msg.sender == admin, "Not authorized");
assembly {
sstore(0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc, newImplementation)
}
}
function someFunction() public pure returns (string memory) {
return "Original Function";
}
}
- 장점:
- 프록시 관리 오버헤드 감소.
- 업그레이드 로직이 로직 컨트랙트 내부에 존재하여 간단.
- 단점:
- 프록시 주소를 정확히 설정하지 않으면 문제가 발생할 수 있음.
3. Beacon Proxy Pattern
- Beacon Proxy는 프록시가
Beacon Contract를 통해 로직 컨트랙트를 참조하도록 설정합니다. - 여러 프록시 인스턴스가 동일한 로직 컨트랙트를 공유할 수 있어 가스 효율적.
- 구조:
- Beacon Contract: 업그레이드 로직 컨트랙트 주소를 저장.
- Proxy Contract: Beacon을 통해 로직을 참조.
4. Eternal Storage Pattern
Eternal Storage Pattern은 상태 저장소를 별도의 컨트랙트에 유지하여 로직 컨트랙트를 교체해도 상태를 유지할 수 있습니다.
- 구조:
- Storage Contract: 상태 데이터를 저장.
- Logic Contract: 로직과 기능을 처리.
- 장점:
- 상태 데이터와 로직 분리.
- 단점:
- 개발 복잡성 증가.
상태와 저장소 관리
Solidity에서 delegatecall을 사용하면 프록시 컨트랙트가 로직 컨트랙트의 함수를 호출하더라도 프록시 컨트랙트의 저장소를 사용합니다. 이 때문에, 저장소 레이아웃을 신중하게 관리해야 합니다.
저장소 충돌 방지
- 항상 저장소 슬롯을 동일하게 유지해야 합니다.
- Solidity 0.8.x 이상에서는
immutable키워드를 사용하여 구조를 강화할 수 있습니다.
보안 고려사항
- 관리 권한 보호:
- 업그레이드 권한을 다중 서명(Multisig)으로 관리.
- Time-lock 기법으로 변경 사항에 대한 시간을 확보.
- 데이터 손실 방지:
- 업그레이드 시 데이터 저장소를 정확히 유지하도록 관리.
- 잘못된 업그레이드 방지:
- 새 로직 컨트랙트를 철저히 테스트.
- 커뮤니티 감사(Security Audits)를 진행.
결론
스마트 컨트랙트를 수정 가능하게 만드는 가장 일반적인 방법은 프록시 패턴을 사용하는 것입니다. Transparent Proxy와 UUPS Proxy는 업그레이드 가능한 컨트랙트를 구현하는 데 적합하며, 프로젝트의 복잡성과 보안 요구 사항에 따라 적절한 구조를 선택하세요.
궁금한 점이 있다면 언제든지 문의해주세요! 😊
'이더리움' 카테고리의 다른 글
| 수정 가능한 스마트 컨트랙트의 배포 방법들 (0) | 2025.03.25 |
|---|---|
| 트랜잭션 오류 발생시, 오류 원인 파악 방법 (0) | 2025.03.20 |
| MPC Wallet 의 서명 합계 과정에 대한 수학적 증명 (0) | 2025.01.31 |
| MPC Wallet 의 개념 및 구현 (0) | 2025.01.31 |
| MultiSig 의 개념 및 구현 (2) | 2025.01.31 |