이 글은 내가 Abstract를 이해하기 위해 문서를 보고 싸지른 글 입니다. 헷갈려서 한글로 이름 바꾼게 여럿 있습니다.

Abstract 체인 이해하기

L2 체인 Abstract: ZK Rollup을 활용한 확장성 솔루션

Abstract는 이더리움의 보안성을 기반으로 작동하는 Layer 2(L2) 체인입니다. 이 L2 체인에서는 발생한 트랜잭션을 큰 묶음(배치)으로 모아 이더리움 메인넷에 기록합니다.

이 체인은 ZK Stack이라는 프레임워크를 통해 구축된 ZK Rollup 체인을 사용합니다. ZK Rollup은 영지식 증명(Zero-Knowledge Proof) 기술을 활용하여, 이더리움의 보안을 유지하면서도 거래 처리량을 크게 증가시키는 확장성 솔루션을 제공합니다.

L2가 필요한 이유

Layer 2(L2)는 이더리움의 확장 문제를 해결하기 위해 설계된 블록체인 집합을 말합니다. 이더리움은 초당 약 15건의 트랜잭션(TPS)만 처리할 수 있으며, 트랜잭션 처리 비용인 가스 수수료가 종종 비싸다는 단점이 있습니다. 이러한 제약으로 인해, 소규모 금융 거래와 같은 낮은 가치의 트랜잭션을 이더리움에서 처리하기는 비효율적입니다.

L2의 주된 목표는 분산화와 보안을 희생하지 않고 TPS를 증가시키고, 동시에 트랜잭션의 가스 수수료를 낮추는 것입니다. 이를 통해 이더리움 생태계는 더 많은 사용자를 수용할 수 있습니다.

ZK Rollup: 영지식 묶음의 원리와 이점

ZK Rollup(Zero-Knowledge Rollup)은 블록체인의 확장성을 높이기 위한 기술로, 영지식 증명을 통해 트랜잭션 검증 비용을 최소화합니다.

ZK Rollup의 핵심은 오프체인(off-chain)에서 트랜잭션을 처리하고, 변경사항만 이더리움 메인넷에 게시하는 방식입니다. 모든 트랜잭션 데이터를 온체인에 기록할 필요 없이, 유효성 증명(Validity Proof)이라는 암호학적 증거만 제출함으로써 트랜잭션의 신뢰성을 보장합니다.

ZK Rollup의 작동 방식은 다음과 같습니다

  1. 트랜잭션 배치 처리: 개별 트랜잭션을 오프체인에서 처리하여 실행 결과를 묶음 단위로 생성합니다.
  2. 변경 사항 제출: 배치 처리 결과를 요약하여 이더리움에 기록합니다.
  3. 유효성 증명 제출: 스마트 컨트랙트에 의해 관리되는 상태를 업데이트하기 위해 ZK Rollup 노드가 유효성 증명을 제공합니다. 이 증명은 해당 배치가 정확히 처리되었음을 보장합니다.

이 방식의 주요 장점은 다음과 같습니다:

  • 온체인 데이터 감소: 이더리움 네트워크에 모든 트랜잭션을 기록할 필요가 없으므로 저장 공간과 처리 비용이 절감됩니다.
  • 보안성 유지: 영지식 증명을 통해 배치 처리의 정확성을 보장하며, 이더리움의 스마트 컨트랙트를 통해 상태를 안전하게 관리합니다.
  • 효율적 확장: 더 높은 거래 처리량을 제공하면서도 이더리움 메인넷의 보안성을 유지합니다.

ZK Rollup과 Optimistic Rollup의 차이점

ZK Rollup은 유효성 증명을 사용하여 즉시 트랜잭션 상태를 확정짓는 반면, Optimistic Rollup은 모든 트랜잭션 데이터를 온체인에 기록하며 검증 기간 동안 이의를 제기할 수 있는 시간을 제공합니다. 이러한 차이로 인해 ZK Rollup은 더 빠르고 효율적인 방식으로 이더리움 네트워크의 확장 문제를 해결합니다.

Transaction 의 생명주기

