[Account Abstraction] 계정 추상화 시리즈- 2편

user-image
+2
남민우(Bvbv)외 2명
Researcher/
CURG
2023.08.25

[Xangle Digest]

※해당 컨텐츠는 8월 9일 외부에서 기발간 된 컨텐츠입니다. 컨텐츠에 대한 추가적인 주의사항은 본문 하단에서 확인해주세요.

계정추상화, VISA 페이마스터, 가스비 대납 솔루션, 블록체인 계정 추상화, 메타 트랜잭션

목차
들어가며
EIP-2771: 메타 트랜잭션
Paymaster란?
Paymaster는 어떻게 작동할까?
ERC-4337에서의 Aggregator란?
계정 추상화: 프로젝트 리뷰
맺으며

 

들어가며

본 글은 CURG에서 기획한 [Account Abstraction] 계정 추상화 시리즈- 1편에 이은 두 번째 시리즈로, ERC-4337의 가스비 대납 솔루션인 Paymaster와 Paymaster contract가 EntryPoint contract와 어떻게 상호작용 하는지, 그리고 ERC-4337의 가스비 최적화 모듈 aggregator에 대해서 다룬다. 마지막으로 Account Abstraction을 실제로 구현한 프로젝트들을 리뷰해 볼 예정이다. 

 

다른 토큰으로 가스비를 지불하는 모습, 계정추상화, 가스비 대납 솔루션, 블록체인 계정 추상화, 메타 트랜잭션

출처: Biconomy / Obvious Smart Wallet with biconomySDK. 다른 토큰으로 가스비를 지불하는 모습

일반적으로 이더리움을 보유하고 있지 않거나 가스비 개념에 익숙하지 않은 신규 사용자들은 DApp서비스를 이용하기가 매우 힘들다.

이더리움에서 가스비는 트랜잭션을 실행하거나 스마트 컨트랙트를 호출할 때 사용되는 수수료를 말한다.

예를 들어, ETH를 가지고 있는 사용자는 바로 수수료로 ETH를 지불하여 DApp을 사용할 수 있지만 그렇지 않은 신규사용자는 수수료를 낼 수 없어 아예 이용이 불가능하다. 또한 이런 진입장벽, 즉 UX(user experience)를 헤치는 문제들은 블록체인 생태계의 단점으로 작용한다. 그렇다면 이러한 진입장벽을 없애기 위해서는 어떤 기술을 사용해야할까?

이러한 문제를 해결하기 위해 Paymaster, Meta transaction을 통해서 가스비를 대납해주는 솔루션이 있다.

 

EIP-2771: 메타 트랜잭션

EIP-2771: 메타 트랜잭션(Meta Transaction)의 특징은 다음과 같다.

  • gasLess : relayer가 사용자를 대신해 가스비를 지불하는 것이다. 이는 사용자가 이더리움을 소유하고 있지 않아도 DApp을 사용할 수 있게 해준다.
  • User signer: offChain에서 즉 Relayer에서 User의 서명이 이루어진다.

Meta Transaction의 한계는
1. 기존의 스마트 컨트랙트를 전부 수정해야하며 대규모 컨트랙트의 경우에는 광범위한 수정과 감사가 필요하다.

2. Relayer는 중앙 집권화 되어 있으며 트랜잭션이 중간에 검열될 수 있다.

이러한 문제점과 Meta trancsaction 한계점을 벗어나기 위해 ERC-4337 표준과 함께 Paymaster가 등장했다. 

 

Paymaster란?

Paymaster 구조, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

출처: Medium @CURG 원문

ERC-4337에서 Paymaster를 통해 Smart contract가 특정한 조건에서 사용자를 대신하여 지불할 수 있도록 허용할 수 있게된다. 즉, 대납의 조건 혹은 수수료 지불을 커스텀 로직으로 짤 수 있다.

또한 Paymaster는 누구나 등록할 수 있으며 누구나 후원자가 될 수 있다.

Feature

커스텀 로직을 짤 수 있게 되면서 다양한 기능들이 가능해졌다.

  1. 대납 지원: Paymaster 계정에 예치금을 입금하여 사용자의 트랜잭션을 후원하거나 대납할 수 있다.
  2. 다양한 토큰으로 수수료 지불: Paymaster에 토큰 컨트랙트를 등록하면 해당 토큰 컨트랙트로 가스비를 지불할 수 있게 된다. 이는 ERC-20 토큰 모두에 적용된다.
  3. 화이트리스트 : Paymaster는 화이트리스트를 받아 특정 사용자만이 서비스를 이용하도록 제한할 수 있다. 이는 악의적인 유저가 Paymaster 의 후원금을 고갈시키는 것을 막는 역할을 한다.
  4. 수수료 견적 기능: Paymaster는 사용자가 예상 수수료를 미리 알 수 있도록 견적 기능을 제공한다. 이는 사용자가 높은 수수료를 지불하는 것을 방지하며, 악의적인 유저로 인한 수수료 상승도 방지할 수 있다.

이제 우리는 Paymaster가 다양한 기능을 제공한다는 것을 알았다. Paymaster가 어떻게 유저의 트랜잭션을 대납해주게 되는지 코드레벨로 자세히 알아보자.

Paymaster 구조, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

출처: Medium @CURG 원문

알아보기 위해 일단 간단한 플로우부터 짚고 넘어가보자. 유저는 handleOps에 UserOp를 담아 엔트리포인트에 요청을 보낸다.

  1. 엔트리포인트는 유저의 Contract Wallet에 validateOp를 호출한다.
  2. adddress paymaster가 있다면 Paymaster Contract의 validatePaymasterOp 를 호출한다.
  3. 해당 데이터에서 유효청 검사를 진행하고 실패한 요청들은 폐기한다.
  4. Contract Wallet의 executeOp을 실행하고 소모된 가스를 추적하여 그에 상응하는 ETH를 실행자에게 지불한다. 만약 paymaster항목이 있다면 Paymaster Contract가 이를 대신 지불한다.

이때 Paymaster Contract는 EntryPoint Contract의 deposit 메소드를 통해 ETH를 미리 예치할 수 있다.

 

Paymaster는 어떻게 작동할까?

