ERC-7786 크로스체인 메시징 게이트웨이 (Cross-Chain Messaging Gateway)
Final · 기타 · 2024-10-14 작성
서로 다른 블록체인에 있는 컨트랙트끼리 임의의 데이터를 주고받을 때 쓰는 공통 인터페이스입니다. 어떤 브리지를 쓰든 같은 방식으로 메시지를 보내고 받을 수 있게 합니다.
왜 필요한가
체인 사이에 메시지를 전달해 주는 크로스체인 메시징 프로토콜(브리지)은 종류가 매우 많고 탈중앙화 정도나 구조, 보장하는 성질이 제각각입니다. 거의 모든 프로토콜이 서로 다른 인터페이스와 작업 흐름을 쓴다는 게 문제입니다. 그래서 한 브리지에 맞춰 만든 컨트랙트를 다른 브리지로 옮기는 일이 사실상 불가능하고 크로스체인 통신에 기대는 범용 컨트랙트를 만들기도 어렵습니다. 특정 브리지 하나에 묶인 컨트랙트는 그 브리지가 멈추거나 폐기되면 함께 위험해집니다. 이 표준은 메시지를 보내고 받는 공통 인터페이스와 흐름을 정해 기존 프로토콜도 어댑터(adapter) 컨트랙트를 통해 같은 방식으로 쓸 수 있게 하고 브리지 교체와 조합을 쉽게 만들어 특정 업체 종속을 줄이는 것이 목표입니다.
구현해야 하는 것
function supportsAttribute(bytes4 selector) external view returns (bool);: 키로 지정한 속성(attribute)을 이 게이트웨이가 지원하는지 참·거짓으로 알려줍니다. 한번 지원한 속성은 호환성을 위해 빼지 않는 것이 권장됩니다.error UnsupportedAttribute(bytes4 selector);: 지원하지 않는 속성 키가 포함됐을 때 sendMessage가 이 오류로 되돌립니다.
심층 설명
메시지의 다섯 가지 구성 요소
크로스체인 메시지는 발신자(sender), 수신자(recipient), 페이로드(payload), 네이티브 토큰 값(value), 속성 목록(attributes)으로 구성됩니다. 발신자와 수신자 주소는 ERC-7930의 상호운용 주소(Interoperable Address) 바이너리 형식으로 입력해야 하며 CAIP-350에 따라 직렬화해야 합니다. 이 덕분에 EVM 계열이 아닌 체인의 주소도 표현할 수 있습니다. 수신자 필드는 생략하거나 모든 필드를 0으로 채울 수 있는데, 브로드캐스트처럼 최종 수신자를 프로토콜이 정하는 경우에 씁니다. 페이로드는 해석하지 않는 불투명한 bytes 값입니다.
속성(attribute)으로 확장하는 모듈형 설계
속성은 키-값 쌍이며 키는 4바이트(bytes4), 값은 바이트 배열(bytes)입니다. 키는 Solidity ABI의 함수 선택자로 정의하는 것이 권장됩니다. 예를 들어 minGasLimit(uint256) 시그니처는 키 39f87ba1이 되고, 값 인코딩은 함수 인자의 ABI 인코딩을 따르므로 사람이 읽을 수 있는 이름과 표준 인코딩을 동시에 얻습니다. 속성 목록은 bytes[]로 인코딩하며 각 원소는 키와 값을 이어 붙인 것입니다(선택자 방식 키라면 abi.encodeWithSignature로 만들 수 있습니다). 속성이 하나도 없는 메시지도 유효한 메시지로 취급해야 합니다. 유효한 속성 집합은 확장 가능하며 ERC로 표준화해 공개하는 것이 권장됩니다. 이렇게 브리지 고유 기능을 전용 인터페이스 없이 노출할 수 있어 컨트랙트가 메시지 표현 방식은 그대로 두고 게이트웨이만 바꿀 수 있습니다.
sendMessage의 동작 규칙
소스 게이트웨이(Source Gateway)는 IERC7786GatewaySource를 구현해야 합니다. sendMessage는 지원하지 않는 속성이 포함되면 UnsupportedAttribute로 되돌려야 하고 속성 값이 키에 맞지 않으면 되돌릴 수 있습니다. 네이티브 토큰을 함께 받을 수 있지만 지원하지 않는 기능인데 값이 포함되면 되돌려야 합니다. 이 값이 도착 체인에서 어떻게 표현되는지는 표준이 정하지 않습니다. 고유한 0이 아닌 전송 식별자(sendId)를 만들어 반환하거나 0을 반환할 수 있고 MessageSent 이벤트는 반드시 발생시켜야 합니다. sendId는 수신 측에 전달되는 receiveId와 다를 수 있는데, receiveId는 트랜잭션 id나 로그 인덱스처럼 실행 환경에서 얻을 수 없는 값으로 만들어질 수 있기 때문입니다.
후처리(post-processing)와 설계 의도
sendMessage 호출 뒤에도 메시지를 실제로 보내기 위한 추가 조치가 필요할 수 있습니다. 대표적으로 도착 체인에서 메시지를 실행할 가스비 지불이 있습니다. 이런 조치의 정확한 인터페이스는 이 표준의 범위 밖입니다. 추가 파라미터를 속성으로 받으면 후처리 단계가 필요 없어지지만 속성으로 제공되지 않으면 메시지 전송을 위해 게이트웨이에 추가 호출이 필요합니다. 가능하다면 메시지 전달에 이해관계가 있는 누구나 이 단계에 뛰어들 수 있게 게이트웨이를 설계하는 것이 권장됩니다. 악의적 행위자가 잘못된 파라미터를 제공해도 다른 사람이 메시지를 성공적으로 중계하는 것을 막을 수 없어야 합니다.
수신 절차와 receiveMessage 검증
목적지 게이트웨이(Destination Gateway)는 다른 체인에서 보낸 메시지를 검증하는 컨트랙트이며, 그 인터페이스와 호출 방식은 표준 범위 밖입니다. 다만 프로토콜은 보낸 메시지를 IERC7786Recipient 인터페이스로 수신자에게 전달하는 것을 보장해야 하고 수신자는 이 인터페이스를 구현해야 합니다. receiveId는 비어 있거나 해당 게이트웨이 안에서 그 메시지에 고유해야 합니다. receiveMessage는 IERC7786Recipient.receiveMessage.selector(0x2432ef26)를 반환해야 하고 게이트웨이는 반환값이 올바른지 확인해 아니면 되돌려야 합니다. 수신자는 호출자가 자신이 신뢰하는 프로토콜의 알려진 게이트웨이인지 반드시 검증해야 합니다.
프로토콜이 보장해야 하는 성질
게이트웨이 쌍의 바탕이 되는 프로토콜은 몇 가지 성질을 보장해야 합니다. 안전성(Safety)은 메시지가 출발 체인에서 보내진 경우에만, 보내는 트랜잭션이 확정(finalized)된 뒤에, 두 번 이상이 아니라 정확히 한 번 전달되는 것입니다. 파라미터가 같은 메시지가 여러 개 있으면 각각 따로 전달해야 합니다. 활성(Liveness)은 체인이 정상이고 비용이 지불되면 보낸 메시지가 결국 전달되는 것이고 적시성(Timeliness)은 문서화된 한도 시간 안에 전달되는 것으로 권장 사항입니다. 이 성질들은 중앙화된 행위자에 대한 신뢰에 기대지 않아야 합니다. 예를 들어 안전성은 라이트 클라이언트 증명이나 개방적이고 탈중앙화된 검증자 집합의 증명 같은 무신뢰 방식으로, 중계는 탈중앙화되거나 무허가 방식으로 보장되는 것이 바람직합니다.
왜 전용 receiveMessage 함수를 쓰는가
일부 프로토콜의 게이트웨이는 수신자에게 임의의 직접 호출을 하는 방식을 지원합니다. 이 방식에서는 수신자가 게이트웨이의 호출임을 스스로 감지해야 하고 출처 체인과 발신자 주소는 게이트웨이의 조회 함수로 알아냅니다. 그런데 이 방식은 누구나 게이트웨이를 통해 아무 컨트랙트에나 아무 호출을 일으킬 수 있게 하므로, 게이트웨이가 ERC-20 같은 자산을 보유하고 있다면 위험합니다. 수신자 쪽에 전용 receiveMessage 함수를 두면 게이트웨이가 가진 자산이나 권한을 이런 공격에서 보호할 수 있습니다. 직접 호출 기능이 필요하면 이 표준을 구현한 게이트웨이 위에 래퍼(wrapper)로 만들 수 있습니다.
쓸 때 주의할 점
- 비정규 주소 인코딩 주의: 상호운용 주소(ERC-7930)는 CAIP-350 직렬화에 의존합니다. 정규(canonical) 형식이 아닌 인코딩을 쓰면 메시지가 소리 없이 전달에 실패할 수 있습니다. 게이트웨이는 비정규 인코딩을 거부하는 것이 권장되며 이벤트 발생 전에 정규화할 수도 있습니다.