본문으로 건너뛰기
0%

어카운트 추상화 4부: 집합 서명(Aggregate Signatures)

David Philipson headshot

작성자 David Philipson

2023년 2월 14일에 게시됨4분 읽기

집계 서명 (Aggregate signatures)

현재 구현은 번들에 있는 각 user op를 개별적으로 검증합니다. 검증을 이렇게 생각하는 것은 매우 직관적인 방식이지만, 어쩌면 비효율적일 수 있습니다. 서명 검증에는 상당한 양의 암호화 연산이 필요하기 때문에 gas 비용이 많이 드는 편입니다.

여러 op를 한 번에, 여러 개의 서명이 아니라 단 하나의 서명으로 검증할 수 있다면 좋지 않을까요?

이를 위해서는 암호학의 한 개념인 **집계 서명(aggregate signatures)**에 의존해야 합니다.

집계를 지원하는 서명 스킴은, 서로 다른 키로 서명된 여러 메시지가 주어졌을 때, 하나의 결합된 서명을 생성하는 방법을 제공합니다. 이 결합된 서명을 검증하면 개별 서명들이 모두 유효하다는 것이 성립합니다.

집계를 지원하는 서명 스킴의 대표적인 예로 BLS가 있습니다.

이 최적화는 롤업을 구현할 때 특히 유용한데, 롤업의 주요 목표가 데이터 압축이고 서명 집계를 통해 서명 부분을 압축할 수 있기 때문입니다.

서명 집계로 얻는 공간 절약에 대해 더 알아보려면 이 주제에 관한 Vitalik의 트윗을 참고하세요.

Aggregator 소개

바로 알 수 있는 사실은, 번들 내 모든 user op의 서명을 함께 집계할 수는 없다는 것입니다. 지갑은 자신에게 주어진 서명을 검증하기 위해 임의의 로직을 사용할 수 있다는 점을 기억하세요. 따라서 같은 번들 안에 다양한 서명 스킴이 존재할 수 있습니다.

서로 다른 스킴의 서명은 아마도 집계할 수 없으므로, 번들은 결국 각각 고유한 집계 스킴을 사용하거나 아예 집계 스킴을 사용하지 않는 op들의 그룹으로 나뉘게 됩니다.

체인상에서 각기 다른 집계 스킴들을 각자의 로직과 함께 표현해야 하므로, 각 집계 스킴을 aggregator라고 부르는 컨트랙트로 표현하겠습니다.

집계 스킴은 여러 서명을 하나로 결합하는 방법과 결합된 서명을 검증하는 방법으로 정의됩니다. 따라서 aggregator는 이 두 함수를 메서드로 노출합니다:

애그리게이터 컨트랙트는 여러 사용자의 op를 하나의 서명으로 그룹화합니다.
애그리게이터 컨트랙트는 여러 사용자의 op를 하나의 서명으로 그룹화합니다.

각 지갑이 자신만의 서명 스킴을 정의하므로, 어떤 aggregator와 호환될지(호환되는 것이 있다면)는 각 지갑이 결정할 몫입니다.

지갑이 집계에 참여하고자 한다면, 자신의 aggregator를 선택하는 메서드를 노출합니다:

이 새로운 getAggregator 메서드를 사용해, 번들러는 같은 aggregator를 사용하는 op들을 함께 그룹화하고 해당 aggregator의 aggregateSignatures 메서드를 이용해 이들의 결합된 서명을 계산할 수 있습니다.

그룹은 다음과 같은 모습일 수 있습니다:

💡 번들러가 특정 aggregator에 대해 체인 외부(off-chain) 지식을 가지고 있다면, aggregateSignatures를 EVM 코드로 실행하는 대신 서명 집계 알고리즘의 네이티브 버전을 하드코딩하여 최적화할 수 있습니다.

다음으로, 새로운 aggregator들을 활용하도록 entry point 컨트랙트를 업데이트해야 합니다.

entry point에는 op 목록을 받는 handleOps 메서드가 있었다는 것을 떠올려 보세요.

여기에 같은 동작을 하지만 op들을 aggregator별로 그룹화하여 받는 새로운 메서드 handleAggregatedOps를 추가하겠습니다:

새로운 메서드인 handleAggregatedOps는 handleOps과 대체로 동일하게 동작합니다. 유일한 차이는 검증 단계에 있습니다.

handleOps이 각 지갑의 validateOp 메서드를 호출해 검증을 수행하는 반면, handleAggregatedOps는 대신 각 그룹의 결합된 서명에 대해 해당 그룹의 aggregator가 가진 validateSignatures 메서드를 호출합니다.

다이어그램: executor가 aggregator로 user op를 그룹화한 후 entry point로 전송하여 일괄 검증하는 과정
executor는 aggregator를 사용해 op들을 그룹화한 뒤 entry point로 전송하여, 한 번에 모두 검증되도록 합니다.

거의 다 됐습니다!

하지만 이제는 익숙할 법한 한 가지 문제가 여기 남아 있습니다.

번들러는 op 그룹을 번들에 포함시키기 전에 검증을 시뮬레이션하여 aggregator가 해당 그룹을 검증에 통과시킬 것인지 확인하고 싶어 합니다. 검증이 실패하면 번들러가 gas 비용을 부담해야 하기 때문입니다. 하지만 임의의 로직을 가진 aggregator는 시뮬레이션에서는 쉽게 성공하고 실행 시에는 실패할 수 있습니다.