페이마스터가 어떻게 작동하는지 알 수 있는 코드들은 다음과 같다.

  • UserOp struct
  • EntryPoint validatePaymasterPrepayment, handlePostOp
  • Paymaster validatePaymasterUserOp, postOp

UserOp 구조체 소개

EntryPoint가 어떻게 Paymaster에 대한 데이터를 가지고 오는지 알기 위해 먼저 UserOperaion에 대해 살펴보자.

EntryPoint 계약에는 UserOperation , UserInfo, MemoryuserOp 가지 데이터 구조가 사용된다.

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

출처: Medium @CURG

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

출처: Medium @CURG 원문

EntryPotint에서는 많은 내부함수가 호출되기때문에 가스비 절약을 위하여 UserOperation 대신 memoryMemoryuserOp를 전달한다.

또한, MemoryuserOp 로 변환시 signature를 지우기 때문에 개인키의 보안 역시 안전해진다.

번들러에서 EntryPoint로 보낼 때 기존에 검증을 위해 필요한 바이트 코드들이 삭제되고 paymasterAndData 에서 paymaster 주소를 추출하여 paymaster 로 변경되어 MemoryUserOp에 들어간다.

이제 MemoryUserOp address paymaster의 데이터를 엔트리포인트가 어떻게 활용하는지 살펴보자.

앞에서 언급한 플로우를 기억해 보면, 

  1. EntryPoint에서 Contract Wallet으로 validateOp 호출한다.
  2. 그 후 userOp에 address paymaster가 있다면 validatePaymasterOp를 호출한다.
  3. 해당 데이터에서 유효청 검사를 진행하고 실패한 요청들은 폐기한다.
  4. Contract Wallet의 executeOp을 실행하고 소모된 가스를 추적하여 그에 상응하는 ETH를 실행자에게 지불한다. 만약 paymaster 항목이 있다면 Paymaster 가 이를 대신 지불한다.

아래 그림에서 자세히 알아보도록 하자. 

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

출처: Medium @CURG 원문

위의 플로우라면 두 번째 부분부터 Paymaster가 관여하는 것을 알 수 있다. 이를 토대로 두 번째 부분에서 어떻게 Paymaster 를 호출하는지 자세히 알아보자.

Paymaster의 기본구조는 다음과 같다.

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

해당 코드에서 _validatePaymasterUserOp와 EntryPoint의_validatePaymasterPrepayment를 같이 살펴보자.

  • _validatePaymasterOp of Paymaster
  • _validatePaymasterOp(EntryPoint Contract => Paymaster Contract)

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

validatePaymasterUserOp EntryPoint에게 Paymaster 예치금에 대한 권한을 제어하는 함수다.

 

EntryPoint _validatePaymasterPrepayment 함수이다. 
Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

더 자세히 살펴보자. 

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

1. opInfo에서 mUserOp 개체를 가져온다.

2. GasLimit 확인: 유효성 검사를 위한 가스 한도가 유효성 검사 계정 선불 기능에서 사용한 가스보다 큰지 확인한다. 그렇지 않은 경우 오류 메시지와 함께 트랜잭션을 되돌린다.

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

3. Paymaster 예치금 확인: 이 함수는 Paymaster 의 예치금을 확인하여 필요한 선불 금액을 충당하기에 충분한지 확인한다. 그렇지 않은 경우 오류 메시지와 함께 트랜잭션을 되돌린다.

4. Paymaster 예치금 업데이트: 전자결제 대행사 예치금이 충분한 경우, 필요한 선결제 금액을 전자결제 대행사 예치금에서 차감한다.

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

5. UserOp 유효성 검증: 그런 다음 이 함수는 Payamaster  validatePaymasterUserOp 함수를 호출한다. 이 함수는 Payamaster의 관점에서 사용자 작업의 유효성을 검사한다. 이 함수는 Paymaster의 서명과 잔액을 확인하여 Paymaster가 거래에 대해 결제할 수 있는 권한과 능력이 있는지 확인한다.

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

6. 서명 및 후원금 확인 후 Context 반환

이로써 Paymaster는 EntryPoint에게 차감된 후원금의 권한을 넘겨주게 되었다.
위 코드의 플로우는 아래에서 확인할 수 있다.

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

출처: Medium @CURG 원문

이제 세 번째 단계, 바로 postOp를 호출하는 부분이다.

간단하게 postOp 여러가지 시나리오에 대응하기 위해 Paymaster 동작을 제어하는 함수다.

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

출처: Medium @CURG 원문

paymaster의 postOp와 EntryPoint의 _handlePostOp가 어떻게 작동하는지 알아보자.

postOp(Paymaster Contract => EntryPoint Contract)

먼저, postOp의 등장 배경에 대해 살펴보자. 

 

배경

사용자의 가스비를 USDC로 대신 지불하기 위해선, 요청 과정에서 소비된 가스량을 알 필요가 있다. 따라서, 유효성 검사를 시작하기 전에 이 정보를 미리 파악해야 한다. 이처럼, Paymaster가 특정 정보를 자신에게 전달해 처리된 데이터를 postOp 단계에서 활용하는 경우도 있을 수 있다. 이를 위해 임의의 “Context” 데이터를 나중에 postOp으로 전달할 수 있게 된다.

이렇게 Paymaster가 가스비를 USDC로 청구하는 경우, 주의해야 할 점이 있다. Paymaster는 작업을 실행하기 전(validatePaymasterOp 단계)에 사용자가 충분한 USDC를 가지고 있는지 확인한다. 하지만 작업이 실행되는 동안, 사용자는 지갑의 USDC를 모두 인출해버릴 수 있다. 이 경우, Paymaster는 마지막 단계에서 가스비를 받지 못하게 된다.

그러므로, 요청이 실행된 후에도 Paymaster가 요청을 무효화시킬 수 있는 방법이 필요하다. 이미 validatePaymasterOp 단계에서 가스비 청구에 동의했기 때문에 어떠한 상황에서도 가스비 인출이 가능해야 한다.

