ERC-3448 메타프록시 표준 (MetaProxy Standard)
Final · 기타 · 2021-03-29 작성
고정된 설정값(메타데이터)을 코드 끝에 붙인 초소형 프록시 컨트랙트의 바이트코드를 표준으로 정해, 같은 기능을 값만 바꿔 싸게 복제하고 도구가 그 구조를 쉽게 확인할 수 있게 합니다.
왜 필요한가
같은 컨트랙트 기능을 설정값만 바꿔 여러 주소에 배포하고 싶을 때, 각 복제본이 자기 설정값을 저장 공간(storage)에 넣으면 비용이 비싸집니다. 또 프록시(호출을 다른 컨트랙트로 넘겨주는 컨트랙트)의 코드가 제각각이면, 이더스캔 같은 외부 도구는 이 컨트랙트가 어디로 호출을 넘기는지, 어떤 값이 붙어 있는지 일일이 분석해야 알 수 있습니다. 정해진 최소 바이트코드가 없으면 복제본의 동작을 목적지 컨트랙트의 동작과 같다고 믿고 의존하기도 어렵습니다. 이 표준은 알려진 하나의 바이트코드 형태를 정해, 복제 배포를 싸게 하면서 붙어 있는 메타데이터까지 누구나 확인하고 검증할 수 있게 합니다.
심층 설명
바이트코드 명세
메타프록시의 바이트코드는 원문에 명시된 54바이트 코드 363d3d373d3d3d3d60368038038091363936013d73...5af43d3d93803e603457fd5bf3으로 고정되며 인덱스 21~41(포함)의 바이트를 기능을 담은 마스터 컨트랙트의 20바이트 주소로 바꿔 넣습니다. 그 뒤에는 임의의 메타데이터를 붙일 수 있고 전체 바이트코드의 마지막 32바이트(1워드)는 메타데이터의 길이를 바이트 단위 uint256으로 나타내야 합니다. 즉 전체 구조는 <54바이트 메타프록시> <임의 데이터> <임의 데이터의 바이트 길이> 순서입니다.
호출 전달 동작
프록시는 모든 호출을 DELEGATECALL로 전달합니다. 이때 호출 입력(calldata) 전체에 바이트코드 끝에 붙은 메타데이터를 이어 붙여 구현 컨트랙트에 넘기고 구현 컨트랙트의 반환값을 호출자에게 다시 전달합니다. 구현 컨트랙트가 revert하면 revert와 함께 그 payload 데이터도 호출자에게 그대로 전달됩니다. 주석 달린 바이트코드를 보면 CALLDATACOPY로 호출 데이터를 복사하고 CODECOPY로 코드의 54바이트 이후 부분(메타데이터)을 그 뒤에 복사한 다음, DELEGATECALL 결과에 따라 RETURN 또는 REVERT로 분기합니다.
설계 목표
원문이 밝힌 목표는 세 가지입니다. 첫째, 각 복제본(child)의 불변 메타데이터를 저장 공간 슬롯 대신 바이트코드에 담아 싸게 보관합니다. 둘째, 복제본 배포 비용을 낮춥니다. 셋째, revert 메시지가 호출자까지 전달되도록 오류 반환 버블링을 처리합니다. 배포용 바이트코드는 PUSH1 11, CODESIZE, SUB, DUP1, PUSH1 11, RETURNDATASIZE, CODECOPY, RETURNDATASIZE, RETURN으로, 배포 코드 11바이트를 제외한 나머지를 런타임 코드로 반환하는 단순한 구조입니다.
메타데이터 읽는 방법(예시)
원문의 예시 코드는 표준의 일부가 아닌 제안입니다. 프록시가 호출 데이터 뒤에 메타데이터를 붙여 주므로, 구현 컨트랙트는 calldatasize()에서 32를 뺀 위치에서 메타데이터 길이를 읽고 거기서 길이만큼 앞으로 간 지점부터 메타데이터를 calldatacopy로 복사해 abi.decode로 해석할 수 있습니다. 예시로 호출 없이 읽는 getMetadataWithoutCall과 자기 자신에 대한 호출로 읽는 getMetadataViaCall 두 방식이 제시되며 Solidity 구조체나 임의의 커스텀 인코딩도 사용할 수 있습니다.
팩토리 활용과 초기화
예시의 팩토리는 두 방식으로 프록시를 만듭니다. _metaProxyFromBytes는 abi.encode(a, b, c)의 결과를 메타데이터로 붙이고 _metaProxyFromCalldata는 calldata의 4번째 바이트 이후 전부를 메타데이터로 붙입니다. 배포 직후 생성자(constructor) 대신 init() 같은 일회성 설정 함수를 선택적으로 호출할 수 있습니다. 테스트는 인자 없는 호출, 인자 있는 호출, 반환값 있는 호출, revert 시 payload 전달 확인의 네 경우를 다룹니다.
쓸 때 주의할 점
- 표준은 바이트코드만 다룹니다: 이 표준은 바이트코드 구현만 정의하며 그 자체로 심각한 부작용을 포함하지 않습니다. 하지만 기능이 어떻게 쓰이고 구현되는지에 따라 생길 수 있는 부작용은 각 프로젝트에서 직접 조사할 것을 강하게 권장합니다.
- 참조 구현은 예시일 뿐입니다: 원문의 참조 구현은 예시로만 제공되므로 그대로 신뢰하지 말고 실제 사용 환경에 맞게 검토해야 합니다.