트랜잭션은 Layer 2(L2) 네트워크인 Abstract와 Layer 1(L1) 네트워크인 이더리움 사이에서 일련의 단계를 거쳐 처리되고 최종 확정됩니다. 이 과정을 크게 Processed, Sending, Validation, Executing의 4단계로 나눌 수 있습니다.

  1. Abstract (Processed)
    L2 네트워크에서 트랜잭션은 다음과 같은 과정을 거칩니다
    • 트랜잭션 입력: 사용자가 RPC를 통해 트랜잭션을 제출합니다.
    • 트랜잭션 실행: L2 네트워크에서 트랜잭션이 실행됩니다.
    • 응답 제공: 사용자에게 트랜잭션 실행 결과(성공 여부)가 반환됩니다.
    • 시퀀싱: 시퀀서가 실행된 트랜잭션을 포함한 블록을 ZK Prover에게 전달합니다.
    • 배치 생성: Prover가 여러 블록의 트랜잭션을 묶어 배치(batch)를 생성합니다.
  2. Ethereum (Sending)
    생성된 배치는 L1 네트워크로 전송되며 다음 과정을 거칩니다
    • 상태 변경사항 제출: 블록체인 상태의 변경사항만을 포함하는 최적화된 데이터가 이더리움에 제출됩니다.
    • 배치 커밋: 시퀀서는 L1 Rollup Contract의 commitBatches 함수를 호출하여 여러 배치를 단일 트랜잭션으로 이더리움에 커밋합니다. 배치는 blob 데이터 형태로 저장되며, 이는 EIP-4844 표준을 따릅니다. 이 과정에서 데이터 가용성(data availability)을 보장합니다.
  3. Ethereum (Validation)
    제출된 배치는 이더리움에서 검증 과정을 거칩니다
    • ZK Prover 생성: 배치를 검증하기 위해 ZK Prover가 생성됩니다
    • 배치 검증: L1 Rollup Contract에서 proveBatches 함수가 호출되어, 배치에 포함된 상태 변경사항의 유효성을 증명합니다.
  4. Ethereum (Executing)
    검증이 완료된 배치는 최종 실행을 통해 확정됩니다
    • 상태 확정: 검증이 완료되면, 배치에 포함된 상태가 최종 확정됩니다.
    • 실행 호출: L1 Rollup Contract에서 executeBatches 함수가 호출합니다.
    • 머클 트리 저장: 실행 과정에서 생성된 L2 로그 포함된 머클 트리가 저장됩니다.

Abstract 시스템의 주요 구성 요소

  1. Sequencer (트랜잭션 모으기 및 처리)
    Sequencer는 L2 트랜잭션을 수집, 처리, 그리고 블록 및 배치로 정리하여 L1에 제출하는 역할을 합니다.
    • RPC (Remote Procedure Call)
      클라이언트와 L2 체인 간 상호작용 API를 제공합니다. 주요 기능: 트랜잭션 제출, 상태 쿼리, 이벤트 요청 등.

    • Sequencer
      RPC를 통해 들어온 트랜잭션을 수집하고 블록으로 정리합니다. 트랜잭션 처리 시 검증 시스템의 제약 조건을 준수하도록 설계되었습니다.

    • ETH Operator
      L1에서 발생하는 이벤트(입금, 시스템 업그레이드 등)를 모니터링하여 L2와 동기화 상태를 유지합니다. 트랜잭션을 배치로 묶어 L1으로 전송합니다.

  2. Prover & Verifier (증명과 검증)
    Prover는 Sequencer가 생성한 배치에 대해 유효성 증명을 생성하며, Verifier는 이를 검증합니다.
    1. Proof Generation (증명 생성)
      • Witness(증인) Generation
        증인은 Prover가 트랜잭션이 유효함을 증명하기 위해 사용하는 데이터입니다. 이 데이터는 각 트랜잭션 세부정보를 포함하지 않고, 배치 단위로 처리됩니다.
      • Circuit(회로) Execution
        회로는 Prover와 Verifier 에 의해 실행됩니다. Prover는 증인을 사용해 증명을 생성하며, Verifier는 이 증명을 회로와 비교해 검증합니다. ZK-Proving Circuit은 트랜잭션 배치를 반복적으로 검증하며 최종 상태 루트를 도출합니다. Abstract는 Boojum 을 사용하여 회로를 증명, 검증 합니다. 회로 구성을 위해 백엔드 구성 요소를 운영합니다.
      • Proof Compression
        회로가 출력하는 ZK-STARK는 크기가 커서 가스가 높을 수 있습니다. 이를 압축하여 작은 크기의 ZK-SNARK 증명을 생성합니다. ZK-SNARK는 크기가 작아 빠르고 저렴하게 검증할 수 있습니다.
    2. Proof Verification (증명 검증)
      • Prover가 생성한 ZK-SNARK는 L1 Rollup Contract의 proveBatches 함수 호출과 함께 제출됩니다.
      • L1 스마트 컨트랙트는 다음과 같은 인터페이스를 통해 증명을 검증합니다
          function verify(
              uint256[] calldata _publicInputs,
              uint256[] calldata _proof,
              uint256[] calldata _recursiveAggregationInput
          ) external view returns (bool);
        
  3. L1 Rollup Contracts
    • 배치에서 추출된 상태 변경사항(state diffs)을 blobs 형태로 저장합니다.
    • L2에서 제출된 유효성 증명(validity proofs)을 수신하고 검증합니다.
    • 크로스체인 메시징 및 브리징을 통해 L1과 L2 간의 데이터를 교환합니다.