이를 구현하기 위해 EntryPoint가 postOp를 두 번 이상 호출할 수 있도록 설계되었다. EntryPoint는 스마트 컨트랙트 지갑의 executeOp가 실행되는 과정에서 한 번 postOp를 실행하고, 만약 postOp이 되돌려진(revert)다면, executeOp도 되돌려진다. 이런 상황이 발생하면, EntryPoint는 다시 한번 postOp를 호출한다. 하지만 이때는 executeOp가 실행되기 전 상태, 즉 validatePaymasterOp만 통과된 상태이므로, Paymaster는 가스비를 인출할 수 있어야 한다.

Paymaster와 관련된 postOp의 코드를 이해하는 데 중요하다는 것을 깨달았다. 이 복잡한 과정을 쉽게 이해하기 위해, 실제 코드를 통해 접근하는 것이 더 효과적일 수 있다. 그럼 이제 각 단계가 어떻게 코드로 표현되는지 자세히 살펴보자.

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

EntryPoint의 _handlePostOp 이 함수는 postOp 단계를 처리하는 핵심 로직을 담고 있다. 그러면 이 함수가 어떤 작업을 수행하는지 살펴보자.

먼저, 함수는 각 사용자 작업(UserOp)에 대해 Paymaster의 주소를 얻는다. 이 주소가 없으면(UserOp.paymaster가 0이면) refundAddress는 UserOp.sender가 된다. 즉, 사용자가 직접 가스비를 지불하게 된다. 그렇지 않으면 refundAddress는 paymaster가 된다. 이 경우, Paymaster가 가스비를 지불하게 된다.

이후, context가 존재하면(즉, Paymaster가 전달해야하는 정보가 있는 경우) Paymaster의 postOp 이 호출된다. 그러나 만약 postOp이 되돌려진(reverted) 경우 EntryPoint의 _handlePostOp에서 에러가 발생하며, 해당 작업(op)에 대해 실패 메시지를 전달한다.

그리고 실제로 사용된 가스비는 계산되어 Paymaster 또는 사용자에게 반환된다.

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

다음으로, base Paymaster postOp 함수를 살펴보자. 이 함수는 EntryPoint에서 호출되며, 여기서는 실제로는 아무 일도 하지 않는다. 대신 이 함수는 보통 상속을 통해 구체적인 로직이 구현되는 추상 메서드로 작동한다. 아래 deposit Paymaster에서 이 메서드가 구현된다.

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화

마지막으로, deposit Paymaster의 _postOp을 살펴보면, 이 메서드는 주어진 ERC20 토큰으로 가스비를 지불하도록 사용자에게 허용한다. 이 함수는 먼저 context에서 정보를 추출하고, 실제로 소비된 가스비를 계산한다. 그리고 이 가스비를 사용자의 토큰에서 차감한다. 만약 작업이 되돌려진(reverted) 경우에는, deposit Paymaster의 잔액에서 차감한다.

정리하자면,

Entrypoint PostOp mode 따라 Paymaster에게 후원금을 돌려줄지 말지 정한다.

Paymaster PostOp 실제가스비를 측정한다.

이때 토큰의 값을 오라클을 이용하여 가져와 이더리움과 비교후 actualGasfee 측정하여 다른 토큰으로도 가스비를 지불할 있게 도와준다.

PostOp단계에서 revert한다면 Paymaster 잔액을 돌려받는다.

아래 그림에서 위 코드가 어떻게 작동 하는지 한번에 확인할 수 있다.

1. excuteOp 실행 postOp에서 검증 ERC20 토큰의 Value 충분하여 성공했을 때
Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화, ERC-20

출처: Medium @CURG 원문

2. excuteOp실행 postOp에서 검증 revert 했을 때

Paymaster 작동 원리, 가스비 대납 솔루션, VISA Paymaster, 블록체인 계정 추상화, ERC-20

출처: Medium @CURG 원문

 

정리

Paymaster는 ERC-4337 표준화 이후 주요 대납 솔루션 중 하나로서 모듈 컨트랙트로 적용되었다. 이러한 계약 지갑을 지원하는 다양한 컨트랙트를 추가하는 것으로, 온체인에서는 실행이 불가능했던 다양한 비즈니스 로직을 앞으로 적용할 수 있을 것으로 기대한다.

그러나, 이더리움에서 가스비를 많이 사용하는 것은 피할 수 없는 상황이지만, 이를 해결하기 위해 레이어 2인 폴리곤에 프로젝트를 이전하는 사례도 많이 보인다. 따라서 가스비 문제 때문에 순수 이더리움을 사용하는 프로젝트가 적을 것으로 예상된다.

 

다음은 ERC-4337의 가스비 최적화 모듈 aggregator에 대해 알아보자. 

 

ERC-4337에서의 Aggregator란?

Aggregator란?

ERC-4337에서는 기존 프로세스 이외에 확장적인 개념으로 Paymaster와 Aggregator라는 역할이 소개되었는데, 이 글에서는 그 중 Aggregator라는 역할에 초점을 맞춰 소개하려고 한다.

Aggregator란 말 그대로, 집계자라는 뜻으로 여러 개의 서명을 검증 및 통합해 하나의 서명으로 만들어 주는 역할을 한다.

우리가 잘 알고 있는 이더리움의 서명 검증 방식은 ECDSA 방식으로 타원 곡선 알고리즘을 기반으로 수행되고 있다. 하지만, ERC-4437에서의 Aggregator는 단일 서명이 아니라 여러 개의 서명을 검증해야 하므로, 기존 방식과 다른 특별한 알고리즘의 도입이 필요하다.

또한, Aggregator는 노드가 아니라 하나의 컨트랙트이다. 따라서, Aggregator 컨트랙트 함수 안에 여러 서명을 받고, 검증하는 알고리즘이 담긴 함수 로직이 필요하다.

 

Aggregator Process

[기존의 ERC-4337 프로세스]

기존 ERC-4337 프로세스, 계정 추상화, 블록체인 계정추상화

출처: alchemy blog 

 

  1. Multiple Users -> Bundler : 번들러는 멤풀에 있는 유저들의 userOperation들을 하나로 묶어서 번들을 생성한다.
  2. Bundler -> EntryPoint : 번들러는 엔트리포인트에게 userOperation 번들을 처리해달라고 보내고, 엔트리포인트는 내부적으로 번들 속 userOperation 들을 검증 및 실행하는 과정을 거친다.
  3. EntryPoint -> Smart Contract Wallets(User) : 엔트리포인트는 번들러에게 userOperation 들을 검증하라고 위임하고, 정상적인 userOperation 들만 실행시켜 유저와 상호작용을 한다.

