Upgradable Smart Contracts
Upgradable Smart Contracts
해당 포스트는 moralis 의 What are Upgradable Smart Contracts? Full Guide 를 기반하여 Upgradable Contract 개념에 대해 작성한 것 입니다.
What are Upgradable Smart Contracts? Full Guide
![]() |
---|
Upgradable Smart Contract |
Warning
Upgradable Smart Contract 에 대해서 이해하려면, 블록체인 개발의 기본 및 Delegate Call 이라는 개념부터 우선적으로 이해해야한다.
여느 개발자와 마찬가지로, Smart Contract 개발자들도 코드를 리팩터링하여 개선하거나, 새로운 기능을 추가하는 식으로 유지보수한다. 그러기 위해서는 이미 블록체인 상에 배포된 Smart Contract의 소스코드 또한 수정해야할 것이다. 그러나, 블록체인은 그 특성상 한 번 블록체인 상에 이미 배포된 데이터는 변경이 불가하다. 이러한 블록체인의 특성을 immutability(불변성) 라 한다. 이러한 불변성을 통해, 유저들은 블록체인 상에서 이루어지는 다양한 거래가 악의적으로 조작되지 않을 것을 기대할 수 있다. 마찬가지로 Smart Contract 또한 블록체인 상에서 이루어진 여느 일반적인 거래와 마찬가지로 데이터로서 배포되어 동작한다. 즉, 언뜻보기에는 이미 블록체인 상에 배포된 Smart Contract에 구현된 함수나 기능 또한 블록체인의 불변성 에 의해 새로이 업데이트하거나 효율적으로 개선하는 것이 불가해 보인다.
이러한 문제를 해결하기 위해, Upgradable Smart Contract 라는 개념이 존재한다. 또 앞서 말하자면, Upgradable Smart Contract 를 구현하기 위한 방식으로는 대표적으로 transparent proxy 와 upgradable proxy 가 존재한다. 본 아티클에서는 Upgradable Smart Contract 개념과 이를 구현하기 위한 대표적인 방식인 transparent proxy 와 UUPS(Universal Upgradable Proxy Standard) proxy 를 설명한다.
What are Upgradable Smart Contracts?
Smart Contract는 사전에 정의된 규칙에 따라서 작업이 실행되도록 함으로써, 순서를 지키며 동작한다. Smart Contract가 존재하지 않는다면 , token 이나 NFT 등, 다양한 DApp 또한 존재할 수 없다. 그렇다면 Upgradable Smart Contract 란 대체 무엇일까? 일단 주지해야하는 사실이 있다. "Upgradable(업그레이드 가능한)" 이라는 개념이 변할수 있다(mutable)는 개념을 뜻하는게 아니다. Ethereum Virtual Machine (EVM) 의 대원칙중 하나는, 일단 Smart Contract가 블록체인 상에 배포되는 순간, 변할 수 없다는 것이다. 즉, immutable(불변)이다. 이러한 EVM 의 대원칙을 어길 수 없기 때문에, Upgradable Smart Contract 는 특수한 프록시 패턴을 사용한다. 프록시 패턴은 대표적인 소프트웨어 디자인 패턴으로서, 해당 패턴에 대한 자세한 설명은 Refactoring Guru 사이트의 설명으로 갈음한다. 결론부터 말하자면, Upgradable Smart Contract 를 구현하는 데에 있어서, 프록시 패턴은 아래의 두 컨트랙트로 분리하여 별도로 배포하는 식으로 적용되었다.
- proxy contract (storage contract 라고도 불린다)
- logic contract (implementation contract 라고도 불린다)
How do Upgradable Smart Contracts Work?
위의 도식을 보면, 유저가 logic contract 를 최종적으로 호출하기 위해서는 proxy contract 를 거쳐서 상호작용하고 있음을 알 수 있다. 이것이 가능한 것은, proxy contract 가 logic contract 의 주소를 저장할 수 있기 때문이다. Smart Contract 에서는 변수를 선언하여 다룰 수 있고, 변수에 특정 contract의 주소값도 저장할 수 있음은 쉬운 개념이다. (가령, Solidity 에서는 address
를 변수 타입으로 선언할 수 있다) 만약, 우리가 새로운 logic contract 를 구현해서 deploy했다면, proxy contract 에 저장된 logic contract 의 주소를 업데이트 된 새로운 컨트랙트의 주소값으로 바꿔주기만하면 된다는 것이 Upgradalbe Smart Contract 의 핵심이다.
Upgradable Smart Contract 를 구현하기 위한 프록시 패턴에는 여러 방법이 있다. 다만, 프록시 패턴이 여러 유형으로 존재하지만, 대부분의 프록시 패턴은 앞서 언급한 transparent proxy 와 UUPS(Universal Upgradable Proxy Standard) 의 형태로 구현된다. 다행히도, 이 두 타입의 예제 코드 모두 OpenZeppelin 에서 제공하고있다.
Transparent vs UUPS Proxy
앞서 언급한 바와 같이, transparent와 UUPS 두 가지의 proxy pattern들은 가장 보편적인 방법이다. 두 방법 모두 같은 원리를 사용하지만, 약간의 다른 구조를 취하고 있다. 이에 둘을 비교하고, 두 프록시 패턴의 주요한 특징을 알아보도록하자.
Transparent Proxy Pattern Type:
- 업그레이드에 관련된 기능이 proxy Contract 에서 관리됨
- 상대적으로 고비용
- 상대적으로 유지보수가 쉬움
UUPS Proxy Pattern Type:
- 업그레이드에 관련된 기능이 logic contract 에서 관리됨
- 상대적으로 저비용
- 상대적으로 유지보수가 어려움
How to Make a Smart Contract Upgradable?
Upgradable Smart Contract 를 구현하기 위해서는 일반적으로 Openzepplin 에서 구현해 둔 코드를 상속받아 활용할 수 있기 때문에, 굳이 프록시 패턴 자체를 우리 스스로 구현할 필요는 없다.
본 아티클에서는 Openzeppelin 에서 구현한 코드에 대한 구체적인 설명은 해당 아티클에서 다루지 않고, 어떻게 상속받아야 하는지만 다루겠다.
- 먼저, 우리는 initializable 컨트랙트를 상속해야한다.
contract ExampleContractName is initializable {}
transparent proxy pattern 를 구현할 때는 위와 같이 initializable 만 상속하여 사용하고, UUPS proxy pattern 을 구현한다면 아래와 같이 UUPSUpgradable 상속을 추가해야한다.
contract ExampleContractName is initializable, UUPSUpgradable {}
-
다음으로, 일반적으로
constructor
기능을 하는constructor () {}
구문과 별개로 Smart Contract 를 활성화시키는 initializer 함수를 추가할 필요가 있다. 즉,function initialize() public initializer {}
를 추가해야한다. 함수 이름이 굳이 initialize 일 필요는 없으나, 끝의initializer
modifier 부분은 필수적이다. -
다음으로, 기존 OpenZeppelin 컨트랙트 라이브러리를 아래 그림과 같이 upgradable 버전으로 바꿔줘야한다.
- initialize 함수에서, 함수인 _init 함수들을 다음과 같이 호출해야한다.
function initialize() initializer public {
__ERC1155_init(“”);
__Ownable_init();
__UUPSUpgradeable_init();
}
- 마지막으로,
msg.sender
를_msgSender()
로 대체한다. proxy address가 아닌, 유저의 wallet address와 상호작용해야하기 때문이다.
The main Limitation of Upgradable Smart Contracts
Upgradable Smart Contract 을 활용할 때 가장 주의해야할 점은 storage collision 문제이다.
storage collision 을 고려하지 않는 식으로 미숙하게 Upgradable Smart Contract 를 구현하면 치명적인 보안 사고로 이어질 수 있다.
위의 이미지는 새로운 컨트랙트 버전에서 새로운 변수명이 위에 선언되어있고, storage collision 을 일으키고 있는 것을 나타낸다.
새로운 변수명은 꼭 아래 부분에 정의해야 storage collison 이 일어나지 않는다.
TL;DR
- Immutable한 블록체인 환경에서도 Upgradable Smart Contract 를 배포할 수 있다.
- 이를 위해서 proxy pattern 을 활용하는데, proxy contract 와 logic contract 를 별도로 배포하는 방식이다.
- 본문에서는 다루고 있지 않지만, Upgradable Smart Contract 구현의 핵심은 delegate call 을 이용하는 것이다.
- storage collision 이 일어나지 않도록 주의해야한다.
Note
해당 아티클은 Openzeppelin 코드에 대한 설명 추가, storage collision에 대한 자세한 설명 등 좀 더 자세한 사항을 업데이트 할 예정입니다.