Native Account Abstraction (네이티브 계정 추상화)

Abstract는 모든 계정이 스마트 컨트랙트 계정(CA)으로만 구성되는 독특한 구조를 갖추고 있습니다. 이는 이더리움처럼 사용자 계정(EOA)과 스마트 컨트랙트 계정(CA)이 병렬적으로 존재하지 않으며, 모든 계정이 동일한 트랜잭션 생명주기를 따르는 환경을 제공합니다.

  • 모든 계정은 표준 인터페이스인 IAccount 를 구현합니다. IAccount는 계정의 최소 구현 (메서드 모음) 스펙입니다.
  • 사용자는 여전히 EOA 지갑을 사용할 수 있습니다. 다만 EOA 사용 시 트랜젝션 생명주기 내에서 DefaultAccount(IAccount 구현체)로 변환됩니다.
  • 모든 계정은 Paymaster를 네이티브로 지원합니다. 즉 어떤 계정이든 다른 계정의 트랜젝션에 대한 가스비를 대신 지불하거나 ERC-20토큰으로 지불할 수 있습니다.

Transaction Flow

  1. Submitting transactions (트랜젝션 제출)
    • 트랜젝션은 RPC 를 통해 제출되고, 트랜젝션 멤풀(mempool)에 들어갑니다.
    • 트랜젝션을 어떻게 검증할지는 스마트컨트렉트 계정이 결정하기 때문에, 이 단계에서 from 필드를 스마트컨트렉트 주소로 설정한 후 제출할 수 있습니다.
  2. Bootloader processing
    • 부트로더는 멤풀에서 트랜젝션을 읽어와 이를 배치 단위로 처리합니다.
    • 각 트랜젝션이 시작되기전에 NonceHolder 시스템 컨트렉트를 호출하여 제공된 nonce가 이미 사용된것이 아닌지 확인합니다.
    • 각 트랜젝션에 대해 부트로더는 tx.from 필드를 읽고 해당 주소에 컨트렉트 코드가 있는지 확인합니다. 컨트렉트 코드가 없는경우 송신계정을 EOA로 간주하고 이를 DefaultAccount로 변환합니다.
  3. Smart contract account validation & execution
    부트로더는 이후 tx.from 주소에 배포된 계정에서 다음 함수를 호출합니다. 함수 호출 시 msg.sender는 부트로더의 컨트렉트 주소로 설정됩니다.
    • validateTransaction: 트랜잭션을 실행할지 여부를 결정합니다. 일반적으로 이 단계에서 특정 사용자가 계정을 사용할 수 있도록 제한하는 검사가 수행합니다. (여기서 일반적으로 라는 말을 붙이는 이유가 뭘까? 다른 경우가 있나?)
    • executeTransaction: 검증을 통과한 경우 트랜잭션을 실행합니다.
    • payForTransaction 또는 prepareForPaymaster: 가스비를 직접 지불하거나, 페이마스터에게 해당 트랜잭션의 가스비를 요청합니다.
  4. Paymasters (Optional)
    페이마스터가 설정된 경우, bootloader 는 paymaster 함수를 호출합니다. 함수 호출 시 msg.sender는 부트로더의 컨트렉트 주소로 설정됩니다.
    1. validateAndPayForPaymasterTransaction: 트랜잭션의 가스비를 지불할지 여부를 결정하고, 지불하기로 결정된 경우 계산된 가스비를 지불합니다.
    2. postTransaction: 트랜잭션 실행 후 선택적으로 추가 로직을 실행합니다.