userOperation들을 실행시키기 전에, 검증해야 하는 이유는

만약, 실행 과정에서 userOperation이 fail 될 경우 Bundler는 가스비만 날리게 된다. 이 상황이 빈번하게 나타난다면, Bundler는 ERC-4337 네트워크에 참여해 얻은 보상보다 fail로 인해 발생하는 가스비가 더 커질 수 있는 위험이 있기 때문이다.

하지만, 여기에는 치명적인 단점이 있다.

Bundler는 모든 userOps 들을 반복적으로 돌면서 하나씩 검증하는 절차를 밟게되는데, 번들에 많은 UserOp들이 담긴 경우, 해당 프로세스가 너무 오래걸린다는 단점이 있다.

 

그렇다면 Aggergator는 어떻게 해결할까?

해당 부분을 ERC-4337에서는 Aggregator라는 스마트 컨트랙트로 해결하고자 한다.

먼저 Aggregator 의 표준 인터페이스 스펙을 살펴보자.

Aggregator 작동 원리, 가스비 대납 솔루션, 블록체인 계정 추상화, AA

  1. 각 userOps들의 유효성 검증 함수
  2. 이후 검증된 userOps들에 대한 집계서명을 제공하는 함수
  3. 만들어진 집계서명을 검증하는 함수

로 구성되어 있다.

Aggregator 를 이용한 ERC-4337의 프로세스는 다음과 같다. Aggregator 를 사용했을 때와 사용하지 않았을 때를 비교하면 두 가지 큰 차이점이 있다.

Aggregator 작동 원리,  Aggregator를 이용한 ERC- 4337 프로세스,  가스비 대납 솔루션, 블록체인 계정 추상화

출처: alchemy blog , CURG

  1. Bundler에서 Entry Point로 보내지기 전 집계 서명 요청에 대한 차이
  2. EntryPoint에서 userOps들을 실행시킬 때 validateSignatures함수를 실행시키냐의 차이


Aggregator를 사용하면 사전에 집계 함수를 만들고 EntryPoint에서 해당 집계 서명만 검증하면 되므로 userOp 들을 실행시키기전에 가스비 손실을 줄이고자 시행하는 validateOp의 반복 과정을 거치지 않는다. Aggregator의 validateSignatures 함수를 호출하고, 유효하다는 응답을 받으면 바로 executeOp 과정으로 돌입할 수 있다는 점이 큰 장점이다.

그렇다면 근본적으로 돌아가서 어떻게 하나의 서명으로 여러 개의 userOp , 여러 개의 트랜잭션의 유효성을 판단 있을까?

해답은 이미 유명하게 나와있다. 답은 Ethereum 2.0에서 소개된 BLS 서명 방식이다. Aggregator 컨트랙트의 로직을 구현할 때 BLS 방식의 암호화, 복호화 방식을 이용하면 된다. 실제, 대부분의 AA 관련 서비스를 제공하는 업체들도 Aggregator 컨트랙트는 모두 BLS 방식을 채택해 구현했다.

E.g.) Stackup, infinitism …

 

Aggregator의 효과

1. 시간적 측면

IEFT에서 소개된 ECDSA와 BLS의 시간 차이는 다음과 같다.

“For 128 bits security, ECDSA takes 37 and 79 micro-seconds to sign and verify a signature on a typical laptop. In comparison, for the same level of security, BLS takes 370 and 2700 micro-seconds to sign and verify a signature.

In terms of sizes, ECDSA uses 32 bytes for public keys and 64 bytes for signatures; while BLS uses 96 bytes for public keys, and 48 bytes for signatures. Alternatively, BLS can also be instantiated with 48 bytes of public keys and 96 bytes of signatures. BLS also allows for signature aggregation. In other words, a single signature is sufficient to authenticate multiple messages and public keys.”

TL;DR

→ ECDSA에서 128bit 서명 및 서명 검증 과정은 각각 37us 79us가 걸린다. 하지만, BLS의 경우 서명은 370us 검증의 경우 2700us의 시간이 걸린다.

하지만 이는 단일 서명의 경우이고, 집계 서명의 경우에는 BLS의 장점이 부각된다. IEFT-draft에서 소개된 성능 기준으로 현재 이더리움에서 사용하는 서명 17개 이상의 서명을 집계하면 ECDSA 방식보다 빠르다는 실험 결과가 있다.

2. 비용적 측면

BLS 방식은 집계 서명한 결과와 단일서명 한 결과가 모두 96byte의 길이를 가진다. 따라서 N개의 서명을 집계한다면, BLS 단일 서명 결과의 비해 N 분의 1의 효율을 얻을 수 있다. 이더리움에서 서명의 크기를 줄인다는 것은 가스비 절감으로도 이어진다. 따라서 집계 서명을 통해 가스비 절감의 효과도 누릴 수 있다.

이제 Aggregator 의 구현체를 살펴보자.

다음은 infinitism이 구현한 Aggregator의 구현체이다. infinitism에서 제공하는 다양한 기능과 메소드들이 있지만, 이 글에서는 EIP에서 명시된 스펙인 위의 3가지 인터페이스 메소드들의 구현에 대해서만 알아볼 것이다.

Aggregator 작동 원리, 가스비 대납 솔루션, 블록체인 계정 추상화

validateSignautres는 위의 인터페이스를 상속받아 구현한 메소드이며, 집계서명(파라미터 : signature)을 검증하는 함수이다.

기본적인 예외 처리와 선언문 이후, blsSignature 부분을 분리하고 for 문을 통해 userOps에서 메세지와 Public Key 분리한다. 마지막으로 require문으로 집계 서명으로 분리 메세지와 Public Key들을 함께 verifyMultiple이라는 함수에 인자로 넣어 검증을 요청한다. verifyMultiple 메소드를 지원하는 BLSOpen클래스는 BLS로직을 구현한 클래스이며, BLS 구현에 관해서는 내용이 너무 방대해 현재 글에서는 다루지 않을 것이다.