이 문제는 paymaster와 factory에서와 정확히 같은 방식으로 해결합니다. aggregator가 접근할 수 있는 storage와 사용할 수 있는 opcode를 제한하고, storage에 접근하지 않는 경우가 아니라면 entry point에 ETH를 스테이킹하도록 요구합니다.

이것으로 집계 서명에 대한 내용은 끝입니다!

마무리

지금까지 만든 것은 거의 ERC-4337의 전체 아키텍처와 다름없습니다! 일부 메서드의 이름이나 인자 같은 세부사항에 차이가 있지만, 아키텍처상의 차이라고 할 만한 것은 남아 있지 않습니다. 제가 설명을 잘 했다면, 이제 여러분은 실제 ERC-4337을 읽고 무슨 내용인지 이해할 수 있을 것입니다.

여기까지 읽어주셔서 정말 감사합니다! 이 글을 쓰면서 제가 도움을 받은 만큼, 여러분에게도 도움이 되었기를 바랍니다.

부록: ERC-4337과의 차이점

계정 추상화의 전반적인 아키텍처는 이제 파악했지만, ERC-4337을 만든 똑똑한 사람들은 위에서 설명한 것과는 조금 다른 몇 가지를 생각해냈습니다.

몇 가지를 살펴보겠습니다!

1. 검증 시간 범위

위에서 지갑의 validateOp과 paymaster의 validatePaymasterOp의 반환 타입에 대해 다소 모호하게 설명했습니다. ERC-4337은 이를 잘 활용하는 방법을 찾아냈습니다.

지갑이 정말 하고 싶어할 만한 일은 user op가 특정 시간 동안만 유효하도록 허용하는 것입니다. 그렇지 않으면 악의적인 번들러가 해당 op를 아주 오랫동안 가지고 있다가, 자신에게 유리한 시점에 훨씬 나중에 번들에 포함시킬 수 있습니다.

지갑은 검증 중에 TIMESTAMP을 확인해서 너무 먼 미래가 아닌지 체크하여 이를 방어하고 싶어 할 수 있지만, 그렇게 할 수 없습니다. 시뮬레이션이 부정확해지는 것을 막기 위해 검증 중 TIMESTAMP를 금지했기 때문입니다. 이는 지갑이 op가 유효한 시간 범위를 나타낼 다른 방법이 필요하다는 것을 의미합니다.

따라서 ERC-4337은 validateOp에 지갑이 시간 범위를 선택할 수 있는 반환 값을 부여합니다:

이 반환 값은 op가 유효한 시간 범위를, 연달아 나오는 두 개의 8바이트 정수로 나타냅니다.

ERC-4337의 또 다른 특이사항: 지갑은 검증 실패 시 revert하는 대신 validateOp에서 sentinel 값을 반환해야 합니다. 이는 gas 추정에 도움이 되는데, eth_estimateGas는 revert된 트랜잭션에서 얼마나 gas가 사용되었는지 알려주지 않기 때문입니다.

2. 지갑과 factory를 위한 임의의 호출 데이터

우리가 정의했던 지갑의 인터페이스는 다음과 같았습니다:

ERC-4337에서는 지갑에 실제로 executeOp이라는 이름의 메서드가 없습니다.

대신, user operation에는 callData 필드가 있습니다:

이 값은 호출 데이터로 지갑에 전달됩니다.

일반적인 스마트 컨트랙트의 경우, 이 데이터의 처음 4바이트는 함수 선택자(function selector)로 해석되고 나머지는 함수 인자로 해석됩니다.

이는 필수인 validateOp 메서드를 제외하면 지갑이 자체적인 인터페이스를 정의할 수 있으며, user operation을 이용해 지갑의 임의 메서드를 호출할 수 있음을 의미합니다.

마찬가지로, ERC-4337에서 factory 컨트랙트도 실제로는 deployContract 메서드를 가지고 있지 않습니다. factory 역시 임의의 호출 데이터를 받는데, 이 경우 op의 initCode 필드로부터 전달받습니다.

3. paymaster와 factory를 위한 압축된 데이터

위에서 user operation에는 paymaster를 지정하는 필드와 paymaster에 전달할 데이터를 지정하는 필드가 있다고 했습니다:

ERC-4337에서는 최적화를 위해 이 두 필드가 하나로 합쳐지며, 필드의 처음 20바이트가 paymaster 주소이고 나머지가 데이터입니다:

factory와 그에 전달되는 데이터도 마찬가지입니다. 우리는 factory과 factoryData라는 두 개의 필드를 사용했지만, ERC-4337은 이를 하나의 필드인 initCode으로 합칩니다.

자, 다 되었습니다!

계정 추상화에 대해 많은 것을 배우셨기를 바랍니다.

계정 추상화를 직접 발명할 수 있었다면

이 4부작 시리즈의 시작 부분을 놓치셨나요? 돌아가서 처음부터 읽어보세요!

  1. Account Abstraction Part 1: Protect Our Assets
  2. Account Abstraction Part 2: Sponsoring Transactions with Paymasters
  3. Account Abstraction Part 3: Wallet Creation
Background gradient

블록체인 매직을 만드세요

Alchemy는 가장 강력한 Web3 개발자 제품 및 도구를 리소스, 커뮤니티, 그리고 전설적인 지원과 결합합니다.