Smart Contract Wallet

Abstract에서는 모든 계정이 IAccount 인터페이스를 구현하는 스마트 컨트랙트로 구성됩니다. 각 트랜잭션이 실행될 때, 부트로더는 tx.from 주소에 배포된 스마트 컨트랙트 계정의 특정 함수를 호출합니다.

hardhat 환경에서 cosmo-contract 레포지토리를 기반으로 스마트 컨트랙트 월렛을 개발할 때 @matterlabs/zksync-contracts 패키지를 사용하여 IAccount 인터페이스 구현체를 개발할 수 있습니다.

주요 함수 및 설명

  • validateTransaction
    • 트랜잭션 실행 여부를 결정하는 함수입니다.
    • 이 함수는 계정 사용자를 제한하거나 트랜잭션 조건을 검증하는 데 사용됩니다.
    • 주요 조건:
      • 트랜잭션 실행 시 계정의 nonce를 증가시켜야 합니다.
      • 트랜잭션이 유효하다면 ACCOUNT_VALIDATION_SUCCESS_MAGIC 값을 반환해야 합니다.
      • 반드시 부트로더 컨트랙트에 의해서만 호출되어야 하며, 이를 보장하기 위해 onlyBootloader를 사용할 수 있습니다.
  • executeTransaction
    • validateTransaction에서 트랜잭션이 유효하다고 판별된 경우 실행됩니다. (ACCOUNT_VALIDATION_SUCCESS_MAGIC가 반환된 경우)
    • 실행 시 무결성을 보장하기 위해 다음 조건을 충족해야 합니다:
      • EfficientCall 라이브러리 활용: zkEVM의 기능을 활용하여 트랜잭션을 효율적으로 실행합니다.
      • 컨트랙트 배포 처리: 트랜잭션에 컨트랙트 배포가 포함될 경우, ContractDeployer 시스템 컨트랙트를 사용하고 isSystemCall 플래그를 true로 설정해야 합니다.
      • 반드시 부트로더 컨트랙트에 의해서만 호출되어야 하며, onlyBootloader로 이를 보장할 수 있습니다.
  • executeTransactionFromOutside
    • 스마트 컨트랙트 월렛 외부에서 트랜잭션을 시작할 때 사용됩니다.
    • 이 함수는 L1 -> L2 통신을 통해 특정 계정을 대신하여 트랜잭션을 실행할 수 있도록 지원합니다.
    • 외부 시스템이나 컨트랙트가 계정 권한으로 트랜잭션을 실행해야 하는 상황에서 유용합니다.
  • payForTransaction
    • 트랜잭션 실행 시 가스비를 부트로더에 지불하는 데 사용됩니다.
    • 가스비 처리를 간소화하기 위해 _transaction.payToTheBootloader() 함수가 제공됩니다.
    • 반드시 부트로더 컨트랙트에 의해서만 호출되어야 하며, onlyBootloader로 이를 보장할 수 있습니다.
  • prepareForPaymaster
    • 페이마스터가 가스비를 대신 지불하는 트랜잭션에서 사용됩니다.
    • payForTransaction 대신 페이마스터를 설정하여 가스비를 지원받는 구조를 구현할 수 있습니다.
    • 페이마스터 입력 처리를 간소화하기 위해 _transaction.processPaymasterInput() 함수가 제공됩니다.

Paymasters

페이마스터(Paymaster)는 다른 계정을 대신하여 트랜잭션의 가스비를 지불하는 스마트 컨트랙트입니다. 모든 페이마스터는 IPaymaster 인터페이스를 구현해야 합니다. 파이마스터는 계정 소유자가 직접 가스비를 지불하지 않도록 지원함으로써 더 유연한 트랜잭션 비용 관리가 가능하게 합니다.

  • 트랜잭션 흐름에서의 역할
    • 스마트 컨트랙트 지갑이 트랜잭션을 검증하고 실행한 후, 선택적으로 prepareForPaymaster를 호출하여 트랜잭션에 설정된 페이마스터에게 가스비 지급을 위임할 수 있습니다.
    • 이 시점에서 페이마스터는 트랜잭션을 검증하고 가스비를 지불합니다.