validateUserOpSignature()

Aggregator 작동 원리, 가스비 대납 솔루션, 블록체인 계정 추상화

마찬가지로 userOp에서 public Key와 메세지, Signature부분을 분리한다. 이후, BLSOpen클래스에서 제공하는 verifySingle 즉, 단일 userOperation에 대해서 검증을 하는 메소드를 사용해 검증한다.

aggregateSignatures()

Aggregator 작동 원리, 가스비 대납 솔루션, 블록체인 계정 추상화

userOperation들을 받아서 abi decoding을 통해서 하나의 서명을 2개의 cordination으로 나눈다. 이후, 나눈 서명의 cordination을 하나의 points라는 변수에 넣고, 반복문으로 모든 userOperation의 쪼개진 cordination 값들을 넣는다. 마지막으로 BLSHelper에서 제공하는 sum메소드로 함수를 합친다. 여기서 N값은 BLS로직이 구현된 solidity코드의 주소이다. 이 sum값을 다시 encoding시켜 넘겨주면 된다.

이번 글에서는 Aggregator라는 역할에 대해서 알아보았는데, 대부분이 BLS내용이라서 이해가 어려울 수도 있다. BLS 방식을 알고싶지 않거나 너무 복잡해 이해가 어려운 독자들은 userOperation이 처리되는 과정 중 Aggregator가 어디서 영향을 끼치고 어떤 역할을 수행하는지만 짚고 넘어가면 될 것이다.

 

정리

개인적으로는 대부분의 ERC-4337 서비스를 제공하는 업체들은 Aggregator를 통해 가스비 절약, 트랜잭션 시간 단축 등 ERC-4337의 단점을 모두 Aggregator가 해결해줄 것이라고 믿는다.

이는 Stack-up의 블로그 중 AA의 가스비 우려에 대한 글이다. 요약하자면, 

ERC-4337의 가스비에 대한 테스트를 해봤고, 비싼 건 맞지만, BLS로직을 구현해 Aggregator를 만들면 괜찮아질 것이다. 지금은 ERC-4337의 사용량이 많이 없어서 Aggregator의 효과가 미미한데, 앞으로 ERC-4337 풀은 늘어날 것이고, 그때는 Aggregator의 효과가 굉장할 것이다.

ERC-4337의 userOps가 많아진다는 대전제 하에, 실제로 집계 서명을 통한 서명 데이터 압축으로 가스비가 절감되고, 속도도 빨라지는 것이 맞다. 하지만 애초에 AA의 가스비가 많이 나오는 것은 실제 거쳐 가는 컨트랙트가 많아서다.물론 Aggregator를 통해 지금보다는 낮은 가스비와 높은 속도를 보여줄 순 있겠지만, 거쳐가는 트랜잭션이 너무 많다는 근본적인 문제가 해결될 수 없으므로, 기존의 트랙잭션보다는 더 낮은 UX를 제공할 수밖에 없을 것이다. Aggregator라는 플레이어가 꽤 합리적인 존재임은 인정하지만, 이를 통해서 ERC-4337의 단점을 해결된다고 생각하기에는 어렵다고 본다.

*만약 BLS 로직이 궁금하다면 아래 링크에서 자세히 살펴볼 수 있다. 

 

계정 추상화 : 프로젝트 리뷰

마지막으로 Account Abstraction을 실제로 구현한 프로젝트들에 대해 알아보자. 
Account Abstraction은 지금까지 사용자들이 EOA(Externally Owned Account)를 개인 계정으로 사용해오던 것을 CA(Contract Account)로 전환하는 것을 말한다. 시리즈의 앞선 본문에서는 사용자들이 CA를 개인 계정(마치 EOA와 같이)으로 사용할 수 있도록 하기 위해 어떤 문제들이 있었고 이를 어떻게 기술적으로 해결했는지 이야기했다.

하지만 꼭 ERC-4337을 통해서만 Account Abstraction을 구현할 수 있는 것은 아니다. 다시 말해, 꼭 ERC-4337을 통해서만 CA를 개인 계정으로 만들 수 있는 것은 아니다. 실제로 이 글에서 다루게될 Argent와 Safe 그리고 Instadapp의 경우 각자 다른 방식으로 사용자들이 개인 계정으로 사용할 수 있는 CA를 프로덕트 수준에서 제공해왔다.

이 글에서는 ERC-4337 표준을 따르며 Account Abstraction을 구현한 프로젝트와 ERC-4337 표준을 따르지 않으며 독자적인 방식으로 Account Abstraction을 구현한 프로젝트(주로 지갑이 될 것이다)를 살펴본다. 특별히 이 글에서는 표준을 따른 프로젝트와 표준을 따르지 않은 프로젝트의 구현 방식의 차이에 대해 살펴보도록 할 것이다.

 

ERC-4337 Standard

먼저 ERC-4337 표준에 따라 Account Abstraction을 구현한 프로젝트를 살펴본다. 표준에 따라 구현된 프로젝트의 경우 구현 방식이 유사하기 때문에 ERC-4337 표준의 Wallet Contract와 Wallet Factory Contract의 아키텍처를 살펴보고 현재 가장 많이 Develop된 Wallet 중 하나인 ZeroDev Kernel에 대해 살펴보도록 한다.

Common Architecture

Wallet Contract
먼저 ERC-4337 표준의 Wallet Contract를 살펴보자. Wallet Contract는 사용자들이 사용하게 되는 개인 계정, 즉 CA의 역할을 한다. ERC-4337 표준에 따르면 Wallet Contract는 validateUserOp와 executeUserOp 메소드가 필수적으로 구현되어 있어야 한다.

이때, validateUserOp은 Entrypoint에 의해 호출되며, 해당 Wallet Contract가 실행할 UserOperation의 서명을 검증하는 역할을 하고, executeUserOp은 사용자가 요청한 임의의 명령을 수행하는 역할을 한다. 말그대로 임의의 명령이기 때문에 executeUserOp은 다양한 방식으로 구현될 수 있다.

아래 Wallet Contract 인터페이스를 확인해보자.

Aggregator 작동 원리, 가스비 대납 솔루션, 블록체인 계정 추상화

아래는 Infinitism BaseAccount Contract validateUserOp 메소드이다.

Aggregator 작동 원리, 가스비 대납 솔루션, 블록체인 계정 추상화

위의 인터페이스를 보았을 때, validateUserOp 메소드의 역할에 대한 의문이 생길 수 있다. 왜냐하면 executeUserOp 메소드의 경우 애초에 UserOperation의 signature와 Wallet의 Owner가 일치하지 않을 경우 실행되지 않기 때문이다. 때문에 생각해보면 애초에 validateUserOp이라는 메소드가 왜 필요한가에 대해서도 고민해볼 수 있다.

그러나 앞선 글에서 이야기했던 것처럼 ERC-4337 표준에 따른 CA는 기존과는 다른 방식으로 동작한다. 개인 계정으로서의 Wallet은 사용자의 EOA에 의해 실행되지 않고 Bundler에 의해 실행되기 때문이다. 만일 UserOperation의 Signature와 Wallet의 Owner가 일치하지 않을 경우 해당 트랜잭션은 실패하게 되고 실패하게 되더라도 가스비는 지불해야 한다.

악의적인 사용자가 계속해서 다른 Wallet으로 UserOperation을 제출하게 되고, Bundler가 해당 UserOperation으로 트랜잭션을 생성하게 되면 Bundler가 계속해서 손해를 입게 되고 네트워크에 악영향을 미치게 될 것 이다. 때문에 validateUserOp 메소드를 통해 사전에 signature와 Owner가 일치하지 않아 발생하는 트랜잭션 실패를 방지하는 것이다.

이것이 트랜잭션을 생성하기에 앞서 validateUserOp을 통해 해당 UserOperation의 signature가 유효한지 검증하는 단계를 거치게 되는 이유이다.

ZeroDev, Stackup 포함하여 ERC-4337 표준을 따르는 프로젝트는 대부분 표준을 구현한 eth-infinitism BaseAccount.sol 가지 메소드를 추가하거나 제외하여 구현하고 있으며, 이는 아래 링크를 통해 확인할 있다.

 

WalletFactory Contract

WalletFactory Contract는 Wallet이 업그레이드되더라도 새로운 Contract 주소가 아닌 기존과 동일한 Contract 주소에 배포되도록 하는 역할을 하며 Singleton 구조입니다. WalletFactory Contract의 구체적인 역할에 대해서는 아래에서 설명하도록 한다.

기존에 우리가 사용하고 있는 EOA의 경우 계정을 생성하면 어떤 트랜잭션이 발생하지 않았음에도 불구하고 다른 이들로부터 자산을 송금받을 수 있다.

하지만 CA의 경우 Bundler를 통하여 Contract가 배포되기 전까지는 해당 계정으로 자산을 송금받을 수 없다. 왜냐하면 CA를 배포하지 않는 이상 계정 자체가 존재하지 않을 뿐더러 일반적인 방법으로 배포된 Contract는 배포전까지 Address를 알 수 없기 때문이다.

ERC-4337 표준에서는 이 문제를 해결하기 위해 CREATE2 Opcode를 사용하여 CA를 배포한다.

CREATE2 Opcode
CREATE2 EIP-1014에서 제안된 컨트랙트 배포 방식으로 주소를 특정하여 컨트랙트를 생성할 있도록 한다. 이것이 가능한 이유는 CREATE2 CREATE와는 다르게 nonce 제외하고 0xff, salt, init_code 만을 파라미터로 받아서 컨트랙트의 주소를 생성하기 때문이다. 아래는 ethers.js 예시이다.

Aggregator 작동 원리, 가스비 대납 솔루션, 블록체인 계정 추상화

각 파라미터의 자세한 정보는 아래와 같다.

  • fromAddress
    fromAddress는 Singleton Factory의 주소이다. 이 Factory Contract는 salt와 initCode를 인자로 받은 후 CREATE2 Opcode를 사용하여 Wallet Contract를 온체인에 배포하는 역할을 한다.
  • salt
    salt는 nonce 값을 의미한다. 하지만 CREATE2 Opcode를 사용하여 미리 정한 주소 값을 가진 Contract를 배포하기 위해서는 해당 파라미터를 Init Nonce 즉, 0으로 입력해야 한다.
  • initCodeHash
    initCode는 UserOperation의 필드에도 포함되는 데이터로 Wallet Contract를 초기화하는데 사용된다. 만약 Contract의 주소가 존재하지 않는 경우 UserOperation의 initCode를 참조하여 Contract를 배포하게 되지만, 만약 Contract의 주소와 initCode가 모두 비어있을 경우 getCreate2Address 호출은 실패하게 된다.

CREATE2에 대한 자세한 내용은 아래 링크를 참고하자.

 

Proxy Pattern
Proxy Pattern은 업그레이드 가능한 컨트랙트를 구현할 때 있어서 일반적으로 사용하는 패턴이다. 만일 사용자가 Wallet Contract에 어떤 기능을 추가하고자 할 경우 Wallet Contract가 업그레이드 되어야 한다.

이때, Proxy Pattern을 사용하여 사용자의 Wallet Contract를 업그레이드 해야 한다. Proxy Pattern을 사용하지 않을 경우 컨트랙트를 업그레이드할 때마다 새로운 주소를 배포되는 문제가 생기기 때문이다.

Proxy Pattern을 사용하여 업그레이드 가능한 컨트랙트를 구현할 경우 스토리지 충돌과 같은 문제가 생기게 되는데, 이를 해결하기 위한 것이 EIP-1967이다. 여기서 EIP-1967 Proxy Pattern을 구현할 때 스토리지 충돌을 방지하기 위한 표준 정도로 이해하도록 하고 자세한 내용은 아래 링크를 참고해주길 바란다

 

ZeroDev Kernel

ZeroDev Kernel은 Account Abstraction Wallet의 필수적인 기본 기능을 포함하고 있으며, 이는 아래와 같다.

  • ERC-4337과의 호환성
  • ERC-1271을 통한 유효성 검사
  • Batching 트랜잭션
  • Delegating Calls