Handling Nonces (Nonce 관리)

트랜잭션 시작 전에, 제공된 nonce가 이미 사용되었는지 확인하기 위해 NonceHolder 시스템 컨트랙트에서 validateNonceUsage 함수가 호출됩니다. 유효성 검사 단계에서 nonce를 사용된 것으로 표시하기 위해 두 가지 옵션이 있습니다.

  1. minNonce를 증가시켜 minNonce보다 작은 모든 nonce가 사용된 것으로 간주시킵니다.
  2. nonce 아래에 0이 아닌 값을 setValueUnderNonce를 통해 설정합니다.

NonceHolder 시스템 계약에서 제공하는 편리한 메서드인 incrementMinNonceIfEquals를 사용할 수 있습니다. 예를 들어, 스마트 계약 지갑 내부에서 이를 사용하여 계정의 minNonce를 증가시킬 수 있습니다.

NonceHolder 시스템 계약을 사용하려면 트랜잭션에서 isSystem 플래그를 true로 설정해야 하며, 이는 아래에 표시된 SystemContractsCaller 라이브러리를 사용하여 설정할 수 있습니다.

Signature Validation

스마트컨트렉트 계정(Smart Contract Account)은 EOA(Externally Owned Account)처럼 서명을 검증할 방법이 없기 때문에, 스마트 계약 계정에 대해 EIP-1271을 구현하는 것이 권장됩니다. 이 EIP는 스마트 계약이 특정 메시지에 대한 서명이 유효한지 검증할 수 있는 표준화된 방법을 제공합니다.

EIP-1271은 단일 함수인 isValidSignature를 지정하며, 이 함수는 주어진 서명을 검증하기 위한 임의의 로직을 포함할 수 있습니다. 이 로직은 주로 스마트 계약 계정을 어떻게 구현했는지에 따라 달라짐니다.

AGW (Abstract Global Wallet)

사용자가 네이티브 계정 추상화로 구동되는 Abstract에 구축된 모든 애플리케이션(컨트렉트)과 상호 작용하기 위해 만들어졌습니다.

SmartContract 지갑입니다.

AGW는 사용자가 친숙한 로그인 방법(이메일, 소셜 계정, 패스키 등)을 사용하여 한 번만 가입하고 이 계정을 사용하여 트랜젝션을 만들 수 있습니다.

스마트 계약 지갑 배포

EOA(Externally Owned Account) 지갑이 생성되면, 해당 지갑의 공개 키가 스마트 계약 지갑 배포에 사용됩니다. 스마트 계약 지갑은 배포시 초기화 과정에서 EOA 지갑이 지갑의 승인된 서명자로 추가됩니다.

Abstract의 모든 계정은 스마트 계약 계정(네이티브 계정 추상화 참고)이므로, 스마트 계약 지갑은 Abstract 생태계와 상호작용할 때 1급 시민(First-Class Citizen)으로 간주됩니다.

배포되는 스마트 계약 지갑은 Clave의 수정된 포크 버전으로, 기본적으로 secp256k1 서명자를 지원하도록 커스터마이징되었습니다.

이는 기본 Clave에서 사용되는 secp256r1 서명자 대신 Privy Embedded Wallet을 지원하기 위함이며, EIP-712 서명을 지원하는 맞춤형 검증 로직도 포함하고 있습니다.

스마트 계약 지갑 기능

스마트 계약 지갑에는 다양한 기능을 확장할 수 있는 모듈이 포함되어 있습니다.

  • 복구 모듈(Recovery Modules): 사용자가 이메일 복구 또는 Guardian 복구와 같은 복구 방법을 통해 로그인 방법을 잃어버린 경우 계정을 복구할 수 있도록 지원합니다.
  • 페이마스터(Paymaster) 지원: 거래 가스 비용을 Paymaster가 후원할 수 있습니다.
  • 다중 서명자(Multiple Signers): 사용자가 여러 서명자를 지갑에 추가하여 다양한 계정이 거래를 서명할 수 있도록 지원합니다.
  • P256/secp256r1 지원: 사용자가 패스키(Passkey)로 생성된 서명자를 추가하여 거래를 승인할 수 있습니다.