추가적으로 ZeroDev Kernel은 지갑 개발자가 온체인 기능을 구축할 수 있도록 지원하기 위해 플러그인 프레임워크를 제공하고 있다. 이 플러그인을 통해 개발자는 Contract를 업그레이드할 필요 없이 Kernel 위에 기능을 추가할 수 있다.

 

EIP-4337 Unstandard

이제 ERC-4337 표준을 따르지 않으며 Account Abstraction을 구현한 프로젝트들을 살펴보도록 한다. 이 경우에는 표준을 따르지 않아 구현 방식이 각각 다르기 때문에 각 프로젝트들의 구현 방식을 하나씩 확인해보도록 하겠다.

Argent Wallet

먼저 Argent Wallet이다. Argent Wallet ERC-4337 표준을 따르지 않고 독자적인 방식으로 Account Abstraction 구현한 대표적인 프로젝트이다. Argent Wallet MultiSig 보안 기능, Guardian 통한 지갑 복구 기능 등을 제공한다.

Argent Wallet, EIP-4337 프로젝트, 블록체인 계정 추상화

출처: Argent Wallet

Argent Architecture
Argent는 ERC-4337 표준에서 Wallet Contract를 생성하는 방식과 유사하게 WalletFactory Contract가 CREATE2 Opcode를 사용하여 사용자의 Wallet Contract를 배포하고, Proxy Pattern을 할용하여 업그레이드 가능한 Wallet Contract를 구현했다는 점에서 ERC-4337 표준과 유사하다고 할 수 있다.

다만,

  1. ERC-4337 표준에서는 UserOperation이라는 가상의 트랜잭션 객체를 기반으로 동작한다면, Argent Wallet은 Argent Module Contract를 통해 Module Contract를 추가하고 BaseWallet에서 Module을 실행한다는 점,
  2. ERC-4337 표준에서는 AltMempool에서 여러 Bundler들이 사용자 Wallet을 대신하여 UserOperation을 트랜잭션으로 생성하는 반면, Argent에서는 Argent Module에 의해 Wallet Contract가 실행된다는 점,
  3. ERC-4337 표준에서는 UserOperation에 Paymaster에 의해 대납이 이루어지지만, Argent의 경우 Meta-transaction 방식을 사용하며, 때문에 별도의 Relayer Module Contract에 정의된 Relayer에 의해 대납이 이루어진다는 점,

에서 차이가 존재한다.

Argent Wallet 아키텍쳐는 다음과 같다.

Argent Wallet 아키텍쳐,Argent Wallet, EIP-4337 프로젝트, 블록체인 계정 추상화

출처: Argent Wallet, Medium @CURG 원문

Argent Wallet의 주요 Entity
Wallet Factory: CREATE2 Opcode를 사용하여 Proxy Wallet을 생성하고 사용자에게 할당하는데 사용되는 Singleton Factory Contract이다.

  • Base Wallet: Base Wallet은 Proxy Wallet이 호출하게 될 메소드 즉, Base Wallet의 기능이 구현된 Contract이다. Base Wallet은 Contract의 Owner를 변경하거나, Module에 대한 실행 권한은 검증하거나, 실제로 Module을 실행하는 메소드를 포함한다.
  • Module: Module은 Wallet Contract에 기능을 의미한다. 사용자의 Wallet Contract는 Module이 추가 또는 제거될 때마다 업데이트된다. 현재 Argent에서 기본적으로 제공하는 Module에는 Meta-Transaction을 위한 Relayer Module, Contract 업그레이드를 위한 SimpleUpgrade Module, 계정 복구 시 필요한 Guardians Module 등이 배포되어있다..
  • Module Registry: Module Registry는 사용자의 Wallet에 등록된 Module Contract를 저장하는 역할을 한다.

이외에도 MultiSig Wallet, ENS Manager, ENS Resolver, Token Registry, Storage Contract 등이 포함된다.

 

Safe SDK

다음으로 Safe SDK 대해 살펴보도록 한다. 초기 Gnosis Safe 다중 서명을 통해 프로젝트나 펀드에서 자금을 공동으로 관리해야 하는 문제를 해결하기 위해 만들어졌지만, 현재는 Account Abstraction Wallet SDK 제공하고 있다. Safe SDK 역시 EIP-4337 표준을 따르지 않고 Account Abstraction 구현하는 프로젝트 하나이며, Argent와는 다르게 서비스 뿐만 아니라 SDK까지 제공하고 있다. 실제로 Soul Wallet, Candedi Wallet 여러 Wallet 서비스들이 Safe SDK 활용하고 있다.

Safe SDK, EIP-4337 프로젝트, 블록체인 계정 추상화

출처: Safe Global

Safe의 Account Abstraction SDK는 Protocol Kit, Relay Kit, Auth Kit, Onramp Kit으로 총 4개로 구성된다. 이 글에서는 Onramp Kit을 제외한 세 가지 Kit에 대해서만 살펴보도록 한다.

Protocol Kit
Protocol Kit은 Safe가 자체적으로 구현한 것으로 Wallet을 생성하는 역할을 하며, 이때 Wallet Contract는 두 가지 방법을 통해 생성될 수 있다. 이는 아래에서 자세히 이야기하도록 한다.

Relay Kit
Relay Kit은 수수료 대납과 ERC-20을 수수료로 지불할 수 있는 기능을 위해 만들어진 것으로 Gelato Network의 Relay Module에 의존하고 있다. Gelato Network의 Relay Module은 Meta-Transaction 방식을 사용하여 수수료 대납을 지원한다.

Auth Kit
Auth Kit은 소셜 로그인, 즉 이메일 등을 통한 로그인을 지원하기 위한 것으로 Coinbase의 Base가 개발한 Web3Auth에 의존하고 있다.

Safe SDK 아키텍처
Safe 경우 Wallet 배포하는 방법이 가지가 존재한다. 번째는 앞서 살펴본 Argent 동일한 방식으로 WalletFactory Contract Wallet 배포하는 방법이 있고, 번째는 사용자가 자신의 서명(EOA 개인 ) 가지고 Wallet 생성하는 방법이 있다. Safe Contract Wallet Safe라고 부르며, Safe Contract 아래와 같은 Contract 상속받는다.

Safe SDK 아키텍쳐, EIP-4337 프로젝트, 블록체인 계정 추상화

상속받는 Contract 이름에서 있듯이 Safe 해당 Contract Address 대한 Module, Owner, Guardian, Storage 접근 등을 관리할 있다.

Safe Global, EIP-4337 프로젝트, 블록체인 계정 추상화

출처: Safe Global, Medium @CURG 원문

추가로 Safe 경우 Safe Guard 통해 권한이 없는 3자가 Safe Contract 메소드를 호출하는 것을 방지한다.

출처: Safe Global, Medium @CURG 원문

Safe 역시 ERC-4337 표준과 유사하게 CREATE2 Opcode를 사용하여 사용자의 Wallet Contract를 배포하고, Proxy Pattern을 할용하여 업그레이드 가능한 Wallet Contract를 구현했다는 점에서 ERC-4337 표준 및 Argent와 유사하다고 할 수 있다.

다만,

  1. Safe의 경우 추가적으로 개인 키를 기반으로 Contract Address를 배포할 수 있다는 점,
  2. ERC-4337 표준에서는 AltMempool에서 여러 Bundler들이 사용자 Wallet을 대신하여 UserOperation을 트랜잭션으로 제출하는 반면, Safe의 경우 개인 키를 가지고 직접 지갑에 서명을 하거나, Module에 Safe Module에 의해 호출된다는 점,
  3. ERC-4337의 경우 AltMempool에서 UserOperation이 Bundling 되고, Argent의 경우 자체적인 Relayer가 있지만, Safe의 경우 Gelato Network의 Relay 모듈에 의존한다는 점,
  4. ERC-4337 표준에서는 UserOperation에 Paymaster에 의해 대납이 이루어지고, Argent의 경우 별도의 RelayerModule Contract에 정의된 Relayer에 의해 대납이 이루어진다. Safe의 경우 역시 Meta-transaction을 사용하지만, Gelato Network의 Relaying Module에 의존하고 있다는 점.

에서 차이가 존재한다.

참고로 Safe는 가장 최근에 릴리즈된 v1.4.0부터는 ERC-4337을 동시에 지원하고 있다. 앞서 소개한 Safe Contract에 4337Module을 추가하는 방식으로 동작한다.

 

맺으며

지금까지 ERC-4337 표준의 Wallet Contract와 Wallet Factory Contact 그리고 ERC-4337 표준을 따라 Account Abstraction을 구현한 ZeroDev라는 프로젝트를 봤다. 또한 ERC-4337 표준을 따르지 않으면서 독자적인 방식으로 Account Abstraction을 구현한 Argent와 Safe SDK에 대해서도 이야기했다.

이를 통해 알 수 있었던 것은 ERC-4337 표준을 통해서만 Contract Account를 구현할 수 있는 것이 아니며, Argent, Safe은 각자 다른 방식으로 Contract Account를 구현했다는 사실이다.

실제로 Argent와 Safe는 Account Abstraction과 함께 Smart Contract Wallet이라는 용어가 떠오르기 이전부터 Smart Wallet, Smart Account라는 이름으로 개인 계정으로서의 CA를 개발해왔다.

ERC-4337이 없이도 Argent, Safe와 비슷한 방식으로 Contract Account 자체를 구현할 수 있었다. 마지막으로 ERC-4337 표준을 따른 Account Abstraction의 의의를 생각해보며 글을 마무리하도록 하겠다.

통합성

ERC-4337 이전에는 여러 플레이어들이 각기 다른 기능을 구현해왔기 때문에 Multi-Sig, Paymaster 등의 기능들이 파편적으로 제공되어져왔다. 실제로 Safe의 경우 Smart Contract Wallet을 생성하는 Protocol Kit은 자체적으로 구현했지만, 대납 구현을 위한 Relayer Kit은 Gelato Raley를 사용하고, 소셜 로그인 구현을 위한 Auth Kit은 Web3Auth를 사용하고 있다. 하지만 ERC-4337을 통해 계정추상화 구현에 대한 표준이 만들어졌다고 할 수 있다.

탈중앙성

독자적으로 계정추상화 구현을 시도한 이들의 경우 어느 정도 탈중앙에 대한 타협이 있다. 그러나 ERC-4337의 경우 탈중앙에 대한 타협없이 계정추상화 구현을 시도했다. 물론 AltMempool이 더 안정되고 UserOperation을 번들링해주는 Bundler들에 대한 인센티브 시스템이 안정적으로 동작하기 전까지는 불안정하고 또 중앙화된 상태라고 할 수 있다. 그러나 최종적인 방향성은 탈중앙에 대해서는 타협하지 않는다는 점에서 의의가 있다고 할 수 있다.

 

※참고자료 

-> ‘[Account Abstraction] 계정 추상화 시리즈- 2편’ 원문 보러가기

*[AA Series #3]Paymaster: 대납이 어떻게 이뤄질까?
*[AA Series #4] Account Abstraction : Aggregator
* Account Abstraction : Project Review

주의사항
본 글에 기재된 내용들은 작성자 본인의 의견을 정확하게 반영하고 있으며 외부의 부당한 압력이나 간섭 없이 작성되었음을 확인합니다. 작성된 내용은 작성자 본인의 견해이며, (주)크로스앵글의 공식 입장이나 의견을 대변하지 않습니다. 본 글은 정보 제공을 목적으로 배포되는 자료입니다. 본 글은 투자 자문이나 투자권유에 해당하지 않습니다. 별도로 명시되지 않은 경우, 투자 및 투자전략, 또는 기타 상품이나 서비스 사용에 대한 결정 및 책임은 사용자에게 있으며 투자 목적, 개인적 상황, 재정적 상황을 고려하여 투자 결정은 사용자 본인이 직접 해야 합니다. 보다 자세한 내용은 금융관련 전문가를 통해 확인하십시오. 과거 수익률이나 전망이 반드시 미래의 수익률을 보장하지 않습니다.
본 제작 자료 및 콘텐츠에 대한 저작권은 자사 또는 제휴 파트너에게 있으며, 저작권에 위배되는 편집이나 무단 복제 및 무단 전재, 재배포 시 사전 경고 없이 형사고발 조치됨을 알려드립니다.